@weasel-js/geom 1.10.0 → 1.11.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 (55) hide show
  1. package/README.md +140 -16
  2. package/dist/3d/index.d.ts +29 -2
  3. package/dist/3d/index.js +49 -1
  4. package/dist/3d/index.js.map +1 -1
  5. package/dist/booleans/index.d.ts +34 -10
  6. package/dist/booleans/index.js +344 -352
  7. package/dist/booleans/index.js.map +1 -1
  8. package/dist/{chunk-MDT7F32A.js → chunk-6XSJY3TC.js} +10 -39
  9. package/dist/chunk-6XSJY3TC.js.map +1 -0
  10. package/dist/chunk-7KLJZFBY.js +573 -0
  11. package/dist/chunk-7KLJZFBY.js.map +1 -0
  12. package/dist/chunk-B53BERKG.js +386 -0
  13. package/dist/chunk-B53BERKG.js.map +1 -0
  14. package/dist/chunk-CE7IY7TB.js +104 -0
  15. package/dist/chunk-CE7IY7TB.js.map +1 -0
  16. package/dist/chunk-HGF5OVEN.js +129 -0
  17. package/dist/chunk-HGF5OVEN.js.map +1 -0
  18. package/dist/chunk-J2O2B54M.js +800 -0
  19. package/dist/chunk-J2O2B54M.js.map +1 -0
  20. package/dist/chunk-Q66NA2FU.js +174 -0
  21. package/dist/chunk-Q66NA2FU.js.map +1 -0
  22. package/dist/chunk-SASMXDBE.js +443 -0
  23. package/dist/chunk-SASMXDBE.js.map +1 -0
  24. package/dist/chunk-UI3FUWXM.js +533 -0
  25. package/dist/chunk-UI3FUWXM.js.map +1 -0
  26. package/dist/curvature-PkoLduGc.d.ts +454 -0
  27. package/dist/curves/index.d.ts +14 -5
  28. package/dist/curves/index.js +33 -111
  29. package/dist/curves/index.js.map +1 -1
  30. package/dist/index.d.ts +1697 -205
  31. package/dist/index.js +4729 -1308
  32. package/dist/index.js.map +1 -1
  33. package/dist/nd/index.d.ts +91 -27
  34. package/dist/nd/index.js +282 -0
  35. package/dist/nd/index.js.map +1 -1
  36. package/dist/path-BW8d_2RY.d.ts +157 -0
  37. package/dist/portCurve-C5HDWuuV.d.ts +30 -0
  38. package/dist/tessellate/index.d.ts +18 -4
  39. package/dist/tessellate/index.js +21 -277
  40. package/dist/tessellate/index.js.map +1 -1
  41. package/dist/warp/index.d.ts +252 -0
  42. package/dist/warp/index.js +1316 -0
  43. package/dist/warp/index.js.map +1 -0
  44. package/package.json +5 -1
  45. package/dist/chunk-MDT7F32A.js.map +0 -1
  46. package/dist/chunk-OXH3NOXC.js +0 -62
  47. package/dist/chunk-OXH3NOXC.js.map +0 -1
  48. package/dist/chunk-URQMDR6Q.js +0 -80
  49. package/dist/chunk-URQMDR6Q.js.map +0 -1
  50. package/dist/chunk-WQMYOCFB.js +0 -167
  51. package/dist/chunk-WQMYOCFB.js.map +0 -1
  52. package/dist/chunk-XS6ERH3X.js +0 -34
  53. package/dist/chunk-XS6ERH3X.js.map +0 -1
  54. package/dist/cubicMath-CFXbq6vp.d.ts +0 -116
  55. package/dist/path-Bf9KeVxC.d.ts +0 -44
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { b as PathOpenFill, P as PolygonPath, c as PathFillRule, R as RectPath, a as Path } from './path-Bf9KeVxC.js';
2
- import { P as Point, a as PenAnchor } from './cubicMath-CFXbq6vp.js';
3
- export { b as anchorsToPath, c as countPathAnchors, d as cubicPointAt, f as fitCubicThroughDeletion, i as isAnchorSmooth, e as isStraightSegment, n as nearestSegmentT, p as pathToAnchors, s as segmentEnds, g as splitCubicAtT } from './cubicMath-CFXbq6vp.js';
4
- export { PORT_REACH } from './nd/index.js';
1
+ import { c as PathOpenFill, P as PolygonPath, d as PathFillRule, a as Path, R as RectPath, b as Point, C as CornerSpec, e as CornerPiece } from './path-BW8d_2RY.js';
2
+ export { f as CORNER_FIT_TOLERANCE, g as CORNER_KEYWORDS, h as CornerKeyword, i as CornerShape, j as CornerShapeFrame, k as compactCorners, l as cornerAt, m as cornerCenter, n as cornerKeywordOf, o as cornerShapeK, p as cornerShapePieces, q as cornerShapePoint, r as cornerSpecAt, s as cubicPointAt, t as hasCorners, u as isCornerShape, v as lerpCorners, w as nextCornerShape, x as scaleCorners, y as splitCubicAtT, z as withCorner, A as withoutCorners } from './path-BW8d_2RY.js';
3
+ import { A as AnchorSet, P as PenAnchor, a as SpiroPoint, S as SpiroKind, b as Scale2 } from './curvature-PkoLduGc.js';
4
+ export { c as AnchorRect, d as CombTooth, e as CurvatureCombOptions, C as CurvatureSource, E as EulerFit, f as EulerSegment, g as SPIRO_KINDS, h as SegmentComb, i as SegmentHit, j as SpiroSegment, k as anchorAt, l as anchorCount, m as anchorsInRect, n as anchorsToPath, o as combTeeth, p as countPathAnchors, q as cubicCurvatureAt, r as cubicCurvatureSource, s as cubicDerivativeAt, t as curvatureComb, u as deleteAnchorsAt, v as editAnchorSet, w as eulerAngleAt, x as eulerCurvatureAt, y as eulerCurvatureDerivativesAt, z as eulerPointAt, B as fitEulerSegment, D as flatAnchorIndex, F as insertAnchorOnSegment, G as isAnchorSmooth, H as isSpiroKind, I as isStraightSegment, J as locateAnchor, K as moveHandleTo, L as nearestSegmentT, M as openSubpathAt, N as pathToAnchors, O as placeEulerSegment, Q as pxExtent, R as scaleDelta, T as screenAngleOf, U as segmentAt, V as segmentEnds, W as solveSpiro, X as standoff, Y as translateAnchorBy, Z as withinPxBox, _ as withinPxRadius } from './curvature-PkoLduGc.js';
5
+ export { P as PORT_REACH } from './portCurve-C5HDWuuV.js';
5
6
 
6
7
  /**
7
8
  * Scalar / 2-vector primitives and the kernel-wide epsilon policy.
@@ -70,28 +71,35 @@ declare const ZOOM_FLOOR = 1e-9;
70
71
  declare function normalizeZoom(zoom: number): number;
71
72
 
72
73
  /** `[a, b, c, d, e, f]`: `x' = a·x + c·y + e`, `y' = b·x + d·y + f`. */
73
- type Mat3 = readonly [number, number, number, number, number, number];
74
+ type Affine = readonly [number, number, number, number, number, number];
74
75
  /** The transform that leaves a point where it is. */
75
- declare function identity$1(): Mat3;
76
+ declare function identity$1(): Affine;
76
77
  /** A pure translation by `(tx, ty)`. */
77
- declare function translate(tx: number, ty: number): Mat3;
78
+ declare function translate(tx: number, ty: number): Affine;
78
79
  /** A pure scale about the origin. Scale about another point by composing
79
80
  * translate → scale → inverse translate with `multiply`. */
80
- declare function scale(sx: number, sy: number): Mat3;
81
+ declare function scale(sx: number, sy: number): Affine;
81
82
  /** A pure rotation about the origin, in radians, clockwise in canvas
82
83
  * coordinates (y down). */
83
- declare function rotate(rad: number): Mat3;
84
+ declare function rotate(rad: number): Affine;
84
85
  /** Compose: result applies `n` first, then `m` (m·n). */
85
- declare function multiply$1(m: Mat3, n: Mat3): Mat3;
86
+ declare function multiply$1(m: Affine, n: Affine): Affine;
86
87
  /** Inverse, or null when the matrix is singular, non-finite, or too
87
88
  * ill-conditioned to invert meaningfully at its own scale. */
88
- declare function invert$1(m: Mat3): Mat3 | null;
89
+ declare function invert$1(m: Affine): Affine | null;
89
90
  /** Apply to a point, returning a tuple. Cold-path use; hot loops inline. */
90
- declare function applyToPoint(m: Mat3, x: number, y: number): [number, number];
91
+ declare function applyToPoint(m: Affine, x: number, y: number): [number, number];
91
92
  /** Affine that maps source box (sx,sy,sw,sh) onto destination box (dx,dy,dw,dh). */
92
- declare function boxToBox(sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): Mat3;
93
+ declare function boxToBox(sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): Affine;
93
94
  /** Rotation by `rad` about pivot (cx,cy): translate(c)·rotate·translate(-c). */
94
- declare function rotateAboutPoint(cx: number, cy: number, rad: number): Mat3;
95
+ declare function rotateAboutPoint(cx: number, cy: number, rad: number): Affine;
96
+ /**
97
+ * Apply an affine to an interleaved coord stream, returning a fresh f64
98
+ * buffer. Command codes are unaffected — for a Bezier the transformed control
99
+ * points define the transformed curve exactly (affine invariance), so callers
100
+ * pass `path.coords` straight through and keep `path.commands` as-is.
101
+ */
102
+ declare function transformCoords(coords: ArrayLike<number>, m: Affine): Float64Array;
95
103
 
96
104
  /** Axis-aligned box as [minX, minY, maxX, maxY]. */
97
105
  type Box = readonly [number, number, number, number];
