UNPKG

2d-physics-engine

Version:

A lightweight, flexible 2D physics engine with ECS architecture, built with TypeScript

115 lines 4.54 kB
import { Vector2 } from './Vector2'; /** * Axis-Aligned Bounding Box (AABB). * Always normalized: min.x <= max.x and min.y <= max.y. */ export class AABB { constructor(min, max) { Object.defineProperty(this, "min", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "max", { enumerable: true, configurable: true, writable: true, value: void 0 }); // Normalize automatically to avoid invalid AABBs this.min = new Vector2(Math.min(min.x, max.x), Math.min(min.y, max.y)); this.max = new Vector2(Math.max(min.x, max.x), Math.max(min.y, max.y)); } /** ---------------- FACTORY HELPERS ---------------- **/ /** Create AABB from center and half-size. */ static fromCenter(center, halfSize) { return new AABB(center.subtract(halfSize), center.add(halfSize)); } /** Create AABB from minimum corner and width/height. */ static fromMinSize(min, width, height) { return new AABB(min, new Vector2(min.x + width, min.y + height)); } /** ---------------- GEOMETRY GETTERS ---------------- **/ get width() { return this.max.x - this.min.x; } get height() { return this.max.y - this.min.y; } get size() { return new Vector2(this.width, this.height); } get center() { return new Vector2((this.min.x + this.max.x) * 0.5, (this.min.y + this.max.y) * 0.5); } get area() { return this.width * this.height; } get perimeter() { return 2 * (this.width + this.height); } /** Length of the diagonal. */ get diagonal() { return Math.sqrt(this.width ** 2 + this.height ** 2); } /** Returns true if width or height is zero (degenerate box). */ get isEmpty() { return this.width === 0 || this.height === 0; } /** ---------------- OPERATIONS ---------------- **/ /** Moves the AABB by a vector. */ move(offset) { return new AABB(this.min.add(offset), this.max.add(offset)); } /** Expands or shrinks the bounds by a margin. */ expand(margin) { return new AABB(new Vector2(this.min.x - margin, this.min.y - margin), new Vector2(this.max.x + margin, this.max.y + margin)); } /** Returns a copy of this AABB. */ clone() { return new AABB(this.min.clone(), this.max.clone()); } /** ---------------- CONTAINMENT & INTERSECTION ---------------- **/ /** Checks if a point is inside the bounds. */ containsPoint(point, inclusive = true) { if (inclusive) { return point.x >= this.min.x && point.x <= this.max.x && point.y >= this.min.y && point.y <= this.max.y; } else { return point.x > this.min.x && point.x < this.max.x && point.y > this.min.y && point.y < this.max.y; } } /** True if two AABBs overlap or touch. */ intersects(other, inclusive = true) { if (inclusive) { return (this.min.x <= other.max.x && this.max.x >= other.min.x && this.min.y <= other.max.y && this.max.y >= other.min.y); } else { return this.min.x < other.max.x && this.max.x > other.min.x && this.min.y < other.max.y && this.max.y > other.min.y; } } /** Returns true if `other` is entirely inside this AABB. */ contains(other, inclusive = true) { if (inclusive) { return (other.min.x >= this.min.x && other.max.x <= this.max.x && other.min.y >= this.min.y && other.max.y <= this.max.y); } else { return other.min.x > this.min.x && other.max.x < this.max.x && other.min.y > this.min.y && other.max.y < this.max.y; } } /** Returns a new AABB which encloses both boxes. */ union(other) { return new AABB(new Vector2(Math.min(this.min.x, other.min.x), Math.min(this.min.y, other.min.y)), new Vector2(Math.max(this.max.x, other.max.x), Math.max(this.max.y, other.max.y))); } /** Returns intersection AABB or null if none. */ intersection(other) { const min = new Vector2(Math.max(this.min.x, other.min.x), Math.max(this.min.y, other.min.y)); const max = new Vector2(Math.min(this.max.x, other.max.x), Math.min(this.max.y, other.max.y)); if (min.x <= max.x && min.y <= max.y) { return new AABB(min, max); } return null; } } //# sourceMappingURL=AABB.js.map