@weasel-js/geom 1.15.1 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/3d/index.d.ts +16 -2
  2. package/dist/3d/index.js +17 -10
  3. package/dist/3d/index.js.map +1 -1
  4. package/dist/booleans/index.js +5 -5
  5. package/dist/{chunk-B3D6KWW3.js → chunk-346XYYMX.js} +154 -3
  6. package/dist/chunk-346XYYMX.js.map +1 -0
  7. package/dist/{chunk-FROQPADA.js → chunk-5W425HJA.js} +82 -16
  8. package/dist/chunk-5W425HJA.js.map +1 -0
  9. package/dist/chunk-DNX24KBT.js +82 -0
  10. package/dist/chunk-DNX24KBT.js.map +1 -0
  11. package/dist/chunk-FOXMA54X.js +266 -0
  12. package/dist/chunk-FOXMA54X.js.map +1 -0
  13. package/dist/{chunk-3HQJGI63.js → chunk-HTT5AQIU.js} +3 -4
  14. package/dist/chunk-HTT5AQIU.js.map +1 -0
  15. package/dist/chunk-IOD2NFHQ.js +212 -0
  16. package/dist/chunk-IOD2NFHQ.js.map +1 -0
  17. package/dist/{chunk-HNLJO3E7.js → chunk-MOCSUHEI.js} +22 -8
  18. package/dist/chunk-MOCSUHEI.js.map +1 -0
  19. package/dist/{chunk-3YH5XU3H.js → chunk-OBPXEPUH.js} +14 -23
  20. package/dist/chunk-OBPXEPUH.js.map +1 -0
  21. package/dist/chunk-QSVZO7QZ.js +40 -0
  22. package/dist/chunk-QSVZO7QZ.js.map +1 -0
  23. package/dist/{chunk-EURWZUNY.js → chunk-TBS4NHGQ.js} +70 -93
  24. package/dist/chunk-TBS4NHGQ.js.map +1 -0
  25. package/dist/{chunk-HMGGJZTM.js → chunk-VJG5ZWCR.js} +7 -21
  26. package/dist/chunk-VJG5ZWCR.js.map +1 -0
  27. package/dist/{chunk-ZOMUTU7J.js → chunk-ZV24QZ6X.js} +5 -11
  28. package/dist/chunk-ZV24QZ6X.js.map +1 -0
  29. package/dist/{curvature-CDOI32hv.d.ts → curvature-DKPO4Qeh.d.ts} +0 -27
  30. package/dist/curves/index.d.ts +1 -1
  31. package/dist/curves/index.js +4 -4
  32. package/dist/index.d.ts +193 -13
  33. package/dist/index.js +409 -273
  34. package/dist/index.js.map +1 -1
  35. package/dist/lengthTable-Cv3W6lsH.d.ts +64 -0
  36. package/dist/nd/index.d.ts +28 -1
  37. package/dist/nd/index.js +2 -5
  38. package/dist/nd/index.js.map +1 -1
  39. package/dist/tessellate/index.d.ts +23 -1
  40. package/dist/tessellate/index.js +31 -17
  41. package/dist/tessellate/index.js.map +1 -1
  42. package/dist/warp/index.js +9 -15
  43. package/dist/warp/index.js.map +1 -1
  44. package/package.json +2 -2
  45. package/dist/chunk-2N6OWA7E.js +0 -104
  46. package/dist/chunk-2N6OWA7E.js.map +0 -1
  47. package/dist/chunk-3HQJGI63.js.map +0 -1
  48. package/dist/chunk-3YH5XU3H.js.map +0 -1
  49. package/dist/chunk-755Y4YZS.js +0 -32
  50. package/dist/chunk-755Y4YZS.js.map +0 -1
  51. package/dist/chunk-B3D6KWW3.js.map +0 -1
  52. package/dist/chunk-EURWZUNY.js.map +0 -1
  53. package/dist/chunk-FROQPADA.js.map +0 -1
  54. package/dist/chunk-HMGGJZTM.js.map +0 -1
  55. package/dist/chunk-HNLJO3E7.js.map +0 -1
  56. package/dist/chunk-ZOMUTU7J.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,29 +1,50 @@
1
- import { carryCorners, parseSubpaths, intersectSegments, evalSeg, keepCorners, anchorRange, subSeg, writePath, derivSeg } from './chunk-3YH5XU3H.js';
2
- export { CORNER_RESTORE_TOLERANCE, cornerTraces, intersectSegments, keepCorners, restoreCorners } from './chunk-3YH5XU3H.js';
1
+ import { carryCorners, parseSubpaths, intersectSegments, evalSeg, keepCorners, anchorRange, subSeg, writePath, derivSeg } from './chunk-OBPXEPUH.js';
2
+ export { CORNER_RESTORE_TOLERANCE, cornerTraces, intersectSegments, keepCorners, restoreCorners } from './chunk-OBPXEPUH.js';
3
+ import { lengthTable, closingAnchor, tableInternals, segmentArc, pointOnSegment } from './chunk-FOXMA54X.js';
4
+ export { anchorAtLength, closingAnchor, lengthAtAnchor, lengthAtSegmentFraction, lengthAtSubpath, lengthTable, pointAtLength } from './chunk-FOXMA54X.js';
3
5
  import { createBvh } from './chunk-XXNK4N7Z.js';
4
6
  import { PORT_REACH, portControls, portCurvePoints } from './chunk-YQADYJJ7.js';
5
7
  export { PORT_REACH } from './chunk-YQADYJJ7.js';
6
- import { eulerAngleAt, eulerSegmentCubic, eulerCubicError, integrateEuler, eulerPointAt, eulerCurvatureAt, cubicDerivativeAt, cubicCurvatureAt } from './chunk-HNLJO3E7.js';
7
- export { SPIRO_KINDS, combTeeth, cubicCurvatureAt, cubicCurvatureSource, cubicDerivativeAt, curvatureComb, eulerAngleAt, eulerCurvatureAt, eulerCurvatureDerivativesAt, eulerPointAt, fitEulerSegment, isSpiroKind, placeEulerSegment, solveSpiro, spiroContoursFromControl, spiroContoursOf, spiroControlPath, spiroToPath } from './chunk-HNLJO3E7.js';
8
- import { extractPolylines } from './chunk-FROQPADA.js';
9
- import { boundsOfPath, fitPointRun, keepCorner } from './chunk-ZOMUTU7J.js';
10
- export { boundsOfPath } from './chunk-ZOMUTU7J.js';
11
- import { countPathAnchors, pathToAnchors, anchorsToPath, isAnchorSmooth, segmentEnds, isStraightSegment, nearestSegmentT } from './chunk-3HQJGI63.js';
12
- export { anchorsToPath, countPathAnchors, isAnchorSmooth, isStraightSegment, nearestSegmentT, pathToAnchors, segmentEnds } from './chunk-3HQJGI63.js';
13
- import { nearestOnLine, nearestOnPath, nearestOnCubic, nearestOnQuadratic } from './chunk-2N6OWA7E.js';
14
- export { nearestOnCubic, nearestOnLine, nearestOnPath, nearestOnQuadratic } from './chunk-2N6OWA7E.js';
15
- import { resolveCorners, pointInPolygon, cornerCuts, fillsOpenSubpaths, schneiderFit, fitCubicWithTangents, flattenCubic, pointSegmentDist2, flattenQuadratic, DEFAULT_FLATTEN_TOLERANCE, segmentsCross, tangentAt } from './chunk-EURWZUNY.js';
16
- export { CORNER_FIT_TOLERANCE, CORNER_KEYWORDS, DEFAULT_FLATTEN_TOLERANCE, cornerCenter, cornerCuts, cornerFrameAt, cornerFrames, cornerKeywordOf, cornerShapeK, cornerShapePieces, cornerShapePoint, defaultOpenFill, fillsOpenSubpaths, fitCubicWithTangents, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, isCornerShape, nextCornerShape, pointInPolygon, pointSegmentDist2, remapPerAnchor, resolveCorners, resolveCornersWithSources, schneiderFit, segmentsCross, setDefaultOpenFill } from './chunk-EURWZUNY.js';
17
- import { SINGULAR_RATIO, clamp } from './chunk-755Y4YZS.js';
18
- export { EPS, approxEq, clamp, cross, dot, len2, sign, sub } from './chunk-755Y4YZS.js';
19
- import { worldCoords, PATH_M, PATH_L, PATH_C, PATH_Q, PATH_Z, PathBuilder, withCorner, scaleCorners, originFor, coordsFrom, forEachSegment, pathCommandCoordCount, withoutCorners, elevateQuadraticToCubic, cubicEvalAt, cubicPointAt, splitCubicAtT, cubicAxisExtremaTs, rectToContour, splitCubicAt } from './chunk-B3D6KWW3.js';
20
- export { ORIGIN_THRESHOLD, PATH_C, PATH_CMD_LENGTHS, PATH_COMMANDS, PATH_L, PATH_M, PATH_Q, PATH_Z, PathBuilder, addOrigin, asPolygonPath, boundsOfCoords, boxContainsBox, boxContainsPoint, compactCorners, coordsFrom, cornerAt, cornerSpecAt, cubicAxisExtremaTs, cubicBounds, cubicEvalAt, cubicPointAt, elevateQuadraticToCubic, ellipsePath, forEachSegment, hasCorners, joinPaths, lerpCorners, linePath, originFor, originOf, pathCommandCoordCount, pathPoint, polygonFromPoints, polylineFromPoints, quadraticEvalAt, rectPath, rectToContour, regularPolygonPath, scaleCorners, splitCubicAt, splitCubicAtT, splitLineAt, splitQuadraticAt, starPath, unionBox, withCorner, withoutCorners, worldCoords } from './chunk-B3D6KWW3.js';
8
+ import { wrapPositive, eulerAngleAt, eulerSegmentCubic, eulerCubicError, integrateEuler, eulerPointAt, eulerCurvatureAt, DEG_TO_RAD, RAD_TO_DEG, cubicDerivativeAt, cubicCurvatureAt } from './chunk-MOCSUHEI.js';
9
+ export { DEG_TO_RAD, RAD_TO_DEG, SPIRO_KINDS, combTeeth, cubicCurvatureAt, cubicCurvatureSource, cubicDerivativeAt, curvatureComb, eulerAngleAt, eulerCurvatureAt, eulerCurvatureDerivativesAt, eulerPointAt, fitEulerSegment, isSpiroKind, placeEulerSegment, solveSpiro, spiroContoursFromControl, spiroContoursOf, spiroControlPath, spiroToPath, wrapAngle, wrapPositive } from './chunk-MOCSUHEI.js';
10
+ import { extractPolylines, polylineAtCurve } from './chunk-5W425HJA.js';
11
+ import './chunk-QSVZO7QZ.js';
12
+ import { boundsOfPath, fitPointRun, keepCorner } from './chunk-ZV24QZ6X.js';
13
+ export { boundsOfPath } from './chunk-ZV24QZ6X.js';
14
+ import { countPathAnchors, pathToAnchors, anchorsToPath, isAnchorSmooth, segmentEnds, isStraightSegment, nearestSegmentT } from './chunk-HTT5AQIU.js';
15
+ export { anchorsToPath, countPathAnchors, isAnchorSmooth, isStraightSegment, nearestSegmentT, pathToAnchors, segmentEnds } from './chunk-HTT5AQIU.js';
16
+ import { resolveCornersWithSources, resolveCorners, pointInPolygon, cornerCuts, fillsOpenSubpaths, schneiderFit, fitCubicWithTangents, flattenCubic, DEFAULT_FLATTEN_TOLERANCE, segmentsCross, flattenQuadratic, tangentAt } from './chunk-TBS4NHGQ.js';
17
+ export { CORNER_FIT_TOLERANCE, CORNER_KEYWORDS, DEFAULT_FLATTEN_TOLERANCE, cornerCenter, cornerCuts, cornerFrameAt, cornerFrames, cornerKeywordOf, cornerShapeK, cornerShapePieces, cornerShapePoint, defaultOpenFill, fillsOpenSubpaths, fitCubicWithTangents, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, isCornerShape, nextCornerShape, pointInPolygon, remapPerAnchor, resolveCorners, resolveCornersWithSources, ringSignedArea, schneiderFit, segmentsCross, setDefaultOpenFill } from './chunk-TBS4NHGQ.js';
18
+ import { lookupLength, lookupT, flatPoints, segmentLength, segmentTAtLength } from './chunk-IOD2NFHQ.js';
19
+ export { segmentLength, segmentTAtLength } from './chunk-IOD2NFHQ.js';
20
+ import { nearestOnLine, forEachSegment, PATH_M, PATH_L, PATH_Q, PATH_C, worldCoords, PATH_Z, PathBuilder, withCorner, scaleCorners, originFor, coordsFrom, pathCommandCoordCount, withoutCorners, elevateQuadraticToCubic, cubicEvalAt, nearestOnPath, cubicPointAt, splitCubicAtT, nearestOnCubic, cubicAxisExtremaTs, rectToContour, pointSegmentDist2, lerpPoint, distance, originOf, splitCubicAt, nearestOnQuadratic, boxesIntersect } from './chunk-346XYYMX.js';
21
+ export { ORIGIN_THRESHOLD, PATH_C, PATH_CMD_LENGTHS, PATH_COMMANDS, PATH_L, PATH_M, PATH_Q, PATH_Z, PathBuilder, addOrigin, asPolygonPath, boundsOfCoords, boxContainsBox, boxContainsPoint, boxesIntersect, compactCorners, coordsFrom, cornerAt, cornerSpecAt, cubicAxisExtremaTs, cubicBounds, cubicEvalAt, cubicPointAt, distance, distance2, elevateQuadraticToCubic, ellipsePath, forEachSegment, hasCorners, joinPaths, lerpCorners, lerpPoint, linePath, nearestOnCubic, nearestOnLine, nearestOnPath, nearestOnQuadratic, originFor, originOf, pathCommandCoordCount, pathPoint, pointSegmentDist, pointSegmentDist2, polygonFromPoints, polylineFromPoints, quadraticEvalAt, rectContainsPoint, rectContainsPointHalfOpen, rectPath, rectToContour, rectsIntersect, rectsOverlap, regularPolygonPath, scaleCorners, segmentT, splitCubicAt, splitCubicAtT, splitLineAt, splitQuadraticAt, starPath, unionBox, unionRects, withCorner, withoutCorners, worldCoords } from './chunk-346XYYMX.js';
22
+ import { SINGULAR_RATIO, clamp } from './chunk-DNX24KBT.js';
23
+ export { EPS, approxEq, clamp, clamp01, cross, dot, len2, lerp, sign, sub } from './chunk-DNX24KBT.js';
21
24
 
