@pixerate/editor 0.34.0 → 0.36.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.
@@ -19,6 +19,22 @@ interface Vec2 {
19
19
  * - `symmetric`: handles stay on one line with equal lengths.
20
20
  */
21
21
  type VectorAnchorType = 'corner' | 'smooth' | 'symmetric';
22
+ /**
23
+ * Live corner shapes: `round` (an arc tangent to both sides), `inverted` (an arc centred on
24
+ * the corner, cut into the shape) and `chamfer` (a straight bevel).
25
+ */
26
+ type VectorCornerKind = 'round' | 'inverted' | 'chamfer';
27
+ declare const VECTOR_CORNER_KINDS: readonly VectorCornerKind[];
28
+ /**
29
+ * A live corner: drawn rounded (or cut) at render, hit-test, bounds, boolean, outline and export
30
+ * time while the stored anchor stays sharp. The radius shrinks to fit when the sides are short:
31
+ * the cut reaches at most halfway along each side.
32
+ */
33
+ interface VectorCorner {
34
+ radius: number;
35
+ /** Default `round`. */
36
+ kind?: VectorCornerKind;
37
+ }
22
38
  /**
23
39
  * One anchor of a subpath. Handles are absolute document positions; a missing (or `null`)
24
40
  * handle sits on the anchor, so a segment with no handles at either end is a straight line.
@@ -32,6 +48,8 @@ interface VectorAnchor {
32
48
  out?: Vec2 | null;
33
49
  /** Default `corner`. */
34
50
  type?: VectorAnchorType;
51
+ /** Live corner radius. Ignored on smooth / symmetric anchors and the ends of open subpaths. */
52
+ corner?: VectorCorner | null;
35
53
  }
36
54
  /** A chain of anchors joined by line or cubic Bézier segments. */
