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
package/dist/index.js CHANGED
@@ -765,6 +765,193 @@ function parallelSegments(centerX, centerY, length, spacing, angle, n) {
765
765
  }
766
766
  return edges;
767
767
  }
768
+ function stochasticSegments(width2, height2, n, minLength, maxLength, seed = 1, originX = 0, originY = 0) {
769
+ const minL = minLength ?? 1;
770
+ const maxL = maxLength ?? Math.min(width2, height2);
771
+ if (n < 1 || width2 <= 0 || height2 <= 0) return [];
772
+ const monoLength = minL === maxL;
773
+ const rng = mulberry32(seed >>> 0);
774
+ const edges = [];
775
+ const maxAttempts = Math.max(1e4, n * 500);
776
+ let attempts = 0;
777
+ while (edges.length < n && attempts < maxAttempts) {
778
+ attempts++;
779
+ const length = monoLength ? minL : minL + rng() * (maxL - minL);
780
+ if (!(length > 0) || length * 2 >= width2 || length * 2 >= height2) {
781
+ continue;
782
+ }
783
+ const ax = originX + rng() * (width2 - length * 2) + length;
784
+ const ay = originY + rng() * (height2 - length * 2) + length;
785
+ const theta = rng() * Math.PI * 2;
786
+ const bx = ax + Math.cos(theta) * length;
787
+ const by = ay + Math.sin(theta) * length;
788
+ const a = vec2(ax, ay);
789
+ const b = vec2(bx, by);
790
+ let hits = false;
791
+ for (const s of edges) {
792
+ if (segmentsIntersect(a, b, s.a, s.b)) {
793
+ hits = true;
794
+ break;
795
+ }
796
+ }
797
+ if (!hits) edges.push(segment(a, b));
798
+ }
799
+ return edges;
800
+ }
801
+ function weaveSegments(width2, height2, cellSize, A = 1, B = 1, C = 1, options = {}) {
802
+ if (!(cellSize > 0)) {
803
+ throw new Error("weaveSegments: cellSize must be > 0");
804
+ }
805
+ if (A <= 0 || B <= 0) {
806
+ throw new Error("weaveSegments: A and B must be > 0");
807
+ }
808
+ const {
809
+ originX = 0,
810
+ originY = 0,
811
+ swapColors = false,
812
+ cellFraction = 1,
813
+ extendSingletonsToEdge = true
814
+ } = options;
815
+ if (!(cellFraction > 0)) {
816
+ throw new Error("weaveSegments: cellFraction must be > 0");
817
+ }
818
+ const P = A + B;
819
+ const f = Math.max(0, Math.min(1, cellFraction));
820
+ const cols = Math.max(1, Math.floor(width2 / cellSize));
821
+ const rows = Math.max(1, Math.floor(height2 / cellSize));
822
+ const gridW = cols * cellSize;
823
+ const gridH = rows * cellSize;
824
+ const dx = originX + (width2 - gridW) * 0.5;
825
+ const dy = originY + (height2 - gridH) * 0.5;
826
+ const gridLeft = dx;
827
+ const gridRight = dx + gridW;
828
+ const gridBottom = dy;
829
+ const gridTop = dy + gridH;
830
+ const weft = Array.from(
831
+ { length: rows },
832
+ () => Array.from({ length: cols }, () => false)
833
+ );
834
+ for (let r = 0; r < rows; r++) {
835
+ const shift = floorMod(r * C, P);
836
+ for (let c = 0; c < cols; c++) {
837
+ const idx = floorMod(c + shift, P);
838
+ let isWeft = idx < A;
839
+ if (swapColors) isWeft = !isWeft;
840
+ weft[r][c] = isWeft;
841
+ }
842
+ }
843
+ const segs = [];
844
+ for (let r = 0; r < rows; r++) {
845
+ let c = 0;
846
+ while (c < cols) {
847
+ if (!weft[r][c]) {
848
+ c++;
849
+ continue;
850
+ }
851
+ const start = c;
852
+ while (c + 1 < cols && weft[r][c + 1]) c++;
853
+ const end = c;
854
+ const runLen = end - start + 1;
855
+ const y = dy + (r + 0.5) * cellSize;
856
+ let x0 = dx + (start + (1 - f)) * cellSize;
857
+ let x1 = dx + (end + f) * cellSize;
858
+ if (extendSingletonsToEdge && runLen === 1) {
859
+ const cx = dx + (start + 0.5) * cellSize;
860
+ if (start === 0) {
861
+ x0 = gridLeft;
862
+ x1 = cx;
863
+ } else if (end === cols - 1) {
864
+ x0 = cx;
865
+ x1 = gridRight;
866
+ }
867
+ }
868
+ x0 = clamp(x0, gridLeft, gridRight);
869
+ x1 = clamp(x1, gridLeft, gridRight);
870
+ segs.push(segment(vec2(x0, y), vec2(x1, y)));
871
+ c++;
872
+ }
873
+ }
874
+ for (let c = 0; c < cols; c++) {
875
+ let r = 0;
876
+ while (r < rows) {
877
+ if (weft[r][c]) {
878
+ r++;
879
+ continue;
880
+ }
881
+ const start = r;
882
+ while (r + 1 < rows && !weft[r + 1][c]) r++;
883
+ const end = r;
884
+ const runLen = end - start + 1;
885
+ const x = dx + (c + 0.5) * cellSize;
886
+ let y0 = dy + (start + (1 - f)) * cellSize;
887
+ let y1 = dy + (end + f) * cellSize;
888
+ if (extendSingletonsToEdge && runLen === 1) {
889
+ const cy = dy + (start + 0.5) * cellSize;
890
+ if (start === 0) {
891
+ y0 = gridBottom;
892
+ y1 = cy;
893
+ } else if (end === rows - 1) {
894
+ y0 = cy;
895
+ y1 = gridTop;
896
+ }
897
+ }
898
+ y0 = clamp(y0, gridBottom, gridTop);
899
+ y1 = clamp(y1, gridBottom, gridTop);
900
+ segs.push(segment(vec2(x, y0), vec2(x, y1)));
901
+ r++;
902
+ }
903
+ }
904
+ return segs;
905
+ }
906
+ function perpendicularPathSegments(path3, interSegmentDistance, length, startOffset = 0) {
907
+ if (!(interSegmentDistance > 0)) return [];
908
+ const lengthFn = typeof length === "function" ? length : () => length;
909
+ const startNorm = (startOffset % 1 + 1) % 1;
910
+ const edges = [];
911
+ for (const ring2 of path3.rings) {
912
+ if (ring2.length < 2) continue;
913
+ const closed = path3.closed;
914
+ let pts = ring2.map((v) => vec2(v.x, v.y));
915
+ if (closed && shoelace(pts) > 0) {
916
+ pts = pts.slice().reverse();
917
+ }
918
+ const indexed = lengthIndex(pts, closed);
919
+ const end = indexed.total;
920
+ if (!(end > 0)) continue;
921
+ const count = Math.max(1, Math.round(end / interSegmentDistance));
922
+ const increment = 1 / count;
923
+ const eps = Math.max(
924
+ end * 1e-4,
925
+ Math.min(interSegmentDistance * 0.35, end * 0.01)
926
+ );
927
+ for (let i = 0; i < count; i++) {
928
+ const posFrac = (startNorm + i * increment) % 1;
929
+ const idx = posFrac * end;
930
+ const idxM = closed ? wrapIndex(idx - eps, end) : clamp(idx - eps, 0, end);
931
+ const idxP = closed ? wrapIndex(idx + eps, end) : clamp(idx + eps, 0, end);
932
+ const pm = indexed.pointAt(idxM);
933
+ const pp = indexed.pointAt(idxP);
934
+ const pc = indexed.pointAt(idx);
935
+ const tx = pp.x - pm.x;
936
+ const ty = pp.y - pm.y;
937
+ const tlen = Math.hypot(tx, ty);
938
+ if (tlen === 0) continue;
939
+ const nx = -ty / tlen;
940
+ const ny = tx / tlen;
941
+ const normalAngle = Math.atan2(ny, nx);
942
+ const L = lengthFn(pc.x, pc.y, posFrac, normalAngle);
943
+ if (!(L > 0)) continue;
944
+ const half = L * 0.5;
945
+ edges.push(
946
+ segment(
947
+ vec2(pc.x - nx * half, pc.y - ny * half),
948
+ vec2(pc.x + nx * half, pc.y + ny * half)
949
+ )
950
+ );
951
+ }
952
+ }
953
+ return edges;
954
+ }
768
955
  function filterByMinLength(segments, minLength) {
769
956
  return segments.filter((s) => segmentLength(s) >= minLength);
770
957
  }
@@ -790,11 +977,84 @@ function segmentsToOpenPaths(segments) {
790
977
  }
791
978
  var segmentSet = {
792
979
  parallelSegments,
980
+ stochasticSegments,
981
+ weaveSegments,
982
+ perpendicularPathSegments,
793
983
  filterByMinLength,
794
984
  filterAxisAligned,
795
985
  segmentLength,
796
986
  segmentsToOpenPaths
797
987
  };
988
+ function floorMod(n, m) {
989
+ return (n % m + m) % m;
990
+ }
991
+ function clamp(v, lo, hi) {
992
+ return Math.max(lo, Math.min(hi, v));
993
+ }
994
+ function wrapIndex(idx, end) {
995
+ if (end <= 0) return 0;
996
+ let x = idx % end;
997
+ if (x < 0) x += end;
998
+ return x;
999
+ }
1000
+ function shoelace(ring2) {
1001
+ let sum = 0;
1002
+ for (let i = 0; i < ring2.length; i++) {
1003
+ const a = ring2[i];
1004
+ const b = ring2[(i + 1) % ring2.length];
1005
+ sum += a.x * b.y - b.x * a.y;
1006
+ }
1007
+ return sum / 2;
1008
+ }
1009
+ function segmentsIntersect(a, b, c, d) {
1010
+ return ccw(a, c, d) !== ccw(b, c, d) && ccw(a, b, c) !== ccw(a, b, d);
1011
+ }
1012
+ function ccw(a, b, c) {
1013
+ return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);
1014
+ }
1015
+ function mulberry32(seed) {
1016
+ return function() {
1017
+ let t = seed += 1831565813;
1018
+ t = Math.imul(t ^ t >>> 15, t | 1);
1019
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
1020
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
1021
+ };
1022
+ }
1023
+ function lengthIndex(ring2, closed) {
1024
+ const n = ring2.length;
1025
+ const edgeCount = closed ? n : n - 1;
1026
+ const lengths = [];
1027
+ let total = 0;
1028
+ for (let i = 0; i < edgeCount; i++) {
1029
+ const a = ring2[i];
1030
+ const b = ring2[(i + 1) % n];
1031
+ const L = Math.hypot(b.x - a.x, b.y - a.y);
1032
+ lengths.push(L);
1033
+ total += L;
1034
+ }
1035
+ return {
1036
+ total,
1037
+ pointAt(dist2) {
1038
+ let remain = dist2;
1039
+ if (remain <= 0) return { ...ring2[0] };
1040
+ if (remain >= total) return { ...ring2[closed ? 0 : n - 1] };
1041
+ for (let i = 0; i < lengths.length; i++) {
1042
+ const L = lengths[i];
1043
+ if (remain <= L || i === lengths.length - 1) {
1044
+ const t = L < 1e-12 ? 0 : remain / L;
1045
+ const a = ring2[i];
1046
+ const b = ring2[(i + 1) % n];
1047
+ return {
1048
+ x: a.x + (b.x - a.x) * t,
1049
+ y: a.y + (b.y - a.y) * t
1050
+ };
1051
+ }
1052
+ remain -= L;
1053
+ }
1054
+ return { ...ring2[0] };
1055
+ }
1056
+ };
1057
+ }
798
1058
 
799
1059
  // src/segmentSet/clip.ts
800
1060
  function clipSegmentToPath(seg, clip) {
@@ -854,6 +1114,34 @@ function segmentIntersection(a, b, c, d) {
854
1114
  p: vec2(a.x + t * rX, a.y + t * rY)
855
1115
  };
856
1116
  }
1117
+ function buffer(path3, delta, options = {}) {
1118
+ const subject = pathToPathsD(path3);
1119
+ const join = options.join === "miter" ? JoinType.Miter : options.join === "square" ? JoinType.Square : JoinType.Round;
1120
+ const inflated = inflatePathsD(subject, delta, join, EndType.Polygon);
1121
+ return pathsDToGroup(inflated);
1122
+ }
1123
+ function erosionDilation(p, amount) {
1124
+ const a = Math.abs(amount);
1125
+ if (a < 1e-12) return group([p]);
1126
+ const eroded = buffer(p, -a);
1127
+ if (eroded.paths.length === 0) return eroded;
1128
+ const dilated = [];
1129
+ for (const q of eroded.paths) {
1130
+ dilated.push(...buffer(q, a).paths);
1131
+ }
1132
+ return group(dilated);
1133
+ }
1134
+ function dilationErosion(p, amount) {
1135
+ const a = Math.abs(amount);
1136
+ if (a < 1e-12) return group([p]);
1137
+ const dilated = buffer(p, a);
1138
+ if (dilated.paths.length === 0) return dilated;
1139
+ const eroded = [];
1140
+ for (const q of dilated.paths) {
1141
+ eroded.push(...buffer(q, -a).paths);
1142
+ }
1143
+ return group(eroded);
1144
+ }
857
1145
 
858
1146
  // src/hatch/index.ts
