transformation-matrix
Version:
2d transformation matrix functions written in ES6 syntax. Tree shaking ready!
67 lines (61 loc) • 2.25 kB
JavaScript
import { scale } from './scale'
import { compose } from './transform'
/**
* Decompose a matrix into translation, scaling and rotation components, optionally
* take horizontal and vertical flip in to consideration.
* Note this function decomposes a matrix in rotation -> scaling -> translation order. I.e. for
* certain translation T {tx, ty}, rotation R and scaling S { sx, sy }, it's only true for:
* decomposeTSR(compose(T, S, R)) === { translate: T, rotation: R, scale: S }
* composing in a different order may yield a different decomposition result.
* @param matrix {Matrix} Affine Matrix
* @param flipX {boolean} Whether the matrix contains vertical flip, i.e. mirrors on x-axis
* @param flipY {boolean} Whether the matrix contains horizontal flip, i.e. mirrors on y-axis
* @returns {Transform} A transform object consisted by its translation, scaling
* and rotation components.
*/
export function decomposeTSR (matrix, flipX = false, flipY = false) {
// Remove flip from the matrix first - flip could be incorrectly interpreted as
// rotations (e.g. flipX + flipY = rotate by 180 degrees).
// Note flipX is a vertical flip, and flipY is a horizontal flip.
if (flipX) {
if (flipY) {
matrix = compose(matrix, scale(-1, -1))
} else {
matrix = compose(matrix, scale(1, -1))
}
} else if (flipY) {
matrix = compose(matrix, scale(-1, 1))
}
const a = matrix.a; const b = matrix.b
const c = matrix.c; const d = matrix.d
let scaleX, scaleY, rotation
if (a !== 0 || c !== 0) {
const hypotAc = Math.hypot(a, c)
scaleX = hypotAc
scaleY = (a * d - b * c) / hypotAc
const acos = Math.acos(a / hypotAc)
rotation = c > 0 ? -acos : acos
} else if (b !== 0 || d !== 0) {
const hypotBd = Math.hypot(b, d)
scaleX = (a * d - b * c) / hypotBd
scaleY = hypotBd
const acos = Math.acos(b / hypotBd)
rotation = Math.PI / 2 + (d > 0 ? -acos : acos)
} else {
scaleX = 0
scaleY = 0
rotation = 0
}
// put the flip factors back
if (flipY) {
scaleX = -scaleX
}
if (flipX) {
scaleY = -scaleY
}
return {
translate: { tx: matrix.e, ty: matrix.f },
scale: { sx: scaleX, sy: scaleY },
rotation: { angle: rotation }
}
}