20
$\begingroup$

How can I extract rotation and scale values from a 2D transformation matrix?

matrix = [1, 0, 0, 1, 0, 0]

matrix.rotate(45 / 180 * PI)
matrix.scale(3, 4)
matrix.translate(50, 100)
matrix.rotate(30 / 180 * PI)
matrix.scale(-2, 4)

Now my matrix have values [a, b, c, d, tx, ty]. Lets forget about the processes above and imagine that we have only the values a, b, c, d, tx, and ty. How can I find final rotation and scale values?

$\endgroup$
29
$\begingroup$

Essentially you need to solve the following

$$\left[\begin{array}{ccc} \mathrm{a} & \mathrm{b} & \mathrm{tx}\\ \mathrm{c} & \mathrm{d} & \mathrm{ty}\end{array}\right]=\left[\begin{array}{ccc} s_{x}\cos\psi & -s_{x}\sin\psi & x_{c}\\ s_{y}\sin\psi & s_{y}\cos\psi & y_{c}\end{array}\right]$$

where $s_x$, $s_y$ are the scalings, $x_c$, $y_c$ is the translation and $\psi$ is the rotation angle. The results I get are:

$$x_{c}=\mathrm{tx}$$ $$y_{c}=\mathrm{ty}$$ $$s_{x}=\mathrm{sign(a)\,}\sqrt{\mathrm{a}^{2}+\mathrm{b}^{2}}$$ $$s_{y}=\mathrm{sign(d)\,}\sqrt{\mathrm{c}^{2}+\mathrm{d}^{2}}$$ $$\tan\psi=-\frac{\mathrm{b}}{\mathrm{a}}=\frac{\mathrm{c}}{\mathrm{d}}$$

So the angle is either $\psi = {\rm atan2}(-b,a)$ or $\psi = {\rm atan2}(c,d)$

$\endgroup$
  • 1
    $\begingroup$ thank you but sx and sy will always be positive because of power 2. what about negative scale values? $\endgroup$ – Tolgahan Albayrak Dec 5 '10 at 22:20
  • 1
    $\begingroup$ You can check the sign of the scalings by looking at the sign of $\mathrm{a}$ for $s_x$ and the sign of $\mathrm{d}$ for $s_y$, since the $\cos\psi$ operation is not expected to produce a negative number for $-\pi/2\leq\psi\leq\pi/2$ which is what is returned by the $\arctan$ function. $\endgroup$ – ja72 Dec 5 '10 at 22:29
  • 1
    $\begingroup$ See edit now... $\endgroup$ – ja72 May 6 '14 at 12:44
  • 1
    $\begingroup$ Scaling one direction negative and rotating by 180° are equivalent operations when looking at only one point. You will need more that one point to discern rigid body motion. $\endgroup$ – ja72 May 7 '14 at 12:49
  • 2
    $\begingroup$ So the implementation of the rotation matrix may not be $\left[\begin{array}{ccc} s_{x}\cos\psi & -s_{x}\sin\psi & x_{c}\\ s_{y}\sin\psi & s_{y}\cos\psi & y_{c}\end{array}\right]$ in the programming language you are using. There are left hand and right hand rotation conventions as well as pre or post multiplication operations. Without more details (give out matrix values) after each operation there is no way to correctly answer this question. $\endgroup$ – ja72 May 7 '14 at 13:42
3
$\begingroup$

Scale and Rotation Extraction for Action Script 3


