xmlgraphics-batik-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Andrew Plotkin <erkyr...@eblong.com>
Subject RE: Extracting information from a transformation matrix
Date Thu, 01 Jun 2006 16:27:53 GMT
On Thu, 1 Jun 2006, Bishop, Michael W. CONTR J9C880 wrote:

> Wow, this is going to make me take another close look at the transformation matrix for
SVG.  Let's start with an example:
> transform="translate(x, y) scale(sx, sy) rotate(t, cx, cy)"
> The above is the only kind of transform I maintain in my application.
> If the user drags an element around the screen, it directly affects 
> translate. 
> If the user scales the element, it affects scale which indirectly 
> affects translate. 
> If the user rotates the element, I believe it affects scale which also 
> affects translate. 
> What I want to do is be able to break apart the above to find out the 
> values I wanted before.  The first thing I need to go is get a 
> representation of the above into the matrix values (a, b, c, d, e, f).

Ok, let's work through it.

Here is a simple routine that gets the transform of an object relative to 
its parent:

function dump(id) {
   var el = document.getElementById(id);
   var parent = el.parentNode;
   var matrix = el.getTransformToElement(parent);
   alert(matrix.a + " " + matrix.c + " " + matrix.e
     + "\n" + matrix.b + " " + matrix.d + " " + matrix.f);

Some samples, in the standard format:
   a c e
   b d f

<ellipse id="none" rx="1" ry="2" />
   1 0 0
   0 1 0

<ellipse id="trans" transform="translate(4,4)" rx="1" ry="2" />
   1 0 4
   0 1 4

<ellipse id="scale" transform="scale(3)" rx="1" ry="2" />
   3 0 0
   0 3 0

<ellipse id="rot" transform="rotate(60)" rx="1" ry="2" />
   0.5 -0.8660253882408142 0
   0.8660253882408142 0.5 0

(Note that 0.8660253882408142 is sqrt(3)/2, or sin(60). 0.5 is cos(60). 
Now you know where those numbers come from.)

So an object with a translate+scale+rotate:

<ellipse id="all" transform="translate(4,4) scale(3) rotate(60)" rx="1"
   ry="2" />
   1.5 -2.598076105117798 4
   2.598076105117798 1.5 4

Now this is easy to undo. We're going to pull the transforms off in 
forwards order (the *same* order they're listed in the transform 

Translate is the (e,f) values directly:
   translate = (4,4).

Now remove this by setting e and f to zero:

   1.5 -2.598076105117798 0
   2.598076105117798 1.5 0

The scale is the magnitude of (a,b). Or (c,d) or (a,c) -- all the same. 
Pythagorean theorem:

   scale = sqrt(1.5*1.5 + -2.598076105117798*-2.598076105117798) = 3

Now remove this by dividing each element of the matrix by 3:

   0.5 -0.8660253882408142 0
   0.8660253882408142 0.5 0

What's left is the pure rotation -- you can see it's the same as the "rot" 
matrix above. To get the angle out, you'll want to use an atan2 function; 
in Javascript this is Math.atan2. The result of that is radians, so we 
also have to convert to degrees:

   rotate = atan2(b,a) * 180 / 3.1415926 = 60

Now, the rotation in this example is a rotation around the origin. You 
asked about the more complex rotation "rotate(t, cx, cy)". Unfortunately, 
there's no way to get that information! If we consider the object

<ellipse id="sall" transform="translate(4,4) scale(3) rotate(60,1,2)"
   rx="1" ry="2" />

and apply the above procedure, you'll get the result:

Or, if you like,

<ellipse id="sall2" transform="translate(10.69615268,4.401909828) scale(3)
   rotate(60)" rx="1" ry="2" />

The problem is, this is the right answer! These two transforms produce the 
*same* matrix -- the same a,b,c,d,e,f values. And there are an infinite 
other number of "translate(x, y) scale(sx, sy) rotate(t, cx, cy)" 
transforms that produce that matrix. To get a single solution, you *have* 
to assume that cx and cy are zero. Otherwise you're trying to get 
seven unknowns out of six variables, which is impossible.

(If you assume cx and cy are zero, you get five unknowns out of six 
variable. The sixth unknown is skew, or stretching on an axis; the 
procedure above assumes there's none of that either.)


"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the borogoves..."
If the Bush administration hasn't shipped you to Syria for interrogation,
it's for one reason: they don't feel like it. Not because you're patriotic.

To unsubscribe, e-mail: batik-users-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: batik-users-help@xmlgraphics.apache.org

View raw message