pattapatta 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -825,8 +825,8 @@ function clipSegmentToPath(seg, clip) {
825
825
  const h0 = hits[i];
826
826
  const h1 = hits[i + 1];
827
827
  if (h1.t - h0.t < 1e-9) continue;
828
- const mid3 = vec2((h0.p.x + h1.p.x) / 2, (h0.p.y + h1.p.y) / 2);
829
- if (containsPoint(clip, mid3)) {
828
+ const mid5 = vec2((h0.p.x + h1.p.x) / 2, (h0.p.y + h1.p.y) / 2);
829
+ if (containsPoint(clip, mid5)) {
830
830
  out.push(segment(h0.p, h1.p));
831
831
  }
832
832
  }
@@ -1569,10 +1569,10 @@ function segmentsOnExterior(p) {
1569
1569
  function slice(p, a, b) {
1570
1570
  const dx = b.x - a.x;
1571
1571
  const dy = b.y - a.y;
1572
- const len = Math.hypot(dx, dy);
1573
- if (len < 1e-12) return group([p]);
1574
- const nx = -dy / len;
1575
- const ny = dx / len;
1572
+ const len2 = Math.hypot(dx, dy);
1573
+ if (len2 < 1e-12) return group([p]);
1574
+ const nx = -dy / len2;
1575
+ const ny = dx / len2;
1576
1576
  const bb = bounds(p);
1577
1577
  const span = Math.hypot(bb.maxX - bb.minX, bb.maxY - bb.minY) * 4 + 10;
1578
1578
  const left = halfPlanePoly(a, nx, ny, span);
@@ -1601,9 +1601,9 @@ function intersectionPoints(a, b) {
1601
1601
  for (const sb of segsB) {
1602
1602
  const hit = segmentIntersection2(sa.a, sa.b, sb.a, sb.b);
1603
1603
  if (!hit) continue;
1604
- const key = `${hit.x.toFixed(9)},${hit.y.toFixed(9)}`;
1605
- if (seen.has(key)) continue;
1606
- seen.add(key);
1604
+ const key4 = `${hit.x.toFixed(9)},${hit.y.toFixed(9)}`;
1605
+ if (seen.has(key4)) continue;
1606
+ seen.add(key4);
1607
1607
  out.push(hit);
1608
1608
  }
1609
1609
  }
@@ -1762,76 +1762,6 @@ var conversion = {
1762
1762
  group
1763
1763
  };
1764
1764
 
1765
- // src/contour/index.ts
1766
- function offsetCurvesOutward(p, distance) {
1767
- return buffer(p, Math.abs(distance));
1768
- }
1769
- function offsetCurvesInward(p, distance) {
1770
- return buffer(p, -Math.abs(distance));
1771
- }
1772
- var contour = {
1773
- offsetCurvesOutward,
1774
- offsetCurvesInward
1775
- };
1776
-
1777
- // src/hull/index.ts
1778
- function convexHull(points) {
1779
- const pts = uniquePoints(points);
1780
- if (pts.length < 3) return polygon(pts);
1781
- pts.sort((a, b) => a.x === b.x ? a.y - b.y : a.x - b.x);
1782
- const lower = [];
1783
- for (const p of pts) {
1784
- while (lower.length >= 2 && cross2(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
1785
- lower.pop();
1786
- }
1787
- lower.push(p);
1788
- }
1789
- const upper = [];
1790
- for (let i = pts.length - 1; i >= 0; i--) {
1791
- const p = pts[i];
1792
- while (upper.length >= 2 && cross2(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
1793
- upper.pop();
1794
- }
1795
- upper.push(p);
1796
- }
1797
- lower.pop();
1798
- upper.pop();
1799
- return polygon([...lower, ...upper]);
1800
- }
1801
- function convexHullPath(p) {
1802
- const pts = [];
1803
- for (const ring2 of p.rings) pts.push(...ring2);
1804
- return convexHull(pts);
1805
- }
1806
- function boundingBox(p) {
1807
- const b = bounds(p);
1808
- return polygon([
1809
- { x: b.minX, y: b.minY },
1810
- { x: b.maxX, y: b.minY },
1811
- { x: b.maxX, y: b.maxY },
1812
- { x: b.minX, y: b.maxY }
1813
- ]);
1814
- }
1815
- function cross2(o, a, b) {
1816
- return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
1817
- }
1818
- function uniquePoints(points) {
1819
- const seen = /* @__PURE__ */ new Set();
1820
- const out = [];
1821
- for (const p of points) {
1822
- const key = `${p.x},${p.y}`;
1823
- if (seen.has(key)) continue;
1824
- seen.add(key);
1825
- out.push({ x: p.x, y: p.y });
1826
- }
1827
- return out;
1828
- }
1829
- var hull = {
1830
- convexHull,
1831
- convexHullPath,
1832
- boundingBox
1833
- };
1834
-
1835
1765
  // src/pointSet/index.ts
1836
1766
  function random(count, minX, minY, maxX, maxY, seed = 1) {
1837
1767
  const rng = mulberry323(seed >>> 0);
@@ -2121,9 +2051,9 @@ function longestEdgeMidpoint(a, b, c) {
2121
2051
  let best = edges[0];
2122
2052
  let bestLen = -1;
2123
2053
  for (const e of edges) {
2124
- const len = Math.hypot(e[0].x - e[1].x, e[0].y - e[1].y);
2125
- if (len > bestLen) {
2126
- bestLen = len;
2054
+ const len2 = Math.hypot(e[0].x - e[1].x, e[0].y - e[1].y);
2055
+ if (len2 > bestLen) {
2056
+ bestLen = len2;
2127
2057
  best = e;
2128
2058
  }
2129
2059
  }
@@ -2138,6 +2068,205 @@ var triangulation = {
2138
2068
  refine
2139
2069
  };
2140
2070
 
2071
+ // src/contour/dissolve.ts
2072
+ function key(v, digits = 9) {
2073
+ return `${v.x.toFixed(digits)},${v.y.toFixed(digits)}`;
2074
+ }
2075
+ function same(a, b, eps = 1e-9) {
2076
+ return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps;
2077
+ }
2078
+ function dissolveSegments(segments) {
2079
+ const segs = segments.filter((s) => !same(s.a, s.b));
2080
+ if (segs.length === 0) return group([]);
2081
+ const adj = /* @__PURE__ */ new Map();
2082
+ const pts = /* @__PURE__ */ new Map();
2083
+ const add = (a, b, segIdx) => {
2084
+ const ka = key(a);
2085
+ const kb = key(b);
2086
+ pts.set(ka, a);
2087
+ pts.set(kb, b);
2088
+ if (!adj.has(ka)) adj.set(ka, []);
2089
+ if (!adj.has(kb)) adj.set(kb, []);
2090
+ adj.get(ka).push({ segIdx, other: kb });
2091
+ adj.get(kb).push({ segIdx, other: ka });
2092
+ };
2093
+ for (let i = 0; i < segs.length; i++) {
2094
+ add(segs[i].a, segs[i].b, i);
2095
+ }
2096
+ const used = /* @__PURE__ */ new Set();
2097
+ const paths = [];
2098
+ const unusedDegree = (k) => (adj.get(k) ?? []).filter((e) => !used.has(e.segIdx)).length;
2099
+ const nextUnused = (k) => (adj.get(k) ?? []).find((e) => !used.has(e.segIdx));
2100
+ const starts = [...adj.keys()].sort(
2101
+ (a, b) => unusedDegree(a) - unusedDegree(b)
2102
+ );
2103
+ for (const start of starts) {
2104
+ let link = nextUnused(start);
2105
+ while (link) {
2106
+ const chain = [pts.get(start)];
2107
+ used.add(link.segIdx);
2108
+ let next = link.other;
2109
+ chain.push(pts.get(next));
2110
+ while (unusedDegree(next) === 1) {
2111
+ const e = nextUnused(next);
2112
+ if (!e) break;
2113
+ used.add(e.segIdx);
2114
+ next = e.other;
2115
+ chain.push(pts.get(next));
2116
+ if (next === start) break;
2117
+ }
2118
+ if (chain.length >= 2) paths.push(polyline(chain));
2119
+ link = nextUnused(start);
2120
+ }
2121
+ }
2122
+ for (let i = 0; i < segs.length; i++) {
2123
+ if (used.has(i)) continue;
2124
+ const s = segs[i];
2125
+ paths.push(polyline([s.a, s.b]));
2126
+ }
2127
+ return group(paths);
2128
+ }
2129
+
2130
+ // src/contour/chordalAxis.ts
2131
+ function mid(a, b) {
2132
+ return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
2133
+ }
2134
+ function len(a, b) {
2135
+ return Math.hypot(b.x - a.x, b.y - a.y);
2136
+ }
2137
+ function edgeKey(a, b) {
2138
+ const ka = `${a.x.toFixed(8)},${a.y.toFixed(8)}`;
2139
+ const kb = `${b.x.toFixed(8)},${b.y.toFixed(8)}`;
2140
+ return ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`;
2141
+ }
2142
+ function chordalAxis(shape) {
2143
+ if (!shape.closed || shape.rings.length === 0) return dissolveSegments([]);
2144
+ const b = boundsSpan(shape);
2145
+ const densified = densify(shape, Math.max(b / 40, 1e-3));
2146
+ const tris = delaunayTriangulation(densified);
2147
+ if (tris.length === 0) return dissolveSegments([]);
2148
+ const edgeCount = /* @__PURE__ */ new Map();
2149
+ const triEdges = [];
2150
+ for (const t of tris) {
2151
+ const r = t.rings[0];
2152
+ if (!r || r.length < 3) continue;
2153
+ const a = r[0];
2154
+ const b0 = r[1];
2155
+ const c = r[2];
2156
+ triEdges.push([a, b0, c]);
2157
+ for (const [u, v] of [
2158
+ [a, b0],
2159
+ [b0, c],
2160
+ [c, a]
2161
+ ]) {
2162
+ const k = edgeKey(u, v);
2163
+ edgeCount.set(k, (edgeCount.get(k) ?? 0) + 1);
2164
+ }
2165
+ }
2166
+ const isBoundary = (u, v) => (edgeCount.get(edgeKey(u, v)) ?? 0) <= 1;
2167
+ const segs = [];
2168
+ for (const [a, b0, c] of triEdges) {
2169
+ const edges = [
2170
+ [a, b0],
2171
+ [b0, c],
2172
+ [c, a]
2173
+ ];
2174
+ const interior = edges.filter(([u, v]) => !isBoundary(u, v));
2175
+ const degree = interior.length;
2176
+ if (degree === 1) {
2177
+ const [u, v] = interior[0];
2178
+ const cen = centroid({ rings: [[a, b0, c]]});
2179
+ segs.push(segment(cen, mid(u, v)));
2180
+ } else if (degree === 2) {
2181
+ const m0 = mid(interior[0][0], interior[0][1]);
2182
+ const m1 = mid(interior[1][0], interior[1][1]);
2183
+ segs.push(segment(m0, m1));
2184
+ } else if (degree === 3) {
2185
+ const ranked = edges.map(([u, v]) => ({ u, v, l: len(u, v) })).sort((x, y) => y.l - x.l);
2186
+ const longest = ranked[0];
2187
+ const shortA = ranked[1];
2188
+ const shortB = ranked[2];
2189
+ const mL = mid(longest.u, longest.v);
2190
+ segs.push(segment(mid(shortA.u, shortA.v), mL));
2191
+ segs.push(segment(mid(shortB.u, shortB.v), mL));
2192
+ }
2193
+ }
2194
+ return dissolveSegments(segs);
2195
+ }
2196
+ function boundsSpan(p) {
2197
+ let minX = Infinity;
2198
+ let minY = Infinity;
2199
+ let maxX = -Infinity;
2200
+ let maxY = -Infinity;
2201
+ for (const ring2 of p.rings) {
2202
+ for (const v of ring2) {
2203
+ minX = Math.min(minX, v.x);
2204
+ minY = Math.min(minY, v.y);
2205
+ maxX = Math.max(maxX, v.x);
2206
+ maxY = Math.max(maxY, v.y);
2207
+ }
2208
+ }
2209
+ return Math.max(maxX - minX, maxY - minY, 1);
2210
+ }
2211
+
2212
+ // src/hull/index.ts
2213
+ function convexHull(points) {
2214
+ const pts = uniquePoints(points);
2215
+ if (pts.length < 3) return polygon(pts);
2216
+ pts.sort((a, b) => a.x === b.x ? a.y - b.y : a.x - b.x);
2217
+ const lower = [];
2218
+ for (const p of pts) {
2219
+ while (lower.length >= 2 && cross2(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
2220
+ lower.pop();
2221
+ }
2222
+ lower.push(p);
2223
+ }
2224
+ const upper = [];
2225
+ for (let i = pts.length - 1; i >= 0; i--) {
2226
+ const p = pts[i];
2227
+ while (upper.length >= 2 && cross2(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
2228
+ upper.pop();
2229
+ }
2230
+ upper.push(p);
2231
+ }
2232
+ lower.pop();
2233
+ upper.pop();
2234
+ return polygon([...lower, ...upper]);
2235
+ }
2236
+ function convexHullPath(p) {
2237
+ const pts = [];
2238
+ for (const ring2 of p.rings) pts.push(...ring2);
2239
+ return convexHull(pts);
2240
+ }
2241
+ function boundingBox(p) {
2242
+ const b = bounds(p);
2243
+ return polygon([
2244
+ { x: b.minX, y: b.minY },
2245
+ { x: b.maxX, y: b.minY },
2246
+ { x: b.maxX, y: b.maxY },
2247
+ { x: b.minX, y: b.maxY }
2248
+ ]);
2249
+ }
2250
+ function cross2(o, a, b) {
2251
+ return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
2252
+ }
2253
+ function uniquePoints(points) {
2254
+ const seen = /* @__PURE__ */ new Set();
2255
+ const out = [];
2256
+ for (const p of points) {
2257
+ const key4 = `${p.x},${p.y}`;
2258
+ if (seen.has(key4)) continue;
2259
+ seen.add(key4);
2260
+ out.push({ x: p.x, y: p.y });
2261
+ }
2262
+ return out;
2263
+ }
2264
+ var hull = {
2265
+ convexHull,
2266
+ convexHullPath,
2267
+ boundingBox
2268
+ };
2269
+
2141
2270
  // src/optimisation/index.ts
2142
2271
  function envelope(p) {
2143
2272
  return boundingBox(p);
@@ -2170,12 +2299,12 @@ function maximumInscribedAARectangle(p) {
2170
2299
  let hi = Math.min(w, h) / 2;
2171
2300
  let best = hi;
2172
2301
  for (let i = 0; i < 40; i++) {
2173
- const mid3 = (lo + hi) / 2;
2174
- if (fits(mid3)) {
2175
- best = mid3;
2176
- hi = mid3;
2302
+ const mid5 = (lo + hi) / 2;
2303
+ if (fits(mid5)) {
2304
+ best = mid5;
2305
+ hi = mid5;
2177
2306
  } else {
2178
- lo = mid3;
2307
+ lo = mid5;
2179
2308
  }
2180
2309
  }
2181
2310
  const m = best;
@@ -2320,6 +2449,917 @@ var optimisation = {
2320
2449
  farthestPointPair,
2321
2450
  minimumBoundingCircle
2322
2451
  };
2452
+
2453
+ // src/contour/boundaryDistance.ts
2454
+ function distanceToBoundary2(path3, p) {
2455
+ let best = Infinity;
2456
+ for (const ring2 of path3.rings) {
2457
+ if (ring2.length < 2) continue;
2458
+ const n = ring2.length;
2459
+ const closed = path3.closed;
2460
+ const limit = closed ? n : n - 1;
2461
+ for (let i = 0; i < limit; i++) {
2462
+ const a = ring2[i];
2463
+ const b = ring2[(i + 1) % n];
2464
+ best = Math.min(best, distPointSeg(p, a, b));
2465
+ }
2466
+ }
2467
+ return best;
2468
+ }
2469
+ function sampleBoundary(path3, maxSegLen) {
2470
+ const pts = [];
2471
+ const lim = Math.max(maxSegLen, 1e-9);
2472
+ for (const ring2 of path3.rings) {
2473
+ if (ring2.length < 2) continue;
2474
+ const n = ring2.length;
2475
+ const limit = path3.closed ? n : n - 1;
2476
+ for (let i = 0; i < limit; i++) {
2477
+ const a = ring2[i];
2478
+ const b = ring2[(i + 1) % n];
2479
+ const len2 = Math.hypot(b.x - a.x, b.y - a.y);
2480
+ const steps = Math.max(1, Math.ceil(len2 / lim));
2481
+ for (let s = 0; s < steps; s++) {
2482
+ const t = s / steps;
2483
+ pts.push({ x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t });
2484
+ }
2485
+ }
2486
+ }
2487
+ return pts;
2488
+ }
2489
+ function distPointSeg(p, a, b) {
2490
+ const dx = b.x - a.x;
2491
+ const dy = b.y - a.y;
2492
+ const len2 = dx * dx + dy * dy;
2493
+ if (len2 < 1e-18) return Math.hypot(p.x - a.x, p.y - a.y);
2494
+ let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
2495
+ t = Math.max(0, Math.min(1, t));
2496
+ return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
2497
+ }
2498
+
2499
+ // src/contour/medialAxis.ts
2500
+ function medialAxis(shape, axialThreshold = 0, distanceThreshold = 0, areaThreshold = 0) {
2501
+ const g = buildMedialAxisGraph(shape);
2502
+ if (!g) return dissolveSegments([]);
2503
+ const kept = pruneEdges(g, axialThreshold, distanceThreshold, areaThreshold);
2504
+ const segs = kept.map((e) => {
2505
+ const a = g.nodes[e.head].position;
2506
+ const b = g.nodes[e.tail].position;
2507
+ return segment(a, b);
2508
+ });
2509
+ return dissolveSegments(segs);
2510
+ }
2511
+ function buildMedialAxisGraph(shape) {
2512
+ if (!shape.closed || shape.rings.length === 0) return null;
2513
+ const b = bounds(shape);
2514
+ const span = Math.max(b.maxX - b.minX, b.maxY - b.minY, 1);
2515
+ const densified = densify(shape, Math.max(span / 60, 1e-3));
2516
+ const samples = sampleBoundary(densified, Math.max(span / 80, 1e-3));
2517
+ if (samples.length < 3) return null;
2518
+ const uniq = [];
2519
+ const seen = /* @__PURE__ */ new Set();
2520
+ for (const p of samples) {
2521
+ const k = `${p.x.toFixed(6)},${p.y.toFixed(6)}`;
2522
+ if (seen.has(k)) continue;
2523
+ seen.add(k);
2524
+ uniq.push(p);
2525
+ }
2526
+ if (uniq.length < 3) return null;
2527
+ const delaunay = Delaunay.from(
2528
+ uniq,
2529
+ (d) => d.x,
2530
+ (d) => d.y
2531
+ );
2532
+ const pad = span * 0.5;
2533
+ const voronoi2 = delaunay.voronoi([
2534
+ b.minX - pad,
2535
+ b.minY - pad,
2536
+ b.maxX + pad,
2537
+ b.maxY + pad
2538
+ ]);
2539
+ const raw = [];
2540
+ const edgeSeen = /* @__PURE__ */ new Set();
2541
+ for (let i = 0; i < uniq.length; i++) {
2542
+ const poly = voronoi2.cellPolygon(i);
2543
+ if (!poly || poly.length < 2) continue;
2544
+ for (let j = 0; j < poly.length - 1; j++) {
2545
+ const p0 = poly[j];
2546
+ const p1 = poly[j + 1];
2547
+ const a = { x: p0[0], y: p0[1] };
2548
+ const bb = { x: p1[0], y: p1[1] };
2549
+ if (!containsPoint(shape, a) || !containsPoint(shape, bb)) continue;
2550
+ const ek = edgeKey2(a, bb);
2551
+ if (edgeSeen.has(ek)) continue;
2552
+ edgeSeen.add(ek);
2553
+ const mid5 = { x: (a.x + bb.x) / 2, y: (a.y + bb.y) / 2 };
2554
+ const site = uniq[i];
2555
+ const radius = Math.hypot(mid5.x - site.x, mid5.y - site.y);
2556
+ raw.push({ a, b: bb, radius });
2557
+ }
2558
+ }
2559
+ if (raw.length === 0) return null;
2560
+ const nodeIndex = /* @__PURE__ */ new Map();
2561
+ const nodes = [];
2562
+ const ensure = (p, radius) => {
2563
+ const k = key2(p);
2564
+ let id = nodeIndex.get(k);
2565
+ if (id !== void 0) {
2566
+ nodes[id].radius = Math.max(nodes[id].radius, radius);
2567
+ return id;
2568
+ }
2569
+ id = nodes.length;
2570
+ nodeIndex.set(k, id);
2571
+ nodes.push({
2572
+ id,
2573
+ position: p,
2574
+ radius,
2575
+ parent: null,
2576
+ children: [],
2577
+ rootDist: 0,
2578
+ featureArea: Math.PI * radius * radius
2579
+ });
2580
+ return id;
2581
+ };
2582
+ const undirected = [];
2583
+ for (const e of raw) {
2584
+ const midR = e.radius;
2585
+ const u = ensure(e.a, midR);
2586
+ const v = ensure(e.b, midR);
2587
+ if (u === v) continue;
2588
+ const length = Math.hypot(
2589
+ nodes[u].position.x - nodes[v].position.x,
2590
+ nodes[u].position.y - nodes[v].position.y
2591
+ );
2592
+ const axial = length > 1e-12 ? Math.abs(nodes[u].radius - nodes[v].radius) / length : 0;
2593
+ undirected.push({ u, v, axial, length });
2594
+ }
2595
+ const mic = maximumInscribedCircle(shape, Math.max(span * 0.01, 0.1));
2596
+ const rootPos = mic ? { x: mic.x, y: mic.y } : {
2597
+ x: (b.minX + b.maxX) / 2,
2598
+ y: (b.minY + b.maxY) / 2
2599
+ };
2600
+ let root = 0;
2601
+ let bestD = Infinity;
2602
+ for (const n of nodes) {
2603
+ const d = Math.hypot(n.position.x - rootPos.x, n.position.y - rootPos.y);
2604
+ if (d < bestD) {
2605
+ bestD = d;
2606
+ root = n.id;
2607
+ }
2608
+ }
2609
+ const adj = /* @__PURE__ */ new Map();
2610
+ for (const e of undirected) {
2611
+ if (!adj.has(e.u)) adj.set(e.u, []);
2612
+ if (!adj.has(e.v)) adj.set(e.v, []);
2613
+ adj.get(e.u).push({ to: e.v, axial: e.axial, length: e.length });
2614
+ adj.get(e.v).push({ to: e.u, axial: e.axial, length: e.length });
2615
+ }
2616
+ const edges = [];
2617
+ const visited = /* @__PURE__ */ new Set([root]);
2618
+ const queue = [root];
2619
+ nodes[root].rootDist = 0;
2620
+ while (queue.length) {
2621
+ const u = queue.shift();
2622
+ for (const link of adj.get(u) ?? []) {
2623
+ if (visited.has(link.to)) continue;
2624
+ visited.add(link.to);
2625
+ nodes[link.to].parent = u;
2626
+ nodes[u].children.push(link.to);
2627
+ nodes[link.to].rootDist = nodes[u].rootDist + link.length;
2628
+ edges.push({
2629
+ head: u,
2630
+ tail: link.to,
2631
+ axial: link.axial,
2632
+ length: link.length
2633
+ });
2634
+ queue.push(link.to);
2635
+ }
2636
+ }
2637
+ const post = [];
2638
+ const stack = [root];
2639
+ const seenN = /* @__PURE__ */ new Set();
2640
+ while (stack.length) {
2641
+ const u = stack.pop();
2642
+ if (seenN.has(u)) {
2643
+ post.push(u);
2644
+ continue;
2645
+ }
2646
+ seenN.add(u);
2647
+ stack.push(u);
2648
+ for (const c of nodes[u].children) stack.push(c);
2649
+ }
2650
+ for (const u of post) {
2651
+ let area2 = nodes[u].featureArea;
2652
+ for (const c of nodes[u].children) area2 += nodes[c].featureArea;
2653
+ nodes[u].featureArea = area2;
2654
+ }
2655
+ return { nodes, edges, root };
2656
+ }
2657
+ function pruneEdges(g, axialT, distT, areaT) {
2658
+ const maxDist = Math.max(...g.nodes.map((n) => n.rootDist), 1e-9);
2659
+ const maxArea2 = Math.max(...g.nodes.map((n) => n.featureArea), 1e-9);
2660
+ const maxAxial = Math.max(...g.edges.map((e) => e.axial), 1e-9);
2661
+ const aT = clamp01(axialT);
2662
+ const dT = clamp01(distT);
2663
+ const arT = clamp01(areaT);
2664
+ return g.edges.filter((e) => {
2665
+ const tail = g.nodes[e.tail];
2666
+ if (aT > 0 && e.axial / maxAxial < aT * 0.15 && aT > 0.05) {
2667
+ if (tail.children.length === 0 && e.axial / maxAxial < aT) return false;
2668
+ }
2669
+ if (dT > 0) {
2670
+ const nd = tail.rootDist / maxDist;
2671
+ if (tail.children.length === 0 && nd > 1 - dT && dT > 0) ;
2672
+ if (tail.children.length === 0 && 1 - nd < dT * 0.5 && dT >= 0.5) {
2673
+ return false;
2674
+ }
2675
+ if (tail.children.length === 0 && nd * dT > 0.85) return false;
2676
+ }
2677
+ if (arT > 0) {
2678
+ const na = tail.featureArea / maxArea2;
2679
+ if (na < arT) return false;
2680
+ }
2681
+ if (aT > 0 && e.axial / maxAxial > 1 - aT * 0.5 && tail.children.length === 0) {
2682
+ return false;
2683
+ }
2684
+ return true;
2685
+ });
2686
+ }
2687
+ function centerLine(shape, straightnessWeighting = 0.7, smoothing = 50) {
2688
+ const g = buildMedialAxisGraph(shape);
2689
+ if (!g || g.nodes.length < 2) {
2690
+ return polyline([]);
2691
+ }
2692
+ const root = g.nodes[g.root];
2693
+ const childRoots = root.children;
2694
+ const leavesOf = (start) => {
2695
+ const out = [];
2696
+ const stack = [start];
2697
+ while (stack.length) {
2698
+ const u = stack.pop();
2699
+ const n = g.nodes[u];
2700
+ if (n.children.length === 0) out.push(n);
2701
+ else stack.push(...n.children);
2702
+ }
2703
+ return out;
2704
+ };
2705
+ let bestPath = [];
2706
+ if (childRoots.length <= 1) {
2707
+ bestPath = longestPathNodes(g);
2708
+ } else if (childRoots.length === 2) {
2709
+ bestPath = pathBetweenLeaves(
2710
+ g,
2711
+ leavesOf(childRoots[0]),
2712
+ leavesOf(childRoots[1]),
2713
+ root,
2714
+ straightnessWeighting
2715
+ );
2716
+ } else {
2717
+ const groups = childRoots.map(leavesOf);
2718
+ let bestW = -Infinity;
2719
+ for (let i = 0; i < groups.length; i++) {
2720
+ for (let j = i + 1; j < groups.length; j++) {
2721
+ const { path: p, weight } = bestLeafPair(
2722
+ g,
2723
+ groups[i],
2724
+ groups[j],
2725
+ root,
2726
+ straightnessWeighting
2727
+ );
2728
+ if (weight > bestW) {
2729
+ bestW = weight;
2730
+ bestPath = p;
2731
+ }
2732
+ }
2733
+ }
2734
+ }
2735
+ if (bestPath.length < 2) bestPath = longestPathNodes(g);
2736
+ const pts = bestPath.map((id) => g.nodes[id].position);
2737
+ return polyline(gaussianSmooth(pts, smoothing));
2738
+ }
2739
+ function bestLeafPair(g, a, b, root, straightness) {
2740
+ let bestW = -Infinity;
2741
+ let best = [];
2742
+ for (const d1 of a) {
2743
+ for (const d2 of b) {
2744
+ const angle = angleBetween(d1.position, root.position, d2.position);
2745
+ const aw = Math.pow(1 + angle, straightness);
2746
+ const weight = (d1.rootDist + d2.rootDist) * Math.max(aw - 1, 1);
2747
+ if (weight > bestW) {
2748
+ bestW = weight;
2749
+ best = [...pathToRoot(g, d1.id), ...pathToRoot(g, d2.id).reverse().slice(1)];
2750
+ }
2751
+ }
2752
+ }
2753
+ return { path: best, weight: bestW };
2754
+ }
2755
+ function pathBetweenLeaves(g, a, b, root, straightness) {
2756
+ return bestLeafPair(g, a, b, root, straightness).path;
2757
+ }
2758
+ function pathToRoot(g, id) {
2759
+ const path3 = [id];
2760
+ let cur = id;
2761
+ while (g.nodes[cur].parent !== null) {
2762
+ cur = g.nodes[cur].parent;
2763
+ path3.push(cur);
2764
+ }
2765
+ return path3;
2766
+ }
2767
+ function longestPathNodes(g) {
2768
+ const leaves = g.nodes.filter((n) => n.children.length === 0 && n.id !== g.root);
2769
+ if (leaves.length === 0) return g.nodes.map((n) => n.id);
2770
+ let farthest = leaves[0];
2771
+ let best = -1;
2772
+ for (const L of leaves) {
2773
+ if (L.rootDist > best) {
2774
+ best = L.rootDist;
2775
+ farthest = L;
2776
+ }
2777
+ }
2778
+ let bestPath = pathToRoot(g, farthest.id);
2779
+ let bestLen = farthest.rootDist;
2780
+ for (const L of leaves) {
2781
+ if (L.id === farthest.id) continue;
2782
+ const p = [
2783
+ ...pathToRoot(g, farthest.id),
2784
+ ...pathToRoot(g, L.id).reverse().slice(1)
2785
+ ];
2786
+ const len2 = farthest.rootDist + L.rootDist;
2787
+ if (len2 > bestLen) {
2788
+ bestLen = len2;
2789
+ bestPath = p;
2790
+ }
2791
+ }
2792
+ return bestPath;
2793
+ }
2794
+ function angleBetween(a, apex, b) {
2795
+ const ax = a.x - apex.x;
2796
+ const ay = a.y - apex.y;
2797
+ const bx = b.x - apex.x;
2798
+ const by = b.y - apex.y;
2799
+ const la = Math.hypot(ax, ay) || 1;
2800
+ const lb = Math.hypot(bx, by) || 1;
2801
+ const cos = Math.max(-1, Math.min(1, (ax * bx + ay * by) / (la * lb)));
2802
+ return Math.acos(cos);
2803
+ }
2804
+ function gaussianSmooth(pts, sigma) {
2805
+ if (pts.length < 3 || sigma <= 0) return pts;
2806
+ const radius = Math.max(1, Math.round(Math.min(pts.length / 4, sigma / 10)));
2807
+ const out = [];
2808
+ for (let i = 0; i < pts.length; i++) {
2809
+ let sx = 0;
2810
+ let sy = 0;
2811
+ let w = 0;
2812
+ for (let k = -radius; k <= radius; k++) {
2813
+ const j = Math.max(0, Math.min(pts.length - 1, i + k));
2814
+ const wk = Math.exp(-(k * k) / (2 * (radius * 0.5) ** 2 + 1e-9));
2815
+ sx += pts[j].x * wk;
2816
+ sy += pts[j].y * wk;
2817
+ w += wk;
2818
+ }
2819
+ out.push({ x: sx / w, y: sy / w });
2820
+ }
2821
+ out[0] = pts[0];
2822
+ out[out.length - 1] = pts[pts.length - 1];
2823
+ return out;
2824
+ }
2825
+ function key2(p) {
2826
+ return `${p.x.toFixed(7)},${p.y.toFixed(7)}`;
2827
+ }
2828
+ function edgeKey2(a, b) {
2829
+ const ka = key2(a);
2830
+ const kb = key2(b);
2831
+ return ka < kb ? `${ka}|${kb}` : `${kb}|${ka}`;
2832
+ }
2833
+ function clamp01(v) {
2834
+ return Math.max(0, Math.min(1, v));
2835
+ }
2836
+
2837
+ // src/contour/straightSkeleton.ts
2838
+ function straightSkeleton(shape) {
2839
+ const parts = straightSkeletonParts(shape);
2840
+ return group([
2841
+ ...parts.faces.paths,
2842
+ ...parts.branches.paths,
2843
+ ...parts.bones.paths
2844
+ ]);
2845
+ }
2846
+ function straightSkeletonParts(shape) {
2847
+ const empty = {
2848
+ faces: group([]),
2849
+ branches: group([]),
2850
+ bones: group([])
2851
+ };
2852
+ if (!shape.closed || !shape.rings[0] || shape.rings[0].length < 3) {
2853
+ return empty;
2854
+ }
2855
+ const b = bounds(shape);
2856
+ const span = Math.max(b.maxX - b.minX, b.maxY - b.minY, 1);
2857
+ const delta = Math.max(span / 40, 0.5);
2858
+ const densified = densify(shape, Math.max(span / 40, 1e-3));
2859
+ const levels = [densified];
2860
+ let current = densified;
2861
+ for (let i = 0; i < 40; i++) {
2862
+ const next = buffer(current, -delta);
2863
+ if (next.paths.length === 0) break;
2864
+ let best = next.paths[0];
2865
+ let bestA = -1;
2866
+ for (const p of next.paths) {
2867
+ const bb = bounds(p);
2868
+ const a = (bb.maxX - bb.minX) * (bb.maxY - bb.minY);
2869
+ if (a > bestA) {
2870
+ bestA = a;
2871
+ best = p;
2872
+ }
2873
+ }
2874
+ if (!best.rings[0] || best.rings[0].length < 3) break;
2875
+ levels.push(best);
2876
+ current = best;
2877
+ }
2878
+ const boneSegs = [];
2879
+ for (let i = 0; i < levels.length - 1; i++) {
2880
+ const a = levels[i];
2881
+ const bb = levels[i + 1];
2882
+ const ca = centroid(a);
2883
+ const cb = centroid(bb);
2884
+ if (containsPoint(shape, mid2(ca, cb))) {
2885
+ boneSegs.push(segment(ca, cb));
2886
+ }
2887
+ for (const ring2 of a.rings) {
2888
+ for (const p of ring2) {
2889
+ const q = nearestOnPath(bb, p);
2890
+ if (q && containsPoint(shape, mid2(p, q))) {
2891
+ boneSegs.push(segment(p, q));
2892
+ }
2893
+ }
2894
+ }
2895
+ }
2896
+ const branchSegs = [];
2897
+ const outer = densified.rings[0];
2898
+ const target = levels[1] ?? levels[0];
2899
+ for (let i = 0; i < outer.length; i++) {
2900
+ const a = outer[i];
2901
+ const bb = outer[(i + 1) % outer.length];
2902
+ const m = mid2(a, bb);
2903
+ const q = nearestOnPath(target, m);
2904
+ if (q) branchSegs.push(segment(m, q));
2905
+ }
2906
+ const faces = [];
2907
+ if (levels.length >= 2) {
2908
+ const inner = levels[1].rings[0] ?? [];
2909
+ for (let i = 0; i < outer.length; i++) {
2910
+ const a = outer[i];
2911
+ const bb = outer[(i + 1) % outer.length];
2912
+ const ia = nearestOnRing(inner, a);
2913
+ const ib = nearestOnRing(inner, bb);
2914
+ if (ia && ib) {
2915
+ const face = [a, bb, ib, ia];
2916
+ const c = {
2917
+ x: (a.x + bb.x + ib.x + ia.x) / 4,
2918
+ y: (a.y + bb.y + ib.y + ia.y) / 4
2919
+ };
2920
+ if (containsPoint(shape, c)) faces.push(polygon(face));
2921
+ }
2922
+ }
2923
+ }
2924
+ return {
2925
+ faces: group(faces),
2926
+ branches: dissolveSegments(branchSegs),
2927
+ bones: dissolveSegments(boneSegs)
2928
+ };
2929
+ }
2930
+ function mid2(a, b) {
2931
+ return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
2932
+ }
2933
+ function nearestOnPath(p, q) {
2934
+ let best = null;
2935
+ let bestD = Infinity;
2936
+ for (const ring2 of p.rings) {
2937
+ for (const v of ring2) {
2938
+ const d = Math.hypot(v.x - q.x, v.y - q.y);
2939
+ if (d < bestD) {
2940
+ bestD = d;
2941
+ best = v;
2942
+ }
2943
+ }
2944
+ }
2945
+ return best;
2946
+ }
2947
+ function nearestOnRing(ring2, q) {
2948
+ if (ring2.length === 0) return null;
2949
+ let best = ring2[0];
2950
+ let bestD = Infinity;
2951
+ for (const v of ring2) {
2952
+ const d = Math.hypot(v.x - q.x, v.y - q.y);
2953
+ if (d < bestD) {
2954
+ bestD = d;
2955
+ best = v;
2956
+ }
2957
+ }
2958
+ return best;
2959
+ }
2960
+
2961
+ // src/contour/marchingSquares.ts
2962
+ function isolinesFromFunction(bounds2, sampleSpacing, contourInterval, fn, isolineMin = Number.NaN, isolineMax = Number.NaN) {
2963
+ const [xmin, ymin, xmax, ymax] = bounds2;
2964
+ const dx = Math.max(sampleSpacing, 1e-9);
2965
+ const dy = dx;
2966
+ const nx = Math.max(2, Math.ceil((xmax - xmin) / dx) + 1);
2967
+ const ny = Math.max(2, Math.ceil((ymax - ymin) / dy) + 1);
2968
+ const grid = [];
2969
+ let gmin = Infinity;
2970
+ let gmax = -Infinity;
2971
+ for (let j = 0; j < ny; j++) {
2972
+ const row = [];
2973
+ const y = ymin + j * ((ymax - ymin) / (ny - 1));
2974
+ for (let i = 0; i < nx; i++) {
2975
+ const x = xmin + i * ((xmax - xmin) / (nx - 1));
2976
+ let v = fn(x, y);
2977
+ if (!Number.isFinite(v)) v = Number.NEGATIVE_INFINITY;
2978
+ row.push(v);
2979
+ if (Number.isFinite(v)) {
2980
+ gmin = Math.min(gmin, v);
2981
+ gmax = Math.max(gmax, v);
2982
+ }
2983
+ }
2984
+ grid.push(row);
2985
+ }
2986
+ const z0 = Number.isFinite(isolineMin) ? isolineMin : gmin;
2987
+ const z1 = Number.isFinite(isolineMax) ? isolineMax : gmax;
2988
+ if (!Number.isFinite(z0) || !Number.isFinite(z1) || contourInterval <= 0) {
2989
+ return group([]);
2990
+ }
2991
+ const levels = [];
2992
+ const start = Math.ceil(z0 / contourInterval) * contourInterval;
2993
+ for (let z = start; z <= z1 + 1e-12; z += contourInterval) {
2994
+ levels.push(z);
2995
+ }
2996
+ const segs = [];
2997
+ for (const level of levels) {
2998
+ segs.push(...marchLevel(grid, xmin, ymin, xmax, ymax, nx, ny, level));
2999
+ }
3000
+ return dissolveSegments(segs);
3001
+ }
3002
+ function isolineZeroFromFunction(bounds2, sampleSpacing, fn) {
3003
+ return isolinesFromFunction(bounds2, sampleSpacing, 1, fn, 0, 0);
3004
+ }
3005
+ function marchLevel(grid, xmin, ymin, xmax, ymax, nx, ny, level) {
3006
+ const segs = [];
3007
+ const xAt = (i) => xmin + i / (nx - 1) * (xmax - xmin);
3008
+ const yAt = (j) => ymin + j / (ny - 1) * (ymax - ymin);
3009
+ const lerp2 = (x0, y0, v0, x1, y1, v1) => {
3010
+ const t = (level - v0) / (v1 - v0 || 1e-15);
3011
+ return { x: x0 + t * (x1 - x0), y: y0 + t * (y1 - y0) };
3012
+ };
3013
+ for (let j = 0; j < ny - 1; j++) {
3014
+ for (let i = 0; i < nx - 1; i++) {
3015
+ const v00 = grid[j][i];
3016
+ const v10 = grid[j][i + 1];
3017
+ const v11 = grid[j + 1][i + 1];
3018
+ const v01 = grid[j + 1][i];
3019
+ const x0 = xAt(i);
3020
+ const x1 = xAt(i + 1);
3021
+ const y0 = yAt(j);
3022
+ const y1 = yAt(j + 1);
3023
+ let code = 0;
3024
+ if (v00 >= level) code |= 1;
3025
+ if (v10 >= level) code |= 2;
3026
+ if (v11 >= level) code |= 4;
3027
+ if (v01 >= level) code |= 8;
3028
+ if (code === 0 || code === 15) continue;
3029
+ const bottom = () => lerp2(x0, y0, v00, x1, y0, v10);
3030
+ const right = () => lerp2(x1, y0, v10, x1, y1, v11);
3031
+ const top = () => lerp2(x0, y1, v01, x1, y1, v11);
3032
+ const left = () => lerp2(x0, y0, v00, x0, y1, v01);
3033
+ const add = (a, b) => segs.push(segment(a, b));
3034
+ switch (code) {
3035
+ case 1:
3036
+ case 14:
3037
+ add(left(), bottom());
3038
+ break;
3039
+ case 2:
3040
+ case 13:
3041
+ add(bottom(), right());
3042
+ break;
3043
+ case 3:
3044
+ case 12:
3045
+ add(left(), right());
3046
+ break;
3047
+ case 4:
3048
+ case 11:
3049
+ add(right(), top());
3050
+ break;
3051
+ case 6:
3052
+ case 9:
3053
+ add(bottom(), top());
3054
+ break;
3055
+ case 7:
3056
+ case 8:
3057
+ add(left(), top());
3058
+ break;
3059
+ case 5: {
3060
+ const avg = (v00 + v10 + v11 + v01) / 4;
3061
+ if (avg >= level) {
3062
+ add(left(), top());
3063
+ add(bottom(), right());
3064
+ } else {
3065
+ add(left(), bottom());
3066
+ add(right(), top());
3067
+ }
3068
+ break;
3069
+ }
3070
+ case 10: {
3071
+ const avg = (v00 + v10 + v11 + v01) / 4;
3072
+ if (avg >= level) {
3073
+ add(left(), bottom());
3074
+ add(right(), top());
3075
+ } else {
3076
+ add(left(), top());
3077
+ add(bottom(), right());
3078
+ }
3079
+ break;
3080
+ }
3081
+ }
3082
+ }
3083
+ }
3084
+ return segs;
3085
+ }
3086
+ function isolinesFromPoints(points, values, intervals, _smoothing = 0) {
3087
+ if (points.length < 3 || intervals < 1) return group([]);
3088
+ let minZ = Infinity;
3089
+ let maxZ = -Infinity;
3090
+ for (const v of values) {
3091
+ minZ = Math.min(minZ, v);
3092
+ maxZ = Math.max(maxZ, v);
3093
+ }
3094
+ if (!Number.isFinite(minZ) || maxZ - minZ < 1e-15) return group([]);
3095
+ let minX = Infinity;
3096
+ let minY = Infinity;
3097
+ let maxX = -Infinity;
3098
+ let maxY = -Infinity;
3099
+ for (const p of points) {
3100
+ minX = Math.min(minX, p.x);
3101
+ minY = Math.min(minY, p.y);
3102
+ maxX = Math.max(maxX, p.x);
3103
+ maxY = Math.max(maxY, p.y);
3104
+ }
3105
+ const span = Math.max(maxX - minX, maxY - minY, 1);
3106
+ const sampleSpacing = span / Math.max(20, Math.sqrt(points.length));
3107
+ const interval = (maxZ - minZ) / intervals;
3108
+ const fn = (x, y) => {
3109
+ let num2 = 0;
3110
+ let den = 0;
3111
+ for (let i = 0; i < points.length; i++) {
3112
+ const p = points[i];
3113
+ const d2 = (x - p.x) ** 2 + (y - p.y) ** 2;
3114
+ const w = 1 / Math.max(d2, 1e-12);
3115
+ num2 += w * values[i];
3116
+ den += w;
3117
+ }
3118
+ return num2 / den;
3119
+ };
3120
+ return isolinesFromFunction(
3121
+ [minX, minY, maxX, maxY],
3122
+ sampleSpacing,
3123
+ interval,
3124
+ fn,
3125
+ minZ,
3126
+ maxZ
3127
+ );
3128
+ }
3129
+
3130
+ // src/contour/fields.ts
3131
+ function distanceField(shape, spacing, pole) {
3132
+ if (!shape.closed || shape.rings.length === 0 || spacing <= 0) return group([]);
3133
+ const b = bounds(shape);
3134
+ const span = Math.max(b.maxX - b.minX, b.maxY - b.minY, 1);
3135
+ let polePt = pole;
3136
+ if (!polePt) {
3137
+ const mic = maximumInscribedCircle(shape, Math.max(span * 0.01, 0.1));
3138
+ polePt = mic ? { x: mic.x, y: mic.y } : { x: (b.minX + b.maxX) / 2, y: (b.minY + b.maxY) / 2 };
3139
+ }
3140
+ const pad = Math.max(spacing, 1);
3141
+ const box = [
3142
+ b.minX - pad,
3143
+ b.minY - pad,
3144
+ b.maxX + pad,
3145
+ b.maxY + pad
3146
+ ];
3147
+ const sampleSpacing = Math.max(spacing / 10, span / 80);
3148
+ const fn = (x, y) => {
3149
+ const p = { x, y };
3150
+ if (!insideSafe(shape, p)) return Number.NEGATIVE_INFINITY;
3151
+ const dGeo = distanceToBoundary2(shape, p);
3152
+ const dPoint = Math.hypot(x - polePt.x, y - polePt.y);
3153
+ return dGeo - dPoint;
3154
+ };
3155
+ const raw = isolinesFromFunction(box, sampleSpacing, spacing, fn);
3156
+ return clipPolylinesToPath(raw, shape);
3157
+ }
3158
+ function contrastField(shape, intervals, reference) {
3159
+ if (!shape.closed || shape.rings.length === 0 || intervals < 1) {
3160
+ return group([]);
3161
+ }
3162
+ const b = bounds(shape);
3163
+ const areaApprox = (b.maxX - b.minX) * (b.maxY - b.minY);
3164
+ const count = Math.max(100, Math.floor(areaApprox / 100));
3165
+ const minDist = Math.sqrt(areaApprox / (count * 1.5));
3166
+ const samples = poisson(
3167
+ Math.max(minDist, 1e-3),
3168
+ b.minX,
3169
+ b.minY,
3170
+ b.maxX,
3171
+ b.maxY,
3172
+ 1337
3173
+ ).filter((p) => containsPoint(shape, p));
3174
+ if (samples.length < 3) return group([]);
3175
+ const values = samples.map((p) => {
3176
+ const dB = distanceToBoundary2(shape, p);
3177
+ const dR = Math.hypot(p.x - reference.x, p.y - reference.y);
3178
+ return Math.abs(dB - dR);
3179
+ });
3180
+ const lines = isolinesFromPoints(samples, values, Math.max(1, intervals), 0);
3181
+ return clipPolylinesToPath(lines, shape);
3182
+ }
3183
+ function isolines(shape, highPoint, intervalSpacing) {
3184
+ if (!shape.closed || intervalSpacing <= 0) return group([]);
3185
+ const b = bounds(shape);
3186
+ const span = Math.max(b.maxX - b.minX, b.maxY - b.minY, 1);
3187
+ const pad = intervalSpacing;
3188
+ const box = [
3189
+ b.minX - pad,
3190
+ b.minY - pad,
3191
+ b.maxX + pad,
3192
+ b.maxY + pad
3193
+ ];
3194
+ const fn = (x, y) => {
3195
+ if (!containsPoint(shape, { x, y })) return -1;
3196
+ return Math.hypot(x - highPoint.x, y - highPoint.y);
3197
+ };
3198
+ const raw = isolinesFromFunction(
3199
+ box,
3200
+ Math.max(intervalSpacing / 8, span / 100),
3201
+ intervalSpacing,
3202
+ fn,
3203
+ 0,
3204
+ span
3205
+ );
3206
+ return clipPolylinesToPath(raw, shape);
3207
+ }
3208
+ function clipPolylinesToPath(g, shape) {
3209
+ const paths = [];
3210
+ for (const p of g.paths) {
3211
+ const ring2 = p.rings[0];
3212
+ if (!ring2 || ring2.length < 2) continue;
3213
+ let current = [];
3214
+ const flush = () => {
3215
+ if (current.length >= 2) {
3216
+ paths.push({ rings: [current], closed: false });
3217
+ }
3218
+ current = [];
3219
+ };
3220
+ for (let i = 0; i < ring2.length - 1; i++) {
3221
+ const a = ring2[i];
3222
+ const b = ring2[i + 1];
3223
+ if (!finitePt(a) || !finitePt(b)) {
3224
+ flush();
3225
+ continue;
3226
+ }
3227
+ const mid5 = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
3228
+ if (insideSafe(shape, mid5)) {
3229
+ if (current.length === 0) current.push(a);
3230
+ current.push(b);
3231
+ } else {
3232
+ flush();
3233
+ }
3234
+ }
3235
+ flush();
3236
+ }
3237
+ return group(paths);
3238
+ }
3239
+ function finitePt(p) {
3240
+ return Number.isFinite(p.x) && Number.isFinite(p.y);
3241
+ }
3242
+ function insideSafe(shape, point) {
3243
+ if (!finitePt(point)) return false;
3244
+ try {
3245
+ return containsPoint(shape, point);
3246
+ } catch {
3247
+ return pointInPoly2(point, shape.rings[0]) && shape.rings.slice(1).every((h) => !pointInPoly2(point, h));
3248
+ }
3249
+ }
3250
+ function pointInPoly2(point, ring2) {
3251
+ let inside = false;
3252
+ for (let i = 0, j = ring2.length - 1; i < ring2.length; j = i++) {
3253
+ const pi = ring2[i];
3254
+ const pj = ring2[j];
3255
+ if (pi.y > point.y !== pj.y > point.y && point.x < (pj.x - pi.x) * (point.y - pi.y) / (pj.y - pi.y + 1e-15) + pi.x) {
3256
+ inside = !inside;
3257
+ }
3258
+ }
3259
+ return inside;
3260
+ }
3261
+
3262
+ // src/contour/distanceTree.ts
3263
+ function key3(v) {
3264
+ return `${v.x.toFixed(8)},${v.y.toFixed(8)}`;
3265
+ }
3266
+ function distanceTree(mesh, source, flatten2) {
3267
+ const adj = /* @__PURE__ */ new Map();
3268
+ const pts = /* @__PURE__ */ new Map();
3269
+ const addEdge = (a, b) => {
3270
+ const ka = key3(a);
3271
+ const kb = key3(b);
3272
+ if (ka === kb) return;
3273
+ pts.set(ka, a);
3274
+ pts.set(kb, b);
3275
+ if (!adj.has(ka)) adj.set(ka, []);
3276
+ if (!adj.has(kb)) adj.set(kb, []);
3277
+ adj.get(ka).push({ to: kb, pt: b });
3278
+ adj.get(kb).push({ to: ka, pt: a });
3279
+ };
3280
+ for (const face of mesh.paths) {
3281
+ for (const ring2 of face.rings) {
3282
+ if (ring2.length < 2) continue;
3283
+ const n = ring2.length;
3284
+ const closed = face.closed;
3285
+ const limit = closed ? n : n - 1;
3286
+ for (let i = 0; i < limit; i++) {
3287
+ addEdge(ring2[i], ring2[(i + 1) % n]);
3288
+ }
3289
+ }
3290
+ }
3291
+ if (pts.size === 0) return group([]);
3292
+ let sourceKey = "";
3293
+ let best = Infinity;
3294
+ for (const [k, p] of pts) {
3295
+ const d = Math.hypot(p.x - source.x, p.y - source.y);
3296
+ if (d < best) {
3297
+ best = d;
3298
+ sourceKey = k;
3299
+ }
3300
+ }
3301
+ const parent = /* @__PURE__ */ new Map();
3302
+ parent.set(sourceKey, null);
3303
+ const q = [sourceKey];
3304
+ while (q.length) {
3305
+ const u = q.shift();
3306
+ for (const { to } of adj.get(u) ?? []) {
3307
+ if (parent.has(to)) continue;
3308
+ parent.set(to, u);
3309
+ q.push(to);
3310
+ }
3311
+ }
3312
+ if (flatten2) {
3313
+ const segs = [];
3314
+ const seen = /* @__PURE__ */ new Set();
3315
+ for (const [v, p] of parent) {
3316
+ if (p === null) continue;
3317
+ const ek = v < p ? `${v}|${p}` : `${p}|${v}`;
3318
+ if (seen.has(ek)) continue;
3319
+ seen.add(ek);
3320
+ segs.push(segment(pts.get(p), pts.get(v)));
3321
+ }
3322
+ return dissolveSegments(segs);
3323
+ }
3324
+ const paths = [];
3325
+ for (const [v] of parent) {
3326
+ if (v === sourceKey) continue;
3327
+ const chain = [];
3328
+ let cur = v;
3329
+ while (cur !== null) {
3330
+ chain.push(pts.get(cur));
3331
+ cur = parent.get(cur) ?? null;
3332
+ }
3333
+ chain.reverse();
3334
+ if (chain.length >= 2) paths.push(polyline(chain));
3335
+ }
3336
+ return group(paths);
3337
+ }
3338
+
3339
+ // src/contour/index.ts
3340
+ function offsetCurvesOutward(p, distance) {
3341
+ return buffer(p, Math.abs(distance));
3342
+ }
3343
+ function offsetCurvesInward(p, distance) {
3344
+ return buffer(p, -Math.abs(distance));
3345
+ }
3346
+ var contour = {
3347
+ offsetCurvesOutward,
3348
+ offsetCurvesInward,
3349
+ medialAxis,
3350
+ chordalAxis,
3351
+ straightSkeleton,
3352
+ straightSkeletonParts,
3353
+ centerLine,
3354
+ distanceField,
3355
+ contrastField,
3356
+ distanceTree,
3357
+ isolines,
3358
+ isolinesFromFunction,
3359
+ isolineZeroFromFunction,
3360
+ isolinesFromPoints,
3361
+ dissolveSegments
3362
+ };
2323
3363
  function compoundVoronoi(sites, options = {}) {
2324
3364
  if (sites.length === 0) return group([]);
2325
3365
  if (sites.length === 1) {
@@ -2541,11 +3581,11 @@ function subdivideQuad(p) {
2541
3581
  if (!ring2 || ring2.length < 4) return [p];
2542
3582
  const [a, b, c, d] = ring2;
2543
3583
  if (!a || !b || !c || !d) return [p];
2544
- const mab = mid(a, b);
2545
- const mbc = mid(b, c);
2546
- const mcd = mid(c, d);
2547
- const mda = mid(d, a);
2548
- const cen = mid(mid(a, c), mid(b, d));
3584
+ const mab = mid3(a, b);
3585
+ const mbc = mid3(b, c);
3586
+ const mcd = mid3(c, d);
3587
+ const mda = mid3(d, a);
3588
+ const cen = mid3(mid3(a, c), mid3(b, d));
2549
3589
  return [
2550
3590
  polygon([a, mab, cen, mda]),
2551
3591
  polygon([mab, b, mbc, cen]),
@@ -2571,9 +3611,9 @@ function subdivideTriangle(p) {
2571
3611
  const a = ring2[0];
2572
3612
  const b = ring2[1];
2573
3613
  const c = ring2[2];
2574
- const mab = mid(a, b);
2575
- const mbc = mid(b, c);
2576
- const mca = mid(c, a);
3614
+ const mab = mid3(a, b);
3615
+ const mbc = mid3(b, c);
3616
+ const mca = mid3(c, a);
2577
3617
  return [
2578
3618
  polygon([a, mab, mca]),
2579
3619
  polygon([mab, b, mbc]),
@@ -2608,7 +3648,7 @@ function sliceDivision(p, spacing, angle = 0) {
2608
3648
  function hatchSubdivision(p, spacing, angle = 0) {
2609
3649
  return sliceDivision(p, spacing, angle);
2610
3650
  }
2611
- function mid(a, b) {
3651
+ function mid3(a, b) {
2612
3652
  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
2613
3653
  }
2614
3654
  var tiling = {
@@ -2814,7 +3854,7 @@ var polygonisation = {
2814
3854
  };
2815
3855
 
2816
3856
  // src/meshing/index.ts
2817
- function edgeKey(a, b) {
3857
+ function edgeKey3(a, b) {
2818
3858
  const aKey = `${a.x},${a.y}`;
2819
3859
  const bKey = `${b.x},${b.y}`;
2820
3860
  return aKey < bKey ? `${aKey}|${bKey}` : `${bKey}|${aKey}`;
@@ -2822,13 +3862,13 @@ function edgeKey(a, b) {
2822
3862
  function vertKey(v) {
2823
3863
  return `${v.x},${v.y}`;
2824
3864
  }
2825
- function same(a, b, eps = 1e-12) {
3865
+ function same2(a, b, eps = 1e-12) {
2826
3866
  return Math.abs(a.x - b.x) < eps && Math.abs(a.y - b.y) < eps;
2827
3867
  }
2828
3868
  function dist(a, b) {
2829
3869
  return Math.hypot(a.x - b.x, a.y - b.y);
2830
3870
  }
2831
- function mid2(a, b) {
3871
+ function mid4(a, b) {
2832
3872
  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
2833
3873
  }
2834
3874
  function lerp(a, b, t) {
@@ -2862,10 +3902,10 @@ function buildEdgeMap(faces) {
2862
3902
  for (let i = 0; i < lim; i++) {
2863
3903
  const a = ring2[i];
2864
3904
  const b = ring2[(i + 1) % n];
2865
- const key = edgeKey(a, b);
2866
- const rec = edges.get(key);
3905
+ const key4 = edgeKey3(a, b);
3906
+ const rec = edges.get(key4);
2867
3907
  if (rec) rec.faceIds.push(fi);
2868
- else edges.set(key, { a: { ...a }, b: { ...b }, faceIds: [fi] });
3908
+ else edges.set(key4, { a: { ...a }, b: { ...b }, faceIds: [fi] });
2869
3909
  }
2870
3910
  });
2871
3911
  return edges;
@@ -2909,8 +3949,8 @@ function facesFromKeptEdges(tris, kept) {
2909
3949
  if (ra !== rb) parent[rb] = ra;
2910
3950
  };
2911
3951
  const edgeMap = buildEdgeMap(tris);
2912
- for (const [key, e] of edgeMap) {
2913
- if (e.faceIds.length === 2 && !kept.has(key)) {
3952
+ for (const [key4, e] of edgeMap) {
3953
+ if (e.faceIds.length === 2 && !kept.has(key4)) {
2914
3954
  unite(e.faceIds[0], e.faceIds[1]);
2915
3955
  }
2916
3956
  }
@@ -2936,10 +3976,10 @@ function boundaryRingOfComponent(tris, members) {
2936
3976
  for (let i = 0; i < ring2.length; i++) {
2937
3977
  const a = ring2[i];
2938
3978
  const b = ring2[(i + 1) % ring2.length];
2939
- const key = edgeKey(a, b);
2940
- const rec = edgeCount.get(key);
3979
+ const key4 = edgeKey3(a, b);
3980
+ const rec = edgeCount.get(key4);
2941
3981
  if (rec) rec.n++;
2942
- else edgeCount.set(key, { a: { ...a }, b: { ...b }, n: 1 });
3982
+ else edgeCount.set(key4, { a: { ...a }, b: { ...b }, n: 1 });
2943
3983
  }
2944
3984
  }
2945
3985
  const boundary = [];
@@ -2969,14 +4009,14 @@ function orderEdgesIntoRing(edges) {
2969
4009
  const maxSteps = edges.length + 2;
2970
4010
  for (let step = 0; step < maxSteps; step++) {
2971
4011
  ring2.push({ ...curr });
2972
- if (same(curr, start) && ring2.length > 2) {
4012
+ if (same2(curr, start) && ring2.length > 2) {
2973
4013
  ring2.pop();
2974
4014
  return ring2;
2975
4015
  }
2976
4016
  const nbrs = adj.get(vertKey(curr)) ?? [];
2977
4017
  let next = null;
2978
4018
  for (const n of nbrs) {
2979
- if (!same(n, prev)) {
4019
+ if (!same2(n, prev)) {
2980
4020
  next = n;
2981
4021
  break;
2982
4022
  }
@@ -2991,13 +4031,13 @@ function applyEdgeFilter(tris, shouldKeep, preservePerimeter) {
2991
4031
  const edgeMap = buildEdgeMap(tris);
2992
4032
  const peri = perimeterKeys(edgeMap);
2993
4033
  const kept = /* @__PURE__ */ new Set();
2994
- for (const [key, e] of edgeMap) {
2995
- const isPeri = peri.has(key);
4034
+ for (const [key4, e] of edgeMap) {
4035
+ const isPeri = peri.has(key4);
2996
4036
  if (isPeri && preservePerimeter) {
2997
- kept.add(key);
4037
+ kept.add(key4);
2998
4038
  continue;
2999
4039
  }
3000
- if (shouldKeep(e.a, e.b, isPeri)) kept.add(key);
4040
+ if (shouldKeep(e.a, e.b, isPeri)) kept.add(key4);
3001
4041
  }
3002
4042
  return facesFromKeptEdges(tris, kept);
3003
4043
  }
@@ -3025,8 +4065,8 @@ function extractInnerVertices(faces) {
3025
4065
  const edgeMap = buildEdgeMap(faces);
3026
4066
  const peri = perimeterKeys(edgeMap);
3027
4067
  const periVerts = /* @__PURE__ */ new Set();
3028
- for (const key of peri) {
3029
- const e = edgeMap.get(key);
4068
+ for (const key4 of peri) {
4069
+ const e = edgeMap.get(key4);
3030
4070
  periVerts.add(vertKey(e.a));
3031
4071
  periVerts.add(vertKey(e.b));
3032
4072
  }
@@ -3076,10 +4116,10 @@ function densifyRing2(ring2, maxLen) {
3076
4116
  return out;
3077
4117
  }
3078
4118
  function isGabriel(a, b, points) {
3079
- const m = mid2(a, b);
4119
+ const m = mid4(a, b);
3080
4120
  const r = dist(a, b) / 2;
3081
4121
  for (const p of points) {
3082
- if (same(p, a) || same(p, b)) continue;
4122
+ if (same2(p, a) || same2(p, b)) continue;
3083
4123
  if (dist(p, m) < r - 1e-9) return false;
3084
4124
  }
3085
4125
  return true;
@@ -3087,7 +4127,7 @@ function isGabriel(a, b, points) {
3087
4127
  function isRelativeNeighbor(a, b, points) {
3088
4128
  const dab = dist(a, b);
3089
4129
  for (const p of points) {
3090
- if (same(p, a) || same(p, b)) continue;
4130
+ if (same2(p, a) || same2(p, b)) continue;
3091
4131
  if (Math.max(dist(p, a), dist(p, b)) < dab - 1e-9) return false;
3092
4132
  }
3093
4133
  return true;
@@ -3108,7 +4148,7 @@ function urquhartFaces(input, preservePerimeter = false) {
3108
4148
  const d = dist(a, b);
3109
4149
  if (d > longestLen) {
3110
4150
  longestLen = d;
3111
- longestKey = edgeKey(a, b);
4151
+ longestKey = edgeKey3(a, b);
3112
4152
  }
3113
4153
  }
3114
4154
  if (longestKey && !(preservePerimeter && peri.has(longestKey))) {
@@ -3116,8 +4156,8 @@ function urquhartFaces(input, preservePerimeter = false) {
3116
4156
  }
3117
4157
  }
3118
4158
  const kept = /* @__PURE__ */ new Set();
3119
- for (const key of edgeMap.keys()) {
3120
- if (!removed.has(key)) kept.add(key);
4159
+ for (const key4 of edgeMap.keys()) {
4160
+ if (!removed.has(key4)) kept.add(key4);
3121
4161
  }
3122
4162
  return group(facesFromKeptEdges(tris, kept));
3123
4163
  }
@@ -3148,12 +4188,12 @@ function spannerFaces(input, k = 1, preservePerimeter = false) {
3148
4188
  const edgeMap = buildEdgeMap(tris);
3149
4189
  const peri = perimeterKeys(edgeMap);
3150
4190
  const stretch = Math.max(1, k);
3151
- const edges = [...edgeMap.entries()].map(([key, e]) => ({
3152
- key,
4191
+ const edges = [...edgeMap.entries()].map(([key4, e]) => ({
4192
+ key: key4,
3153
4193
  a: e.a,
3154
4194
  b: e.b,
3155
4195
  len: dist(e.a, e.b),
3156
- peri: peri.has(key)
4196
+ peri: peri.has(key4)
3157
4197
  }));
3158
4198
  edges.sort((a, b) => a.len - b.len);
3159
4199
  const adj = /* @__PURE__ */ new Map();
@@ -3208,8 +4248,8 @@ function dualFaces(faces) {
3208
4248
  const edgeMap = buildEdgeMap(faces);
3209
4249
  const peri = perimeterKeys(edgeMap);
3210
4250
  const periVerts = /* @__PURE__ */ new Set();
3211
- for (const key of peri) {
3212
- const e = edgeMap.get(key);
4251
+ for (const key4 of peri) {
4252
+ const e = edgeMap.get(key4);
3213
4253
  periVerts.add(vertKey(e.a));
3214
4254
  periVerts.add(vertKey(e.b));
3215
4255
  }
@@ -3245,8 +4285,8 @@ function triVerts(t) {
3245
4285
  function midpointMap(faces) {
3246
4286
  const mids = /* @__PURE__ */ new Map();
3247
4287
  const edgeMap = buildEdgeMap(faces);
3248
- for (const [key, e] of edgeMap) {
3249
- mids.set(key, mid2(e.a, e.b));
4288
+ for (const [key4, e] of edgeMap) {
4289
+ mids.set(key4, mid4(e.a, e.b));
3250
4290
  }
3251
4291
  return mids;
3252
4292
  }
@@ -3256,7 +4296,7 @@ function centroidQuadrangulation(input, preservePerimeter = true) {
3256
4296
  const centers = tris.map((t) => centroid(t));
3257
4297
  const out = [];
3258
4298
  const peri = perimeterKeys(edgeMap);
3259
- for (const [key, e] of edgeMap) {
4299
+ for (const [key4, e] of edgeMap) {
3260
4300
  const ids = e.faceIds;
3261
4301
  if (ids.length === 2) {
3262
4302
  const c0 = centers[ids[0]];
@@ -3265,7 +4305,7 @@ function centroidQuadrangulation(input, preservePerimeter = true) {
3265
4305
  } else if (ids.length === 1 && preservePerimeter) {
3266
4306
  const c0 = centers[ids[0]];
3267
4307
  out.push(polygon([e.a, e.b, c0]));
3268
- } else if (ids.length === 1 && !preservePerimeter && peri.has(key)) ;
4308
+ } else if (ids.length === 1 && !preservePerimeter && peri.has(key4)) ;
3269
4309
  }
3270
4310
  return group(out);
3271
4311
  }
@@ -3278,9 +4318,9 @@ function splitQuadrangulation(input) {
3278
4318
  if (!v) continue;
3279
4319
  const [a, b, c] = v;
3280
4320
  const g = centroid(t);
3281
- const mab = mids.get(edgeKey(a, b)) ?? mid2(a, b);
3282
- const mbc = mids.get(edgeKey(b, c)) ?? mid2(b, c);
3283
- const mca = mids.get(edgeKey(c, a)) ?? mid2(c, a);
4321
+ const mab = mids.get(edgeKey3(a, b)) ?? mid4(a, b);
4322
+ const mbc = mids.get(edgeKey3(b, c)) ?? mid4(b, c);
4323
+ const mca = mids.get(edgeKey3(c, a)) ?? mid4(c, a);
3284
4324
  out.push(polygon([a, mab, g, mca]));
3285
4325
  out.push(polygon([b, mbc, g, mab]));
3286
4326
  out.push(polygon([c, mca, g, mbc]));
@@ -3291,13 +4331,13 @@ function edgeCollapseQuadrangulation(input, preservePerimeter = true) {
3291
4331
  const tris = asTriFaces(input);
3292
4332
  const edgeMap = buildEdgeMap(tris);
3293
4333
  const candidates = [];
3294
- for (const [key, e] of edgeMap) {
4334
+ for (const [key4, e] of edgeMap) {
3295
4335
  if (e.faceIds.length !== 2) continue;
3296
4336
  const [i, j] = e.faceIds;
3297
4337
  const quad = mergeTriPair(tris[i], tris[j], e.a, e.b);
3298
4338
  if (!quad) continue;
3299
4339
  candidates.push({
3300
- key,
4340
+ key: key4,
3301
4341
  a: e.a,
3302
4342
  b: e.b,
3303
4343
  i,
@@ -3333,7 +4373,7 @@ function mergeTriPair(t0, t1, ea, eb) {
3333
4373
  }
3334
4374
  function oppositeVertex(t, ea, eb) {
3335
4375
  for (const v of ringOf(t)) {
3336
- if (!same(v, ea) && !same(v, eb)) return v;
4376
+ if (!same2(v, ea) && !same2(v, eb)) return v;
3337
4377
  }
3338
4378
  return null;
3339
4379
  }
@@ -3395,8 +4435,8 @@ function vertexNeighborhood(faces) {
3395
4435
  const edgeMap = buildEdgeMap(faces);
3396
4436
  const peri = perimeterKeys(edgeMap);
3397
4437
  const perimeter = /* @__PURE__ */ new Set();
3398
- for (const key of peri) {
3399
- const e = edgeMap.get(key);
4438
+ for (const key4 of peri) {
4439
+ const e = edgeMap.get(key4);
3400
4440
  perimeter.add(vertKey(e.a));
3401
4441
  perimeter.add(vertKey(e.b));
3402
4442
  }
@@ -3519,8 +4559,8 @@ function simplifyMesh(faces, tolerance, preservePerimeter = true) {
3519
4559
  };
3520
4560
  const periVerts = /* @__PURE__ */ new Set();
3521
4561
  if (preservePerimeter) {
3522
- for (const key of peri) {
3523
- const e = edgeMap.get(key);
4562
+ for (const key4 of peri) {
4563
+ const e = edgeMap.get(key4);
3524
4564
  periVerts.add(vertKey(e.a));
3525
4565
  periVerts.add(vertKey(e.b));
3526
4566
  }
@@ -3741,8 +4781,8 @@ function findBreaks(faces) {
3741
4781
  function nearDuplicateEdge(a, b, tol) {
3742
4782
  const d1 = dist(a.a, b.a) + dist(a.b, b.b);
3743
4783
  const d2 = dist(a.a, b.b) + dist(a.b, b.a);
3744
- const len = dist(a.a, a.b);
3745
- return Math.min(d1, d2) < tol * 2 + len * 0.05 && Math.min(d1, d2) > 1e-9;
4784
+ const len2 = dist(a.a, a.b);
4785
+ return Math.min(d1, d2) < tol * 2 + len2 * 0.05 && Math.min(d1, d2) > 1e-9;
3746
4786
  }
3747
4787
  function fixBreaks(faces, maxGapWidth) {
3748
4788
  if (faces.length === 0) return group([]);
@@ -3851,7 +4891,7 @@ function fixBrokenFaces(coverage, tolerance, polygonise = true) {
3851
4891
  out.push(p);
3852
4892
  continue;
3853
4893
  }
3854
- if (ring2.length >= 3 && same(ring2[0], ring2[ring2.length - 1], tol * 2)) {
4894
+ if (ring2.length >= 3 && same2(ring2[0], ring2[ring2.length - 1], tol * 2)) {
3855
4895
  out.push(polygon(ring2.slice(0, -1)));
3856
4896
  } else if (ring2.length >= 3 && dist(ring2[0], ring2[ring2.length - 1]) <= tol * 2) {
3857
4897
  out.push(polygon(ring2));
@@ -3928,6 +4968,6 @@ var meshing = {
3928
4968
  findIslands
3929
4969
  };
3930
4970
 
3931
- export { angular, area, areaGroup, areaMerge, areaSimilarity, assertAreaClose, boundingBox, bounds, buffer, centroid, centroidQuadrangulation, centroidSortFaces, centroidSplit, chaikinCut, circle, circleCenter, circleContainedInPath, circleOverlapsPath, circlePacking, circular, clipSegmentToPath, clipSegmentsToPath, cloneGroup, clonePath, cloneVec2, closestPoint, closestPointPair, closestVertex, complement, compoundVoronoi, construction, containmentAgreement, containsPoint, contour, conversion, convexHull, convexHullPath, copy, createArc, createCircle, createKochSnowflake, createRect, createRegularPolygon, createRing, createStar, delaunayTriangulation, delaunayTriangulationPoints, densify, dilationErosion, dissolve, dualFaces, earCutTriangulation, edgeCollapseQuadrangulation, envelope, equalsVec2, erosionDilation, extractBoundary, extractHoles, extractInnerEdges, extractInnerVertices, extractPerimeter, farthestPointPair, filterAxisAligned, filterByMinLength, findBreaks, findContainingFace, findIslands, fixBreaks, fixBrokenFaces, flatten, flipHorizontal, flipVertical, fromArray, fromContours, frontChainPack, gabrielFaces, generateRandomGridPoints, generateRandomPoints, group, groupFromPaths, hatch, cross as hatchCross, parallel as hatchParallel, hatchSubdivision, height, hexGrid, hexLatticePack, hexTiling, hilbert as hilbertPolygonise, hull, innerVoronoi, innerVoronoiRaw, intersect, intersectionPoints, largestEmptyCircle, largestEmptyCircles, matchingQuadrangulation, maxArea, maximumInscribedAARectangle, maximumInscribedCircle, maximumInscribedPack, maximumInscribedPackUntil, meshing, minArea, minPerimeter, minimumBoundingCircle, minkDifference, minkSum, morphology, normalizeRing, obstaclePack, occlusionSubtract, offsetCurvesInward, offsetCurvesOutward, onion, onionLayers, optimisation, oraclePathsToGroup, originScale, overlapRegions, parallelSegments, parsePathData, parseSvg, path, ring as pointRing, pointSet, pointsOnExterior, poisson, poissonTriangulation, poissonTriangulationPoints, polygon, polygonisation, horizontal as polygoniseHorizontal, vertical as polygoniseVertical, polyline, predicates, processing, prunePointsWithinDistance, quadSubdivision, radialSortFaces, radialWarp, random as randomPoints, rectSubdivision, reducePrecision, refine, relativeAreaError, relativeNeighborFaces, removeSmallHoles, repulsionPack, resizeByHeight, resizeByWidth, ringArea, ringsToPath, rotate, rotateAroundCenter, roundVertexCoords, scale, segment, segmentLength, segmentSet, segmentsOnExterior, segmentsToOpenPaths, serializePathData, shapeBoolean, shear, simplify, simplifyMesh, sineWarp, slice, sliceDivision, smooth, smoothMesh, spannerFaces, spiralQuadrangulation, splitEdges, splitQuadrangulation, squareGrid, squareLatticePack, squareGrid2 as squareTiling, stochasticMerge, stochasticPack, subdivideMesh, subtract, subtractAll, symDifference, tiling, toArray, toContours, toSvg, transformation, translate, translateCentroidTo, translateCornerTo, translateToOrigin, triangleSubdivision, triangulation, union, unionAll, urquhartFaces, vec2, voronoi, width };
4971
+ export { angular, area, areaGroup, areaMerge, areaSimilarity, assertAreaClose, boundingBox, bounds, buffer, centerLine, centroid, centroidQuadrangulation, centroidSortFaces, centroidSplit, chaikinCut, chordalAxis, circle, circleCenter, circleContainedInPath, circleOverlapsPath, circlePacking, circular, clipSegmentToPath, clipSegmentsToPath, cloneGroup, clonePath, cloneVec2, closestPoint, closestPointPair, closestVertex, complement, compoundVoronoi, construction, containmentAgreement, containsPoint, contour, contrastField, conversion, convexHull, convexHullPath, copy, createArc, createCircle, createKochSnowflake, createRect, createRegularPolygon, createRing, createStar, delaunayTriangulation, delaunayTriangulationPoints, densify, dilationErosion, dissolve, dissolveSegments, distanceField, distanceTree, dualFaces, earCutTriangulation, edgeCollapseQuadrangulation, envelope, equalsVec2, erosionDilation, extractBoundary, extractHoles, extractInnerEdges, extractInnerVertices, extractPerimeter, farthestPointPair, filterAxisAligned, filterByMinLength, findBreaks, findContainingFace, findIslands, fixBreaks, fixBrokenFaces, flatten, flipHorizontal, flipVertical, fromArray, fromContours, frontChainPack, gabrielFaces, generateRandomGridPoints, generateRandomPoints, group, groupFromPaths, hatch, cross as hatchCross, parallel as hatchParallel, hatchSubdivision, height, hexGrid, hexLatticePack, hexTiling, hilbert as hilbertPolygonise, hull, innerVoronoi, innerVoronoiRaw, intersect, intersectionPoints, isolineZeroFromFunction, isolines, isolinesFromFunction, isolinesFromPoints, largestEmptyCircle, largestEmptyCircles, matchingQuadrangulation, maxArea, maximumInscribedAARectangle, maximumInscribedCircle, maximumInscribedPack, maximumInscribedPackUntil, medialAxis, meshing, minArea, minPerimeter, minimumBoundingCircle, minkDifference, minkSum, morphology, normalizeRing, obstaclePack, occlusionSubtract, offsetCurvesInward, offsetCurvesOutward, onion, onionLayers, optimisation, oraclePathsToGroup, originScale, overlapRegions, parallelSegments, parsePathData, parseSvg, path, ring as pointRing, pointSet, pointsOnExterior, poisson, poissonTriangulation, poissonTriangulationPoints, polygon, polygonisation, horizontal as polygoniseHorizontal, vertical as polygoniseVertical, polyline, predicates, processing, prunePointsWithinDistance, quadSubdivision, radialSortFaces, radialWarp, random as randomPoints, rectSubdivision, reducePrecision, refine, relativeAreaError, relativeNeighborFaces, removeSmallHoles, repulsionPack, resizeByHeight, resizeByWidth, ringArea, ringsToPath, rotate, rotateAroundCenter, roundVertexCoords, scale, segment, segmentLength, segmentSet, segmentsOnExterior, segmentsToOpenPaths, serializePathData, shapeBoolean, shear, simplify, simplifyMesh, sineWarp, slice, sliceDivision, smooth, smoothMesh, spannerFaces, spiralQuadrangulation, splitEdges, splitQuadrangulation, squareGrid, squareLatticePack, squareGrid2 as squareTiling, stochasticMerge, stochasticPack, straightSkeleton, straightSkeletonParts, subdivideMesh, subtract, subtractAll, symDifference, tiling, toArray, toContours, toSvg, transformation, translate, translateCentroidTo, translateCornerTo, translateToOrigin, triangleSubdivision, triangulation, union, unionAll, urquhartFaces, vec2, voronoi, width };
3932
4972
  //# sourceMappingURL=index.js.map
3933
4973
  //# sourceMappingURL=index.js.map