質問

I have a SVG file that I want to store in a database. It would be very helpful to store all the objects as polygons and store the coordinates. However the file I work with was exported from Ilustrator. It does contain polygons but also transformed rectangles and other forms. The elements don't have to be rendered with absolute precision so rounding errors are not an issue.

How can I convert the transformed rects?

This is ok:

<polygon points="2694.423,2972.209 2685.76,2982.961 2702.282,2996.274
2710.938,2985.526   "/

and can be stored as a list of coordinates. This is problematic

<rect x="4316.474" y="2236.01" transform="matrix(-0.3208 -0.9471 0.9471 -0.3208 3591.1353 7063.0771)" width="22.991" height="15.92"/>

and should be converted to a polygon as in the first example.

Eventually I would like to use a html5 canvas to display the shapes.

To render the polyon data I've use the kinecticjs framework in the following way:

    function poly (layer, points)
{
    var poly = new Kinetic.Line({
        points: points,
        fill: 'black',
        stroke: 'black',
        strokeWidth: 0,
        closed: true
      });
  // add the shape to the layer
  layer.add(poly);
  return poly;
}

Are there know performance issues with canvas vs svg?. I have about 3000 clickable objects to render.

役に立ちましたか?

解決

As to the question:

Are there know performance issues with canvas vs svg?. I have about 3000 clickable objects to render.

The 3000 elements are no problem. But you cannot include events on the individual canvas elements.

他のヒント

The apply-transforms normaliser step could be achieved using http://petercollingridge.appspot.com/svg_optimiser.

As for the second step, you may have to add your own code for that.

In many cases it is meaningful to return certain svg elements(line, rect, circle, ellipse, polygon, polyline, and path) to their screen x,y values following transformations. This is accomplished using getCTM, and matrixTransform Note: Use vector-effect="non-scaling-stroke" for elements with stroke(*not available in IE). The following is code to return screen points for various transformed svg elemens:

