165 lines
3.9 KiB
TypeScript
165 lines
3.9 KiB
TypeScript
|
/* eslint-disable no-dupe-class-members */
|
||
|
/**
|
||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*
|
||
|
*/
|
||
|
import { isPoint, Point } from './point';
|
||
|
|
||
|
type ContainsPointReturn = {
|
||
|
result: boolean
|
||
|
reason: {
|
||
|
isOnTopSide: boolean
|
||
|
isOnBottomSide: boolean
|
||
|
isOnLeftSide: boolean
|
||
|
isOnRightSide: boolean
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class Rect {
|
||
|
|
||
|
private readonly _left: number;
|
||
|
private readonly _top: number;
|
||
|
private readonly _right: number;
|
||
|
private readonly _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 top(): number {
|
||
|
return this._top;
|
||
|
}
|
||
|
|
||
|
get right(): number {
|
||
|
return this._right;
|
||
|
}
|
||
|
|
||
|
get bottom(): number {
|
||
|
return this._bottom;
|
||
|
}
|
||
|
|
||
|
get left(): number {
|
||
|
return this._left;
|
||
|
}
|
||
|
|
||
|
get width(): number {
|
||
|
return Math.abs(this._left - this._right);
|
||
|
}
|
||
|
|
||
|
get height(): number {
|
||
|
return Math.abs(this._bottom - this._top);
|
||
|
}
|
||
|
|
||
|
public equals({ top, left, bottom, right }: Rect): boolean {
|
||
|
return (
|
||
|
top === this._top &&
|
||
|
bottom === this._bottom &&
|
||
|
left === this._left &&
|
||
|
right === this._right
|
||
|
);
|
||
|
}
|
||
|
|
||
|
public contains({ x, y }: Point): ContainsPointReturn;
|
||
|
public contains({ top, left, bottom, right }: Rect): boolean;
|
||
|
public contains(target: Point | Rect): boolean | ContainsPointReturn {
|
||
|
if (isPoint(target)) {
|
||
|
const { x, y } = target;
|
||
|
|
||
|
const isOnTopSide = y < this._top;
|
||
|
const isOnBottomSide = y > this._bottom;
|
||
|
const isOnLeftSide = x < this._left;
|
||
|
const isOnRightSide = x > this._right;
|
||
|
|
||
|
const result =
|
||
|
!isOnTopSide && !isOnBottomSide && !isOnLeftSide && !isOnRightSide;
|
||
|
|
||
|
return {
|
||
|
reason: {
|
||
|
isOnBottomSide,
|
||
|
isOnLeftSide,
|
||
|
isOnRightSide,
|
||
|
isOnTopSide,
|
||
|
},
|
||
|
result,
|
||
|
};
|
||
|
} else {
|
||
|
const { top, left, bottom, right } = target;
|
||
|
|
||
|
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
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public intersectsWith(rect: Rect): boolean {
|
||
|
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;
|
||
|
return maxX - minX <= w1 + w2 && maxY - minY <= h1 + h2;
|
||
|
}
|
||
|
|
||
|
public generateNewRect({
|
||
|
left = this.left,
|
||
|
top = this.top,
|
||
|
right = this.right,
|
||
|
bottom = this.bottom,
|
||
|
}): Rect {
|
||
|
return new Rect(left, top, right, bottom);
|
||
|
}
|
||
|
|
||
|
static fromLTRB(
|
||
|
left: number,
|
||
|
top: number,
|
||
|
right: number,
|
||
|
bottom: number,
|
||
|
): Rect {
|
||
|
return new Rect(left, top, right, bottom);
|
||
|
}
|
||
|
|
||
|
static fromLWTH(
|
||
|
left: number,
|
||
|
width: number,
|
||
|
top: number,
|
||
|
height: number,
|
||
|
): Rect {
|
||
|
return new Rect(left, top, left + width, top + height);
|
||
|
}
|
||
|
|
||
|
static fromPoints(startPoint: Point, endPoint: Point): Rect {
|
||
|
const { y: top, x: left } = startPoint;
|
||
|
const { y: bottom, x: right } = endPoint;
|
||
|
return Rect.fromLTRB(left, top, right, bottom);
|
||
|
}
|
||
|
|
||
|
static fromDOM(dom: HTMLElement): Rect {
|
||
|
const { top, width, left, height } = dom.getBoundingClientRect();
|
||
|
return Rect.fromLWTH(left, width, top, height);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
export default Rect;
|
||
|
export { Rect };
|