Description
2D spec: https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix
3D spec: https://drafts.csswg.org/css-transforms-2/#decomposing-a-3d-matrix
A minor tweak to a transformation matrix that alters the transform from 2D to 3D, or vice versa, should not radically alter the interpolation path.
In the 2D case, the decomposed matrix has a total of 9 parameters: 2 translate, 2 scale, an angle and a 2x2 matrix that corresponds to the skew. This feels over-specified as the skew should be expressible with at most 2 parameters.
Here is an alternative, which is more consistent with the 3D matrix decomposition:
Input:
matrix ; a 4x4 matrix
Output:
translation ; a 2 component vector
scale ; a 2 component vector
skew ; X skew
angle ; rotation
Returns false if the matrix cannot be decomposed, true if it can
m11 = matrix[0][0]
m12 = matrix[0][1]
m21 = matrix[1][0]
m22 = matrix[1][1]
determinant = m11 * m22 - m12 * m21;
if (determinant == 0)
return false;
translate[0] = matrix[3][0]
translate[1] = matrix[3][1]
scale[0] = 1;
scale[1] = 1;
if (determinant < 0)
// If the determinant is negative, we need to flip either the x or y scale.
// Flipping both is equivalent to rotating by 180 degrees.
if (m11 < m22)
scale[0] = -1
else
scale[1] = -1
scale[0] *= sqrt(m11 * m11 + m12 * m12)
m11 /= scale[0]
m12 /= scale[0]
scaledShear = m11 * m21 + m12 * m22
m21 -= m11 * scaledShear
m22 -= m12 * scaledShear
scale[1] *= sqrt(m21 * m21 + m22 * m22)
m21 /= scale[1]
m22 /= scale[1]
skew = scaledShear / scale[1]
angle = atan2(m12, m11)
The inconsistency in handling negative determinants is files as #3712.
The 3D counterpart to this 2D decomposition is:
decomp3d.scale = [decomp2d.scale[0], decomp2d.scale[1], 1]
decomp3d.translate = [decomp2d.translate[0], decomp2d.translate[1], 0]
decomp3d.skew = [decomp2d.skew, 0, 0]
decomp3d.perspective = [0, 0, 0, 1]
decomp3d.quaternion = [0, 0, sin(decomp2d.angle/2), cos(decomp2d.angle/2)]
Having this simple mapping facilitates blending a 2D and 3D transformation matrix.