import { Point } from "./objects/point"; const PI_2 = Math.PI * 2; /** * | a | c | tx| * | b | d | ty| * | 0 | 0 | 1 | * ``` */ export class Matrix { a = 1; b = 0; c = 0; d = 1; tx = 0; ty = 0; constructor(a = 1, b = 0, c = 0, d = 1, tx = 0, ty = 0) { this.a = a; this.b = b; this.c = c; this.d = d; this.tx = tx; this.ty = ty; } static createFromDiv(div: HTMLElement) { const out = new Matrix(); out.setFormDiv(div); return out; } //matrix(1,0,0,1,0,0) static createFromMatrixStr(str: string) { const out = new Matrix(); out.setMatrixStr(str); return out; } setMatrixStr(transformMatrix:string) { const values = transformMatrix.split("(")[1].split(")")[0].split(","); this.a = +values[0]; this.b = +values[1]; this.c = +values[2]; this.d = +values[3]; this.tx = +values[4]; this.ty = +values[5]; } setFormDiv(div: HTMLElement) { let transformMatrix = window .getComputedStyle(div) .getPropertyValue("transform"); if (!transformMatrix || transformMatrix === "none") { transformMatrix = "matrix(1,0,0,1,0,0)"; } this.setMatrixStr(transformMatrix); } applyDiv(div: HTMLElement) { div.style.transform = this.getMatrixStr(); div.style.transformOrigin = "0 0"; return this; } getMatrixStr() { return `matrix(${this.a},${this.b},${this.c},${this.d},${this.tx},${this.ty})`; } fromArray(array: number[]) { this.a = array[0]; this.b = array[1]; this.c = array[3]; this.d = array[4]; this.tx = array[2]; this.ty = array[5]; } set(a: number, b: number, c: number, d: number, tx: number, ty: number) { this.a = a; this.b = b; this.c = c; this.d = d; this.tx = tx; this.ty = ty; return this; } array = new Float32Array(9); toArray(transpose: boolean, out: any) { if (!this.array) { this.array = new Float32Array(9); } const array = out || this.array; if (transpose) { array[0] = this.a; array[1] = this.b; array[2] = 0; array[3] = this.c; array[4] = this.d; array[5] = 0; array[6] = this.tx; array[7] = this.ty; array[8] = 1; } else { array[0] = this.a; array[1] = this.c; array[2] = this.tx; array[3] = this.b; array[4] = this.d; array[5] = this.ty; array[6] = 0; array[7] = 0; array[8] = 1; } return array; } apply(pos: Point, newPos?: Point) { newPos = newPos || new Point(); const x = pos.x; const y = pos.y; newPos.x = this.a * x + this.c * y + this.tx; newPos.y = this.b * x + this.d * y + this.ty; return newPos; } applyInverse(pos: Point, newPos?: Point) { newPos = newPos || new Point(); const id = 1 / (this.a * this.d + this.c * -this.b); const x = pos.x; const y = pos.y; newPos.x = this.d * id * x + -this.c * id * y + (this.ty * this.c - this.tx * this.d) * id; newPos.y = this.a * id * y + -this.b * id * x + (-this.ty * this.a + this.tx * this.b) * id; return newPos; } translate(x: number, y: number) { this.tx += x; this.ty += y; return this; } scale(x: number, y: number) { this.a *= x; this.d *= y; this.c *= x; this.b *= y; this.tx *= x; this.ty *= y; return this; } getScale() { const a = this.a, b = this.b, c = this.c, d = this.d; const x = Math.sqrt((a * a) + (b * b)); const y = Math.sqrt((c * c) + (d * d)); return {x, y}; } rotate(angle: number) { const cos = Math.cos(angle); const sin = Math.sin(angle); const a1 = this.a; const c1 = this.c; const tx1 = this.tx; this.a = a1 * cos - this.b * sin; this.b = a1 * sin + this.b * cos; this.c = c1 * cos - this.d * sin; this.d = c1 * sin + this.d * cos; this.tx = tx1 * cos - this.ty * sin; this.ty = tx1 * sin + this.ty * cos; return this; } append(matrix: Matrix) { const a1 = this.a; const b1 = this.b; const c1 = this.c; const d1 = this.d; this.a = matrix.a * a1 + matrix.b * c1; this.b = matrix.a * b1 + matrix.b * d1; this.c = matrix.c * a1 + matrix.d * c1; this.d = matrix.c * b1 + matrix.d * d1; this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx; this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty; return this; } setTransform( x: number, y: number, pivotX: number, pivotY: number, scaleX: number, scaleY: number, rotation: number, skewX: number, skewY: number ) { this.a = Math.cos(rotation + skewY) * scaleX; this.b = Math.sin(rotation + skewY) * scaleX; this.c = -Math.sin(rotation - skewX) * scaleY; this.d = Math.cos(rotation - skewX) * scaleY; this.tx = x - (pivotX * this.a + pivotY * this.c); this.ty = y - (pivotX * this.b + pivotY * this.d); return this; } prepend(matrix: Matrix) { const tx1 = this.tx; if (matrix.a !== 1 || matrix.b !== 0 || matrix.c !== 0 || matrix.d !== 1) { const a1 = this.a; const c1 = this.c; this.a = a1 * matrix.a + this.b * matrix.c; this.b = a1 * matrix.b + this.b * matrix.d; this.c = c1 * matrix.a + this.d * matrix.c; this.d = c1 * matrix.b + this.d * matrix.d; } this.tx = tx1 * matrix.a + this.ty * matrix.c + matrix.tx; this.ty = tx1 * matrix.b + this.ty * matrix.d + matrix.ty; return this; } decompose(transform:any) { // sort out rotation / skew.. const a = this.a; const b = this.b; const c = this.c; const d = this.d; const skewX = -Math.atan2(-c, d); const skewY = Math.atan2(b, a); const delta = Math.abs(skewX + skewY); transform.rotation = skewY; transform.skew.x = transform.skew.y = 0; if (delta < 0.00001 || Math.abs(PI_2 - delta) < 0.00001) { transform.rotation = skewY; transform.skew.x = transform.skew.y = 0; } else { transform.rotation = 0; transform.skew.x = skewX; transform.skew.y = skewY; } // next set scale transform.scale.x = Math.sqrt((a * a) + (b * b)); transform.scale.y = Math.sqrt((c * c) + (d * d)); // next set position transform.position.x = this.tx; transform.position.y = this.ty; return transform; } invert() { const a1 = this.a; const b1 = this.b; const c1 = this.c; const d1 = this.d; const tx1 = this.tx; const n = a1 * d1 - b1 * c1; this.a = d1 / n; this.b = -b1 / n; this.c = -c1 / n; this.d = a1 / n; this.tx = (c1 * this.ty - d1 * tx1) / n; this.ty = -(a1 * this.ty - b1 * tx1) / n; return this; } identity() { this.a = 1; this.b = 0; this.c = 0; this.d = 1; this.tx = 0; this.ty = 0; return this; } clone() { const matrix = new Matrix(); matrix.a = this.a; matrix.b = this.b; matrix.c = this.c; matrix.d = this.d; matrix.tx = this.tx; matrix.ty = this.ty; return matrix; } copyTo(matrix: Matrix) { matrix.a = this.a; matrix.b = this.b; matrix.c = this.c; matrix.d = this.d; matrix.tx = this.tx; matrix.ty = this.ty; return matrix; } copyFrom(matrix: Matrix) { this.a = matrix.a; this.b = matrix.b; this.c = matrix.c; this.d = matrix.d; this.tx = matrix.tx; this.ty = matrix.ty; return this; } static get IDENTITY() { return new Matrix(); } static get TEMP_MATRIX() { return new Matrix(); } }