How to extract position, rotation and scale from matrix SVG
To decompose separately rotation and the skew you can check those examples:
react-native:
https://github.com/facebook/react-native/blob/master/Libraries/Utilities/MatrixMath.js
three.js: https://github.com/mrdoob/three.js/blob/302f1d155835888a77aad31241e0d74a2ee2f926/src/math/Matrix4.js
https://github.com/ismailman/decompose-dommatrix/blob/master/decomposeDommatrix.mjs
http://math.stackexchange.com/questions/861674/decompose-a-2d-arbitrary-transform-into-only-scaling-and-rotation
https://gist.github.com/Breton/9d217e0375de055d563b9a0b758d4ae6
function decomposeMatrix(m) {
var E = (m.a + m.d) / 2
var F = (m.a - m.d) / 2
var G = (m.c + m.b) / 2
var H = (m.c - m.b) / 2
var Q = Math.sqrt(E * E + H * H);
var R = Math.sqrt(F * F + G * G);
var a1 = Math.atan2(G, F);
var a2 = Math.atan2(H, E);
var theta = (a2 - a1) / 2;
var phi = (a2 + a1) / 2;
// The requested parameters are then theta,
// sx, sy, phi,
return {
translateX: m.e,
translateY: m.f,
rotate: -phi * 180 / Math.PI,
scaleX: Q + R,
scaleY: Q - R,
skew: -theta * 180 / Math.PI
};
}
https://www.w3.org/TR/css-transforms-1/#decomposing-a-2d-matrix
function decomposeMatrix2DW3(m) {
var row0x = m.a;
var row0y = m.b;
var row1x = m.c;
var row1y = m.d;
var scaleX = Math.sqrt(row0x * row0x + row0y * row0y)
var scaleY = Math.sqrt(row1x * row1x + row1y * row1y)
// If determinant is negative, one axis was flipped.
var determinant = row0x * row1y - row0y * row1x
if (determinant < 0)
// Flip axis with minimum unit vector dot product.
if (row0x < row1y)
scaleX = -scaleX
else
scaleY = -scaleY
// Renormalize matrix to remove scale.
if (scaleX) {
row0x *= 1 / scaleX
row0y *= 1 / scaleX
}
if (scaleY) {
row1x *= 1 / scaleY
row1y *= 1 / scaleY
}
// Compute rotation and renormalize matrix.
var angle = Math.atan2(row0y, row0x);
if (angle) {
// Rotate(-angle) = [cos(angle), sin(angle), -sin(angle), cos(angle)]
// = [row0x, -row0y, row0y, row0x]
// Thanks to the normalization above.
var sn = -row0y
var cs = row0x
var m11 = row0x
var m12 = row0y
var m21 = row1x
var m22 = row1y
row0x = cs * m11 + sn * m21
row0y = cs * m12 + sn * m22
row1x = -sn * m11 + cs * m21
row1y = -sn * m12 + cs * m22
}
m11 = row0x
m12 = row0y
m21 = row1x
m22 = row1y
// Convert into degrees because our rotation functions expect it.
angle = angle * (180 / Math.PI);
// The requested parameters are then theta,
// sx, sy, phi,
return {
translateX: m.e,
translateY: m.f,
rotateZ: angle,
scaleX: scaleX,
scaleY: scaleY,
matrix: [m11, m12, m21, m22, 0, 0]
};
}
You can also take a look:
https://github.com/d3/d3-interpolate/blob/master/src/transform/decompose.js#L12-L25
Inspired by this ActionScript version: https://gist.github.com/fwextensions/2052247, I wrote a JavaScript port:
function deltaTransformPoint(matrix, point) {
var dx = point.x * matrix.a + point.y * matrix.c + 0;
var dy = point.x * matrix.b + point.y * matrix.d + 0;
return { x: dx, y: dy };
}
function decomposeMatrix(matrix) {
// @see https://gist.github.com/2052247
// calculate delta transform point
var px = deltaTransformPoint(matrix, { x: 0, y: 1 });
var py = deltaTransformPoint(matrix, { x: 1, y: 0 });
// calculate skew
var skewX = ((180 / Math.PI) * Math.atan2(px.y, px.x) - 90);
var skewY = ((180 / Math.PI) * Math.atan2(py.y, py.x));
return {
translateX: matrix.e,
translateY: matrix.f,
scaleX: Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b),
scaleY: Math.sqrt(matrix.c * matrix.c + matrix.d * matrix.d),
skewX: skewX,
skewY: skewY,
rotation: skewX // rotation is the same as skew x
};
}
Usage: decomposeMatrix(document.getElementById('myElement').getCTM())