22
25
  // src/view.ts
23
26
  var ZOOM_FLOOR = 1e-9;
24
27
  function normalizeZoom(zoom) {
25
28
  return zoom >= ZOOM_FLOOR && zoom < Infinity ? zoom : ZOOM_FLOOR;
26
29
  }
30
+ function viewWorldToScreen(view, p) {
31
+ return { x: (p.x - view.x) * view.scale.x, y: (p.y - view.y) * view.scale.y };
32
+ }
33
+ function viewScreenToWorld(view, p) {
34
+ return { x: p.x / view.scale.x + view.x, y: p.y / view.scale.y + view.y };
35
+ }
36
+ function viewRectToScreen(view, r) {
37
+ const x0 = (r.x - view.x) * view.scale.x;
38
+ const y0 = (r.y - view.y) * view.scale.y;
39
+ const w = r.width * view.scale.x;
40
+ const h = r.height * view.scale.y;
41
+ return {
42
+ x: w < 0 ? x0 + w : x0,
43
+ y: h < 0 ? y0 + h : y0,
44
+ width: Math.abs(w),
45
+ height: Math.abs(h)
46
+ };
47
+ }
27
48
 
28
49
  // src/affine.ts
29
50
  function identity() {
@@ -87,12 +108,12 @@ function transformCoords(coords, m) {
87
108
  function lineShape(from, to) {
88
109
  const dx2 = to.x - from.x;
89
110
  const dy2 = to.y - from.y;
90
- const length2 = Math.hypot(dx2, dy2);
91
- const tangent = length2 === 0 ? { x: 1, y: 0 } : { x: dx2 / length2, y: dy2 / length2 };
111
+ const length = Math.hypot(dx2, dy2);
112
+ const tangent = length === 0 ? { x: 1, y: 0 } : { x: dx2 / length, y: dy2 / length };
92
113
  const normal2 = { x: tangent.y, y: -tangent.x };
93
114
  return {
94
115
  kind: "line",
95
- length: length2,
116
+ length,
96
117
  closed: false,
97
118
  at(f, options) {
98
119
  const t = options?.open ? f : clamp(f, 0, 1);
@@ -104,25 +125,23 @@ function lineShape(from, to) {
104
125
  },
105
126
  nearest(p, options) {
106
127
  const n = nearestOnLine(p.x, p.y, from.x, from.y, to.x, to.y);
107
- if (!options?.open || length2 === 0) return { f: n.t, dist: n.dist };
108
- const f = ((p.x - from.x) * dx2 + (p.y - from.y) * dy2) / (length2 * length2);
128
+ if (!options?.open || length === 0) return { f: n.t, dist: n.dist };
129
+ const f = ((p.x - from.x) * dx2 + (p.y - from.y) * dy2) / (length * length);
109
130
  return { f, dist: n.dist };
110
131
  }
111
132
  };
112
133
  }
113
134
 
114
135
  // src/track/arc.ts
115
- var RAD = Math.PI / 180;
116
- var mod360 = (a) => (a % 360 + 360) % 360;
117
136
  function arcShape(spec) {
118
137
  const { center, radius, start } = spec;
119
138
  const sign2 = spec.direction === "ccw" ? -1 : 1;
120
- const raw = mod360(sign2 * (spec.end - start));
139
+ const raw = wrapPositive(sign2 * (spec.end - start), 360);
121
140
  const sweep = raw === 0 ? 360 : raw;
122
- const length2 = radius * sweep * RAD;
141
+ const length = radius * sweep * DEG_TO_RAD;
123
142
  const at2 = (f, options) => {
124
143
  const t = options?.open ? f : clamp(f, 0, 1);
125
- const theta = (start + sign2 * t * sweep) * RAD;
144
+ const theta = (start + sign2 * t * sweep) * DEG_TO_RAD;
126
145
  const s = Math.sin(theta);
127
146
  const c = Math.cos(theta);
128
147
  return {
@@ -133,14 +152,14 @@ function arcShape(spec) {
133
152
  };
134
153
  const shape = {
135
154
  kind: "arc",
136
- length: length2,
155
+ length,
137
156
  closed: raw === 0,
138
157
  at: at2,
139
158
  nearest(p, options) {
140
159
  const dx2 = p.x - center.x;
141
160
  const dy2 = p.y - center.y;
142
- const angle = dx2 === 0 && dy2 === 0 ? 0 : Math.atan2(dx2, -dy2) / RAD;
143
- const offset2 = mod360(sign2 * (angle - start));
161
+ const angle = dx2 === 0 && dy2 === 0 ? 0 : Math.atan2(dx2, -dy2) / DEG_TO_RAD;
162
+ const offset2 = wrapPositive(sign2 * (angle - start), 360);
144
163
  let f;
145
164
  if (offset2 <= sweep) f = offset2 / sweep;
146
165
  else f = 360 - offset2 < offset2 - sweep ? 0 : 1;
@@ -148,7 +167,7 @@ function arcShape(spec) {
148
167
  const dist = Math.hypot(p.x - q.x, p.y - q.y);
149
168
  if (!options?.open) return { f, dist };
150
169
  const mid = (options.about ?? 0.5) * sweep;
151
- const unwrapped = mid + mod360(offset2 - mid + 180) - 180;
170
+ const unwrapped = mid + wrapPositive(offset2 - mid + 180, 360) - 180;
152
171
  return { f: unwrapped / sweep, dist };
153
172
  }
154
173
  };
@@ -157,20 +176,18 @@ function arcShape(spec) {
157
176
  }
158
177
 
159
178
  // src/track/ellipse.ts
160
- var RAD2 = Math.PI / 180;
161
- var mod3602 = (a) => (a % 360 + 360) % 360;
162
179
  var PIECES = 128;
163
180
  var PANELS = 4;
164
181
  var RING = 256;
165
182
  var pointAt = ({ center, a, b, t0, sign: sign2 }, u) => {
166
- const t = (t0 + sign2 * u) * RAD2;
183
+ const t = (t0 + sign2 * u) * DEG_TO_RAD;
167
184
  return { x: center.x + a * Math.sin(t), y: center.y - b * Math.cos(t) };
168
185
  };
169
186
  var speed = ({ a, b, t0, sign: sign2 }, u) => {
170
- const t = (t0 + sign2 * u) * RAD2;
171
- return Math.hypot(a * Math.cos(t), b * Math.sin(t)) * RAD2;
187
+ const t = (t0 + sign2 * u) * DEG_TO_RAD;
188
+ return Math.hypot(a * Math.cos(t), b * Math.sin(t)) * DEG_TO_RAD;
172
189
  };
173
- function lengthTable(e, sweep) {
190
+ function lengthTable2(e, sweep) {
174
191
  const simpson = (u0, u1) => {
175
192
  const h = (u1 - u0) / PANELS;
176
193
  let sum = speed(e, u0) + speed(e, u1);
@@ -180,7 +197,7 @@ function lengthTable(e, sweep) {
180
197
  const du = sweep / PIECES;
181
198
  const table = [0];
182
199
  for (let i = 0; i < PIECES; i++) table.push(table[i] + simpson(i * du, (i + 1) * du));
183
- const length2 = table[PIECES];
200
+ const length = table[PIECES];
184
201
  const lengthTo = (u) => {
185
202
  if (u >= 0 && u <= sweep) {
186
203
  const i = Math.min(PIECES - 1, Math.floor(u / du));
@@ -192,8 +209,8 @@ function lengthTable(e, sweep) {
192
209
  return s;
193
210
  };
194
211
  const offsetAt = (f) => {
195
- if (length2 === 0) return f * sweep;
196
- const s = f * length2;
212
+ if (length === 0) return f * sweep;
213
+ const s = f * length;
197
214
  let lo = 0;
198
215
  let hi = PIECES;
199
216
  while (hi - lo > 1) {
@@ -210,7 +227,7 @@ function lengthTable(e, sweep) {
210
227
  }
211
228
  return u;
212
229
  };
213
- return { length: length2, lengthTo, offsetAt };
230
+ return { length, lengthTo, offsetAt };
214
231
  }
215
232
  var dist2 = (e, p, u) => {
216
233
  const q = pointAt(e, u);
@@ -230,13 +247,13 @@ function footBetween(e, p, lo, hi, n, late) {
230
247
  const dy2 = p.y - e.center.y;
231
248
  let u = best;
232
249
  for (let i = 0; i < 12; i++) {
233
- const t = (t0 + sign2 * u) * RAD2;
250
+ const t = (t0 + sign2 * u) * DEG_TO_RAD;
234
251
  const s = Math.sin(t);
235
252
  const c = Math.cos(t);
236
253
  const g = (a * a - b * b) * s * c - dx2 * a * c - dy2 * b * s;
237
254
  const g1 = (a * a - b * b) * (c * c - s * s) + dx2 * a * s - dy2 * b * c;
238
255
  if (!(g1 > 0)) break;
239
- const next = clamp(u - sign2 * g / g1 / RAD2, Math.max(lo, best - step), Math.min(hi, best + step));
256
+ const next = clamp(u - sign2 * g / g1 / DEG_TO_RAD, Math.max(lo, best - step), Math.min(hi, best + step));
240
257
  if (Math.abs(next - u) < 1e-12) break;
241
258
  u = next;
242
259
  }
@@ -245,24 +262,24 @@ function footBetween(e, p, lo, hi, n, late) {
245
262
  function ellipseShape(spec) {
246
263
  const { center, radiusX: a, radiusY: b } = spec;
247
264
  const sign2 = spec.direction === "ccw" ? -1 : 1;
248
- const param = (seenDeg) => Math.atan2(b * Math.sin(seenDeg * RAD2), a * Math.cos(seenDeg * RAD2)) / RAD2;
265
+ const param = (seenDeg) => Math.atan2(b * Math.sin(seenDeg * DEG_TO_RAD), a * Math.cos(seenDeg * DEG_TO_RAD)) / DEG_TO_RAD;
249
266
  const e = { center, a, b, t0: param(spec.start), sign: sign2 };
250
- const raw = mod3602(sign2 * (param(spec.end) - e.t0));
267
+ const raw = wrapPositive(sign2 * (param(spec.end) - e.t0), 360);
251
268
  const sweep = raw === 0 ? 360 : raw;
252
- const { length: length2, lengthTo, offsetAt } = lengthTable(e, sweep);
269
+ const { length, lengthTo, offsetAt } = lengthTable2(e, sweep);
253
270
  const offsetPast = (f) => {
254
271
  if (raw === 0) return offsetAt((f % 1 + 1) % 1);
255
272
  let u = f * sweep;
256
273
  for (let i = 0; i < 8; i++) {
257
274
  const v = speed(e, u);
258
275
  if (!(v > 0)) break;
259
- u -= (lengthTo(u) - f * length2) / v;
276
+ u -= (lengthTo(u) - f * length) / v;
260
277
  }
261
278
  return u;
262
279
  };
263
280
  const at2 = (f, options) => {
264
281
  const u = options?.open && (f < 0 || f > 1) ? offsetPast(f) : offsetAt(clamp(f, 0, 1));
265
- const t = (e.t0 + sign2 * u) * RAD2;
282
+ const t = (e.t0 + sign2 * u) * DEG_TO_RAD;
266
283
  const s = Math.sin(t);
267
284
  const c = Math.cos(t);
268
285
  const k = Math.hypot(a * c, b * s);
@@ -272,56 +289,94 @@ function ellipseShape(spec) {
272
289
  };
273
290
  const shape = {
274
291
  kind: "ellipse",
275
- length: length2,
292
+ length,
276
293
  closed: raw === 0,
277
294
  at: at2,
278
295
  nearest(p, options) {
279
296
  const u = footBetween(e, p, 0, sweep, PIECES, true);
280
- const f = length2 === 0 ? 0 : clamp(lengthTo(u) / length2, 0, 1);
297
+ const f = length === 0 ? 0 : clamp(lengthTo(u) / length, 0, 1);
281
298
  const dist = Math.sqrt(dist2(e, p, u));
282
- if (!options?.open || length2 === 0) return { f, dist };
299
+ if (!options?.open || length === 0) return { f, dist };
283
300
  const mid = offsetAt(options.about ?? 0.5);
284
- return { f: lengthTo(footBetween(e, p, mid - 180, mid + 180, RING, false)) / length2, dist };
301
+ return { f: lengthTo(footBetween(e, p, mid - 180, mid + 180, RING, false)) / length, dist };
285
302
  }
286
303
  };
287
304
  Object.defineProperty(shape, "ellipse", { value: spec, enumerable: false });
288
305
  return shape;
289
306
  }
290
307
 
308
+ // src/along/pathTable.ts
309
+ var tables = /* @__PURE__ */ new WeakMap();
310
+ function pathLengthTable(path) {
311
+ const hit = tables.get(path);
312
+ if (hit) return hit;
313
+ let table;
314
+ if (path.kind === "rect") {
315
+ table = lengthTable(extractPolylines(path), 2);
316
+ } else {
317
+ const { path: resolved, anchorSources } = resolveCornersWithSources(path);
318
+ const controls2 = controlsByAnchor(resolved);
319
+ table = lengthTable(extractPolylines(resolved).map((pl) => anchorRun(pl, controls2)), 2, anchorSources);
320
+ }
321
+ tables.set(path, table);
322
+ return table;
323
+ }
324
+ function controlsByAnchor(path) {
325
+ const out = [];
326
+ const { commands, coords } = path;
327
+ forEachSegment(commands, coords, (cmd, ci) => {
328
+ if (cmd === PATH_M || cmd === PATH_L) out.push(null);
329
+ else if (cmd === PATH_Q) out.push([coords[ci], coords[ci + 1]]);
330
+ else if (cmd === PATH_C) out.push([coords[ci], coords[ci + 1], coords[ci + 2], coords[ci + 3]]);
331
+ });
332
+ return out;
333
+ }
334
+ function anchorRun(pl, controls2) {
335
+ const n = pl.points.length / 2;
336
+ const { anchorA: A, anchorB: B } = pl;
337
+ const points = [];
338
+ const ids = [];
339
+ for (let i = 0; i < n; i++) {
340
+ if (A && B && A[i] !== B[i]) continue;
341
+ points.push(pl.points[i * 2], pl.points[i * 2 + 1]);
342
+ ids.push(A ? A[i] : i);
343
+ }
344
+ const segControls = ids.slice(1).map((id) => controls2[id] ?? null);
345
+ const close = closingAnchor(pl);
346
+ if (pl.closed && n >= 2) segControls.push(close === void 0 ? null : controls2[close] ?? null);
347
+ const anchors = Uint32Array.from(ids);
348
+ return {
349
+ points,
350
+ closed: pl.closed,
351
+ anchorA: anchors,
352
+ anchorB: anchors.slice(),
353
+ anchorT: new Float32Array(ids.length),
354
+ ...close !== void 0 ? { closeAnchor: close } : {},
355
+ controls: segControls
356
+ };
357
+ }
358
+
291
359
  // src/paths/pathMeasure.ts
292
360
  function measurePath(path, opts = {}) {
293
361
  const polylines = extractPolylines(
294
362
  path,
295
363
  opts.flattenTolerance === void 0 ? {} : { flattenTolerance: opts.flattenTolerance }
296
364
  );
365
+ const table = pathLengthTable(path);
366
+ const cornerTolerance = opts.keepCorners ? opts.flattenTolerance ?? 0.5 : null;
297
367
  const segments = [];
298
- let total = 0;
299
368
  let first;
300
369
  let contours = 0;
301
370
  let lastClosed = false;
302
- for (const pl of polylines) {
303
- const count = pl.points.length / 2;
304
- if (count === 0) continue;
371
+ polylines.forEach((pl, r) => {
372
+ if (pl.points.length === 0) return;
305
373
  contours++;
306
374
  lastClosed = pl.closed;
307
375
  first ??= { x: pl.points[0], y: pl.points[1] };
308
376
  const startIndex = segments.length;
309
- const last = pl.closed ? count : count - 1;
310
- for (let i = 0; i < last; i++) {
311
- const j = (i + 1) % count;
312
- const ax = pl.points[i * 2];
313
- const ay = pl.points[i * 2 + 1];
314
- const bx = pl.points[j * 2];
315
- const by = pl.points[j * 2 + 1];
316
- const len = Math.hypot(bx - ax, by - ay);
317
- if (len === 0) continue;
318
- const tx = (bx - ax) / len;
319
- const ty = (by - ay) / len;
320
- segments.push({ ax, ay, bx, by, start: total, len, tx, ty, t0x: tx, t0y: ty, t1x: tx, t1y: ty });
321
- total += len;
322
- }
323
- smoothVertexHeadings(segments, startIndex, pl.closed, opts.keepCorners ? opts.flattenTolerance ?? 0.5 : null);
324
- }
377
+ pushChords(pl, r, table, segments);
378
+ smoothVertexHeadings(segments, startIndex, pl.closed, cornerTolerance);
379
+ });
325
380
  if (first === void 0) return null;
326
381
  const origin = first;
327
382
  const closed = contours === 1 && lastClosed;
@@ -338,15 +393,20 @@ function measurePath(path, opts = {}) {
338
393
  })
339
394
  };
340
395
  }
396
+ const total = table.total;
397
+ const pointAt2 = (seg, s) => {
398
+ const { point } = pointOnSegment(table, seg.run, seg.seg, s);
399
+ return { x: point[0], y: point[1] };
400
+ };
341
401
  return {
342
402
  length: total,
343
403
  closed,
344
404
  at(s, smooth = false) {
345
405
  const target = Math.min(Math.max(s, 0), total);
346
406
  const seg = segments[segmentAt(segments, target)];
347
- const u = Math.min(Math.max((target - seg.start) / seg.len, 0), 1);
348
- const point = { x: seg.ax + (seg.bx - seg.ax) * u, y: seg.ay + (seg.by - seg.ay) * u };
407
+ const point = pointAt2(seg, target);
349
408
  if (!smooth) return { point, tangent: { x: seg.tx, y: seg.ty } };
409
+ const u = seg.arc > 0 ? Math.min(Math.max((target - seg.start) / seg.arc, 0), 1) : 0;
350
410
  const x = seg.t0x + (seg.t1x - seg.t0x) * u;
351
411
  const y = seg.t0y + (seg.t1y - seg.t0y) * u;
352
412
  const l = Math.hypot(x, y);
@@ -365,15 +425,58 @@ function measurePath(path, opts = {}) {
365
425
  bestDist = d;
366
426
  }
367
427
  }
368
- return {
369
- s: best.start + best.len * bestU,
370
- dist: bestDist,
371
- point: { x: best.ax + (best.bx - best.ax) * bestU, y: best.ay + (best.by - best.ay) * bestU },
372
- tangent: { x: best.tx, y: best.ty }
373
- };
428
+ const s = best.start + best.arc * bestU;
429
+ const point = pointAt2(best, s);
430
+ return { s, dist: Math.hypot(p.x - point.x, p.y - point.y), point, tangent: { x: best.tx, y: best.ty } };
374
431
  }
375
432
  };
376
433
  }
434
+ function pushChords(pl, r, table, segments) {
435
+ const c = tableInternals(table).cum[r];
436
+ const { points, anchorA: A, anchorB: B, anchorT: T } = pl;
437
+ const count = points.length / 2;
438
+ const segOf = new Int32Array(count);
439
+ const lenOf = new Float64Array(count);
440
+ let k = -1;
441
+ for (let i = 0; i < count; i++) {
442
+ const aligned = !A || !B || A[i] === B[i];
443
+ if (aligned) k++;
444
+ const at2 = Math.max(k, 0);
445
+ segOf[i] = Math.min(at2, c.length - 2);
446
+ lenOf[i] = aligned || !T ? c[at2] : c[at2] + T[i] * (c[at2 + 1] - c[at2]);
447
+ }
448
+ const last = pl.closed ? count : count - 1;
449
+ for (let i = 0; i < last; i++) {
450
+ const j = (i + 1) % count;
451
+ const ax = points[i * 2];
452
+ const ay = points[i * 2 + 1];
453
+ const bx = points[j * 2];
454
+ const by = points[j * 2 + 1];
455
+ const len = Math.hypot(bx - ax, by - ay);
456
+ if (len === 0) continue;
457
+ const tx = (bx - ax) / len;
458
+ const ty = (by - ay) / len;
459
+ const start = lenOf[i];
460
+ const arc2 = (j === 0 ? c[c.length - 1] : lenOf[j]) - start;
461
+ segments.push({
462
+ ax,
463
+ ay,
464
+ bx,
465
+ by,
466
+ len,
467
+ start,
468
+ arc: arc2,
469
+ run: r,
470
+ seg: segOf[i],
471
+ tx,
472
+ ty,
473
+ t0x: tx,
474
+ t0y: ty,
475
+ t1x: tx,
476
+ t1y: ty
477
+ });
478
+ }
479
+ }
377
480
  function smoothVertexHeadings(segments, from, closed, cornerTolerance) {
378
481
  const n = segments.length - from;
379
482
  if (n < 2) return;
@@ -413,22 +516,22 @@ function segmentAt(segments, s) {
413
516
  function pathShape(path, options = {}) {
414
517
  const measure2 = measurePath(path, { ...options, keepCorners: true });
415
518
  if (!measure2) return null;
416
- const { length: length2, closed } = measure2;
519
+ const { length, closed } = measure2;
417
520
  const shape = {
418
521
  kind: "path",
419
- length: length2,
522
+ length,
420
523
  closed,
421
524
  at(f, options2) {
422
525
  const open = options2?.open === true && (f < 0 || f > 1);
423
526
  const g = open && closed ? (f % 1 + 1) % 1 : clamp(f, 0, 1);
424
- const { point, tangent } = measure2.at(g * length2, true);
425
- const past = open && !closed ? (f - g) * length2 : 0;
527
+ const { point, tangent } = measure2.at(g * length, true);
528
+ const past = open && !closed ? (f - g) * length : 0;
426
529
  return { point: { x: point.x + past * tangent.x, y: point.y + past * tangent.y }, tangent, normal: { x: tangent.y, y: -tangent.x } };
427
530
  },
428
531
  nearest(p, opts) {
429
532
  const n = measure2.nearest(p);
430
- const f = length2 === 0 ? 0 : n.s / length2;
431
- if (!opts?.open || !closed || length2 === 0) return { f, dist: n.dist };
533
+ const f = length === 0 ? 0 : n.s / length;
534
+ if (!opts?.open || !closed || length === 0) return { f, dist: n.dist };
432
535
  const about = opts.about ?? 0.5;
433
536
  const unwrapped = [f - 1, f + 1].reduce((best, g) => Math.abs(g - about) < Math.abs(best - about) ? g : best, f);
434
537
  return { f: unwrapped, dist: n.dist };
@@ -456,7 +559,7 @@ function trackRun(shape, from = 0, to = 1, options = {}) {
456
559
  const at2 = (g, options2) => {
457
560
  const s = options2?.open ? shape.at(toShape(g), options2) : shape.at(toShape(clamp(g, 0, 1)));
458
561
  const dir = reading(s.normal);
459
- const angle = Math.atan2(dir.y, dir.x) * 180 / Math.PI;
562
+ const angle = Math.atan2(dir.y, dir.x) * RAD_TO_DEG;
460
563
  const normal2 = { x: sgn * s.normal.x, y: sgn * s.normal.y };
461
564
  return {
462
565
  point: s.point,
@@ -556,7 +659,7 @@ function trackShapeD(shape, from = 0, to = 1) {
556
659
  const param = (p) => Math.atan2((p.x - center.x) / radiusX, (center.y - p.y) / radiusY);
557
660
  const sign2 = direction === "ccw" ? -1 : 1;
558
661
  const whole = (shape.ellipse.start - shape.ellipse.end) % 360 === 0 && Math.abs(to - from) >= 1 - 1e-12;
559
- const raw = (sign2 * Math.sign(to - from) * (param(b) - param(a)) + 4 * Math.PI) % (2 * Math.PI);
662
+ const raw = wrapPositive(sign2 * Math.sign(to - from) * (param(b) - param(a)));
560
663
  return arcsD(a, b, m, radiusX, radiusY, whole ? 2 * Math.PI : raw, direction === "ccw" === to < from ? 1 : 0);
561
664
  }
562
665
  let d = `M ${pt(a)}`;
@@ -591,15 +694,15 @@ function shiftTrack(shape, shift) {
591
694
  table[i] = table[i - 1] + Math.hypot(step.x, step.y);
592
695
  prev = next;
593
696
  }
594
- const length2 = table[SAMPLES2];
697
+ const length = table[SAMPLES2];
595
698
  const per = shape.length / SAMPLES2;
596
699
  const startRatio = table[1] > 0 ? per / table[1] : 1;
597
700
  const endRatio = table[SAMPLES2] - table[SAMPLES2 - 1] > 0 ? per / (table[SAMPLES2] - table[SAMPLES2 - 1]) : 1;
598
701
  const toBase = (f) => {
599
- const d = f * length2;
600
- if (length2 <= 0 || shape.length <= 0) return f;
702
+ const d = f * length;
703
+ if (length <= 0 || shape.length <= 0) return f;
601
704
  if (d < 0) return d * startRatio / shape.length;
602
- if (d > length2) return 1 + (d - length2) * endRatio / shape.length;
705
+ if (d > length) return 1 + (d - length) * endRatio / shape.length;
603
706
  let lo = 0;
604
707
  let hi = SAMPLES2;
605
708
  while (hi - lo > 1) {
@@ -611,17 +714,17 @@ function shiftTrack(shape, shift) {
611
714
  return (lo + (span > 0 ? (d - table[lo]) / span : 0)) / SAMPLES2;
612
715
  };
613
716
  const fromBase = (g) => {
614
- if (length2 <= 0 || shape.length <= 0) return g;
615
- if (g < 0) return g * shape.length / startRatio / length2;
616
- if (g > 1) return (length2 + (g - 1) * shape.length / endRatio) / length2;
717
+ if (length <= 0 || shape.length <= 0) return g;
718
+ if (g < 0) return g * shape.length / startRatio / length;
719
+ if (g > 1) return (length + (g - 1) * shape.length / endRatio) / length;
617
720
  const x = g * SAMPLES2;
618
721
  const i = Math.min(SAMPLES2 - 1, Math.floor(x));
619
- return (table[i] + (x - i) * (table[i + 1] - table[i])) / length2;
722
+ return (table[i] + (x - i) * (table[i + 1] - table[i])) / length;
620
723
  };
621
724
  const at2 = (f, options) => moved2(shape.at(toBase(options?.open ? f : clamp(f, 0, 1)), options));
622
725
  return {
623
726
  kind: shape.kind,
624
- length: length2,
727
+ length,
625
728
  closed: shape.closed,
626
729
  at: at2,
627
730
  nearest(p, options) {
@@ -634,8 +737,7 @@ function shiftTrack(shape, shift) {
634
737
  }
635
738
 
636
739
  // src/track/slice.ts
637
- var RAD3 = Math.PI / 180;
638
- var seen = (c, p) => Math.atan2(p.x - c.x, c.y - p.y) / RAD3;
740
+ var seen = (c, p) => Math.atan2(p.x - c.x, c.y - p.y) / DEG_TO_RAD;
639
741
  function trackSlice(shape, from, to) {
640
742
  const lo = clamp(Math.min(from, to), 0, 1);
641
743
  const hi = clamp(Math.max(from, to), 0, 1);
@@ -675,25 +777,25 @@ function runPlaces(lengths, gap, room, align, offset2, at2) {
675
777
  const fits = at2 === void 0 && total <= room;
676
778
  const lead = align === "start" ? 0 : align === "end" ? total : total / 2;
677
779
  let lo = at2 !== void 0 ? at2 - offset2 - lead : !fits ? -offset2 : align === "start" ? 0 : align === "end" ? room - total : (room - total) / 2;
678
- return lengths.map((length2) => {
780
+ return lengths.map((length) => {
679
781
  const start = lo;
680
- const hi = start + length2;
782
+ const hi = start + length;
681
783
  lo = hi + gap;
682
- return { center: start + length2 / 2, clipped: fits ? "none" : clipOf(start, hi, 0, room) };
784
+ return { center: start + length / 2, clipped: fits ? "none" : clipOf(start, hi, 0, room) };
683
785
  });
684
786
  }
685
787
  function runLength(lengths, gap) {
686
- return lengths.reduce((sum, length2, i) => sum + length2 + (i > 0 ? gap : 0), 0);
788
+ return lengths.reduce((sum, length, i) => sum + length + (i > 0 ? gap : 0), 0);
687
789
  }
688
- function runStationAt(run2, distance, shift = 0, past = "extend") {
689
- const { length: length2 } = run2;
690
- if (past === "follow" && length2 > 0) {
691
- const place2 = run2.at(distance / length2, { open: true });
790
+ function runStationAt(run2, distance3, shift = 0, past = "extend") {
791
+ const { length } = run2;
792
+ if (past === "follow" && length > 0) {
793
+ const place2 = run2.at(distance3 / length, { open: true });
692
794
  const { point: point2, normal: normal3 } = place2;
693
795
  return { ...place2, point: { x: point2.x + shift * normal3.x, y: point2.y + shift * normal3.y } };
694
796
  }
695
- const place = run2.at(length2 === 0 ? 0 : distance / length2);
696
- const beyond = distance < 0 ? distance : distance > length2 ? distance - length2 : 0;
797
+ const place = run2.at(length === 0 ? 0 : distance3 / length);
798
+ const beyond = distance3 < 0 ? distance3 : distance3 > length ? distance3 - length : 0;
697
799
  const { point, tangent, normal: normal2 } = place;
698
800
  return {
699
801
  ...place,
@@ -702,22 +804,22 @@ function runStationAt(run2, distance, shift = 0, past = "extend") {
702
804
  }
703
805
  function placeAlongRun(run2, lengths, options = {}) {
704
806
  const { gap = 0, align = "center", offset: offset2 = 0, at: at2, shift = 0, wrap = false, past = "extend", order = "reading" } = options;
705
- const { length: length2 } = run2;
807
+ const { length } = run2;
706
808
  const mirror = order === "track" && run2.reversed;
707
- const spots = runPlaces(lengths, gap, length2, align, offset2, at2);
708
- const lap = wrap && run2.closed && spots.length > 0 ? wrapWindow(spots[0], lengths[0], length2) : null;
809
+ const spots = runPlaces(lengths, gap, length, align, offset2, at2);
810
+ const lap = wrap && run2.closed && spots.length > 0 ? wrapWindow(spots[0], lengths[0], length) : null;
709
811
  return spots.map((spot, i) => {
710
812
  const half = lengths[i] / 2;
711
813
  const clipped = lap ? clipOf(spot.center - half, spot.center + half, lap.start, lap.end, lap.hair) : spot.clipped;
712
- let distance = spot.center;
713
- if (past === "hold" && clipped === "past") distance = clamp(distance, lap?.start ?? 0, lap?.end ?? length2);
714
- if (lap && length2 > 0) distance = (distance % length2 + length2) % length2;
715
- return { ...runStationAt(run2, mirror ? length2 - distance : distance, shift, past === "extend" ? "extend" : "follow"), center: spot.center, clipped };
814
+ let distance3 = spot.center;
815
+ if (past === "hold" && clipped === "past") distance3 = clamp(distance3, lap?.start ?? 0, lap?.end ?? length);
816
+ if (lap && length > 0) distance3 = (distance3 % length + length) % length;
817
+ return { ...runStationAt(run2, mirror ? length - distance3 : distance3, shift, past === "extend" ? "extend" : "follow"), center: spot.center, clipped };
716
818
  });
717
819
  }
718
- function wrapWindow(first, firstLength, length2) {
820
+ function wrapWindow(first, firstLength, length) {
719
821
  const start = first.center - firstLength / 2;
720
- return { start, end: start + length2, hair: 1e-9 * Math.max(1, length2) };
822
+ return { start, end: start + length, hair: 1e-9 * Math.max(1, length) };
721
823
  }
722
824
 
723
825
  // src/portCurve2.ts
@@ -734,6 +836,52 @@ function portCurvePoints2(ax, ay, bx, by, outNormal, inNormal, samples, reach =
734
836
  );
735
837
  }
736
838
 
839
+ // src/along/curvePosition.ts
840
+ function runSegments(t, r) {
841
+ return tableInternals(t).cum[r].length - 1;
842
+ }
843
+ function curveSpan(t, span) {
844
+ const from = Math.max(span.from, 0);
845
+ const to = Math.min(span.to, t.total);
846
+ if (!(from < to)) return null;
847
+ const a = curveAtLength(t, from, true);
848
+ const b = curveAtLength(t, to, false);
849
+ return a && b ? { from: a, to: b } : null;
850
+ }
851
+ function lengthAtCurve(t, at2) {
852
+ const { cum } = tableInternals(t);
853
+ const r = Math.min(Math.max(at2.run, 0), t.runs.length - 1);
854
+ if (r < 0) return 0;
855
+ const c = cum[r];
856
+ const segs = c.length - 1;
857
+ if (segs <= 0) return c[0];
858
+ const k = Math.min(Math.max(Math.floor(at2.segment), 0), segs - 1);
859
+ const f = Math.min(Math.max(at2.t, 0), 1);
860
+ const len = c[k + 1] - c[k];
861
+ const arc2 = f <= 0 || f >= 1 || !(len > 0) ? null : segmentArc(t, r, k);
862
+ if (!arc2) return c[k] + f * len;
863
+ return c[k] + Math.min(lookupLength(arc2.coords, t.dim, arc2.lookup, f), len);
864
+ }
865
+ function curveAtLength(t, s, starts2) {
866
+ const { cum } = tableInternals(t);
867
+ for (let r = 0; r < cum.length; r++) {
868
+ const c = cum[r];
869
+ const end = c[c.length - 1];
870
+ if (c.length < 2 || !(end > c[0]) || (starts2 ? s >= end : s > end)) continue;
871
+ for (let k = 0; k < c.length - 1; k++) {
872
+ const len = c[k + 1] - c[k];
873
+ if (!(len > 0) || (starts2 ? s >= c[k + 1] : s > c[k + 1])) continue;
874
+ return { run: r, segment: k, t: paramAt(t, r, k, s - c[k], len) };
875
+ }
876
+ }
877
+ return null;
878
+ }
879
+ function paramAt(t, r, k, local, len) {
880
+ const arc2 = segmentArc(t, r, k);
881
+ if (!arc2) return Math.min(Math.max(local / len, 0), 1);
882
+ return lookupT(arc2.coords, t.dim, arc2.lookup, local);
883
+ }
884
+
737
885
  // src/pathSubpaths.ts
738
886
  function flatSubpaths(path, tolerance) {
739
887
  const drawn = resolveCorners(path);
@@ -802,6 +950,71 @@ function closedSubpaths(path, opts) {
802
950
  return regionSubpaths(path, opts).closed;
803
951
  }
804
952
 
953
+ // src/outlineSegments.ts
954
+ function someSegment(path, tolerance, span, visit) {
955
+ if (path.kind === "rect") return someRectEdge(path, span, visit);
956
+ if (!span) return someFlatEdge(path, tolerance, visit);
957
+ const o = originOf(path);
958
+ const polylines = extractPolylines(path, { flattenTolerance: tolerance ?? DEFAULT_FLATTEN_TOLERANCE });
959
+ const { runs } = pathLengthTable(path);
960
+ for (let r = Math.max(span.from.run, 0); r <= Math.min(span.to.run, polylines.length - 1); r++) {
961
+ const pl = polylines[r];
962
+ const place = (at2) => polylineAtCurve(pl, at2.segment, at2.t, runs[r]);
963
+ const head = r === span.from.run ? place(span.from) : null;
964
+ const tail = r === span.to.run ? place(span.to) : null;
965
+ const from = head?.length ?? 0;
966
+ const to = tail?.length ?? Infinity;
967
+ if (!(from < to)) continue;
968
+ const p = pl.points;
969
+ const n = p.length / 2;
970
+ const end = pl.closed && n >= 2 ? n : n - 1;
971
+ let s = 0;
972
+ for (let i = 0; i < end; i++) {
973
+ const j = (i + 1) % n;
974
+ const ax = p[i * 2], ay = p[i * 2 + 1];
975
+ const bx = p[j * 2], by = p[j * 2 + 1];
976
+ const s0 = s;
977
+ s += Math.hypot(bx - ax, by - ay);
978
+ if (head && s <= from) continue;
979
+ if (tail && s0 >= to) break;
980
+ const [x0, y0] = head && s0 < from ? [head.x, head.y] : [ax, ay];
981
+ const [x1, y1] = tail && s > to ? [tail.x, tail.y] : [bx, by];
982
+ if (visit(x0 + o.x, y0 + o.y, x1 + o.x, y1 + o.y)) return true;
983
+ }
984
+ }
985
+ return false;
986
+ }
987
+ function someFlatEdge(path, tolerance, visit) {
988
+ const { closed, open } = flatSubpaths(path, tolerance ?? DEFAULT_FLATTEN_TOLERANCE);
989
+ const run2 = (sub3, wrap) => {
990
+ const n = sub3.length;
991
+ const last = wrap ? n : n - 2;
992
+ for (let i = 0; i < last; i += 2) {
993
+ const j = (i + 2) % n;
994
+ if (visit(sub3[i], sub3[i + 1], sub3[j], sub3[j + 1])) return true;
995
+ }
996
+ return false;
997
+ };
998
+ return closed.some((sub3) => run2(sub3, true)) || open.some((sub3) => run2(sub3, false));
999
+ }
1000
+ function someRectEdge(rect, span, visit) {
1001
+ const { x, y, width: w, height: h } = rect;
1002
+ const cx = [x, x + w, x + w, x];
1003
+ const cy = [y, y, y + h, y + h];
1004
+ const lo = span ? span.from : { run: 0, segment: 0, t: 0 };
1005
+ const hi = span ? span.to : { run: 0, segment: 3, t: 1 };
1006
+ if (lo.run !== 0 || hi.run !== 0) return false;
1007
+ for (let i = Math.max(lo.segment, 0); i <= Math.min(hi.segment, 3); i++) {
1008
+ const j = i + 1 & 3;
1009
+ const t0 = i === lo.segment ? lo.t : 0;
1010
+ const t1 = i === hi.segment ? hi.t : 1;
1011
+ if (!(t1 > t0) && lo.segment === hi.segment) return false;
1012
+ const at2 = (f) => [cx[i] + (cx[j] - cx[i]) * f, cy[i] + (cy[j] - cy[i]) * f];
1013
+ if (visit(...at2(t0), ...at2(t1))) return true;
1014
+ }
1015
+ return false;
1016
+ }
1017
+
805
1018
  // src/screenBand.ts
806
1019
  function stretchOf(m) {
807
1020
  const e = (m.a + m.d) / 2;
@@ -933,30 +1146,16 @@ function strokeHitTest(path, x, y, threshold, opts = {}) {
933
1146
  const st = stretchOf(m);
934
1147
  if (st.s1 === 0) return false;
935
1148
  if (isSimilarity(st)) {
936
- return strokeHitTest(path, x, y, threshold + slop.px / st.s1, { tolerance: opts.tolerance });
1149
+ return strokeHitTest(path, x, y, threshold + slop.px / st.s1, { tolerance: opts.tolerance, span: opts.span });
937
1150
  }
938
1151
  const sx = a * x + c * y, sy = b * x + d * y;
939
- return someSegment(path, opts.tolerance, (ax, ay, bx, by) => capsuleWithinPx(sx, sy, ax, ay, bx, by, threshold, slop.px, m, st));
1152
+ return walk(path, opts, (ax, ay, bx, by) => capsuleWithinPx(sx, sy, ax, ay, bx, by, threshold, slop.px, m, st));
940
1153
  }
941
1154
  const t2 = threshold * threshold;
942
- return someSegment(path, opts.tolerance, (ax, ay, bx, by) => pointSegmentDist2(x, y, ax, ay, bx, by) <= t2);
1155
+ return walk(path, opts, (ax, ay, bx, by) => pointSegmentDist2(x, y, ax, ay, bx, by) <= t2);
943
1156
  }
944
- function someSegment(path, tolerance, visit) {
945
- if (path.kind === "rect") {
946
- const { x: rx, y: ry, width: w, height: h } = path;
947
- return visit(rx, ry, rx + w, ry) || visit(rx + w, ry, rx + w, ry + h) || visit(rx + w, ry + h, rx, ry + h) || visit(rx, ry + h, rx, ry);
948
- }
949
- const { closed, open } = flatSubpaths(path, tolerance ?? DEFAULT_FLATTEN_TOLERANCE);
950
- const run2 = (sub3, wrap) => {
951
- const n = sub3.length;
952
- const last = wrap ? n : n - 2;
953
- for (let i = 0; i < last; i += 2) {
954
- const j = (i + 2) % n;
955
- if (visit(sub3[i], sub3[i + 1], sub3[j], sub3[j + 1])) return true;
956
- }
957
- return false;
958
- };
959
- return closed.some((sub3) => run2(sub3, true)) || open.some((sub3) => run2(sub3, false));
1157
+ function walk(path, opts, visit) {
1158
+ return someSegment(path, opts.tolerance, opts.span, visit);
960
1159
  }
961
1160
  function rectToVerts(r) {
962
1161
  return [
@@ -1128,8 +1327,8 @@ function polygonIntersectsPath(polygon, path, opts = {}) {
1128
1327
  }
1129
1328
 
1130
1329
  // src/paths/cornerOffset.ts
1131
- function offsetCornerTraces(path, distance) {
1132
- if (path.kind !== "polygon" || distance === 0) return [];
1330
+ function offsetCornerTraces(path, distance3) {
1331
+ if (path.kind !== "polygon" || distance3 === 0) return [];
1133
1332
  const drawn = resolveCorners(path);
1134
1333
  const out = [];
1135
1334
  for (const cut of cornerCuts(path)) {
@@ -1146,9 +1345,9 @@ function offsetCornerTraces(path, distance) {
1146
1345
  const half = Math.acos(Math.max(-1, Math.min(1, din.x * b.x + din.y * b.y)));
1147
1346
  const probe = Math.min(inLen, outLen) * 0.01;
1148
1347
  const sign2 = pointInPath(drawn, v.x + b.x * probe, v.y + b.y * probe) ? -1 : 1;
1149
- const radius = spec.radius + sign2 * distance / Math.tan(half);
1348
+ const radius = spec.radius + sign2 * distance3 / Math.tan(half);
1150
1349
  if (!(radius > 0)) continue;
1151
- const at2 = { x: v.x - b.x * sign2 * distance / Math.sin(half), y: v.y - b.y * sign2 * distance / Math.sin(half) };
1350
+ const at2 = { x: v.x - b.x * sign2 * distance3 / Math.sin(half), y: v.y - b.y * sign2 * distance3 / Math.sin(half) };
1152
1351
  const reach = radius * 2 + 1;
1153
1352
  const wedge = new PathBuilder().moveTo(at2.x + din.x * reach, at2.y + din.y * reach).lineTo(at2.x, at2.y).lineTo(at2.x + dout.x * reach, at2.y + dout.y * reach).build();
1154
1353
  const [moved2] = cornerCuts(withCorner(wedge, 1, { shape: spec.shape, radius }));
@@ -1704,7 +1903,7 @@ function arcToCubics(x1, y1, x2, y2, rx, ry, xAxisRotationDeg, largeArc, sweep,
1704
1903
  b.lineTo(x2, y2);
1705
1904
  return;
1706
1905
  }
1707
- const phi = xAxisRotationDeg * Math.PI / 180;
1906
+ const phi = xAxisRotationDeg * DEG_TO_RAD;
1708
1907
  const cosPhi = Math.cos(phi);
1709
1908
  const sinPhi = Math.sin(phi);
1710
1909
  const dx2 = (x1 - x2) / 2;
@@ -2306,64 +2505,11 @@ function crossingStops(path, cutters) {
2306
2505
  return stops;
2307
2506
  }
2308
2507
 
2309
- // src/paths/segmentLength.ts
2310
- var GL_X = [
2311
- -0.9602898564975363,
2312
- -0.7966664774136267,
2313
- -0.525532409916329,
2314
- -0.1834346424956498,
2315
- 0.1834346424956498,
2316
- 0.525532409916329,
2317
- 0.7966664774136267,
2318
- 0.9602898564975363
2319
- ];
2320
- var GL_W2 = [
2321
- 0.1012285362903763,
2322
- 0.2223810344533745,
2323
- 0.3137066458778873,
2324
- 0.362683783378362,
2325
- 0.362683783378362,
2326
- 0.3137066458778873,
2327
- 0.2223810344533745,
2328
- 0.1012285362903763
2329
- ];
2330
- var speed2 = (s, t) => {
2331
- const d = derivSeg(s, t);
2332
- return Math.hypot(d.x, d.y);
2333
- };
2334
- function segmentLength(s, a = 0, b = 1) {
2335
- if (s.length === 2) return speed2(s, 0) * (b - a);
2336
- const panels = 8, h = (b - a) / panels;
2337
- let sum = 0;
2338
- for (let k = 0; k < panels; k++) {
2339
- const mid = a + (k + 0.5) * h;
2340
- for (let i = 0; i < GL_X.length; i++) sum += GL_W2[i] * speed2(s, mid + GL_X[i] * h / 2);
2341
- }
2342
- return sum * h / 2;
2343
- }
2344
- function segmentTAtLength(s, length2) {
2345
- const total = segmentLength(s);
2346
- if (length2 <= 0 || !(total > 0)) return 0;
2347
- if (length2 >= total) return 1;
2348
- if (s.length === 2) return length2 / total;
2349
- let lo = 0, hi = 1, t = length2 / total;
2350
- for (let i = 0; i < 30; i++) {
2351
- const err = segmentLength(s, 0, t) - length2;
2352
- if (Math.abs(err) < 1e-10 * Math.max(1, total)) break;
2353
- if (err > 0) hi = t;
2354
- else lo = t;
2355
- const v = speed2(s, t);
2356
- let next = v > 0 ? t - err / v : NaN;
2357
- if (!(next > lo && next < hi)) next = (lo + hi) / 2;
2358
- t = next;
2359
- }
2360
- return t;
2361
- }
2362
-
2363
2508
  // src/snap/segmentFeatures.ts
2364
2509
  function segmentMidT(s) {
2365
2510
  if (s.length === 2) return 0.5;
2366
- return segmentTAtLength(s, segmentLength(s) / 2);
2511
+ const c = flatPoints(s);
2512
+ return segmentTAtLength(c, 2, segmentLength(c, 2) / 2);
2367
2513
  }
2368
2514
  function unitQuadraticRoots(a, b, c) {
2369
2515
  const inside = (t) => t >= -1e-9 && t <= 1 + 1e-9;
@@ -2559,8 +2705,8 @@ function findPathSnaps(index, probe, query) {
2559
2705
  const skip = query.skip ?? (() => false);
2560
2706
  const hits = [];
2561
2707
  const offer = (kind, x, y, paths) => {
2562
- const distance = metric(x - probe.x, y - probe.y);
2563
- if (distance <= r) hits.push({ kind, x, y, distance, paths });
2708
+ const distance3 = metric(x - probe.x, y - probe.y);
2709
+ if (distance3 <= r) hits.push({ kind, x, y, distance: distance3, paths });
2564
2710
  };
2565
2711
  const rx = sx > 0 ? r / sx : Infinity, ry = sy > 0 ? r / sy : Infinity;
2566
2712
  const box = [probe.x - rx, probe.y - ry, probe.x + rx, probe.y + ry];
@@ -3057,14 +3203,14 @@ function segmentAt2(set, x, y, opts) {
3057
3203
  const hit = nearestSegmentT(screenAnchors, x * scale3.x, y * scale3.y, set.closed);
3058
3204
  if (!hit) return null;
3059
3205
  const ends = segmentEnds(set.anchors, set.closed, hit.sub, hit.segIdx);
3060
- const point = pointOnSegment(ends.a, ends.b, hit.t);
3206
+ const point = pointOnSegment2(ends.a, ends.b, hit.t);
3061
3207
  const tol = opts.tolerancePx;
3062
3208
  if (!withinPxRadius(point.x - x, point.y - y, tol, scale3)) return null;
3063
3209
  if (withinPxRadius(point.x - ends.a.x, point.y - ends.a.y, tol, scale3)) return null;
3064
3210
  if (withinPxRadius(point.x - ends.b.x, point.y - ends.b.y, tol, scale3)) return null;
3065
3211
  return { sub: hit.sub, segIdx: hit.segIdx, t: hit.t, x: point.x, y: point.y };
3066
3212
  }
3067
- function pointOnSegment(a, b, t) {
3213
+ function pointOnSegment2(a, b, t) {
3068
3214
  if (isStraightSegment(a, b)) return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
3069
3215
  return cubicPointAt(a, a.outHandle ?? a, b.inHandle ?? b, b, t);
3070
3216
  }
@@ -3142,12 +3288,12 @@ var FULL = 2 * Math.PI;
3142
3288
  var FIT_SHARE = 1e-4;
3143
3289
  var FIT_FLOOR = 1e-3;
3144
3290
  var MAX_SPLITS = 6;
3145
- function extensionPieces(frame, distance, mode, opts = {}) {
3146
- if (!(distance > 0)) return [];
3147
- const bends = Math.abs(frame.curvature) * distance > STRAIGHT_TURN;
3148
- if (mode === "curvature" && bends) return arcPieces(frame, distance);
3149
- if (mode === "eased" && bends) return easedPieces(frame, distance, easeTurnOf(opts));
3150
- return [line(frame.point, frame.tangent, distance)];
3291
+ function extensionPieces(frame, distance3, mode, opts = {}) {
3292
+ if (!(distance3 > 0)) return [];
3293
+ const bends = Math.abs(frame.curvature) * distance3 > STRAIGHT_TURN;
3294
+ if (mode === "curvature" && bends) return arcPieces(frame, distance3);
3295
+ if (mode === "eased" && bends) return easedPieces(frame, distance3, easeTurnOf(opts));
3296
+ return [line(frame.point, frame.tangent, distance3)];
3151
3297
  }
3152
3298
  function easeLength(frame, opts = {}) {
3153
3299
  const k = Math.abs(frame.curvature);
@@ -3157,16 +3303,16 @@ function easeTurnOf(opts) {
3157
3303
  const turn = opts.easeTurn;
3158
3304
  return turn !== void 0 && Number.isFinite(turn) && turn > 0 ? Math.min(turn, QUARTER) : DEFAULT_EASE_TURN;
3159
3305
  }
3160
- function line(from, dir, length2) {
3161
- return { pts: [from, { x: from.x + dir.x * length2, y: from.y + dir.y * length2 }], length: length2 };
3306
+ function line(from, dir, length) {
3307
+ return { pts: [from, { x: from.x + dir.x * length, y: from.y + dir.y * length }], length };
3162
3308
  }
3163
3309
  function arcCenter({ point, tangent, curvature }) {
3164
3310
  return { x: point.x - tangent.y / curvature, y: point.y + tangent.x / curvature };
3165
3311
  }
3166
- function arcPieces(frame, distance) {
3312
+ function arcPieces(frame, distance3) {
3167
3313
  const s = Math.sign(frame.curvature);
3168
3314
  const r = 1 / Math.abs(frame.curvature);
3169
- const sweep = Math.min(distance / r, FULL);
3315
+ const sweep = Math.min(distance3 / r, FULL);
3170
3316
  const count = Math.max(1, Math.ceil(sweep / QUARTER - 1e-9));
3171
3317
  const step = sweep / count;
3172
3318
  const reach = 4 / 3 * Math.tan(step / 4) * r;
@@ -3197,7 +3343,7 @@ function arcPieces(frame, distance) {
3197
3343
  }
3198
3344
  return out;
3199
3345
  }
3200
- function easeSpiral(frame, length2, turn) {
3346
+ function easeSpiral(frame, length, turn) {
3201
3347
  const sign2 = Math.sign(frame.curvature);
3202
3348
  const k0 = sign2 * turn;
3203
3349
  const k1 = -2 * sign2 * turn;
@@ -3212,19 +3358,19 @@ function easeSpiral(frame, length2, turn) {
3212
3358
  chordAngle: Math.atan2(c.y, c.x),
3213
3359
  p0: frame.point,
3214
3360
  p1: frame.point,
3215
- length: length2,
3361
+ length,
3216
3362
  rotation
3217
3363
  };
3218
3364
  return { ...seg, p1: eulerPointAt(seg, 1) };
3219
3365
  }
3220
- function easedPieces(frame, distance, turn) {
3366
+ function easedPieces(frame, distance3, turn) {
3221
3367
  const ease = 2 * turn / Math.abs(frame.curvature);
3222
3368
  const spiral = easeSpiral(frame, ease, turn);
3223
- const drawn = distance < ease ? spiralPiece(spiral, 0, distance / ease) : spiral;
3369
+ const drawn = distance3 < ease ? spiralPiece(spiral, 0, distance3 / ease) : spiral;
3224
3370
  const out = fitSpiral(drawn, Math.max(FIT_FLOOR, FIT_SHARE * ease), 0);
3225
- if (distance > ease) {
3371
+ if (distance3 > ease) {
3226
3372
  const a = eulerAngleAt(spiral, 1);
3227
- out.push(line(spiral.p1, { x: Math.cos(a), y: Math.sin(a) }, distance - ease));
3373
+ out.push(line(spiral.p1, { x: Math.cos(a), y: Math.sin(a) }, distance3 - ease));
3228
3374
  }
3229
3375
  return out;
3230
3376
  }
@@ -3291,8 +3437,9 @@ function g2Cubic(seg) {
3291
3437
  }
3292
3438
  function pieceLengthAt(piece, t) {
3293
3439
  if (piece.pts.length === 2) return piece.length * t;
3294
- const whole = segmentLength(piece.pts);
3295
- return whole > 0 ? piece.length * segmentLength(piece.pts, 0, t) / whole : 0;
3440
+ const c = flatPoints(piece.pts);
3441
+ const whole = segmentLength(c, 2);
3442
+ return whole > 0 ? piece.length * segmentLength(c, 2, 0, t) / whole : 0;
3296
3443
  }
3297
3444
 
3298
3445
  // src/paths/lengthenPathEnd.ts
@@ -3315,9 +3462,9 @@ function pathEndAnchorIndex(set, end) {
3315
3462
  function pathEndFrame(set, end) {
3316
3463
  return atTail(set, end, tailFrame) ?? null;
3317
3464
  }
3318
- function lengthenPathEnd(set, end, distance, mode, opts = {}) {
3319
- if (!(distance !== 0 && Number.isFinite(distance))) return false;
3320
- return atTail(set, end, (sub3) => distance < 0 ? trimTail(sub3, -distance) : growTail(sub3, distance, mode, opts)) ?? false;
3465
+ function lengthenPathEnd(set, end, distance3, mode, opts = {}) {
3466
+ if (!(distance3 !== 0 && Number.isFinite(distance3))) return false;
3467
+ return atTail(set, end, (sub3) => distance3 < 0 ? trimTail(sub3, -distance3) : growTail(sub3, distance3, mode, opts)) ?? false;
3321
3468
  }
3322
3469
  function lengthToward(set, end, mode, point, near2 = 0, opts = {}) {
3323
3470
  const frame = pathEndFrame(set, end);
@@ -3393,10 +3540,10 @@ function lengthAlongEased(frame, point, opts) {
3393
3540
  }
3394
3541
  return best.length;
3395
3542
  }
3396
- function growTail(sub3, distance, mode, opts) {
3543
+ function growTail(sub3, distance3, mode, opts) {
3397
3544
  const frame = tailFrame(sub3);
3398
3545
  if (!frame) return false;
3399
- const pieces = extensionPieces(frame, distance, mode, opts);
3546
+ const pieces = extensionPieces(frame, distance3, mode, opts);
3400
3547
  const end = sub3[sub3.length - 1];
3401
3548
  const first = pieces[0];
3402
3549
  if (pieces.length === 1 && first.pts.length === 2 && isStraightSegment(sub3[sub3.length - 2], end)) {
@@ -3423,7 +3570,7 @@ function appendPieces(sub3, pieces) {
3423
3570
  function tailLength(sub3) {
3424
3571
  const [p0, p1, p2, p3] = tailCubic(sub3);
3425
3572
  if (isStraightSegment(p0, p3)) return Math.hypot(p3.x - p0.x, p3.y - p0.y);
3426
- return segmentLength([p0, p1, p2, p3]);
3573
+ return segmentLength(flatPoints([p0, p1, p2, p3]), 2);
3427
3574
  }
3428
3575
  function subpathLength(sub3) {
3429
3576
  let total = 0;
@@ -3453,7 +3600,7 @@ function cutTail(sub3, keep, len) {
3453
3600
  p3.y = p0.y + (p3.y - p0.y) * f;
3454
3601
  return;
3455
3602
  }
3456
- const t = segmentTAtLength([p0, p1, p2, p3], keep);
3603
+ const t = segmentTAtLength(flatPoints([p0, p1, p2, p3]), 2, keep);
3457
3604
  const { left } = splitCubicAtT(p0, p1, p2, p3, t);
3458
3605
  if (p0.outHandle) p0.outHandle = left[1];
3459
3606
  p3.inHandle = left[2];
@@ -3479,9 +3626,9 @@ function segmentsBesideEnd(path, end) {
3479
3626
  return s === end.sub ? segs.filter((g) => g.index !== (end.end === "last" ? g.count - 1 : 0)) : segs;
3480
3627
  });
3481
3628
  }
3482
- function lengthenOpenEnd(path, end, distance, mode = "tangent", opts = {}) {
3483
- if (!(distance > 0) || path.kind !== "polygon") return null;
3484
- return editAnchorSetTracked(path, (set) => lengthenPathEnd(set, end, distance, mode, opts))?.path ?? null;
3629
+ function lengthenOpenEnd(path, end, distance3, mode = "tangent", opts = {}) {
3630
+ if (!(distance3 > 0) || path.kind !== "polygon") return null;
3631
+ return editAnchorSetTracked(path, (set) => lengthenPathEnd(set, end, distance3, mode, opts))?.path ?? null;
3485
3632
  }
3486
3633
  function extendToMeet(path, boundaries, end, opts = {}) {
3487
3634
  if (path.kind !== "polygon") return null;
@@ -3564,7 +3711,7 @@ function simplifySpan(sub3, closed, from, to, tolerance, opts = {}) {
3564
3711
  function fitParams(tolerance, opts) {
3565
3712
  return {
3566
3713
  tol: Math.max(tolerance, 1e-6),
3567
- cosCorner: Math.cos((opts.cornerAngle ?? 40) * Math.PI / 180)
3714
+ cosCorner: Math.cos((opts.cornerAngle ?? 40) * DEG_TO_RAD)
3568
3715
  };
3569
3716
  }
3570
3717
  function breaksOf(sub3, closed, cosCorner) {
@@ -3649,12 +3796,12 @@ function unit(p) {
3649
3796
  // src/paths/growRegion.ts
3650
3797
  var MAX_DEPTH = 6;
3651
3798
  var SAMPLES3 = [0.2, 0.35, 0.5, 0.65, 0.8];
3652
- function growRegion(path, distance, opts = {}) {
3799
+ function growRegion(path, distance3, opts = {}) {
3653
3800
  const b = new PathBuilder();
3654
- if (!(distance > 0)) return b.build();
3801
+ if (!(distance3 > 0)) return b.build();
3655
3802
  const sigma = pathSignedArea(path) < 0 ? -1 : 1;
3656
3803
  const ctx = {
3657
- d: distance,
3804
+ d: distance3,
3658
3805
  sigma,
3659
3806
  tol: opts.tolerance ?? 0.1,
3660
3807
  join: opts.join ?? "miter",
@@ -3887,7 +4034,7 @@ function segmentCrossings(segs) {
3887
4034
  const out = [];
3888
4035
  for (let a = 0; a < segs.length; a++) {
3889
4036
  for (let b = a + 1; b < segs.length; b++) {
3890
- if (!overlaps(boxes[a], boxes[b])) continue;
4037
+ if (!boxesIntersect(boxes[a], boxes[b])) continue;
3891
4038
  const pa = flat[a], pb = flat[b];
3892
4039
  for (let i = 0; i + 1 < pa.length; i++) {
3893
4040
  for (let j = 0; j + 1 < pb.length; j++) {
@@ -3921,7 +4068,6 @@ function boxOf(pts) {
3921
4068
  }
3922
4069
  return b;
3923
4070
  }
3924
- var overlaps = (p, q) => p[0] <= q[2] && q[0] <= p[2] && p[1] <= q[3] && q[1] <= p[3];
3925
4071
  function linesCross(a, b, c, d) {
3926
4072
  const rx = b[0] - a[0], ry = b[1] - a[1], sx = d[0] - c[0], sy = d[1] - c[1];
3927
4073
  const den = rx * sy - ry * sx;
@@ -4204,7 +4350,7 @@ function measure(a, b, from) {
4204
4350
  }
4205
4351
  return { a, c1, c2, b, straight, from, len: table[LENGTH_SAMPLES], table };
4206
4352
  }
4207
- function paramAt(seg, d) {
4353
+ function paramAt2(seg, d) {
4208
4354
  const { table } = seg;
4209
4355
  if (seg.len === 0) return 0;
4210
4356
  let lo = 0;
@@ -4221,21 +4367,21 @@ function paramAt(seg, d) {
4221
4367
  function arcRun(sub3, from, to) {
4222
4368
  const n = sub3.length;
4223
4369
  const segs = [];
4224
- let length2 = 0;
4370
+ let length = 0;
4225
4371
  for (let i = from; i < to; i++) {
4226
- const seg = measure(sub3[i % n], sub3[(i + 1) % n], length2);
4372
+ const seg = measure(sub3[i % n], sub3[(i + 1) % n], length);
4227
4373
  segs.push(seg);
4228
- length2 += seg.len;
4374
+ length += seg.len;
4229
4375
  }
4230
4376
  const locate = (s, arriving) => {
4231
- const d = Math.min(Math.max(s, 0), length2);
4377
+ const d = Math.min(Math.max(s, 0), length);
4232
4378
  let k = 0;
4233
4379
  while (k < segs.length - 1 && (arriving ? segs[k].from + segs[k].len < d : segs[k].from + segs[k].len <= d)) k++;
4234
- return { seg: segs[k], t: paramAt(segs[k], d - segs[k].from) };
4380
+ return { seg: segs[k], t: paramAt2(segs[k], d - segs[k].from) };
4235
4381
  };
4236
4382
  const start = { x: sub3[from % n].x, y: sub3[from % n].y };
4237
4383
  return {
4238
- length: length2,
4384
+ length,
4239
4385
  pointAt(s) {
4240
4386
  if (segs.length === 0) return start;
4241
4387
  const { seg, t } = locate(s, false);
@@ -5179,7 +5325,10 @@ function poseFrameInverseOf(pose) {
5179
5325
  function pathThroughPoseFrame(path, pose) {
5180
5326
  if (poseLinearOf(pose)) {
5181
5327
  const frame = poseFrameOf(pose);
5182
- return frame ? transformPath(path, frame) : path;
5328
+ if (!frame) return path;
5329
+ const [a, b, c, d] = frame;
5330
+ const uniform = isSimilarity(stretchOf({ a, b, c, d }));
5331
+ return transformPath(path.kind === "polygon" && !uniform ? resolveCorners(path) : path, frame);
5183
5332
  }
5184
5333
  const r = poseRotationOf(pose);
5185
5334
  return r ? rotatePathAround(path, r.cx, r.cy, r.rotation) : path;
@@ -5189,17 +5338,13 @@ function pathThroughPoseFrame(path, pose) {
5189
5338
  function anchorsUnderBrush(set, from, to, opts) {
5190
5339
  const scale3 = opts.scale ?? { x: 1, y: 1 };
5191
5340
  const ax = from.x * scale3.x, ay = from.y * scale3.y;
5192
- const dx2 = to.x * scale3.x - ax, dy2 = to.y * scale3.y - ay;
5193
- const len22 = dx2 * dx2 + dy2 * dy2;
5341
+ const bx = to.x * scale3.x, by = to.y * scale3.y;
5194
5342
  const r2 = opts.radiusPx * opts.radiusPx;
5195
5343
  const hits = [];
5196
5344
  let flat = 0;
5197
5345
  for (const sub3 of set.anchors) {
5198
5346
  for (const a of sub3) {
5199
- const px = a.x * scale3.x - ax, py = a.y * scale3.y - ay;
5200
- const t = len22 === 0 ? 0 : Math.max(0, Math.min(1, (px * dx2 + py * dy2) / len22));
5201
- const ex = px - t * dx2, ey = py - t * dy2;
5202
- if (ex * ex + ey * ey <= r2) hits.push(flat);
5347
+ if (pointSegmentDist2(a.x * scale3.x, a.y * scale3.y, ax, ay, bx, by) <= r2) hits.push(flat);
5203
5348
  flat++;
5204
5349
  }
5205
5350
  }
@@ -5321,8 +5466,8 @@ function bendSegment(set, grip, dx2, dy2) {
5321
5466
  const aSmooth = isAnchorSmooth(a);
5322
5467
  const bSmooth = isAnchorSmooth(b);
5323
5468
  const straight = isStraightSegment(a, b);
5324
- const p1 = a.outHandle ?? (straight ? lerp(a, b, 1 / 3) : { x: a.x, y: a.y });
5325
- const p2 = b.inHandle ?? (straight ? lerp(a, b, 2 / 3) : { x: b.x, y: b.y });
5469
+ const p1 = a.outHandle ?? (straight ? lerpPoint(a, b, 1 / 3) : { x: a.x, y: a.y });
5470
+ const p2 = b.inHandle ?? (straight ? lerpPoint(a, b, 2 / 3) : { x: b.x, y: b.y });
5326
5471
  const t = Math.min(1 - T_MARGIN, Math.max(T_MARGIN, grip.t));
5327
5472
  const u = 1 - t;
5328
5473
  const k = 1 / (3 * t * u * (u * u + t * t));
@@ -5332,9 +5477,6 @@ function bendSegment(set, grip, dx2, dy2) {
5332
5477
  if (bSmooth) b.outHandle = alignedOpposite(b, b.inHandle, b.outHandle);
5333
5478
  return true;
5334
5479
  }
5335
- function lerp(a, b, f) {
5336
- return { x: a.x + (b.x - a.x) * f, y: a.y + (b.y - a.y) * f };
5337
- }
5338
5480
  function alignedOpposite(anchor, moved2, other2) {
5339
5481
  const mx = moved2.x - anchor.x;
5340
5482
  const my = moved2.y - anchor.y;
@@ -5483,10 +5625,10 @@ function same2(p, q) {
5483
5625
  }
5484
5626
 
5485
5627
  // src/paths/softSelection.ts
5486
- function softFalloff(falloff, distance, radius) {
5487
- if (!(radius > 0) || distance >= radius) return 0;
5488
- if (distance <= 0) return 1;
5489
- const u = distance / radius;
5628
+ function softFalloff(falloff, distance3, radius) {
5629
+ if (!(radius > 0) || distance3 >= radius) return 0;
5630
+ if (distance3 <= 0) return 1;
5631
+ const u = distance3 / radius;
5490
5632
  switch (falloff) {
5491
5633
  case "linear":
5492
5634
  return 1 - u;
@@ -5581,7 +5723,7 @@ function softSelectionBand(measure2, set, selected, radius) {
5581
5723
  sub3.segments.forEach((seg, i) => {
5582
5724
  const c = segmentControls2(anchors, sub3.closed, i);
5583
5725
  if (!c) return;
5584
- const at2 = (t) => isStraight(c) ? lerp2(c[0], c[3], t) : cubicPointAt(c[0], c[1], c[2], c[3], t);
5726
+ const at2 = (t) => isStraight(c) ? lerpPoint(c[0], c[3], t) : cubicPointAt(c[0], c[1], c[2], c[3], t);
5585
5727
  const steps = seg.lengths.length - 1;
5586
5728
  for (let k = 0; k <= steps; k++) {
5587
5729
  const len = seg.lengths[k];
@@ -5631,9 +5773,6 @@ function crossing2(a, b, aIn, inside) {
5631
5773
  }
5632
5774
  return (lo + hi) / 2;
5633
5775
  }
5634
- function lerp2(a, b, t) {
5635
- return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
5636
- }
5637
5776
 
5638
5777
  // src/paths/balanceHandles.ts
5639
5778
  function handleBalances(set) {
@@ -5661,7 +5800,7 @@ function segmentHandles(set, sub3, segIdx) {
5661
5800
  if (!ends || isStraightSegment(ends.a, ends.b)) return null;
5662
5801
  const { a, b } = ends;
5663
5802
  if (!a.outHandle || !b.inHandle) return null;
5664
- if (length(a, a.outHandle) === 0 || length(b, b.inHandle) === 0) return null;
5803
+ if (distance(a, a.outHandle) === 0 || distance(b, b.inHandle) === 0) return null;
5665
5804
  return { a, b, from: a.outHandle, to: b.inHandle };
5666
5805
  }
5667
5806
  function scaleSegmentHandles(set, sub3, segIdx, factor, syms = []) {
@@ -5691,8 +5830,8 @@ function equalizeSegmentHandles(set, sub3, segIdx) {
5691
5830
  const handles = segmentHandles(set, sub3, segIdx);
5692
5831
  if (!handles) return false;
5693
5832
  const { a, b, from, to } = handles;
5694
- const lenA = length(a, from);
5695
- const lenB = length(b, to);
5833
+ const lenA = distance(a, from);
5834
+ const lenB = distance(b, to);
5696
5835
  const ua = { x: (from.x - a.x) / lenA, y: (from.y - a.y) / lenA };
5697
5836
  const ub = { x: (to.x - b.x) / lenB, y: (to.y - b.y) / lenB };
5698
5837
  const corner = rayCrossing(a, ua, b, ub);
@@ -5719,9 +5858,6 @@ function rayCrossing(p, u, q, v) {
5719
5858
  const tb = (dx2 * u.y - dy2 * u.x) / den;
5720
5859
  return ta > 0 && tb > 0 ? { ta, tb } : null;
5721
5860
  }
5722
- function length(a, b) {
5723
- return Math.hypot(b.x - a.x, b.y - a.y);
5724
- }
5725
5861
  function scaledFrom(anchor, handle, k) {
5726
5862
  return { x: anchor.x + (handle.x - anchor.x) * k, y: anchor.y + (handle.y - anchor.y) * k };
5727
5863
  }
@@ -6917,11 +7053,11 @@ function handleOf(a, side) {
6917
7053
  function handleTarget(a, h, part, entry) {
6918
7054
  if (part === "X") return { x: apply2(h.x, entry), y: h.y };
6919
7055
  if (part === "Y") return { x: h.x, y: apply2(h.y, entry) };
6920
- const length2 = Math.hypot(h.x - a.x, h.y - a.y);
7056
+ const length = Math.hypot(h.x - a.x, h.y - a.y);
6921
7057
  const angle = angleOf(a, h);
6922
7058
  const nextAngle = part === "Angle" ? apply2(angle, entry) : angle;
6923
- const nextLength = part === "Length" ? Math.max(0, apply2(length2, entry)) : length2;
6924
- const rad = nextAngle * Math.PI / 180;
7059
+ const nextLength = part === "Length" ? Math.max(0, apply2(length, entry)) : length;
7060
+ const rad = nextAngle * DEG_TO_RAD;
6925
7061
  return { x: a.x + Math.cos(rad) * nextLength, y: a.y - Math.sin(rad) * nextLength };
6926
7062
  }
6927
7063
  function apply2(current, entry) {
@@ -6929,7 +7065,7 @@ function apply2(current, entry) {
6929
7065
  }
6930
7066
  function angleOf(a, h) {
6931
7067
  if (h.x === a.x && h.y === a.y) return 0;
6932
- return Math.atan2(a.y - h.y, h.x - a.x) * 180 / Math.PI;
7068
+ return Math.atan2(a.y - h.y, h.x - a.x) * RAD_TO_DEG;
6933
7069
  }
6934
7070
  function agreed(values) {
6935
7071
  const first = values[0];
@@ -7020,6 +7156,6 @@ function typedArrayReplacer(_key, value) {
7020
7156
  return tagTypedArray(value) ?? value;
7021
7157
  }
7022
7158
 
7023
- export { ANCHOR_FIELDS, CONSTRAINT_TOLERANCE, EASINGS, PATH_SNAP_KINDS, SPRING_PRESETS, TYPED_ARRAY_CONSTRUCTORS, ZOOM_FLOOR, addExtremaAnchors, anchorAt, anchorCount, anchorOriginsOf, anchorSymmetries, anchorSymmetry, anchorsBefore, anchorsInRect, anchorsUnderBrush, anchorsWhere, applyToPoint, arcShape, averageAnchorsAt, balanceFactorToward, bendAlongPath, bendSegment, bendSide, blendPaths, boxFrameOf, boxToBox, circlePath, clampRectWithin, closestOpenEnds, composePath, conflictingConstraints, constrainPathEdit, constrainPathsEdit, constraintAnchors, constraintApplies, constraintFits, convertSegments, cornerAnchors, createPathSnapIndex, cubicBezierEasing, cubicContoursToPath, decomposePath, deleteAnchorsAt, directionMarks, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeLength, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorSet, editAnchorSetTracked, ellipseShape, equalizeSegmentHandles, everyNthAnchor, extendToMeet, extensionPieces, findPathSnap, findPathSnaps, fitAffine, fitCubicThroughDeletion, flatAnchorIndex, flatIndexesAfterReverse, growAnchorSelection, growRegion, handleAlongItsAngle, handleAtItsLength, handleBalances, harmonizeAnchors, hasOpenSubpath, identity, insertAnchorOnSegment, invert, invertAnchorSelection, isSegmentStraight, joinAnchorsAt, joinSubpathEnds, lengthToward, lengthenOpenEnd, lengthenPathEnd, lineShape, linear, locateAnchor, makeHandlesSymmetric, mapConstraintAnchors, mat3, meanScale, measureAnchorPath, measurePath, mergeRegion, moveHandleSymmetric, moveHandleTo, multiply, nearestPathAnchor, normalizeZoom, offsetCornerTraces, openEndAt, openEndRay, openPathEnds, openSubpathAt, pathAnchorPoints, pathAwayFromAnchors, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathCrossings, pathDistanceToPoint, pathEndAnchorIndex, pathEndFrame, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathSegments, pathShape, pathSignedArea, pathThroughPoseFrame, pathToCubicContours, pathToD, placeAlongRun, placeRect, pointAlongPath, pointInPath, polygonContainsPath, polygonIntersectsPath, portControls2 as portControls, portCurvePoints2 as portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, prepareBlend, puckerBloatPath, pxExtent, pxExtentInFrame, readAnchorFields, rectMarkerPath, remapAnchorValues, remapConstraints, remapPointValues, repeatAlongPath, resamplePath, resolveEasing, retractHandles, reverseAnchors, reversePath, reverseSubpaths, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roughenPath, roundRectPath, runLength, runPlaces, runStationAt, scale, scaleDelta, scalePathToBounds, scaleSegmentHandles, screenAngleOf, segmentAt2 as segmentAt, segmentBetween, segmentExtremaTs, segmentsBetweenAnchors, setSubpathStart, shiftTrack, shrinkAnchorSelection, simplifyAnchorsAt, simplifyPath, simplifySpan, simplifySubpath, smoothAnchors, smudgePath, softFalloff, softSelectionBand, softSelectionWeights, solveConstraints, solvePathConstraints, splitByClosure, splitSubpaths, squarePath, standoff, stationsAlongPath, strokeHitTest, subpathEndAt, subpathsOfAnchors, tagTypedArray, tagTypedArrays, trackRun, trackShapeD, trackSlice, transformCoords, transformPath, translate, translateAnchorBy, translateAnchorsSymmetric, translatePath, trimPathAt, typedArrayReplacer, unionBoundsPath, unsatisfiedConstraints, untagTypedArray, viewToMat3, withAnchorPoints, withinPxBox, withinPxRadius, worldEditToStorage, writeAnchorField, zigzagPath };
7159
+ export { ANCHOR_FIELDS, CONSTRAINT_TOLERANCE, EASINGS, PATH_SNAP_KINDS, SPRING_PRESETS, TYPED_ARRAY_CONSTRUCTORS, ZOOM_FLOOR, addExtremaAnchors, anchorAt, anchorCount, anchorOriginsOf, anchorSymmetries, anchorSymmetry, anchorsBefore, anchorsInRect, anchorsUnderBrush, anchorsWhere, applyToPoint, arcShape, averageAnchorsAt, balanceFactorToward, bendAlongPath, bendSegment, bendSide, blendPaths, boxFrameOf, boxToBox, circlePath, clampRectWithin, closestOpenEnds, composePath, conflictingConstraints, constrainPathEdit, constrainPathsEdit, constraintAnchors, constraintApplies, constraintFits, convertSegments, cornerAnchors, createPathSnapIndex, cubicBezierEasing, cubicContoursToPath, curveSpan, decomposePath, deleteAnchorsAt, directionMarks, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeLength, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorSet, editAnchorSetTracked, ellipseShape, equalizeSegmentHandles, everyNthAnchor, extendToMeet, extensionPieces, findPathSnap, findPathSnaps, fitAffine, fitCubicThroughDeletion, flatAnchorIndex, flatIndexesAfterReverse, growAnchorSelection, growRegion, handleAlongItsAngle, handleAtItsLength, handleBalances, harmonizeAnchors, hasOpenSubpath, identity, insertAnchorOnSegment, invert, invertAnchorSelection, isSegmentStraight, joinAnchorsAt, joinSubpathEnds, lengthAtCurve, lengthToward, lengthenOpenEnd, lengthenPathEnd, lineShape, linear, locateAnchor, makeHandlesSymmetric, mapConstraintAnchors, mat3, meanScale, measureAnchorPath, measurePath, mergeRegion, moveHandleSymmetric, moveHandleTo, multiply, nearestPathAnchor, normalizeZoom, offsetCornerTraces, openEndAt, openEndRay, openPathEnds, openSubpathAt, pathAnchorPoints, pathAwayFromAnchors, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathCrossings, pathDistanceToPoint, pathEndAnchorIndex, pathEndFrame, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathLengthTable, pathSegments, pathShape, pathSignedArea, pathThroughPoseFrame, pathToCubicContours, pathToD, placeAlongRun, placeRect, pointAlongPath, pointInPath, polygonContainsPath, polygonIntersectsPath, portControls2 as portControls, portCurvePoints2 as portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, prepareBlend, puckerBloatPath, pxExtent, pxExtentInFrame, readAnchorFields, rectMarkerPath, remapAnchorValues, remapConstraints, remapPointValues, repeatAlongPath, resamplePath, resolveEasing, retractHandles, reverseAnchors, reversePath, reverseSubpaths, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roughenPath, roundRectPath, runLength, runPlaces, runSegments, runStationAt, scale, scaleDelta, scalePathToBounds, scaleSegmentHandles, screenAngleOf, segmentAt2 as segmentAt, segmentBetween, segmentExtremaTs, segmentsBetweenAnchors, setSubpathStart, shiftTrack, shrinkAnchorSelection, simplifyAnchorsAt, simplifyPath, simplifySpan, simplifySubpath, smoothAnchors, smudgePath, softFalloff, softSelectionBand, softSelectionWeights, solveConstraints, solvePathConstraints, splitByClosure, splitSubpaths, squarePath, standoff, stationsAlongPath, strokeHitTest, subpathEndAt, subpathsOfAnchors, tagTypedArray, tagTypedArrays, trackRun, trackShapeD, trackSlice, transformCoords, transformPath, translate, translateAnchorBy, translateAnchorsSymmetric, translatePath, trimPathAt, typedArrayReplacer, unionBoundsPath, unsatisfiedConstraints, untagTypedArray, viewRectToScreen, viewScreenToWorld, viewToMat3, viewWorldToScreen, withAnchorPoints, withinPxBox, withinPxRadius, worldEditToStorage, writeAnchorField, zigzagPath };
7024
7160
  //# sourceMappingURL=index.js.map
7025
7161
  //# sourceMappingURL=index.js.map