pattapatta 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +98 -0
- package/dist/circle-BDbKfrdR.d.ts +12 -0
- package/dist/circlePacking/index.d.ts +78 -0
- package/dist/circlePacking/index.js +381 -0
- package/dist/circlePacking/index.js.map +1 -0
- package/dist/cli.js +365 -0
- package/dist/cli.js.map +1 -0
- package/dist/construction/index.d.ts +26 -0
- package/dist/construction/index.js +102 -0
- package/dist/construction/index.js.map +1 -0
- package/dist/contour/index.d.ts +20 -0
- package/dist/contour/index.js +118 -0
- package/dist/contour/index.js.map +1 -0
- package/dist/conversion/index.d.ts +31 -0
- package/dist/conversion/index.js +99 -0
- package/dist/conversion/index.js.map +1 -0
- package/dist/group-BNjVVYSQ.d.ts +13 -0
- package/dist/hatch/index.d.ts +41 -0
- package/dist/hatch/index.js +253 -0
- package/dist/hatch/index.js.map +1 -0
- package/dist/hull/index.d.ts +16 -0
- package/dist/hull/index.js +97 -0
- package/dist/hull/index.js.map +1 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +3933 -0
- package/dist/index.js.map +1 -0
- package/dist/meshing/index.d.ts +125 -0
- package/dist/meshing/index.js +1333 -0
- package/dist/meshing/index.js.map +1 -0
- package/dist/morphology/index.d.ts +58 -0
- package/dist/morphology/index.js +268 -0
- package/dist/morphology/index.js.map +1 -0
- package/dist/optimisation/index.d.ts +43 -0
- package/dist/optimisation/index.js +400 -0
- package/dist/optimisation/index.js.map +1 -0
- package/dist/path-0RjhYjJs.d.ts +21 -0
- package/dist/pointSet/index.d.ts +24 -0
- package/dist/pointSet/index.js +139 -0
- package/dist/pointSet/index.js.map +1 -0
- package/dist/polygonisation/index.d.ts +44 -0
- package/dist/polygonisation/index.js +310 -0
- package/dist/polygonisation/index.js.map +1 -0
- package/dist/predicates/index.d.ts +35 -0
- package/dist/predicates/index.js +159 -0
- package/dist/predicates/index.js.map +1 -0
- package/dist/processing/index.d.ts +54 -0
- package/dist/processing/index.js +457 -0
- package/dist/processing/index.js.map +1 -0
- package/dist/segment-BKuIFMKD.d.ts +10 -0
- package/dist/segmentSet/index.d.ts +25 -0
- package/dist/segmentSet/index.js +109 -0
- package/dist/segmentSet/index.js.map +1 -0
- package/dist/shapeBoolean/index.d.ts +41 -0
- package/dist/shapeBoolean/index.js +211 -0
- package/dist/shapeBoolean/index.js.map +1 -0
- package/dist/svg/index.d.ts +38 -0
- package/dist/svg/index.js +317 -0
- package/dist/svg/index.js.map +1 -0
- package/dist/tiling/index.d.ts +32 -0
- package/dist/tiling/index.js +344 -0
- package/dist/tiling/index.js.map +1 -0
- package/dist/transformation/index.d.ts +37 -0
- package/dist/transformation/index.js +184 -0
- package/dist/transformation/index.js.map +1 -0
- package/dist/triangulation/index.d.ts +48 -0
- package/dist/triangulation/index.js +334 -0
- package/dist/triangulation/index.js.map +1 -0
- package/dist/vec2-CWyXN1Wj.d.ts +10 -0
- package/dist/voronoi/index.d.ts +31 -0
- package/dist/voronoi/index.js +211 -0
- package/dist/voronoi/index.js.map +1 -0
- package/package.json +165 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { FillRule, intersectD, isPositiveD, 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 path(rings, closed = true) {
|
|
16
|
+
return {
|
|
17
|
+
rings: rings.map((ring) => ring.map(cloneVec2)),
|
|
18
|
+
closed
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function polygon(exterior, holes = []) {
|
|
22
|
+
return path([exterior, ...holes], true);
|
|
23
|
+
}
|
|
24
|
+
function normalizeRing(ring, eps = 1e-9) {
|
|
25
|
+
if (ring.length < 2) return ring.map(cloneVec2);
|
|
26
|
+
const first = ring[0];
|
|
27
|
+
const last = ring[ring.length - 1];
|
|
28
|
+
if (first && last && equalsVec2(first, last, eps)) {
|
|
29
|
+
return ring.slice(0, -1).map(cloneVec2);
|
|
30
|
+
}
|
|
31
|
+
return ring.map(cloneVec2);
|
|
32
|
+
}
|
|
33
|
+
function clonePath(p) {
|
|
34
|
+
return {
|
|
35
|
+
closed: p.closed,
|
|
36
|
+
rings: p.rings.map((ring) => ring.map(cloneVec2))
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/types/group.ts
|
|
41
|
+
function group(paths = []) {
|
|
42
|
+
return { paths: paths.map(clonePath) };
|
|
43
|
+
}
|
|
44
|
+
var DEFAULT_FILL_RULE = FillRule.NonZero;
|
|
45
|
+
function ringToPathD(ring) {
|
|
46
|
+
const pts = [];
|
|
47
|
+
for (const v of normalizeRing(ring)) {
|
|
48
|
+
pts.push({ x: v.x, y: v.y, z: 0 });
|
|
49
|
+
}
|
|
50
|
+
return pts;
|
|
51
|
+
}
|
|
52
|
+
function pathToPathsD(p) {
|
|
53
|
+
if (!p.closed || p.rings.length === 0) {
|
|
54
|
+
throw new Error("Boolean ops require a closed path with at least one ring");
|
|
55
|
+
}
|
|
56
|
+
return p.rings.map((ring) => ringToPathD(ring));
|
|
57
|
+
}
|
|
58
|
+
function pathsDToGroup(paths) {
|
|
59
|
+
if (paths.length === 0) return group([]);
|
|
60
|
+
const outers = [];
|
|
61
|
+
const holes = [];
|
|
62
|
+
for (const pd of paths) {
|
|
63
|
+
const ring = pathDToRing(pd);
|
|
64
|
+
if (ring.length < 3) continue;
|
|
65
|
+
if (isPositiveD(pd)) {
|
|
66
|
+
outers.push({ ring, holes: [] });
|
|
67
|
+
} else {
|
|
68
|
+
holes.push(ring);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const hole of holes) {
|
|
72
|
+
const sample = hole[0];
|
|
73
|
+
if (!sample) continue;
|
|
74
|
+
let assigned = false;
|
|
75
|
+
for (const outer of outers) {
|
|
76
|
+
if (pointInRing(sample, outer.ring)) {
|
|
77
|
+
outer.holes.push(hole);
|
|
78
|
+
assigned = true;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!assigned) {
|
|
83
|
+
outers.push({ ring: [...hole].reverse(), holes: [] });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return group(outers.map((o) => path([o.ring, ...o.holes], true)));
|
|
87
|
+
}
|
|
88
|
+
function pathDToRing(pd) {
|
|
89
|
+
return normalizeRing(pd.map((p) => vec2(p.x, p.y)));
|
|
90
|
+
}
|
|
91
|
+
function pointInRing(point, ring) {
|
|
92
|
+
const result = pointInPolygonD(
|
|
93
|
+
{ x: point.x, y: point.y, z: 0 },
|
|
94
|
+
ringToPathD(ring)
|
|
95
|
+
);
|
|
96
|
+
return result === PointInPolygonResult.IsInside || result === PointInPolygonResult.IsOn;
|
|
97
|
+
}
|
|
98
|
+
function clipIntersect(a, b) {
|
|
99
|
+
return intersectD(a, b, DEFAULT_FILL_RULE);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/predicates/index.ts
|
|
103
|
+
function bounds(p) {
|
|
104
|
+
let minX = Infinity;
|
|
105
|
+
let minY = Infinity;
|
|
106
|
+
let maxX = -Infinity;
|
|
107
|
+
let maxY = -Infinity;
|
|
108
|
+
for (const ring of p.rings) {
|
|
109
|
+
for (const v of ring) {
|
|
110
|
+
minX = Math.min(minX, v.x);
|
|
111
|
+
minY = Math.min(minY, v.y);
|
|
112
|
+
maxX = Math.max(maxX, v.x);
|
|
113
|
+
maxY = Math.max(maxY, v.y);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!Number.isFinite(minX)) {
|
|
117
|
+
return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
|
118
|
+
}
|
|
119
|
+
return { minX, minY, maxX, maxY };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/construction/index.ts
|
|
123
|
+
function createRect(x, y, width, height) {
|
|
124
|
+
return polygon([
|
|
125
|
+
{ x, y },
|
|
126
|
+
{ x: x + width, y },
|
|
127
|
+
{ x: x + width, y: y + height },
|
|
128
|
+
{ x, y: y + height }
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/shapeBoolean/index.ts
|
|
133
|
+
function ensureClosed(p) {
|
|
134
|
+
if (!p.closed) {
|
|
135
|
+
throw new Error("shapeBoolean requires closed paths");
|
|
136
|
+
}
|
|
137
|
+
return p;
|
|
138
|
+
}
|
|
139
|
+
function intersect(a, b) {
|
|
140
|
+
const result = clipIntersect(
|
|
141
|
+
pathToPathsD(ensureClosed(a)),
|
|
142
|
+
pathToPathsD(ensureClosed(b))
|
|
143
|
+
);
|
|
144
|
+
return pathsDToGroup(result);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/processing/index.ts
|
|
148
|
+
function slice(p, a, b) {
|
|
149
|
+
const dx = b.x - a.x;
|
|
150
|
+
const dy = b.y - a.y;
|
|
151
|
+
const len = Math.hypot(dx, dy);
|
|
152
|
+
if (len < 1e-12) return group([p]);
|
|
153
|
+
const nx = -dy / len;
|
|
154
|
+
const ny = dx / len;
|
|
155
|
+
const bb = bounds(p);
|
|
156
|
+
const span = Math.hypot(bb.maxX - bb.minX, bb.maxY - bb.minY) * 4 + 10;
|
|
157
|
+
const left = halfPlanePoly(a, nx, ny, span);
|
|
158
|
+
const right = halfPlanePoly(a, -nx, -ny, span);
|
|
159
|
+
const parts = [];
|
|
160
|
+
for (const half of [left, right]) {
|
|
161
|
+
parts.push(...intersect(p, half).paths);
|
|
162
|
+
}
|
|
163
|
+
return group(parts);
|
|
164
|
+
}
|
|
165
|
+
function halfPlanePoly(origin, nx, ny, span) {
|
|
166
|
+
const tx = -ny;
|
|
167
|
+
const ty = nx;
|
|
168
|
+
const o = origin;
|
|
169
|
+
return polygon([
|
|
170
|
+
{
|
|
171
|
+
x: o.x - tx * span + nx * 1e-9,
|
|
172
|
+
y: o.y - ty * span + ny * 1e-9
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
x: o.x + tx * span + nx * 1e-9,
|
|
176
|
+
y: o.y + ty * span + ny * 1e-9
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
x: o.x + tx * span + nx * span,
|
|
180
|
+
y: o.y + ty * span + ny * span
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
x: o.x - tx * span + nx * span,
|
|
184
|
+
y: o.y - ty * span + ny * span
|
|
185
|
+
}
|
|
186
|
+
]);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/tiling/index.ts
|
|
190
|
+
function squareGrid(spacing, minX, minY, maxX, maxY) {
|
|
191
|
+
const s = Math.max(spacing, 1e-9);
|
|
192
|
+
const paths = [];
|
|
193
|
+
for (let x = minX; x < maxX - 1e-12; x += s) {
|
|
194
|
+
for (let y = minY; y < maxY - 1e-12; y += s) {
|
|
195
|
+
const w = Math.min(s, maxX - x);
|
|
196
|
+
const h = Math.min(s, maxY - y);
|
|
197
|
+
if (w > 1e-12 && h > 1e-12) paths.push(createRect(x, y, w, h));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return group(paths);
|
|
201
|
+
}
|
|
202
|
+
function hexTiling(spacing, minX, minY, maxX, maxY) {
|
|
203
|
+
const s = Math.max(spacing, 1e-9);
|
|
204
|
+
const r = s / 2;
|
|
205
|
+
const dx = s * 0.75;
|
|
206
|
+
const dy = s * Math.sqrt(3) * 0.5;
|
|
207
|
+
const paths = [];
|
|
208
|
+
let row = 0;
|
|
209
|
+
for (let y = minY + r; y <= maxY + r; y += dy, row++) {
|
|
210
|
+
const x0 = minX + r + (row % 2 === 0 ? 0 : dx * 0.5);
|
|
211
|
+
for (let x = x0; x <= maxX + r; x += dx) {
|
|
212
|
+
paths.push(hexagon(x, y, r));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return group(paths);
|
|
216
|
+
}
|
|
217
|
+
function hexagon(cx, cy, r) {
|
|
218
|
+
const ring = [];
|
|
219
|
+
for (let i = 0; i < 6; i++) {
|
|
220
|
+
const t = Math.PI / 180 * (60 * i - 30);
|
|
221
|
+
ring.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r });
|
|
222
|
+
}
|
|
223
|
+
return polygon(ring);
|
|
224
|
+
}
|
|
225
|
+
function rectSubdivision(p, nx, ny) {
|
|
226
|
+
const b = bounds(p);
|
|
227
|
+
const cols = Math.max(1, Math.floor(nx));
|
|
228
|
+
const rows = Math.max(1, Math.floor(ny));
|
|
229
|
+
const dw = (b.maxX - b.minX) / cols;
|
|
230
|
+
const dh = (b.maxY - b.minY) / rows;
|
|
231
|
+
const paths = [];
|
|
232
|
+
for (let i = 0; i < cols; i++) {
|
|
233
|
+
for (let j = 0; j < rows; j++) {
|
|
234
|
+
const cell = createRect(
|
|
235
|
+
b.minX + i * dw,
|
|
236
|
+
b.minY + j * dh,
|
|
237
|
+
dw,
|
|
238
|
+
dh
|
|
239
|
+
);
|
|
240
|
+
paths.push(...intersect(p, cell).paths);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return group(paths);
|
|
244
|
+
}
|
|
245
|
+
function quadSubdivision(faces, depth = 1) {
|
|
246
|
+
let cur = faces;
|
|
247
|
+
const d = Math.max(0, Math.floor(depth));
|
|
248
|
+
for (let i = 0; i < d; i++) {
|
|
249
|
+
const next = [];
|
|
250
|
+
for (const f of cur) {
|
|
251
|
+
next.push(...subdivideQuad(f));
|
|
252
|
+
}
|
|
253
|
+
cur = next;
|
|
254
|
+
}
|
|
255
|
+
return group(cur);
|
|
256
|
+
}
|
|
257
|
+
function subdivideQuad(p) {
|
|
258
|
+
const ring = p.rings[0];
|
|
259
|
+
if (!ring || ring.length < 4) return [p];
|
|
260
|
+
const [a, b, c, d] = ring;
|
|
261
|
+
if (!a || !b || !c || !d) return [p];
|
|
262
|
+
const mab = mid(a, b);
|
|
263
|
+
const mbc = mid(b, c);
|
|
264
|
+
const mcd = mid(c, d);
|
|
265
|
+
const mda = mid(d, a);
|
|
266
|
+
const cen = mid(mid(a, c), mid(b, d));
|
|
267
|
+
return [
|
|
268
|
+
polygon([a, mab, cen, mda]),
|
|
269
|
+
polygon([mab, b, mbc, cen]),
|
|
270
|
+
polygon([cen, mbc, c, mcd]),
|
|
271
|
+
polygon([mda, cen, mcd, d])
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
function triangleSubdivision(faces, depth = 1) {
|
|
275
|
+
let cur = faces;
|
|
276
|
+
const d = Math.max(0, Math.floor(depth));
|
|
277
|
+
for (let i = 0; i < d; i++) {
|
|
278
|
+
const next = [];
|
|
279
|
+
for (const f of cur) {
|
|
280
|
+
next.push(...subdivideTriangle(f));
|
|
281
|
+
}
|
|
282
|
+
cur = next;
|
|
283
|
+
}
|
|
284
|
+
return group(cur);
|
|
285
|
+
}
|
|
286
|
+
function subdivideTriangle(p) {
|
|
287
|
+
const ring = p.rings[0];
|
|
288
|
+
if (!ring || ring.length < 3) return [p];
|
|
289
|
+
const a = ring[0];
|
|
290
|
+
const b = ring[1];
|
|
291
|
+
const c = ring[2];
|
|
292
|
+
const mab = mid(a, b);
|
|
293
|
+
const mbc = mid(b, c);
|
|
294
|
+
const mca = mid(c, a);
|
|
295
|
+
return [
|
|
296
|
+
polygon([a, mab, mca]),
|
|
297
|
+
polygon([mab, b, mbc]),
|
|
298
|
+
polygon([mca, mbc, c]),
|
|
299
|
+
polygon([mab, mbc, mca])
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
function sliceDivision(p, spacing, angle = 0) {
|
|
303
|
+
const s = Math.max(spacing, 1e-9);
|
|
304
|
+
const b = bounds(p);
|
|
305
|
+
const cx = (b.minX + b.maxX) / 2;
|
|
306
|
+
const cy = (b.minY + b.maxY) / 2;
|
|
307
|
+
const diag = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) + s;
|
|
308
|
+
const ux = Math.cos(angle);
|
|
309
|
+
const uy = Math.sin(angle);
|
|
310
|
+
const nx = -uy;
|
|
311
|
+
const ny = ux;
|
|
312
|
+
const parts = [p];
|
|
313
|
+
for (let t = -diag; t <= diag; t += s) {
|
|
314
|
+
const a = { x: cx + nx * t - ux * diag, y: cy + ny * t - uy * diag };
|
|
315
|
+
const bb = { x: cx + nx * t + ux * diag, y: cy + ny * t + uy * diag };
|
|
316
|
+
const next = [];
|
|
317
|
+
for (const piece of parts) {
|
|
318
|
+
const g = slice(piece, a, bb);
|
|
319
|
+
next.push(...g.paths.length ? g.paths : [piece]);
|
|
320
|
+
}
|
|
321
|
+
parts.length = 0;
|
|
322
|
+
parts.push(...next);
|
|
323
|
+
}
|
|
324
|
+
return group(parts);
|
|
325
|
+
}
|
|
326
|
+
function hatchSubdivision(p, spacing, angle = 0) {
|
|
327
|
+
return sliceDivision(p, spacing, angle);
|
|
328
|
+
}
|
|
329
|
+
function mid(a, b) {
|
|
330
|
+
return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
|
|
331
|
+
}
|
|
332
|
+
var tiling = {
|
|
333
|
+
squareGrid,
|
|
334
|
+
hexTiling,
|
|
335
|
+
rectSubdivision,
|
|
336
|
+
quadSubdivision,
|
|
337
|
+
triangleSubdivision,
|
|
338
|
+
sliceDivision,
|
|
339
|
+
hatchSubdivision
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
export { hatchSubdivision, hexTiling, quadSubdivision, rectSubdivision, sliceDivision, squareGrid, tiling, triangleSubdivision };
|
|
343
|
+
//# sourceMappingURL=index.js.map
|
|
344
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/types/group.ts","../../src/clipper/convert.ts","../../src/predicates/index.ts","../../src/construction/index.ts","../../src/shapeBoolean/index.ts","../../src/processing/index.ts","../../src/tiling/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** 2D point / vector. */\nexport type Vec2 = {\n x: number\n y: number\n}\n\nexport function vec2(x: number, y: number): Vec2 {\n return { x, y }\n}\n\nexport function cloneVec2(v: Vec2): Vec2 {\n return { x: v.x, y: v.y }\n}\n\nexport function equalsVec2(a: Vec2, b: Vec2, eps = 1e-9): boolean {\n return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps\n}\n","import type { Vec2 } from './vec2.js'\nimport { cloneVec2, equalsVec2 } from './vec2.js'\n\n/** Ordered ring of vertices (typically without repeating the closing vertex). */\nexport type Ring = Vec2[]\n\n/**\n * A path: one exterior ring plus optional holes, or an open polyline.\n * `rings[0]` is the exterior (or the open polyline when `closed` is false).\n * Additional rings are holes (only meaningful when `closed` is true).\n */\nexport type Path = {\n rings: Ring[]\n closed: boolean\n}\n\nexport function path(rings: Ring[], closed = true): Path {\n return {\n rings: rings.map((ring) => ring.map(cloneVec2)),\n closed,\n }\n}\n\nexport function polyline(points: Ring): Path {\n return path([points], false)\n}\n\nexport function polygon(exterior: Ring, holes: Ring[] = []): Path {\n return path([exterior, ...holes], true)\n}\n\n/** Drop a trailing vertex that duplicates the first (SVG often closes this way). */\nexport function normalizeRing(ring: Ring, eps = 1e-9): Ring {\n if (ring.length < 2) return ring.map(cloneVec2)\n const first = ring[0]\n const last = ring[ring.length - 1]\n if (first && last && equalsVec2(first, last, eps)) {\n return ring.slice(0, -1).map(cloneVec2)\n }\n return ring.map(cloneVec2)\n}\n\nexport function clonePath(p: Path): Path {\n return {\n closed: p.closed,\n rings: p.rings.map((ring) => ring.map(cloneVec2)),\n }\n}\n","import type { Path } from './path.js'\nimport { clonePath } from './path.js'\n\n/**\n * Ordered collection of paths. Later entries are “on top” for occlusion-style ops.\n */\nexport type Group = {\n paths: Path[]\n}\n\nexport function group(paths: Path[] = []): Group {\n return { paths: paths.map(clonePath) }\n}\n\nexport function groupFromPaths(...paths: Path[]): Group {\n return group(paths)\n}\n\nexport function cloneGroup(g: Group): Group {\n return group(g.paths)\n}\n","import {\n FillRule,\n areaD,\n differenceD,\n intersectD,\n isPositiveD,\n pointInPolygonD,\n PointInPolygonResult,\n unionD,\n xorD,\n type PathD,\n type PathsD,\n type PointD,\n} from 'clipper2-ts'\nimport type { Group, Path, Ring, Vec2 } from '../types/index.js'\nimport { group, normalizeRing, path, vec2 } from '../types/index.js'\n\nexport const DEFAULT_FILL_RULE = FillRule.NonZero\n\nexport function ringToPathD(ring: Ring): PathD {\n const pts: PointD[] = []\n for (const v of normalizeRing(ring)) {\n pts.push({ x: v.x, y: v.y, z: 0 })\n }\n return pts\n}\n\n/** Closed path → Clipper subject/clip paths (exterior + holes). */\nexport function pathToPathsD(p: Path): PathsD {\n if (!p.closed || p.rings.length === 0) {\n throw new Error('Boolean ops require a closed path with at least one ring')\n }\n return p.rings.map((ring) => ringToPathD(ring))\n}\n\nexport function pathsDToGroup(paths: PathsD): Group {\n if (paths.length === 0) return group([])\n\n const outers: { ring: Ring; holes: Ring[] }[] = []\n const holes: Ring[] = []\n\n for (const pd of paths) {\n const ring = pathDToRing(pd)\n if (ring.length < 3) continue\n if (isPositiveD(pd)) {\n outers.push({ ring, holes: [] })\n } else {\n holes.push(ring)\n }\n }\n\n for (const hole of holes) {\n const sample = hole[0]\n if (!sample) continue\n let assigned = false\n for (const outer of outers) {\n if (pointInRing(sample, outer.ring)) {\n outer.holes.push(hole)\n assigned = true\n break\n }\n }\n if (!assigned) {\n // Treat unmatched negative rings as their own outers (orientation flip).\n outers.push({ ring: [...hole].reverse(), holes: [] })\n }\n }\n\n return group(outers.map((o) => path([o.ring, ...o.holes], true)))\n}\n\nexport function pathDToRing(pd: PathD): Ring {\n return normalizeRing(pd.map((p) => vec2(p.x, p.y)))\n}\n\nexport function pointInRing(point: Vec2, ring: Ring): boolean {\n const result = pointInPolygonD(\n { x: point.x, y: point.y, z: 0 },\n ringToPathD(ring),\n )\n return (\n result === PointInPolygonResult.IsInside ||\n result === PointInPolygonResult.IsOn\n )\n}\n\nexport function clipUnion(a: PathsD, b: PathsD): PathsD {\n return unionD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipIntersect(a: PathsD, b: PathsD): PathsD {\n return intersectD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipDifference(a: PathsD, b: PathsD): PathsD {\n return differenceD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function clipXor(a: PathsD, b: PathsD): PathsD {\n return xorD(a, b, DEFAULT_FILL_RULE)\n}\n\nexport function pathsArea(paths: PathsD): number {\n let sum = 0\n for (const p of paths) {\n sum += Math.abs(areaD(p))\n }\n return sum\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { vec2 } from '../types/index.js'\nimport {\n pathToPathsD,\n pathsArea,\n pointInRing,\n} from '../clipper/convert.js'\n\n/** Signed area of a closed ring (shoelace). */\nexport function ringArea(ring: Vec2[]): number {\n if (ring.length < 3) return 0\n let sum = 0\n for (let i = 0; i < ring.length; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % ring.length]!\n sum += a.x * b.y - b.x * a.y\n }\n return sum / 2\n}\n\n/** Absolute area of a closed path (exterior minus holes). */\nexport function area(p: Path): number {\n if (!p.closed || p.rings.length === 0) return 0\n try {\n return pathsArea(pathToPathsD(p))\n } catch {\n let total = 0\n for (let i = 0; i < p.rings.length; i++) {\n const a = Math.abs(ringArea(p.rings[i]!))\n total += i === 0 ? a : -a\n }\n return Math.max(0, total)\n }\n}\n\nexport function areaGroup(g: Group): number {\n return g.paths.reduce((sum, p) => sum + area(p), 0)\n}\n\n/** Centroid of a closed exterior ring (ignores holes for Phase 2). */\nexport function centroid(p: Path): Vec2 {\n const ring = p.rings[0]\n if (!ring || ring.length === 0) return vec2(0, 0)\n const a = ringArea(ring)\n if (Math.abs(a) < 1e-12) {\n let sx = 0\n let sy = 0\n for (const v of ring) {\n sx += v.x\n sy += v.y\n }\n return vec2(sx / ring.length, sy / ring.length)\n }\n let cx = 0\n let cy = 0\n for (let i = 0; i < ring.length; i++) {\n const p0 = ring[i]!\n const p1 = ring[(i + 1) % ring.length]!\n const cross = p0.x * p1.y - p1.x * p0.y\n cx += (p0.x + p1.x) * cross\n cy += (p0.y + p1.y) * cross\n }\n return vec2(cx / (6 * a), cy / (6 * a))\n}\n\nexport function containsPoint(p: Path, point: Vec2): boolean {\n if (!p.closed || !p.rings[0]) return false\n if (!pointInRing(point, p.rings[0])) return false\n for (let i = 1; i < p.rings.length; i++) {\n const hole = p.rings[i]\n if (hole && pointInRing(point, hole)) return false\n }\n return true\n}\n\nexport function bounds(p: Path): {\n minX: number\n minY: number\n maxX: number\n maxY: number\n} {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const ring of p.rings) {\n for (const v of ring) {\n minX = Math.min(minX, v.x)\n minY = Math.min(minY, v.y)\n maxX = Math.max(maxX, v.x)\n maxY = Math.max(maxY, v.y)\n }\n }\n if (!Number.isFinite(minX)) {\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 }\n }\n return { minX, minY, maxX, maxY }\n}\n\nexport function width(p: Path): number {\n const b = bounds(p)\n return b.maxX - b.minX\n}\n\nexport function height(p: Path): number {\n const b = bounds(p)\n return b.maxY - b.minY\n}\n\n/** Rough polygon similarity via absolute area difference relative to max area. */\nexport function areaSimilarity(a: Path, b: Path): number {\n const aa = area(a)\n const bb = area(b)\n const denom = Math.max(aa, bb, 1e-12)\n return 1 - Math.abs(aa - bb) / denom\n}\n\nexport const predicates = {\n area,\n areaGroup,\n ringArea,\n centroid,\n containsPoint,\n bounds,\n width,\n height,\n areaSimilarity,\n}\n","import type { Path, Vec2 } from '../types/index.js'\nimport { path, polygon, polyline } from '../types/index.js'\n\n/** Regular n-gon approximating a circle. */\nexport function createCircle(\n cx: number,\n cy: number,\n radius: number,\n segments = 64,\n): Path {\n return createRegularPolygon(cx, cy, radius, Math.max(3, Math.floor(segments)))\n}\n\nexport function createRect(\n x: number,\n y: number,\n width: number,\n height: number,\n): Path {\n return polygon([\n { x, y },\n { x: x + width, y },\n { x: x + width, y: y + height },\n { x, y: y + height },\n ])\n}\n\nexport function createRegularPolygon(\n cx: number,\n cy: number,\n radius: number,\n sides: number,\n rotation = -Math.PI / 2,\n): Path {\n const n = Math.max(3, Math.floor(sides))\n const ring: Vec2[] = []\n for (let i = 0; i < n; i++) {\n const t = rotation + (i / n) * Math.PI * 2\n ring.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius })\n }\n return polygon(ring)\n}\n\n/** Annulus as exterior + hole. */\nexport function createRing(\n cx: number,\n cy: number,\n outerRadius: number,\n innerRadius: number,\n segments = 64,\n): Path {\n const outer = createCircle(cx, cy, outerRadius, segments).rings[0]!\n const inner = createCircle(cx, cy, Math.min(innerRadius, outerRadius), segments)\n .rings[0]!\n // hole winding opposite for Clipper\n return path([outer, [...inner].reverse()], true)\n}\n\n/** Open circular arc polyline from `startAngle` to `endAngle` (radians). */\nexport function createArc(\n cx: number,\n cy: number,\n radius: number,\n startAngle: number,\n endAngle: number,\n segments = 32,\n): Path {\n const n = Math.max(2, Math.floor(segments))\n const pts: Vec2[] = []\n for (let i = 0; i <= n; i++) {\n const t = startAngle + ((endAngle - startAngle) * i) / n\n pts.push({ x: cx + Math.cos(t) * radius, y: cy + Math.sin(t) * radius })\n }\n return polyline(pts)\n}\n\n/** Star polygon (outer/inner radius alternating). */\nexport function createStar(\n cx: number,\n cy: number,\n outerRadius: number,\n innerRadius: number,\n points = 5,\n rotation = -Math.PI / 2,\n): Path {\n const n = Math.max(3, Math.floor(points))\n const ring: Vec2[] = []\n for (let i = 0; i < n * 2; i++) {\n const r = i % 2 === 0 ? outerRadius : innerRadius\n const t = rotation + (i / (n * 2)) * Math.PI * 2\n ring.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r })\n }\n return polygon(ring)\n}\n\n/** Koch snowflake after `iterations` refinement (0 = equilateral triangle). */\nexport function createKochSnowflake(\n cx: number,\n cy: number,\n radius: number,\n iterations = 3,\n): Path {\n const tri = createRegularPolygon(cx, cy, radius, 3)\n let ring = tri.rings[0]!\n const iters = Math.max(0, Math.min(6, Math.floor(iterations)))\n for (let i = 0; i < iters; i++) {\n ring = kochRefine(ring)\n }\n return polygon(ring)\n}\n\nfunction kochRefine(ring: Vec2[]): Vec2[] {\n const out: Vec2[] = []\n const n = ring.length\n for (let i = 0; i < n; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n const dx = b.x - a.x\n const dy = b.y - a.y\n const p1 = { x: a.x + dx / 3, y: a.y + dy / 3 }\n const p2 = { x: a.x + (2 * dx) / 3, y: a.y + (2 * dy) / 3 }\n // outward peak (left of directed edge in Y-down SVG coords = rotate +90 for CCW tri)\n const px = a.x + dx / 2 - dy * (Math.sqrt(3) / 6)\n const py = a.y + dy / 2 + dx * (Math.sqrt(3) / 6)\n out.push(a, p1, { x: px, y: py }, p2)\n }\n return out\n}\n\nexport const construction = {\n createCircle,\n createRect,\n createRegularPolygon,\n createRing,\n createArc,\n createStar,\n createKochSnowflake,\n}\n","import type { Group, Path } from '../types/index.js'\nimport { group, groupFromPaths, path, polygon, vec2 } from '../types/index.js'\nimport {\n clipDifference,\n clipIntersect,\n clipUnion,\n clipXor,\n pathToPathsD,\n pathsDToGroup,\n} from '../clipper/convert.js'\n\nfunction ensureClosed(p: Path): Path {\n if (!p.closed) {\n throw new Error('shapeBoolean requires closed paths')\n }\n return p\n}\n\n/** Union of two closed paths. */\nexport function union(a: Path, b: Path): Group {\n const result = clipUnion(pathToPathsD(ensureClosed(a)), pathToPathsD(ensureClosed(b)))\n return pathsDToGroup(result)\n}\n\n/** Union of many closed paths (folded left-to-right). */\nexport function unionAll(paths: Path[]): Group {\n if (paths.length === 0) return group([])\n let acc = pathToPathsD(ensureClosed(paths[0]!))\n for (let i = 1; i < paths.length; i++) {\n acc = clipUnion(acc, pathToPathsD(ensureClosed(paths[i]!)))\n }\n return pathsDToGroup(acc)\n}\n\n/** Intersection of two closed paths. */\nexport function intersect(a: Path, b: Path): Group {\n const result = clipIntersect(\n pathToPathsD(ensureClosed(a)),\n pathToPathsD(ensureClosed(b)),\n )\n return pathsDToGroup(result)\n}\n\n/** Subtract b from a (a \\ b). */\nexport function subtract(a: Path, b: Path): Group {\n const result = clipDifference(\n pathToPathsD(ensureClosed(a)),\n pathToPathsD(ensureClosed(b)),\n )\n return pathsDToGroup(result)\n}\n\n/** Subtract many shapes from a base. */\nexport function subtractAll(base: Path, shapes: Path[]): Group {\n let acc = pathToPathsD(ensureClosed(base))\n for (const s of shapes) {\n acc = clipDifference(acc, pathToPathsD(ensureClosed(s)))\n }\n return pathsDToGroup(acc)\n}\n\n/** Symmetric difference. */\nexport function symDifference(a: Path, b: Path): Group {\n const result = clipXor(pathToPathsD(ensureClosed(a)), pathToPathsD(ensureClosed(b)))\n return pathsDToGroup(result)\n}\n\n/** Complement of shape within axis-aligned rectangle [0,width] x [0,height]. */\nexport function complement(shape: Path, width: number, height: number): Group {\n const frame = polygon([\n vec2(0, 0),\n vec2(width, 0),\n vec2(width, height),\n vec2(0, height),\n ])\n return subtract(frame, shape)\n}\n\n/**\n * Painter-style occlusion: later paths in the group are on top.\n * Each path has higher paths subtracted from it; returns visible remainders.\n */\nexport function occlusionSubtract(g: Group): Group {\n const paths = g.paths\n const visible: Path[] = []\n for (let i = 0; i < paths.length; i++) {\n const subject = paths[i]\n if (!subject) continue\n const occluders = paths.slice(i + 1)\n const piece =\n occluders.length === 0\n ? groupFromPaths(ensureClosed(subject))\n : subtractAll(subject, occluders)\n for (const p of piece.paths) {\n visible.push(p)\n }\n }\n return group(visible)\n}\n\n/**\n * Regions covered by at least two input shapes.\n * If `merged`, collapse multi-overlaps into disjoint patches via union of pairwise intersects.\n */\nexport function overlapRegions(shapes: Path[], merged: boolean): Group {\n if (shapes.length < 2) return group([])\n const pairs: Path[] = []\n for (let i = 0; i < shapes.length; i++) {\n for (let j = i + 1; j < shapes.length; j++) {\n const a = shapes[i]\n const b = shapes[j]\n if (!a || !b) continue\n for (const p of intersect(a, b).paths) {\n pairs.push(p)\n }\n }\n }\n if (!merged) return group(pairs)\n return unionAll(pairs)\n}\n\nexport const shapeBoolean = {\n union,\n unionAll,\n intersect,\n subtract,\n subtractAll,\n symDifference,\n complement,\n occlusionSubtract,\n overlapRegions,\n}\n","import type { Group, Path, Segment, Vec2 } from '../types/index.js'\nimport { path, normalizeRing, polygon, segment, group } from '../types/index.js'\nimport { bounds, containsPoint, centroid } from '../predicates/index.js'\nimport { intersect, unionAll } from '../shapeBoolean/index.js'\n\n/** Exterior ring as its own closed path (holes dropped). */\nexport function extractPerimeter(p: Path): Path {\n const exterior = p.rings[0]\n if (!exterior) return path([], true)\n return polygon(exterior)\n}\n\n/** Alias for extractPerimeter. */\nexport function extractBoundary(p: Path): Path {\n return extractPerimeter(p)\n}\n\n/** Each hole as a separate closed path. */\nexport function extractHoles(p: Path): Path[] {\n const holes: Path[] = []\n for (let i = 1; i < p.rings.length; i++) {\n const hole = p.rings[i]\n if (hole && hole.length >= 3) holes.push(polygon(hole))\n }\n return holes\n}\n\n/**\n * Insert vertices so no edge exceeds `maxSegLen`.\n * Closed rings treat the wrap-around edge.\n */\nexport function densify(p: Path, maxSegLen: number): Path {\n if (maxSegLen <= 0) return p\n return path(\n p.rings.map((ring) => densifyRing(ring, maxSegLen, p.closed)),\n p.closed,\n )\n}\n\nfunction densifyRing(\n ring: Vec2[],\n maxSegLen: number,\n closed: boolean,\n): Vec2[] {\n if (ring.length < 2) return [...ring]\n const out: Vec2[] = []\n const n = ring.length\n const edges = closed ? n : n - 1\n for (let i = 0; i < edges; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n out.push(a)\n const dist = Math.hypot(b.x - a.x, b.y - a.y)\n const steps = Math.floor(dist / maxSegLen)\n for (let s = 1; s < steps; s++) {\n const t = s / steps\n out.push({\n x: a.x + (b.x - a.x) * t,\n y: a.y + (b.y - a.y) * t,\n })\n }\n }\n if (!closed) out.push(ring[n - 1]!)\n return closed ? normalizeRing(out) : out\n}\n\n/** Drop holes whose absolute area is below `minArea`. */\nexport function removeSmallHoles(p: Path, minArea: number): Path {\n if (p.rings.length <= 1) return p\n const exterior = p.rings[0]!\n const kept = p.rings.slice(1).filter((hole) => {\n const a = Math.abs(shoelace(hole))\n return a >= minArea\n })\n return path([exterior, ...kept], p.closed)\n}\n\n/** Seeded uniform random points inside a closed path. */\nexport function generateRandomPoints(\n p: Path,\n count: number,\n seed = 1,\n): Vec2[] {\n const rng = mulberry32(seed >>> 0)\n const b = bounds(p)\n const out: Vec2[] = []\n let attempts = 0\n const maxAttempts = Math.max(count * 50, 100)\n while (out.length < count && attempts < maxAttempts) {\n attempts++\n const pt = {\n x: b.minX + rng() * (b.maxX - b.minX),\n y: b.minY + rng() * (b.maxY - b.minY),\n }\n if (containsPoint(p, pt)) out.push(pt)\n }\n return out\n}\n\n/** Axis-aligned grid samples that fall inside the path. */\nexport function generateRandomGridPoints(\n p: Path,\n spacing: number,\n): Vec2[] {\n const step = Math.max(spacing, 1e-9)\n const b = bounds(p)\n const out: Vec2[] = []\n for (let x = b.minX; x <= b.maxX + 1e-12; x += step) {\n for (let y = b.minY; y <= b.maxY + 1e-12; y += step) {\n const pt = { x, y }\n if (containsPoint(p, pt)) out.push(pt)\n }\n }\n return out\n}\n\n/** Evenly spaced samples along the exterior perimeter. */\nexport function pointsOnExterior(p: Path, count: number): Vec2[] {\n const ring = p.rings[0]\n if (!ring || ring.length < 2 || count <= 0) return []\n const lengths = edgeLengths(ring, true)\n const total = lengths.reduce((s, L) => s + L, 0)\n if (total < 1e-12) return []\n const out: Vec2[] = []\n for (let i = 0; i < count; i++) {\n const target = (i / count) * total\n out.push(pointAtArcLength(ring, lengths, target))\n }\n return out\n}\n\n/** Exterior edges as segments. */\nexport function segmentsOnExterior(p: Path): Segment[] {\n const ring = p.rings[0]\n if (!ring || ring.length < 2) return []\n const n = ring.length\n const closed = p.closed\n const edges = closed ? n : n - 1\n const out: Segment[] = []\n for (let i = 0; i < edges; i++) {\n out.push(segment(ring[i]!, ring[(i + 1) % n]!))\n }\n return out\n}\n\n/**\n * Cut `p` with the infinite line through `a`→`b`.\n * Returns pieces on both sides (typically 0–2 paths).\n */\nexport function slice(p: Path, a: Vec2, b: Vec2): Group {\n const dx = b.x - a.x\n const dy = b.y - a.y\n const len = Math.hypot(dx, dy)\n if (len < 1e-12) return group([p])\n const nx = -dy / len\n const ny = dx / len\n const bb = bounds(p)\n const span =\n Math.hypot(bb.maxX - bb.minX, bb.maxY - bb.minY) * 4 + 10\n const left = halfPlanePoly(a, nx, ny, span)\n const right = halfPlanePoly(a, -nx, -ny, span)\n const parts: Path[] = []\n for (const half of [left, right]) {\n parts.push(...intersect(p, half).paths)\n }\n return group(parts)\n}\n\n/** Split through the centroid with a vertical cut (left/right). */\nexport function centroidSplit(p: Path): Group {\n const c = centroid(p)\n const bb = bounds(p)\n const pad = Math.max(bb.maxY - bb.minY, 1) + 1\n return slice(p, { x: c.x, y: c.y - pad }, { x: c.x, y: c.y + pad })\n}\n\n/** Union all closed paths in a group (dissolve overlapping contours). */\nexport function dissolve(paths: Path[]): Group {\n return unionAll(paths)\n}\n\n/** Vertex–edge intersection points between two path boundaries. */\nexport function intersectionPoints(a: Path, b: Path): Vec2[] {\n const segsA = allSegments(a)\n const segsB = allSegments(b)\n const out: Vec2[] = []\n const seen = new Set<string>()\n for (const sa of segsA) {\n for (const sb of segsB) {\n const hit = segmentIntersection(sa.a, sa.b, sb.a, sb.b)\n if (!hit) continue\n const key = `${hit.x.toFixed(9)},${hit.y.toFixed(9)}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push(hit)\n }\n }\n return out\n}\n\nfunction halfPlanePoly(\n origin: Vec2,\n nx: number,\n ny: number,\n span: number,\n): Path {\n // Orthonormal tangent\n const tx = -ny\n const ty = nx\n const o = origin\n return polygon([\n {\n x: o.x - tx * span + nx * 1e-9,\n y: o.y - ty * span + ny * 1e-9,\n },\n {\n x: o.x + tx * span + nx * 1e-9,\n y: o.y + ty * span + ny * 1e-9,\n },\n {\n x: o.x + tx * span + nx * span,\n y: o.y + ty * span + ny * span,\n },\n {\n x: o.x - tx * span + nx * span,\n y: o.y - ty * span + ny * span,\n },\n ])\n}\n\nfunction allSegments(p: Path): Segment[] {\n const out: Segment[] = []\n for (const ring of p.rings) {\n const n = ring.length\n const edges = p.closed ? n : Math.max(0, n - 1)\n for (let i = 0; i < edges; i++) {\n out.push(segment(ring[i]!, ring[(i + 1) % n]!))\n }\n }\n return out\n}\n\nfunction segmentIntersection(\n a: Vec2,\n b: Vec2,\n c: Vec2,\n d: Vec2,\n): Vec2 | null {\n const rX = b.x - a.x\n const rY = b.y - a.y\n const sX = d.x - c.x\n const sY = d.y - c.y\n const den = rX * sY - rY * sX\n if (Math.abs(den) < 1e-14) return null\n const t = ((c.x - a.x) * sY - (c.y - a.y) * sX) / den\n const u = ((c.x - a.x) * rY - (c.y - a.y) * rX) / den\n if (t < -1e-9 || t > 1 + 1e-9 || u < -1e-9 || u > 1 + 1e-9) return null\n return { x: a.x + t * rX, y: a.y + t * rY }\n}\n\nfunction edgeLengths(ring: Vec2[], closed: boolean): number[] {\n const n = ring.length\n const edges = closed ? n : n - 1\n const lengths: number[] = []\n for (let i = 0; i < edges; i++) {\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n lengths.push(Math.hypot(b.x - a.x, b.y - a.y))\n }\n return lengths\n}\n\nfunction pointAtArcLength(\n ring: Vec2[],\n lengths: number[],\n target: number,\n): Vec2 {\n let remain = target\n const n = ring.length\n for (let i = 0; i < lengths.length; i++) {\n const L = lengths[i]!\n if (remain <= L || i === lengths.length - 1) {\n const t = L < 1e-12 ? 0 : remain / L\n const a = ring[i]!\n const b = ring[(i + 1) % n]!\n return {\n x: a.x + (b.x - a.x) * t,\n y: a.y + (b.y - a.y) * t,\n }\n }\n remain -= L\n }\n return { ...ring[0]! }\n}\n\nfunction shoelace(ring: Vec2[]): number {\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\nfunction mulberry32(a: number): () => number {\n return function () {\n let t = (a += 0x6d2b79f5)\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\nexport const processing = {\n extractPerimeter,\n extractBoundary,\n extractHoles,\n densify,\n removeSmallHoles,\n generateRandomPoints,\n generateRandomGridPoints,\n pointsOnExterior,\n segmentsOnExterior,\n slice,\n centroidSplit,\n dissolve,\n intersectionPoints,\n}\n","import type { Group, Path, Vec2 } from '../types/index.js'\nimport { group, polygon } from '../types/index.js'\nimport { bounds } from '../predicates/index.js'\nimport { createRect } from '../construction/index.js'\nimport { intersect } from '../shapeBoolean/index.js'\nimport { slice } from '../processing/index.js'\n\n/** Axis-aligned square cells covering a box. */\nexport function squareGrid(\n spacing: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n): Group {\n const s = Math.max(spacing, 1e-9)\n const paths: Path[] = []\n for (let x = minX; x < maxX - 1e-12; x += s) {\n for (let y = minY; y < maxY - 1e-12; y += s) {\n const w = Math.min(s, maxX - x)\n const h = Math.min(s, maxY - y)\n if (w > 1e-12 && h > 1e-12) paths.push(createRect(x, y, w, h))\n }\n }\n return group(paths)\n}\n\n/** Pointy-top hex cells covering a box (flat radii ≈ spacing/2). */\nexport function hexTiling(\n spacing: number,\n minX: number,\n minY: number,\n maxX: number,\n maxY: number,\n): Group {\n const s = Math.max(spacing, 1e-9)\n const r = s / 2\n const dx = s * 0.75\n const dy = s * Math.sqrt(3) * 0.5\n const paths: Path[] = []\n let row = 0\n for (let y = minY + r; y <= maxY + r; y += dy, row++) {\n const x0 = minX + r + (row % 2 === 0 ? 0 : dx * 0.5)\n for (let x = x0; x <= maxX + r; x += dx) {\n paths.push(hexagon(x, y, r))\n }\n }\n return group(paths)\n}\n\nfunction hexagon(cx: number, cy: number, r: number): Path {\n const ring: Vec2[] = []\n for (let i = 0; i < 6; i++) {\n const t = (Math.PI / 180) * (60 * i - 30)\n ring.push({ x: cx + Math.cos(t) * r, y: cy + Math.sin(t) * r })\n }\n return polygon(ring)\n}\n\n/** Split a rectangle into an `nx` × `ny` grid of cells. */\nexport function rectSubdivision(p: Path, nx: number, ny: number): Group {\n const b = bounds(p)\n const cols = Math.max(1, Math.floor(nx))\n const rows = Math.max(1, Math.floor(ny))\n const dw = (b.maxX - b.minX) / cols\n const dh = (b.maxY - b.minY) / rows\n const paths: Path[] = []\n for (let i = 0; i < cols; i++) {\n for (let j = 0; j < rows; j++) {\n const cell = createRect(\n b.minX + i * dw,\n b.minY + j * dh,\n dw,\n dh,\n )\n paths.push(...intersect(p, cell).paths)\n }\n }\n return group(paths)\n}\n\n/** Recursively split each quad face into 4 by edge midpoints (`depth` times). */\nexport function quadSubdivision(faces: Path[], depth = 1): Group {\n let cur = faces\n const d = Math.max(0, Math.floor(depth))\n for (let i = 0; i < d; i++) {\n const next: Path[] = []\n for (const f of cur) {\n next.push(...subdivideQuad(f))\n }\n cur = next\n }\n return group(cur)\n}\n\nfunction subdivideQuad(p: Path): Path[] {\n const ring = p.rings[0]\n if (!ring || ring.length < 4) return [p]\n const [a, b, c, d] = ring\n if (!a || !b || !c || !d) return [p]\n const mab = mid(a, b)\n const mbc = mid(b, c)\n const mcd = mid(c, d)\n const mda = mid(d, a)\n const cen = mid(mid(a, c), mid(b, d))\n return [\n polygon([a, mab, cen, mda]),\n polygon([mab, b, mbc, cen]),\n polygon([cen, mbc, c, mcd]),\n polygon([mda, cen, mcd, d]),\n ]\n}\n\n/** Split each triangle into 4 by edge midpoints. */\nexport function triangleSubdivision(faces: Path[], depth = 1): Group {\n let cur = faces\n const d = Math.max(0, Math.floor(depth))\n for (let i = 0; i < d; i++) {\n const next: Path[] = []\n for (const f of cur) {\n next.push(...subdivideTriangle(f))\n }\n cur = next\n }\n return group(cur)\n}\n\nfunction subdivideTriangle(p: Path): Path[] {\n const ring = p.rings[0]\n if (!ring || ring.length < 3) return [p]\n const a = ring[0]!\n const b = ring[1]!\n const c = ring[2]!\n const mab = mid(a, b)\n const mbc = mid(b, c)\n const mca = mid(c, a)\n return [\n polygon([a, mab, mca]),\n polygon([mab, b, mbc]),\n polygon([mca, mbc, c]),\n polygon([mab, mbc, mca]),\n ]\n}\n\n/**\n * Slice a path into strips with parallel cuts spaced by `spacing`.\n * Angle in radians (0 = vertical cuts).\n */\nexport function sliceDivision(\n p: Path,\n spacing: number,\n angle = 0,\n): Group {\n const s = Math.max(spacing, 1e-9)\n const b = bounds(p)\n const cx = (b.minX + b.maxX) / 2\n const cy = (b.minY + b.maxY) / 2\n const diag = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) + s\n const ux = Math.cos(angle)\n const uy = Math.sin(angle)\n // normal for cut lines\n const nx = -uy\n const ny = ux\n const parts: Path[] = [p]\n for (let t = -diag; t <= diag; t += s) {\n const a = { x: cx + nx * t - ux * diag, y: cy + ny * t - uy * diag }\n const bb = { x: cx + nx * t + ux * diag, y: cy + ny * t + uy * diag }\n const next: Path[] = []\n for (const piece of parts) {\n const g = slice(piece, a, bb)\n next.push(...(g.paths.length ? g.paths : [piece]))\n }\n parts.length = 0\n parts.push(...next)\n }\n return group(parts)\n}\n\n/** Parallel hatch-like strip division (alias of sliceDivision). */\nexport function hatchSubdivision(\n p: Path,\n spacing: number,\n angle = 0,\n): Group {\n return sliceDivision(p, spacing, angle)\n}\n\nfunction mid(a: Vec2, b: Vec2): Vec2 {\n return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }\n}\n\nexport const tiling = {\n squareGrid,\n hexTiling,\n rectSubdivision,\n quadSubdivision,\n triangleSubdivision,\n sliceDivision,\n hatchSubdivision,\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { V as Vec2 } from '../vec2-CWyXN1Wj.js';
|
|
2
|
+
import { P as Path } from '../path-0RjhYjJs.js';
|
|
3
|
+
|
|
4
|
+
declare function translate(p: Path, dx: number, dy: number): Path;
|
|
5
|
+
declare function translateToOrigin(p: Path): Path;
|
|
6
|
+
declare function translateCentroidTo(p: Path, target: Vec2): Path;
|
|
7
|
+
declare function rotate(p: Path, angleRad: number, pivot?: Vec2): Path;
|
|
8
|
+
declare function rotateAroundCenter(p: Path, angleRad: number): Path;
|
|
9
|
+
declare function scale(p: Path, sx: number, sy?: number, pivot?: Vec2): Path;
|
|
10
|
+
declare function originScale(p: Path, s: number): Path;
|
|
11
|
+
declare function flipHorizontal(p: Path, x?: number): Path;
|
|
12
|
+
declare function flipVertical(p: Path, y?: number): Path;
|
|
13
|
+
/** Uniform resize so bounding-box width equals `width`. */
|
|
14
|
+
declare function resizeByWidth(p: Path, width: number): Path;
|
|
15
|
+
/** Uniform resize so bounding-box height equals `height`. */
|
|
16
|
+
declare function resizeByHeight(p: Path, height: number): Path;
|
|
17
|
+
/** Move the AABB min corner to `target`. */
|
|
18
|
+
declare function translateCornerTo(p: Path, target: Vec2): Path;
|
|
19
|
+
/** Shear: x' = x + shx * y, y' = y + shy * x (about origin or pivot). */
|
|
20
|
+
declare function shear(p: Path, shx: number, shy?: number, pivot?: Vec2): Path;
|
|
21
|
+
declare const transformation: {
|
|
22
|
+
translate: typeof translate;
|
|
23
|
+
translateToOrigin: typeof translateToOrigin;
|
|
24
|
+
translateCentroidTo: typeof translateCentroidTo;
|
|
25
|
+
translateCornerTo: typeof translateCornerTo;
|
|
26
|
+
rotate: typeof rotate;
|
|
27
|
+
rotateAroundCenter: typeof rotateAroundCenter;
|
|
28
|
+
scale: typeof scale;
|
|
29
|
+
originScale: typeof originScale;
|
|
30
|
+
flipHorizontal: typeof flipHorizontal;
|
|
31
|
+
flipVertical: typeof flipVertical;
|
|
32
|
+
resizeByWidth: typeof resizeByWidth;
|
|
33
|
+
resizeByHeight: typeof resizeByHeight;
|
|
34
|
+
shear: typeof shear;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { flipHorizontal, flipVertical, originScale, resizeByHeight, resizeByWidth, rotate, rotateAroundCenter, scale, shear, transformation, translate, translateCentroidTo, translateCornerTo, translateToOrigin };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// src/types/vec2.ts
|
|
2
|
+
function vec2(x, y) {
|
|
3
|
+
return { x, y };
|
|
4
|
+
}
|
|
5
|
+
function cloneVec2(v) {
|
|
6
|
+
return { x: v.x, y: v.y };
|
|
7
|
+
}
|
|
8
|
+
function equalsVec2(a, b, eps = 1e-9) {
|
|
9
|
+
return Math.abs(a.x - b.x) <= eps && Math.abs(a.y - b.y) <= eps;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/types/path.ts
|
|
13
|
+
function path(rings, closed = true) {
|
|
14
|
+
return {
|
|
15
|
+
rings: rings.map((ring) => ring.map(cloneVec2)),
|
|
16
|
+
closed
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function normalizeRing(ring, eps = 1e-9) {
|
|
20
|
+
if (ring.length < 2) return ring.map(cloneVec2);
|
|
21
|
+
const first = ring[0];
|
|
22
|
+
const last = ring[ring.length - 1];
|
|
23
|
+
if (first && last && equalsVec2(first, last, eps)) {
|
|
24
|
+
return ring.slice(0, -1).map(cloneVec2);
|
|
25
|
+
}
|
|
26
|
+
return ring.map(cloneVec2);
|
|
27
|
+
}
|
|
28
|
+
function clonePath(p) {
|
|
29
|
+
return {
|
|
30
|
+
closed: p.closed,
|
|
31
|
+
rings: p.rings.map((ring) => ring.map(cloneVec2))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/predicates/index.ts
|
|
36
|
+
function ringArea(ring) {
|
|
37
|
+
if (ring.length < 3) return 0;
|
|
38
|
+
let sum = 0;
|
|
39
|
+
for (let i = 0; i < ring.length; i++) {
|
|
40
|
+
const a = ring[i];
|
|
41
|
+
const b = ring[(i + 1) % ring.length];
|
|
42
|
+
sum += a.x * b.y - b.x * a.y;
|
|
43
|
+
}
|
|
44
|
+
return sum / 2;
|
|
45
|
+
}
|
|
46
|
+
function centroid(p) {
|
|
47
|
+
const ring = p.rings[0];
|
|
48
|
+
if (!ring || ring.length === 0) return vec2(0, 0);
|
|
49
|
+
const a = ringArea(ring);
|
|
50
|
+
if (Math.abs(a) < 1e-12) {
|
|
51
|
+
let sx = 0;
|
|
52
|
+
let sy = 0;
|
|
53
|
+
for (const v of ring) {
|
|
54
|
+
sx += v.x;
|
|
55
|
+
sy += v.y;
|
|
56
|
+
}
|
|
57
|
+
return vec2(sx / ring.length, sy / ring.length);
|
|
58
|
+
}
|
|
59
|
+
let cx = 0;
|
|
60
|
+
let cy = 0;
|
|
61
|
+
for (let i = 0; i < ring.length; i++) {
|
|
62
|
+
const p0 = ring[i];
|
|
63
|
+
const p1 = ring[(i + 1) % ring.length];
|
|
64
|
+
const cross = p0.x * p1.y - p1.x * p0.y;
|
|
65
|
+
cx += (p0.x + p1.x) * cross;
|
|
66
|
+
cy += (p0.y + p1.y) * cross;
|
|
67
|
+
}
|
|
68
|
+
return vec2(cx / (6 * a), cy / (6 * a));
|
|
69
|
+
}
|
|
70
|
+
function bounds(p) {
|
|
71
|
+
let minX = Infinity;
|
|
72
|
+
let minY = Infinity;
|
|
73
|
+
let maxX = -Infinity;
|
|
74
|
+
let maxY = -Infinity;
|
|
75
|
+
for (const ring of p.rings) {
|
|
76
|
+
for (const v of ring) {
|
|
77
|
+
minX = Math.min(minX, v.x);
|
|
78
|
+
minY = Math.min(minY, v.y);
|
|
79
|
+
maxX = Math.max(maxX, v.x);
|
|
80
|
+
maxY = Math.max(maxY, v.y);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!Number.isFinite(minX)) {
|
|
84
|
+
return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
|
85
|
+
}
|
|
86
|
+
return { minX, minY, maxX, maxY };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/transformation/index.ts
|
|
90
|
+
function mapPath(p, fn) {
|
|
91
|
+
return path(
|
|
92
|
+
p.rings.map((ring) => normalizeRing(ring.map(fn))),
|
|
93
|
+
p.closed
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
function translate(p, dx, dy) {
|
|
97
|
+
return mapPath(p, (v) => ({ x: v.x + dx, y: v.y + dy }));
|
|
98
|
+
}
|
|
99
|
+
function translateToOrigin(p) {
|
|
100
|
+
const c = centroid(p);
|
|
101
|
+
return translate(p, -c.x, -c.y);
|
|
102
|
+
}
|
|
103
|
+
function translateCentroidTo(p, target) {
|
|
104
|
+
const c = centroid(p);
|
|
105
|
+
return translate(p, target.x - c.x, target.y - c.y);
|
|
106
|
+
}
|
|
107
|
+
function rotate(p, angleRad, pivot) {
|
|
108
|
+
const o = pivot ?? { x: 0, y: 0 };
|
|
109
|
+
const cos = Math.cos(angleRad);
|
|
110
|
+
const sin = Math.sin(angleRad);
|
|
111
|
+
return mapPath(p, (v) => {
|
|
112
|
+
const x = v.x - o.x;
|
|
113
|
+
const y = v.y - o.y;
|
|
114
|
+
return {
|
|
115
|
+
x: o.x + x * cos - y * sin,
|
|
116
|
+
y: o.y + x * sin + y * cos
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function rotateAroundCenter(p, angleRad) {
|
|
121
|
+
return rotate(p, angleRad, centroid(p));
|
|
122
|
+
}
|
|
123
|
+
function scale(p, sx, sy = sx, pivot) {
|
|
124
|
+
const o = pivot ?? { x: 0, y: 0 };
|
|
125
|
+
return mapPath(p, (v) => ({
|
|
126
|
+
x: o.x + (v.x - o.x) * sx,
|
|
127
|
+
y: o.y + (v.y - o.y) * sy
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
function originScale(p, s) {
|
|
131
|
+
return scale(p, s, s, { x: 0, y: 0 });
|
|
132
|
+
}
|
|
133
|
+
function flipHorizontal(p, x = 0) {
|
|
134
|
+
return mapPath(p, (v) => ({ x: 2 * x - v.x, y: v.y }));
|
|
135
|
+
}
|
|
136
|
+
function flipVertical(p, y = 0) {
|
|
137
|
+
return mapPath(p, (v) => ({ x: v.x, y: 2 * y - v.y }));
|
|
138
|
+
}
|
|
139
|
+
function resizeByWidth(p, width) {
|
|
140
|
+
const b = bounds(p);
|
|
141
|
+
const w = b.maxX - b.minX;
|
|
142
|
+
if (w < 1e-12) return clonePath(p);
|
|
143
|
+
return scale(p, width / w, width / w, { x: b.minX, y: b.minY });
|
|
144
|
+
}
|
|
145
|
+
function resizeByHeight(p, height) {
|
|
146
|
+
const b = bounds(p);
|
|
147
|
+
const h = b.maxY - b.minY;
|
|
148
|
+
if (h < 1e-12) return clonePath(p);
|
|
149
|
+
return scale(p, height / h, height / h, { x: b.minX, y: b.minY });
|
|
150
|
+
}
|
|
151
|
+
function translateCornerTo(p, target) {
|
|
152
|
+
const b = bounds(p);
|
|
153
|
+
return translate(p, target.x - b.minX, target.y - b.minY);
|
|
154
|
+
}
|
|
155
|
+
function shear(p, shx, shy = 0, pivot) {
|
|
156
|
+
const o = pivot ?? { x: 0, y: 0 };
|
|
157
|
+
return mapPath(p, (v) => {
|
|
158
|
+
const x = v.x - o.x;
|
|
159
|
+
const y = v.y - o.y;
|
|
160
|
+
return {
|
|
161
|
+
x: o.x + x + shx * y,
|
|
162
|
+
y: o.y + y + shy * x
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
var transformation = {
|
|
167
|
+
translate,
|
|
168
|
+
translateToOrigin,
|
|
169
|
+
translateCentroidTo,
|
|
170
|
+
translateCornerTo,
|
|
171
|
+
rotate,
|
|
172
|
+
rotateAroundCenter,
|
|
173
|
+
scale,
|
|
174
|
+
originScale,
|
|
175
|
+
flipHorizontal,
|
|
176
|
+
flipVertical,
|
|
177
|
+
resizeByWidth,
|
|
178
|
+
resizeByHeight,
|
|
179
|
+
shear
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export { flipHorizontal, flipVertical, originScale, resizeByHeight, resizeByWidth, rotate, rotateAroundCenter, scale, shear, transformation, translate, translateCentroidTo, translateCornerTo, translateToOrigin };
|
|
183
|
+
//# sourceMappingURL=index.js.map
|
|
184
|
+
//# sourceMappingURL=index.js.map
|