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,317 @@
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 polyline(points) {
20
+ return path([points], false);
21
+ }
22
+ function polygon(exterior, holes = []) {
23
+ return path([exterior, ...holes], true);
24
+ }
25
+ function normalizeRing(ring, eps = 1e-9) {
26
+ if (ring.length < 2) return ring.map(cloneVec2);
27
+ const first = ring[0];
28
+ const last = ring[ring.length - 1];
29
+ if (first && last && equalsVec2(first, last, eps)) {
30
+ return ring.slice(0, -1).map(cloneVec2);
31
+ }
32
+ return ring.map(cloneVec2);
33
+ }
34
+ function clonePath(p) {
35
+ return {
36
+ closed: p.closed,
37
+ rings: p.rings.map((ring) => ring.map(cloneVec2))
38
+ };
39
+ }
40
+
41
+ // src/types/group.ts
42
+ function group(paths = []) {
43
+ return { paths: paths.map(clonePath) };
44
+ }
45
+
46
+ // src/svg/pathData.ts
47
+ function parsePathData(d) {
48
+ const tokens = tokenize(d);
49
+ const rings = [];
50
+ let current = [];
51
+ let cx = 0;
52
+ let cy = 0;
53
+ let startX = 0;
54
+ let startY = 0;
55
+ let sawClose = false;
56
+ let i = 0;
57
+ const pushPoint = (x, y) => {
58
+ current.push(vec2(x, y));
59
+ cx = x;
60
+ cy = y;
61
+ };
62
+ const finishSubpath = () => {
63
+ if (current.length === 0) return;
64
+ const ring = normalizeRing(current);
65
+ if (ring.length > 0) rings.push(ring);
66
+ current = [];
67
+ };
68
+ while (i < tokens.length) {
69
+ const raw = tokens[i];
70
+ if (raw === void 0) break;
71
+ i += 1;
72
+ if (!/^[MmLlHhVvZz]$/.test(raw)) {
73
+ throw new Error(
74
+ `Unsupported SVG path command or token "${raw}". Phase 1 supports M/L/H/V/Z only.`
75
+ );
76
+ }
77
+ const relative = raw === raw.toLowerCase();
78
+ const kind = raw.toUpperCase();
79
+ if (kind === "Z") {
80
+ sawClose = true;
81
+ finishSubpath();
82
+ cx = startX;
83
+ cy = startY;
84
+ continue;
85
+ }
86
+ if (kind === "M") {
87
+ finishSubpath();
88
+ const pair = readPair(tokens, i);
89
+ i = pair.next;
90
+ const x = relative ? cx + pair.x : pair.x;
91
+ const y = relative ? cy + pair.y : pair.y;
92
+ pushPoint(x, y);
93
+ startX = x;
94
+ startY = y;
95
+ while (i < tokens.length && isNumberToken(tokens[i])) {
96
+ const next = readPair(tokens, i);
97
+ i = next.next;
98
+ const nx = relative ? cx + next.x : next.x;
99
+ const ny = relative ? cy + next.y : next.y;
100
+ pushPoint(nx, ny);
101
+ }
102
+ continue;
103
+ }
104
+ if (kind === "L") {
105
+ while (i < tokens.length && isNumberToken(tokens[i])) {
106
+ const pair = readPair(tokens, i);
107
+ i = pair.next;
108
+ const x = relative ? cx + pair.x : pair.x;
109
+ const y = relative ? cy + pair.y : pair.y;
110
+ pushPoint(x, y);
111
+ }
112
+ continue;
113
+ }
114
+ if (kind === "H") {
115
+ while (i < tokens.length && isNumberToken(tokens[i])) {
116
+ const v = readNumber(tokens, i);
117
+ i = v.next;
118
+ const x = relative ? cx + v.value : v.value;
119
+ pushPoint(x, cy);
120
+ }
121
+ continue;
122
+ }
123
+ if (kind === "V") {
124
+ while (i < tokens.length && isNumberToken(tokens[i])) {
125
+ const v = readNumber(tokens, i);
126
+ i = v.next;
127
+ const y = relative ? cy + v.value : v.value;
128
+ pushPoint(cx, y);
129
+ }
130
+ }
131
+ }
132
+ finishSubpath();
133
+ return { rings, closed: sawClose };
134
+ }
135
+ function serializePathData(rings, closed) {
136
+ const parts = [];
137
+ for (const ring of rings) {
138
+ if (ring.length === 0) continue;
139
+ const first = ring[0];
140
+ if (!first) continue;
141
+ parts.push(`M ${fmt(first.x)} ${fmt(first.y)}`);
142
+ for (let i = 1; i < ring.length; i += 1) {
143
+ const p = ring[i];
144
+ if (!p) continue;
145
+ parts.push(`L ${fmt(p.x)} ${fmt(p.y)}`);
146
+ }
147
+ if (closed) parts.push("Z");
148
+ }
149
+ return parts.join(" ");
150
+ }
151
+ function tokenize(d) {
152
+ const out = [];
153
+ const re = /([MmLlHhVvZz])|([+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)/g;
154
+ let m;
155
+ while ((m = re.exec(d)) !== null) {
156
+ const token = m[1] ?? m[2];
157
+ if (token) out.push(token);
158
+ }
159
+ return out;
160
+ }
161
+ function isNumberToken(token) {
162
+ return token !== void 0 && /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(token);
163
+ }
164
+ function readNumber(tokens, i) {
165
+ const t = tokens[i];
166
+ if (!isNumberToken(t) || t === void 0) {
167
+ throw new Error(`Expected number in path data at token index ${i}`);
168
+ }
169
+ return { value: Number(t), next: i + 1 };
170
+ }
171
+ function readPair(tokens, i) {
172
+ const x = readNumber(tokens, i);
173
+ const y = readNumber(tokens, x.next);
174
+ return { x: x.value, y: y.value, next: y.next };
175
+ }
176
+ function fmt(n) {
177
+ if (Number.isInteger(n)) return String(n);
178
+ return String(Number(n.toPrecision(12)));
179
+ }
180
+
181
+ // src/svg/parse.ts
182
+ var SHAPE_RE = /<(path|rect|circle|line|polyline|polygon)\b[^>]*\/?>/gi;
183
+ function parseSvg(svg) {
184
+ const paths = [];
185
+ for (const match of svg.matchAll(SHAPE_RE)) {
186
+ const el = match[0];
187
+ const tag = match[1]?.toLowerCase();
188
+ if (!tag) continue;
189
+ if (tag === "path") {
190
+ const d = getAttr(el, "d");
191
+ if (!d) continue;
192
+ const parsed = parsePathData(d);
193
+ if (parsed.rings.length === 0) continue;
194
+ paths.push(path(parsed.rings, parsed.closed));
195
+ continue;
196
+ }
197
+ if (tag === "rect") {
198
+ const x = num(getAttr(el, "x"), 0);
199
+ const y = num(getAttr(el, "y"), 0);
200
+ const w = num(getAttr(el, "width"), 0);
201
+ const h = num(getAttr(el, "height"), 0);
202
+ if (w === 0 || h === 0) continue;
203
+ paths.push(
204
+ polygon([
205
+ vec2(x, y),
206
+ vec2(x + w, y),
207
+ vec2(x + w, y + h),
208
+ vec2(x, y + h)
209
+ ])
210
+ );
211
+ continue;
212
+ }
213
+ if (tag === "circle") {
214
+ const cx = num(getAttr(el, "cx"), 0);
215
+ const cy = num(getAttr(el, "cy"), 0);
216
+ const r = num(getAttr(el, "r"), 0);
217
+ if (r <= 0) continue;
218
+ paths.push(polygon(circleRing(cx, cy, r, 64)));
219
+ continue;
220
+ }
221
+ if (tag === "line") {
222
+ const x1 = num(getAttr(el, "x1"), 0);
223
+ const y1 = num(getAttr(el, "y1"), 0);
224
+ const x2 = num(getAttr(el, "x2"), 0);
225
+ const y2 = num(getAttr(el, "y2"), 0);
226
+ paths.push(polyline([vec2(x1, y1), vec2(x2, y2)]));
227
+ continue;
228
+ }
229
+ if (tag === "polyline") {
230
+ const pts = parsePoints(getAttr(el, "points") ?? "");
231
+ if (pts.length >= 2) paths.push(polyline(pts));
232
+ continue;
233
+ }
234
+ if (tag === "polygon") {
235
+ const pts = parsePoints(getAttr(el, "points") ?? "");
236
+ if (pts.length >= 3) paths.push(polygon(pts));
237
+ }
238
+ }
239
+ return group(paths);
240
+ }
241
+ function getAttr(el, name) {
242
+ const re = new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, "i");
243
+ const m = re.exec(el);
244
+ return m?.[1];
245
+ }
246
+ function num(value, fallback) {
247
+ if (value === void 0 || value === "") return fallback;
248
+ const n = Number(value);
249
+ return Number.isFinite(n) ? n : fallback;
250
+ }
251
+ function parsePoints(points) {
252
+ const nums = points.trim().split(/[\s,]+/).filter(Boolean).map(Number);
253
+ const out = [];
254
+ for (let i = 0; i + 1 < nums.length; i += 2) {
255
+ const x = nums[i];
256
+ const y = nums[i + 1];
257
+ if (x === void 0 || y === void 0) continue;
258
+ if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
259
+ out.push(vec2(x, y));
260
+ }
261
+ return out;
262
+ }
263
+ function circleRing(cx, cy, r, segments) {
264
+ const ring = [];
265
+ for (let i = 0; i < segments; i += 1) {
266
+ const t = i / segments * Math.PI * 2;
267
+ ring.push(vec2(cx + r * Math.cos(t), cy + r * Math.sin(t)));
268
+ }
269
+ return ring;
270
+ }
271
+
272
+ // src/svg/serialize.ts
273
+ function toSvg(g, options = {}) {
274
+ const stroke = options.stroke ?? "#000";
275
+ const strokeWidth = options.strokeWidth ?? 1;
276
+ const viewBox = options.viewBox ?? inferViewBox(g);
277
+ const widthAttr = options.width !== void 0 ? ` width="${options.width}"` : "";
278
+ const heightAttr = options.height !== void 0 ? ` height="${options.height}"` : "";
279
+ const body = g.paths.map((p) => pathElement(p, stroke, strokeWidth)).filter(Boolean).join("\n ");
280
+ return [
281
+ `<?xml version="1.0" encoding="UTF-8"?>`,
282
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}"${widthAttr}${heightAttr} fill="none">`,
283
+ body ? ` ${body}` : "",
284
+ `</svg>`
285
+ ].filter((line) => line !== "").join("\n");
286
+ }
287
+ function pathElement(p, stroke, strokeWidth) {
288
+ if (p.rings.length === 0) return "";
289
+ const d = serializePathData(p.rings, p.closed);
290
+ if (!d) return "";
291
+ return `<path d="${d}" stroke="${stroke}" stroke-width="${strokeWidth}" fill="none" />`;
292
+ }
293
+ function inferViewBox(g) {
294
+ let minX = Infinity;
295
+ let minY = Infinity;
296
+ let maxX = -Infinity;
297
+ let maxY = -Infinity;
298
+ for (const p of g.paths) {
299
+ for (const ring of p.rings) {
300
+ for (const v of ring) {
301
+ minX = Math.min(minX, v.x);
302
+ minY = Math.min(minY, v.y);
303
+ maxX = Math.max(maxX, v.x);
304
+ maxY = Math.max(maxY, v.y);
305
+ }
306
+ }
307
+ }
308
+ if (!Number.isFinite(minX)) return "0 0 100 100";
309
+ const pad = 1;
310
+ const w = Math.max(maxX - minX, 0) + pad * 2;
311
+ const h = Math.max(maxY - minY, 0) + pad * 2;
312
+ return `${minX - pad} ${minY - pad} ${w} ${h}`;
313
+ }
314
+
315
+ export { parsePathData, parseSvg, serializePathData, toSvg };
316
+ //# sourceMappingURL=index.js.map
317
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/vec2.ts","../../src/types/path.ts","../../src/types/group.ts","../../src/svg/pathData.ts","../../src/svg/parse.ts","../../src/svg/serialize.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 type { Ring } from '../types/index.js'\nimport { normalizeRing, vec2 } from '../types/index.js'\n\n/**\n * Minimal SVG path `d` tokenizer for plotter-oriented paths.\n * Supports M/m L/l H/h V/v Z/z. Curves are rejected with a clear error (Phase 1).\n */\nexport function parsePathData(d: string): { rings: Ring[]; closed: boolean } {\n const tokens = tokenize(d)\n const rings: Ring[] = []\n let current: Ring = []\n let cx = 0\n let cy = 0\n let startX = 0\n let startY = 0\n let sawClose = false\n let i = 0\n\n const pushPoint = (x: number, y: number) => {\n current.push(vec2(x, y))\n cx = x\n cy = y\n }\n\n const finishSubpath = () => {\n if (current.length === 0) return\n const ring = normalizeRing(current)\n if (ring.length > 0) rings.push(ring)\n current = []\n }\n\n while (i < tokens.length) {\n const raw = tokens[i]\n if (raw === undefined) break\n i += 1\n\n if (!/^[MmLlHhVvZz]$/.test(raw)) {\n throw new Error(\n `Unsupported SVG path command or token \"${raw}\". Phase 1 supports M/L/H/V/Z only.`,\n )\n }\n\n const relative = raw === raw.toLowerCase()\n const kind = raw.toUpperCase()\n\n if (kind === 'Z') {\n sawClose = true\n finishSubpath()\n cx = startX\n cy = startY\n continue\n }\n\n if (kind === 'M') {\n finishSubpath()\n const pair = readPair(tokens, i)\n i = pair.next\n const x = relative ? cx + pair.x : pair.x\n const y = relative ? cy + pair.y : pair.y\n pushPoint(x, y)\n startX = x\n startY = y\n while (i < tokens.length && isNumberToken(tokens[i])) {\n const next = readPair(tokens, i)\n i = next.next\n const nx = relative ? cx + next.x : next.x\n const ny = relative ? cy + next.y : next.y\n pushPoint(nx, ny)\n }\n continue\n }\n\n if (kind === 'L') {\n while (i < tokens.length && isNumberToken(tokens[i])) {\n const pair = readPair(tokens, i)\n i = pair.next\n const x = relative ? cx + pair.x : pair.x\n const y = relative ? cy + pair.y : pair.y\n pushPoint(x, y)\n }\n continue\n }\n\n if (kind === 'H') {\n while (i < tokens.length && isNumberToken(tokens[i])) {\n const v = readNumber(tokens, i)\n i = v.next\n const x = relative ? cx + v.value : v.value\n pushPoint(x, cy)\n }\n continue\n }\n\n if (kind === 'V') {\n while (i < tokens.length && isNumberToken(tokens[i])) {\n const v = readNumber(tokens, i)\n i = v.next\n const y = relative ? cy + v.value : v.value\n pushPoint(cx, y)\n }\n }\n }\n\n finishSubpath()\n return { rings, closed: sawClose }\n}\n\nexport function serializePathData(rings: Ring[], closed: boolean): string {\n const parts: string[] = []\n for (const ring of rings) {\n if (ring.length === 0) continue\n const first = ring[0]\n if (!first) continue\n parts.push(`M ${fmt(first.x)} ${fmt(first.y)}`)\n for (let i = 1; i < ring.length; i += 1) {\n const p = ring[i]\n if (!p) continue\n parts.push(`L ${fmt(p.x)} ${fmt(p.y)}`)\n }\n if (closed) parts.push('Z')\n }\n return parts.join(' ')\n}\n\nfunction tokenize(d: string): string[] {\n const out: string[] = []\n const re = /([MmLlHhVvZz])|([+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?)/g\n let m: RegExpExecArray | null\n while ((m = re.exec(d)) !== null) {\n const token = m[1] ?? m[2]\n if (token) out.push(token)\n }\n return out\n}\n\nfunction isNumberToken(token: string | undefined): boolean {\n return token !== undefined && /^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?$/.test(token)\n}\n\nfunction readNumber(tokens: string[], i: number): { value: number; next: number } {\n const t = tokens[i]\n if (!isNumberToken(t) || t === undefined) {\n throw new Error(`Expected number in path data at token index ${i}`)\n }\n return { value: Number(t), next: i + 1 }\n}\n\nfunction readPair(\n tokens: string[],\n i: number,\n): { x: number; y: number; next: number } {\n const x = readNumber(tokens, i)\n const y = readNumber(tokens, x.next)\n return { x: x.value, y: y.value, next: y.next }\n}\n\nfunction fmt(n: number): string {\n if (Number.isInteger(n)) return String(n)\n return String(Number(n.toPrecision(12)))\n}\n","import type { Group, Path } from '../types/index.js'\nimport { group, path, polygon, polyline, vec2 } from '../types/index.js'\nimport { parsePathData } from './pathData.js'\n\nconst SHAPE_RE =\n /<(path|rect|circle|line|polyline|polygon)\\b[^>]*\\/?>/gi\n\n/**\n * Parse a subset of SVG into a geometry group.\n * Supports: `path` (M/L/H/V/Z), `rect`, `circle`, `line`, `polyline`, `polygon`.\n * Element order in the document is preserved (later = on top for occlusion).\n */\nexport function parseSvg(svg: string): Group {\n const paths: Path[] = []\n\n for (const match of svg.matchAll(SHAPE_RE)) {\n const el = match[0]\n const tag = match[1]?.toLowerCase()\n if (!tag) continue\n\n if (tag === 'path') {\n const d = getAttr(el, 'd')\n if (!d) continue\n const parsed = parsePathData(d)\n if (parsed.rings.length === 0) continue\n paths.push(path(parsed.rings, parsed.closed))\n continue\n }\n\n if (tag === 'rect') {\n const x = num(getAttr(el, 'x'), 0)\n const y = num(getAttr(el, 'y'), 0)\n const w = num(getAttr(el, 'width'), 0)\n const h = num(getAttr(el, 'height'), 0)\n if (w === 0 || h === 0) continue\n paths.push(\n polygon([\n vec2(x, y),\n vec2(x + w, y),\n vec2(x + w, y + h),\n vec2(x, y + h),\n ]),\n )\n continue\n }\n\n if (tag === 'circle') {\n const cx = num(getAttr(el, 'cx'), 0)\n const cy = num(getAttr(el, 'cy'), 0)\n const r = num(getAttr(el, 'r'), 0)\n if (r <= 0) continue\n paths.push(polygon(circleRing(cx, cy, r, 64)))\n continue\n }\n\n if (tag === 'line') {\n const x1 = num(getAttr(el, 'x1'), 0)\n const y1 = num(getAttr(el, 'y1'), 0)\n const x2 = num(getAttr(el, 'x2'), 0)\n const y2 = num(getAttr(el, 'y2'), 0)\n paths.push(polyline([vec2(x1, y1), vec2(x2, y2)]))\n continue\n }\n\n if (tag === 'polyline') {\n const pts = parsePoints(getAttr(el, 'points') ?? '')\n if (pts.length >= 2) paths.push(polyline(pts))\n continue\n }\n\n if (tag === 'polygon') {\n const pts = parsePoints(getAttr(el, 'points') ?? '')\n if (pts.length >= 3) paths.push(polygon(pts))\n }\n }\n\n return group(paths)\n}\n\nfunction getAttr(el: string, name: string): string | undefined {\n const re = new RegExp(`\\\\b${name}\\\\s*=\\\\s*[\"']([^\"']*)[\"']`, 'i')\n const m = re.exec(el)\n return m?.[1]\n}\n\nfunction num(value: string | undefined, fallback: number): number {\n if (value === undefined || value === '') return fallback\n const n = Number(value)\n return Number.isFinite(n) ? n : fallback\n}\n\nfunction parsePoints(points: string) {\n const nums = points\n .trim()\n .split(/[\\s,]+/)\n .filter(Boolean)\n .map(Number)\n const out = []\n for (let i = 0; i + 1 < nums.length; i += 2) {\n const x = nums[i]\n const y = nums[i + 1]\n if (x === undefined || y === undefined) continue\n if (!Number.isFinite(x) || !Number.isFinite(y)) continue\n out.push(vec2(x, y))\n }\n return out\n}\n\nfunction circleRing(cx: number, cy: number, r: number, segments: number) {\n const ring = []\n for (let i = 0; i < segments; i += 1) {\n const t = (i / segments) * Math.PI * 2\n ring.push(vec2(cx + r * Math.cos(t), cy + r * Math.sin(t)))\n }\n return ring\n}\n","import type { Group, Path } from '../types/index.js'\nimport { serializePathData } from './pathData.js'\n\nexport type ToSvgOptions = {\n /** SVG viewBox attribute, e.g. `\"0 0 100 100\"`. */\n viewBox?: string\n /** Root width/height attributes (optional). */\n width?: number | string\n height?: number | string\n /** Stroke color (default `#000`). */\n stroke?: string\n /** Stroke width (default `1`). */\n strokeWidth?: number | string\n}\n\n/**\n * Serialize a group to plotter-oriented SVG (`fill=\"none\"`, stroked paths).\n */\nexport function toSvg(g: Group, options: ToSvgOptions = {}): string {\n const stroke = options.stroke ?? '#000'\n const strokeWidth = options.strokeWidth ?? 1\n const viewBox = options.viewBox ?? inferViewBox(g)\n const widthAttr =\n options.width !== undefined ? ` width=\"${options.width}\"` : ''\n const heightAttr =\n options.height !== undefined ? ` height=\"${options.height}\"` : ''\n\n const body = g.paths\n .map((p) => pathElement(p, stroke, strokeWidth))\n .filter(Boolean)\n .join('\\n ')\n\n return [\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>`,\n `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${viewBox}\"${widthAttr}${heightAttr} fill=\"none\">`,\n body ? ` ${body}` : '',\n `</svg>`,\n ]\n .filter((line) => line !== '')\n .join('\\n')\n}\n\nfunction pathElement(\n p: Path,\n stroke: string,\n strokeWidth: number | string,\n): string {\n if (p.rings.length === 0) return ''\n const d = serializePathData(p.rings, p.closed)\n if (!d) return ''\n return `<path d=\"${d}\" stroke=\"${stroke}\" stroke-width=\"${strokeWidth}\" fill=\"none\" />`\n}\n\nfunction inferViewBox(g: Group): string {\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n for (const p of g.paths) {\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 }\n if (!Number.isFinite(minX)) return '0 0 100 100'\n const pad = 1\n const w = Math.max(maxX - minX, 0) + pad * 2\n const h = Math.max(maxY - minY, 0) + pad * 2\n return `${minX - pad} ${minY - pad} ${w} ${h}`\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import { P as Path } from '../path-0RjhYjJs.js';
2
+ import { G as Group } from '../group-BNjVVYSQ.js';
3
+ import '../vec2-CWyXN1Wj.js';
4
+
5
+ /** Axis-aligned square cells covering a box. */
6
+ declare function squareGrid(spacing: number, minX: number, minY: number, maxX: number, maxY: number): Group;
7
+ /** Pointy-top hex cells covering a box (flat radii ≈ spacing/2). */
8
+ declare function hexTiling(spacing: number, minX: number, minY: number, maxX: number, maxY: number): Group;
9
+ /** Split a rectangle into an `nx` × `ny` grid of cells. */
10
+ declare function rectSubdivision(p: Path, nx: number, ny: number): Group;
11
+ /** Recursively split each quad face into 4 by edge midpoints (`depth` times). */
12
+ declare function quadSubdivision(faces: Path[], depth?: number): Group;
13
+ /** Split each triangle into 4 by edge midpoints. */
14
+ declare function triangleSubdivision(faces: Path[], depth?: number): Group;
15
+ /**
16
+ * Slice a path into strips with parallel cuts spaced by `spacing`.
17
+ * Angle in radians (0 = vertical cuts).
18
+ */
19
+ declare function sliceDivision(p: Path, spacing: number, angle?: number): Group;
20
+ /** Parallel hatch-like strip division (alias of sliceDivision). */
21
+ declare function hatchSubdivision(p: Path, spacing: number, angle?: number): Group;
22
+ declare const tiling: {
23
+ squareGrid: typeof squareGrid;
24
+ hexTiling: typeof hexTiling;
25
+ rectSubdivision: typeof rectSubdivision;
26
+ quadSubdivision: typeof quadSubdivision;
27
+ triangleSubdivision: typeof triangleSubdivision;
28
+ sliceDivision: typeof sliceDivision;
29
+ hatchSubdivision: typeof hatchSubdivision;
30
+ };
31
+
32
+ export { hatchSubdivision, hexTiling, quadSubdivision, rectSubdivision, sliceDivision, squareGrid, tiling, triangleSubdivision };