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); } }