CSS transforms and <la-tex> Custom Element

About this demo

Decomposition of 3D matrix

Number of digits after decimal point:

Enter the matrix M = ( ) or select an example

Here is how the transformed transform matrix M looks like with extra perspective:

A decomposition written as a CSS transform property is A decomposition written as a product of matrices is:

M = \underset{\text{translation}}{\underbrace{ \begin{pmatrix} I_3 & T \\ 0 & 1 \end{pmatrix}}} \underset{\text{rotation}}{\underbrace{ \begin{pmatrix} Q & 0\\ 0 & 1\end{pmatrix}} } \underset{\text{scale}}{\underbrace{ \begin{pmatrix} S & 0\\ 0 & 1 \\ \end{pmatrix}}} \underset{\text{"unscaled"}}{\underbrace{ \begin{pmatrix} U & 0\\ 0 & 1 \end{pmatrix}}} \underset{\text{perspective}}{\underbrace{ \begin{pmatrix} I_3 & 0 \\ P & 1 \end{pmatrix}}}

with , , , and .

The decomposition is performed using this JavaScript program using an in-place algorithm. The mathematical description is explained below.

Syntax and matrices

The transform matrices from CSS Transforms Module Level 1 and CSS Transforms Module Level 2 are the following:

CSS syntax 4x4 matrix
General 3D matrix
matrix3d(m11, m12, m13, m14,
         m21, m22, m23, m24,
         m31, m32, m33, m34,
         m41, m42, m43, m44)
M = \begin{pmatrix} m_11 & m_21 & m_31 & m_41 \\ m_12 & m_22 & m_32 & m_42 \\ m_13 & m_23 & m_33 & m_43 \\ m_14 & m_24 & m_34 & m_44 \\ \end{pmatrix} = \begin{pmatrix} A & T \\ P & m_44 \end{pmatrix}
where A= \begin{pmatrix} m_11 & m_21 & m_31 \\ m_12 & m_22 & m_32 \\ m_13 & m_23 & m_33 \\ \end{pmatrix} , P = \begin{pmatrix} m_14 & m_24 & m_34 \end{pmatrix} , and T = \begin{pmatrix} m_41 \\ m_42 \\ m_43 \\ m_44 \end{pmatrix}
General 2D matrix matrix(a, b, c, d, e, f) {M_2(a, b, c, d, e, f)} = \begin{pmatrix} a & c & 0 & e \\ b & d & 0 & f \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}
Translations
  • translate3d(tx, ty, tz)
  • translateZ(tz) (t_x = t_y = 0)
  • translate(tx, ty) (t_z = 0)
  • translate(tx) (t_y = t_x = 0)
  • translateX(tx) (t_y = t_z = 0)
  • translateY(ty) (t_x = t_z = 0)
{{\operatorname{Translate}}(t_x, t_y, t_z)} = \begin{pmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{pmatrix}
Scales
  • scale3d(sx, sy, sz)
  • scaleZ(sz) (s_x = s_y = 1)
  • scale(sx, sy) (s_z = 1)
  • scale(sx) (s_y = s_x, s_z = 1)
  • scaleX(sx) (s_y = s_z = 1)
  • scaleY(sy) (s_x = s_z = 1)
{{\operatorname{Scale}}(s_x, s_y, s_z)} = \begin{pmatrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}
Rotations
  • rotate3d(x, y, z, α)
  • rotateX(α) (y = z = 0, x = 1)
  • rotateY(α) (x = z = 0, y = 1)
  • rotateZ(α) (x = y = 0, z = 1)
  • rotate(α) (x = y = 0, z = 1)
{R(x,y,z, α)} = \begin{pmatrix} {1 - 2 \left(Y^2 + Z^2\right) S^2} & {2 \left(X Y S^2 - Z SC\right)} & {2 \left(X Z S^2 + Y SC\right)} & 0 \\ {2 \left(X Y S^2 + Z SC\right)} & {1 - 2 \left(X^2 + Z^2\right) S^2} & {2 \left(Y Z S^2 - X SC\right)} & 0 \\ {2 \left(X Z S^2 - Y SC\right)} & {2 \left(Y Z S^2 + X SC\right)} & {1 - 2 \left(X^2 + Y^2\right) S^2} & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}
where {(X, Y, Z)} = {\frac{1}{\sqrt{x^2+y^2+z^2}} {(x, y, z)}} is the normalized rotation axis, C = \cos \left(\frac{α}{2}\right) and S = \sin \left(\frac{α}{2}\right).
Perspective perspective(d) {{\operatorname{Persp}}(d)} = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & {-\frac{1}{d}} & 1 \end{pmatrix}
Skews
  • skew(α, β)
  • skewX(α) (β = 0)
  • skewY(β) (α = 0)
{K(α, β)} = \begin{pmatrix} 1 & {\tan(α)} & 0 & 0 \\ {\tan(β)} & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}

