@pixerate/editor 0.35.0 → 0.37.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.
@@ -1,4 +1,4 @@
1
- import { C as CanvasGradientDef, A as AffineMatrix, B as BezierPathInput, a as BezierPath, S as SvgGradientDef } from '../bezier-D8bsO-L0.cjs';
1
+ import { C as CanvasGradientDef, A as AffineMatrix, B as BezierPathInput, a as BezierPath, S as SvgGradientDef, b as StrokeJoin, c as StrokeCap } from '../stroke-CWjFKal4.cjs';
2
2
  import { P as Point } from '../types-BFkYA4eF.cjs';
3
3
  import { H as HistoryManager } from '../HistoryManager-DeQQMBCj.cjs';
4
4
  import { C as CommandSpec } from '../registry-D0GADKc7.cjs';
@@ -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,30 @@ 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';
78
+ /**
79
+ * One point of a variable-width stroke profile. `t` is the position along the subpath by
80
+ * length (0 at its start, 1 at its end); `left` and `right` are how far the stroke reaches on
81
+ * each side, as multiples of half the stroke width (looking along the path on screen, y down).
82
+ * A uniform stroke has 1 on both sides. Two points at the same `t` make a step.
83
+ */
84
+ interface VectorWidthPoint {
85
+ t: number;
86
+ left: number;
87
+ right: number;
88
+ }
47
89
  /** Paint and stroke settings of a path. */