859
1147
  function parallel(path3, options = {}) {
@@ -886,6 +1174,94 @@ function cross(path3, options = {}) {
886
1174
  const b = parallel(path3, { ...options, angle: angle + Math.PI / 2 });
887
1175
  return [...a, ...b];
888
1176
  }
1177
+ function weave(path3, options = {}) {
1178
+ if (!path3.closed || !path3.rings[0]) {
1179
+ throw new Error("hatch.weave requires a closed path");
1180
+ }
1181
+ const bounds2 = pathBounds(path3);
1182
+ const width2 = Math.max(bounds2.maxX - bounds2.minX, 1e-9);
1183
+ const height2 = Math.max(bounds2.maxY - bounds2.minY, 1e-9);
1184
+ const cellSize = options.cellSize ?? Math.max(Math.min(width2, height2) / 12, 1e-6);
1185
+ const weaveOpts = {
1186
+ originX: bounds2.minX,
1187
+ originY: bounds2.minY
1188
+ };
1189
+ if (options.swapColors !== void 0) weaveOpts.swapColors = options.swapColors;
1190
+ if (options.cellFraction !== void 0) {
1191
+ weaveOpts.cellFraction = options.cellFraction;
1192
+ }
1193
+ if (options.extendSingletonsToEdge !== void 0) {
1194
+ weaveOpts.extendSingletonsToEdge = options.extendSingletonsToEdge;
1195
+ }
1196
+ const raw = weaveSegments(
1197
+ width2,
1198
+ height2,
1199
+ cellSize,
1200
+ options.A ?? 1,
1201
+ options.B ?? 1,
1202
+ options.C ?? 1,
1203
+ weaveOpts
1204
+ );
1205
+ return clipSegmentsToPath(raw, path3);
1206
+ }
1207
+ function stochastic(path3, options = {}) {
1208
+ if (!path3.closed || !path3.rings[0]) {
1209
+ throw new Error("hatch.stochastic requires a closed path");
1210
+ }
1211
+ const bounds2 = pathBounds(path3);
1212
+ const width2 = Math.max(bounds2.maxX - bounds2.minX, 1e-9);
1213
+ const height2 = Math.max(bounds2.maxY - bounds2.minY, 1e-9);
1214
+ const count = options.count ?? 40;
1215
+ const minLength = options.length ?? options.minLength ?? 1;
1216
+ const maxLength = options.length ?? options.maxLength ?? Math.min(width2, height2);
1217
+ const raw = stochasticSegments(
1218
+ width2,
1219
+ height2,
1220
+ count,
1221
+ minLength,
1222
+ maxLength,
1223
+ options.seed ?? 1,
1224
+ bounds2.minX,
1225
+ bounds2.minY
1226
+ );
1227
+ return clipSegmentsToPath(raw, path3);
1228
+ }
1229
+ function perpendicular(path3, options = {}) {
1230
+ if (!path3.rings[0]) {
1231
+ throw new Error("hatch.perpendicular requires a path with at least one ring");
1232
+ }
1233
+ const spacing = Math.max(options.spacing ?? 0.15, 1e-9);
1234
+ const length = options.length ?? 0.2;
1235
+ return perpendicularPathSegments(
1236
+ path3,
1237
+ spacing,
1238
+ length,
1239
+ options.startOffset ?? 0
1240
+ );
1241
+ }
1242
+ function concentric(path3, options = {}) {
1243
+ if (!path3.closed || !path3.rings[0]) {
1244
+ throw new Error("hatch.concentric requires a closed path");
1245
+ }
1246
+ const spacing = Math.max(options.spacing ?? 0.15, 1e-9);
1247
+ const bounds2 = pathBounds(path3);
1248
+ const diag = Math.hypot(bounds2.maxX - bounds2.minX, bounds2.maxY - bounds2.minY);
1249
+ const maxCount = options.count ?? Math.max(1, Math.floor(diag / (2 * spacing)));
1250
+ const out = [];
1251
+ for (let i = 1; i <= maxCount; i++) {
1252
+ const shell = buffer(path3, -spacing * i);
1253
+ if (shell.paths.length === 0) break;
1254
+ for (const p of shell.paths) {
1255
+ out.push(
1256
+ path(
1257
+ p.rings.map((r) => r.map((v) => ({ x: v.x, y: v.y }))),
1258
+ true
1259
+ )
1260
+ );
1261
+ }
1262
+ }
1263
+ return out;
1264
+ }
889
1265
  function hatchLineCount(bounds2, spacing, angle) {
890
1266
  const nx = Math.cos(angle + Math.PI / 2);
891
1267
  const ny = Math.sin(angle + Math.PI / 2);
@@ -922,7 +1298,11 @@ function pathBounds(path3) {
922
1298
  }
923
1299
  var hatch = {
924
1300
  parallel,
925
- cross
1301
+ cross,
1302
+ weave,
1303
+ stochastic,
1304
+ perpendicular,
1305
+ concentric
926
1306
  };
927
1307
 
928
1308
  // src/circlePacking/index.ts
@@ -988,7 +1368,7 @@ function centeredLatticeGrid(b, stepX, stepY, inset, endPad) {
988
1368
  return { ox, oy, nX, nY };
989
1369
  }
990
1370
  function stochasticPack(path3, points, minRadius, seed = 1) {
991
- const rng = mulberry32(seed >>> 0);
1371
+ const rng = mulberry322(seed >>> 0);
992
1372
  const b = bounds(path3);
993
1373
  const packing = [];
994
1374
  for (let attempt = 0; attempt < points; attempt++) {
@@ -1029,7 +1409,7 @@ function obstaclePack(path3, obstacles, n, tolerance = 1) {
1029
1409
  function frontChainPack(path3, radiusMin, radiusMax, seed = 1) {
1030
1410
  let rMin = Math.max(1e-6, Math.min(radiusMin, radiusMax));
1031
1411
  let rMax = Math.max(rMin, Math.max(radiusMin, radiusMax));
1032
- const rng = mulberry32(seed >>> 0);
1412
+ const rng = mulberry322(seed >>> 0);
1033
1413
  const b = bounds(path3);
1034
1414
  const packing = [];
1035
1415
  const area2 = Math.max(1e-6, (b.maxX - b.minX) * (b.maxY - b.minY));
@@ -1088,7 +1468,7 @@ function frontChainPack(path3, radiusMin, radiusMax, seed = 1) {
1088
1468
  function repulsionPack(path3, radiusMin, radiusMax, seed = 1, iterations = 80) {
1089
1469
  const rMin = Math.max(1e-6, Math.min(radiusMin, radiusMax));
1090
1470
  const rMax = Math.max(rMin, Math.max(radiusMin, radiusMax));
1091
- const rng = mulberry32(seed >>> 0);
1471
+ const rng = mulberry322(seed >>> 0);
1092
1472
  const b = bounds(path3);
1093
1473
  const area2 = Math.max(1e-6, (b.maxX - b.minX) * (b.maxY - b.minY));
1094
1474
  const avgR = (rMin + rMax) / 2;
@@ -1212,7 +1592,7 @@ function vecInBounds(b, rng) {
1212
1592
  y: b.minY + rng() * (b.maxY - b.minY)
1213
1593
  };
1214
1594
  }
1215
- function mulberry32(a) {
1595
+ function mulberry322(a) {
1216
1596
  return function() {
1217
1597
  let t = a += 1831565813;
1218
1598
  t = Math.imul(t ^ t >>> 15, t | 1);
@@ -1232,36 +1612,6 @@ var circlePacking = {
1232
1612
  circleOverlapsPath,
1233
1613
  circleContainedInPath
1234
1614
  };
1235
- function buffer(path3, delta, options = {}) {
1236
- const subject = pathToPathsD(path3);
1237
- const join = options.join === "miter" ? JoinType.Miter : options.join === "square" ? JoinType.Square : JoinType.Round;
1238
- const inflated = inflatePathsD(subject, delta, join, EndType.Polygon);
1239
- return pathsDToGroup(inflated);
1240
- }
1241
- function erosionDilation(p, amount) {
1242
- const a = Math.abs(amount);
1243
- if (a < 1e-12) return group([p]);
1244
- const eroded = buffer(p, -a);
1245
- if (eroded.paths.length === 0) return eroded;
1246
- const dilated = [];
1247
- for (const q of eroded.paths) {
1248
- dilated.push(...buffer(q, a).paths);
1249
- }
1250
- return group(dilated);
1251
- }
1252
- function dilationErosion(p, amount) {
1253
- const a = Math.abs(amount);
1254
- if (a < 1e-12) return group([p]);
1255
- const dilated = buffer(p, a);
1256
- if (dilated.paths.length === 0) return dilated;
1257
- const eroded = [];
1258
- for (const q of dilated.paths) {
1259
- eroded.push(...buffer(q, -a).paths);
1260
- }
1261
- return group(eroded);
1262
- }
1263
-
1264
- // src/morphology/index.ts
1265
1615
  function simplify(p, epsilon) {
1266
1616
  if (epsilon <= 0 || p.rings.length === 0) return p;
1267
1617
  const rings = [];
@@ -1508,13 +1858,13 @@ function removeSmallHoles(p, minArea2) {
1508
1858
  if (p.rings.length <= 1) return p;
1509
1859
  const exterior = p.rings[0];
1510
1860
  const kept = p.rings.slice(1).filter((hole) => {
1511
- const a = Math.abs(shoelace(hole));
1861
+ const a = Math.abs(shoelace2(hole));
1512
1862
  return a >= minArea2;
1513
1863
  });
1514
1864
  return path([exterior, ...kept], p.closed);
1515
1865
  }
1516
1866
  function generateRandomPoints(p, count, seed = 1) {
1517
- const rng = mulberry322(seed >>> 0);
1867
+ const rng = mulberry323(seed >>> 0);
1518
1868
  const b = bounds(p);
1519
1869
  const out = [];
1520
1870
  let attempts = 0;
@@ -1684,7 +2034,7 @@ function pointAtArcLength(ring2, lengths, target) {
1684
2034
  }
1685
2035
  return { ...ring2[0] };
1686
2036
  }
1687
- function shoelace(ring2) {
2037
+ function shoelace2(ring2) {
1688
2038
  let sum = 0;
1689
2039
  for (let i = 0; i < ring2.length; i++) {
1690
2040
  const a = ring2[i];
@@ -1693,7 +2043,7 @@ function shoelace(ring2) {
1693
2043
  }
1694
2044
  return sum / 2;
1695
2045
  }
1696
- function mulberry322(a) {
2046
+ function mulberry323(a) {
1697
2047
  return function() {
1698
2048
  let t = a += 1831565813;
1699
2049
  t = Math.imul(t ^ t >>> 15, t | 1);
@@ -1762,9 +2112,62 @@ var conversion = {
1762
2112
  group
1763
2113
  };
1764
2114
 
2115
+ // src/pointSet/hilbert.ts
2116
+ function xy2d(order, x, y) {
2117
+ let rx;
2118
+ let ry;
2119
+ let s;
2120
+ let d = 0;
2121
+ for (s = 1 << order - 1; s > 0; s >>= 1) {
2122
+ rx = (x & s) > 0 ? 1 : 0;
2123
+ ry = (y & s) > 0 ? 1 : 0;
2124
+ d += s * s * (3 * rx ^ ry);
2125
+ ({ x, y } = rot(s, x, y, rx, ry));
2126
+ }
2127
+ return d;
2128
+ }
2129
+ function rot(n, x, y, rx, ry) {
2130
+ if (ry === 0) {
2131
+ if (rx === 1) {
2132
+ x = n - 1 - x;
2133
+ y = n - 1 - y;
2134
+ }
2135
+ return { x: y, y: x };
2136
+ }
2137
+ return { x, y };
2138
+ }
2139
+ function hilbertSort(points, order = 10) {
2140
+ if (points.length === 0) return [];
2141
+ let minX = Infinity;
2142
+ let minY = Infinity;
2143
+ let maxX = -Infinity;
2144
+ let maxY = -Infinity;
2145
+ for (const p of points) {
2146
+ minX = Math.min(minX, p.x);
2147
+ minY = Math.min(minY, p.y);
2148
+ maxX = Math.max(maxX, p.x);
2149
+ maxY = Math.max(maxY, p.y);
2150
+ }
2151
+ const span = Math.max(maxX - minX, maxY - minY, 1e-9);
2152
+ const n = 1 << order;
2153
+ const scored = points.map((p) => {
2154
+ const x = Math.min(
2155
+ n - 1,
2156
+ Math.max(0, Math.floor((p.x - minX) / span * (n - 1)))
2157
+ );
2158
+ const y = Math.min(
2159
+ n - 1,
2160
+ Math.max(0, Math.floor((p.y - minY) / span * (n - 1)))
2161
+ );
2162
+ return { p, h: xy2d(order, x, y) };
2163
+ });
2164
+ scored.sort((a, b) => a.h - b.h);
2165
+ return scored.map((s) => s.p);
2166
+ }
2167
+
1765
2168
  // src/pointSet/index.ts
1766
2169
  function random(count, minX, minY, maxX, maxY, seed = 1) {
1767
- const rng = mulberry323(seed >>> 0);
2170
+ const rng = mulberry324(seed >>> 0);
1768
2171
  const out = [];
1769
2172
  for (let i = 0; i < count; i++) {
1770
2173
  out.push({
@@ -1809,7 +2212,7 @@ function ring(count, center, radius) {
1809
2212
  }
1810
2213
  return out;
1811
2214
  }
1812
- function mulberry323(a) {
2215
+ function mulberry324(a) {
1813
2216
  return function() {
1814
2217
  let t = a += 1831565813;
1815
2218
  t = Math.imul(t ^ t >>> 15, t | 1);
@@ -1818,7 +2221,7 @@ function mulberry323(a) {
1818
2221
  };
1819
2222
  }
1820
2223
  function poisson(minDistance, minX, minY, maxX, maxY, seed = 1, maxAttempts = 30) {
1821
- const rng = mulberry323(seed >>> 0);
2224
+ const rng = mulberry324(seed >>> 0);
1822
2225
  const r = Math.max(minDistance, 1e-9);
1823
2226
  const cell = r / Math.SQRT2;
1824
2227
  const w = maxX - minX;
@@ -1889,13 +2292,87 @@ function prunePointsWithinDistance(points, minDistance) {
1889
2292
  }
1890
2293
  return out;
1891
2294
  }
2295
+ function findShortestTour(points) {
2296
+ if (points.length === 0) return polygon([]);
2297
+ if (points.length === 1) return polygon([{ ...points[0] }]);
2298
+ if (points.length === 2) {
2299
+ return polyline([
2300
+ { ...points[0] },
2301
+ { ...points[1] },
2302
+ { ...points[0] }
2303
+ ]);
2304
+ }
2305
+ const tour = nearestNeighborTour(points);
2306
+ twoOpt(tour);
2307
+ return polygon(tour);
2308
+ }
2309
+ function nearestNeighborTour(points) {
2310
+ const remaining = points.map((p) => ({ ...p }));
2311
+ let startI = 0;
2312
+ for (let i = 1; i < remaining.length; i++) {
2313
+ const a = remaining[i];
2314
+ const b = remaining[startI];
2315
+ if (a.x < b.x || a.x === b.x && a.y < b.y) startI = i;
2316
+ }
2317
+ const ordered = [remaining.splice(startI, 1)[0]];
2318
+ while (remaining.length) {
2319
+ const last = ordered[ordered.length - 1];
2320
+ let bestI = 0;
2321
+ let bestD = Infinity;
2322
+ for (let i = 0; i < remaining.length; i++) {
2323
+ const d = Math.hypot(
2324
+ remaining[i].x - last.x,
2325
+ remaining[i].y - last.y
2326
+ );
2327
+ if (d < bestD) {
2328
+ bestD = d;
2329
+ bestI = i;
2330
+ }
2331
+ }
2332
+ ordered.push(remaining.splice(bestI, 1)[0]);
2333
+ }
2334
+ return ordered;
2335
+ }
2336
+ function twoOpt(tour) {
2337
+ const n = tour.length;
2338
+ if (n < 4) return;
2339
+ let improved = true;
2340
+ while (improved) {
2341
+ improved = false;
2342
+ for (let i = 0; i < n - 1; i++) {
2343
+ for (let j = i + 2; j < n; j++) {
2344
+ if (i === 0 && j === n - 1) continue;
2345
+ const a = tour[i];
2346
+ const b = tour[(i + 1) % n];
2347
+ const c = tour[j];
2348
+ const d = tour[(j + 1) % n];
2349
+ const before = Math.hypot(a.x - b.x, a.y - b.y) + Math.hypot(c.x - d.x, c.y - d.y);
2350
+ const after = Math.hypot(a.x - c.x, a.y - c.y) + Math.hypot(b.x - d.x, b.y - d.y);
2351
+ if (after + 1e-12 < before) {
2352
+ let lo = i + 1;
2353
+ let hi = j;
2354
+ while (lo < hi) {
2355
+ const tmp = tour[lo];
2356
+ tour[lo] = tour[hi];
2357
+ tour[hi] = tmp;
2358
+ lo++;
2359
+ hi--;
2360
+ }
2361
+ improved = true;
2362
+ }
2363
+ }
2364
+ }
2365
+ }
2366
+ }
1892
2367
  var pointSet = {
1893
2368
  random,
1894
2369
  squareGrid,
1895
2370
  hexGrid,
1896
2371
  ring,
1897
2372
  poisson,
1898
- prunePointsWithinDistance
2373
+ prunePointsWithinDistance,
2374
+ hilbertSort,
2375
+ findShortestTour
1899
2376
  };
1900
2377
 
1901
2378
  // src/triangulation/index.ts
@@ -3428,640 +3905,398 @@ var voronoi = {
3428
3905
  innerVoronoiRaw
3429
3906
  };
3430
3907
 
3431
- // src/construction/index.ts
3432
- function createCircle(cx, cy, radius, segments = 64) {
3433
- return createRegularPolygon(cx, cy, radius, Math.max(3, Math.floor(segments)));
3434
- }
3435
- function createRect(x, y, width2, height2) {
3436
- return polygon([
3437
- { x, y },
3438
- { x: x + width2, y },
3439
- { x: x + width2, y: y + height2 },
3440
- { x, y: y + height2 }
3441
- ]);
3908
+ // src/meshing/index.ts
3909
+ function edgeKey3(a, b) {
3910
+ const aKey = `${a.x},${a.y}`;
3911
+ const bKey = `${b.x},${b.y}`;
3912
+ return aKey < bKey ? `${aKey}|${bKey}` : `${bKey}|${aKey}`;
3442
3913
  }
3443
- function createRegularPolygon(cx, cy, radius, sides, rotation = -Math.PI / 2) {
3444
- const n = Math.max(3, Math.floor(sides));
3445
- const ring2 = [];
3446
- for (let i = 0; i < n; i++) {
3447
- const t = rotation + i / n * Math.PI * 2;
3448
- ring2.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius });
3449
- }
3450
- return polygon(ring2);
3914
+ function vertKey(v) {
3915
+ return `${v.x},${v.y}`;
3451
3916
  }
3452
- function createRing(cx, cy, outerRadius, innerRadius, segments = 64) {
3453
- const outer = createCircle(cx, cy, outerRadius, segments).rings[0];
3454
- const inner = createCircle(cx, cy, Math.min(innerRadius, outerRadius), segments).rings[0];
3455
- return path([outer, [...inner].reverse()], true);
3917
+ function same2(a, b, eps = 1e-12) {
3918
+ return Math.abs(a.x - b.x) < eps && Math.abs(a.y - b.y) < eps;
3456
3919
  }
3457
- function createArc(cx, cy, radius, startAngle, endAngle, segments = 32) {
3458
- const n = Math.max(2, Math.floor(segments));
3459
- const pts = [];
3460
- for (let i = 0; i <= n; i++) {
3461
- const t = startAngle + (endAngle - startAngle) * i / n;
3462
- pts.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius });
3463
- }
3464
- return polyline(pts);
3920
+ function dist(a, b) {
3921
+ return Math.hypot(a.x - b.x, a.y - b.y);
3465
3922
  }
3466
- function createStar(cx, cy, outerRadius, innerRadius, points = 5, rotation = -Math.PI / 2) {
3467
- const n = Math.max(3, Math.floor(points));
3468
- const ring2 = [];
3469
- for (let i = 0; i < n * 2; i++) {
3470
- const r = i % 2 === 0 ? outerRadius : innerRadius;
3471
- const t = rotation + i / (n * 2) * Math.PI * 2;
3472
- ring2.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r });
3473
- }
3474
- return polygon(ring2);
3923
+ function mid3(a, b) {
3924
+ return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
3475
3925
  }
3476
- function createKochSnowflake(cx, cy, radius, iterations = 3) {
3477
- const tri = createRegularPolygon(cx, cy, radius, 3);
3478
- let ring2 = tri.rings[0];
3479
- const iters = Math.max(0, Math.min(6, Math.floor(iterations)));
3480
- for (let i = 0; i < iters; i++) {
3481
- ring2 = kochRefine(ring2);
3482
- }
3483
- return polygon(ring2);
3926
+ function lerp(a, b, t) {
3927
+ return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
3484
3928
  }
3485
- function kochRefine(ring2) {
3929
+ function unique(points) {
3930
+ const seen = /* @__PURE__ */ new Set();
3486
3931
  const out = [];
3487
- const n = ring2.length;
3488
- for (let i = 0; i < n; i++) {
3489
- const a = ring2[i];
3490
- const b = ring2[(i + 1) % n];
3491
- const dx = b.x - a.x;
3492
- const dy = b.y - a.y;
3493
- const p1 = { x: a.x + dx / 3, y: a.y + dy / 3 };
3494
- const p2 = { x: a.x + 2 * dx / 3, y: a.y + 2 * dy / 3 };
3495
- const px = a.x + dx / 2 - dy * (Math.sqrt(3) / 6);
3496
- const py = a.y + dy / 2 + dx * (Math.sqrt(3) / 6);
3497
- out.push(a, p1, { x: px, y: py }, p2);
3932
+ for (const p of points) {
3933
+ const k = vertKey(p);
3934
+ if (seen.has(k)) continue;
3935
+ seen.add(k);
3936
+ out.push({ ...p });
3498
3937
  }
3499
3938
  return out;
3500
3939
  }
3501
- var construction = {
3502
- createCircle,
3503
- createRect,
3504
- createRegularPolygon,
3505
- createRing,
3506
- createArc,
3507
- createStar,
3508
- createKochSnowflake
3509
- };
3510
-
3511
- // src/tiling/index.ts
3512
- function squareGrid2(spacing, minX, minY, maxX, maxY) {
3513
- const s = Math.max(spacing, 1e-9);
3514
- const paths = [];
3515
- for (let x = minX; x < maxX - 1e-12; x += s) {
3516
- for (let y = minY; y < maxY - 1e-12; y += s) {
3517
- const w = Math.min(s, maxX - x);
3518
- const h = Math.min(s, maxY - y);
3519
- if (w > 1e-12 && h > 1e-12) paths.push(createRect(x, y, w, h));
3520
- }
3521
- }
3522
- return group(paths);
3940
+ function cross3(o, a, b) {
3941
+ return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
3523
3942
  }
3524
- function hexTiling(spacing, minX, minY, maxX, maxY) {
3525
- const s = Math.max(spacing, 1e-9);
3526
- const r = s / 2;
3527
- const dx = s * 0.75;
3528
- const dy = s * Math.sqrt(3) * 0.5;
3529
- const paths = [];
3530
- let row = 0;
3531
- for (let y = minY + r; y <= maxY + r; y += dy, row++) {
3532
- const x0 = minX + r + (row % 2 === 0 ? 0 : dx * 0.5);
3533
- for (let x = x0; x <= maxX + r; x += dx) {
3534
- paths.push(hexagon(x, y, r));
3943
+ function ringOf(face) {
3944
+ return face.rings[0] ?? [];
3945
+ }
3946
+ function buildEdgeMap(faces) {
3947
+ const edges = /* @__PURE__ */ new Map();
3948
+ faces.forEach((f, fi) => {
3949
+ const ring2 = ringOf(f);
3950
+ if (ring2.length < 2) return;
3951
+ const n = ring2.length;
3952
+ const closed = f.closed !== false && n >= 3;
3953
+ const lim = closed ? n : n - 1;
3954
+ for (let i = 0; i < lim; i++) {
3955
+ const a = ring2[i];
3956
+ const b = ring2[(i + 1) % n];
3957
+ const key4 = edgeKey3(a, b);
3958
+ const rec = edges.get(key4);
3959
+ if (rec) rec.faceIds.push(fi);
3960
+ else edges.set(key4, { a: { ...a }, b: { ...b }, faceIds: [fi] });
3535
3961
  }
3536
- }
3537
- return group(paths);
3962
+ });
3963
+ return edges;
3538
3964
  }
3539
- function hexagon(cx, cy, r) {
3540
- const ring2 = [];
3541
- for (let i = 0; i < 6; i++) {
3542
- const t = Math.PI / 180 * (60 * i - 30);
3543
- ring2.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r });
3965
+ function perimeterKeys(edgeMap) {
3966
+ const out = /* @__PURE__ */ new Set();
3967
+ for (const [k, e] of edgeMap) {
3968
+ if (e.faceIds.length === 1) out.add(k);
3544
3969
  }
3545
- return polygon(ring2);
3970
+ return out;
3546
3971
  }
3547
- function rectSubdivision(p, nx, ny) {
3548
- const b = bounds(p);
3549
- const cols = Math.max(1, Math.floor(nx));
3550
- const rows = Math.max(1, Math.floor(ny));
3551
- const dw = (b.maxX - b.minX) / cols;
3552
- const dh = (b.maxY - b.minY) / rows;
3553
- const paths = [];
3554
- for (let i = 0; i < cols; i++) {
3555
- for (let j = 0; j < rows; j++) {
3556
- const cell = createRect(
3557
- b.minX + i * dw,
3558
- b.minY + j * dh,
3559
- dw,
3560
- dh
3561
- );
3562
- paths.push(...intersect(p, cell).paths);
3972
+ function faceAdjacency(faces) {
3973
+ const edgeMap = buildEdgeMap(faces);
3974
+ const adj = faces.map(() => []);
3975
+ for (const e of edgeMap.values()) {
3976
+ if (e.faceIds.length === 2) {
3977
+ const [i, j] = e.faceIds;
3978
+ adj[i].push(j);
3979
+ adj[j].push(i);
3563
3980
  }
3564
3981
  }
3565
- return group(paths);
3982
+ return adj;
3566
3983
  }
3567
- function quadSubdivision(faces, depth = 1) {
3568
- let cur = faces;
3569
- const d = Math.max(0, Math.floor(depth));
3570
- for (let i = 0; i < d; i++) {
3571
- const next = [];
3572
- for (const f of cur) {
3573
- next.push(...subdivideQuad(f));
3984
+ function sitesFromFaces(faces) {
3985
+ return unique(faces.flatMap((f) => ringOf(f)));
3986
+ }
3987
+ function facesFromKeptEdges(tris, kept) {
3988
+ if (tris.length === 0) return [];
3989
+ const parent = tris.map((_, i) => i);
3990
+ const find = (i) => {
3991
+ let x = i;
3992
+ while (parent[x] !== x) {
3993
+ parent[x] = parent[parent[x]];
3994
+ x = parent[x];
3995
+ }
3996
+ return x;
3997
+ };
3998
+ const unite = (a, b) => {
3999
+ const ra = find(a);
4000
+ const rb = find(b);
4001
+ if (ra !== rb) parent[rb] = ra;
4002
+ };
4003
+ const edgeMap = buildEdgeMap(tris);
4004
+ for (const [key4, e] of edgeMap) {
4005
+ if (e.faceIds.length === 2 && !kept.has(key4)) {
4006
+ unite(e.faceIds[0], e.faceIds[1]);
3574
4007
  }
3575
- cur = next;
3576
4008
  }
3577
- return group(cur);
3578
- }
3579
- function subdivideQuad(p) {
3580
- const ring2 = p.rings[0];
3581
- if (!ring2 || ring2.length < 4) return [p];
3582
- const [a, b, c, d] = ring2;
3583
- if (!a || !b || !c || !d) return [p];
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));
3589
- return [
3590
- polygon([a, mab, cen, mda]),
3591
- polygon([mab, b, mbc, cen]),
3592
- polygon([cen, mbc, c, mcd]),
3593
- polygon([mda, cen, mcd, d])
3594
- ];
4009
+ const components = /* @__PURE__ */ new Map();
4010
+ for (let i = 0; i < tris.length; i++) {
4011
+ const r = find(i);
4012
+ const list = components.get(r) ?? [];
4013
+ list.push(i);
4014
+ components.set(r, list);
4015
+ }
4016
+ const out = [];
4017
+ for (const members of components.values()) {
4018
+ const ring2 = boundaryRingOfComponent(tris, members);
4019
+ if (ring2 && ring2.length >= 3) out.push(polygon(ring2));
4020
+ }
4021
+ return out;
3595
4022
  }
3596
- function triangleSubdivision(faces, depth = 1) {
3597
- let cur = faces;
3598
- const d = Math.max(0, Math.floor(depth));
3599
- for (let i = 0; i < d; i++) {
3600
- const next = [];
3601
- for (const f of cur) {
3602
- next.push(...subdivideTriangle(f));
4023
+ function boundaryRingOfComponent(tris, members) {
4024
+ const edgeCount = /* @__PURE__ */ new Map();
4025
+ for (const fi of members) {
4026
+ const ring2 = ringOf(tris[fi]);
4027
+ if (ring2.length < 3) continue;
4028
+ for (let i = 0; i < ring2.length; i++) {
4029
+ const a = ring2[i];
4030
+ const b = ring2[(i + 1) % ring2.length];
4031
+ const key4 = edgeKey3(a, b);
4032
+ const rec = edgeCount.get(key4);
4033
+ if (rec) rec.n++;
4034
+ else edgeCount.set(key4, { a: { ...a }, b: { ...b }, n: 1 });
3603
4035
  }
3604
- cur = next;
3605
4036
  }
3606
- return group(cur);
3607
- }
3608
- function subdivideTriangle(p) {
3609
- const ring2 = p.rings[0];
3610
- if (!ring2 || ring2.length < 3) return [p];
3611
- const a = ring2[0];
3612
- const b = ring2[1];
3613
- const c = ring2[2];
3614
- const mab = mid3(a, b);
3615
- const mbc = mid3(b, c);
3616
- const mca = mid3(c, a);
3617
- return [
3618
- polygon([a, mab, mca]),
3619
- polygon([mab, b, mbc]),
3620
- polygon([mca, mbc, c]),
3621
- polygon([mab, mbc, mca])
3622
- ];
3623
- }
3624
- function sliceDivision(p, spacing, angle = 0) {
3625
- const s = Math.max(spacing, 1e-9);
3626
- const b = bounds(p);
3627
- const cx = (b.minX + b.maxX) / 2;
3628
- const cy = (b.minY + b.maxY) / 2;
3629
- const diag = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) + s;
3630
- const ux = Math.cos(angle);
3631
- const uy = Math.sin(angle);
3632
- const nx = -uy;
3633
- const ny = ux;
3634
- const parts = [p];
3635
- for (let t = -diag; t <= diag; t += s) {
3636
- const a = { x: cx + nx * t - ux * diag, y: cy + ny * t - uy * diag };
3637
- const bb = { x: cx + nx * t + ux * diag, y: cy + ny * t + uy * diag };
3638
- const next = [];
3639
- for (const piece of parts) {
3640
- const g = slice(piece, a, bb);
3641
- next.push(...g.paths.length ? g.paths : [piece]);
3642
- }
3643
- parts.length = 0;
3644
- parts.push(...next);
4037
+ const boundary = [];
4038
+ for (const e of edgeCount.values()) {
4039
+ if (e.n === 1) boundary.push({ a: e.a, b: e.b });
3645
4040
  }
3646
- return group(parts);
3647
- }
3648
- function hatchSubdivision(p, spacing, angle = 0) {
3649
- return sliceDivision(p, spacing, angle);
3650
- }
3651
- function mid3(a, b) {
3652
- return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
3653
- }
3654
- var tiling = {
3655
- squareGrid: squareGrid2,
3656
- hexTiling,
3657
- rectSubdivision,
3658
- quadSubdivision,
3659
- triangleSubdivision,
3660
- sliceDivision,
3661
- hatchSubdivision
3662
- };
3663
-
3664
- // src/polygonisation/index.ts
3665
- function maxArea(points) {
3666
- return convexHull(points);
4041
+ if (boundary.length < 3) return null;
4042
+ return orderEdgesIntoRing(boundary);
3667
4043
  }
3668
- function minArea(points) {
3669
- const hull2 = convexHull(points).rings[0] ?? [];
3670
- if (hull2.length < 3) return convexHull(points);
3671
- if (hull2.length === 3) return polygon(hull2);
3672
- let best = null;
3673
- let bestA = Infinity;
3674
- const h = hull2;
3675
- const n = h.length;
3676
- for (let i = 0; i < n; i++) {
3677
- for (let j = i + 1; j < n; j++) {
3678
- for (let k = j + 1; k < n; k++) {
3679
- const tri = polygon([h[i], h[j], h[k]]);
3680
- if (!coversPoints(tri, points)) continue;
3681
- const a = area(tri);
3682
- if (a < bestA) {
3683
- bestA = a;
3684
- best = tri;
3685
- }
4044
+ function orderEdgesIntoRing(edges) {
4045
+ if (edges.length === 0) return null;
4046
+ const adj = /* @__PURE__ */ new Map();
4047
+ const add = (from, to) => {
4048
+ const k = vertKey(from);
4049
+ const list = adj.get(k) ?? [];
4050
+ list.push(to);
4051
+ adj.set(k, list);
4052
+ };
4053
+ for (const e of edges) {
4054
+ add(e.a, e.b);
4055
+ add(e.b, e.a);
4056
+ }
4057
+ const start = edges[0].a;
4058
+ const ring2 = [{ ...start }];
4059
+ let prev = start;
4060
+ let curr = edges[0].b;
4061
+ const maxSteps = edges.length + 2;
4062
+ for (let step = 0; step < maxSteps; step++) {
4063
+ ring2.push({ ...curr });
4064
+ if (same2(curr, start) && ring2.length > 2) {
4065
+ ring2.pop();
4066
+ return ring2;
4067
+ }
4068
+ const nbrs = adj.get(vertKey(curr)) ?? [];
4069
+ let next = null;
4070
+ for (const n of nbrs) {
4071
+ if (!same2(n, prev)) {
4072
+ next = n;
4073
+ break;
3686
4074
  }
3687
4075
  }
4076
+ if (!next) break;
4077
+ prev = curr;
4078
+ curr = next;
3688
4079
  }
3689
- return best ?? convexHull(points);
4080
+ return ring2.length >= 3 ? ring2 : null;
3690
4081
  }
3691
- function minPerimeter(points) {
3692
- if (points.length === 0) return polygon([]);
3693
- if (points.length < 3) return polygon(points);
3694
- const remaining = points.map((p) => ({ ...p }));
3695
- const ordered = [remaining.shift()];
3696
- while (remaining.length) {
3697
- const last = ordered[ordered.length - 1];
3698
- let bestI = 0;
3699
- let bestD = Infinity;
3700
- for (let i = 0; i < remaining.length; i++) {
3701
- const d = Math.hypot(
3702
- remaining[i].x - last.x,
3703
- remaining[i].y - last.y
3704
- );
3705
- if (d < bestD) {
3706
- bestD = d;
3707
- bestI = i;
3708
- }
4082
+ function applyEdgeFilter(tris, shouldKeep, preservePerimeter) {
4083
+ const edgeMap = buildEdgeMap(tris);
4084
+ const peri = perimeterKeys(edgeMap);
4085
+ const kept = /* @__PURE__ */ new Set();
4086
+ for (const [key4, e] of edgeMap) {
4087
+ const isPeri = peri.has(key4);
4088
+ if (isPeri && preservePerimeter) {
4089
+ kept.add(key4);
4090
+ continue;
3709
4091
  }
3710
- ordered.push(remaining.splice(bestI, 1)[0]);
4092
+ if (shouldKeep(e.a, e.b, isPeri)) kept.add(key4);
3711
4093
  }
3712
- return polygon(ordered);
4094
+ return facesFromKeptEdges(tris, kept);
3713
4095
  }
3714
- function horizontal(points) {
3715
- const pts = [...points].sort(
3716
- (a, b) => a.y === b.y ? a.x - b.x : a.y - b.y
3717
- );
3718
- return polygon(pts);
4096
+ function asTriFaces(input) {
4097
+ if (input.length === 0) return [];
4098
+ const first = input[0];
4099
+ if ("rings" in first) return input;
4100
+ return delaunayTriangulationPoints(input);
3719
4101
  }
3720
- function vertical(points) {
3721
- const pts = [...points].sort(
3722
- (a, b) => a.x === b.x ? a.y - b.y : a.x - b.x
3723
- );
3724
- return polygon(pts);
4102
+ function pointsOf(input) {
4103
+ if (input.length === 0) return [];
4104
+ const first = input[0];
4105
+ if ("rings" in first) return sitesFromFaces(input);
4106
+ return input;
3725
4107
  }
3726
- function angular(points) {
3727
- if (points.length === 0) return polygon([]);
3728
- const c = centroidOf(points);
3729
- const pts = [...points].sort(
3730
- (a, b) => Math.atan2(a.y - c.y, a.x - c.x) - Math.atan2(b.y - c.y, b.x - c.x)
3731
- );
3732
- return polygon(pts);
4108
+ function extractInnerEdges(faces) {
4109
+ const edgeMap = buildEdgeMap(faces);
4110
+ const out = [];
4111
+ for (const e of edgeMap.values()) {
4112
+ if (e.faceIds.length >= 2) out.push(segment(e.a, e.b));
4113
+ }
4114
+ return out;
3733
4115
  }
3734
- function circular(points) {
3735
- return angular(points);
4116
+ function extractInnerVertices(faces) {
4117
+ const edgeMap = buildEdgeMap(faces);
4118
+ const peri = perimeterKeys(edgeMap);
4119
+ const periVerts = /* @__PURE__ */ new Set();
4120
+ for (const key4 of peri) {
4121
+ const e = edgeMap.get(key4);
4122
+ periVerts.add(vertKey(e.a));
4123
+ periVerts.add(vertKey(e.b));
4124
+ }
4125
+ const out = [];
4126
+ const seen = /* @__PURE__ */ new Set();
4127
+ for (const f of faces) {
4128
+ for (const v of ringOf(f)) {
4129
+ const k = vertKey(v);
4130
+ if (periVerts.has(k) || seen.has(k)) continue;
4131
+ seen.add(k);
4132
+ out.push({ ...v });
4133
+ }
4134
+ }
4135
+ return out;
3736
4136
  }
3737
- function onion(points) {
3738
- const layers = onionLayers(points);
3739
- return layers[0] ?? polygon([]);
4137
+ function findContainingFace(faces, point) {
4138
+ for (const f of faces) {
4139
+ if (containsPoint(f, point)) return f;
4140
+ }
4141
+ return null;
3740
4142
  }
3741
- function onionLayers(points) {
3742
- let remaining = points.map((p) => ({ ...p }));
3743
- const layers = [];
3744
- while (remaining.length >= 3) {
3745
- const hull2 = convexHull(remaining);
3746
- const ring2 = hull2.rings[0] ?? [];
3747
- if (ring2.length < 3) break;
3748
- layers.push(hull2);
3749
- const onHull = new Set(ring2.map((v) => `${v.x},${v.y}`));
3750
- remaining = remaining.filter((p) => !onHull.has(`${p.x},${p.y}`));
4143
+ function splitEdges(faces, maxLen) {
4144
+ const lim = Math.max(maxLen, 1e-9);
4145
+ const out = [];
4146
+ for (const f of faces) {
4147
+ out.push(
4148
+ polygon(
4149
+ densifyRing2(ringOf(f), lim),
4150
+ f.rings.slice(1).map((r) => densifyRing2(r, lim))
4151
+ )
4152
+ );
3751
4153
  }
3752
- return layers;
4154
+ return group(out);
3753
4155
  }
3754
- function hilbert(points) {
3755
- if (points.length === 0) return polygon([]);
3756
- let minX = Infinity;
3757
- let minY = Infinity;
3758
- let maxX = -Infinity;
3759
- let maxY = -Infinity;
4156
+ function densifyRing2(ring2, maxLen) {
4157
+ if (ring2.length < 2) return ring2.map((v) => ({ ...v }));
4158
+ const out = [];
4159
+ const n = ring2.length;
4160
+ for (let i = 0; i < n; i++) {
4161
+ const a = ring2[i];
4162
+ const b = ring2[(i + 1) % n];
4163
+ out.push({ ...a });
4164
+ const d = dist(a, b);
4165
+ const steps = Math.floor(d / maxLen);
4166
+ for (let s = 1; s < steps; s++) out.push(lerp(a, b, s / steps));
4167
+ }
4168
+ return out;
4169
+ }
4170
+ function isGabriel(a, b, points) {
4171
+ const m = mid3(a, b);
4172
+ const r = dist(a, b) / 2;
3760
4173
  for (const p of points) {
3761
- minX = Math.min(minX, p.x);
3762
- minY = Math.min(minY, p.y);
3763
- maxX = Math.max(maxX, p.x);
3764
- maxY = Math.max(maxY, p.y);
3765
- }
3766
- const span = Math.max(maxX - minX, maxY - minY, 1e-9);
3767
- const order = 10;
3768
- const n = 1 << order;
3769
- const scored = points.map((p) => {
3770
- const x = Math.min(
3771
- n - 1,
3772
- Math.max(0, Math.floor((p.x - minX) / span * (n - 1)))
3773
- );
3774
- const y = Math.min(
3775
- n - 1,
3776
- Math.max(0, Math.floor((p.y - minY) / span * (n - 1)))
3777
- );
3778
- return { p, h: xy2d(order, x, y) };
3779
- });
3780
- scored.sort((a, b) => a.h - b.h);
3781
- return polygon(scored.map((s) => s.p));
3782
- }
3783
- function coversPoints(poly, points) {
3784
- const r = poly.rings[0];
3785
- if (!r || r.length < 3) return false;
3786
- const a = r[0];
3787
- const b = r[1];
3788
- const c = r[2];
3789
- for (const p of points) {
3790
- if (!pointInTriangle(p, a, b, c)) return false;
4174
+ if (same2(p, a) || same2(p, b)) continue;
4175
+ if (dist(p, m) < r - 1e-9) return false;
3791
4176
  }
3792
4177
  return true;
3793
4178
  }
3794
- function pointInTriangle(p, a, b, c) {
3795
- const v0x = c.x - a.x;
3796
- const v0y = c.y - a.y;
3797
- const v1x = b.x - a.x;
3798
- const v1y = b.y - a.y;
3799
- const v2x = p.x - a.x;
3800
- const v2y = p.y - a.y;
3801
- const dot00 = v0x * v0x + v0y * v0y;
3802
- const dot01 = v0x * v1x + v0y * v1y;
3803
- const dot02 = v0x * v2x + v0y * v2y;
3804
- const dot11 = v1x * v1x + v1y * v1y;
3805
- const dot12 = v1x * v2x + v1y * v2y;
3806
- const inv = 1 / (dot00 * dot11 - dot01 * dot01 || 1e-18);
3807
- const u = (dot11 * dot02 - dot01 * dot12) * inv;
3808
- const v = (dot00 * dot12 - dot01 * dot02) * inv;
3809
- return u >= -1e-9 && v >= -1e-9 && u + v <= 1 + 1e-9;
3810
- }
3811
- function centroidOf(points) {
3812
- let sx = 0;
3813
- let sy = 0;
4179
+ function isRelativeNeighbor(a, b, points) {
4180
+ const dab = dist(a, b);
3814
4181
  for (const p of points) {
3815
- sx += p.x;
3816
- sy += p.y;
3817
- }
3818
- return { x: sx / points.length, y: sy / points.length };
3819
- }
3820
- function xy2d(order, x, y) {
3821
- let rx;
3822
- let ry;
3823
- let s;
3824
- let d = 0;
3825
- for (s = 1 << order - 1; s > 0; s >>= 1) {
3826
- rx = (x & s) > 0 ? 1 : 0;
3827
- ry = (y & s) > 0 ? 1 : 0;
3828
- d += s * s * (3 * rx ^ ry);
3829
- ({ x, y } = rot(s, x, y, rx, ry));
4182
+ if (same2(p, a) || same2(p, b)) continue;
4183
+ if (Math.max(dist(p, a), dist(p, b)) < dab - 1e-9) return false;
3830
4184
  }
3831
- return d;
4185
+ return true;
3832
4186
  }
3833
- function rot(n, x, y, rx, ry) {
3834
- if (ry === 0) {
3835
- if (rx === 1) {
3836
- x = n - 1 - x;
3837
- y = n - 1 - y;
4187
+ function urquhartFaces(input, preservePerimeter = false) {
4188
+ const tris = asTriFaces(input);
4189
+ const edgeMap = buildEdgeMap(tris);
4190
+ const peri = perimeterKeys(edgeMap);
4191
+ const removed = /* @__PURE__ */ new Set();
4192
+ for (const t of tris) {
4193
+ const r = ringOf(t);
4194
+ if (r.length < 3) continue;
4195
+ let longestKey = "";
4196
+ let longestLen = -1;
4197
+ for (let i = 0; i < 3; i++) {
4198
+ const a = r[i];
4199
+ const b = r[(i + 1) % 3];
4200
+ const d = dist(a, b);
4201
+ if (d > longestLen) {
4202
+ longestLen = d;
4203
+ longestKey = edgeKey3(a, b);
4204
+ }
4205
+ }
4206
+ if (longestKey && !(preservePerimeter && peri.has(longestKey))) {
4207
+ removed.add(longestKey);
3838
4208
  }
3839
- return { x: y, y: x };
3840
4209
  }
3841
- return { x, y };
3842
- }
3843
- var polygonisation = {
3844
- maxArea,
3845
- minArea,
3846
- minPerimeter,
3847
- horizontal,
3848
- vertical,
3849
- angular,
3850
- circular,
3851
- onion,
3852
- onionLayers,
3853
- hilbert
3854
- };
3855
-
3856
- // src/meshing/index.ts
3857
- function edgeKey3(a, b) {
3858
- const aKey = `${a.x},${a.y}`;
3859
- const bKey = `${b.x},${b.y}`;
3860
- return aKey < bKey ? `${aKey}|${bKey}` : `${bKey}|${aKey}`;
3861
- }
3862
- function vertKey(v) {
3863
- return `${v.x},${v.y}`;
3864
- }
3865
- function same2(a, b, eps = 1e-12) {
3866
- return Math.abs(a.x - b.x) < eps && Math.abs(a.y - b.y) < eps;
3867
- }
3868
- function dist(a, b) {
3869
- return Math.hypot(a.x - b.x, a.y - b.y);
3870
- }
3871
- function mid4(a, b) {
3872
- return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
3873
- }
3874
- function lerp(a, b, t) {
3875
- return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
3876
- }
3877
- function unique(points) {
3878
- const seen = /* @__PURE__ */ new Set();
3879
- const out = [];
3880
- for (const p of points) {
3881
- const k = vertKey(p);
3882
- if (seen.has(k)) continue;
3883
- seen.add(k);
3884
- out.push({ ...p });
4210
+ const kept = /* @__PURE__ */ new Set();
4211
+ for (const key4 of edgeMap.keys()) {
4212
+ if (!removed.has(key4)) kept.add(key4);
3885
4213
  }
3886
- return out;
4214
+ return group(facesFromKeptEdges(tris, kept));
3887
4215
  }
3888
- function cross3(o, a, b) {
3889
- return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
4216
+ function gabrielFaces(input, preservePerimeter = false) {
4217
+ const tris = asTriFaces(input);
4218
+ const pts = pointsOf(input);
4219
+ return group(
4220
+ applyEdgeFilter(
4221
+ tris,
4222
+ (a, b) => isGabriel(a, b, pts),
4223
+ preservePerimeter
4224
+ )
4225
+ );
3890
4226
  }
3891
- function ringOf(face) {
3892
- return face.rings[0] ?? [];
4227
+ function relativeNeighborFaces(input, preservePerimeter = false) {
4228
+ const tris = asTriFaces(input);
4229
+ const pts = pointsOf(input);
4230
+ return group(
4231
+ applyEdgeFilter(
4232
+ tris,
4233
+ (a, b) => isRelativeNeighbor(a, b, pts),
4234
+ preservePerimeter
4235
+ )
4236
+ );
3893
4237
  }
3894
- function buildEdgeMap(faces) {
3895
- const edges = /* @__PURE__ */ new Map();
3896
- faces.forEach((f, fi) => {
3897
- const ring2 = ringOf(f);
3898
- if (ring2.length < 2) return;
3899
- const n = ring2.length;
3900
- const closed = f.closed !== false && n >= 3;
3901
- const lim = closed ? n : n - 1;
3902
- for (let i = 0; i < lim; i++) {
3903
- const a = ring2[i];
3904
- const b = ring2[(i + 1) % n];
3905
- const key4 = edgeKey3(a, b);
3906
- const rec = edges.get(key4);
3907
- if (rec) rec.faceIds.push(fi);
3908
- else edges.set(key4, { a: { ...a }, b: { ...b }, faceIds: [fi] });
4238
+ function spannerFaces(input, k = 1, preservePerimeter = false) {
4239
+ const tris = asTriFaces(input);
4240
+ const edgeMap = buildEdgeMap(tris);
4241
+ const peri = perimeterKeys(edgeMap);
4242
+ const stretch = Math.max(1, k);
4243
+ const edges = [...edgeMap.entries()].map(([key4, e]) => ({
4244
+ key: key4,
4245
+ a: e.a,
4246
+ b: e.b,
4247
+ len: dist(e.a, e.b),
4248
+ peri: peri.has(key4)
4249
+ }));
4250
+ edges.sort((a, b) => a.len - b.len);
4251
+ const adj = /* @__PURE__ */ new Map();
4252
+ const link = (a, b, w) => {
4253
+ const ka = vertKey(a);
4254
+ const kb = vertKey(b);
4255
+ if (!adj.has(ka)) adj.set(ka, []);
4256
+ if (!adj.has(kb)) adj.set(kb, []);
4257
+ adj.get(ka).push({ to: kb, w });
4258
+ adj.get(kb).push({ to: ka, w });
4259
+ };
4260
+ const shortest = (from, to) => {
4261
+ const start = vertKey(from);
4262
+ const goal = vertKey(to);
4263
+ const distMap = /* @__PURE__ */ new Map([[start, 0]]);
4264
+ const pq = [{ k: start, d: 0 }];
4265
+ while (pq.length) {
4266
+ pq.sort((x, y) => x.d - y.d);
4267
+ const cur = pq.shift();
4268
+ if (cur.k === goal) return cur.d;
4269
+ if (cur.d > (distMap.get(cur.k) ?? Infinity)) continue;
4270
+ for (const e of adj.get(cur.k) ?? []) {
4271
+ const nd = cur.d + e.w;
4272
+ if (nd < (distMap.get(e.to) ?? Infinity)) {
4273
+ distMap.set(e.to, nd);
4274
+ pq.push({ k: e.to, d: nd });
4275
+ }
4276
+ }
3909
4277
  }
3910
- });
3911
- return edges;
3912
- }
3913
- function perimeterKeys(edgeMap) {
3914
- const out = /* @__PURE__ */ new Set();
3915
- for (const [k, e] of edgeMap) {
3916
- if (e.faceIds.length === 1) out.add(k);
3917
- }
3918
- return out;
3919
- }
3920
- function faceAdjacency(faces) {
3921
- const edgeMap = buildEdgeMap(faces);
3922
- const adj = faces.map(() => []);
3923
- for (const e of edgeMap.values()) {
3924
- if (e.faceIds.length === 2) {
3925
- const [i, j] = e.faceIds;
3926
- adj[i].push(j);
3927
- adj[j].push(i);
4278
+ return Infinity;
4279
+ };
4280
+ const kept = /* @__PURE__ */ new Set();
4281
+ for (const e of edges) {
4282
+ if (e.peri && preservePerimeter) {
4283
+ kept.add(e.key);
4284
+ link(e.a, e.b, e.len);
4285
+ continue;
4286
+ }
4287
+ const pathLen = shortest(e.a, e.b);
4288
+ if (pathLen > stretch * e.len + 1e-9) {
4289
+ kept.add(e.key);
4290
+ link(e.a, e.b, e.len);
4291
+ } else if (e.peri) {
4292
+ kept.add(e.key);
4293
+ link(e.a, e.b, e.len);
3928
4294
  }
3929
4295
  }
3930
- return adj;
3931
- }
3932
- function sitesFromFaces(faces) {
3933
- return unique(faces.flatMap((f) => ringOf(f)));
4296
+ return group(facesFromKeptEdges(tris, kept));
3934
4297
  }
3935
- function facesFromKeptEdges(tris, kept) {
3936
- if (tris.length === 0) return [];
3937
- const parent = tris.map((_, i) => i);
3938
- const find = (i) => {
3939
- let x = i;
3940
- while (parent[x] !== x) {
3941
- parent[x] = parent[parent[x]];
3942
- x = parent[x];
3943
- }
3944
- return x;
3945
- };
3946
- const unite = (a, b) => {
3947
- const ra = find(a);
3948
- const rb = find(b);
3949
- if (ra !== rb) parent[rb] = ra;
3950
- };
3951
- const edgeMap = buildEdgeMap(tris);
3952
- for (const [key4, e] of edgeMap) {
3953
- if (e.faceIds.length === 2 && !kept.has(key4)) {
3954
- unite(e.faceIds[0], e.faceIds[1]);
3955
- }
3956
- }
3957
- const components = /* @__PURE__ */ new Map();
3958
- for (let i = 0; i < tris.length; i++) {
3959
- const r = find(i);
3960
- const list = components.get(r) ?? [];
3961
- list.push(i);
3962
- components.set(r, list);
3963
- }
3964
- const out = [];
3965
- for (const members of components.values()) {
3966
- const ring2 = boundaryRingOfComponent(tris, members);
3967
- if (ring2 && ring2.length >= 3) out.push(polygon(ring2));
3968
- }
3969
- return out;
3970
- }
3971
- function boundaryRingOfComponent(tris, members) {
3972
- const edgeCount = /* @__PURE__ */ new Map();
3973
- for (const fi of members) {
3974
- const ring2 = ringOf(tris[fi]);
3975
- if (ring2.length < 3) continue;
3976
- for (let i = 0; i < ring2.length; i++) {
3977
- const a = ring2[i];
3978
- const b = ring2[(i + 1) % ring2.length];
3979
- const key4 = edgeKey3(a, b);
3980
- const rec = edgeCount.get(key4);
3981
- if (rec) rec.n++;
3982
- else edgeCount.set(key4, { a: { ...a }, b: { ...b }, n: 1 });
3983
- }
3984
- }
3985
- const boundary = [];
3986
- for (const e of edgeCount.values()) {
3987
- if (e.n === 1) boundary.push({ a: e.a, b: e.b });
3988
- }
3989
- if (boundary.length < 3) return null;
3990
- return orderEdgesIntoRing(boundary);
3991
- }
3992
- function orderEdgesIntoRing(edges) {
3993
- if (edges.length === 0) return null;
3994
- const adj = /* @__PURE__ */ new Map();
3995
- const add = (from, to) => {
3996
- const k = vertKey(from);
3997
- const list = adj.get(k) ?? [];
3998
- list.push(to);
3999
- adj.set(k, list);
4000
- };
4001
- for (const e of edges) {
4002
- add(e.a, e.b);
4003
- add(e.b, e.a);
4004
- }
4005
- const start = edges[0].a;
4006
- const ring2 = [{ ...start }];
4007
- let prev = start;
4008
- let curr = edges[0].b;
4009
- const maxSteps = edges.length + 2;
4010
- for (let step = 0; step < maxSteps; step++) {
4011
- ring2.push({ ...curr });
4012
- if (same2(curr, start) && ring2.length > 2) {
4013
- ring2.pop();
4014
- return ring2;
4015
- }
4016
- const nbrs = adj.get(vertKey(curr)) ?? [];
4017
- let next = null;
4018
- for (const n of nbrs) {
4019
- if (!same2(n, prev)) {
4020
- next = n;
4021
- break;
4022
- }
4023
- }
4024
- if (!next) break;
4025
- prev = curr;
4026
- curr = next;
4027
- }
4028
- return ring2.length >= 3 ? ring2 : null;
4029
- }
4030
- function applyEdgeFilter(tris, shouldKeep, preservePerimeter) {
4031
- const edgeMap = buildEdgeMap(tris);
4032
- const peri = perimeterKeys(edgeMap);
4033
- const kept = /* @__PURE__ */ new Set();
4034
- for (const [key4, e] of edgeMap) {
4035
- const isPeri = peri.has(key4);
4036
- if (isPeri && preservePerimeter) {
4037
- kept.add(key4);
4038
- continue;
4039
- }
4040
- if (shouldKeep(e.a, e.b, isPeri)) kept.add(key4);
4041
- }
4042
- return facesFromKeptEdges(tris, kept);
4043
- }
4044
- function asTriFaces(input) {
4045
- if (input.length === 0) return [];
4046
- const first = input[0];
4047
- if ("rings" in first) return input;
4048
- return delaunayTriangulationPoints(input);
4049
- }
4050
- function pointsOf(input) {
4051
- if (input.length === 0) return [];
4052
- const first = input[0];
4053
- if ("rings" in first) return sitesFromFaces(input);
4054
- return input;
4055
- }
4056
- function extractInnerEdges(faces) {
4057
- const edgeMap = buildEdgeMap(faces);
4058
- const out = [];
4059
- for (const e of edgeMap.values()) {
4060
- if (e.faceIds.length >= 2) out.push(segment(e.a, e.b));
4061
- }
4062
- return out;
4063
- }
4064
- function extractInnerVertices(faces) {
4298
+ function dualFaces(faces) {
4299
+ if (faces.length === 0) return group([]);
4065
4300
  const edgeMap = buildEdgeMap(faces);
4066
4301
  const peri = perimeterKeys(edgeMap);
4067
4302
  const periVerts = /* @__PURE__ */ new Set();
@@ -4070,246 +4305,63 @@ function extractInnerVertices(faces) {
4070
4305
  periVerts.add(vertKey(e.a));
4071
4306
  periVerts.add(vertKey(e.b));
4072
4307
  }
4073
- const out = [];
4074
- const seen = /* @__PURE__ */ new Set();
4075
- for (const f of faces) {
4308
+ const incident = /* @__PURE__ */ new Map();
4309
+ faces.forEach((f, fi) => {
4310
+ const c = centroid(f);
4076
4311
  for (const v of ringOf(f)) {
4077
4312
  const k = vertKey(v);
4078
- if (periVerts.has(k) || seen.has(k)) continue;
4079
- seen.add(k);
4080
- out.push({ ...v });
4313
+ if (periVerts.has(k)) continue;
4314
+ const list = incident.get(k) ?? [];
4315
+ list.push({ fi, ang: Math.atan2(c.y - v.y, c.x - v.x), v });
4316
+ incident.set(k, list);
4081
4317
  }
4082
- }
4083
- return out;
4084
- }
4085
- function findContainingFace(faces, point) {
4086
- for (const f of faces) {
4087
- if (containsPoint(f, point)) return f;
4088
- }
4089
- return null;
4090
- }
4091
- function splitEdges(faces, maxLen) {
4092
- const lim = Math.max(maxLen, 1e-9);
4318
+ });
4093
4319
  const out = [];
4094
- for (const f of faces) {
4095
- out.push(
4096
- polygon(
4097
- densifyRing2(ringOf(f), lim),
4098
- f.rings.slice(1).map((r) => densifyRing2(r, lim))
4099
- )
4100
- );
4320
+ for (const list of incident.values()) {
4321
+ if (list.length < 3) continue;
4322
+ const byFace = /* @__PURE__ */ new Map();
4323
+ for (const item of list) byFace.set(item.fi, item);
4324
+ const uniqueList = [...byFace.values()];
4325
+ if (uniqueList.length < 3) continue;
4326
+ uniqueList.sort((a, b) => a.ang - b.ang);
4327
+ const ring2 = uniqueList.map((item) => centroid(faces[item.fi]));
4328
+ out.push(polygon(ring2));
4101
4329
  }
4102
4330
  return group(out);
4103
4331
  }
4104
- function densifyRing2(ring2, maxLen) {
4105
- if (ring2.length < 2) return ring2.map((v) => ({ ...v }));
4106
- const out = [];
4107
- const n = ring2.length;
4108
- for (let i = 0; i < n; i++) {
4109
- const a = ring2[i];
4110
- const b = ring2[(i + 1) % n];
4111
- out.push({ ...a });
4112
- const d = dist(a, b);
4113
- const steps = Math.floor(d / maxLen);
4114
- for (let s = 1; s < steps; s++) out.push(lerp(a, b, s / steps));
4115
- }
4116
- return out;
4117
- }
4118
- function isGabriel(a, b, points) {
4119
- const m = mid4(a, b);
4120
- const r = dist(a, b) / 2;
4121
- for (const p of points) {
4122
- if (same2(p, a) || same2(p, b)) continue;
4123
- if (dist(p, m) < r - 1e-9) return false;
4124
- }
4125
- return true;
4332
+ function triVerts(t) {
4333
+ const r = ringOf(t);
4334
+ if (r.length < 3) return null;
4335
+ return [r[0], r[1], r[2]];
4126
4336
  }
4127
- function isRelativeNeighbor(a, b, points) {
4128
- const dab = dist(a, b);
4129
- for (const p of points) {
4130
- if (same2(p, a) || same2(p, b)) continue;
4131
- if (Math.max(dist(p, a), dist(p, b)) < dab - 1e-9) return false;
4337
+ function midpointMap(faces) {
4338
+ const mids = /* @__PURE__ */ new Map();
4339
+ const edgeMap = buildEdgeMap(faces);
4340
+ for (const [key4, e] of edgeMap) {
4341
+ mids.set(key4, mid3(e.a, e.b));
4132
4342
  }
4133
- return true;
4343
+ return mids;
4134
4344
  }
4135
- function urquhartFaces(input, preservePerimeter = false) {
4345
+ function centroidQuadrangulation(input, preservePerimeter = true) {
4136
4346
  const tris = asTriFaces(input);
4137
4347
  const edgeMap = buildEdgeMap(tris);
4348
+ const centers = tris.map((t) => centroid(t));
4349
+ const out = [];
4138
4350
  const peri = perimeterKeys(edgeMap);
4139
- const removed = /* @__PURE__ */ new Set();
4140
- for (const t of tris) {
4141
- const r = ringOf(t);
4142
- if (r.length < 3) continue;
4143
- let longestKey = "";
4144
- let longestLen = -1;
4145
- for (let i = 0; i < 3; i++) {
4146
- const a = r[i];
4147
- const b = r[(i + 1) % 3];
4148
- const d = dist(a, b);
4149
- if (d > longestLen) {
4150
- longestLen = d;
4151
- longestKey = edgeKey3(a, b);
4152
- }
4153
- }
4154
- if (longestKey && !(preservePerimeter && peri.has(longestKey))) {
4155
- removed.add(longestKey);
4156
- }
4157
- }
4158
- const kept = /* @__PURE__ */ new Set();
4159
- for (const key4 of edgeMap.keys()) {
4160
- if (!removed.has(key4)) kept.add(key4);
4351
+ for (const [key4, e] of edgeMap) {
4352
+ const ids = e.faceIds;
4353
+ if (ids.length === 2) {
4354
+ const c0 = centers[ids[0]];
4355
+ const c1 = centers[ids[1]];
4356
+ out.push(polygon([e.a, c0, e.b, c1]));
4357
+ } else if (ids.length === 1 && preservePerimeter) {
4358
+ const c0 = centers[ids[0]];
4359
+ out.push(polygon([e.a, e.b, c0]));
4360
+ } else if (ids.length === 1 && !preservePerimeter && peri.has(key4)) ;
4161
4361
  }
4162
- return group(facesFromKeptEdges(tris, kept));
4362
+ return group(out);
4163
4363
  }
4164
- function gabrielFaces(input, preservePerimeter = false) {
4165
- const tris = asTriFaces(input);
4166
- const pts = pointsOf(input);
4167
- return group(
4168
- applyEdgeFilter(
4169
- tris,
4170
- (a, b) => isGabriel(a, b, pts),
4171
- preservePerimeter
4172
- )
4173
- );
4174
- }
4175
- function relativeNeighborFaces(input, preservePerimeter = false) {
4176
- const tris = asTriFaces(input);
4177
- const pts = pointsOf(input);
4178
- return group(
4179
- applyEdgeFilter(
4180
- tris,
4181
- (a, b) => isRelativeNeighbor(a, b, pts),
4182
- preservePerimeter
4183
- )
4184
- );
4185
- }
4186
- function spannerFaces(input, k = 1, preservePerimeter = false) {
4187
- const tris = asTriFaces(input);
4188
- const edgeMap = buildEdgeMap(tris);
4189
- const peri = perimeterKeys(edgeMap);
4190
- const stretch = Math.max(1, k);
4191
- const edges = [...edgeMap.entries()].map(([key4, e]) => ({
4192
- key: key4,
4193
- a: e.a,
4194
- b: e.b,
4195
- len: dist(e.a, e.b),
4196
- peri: peri.has(key4)
4197
- }));
4198
- edges.sort((a, b) => a.len - b.len);
4199
- const adj = /* @__PURE__ */ new Map();
4200
- const link = (a, b, w) => {
4201
- const ka = vertKey(a);
4202
- const kb = vertKey(b);
4203
- if (!adj.has(ka)) adj.set(ka, []);
4204
- if (!adj.has(kb)) adj.set(kb, []);
4205
- adj.get(ka).push({ to: kb, w });
4206
- adj.get(kb).push({ to: ka, w });
4207
- };
4208
- const shortest = (from, to) => {
4209
- const start = vertKey(from);
4210
- const goal = vertKey(to);
4211
- const distMap = /* @__PURE__ */ new Map([[start, 0]]);
4212
- const pq = [{ k: start, d: 0 }];
4213
- while (pq.length) {
4214
- pq.sort((x, y) => x.d - y.d);
4215
- const cur = pq.shift();
4216
- if (cur.k === goal) return cur.d;
4217
- if (cur.d > (distMap.get(cur.k) ?? Infinity)) continue;
4218
- for (const e of adj.get(cur.k) ?? []) {
4219
- const nd = cur.d + e.w;
4220
- if (nd < (distMap.get(e.to) ?? Infinity)) {
4221
- distMap.set(e.to, nd);
4222
- pq.push({ k: e.to, d: nd });
4223
- }
4224
- }
4225
- }
4226
- return Infinity;
4227
- };
4228
- const kept = /* @__PURE__ */ new Set();
4229
- for (const e of edges) {
4230
- if (e.peri && preservePerimeter) {
4231
- kept.add(e.key);
4232
- link(e.a, e.b, e.len);
4233
- continue;
4234
- }
4235
- const pathLen = shortest(e.a, e.b);
4236
- if (pathLen > stretch * e.len + 1e-9) {
4237
- kept.add(e.key);
4238
- link(e.a, e.b, e.len);
4239
- } else if (e.peri) {
4240
- kept.add(e.key);
4241
- link(e.a, e.b, e.len);
4242
- }
4243
- }
4244
- return group(facesFromKeptEdges(tris, kept));
4245
- }
4246
- function dualFaces(faces) {
4247
- if (faces.length === 0) return group([]);
4248
- const edgeMap = buildEdgeMap(faces);
4249
- const peri = perimeterKeys(edgeMap);
4250
- const periVerts = /* @__PURE__ */ new Set();
4251
- for (const key4 of peri) {
4252
- const e = edgeMap.get(key4);
4253
- periVerts.add(vertKey(e.a));
4254
- periVerts.add(vertKey(e.b));
4255
- }
4256
- const incident = /* @__PURE__ */ new Map();
4257
- faces.forEach((f, fi) => {
4258
- const c = centroid(f);
4259
- for (const v of ringOf(f)) {
4260
- const k = vertKey(v);
4261
- if (periVerts.has(k)) continue;
4262
- const list = incident.get(k) ?? [];
4263
- list.push({ fi, ang: Math.atan2(c.y - v.y, c.x - v.x), v });
4264
- incident.set(k, list);
4265
- }
4266
- });
4267
- const out = [];
4268
- for (const list of incident.values()) {
4269
- if (list.length < 3) continue;
4270
- const byFace = /* @__PURE__ */ new Map();
4271
- for (const item of list) byFace.set(item.fi, item);
4272
- const uniqueList = [...byFace.values()];
4273
- if (uniqueList.length < 3) continue;
4274
- uniqueList.sort((a, b) => a.ang - b.ang);
4275
- const ring2 = uniqueList.map((item) => centroid(faces[item.fi]));
4276
- out.push(polygon(ring2));
4277
- }
4278
- return group(out);
4279
- }
4280
- function triVerts(t) {
4281
- const r = ringOf(t);
4282
- if (r.length < 3) return null;
4283
- return [r[0], r[1], r[2]];
4284
- }
4285
- function midpointMap(faces) {
4286
- const mids = /* @__PURE__ */ new Map();
4287
- const edgeMap = buildEdgeMap(faces);
4288
- for (const [key4, e] of edgeMap) {
4289
- mids.set(key4, mid4(e.a, e.b));
4290
- }
4291
- return mids;
4292
- }
4293
- function centroidQuadrangulation(input, preservePerimeter = true) {
4294
- const tris = asTriFaces(input);
4295
- const edgeMap = buildEdgeMap(tris);
4296
- const centers = tris.map((t) => centroid(t));
4297
- const out = [];
4298
- const peri = perimeterKeys(edgeMap);
4299
- for (const [key4, e] of edgeMap) {
4300
- const ids = e.faceIds;
4301
- if (ids.length === 2) {
4302
- const c0 = centers[ids[0]];
4303
- const c1 = centers[ids[1]];
4304
- out.push(polygon([e.a, c0, e.b, c1]));
4305
- } else if (ids.length === 1 && preservePerimeter) {
4306
- const c0 = centers[ids[0]];
4307
- out.push(polygon([e.a, e.b, c0]));
4308
- } else if (ids.length === 1 && !preservePerimeter && peri.has(key4)) ;
4309
- }
4310
- return group(out);
4311
- }
4312
- function splitQuadrangulation(input) {
4364
+ function splitQuadrangulation(input) {
4313
4365
  const tris = asTriFaces(input);
4314
4366
  const mids = midpointMap(tris);
4315
4367
  const out = [];
@@ -4318,9 +4370,9 @@ function splitQuadrangulation(input) {
4318
4370
  if (!v) continue;
4319
4371
  const [a, b, c] = v;
4320
4372
  const g = centroid(t);
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);
4373
+ const mab = mids.get(edgeKey3(a, b)) ?? mid3(a, b);
4374
+ const mbc = mids.get(edgeKey3(b, c)) ?? mid3(b, c);
4375
+ const mca = mids.get(edgeKey3(c, a)) ?? mid3(c, a);
4324
4376
  out.push(polygon([a, mab, g, mca]));
4325
4377
  out.push(polygon([b, mbc, g, mab]));
4326
4378
  out.push(polygon([c, mca, g, mbc]));
@@ -4503,471 +4555,975 @@ function smoothMesh(faces, iterationsOrCutoff, preservePerimeter = true) {
4503
4555
  positions = next;
4504
4556
  if (cutoff > 0 && maxDisp < cutoff) break;
4505
4557
  }
4506
- return group(remapFaces(faces, positions));
4558
+ return group(remapFaces(faces, positions));
4559
+ }
4560
+ function subdivideMesh(faces, edgeSplitRatio = 0.5) {
4561
+ const t = Math.min(1, Math.max(0, edgeSplitRatio));
4562
+ const out = [];
4563
+ for (const f of faces) {
4564
+ const ring2 = ringOf(f);
4565
+ if (ring2.length < 3) continue;
4566
+ const c = centroid(f);
4567
+ const n = ring2.length;
4568
+ const splits = [];
4569
+ for (let i = 0; i < n; i++) {
4570
+ const a = ring2[i];
4571
+ const b = ring2[(i + 1) % n];
4572
+ splits.push(lerp(a, b, t));
4573
+ }
4574
+ for (let i = 0; i < n; i++) {
4575
+ const v = ring2[i];
4576
+ const sPrev = splits[(i - 1 + n) % n];
4577
+ const sNext = splits[i];
4578
+ out.push(polygon([v, sNext, c, sPrev]));
4579
+ }
4580
+ }
4581
+ return group(out);
4582
+ }
4583
+ function simplifyMesh(faces, tolerance, preservePerimeter = true) {
4584
+ if (tolerance <= 0 || faces.length === 0) return group(faces.map((f) => polygon(ringOf(f), f.rings.slice(1))));
4585
+ const edgeMap = buildEdgeMap(faces);
4586
+ const peri = perimeterKeys(edgeMap);
4587
+ const simplified = faces.map((f) => {
4588
+ const ring2 = ringOf(f);
4589
+ if (ring2.length <= 3) return polygon(ring2);
4590
+ if (preservePerimeter) {
4591
+ const s = simplify(f, tolerance);
4592
+ const sr = ringOf(s);
4593
+ if (sr.length < 3) return polygon(ring2);
4594
+ return polygon(sr, s.rings.slice(1));
4595
+ }
4596
+ return simplify(f, tolerance);
4597
+ });
4598
+ const all = unique(simplified.flatMap((f) => ringOf(f)));
4599
+ const snapped = clusterSnap(all, Math.max(tolerance * 0.5, 1e-9));
4600
+ const remap = (v) => {
4601
+ let best = v;
4602
+ let bestD = Infinity;
4603
+ for (const s of snapped) {
4604
+ const d = dist(v, s);
4605
+ if (d < bestD) {
4606
+ bestD = d;
4607
+ best = s;
4608
+ }
4609
+ }
4610
+ return { ...best };
4611
+ };
4612
+ const periVerts = /* @__PURE__ */ new Set();
4613
+ if (preservePerimeter) {
4614
+ for (const key4 of peri) {
4615
+ const e = edgeMap.get(key4);
4616
+ periVerts.add(vertKey(e.a));
4617
+ periVerts.add(vertKey(e.b));
4618
+ }
4619
+ }
4620
+ return group(
4621
+ simplified.map((f, i) => {
4622
+ const original = ringOf(faces[i]);
4623
+ const ring2 = ringOf(f).map((v, vi) => {
4624
+ if (preservePerimeter && original[vi] && periVerts.has(vertKey(original[vi]))) {
4625
+ return { ...original[vi] };
4626
+ }
4627
+ for (const ov of original) {
4628
+ if (periVerts.has(vertKey(ov)) && dist(v, ov) < tolerance * 2) {
4629
+ return { ...ov };
4630
+ }
4631
+ }
4632
+ return remap(v);
4633
+ });
4634
+ return polygon(unique(ring2).length >= 3 ? ring2 : original);
4635
+ })
4636
+ );
4637
+ }
4638
+ function clusterSnap(points, tol) {
4639
+ const clusters = [];
4640
+ for (const p of points) {
4641
+ let found = false;
4642
+ for (const c of clusters) {
4643
+ if (dist(p, c[0]) <= tol) {
4644
+ c.push(p);
4645
+ found = true;
4646
+ break;
4647
+ }
4648
+ }
4649
+ if (!found) clusters.push([p]);
4650
+ }
4651
+ return clusters.map((c) => ({
4652
+ x: c.reduce((s, p) => s + p.x, 0) / c.length,
4653
+ y: c.reduce((s, p) => s + p.y, 0) / c.length
4654
+ }));
4655
+ }
4656
+ function stochasticMerge(faces, nClasses, seed = 1) {
4657
+ if (faces.length === 0) return group([]);
4658
+ const classes = Math.max(1, Math.floor(nClasses));
4659
+ const rng = mulberry325(seed >>> 0);
4660
+ const label = faces.map(() => Math.floor(rng() * classes));
4661
+ const adj = faceAdjacency(faces);
4662
+ const parent = faces.map((_, i) => i);
4663
+ const find = (i) => {
4664
+ let x = i;
4665
+ while (parent[x] !== x) {
4666
+ parent[x] = parent[parent[x]];
4667
+ x = parent[x];
4668
+ }
4669
+ return x;
4670
+ };
4671
+ const unite = (a, b) => {
4672
+ const ra = find(a);
4673
+ const rb = find(b);
4674
+ if (ra !== rb) parent[rb] = ra;
4675
+ };
4676
+ for (let i = 0; i < faces.length; i++) {
4677
+ for (const j of adj[i]) {
4678
+ if (j > i && label[i] === label[j]) unite(i, j);
4679
+ }
4680
+ }
4681
+ const comps = /* @__PURE__ */ new Map();
4682
+ for (let i = 0; i < faces.length; i++) {
4683
+ const r = find(i);
4684
+ const list = comps.get(r) ?? [];
4685
+ list.push(i);
4686
+ comps.set(r, list);
4687
+ }
4688
+ const out = [];
4689
+ for (const members of comps.values()) {
4690
+ if (members.length === 1) {
4691
+ out.push(faces[members[0]]);
4692
+ continue;
4693
+ }
4694
+ const merged = dissolveFaces(members.map((i) => faces[i]));
4695
+ out.push(...merged);
4696
+ }
4697
+ return group(out);
4698
+ }
4699
+ function dissolveFaces(faces) {
4700
+ if (faces.length === 0) return [];
4701
+ if (faces.length === 1) return [faces[0]];
4702
+ try {
4703
+ let acc = faces[0];
4704
+ for (let i = 1; i < faces.length; i++) {
4705
+ const u = union(acc, faces[i]);
4706
+ if (u.paths.length === 0) continue;
4707
+ acc = u.paths.reduce(
4708
+ (best, p) => area(p) > area(best) ? p : best
4709
+ );
4710
+ }
4711
+ return [acc];
4712
+ } catch {
4713
+ return [convexHullOf(faces.flatMap((f) => ringOf(f)))];
4714
+ }
4715
+ }
4716
+ function areaMerge(faces, minAreaOrOptions) {
4717
+ let working = [...faces];
4718
+ if (working.length === 0) return group([]);
4719
+ if (typeof minAreaOrOptions === "object" && minAreaOrOptions.remainingFaces != null) {
4720
+ const target = Math.max(1, Math.floor(minAreaOrOptions.remainingFaces));
4721
+ while (working.length > target) {
4722
+ const next = mergeSmallestAdjacent(working);
4723
+ if (next.length >= working.length) break;
4724
+ working = next;
4725
+ }
4726
+ return group(working);
4727
+ }
4728
+ const minArea2 = typeof minAreaOrOptions === "number" ? minAreaOrOptions : minAreaOrOptions.minArea ?? 0;
4729
+ let guard = 0;
4730
+ while (guard++ < working.length + 10) {
4731
+ const smallIdx = working.findIndex((f) => area(f) < minArea2);
4732
+ if (smallIdx < 0) break;
4733
+ const next = mergeFaceIntoNeighbor(working, smallIdx);
4734
+ if (!next) break;
4735
+ working = next;
4736
+ }
4737
+ return group(working);
4738
+ }
4739
+ function mergeSmallestAdjacent(faces) {
4740
+ let best = -1;
4741
+ let bestA = Infinity;
4742
+ for (let i = 0; i < faces.length; i++) {
4743
+ const a = area(faces[i]);
4744
+ if (a < bestA) {
4745
+ bestA = a;
4746
+ best = i;
4747
+ }
4748
+ }
4749
+ if (best < 0) return faces;
4750
+ return mergeFaceIntoNeighbor(faces, best) ?? faces;
4751
+ }
4752
+ function mergeFaceIntoNeighbor(faces, idx) {
4753
+ const adj = faceAdjacency(faces);
4754
+ const nbrs = adj[idx] ?? [];
4755
+ if (nbrs.length === 0) {
4756
+ return faces.filter((_, i) => i !== idx);
4757
+ }
4758
+ let bestN = nbrs[0];
4759
+ let bestA = -1;
4760
+ for (const n of nbrs) {
4761
+ const a = area(faces[n]);
4762
+ if (a > bestA) {
4763
+ bestA = a;
4764
+ bestN = n;
4765
+ }
4766
+ }
4767
+ const merged = dissolveFaces([faces[idx], faces[bestN]]);
4768
+ const out = [];
4769
+ for (let i = 0; i < faces.length; i++) {
4770
+ if (i === idx || i === bestN) continue;
4771
+ out.push(faces[i]);
4772
+ }
4773
+ out.push(...merged);
4774
+ return out;
4775
+ }
4776
+ function convexHullOf(points) {
4777
+ const pts = unique(points);
4778
+ if (pts.length < 3) return polygon(pts);
4779
+ pts.sort((a, b) => a.x === b.x ? a.y - b.y : a.x - b.x);
4780
+ const lower = [];
4781
+ for (const p of pts) {
4782
+ while (lower.length >= 2 && cross3(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
4783
+ lower.pop();
4784
+ }
4785
+ lower.push(p);
4786
+ }
4787
+ const upper = [];
4788
+ for (let i = pts.length - 1; i >= 0; i--) {
4789
+ const p = pts[i];
4790
+ while (upper.length >= 2 && cross3(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
4791
+ upper.pop();
4792
+ }
4793
+ upper.push(p);
4794
+ }
4795
+ lower.pop();
4796
+ upper.pop();
4797
+ return polygon([...lower, ...upper]);
4798
+ }
4799
+ function mulberry325(a) {
4800
+ return () => {
4801
+ a |= 0;
4802
+ a = a + 1831565813 | 0;
4803
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
4804
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
4805
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
4806
+ };
4807
+ }
4808
+ function findBreaks(faces) {
4809
+ const edgeMap = buildEdgeMap(faces);
4810
+ const segs = [];
4811
+ const peri = [];
4812
+ for (const e of edgeMap.values()) {
4813
+ if (e.faceIds.length === 1) peri.push(e);
4814
+ }
4815
+ const used = /* @__PURE__ */ new Set();
4816
+ for (let i = 0; i < peri.length; i++) {
4817
+ if (used.has(i)) continue;
4818
+ const a = peri[i];
4819
+ for (let j = i + 1; j < peri.length; j++) {
4820
+ if (used.has(j)) continue;
4821
+ const b = peri[j];
4822
+ if (nearDuplicateEdge(a, b, 1e-3)) {
4823
+ segs.push(polyline([a.a, a.b]));
4824
+ segs.push(polyline([b.a, b.b]));
4825
+ used.add(i);
4826
+ used.add(j);
4827
+ break;
4828
+ }
4829
+ }
4830
+ }
4831
+ return group(segs);
4832
+ }
4833
+ function nearDuplicateEdge(a, b, tol) {
4834
+ const d1 = dist(a.a, b.a) + dist(a.b, b.b);
4835
+ const d2 = dist(a.a, b.b) + dist(a.b, b.a);
4836
+ const len2 = dist(a.a, a.b);
4837
+ return Math.min(d1, d2) < tol * 2 + len2 * 0.05 && Math.min(d1, d2) > 1e-9;
4838
+ }
4839
+ function fixBreaks(faces, maxGapWidth) {
4840
+ if (faces.length === 0) return group([]);
4841
+ const tol = Math.max(maxGapWidth, 1e-9);
4842
+ const verts = unique(faces.flatMap((f) => ringOf(f)));
4843
+ const snapped = clusterSnap(verts, tol);
4844
+ const remap = (v) => {
4845
+ let best = snapped[0];
4846
+ let bestD = Infinity;
4847
+ for (const s of snapped) {
4848
+ const d = dist(v, s);
4849
+ if (d < bestD) {
4850
+ bestD = d;
4851
+ best = s;
4852
+ }
4853
+ }
4854
+ return { ...best };
4855
+ };
4856
+ const fixed = faces.map((f) => {
4857
+ const ring2 = unique(ringOf(f).map(remap));
4858
+ return ring2.length >= 3 ? polygon(ring2) : f;
4859
+ });
4860
+ try {
4861
+ return group(dissolveOverlaps(fixed));
4862
+ } catch {
4863
+ return group(fixed);
4864
+ }
4865
+ }
4866
+ function dissolveOverlaps(faces) {
4867
+ let working = [...faces];
4868
+ let changed = true;
4869
+ let guard = 0;
4870
+ while (changed && guard++ < working.length) {
4871
+ changed = false;
4872
+ outer: for (let i = 0; i < working.length; i++) {
4873
+ for (let j = i + 1; j < working.length; j++) {
4874
+ const a = working[i];
4875
+ const b = working[j];
4876
+ const u = union(a, b);
4877
+ const ua = u.paths.reduce((s, p) => s + area(p), 0);
4878
+ if (ua < area(a) + area(b) - 1e-6) {
4879
+ const next = working.filter((_, k) => k !== i && k !== j);
4880
+ next.push(...u.paths);
4881
+ working = next;
4882
+ changed = true;
4883
+ break outer;
4884
+ }
4885
+ }
4886
+ }
4887
+ }
4888
+ return working;
4889
+ }
4890
+ function fixBrokenFaces(coverage, tolerance, polygonise = true) {
4891
+ const tol = Math.max(tolerance, 1e-9);
4892
+ const anchors = [];
4893
+ const endpoints = [];
4894
+ coverage.forEach((p, pi) => {
4895
+ const ring2 = ringOf(p);
4896
+ if (ring2.length === 0) return;
4897
+ if (p.closed) {
4898
+ for (const v of ring2) anchors.push(v);
4899
+ } else {
4900
+ endpoints.push({ pathIdx: pi, end: 0, v: ring2[0] });
4901
+ endpoints.push({
4902
+ pathIdx: pi,
4903
+ end: 1,
4904
+ v: ring2[ring2.length - 1]
4905
+ });
4906
+ }
4907
+ });
4908
+ const snapTargets = clusterSnap(
4909
+ [...anchors, ...endpoints.map((e) => e.v)],
4910
+ tol
4911
+ );
4912
+ const snapTo = (v, preferAnchor) => {
4913
+ let best = v;
4914
+ let bestD = Infinity;
4915
+ for (const s of snapTargets) {
4916
+ const d = dist(v, s);
4917
+ if (d <= tol && d < bestD) {
4918
+ bestD = d;
4919
+ best = s;
4920
+ }
4921
+ }
4922
+ {
4923
+ for (const a of anchors) {
4924
+ if (dist(v, a) <= tol) return { ...a };
4925
+ }
4926
+ }
4927
+ return { ...best };
4928
+ };
4929
+ const snappedPaths = coverage.map((p) => {
4930
+ const ring2 = ringOf(p);
4931
+ if (ring2.length === 0) return p;
4932
+ if (p.closed) return p;
4933
+ const next = ring2.map((v) => ({ ...v }));
4934
+ next[0] = snapTo(ring2[0]);
4935
+ next[next.length - 1] = snapTo(ring2[ring2.length - 1]);
4936
+ return polyline(next);
4937
+ });
4938
+ if (!polygonise) return group(snappedPaths);
4939
+ const out = [];
4940
+ for (const p of snappedPaths) {
4941
+ const ring2 = ringOf(p);
4942
+ if (p.closed) {
4943
+ out.push(p);
4944
+ continue;
4945
+ }
4946
+ if (ring2.length >= 3 && same2(ring2[0], ring2[ring2.length - 1], tol * 2)) {
4947
+ out.push(polygon(ring2.slice(0, -1)));
4948
+ } else if (ring2.length >= 3 && dist(ring2[0], ring2[ring2.length - 1]) <= tol * 2) {
4949
+ out.push(polygon(ring2));
4950
+ } else {
4951
+ out.push(p);
4952
+ }
4953
+ }
4954
+ return group(out);
4955
+ }
4956
+ function findIslands(faces) {
4957
+ if (faces.length === 0) return [];
4958
+ const adj = faceAdjacency(faces);
4959
+ const seen = /* @__PURE__ */ new Set();
4960
+ const islands = [];
4961
+ for (let i = 0; i < faces.length; i++) {
4962
+ if (seen.has(i)) continue;
4963
+ const stack = [i];
4964
+ const members = [];
4965
+ seen.add(i);
4966
+ while (stack.length) {
4967
+ const cur = stack.pop();
4968
+ members.push(cur);
4969
+ for (const n of adj[cur]) {
4970
+ if (!seen.has(n)) {
4971
+ seen.add(n);
4972
+ stack.push(n);
4973
+ }
4974
+ }
4975
+ }
4976
+ islands.push(members);
4977
+ }
4978
+ islands.sort((a, b) => b.length - a.length);
4979
+ return islands.map((m) => group(m.map((i) => faces[i])));
4980
+ }
4981
+ function radialSortFaces(faces) {
4982
+ if (faces.length === 0) return [];
4983
+ const cs = faces.map((f) => centroid(f));
4984
+ const cx = cs.reduce((s, p) => s + p.x, 0) / cs.length;
4985
+ const cy = cs.reduce((s, p) => s + p.y, 0) / cs.length;
4986
+ return faces.map((f, i) => ({ f, a: Math.atan2(cs[i].y - cy, cs[i].x - cx) })).sort((a, b) => a.a - b.a).map((x) => x.f);
4987
+ }
4988
+ function centroidSortFaces(faces) {
4989
+ return [...faces].sort((a, b) => {
4990
+ const ca = centroid(a);
4991
+ const cb = centroid(b);
4992
+ return ca.x === cb.x ? ca.y - cb.y : ca.x - cb.x;
4993
+ });
4994
+ }
4995
+ var meshing = {
4996
+ extractInnerEdges,
4997
+ extractInnerVertices,
4998
+ findContainingFace,
4999
+ splitEdges,
5000
+ urquhartFaces,
5001
+ gabrielFaces,
5002
+ relativeNeighborFaces,
5003
+ spannerFaces,
5004
+ dualFaces,
5005
+ centroidQuadrangulation,
5006
+ edgeCollapseQuadrangulation,
5007
+ splitQuadrangulation,
5008
+ spiralQuadrangulation,
5009
+ matchingQuadrangulation,
5010
+ smoothMesh,
5011
+ subdivideMesh,
5012
+ simplifyMesh,
5013
+ stochasticMerge,
5014
+ areaMerge,
5015
+ radialSortFaces,
5016
+ centroidSortFaces,
5017
+ findBreaks,
5018
+ fixBreaks,
5019
+ fixBrokenFaces,
5020
+ findIslands
5021
+ };
5022
+
5023
+ // src/construction/index.ts
5024
+ function createCircle(cx, cy, radius, segments = 64) {
5025
+ return createRegularPolygon(cx, cy, radius, Math.max(3, Math.floor(segments)));
5026
+ }
5027
+ function createRect(x, y, width2, height2) {
5028
+ return polygon([
5029
+ { x, y },
5030
+ { x: x + width2, y },
5031
+ { x: x + width2, y: y + height2 },
5032
+ { x, y: y + height2 }
5033
+ ]);
5034
+ }
5035
+ function createRegularPolygon(cx, cy, radius, sides, rotation = -Math.PI / 2) {
5036
+ const n = Math.max(3, Math.floor(sides));
5037
+ const ring2 = [];
5038
+ for (let i = 0; i < n; i++) {
5039
+ const t = rotation + i / n * Math.PI * 2;
5040
+ ring2.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius });
5041
+ }
5042
+ return polygon(ring2);
4507
5043
  }
4508
- function subdivideMesh(faces, edgeSplitRatio = 0.5) {
4509
- const t = Math.min(1, Math.max(0, edgeSplitRatio));
4510
- const out = [];
4511
- for (const f of faces) {
4512
- const ring2 = ringOf(f);
4513
- if (ring2.length < 3) continue;
4514
- const c = centroid(f);
4515
- const n = ring2.length;
4516
- const splits = [];
4517
- for (let i = 0; i < n; i++) {
4518
- const a = ring2[i];
4519
- const b = ring2[(i + 1) % n];
4520
- splits.push(lerp(a, b, t));
4521
- }
4522
- for (let i = 0; i < n; i++) {
4523
- const v = ring2[i];
4524
- const sPrev = splits[(i - 1 + n) % n];
4525
- const sNext = splits[i];
4526
- out.push(polygon([v, sNext, c, sPrev]));
4527
- }
5044
+ function createRing(cx, cy, outerRadius, innerRadius, segments = 64) {
5045
+ const outer = createCircle(cx, cy, outerRadius, segments).rings[0];
5046
+ const inner = createCircle(cx, cy, Math.min(innerRadius, outerRadius), segments).rings[0];
5047
+ return path([outer, [...inner].reverse()], true);
5048
+ }
5049
+ function createArc(cx, cy, radius, startAngle, endAngle, segments = 32) {
5050
+ const n = Math.max(2, Math.floor(segments));
5051
+ const pts = [];
5052
+ for (let i = 0; i <= n; i++) {
5053
+ const t = startAngle + (endAngle - startAngle) * i / n;
5054
+ pts.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius });
4528
5055
  }
4529
- return group(out);
5056
+ return polyline(pts);
4530
5057
  }
4531
- function simplifyMesh(faces, tolerance, preservePerimeter = true) {
4532
- if (tolerance <= 0 || faces.length === 0) return group(faces.map((f) => polygon(ringOf(f), f.rings.slice(1))));
4533
- const edgeMap = buildEdgeMap(faces);
4534
- const peri = perimeterKeys(edgeMap);
4535
- const simplified = faces.map((f) => {
4536
- const ring2 = ringOf(f);
4537
- if (ring2.length <= 3) return polygon(ring2);
4538
- if (preservePerimeter) {
4539
- const s = simplify(f, tolerance);
4540
- const sr = ringOf(s);
4541
- if (sr.length < 3) return polygon(ring2);
4542
- return polygon(sr, s.rings.slice(1));
4543
- }
4544
- return simplify(f, tolerance);
4545
- });
4546
- const all = unique(simplified.flatMap((f) => ringOf(f)));
4547
- const snapped = clusterSnap(all, Math.max(tolerance * 0.5, 1e-9));
4548
- const remap = (v) => {
4549
- let best = v;
4550
- let bestD = Infinity;
4551
- for (const s of snapped) {
4552
- const d = dist(v, s);
4553
- if (d < bestD) {
4554
- bestD = d;
4555
- best = s;
4556
- }
4557
- }
4558
- return { ...best };
4559
- };
4560
- const periVerts = /* @__PURE__ */ new Set();
4561
- if (preservePerimeter) {
4562
- for (const key4 of peri) {
4563
- const e = edgeMap.get(key4);
4564
- periVerts.add(vertKey(e.a));
4565
- periVerts.add(vertKey(e.b));
4566
- }
5058
+ function createStar(cx, cy, outerRadius, innerRadius, points = 5, rotation = -Math.PI / 2) {
5059
+ const n = Math.max(3, Math.floor(points));
5060
+ const ring2 = [];
5061
+ for (let i = 0; i < n * 2; i++) {
5062
+ const r = i % 2 === 0 ? outerRadius : innerRadius;
5063
+ const t = rotation + i / (n * 2) * Math.PI * 2;
5064
+ ring2.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r });
4567
5065
  }
4568
- return group(
4569
- simplified.map((f, i) => {
4570
- const original = ringOf(faces[i]);
4571
- const ring2 = ringOf(f).map((v, vi) => {
4572
- if (preservePerimeter && original[vi] && periVerts.has(vertKey(original[vi]))) {
4573
- return { ...original[vi] };
4574
- }
4575
- for (const ov of original) {
4576
- if (periVerts.has(vertKey(ov)) && dist(v, ov) < tolerance * 2) {
4577
- return { ...ov };
4578
- }
4579
- }
4580
- return remap(v);
4581
- });
4582
- return polygon(unique(ring2).length >= 3 ? ring2 : original);
4583
- })
4584
- );
5066
+ return polygon(ring2);
4585
5067
  }
4586
- function clusterSnap(points, tol) {
4587
- const clusters = [];
4588
- for (const p of points) {
4589
- let found = false;
4590
- for (const c of clusters) {
4591
- if (dist(p, c[0]) <= tol) {
4592
- c.push(p);
4593
- found = true;
4594
- break;
4595
- }
4596
- }
4597
- if (!found) clusters.push([p]);
5068
+ function createKochSnowflake(cx, cy, radius, iterations = 3) {
5069
+ const tri = createRegularPolygon(cx, cy, radius, 3);
5070
+ let ring2 = tri.rings[0];
5071
+ const iters = Math.max(0, Math.min(6, Math.floor(iterations)));
5072
+ for (let i = 0; i < iters; i++) {
5073
+ ring2 = kochRefine(ring2);
4598
5074
  }
4599
- return clusters.map((c) => ({
4600
- x: c.reduce((s, p) => s + p.x, 0) / c.length,
4601
- y: c.reduce((s, p) => s + p.y, 0) / c.length
4602
- }));
5075
+ return polygon(ring2);
4603
5076
  }
4604
- function stochasticMerge(faces, nClasses, seed = 1) {
4605
- if (faces.length === 0) return group([]);
4606
- const classes = Math.max(1, Math.floor(nClasses));
4607
- const rng = mulberry324(seed >>> 0);
4608
- const label = faces.map(() => Math.floor(rng() * classes));
4609
- const adj = faceAdjacency(faces);
4610
- const parent = faces.map((_, i) => i);
4611
- const find = (i) => {
4612
- let x = i;
4613
- while (parent[x] !== x) {
4614
- parent[x] = parent[parent[x]];
4615
- x = parent[x];
4616
- }
4617
- return x;
4618
- };
4619
- const unite = (a, b) => {
4620
- const ra = find(a);
4621
- const rb = find(b);
4622
- if (ra !== rb) parent[rb] = ra;
4623
- };
4624
- for (let i = 0; i < faces.length; i++) {
4625
- for (const j of adj[i]) {
4626
- if (j > i && label[i] === label[j]) unite(i, j);
5077
+ function createSponge(width2, height2, generators, thickness, smoothing, classes, seed = 1) {
5078
+ const w = Math.max(width2, 1e-9);
5079
+ const h = Math.max(height2, 1e-9);
5080
+ const nGen = Math.max(6, Math.floor(generators));
5081
+ const nClasses = Math.max(1, Math.floor(classes));
5082
+ const smoothIters = Math.max(0, Math.floor(smoothing));
5083
+ const wall = Math.max(0, thickness);
5084
+ const sites = randomInBox(nGen, 0, 0, w, h, seed);
5085
+ const cells = compoundVoronoi(sites, {
5086
+ bounds: { minX: 0, minY: 0, maxX: w, maxY: h }
5087
+ });
5088
+ const merged = stochasticMerge(cells.paths, nClasses, seed + 1);
5089
+ const pores = [];
5090
+ for (let p of merged.paths) {
5091
+ if (smoothIters > 0) p = smooth(p, smoothIters);
5092
+ if (wall > 1e-12) {
5093
+ pores.push(...buffer(p, -wall * 0.5).paths);
5094
+ } else {
5095
+ pores.push(p);
4627
5096
  }
4628
5097
  }
4629
- const comps = /* @__PURE__ */ new Map();
4630
- for (let i = 0; i < faces.length; i++) {
4631
- const r = find(i);
4632
- const list = comps.get(r) ?? [];
4633
- list.push(i);
4634
- comps.set(r, list);
4635
- }
5098
+ const frame = createRect(0, 0, w, h);
5099
+ if (pores.length === 0) return group([frame]);
5100
+ return subtractAll(frame, pores);
5101
+ }
5102
+ function randomInBox(count, minX, minY, maxX, maxY, seed) {
5103
+ const rng = mulberry326(seed >>> 0);
4636
5104
  const out = [];
4637
- for (const members of comps.values()) {
4638
- if (members.length === 1) {
4639
- out.push(faces[members[0]]);
4640
- continue;
4641
- }
4642
- const merged = dissolveFaces(members.map((i) => faces[i]));
4643
- out.push(...merged);
5105
+ for (let i = 0; i < count; i++) {
5106
+ out.push({
5107
+ x: minX + rng() * (maxX - minX),
5108
+ y: minY + rng() * (maxY - minY)
5109
+ });
4644
5110
  }
4645
- return group(out);
5111
+ return out;
4646
5112
  }
4647
- function dissolveFaces(faces) {
4648
- if (faces.length === 0) return [];
4649
- if (faces.length === 1) return [faces[0]];
4650
- try {
4651
- let acc = faces[0];
4652
- for (let i = 1; i < faces.length; i++) {
4653
- const u = union(acc, faces[i]);
4654
- if (u.paths.length === 0) continue;
4655
- acc = u.paths.reduce(
4656
- (best, p) => area(p) > area(best) ? p : best
4657
- );
4658
- }
4659
- return [acc];
4660
- } catch {
4661
- return [convexHullOf(faces.flatMap((f) => ringOf(f)))];
4662
- }
5113
+ function mulberry326(a) {
5114
+ return function() {
5115
+ let t = a += 1831565813;
5116
+ t = Math.imul(t ^ t >>> 15, t | 1);
5117
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
5118
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
5119
+ };
4663
5120
  }
4664
- function areaMerge(faces, minAreaOrOptions) {
4665
- let working = [...faces];
4666
- if (working.length === 0) return group([]);
4667
- if (typeof minAreaOrOptions === "object" && minAreaOrOptions.remainingFaces != null) {
4668
- const target = Math.max(1, Math.floor(minAreaOrOptions.remainingFaces));
4669
- while (working.length > target) {
4670
- const next = mergeSmallestAdjacent(working);
4671
- if (next.length >= working.length) break;
4672
- working = next;
4673
- }
4674
- return group(working);
5121
+ function kochRefine(ring2) {
5122
+ const out = [];
5123
+ const n = ring2.length;
5124
+ for (let i = 0; i < n; i++) {
5125
+ const a = ring2[i];
5126
+ const b = ring2[(i + 1) % n];
5127
+ const dx = b.x - a.x;
5128
+ const dy = b.y - a.y;
5129
+ const p1 = { x: a.x + dx / 3, y: a.y + dy / 3 };
5130
+ const p2 = { x: a.x + 2 * dx / 3, y: a.y + 2 * dy / 3 };
5131
+ const px = a.x + dx / 2 - dy * (Math.sqrt(3) / 6);
5132
+ const py = a.y + dy / 2 + dx * (Math.sqrt(3) / 6);
5133
+ out.push(a, p1, { x: px, y: py }, p2);
4675
5134
  }
4676
- const minArea2 = typeof minAreaOrOptions === "number" ? minAreaOrOptions : minAreaOrOptions.minArea ?? 0;
4677
- let guard = 0;
4678
- while (guard++ < working.length + 10) {
4679
- const smallIdx = working.findIndex((f) => area(f) < minArea2);
4680
- if (smallIdx < 0) break;
4681
- const next = mergeFaceIntoNeighbor(working, smallIdx);
4682
- if (!next) break;
4683
- working = next;
5135
+ return out;
5136
+ }
5137
+ var construction = {
5138
+ createCircle,
5139
+ createRect,
5140
+ createRegularPolygon,
5141
+ createRing,
5142
+ createArc,
5143
+ createStar,
5144
+ createKochSnowflake,
5145
+ createSponge
5146
+ };
5147
+
5148
+ // src/tiling/index.ts
5149
+ function squareGrid2(spacing, minX, minY, maxX, maxY) {
5150
+ const s = Math.max(spacing, 1e-9);
5151
+ const paths = [];
5152
+ for (let x = minX; x < maxX - 1e-12; x += s) {
5153
+ for (let y = minY; y < maxY - 1e-12; y += s) {
5154
+ const w = Math.min(s, maxX - x);
5155
+ const h = Math.min(s, maxY - y);
5156
+ if (w > 1e-12 && h > 1e-12) paths.push(createRect(x, y, w, h));
5157
+ }
4684
5158
  }
4685
- return group(working);
5159
+ return group(paths);
4686
5160
  }
4687
- function mergeSmallestAdjacent(faces) {
4688
- let best = -1;
4689
- let bestA = Infinity;
4690
- for (let i = 0; i < faces.length; i++) {
4691
- const a = area(faces[i]);
4692
- if (a < bestA) {
4693
- bestA = a;
4694
- best = i;
5161
+ function hexTiling(spacing, minX, minY, maxX, maxY) {
5162
+ const s = Math.max(spacing, 1e-9);
5163
+ const r = s / 2;
5164
+ const dx = s * 0.75;
5165
+ const dy = s * Math.sqrt(3) * 0.5;
5166
+ const paths = [];
5167
+ let row = 0;
5168
+ for (let y = minY + r; y <= maxY + r; y += dy, row++) {
5169
+ const x0 = minX + r + (row % 2 === 0 ? 0 : dx * 0.5);
5170
+ for (let x = x0; x <= maxX + r; x += dx) {
5171
+ paths.push(hexagon(x, y, r));
4695
5172
  }
4696
5173
  }
4697
- if (best < 0) return faces;
4698
- return mergeFaceIntoNeighbor(faces, best) ?? faces;
5174
+ return group(paths);
4699
5175
  }
4700
- function mergeFaceIntoNeighbor(faces, idx) {
4701
- const adj = faceAdjacency(faces);
4702
- const nbrs = adj[idx] ?? [];
4703
- if (nbrs.length === 0) {
4704
- return faces.filter((_, i) => i !== idx);
5176
+ function hexagon(cx, cy, r) {
5177
+ const ring2 = [];
5178
+ for (let i = 0; i < 6; i++) {
5179
+ const t = Math.PI / 180 * (60 * i - 30);
5180
+ ring2.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r });
4705
5181
  }
4706
- let bestN = nbrs[0];
4707
- let bestA = -1;
4708
- for (const n of nbrs) {
4709
- const a = area(faces[n]);
4710
- if (a > bestA) {
4711
- bestA = a;
4712
- bestN = n;
5182
+ return polygon(ring2);
5183
+ }
5184
+ function rectSubdivision(p, nx, ny) {
5185
+ const b = bounds(p);
5186
+ const cols = Math.max(1, Math.floor(nx));
5187
+ const rows = Math.max(1, Math.floor(ny));
5188
+ const dw = (b.maxX - b.minX) / cols;
5189
+ const dh = (b.maxY - b.minY) / rows;
5190
+ const paths = [];
5191
+ for (let i = 0; i < cols; i++) {
5192
+ for (let j = 0; j < rows; j++) {
5193
+ const cell = createRect(
5194
+ b.minX + i * dw,
5195
+ b.minY + j * dh,
5196
+ dw,
5197
+ dh
5198
+ );
5199
+ paths.push(...intersect(p, cell).paths);
4713
5200
  }
4714
5201
  }
4715
- const merged = dissolveFaces([faces[idx], faces[bestN]]);
4716
- const out = [];
4717
- for (let i = 0; i < faces.length; i++) {
4718
- if (i === idx || i === bestN) continue;
4719
- out.push(faces[i]);
4720
- }
4721
- out.push(...merged);
4722
- return out;
5202
+ return group(paths);
4723
5203
  }
4724
- function convexHullOf(points) {
4725
- const pts = unique(points);
4726
- if (pts.length < 3) return polygon(pts);
4727
- pts.sort((a, b) => a.x === b.x ? a.y - b.y : a.x - b.x);
4728
- const lower = [];
4729
- for (const p of pts) {
4730
- while (lower.length >= 2 && cross3(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) {
4731
- lower.pop();
5204
+ function quadSubdivision(faces, depth = 1) {
5205
+ let cur = faces;
5206
+ const d = Math.max(0, Math.floor(depth));
5207
+ for (let i = 0; i < d; i++) {
5208
+ const next = [];
5209
+ for (const f of cur) {
5210
+ next.push(...subdivideQuad(f));
4732
5211
  }
4733
- lower.push(p);
5212
+ cur = next;
4734
5213
  }
4735
- const upper = [];
4736
- for (let i = pts.length - 1; i >= 0; i--) {
4737
- const p = pts[i];
4738
- while (upper.length >= 2 && cross3(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) {
4739
- upper.pop();
5214
+ return group(cur);
5215
+ }
5216
+ function subdivideQuad(p) {
5217
+ const ring2 = p.rings[0];
5218
+ if (!ring2 || ring2.length < 4) return [p];
5219
+ const [a, b, c, d] = ring2;
5220
+ if (!a || !b || !c || !d) return [p];
5221
+ const mab = mid4(a, b);
5222
+ const mbc = mid4(b, c);
5223
+ const mcd = mid4(c, d);
5224
+ const mda = mid4(d, a);
5225
+ const cen = mid4(mid4(a, c), mid4(b, d));
5226
+ return [
5227
+ polygon([a, mab, cen, mda]),
5228
+ polygon([mab, b, mbc, cen]),
5229
+ polygon([cen, mbc, c, mcd]),
5230
+ polygon([mda, cen, mcd, d])
5231
+ ];
5232
+ }
5233
+ function triangleSubdivision(faces, depth = 1) {
5234
+ let cur = faces;
5235
+ const d = Math.max(0, Math.floor(depth));
5236
+ for (let i = 0; i < d; i++) {
5237
+ const next = [];
5238
+ for (const f of cur) {
5239
+ next.push(...subdivideTriangle(f));
4740
5240
  }
4741
- upper.push(p);
5241
+ cur = next;
4742
5242
  }
4743
- lower.pop();
4744
- upper.pop();
4745
- return polygon([...lower, ...upper]);
5243
+ return group(cur);
4746
5244
  }
4747
- function mulberry324(a) {
4748
- return () => {
4749
- a |= 0;
4750
- a = a + 1831565813 | 0;
4751
- let t = Math.imul(a ^ a >>> 15, 1 | a);
4752
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
4753
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
4754
- };
5245
+ function subdivideTriangle(p) {
5246
+ const ring2 = p.rings[0];
5247
+ if (!ring2 || ring2.length < 3) return [p];
5248
+ const a = ring2[0];
5249
+ const b = ring2[1];
5250
+ const c = ring2[2];
5251
+ const mab = mid4(a, b);
5252
+ const mbc = mid4(b, c);
5253
+ const mca = mid4(c, a);
5254
+ return [
5255
+ polygon([a, mab, mca]),
5256
+ polygon([mab, b, mbc]),
5257
+ polygon([mca, mbc, c]),
5258
+ polygon([mab, mbc, mca])
5259
+ ];
4755
5260
  }
4756
- function findBreaks(faces) {
4757
- const edgeMap = buildEdgeMap(faces);
4758
- const segs = [];
4759
- const peri = [];
4760
- for (const e of edgeMap.values()) {
4761
- if (e.faceIds.length === 1) peri.push(e);
5261
+ function sliceDivision(p, spacing, angle = 0) {
5262
+ const s = Math.max(spacing, 1e-9);
5263
+ const b = bounds(p);
5264
+ const cx = (b.minX + b.maxX) / 2;
5265
+ const cy = (b.minY + b.maxY) / 2;
5266
+ const diag = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) + s;
5267
+ const ux = Math.cos(angle);
5268
+ const uy = Math.sin(angle);
5269
+ const nx = -uy;
5270
+ const ny = ux;
5271
+ const parts = [p];
5272
+ for (let t = -diag; t <= diag; t += s) {
5273
+ const a = { x: cx + nx * t - ux * diag, y: cy + ny * t - uy * diag };
5274
+ const bb = { x: cx + nx * t + ux * diag, y: cy + ny * t + uy * diag };
5275
+ const next = [];
5276
+ for (const piece of parts) {
5277
+ const g = slice(piece, a, bb);
5278
+ next.push(...g.paths.length ? g.paths : [piece]);
5279
+ }
5280
+ parts.length = 0;
5281
+ parts.push(...next);
4762
5282
  }
4763
- const used = /* @__PURE__ */ new Set();
4764
- for (let i = 0; i < peri.length; i++) {
4765
- if (used.has(i)) continue;
4766
- const a = peri[i];
4767
- for (let j = i + 1; j < peri.length; j++) {
4768
- if (used.has(j)) continue;
4769
- const b = peri[j];
4770
- if (nearDuplicateEdge(a, b, 1e-3)) {
4771
- segs.push(polyline([a.a, a.b]));
4772
- segs.push(polyline([b.a, b.b]));
4773
- used.add(i);
4774
- used.add(j);
4775
- break;
5283
+ return group(parts);
5284
+ }
5285
+ function hatchSubdivision(p, spacing, angle = 0) {
5286
+ return sliceDivision(p, spacing, angle);
5287
+ }
5288
+ function arcDivision(width2, height2, arcs, seed = 1, circlePoints = 64) {
5289
+ const w = Math.max(width2, 1e-9);
5290
+ const h = Math.max(height2, 1e-9);
5291
+ const n = Math.max(0, Math.floor(arcs));
5292
+ const segs = Math.max(8, Math.floor(circlePoints));
5293
+ const rect = createRect(0, 0, w, h);
5294
+ if (n === 0) return group([rect]);
5295
+ const rng = mulberry327(seed >>> 0);
5296
+ let faces = [rect];
5297
+ const minArea2 = w * h * 1e-8;
5298
+ for (let i = 0; i < n; i++) {
5299
+ const { cx, cy, r } = sampleBoundaryCircle(w, h, rng);
5300
+ const disk = createCircle(cx, cy, r, segs);
5301
+ const next = [];
5302
+ for (const face of faces) {
5303
+ for (const piece of intersect(face, disk).paths) {
5304
+ if (area(piece) > minArea2) next.push(piece);
5305
+ }
5306
+ for (const piece of subtract(face, disk).paths) {
5307
+ if (area(piece) > minArea2) next.push(piece);
4776
5308
  }
4777
5309
  }
5310
+ if (next.length > 0) faces = next;
4778
5311
  }
4779
- return group(segs);
4780
- }
4781
- function nearDuplicateEdge(a, b, tol) {
4782
- const d1 = dist(a.a, b.a) + dist(a.b, b.b);
4783
- const d2 = dist(a.a, b.b) + dist(a.b, b.a);
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;
5312
+ return group(faces);
4786
5313
  }
4787
- function fixBreaks(faces, maxGapWidth) {
4788
- if (faces.length === 0) return group([]);
4789
- const tol = Math.max(maxGapWidth, 1e-9);
4790
- const verts = unique(faces.flatMap((f) => ringOf(f)));
4791
- const snapped = clusterSnap(verts, tol);
4792
- const remap = (v) => {
4793
- let best = snapped[0];
4794
- let bestD = Infinity;
4795
- for (const s of snapped) {
4796
- const d = dist(v, s);
4797
- if (d < bestD) {
4798
- bestD = d;
4799
- best = s;
5314
+ function sampleBoundaryCircle(w, h, rng) {
5315
+ const peri = 2 * (w + h);
5316
+ let t = rng() * peri;
5317
+ let cx;
5318
+ let cy;
5319
+ let side;
5320
+ if (t < w) {
5321
+ side = 0;
5322
+ cx = t;
5323
+ cy = 0;
5324
+ } else {
5325
+ t -= w;
5326
+ if (t < h) {
5327
+ side = 1;
5328
+ cx = w;
5329
+ cy = t;
5330
+ } else {
5331
+ t -= h;
5332
+ if (t < w) {
5333
+ side = 2;
5334
+ cx = w - t;
5335
+ cy = h;
5336
+ } else {
5337
+ side = 3;
5338
+ cx = 0;
5339
+ cy = h - (t - w);
4800
5340
  }
4801
5341
  }
4802
- return { ...best };
4803
- };
4804
- const fixed = faces.map((f) => {
4805
- const ring2 = unique(ringOf(f).map(remap));
4806
- return ring2.length >= 3 ? polygon(ring2) : f;
4807
- });
4808
- try {
4809
- return group(dissolveOverlaps(fixed));
4810
- } catch {
4811
- return group(fixed);
4812
5342
  }
5343
+ const distOther = [
5344
+ cy,
5345
+ // to top
5346
+ w - cx,
5347
+ // to right
5348
+ h - cy,
5349
+ // to bottom
5350
+ cx
5351
+ // to left
5352
+ ];
5353
+ let minSecond = Infinity;
5354
+ for (let s = 0; s < 4; s++) {
5355
+ if (s === side) continue;
5356
+ minSecond = Math.min(minSecond, distOther[s]);
5357
+ }
5358
+ const maxR = Math.hypot(w, h);
5359
+ const r = minSecond + rng() * Math.max(0, maxR - minSecond);
5360
+ return { cx, cy, r: Math.max(r, minSecond + 1e-6) };
4813
5361
  }
4814
- function dissolveOverlaps(faces) {
4815
- let working = [...faces];
4816
- let changed = true;
4817
- let guard = 0;
4818
- while (changed && guard++ < working.length) {
4819
- changed = false;
4820
- outer: for (let i = 0; i < working.length; i++) {
4821
- for (let j = i + 1; j < working.length; j++) {
4822
- const a = working[i];
4823
- const b = working[j];
4824
- const u = union(a, b);
4825
- const ua = u.paths.reduce((s, p) => s + area(p), 0);
4826
- if (ua < area(a) + area(b) - 1e-6) {
4827
- const next = working.filter((_, k) => k !== i && k !== j);
4828
- next.push(...u.paths);
4829
- working = next;
4830
- changed = true;
4831
- break outer;
5362
+ function mulberry327(a) {
5363
+ return function() {
5364
+ let t = a += 1831565813;
5365
+ t = Math.imul(t ^ t >>> 15, t | 1);
5366
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
5367
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
5368
+ };
5369
+ }
5370
+ function mid4(a, b) {
5371
+ return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
5372
+ }
5373
+ var tiling = {
5374
+ squareGrid: squareGrid2,
5375
+ hexTiling,
5376
+ rectSubdivision,
5377
+ quadSubdivision,
5378
+ triangleSubdivision,
5379
+ sliceDivision,
5380
+ hatchSubdivision,
5381
+ arcDivision
5382
+ };
5383
+
5384
+ // src/polygonisation/index.ts
5385
+ function maxArea(points) {
5386
+ return convexHull(points);
5387
+ }
5388
+ function minArea(points) {
5389
+ const hull2 = convexHull(points).rings[0] ?? [];
5390
+ if (hull2.length < 3) return convexHull(points);
5391
+ if (hull2.length === 3) return polygon(hull2);
5392
+ let best = null;
5393
+ let bestA = Infinity;
5394
+ const h = hull2;
5395
+ const n = h.length;
5396
+ for (let i = 0; i < n; i++) {
5397
+ for (let j = i + 1; j < n; j++) {
5398
+ for (let k = j + 1; k < n; k++) {
5399
+ const tri = polygon([h[i], h[j], h[k]]);
5400
+ if (!coversPoints(tri, points)) continue;
5401
+ const a = area(tri);
5402
+ if (a < bestA) {
5403
+ bestA = a;
5404
+ best = tri;
4832
5405
  }
4833
5406
  }
4834
5407
  }
4835
5408
  }
4836
- return working;
5409
+ return best ?? convexHull(points);
4837
5410
  }
4838
- function fixBrokenFaces(coverage, tolerance, polygonise = true) {
4839
- const tol = Math.max(tolerance, 1e-9);
4840
- const anchors = [];
4841
- const endpoints = [];
4842
- coverage.forEach((p, pi) => {
4843
- const ring2 = ringOf(p);
4844
- if (ring2.length === 0) return;
4845
- if (p.closed) {
4846
- for (const v of ring2) anchors.push(v);
4847
- } else {
4848
- endpoints.push({ pathIdx: pi, end: 0, v: ring2[0] });
4849
- endpoints.push({
4850
- pathIdx: pi,
4851
- end: 1,
4852
- v: ring2[ring2.length - 1]
4853
- });
4854
- }
4855
- });
4856
- const snapTargets = clusterSnap(
4857
- [...anchors, ...endpoints.map((e) => e.v)],
4858
- tol
4859
- );
4860
- const snapTo = (v, preferAnchor) => {
4861
- let best = v;
5411
+ function minPerimeter(points) {
5412
+ if (points.length === 0) return polygon([]);
5413
+ if (points.length < 3) return polygon(points);
5414
+ const remaining = points.map((p) => ({ ...p }));
5415
+ const ordered = [remaining.shift()];
5416
+ while (remaining.length) {
5417
+ const last = ordered[ordered.length - 1];
5418
+ let bestI = 0;
4862
5419
  let bestD = Infinity;
4863
- for (const s of snapTargets) {
4864
- const d = dist(v, s);
4865
- if (d <= tol && d < bestD) {
5420
+ for (let i = 0; i < remaining.length; i++) {
5421
+ const d = Math.hypot(
5422
+ remaining[i].x - last.x,
5423
+ remaining[i].y - last.y
5424
+ );
5425
+ if (d < bestD) {
4866
5426
  bestD = d;
4867
- best = s;
4868
- }
4869
- }
4870
- {
4871
- for (const a of anchors) {
4872
- if (dist(v, a) <= tol) return { ...a };
5427
+ bestI = i;
4873
5428
  }
4874
5429
  }
4875
- return { ...best };
4876
- };
4877
- const snappedPaths = coverage.map((p) => {
4878
- const ring2 = ringOf(p);
4879
- if (ring2.length === 0) return p;
4880
- if (p.closed) return p;
4881
- const next = ring2.map((v) => ({ ...v }));
4882
- next[0] = snapTo(ring2[0]);
4883
- next[next.length - 1] = snapTo(ring2[ring2.length - 1]);
4884
- return polyline(next);
4885
- });
4886
- if (!polygonise) return group(snappedPaths);
4887
- const out = [];
4888
- for (const p of snappedPaths) {
4889
- const ring2 = ringOf(p);
4890
- if (p.closed) {
4891
- out.push(p);
4892
- continue;
4893
- }
4894
- if (ring2.length >= 3 && same2(ring2[0], ring2[ring2.length - 1], tol * 2)) {
4895
- out.push(polygon(ring2.slice(0, -1)));
4896
- } else if (ring2.length >= 3 && dist(ring2[0], ring2[ring2.length - 1]) <= tol * 2) {
4897
- out.push(polygon(ring2));
4898
- } else {
4899
- out.push(p);
4900
- }
5430
+ ordered.push(remaining.splice(bestI, 1)[0]);
4901
5431
  }
4902
- return group(out);
5432
+ return polygon(ordered);
4903
5433
  }
4904
- function findIslands(faces) {
4905
- if (faces.length === 0) return [];
4906
- const adj = faceAdjacency(faces);
4907
- const seen = /* @__PURE__ */ new Set();
4908
- const islands = [];
4909
- for (let i = 0; i < faces.length; i++) {
4910
- if (seen.has(i)) continue;
4911
- const stack = [i];
4912
- const members = [];
4913
- seen.add(i);
4914
- while (stack.length) {
4915
- const cur = stack.pop();
4916
- members.push(cur);
4917
- for (const n of adj[cur]) {
4918
- if (!seen.has(n)) {
4919
- seen.add(n);
4920
- stack.push(n);
4921
- }
4922
- }
4923
- }
4924
- islands.push(members);
5434
+ function horizontal(points) {
5435
+ const pts = [...points].sort(
5436
+ (a, b) => a.y === b.y ? a.x - b.x : a.y - b.y
5437
+ );
5438
+ return polygon(pts);
5439
+ }
5440
+ function vertical(points) {
5441
+ const pts = [...points].sort(
5442
+ (a, b) => a.x === b.x ? a.y - b.y : a.x - b.x
5443
+ );
5444
+ return polygon(pts);
5445
+ }
5446
+ function angular(points) {
5447
+ if (points.length === 0) return polygon([]);
5448
+ const c = centroidOf(points);
5449
+ const pts = [...points].sort(
5450
+ (a, b) => Math.atan2(a.y - c.y, a.x - c.x) - Math.atan2(b.y - c.y, b.x - c.x)
5451
+ );
5452
+ return polygon(pts);
5453
+ }
5454
+ function circular(points) {
5455
+ return angular(points);
5456
+ }
5457
+ function onion(points) {
5458
+ const layers = onionLayers(points);
5459
+ return layers[0] ?? polygon([]);
5460
+ }
5461
+ function onionLayers(points) {
5462
+ let remaining = points.map((p) => ({ ...p }));
5463
+ const layers = [];
5464
+ while (remaining.length >= 3) {
5465
+ const hull2 = convexHull(remaining);
5466
+ const ring2 = hull2.rings[0] ?? [];
5467
+ if (ring2.length < 3) break;
5468
+ layers.push(hull2);
5469
+ const onHull = new Set(ring2.map((v) => `${v.x},${v.y}`));
5470
+ remaining = remaining.filter((p) => !onHull.has(`${p.x},${p.y}`));
4925
5471
  }
4926
- islands.sort((a, b) => b.length - a.length);
4927
- return islands.map((m) => group(m.map((i) => faces[i])));
5472
+ return layers;
4928
5473
  }
4929
- function radialSortFaces(faces) {
4930
- if (faces.length === 0) return [];
4931
- const cs = faces.map((f) => centroid(f));
4932
- const cx = cs.reduce((s, p) => s + p.x, 0) / cs.length;
4933
- const cy = cs.reduce((s, p) => s + p.y, 0) / cs.length;
4934
- return faces.map((f, i) => ({ f, a: Math.atan2(cs[i].y - cy, cs[i].x - cx) })).sort((a, b) => a.a - b.a).map((x) => x.f);
5474
+ function hilbert(points) {
5475
+ return polygon(hilbertSort(points));
4935
5476
  }
4936
- function centroidSortFaces(faces) {
4937
- return [...faces].sort((a, b) => {
4938
- const ca = centroid(a);
4939
- const cb = centroid(b);
4940
- return ca.x === cb.x ? ca.y - cb.y : ca.x - cb.x;
4941
- });
5477
+ function coversPoints(poly, points) {
5478
+ const r = poly.rings[0];
5479
+ if (!r || r.length < 3) return false;
5480
+ const a = r[0];
5481
+ const b = r[1];
5482
+ const c = r[2];
5483
+ for (const p of points) {
5484
+ if (!pointInTriangle(p, a, b, c)) return false;
5485
+ }
5486
+ return true;
4942
5487
  }
4943
- var meshing = {
4944
- extractInnerEdges,
4945
- extractInnerVertices,
4946
- findContainingFace,
4947
- splitEdges,
4948
- urquhartFaces,
4949
- gabrielFaces,
4950
- relativeNeighborFaces,
4951
- spannerFaces,
4952
- dualFaces,
4953
- centroidQuadrangulation,
4954
- edgeCollapseQuadrangulation,
4955
- splitQuadrangulation,
4956
- spiralQuadrangulation,
4957
- matchingQuadrangulation,
4958
- smoothMesh,
4959
- subdivideMesh,
4960
- simplifyMesh,
4961
- stochasticMerge,
4962
- areaMerge,
4963
- radialSortFaces,
4964
- centroidSortFaces,
4965
- findBreaks,
4966
- fixBreaks,
4967
- fixBrokenFaces,
4968
- findIslands
5488
+ function pointInTriangle(p, a, b, c) {
5489
+ const v0x = c.x - a.x;
5490
+ const v0y = c.y - a.y;
5491
+ const v1x = b.x - a.x;
5492
+ const v1y = b.y - a.y;
5493
+ const v2x = p.x - a.x;
5494
+ const v2y = p.y - a.y;
5495
+ const dot00 = v0x * v0x + v0y * v0y;
5496
+ const dot01 = v0x * v1x + v0y * v1y;
5497
+ const dot02 = v0x * v2x + v0y * v2y;
5498
+ const dot11 = v1x * v1x + v1y * v1y;
5499
+ const dot12 = v1x * v2x + v1y * v2y;
5500
+ const inv = 1 / (dot00 * dot11 - dot01 * dot01 || 1e-18);
5501
+ const u = (dot11 * dot02 - dot01 * dot12) * inv;
5502
+ const v = (dot00 * dot12 - dot01 * dot02) * inv;
5503
+ return u >= -1e-9 && v >= -1e-9 && u + v <= 1 + 1e-9;
5504
+ }
5505
+ function centroidOf(points) {
5506
+ let sx = 0;
5507
+ let sy = 0;
5508
+ for (const p of points) {
5509
+ sx += p.x;
5510
+ sy += p.y;
5511
+ }
5512
+ return { x: sx / points.length, y: sy / points.length };
5513
+ }
5514
+ var polygonisation = {
5515
+ maxArea,
5516
+ minArea,
5517
+ minPerimeter,
5518
+ horizontal,
5519
+ vertical,
5520
+ angular,
5521
+ circular,
5522
+ onion,
5523
+ onionLayers,
5524
+ hilbert
4969
5525
  };
4970
5526
 
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 };
5527
+ export { angular, arcDivision, 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, createSponge, 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, findShortestTour, fixBreaks, fixBrokenFaces, flatten, flipHorizontal, flipVertical, fromArray, fromContours, frontChainPack, gabrielFaces, generateRandomGridPoints, generateRandomPoints, group, groupFromPaths, hatch, concentric as hatchConcentric, cross as hatchCross, parallel as hatchParallel, perpendicular as hatchPerpendicular, stochastic as hatchStochastic, hatchSubdivision, weave as hatchWeave, height, hexGrid, hexLatticePack, hexTiling, hilbert as hilbertPolygonise, hilbertSort, 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, perpendicularPathSegments, 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, stochasticSegments, straightSkeleton, straightSkeletonParts, subdivideMesh, subtract, subtractAll, symDifference, tiling, toArray, toContours, toSvg, transformation, translate, translateCentroidTo, translateCornerTo, translateToOrigin, triangleSubdivision, triangulation, union, unionAll, urquhartFaces, vec2, voronoi, weaveSegments, width };
4972
5528
  //# sourceMappingURL=index.js.map
4973
5529
  //# sourceMappingURL=index.js.map