I have a free clip art SVG file originally created in Inkscape which I'm making modifications to for use in a Windows 8 JavaScript game. It contains numerous instances of a path with a matrix transform applied on a surrounding group, like this:
<g transform="matrix(0.443,0.896,-0.896,0.443,589.739,-373.223)">
<path d="M486,313s27-9,43-29l26,4,1,23-22,5s-25-6-48-3z" />
</g>
I want to flatten that transform by applying it in advance to the path in Inkscape, to reduce browser work during animation. However when I plug the 6 matrix values into the A B C D E F parameters in Inkscape and apply it, it gives the path a completely different rotation and scaling to what the IE10 engine does.
I have checked numerous times that I have the 6 values mapped correctly. What am I doing wrong?
EDIT: OK, here are before and after screenshots from IE10 and Inkscape. For the IE10 case, the SVG resides directly inside the body of an otherwise empty HTML document (the rendering is exactly the same in Firefox). In Inkscape, I simply opened the "before" SVG file which contains only the path element, selected the path, and plugged in the 6 matrix transform values into Object > Transform > Matrix. I know very little about matrices, I just want to be able to pre-apply these transformations in the same way the browser does, and ideally to understand why there is a difference in Inkscape. Thanks.
When typing the transformation matrix params in Inkscape, make sure you have "Edit current matrix" checked, since if you apply a new transformation matrix to an object, you're actually multiplying this new matrix with the existing transformation matrix of the object, so make sure you edit it instead.
How to recalculate everything yourself.
First let us try and understand the transformation matrices a bit.
A transformation matrix is a quick and clever tool for applying affine transformations ( transformation which preserves straight lines) to a vector.
So, if you have a vector (say, 2d coordinates) and a transformation matrix, and multiply the two together, you will end up with transformed coordinates, with the transformations defined in the transformation matrix, applied.
Calculating x'
and y'
is done like so:
x' = a*x + c*y + e
y' = b*x + d*y + f
Next, we need to understand the svg format a bit.
According to the w3c svg spec the matrix
transform takes exactly those 6 parameters (a,b,c,d,e,f) as arguments.
Therefore, from your example,
<g transform="matrix(0.443,0.896,-0.896,0.443,589.739,-373.223)">
we have the following transformation matrix params:
a=0.443
b=0.896
c=-0.896
d=0.443
e=589.739
f=-373.223
Now, if we have the following example coordinate: x=27, y=-9
, we can transform it, by using the previously defined transformation matrix like this:
x' = a*x + c*y + e
x' = 0.443*27 + -0.896*-9 + 589.739
x' = 609.764
y' = b*x + d*y + f
y' = 0.896*27 + 0.443*-9 -373.223
y' = −353.018
Neat, huh? You can get more info here
But that is not all. We also need to understand svg path data.
According to the w3c svg path dspecification each letter in the path data represents an instruction. And each of the number pairs that follow an instruction represent a coordinate value.
From your example, we have the following path:
<path d="M486,313s27-9,43-29l26,4,1,23-22,5s-25-6-48-3z" />
Here we see that this path object uses one absolute moveto
instruction (uppercase M), a relative smooth curveto
cubic Bézier curve (lowercase s), a relative lineto
instruction (lowercase l), and another relative smooth curveto
cubic Bézier curve instruction, followed by a closepath
instruction (lowercase z).
M486,313
is translated to absolute moveto x=486, y=313
s27-9,43-29
is a bit more complicated to read because some comas are omitted because they're not needed if the negative number is negative, so the minus sign acts as a coma - anyways, it translates to relative smooth bezier curveto x=27, y=-9, x=43, y=-29 (one destination point and one control point)
And so on.
So, how do we apply and remove the transformation matrix from your svg group? Like so:
// we read the transformation matrix params
// <g transform="matrix(0.443,0.896,-0.896,0.443,589.739,-373.223)">
a=0.443
b=0.896
c=-0.896
d=0.443
e=589.739
f=-373.223
// we read the path data, and transform each instruction
// <path d="M486,313s27-9,43-29l26,4,1,23-22,5s-25-6-48-3z" />
M486,313 Absolute move to
x' = a*x + c*y + e = a*486 + c*313 + e = 524.589
y' = b*x + d*y + f = b*486 + d*313 + f = 200.892
Move to instruction is now M524.589,200.892
S27-9,43-29 - smooth curveto, repeat the same process for each coordinate, but set the e
and f
(translation parameters) to 0, since it's a relative instruction not an absolute.
It is now
s20.025,20.205,45.033,25.680999999999997
l26,4,1,23-22,5
will become
l7.934000000000001,25.067999999999998,-20.165,11.085,-14.226,-17.497
s-25-6-48-3
will become
s-5.698999999999999,-25.058000000000003,-18.576,-44.337
And z will remain z
So the resulting transformed path will be:
<path d="M524.589,200.892s20.025,20.205,45.033,25.680999999999997l7.934000000000001,25.067999999999998,-20.165,11.085,-14.226,-17.497s-5.698999999999999,-25.058000000000003,-18.576,-44.337z" />
I hope this makes sense to you.