@@ -196,6 +204,9 @@ declare function cubicEvalAt(x0: number, y0: number, x1: number, y1: number, x2:
196
204
  * quadratic endpoints. c1 = q0 + 2/3(ctrl-q0), c2 = q1 + 2/3(ctrl-q1).
197
205
  */
198
206
  declare function elevateQuadraticToCubic(q0x: number, q0y: number, cx: number, cy: number, q1x: number, q1y: number): [number, number, number, number];
207
+ /** The parameters in (0, 1) where one coordinate of a cubic, with control
208
+ * values `p0..p3`, turns: the roots of its derivative. */
209
+ declare function cubicAxisExtremaTs(p0: number, p1: number, p2: number, p3: number): number[];
199
210
  /** Tight AABB of a cubic, evaluating only extrema that lie on the curve. */
200
211
  declare function cubicBounds(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): Box;
201
212
  /** Quadratic Bezier point at parameter t (Bernstein form). */
@@ -291,14 +302,15 @@ declare function flattenQuadratic(x0: number, y0: number, x1: number, y1: number
291
302
  * Like `flattenCubic` but also appends to `arcOut`, for each new flattened
292
303
  * point, its *cumulative distance from the segment start* — not a normalized
293
304
  * fraction. The caller divides each by the segment's total flattened arc
294
- * length to yield t ∈ (0, 1].
305
+ * length to yield t ∈ (0, 1]. `maxTurn`, in radians, also bounds how far each
306
+ * piece's control polygon may turn.
295
307
  */
296
- declare function flattenCubicWithArcLen(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, tolerance: number, out: number[], arcOut: number[]): number;
308
+ declare function flattenCubicWithArcLen(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, tolerance: number, out: number[], arcOut: number[], maxTurn?: number): number;
297
309
  /** Flatten a quadratic bezier into line segments within `tolerance`, appending
298
310
  * points to `out` and their cumulative arc lengths to `arcOut`. The arc
299
311
  * lengths are what lets per-anchor color and dash phase be interpolated
300
312
  * evenly along the curve rather than per segment. */
301
- declare function flattenQuadraticWithArcLen(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, tolerance: number, out: number[], arcOut: number[]): number;
313
+ declare function flattenQuadraticWithArcLen(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, tolerance: number, out: number[], arcOut: number[], maxTurn?: number): number;
302
314
 
303
315
  /**
304
316
  * Even-odd ray-cast point-in-polygon over an interleaved, unclosed contour
@@ -367,6 +379,9 @@ declare class PathBuilder {
367
379
  }
368
380
  /** Construct a `RectPath` (the fast-path subtype). */
369
381
  declare function rectPath(x: number, y: number, width: number, height: number): RectPath;
382
+ /** `path` as a command stream drawing the same outline. A rect becomes its four
383
+ * corners clockwise from `(x, y)`, the order `countPathAnchors` numbers them in. */
384
+ declare function asPolygonPath(path: Path): PolygonPath;
370
385
  /** Build an open polyline from a flat list of points — the same geometry as
371
386
  * {@link polygonFromPoints} without the closing edge. A freehand stroke and a
372
387
  * measurement line want this; a region wants the closed one. */
@@ -413,10 +428,133 @@ declare function linePath(a: {
413
428
  /**
414
429
  * AABB of a `Path`, returned as a `RectPath` for direct reuse with rect-fast-path machinery.
415
430
  * Control points are NOT included directly; they're used only to compute extrema that actually
416
- * lie on the curve. Empty paths (no commands) return a zero-size rect anchored at the origin.
431
+ * lie on the curve. Corner shapes are bounded as they are drawn, expanded. Empty paths (no commands) return a zero-size rect anchored at the origin.
417
432
  */
418
433
  declare function boundsOfPath(path: Path): RectPath;
419
434
 
435
+ /**
436
+ * Corner expansion: a path's corner specs turned into the plain geometry
437
+ * that every reader draws, hits, bounds, and exports.
438
+ */
439
+
440
+ /** Where and how a corner anchor can be cut. */
441
+ interface CornerFrame {
442
+ /** Flat anchor index. */
443
+ index: number;
444
+ /** The anchor. */
445
+ x: number;
446
+ y: number;
447
+ /** Unit direction from the anchor into the wedge the cut is taken from. */
448
+ bisector: Point;
449
+ /** Half the angle between the two edges at the anchor. */
450
+ halfAngle: number;
451
+ /** How far along each edge the stored spec cuts, its setback, after clamping. */
452
+ trim: number;
453
+ /** The longest cut the edges allow while the neighbors keep theirs. */
454
+ maxTrim: number;
455
+ }
456
+ /** Every anchor of `path` that can take a corner, with the cut its spec
457
+ * makes now. */
458
+ declare function cornerFrames(path: PolygonPath): CornerFrame[];
459
+ /** The frame of the corner at flat anchor `index`, or null when that anchor
460
+ * cannot take one. */
461
+ declare function cornerFrameAt(path: PolygonPath, index: number): CornerFrame | null;
462
+ /** A path with its corners expanded, and where its anchors came from. */
463
+ interface ResolvedCorners {
464
+ path: PolygonPath;
465
+ /**
466
+ * For each anchor of `path` (each M, L, C, or Q, as `countPathAnchors`
467
+ * counts them), the anchor of the source path it belongs to, in the same
468
+ * numbering. Every point a corner's cut adds belongs to that corner's
469
+ * anchor. Null when the expansion added none.
470
+ */
471
+ anchorSources: Uint32Array | null;
472
+ }
473
+ /** {@link resolveCorners}, also answering which source anchor each anchor of
474
+ * the result came from, so an array kept per anchor (`vertexColors`) can be
475
+ * carried onto it with {@link remapPerAnchor}. */
476
+ declare function resolveCornersWithSources(path: PolygonPath): ResolvedCorners;
477
+ /**
478
+ * `path` with its corner specs expanded into plain geometry and dropped: each
479
+ * corner is cut `radius` along both edges and spans the cut with its shape's
480
+ * superellipse (`cornerShapePieces`). Each cut is clamped so the cuts on
481
+ * either end of a segment never overlap. A path with no corners comes back
482
+ * as itself, and so does a rect.
483
+ */
484
+ declare function resolveCorners<P extends Path>(path: P): P;
485
+ /** `values`, `stride` numbers per source anchor, as `stride` per anchor of
486
+ * the expanded path that `sources` describes. */
487
+ declare function remapPerAnchor<T>(values: readonly T[], sources: Uint32Array, stride: number): T[];
488
+ /** One corner of a path as its expansion draws it. */
489
+ interface CornerCut {
490
+ /** Flat anchor index. */
491
+ index: number;
492
+ anchor: Point;
493
+ spec: CornerSpec;
494
+ /** Where the cut leaves the edge into the anchor, and where it rejoins the edge out of it. */
495
+ start: Point;
496
+ end: Point;
497
+ /** The outline from `start` to `end`. */
498
+ pieces: CornerPiece[];
499
+ }
500
+ /** Every corner `path` cuts, in path order. */
501
+ declare function cornerCuts(path: PolygonPath): CornerCut[];
502
+
503
+ /**
504
+ * Putting corner specs back on a path an operation rebuilt from expanded
505
+ * geometry. Booleans, trims and cuts work on what a path draws, its corners
506
+ * cut and spanned (`resolveCorners`); wherever a corner's cut comes through
507
+ * whole, it folds back into its anchor and spec, and only a corner the
508
+ * operation cut into or moved stays expanded.
509
+ */
510
+
511
+ /** A corner a rebuilt path may hold expanded: the anchor and spec it folds
512
+ * back into, and the cut it is drawn as, from `start` to `end`. */
513
+ interface CornerTrace {
514
+ anchor: Point;
515
+ spec: CornerSpec;
516
+ start: Point;
517
+ end: Point;
518
+ pieces: readonly CornerPiece[];
519
+ }
520
+ /** How far a point may sit from where a corner draws it and still count as on it. */
521
+ declare const CORNER_RESTORE_TOLERANCE = 0.001;
522
+ /** The corners `path` cuts, as {@link restoreCorners} looks for them. */
523
+ declare function cornerTraces(path: Path): CornerTrace[];
524
+ /**
525
+ * `result` with the corners of `sources` folded back in wherever it still
526
+ * draws them whole: an operation's output, rebuilt from what the sources
527
+ * draw. A corner the operation cut into, or whose edges it moved, stays as
528
+ * the plain geometry it already is.
529
+ */
530
+ declare function keepCorners(sources: readonly Path[], result: PolygonPath, tolerance?: number): PolygonPath;
531
+ /**
532
+ * `result` with each of `traces` it draws whole folded back into its anchor
533
+ * and spec. A trace counts as drawn when a run of anchors goes from one end
534
+ * of its cut to the other along it, the edges either side carry on to its
535
+ * anchor, and the folded corner cuts the same again. Specs `result` already
536
+ * holds are kept.
537
+ */
538
+ declare function restoreCorners(result: PolygonPath, traces: readonly CornerTrace[], tolerance?: number): PolygonPath;
539
+
540
+ /**
541
+ * The corners an offset outline would hold as corners: each of the source's,
542
+ * moved to where its offset edges meet and resized so its cut keeps the
543
+ * same center.
544
+ */
545
+
546
+ /**
547
+ * Where the corners of `path` would sit on its outline grown by `distance`
548
+ * (shrunk by a negative one), for `restoreCorners` to look for. A corner
549
+ * moves with its edges and keeps its center, so its setback changes by
550
+ * `distance / tan(half the corner's angle)`: a round corner at a right angle
551
+ * grows its radius by exactly `distance`, and is then exactly what the offset
552
+ * draws. A shape whose offset is no longer that shape, or a corner between
553
+ * curved edges, draws differently from its trace, so `restoreCorners` leaves
554
+ * it expanded. A corner shrunk to nothing has no trace.
555
+ */
556
+ declare function offsetCornerTraces(path: Path, distance: number): CornerTrace[];
557
+
420
558
  /**
421
559
  * AABB union over a heterogeneous set of `Path` poses. Returns the
422
560
  * envelope as a `RectPath`, or `null` for empty input — analog of
@@ -452,12 +590,12 @@ declare function translatePath(path: Path, dx: number, dy: number): Path;
452
590
  */
453
591
  declare function scalePathToBounds(path: Path, target: RectPath): Path;
454
592
 
455
- /** Apply an affine `Mat3` to a path's geometry. Never mutates the input.
593
+ /** Apply an `Affine` to a path's geometry. Never mutates the input.
456
594
  *
457
595
  * Axis-aligned maps (no rotation/shear: `m[1]` and `m[2]` within 1e-9 of 0) keep a
458
596
  * `RectPath` a rect, normalizing negative extent (mirror). Rotation or
459
597
  * shear promotes the rect's four corners to a `PolygonPath`. */
460
- declare function transformPath(path: Path, m: Mat3): Path;
598
+ declare function transformPath(path: Path, m: Affine): Path;
461
599
 
462
600
  /**
463
601
  * Translation-only compose/decompose for `Path` poses, the analog of
@@ -478,7 +616,8 @@ declare function decomposePath(parent: Path, world: Path): Path;
478
616
  /** Split a path into one path per subpath, cutting at each `M`. A path with a
479
617
  * single subpath comes back as a one-element array. `fillRule` is propagated
480
618
  * unchanged, so even-odd compound paths with interior "holes" lose the hole
481
- * semantics — each subpath becomes its own filled region. */
619
+ * semantics — each subpath becomes its own filled region. Each subpath keeps
620
+ * the corner specs of its own anchors. */
482
621
  declare function splitSubpaths(path: PolygonPath): PolygonPath[];
483
622
 
484
623
  /** Whether any subpath of `path` lacks the `Z` that closes it. */
@@ -513,7 +652,7 @@ declare function pathSignedArea(path: Path): number;
513
652
  /**
514
653
  * `path` with every contour traversed backwards: same region, same curves,
515
654
  * opposite winding. A closed contour keeps its starting point; an open one
516
- * starts from where it used to end.
655
+ * starts from where it used to end. Corner specs stay on their anchors.
517
656
  */
518
657
  declare function reversePath(path: PolygonPath): PolygonPath;
519
658
 
@@ -538,26 +677,60 @@ declare function reversePath(path: PolygonPath): PolygonPath;
538
677
  declare function pathFromD(d: string, onWarn?: (message: string) => void): ReturnType<PathBuilder['build']>;
539
678
 
540
679
  /**
541
- * A station along a path: where it is at a fraction of its length, and which
542
- * way it is heading there.
543
- *
544
- * For anything positioned *along* geometry rather than beside it — a label on a
545
- * routed edge, a tick on a curve, a badge near an arrowhead. Curves are
546
- * flattened first, so the fraction is arc length along the drawn shape rather
547
- * than a curve parameter, which is what makes 0.5 look like the middle.
680
+ * A path measured once by arc length, so many stations along it cost a binary
681
+ * search each rather than a fresh flatten: what `pointAlongPath` answers one
682
+ * fraction at a time, and what anything placing or bending geometry along a
683
+ * path reads many times over.
548
684
  */
549
685
 
550
- /** The answer `pointAlongPath` gives: a point on the path and its heading there. */
686
+ /** A point on a path and its heading there. */
551
687
  interface PathStation {
552
688
  point: Point;
553
689
  /** Unit vector along the path at `point`, pointing toward the end. */
554
690
  tangent: Point;
555
691
  }
556
- /** Options for `pointAlongPath`. */
557
- interface PointAlongPathOptions {
692
+ /** Options for {@link measurePath}. */
693
+ interface MeasurePathOptions {
558
694
  /** Curve flattening tolerance, in world units. Default 0.5. */
559
695
  flattenTolerance?: number;
560
696
  }
697
+ /** A measured path: its length, and the station at any distance along it. */
698
+ interface PathMeasure {
699
+ /** Total arc length, every subpath counted in order. */
700
+ readonly length: number;
701
+ /** True when the path is a single closed contour, so a distance past the
702
+ * end can wrap round to the start. */
703
+ readonly closed: boolean;
704
+ /**
705
+ * The station `s` along the path, `s` clamped to `0..length`.
706
+ *
707
+ * The tangent is the heading of the flattened segment under `s`. With
708
+ * `smooth`, it is instead blended across each segment from the heading at
709
+ * one vertex to the next, so geometry bent along it turns without facets.
710
+ */
711
+ at(s: number, smooth?: boolean): PathStation;
712
+ }
713
+ /**
714
+ * Measure `path` by arc length. Subpaths are measured in order and treated as
715
+ * one run, the way a browser measures a whole `<path>`. Returns `null` for a
716
+ * path with no points; a path of zero length answers at its single point,
717
+ * heading along +X.
718
+ */
719
+ declare function measurePath(path: Path, opts?: MeasurePathOptions): PathMeasure | null;
720
+
721
+ /**
722
+ * A station along a path: where it is at a fraction of its length, and which
723
+ * way it is heading there.
724
+ *
725
+ * For anything positioned *along* geometry rather than beside it — a label on a
726
+ * routed edge, a tick on a curve, a badge near an arrowhead. Curves are
727
+ * flattened first, so the fraction is arc length along the drawn shape rather
728
+ * than a curve parameter, which is what makes 0.5 look like the middle. For
729
+ * many stations along one path, measure it once with `measurePath`.
730
+ */
731
+
732
+ /** Options for `pointAlongPath`. */
733
+ type PointAlongPathOptions = MeasurePathOptions;
561
734
  /**
562
735
  * Where `path` is at `t` of its total length, and its heading there.
563
736
  *
@@ -568,6 +741,144 @@ interface PointAlongPathOptions {
568
741
  */
569
742
  declare function pointAlongPath(path: Path, t: number, opts?: PointAlongPathOptions): PathStation | null;
570
743
 
744
+ /** One cubic segment, `[x0, y0, x1, y1, x2, y2, x3, y3]`, and whether it was
745
+ * drawn straight. A straight one keeps its controls at its thirds, so it
746
+ * interpolates and warps as a line would. */
747
+ interface CubicSegment {
748
+ c: [number, number, number, number, number, number, number, number];
749
+ line: boolean;
750
+ }
751
+ /** One contour of segments, each starting where the one before it ends. */
752
+ interface CubicContour {
753
+ segs: CubicSegment[];
754
+ closed: boolean;
755
+ }
756
+ /**
757
+ * `path`'s contours as cubic segments. A closed contour whose last point
758
+ * stops short of its first gains the straight closing edge it is drawn with;
759
+ * zero-length straight segments are dropped. A contour with no segments is a
760
+ * lone point and is left out. Corner specs are expanded first.
761
+ */
762
+ declare function pathToCubicContours(path: Path): CubicContour[];
763
+ /** The path drawing `contours`: a straight segment as `L`, the rest as `C`. */
764
+ declare function cubicContoursToPath(contours: readonly CubicContour[], fillRule?: PathFillRule): PolygonPath;
765
+
766
+ /**
767
+ * Shape interpolation between two paths: the steps of a blend, or the frames
768
+ * of a morph.
769
+ *
770
+ * The two shapes are matched once — contour to contour, then segment to
771
+ * segment — and every step is a straight lerp of the matched control points.
772
+ * Where one contour has fewer segments, its longest ones are split in half
773
+ * until the counts agree, which adds anchors without moving the outline.
774
+ */
775
+
776
+ /** How a blend matches the second shape's anchors to the first's. */
777
+ interface BlendPathsOptions {
778
+ /**
779
+ * Choose, for each pair of contours, the start point (and, when `reverse`
780
+ * is unset, the direction) of the second that keeps matched anchors
781
+ * closest, with both contours centered on their own middles first so that
782
+ * where the shapes sit does not decide it. Default `true`.
783
+ */
784
+ autoAlign?: boolean;
785
+ /**
786
+ * Turn the second shape's start point this many anchors further round each
787
+ * closed contour, after any `autoAlign` choice. Negative turns it back.
788
+ * Default 0.
789
+ */
790
+ startOffset?: number;
791
+ /** Run the second shape's contours backwards. Unset, `autoAlign` decides;
792
+ * without it, they run as drawn. */
793
+ reverse?: boolean;
794
+ }
795
+ /** Two shapes matched for interpolation. */
796
+ interface PathBlend {
797
+ /** The shape `t` of the way from the first to the second: exactly the
798
+ * first at 0 and the second at 1, up to the anchors matching added. */
799
+ at(t: number): PolygonPath;
800
+ /** Segments per matched pair of contours. */
801
+ readonly segmentCounts: readonly number[];
802
+ }
803
+ /** The shape `t` of the way from `a` to `b`. Matching once with
804
+ * {@link prepareBlend} is cheaper when several steps are wanted. */
805
+ declare function blendPaths(a: Path, b: Path, t: number, opts?: BlendPathsOptions): PolygonPath;
806
+ /** Match `a` to `b` for interpolation. */
807
+ declare function prepareBlend(a: Path, b: Path, opts?: BlendPathsOptions): PathBlend;
808
+
809
+ /**
810
+ * Bend a shape along a spine: the shape's x becomes distance along the spine
811
+ * and its y distance off it, along the spine's normal. A straight edge of the
812
+ * shape comes out curved wherever the spine bends, so each segment is
813
+ * sampled through the warp and refit to cubics within a tolerance; anchors
814
+ * stay where they were, so a corner of the shape stays a corner.
815
+ */
816
+
817
+ /** How a shape is laid along its spine. */
818
+ interface BendAlongPathOptions {
819
+ /**
820
+ * `'stretch'`, the default, spreads the shape's width over the spine's
821
+ * whole length. `'natural'` keeps its width: one unit across the shape is
822
+ * one unit along the spine, starting at the spine's start, and a shape
823
+ * longer than the spine runs on straight past its end.
824
+ */
825
+ fit?: 'stretch' | 'natural';
826
+ /** Where across the shape's height the spine runs, 0 at its top and 1 at
827
+ * its bottom. Default 0.5, the middle. */
828
+ baseline?: number;
829
+ /** Lay the shape on the other side of the spine, mirrored across it. */
830
+ flip?: boolean;
831
+ /** How far the refit curves may stray from the exact bend, in world
832
+ * units. Default 0.5. */
833
+ tolerance?: number;
834
+ }
835
+ /**
836
+ * `shape` bent along `spine`. A spine heading left to right keeps the shape
837
+ * upright; one heading the other way turns it over, as a road sign painted
838
+ * along a lane does. Returns `null` when the spine has no length or the shape
839
+ * nothing to bend.
840
+ */
841
+ declare function bendAlongPath(shape: Path, spine: Path, opts?: BendAlongPathOptions): PolygonPath | null;
842
+
843
+ /** How copies are spaced along a path. */
844
+ interface RepeatAlongPathOptions {
845
+ /**
846
+ * How many copies. With no `spacing`, they spread evenly over the whole
847
+ * path — end to end on an open one, evenly round a closed one — and the
848
+ * default is 5. With a `spacing`, it caps the run; unset, as many as fit.
849
+ */
850
+ count?: number;
851
+ /** Distance along the path from one copy's center to the next. */
852
+ spacing?: number;
853
+ /** Where a spaced run that is shorter than the path sits on it. Default
854
+ * `'start'`. */
855
+ align?: 'start' | 'center' | 'end';
856
+ /** Slide every copy this far along the path. On an open path a copy slid
857
+ * off either end is dropped; round a closed one it wraps. Default 0. */
858
+ offset?: number;
859
+ /** Turn each copy to the path's heading where it sits. Default `true`. */
860
+ orient?: boolean;
861
+ /** Curve flattening tolerance for measuring the path. Default 0.5. */
862
+ flattenTolerance?: number;
863
+ }
864
+ /** Where one copy sits. */
865
+ interface RepeatStation {
866
+ point: Point;
867
+ /** The path's heading there, radians clockwise from +X in a y-down frame. */
868
+ angle: number;
869
+ /** Arc length from the path's start. */
870
+ distance: number;
871
+ }
872
+ /** The stations copies sit at along `path`, in order. Empty for a path with
873
+ * no points. */
874
+ declare function stationsAlongPath(path: Path, opts?: RepeatAlongPathOptions): RepeatStation[];
875
+ /**
876
+ * Copies of `shape`, each centered on a station along `path` — its bounding
877
+ * box's middle on the path — and, with `orient`, turned to the heading
878
+ * there. The shape's own +X runs along the path.
879
+ */
880
+ declare function repeatAlongPath(shape: Path, path: Path, opts?: RepeatAlongPathOptions): Path[];
881
+
571
882
  /**
572
883
  * Pure-geometry distance from a point to a path's boundary (stroke), with
573
884
  * no fill-side logic. Used by pickers that want to know "how close is this
@@ -585,6 +896,326 @@ declare function pointAlongPath(path: Path, t: number, opts?: PointAlongPathOpti
585
896
  * combine with `pathContainsPoint` at the call site. */
586
897
  declare function pathDistanceToPoint(path: Path, px: number, py: number): number;
587
898
 
899
+ /** Where two Bézier segments cross, by subdivision then Newton refinement. */
900
+
901
+ /** One crossing of segments `a` and `b`: the parameter on each, and the point. */
902
+ interface SegmentCrossing {
903
+ ta: number;
904
+ tb: number;
905
+ x: number;
906
+ y: number;
907
+ }
908
+ /**
909
+ * Every point where segment `a` crosses or touches segment `b` — each a line,
910
+ * quadratic or cubic given by its control points — with the parameter on
911
+ * each. Two lines are solved exactly; a curve is subdivided until its pieces
912
+ * are flat, their chords crossed, and the answer refined against the curves.
913
+ * Segments that overlap along a stretch report at most a few points of it.
914
+ * Sorted by `ta`.
915
+ */
916
+ declare function intersectSegments(a: Point[], b: Point[]): SegmentCrossing[];
917
+
918
+ /** A path's stroke as a flat list of segments, each knowing where it sits. */
919
+
920
+ /** One segment of a path's stroke, in the order the path draws them. */
921
+ interface PathSegment {
922
+ /** Control points: 2 for a line, 3 for a quadratic, 4 for a cubic. */
923
+ pts: Point[];
924
+ /** Index of its subpath among the path's subpaths. */
925
+ sub: number;
926
+ /** Index within its subpath. */
927
+ index: number;
928
+ /** Segments in its subpath. */
929
+ count: number;
930
+ /** Whether its subpath is closed, so its last segment meets its first. */
931
+ closed: boolean;
932
+ /** Axis-aligned box of the control points, which holds the segment. */
933
+ box: {
934
+ x0: number;
935
+ y0: number;
936
+ x1: number;
937
+ y1: number;
938
+ };
939
+ }
940
+ /** `path`'s stroke as segments: a closed subpath includes its closing edge,
941
+ * an open one does not, and a zero-length line is left out. */
942
+ declare function pathSegments(path: Path): PathSegment[];
943
+
944
+ /** Where paths cross one another and themselves. */
945
+
946
+ /** A place on a path's stroke: which segment, and how far along it. */
947
+ interface PathStop {
948
+ /** Index of the path in the list it came from. */
949
+ path: number;
950
+ sub: number;
951
+ /** Segment index within the subpath. */
952
+ index: number;
953
+ /** Parameter on that segment, in [0, 1]. */
954
+ t: number;
955
+ }
956
+ /** A point where two strokes cross or touch, and where it falls on each. */
957
+ interface PathCrossing {
958
+ x: number;
959
+ y: number;
960
+ a: PathStop;
961
+ b: PathStop;
962
+ }
963
+ /**
964
+ * Every crossing among `paths`: between each pair, and — unless `self` is
965
+ * false — of a path with itself, leaving out the joint where one segment
966
+ * meets the next. Points where strokes merely touch count too. With `of`,
967
+ * only the crossings involving that one path.
968
+ */
969
+ declare function pathCrossings(paths: readonly Path[], opts?: {
970
+ self?: boolean;
971
+ of?: number;
972
+ }): PathCrossing[];
973
+
974
+ /**
975
+ * Geometric snapping onto paths: the points on a set of strokes a drawn or
976
+ * dragged point can lock to, and which of them a probe lands on.
977
+ */
978
+
979
+ /**
980
+ * What a snap target is.
981
+ *
982
+ * - `anchor` — an end of a segment: a path's anchors, a box's corners.
983
+ * - `center` — a point a caller names as the middle of something: a text or
984
+ * image box's center, given to {@link createPathSnapIndex} as a target.
985
+ * - `intersection` — where two strokes cross, or one crosses itself.
986
+ * - `tangent` — where a line from the query's origin touches a curve.
987
+ * - `perpendicular` — where a line from the origin meets a stroke square on.
988
+ * - `midpoint` — the middle of a segment by length.
989
+ * - `extremum` — on a curve, where its tangent turns horizontal or vertical.
990
+ * - `quadrant` — where a closed subpath crosses the horizontal and vertical
991
+ * lines through the center of its box: a circle's four compass points.
992
+ * - `nearest` — the closest point on any stroke, when nothing else is in range.
993
+ */
994
+ type PathSnapKind = 'anchor' | 'center' | 'intersection' | 'tangent' | 'perpendicular' | 'midpoint' | 'extremum' | 'quadrant' | 'nearest';
995
+ /** Every snap kind, in the order a picker lists them. */
996
+ declare const PATH_SNAP_KINDS: readonly PathSnapKind[];
997
+ /** The kinds whose targets stand whatever the probe. */
998
+ type StaticPathSnapKind = 'anchor' | 'center' | 'intersection' | 'midpoint' | 'extremum' | 'quadrant';
999
+ /** A target the probe landed on. */
1000
+ interface PathSnapHit {
1001
+ kind: PathSnapKind;
1002
+ x: number;
1003
+ y: number;
1004
+ /** Distance from the probe, in the query's `radius` units. */
1005
+ distance: number;
1006
+ /** Indices of the paths that make the target: two for a crossing between
1007
+ * paths, one otherwise. */
1008
+ paths: readonly number[];
1009
+ }
1010
+ /** A query for {@link findPathSnap}. */
1011
+ interface PathSnapQuery {
1012
+ /** How far from the probe a target may be, in the paths' units times
1013
+ * `scale`. */
1014
+ radius: number;
1015
+ /** Per-axis scale from the paths' units to the units `radius` is in — a
1016
+ * view's, to measure the radius in screen pixels under any zoom. Default
1017
+ * `{ x: 1, y: 1 }`. */
1018
+ scale?: {
1019
+ x: number;
1020
+ y: number;
1021
+ };
1022
+ /** Kinds to consider. Absent, all of them. */
1023
+ kinds?: Iterable<PathSnapKind>;
1024
+ /** Where the line being drawn starts. Tangent and perpendicular targets are
1025
+ * measured from it, and there are none without it. */
1026
+ origin?: Point;
1027
+ /** Paths by index that offer no targets — the ones a drag is moving. */
1028
+ skip?: (path: number) => boolean;
1029
+ }
1030
+ /** A target that stands whatever the probe: an anchor, a center, a crossing,
1031
+ * a midpoint, an extremum or a quadrant point. */
1032
+ interface PathSnapTarget {
1033
+ kind: StaticPathSnapKind;
1034
+ x: number;
1035
+ y: number;
1036
+ paths: readonly number[];
1037
+ }
1038
+ /** A set of paths, prepared for repeated snapping. Static targets are
1039
+ * computed on first use and kept. */
1040
+ interface PathSnapIndex {
1041
+ readonly paths: readonly Path[];
1042
+ /** Each path's segments. */
1043
+ readonly segments: readonly (readonly PathSegment[])[];
1044
+ /** The targets that do not depend on the probe, of one kind. */
1045
+ targets(kind: StaticPathSnapKind): readonly PathSnapTarget[];
1046
+ }
1047
+ /** Prepare `paths` for {@link findPathSnap}. Build one per set of paths and
1048
+ * reuse it while they stand: a crossing search is quadratic in segments.
1049
+ * `extra` adds targets of the caller's own — the center of a box, a point
1050
+ * with no segment at it — each naming the path it belongs to. */
1051
+ declare function createPathSnapIndex(paths: readonly Path[], extra?: readonly PathSnapTarget[]): PathSnapIndex;
1052
+ /**
1053
+ * The snap target nearest `probe` within `query.radius`, or null when none
1054
+ * is in range. Every enabled kind competes on distance, except `nearest`,
1055
+ * which answers only when no other target is in range.
1056
+ */
1057
+ declare function findPathSnap(index: PathSnapIndex, probe: Point, query: PathSnapQuery): PathSnapHit | null;
1058
+ /**
1059
+ * Every snap target within `query.radius` of `probe`, unsorted. `nearest`
1060
+ * hits appear only when no other target is in range. Tangent and perpendicular targets
1061
+ * closer to the origin than the radius are dropped, so a line cannot snap
1062
+ * back onto its own start.
1063
+ */
1064
+ declare function findPathSnaps(index: PathSnapIndex, probe: Point, query: PathSnapQuery): PathSnapHit[];
1065
+
1066
+ /**
1067
+ * Trim: remove the piece of a stroke between the crossings on either side of
1068
+ * a point, as a drafting trim does.
1069
+ */
1070
+
1071
+ /** Options for {@link trimPathAt}. */
1072
+ interface TrimPathOptions {
1073
+ /** How far from the stroke `probe` may be. Default: any distance. */
1074
+ radius?: number;
1075
+ }
1076
+ /** What {@link trimPathAt} did. */
1077
+ interface TrimResult {
1078
+ /** The path with the piece gone; it has no commands when nothing is left. */
1079
+ path: PolygonPath;
1080
+ /** The piece removed, as an open path. */
1081
+ removed: PolygonPath;
1082
+ }
1083
+ /**
1084
+ * Remove the piece of `path`'s stroke that `probe` is nearest, bounded by the
1085
+ * nearest crossings on either side — with any of `cutters`, or with the path
1086
+ * itself. On an open subpath an end with no crossing before it bounds the
1087
+ * piece too, so a subpath touched only at its ends goes whole; a closed
1088
+ * subpath is opened, and one crossed at a single point is removed whole.
1089
+ *
1090
+ * Returns null when the probe is further than `radius` from the stroke, or
1091
+ * when nothing crosses or touches the subpath it is on. Curves are split where they are
1092
+ * cut and keep their type; other subpaths are left as they were.
1093
+ */
1094
+ declare function trimPathAt(path: Path, cutters: readonly Path[], probe: Point, opts?: TrimPathOptions): TrimResult | null;
1095
+
1096
+ /** What stays put of a path while some of its anchors move. */
1097
+ interface PathAwayFromAnchors {
1098
+ /** The segments touching none of the moving anchors, as open runs; a
1099
+ * subpath none of them is on stays as it was. */
1100
+ path: PolygonPath;
1101
+ /** Every anchor that is not moving, a lone one with no segment left at it
1102
+ * included. */
1103
+ anchors: Point[];
1104
+ }
1105
+ /**
1106
+ * The part of `path` that holds still while the anchors at `moving` (flat
1107
+ * indices, as anchor editing numbers them) are dragged: every segment with a
1108
+ * moving anchor at either end is dropped, and so are the moving anchors.
1109
+ */
1110
+ declare function pathAwayFromAnchors(path: PolygonPath, moving: Iterable<number>): PathAwayFromAnchors;
1111
+
1112
+ /**
1113
+ * Growing an open end of a path along its tangent: by a distance, or until it
1114
+ * meets another stroke.
1115
+ */
1116
+
1117
+ /** One end of an open subpath. `sub` counts subpaths as the path's anchor set
1118
+ * (`pathToAnchors`) does, a bare `M` included. */
1119
+ interface OpenPathEnd {
1120
+ sub: number;
1121
+ end: 'first' | 'last';
1122
+ }
1123
+ /** Every end of every open subpath of `path` that has a direction to grow in. */
1124
+ declare function openPathEnds(path: Path): OpenPathEnd[];
1125
+ /** Where `end` sits and the unit direction it points out along, or null when
1126
+ * it is no open end or has no direction there. */
1127
+ declare function openEndRay(path: Path, end: OpenPathEnd): {
1128
+ at: Point;
1129
+ dir: Point;
1130
+ } | null;
1131
+ /**
1132
+ * `path` with `end` carried `distance` further along its tangent, by
1133
+ * `lengthenPathEnd` in `'tangent'` mode: a straight end segment grows longer,
1134
+ * and a curved one gains a straight segment, which keeps the tangent
1135
+ * continuous. Null when the end is not open or has no direction, or
1136
+ * `distance` is not positive.
1137
+ */
1138
+ declare function lengthenOpenEnd(path: Path, end: OpenPathEnd, distance: number): PolygonPath | null;
1139
+ /** Options for {@link extendToMeet}. */
1140
+ interface ExtendToMeetOptions {
1141
+ /** The furthest the end may travel. Default: as far as any stroke reaches. */
1142
+ maxDistance?: number;
1143
+ }
1144
+ /**
1145
+ * Extend `end` of `path` along its tangent to the first stroke it meets —
1146
+ * one of `boundaries`, or `path`'s own other segments. Returns the extended
1147
+ * path, the point it now ends at and how far it went, or null when the
1148
+ * tangent meets nothing.
1149
+ */
1150
+ declare function extendToMeet(path: Path, boundaries: readonly Path[], end: OpenPathEnd, opts?: ExtendToMeetOptions): {
1151
+ path: PolygonPath;
1152
+ at: Point;
1153
+ distance: number;
1154
+ } | null;
1155
+
1156
+ /**
1157
+ * Grow or trim an open subpath at one of its ends. Lengthen drags this, and
1158
+ * Extend to next path reaches it through `lengthenOpenEnd`.
1159
+ *
1160
+ * A positive distance extends the end outward: straight along the end tangent,
1161
+ * or along a circular arc with the curvature the end has, which meets the path
1162
+ * with matching direction and curvature. A straight end segment is lengthened
1163
+ * in place; a curved one is continued with a new segment. A negative distance
1164
+ * trims the path back along itself by arc length, whichever way it would have
1165
+ * grown.
1166
+ *
1167
+ * The arc rather than extrapolating the end cubic past t = 1: a cubic's
1168
+ * leading term takes over beyond its range, so a continued curve swings off
1169
+ * and curls on itself within a segment's length, while the arc keeps the
1170
+ * curvature the end shows for as long as it is drawn.
1171
+ */
1172
+
1173
+ /** How `lengthenPathEnd` grows an end: straight on, or bending as the end bends. */
1174
+ type LengthenMode = 'tangent' | 'curvature';
1175
+ /** Where an end is, which way it heads out of the path, and how it bends there. */
1176
+ interface PathEndFrame {
1177
+ point: Point;
1178
+ /** Unit vector pointing out of the path. */
1179
+ tangent: Point;
1180
+ /** Signed curvature along `tangent`: positive turns toward `(-tangent.y, tangent.x)`. */
1181
+ curvature: number;
1182
+ }
1183
+ /** The end of an open subpath that the anchor at `flatIndex` is, or null when it is
1184
+ * an inner anchor, on a closed subpath, or alone in its subpath. */
1185
+ declare function openEndAt(set: AnchorSet, flatIndex: number): OpenPathEnd | null;
1186
+ /** Flat index of the anchor at `end`, or -1 when there is none. */
1187
+ declare function pathEndAnchorIndex(set: AnchorSet, end: OpenPathEnd): number;
1188
+ /** The frame at `end`, or null when it is not the end of an open subpath of two or
1189
+ * more anchors, or the path has no direction there. */
1190
+ declare function pathEndFrame(set: AnchorSet, end: OpenPathEnd): PathEndFrame | null;
1191
+ /**
1192
+ * Lengthen `end` by `distance` (see the module comment), in place. A curvature arc
1193
+ * stops at one full turn. Returns false when `end` is not the end of an open
1194
+ * subpath or nothing changed.
1195
+ */
1196
+ declare function lengthenPathEnd(set: AnchorSet, end: OpenPathEnd, distance: number, mode: LengthenMode): boolean;
1197
+ /**
1198
+ * The distance `lengthenPathEnd` takes for its new end to land nearest `point`: the
1199
+ * point projected onto the end tangent, or for a curvature lengthening, measured
1200
+ * around the circle of curvature. A measurement around the circle can name the
1201
+ * same spot a turn apart; it answers the one nearest `near`, so a drag passing
1202
+ * half a turn keeps going instead of jumping back.
1203
+ */
1204
+ declare function lengthToward(set: AnchorSet, end: OpenPathEnd, mode: LengthenMode, point: Point, near?: number): number;
1205
+
1206
+ /**
1207
+ * Controls for the one cubic that replaces `prev → removed → next` when
1208
+ * `removed` is deleted. The new controls keep the directions the curve leaves
1209
+ * `prev` and arrives at `next` with, and their lengths are least-squares fitted
1210
+ * to samples of the two segments being replaced, so the curve keeps its shape
1211
+ * as closely as one cubic can. Where `removed` was inserted by splitting a
1212
+ * cubic, the original cubic comes back.
1213
+ */
1214
+ declare function fitCubicThroughDeletion(prev: PenAnchor, removed: PenAnchor, next: PenAnchor): {
1215
+ c1: Point;
1216
+ c2: Point;
1217
+ };
1218
+
588
1219
  /**
589
1220
  * Schneider (1990, Graphics Gems I) adaptive cubic-Bezier fitter.
590
1221
  * Given a sequence of sample points, returns a `PolygonPath` whose `C`
@@ -595,6 +1226,275 @@ declare function pathDistanceToPoint(path: Path, px: number, py: number): number
595
1226
  * degenerate cubic. For empty input, returns an empty polygon path.
596
1227
  */
597
1228
  declare function schneiderFit(samples: ReadonlyArray<Point>, errorTolerance: number): PolygonPath;
1229
+ /**
1230
+ * The single cubic from the first sample to the last that best fits the
1231
+ * samples between, with its control points held to the given unit tangents:
1232
+ * `tStart` leaving the first sample, `tEnd` leaving the last one backward.
1233
+ * Only the two handle lengths are solved for.
1234
+ *
1235
+ * `params` is the curve parameter each sample starts at, chord length when
1236
+ * omitted; `passes` is how many times those are refined and the lengths
1237
+ * solved again.
1238
+ */
1239
+ declare function fitCubicWithTangents(samples: ReadonlyArray<Point>, tStart: Point, tEnd: Point, opts?: {
1240
+ params?: ReadonlyArray<number>;
1241
+ passes?: number;
1242
+ }): {
1243
+ cp1: Point;
1244
+ cp2: Point;
1245
+ };
1246
+
1247
+ /** Options for {@link simplifyPath}. */
1248
+ interface SimplifyPathOptions {
1249
+ /**
1250
+ * An anchor whose path turns by more than this many degrees is a corner:
1251
+ * it is kept, and the curve is refitted on either side of it. Default 40.
1252
+ */
1253
+ cornerAngle?: number;
1254
+ }
1255
+ /**
1256
+ * `path` refitted with as few anchors as hold it within `tolerance`: every
1257
+ * point sampled along the original lies within `tolerance` of the result.
1258
+ * Corners (see {@link SimplifyPathOptions.cornerAngle}) and the ends of open
1259
+ * subpaths stay where they are; the runs between them are refitted as smooth
1260
+ * cubics, Schneider-style, and a run that lies within `tolerance` of a line
1261
+ * becomes one straight segment. Fill rules carry over.
1262
+ */
1263
+ declare function simplifyPath(path: PolygonPath, tolerance: number, opts?: SimplifyPathOptions): PolygonPath;
1264
+ /** One subpath of {@link simplifyPath}: its anchors refitted, corners and
1265
+ * open ends kept. */
1266
+ declare function simplifySubpath(sub: PenAnchor[], closed: boolean, tolerance: number, opts?: SimplifyPathOptions): PenAnchor[];
1267
+ /**
1268
+ * The anchors of `sub` from index `from` to `to`, refitted the way
1269
+ * {@link simplifyPath} refits a subpath: as few as hold the span within
1270
+ * `tolerance`, corners inside it kept. Indices run modulo the subpath's
1271
+ * length, so on a closed subpath `to` may pass its end and wrap; the span is
1272
+ * shorter than one full turn. The two end anchors stay where they are and
1273
+ * keep their handles facing out of the span. Answers the anchors from `from`
1274
+ * to `to`, both included, to splice in place of the originals.
1275
+ */
1276
+ declare function simplifySpan(sub: PenAnchor[], closed: boolean, from: number, to: number, tolerance: number, opts?: SimplifyPathOptions): PenAnchor[];
1277
+
1278
+ /** Options for {@link growRegion}. */
1279
+ interface GrowRegionOptions {
1280
+ /** How a convex corner turns. `'miter'` keeps it sharp, as a pseudo-distance
1281
+ * field draws it; `'round'` follows the true distance. Default `'miter'`. */
1282
+ join?: 'miter' | 'round';
1283
+ /** Past this ratio of miter length to distance a miter corner is beveled.
1284
+ * Default 4. */
1285
+ miterLimit?: number;
1286
+ /** How far a grown curve may stray from the true offset, in the path's
1287
+ * units. Default 0.1. */
1288
+ tolerance?: number;
1289
+ }
1290
+ /**
1291
+ * The region `path` fills under `'nonzero'`, grown by `distance` (> 0), as a
1292
+ * path that fills it under `'nonzero'`. Every subpath counts as closed. Each
1293
+ * contour is replaced by its offset, so where grown parts meet the contours
1294
+ * overlap rather than merge, and a hole grown shut winds back on itself and
1295
+ * fills. At an inside corner the two offset edges are cut back to where they
1296
+ * cross; where they do not, the gap is passed through the corner's original
1297
+ * point, which closes it with a loop inside the fill.
1298
+ *
1299
+ * What it costs: one segment per curve, more where `tolerance` needs the curve
1300
+ * split; a miter corner between two straight edges costs nothing, one beside
1301
+ * a curve gains an anchor or two, a round corner an arc, and an inside corner
1302
+ * whose edges do not cross two.
1303
+ */
1304
+ declare function growRegion(path: Path, distance: number, opts?: GrowRegionOptions): PolygonPath;
1305
+
1306
+ /**
1307
+ * Simplify only where `flatIndexes` say: each unbroken run of those anchors
1308
+ * along a subpath is refitted as one span by the fitter {@link simplifyPath}
1309
+ * uses (`simplifySpan`), so only anchors the shape does not need, within
1310
+ * `tolerance`, go. A run's first and last anchors stay, and every anchor
1311
+ * outside a run is left as it was. A closed subpath named whole is simplified
1312
+ * whole. Answers whether any anchor went; a run the fit cannot thin is left
1313
+ * untouched.
1314
+ */
1315
+ declare function simplifyAnchorsAt(set: AnchorSet, flatIndexes: Iterable<number>, tolerance: number, opts?: SimplifyPathOptions): boolean;
1316
+
1317
+ /**
1318
+ * Resample: a path's anchors spread evenly along it by arc length, and its
1319
+ * curves refitted through them.
1320
+ */
1321
+
1322
+ /** Options for {@link resamplePath}. */
1323
+ interface ResamplePathOptions {
1324
+ /** How many anchors the result holds across all its subpaths. Default: as
1325
+ * many as `path` has. Ignored when `spacing` is given. */
1326
+ count?: number;
1327
+ /** The distance along the path between neighboring anchors, in path units.
1328
+ * Each run between fixed anchors is cut into the whole number of equal
1329
+ * pieces nearest this. */
1330
+ spacing?: number;
1331
+ /** Keep every corner anchor (`isAnchorSmooth` false) where it is, with its
1332
+ * corner, and resample the runs between. Default `true`. Open ends are
1333
+ * kept either way. */
1334
+ keepCorners?: boolean;
1335
+ /** How far the result may stray from the original, in path units. A piece
1336
+ * that cannot follow the original this closely is halved, so the result can
1337
+ * hold more anchors than `count` asks for. Default 0.5. */
1338
+ tolerance?: number;
1339
+ }
1340
+ /**
1341
+ * `path` with its anchors spread evenly along each run between fixed anchors
1342
+ * ({@link ResamplePathOptions}), each new anchor heading the way the original
1343
+ * does there. Anchors that were smooth stay smooth, and the curves between
1344
+ * are fitted to the original. A closed subpath with no fixed anchor keeps
1345
+ * its first one as the place the even spacing starts from. Fill rules carry
1346
+ * over.
1347
+ */
1348
+ declare function resamplePath(path: PolygonPath, opts?: ResamplePathOptions): PolygonPath;
1349
+
1350
+ /** The parameters in (0, 1) where the segment from `a` to `b` turns in x or
1351
+ * y, ascending, or none for a straight segment. */
1352
+ declare function segmentExtremaTs(a: PenAnchor, b: PenAnchor): number[];
1353
+ /**
1354
+ * Split every curved segment of `set` at its extremes
1355
+ * ({@link segmentExtremaTs}), in place. `include(sub, segIdx)` limits it to
1356
+ * some segments, named as `segmentEnds` names them; absent, every one.
1357
+ * Returns the new anchors' flat indices in the edited set. Each new anchor is
1358
+ * smooth, and every anchor after one moves up an index.
1359
+ */
1360
+ declare function addExtremaAnchors(set: AnchorSet, include?: (sub: number, segIdx: number) => boolean): number[];
1361
+
1362
+ /**
1363
+ * Anchor selections walked along the path: grown or shrunk by one anchor,
1364
+ * every Nth anchor, anchors by kind, and the complement. Each takes the
1365
+ * decoded set and a selection as flat indices, and returns a new selection
1366
+ * as sorted flat indices; none of them edits the set.
1367
+ *
1368
+ * "Along the path" means within a subpath: an anchor's neighbors are the
1369
+ * anchors before and after it there, and a closed subpath's first and last
1370
+ * anchors are neighbors.
1371
+ */
1372
+
1373
+ /** `selected` with each selected anchor's neighbors along the path added. */
1374
+ declare function growAnchorSelection(set: AnchorSet, selected: Iterable<number>): number[];
1375
+ /**
1376
+ * `selected` less every anchor at the edge of a selected run: one with a
1377
+ * neighbor that is unselected, or none on one side, as an open subpath's ends
1378
+ * have. A closed subpath selected all the way round has no edge and stays.
1379
+ */
1380
+ declare function shrinkAnchorSelection(set: AnchorSet, selected: Iterable<number>): number[];
1381
+ /**
1382
+ * Every `n`th anchor along each subpath, counting from its first selected
1383
+ * anchor. With nothing selected, every subpath counts from its first anchor;
1384
+ * otherwise only subpaths holding a selected anchor take part. A closed
1385
+ * subpath is walked once round, so the count does not wrap onto itself.
1386
+ */
1387
+ declare function everyNthAnchor(set: AnchorSet, n: number, selected?: Iterable<number>): number[];
1388
+ /** Every anchor `keep` says yes to. */
1389
+ declare function anchorsWhere(set: AnchorSet, keep: (anchor: PenAnchor) => boolean): number[];
1390
+ /** Every corner anchor: one whose handles are not collinear through it, or
1391
+ * that lacks one (`isAnchorSmooth` false). */
1392
+ declare function cornerAnchors(set: AnchorSet): number[];
1393
+ /** Every smooth anchor (`isAnchorSmooth`). */
1394
+ declare function smoothAnchors(set: AnchorSet): number[];
1395
+ /** Every anchor `selected` leaves out. */
1396
+ declare function invertAnchorSelection(set: AnchorSet, selected: Iterable<number>): number[];
1397
+
1398
+ /**
1399
+ * Average and Join: anchor-set edits that bring anchors together. Both mutate
1400
+ * the model `pathToAnchors` produces in place, like the edits in
1401
+ * `anchorEdits`, and address anchors by flat index.
1402
+ */
1403
+
1404
+ /** Which coordinates {@link averageAnchorsAt} averages: `'x'` lines the
1405
+ * anchors up on one vertical, `'y'` on one horizontal, `'both'` stacks them
1406
+ * on one point. */
1407
+ type AverageAxis = 'x' | 'y' | 'both';
1408
+ /** One end of an open subpath. */
1409
+ interface SubpathEnd {
1410
+ sub: number;
1411
+ end: 'first' | 'last';
1412
+ }
1413
+ /** How {@link joinSubpathEnds} spans the gap between two ends apart. */
1414
+ interface JoinOptions {
1415
+ /** Absent, a straight segment. Given, a curve leaving each end along the
1416
+ * direction its path was heading there, each handle `tension` times the
1417
+ * gap long; `0` is a straight segment. */
1418
+ tension?: number;
1419
+ }
1420
+ /**
1421
+ * Move each anchor in `flatIndexes` to their mean position, along `axis`.
1422
+ * Each anchor carries its handles with it. Returns false, changing nothing,
1423
+ * when fewer than two of the indexes name anchors.
1424
+ */
1425
+ declare function averageAnchorsAt(set: AnchorSet, flatIndexes: Iterable<number>, axis?: AverageAxis): boolean;
1426
+ /** The open-subpath end anchor `flatIndex` names, or null for an interior
1427
+ * anchor or one on a closed subpath. A lone anchor reads as `'last'`. */
1428
+ declare function subpathEndAt(set: AnchorSet, flatIndex: number): SubpathEnd | null;
1429
+ /**
1430
+ * Join the two open-subpath ends `a` and `b`. The two ends of one subpath
1431
+ * close it; ends of two subpaths make one subpath, `a`'s run followed by
1432
+ * `b`'s, in the slot of whichever came first. Ends that coincide merge into
1433
+ * one anchor, keeping the handle on either side; ends apart are bridged by a
1434
+ * straight segment, or with `options.tension` by a curve meeting each end
1435
+ * tangentially. Returns false, changing nothing, when the ends are not two
1436
+ * distinct ends of open subpaths.
1437
+ */
1438
+ declare function joinSubpathEnds(set: AnchorSet, a: SubpathEnd, b: SubpathEnd, options?: JoinOptions): boolean;
1439
+ /** {@link joinSubpathEnds} for two anchors named by flat index, each the end
1440
+ * of an open subpath. */
1441
+ declare function joinAnchorsAt(set: AnchorSet, flatA: number, flatB: number, options?: JoinOptions): boolean;
1442
+ /**
1443
+ * The two open-subpath ends nearest each other that `accept` allows, on two
1444
+ * different subpaths. Null when there are no two such subpaths.
1445
+ */
1446
+ declare function closestOpenEnds(set: AnchorSet, accept?: (a: SubpathEnd, b: SubpathEnd) => boolean): [SubpathEnd, SubpathEnd] | null;
1447
+
1448
+ /**
1449
+ * Spiro curves as cubic paths, and back. A solved curve is written as cubics,
1450
+ * each spiral segment split into as many as it takes to stay within tolerance,
1451
+ * so most anchors of the path are not spiro points. `at` records which are:
1452
+ * `at[i]` is the flat anchor index of point `i`, counted across subpaths. The
1453
+ * points are then read back out of the path itself, so whatever moves the path
1454
+ * — a move, a resize, a rotation — moves them with it.
1455
+ */
1456
+
1457
+ /** One subpath of a spiro curve: its points, and whether it closes. */
1458
+ interface SpiroContour {
1459
+ points: SpiroPoint[];
1460
+ closed: boolean;
1461
+ }
1462
+ /** A spiro curve written as a cubic path, with the anchor each point became. */
1463
+ interface SpiroPath {
1464
+ path: PolygonPath;
1465
+ /** The flat anchor index of each point, in contour order. */
1466
+ at: number[];
1467
+ }
1468
+ /** Options for {@link spiroToPath}. */
1469
+ interface SpiroToPathOptions {
1470
+ /** The furthest a cubic may stray from its spiral, as a fraction of that
1471
+ * segment's arc length. Default `1e-4`. */
1472
+ tolerance?: number;
1473
+ /** The path being reshaped, whose fill rules the result keeps. */
1474
+ like?: PolygonPath;
1475
+ }
1476
+ /**
1477
+ * Solve each contour and write it as a cubic path. Straight segments carry no
1478
+ * handles.
1479
+ */
1480
+ declare function spiroToPath(contours: readonly SpiroContour[], options?: SpiroToPathOptions): SpiroPath;
1481
+ /**
1482
+ * The spiro points `path` holds, given the kind of each and the anchor each
1483
+ * became. Null when `kinds` and `at` do not describe `path`: the counts
1484
+ * differ, an index is out of order or out of range, or a subpath does not
1485
+ * start on a point.
1486
+ */
1487
+ declare function spiroContoursOf(path: PolygonPath, kinds: readonly SpiroKind[], at: readonly number[]): SpiroContour[] | null;
1488
+ /** The straight-sided path through each contour's points: what an editor
1489
+ * shows and moves in place of the curve. */
1490
+ declare function spiroControlPath(contours: readonly SpiroContour[]): PolygonPath;
1491
+ /**
1492
+ * Read an edited control path back as contours: every anchor is a point.
1493
+ * Kinds follow from `before`, the contours the edit started from: point for
1494
+ * point when the count is unchanged, else by position, with an anchor that
1495
+ * lands nowhere a point was taking `fallback`. Handles are ignored.
1496
+ */
1497
+ declare function spiroContoursFromControl(control: PolygonPath, before: readonly SpiroContour[], fallback?: SpiroKind): SpiroContour[];
598
1498
 
599
1499
  /**
600
1500
  * Path hit-testing: path-vs-point (filled region and stroke distance),
@@ -630,7 +1530,7 @@ interface StrokeHitTestOptions {
630
1530
  * transform no world distance equals it. */
631
1531
  slop?: {
632
1532
  px: number;
633
- transform: Mat3;
1533
+ transform: Affine;
634
1534
  };
635
1535
  }
636
1536
  /** Filled-region hit-test for a path. Rect short-circuits to AABB; polygons run ray-cast / winding per `fillRule`. */
@@ -669,12 +1569,13 @@ declare function polygonContainsPath(polygon: readonly Point[], path: Path, opts
669
1569
  declare function polygonIntersectsPath(polygon: readonly Point[], path: Path, opts?: PolygonIntersectsPathOptions): boolean;
670
1570
 
671
1571
  /**
672
- * Apply an affine to an interleaved coord stream, returning a fresh f64
673
- * buffer. Command codes are unaffected — for a Bezier the transformed control
674
- * points define the transformed curve exactly (affine invariance), so callers
675
- * pass `path.coords` straight through and keep `path.commands` as-is.
1572
+ * The affine map carrying the points of `from` closest to those of `to`, by
1573
+ * least squares; both are interleaved `x, y` streams of the same length. When
1574
+ * the points lie on one line an affine map is not determined, and the
1575
+ * similarity (turn, even scale and move) is returned instead. Null for fewer
1576
+ * than two distinct points.
676
1577
  */
677
- declare function transformCoords(coords: ArrayLike<number>, m: Mat3): Float64Array;
1578
+ declare function fitAffine(from: ArrayLike<number>, to: ArrayLike<number>): Affine | null;
678
1579
 
679
1580
  /**
680
1581
  * Ergonomic builders for tiny shape paths used by chrome / overlays / markers.
@@ -730,9 +1631,9 @@ declare function translated(m: GlMat3, tx: number, ty: number): GlMat3;
730
1631
  declare function scaled(m: GlMat3, sx: number, sy: number): GlMat3;
731
1632
  /** This layout read as the kernel's 6-element affine. The two carry the same
732
1633
  * logical order, so the conversion is a repack and nothing else. */
733
- declare function toAffine(m: GlMat3): Mat3;
1634
+ declare function toAffine(m: GlMat3): Affine;
734
1635
  /** The kernel's affine in this layout. The inverse of {@link toAffine}. */
735
- declare function fromAffine(a: Mat3): GlMat3;
1636
+ declare function fromAffine(a: Affine): GlMat3;
736
1637
  /** Inverse of an affine matrix, or `null` when `@weasel-js/geom`'s `invert`
737
1638
  * finds it singular — the same rule, read through this layout. */
738
1639
  declare function invert(m: GlMat3): GlMat3 | null;
@@ -757,7 +1658,7 @@ declare function meanScaleOf(m: GlMat3): number;
757
1658
  declare function minScaleOf(m: GlMat3): number;
758
1659
  /** The renderer's 3x3 matrix operations, as one namespace. These work on the
759
1660
  * 9-element form the GL uniform upload wants — distinct from the 6-element
760
- * affine `Mat3`, though the logical element order is the same. */
1661
+ * `Affine`, though the logical element order is the same. */
761
1662
  declare const mat3: {
762
1663
  identity: typeof identity;
763
1664
  toAffine: typeof toAffine;
@@ -996,77 +1897,6 @@ declare function meanScale(s: {
996
1897
  y: number;
997
1898
  }): number;
998
1899
 
999
- /**
1000
- * Screen-pixel lengths in world units, per axis.
1001
- *
1002
- * Chrome declares its hit zones in screen pixels — an 8px handle stays 8px at
1003
- * every zoom, because it paints in screen space. Converting one such length to
1004
- * world units with a single scalar (see `meanScale`) is exact only under
1005
- * uniform zoom; per-axis it is too generous on one axis and too mean on the
1006
- * other, so the pickable region stops matching the painted one.
1007
- *
1008
- * Prefer {@link withinPxBox} / {@link withinPxRadius}, which compare in screen
1009
- * space and so can't drift from the paint at all. {@link pxExtent} is for the
1010
- * cases that must stay in world units — a tolerance handed to geometry that
1011
- * doesn't know about the view.
1012
- */
1013
- interface Scale2 {
1014
- x: number;
1015
- y: number;
1016
- }
1017
- /** One screen-pixel length as world-space extents, per axis. `scale` is a
1018
- * view's, so neither axis is zero (see `View`).
1019
- *
1020
- * With `rotation`, the axes are a frame turned that far from world, and each
1021
- * extent is how far along it an edge across it must move to move `px` on
1022
- * screen — which, under non-uniform zoom, is not the extent along the screen
1023
- * axis nearest to it. */
1024
- declare function pxExtent(px: number, scale: Scale2, rotation?: number): {
1025
- x: number;
1026
- y: number;
1027
- };
1028
- /** World delta → screen delta. Only the scale participates: translation
1029
- * cancels in a difference, and the kit's views carry no skew. */
1030
- declare function scaleDelta(dx: number, dy: number, scale: Scale2): {
1031
- x: number;
1032
- y: number;
1033
- };
1034
- /**
1035
- * Is a world-space delta inside a screen-space square of half-extent `px`?
1036
- *
1037
- * The square is axis-aligned **on screen**, which is what a handle painted in
1038
- * screen space actually is — so this stays true under non-uniform zoom and
1039
- * under a rotated target, where an axis-aligned world-space test is a rotated
1040
- * rectangle on screen.
1041
- */
1042
- declare function withinPxBox(dx: number, dy: number, px: number, scale: Scale2): boolean;
1043
- /** Is a world-space delta inside a screen-space circle of radius `px`? */
1044
- declare function withinPxRadius(dx: number, dy: number, px: number, scale: Scale2): boolean;
1045
- /**
1046
- * A world point pushed `px` screen pixels off an edge, along the edge's normal
1047
- * as it lands on screen. `normal` is the edge's outward normal in world space.
1048
- *
1049
- * Scaling the world normal by the view is not the same thing: under
1050
- * non-uniform zoom the image of a normal is no longer perpendicular to the
1051
- * image of its edge, so chrome placed that way drifts sideways and sits closer
1052
- * than `px`. Returns the point in world coords and the on-screen unit normal.
1053
- */
1054
- declare function standoff(anchor: {
1055
- x: number;
1056
- y: number;
1057
- }, normal: {
1058
- x: number;
1059
- y: number;
1060
- }, px: number, scale: Scale2): {
1061
- x: number;
1062
- y: number;
1063
- nx: number;
1064
- ny: number;
1065
- };
1066
- /** The screen angle a frame turned `rotation` from world lands its x axis at.
1067
- * Equals `rotation` under uniform zoom; non-uniform zoom bends it. */
1068
- declare function screenAngleOf(rotation: number, scale: Scale2): number;
1069
-
1070
1900
  /** Pivot (`cx`, `cy`) and angle resolved from a pose's rotation convention. */
1071
1901
  interface PoseRotation {
1072
1902
  cx: number;
@@ -1098,11 +1928,25 @@ declare function rotatePathAround(path: Path, cx: number, cy: number, rotation:
1098
1928
  /**
1099
1929
  * The linear part of a box pose that its box, rotation, and flips cannot
1100
1930
  * hold: a skew, or a scale that differs by axis. `[a, b, c, d]` in
1101
- * `Mat3` order (`x' = a·x + c·y`, `y' = b·x + d·y`), applied about the
1931
+ * `Affine` order (`x' = a·x + c·y`, `y' = b·x + d·y`), applied about the
1102
1932
  * box center to what the node draws in its unrotated box, before the
1103
1933
  * rotation. Absent is the identity.
1104
1934
  */
1105
1935
  type PoseLinear = readonly [a: number, b: number, c: number, d: number];
1936
+ /**
1937
+ * Axis-aligned rectangle in world space. The optional `rotation` (radians,
1938
+ * around the AABB center) and `transform` (inside it, about the same center)
1939
+ * let chrome carry the frame a box is drawn in without a parallel type;
1940
+ * `poseFrameOf` composes the two. routing and core call it `Bounds`.
1941
+ */
1942
+ interface FramedBox {
1943
+ x: number;
1944
+ y: number;
1945
+ width: number;
1946
+ height: number;
1947
+ rotation?: number;
1948
+ transform?: PoseLinear;
1949
+ }
1106
1950
  /** `transform` when it changes anything, `null` for absent or the identity. */
1107
1951
  declare function poseLinearOf(pose: unknown): PoseLinear | null;
1108
1952
  /**
@@ -1111,133 +1955,781 @@ declare function poseLinearOf(pose: unknown): PoseLinear | null;
1111
1955
  * `null` when the pose has neither, or is not a box, so a caller can skip
1112
1956
  * the step.
1113
1957
  */
1114
- declare function poseFrameOf(pose: unknown): Mat3 | null;
1958
+ declare function poseFrameOf(pose: unknown): Affine | null;
1115
1959
  /**
1116
1960
  * The inverse of {@link poseFrameOf}: where a parent-frame point falls in the
1117
1961
  * pose's unrotated box frame. `null` when there is no frame to undo, or when
1118
1962
  * the `transform` is singular and nothing maps back.
1119
1963
  */
1120
- declare function poseFrameInverseOf(pose: unknown): Mat3 | null;
1964
+ declare function poseFrameInverseOf(pose: unknown): Affine | null;
1121
1965
  /** `path`, given in the pose's unrotated box frame, as the node draws it. */
1122
1966
  declare function pathThroughPoseFrame(path: Path, pose: unknown): Path;
1123
1967
 
1124
1968
  /**
1125
- * Pure anchor-set edits on the model `pathToAnchors` produces: subpath-major
1126
- * `PenAnchor[][]` plus a parallel `closed: boolean[]`. Every function here
1127
- * mutates that structure in place; callers decode with `pathToAnchors`,
1128
- * apply, and re-encode with `anchorsToPath` (see {@link editAnchorSet}).
1129
- *
1130
- * Callers address anchors by flat index — the sequential position in walk
1131
- * order across all subpaths, which is what `enumerateAnchors` reports.
1132
- * {@link locateAnchor} and {@link flatAnchorIndex} convert to and from the
1133
- * `(sub, idx)` pair.
1134
- *
1135
- * The two orders agree because `pathToAnchors` and `enumerateAnchors`
1136
- * walk the same command stream and emit one anchor per M/L/C/Q, less the
1137
- * end of a closing curve that lands on its subpath's first point.
1138
- * `countPathAnchors`, which sizes the renderer's per-anchor arrays, counts
1139
- * that end too and so can be one higher per closed subpath.
1969
+ * Flat indices of every anchor a round brush covers as it moves from `from` to
1970
+ * `to`: those within `radiusPx` of that segment, measured on screen. A dab is a
1971
+ * segment of zero length. `scale` is the view's, so the brush stays round on
1972
+ * screen under non-uniform zoom; omitted, one unit is one pixel.
1140
1973
  */
1974
+ declare function anchorsUnderBrush(set: AnchorSet, from: Point, to: Point, opts: {
1975
+ radiusPx: number;
1976
+ scale?: Scale2;
1977
+ }): number[];
1141
1978
 
1142
- /** Subpath-major anchor model — the decoded form of a `PolygonPath`. */
1143
- interface AnchorSet {
1144
- anchors: PenAnchor[][];
1145
- closed: boolean[];
1146
- }
1147
- /** A world-space rectangle, as produced by a drag-rect gesture. */
1148
- interface AnchorRect {
1149
- x: number;
1150
- y: number;
1151
- width: number;
1152
- height: number;
1979
+ /**
1980
+ * How a path maps onto itself under one transform `m`: `partners[i]` is the
1981
+ * flat index of the anchor that anchor `i` lands on, and `flips[i]` whether
1982
+ * its in-handle lands on that anchor's out-handle — a mirror reverses which
1983
+ * way a path runs, a rotation does not. An anchor `m` fixes is its own
1984
+ * partner.
1985
+ */
1986
+ interface AnchorSymmetry {
1987
+ readonly m: Affine;
1988
+ readonly partners: readonly number[];
1989
+ readonly flips: readonly boolean[];
1153
1990
  }
1154
- /** Total anchors across every subpath. */
1155
- declare function anchorCount(set: AnchorSet): number;
1156
- /** Flat index → `(sub, idx)`. Returns null when out of range. */
1157
- declare function locateAnchor(set: AnchorSet, flatIndex: number): {
1158
- sub: number;
1159
- idx: number;
1160
- } | null;
1161
- /** `(sub, idx)` → flat index. Returns -1 when out of range. */
1162
- declare function flatAnchorIndex(set: AnchorSet, sub: number, idx: number): number;
1163
- /** Resolve a flat index to the anchor itself. Null when out of range. */
1164
- declare function anchorAt(set: AnchorSet, flatIndex: number): PenAnchor | null;
1165
1991
  /**
1166
- * Decode `path`, hand the mutable anchor set to `edit`, and re-encode.
1167
- * Returns null when `edit` returns `false` (it changed nothing), so callers
1168
- * can skip a no-op commit.
1992
+ * The pairing that makes `set` its own image under `m`, or `null` when it is
1993
+ * not one: every anchor, handles included, must land on a distinct anchor
1994
+ * within `tolerance` on each axis.
1169
1995
  */
1170
- declare function editAnchorSet(path: PolygonPath, edit: (set: AnchorSet) => boolean | void): PolygonPath | null;
1171
- /** Translate an anchor and both of its handles by `(dx, dy)`. */
1172
- declare function translateAnchorBy(set: AnchorSet, flatIndex: number, dx: number, dy: number): boolean;
1996
+ declare function anchorSymmetry(set: AnchorSet, m: Affine, tolerance?: number): AnchorSymmetry | null;
1997
+ /** Every transform in `group` that `set` is its own image under, paired. */
1998
+ declare function anchorSymmetries(set: AnchorSet, group: readonly Affine[], tolerance?: number): AnchorSymmetry[];
1173
1999
  /**
1174
- * Move one control handle to an absolute world position. When the anchor was
1175
- * smooth (handles collinear through the anchor) and `breakSmoothness` is
1176
- * false, the opposite handle is mirrored so the curve stays smooth.
2000
+ * Translate the anchors `flats` by `(dx, dy)` the way `translateAnchorBy`
2001
+ * does, and every image of each under `syms` by the transformed delta, so a
2002
+ * path that was its own image under each of them still is. `syms` should be
2003
+ * the whole symmetry group less its identity, as `anchorSymmetries` gives it
2004
+ * for a group; with a part of one, the anchors it fixes are only partly held.
2005
+ * An anchor that is itself being dragged moves as dragged.
1177
2006
  */
1178
- declare function moveHandleTo(set: AnchorSet, flatIndex: number, side: 'in' | 'out', toX: number, toY: number, breakSmoothness: boolean): boolean;
2007
+ declare function translateAnchorsSymmetric(set: AnchorSet, flats: readonly number[], dx: number, dy: number, syms: readonly AnchorSymmetry[]): boolean;
1179
2008
  /**
1180
- * Split the segment `(sub, segIdx) → (sub, segIdx + 1)` at parameter `t`,
1181
- * inserting a new anchor there. On a closed subpath `segIdx` may also name
1182
- * the closing segment (`sub.length - 1`); its new anchor is appended.
1183
- *
1184
- * A straight segment stays straight — the new anchor has no handles. A curved
1185
- * one splits by de Casteljau, so the two halves trace the original path.
2009
+ * Move one handle to `(toX, toY)` and every image of it under `syms` to the
2010
+ * image of that point, so a path that was its own image under each of them
2011
+ * still is. A handle a transform maps onto itself keeps to where that
2012
+ * transform allows; one mapped onto its own anchor's opposite handle drives
2013
+ * that handle.
1186
2014
  *
1187
- * Returns the new anchor's flat index, or -1 when the segment doesn't
1188
- * exist.
2015
+ * Otherwise a smooth anchor keeps the opposite handle opposite, as
2016
+ * `moveHandleTo` does, unless `breakSmoothness`, and that handle's images
2017
+ * follow it. Every handle is written once, from its target, so no anchor
2018
+ * re-aims a handle another image already placed.
1189
2019
  */
1190
- declare function insertAnchorOnSegment(set: AnchorSet, args: {
2020
+ declare function moveHandleSymmetric(set: AnchorSet, flat: number, side: 'in' | 'out', toX: number, toY: number, breakSmoothness: boolean, syms: readonly AnchorSymmetry[]): boolean;
2021
+
2022
+ /** Where on a segment a bend is held, as `segmentAt` reports it. */
2023
+ interface BendGrip {
1191
2024
  sub: number;
1192
2025
  segIdx: number;
2026
+ /** Curve parameter of the held point. */
1193
2027
  t: number;
1194
- }): number;
1195
- /** A point on a segment, as {@link segmentAt} reports it. */
1196
- interface SegmentHit {
2028
+ }
2029
+ /**
2030
+ * Bend one segment so the point held at `grip.t` moves by `(dx, dy)`, by
2031
+ * moving its two control handles: each by an amount weighted toward the end it
2032
+ * belongs to, together exactly enough to carry the held point along. A straight
2033
+ * segment becomes a curve. Where an end anchor was smooth, its other handle
2034
+ * turns to stay in line, keeping its length, so the neighboring segment bends
2035
+ * with it rather than kinking.
2036
+ *
2037
+ * Returns false when the segment doesn't exist.
2038
+ */
2039
+ declare function bendSegment(set: AnchorSet, grip: BendGrip, dx: number, dy: number): boolean;
2040
+
2041
+ /**
2042
+ * `smudgePath` — one dab of a smudge brush: the outline under a round brush
2043
+ * moved along the brush's motion, most at its center and not at all at its rim.
2044
+ */
2045
+
2046
+ /** Options for {@link smudgePath}. */
2047
+ interface SmudgePathOptions {
2048
+ /** The brush's center before this dab's motion. */
2049
+ center: Point;
2050
+ /** How far the brush moved. */
2051
+ delta: Point;
2052
+ /** The brush's radius, in the path's units. */
2053
+ radius: number;
2054
+ /** How much of `delta` a point under the brush's center moves: `1` all of it. Default 1. */
2055
+ strength?: number;
2056
+ /**
2057
+ * The longest a segment under the brush may be before an anchor is added at its
2058
+ * middle, in the path's units, so the brush has points to move where it needs
2059
+ * them. Default a third of `radius`; `Infinity` adds none.
2060
+ */
2061
+ detail?: number;
2062
+ }
2063
+ /**
2064
+ * `path` with the outline under the brush pushed along `delta`. A point at distance
2065
+ * `d` from `center` moves by `delta × strength × (1 − (d/radius)²)²`, and an anchor's
2066
+ * handles move with it, so a smooth anchor stays smooth. First, every segment that
2067
+ * passes under the brush and is longer than `detail` is split at its middle until it
2068
+ * is not, so a long straight edge bends where the brush meets it. Returns `path`
2069
+ * itself when nothing is under the brush.
2070
+ */
2071
+ declare function smudgePath(path: PolygonPath, opts: SmudgePathOptions): PolygonPath;
2072
+
2073
+ /**
2074
+ * Collapse the handles of the anchors at `flatIndexes` onto their anchors,
2075
+ * leaving sharp corners. A segment whose ends both lose their handles turns
2076
+ * straight.
2077
+ *
2078
+ * Returns false when every handle was already on its anchor. A segment that
2079
+ * stays curved because its far end keeps a handle still encodes a control at
2080
+ * this end, so it decodes as a handle sitting on the anchor: retracted.
2081
+ */
2082
+ declare function retractHandles(set: AnchorSet, flatIndexes: Iterable<number>): boolean;
2083
+ /**
2084
+ * Give each anchor at `flatIndexes` two handles of equal length pointing in
2085
+ * opposite directions, so the curve passes through it smoothly. The shared
2086
+ * direction splits the difference between the two handles' own directions,
2087
+ * and the shared length is their mean; an anchor with one handle mirrors it.
2088
+ *
2089
+ * Skipped: an anchor with no handles, and the ends of an open subpath, which
2090
+ * have only one segment to shape. Returns false when nothing changed.
2091
+ */
2092
+ declare function makeHandlesSymmetric(set: AnchorSet, flatIndexes: Iterable<number>): boolean;
2093
+ /**
2094
+ * Where a handle lands when dragged toward `to` but held to its current
2095
+ * direction from `anchor`: `to` projected onto that ray. The length never
2096
+ * goes below zero, so the handle can shrink onto the anchor but not flip
2097
+ * through it. A handle already sitting on its anchor has no direction to
2098
+ * keep and follows `to` freely.
2099
+ */
2100
+ declare function handleAlongItsAngle(anchor: Point, handle: Point, to: Point): Point;
2101
+ /**
2102
+ * Where a handle lands when dragged toward `to` but held to its current
2103
+ * length from `anchor`: on the circle of that length, aimed at `to`. A `to`
2104
+ * on the anchor itself gives no direction, and leaves the handle where it is.
2105
+ */
2106
+ declare function handleAtItsLength(anchor: Point, handle: Point, to: Point): Point;
2107
+
2108
+ /**
2109
+ * Soft selection over an anchor set: how strongly an edit to some anchors
2110
+ * carries to the anchors near them, by distance measured along the path.
2111
+ *
2112
+ * Distance is arc length along the drawn curve, not a straight line, so two
2113
+ * anchors that sit close across a gap in a shape are as far apart as the
2114
+ * path between them. A `scale` measures it in screen pixels under a view's
2115
+ * zoom, per axis.
2116
+ */
2117
+
2118
+ /** How influence fades from 1 at a selected anchor to 0 at the radius:
2119
+ * `smooth` eases out and in, `linear` falls evenly, `sharp` drops fast and
2120
+ * tails off. */
2121
+ type SoftFalloff = 'smooth' | 'linear' | 'sharp';
2122
+ /**
2123
+ * The weight `falloff` gives a point `distance` from the selection, for a
2124
+ * radius of `radius`: 1 at distance 0, 0 at the radius and beyond.
2125
+ */
2126
+ declare function softFalloff(falloff: SoftFalloff, distance: number, radius: number): number;
2127
+ /** Arc length along one segment, sampled at evenly spaced curve parameters. */
2128
+ interface SegmentMeasure {
2129
+ /** Arc length from the subpath's start to each sample; sample `i` is at
2130
+ * parameter `i / (lengths.length - 1)`. */
2131
+ lengths: Float64Array;
2132
+ }
2133
+ /** Arc lengths along one subpath, as {@link measureAnchorPath} takes them. */
2134
+ interface AnchorSubpathMeasure {
2135
+ closed: boolean;
2136
+ /** Flat index of the subpath's first anchor. */
2137
+ first: number;
2138
+ /** Arc length from the subpath's start to each anchor. */
2139
+ at: Float64Array;
2140
+ /** Total length, the closing segment included on a closed subpath. */
2141
+ length: number;
2142
+ /** Segment `i` runs from anchor `i` to anchor `i + 1`, wrapping on a closed subpath. */
2143
+ segments: SegmentMeasure[];
2144
+ }
2145
+ /** Arc lengths along every subpath of an anchor set, as
2146
+ * {@link measureAnchorPath} takes them. Read it with
2147
+ * {@link softSelectionWeights} and {@link softSelectionBand}. */
2148
+ interface AnchorPathMeasure {
2149
+ readonly subpaths: readonly AnchorSubpathMeasure[];
2150
+ }
2151
+ /** Options for {@link measureAnchorPath}. */
2152
+ interface MeasureAnchorPathOptions {
2153
+ /** Per-axis factor applied before measuring — a view's scale, to measure in
2154
+ * screen pixels. Default 1 on both axes. */
2155
+ scale?: {
2156
+ x: number;
2157
+ y: number;
2158
+ };
2159
+ /** Samples taken along each curved segment. Default 24. */
2160
+ samples?: number;
2161
+ }
2162
+ /** Measure the arc length along every subpath of `set`. */
2163
+ declare function measureAnchorPath(set: AnchorSet, opts?: MeasureAnchorPathOptions): AnchorPathMeasure;
2164
+ /**
2165
+ * How much of an edit to the `selected` anchors each anchor receives: 1 for
2166
+ * a selected anchor, the falloff of its distance along the path for one
2167
+ * within `radius` of the nearest selected anchor on its subpath. Anchors
2168
+ * that receive nothing are absent, as are anchors on a subpath with no
2169
+ * selected anchor.
2170
+ */
2171
+ declare function softSelectionWeights(measure: AnchorPathMeasure, selected: Iterable<number>, radius: number, falloff: SoftFalloff): Map<number, number>;
2172
+ /**
2173
+ * The stretches of path within `radius` of the `selected` anchors, as world
2174
+ * polylines drawn on `set`. `measure` is of the set the distances are taken
2175
+ * on — the path as it was when the edit began — and `set` the geometry to
2176
+ * draw on, which must have the same anchors. A stretch crossing the seam of a
2177
+ * closed subpath comes back as one run.
2178
+ */
2179
+ declare function softSelectionBand(measure: AnchorPathMeasure, set: AnchorSet, selected: Iterable<number>, radius: number): Point[][];
2180
+
2181
+ /** A curved segment's balance line: from its first handle's tip to its
2182
+ * second's, with a grip at the middle. */
2183
+ interface HandleBalance {
1197
2184
  sub: number;
1198
2185
  segIdx: number;
1199
- /** Parameter on the segment — what {@link insertAnchorOnSegment} takes. */
2186
+ /** Flat index of the anchor the segment starts at, which names the segment
2187
+ * the way an anchor's flat index names the anchor. */
2188
+ flat: number;
2189
+ /** Tip of the segment's first handle, the start anchor's out-handle. */
2190
+ from: Point;
2191
+ /** Tip of the segment's second handle, the end anchor's in-handle. */
2192
+ to: Point;
2193
+ /** Midway between the two tips. */
2194
+ grip: Point;
2195
+ }
2196
+ /**
2197
+ * The balance line of every curved segment in `set` that has both handles
2198
+ * pulled off their anchors. Dragging a line's grip scales both handles
2199
+ * together ({@link scaleSegmentHandles}), which fills the curve out toward
2200
+ * its corner or flattens it toward its chord without turning either end.
2201
+ */
2202
+ declare function handleBalances(set: AnchorSet): HandleBalance[];
2203
+ /**
2204
+ * Scale both handles of segment `(sub, segIdx)` by `factor`, each along its
2205
+ * own direction, so the segment keeps its end tangents. A smooth anchor at
2206
+ * either end stays smooth: its other handle points the same way as before.
2207
+ * `factor` below zero counts as zero, which retracts both handles.
2208
+ *
2209
+ * Under `syms`, as `anchorSymmetries` gives them, every image of either handle
2210
+ * moves to the image of where that handle goes, the way `moveHandleSymmetric`
2211
+ * moves a dragged handle's.
2212
+ *
2213
+ * Returns false when the segment has no balance line or nothing moved.
2214
+ */
2215
+ declare function scaleSegmentHandles(set: AnchorSet, sub: number, segIdx: number, factor: number, syms?: readonly AnchorSymmetry[]): boolean;
2216
+ /**
2217
+ * The factor {@link scaleSegmentHandles} takes to bring segment
2218
+ * `(sub, segIdx)`'s grip as near `to` as scaling can: `to` projected onto the
2219
+ * line the grip slides along as both handles grow. 1 when the segment has no
2220
+ * balance line.
2221
+ */
2222
+ declare function balanceFactorToward(set: AnchorSet, sub: number, segIdx: number, to: Point): number;
2223
+ /**
2224
+ * Give both handles of segment `(sub, segIdx)` the same proportion: each
2225
+ * reaches the same fraction of the way from its anchor to the corner where
2226
+ * the two handle lines cross, that fraction being the mean of the two they
2227
+ * had. Where the lines don't cross ahead of both handles, both take the mean
2228
+ * length instead. Directions are kept either way.
2229
+ *
2230
+ * Returns false when the segment has no balance line or the handles already
2231
+ * match.
2232
+ */
2233
+ declare function equalizeSegmentHandles(set: AnchorSet, sub: number, segIdx: number): boolean;
2234
+
2235
+ /**
2236
+ * Make each smooth anchor at `flatIndexes` curvature-continuous: the curve
2237
+ * bends as tightly just after the anchor as just before it, so it shows no
2238
+ * kink in its curvature there. Only handle lengths change — each handle keeps
2239
+ * its direction, so the anchor stays smooth and the tangent stays put — and
2240
+ * the product of the two lengths is kept, so the anchor's pull on the curve
2241
+ * stays about as strong.
2242
+ *
2243
+ * Absent `flatIndexes`, every anchor in the set. Moving one anchor's handles
2244
+ * changes the curvature at its neighbors, so the fixes are repeated until
2245
+ * they settle.
2246
+ *
2247
+ * Skipped, because lengths alone cannot do it: a corner, an anchor missing a
2248
+ * handle, an end of an open subpath, and an anchor the curve bends opposite
2249
+ * ways either side of, or straight through on one side.
2250
+ *
2251
+ * Returns false when nothing changed.
2252
+ */
2253
+ declare function harmonizeAnchors(set: AnchorSet, flatIndexes?: Iterable<number>): boolean;
2254
+
2255
+ /**
2256
+ * Where each point of an edited path came from, so an array kept per point (a skin's
2257
+ * weights) can follow an edit that inserts, deletes, or reorders points.
2258
+ */
2259
+
2260
+ /**
2261
+ * Where a point of an edited path came from: point `a` of the path before the edit, or for a
2262
+ * point the edit made, a blend `t` of the way from point `a` to point `b`.
2263
+ */
2264
+ interface PointOrigin {
2265
+ a: number;
2266
+ b: number;
1200
2267
  t: number;
1201
- /** The point on the path at `t`, in the set's own space. */
2268
+ /** On an anchor's own point: its subpath now runs the other way through it. */
2269
+ reversed?: true;
2270
+ }
2271
+ /**
2272
+ * `editAnchorSet`, answering also where each point of the new path came from.
2273
+ *
2274
+ * An anchor the edit kept, moved or not, keeps its points. One the edit put exactly where an
2275
+ * anchor was takes that anchor's, each handle by where it is, so a subpath turned around
2276
+ * keeps its weights. A handle an edit gave an anchor that had none takes the anchor's point. An anchor the edit made is a blend of the nearest kept anchors either side
2277
+ * of it in its subpath, by its distance to each, or a copy of the one it has; its origin is
2278
+ * `null` when its subpath has none.
2279
+ */
2280
+ declare function editAnchorSetTracked(path: PolygonPath, edit: (set: AnchorSet) => boolean | void): {
2281
+ path: PolygonPath;
2282
+ origins: (PointOrigin | null)[];
2283
+ } | null;
2284
+ /**
2285
+ * An array kept per point of a path, carried through an edit: `origins` is what
2286
+ * {@link editAnchorSetTracked} answered. A point with no origin, and an origin past the
2287
+ * array's end, read 0.
2288
+ */
2289
+ declare function remapPointValues(values: ArrayLike<number>, origins: readonly (PointOrigin | null)[]): number[];
2290
+
2291
+ /**
2292
+ * Arrays kept per anchor of a path — vertex colors, stroke widths — carried through an edit
2293
+ * that `editAnchorSetTracked` reported.
2294
+ */
2295
+
2296
+ /**
2297
+ * Where each anchor of `after` came from, in anchors of `before` as `countPathAnchors` counts
2298
+ * them, given the point `origins` `editAnchorSetTracked` answered for the edit: anchor `a`, or
2299
+ * for an anchor the edit made, a blend `t` of the way from `a` to `b`. `reversed` marks an
2300
+ * anchor whose subpath now runs the other way through it. Null for an anchor with no origin.
2301
+ */
2302
+ declare function anchorOriginsOf(before: PolygonPath, after: PolygonPath, origins: readonly (PointOrigin | null)[]): (PointOrigin | null)[];
2303
+ /**
2304
+ * An array kept per anchor of a path, `stride` numbers each — four color channels, say —
2305
+ * carried through an edit from `before` to `after`, with `origins` as
2306
+ * `editAnchorSetTracked` answered. An anchor the edit kept keeps its numbers; one it
2307
+ * made blends those of the anchors it came between. An anchor with no origin reads 0. Anchors
2308
+ * are counted as `countPathAnchors` counts them.
2309
+ */
2310
+ declare function remapAnchorValues(values: ArrayLike<number>, stride: number, before: PolygonPath, after: PolygonPath, origins: readonly (PointOrigin | null)[]): number[];
2311
+
2312
+ /**
2313
+ * Geometric constraints on a path's anchors. Each names anchors by flat index,
2314
+ * the walk-order numbering `enumerateAnchors` and `anchorAt` use, and is plain
2315
+ * JSON, so a list of them can be kept in scene data and saved with it.
2316
+ */
2317
+ /** Two anchors by flat index: the straight line from the first to the second. */
2318
+ type AnchorPair = readonly [number, number];
2319
+ /**
2320
+ * One constraint. `fixed` holds an anchor where it was before an edit, so it
2321
+ * moves with its node but not with any edit of the path. `horizontal` and
2322
+ * `vertical` read the axes of the frame the solve is given; `length` is in
2323
+ * the path's own units. `tangent` keeps an anchor's two handles on one line
2324
+ * through it, pointing apart (G1 continuity), their lengths free; it needs
2325
+ * both handles. `onPath` holds an anchor on the path's segment from
2326
+ * `segment[0]` to `segment[1]`, anywhere along it.
2327
+ */
2328
+ type PathConstraint = {
2329
+ readonly kind: 'fixed' | 'tangent';
2330
+ readonly anchor: number;
2331
+ } | {
2332
+ readonly kind: 'horizontal' | 'vertical';
2333
+ readonly segment: AnchorPair;
2334
+ } | {
2335
+ readonly kind: 'parallel' | 'perpendicular' | 'equalLength';
2336
+ readonly segments: readonly [AnchorPair, AnchorPair];
2337
+ } | {
2338
+ readonly kind: 'length';
2339
+ readonly segment: AnchorPair;
2340
+ readonly length: number;
2341
+ } | {
2342
+ readonly kind: 'coincident';
2343
+ readonly anchors: AnchorPair;
2344
+ } | {
2345
+ readonly kind: 'onPath';
2346
+ readonly anchor: number;
2347
+ readonly segment: AnchorPair;
2348
+ };
2349
+ /** Every kind of {@link PathConstraint}. */
2350
+ type PathConstraintKind = PathConstraint['kind'];
2351
+ /** The anchors `c` reads, in the order it names them; an anchor named twice is listed twice. */
2352
+ declare function constraintAnchors(c: PathConstraint): number[];
2353
+ /**
2354
+ * `c` with every anchor index passed through `to`, or null when `to` answers
2355
+ * `undefined` for any of them — the anchor is gone, and so is the constraint.
2356
+ */
2357
+ declare function mapConstraintAnchors(c: PathConstraint, to: (anchor: number) => number | undefined): PathConstraint | null;
2358
+ /** Whether every anchor `c` names is an index below `count`. */
2359
+ declare function constraintFits(c: PathConstraint, count: number): boolean;
2360
+
2361
+ /**
2362
+ * The points a constraint solve reads, and the coordinate vector it works in:
2363
+ * anchors, then each anchor's handles as offsets from it (see `ResidualSpace`).
2364
+ */
2365
+
2366
+ /** An anchor the solver reads and writes, with its handles where it has them. */
2367
+ interface ConstraintPoint {
1202
2368
  x: number;
1203
2369
  y: number;
2370
+ inHandle?: {
2371
+ x: number;
2372
+ y: number;
2373
+ };
2374
+ outHandle?: {
2375
+ x: number;
2376
+ y: number;
2377
+ };
2378
+ }
2379
+ /** One handle of an anchor. */
2380
+ interface HandleRef {
2381
+ anchor: number;
2382
+ side: 'in' | 'out';
1204
2383
  }
1205
2384
  /**
1206
- * The segment point under `(x, y)`, when it lies within `tolerancePx` of
1207
- * the path on screen and no nearer than that to either of the segment's
1208
- * own anchors — a press on an anchor is about the anchor, not the segment
1209
- * beside it.
2385
+ * Whether `c` can be solved over `points`: every anchor it names exists, and
2386
+ * a `tangent` anchor has both handles.
2387
+ */
2388
+ declare function constraintApplies(c: PathConstraint, points: readonly ConstraintPoint[]): boolean;
2389
+
2390
+ /**
2391
+ * A damped Gauss-Newton solve for anchor constraints: from the state an edit
2392
+ * left, the smallest weighted move of the anchors and handles that makes
2393
+ * every residual zero. Each step is the minimum-norm solution of the
2394
+ * linearized system, `Δ = -M Jᵀ (J M Jᵀ + λI)⁻¹ r`, where `M` weighs how
2395
+ * freely each coordinate may move; λ grows when a step makes things worse and
2396
+ * shrinks when it helps, which is the Levenberg-Marquardt schedule run on the
2397
+ * constraint side.
1210
2398
  *
1211
- * `scale` is the view's, so the tolerance holds per axis under
1212
- * non-uniform zoom. The search runs in screen-scaled space: scaling is
1213
- * affine, so the curve's parameterization, and therefore `t`, carries
1214
- * straight back to world space.
2399
+ * A handle is a coordinate of its own only where a `tangent` reads it; any
2400
+ * other rides with its anchor, as an offset nothing changes.
1215
2401
  */
1216
- declare function segmentAt(set: AnchorSet, x: number, y: number, opts: {
1217
- tolerancePx: number;
1218
- scale?: Scale2;
1219
- }): SegmentHit | null;
2402
+
2403
+ /** Options for {@link solveConstraints}. */
2404
+ interface SolveConstraintsOptions {
2405
+ /** Anchors the edit put where they are — the dragged ones. They move only when no other anchor can satisfy the constraints. */
2406
+ held?: Iterable<number>;
2407
+ /** Handles the edit put where they are, held the way `held` holds anchors. */
2408
+ heldHandles?: Iterable<HandleRef>;
2409
+ /** Where each anchor was before the edit, which is where a `fixed` constraint keeps it. Default: where it is now. */
2410
+ reference?: readonly (ConstraintPoint | undefined)[];
2411
+ /** Radians the frame `horizontal` and `vertical` read is turned from the world's axes. Default 0. */
2412
+ axisAngle?: number;
2413
+ /** A frame per constraint, by index, over `axisAngle` — for constraints gathered from paths drawn in different frames. */
2414
+ axisAngles?: readonly (number | undefined)[];
2415
+ /** Indices of constraints to leave out of the solve — ones already known not to hold. */
2416
+ skip?: Iterable<number>;
2417
+ /** Default 100. */
2418
+ maxIterations?: number;
2419
+ /** The largest residual the solve stops at. Default 1e-7. */
2420
+ tolerance?: number;
2421
+ }
2422
+ /** What {@link solveConstraints} answers. */
2423
+ interface SolveConstraintsResult {
2424
+ /** Every anchor, moved, with its handles. */
2425
+ points: ConstraintPoint[];
2426
+ /** Whether every constraint the solve took holds within its tolerance. */
2427
+ converged: boolean;
2428
+ iterations: number;
2429
+ /** Indices of the constraints that do not hold in `points`, skipped ones included. */
2430
+ unsatisfied: number[];
2431
+ }
2432
+ /**
2433
+ * Move `points` the least that makes `constraints` hold. Anchors named by a
2434
+ * `fixed` constraint go back to `reference` and stay there; `held` anchors
2435
+ * and `heldHandles` stay where they are when the rest can satisfy the
2436
+ * constraints, and give way as little as they must when not. When the
2437
+ * constraints cannot all hold, `converged` is false and `unsatisfied` names
2438
+ * the ones that do not.
2439
+ */
2440
+ declare function solveConstraints(points: readonly ConstraintPoint[], constraints: readonly PathConstraint[], opts?: SolveConstraintsOptions): SolveConstraintsResult;
2441
+
2442
+ /**
2443
+ * Whether constraints hold for a set of points, and whether a solve made them
2444
+ * hold only by collapsing what they measure.
2445
+ */
2446
+
2447
+ /** The residual, in the path's units, under which a constraint counts as holding. */
2448
+ declare const CONSTRAINT_TOLERANCE = 0.001;
2449
+ /** Options for {@link unsatisfiedConstraints}. */
2450
+ interface UnsatisfiedConstraintsOptions {
2451
+ axisAngle?: number;
2452
+ /** A frame per constraint, by index, over `axisAngle`. */
2453
+ axisAngles?: readonly (number | undefined)[];
2454
+ /** Where a `fixed` anchor belongs. Absent, a `fixed` constraint always holds. */
2455
+ reference?: readonly (ConstraintPoint | undefined)[];
2456
+ /** Default {@link CONSTRAINT_TOLERANCE}. */
2457
+ tolerance?: number;
2458
+ }
2459
+ /**
2460
+ * Indices of the constraints that do not hold for `points`: a residual past
2461
+ * the tolerance, an anchor index the points do not have, or a tangent on an
2462
+ * anchor missing a handle.
2463
+ */
2464
+ declare function unsatisfiedConstraints(points: readonly ConstraintPoint[], constraints: readonly PathConstraint[], opts?: UnsatisfiedConstraintsOptions): number[];
2465
+
2466
+ /**
2467
+ * Constraints on a `PolygonPath`: solving them over its anchors, and carrying
2468
+ * them through an edit that renumbers the anchors.
2469
+ */
2470
+
2471
+ /** Every anchor of `path`, by flat index, with the handles it has. */
2472
+ declare function pathAnchorPoints(path: PolygonPath): ConstraintPoint[];
2473
+ /** {@link solveConstraints} over `path`'s anchors, and `path` with them moved. */
2474
+ interface SolvePathConstraintsResult extends SolveConstraintsResult {
2475
+ /** `path` with each anchor moved where the solve put it, its handles moved with it; `path` itself when none moved. */
2476
+ path: PolygonPath;
2477
+ }
2478
+ /**
2479
+ * Solve `constraints` over the anchors of `path` and write the result back,
2480
+ * carrying each moved anchor's handles with it so its curves keep their
2481
+ * shape, except where a `tangent` turned them.
2482
+ */
2483
+ declare function solvePathConstraints(path: PolygonPath, constraints: readonly PathConstraint[], opts?: SolveConstraintsOptions): SolvePathConstraintsResult;
2484
+ /**
2485
+ * `path` with its anchors and handles where `to` puts them, given `from`,
2486
+ * where they are now; `path` itself when nothing moved.
2487
+ */
2488
+ declare function withAnchorPoints(path: PolygonPath, from: readonly ConstraintPoint[], to: readonly ConstraintPoint[]): PolygonPath;
2489
+ /**
2490
+ * For each anchor of `after`, the anchor of `before` it is: the one whose
2491
+ * point `origins` says it was copied from. An anchor the edit made has none.
2492
+ * `origins` is what `editAnchorSetTracked` answered for the edit.
2493
+ */
2494
+ declare function anchorsBefore(before: PolygonPath, after: PolygonPath, origins: readonly (PointOrigin | null)[]): (number | undefined)[];
2495
+ /**
2496
+ * `constraints` renumbered for the anchors of a path after an edit, given
2497
+ * `wasAnchor` — for each new anchor, the old one it is, as
2498
+ * {@link anchorsBefore} answers. A constraint naming an anchor the edit
2499
+ * removed is dropped; `from` holds each kept one's index in `constraints`.
2500
+ */
2501
+ declare function remapConstraints(constraints: readonly PathConstraint[], wasAnchor: readonly (number | undefined)[]): {
2502
+ constraints: PathConstraint[];
2503
+ from: number[];
2504
+ };
2505
+ /**
2506
+ * The segment of `path` joining anchors `a` and `b`, as the pair in the order
2507
+ * the path runs it, or null when no segment joins them.
2508
+ */
2509
+ declare function segmentBetween(path: PolygonPath, a: number, b: number): AnchorPair | null;
2510
+
2511
+ /**
2512
+ * Finishing an edit of constrained paths so their constraints hold: one path
2513
+ * on its own, or several solved together when links — constraints naming
2514
+ * anchors of more than one — tie them.
2515
+ */
2516
+
2517
+ /** Options for {@link constrainPathEdit}. */
2518
+ interface ConstrainPathEditOptions {
2519
+ /**
2520
+ * Where each point of the edited path came from, as `editAnchorSetTracked`
2521
+ * answers. Absent, the edit is taken to keep every anchor's number, and one
2522
+ * that changes the anchor count drops every constraint.
2523
+ */
2524
+ origins?: readonly (PointOrigin | null)[];
2525
+ /** See {@link SolveConstraintsOptions.axisAngle}. */
2526
+ axisAngle?: number;
2527
+ }
2528
+ /** What {@link constrainPathEdit} answers. */
2529
+ interface ConstrainedPathEdit {
2530
+ /** The edited path with the constraints holding again. */
2531
+ path: PolygonPath;
2532
+ /** The constraints renumbered for `path`'s anchors. */
2533
+ constraints: PathConstraint[];
2534
+ /** Whether every constraint that held before the edit holds after it. */
2535
+ converged: boolean;
2536
+ /** Indices into `constraints` of the ones that do not hold, including any that already did not before the edit. */
2537
+ unsatisfied: number[];
2538
+ }
2539
+ /** One path of a {@link constrainPathsEdit}: its edit, and its own constraints. */
2540
+ interface PathsEditItem extends ConstrainPathEditOptions {
2541
+ before: PolygonPath;
2542
+ /** `before` as the edit left it; `before` itself for a path the edit did not touch. */
2543
+ after: PolygonPath;
2544
+ constraints: readonly PathConstraint[];
2545
+ }
1220
2546
  /**
1221
- * Delete the anchors at `flatIndexes`. Where a deleted anchor had
1222
- * neighbors on both sides — across the seam too, on a closed subpath — the
1223
- * surviving segment is refitted through the gap (`fitCubicThroughDeletion`)
1224
- * so the path keeps its shape instead of snapping to a straight line.
2547
+ * A constraint across paths. Its anchors are numbered over the anchors of
2548
+ * `paths` — indices into the items — one after another in that order: with
2549
+ * `paths: [2, 0]` and a 5-anchor item 2, anchor 6 is item 0's anchor 1. Its
2550
+ * `horizontal` and `vertical` read the world's axes.
2551
+ */
2552
+ interface PathLink {
2553
+ constraint: PathConstraint;
2554
+ paths: readonly number[];
2555
+ }
2556
+ /** What {@link constrainPathsEdit} answers. */
2557
+ interface ConstrainedPathsEdit {
2558
+ /** Each item, in order: its path with the constraints holding, and its own constraints renumbered for it. */
2559
+ items: {
2560
+ path: PolygonPath;
2561
+ constraints: PathConstraint[];
2562
+ unsatisfied: number[];
2563
+ }[];
2564
+ /** Each link renumbered over its paths' anchors after the edit; null when the edit removed an anchor it names. */
2565
+ links: {
2566
+ constraint: PathConstraint | null;
2567
+ satisfied: boolean;
2568
+ }[];
2569
+ /** Whether every constraint that held before the edit holds after it. */
2570
+ converged: boolean;
2571
+ }
2572
+ /**
2573
+ * An edit of constrained paths, finished so their constraints and the links
2574
+ * between them hold, as one solve over every anchor: the anchors and handles
2575
+ * the edit moved are held, and so are the anchors of a path it left alone
2576
+ * (`after === before`), except one held on a segment; `fixed` anchors go
2577
+ * back to where they were, and the rest move the least they must. A constraint
2578
+ * that did not hold before the edit is left out of the solve, and reported.
2579
+ * Each item's own `horizontal` and `vertical` read its own `axisAngle`.
2580
+ */
2581
+ declare function constrainPathsEdit(items: readonly PathsEditItem[], links?: readonly PathLink[]): ConstrainedPathsEdit;
2582
+ /**
2583
+ * An edit of a constrained path, `before` → `after`, finished so its
2584
+ * constraints hold: the anchors and handles the edit moved are held, `fixed`
2585
+ * anchors go back to where they were, and the rest move the least they must.
2586
+ * A constraint that did not hold before the edit conflicts with the others
2587
+ * and is left out of the solve rather than allowed to pull them out of true;
2588
+ * `unsatisfied` reports it.
2589
+ */
2590
+ declare function constrainPathEdit(before: PolygonPath, after: PolygonPath, constraints: readonly PathConstraint[], opts?: ConstrainPathEditOptions): ConstrainedPathEdit;
2591
+
2592
+ /**
2593
+ * Which constraints conflict: not merely the ones a failed solve leaves
2594
+ * unsatisfied, but a smallest set that cannot all hold together, found by a
2595
+ * deletion filter — drop each member in turn, and keep it out when what is
2596
+ * left still fails. Every member of the answer is needed for the conflict.
2597
+ */
2598
+
2599
+ /** Options for {@link conflictingConstraints}. */
2600
+ interface ConflictingConstraintsOptions {
2601
+ /** Radians the frame `horizontal` and `vertical` read is turned from the world's axes. Default 0. */
2602
+ axisAngle?: number;
2603
+ /** A frame per constraint, by index, over `axisAngle`. */
2604
+ axisAngles?: readonly (number | undefined)[];
2605
+ /** Where each anchor belongs for a `fixed` constraint. Default: where it is now. */
2606
+ reference?: readonly (ConstraintPoint | undefined)[];
2607
+ }
2608
+ /**
2609
+ * The constraints of `constraints` that conflict over `points`: for every
2610
+ * group of them sharing anchors that cannot all hold, a smallest subset that
2611
+ * still cannot — each member needed, the later ones in the list kept in
2612
+ * preference to the earlier — and every constraint that cannot apply at all.
2613
+ * Sorted; empty when every constraint can hold at once.
2614
+ */
2615
+ declare function conflictingConstraints(points: readonly ConstraintPoint[], constraints: readonly PathConstraint[], opts?: ConflictingConstraintsOptions): number[];
2616
+
2617
+ /**
2618
+ * Edits to a path's order rather than its shape: where a subpath starts, and
2619
+ * which way it runs. Each mutates an `AnchorSet` in place, like the edits in
2620
+ * `anchorEdits`, and moves anchors whole, so anything an anchor carries beside
2621
+ * its point and handles travels with it.
2622
+ */
2623
+
2624
+ /**
2625
+ * Make the anchor at `flatIndex` its subpath's first. A closed subpath turns
2626
+ * so it starts there, keeping its direction. An open subpath can only start
2627
+ * at one of its ends: its last anchor makes it run the other way, and an
2628
+ * interior anchor changes nothing. Returns whether the set changed.
2629
+ */
2630
+ declare function setSubpathStart(set: AnchorSet, flatIndex: number): boolean;
2631
+ /**
2632
+ * Run each subpath in `subs` the other way, every one when `subs` is absent.
2633
+ * The shape is unchanged: each anchor's handles trade places. A closed
2634
+ * subpath keeps its start anchor, as `reversePath` does; an open one starts
2635
+ * where it used to end. Returns whether any subpath had a segment to turn.
2636
+ */
2637
+ declare function reverseSubpaths(set: AnchorSet, subs?: Iterable<number>): boolean;
2638
+ /**
2639
+ * Where each anchor at `flatIndexes` sits once `reverseSubpaths(set, subs)`
2640
+ * has run: an anchor keeps its point, so a selection can follow it. Indexes
2641
+ * outside the set are dropped.
2642
+ */
2643
+ declare function flatIndexesAfterReverse(set: AnchorSet, flatIndexes: Iterable<number>, subs?: Iterable<number>): number[];
2644
+ /** The subpaths holding the anchors at `flatIndexes`, each once, in order. */
2645
+ declare function subpathsOfAnchors(set: AnchorSet, flatIndexes: Iterable<number>): number[];
2646
+ /** A mark showing which way a path runs: a point on it and the unit vector of its heading there. */
2647
+ interface DirectionMark {
2648
+ x: number;
2649
+ y: number;
2650
+ dx: number;
2651
+ dy: number;
2652
+ /** The subpath the mark is on. */
2653
+ sub: number;
2654
+ }
2655
+ /**
2656
+ * One {@link DirectionMark} at the middle of every segment of `set`, closing
2657
+ * segments included, in walk order. A segment with no length gets none.
2658
+ */
2659
+ declare function directionMarks(set: AnchorSet): DirectionMark[];
2660
+
2661
+ /**
2662
+ * Straight and curved segments, converted in place. A segment runs from one
2663
+ * anchor to the next, the closing segment of a closed subpath included, and is
2664
+ * named `(sub, segIdx)` as `segmentAt` reports it.
2665
+ */
2666
+
2667
+ /** A segment of an `AnchorSet`: `segIdx === sub.length - 1` is a closed
2668
+ * subpath's closing segment. */
2669
+ interface SegmentRef {
2670
+ sub: number;
2671
+ segIdx: number;
2672
+ }
2673
+ /** What a segment is made: a line, a curve, or the other of what it is. */
2674
+ type SegmentType = 'line' | 'curve';
2675
+ /**
2676
+ * The segments both of whose ends are among `flatIndexes`, in walk order.
2677
+ * Two adjacent selected anchors select the segment between them; a closed
2678
+ * subpath with every anchor selected has all its segments.
2679
+ */
2680
+ declare function segmentsBetweenAnchors(set: AnchorSet, flatIndexes: Iterable<number>): SegmentRef[];
2681
+ /** Whether segment `seg` is straight, or null when it does not exist. */
2682
+ declare function isSegmentStraight(set: AnchorSet, seg: SegmentRef): boolean | null;
2683
+ /**
2684
+ * Make each segment in `segs` straight or curved without moving its anchors.
2685
+ * A line becomes a curve with its handles a third of the way in from each
2686
+ * end, which draws the same line; a curve becomes a line by dropping the two
2687
+ * handles that shape it, leaving the handles of the segments beside it alone.
2688
+ * With no `to`, every segment turns curved when any of them is straight, and
2689
+ * straight when all are curved: one press flips the lot.
1225
2690
  *
1226
- * Subpaths left with fewer than two anchors are dropped entirely. If that
1227
- * would leave the path with no subpaths at all, the whole deletion is refused
1228
- * (returns false, `set` untouched): an empty path leaves the node selected
1229
- * and in the scene with nothing on screen and no anchors to grab it by.
2691
+ * Returns whether any segment changed.
2692
+ */
2693
+ declare function convertSegments(set: AnchorSet, segs: readonly SegmentRef[], to?: SegmentType): boolean;
2694
+
2695
+ /**
2696
+ * Anchors as numbers a person types: the position of the selected anchors,
2697
+ * and for a lone anchor its handles' positions, angles and lengths. Reading
2698
+ * answers what a field shows; writing sets a value on every selected anchor,
2699
+ * or offsets each by an amount.
2700
+ */
2701
+
2702
+ /** One number of an anchor. Handle angles are degrees counterclockwise from
2703
+ * +x as drawn, where y grows downward; lengths and positions are in the
2704
+ * set's own units. */
2705
+ type AnchorField = 'x' | 'y' | 'inX' | 'inY' | 'inAngle' | 'inLength' | 'outX' | 'outY' | 'outAngle' | 'outLength';
2706
+ /** Every {@link AnchorField}, in the order a panel lists them. */
2707
+ declare const ANCHOR_FIELDS: readonly AnchorField[];
2708
+ /** What a typed field asks for: a value for every anchor, or an amount added to each one's own. */
2709
+ type NumberEntry = {
2710
+ to: number;
2711
+ } | {
2712
+ by: number;
2713
+ };
2714
+ /**
2715
+ * What each field shows for the anchors at `flatIndexes`: a number where
2716
+ * they agree, `null` where they differ, and no key for a field that does not
2717
+ * apply. Position applies to any selection; handle fields only to a single
2718
+ * anchor, and only for a handle it has.
1230
2719
  */
1231
- declare function deleteAnchorsAt(set: AnchorSet, flatIndexes: Iterable<number>): boolean;
2720
+ type AnchorFieldValues = Partial<Record<AnchorField, number | null>>;
2721
+ /** The fields for the anchors at `flatIndexes`; `{}` when none of them exists. */
2722
+ declare function readAnchorFields(set: AnchorSet, flatIndexes: Iterable<number>): AnchorFieldValues;
1232
2723
  /**
1233
- * Scissors: open a closed subpath at the given anchor. The anchor list is
1234
- * rotated to start there and a second anchor at the same point ends it, so
1235
- * every segment survives and the two ends can be pulled apart. No-ops on an
1236
- * already-open subpath — there's nothing to cut.
2724
+ * Write `entry` into `field` of every anchor at `flatIndexes`. Moving an
2725
+ * anchor carries its handles. A handle field moves the handle as a drag to
2726
+ * the typed spot would (`moveHandleTo`): on a smooth anchor the opposite
2727
+ * handle mirrors it, taking its turn and its length, and on a corner it stays
2728
+ * where it is. An anchor without that handle is skipped. An angle keeps the
2729
+ * handle's length, and a length keeps its angle. Returns whether anything
2730
+ * moved.
1237
2731
  */
1238
- declare function openSubpathAt(set: AnchorSet, flatIndex: number): boolean;
1239
- /** Flat indices of every anchor whose on-curve point falls inside `rect`. */
1240
- declare function anchorsInRect(set: AnchorSet, rect: AnchorRect): number[];
2732
+ declare function writeAnchorField(set: AnchorSet, flatIndexes: Iterable<number>, field: AnchorField, entry: NumberEntry): boolean;
1241
2733
 
1242
2734
  /**
1243
2735
  * Anchor lookup across several paths, measured on screen: which existing
@@ -1363,4 +2855,4 @@ declare function reviveTypedArrays(value: unknown): unknown;
1363
2855
  /** `tagTypedArrays` as a `JSON.stringify` replacer. */
1364
2856
  declare function typedArrayReplacer(_key: string, value: unknown): unknown;
1365
2857
 
1366
- export { type AnchorRect, type AnchorSet, type BezierEasing, type Box, type CubicCoords, type CurveNearest, DEFAULT_FLATTEN_TOLERANCE, EASINGS, EPS, type EasingFn, type EasingName, type EasingSpec, type GlMat3, type LineCoords, type Mat3, type NumberTypedArray, PATH_C, PATH_CMD_LENGTHS, PATH_COMMANDS, PATH_L, PATH_M, PATH_Q, PATH_Z, Path, type PathAnchorHit, PathBuilder, type PathCommandCode, type PathCommandName, PathFillRule, type PathInWorldPose, type PathNearest, PathOpenFill, type PathStation, PenAnchor, type PlaceRectOptions, type PlacedRect, type Placement, type PlacementAlign, type PlacementSide, Point, type PointAlongPathOptions, type PointInPathOptions, type PolygonIntersectsPathOptions, PolygonPath, type PoseLinear, type PoseRotation, type QuadraticCoords, type Rect, RectPath, SPRING_PRESETS, type Scale2, type SegmentHit, type SpringPreset, type SpringPresetName, type StrokeHitTestOptions, TYPED_ARRAY_CONSTRUCTORS, type TaggedTypedArray, type TypedArrayTag, type View, ZOOM_FLOOR, anchorAt, anchorCount, anchorsInRect, applyToPoint, approxEq, boundsOfCoords, boundsOfPath, boxContainsBox, boxContainsPoint, boxToBox, circlePath, clamp, clampRectWithin, composePath, cross, cubicBezierEasing, cubicBounds, cubicEvalAt, decomposePath, defaultOpenFill, deleteAnchorsAt, dot, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorSet, elevateQuadraticToCubic, ellipsePath, fillsOpenSubpaths, flatAnchorIndex, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, forEachSegment, hasOpenSubpath, identity$1 as identity, insertAnchorOnSegment, invert$1 as invert, len2, linePath, linear, locateAnchor, mat3, meanScale, moveHandleTo, multiply$1 as multiply, nearestOnCubic, nearestOnLine, nearestOnPath, nearestOnQuadratic, nearestPathAnchor, normalizeZoom, openSubpathAt, pathCommandCoordCount, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathSignedArea, pathThroughPoseFrame, placeRect, pointAlongPath, pointInPath, pointInPolygon, pointSegmentDist2, polygonContainsPath, polygonFromPoints, polygonIntersectsPath, polylineFromPoints, portControls, portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, pxExtent, quadraticEvalAt, rectMarkerPath, rectPath, rectToContour, regularPolygonPath, resolveEasing, reverseAnchors, reversePath, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roundRectPath, scale, scaleDelta, scalePathToBounds, schneiderFit, screenAngleOf, segmentAt, segmentsCross, setDefaultOpenFill, sign, splitByClosure, splitCubicAt, splitLineAt, splitQuadraticAt, splitSubpaths, squarePath, standoff, starPath, strokeHitTest, sub, tagTypedArray, tagTypedArrays, transformCoords, transformPath, translate, translateAnchorBy, translatePath, typedArrayReplacer, unionBoundsPath, unionBox, untagTypedArray, viewToMat3, withinPxBox, withinPxRadius, worldEditToStorage };
2858
+ export { ANCHOR_FIELDS, type Affine, type AnchorField, type AnchorFieldValues, type AnchorPair, type AnchorPathMeasure, AnchorSet, type AnchorSubpathMeasure, type AnchorSymmetry, type AverageAxis, type BendAlongPathOptions, type BendGrip, type BezierEasing, type BlendPathsOptions, type Box, CONSTRAINT_TOLERANCE, CORNER_RESTORE_TOLERANCE, type ConflictingConstraintsOptions, type ConstrainPathEditOptions, type ConstrainedPathEdit, type ConstrainedPathsEdit, type ConstraintPoint, type CornerCut, type CornerFrame, CornerPiece, CornerSpec, type CornerTrace, type CubicContour, type CubicCoords, type CubicSegment, type CurveNearest, DEFAULT_FLATTEN_TOLERANCE, type DirectionMark, EASINGS, EPS, type EasingFn, type EasingName, type EasingSpec, type ExtendToMeetOptions, type FramedBox, type GlMat3, type GrowRegionOptions, type HandleBalance, type HandleRef, type JoinOptions, type LengthenMode, type LineCoords, type MeasureAnchorPathOptions, type MeasurePathOptions, type NumberEntry, type NumberTypedArray, type OpenPathEnd, PATH_C, PATH_CMD_LENGTHS, PATH_COMMANDS, PATH_L, PATH_M, PATH_Q, PATH_SNAP_KINDS, PATH_Z, Path, type PathAnchorHit, type PathAwayFromAnchors, type PathBlend, PathBuilder, type PathCommandCode, type PathCommandName, type PathConstraint, type PathConstraintKind, type PathCrossing, type PathEndFrame, PathFillRule, type PathInWorldPose, type PathLink, type PathMeasure, type PathNearest, PathOpenFill, type PathSegment, type PathSnapHit, type PathSnapIndex, type PathSnapKind, type PathSnapQuery, type PathSnapTarget, type PathStation, type PathStop, type PathsEditItem, PenAnchor, type PlaceRectOptions, type PlacedRect, type Placement, type PlacementAlign, type PlacementSide, Point, type PointAlongPathOptions, type PointInPathOptions, type PointOrigin, type PolygonIntersectsPathOptions, PolygonPath, type PoseLinear, type PoseRotation, type QuadraticCoords, type Rect, RectPath, type RepeatAlongPathOptions, type RepeatStation, type ResamplePathOptions, type ResolvedCorners, SPRING_PRESETS, Scale2, type SegmentCrossing, type SegmentMeasure, type SegmentRef, type SegmentType, type SimplifyPathOptions, type SmudgePathOptions, type SoftFalloff, type SolveConstraintsOptions, type SolveConstraintsResult, type SolvePathConstraintsResult, type SpiroContour, SpiroKind, type SpiroPath, SpiroPoint, type SpiroToPathOptions, type SpringPreset, type SpringPresetName, type StaticPathSnapKind, type StrokeHitTestOptions, type SubpathEnd, TYPED_ARRAY_CONSTRUCTORS, type TaggedTypedArray, type TrimPathOptions, type TrimResult, type TypedArrayTag, type UnsatisfiedConstraintsOptions, type View, ZOOM_FLOOR, addExtremaAnchors, anchorOriginsOf, anchorSymmetries, anchorSymmetry, anchorsBefore, anchorsUnderBrush, anchorsWhere, applyToPoint, approxEq, asPolygonPath, averageAnchorsAt, balanceFactorToward, bendAlongPath, bendSegment, blendPaths, boundsOfCoords, boundsOfPath, boxContainsBox, boxContainsPoint, boxToBox, circlePath, clamp, clampRectWithin, closestOpenEnds, composePath, conflictingConstraints, constrainPathEdit, constrainPathsEdit, constraintAnchors, constraintApplies, constraintFits, convertSegments, cornerAnchors, cornerCuts, cornerFrameAt, cornerFrames, cornerTraces, createPathSnapIndex, cross, cubicAxisExtremaTs, cubicBezierEasing, cubicBounds, cubicContoursToPath, cubicEvalAt, decomposePath, defaultOpenFill, directionMarks, dot, easeIn, easeInBack, easeInBounce, easeInCirc, easeInCubic, easeInElastic, easeInExpo, easeInOut, easeInOutBack, easeInOutBounce, easeInOutCirc, easeInOutCubic, easeInOutElastic, easeInOutExpo, easeInOutQuad, easeInOutQuart, easeInOutQuint, easeInOutSine, easeInQuad, easeInQuart, easeInQuint, easeInSine, easeOut, easeOutBack, easeOutBounce, easeOutCirc, easeOutCubic, easeOutElastic, easeOutExpo, easeOutQuad, easeOutQuart, easeOutQuint, easeOutSine, editAnchorSetTracked, elevateQuadraticToCubic, ellipsePath, equalizeSegmentHandles, everyNthAnchor, extendToMeet, fillsOpenSubpaths, findPathSnap, findPathSnaps, fitAffine, fitCubicThroughDeletion, fitCubicWithTangents, flatIndexesAfterReverse, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, forEachSegment, growAnchorSelection, growRegion, handleAlongItsAngle, handleAtItsLength, handleBalances, harmonizeAnchors, hasOpenSubpath, identity$1 as identity, intersectSegments, invert$1 as invert, invertAnchorSelection, isSegmentStraight, joinAnchorsAt, joinSubpathEnds, keepCorners, len2, lengthToward, lengthenOpenEnd, lengthenPathEnd, linePath, linear, makeHandlesSymmetric, mapConstraintAnchors, mat3, meanScale, measureAnchorPath, measurePath, moveHandleSymmetric, multiply$1 as multiply, nearestOnCubic, nearestOnLine, nearestOnPath, nearestOnQuadratic, nearestPathAnchor, normalizeZoom, offsetCornerTraces, openEndAt, openEndRay, openPathEnds, pathAnchorPoints, pathAwayFromAnchors, pathCommandCoordCount, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathCrossings, pathDistanceToPoint, pathEndAnchorIndex, pathEndFrame, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathSegments, pathSignedArea, pathThroughPoseFrame, pathToCubicContours, placeRect, pointAlongPath, pointInPath, pointInPolygon, pointSegmentDist2, polygonContainsPath, polygonFromPoints, polygonIntersectsPath, polylineFromPoints, portControls, portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, prepareBlend, quadraticEvalAt, readAnchorFields, rectMarkerPath, rectPath, rectToContour, regularPolygonPath, remapAnchorValues, remapConstraints, remapPerAnchor, remapPointValues, repeatAlongPath, resamplePath, resolveCorners, resolveCornersWithSources, resolveEasing, restoreCorners, retractHandles, reverseAnchors, reversePath, reverseSubpaths, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roundRectPath, scale, scalePathToBounds, scaleSegmentHandles, schneiderFit, segmentBetween, segmentExtremaTs, segmentsBetweenAnchors, segmentsCross, setDefaultOpenFill, setSubpathStart, shrinkAnchorSelection, sign, simplifyAnchorsAt, simplifyPath, simplifySpan, simplifySubpath, smoothAnchors, smudgePath, softFalloff, softSelectionBand, softSelectionWeights, solveConstraints, solvePathConstraints, spiroContoursFromControl, spiroContoursOf, spiroControlPath, spiroToPath, splitByClosure, splitCubicAt, splitLineAt, splitQuadraticAt, splitSubpaths, squarePath, starPath, stationsAlongPath, strokeHitTest, sub, subpathEndAt, subpathsOfAnchors, tagTypedArray, tagTypedArrays, transformCoords, transformPath, translate, translateAnchorsSymmetric, translatePath, trimPathAt, typedArrayReplacer, unionBoundsPath, unionBox, unsatisfiedConstraints, untagTypedArray, viewToMat3, withAnchorPoints, worldEditToStorage, writeAnchorField };