@weasel-js/geom 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -16
- package/dist/booleans/index.d.ts +22 -3
- package/dist/booleans/index.js +41 -2
- package/dist/booleans/index.js.map +1 -1
- package/dist/{chunk-MDT7F32A.js → chunk-7WFFCHRL.js} +7 -5
- package/dist/chunk-7WFFCHRL.js.map +1 -0
- package/dist/{chunk-WQMYOCFB.js → chunk-IDXY2NCA.js} +24 -14
- package/dist/chunk-IDXY2NCA.js.map +1 -0
- package/dist/chunk-NJ3IGAKR.js +16 -0
- package/dist/chunk-NJ3IGAKR.js.map +1 -0
- package/dist/cubicMath-BW-HJrCE.d.ts +16 -0
- package/dist/curves/index.d.ts +1 -1
- package/dist/curves/index.js +1 -1
- package/dist/index.d.ts +516 -208
- package/dist/index.js +1184 -541
- package/dist/index.js.map +1 -1
- package/dist/{path-Bf9KeVxC.d.ts → path-ljHxobyu.d.ts} +1 -1
- package/dist/tessellate/index.d.ts +4 -1
- package/dist/tessellate/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-MDT7F32A.js.map +0 -1
- package/dist/chunk-WQMYOCFB.js.map +0 -1
- package/dist/chunk-XS6ERH3X.js +0 -34
- package/dist/chunk-XS6ERH3X.js.map +0 -1
- package/dist/cubicMath-CFXbq6vp.d.ts +0 -116
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { b as PathOpenFill,
|
|
2
|
-
import { P as Point
|
|
3
|
-
export {
|
|
1
|
+
import { b as PathOpenFill, a as PolygonPath, c as PathFillRule, R as RectPath, P as Path } from './path-ljHxobyu.js';
|
|
2
|
+
import { P as Point$1 } from './cubicMath-BW-HJrCE.js';
|
|
3
|
+
export { c as cubicPointAt, s as splitCubicAtT } from './cubicMath-BW-HJrCE.js';
|
|
4
4
|
export { PORT_REACH } from './nd/index.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -70,28 +70,35 @@ declare const ZOOM_FLOOR = 1e-9;
|
|
|
70
70
|
declare function normalizeZoom(zoom: number): number;
|
|
71
71
|
|
|
72
72
|
/** `[a, b, c, d, e, f]`: `x' = a·x + c·y + e`, `y' = b·x + d·y + f`. */
|
|
73
|
-
type
|
|
73
|
+
type Affine = readonly [number, number, number, number, number, number];
|
|
74
74
|
/** The transform that leaves a point where it is. */
|
|
75
|
-
declare function identity$1():
|
|
75
|
+
declare function identity$1(): Affine;
|
|
76
76
|
/** A pure translation by `(tx, ty)`. */
|
|
77
|
-
declare function translate(tx: number, ty: number):
|
|
77
|
+
declare function translate(tx: number, ty: number): Affine;
|
|
78
78
|
/** A pure scale about the origin. Scale about another point by composing
|
|
79
79
|
* translate → scale → inverse translate with `multiply`. */
|
|
80
|
-
declare function scale(sx: number, sy: number):
|
|
80
|
+
declare function scale(sx: number, sy: number): Affine;
|
|
81
81
|
/** A pure rotation about the origin, in radians, clockwise in canvas
|
|
82
82
|
* coordinates (y down). */
|
|
83
|
-
declare function rotate(rad: number):
|
|
83
|
+
declare function rotate(rad: number): Affine;
|
|
84
84
|
/** Compose: result applies `n` first, then `m` (m·n). */
|
|
85
|
-
declare function multiply$1(m:
|
|
85
|
+
declare function multiply$1(m: Affine, n: Affine): Affine;
|
|
86
86
|
/** Inverse, or null when the matrix is singular, non-finite, or too
|
|
87
87
|
* ill-conditioned to invert meaningfully at its own scale. */
|
|
88
|
-
declare function invert$1(m:
|
|
88
|
+
declare function invert$1(m: Affine): Affine | null;
|
|
89
89
|
/** Apply to a point, returning a tuple. Cold-path use; hot loops inline. */
|
|
90
|
-
declare function applyToPoint(m:
|
|
90
|
+
declare function applyToPoint(m: Affine, x: number, y: number): [number, number];
|
|
91
91
|
/** 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):
|
|
92
|
+
declare function boxToBox(sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): Affine;
|
|
93
93
|
/** Rotation by `rad` about pivot (cx,cy): translate(c)·rotate·translate(-c). */
|
|
94
|
-
declare function rotateAboutPoint(cx: number, cy: number, rad: number):
|
|
94
|
+
declare function rotateAboutPoint(cx: number, cy: number, rad: number): Affine;
|
|
95
|
+
/**
|
|
96
|
+
* Apply an affine to an interleaved coord stream, returning a fresh f64
|
|
97
|
+
* buffer. Command codes are unaffected — for a Bezier the transformed control
|
|
98
|
+
* points define the transformed curve exactly (affine invariance), so callers
|
|
99
|
+
* pass `path.coords` straight through and keep `path.commands` as-is.
|
|
100
|
+
*/
|
|
101
|
+
declare function transformCoords(coords: ArrayLike<number>, m: Affine): Float64Array;
|
|
95
102
|
|
|
96
103
|
/** Axis-aligned box as [minX, minY, maxX, maxY]. */
|
|
97
104
|
type Box = readonly [number, number, number, number];
|
|
@@ -291,14 +298,15 @@ declare function flattenQuadratic(x0: number, y0: number, x1: number, y1: number
|
|
|
291
298
|
* Like `flattenCubic` but also appends to `arcOut`, for each new flattened
|
|
292
299
|
* point, its *cumulative distance from the segment start* — not a normalized
|
|
293
300
|
* fraction. The caller divides each by the segment's total flattened arc
|
|
294
|
-
* length to yield t ∈ (0, 1].
|
|
301
|
+
* length to yield t ∈ (0, 1]. `maxTurn`, in radians, also bounds how far each
|
|
302
|
+
* piece's control polygon may turn.
|
|
295
303
|
*/
|
|
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;
|
|
304
|
+
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
305
|
/** Flatten a quadratic bezier into line segments within `tolerance`, appending
|
|
298
306
|
* points to `out` and their cumulative arc lengths to `arcOut`. The arc
|
|
299
307
|
* lengths are what lets per-anchor color and dash phase be interpolated
|
|
300
308
|
* 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;
|
|
309
|
+
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
310
|
|
|
303
311
|
/**
|
|
304
312
|
* Even-odd ray-cast point-in-polygon over an interleaved, unclosed contour
|
|
@@ -452,12 +460,12 @@ declare function translatePath(path: Path, dx: number, dy: number): Path;
|
|
|
452
460
|
*/
|
|
453
461
|
declare function scalePathToBounds(path: Path, target: RectPath): Path;
|
|
454
462
|
|
|
455
|
-
/** Apply an
|
|
463
|
+
/** Apply an `Affine` to a path's geometry. Never mutates the input.
|
|
456
464
|
*
|
|
457
465
|
* Axis-aligned maps (no rotation/shear: `m[1]` and `m[2]` within 1e-9 of 0) keep a
|
|
458
466
|
* `RectPath` a rect, normalizing negative extent (mirror). Rotation or
|
|
459
467
|
* shear promotes the rect's four corners to a `PolygonPath`. */
|
|
460
|
-
declare function transformPath(path: Path, m:
|
|
468
|
+
declare function transformPath(path: Path, m: Affine): Path;
|
|
461
469
|
|
|
462
470
|
/**
|
|
463
471
|
* Translation-only compose/decompose for `Path` poses, the analog of
|
|
@@ -549,9 +557,9 @@ declare function pathFromD(d: string, onWarn?: (message: string) => void): Retur
|
|
|
549
557
|
|
|
550
558
|
/** The answer `pointAlongPath` gives: a point on the path and its heading there. */
|
|
551
559
|
interface PathStation {
|
|
552
|
-
point: Point;
|
|
560
|
+
point: Point$1;
|
|
553
561
|
/** Unit vector along the path at `point`, pointing toward the end. */
|
|
554
|
-
tangent: Point;
|
|
562
|
+
tangent: Point$1;
|
|
555
563
|
}
|
|
556
564
|
/** Options for `pointAlongPath`. */
|
|
557
565
|
interface PointAlongPathOptions {
|
|
@@ -585,6 +593,102 @@ declare function pointAlongPath(path: Path, t: number, opts?: PointAlongPathOpti
|
|
|
585
593
|
* combine with `pathContainsPoint` at the call site. */
|
|
586
594
|
declare function pathDistanceToPoint(path: Path, px: number, py: number): number;
|
|
587
595
|
|
|
596
|
+
/**
|
|
597
|
+
* Counts the path anchors used by the per-anchor coloring surface. An
|
|
598
|
+
* "anchor" is the destination point of a path command: M, L, C, Q each
|
|
599
|
+
* contribute one (the (x, y) where the pen ends up); Z contributes none
|
|
600
|
+
* (it closes back to the subpath's first M). RectPath has four implicit
|
|
601
|
+
* anchors — the corners — matching its M/L/L/L/Z stroke tessellation.
|
|
602
|
+
*
|
|
603
|
+
* Consumers use this to size their per-anchor color array; the renderer
|
|
604
|
+
* uses it to validate the array length in dev builds.
|
|
605
|
+
*/
|
|
606
|
+
|
|
607
|
+
/** One anchor of an editable path: its on-curve point plus the two control
|
|
608
|
+
* handles that shape the segments either side of it. Handles are in the same
|
|
609
|
+
* space as the point, and absent when the adjoining segment is straight. */
|
|
610
|
+
interface PenAnchor {
|
|
611
|
+
x: number;
|
|
612
|
+
y: number;
|
|
613
|
+
inHandle?: {
|
|
614
|
+
x: number;
|
|
615
|
+
y: number;
|
|
616
|
+
};
|
|
617
|
+
outHandle?: {
|
|
618
|
+
x: number;
|
|
619
|
+
y: number;
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Derive a per-subpath anchor model from a PolygonPath. Subpaths split on
|
|
624
|
+
* every `M` command; a subpath is closed iff it ends with `Z`.
|
|
625
|
+
*
|
|
626
|
+
* Cubic-segment control points become the outHandle of the previous anchor
|
|
627
|
+
* and the inHandle of the next anchor. A closed subpath whose last curve ends
|
|
628
|
+
* exactly on its first point holds that point twice; the two are one anchor,
|
|
629
|
+
* and the curve's second control is that anchor's inHandle. Quadratic segments are upgraded to
|
|
630
|
+
* cubics (each control reused for both adjacent handles) — this loses no
|
|
631
|
+
* geometry. Linear segments produce anchors with no handles.
|
|
632
|
+
*/
|
|
633
|
+
declare function pathToAnchors(path: PolygonPath): {
|
|
634
|
+
anchors: PenAnchor[][];
|
|
635
|
+
closed: boolean[];
|
|
636
|
+
};
|
|
637
|
+
/**
|
|
638
|
+
* Serialize the per-subpath anchor model back to a PolygonPath. Inverse of
|
|
639
|
+
* `pathToAnchors`. Curve-vs-line decision per segment:
|
|
640
|
+
* - Both adjacent handles absent → straight L segment.
|
|
641
|
+
* - Either handle present → C segment, with missing handles defaulting to
|
|
642
|
+
* the anchor point itself (degenerate but valid; renders as a near-line).
|
|
643
|
+
* Given `like`, the path being reshaped, the result keeps its fill rules.
|
|
644
|
+
*/
|
|
645
|
+
declare function anchorsToPath(anchors: PenAnchor[][], closed: boolean[], like?: PolygonPath): PolygonPath;
|
|
646
|
+
/**
|
|
647
|
+
* Returns true if the anchor's in-handle and out-handle are collinear with
|
|
648
|
+
* the anchor point, indicating "smooth" (mirror-drag) behavior. Detected via
|
|
649
|
+
* the magnitude of the normalized cross product of the two handle vectors.
|
|
650
|
+
*
|
|
651
|
+
* Edge cases:
|
|
652
|
+
* - Missing in or out handle → false (corner by definition).
|
|
653
|
+
* - Either handle at zero distance from anchor → false.
|
|
654
|
+
*/
|
|
655
|
+
declare function isAnchorSmooth(a: PenAnchor): boolean;
|
|
656
|
+
/** How many anchors a path has. Matches the per-anchor arrays the renderer
|
|
657
|
+
* expects for `vertexColors`. */
|
|
658
|
+
declare function countPathAnchors(path: Path): number;
|
|
659
|
+
/** The segment `(sub, segIdx)` as its two end anchors, or null when it
|
|
660
|
+
* doesn't exist. `segIdx === sub.length - 1` names a closed subpath's
|
|
661
|
+
* closing segment, which runs from the last anchor back to the first. */
|
|
662
|
+
declare function segmentEnds(anchors: readonly PenAnchor[][], closed: readonly boolean[] | undefined, sub: number, segIdx: number): {
|
|
663
|
+
a: PenAnchor;
|
|
664
|
+
b: PenAnchor;
|
|
665
|
+
} | null;
|
|
666
|
+
/** Whether the segment from `a` to `b` is a straight line (no handles). */
|
|
667
|
+
declare function isStraightSegment(a: PenAnchor, b: PenAnchor): boolean;
|
|
668
|
+
/** The segment of a decoded path nearest `(wx, wy)`, and the parameter `t`
|
|
669
|
+
* on it closest to that point. Pass `closed` (from `pathToAnchors`) to
|
|
670
|
+
* include each closed subpath's closing segment, reported as
|
|
671
|
+
* `segIdx === sub.length - 1`. `t` spans `[0, 1]`, endpoints included.
|
|
672
|
+
* Returns `null` only when there are no segments. */
|
|
673
|
+
declare function nearestSegmentT(anchors: PenAnchor[][], wx: number, wy: number, closed?: readonly boolean[]): {
|
|
674
|
+
sub: number;
|
|
675
|
+
segIdx: number;
|
|
676
|
+
t: number;
|
|
677
|
+
} | null;
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Controls for the one cubic that replaces `prev → removed → next` when
|
|
681
|
+
* `removed` is deleted. The new controls keep the directions the curve leaves
|
|
682
|
+
* `prev` and arrives at `next` with, and their lengths are least-squares fitted
|
|
683
|
+
* to samples of the two segments being replaced, so the curve keeps its shape
|
|
684
|
+
* as closely as one cubic can. Where `removed` was inserted by splitting a
|
|
685
|
+
* cubic, the original cubic comes back.
|
|
686
|
+
*/
|
|
687
|
+
declare function fitCubicThroughDeletion(prev: PenAnchor, removed: PenAnchor, next: PenAnchor): {
|
|
688
|
+
c1: Point$1;
|
|
689
|
+
c2: Point$1;
|
|
690
|
+
};
|
|
691
|
+
|
|
588
692
|
/**
|
|
589
693
|
* Schneider (1990, Graphics Gems I) adaptive cubic-Bezier fitter.
|
|
590
694
|
* Given a sequence of sample points, returns a `PolygonPath` whose `C`
|
|
@@ -594,7 +698,273 @@ declare function pathDistanceToPoint(path: Path, px: number, py: number): number
|
|
|
594
698
|
* `C` cubic segments. For colinear / two-point inputs, returns a single
|
|
595
699
|
* degenerate cubic. For empty input, returns an empty polygon path.
|
|
596
700
|
*/
|
|
597
|
-
declare function schneiderFit(samples: ReadonlyArray<Point>, errorTolerance: number): PolygonPath;
|
|
701
|
+
declare function schneiderFit(samples: ReadonlyArray<Point$1>, errorTolerance: number): PolygonPath;
|
|
702
|
+
/**
|
|
703
|
+
* The single cubic from the first sample to the last that best fits the
|
|
704
|
+
* samples between, with its control points held to the given unit tangents:
|
|
705
|
+
* `tStart` leaving the first sample, `tEnd` leaving the last one backward.
|
|
706
|
+
* Only the two handle lengths are solved for.
|
|
707
|
+
*
|
|
708
|
+
* `params` is the curve parameter each sample starts at, chord length when
|
|
709
|
+
* omitted; `passes` is how many times those are refined and the lengths
|
|
710
|
+
* solved again.
|
|
711
|
+
*/
|
|
712
|
+
declare function fitCubicWithTangents(samples: ReadonlyArray<Point$1>, tStart: Point$1, tEnd: Point$1, opts?: {
|
|
713
|
+
params?: ReadonlyArray<number>;
|
|
714
|
+
passes?: number;
|
|
715
|
+
}): {
|
|
716
|
+
cp1: Point$1;
|
|
717
|
+
cp2: Point$1;
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
/** Options for {@link simplifyPath}. */
|
|
721
|
+
interface SimplifyPathOptions {
|
|
722
|
+
/**
|
|
723
|
+
* An anchor whose path turns by more than this many degrees is a corner:
|
|
724
|
+
* it is kept, and the curve is refitted on either side of it. Default 40.
|
|
725
|
+
*/
|
|
726
|
+
cornerAngle?: number;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* `path` refitted with as few anchors as hold it within `tolerance`: every
|
|
730
|
+
* point sampled along the original lies within `tolerance` of the result.
|
|
731
|
+
* Corners (see {@link SimplifyPathOptions.cornerAngle}) and the ends of open
|
|
732
|
+
* subpaths stay where they are; the runs between them are refitted as smooth
|
|
733
|
+
* cubics, Schneider-style, and a run that lies within `tolerance` of a line
|
|
734
|
+
* becomes one straight segment. Fill rules carry over.
|
|
735
|
+
*/
|
|
736
|
+
declare function simplifyPath(path: PolygonPath, tolerance: number, opts?: SimplifyPathOptions): PolygonPath;
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Screen-pixel lengths in world units, per axis.
|
|
740
|
+
*
|
|
741
|
+
* Chrome declares its hit zones in screen pixels — an 8px handle stays 8px at
|
|
742
|
+
* every zoom, because it paints in screen space. Converting one such length to
|
|
743
|
+
* world units with a single scalar (see `meanScale`) is exact only under
|
|
744
|
+
* uniform zoom; per-axis it is too generous on one axis and too mean on the
|
|
745
|
+
* other, so the pickable region stops matching the painted one.
|
|
746
|
+
*
|
|
747
|
+
* Prefer {@link withinPxBox} / {@link withinPxRadius}, which compare in screen
|
|
748
|
+
* space and so can't drift from the paint at all. {@link pxExtent} is for the
|
|
749
|
+
* cases that must stay in world units — a tolerance handed to geometry that
|
|
750
|
+
* doesn't know about the view.
|
|
751
|
+
*/
|
|
752
|
+
interface Scale2 {
|
|
753
|
+
x: number;
|
|
754
|
+
y: number;
|
|
755
|
+
}
|
|
756
|
+
/** One screen-pixel length as world-space extents, per axis. `scale` is a
|
|
757
|
+
* view's, so neither axis is zero (see `View`).
|
|
758
|
+
*
|
|
759
|
+
* With `rotation`, the axes are a frame turned that far from world, and each
|
|
760
|
+
* extent is how far along it an edge across it must move to move `px` on
|
|
761
|
+
* screen — which, under non-uniform zoom, is not the extent along the screen
|
|
762
|
+
* axis nearest to it. */
|
|
763
|
+
declare function pxExtent(px: number, scale: Scale2, rotation?: number): {
|
|
764
|
+
x: number;
|
|
765
|
+
y: number;
|
|
766
|
+
};
|
|
767
|
+
/** World delta → screen delta. Only the scale participates: translation
|
|
768
|
+
* cancels in a difference, and the kit's views carry no skew. */
|
|
769
|
+
declare function scaleDelta(dx: number, dy: number, scale: Scale2): {
|
|
770
|
+
x: number;
|
|
771
|
+
y: number;
|
|
772
|
+
};
|
|
773
|
+
/**
|
|
774
|
+
* Is a world-space delta inside a screen-space square of half-extent `px`?
|
|
775
|
+
*
|
|
776
|
+
* The square is axis-aligned **on screen**, which is what a handle painted in
|
|
777
|
+
* screen space actually is — so this stays true under non-uniform zoom and
|
|
778
|
+
* under a rotated target, where an axis-aligned world-space test is a rotated
|
|
779
|
+
* rectangle on screen.
|
|
780
|
+
*/
|
|
781
|
+
declare function withinPxBox(dx: number, dy: number, px: number, scale: Scale2): boolean;
|
|
782
|
+
/** Is a world-space delta inside a screen-space circle of radius `px`? */
|
|
783
|
+
declare function withinPxRadius(dx: number, dy: number, px: number, scale: Scale2): boolean;
|
|
784
|
+
/**
|
|
785
|
+
* A world point pushed `px` screen pixels off an edge, along the edge's normal
|
|
786
|
+
* as it lands on screen. `normal` is the edge's outward normal in world space.
|
|
787
|
+
*
|
|
788
|
+
* Scaling the world normal by the view is not the same thing: under
|
|
789
|
+
* non-uniform zoom the image of a normal is no longer perpendicular to the
|
|
790
|
+
* image of its edge, so chrome placed that way drifts sideways and sits closer
|
|
791
|
+
* than `px`. Returns the point in world coords and the on-screen unit normal.
|
|
792
|
+
*/
|
|
793
|
+
declare function standoff(anchor: {
|
|
794
|
+
x: number;
|
|
795
|
+
y: number;
|
|
796
|
+
}, normal: {
|
|
797
|
+
x: number;
|
|
798
|
+
y: number;
|
|
799
|
+
}, px: number, scale: Scale2): {
|
|
800
|
+
x: number;
|
|
801
|
+
y: number;
|
|
802
|
+
nx: number;
|
|
803
|
+
ny: number;
|
|
804
|
+
};
|
|
805
|
+
/** The screen angle a frame turned `rotation` from world lands its x axis at.
|
|
806
|
+
* Equals `rotation` under uniform zoom; non-uniform zoom bends it. */
|
|
807
|
+
declare function screenAngleOf(rotation: number, scale: Scale2): number;
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Pure anchor-set edits on the model `pathToAnchors` produces: subpath-major
|
|
811
|
+
* `PenAnchor[][]` plus a parallel `closed: boolean[]`. Every function here
|
|
812
|
+
* mutates that structure in place; callers decode with `pathToAnchors`,
|
|
813
|
+
* apply, and re-encode with `anchorsToPath` (see {@link editAnchorSet}).
|
|
814
|
+
*
|
|
815
|
+
* Callers address anchors by flat index — the sequential position in walk
|
|
816
|
+
* order across all subpaths, which is what `enumerateAnchors` reports.
|
|
817
|
+
* {@link locateAnchor} and {@link flatAnchorIndex} convert to and from the
|
|
818
|
+
* `(sub, idx)` pair.
|
|
819
|
+
*
|
|
820
|
+
* The two orders agree because `pathToAnchors` and `enumerateAnchors`
|
|
821
|
+
* walk the same command stream and emit one anchor per M/L/C/Q, less the
|
|
822
|
+
* end of a closing curve that lands on its subpath's first point.
|
|
823
|
+
* `countPathAnchors`, which sizes the renderer's per-anchor arrays, counts
|
|
824
|
+
* that end too and so can be one higher per closed subpath.
|
|
825
|
+
*/
|
|
826
|
+
|
|
827
|
+
/** Subpath-major anchor model — the decoded form of a `PolygonPath`. */
|
|
828
|
+
interface AnchorSet {
|
|
829
|
+
anchors: PenAnchor[][];
|
|
830
|
+
closed: boolean[];
|
|
831
|
+
}
|
|
832
|
+
/** A world-space rectangle, as produced by a drag-rect gesture. */
|
|
833
|
+
interface AnchorRect {
|
|
834
|
+
x: number;
|
|
835
|
+
y: number;
|
|
836
|
+
width: number;
|
|
837
|
+
height: number;
|
|
838
|
+
}
|
|
839
|
+
/** Total anchors across every subpath. */
|
|
840
|
+
declare function anchorCount(set: AnchorSet): number;
|
|
841
|
+
/** Flat index → `(sub, idx)`. Returns null when out of range. */
|
|
842
|
+
declare function locateAnchor(set: AnchorSet, flatIndex: number): {
|
|
843
|
+
sub: number;
|
|
844
|
+
idx: number;
|
|
845
|
+
} | null;
|
|
846
|
+
/** `(sub, idx)` → flat index. Returns -1 when out of range. */
|
|
847
|
+
declare function flatAnchorIndex(set: AnchorSet, sub: number, idx: number): number;
|
|
848
|
+
/** Resolve a flat index to the anchor itself. Null when out of range. */
|
|
849
|
+
declare function anchorAt(set: AnchorSet, flatIndex: number): PenAnchor | null;
|
|
850
|
+
/**
|
|
851
|
+
* Decode `path`, hand the mutable anchor set to `edit`, and re-encode.
|
|
852
|
+
* Returns null when `edit` returns `false` (it changed nothing), so callers
|
|
853
|
+
* can skip a no-op commit.
|
|
854
|
+
*/
|
|
855
|
+
declare function editAnchorSet(path: PolygonPath, edit: (set: AnchorSet) => boolean | void): PolygonPath | null;
|
|
856
|
+
/** Translate an anchor and both of its handles by `(dx, dy)`. */
|
|
857
|
+
declare function translateAnchorBy(set: AnchorSet, flatIndex: number, dx: number, dy: number): boolean;
|
|
858
|
+
/**
|
|
859
|
+
* Move one control handle to an absolute world position. When the anchor was
|
|
860
|
+
* smooth (handles collinear through the anchor) and `breakSmoothness` is
|
|
861
|
+
* false, the opposite handle is mirrored so the curve stays smooth.
|
|
862
|
+
*/
|
|
863
|
+
declare function moveHandleTo(set: AnchorSet, flatIndex: number, side: 'in' | 'out', toX: number, toY: number, breakSmoothness: boolean): boolean;
|
|
864
|
+
/**
|
|
865
|
+
* Split the segment `(sub, segIdx) → (sub, segIdx + 1)` at parameter `t`,
|
|
866
|
+
* inserting a new anchor there. On a closed subpath `segIdx` may also name
|
|
867
|
+
* the closing segment (`sub.length - 1`); its new anchor is appended.
|
|
868
|
+
*
|
|
869
|
+
* A straight segment stays straight — the new anchor has no handles. A curved
|
|
870
|
+
* one splits by de Casteljau, so the two halves trace the original path.
|
|
871
|
+
*
|
|
872
|
+
* Returns the new anchor's flat index, or -1 when the segment doesn't
|
|
873
|
+
* exist.
|
|
874
|
+
*/
|
|
875
|
+
declare function insertAnchorOnSegment(set: AnchorSet, args: {
|
|
876
|
+
sub: number;
|
|
877
|
+
segIdx: number;
|
|
878
|
+
t: number;
|
|
879
|
+
}): number;
|
|
880
|
+
/** A point on a segment, as {@link segmentAt} reports it. */
|
|
881
|
+
interface SegmentHit {
|
|
882
|
+
sub: number;
|
|
883
|
+
segIdx: number;
|
|
884
|
+
/** Parameter on the segment — what {@link insertAnchorOnSegment} takes. */
|
|
885
|
+
t: number;
|
|
886
|
+
/** The point on the path at `t`, in the set's own space. */
|
|
887
|
+
x: number;
|
|
888
|
+
y: number;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* The segment point under `(x, y)`, when it lies within `tolerancePx` of
|
|
892
|
+
* the path on screen and no nearer than that to either of the segment's
|
|
893
|
+
* own anchors — a press on an anchor is about the anchor, not the segment
|
|
894
|
+
* beside it.
|
|
895
|
+
*
|
|
896
|
+
* `scale` is the view's, so the tolerance holds per axis under
|
|
897
|
+
* non-uniform zoom. The search runs in screen-scaled space: scaling is
|
|
898
|
+
* affine, so the curve's parameterization, and therefore `t`, carries
|
|
899
|
+
* straight back to world space.
|
|
900
|
+
*/
|
|
901
|
+
declare function segmentAt(set: AnchorSet, x: number, y: number, opts: {
|
|
902
|
+
tolerancePx: number;
|
|
903
|
+
scale?: Scale2;
|
|
904
|
+
}): SegmentHit | null;
|
|
905
|
+
/**
|
|
906
|
+
* Delete the anchors at `flatIndexes`. Where a deleted anchor had
|
|
907
|
+
* neighbors on both sides — across the seam too, on a closed subpath — the
|
|
908
|
+
* surviving segment is refitted to the two it replaces (`fitCubicThroughDeletion`)
|
|
909
|
+
* so the path keeps its shape; a gap between two straight segments closes straight.
|
|
910
|
+
*
|
|
911
|
+
* Subpaths left with fewer than two anchors are dropped entirely. If that
|
|
912
|
+
* would leave the path with no subpaths at all, the whole deletion is refused
|
|
913
|
+
* (returns false, `set` untouched): an empty path leaves the node selected
|
|
914
|
+
* and in the scene with nothing on screen and no anchors to grab it by.
|
|
915
|
+
*/
|
|
916
|
+
declare function deleteAnchorsAt(set: AnchorSet, flatIndexes: Iterable<number>): boolean;
|
|
917
|
+
/**
|
|
918
|
+
* Scissors: open a closed subpath at the given anchor. The anchor list is
|
|
919
|
+
* rotated to start there and a second anchor at the same point ends it, so
|
|
920
|
+
* every segment survives and the two ends can be pulled apart. No-ops on an
|
|
921
|
+
* already-open subpath — there's nothing to cut.
|
|
922
|
+
*/
|
|
923
|
+
declare function openSubpathAt(set: AnchorSet, flatIndex: number): boolean;
|
|
924
|
+
/** Flat indices of every anchor whose on-curve point falls inside `rect`. */
|
|
925
|
+
declare function anchorsInRect(set: AnchorSet, rect: AnchorRect): number[];
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Average and Join: anchor-set edits that bring anchors together. Both mutate
|
|
929
|
+
* the model `pathToAnchors` produces in place, like the edits in
|
|
930
|
+
* `anchorEdits`, and address anchors by flat index.
|
|
931
|
+
*/
|
|
932
|
+
|
|
933
|
+
/** Which coordinates {@link averageAnchorsAt} averages: `'x'` lines the
|
|
934
|
+
* anchors up on one vertical, `'y'` on one horizontal, `'both'` stacks them
|
|
935
|
+
* on one point. */
|
|
936
|
+
type AverageAxis = 'x' | 'y' | 'both';
|
|
937
|
+
/** One end of an open subpath. */
|
|
938
|
+
interface SubpathEnd {
|
|
939
|
+
sub: number;
|
|
940
|
+
end: 'first' | 'last';
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Move each anchor in `flatIndexes` to their mean position, along `axis`.
|
|
944
|
+
* Each anchor carries its handles with it. Returns false, changing nothing,
|
|
945
|
+
* when fewer than two of the indexes name anchors.
|
|
946
|
+
*/
|
|
947
|
+
declare function averageAnchorsAt(set: AnchorSet, flatIndexes: Iterable<number>, axis?: AverageAxis): boolean;
|
|
948
|
+
/** The open-subpath end anchor `flatIndex` names, or null for an interior
|
|
949
|
+
* anchor or one on a closed subpath. A lone anchor reads as `'last'`. */
|
|
950
|
+
declare function subpathEndAt(set: AnchorSet, flatIndex: number): SubpathEnd | null;
|
|
951
|
+
/**
|
|
952
|
+
* Join the two open-subpath ends `a` and `b`. The two ends of one subpath
|
|
953
|
+
* close it; ends of two subpaths make one subpath, `a`'s run followed by
|
|
954
|
+
* `b`'s, in the slot of whichever came first. Ends that coincide merge into
|
|
955
|
+
* one anchor, keeping the handle on either side; ends apart are bridged by a
|
|
956
|
+
* straight segment. Returns false, changing nothing, when the ends are not
|
|
957
|
+
* two distinct ends of open subpaths.
|
|
958
|
+
*/
|
|
959
|
+
declare function joinSubpathEnds(set: AnchorSet, a: SubpathEnd, b: SubpathEnd): boolean;
|
|
960
|
+
/** {@link joinSubpathEnds} for two anchors named by flat index, each the end
|
|
961
|
+
* of an open subpath. */
|
|
962
|
+
declare function joinAnchorsAt(set: AnchorSet, flatA: number, flatB: number): boolean;
|
|
963
|
+
/**
|
|
964
|
+
* The two open-subpath ends nearest each other that `accept` allows, on two
|
|
965
|
+
* different subpaths. Null when there are no two such subpaths.
|
|
966
|
+
*/
|
|
967
|
+
declare function closestOpenEnds(set: AnchorSet, accept?: (a: SubpathEnd, b: SubpathEnd) => boolean): [SubpathEnd, SubpathEnd] | null;
|
|
598
968
|
|
|
599
969
|
/**
|
|
600
970
|
* Path hit-testing: path-vs-point (filled region and stroke distance),
|
|
@@ -630,7 +1000,7 @@ interface StrokeHitTestOptions {
|
|
|
630
1000
|
* transform no world distance equals it. */
|
|
631
1001
|
slop?: {
|
|
632
1002
|
px: number;
|
|
633
|
-
transform:
|
|
1003
|
+
transform: Affine;
|
|
634
1004
|
};
|
|
635
1005
|
}
|
|
636
1006
|
/** Filled-region hit-test for a path. Rect short-circuits to AABB; polygons run ray-cast / winding per `fillRule`. */
|
|
@@ -650,15 +1020,15 @@ declare function pathContainsRect(path: Path, rect: Rect, opts?: PointInPathOpti
|
|
|
650
1020
|
/** Returns true if `rect` overlaps (intersects or contains) `path`. */
|
|
651
1021
|
declare function pathIntersectsRect(path: Path, rect: Rect, opts?: PointInPathOptions): boolean;
|
|
652
1022
|
/** Returns true if every vertex of `polygon` lies inside `path`. */
|
|
653
|
-
declare function pathContainsPolygon(path: Path, polygon: readonly Point[], opts?: PointInPathOptions): boolean;
|
|
1023
|
+
declare function pathContainsPolygon(path: Path, polygon: readonly Point$1[], opts?: PointInPathOptions): boolean;
|
|
654
1024
|
/** Returns true if `polygon` overlaps (intersects or is contained by) `path`. */
|
|
655
|
-
declare function pathIntersectsPolygon(path: Path, polygon: readonly Point[], opts?: PointInPathOptions): boolean;
|
|
1025
|
+
declare function pathIntersectsPolygon(path: Path, polygon: readonly Point$1[], opts?: PointInPathOptions): boolean;
|
|
656
1026
|
/**
|
|
657
1027
|
* Returns true if all of `path` — every subpath, open ones included, beziers
|
|
658
1028
|
* flattened — lies inside the closed `polygon`: the inverse of
|
|
659
1029
|
* {@link pathContainsPolygon}. A lasso enclosing a shape asks this.
|
|
660
1030
|
*/
|
|
661
|
-
declare function polygonContainsPath(polygon: readonly Point[], path: Path, opts?: PointInPathOptions): boolean;
|
|
1031
|
+
declare function polygonContainsPath(polygon: readonly Point$1[], path: Path, opts?: PointInPathOptions): boolean;
|
|
662
1032
|
/**
|
|
663
1033
|
* Returns true if any of `path` meets the closed `polygon`: its filled region,
|
|
664
1034
|
* or the line of an open subpath, which encloses nothing. With `filled: false`
|
|
@@ -666,15 +1036,7 @@ declare function polygonContainsPath(polygon: readonly Point[], path: Path, opts
|
|
|
666
1036
|
* flattened, so a control point off the curve never counts. The companion of
|
|
667
1037
|
* {@link polygonContainsPath}; a lasso touching a shape asks this.
|
|
668
1038
|
*/
|
|
669
|
-
declare function polygonIntersectsPath(polygon: readonly Point[], path: Path, opts?: PolygonIntersectsPathOptions): boolean;
|
|
670
|
-
|
|
671
|
-
/**
|
|
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.
|
|
676
|
-
*/
|
|
677
|
-
declare function transformCoords(coords: ArrayLike<number>, m: Mat3): Float64Array;
|
|
1039
|
+
declare function polygonIntersectsPath(polygon: readonly Point$1[], path: Path, opts?: PolygonIntersectsPathOptions): boolean;
|
|
678
1040
|
|
|
679
1041
|
/**
|
|
680
1042
|
* Ergonomic builders for tiny shape paths used by chrome / overlays / markers.
|
|
@@ -730,9 +1092,9 @@ declare function translated(m: GlMat3, tx: number, ty: number): GlMat3;
|
|
|
730
1092
|
declare function scaled(m: GlMat3, sx: number, sy: number): GlMat3;
|
|
731
1093
|
/** This layout read as the kernel's 6-element affine. The two carry the same
|
|
732
1094
|
* logical order, so the conversion is a repack and nothing else. */
|
|
733
|
-
declare function toAffine(m: GlMat3):
|
|
1095
|
+
declare function toAffine(m: GlMat3): Affine;
|
|
734
1096
|
/** The kernel's affine in this layout. The inverse of {@link toAffine}. */
|
|
735
|
-
declare function fromAffine(a:
|
|
1097
|
+
declare function fromAffine(a: Affine): GlMat3;
|
|
736
1098
|
/** Inverse of an affine matrix, or `null` when `@weasel-js/geom`'s `invert`
|
|
737
1099
|
* finds it singular — the same rule, read through this layout. */
|
|
738
1100
|
declare function invert(m: GlMat3): GlMat3 | null;
|
|
@@ -757,7 +1119,7 @@ declare function meanScaleOf(m: GlMat3): number;
|
|
|
757
1119
|
declare function minScaleOf(m: GlMat3): number;
|
|
758
1120
|
/** The renderer's 3x3 matrix operations, as one namespace. These work on the
|
|
759
1121
|
* 9-element form the GL uniform upload wants — distinct from the 6-element
|
|
760
|
-
*
|
|
1122
|
+
* `Affine`, though the logical element order is the same. */
|
|
761
1123
|
declare const mat3: {
|
|
762
1124
|
identity: typeof identity;
|
|
763
1125
|
toAffine: typeof toAffine;
|
|
@@ -996,77 +1358,6 @@ declare function meanScale(s: {
|
|
|
996
1358
|
y: number;
|
|
997
1359
|
}): number;
|
|
998
1360
|
|
|
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
1361
|
/** Pivot (`cx`, `cy`) and angle resolved from a pose's rotation convention. */
|
|
1071
1362
|
interface PoseRotation {
|
|
1072
1363
|
cx: number;
|
|
@@ -1098,7 +1389,7 @@ declare function rotatePathAround(path: Path, cx: number, cy: number, rotation:
|
|
|
1098
1389
|
/**
|
|
1099
1390
|
* The linear part of a box pose that its box, rotation, and flips cannot
|
|
1100
1391
|
* hold: a skew, or a scale that differs by axis. `[a, b, c, d]` in
|
|
1101
|
-
* `
|
|
1392
|
+
* `Affine` order (`x' = a·x + c·y`, `y' = b·x + d·y`), applied about the
|
|
1102
1393
|
* box center to what the node draws in its unrotated box, before the
|
|
1103
1394
|
* rotation. Absent is the identity.
|
|
1104
1395
|
*/
|
|
@@ -1111,133 +1402,150 @@ declare function poseLinearOf(pose: unknown): PoseLinear | null;
|
|
|
1111
1402
|
* `null` when the pose has neither, or is not a box, so a caller can skip
|
|
1112
1403
|
* the step.
|
|
1113
1404
|
*/
|
|
1114
|
-
declare function poseFrameOf(pose: unknown):
|
|
1405
|
+
declare function poseFrameOf(pose: unknown): Affine | null;
|
|
1115
1406
|
/**
|
|
1116
1407
|
* The inverse of {@link poseFrameOf}: where a parent-frame point falls in the
|
|
1117
1408
|
* pose's unrotated box frame. `null` when there is no frame to undo, or when
|
|
1118
1409
|
* the `transform` is singular and nothing maps back.
|
|
1119
1410
|
*/
|
|
1120
|
-
declare function poseFrameInverseOf(pose: unknown):
|
|
1411
|
+
declare function poseFrameInverseOf(pose: unknown): Affine | null;
|
|
1121
1412
|
/** `path`, given in the pose's unrotated box frame, as the node draws it. */
|
|
1122
1413
|
declare function pathThroughPoseFrame(path: Path, pose: unknown): Path;
|
|
1123
1414
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
*
|
|
1127
|
-
*
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
1130
|
-
*
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
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.
|
|
1140
|
-
*/
|
|
1141
|
-
|
|
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;
|
|
1415
|
+
type Side = 'in' | 'out';
|
|
1416
|
+
/**
|
|
1417
|
+
* How a path maps onto itself under one transform `m`: `partners[i]` is the
|
|
1418
|
+
* flat index of the anchor that anchor `i` lands on, and `flips[i]` whether
|
|
1419
|
+
* its in-handle lands on that anchor's out-handle — a mirror reverses which
|
|
1420
|
+
* way a path runs, a rotation does not. An anchor `m` fixes is its own
|
|
1421
|
+
* partner.
|
|
1422
|
+
*/
|
|
1423
|
+
interface AnchorSymmetry {
|
|
1424
|
+
readonly m: Affine;
|
|
1425
|
+
readonly partners: readonly number[];
|
|
1426
|
+
readonly flips: readonly boolean[];
|
|
1153
1427
|
}
|
|
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
1428
|
/**
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1429
|
+
* The pairing that makes `set` its own image under `m`, or `null` when it is
|
|
1430
|
+
* not one: every anchor, handles included, must land on a distinct anchor
|
|
1431
|
+
* within `tolerance` on each axis.
|
|
1169
1432
|
*/
|
|
1170
|
-
declare function
|
|
1171
|
-
/**
|
|
1172
|
-
declare function
|
|
1433
|
+
declare function anchorSymmetry(set: AnchorSet, m: Affine, tolerance?: number): AnchorSymmetry | null;
|
|
1434
|
+
/** Every transform in `group` that `set` is its own image under, paired. */
|
|
1435
|
+
declare function anchorSymmetries(set: AnchorSet, group: readonly Affine[], tolerance?: number): AnchorSymmetry[];
|
|
1173
1436
|
/**
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1437
|
+
* Translate the anchors `flats` by `(dx, dy)` the way `translateAnchorBy`
|
|
1438
|
+
* does, and every image of each under `syms` by the transformed delta, so a
|
|
1439
|
+
* path that was its own image under each of them still is. `syms` should be
|
|
1440
|
+
* the whole symmetry group less its identity, as `anchorSymmetries` gives it
|
|
1441
|
+
* for a group; with a part of one, the anchors it fixes are only partly held.
|
|
1442
|
+
* An anchor that is itself being dragged moves as dragged.
|
|
1177
1443
|
*/
|
|
1178
|
-
declare function
|
|
1444
|
+
declare function translateAnchorsSymmetric(set: AnchorSet, flats: readonly number[], dx: number, dy: number, syms: readonly AnchorSymmetry[]): boolean;
|
|
1179
1445
|
/**
|
|
1180
|
-
*
|
|
1181
|
-
*
|
|
1182
|
-
*
|
|
1446
|
+
* Move one handle to `(toX, toY)` and every image of it under `syms` to the
|
|
1447
|
+
* image of that point, so a path that was its own image under each of them
|
|
1448
|
+
* still is. A handle a transform maps onto itself keeps to where that
|
|
1449
|
+
* transform allows; one mapped onto its own anchor's opposite handle drives
|
|
1450
|
+
* that handle.
|
|
1183
1451
|
*
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1188
|
-
* exist.
|
|
1452
|
+
* Otherwise a smooth anchor keeps the opposite handle opposite, as
|
|
1453
|
+
* `moveHandleTo` does, unless `breakSmoothness`, and that handle's images
|
|
1454
|
+
* follow it. Every handle is written once, from its target, so no anchor
|
|
1455
|
+
* re-aims a handle another image already placed.
|
|
1189
1456
|
*/
|
|
1190
|
-
declare function
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
}): number;
|
|
1195
|
-
/** A point on a segment, as {@link segmentAt} reports it. */
|
|
1196
|
-
interface SegmentHit {
|
|
1457
|
+
declare function moveHandleSymmetric(set: AnchorSet, flat: number, side: Side, toX: number, toY: number, breakSmoothness: boolean, syms: readonly AnchorSymmetry[]): boolean;
|
|
1458
|
+
|
|
1459
|
+
/** Where on a segment a bend is held, as `segmentAt` reports it. */
|
|
1460
|
+
interface BendGrip {
|
|
1197
1461
|
sub: number;
|
|
1198
1462
|
segIdx: number;
|
|
1199
|
-
/**
|
|
1463
|
+
/** Curve parameter of the held point. */
|
|
1200
1464
|
t: number;
|
|
1201
|
-
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Bend one segment so the point held at `grip.t` moves by `(dx, dy)`, by
|
|
1468
|
+
* moving its two control handles: each by an amount weighted toward the end it
|
|
1469
|
+
* belongs to, together exactly enough to carry the held point along. A straight
|
|
1470
|
+
* segment becomes a curve. Where an end anchor was smooth, its other handle
|
|
1471
|
+
* turns to stay in line, keeping its length, so the neighboring segment bends
|
|
1472
|
+
* with it rather than kinking.
|
|
1473
|
+
*
|
|
1474
|
+
* Returns false when the segment doesn't exist.
|
|
1475
|
+
*/
|
|
1476
|
+
declare function bendSegment(set: AnchorSet, grip: BendGrip, dx: number, dy: number): boolean;
|
|
1477
|
+
|
|
1478
|
+
type Point = {
|
|
1202
1479
|
x: number;
|
|
1203
1480
|
y: number;
|
|
1204
|
-
}
|
|
1481
|
+
};
|
|
1205
1482
|
/**
|
|
1206
|
-
*
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
1209
|
-
* beside it.
|
|
1483
|
+
* Collapse the handles of the anchors at `flatIndexes` onto their anchors,
|
|
1484
|
+
* leaving sharp corners. A segment whose ends both lose their handles turns
|
|
1485
|
+
* straight.
|
|
1210
1486
|
*
|
|
1211
|
-
*
|
|
1212
|
-
*
|
|
1213
|
-
*
|
|
1214
|
-
* straight back to world space.
|
|
1487
|
+
* Returns false when every handle was already on its anchor. A segment that
|
|
1488
|
+
* stays curved because its far end keeps a handle still encodes a control at
|
|
1489
|
+
* this end, so it decodes as a handle sitting on the anchor: retracted.
|
|
1215
1490
|
*/
|
|
1216
|
-
declare function
|
|
1217
|
-
tolerancePx: number;
|
|
1218
|
-
scale?: Scale2;
|
|
1219
|
-
}): SegmentHit | null;
|
|
1491
|
+
declare function retractHandles(set: AnchorSet, flatIndexes: Iterable<number>): boolean;
|
|
1220
1492
|
/**
|
|
1221
|
-
*
|
|
1222
|
-
*
|
|
1223
|
-
*
|
|
1224
|
-
*
|
|
1493
|
+
* Give each anchor at `flatIndexes` two handles of equal length pointing in
|
|
1494
|
+
* opposite directions, so the curve passes through it smoothly. The shared
|
|
1495
|
+
* direction splits the difference between the two handles' own directions,
|
|
1496
|
+
* and the shared length is their mean; an anchor with one handle mirrors it.
|
|
1225
1497
|
*
|
|
1226
|
-
*
|
|
1227
|
-
*
|
|
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.
|
|
1498
|
+
* Skipped: an anchor with no handles, and the ends of an open subpath, which
|
|
1499
|
+
* have only one segment to shape. Returns false when nothing changed.
|
|
1230
1500
|
*/
|
|
1231
|
-
declare function
|
|
1501
|
+
declare function makeHandlesSymmetric(set: AnchorSet, flatIndexes: Iterable<number>): boolean;
|
|
1232
1502
|
/**
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
* already
|
|
1503
|
+
* Where a handle lands when dragged toward `to` but held to its current
|
|
1504
|
+
* direction from `anchor`: `to` projected onto that ray. The length never
|
|
1505
|
+
* goes below zero, so the handle can shrink onto the anchor but not flip
|
|
1506
|
+
* through it. A handle already sitting on its anchor has no direction to
|
|
1507
|
+
* keep and follows `to` freely.
|
|
1237
1508
|
*/
|
|
1238
|
-
declare function
|
|
1239
|
-
|
|
1240
|
-
|
|
1509
|
+
declare function handleAlongItsAngle(anchor: Point, handle: Point, to: Point): Point;
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Where each point of an edited path came from, so an array kept per point (a skin's
|
|
1513
|
+
* weights) can follow an edit that inserts, deletes, or reorders points.
|
|
1514
|
+
*/
|
|
1515
|
+
|
|
1516
|
+
/** The anchor model `pathToAnchors` produces; `AnchorSet` in `anchorEdits`. */
|
|
1517
|
+
interface Anchors {
|
|
1518
|
+
anchors: PenAnchor[][];
|
|
1519
|
+
closed: boolean[];
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Where a point of an edited path came from: point `a` of the path before the edit, or for a
|
|
1523
|
+
* point the edit made, a blend `t` of the way from point `a` to point `b`.
|
|
1524
|
+
*/
|
|
1525
|
+
interface PointOrigin {
|
|
1526
|
+
a: number;
|
|
1527
|
+
b: number;
|
|
1528
|
+
t: number;
|
|
1529
|
+
}
|
|
1530
|
+
/**
|
|
1531
|
+
* `editAnchorSet`, answering also where each point of the new path came from.
|
|
1532
|
+
*
|
|
1533
|
+
* An anchor the edit kept, moved or not, keeps its points. One the edit put exactly where an
|
|
1534
|
+
* anchor was takes that anchor's, each handle by where it is, so a subpath turned around
|
|
1535
|
+
* 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
|
|
1536
|
+
* of it in its subpath, by its distance to each, or a copy of the one it has; its origin is
|
|
1537
|
+
* `null` when its subpath has none.
|
|
1538
|
+
*/
|
|
1539
|
+
declare function editAnchorSetTracked(path: PolygonPath, edit: (set: Anchors) => boolean | void): {
|
|
1540
|
+
path: PolygonPath;
|
|
1541
|
+
origins: (PointOrigin | null)[];
|
|
1542
|
+
} | null;
|
|
1543
|
+
/**
|
|
1544
|
+
* An array kept per point of a path, carried through an edit: `origins` is what
|
|
1545
|
+
* {@link editAnchorSetTracked} answered. A point with no origin, and an origin past the
|
|
1546
|
+
* array's end, read 0.
|
|
1547
|
+
*/
|
|
1548
|
+
declare function remapPointValues(values: ArrayLike<number>, origins: readonly (PointOrigin | null)[]): number[];
|
|
1241
1549
|
|
|
1242
1550
|
/**
|
|
1243
1551
|
* Anchor lookup across several paths, measured on screen: which existing
|
|
@@ -1363,4 +1671,4 @@ declare function reviveTypedArrays(value: unknown): unknown;
|
|
|
1363
1671
|
/** `tagTypedArrays` as a `JSON.stringify` replacer. */
|
|
1364
1672
|
declare function typedArrayReplacer(_key: string, value: unknown): unknown;
|
|
1365
1673
|
|
|
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
|
|
1674
|
+
export { type Affine, type AnchorRect, type AnchorSet, type AnchorSymmetry, type AverageAxis, type BendGrip, type BezierEasing, type Box, type CubicCoords, type CurveNearest, DEFAULT_FLATTEN_TOLERANCE, EASINGS, EPS, type EasingFn, type EasingName, type EasingSpec, type GlMat3, type LineCoords, 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, type PenAnchor, type PlaceRectOptions, type PlacedRect, type Placement, type PlacementAlign, type PlacementSide, Point$1 as Point, type PointAlongPathOptions, type PointInPathOptions, type PointOrigin, type PolygonIntersectsPathOptions, PolygonPath, type PoseLinear, type PoseRotation, type QuadraticCoords, type Rect, RectPath, SPRING_PRESETS, type Scale2, type SegmentHit, type SimplifyPathOptions, type SpringPreset, type SpringPresetName, type StrokeHitTestOptions, type SubpathEnd, TYPED_ARRAY_CONSTRUCTORS, type TaggedTypedArray, type TypedArrayTag, type View, ZOOM_FLOOR, anchorAt, anchorCount, anchorSymmetries, anchorSymmetry, anchorsInRect, anchorsToPath, applyToPoint, approxEq, averageAnchorsAt, bendSegment, boundsOfCoords, boundsOfPath, boxContainsBox, boxContainsPoint, boxToBox, circlePath, clamp, clampRectWithin, closestOpenEnds, composePath, countPathAnchors, 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, editAnchorSetTracked, elevateQuadraticToCubic, ellipsePath, fillsOpenSubpaths, fitCubicThroughDeletion, fitCubicWithTangents, flatAnchorIndex, flattenCubic, flattenCubicWithArcLen, flattenQuadratic, flattenQuadraticWithArcLen, forEachSegment, handleAlongItsAngle, hasOpenSubpath, identity$1 as identity, insertAnchorOnSegment, invert$1 as invert, isAnchorSmooth, isStraightSegment, joinAnchorsAt, joinSubpathEnds, len2, linePath, linear, locateAnchor, makeHandlesSymmetric, mat3, meanScale, moveHandleSymmetric, moveHandleTo, multiply$1 as multiply, nearestOnCubic, nearestOnLine, nearestOnPath, nearestOnQuadratic, nearestPathAnchor, nearestSegmentT, normalizeZoom, openSubpathAt, pathCommandCoordCount, pathContainsPoint, pathContainsPolygon, pathContainsRect, pathDistanceToPoint, pathFromD, pathInPoseFrame, pathInWorld, pathIntersectsPolygon, pathIntersectsRect, pathSignedArea, pathThroughPoseFrame, pathToAnchors, placeRect, pointAlongPath, pointInPath, pointInPolygon, pointSegmentDist2, polygonContainsPath, polygonFromPoints, polygonIntersectsPath, polylineFromPoints, portControls, portCurvePoints, poseFrameInverseOf, poseFrameOf, poseLinearOf, poseRotationOf, pxExtent, quadraticEvalAt, rectMarkerPath, rectPath, rectToContour, regularPolygonPath, remapPointValues, resolveEasing, retractHandles, reverseAnchors, reversePath, reviveTypedArrays, rotate, rotateAboutPoint, rotatePathAround, roundRectPath, scale, scaleDelta, scalePathToBounds, schneiderFit, screenAngleOf, segmentAt, segmentEnds, segmentsCross, setDefaultOpenFill, sign, simplifyPath, splitByClosure, splitCubicAt, splitLineAt, splitQuadraticAt, splitSubpaths, squarePath, standoff, starPath, strokeHitTest, sub, subpathEndAt, tagTypedArray, tagTypedArrays, transformCoords, transformPath, translate, translateAnchorBy, translateAnchorsSymmetric, translatePath, typedArrayReplacer, unionBoundsPath, unionBox, untagTypedArray, viewToMat3, withinPxBox, withinPxRadius, worldEditToStorage };
|