48
90
  interface VectorStyle {
49
91
  fill?: VectorPaint;
@@ -55,6 +97,24 @@ interface VectorStyle {
55
97
  strokeDasharray?: number[];
56
98
  /** Default `nonzero`. */
57
99
  fillRule?: VectorFillRule;
100
+ /** Default `center`. */
101
+ strokeAlign?: VectorStrokeAlign;
102
+ /** Head at the start of the first subpath when it is open, or none. */
103
+ startArrow?: VectorArrowhead | null;
104
+ /** Head at the end of the last subpath when it is open, or none. */
105
+ endArrow?: VectorArrowhead | null;
106
+ /** Default `extend`. */
107
+ arrowAlign?: VectorArrowAlign;
108
+ /** Size of the start head as a multiple of the stroke width (default 1: a head 4 widths long). */
109
+ startArrowScale?: number;
110
+ /** Size of the end head (default 1). */
111
+ endArrowScale?: number;
112
+ /**
113
+ * Variable width: side scales along each subpath, interpolated with a monotone cubic (see
114
+ * `widthProfileAt`). Absent, empty or `null` strokes at a uniform width. A profiled stroke is
115
+ * drawn as its filled outline.
116
+ */
117
+ strokeWidthProfile?: VectorWidthPoint[] | null;
58
118
  }
59
119
  interface VectorNodeBase {
60
120
  id: string;
@@ -111,10 +171,10 @@ interface VectorHandleRef extends VectorAnchorRef {
111
171
  }
112
172
  /**
113
173
  * Canvas tools. `direct` edits anchors, handles and segments; the shape tools drag out a new
114
- * shape; `scissors` cuts a path at a point, `knife` cuts filled shapes along a freehand stroke
115
- * and `eraser` rubs out the area under one.
174
+ * shape; `scissors` cuts a path at a point, `knife` cuts filled shapes along a freehand stroke,
175
+ * `eraser` rubs out the area under one and `width` drags a stroke's width at a point.
116
176
  */
117
- type VectorTool = 'select' | 'direct' | 'pen' | 'pencil' | 'rect' | 'ellipse' | 'polygon' | 'star' | 'line' | 'scissors' | 'knife' | 'eraser';
177
+ type VectorTool = 'select' | 'direct' | 'pen' | 'pencil' | 'rect' | 'ellipse' | 'polygon' | 'star' | 'line' | 'scissors' | 'knife' | 'eraser' | 'width';
118
178
  declare const VECTOR_TOOLS: readonly VectorTool[];
119
179
  /** Keyboard modifiers held during a pointer event. */
120
180
  interface VectorModifiers {
@@ -141,8 +201,10 @@ interface VectorDefaults extends VectorStyle {
141
201
  starPoints?: number;
142
202
  /** Inner radius of the star tool as a fraction of the outer one (default 0.5). */
143
203
  starInnerRatio?: number;
144
- /** Corner radius of the rectangle tool (default 0). */
204
+ /** Live corner radius the rectangle, polygon and star tools give their corners (default 0). */
145
205
  cornerRadius?: number;
206
+ /** Corner kind of those tools (default `round`). */
207
+ cornerKind?: VectorCornerKind;
146
208
  }
147
209
  type VectorAlign = 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom';
148
210
  type VectorDistribute = 'horizontal' | 'vertical';
@@ -173,8 +235,13 @@ declare function hasPaint(paint: VectorPaint | undefined): boolean;
173
235
  declare function subpathSegmentCount(sp: VectorSubpath): number;
174
236
  /** Control points of segment `i` of a subpath: 2 for a line, 4 for a cubic. */
175
237
  declare function subpathSegment(sp: VectorSubpath, i: number): Point[];
176
- /** Absolute `M` / `L` / `C` / `Z` commands of subpaths. */
177
- declare function subpathsToBezier(subpaths: readonly VectorSubpath[]): BezierPath;
238
+ /**
239
+ * Absolute `M` / `L` / `C` / `Z` commands of subpaths, with live corners cut
240
+ * (`expandVectorCorners`). Pass `{ corners: false }` for the stored anchors as they are.
241
+ */
242
+ declare function subpathsToBezier(subpaths: readonly VectorSubpath[], options?: {
243
+ corners?: boolean;
244
+ }): BezierPath;
178
245
  /** The node's outline as Bézier commands. Groups have none. */
179
246
  declare function vectorNodeToBezier(node: VectorNode): BezierPath;
180
247
  /** SVG path data (`d`) of a path node or subpaths. */
@@ -216,10 +283,16 @@ declare function boundsIntersect(a: VectorBounds, b: VectorBounds): boolean;
216
283
  /** A box from two corners in any order. */
217
284
  declare function boundsFromPoints(a: Vec2, b: Vec2): VectorBounds;
218
285
  /**
219
- * Whether `p` hits a node: inside its fill (when it has one) or within half its stroke
220
- * width plus `tolerance` of its outline. A group is hit when any visible child is.
286
+ * Whether `p` hits a node: inside its fill (when it has one), on its stroke as drawn (its
287
+ * alignment and arrowheads respected) or within `tolerance` of either. A group is hit when
288
+ * any visible child is.
221
289
  */
222
290
  declare function hitTestVectorNode(node: VectorNode, p: Vec2, tolerance?: number, parent?: AffineMatrix): boolean;
291
+ /**
292
+ * The area a path node's stroke paints (alignment, caps, joins, dashes and arrowheads
293
+ * included), as a filled path in the space `parent` maps to. Empty without a stroke.
294
+ */
295
+ declare function vectorStrokeOutline(node: VectorPathNode, parent?: AffineMatrix): BezierPath;
223
296
  /** The topmost node hit by `p` (skipping hidden and locked ones), or null. */
224
297
  declare function hitTestVectorNodes(nodes: readonly VectorNode[], p: Vec2, tolerance?: number): VectorNode | null;
225
298
  interface NearestSegment {
@@ -302,8 +375,11 @@ declare function removeAnchors(sp: VectorSubpath, indices: readonly number[]): V
302
375
  declare function reverseSubpath(sp: VectorSubpath): VectorSubpath;
303
376
  /** Handle length that makes a cubic approximate a quarter circle. */
304
377
  declare const KAPPA = 0.5522847498307936;
305
- /** A rectangle, optionally with rounded corners (clamped to half the shorter side). */
306
- declare function rectSubpath(b: VectorBounds, radius?: number): VectorSubpath;
378
+ /**
379
+ * A rectangle. A `radius` becomes a live corner on each of its four anchors (drawn round, and
380
+ * clamped to half the shorter side, when the path is turned into curves).
381
+ */
382
+ declare function rectSubpath(b: VectorBounds, radius?: number, kind?: VectorCornerKind): VectorSubpath;
307
383
  /** An ellipse inscribed in a box (four smooth anchors). */
308
384
  declare function ellipseSubpath(b: VectorBounds): VectorSubpath;
309
385
  /** A regular polygon inscribed in a box, first vertex at the top. */
@@ -315,14 +391,6 @@ declare function lineSubpath(a: Vec2, b: Vec2): VectorSubpath;
315
391
  /** Rounds every coordinate to `precision` decimals (stable JSON and SVG output). */
316
392
  declare function roundSubpaths(subpaths: readonly VectorSubpath[], precision?: number): VectorSubpath[];
317
393
 
318
- /**
319
- * SVG export and import for vector documents. Import is DOM-free (works in Node and
320
- * workers): it reads `<svg>`, `<g>`, `<path>`, `<rect>`, `<circle>`, `<ellipse>`, `<line>`,
321
- * `<polyline>`, `<polygon>`, linear / radial gradients, presentation attributes, `style`
322
- * declarations and `transform`s. Text, images, clip paths, masks, filters and `<use>` are
323
- * skipped.
324
- */
325
-
326
394
  interface VectorSvgExportOptions {
327
395
  /** Decimal places for coordinates (default 3). */
328
396
  precision?: number;
@@ -334,12 +402,52 @@ interface VectorSvgExportOptions {
334
402
  idPrefix?: string;
335
403
  /** Indent nested elements (default true). */
336
404
  pretty?: boolean;
405
+ /**
406
+ * How strokes with an inside / outside alignment or arrowheads are written: `clip` (default)
407
+ * as a double-width stroke clipped or masked by the shape plus filled heads, `outline` as
408
+ * one filled path of the area the stroke paints.
409
+ */
410
+ strokes?: 'clip' | 'outline';
337
411
  }
412
+ /** `data-vector-corners`: the live corners of subpaths as `subpath.index:radius[:kind]`, space separated. */
413
+ declare function vectorCornersAttribute(subpaths: readonly VectorSubpath[], precision?: number): string;
414
+ /** Puts `data-vector-corners` back on subpaths (malformed entries are ignored). */
415
+ declare function applyVectorCornersAttribute(subpaths: VectorSubpath[], value: string): VectorSubpath[];
338
416
  /** A paint as an SVG attribute value plus the gradient def it needs (if any). */
339
417
  declare function vectorPaintToSvg(paint: VectorPaint | undefined, gradientId: string): {
340
418
  value: string;
341
419
  gradient: SvgGradientDef | null;
342
420
  };
421
+ /** The parts of an aligned or arrowed stroke (see `VectorRenderPath.strokeParts`). */
422
+ interface VectorRenderStrokeParts {
423
+ align: VectorStrokeAlign;
424
+ /** The line the stroke follows (shortened under `tip`-aligned heads). */
425
+ d: string;
426
+ /** Width to draw it with: twice the stroke width when aligned, half of it being clipped away. */
427
+ width: number;
428
+ /**
429
+ * A variable-width stroke: the area to fill with `stroke` (nonzero) instead of stroking `d`,
430
+ * clipped or masked like the stroke would be.
431
+ */
432
+ outline?: string;
433
+ /**
434
+ * Inside: a `<clipPath id>` holding the shape (`d`, `clip-rule` = `fillRule`). Outside: a
435
+ * `<mask id>` in user space over `bounds`, white with the shape (`d`) in black.
436
+ */
437
+ clip: {
438
+ kind: 'clip' | 'mask';
439
+ id: string;
440
+ bounds: VectorBounds;
441
+ } | null;
442
+ /** Arrowhead outlines (fill with `stroke`, nonzero). */
443
+ heads: string[];
444
+ }
445
+ /** The stroke parts of a path whose stroke needs more than one element, in its own space (undefined otherwise). */
446
+ declare function vectorStrokeSvgParts(node: VectorPathNode, clipId: string, precision?: number): VectorRenderStrokeParts | undefined;
447
+ /** A width profile as a data attribute: `t,left,right` points separated by `;`. */
448
+ declare function widthProfileToAttribute(points: readonly VectorWidthPoint[], precision?: number): string;
449
+ /** Reads `widthProfileToAttribute` output (invalid points are dropped). */
450
+ declare function widthProfileFromAttribute(value: string | undefined): VectorWidthPoint[];
343
451
  /** Serializes a vector document as a standalone SVG string. */
344
452
  declare function vectorDocumentToSvg(doc: VectorDocument, options?: VectorSvgExportOptions): string;
345
453
  interface VectorSvgImportOptions {
@@ -351,6 +459,194 @@ declare function parseSvgTransform(value: string | undefined): AffineMatrix;
351
459
  /** Parses SVG markup into a vector document. Throws when there is no `<svg>` element. */
352
460
  declare function parseVectorSvg(svg: string, options?: VectorSvgImportOptions): VectorDocument;
353
461
 
462
+ /**
463
+ * A corner a live radius can cut: an anchor that is not smooth, where two sides (straight or
464
+ * curved) meet at an angle. Positions are in the path node's own space.
465
+ */
466
+ interface VectorCornerInfo {
467
+ subpath: number;
468
+ index: number;
469
+ /** Where the sides meet. */
470
+ at: Vec2;
471
+ /** Unit directions the sides leave the corner in: towards the previous anchor, then the next one. */
472
+ u: Vec2;
473
+ v: Vec2;
474
+ /** The sides' lengths (along curves), in the same order. */
475
+ lu: number;
476
+ lv: number;
477
+ /** Cosine and sine of the angle between the sides. */
478
+ cos: number;
479
+ sin: number;
480
+ /** The angle between the sides in degrees (90 at a rectangle's corners). */
481
+ angle: number;
482
+ /** The largest radius that fits: its cut then reaches halfway along the shorter side. */
483
+ maxRadius: number;
484
+ }
485
+ /** The corner at anchor `index` of a subpath, or null when the anchor makes none (an open end, smooth, or straight on). */
486
+ declare function vectorCornerAt(sp: VectorSubpath, index: number, subpath?: number): VectorCornerInfo | null;
487
+ /** Every corner of the subpaths that a live radius can cut, in anchor order. */
488
+ declare function vectorCorners(subpaths: readonly VectorSubpath[]): VectorCornerInfo[];
489
+ /** How far from the corner a round cut of radius `r` meets each side: r / tan(angle / 2). */
490
+ declare const cornerSetback: (c: Pick<VectorCornerInfo, "cos" | "sin">, r: number) => number;
491
+ /** `radius` clamped to what the corner can draw (0 for negative or non-finite radii). */
492
+ declare const fitCornerRadius: (c: Pick<VectorCornerInfo, "maxRadius">, radius: number) => number;
493
+ /** The point `k` along the corner's bisector, measured in (u + v): a round cut of radius r is centred at k = r / sin. */
494
+ declare const cornerBisectorPoint: (c: VectorCornerInfo, k: number) => Vec2;
495
+ /** How much the radius grows when the round cut's centre moves by `d` (its part along the bisector, in radius units). */
496
+ declare const cornerRadiusChange: (c: VectorCornerInfo, d: Vec2) => number;
497
+ /** True when any anchor of the subpaths has a live corner radius. */
498
+ declare function hasVectorCorners(subpaths: readonly VectorSubpath[]): boolean;
499
+ /** One subpath with its corners cut; `sources[i]` is the stored anchor each output anchor comes from. */
500
+ declare function expandVectorCornersOf(sp: VectorSubpath, subpath?: number): {
501
+ subpath: VectorSubpath;
502
+ sources: number[];
503
+ };
504
+ /**
505
+ * The subpaths with every live corner cut (round, inverted or chamfer), each radius no larger
506
+ * than fits. A cut corner becomes two anchors and curved sides are trimmed to what the cuts
507
+ * leave of them. Subpaths without corners come back unchanged.
508
+ */
509
+ declare function expandVectorCorners(subpaths: readonly VectorSubpath[]): VectorSubpath[];
510
+ /**
511
+ * Sets (or clears, with radius 0) the live corner of anchors. `kind` keeps each anchor's own
512
+ * kind when omitted. Anchors that make no corner keep the radius too, so it applies once they do.
513
+ */
514
+ declare function setAnchorCorner(a: VectorAnchor, radius: number, kind?: VectorCornerKind): VectorAnchor;
515
+ /** The next corner kind (Alt-clicking a corner widget cycles round → inverted → chamfer). */
516
+ declare function nextVectorCornerKind(kind: VectorCornerKind | undefined): VectorCornerKind;
517
+ /** Every anchor of a subpath given the same live corner (radius 0 clears them). */
518
+ declare function setSubpathCorners(sp: VectorSubpath, radius: number, kind?: VectorCornerKind): VectorSubpath;
519
+
520
+ /**
521
+ * Variable-width stroke profiles: stroke widths at positions along a path (each subpath on its
522
+ * own), interpolated smoothly and held flat before the first and after the last point. Each
523
+ * point has a left and a right side scale (looking along the path on screen, y down), so a
524
+ * stroke can grow on one side only.
525
+ *
526
+ * Ported from ArtCraft (MIT OR Apache-2.0, see THIRD_PARTY_NOTICES.md):
527
+ * - figcraft `crates/doc/src/props.rs` — `WidthProfile` / `WidthPoint`: sanitising, side
528
+ * scales, inserting and moving points, and the monotone cubic (Fritsch–Carlson) interpolation
529
+ * that never overshoots its neighbouring points.
530
+ * - vectorcraft `crates/doc/src/appearance.rs` — `WidthProfile` as `(t, left, right)` points,
531
+ * discontinuous points (two points at one `t` make a step) and the profile presets.
532
+ * - effectcraft `crates/path/src/varstroke.rs` — `width_factor`: the parametric Taper (lengths,
533
+ * end widths, ease) and Wave (amount, cycles, phase) shapes, used here as profile generators.
534
+ */
535
+
536
+ /** The most points a profile keeps. */
537
+ declare const VECTOR_WIDTH_PROFILE_MAX_POINTS = 64;
538
+ /** The widest a side can be, as a multiple of the stroke width. */
539
+ declare const VECTOR_WIDTH_PROFILE_MAX_SCALE = 1000;
540
+ /** Positions closer than this are the same place (the two sides of a discontinuous point). */
541
+ declare const VECTOR_WIDTH_SAME_T = 0.000001;
542
+ /** Built-in profiles. `uniform` is the plain stroke (no profile). */
543
+ type VectorWidthPreset = 'uniform' | 'taperStart' | 'taperEnd' | 'taperBoth' | 'bulge' | 'pinch' | 'teardrop' | 'wave' | 'pressure';
544
+ /** One preset as a menu shows it. */
545
+ interface VectorWidthPresetInfo {
546
+ id: VectorWidthPreset;
547
+ label: string;
548
+ points: readonly VectorWidthPoint[];
549
+ }
550
+ /** The built-in profiles, in menu order. */
551
+ declare const VECTOR_WIDTH_PRESETS: readonly VectorWidthPresetInfo[];
552
+ declare const VECTOR_WIDTH_PRESET_IDS: readonly VectorWidthPreset[];
553
+ /**
554
+ * A profile as the outline uses it: finite points only, `t` in 0–1, side scales in 0–1000,
555
+ * sorted by `t` (stable, so a step keeps its order), at most 64 points. Points missing a side
556
+ * take the other side's scale.
557
+ */
558
+ declare function sanitizeWidthProfile(points: readonly Partial<VectorWidthPoint>[] | null | undefined): VectorWidthPoint[];
559
+ /** True when the profile draws a plain stroke: no points, or every side at scale 1. */
560
+ declare function isUniformWidthProfile(points: readonly VectorWidthPoint[] | null | undefined): boolean;
561
+ /**
562
+ * The left and right side scales at `t` (0–1 along the subpath by length). Each side is
563
+ * interpolated on its own with a monotone cubic, so the width never overshoots its points;
564
+ * a profile without points is uniform (1, 1).
565
+ */
566
+ declare function widthProfileAt(points: readonly VectorWidthPoint[] | null | undefined, t: number): {
567
+ left: number;
568
+ right: number;
569
+ };
570
+ /** The largest side scale (the interpolation never exceeds its points); 1 without a profile. */
571
+ declare function widthProfileMaxScale(points: readonly VectorWidthPoint[] | null | undefined): number;
572
+ /** The points of a preset (empty for `uniform`), or null for an unknown id. */
573
+ declare function widthProfilePreset(id: string): VectorWidthPoint[] | null;
574
+ /** The preset a profile matches: `uniform` without one (or all at scale 1), `custom` when none matches. */
575
+ declare function widthProfilePresetId(points: readonly VectorWidthPoint[] | null | undefined): VectorWidthPreset | 'custom';
576
+ /** The profile of the reversed path: positions mirrored and the sides swapped. */
577
+ declare function reverseWidthProfile(points: readonly VectorWidthPoint[]): VectorWidthPoint[];
578
+ /** The profile of a mirrored path (it runs the same way, but its sides swap). */
579
+ declare function mirrorWidthProfile(points: readonly VectorWidthPoint[]): VectorWidthPoint[];
580
+ /**
581
+ * Adds a width point at `t` (side scales `left` / `right`, default: as wide as the stroke is
582
+ * there) and returns the new profile and the point's index. Inserting at the position of an
583
+ * existing point replaces it, unless `step` is set (which makes a discontinuous point).
584
+ */
585
+ declare function insertWidthPoint(points: readonly VectorWidthPoint[] | null | undefined, t: number, sides?: {
586
+ left?: number;
587
+ right?: number;
588
+ }, options?: {
589
+ step?: boolean;
590
+ }): {
591
+ points: VectorWidthPoint[];
592
+ index: number;
593
+ };
594
+ /**
595
+ * Changes point `index`: moves it to `t` (kept between its neighbours, so indices stay put)
596
+ * and / or sets its side scales. Out-of-range indices leave the profile as it is.
597
+ */
598
+ declare function setWidthPoint(points: readonly VectorWidthPoint[], index: number, patch: Partial<VectorWidthPoint>): VectorWidthPoint[];
599
+ /** Removes the points at `indices`. */
600
+ declare function removeWidthPoints(points: readonly VectorWidthPoint[], indices: readonly number[]): VectorWidthPoint[];
601
+ /** A parametric taper (effectcraft's Stroke Taper), lengths as fractions of the path. */
602
+ interface VectorTaperOptions {
603
+ /** Fraction of the path the start taper covers (default 0). */
604
+ startLength?: number;
605
+ /** Fraction the end taper covers (default 0). */
606
+ endLength?: number;
607
+ /** Width at the start as a fraction of the stroke width (default 0: a point). */
608
+ startWidth?: number;
609
+ endWidth?: number;
610
+ /** 0 linear, 1 a quarter circle bulging outwards (default 0). */
611
+ startEase?: number;
612
+ endEase?: number;
613
+ }
614
+ /** A width wave (effectcraft's Stroke Wave): the width swings between `1 − amount` and 1. */
615
+ interface VectorWaveOptions {
616
+ /** 0–1 (default 0.5). */
617
+ amount?: number;
618
+ /** Whole cycles along the path (default 4). */
619
+ cycles?: number;
620
+ /** Degrees (default 0). */
621
+ phase?: number;
622
+ }
623
+ /** The width factor at fraction `t` of a taper and / or a wave (effectcraft `width_factor`). */
624
+ declare function taperWaveFactor(t: number, taper?: VectorTaperOptions | null, wave?: VectorWaveOptions | null): number;
625
+ /**
626
+ * Samples a taper and / or wave into profile points (symmetric), simplified to within
627
+ * `tolerance` of the factor (default 0.01). The monotone interpolation draws them back smoothly.
628
+ */
629
+ declare function generateWidthProfile(options: {
630
+ taper?: VectorTaperOptions | null;
631
+ wave?: VectorWaveOptions | null;
632
+ samples?: number;
633
+ tolerance?: number;
634
+ }): VectorWidthPoint[];
635
+ /** Pen pressure that draws the stroke's own width (a mouse reports 0.5 while a button is down). */
636
+ declare const VECTOR_PRESSURE_NOMINAL = 0.5;
637
+ /**
638
+ * A profile from pen pressure recorded along a stroke: `(t, pressure 0–1)` samples in order.
639
+ * The width scale is pressure ÷ 0.5 (so half pressure draws the stroke's width, full pressure
640
+ * twice it), at least 0.05; the samples are simplified to within `tolerance` (default 0.02).
641
+ * Returns an empty (uniform) profile when every sample is the same.
642
+ */
643
+ declare function pressureWidthProfile(samples: readonly {
644
+ t: number;
645
+ pressure: number;
646
+ }[], options?: {
647
+ tolerance?: number;
648
+ }): VectorWidthPoint[];
649
+
354
650
  /**
355
651
  * What a guide line shows: `align` lines run from a target through the snapped point or box,
356
652
  * `construction` lines from where a drawn point leaves, `spacing` marks equal gaps and `size`
@@ -585,6 +881,11 @@ interface VectorPathEditOptions {
585
881
  recognizeShapes: boolean;
586
882
  /** Eraser diameter in document units (default 10). */
587
883
  eraserSize: number;
884
+ /**
885
+ * Record pointer pressure on pencil strokes as a width profile: `pen` (default) for pen input
886
+ * only, `all` for any pointer that reports pressure, `off` never.
887
+ */
888
+ pencilPressure: 'pen' | 'all' | 'off';
588
889
  }
589
890
  /** Path-editing options new editors start with. */
590
891
  declare const DEFAULT_VECTOR_PATH_EDIT: Readonly<VectorPathEditOptions>;
@@ -612,6 +913,27 @@ interface VectorEditorOptions {
612
913
  interface VectorPointerInput extends VectorModifiers {
613
914
  /** 0 = primary, 1 = middle (pans), 2 = secondary. Default 0. */
614
915
  button?: number;
916
+ /** `PointerEvent.pressure` (0–1), recorded on pencil strokes. */
917
+ pressure?: number;
918
+ /** `PointerEvent.pointerType` (`mouse`, `pen`, `touch`). */
919
+ pointerType?: string;
920
+ }
921
+ /** One width point of a path's stroke profile; `subpath` is the subpath it is shown on. */
922
+ interface VectorWidthPointRef {
923
+ nodeId: string;
924
+ index: number;
925
+ subpath: number;
926
+ }
927
+ /** A spot on a stroke (width tool): where a drag would add a width point. Positions in document units. */
928
+ interface VectorStrokeSpot {
929
+ nodeId: string;
930
+ subpath: number;
931
+ /** Position along the subpath by length, 0–1. */
932
+ t: number;
933
+ point: Vec2;
934
+ /** Ends of the stroke's width across the path there. */
935
+ left: Vec2;
936
+ right: Vec2;
615
937
  }
616
938
  /** A key press as the controller needs it. */
617
939
  interface VectorKeyInput extends VectorModifiers {
@@ -632,6 +954,14 @@ interface VectorHover {
632
954
  point: Vec2;
633
955
  };
634
956
  transformHandle?: VectorTransformHandle;
957
+ /** A live corner widget (direct tool): the corner's anchor. */
958
+ corner?: VectorAnchorRef;
959
+ /** A width point (width tool): its centre or the end of its left / right side. */
960
+ widthPoint?: VectorWidthPointRef & {
961
+ part: 'center' | 'left' | 'right';
962
+ };
963
+ /** A spot on a stroke away from its width points (width tool). */
964
+ strokeSpot?: VectorStrokeSpot;
635
965
  }
636
966
  /** The drag in progress, for overlays and status text. */
637
967
  type VectorInteraction = {
@@ -659,6 +989,10 @@ type VectorInteraction = {
659
989
  } | {
660
990
  type: 'bend';
661
991
  ref: VectorSegmentRef;
992
+ } | {
993
+ type: 'corner';
994
+ ref: VectorAnchorRef;
995
+ radius: number;
662
996
  } | {
663
997
  type: 'draw';
664
998
  tool: VectorTool;
@@ -670,6 +1004,11 @@ type VectorInteraction = {
670
1004
  tool: 'knife' | 'eraser';
671
1005
  points: Vec2[];
672
1006
  width: number;
1007
+ } | {
1008
+ type: 'width';
1009
+ ref: VectorWidthPointRef;
1010
+ mode: 'width' | 'move';
1011
+ width: number;
673
1012
  } | {
674
1013
  type: 'pen';
675
1014
  nodeId: string;
@@ -684,6 +1023,8 @@ interface VectorEditorState {
684
1023
  selection: readonly string[];
685
1024
  /** Selected anchors (direct tool). */
686
1025
  selectedAnchors: readonly VectorAnchorRef[];
1026
+ /** The selected width point (width tool); Delete removes it. */
1027
+ selectedWidthPoint: VectorWidthPointRef | null;
687
1028
  /** The path whose anchors are being edited, if any. */
688
1029
  editingId: string | null;
689
1030
  /** Axis-aligned bounds of the selected nodes in document units. */
@@ -691,7 +1032,7 @@ interface VectorEditorState {
691
1032
  /** The selection box, rotated with the selection when its nodes share a rotation. */
692
1033
  selectionFrame: VectorSelectionFrame | null;
693
1034
  viewport: VectorViewport;
694
- defaults: Readonly<Required<Pick<VectorDefaults, 'polygonSides' | 'starPoints' | 'starInnerRatio' | 'cornerRadius'>> & VectorStyle>;
1035
+ defaults: Readonly<Required<Pick<VectorDefaults, 'polygonSides' | 'starPoints' | 'starInnerRatio' | 'cornerRadius'>> & Pick<VectorDefaults, 'cornerKind'> & VectorStyle>;
695
1036
  snap: VectorSnapOptions;
696
1037
  editing: VectorPathEditOptions;
697
1038
  /** Smart guides of the snap in progress, in document units (null when there are none). */
@@ -738,12 +1079,16 @@ interface VectorShapeInput extends VectorStyle {
738
1079
  y: number;
739
1080
  width: number;
740
1081
  height: number;
1082
+ /** Live corner radius of a rectangle, polygon or star (default: the editor default). */
741
1083
  cornerRadius?: number;
1084
+ cornerKind?: VectorCornerKind;
742
1085
  sides?: number;
743
1086
  points?: number;
744
1087
  innerRatio?: number;
745
1088
  name?: string;
746
1089
  }
1090
+ /** Every field of `VectorStyle`, in the order style panels and commands list them. */
1091
+ declare const VECTOR_STYLE_KEYS: readonly ["fill", "stroke", "strokeWidth", "strokeLinecap", "strokeLinejoin", "strokeDasharray", "fillRule", "strokeAlign", "startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale", "strokeWidthProfile"];
747
1092
  /** Clipboard payload type written by `copy()`. */
748
1093
  declare const VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
749
1094
  /** Screen distance of the rotation handle above the selection box. */
@@ -757,6 +1102,60 @@ declare function getVisibleAnchors(state: Pick<VectorEditorState, 'document' | '
757
1102
  * (direct tool), or of the last anchor of the pen path.
758
1103
  */
759
1104
  declare function getVisibleHandles(state: Pick<VectorEditorState, 'document' | 'selection' | 'editingId' | 'tool' | 'pen' | 'selectedAnchors'>): VectorHandleRef[];
1105
+ /** Live corner widgets sit at least this far inside their corner (screen px along each side of a right angle). */
1106
+ declare const VECTOR_CORNER_WIDGET_INSET = 10;
1107
+ /** A live corner widget of the direct tool: drag it to round the corners, Alt-click to change their kind. */
1108
+ interface VectorCornerWidget {
1109
+ ref: VectorAnchorRef;
1110
+ /** Document position of the widget. */
1111
+ x: number;
1112
+ y: number;
1113
+ /** The radius stored on the anchor and the radius drawn (no larger than fits). */
1114
+ radius: number;
1115
+ drawnRadius: number;
1116
+ kind: VectorCornerKind;
1117
+ /** The corner in its node's own space. */
1118
+ corner: VectorCornerInfo;
1119
+ }
1120
+ /**
1121
+ * The live corner widgets the direct tool shows: on each editable path, at its selected corner
1122
+ * anchors (or at every corner when none of its anchors is selected). Corners whose sides are
1123
+ * shorter than three insets on screen hide theirs. Dragging one sets the radius of all of them.
1124
+ * Ported from ArtCraft vectorcraft `crates/tools/src/corners.rs` (MIT OR Apache-2.0, see
1125
+ * THIRD_PARTY_NOTICES.md), as is the `corner` drag below.
1126
+ */
1127
+ declare function getVectorCornerWidgets(state: Pick<VectorEditorState, 'document' | 'selection' | 'editingId' | 'tool' | 'pen' | 'selectedAnchors' | 'viewport'>): VectorCornerWidget[];
1128
+ /**
1129
+ * The line a path's stroke follows (node space; shortened under `tip` arrowheads) and the
1130
+ * distance a side scale of 1 reaches from it (half the drawn width: twice the stroke width for
1131
+ * inside / outside strokes). Null for paths without a visible stroke.
1132
+ */
1133
+ declare function vectorStrokeWidthGeometry(node: VectorPathNode): {
1134
+ line: BezierPath;
1135
+ unit: number;
1136
+ } | null;
1137
+ /** A width point of the width tool, in document units. */
1138
+ interface VectorWidthHandle {
1139
+ ref: VectorWidthPointRef;
1140
+ /** The point on the path. */
1141
+ x: number;
1142
+ y: number;
1143
+ /** Ends of its left and right sides. */
1144
+ left: Vec2;
1145
+ right: Vec2;
1146
+ t: number;
1147
+ /** Side scales (multiples of half the stroke width). */
1148
+ leftScale: number;
1149
+ rightScale: number;
1150
+ selected: boolean;
1151
+ }
1152
+ /**
1153
+ * The width points the width tool shows: every profile point on every subpath of the stroke
1154
+ * under the pointer, the selected paths and the stroke being edited. Ported from ArtCraft
1155
+ * vectorcraft `crates/tools/src/distort/width.rs` (MIT OR Apache-2.0, see
1156
+ * THIRD_PARTY_NOTICES.md), as are the `width` drag and hover below.
1157
+ */
1158
+ declare function getVectorWidthHandles(state: Pick<VectorEditorState, 'document' | 'selection' | 'tool' | 'hover' | 'interaction' | 'selectedWidthPoint'>): VectorWidthHandle[];
760
1159
  /** Looks up an anchor (positions in its node's own space). */
761
1160
  declare function getAnchor(doc: VectorDocument, ref: VectorAnchorRef): VectorAnchor | undefined;
762
1161
  /** An anchor with its position and handles in document space (its node's transforms applied). */
@@ -825,6 +1224,7 @@ declare class VectorEditorController {
825
1224
  private doc;
826
1225
  private selection;
827
1226
  private anchors;
1227
+ private widthPoint;
828
1228
  private editingId;
829
1229
  private pen;
830
1230
  private tool;
@@ -958,6 +1358,44 @@ declare class VectorEditorController {
958
1358
  }, ids?: readonly string[], options?: {
959
1359
  label?: string;
960
1360
  }): VectorActionResult;
1361
+ /**
1362
+ * Sets the variable-width profile of the selected (or given) paths: a preset id
1363
+ * (`uniform` removes the profile), points, or a parametric taper / wave. Returns an error for
1364
+ * an unknown preset.
1365
+ */
1366
+ setWidthProfile(profile: VectorWidthPreset | readonly VectorWidthPoint[] | {
1367
+ taper?: VectorTaperOptions;
1368
+ wave?: VectorWaveOptions;
1369
+ } | null, options?: {
1370
+ ids?: readonly string[];
1371
+ }): VectorActionResult;
1372
+ /** Removes one width point of a path's profile (the width tool's Delete). */
1373
+ removeWidthPoint(ref: Pick<VectorWidthPointRef, 'nodeId' | 'index'>): VectorActionResult;
1374
+ /**
1375
+ * Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
1376
+ * anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
1377
+ * corners sharp again. Corners shrink to fit when drawn, so any radius is accepted.
1378
+ */
1379
+ setCornerRadius(radius: number, options?: {
1380
+ kind?: VectorCornerKind;
1381
+ refs?: readonly VectorAnchorRef[];
1382
+ ids?: readonly string[];
1383
+ }): VectorActionResult;
1384
+ /** Sets the live corner kind of the same anchors as `setCornerRadius`, keeping their radii. */
1385
+ setCornerKind(kind: VectorCornerKind, options?: {
1386
+ refs?: readonly VectorAnchorRef[];
1387
+ ids?: readonly string[];
1388
+ }): VectorActionResult;
1389
+ /** Anchors a corner change applies to (see `setCornerRadius`). */
1390
+ private cornerTargets;
1391
+ /**
1392
+ * Outline Stroke: replaces each selected (or given) path's stroke with a filled path of the
1393
+ * area it paints (alignment, joins, caps, dashes and arrowheads included), placed above a copy
1394
+ * of the path that keeps the fill (left out when there is none). Returns the new paths' ids.
1395
+ */
1396
+ outlineStroke(options?: {
1397
+ ids?: readonly string[];
1398
+ }): string[];
961
1399
  rename(id: string, name: string): VectorActionResult;
962
1400
  setVisible(id: string, visible: boolean): VectorActionResult;
963
1401
  setLocked(id: string, locked: boolean): VectorActionResult;
@@ -1197,12 +1635,20 @@ declare class VectorEditorController {
1197
1635
  private tolerance;
1198
1636
  private hitTransformHandle;
1199
1637
  private hitHandle;
1638
+ private cornerView;
1639
+ private hitCornerWidget;
1200
1640
  private hitAnchor;
1201
1641
  /** Deepest unlocked visible path under `p`. */
1202
1642
  private hitPath;
1203
1643
  /** Endpoints of open subpaths of unlocked visible paths (pen tool resumes from them). */
1204
1644
  private openEndpoints;
1205
1645
  private computeHover;
1646
+ private widthView;
1647
+ /** The width point centre or side end under `screen` (width tool). */
1648
+ private hitWidthHandle;
1649
+ /** The nearest stroked, unlocked visible path whose stroke is under document point `p` (width tool). */
1650
+ private strokeSpotAt;
1651
+ private downWidth;
1206
1652
  /** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
1207
1653
  private nearestOutline;
1208
1654
  private sameHover;
@@ -1258,6 +1704,13 @@ interface VectorRenderPath {
1258
1704
  strokeDasharray?: string;
1259
1705
  fillRule: 'nonzero' | 'evenodd';
1260
1706
  opacity: number;
1707
+ /**
1708
+ * Set when the stroke takes more than one element: an inside / outside alignment or
1709
+ * arrowheads. Draw `d` with the fill only, then `stroke.d` with the stroke (at
1710
+ * `stroke.width`, clipped or masked as `stroke.clip` says), then each head filled with
1711
+ * `stroke`; group them under `id`, `transform` and `opacity`.
1712
+ */
1713
+ strokeParts?: VectorRenderStrokeParts;
1261
1714
  }
1262
1715
  interface VectorRenderGroup {
1263
1716
  kind: 'group';
@@ -1299,6 +1752,33 @@ interface VectorRenderHandle {
1299
1752
  anchorY: number;
1300
1753
  hovered: boolean;
1301
1754
  }
1755
+ /** A live corner widget in screen pixels. */
1756
+ interface VectorRenderCornerWidget {
1757
+ ref: VectorAnchorRef;
1758
+ x: number;
1759
+ y: number;
1760
+ radius: number;
1761
+ kind: VectorCornerKind;
1762
+ hovered: boolean;
1763
+ /** Its corners are being dragged. */
1764
+ active: boolean;
1765
+ }
1766
+ /**
1767
+ * A width point of the width tool in screen pixels: a diamond at (`x`, `y`) on the path and a
1768
+ * line across the stroke from `left` to `right`, with a handle at each end.
1769
+ */
1770
+ interface VectorRenderWidthPoint {
1771
+ ref: VectorWidthPointRef;
1772
+ x: number;
1773
+ y: number;
1774
+ left: Vec2;
1775
+ right: Vec2;
1776
+ selected: boolean;
1777
+ /** The part under the pointer, if any. */
1778
+ hovered: 'center' | 'left' | 'right' | null;
1779
+ /** Being dragged. */
1780
+ active: boolean;
1781
+ }
1302
1782
  /** A smart guide line in screen pixels. */
1303
1783
  interface VectorRenderGuideLine {
1304
1784
  kind: VectorGuideKind;
@@ -1363,6 +1843,23 @@ interface VectorOverlayRenderData {
1363
1843
  } | null;
1364
1844
  /** Where the scissors would cut (screen space). */
1365
1845
  cutPoint: Vec2 | null;
1846
+ /**
1847
+ * Live corner widgets of the direct tool (screen space): drag one to round its corners,
1848
+ * Alt-click to cycle round / inverted / chamfer, click to pick its corner alone.
1849
+ */
1850
+ cornerWidgets: VectorRenderCornerWidget[];
1851
+ /** Width points of the width tool (screen space). */
1852
+ widthPoints: VectorRenderWidthPoint[];
1853
+ /**
1854
+ * Where a width-tool drag would add a width point (screen space): draw a dashed line from
1855
+ * `left` to `right` and a hollow diamond at (`x`, `y`).
1856
+ */
1857
+ widthSpot: {
1858
+ x: number;
1859
+ y: number;
1860
+ left: Vec2;
1861
+ right: Vec2;
1862
+ } | null;
1366
1863
  /** A short status, e.g. "W 120 × H 80" or "45°". */
1367
1864
  status: string | null;
1368
1865
  }
@@ -1387,7 +1884,13 @@ declare const VECTOR_OVERLAY_THEME: {
1387
1884
  readonly transformHandleSize: 8;
1388
1885
  readonly anchorSize: 7;
1389
1886
  readonly handleRadius: 3.5;
1887
+ /** Radius of a live corner widget (a ring with a dot). */
1888
+ readonly cornerWidgetRadius: 4;
1889
+ /** Half the diagonal of a width point's diamond. */
1890
+ readonly widthPointSize: 5;
1390
1891
  };
1892
+ /** SVG path data of the diamond a width point is drawn with (screen pixels). */
1893
+ declare function vectorDiamondPath(x: number, y: number, r?: number): string;
1391
1894
  interface ModifierEventLike {
1392
1895
  shiftKey?: boolean;
1393
1896
  altKey?: boolean;
@@ -1400,10 +1903,17 @@ declare function toVectorModifiers(event: ModifierEventLike): {
1400
1903
  alt: boolean;
1401
1904
  mod: boolean;
1402
1905
  };
1403
- /** A pointer event as `VectorEditorController.pointerDown/Move/Up` input. */
1906
+ /**
1907
+ * A pointer event as `VectorEditorController.pointerDown/Move/Up` input: modifiers, button,
1908
+ * and the pressure and pointer type the pencil records as a width profile.
1909
+ */
1404
1910
  declare function toVectorPointerInput(event: ModifierEventLike & {
1405
1911
  button?: number;
1912
+ pressure?: number;
1913
+ pointerType?: string;
1406
1914
  }): {
1915
+ pointerType?: string | undefined;
1916
+ pressure?: number | undefined;
1407
1917
  button: number | undefined;
1408
1918
  shift: boolean;
1409
1919
  alt: boolean;
@@ -1426,21 +1936,52 @@ interface VectorSelectionStyle {
1426
1936
  stroke: VectorPaint;
1427
1937
  strokeWidth: number;
1428
1938
  opacity: number;
1939
+ strokeAlign: VectorStrokeAlign;
1940
+ startArrow: VectorArrowhead | null;
1941
+ endArrow: VectorArrowhead | null;
1942
+ arrowAlign: VectorArrowAlign;
1943
+ startArrowScale: number;
1944
+ endArrowScale: number;
1945
+ /** The first path's width profile (null when uniform) and the preset it matches. */
1946
+ strokeWidthProfile: VectorWidthPoint[] | null;
1947
+ widthProfile: VectorWidthPreset | 'custom';
1948
+ /**
1949
+ * Live corners of the selected anchors (direct tool) or of every corner of the selected
1950
+ * paths: the first one's radius and kind, and how many corners there are (0: none to round).
1951
+ */
1952
+ cornerRadius: number;
1953
+ cornerKind: VectorCornerKind;
1954
+ corners: number;
1955
+ /** Some selected path is open, so arrowheads apply. */
1956
+ hasOpenPath: boolean;
1429
1957
  mixed: {
1430
1958
  fill: boolean;
1431
1959
  stroke: boolean;
1432
1960
  strokeWidth: boolean;
1433
1961
  opacity: boolean;
1962
+ strokeAlign: boolean;
1963
+ startArrow: boolean;
1964
+ endArrow: boolean;
1965
+ arrowAlign: boolean;
1966
+ startArrowScale: boolean;
1967
+ endArrowScale: boolean;
1968
+ widthProfile: boolean;
1969
+ cornerRadius: boolean;
1970
+ cornerKind: boolean;
1434
1971
  };
1435
1972
  /** Ids of the paths the style applies to. */
1436
1973
  pathIds: string[];
1437
1974
  }
1438
- declare function getVectorSelectionStyle(state: Pick<VectorEditorState, 'document' | 'selection'>): VectorSelectionStyle | null;
1975
+ declare function getVectorSelectionStyle(state: Pick<VectorEditorState, 'document' | 'selection'> & Partial<Pick<VectorEditorState, 'selectedAnchors' | 'tool'>>): VectorSelectionStyle | null;
1976
+ /** Labels of the stroke alignments, corner kinds and arrowheads, for style panels. */
1977
+ declare const VECTOR_STROKE_ALIGN_LABELS: Readonly<Record<VectorStrokeAlign, string>>;
1978
+ declare const VECTOR_CORNER_KIND_LABELS: Readonly<Record<VectorCornerKind, string>>;
1979
+ declare const VECTOR_ARROWHEAD_LABELS: Readonly<Record<VectorArrowhead, string>>;
1439
1980
  /** A paint as `#rrggbb` for `<input type="color">`, or null for none, gradients and other CSS colours. */
1440
1981
  declare function vectorPaintToHex(paint: VectorPaint | undefined): string | null;
1441
1982
  /** A path-editing button of the bundled toolbars. */
1442
1983
  interface VectorPathAction {
1443
- id: 'join' | 'average' | 'cut' | 'clean' | 'smooth';
1984
+ id: 'join' | 'average' | 'cut' | 'clean' | 'smooth' | 'outline';
1444
1985
  label: string;
1445
1986
  title: string;
1446
1987
  /** Shortcut after Cmd / Ctrl, if any. */
@@ -1449,7 +1990,7 @@ interface VectorPathAction {
1449
1990
  enabled: (selected: number, points: number) => boolean;
1450
1991
  run: (controller: VectorEditorController) => unknown;
1451
1992
  }
1452
- /** Join, Average, Cut at Points, Remove Redundant Points and Smooth, as the toolbars show them. */
1993
+ /** Join, Average, Cut at Points, Remove Redundant Points, Smooth and Outline Stroke, as the toolbars show them. */
1453
1994
  declare const VECTOR_PATH_ACTIONS: readonly VectorPathAction[];
1454
1995
  /** The on / off path-editing settings the toolbars toggle. */
1455
1996
  declare const VECTOR_EDIT_TOGGLES: readonly {
@@ -1677,4 +2218,142 @@ declare function vectorInkShapeName(shape: VectorInkShape): string;
1677
2218
  */
1678
2219
  declare function recognizeInkShape(points: readonly Point[], options?: RecognizeInkOptions): VectorInkShape | null;
1679
2220
 
1680
- export { type AddVectorNodeOptions, AffineMatrix, DEFAULT_VECTOR_PATH_EDIT, DEFAULT_VECTOR_SNAP, IDENTITY_MATRIX, KAPPA, LazyMouse, type NearestSegment, type RecognizeInkOptions, SvgGradientDef, VECTOR_CLIPBOARD_TYPE, VECTOR_CONSTRUCTION_ANGLES, VECTOR_EDIT_TOGGLES, VECTOR_INK_MAX_CORNERS, VECTOR_OVERLAY_THEME, VECTOR_PATH_ACTIONS, VECTOR_PENCIL_STRING_PX, 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 VectorAverageAxis, 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 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 VectorRenderGroup, type VectorRenderGuideLabel, type VectorRenderGuideLine, type VectorRenderHandle, type VectorRenderNode, type VectorRenderPath, type VectorScaleSnap, type VectorSceneRenderData, type VectorSegmentRef, 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, bendSegment, bezierToSubpaths, boundsFromPoints, boundsIntersect, cloneAnchor, cloneSubpath, cloneVectorDocument, cloneVectorNode, collectPathNodes, constrainToLeave, convertAnchor, createVectorCommands, curvatureMoveAnchor, cutSubpathAt, decomposeRigid, ellipseSubpath, eraseFromFill, eraseFromOutline, eraserRegion, findVectorNode, getAnchor, getAnchorWorld, getEditablePathIds, getTransformHandlePositions, getVectorOverlayRenderData, getVectorSceneRenderData, getVectorSelectionBoxGeometry, getVectorSelectionFrame, getVectorSelectionStyle, getVisibleAnchors, getVisibleHandles, hasPaint, hitTestVectorNode, hitTestVectorNodes, inferAnchorType, insertAnchor, invertMatrix, isIdentityMatrix, isVectorTextTarget, joinSubpaths, knifePath, lineSubpath, locateVectorNode, matrixRotation, moveAnchorHandle, multiplyMatrices, nearestSegment, nodeMatrix, parseSvgTransform, parseVectorSvg, polygonSubpath, polylineBounds, recognizeInkShape, rectSubpath, removeAnchorKeepingShape, removeAnchors, removeAnchorsKeepingShape, removeRedundantPoints, reverseSubpath, rotateMatrix, roundSubpaths, scaleFor, scaleMatrix, simplifyPolyline, smoothSubpath, splitIntoFaces, splitSubpathAt, starSubpath, subpathPointAt, subpathSegment, subpathSegmentCount, subpathsToBezier, toVectorKeyInput, toVectorModifiers, toVectorPointerInput, transformVectorNode, translateAnchor, translateMatrix, unionBounds, vectorDocumentToSvg, vectorInkShapeName, vectorNodeBounds, vectorNodeRotation, vectorNodeToBezier, vectorPaintToHex, vectorPaintToSvg, vectorParentMatrix, vectorPathData, vectorPathWorldBezier, vectorResizeCursor, vectorToolLabel, vectorWorldMatrix, walkVectorNodes };
2221
+ /** One arrowhead: a filled outline (nonzero) painted with the stroke's paint. */
2222
+ interface VectorArrowheadShape {
2223
+ end: 'start' | 'end';
2224
+ kind: VectorArrowhead;
2225
+ tip: Vec2;
2226
+ /** Unit direction the head points in. */
2227
+ dir: Vec2;
2228
+ /** Distance from the tip back to where the stroke ends under the head. */
2229
+ inset: number;
2230
+ path: BezierPath;
2231
+ }
2232
+ /** What the stroke of a path draws. */
2233
+ interface VectorStrokeParts {
2234
+ /** Where the stroke sits (`center` for open shapes, whatever the style says). */
2235
+ align: VectorStrokeAlign;
2236
+ /** The line the stroke follows: the outline, shortened under `tip`-aligned heads. */
2237
+ line: BezierPath;
2238
+ /** The stroke width to draw `line` with: twice the style's width for inside / outside (half is clipped away). */
2239
+ drawWidth: number;
2240
+ heads: VectorArrowheadShape[];
2241
+ }
2242
+ /** True when the path has subpaths and all of them are closed (only such shapes have an inside). */
2243
+ declare function isClosedBezierShape(path: BezierPath): boolean;
2244
+ /** The alignment a stroke is drawn with: the style's, or `center` for open shapes. */
2245
+ declare function effectiveStrokeAlign(style: Pick<VectorStyle, 'strokeAlign'>, closedShape: boolean): VectorStrokeAlign;
2246
+ /** The style's width profile, sanitised, or null when the stroke is uniform. */
2247
+ declare function strokeWidthProfileOf(style: Pick<VectorStyle, 'strokeWidthProfile'>): VectorWidthPoint[] | null;
2248
+ /** True when the style asks for more than a plain centred stroke (an alignment, arrowheads or a width profile). */
2249
+ declare function hasStrokeExtras(style: VectorStyle): boolean;
2250
+ /**
2251
+ * The area of the stroke along `parts.line` before alignment clipping: SVG's stroke for a
2252
+ * uniform width, the variable-width outline (`variableStrokeOutline`) for a profiled one.
2253
+ */
2254
+ declare function vectorStrokeLineArea(parts: Pick<VectorStrokeParts, 'line' | 'drawWidth'>, style: VectorStyle, tolerance?: number): BezierPath;
2255
+ /** The weight of a head: the stroke width times its scale (at least 0.25). A head is 4 weights long and wide. */
2256
+ declare function arrowheadWeight(style: VectorStyle, end: 'start' | 'end'): number;
2257
+ /** Distance from the tip where the stroke ends, for a stroke whose cap reaches `cap` past its end. */
2258
+ declare function arrowheadInset(kind: VectorArrowhead, hw: number, cap: number): number;
2259
+ /** The outline of a `kind` head of weight `hw` with its tip at `tip`, pointing along unit `dir`. */
2260
+ declare function arrowheadPath(kind: VectorArrowhead, tip: Vec2, dir: Vec2, hw: number): BezierPath;
2261
+ /**
2262
+ * The stroke of `path` as `style` draws it: its alignment, the line it follows (shortened under
2263
+ * `tip`-aligned heads) and its arrowheads. Heads go on the start of the first subpath and the
2264
+ * end of the last one, when those are open.
2265
+ */
2266
+ declare function vectorStrokeParts(path: BezierPath, style: VectorStyle): VectorStrokeParts;
2267
+ /**
2268
+ * The area the stroke paints, as one filled path: the outlined stroke (joins, caps and dashes
2269
+ * as SVG) clipped by the alignment (against the fill, read with the style's `fillRule`), plus
2270
+ * the arrowheads.
2271
+ */
2272
+ declare function vectorStrokeOutlinePath(path: BezierPath, style: VectorStyle): BezierPath;
2273
+ /**
2274
+ * Whether point `p` hits the stroke of `path` (`tolerance` added on every side): within half
2275
+ * the width of the outline for a centred stroke, within the whole width on the inside or the
2276
+ * outside of the fill for an aligned one, or on an arrowhead.
2277
+ */
2278
+ declare function hitTestVectorStroke(path: BezierPath, p: Vec2, style: VectorStyle, tolerance?: number): boolean;
2279
+ /** How far the stroke can reach past the outline (for padding bounds and masks). */
2280
+ declare function vectorStrokeReach(style: VectorStyle): number;
2281
+
2282
+ /**
2283
+ * The outline of a variable-width stroke, as a clean filled Bézier path.
2284
+ *
2285
+ * Each subpath is flattened (finely enough for the width to vary smoothly) and the stroke is
2286
+ * the union of positively wound pieces: one trapezoid per flattened segment (its ends at the
2287
+ * local side widths), a wedge on the outer side of every bend (the style's join at the path's
2288
+ * own corners, round inside curves) and the caps. Filled with the non-zero rule the union is
2289
+ * exact whatever the profile does; it is built as the boundary those pieces add up to (see
2290
+ * `variableStrokeRings`), normalised with the polygon boolean engine, and every ring is refitted
2291
+ * with cubics (`fitCubics`) to within the tolerance — a real Bézier outline, not a polyline.
2292
+ *
2293
+ * Ported from ArtCraft effectcraft `crates/path/src/varstroke.rs` (`outline`, `dash_intervals`)
2294
+ * (MIT OR Apache-2.0, see THIRD_PARTY_NOTICES.md), with one-sided widths after figcraft
2295
+ * `crates/pathops/src/width.rs` (`variable_outline`).
2296
+ */
2297
+
2298
+ interface VariableStrokeOptions {
2299
+ /** Stroke width: a side scale of 1 reaches half of it from the centre line. */
2300
+ width: number;
2301
+ profile: readonly VectorWidthPoint[];
2302
+ /** Joins at the path's own corners (default `miter`); bends inside curves are always round. */
2303
+ join?: StrokeJoin;
2304
+ /** Default `butt`. */
2305
+ cap?: StrokeCap;
2306
+ /** Default 4 (as SVG). */
2307
+ miterLimit?: number;
2308
+ /** Dash pattern (as SVG); the profile still runs along the whole subpath. */
2309
+ dash?: readonly number[];
2310
+ dashOffset?: number;
2311
+ /** Largest distance of the result from the exact outline (default 0.05). */
2312
+ tolerance?: number;
2313
+ }
2314
+ type Vec = [number, number];
2315
+ /** Arc-length intervals that are "on" for a dash pattern on a path `len` long (effectcraft `dash_intervals`). */
2316
+ declare function dashIntervals(pattern: readonly number[], offset: number, len: number): [number, number][];
2317
+ /**
2318
+ * The rings of a variable-width stroke in the path's own space; filled with the non-zero rule
2319
+ * they are the stroke.
2320
+ *
2321
+ * effectcraft builds the stroke as a union of positively wound pieces: a trapezoid per flattened
2322
+ * segment (its ends at the local side widths), a wedge on the outer side of every bend and the
2323
+ * caps. Unioning thousands of overlapping pieces is slow, so each run is emitted as the boundary
2324
+ * those pieces add up to instead (winding numbers add, so the non-zero fill is the same): the
2325
+ * offset sides with the outer join between them, and on the inner side of a bend either the point
2326
+ * where the two offsets cross (the region cut off is covered by both trapezoids) or, when they do
2327
+ * not cross, a detour through the centre point (which keeps the winding right when segments are
2328
+ * shorter than the width). A closed subpath gives two rings, an open run one.
2329
+ */
2330
+ declare function variableStrokeRings(path: BezierPath, options: VariableStrokeOptions): Point[][];
2331
+ /**
2332
+ * The outline of a variable-width stroke of `path` (side widths from `profile`), as a clean
2333
+ * Bézier path: overlaps removed, outer rings and holes, each refitted with cubics to within
2334
+ * `tolerance` of the exact outline. Fill it with the non-zero rule (either rule works).
2335
+ */
2336
+ declare function variableStrokeOutline(path: BezierPath, options: VariableStrokeOptions): BezierPath;
2337
+ /**
2338
+ * Where point `p` lies relative to the stroke's centre line: the nearest point's contour,
2339
+ * its position `t` by length (0–1), the distance, and the side (`left` looking along the
2340
+ * path on screen) — what hit-testing and the width tool need.
2341
+ */
2342
+ declare function nearestStrokePosition(path: BezierPath, p: Point,
2343
+ /** Only this contour (subpath) of the path. */
2344
+ only?: number): {
2345
+ contour: number;
2346
+ t: number;
2347
+ distance: number;
2348
+ side: 'left' | 'right';
2349
+ point: Point;
2350
+ tangent: Vec;
2351
+ length: number;
2352
+ } | null;
2353
+ /** The point and unit tangent at position `t` (0–1 by length) of contour `index` of `path`. */
2354
+ declare function strokePositionPoint(path: BezierPath, index: number, t: number): {
2355
+ point: Point;
2356
+ tangent: Vec;
2357
+ } | null;
2358
+
2359
+ 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_PRESSURE_NOMINAL, VECTOR_ROTATE_HANDLE_OFFSET, VECTOR_STROKE_ALIGN_LABELS, VECTOR_STYLE_KEYS, VECTOR_TOOLS, VECTOR_TOOL_GLYPHS, VECTOR_TOOL_SHORTCUTS, VECTOR_TRANSFORM_HANDLES, VECTOR_WIDTH_PRESETS, VECTOR_WIDTH_PRESET_IDS, VECTOR_WIDTH_PROFILE_MAX_POINTS, VECTOR_WIDTH_PROFILE_MAX_SCALE, VECTOR_WIDTH_SAME_T, type VariableStrokeOptions, 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 VectorRenderWidthPoint, 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 VectorStrokeSpot, type VectorStyle, type VectorSubpath, type VectorSvgExportOptions, type VectorSvgImportOptions, type VectorTaperOptions, type VectorTool, type VectorTransformHandle, type VectorViewport, type VectorWaveOptions, type VectorWidthHandle, type VectorWidthPoint, type VectorWidthPointRef, type VectorWidthPreset, type VectorWidthPresetInfo, applyMatrix, applyMatrixLinear, applyVectorCornersAttribute, arrowheadInset, arrowheadPath, arrowheadWeight, bendSegment, bezierToSubpaths, boundsFromPoints, boundsIntersect, cloneAnchor, cloneSubpath, cloneVectorDocument, cloneVectorNode, collectPathNodes, constrainToLeave, convertAnchor, cornerBisectorPoint, cornerRadiusChange, cornerSetback, createVectorCommands, curvatureMoveAnchor, cutSubpathAt, dashIntervals, decomposeRigid, effectiveStrokeAlign, ellipseSubpath, eraseFromFill, eraseFromOutline, eraserRegion, expandVectorCorners, expandVectorCornersOf, findVectorNode, fitCornerRadius, generateWidthProfile, getAnchor, getAnchorWorld, getEditablePathIds, getTransformHandlePositions, getVectorCornerWidgets, getVectorOverlayRenderData, getVectorSceneRenderData, getVectorSelectionBoxGeometry, getVectorSelectionFrame, getVectorSelectionStyle, getVectorWidthHandles, getVisibleAnchors, getVisibleHandles, hasPaint, hasStrokeExtras, hasVectorCorners, hitTestVectorNode, hitTestVectorNodes, hitTestVectorStroke, inferAnchorType, insertAnchor, insertWidthPoint, invertMatrix, isClosedBezierShape, isIdentityMatrix, isUniformWidthProfile, isVectorTextTarget, joinSubpaths, knifePath, lineSubpath, locateVectorNode, matrixRotation, mirrorWidthProfile, moveAnchorHandle, multiplyMatrices, nearestSegment, nearestStrokePosition, nextVectorCornerKind, nodeMatrix, parseSvgTransform, parseVectorSvg, polygonSubpath, polylineBounds, pressureWidthProfile, recognizeInkShape, rectSubpath, removeAnchorKeepingShape, removeAnchors, removeAnchorsKeepingShape, removeRedundantPoints, removeWidthPoints, reverseSubpath, reverseWidthProfile, rotateMatrix, roundSubpaths, sanitizeWidthProfile, scaleFor, scaleMatrix, setAnchorCorner, setSubpathCorners, setWidthPoint, simplifyPolyline, smoothSubpath, splitIntoFaces, splitSubpathAt, starSubpath, strokePositionPoint, strokeWidthProfileOf, subpathPointAt, subpathSegment, subpathSegmentCount, subpathsToBezier, taperWaveFactor, toVectorKeyInput, toVectorModifiers, toVectorPointerInput, transformVectorNode, translateAnchor, translateMatrix, unionBounds, variableStrokeOutline, variableStrokeRings, vectorCornerAt, vectorCorners, vectorCornersAttribute, vectorDiamondPath, vectorDocumentToSvg, vectorInkShapeName, vectorNodeBounds, vectorNodeRotation, vectorNodeToBezier, vectorPaintToHex, vectorPaintToSvg, vectorParentMatrix, vectorPathData, vectorPathWorldBezier, vectorResizeCursor, vectorStrokeLineArea, vectorStrokeOutline, vectorStrokeOutlinePath, vectorStrokeParts, vectorStrokeReach, vectorStrokeSvgParts, vectorStrokeWidthGeometry, vectorToolLabel, vectorWorldMatrix, walkVectorNodes, widthProfileAt, widthProfileFromAttribute, widthProfileMaxScale, widthProfilePreset, widthProfilePresetId, widthProfileToAttribute };