The determinants are listed in the following table table. When it is nonzero, the matrix is invertible and the inverse is provided in the last column.

Determinant Inverse
General 3D matrix \det M M^{-1} if {\det M} \neq 0
General 2D matrix ad - bc {{\operatorname{Scale}}\left(\frac{1}{ad-bc},\frac{1}{ad-bc}, 1\right)} {M_2(d,-b,-c,a,{cf-de},{be-af})}
Translations 1 {\operatorname{Translate}}(-t_x, -t_y, -t_z)
Scales s_x s_y s_z s_w {{\operatorname{Scale}}\left(\frac{1}{s_x}, \frac{1}{s_y}, \frac{1}{s_z}\right)}
Rotations 1 R(x, y, z, -\alpha)
Perspective 1 {{\operatorname{Persp}}(-d)}
Skews 1 - {\tan \alpha \tan \beta} {{\operatorname{Scale}}\left( \frac{1}{1 - {\tan \alpha \tan \beta}}, \frac{1}{1 - {\tan \alpha \tan \beta}}, 1\right)} {K(-α, -β)}

Applying a transform

Let's consider a matrix

M = \begin{pmatrix} A & T \\ P & m_44 \end{pmatrix}

describing a transformation in the projective space. A point of homogeneous coordinates v = {(x, y, z, 1)}^{\mathrm T} is sent by this matrix to

Mv = \begin{pmatrix} {Av + T} \\ {Pv + m_44} \end{pmatrix}

If Pv + m_44 \neq 0, that image corresponds to the following point in homogeneous coordinates

\begin{pmatrix} \frac{Av + T}{Pv + m_44} \\ 1 \end{pmatrix}

If Pv + m_44 = 0, then either Av + T \neq 0 and this corresponds to the point at infinity in the direction of the vector Av + T, or Av + T = 0 and this does not define any point in the projective space.

Note that if M is multiplied by any nonzero scalar \lambda then so are A, T, P, m_44 and \lambda M defines exactly the same transform as M per the description above.

Normalization of m_44

Except for matrix3d all the basic CSS transforms correspond to a matrix with m_44 = 1 per the table above. The inverse of such a matrix is also of this form and so is the product of two such matrices:

\begin{pmatrix} A_1 & T_1 \\ P_1 & 1 \end{pmatrix} \begin{pmatrix} A_2 & T_2 \\ P_2 & 1 \end{pmatrix} = \begin{pmatrix} {A_1A_2} & {A_1T_2+T_1} \\ {P_1A_2 + T_2} & 1 \end{pmatrix}

Let's consider a matrix

M = \begin{pmatrix} A & T \\ P & m_44 \end{pmatrix}

If m_44 = 0 then M cannot be decomposed as a product of a \lambda \neq 0 and other simpler matrices defined by the CSS syntax, since the coefficent at position 4,4 of such a product should be \lambda \neq 0 = m_44. If additionally, P = 0, this corresponds to the transform sending v = {(x, y, z, 1)}^{\mathrm T} to the point at infinity in the direction of the vector Av + T (or is undefined if Av + T = 0).

