2025-12-30 09:47:47 +01:00

236 lines
4.1 KiB
JavaScript

export class Point {
static create(x = 0.0, y = 0.0) {
return new Point(x, y);
}
constructor(x = 0.0, y = 0.0) {
this.x = x || 0.0;
this.y = y || 0.0;
}
get length() {
return Math.hypot(this.x, this.y);
}
get lengthSquared() {
return this.x * this.x + this.y * this.y;
}
get angle() {
return Math.atan2(this.y, this.x);
}
set(x, y) {
this.x = x ?? this.x;
this.y = y ?? this.y;
return this;
}
reset() {
return this.set(0, 0);
}
copy({ x, y }) {
return this.set(x, y);
}
clone() {
return new Point(this.x, this.y);
}
polar(angle, length = 1.0) {
return this.set(Math.cos(angle) * length, Math.sin(angle) * length);
}
add({ x, y }) {
return this.set(this.x + x, this.y + y);
}
addScaled({ x, y }, sx, sy = sx) {
return this.set(this.x + x * sx, this.y + y * sy);
}
subtract({ x, y }) {
return this.set(this.x - x, this.y - y);
}
multiply({ x, y }) {
return this.set(this.x * x, this.y * y);
}
divide({ x, y }) {
return this.set(this.x / x, this.y / y);
}
scale(sx, sy = sx) {
return this.set(this.x * sx, this.y * sy);
}
rotate(angle) {
const c = Math.cos(angle);
const s = Math.sin(angle);
return this.set(c * this.x - s * this.y, s * this.x + c * this.y);
}
perpLeft() {
return this.set(this.y, -this.x);
}
perpRight() {
return this.set(-this.y, this.x);
}
normalize() {
const length = this.length;
return this.set(this.x / length, this.y / length);
}
negate() {
return this.set(-this.x, -this.y);
}
dot({ x, y }) {
return this.x * x + this.y * y;
}
cross({ x, y }) {
return this.x * y + this.y * x;
}
angleTo({ x, y }) {
return Math.atan2(this.y - y, this.x - x);
}
distanceTo({ x, y }) {
return Math.hypot(this.x - x, this.y - y);
}
toFixed(fractionDigits = 0) {
return `Point(${this.x.toFixed(fractionDigits)}, ${this.y.toFixed(fractionDigits)})`;
}
toString() {
return `Point(${this.x}, ${this.y})`;
}
}
export class Rect {
static create(x, y, width, height) {
return new Rect(new Point(width, height), new Point(x, y));
}
#size;
#position;
constructor(size = new Point(), position = new Point()) {
this.#size = size ?? new Point();
this.#position = position ?? new Point();
}
get x() {
return this.#position.x;
}
set x(newValue) {
this.#position.x = newValue;
}
get y() {
return this.#position.y;
}
set y(newValue) {
this.#position.y = newValue;
}
get width() {
return this.#size.x;
}
set width(newValue) {
this.#size.x = newValue;
}
get height() {
return this.#size.y;
}
set height(newValue) {
this.#size.y = newValue;
}
get left() {
return this.#position.x;
}
set left(newValue) {
this.#position.x = newValue;
}
get right() {
return this.#position.x + this.#size.x;
}
set right(newValue) {
this.#size.x = newValue - this.#position.x;
}
get top() {
return this.#position.y;
}
set top(newValue) {
this.#position.y = newValue;
}
get bottom() {
return this.#position.y + this.#size.y;
}
set bottom(newValue) {
this.#size.y = newValue - this.#position.y;
}
get aspectRatio() {
return this.width / this.height;
}
get isHorizontal() {
return this.width > this.height;
}
get isVertical() {
return this.width < this.height;
}
get isSquare() {
return this.width === this.height;
}
set(x, y, width, height) {
this.#position.set(x, y);
this.#size.set(width, height);
return this;
}
reset() {
return this.set(0, 0, 0, 0);
}
copy({ x, y, width, height }) {
return this.set(x, y, width, height);
}
clone() {
return new Rect(this.#size.clone(), this.#position.clone());
}
toFixed(fractionDigits = 0) {
return `Rect(${this.x.toFixed(fractionDigits)}, ${this.y.toFixed(fractionDigits)}, ${this.width.toFixed(fractionDigits)}, ${this.height.toFixed(fractionDigits)})`;
}
toString() {
return `Rect(${this.x}, ${this.y}, ${this.width}, ${this.height})`;
}
}