//----build a generic document SVG root to hold svg point---
function screenLine(line,svg)
{
    var sCTM = line.getCTM()
    var x1=parseFloat(line.getAttribute("x1"))
    var y1=parseFloat(line.getAttribute("y1"))
    var x2=parseFloat(line.getAttribute("x2"))
    var y2=parseFloat(line.getAttribute("y2"))

    var mySVGPoint1 = svg.createSVGPoint();
    mySVGPoint1.x = x1
    mySVGPoint1.y = y1
    mySVGPointTrans1 = mySVGPoint1.matrixTransform(sCTM)
    line.setAttribute("x1",mySVGPointTrans1.x)
    line.setAttribute("y1",mySVGPointTrans1.y)

    var mySVGPoint2 = svg.createSVGPoint();
    mySVGPoint2.x = x2
    mySVGPoint2.y = y2
    mySVGPointTrans2= mySVGPoint2.matrixTransform(sCTM)
    line.setAttribute("x2",mySVGPointTrans2.x)
    line.setAttribute("y2",mySVGPointTrans2.y)
    //---force removal of transform--
    line.setAttribute("transform","")
    line.removeAttribute("transform")
}
function screenCircle(circle,svg)
{
    var sCTM = circle.getCTM()
    var scaleX  = sCTM.a;

    var cx=parseFloat(circle.getAttribute("cx"))
    var cy=parseFloat(circle.getAttribute("cy"))

    var r=parseFloat(circle.getAttribute("r"))

    var mySVGPointC = svg.createSVGPoint();
    mySVGPointC.x = cx
    mySVGPointC.y = cy
    mySVGPointTransC = mySVGPointC.matrixTransform(sCTM)
    circle.setAttribute("cx",mySVGPointTransC.x)
    circle.setAttribute("cy",mySVGPointTransC.y)

    circle.setAttribute("r",r*scaleX)
    //---force removal of transform--
    circle.setAttribute("transform","")
    circle.removeAttribute("transform")
}
function screenEllipse(ellipse,svg)
{
    var sCTM = ellipse.getCTM()
    var scaleX  = sCTM.a;
    var scaleY  = sCTM.d;

    var cx=parseFloat(ellipse.getAttribute("cx"))
    var cy=parseFloat(ellipse.getAttribute("cy"))

    var rx=parseFloat(ellipse.getAttribute("rx"))
    var ry=parseFloat(ellipse.getAttribute("ry"))

    var mySVGPointC = svg.createSVGPoint();
    mySVGPointC.x = cx
    mySVGPointC.y = cy
    mySVGPointTransC = mySVGPointC.matrixTransform(sCTM)
    ellipse.setAttribute("cx",mySVGPointTransC.x)
    ellipse.setAttribute("cy",mySVGPointTransC.y)

    ellipse.setAttribute("rx",rx*scaleX)
    ellipse.setAttribute("ry",ry*scaleY)

    //---force removal of transform--
    ellipse.setAttribute("transform","")
    ellipse.removeAttribute("transform")
}
function screenRect(rect,svg)
{
    var sCTM = rect.getCTM()
    var scaleX  = sCTM.a;
    var scaleY  = sCTM.d;

    var x=parseFloat(rect.getAttribute("x"))
    var y=parseFloat(rect.getAttribute("y"))

    var width=parseFloat(rect.getAttribute("width"))
    var height=parseFloat(rect.getAttribute("height"))

    var mySVGPoint = svg.createSVGPoint();
    mySVGPoint.x = x
    mySVGPoint.y = y
    mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
    rect.setAttribute("x",mySVGPointTrans.x)
    rect.setAttribute("y",mySVGPointTrans.y)

    rect.setAttribute("width",width*scaleX)
    rect.setAttribute("height",height*scaleY)

    //---force removal of transform--
    rect.setAttribute("transform","")
    rect.removeAttribute("transform")
}
function screenPolyline(myPoly,svg)
{
    var sCTM = myPoly.getCTM()
    var pointsList = myPoly.points;
    var n = pointsList.numberOfItems;
    for(var m=0;m<n;m++)
    {
        var mySVGPoint = mySVG.createSVGPoint();
        mySVGPoint.x = pointsList.getItem(m).x
        mySVGPoint.y = pointsList.getItem(m).y
        mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
        pointsList.getItem(m).x=mySVGPointTrans.x
        pointsList.getItem(m).y=mySVGPointTrans.y
    }
    //---force removal of transform--
    myPoly.setAttribute("transform","")
    myPoly.removeAttribute("transform")
}

function screenPath(path,svg)
{
    var sCTM = path.getCTM()
    var scaleX  = sCTM.a;
    var scaleY  = sCTM.d;

    var segList=path.pathSegList
    var segs=segList.numberOfItems
    //---change segObj values
    for(var k=0;k<segs;k++)
    {
        var segObj=segList.getItem(k)

        if(segObj.x && segObj.y )
        {
            var mySVGPoint = svg.createSVGPoint();
            mySVGPoint.x = segObj.x
            mySVGPoint.y = segObj.y
            mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
            segObj.x=mySVGPointTrans.x
            segObj.y=mySVGPointTrans.y
        }

        if(segObj.x1 && segObj.y1)
        {
            var mySVGPoint1 = svg.createSVGPoint();
            mySVGPoint1.x = segObj.x1
            mySVGPoint1.y = segObj.y1
            mySVGPointTrans1 = mySVGPoint1.matrixTransform(sCTM)
            segObj.x1=mySVGPointTrans1.x
            segObj.y1=mySVGPointTrans1.y
        }
        if(segObj.x2 && segObj.y2)
        {
            var mySVGPoint2 = svg.createSVGPoint();
            mySVGPoint2.x = segObj.x2
            mySVGPoint2.y = segObj.y2
            mySVGPointTrans2 = mySVGPoint2.matrixTransform(sCTM)
            segObj.x2=mySVGPointTrans2.x
            segObj.y2=mySVGPointTrans2.y
        }

        if(segObj.r1)segObj.r1=segObj.r1*scaleX
        if(segObj.r2)segObj.r2=segObj.r2*scaleX
    }
    //---force removal of transform--
    path.setAttribute("transform","")
    path.removeAttribute("transform")
}