Let's consider the following matrix:

{\Sigma} = \begin{pmatrix}0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\\ 1 & 0 & 0 & 0\end{pmatrix}

Right-multiplying M by \Sigma circularly shifts (from left to right) the columns of M. Similarly, \Sigma^{n} is a matrix with only 0's and 1's that circularly shifts |n| times the columns of M (in a direction depending on the sign of the integer).

If P \neq 0 and m_44 \neq 0, then let's pick the smallest n \in {\{0, 1, 2, 3\}} such that the bottom right coefficient {\left(M \Sigma^n\right)}_44 is nonzero. Then M can be decomposed as follows:

M = {{\left(M \Sigma^n\right)}_44 M' {\Sigma^{-n}}}

where M' = \frac{1}{{\left(M \Sigma^n\right)}_44} M \Sigma^n is of the form

{M'} = {\begin{pmatrix} A' & T' \\ P' & 1 \end{pmatrix}}

and the n first coefficients of P' are zeros.

Factoring T, P out

Let's now consider matrices of the form

M = \begin{pmatrix} A & T \\ P & 1 \end{pmatrix}

We note that scales, rotations, perspectives and skews all belongs to the subset of matrices for which T = 0 (lower triangular by block) and this subset is stable by composition, inversion or multiplication by a nonzero scalar. However, it is easy to see that:

M ={ \begin{pmatrix} 1_3 & T \\ 0 & 1 \end{pmatrix} \begin{pmatrix} A & 0 \\ P & 1 \end{pmatrix}}

Then scales, rotations and skews all belong to the set of matrices for which T = P = 0 (diagonal by block) and this subset is stable by composition, inversion or multiplication by a nonzero scalar. Again, one can easily check that

M ={ \underset{\text{translation}}{\underbrace{ \begin{pmatrix} I_3 & T \\ 0 & 1 \end{pmatrix}}} \begin{pmatrix} A & 0 \\ 0 & 1 \end{pmatrix} \begin{pmatrix} I_3 & 0 \\ P & 1 \end{pmatrix}}

which gives a straightforward extraction of the translation part. If P = {(p_x, p_y, p_z)} \neq 0 then one of the following matrix is well-defined:

\begin{aligned} A_x &= \begin{pmatrix} -\frac{py}{px} & -\frac{pz}{px} & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 0 \\ \end{pmatrix} \\ A_y &= \begin{pmatrix} 0 & 1 & 0 \\ -\frac{pz}{py} & -\frac{px}{py} & 1 \\ 1 & 0 & 0 \end{pmatrix} \\ A_z &= \begin{pmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ -\frac{px}{pz} & -\frac{py}{pz} & 1 \\ \end{pmatrix} \\ \end{aligned}

These matrices are of determinant 1 since they can be obtained by permutting the rows (or columns) of a triangular matrix with only 1's on the diagonal. Moreover, one can check that

\begin{aligned} \begin{pmatrix} I_3 & 0 \\ P & 1 \end{pmatrix} &= { \begin{pmatrix} A_x & 0 \\ 0 & 1 \end{pmatrix} {{\operatorname{Persp}}\left(\frac{1}{p_x}\right)} \begin{pmatrix} A_x^{-1} & 0 \\ 0 & 1 \end{pmatrix} }\\ \begin{pmatrix} I_3 & 0 \\ P & 1 \end{pmatrix} &= { \begin{pmatrix} A_y & 0 \\ 0 & 1 \end{pmatrix} {{\operatorname{Persp}}\left(\frac{1}{p_y}\right)} \begin{pmatrix} A_y^{-1} & 0 \\ 0 & 1 \end{pmatrix} }\\ \begin{pmatrix} I_3 & 0 \\ P & 1 \end{pmatrix} &= { \begin{pmatrix} A_z & 0 \\ 0 & 1 \end{pmatrix} {{\operatorname{Persp}}\left(\frac{1}{p_z}\right)} \begin{pmatrix} A_z^{-1} & 0 \\ 0 & 1 \end{pmatrix} } \end{aligned}

For P \neq 0, let d, A_P be the first of \frac{1}{p_z}, \frac{1}{p_y}, \frac{1}{p_x} A_z, A_y, A_z that is well_defined then one can write:

M ={ \underset{\text{translation}}{\underbrace{ \begin{pmatrix} I_3 & T \\ 0 & 1 \end{pmatrix}}} \begin{pmatrix} AA_P^{-1} & 0 \\ 0 & 1 \end{pmatrix} {{\operatorname{Persp}}(d)} \begin{pmatrix} A_P & 0 \\ 0 & 1 \end{pmatrix}}

and one can extend that to P = 0 using the convention {{\operatorname{Persp}}(\infty)} = I_n and taking A_0 = I_3, d = \infty.

QR decomposition of A

Let's consider a matrix

A= \begin{pmatrix} a_11 & a_21 & a_31 \\ a_12 & a_22 & a_32 \\ a_13 & a_23 & a_33 \\ \end{pmatrix}

Using the QR factorization, one can find an orthogonal matrix Q and an upper triangular matrix R such that A = QR. There are several ways to calculate this decomposition e.g. using the Gram–Schmidt process or Givens rotations. Our algorithm will use the Householder procedure which is simple, numerically stable and works for a non-invertible matrix A. Our matrix is small, the decomposition only requires at most two reflections and one scale.

As a quick reminder, one can define for any unitary vector v (i.e. v^\mathrm T v = 1) the matrix P_v = I_3 - 2v v^{\mathrm T}. One can verify that

{P_v v} = {v - 2v} = -v {P_v v^⟂} = {v^⟂ - 0} = v^⟂ \text{ for any } {v^⟂ ⟂ v} P_v^{\mathrm T} = I_3^{\mathrm T} - 2{(v^{\mathrm T})}^{\mathrm T}v^{\mathrm T} = I_3 - 2v v^{\mathrm T} = P_v {P_v P_v^{\mathrm T}} = P_v^2 = I_3^2 - {2 {(2 v v^\mathrm T)}} + {4 {(v v^\mathrm T v v^\mathrm T)}} = I_3

The two first equalities show that P_v is the reflection about the plan orthogonal to v and that its eigenvalues are 1 (with mutplicity 2) and -1 (with multiplicity 1) and so \det P_v = -1. The two last equalities show that P_v is an orthogonal matrix.

Let's start with the first step of Householder's algorithm. Let x_1 = \begin{pmatrix} a_11 \\ a_12 \\ a_13 \\ \end{pmatrix}. Suppose one of a_12 or a_13 is nonzero. Then let \alpha_1 = \pm \sqrt{x_1^{\mathrm T} x_1} \neq 0 with the sign chosen to maximize (and in particular make it nonzero) the absolute value of \alpha_1 - a_11. Then u_1 = x_1 - \begin{pmatrix} \alpha_1 \\ 0 \\ 0 \\ \end{pmatrix} satisfies u_1^{\mathrm T} u_1 = 2 \alpha_1 {(\alpha_1 - a_11)} \neq 0 and the unitary vector v_1 = \frac{u_1}{\sqrt{u_1^{\mathrm T} u_1}} satisfies:

\begin{aligned} P_{v_1} x_1 &= {\left(I - 2\frac{u_1 u_1^{\mathrm T}}{u_1^{\mathrm T} u_1}\right) x_1} \\ &= {x_1 - 2\frac{u_1 {(\alpha_1 {(\alpha_1 - a_11)})}}{2 \alpha_1 {(\alpha_1 - a_11)}}} &= {x_1 - u_1} \\ &= {\begin{pmatrix} \alpha_1 \\ 0 \\ 0 \\ \end{pmatrix}} \end{aligned}

As a consequence, one can write

{Q_1 A} = \begin{pmatrix} \alpha_1 & b_21 & b_31 \\ 0 & b_22 & b_32 \\ 0 & b_23 & b_33 \\ \end{pmatrix}

with Q_1 = P_{v_1}. If a_12 = a_13 = 0 then, one can instead choose Q_1 = I_3 and \alpha_1 = a_{11} to obtain the same form.

Similarly, if b_32 \neq 0 then we consider x_2 = \begin{pmatrix} 0 \\ b_22 \\ b_23 \\ \end{pmatrix}, \alpha_2 = \pm \sqrt{x_2^{\mathrm T} x_2} \neq 0 with the sign chosen to maximize \left|\alpha_2 - b_22\right|, u_2 = x_2 - \begin{pmatrix} 0 \\ \alpha_2 \\ 0 \\ \end{pmatrix} and finally v_2 = \frac{u_2}{\sqrt{u_2^{\mathrm T} u_2}} and Q_2 = P_{v_2}. If b_32 \neq 0 then we consider instead Q_2 = I_3 and \alpha_2 = b_{22}. In any case, one can write:

{Q_2 Q_1 A} = \begin{pmatrix} \alpha_1 & c_21 & c_31 \\ 0 & \alpha_2 & c_32 \\ 0 & 0 & c_33 \\ \end{pmatrix}

Finally, one can define the orthogonal matrix Q_3 = \begin{pmatrix} \det{(Q_2 Q_1)} \epsilon_2 \epsilon_3 & 0 & 0 \\ 0 & \epsilon_2 & 0 \\ 0 & 0 & \epsilon_3 \\ \end{pmatrix} with \epsilon_2, \epsilon_3 \in {\{-1, 1\}} such that \epsilon_3 = 1 if and only if c_33 \geq 0 and \epsilon_2 = 1 if and only if \alpha_2 \geq 0. Then we obtain

R = {Q_3 Q_2 Q_1 A} = \begin{pmatrix} r_11 & r_21 & r_31 \\ 0 & r_22 & r_32 \\ 0 & 0 & r_33 \\ \end{pmatrix}

where r_22, r_33 are nonnegative. By construction, Q = Q_1 Q_2 Q_3 is an orthogonal matrix of determinant 1 and so is a 3D rotation, which gives the following factorization:

\begin{pmatrix} A & 0\\ 0 & 1\end{pmatrix} = { \underset{\text{rotation}}{\underbrace{ \begin{pmatrix} Q & 0\\ 0 & 1\end{pmatrix}} } \begin{pmatrix} R & 0\\ 0 & 1 \end{pmatrix} }

Unscaling the diagonal of R

Let's consider the following upper triangular matrix:

R = \begin{pmatrix} r_11 & r_21 & r_31 \\ 0 & r_22 & r_32 \\ 0 & 0 & r_33 \\ \end{pmatrix}

From that, we define a diagonal matrix

S = \begin{pmatrix} s_1 & 0 & 0 \\ 0 & s_2 & 0 \\ 0 & 0 & s_3 \\ \end{pmatrix}

and an "unscaled" upper triangular matrix

U = \begin{pmatrix} \frac{r_11}{s_1} & \frac{r_21}{s_1} & \frac{r_31}{s_1} \\ 0 & \frac{r_22}{s_2} & \frac{r_32}{s_2} \\ 0 & 0 & \frac{r_33}{s_3} \\ \end{pmatrix}

where s_i = r_ii if nonzero and 1 otherwise.

R = {SU} = \underset{\text{scale}}{\underbrace{ \begin{pmatrix} s_1 & 0 & 0 \\ 0 & s_2 & 0 \\ 0 & 0 & s_3 \\ \end{pmatrix} }} \underset{\text{upper triangular}}{\underbrace{ \begin{pmatrix} u_11 & u_21 & u_31 \\ 0 & u_22 & u_32 \\ 0 & 0 & u_33 \\ \end{pmatrix}}} = {US}

where the diagonal coefficients of U are only 0's or 1's.

Finding the rotation Q

In this section, we consider an orthogonal matrix

Q = \begin{pmatrix} q_11 & q_21 & q_31 \\ q_12 & q_22 & q_32 \\ q_13 & q_23 & q_33 \\ \end{pmatrix}

with \detQ = 1. Such a matrix corresponds to a 3D rotation and can thus can be described by a non-zero vector (x, y, z) and an angle \alpha. As described in the table above, it will then be written Q = R(x, y, z, \alpha) from the normalized rotation axis {(X, Y, Z)} = {\frac{1}{\sqrt{x^2+y^2+z^2}} {(x, y, z)}} as well as C = \cos \left(\frac{α}{2}\right) and S = \sin \left(\frac{α}{2}\right).

The trace of the rotation matrix allows to easily obtain the angle from the formula \alpha = \arccos\left(\frac{{{\operatorname{tr}} {Q}} - 1}{2}\right) \in {[0, \pi]} . Indeed, we have:

\begin{aligned} {\operatorname{tr}} {Q} + 1 &= q_11 + q_22 + q_33 + 1 \\ &= {\operatorname{tr}} {R(x, y, z, \alpha)} \\ &= 4 - 2S^2\left(Y^2+Z^2+X^2+Z^2 + X^2+Y^2\right) \\ &= 4{\left(1 - S^2\right)} \\ &= 4C^2 \\ &= 2\left(\cos{\alpha} + 1\right) \end{aligned}

Similarly, using different linear combinations of the elements of Q = R(x, y, z, \alpha) one obtains the following equalities:

\begin{aligned} 1 + q_11 - q_22 - q_33 &= {4S^2 X^2} & \text{ (1)} \\ 1 - q_11 + q_22 - q_33 &= {4S^2 Y^2} & \text{ (2)} \\ 1 - q_11 - q_22 + q_33 &= {4S^2 Z^2} & \text{ (3)} \\ q_12 - q_21 &= {4 ZSC} & \text{ (4)} \\ q_31 - q_13 &= {4 YSC} & \text{ (5)} \\ q_23 - q_32 &= {4 XSC} & \text{ (6)} \\ q_12 + q_21 &= {4 XYS^2} & \text{ (7)} \\ q_31 + q_13 &= {4 XZS^2} & \text{ (8)} \\ q_23 + q_32 &= {4 YZS^2} & \text{ (9)} \\ \end{aligned}

If \alpha = 0, then Q = I_3 is the identity and one can choose any rotation vector. Note that S = 0 so equalities (1-9) don't give any information.

Otherwise, 0 < \alpha \leq \pi so 0 < S \leq 1 and equalities (1-3) become:

\begin{aligned} {|X|} = \frac{\sqrt{1 + q_11 - q_22 - q_33}}{2S} \\ {|Y|} = \frac{\sqrt{1 + q_11 + q_22 - q_33}}{2S} \\ {|Z|} = \frac{\sqrt{1 + q_11 - q_22 + q_33}}{2S} \\ \end{aligned}

If additionally \alpha < \pi, then C \neq 0 and equalities (4-6) provides the sign of X, Y, Z. Otherwise can arbitrarily choose any sign for one of them and the sign of the others is implied by equalities (7-8).

In computers, 3D rotations are often just encoded as quaternion with 4 coordinates (C, XS, YS, ZS) and it is easy to rebuild the matrix Q = R(x, y, z, \alpha) from these values using the formula of the table above.

Wrapping up: Factorization of M

Summarizing what we have done so far, we are able to describe the transformation matrix in two ways. If the last row of the matrix is zero then it can be written:

v \mapsto \begin{pmatrix} QRv + T \\ 0 \end{pmatrix}

The image is well defined if Rv \neq -Q^{\mathrm T} T and is the point at infinity in the direction QRv + T. If R (or equivalently A) is invertible then clearly there is exactly one vector with undefined image. The general case is more complicate to express concisely, but is equivalent to solving the following triangular system:

\left\{ \begin{aligned} {r_33 v_3} &= {(-Q^{\mathrm T} T)}_3 \\ {r_22 v_2} &= {{(-Q^{\mathrm T} T)}_2 - r_32 v_3} \\ {r_11 v_1} &= {{(-Q^{\mathrm T} T)}_1 - r_31 v_3 - r_21 v_2} \\ \end{aligned} \right.

The second way is when the last row of the matrix is nonzero:

M = { \lambda \underset{\text{translation}}{\underbrace{ \begin{pmatrix} I_3 & T \\ 0 & 1 \end{pmatrix}}} \underset{\text{rotation}}{\underbrace{ \begin{pmatrix} Q & 0\\ 0 & 1\end{pmatrix}} } \underset{\text{scale}}{\underbrace{ \begin{pmatrix} s_1 & 0 & 0 & 0\\ 0 & s_2 & 0 & 0 \\ 0 & 0 & s_3 & 0\\ 0 & 0 & 0 & 1 \\ \end{pmatrix}}} \underset{\text{"unscaled" i.e. } u_{ii} \in {\{0,1\}} }{\underbrace{ \begin{pmatrix} u_11 & u_21 & u_31 & 0\\ 0 & u_22 & u_32 & 0 \\ 0 & 0 & u_33 & 0\\ 0 & 0 & 0 & 1 \\ \end{pmatrix}}} \underset{\text{"perspective"}}{\underbrace{ \begin{pmatrix} I_3 & 0 \\ P & 1 \end{pmatrix}}} \Sigma^{-n} }

with \lambda \in {\mathbb R}^* and \Sigma^{-n} is the matrix shifting n \in {\{0,1,2,3\}} times the columns of a left operand. In particular \det M = {{(-1)}^n \lambda s_11 s_22 s_33 u_11 u_22 u_33} so the matrix is invertible if and only if all the diagonal coefficients of the scale and unscaled factors are nonzero.

Decomposition of non-general 3D matrices

Except for matrix3d all the basic CSS transforms correspond to a matrix M = \begin{pmatrix} A & T \\ P & 1 \end{pmatrix} whose last row is (0, 0, p_z, 1). So our decompositon algorithm will always choose the second way and yield trivial factors \lambda = 1 and n = 0. The translation and "perspective" factors are just given by the original T, P. The rest of the factors are obtained by factorization of A.

Decomposing A is less obvious, for example rotateZ(180°) scale(1, 1, 2) rotates the x-axis and y-axis by 180° (either clockwise or anticlockwise). and scales the z-axis by 2 and so is then equivalent to scale(-1, -1, 2). The same example shows that the QR decomposition is not unique so in theory we cannot just say that our algorithm will choose a factorization once we find one that works...

For perspective() and translate3d() (and their aliases), A = I_3 is upper triangular so our algorithm will yield Q_1 = Q_2 = I_3, {\det {(Q_1 Q_2)}} = 1 and Q_2 Q_1 I_3 = I_3. The Q_3 = I_3 and so Q = Q_1 Q_2 Q_3 = I_3 and R = Q^{\mathrm T} A = I_3. The scale is then S = I_3 and the unscaled upper triangular factor is also U = I_3. So these two are indeed just decomposed into a single perspective or translation factor.

For rotate3D() (and its aliases) the algorithm will yield an upper triangular matrix Q_2 Q_1 A, which is additionnally orthogonal in that case (because A is a rotation). This means that its eigenvalues are both its diagonal elements (real numbers) and complex numbers of norm 1, that is they belong to \{-1, 1\}. Additionally, the inverse is both the transpose and an upper triangular matrix so the matrix is actually diagonal. Finally its determinant is 1, so \alpha_2, c_33 \in {\{-1, 1\}}, c_21 = c_31 = c_32 = 0 and \alpha_1 = \alpha_2 c_33. The algorithm will choose \epsilon_3, \epsilon_2 such that Q_3 is this diagonal matrix. From Q_3 = Q_2 Q_1 A we get Q = A, R = I_3 and so S=U=I_3. Again, this is decomposed into a single rotation factor.

For scale3d() (and its aliases) the matrix is

A= \begin{pmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & s_z \\ \end{pmatrix}

It is diagonal and in particular upper triangular so our algorithm will yield Q_1 = Q_2 = I_3 and \det{(Q_1 Q_2)} = 1. Then Q_3 is defined according to the signs of s_y, s_z (choosing the diagonal element to 1 if the corresponding scale element is zero) to provide the following QR decomposition:

A= \begin{pmatrix} {\epsilon_2 \epsilon_3} & 0 & 0 \\ 0 & \epsilon_2 & 0 \\ 0 & 0 & \epsilon_3 \\ \end{pmatrix} \begin{pmatrix} \epsilon_2 \epsilon_3 s_x & 0 & 0 \\ 0 & {|s_y|} & 0 \\ 0 & 0 & {|s_z|} \\ \end{pmatrix}

It is decomposed into a single scale factor if and only if s_x \neq 0, s_y, s_z > 0.

Decomposition of 2D matrices

For a 2D transform matrix(a, b, c, d, e, f) (including skews, 2D translations, 2D scales, 2D rotations) as explained in the previous section, e, f are extracted in the translation component and this is actually a 2D translation. The remaining part is then

A= \begin{pmatrix} a & c & 0 \\ b & d & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

If b = 0 (e.g. translations, scales and skewX) then the matrix is upper triangular and the algorithm calculates Q_1 = Q_2 = I_3. \epsilon_3 = 1 and \epsilon_2 is chosen to the sign of d. This gives the following QR decomposition:

A= \begin{pmatrix} \epsilon_2 & 0 & 0 \\ 0 & \epsilon_2 & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} {\epsilon_2 a} & {\epsilon_2 c} & 0 \\ 0 & {|d|} & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

If b \neq 0 (e.g. rotations of angle other than a multiple of 180°) then \alpha_1 = \pm \sqrt{a^2 + b^2} \neq 0 with the sign chosen to maximize the absolute value of \alpha_1 - a. In any case, one can verify that:

Q_1 = \begin{pmatrix} \frac{a}{\alpha_1} & \frac{b}{\alpha_1} & 0 \\ \frac{b}{\alpha_1} & \frac{-a}{\alpha_1} & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} Q_1 A = \begin{pmatrix} \alpha_1 & \frac{ac+bd}{\alpha_1} & 0 \\ 0 & {-\frac{\det{(A)}}{\alpha_1}} & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

This is an upper diagonal matrix and so Q_2 = I_3. Then \epsilon_3 = 1 is chosen and \epsilon_2 is chosen with same sign as \mp {\det(A)} (1 if the determinant is zero). The decomposition becomes:

A= \begin{pmatrix} \epsilon \frac{a}{\sqrt{a^2+b^2}} & \epsilon \frac{b}{\sqrt{a^2+b^2}} & 0 \\ \epsilon \frac{b}{\sqrt{a^2+b^2}} & -\epsilon\frac{a}{\sqrt{a^2+b^2}} & 0 \\ 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} \epsilon \sqrt{a^2+b^2} & \epsilon \frac{ac+bd}{\sqrt{a^2+b^2}} & 0 \\ 0 & {\frac{\left|\det{(A)}\right|}{\sqrt{a^2+b^2}}} & 0 \\ 0 & 0 & 1 \\ \end{pmatrix}

where \epsilon is chosen with same sign as \det{(A)} (1 if the determinant is zero). Note that this is slightly different from the decomposition provided in this blog post which uses Gram–Schmidt to perform the QR factorization.

To summarize, matrix(a, b, c, d, e, f) is decomposed as a product of a 2D-translation, a 2D-rotation, a 2D-scale and an unscaled factor with u_33 = 1, u_31 = u_32 = 0 (i.e. a 2D transform). If the matrix is invertible, then the unscaled factor can be written as a skewX. Per previous section, a 2D-translation or 2D-rotation is decomposed into a single 2D-translation or 2D-rotation respectively. A 2D-scale is decomposed as a product of a 2D-rotation and a 2D-scale (with the rotation omitted if the y-scale is nonnegative).