pattapatta 0.3.0 → 0.4.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/{circle-BDbKfrdR.d.ts → circle-CauDMv8V.d.ts} +1 -1
  3. package/dist/circlePacking/index.d.ts +2 -3
  4. package/dist/construction/index.d.ts +11 -3
  5. package/dist/construction/index.js +429 -2
  6. package/dist/construction/index.js.map +1 -1
  7. package/dist/contour/index.d.ts +3 -4
  8. package/dist/contour/index.js.map +1 -1
  9. package/dist/conversion/index.d.ts +2 -3
  10. package/dist/{group-BNjVVYSQ.d.ts → group-YVmSJuGo.d.ts} +1 -1
  11. package/dist/hatch/index.d.ts +70 -4
  12. package/dist/hatch/index.js +414 -3
  13. package/dist/hatch/index.js.map +1 -1
  14. package/dist/hull/index.d.ts +1 -2
  15. package/dist/index.d.ts +12 -13
  16. package/dist/index.js +1816 -1260
  17. package/dist/index.js.map +1 -1
  18. package/dist/meshing/index.d.ts +3 -4
  19. package/dist/morphology/index.d.ts +2 -3
  20. package/dist/optimisation/index.d.ts +2 -3
  21. package/dist/{path-0RjhYjJs.d.ts → path-Boeshk9L.d.ts} +9 -2
  22. package/dist/pointSet/index.d.ts +15 -2
  23. package/dist/pointSet/index.js +148 -2
  24. package/dist/pointSet/index.js.map +1 -1
  25. package/dist/polygonisation/index.d.ts +1 -2
  26. package/dist/polygonisation/index.js +54 -50
  27. package/dist/polygonisation/index.js.map +1 -1
  28. package/dist/predicates/index.d.ts +2 -3
  29. package/dist/processing/index.d.ts +3 -4
  30. package/dist/{segment-BKuIFMKD.d.ts → segment-CpLILSd7.d.ts} +1 -1
  31. package/dist/segmentSet/index.d.ts +41 -3
  32. package/dist/segmentSet/index.js +261 -1
  33. package/dist/segmentSet/index.js.map +1 -1
  34. package/dist/shapeBoolean/index.d.ts +2 -3
  35. package/dist/svg/index.d.ts +2 -3
  36. package/dist/tiling/index.d.ts +10 -4
  37. package/dist/tiling/index.js +148 -13
  38. package/dist/tiling/index.js.map +1 -1
  39. package/dist/transformation/index.d.ts +1 -2
  40. package/dist/triangulation/index.d.ts +1 -2
  41. package/dist/triangulation/index.js.map +1 -1
  42. package/dist/voronoi/index.d.ts +2 -3
  43. package/package.json +1 -1
  44. package/dist/vec2-CWyXN1Wj.d.ts +0 -10
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/construction/index.ts"],"names":[],"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, Vec2 } from '../types/index.js'\nimport { path, polygon, polyline } from '../types/index.js'\n\n/** Regular n-gon approximating a circle. */\nexport function createCircle(\n cx: number,\n cy: number,\n radius: number,\n segments = 64,\n): Path {\n return createRegularPolygon(cx, cy, radius, Math.max(3, Math.floor(segments)))\n}\n\nexport function createRect(\n x: number,\n y: number,\n width: number,\n height: number,\n): Path {\n return polygon([\n { x, y },\n { x: x + width, y },\n { x: x + width, y: y + height },\n { x, y: y + height },\n ])\n}\n\nexport function createRegularPolygon(\n cx: number,\n cy: number,\n radius: number,\n sides: number,\n rotation = -Math.PI / 2,\n): Path {\n const n = Math.max(3, Math.floor(sides))\n const ring: Vec2[] = []\n for (let i = 0; i < n; i++) {\n const t = rotation + (i / n) * Math.PI * 2\n ring.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius })\n }\n return polygon(ring)\n}\n\n/** Annulus as exterior + hole. */\nexport function createRing(\n cx: number,\n cy: number,\n outerRadius: number,\n innerRadius: number,\n segments = 64,\n): Path {\n const outer = createCircle(cx, cy, outerRadius, segments).rings[0]!\n const inner = createCircle(cx, cy, Math.min(innerRadius, outerRadius), segments)\n .rings[0]!\n // hole winding opposite for Clipper\n return path([outer, [...inner].reverse()], true)\n}\n\n/** Open circular arc polyline from `startAngle` to `endAngle` (radians). */\nexport function createArc(\n cx: number,\n cy: number,\n radius: number,\n startAngle: number,\n endAngle: number,\n segments = 32,\n): Path {\n const n = Math.max(2, Math.floor(segments))\n const pts: Vec2[] = []\n for (let i = 0; i <= n; i++) {\n const t = startAngle + ((endAngle - startAngle) * i) / n\n pts.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius })\n }\n return polyline(pts)\n}\n\n/** Star polygon (outer/inner radius alternating). */\nexport function createStar(\n cx: number,\n cy: number,\n outerRadius: number,\n innerRadius: number,\n points = 5,\n rotation = -Math.PI / 2,\n): Path {\n const n = Math.max(3, Math.floor(points))\n const ring: Vec2[] = []\n for (let i = 0; i < n * 2; i++) {\n const r = i % 2 === 0 ? outerRadius : innerRadius\n const t = rotation + (i / (n * 2)) * Math.PI * 2\n ring.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r })\n }\n return polygon(ring)\n}\n\n/** Koch snowflake after `iterations` refinement (0 = equilateral triangle). */\nexport function createKochSnowflake(\n cx: number,\n cy: number,\n radius: number,\n iterations = 3,\n): Path {\n const tri = createRegularPolygon(cx, cy, radius, 3)\n let ring = tri.rings[0]!\n const iters = Math.max(0, Math.min(6, Math.floor(iterations)))\n for (let i = 0; i < iters; i++) {\n ring = kochRefine(ring)\n }\n return polygon(ring)\n}\n\nfunction kochRefine(ring: Vec2[]): Vec2[] {\n const out: Vec2[] = []\n const n = ring.length\n for (let i = 0; i < n; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n const dx = b.x - a.x\n const dy = b.y - a.y\n const p1 = { x: a.x + dx / 3, y: a.y + dy / 3 }\n const p2 = { x: a.x + (2 * dx) / 3, y: a.y + (2 * dy) / 3 }\n // outward peak (left of directed edge in Y-down SVG coords = rotate +90 for CCW tri)\n const px = a.x + dx / 2 - dy * (Math.sqrt(3) / 6)\n const py = a.y + dy / 2 + dx * (Math.sqrt(3) / 6)\n out.push(a, p1, { x: px, y: py }, p2)\n }\n return out\n}\n\nexport const construction = {\n createCircle,\n createRect,\n createRegularPolygon,\n createRing,\n createArc,\n createStar,\n createKochSnowflake,\n}\n"]}
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/shapeBoolean/index.ts","../../src/voronoi/index.ts","../../src/morphology/buffer.ts","../../src/morphology/index.ts","../../src/meshing/index.ts","../../src/construction/index.ts"],"names":["b","path","mulberry32"],"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, groupFromPaths, path, polygon, vec2 } from '../types/index.js'\nimport {\n clipDifference,\n clipIntersect,\n clipUnion,\n clipXor,\n pathToPathsD,\n pathsDToGroup,\n} from '../clipper/convert.js'\n\nfunction ensureClosed(p: Path): Path {\n if (!p.closed) {\n throw new Error('shapeBoolean requires closed paths')\n }\n return p\n}\n\n/** Union of two closed paths. */\nexport function union(a: Path, b: Path): Group {\n const result = clipUnion(pathToPathsD(ensureClosed(a)), pathToPathsD(ensureClosed(b)))\n return pathsDToGroup(result)\n}\n\n/** Union of many closed paths (folded left-to-right). */\nexport function unionAll(paths: Path[]): Group {\n if (paths.length === 0) return group([])\n let acc = pathToPathsD(ensureClosed(paths[0]!))\n for (let i = 1; i < paths.length; i++) {\n acc = clipUnion(acc, pathToPathsD(ensureClosed(paths[i]!)))\n }\n return pathsDToGroup(acc)\n}\n\n/** Intersection of two closed paths. */\nexport function intersect(a: Path, b: Path): Group {\n const result = clipIntersect(\n pathToPathsD(ensureClosed(a)),\n pathToPathsD(ensureClosed(b)),\n )\n return pathsDToGroup(result)\n}\n\n/** Subtract b from a (a \\ b). */\nexport function subtract(a: Path, b: Path): Group {\n const result = clipDifference(\n pathToPathsD(ensureClosed(a)),\n pathToPathsD(ensureClosed(b)),\n )\n return pathsDToGroup(result)\n}\n\n/** Subtract many shapes from a base. */\nexport function subtractAll(base: Path, shapes: Path[]): Group {\n let acc = pathToPathsD(ensureClosed(base))\n for (const s of shapes) {\n acc = clipDifference(acc, pathToPathsD(ensureClosed(s)))\n }\n return pathsDToGroup(acc)\n}\n\n/** Symmetric difference. */\nexport function symDifference(a: Path, b: Path): Group {\n const result = clipXor(pathToPathsD(ensureClosed(a)), pathToPathsD(ensureClosed(b)))\n return pathsDToGroup(result)\n}\n\n/** Complement of shape within axis-aligned rectangle [0,width] x [0,height]. */\nexport function complement(shape: Path, width: number, height: number): Group {\n const frame = polygon([\n vec2(0, 0),\n vec2(width, 0),\n vec2(width, height),\n vec2(0, height),\n ])\n return subtract(frame, shape)\n}\n\n/**\n * Painter-style occlusion: later paths in the group are on top.\n * Each path has higher paths subtracted from it; returns visible remainders.\n */\nexport function occlusionSubtract(g: Group): Group {\n const paths = g.paths\n const visible: Path[] = []\n for (let i = 0; i < paths.length; i++) {\n const subject = paths[i]\n if (!subject) continue\n const occluders = paths.slice(i + 1)\n const piece =\n occluders.length === 0\n ? groupFromPaths(ensureClosed(subject))\n : subtractAll(subject, occluders)\n for (const p of piece.paths) {\n visible.push(p)\n }\n }\n return group(visible)\n}\n\n/**\n * Regions covered by at least two input shapes.\n * If `merged`, collapse multi-overlaps into disjoint patches via union of pairwise intersects.\n */\nexport function overlapRegions(shapes: Path[], merged: boolean): Group {\n if (shapes.length < 2) return group([])\n const pairs: Path[] = []\n for (let i = 0; i < shapes.length; i++) {\n for (let j = i + 1; j < shapes.length; j++) {\n const a = shapes[i]\n const b = shapes[j]\n if (!a || !b) continue\n for (const p of intersect(a, b).paths) {\n pairs.push(p)\n }\n }\n }\n if (!merged) return group(pairs)\n return unionAll(pairs)\n}\n\nexport const shapeBoolean = {\n union,\n unionAll,\n intersect,\n subtract,\n subtractAll,\n symDifference,\n complement,\n occlusionSubtract,\n overlapRegions,\n}\n","import { Delaunay } from 'd3-delaunay'\nimport type { Group, Path, Vec2 } from '../types/index.js'\nimport { group, polygon } from '../types/index.js'\nimport { bounds } from '../predicates/index.js'\nimport { intersect } from '../shapeBoolean/index.js'\n\nexport type VoronoiOptions = {\n /** Clip cells to this bbox; default padded bounds of the sites. */\n bounds?: { minX: number; minY: number; maxX: number; maxY: number }\n}\n\n/**\n * Voronoi cells for a point set as closed polygons (clipped to a bbox).\n * PGS `compoundVoronoi` analogue — one path per site.\n */\nexport function compoundVoronoi(\n sites: Vec2[],\n options: VoronoiOptions = {},\n): Group {\n if (sites.length === 0) return group([])\n if (sites.length === 1) {\n const b = options.bounds ?? padBounds(sites, 1)\n return group([\n 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 }\n\n const b = options.bounds ?? padBounds(sites, 0.1)\n const delaunay = Delaunay.from(\n sites,\n (d) => d.x,\n (d) => d.y,\n )\n const voronoi = delaunay.voronoi([b.minX, b.minY, b.maxX, b.maxY])\n const paths: Path[] = []\n for (let i = 0; i < sites.length; i++) {\n const poly = voronoi.cellPolygon(i)\n if (!poly || poly.length < 3) continue\n // d3 closes the ring by repeating the first point\n const ring = poly.slice(0, -1).map(([x, y]) => ({ x, y }))\n if (ring.length >= 3) paths.push(polygon(ring))\n }\n return group(paths)\n}\n\n/**\n * Voronoi clipped to the interior of `container` (intersect each cell).\n */\nexport function innerVoronoi(\n sites: Vec2[],\n container: Path,\n options: VoronoiOptions = {},\n): Group {\n const b = options.bounds ?? {\n ...bounds(container),\n }\n const cells = compoundVoronoi(sites, { bounds: b })\n const clipped: Path[] = []\n for (const cell of cells.paths) {\n clipped.push(...intersect(cell, container).paths)\n }\n return group(clipped)\n}\n\n/** Raw cell polygons without container clip (same as compoundVoronoi). */\nexport function innerVoronoiRaw(\n sites: Vec2[],\n options: VoronoiOptions = {},\n): Group {\n return compoundVoronoi(sites, options)\n}\n\nfunction padBounds(\n sites: Vec2[],\n padRatio: number,\n): { minX: number; minY: number; maxX: number; maxY: number } {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const s of sites) {\n minX = Math.min(minX, s.x)\n minY = Math.min(minY, s.y)\n maxX = Math.max(maxX, s.x)\n maxY = Math.max(maxY, s.y)\n }\n const pad =\n Math.max(maxX - minX, maxY - minY, 1) * padRatio + 1e-6\n return {\n minX: minX - pad,\n minY: minY - pad,\n maxX: maxX + pad,\n maxY: maxY + pad,\n }\n}\n\nexport const voronoi = {\n compoundVoronoi,\n innerVoronoi,\n innerVoronoiRaw,\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","import type { Group, Path, Segment, Vec2 } from '../types/index.js'\nimport { group, polygon, polyline, segment } from '../types/index.js'\nimport { centroid, containsPoint, area } from '../predicates/index.js'\nimport { delaunayTriangulationPoints } from '../triangulation/index.js'\nimport { union } from '../shapeBoolean/index.js'\nimport { simplify as simplifyPath } from '../morphology/index.js'\n\n// ---------------------------------------------------------------------------\n// Shared mesh primitives\n// ---------------------------------------------------------------------------\n\ntype EdgeRec = { a: Vec2; b: Vec2; faceIds: number[] }\n\nfunction edgeKey(a: Vec2, b: Vec2): string {\n const aKey = `${a.x},${a.y}`\n const bKey = `${b.x},${b.y}`\n return aKey < bKey ? `${aKey}|${bKey}` : `${bKey}|${aKey}`\n}\n\nfunction vertKey(v: Vec2): string {\n return `${v.x},${v.y}`\n}\n\nfunction same(a: Vec2, b: Vec2, eps = 1e-12): boolean {\n return Math.abs(a.x - b.x) < eps && Math.abs(a.y - b.y) < eps\n}\n\nfunction dist(a: Vec2, b: Vec2): number {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n\nfunction mid(a: Vec2, b: Vec2): Vec2 {\n return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }\n}\n\nfunction lerp(a: Vec2, b: Vec2, t: number): Vec2 {\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t }\n}\n\nfunction unique(points: Vec2[]): Vec2[] {\n const seen = new Set<string>()\n const out: Vec2[] = []\n for (const p of points) {\n const k = vertKey(p)\n if (seen.has(k)) continue\n seen.add(k)\n out.push({ ...p })\n }\n return out\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 ringOf(face: Path): Vec2[] {\n return face.rings[0] ?? []\n}\n\n/** Build undirected edge → incident face indices for exterior rings. */\nfunction buildEdgeMap(faces: Path[]): Map<string, EdgeRec> {\n const edges = new Map<string, EdgeRec>()\n faces.forEach((f, fi) => {\n const ring = ringOf(f)\n if (ring.length < 2) return\n const n = ring.length\n const closed = f.closed !== false && n >= 3\n const lim = closed ? n : n - 1\n for (let i = 0; i < lim; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n const key = edgeKey(a, b)\n const rec = edges.get(key)\n if (rec) rec.faceIds.push(fi)\n else edges.set(key, { a: { ...a }, b: { ...b }, faceIds: [fi] })\n }\n })\n return edges\n}\n\nfunction perimeterKeys(edgeMap: Map<string, EdgeRec>): Set<string> {\n const out = new Set<string>()\n for (const [k, e] of edgeMap) {\n if (e.faceIds.length === 1) out.add(k)\n }\n return out\n}\n\nfunction faceAdjacency(faces: Path[]): number[][] {\n const edgeMap = buildEdgeMap(faces)\n const adj: number[][] = faces.map(() => [])\n for (const e of edgeMap.values()) {\n if (e.faceIds.length === 2) {\n const [i, j] = e.faceIds\n adj[i!]!.push(j!)\n adj[j!]!.push(i!)\n }\n }\n return adj\n}\n\n/** Collect unique sites from triangulation faces. */\nfunction sitesFromFaces(faces: Path[]): Vec2[] {\n return unique(faces.flatMap((f) => ringOf(f)))\n}\n\n/**\n * Merge triangles that share a removed (non-kept) edge into polygonal faces.\n * Kept edges (and perimeter) form the resulting face boundaries.\n */\nfunction facesFromKeptEdges(\n tris: Path[],\n kept: Set<string>,\n): Path[] {\n if (tris.length === 0) return []\n const parent = tris.map((_, i) => i)\n const find = (i: number): number => {\n let x = i\n while (parent[x] !== x) {\n parent[x] = parent[parent[x]!]!\n x = parent[x]!\n }\n return x\n }\n const unite = (a: number, b: number) => {\n const ra = find(a)\n const rb = find(b)\n if (ra !== rb) parent[rb] = ra\n }\n\n const edgeMap = buildEdgeMap(tris)\n for (const [key, e] of edgeMap) {\n if (e.faceIds.length === 2 && !kept.has(key)) {\n unite(e.faceIds[0]!, e.faceIds[1]!)\n }\n }\n\n const components = new Map<number, number[]>()\n for (let i = 0; i < tris.length; i++) {\n const r = find(i)\n const list = components.get(r) ?? []\n list.push(i)\n components.set(r, list)\n }\n\n const out: Path[] = []\n for (const members of components.values()) {\n const ring = boundaryRingOfComponent(tris, members)\n if (ring && ring.length >= 3) out.push(polygon(ring))\n }\n return out\n}\n\n/** Exterior ring of a set of triangles (edges appearing once in the set). */\nfunction boundaryRingOfComponent(\n tris: Path[],\n members: number[],\n): Vec2[] | null {\n const edgeCount = new Map<string, { a: Vec2; b: Vec2; n: number }>()\n for (const fi of members) {\n const ring = ringOf(tris[fi]!)\n if (ring.length < 3) continue\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n const key = edgeKey(a, b)\n const rec = edgeCount.get(key)\n if (rec) rec.n++\n else edgeCount.set(key, { a: { ...a }, b: { ...b }, n: 1 })\n }\n }\n const boundary: { a: Vec2; b: Vec2 }[] = []\n for (const e of edgeCount.values()) {\n if (e.n === 1) boundary.push({ a: e.a, b: e.b })\n }\n if (boundary.length < 3) return null\n return orderEdgesIntoRing(boundary)\n}\n\nfunction orderEdgesIntoRing(edges: { a: Vec2; b: Vec2 }[]): Vec2[] | null {\n if (edges.length === 0) return null\n const adj = new Map<string, Vec2[]>()\n const add = (from: Vec2, to: Vec2) => {\n const k = vertKey(from)\n const list = adj.get(k) ?? []\n list.push(to)\n adj.set(k, list)\n }\n for (const e of edges) {\n add(e.a, e.b)\n add(e.b, e.a)\n }\n\n const start = edges[0]!.a\n const ring: Vec2[] = [{ ...start }]\n let prev = start\n let curr = edges[0]!.b\n const maxSteps = edges.length + 2\n for (let step = 0; step < maxSteps; step++) {\n ring.push({ ...curr })\n if (same(curr, start) && ring.length > 2) {\n ring.pop()\n return ring\n }\n const nbrs = adj.get(vertKey(curr)) ?? []\n let next: Vec2 | null = null\n for (const n of nbrs) {\n if (!same(n, prev)) {\n next = n\n break\n }\n }\n if (!next) break\n prev = curr\n curr = next\n }\n return ring.length >= 3 ? ring : null\n}\n\nfunction applyEdgeFilter(\n tris: Path[],\n shouldKeep: (a: Vec2, b: Vec2, perimeter: boolean) => boolean,\n preservePerimeter: boolean,\n): Path[] {\n const edgeMap = buildEdgeMap(tris)\n const peri = perimeterKeys(edgeMap)\n const kept = new Set<string>()\n for (const [key, e] of edgeMap) {\n const isPeri = peri.has(key)\n if (isPeri && preservePerimeter) {\n kept.add(key)\n continue\n }\n if (shouldKeep(e.a, e.b, isPeri)) kept.add(key)\n }\n return facesFromKeptEdges(tris, kept)\n}\n\nfunction asTriFaces(input: Vec2[] | Path[]): Path[] {\n if (input.length === 0) return []\n const first = input[0]!\n if ('rings' in first) return input as Path[]\n return delaunayTriangulationPoints(input as Vec2[])\n}\n\nfunction pointsOf(input: Vec2[] | Path[]): Vec2[] {\n if (input.length === 0) return []\n const first = input[0]!\n if ('rings' in first) return sitesFromFaces(input as Path[])\n return input as Vec2[]\n}\n\n// ---------------------------------------------------------------------------\n// Extract / query\n// ---------------------------------------------------------------------------\n\n/** Edges shared by at least two faces (excludes perimeter and hole boundaries). */\nexport function extractInnerEdges(faces: Path[]): Segment[] {\n const edgeMap = buildEdgeMap(faces)\n const out: Segment[] = []\n for (const e of edgeMap.values()) {\n if (e.faceIds.length >= 2) out.push(segment(e.a, e.b))\n }\n return out\n}\n\n/** Vertices not on the mesh perimeter. */\nexport function extractInnerVertices(faces: Path[]): Vec2[] {\n const edgeMap = buildEdgeMap(faces)\n const peri = perimeterKeys(edgeMap)\n const periVerts = new Set<string>()\n for (const key of peri) {\n const e = edgeMap.get(key)!\n periVerts.add(vertKey(e.a))\n periVerts.add(vertKey(e.b))\n }\n const out: Vec2[] = []\n const seen = new Set<string>()\n for (const f of faces) {\n for (const v of ringOf(f)) {\n const k = vertKey(v)\n if (periVerts.has(k) || seen.has(k)) continue\n seen.add(k)\n out.push({ ...v })\n }\n }\n return out\n}\n\n/** First face that contains `point`, or null. */\nexport function findContainingFace(\n faces: Path[],\n point: Vec2,\n): Path | null {\n for (const f of faces) {\n if (containsPoint(f, point)) return f\n }\n return null\n}\n\n/** Split every face edge longer than `maxLen` by inserting subdivision points. */\nexport function splitEdges(faces: Path[], maxLen: number): Group {\n const lim = Math.max(maxLen, 1e-9)\n const out: Path[] = []\n for (const f of faces) {\n out.push(\n polygon(\n densifyRing(ringOf(f), lim),\n f.rings.slice(1).map((r) => densifyRing(r, lim)),\n ),\n )\n }\n return group(out)\n}\n\nfunction densifyRing(ring: Vec2[], maxLen: number): Vec2[] {\n if (ring.length < 2) return ring.map((v) => ({ ...v }))\n const out: Vec2[] = []\n const n = ring.length\n for (let i = 0; i < n; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n out.push({ ...a })\n const d = dist(a, b)\n const steps = Math.floor(d / maxLen)\n for (let s = 1; s < steps; s++) out.push(lerp(a, b, s / steps))\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Graph faces (Urquhart / Gabriel / RNG / Spanner / Dual)\n// ---------------------------------------------------------------------------\n\nfunction isGabriel(a: Vec2, b: Vec2, points: Vec2[]): boolean {\n const m = mid(a, b)\n const r = dist(a, b) / 2\n for (const p of points) {\n if (same(p, a) || same(p, b)) continue\n if (dist(p, m) < r - 1e-9) return false\n }\n return true\n}\n\nfunction isRelativeNeighbor(a: Vec2, b: Vec2, points: Vec2[]): boolean {\n const dab = dist(a, b)\n for (const p of points) {\n if (same(p, a) || same(p, b)) continue\n if (Math.max(dist(p, a), dist(p, b)) < dab - 1e-9) return false\n }\n return true\n}\n\n/** Urquhart faces: remove longest edge of each triangle, then polygonize. */\nexport function urquhartFaces(\n input: Vec2[] | Path[],\n preservePerimeter = false,\n): Group {\n const tris = asTriFaces(input)\n const edgeMap = buildEdgeMap(tris)\n const peri = perimeterKeys(edgeMap)\n const removed = new Set<string>()\n for (const t of tris) {\n const r = ringOf(t)\n if (r.length < 3) continue\n let longestKey = ''\n let longestLen = -1\n for (let i = 0; i < 3; i++) {\n const a = r[i]!\n const b = r[(i + 1) % 3]!\n const d = dist(a, b)\n if (d > longestLen) {\n longestLen = d\n longestKey = edgeKey(a, b)\n }\n }\n if (longestKey && !(preservePerimeter && peri.has(longestKey))) {\n removed.add(longestKey)\n }\n }\n const kept = new Set<string>()\n for (const key of edgeMap.keys()) {\n if (!removed.has(key)) kept.add(key)\n }\n return group(facesFromKeptEdges(tris, kept))\n}\n\n/** Gabriel graph faces from a point set or triangulation. */\nexport function gabrielFaces(\n input: Vec2[] | Path[],\n preservePerimeter = false,\n): Group {\n const tris = asTriFaces(input)\n const pts = pointsOf(input)\n return group(\n applyEdgeFilter(\n tris,\n (a, b) => isGabriel(a, b, pts),\n preservePerimeter,\n ),\n )\n}\n\n/** Relative-neighborhood graph faces. */\nexport function relativeNeighborFaces(\n input: Vec2[] | Path[],\n preservePerimeter = false,\n): Group {\n const tris = asTriFaces(input)\n const pts = pointsOf(input)\n return group(\n applyEdgeFilter(\n tris,\n (a, b) => isRelativeNeighbor(a, b, pts),\n preservePerimeter,\n ),\n )\n}\n\n/**\n * Greedy sparse spanner faces. Higher `k` allows longer detours, so more edges\n * are collapsed → larger faces.\n */\nexport function spannerFaces(\n input: Vec2[] | Path[],\n k = 1,\n preservePerimeter = false,\n): Group {\n const tris = asTriFaces(input)\n const edgeMap = buildEdgeMap(tris)\n const peri = perimeterKeys(edgeMap)\n const stretch = Math.max(1, k)\n const edges = [...edgeMap.entries()].map(([key, e]) => ({\n key,\n a: e.a,\n b: e.b,\n len: dist(e.a, e.b),\n peri: peri.has(key),\n }))\n edges.sort((a, b) => a.len - b.len)\n\n const adj = new Map<string, { to: string; w: number }[]>()\n const link = (a: Vec2, b: Vec2, w: number) => {\n const ka = vertKey(a)\n const kb = vertKey(b)\n if (!adj.has(ka)) adj.set(ka, [])\n if (!adj.has(kb)) adj.set(kb, [])\n adj.get(ka)!.push({ to: kb, w })\n adj.get(kb)!.push({ to: ka, w })\n }\n const shortest = (from: Vec2, to: Vec2): number => {\n const start = vertKey(from)\n const goal = vertKey(to)\n const distMap = new Map<string, number>([[start, 0]])\n const pq: { k: string; d: number }[] = [{ k: start, d: 0 }]\n while (pq.length) {\n pq.sort((x, y) => x.d - y.d)\n const cur = pq.shift()!\n if (cur.k === goal) return cur.d\n if (cur.d > (distMap.get(cur.k) ?? Infinity)) continue\n for (const e of adj.get(cur.k) ?? []) {\n const nd = cur.d + e.w\n if (nd < (distMap.get(e.to) ?? Infinity)) {\n distMap.set(e.to, nd)\n pq.push({ k: e.to, d: nd })\n }\n }\n }\n return Infinity\n }\n\n const kept = new Set<string>()\n for (const e of edges) {\n if (e.peri && preservePerimeter) {\n kept.add(e.key)\n link(e.a, e.b, e.len)\n continue\n }\n const pathLen = shortest(e.a, e.b)\n if (pathLen > stretch * e.len + 1e-9) {\n kept.add(e.key)\n link(e.a, e.b, e.len)\n } else if (e.peri) {\n // Keep perimeter even when stretch would drop it, so faces stay bounded.\n kept.add(e.key)\n link(e.a, e.b, e.len)\n }\n }\n return group(facesFromKeptEdges(tris, kept))\n}\n\n/**\n * Dual faces of a triangulation: closed polygons connecting centroids of\n * triangles around each interior primal vertex.\n */\nexport function dualFaces(faces: Path[]): Group {\n if (faces.length === 0) return group([])\n const edgeMap = buildEdgeMap(faces)\n const peri = perimeterKeys(edgeMap)\n const periVerts = new Set<string>()\n for (const key of peri) {\n const e = edgeMap.get(key)!\n periVerts.add(vertKey(e.a))\n periVerts.add(vertKey(e.b))\n }\n\n // vertex → list of (faceIndex, angle of face centroid from vertex)\n const incident = new Map<string, { fi: number; ang: number; v: Vec2 }[]>()\n faces.forEach((f, fi) => {\n const c = centroid(f)\n for (const v of ringOf(f)) {\n const k = vertKey(v)\n if (periVerts.has(k)) continue\n const list = incident.get(k) ?? []\n list.push({ fi, ang: Math.atan2(c.y - v.y, c.x - v.x), v })\n incident.set(k, list)\n }\n })\n\n const out: Path[] = []\n for (const list of incident.values()) {\n if (list.length < 3) continue\n // Dedupe by face index\n const byFace = new Map<number, (typeof list)[0]>()\n for (const item of list) byFace.set(item.fi, item)\n const uniqueList = [...byFace.values()]\n if (uniqueList.length < 3) continue\n uniqueList.sort((a, b) => a.ang - b.ang)\n const ring = uniqueList.map((item) => centroid(faces[item.fi]!))\n out.push(polygon(ring))\n }\n return group(out)\n}\n\n// ---------------------------------------------------------------------------\n// Quadrangulations\n// ---------------------------------------------------------------------------\n\nfunction triVerts(t: Path): [Vec2, Vec2, Vec2] | null {\n const r = ringOf(t)\n if (r.length < 3) return null\n return [r[0]!, r[1]!, r[2]!]\n}\n\n/** Midpoint cache keyed by edge. */\nfunction midpointMap(faces: Path[]): Map<string, Vec2> {\n const mids = new Map<string, Vec2>()\n const edgeMap = buildEdgeMap(faces)\n for (const [key, e] of edgeMap) {\n mids.set(key, mid(e.a, e.b))\n }\n return mids\n}\n\n/**\n * Centroid quadrangulation: one quad per triangulation edge using the\n * centroids of the one or two incident triangles.\n */\nexport function centroidQuadrangulation(\n input: Vec2[] | Path[],\n preservePerimeter = true,\n): Group {\n const tris = asTriFaces(input)\n const edgeMap = buildEdgeMap(tris)\n const centers = tris.map((t) => centroid(t))\n const out: Path[] = []\n const peri = perimeterKeys(edgeMap)\n\n for (const [key, e] of edgeMap) {\n const ids = e.faceIds\n if (ids.length === 2) {\n const c0 = centers[ids[0]!]!\n const c1 = centers[ids[1]!]!\n out.push(polygon([e.a, c0, e.b, c1]))\n } else if (ids.length === 1 && preservePerimeter) {\n const c0 = centers[ids[0]!]!\n out.push(polygon([e.a, e.b, c0]))\n } else if (ids.length === 1 && !preservePerimeter && peri.has(key)) {\n // drop perimeter-only triangles when not preserving\n }\n }\n return group(out)\n}\n\n/**\n * Split (Catmull–Clark) quadrangulation: 3 quads per triangle via edge\n * midpoints and the face centroid.\n */\nexport function splitQuadrangulation(input: Vec2[] | Path[]): Group {\n const tris = asTriFaces(input)\n const mids = midpointMap(tris)\n const out: Path[] = []\n for (const t of tris) {\n const v = triVerts(t)\n if (!v) continue\n const [a, b, c] = v\n const g = centroid(t)\n const mab = mids.get(edgeKey(a, b)) ?? mid(a, b)\n const mbc = mids.get(edgeKey(b, c)) ?? mid(b, c)\n const mca = mids.get(edgeKey(c, a)) ?? mid(c, a)\n out.push(polygon([a, mab, g, mca]))\n out.push(polygon([b, mbc, g, mab]))\n out.push(polygon([c, mca, g, mbc]))\n }\n return group(out)\n}\n\n/**\n * Edge-collapse quadrangulation: greedy pairing of adjacent triangles into quads\n * (edge-coloring style collapse of shared edges).\n */\nexport function edgeCollapseQuadrangulation(\n input: Vec2[] | Path[],\n preservePerimeter = true,\n): Group {\n const tris = asTriFaces(input)\n const edgeMap = buildEdgeMap(tris)\n const candidates: { key: string; a: Vec2; b: Vec2; i: number; j: number; q: number }[] =\n []\n for (const [key, e] of edgeMap) {\n if (e.faceIds.length !== 2) continue\n const [i, j] = e.faceIds\n const quad = mergeTriPair(tris[i!]!, tris[j!]!, e.a, e.b)\n if (!quad) continue\n candidates.push({\n key,\n a: e.a,\n b: e.b,\n i: i!,\n j: j!,\n q: quadQuality(quad),\n })\n }\n candidates.sort((x, y) => y.q - x.q)\n\n const used = new Set<number>()\n const out: Path[] = []\n for (const c of candidates) {\n if (used.has(c.i) || used.has(c.j)) continue\n const quad = mergeTriPair(tris[c.i]!, tris[c.j]!, c.a, c.b)\n if (!quad) continue\n out.push(quad)\n used.add(c.i)\n used.add(c.j)\n }\n for (let i = 0; i < tris.length; i++) {\n if (used.has(i)) continue\n if (preservePerimeter) out.push(tris[i]!)\n }\n return group(out)\n}\n\n/**\n * Matching quadrangulation: quality-weighted greedy matching of triangles\n * (Blossom-Quad first step; leftover triangles retained).\n */\nexport function matchingQuadrangulation(input: Vec2[] | Path[]): Group {\n return edgeCollapseQuadrangulation(input, true)\n}\n\nfunction mergeTriPair(\n t0: Path,\n t1: Path,\n ea: Vec2,\n eb: Vec2,\n): Path | null {\n const opp0 = oppositeVertex(t0, ea, eb)\n const opp1 = oppositeVertex(t1, ea, eb)\n if (!opp0 || !opp1) return null\n // Order: opp0 → ea → opp1 → eb (convex quad when possible)\n return polygon([opp0, ea, opp1, eb])\n}\n\nfunction oppositeVertex(t: Path, ea: Vec2, eb: Vec2): Vec2 | null {\n for (const v of ringOf(t)) {\n if (!same(v, ea) && !same(v, eb)) return v\n }\n return null\n}\n\nfunction quadQuality(q: Path): number {\n const r = ringOf(q)\n if (r.length < 4) return 0\n // Prefer near-square: maximize min edge / max edge and prefer angles near 90°.\n const lens: number[] = []\n for (let i = 0; i < 4; i++) {\n lens.push(dist(r[i]!, r[(i + 1) % 4]!))\n }\n const minL = Math.min(...lens)\n const maxL = Math.max(...lens)\n if (maxL < 1e-12) return 0\n return minL / maxL\n}\n\n/**\n * Spiral quadrangulation from a point set (characteristic spiral pattern).\n */\nexport function spiralQuadrangulation(points: Vec2[]): Group {\n if (points.length < 4) return group([])\n const pts = unique(points)\n const cx = pts.reduce((s, p) => s + p.x, 0) / pts.length\n const cy = pts.reduce((s, p) => s + p.y, 0) / pts.length\n\n // Sort by angle, then assign to radial rings by distance rank.\n const withMeta = pts.map((p) => ({\n p,\n ang: Math.atan2(p.y - cy, p.x - cx),\n rad: Math.hypot(p.x - cx, p.y - cy),\n }))\n withMeta.sort((a, b) => a.rad - b.rad || a.ang - b.ang)\n\n const ringCount = Math.max(2, Math.ceil(Math.sqrt(pts.length)))\n const rings: Vec2[][] = Array.from({ length: ringCount }, () => [])\n withMeta.forEach((item, i) => {\n const ri = Math.min(\n ringCount - 1,\n Math.floor((i / withMeta.length) * ringCount),\n )\n rings[ri]!.push(item.p)\n })\n for (const ring of rings) {\n ring.sort(\n (a, b) =>\n Math.atan2(a.y - cy, a.x - cx) - Math.atan2(b.y - cy, b.x - cx),\n )\n }\n\n const out: Path[] = []\n for (let r = 0; r < rings.length - 1; r++) {\n const inner = rings[r]!\n const outer = rings[r + 1]!\n if (inner.length === 0 || outer.length === 0) continue\n const n = Math.max(inner.length, outer.length)\n for (let i = 0; i < n; i++) {\n const a = inner[i % inner.length]!\n const b = inner[(i + 1) % inner.length]!\n const c = outer[(i + 1) % outer.length]!\n const d = outer[i % outer.length]!\n // Skip degenerate\n const verts = unique([a, b, c, d])\n if (verts.length >= 3) out.push(polygon(verts.length === 3 ? verts : [a, b, c, d]))\n }\n }\n return group(out)\n}\n\n// ---------------------------------------------------------------------------\n// Mesh process\n// ---------------------------------------------------------------------------\n\nfunction vertexNeighborhood(faces: Path[]): {\n positions: Map<string, Vec2>\n neighbors: Map<string, Set<string>>\n perimeter: Set<string>\n} {\n const edgeMap = buildEdgeMap(faces)\n const peri = perimeterKeys(edgeMap)\n const perimeter = new Set<string>()\n for (const key of peri) {\n const e = edgeMap.get(key)!\n perimeter.add(vertKey(e.a))\n perimeter.add(vertKey(e.b))\n }\n const positions = new Map<string, Vec2>()\n const neighbors = new Map<string, Set<string>>()\n const link = (a: Vec2, b: Vec2) => {\n const ka = vertKey(a)\n const kb = vertKey(b)\n positions.set(ka, { ...a })\n positions.set(kb, { ...b })\n if (!neighbors.has(ka)) neighbors.set(ka, new Set())\n if (!neighbors.has(kb)) neighbors.set(kb, new Set())\n neighbors.get(ka)!.add(kb)\n neighbors.get(kb)!.add(ka)\n }\n for (const e of edgeMap.values()) link(e.a, e.b)\n return { positions, neighbors, perimeter }\n}\n\nfunction remapFaces(\n faces: Path[],\n positions: Map<string, Vec2>,\n): Path[] {\n return faces.map((f) =>\n polygon(\n ringOf(f).map((v) => {\n const p = positions.get(vertKey(v))\n return p ? { ...p } : { ...v }\n }),\n f.rings.slice(1).map((r) =>\n r.map((v) => {\n const p = positions.get(vertKey(v))\n return p ? { ...p } : { ...v }\n }),\n ),\n ),\n )\n}\n\n/** Weighted Laplacian mesh smoothing. */\nexport function smoothMesh(\n faces: Path[],\n iterationsOrCutoff: number,\n preservePerimeter = true,\n): Group {\n if (faces.length === 0) return group([])\n let { positions, neighbors, perimeter } = vertexNeighborhood(faces)\n const maxIter =\n iterationsOrCutoff >= 1 && Number.isInteger(iterationsOrCutoff)\n ? iterationsOrCutoff\n : 200\n const cutoff =\n iterationsOrCutoff > 0 && iterationsOrCutoff < 1\n ? iterationsOrCutoff\n : 0\n\n for (let iter = 0; iter < maxIter; iter++) {\n const next = new Map<string, Vec2>()\n let maxDisp = 0\n for (const [k, p] of positions) {\n if (preservePerimeter && perimeter.has(k)) {\n next.set(k, p)\n continue\n }\n const nbrs = neighbors.get(k)\n if (!nbrs || nbrs.size === 0) {\n next.set(k, p)\n continue\n }\n let sx = 0\n let sy = 0\n for (const nk of nbrs) {\n const n = positions.get(nk)!\n sx += n.x\n sy += n.y\n }\n const np = { x: sx / nbrs.size, y: sy / nbrs.size }\n maxDisp = Math.max(maxDisp, dist(p, np))\n next.set(k, np)\n }\n positions = next\n if (cutoff > 0 && maxDisp < cutoff) break\n }\n return group(remapFaces(faces, positions))\n}\n\n/**\n * Catmull–Clark-style subdivision: split each edge at `edgeSplitRatio` and\n * connect to the face centroid (N subfaces per N-gon).\n */\nexport function subdivideMesh(\n faces: Path[],\n edgeSplitRatio = 0.5,\n): Group {\n const t = Math.min(1, Math.max(0, edgeSplitRatio))\n const out: Path[] = []\n for (const f of faces) {\n const ring = ringOf(f)\n if (ring.length < 3) continue\n const c = centroid(f)\n const n = ring.length\n const splits: Vec2[] = []\n for (let i = 0; i < n; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n splits.push(lerp(a, b, t))\n }\n for (let i = 0; i < n; i++) {\n const v = ring[i]!\n const sPrev = splits[(i - 1 + n) % n]!\n const sNext = splits[i]!\n out.push(polygon([v, sNext, c, sPrev]))\n }\n }\n return group(out)\n}\n\n/**\n * Simplify face boundaries while preserving mesh topology (shared edges\n * simplified once, then faces rebuilt).\n */\nexport function simplifyMesh(\n faces: Path[],\n tolerance: number,\n preservePerimeter = true,\n): Group {\n if (tolerance <= 0 || faces.length === 0) return group(faces.map((f) => polygon(ringOf(f), f.rings.slice(1))))\n const edgeMap = buildEdgeMap(faces)\n const peri = perimeterKeys(edgeMap)\n\n // Simplify each unique edge as a 2-point segment is a no-op; for faces with\n // more than 3 verts, simplify the full ring then re-snap shared vertices.\n const simplified = faces.map((f) => {\n const ring = ringOf(f)\n if (ring.length <= 3) return polygon(ring)\n if (preservePerimeter) {\n // Only simplify interior-only vertices: use path simplify then restore peri verts.\n const s = simplifyPath(f, tolerance)\n const sr = ringOf(s)\n if (sr.length < 3) return polygon(ring)\n return polygon(sr, s.rings.slice(1))\n }\n return simplifyPath(f, tolerance)\n })\n\n // Snap nearly-coincident vertices across faces for topology\n const all = unique(simplified.flatMap((f) => ringOf(f)))\n const snapped = clusterSnap(all, Math.max(tolerance * 0.5, 1e-9))\n const remap = (v: Vec2): Vec2 => {\n let best = v\n let bestD = Infinity\n for (const s of snapped) {\n const d = dist(v, s)\n if (d < bestD) {\n bestD = d\n best = s\n }\n }\n return { ...best }\n }\n // Preserve perimeter vertices exactly when requested\n const periVerts = new Set<string>()\n if (preservePerimeter) {\n for (const key of peri) {\n const e = edgeMap.get(key)!\n periVerts.add(vertKey(e.a))\n periVerts.add(vertKey(e.b))\n }\n }\n\n return group(\n simplified.map((f, i) => {\n const original = ringOf(faces[i]!)\n const ring = ringOf(f).map((v, vi) => {\n if (preservePerimeter && original[vi] && periVerts.has(vertKey(original[vi]!))) {\n return { ...original[vi]! }\n }\n // also check if v matches a peri vert\n for (const ov of original) {\n if (periVerts.has(vertKey(ov)) && dist(v, ov) < tolerance * 2) {\n return { ...ov }\n }\n }\n return remap(v)\n })\n return polygon(unique(ring).length >= 3 ? ring : original)\n }),\n )\n}\n\nfunction clusterSnap(points: Vec2[], tol: number): Vec2[] {\n const clusters: Vec2[][] = []\n for (const p of points) {\n let found = false\n for (const c of clusters) {\n if (dist(p, c[0]!) <= tol) {\n c.push(p)\n found = true\n break\n }\n }\n if (!found) clusters.push([p])\n }\n return clusters.map((c) => ({\n x: c.reduce((s, p) => s + p.x, 0) / c.length,\n y: c.reduce((s, p) => s + p.y, 0) / c.length,\n }))\n}\n\n/** Randomly dissolve adjacent faces that share a class id. */\nexport function stochasticMerge(\n faces: Path[],\n nClasses: number,\n seed = 1,\n): Group {\n if (faces.length === 0) return group([])\n const classes = Math.max(1, Math.floor(nClasses))\n const rng = mulberry32(seed >>> 0)\n const label = faces.map(() => Math.floor(rng() * classes))\n const adj = faceAdjacency(faces)\n const parent = faces.map((_, i) => i)\n const find = (i: number): number => {\n let x = i\n while (parent[x] !== x) {\n parent[x] = parent[parent[x]!]!\n x = parent[x]!\n }\n return x\n }\n const unite = (a: number, b: number) => {\n const ra = find(a)\n const rb = find(b)\n if (ra !== rb) parent[rb] = ra\n }\n for (let i = 0; i < faces.length; i++) {\n for (const j of adj[i]!) {\n if (j > i && label[i] === label[j]) unite(i, j)\n }\n }\n const comps = new Map<number, number[]>()\n for (let i = 0; i < faces.length; i++) {\n const r = find(i)\n const list = comps.get(r) ?? []\n list.push(i)\n comps.set(r, list)\n }\n const out: Path[] = []\n for (const members of comps.values()) {\n if (members.length === 1) {\n out.push(faces[members[0]!]!)\n continue\n }\n const merged = dissolveFaces(members.map((i) => faces[i]!))\n out.push(...merged)\n }\n return group(out)\n}\n\nfunction dissolveFaces(faces: Path[]): Path[] {\n if (faces.length === 0) return []\n if (faces.length === 1) return [faces[0]!]\n try {\n let acc = faces[0]!\n for (let i = 1; i < faces.length; i++) {\n const u = union(acc, faces[i]!)\n if (u.paths.length === 0) continue\n // Prefer largest piece if union fragments\n acc = u.paths.reduce((best, p) =>\n area(p) > area(best) ? p : best,\n )\n }\n return [acc]\n } catch {\n // Fallback: convex hull of vertices\n return [convexHullOf(faces.flatMap((f) => ringOf(f)))]\n }\n}\n\n/**\n * Merge faces below `minArea` into adjacent neighbors, or merge until at most\n * `remainingFaces` remain when options use `{ remainingFaces }`.\n */\nexport function areaMerge(\n faces: Path[],\n minAreaOrOptions: number | { minArea?: number; remainingFaces?: number },\n): Group {\n let working = [...faces]\n if (working.length === 0) return group([])\n\n if (typeof minAreaOrOptions === 'object' && minAreaOrOptions.remainingFaces != null) {\n const target = Math.max(1, Math.floor(minAreaOrOptions.remainingFaces))\n while (working.length > target) {\n const next = mergeSmallestAdjacent(working)\n if (next.length >= working.length) break\n working = next\n }\n return group(working)\n }\n\n const minArea =\n typeof minAreaOrOptions === 'number'\n ? minAreaOrOptions\n : (minAreaOrOptions.minArea ?? 0)\n\n let guard = 0\n while (guard++ < working.length + 10) {\n const smallIdx = working.findIndex((f) => area(f) < minArea)\n if (smallIdx < 0) break\n const next = mergeFaceIntoNeighbor(working, smallIdx)\n if (!next) break\n working = next\n }\n return group(working)\n}\n\nfunction mergeSmallestAdjacent(faces: Path[]): Path[] {\n let best = -1\n let bestA = Infinity\n for (let i = 0; i < faces.length; i++) {\n const a = area(faces[i]!)\n if (a < bestA) {\n bestA = a\n best = i\n }\n }\n if (best < 0) return faces\n return mergeFaceIntoNeighbor(faces, best) ?? faces\n}\n\nfunction mergeFaceIntoNeighbor(\n faces: Path[],\n idx: number,\n): Path[] | null {\n const adj = faceAdjacency(faces)\n const nbrs = adj[idx] ?? []\n if (nbrs.length === 0) {\n // No adjacency — drop tiny island or keep\n return faces.filter((_, i) => i !== idx)\n }\n // Prefer largest neighbor\n let bestN = nbrs[0]!\n let bestA = -1\n for (const n of nbrs) {\n const a = area(faces[n]!)\n if (a > bestA) {\n bestA = a\n bestN = n\n }\n }\n const merged = dissolveFaces([faces[idx]!, faces[bestN]!])\n const out: Path[] = []\n for (let i = 0; i < faces.length; i++) {\n if (i === idx || i === bestN) continue\n out.push(faces[i]!)\n }\n out.push(...merged)\n return out\n}\n\nfunction convexHullOf(points: Vec2[]): Path {\n const pts = unique(points)\n if (pts.length < 3) return polygon(pts)\n pts.sort((a, b) => (a.x === b.x ? a.y - b.y : a.x - b.x))\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 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 lower.pop()\n upper.pop()\n return polygon([...lower, ...upper])\n}\n\nfunction mulberry32(a: number): () => number {\n return () => {\n a |= 0\n a = (a + 0x6d2b79f5) | 0\n let t = Math.imul(a ^ (a >>> 15), 1 | a)\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n// ---------------------------------------------------------------------------\n// Repair\n// ---------------------------------------------------------------------------\n\n/**\n * Locate near-miss gaps between faces that should form a valid mesh\n * (boundary segments without a matching twin within a tiny tolerance).\n */\nexport function findBreaks(faces: Path[]): Group {\n const edgeMap = buildEdgeMap(faces)\n const segs: Path[] = []\n // Soft-match: perimeter edges that have a near-parallel nearby perimeter edge\n const peri: EdgeRec[] = []\n for (const e of edgeMap.values()) {\n if (e.faceIds.length === 1) peri.push(e)\n }\n const used = new Set<number>()\n for (let i = 0; i < peri.length; i++) {\n if (used.has(i)) continue\n const a = peri[i]!\n for (let j = i + 1; j < peri.length; j++) {\n if (used.has(j)) continue\n const b = peri[j]!\n if (nearDuplicateEdge(a, b, 1e-3)) {\n segs.push(polyline([a.a, a.b]))\n segs.push(polyline([b.a, b.b]))\n used.add(i)\n used.add(j)\n break\n }\n }\n }\n return group(segs)\n}\n\nfunction nearDuplicateEdge(a: EdgeRec, b: EdgeRec, tol: number): boolean {\n const d1 = dist(a.a, b.a) + dist(a.b, b.b)\n const d2 = dist(a.a, b.b) + dist(a.b, b.a)\n const len = dist(a.a, a.b)\n return Math.min(d1, d2) < tol * 2 + len * 0.05 && Math.min(d1, d2) > 1e-9\n}\n\n/**\n * Clean inter-face gaps/overlaps/slivers toward a valid coverage by snapping\n * vertices within `maxGapWidth` and dissolving overlaps.\n */\nexport function fixBreaks(faces: Path[], maxGapWidth: number): Group {\n if (faces.length === 0) return group([])\n const tol = Math.max(maxGapWidth, 1e-9)\n const verts = unique(faces.flatMap((f) => ringOf(f)))\n const snapped = clusterSnap(verts, tol)\n const remap = (v: Vec2): Vec2 => {\n let best = snapped[0]!\n let bestD = Infinity\n for (const s of snapped) {\n const d = dist(v, s)\n if (d < bestD) {\n bestD = d\n best = s\n }\n }\n return { ...best }\n }\n const fixed = faces.map((f) => {\n const ring = unique(ringOf(f).map(remap))\n return ring.length >= 3 ? polygon(ring) : f\n })\n // Dissolve overlaps via pairwise union of intersecting faces\n try {\n return group(dissolveOverlaps(fixed))\n } catch {\n return group(fixed)\n }\n}\n\nfunction dissolveOverlaps(faces: Path[]): Path[] {\n // Simple O(n²): if two faces overlap significantly, union them\n let working = [...faces]\n let changed = true\n let guard = 0\n while (changed && guard++ < working.length) {\n changed = false\n outer: for (let i = 0; i < working.length; i++) {\n for (let j = i + 1; j < working.length; j++) {\n const a = working[i]!\n const b = working[j]!\n const u = union(a, b)\n const ua = u.paths.reduce((s, p) => s + area(p), 0)\n if (ua < area(a) + area(b) - 1e-6) {\n // overlap — replace with union pieces\n const next = working.filter((_, k) => k !== i && k !== j)\n next.push(...u.paths)\n working = next\n changed = true\n break outer\n }\n }\n }\n }\n return working\n}\n\n/**\n * Endpoint-only snap within `tolerance`, then optionally polygonize linework\n * into faces.\n */\nexport function fixBrokenFaces(\n coverage: Path[],\n tolerance: number,\n polygonise = true,\n): Group {\n const tol = Math.max(tolerance, 1e-9)\n // Collect endpoints of open paths + all polygon verts as anchors\n const anchors: Vec2[] = []\n const endpoints: { pathIdx: number; end: 0 | 1; v: Vec2 }[] = []\n\n coverage.forEach((p, pi) => {\n const ring = ringOf(p)\n if (ring.length === 0) return\n if (p.closed) {\n for (const v of ring) anchors.push(v)\n } else {\n endpoints.push({ pathIdx: pi, end: 0, v: ring[0]! })\n endpoints.push({\n pathIdx: pi,\n end: 1,\n v: ring[ring.length - 1]!,\n })\n }\n })\n\n const snapTargets = clusterSnap(\n [...anchors, ...endpoints.map((e) => e.v)],\n tol,\n )\n\n const snapTo = (v: Vec2, preferAnchor: boolean): Vec2 => {\n // Prefer anchors if in cluster\n let best = v\n let bestD = Infinity\n for (const s of snapTargets) {\n const d = dist(v, s)\n if (d <= tol && d < bestD) {\n bestD = d\n best = s\n }\n }\n if (preferAnchor) {\n for (const a of anchors) {\n if (dist(v, a) <= tol) return { ...a }\n }\n }\n return { ...best }\n }\n\n const snappedPaths = coverage.map((p) => {\n const ring = ringOf(p)\n if (ring.length === 0) return p\n if (p.closed) return p\n const next = ring.map((v) => ({ ...v }))\n next[0] = snapTo(ring[0]!, true)\n next[next.length - 1] = snapTo(ring[ring.length - 1]!, true)\n return polyline(next)\n })\n\n if (!polygonise) return group(snappedPaths)\n\n // Closed rings stay; try to close open paths whose ends meet\n const out: Path[] = []\n for (const p of snappedPaths) {\n const ring = ringOf(p)\n if (p.closed) {\n out.push(p)\n continue\n }\n if (ring.length >= 3 && same(ring[0]!, ring[ring.length - 1]!, tol * 2)) {\n out.push(polygon(ring.slice(0, -1)))\n } else if (ring.length >= 3 && dist(ring[0]!, ring[ring.length - 1]!) <= tol * 2) {\n out.push(polygon(ring))\n } else {\n out.push(p)\n }\n }\n return group(out)\n}\n\n/** Disconnected face islands sorted by face count descending. */\nexport function findIslands(faces: Path[]): Group[] {\n if (faces.length === 0) return []\n const adj = faceAdjacency(faces)\n const seen = new Set<number>()\n const islands: number[][] = []\n for (let i = 0; i < faces.length; i++) {\n if (seen.has(i)) continue\n const stack = [i]\n const members: number[] = []\n seen.add(i)\n while (stack.length) {\n const cur = stack.pop()!\n members.push(cur)\n for (const n of adj[cur]!) {\n if (!seen.has(n)) {\n seen.add(n)\n stack.push(n)\n }\n }\n }\n islands.push(members)\n }\n islands.sort((a, b) => b.length - a.length)\n return islands.map((m) => group(m.map((i) => faces[i]!)))\n}\n\n// ---------------------------------------------------------------------------\n// Sort helpers\n// ---------------------------------------------------------------------------\n\n/** Sort faces by centroid angle around their collective centroid. */\nexport function radialSortFaces(faces: Path[]): Path[] {\n if (faces.length === 0) return []\n const cs = faces.map((f) => centroid(f))\n const cx = cs.reduce((s, p) => s + p.x, 0) / cs.length\n const cy = cs.reduce((s, p) => s + p.y, 0) / cs.length\n return faces\n .map((f, i) => ({ f, a: Math.atan2(cs[i]!.y - cy, cs[i]!.x - cx) }))\n .sort((a, b) => a.a - b.a)\n .map((x) => x.f)\n}\n\n/** Sort faces by centroid x then y. */\nexport function centroidSortFaces(faces: Path[]): Path[] {\n return [...faces].sort((a, b) => {\n const ca = centroid(a)\n const cb = centroid(b)\n return ca.x === cb.x ? ca.y - cb.y : ca.x - cb.x\n })\n}\n\nexport const meshing = {\n extractInnerEdges,\n extractInnerVertices,\n findContainingFace,\n splitEdges,\n urquhartFaces,\n gabrielFaces,\n relativeNeighborFaces,\n spannerFaces,\n dualFaces,\n centroidQuadrangulation,\n edgeCollapseQuadrangulation,\n splitQuadrangulation,\n spiralQuadrangulation,\n matchingQuadrangulation,\n smoothMesh,\n subdivideMesh,\n simplifyMesh,\n stochasticMerge,\n areaMerge,\n radialSortFaces,\n centroidSortFaces,\n findBreaks,\n fixBreaks,\n fixBrokenFaces,\n findIslands,\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { group, path, polygon, polyline } from '../types/index.js'\nimport { compoundVoronoi } from '../voronoi/index.js'\nimport { stochasticMerge } from '../meshing/index.js'\nimport { buffer, smooth } from '../morphology/index.js'\nimport { subtractAll } from '../shapeBoolean/index.js'\n\n/** Regular n-gon approximating a circle. */\nexport function createCircle(\n cx: number,\n cy: number,\n radius: number,\n segments = 64,\n): Path {\n return createRegularPolygon(cx, cy, radius, Math.max(3, Math.floor(segments)))\n}\n\nexport function createRect(\n x: number,\n y: number,\n width: number,\n height: number,\n): Path {\n return polygon([\n { x, y },\n { x: x + width, y },\n { x: x + width, y: y + height },\n { x, y: y + height },\n ])\n}\n\nexport function createRegularPolygon(\n cx: number,\n cy: number,\n radius: number,\n sides: number,\n rotation = -Math.PI / 2,\n): Path {\n const n = Math.max(3, Math.floor(sides))\n const ring: Vec2[] = []\n for (let i = 0; i < n; i++) {\n const t = rotation + (i / n) * Math.PI * 2\n ring.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius })\n }\n return polygon(ring)\n}\n\n/** Annulus as exterior + hole. */\nexport function createRing(\n cx: number,\n cy: number,\n outerRadius: number,\n innerRadius: number,\n segments = 64,\n): Path {\n const outer = createCircle(cx, cy, outerRadius, segments).rings[0]!\n const inner = createCircle(cx, cy, Math.min(innerRadius, outerRadius), segments)\n .rings[0]!\n // hole winding opposite for Clipper\n return path([outer, [...inner].reverse()], true)\n}\n\n/** Open circular arc polyline from `startAngle` to `endAngle` (radians). */\nexport function createArc(\n cx: number,\n cy: number,\n radius: number,\n startAngle: number,\n endAngle: number,\n segments = 32,\n): Path {\n const n = Math.max(2, Math.floor(segments))\n const pts: Vec2[] = []\n for (let i = 0; i <= n; i++) {\n const t = startAngle + ((endAngle - startAngle) * i) / n\n pts.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius })\n }\n return polyline(pts)\n}\n\n/** Star polygon (outer/inner radius alternating). */\nexport function createStar(\n cx: number,\n cy: number,\n outerRadius: number,\n innerRadius: number,\n points = 5,\n rotation = -Math.PI / 2,\n): Path {\n const n = Math.max(3, Math.floor(points))\n const ring: Vec2[] = []\n for (let i = 0; i < n * 2; i++) {\n const r = i % 2 === 0 ? outerRadius : innerRadius\n const t = rotation + (i / (n * 2)) * Math.PI * 2\n ring.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r })\n }\n return polygon(ring)\n}\n\n/** Koch snowflake after `iterations` refinement (0 = equilateral triangle). */\nexport function createKochSnowflake(\n cx: number,\n cy: number,\n radius: number,\n iterations = 3,\n): Path {\n const tri = createRegularPolygon(cx, cy, radius, 3)\n let ring = tri.rings[0]!\n const iters = Math.max(0, Math.min(6, Math.floor(iterations)))\n for (let i = 0; i < iters; i++) {\n ring = kochRefine(ring)\n }\n return polygon(ring)\n}\n\n/**\n * Sponge-like porous structure (PGS `createSponge`).\n * Voronoi cells are randomly class-merged, smoothed (Chaikin), optionally\n * eroded for wall thickness, then subtracted from the bounding rectangle.\n * Returns a group (may be multi-component).\n */\nexport function createSponge(\n width: number,\n height: number,\n generators: number,\n thickness: number,\n smoothing: number,\n classes: number,\n seed = 1,\n): Group {\n const w = Math.max(width, 1e-9)\n const h = Math.max(height, 1e-9)\n const nGen = Math.max(6, Math.floor(generators))\n const nClasses = Math.max(1, Math.floor(classes))\n const smoothIters = Math.max(0, Math.floor(smoothing))\n const wall = Math.max(0, thickness)\n\n const sites = randomInBox(nGen, 0, 0, w, h, seed)\n const cells = compoundVoronoi(sites, {\n bounds: { minX: 0, minY: 0, maxX: w, maxY: h },\n })\n const merged = stochasticMerge(cells.paths, nClasses, seed + 1)\n\n const pores: Path[] = []\n for (let p of merged.paths) {\n if (smoothIters > 0) p = smooth(p, smoothIters)\n if (wall > 1e-12) {\n // Erode pore blobs so remaining walls are thicker\n pores.push(...buffer(p, -wall * 0.5).paths)\n } else {\n pores.push(p)\n }\n }\n\n const frame = createRect(0, 0, w, h)\n if (pores.length === 0) return group([frame])\n return subtractAll(frame, pores)\n}\n\nfunction randomInBox(\n count: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n seed: number,\n): Vec2[] {\n const rng = mulberry32(seed >>> 0)\n const out: Vec2[] = []\n for (let i = 0; i < count; i++) {\n out.push({\n x: minX + rng() * (maxX - minX),\n y: minY + rng() * (maxY - minY),\n })\n }\n return out\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\nfunction kochRefine(ring: Vec2[]): Vec2[] {\n const out: Vec2[] = []\n const n = ring.length\n for (let i = 0; i < n; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n const dx = b.x - a.x\n const dy = b.y - a.y\n const p1 = { x: a.x + dx / 3, y: a.y + dy / 3 }\n const p2 = { x: a.x + (2 * dx) / 3, y: a.y + (2 * dy) / 3 }\n // outward peak (left of directed edge in Y-down SVG coords = rotate +90 for CCW tri)\n const px = a.x + dx / 2 - dy * (Math.sqrt(3) / 6)\n const py = a.y + dy / 2 + dx * (Math.sqrt(3) / 6)\n out.push(a, p1, { x: px, y: py }, p2)\n }\n return out\n}\n\nexport const construction = {\n createCircle,\n createRect,\n createRegularPolygon,\n createRing,\n createArc,\n createStar,\n createKochSnowflake,\n createSponge,\n}\n"]}
@@ -1,7 +1,6 @@
1
- import { V as Vec2 } from '../vec2-CWyXN1Wj.js';
2
- import { P as Path } from '../path-0RjhYjJs.js';
3
- import { G as Group } from '../group-BNjVVYSQ.js';
4
- import { S as Segment } from '../segment-BKuIFMKD.js';
1
+ import { P as Path, V as Vec2 } from '../path-Boeshk9L.js';
2
+ import { G as Group } from '../group-YVmSJuGo.js';
3
+ import { S as Segment } from '../segment-CpLILSd7.js';
5
4
 
6
5
  /**
7
6
  * Chordal Axis Transform: join midpoints of interior chords / centroids of