//---changes all transformed points to screen points---
function screenPolygon(myPoly,mySVG)
{
    var sCTM = myPoly.getCTM()
    var pointsList = myPoly.points;
    var n = pointsList.numberOfItems;
    for(var m=0;m<n;m++)
    {
        var mySVGPoint = mySVG.createSVGPoint();
        mySVGPoint.x = pointsList.getItem(m).x
        mySVGPoint.y = pointsList.getItem(m).y
        mySVGPointTrans = mySVGPoint.matrixTransform(sCTM)
        pointsList.getItem(m).x=mySVGPointTrans.x
        pointsList.getItem(m).y=mySVGPointTrans.y
    }
    //---force removal of transform--
    myPoly.setAttribute("transform","")
    myPoly.removeAttribute("transform")
}

Given:

  • Your untransformed SVG points

  • And your SVG transformation matrix

You can mathematically apply the transform to those points.

The resulting points are the transformed XY--the "normalized" XY.

The resulting points will draw to canvas in the same locations as the transformed SVG points.

The resulting points won't require the SVG transformation to be draw in the correct locations.

Here's the code that applies a transformation matrix to "normalize" a point:

function simplifyTransform(point,matrix){
    simpleX = point.x * matrix[0] + point.y * matrix[2] + matrix[4];
    simpleY = point.x * matrix[1] + point.y * matrix[3] + matrix[5];
    return({x:simpleX,y:simpleY});
}

Here's an illustration:

enter image description here

Here's code and a Demo: http://jsfiddle.net/m1erickson/ushWr/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    var x=50;
    var y=50;
    var h=30;
    var w=50;
    var matrix=[1.25,.75,.75,1.25,20,20];

    ctx.save();
    ctx.transform(matrix[0],matrix[1],matrix[2],matrix[3],matrix[4],matrix[5],matrix[6]);
    ctx.fillStyle="red";
    ctx.fillRect(x,y,w,h);
    ctx.restore();


    // make an array of the 4 corners of the rectangle
    var points=[];
    points.push({x:x,y:y});
    points.push({x:x+w,y:y});
    points.push({x:x+w,y:y+h});
    points.push({x:x,y:y+h});

    // get the transformed rectangle's corners in untransformed space
    var rect=simplifyPoly(points,matrix);

    // stroke the untransformed rectangle
    ctx.save();
    ctx.strokeStyle="green";
    ctx.lineWidth=2;
    ctx.moveTo(rect[0].x,rect[0].y);
    for(var i=1;i<rect.length;i++){
        ctx.lineTo(rect[i].x,rect[i].y);
    }
    ctx.closePath();
    ctx.stroke();
    ctx.restore();

    function simplifyTransform(point,matrix){
        simpleX = point.x * matrix[0] + point.y * matrix[2] + matrix[4];
        simpleY = point.x * matrix[1] + point.y * matrix[3] + matrix[5];
        return({x:simpleX,y:simpleY});
    }

    function simplifyPoly(points,matrix){
        var simplePoints=[];
        for(var i=0;i<points.length;i++){
            simplePoints.push(simplifyTransform(points[i],matrix));
        }
        return(simplePoints);
    }

}); // end $(function(){});
</script>

</head>

<body>
    <h4>The red fill is drawn using untransformed points plus a transform<br>The green stroke is drawn using the "simplified" points--no transform involved.</h4>
    <canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

After a more elaborate search I'd found Peter Collingridge experiments on http://petercollingridge.appspot.com/svg-editor/

In Illustrator it is possible to change transformed rects in poly's with Object/Path/Simplify

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top