Files
furniture/src/geometry/polygon.ts
T
2026-10-18 08:41:47 -04:00

59 lines
1.8 KiB
TypeScript

import type { Point } from '../types';
export function pointInPolygon(p: Point, poly: Point[]): boolean {
if (poly.length < 3) return true;
let inside = false;
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
const a = poly[i];
const b = poly[j];
if (onSegment(a, b, p)) return true;
const intersect = a.y > p.y !== b.y > p.y && p.x < ((b.x - a.x) * (p.y - a.y)) / (b.y - a.y) + a.x;
if (intersect) inside = !inside;
}
return inside;
}
function onSegment(a: Point, b: Point, p: Point): boolean {
const cross = (b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x);
if (Math.abs(cross) > 1e-9) return false;
const dot = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y);
if (dot < 0) return false;
const len2 = (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
return dot <= len2;
}
function closestOnSegment(a: Point, b: Point, p: Point): Point {
const dx = b.x - a.x;
const dy = b.y - a.y;
const len2 = dx * dx + dy * dy;
if (len2 === 0) return { ...a };
let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
t = Math.max(0, Math.min(1, t));
return { x: a.x + t * dx, y: a.y + t * dy };
}
export function nearestPointInPolygon(p: Point, poly: Point[]): Point {
if (poly.length < 3) return { ...p };
if (pointInPolygon(p, poly)) return { ...p };
let best = poly[0];
let bestD = Infinity;
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
const q = closestOnSegment(poly[j], poly[i], p);
const d = (q.x - p.x) ** 2 + (q.y - p.y) ** 2;
if (d < bestD) {
bestD = d;
best = q;
}
}
return best;
}
export function rectanglePoints(width: number, height: number): Point[] {
return [
{ x: 0, y: 0 },
{ x: width, y: 0 },
{ x: width, y: height },
{ x: 0, y: height },
];
}