pattapatta 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +98 -0
  4. package/dist/circle-BDbKfrdR.d.ts +12 -0
  5. package/dist/circlePacking/index.d.ts +78 -0
  6. package/dist/circlePacking/index.js +381 -0
  7. package/dist/circlePacking/index.js.map +1 -0
  8. package/dist/cli.js +365 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/construction/index.d.ts +26 -0
  11. package/dist/construction/index.js +102 -0
  12. package/dist/construction/index.js.map +1 -0
  13. package/dist/contour/index.d.ts +20 -0
  14. package/dist/contour/index.js +118 -0
  15. package/dist/contour/index.js.map +1 -0
  16. package/dist/conversion/index.d.ts +31 -0
  17. package/dist/conversion/index.js +99 -0
  18. package/dist/conversion/index.js.map +1 -0
  19. package/dist/group-BNjVVYSQ.d.ts +13 -0
  20. package/dist/hatch/index.d.ts +41 -0
  21. package/dist/hatch/index.js +253 -0
  22. package/dist/hatch/index.js.map +1 -0
  23. package/dist/hull/index.d.ts +16 -0
  24. package/dist/hull/index.js +97 -0
  25. package/dist/hull/index.js.map +1 -0
  26. package/dist/index.d.ts +67 -0
  27. package/dist/index.js +3933 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/meshing/index.d.ts +125 -0
  30. package/dist/meshing/index.js +1333 -0
  31. package/dist/meshing/index.js.map +1 -0
  32. package/dist/morphology/index.d.ts +58 -0
  33. package/dist/morphology/index.js +268 -0
  34. package/dist/morphology/index.js.map +1 -0
  35. package/dist/optimisation/index.d.ts +43 -0
  36. package/dist/optimisation/index.js +400 -0
  37. package/dist/optimisation/index.js.map +1 -0
  38. package/dist/path-0RjhYjJs.d.ts +21 -0
  39. package/dist/pointSet/index.d.ts +24 -0
  40. package/dist/pointSet/index.js +139 -0
  41. package/dist/pointSet/index.js.map +1 -0
  42. package/dist/polygonisation/index.d.ts +44 -0
  43. package/dist/polygonisation/index.js +310 -0
  44. package/dist/polygonisation/index.js.map +1 -0
  45. package/dist/predicates/index.d.ts +35 -0
  46. package/dist/predicates/index.js +159 -0
  47. package/dist/predicates/index.js.map +1 -0
  48. package/dist/processing/index.d.ts +54 -0
  49. package/dist/processing/index.js +457 -0
  50. package/dist/processing/index.js.map +1 -0
  51. package/dist/segment-BKuIFMKD.d.ts +10 -0
  52. package/dist/segmentSet/index.d.ts +25 -0
  53. package/dist/segmentSet/index.js +109 -0
  54. package/dist/segmentSet/index.js.map +1 -0
  55. package/dist/shapeBoolean/index.d.ts +41 -0
  56. package/dist/shapeBoolean/index.js +211 -0
  57. package/dist/shapeBoolean/index.js.map +1 -0
  58. package/dist/svg/index.d.ts +38 -0
  59. package/dist/svg/index.js +317 -0
  60. package/dist/svg/index.js.map +1 -0
  61. package/dist/tiling/index.d.ts +32 -0
  62. package/dist/tiling/index.js +344 -0
  63. package/dist/tiling/index.js.map +1 -0
  64. package/dist/transformation/index.d.ts +37 -0
  65. package/dist/transformation/index.js +184 -0
  66. package/dist/transformation/index.js.map +1 -0
  67. package/dist/triangulation/index.d.ts +48 -0
  68. package/dist/triangulation/index.js +334 -0
  69. package/dist/triangulation/index.js.map +1 -0
  70. package/dist/vec2-CWyXN1Wj.d.ts +10 -0
  71. package/dist/voronoi/index.d.ts +31 -0
  72. package/dist/voronoi/index.js +211 -0
  73. package/dist/voronoi/index.js.map +1 -0
  74. package/package.json +165 -0
