98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
import { Component, ComponentNames } from ".";
|
|
import type { Coord2D, Dimension2D } from "../interfaces";
|
|
import { dotProduct, rotateVector, normalizeVector } 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 {
|
|
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))
|
|
.map((vertex) => {
|
|
return {
|
|
x: vertex.x + this.center.x,
|
|
y: vertex.y + this.center.y,
|
|
};
|
|
});
|
|
}
|
|
|
|
private getAxes() {
|
|
const corners: Coord2D[] = this.getVerticesRelativeToCenter();
|
|
const axes: Coord2D[] = [];
|
|
|
|
for (let i = 0; i < corners.length; ++i) {
|
|
const [cornerA, cornerB] = [
|
|
corners[i],
|
|
corners[(i + 1) % corners.length],
|
|
].map((corner) => rotateVector(corner, this.rotation));
|
|
|
|
axes.push(
|
|
normalizeVector({
|
|
x: cornerB.y - cornerA.y,
|
|
y: -(cornerB.x - cornerA.x),
|
|
})
|
|
);
|
|
}
|
|
|
|
return axes;
|
|
}
|
|
|
|
private project(axis: Coord2D): [number, number] {
|
|
const corners = this.getCornersRelativeToCenter();
|
|
let [min, max] = [Infinity, -Infinity];
|
|
|
|
for (const corner of corners) {
|
|
const rotated = rotateVector(corner, this.rotation);
|
|
const translated = {
|
|
x: rotated.x + this.center.x,
|
|
y: rotated.y + this.center.y,
|
|
};
|
|
const projection = dotProduct(translated, axis);
|
|
|
|
min = Math.min(projection, min);
|
|
max = Math.max(projection, max);
|
|
}
|
|
|
|
return [min, max];
|
|
}
|
|
}
|