@weasel-js/geom 1.0.2 → 1.0.4
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/dist/booleans/index.js +49 -2
- package/dist/booleans/index.js.map +1 -1
- package/dist/{chunk-K5JJ6WRB.js → chunk-KU7FXMOA.js} +9 -5
- package/dist/chunk-KU7FXMOA.js.map +1 -0
- package/dist/index.d.ts +25 -5
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-K5JJ6WRB.js.map +0 -1
package/dist/booleans/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PATH_M, PATH_L, PATH_Z, PATH_Q, elevateQuadraticToCubic, flattenCubic, PATH_C } from '../chunk-
|
|
1
|
+
import { PATH_M, PATH_L, PATH_Z, PATH_Q, elevateQuadraticToCubic, flattenCubic, PATH_C } from '../chunk-KU7FXMOA.js';
|
|
2
2
|
import polygonClipping from 'polygon-clipping';
|
|
3
3
|
|
|
4
4
|
// src/booleans/adapter.ts
|
|
@@ -81,7 +81,54 @@ function pathToMultiPolygon(path, opts = {}) {
|
|
|
81
81
|
}
|
|
82
82
|
finalizeCurrent();
|
|
83
83
|
if (rings.length === 0) return [];
|
|
84
|
-
return rings.
|
|
84
|
+
return nestRings(rings, path.fillRule ?? "nonzero");
|
|
85
|
+
}
|
|
86
|
+
function doubleSignedArea(ring) {
|
|
87
|
+
let a = 0;
|
|
88
|
+
for (let i = 0, n = ring.length - 1; i < n; i++) {
|
|
89
|
+
a += ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1];
|
|
90
|
+
}
|
|
91
|
+
return a;
|
|
92
|
+
}
|
|
93
|
+
function pointInRing(ring, px, py) {
|
|
94
|
+
let inside = false;
|
|
95
|
+
for (let i = 0, j = ring.length - 2, n = ring.length - 1; i < n; j = i++) {
|
|
96
|
+
const [xi, yi] = ring[i];
|
|
97
|
+
const [xj, yj] = ring[j];
|
|
98
|
+
if (yi > py !== yj > py && px < (xj - xi) * (py - yi) / (yj - yi) + xi) {
|
|
99
|
+
inside = !inside;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return inside;
|
|
103
|
+
}
|
|
104
|
+
function nestRings(rings, fillRule) {
|
|
105
|
+
const n = rings.length;
|
|
106
|
+
if (n === 1) return [[rings[0]]];
|
|
107
|
+
const areas = rings.map(doubleSignedArea);
|
|
108
|
+
const probes = rings.map((r) => [(r[0][0] + r[1][0]) / 2, (r[0][1] + r[1][1]) / 2]);
|
|
109
|
+
const containers = rings.map(() => []);
|
|
110
|
+
for (let i = 0; i < n; i++) {
|
|
111
|
+
for (let j = 0; j < n; j++) {
|
|
112
|
+
if (i !== j && pointInRing(rings[j], probes[i][0], probes[i][1])) containers[i].push(j);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const filled = rings.map((_, i) => {
|
|
116
|
+
if (fillRule === "evenodd") return containers[i].length % 2 === 0;
|
|
117
|
+
let winding = Math.sign(areas[i]);
|
|
118
|
+
for (const j of containers[i]) winding += Math.sign(areas[j]);
|
|
119
|
+
return winding !== 0;
|
|
120
|
+
});
|
|
121
|
+
const out = [];
|
|
122
|
+
for (let i = 0; i < n; i++) {
|
|
123
|
+
if (!filled[i]) continue;
|
|
124
|
+
const poly = [rings[i]];
|
|
125
|
+
for (let k = 0; k < n; k++) {
|
|
126
|
+
if (filled[k] || containers[k].length !== containers[i].length + 1) continue;
|
|
127
|
+
if (containers[k].includes(i)) poly.push(rings[k]);
|
|
128
|
+
}
|
|
129
|
+
out.push(poly);
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
85
132
|
}
|
|
86
133
|
function multiPolygonToPath(mp) {
|
|
87
134
|
let nCmds = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/booleans/adapter.ts","../../src/booleans/index.ts"],"names":[],"mappings":"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","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"]}
|
|
1
|
+
{"version":3,"sources":["../../src/booleans/adapter.ts","../../src/booleans/index.ts"],"names":[],"mappings":"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","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 return nestRings(rings, path.fillRule ?? 'nonzero');\n}\n\n/** Twice the signed area of a closed ring. Positive and negative encode the\n * two winding directions; the factor of two is irrelevant to both uses\n * (sign, and comparison against zero). */\nfunction doubleSignedArea(ring: Ring): number {\n let a = 0;\n for (let i = 0, n = ring.length - 1; i < n; i++) {\n a += ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1];\n }\n return a;\n}\n\n/** Even-odd ray cast of `(px, py)` against a closed ring. */\nfunction pointInRing(ring: Ring, px: number, py: number): boolean {\n let inside = false;\n for (let i = 0, j = ring.length - 2, n = ring.length - 1; i < n; j = i++) {\n const [xi, yi] = ring[i];\n const [xj, yj] = ring[j];\n if ((yi > py) !== (yj > py) && px < ((xj - xi) * (py - yi)) / (yj - yi) + xi) {\n inside = !inside;\n }\n }\n return inside;\n}\n\n/**\n * Group a path's rings into `polygon-clipping` polygons — one filled outer\n * ring followed by the hole rings immediately inside it.\n *\n * Without this a path's own holes are lost: separate polygons in a\n * MultiPolygon are unioned, so a donut arrives at the clipper as a solid disc.\n *\n * Containment is probed from the midpoint of each ring's first edge rather\n * than from a vertex — a shared vertex between a hole and its container is\n * common, and a probe point sitting exactly on the tested boundary answers\n * arbitrarily.\n */\nfunction nestRings(rings: Ring[], fillRule: 'nonzero' | 'evenodd'): MultiPolygon {\n const n = rings.length;\n if (n === 1) return [[rings[0]]];\n\n const areas = rings.map(doubleSignedArea);\n const probes = rings.map((r): Pair => [(r[0][0] + r[1][0]) / 2, (r[0][1] + r[1][1]) / 2]);\n\n // containers[i] — indices of the rings that enclose ring i.\n const containers: number[][] = rings.map(() => []);\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < n; j++) {\n if (i !== j && pointInRing(rings[j], probes[i][0], probes[i][1])) containers[i].push(j);\n }\n }\n\n const filled = rings.map((_, i) => {\n if (fillRule === 'evenodd') return containers[i].length % 2 === 0;\n let winding = Math.sign(areas[i]);\n for (const j of containers[i]) winding += Math.sign(areas[j]);\n return winding !== 0;\n });\n\n const out: MultiPolygon = [];\n for (let i = 0; i < n; i++) {\n if (!filled[i]) continue;\n const poly: Polygon = [rings[i]];\n for (let k = 0; k < n; k++) {\n if (filled[k] || containers[k].length !== containers[i].length + 1) continue;\n if (containers[k].includes(i)) poly.push(rings[k]);\n }\n out.push(poly);\n }\n return out;\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"]}
|
|
@@ -49,10 +49,14 @@ function distPointToLine(px, py, ax, ay, bx, by) {
|
|
|
49
49
|
const cross = (px - ax) * dy - (py - ay) * dx;
|
|
50
50
|
return Math.abs(cross) / Math.sqrt(len2);
|
|
51
51
|
}
|
|
52
|
+
var MAX_FLATTEN_DEPTH = 16;
|
|
52
53
|
function flattenCubic(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out) {
|
|
54
|
+
flattenCubicRec(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out, 0);
|
|
55
|
+
}
|
|
56
|
+
function flattenCubicRec(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out, depth) {
|
|
53
57
|
const d1 = distPointToLine(x1, y1, x0, y0, x3, y3);
|
|
54
58
|
const d2 = distPointToLine(x2, y2, x0, y0, x3, y3);
|
|
55
|
-
if (Math.max(d1, d2)
|
|
59
|
+
if (!(Math.max(d1, d2) > tolerance) || depth >= MAX_FLATTEN_DEPTH) {
|
|
56
60
|
out.push(x3, y3);
|
|
57
61
|
return;
|
|
58
62
|
}
|
|
@@ -62,8 +66,8 @@ function flattenCubic(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out) {
|
|
|
62
66
|
const x012 = (x01 + x12) * 0.5, y012 = (y01 + y12) * 0.5;
|
|
63
67
|
const x123 = (x12 + x23) * 0.5, y123 = (y12 + y23) * 0.5;
|
|
64
68
|
const x0123 = (x012 + x123) * 0.5, y0123 = (y012 + y123) * 0.5;
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
flattenCubicRec(x0, y0, x01, y01, x012, y012, x0123, y0123, tolerance, out, depth + 1);
|
|
70
|
+
flattenCubicRec(x0123, y0123, x123, y123, x23, y23, x3, y3, tolerance, out, depth + 1);
|
|
67
71
|
}
|
|
68
72
|
function componentExtremaTs(p0, p1, p2, p3) {
|
|
69
73
|
const a = -p0 + 3 * p1 - 3 * p2 + p3;
|
|
@@ -102,5 +106,5 @@ function cubicBounds(x0, y0, x1, y1, x2, y2, x3, y3) {
|
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
export { PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, cubicBounds, cubicEvalAt, elevateQuadraticToCubic, flattenCubic, forEachSegment };
|
|
105
|
-
//# sourceMappingURL=chunk-
|
|
106
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-KU7FXMOA.js.map
|
|
110
|
+
//# sourceMappingURL=chunk-KU7FXMOA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands.ts","../src/curve.ts"],"names":[],"mappings":"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","file":"chunk-KU7FXMOA.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\n/** Straight segment from the pen to the next coord pair. */\nexport const PATH_L = 1;\n/** Cubic bezier: two control points then the endpoint. */\nexport const PATH_C = 2;\n/** Quadratic bezier: one control point then the endpoint. */\nexport const PATH_Q = 3;\n/** Close the current subpath back to its start. Consumes no coords and leaves\n * the pen where it is. */\nexport const PATH_Z = 4;\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 * Subdivision ceiling for {@link flattenCubic}. Error falls roughly 4× per\n * level, so 16 levels is ~4e9 times tighter than the starting chord — far\n * beyond any tolerance a caller can mean, and reached only by input that\n * would otherwise never terminate (a non-finite coordinate, `tolerance <= 0`).\n */\nconst MAX_FLATTEN_DEPTH = 16;\n\n/**\n * Adaptive flatten of a cubic into interleaved points appended to `out`\n * (excludes the start point, includes the endpoint).\n *\n * Always terminates: a non-finite coordinate makes the flatness test\n * unsatisfiable and `tolerance <= 0` makes it unreachable, so subdivision is\n * capped at {@link MAX_FLATTEN_DEPTH}.\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 flattenCubicRec(x0, y0, x1, y1, x2, y2, x3, y3, tolerance, out, 0);\n}\n\nfunction flattenCubicRec(\n x0: number, y0: number, x1: number, y1: number,\n x2: number, y2: number, x3: number, y3: number,\n tolerance: number, out: number[], depth: number,\n): void {\n const d1 = distPointToLine(x1, y1, x0, y0, x3, y3);\n const d2 = distPointToLine(x2, y2, x0, y0, x3, y3);\n // Negated so a NaN deviation reads as flat rather than as \"keep splitting\".\n if (!(Math.max(d1, d2) > tolerance) || depth >= MAX_FLATTEN_DEPTH) {\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 flattenCubicRec(x0, y0, x01, y01, x012, y012, x0123, y0123, tolerance, out, depth + 1);\n flattenCubicRec(x0123, y0123, x123, y123, x23, y23, x3, y3, tolerance, out, depth + 1);\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"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -42,13 +42,20 @@ declare function approxEq(a: number, b: number, eps?: number): boolean;
|
|
|
42
42
|
* form stays a render-layer concern. No second logical convention is created.
|
|
43
43
|
*/
|
|
44
44
|
type Mat3 = number[];
|
|
45
|
+
/** The transform that leaves a point where it is. */
|
|
45
46
|
declare function identity(): Mat3;
|
|
47
|
+
/** A pure translation by `(tx, ty)`. */
|
|
46
48
|
declare function translate(tx: number, ty: number): Mat3;
|
|
49
|
+
/** A pure scale about the origin. Scale about another point by composing
|
|
50
|
+
* translate → scale → inverse translate with `multiply`. */
|
|
47
51
|
declare function scale(sx: number, sy: number): Mat3;
|
|
52
|
+
/** A pure rotation about the origin, in radians, clockwise in canvas
|
|
53
|
+
* coordinates (y down). */
|
|
48
54
|
declare function rotate(rad: number): Mat3;
|
|
49
55
|
/** Compose: result applies `n` first, then `m` (m·n). */
|
|
50
56
|
declare function multiply(m: Mat3, n: Mat3): Mat3;
|
|
51
|
-
/** Inverse, or null when the matrix is singular
|
|
57
|
+
/** Inverse, or null when the matrix is singular, non-finite, or too
|
|
58
|
+
* ill-conditioned to invert meaningfully at its own scale. */
|
|
52
59
|
declare function invert(m: Mat3): Mat3 | null;
|
|
53
60
|
/** Apply to a point, returning a tuple. Cold-path use; hot loops inline. */
|
|
54
61
|
declare function applyToPoint(m: Mat3, x: number, y: number): [number, number];
|
|
@@ -74,9 +81,14 @@ declare function rectToContour(x: number, y: number, w: number, h: number): Floa
|
|
|
74
81
|
* features/paths/types.ts; Spec 2 re-points that file to re-export these.
|
|
75
82
|
*/
|
|
76
83
|
declare const PATH_M = 0;
|
|
84
|
+
/** Straight segment from the pen to the next coord pair. */
|
|
77
85
|
declare const PATH_L = 1;
|
|
86
|
+
/** Cubic bezier: two control points then the endpoint. */
|
|
78
87
|
declare const PATH_C = 2;
|
|
88
|
+
/** Quadratic bezier: one control point then the endpoint. */
|
|
79
89
|
declare const PATH_Q = 3;
|
|
90
|
+
/** Close the current subpath back to its start. Consumes no coords and leaves
|
|
91
|
+
* the pen where it is. */
|
|
80
92
|
declare const PATH_Z = 4;
|
|
81
93
|
/** Float coords consumed by each command, indexed by command code. */
|
|
82
94
|
declare const PATH_CMD_LENGTHS: readonly number[];
|
|
@@ -98,8 +110,11 @@ declare function cubicEvalAt(x0: number, y0: number, x1: number, y1: number, x2:
|
|
|
98
110
|
declare function elevateQuadraticToCubic(q0x: number, q0y: number, cx: number, cy: number, q1x: number, q1y: number): [number, number, number, number];
|
|
99
111
|
/**
|
|
100
112
|
* Adaptive flatten of a cubic into interleaved points appended to `out`
|
|
101
|
-
* (excludes the start point, includes the endpoint).
|
|
102
|
-
*
|
|
113
|
+
* (excludes the start point, includes the endpoint).
|
|
114
|
+
*
|
|
115
|
+
* Always terminates: a non-finite coordinate makes the flatness test
|
|
116
|
+
* unsatisfiable and `tolerance <= 0` makes it unreachable, so subdivision is
|
|
117
|
+
* capped at {@link MAX_FLATTEN_DEPTH}.
|
|
103
118
|
*/
|
|
104
119
|
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
120
|
/** Tight AABB of a cubic, evaluating only extrema that lie on the curve. */
|
|
@@ -111,8 +126,13 @@ declare function cubicBounds(x0: number, y0: number, x1: number, y1: number, x2:
|
|
|
111
126
|
* features/paths/polygonHitTestRect.ts pointInPolygon — same algorithm.
|
|
112
127
|
*/
|
|
113
128
|
declare function pointInPolygon(coords: ArrayLike<number>, px: number, py: number): boolean;
|
|
114
|
-
/**
|
|
115
|
-
*
|
|
129
|
+
/**
|
|
130
|
+
* True if segments (ax,ay)-(bx,by) and (cx,cy)-(dx,dy) intersect at a single
|
|
131
|
+
* point. An endpoint landing on the other segment counts (a T-junction and a
|
|
132
|
+
* shared endpoint both answer true) — hit-testing wants a grazing edge to
|
|
133
|
+
* register. Collinear overlap answers false: there is no single crossing
|
|
134
|
+
* point, so the sign test both segments rely on is zero on all four sides.
|
|
135
|
+
*/
|
|
116
136
|
declare function segmentsCross(ax: number, ay: number, bx: number, by: number, cx: number, cy: number, dx: number, dy: number): boolean;
|
|
117
137
|
/** Squared distance from (px,py) to segment (ax,ay)-(bx,by), endpoint-clamped. */
|
|
118
138
|
declare function pointSegmentDist2(px: number, py: number, ax: number, ay: number, bx: number, by: number): number;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, cubicBounds, cubicEvalAt, elevateQuadraticToCubic, flattenCubic, forEachSegment } from './chunk-
|
|
1
|
+
export { PATH_C, PATH_CMD_LENGTHS, PATH_L, PATH_M, PATH_Q, PATH_Z, cubicBounds, cubicEvalAt, elevateQuadraticToCubic, flattenCubic, forEachSegment } from './chunk-KU7FXMOA.js';
|
|
2
2
|
|
|
3
3
|
// src/scalar.ts
|
|
4
4
|
var EPS = 1e-6;
|
|
@@ -18,8 +18,9 @@ function sign(n) {
|
|
|
18
18
|
return n > 0 ? 1 : n < 0 ? -1 : 0;
|
|
19
19
|
}
|
|
20
20
|
function approxEq(a, b, eps = EPS) {
|
|
21
|
+
if (a === b) return true;
|
|
21
22
|
const diff = Math.abs(a - b);
|
|
22
|
-
if (diff
|
|
23
|
+
if (!Number.isFinite(diff)) return false;
|
|
23
24
|
const scale2 = Math.max(1, Math.abs(a), Math.abs(b));
|
|
24
25
|
return diff <= eps * scale2;
|
|
25
26
|
}
|
|
@@ -49,9 +50,11 @@ function multiply(m, n) {
|
|
|
49
50
|
m[1] * n[4] + m[3] * n[5] + m[5]
|
|
50
51
|
];
|
|
51
52
|
}
|
|
53
|
+
var SINGULAR_RATIO = 1e-12;
|
|
52
54
|
function invert(m) {
|
|
53
55
|
const det = m[0] * m[3] - m[1] * m[2];
|
|
54
|
-
|
|
56
|
+
const norm2 = Math.max(m[0] * m[0] + m[1] * m[1], m[2] * m[2] + m[3] * m[3]);
|
|
57
|
+
if (!(Math.abs(det) > SINGULAR_RATIO * norm2)) return null;
|
|
55
58
|
const id = 1 / det;
|
|
56
59
|
const a = m[3] * id;
|
|
57
60
|
const b = -m[1] * id;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scalar.ts","../src/mat3.ts","../src/box.ts","../src/polyline.ts","../src/affine.ts"],"names":["scale"],"mappings":"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","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"]}
|
|
1
|
+
{"version":3,"sources":["../src/scalar.ts","../src/mat3.ts","../src/box.ts","../src/polyline.ts","../src/affine.ts"],"names":["scale"],"mappings":"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","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 if (a === b) return true; // also the only way two infinities compare equal\n const diff = Math.abs(a - b);\n // Infinity/Infinity is 1, so an unguarded relative test calls every finite\n // number equal to an infinity.\n if (!Number.isFinite(diff)) return false;\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\n/** The transform that leaves a point where it is. */\nexport function identity(): Mat3 {\n return [1, 0, 0, 1, 0, 0];\n}\n\n/** A pure translation by `(tx, ty)`. */\nexport function translate(tx: number, ty: number): Mat3 {\n return [1, 0, 0, 1, tx, ty];\n}\n\n/** A pure scale about the origin. Scale about another point by composing\n * translate → scale → inverse translate with `multiply`. */\nexport function scale(sx: number, sy: number): Mat3 {\n return [sx, 0, 0, sy, 0, 0];\n}\n\n/** A pure rotation about the origin, in radians, clockwise in canvas\n * coordinates (y down). */\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/** Largest tolerated ratio between a matrix's conditioning and its scale.\n * The determinant is an area, so it must be judged against the squared\n * column norms — an absolute floor calls a uniform 1e-7 scale singular while\n * waving through a large matrix whose determinant is pure cancellation. */\nconst SINGULAR_RATIO = 1e-12;\n\n/** Inverse, or null when the matrix is singular, non-finite, or too\n * ill-conditioned to invert meaningfully at its own scale. */\nexport function invert(m: Mat3): Mat3 | null {\n const det = m[0] * m[3] - m[1] * m[2];\n const norm2 = Math.max(m[0] * m[0] + m[1] * m[1], m[2] * m[2] + m[3] * m[3]);\n // Negated so a non-finite determinant falls out as singular.\n if (!(Math.abs(det) > SINGULAR_RATIO * norm2)) 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/**\n * True if segments (ax,ay)-(bx,by) and (cx,cy)-(dx,dy) intersect at a single\n * point. An endpoint landing on the other segment counts (a T-junction and a\n * shared endpoint both answer true) — hit-testing wants a grazing edge to\n * register. Collinear overlap answers false: there is no single crossing\n * point, so the sign test both segments rely on is zero on all four sides.\n */\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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/geom",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Pure 2D geometry kernel for @weasel-js/core: affine, box, curve, polyline. Dependency-free core; polygon booleans in the ./booleans subpath.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands.ts","../src/curve.ts"],"names":[],"mappings":";AAKO,IAAM,MAAA,GAAS;AACf,IAAM,MAAA,GAAS;AACf,IAAM,MAAA,GAAS;AACf,IAAM,MAAA,GAAS;AACf,IAAM,MAAA,GAAS;AAGf,IAAM,mBAAsC,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC;AAQ1D,SAAS,cAAA,CACd,QAAA,EACA,MAAA,EACA,KAAA,EACM;AACN,EAAA,IAAI,EAAA,GAAK,CAAA;AACT,EAAA,IAAI,EAAA,GAAK,GAAG,EAAA,GAAK,CAAA;AACjB,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,KAAA,CAAM,GAAA,EAAK,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA;AACrB,IAAA,MAAM,GAAA,GAAM,iBAAiB,GAAG,CAAA;AAChC,IAAA,IAAI,MAAM,CAAA,EAAG;AACX,MAAA,EAAA,GAAK,MAAA,CAAO,EAAA,GAAK,GAAA,GAAM,CAAC,CAAA;AACxB,MAAA,EAAA,GAAK,MAAA,CAAO,EAAA,GAAK,GAAA,GAAM,CAAC,CAAA;AACxB,MAAA,EAAA,IAAM,GAAA;AAAA,IACR;AAAA,EACF;AACF;;;AClCO,SAAS,WAAA,CACd,IAAY,EAAA,EAAY,EAAA,EAAY,IACpC,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,CAAA,EAC9B;AAClB,EAAA,MAAM,IAAI,CAAA,GAAI,CAAA;AACd,EAAA,MAAM,IAAI,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAI,CAAA,GAAI,CAAA;AACvE,EAAA,OAAO;AAAA,IACL,IAAI,EAAA,GAAK,CAAA,GAAI,EAAA,GAAK,CAAA,GAAI,KAAK,CAAA,GAAI,EAAA;AAAA,IAC/B,IAAI,EAAA,GAAK,CAAA,GAAI,EAAA,GAAK,CAAA,GAAI,KAAK,CAAA,GAAI;AAAA,GACjC;AACF;AAOO,SAAS,wBACd,GAAA,EAAa,GAAA,EAAa,EAAA,EAAY,EAAA,EAAY,KAAa,GAAA,EAC7B;AAClC,EAAA,OAAO;AAAA,IACL,GAAA,GAAO,CAAA,GAAI,CAAA,IAAM,EAAA,GAAK,GAAA,CAAA;AAAA,IACtB,GAAA,GAAO,CAAA,GAAI,CAAA,IAAM,EAAA,GAAK,GAAA,CAAA;AAAA,IACtB,GAAA,GAAO,CAAA,GAAI,CAAA,IAAM,EAAA,GAAK,GAAA,CAAA;AAAA,IACtB,GAAA,GAAO,CAAA,GAAI,CAAA,IAAM,EAAA,GAAK,GAAA;AAAA,GACxB;AACF;AAIA,SAAS,gBAAgB,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,IAAY,EAAA,EAAoB;AACvG,EAAA,MAAM,KAAK,EAAA,GAAK,EAAA;AAChB,EAAA,MAAM,KAAK,EAAA,GAAK,EAAA;AAChB,EAAA,MAAM,IAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,EAAA;AAC5B,EAAA,IAAI,SAAS,CAAA,EAAG;AACd,IAAA,MAAM,KAAK,EAAA,GAAK,EAAA;AAChB,IAAA,MAAM,KAAK,EAAA,GAAK,EAAA;AAChB,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,EAAA,GAAK,EAAA,GAAK,KAAK,EAAE,CAAA;AAAA,EACpC;AACA,EAAA,MAAM,KAAA,GAAA,CAAS,EAAA,GAAK,EAAA,IAAM,EAAA,GAAA,CAAM,KAAK,EAAA,IAAM,EAAA;AAC3C,EAAA,OAAO,KAAK,GAAA,CAAI,KAAK,CAAA,GAAI,IAAA,CAAK,KAAK,IAAI,CAAA;AACzC;AAOO,SAAS,YAAA,CACd,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EACpC,IAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EACpC,SAAA,EAAmB,GAAA,EACb;AACN,EAAA,MAAM,KAAK,eAAA,CAAgB,EAAA,EAAI,IAAI,EAAA,EAAI,EAAA,EAAI,IAAI,EAAE,CAAA;AACjD,EAAA,MAAM,KAAK,eAAA,CAAgB,EAAA,EAAI,IAAI,EAAA,EAAI,EAAA,EAAI,IAAI,EAAE,CAAA;AACjD,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,EAAE,KAAK,SAAA,EAAW;AACjC,IAAA,GAAA,CAAI,IAAA,CAAK,IAAI,EAAE,CAAA;AACf,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,OAAO,EAAA,GAAK,EAAA,IAAM,GAAA,EAAK,GAAA,GAAA,CAAO,KAAK,EAAA,IAAM,GAAA;AAC/C,EAAA,MAAM,OAAO,EAAA,GAAK,EAAA,IAAM,GAAA,EAAK,GAAA,GAAA,CAAO,KAAK,EAAA,IAAM,GAAA;AAC/C,EAAA,MAAM,OAAO,EAAA,GAAK,EAAA,IAAM,GAAA,EAAK,GAAA,GAAA,CAAO,KAAK,EAAA,IAAM,GAAA;AAC/C,EAAA,MAAM,QAAQ,GAAA,GAAM,GAAA,IAAO,GAAA,EAAK,IAAA,GAAA,CAAQ,MAAM,GAAA,IAAO,GAAA;AACrD,EAAA,MAAM,QAAQ,GAAA,GAAM,GAAA,IAAO,GAAA,EAAK,IAAA,GAAA,CAAQ,MAAM,GAAA,IAAO,GAAA;AACrD,EAAA,MAAM,SAAS,IAAA,GAAO,IAAA,IAAQ,GAAA,EAAK,KAAA,GAAA,CAAS,OAAO,IAAA,IAAQ,GAAA;AAC3D,EAAA,YAAA,CAAa,EAAA,EAAI,IAAI,GAAA,EAAK,GAAA,EAAK,MAAM,IAAA,EAAM,KAAA,EAAO,KAAA,EAAO,SAAA,EAAW,GAAG,CAAA;AACvE,EAAA,YAAA,CAAa,KAAA,EAAO,OAAO,IAAA,EAAM,IAAA,EAAM,KAAK,GAAA,EAAK,EAAA,EAAI,EAAA,EAAI,SAAA,EAAW,GAAG,CAAA;AACzE;AAIA,SAAS,kBAAA,CAAmB,EAAA,EAAY,EAAA,EAAY,EAAA,EAAY,EAAA,EAAsB;AAEpF,EAAA,MAAM,IAAI,CAAC,EAAA,GAAK,CAAA,GAAI,EAAA,GAAK,IAAI,EAAA,GAAK,EAAA;AAClC,EAAA,MAAM,CAAA,GAAI,CAAA,IAAK,EAAA,GAAK,CAAA,GAAI,EAAA,GAAK,EAAA,CAAA;AAC7B,EAAA,MAAM,CAAA,GAAI,CAAC,EAAA,GAAK,EAAA;AAChB,EAAA,MAAM,KAAe,EAAC;AACtB,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAc;AAAE,IAAA,IAAI,IAAI,CAAA,IAAK,CAAA,GAAI,CAAA,EAAG,EAAA,CAAG,KAAK,CAAC,CAAA;AAAA,EAAG,CAAA;AAC9D,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,GAAI,KAAA,EAAO;AACvB,IAAA,IAAI,IAAA,CAAK,IAAI,CAAC,CAAA,GAAI,OAAO,IAAA,CAAK,CAAC,IAAI,CAAC,CAAA;AAAA,EACtC,CAAA,MAAO;AACL,IAAA,MAAM,IAAA,GAAO,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA;AAC7B,IAAA,IAAI,QAAQ,CAAA,EAAG;AACb,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA;AACzB,MAAA,IAAA,CAAA,CAAM,CAAC,CAAA,GAAI,EAAA,KAAO,CAAA,GAAI,CAAA,CAAE,CAAA;AACxB,MAAA,IAAA,CAAA,CAAM,CAAC,CAAA,GAAI,EAAA,KAAO,CAAA,GAAI,CAAA,CAAE,CAAA;AAAA,IAC1B;AAAA,EACF;AACA,EAAA,OAAO,EAAA;AACT;AAGO,SAAS,WAAA,CACd,IAAY,EAAA,EAAY,EAAA,EAAY,IACpC,EAAA,EAAY,EAAA,EAAY,IAAY,EAAA,EAC/B;AACL,EAAA,IAAI,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,EAAE,GAAG,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,EAAE,CAAA;AACnD,EAAA,IAAI,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,EAAE,GAAG,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,EAAE,CAAA;AACnD,EAAA,KAAA,MAAW,KAAK,kBAAA,CAAmB,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,EAAG;AAClD,IAAA,MAAM,CAAC,EAAE,CAAA,GAAI,WAAA,CAAY,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,CAAC,CAAA;AAC1D,IAAA,IAAI,EAAA,GAAK,MAAM,IAAA,GAAO,EAAA;AACtB,IAAA,IAAI,EAAA,GAAK,MAAM,IAAA,GAAO,EAAA;AAAA,EACxB;AACA,EAAA,KAAA,MAAW,KAAK,kBAAA,CAAmB,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAE,CAAA,EAAG;AAClD,IAAA,MAAM,GAAG,EAAE,CAAA,GAAI,WAAA,CAAY,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,IAAI,CAAC,CAAA;AAC5D,IAAA,IAAI,EAAA,GAAK,MAAM,IAAA,GAAO,EAAA;AACtB,IAAA,IAAI,EAAA,GAAK,MAAM,IAAA,GAAO,EAAA;AAAA,EACxB;AACA,EAAA,OAAO,CAAC,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,IAAI,CAAA;AAChC","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"]}
|