@@ -0,0 +1,159 @@
1
+ import { FillRule, areaD, pointInPolygonD, PointInPolygonResult } from 'clipper2-ts';
2
+
3
+ // src/types/vec2.ts
4
+ function vec2(x, y) {
5
+ return { x, y };
6
+ }
7
+ function cloneVec2(v) {
8
+ return { x: v.x, y: v.y };
9
+ }
10
+ function equalsVec2(a, b, eps = 1e-9) {
11
+ return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps;
12
+ }
13
+
14
+ // src/types/path.ts
15
+ function normalizeRing(ring, eps = 1e-9) {
16
+ if (ring.length < 2) return ring.map(cloneVec2);
17
+ const first = ring[0];
18
+ const last = ring[ring.length - 1];
19
+ if (first && last && equalsVec2(first, last, eps)) {
20
+ return ring.slice(0, -1).map(cloneVec2);
21
+ }
22
+ return ring.map(cloneVec2);
23
+ }
24
+ FillRule.NonZero;
25
+ function ringToPathD(ring) {
26
+ const pts = [];
27
+ for (const v of normalizeRing(ring)) {
28
+ pts.push({ x: v.x, y: v.y, z: 0 });
29
+ }
30
+ return pts;
31
+ }
32
+ function pathToPathsD(p) {
33
+ if (!p.closed || p.rings.length === 0) {
34
+ throw new Error("Boolean ops require a closed path with at least one ring");
35
+ }
36
+ return p.rings.map((ring) => ringToPathD(ring));
37
+ }
38
+ function pointInRing(point, ring) {
39
+ const result = pointInPolygonD(
40
+ { x: point.x, y: point.y, z: 0 },
41
+ ringToPathD(ring)
42
+ );
43
+ return result === PointInPolygonResult.IsInside || result === PointInPolygonResult.IsOn;
44
+ }
45
+ function pathsArea(paths) {
46
+ let sum = 0;
47
+ for (const p of paths) {
48
+ sum += Math.abs(areaD(p));
49
+ }
50
+ return sum;
51
+ }
52
+
53
+ // src/predicates/index.ts
54
+ function ringArea(ring) {
55
+ if (ring.length < 3) return 0;
56
+ let sum = 0;
57
+ for (let i = 0; i < ring.length; i++) {
58
+ const a = ring[i];
59
+ const b = ring[(i + 1) % ring.length];
60
+ sum += a.x * b.y - b.x * a.y;
61
+ }
62
+ return sum / 2;
63
+ }
64
+ function area(p) {
65
+ if (!p.closed || p.rings.length === 0) return 0;
66
+ try {
67
+ return pathsArea(pathToPathsD(p));
68
+ } catch {
69
+ let total = 0;
70
+ for (let i = 0; i < p.rings.length; i++) {
71
+ const a = Math.abs(ringArea(p.rings[i]));
72
+ total += i === 0 ? a : -a;
73
+ }
74
+ return Math.max(0, total);
75
+ }
76
+ }
77
+ function areaGroup(g) {
78
+ return g.paths.reduce((sum, p) => sum + area(p), 0);
79
+ }
80
+ function centroid(p) {
81
+ const ring = p.rings[0];
82
+ if (!ring || ring.length === 0) return vec2(0, 0);
83
+ const a = ringArea(ring);
84
+ if (Math.abs(a) < 1e-12) {
85
+ let sx = 0;
86
+ let sy = 0;
87
+ for (const v of ring) {
88
+ sx += v.x;
89
+ sy += v.y;
90
+ }
91
+ return vec2(sx / ring.length, sy / ring.length);
92
+ }
93
+ let cx = 0;
94
+ let cy = 0;
95
+ for (let i = 0; i < ring.length; i++) {
96
+ const p0 = ring[i];
97
+ const p1 = ring[(i + 1) % ring.length];
98
+ const cross = p0.x * p1.y - p1.x * p0.y;
99
+ cx += (p0.x + p1.x) * cross;
100
+ cy += (p0.y + p1.y) * cross;
101
+ }
102
+ return vec2(cx / (6 * a), cy / (6 * a));
103
+ }
104
+ function containsPoint(p, point) {
105
+ if (!p.closed || !p.rings[0]) return false;
106
+ if (!pointInRing(point, p.rings[0])) return false;
107
+ for (let i = 1; i < p.rings.length; i++) {
108
+ const hole = p.rings[i];
109
+ if (hole && pointInRing(point, hole)) return false;
110
+ }
111
+ return true;
112
+ }
113
+ function bounds(p) {
114
+ let minX = Infinity;
115
+ let minY = Infinity;
116
+ let maxX = -Infinity;
117
+ let maxY = -Infinity;
118
+ for (const ring of p.rings) {
119
+ for (const v of ring) {
120
+ minX = Math.min(minX, v.x);
121
+ minY = Math.min(minY, v.y);
122
+ maxX = Math.max(maxX, v.x);
123
+ maxY = Math.max(maxY, v.y);
124
+ }
125
+ }
126
+ if (!Number.isFinite(minX)) {
127
+ return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
128
+ }
129
+ return { minX, minY, maxX, maxY };
130
+ }
131
+ function width(p) {
132
+ const b = bounds(p);
133
+ return b.maxX - b.minX;
134
+ }
135
+ function height(p) {
136
+ const b = bounds(p);
137
+ return b.maxY - b.minY;
138
+ }
139
+ function areaSimilarity(a, b) {
140
+ const aa = area(a);
141
+ const bb = area(b);
142
+ const denom = Math.max(aa, bb, 1e-12);
143
+ return 1 - Math.abs(aa - bb) / denom;
144
+ }
145
+ var predicates = {
146
+ area,
147
+ areaGroup,
148
+ ringArea,
149
+ centroid,
150
+ containsPoint,
151
+ bounds,
152
+ width,
153
+ height,
154
+ areaSimilarity
155
+ };
156
+
157
+ export { area, areaGroup, areaSimilarity, bounds, centroid, containsPoint, height, predicates, ringArea, width };
158
+ //# sourceMappingURL=index.js.map
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/clipper/convert.ts","../../src/predicates/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** 2D point / vector. */\nexport type Vec2 = {\n x: number\n y: number\n}\n\nexport function vec2(x: number, y: number): Vec2 {\n return { x, y }\n}\n\nexport function cloneVec2(v: Vec2): Vec2 {\n return { x: v.x, y: v.y }\n}\n\nexport function equalsVec2(a: Vec2, b: Vec2, eps = 1e-9): boolean {\n return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps\n}\n","import type { Vec2 } from './vec2.js'\nimport { cloneVec2, equalsVec2 } from './vec2.js'\n\n/** Ordered ring of vertices (typically without repeating the closing vertex). */\nexport type Ring = Vec2[]\n\n/**\n * A path: one exterior ring plus optional holes, or an open polyline.\n * `rings[0]` is the exterior (or the open polyline when `closed` is false).\n * Additional rings are holes (only meaningful when `closed` is true).\n */\nexport type Path = {\n rings: Ring[]\n closed: boolean\n}\n\nexport function path(rings: Ring[], closed = true): Path {\n return {\n rings: rings.map((ring) => ring.map(cloneVec2)),\n closed,\n }\n}\n\nexport function polyline(points: Ring): Path {\n return path([points], false)\n}\n\nexport function polygon(exterior: Ring, holes: Ring[] = []): Path {\n return path([exterior, ...holes], true)\n}\n\n/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */\nexport function normalizeRing(ring: Ring, eps = 1e-9): Ring {\n if (ring.length < 2) return ring.map(cloneVec2)\n const first = ring[0]\n const last = ring[ring.length - 1]\n if (first && last && equalsVec2(first, last, eps)) {\n return ring.slice(0, -1).map(cloneVec2)\n }\n return ring.map(cloneVec2)\n}\n\nexport function clonePath(p: Path): Path {\n return {\n closed: p.closed,\n rings: p.rings.map((ring) => ring.map(cloneVec2)),\n }\n}\n","import {\n FillRule,\n areaD,\n differenceD,\n intersectD,\n isPositiveD,\n pointInPolygonD,\n PointInPolygonResult,\n unionD,\n xorD,\n type PathD,\n type PathsD,\n type PointD,\n} from 'clipper2-ts'\nimport type { Group, Path, Ring, Vec2 } from '../types/index.js'\nimport { group, normalizeRing, path, vec2 } from '../types/index.js'\n\nexport const DEFAULT_FILL_RULE = FillRule.NonZero\n\nexport function ringToPathD(ring: Ring): PathD {\n const pts: PointD[] = []\n for (const v of normalizeRing(ring)) {\n pts.push({ x: v.x, y: v.y, z: 0 })\n }\n return pts\n}\n\n/** Closed path → Clipper subject/clip paths (exterior + holes). */\nexport function pathToPathsD(p: Path): PathsD {\n if (!p.closed || p.rings.length === 0) {\n throw new Error('Boolean ops require a closed path with at least one ring')\n }\n return p.rings.map((ring) => ringToPathD(ring))\n}\n\nexport function pathsDToGroup(paths: PathsD): Group {\n if (paths.length === 0) return group([])\n\n const outers: { ring: Ring; holes: Ring[] }[] = []\n const holes: Ring[] = []\n\n for (const pd of paths) {\n const ring = pathDToRing(pd)\n if (ring.length < 3) continue\n if (isPositiveD(pd)) {\n outers.push({ ring, holes: [] })\n } else {\n holes.push(ring)\n }\n }\n\n for (const hole of holes) {\n const sample = hole[0]\n if (!sample) continue\n let assigned = false\n for (const outer of outers) {\n if (pointInRing(sample, outer.ring)) {\n outer.holes.push(hole)\n assigned = true\n break\n }\n }\n if (!assigned) {\n // Treat unmatched negative rings as their own outers (orientation flip).\n outers.push({ ring: [...hole].reverse(), holes: [] })\n }\n }\n\n return group(outers.map((o) => path([o.ring, ...o.holes], true)))\n}\n\nexport function pathDToRing(pd: PathD): Ring {\n return normalizeRing(pd.map((p) => vec2(p.x, p.y)))\n}\n\nexport function pointInRing(point: Vec2, ring: Ring): boolean {\n const result = pointInPolygonD(\n { x: point.x, y: point.y, z: 0 },\n ringToPathD(ring),\n )\n return (\n result === PointInPolygonResult.IsInside ||\n result === PointInPolygonResult.IsOn\n )\n}\n\nexport function clipUnion(a: PathsD, b: PathsD): PathsD {\n return unionD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipIntersect(a: PathsD, b: PathsD): PathsD {\n return intersectD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipDifference(a: PathsD, b: PathsD): PathsD {\n return differenceD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipXor(a: PathsD, b: PathsD): PathsD {\n return xorD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function pathsArea(paths: PathsD): number {\n let sum = 0\n for (const p of paths) {\n sum += Math.abs(areaD(p))\n }\n return sum\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { vec2 } from '../types/index.js'\nimport {\n pathToPathsD,\n pathsArea,\n pointInRing,\n} from '../clipper/convert.js'\n\n/** Signed area of a closed ring (shoelace). */\nexport function ringArea(ring: Vec2[]): number {\n if (ring.length < 3) return 0\n let sum = 0\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n sum += a.x * b.y - b.x * a.y\n }\n return sum / 2\n}\n\n/** Absolute area of a closed path (exterior minus holes). */\nexport function area(p: Path): number {\n if (!p.closed || p.rings.length === 0) return 0\n try {\n return pathsArea(pathToPathsD(p))\n } catch {\n let total = 0\n for (let i = 0; i < p.rings.length; i++) {\n const a = Math.abs(ringArea(p.rings[i]!))\n total += i === 0 ? a : -a\n }\n return Math.max(0, total)\n }\n}\n\nexport function areaGroup(g: Group): number {\n return g.paths.reduce((sum, p) => sum + area(p), 0)\n}\n\n/** Centroid of a closed exterior ring (ignores holes for Phase 2). */\nexport function centroid(p: Path): Vec2 {\n const ring = p.rings[0]\n if (!ring || ring.length === 0) return vec2(0, 0)\n const a = ringArea(ring)\n if (Math.abs(a) < 1e-12) {\n let sx = 0\n let sy = 0\n for (const v of ring) {\n sx += v.x\n sy += v.y\n }\n return vec2(sx / ring.length, sy / ring.length)\n }\n let cx = 0\n let cy = 0\n for (let i = 0; i < ring.length; i++) {\n const p0 = ring[i]!\n const p1 = ring[(i + 1) % ring.length]!\n const cross = p0.x * p1.y - p1.x * p0.y\n cx += (p0.x + p1.x) * cross\n cy += (p0.y + p1.y) * cross\n }\n return vec2(cx / (6 * a), cy / (6 * a))\n}\n\nexport function containsPoint(p: Path, point: Vec2): boolean {\n if (!p.closed || !p.rings[0]) return false\n if (!pointInRing(point, p.rings[0])) return false\n for (let i = 1; i < p.rings.length; i++) {\n const hole = p.rings[i]\n if (hole && pointInRing(point, hole)) return false\n }\n return true\n}\n\nexport function bounds(p: Path): {\n minX: number\n minY: number\n maxX: number\n maxY: number\n} {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const ring of p.rings) {\n for (const v of ring) {\n minX = Math.min(minX, v.x)\n minY = Math.min(minY, v.y)\n maxX = Math.max(maxX, v.x)\n maxY = Math.max(maxY, v.y)\n }\n }\n if (!Number.isFinite(minX)) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 }\n }\n return { minX, minY, maxX, maxY }\n}\n\nexport function width(p: Path): number {\n const b = bounds(p)\n return b.maxX - b.minX\n}\n\nexport function height(p: Path): number {\n const b = bounds(p)\n return b.maxY - b.minY\n}\n\n/** Rough polygon similarity via absolute area difference relative to max area. */\nexport function areaSimilarity(a: Path, b: Path): number {\n const aa = area(a)\n const bb = area(b)\n const denom = Math.max(aa, bb, 1e-12)\n return 1 - Math.abs(aa - bb) / denom\n}\n\nexport const predicates = {\n area,\n areaGroup,\n ringArea,\n centroid,\n containsPoint,\n bounds,\n width,\n height,\n areaSimilarity,\n}\n"]}
@@ -0,0 +1,54 @@
1
+ import { V as Vec2 } from '../vec2-CWyXN1Wj.js';
2
+ import { S as Segment } from '../segment-BKuIFMKD.js';
3
+ import { P as Path } from '../path-0RjhYjJs.js';
4
+ import { G as Group } from '../group-BNjVVYSQ.js';
5
+
6
+ /** Exterior ring as its own closed path (holes dropped). */
7
+ declare function extractPerimeter(p: Path): Path;
8
+ /** Alias for extractPerimeter. */
9
+ declare function extractBoundary(p: Path): Path;
10
+ /** Each hole as a separate closed path. */
11
+ declare function extractHoles(p: Path): Path[];
12
+ /**
13
+ * Insert vertices so no edge exceeds `maxSegLen`.
14
+ * Closed rings treat the wrap-around edge.
15
+ */
16
+ declare function densify(p: Path, maxSegLen: number): Path;
17
+ /** Drop holes whose absolute area is below `minArea`. */
18
+ declare function removeSmallHoles(p: Path, minArea: number): Path;
19
+ /** Seeded uniform random points inside a closed path. */
20
+ declare function generateRandomPoints(p: Path, count: number, seed?: number): Vec2[];
21
+ /** Axis-aligned grid samples that fall inside the path. */
22
+ declare function generateRandomGridPoints(p: Path, spacing: number): Vec2[];
23
+ /** Evenly spaced samples along the exterior perimeter. */
24
+ declare function pointsOnExterior(p: Path, count: number): Vec2[];
25
+ /** Exterior edges as segments. */
26
+ declare function segmentsOnExterior(p: Path): Segment[];
27
+ /**
28
+ * Cut `p` with the infinite line through `a`→`b`.
29
+ * Returns pieces on both sides (typically 0–2 paths).
30
+ */
31
+ declare function slice(p: Path, a: Vec2, b: Vec2): Group;
32
+ /** Split through the centroid with a vertical cut (left/right). */
33
+ declare function centroidSplit(p: Path): Group;
34
+ /** Union all closed paths in a group (dissolve overlapping contours). */
35
+ declare function dissolve(paths: Path[]): Group;
36
+ /** Vertex–edge intersection points between two path boundaries. */
37
+ declare function intersectionPoints(a: Path, b: Path): Vec2[];
38
+ declare const processing: {
39
+ extractPerimeter: typeof extractPerimeter;
40
+ extractBoundary: typeof extractBoundary;
41
+ extractHoles: typeof extractHoles;
42
+ densify: typeof densify;
43
+ removeSmallHoles: typeof removeSmallHoles;
44
+ generateRandomPoints: typeof generateRandomPoints;
45
+ generateRandomGridPoints: typeof generateRandomGridPoints;
46
+ pointsOnExterior: typeof pointsOnExterior;
47
+ segmentsOnExterior: typeof segmentsOnExterior;
48
+ slice: typeof slice;
49
+ centroidSplit: typeof centroidSplit;
50
+ dissolve: typeof dissolve;
51
+ intersectionPoints: typeof intersectionPoints;
52
+ };
53
+
54
+ export { centroidSplit, densify, dissolve, extractBoundary, extractHoles, extractPerimeter, generateRandomGridPoints, generateRandomPoints, intersectionPoints, pointsOnExterior, processing, removeSmallHoles, segmentsOnExterior, slice };
@@ -0,0 +1,457 @@
1
+ import { FillRule, pointInPolygonD, PointInPolygonResult, intersectD, isPositiveD, unionD } from 'clipper2-ts';
2
+
3
+ // src/types/vec2.ts
4
+ function vec2(x, y) {
5
+ return { x, y };
6
+ }
7
+ function cloneVec2(v) {
8
+ return { x: v.x, y: v.y };
9
+ }
10
+ function equalsVec2(a, b, eps = 1e-9) {
11
+ return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps;
12
+ }
13
+
14
+ // src/types/segment.ts
15
+ function segment(a, b) {
16
+ return { a, b };
17
+ }
18
+
19
+ // src/types/path.ts
20
+ function path(rings, closed = true) {
21
+ return {
22
+ rings: rings.map((ring) => ring.map(cloneVec2)),
23
+ closed
24
+ };
25
+ }
26
+ function polygon(exterior, holes = []) {
27
+ return path([exterior, ...holes], true);
28
+ }
29
+ function normalizeRing(ring, eps = 1e-9) {
30
+ if (ring.length < 2) return ring.map(cloneVec2);
31
+ const first = ring[0];
32
+ const last = ring[ring.length - 1];
33
+ if (first && last && equalsVec2(first, last, eps)) {
34
+ return ring.slice(0, -1).map(cloneVec2);
35
+ }
36
+ return ring.map(cloneVec2);
37
+ }
38
+ function clonePath(p) {
39
+ return {
40
+ closed: p.closed,
41
+ rings: p.rings.map((ring) => ring.map(cloneVec2))
42
+ };
43
+ }
44
+
45
+ // src/types/group.ts
46
+ function group(paths = []) {
47
+ return { paths: paths.map(clonePath) };
48
+ }
49
+ var DEFAULT_FILL_RULE = FillRule.NonZero;
50
+ function ringToPathD(ring) {
51
+ const pts = [];
52
+ for (const v of normalizeRing(ring)) {
53
+ pts.push({ x: v.x, y: v.y, z: 0 });
54
+ }
55
+ return pts;
56
+ }
57
+ function pathToPathsD(p) {
58
+ if (!p.closed || p.rings.length === 0) {
59
+ throw new Error("Boolean ops require a closed path with at least one ring");
60
+ }
61
+ return p.rings.map((ring) => ringToPathD(ring));
62
+ }
63
+ function pathsDToGroup(paths) {
64
+ if (paths.length === 0) return group([]);
65
+ const outers = [];
66
+ const holes = [];
67
+ for (const pd of paths) {
68
+ const ring = pathDToRing(pd);
69
+ if (ring.length < 3) continue;
70
+ if (isPositiveD(pd)) {
71
+ outers.push({ ring, holes: [] });
72
+ } else {
73
+ holes.push(ring);
74
+ }
75
+ }
76
+ for (const hole of holes) {
77
+ const sample = hole[0];
78
+ if (!sample) continue;
79
+ let assigned = false;
80
+ for (const outer of outers) {
81
+ if (pointInRing(sample, outer.ring)) {
82
+ outer.holes.push(hole);
83
+ assigned = true;
84
+ break;
85
+ }
86
+ }
87
+ if (!assigned) {
88
+ outers.push({ ring: [...hole].reverse(), holes: [] });
89
+ }
90
+ }
91
+ return group(outers.map((o) => path([o.ring, ...o.holes], true)));
92
+ }
93
+ function pathDToRing(pd) {
94
+ return normalizeRing(pd.map((p) => vec2(p.x, p.y)));
95
+ }
96
+ function pointInRing(point, ring) {
97
+ const result = pointInPolygonD(
98
+ { x: point.x, y: point.y, z: 0 },
99
+ ringToPathD(ring)
100
+ );
101
+ return result === PointInPolygonResult.IsInside || result === PointInPolygonResult.IsOn;
102
+ }
103
+ function clipUnion(a, b) {
104
+ return unionD(a, b, DEFAULT_FILL_RULE);
105
+ }
106
+ function clipIntersect(a, b) {
107
+ return intersectD(a, b, DEFAULT_FILL_RULE);
108
+ }
109
+
110
+ // src/predicates/index.ts
111
+ function ringArea(ring) {
112
+ if (ring.length < 3) return 0;
113
+ let sum = 0;
114
+ for (let i = 0; i < ring.length; i++) {
115
+ const a = ring[i];
116
+ const b = ring[(i + 1) % ring.length];
117
+ sum += a.x * b.y - b.x * a.y;
118
+ }
119
+ return sum / 2;
120
+ }
121
+ function centroid(p) {
122
+ const ring = p.rings[0];
123
+ if (!ring || ring.length === 0) return vec2(0, 0);
124
+ const a = ringArea(ring);
125
+ if (Math.abs(a) < 1e-12) {
126
+ let sx = 0;
127
+ let sy = 0;
128
+ for (const v of ring) {
129
+ sx += v.x;
130
+ sy += v.y;
131
+ }
132
+ return vec2(sx / ring.length, sy / ring.length);
133
+ }
134
+ let cx = 0;
135
+ let cy = 0;
136
+ for (let i = 0; i < ring.length; i++) {
137
+ const p0 = ring[i];
138
+ const p1 = ring[(i + 1) % ring.length];
139
+ const cross = p0.x * p1.y - p1.x * p0.y;
140
+ cx += (p0.x + p1.x) * cross;
141
+ cy += (p0.y + p1.y) * cross;
142
+ }
143
+ return vec2(cx / (6 * a), cy / (6 * a));
144
+ }
145
+ function containsPoint(p, point) {
146
+ if (!p.closed || !p.rings[0]) return false;
147
+ if (!pointInRing(point, p.rings[0])) return false;
148
+ for (let i = 1; i < p.rings.length; i++) {
149
+ const hole = p.rings[i];
150
+ if (hole && pointInRing(point, hole)) return false;
151
+ }
152
+ return true;
153
+ }
154
+ function bounds(p) {
155
+ let minX = Infinity;
156
+ let minY = Infinity;
157
+ let maxX = -Infinity;
158
+ let maxY = -Infinity;
159
+ for (const ring of p.rings) {
160
+ for (const v of ring) {
161
+ minX = Math.min(minX, v.x);
162
+ minY = Math.min(minY, v.y);
163
+ maxX = Math.max(maxX, v.x);
164
+ maxY = Math.max(maxY, v.y);
165
+ }
166
+ }
167
+ if (!Number.isFinite(minX)) {
168
+ return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
169
+ }
170
+ return { minX, minY, maxX, maxY };
171
+ }
172
+
173
+ // src/shapeBoolean/index.ts
174
+ function ensureClosed(p) {
175
+ if (!p.closed) {
176
+ throw new Error("shapeBoolean requires closed paths");
177
+ }
178
+ return p;
179
+ }
180
+ function unionAll(paths) {
181
+ if (paths.length === 0) return group([]);
182
+ let acc = pathToPathsD(ensureClosed(paths[0]));
183
+ for (let i = 1; i < paths.length; i++) {
184
+ acc = clipUnion(acc, pathToPathsD(ensureClosed(paths[i])));
185
+ }
186
+ return pathsDToGroup(acc);
187
+ }
188
+ function intersect(a, b) {
189
+ const result = clipIntersect(
190
+ pathToPathsD(ensureClosed(a)),
191
+ pathToPathsD(ensureClosed(b))
192
+ );
193
+ return pathsDToGroup(result);
194
+ }
195
+
196
+ // src/processing/index.ts
197
+ function extractPerimeter(p) {
198
+ const exterior = p.rings[0];
199
+ if (!exterior) return path([], true);
200
+ return polygon(exterior);
201
+ }
202
+ function extractBoundary(p) {
203
+ return extractPerimeter(p);
204
+ }
205
+ function extractHoles(p) {
206
+ const holes = [];
207
+ for (let i = 1; i < p.rings.length; i++) {
208
+ const hole = p.rings[i];
209
+ if (hole && hole.length >= 3) holes.push(polygon(hole));
210
+ }
211
+ return holes;
212
+ }
213
+ function densify(p, maxSegLen) {
214
+ if (maxSegLen <= 0) return p;
215
+ return path(
216
+ p.rings.map((ring) => densifyRing(ring, maxSegLen, p.closed)),
217
+ p.closed
218
+ );
219
+ }
220
+ function densifyRing(ring, maxSegLen, closed) {
221
+ if (ring.length < 2) return [...ring];
222
+ const out = [];
223
+ const n = ring.length;
224
+ const edges = closed ? n : n - 1;
225
+ for (let i = 0; i < edges; i++) {
226
+ const a = ring[i];
227
+ const b = ring[(i + 1) % n];
228
+ out.push(a);
229
+ const dist = Math.hypot(b.x - a.x, b.y - a.y);
230
+ const steps = Math.floor(dist / maxSegLen);
231
+ for (let s = 1; s < steps; s++) {
232
+ const t = s / steps;
233
+ out.push({
234
+ x: a.x + (b.x - a.x) * t,
235
+ y: a.y + (b.y - a.y) * t
236
+ });
237
+ }
238
+ }
239
+ if (!closed) out.push(ring[n - 1]);
240
+ return closed ? normalizeRing(out) : out;
241
+ }
242
+ function removeSmallHoles(p, minArea) {
243
+ if (p.rings.length <= 1) return p;
244
+ const exterior = p.rings[0];
245
+ const kept = p.rings.slice(1).filter((hole) => {
246
+ const a = Math.abs(shoelace(hole));
247
+ return a >= minArea;
248
+ });
249
+ return path([exterior, ...kept], p.closed);
250
+ }
251
+ function generateRandomPoints(p, count, seed = 1) {
252
+ const rng = mulberry32(seed >>> 0);
253
+ const b = bounds(p);
254
+ const out = [];
255
+ let attempts = 0;
256
+ const maxAttempts = Math.max(count * 50, 100);
257
+ while (out.length < count && attempts < maxAttempts) {
258
+ attempts++;
259
+ const pt = {
260
+ x: b.minX + rng() * (b.maxX - b.minX),
261
+ y: b.minY + rng() * (b.maxY - b.minY)
262
+ };
263
+ if (containsPoint(p, pt)) out.push(pt);
264
+ }
265
+ return out;
266
+ }
267
+ function generateRandomGridPoints(p, spacing) {
268
+ const step = Math.max(spacing, 1e-9);
269
+ const b = bounds(p);
270
+ const out = [];
271
+ for (let x = b.minX; x <= b.maxX + 1e-12; x += step) {
272
+ for (let y = b.minY; y <= b.maxY + 1e-12; y += step) {
273
+ const pt = { x, y };
274
+ if (containsPoint(p, pt)) out.push(pt);
275
+ }
276
+ }
277
+ return out;
278
+ }
279
+ function pointsOnExterior(p, count) {
280
+ const ring = p.rings[0];
281
+ if (!ring || ring.length < 2 || count <= 0) return [];
282
+ const lengths = edgeLengths(ring);
283
+ const total = lengths.reduce((s, L) => s + L, 0);
284
+ if (total < 1e-12) return [];
285
+ const out = [];
286
+ for (let i = 0; i < count; i++) {
287
+ const target = i / count * total;
288
+ out.push(pointAtArcLength(ring, lengths, target));
289
+ }
290
+ return out;
291
+ }
292
+ function segmentsOnExterior(p) {
293
+ const ring = p.rings[0];
294
+ if (!ring || ring.length < 2) return [];
295
+ const n = ring.length;
296
+ const closed = p.closed;
297
+ const edges = closed ? n : n - 1;
298
+ const out = [];
299
+ for (let i = 0; i < edges; i++) {
300
+ out.push(segment(ring[i], ring[(i + 1) % n]));
301
+ }
302
+ return out;
303
+ }
304
+ function slice(p, a, b) {
305
+ const dx = b.x - a.x;
306
+ const dy = b.y - a.y;
307
+ const len = Math.hypot(dx, dy);
308
+ if (len < 1e-12) return group([p]);
309
+ const nx = -dy / len;
310
+ const ny = dx / len;
311
+ const bb = bounds(p);
312
+ const span = Math.hypot(bb.maxX - bb.minX, bb.maxY - bb.minY) * 4 + 10;
313
+ const left = halfPlanePoly(a, nx, ny, span);
314
+ const right = halfPlanePoly(a, -nx, -ny, span);
315
+ const parts = [];
316
+ for (const half of [left, right]) {
317
+ parts.push(...intersect(p, half).paths);
318
+ }
319
+ return group(parts);
320
+ }
321
+ function centroidSplit(p) {
322
+ const c = centroid(p);
323
+ const bb = bounds(p);
324
+ const pad = Math.max(bb.maxY - bb.minY, 1) + 1;
325
+ return slice(p, { x: c.x, y: c.y - pad }, { x: c.x, y: c.y + pad });
326
+ }
327
+ function dissolve(paths) {
328
+ return unionAll(paths);
329
+ }
330
+ function intersectionPoints(a, b) {
331
+ const segsA = allSegments(a);
332
+ const segsB = allSegments(b);
333
+ const out = [];
334
+ const seen = /* @__PURE__ */ new Set();
335
+ for (const sa of segsA) {
336
+ for (const sb of segsB) {
337
+ const hit = segmentIntersection(sa.a, sa.b, sb.a, sb.b);
338
+ if (!hit) continue;
339
+ const key = `${hit.x.toFixed(9)},${hit.y.toFixed(9)}`;
340
+ if (seen.has(key)) continue;
341
+ seen.add(key);
342
+ out.push(hit);
343
+ }
344
+ }
345
+ return out;
346
+ }
347
+ function halfPlanePoly(origin, nx, ny, span) {
348
+ const tx = -ny;
349
+ const ty = nx;
350
+ const o = origin;
351
+ return polygon([
352
+ {
353
+ x: o.x - tx * span + nx * 1e-9,
354
+ y: o.y - ty * span + ny * 1e-9
355
+ },
356
+ {
357
+ x: o.x + tx * span + nx * 1e-9,
358
+ y: o.y + ty * span + ny * 1e-9
359
+ },
360
+ {
361
+ x: o.x + tx * span + nx * span,
362
+ y: o.y + ty * span + ny * span
363
+ },
364
+ {
365
+ x: o.x - tx * span + nx * span,
366
+ y: o.y - ty * span + ny * span
367
+ }
368
+ ]);
369
+ }
370
+ function allSegments(p) {
371
+ const out = [];
372
+ for (const ring of p.rings) {
373
+ const n = ring.length;
374
+ const edges = p.closed ? n : Math.max(0, n - 1);
375
+ for (let i = 0; i < edges; i++) {
376
+ out.push(segment(ring[i], ring[(i + 1) % n]));
377
+ }
378
+ }
379
+ return out;
380
+ }
381
+ function segmentIntersection(a, b, c, d) {
382
+ const rX = b.x - a.x;
383
+ const rY = b.y - a.y;
384
+ const sX = d.x - c.x;
385
+ const sY = d.y - c.y;
386
+ const den = rX * sY - rY * sX;
387
+ if (Math.abs(den) < 1e-14) return null;
388
+ const t = ((c.x - a.x) * sY - (c.y - a.y) * sX) / den;
389
+ const u = ((c.x - a.x) * rY - (c.y - a.y) * rX) / den;
390
+ if (t < -1e-9 || t > 1 + 1e-9 || u < -1e-9 || u > 1 + 1e-9) return null;
391
+ return { x: a.x + t * rX, y: a.y + t * rY };
392
+ }
393
+ function edgeLengths(ring, closed) {
394
+ const n = ring.length;
395
+ const edges = n ;
396
+ const lengths = [];
397
+ for (let i = 0; i < edges; i++) {
398
+ const a = ring[i];
399
+ const b = ring[(i + 1) % n];
400
+ lengths.push(Math.hypot(b.x - a.x, b.y - a.y));
401
+ }
402
+ return lengths;
403
+ }
404
+ function pointAtArcLength(ring, lengths, target) {
405
+ let remain = target;
406
+ const n = ring.length;
407
+ for (let i = 0; i < lengths.length; i++) {
408
+ const L = lengths[i];
409
+ if (remain <= L || i === lengths.length - 1) {
410
+ const t = L < 1e-12 ? 0 : remain / L;
411
+ const a = ring[i];
412
+ const b = ring[(i + 1) % n];
413
+ return {
414
+ x: a.x + (b.x - a.x) * t,
415
+ y: a.y + (b.y - a.y) * t
416
+ };
417
+ }
418
+ remain -= L;
419
+ }
420
+ return { ...ring[0] };
421
+ }
422
+ function shoelace(ring) {
423
+ let sum = 0;
424
+ for (let i = 0; i < ring.length; i++) {
425
+ const a = ring[i];
426
+ const b = ring[(i + 1) % ring.length];
427
+ sum += a.x * b.y - b.x * a.y;
428
+ }
429
+ return sum / 2;
430
+ }
431
+ function mulberry32(a) {
432
+ return function() {
433
+ let t = a += 1831565813;
434
+ t = Math.imul(t ^ t >>> 15, t | 1);
435
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
436
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
437
+ };
438
+ }
439
+ var processing = {
440
+ extractPerimeter,
441
+ extractBoundary,
442
+ extractHoles,
443
+ densify,
444
+ removeSmallHoles,
445
+ generateRandomPoints,
446
+ generateRandomGridPoints,
447
+ pointsOnExterior,
448
+ segmentsOnExterior,
449
+ slice,
450
+ centroidSplit,
451
+ dissolve,
452
+ intersectionPoints
453
+ };
454
+
455
+ export { centroidSplit, densify, dissolve, extractBoundary, extractHoles, extractPerimeter, generateRandomGridPoints, generateRandomPoints, intersectionPoints, pointsOnExterior, processing, removeSmallHoles, segmentsOnExterior, slice };
456
+ //# sourceMappingURL=index.js.map
457
+ //# sourceMappingURL=index.js.map