pattapatta 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/circle-BDbKfrdR.d.ts +12 -0
- package/dist/circlePacking/index.d.ts +78 -0
- package/dist/circlePacking/index.js +381 -0
- package/dist/circlePacking/index.js.map +1 -0
- package/dist/cli.js +365 -0
- package/dist/cli.js.map +1 -0
- package/dist/construction/index.d.ts +26 -0
- package/dist/construction/index.js +102 -0
- package/dist/construction/index.js.map +1 -0
- package/dist/contour/index.d.ts +20 -0
- package/dist/contour/index.js +118 -0
- package/dist/contour/index.js.map +1 -0
- package/dist/conversion/index.d.ts +31 -0
- package/dist/conversion/index.js +99 -0
- package/dist/conversion/index.js.map +1 -0
- package/dist/group-BNjVVYSQ.d.ts +13 -0
- package/dist/hatch/index.d.ts +41 -0
- package/dist/hatch/index.js +253 -0
- package/dist/hatch/index.js.map +1 -0
- package/dist/hull/index.d.ts +16 -0
- package/dist/hull/index.js +97 -0
- package/dist/hull/index.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +3933 -0
- package/dist/index.js.map +1 -0
- package/dist/meshing/index.d.ts +125 -0
- package/dist/meshing/index.js +1333 -0
- package/dist/meshing/index.js.map +1 -0
- package/dist/morphology/index.d.ts +58 -0
- package/dist/morphology/index.js +268 -0
- package/dist/morphology/index.js.map +1 -0
- package/dist/optimisation/index.d.ts +43 -0
- package/dist/optimisation/index.js +400 -0
- package/dist/optimisation/index.js.map +1 -0
- package/dist/path-0RjhYjJs.d.ts +21 -0
- package/dist/pointSet/index.d.ts +24 -0
- package/dist/pointSet/index.js +139 -0
- package/dist/pointSet/index.js.map +1 -0
- package/dist/polygonisation/index.d.ts +44 -0
- package/dist/polygonisation/index.js +310 -0
- package/dist/polygonisation/index.js.map +1 -0
- package/dist/predicates/index.d.ts +35 -0
- package/dist/predicates/index.js +159 -0
- package/dist/predicates/index.js.map +1 -0
- package/dist/processing/index.d.ts +54 -0
- package/dist/processing/index.js +457 -0
- package/dist/processing/index.js.map +1 -0
- package/dist/segment-BKuIFMKD.d.ts +10 -0
- package/dist/segmentSet/index.d.ts +25 -0
- package/dist/segmentSet/index.js +109 -0
- package/dist/segmentSet/index.js.map +1 -0
- package/dist/shapeBoolean/index.d.ts +41 -0
- package/dist/shapeBoolean/index.js +211 -0
- package/dist/shapeBoolean/index.js.map +1 -0
- package/dist/svg/index.d.ts +38 -0
- package/dist/svg/index.js +317 -0
- package/dist/svg/index.js.map +1 -0
- package/dist/tiling/index.d.ts +32 -0
- package/dist/tiling/index.js +344 -0
- package/dist/tiling/index.js.map +1 -0
- package/dist/transformation/index.d.ts +37 -0
- package/dist/transformation/index.js +184 -0
- package/dist/transformation/index.js.map +1 -0
- package/dist/triangulation/index.d.ts +48 -0
- package/dist/triangulation/index.js +334 -0
- package/dist/triangulation/index.js.map +1 -0
- package/dist/vec2-CWyXN1Wj.d.ts +10 -0
- package/dist/voronoi/index.d.ts +31 -0
- package/dist/voronoi/index.js +211 -0
- package/dist/voronoi/index.js.map +1 -0
- package/package.json +165 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/types/group.ts","../../src/clipper/convert.ts","../../src/predicates/index.ts","../../src/morphology/buffer.ts","../../src/morphology/index.ts"],"names":["path"],"mappings":"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","file":"index.js","sourcesContent":["/** 2D point / vector. */\nexport type Vec2 = {\n x: number\n y: number\n}\n\nexport function vec2(x: number, y: number): Vec2 {\n return { x, y }\n}\n\nexport function cloneVec2(v: Vec2): Vec2 {\n return { x: v.x, y: v.y }\n}\n\nexport function equalsVec2(a: Vec2, b: Vec2, eps = 1e-9): boolean {\n return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps\n}\n","import type { Vec2 } from './vec2.js'\nimport { cloneVec2, equalsVec2 } from './vec2.js'\n\n/** Ordered ring of vertices (typically without repeating the closing vertex). */\nexport type Ring = Vec2[]\n\n/**\n * A path: one exterior ring plus optional holes, or an open polyline.\n * `rings[0]` is the exterior (or the open polyline when `closed` is false).\n * Additional rings are holes (only meaningful when `closed` is true).\n */\nexport type Path = {\n rings: Ring[]\n closed: boolean\n}\n\nexport function path(rings: Ring[], closed = true): Path {\n return {\n rings: rings.map((ring) => ring.map(cloneVec2)),\n closed,\n }\n}\n\nexport function polyline(points: Ring): Path {\n return path([points], false)\n}\n\nexport function polygon(exterior: Ring, holes: Ring[] = []): Path {\n return path([exterior, ...holes], true)\n}\n\n/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */\nexport function normalizeRing(ring: Ring, eps = 1e-9): Ring {\n if (ring.length < 2) return ring.map(cloneVec2)\n const first = ring[0]\n const last = ring[ring.length - 1]\n if (first && last && equalsVec2(first, last, eps)) {\n return ring.slice(0, -1).map(cloneVec2)\n }\n return ring.map(cloneVec2)\n}\n\nexport function clonePath(p: Path): Path {\n return {\n closed: p.closed,\n rings: p.rings.map((ring) => ring.map(cloneVec2)),\n }\n}\n","import type { Path } from './path.js'\nimport { clonePath } from './path.js'\n\n/**\n * Ordered collection of paths. Later entries are “on top” for occlusion-style ops.\n */\nexport type Group = {\n paths: Path[]\n}\n\nexport function group(paths: Path[] = []): Group {\n return { paths: paths.map(clonePath) }\n}\n\nexport function groupFromPaths(...paths: Path[]): Group {\n return group(paths)\n}\n\nexport function cloneGroup(g: Group): Group {\n return group(g.paths)\n}\n","import {\n FillRule,\n areaD,\n differenceD,\n intersectD,\n isPositiveD,\n pointInPolygonD,\n PointInPolygonResult,\n unionD,\n xorD,\n type PathD,\n type PathsD,\n type PointD,\n} from 'clipper2-ts'\nimport type { Group, Path, Ring, Vec2 } from '../types/index.js'\nimport { group, normalizeRing, path, vec2 } from '../types/index.js'\n\nexport const DEFAULT_FILL_RULE = FillRule.NonZero\n\nexport function ringToPathD(ring: Ring): PathD {\n const pts: PointD[] = []\n for (const v of normalizeRing(ring)) {\n pts.push({ x: v.x, y: v.y, z: 0 })\n }\n return pts\n}\n\n/** Closed path → Clipper subject/clip paths (exterior + holes). */\nexport function pathToPathsD(p: Path): PathsD {\n if (!p.closed || p.rings.length === 0) {\n throw new Error('Boolean ops require a closed path with at least one ring')\n }\n return p.rings.map((ring) => ringToPathD(ring))\n}\n\nexport function pathsDToGroup(paths: PathsD): Group {\n if (paths.length === 0) return group([])\n\n const outers: { ring: Ring; holes: Ring[] }[] = []\n const holes: Ring[] = []\n\n for (const pd of paths) {\n const ring = pathDToRing(pd)\n if (ring.length < 3) continue\n if (isPositiveD(pd)) {\n outers.push({ ring, holes: [] })\n } else {\n holes.push(ring)\n }\n }\n\n for (const hole of holes) {\n const sample = hole[0]\n if (!sample) continue\n let assigned = false\n for (const outer of outers) {\n if (pointInRing(sample, outer.ring)) {\n outer.holes.push(hole)\n assigned = true\n break\n }\n }\n if (!assigned) {\n // Treat unmatched negative rings as their own outers (orientation flip).\n outers.push({ ring: [...hole].reverse(), holes: [] })\n }\n }\n\n return group(outers.map((o) => path([o.ring, ...o.holes], true)))\n}\n\nexport function pathDToRing(pd: PathD): Ring {\n return normalizeRing(pd.map((p) => vec2(p.x, p.y)))\n}\n\nexport function pointInRing(point: Vec2, ring: Ring): boolean {\n const result = pointInPolygonD(\n { x: point.x, y: point.y, z: 0 },\n ringToPathD(ring),\n )\n return (\n result === PointInPolygonResult.IsInside ||\n result === PointInPolygonResult.IsOn\n )\n}\n\nexport function clipUnion(a: PathsD, b: PathsD): PathsD {\n return unionD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipIntersect(a: PathsD, b: PathsD): PathsD {\n return intersectD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipDifference(a: PathsD, b: PathsD): PathsD {\n return differenceD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipXor(a: PathsD, b: PathsD): PathsD {\n return xorD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function pathsArea(paths: PathsD): number {\n let sum = 0\n for (const p of paths) {\n sum += Math.abs(areaD(p))\n }\n return sum\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { vec2 } from '../types/index.js'\nimport {\n pathToPathsD,\n pathsArea,\n pointInRing,\n} from '../clipper/convert.js'\n\n/** Signed area of a closed ring (shoelace). */\nexport function ringArea(ring: Vec2[]): number {\n if (ring.length < 3) return 0\n let sum = 0\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n sum += a.x * b.y - b.x * a.y\n }\n return sum / 2\n}\n\n/** Absolute area of a closed path (exterior minus holes). */\nexport function area(p: Path): number {\n if (!p.closed || p.rings.length === 0) return 0\n try {\n return pathsArea(pathToPathsD(p))\n } catch {\n let total = 0\n for (let i = 0; i < p.rings.length; i++) {\n const a = Math.abs(ringArea(p.rings[i]!))\n total += i === 0 ? a : -a\n }\n return Math.max(0, total)\n }\n}\n\nexport function areaGroup(g: Group): number {\n return g.paths.reduce((sum, p) => sum + area(p), 0)\n}\n\n/** Centroid of a closed exterior ring (ignores holes for Phase 2). */\nexport function centroid(p: Path): Vec2 {\n const ring = p.rings[0]\n if (!ring || ring.length === 0) return vec2(0, 0)\n const a = ringArea(ring)\n if (Math.abs(a) < 1e-12) {\n let sx = 0\n let sy = 0\n for (const v of ring) {\n sx += v.x\n sy += v.y\n }\n return vec2(sx / ring.length, sy / ring.length)\n }\n let cx = 0\n let cy = 0\n for (let i = 0; i < ring.length; i++) {\n const p0 = ring[i]!\n const p1 = ring[(i + 1) % ring.length]!\n const cross = p0.x * p1.y - p1.x * p0.y\n cx += (p0.x + p1.x) * cross\n cy += (p0.y + p1.y) * cross\n }\n return vec2(cx / (6 * a), cy / (6 * a))\n}\n\nexport function containsPoint(p: Path, point: Vec2): boolean {\n if (!p.closed || !p.rings[0]) return false\n if (!pointInRing(point, p.rings[0])) return false\n for (let i = 1; i < p.rings.length; i++) {\n const hole = p.rings[i]\n if (hole && pointInRing(point, hole)) return false\n }\n return true\n}\n\nexport function bounds(p: Path): {\n minX: number\n minY: number\n maxX: number\n maxY: number\n} {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const ring of p.rings) {\n for (const v of ring) {\n minX = Math.min(minX, v.x)\n minY = Math.min(minY, v.y)\n maxX = Math.max(maxX, v.x)\n maxY = Math.max(maxY, v.y)\n }\n }\n if (!Number.isFinite(minX)) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 }\n }\n return { minX, minY, maxX, maxY }\n}\n\nexport function width(p: Path): number {\n const b = bounds(p)\n return b.maxX - b.minX\n}\n\nexport function height(p: Path): number {\n const b = bounds(p)\n return b.maxY - b.minY\n}\n\n/** Rough polygon similarity via absolute area difference relative to max area. */\nexport function areaSimilarity(a: Path, b: Path): number {\n const aa = area(a)\n const bb = area(b)\n const denom = Math.max(aa, bb, 1e-12)\n return 1 - Math.abs(aa - bb) / denom\n}\n\nexport const predicates = {\n area,\n areaGroup,\n ringArea,\n centroid,\n containsPoint,\n bounds,\n width,\n height,\n areaSimilarity,\n}\n","import type { Group, Path } from '../types/index.js'\nimport { group } from '../types/index.js'\nimport { inflatePathsD, JoinType, EndType } from 'clipper2-ts'\nimport { pathToPathsD, pathsDToGroup } from '../clipper/convert.js'\n\nexport type BufferOptions = {\n /** Join style for offset. Default round. */\n join?: 'round' | 'miter' | 'square'\n}\n\n/**\n * Offset / buffer a closed path by `delta` (positive = expand, negative = erode).\n * Returns a group of resulting closed paths (may split or vanish when eroding).\n */\nexport function buffer(\n path: Path,\n delta: number,\n options: BufferOptions = {},\n): Group {\n const subject = pathToPathsD(path)\n const join =\n options.join === 'miter'\n ? JoinType.Miter\n : options.join === 'square'\n ? JoinType.Square\n : JoinType.Round\n const inflated = inflatePathsD(subject, delta, join, EndType.Polygon)\n return pathsDToGroup(inflated)\n}\n\n/**\n * Erode by `|amount|` then dilate by `|amount|` (opening). Useful for cleaning.\n */\nexport function erosionDilation(p: Path, amount: number): Group {\n const a = Math.abs(amount)\n if (a < 1e-12) return group([p])\n const eroded = buffer(p, -a)\n if (eroded.paths.length === 0) return eroded\n const dilated: Path[] = []\n for (const q of eroded.paths) {\n dilated.push(...buffer(q, a).paths)\n }\n return group(dilated)\n}\n\n/**\n * Dilate by `|amount|` then erode by `|amount|` (closing). Fills small gaps.\n */\nexport function dilationErosion(p: Path, amount: number): Group {\n const a = Math.abs(amount)\n if (a < 1e-12) return group([p])\n const dilated = buffer(p, a)\n if (dilated.paths.length === 0) return dilated\n const eroded: Path[] = []\n for (const q of dilated.paths) {\n eroded.push(...buffer(q, -a).paths)\n }\n return group(eroded)\n}\n","import { simplifyPathD, minkowskiSumD, minkowskiDiffD } from 'clipper2-ts'\nimport type { Group, Path, Ring, Vec2 } from '../types/index.js'\nimport { path, normalizeRing } from '../types/index.js'\nimport { pathDToRing, ringToPathD, pathsDToGroup } from '../clipper/convert.js'\nimport { centroid } from '../predicates/index.js'\nimport { buffer, erosionDilation, dilationErosion } from './buffer.js'\n\nexport type { BufferOptions } from './buffer.js'\nexport { buffer, erosionDilation, dilationErosion } from './buffer.js'\n\n/**\n * Ramer–Douglas–Peucker simplify (Clipper2). `epsilon` is absolute distance.\n */\nexport function simplify(p: Path, epsilon: number): Path {\n if (epsilon <= 0 || p.rings.length === 0) return p\n const rings: Ring[] = []\n for (const ring of p.rings) {\n const simplified = simplifyPathD(ringToPathD(ring), epsilon, p.closed)\n const out = pathDToRing(simplified)\n if (out.length >= (p.closed ? 3 : 2)) rings.push(out)\n }\n if (rings.length === 0) return path([], p.closed)\n return path(rings, p.closed)\n}\n\n/** Round vertex coordinates to `decimals` places. */\nexport function reducePrecision(p: Path, decimals: number): Path {\n const f = 10 ** Math.max(0, Math.floor(decimals))\n const round = (n: number) => Math.round(n * f) / f\n return path(\n p.rings.map((ring) =>\n normalizeRing(ring.map((v) => ({ x: round(v.x), y: round(v.y) }))),\n ),\n p.closed,\n )\n}\n\n/**\n * Chaikin corner cutting. `iterations` ≥ 1; closed rings stay closed.\n * Ratio defaults to classic 1/4–3/4 cut.\n */\nexport function chaikinCut(p: Path, iterations = 1, ratio = 0.25): Path {\n const r = Math.min(0.49, Math.max(0.01, ratio))\n let rings = p.rings\n for (let i = 0; i < Math.max(1, Math.floor(iterations)); i++) {\n rings = rings.map((ring) => chaikinRing(ring, r, p.closed))\n }\n return path(rings, p.closed)\n}\n\n/** Alias: Chaikin smoothing for `iterations` passes. */\nexport function smooth(p: Path, iterations = 2): Path {\n return chaikinCut(p, iterations)\n}\n\n/** Radial displace from centroid: r' = r * (1 + amplitude * cos(freq * θ + phase)). */\nexport function radialWarp(\n p: Path,\n amplitude: number,\n frequency = 3,\n phase = 0,\n): Path {\n const c = centroid(p)\n return mapVerts(p, (v) => {\n const dx = v.x - c.x\n const dy = v.y - c.y\n const dist = Math.hypot(dx, dy)\n if (dist < 1e-12) return v\n const theta = Math.atan2(dy, dx)\n const s = 1 + amplitude * Math.cos(frequency * theta + phase)\n return { x: c.x + dx * s, y: c.y + dy * s }\n })\n}\n\n/** Sine warp along X: y' = y + amplitude * sin(2π * frequency * x + phase). */\nexport function sineWarp(\n p: Path,\n amplitude: number,\n frequency = 1,\n phase = 0,\n): Path {\n return mapVerts(p, (v) => ({\n x: v.x,\n y: v.y + amplitude * Math.sin(2 * Math.PI * frequency * v.x + phase),\n }))\n}\n\n/** Minkowski sum of two closed paths (Clipper2). */\nexport function minkSum(a: Path, b: Path): Group {\n const pattern = ringToPathD(b.rings[0] ?? [])\n const subject = ringToPathD(a.rings[0] ?? [])\n return pathsDToGroup(minkowskiSumD(pattern, subject, true))\n}\n\n/** Minkowski difference of two closed paths (Clipper2). */\nexport function minkDifference(a: Path, b: Path): Group {\n const pattern = ringToPathD(b.rings[0] ?? [])\n const subject = ringToPathD(a.rings[0] ?? [])\n return pathsDToGroup(minkowskiDiffD(pattern, subject, true))\n}\n\nfunction chaikinRing(ring: Ring, ratio: number, closed: boolean): Ring {\n if (ring.length < 2) return ring.map((v) => ({ ...v }))\n const out: Vec2[] = []\n const n = ring.length\n const edges = closed ? n : n - 1\n for (let i = 0; i < edges; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n out.push({\n x: a.x * (1 - ratio) + b.x * ratio,\n y: a.y * (1 - ratio) + b.y * ratio,\n })\n out.push({\n x: a.x * ratio + b.x * (1 - ratio),\n y: a.y * ratio + b.y * (1 - ratio),\n })\n }\n if (!closed) {\n out.unshift({ ...ring[0]! })\n out.push({ ...ring[n - 1]! })\n }\n return closed ? normalizeRing(out) : out\n}\n\nfunction mapVerts(p: Path, fn: (v: Vec2) => Vec2): Path {\n return path(\n p.rings.map((ring) => normalizeRing(ring.map(fn))),\n p.closed,\n )\n}\n\nexport const morphology = {\n buffer,\n erosionDilation,\n dilationErosion,\n simplify,\n reducePrecision,\n chaikinCut,\n smooth,\n radialWarp,\n sineWarp,\n minkSum,\n minkDifference,\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { V as Vec2 } from '../vec2-CWyXN1Wj.js';
|
|
2
|
+
import { C as Circle } from '../circle-BDbKfrdR.js';
|
|
3
|
+
import { P as Path } from '../path-0RjhYjJs.js';
|
|
4
|
+
|
|
5
|
+
/** Axis-aligned envelope (alias of hull.boundingBox). */
|
|
6
|
+
declare function envelope(p: Path): Path;
|
|
7
|
+
/** Largest inscribed circle (fully contained). */
|
|
8
|
+
declare function maximumInscribedCircle(p: Path, tolerance?: number): Circle | null;
|
|
9
|
+
/** Alias: successive LECs until radius drops below `minRadius`. */
|
|
10
|
+
declare function largestEmptyCircles(p: Path, minRadius: number, tolerance?: number): Circle[];
|
|
11
|
+
/** Single largest empty circle (no obstacle circles). */
|
|
12
|
+
declare function largestEmptyCircle(p: Path, tolerance?: number): Circle | null;
|
|
13
|
+
/**
|
|
14
|
+
* Heuristic maximum axis-aligned inscribed rectangle: inset the AABB until all
|
|
15
|
+
* four corners lie inside. If the AABB corners are already inside, returns the envelope.
|
|
16
|
+
*/
|
|
17
|
+
declare function maximumInscribedAARectangle(p: Path): Path;
|
|
18
|
+
/** Closest point on the path boundary to `point`. */
|
|
19
|
+
declare function closestPoint(p: Path, point: Vec2): Vec2;
|
|
20
|
+
/** Closest vertex of the path to `point`. */
|
|
21
|
+
declare function closestVertex(p: Path, point: Vec2): Vec2;
|
|
22
|
+
/** Closest pair among a point set (O(n²); fine for small sets). */
|
|
23
|
+
declare function closestPointPair(points: Vec2[]): [Vec2, Vec2] | null;
|
|
24
|
+
/** Farthest pair among a point set (O(n²)). */
|
|
25
|
+
declare function farthestPointPair(points: Vec2[]): [Vec2, Vec2] | null;
|
|
26
|
+
/**
|
|
27
|
+
* Minimum bounding circle of a path's vertices (Welzl on convex hull).
|
|
28
|
+
*/
|
|
29
|
+
declare function minimumBoundingCircle(p: Path): Circle;
|
|
30
|
+
declare const optimisation: {
|
|
31
|
+
envelope: typeof envelope;
|
|
32
|
+
maximumInscribedCircle: typeof maximumInscribedCircle;
|
|
33
|
+
largestEmptyCircle: typeof largestEmptyCircle;
|
|
34
|
+
largestEmptyCircles: typeof largestEmptyCircles;
|
|
35
|
+
maximumInscribedAARectangle: typeof maximumInscribedAARectangle;
|
|
36
|
+
closestPoint: typeof closestPoint;
|
|
37
|
+
closestVertex: typeof closestVertex;
|
|
38
|
+
closestPointPair: typeof closestPointPair;
|
|
39
|
+
farthestPointPair: typeof farthestPointPair;
|
|
40
|
+
minimumBoundingCircle: typeof minimumBoundingCircle;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export { closestPoint, closestPointPair, closestVertex, envelope, farthestPointPair, largestEmptyCircle, largestEmptyCircles, maximumInscribedAARectangle, maximumInscribedCircle, minimumBoundingCircle, optimisation };
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { FillRule, pointInPolygonD, PointInPolygonResult } from 'clipper2-ts';
|
|
2
|
+
|
|
3
|
+
// src/types/vec2.ts
|
|
4
|
+
function cloneVec2(v) {
|
|
5
|
+
return { x: v.x, y: v.y };
|
|
6
|
+
}
|
|
7
|
+
function equalsVec2(a, b, eps = 1e-9) {
|
|
8
|
+
return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/types/circle.ts
|
|
12
|
+
function circle(x, y, r) {
|
|
13
|
+
return { x, y, r };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// src/types/path.ts
|
|
17
|
+
function path(rings, closed = true) {
|
|
18
|
+
return {
|
|
19
|
+
rings: rings.map((ring) => ring.map(cloneVec2)),
|
|
20
|
+
closed
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function polygon(exterior, holes = []) {
|
|
24
|
+
return path([exterior, ...holes], true);
|
|
25
|
+
}
|
|
26
|
+
function normalizeRing(ring, eps = 1e-9) {
|
|
27
|
+
if (ring.length < 2) return ring.map(cloneVec2);
|
|
28
|
+
const first = ring[0];
|
|
29
|
+
const last = ring[ring.length - 1];
|
|
30
|
+
if (first && last && equalsVec2(first, last, eps)) {
|
|
31
|
+
return ring.slice(0, -1).map(cloneVec2);
|
|
32
|
+
}
|
|
33
|
+
return ring.map(cloneVec2);
|
|
34
|
+
}
|
|
35
|
+
FillRule.NonZero;
|
|
36
|
+
function ringToPathD(ring) {
|
|
37
|
+
const pts = [];
|
|
38
|
+
for (const v of normalizeRing(ring)) {
|
|
39
|
+
pts.push({ x: v.x, y: v.y, z: 0 });
|
|
40
|
+
}
|
|
41
|
+
return pts;
|
|
42
|
+
}
|
|
43
|
+
function pointInRing(point, ring) {
|
|
44
|
+
const result = pointInPolygonD(
|
|
45
|
+
{ x: point.x, y: point.y, z: 0 },
|
|
46
|
+
ringToPathD(ring)
|
|
47
|
+
);
|
|
48
|
+
return result === PointInPolygonResult.IsInside || result === PointInPolygonResult.IsOn;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/predicates/index.ts
|
|
52
|
+
function containsPoint(p, point) {
|
|
53
|
+
if (!p.closed || !p.rings[0]) return false;
|
|
54
|
+
if (!pointInRing(point, p.rings[0])) return false;
|
|
55
|
+
for (let i = 1; i < p.rings.length; i++) {
|
|
56
|
+
const hole = p.rings[i];
|
|
57
|
+
if (hole && pointInRing(point, hole)) return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
function bounds(p) {
|
|
62
|
+
let minX = Infinity;
|
|
63
|
+
let minY = Infinity;
|
|
64
|
+
let maxX = -Infinity;
|
|
65
|
+
let maxY = -Infinity;
|
|
66
|
+
for (const ring of p.rings) {
|
|
67
|
+
for (const v of ring) {
|
|
68
|
+
minX = Math.min(minX, v.x);
|
|
69
|
+
minY = Math.min(minY, v.y);
|
|
70
|
+
maxX = Math.max(maxX, v.x);
|
|
71
|
+
maxY = Math.max(maxY, v.y);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (!Number.isFinite(minX)) {
|
|
75
|
+
return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
|
76
|
+
}
|
|
77
|
+
return { minX, minY, maxX, maxY };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/hull/index.ts
|
|
81
|
+
function convexHull(points) {
|
|
82
|
+
const pts = uniquePoints(points);
|
|
83
|
+
if (pts.length < 3) return polygon(pts);
|
|
84
|
+
pts.sort((a, b) => a.x === b.x ? a.y - b.y : a.x - b.x);
|
|
85
|
+
const lower = [];
|
|
86
|
+
for (const p of pts) {
|
|
87
|
+
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
|
|
88
|
+
lower.pop();
|
|
89
|
+
}
|
|
90
|
+
lower.push(p);
|
|
91
|
+
}
|
|
92
|
+
const upper = [];
|
|
93
|
+
for (let i = pts.length - 1; i >= 0; i--) {
|
|
94
|
+
const p = pts[i];
|
|
95
|
+
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
|
|
96
|
+
upper.pop();
|
|
97
|
+
}
|
|
98
|
+
upper.push(p);
|
|
99
|
+
}
|
|
100
|
+
lower.pop();
|
|
101
|
+
upper.pop();
|
|
102
|
+
return polygon([...lower, ...upper]);
|
|
103
|
+
}
|
|
104
|
+
function boundingBox(p) {
|
|
105
|
+
const b = bounds(p);
|
|
106
|
+
return polygon([
|
|
107
|
+
{ x: b.minX, y: b.minY },
|
|
108
|
+
{ x: b.maxX, y: b.minY },
|
|
109
|
+
{ x: b.maxX, y: b.maxY },
|
|
110
|
+
{ x: b.minX, y: b.maxY }
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
function cross(o, a, b) {
|
|
114
|
+
return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
115
|
+
}
|
|
116
|
+
function uniquePoints(points) {
|
|
117
|
+
const seen = /* @__PURE__ */ new Set();
|
|
118
|
+
const out = [];
|
|
119
|
+
for (const p of points) {
|
|
120
|
+
const key = `${p.x},${p.y}`;
|
|
121
|
+
if (seen.has(key)) continue;
|
|
122
|
+
seen.add(key);
|
|
123
|
+
out.push({ x: p.x, y: p.y });
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/circlePacking/index.ts
|
|
129
|
+
function maximumInscribedPack(path2, n, tolerance = 1) {
|
|
130
|
+
return obstaclePack(path2, [], n, tolerance);
|
|
131
|
+
}
|
|
132
|
+
function maximumInscribedPackUntil(path2, minRadius, tolerance = 1) {
|
|
133
|
+
const packing = [];
|
|
134
|
+
const tol = Math.max(0.01, tolerance);
|
|
135
|
+
const minR = Math.max(0.01, minRadius);
|
|
136
|
+
for (; ; ) {
|
|
137
|
+
const next = findLargestEmptyCircle(path2, packing, tol);
|
|
138
|
+
if (!next || next.r < minR) break;
|
|
139
|
+
packing.push(next);
|
|
140
|
+
}
|
|
141
|
+
return packing;
|
|
142
|
+
}
|
|
143
|
+
function obstaclePack(path2, obstacles, n, tolerance = 1) {
|
|
144
|
+
const packing = obstacles.map((c) => ({ ...c }));
|
|
145
|
+
const placed = [];
|
|
146
|
+
const tol = Math.max(0.01, tolerance);
|
|
147
|
+
for (let i = 0; i < n; i++) {
|
|
148
|
+
const next = findLargestEmptyCircle(path2, packing, tol);
|
|
149
|
+
if (!next || next.r <= 1e-9) break;
|
|
150
|
+
packing.push(next);
|
|
151
|
+
placed.push(next);
|
|
152
|
+
}
|
|
153
|
+
return placed;
|
|
154
|
+
}
|
|
155
|
+
function findLargestEmptyCircle(path2, obstacles, tolerance) {
|
|
156
|
+
const b = bounds(path2);
|
|
157
|
+
const diag = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) || 1;
|
|
158
|
+
const step = Math.max(diag * 0.02 * Math.sqrt(tolerance), diag * 0.01);
|
|
159
|
+
let best = null;
|
|
160
|
+
for (let x = b.minX; x <= b.maxX; x += step) {
|
|
161
|
+
for (let y = b.minY; y <= b.maxY; y += step) {
|
|
162
|
+
const p = { x, y };
|
|
163
|
+
if (!containsPoint(path2, p)) continue;
|
|
164
|
+
const r = clearanceRadius(p, path2, obstacles);
|
|
165
|
+
if (!best || r > best.r) best = circle(p.x, p.y, r);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (!best) return null;
|
|
169
|
+
let cur = best;
|
|
170
|
+
let span = step;
|
|
171
|
+
for (let iter = 0; iter < 8; iter++) {
|
|
172
|
+
let improved = cur;
|
|
173
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
174
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
175
|
+
if (dx === 0 && dy === 0) continue;
|
|
176
|
+
const p = { x: cur.x + dx * span, y: cur.y + dy * span };
|
|
177
|
+
if (!containsPoint(path2, p)) continue;
|
|
178
|
+
const r = clearanceRadius(p, path2, obstacles);
|
|
179
|
+
if (r > improved.r) improved = circle(p.x, p.y, r);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
cur = improved;
|
|
183
|
+
span *= 0.5;
|
|
184
|
+
}
|
|
185
|
+
return cur;
|
|
186
|
+
}
|
|
187
|
+
function clearanceRadius(p, path2, obstacles) {
|
|
188
|
+
let r = distanceToBoundary(p, path2);
|
|
189
|
+
for (const c of obstacles) {
|
|
190
|
+
r = Math.min(r, Math.hypot(p.x - c.x, p.y - c.y) - c.r);
|
|
191
|
+
}
|
|
192
|
+
return Math.max(0, r);
|
|
193
|
+
}
|
|
194
|
+
function distanceToBoundary(p, path2) {
|
|
195
|
+
let min = Infinity;
|
|
196
|
+
for (const ring of path2.rings) {
|
|
197
|
+
for (let i = 0; i < ring.length; i++) {
|
|
198
|
+
const a = ring[i];
|
|
199
|
+
const b = ring[(i + 1) % ring.length];
|
|
200
|
+
min = Math.min(min, pointSegmentDistance(p, a, b));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return min;
|
|
204
|
+
}
|
|
205
|
+
function pointSegmentDistance(p, a, b) {
|
|
206
|
+
const abx = b.x - a.x;
|
|
207
|
+
const aby = b.y - a.y;
|
|
208
|
+
const len2 = abx * abx + aby * aby;
|
|
209
|
+
if (len2 < 1e-18) return Math.hypot(p.x - a.x, p.y - a.y);
|
|
210
|
+
let t = ((p.x - a.x) * abx + (p.y - a.y) * aby) / len2;
|
|
211
|
+
t = Math.max(0, Math.min(1, t));
|
|
212
|
+
return Math.hypot(p.x - (a.x + t * abx), p.y - (a.y + t * aby));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/optimisation/index.ts
|
|
216
|
+
function envelope(p) {
|
|
217
|
+
return boundingBox(p);
|
|
218
|
+
}
|
|
219
|
+
function maximumInscribedCircle(p, tolerance = 1) {
|
|
220
|
+
return maximumInscribedPack(p, 1, tolerance)[0] ?? null;
|
|
221
|
+
}
|
|
222
|
+
function largestEmptyCircles(p, minRadius, tolerance = 1) {
|
|
223
|
+
return maximumInscribedPackUntil(p, minRadius, tolerance);
|
|
224
|
+
}
|
|
225
|
+
function largestEmptyCircle(p, tolerance = 1) {
|
|
226
|
+
return maximumInscribedCircle(p, tolerance);
|
|
227
|
+
}
|
|
228
|
+
function maximumInscribedAARectangle(p) {
|
|
229
|
+
const b = bounds(p);
|
|
230
|
+
const w = b.maxX - b.minX;
|
|
231
|
+
const h = b.maxY - b.minY;
|
|
232
|
+
if (w < 1e-12 || h < 1e-12) return envelope(p);
|
|
233
|
+
const fits = (m2) => {
|
|
234
|
+
const rect = [
|
|
235
|
+
{ x: b.minX + m2, y: b.minY + m2 },
|
|
236
|
+
{ x: b.maxX - m2, y: b.minY + m2 },
|
|
237
|
+
{ x: b.maxX - m2, y: b.maxY - m2 },
|
|
238
|
+
{ x: b.minX + m2, y: b.maxY - m2 }
|
|
239
|
+
];
|
|
240
|
+
return rect.every((pt) => containsPoint(p, pt));
|
|
241
|
+
};
|
|
242
|
+
if (fits(0)) return envelope(p);
|
|
243
|
+
let lo = 0;
|
|
244
|
+
let hi = Math.min(w, h) / 2;
|
|
245
|
+
let best = hi;
|
|
246
|
+
for (let i = 0; i < 40; i++) {
|
|
247
|
+
const mid = (lo + hi) / 2;
|
|
248
|
+
if (fits(mid)) {
|
|
249
|
+
best = mid;
|
|
250
|
+
hi = mid;
|
|
251
|
+
} else {
|
|
252
|
+
lo = mid;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const m = best;
|
|
256
|
+
return polygon([
|
|
257
|
+
{ x: b.minX + m, y: b.minY + m },
|
|
258
|
+
{ x: b.maxX - m, y: b.minY + m },
|
|
259
|
+
{ x: b.maxX - m, y: b.maxY - m },
|
|
260
|
+
{ x: b.minX + m, y: b.maxY - m }
|
|
261
|
+
]);
|
|
262
|
+
}
|
|
263
|
+
function closestPoint(p, point) {
|
|
264
|
+
let best = point;
|
|
265
|
+
let bestD = Infinity;
|
|
266
|
+
for (const ring of p.rings) {
|
|
267
|
+
const n = ring.length;
|
|
268
|
+
const edges = p.closed ? n : Math.max(0, n - 1);
|
|
269
|
+
for (let i = 0; i < edges; i++) {
|
|
270
|
+
const a = ring[i];
|
|
271
|
+
const b = ring[(i + 1) % n];
|
|
272
|
+
const q = projectPointToSegment(point, a, b);
|
|
273
|
+
const d = Math.hypot(q.x - point.x, q.y - point.y);
|
|
274
|
+
if (d < bestD) {
|
|
275
|
+
bestD = d;
|
|
276
|
+
best = q;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return best;
|
|
281
|
+
}
|
|
282
|
+
function closestVertex(p, point) {
|
|
283
|
+
let best = p.rings[0]?.[0] ?? point;
|
|
284
|
+
let bestD = Infinity;
|
|
285
|
+
for (const ring of p.rings) {
|
|
286
|
+
for (const v of ring) {
|
|
287
|
+
const d = Math.hypot(v.x - point.x, v.y - point.y);
|
|
288
|
+
if (d < bestD) {
|
|
289
|
+
bestD = d;
|
|
290
|
+
best = v;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { ...best };
|
|
295
|
+
}
|
|
296
|
+
function closestPointPair(points) {
|
|
297
|
+
if (points.length < 2) return null;
|
|
298
|
+
let best = [points[0], points[1]];
|
|
299
|
+
let bestD = Infinity;
|
|
300
|
+
for (let i = 0; i < points.length; i++) {
|
|
301
|
+
for (let j = i + 1; j < points.length; j++) {
|
|
302
|
+
const d = Math.hypot(
|
|
303
|
+
points[i].x - points[j].x,
|
|
304
|
+
points[i].y - points[j].y
|
|
305
|
+
);
|
|
306
|
+
if (d < bestD) {
|
|
307
|
+
bestD = d;
|
|
308
|
+
best = [points[i], points[j]];
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return best;
|
|
313
|
+
}
|
|
314
|
+
function farthestPointPair(points) {
|
|
315
|
+
if (points.length < 2) return null;
|
|
316
|
+
let best = [points[0], points[1]];
|
|
317
|
+
let bestD = -1;
|
|
318
|
+
for (let i = 0; i < points.length; i++) {
|
|
319
|
+
for (let j = i + 1; j < points.length; j++) {
|
|
320
|
+
const d = Math.hypot(
|
|
321
|
+
points[i].x - points[j].x,
|
|
322
|
+
points[i].y - points[j].y
|
|
323
|
+
);
|
|
324
|
+
if (d > bestD) {
|
|
325
|
+
bestD = d;
|
|
326
|
+
best = [points[i], points[j]];
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return best;
|
|
331
|
+
}
|
|
332
|
+
function minimumBoundingCircle(p) {
|
|
333
|
+
const pts = [];
|
|
334
|
+
for (const ring of p.rings) pts.push(...ring);
|
|
335
|
+
const hull = convexHull(pts).rings[0] ?? pts;
|
|
336
|
+
return minDisk(hull);
|
|
337
|
+
}
|
|
338
|
+
function minDisk(points) {
|
|
339
|
+
return welzl(points.map((p) => ({ ...p })), [], 0);
|
|
340
|
+
}
|
|
341
|
+
function welzl(P, R, i) {
|
|
342
|
+
if (i === P.length || R.length === 3) return diskFromBoundary(R);
|
|
343
|
+
const d = welzl(P, R, i + 1);
|
|
344
|
+
const p = P[i];
|
|
345
|
+
if (Math.hypot(p.x - d.x, p.y - d.y) <= d.r + 1e-9) return d;
|
|
346
|
+
return welzl(P, [...R, p], i + 1);
|
|
347
|
+
}
|
|
348
|
+
function diskFromBoundary(R) {
|
|
349
|
+
if (R.length === 0) return circle(0, 0, 0);
|
|
350
|
+
if (R.length === 1) return circle(R[0].x, R[0].y, 0);
|
|
351
|
+
if (R.length === 2) {
|
|
352
|
+
const a = R[0];
|
|
353
|
+
const b = R[1];
|
|
354
|
+
return circle(
|
|
355
|
+
(a.x + b.x) / 2,
|
|
356
|
+
(a.y + b.y) / 2,
|
|
357
|
+
Math.hypot(a.x - b.x, a.y - b.y) / 2
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
return circumcircle(R[0], R[1], R[2]);
|
|
361
|
+
}
|
|
362
|
+
function circumcircle(a, b, c) {
|
|
363
|
+
const d = 2 * (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y));
|
|
364
|
+
if (Math.abs(d) < 1e-14) {
|
|
365
|
+
const pair = farthestPointPair([a, b, c]);
|
|
366
|
+
return circle(
|
|
367
|
+
(pair[0].x + pair[1].x) / 2,
|
|
368
|
+
(pair[0].y + pair[1].y) / 2,
|
|
369
|
+
Math.hypot(pair[0].x - pair[1].x, pair[0].y - pair[1].y) / 2
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const ux = ((a.x * a.x + a.y * a.y) * (b.y - c.y) + (b.x * b.x + b.y * b.y) * (c.y - a.y) + (c.x * c.x + c.y * c.y) * (a.y - b.y)) / d;
|
|
373
|
+
const uy = ((a.x * a.x + a.y * a.y) * (c.x - b.x) + (b.x * b.x + b.y * b.y) * (a.x - c.x) + (c.x * c.x + c.y * c.y) * (b.x - a.x)) / d;
|
|
374
|
+
return circle(ux, uy, Math.hypot(ux - a.x, uy - a.y));
|
|
375
|
+
}
|
|
376
|
+
function projectPointToSegment(p, a, b) {
|
|
377
|
+
const abx = b.x - a.x;
|
|
378
|
+
const aby = b.y - a.y;
|
|
379
|
+
const len2 = abx * abx + aby * aby;
|
|
380
|
+
if (len2 < 1e-18) return { ...a };
|
|
381
|
+
let t = ((p.x - a.x) * abx + (p.y - a.y) * aby) / len2;
|
|
382
|
+
t = Math.max(0, Math.min(1, t));
|
|
383
|
+
return { x: a.x + t * abx, y: a.y + t * aby };
|
|
384
|
+
}
|
|
385
|
+
var optimisation = {
|
|
386
|
+
envelope,
|
|
387
|
+
maximumInscribedCircle,
|
|
388
|
+
largestEmptyCircle,
|
|
389
|
+
largestEmptyCircles,
|
|
390
|
+
maximumInscribedAARectangle,
|
|
391
|
+
closestPoint,
|
|
392
|
+
closestVertex,
|
|
393
|
+
closestPointPair,
|
|
394
|
+
farthestPointPair,
|
|
395
|
+
minimumBoundingCircle
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
export { closestPoint, closestPointPair, closestVertex, envelope, farthestPointPair, largestEmptyCircle, largestEmptyCircles, maximumInscribedAARectangle, maximumInscribedCircle, minimumBoundingCircle, optimisation };
|
|
399
|
+
//# sourceMappingURL=index.js.map
|
|
400
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types/vec2.ts","../../src/types/circle.ts","../../src/types/path.ts","../../src/clipper/convert.ts","../../src/predicates/index.ts","../../src/hull/index.ts","../../src/circlePacking/index.ts","../../src/optimisation/index.ts"],"names":["path","m"],"mappings":"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","file":"index.js","sourcesContent":["/** 2D point / vector. */\nexport type Vec2 = {\n x: number\n y: number\n}\n\nexport function vec2(x: number, y: number): Vec2 {\n return { x, y }\n}\n\nexport function cloneVec2(v: Vec2): Vec2 {\n return { x: v.x, y: v.y }\n}\n\nexport function equalsVec2(a: Vec2, b: Vec2, eps = 1e-9): boolean {\n return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps\n}\n","import type { Vec2 } from './vec2.js'\n\n/** Circle with center (x, y) and radius r (PGS often stored radius in PVector.z). */\nexport type Circle = {\n x: number\n y: number\n r: number\n}\n\nexport function circle(x: number, y: number, r: number): Circle {\n return { x, y, r }\n}\n\nexport function circleCenter(c: Circle): Vec2 {\n return { x: c.x, y: c.y }\n}\n","import type { Vec2 } from './vec2.js'\nimport { cloneVec2, equalsVec2 } from './vec2.js'\n\n/** Ordered ring of vertices (typically without repeating the closing vertex). */\nexport type Ring = Vec2[]\n\n/**\n * A path: one exterior ring plus optional holes, or an open polyline.\n * `rings[0]` is the exterior (or the open polyline when `closed` is false).\n * Additional rings are holes (only meaningful when `closed` is true).\n */\nexport type Path = {\n rings: Ring[]\n closed: boolean\n}\n\nexport function path(rings: Ring[], closed = true): Path {\n return {\n rings: rings.map((ring) => ring.map(cloneVec2)),\n closed,\n }\n}\n\nexport function polyline(points: Ring): Path {\n return path([points], false)\n}\n\nexport function polygon(exterior: Ring, holes: Ring[] = []): Path {\n return path([exterior, ...holes], true)\n}\n\n/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */\nexport function normalizeRing(ring: Ring, eps = 1e-9): Ring {\n if (ring.length < 2) return ring.map(cloneVec2)\n const first = ring[0]\n const last = ring[ring.length - 1]\n if (first && last && equalsVec2(first, last, eps)) {\n return ring.slice(0, -1).map(cloneVec2)\n }\n return ring.map(cloneVec2)\n}\n\nexport function clonePath(p: Path): Path {\n return {\n closed: p.closed,\n rings: p.rings.map((ring) => ring.map(cloneVec2)),\n }\n}\n","import {\n FillRule,\n areaD,\n differenceD,\n intersectD,\n isPositiveD,\n pointInPolygonD,\n PointInPolygonResult,\n unionD,\n xorD,\n type PathD,\n type PathsD,\n type PointD,\n} from 'clipper2-ts'\nimport type { Group, Path, Ring, Vec2 } from '../types/index.js'\nimport { group, normalizeRing, path, vec2 } from '../types/index.js'\n\nexport const DEFAULT_FILL_RULE = FillRule.NonZero\n\nexport function ringToPathD(ring: Ring): PathD {\n const pts: PointD[] = []\n for (const v of normalizeRing(ring)) {\n pts.push({ x: v.x, y: v.y, z: 0 })\n }\n return pts\n}\n\n/** Closed path → Clipper subject/clip paths (exterior + holes). */\nexport function pathToPathsD(p: Path): PathsD {\n if (!p.closed || p.rings.length === 0) {\n throw new Error('Boolean ops require a closed path with at least one ring')\n }\n return p.rings.map((ring) => ringToPathD(ring))\n}\n\nexport function pathsDToGroup(paths: PathsD): Group {\n if (paths.length === 0) return group([])\n\n const outers: { ring: Ring; holes: Ring[] }[] = []\n const holes: Ring[] = []\n\n for (const pd of paths) {\n const ring = pathDToRing(pd)\n if (ring.length < 3) continue\n if (isPositiveD(pd)) {\n outers.push({ ring, holes: [] })\n } else {\n holes.push(ring)\n }\n }\n\n for (const hole of holes) {\n const sample = hole[0]\n if (!sample) continue\n let assigned = false\n for (const outer of outers) {\n if (pointInRing(sample, outer.ring)) {\n outer.holes.push(hole)\n assigned = true\n break\n }\n }\n if (!assigned) {\n // Treat unmatched negative rings as their own outers (orientation flip).\n outers.push({ ring: [...hole].reverse(), holes: [] })\n }\n }\n\n return group(outers.map((o) => path([o.ring, ...o.holes], true)))\n}\n\nexport function pathDToRing(pd: PathD): Ring {\n return normalizeRing(pd.map((p) => vec2(p.x, p.y)))\n}\n\nexport function pointInRing(point: Vec2, ring: Ring): boolean {\n const result = pointInPolygonD(\n { x: point.x, y: point.y, z: 0 },\n ringToPathD(ring),\n )\n return (\n result === PointInPolygonResult.IsInside ||\n result === PointInPolygonResult.IsOn\n )\n}\n\nexport function clipUnion(a: PathsD, b: PathsD): PathsD {\n return unionD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipIntersect(a: PathsD, b: PathsD): PathsD {\n return intersectD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipDifference(a: PathsD, b: PathsD): PathsD {\n return differenceD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipXor(a: PathsD, b: PathsD): PathsD {\n return xorD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function pathsArea(paths: PathsD): number {\n let sum = 0\n for (const p of paths) {\n sum += Math.abs(areaD(p))\n }\n return sum\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { vec2 } from '../types/index.js'\nimport {\n pathToPathsD,\n pathsArea,\n pointInRing,\n} from '../clipper/convert.js'\n\n/** Signed area of a closed ring (shoelace). */\nexport function ringArea(ring: Vec2[]): number {\n if (ring.length < 3) return 0\n let sum = 0\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n sum += a.x * b.y - b.x * a.y\n }\n return sum / 2\n}\n\n/** Absolute area of a closed path (exterior minus holes). */\nexport function area(p: Path): number {\n if (!p.closed || p.rings.length === 0) return 0\n try {\n return pathsArea(pathToPathsD(p))\n } catch {\n let total = 0\n for (let i = 0; i < p.rings.length; i++) {\n const a = Math.abs(ringArea(p.rings[i]!))\n total += i === 0 ? a : -a\n }\n return Math.max(0, total)\n }\n}\n\nexport function areaGroup(g: Group): number {\n return g.paths.reduce((sum, p) => sum + area(p), 0)\n}\n\n/** Centroid of a closed exterior ring (ignores holes for Phase 2). */\nexport function centroid(p: Path): Vec2 {\n const ring = p.rings[0]\n if (!ring || ring.length === 0) return vec2(0, 0)\n const a = ringArea(ring)\n if (Math.abs(a) < 1e-12) {\n let sx = 0\n let sy = 0\n for (const v of ring) {\n sx += v.x\n sy += v.y\n }\n return vec2(sx / ring.length, sy / ring.length)\n }\n let cx = 0\n let cy = 0\n for (let i = 0; i < ring.length; i++) {\n const p0 = ring[i]!\n const p1 = ring[(i + 1) % ring.length]!\n const cross = p0.x * p1.y - p1.x * p0.y\n cx += (p0.x + p1.x) * cross\n cy += (p0.y + p1.y) * cross\n }\n return vec2(cx / (6 * a), cy / (6 * a))\n}\n\nexport function containsPoint(p: Path, point: Vec2): boolean {\n if (!p.closed || !p.rings[0]) return false\n if (!pointInRing(point, p.rings[0])) return false\n for (let i = 1; i < p.rings.length; i++) {\n const hole = p.rings[i]\n if (hole && pointInRing(point, hole)) return false\n }\n return true\n}\n\nexport function bounds(p: Path): {\n minX: number\n minY: number\n maxX: number\n maxY: number\n} {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const ring of p.rings) {\n for (const v of ring) {\n minX = Math.min(minX, v.x)\n minY = Math.min(minY, v.y)\n maxX = Math.max(maxX, v.x)\n maxY = Math.max(maxY, v.y)\n }\n }\n if (!Number.isFinite(minX)) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 }\n }\n return { minX, minY, maxX, maxY }\n}\n\nexport function width(p: Path): number {\n const b = bounds(p)\n return b.maxX - b.minX\n}\n\nexport function height(p: Path): number {\n const b = bounds(p)\n return b.maxY - b.minY\n}\n\n/** Rough polygon similarity via absolute area difference relative to max area. */\nexport function areaSimilarity(a: Path, b: Path): number {\n const aa = area(a)\n const bb = area(b)\n const denom = Math.max(aa, bb, 1e-12)\n return 1 - Math.abs(aa - bb) / denom\n}\n\nexport const predicates = {\n area,\n areaGroup,\n ringArea,\n centroid,\n containsPoint,\n bounds,\n width,\n height,\n areaSimilarity,\n}\n","import type { Path, Vec2 } from '../types/index.js'\nimport { polygon } from '../types/index.js'\nimport { bounds } from '../predicates/index.js'\n\n/** Andrew's monotone-chain convex hull of a point set. */\nexport function convexHull(points: Vec2[]): Path {\n const pts = uniquePoints(points)\n if (pts.length < 3) return polygon(pts)\n\n pts.sort((a, b) => (a.x === b.x ? a.y - b.y : a.x - b.x))\n\n const lower: Vec2[] = []\n for (const p of pts) {\n while (\n lower.length >= 2 &&\n cross(lower[lower.length - 2]!, lower[lower.length - 1]!, p) <= 0\n ) {\n lower.pop()\n }\n lower.push(p)\n }\n\n const upper: Vec2[] = []\n for (let i = pts.length - 1; i >= 0; i--) {\n const p = pts[i]!\n while (\n upper.length >= 2 &&\n cross(upper[upper.length - 2]!, upper[upper.length - 1]!, p) <= 0\n ) {\n upper.pop()\n }\n upper.push(p)\n }\n\n lower.pop()\n upper.pop()\n return polygon([...lower, ...upper])\n}\n\n/** Convex hull of all vertices in a path (exterior + holes). */\nexport function convexHullPath(p: Path): Path {\n const pts: Vec2[] = []\n for (const ring of p.rings) pts.push(...ring)\n return convexHull(pts)\n}\n\n/** Axis-aligned bounding box of a path as a closed rectangle. */\nexport function boundingBox(p: Path): Path {\n const b = bounds(p)\n return polygon([\n { x: b.minX, y: b.minY },\n { x: b.maxX, y: b.minY },\n { x: b.maxX, y: b.maxY },\n { x: b.minX, y: b.maxY },\n ])\n}\n\nfunction cross(o: Vec2, a: Vec2, b: Vec2): number {\n return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)\n}\n\nfunction uniquePoints(points: Vec2[]): Vec2[] {\n const seen = new Set<string>()\n const out: Vec2[] = []\n for (const p of points) {\n const key = `${p.x},${p.y}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push({ x: p.x, y: p.y })\n }\n return out\n}\n\nexport const hull = {\n convexHull,\n convexHullPath,\n boundingBox,\n}\n","import type { Circle, Path, Vec2 } from '../types/index.js'\nimport { circle } from '../types/index.js'\nimport { bounds, containsPoint } from '../predicates/index.js'\n\n/**\n * Lattice inclusion mode.\n * - `overlap` (default): full-path fill — keep disks that overlap the path;\n * center the lattice so opposite-edge cutoffs match. Clip in SVG to cut at\n * the boundary (including holes).\n * - `contained`: keep only disks fully inside the path (when you must stroke\n * complete circles); lattice centered with equal leftover margins.\n */\nexport type LatticePackMode = 'overlap' | 'contained'\n\n/**\n * Square lattice of equal circles.\n * @see docs/decisions/0003-deferred-circle-packing.md\n */\nexport function squareLatticePack(\n path: Path,\n diameter: number,\n mode: LatticePackMode = 'overlap',\n): Circle[] {\n const d = Math.max(diameter, 0.1)\n const r = d / 2\n const b = bounds(path)\n const out: Circle[] = []\n const accept =\n mode === 'contained' ? circleContainedInPath : circleOverlapsPath\n const inset = mode === 'contained' ? r : 0\n const { ox, oy, nX, nY } = centeredLatticeGrid(b, d, d, inset, d)\n if (nX < 1 || nY < 1) return out\n\n for (let i = 0; i < nX; i++) {\n for (let j = 0; j < nY; j++) {\n const c = circle(ox + i * d, oy + j * d, r)\n if (accept(c, path)) out.push(c)\n }\n }\n return out\n}\n\n/**\n * Hexagonal lattice of equal circles.\n */\nexport function hexLatticePack(\n path: Path,\n diameter: number,\n mode: LatticePackMode = 'overlap',\n): Circle[] {\n const d = Math.max(diameter, 0.1)\n const r = d / 2\n const b = bounds(path)\n const z = r * Math.sqrt(3)\n const out: Circle[] = []\n const accept =\n mode === 'contained' ? circleContainedInPath : circleOverlapsPath\n const inset = mode === 'contained' ? r : 0\n const { ox, nX, nY } = centeredLatticeGrid(b, z, d, inset, d)\n if (nX < 1 || nY < 1) return out\n\n // First column uses offset `r`, so Y centers span [oy - r, oy + (nY-1)*d].\n // Re-center that full extent so top/bottom cutoffs match.\n const innerH = b.maxY - b.minY - 2 * inset\n const extentY = (nY - 1) * d + r\n const oy = b.minY + inset + (innerH - extentY) / 2 + r\n\n let offset = 0\n for (let i = 0; i < nX; i++) {\n offset = offset === r ? 0 : r\n for (let j = 0; j < nY; j++) {\n const c = circle(ox + i * z, oy + j * d - offset, r)\n if (accept(c, path)) out.push(c)\n }\n }\n return out\n}\n\n/**\n * Centered lattice grid. `inset` > 0 (contained) keeps disk edges inside the AABB;\n * `inset` 0 (overlap) matches PGS coverage count, shifted so opposite cutoffs match.\n * `endPad` mirrors the historical loop bound `max + diameter`.\n */\nfunction centeredLatticeGrid(\n b: { minX: number; minY: number; maxX: number; maxY: number },\n stepX: number,\n stepY: number,\n inset: number,\n endPad: number,\n): { ox: number; oy: number; nX: number; nY: number } {\n const innerW = b.maxX - b.minX - 2 * inset\n const innerH = b.maxY - b.minY - 2 * inset\n if (innerW < -1e-9 || innerH < -1e-9) {\n return { ox: 0, oy: 0, nX: 0, nY: 0 }\n }\n\n let nX: number\n let nY: number\n if (inset > 0) {\n nX = Math.floor(innerW / stepX + 1e-9) + 1\n nY = Math.floor(innerH / stepY + 1e-9) + 1\n } else {\n // Same count as `for (x = min; x < max + endPad; x += step)`\n nX = Math.floor((innerW + endPad) / stepX - 1e-12) + 1\n nY = Math.floor((innerH + endPad) / stepY - 1e-12) + 1\n }\n\n const spanX = (nX - 1) * stepX\n const spanY = (nY - 1) * stepY\n const ox = b.minX + inset + (innerW - spanX) / 2\n const oy = b.minY + inset + (innerH - spanY) / 2\n return { ox, oy, nX, nY }\n}\n\n/**\n * Seeded stochastic packing: sample random points, grow max non-overlapping\n * radius (tangent to nearest circle or limited by distance to boundary).\n * Triangulation Steiner mode deferred.\n */\nexport function stochasticPack(\n path: Path,\n points: number,\n minRadius: number,\n seed = 1,\n): Circle[] {\n const rng = mulberry32(seed >>> 0)\n const b = bounds(path)\n const packing: Circle[] = []\n\n for (let attempt = 0; attempt < points; attempt++) {\n const p = vecInBounds(b, rng)\n if (!containsPoint(path, p)) continue\n\n const maxR = clearanceRadius(p, path, packing)\n if (maxR < minRadius) continue\n packing.push(circle(p.x, p.y, maxR))\n }\n\n return packing\n}\n\n/**\n * Pack up to `n` maximum inscribed circles via iterative largest-empty-circle\n * search (grid + local refine). Circles are fully contained and non-overlapping.\n * `tolerance` controls coarse grid step as a fraction of the bbox diagonal\n * (PGS uses a related accuracy knob; values ~0.5–1 are reasonable).\n *\n * Deferred: `tangencyPack`, `trinscribedPack` — see ADR 0003.\n */\nexport function maximumInscribedPack(\n path: Path,\n n: number,\n tolerance = 1,\n): Circle[] {\n return obstaclePack(path, [], n, tolerance)\n}\n\n/**\n * Pack inscribed circles while radius stays ≥ `minRadius`.\n */\nexport function maximumInscribedPackUntil(\n path: Path,\n minRadius: number,\n tolerance = 1,\n): Circle[] {\n const packing: Circle[] = []\n const tol = Math.max(0.01, tolerance)\n const minR = Math.max(0.01, minRadius)\n for (;;) {\n const next = findLargestEmptyCircle(path, packing, tol)\n if (!next || next.r < minR) break\n packing.push(next)\n }\n return packing\n}\n\n/**\n * Successive LECs inside `path`, treating `obstacles` as already-placed disks.\n * Simpler than full PGS obstacle packing; sufficient for “avoid these circles.”\n */\nexport function obstaclePack(\n path: Path,\n obstacles: Circle[],\n n: number,\n tolerance = 1,\n): Circle[] {\n const packing: Circle[] = obstacles.map((c) => ({ ...c }))\n const placed: Circle[] = []\n const tol = Math.max(0.01, tolerance)\n for (let i = 0; i < n; i++) {\n const next = findLargestEmptyCircle(path, packing, tol)\n if (!next || next.r <= 1e-9) break\n packing.push(next)\n placed.push(next)\n }\n return placed\n}\n\n/**\n * Front-chain-inspired packing of (possibly varying) radii in the path envelope,\n * then keep circles that overlap the path. Seeded for determinism.\n * Grows until placement stalls (region effectively full); no fixed circle count.\n * Not a line-by-line port of PGS FrontChainPacker — same role, approximate layout.\n */\nexport function frontChainPack(\n path: Path,\n radiusMin: number,\n radiusMax: number,\n seed = 1,\n): Circle[] {\n let rMin = Math.max(1e-6, Math.min(radiusMin, radiusMax))\n let rMax = Math.max(rMin, Math.max(radiusMin, radiusMax))\n const rng = mulberry32(seed >>> 0)\n const b = bounds(path)\n const packing: Circle[] = []\n const area = Math.max(1e-6, (b.maxX - b.minX) * (b.maxY - b.minY))\n // Safety ceiling only — real stop is consecutive placement failures.\n const capacity = Math.max(\n 4,\n Math.ceil((area / (Math.PI * rMin * rMin)) * 2),\n )\n\n const trySeed = (): boolean => {\n const p = vecInBounds(b, rng)\n const r = rMin + rng() * (rMax - rMin)\n const c = circle(p.x, p.y, r)\n if (!overlapsAny(c, packing) && circleOverlapsPath(c, path)) {\n packing.push(c)\n return true\n }\n return false\n }\n\n // Seed a few circles, then grow a frontier of tangent candidates.\n for (let i = 0; i < 8; i++) trySeed()\n\n let fails = 0\n while (packing.length < capacity) {\n const stallLimit = Math.max(500, packing.length * 12)\n if (fails >= stallLimit) break\n\n if (packing.length === 0) {\n if (trySeed()) fails = 0\n else fails++\n continue\n }\n\n // Reseed empty pockets so growth is not stuck on one cluster.\n if (fails > 0 && fails % 50 === 0) {\n if (trySeed()) {\n fails = 0\n continue\n }\n }\n\n const base = packing[Math.floor(rng() * packing.length)]!\n const ang = rng() * Math.PI * 2\n const r = rMin + rng() * (rMax - rMin)\n const dist = base.r + r\n const c = circle(\n base.x + Math.cos(ang) * dist,\n base.y + Math.sin(ang) * dist,\n r,\n )\n if (\n c.x < b.minX - rMax ||\n c.x > b.maxX + rMax ||\n c.y < b.minY - rMax ||\n c.y > b.maxY + rMax\n ) {\n fails++\n continue\n }\n if (!overlapsAny(c, packing) && circleOverlapsPath(c, path)) {\n packing.push(c)\n fails = 0\n } else {\n fails++\n }\n }\n\n return packing.filter((c) => circleOverlapsPath(c, path))\n}\n\n/**\n * Seed random circles in the envelope, then iteratively repulse overlaps;\n * keep those that overlap the path (PGS repulsionPack role).\n */\nexport function repulsionPack(\n path: Path,\n radiusMin: number,\n radiusMax: number,\n seed = 1,\n iterations = 80,\n): Circle[] {\n const rMin = Math.max(1e-6, Math.min(radiusMin, radiusMax))\n const rMax = Math.max(rMin, Math.max(radiusMin, radiusMax))\n const rng = mulberry32(seed >>> 0)\n const b = bounds(path)\n const area = Math.max(1e-6, (b.maxX - b.minX) * (b.maxY - b.minY))\n const avgR = (rMin + rMax) / 2\n const target = Math.max(\n 4,\n Math.floor(area / (Math.PI * avgR * avgR) * 0.6),\n )\n\n const circles: Circle[] = []\n for (let i = 0; i < target; i++) {\n const p = vecInBounds(b, rng)\n circles.push(circle(p.x, p.y, rMin + rng() * (rMax - rMin)))\n }\n\n for (let iter = 0; iter < iterations; iter++) {\n for (let i = 0; i < circles.length; i++) {\n for (let j = i + 1; j < circles.length; j++) {\n const a = circles[i]!\n const bC = circles[j]!\n const dx = bC.x - a.x\n const dy = bC.y - a.y\n const dist = Math.hypot(dx, dy) || 1e-9\n const minDist = a.r + bC.r\n if (dist >= minDist) continue\n const push = ((minDist - dist) / 2) * 0.5\n const ux = dx / dist\n const uy = dy / dist\n a.x -= ux * push\n a.y -= uy * push\n bC.x += ux * push\n bC.y += uy * push\n }\n }\n // Soft clamp to inflated envelope\n for (const c of circles) {\n c.x = Math.min(b.maxX + c.r, Math.max(b.minX - c.r, c.x))\n c.y = Math.min(b.maxY + c.r, Math.max(b.minY - c.r, c.y))\n }\n }\n\n return circles.filter((c) => circleOverlapsPath(c, path))\n}\n\n/** True if disk overlaps the filled path (including partial exterior overlap). */\nexport function circleOverlapsPath(c: Circle, path: Path): boolean {\n const center = { x: c.x, y: c.y }\n const dist = distanceToPolygon(center, path)\n return dist <= c.r * 0.95\n}\n\n/** True if the disk lies fully inside the path. */\nexport function circleContainedInPath(c: Circle, path: Path): boolean {\n const center = { x: c.x, y: c.y }\n if (!containsPoint(path, center)) return false\n return distanceToBoundary(center, path) + 1e-9 >= c.r\n}\n\n/** Distance from point to polygon: 0 if inside, else distance to boundary. */\nexport function distanceToPolygon(p: Vec2, path: Path): number {\n if (containsPoint(path, p)) return 0\n return distanceToBoundary(p, path)\n}\n\nfunction findLargestEmptyCircle(\n path: Path,\n obstacles: Circle[],\n tolerance: number,\n): Circle | null {\n const b = bounds(path)\n const diag = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) || 1\n // denser grid for smaller tolerance\n const step = Math.max(diag * 0.02 * Math.sqrt(tolerance), diag * 0.01)\n let best: Circle | null = null\n\n for (let x = b.minX; x <= b.maxX; x += step) {\n for (let y = b.minY; y <= b.maxY; y += step) {\n const p = { x, y }\n if (!containsPoint(path, p)) continue\n const r = clearanceRadius(p, path, obstacles)\n if (!best || r > best.r) best = circle(p.x, p.y, r)\n }\n }\n\n if (!best) return null\n\n // Local refine around best\n let cur = best\n let span = step\n for (let iter = 0; iter < 8; iter++) {\n let improved = cur\n for (let dx = -1; dx <= 1; dx++) {\n for (let dy = -1; dy <= 1; dy++) {\n if (dx === 0 && dy === 0) continue\n const p = { x: cur.x + dx * span, y: cur.y + dy * span }\n if (!containsPoint(path, p)) continue\n const r = clearanceRadius(p, path, obstacles)\n if (r > improved.r) improved = circle(p.x, p.y, r)\n }\n }\n cur = improved\n span *= 0.5\n }\n return cur\n}\n\nfunction clearanceRadius(\n p: Vec2,\n path: Path,\n obstacles: Circle[],\n): number {\n let r = distanceToBoundary(p, path)\n for (const c of obstacles) {\n r = Math.min(r, Math.hypot(p.x - c.x, p.y - c.y) - c.r)\n }\n return Math.max(0, r)\n}\n\nfunction overlapsAny(c: Circle, packing: Circle[]): boolean {\n for (const o of packing) {\n if (Math.hypot(c.x - o.x, c.y - o.y) < c.r + o.r - 1e-9) return true\n }\n return false\n}\n\nfunction distanceToBoundary(p: Vec2, path: Path): number {\n let min = Infinity\n for (const ring of path.rings) {\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n min = Math.min(min, pointSegmentDistance(p, a, b))\n }\n }\n return min\n}\n\nfunction pointSegmentDistance(p: Vec2, a: Vec2, b: Vec2): number {\n const abx = b.x - a.x\n const aby = b.y - a.y\n const len2 = abx * abx + aby * aby\n if (len2 < 1e-18) return Math.hypot(p.x - a.x, p.y - a.y)\n let t = ((p.x - a.x) * abx + (p.y - a.y) * aby) / len2\n t = Math.max(0, Math.min(1, t))\n return Math.hypot(p.x - (a.x + t * abx), p.y - (a.y + t * aby))\n}\n\nfunction vecInBounds(\n b: { minX: number; minY: number; maxX: number; maxY: number },\n rng: () => number,\n): Vec2 {\n return {\n x: b.minX + rng() * (b.maxX - b.minX),\n y: b.minY + rng() * (b.maxY - b.minY),\n }\n}\n\nfunction mulberry32(a: number): () => number {\n return function () {\n let t = (a += 0x6d2b79f5)\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\nexport const circlePacking = {\n squareLatticePack,\n hexLatticePack,\n stochasticPack,\n maximumInscribedPack,\n maximumInscribedPackUntil,\n obstaclePack,\n frontChainPack,\n repulsionPack,\n circleOverlapsPath,\n circleContainedInPath,\n}\n","import type { Circle, Path, Vec2 } from '../types/index.js'\nimport { circle, polygon } from '../types/index.js'\nimport { bounds, containsPoint } from '../predicates/index.js'\nimport { boundingBox, convexHull } from '../hull/index.js'\nimport {\n maximumInscribedPack,\n maximumInscribedPackUntil,\n} from '../circlePacking/index.js'\n\n/** Axis-aligned envelope (alias of hull.boundingBox). */\nexport function envelope(p: Path): Path {\n return boundingBox(p)\n}\n\n/** Largest inscribed circle (fully contained). */\nexport function maximumInscribedCircle(\n p: Path,\n tolerance = 1,\n): Circle | null {\n return maximumInscribedPack(p, 1, tolerance)[0] ?? null\n}\n\n/** Alias: successive LECs until radius drops below `minRadius`. */\nexport function largestEmptyCircles(\n p: Path,\n minRadius: number,\n tolerance = 1,\n): Circle[] {\n return maximumInscribedPackUntil(p, minRadius, tolerance)\n}\n\n/** Single largest empty circle (no obstacle circles). */\nexport function largestEmptyCircle(\n p: Path,\n tolerance = 1,\n): Circle | null {\n return maximumInscribedCircle(p, tolerance)\n}\n\n/**\n * Heuristic maximum axis-aligned inscribed rectangle: inset the AABB until all\n * four corners lie inside. If the AABB corners are already inside, returns the envelope.\n */\nexport function maximumInscribedAARectangle(p: Path): Path {\n const b = bounds(p)\n const w = b.maxX - b.minX\n const h = b.maxY - b.minY\n if (w < 1e-12 || h < 1e-12) return envelope(p)\n\n const fits = (m: number) => {\n const rect = [\n { x: b.minX + m, y: b.minY + m },\n { x: b.maxX - m, y: b.minY + m },\n { x: b.maxX - m, y: b.maxY - m },\n { x: b.minX + m, y: b.maxY - m },\n ]\n return rect.every((pt) => containsPoint(p, pt))\n }\n\n if (fits(0)) return envelope(p)\n\n let lo = 0\n let hi = Math.min(w, h) / 2\n let best = hi\n for (let i = 0; i < 40; i++) {\n const mid = (lo + hi) / 2\n if (fits(mid)) {\n best = mid\n hi = mid\n } else {\n lo = mid\n }\n }\n const m = best\n return polygon([\n { x: b.minX + m, y: b.minY + m },\n { x: b.maxX - m, y: b.minY + m },\n { x: b.maxX - m, y: b.maxY - m },\n { x: b.minX + m, y: b.maxY - m },\n ])\n}\n\n/** Closest point on the path boundary to `point`. */\nexport function closestPoint(p: Path, point: Vec2): Vec2 {\n let best = point\n let bestD = Infinity\n for (const ring of p.rings) {\n const n = ring.length\n const edges = p.closed ? n : Math.max(0, n - 1)\n for (let i = 0; i < edges; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n const q = projectPointToSegment(point, a, b)\n const d = Math.hypot(q.x - point.x, q.y - point.y)\n if (d < bestD) {\n bestD = d\n best = q\n }\n }\n }\n return best\n}\n\n/** Closest vertex of the path to `point`. */\nexport function closestVertex(p: Path, point: Vec2): Vec2 {\n let best = p.rings[0]?.[0] ?? point\n let bestD = Infinity\n for (const ring of p.rings) {\n for (const v of ring) {\n const d = Math.hypot(v.x - point.x, v.y - point.y)\n if (d < bestD) {\n bestD = d\n best = v\n }\n }\n }\n return { ...best }\n}\n\n/** Closest pair among a point set (O(n²); fine for small sets). */\nexport function closestPointPair(points: Vec2[]): [Vec2, Vec2] | null {\n if (points.length < 2) return null\n let best: [Vec2, Vec2] = [points[0]!, points[1]!]\n let bestD = Infinity\n for (let i = 0; i < points.length; i++) {\n for (let j = i + 1; j < points.length; j++) {\n const d = Math.hypot(\n points[i]!.x - points[j]!.x,\n points[i]!.y - points[j]!.y,\n )\n if (d < bestD) {\n bestD = d\n best = [points[i]!, points[j]!]\n }\n }\n }\n return best\n}\n\n/** Farthest pair among a point set (O(n²)). */\nexport function farthestPointPair(points: Vec2[]): [Vec2, Vec2] | null {\n if (points.length < 2) return null\n let best: [Vec2, Vec2] = [points[0]!, points[1]!]\n let bestD = -1\n for (let i = 0; i < points.length; i++) {\n for (let j = i + 1; j < points.length; j++) {\n const d = Math.hypot(\n points[i]!.x - points[j]!.x,\n points[i]!.y - points[j]!.y,\n )\n if (d > bestD) {\n bestD = d\n best = [points[i]!, points[j]!]\n }\n }\n }\n return best\n}\n\n/**\n * Minimum bounding circle of a path's vertices (Welzl on convex hull).\n */\nexport function minimumBoundingCircle(p: Path): Circle {\n const pts: Vec2[] = []\n for (const ring of p.rings) pts.push(...ring)\n const hull = convexHull(pts).rings[0] ?? pts\n return minDisk(hull)\n}\n\nfunction minDisk(points: Vec2[]): Circle {\n // Deterministic Welzl (no shuffle); fine for typical vertex counts.\n return welzl(points.map((p) => ({ ...p })), [], 0)\n}\n\nfunction welzl(P: Vec2[], R: Vec2[], i: number): Circle {\n if (i === P.length || R.length === 3) return diskFromBoundary(R)\n const d = welzl(P, R, i + 1)\n const p = P[i]!\n if (Math.hypot(p.x - d.x, p.y - d.y) <= d.r + 1e-9) return d\n return welzl(P, [...R, p], i + 1)\n}\n\nfunction diskFromBoundary(R: Vec2[]): Circle {\n if (R.length === 0) return circle(0, 0, 0)\n if (R.length === 1) return circle(R[0]!.x, R[0]!.y, 0)\n if (R.length === 2) {\n const a = R[0]!\n const b = R[1]!\n return circle(\n (a.x + b.x) / 2,\n (a.y + b.y) / 2,\n Math.hypot(a.x - b.x, a.y - b.y) / 2,\n )\n }\n return circumcircle(R[0]!, R[1]!, R[2]!)\n}\n\nfunction circumcircle(a: Vec2, b: Vec2, c: Vec2): Circle {\n const d =\n 2 * (a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y))\n if (Math.abs(d) < 1e-14) {\n // collinear — diameter of farthest pair\n const pair = farthestPointPair([a, b, c])!\n return circle(\n (pair[0].x + pair[1].x) / 2,\n (pair[0].y + pair[1].y) / 2,\n Math.hypot(pair[0].x - pair[1].x, pair[0].y - pair[1].y) / 2,\n )\n }\n const ux =\n ((a.x * a.x + a.y * a.y) * (b.y - c.y) +\n (b.x * b.x + b.y * b.y) * (c.y - a.y) +\n (c.x * c.x + c.y * c.y) * (a.y - b.y)) /\n d\n const uy =\n ((a.x * a.x + a.y * a.y) * (c.x - b.x) +\n (b.x * b.x + b.y * b.y) * (a.x - c.x) +\n (c.x * c.x + c.y * c.y) * (b.x - a.x)) /\n d\n return circle(ux, uy, Math.hypot(ux - a.x, uy - a.y))\n}\n\nfunction projectPointToSegment(p: Vec2, a: Vec2, b: Vec2): Vec2 {\n const abx = b.x - a.x\n const aby = b.y - a.y\n const len2 = abx * abx + aby * aby\n if (len2 < 1e-18) return { ...a }\n let t = ((p.x - a.x) * abx + (p.y - a.y) * aby) / len2\n t = Math.max(0, Math.min(1, t))\n return { x: a.x + t * abx, y: a.y + t * aby }\n}\n\nexport const optimisation = {\n envelope,\n maximumInscribedCircle,\n largestEmptyCircle,\n largestEmptyCircles,\n maximumInscribedAARectangle,\n closestPoint,\n closestVertex,\n closestPointPair,\n farthestPointPair,\n minimumBoundingCircle,\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { V as Vec2 } from './vec2-CWyXN1Wj.js';
|
|
2
|
+
|
|
3
|
+
/** Ordered ring of vertices (typically without repeating the closing vertex). */
|
|
4
|
+
type Ring = Vec2[];
|
|
5
|
+
/**
|
|
6
|
+
* A path: one exterior ring plus optional holes, or an open polyline.
|
|
7
|
+
* `rings[0]` is the exterior (or the open polyline when `closed` is false).
|
|
8
|
+
* Additional rings are holes (only meaningful when `closed` is true).
|
|
9
|
+
*/
|
|
10
|
+
type Path = {
|
|
11
|
+
rings: Ring[];
|
|
12
|
+
closed: boolean;
|
|
13
|
+
};
|
|
14
|
+
declare function path(rings: Ring[], closed?: boolean): Path;
|
|
15
|
+
declare function polyline(points: Ring): Path;
|
|
16
|
+
declare function polygon(exterior: Ring, holes?: Ring[]): Path;
|
|
17
|
+
/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */
|
|
18
|
+
declare function normalizeRing(ring: Ring, eps?: number): Ring;
|
|
19
|
+
declare function clonePath(p: Path): Path;
|
|
20
|
+
|
|
21
|
+
export { type Path as P, type Ring as R, polygon as a, polyline as b, clonePath as c, normalizeRing as n, path as p };
|