export class Transform { private _translateX: number = 0; private _translateY: number = 0; private _scale: number = 1; private _rotate: number = 0; static Identity(): Transform { return new Transform(0, 0, 1); } get TranslateX(): number { return this._translateX; } get TranslateY(): number { return this._translateY; } get Scale(): number { return this._scale; } get Rotate(): number { return this._rotate; } get RotateDeg(): number { return (this._rotate * 180) / Math.PI; } /** * Represents a transformation/scale matrix (can contain a rotation value, but it is not used when transforming points) * @param x * @param y * @param scale * @param rotation NOTE: this is passed along but is NOT used by any of the transformation functionsStores */ constructor(x: number, y: number, scale: number, rotationRadians?: number) { this._translateX = x; this._translateY = y; this._scale = scale; this._rotate = rotationRadians ?? 0; } /** * Rotate in radians * @param rot * @returns the modified transformation */ rotate = (rot: number): this => { this._rotate += rot; return this; }; /** * Rotation in degrees * @param rot * @returns the modified transformation */ rotateDeg = (rot: number): this => { this._rotate += (rot * Math.PI) / 180; return this; }; translate = (x: number, y: number): this => { this._translateX += x; this._translateY += y; return this; }; scale = (scale: number): this => { this._scale *= scale; this._translateX *= scale; this._translateY *= scale; return this; }; scaleAbout = (scale: number, x: number, y: number): this => { this._translateX += x * this._scale - x * this._scale * scale; this._translateY += y * this._scale - y * this._scale * scale; this._scale *= scale; return this; }; transform = (transform: Transform): this => { this._translateX = transform._translateX + transform._scale * this._translateX; this._translateY = transform._translateY + transform._scale * this._translateY; this._scale *= transform._scale; return this; }; preTranslate = (x: number, y: number): this => { this._translateX += this._scale * x; this._translateY += this._scale * y; return this; }; preScale = (scale: number): this => { this._scale *= scale; return this; }; preTransform = (transform: Transform): this => { this._translateX += transform._translateX * this._scale; this._translateY += transform._translateY * this._scale; this._scale *= transform._scale; return this; }; translated = (x: number, y: number): Transform => this.copy().translate(x, y); preTranslated = (x: number, y: number): Transform => this.copy().preTranslate(x, y); scaled = (scale: number): Transform => this.copy().scale(scale); scaledAbout = (scale: number, x: number, y: number): Transform => this.copy().scaleAbout(scale, x, y); preScaled = (scale: number): Transform => this.copy().preScale(scale); transformed = (transform: Transform): Transform => this.copy().transform(transform); preTransformed = (transform: Transform): Transform => this.copy().preTransform(transform); transformPoint = (x: number, y: number): [number, number] => [x * this._scale + this._translateX, y * this._scale + this._translateY]; transformDirection = (x: number, y: number): [number, number] => [x * this._scale, y * this._scale]; transformBounds(x: number, y: number, width: number, height: number): { x: number; y: number; width: number; height: number } { const [tx, ty] = this.transformPoint(x, y); const [twidth, theight] = this.transformDirection(width, height); return { x: tx, y: ty, width: twidth, height: theight }; } inverse = () => new Transform(-this._translateX / this._scale, -this._translateY / this._scale, 1 / this._scale, -this._rotate); copy = () => new Transform(this._translateX, this._translateY, this._scale, this._rotate); }