@weasel-js/geom 0.5.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 orochi235
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # @weasel-js/geom
2
+
3
+ Pure 2D geometry kernel for @weasel-js/core: affine, box, curve, polyline. Dependency-free core; polygon booleans in the ./booleans subpath.
4
+
5
+ Part of [weasel](https://github.com/orochi235/weasel), a domain-agnostic 2D
6
+ scene-graph canvas kit for React. See the
7
+ [API reference](https://orochi235.github.io/weasel/api/).
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ npm install @weasel-js/geom
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { /* … */ } from '@weasel-js/geom';
19
+ import { /* … */ } from '@weasel-js/geom/booleans';
20
+ ```
21
+
22
+ ## License
23
+
24
+ MIT
@@ -0,0 +1,47 @@
1
+ /** Minimal path input: a rect or a polygon command stream. geom does not
2
+ * import @weasel-js/core's `Path`; the kit maps `Path` onto this shape. */
3
+ type GeomPath = {
4
+ kind: 'rect';
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ } | {
10
+ kind: 'polygon';
11
+ commands: ArrayLike<number>;
12
+ coords: ArrayLike<number>;
13
+ fillRule?: 'nonzero' | 'evenodd';
14
+ };
15
+ /** Polygon result shape emitted by `multiPolygonToPath`. */
16
+ type GeomPolygonPath = {
17
+ kind: 'polygon';
18
+ commands: Uint8Array;
19
+ coords: Float32Array;
20
+ fillRule: 'nonzero';
21
+ };
22
+
23
+ /** Union of N paths. Commutative. Returns an empty path if all inputs are empty. */
24
+ declare function pathUnion(...paths: GeomPath[]): GeomPolygonPath;
25
+ /** Intersection of N paths. Commutative. Empty result is an empty path. */
26
+ declare function pathIntersect(...paths: GeomPath[]): GeomPolygonPath;
27
+ /** Asymmetric difference: returns `a − b`. */
28
+ declare function pathSubtract(a: GeomPath, b: GeomPath): GeomPolygonPath;
29
+ /** Symmetric difference (XOR) of N paths. Commutative. */
30
+ declare function pathExclude(...paths: GeomPath[]): GeomPolygonPath;
31
+ /**
32
+ * Fracture N paths along every intersection into the maximal set of
33
+ * non-overlapping regions. Returns one polygon path per region.
34
+ *
35
+ * Algorithm: for every non-empty subset S ⊆ {A1..AN}, emit the region
36
+ * `(∩ S) − (∪ complement)` — points covered by exactly the members of S
37
+ * and no others. By construction the emitted regions are pairwise disjoint
38
+ * and their union equals `pathUnion(A1..AN)`.
39
+ *
40
+ * For N=2 this collapses to the three Illustrator "Divide" outputs
41
+ * (A−B, B−A, A∩B). For N=3 up to 7 regions are emitted. The 2^N − 1
42
+ * subset count limits this to small N in practice; passing more than ~8
43
+ * inputs is a misuse — the polygon-clipping kernel dominates anyway.
44
+ */
45
+ declare function pathDivide(...paths: GeomPath[]): GeomPolygonPath[];
46
+
47
+ export { type GeomPath, type GeomPolygonPath, pathDivide, pathExclude, pathIntersect, pathSubtract, pathUnion };
@@ -0,0 +1,182 @@
1
+ import { PATH_M, PATH_L, PATH_Z, PATH_Q, elevateQuadraticToCubic, flattenCubic, PATH_C } from '../chunk-K5JJ6WRB.js';
2
+ import polygonClipping from 'polygon-clipping';
3
+
4
+ // src/booleans/adapter.ts
5
+ var DEFAULT_FLATTEN_TOLERANCE = 0.5;
6
+ function pathToMultiPolygon(path, opts = {}) {
7
+ if (path.kind === "rect") {
8
+ const { x, y, width, height } = path;
9
+ const ring = [
10
+ [x, y],
11
+ [x + width, y],
12
+ [x + width, y + height],
13
+ [x, y + height],
14
+ [x, y]
15
+ ];
16
+ return [[ring]];
17
+ }
18
+ const tolerance = opts.tolerance ?? DEFAULT_FLATTEN_TOLERANCE;
19
+ const rings = [];
20
+ let current = null;
21
+ let cx = 0, cy = 0;
22
+ let ci = 0;
23
+ const { commands, coords } = path;
24
+ const finalizeCurrent = () => {
25
+ if (!current || current.length === 0) return;
26
+ const first = current[0];
27
+ const last = current[current.length - 1];
28
+ if (first[0] !== last[0] || first[1] !== last[1]) {
29
+ current.push([first[0], first[1]]);
30
+ }
31
+ if (current.length >= 4) rings.push(current);
32
+ current = null;
33
+ };
34
+ for (let i = 0; i < commands.length; i++) {
35
+ const cmd = commands[i];
36
+ switch (cmd) {
37
+ case PATH_M: {
38
+ finalizeCurrent();
39
+ cx = coords[ci];
40
+ cy = coords[ci + 1];
41
+ current = [[cx, cy]];
42
+ ci += 2;
43
+ break;
44
+ }
45
+ case PATH_L: {
46
+ cx = coords[ci];
47
+ cy = coords[ci + 1];
48
+ if (current) current.push([cx, cy]);
49
+ ci += 2;
50
+ break;
51
+ }
52
+ case PATH_C: {
53
+ const x1 = coords[ci], y1 = coords[ci + 1];
54
+ const x2 = coords[ci + 2], y2 = coords[ci + 3];
55
+ const x3 = coords[ci + 4], y3 = coords[ci + 5];
56
+ const out = [];
57
+ flattenCubic(cx, cy, x1, y1, x2, y2, x3, y3, tolerance, out);
58
+ if (current) for (let k = 0; k < out.length; k += 2) current.push([out[k], out[k + 1]]);
59
+ cx = x3;
60
+ cy = y3;
61
+ ci += 6;
62
+ break;
63
+ }
64
+ case PATH_Q: {
65
+ const x1 = coords[ci], y1 = coords[ci + 1];
66
+ const x2 = coords[ci + 2], y2 = coords[ci + 3];
67
+ const [c1x, c1y, c2x, c2y] = elevateQuadraticToCubic(cx, cy, x1, y1, x2, y2);
68
+ const out = [];
69
+ flattenCubic(cx, cy, c1x, c1y, c2x, c2y, x2, y2, tolerance, out);
70
+ if (current) for (let k = 0; k < out.length; k += 2) current.push([out[k], out[k + 1]]);
71
+ cx = x2;
72
+ cy = y2;
73
+ ci += 4;
74
+ break;
75
+ }
76
+ case PATH_Z: {
77
+ finalizeCurrent();
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ finalizeCurrent();
83
+ if (rings.length === 0) return [];
84
+ return rings.map((r) => [r]);
85
+ }
86
+ function multiPolygonToPath(mp) {
87
+ let nCmds = 0;
88
+ let nCoords = 0;
89
+ for (const poly of mp) {
90
+ for (const ring of poly) {
91
+ if (ring.length < 4) continue;
92
+ const unique = ring.length - 1;
93
+ nCmds += 1 + (unique - 1) + 1;
94
+ nCoords += unique * 2;
95
+ }
96
+ }
97
+ const commands = new Uint8Array(nCmds);
98
+ const coords = new Float32Array(nCoords);
99
+ let ci = 0;
100
+ let pi = 0;
101
+ for (const poly of mp) {
102
+ for (const ring of poly) {
103
+ if (ring.length < 4) continue;
104
+ const unique = ring.length - 1;
105
+ commands[ci++] = PATH_M;
106
+ coords[pi++] = ring[0][0];
107
+ coords[pi++] = ring[0][1];
108
+ for (let k = 1; k < unique; k++) {
109
+ commands[ci++] = PATH_L;
110
+ coords[pi++] = ring[k][0];
111
+ coords[pi++] = ring[k][1];
112
+ }
113
+ commands[ci++] = PATH_Z;
114
+ }
115
+ }
116
+ return { kind: "polygon", commands, coords, fillRule: "nonzero" };
117
+ }
118
+
119
+ // src/booleans/index.ts
120
+ function pathUnion(...paths) {
121
+ if (paths.length === 0) return multiPolygonToPath([]);
122
+ const mps = paths.map((p) => pathToMultiPolygon(p));
123
+ const [head, ...rest] = mps;
124
+ return multiPolygonToPath(polygonClipping.union(head, ...rest));
125
+ }
126
+ function pathIntersect(...paths) {
127
+ if (paths.length === 0) return multiPolygonToPath([]);
128
+ const mps = paths.map((p) => pathToMultiPolygon(p));
129
+ const [head, ...rest] = mps;
130
+ return multiPolygonToPath(polygonClipping.intersection(head, ...rest));
131
+ }
132
+ function pathSubtract(a, b) {
133
+ return multiPolygonToPath(polygonClipping.difference(pathToMultiPolygon(a), pathToMultiPolygon(b)));
134
+ }
135
+ function pathExclude(...paths) {
136
+ if (paths.length === 0) return multiPolygonToPath([]);
137
+ const mps = paths.map((p) => pathToMultiPolygon(p));
138
+ const [head, ...rest] = mps;
139
+ return multiPolygonToPath(polygonClipping.xor(head, ...rest));
140
+ }
141
+ function pathDivide(...paths) {
142
+ if (paths.length === 0) return [];
143
+ if (paths.length === 1) {
144
+ return [multiPolygonToPath(pathToMultiPolygon(paths[0]))];
145
+ }
146
+ const mps = paths.map((p) => pathToMultiPolygon(p));
147
+ const n = mps.length;
148
+ const out = [];
149
+ const subsets = [];
150
+ for (let mask = 1; mask < 1 << n; mask++) subsets.push(mask);
151
+ subsets.sort((a, b) => popcount(a) - popcount(b) || a - b);
152
+ for (const mask of subsets) {
153
+ const inside = [];
154
+ const outside = [];
155
+ for (let i = 0; i < n; i++) {
156
+ (mask & 1 << i ? inside : outside).push(mps[i]);
157
+ }
158
+ const [insideHead, ...insideRest] = inside;
159
+ let region = insideRest.length === 0 ? insideHead : polygonClipping.intersection(insideHead, ...insideRest);
160
+ if (region.length === 0) continue;
161
+ if (outside.length > 0) {
162
+ const [outHead, ...outRest] = outside;
163
+ const outsideUnion = polygonClipping.union(outHead, ...outRest);
164
+ region = polygonClipping.difference(region, outsideUnion);
165
+ if (region.length === 0) continue;
166
+ }
167
+ out.push(multiPolygonToPath(region));
168
+ }
169
+ return out;
170
+ }
171
+ function popcount(n) {
172
+ let c = 0;
173
+ while (n) {
174
+ n &= n - 1;
175
+ c++;
176
+ }
177
+ return c;
178
+ }
179
+
180
+ export { pathDivide, pathExclude, pathIntersect, pathSubtract, pathUnion };
181
+ //# sourceMappingURL=index.js.map
182
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/booleans/adapter.ts","../../src/booleans/index.ts"],"names":[],"mappings":";;;;AA4BA,IAAM,yBAAA,GAA4B,GAAA;AAoB3B,SAAS,kBAAA,CACd,IAAA,EACA,IAAA,GAAkC,EAAC,EACrB;AACd,EAAA,IAAI,IAAA,CAAK,SAAS,MAAA,EAAQ;AACxB,IAAA,MAAM,EAAE,CAAA,EAAG,CAAA,EAAG,KAAA,EAAO,QAAO,GAAI,IAAA;AAChC,IAAA,MAAM,IAAA,GAAa;AAAA,MACjB,CAAC,GAAG,CAAC,CAAA;AAAA,MACL,CAAC,CAAA,GAAI,KAAA,EAAO,CAAC,CAAA;AAAA,MACb,CAAC,CAAA,GAAI,KAAA,EAAO,CAAA,GAAI,MAAM,CAAA;AAAA,MACtB,CAAC,CAAA,EAAG,CAAA,GAAI,MAAM,CAAA;AAAA,MACd,CAAC,GAAG,CAAC;AAAA,KACP;AACA,IAAA,OAAO,CAAC,CAAC,IAAI,CAAC,CAAA;AAAA,EAChB;AAEA,EAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,yBAAA;AACpC,EAAA,MAAM,QAAgB,EAAC;AACvB,EAAA,IAAI,OAAA,GAAuB,IAAA;AAC3B,EAAA,IAAI,EAAA,GAAK,GAAG,EAAA,GAAK,CAAA;AACjB,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAO,GAAI,IAAA;AAE7B,EAAA,MAAM,kBAAkB,MAAM;AAC5B,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG;AAEtC,IAAA,MAAM,KAAA,GAAQ,QAAQ,CAAC,CAAA;AACvB,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AACvC,IAAA,IAAI,KAAA,CAAM,CAAC,CAAA,KAAM,IAAA,CAAK,CAAC,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA,KAAM,IAAA,CAAK,CAAC,CAAA,EAAG;AAChD,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAC,KAAA,CAAM,CAAC,GAAG,KAAA,CAAM,CAAC,CAAC,CAAC,CAAA;AAAA,IACnC;AACA,IAAA,IAAI,OAAA,CAAQ,MAAA,IAAU,CAAA,EAAG,KAAA,CAAM,KAAK,OAAO,CAAA;AAC3C,IAAA,OAAA,GAAU,IAAA;AAAA,EACZ,CAAA;AAEA,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,QAAQ,CAAA,EAAA,EAAK;AACxC,IAAA,MAAM,GAAA,GAAM,SAAS,CAAC,CAAA;AACtB,IAAA,QAAQ,GAAA;AAAK,MACX,KAAK,MAAA,EAAQ;AACX,QAAA,eAAA,EAAgB;AAChB,QAAA,EAAA,GAAK,OAAO,EAAE,CAAA;AAAG,QAAA,EAAA,GAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AACnC,QAAA,OAAA,GAAU,CAAC,CAAC,EAAA,EAAI,EAAE,CAAC,CAAA;AACnB,QAAA,EAAA,IAAM,CAAA;AACN,QAAA;AAAA,MACF;AAAA,MACA,KAAK,MAAA,EAAQ;AACX,QAAA,EAAA,GAAK,OAAO,EAAE,CAAA;AAAG,QAAA,EAAA,GAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AACnC,QAAA,IAAI,SAAS,OAAA,CAAQ,IAAA,CAAK,CAAC,EAAA,EAAI,EAAE,CAAC,CAAA;AAClC,QAAA,EAAA,IAAM,CAAA;AACN,QAAA;AAAA,MACF;AAAA,MACA,KAAK,MAAA,EAAQ;AACX,QAAA,MAAM,KAAK,MAAA,CAAO,EAAE,GAAG,EAAA,GAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AACzC,QAAA,MAAM,EAAA,GAAK,OAAO,EAAA,GAAK,CAAC,GAAG,EAAA,GAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAC7C,QAAA,MAAM,EAAA,GAAK,OAAO,EAAA,GAAK,CAAC,GAAG,EAAA,GAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAC7C,QAAA,MAAM,MAAgB,EAAC;AACvB,QAAA,YAAA,CAAa,EAAA,EAAI,IAAI,EAAA,EAAI,EAAA,EAAI,IAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,SAAA,EAAW,GAAG,CAAA;AAC3D,QAAA,IAAI,SAAS,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,GAAA,CAAI,QAAQ,CAAA,IAAK,CAAA,UAAW,IAAA,CAAK,CAAC,IAAI,CAAC,CAAA,EAAG,IAAI,CAAA,GAAI,CAAC,CAAC,CAAC,CAAA;AACtF,QAAA,EAAA,GAAK,EAAA;AAAI,QAAA,EAAA,GAAK,EAAA;AACd,QAAA,EAAA,IAAM,CAAA;AACN,QAAA;AAAA,MACF;AAAA,MACA,KAAK,MAAA,EAAQ;AACX,QAAA,MAAM,KAAK,MAAA,CAAO,EAAE,GAAG,EAAA,GAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AACzC,QAAA,MAAM,EAAA,GAAK,OAAO,EAAA,GAAK,CAAC,GAAG,EAAA,GAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAC7C,QAAA,MAAM,CAAC,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAG,CAAA,GAAI,uBAAA,CAAwB,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA;AAC3E,QAAA,MAAM,MAAgB,EAAC;AACvB,QAAA,YAAA,CAAa,EAAA,EAAI,IAAI,GAAA,EAAK,GAAA,EAAK,KAAK,GAAA,EAAK,EAAA,EAAI,EAAA,EAAI,SAAA,EAAW,GAAG,CAAA;AAC/D,QAAA,IAAI,SAAS,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,GAAA,CAAI,QAAQ,CAAA,IAAK,CAAA,UAAW,IAAA,CAAK,CAAC,IAAI,CAAC,CAAA,EAAG,IAAI,CAAA,GAAI,CAAC,CAAC,CAAC,CAAA;AACtF,QAAA,EAAA,GAAK,EAAA;AAAI,QAAA,EAAA,GAAK,EAAA;AACd,QAAA,EAAA,IAAM,CAAA;AACN,QAAA;AAAA,MACF;AAAA,MACA,KAAK,MAAA,EAAQ;AACX,QAAA,eAAA,EAAgB;AAChB,QAAA;AAAA,MACF;AAAA;AACF,EACF;AACA,EAAA,eAAA,EAAgB;AAEhB,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAIhC,EAAA,OAAO,MAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAC,CAAC,CAAA;AAC7B;AAGO,SAAS,mBAAmB,EAAA,EAAmC;AAEpE,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,KAAA,MAAW,QAAQ,EAAA,EAAI;AACrB,IAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,MAAA,IAAI,IAAA,CAAK,SAAS,CAAA,EAAG;AAErB,MAAA,MAAM,MAAA,GAAS,KAAK,MAAA,GAAS,CAAA;AAC7B,MAAA,KAAA,IAAS,CAAA,IAAK,SAAS,CAAA,CAAA,GAAK,CAAA;AAC5B,MAAA,OAAA,IAAW,MAAA,GAAS,CAAA;AAAA,IACtB;AAAA,EACF;AACA,EAAA,MAAM,QAAA,GAAW,IAAI,UAAA,CAAW,KAAK,CAAA;AACrC,EAAA,MAAM,MAAA,GAAS,IAAI,YAAA,CAAa,OAAO,CAAA;AACvC,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,KAAA,MAAW,QAAQ,EAAA,EAAI;AACrB,IAAA,KAAA,MAAW,QAAQ,IAAA,EAAM;AACvB,MAAA,IAAI,IAAA,CAAK,SAAS,CAAA,EAAG;AACrB,MAAA,MAAM,MAAA,GAAS,KAAK,MAAA,GAAS,CAAA;AAE7B,MAAA,QAAA,CAAS,IAAI,CAAA,GAAI,MAAA;AACjB,MAAA,MAAA,CAAO,EAAA,EAAI,CAAA,GAAI,IAAA,CAAK,CAAC,EAAE,CAAC,CAAA;AACxB,MAAA,MAAA,CAAO,EAAA,EAAI,CAAA,GAAI,IAAA,CAAK,CAAC,EAAE,CAAC,CAAA;AAExB,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AAC/B,QAAA,QAAA,CAAS,IAAI,CAAA,GAAI,MAAA;AACjB,QAAA,MAAA,CAAO,EAAA,EAAI,CAAA,GAAI,IAAA,CAAK,CAAC,EAAE,CAAC,CAAA;AACxB,QAAA,MAAA,CAAO,EAAA,EAAI,CAAA,GAAI,IAAA,CAAK,CAAC,EAAE,CAAC,CAAA;AAAA,MAC1B;AAEA,MAAA,QAAA,CAAS,IAAI,CAAA,GAAI,MAAA;AAAA,IACnB;AAAA,EACF;AACA,EAAA,OAAO,EAAE,IAAA,EAAM,SAAA,EAAW,QAAA,EAAU,MAAA,EAAQ,UAAU,SAAA,EAAU;AAClE;;;ACxJO,SAAS,aAAa,KAAA,EAAoC;AAC/D,EAAA,IAAI,MAAM,MAAA,KAAW,CAAA,EAAG,OAAO,kBAAA,CAAmB,EAAE,CAAA;AACpD,EAAA,MAAM,MAAM,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,kBAAA,CAAmB,CAAC,CAAC,CAAA;AAClD,EAAA,MAAM,CAAC,IAAA,EAAM,GAAG,IAAI,CAAA,GAAI,GAAA;AACxB,EAAA,OAAO,mBAAmB,eAAA,CAAgB,KAAA,CAAM,IAAA,EAAM,GAAG,IAAI,CAAC,CAAA;AAChE;AAGO,SAAS,iBAAiB,KAAA,EAAoC;AACnE,EAAA,IAAI,MAAM,MAAA,KAAW,CAAA,EAAG,OAAO,kBAAA,CAAmB,EAAE,CAAA;AACpD,EAAA,MAAM,MAAM,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,kBAAA,CAAmB,CAAC,CAAC,CAAA;AAClD,EAAA,MAAM,CAAC,IAAA,EAAM,GAAG,IAAI,CAAA,GAAI,GAAA;AACxB,EAAA,OAAO,mBAAmB,eAAA,CAAgB,YAAA,CAAa,IAAA,EAAM,GAAG,IAAI,CAAC,CAAA;AACvE;AAGO,SAAS,YAAA,CAAa,GAAa,CAAA,EAA8B;AACtE,EAAA,OAAO,kBAAA,CAAmB,gBAAgB,UAAA,CAAW,kBAAA,CAAmB,CAAC,CAAA,EAAG,kBAAA,CAAmB,CAAC,CAAC,CAAC,CAAA;AACpG;AAGO,SAAS,eAAe,KAAA,EAAoC;AACjE,EAAA,IAAI,MAAM,MAAA,KAAW,CAAA,EAAG,OAAO,kBAAA,CAAmB,EAAE,CAAA;AACpD,EAAA,MAAM,MAAM,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,kBAAA,CAAmB,CAAC,CAAC,CAAA;AAClD,EAAA,MAAM,CAAC,IAAA,EAAM,GAAG,IAAI,CAAA,GAAI,GAAA;AACxB,EAAA,OAAO,mBAAmB,eAAA,CAAgB,GAAA,CAAI,IAAA,EAAM,GAAG,IAAI,CAAC,CAAA;AAC9D;AAgBO,SAAS,cAAc,KAAA,EAAsC;AAClE,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAChC,EAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACtB,IAAA,OAAO,CAAC,kBAAA,CAAmB,kBAAA,CAAmB,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AAAA,EAC1D;AACA,EAAA,MAAM,MAAM,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,kBAAA,CAAmB,CAAC,CAAC,CAAA;AAClD,EAAA,MAAM,IAAI,GAAA,CAAI,MAAA;AACd,EAAA,MAAM,MAAyB,EAAC;AAKhC,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,KAAA,IAAS,IAAA,GAAO,GAAG,IAAA,GAAO,CAAA,IAAK,GAAG,IAAA,EAAA,EAAQ,OAAA,CAAQ,KAAK,IAAI,CAAA;AAC3D,EAAA,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,QAAA,CAAS,CAAC,CAAA,GAAI,QAAA,CAAS,CAAC,CAAA,IAAK,CAAA,GAAI,CAAC,CAAA;AAEzD,EAAA,KAAA,MAAW,QAAQ,OAAA,EAAS;AAC1B,IAAA,MAAM,SAAqB,EAAC;AAC5B,IAAA,MAAM,UAAsB,EAAC;AAC7B,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,MAAA,CAAC,IAAA,GAAQ,KAAK,CAAA,GAAK,MAAA,GAAS,SAAS,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,IAClD;AACA,IAAA,MAAM,CAAC,UAAA,EAAY,GAAG,UAAU,CAAA,GAAI,MAAA;AACpC,IAAA,IAAI,MAAA,GAAS,WAAW,MAAA,KAAW,CAAA,GAC/B,aACA,eAAA,CAAgB,YAAA,CAAa,UAAA,EAAY,GAAG,UAAU,CAAA;AAC1D,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACzB,IAAA,IAAI,OAAA,CAAQ,SAAS,CAAA,EAAG;AACtB,MAAA,MAAM,CAAC,OAAA,EAAS,GAAG,OAAO,CAAA,GAAI,OAAA;AAC9B,MAAA,MAAM,YAAA,GAAe,eAAA,CAAgB,KAAA,CAAM,OAAA,EAAS,GAAG,OAAO,CAAA;AAC9D,MAAA,MAAA,GAAS,eAAA,CAAgB,UAAA,CAAW,MAAA,EAAQ,YAAY,CAAA;AACxD,MAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AAAA,IAC3B;AACA,IAAA,GAAA,CAAI,IAAA,CAAK,kBAAA,CAAmB,MAAM,CAAC,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,GAAA;AACT;AAEA,SAAS,SAAS,CAAA,EAAmB;AACnC,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,OAAO,CAAA,EAAG;AAAE,IAAA,CAAA,IAAK,CAAA,GAAI,CAAA;AAAG,IAAA,CAAA,EAAA;AAAA,EAAK;AAC7B,EAAA,OAAO,CAAA;AACT","file":"index.js","sourcesContent":["/**\n * Adapter between geom's path input shape and `polygon-clipping`'s\n * `MultiPolygon` format. Kept in a dedicated module so the dep is one\n * import away from being swappable. Ported from\n * `src/features/paths/booleans.adapter.ts`.\n *\n * `polygon-clipping` expects:\n * MultiPolygon = Polygon[]\n * Polygon = Ring[] (first ring outer, subsequent rings holes)\n * Ring = [x, y][] (closed — first vertex repeated as last)\n *\n * Bezier inputs are flattened (cubic; quadratics are degree-elevated to cubic\n * first) — v1 is straight-line only. Open contours are implicitly closed\n * because boolean ops are defined on closed regions; polylines have zero area\n * and would otherwise be silently dropped.\n *\n * The `[x,y][]` nested-array form here is the one place geom's flat-everywhere\n * rule is suspended — it is the third-party clipper's required API.\n */\nimport { PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z } from '../commands';\nimport { flattenCubic, elevateQuadraticToCubic } from '../curve';\n\n/** Minimal path input: a rect or a polygon command stream. geom does not\n * import @weasel-js/core's `Path`; the kit maps `Path` onto this shape. */\nexport type GeomPath =\n | { kind: 'rect'; x: number; y: number; width: number; height: number }\n | { kind: 'polygon'; commands: ArrayLike<number>; coords: ArrayLike<number>; fillRule?: 'nonzero' | 'evenodd' };\n\nconst DEFAULT_FLATTEN_TOLERANCE = 0.5;\n\n/** A `[x, y]` 2-tuple. */\nexport type Pair = [number, number];\n/** Closed ring (first vertex repeated as last). */\nexport type Ring = Pair[];\n/** Polygon: one outer ring optionally followed by hole rings. */\nexport type Polygon = Ring[];\n/** MultiPolygon: list of polygons (used for boolean op I/O). */\nexport type MultiPolygon = Polygon[];\n\nexport interface PathToMultiPolygonOptions {\n /** Flattening tolerance for bezier segments. Default: `DEFAULT_FLATTEN_TOLERANCE`. */\n tolerance?: number;\n}\n\n/** Polygon result shape emitted by `multiPolygonToPath`. */\nexport type GeomPolygonPath = { kind: 'polygon'; commands: Uint8Array; coords: Float32Array; fillRule: 'nonzero' };\n\n/** Convert a `GeomPath` to a `MultiPolygon` suitable for `polygon-clipping`. */\nexport function pathToMultiPolygon(\n path: GeomPath,\n opts: PathToMultiPolygonOptions = {},\n): MultiPolygon {\n if (path.kind === 'rect') {\n const { x, y, width, height } = path;\n const ring: Ring = [\n [x, y],\n [x + width, y],\n [x + width, y + height],\n [x, y + height],\n [x, y],\n ];\n return [[ring]];\n }\n\n const tolerance = opts.tolerance ?? DEFAULT_FLATTEN_TOLERANCE;\n const rings: Ring[] = [];\n let current: Ring | null = null;\n let cx = 0, cy = 0;\n let ci = 0;\n const { commands, coords } = path;\n\n const finalizeCurrent = () => {\n if (!current || current.length === 0) return;\n // Close the ring by repeating the first vertex if not already closed.\n const first = current[0];\n const last = current[current.length - 1];\n if (first[0] !== last[0] || first[1] !== last[1]) {\n current.push([first[0], first[1]]);\n }\n if (current.length >= 4) rings.push(current); // 3 unique + 1 closing\n current = null;\n };\n\n for (let i = 0; i < commands.length; i++) {\n const cmd = commands[i];\n switch (cmd) {\n case PATH_M: {\n finalizeCurrent();\n cx = coords[ci]; cy = coords[ci + 1];\n current = [[cx, cy]];\n ci += 2;\n break;\n }\n case PATH_L: {\n cx = coords[ci]; cy = coords[ci + 1];\n if (current) current.push([cx, cy]);\n ci += 2;\n break;\n }\n case PATH_C: {\n const x1 = coords[ci], y1 = coords[ci + 1];\n const x2 = coords[ci + 2], y2 = coords[ci + 3];\n const x3 = coords[ci + 4], y3 = coords[ci + 5];\n const out: number[] = [];\n flattenCubic(cx, cy, x1, y1, x2, y2, x3, y3, tolerance, out);\n if (current) for (let k = 0; k < out.length; k += 2) current.push([out[k], out[k + 1]]);\n cx = x3; cy = y3;\n ci += 6;\n break;\n }\n case PATH_Q: {\n const x1 = coords[ci], y1 = coords[ci + 1];\n const x2 = coords[ci + 2], y2 = coords[ci + 3];\n const [c1x, c1y, c2x, c2y] = elevateQuadraticToCubic(cx, cy, x1, y1, x2, y2);\n const out: number[] = [];\n flattenCubic(cx, cy, c1x, c1y, c2x, c2y, x2, y2, tolerance, out);\n if (current) for (let k = 0; k < out.length; k += 2) current.push([out[k], out[k + 1]]);\n cx = x2; cy = y2;\n ci += 4;\n break;\n }\n case PATH_Z: {\n finalizeCurrent();\n break;\n }\n }\n }\n finalizeCurrent();\n\n if (rings.length === 0) return [];\n // v1: emit every ring as its own polygon. `polygon-clipping`'s engine\n // re-classifies winding internally during the op, so we don't need to\n // pre-sort outer/hole rings.\n return rings.map((r) => [r]);\n}\n\n/** Convert a `MultiPolygon` to a polygon path with `fillRule: 'nonzero'`. */\nexport function multiPolygonToPath(mp: MultiPolygon): GeomPolygonPath {\n // First pass: count total commands and coord floats.\n let nCmds = 0;\n let nCoords = 0;\n for (const poly of mp) {\n for (const ring of poly) {\n if (ring.length < 4) continue; // 3 unique + 1 closing minimum\n // Drop the repeated closing vertex; emit M + (n-2) L + Z.\n const unique = ring.length - 1;\n nCmds += 1 + (unique - 1) + 1; // M + L*(unique-1) + Z\n nCoords += unique * 2;\n }\n }\n const commands = new Uint8Array(nCmds);\n const coords = new Float32Array(nCoords);\n let ci = 0;\n let pi = 0;\n for (const poly of mp) {\n for (const ring of poly) {\n if (ring.length < 4) continue;\n const unique = ring.length - 1;\n // M (first vertex)\n commands[ci++] = PATH_M;\n coords[pi++] = ring[0][0];\n coords[pi++] = ring[0][1];\n // L for vertices 1..unique-1\n for (let k = 1; k < unique; k++) {\n commands[ci++] = PATH_L;\n coords[pi++] = ring[k][0];\n coords[pi++] = ring[k][1];\n }\n // Z\n commands[ci++] = PATH_Z;\n }\n }\n return { kind: 'polygon', commands, coords, fillRule: 'nonzero' };\n}\n","/**\n * Polygon-boolean operations on `GeomPath` values, backed by\n * `polygon-clipping`. Ported from `src/features/paths/booleans.ts`.\n *\n * v1 limitations (documented; see design doc):\n * - Bezier inputs are flattened to straight-line segments before clipping.\n * The result therefore contains only M/L/Z commands.\n * - Open contours are treated as closed for boolean purposes (a polyline\n * has zero area and would otherwise be silently dropped).\n * - Output `fillRule` is always `'nonzero'`. The engine emits canonical\n * non-overlapping rings, so the choice is cosmetic on its output.\n *\n * This is the ONLY geom module that depends on `polygon-clipping`; it lives in\n * the `@weasel-js/geom/booleans` subpath so the core stays `deps: {}`.\n */\nimport polygonClipping from 'polygon-clipping';\nimport { pathToMultiPolygon, multiPolygonToPath, type GeomPath, type GeomPolygonPath } from './adapter';\n\nexport type { GeomPath, GeomPolygonPath } from './adapter';\n\n/** Union of N paths. Commutative. Returns an empty path if all inputs are empty. */\nexport function pathUnion(...paths: GeomPath[]): GeomPolygonPath {\n if (paths.length === 0) return multiPolygonToPath([]);\n const mps = paths.map((p) => pathToMultiPolygon(p));\n const [head, ...rest] = mps;\n return multiPolygonToPath(polygonClipping.union(head, ...rest));\n}\n\n/** Intersection of N paths. Commutative. Empty result is an empty path. */\nexport function pathIntersect(...paths: GeomPath[]): GeomPolygonPath {\n if (paths.length === 0) return multiPolygonToPath([]);\n const mps = paths.map((p) => pathToMultiPolygon(p));\n const [head, ...rest] = mps;\n return multiPolygonToPath(polygonClipping.intersection(head, ...rest));\n}\n\n/** Asymmetric difference: returns `a − b`. */\nexport function pathSubtract(a: GeomPath, b: GeomPath): GeomPolygonPath {\n return multiPolygonToPath(polygonClipping.difference(pathToMultiPolygon(a), pathToMultiPolygon(b)));\n}\n\n/** Symmetric difference (XOR) of N paths. Commutative. */\nexport function pathExclude(...paths: GeomPath[]): GeomPolygonPath {\n if (paths.length === 0) return multiPolygonToPath([]);\n const mps = paths.map((p) => pathToMultiPolygon(p));\n const [head, ...rest] = mps;\n return multiPolygonToPath(polygonClipping.xor(head, ...rest));\n}\n\n/**\n * Fracture N paths along every intersection into the maximal set of\n * non-overlapping regions. Returns one polygon path per region.\n *\n * Algorithm: for every non-empty subset S ⊆ {A1..AN}, emit the region\n * `(∩ S) − (∪ complement)` — points covered by exactly the members of S\n * and no others. By construction the emitted regions are pairwise disjoint\n * and their union equals `pathUnion(A1..AN)`.\n *\n * For N=2 this collapses to the three Illustrator \"Divide\" outputs\n * (A−B, B−A, A∩B). For N=3 up to 7 regions are emitted. The 2^N − 1\n * subset count limits this to small N in practice; passing more than ~8\n * inputs is a misuse — the polygon-clipping kernel dominates anyway.\n */\nexport function pathDivide(...paths: GeomPath[]): GeomPolygonPath[] {\n if (paths.length === 0) return [];\n if (paths.length === 1) {\n return [multiPolygonToPath(pathToMultiPolygon(paths[0]))];\n }\n const mps = paths.map((p) => pathToMultiPolygon(p));\n const n = mps.length;\n const out: GeomPolygonPath[] = [];\n\n // Iterate every non-empty subset via bitmask. Emit subsets in size-\n // ascending order so the output is stable and reads \"exclusives first,\n // then pairs, then triples, …\" — matches the old N=2 ordering.\n const subsets: number[] = [];\n for (let mask = 1; mask < 1 << n; mask++) subsets.push(mask);\n subsets.sort((a, b) => popcount(a) - popcount(b) || a - b);\n\n for (const mask of subsets) {\n const inside: typeof mps = [];\n const outside: typeof mps = [];\n for (let i = 0; i < n; i++) {\n (mask & (1 << i) ? inside : outside).push(mps[i]);\n }\n const [insideHead, ...insideRest] = inside;\n let region = insideRest.length === 0\n ? insideHead\n : polygonClipping.intersection(insideHead, ...insideRest);\n if (region.length === 0) continue;\n if (outside.length > 0) {\n const [outHead, ...outRest] = outside;\n const outsideUnion = polygonClipping.union(outHead, ...outRest);\n region = polygonClipping.difference(region, outsideUnion);\n if (region.length === 0) continue;\n }\n out.push(multiPolygonToPath(region));\n }\n\n return out;\n}\n\nfunction popcount(n: number): number {\n let c = 0;\n while (n) { n &= n - 1; c++; }\n return c;\n}\n"]}
@@ -0,0 +1,106 @@
1
+ // src/commands.ts
2
+ var PATH_M = 0;
3
+ var PATH_L = 1;
4
+ var PATH_C = 2;
5
+ var PATH_Q = 3;
6
+ var PATH_Z = 4;
7
+ var PATH_CMD_LENGTHS = [2, 2, 6, 4, 0];
8
+ function forEachSegment(commands, coords, visit) {
9
+ let ci = 0;
10
+ let px = 0, py = 0;
11
+ for (let i = 0; i < commands.length; i++) {
12
+ const cmd = commands[i];
13
+ visit(cmd, ci, px, py);
14
+ const len = PATH_CMD_LENGTHS[cmd];
15
+ if (len > 0) {
16
+ px = coords[ci + len - 2];
17
+ py = coords[ci + len - 1];
18
+ ci += len;
19
+ }
20
+ }
21
+ }
22
+
23
+ // src/curve.ts
24
+ function cubicEvalAt(x0, y0, x1, y1, x2, y2, x3, y3, t) {
25
+ const u = 1 - t;
26
+ const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
27
+ return [
28
+ a * x0 + b * x1 + c * x2 + d * x3,
29
+ a * y0 + b * y1 + c * y2 + d * y3
30
+ ];
31
+ }
32
+ function elevateQuadraticToCubic(q0x, q0y, cx, cy, q1x, q1y) {
33
+ return [
34
+ q0x + 2 / 3 * (cx - q0x),
35
+ q0y + 2 / 3 * (cy - q0y),
36
+ q1x + 2 / 3 * (cx - q1x),
37
+ q1y + 2 / 3 * (cy - q1y)
38
+ ];
39
+ }
40
+ function distPointToLine(px, py, ax, ay, bx, by) {
41
+ const dx = bx - ax;
42
+ const dy = by - ay;
43
+ const len2 = dx * dx + dy * dy;
44
+ if (len2 === 0) {
45
+ const ex = px - ax;
46
+ const ey = py - ay;
47
+ return Math.sqrt(ex * ex + ey * ey);
48
+ }
49
+ const cross = (px - ax) * dy - (py - ay) * dx;
50
+ return Math.abs(cross) / Math.sqrt(len2);
51
+ }
52
+ function flattenCubic(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out) {
53
+ const d1 = distPointToLine(x1, y1, x0, y0, x3, y3);
54
+ const d2 = distPointToLine(x2, y2, x0, y0, x3, y3);
55
+ if (Math.max(d1, d2) <= tolerance) {
56
+ out.push(x3, y3);
57
+ return;
58
+ }
59
+ const x01 = (x0 + x1) * 0.5, y01 = (y0 + y1) * 0.5;
60
+ const x12 = (x1 + x2) * 0.5, y12 = (y1 + y2) * 0.5;
61
+ const x23 = (x2 + x3) * 0.5, y23 = (y2 + y3) * 0.5;
62
+ const x012 = (x01 + x12) * 0.5, y012 = (y01 + y12) * 0.5;
63
+ const x123 = (x12 + x23) * 0.5, y123 = (y12 + y23) * 0.5;
64
+ const x0123 = (x012 + x123) * 0.5, y0123 = (y012 + y123) * 0.5;
65
+ flattenCubic(x0, y0, x01, y01, x012, y012, x0123, y0123, tolerance, out);
66
+ flattenCubic(x0123, y0123, x123, y123, x23, y23, x3, y3, tolerance, out);
67
+ }
68
+ function componentExtremaTs(p0, p1, p2, p3) {
69
+ const a = -p0 + 3 * p1 - 3 * p2 + p3;
70
+ const b = 2 * (p0 - 2 * p1 + p2);
71
+ const c = -p0 + p1;
72
+ const ts = [];
73
+ const push = (t) => {
74
+ if (t > 0 && t < 1) ts.push(t);
75
+ };
76
+ if (Math.abs(a) < 1e-12) {
77
+ if (Math.abs(b) > 1e-12) push(-c / b);
78
+ } else {
79
+ const disc = b * b - 4 * a * c;
80
+ if (disc >= 0) {
81
+ const sq = Math.sqrt(disc);
82
+ push((-b + sq) / (2 * a));
83
+ push((-b - sq) / (2 * a));
84
+ }
85
+ }
86
+ return ts;
87
+ }
88
+ function cubicBounds(x0, y0, x1, y1, x2, y2, x3, y3) {
89
+ let minX = Math.min(x0, x3), maxX = Math.max(x0, x3);
90
+ let minY = Math.min(y0, y3), maxY = Math.max(y0, y3);
91
+ for (const t of componentExtremaTs(x0, x1, x2, x3)) {
92
+ const [ex] = cubicEvalAt(x0, y0, x1, y1, x2, y2, x3, y3, t);
93
+ if (ex < minX) minX = ex;
94
+ if (ex > maxX) maxX = ex;
95
+ }
96
+ for (const t of componentExtremaTs(y0, y1, y2, y3)) {
97
+ const [, ey] = cubicEvalAt(x0, y0, x1, y1, x2, y2, x3, y3, t);
98
+ if (ey < minY) minY = ey;
99
+ if (ey > maxY) maxY = ey;
100
+ }
101
+ return [minX, minY, maxX, maxY];
102
+ }
103
+
104
+ export { PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, cubicBounds, cubicEvalAt, elevateQuadraticToCubic, flattenCubic, forEachSegment };
105
+ //# sourceMappingURL=chunk-K5JJ6WRB.js.map
106
+ //# sourceMappingURL=chunk-K5JJ6WRB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands.ts","../src/curve.ts"],"names":[],"mappings":"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","file":"chunk-K5JJ6WRB.js","sourcesContent":["/**\n * SVG-style command-stream encoding — geom's canonical copy. `Path` in\n * @weasel-js/core wraps this with `kind` + `fillRule`. Codes lifted from\n * features/paths/types.ts; Spec 2 re-points that file to re-export these.\n */\nexport const PATH_M = 0; // moveTo\nexport const PATH_L = 1; // lineTo\nexport const PATH_C = 2; // cubic bezier\nexport const PATH_Q = 3; // quadratic bezier\nexport const PATH_Z = 4; // close subpath\n\n/** Float coords consumed by each command, indexed by command code. */\nexport const PATH_CMD_LENGTHS: readonly number[] = [2, 2, 6, 4, 0];\n\n/**\n * Visit each command with its coord offset and the pen position BEFORE the\n * command consumes its coords (the segment start). The callback receives\n * (cmd, coordIndex, penX, penY). The pen advances to the command's last\n * coord pair afterward (Z leaves the pen unchanged).\n */\nexport function forEachSegment(\n commands: ArrayLike<number>,\n coords: ArrayLike<number>,\n visit: (cmd: number, coordIndex: number, penX: number, penY: number) => void,\n): void {\n let ci = 0;\n let px = 0, py = 0;\n for (let i = 0; i < commands.length; i++) {\n const cmd = commands[i];\n visit(cmd, ci, px, py);\n const len = PATH_CMD_LENGTHS[cmd];\n if (len > 0) {\n px = coords[ci + len - 2];\n py = coords[ci + len - 1];\n ci += len;\n }\n }\n}\n","import type { Box } from './box';\n\n/** Cubic Bezier point at parameter t (de Casteljau / Bernstein form). */\nexport function cubicEvalAt(\n x0: number, y0: number, x1: number, y1: number,\n x2: number, y2: number, x3: number, y3: number, t: number,\n): [number, number] {\n const u = 1 - t;\n const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;\n return [\n a * x0 + b * x1 + c * x2 + d * x3,\n a * y0 + b * y1 + c * y2 + d * y3,\n ];\n}\n\n/**\n * Degree-elevate a quadratic (q0, ctrl, q1) to a cubic. Returns the two\n * cubic control points [c1x, c1y, c2x, c2y]; the cubic endpoints equal the\n * quadratic endpoints. c1 = q0 + 2/3(ctrl-q0), c2 = q1 + 2/3(ctrl-q1).\n */\nexport function elevateQuadraticToCubic(\n q0x: number, q0y: number, cx: number, cy: number, q1x: number, q1y: number,\n): [number, number, number, number] {\n return [\n q0x + (2 / 3) * (cx - q0x),\n q0y + (2 / 3) * (cy - q0y),\n q1x + (2 / 3) * (cx - q1x),\n q1y + (2 / 3) * (cy - q1y),\n ];\n}\n\n/** Distance from point P to line AB (perpendicular). Ported verbatim from\n * features/paths/flatten.ts (helper for flattenCubic's flatness predicate). */\nfunction distPointToLine(px: number, py: number, ax: number, ay: number, bx: number, by: number): number {\n const dx = bx - ax;\n const dy = by - ay;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) {\n const ex = px - ax;\n const ey = py - ay;\n return Math.sqrt(ex * ex + ey * ey);\n }\n const cross = (px - ax) * dy - (py - ay) * dx;\n return Math.abs(cross) / Math.sqrt(len2);\n}\n\n/**\n * Adaptive flatten of a cubic into interleaved points appended to `out`\n * (excludes the start point, includes the endpoint). Ported verbatim from\n * features/paths/flatten.ts:37.\n */\nexport function flattenCubic(\n x0: number, y0: number, x1: number, y1: number,\n x2: number, y2: number, x3: number, y3: number,\n tolerance: number, out: number[],\n): void {\n const d1 = distPointToLine(x1, y1, x0, y0, x3, y3);\n const d2 = distPointToLine(x2, y2, x0, y0, x3, y3);\n if (Math.max(d1, d2) <= tolerance) {\n out.push(x3, y3);\n return;\n }\n // De Casteljau split at t=0.5\n const x01 = (x0 + x1) * 0.5, y01 = (y0 + y1) * 0.5;\n const x12 = (x1 + x2) * 0.5, y12 = (y1 + y2) * 0.5;\n const x23 = (x2 + x3) * 0.5, y23 = (y2 + y3) * 0.5;\n const x012 = (x01 + x12) * 0.5, y012 = (y01 + y12) * 0.5;\n const x123 = (x12 + x23) * 0.5, y123 = (y12 + y23) * 0.5;\n const x0123 = (x012 + x123) * 0.5, y0123 = (y012 + y123) * 0.5;\n flattenCubic(x0, y0, x01, y01, x012, y012, x0123, y0123, tolerance, out);\n flattenCubic(x0123, y0123, x123, y123, x23, y23, x3, y3, tolerance, out);\n}\n\n/** Axis-aligned extrema parameters of one cubic component (the 0,1 ends plus\n * any derivative roots in (0,1)). Used by cubicBounds. */\nfunction componentExtremaTs(p0: number, p1: number, p2: number, p3: number): number[] {\n // B'(t)=0 → quadratic a t² + b t + c = 0 with:\n const a = -p0 + 3 * p1 - 3 * p2 + p3;\n const b = 2 * (p0 - 2 * p1 + p2);\n const c = -p0 + p1;\n const ts: number[] = [];\n const push = (t: number) => { if (t > 0 && t < 1) ts.push(t); };\n if (Math.abs(a) < 1e-12) {\n if (Math.abs(b) > 1e-12) push(-c / b);\n } else {\n const disc = b * b - 4 * a * c;\n if (disc >= 0) {\n const sq = Math.sqrt(disc);\n push((-b + sq) / (2 * a));\n push((-b - sq) / (2 * a));\n }\n }\n return ts;\n}\n\n/** Tight AABB of a cubic, evaluating only extrema that lie on the curve. */\nexport function cubicBounds(\n x0: number, y0: number, x1: number, y1: number,\n x2: number, y2: number, x3: number, y3: number,\n): Box {\n let minX = Math.min(x0, x3), maxX = Math.max(x0, x3);\n let minY = Math.min(y0, y3), maxY = Math.max(y0, y3);\n for (const t of componentExtremaTs(x0, x1, x2, x3)) {\n const [ex] = cubicEvalAt(x0, y0, x1, y1, x2, y2, x3, y3, t);\n if (ex < minX) minX = ex;\n if (ex > maxX) maxX = ex;\n }\n for (const t of componentExtremaTs(y0, y1, y2, y3)) {\n const [, ey] = cubicEvalAt(x0, y0, x1, y1, x2, y2, x3, y3, t);\n if (ey < minY) minY = ey;\n if (ey > maxY) maxY = ey;\n }\n return [minX, minY, maxX, maxY];\n}\n"]}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Scalar / 2-vector primitives and the kernel-wide epsilon policy.
3
+ *
4
+ * Points are scalar pairs (px, py); there is no point struct. All math is
5
+ * f64 (JS-native). Epsilons are f32-SCALE and magnitude-relative, because
6
+ * stored coords are quantized to Float32 (~7 significant digits) — see the
7
+ * geometry-kernel spec.
8
+ */
9
+ /** Base relative epsilon, sized for Float32 storage (~1 part in 1e-6). */
10
+ declare const EPS = 0.000001;
11
+ /** 2D cross (wedge) product of vectors (ax,ay) and (bx,by). */
12
+ declare function cross(ax: number, ay: number, bx: number, by: number): number;
13
+ /** 2D dot product. */
14
+ declare function dot(ax: number, ay: number, bx: number, by: number): number;
15
+ /** Component difference (ax-bx, ay-by) as a tuple. Cold-path use only. */
16
+ declare function sub(ax: number, ay: number, bx: number, by: number): [number, number];
17
+ /** Squared length of (x,y). Avoids the sqrt; compare against squared thresholds. */
18
+ declare function len2(x: number, y: number): number;
19
+ /** Three-valued sign. */
20
+ declare function sign(n: number): -1 | 0 | 1;
21
+ /**
22
+ * Magnitude-scaled approximate equality. Two values are equal when their
23
+ * absolute difference is within EPS scaled by the larger magnitude. This is
24
+ * the ONLY equality the kernel uses on computed coordinates — never `===`,
25
+ * never an f64-tight literal.
26
+ */
27
+ declare function approxEq(a: number, b: number, eps?: number): boolean;
28
+
29
+ /**
30
+ * 2D affine transforms in canvas/DOMMatrix order: [a, b, c, d, e, f].
31
+ * x' = a·x + c·y + e
32
+ * y' = b·x + d·y + f
33
+ * Represented as a 6-element number[] (f64). The affine tier of the kernel.
34
+ *
35
+ * Convention alignment: the renderer already has a `Mat3` in
36
+ * `src/renderer/math/mat3.ts`. That one is a 9-element column-major
37
+ * `Float32Array` (a full 3×3) shaped for `uniformMatrix3fv` — a deliberately
38
+ * different *representation* for the WebGL upload path. Its *logical element
39
+ * order* is identical to ours: `create(a, b, c, d, tx, ty)` maps
40
+ * `x' = a·x + c·y + tx`, `y' = b·x + d·y + ty` (canvas/DOMMatrix a,b,c,d,e,f).
41
+ * We keep the pure 6-tuple f64 form here (the kernel form); the 9-element f32
42
+ * form stays a render-layer concern. No second logical convention is created.
43
+ */
44
+ type Mat3 = number[];
45
+ declare function identity(): Mat3;
46
+ declare function translate(tx: number, ty: number): Mat3;
47
+ declare function scale(sx: number, sy: number): Mat3;
48
+ declare function rotate(rad: number): Mat3;
49
+ /** Compose: result applies `n` first, then `m` (m·n). */
50
+ declare function multiply(m: Mat3, n: Mat3): Mat3;
51
+ /** Inverse, or null when the matrix is singular (|det| below the epsilon). */
52
+ declare function invert(m: Mat3): Mat3 | null;
53
+ /** Apply to a point, returning a tuple. Cold-path use; hot loops inline. */
54
+ declare function applyToPoint(m: Mat3, x: number, y: number): [number, number];
55
+ /** Affine that maps source box (sx,sy,sw,sh) onto destination box (dx,dy,dw,dh). */
56
+ declare function boxToBox(sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): Mat3;
57
+ /** Rotation by `rad` about pivot (cx,cy): translate(c)·rotate·translate(-c). */
58
+ declare function rotateAboutPoint(cx: number, cy: number, rad: number): Mat3;
59
+
60
+ /** Axis-aligned box as [minX, minY, maxX, maxY]. */
61
+ type Box = [number, number, number, number];
62
+ /** Tight bounds of an interleaved coord stream, or null if empty. */
63
+ declare function boundsOfCoords(coords: ArrayLike<number>): Box | null;
64
+ /** Smallest box containing both inputs. */
65
+ declare function unionBox(a: Box, b: Box): Box;
66
+ /** Inclusive point-in-box test. */
67
+ declare function boxContainsPoint(b: Box, x: number, y: number): boolean;
68
+ /** Closed interleaved ring (first vertex repeated) for a rect at (x,y,w,h). */
69
+ declare function rectToContour(x: number, y: number, w: number, h: number): Float64Array;
70
+
71
+ /**
72
+ * SVG-style command-stream encoding — geom's canonical copy. `Path` in
73
+ * @weasel-js/core wraps this with `kind` + `fillRule`. Codes lifted from
74
+ * features/paths/types.ts; Spec 2 re-points that file to re-export these.
75
+ */
76
+ declare const PATH_M = 0;
77
+ declare const PATH_L = 1;
78
+ declare const PATH_C = 2;
79
+ declare const PATH_Q = 3;
80
+ declare const PATH_Z = 4;
81
+ /** Float coords consumed by each command, indexed by command code. */
82
+ declare const PATH_CMD_LENGTHS: readonly number[];
83
+ /**
84
+ * Visit each command with its coord offset and the pen position BEFORE the
85
+ * command consumes its coords (the segment start). The callback receives
86
+ * (cmd, coordIndex, penX, penY). The pen advances to the command's last
87
+ * coord pair afterward (Z leaves the pen unchanged).
88
+ */
89
+ declare function forEachSegment(commands: ArrayLike<number>, coords: ArrayLike<number>, visit: (cmd: number, coordIndex: number, penX: number, penY: number) => void): void;
90
+
91
+ /** Cubic Bezier point at parameter t (de Casteljau / Bernstein form). */
92
+ declare function cubicEvalAt(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, t: number): [number, number];
93
+ /**
94
+ * Degree-elevate a quadratic (q0, ctrl, q1) to a cubic. Returns the two
95
+ * cubic control points [c1x, c1y, c2x, c2y]; the cubic endpoints equal the
96
+ * quadratic endpoints. c1 = q0 + 2/3(ctrl-q0), c2 = q1 + 2/3(ctrl-q1).
97
+ */
98
+ declare function elevateQuadraticToCubic(q0x: number, q0y: number, cx: number, cy: number, q1x: number, q1y: number): [number, number, number, number];
99
+ /**
100
+ * Adaptive flatten of a cubic into interleaved points appended to `out`
101
+ * (excludes the start point, includes the endpoint). Ported verbatim from
102
+ * features/paths/flatten.ts:37.
103
+ */
104
+ declare function flattenCubic(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, tolerance: number, out: number[]): void;
105
+ /** Tight AABB of a cubic, evaluating only extrema that lie on the curve. */
106
+ declare function cubicBounds(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): Box;
107
+
108
+ /**
109
+ * Even-odd ray-cast point-in-polygon over an interleaved, unclosed contour
110
+ * [x0,y0,x1,y1,…]. The closing edge (last→first) is implicit. Flat rewrite of
111
+ * features/paths/polygonHitTestRect.ts pointInPolygon — same algorithm.
112
+ */
113
+ declare function pointInPolygon(coords: ArrayLike<number>, px: number, py: number): boolean;
114
+ /** True if segment (ax,ay)-(bx,by) properly crosses (cx,cy)-(dx,dy). Flat
115
+ * rewrite of polygonHitTestRect.ts segmentsCross. */
116
+ declare function segmentsCross(ax: number, ay: number, bx: number, by: number, cx: number, cy: number, dx: number, dy: number): boolean;
117
+ /** Squared distance from (px,py) to segment (ax,ay)-(bx,by), endpoint-clamped. */
118
+ declare function pointSegmentDist2(px: number, py: number, ax: number, ay: number, bx: number, by: number): number;
119
+
120
+ /**
121
+ * Apply an affine to an interleaved coord stream, returning a fresh f64
122
+ * buffer. Command codes are unaffected — for a Bezier the transformed control
123
+ * points define the transformed curve exactly (affine invariance), so callers
124
+ * pass `path.coords` straight through and keep `path.commands` as-is.
125
+ */
126
+ declare function transformCoords(coords: ArrayLike<number>, m: Mat3): Float64Array;
127
+
128
+ export { type Box, EPS, type Mat3, PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, applyToPoint, approxEq, boundsOfCoords, boxContainsPoint, boxToBox, cross, cubicBounds, cubicEvalAt, dot, elevateQuadraticToCubic, flattenCubic, forEachSegment, identity, invert, len2, multiply, pointInPolygon, pointSegmentDist2, rectToContour, rotate, rotateAboutPoint, scale, segmentsCross, sign, sub, transformCoords, translate, unionBox };
package/dist/index.js ADDED
@@ -0,0 +1,146 @@
1
+ export { PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, cubicBounds, cubicEvalAt, elevateQuadraticToCubic, flattenCubic, forEachSegment } from './chunk-K5JJ6WRB.js';
2
+
3
+ // src/scalar.ts
4
+ var EPS = 1e-6;
5
+ function cross(ax, ay, bx, by) {
6
+ return ax * by - ay * bx;
7
+ }
8
+ function dot(ax, ay, bx, by) {
9
+ return ax * bx + ay * by;
10
+ }
11
+ function sub(ax, ay, bx, by) {
12
+ return [ax - bx, ay - by];
13
+ }
14
+ function len2(x, y) {
15
+ return x * x + y * y;
16
+ }
17
+ function sign(n) {
18
+ return n > 0 ? 1 : n < 0 ? -1 : 0;
19
+ }
20
+ function approxEq(a, b, eps = EPS) {
21
+ const diff = Math.abs(a - b);
22
+ if (diff === 0) return true;
23
+ const scale2 = Math.max(1, Math.abs(a), Math.abs(b));
24
+ return diff <= eps * scale2;
25
+ }
26
+
27
+ // src/mat3.ts
28
+ function identity() {
29
+ return [1, 0, 0, 1, 0, 0];
30
+ }
31
+ function translate(tx, ty) {
32
+ return [1, 0, 0, 1, tx, ty];
33
+ }
34
+ function scale(sx, sy) {
35
+ return [sx, 0, 0, sy, 0, 0];
36
+ }
37
+ function rotate(rad) {
38
+ const c = Math.cos(rad);
39
+ const s = Math.sin(rad);
40
+ return [c, s, -s, c, 0, 0];
41
+ }
42
+ function multiply(m, n) {
43
+ return [
44
+ m[0] * n[0] + m[2] * n[1],
45
+ m[1] * n[0] + m[3] * n[1],
46
+ m[0] * n[2] + m[2] * n[3],
47
+ m[1] * n[2] + m[3] * n[3],
48
+ m[0] * n[4] + m[2] * n[5] + m[4],
49
+ m[1] * n[4] + m[3] * n[5] + m[5]
50
+ ];
51
+ }
52
+ function invert(m) {
53
+ const det = m[0] * m[3] - m[1] * m[2];
54
+ if (Math.abs(det) < 1e-12) return null;
55
+ const id = 1 / det;
56
+ const a = m[3] * id;
57
+ const b = -m[1] * id;
58
+ const c = -m[2] * id;
59
+ const d = m[0] * id;
60
+ return [a, b, c, d, -(m[4] * a + m[5] * c), -(m[4] * b + m[5] * d)];
61
+ }
62
+ function applyToPoint(m, x, y) {
63
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
64
+ }
65
+ function boxToBox(sx, sy, sw, sh, dx, dy, dw, dh) {
66
+ const kx = sw === 0 ? 1 : dw / sw;
67
+ const ky = sh === 0 ? 1 : dh / sh;
68
+ return [kx, 0, 0, ky, dx - sx * kx, dy - sy * ky];
69
+ }
70
+ function rotateAboutPoint(cx, cy, rad) {
71
+ return multiply(translate(cx, cy), multiply(rotate(rad), translate(-cx, -cy)));
72
+ }
73
+
74
+ // src/box.ts
75
+ function boundsOfCoords(coords) {
76
+ if (coords.length < 2) return null;
77
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
78
+ for (let i = 0; i + 1 < coords.length; i += 2) {
79
+ const x = coords[i], y = coords[i + 1];
80
+ if (x < minX) minX = x;
81
+ if (y < minY) minY = y;
82
+ if (x > maxX) maxX = x;
83
+ if (y > maxY) maxY = y;
84
+ }
85
+ return [minX, minY, maxX, maxY];
86
+ }
87
+ function unionBox(a, b) {
88
+ return [
89
+ Math.min(a[0], b[0]),
90
+ Math.min(a[1], b[1]),
91
+ Math.max(a[2], b[2]),
92
+ Math.max(a[3], b[3])
93
+ ];
94
+ }
95
+ function boxContainsPoint(b, x, y) {
96
+ return x >= b[0] && x <= b[2] && y >= b[1] && y <= b[3];
97
+ }
98
+ function rectToContour(x, y, w, h) {
99
+ return Float64Array.of(x, y, x + w, y, x + w, y + h, x, y + h, x, y);
100
+ }
101
+
102
+ // src/polyline.ts
103
+ function pointInPolygon(coords, px, py) {
104
+ const n = coords.length >> 1;
105
+ if (n < 3) return false;
106
+ let inside = false;
107
+ for (let i = 0, j = n - 1; i < n; j = i++) {
108
+ const xi = coords[i * 2], yi = coords[i * 2 + 1];
109
+ const xj = coords[j * 2], yj = coords[j * 2 + 1];
110
+ const crosses = yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi;
111
+ if (crosses) inside = !inside;
112
+ }
113
+ return inside;
114
+ }
115
+ function segmentsCross(ax, ay, bx, by, cx, cy, dx, dy) {
116
+ const d1 = sign((dx - cx) * (ay - cy) - (dy - cy) * (ax - cx));
117
+ const d2 = sign((dx - cx) * (by - cy) - (dy - cy) * (bx - cx));
118
+ const d3 = sign((bx - ax) * (cy - ay) - (by - ay) * (cx - ax));
119
+ const d4 = sign((bx - ax) * (dy - ay) - (by - ay) * (dx - ax));
120
+ return d1 !== d2 && d3 !== d4;
121
+ }
122
+ function pointSegmentDist2(px, py, ax, ay, bx, by) {
123
+ const vx = bx - ax, vy = by - ay;
124
+ const wx = px - ax, wy = py - ay;
125
+ const vv = len2(vx, vy);
126
+ let t = vv === 0 ? 0 : dot(wx, wy, vx, vy) / vv;
127
+ t = t < 0 ? 0 : t > 1 ? 1 : t;
128
+ const dx = px - (ax + t * vx), dy = py - (ay + t * vy);
129
+ return len2(dx, dy);
130
+ }
131
+
132
+ // src/affine.ts
133
+ function transformCoords(coords, m) {
134
+ const out = new Float64Array(coords.length);
135
+ const a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
136
+ for (let i = 0; i + 1 < coords.length; i += 2) {
137
+ const x = coords[i], y = coords[i + 1];
138
+ out[i] = a * x + c * y + e;
139
+ out[i + 1] = b * x + d * y + f;
140
+ }
141
+ return out;
142
+ }
143
+
144
+ export { EPS, applyToPoint, approxEq, boundsOfCoords, boxContainsPoint, boxToBox, cross, dot, identity, invert, len2, multiply, pointInPolygon, pointSegmentDist2, rectToContour, rotate, rotateAboutPoint, scale, segmentsCross, sign, sub, transformCoords, translate, unionBox };
145
+ //# sourceMappingURL=index.js.map
146
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scalar.ts","../src/mat3.ts","../src/box.ts","../src/polyline.ts","../src/affine.ts"],"names":["scale"],"mappings":";;;AAUO,IAAM,GAAA,GAAM;AAGZ,SAAS,KAAA,CAAM,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAAoB;AAC5E,EAAA,OAAO,EAAA,GAAK,KAAK,EAAA,GAAK,EAAA;AACxB;AAGO,SAAS,GAAA,CAAI,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAAoB;AAC1E,EAAA,OAAO,EAAA,GAAK,KAAK,EAAA,GAAK,EAAA;AACxB;AAGO,SAAS,GAAA,CAAI,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAA8B;AACpF,EAAA,OAAO,CAAC,EAAA,GAAK,EAAA,EAAI,EAAA,GAAK,EAAE,CAAA;AAC1B;AAGO,SAAS,IAAA,CAAK,GAAW,CAAA,EAAmB;AACjD,EAAA,OAAO,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AACrB;AAGO,SAAS,KAAK,CAAA,EAAuB;AAC1C,EAAA,OAAO,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,IAAI,EAAA,GAAK,CAAA;AAClC;AAQO,SAAS,QAAA,CAAS,CAAA,EAAW,CAAA,EAAW,GAAA,GAAc,GAAA,EAAc;AACzE,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,CAAC,CAAA;AAC3B,EAAA,IAAI,IAAA,KAAS,GAAG,OAAO,IAAA;AACvB,EAAA,MAAMA,MAAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAClD,EAAA,OAAO,QAAQ,GAAA,GAAMA,MAAAA;AACvB;;;AC/BO,SAAS,QAAA,GAAiB;AAC/B,EAAA,OAAO,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAAA;AAC1B;AAEO,SAAS,SAAA,CAAU,IAAY,EAAA,EAAkB;AACtD,EAAA,OAAO,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,IAAI,EAAE,CAAA;AAC5B;AAEO,SAAS,KAAA,CAAM,IAAY,EAAA,EAAkB;AAClD,EAAA,OAAO,CAAC,EAAA,EAAI,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,GAAG,CAAC,CAAA;AAC5B;AAEO,SAAS,OAAO,GAAA,EAAmB;AACxC,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AACtB,EAAA,MAAM,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AACtB,EAAA,OAAO,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAAA;AAC3B;AAGO,SAAS,QAAA,CAAS,GAAS,CAAA,EAAe;AAC/C,EAAA,OAAO;AAAA,IACL,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,IAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AAAA,IACxB,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,IAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AAAA,IACxB,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,IAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AAAA,IACxB,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,IAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AAAA,IACxB,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,EAAE,CAAC,CAAA;AAAA,IAC/B,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,EAAE,CAAC;AAAA,GACjC;AACF;AAGO,SAAS,OAAO,CAAA,EAAsB;AAC3C,EAAA,MAAM,GAAA,GAAM,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AACpC,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,GAAI,OAAO,OAAO,IAAA;AAClC,EAAA,MAAM,KAAK,CAAA,GAAI,GAAA;AACf,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA;AACjB,EAAA,MAAM,CAAA,GAAI,CAAC,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA;AAClB,EAAA,MAAM,CAAA,GAAI,CAAC,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA;AAClB,EAAA,MAAM,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,EAAA;AACjB,EAAA,OAAO,CAAC,GAAG,CAAA,EAAG,CAAA,EAAG,GAAG,EAAE,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAE,CAAC,IAAI,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAA;AACpE;AAGO,SAAS,YAAA,CAAa,CAAA,EAAS,CAAA,EAAW,CAAA,EAA6B;AAC5E,EAAA,OAAO,CAAC,EAAE,CAAC,CAAA,GAAI,IAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA,CAAE,CAAC,GAAG,CAAA,CAAE,CAAC,IAAI,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA,CAAE,CAAC,CAAC,CAAA;AAChE;AAGO,SAAS,QAAA,CACd,IAAY,EAAA,EAAY,EAAA,EAAY,IACpC,EAAA,EAAY,EAAA,EAAY,IAAY,EAAA,EAC9B;AACN,EAAA,MAAM,EAAA,GAAK,EAAA,KAAO,CAAA,GAAI,CAAA,GAAI,EAAA,GAAK,EAAA;AAC/B,EAAA,MAAM,EAAA,GAAK,EAAA,KAAO,CAAA,GAAI,CAAA,GAAI,EAAA,GAAK,EAAA;AAE/B,EAAA,OAAO,CAAC,EAAA,EAAI,CAAA,EAAG,CAAA,EAAG,EAAA,EAAI,KAAK,EAAA,GAAK,EAAA,EAAI,EAAA,GAAK,EAAA,GAAK,EAAE,CAAA;AAClD;AAGO,SAAS,gBAAA,CAAiB,EAAA,EAAY,EAAA,EAAY,GAAA,EAAmB;AAC1E,EAAA,OAAO,QAAA,CAAS,SAAA,CAAU,EAAA,EAAI,EAAE,GAAG,QAAA,CAAS,MAAA,CAAO,GAAG,CAAA,EAAG,UAAU,CAAC,EAAA,EAAI,CAAC,EAAE,CAAC,CAAC,CAAA;AAC/E;;;AC1EO,SAAS,eAAe,MAAA,EAAuC;AACpE,EAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAC9B,EAAA,IAAI,OAAO,QAAA,EAAU,IAAA,GAAO,QAAA,EAAU,IAAA,GAAO,WAAW,IAAA,GAAO,CAAA,QAAA;AAC/D,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAI,MAAA,CAAO,MAAA,EAAQ,KAAK,CAAA,EAAG;AAC7C,IAAA,MAAM,IAAI,MAAA,CAAO,CAAC,GAAG,CAAA,GAAI,MAAA,CAAO,IAAI,CAAC,CAAA;AACrC,IAAA,IAAI,CAAA,GAAI,MAAM,IAAA,GAAO,CAAA;AACrB,IAAA,IAAI,CAAA,GAAI,MAAM,IAAA,GAAO,CAAA;AACrB,IAAA,IAAI,CAAA,GAAI,MAAM,IAAA,GAAO,CAAA;AACrB,IAAA,IAAI,CAAA,GAAI,MAAM,IAAA,GAAO,CAAA;AAAA,EACvB;AACA,EAAA,OAAO,CAAC,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAChC;AAGO,SAAS,QAAA,CAAS,GAAQ,CAAA,EAAa;AAC5C,EAAA,OAAO;AAAA,IACL,KAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAA;AAAA,IACnB,KAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAA;AAAA,IACnB,KAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC,CAAA;AAAA,IACnB,KAAK,GAAA,CAAI,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,CAAE,CAAC,CAAC;AAAA,GACrB;AACF;AAGO,SAAS,gBAAA,CAAiB,CAAA,EAAQ,CAAA,EAAW,CAAA,EAAoB;AACtE,EAAA,OAAO,CAAA,IAAK,CAAA,CAAE,CAAC,CAAA,IAAK,KAAK,CAAA,CAAE,CAAC,CAAA,IAAK,CAAA,IAAK,CAAA,CAAE,CAAC,CAAA,IAAK,CAAA,IAAK,EAAE,CAAC,CAAA;AACxD;AAGO,SAAS,aAAA,CAAc,CAAA,EAAW,CAAA,EAAW,CAAA,EAAW,CAAA,EAAyB;AACtF,EAAA,OAAO,YAAA,CAAa,EAAA,CAAG,CAAA,EAAG,CAAA,EAAG,IAAI,CAAA,EAAG,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,IAAI,CAAA,EAAG,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,GAAG,CAAC,CAAA;AACrE;;;AC5BO,SAAS,cAAA,CAAe,MAAA,EAA2B,EAAA,EAAY,EAAA,EAAqB;AACzF,EAAA,MAAM,CAAA,GAAI,OAAO,MAAA,IAAU,CAAA;AAC3B,EAAA,IAAI,CAAA,GAAI,GAAG,OAAO,KAAA;AAClB,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,CAAA,GAAI,GAAG,CAAA,GAAI,CAAA,EAAG,IAAI,CAAA,EAAA,EAAK;AACzC,IAAA,MAAM,EAAA,GAAK,OAAO,CAAA,GAAI,CAAC,GAAG,EAAA,GAAK,MAAA,CAAO,CAAA,GAAI,CAAA,GAAI,CAAC,CAAA;AAC/C,IAAA,MAAM,EAAA,GAAK,OAAO,CAAA,GAAI,CAAC,GAAG,EAAA,GAAK,MAAA,CAAO,CAAA,GAAI,CAAA,GAAI,CAAC,CAAA;AAC/C,IAAA,MAAM,OAAA,GACH,EAAA,GAAK,EAAA,KAAS,EAAA,GAAK,EAAA,IACpB,EAAA,GAAA,CAAO,EAAA,GAAK,EAAA,KAAO,EAAA,GAAK,EAAA,CAAA,IAAQ,EAAA,GAAK,EAAA,CAAA,GAAM,EAAA;AAC7C,IAAA,IAAI,OAAA,WAAkB,CAAC,MAAA;AAAA,EACzB;AACA,EAAA,OAAO,MAAA;AACT;AAIO,SAAS,aAAA,CACd,IAAY,EAAA,EAAY,EAAA,EAAY,IACpC,EAAA,EAAY,EAAA,EAAY,IAAY,EAAA,EAC3B;AACT,EAAA,MAAM,EAAA,GAAK,MAAM,EAAA,GAAK,EAAA,KAAO,KAAK,EAAA,CAAA,GAAA,CAAO,EAAA,GAAK,EAAA,KAAO,EAAA,GAAK,EAAA,CAAG,CAAA;AAC7D,EAAA,MAAM,EAAA,GAAK,MAAM,EAAA,GAAK,EAAA,KAAO,KAAK,EAAA,CAAA,GAAA,CAAO,EAAA,GAAK,EAAA,KAAO,EAAA,GAAK,EAAA,CAAG,CAAA;AAC7D,EAAA,MAAM,EAAA,GAAK,MAAM,EAAA,GAAK,EAAA,KAAO,KAAK,EAAA,CAAA,GAAA,CAAO,EAAA,GAAK,EAAA,KAAO,EAAA,GAAK,EAAA,CAAG,CAAA;AAC7D,EAAA,MAAM,EAAA,GAAK,MAAM,EAAA,GAAK,EAAA,KAAO,KAAK,EAAA,CAAA,GAAA,CAAO,EAAA,GAAK,EAAA,KAAO,EAAA,GAAK,EAAA,CAAG,CAAA;AAC7D,EAAA,OAAO,EAAA,KAAO,MAAM,EAAA,KAAO,EAAA;AAC7B;AAGO,SAAS,kBACd,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,IAAY,EAAA,EACpD;AACR,EAAA,MAAM,EAAA,GAAK,EAAA,GAAK,EAAA,EAAI,EAAA,GAAK,EAAA,GAAK,EAAA;AAC9B,EAAA,MAAM,EAAA,GAAK,EAAA,GAAK,EAAA,EAAI,EAAA,GAAK,EAAA,GAAK,EAAA;AAC9B,EAAA,MAAM,EAAA,GAAK,IAAA,CAAK,EAAA,EAAI,EAAE,CAAA;AACtB,EAAA,IAAI,CAAA,GAAI,OAAO,CAAA,GAAI,CAAA,GAAI,IAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,GAAI,EAAA;AAC7C,EAAA,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AAC5B,EAAA,MAAM,EAAA,GAAK,MAAM,EAAA,GAAK,CAAA,GAAI,KAAK,EAAA,GAAK,EAAA,IAAM,KAAK,CAAA,GAAI,EAAA,CAAA;AACnD,EAAA,OAAO,IAAA,CAAK,IAAI,EAAE,CAAA;AACpB;;;ACtCO,SAAS,eAAA,CAAgB,QAA2B,CAAA,EAAuB;AAChF,EAAA,MAAM,GAAA,GAAM,IAAI,YAAA,CAAa,MAAA,CAAO,MAAM,CAAA;AAC1C,EAAA,MAAM,CAAA,GAAI,EAAE,CAAC,CAAA,EAAG,IAAI,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,CAAC,GAAG,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA,EAAG,CAAA,GAAI,EAAE,CAAC,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,CAAC,CAAA;AAC/D,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAI,MAAA,CAAO,MAAA,EAAQ,KAAK,CAAA,EAAG;AAC7C,IAAA,MAAM,IAAI,MAAA,CAAO,CAAC,GAAG,CAAA,GAAI,MAAA,CAAO,IAAI,CAAC,CAAA;AACrC,IAAA,GAAA,CAAI,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AACzB,IAAA,GAAA,CAAI,IAAI,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AAAA,EAC/B;AACA,EAAA,OAAO,GAAA;AACT","file":"index.js","sourcesContent":["/**\n * Scalar / 2-vector primitives and the kernel-wide epsilon policy.\n *\n * Points are scalar pairs (px, py); there is no point struct. All math is\n * f64 (JS-native). Epsilons are f32-SCALE and magnitude-relative, because\n * stored coords are quantized to Float32 (~7 significant digits) — see the\n * geometry-kernel spec.\n */\n\n/** Base relative epsilon, sized for Float32 storage (~1 part in 1e-6). */\nexport const EPS = 1e-6;\n\n/** 2D cross (wedge) product of vectors (ax,ay) and (bx,by). */\nexport function cross(ax: number, ay: number, bx: number, by: number): number {\n return ax * by - ay * bx;\n}\n\n/** 2D dot product. */\nexport function dot(ax: number, ay: number, bx: number, by: number): number {\n return ax * bx + ay * by;\n}\n\n/** Component difference (ax-bx, ay-by) as a tuple. Cold-path use only. */\nexport function sub(ax: number, ay: number, bx: number, by: number): [number, number] {\n return [ax - bx, ay - by];\n}\n\n/** Squared length of (x,y). Avoids the sqrt; compare against squared thresholds. */\nexport function len2(x: number, y: number): number {\n return x * x + y * y;\n}\n\n/** Three-valued sign. */\nexport function sign(n: number): -1 | 0 | 1 {\n return n > 0 ? 1 : n < 0 ? -1 : 0;\n}\n\n/**\n * Magnitude-scaled approximate equality. Two values are equal when their\n * absolute difference is within EPS scaled by the larger magnitude. This is\n * the ONLY equality the kernel uses on computed coordinates — never `===`,\n * never an f64-tight literal.\n */\nexport function approxEq(a: number, b: number, eps: number = EPS): boolean {\n const diff = Math.abs(a - b);\n if (diff === 0) return true;\n const scale = Math.max(1, Math.abs(a), Math.abs(b));\n return diff <= eps * scale;\n}\n","/**\n * 2D affine transforms in canvas/DOMMatrix order: [a, b, c, d, e, f].\n * x' = a·x + c·y + e\n * y' = b·x + d·y + f\n * Represented as a 6-element number[] (f64). The affine tier of the kernel.\n *\n * Convention alignment: the renderer already has a `Mat3` in\n * `src/renderer/math/mat3.ts`. That one is a 9-element column-major\n * `Float32Array` (a full 3×3) shaped for `uniformMatrix3fv` — a deliberately\n * different *representation* for the WebGL upload path. Its *logical element\n * order* is identical to ours: `create(a, b, c, d, tx, ty)` maps\n * `x' = a·x + c·y + tx`, `y' = b·x + d·y + ty` (canvas/DOMMatrix a,b,c,d,e,f).\n * We keep the pure 6-tuple f64 form here (the kernel form); the 9-element f32\n * form stays a render-layer concern. No second logical convention is created.\n */\nexport type Mat3 = number[];\n\nexport function identity(): Mat3 {\n return [1, 0, 0, 1, 0, 0];\n}\n\nexport function translate(tx: number, ty: number): Mat3 {\n return [1, 0, 0, 1, tx, ty];\n}\n\nexport function scale(sx: number, sy: number): Mat3 {\n return [sx, 0, 0, sy, 0, 0];\n}\n\nexport function rotate(rad: number): Mat3 {\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n return [c, s, -s, c, 0, 0];\n}\n\n/** Compose: result applies `n` first, then `m` (m·n). */\nexport function multiply(m: Mat3, n: Mat3): Mat3 {\n return [\n m[0] * n[0] + m[2] * n[1],\n m[1] * n[0] + m[3] * n[1],\n m[0] * n[2] + m[2] * n[3],\n m[1] * n[2] + m[3] * n[3],\n m[0] * n[4] + m[2] * n[5] + m[4],\n m[1] * n[4] + m[3] * n[5] + m[5],\n ];\n}\n\n/** Inverse, or null when the matrix is singular (|det| below the epsilon). */\nexport function invert(m: Mat3): Mat3 | null {\n const det = m[0] * m[3] - m[1] * m[2];\n if (Math.abs(det) < 1e-12) return null;\n const id = 1 / det;\n const a = m[3] * id;\n const b = -m[1] * id;\n const c = -m[2] * id;\n const d = m[0] * id;\n return [a, b, c, d, -(m[4] * a + m[5] * c), -(m[4] * b + m[5] * d)];\n}\n\n/** Apply to a point, returning a tuple. Cold-path use; hot loops inline. */\nexport function applyToPoint(m: Mat3, x: number, y: number): [number, number] {\n return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];\n}\n\n/** Affine that maps source box (sx,sy,sw,sh) onto destination box (dx,dy,dw,dh). */\nexport function boxToBox(\n sx: number, sy: number, sw: number, sh: number,\n dx: number, dy: number, dw: number, dh: number,\n): Mat3 {\n const kx = sw === 0 ? 1 : dw / sw;\n const ky = sh === 0 ? 1 : dh / sh;\n // translate(dx,dy) · scale(kx,ky) · translate(-sx,-sy)\n return [kx, 0, 0, ky, dx - sx * kx, dy - sy * ky];\n}\n\n/** Rotation by `rad` about pivot (cx,cy): translate(c)·rotate·translate(-c). */\nexport function rotateAboutPoint(cx: number, cy: number, rad: number): Mat3 {\n return multiply(translate(cx, cy), multiply(rotate(rad), translate(-cx, -cy)));\n}\n","/** Axis-aligned box as [minX, minY, maxX, maxY]. */\nexport type Box = [number, number, number, number];\n\n/** Tight bounds of an interleaved coord stream, or null if empty. */\nexport function boundsOfCoords(coords: ArrayLike<number>): Box | null {\n if (coords.length < 2) return null;\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (let i = 0; i + 1 < coords.length; i += 2) {\n const x = coords[i], y = coords[i + 1];\n if (x < minX) minX = x;\n if (y < minY) minY = y;\n if (x > maxX) maxX = x;\n if (y > maxY) maxY = y;\n }\n return [minX, minY, maxX, maxY];\n}\n\n/** Smallest box containing both inputs. */\nexport function unionBox(a: Box, b: Box): Box {\n return [\n Math.min(a[0], b[0]),\n Math.min(a[1], b[1]),\n Math.max(a[2], b[2]),\n Math.max(a[3], b[3]),\n ];\n}\n\n/** Inclusive point-in-box test. */\nexport function boxContainsPoint(b: Box, x: number, y: number): boolean {\n return x >= b[0] && x <= b[2] && y >= b[1] && y <= b[3];\n}\n\n/** Closed interleaved ring (first vertex repeated) for a rect at (x,y,w,h). */\nexport function rectToContour(x: number, y: number, w: number, h: number): Float64Array {\n return Float64Array.of(x, y, x + w, y, x + w, y + h, x, y + h, x, y);\n}\n","import { sign, dot, len2 } from './scalar';\n\n/**\n * Even-odd ray-cast point-in-polygon over an interleaved, unclosed contour\n * [x0,y0,x1,y1,…]. The closing edge (last→first) is implicit. Flat rewrite of\n * features/paths/polygonHitTestRect.ts pointInPolygon — same algorithm.\n */\nexport function pointInPolygon(coords: ArrayLike<number>, px: number, py: number): boolean {\n const n = coords.length >> 1;\n if (n < 3) return false;\n let inside = false;\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const xi = coords[i * 2], yi = coords[i * 2 + 1];\n const xj = coords[j * 2], yj = coords[j * 2 + 1];\n const crosses =\n (yi > py) !== (yj > py) &&\n px < ((xj - xi) * (py - yi)) / (yj - yi) + xi;\n if (crosses) inside = !inside;\n }\n return inside;\n}\n\n/** True if segment (ax,ay)-(bx,by) properly crosses (cx,cy)-(dx,dy). Flat\n * rewrite of polygonHitTestRect.ts segmentsCross. */\nexport function segmentsCross(\n ax: number, ay: number, bx: number, by: number,\n cx: number, cy: number, dx: number, dy: number,\n): boolean {\n const d1 = sign((dx - cx) * (ay - cy) - (dy - cy) * (ax - cx));\n const d2 = sign((dx - cx) * (by - cy) - (dy - cy) * (bx - cx));\n const d3 = sign((bx - ax) * (cy - ay) - (by - ay) * (cx - ax));\n const d4 = sign((bx - ax) * (dy - ay) - (by - ay) * (dx - ax));\n return d1 !== d2 && d3 !== d4;\n}\n\n/** Squared distance from (px,py) to segment (ax,ay)-(bx,by), endpoint-clamped. */\nexport function pointSegmentDist2(\n px: number, py: number, ax: number, ay: number, bx: number, by: number,\n): number {\n const vx = bx - ax, vy = by - ay;\n const wx = px - ax, wy = py - ay;\n const vv = len2(vx, vy);\n let t = vv === 0 ? 0 : dot(wx, wy, vx, vy) / vv;\n t = t < 0 ? 0 : t > 1 ? 1 : t;\n const dx = px - (ax + t * vx), dy = py - (ay + t * vy);\n return len2(dx, dy);\n}\n","import type { Mat3 } from './mat3';\n\n/**\n * Apply an affine to an interleaved coord stream, returning a fresh f64\n * buffer. Command codes are unaffected — for a Bezier the transformed control\n * points define the transformed curve exactly (affine invariance), so callers\n * pass `path.coords` straight through and keep `path.commands` as-is.\n */\nexport function transformCoords(coords: ArrayLike<number>, m: Mat3): Float64Array {\n const out = new Float64Array(coords.length);\n const a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];\n for (let i = 0; i + 1 < coords.length; i += 2) {\n const x = coords[i], y = coords[i + 1];\n out[i] = a * x + c * y + e;\n out[i + 1] = b * x + d * y + f;\n }\n return out;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@weasel-js/geom",
3
+ "version": "0.5.0",
4
+ "description": "Pure 2D geometry kernel for @weasel-js/core: affine, box, curve, polyline. Dependency-free core; polygon booleans in the ./booleans subpath.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./booleans": {
17
+ "import": "./dist/booleans/index.js",
18
+ "types": "./dist/booleans/index.d.ts"
19
+ },
20
+ "./package.json": "./package.json"
21
+ },
22
+ "dependencies": {
23
+ "polygon-clipping": "^0.15.7"
24
+ },
25
+ "author": "orochi235",
26
+ "homepage": "https://orochi235.github.io/weasel/",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/orochi235/weasel.git",
30
+ "directory": "packages/geom"
31
+ },
32
+ "bugs": {
33
+ "url": "https://github.com/orochi235/weasel/issues"
34
+ },
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "scripts": {
47
+ "test": "vitest run",
48
+ "build": "tsup"
49
+ }
50
+ }