124 lines
3.1 KiB
TypeScript
124 lines
3.1 KiB
TypeScript
import { axisAlignedBoundingBox } from "../collision/aa.ts";
|
|
import { CircleLike } from "../collision/circular.ts";
|
|
import { Vector } from "../mod.ts";
|
|
import { Point } from "./vector.ts";
|
|
|
|
export class Polygon {
|
|
points: Vector[];
|
|
center: Vector;
|
|
|
|
constructor(points: Point[]) {
|
|
this.points = points.map((p) => new Vector(p));
|
|
this.center = this.calcCenter();
|
|
}
|
|
|
|
draw(color?: string) {
|
|
for (let i = 0; i < this.points.length; i++) {
|
|
const p1 = this.points[i];
|
|
const p2 = this.points.at(i - this.points.length + 1)!;
|
|
doodler.line(p1.copy().add(this.center), p2.copy().add(this.center), {
|
|
color,
|
|
});
|
|
}
|
|
}
|
|
|
|
calcCenter() {
|
|
if (!this.points.length) return new Vector();
|
|
const center = new Vector();
|
|
|
|
for (const point of this.points) {
|
|
center.add(point);
|
|
}
|
|
center.div(this.points.length);
|
|
return center;
|
|
}
|
|
|
|
get circularHitbox(): CircleLike {
|
|
let greatestDistance = 0;
|
|
for (const p of this.points) {
|
|
greatestDistance = Math.max(
|
|
p.copy().add(this.center).dist(this.center),
|
|
greatestDistance,
|
|
);
|
|
}
|
|
|
|
return {
|
|
center: this.center.copy(),
|
|
radius: greatestDistance,
|
|
};
|
|
}
|
|
|
|
_aabb?: axisAlignedBoundingBox;
|
|
get AABB(): axisAlignedBoundingBox {
|
|
if (!this._aabb) {
|
|
this._aabb = this.recalculateAABB();
|
|
}
|
|
return this._aabb;
|
|
}
|
|
|
|
recalculateAABB(): axisAlignedBoundingBox {
|
|
let smallestX, biggestX, smallestY, biggestY;
|
|
smallestX =
|
|
smallestY =
|
|
Infinity;
|
|
biggestX =
|
|
biggestY =
|
|
-Infinity;
|
|
|
|
for (const p of this.points) {
|
|
const temp = p.copy().add(this.center);
|
|
smallestX = Math.min(temp.x, smallestX);
|
|
biggestX = Math.max(temp.x, biggestX);
|
|
smallestY = Math.min(temp.y, smallestY);
|
|
biggestY = Math.max(temp.y, biggestY);
|
|
}
|
|
|
|
return {
|
|
x: smallestX,
|
|
y: smallestY,
|
|
w: biggestX - smallestX,
|
|
h: biggestY - smallestY,
|
|
};
|
|
}
|
|
|
|
static createPolygon(sides = 3, radius = 100) {
|
|
sides = Math.round(sides);
|
|
if (sides < 3) {
|
|
throw "You need at least 3 sides for a polygon";
|
|
}
|
|
|
|
const poly = new Polygon([]);
|
|
// figure out the angles required
|
|
const rotangle = (Math.PI * 2) / sides;
|
|
let angle = 0;
|
|
// loop through and generate each point
|
|
for (let i = 0; i < sides; i++) {
|
|
angle = (i * rotangle) + ((Math.PI - rotangle) * 0.5);
|
|
const pt = new Vector(Math.cos(angle) * radius, Math.sin(angle) * radius);
|
|
poly.points.push(pt);
|
|
}
|
|
poly.center = poly.calcCenter();
|
|
return poly;
|
|
}
|
|
|
|
getEdges(): Vector[] {
|
|
const edges: Vector[] = [];
|
|
for (let i = 0; i < this.points.length; i++) {
|
|
const nextIndex = (i + 1) % this.points.length;
|
|
const edge = this.points[nextIndex].copy().add(this.center).sub(
|
|
this.points[i].copy().add(this.center),
|
|
);
|
|
edges.push(edge);
|
|
}
|
|
return edges;
|
|
}
|
|
|
|
getNearestPoint(p: Vector) {
|
|
let nearest = this.points[0];
|
|
for (const point of this.points) {
|
|
if (p.dist(point) < p.dist(nearest)) nearest = point;
|
|
}
|
|
return nearest;
|
|
}
|
|
}
|