@weasel-js/path-tools 0.0.0-stage → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1550 @@
1
+ import { PathEditExtension, SnapSource, FeaturePreset, EditableShape, BlendEffect, BendAlongEffect, RepeatAlongEffect, PathNode, Tool, SurfaceContribution, NodeId, PathPaint, BooleanOpResult, BooleansAdapter, BooleanOpOptions, BooleanStrokes, RenderLayer, SceneNode, PathRepresentation, AnchorMarkerShape, EditAnchorsDep, RectPose, AddNodeSpec } from '@weasel-js/core';
2
+ import { DrawCommand } from '@weasel-js/renderer';
3
+ import { AnchorSet, Point, PolygonPath, View, CornerFrame, SoftFalloff, PathConstraintKind, PathConstraint, LengthenMode, Affine, SpiroKind, SegmentType, CornerShape, TrimResult, SpiroPoint, SpiroContour, CornerKeyword } from '@weasel-js/geom';
4
+ import * as _weasel_js_routing from '@weasel-js/routing';
5
+ import { Action, ActionDeps, ToolKeybinding } from '@weasel-js/routing';
6
+ export { ActionParamSchema } from '@weasel-js/routing';
7
+ import { CursorSpec } from '@weasel-js/cursor';
8
+ import { PathWarp, DistortMode, EnvelopeMapping } from '@weasel-js/geom/warp';
9
+ import { VertexWidth, Stroke, HalfWidths } from '@weasel-js/paint';
10
+ import { StrokeEdgeHit, StrokeSide } from '@weasel-js/stroke';
11
+ import { PrefGroup } from '@weasel-js/prefs';
12
+
13
+ /**
14
+ * The balance lines path edit can draw over the open path: one per curved
15
+ * segment, from its first handle's tip to its second's, with the grip
16
+ * `balanceHandles` drags at the middle, pressed as `balance:N`.
17
+ */
18
+
19
+ /** Chrome-caps id the balance lines and their grips are drawn and hit-tested
20
+ * under. Off by default. */
21
+ declare const PATH_BALANCE_CHROME_ID = "path-edit.balance";
22
+ /** How the balance lines are drawn. */
23
+ interface PathEditBalanceStyle {
24
+ /** Color of the line between a segment's handle tips and of its grip's rim. */
25
+ color?: string;
26
+ /** Fill of the grip. */
27
+ gripFill?: string;
28
+ /** Radius of the grip, in screen px. Default 4. */
29
+ gripRadiusPx?: number;
30
+ }
31
+ /** Draw commands for `set`'s balance lines and their grips, in screen space. */
32
+ declare function balanceCommands(set: AnchorSet, toScreen: (p: Point) => Point, style?: PathEditBalanceStyle): DrawCommand[];
33
+ /** The balance lines as a path-edit extension: drawn, and their grips
34
+ * pressed, while chrome-caps shows {@link PATH_BALANCE_CHROME_ID}. */
35
+ declare function createBalanceExtension(style?: PathEditBalanceStyle): PathEditExtension;
36
+
37
+ /**
38
+ * The curvature comb path edit can draw over the open path.
39
+ */
40
+
41
+ /** Chrome-caps id the curvature comb is drawn under. Off by default. */
42
+ declare const PATH_COMB_CHROME_ID = "path-edit.comb";
43
+ /** How the curvature comb is drawn. */
44
+ interface PathEditCombOptions {
45
+ /** Tooth length per unit of curvature, in world units: a tooth over a
46
+ * stretch of radius r is `scale / r` long. Default 2000. */
47
+ scale?: number;
48
+ /** Teeth per segment, ends included. Default 24. */
49
+ samplesPerSegment?: number;
50
+ /** Color of the teeth and of the line through their tips. */
51
+ color?: string;
52
+ }
53
+ /** Draw commands for `set`'s curvature comb, in screen space. */
54
+ declare function combCommands(set: AnchorSet, toScreen: (p: Point) => Point, opts?: PathEditCombOptions): DrawCommand[];
55
+ /** The comb as a path-edit extension, drawn while chrome-caps shows
56
+ * {@link PATH_COMB_CHROME_ID}. `options` is read every frame. */
57
+ declare function createCombExtension(options?: () => PathEditCombOptions | undefined): PathEditExtension;
58
+
59
+ /**
60
+ * The corner widgets path edit shows on the open path: one inside each corner
61
+ * anchor, at the middle of its cut, pushed a fixed distance further in on
62
+ * screen so that it clears the anchor square at a zero radius. The overlay
63
+ * paints it and the hit-test presses it, as `corner:N`, from this one
64
+ * placement.
65
+ */
66
+
67
+ /** Chrome-caps id the corner widgets are drawn and hit-tested under. On by
68
+ * default. */
69
+ declare const PATH_CORNERS_CHROME_ID = "path-edit.corners";
70
+ /** Screen distance from the middle of a corner's cut to its widget. */
71
+ declare const CORNER_WIDGET_STANDOFF_PX = 14;
72
+ /** The corner anchors of `path` that show a widget. */
73
+ declare function cornerWidgetFrames(path: PolygonPath): CornerFrame[];
74
+ /** How the widgets are drawn. */
75
+ interface CornerWidgetStyle {
76
+ /** Default: path edit's anchor fill. */
77
+ fill?: string;
78
+ /** Default: path edit's anchor stroke. */
79
+ stroke?: string;
80
+ }
81
+ /**
82
+ * The widget of every corner anchor of `path`, in screen space: a small
83
+ * square whose corner toward the anchor is cut in the corner's shape. A
84
+ * corner with no spec draws as round, the shape a drag gives it.
85
+ */
86
+ declare function cornerWidgetCommands(path: PolygonPath, view: View, style?: CornerWidgetStyle): DrawCommand[];
87
+ /** The corner widgets as a path-edit extension: drawn, and pressed as
88
+ * `corner:N`, while chrome-caps shows {@link PATH_CORNERS_CHROME_ID}. */
89
+ declare function createCornerWidgetsExtension(style?: CornerWidgetStyle): PathEditExtension;
90
+
91
+ /**
92
+ * The small marks the path-edit overlay draws for a path's constraints, in
93
+ * screen pixels, and where a click finds them: a letter beside a horizontal
94
+ * or vertical segment, paired strokes beside parallel ones, a foot beside
95
+ * perpendicular ones, ticks across equal ones, a dimension bar beside a fixed
96
+ * length, a pin by a fixed anchor, a ring by coincident ones, a line
97
+ * touching a circle by a tangent one, and a dot on a line by one held on a
98
+ * segment. Marks sit
99
+ * on the inside of the shape, toward the centroid of its anchors, so a click
100
+ * on one lands on the shape rather than on empty canvas.
101
+ */
102
+
103
+ /** Colors for the constraint marks. */
104
+ interface ConstraintGlyphStyle {
105
+ /** A constraint that holds. */
106
+ stroke: string;
107
+ /** One that does not: it conflicts with the others. */
108
+ conflictStroke: string;
109
+ }
110
+
111
+ /** Angle snapping for a control-handle drag. */
112
+ interface HandleSnapOptions {
113
+ /** Snap to every multiple of this many degrees. Default 45; 0 turns the
114
+ * steps off and leaves only the opposite handle. */
115
+ stepDeg?: number;
116
+ /** How near, in degrees, the handle must come to an angle to snap. Read
117
+ * by the drag as it starts. Default 6. */
118
+ reachDeg?: number;
119
+ /** Also snap onto the line through the anchor's other handle. Default true. */
120
+ opposite?: boolean;
121
+ }
122
+ /** Options, or a thunk read on every move: null or undefined from it leaves
123
+ * the handle unsnapped. */
124
+ type HandleSnapOptionsInput = HandleSnapOptions | (() => HandleSnapOptions | null | undefined);
125
+ /**
126
+ * A handle drag's angle targets: the multiples of `stepDeg` either side of
127
+ * the handle, and the angle that puts it in line with the anchor's other
128
+ * handle, which the drag brings as `also.anchors`, ahead of a step it ties
129
+ * with. Each draws its line.
130
+ */
131
+ declare function handleSnapSource(options?: HandleSnapOptionsInput): SnapSource;
132
+ /**
133
+ * Handle snapping as a path-edit extension: each control-handle drag runs a
134
+ * `'handle'` session on the canvas's `snap` dep, in polar coordinates about
135
+ * its anchor, and the handle keeps the length the pointer and the locks give
136
+ * it. A held angle lock holds the angle; a held length lock keeps the length
137
+ * while the angle snaps. The angles come from {@link handleSnapSource}, which
138
+ * `createHandleSnapPreset` registers beside it.
139
+ */
140
+ declare function createHandleSnapExtension(options?: HandleSnapOptionsInput): PathEditExtension;
141
+
142
+ /** How far soft selection reaches, and how it fades. */
143
+ interface SoftSelectionOptions {
144
+ /** How far along the path the influence reaches, in screen pixels. */
145
+ radiusPx: number;
146
+ /** How the influence fades over the radius. Default `'smooth'`. */
147
+ falloff?: SoftFalloff;
148
+ /** Told each radius the wheel scrubs to, so a host can keep it for the next drag. */
149
+ onRadius?: (radiusPx: number) => void;
150
+ }
151
+ /** Soft selection as a path-edit extension. `options` is read as each anchor
152
+ * drag starts; null or undefined leaves that drag hard. */
153
+ declare function createSoftSelectionExtension(options: () => SoftSelectionOptions | null | undefined): PathEditExtension;
154
+
155
+ /**
156
+ * Which way the open path runs, drawn over it: an arrow at the middle of
157
+ * every segment and a ring round each subpath's start anchor.
158
+ */
159
+
160
+ /** Chrome-caps id the direction arrows and start rings are drawn under. Off by default. */
161
+ declare const PATH_DIRECTION_CHROME_ID = "path-edit.direction";
162
+ /** How the direction marks are drawn. */
163
+ interface PathDirectionStyle {
164
+ /** Color of the arrows and the start rings. */
165
+ color?: string;
166
+ /** Length of an arrow, tip to base, in screen pixels. */
167
+ arrowPx?: number;
168
+ /** Radius of a start ring, in screen pixels. */
169
+ ringPx?: number;
170
+ }
171
+ /** Draw commands for `set`'s direction arrows and start rings, in screen space. */
172
+ declare function directionCommands(set: AnchorSet, toScreen: (p: Point) => Point, style?: PathDirectionStyle): DrawCommand[];
173
+ /** The direction marks as a path-edit extension, drawn in `style` while
174
+ * chrome-caps shows {@link PATH_DIRECTION_CHROME_ID}. */
175
+ declare function createDirectionExtension(style?: PathDirectionStyle): PathEditExtension;
176
+
177
+ /**
178
+ * The package's behavior as `<SceneCanvas features>` presets: one per tool
179
+ * family, and `pathTools` for all of them. Each assumes core's `paths`
180
+ * preset, which opens a path for editing.
181
+ */
182
+
183
+ /** The Simplify brush, a path-edit tool that refits the curve it passes over. */
184
+ declare const simplifyBrushPreset: FeaturePreset;
185
+ /** The `handleLock.angle` and `handleLock.length` toggles. */
186
+ declare const handleLocksPreset: FeaturePreset;
187
+ /**
188
+ * Soft selection: dragging anchors in path edit also moves the anchors near
189
+ * them along the path, by a falloff over `options`' radius, read as each drag
190
+ * starts. The wheel scrubs the radius mid-drag. Null or undefined from
191
+ * `options` leaves a drag hard.
192
+ */
193
+ declare function createSoftSelectionPreset(options: () => SoftSelectionOptions | null | undefined): FeaturePreset;
194
+ /**
195
+ * Handle snapping: a dragged control handle's angle snaps to steps and to the
196
+ * line through its anchor's other handle, as `options` says. A thunk is read
197
+ * every move, and null or undefined from it leaves the handle unsnapped.
198
+ */
199
+ declare function createHandleSnapPreset(options?: HandleSnapOptionsInput): FeaturePreset;
200
+ /** Handle snapping every 45°, within 6°, and to the opposite handle. */
201
+ declare const handleSnapPreset: FeaturePreset;
202
+ /** Bridge, on whole paths and on two selected anchors. */
203
+ declare const bridgePreset: FeaturePreset;
204
+ /** `trimPath`, which `useTrimTool` binds. */
205
+ declare const trimPreset: FeaturePreset;
206
+ /** `extendToMeet`, which `useExtendTool` binds. */
207
+ declare const extendPreset: FeaturePreset;
208
+ /** Lengthen, a path-edit tool whose drag on an open end grows or trims the
209
+ * path from that end, and its toggle. */
210
+ declare const lengthenPreset: FeaturePreset;
211
+ /** `smudgeBrush`, which `useSmudgeBrushTool` binds. */
212
+ declare const smudgePreset: FeaturePreset;
213
+ /** Harmonize curvature. */
214
+ declare const harmonizePreset: FeaturePreset;
215
+ /** Anchor selection along the path: grow, shrink, every Nth, corners, smooth
216
+ * anchors, and the complement. */
217
+ declare const anchorSelectionPreset: FeaturePreset;
218
+ /** Resample, which spreads anchors evenly by arc length. */
219
+ declare const resamplePreset: FeaturePreset;
220
+ /** Add extremes, an anchor at each curve's leftmost, rightmost, top, and bottom. */
221
+ declare const addExtremesPreset: FeaturePreset;
222
+ /** The curvature comb over the open path, drawn as `options` says, read every
223
+ * frame. Off until chrome-caps shows `path-edit.comb`. */
224
+ declare function createCurvatureCombPreset(options?: () => PathEditCombOptions | undefined): FeaturePreset;
225
+ /** The curvature comb with its default look. */
226
+ declare const curvatureCombPreset: FeaturePreset;
227
+ /**
228
+ * Balance handles: a line between each curved segment's handle tips with a
229
+ * grip to drag (`balanceHandles`) or double-click (`equalizeHandles`), drawn
230
+ * in `style`. Off until chrome-caps shows `path-edit.balance`. It refines
231
+ * `paths`, so a grip's double-click goes ahead of entering path edit.
232
+ */
233
+ declare function createBalancePreset(style?: PathEditBalanceStyle): FeaturePreset;
234
+ /** Balance handles with their default look. */
235
+ declare const balancePreset: FeaturePreset;
236
+ /** Path constraints: their actions, their marks in `style`'s colors, and
237
+ * every edit of a constrained path finished so they hold. */
238
+ declare function createConstraintsPreset(style?: Partial<ConstraintGlyphStyle>): FeaturePreset;
239
+ /** Path constraints with their default colors. */
240
+ declare const constraintsPreset: FeaturePreset;
241
+ /**
242
+ * Corner shape: a widget inside each corner anchor of the open path, drawn in
243
+ * `style`, whose drag sets the corner's radius (`editCorner`), and the
244
+ * commands on the selected corners (`setCorners`, `cycleCornerShape`). The
245
+ * widgets show while chrome-caps shows `path-edit.corners`, which is on by
246
+ * default. It refines `paths`, so a widget's drag goes ahead of the marquee.
247
+ */
248
+ declare function createCornerShapePreset(style?: CornerWidgetStyle): FeaturePreset;
249
+ /** Corner shape with path edit's anchor colors. */
250
+ declare const cornerShapePreset: FeaturePreset;
251
+ /**
252
+ * Where a path starts and which way it runs: `setPathStart` and
253
+ * `reversePath`, and arrows and start rings over the open path, drawn in
254
+ * `style`. The marks are off until chrome-caps shows `path-edit.direction`.
255
+ */
256
+ declare function createPathStructurePreset(style?: PathDirectionStyle): FeaturePreset;
257
+ /** Path start and direction with the marks' default look. */
258
+ declare const pathStructurePreset: FeaturePreset;
259
+ /** `convertSegments` and `convertSegmentAt`, turning segments straight or
260
+ * curved. It refines `paths`, so a segment's Shift+double-click goes ahead
261
+ * of entering path edit. */
262
+ declare const segmentTypePreset: FeaturePreset;
263
+ /** Blend, Bend along, and Repeat along, live, and the actions that edit and
264
+ * expand them. The scene must hold `withPathEffectsRegistry`'s derivations. */
265
+ declare const pathEffectsPreset: FeaturePreset;
266
+ /** `expandWarp` and `configureWarp`; the warp tools bring their own drags. */
267
+ declare const warpPreset: FeaturePreset;
268
+ /** Every preset above, spiro curves, the comb, balance, constraints, path
269
+ * direction, and corner shape with their defaults — except soft selection and
270
+ * handle snapping, which change what every anchor or handle drag does. */
271
+ declare const pathTools: FeaturePreset;
272
+
273
+ /**
274
+ * Add and remove geometric constraints on the anchors selected in path edit.
275
+ * Neither has a default binding: a consumer binds each kind it offers, with
276
+ * the kind as a param (see `<SceneCanvas tune>`).
277
+ */
278
+
279
+ /** Params for `constrainAnchors`. */
280
+ interface ConstrainAnchorsParams {
281
+ kind: PathConstraintKind;
282
+ /** For `length`: the length to hold. Default: each pair's length now. */
283
+ length?: number;
284
+ }
285
+ /**
286
+ * The constraints of `kind` that `anchors`, in the order they were selected,
287
+ * ask for: each anchor for `fixed`; each anchor with both handles for
288
+ * `tangent`; for `onPath`, three anchors, the first held on the segment the
289
+ * other two end; each consecutive pair for `horizontal`,
290
+ * `vertical`, `length`, and `coincident`; and for the kinds relating two
291
+ * segments, three anchors as two segments sharing the middle one or four as
292
+ * two pairs. Empty when the anchors cannot make that kind.
293
+ */
294
+ declare function constraintsForAnchors(kind: PathConstraintKind, anchors: readonly number[], path: PolygonPath, length?: number): PathConstraint[];
295
+ /**
296
+ * Delete the constraint selected in path edit, as a command. Delete and
297
+ * Backspace reach it through `deleteAnchors`, which deletes whichever of the
298
+ * two is selected, so the keys have one binding.
299
+ */
300
+ declare const deleteConstraintAction: Action & {
301
+ requires: string[];
302
+ };
303
+ /**
304
+ * Constrain the selected anchors: params `{ kind, length? }`, read as
305
+ * {@link constraintsForAnchors} reads them. The path moves the least that
306
+ * makes every constraint hold; a constraint that conflicts with the rest is
307
+ * kept all the same, left unsolved, and shown as a conflict.
308
+ */
309
+ declare const constrainAnchorsAction: Action & {
310
+ requires: string[];
311
+ };
312
+ /**
313
+ * Remove the constraints whose anchors are all selected — only those of
314
+ * `kind` when the params name one.
315
+ */
316
+ declare const unconstrainAnchorsAction: Action & {
317
+ requires: string[];
318
+ };
319
+
320
+ /** The path-edit tool under which a drag on an open end lengthens the path. */
321
+ declare const LENGTHEN_TOOL = "lengthen";
322
+ /** Params `lengthenPath` reads on every move. */
323
+ interface LengthenPathParams {
324
+ /** How an unmodified drag grows the end; Shift picks the other. Default `'tangent'`. */
325
+ mode?: LengthenMode;
326
+ }
327
+ /**
328
+ * Lengthen, the path-edit tool's drag. While path edit's `lengthen` tool is
329
+ * on, dragging an open path's end anchor lengthens the path from that end
330
+ * (`lengthenPathEnd`): straight along its end tangent, or along an arc with
331
+ * the curvature the end has. Dragging back into the path shortens it along
332
+ * itself. The `mode` param (`'tangent'`, the default, or `'curvature'`) picks
333
+ * which, and Shift picks the other; both are read on every move. One undo
334
+ * step per drag. With the tool off, a drag on the anchor is `editAnchors`'
335
+ * and moves it.
336
+ */
337
+ declare const lengthenPathAction: Action & {
338
+ requires: string[];
339
+ };
340
+ /** The bar toggle for Lengthen: `pathEditTool.lengthen`. */
341
+ declare const lengthenToolAction: Action & {
342
+ requires: string[];
343
+ };
344
+
345
+ /** What the `resamplePath` action reads from its params. */
346
+ type ResamplePathParams = {
347
+ /** Anchors in the result. 0 or absent keeps the count the path has. */
348
+ count?: number;
349
+ /** Distance between anchors along the path; overrides `count` when above 0. */
350
+ spacing?: number;
351
+ /** Hold corner anchors where they are. Default `true`. */
352
+ keepCorners?: boolean;
353
+ /** How far the result may stray from the original. Default 0.5. */
354
+ tolerance?: number;
355
+ };
356
+ /**
357
+ * Redistribute each selected path's anchors evenly by arc length
358
+ * (`resamplePath`), to `params.count` anchors or at `params.spacing`, with
359
+ * corners held unless `params.keepCorners` is false, and curves refitted
360
+ * within `params.tolerance` of the original ({@link ResamplePathParams}).
361
+ * Ongoing: `begin` previews it, each update with new params recomputes it,
362
+ * and committing writes one undo entry.
363
+ */
364
+ declare const resamplePathAction: _weasel_js_routing.Action & {
365
+ requires: string[];
366
+ };
367
+
368
+ /** The path-edit tool under which a plain drag brushes. */
369
+ declare const SIMPLIFY_BRUSH_TOOL = "simplifyBrush";
370
+ /** The brush's radius on screen when no param names one. */
371
+ declare const SIMPLIFY_BRUSH_RADIUS_PX = 12;
372
+ /** Params the brush reads on every move, so a thunk can change them mid-stroke. */
373
+ interface SimplifyBrushParams {
374
+ /** Brush radius in screen pixels. */
375
+ radiusPx?: number;
376
+ /** How far the refit may stray from the original, in world units, as for
377
+ * the Simplify command. */
378
+ tolerance?: number;
379
+ }
380
+ /** The kit's brush-ring cursor drawn `radiusPx` in radius on screen. */
381
+ declare function brushRingCursor(radiusPx: number): CursorSpec;
382
+ /**
383
+ * The Simplify command, brushed on. While path edit's
384
+ * `simplifyBrush` tool is on, a plain drag gathers every anchor a round brush
385
+ * passes over, and each unbroken stretch of them along a subpath is refitted
386
+ * as one span by Simplify's own fitter (`simplifyAnchorsAt`) at `tolerance`.
387
+ * Only anchors the shape does not need go, so brushing again goes further.
388
+ * The path previews the result while the brush is down and commits as one
389
+ * undo step on release.
390
+ */
391
+ declare const simplifyBrushAction: Action & {
392
+ requires: string[];
393
+ };
394
+ /** The bar toggle for the brush: `pathEditTool.simplifyBrush`. */
395
+ declare const simplifyBrushToolAction: Action & {
396
+ requires: string[];
397
+ };
398
+
399
+ /**
400
+ * The Smudge brush's drag: the selected paths' outlines under a round brush
401
+ * follow the pointer, as one undo entry per stroke.
402
+ */
403
+
404
+ /** The brush's radius in screen px when no param gives one. */
405
+ declare const DEFAULT_SMUDGE_RADIUS_PX = 40;
406
+ /** How much of the pointer's motion the outline under the brush's center follows,
407
+ * when no param gives it. */
408
+ declare const DEFAULT_SMUDGE_STRENGTH = 0.6;
409
+ /** The params `smudgeBrush` reads, each on every pointer move. */
410
+ interface SmudgeBrushParams {
411
+ /** The brush's radius, in screen px. */
412
+ radiusPx?: number;
413
+ /** `1` moves the outline under the brush's center as far as the pointer; less, less. */
414
+ strength?: number;
415
+ }
416
+ /**
417
+ * A drag that pushes the outline of every selected path along with
418
+ * the pointer (`smudgePath` in `@weasel-js/geom`). Each pointer move is one dab from
419
+ * where the last one ended: points under the brush move with it, most at its center
420
+ * and not at all at its rim, and a long segment the brush meets gets anchors added
421
+ * so it can bend there. `params.radiusPx` is the brush's radius on screen, so it
422
+ * covers the same part of the canvas at every zoom, and `params.strength` how closely
423
+ * the outline follows; both are read on every move. The cursor is a ring the size of
424
+ * the brush. The stroke commits as one undo entry; canceling it leaves the paths as
425
+ * they were.
426
+ */
427
+ declare const smudgeBrushAction: Action & {
428
+ requires: string[];
429
+ };
430
+
431
+ /**
432
+ * A warp a node is holding: the path it warped, the warp, and the tolerance
433
+ * the result was refitted to, kept on the node as `data.warp` beside the
434
+ * `data.path` (or polygon pose) they produced. The geometry stays what every
435
+ * reader uses; the warp only says how to make it again, so it can be edited
436
+ * until it is expanded away.
437
+ *
438
+ * The warp lives in its own frame, fixed when it was made. `frame` carries
439
+ * that frame onto the stored path, so moving, resizing or rotating the node
440
+ * keeps the warp without touching it. A writer that changes the geometry some
441
+ * other way leaves a warp that no longer reproduces it, and such a warp is
442
+ * ignored, the way a stale shape generator is.
443
+ */
444
+
445
+ /** What `data.warp` holds. */
446
+ interface HeldPathWarp {
447
+ /** Shared by every node warped together, so one handle drives them all. */
448
+ id: string;
449
+ /** The path before warping, in the warp's frame. */
450
+ source: PolygonPath;
451
+ warp: PathWarp;
452
+ /** How far the refitted result may stray from the exact warp. */
453
+ tolerance: number;
454
+ /** From the warp's frame to the frame the node stores its path in. */
455
+ frame: Affine;
456
+ }
457
+ /** A held warp that still reproduces its node, and where its frame lands in
458
+ * world space now. */
459
+ interface ResolvedPathWarp {
460
+ held: HeldPathWarp;
461
+ /** From the warp's frame to world space. */
462
+ toWorld: Affine;
463
+ }
464
+ /** The warp a node's `data` holds, whether or not it still matches. */
465
+ declare function heldPathWarpOf(data: unknown): HeldPathWarp | null;
466
+ /** `data` without its warp: the shape as plain geometry. */
467
+ declare function withoutPathWarp<T>(data: T): T;
468
+ /**
469
+ * The warp `node` holds and its frame in world space, or null when it holds
470
+ * none or the one it holds no longer reproduces its geometry. A node whose
471
+ * stored path was rewritten by an affine map since — a polygon pose moved,
472
+ * resized or rotated, which rewrites its coordinates — still resolves: the
473
+ * map is recovered from the two sets of points and replaces `frame`.
474
+ */
475
+ declare function resolvePathWarp(node: EditableShape | null | undefined): ResolvedPathWarp | null;
476
+
477
+ /**
478
+ * What every warp action shares: finding the selected paths' warps of one
479
+ * kind, making fresh ones where a path holds none, and writing a changed warp
480
+ * back as the geometry it makes plus the warp itself.
481
+ */
482
+
483
+ /** Which warp a tool or action works with. */
484
+ type WarpKind = PathWarp['kind'];
485
+ /** The settings a warp is made or remade with. Each applies only to the kinds
486
+ * it names; absent ones leave a held warp as it is. */
487
+ interface WarpSettings {
488
+ /** A distort's map. Default `'free'`. */
489
+ mode?: DistortMode;
490
+ /** An envelope's interpolation. Default `'bicubic'`. */
491
+ mapping?: EnvelopeMapping;
492
+ /** An envelope's patch rows and columns. Default 3 each. */
493
+ rows?: number;
494
+ cols?: number;
495
+ /** How far, in world units, the refitted result may stray from the exact
496
+ * warp. Default {@link DEFAULT_WARP_TOLERANCE}. */
497
+ tolerance?: number;
498
+ }
499
+ /** The refit tolerance of a warp made without one. */
500
+ declare const DEFAULT_WARP_TOLERANCE = 0.5;
501
+ /** The patch rows and columns of an envelope made without them. */
502
+ declare const DEFAULT_ENVELOPE_SIZE = 3;
503
+ /**
504
+ * `shape` (a node's pose and data) warped by `warp`, whose controls are in
505
+ * world space, and holding it — for a scene that opens on a warp already
506
+ * made. Null when `shape` holds no editable polygon.
507
+ */
508
+ declare function withPathWarp<S extends {
509
+ pose: unknown;
510
+ data: unknown;
511
+ }>(shape: S, warp: PathWarp, opts?: {
512
+ id?: string;
513
+ tolerance?: number;
514
+ }): {
515
+ pose: unknown;
516
+ data: unknown;
517
+ } | null;
518
+
519
+ /**
520
+ * The params this package's actions read, merged into routing's
521
+ * `ActionParamSchema` so `<SceneCanvas tune>` types them.
522
+ */
523
+
524
+ /** A path effect's settings as params; a blend's `reverse` also takes
525
+ * `'auto'`, handing the direction back to `autoAlign`. */
526
+ type SettingsOf<E> = {
527
+ [K in Exclude<keyof E, 'kind'>]?: E[K];
528
+ };
529
+ type BlendParams = Omit<SettingsOf<BlendEffect>, 'reverse'> & {
530
+ reverse?: boolean | 'auto';
531
+ };
532
+ /** Either field left out keeps that part of each selected corner. */
533
+ type SetCornersParams = {
534
+ shape: CornerShape;
535
+ radius: number;
536
+ };
537
+ type EditPathEffectParams = BlendParams & SettingsOf<BendAlongEffect> & SettingsOf<RepeatAlongEffect> & {
538
+ ids?: readonly string[];
539
+ };
540
+ declare module '@weasel-js/routing' {
541
+ interface ActionParamSchema {
542
+ blend: BlendParams;
543
+ bendAlong: SettingsOf<BendAlongEffect>;
544
+ repeatAlong: SettingsOf<RepeatAlongEffect>;
545
+ editPathEffect: EditPathEffectParams;
546
+ simplifyBrush: SimplifyBrushParams;
547
+ smudgeBrush: SmudgeBrushParams;
548
+ lengthenPath: LengthenPathParams;
549
+ bridgePaths: {
550
+ tension?: number;
551
+ };
552
+ bridgeAnchors: {
553
+ tension?: number;
554
+ };
555
+ setSpiroKind: {
556
+ kind?: SpiroKind;
557
+ };
558
+ 'spiro.placePoint': {
559
+ kind?: SpiroKind;
560
+ };
561
+ constrainAnchors: ConstrainAnchorsParams;
562
+ unconstrainAnchors: {
563
+ kind?: PathConstraintKind;
564
+ };
565
+ 'distort.drag': WarpSettings;
566
+ 'envelope.drag': WarpSettings;
567
+ 'puppet.drag': WarpSettings;
568
+ 'puppet.addPin': WarpSettings;
569
+ configureWarp: WarpSettings & {
570
+ kind?: WarpKind;
571
+ };
572
+ convertSegments: {
573
+ to?: SegmentType;
574
+ };
575
+ convertSegmentAt: {
576
+ to?: SegmentType;
577
+ };
578
+ selectEveryNthAnchor: {
579
+ n?: number;
580
+ };
581
+ resamplePath: ResamplePathParams;
582
+ setCorners: SetCornersParams;
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Toggles for the locks every path-edit handle drag holds without a
588
+ * modifier: its angle, its length, or both, which freezes it. They set the
589
+ * `editAnchors` dep's `handleLocks`, so a bar, the command palette, and
590
+ * `<SceneCanvas tune>`'s `handleLock` param, which they start from, all
591
+ * reach the one value the drag reads.
592
+ */
593
+
594
+ /** Hold every handle drag to the handle's angle, as Shift does. */
595
+ declare const handleLockAngleAction: Action & {
596
+ requires: string[];
597
+ };
598
+ /** Hold every handle drag to the handle's length, as Mod (Cmd or Ctrl) does. */
599
+ declare const handleLockLengthAction: Action & {
600
+ requires: string[];
601
+ };
602
+
603
+ /**
604
+ * Bridge: join open path ends with a curve that leaves each end along the
605
+ * direction its path was heading, on whole paths or on the two anchors
606
+ * selected in path edit.
607
+ */
608
+
609
+ /** How long Bridge makes each handle, as a fraction of the gap, when its
610
+ * params name no `tension`. */
611
+ declare const DEFAULT_BRIDGE_TENSION = 0.4;
612
+ /** A bridge's `params.tension`, or the default. Negative reads as `0`. */
613
+ declare function bridgeTensionOf(params: Record<string, unknown> | undefined): number;
614
+ /**
615
+ * Bridge open paths: as `joinPaths`, but the gap is spanned by
616
+ * a curve leaving each end along the direction its path was heading, each
617
+ * handle `params.tension` times the gap long (default
618
+ * {@link DEFAULT_BRIDGE_TENSION}). Two paths become one. In path edit,
619
+ * `bridgeAnchors` bridges the two selected anchors instead.
620
+ */
621
+ declare const bridgePathsAction: Action & {
622
+ requires: string[];
623
+ };
624
+ /**
625
+ * Bridge the two selected anchors, each the end of an open
626
+ * subpath, as `joinAnchors` does, but with a curve leaving each end along the
627
+ * direction its path was heading, each handle `params.tension` times the gap
628
+ * long (`bridgePaths`'s default when absent). The anchor selection is
629
+ * cleared.
630
+ */
631
+ declare const bridgeAnchorsAction: Action & {
632
+ requires: string[];
633
+ };
634
+
635
+ /** Trim segment: cut a stroke back to the crossings on either side of a click. */
636
+
637
+ /** What a trim at the press in `params` would do: the stroke it reaches, and
638
+ * what `trimPathAt` makes of it. Null when it reaches none it can trim.
639
+ * `params` as {@link trimPathAction} reads them; a hover's pointer serves
640
+ * as the press, which is how a tool previews the piece. */
641
+ declare function planTrim(deps: ActionDeps, params: unknown): {
642
+ target: PathNode;
643
+ result: TrimResult;
644
+ } | null;
645
+ /**
646
+ * Trim segment: remove the piece of the stroke under the press
647
+ * between the nearest crossings on either side — with any other node's
648
+ * outline, or with itself — as `trimPathAt` cuts it. The topmost stroke
649
+ * within `radiusPx` of the press (default 6) is the one trimmed; a stroke
650
+ * with nothing left is deleted. Params: `pressX`, `pressY` in world space,
651
+ * which a click binding supplies.
652
+ */
653
+ declare const trimPathAction: Action & {
654
+ requires: string[];
655
+ };
656
+
657
+ /** Extend to next path: carry an open end along its tangent until it meets a stroke. */
658
+
659
+ /**
660
+ * Extend to next path: carry each open end of the selected
661
+ * paths along its tangent to the first stroke it meets (`extendToMeet`). In
662
+ * path edit, the selected end anchors of the edited path, or all its open
663
+ * ends when none is selected. Given a press (`pressX`, `pressY`, as a click
664
+ * binding supplies), the open end nearest it of the topmost stroke within
665
+ * `radiusPx` instead. An end that meets nothing stays put. One undo step.
666
+ */
667
+ declare const extendToMeetAction: Action & {
668
+ requires: string[];
669
+ };
670
+
671
+ /**
672
+ * Make the selected smooth anchors curvature-continuous by adjusting their
673
+ * handle lengths, or every smooth anchor of the path while none is selected.
674
+ */
675
+ declare const harmonizeAnchorsAction: _weasel_js_routing.Action & {
676
+ requires: string[];
677
+ };
678
+
679
+ /**
680
+ * Where a path starts and which way it runs: make the selected anchor its
681
+ * subpath's start, and reverse a path's direction. Both reorder anchors
682
+ * without moving them, and what a node keeps per anchor moves with them.
683
+ */
684
+
685
+ /**
686
+ * Make the selected anchor where its subpath starts. A closed
687
+ * subpath turns to start there; an open one can start only at an end, so its
688
+ * last anchor reverses it (`setSubpathStart`). The anchor stays selected.
689
+ */
690
+ declare const setPathStartAction: Action & {
691
+ requires: string[];
692
+ };
693
+ /**
694
+ * Run paths the other way, keeping their shape. In path edit it
695
+ * reverses the subpaths holding the selected anchors — every subpath when
696
+ * none is selected — and the selection follows its anchors. Otherwise it
697
+ * reverses every selected path whole, in one undo entry. A closed subpath
698
+ * keeps its start; an open one starts where it ended (`reverseSubpaths`).
699
+ */
700
+ declare const reversePathAction: Action & {
701
+ requires: string[];
702
+ };
703
+
704
+ /**
705
+ * Make segments of the path being edited straight or curved, in place
706
+ * (`convertSegments`): the selected segments from the keyboard, or the one
707
+ * under a Shift+double-click.
708
+ */
709
+
710
+ /**
711
+ * Convert the selected segments — those whose two ends are both
712
+ * selected anchors. `params.to` `'line'` straightens them, `'curve'` curves
713
+ * them with handles at their thirds so they keep their shape, and no `to`
714
+ * (Shift+C) curves them all when any is straight and straightens them
715
+ * otherwise.
716
+ */
717
+ declare const convertSegmentsAction: Action & {
718
+ requires: string[];
719
+ };
720
+ /**
721
+ * Shift+double-click a segment of the path being edited to flip
722
+ * it between straight and curved. Off every segment the click falls through.
723
+ */
724
+ declare const convertSegmentAtAction: Action & {
725
+ requires: string[];
726
+ };
727
+
728
+ /**
729
+ * Anchor selection along the path in path edit: grow or shrink it by one
730
+ * anchor, every Nth anchor, corners or smooth anchors, and the complement.
731
+ * They change which anchors are selected and nothing else, so none of them
732
+ * is an undo step.
733
+ */
734
+
735
+ /** The group every action here is in, which an `ActionBar` can show. */
736
+ declare const ANCHOR_SELECTION_GROUP = "anchorSelection";
737
+ /** Add each selected anchor's neighbors along the path. `]`. */
738
+ declare const growAnchorSelectionAction: Action & {
739
+ requires: string[];
740
+ };
741
+ /** Drop the anchors at the ends of each selected run. `[`. */
742
+ declare const shrinkAnchorSelectionAction: Action & {
743
+ requires: string[];
744
+ };
745
+ /**
746
+ * Select every `params.n`th anchor (default 2) along each subpath, counting
747
+ * from its first selected anchor, or from the start of every subpath when
748
+ * none is selected. Alt+N.
749
+ */
750
+ declare const selectEveryNthAnchorAction: Action & {
751
+ requires: string[];
752
+ };
753
+ /** Select every corner anchor of the edited path. Alt+C. */
754
+ declare const selectCornerAnchorsAction: Action & {
755
+ requires: string[];
756
+ };
757
+ /** Select every smooth anchor of the edited path. Alt+M. */
758
+ declare const selectSmoothAnchorsAction: Action & {
759
+ requires: string[];
760
+ };
761
+ /** Select the anchors that are not selected, and only those. Alt+I. */
762
+ declare const invertAnchorSelectionAction: Action & {
763
+ requires: string[];
764
+ };
765
+
766
+ /**
767
+ * Add extremes in path edit: an anchor at each turn of a curved segment, in x
768
+ * and in y, with the shape left as it was.
769
+ */
770
+
771
+ /**
772
+ * Split the curved segments of the edited path at their x and y extremes:
773
+ * those whose two ends are both selected, or every one while no anchor is.
774
+ * The selected anchors stay selected, and the new ones join them. Alt+X.
775
+ * Enabled only where it would add an anchor.
776
+ */
777
+ declare const addExtremeAnchorsAction: Action & {
778
+ requires: string[];
779
+ };
780
+
781
+ /**
782
+ * The balance grip on each curved segment of the path being edited, midway
783
+ * along the line joining its two handle tips:
784
+ *
785
+ * - `balanceHandles` — drag the grip to scale both handles together, each
786
+ * along its own direction, so the segment fills out toward its corner or
787
+ * flattens toward its chord without turning at either end.
788
+ * - `equalizeHandles` — double-click the grip to give both handles the same
789
+ * proportion of the way to that corner.
790
+ *
791
+ * The grips are the `balance:N` affordances path edit lays out, shown and
792
+ * hit-tested only while chrome-caps shows `path-edit.balance`. N is the flat
793
+ * index of the anchor the segment starts at.
794
+ *
795
+ * Under a `pathSymmetry` dep a grip drag moves every image of both handles,
796
+ * as `editAnchors` moves a dragged handle's. Both keep each handle's angle, so an angle lock holds through them. A length
797
+ * lock in force for handle drags (the `editAnchors` dep's `handleLocks`, or Mod)
798
+ * holds the drag still, as it would a handle drag with the angle held too.
799
+ */
800
+
801
+ /** Drag a segment's balance grip to scale both its handles together. */
802
+ declare const balanceHandlesAction: Action & {
803
+ requires: string[];
804
+ };
805
+ /** Double-click a segment's balance grip to even out its handles' proportions. */
806
+ declare const equalizeHandlesAction: Action & {
807
+ requires: string[];
808
+ };
809
+
810
+ /**
811
+ * The path-effect actions: make a live blend, bend, or repeat from the
812
+ * selection; change one's settings; and expand one into ordinary paths.
813
+ */
814
+
815
+ /** The `group` every path-effect action is in, for an `<ActionBar>`. */
816
+ declare const PATH_EFFECTS_GROUP = "pathEffects";
817
+ /**
818
+ * Steps between the first two selected shapes, in the order they were
819
+ * selected: `params.steps` of them, each shape, fill, stroke and turn
820
+ * interpolated, laid along a third selected path where there is one. The
821
+ * blend is live — editing a source moves its steps — and goes just under the
822
+ * upper of the first two. Its other params are `BlendEffect`'s.
823
+ */
824
+ declare const blendAction: Action & {
825
+ requires: string[];
826
+ };
827
+ /**
828
+ * The first selected shape bent along the second, live: editing the spine
829
+ * re-bends it. Params are `BendAlongEffect`'s.
830
+ */
831
+ declare const bendAlongAction: Action & {
832
+ requires: string[];
833
+ };
834
+ /**
835
+ * Copies of the first selected shape along the second, live. Params are
836
+ * `RepeatAlongEffect`'s.
837
+ */
838
+ declare const repeatAlongAction: Action & {
839
+ requires: string[];
840
+ };
841
+ /**
842
+ * Change the settings of path effects: `params.ids` when given, else the
843
+ * selected ones. Every other param is a setting, laid over each effect's
844
+ * own, and one that would change nothing commits nothing. Consecutive edits
845
+ * of one effect coalesce, so a slider dragged through many values is one
846
+ * undo step where the scene coalesces.
847
+ */
848
+ declare const editPathEffectAction: Action & {
849
+ requires: string[];
850
+ };
851
+ /**
852
+ * Turn each selected path effect into the ordinary paths it shows, in its
853
+ * slot and in its paint, and select them. Its sources stay; the paths no
854
+ * longer follow them.
855
+ */
856
+ declare const expandPathEffectAction: Action & {
857
+ requires: string[];
858
+ };
859
+
860
+ /**
861
+ * The warp actions. A warp tool binds the per-kind ones (`<kind>.drag`,
862
+ * `puppet.addPin`, `puppet.removePin`) and owns them; `expandWarp` and
863
+ * `configureWarp` work on whatever warp the selection holds and ship with
864
+ * the `paths` preset.
865
+ */
866
+
867
+ /** What a press on a warp control hands the drag that follows. */
868
+ interface WarpHandleScratch {
869
+ /** The warp the control belongs to, or null for one shown over a selection
870
+ * holding none yet, which the drag makes. */
871
+ warpId: string | null;
872
+ /** The control's index, as `warpControls` counts them. */
873
+ index: number;
874
+ }
875
+ /** The scratch a control press carried, or null when the press was elsewhere. */
876
+ declare function warpHandleScratchOf(hit: unknown): WarpHandleScratch | null;
877
+ /**
878
+ * Drop the warp from each selected path, keeping the geometry it made: the
879
+ * shape becomes plain geometry that path editing and other warps start from.
880
+ */
881
+ declare const expandWarpAction: Action;
882
+ /**
883
+ * Remake the selection's warps under new settings (`params`, a
884
+ * {@link WarpSettings}, plus an optional `kind` limiting it to one kind of
885
+ * warp): switch a distort between free and perspective, an envelope between
886
+ * its mappings or to a finer grid, or refit at another tolerance. One undo step.
887
+ */
888
+ declare const configureWarpAction: Action;
889
+
890
+ /**
891
+ * Corner shape in path edit: drag a corner's widget to set its radius.
892
+ *
893
+ * A drag measures how far the pointer moves along the corner's bisector and
894
+ * turns that into a cut along the edges, clamped to the room the neighboring
895
+ * corners leave. Pressing the widget of a selected anchor gives every
896
+ * selected corner the same radius. The release commits one undo step.
897
+ */
898
+
899
+ /** The shape a corner with no spec yet takes. */
900
+ declare const DEFAULT_CORNER_SHAPE: CornerShape;
901
+ /** Drag a corner widget to set the corner's radius. */
902
+ declare const editCornerAction: Action & {
903
+ requires: string[];
904
+ };
905
+
906
+ /**
907
+ * Commands on the corners of the anchors selected in path edit: set their
908
+ * shape or radius, or step their shape through the `corner-shape` keywords.
909
+ */
910
+
911
+ /** The share of a corner's room that a shape given to a sharp corner cuts. */
912
+ declare const DEFAULT_CORNER_TRIM_SHARE = 0.25;
913
+ /**
914
+ * Set the selected corners' shape, radius, or both, from the `shape` (a
915
+ * keyword or a finite `superellipse()` parameter) and `radius` params; each
916
+ * is left alone when its param is absent, except that a shape given to a
917
+ * sharp corner also gives it a radius. A UI control's action: it has no
918
+ * default binding.
919
+ */
920
+ declare const setCornersAction: Action & {
921
+ requires: string[];
922
+ };
923
+ /**
924
+ * Step the selected corners to the next `corner-shape` keyword, keeping
925
+ * their radius. Every one takes the keyword after the first selected
926
+ * corner's shape, so a mixed selection comes out alike, and a sharp corner
927
+ * gets a radius that shows it.
928
+ */
929
+ declare const cycleCornerShapeAction: Action & {
930
+ requires: string[];
931
+ };
932
+
933
+ /** Options for a tool that acts on the stroke under a click. */
934
+ interface StrokeClickToolOptions {
935
+ /** Override the default keybinding. Pass `null` to omit. */
936
+ keybinding?: ToolKeybinding | null;
937
+ /** How far from a stroke a click may land and still reach it, in screen
938
+ * pixels. Read on every click. Default 6. */
939
+ radiusPx?: number | (() => number);
940
+ }
941
+
942
+ /** Options for `useTrimTool`; the default keybinding is `{ key: 'Y' }`. */
943
+ type UseTrimToolOptions = StrokeClickToolOptions;
944
+ /**
945
+ * Trim segment tool. A click on a stroke removes its piece between the
946
+ * crossings on either side — a declarative shell over the `trimPath` action,
947
+ * which owns the hit-test and the commit. While it is up, the piece under the
948
+ * pointer is drawn dashed, through the same `planTrim` the click commits.
949
+ */
950
+ declare function useTrimTool(options?: UseTrimToolOptions): Tool;
951
+
952
+ /** Options for `useExtendTool`; the default keybinding is `{ key: 'U' }`. */
953
+ type UseExtendToolOptions = StrokeClickToolOptions;
954
+ /**
955
+ * Extend to next path tool. A click near an open end of a stroke carries
956
+ * that end along its tangent to the next stroke it meets — a declarative
957
+ * shell over the `extendToMeet` action.
958
+ */
959
+ declare function useExtendTool(options?: UseExtendToolOptions): Tool;
960
+
961
+ /** Options for `useSmudgeBrushTool`. */
962
+ interface UseSmudgeBrushToolOptions {
963
+ /** Override the default keybinding (`{ key: 'S' }`). Pass `null` to omit. */
964
+ keybinding?: ToolKeybinding | null;
965
+ }
966
+ /**
967
+ * Smudge brush tool. A round brush that pushes the outline of the selected
968
+ * paths along with the pointer — a declarative shell whose one binding routes a
969
+ * drag to the `smudgeBrush` action, which owns the stroke, its preview, its ring
970
+ * cursor, and its one undo entry. The brush's radius (screen px) and strength are
971
+ * that action's params: `tune={{ smudgeBrush: { params: () => ({ radiusPx, strength }) } }}`.
972
+ */
973
+ declare function useSmudgeBrushTool(options?: UseSmudgeBrushToolOptions): Tool;
974
+
975
+ /** A warp tool: a `Tool` that also registers the layer showing its controls. */
976
+ type WarpTool = Tool & Pick<SurfaceContribution, 'attach'>;
977
+ /** Options every warp tool takes. */
978
+ interface WarpToolOptions {
979
+ /** Palette label. */
980
+ label?: string;
981
+ /** Override the default keybinding (Distort `F`, Envelope `M`, Puppet Warp
982
+ * `B`). Pass `null` to omit. */
983
+ keybinding?: ToolKeybinding | null;
984
+ /** How far, in world units, a warped path's refitted curves may stray from
985
+ * the exact warp. Default 0.5. */
986
+ tolerance?: number;
987
+ }
988
+ /** Options for {@link useDistortTool}. */
989
+ interface UseDistortToolOptions extends WarpToolOptions {
990
+ /** The map a new distort starts with: `'free'` (bilinear, the default) or
991
+ * `'perspective'` (projective). */
992
+ mode?: DistortMode;
993
+ }
994
+ /** Options for {@link useEnvelopeTool}. */
995
+ interface UseEnvelopeToolOptions extends WarpToolOptions {
996
+ /** Patch rows and columns of a new envelope's mesh. Default 3 each. */
997
+ rows?: number;
998
+ cols?: number;
999
+ /** How a new envelope interpolates its mesh. Default `'bicubic'`. */
1000
+ mapping?: EnvelopeMapping;
1001
+ }
1002
+ /** Options for {@link usePuppetTool}. */
1003
+ type UsePuppetToolOptions = WarpToolOptions;
1004
+ /**
1005
+ * Distort the selected paths by their box's four corners, each dragged on its
1006
+ * own. The first drag gives the selection a distort, kept on each path as
1007
+ * `data.warp` until it is expanded (`expandWarp`); selecting the paths again
1008
+ * with this tool shows its corners where they were left. `configureWarp`
1009
+ * switches a held distort between free and perspective.
1010
+ */
1011
+ declare function useDistortTool(options?: UseDistortToolOptions): WarpTool;
1012
+ /**
1013
+ * Warp the selected paths through a mesh of control points over their box:
1014
+ * drag any point and the shape bends smoothly around it. The mesh is held on
1015
+ * each path until it is expanded, and `configureWarp` changes its grid or its
1016
+ * interpolation.
1017
+ */
1018
+ declare function useEnvelopeTool(options?: UseEnvelopeToolOptions): WarpTool;
1019
+ /**
1020
+ * Pin the selected paths where they are clicked, and drag a pin to bend them:
1021
+ * the rest of the shape follows as rigidly as the pins allow, and a pin left
1022
+ * alone holds its place. Alt-click a pin to take it out. The pins are held on
1023
+ * each path until it is expanded.
1024
+ */
1025
+ declare function usePuppetTool(options?: UsePuppetToolOptions): WarpTool;
1026
+
1027
+ /** The id of the layer showing a `kind` warp's controls. A binding reaches
1028
+ * its controls through `affordance:layer:<id>`. */
1029
+ declare const warpLayerId: (kind: WarpKind) => string;
1030
+
1031
+ /**
1032
+ * Shape building: the selected paths cut into the faces their outlines bound,
1033
+ * then a pick of those faces united into one path, or deleted, as one undo step.
1034
+ */
1035
+
1036
+ /** What a pick of faces does: unite them into one path, or delete them. */
1037
+ type ShapeBuilderMode = 'merge' | 'delete';
1038
+ /** One face of the selection: a connected region no outline crosses. */
1039
+ interface ShapeBuilderFace {
1040
+ path: PolygonPath;
1041
+ /** The topmost source covering the face, which a path made of it stands in for. */
1042
+ sourceId: NodeId;
1043
+ /** Set when that source's stroke went into the face: the paint it wears. */
1044
+ paint?: PathPaint;
1045
+ }
1046
+ /** The faces of the selection, and the commit that rebuilds it from a pick of them. */
1047
+ interface ShapeBuilderPlan {
1048
+ kind: 'faces';
1049
+ /** Back to front by the sources they cover, exclusives first. */
1050
+ faces: readonly ShapeBuilderFace[];
1051
+ /** The index of the face holding the world point, or -1. */
1052
+ faceAt(x: number, y: number): number;
1053
+ /**
1054
+ * Replace the selection with its faces, the `picked` ones united into one
1055
+ * path standing in for the source of the first one picked (`'merge'`) or
1056
+ * left out (`'delete'`). Every other face becomes a path of its own. One
1057
+ * undo step, and what it made is selected. Commits nothing for an empty pick.
1058
+ */
1059
+ commit(picked: readonly number[], mode: ShapeBuilderMode): BooleanOpResult;
1060
+ }
1061
+ /**
1062
+ * The faces the selected paths cut each other into, or why there are none.
1063
+ * Reads the adapter and changes nothing; the plan's `commit` writes.
1064
+ */
1065
+ declare function planShapeBuilder(adapter: BooleansAdapter, opts?: BooleanOpOptions): ShapeBuilderPlan | Exclude<BooleanOpResult, {
1066
+ kind: 'applied';
1067
+ }>;
1068
+ /**
1069
+ * The faces a run of world points passes through, each once, in the order
1070
+ * the run enters them. `step` is the world distance between the samples
1071
+ * taken along each segment; a face narrower than it can be stepped over.
1072
+ */
1073
+ declare function facesAlong(plan: Pick<ShapeBuilderPlan, 'faceAt'>, run: readonly {
1074
+ x: number;
1075
+ y: number;
1076
+ }[], step: number): number[];
1077
+
1078
+ /** Options for `useShapeBuilderTool`. */
1079
+ interface UseShapeBuilderToolOptions {
1080
+ /** Override the default keybinding (`{ key: 'M', shift: true }`). Pass `null` to omit. */
1081
+ keybinding?: ToolKeybinding | null;
1082
+ /** What a stroked path gives the faces. Default `'unfilled'`, as the
1083
+ * Pathfinder actions: an unfilled stroke counts as the region it covers. */
1084
+ strokes?: BooleanStrokes;
1085
+ /** What a drag or a click does without Alt: unite the faces (`'merge'`, the
1086
+ * default) or delete them. With Alt it does the other. Read on every gesture. */
1087
+ mode?: ShapeBuilderMode;
1088
+ }
1089
+ /**
1090
+ * Shape builder. With two or more paths selected, the faces their outlines cut
1091
+ * each other into are what the tool works on: the face under the pointer is
1092
+ * shown, a drag unites every face it passes through into one path wearing the
1093
+ * paint of the first, and an Alt-drag deletes them (the other way round under
1094
+ * `mode: 'delete'`). A click takes the one face. Every face not taken becomes a path of its own, and each gesture is
1095
+ * one undo step. With fewer than two selected, a drag marquee-selects.
1096
+ *
1097
+ * It reads the `booleansAdapter` dep the Pathfinder actions do.
1098
+ */
1099
+ declare function useShapeBuilderTool(options?: UseShapeBuilderToolOptions): Tool;
1100
+
1101
+ /** Options for `useGirthTool`. */
1102
+ interface UseGirthToolOptions {
1103
+ /** Override the default keybinding (`{ key: 'W', shift: true }`). Pass `null` to omit. */
1104
+ keybinding?: ToolKeybinding | null;
1105
+ /** Screen px around a stroke's edge within which a press takes hold of it,
1106
+ * and along the path within which it takes the anchor there rather than
1107
+ * inserting one. Default 6. */
1108
+ reach?: number;
1109
+ }
1110
+ /**
1111
+ * Girth tool: shapes a stroke's width along its length, into its
1112
+ * `vertexWidths`, by dragging the stroke's edges. Hovering near either edge
1113
+ * marks the grab point on it and the width point a press there moves: the
1114
+ * anchor at that spot, or a new one inserted between two, keeping the path's
1115
+ * shape. Dragging moves the grabbed edge along the path's normal, the other
1116
+ * edge mirroring it so the width stays centered; inward pinches it, down to
1117
+ * zero. With Alt held only the grabbed edge moves, writing a `{ left, right }`
1118
+ * pair. Either drag is live and one undo entry. Alt-click on a width point
1119
+ * clears it, so it takes the stroke's own width. The last stroke touched shows
1120
+ * its anchors, with a line across the stroke at each that has a width.
1121
+ */
1122
+ declare function useGirthTool(options?: UseGirthToolOptions): Tool;
1123
+
1124
+ /** What the Girth tool's overlay shows, in world space: the anchors of the
1125
+ * stroke last hovered or pressed on, each with its unit normal toward its
1126
+ * right edge and its `vertexWidths` entry (`NaN` for none), and, over an
1127
+ * edge, the grab point on it and the width point a press there moves. */
1128
+ interface GirthFocus {
1129
+ anchors: readonly Point[];
1130
+ normals: readonly Point[];
1131
+ widths: readonly VertexWidth[];
1132
+ /** The stroke's own width, which an unset entry takes. */
1133
+ fallback: number;
1134
+ grab?: {
1135
+ edge: Point;
1136
+ at: Point;
1137
+ };
1138
+ }
1139
+ /** The Girth tool's own state, shared by its actions and its overlay. */
1140
+ interface GirthEnv {
1141
+ focus(): GirthFocus | null;
1142
+ setFocus(next: GirthFocus | null): void;
1143
+ /** Screen px around a stroke's edge within which a press takes hold of it,
1144
+ * and along the path within which it takes the anchor there. */
1145
+ reachPx(): number;
1146
+ }
1147
+ /** Id of the action that drags a stroke's edge. */
1148
+ declare const GIRTH_DRAG_ID = "girth.drag";
1149
+ /** Id of the action that clears the width point under a click. */
1150
+ declare const GIRTH_CLEAR_ID = "girth.clear";
1151
+ /** Id of the action that marks the edge under a hover. */
1152
+ declare const GIRTH_HOVER_ID = "girth.hover";
1153
+ /** Id of the action that drops the hover mark when the pointer leaves. */
1154
+ declare const GIRTH_UNHOVER_ID = "girth.unhover";
1155
+ /**
1156
+ * The Girth tool's actions, which write a stroke's `vertexWidths`.
1157
+ * `girth.drag` takes hold of the stroke edge under the press, at the anchor
1158
+ * there or at one it inserts, shape kept, between two. The edge follows the
1159
+ * pointer along the path's normal, the other edge mirroring it so the width
1160
+ * stays centered; with Alt held only the grabbed edge moves, which writes a
1161
+ * `{ left, right }` pair. Either is live, and one undo entry. `girth.clear`
1162
+ * clears the width point under a click, so it takes the stroke's own width
1163
+ * again. `girth.hover` and `girth.unhover` show and hide the grab point under
1164
+ * the pointer.
1165
+ */
1166
+ declare function createGirthActions(env: GirthEnv): Action[];
1167
+
1168
+ /**
1169
+ * A screen-space layer marking the width points of the stroke the Girth tool
1170
+ * last touched: a dot on each anchor, white where it has a width of its own,
1171
+ * with a line across the stroke from its left edge to its right. Over an
1172
+ * edge it adds the grab point on that edge, ringing the width point a press
1173
+ * there moves.
1174
+ */
1175
+ declare function createGirthOverlay(source: {
1176
+ focus(): GirthFocus | null;
1177
+ subscribe(fn: () => void): () => void;
1178
+ }): RenderLayer<unknown>;
1179
+
1180
+ /**
1181
+ * What the Girth tool reads and writes: the stroke edge under a point, the
1182
+ * width point a press on it moves — inserting an anchor where the press falls
1183
+ * between two — and the per-vertex widths a drag leaves behind.
1184
+ */
1185
+
1186
+ /** A stroked path node and its centerline in world space. */
1187
+ interface WidthTarget {
1188
+ id: NodeId;
1189
+ node: SceneNode<unknown, string, unknown>;
1190
+ path: PolygonPath;
1191
+ stroke: Stroke;
1192
+ /** The stroke's own width in world units: what an unset entry takes. */
1193
+ fallback: number;
1194
+ }
1195
+ /** A stroke edge found under a point, and the width point a press there
1196
+ * moves: anchor `index`, or one inserted at `at` when `index` is null. */
1197
+ interface WidthEdgeHit {
1198
+ target: WidthTarget;
1199
+ edge: StrokeEdgeHit;
1200
+ index: number | null;
1201
+ at: Point;
1202
+ }
1203
+ /**
1204
+ * The width point a drag edits, on a node's geometry as the press leaves it:
1205
+ * `path` gained an anchor at `index` when the press fell between two.
1206
+ */
1207
+ interface WidthGrip {
1208
+ target: WidthTarget;
1209
+ /** The world path, with the inserted anchor if there is one. */
1210
+ path: PolygonPath;
1211
+ /** One entry per anchor of `path`; `NaN` takes the stroke's own width. */
1212
+ widths: VertexWidth[];
1213
+ index: number;
1214
+ at: Point;
1215
+ inserted: boolean;
1216
+ /** The edge taken hold of, and the unit normal it moves along. */
1217
+ side: StrokeSide;
1218
+ normal: Point;
1219
+ /** Each edge's distance from the path at the width point, before the drag. */
1220
+ start: HalfWidths;
1221
+ /** What the nib multiplies a stored width by there, so a drag moves the
1222
+ * edge as drawn. */
1223
+ factor: number;
1224
+ }
1225
+
1226
+ /** Spiro curves: path edit edits a spiro node's points and solves the curve
1227
+ * again through them (`spiroRepresentation`), `setSpiroKind` sets the kind
1228
+ * of the selected points, and `releaseSpiro` converts a spiro curve to an
1229
+ * ordinary path. `useSpiroTool` draws them. */
1230
+ declare const spiroPreset: FeaturePreset;
1231
+
1232
+ /** Options for {@link useSpiroTool}. */
1233
+ interface UseSpiroToolOptions {
1234
+ /** Select the new curve once it commits. Default `true`. */
1235
+ autoSelect?: boolean;
1236
+ /** Screen-px radius of "click the first point to close". Default `8`. */
1237
+ closeHitRadius?: number;
1238
+ }
1239
+ /**
1240
+ * Active-slot Tool that draws a spiro curve (`solveSpiro`) by placing its
1241
+ * on-curve points: each click places one, of the kind `params.kind` of the
1242
+ * `spiro.placePoint` action (default `'smooth'`; Alt-click places a corner),
1243
+ * and the preview solves the curve through the points and the cursor as it
1244
+ * moves. Clicking the first point of three or more closes the curve; Enter,
1245
+ * a double-click or Mod-click finishes it open; Backspace takes back the last
1246
+ * point and Escape discards them all, as does switching tools.
1247
+ *
1248
+ * The curve is inserted through the `insert` dep as a `'path'`, its spiro
1249
+ * form beside it on `data.representation`, so a consumer shapes the node by
1250
+ * overriding that dep. With `spiroPreset` on, path edit then edits its points
1251
+ * (`spiroRepresentation`).
1252
+ */
1253
+ declare function useSpiroTool(options?: UseSpiroToolOptions): Tool;
1254
+
1255
+ /**
1256
+ * Commands on spiro curves (`spiroShape`): set the kind of the points
1257
+ * selected in path edit, and convert a spiro curve to an ordinary path.
1258
+ */
1259
+
1260
+ /**
1261
+ * @experimental In path edit on a spiro curve, give the selected points the
1262
+ * kind `params.kind` (default `'smooth'`) and solve the curve again. One
1263
+ * variant per kind.
1264
+ */
1265
+ declare const setSpiroKindAction: Action & {
1266
+ requires: string[];
1267
+ };
1268
+ /**
1269
+ * @experimental Convert the selected spiro curves, or the one in path edit, to
1270
+ * ordinary paths: the curve stays as drawn and its anchors and handles become
1271
+ * editable, while the spiro points stop being solved. Path edit's anchor
1272
+ * selection is cleared, since the anchors it numbered are gone.
1273
+ */
1274
+ declare const releaseSpiroAction: Action & {
1275
+ requires: string[];
1276
+ };
1277
+
1278
+ /**
1279
+ * Spiro curves in path edit: a spiro node is edited through its straight-sided
1280
+ * control path, whose anchors are the curve's points, and every edit solves
1281
+ * the curve again through them. Each point draws by its kind, and a press on
1282
+ * the curve between two points adds one there.
1283
+ */
1284
+
1285
+ /** How path edit reads and writes a spiro form. */
1286
+ declare const spiroRepresentation: PathRepresentation;
1287
+ /** The path-edit extension carrying {@link spiroRepresentation}. */
1288
+ declare const spiroExtension: PathEditExtension;
1289
+
1290
+ /**
1291
+ * How a spiro point is marked, by its kind: a circle for a smooth point, a
1292
+ * diamond for a g4 point, a square for a corner, and a half-disc for a point
1293
+ * beside a straight segment, its flat side toward the line.
1294
+ */
1295
+
1296
+ /**
1297
+ * The marker shape of a spiro point of `kind`. A `left` point's line arrives
1298
+ * from its previous neighbor and a `right` point's leaves toward its next,
1299
+ * both in the same screen space; without that neighbor the half-disc faces
1300
+ * right.
1301
+ */
1302
+ declare function spiroMarker(kind: SpiroKind): AnchorMarkerShape;
1303
+
1304
+ /** What the spiro tool has placed, as the preview reads it each frame. */
1305
+ interface SpiroPreviewState {
1306
+ points: readonly SpiroPoint[];
1307
+ /** Where the next point would go, or null while the pointer is away. */
1308
+ cursor: SpiroPoint | null;
1309
+ /** True while a click would close the curve on its first point. */
1310
+ closeHint: boolean;
1311
+ }
1312
+ /** Options for {@link createSpiroPreviewLayer}. */
1313
+ interface CreateSpiroPreviewLayerOptions {
1314
+ state: () => SpiroPreviewState;
1315
+ /** The color the curve and markers are drawn in. */
1316
+ color?: string;
1317
+ }
1318
+ /** Render layer drawing the spiro tool's curve in progress; nothing while
1319
+ * no point is placed. */
1320
+ declare function createSpiroPreviewLayer(opts: CreateSpiroPreviewLayerOptions): RenderLayer<unknown>;
1321
+
1322
+ /**
1323
+ * A spiro node: a path node whose `data.representation` is a spiro form,
1324
+ * saying its `data.path` is a solved spiro curve and which of the path's
1325
+ * anchors are the curve's points (`spiroToPath`). The path stays the geometry
1326
+ * every reader uses; path edit edits the points instead, through the
1327
+ * straight-sided control path, and each edit solves the curve again
1328
+ * (`spiroRepresentation`). Converting to a path drops the form.
1329
+ */
1330
+
1331
+ /** The `kind` of a spiro form. */
1332
+ declare const SPIRO_FORM = "spiro";
1333
+ /** A spiro form, as `data.representation` holds it: each point's kind, and
1334
+ * the anchor of `data.path` it became. */
1335
+ type SpiroShape = {
1336
+ kind: typeof SPIRO_FORM;
1337
+ kinds: SpiroKind[];
1338
+ at: number[];
1339
+ };
1340
+ /** `form` as a spiro form, or null when it is not a well-formed one. */
1341
+ declare function asSpiro(form: unknown): SpiroShape | null;
1342
+ /** The spiro form a node's `data` holds, or `null`. */
1343
+ declare function spiroOf(data: unknown): SpiroShape | null;
1344
+ /** `data` without its spiro form: the curve as plain geometry. */
1345
+ declare function withoutSpiro<T>(data: T): T;
1346
+ /** The node data a solved spiro curve is stored as: its path, and the form
1347
+ * that says which anchors are points. */
1348
+ declare function spiroNodeData(contours: readonly SpiroContour[], like?: PolygonPath): {
1349
+ path: PolygonPath;
1350
+ representation: SpiroShape;
1351
+ };
1352
+ /** The contours `spiro` reads out of `path`, or null when it does not
1353
+ * describe it. */
1354
+ declare function spiroContours(path: PolygonPath, spiro: SpiroShape): SpiroContour[] | null;
1355
+ /** The straight-sided path through `spiro`'s points on `path`: what path
1356
+ * edit shows and moves. Null when `spiro` does not describe `path`. */
1357
+ declare function spiroEditPath(path: PolygonPath, spiro: SpiroShape): PolygonPath | null;
1358
+ /**
1359
+ * The curve an edit of the control path makes, solved through its anchors:
1360
+ * `control` is the edited control path, `path` the curve it was edited from.
1361
+ * Each point keeps its kind (`spiroContoursFromControl`). Null when `spiro`
1362
+ * does not describe `path`.
1363
+ */
1364
+ declare function respiro(path: PolygonPath, spiro: SpiroShape, control: PolygonPath): {
1365
+ path: PolygonPath;
1366
+ spiro: SpiroShape;
1367
+ } | null;
1368
+ /** The curve `path` makes with the points at `flatIndexes` set to `kind`. Null
1369
+ * when `spiro` does not describe `path`, or no kind changes. */
1370
+ declare function respiroWithKind(path: PolygonPath, spiro: SpiroShape, flatIndexes: Iterable<number>, kind: SpiroKind): {
1371
+ path: PolygonPath;
1372
+ spiro: SpiroShape;
1373
+ } | null;
1374
+
1375
+ /**
1376
+ * Path constraints in path edit: their marks on the open path, a click
1377
+ * selecting one, Delete removing it, and every edit finished so they hold.
1378
+ */
1379
+
1380
+ /** Owner id of the constraint marks, as `EditAnchorsDep.selectedMark` names them. */
1381
+ declare const CONSTRAINTS_EXTENSION_ID = "constraints";
1382
+ /**
1383
+ * Replace the open path's constraints with `constraints` as one history
1384
+ * entry, moving it the least that makes them all hold. When they cannot all
1385
+ * hold the path stays as it is and the list is stored all the same, the
1386
+ * conflict left for the marks to show. Clears the selected mark, since the
1387
+ * list is renumbered. Answers the indices in conflict, as
1388
+ * `constraintConflictsOf` names them — empty when every one holds — or null
1389
+ * when the dep cannot store.
1390
+ */
1391
+ declare function setConstraints(dep: EditAnchorsDep, constraints: readonly PathConstraint[], label: string): number[] | null;
1392
+ /** The constraint selected in path edit, as an index into the open path's list, or null. */
1393
+ declare function selectedConstraintOf(dep: EditAnchorsDep): number | null;
1394
+ /** The constraints extension, its marks drawn in `style`'s colors. */
1395
+ declare function createConstraintsExtension(style?: Partial<ConstraintGlyphStyle>): PathEditExtension;
1396
+
1397
+ /**
1398
+ * Constraints between nodes. A link is a node of its own: its `dependsOn`
1399
+ * names the nodes it ties, in order, and its `data.constraintLink` holds one
1400
+ * `PathConstraint` whose anchors are numbered over theirs one after another —
1401
+ * with two nodes of 4 and 5 anchors, anchor 6 is the second node's anchor 2.
1402
+ * Being a node, it is saved and undone with the scene, and deleting a node it
1403
+ * ties deletes it too. It draws nothing and takes no clicks; keep it on a
1404
+ * layer that is not exported, such as an annotation layer.
1405
+ *
1406
+ * An edit of any node a link ties solves every node the links reach in one
1407
+ * solve: an anchor held on another node's segment slides along it when
1408
+ * dragged and follows it when that node is edited.
1409
+ */
1410
+
1411
+ /** What a link node keeps in `data`. */
1412
+ interface ConstraintLinkData {
1413
+ constraintLink: PathConstraint;
1414
+ }
1415
+ /** The constraint `data` holds as a link, or null when it is not one. */
1416
+ declare function constraintLinkOf(data: unknown): PathConstraint | null;
1417
+ /** Options for {@link constraintLinkNode}. */
1418
+ interface ConstraintLinkNodeOptions<TPose> {
1419
+ layer: string;
1420
+ /** The nodes it ties, in the order its anchors number them. */
1421
+ nodes: readonly string[];
1422
+ constraint: PathConstraint;
1423
+ id?: string;
1424
+ /** Default: an empty box at the origin. */
1425
+ pose?: TPose;
1426
+ }
1427
+ /** A link node, ready for `scene.add`. Registers the painter that keeps link nodes from drawing. */
1428
+ declare function constraintLinkNode<TPose = RectPose>(opts: ConstraintLinkNodeOptions<TPose>): AddNodeSpec<ConstraintLinkData, string, TPose>;
1429
+
1430
+ /**
1431
+ * The controls for the selected corners of a path in edit: a schema any
1432
+ * prefs form draws, the values and mixed fields it shows for a selection,
1433
+ * and the `setCorners` params a change to one of them makes.
1434
+ */
1435
+
1436
+ /** How far the parameter slider reaches either side of the bevel; the
1437
+ * `square` and `notch` keywords stand for the two infinities beyond it. */
1438
+ declare const CORNER_K_SLIDER_RANGE = 4;
1439
+ /** What the corner controls ask for while no corner is selected. */
1440
+ declare const CORNER_CONTROLS_PROMPT = "Click a corner anchor (Shift-click for more) to edit it";
1441
+ /** The corner controls as a prefs schema: the keywords, the parameter, and
1442
+ * the radius, each disabled while no corner is selected. */
1443
+ declare const CORNER_CONTROLS: PrefGroup;
1444
+ /** What the controls show for a selection. */
1445
+ interface CornerControlsState {
1446
+ /** `shape` is null where no keyword applies, so a form does not fall back
1447
+ * to the leaf's default. */
1448
+ values: {
1449
+ count: number;
1450
+ shape: CornerKeyword | null;
1451
+ k?: number;
1452
+ radius?: number;
1453
+ };
1454
+ /** The fields whose selected corners disagree. */
1455
+ mixed: ReadonlySet<string>;
1456
+ }
1457
+ /** The controls for the corners of `path` among `selected` anchors. A sharp
1458
+ * corner shows no shape and no parameter. */
1459
+ declare function cornerControlsState(path: PolygonPath | null, selected: ReadonlySet<number>): CornerControlsState;
1460
+ /** The `setCorners` params a change of `field` to `value` makes, or null for
1461
+ * a field that sets nothing. */
1462
+ declare function cornerControlsParams(field: string, value: unknown): {
1463
+ shape: CornerShape;
1464
+ } | {
1465
+ radius: number;
1466
+ } | null;
1467
+
1468
+ /** The part of a scene the controls read. */
1469
+ interface CornerControlsScene {
1470
+ get(id: NodeId): EditableShape | undefined;
1471
+ subscribe(listener: () => void): () => void;
1472
+ }
1473
+ /** Which path is open for anchor editing and which of its anchors are
1474
+ * selected: what a `PathEditHost` that owns the anchor selection knows. */
1475
+ interface CornerControlsSelection {
1476
+ editingId(): string | null;
1477
+ selectedAnchors(): ReadonlySet<number>;
1478
+ subscribe(listener: () => void): () => void;
1479
+ }
1480
+ /** What {@link useCornerControls} gives a corner panel to draw. */
1481
+ interface UseCornerControlsReturn {
1482
+ /** The schema a prefs form draws: `CORNER_CONTROLS`. Its `name` is the
1483
+ * controls' title. */
1484
+ schema: typeof CORNER_CONTROLS;
1485
+ /** What to show above the form, or null once a corner is selected. */
1486
+ prompt: string | null;
1487
+ values: Record<string, unknown>;
1488
+ mixed: ReadonlySet<string>;
1489
+ /** A prefs form's `onChange` for `CORNER_CONTROLS`. */
1490
+ onChange: (field: string, value: unknown) => void;
1491
+ }
1492
+ /**
1493
+ * A corner panel over the selected corners of the path open for anchor
1494
+ * editing: their shared shape and radius, kept current as the scene or the
1495
+ * selection changes, and an `onChange` that writes through `setCorners`.
1496
+ */
1497
+ declare function useCornerControls(scene: CornerControlsScene, selection: CornerControlsSelection): UseCornerControlsReturn;
1498
+
1499
+ /**
1500
+ * A node's path constraints: kept as `data.constraints`, numbered by the
1501
+ * anchors of its editable polygon, and solved in the frame that polygon is
1502
+ * drawn in, so `horizontal` on a rotated `data.path` node turns with it.
1503
+ */
1504
+
1505
+ /** The constraints a node's data holds, or none. */
1506
+ declare function constraintsOf(data: unknown): readonly PathConstraint[];
1507
+ /** Radians the frame `horizontal` and `vertical` read is turned from the world's: a `data.path` node's rotation. */
1508
+ declare function constraintAxisOf(node: EditableShape | null | undefined): number;
1509
+ /**
1510
+ * Indices of `node`'s constraints that do not hold in its committed path, and
1511
+ * the others each conflict needs: every member of a smallest set that cannot
1512
+ * all hold. Edits leave the ones that do not hold out of the solve, and so
1513
+ * does a whole-shape transform — resize, rotate, flip — which carries the
1514
+ * list unchanged: a constraint the transform breaks, such as a set length
1515
+ * under a scale or an equal length under a non-uniform one, is reported here
1516
+ * rather than solved back.
1517
+ */
1518
+ declare function constraintConflictsOf(node: EditableShape | null | undefined): number[];
1519
+
1520
+ /** A glyph this package draws, by its bare name. */
1521
+ type PathToolsIconName = 'smudgeBrush' | 'distort' | 'envelope' | 'puppetWarp' | 'blend' | 'bendAlong' | 'repeatAlong' | 'expand' | 'balanceHandles' | 'equalizeHandles' | 'lengthen' | 'trim' | 'extend' | 'girth' | 'spiro' | 'reversePath' | 'setPathStart' | 'cornerRadius' | 'cornerShape' | 'convertSegment' | 'straightenSegment' | 'curveSegment' | 'extremeAnchors';
1522
+ declare module '@weasel-js/icons' {
1523
+ interface IconName {
1524
+ 'path-tools:smudgeBrush': true;
1525
+ 'path-tools:distort': true;
1526
+ 'path-tools:envelope': true;
1527
+ 'path-tools:puppetWarp': true;
1528
+ 'path-tools:blend': true;
1529
+ 'path-tools:bendAlong': true;
1530
+ 'path-tools:repeatAlong': true;
1531
+ 'path-tools:expand': true;
1532
+ 'path-tools:balanceHandles': true;
1533
+ 'path-tools:equalizeHandles': true;
1534
+ 'path-tools:lengthen': true;
1535
+ 'path-tools:trim': true;
1536
+ 'path-tools:extend': true;
1537
+ 'path-tools:girth': true;
1538
+ 'path-tools:spiro': true;
1539
+ 'path-tools:reversePath': true;
1540
+ 'path-tools:setPathStart': true;
1541
+ 'path-tools:cornerRadius': true;
1542
+ 'path-tools:cornerShape': true;
1543
+ 'path-tools:convertSegment': true;
1544
+ 'path-tools:straightenSegment': true;
1545
+ 'path-tools:curveSegment': true;
1546
+ 'path-tools:extremeAnchors': true;
1547
+ }
1548
+ }
1549
+
1550
+ export { ANCHOR_SELECTION_GROUP, CONSTRAINTS_EXTENSION_ID, CORNER_CONTROLS, CORNER_CONTROLS_PROMPT, CORNER_K_SLIDER_RANGE, CORNER_WIDGET_STANDOFF_PX, type ConstrainAnchorsParams, type ConstraintGlyphStyle, type ConstraintLinkData, type ConstraintLinkNodeOptions, type CornerControlsScene, type CornerControlsSelection, type CornerControlsState, type CornerWidgetStyle, type CreateSpiroPreviewLayerOptions, DEFAULT_BRIDGE_TENSION, DEFAULT_CORNER_SHAPE, DEFAULT_CORNER_TRIM_SHARE, DEFAULT_ENVELOPE_SIZE, DEFAULT_SMUDGE_RADIUS_PX, DEFAULT_SMUDGE_STRENGTH, DEFAULT_WARP_TOLERANCE, GIRTH_CLEAR_ID, GIRTH_DRAG_ID, GIRTH_HOVER_ID, GIRTH_UNHOVER_ID, type GirthEnv, type GirthFocus, type HandleSnapOptions, type HandleSnapOptionsInput, type HeldPathWarp, LENGTHEN_TOOL, type LengthenPathParams, PATH_BALANCE_CHROME_ID, PATH_COMB_CHROME_ID, PATH_CORNERS_CHROME_ID, PATH_DIRECTION_CHROME_ID, PATH_EFFECTS_GROUP, type PathDirectionStyle, type PathEditBalanceStyle, type PathEditCombOptions, type PathToolsIconName, type ResamplePathParams, type ResolvedPathWarp, SIMPLIFY_BRUSH_RADIUS_PX, SIMPLIFY_BRUSH_TOOL, SPIRO_FORM, type ShapeBuilderFace, type ShapeBuilderMode, type ShapeBuilderPlan, type SimplifyBrushParams, type SmudgeBrushParams, type SoftSelectionOptions, type SpiroPreviewState, type SpiroShape, type StrokeClickToolOptions, type UseCornerControlsReturn, type UseDistortToolOptions, type UseEnvelopeToolOptions, type UseExtendToolOptions, type UseGirthToolOptions, type UsePuppetToolOptions, type UseShapeBuilderToolOptions, type UseSmudgeBrushToolOptions, type UseSpiroToolOptions, type UseTrimToolOptions, type WarpHandleScratch, type WarpKind, type WarpSettings, type WarpTool, type WarpToolOptions, type WidthEdgeHit, type WidthGrip, type WidthTarget, addExtremeAnchorsAction, addExtremesPreset, anchorSelectionPreset, asSpiro, balanceCommands, balanceHandlesAction, balancePreset, bendAlongAction, blendAction, bridgeAnchorsAction, bridgePathsAction, bridgePreset, bridgeTensionOf, brushRingCursor, combCommands, configureWarpAction, constrainAnchorsAction, constraintAxisOf, constraintConflictsOf, constraintLinkNode, constraintLinkOf, constraintsForAnchors, constraintsOf, constraintsPreset, convertSegmentAtAction, convertSegmentsAction, cornerControlsParams, cornerControlsState, cornerShapePreset, cornerWidgetCommands, cornerWidgetFrames, createBalanceExtension, createBalancePreset, createCombExtension, createConstraintsExtension, createConstraintsPreset, createCornerShapePreset, createCornerWidgetsExtension, createCurvatureCombPreset, createDirectionExtension, createGirthActions, createGirthOverlay, createHandleSnapExtension, createHandleSnapPreset, createPathStructurePreset, createSoftSelectionExtension, createSoftSelectionPreset, createSpiroPreviewLayer, curvatureCombPreset, cycleCornerShapeAction, deleteConstraintAction, directionCommands, editCornerAction, editPathEffectAction, equalizeHandlesAction, expandPathEffectAction, expandWarpAction, extendPreset, extendToMeetAction, facesAlong, growAnchorSelectionAction, handleLockAngleAction, handleLockLengthAction, handleLocksPreset, handleSnapPreset, handleSnapSource, harmonizeAnchorsAction, harmonizePreset, heldPathWarpOf, invertAnchorSelectionAction, lengthenPathAction, lengthenPreset, lengthenToolAction, pathEffectsPreset, pathStructurePreset, pathTools, planShapeBuilder, planTrim, releaseSpiroAction, repeatAlongAction, resamplePathAction, resamplePreset, resolvePathWarp, respiro, respiroWithKind, reversePathAction, segmentTypePreset, selectCornerAnchorsAction, selectEveryNthAnchorAction, selectSmoothAnchorsAction, selectedConstraintOf, setConstraints, setCornersAction, setPathStartAction, setSpiroKindAction, shrinkAnchorSelectionAction, simplifyBrushAction, simplifyBrushPreset, simplifyBrushToolAction, smudgeBrushAction, smudgePreset, spiroContours, spiroEditPath, spiroExtension, spiroMarker, spiroNodeData, spiroOf, spiroPreset, spiroRepresentation, trimPathAction, trimPreset, unconstrainAnchorsAction, useCornerControls, useDistortTool, useEnvelopeTool, useExtendTool, useGirthTool, usePuppetTool, useShapeBuilderTool, useSmudgeBrushTool, useSpiroTool, useTrimTool, warpHandleScratchOf, warpLayerId, warpPreset, withPathWarp, withoutPathWarp, withoutSpiro };