@weasel-js/geom 1.10.1 → 1.11.1
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/README.md +9 -1
- package/dist/3d/index.d.ts +29 -2
- package/dist/3d/index.js +49 -1
- package/dist/3d/index.js.map +1 -1
- package/dist/booleans/index.d.ts +18 -11
- package/dist/booleans/index.js +432 -352
- package/dist/booleans/index.js.map +1 -1
- package/dist/chunk-4Y67BZL5.js +573 -0
- package/dist/chunk-4Y67BZL5.js.map +1 -0
- package/dist/chunk-6FGQML7D.js +152 -0
- package/dist/chunk-6FGQML7D.js.map +1 -0
- package/dist/{chunk-7WFFCHRL.js → chunk-7F66PQCJ.js} +6 -37
- package/dist/chunk-7F66PQCJ.js.map +1 -0
- package/dist/chunk-B53BERKG.js +386 -0
- package/dist/chunk-B53BERKG.js.map +1 -0
- package/dist/chunk-CE7IY7TB.js +104 -0
- package/dist/chunk-CE7IY7TB.js.map +1 -0
- package/dist/chunk-EAKE3N7W.js +804 -0
- package/dist/chunk-EAKE3N7W.js.map +1 -0
- package/dist/chunk-LRVPK7HI.js +491 -0
- package/dist/chunk-LRVPK7HI.js.map +1 -0
- package/dist/chunk-Q66NA2FU.js +174 -0
- package/dist/chunk-Q66NA2FU.js.map +1 -0
- package/dist/chunk-UI3FUWXM.js +533 -0
- package/dist/chunk-UI3FUWXM.js.map +1 -0
- package/dist/curvature-PkoLduGc.d.ts +454 -0
- package/dist/curves/index.d.ts +14 -5
- package/dist/curves/index.js +33 -111
- package/dist/curves/index.js.map +1 -1
- package/dist/index.d.ts +1511 -283
- package/dist/index.js +5755 -2674
- package/dist/index.js.map +1 -1
- package/dist/nd/index.d.ts +92 -27
- package/dist/nd/index.js +297 -0
- package/dist/nd/index.js.map +1 -1
- package/dist/path-BW8d_2RY.d.ts +157 -0
- package/dist/portCurve-C5HDWuuV.d.ts +30 -0
- package/dist/tessellate/index.d.ts +15 -4
- package/dist/tessellate/index.js +21 -277
- package/dist/tessellate/index.js.map +1 -1
- package/dist/warp/index.d.ts +254 -0
- package/dist/warp/index.js +1448 -0
- package/dist/warp/index.js.map +1 -0
- package/package.json +5 -1
- package/dist/chunk-7WFFCHRL.js.map +0 -1
- package/dist/chunk-IDXY2NCA.js +0 -177
- package/dist/chunk-IDXY2NCA.js.map +0 -1
- package/dist/chunk-NJ3IGAKR.js +0 -16
- package/dist/chunk-NJ3IGAKR.js.map +0 -1
- package/dist/chunk-OXH3NOXC.js +0 -62
- package/dist/chunk-OXH3NOXC.js.map +0 -1
- package/dist/chunk-URQMDR6Q.js +0 -80
- package/dist/chunk-URQMDR6Q.js.map +0 -1
- package/dist/cubicMath-BW-HJrCE.d.ts +0 -16
- package/dist/path-ljHxobyu.d.ts +0 -44
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { nearestOnLine, nearestOnCubic } from './chunk-CE7IY7TB.js';
|
|
2
|
+
import { forEachSegment, PATH_Z, PATH_Q, PATH_C, PATH_L, PATH_M, cornerSpecAt, PathBuilder, compactCorners, withoutCorners } from './chunk-B53BERKG.js';
|
|
3
|
+
|
|
4
|
+
// src/paths/anchors.ts
|
|
5
|
+
function pathToAnchors(path) {
|
|
6
|
+
const { commands, coords } = path;
|
|
7
|
+
const anchors = [];
|
|
8
|
+
const closed = [];
|
|
9
|
+
let current = null;
|
|
10
|
+
forEachSegment(commands, coords, (cmd, ci) => {
|
|
11
|
+
switch (cmd) {
|
|
12
|
+
case PATH_M: {
|
|
13
|
+
if (current) {
|
|
14
|
+
anchors.push(current);
|
|
15
|
+
closed.push(false);
|
|
16
|
+
}
|
|
17
|
+
current = [{ x: coords[ci], y: coords[ci + 1] }];
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
case PATH_L: {
|
|
21
|
+
if (!current) throw new Error("pathToAnchors: L without prior M");
|
|
22
|
+
current.push({ x: coords[ci], y: coords[ci + 1] });
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
case PATH_C: {
|
|
26
|
+
if (!current) throw new Error("pathToAnchors: C without prior M");
|
|
27
|
+
const prev = current[current.length - 1];
|
|
28
|
+
prev.outHandle = { x: coords[ci], y: coords[ci + 1] };
|
|
29
|
+
current.push({
|
|
30
|
+
x: coords[ci + 4],
|
|
31
|
+
y: coords[ci + 5],
|
|
32
|
+
inHandle: { x: coords[ci + 2], y: coords[ci + 3] }
|
|
33
|
+
});
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case PATH_Q: {
|
|
37
|
+
if (!current) throw new Error("pathToAnchors: Q without prior M");
|
|
38
|
+
const handle = { x: coords[ci], y: coords[ci + 1] };
|
|
39
|
+
const prev = current[current.length - 1];
|
|
40
|
+
prev.outHandle = handle;
|
|
41
|
+
current.push({ x: coords[ci + 2], y: coords[ci + 3], inHandle: { ...handle } });
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
case PATH_Z: {
|
|
45
|
+
if (current) {
|
|
46
|
+
foldClosingCurve(current);
|
|
47
|
+
anchors.push(current);
|
|
48
|
+
closed.push(true);
|
|
49
|
+
current = null;
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
default:
|
|
54
|
+
throw new Error(`pathToAnchors: unknown command ${cmd}`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (current) {
|
|
58
|
+
anchors.push(current);
|
|
59
|
+
closed.push(false);
|
|
60
|
+
}
|
|
61
|
+
if (path.corners) {
|
|
62
|
+
let flat = 0;
|
|
63
|
+
for (const sub of anchors) {
|
|
64
|
+
for (const a of sub) {
|
|
65
|
+
const corner = cornerSpecAt(path, flat++);
|
|
66
|
+
if (corner) a.corner = { ...corner };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { anchors, closed };
|
|
71
|
+
}
|
|
72
|
+
function foldClosingCurve(sub) {
|
|
73
|
+
if (sub.length < 2) return;
|
|
74
|
+
const first = sub[0];
|
|
75
|
+
const last = sub[sub.length - 1];
|
|
76
|
+
if (!last.inHandle || last.x !== first.x || last.y !== first.y) return;
|
|
77
|
+
sub.pop();
|
|
78
|
+
first.inHandle = last.inHandle;
|
|
79
|
+
}
|
|
80
|
+
function anchorsToPath(anchors, closed, like) {
|
|
81
|
+
const b = new PathBuilder();
|
|
82
|
+
for (let s = 0; s < anchors.length; s++) {
|
|
83
|
+
const sub = anchors[s];
|
|
84
|
+
if (sub.length === 0) continue;
|
|
85
|
+
b.moveTo(sub[0].x, sub[0].y);
|
|
86
|
+
for (let i = 1; i < sub.length; i++) {
|
|
87
|
+
const prev = sub[i - 1];
|
|
88
|
+
const cur = sub[i];
|
|
89
|
+
const hasHandle = prev.outHandle != null || cur.inHandle != null;
|
|
90
|
+
if (!hasHandle) {
|
|
91
|
+
b.lineTo(cur.x, cur.y);
|
|
92
|
+
} else {
|
|
93
|
+
const c1 = prev.outHandle ?? { x: prev.x, y: prev.y };
|
|
94
|
+
const c2 = cur.inHandle ?? { x: cur.x, y: cur.y };
|
|
95
|
+
b.curveTo(c1.x, c1.y, c2.x, c2.y, cur.x, cur.y);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (closed[s]) {
|
|
99
|
+
const last = sub[sub.length - 1];
|
|
100
|
+
const first = sub[0];
|
|
101
|
+
const hasHandle = last.outHandle != null || first.inHandle != null;
|
|
102
|
+
if (hasHandle) {
|
|
103
|
+
const c1 = last.outHandle ?? { x: last.x, y: last.y };
|
|
104
|
+
const c2 = first.inHandle ?? { x: first.x, y: first.y };
|
|
105
|
+
b.curveTo(c1.x, c1.y, c2.x, c2.y, first.x, first.y);
|
|
106
|
+
}
|
|
107
|
+
b.close();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const built = b.build();
|
|
111
|
+
const corners = compactCorners(anchors.flatMap((sub) => sub.map((a) => a.corner ?? null)));
|
|
112
|
+
const base = like ? { ...withoutCorners(like), commands: built.commands, coords: built.coords } : built;
|
|
113
|
+
return corners ? { ...base, corners } : base;
|
|
114
|
+
}
|
|
115
|
+
var SMOOTH_THRESHOLD = 1e-3;
|
|
116
|
+
function isAnchorSmooth(a) {
|
|
117
|
+
if (!a.inHandle || !a.outHandle) return false;
|
|
118
|
+
const inDX = a.inHandle.x - a.x;
|
|
119
|
+
const inDY = a.inHandle.y - a.y;
|
|
120
|
+
const outDX = a.outHandle.x - a.x;
|
|
121
|
+
const outDY = a.outHandle.y - a.y;
|
|
122
|
+
const inLen = Math.hypot(inDX, inDY);
|
|
123
|
+
const outLen = Math.hypot(outDX, outDY);
|
|
124
|
+
if (inLen === 0 || outLen === 0) return false;
|
|
125
|
+
const cross = (inDX * outDY - inDY * outDX) / (inLen * outLen);
|
|
126
|
+
return Math.abs(cross) < SMOOTH_THRESHOLD;
|
|
127
|
+
}
|
|
128
|
+
function countPathAnchors(path) {
|
|
129
|
+
if (path.kind === "rect") return 4;
|
|
130
|
+
const cmds = path.commands;
|
|
131
|
+
let n = 0;
|
|
132
|
+
for (let i = 0; i < cmds.length; i++) {
|
|
133
|
+
const c = cmds[i];
|
|
134
|
+
if (c === PATH_M || c === PATH_L || c === PATH_C || c === PATH_Q) n++;
|
|
135
|
+
}
|
|
136
|
+
return n;
|
|
137
|
+
}
|
|
138
|
+
function segmentEnds(anchors, closed, sub, segIdx) {
|
|
139
|
+
const s = anchors[sub];
|
|
140
|
+
if (!s || segIdx < 0) return null;
|
|
141
|
+
if (segIdx + 1 < s.length) return { a: s[segIdx], b: s[segIdx + 1] };
|
|
142
|
+
if (closed?.[sub] && s.length > 1 && segIdx === s.length - 1) return { a: s[segIdx], b: s[0] };
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
function isStraightSegment(a, b) {
|
|
146
|
+
return a.outHandle == null && b.inHandle == null;
|
|
147
|
+
}
|
|
148
|
+
function nearestOnSegment(a, b, x, y) {
|
|
149
|
+
const p1 = a.outHandle ?? a;
|
|
150
|
+
const p2 = b.inHandle ?? b;
|
|
151
|
+
const hit = isStraightSegment(a, b) ? nearestOnLine(x, y, a.x, a.y, b.x, b.y) : nearestOnCubic(x, y, a.x, a.y, p1.x, p1.y, p2.x, p2.y, b.x, b.y);
|
|
152
|
+
return { t: hit.t, d2: hit.dist * hit.dist };
|
|
153
|
+
}
|
|
154
|
+
function nearestSegmentT(anchors, wx, wy, closed) {
|
|
155
|
+
let bestD2 = Infinity;
|
|
156
|
+
let best = null;
|
|
157
|
+
for (let s = 0; s < anchors.length; s++) {
|
|
158
|
+
const segCount = anchors[s].length - (closed?.[s] ? 0 : 1);
|
|
159
|
+
for (let i = 0; i < segCount; i++) {
|
|
160
|
+
const ends = segmentEnds(anchors, closed, s, i);
|
|
161
|
+
if (!ends) continue;
|
|
162
|
+
const hit = nearestOnSegment(ends.a, ends.b, wx, wy);
|
|
163
|
+
if (hit.d2 < bestD2) {
|
|
164
|
+
bestD2 = hit.d2;
|
|
165
|
+
best = { sub: s, segIdx: i, t: hit.t };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return best;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export { anchorsToPath, countPathAnchors, isAnchorSmooth, isStraightSegment, nearestSegmentT, pathToAnchors, segmentEnds };
|
|
173
|
+
//# sourceMappingURL=chunk-Q66NA2FU.js.map
|
|
174
|
+
//# sourceMappingURL=chunk-Q66NA2FU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/paths/anchors.ts"],"names":[],"mappings":"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","file":"chunk-Q66NA2FU.js","sourcesContent":["/**\n * Counts the path anchors used by the per-anchor coloring surface. An\n * \"anchor\" is the destination point of a path command: M, L, C, Q each\n * contribute one (the (x, y) where the pen ends up); Z contributes none\n * (it closes back to the subpath's first M). RectPath has four implicit\n * anchors — the corners — matching its M/L/L/L/Z stroke tessellation.\n *\n * Consumers use this to size their per-anchor color array; the renderer\n * uses it to validate the array length in dev builds.\n */\n\nimport { PATH_C, PATH_L, PATH_M, PATH_Q, PATH_Z, forEachSegment } from '../commands';\nimport { nearestOnCubic, nearestOnLine } from '../nearest';\nimport type { Path, PolygonPath } from '../path';\nimport { PathBuilder } from './builder';\nimport { compactCorners, cornerSpecAt, withoutCorners, type CornerSpec } from './cornerSpec';\n\n/** One anchor of an editable path: its on-curve point plus the two control\n * handles that shape the segments either side of it. Handles are in the same\n * space as the point, and absent when the adjoining segment is straight. */\nexport interface PenAnchor {\n x: number;\n y: number;\n inHandle?: { x: number; y: number };\n outHandle?: { x: number; y: number };\n /** The corner shape cut at this anchor (`PolygonPath.corners`). */\n corner?: CornerSpec;\n}\n\n/**\n * Derive a per-subpath anchor model from a PolygonPath. Subpaths split on\n * every `M` command; a subpath is closed iff it ends with `Z`.\n *\n * Cubic-segment control points become the outHandle of the previous anchor\n * and the inHandle of the next anchor. A closed subpath whose last curve ends\n * exactly on its first point holds that point twice; the two are one anchor,\n * and the curve's second control is that anchor's inHandle. Quadratic segments are upgraded to\n * cubics (each control reused for both adjacent handles) — this loses no\n * geometry. Linear segments produce anchors with no handles.\n */\nexport function pathToAnchors(\n path: PolygonPath,\n): { anchors: PenAnchor[][]; closed: boolean[] } {\n const { commands, coords } = path;\n const anchors: PenAnchor[][] = [];\n const closed: boolean[] = [];\n let current: PenAnchor[] | null = null;\n\n forEachSegment(commands, coords, (cmd, ci) => {\n switch (cmd) {\n case PATH_M: {\n if (current) { anchors.push(current); closed.push(false); }\n current = [{ x: coords[ci], y: coords[ci + 1] }];\n break;\n }\n case PATH_L: {\n if (!current) throw new Error('pathToAnchors: L without prior M');\n current.push({ x: coords[ci], y: coords[ci + 1] });\n break;\n }\n case PATH_C: {\n if (!current) throw new Error('pathToAnchors: C without prior M');\n const prev = current[current.length - 1];\n prev.outHandle = { x: coords[ci], y: coords[ci + 1] };\n current.push({\n x: coords[ci + 4], y: coords[ci + 5],\n inHandle: { x: coords[ci + 2], y: coords[ci + 3] },\n });\n break;\n }\n case PATH_Q: {\n if (!current) throw new Error('pathToAnchors: Q without prior M');\n // Quadratic → cubic: handle is the same control point on both sides.\n const handle = { x: coords[ci], y: coords[ci + 1] };\n const prev = current[current.length - 1];\n prev.outHandle = handle;\n current.push({ x: coords[ci + 2], y: coords[ci + 3], inHandle: { ...handle } });\n break;\n }\n case PATH_Z: {\n if (current) {\n foldClosingCurve(current);\n anchors.push(current);\n closed.push(true);\n current = null;\n }\n break;\n }\n default:\n throw new Error(`pathToAnchors: unknown command ${cmd}`);\n }\n });\n if (current) { anchors.push(current); closed.push(false); }\n if (path.corners) {\n let flat = 0;\n for (const sub of anchors) {\n for (const a of sub) {\n const corner = cornerSpecAt(path, flat++);\n if (corner) a.corner = { ...corner };\n }\n }\n }\n return { anchors, closed };\n}\n\n/** Merge a closing curve's end anchor into the subpath's first anchor when\n * the two coincide. */\nfunction foldClosingCurve(sub: PenAnchor[]): void {\n if (sub.length < 2) return;\n const first = sub[0];\n const last = sub[sub.length - 1];\n if (!last.inHandle || last.x !== first.x || last.y !== first.y) return;\n sub.pop();\n first.inHandle = last.inHandle;\n}\n\n/**\n * Serialize the per-subpath anchor model back to a PolygonPath. Inverse of\n * `pathToAnchors`. Curve-vs-line decision per segment:\n * - Both adjacent handles absent → straight L segment.\n * - Either handle present → C segment, with missing handles defaulting to\n * the anchor point itself (degenerate but valid; renders as a near-line).\n * Given `like`, the path being reshaped, the result keeps its fill rules.\n */\nexport function anchorsToPath(\n anchors: PenAnchor[][],\n closed: boolean[],\n like?: PolygonPath,\n): PolygonPath {\n const b = new PathBuilder();\n for (let s = 0; s < anchors.length; s++) {\n const sub = anchors[s];\n if (sub.length === 0) continue;\n b.moveTo(sub[0].x, sub[0].y);\n for (let i = 1; i < sub.length; i++) {\n const prev = sub[i - 1];\n const cur = sub[i];\n const hasHandle = prev.outHandle != null || cur.inHandle != null;\n if (!hasHandle) {\n b.lineTo(cur.x, cur.y);\n } else {\n const c1 = prev.outHandle ?? { x: prev.x, y: prev.y };\n const c2 = cur.inHandle ?? { x: cur.x, y: cur.y };\n b.curveTo(c1.x, c1.y, c2.x, c2.y, cur.x, cur.y);\n }\n }\n if (closed[s]) {\n // Bridge last → first if they have curve handles; either way emit Z.\n const last = sub[sub.length - 1];\n const first = sub[0];\n const hasHandle = last.outHandle != null || first.inHandle != null;\n if (hasHandle) {\n const c1 = last.outHandle ?? { x: last.x, y: last.y };\n const c2 = first.inHandle ?? { x: first.x, y: first.y };\n b.curveTo(c1.x, c1.y, c2.x, c2.y, first.x, first.y);\n }\n b.close();\n }\n }\n const built = b.build();\n const corners = compactCorners(anchors.flatMap((sub) => sub.map((a) => a.corner ?? null)));\n const base = like ? { ...withoutCorners(like), commands: built.commands, coords: built.coords } : built;\n return corners ? { ...base, corners } : base;\n}\n\nconst SMOOTH_THRESHOLD = 0.001;\n\n/**\n * Returns true if the anchor's in-handle and out-handle are collinear with\n * the anchor point, indicating \"smooth\" (mirror-drag) behavior. Detected via\n * the magnitude of the normalized cross product of the two handle vectors.\n *\n * Edge cases:\n * - Missing in or out handle → false (corner by definition).\n * - Either handle at zero distance from anchor → false.\n */\nexport function isAnchorSmooth(a: PenAnchor): boolean {\n if (!a.inHandle || !a.outHandle) return false;\n const inDX = a.inHandle.x - a.x;\n const inDY = a.inHandle.y - a.y;\n const outDX = a.outHandle.x - a.x;\n const outDY = a.outHandle.y - a.y;\n const inLen = Math.hypot(inDX, inDY);\n const outLen = Math.hypot(outDX, outDY);\n if (inLen === 0 || outLen === 0) return false;\n // For collinear handles on opposite sides of the anchor, the cross product\n // of the two handle vectors should be zero. Normalize by both magnitudes.\n const cross = (inDX * outDY - inDY * outDX) / (inLen * outLen);\n return Math.abs(cross) < SMOOTH_THRESHOLD;\n}\n\n/** How many anchors a path has. Matches the per-anchor arrays the renderer\n * expects for `vertexColors`. */\nexport function countPathAnchors(path: Path): number {\n if (path.kind === 'rect') return 4;\n const cmds = path.commands;\n let n = 0;\n for (let i = 0; i < cmds.length; i++) {\n const c = cmds[i];\n if (c === PATH_M || c === PATH_L || c === PATH_C || c === PATH_Q) n++;\n }\n return n;\n}\n\n/** The segment `(sub, segIdx)` as its two end anchors, or null when it\n * doesn't exist. `segIdx === sub.length - 1` names a closed subpath's\n * closing segment, which runs from the last anchor back to the first. */\nexport function segmentEnds(\n anchors: readonly PenAnchor[][],\n closed: readonly boolean[] | undefined,\n sub: number,\n segIdx: number,\n): { a: PenAnchor; b: PenAnchor } | null {\n const s = anchors[sub];\n if (!s || segIdx < 0) return null;\n if (segIdx + 1 < s.length) return { a: s[segIdx], b: s[segIdx + 1] };\n if (closed?.[sub] && s.length > 1 && segIdx === s.length - 1) return { a: s[segIdx], b: s[0] };\n return null;\n}\n\n/** Whether the segment from `a` to `b` is a straight line (no handles). */\nexport function isStraightSegment(a: PenAnchor, b: PenAnchor): boolean {\n return a.outHandle == null && b.inHandle == null;\n}\n\n/** Nearest parameter on one segment to `(x, y)`, with its squared distance.\n * A straight segment is parameterized by arc length (`t` is the fraction of\n * the way from `a` to `b`), matching how {@link insertAnchorOnSegment}\n * splits one. */\nfunction nearestOnSegment(a: PenAnchor, b: PenAnchor, x: number, y: number): { t: number; d2: number } {\n const p1 = a.outHandle ?? a;\n const p2 = b.inHandle ?? b;\n const hit = isStraightSegment(a, b)\n ? nearestOnLine(x, y, a.x, a.y, b.x, b.y)\n : nearestOnCubic(x, y, a.x, a.y, p1.x, p1.y, p2.x, p2.y, b.x, b.y);\n return { t: hit.t, d2: hit.dist * hit.dist };\n}\n\n/** The segment of a decoded path nearest `(wx, wy)`, and the parameter `t`\n * on it closest to that point. Pass `closed` (from `pathToAnchors`) to\n * include each closed subpath's closing segment, reported as\n * `segIdx === sub.length - 1`. `t` spans `[0, 1]`, endpoints included.\n * Returns `null` only when there are no segments. */\nexport function nearestSegmentT(\n anchors: PenAnchor[][],\n wx: number,\n wy: number,\n closed?: readonly boolean[],\n): { sub: number; segIdx: number; t: number } | null {\n let bestD2 = Infinity;\n let best: { sub: number; segIdx: number; t: number } | null = null;\n for (let s = 0; s < anchors.length; s++) {\n const segCount = anchors[s].length - (closed?.[s] ? 0 : 1);\n for (let i = 0; i < segCount; i++) {\n const ends = segmentEnds(anchors, closed, s, i);\n if (!ends) continue;\n const hit = nearestOnSegment(ends.a, ends.b, wx, wy);\n if (hit.d2 < bestD2) {\n bestD2 = hit.d2;\n best = { sub: s, segIdx: i, t: hit.t };\n }\n }\n }\n return best;\n}\n"]}
|