doodler/geometry/polygon.ts
2023-11-05 01:41:21 -06:00

129 lines
3.3 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;
}
_circularBoundingBox?: CircleLike;
get circularBoundingBox(): CircleLike {
this._circularBoundingBox = this.calculateCircularBoundingBox();
return this._circularBoundingBox;
}
private calculateCircularBoundingBox() {
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 {
this._aabb = this.recalculateAABB();
return this._aabb;
}
private 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 + this.center.x,
y: smallestY + this.center.y,
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.copy().add(this.center);
}
}