From 67a3d23b4d65354ff648910e6e8b9ac4ba45fd26 Mon Sep 17 00:00:00 2001 From: Orion Reed Date: Fri, 6 Dec 2024 15:24:33 -0500 Subject: [PATCH] rename --- ...tedDOMRect.test.ts => TransformDOMRect.ts} | 26 +- src/common/Matrix.ts | 283 +++++++++++++++++ src/common/rotated-dom-rect-2.ts | 288 ------------------ ...ated-dom-rect.ts => transform-dom-rect.ts} | 14 +- src/folk-rope.ts | 8 +- src/folk-shape.ts | 4 +- 6 files changed, 309 insertions(+), 314 deletions(-) rename src/__tests__/{RotatedDOMRect.test.ts => TransformDOMRect.ts} (91%) create mode 100644 src/common/Matrix.ts delete mode 100644 src/common/rotated-dom-rect-2.ts rename src/common/{rotated-dom-rect.ts => transform-dom-rect.ts} (91%) diff --git a/src/__tests__/RotatedDOMRect.test.ts b/src/__tests__/TransformDOMRect.ts similarity index 91% rename from src/__tests__/RotatedDOMRect.test.ts rename to src/__tests__/TransformDOMRect.ts index a0317fb..69f4023 100644 --- a/src/__tests__/RotatedDOMRect.test.ts +++ b/src/__tests__/TransformDOMRect.ts @@ -1,5 +1,5 @@ import { expect, test, describe } from 'bun:test'; -import { RotatedDOMRect } from '../common/rotated-dom-rect-2'; +import { TransformDOMRect } from '../common/transform-dom-rect'; import { Vector } from '../common/Vector'; // Helper for comparing points with floating point values @@ -11,7 +11,7 @@ const expectPointClose = (actual: { x: number; y: number }, expected: { x: numbe describe('RotatedDOMRect', () => { describe('constructor', () => { test('initializes with default values', () => { - const rect = new RotatedDOMRect(); + const rect = new TransformDOMRect(); expect(rect.x).toBe(0); expect(rect.y).toBe(0); expect(rect.width).toBe(0); @@ -20,7 +20,7 @@ describe('RotatedDOMRect', () => { }); test('initializes with custom values', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 10, y: 20, width: 100, @@ -37,7 +37,7 @@ describe('RotatedDOMRect', () => { describe('corner calculations', () => { test('calculates corners for unrotated rectangle', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 0, y: 0, width: 100, @@ -52,7 +52,7 @@ describe('RotatedDOMRect', () => { }); test('calculates corners for 90-degree rotated rectangle', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 0, y: 0, width: 100, @@ -69,7 +69,7 @@ describe('RotatedDOMRect', () => { describe('bounds', () => { test('calculates bounds for unrotated rectangle', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 0, y: 0, width: 100, @@ -86,7 +86,7 @@ describe('RotatedDOMRect', () => { }); test('calculates bounds for 45-degree rotated rectangle', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 0, y: 0, width: 100, @@ -109,7 +109,7 @@ describe('RotatedDOMRect', () => { describe('setters', () => { test('updates corners when center is modified', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ width: 100, height: 50, }); @@ -120,7 +120,7 @@ describe('RotatedDOMRect', () => { }); test('updates dimensions and rotation when setting topRight', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 0, y: 0, width: 100, @@ -143,7 +143,7 @@ describe('RotatedDOMRect', () => { describe('corner setters', () => { test('updates dimensions when setting bottomLeft', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 0, y: 0, width: 100, @@ -174,7 +174,7 @@ describe('RotatedDOMRect', () => { }); test('maintains rectangle properties when setting corners', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 0, y: 0, width: 100, @@ -193,7 +193,7 @@ describe('RotatedDOMRect', () => { describe('edge cases', () => { test('handles zero dimensions', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ x: 10, y: 10, width: 0, @@ -210,7 +210,7 @@ describe('RotatedDOMRect', () => { }); test('handles 360-degree rotation', () => { - const rect = new RotatedDOMRect({ + const rect = new TransformDOMRect({ width: 100, height: 50, rotation: Math.PI * 2, diff --git a/src/common/Matrix.ts b/src/common/Matrix.ts new file mode 100644 index 0000000..4cc11fb --- /dev/null +++ b/src/common/Matrix.ts @@ -0,0 +1,283 @@ +import type { Point } from './types'; + +// TODO: find right value for precision +const roundToDomPrecision = (value: number) => Math.round(value * 100000) / 100000; + +const PI2 = Math.PI * 2; +const TAU = Math.PI / 2; + +export interface MatrixInit { + a: number; + b: number; + c: number; + d: number; + e: number; + f: number; +} + +export interface IMatrix extends MatrixInit { + equals(m: MatrixInit): boolean; + identity(): Matrix; + multiply(m: MatrixInit): Matrix; + rotate(r: number, cx?: number, cy?: number): Matrix; + translate(x: number, y: number): Matrix; + scale(x: number, y: number): Matrix; + invert(): Matrix; + applyToPoint(point: Point): Point; + applyToPoints(points: Point[]): Point[]; + rotation(): number; + point(): Point; + decompose(): { x: number; y: number; scaleX: number; scaleY: number; rotation: number }; + clone(): Matrix; + toString(): string; + toDOMMatrix(): DOMMatrix; +} + +export class Matrix implements IMatrix { + constructor(a: number, b: number, c: number, d: number, e: number, f: number) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + } + + a = 1.0; + b = 0.0; + c = 0.0; + d = 1.0; + e = 0.0; + f = 0.0; + + equals(m: MatrixInit) { + return this.a === m.a && this.b === m.b && this.c === m.c && this.d === m.d && this.e === m.e && this.f === m.f; + } + + identity() { + this.a = 1.0; + this.b = 0.0; + this.c = 0.0; + this.d = 1.0; + this.e = 0.0; + this.f = 0.0; + return this; + } + + multiply(m: MatrixInit) { + const { a, b, c, d, e, f } = this; + this.a = a * m.a + c * m.b; + this.c = a * m.c + c * m.d; + this.e = a * m.e + c * m.f + e; + this.b = b * m.a + d * m.b; + this.d = b * m.c + d * m.d; + this.f = b * m.e + d * m.f + f; + return this; + } + + rotate(r: number, cx?: number, cy?: number) { + if (r === 0) return this; + if (cx === undefined) return this.multiply(Matrix.Rotate(r)); + return this.translate(cx, cy!).multiply(Matrix.Rotate(r)).translate(-cx, -cy!); + } + + translate(x: number, y: number): Matrix { + return this.multiply(Matrix.Translate(x, y!)); + } + + scale(x: number, y: number) { + return this.multiply(Matrix.Scale(x, y)); + } + + invert() { + const { a, b, c, d, e, f } = this; + const denominator = a * d - b * c; + this.a = d / denominator; + this.b = b / -denominator; + this.c = c / -denominator; + this.d = a / denominator; + this.e = (d * e - c * f) / -denominator; + this.f = (b * e - a * f) / denominator; + return this; + } + + applyToPoint(point: Point) { + return Matrix.applyToPoint(this, point); + } + + applyToPoints(points: Point[]) { + return Matrix.applyToPoints(this, points); + } + + rotation() { + return Matrix.Rotation(this); + } + + point() { + return Matrix.ToPoint(this); + } + + decompose() { + return Matrix.Decompose(this); + } + + clone() { + return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f); + } + + toDOMMatrix(): DOMMatrix { + return new DOMMatrix([this.a, this.b, this.c, this.d, this.e, this.f]); + } + + static Rotate(r: number, cx?: number, cy?: number) { + if (r === 0) return Matrix.Identity(); + + const cosAngle = Math.cos(r); + const sinAngle = Math.sin(r); + + const rotationMatrix = new Matrix(cosAngle, sinAngle, -sinAngle, cosAngle, 0.0, 0.0); + + if (cx === undefined) return rotationMatrix; + + return Matrix.Compose(Matrix.Translate(cx, cy!), rotationMatrix, Matrix.Translate(-cx, -cy!)); + } + + static Scale: { + (x: number, y: number): MatrixInit; + (x: number, y: number, cx: number, cy: number): MatrixInit; + } = (x: number, y: number, cx?: number, cy?: number) => { + const scaleMatrix = new Matrix(x, 0, 0, y, 0, 0); + + if (cx === undefined) return scaleMatrix; + + return Matrix.Compose(Matrix.Translate(cx, cy!), scaleMatrix, Matrix.Translate(-cx, -cy!)); + }; + + static Multiply(m1: MatrixInit, m2: MatrixInit): MatrixInit { + return { + a: m1.a * m2.a + m1.c * m2.b, + c: m1.a * m2.c + m1.c * m2.d, + e: m1.a * m2.e + m1.c * m2.f + m1.e, + b: m1.b * m2.a + m1.d * m2.b, + d: m1.b * m2.c + m1.d * m2.d, + f: m1.b * m2.e + m1.d * m2.f + m1.f, + }; + } + + static Inverse(m: MatrixInit): MatrixInit { + const denominator = m.a * m.d - m.b * m.c; + return { + a: m.d / denominator, + b: m.b / -denominator, + c: m.c / -denominator, + d: m.a / denominator, + e: (m.d * m.e - m.c * m.f) / -denominator, + f: (m.b * m.e - m.a * m.f) / denominator, + }; + } + + static Absolute(m: MatrixInit): MatrixInit { + const denominator = m.a * m.d - m.b * m.c; + return { + a: m.d / denominator, + b: m.b / -denominator, + c: m.c / -denominator, + d: m.a / denominator, + e: (m.d * m.e - m.c * m.f) / denominator, + f: (m.b * m.e - m.a * m.f) / -denominator, + }; + } + + static Compose(...matrices: MatrixInit[]) { + const matrix = Matrix.Identity(); + for (let i = 0, n = matrices.length; i < n; i++) { + matrix.multiply(matrices[i]); + } + return matrix; + } + + static Identity() { + return new Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); + } + + static Translate(x: number, y: number) { + return new Matrix(1.0, 0.0, 0.0, 1.0, x, y); + } + + static ToPoint(m: MatrixInit): Point { + return { x: m.e, y: m.f }; + } + + static Rotation(m: MatrixInit): number { + let rotation; + + if (m.a !== 0 || m.c !== 0) { + const hypotAc = (m.a * m.a + m.c * m.c) ** 0.5; + rotation = Math.acos(m.a / hypotAc) * (m.c > 0 ? -1 : 1); + } else if (m.b !== 0 || m.d !== 0) { + const hypotBd = (m.b * m.b + m.d * m.d) ** 0.5; + rotation = TAU + Math.acos(m.b / hypotBd) * (m.d > 0 ? -1 : 1); + } else { + rotation = 0; + } + + return clampRotation(rotation); + } + + static Decompose(m: MatrixInit) { + let scaleX, scaleY, rotation; + + if (m.a !== 0 || m.c !== 0) { + const hypotAc = (m.a * m.a + m.c * m.c) ** 0.5; + scaleX = hypotAc; + scaleY = (m.a * m.d - m.b * m.c) / hypotAc; + rotation = Math.acos(m.a / hypotAc) * (m.c > 0 ? -1 : 1); + } else if (m.b !== 0 || m.d !== 0) { + const hypotBd = (m.b * m.b + m.d * m.d) ** 0.5; + scaleX = (m.a * m.d - m.b * m.c) / hypotBd; + scaleY = hypotBd; + rotation = TAU + Math.acos(m.b / hypotBd) * (m.d > 0 ? -1 : 1); + } else { + scaleX = 0; + scaleY = 0; + rotation = 0; + } + + return { + x: m.e, + y: m.f, + scaleX, + scaleY, + rotation: clampRotation(rotation), + }; + } + + static applyToPoint(m: MatrixInit, point: Point) { + return { x: m.a * point.x + m.c * point.y + m.e, y: m.b * point.x + m.d * point.y + m.f }; + } + + static applyToPoints(m: MatrixInit, points: Point[]): Point[] { + return points.map((point) => ({ x: m.a * point.x + m.c * point.y + m.e, y: m.b * point.x + m.d * point.y + m.f })); + } + + static From(m: MatrixInit | DOMMatrix) { + if (m instanceof DOMMatrix) { + return Matrix.FromDOMMatrix(m); + } + return new Matrix(m.a, m.b, m.c, m.d, m.e, m.f); + } + + static FromDOMMatrix(domMatrix: DOMMatrix): Matrix { + return new Matrix(domMatrix.a, domMatrix.b, domMatrix.c, domMatrix.d, domMatrix.e, domMatrix.f); + } + + static toString(m: MatrixInit) { + return `matrix(${roundToDomPrecision(m.a)}, ${roundToDomPrecision(m.b)}, ${roundToDomPrecision( + m.c + )}, ${roundToDomPrecision(m.d)}, ${roundToDomPrecision(m.e)}, ${roundToDomPrecision(m.f)})`; + } +} + +function clampRotation(radians: number) { + return (PI2 + radians) % PI2; +} diff --git a/src/common/rotated-dom-rect-2.ts b/src/common/rotated-dom-rect-2.ts deleted file mode 100644 index 751c00d..0000000 --- a/src/common/rotated-dom-rect-2.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { Point } from './types'; -import { Vector } from './Vector'; - -type RotatedDOMRectInit = { - x?: number; - y?: number; - width?: number; - height?: number; - rotation?: number; -}; - -/** - * Represents a rectangle that can be rotated in 2D space. - * All coordinates are relative to the center of the rectangle. - */ -interface IRotatedDOMRect { - /** X coordinate of the rectangle's center */ - x: number; - /** Y coordinate of the rectangle's center */ - y: number; - /** Center point of the rectangle */ - center: Readonly; - /** Width of the rectangle */ - width: number; - /** Height of the rectangle */ - height: number; - /** Rotation of the rectangle in radians */ - rotation: number; - /** Top-left corner of the rotated rectangle */ - topLeft: Readonly; - /** Top-right corner of the rotated rectangle */ - topRight: Readonly; - /** Bottom-left corner of the rotated rectangle */ - bottomLeft: Readonly; - /** Bottom-right corner of the rotated rectangle */ - bottomRight: Readonly; - /** - * Returns an axis-aligned bounding box that contains the rotated rectangle - * @returns A DOMRectInit object representing the bounds - */ - getBounds(): Required; - - /** Mutate multiple properties at once efficiently */ - update(updates: Partial): void; - - /** Create a new instance with modified properties */ - with(updates: Partial): RotatedDOMRect; -} - -export class RotatedDOMRect implements IRotatedDOMRect { - private _center: Point = { x: 0, y: 0 }; - private _width: number = 0; - private _height: number = 0; - private _rotation: number = 0; - - // Cached derived values - private _sinR: number | null = null; - private _cosR: number | null = null; - private _topLeftX: number | null = null; - private _topLeftY: number | null = null; - private _topRightX: number | null = null; - private _topRightY: number | null = null; - private _bottomLeftX: number | null = null; - private _bottomLeftY: number | null = null; - private _bottomRightX: number | null = null; - private _bottomRightY: number | null = null; - - constructor({ x, y, width, height, rotation }: RotatedDOMRectInit = {}) { - this._center = { x: x ?? 0, y: y ?? 0 }; - this._width = width ?? 0; - this._height = height ?? 0; - this._rotation = rotation ?? 0; - } - - /* ——— Getters ——— */ - - get center(): Readonly { - return this._center; - } - - get x(): number { - return this._center.x; - } - - get y(): number { - return this._center.y; - } - - get width(): number { - return this._width; - } - - get height(): number { - return this._height; - } - - get rotation(): number { - return this._rotation; - } - - get topLeft(): Readonly { - this.deriveValuesIfCacheInvalid(); - return { x: this._topLeftX!, y: this._topLeftY! }; - } - - get topRight(): Readonly { - this.deriveValuesIfCacheInvalid(); - return { x: this._topRightX!, y: this._topRightY! }; - } - - get bottomLeft(): Readonly { - this.deriveValuesIfCacheInvalid(); - return { x: this._bottomLeftX!, y: this._bottomLeftY! }; - } - - get bottomRight(): Readonly { - this.deriveValuesIfCacheInvalid(); - return { x: this._bottomRightX!, y: this._bottomRightY! }; - } - - /* ——— Setters ——— */ - - set center(point: Point) { - this._center = point; - this.invalidateCache(); - } - - set x(value: number) { - this._center.x = value; - this.invalidateCache(); - } - - set y(value: number) { - this._center.y = value; - this.invalidateCache(); - } - - set width(value: number) { - this._width = value; - this.invalidateCache(); - } - - set height(value: number) { - this._height = value; - this.invalidateCache(); - } - - set rotation(value: number) { - this._rotation = value; - this.invalidateCache(); - } - - set topLeft(point: Point) { - this.moveCorner(point, this.bottomRight); - } - - set topRight(point: Point) { - this.moveCorner(point, this.bottomLeft); - } - - set bottomLeft(point: Point) { - this.moveCorner(point, this.topRight); - } - - set bottomRight(point: Point) { - this.moveCorner(point, this.topLeft); - } - - getBounds(): Required { - if (!this.isCacheValid()) { - this.deriveValuesIfCacheInvalid(); - } - - const minX = Math.min(this._topLeftX!, this._topRightX!, this._bottomLeftX!, this._bottomRightX!); - const maxX = Math.max(this._topLeftX!, this._topRightX!, this._bottomLeftX!, this._bottomRightX!); - const minY = Math.min(this._topLeftY!, this._topRightY!, this._bottomLeftY!, this._bottomRightY!); - const maxY = Math.max(this._topLeftY!, this._topRightY!, this._bottomLeftY!, this._bottomRightY!); - - return { - x: minX, - y: minY, - width: maxX - minX, - height: maxY - minY, - }; - } - - /** Mutate multiple properties at once efficiently */ - update(updates: Partial) { - if (updates.x !== undefined) this._center.x = updates.x; - if (updates.y !== undefined) this._center.y = updates.y; - if (updates.width !== undefined) this._width = updates.width; - if (updates.height !== undefined) this._height = updates.height; - if (updates.rotation !== undefined) this._rotation = updates.rotation; - this.invalidateCache(); - } - - /** Create a new instance with modified properties */ - with(updates: Partial): RotatedDOMRect { - return new RotatedDOMRect({ - x: updates.x ?? this._center.x, - y: updates.y ?? this._center.y, - width: updates.width ?? this._width, - height: updates.height ?? this._height, - rotation: updates.rotation ?? this._rotation, - }); - } - - /* ——— Private methods ——— */ - - private moveCorner(newCorner: Point, oppositeCorner: Point) { - // Calculate new center midway between the two corners - this._center = Vector.lerp(newCorner, oppositeCorner, 0.5); - - // Get vector from opposite corner to new corner - const delta = Vector.sub(newCorner, oppositeCorner); - - // Rotate the delta vector back by -rotation to get width/height - const rotated = Vector.rotate(delta, -this._rotation); - - // Update width and height with absolute values - this._width = Math.abs(rotated.x); - this._height = Math.abs(rotated.y); - - // Invalidate cache to ensure all derived values are recalculated - this.invalidateCache(); - } - - private getTrigValues(): { sin: number; cos: number } { - if (this._sinR === null || this._cosR === null) { - this._sinR = Math.sin(this._rotation); - this._cosR = Math.cos(this._rotation); - } - return { sin: this._sinR, cos: this._cosR }; - } - - private deriveValuesIfCacheInvalid() { - if (this.isCacheValid()) return; - - const halfWidth = this._width / 2; - const halfHeight = this._height / 2; - const { sin, cos } = this.getTrigValues(); - - this._topLeftX = this._center.x - halfWidth * cos + halfHeight * sin; - this._topLeftY = this._center.y - halfWidth * sin - halfHeight * cos; - - this._topRightX = this._center.x + halfWidth * cos + halfHeight * sin; - this._topRightY = this._center.y + halfWidth * sin - halfHeight * cos; - - this._bottomLeftX = this._center.x - halfWidth * cos - halfHeight * sin; - this._bottomLeftY = this._center.y - halfWidth * sin + halfHeight * cos; - - this._bottomRightX = this._center.x + halfWidth * cos - halfHeight * sin; - this._bottomRightY = this._center.y + halfWidth * sin + halfHeight * cos; - } - - private invalidateCache() { - this._sinR = null; - this._cosR = null; - this._topLeftX = null; - this._topLeftY = null; - this._topRightX = null; - this._topRightY = null; - this._bottomLeftX = null; - this._bottomLeftY = null; - this._bottomRightX = null; - this._bottomRightY = null; - } - - /** - * Checks if the cached corner coordinates are valid. - * Type assertion is safe because invalidateCache() clears all values atomically, - * and calculateCorners() sets all values atomically. - */ - private isCacheValid(): this is { - _topLeftX: number; - _topLeftY: number; - _topRightX: number; - _topRightY: number; - _bottomLeftX: number; - _bottomLeftY: number; - _bottomRightX: number; - _bottomRightY: number; - _sinR: number; - _cosR: number; - } { - return this._topLeftX !== null; - } -} diff --git a/src/common/rotated-dom-rect.ts b/src/common/transform-dom-rect.ts similarity index 91% rename from src/common/rotated-dom-rect.ts rename to src/common/transform-dom-rect.ts index 290dfa1..5015e6a 100644 --- a/src/common/rotated-dom-rect.ts +++ b/src/common/transform-dom-rect.ts @@ -1,7 +1,7 @@ import { Point } from './types'; import { Vector } from './Vector'; -interface RotatedDOMRectInit { +interface TransformDOMRectInit { height?: number; width?: number; x?: number; @@ -9,10 +9,10 @@ interface RotatedDOMRectInit { rotation?: number; } -export class RotatedDOMRect implements DOMRect { - #other: RotatedDOMRectInit; +export class TransformDOMRect implements DOMRect { + #other: TransformDOMRectInit; - constructor(other: RotatedDOMRectInit = {}) { + constructor(other: TransformDOMRectInit = {}) { this.#other = other; } @@ -135,10 +135,10 @@ export class RotatedDOMRect implements DOMRect { } // We cant just override the setter, we need to override the getter and setter. -export class RotatedDOMRectReadonly extends RotatedDOMRect { - #other: RotatedDOMRectInit; +export class TransformDOMRectReadonly extends TransformDOMRect { + #other: TransformDOMRectInit; - constructor(other: RotatedDOMRectInit = {}) { + constructor(other: TransformDOMRectInit = {}) { super(other); this.#other = other; } diff --git a/src/folk-rope.ts b/src/folk-rope.ts index e879468..f44daab 100644 --- a/src/folk-rope.ts +++ b/src/folk-rope.ts @@ -2,7 +2,7 @@ import { Vector } from './common/Vector.ts'; import type { Point } from './common/types.ts'; -import { RotatedDOMRect } from './common/rotated-dom-rect.ts'; +import { TransformDOMRect } from './common/transform-dom-rect.ts'; import { FolkBaseConnection } from './folk-base-connection.ts'; const lerp = (first: number, second: number, percentage: number) => first + (second - first) * percentage; @@ -120,11 +120,11 @@ export class FolkRope extends FolkBaseConnection { this.draw(); }; - override render(sourceRect: RotatedDOMRect | DOMRectReadOnly, targetRect: RotatedDOMRect | DOMRectReadOnly) { + override render(sourceRect: TransformDOMRect | DOMRectReadOnly, targetRect: TransformDOMRect | DOMRectReadOnly) { let source: Point; let target: Point; - if (sourceRect instanceof RotatedDOMRect) { + if (sourceRect instanceof TransformDOMRect) { source = Vector.lerp(sourceRect.bottomRight, sourceRect.bottomLeft, 0.5); } else { source = { @@ -133,7 +133,7 @@ export class FolkRope extends FolkBaseConnection { }; } - if (targetRect instanceof RotatedDOMRect) { + if (targetRect instanceof TransformDOMRect) { target = Vector.lerp(targetRect.bottomRight, targetRect.bottomLeft, 0.5); } else { target = { diff --git a/src/folk-shape.ts b/src/folk-shape.ts index 975b997..c1ed0e2 100644 --- a/src/folk-shape.ts +++ b/src/folk-shape.ts @@ -1,7 +1,7 @@ import { css, html } from './common/tags'; import { ResizeObserverManager } from './common/resize-observer'; import { Point } from './common/types'; -import { RotatedDOMRectReadonly } from './common/rotated-dom-rect'; +import { TransformDOMRectReadonly } from './common/transform-dom-rect'; import { Vector } from './common/Vector'; import { getResizeCursorUrl, getRotateCursorUrl } from './common/cursors'; @@ -357,7 +357,7 @@ export class FolkShape extends HTMLElement { getClientRect() { const { x, y, width, height, rotation } = this; - return new RotatedDOMRectReadonly({ x, y, width, height, rotation }); + return new TransformDOMRectReadonly({ x, y, width, height, rotation }); } // Similar to `Element.getClientBoundingRect()`, but returns an SVG path that precisely outlines the shape.