export class Point { public x: number; public y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } public equals({ x, y }: Point) { return this.x === x && this.y === y; } public xDistance({ x }: Point) { return this.x - x; } public yDistance({ y }: Point) { return this.y - y; } public xDistanceAbsolute(point: Point) { return Math.abs(this.xDistance(point)); } public yDistanceAbsolute(point: Point) { return Math.abs(this.yDistance(point)); } public distance(point: Point) { return Math.sqrt( Math.pow(this.xDistance(point), 2) + Math.pow(this.yDistance(point), 2) ); } } export class Rect { private _left: number; private _top: number; private _right: number; private _bottom: number; constructor(left: number, top: number, right: number, bottom: number) { const [physicTop, physicBottom] = top <= bottom ? [top, bottom] : [bottom, top]; const [physicLeft, physicRight] = left <= right ? [left, right] : [right, left]; this._top = physicTop; this._right = physicRight; this._left = physicLeft; this._bottom = physicBottom; } get width() { return Math.abs(this._left - this._right); } get height() { return Math.abs(this._bottom - this._top); } get top() { return this._top; } get left() { return this._left; } get bottom() { return this._bottom; } get right() { return this._right; } equals({ top, left, bottom, right }: Rect) { return ( top === this._top && bottom === this._bottom && left === this._left && right === this._right ); } isContainPoint({ x, y }: Point) { return ( y >= this._top && y <= this._bottom && x >= this.left && x <= this._right ); } isContain({ top, left, bottom, right }: Rect) { return ( top >= this._top && top <= this._bottom && bottom >= this._top && bottom <= this._bottom && left >= this._left && left <= this.right && right >= this.left && right <= this._right ); } isIntersect(rect: Rect) { const { left: x1, top: y1, width: w1, height: h1 } = rect; const { left: x2, top: y2, width: w2, height: h2 } = this; const maxX = x1 + w1 >= x2 + w2 ? x1 + w1 : x2 + w2; const maxY = y1 + h1 >= y2 + h2 ? y1 + h1 : y2 + h2; const minX = x1 <= x2 ? x1 : x2; const minY = y1 <= y2 ? y1 : y2; if (maxX - minX <= w1 + w2 && maxY - minY <= h1 + h2) { return true; } else { return false; } } isPointDown({ x, y }: Point) { return this.bottom < y && this.left <= x && this.right >= x; } isPointUp({ x, y }: Point) { return y < this.top && this.left <= x && this.right >= x; } isPointLeft({ x, y }: Point) { return x < this.left && this.top <= y && this.bottom >= y; } isPointRight({ x, y }: Point) { return x > this.right && this.top <= y && this.bottom >= y; } fromNewLeft(left: number) { this._left = left; return new Rect(left, this.top, this.right, this.bottom); } // eslint-disable-next-line @typescript-eslint/naming-convention static fromLTRB(left: number, top: number, right: number, bottom: number) { return new Rect(left, top, right, bottom); } // eslint-disable-next-line @typescript-eslint/naming-convention static fromLWTH(left: number, width: number, top: number, height: number) { return new Rect(left, top, left + width, top + height); } // eslint-disable-next-line @typescript-eslint/naming-convention static fromPoints(startPoint: Point, endPoint: Point) { const { y: top, x: left } = startPoint; const { y: bottom, x: right } = endPoint; return Rect.fromLTRB(left, top, right, bottom); } } export const pointsToRect = (startPoint: Point, endPoint: Point) => { const { y: top, x: left } = startPoint; const { y: bottom, x: right } = endPoint; return Rect.fromLTRB(left, top, right, bottom); }; export const domToRect = (dom: HTMLElement) => { const { top, width, left, height } = dom.getBoundingClientRect(); return Rect.fromLWTH(left, width, top, height); };