UNPKG

@technobuddha/library

Version:
113 lines 9.91 kB
import { bounds } from "./bounds.js"; import { isRectangleInPolygon } from "./is-rectangle-in-polygon.js"; import { rotate } from "./rotate.js"; /** * Find the largest inscribed rectangle in a convex polygon * Uses rotating calipers algorithm */ export function largestInscribedRectangle(polygon) { if (polygon.length < 3) { throw new Error('Polygon must have at least 3 vertices'); } let maxRectangle = { x: 0, y: 0, width: 0, height: 0, area: 0, angle: 0 }; // For each edge of the polygon, consider it as one side of the rectangle for (let i = 0; i < polygon.length; i++) { const edge = getEdge(polygon, i); const angle = Math.atan2(edge.dy, edge.dx); // Rotate polygon so this edge is horizontal const rotatedPolygon = rotate(polygon, -angle); // Find largest rectangle with this edge as base const rect = findLargestAxisAlignedRectangle(rotatedPolygon); if (rect.area > maxRectangle.area) { maxRectangle = { ...rect, angle: angle, }; } } return maxRectangle; } /** * Find largest axis-aligned rectangle in polygon using sweepline */ function findLargestAxisAlignedRectangle(polygon) { const bounding = bounds(polygon); let maxArea = 0; let bestRect = { x: 0, y: 0, width: 0, height: 0, area: 0, angle: 0 }; // Get all unique x-coordinates const xCoords = [...new Set(polygon.map((p) => p.x))].sort((a, b) => a - b); // For each pair of x-coordinates, find maximum height for (let i = 0; i < xCoords.length; i++) { for (let j = i + 1; j < xCoords.length; j++) { const left = xCoords[i]; const right = xCoords[j]; const width = right - left; // Find the maximum height rectangle between these x-coordinates const height = getMaxHeightBetweenX(polygon, left, right); const area = width * height; if (area > maxArea) { maxArea = area; bestRect = { x: left, y: bounding.y, width, height, area, angle: 0, }; } } } return bestRect; } /** * Get maximum height of rectangle between two x-coordinates */ function getMaxHeightBetweenX(polygon, leftX, rightX) { const intersections = []; // Find all y-intersections of vertical lines at leftX and rightX for (let i = 0; i < polygon.length; i++) { const p1 = polygon[i]; const p2 = polygon[(i + 1) % polygon.length]; // Check intersection with left vertical line const leftY = getVerticalIntersection(p1, p2, leftX); if (leftY !== null) { intersections.push(leftY); } // Check intersection with right vertical line const rightY = getVerticalIntersection(p1, p2, rightX); if (rightY !== null) { intersections.push(rightY); } } intersections.sort((a, b) => a - b); // Find largest gap between intersections let maxHeight = 0; for (let i = 0; i < intersections.length - 1; i++) { const height = intersections[i + 1] - intersections[i]; if (isRectangleInPolygon({ x: leftX, y: intersections[i], width: rightX - leftX, height }, polygon)) { maxHeight = Math.max(maxHeight, height); } } return maxHeight; } // Helper functions function getEdge(polygon, index) { const p1 = polygon[index]; const p2 = polygon[(index + 1) % polygon.length]; return { dx: p2.x - p1.x, dy: p2.y - p1.y, }; } function getVerticalIntersection(p1, p2, x) { if (p1.x === p2.x) { return null; } // Vertical line if (x < Math.min(p1.x, p2.x) || x > Math.max(p1.x, p2.x)) { return null; } const t = (x - p1.x) / (p2.x - p1.x); return p1.y + t * (p2.y - p1.y); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9seWdvbi1sYXJnZXN0LXJlY3RhbmdsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9wb2x5Z29uLWxhcmdlc3QtcmVjdGFuZ2xlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFckMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDcEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUlyQzs7O0dBR0c7QUFDSCxNQUFNLFVBQVUseUJBQXlCLENBQUMsT0FBZ0I7SUFDeEQsSUFBSSxPQUFPLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBRUQsSUFBSSxZQUFZLEdBQWMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxDQUFDO0lBRXJGLHlFQUF5RTtJQUN6RSxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ3hDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDakMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUUzQyw0Q0FBNEM7UUFDNUMsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRS9DLGdEQUFnRDtRQUNoRCxNQUFN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