2d-physics-engine
Version:
A lightweight, flexible 2D physics engine with ECS architecture, built with TypeScript
115 lines • 4.54 kB
JavaScript
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