37
55
  interface VectorSubpath {
@@ -44,6 +62,19 @@ type VectorPaint = string | CanvasGradientDef | null;
44
62
  type VectorFillRule = 'nonzero' | 'evenodd';
45
63
  type VectorLineCap = 'butt' | 'round' | 'square';
46
64
  type VectorLineJoin = 'miter' | 'round' | 'bevel';
65
+ /**
66
+ * Where the stroke sits on the outline: centred on it, inside the shape or outside it. Only
67
+ * shapes whose subpaths are all closed have an inside; open paths always stroke centred.
68
+ */
69
+ type VectorStrokeAlign = 'center' | 'inside' | 'outside';
70
+ /** Arrowheads and end markers for the ends of open paths. */
71
+ type VectorArrowhead = 'triangle' | 'arrow' | 'open' | 'circle' | 'square' | 'bar';
72
+ declare const VECTOR_ARROWHEADS: readonly VectorArrowhead[];
73
+ /**
74
+ * `extend`: the head's tip sits past the end point and the path keeps its length; `tip`: the
75
+ * tip sits on the end point and the stroke is shortened under the head.
76
+ */
77
+ type VectorArrowAlign = 'extend' | 'tip';
47
78
  /** Paint and stroke settings of a path. */
48
79
  interface VectorStyle {
49
80
  fill?: VectorPaint;
@@ -55,6 +86,18 @@ interface VectorStyle {
55
86
  strokeDasharray?: number[];
56
87
  /** Default `nonzero`. */
57
88
  fillRule?: VectorFillRule;
89
+ /** Default `center`. */
90
+ strokeAlign?: VectorStrokeAlign;
91
+ /** Head at the start of the first subpath when it is open, or none. */
92
+ startArrow?: VectorArrowhead | null;
93
+ /** Head at the end of the last subpath when it is open, or none. */
94
+ endArrow?: VectorArrowhead | null;
95
+ /** Default `extend`. */
96
+ arrowAlign?: VectorArrowAlign;
97
+ /** Size of the start head as a multiple of the stroke width (default 1: a head 4 widths long). */
98
+ startArrowScale?: number;
99
+ /** Size of the end head (default 1). */
100
+ endArrowScale?: number;
58
101
  }
59
102
  interface VectorNodeBase {
60
103
  id: string;
@@ -109,8 +152,12 @@ interface VectorAnchorRef {
109
152
  interface VectorHandleRef extends VectorAnchorRef {
110
153
  handle: 'in' | 'out';
111
154
  }
112
- /** Canvas tools. `direct` edits anchors and handles; the shape tools drag out a new shape. */
113
- type VectorTool = 'select' | 'direct' | 'pen' | 'pencil' | 'rect' | 'ellipse' | 'polygon' | 'star' | 'line';
155
+ /**
156
+ * Canvas tools. `direct` edits anchors, handles and segments; the shape tools drag out a new
157
+ * shape; `scissors` cuts a path at a point, `knife` cuts filled shapes along a freehand stroke
158
+ * and `eraser` rubs out the area under one.
159
+ */
160
+ type VectorTool = 'select' | 'direct' | 'pen' | 'pencil' | 'rect' | 'ellipse' | 'polygon' | 'star' | 'line' | 'scissors' | 'knife' | 'eraser';
114
161
  declare const VECTOR_TOOLS: readonly VectorTool[];
115
162
  /** Keyboard modifiers held during a pointer event. */
116
163
  interface VectorModifiers {
@@ -137,13 +184,23 @@ interface VectorDefaults extends VectorStyle {
137
184
  starPoints?: number;
138
185
  /** Inner radius of the star tool as a fraction of the outer one (default 0.5). */
139
186
  starInnerRatio?: number;
140
- /** Corner radius of the rectangle tool (default 0). */
187
+ /** Live corner radius the rectangle, polygon and star tools give their corners (default 0). */
141
188
  cornerRadius?: number;
189
+ /** Corner kind of those tools (default `round`). */
190
+ cornerKind?: VectorCornerKind;
142
191
  }
143
192
  type VectorAlign = 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom';
144
193
  type VectorDistribute = 'horizontal' | 'vertical';
145
194
  type VectorBooleanOperation = 'union' | 'subtract' | 'intersect' | 'exclude';
146
195
  type VectorArrange = 'front' | 'forward' | 'backward' | 'back';
196
+ /** Average: line anchors up on a horizontal line (same y), a vertical one (same x), or both. */
197
+ type VectorAverageAxis = 'horizontal' | 'vertical' | 'both';
198
+ /** Identifies one segment of a path node: from anchor `segment` to the next. */
199
+ interface VectorSegmentRef {
200
+ nodeId: string;
201
+ subpath: number;
202
+ segment: number;
203
+ }
147
204
 
148
205
  /**
149
206
  * Vector path geometry: anchors and handles to Bézier paths and back, bounds, hit-testing,
@@ -161,8 +218,13 @@ declare function hasPaint(paint: VectorPaint | undefined): boolean;
161
218
  declare function subpathSegmentCount(sp: VectorSubpath): number;
162
219
  /** Control points of segment `i` of a subpath: 2 for a line, 4 for a cubic. */
163
220
  declare function subpathSegment(sp: VectorSubpath, i: number): Point[];
164
- /** Absolute `M` / `L` / `C` / `Z` commands of subpaths. */
165
- declare function subpathsToBezier(subpaths: readonly VectorSubpath[]): BezierPath;
221
+ /**
222
+ * Absolute `M` / `L` / `C` / `Z` commands of subpaths, with live corners cut
223
+ * (`expandVectorCorners`). Pass `{ corners: false }` for the stored anchors as they are.
224
+ */
225
+ declare function subpathsToBezier(subpaths: readonly VectorSubpath[], options?: {
226
+ corners?: boolean;
227
+ }): BezierPath;
166
228
  /** The node's outline as Bézier commands. Groups have none. */
167
229
  declare function vectorNodeToBezier(node: VectorNode): BezierPath;
168
230
  /** SVG path data (`d`) of a path node or subpaths. */
@@ -204,10 +266,16 @@ declare function boundsIntersect(a: VectorBounds, b: VectorBounds): boolean;
204
266
  /** A box from two corners in any order. */
205
267
  declare function boundsFromPoints(a: Vec2, b: Vec2): VectorBounds;
206
268
  /**
207
- * Whether `p` hits a node: inside its fill (when it has one) or within half its stroke
208
- * width plus `tolerance` of its outline. A group is hit when any visible child is.
269
+ * Whether `p` hits a node: inside its fill (when it has one), on its stroke as drawn (its
270
+ * alignment and arrowheads respected) or within `tolerance` of either. A group is hit when
271
+ * any visible child is.
209
272
  */
210
273
  declare function hitTestVectorNode(node: VectorNode, p: Vec2, tolerance?: number, parent?: AffineMatrix): boolean;
274
+ /**
275
+ * The area a path node's stroke paints (alignment, caps, joins, dashes and arrowheads
276
+ * included), as a filled path in the space `parent` maps to. Empty without a stroke.
277
+ */
278
+ declare function vectorStrokeOutline(node: VectorPathNode, parent?: AffineMatrix): BezierPath;
211
279
  /** The topmost node hit by `p` (skipping hidden and locked ones), or null. */
212
280
  declare function hitTestVectorNodes(nodes: readonly VectorNode[], p: Vec2, tolerance?: number): VectorNode | null;
213
281
  interface NearestSegment {
@@ -290,8 +358,11 @@ declare function removeAnchors(sp: VectorSubpath, indices: readonly number[]): V
290
358
  declare function reverseSubpath(sp: VectorSubpath): VectorSubpath;
291
359
  /** Handle length that makes a cubic approximate a quarter circle. */
292
360
  declare const KAPPA = 0.5522847498307936;
293
- /** A rectangle, optionally with rounded corners (clamped to half the shorter side). */
294
- declare function rectSubpath(b: VectorBounds, radius?: number): VectorSubpath;
361
+ /**
362
+ * A rectangle. A `radius` becomes a live corner on each of its four anchors (drawn round, and
363
+ * clamped to half the shorter side, when the path is turned into curves).
364
+ */
365
+ declare function rectSubpath(b: VectorBounds, radius?: number, kind?: VectorCornerKind): VectorSubpath;
295
366
  /** An ellipse inscribed in a box (four smooth anchors). */
296
367
  declare function ellipseSubpath(b: VectorBounds): VectorSubpath;
297
368
  /** A regular polygon inscribed in a box, first vertex at the top. */
@@ -303,14 +374,6 @@ declare function lineSubpath(a: Vec2, b: Vec2): VectorSubpath;
303
374
  /** Rounds every coordinate to `precision` decimals (stable JSON and SVG output). */
304
375
  declare function roundSubpaths(subpaths: readonly VectorSubpath[], precision?: number): VectorSubpath[];
305
376
 
306
- /**
307
- * SVG export and import for vector documents. Import is DOM-free (works in Node and
308
- * workers): it reads `<svg>`, `<g>`, `<path>`, `<rect>`, `<circle>`, `<ellipse>`, `<line>`,
309
- * `<polyline>`, `<polygon>`, linear / radial gradients, presentation attributes, `style`
310
- * declarations and `transform`s. Text, images, clip paths, masks, filters and `<use>` are
311
- * skipped.
312
- */
313
-
314
377
  interface VectorSvgExportOptions {
315
378
  /** Decimal places for coordinates (default 3). */
316
379
  precision?: number;
@@ -322,12 +385,43 @@ interface VectorSvgExportOptions {
322
385
  idPrefix?: string;
323
386
  /** Indent nested elements (default true). */
324
387
  pretty?: boolean;
388
+ /**
389
+ * How strokes with an inside / outside alignment or arrowheads are written: `clip` (default)
390
+ * as a double-width stroke clipped or masked by the shape plus filled heads, `outline` as
391
+ * one filled path of the area the stroke paints.
392
+ */
393
+ strokes?: 'clip' | 'outline';
325
394
  }
395
+ /** `data-vector-corners`: the live corners of subpaths as `subpath.index:radius[:kind]`, space separated. */
396
+ declare function vectorCornersAttribute(subpaths: readonly VectorSubpath[], precision?: number): string;
397
+ /** Puts `data-vector-corners` back on subpaths (malformed entries are ignored). */
398
+ declare function applyVectorCornersAttribute(subpaths: VectorSubpath[], value: string): VectorSubpath[];
326
399
  /** A paint as an SVG attribute value plus the gradient def it needs (if any). */
327
400
  declare function vectorPaintToSvg(paint: VectorPaint | undefined, gradientId: string): {
328
401
  value: string;
329
402
  gradient: SvgGradientDef | null;
330
403
  };
404
+ /** The parts of an aligned or arrowed stroke (see `VectorRenderPath.strokeParts`). */
405
+ interface VectorRenderStrokeParts {
406
+ align: VectorStrokeAlign;
407
+ /** The line the stroke follows (shortened under `tip`-aligned heads). */
408
+ d: string;
409
+ /** Width to draw it with: twice the stroke width when aligned, half of it being clipped away. */
410
+ width: number;
411
+ /**
412
+ * Inside: a `<clipPath id>` holding the shape (`d`, `clip-rule` = `fillRule`). Outside: a
413
+ * `<mask id>` in user space over `bounds`, white with the shape (`d`) in black.
414
+ */
415
+ clip: {
416
+ kind: 'clip' | 'mask';
417
+ id: string;
418
+ bounds: VectorBounds;
419
+ } | null;
420
+ /** Arrowhead outlines (fill with `stroke`, nonzero). */
421
+ heads: string[];
422
+ }
423
+ /** The stroke parts of a path whose stroke needs more than one element, in its own space (undefined otherwise). */
424
+ declare function vectorStrokeSvgParts(node: VectorPathNode, clipId: string, precision?: number): VectorRenderStrokeParts | undefined;
331
425
  /** Serializes a vector document as a standalone SVG string. */
332
426
  declare function vectorDocumentToSvg(doc: VectorDocument, options?: VectorSvgExportOptions): string;
333
427
  interface VectorSvgImportOptions {
@@ -339,6 +433,64 @@ declare function parseSvgTransform(value: string | undefined): AffineMatrix;
339
433
  /** Parses SVG markup into a vector document. Throws when there is no `<svg>` element. */
340
434
  declare function parseVectorSvg(svg: string, options?: VectorSvgImportOptions): VectorDocument;
341
435
 
436
+ /**
437
+ * A corner a live radius can cut: an anchor that is not smooth, where two sides (straight or
438
+ * curved) meet at an angle. Positions are in the path node's own space.
439
+ */
440
+ interface VectorCornerInfo {
441
+ subpath: number;
442
+ index: number;
443
+ /** Where the sides meet. */
444
+ at: Vec2;
445
+ /** Unit directions the sides leave the corner in: towards the previous anchor, then the next one. */
446
+ u: Vec2;
447
+ v: Vec2;
448
+ /** The sides' lengths (along curves), in the same order. */
449
+ lu: number;
450
+ lv: number;
451
+ /** Cosine and sine of the angle between the sides. */
452
+ cos: number;
453
+ sin: number;
454
+ /** The angle between the sides in degrees (90 at a rectangle's corners). */
455
+ angle: number;
456
+ /** The largest radius that fits: its cut then reaches halfway along the shorter side. */
457
+ maxRadius: number;
458
+ }
459
+ /** The corner at anchor `index` of a subpath, or null when the anchor makes none (an open end, smooth, or straight on). */
460
+ declare function vectorCornerAt(sp: VectorSubpath, index: number, subpath?: number): VectorCornerInfo | null;
461
+ /** Every corner of the subpaths that a live radius can cut, in anchor order. */
462
+ declare function vectorCorners(subpaths: readonly VectorSubpath[]): VectorCornerInfo[];
463
+ /** How far from the corner a round cut of radius `r` meets each side: r / tan(angle / 2). */
464
+ declare const cornerSetback: (c: Pick<VectorCornerInfo, "cos" | "sin">, r: number) => number;
465
+ /** `radius` clamped to what the corner can draw (0 for negative or non-finite radii). */
466
+ declare const fitCornerRadius: (c: Pick<VectorCornerInfo, "maxRadius">, radius: number) => number;
467
+ /** The point `k` along the corner's bisector, measured in (u + v): a round cut of radius r is centred at k = r / sin. */
468
+ declare const cornerBisectorPoint: (c: VectorCornerInfo, k: number) => Vec2;
469
+ /** How much the radius grows when the round cut's centre moves by `d` (its part along the bisector, in radius units). */
470
+ declare const cornerRadiusChange: (c: VectorCornerInfo, d: Vec2) => number;
471
+ /** True when any anchor of the subpaths has a live corner radius. */
472
+ declare function hasVectorCorners(subpaths: readonly VectorSubpath[]): boolean;
473
+ /** One subpath with its corners cut; `sources[i]` is the stored anchor each output anchor comes from. */
474
+ declare function expandVectorCornersOf(sp: VectorSubpath, subpath?: number): {
475
+ subpath: VectorSubpath;
476
+ sources: number[];
477
+ };
478
+ /**
479
+ * The subpaths with every live corner cut (round, inverted or chamfer), each radius no larger
480
+ * than fits. A cut corner becomes two anchors and curved sides are trimmed to what the cuts
481
+ * leave of them. Subpaths without corners come back unchanged.
482
+ */
483
+ declare function expandVectorCorners(subpaths: readonly VectorSubpath[]): VectorSubpath[];
484
+ /**
485
+ * Sets (or clears, with radius 0) the live corner of anchors. `kind` keeps each anchor's own
486
+ * kind when omitted. Anchors that make no corner keep the radius too, so it applies once they do.
487
+ */
488
+ declare function setAnchorCorner(a: VectorAnchor, radius: number, kind?: VectorCornerKind): VectorAnchor;
489
+ /** The next corner kind (Alt-clicking a corner widget cycles round → inverted → chamfer). */
490
+ declare function nextVectorCornerKind(kind: VectorCornerKind | undefined): VectorCornerKind;
491
+ /** Every anchor of a subpath given the same live corner (radius 0 clears them). */
492
+ declare function setSubpathCorners(sp: VectorSubpath, radius: number, kind?: VectorCornerKind): VectorSubpath;
493
+
342
494
  /**
343
495
  * What a guide line shows: `align` lines run from a target through the snapped point or box,
344
496
  * `construction` lines from where a drawn point leaves, `spacing` marks equal gaps and `size`
@@ -555,6 +707,29 @@ interface VectorSnapOptions {
555
707
  }
556
708
  /** Snap options new editors start with. Hold Cmd / Ctrl (`mod`) during a drag to bypass snapping. */
557
709
  declare const DEFAULT_VECTOR_SNAP: Readonly<VectorSnapOptions>;
710
+ /** How path-editing gestures and the pencil, knife and eraser behave. */
711
+ interface VectorPathEditOptions {
712
+ /**
713
+ * Deleting anchors refits the neighbouring handles so the shape stays (default true). Off,
714
+ * the neighbours are joined with their handles as they are.
715
+ */
716
+ keepShapeOnDelete: boolean;
717
+ /**
718
+ * Dragging one anchor with the direct tool bends its two segments like the Curvature tool,
719
+ * keeping the segments beyond still (default false). Alt held during the drag flips it.
720
+ */
721
+ curvatureMoves: boolean;
722
+ /** Pencil lazy-mouse smoothing, 0–1: the pulled string is up to 100 screen pixels long (default 0, off). */
723
+ pencilSmoothing: number;
724
+ /** Pencil strokes that look like a line, rectangle, ellipse or polygon become that shape (default false). */
725
+ recognizeShapes: boolean;
726
+ /** Eraser diameter in document units (default 10). */
727
+ eraserSize: number;
728
+ }
729
+ /** Path-editing options new editors start with. */
730
+ declare const DEFAULT_VECTOR_PATH_EDIT: Readonly<VectorPathEditOptions>;
731
+ /** Longest pencil lazy-mouse string (at smoothing 1), in screen pixels. */
732
+ declare const VECTOR_PENCIL_STRING_PX = 100;
558
733
  interface VectorEditorOptions {
559
734
  document?: VectorDocument;
560
735
  /** History to record into, e.g. one shared with the rest of the app (default: a new one). */
@@ -565,6 +740,7 @@ interface VectorEditorOptions {
565
740
  defaults?: VectorDefaults;
566
741
  viewport?: Partial<VectorViewport>;
567
742
  snap?: Partial<VectorSnapOptions>;
743
+ editing?: Partial<VectorPathEditOptions>;
568
744
  /** Pick distance for anchors, handles and outlines in screen pixels (default 6). */
569
745
  hitRadius?: number;
570
746
  /** Id factory for new nodes. */
@@ -587,6 +763,7 @@ interface VectorHover {
587
763
  nodeId: string | null;
588
764
  anchor?: VectorAnchorRef;
589
765
  handle?: VectorHandleRef;
766
+ /** A segment under the pointer (direct tool and scissors); `point` is in document space. */
590
767
  segment?: {
591
768
  nodeId: string;
592
769
  subpath: number;
@@ -595,6 +772,8 @@ interface VectorHover {
595
772
  point: Vec2;
596
773
  };
597
774
  transformHandle?: VectorTransformHandle;
775
+ /** A live corner widget (direct tool): the corner's anchor. */
776
+ corner?: VectorAnchorRef;
598
777
  }
599
778
  /** The drag in progress, for overlays and status text. */
600
779
  type VectorInteraction = {
@@ -619,11 +798,24 @@ type VectorInteraction = {
619
798
  } | {
620
799
  type: 'handle';
621
800
  ref: VectorHandleRef;
801
+ } | {
802
+ type: 'bend';
803
+ ref: VectorSegmentRef;
804
+ } | {
805
+ type: 'corner';
806
+ ref: VectorAnchorRef;
807
+ radius: number;
622
808
  } | {
623
809
  type: 'draw';
624
810
  tool: VectorTool;
625
811
  nodeId: string;
626
812
  bounds: VectorBounds;
813
+ string?: [Vec2, Vec2];
814
+ } | {
815
+ type: 'stroke';
816
+ tool: 'knife' | 'eraser';
817
+ points: Vec2[];
818
+ width: number;
627
819
  } | {
628
820
  type: 'pen';
629
821
  nodeId: string;
@@ -645,8 +837,9 @@ interface VectorEditorState {
645
837
  /** The selection box, rotated with the selection when its nodes share a rotation. */
646
838
  selectionFrame: VectorSelectionFrame | null;
647
839
  viewport: VectorViewport;
648
- defaults: Readonly<Required<Pick<VectorDefaults, 'polygonSides' | 'starPoints' | 'starInnerRatio' | 'cornerRadius'>> & VectorStyle>;
840
+ defaults: Readonly<Required<Pick<VectorDefaults, 'polygonSides' | 'starPoints' | 'starInnerRatio' | 'cornerRadius'>> & Pick<VectorDefaults, 'cornerKind'> & VectorStyle>;
649
841
  snap: VectorSnapOptions;
842
+ editing: VectorPathEditOptions;
650
843
  /** Smart guides of the snap in progress, in document units (null when there are none). */
651
844
  guides: VectorSnapGuides | null;
652
845
  hitRadius: number;
@@ -691,12 +884,16 @@ interface VectorShapeInput extends VectorStyle {
691
884
  y: number;
692
885
  width: number;
693
886
  height: number;
887
+ /** Live corner radius of a rectangle, polygon or star (default: the editor default). */
694
888
  cornerRadius?: number;
889
+ cornerKind?: VectorCornerKind;
695
890
  sides?: number;
696
891
  points?: number;
697
892
  innerRatio?: number;
698
893
  name?: string;
699
894
  }
895
+ /** Every field of `VectorStyle`, in the order style panels and commands list them. */
896
+ declare const VECTOR_STYLE_KEYS: readonly ["fill", "stroke", "strokeWidth", "strokeLinecap", "strokeLinejoin", "strokeDasharray", "fillRule", "strokeAlign", "startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"];
700
897
  /** Clipboard payload type written by `copy()`. */
701
898
  declare const VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
702
899
  /** Screen distance of the rotation handle above the selection box. */
@@ -710,6 +907,29 @@ declare function getVisibleAnchors(state: Pick<VectorEditorState, 'document' | '
710
907
  * (direct tool), or of the last anchor of the pen path.
711
908
  */
712
909
  declare function getVisibleHandles(state: Pick<VectorEditorState, 'document' | 'selection' | 'editingId' | 'tool' | 'pen' | 'selectedAnchors'>): VectorHandleRef[];
910
+ /** Live corner widgets sit at least this far inside their corner (screen px along each side of a right angle). */
911
+ declare const VECTOR_CORNER_WIDGET_INSET = 10;
912
+ /** A live corner widget of the direct tool: drag it to round the corners, Alt-click to change their kind. */
913
+ interface VectorCornerWidget {
914
+ ref: VectorAnchorRef;
915
+ /** Document position of the widget. */
916
+ x: number;
917
+ y: number;
918
+ /** The radius stored on the anchor and the radius drawn (no larger than fits). */
919
+ radius: number;
920
+ drawnRadius: number;
921
+ kind: VectorCornerKind;
922
+ /** The corner in its node's own space. */
923
+ corner: VectorCornerInfo;
924
+ }
925
+ /**
926
+ * The live corner widgets the direct tool shows: on each editable path, at its selected corner
927
+ * anchors (or at every corner when none of its anchors is selected). Corners whose sides are
928
+ * shorter than three insets on screen hide theirs. Dragging one sets the radius of all of them.
929
+ * Ported from ArtCraft vectorcraft `crates/tools/src/corners.rs` (MIT OR Apache-2.0, see
930
+ * THIRD_PARTY_NOTICES.md), as is the `corner` drag below.
931
+ */
932
+ declare function getVectorCornerWidgets(state: Pick<VectorEditorState, 'document' | 'selection' | 'editingId' | 'tool' | 'pen' | 'selectedAnchors' | 'viewport'>): VectorCornerWidget[];
713
933
  /** Looks up an anchor (positions in its node's own space). */
714
934
  declare function getAnchor(doc: VectorDocument, ref: VectorAnchorRef): VectorAnchor | undefined;
715
935
  /** An anchor with its position and handles in document space (its node's transforms applied). */
@@ -784,6 +1004,7 @@ declare class VectorEditorController {
784
1004
  private defaults;
785
1005
  private viewport;
786
1006
  private snap;
1007
+ private editing;
787
1008
  private hitRadius;
788
1009
  private hover;
789
1010
  private pointer;
@@ -855,6 +1076,8 @@ declare class VectorEditorController {
855
1076
  setTool(tool: VectorTool): void;
856
1077
  setDefaults(defaults: Partial<VectorDefaults>): void;
857
1078
  setSnap(snap: Partial<VectorSnapOptions>): void;
1079
+ /** Changes how deleting, dragging anchors, the pencil and the eraser behave. */
1080
+ setEditing(editing: Partial<VectorPathEditOptions>): void;
858
1081
  /** Turns object snapping on or off (the toolbar's snapping toggle). */
859
1082
  toggleSnapping(on?: boolean): void;
860
1083
  private gridValue;
@@ -908,6 +1131,31 @@ declare class VectorEditorController {
908
1131
  }, ids?: readonly string[], options?: {
909
1132
  label?: string;
910
1133
  }): VectorActionResult;
1134
+ /**
1135
+ * Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
1136
+ * anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
1137
+ * corners sharp again. Corners shrink to fit when drawn, so any radius is accepted.
1138
+ */
1139
+ setCornerRadius(radius: number, options?: {
1140
+ kind?: VectorCornerKind;
1141
+ refs?: readonly VectorAnchorRef[];
1142
+ ids?: readonly string[];
1143
+ }): VectorActionResult;
1144
+ /** Sets the live corner kind of the same anchors as `setCornerRadius`, keeping their radii. */
1145
+ setCornerKind(kind: VectorCornerKind, options?: {
1146
+ refs?: readonly VectorAnchorRef[];
1147
+ ids?: readonly string[];
1148
+ }): VectorActionResult;
1149
+ /** Anchors a corner change applies to (see `setCornerRadius`). */
1150
+ private cornerTargets;
1151
+ /**
1152
+ * Outline Stroke: replaces each selected (or given) path's stroke with a filled path of the
1153
+ * area it paints (alignment, joins, caps, dashes and arrowheads included), placed above a copy
1154
+ * of the path that keeps the fill (left out when there is none). Returns the new paths' ids.
1155
+ */
1156
+ outlineStroke(options?: {
1157
+ ids?: readonly string[];
1158
+ }): string[];
911
1159
  rename(id: string, name: string): VectorActionResult;
912
1160
  setVisible(id: string, visible: boolean): VectorActionResult;
913
1161
  setLocked(id: string, locked: boolean): VectorActionResult;
@@ -1013,8 +1261,22 @@ declare class VectorEditorController {
1013
1261
  private updateAnchors;
1014
1262
  /** Moves anchors by a document-space offset (converted into each node's own space). */
1015
1263
  private movedAnchors;
1016
- /** Moves one anchor to a document position (handles follow). */
1017
- moveAnchor(ref: VectorAnchorRef, to: Vec2): VectorActionResult;
1264
+ /**
1265
+ * Moves one anchor to a document position. Its handles follow; with `curvature` (default:
1266
+ * the `curvatureMoves` setting) its two segments bend instead, as the Curvature tool does.
1267
+ */
1268
+ moveAnchor(ref: VectorAnchorRef, to: Vec2, options?: {
1269
+ curvature?: boolean;
1270
+ }): VectorActionResult;
1271
+ /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
1272
+ private curvatureMoved;
1273
+ /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
1274
+ private bent;
1275
+ /**
1276
+ * Bends a segment so the curve passes through document point `to` at parameter `t`
1277
+ * (default 0.5, clamped to 0.05–0.95); see `bendSegment` in `pathEdit`.
1278
+ */
1279
+ bendSegment(ref: VectorSegmentRef, to: Vec2, t?: number): VectorActionResult;
1018
1280
  /** Moves one handle to a document position (see `moveAnchorHandle` for how the opposite handle follows). */
1019
1281
  moveHandle(ref: VectorHandleRef, to: Vec2, options?: {
1020
1282
  breakHandles?: boolean;
@@ -1023,12 +1285,95 @@ declare class VectorEditorController {
1023
1285
  convertAnchors(type: VectorAnchorType, refs?: readonly VectorAnchorRef[]): VectorActionResult;
1024
1286
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
1025
1287
  insertAnchorAt(nodeId: string, point: Vec2): VectorAnchorRef | null;
1026
- /** Deletes the selected anchors; empty subpaths and paths go too. */
1027
- deleteAnchors(refs?: readonly VectorAnchorRef[]): VectorActionResult;
1288
+ /**
1289
+ * Deletes the selected (or given) anchors; subpaths left with one anchor and empty paths go
1290
+ * too. With `keepShape` (default: the `keepShapeOnDelete` setting, on) the neighbours'
1291
+ * handles are refitted so the path keeps its shape; off, the neighbours are joined as they are.
1292
+ */
1293
+ deleteAnchors(refs?: readonly VectorAnchorRef[], options?: {
1294
+ keepShape?: boolean;
1295
+ }): VectorActionResult;
1028
1296
  /** Opens or closes the subpaths that contain selected anchors (or all subpaths of `nodeId`). */
1029
1297
  setClosed(closed: boolean, nodeId?: string): VectorActionResult;
1030
1298
  /** Reverses the direction of every subpath of the selected paths. */
1031
1299
  reversePaths(ids?: readonly string[]): VectorActionResult;
1300
+ /** Unlocked path nodes in the given roots (default: the paths being edited, else the selection). */
1301
+ private pathTargets;
1302
+ /** Subpaths of node `from` mapped into node `to`'s own space. */
1303
+ private subpathsIn;
1304
+ /**
1305
+ * Replaces a path with pieces: each entry becomes one path (the first keeps the node's id and
1306
+ * place, the others go just above it with the same style). No pieces removes the path.
1307
+ * Returns the ids of the paths now standing in for it.
1308
+ */
1309
+ private replaceWithPieces;
1310
+ /** Clears anchor selections, editing and pen state that no longer point at anything. */
1311
+ private dropStaleRefs;
1312
+ /**
1313
+ * Join: the open subpaths of the selected (or given) paths become one open path, joined
1314
+ * nearest endpoints first (ends within `tolerance` document units merge into one anchor,
1315
+ * others get a straight segment); a single open subpath is closed. The result replaces the
1316
+ * lowest path, in its space and style. Default tolerance: the pick distance.
1317
+ */
1318
+ joinPaths(options?: {
1319
+ ids?: readonly string[];
1320
+ tolerance?: number;
1321
+ }): VectorActionResult;
1322
+ /** Average: moves the selected (or given) anchors onto their average position along `axis`, in document space. */
1323
+ averageAnchors(axis?: VectorAverageAxis, refs?: readonly VectorAnchorRef[]): VectorActionResult;
1324
+ /** Applies `fn` to every subpath of the target paths as one undo step. */
1325
+ private editSubpaths;
1326
+ /**
1327
+ * Remove Redundant Points: drops anchors of the selected (or given) paths that can go
1328
+ * without moving the outline more than `tolerance` document units (default 0.5).
1329
+ */
1330
+ removeRedundantPoints(options?: {
1331
+ ids?: readonly string[];
1332
+ tolerance?: number;
1333
+ }): VectorActionResult;
1334
+ /**
1335
+ * Smooth: pulls handles towards smooth tangents by `amount` (0–1, default 1). With the direct
1336
+ * tool and points selected, only those points change.
1337
+ */
1338
+ smoothPaths(options?: {
1339
+ ids?: readonly string[];
1340
+ amount?: number;
1341
+ refs?: readonly VectorAnchorRef[];
1342
+ }): VectorActionResult;
1343
+ /** Splits a cut subpath into pieces: separate paths for a one-subpath node, subpaths in place for a compound one. */
1344
+ private applyCuts;
1345
+ /**
1346
+ * Scissors: cuts path `nodeId` at the outline point nearest document point `point` (at an
1347
+ * anchor when within the pick distance of one). A closed subpath opens there; an open one
1348
+ * splits in two (a one-subpath path becomes two paths). Returns the ids of the paths.
1349
+ */
1350
+ cutPathAt(nodeId: string, point: Vec2): string[] | null;
1351
+ /** Scissors on the selected (or given) anchors: each cuts its subpath there (see `cutPathAt`). */
1352
+ cutAtAnchors(refs?: readonly VectorAnchorRef[]): VectorActionResult;
1353
+ /** Visible, unlocked paths a stroke over `points` can reach: in `ids`, the selection, or (with neither) any whose bounds it crosses. */
1354
+ private strokeTargets;
1355
+ /** A stroke's points in node `id`'s own space. */
1356
+ private localPoints;
1357
+ /**
1358
+ * Knife: cuts closed paths along the polyline `points` (document space) wherever it crosses
1359
+ * them from edge to edge, with no gap between the pieces. It cuts the given paths, the
1360
+ * selection, or (with neither) every path it crosses. Returns the ids of the pieces (empty
1361
+ * when nothing was cut).
1362
+ */
1363
+ knife(points: readonly Vec2[], options?: {
1364
+ ids?: readonly string[];
1365
+ }): string[];
1366
+ /**
1367
+ * Eraser: rubs out a round stroke of diameter `size` (default: the `eraserSize` setting)
1368
+ * along `points` (document space). Filled closed paths lose that area (pieces that come
1369
+ * apart become separate paths); outlines are cut where they pass under it. It erases from
1370
+ * the given paths, the selection, or (with neither) every path it reaches. Returns the ids
1371
+ * of the paths it changed or created.
1372
+ */
1373
+ erase(points: readonly Vec2[], options?: {
1374
+ ids?: readonly string[];
1375
+ size?: number;
1376
+ }): string[];
1032
1377
  /** Ends the pen path being drawn (a lone anchor is removed). */
1033
1378
  finishPath(): void;
1034
1379
  /** Copies the selection. Returns JSON text for the system clipboard (or null). */
@@ -1050,12 +1395,16 @@ declare class VectorEditorController {
1050
1395
  private tolerance;
1051
1396
  private hitTransformHandle;
1052
1397
  private hitHandle;
1398
+ private cornerView;
1399
+ private hitCornerWidget;
1053
1400
  private hitAnchor;
1054
1401
  /** Deepest unlocked visible path under `p`. */
1055
1402
  private hitPath;
1056
1403
  /** Endpoints of open subpaths of unlocked visible paths (pen tool resumes from them). */
1057
1404
  private openEndpoints;
1058
1405
  private computeHover;
1406
+ /** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
1407
+ private nearestOutline;
1059
1408
  private sameHover;
1060
1409
  /** Primary button down on the canvas at `screen` (pixels relative to the canvas). */
1061
1410
  pointerDown(screen: Vec2, input?: VectorPointerInput): void;
@@ -1109,6 +1458,13 @@ interface VectorRenderPath {
1109
1458
  strokeDasharray?: string;
1110
1459
  fillRule: 'nonzero' | 'evenodd';
1111
1460
  opacity: number;
1461
+ /**
1462
+ * Set when the stroke takes more than one element: an inside / outside alignment or
1463
+ * arrowheads. Draw `d` with the fill only, then `stroke.d` with the stroke (at
1464
+ * `stroke.width`, clipped or masked as `stroke.clip` says), then each head filled with
1465
+ * `stroke`; group them under `id`, `transform` and `opacity`.
1466
+ */
1467
+ strokeParts?: VectorRenderStrokeParts;
1112
1468
  }
1113
1469
  interface VectorRenderGroup {
1114
1470
  kind: 'group';
@@ -1150,6 +1506,17 @@ interface VectorRenderHandle {
1150
1506
  anchorY: number;
1151
1507
  hovered: boolean;
1152
1508
  }
1509
+ /** A live corner widget in screen pixels. */
1510
+ interface VectorRenderCornerWidget {
1511
+ ref: VectorAnchorRef;
1512
+ x: number;
1513
+ y: number;
1514
+ radius: number;
1515
+ kind: VectorCornerKind;
1516
+ hovered: boolean;
1517
+ /** Its corners are being dragged. */
1518
+ active: boolean;
1519
+ }
1153
1520
  /** A smart guide line in screen pixels. */
1154
1521
  interface VectorRenderGuideLine {
1155
1522
  kind: VectorGuideKind;
@@ -1196,6 +1563,29 @@ interface VectorOverlayRenderData {
1196
1563
  lines: VectorRenderGuideLine[];
1197
1564
  labels: VectorRenderGuideLabel[];
1198
1565
  };
1566
+ /**
1567
+ * The knife or eraser stroke being drawn, screen space: `d` is an open polyline; draw the
1568
+ * eraser's with a round-capped stroke `width` pixels wide (0 for the knife: a thin line).
1569
+ */
1570
+ toolStroke: {
1571
+ tool: 'knife' | 'eraser';
1572
+ d: string;
1573
+ width: number;
1574
+ } | null;
1575
+ /** The pencil's lazy-mouse string from the pen to the pointer, screen space. */
1576
+ pencilString: {
1577
+ x1: number;
1578
+ y1: number;
1579
+ x2: number;
1580
+ y2: number;
1581
+ } | null;
1582
+ /** Where the scissors would cut (screen space). */
1583
+ cutPoint: Vec2 | null;
1584
+ /**
1585
+ * Live corner widgets of the direct tool (screen space): drag one to round its corners,
1586
+ * Alt-click to cycle round / inverted / chamfer, click to pick its corner alone.
1587
+ */
1588
+ cornerWidgets: VectorRenderCornerWidget[];
1199
1589
  /** A short status, e.g. "W 120 × H 80" or "45°". */
1200
1590
  status: string | null;
1201
1591
  }
@@ -1215,9 +1605,13 @@ declare const VECTOR_OVERLAY_THEME: {
1215
1605
  readonly guide: "#ff3dfc";
1216
1606
  readonly handleFill: "#ffffff";
1217
1607
  readonly marqueeFill: "rgba(14, 165, 233, 0.08)";
1608
+ /** The eraser's swath while it is dragged. */
1609
+ readonly eraserFill: "rgba(239, 68, 68, 0.25)";
1218
1610
  readonly transformHandleSize: 8;
1219
1611
  readonly anchorSize: 7;
1220
1612
  readonly handleRadius: 3.5;
1613
+ /** Radius of a live corner widget (a ring with a dot). */
1614
+ readonly cornerWidgetRadius: 4;
1221
1615
  };
1222
1616
  interface ModifierEventLike {
1223
1617
  shiftKey?: boolean;
@@ -1257,18 +1651,64 @@ interface VectorSelectionStyle {
1257
1651
  stroke: VectorPaint;
1258
1652
  strokeWidth: number;
1259
1653
  opacity: number;
1654
+ strokeAlign: VectorStrokeAlign;
1655
+ startArrow: VectorArrowhead | null;
1656
+ endArrow: VectorArrowhead | null;
1657
+ arrowAlign: VectorArrowAlign;
1658
+ startArrowScale: number;
1659
+ endArrowScale: number;
1660
+ /**
1661
+ * Live corners of the selected anchors (direct tool) or of every corner of the selected
1662
+ * paths: the first one's radius and kind, and how many corners there are (0: none to round).
1663
+ */
1664
+ cornerRadius: number;
1665
+ cornerKind: VectorCornerKind;
1666
+ corners: number;
1667
+ /** Some selected path is open, so arrowheads apply. */
1668
+ hasOpenPath: boolean;
1260
1669
  mixed: {
1261
1670
  fill: boolean;
1262
1671
  stroke: boolean;
1263
1672
  strokeWidth: boolean;
1264
1673
  opacity: boolean;
1674
+ strokeAlign: boolean;
1675
+ startArrow: boolean;
1676
+ endArrow: boolean;
1677
+ arrowAlign: boolean;
1678
+ startArrowScale: boolean;
1679
+ endArrowScale: boolean;
1680
+ cornerRadius: boolean;
1681
+ cornerKind: boolean;
1265
1682
  };
1266
1683
  /** Ids of the paths the style applies to. */
1267
1684
  pathIds: string[];
1268
1685
  }
1269
- declare function getVectorSelectionStyle(state: Pick<VectorEditorState, 'document' | 'selection'>): VectorSelectionStyle | null;
1686
+ declare function getVectorSelectionStyle(state: Pick<VectorEditorState, 'document' | 'selection'> & Partial<Pick<VectorEditorState, 'selectedAnchors' | 'tool'>>): VectorSelectionStyle | null;
1687
+ /** Labels of the stroke alignments, corner kinds and arrowheads, for style panels. */
1688
+ declare const VECTOR_STROKE_ALIGN_LABELS: Readonly<Record<VectorStrokeAlign, string>>;
1689
+ declare const VECTOR_CORNER_KIND_LABELS: Readonly<Record<VectorCornerKind, string>>;
1690
+ declare const VECTOR_ARROWHEAD_LABELS: Readonly<Record<VectorArrowhead, string>>;
1270
1691
  /** A paint as `#rrggbb` for `<input type="color">`, or null for none, gradients and other CSS colours. */
1271
1692
  declare function vectorPaintToHex(paint: VectorPaint | undefined): string | null;
1693
+ /** A path-editing button of the bundled toolbars. */
1694
+ interface VectorPathAction {
1695
+ id: 'join' | 'average' | 'cut' | 'clean' | 'smooth' | 'outline';
1696
+ label: string;
1697
+ title: string;
1698
+ /** Shortcut after Cmd / Ctrl, if any. */
1699
+ shortcut?: string;
1700
+ /** Whether it can run with `selected` nodes and `points` selected anchors (direct tool). */
1701
+ enabled: (selected: number, points: number) => boolean;
1702
+ run: (controller: VectorEditorController) => unknown;
1703
+ }
1704
+ /** Join, Average, Cut at Points, Remove Redundant Points, Smooth and Outline Stroke, as the toolbars show them. */
1705
+ declare const VECTOR_PATH_ACTIONS: readonly VectorPathAction[];
1706
+ /** The on / off path-editing settings the toolbars toggle. */
1707
+ declare const VECTOR_EDIT_TOGGLES: readonly {
1708
+ key: 'keepShapeOnDelete' | 'curvatureMoves' | 'recognizeShapes';
1709
+ label: string;
1710
+ title: string;
1711
+ }[];
1272
1712
 
1273
1713
  interface VectorCommandOptions {
1274
1714
  /** Id prefix (default `vector.`); use one per editor when several are registered. */
@@ -1311,4 +1751,236 @@ interface VectorNodeListing {
1311
1751
  */
1312
1752
  declare function createVectorCommands(controller: VectorEditorController, options?: VectorCommandOptions): CommandSpec[];
1313
1753
 
1314
- export { type AddVectorNodeOptions, AffineMatrix, DEFAULT_VECTOR_SNAP, IDENTITY_MATRIX, KAPPA, type NearestSegment, SvgGradientDef, VECTOR_CLIPBOARD_TYPE, VECTOR_CONSTRUCTION_ANGLES, VECTOR_OVERLAY_THEME, VECTOR_ROTATE_HANDLE_OFFSET, VECTOR_TOOLS, VECTOR_TOOL_GLYPHS, VECTOR_TOOL_SHORTCUTS, VECTOR_TRANSFORM_HANDLES, type Vec2, type VectorActionResult, type VectorAlign, type VectorAnchor, type VectorAnchorRef, type VectorAnchorType, type VectorArrange, type VectorBooleanOperation, type VectorBounds, type VectorCommandOptions, type VectorDefaults, type VectorDistribute, type VectorDocument, VectorEditorController, type VectorEditorOptions, type VectorEditorState, type VectorFillRule, type VectorGroupNode, type VectorGuideKind, type VectorGuideLabel, type VectorGuideLine, type VectorHandleRef, type VectorHover, type VectorInteraction, type VectorKeyInput, type VectorLineCap, type VectorLineJoin, type VectorMatrix, type VectorModifiers, type VectorNode, type VectorNodeListing, type VectorOverlayRenderData, type VectorPaint, type VectorPathNode, type VectorPointSnap, type VectorPointerInput, type VectorRectSnap, type VectorRenderAnchor, type VectorRenderGroup, type VectorRenderGuideLabel, type VectorRenderGuideLine, type VectorRenderHandle, type VectorRenderNode, type VectorRenderPath, type VectorScaleSnap, type VectorSceneRenderData, type VectorSelectionBoxGeometry, type VectorSelectionFrame, type VectorSelectionStyle, type VectorShapeInput, type VectorSnapGuides, type VectorSnapKind, type VectorSnapLeave, type VectorSnapOptions, VectorSnapTargets, type VectorStyle, type VectorSubpath, type VectorSvgExportOptions, type VectorSvgImportOptions, type VectorTool, type VectorTransformHandle, type VectorViewport, applyMatrix, applyMatrixLinear, bezierToSubpaths, boundsFromPoints, boundsIntersect, cloneAnchor, cloneSubpath, cloneVectorDocument, cloneVectorNode, collectPathNodes, constrainToLeave, convertAnchor, createVectorCommands, decomposeRigid, ellipseSubpath, findVectorNode, getAnchor, getAnchorWorld, getEditablePathIds, getTransformHandlePositions, getVectorOverlayRenderData, getVectorSceneRenderData, getVectorSelectionBoxGeometry, getVectorSelectionFrame, getVectorSelectionStyle, getVisibleAnchors, getVisibleHandles, hasPaint, hitTestVectorNode, hitTestVectorNodes, inferAnchorType, insertAnchor, invertMatrix, isIdentityMatrix, isVectorTextTarget, lineSubpath, locateVectorNode, matrixRotation, moveAnchorHandle, multiplyMatrices, nearestSegment, nodeMatrix, parseSvgTransform, parseVectorSvg, polygonSubpath, rectSubpath, removeAnchors, reverseSubpath, rotateMatrix, roundSubpaths, scaleFor, scaleMatrix, starSubpath, subpathSegment, subpathSegmentCount, subpathsToBezier, toVectorKeyInput, toVectorModifiers, toVectorPointerInput, transformVectorNode, translateAnchor, translateMatrix, unionBounds, vectorDocumentToSvg, vectorNodeBounds, vectorNodeRotation, vectorNodeToBezier, vectorPaintToHex, vectorPaintToSvg, vectorParentMatrix, vectorPathData, vectorPathWorldBezier, vectorResizeCursor, vectorToolLabel, vectorWorldMatrix, walkVectorNodes };
1754
+ /**
1755
+ * Removes anchor `index` without opening the path: its neighbours keep their handle directions
1756
+ * and their facing handles are refitted so one cubic follows the two segments that met there
1757
+ * as closely as possible (least maximum error over where the new curve passes the removed
1758
+ * point). Two straight segments become one; removing an end of an open subpath drops its
1759
+ * segment. Returns the subpath unchanged (a copy) when `index` is out of range.
1760
+ */
1761
+ declare function removeAnchorKeepingShape(sp: VectorSubpath, index: number): VectorSubpath;
1762
+ /** Removes several anchors of a subpath, keeping the shape (see `removeAnchorKeepingShape`). */
1763
+ declare function removeAnchorsKeepingShape(sp: VectorSubpath, indices: readonly number[]): VectorSubpath;
1764
+ /**
1765
+ * Bends segment `segment` so the curve passes through `p` at parameter `t` (clamped to
1766
+ * 0.05–0.95): its handles go to the thirds of the chord plus one shared offset, so
1767
+ * `B(t) = L(t) + 3t(1 − t)·offset` exactly. The end anchors keep their types (a smooth or
1768
+ * symmetric end turns its other handle to stay in line).
1769
+ */
1770
+ declare function bendSegment(sp: VectorSubpath, segment: number, t: number, p: Vec2): VectorSubpath;
1771
+ /** The point at parameter `t` of segment `segment` (for tests and previews). */
1772
+ declare function subpathPointAt(sp: VectorSubpath, segment: number, t: number): Vec2;
1773
+ /**
1774
+ * Moves anchor `index` to `q` the way the Curvature tool drags a point: only the two segments
1775
+ * at it change. A smooth or symmetric anchor turns its handles along the line through its
1776
+ * neighbours; the neighbours' handles on those segments, and a corner's own handles, keep
1777
+ * their directions while their lengths scale with their segment's chord, so the segments
1778
+ * beyond don't move and a corner without handles keeps none.
1779
+ */
1780
+ declare function curvatureMoveAnchor(sp: VectorSubpath, index: number, q: Vec2): VectorSubpath;
1781
+ /**
1782
+ * Cuts a subpath at the anchors `at`: each becomes the end of one open piece and the start of
1783
+ * the next, its handle off each piece dropped (the shape doesn't change). A closed subpath
1784
+ * opens at its first cut, so one cut leaves one piece whose ends coincide. The ends of an open
1785
+ * subpath and indices out of range don't cut; with no cut left the subpath comes back as it is.
1786
+ */
1787
+ declare function cutSubpathAt(sp: VectorSubpath, at: readonly number[]): VectorSubpath[];
1788
+ /**
1789
+ * Cuts a subpath at parameter `t` of segment `segment` (Scissors on a segment): an anchor is
1790
+ * inserted there without changing the shape, then cut. Within `endTolerance` of the segment's
1791
+ * ends (in parameter) the existing anchor is cut instead.
1792
+ */
1793
+ declare function splitSubpathAt(sp: VectorSubpath, segment: number, t: number, endTolerance?: number): VectorSubpath[];
1794
+ /**
1795
+ * Join: open subpaths are joined nearest-endpoints-first into one open subpath (endpoints
1796
+ * within `tolerance` merge into one anchor, otherwise a straight segment connects them). A
1797
+ * single open subpath is closed instead (its ends merged when they meet). Closed subpaths and
1798
+ * lone anchors pass through unchanged, before the joined one.
1799
+ */
1800
+ declare function joinSubpaths(subpaths: readonly VectorSubpath[], tolerance: number): VectorSubpath[];
1801
+ /**
1802
+ * Smooth: pulls handles towards Catmull-Rom-like tangents (a third of the way to each
1803
+ * neighbour along the line through both). `amount` 0–1 (0 = unchanged, 1 = fully smooth).
1804
+ * Only the anchors in `only` change when it is given; endpoints of open subpaths keep their
1805
+ * handles. Subpaths of fewer than three anchors are unchanged.
1806
+ */
1807
+ declare function smoothSubpath(sp: VectorSubpath, amount?: number, only?: ReadonlySet<number>): VectorSubpath;
1808
+ /**
1809
+ * Remove Redundant Points: drops anchors that can go without changing the shape by more than
1810
+ * `tolerance` (collinear corners, duplicate points, and smooth joints that one cubic can
1811
+ * replace).
1812
+ */
1813
+ declare function removeRedundantPoints(sp: VectorSubpath, tolerance: number): VectorSubpath;
1814
+
1815
+ /**
1816
+ * Knife and eraser on Bézier paths, built on the curve booleans and `outlineStroke`.
1817
+ *
1818
+ * The knife cuts filled regions along a polyline with no gap: each stretch of the polyline
1819
+ * that crosses a region from boundary to boundary (a chord) splits the boundary loop it starts
1820
+ * and ends on into two loops sharing that chord, or, when it joins two loops (an outline and a
1821
+ * hole), merges them into one. Crossings are exact (line–cubic roots), and the pieces share
1822
+ * the cut edge exactly.
1823
+ *
1824
+ * The eraser removes the area of a round-capped stroke along a polyline: filled shapes lose it
1825
+ * by a boolean difference; outlines are cut where they cross the stroke's outline (exact
1826
+ * curve–curve intersections) and keep the parts outside it.
1827
+ *
1828
+ * Modelled on vectorcraft-main/crates/engine/src/cmd/draw2.rs (`knife`, `erase_region`,
1829
+ * `erase_outline`, `cut_subpath`; MIT OR Apache-2.0, © ArtCraft Team and contributors; see
1830
+ * THIRD_PARTY_NOTICES.md), without its fixed knife slit and its sampled crossing search.
1831
+ */
1832
+
1833
+ type FillRule = 'nonzero' | 'evenodd';
1834
+ /** One path per face (an outline with its holes) of a filled path, largest first. */
1835
+ declare function splitIntoFaces(path: BezierPathInput, fillRule?: FillRule): BezierPath[];
1836
+ /**
1837
+ * Cuts the filled path along the open polyline `knife`. Returns one path per resulting face
1838
+ * (an outline with its holes), or null when the knife crossed nothing from edge to edge. A
1839
+ * stretch of the knife that starts or ends inside the shape does not cut.
1840
+ */
1841
+ declare function knifePath(path: BezierPathInput, fillRule: FillRule, knife: readonly Point[]): BezierPath[] | null;
1842
+ /** The area a round eraser of diameter `width` covers along `points` (a disc for one point). */
1843
+ declare function eraserRegion(points: readonly Point[], width: number): BezierPath;
1844
+ /**
1845
+ * A filled path with `region` removed: one path per remaining face, or null when the region
1846
+ * does not overlap the fill.
1847
+ */
1848
+ declare function eraseFromFill(path: BezierPathInput, fillRule: FillRule, region: BezierPathInput): BezierPath[] | null;
1849
+ /**
1850
+ * The parts of a subpath's outline outside `region`, as open subpaths that keep the original
1851
+ * curves exactly; null when the outline does not enter the region. A closed subpath's last
1852
+ * and first parts join at its first anchor.
1853
+ */
1854
+ declare function eraseFromOutline(sp: VectorSubpath, region: BezierPathInput): VectorSubpath[] | null;
1855
+ /** Bounds of a point list (for picking the paths a knife or eraser stroke can reach). */
1856
+ declare function polylineBounds(points: readonly Point[], pad?: number): {
1857
+ x: number;
1858
+ y: number;
1859
+ width: number;
1860
+ height: number;
1861
+ } | null;
1862
+ /** Ramer–Douglas–Peucker reduction of a polyline (keeps both ends). */
1863
+ declare function simplifyPolyline(points: readonly Point[], tolerance: number): Point[];
1864
+
1865
+ /**
1866
+ * Pencil helpers: lazy-mouse smoothing (a pulled string between the pointer and the pen) and
1867
+ * ink-to-shape recognition (a hand-drawn stroke that is a line, rectangle, ellipse or polygon).
1868
+ *
1869
+ * Portions ported from (MIT OR Apache-2.0, © ArtCraft Team and contributors; see
1870
+ * THIRD_PARTY_NOTICES.md):
1871
+ * - photocraft-main/crates/paint/src/dynamics.rs (`Smoother`: pulled-string mode, `string`,
1872
+ * `finish` with catch-up on stroke end)
1873
+ * - wordcraft-main/crates/engine/src/cmd/ink_shape.rs (`recognise`, `ellipse_rms`,
1874
+ * `snap_square`, `corners`, `turning`, `rdp`, `upright`)
1875
+ */
1876
+
1877
+ /**
1878
+ * Pulled-string smoothing: the pen stays put until the pointer is more than `length` away,
1879
+ * then follows it at that distance, so small wobbles never reach the stroke. Feed pointer
1880
+ * positions to `push`; it returns the pen positions to draw through (possibly none).
1881
+ */
1882
+ declare class LazyMouse {
1883
+ readonly length: number;
1884
+ private pos;
1885
+ private last;
1886
+ /** `length` is the string length in the units of the points (0 turns smoothing off). */
1887
+ constructor(length: number);
1888
+ push(p: Point): Point[];
1889
+ /** The string from the pen to the pointer while they are apart, else null. */
1890
+ string(): [Point, Point] | null;
1891
+ /** End of stroke: the pen catches up with the pointer (the point to add, if it lagged). */
1892
+ finish(): Point[];
1893
+ }
1894
+ /** A shape recognised in a stroke, in the stroke's coordinates. */
1895
+ type VectorInkShape = {
1896
+ kind: 'line';
1897
+ a: Point;
1898
+ b: Point;
1899
+ } | {
1900
+ kind: 'rect';
1901
+ x: number;
1902
+ y: number;
1903
+ width: number;
1904
+ height: number;
1905
+ } | {
1906
+ kind: 'ellipse';
1907
+ x: number;
1908
+ y: number;
1909
+ width: number;
1910
+ height: number;
1911
+ } | {
1912
+ kind: 'polygon';
1913
+ points: Point[];
1914
+ };
1915
+ interface RecognizeInkOptions {
1916
+ /** Strokes whose longer side is smaller than this stay ink (default 6). */
1917
+ minSize?: number;
1918
+ }
1919
+ /** Most corners a recognised polygon may have. */
1920
+ declare const VECTOR_INK_MAX_CORNERS = 8;
1921
+ /** "line", "square", "rectangle", "circle", "ellipse", "triangle" or "polygon". */
1922
+ declare function vectorInkShapeName(shape: VectorInkShape): string;
1923
+ /**
1924
+ * The shape a hand-drawn stroke draws, if it is one: an open stroke is a line when it stays
1925
+ * close to the chord between its ends; a closed one is an ellipse when its points sit on the
1926
+ * ellipse inscribed in their bounds, else a polygon of 3–8 corners when that polygon stays
1927
+ * close to every point (an upright four-cornered one is a rectangle). Nearly square boxes
1928
+ * become squares and circles. Anything else (scribbles, curves, letters) is null.
1929
+ */
1930
+ declare function recognizeInkShape(points: readonly Point[], options?: RecognizeInkOptions): VectorInkShape | null;
1931
+
1932
+ /** One arrowhead: a filled outline (nonzero) painted with the stroke's paint. */
1933
+ interface VectorArrowheadShape {
1934
+ end: 'start' | 'end';
1935
+ kind: VectorArrowhead;
1936
+ tip: Vec2;
1937
+ /** Unit direction the head points in. */
1938
+ dir: Vec2;
1939
+ /** Distance from the tip back to where the stroke ends under the head. */
1940
+ inset: number;
1941
+ path: BezierPath;
1942
+ }
1943
+ /** What the stroke of a path draws. */
1944
+ interface VectorStrokeParts {
1945
+ /** Where the stroke sits (`center` for open shapes, whatever the style says). */
1946
+ align: VectorStrokeAlign;
1947
+ /** The line the stroke follows: the outline, shortened under `tip`-aligned heads. */
1948
+ line: BezierPath;
1949
+ /** The stroke width to draw `line` with: twice the style's width for inside / outside (half is clipped away). */
1950
+ drawWidth: number;
1951
+ heads: VectorArrowheadShape[];
1952
+ }
1953
+ /** True when the path has subpaths and all of them are closed (only such shapes have an inside). */
1954
+ declare function isClosedBezierShape(path: BezierPath): boolean;
1955
+ /** The alignment a stroke is drawn with: the style's, or `center` for open shapes. */
1956
+ declare function effectiveStrokeAlign(style: Pick<VectorStyle, 'strokeAlign'>, closedShape: boolean): VectorStrokeAlign;
1957
+ /** True when the style asks for more than a plain centred stroke (an alignment or arrowheads). */
1958
+ declare function hasStrokeExtras(style: VectorStyle): boolean;
1959
+ /** The weight of a head: the stroke width times its scale (at least 0.25). A head is 4 weights long and wide. */
1960
+ declare function arrowheadWeight(style: VectorStyle, end: 'start' | 'end'): number;
1961
+ /** Distance from the tip where the stroke ends, for a stroke whose cap reaches `cap` past its end. */
1962
+ declare function arrowheadInset(kind: VectorArrowhead, hw: number, cap: number): number;
1963
+ /** The outline of a `kind` head of weight `hw` with its tip at `tip`, pointing along unit `dir`. */
1964
+ declare function arrowheadPath(kind: VectorArrowhead, tip: Vec2, dir: Vec2, hw: number): BezierPath;
1965
+ /**
1966
+ * The stroke of `path` as `style` draws it: its alignment, the line it follows (shortened under
1967
+ * `tip`-aligned heads) and its arrowheads. Heads go on the start of the first subpath and the
1968
+ * end of the last one, when those are open.
1969
+ */
1970
+ declare function vectorStrokeParts(path: BezierPath, style: VectorStyle): VectorStrokeParts;
1971
+ /**
1972
+ * The area the stroke paints, as one filled path: the outlined stroke (joins, caps and dashes
1973
+ * as SVG) clipped by the alignment (against the fill, read with the style's `fillRule`), plus
1974
+ * the arrowheads.
1975
+ */
1976
+ declare function vectorStrokeOutlinePath(path: BezierPath, style: VectorStyle): BezierPath;
1977
+ /**
1978
+ * Whether point `p` hits the stroke of `path` (`tolerance` added on every side): within half
1979
+ * the width of the outline for a centred stroke, within the whole width on the inside or the
1980
+ * outside of the fill for an aligned one, or on an arrowhead.
1981
+ */
1982
+ declare function hitTestVectorStroke(path: BezierPath, p: Vec2, style: VectorStyle, tolerance?: number): boolean;
1983
+ /** How far the stroke can reach past the outline (for padding bounds and masks). */
1984
+ declare function vectorStrokeReach(style: VectorStyle): number;
1985
+
1986
+ export { type AddVectorNodeOptions, AffineMatrix, DEFAULT_VECTOR_PATH_EDIT, DEFAULT_VECTOR_SNAP, IDENTITY_MATRIX, KAPPA, LazyMouse, type NearestSegment, type RecognizeInkOptions, SvgGradientDef, VECTOR_ARROWHEADS, VECTOR_ARROWHEAD_LABELS, VECTOR_CLIPBOARD_TYPE, VECTOR_CONSTRUCTION_ANGLES, VECTOR_CORNER_KINDS, VECTOR_CORNER_KIND_LABELS, VECTOR_CORNER_WIDGET_INSET, VECTOR_EDIT_TOGGLES, VECTOR_INK_MAX_CORNERS, VECTOR_OVERLAY_THEME, VECTOR_PATH_ACTIONS, VECTOR_PENCIL_STRING_PX, VECTOR_ROTATE_HANDLE_OFFSET, VECTOR_STROKE_ALIGN_LABELS, VECTOR_STYLE_KEYS, VECTOR_TOOLS, VECTOR_TOOL_GLYPHS, VECTOR_TOOL_SHORTCUTS, VECTOR_TRANSFORM_HANDLES, type Vec2, type VectorActionResult, type VectorAlign, type VectorAnchor, type VectorAnchorRef, type VectorAnchorType, type VectorArrange, type VectorArrowAlign, type VectorArrowhead, type VectorArrowheadShape, type VectorAverageAxis, type VectorBooleanOperation, type VectorBounds, type VectorCommandOptions, type VectorCorner, type VectorCornerInfo, type VectorCornerKind, type VectorCornerWidget, type VectorDefaults, type VectorDistribute, type VectorDocument, VectorEditorController, type VectorEditorOptions, type VectorEditorState, type VectorFillRule, type VectorGroupNode, type VectorGuideKind, type VectorGuideLabel, type VectorGuideLine, type VectorHandleRef, type VectorHover, type VectorInkShape, type VectorInteraction, type VectorKeyInput, type VectorLineCap, type VectorLineJoin, type VectorMatrix, type VectorModifiers, type VectorNode, type VectorNodeListing, type VectorOverlayRenderData, type VectorPaint, type VectorPathAction, type VectorPathEditOptions, type VectorPathNode, type VectorPointSnap, type VectorPointerInput, type VectorRectSnap, type VectorRenderAnchor, type VectorRenderCornerWidget, type VectorRenderGroup, type VectorRenderGuideLabel, type VectorRenderGuideLine, type VectorRenderHandle, type VectorRenderNode, type VectorRenderPath, type VectorRenderStrokeParts, type VectorScaleSnap, type VectorSceneRenderData, type VectorSegmentRef, type VectorSelectionBoxGeometry, type VectorSelectionFrame, type VectorSelectionStyle, type VectorShapeInput, type VectorSnapGuides, type VectorSnapKind, type VectorSnapLeave, type VectorSnapOptions, VectorSnapTargets, type VectorStrokeAlign, type VectorStrokeParts, type VectorStyle, type VectorSubpath, type VectorSvgExportOptions, type VectorSvgImportOptions, type VectorTool, type VectorTransformHandle, type VectorViewport, applyMatrix, applyMatrixLinear, applyVectorCornersAttribute, arrowheadInset, arrowheadPath, arrowheadWeight, bendSegment, bezierToSubpaths, boundsFromPoints, boundsIntersect, cloneAnchor, cloneSubpath, cloneVectorDocument, cloneVectorNode, collectPathNodes, constrainToLeave, convertAnchor, cornerBisectorPoint, cornerRadiusChange, cornerSetback, createVectorCommands, curvatureMoveAnchor, cutSubpathAt, decomposeRigid, effectiveStrokeAlign, ellipseSubpath, eraseFromFill, eraseFromOutline, eraserRegion, expandVectorCorners, expandVectorCornersOf, findVectorNode, fitCornerRadius, getAnchor, getAnchorWorld, getEditablePathIds, getTransformHandlePositions, getVectorCornerWidgets, getVectorOverlayRenderData, getVectorSceneRenderData, getVectorSelectionBoxGeometry, getVectorSelectionFrame, getVectorSelectionStyle, getVisibleAnchors, getVisibleHandles, hasPaint, hasStrokeExtras, hasVectorCorners, hitTestVectorNode, hitTestVectorNodes, hitTestVectorStroke, inferAnchorType, insertAnchor, invertMatrix, isClosedBezierShape, isIdentityMatrix, isVectorTextTarget, joinSubpaths, knifePath, lineSubpath, locateVectorNode, matrixRotation, moveAnchorHandle, multiplyMatrices, nearestSegment, nextVectorCornerKind, nodeMatrix, parseSvgTransform, parseVectorSvg, polygonSubpath, polylineBounds, recognizeInkShape, rectSubpath, removeAnchorKeepingShape, removeAnchors, removeAnchorsKeepingShape, removeRedundantPoints, reverseSubpath, rotateMatrix, roundSubpaths, scaleFor, scaleMatrix, setAnchorCorner, setSubpathCorners, simplifyPolyline, smoothSubpath, splitIntoFaces, splitSubpathAt, starSubpath, subpathPointAt, subpathSegment, subpathSegmentCount, subpathsToBezier, toVectorKeyInput, toVectorModifiers, toVectorPointerInput, transformVectorNode, translateAnchor, translateMatrix, unionBounds, vectorCornerAt, vectorCorners, vectorCornersAttribute, vectorDocumentToSvg, vectorInkShapeName, vectorNodeBounds, vectorNodeRotation, vectorNodeToBezier, vectorPaintToHex, vectorPaintToSvg, vectorParentMatrix, vectorPathData, vectorPathWorldBezier, vectorResizeCursor, vectorStrokeOutline, vectorStrokeOutlinePath, vectorStrokeParts, vectorStrokeReach, vectorStrokeSvgParts, vectorToolLabel, vectorWorldMatrix, walkVectorNodes };