summaryrefslogtreecommitdiff
path: root/src/engine/components/BoundingBox.ts
diff options
context:
space:
mode:
authorElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-01 18:56:58 -0700
committerElizabeth Hunt <elizabeth.hunt@simponic.xyz>2024-03-01 18:56:58 -0700
commita8d07a790395e14fe7aedd3ba638db466f9c0842 (patch)
tree644f60a6bca79ceb55f24fcab7bdb3dee52c2d6e /src/engine/components/BoundingBox.ts
parentaa08a8943a9a2d4a0e51893eebe6900bca7a7251 (diff)
downloadthe-abstraction-engine-a8d07a790395e14fe7aedd3ba638db466f9c0842.tar.gz
the-abstraction-engine-a8d07a790395e14fe7aedd3ba638db466f9c0842.zip
boundingbox + draw player
Diffstat (limited to 'src/engine/components/BoundingBox.ts')
-rw-r--r--src/engine/components/BoundingBox.ts122
1 files changed, 122 insertions, 0 deletions
diff --git a/src/engine/components/BoundingBox.ts b/src/engine/components/BoundingBox.ts
new file mode 100644
index 0000000..d64041f
--- /dev/null
+++ b/src/engine/components/BoundingBox.ts
@@ -0,0 +1,122 @@
+import { Component, ComponentNames } from ".";
+import type { Coord2D, Dimension2D } from "../interfaces";
+import { dotProduct, rotateVector } from "../utils";
+
+export class BoundingBox extends Component {
+ public center: Coord2D;
+ public dimension: Dimension2D;
+ public rotation: number;
+
+ constructor(center: Coord2D, dimension: Dimension2D, rotation?: number) {
+ super(ComponentNames.BoundingBox);
+
+ this.center = center;
+ this.dimension = dimension;
+ this.rotation = rotation ?? 0;
+ }
+
+ public isCollidingWith(box: BoundingBox): boolean {
+ // optimization; when neither rotates just check if they overlap
+ if (this.rotation == 0 && box.rotation == 0) {
+ const thisTopLeft = this.getTopLeft();
+ const thisBottomRight = this.getBottomRight();
+
+ const thatTopLeft = box.getTopLeft();
+ const thatBottomRight = box.getBottomRight();
+
+ if (
+ thisBottomRight.x <= thatTopLeft.x ||
+ thisTopLeft.x >= thatBottomRight.x ||
+ thisBottomRight.y <= thatTopLeft.y ||
+ thisTopLeft.y >= thatBottomRight.y
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ // https://en.wikipedia.org/wiki/Hyperplane_separation_theorem
+ const boxes = [this.getVertices(), box.getVertices()];
+ for (const poly of boxes) {
+ for (let i = 0; i < poly.length; i++) {
+ const [A, B] = [poly[i], poly[(i + 1) % poly.length]];
+ const normal: Coord2D = { x: B.y - A.y, y: A.x - B.x };
+
+ const [[minThis, maxThis], [minBox, maxBox]] = boxes.map((box) =>
+ box.reduce(
+ ([min, max], vertex) => {
+ const projection = dotProduct(normal, vertex);
+ return [Math.min(min, projection), Math.max(max, projection)];
+ },
+ [Infinity, -Infinity],
+ ),
+ );
+
+ if (maxThis < minBox || maxBox < minThis) return false;
+ }
+ }
+
+ return true;
+ }
+
+ public getVertices(): Coord2D[] {
+ return [
+ { x: -this.dimension.width / 2, y: -this.dimension.height / 2 },
+ { x: -this.dimension.width / 2, y: this.dimension.height / 2 },
+ { x: this.dimension.width / 2, y: this.dimension.height / 2 },
+ { x: this.dimension.width / 2, y: -this.dimension.height / 2 },
+ ]
+ .map((vertex) => rotateVector(vertex, this.rotation)) // rotate
+ .map((vertex) => {
+ // translate
+ return {
+ x: vertex.x + this.center.x,
+ y: vertex.y + this.center.y,
+ };
+ });
+ }
+
+ public getRotationInPiOfUnitCircle(): number {
+ let rads = this.rotation * (Math.PI / 180);
+ if (rads >= Math.PI) {
+ // Physics system guarantees rotation \in [0, 360)
+ rads -= Math.PI;
+ }
+ return rads;
+ }
+
+ public getOutscribedBoxDims(): Dimension2D {
+ let rads = this.getRotationInPiOfUnitCircle();
+ const { width, height } = this.dimension;
+
+ if (rads == 0) return this.dimension;
+
+ if (rads <= Math.PI / 2) {
+ return {
+ width: Math.abs(height * Math.sin(rads) + width * Math.cos(rads)),
+ height: Math.abs(width * Math.sin(rads) + height * Math.cos(rads)),
+ };
+ }
+
+ rads -= Math.PI / 2;
+ return {
+ width: Math.abs(height * Math.cos(rads) + width * Math.sin(rads)),
+ height: Math.abs(width * Math.cos(rads) + height * Math.sin(rads)),
+ };
+ }
+
+ public getTopLeft(): Coord2D {
+ return {
+ x: this.center.x - this.dimension.width / 2,
+ y: this.center.y - this.dimension.height / 2,
+ };
+ }
+
+ public getBottomRight(): Coord2D {
+ return {
+ x: this.center.x + this.dimension.width / 2,
+ y: this.center.y + this.dimension.height / 2,
+ };
+ }
+}