package nid.utils 
{
    import flash.geom.Matrix;
    import flash.geom.Point;
    import nid.geom.DMatrix;
    /**
     * ...
     * @author Nidin P Vinayakan
     */
    public class MatrixConvertor 
    {
        public static const degree:Number = 180 / Math.PI;
        public static const radian:Number = Math.PI / 180;

        public function MatrixConvertor()
        {

        }
        public static function convert(mat:Matrix):DMatrix 
        {
            var dmat:DMatrix = new DMatrix(mat.a, mat.b, mat.c, mat.d, mat.tx, mat.ty);
            var rad:Number;
            var deg:Number;
            var sign:Number;
            /**
             * scaleX = √(a^2+c^2)
             * scaleY = √(b^2+d^2)
             * rotation = tan^-1(c/d) = tan^-1(-b/a) it will not work sometimes 
             * rotation = a / scaleX  = d / scaleY
             */
            with (dmat)
            {
                scaleX = Math.sqrt((a * a) + (c * c));
                scaleY = Math.sqrt((b * b) + (d * d));

                sign = Math.atan(-c / a);
                rad  = Math.acos(a / scaleX);
                deg  = rad * degree;

                if (deg > 90 && sign > 0)
                {
                    rotation = (360 - deg) * radian;
                }
                else if (deg < 90 && sign < 0)
                {
                    rotation = (360 - deg) * radian;
                }
                else
                {
                    rotation = rad;
                }
                rotationInDegree = rotation * degree;
            }
            return dmat;
        }
    }

}

/**
* DMatrix Class
*/
package nid.geom 
{
    import flash.geom.Matrix;
    /**
     * ...
     * @author Nidin P Vinayakan
     */
    public class DMatrix extends Matrix
    {
        public var rotation:Number=0;
        public var rotationInDegree:Number=0;
        public var scaleX:Number=1;
        public var scaleY:Number=1;

        public function DMatrix(a:Number=1, b:Number=0, c:Number=0, d:Number=1, tx:Number=0, ty:Number=0)
        {
            super(a, b, c, d, tx, ty);
        }

    }

}
$\endgroup$
  • 1
    $\begingroup$ Scale and Rotation Extraction for Action Script 3 Thank you for your answer,but i can't understand your answer. for example: what's meaning of the a c degree, radian and so on.... thank you! $\endgroup$ – user19914 Nov 22 '11 at 7:33
  • $\begingroup$ too bad this answer is incomplete. $\endgroup$ – bigp Aug 21 '13 at 15:41
  • $\begingroup$ i calculated the rotation using float rAngle = Math.round(Math.atan2(v[Matrix.MSKEW_X], v[Matrix.MSCALE_X]) * (180 / Math.PI)); need to multiply with -1 to get the correcct value . why is tha happen? $\endgroup$ – DKV Nov 15 '16 at 12:28
  • 1
    $\begingroup$ Hats off to you sir, You saved my week. $\endgroup$ – Rahul Prasad Nov 17 '16 at 16:50
1
$\begingroup$

The term for this is matrix decomposition. Here is a solution that includes skew as described by Frédéric Wang.

It operates on a 2d matrix defined as such:

$$\left[\begin{array}{ccc} \mathrm{a} & \mathrm{c} & \mathrm{tx}\\ \mathrm{b} & \mathrm{d} & \mathrm{ty}\end{array}\right]$$

function decompose_2d_matrix(mat) {
  var a = mat[0];
  var b = mat[1];
  var c = mat[2];
  var d = mat[3];
  var e = mat[4];
  var f = mat[5];

  var delta = a * d - b * c;

  let result = {
    translation: [e, f],
    rotation: 0,
    scale: [0, 0],
    skew: [0, 0],
  };

  // Apply the QR-like decomposition.
  if (a != 0 || b != 0) {
    var r = Math.sqrt(a * a + b * b);
    result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
    result.scale = [r, delta / r];
    result.skew = [Math.atan((a * c + b * d) / (r * r)), 0];
  } else if (c != 0 || d != 0) {
    var s = Math.sqrt(c * c + d * d);
    result.rotation =
      Math.PI / 2 - (d > 0 ? Math.acos(-c / s) : -Math.acos(c / s));
    result.scale = [delta / s, s];
    result.skew = [0, Math.atan((a * c + b * d) / (s * s))];
  } else {
    // a = b = c = d = 0
  }

  return result;
}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy