@pixerate/editor 0.36.0 → 0.38.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.
@@ -31,13 +31,35 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
31
31
 
32
32
 
33
33
 
34
- var _chunkBGNPT5PEcjs = require('../chunk-BGNPT5PE.cjs');
34
+
35
+
36
+
37
+
38
+
39
+ var _chunk7EDLVBOOcjs = require('../chunk-7EDLVBOO.cjs');
35
40
 
36
41
  // src/vector/types.ts
37
42
  var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
38
43
  var VECTOR_ARROWHEADS = ["triangle", "arrow", "open", "circle", "square", "bar"];
39
- var VECTOR_TOOLS = ["select", "direct", "pen", "pencil", "rect", "ellipse", "polygon", "star", "line", "scissors", "knife", "eraser"];
44
+ var VECTOR_TOOLS = [
45
+ "select",
46
+ "direct",
47
+ "pen",
48
+ "pencil",
49
+ "rect",
50
+ "ellipse",
51
+ "polygon",
52
+ "star",
53
+ "line",
54
+ "scissors",
55
+ "knife",
56
+ "eraser",
57
+ "width",
58
+ "shapeBuilder",
59
+ "paintBucket"
60
+ ];
40
61
  var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
62
+ var VECTOR_PATHFINDER_OPERATIONS = ["divide", "trim", "merge", "crop", "outline", "minusBack"];
41
63
 
42
64
  // src/vector/corners.ts
43
65
  var EPS = 1e-9;
@@ -58,7 +80,7 @@ var Side = class {
58
80
  constructor(a, h1, h2, b) {
59
81
  this.straight = onAnchor(a, h1) && onAnchor(b, h2);
60
82
  this.curve = this.straight ? [pt(a), pt(b)] : [pt(a), pt(_nullishCoalesce(h1, () => ( a))), pt(_nullishCoalesce(h2, () => ( b))), pt(b)];
61
- this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunkBGNPT5PEcjs.segmentLength.call(void 0, this.curve);
83
+ this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunk7EDLVBOOcjs.segmentLength.call(void 0, this.curve);
62
84
  }
63
85
  /** Unit direction the side leaves the corner in (towards its first control point off the corner). */
64
86
  direction() {
@@ -76,10 +98,10 @@ var Side = class {
76
98
  const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
77
99
  return { t: t2, p: add(vec(this.curve[0]), mul(u, s)), dir: u };
78
100
  }
79
- const t = _chunkBGNPT5PEcjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
80
- const d = vec(_chunkBGNPT5PEcjs.segmentDerivative.call(void 0, this.curve, t));
101
+ const t = _chunk7EDLVBOOcjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
102
+ const d = vec(_chunk7EDLVBOOcjs.segmentDerivative.call(void 0, this.curve, t));
81
103
  const l = len(d);
82
- return { t, p: vec(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
104
+ return { t, p: vec(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
83
105
  }
84
106
  };
85
107
  function cornerOf(sp, s, i) {
@@ -152,7 +174,7 @@ function cutCorner(c, radius, kind) {
152
174
  return { tu: a.t, tv: b.t, start: a.p, out: add(a.p, mul(d1, k)), in: add(b.p, mul(d2, k)), end: b.p };
153
175
  }
154
176
  var radiusOf = (a) => {
155
- const r = _optionalChain([a, 'access', _2 => _2.corner, 'optionalAccess', _3 => _3.radius]);
177
+ const r = _optionalChain([a, 'access', _3 => _3.corner, 'optionalAccess', _4 => _4.radius]);
156
178
  return typeof r === "number" && r > 0 && Number.isFinite(r) ? r : 0;
157
179
  };
158
180
  function hasVectorCorners(subpaths) {
@@ -166,7 +188,7 @@ function expandVectorCornersOf(sp, subpath = 0) {
166
188
  const c = cornerOf(sp, subpath, i);
167
189
  if (!c) return null;
168
190
  const fitted = fitCornerRadius(c, r);
169
- return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _4 => _4.corner, 'optionalAccess', _5 => _5.kind]), () => ( "round"))) : null;
191
+ return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _5 => _5.corner, 'optionalAccess', _6 => _6.kind]), () => ( "round"))) : null;
170
192
  });
171
193
  if (!cuts.some(Boolean)) return { subpath: { closed: sp.closed, anchors: sp.anchors.map(plain) }, sources: sp.anchors.map((_, i) => i) };
172
194
  const trimmed = sp.anchors.map((a, j) => {
@@ -180,7 +202,7 @@ function expandVectorCornersOf(sp, subpath = 0) {
180
202
  const s0 = from ? from.tv : 0;
181
203
  const s1 = to ? 1 - to.tu : 1;
182
204
  const seg = [pt(a), pt(_nullishCoalesce(a.out, () => ( a))), pt(_nullishCoalesce(b.in, () => ( b))), pt(b)];
183
- const part = _chunkBGNPT5PEcjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
205
+ const part = _chunk7EDLVBOOcjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
184
206
  return { out: vec(part[1]), in: vec(part[2]) };
185
207
  });
186
208
  const anchors = [];
@@ -234,7 +256,7 @@ function expandVectorCorners(subpaths) {
234
256
  function setAnchorCorner(a, radius, kind) {
235
257
  const { corner, ...rest } = a;
236
258
  const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
237
- const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _6 => _6.kind])));
259
+ const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _7 => _7.kind])));
238
260
  if (r <= 0 && !kind) return rest;
239
261
  return { ...rest, corner: { radius: r, ...k && k !== "round" ? { kind: k } : {} } };
240
262
  }
@@ -245,19 +267,561 @@ function setSubpathCorners(sp, radius, kind) {
245
267
  return { closed: sp.closed, anchors: sp.anchors.map((a) => setAnchorCorner(a, radius, kind)) };
246
268
  }
247
269
 
270
+ // src/vector/width.ts
271
+ var VECTOR_WIDTH_PROFILE_MAX_POINTS = 64;
272
+ var VECTOR_WIDTH_PROFILE_MAX_SCALE = 1e3;
273
+ var VECTOR_WIDTH_SAME_T = 1e-6;
274
+ var sym = (t, s) => ({ t, left: s, right: s });
275
+ var VECTOR_WIDTH_PRESETS = [
276
+ { id: "uniform", label: "Uniform", points: [] },
277
+ { id: "taperStart", label: "Taper start", points: [sym(0, 0), sym(1, 1)] },
278
+ { id: "taperEnd", label: "Taper end", points: [sym(0, 1), sym(1, 0)] },
279
+ { id: "taperBoth", label: "Taper both", points: [sym(0, 0), sym(0.5, 1), sym(1, 0)] },
280
+ // Full width at the ends, swelling to twice the width in the middle.
281
+ { id: "bulge", label: "Bulge", points: [sym(0, 1), sym(0.5, 2), sym(1, 1)] },
282
+ // Full width at the ends, pinched to a quarter in the middle.
283
+ { id: "pinch", label: "Pinch", points: [sym(0, 1), sym(0.5, 0.25), sym(1, 1)] },
284
+ // A round head a fifth of the way along, then a long taper to the end.
285
+ { id: "teardrop", label: "Teardrop", points: [sym(0, 0), sym(0.2, 1), sym(1, 0)] },
286
+ // Two swells between narrow necks.
287
+ { id: "wave", label: "Wave", points: [sym(0, 0.3), sym(0.25, 1), sym(0.5, 0.3), sym(0.75, 1), sym(1, 0.3)] },
288
+ // A pen stroke: a quick press, a slow lift.
289
+ { id: "pressure", label: "Pressure", points: [sym(0, 0.15), sym(0.12, 0.85), sym(0.45, 1), sym(0.8, 0.7), sym(1, 0.05)] }
290
+ ];
291
+ var VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESETS.map((p) => p.id);
292
+ var clean = (v2, max) => typeof v2 === "number" && Number.isFinite(v2) ? Math.min(Math.max(v2, 0), max) : null;
293
+ function sanitizeWidthProfile(points) {
294
+ if (!Array.isArray(points)) return [];
295
+ const out = [];
296
+ for (const p of points) {
297
+ if (!p || typeof p !== "object") continue;
298
+ const t = clean(p.t, 1);
299
+ const l = clean(p.left, VECTOR_WIDTH_PROFILE_MAX_SCALE);
300
+ const r = clean(p.right, VECTOR_WIDTH_PROFILE_MAX_SCALE);
301
+ if (t === null || l === null && r === null) continue;
302
+ out.push({ t, left: _nullishCoalesce(l, () => ( r)), right: _nullishCoalesce(r, () => ( l)) });
303
+ if (out.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) break;
304
+ }
305
+ return out.map((p, i) => [p, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([p]) => p);
306
+ }
307
+ function isUniformWidthProfile(points) {
308
+ return !_optionalChain([points, 'optionalAccess', _8 => _8.length]) || points.every((p) => p.left === 1 && p.right === 1);
309
+ }
310
+ function secant(pts, i, k) {
311
+ const a = pts[i];
312
+ const b = pts[i + 1];
313
+ return a && b && b.t - a.t > 1e-12 ? (b[k] - a[k]) / (b.t - a.t) : 0;
314
+ }
315
+ function tangent(pts, i, k) {
316
+ const last = pts.length - 1;
317
+ if (i <= 0) return secant(pts, 0, k);
318
+ if (i >= last) return secant(pts, last - 1, k);
319
+ const d0 = secant(pts, i - 1, k);
320
+ const d1 = secant(pts, i, k);
321
+ return d0 * d1 <= 0 ? 0 : 2 * d0 * d1 / (d0 + d1);
322
+ }
323
+ function interp(pts, t, k) {
324
+ const first = pts[0];
325
+ const last = pts[pts.length - 1];
326
+ if (!(t > first.t)) return first[k];
327
+ if (t >= last.t) return last[k];
328
+ let i = 0;
329
+ while (i + 1 < pts.length && pts[i + 1].t <= t) i++;
330
+ const a = pts[i];
331
+ const b = pts[i + 1];
332
+ const h = b.t - a.t;
333
+ if (h <= 1e-12) return b[k];
334
+ const u = (t - a.t) / h;
335
+ const u2 = u * u;
336
+ const u3 = u2 * u;
337
+ const w = (2 * u3 - 3 * u2 + 1) * a[k] + (u3 - 2 * u2 + u) * h * tangent(pts, i, k) + (3 * u2 - 2 * u3) * b[k] + (u3 - u2) * h * tangent(pts, i + 1, k);
338
+ return Math.max(0, w);
339
+ }
340
+ function widthProfileAt(points, t) {
341
+ if (!_optionalChain([points, 'optionalAccess', _9 => _9.length])) return { left: 1, right: 1 };
342
+ return { left: interp(points, t, "left"), right: interp(points, t, "right") };
343
+ }
344
+ function widthProfileMaxScale(points) {
345
+ if (!_optionalChain([points, 'optionalAccess', _10 => _10.length])) return 1;
346
+ return points.reduce((m, p) => Math.max(m, p.left, p.right), 0);
347
+ }
348
+ function widthProfilePreset(id) {
349
+ const p = VECTOR_WIDTH_PRESETS.find((q) => q.id === id);
350
+ return p ? p.points.map((q) => ({ ...q })) : null;
351
+ }
352
+ function widthProfilePresetId(points) {
353
+ if (isUniformWidthProfile(points)) return "uniform";
354
+ const same = (a, b) => a.length === b.length && a.every((p, i) => Math.abs(p.t - b[i].t) < 1e-9 && Math.abs(p.left - b[i].left) < 1e-9 && Math.abs(p.right - b[i].right) < 1e-9);
355
+ return _nullishCoalesce(_optionalChain([VECTOR_WIDTH_PRESETS, 'access', _11 => _11.find, 'call', _12 => _12((p) => p.points.length && same(points, p.points)), 'optionalAccess', _13 => _13.id]), () => ( "custom"));
356
+ }
357
+ function reverseWidthProfile(points) {
358
+ return points.map((p) => ({ t: 1 - p.t, left: p.right, right: p.left })).reverse();
359
+ }
360
+ function mirrorWidthProfile(points) {
361
+ return points.map((p) => ({ t: p.t, left: p.right, right: p.left }));
362
+ }
363
+ function editable(points) {
364
+ return _optionalChain([points, 'optionalAccess', _14 => _14.length]) ? points.map((p) => ({ ...p })) : [sym(0, 1), sym(1, 1)];
365
+ }
366
+ function insertWidthPoint(points, t, sides2 = {}, options = {}) {
367
+ const pts = editable(points);
368
+ const tt = Math.min(Math.max(Number.isFinite(t) ? t : 0, 0), 1);
369
+ const here = widthProfileAt(_optionalChain([points, 'optionalAccess', _15 => _15.length]) ? points : null, tt);
370
+ const p = {
371
+ t: tt,
372
+ left: _nullishCoalesce(clean(sides2.left, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.left)),
373
+ right: _nullishCoalesce(clean(sides2.right, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.right))
374
+ };
375
+ const same = pts.findIndex((q) => Math.abs(q.t - tt) <= VECTOR_WIDTH_SAME_T);
376
+ if (same >= 0 && !options.step) {
377
+ pts[same] = { ...p, t: pts[same].t };
378
+ return { points: pts, index: same };
379
+ }
380
+ let i = 0;
381
+ while (i < pts.length && pts[i].t <= tt) i++;
382
+ if (pts.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) return { points: pts, index: Math.max(0, i - 1) };
383
+ pts.splice(i, 0, p);
384
+ return { points: pts, index: i };
385
+ }
386
+ function setWidthPoint(points, index, patch) {
387
+ const pts = points.map((p2) => ({ ...p2 }));
388
+ const p = pts[index];
389
+ if (!p) return pts;
390
+ if (typeof patch.t === "number" && Number.isFinite(patch.t)) {
391
+ const lo = index > 0 ? pts[index - 1].t : 0;
392
+ const hi = index < pts.length - 1 ? pts[index + 1].t : 1;
393
+ p.t = Math.min(Math.max(patch.t, lo), Math.max(hi, lo));
394
+ }
395
+ const l = clean(patch.left, VECTOR_WIDTH_PROFILE_MAX_SCALE);
396
+ const r = clean(patch.right, VECTOR_WIDTH_PROFILE_MAX_SCALE);
397
+ if (l !== null) p.left = l;
398
+ if (r !== null) p.right = r;
399
+ return pts;
400
+ }
401
+ function removeWidthPoints(points, indices) {
402
+ const drop = new Set(indices);
403
+ return points.filter((_, i) => !drop.has(i)).map((p) => ({ ...p }));
404
+ }
405
+ function ease(u, e) {
406
+ const x = Math.min(Math.max(u, 0), 1);
407
+ const round = Math.sqrt(Math.max(0, 1 - (1 - x) * (1 - x)));
408
+ return x + (round - x) * Math.min(Math.max(e, 0), 1);
409
+ }
410
+ function taperWaveFactor(t, taper, wave) {
411
+ let k = 1;
412
+ if (taper) {
413
+ let ls = Math.max(0, _nullishCoalesce(taper.startLength, () => ( 0)));
414
+ let le = Math.max(0, _nullishCoalesce(taper.endLength, () => ( 0)));
415
+ if (ls + le > 1) {
416
+ const f = 1 / (ls + le);
417
+ ls *= f;
418
+ le *= f;
419
+ }
420
+ const sw = Math.min(Math.max(_nullishCoalesce(taper.startWidth, () => ( 0)), 0), 1);
421
+ const ew = Math.min(Math.max(_nullishCoalesce(taper.endWidth, () => ( 0)), 0), 1);
422
+ if (ls > 0 && t < ls) k = Math.min(k, sw + (1 - sw) * ease(t / ls, _nullishCoalesce(taper.startEase, () => ( 0))));
423
+ if (le > 0 && t > 1 - le) k = Math.min(k, ew + (1 - ew) * ease((1 - t) / le, _nullishCoalesce(taper.endEase, () => ( 0))));
424
+ }
425
+ if (wave) {
426
+ const cycles = _nullishCoalesce(wave.cycles, () => ( 4));
427
+ const amount = Math.min(Math.max(_nullishCoalesce(wave.amount, () => ( 0.5)), 0), 1);
428
+ if (cycles > 0 && amount > 0) {
429
+ const ph = 2 * Math.PI * t * cycles + (_nullishCoalesce(wave.phase, () => ( 0))) * Math.PI / 180;
430
+ k *= 1 - amount * (0.5 - 0.5 * Math.cos(ph));
431
+ }
432
+ }
433
+ return k;
434
+ }
435
+ function generateWidthProfile(options) {
436
+ const cycles = options.wave ? Math.max(0, _nullishCoalesce(options.wave.cycles, () => ( 4))) : 0;
437
+ const n = Math.max(8, Math.min(VECTOR_WIDTH_PROFILE_MAX_POINTS * 4, _nullishCoalesce(options.samples, () => ( Math.ceil(32 + cycles * 16)))));
438
+ const raw = [];
439
+ for (let i = 0; i <= n; i++) raw.push([i / n, taperWaveFactor(i / n, options.taper, options.wave)]);
440
+ const ts = [_optionalChain([options, 'access', _16 => _16.taper, 'optionalAccess', _17 => _17.startLength]), _optionalChain([options, 'access', _18 => _18.taper, 'optionalAccess', _19 => _19.endLength]) !== void 0 ? 1 - options.taper.endLength : void 0];
441
+ for (const t of ts) if (typeof t === "number" && t > 0 && t < 1) raw.push([t, taperWaveFactor(t, options.taper, options.wave)]);
442
+ raw.sort((a, b) => a[0] - b[0]);
443
+ const kept = simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.01)), VECTOR_WIDTH_PROFILE_MAX_POINTS);
444
+ return kept.map(([t, k]) => sym(t, k));
445
+ }
446
+ function simplifySamples(samples, tolerance, max) {
447
+ if (samples.length <= 2) return samples.slice();
448
+ const keep = new Array(samples.length).fill(false);
449
+ keep[0] = keep[samples.length - 1] = true;
450
+ const stack = [[0, samples.length - 1]];
451
+ while (stack.length) {
452
+ const [a, b] = stack.pop();
453
+ const [ta, va] = samples[a];
454
+ const [tb, vb] = samples[b];
455
+ let worst = -1;
456
+ let err = tolerance;
457
+ for (let i = a + 1; i < b; i++) {
458
+ const [t, v2] = samples[i];
459
+ const lin = tb - ta > 1e-12 ? va + (vb - va) * (t - ta) / (tb - ta) : va;
460
+ const e = Math.abs(v2 - lin);
461
+ if (e > err) {
462
+ err = e;
463
+ worst = i;
464
+ }
465
+ }
466
+ if (worst >= 0) {
467
+ keep[worst] = true;
468
+ stack.push([a, worst], [worst, b]);
469
+ }
470
+ }
471
+ let out = samples.filter((_, i) => keep[i]);
472
+ while (out.length > max) out = out.filter((_, i) => i === 0 || i === out.length - 1 || i % 2 === 0);
473
+ return out;
474
+ }
475
+ var VECTOR_PRESSURE_NOMINAL = 0.5;
476
+ function pressureWidthProfile(samples, options = {}) {
477
+ const raw = samples.filter((s) => Number.isFinite(s.t) && Number.isFinite(s.pressure)).map((s) => [Math.min(Math.max(s.t, 0), 1), Math.max(0.05, Math.min(1, Math.max(0, s.pressure)) / VECTOR_PRESSURE_NOMINAL)]);
478
+ if (raw.length < 2) return [];
479
+ if (raw.every((s) => Math.abs(s[1] - raw[0][1]) < 1e-6)) return [];
480
+ return simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.02)), VECTOR_WIDTH_PROFILE_MAX_POINTS).map(([t, k]) => sym(t, Math.round(k * 1e3) / 1e3));
481
+ }
482
+
483
+ // src/vector/varstroke.ts
484
+ var SAMPLES_PER_SUBPATH = 128;
485
+ var MAX_DASHES = 5e3;
486
+ var sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]];
487
+ var along = (p, v2, k) => [p[0] + v2[0] * k, p[1] + v2[1] * k];
488
+ var lerp = (a, b, u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u];
489
+ var dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
490
+ var unitOf = (v2) => {
491
+ const l = Math.hypot(v2[0], v2[1]);
492
+ return l > 0 ? [v2[0] / l, v2[1] / l] : [1, 0];
493
+ };
494
+ var leftNormal = (d) => [d[1], -d[0]];
495
+ function flatten(segments, closed, tol) {
496
+ const pts = [];
497
+ const corner = [];
498
+ const push = (p, isCorner) => {
499
+ const last = pts[pts.length - 1];
500
+ if (last && dist(last, p) <= 1e-9) {
501
+ corner[corner.length - 1] ||= isCorner;
502
+ return;
503
+ }
504
+ pts.push(p);
505
+ corner.push(isCorner);
506
+ };
507
+ for (const seg of segments) {
508
+ const flat = _chunk7EDLVBOOcjs.flattenChain.call(void 0, [seg], tol);
509
+ if (dist(seg[0], seg[seg.length - 1]) <= 1e-12 && flat.length > 1) flat.push(seg[seg.length - 1]);
510
+ flat.forEach((p, i) => push(p, i === 0 || i === flat.length - 1));
511
+ }
512
+ if (closed && pts.length > 2 && dist(pts[0], pts[pts.length - 1]) > 1e-9) push(pts[0], true);
513
+ if (pts.length < 2) return null;
514
+ let total = 0;
515
+ for (let i = 1; i < pts.length; i++) total += dist(pts[i - 1], pts[i]);
516
+ if (!(total > 0)) return null;
517
+ const step = Math.max(total / SAMPLES_PER_SUBPATH, tol);
518
+ const fine = [pts[0]];
519
+ const fineCorner = [corner[0]];
520
+ for (let i = 1; i < pts.length; i++) {
521
+ const n = Math.max(1, Math.ceil(dist(pts[i - 1], pts[i]) / step));
522
+ for (let k = 1; k <= n; k++) {
523
+ fine.push(k === n ? pts[i] : lerp(pts[i - 1], pts[i], k / n));
524
+ fineCorner.push(k === n ? corner[i] : false);
525
+ }
526
+ }
527
+ const s = [0];
528
+ for (let i = 1; i < fine.length; i++) s.push(s[i - 1] + dist(fine[i - 1], fine[i]));
529
+ return { pts: fine, s, corner: fineCorner, closed };
530
+ }
531
+ function dashIntervals(pattern, offset, len2) {
532
+ let pat = pattern.map((v2) => Number.isFinite(v2) ? Math.max(0, v2) : 0);
533
+ if (pat.length % 2 === 1) pat = [...pat, ...pat];
534
+ const period = pat.reduce((a, b) => a + b, 0);
535
+ if (pat.length < 2 || !(period > 1e-9) || !(len2 > 0)) return [[0, len2]];
536
+ let pos = ((Number.isFinite(offset) ? offset : 0) % period + period) % period;
537
+ let idx = 0;
538
+ while (pos >= pat[idx]) {
539
+ pos -= pat[idx];
540
+ idx = (idx + 1) % pat.length;
541
+ }
542
+ const out = [];
543
+ let s = 0;
544
+ let guard = 0;
545
+ while (s < len2 && guard++ < MAX_DASHES * pat.length) {
546
+ const e = Math.min(s + pat[idx] - pos, len2);
547
+ if (idx % 2 === 0 && e >= s) out.push([s, e]);
548
+ s += pat[idx] - pos;
549
+ pos = 0;
550
+ idx = (idx + 1) % pat.length;
551
+ }
552
+ return out;
553
+ }
554
+ function at(poly, t) {
555
+ let i = 1;
556
+ while (i < poly.pts.length - 1 && poly.s[i] <= t) i++;
557
+ const a = poly.s[i - 1];
558
+ const b = poly.s[i];
559
+ const u = b > a ? Math.min(Math.max((t - a) / (b - a), 0), 1) : 0;
560
+ return [lerp(poly.pts[i - 1], poly.pts[i], u), i];
561
+ }
562
+ function arcPoints(c, a0, sweep, r0, r1, tol) {
563
+ const r = Math.max(r0, r1);
564
+ if (!(r > tol * 0.5) || !Number.isFinite(sweep)) return [];
565
+ const stepAngle = 2 * Math.acos(Math.min(Math.max(1 - tol / r, -1), 1));
566
+ const n = Math.min(128, Math.max(2, Math.ceil(Math.abs(sweep) / Math.max(stepAngle, 1e-3))));
567
+ const out = [];
568
+ for (let k = 1; k < n; k++) {
569
+ const f = k / n;
570
+ const a = a0 + sweep * f;
571
+ const rr = r0 + (r1 - r0) * f;
572
+ out.push([c[0] + Math.cos(a) * rr, c[1] + Math.sin(a) * rr]);
573
+ }
574
+ return out;
575
+ }
576
+ function crossing(a0, a1, b0, b1) {
577
+ const r = sub2(a1, a0);
578
+ const q = sub2(b1, b0);
579
+ const den = r[0] * q[1] - r[1] * q[0];
580
+ if (Math.abs(den) < 1e-12) return null;
581
+ const w = sub2(b0, a0);
582
+ const t = (w[0] * q[1] - w[1] * q[0]) / den;
583
+ const u = (w[0] * r[1] - w[1] * r[0]) / den;
584
+ return t > 1e-9 && t <= 1 && u >= 0 && u < 1 - 1e-9 ? [a0[0] + r[0] * t, a0[1] + r[1] * t] : null;
585
+ }
586
+ function variableStrokeRings(path, options) {
587
+ const half = options.width / 2;
588
+ if (!(half > 0) || !Number.isFinite(half)) return [];
589
+ const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.05;
590
+ const join = _nullishCoalesce(options.join, () => ( "miter"));
591
+ const cap = _nullishCoalesce(options.cap, () => ( "butt"));
592
+ const miterLimit = options.miterLimit && options.miterLimit >= 1 ? options.miterLimit : 4;
593
+ const rings = [];
594
+ const push = (out, p) => {
595
+ const last = out[out.length - 1];
596
+ if (!last || dist(last, p) > 1e-12) out.push(p);
597
+ };
598
+ for (const contour of _chunk7EDLVBOOcjs.pathToContours.call(void 0, path)) {
599
+ if (!contour.segments.length) continue;
600
+ const poly = flatten(contour.segments, contour.closed, Math.min(tol, half) / 4);
601
+ if (!poly) continue;
602
+ const len2 = poly.s[poly.s.length - 1];
603
+ const sides2 = (s) => {
604
+ const w = widthProfileAt(options.profile, s / len2);
605
+ return [w.left * half, w.right * half];
606
+ };
607
+ const intervals = _optionalChain([options, 'access', _20 => _20.dash, 'optionalAccess', _21 => _21.some, 'call', _22 => _22((v2) => v2 > 0)]) ? dashIntervals(options.dash, _nullishCoalesce(options.dashOffset, () => ( 0)), len2) : [[0, len2]];
608
+ const whole = intervals.length === 1 && intervals[0][0] <= 0 && intervals[0][1] >= len2;
609
+ for (const [a, b] of intervals) {
610
+ const [pa, ia] = at(poly, a);
611
+ const [pb, ib] = at(poly, b);
612
+ const pts = [{ p: pa, s: a, corner: poly.corner[0] && a <= 0 }];
613
+ for (let i = ia; i < ib; i++) pts.push({ p: poly.pts[i], s: poly.s[i], corner: poly.corner[i] });
614
+ pts.push({ p: pb, s: b, corner: false });
615
+ const run = pts.filter((q, i) => i === 0 || dist(q.p, pts[i - 1].p) > 1e-9);
616
+ if (run.length < 2) {
617
+ const [l, r] = sides2(a);
618
+ const w2 = (l + r) / 2;
619
+ if (cap === "round" && w2 > 0) rings.push([[pa[0] + w2, pa[1]], ...arcPoints(pa, 0, 2 * Math.PI, w2, w2, tol)]);
620
+ continue;
621
+ }
622
+ const closed = whole && poly.closed;
623
+ const m = run.length - 1;
624
+ const dirs = [];
625
+ for (let i = 0; i < m; i++) dirs.push(unitOf(sub2(run[i + 1].p, run[i].p)));
626
+ const normals = dirs.map(leftNormal);
627
+ const w = run.map((q) => sides2(q.s));
628
+ const side = (sigma) => {
629
+ const k = sigma > 0 ? 0 : 1;
630
+ const S = (i) => along(run[i].p, normals[i], sigma * w[i][k]);
631
+ const E = (i) => along(run[i + 1].p, normals[i], sigma * w[i + 1][k]);
632
+ const out = [S(0)];
633
+ for (let i = 0; i < m; i++) {
634
+ const seam = i === m - 1;
635
+ if (seam && !closed) {
636
+ push(out, E(i));
637
+ break;
638
+ }
639
+ const j = seam ? 0 : i + 1;
640
+ const p = run[i + 1].p;
641
+ const d0 = dirs[i];
642
+ const d1 = dirs[j];
643
+ const cross2 = d0[0] * d1[1] - d0[1] * d1[0];
644
+ const dot2 = d0[0] * d1[0] + d0[1] * d1[1];
645
+ const r0 = w[i + 1][k];
646
+ const r1 = w[j][k];
647
+ const e = E(i);
648
+ const st = S(j);
649
+ if (Math.abs(cross2) < 1e-12 && dot2 > 0) {
650
+ push(out, e);
651
+ if (!seam) push(out, st);
652
+ continue;
653
+ }
654
+ const uturn = Math.abs(cross2) < 1e-12;
655
+ const outer = uturn ? sigma > 0 : cross2 * sigma > 0;
656
+ if (!outer) {
657
+ const x = seam ? null : crossing(out[out.length - 1], e, st, E(j));
658
+ if (x) push(out, x);
659
+ else {
660
+ push(out, e);
661
+ push(out, p);
662
+ if (!seam) push(out, st);
663
+ }
664
+ continue;
665
+ }
666
+ push(out, e);
667
+ const theta = uturn ? Math.PI : Math.atan2(cross2, dot2);
668
+ const r = (r0 + r1) / 2;
669
+ const kind = run[i + 1].corner || seam && poly.corner[0] ? join : "round";
670
+ if (r > 1e-9) {
671
+ if (kind === "round") {
672
+ if (r * (1 - Math.cos(Math.abs(theta) / 2)) > tol * 0.25) {
673
+ for (const q of arcPoints(p, Math.atan2(e[1] - p[1], e[0] - p[0]), theta, r0, r1, tol)) push(out, q);
674
+ }
675
+ } else if (kind === "miter") {
676
+ const halfCos = Math.cos(theta / 2);
677
+ const bis = [normals[i][0] + normals[j][0], normals[i][1] + normals[j][1]];
678
+ const bl = Math.hypot(bis[0], bis[1]);
679
+ if (halfCos > 1e-6 && 1 / halfCos <= miterLimit && bl > 1e-9) push(out, along(p, bis, sigma * r / halfCos / bl));
680
+ }
681
+ }
682
+ if (!seam) push(out, st);
683
+ }
684
+ if (closed && out.length > 1 && dist(out[0], out[out.length - 1]) <= 1e-12) out.pop();
685
+ return out;
686
+ };
687
+ const left = side(1);
688
+ const right = side(-1);
689
+ if (closed) {
690
+ rings.push(left, right.reverse());
691
+ continue;
692
+ }
693
+ const capPoints = (atStart) => {
694
+ if (cap === "butt") return [];
695
+ const i = atStart ? 0 : m;
696
+ const d = atStart ? dirs[0] : dirs[m - 1];
697
+ const [l, r] = w[i];
698
+ if (!(l + r > 1e-9)) return [];
699
+ const p = run[i].p;
700
+ const n = leftNormal(d);
701
+ const L = along(p, n, l);
702
+ const R = along(p, n, -r);
703
+ if (cap === "round") {
704
+ const a0 = Math.atan2(d[1], d[0]) + (atStart ? Math.PI / 2 : -Math.PI / 2);
705
+ return arcPoints(p, a0, Math.PI, atStart ? r : l, atStart ? l : r, tol);
706
+ }
707
+ const ext = (l + r) / 2;
708
+ const o = atStart ? [-d[0], -d[1]] : d;
709
+ return atStart ? [along(R, o, ext), along(L, o, ext)] : [along(L, o, ext), along(R, o, ext)];
710
+ };
711
+ const ring = [];
712
+ for (const q of [...left, ...capPoints(false), ...right.reverse(), ...capPoints(true)]) push(ring, q);
713
+ if (ring.length > 2) rings.push(ring);
714
+ }
715
+ }
716
+ return rings;
717
+ }
718
+ function variableStrokeOutline(path, options) {
719
+ const pieces = variableStrokeRings(path, options);
720
+ if (!pieces.length) return [];
721
+ const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.05;
722
+ let polygons;
723
+ try {
724
+ polygons = _chunk7EDLVBOOcjs.polygonBoolean.call(void 0, "union", [{ rings: pieces, fillRule: "nonzero" }]);
725
+ } catch (e2) {
726
+ return [];
727
+ }
728
+ const out = [];
729
+ for (const polygon2 of polygons) {
730
+ for (const ring of polygon2) {
731
+ if (ring.length < 3) continue;
732
+ const cubics = _chunk7EDLVBOOcjs.fitCubics.call(void 0, ring, tol, { closed: true, cornerAngleDeg: 25 });
733
+ if (!cubics.length) continue;
734
+ out.push({ type: "M", x: cubics[0].p0[0], y: cubics[0].p0[1] });
735
+ cubics.forEach((c, i) => {
736
+ if (_chunk7EDLVBOOcjs.isStraightCubic.call(void 0, c, tol * 0.02)) {
737
+ if (i < cubics.length - 1) out.push({ type: "L", x: c.p3[0], y: c.p3[1] });
738
+ } else out.push({ type: "C", x1: c.p1[0], y1: c.p1[1], x2: c.p2[0], y2: c.p2[1], x: c.p3[0], y: c.p3[1] });
739
+ });
740
+ out.push({ type: "Z" });
741
+ }
742
+ }
743
+ return out;
744
+ }
745
+ function nearestStrokePosition(path, p, only) {
746
+ let best = null;
747
+ _chunk7EDLVBOOcjs.pathToContours.call(void 0, path).forEach((c, ci) => {
748
+ if (!c.segments.length || only !== void 0 && ci !== only) return;
749
+ const lengths = c.segments.map((s) => _chunk7EDLVBOOcjs.segmentLength.call(void 0, s));
750
+ const total = lengths.reduce((a, b) => a + b, 0);
751
+ if (!(total > 0)) return;
752
+ let acc = 0;
753
+ c.segments.forEach((seg, si) => {
754
+ const near2 = _chunk7EDLVBOOcjs.segmentNearest.call(void 0, seg, p);
755
+ if (!best || near2.distance < best.distance) {
756
+ const s = acc + _chunk7EDLVBOOcjs.segmentLength.call(void 0, seg, 0, near2.t);
757
+ const tan = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, seg, near2.t);
758
+ const pt2 = seg.length === 2 ? lerp(seg[0], seg[1], near2.t) : evalSeg(seg, near2.t);
759
+ const n = leftNormal([tan[0], tan[1]]);
760
+ const side = (p[0] - pt2[0]) * n[0] + (p[1] - pt2[1]) * n[1] >= 0 ? "left" : "right";
761
+ best = { contour: ci, t: Math.min(Math.max(s / total, 0), 1), distance: near2.distance, side, point: pt2, tangent: [tan[0], tan[1]], length: total };
762
+ }
763
+ acc += lengths[si];
764
+ });
765
+ });
766
+ return best;
767
+ }
768
+ function evalSeg(seg, t) {
769
+ let pts = seg.map((q) => [q[0], q[1]]);
770
+ while (pts.length > 1) pts = pts.slice(1).map((q, i) => lerp(pts[i], q, t));
771
+ return pts[0];
772
+ }
773
+ function strokePositionPoint(path, index, t) {
774
+ const c = _chunk7EDLVBOOcjs.pathToContours.call(void 0, path)[index];
775
+ if (!_optionalChain([c, 'optionalAccess', _23 => _23.segments, 'access', _24 => _24.length])) return null;
776
+ const lengths = c.segments.map((s) => _chunk7EDLVBOOcjs.segmentLength.call(void 0, s));
777
+ const total = lengths.reduce((a, b) => a + b, 0);
778
+ let want = Math.min(Math.max(t, 0), 1) * total;
779
+ for (let i = 0; i < c.segments.length; i++) {
780
+ const seg = c.segments[i];
781
+ if (want <= lengths[i] + 1e-12 || i === c.segments.length - 1) {
782
+ let lo = 0;
783
+ let hi = 1;
784
+ const target = Math.min(want, lengths[i]);
785
+ for (let k = 0; k < 40; k++) {
786
+ const mid = (lo + hi) / 2;
787
+ if (_chunk7EDLVBOOcjs.segmentLength.call(void 0, seg, 0, mid) < target) lo = mid;
788
+ else hi = mid;
789
+ }
790
+ const u = (lo + hi) / 2;
791
+ const tan = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, seg, u);
792
+ return { point: evalSeg(seg, u), tangent: [tan[0], tan[1]] };
793
+ }
794
+ want -= lengths[i];
795
+ }
796
+ return null;
797
+ }
798
+
248
799
  // src/vector/stroke.ts
249
800
  var KAPPA = 0.5522847498307936;
250
801
  var NOTCH = 0.7;
251
802
  function isClosedBezierShape(path) {
252
- const contours = _chunkBGNPT5PEcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
803
+ const contours = _chunk7EDLVBOOcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
253
804
  return contours.length > 0 && contours.every((c) => c.closed);
254
805
  }
255
806
  function effectiveStrokeAlign(style, closedShape) {
256
807
  const a = _nullishCoalesce(style.strokeAlign, () => ( "center"));
257
808
  return closedShape ? a : "center";
258
809
  }
810
+ function strokeWidthProfileOf(style) {
811
+ if (!_optionalChain([style, 'access', _25 => _25.strokeWidthProfile, 'optionalAccess', _26 => _26.length])) return null;
812
+ const p = sanitizeWidthProfile(style.strokeWidthProfile);
813
+ return isUniformWidthProfile(p) ? null : p;
814
+ }
259
815
  function hasStrokeExtras(style) {
260
- return (_nullishCoalesce(style.strokeAlign, () => ( "center"))) !== "center" || !!style.startArrow || !!style.endArrow;
816
+ return (_nullishCoalesce(style.strokeAlign, () => ( "center"))) !== "center" || !!style.startArrow || !!style.endArrow || !!strokeWidthProfileOf(style);
817
+ }
818
+ function vectorStrokeLineArea(parts, style, tolerance) {
819
+ const profile = strokeWidthProfileOf(style);
820
+ const dash = _optionalChain([style, 'access', _27 => _27.strokeDasharray, 'optionalAccess', _28 => _28.length]) ? { dash: style.strokeDasharray } : {};
821
+ const join = _nullishCoalesce(style.strokeLinejoin, () => ( "miter"));
822
+ const cap = _nullishCoalesce(style.strokeLinecap, () => ( "butt"));
823
+ if (profile) return variableStrokeOutline(parts.line, { width: parts.drawWidth, profile, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
824
+ return _chunk7EDLVBOOcjs.outlineStroke.call(void 0, parts.line, { width: parts.drawWidth, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
261
825
  }
262
826
  var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
263
827
  function arrowheadWeight(style, end) {
@@ -334,27 +898,27 @@ function arrowheadPath(kind, tip, dir, hw) {
334
898
  }
335
899
  }
336
900
  var toVec = (p) => ({ x: p[0], y: p[1] });
337
- var segLen = (s) => _chunkBGNPT5PEcjs.segmentLength.call(void 0, s);
901
+ var segLen = (s) => _chunk7EDLVBOOcjs.segmentLength.call(void 0, s);
338
902
  function endOf(segs, atStart) {
339
903
  const long = (s2) => segLen(s2) > 1e-9;
340
904
  if (atStart) {
341
905
  const s2 = _nullishCoalesce(segs.find(long), () => ( segs[0]));
342
- const t2 = _chunkBGNPT5PEcjs.segmentTangent.call(void 0, s2, 0);
906
+ const t2 = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, s2, 0);
343
907
  return { p: toVec(segs[0][0]), out: v(-t2[0], -t2[1]) };
344
908
  }
345
909
  const s = _nullishCoalesce([...segs].reverse().find(long), () => ( segs[segs.length - 1]));
346
- const t = _chunkBGNPT5PEcjs.segmentTangent.call(void 0, s, 1);
910
+ const t = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, s, 1);
347
911
  const last = segs[segs.length - 1];
348
912
  return { p: toVec(last[last.length - 1]), out: v(t[0], t[1]) };
349
913
  }
350
- function pointAlong(segs, fromStart, dist3) {
914
+ function pointAlong(segs, fromStart, dist4) {
351
915
  const order = fromStart ? segs : [...segs].reverse();
352
916
  let acc = 0;
353
917
  for (const s of order) {
354
918
  const l = segLen(s);
355
- if (acc + l >= dist3) {
356
- const want = fromStart ? dist3 - acc : l - (dist3 - acc);
357
- const pts = _chunkBGNPT5PEcjs.trimSegments.call(void 0, [s], 0, want);
919
+ if (acc + l >= dist4) {
920
+ const want = fromStart ? dist4 - acc : l - (dist4 - acc);
921
+ const pts = _chunk7EDLVBOOcjs.trimSegments.call(void 0, [s], 0, want);
358
922
  const p = pts.length ? pts[0][pts[0].length - 1] : s[0];
359
923
  return toVec(p);
360
924
  }
@@ -364,7 +928,7 @@ function pointAlong(segs, fromStart, dist3) {
364
928
  }
365
929
  function vectorStrokeParts(path, style) {
366
930
  const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
367
- const contours = _chunkBGNPT5PEcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
931
+ const contours = _chunk7EDLVBOOcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
368
932
  const closedShape = contours.length > 0 && contours.every((c) => c.closed);
369
933
  const align = effectiveStrokeAlign(style, closedShape);
370
934
  const drawWidth = align === "center" ? width : width * 2;
@@ -404,42 +968,54 @@ function vectorStrokeParts(path, style) {
404
968
  if (!s0 && !s1) return c;
405
969
  const lengths = c.segments.map(segLen);
406
970
  const total = lengths.reduce((a, b) => a + b, 0);
407
- return { closed: false, segments: total - s1 > s0 ? _chunkBGNPT5PEcjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
971
+ return { closed: false, segments: total - s1 > s0 ? _chunk7EDLVBOOcjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
408
972
  });
409
- return { align, line: _chunkBGNPT5PEcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
973
+ return { align, line: _chunk7EDLVBOOcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
410
974
  }
411
975
  function vectorStrokeOutlinePath(path, style) {
412
976
  const parts = vectorStrokeParts(path, style);
413
- const stroke = _chunkBGNPT5PEcjs.outlineStroke.call(void 0, parts.line, {
414
- width: parts.drawWidth,
415
- join: _nullishCoalesce(style.strokeLinejoin, () => ( "miter")),
416
- cap: _nullishCoalesce(style.strokeLinecap, () => ( "butt")),
417
- ..._optionalChain([style, 'access', _7 => _7.strokeDasharray, 'optionalAccess', _8 => _8.length]) ? { dash: style.strokeDasharray } : {}
418
- });
977
+ const stroke = vectorStrokeLineArea(parts, style);
419
978
  const fill = { path, fillRule: _nullishCoalesce(style.fillRule, () => ( "nonzero")) };
420
979
  let area = stroke;
421
- if (parts.align === "inside") area = _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "intersection", [stroke, fill]).path;
422
- else if (parts.align === "outside") area = _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "difference", [stroke, fill]).path;
980
+ if (parts.align === "inside") area = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "intersection", [stroke, fill]).path;
981
+ else if (parts.align === "outside") area = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "difference", [stroke, fill]).path;
423
982
  if (!parts.heads.length) return area;
424
- return _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
983
+ return _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
425
984
  }
426
985
  function hitTestVectorStroke(path, p, style, tolerance = 0) {
427
986
  const parts = vectorStrokeParts(path, style);
428
987
  const point = [p.x, p.y];
429
988
  const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
430
989
  for (const h of parts.heads) {
431
- if (_chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
990
+ if (_chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
432
991
  }
433
992
  if (!parts.line.length) return false;
434
- const d = _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, parts.line, point);
435
- if (parts.align === "center") return d <= width / 2 + tolerance;
436
- if (d > width + tolerance) return false;
993
+ const profile = strokeWidthProfileOf(style);
994
+ let d;
995
+ let reach = width / 2;
996
+ if (profile) {
997
+ const near2 = nearestStrokePosition(parts.line, point);
998
+ if (!near2) return false;
999
+ d = near2.distance;
1000
+ const w = widthProfileAt(profile, near2.t);
1001
+ reach = width / 2 * (near2.side === "left" ? w.left : w.right);
1002
+ if (parts.align !== "center") reach *= 2;
1003
+ const atEnd = near2.t <= 1e-9 || near2.t >= 1 - 1e-9;
1004
+ if (atEnd && (style.strokeLinecap === "round" || style.strokeLinecap === "square") && d > reach) {
1005
+ reach = Math.max(reach, width / 2 * (w.left + w.right) / 2);
1006
+ }
1007
+ } else {
1008
+ d = _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, parts.line, point);
1009
+ if (parts.align !== "center") reach = width;
1010
+ }
1011
+ if (parts.align === "center") return d <= reach + tolerance;
1012
+ if (d > reach + tolerance) return false;
437
1013
  if (d <= tolerance) return true;
438
- const inside = _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
1014
+ const inside = _chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
439
1015
  return parts.align === "inside" ? inside : !inside;
440
1016
  }
441
1017
  function vectorStrokeReach(style) {
442
- const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
1018
+ const width = (_nullishCoalesce(style.strokeWidth, () => ( 1))) * widthProfileMaxScale(strokeWidthProfileOf(style));
443
1019
  const align = _nullishCoalesce(style.strokeAlign, () => ( "center"));
444
1020
  let reach = align === "center" ? width / 2 : align === "outside" ? width : 0;
445
1021
  if (style.strokeLinejoin !== "round" && style.strokeLinejoin !== "bevel") reach *= 4;
@@ -511,7 +1087,7 @@ function vectorNodeToBezier(node) {
511
1087
  }
512
1088
  function vectorPathData(input, precision = 3) {
513
1089
  const subpaths = Array.isArray(input) ? input : input.subpaths;
514
- return _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
1090
+ return _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
515
1091
  }
516
1092
  function inferAnchorType(a) {
517
1093
  if (isOnAnchor(a, a.in) || isOnAnchor(a, a.out)) return "corner";
@@ -527,7 +1103,7 @@ function inferAnchorType(a) {
527
1103
  return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
528
1104
  }
529
1105
  function bezierToSubpaths(input) {
530
- const path = _chunkBGNPT5PEcjs.toBezierPath.call(void 0, input);
1106
+ const path = _chunk7EDLVBOOcjs.toBezierPath.call(void 0, input);
531
1107
  const out = [];
532
1108
  let current = null;
533
1109
  const last = () => current.anchors[current.anchors.length - 1];
@@ -624,14 +1200,14 @@ function nodeMatrix(node) {
624
1200
  function vectorPathWorldBezier(node, parent = IDENTITY_MATRIX) {
625
1201
  const m = multiplyMatrices(nodeMatrix(node), parent);
626
1202
  const path = subpathsToBezier(node.subpaths);
627
- return isIdentityMatrix(m) ? path : _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, path, m);
1203
+ return isIdentityMatrix(m) ? path : _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, m);
628
1204
  }
629
1205
  function vectorNodeBounds(node, parent = IDENTITY_MATRIX) {
630
1206
  if (node.type === "group") {
631
1207
  const m = multiplyMatrices(nodeMatrix(node), parent);
632
1208
  return unionBounds(node.children.map((c) => vectorNodeBounds(c, m)));
633
1209
  }
634
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
1210
+ const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
635
1211
  if (!b) return null;
636
1212
  return { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY };
637
1213
  }
@@ -665,9 +1241,9 @@ function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX) {
665
1241
  const path = vectorPathWorldBezier(node, parent);
666
1242
  if (path.length === 0) return false;
667
1243
  const point = [p.x, p.y];
668
- if (hasPaint(node.fill) && _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
1244
+ if (hasPaint(node.fill) && _chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
669
1245
  if (hasPaint(node.stroke)) return hitTestVectorStroke(path, p, node, tolerance);
670
- return _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
1246
+ return _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
671
1247
  }
672
1248
  function vectorStrokeOutline(node, parent = IDENTITY_MATRIX) {
673
1249
  if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return [];
@@ -687,9 +1263,9 @@ function nearestSegment(node, p) {
687
1263
  const count = subpathSegmentCount(sp);
688
1264
  for (let i = 0; i < count; i++) {
689
1265
  const seg = subpathSegment(sp, i);
690
- const hit = _chunkBGNPT5PEcjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
1266
+ const hit = _chunk7EDLVBOOcjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
691
1267
  if (!best || hit.distance < best.distance) {
692
- const pt2 = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, hit.t);
1268
+ const pt2 = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, hit.t);
693
1269
  best = { subpath: s, segment: i, t: hit.t, point: { x: pt2[0], y: pt2[1] }, distance: hit.distance };
694
1270
  }
695
1271
  }
@@ -749,8 +1325,10 @@ function bakeIntoGeometry(node, m) {
749
1325
  if (isIdentityMatrix(m)) return node;
750
1326
  if (node.type === "group") return { ...node, children: node.children.map((c) => transformVectorNode(c, m)) };
751
1327
  const path = node;
1328
+ const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _29 => _29.strokeWidthProfile, 'optionalAccess', _30 => _30.length]);
752
1329
  return {
753
1330
  ...path,
1331
+ ...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
754
1332
  subpaths: path.subpaths.map((sp) => ({
755
1333
  closed: sp.closed,
756
1334
  anchors: sp.anchors.map((a) => {
@@ -861,7 +1439,7 @@ function insertAnchor(sp, segment, t) {
861
1439
  const ia = segment;
862
1440
  const ib = (segment + 1) % n;
863
1441
  const seg = subpathSegment(sp, segment);
864
- const [left, right] = _chunkBGNPT5PEcjs.splitSegment.call(void 0, seg, t);
1442
+ const [left, right] = _chunk7EDLVBOOcjs.splitSegment.call(void 0, seg, t);
865
1443
  const mid = left[left.length - 1];
866
1444
  const anchor = { x: mid[0], y: mid[1] };
867
1445
  if (seg.length === 4) {
@@ -915,8 +1493,8 @@ function ellipseSubpath(b) {
915
1493
  ]
916
1494
  };
917
1495
  }
918
- function polygonSubpath(b, sides = 6) {
919
- const n = Math.max(3, Math.round(sides));
1496
+ function polygonSubpath(b, sides2 = 6) {
1497
+ const n = Math.max(3, Math.round(sides2));
920
1498
  const cx = b.x + b.width / 2;
921
1499
  const cy = b.y + b.height / 2;
922
1500
  const anchors = [];
@@ -957,7 +1535,7 @@ function vectorCornersAttribute(subpaths, precision = 3) {
957
1535
  subpaths.forEach(
958
1536
  (sp, s) => sp.anchors.forEach((a, i) => {
959
1537
  if (!a.corner || !(a.corner.radius > 0)) return;
960
- out.push(`${s}.${i}:${_chunkBGNPT5PEcjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
1538
+ out.push(`${s}.${i}:${_chunk7EDLVBOOcjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
961
1539
  })
962
1540
  );
963
1541
  return out.join(" ");
@@ -966,7 +1544,7 @@ function applyVectorCornersAttribute(subpaths, value) {
966
1544
  for (const item of value.trim().split(/\s+/)) {
967
1545
  const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
968
1546
  if (!m) continue;
969
- const a = _optionalChain([subpaths, 'access', _9 => _9[+m[1]], 'optionalAccess', _10 => _10.anchors, 'access', _11 => _11[+m[2]]]);
1547
+ const a = _optionalChain([subpaths, 'access', _31 => _31[+m[1]], 'optionalAccess', _32 => _32.anchors, 'access', _33 => _33[+m[2]]]);
970
1548
  const radius = parseFloat(m[3]);
971
1549
  if (!a || !(radius > 0)) continue;
972
1550
  const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
@@ -978,21 +1556,49 @@ var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;")
978
1556
  function vectorPaintToSvg(paint, gradientId) {
979
1557
  if (!hasPaint(paint)) return { value: "none", gradient: null };
980
1558
  if (typeof paint === "string") return { value: paint, gradient: null };
981
- return { value: `url(#${gradientId})`, gradient: _chunkBGNPT5PEcjs.gradientToSvgDef.call(void 0, paint, gradientId) };
1559
+ return { value: `url(#${gradientId})`, gradient: _chunk7EDLVBOOcjs.gradientToSvgDef.call(void 0, paint, gradientId) };
982
1560
  }
1561
+ var partsCache = /* @__PURE__ */ new WeakMap();
983
1562
  function vectorStrokeSvgParts(node, clipId, precision = 3) {
984
1563
  if (!hasPaint(node.stroke) || !hasStrokeExtras(node)) return void 0;
1564
+ const hit = partsCache.get(node);
1565
+ if (hit && hit.clipId === clipId && hit.precision === precision) return hit.parts;
1566
+ const parts = computeStrokeSvgParts(node, clipId, precision);
1567
+ partsCache.set(node, { clipId, precision, parts });
1568
+ return parts;
1569
+ }
1570
+ function computeStrokeSvgParts(node, clipId, precision) {
985
1571
  const path = subpathsToBezier(node.subpaths);
986
1572
  const parts = vectorStrokeParts(path, node);
987
- if (parts.align === "center" && !parts.heads.length) return void 0;
1573
+ const profiled = !!strokeWidthProfileOf(node);
1574
+ if (parts.align === "center" && !parts.heads.length && !profiled) return void 0;
988
1575
  let clip = null;
989
1576
  if (parts.align !== "center") {
990
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, path);
1577
+ const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, path);
991
1578
  const pad = vectorStrokeReach(node) + 1;
992
1579
  const bounds = b ? { x: round2(b.minX - pad), y: round2(b.minY - pad), width: round2(b.maxX - b.minX + 2 * pad), height: round2(b.maxY - b.minY + 2 * pad) } : { x: 0, y: 0, width: 0, height: 0 };
993
1580
  clip = { kind: parts.align === "inside" ? "clip" : "mask", id: clipId, bounds };
994
1581
  }
995
- return { align: parts.align, d: _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, parts.line, { precision }), width: parts.drawWidth, clip, heads: parts.heads.map((h) => _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, h.path, { precision })) };
1582
+ return {
1583
+ align: parts.align,
1584
+ d: _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, parts.line, { precision }),
1585
+ width: parts.drawWidth,
1586
+ ...profiled && (_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0 ? { outline: _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, vectorStrokeLineArea(parts, node), { precision }) } : {},
1587
+ clip,
1588
+ heads: parts.heads.map((h) => _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, h.path, { precision }))
1589
+ };
1590
+ }
1591
+ function widthProfileToAttribute(points, precision = 4) {
1592
+ return points.map((p) => [p.t, p.left, p.right].map((n) => _chunk7EDLVBOOcjs.formatNumber.call(void 0, n, precision)).join(",")).join(";");
1593
+ }
1594
+ function widthProfileFromAttribute(value) {
1595
+ if (!value) return [];
1596
+ return sanitizeWidthProfile(
1597
+ value.split(";").map((part) => {
1598
+ const [t, left, right] = part.split(",").map((n) => parseFloat(n));
1599
+ return { t, left, right: Number.isFinite(right) ? right : left };
1600
+ })
1601
+ );
996
1602
  }
997
1603
  var round2 = (n) => Math.round(n * 100) / 100;
998
1604
  function gradientMarkup(def) {
@@ -1007,11 +1613,11 @@ function vectorDocumentToSvg(doc, options = {}) {
1007
1613
  const pretty = options.pretty !== false;
1008
1614
  const defs = [];
1009
1615
  const lines = [];
1010
- const num = (n) => _chunkBGNPT5PEcjs.formatNumber.call(void 0, n, precision);
1616
+ const num = (n) => _chunk7EDLVBOOcjs.formatNumber.call(void 0, n, precision);
1011
1617
  const indent = (depth) => pretty ? " ".repeat(depth) : "";
1012
1618
  const common = (node) => {
1013
1619
  let out2 = ` id="${escapeAttr(node.id)}"`;
1014
- if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunkBGNPT5PEcjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
1620
+ if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunk7EDLVBOOcjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
1015
1621
  if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
1016
1622
  if (node.visible === false) out2 += ` display="none"`;
1017
1623
  return out2;
@@ -1030,13 +1636,13 @@ function vectorDocumentToSvg(doc, options = {}) {
1030
1636
  if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
1031
1637
  const d = vectorPathData(node, precision);
1032
1638
  const corners2 = vectorCornersAttribute(node.subpaths, precision);
1033
- const live = corners2 ? ` data-vector-d="${_chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
1639
+ const live = corners2 ? ` data-vector-d="${_chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
1034
1640
  const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
1035
1641
  const lineAttrs = (width) => {
1036
1642
  let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
1037
1643
  if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
1038
1644
  if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
1039
- if (_optionalChain([node, 'access', _12 => _12.strokeDasharray, 'optionalAccess', _13 => _13.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1645
+ if (_optionalChain([node, 'access', _34 => _34.strokeDasharray, 'optionalAccess', _35 => _35.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1040
1646
  return out2;
1041
1647
  };
1042
1648
  const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
@@ -1051,10 +1657,17 @@ function vectorDocumentToSvg(doc, options = {}) {
1051
1657
  if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
1052
1658
  if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num(node.startArrowScale)}"`;
1053
1659
  if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num(node.endArrowScale)}"`;
1660
+ const profile = strokeWidthProfileOf(node);
1661
+ if (profile) {
1662
+ data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
1663
+ if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
1664
+ if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
1665
+ if (_optionalChain([node, 'access', _36 => _36.strokeDasharray, 'optionalAccess', _37 => _37.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1666
+ }
1054
1667
  lines.push(`${indent(depth)}<g${common(node)}${data}>`);
1055
1668
  lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
1056
1669
  if (options.strokes === "outline") {
1057
- const area = _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1670
+ const area = _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1058
1671
  lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${area}" fill="${escapeAttr(stroke.value)}"/>`);
1059
1672
  } else {
1060
1673
  let clip = "";
@@ -1071,7 +1684,8 @@ function vectorDocumentToSvg(doc, options = {}) {
1071
1684
  clip = ` mask="url(#${id})"`;
1072
1685
  }
1073
1686
  }
1074
- lines.push(`${indent(depth + 1)}<path data-vector-part="stroke" d="${parts.d}" fill="none"${lineAttrs(parts.width)}${clip}/>`);
1687
+ if (parts.outline !== void 0) lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${parts.outline}" fill="${escapeAttr(stroke.value)}"${clip}/>`);
1688
+ else lines.push(`${indent(depth + 1)}<path data-vector-part="stroke" d="${parts.d}" fill="none"${lineAttrs(parts.width)}${clip}/>`);
1075
1689
  for (const h2 of parts.heads) lines.push(`${indent(depth + 1)}<path data-vector-part="head" d="${h2}" fill="${escapeAttr(stroke.value)}"/>`);
1076
1690
  }
1077
1691
  lines.push(`${indent(depth)}</g>`);
@@ -1217,7 +1831,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
1217
1831
  seen.add(id);
1218
1832
  let stops = readStops(el);
1219
1833
  const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
1220
- if (!stops.length && _optionalChain([href, 'optionalAccess', _14 => _14.startsWith, 'call', _15 => _15("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _16 => _16(href.slice(1), raw, seen), 'optionalAccess', _17 => _17.stops]), () => ( []));
1834
+ if (!stops.length && _optionalChain([href, 'optionalAccess', _38 => _38.startsWith, 'call', _39 => _39("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _40 => _40(href.slice(1), raw, seen), 'optionalAccess', _41 => _41.stops]), () => ( []));
1221
1835
  if (!stops.length) return null;
1222
1836
  if (el.name === "radialGradient") {
1223
1837
  return { type: "radial", cx: fraction(el.attrs.cx, 0.5), cy: fraction(el.attrs.cy, 0.5), r: fraction(el.attrs.r, 0.5), stops };
@@ -1321,7 +1935,7 @@ function parseVectorSvg(svg, options = {}) {
1321
1935
  const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
1322
1936
  const fillEl = part("fill");
1323
1937
  const subpaths = fillEl ? shapeOf(fillEl) : null;
1324
- if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _18 => _18.length])) return null;
1938
+ if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _42 => _42.length])) return null;
1325
1939
  const a = g.attrs;
1326
1940
  const fs = readStyle(fillEl, style);
1327
1941
  const strokeEl = part("stroke");
@@ -1334,6 +1948,11 @@ function parseVectorSvg(svg, options = {}) {
1334
1948
  return n > 0 ? n : void 0;
1335
1949
  };
1336
1950
  const align = a["data-vector-stroke-align"];
1951
+ const profile = widthProfileFromAttribute(a["data-vector-width-profile"]);
1952
+ const kept = (key, values) => values.includes(a[key]) ? a[key] : void 0;
1953
+ const keptCap = kept("data-vector-stroke-linecap", ["round", "square"]);
1954
+ const keptJoin = kept("data-vector-stroke-linejoin", ["round", "bevel"]);
1955
+ const keptDash = a["data-vector-stroke-dasharray"] ? numberList(a["data-vector-stroke-dasharray"]) : void 0;
1337
1956
  const node = {
1338
1957
  id: idFor(g),
1339
1958
  type: "path",
@@ -1349,7 +1968,11 @@ function parseVectorSvg(svg, options = {}) {
1349
1968
  ...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
1350
1969
  ...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
1351
1970
  ...scale(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale(a["data-vector-start-arrow-scale"]) } : {},
1352
- ...scale(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale(a["data-vector-end-arrow-scale"]) } : {}
1971
+ ...scale(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale(a["data-vector-end-arrow-scale"]) } : {},
1972
+ ...profile.length ? { strokeWidthProfile: profile } : {},
1973
+ ...keptCap ? { strokeLinecap: keptCap } : {},
1974
+ ...keptJoin ? { strokeLinejoin: keptJoin } : {},
1975
+ ..._optionalChain([keptDash, 'optionalAccess', _43 => _43.length]) ? { strokeDasharray: keptDash } : {}
1353
1976
  };
1354
1977
  return placed(node, m);
1355
1978
  };
@@ -1403,7 +2026,7 @@ function parseVectorSvg(svg, options = {}) {
1403
2026
  // src/vector/snap.ts
1404
2027
  var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
1405
2028
  function constrainToLeave(leave, p) {
1406
- if (!_optionalChain([leave, 'optionalAccess', _19 => _19.constrain])) return p;
2029
+ if (!_optionalChain([leave, 'optionalAccess', _44 => _44.constrain])) return p;
1407
2030
  const dx = p.x - leave.at.x;
1408
2031
  const dy = p.y - leave.at.y;
1409
2032
  switch (leave.constrain) {
@@ -1514,7 +2137,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1514
2137
  moving.set(r.nodeId, set);
1515
2138
  }
1516
2139
  const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
1517
- t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _20 => _20.get, 'call', _21 => _21(id), 'optionalAccess', _22 => _22.has, 'call', _23 => _23(`${s}:${i}`)]), true);
2140
+ t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _45 => _45.get, 'call', _46 => _46(id), 'optionalAccess', _47 => _47.has, 'call', _48 => _48(`${s}:${i}`)]), true);
1518
2141
  return t.index();
1519
2142
  }
1520
2143
  /**
@@ -1632,7 +2255,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1632
2255
  }
1633
2256
  const b = vectorNodeBounds(n, parent);
1634
2257
  const f = nodeFrame(n, parent, b);
1635
- if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _24 => _24.centre]), () => ( null)));
2258
+ if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _49 => _49.centre]), () => ( null)));
1636
2259
  }
1637
2260
  };
1638
2261
  visit(doc.nodes, IDENTITY_MATRIX);
@@ -1669,7 +2292,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1669
2292
  */
1670
2293
  snapFrom(p, leave, tol) {
1671
2294
  const none = (q) => ({ point: q, kind: null, x: false, y: false, lines: [], labels: [] });
1672
- const at = (q, kind, lines2 = []) => ({
2295
+ const at2 = (q, kind, lines2 = []) => ({
1673
2296
  point: q,
1674
2297
  kind,
1675
2298
  x: true,
@@ -1677,23 +2300,23 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1677
2300
  lines: lines2,
1678
2301
  labels: [this.label(q, kind)]
1679
2302
  });
1680
- if (_optionalChain([leave, 'optionalAccess', _25 => _25.constrain])) {
2303
+ if (_optionalChain([leave, 'optionalAccess', _50 => _50.constrain])) {
1681
2304
  const q = constrainToLeave(leave, p);
1682
2305
  const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
1683
2306
  return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
1684
2307
  }
1685
2308
  const point = this.nearestPoint(p, tol);
1686
- if (point) return at(point.p, point.kind);
2309
+ if (point) return at2(point.p, point.kind);
1687
2310
  const near2 = this.segmentsNear(p, tol);
1688
- const crossing = this.crossing(near2, p, tol);
1689
- if (crossing) return at(crossing, "intersect");
2311
+ const crossing2 = this.crossing(near2, p, tol);
2312
+ if (crossing2) return at2(crossing2, "intersect");
1690
2313
  if (leave && leave.curves !== false) {
1691
2314
  const foot = this.curvePoint(near2, leave.at, p, tol);
1692
- if (foot) return at(foot.point, foot.kind, [line("construction", leave.at, foot.point)]);
2315
+ if (foot) return at2(foot.point, foot.kind, [line("construction", leave.at, foot.point)]);
1693
2316
  }
1694
2317
  const on = this.onSegment(near2);
1695
- if (on) return at(on, "path");
1696
- if (_optionalChain([leave, 'optionalAccess', _26 => _26.angles, 'optionalAccess', _27 => _27.length])) {
2318
+ if (on) return at2(on, "path");
2319
+ if (_optionalChain([leave, 'optionalAccess', _51 => _51.angles, 'optionalAccess', _52 => _52.length])) {
1697
2320
  const c = construction(leave, p, tol);
1698
2321
  if (c) {
1699
2322
  const hit = this.slide(leave.at, c.dir, c.point, tol, false);
@@ -1739,7 +2362,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1739
2362
  * the nearest target line crosses it, at most twice `tol` away: where it lands and the
1740
2363
  * target point that line comes from.
1741
2364
  */
1742
- slide(at, dir, q, tol, ray) {
2365
+ slide(at2, dir, q, tol, ray) {
1743
2366
  const len2 = dir.x * dir.x + dir.y * dir.y;
1744
2367
  if (!Number.isFinite(len2) || len2 <= 1e-18) return null;
1745
2368
  const reach = 2 * tol;
@@ -1747,10 +2370,10 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1747
2370
  for (const vertical of [true, false]) {
1748
2371
  const d = vertical ? dir.x : dir.y;
1749
2372
  if (Math.abs(d) <= 1e-9) continue;
1750
- const a = vertical ? at.x : at.y;
2373
+ const a = vertical ? at2.x : at2.y;
1751
2374
  for (const l of linesNear(vertical ? this.xs : this.ys, vertical ? q.x : q.y, reach)) {
1752
2375
  const s = (l.v - a) / d;
1753
- const hit = vertical ? { x: l.v, y: at.y + dir.y * s } : { x: at.x + dir.x * s, y: l.v };
2376
+ const hit = vertical ? { x: l.v, y: at2.y + dir.y * s } : { x: at2.x + dir.x * s, y: l.v };
1754
2377
  const moved = distance(hit, q);
1755
2378
  if (moved <= reach && (!ray || s > 0) && (!best || moved < best.moved)) best = { moved, point: hit, from: l.from };
1756
2379
  }
@@ -1769,13 +2392,13 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1769
2392
  const pt2 = toPoint(p);
1770
2393
  for (const s of this.segments) {
1771
2394
  if (p.x < s.minX - tol || p.x > s.maxX + tol || p.y < s.minY - tol || p.y > s.maxY + tol) continue;
1772
- const hit = _chunkBGNPT5PEcjs.segmentNearest.call(void 0, s.seg, pt2);
2395
+ const hit = _chunk7EDLVBOOcjs.segmentNearest.call(void 0, s.seg, pt2);
1773
2396
  if (hit.distance <= tol) out.push({ seg: s.seg, t: hit.t, distance: hit.distance });
1774
2397
  }
1775
2398
  return out.sort((a, b) => a.distance - b.distance);
1776
2399
  }
1777
2400
  onSegment(near2) {
1778
- return near2.length ? toVec2(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
2401
+ return near2.length ? toVec2(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
1779
2402
  }
1780
2403
  /** Where two of the target segments near `p` cross, the crossing nearest `p` within `tol`. */
1781
2404
  crossing(near2, p, tol) {
@@ -1784,7 +2407,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1784
2407
  let bestD = Infinity;
1785
2408
  for (let i = 0; i < list.length; i++) {
1786
2409
  for (let j = i + 1; j < list.length; j++) {
1787
- for (const hit of _chunkBGNPT5PEcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
2410
+ for (const hit of _chunk7EDLVBOOcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
1788
2411
  const q = toVec2(hit.point);
1789
2412
  const d = distance(q, p);
1790
2413
  if (d <= tol && d < bestD) {
@@ -1823,7 +2446,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1823
2446
  * with nothing spaces the box evenly in its row or column. Returns the correction and guides.
1824
2447
  */
1825
2448
  snapRect(r, tol, options = {}) {
1826
- const axes = { x: _optionalChain([options, 'access', _28 => _28.axes, 'optionalAccess', _29 => _29.x]) !== false, y: _optionalChain([options, 'access', _30 => _30.axes, 'optionalAccess', _31 => _31.y]) !== false };
2449
+ const axes = { x: _optionalChain([options, 'access', _53 => _53.axes, 'optionalAccess', _54 => _54.x]) !== false, y: _optionalChain([options, 'access', _55 => _55.axes, 'optionalAccess', _56 => _56.y]) !== false };
1827
2450
  const nearest = (lines, values) => {
1828
2451
  let best = null;
1829
2452
  for (const v2 of values) for (const l of linesNear(lines, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.d)) best = { d: l.v - v2, from: l.from };
@@ -1831,7 +2454,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1831
2454
  };
1832
2455
  const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
1833
2456
  const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
1834
- const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _32 => _32.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _33 => _33.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
2457
+ const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _57 => _57.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _58 => _58.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
1835
2458
  const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
1836
2459
  if (bx) {
1837
2460
  const x = bx.from.x;
@@ -1898,11 +2521,11 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1898
2521
  for (const s of starts) if (Math.abs(s.start - r0) <= tol && (!pick || Math.abs(s.start - r0) < Math.abs(pick.start - r0))) pick = s;
1899
2522
  if (!pick) return null;
1900
2523
  const { start, repeats } = pick;
1901
- const at = horizontal ? { ...r, x: start } : { ...r, y: start };
2524
+ const at2 = horizontal ? { ...r, x: start } : { ...r, y: start };
1902
2525
  const end = start + len2;
1903
2526
  const gaps = [];
1904
- if (before && (repeats === null || Math.abs(start - before.a1 - repeats) <= EQUAL)) gaps.push([before.a1, start, mid(before.b, at)]);
1905
- if (after && (repeats === null || Math.abs(after.a0 - end - repeats) <= EQUAL)) gaps.push([end, after.a0, mid(after.b, at)]);
2527
+ if (before && (repeats === null || Math.abs(start - before.a1 - repeats) <= EQUAL)) gaps.push([before.a1, start, mid(before.b, at2)]);
2528
+ if (after && (repeats === null || Math.abs(after.a0 - end - repeats) <= EQUAL)) gaps.push([end, after.a0, mid(after.b, at2)]);
1906
2529
  if (repeats !== null) {
1907
2530
  for (const p of pairs) if (Math.abs(p.g - repeats) <= EQUAL) gaps.push(p.gap);
1908
2531
  }
@@ -1954,9 +2577,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1954
2577
  const on = this.nearestPoint(p, tol);
1955
2578
  const tries = [];
1956
2579
  if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
1957
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _34 => _34.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _35 => _35.v]), () => ( null)), s: { hx, hy, on: null } });
1958
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _36 => _36.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
1959
- tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _37 => _37.v]), () => ( null)), s: { hx: null, hy, on: null } });
2580
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _59 => _59.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _60 => _60.v]), () => ( null)), s: { hx, hy, on: null } });
2581
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _61 => _61.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
2582
+ tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _62 => _62.v]), () => ( null)), s: { hx: null, hy, on: null } });
1960
2583
  for (const { tx, ty, s } of tries) {
1961
2584
  if (tx === null && ty === null) continue;
1962
2585
  const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
@@ -2106,15 +2729,15 @@ var crossCondition = (r, d) => r[0] * d[1] - r[1] * d[0];
2106
2729
  function solveOnSegment(seg, base, cond) {
2107
2730
  const b = [base.x, base.y];
2108
2731
  const g = (t) => {
2109
- const p = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
2110
- return cond([p[0] - b[0], p[1] - b[1]], _chunkBGNPT5PEcjs.segmentDerivative.call(void 0, seg, t));
2732
+ const p = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
2733
+ return cond([p[0] - b[0], p[1] - b[1]], _chunk7EDLVBOOcjs.segmentDerivative.call(void 0, seg, t));
2111
2734
  };
2112
2735
  const vals = [];
2113
2736
  let scale = 0;
2114
2737
  for (let i = 0; i <= CURVE_SAMPLES; i++) {
2115
2738
  const t = i / CURVE_SAMPLES;
2116
- const p = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
2117
- const d = _chunkBGNPT5PEcjs.segmentDerivative.call(void 0, seg, t);
2739
+ const p = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
2740
+ const d = _chunk7EDLVBOOcjs.segmentDerivative.call(void 0, seg, t);
2118
2741
  const r = [p[0] - b[0], p[1] - b[1]];
2119
2742
  vals.push(cond(r, d));
2120
2743
  scale = Math.max(scale, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
@@ -2141,8 +2764,8 @@ function solveOnSegment(seg, base, cond) {
2141
2764
  } else hi = m;
2142
2765
  }
2143
2766
  const t = (lo + hi) / 2;
2144
- const p = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
2145
- const dv = _chunkBGNPT5PEcjs.segmentDerivative.call(void 0, seg, t);
2767
+ const p = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
2768
+ const dv = _chunk7EDLVBOOcjs.segmentDerivative.call(void 0, seg, t);
2146
2769
  const r = Math.hypot(p[0] - b[0], p[1] - b[1]);
2147
2770
  const dl = Math.hypot(dv[0], dv[1]);
2148
2771
  if (!(r > 1e-9 && dl > 0) || Math.abs(cond([p[0] - b[0], p[1] - b[1]], dv)) > 1e-6 * r * dl) continue;
@@ -2179,7 +2802,7 @@ function endTangent(c) {
2179
2802
  return [1, 0];
2180
2803
  }
2181
2804
  function sample(c, n, out, withStart) {
2182
- for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, c, i / n));
2805
+ for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, c, i / n));
2183
2806
  }
2184
2807
  var onAnchor2 = (a, h) => !h || Math.abs(h.x - a.x) < EPS3 && Math.abs(h.y - a.y) < EPS3;
2185
2808
  function tidy(a, keepType = true) {
@@ -2225,7 +2848,7 @@ function fitAcross(pts, N, t0, t1) {
2225
2848
  const u = [];
2226
2849
  for (let i = 0; i <= N; i++) u.push(k * i / N);
2227
2850
  for (let i = 1; i <= N; i++) u.push(k + (1 - k) * i / N);
2228
- return _chunkBGNPT5PEcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
2851
+ return _chunk7EDLVBOOcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
2229
2852
  };
2230
2853
  return goldenMin(fit, 0, 1);
2231
2854
  }
@@ -2285,7 +2908,7 @@ function bendSegment(sp, segment, t, p) {
2285
2908
  return { anchors, closed: sp.closed };
2286
2909
  }
2287
2910
  function subpathPointAt(sp, segment, t) {
2288
- const [x, y] = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, subpathSegment(sp, segment), t);
2911
+ const [x, y] = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, subpathSegment(sp, segment), t);
2289
2912
  return { x, y };
2290
2913
  }
2291
2914
  function curvatureMoveAnchor(sp, index, q) {
@@ -2331,9 +2954,9 @@ function curvatureMoveAnchor(sp, index, q) {
2331
2954
  anchors[index] = moved;
2332
2955
  return { anchors, closed: sp.closed };
2333
2956
  }
2334
- function cutSubpathAt(sp, at) {
2957
+ function cutSubpathAt(sp, at2) {
2335
2958
  const n = sp.anchors.length;
2336
- const cuts = [...new Set(at)].filter((i) => Number.isInteger(i) && i >= 0 && i < n && (sp.closed || i > 0 && i < n - 1)).sort((a, b) => a - b);
2959
+ const cuts = [...new Set(at2)].filter((i) => Number.isInteger(i) && i >= 0 && i < n && (sp.closed || i > 0 && i < n - 1)).sort((a, b) => a - b);
2337
2960
  if (!cuts.length || n < 2) return [{ anchors: sp.anchors.map(cloneAnchor), closed: sp.closed }];
2338
2961
  const first = cuts[0];
2339
2962
  const run = sp.closed ? Array.from({ length: n + 1 }, (_, i) => sp.anchors[(first + i) % n]) : sp.anchors;
@@ -2364,7 +2987,7 @@ function splitSubpathAt(sp, segment, t, endTolerance = 1e-6) {
2364
2987
  return cutSubpathAt(subpath, [index]);
2365
2988
  }
2366
2989
  var endOf2 = (sp, last) => sp.anchors[last ? sp.anchors.length - 1 : 0];
2367
- var dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
2990
+ var dist2 = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
2368
2991
  function reverse(sp) {
2369
2992
  return {
2370
2993
  closed: sp.closed,
@@ -2391,7 +3014,7 @@ function joinSubpaths(subpaths, tolerance) {
2391
3014
  const sp = open[0];
2392
3015
  const first = sp.anchors[0];
2393
3016
  const last = sp.anchors[sp.anchors.length - 1];
2394
- if (sp.anchors.length > 2 && dist(first, last) <= tolerance) {
3017
+ if (sp.anchors.length > 2 && dist2(first, last) <= tolerance) {
2395
3018
  sp.anchors.pop();
2396
3019
  sp.anchors[0] = tidy({ ...first, in: _nullishCoalesce(last.in, () => ( null)) }, false);
2397
3020
  } else {
@@ -2407,7 +3030,7 @@ function joinSubpaths(subpaths, tolerance) {
2407
3030
  for (let j = i + 1; j < open.length; j++) {
2408
3031
  for (const ei of [false, true]) {
2409
3032
  for (const ej of [false, true]) {
2410
- const d = dist(endOf2(open[i], ei), endOf2(open[j], ej));
3033
+ const d = dist2(endOf2(open[i], ei), endOf2(open[j], ej));
2411
3034
  if (d < best.d) best = { d, i, j, ei, ej };
2412
3035
  }
2413
3036
  }
@@ -2447,8 +3070,8 @@ function smoothSubpath(sp, amount = 1, only) {
2447
3070
  const p = orig[i];
2448
3071
  const dir = unit2(next.x - prev.x, next.y - prev.y);
2449
3072
  if (!dir) continue;
2450
- const li = dist(p, prev) / 3;
2451
- const lo = dist(p, next) / 3;
3073
+ const li = dist2(p, prev) / 3;
3074
+ const lo = dist2(p, next) / 3;
2452
3075
  const hin = _nullishCoalesce(p.in, () => ( p));
2453
3076
  const hout = _nullishCoalesce(p.out, () => ( p));
2454
3077
  const target = (h, x, y) => ({ x: h.x + (x - h.x) * t, y: h.y + (y - h.y) * t });
@@ -2475,12 +3098,12 @@ function segsOf(sp) {
2475
3098
  function segsToSubpath(segs, closed, types) {
2476
3099
  if (!segs.length) return null;
2477
3100
  const anchors = [];
2478
- const at = (p) => ({ x: p[0], y: p[1] });
2479
- anchors.push(at(segs[0].c[0]));
3101
+ const at2 = (p) => ({ x: p[0], y: p[1] });
3102
+ anchors.push(at2(segs[0].c[0]));
2480
3103
  for (const s of segs) {
2481
3104
  const end = s.c[s.c.length - 1];
2482
3105
  if (!s.line) anchors[anchors.length - 1].out = { x: s.c[1][0], y: s.c[1][1] };
2483
- const next = at(end);
3106
+ const next = at2(end);
2484
3107
  if (!s.line) next.in = { x: s.c[2][0], y: s.c[2][1] };
2485
3108
  anchors.push(next);
2486
3109
  }
@@ -2506,8 +3129,8 @@ function mergePair(a, b, tol) {
2506
3129
  if (len2 < 1e-12) return null;
2507
3130
  const mx = a3[0] - a0[0];
2508
3131
  const my = a3[1] - a0[1];
2509
- const along = (mx * cx + my * cy) / len2;
2510
- return Math.abs((mx * cy - my * cx) / len2) <= tol && along >= 0 && along <= len2 ? { c: [a0, b3], line: true } : null;
3132
+ const along2 = (mx * cx + my * cy) / len2;
3133
+ return Math.abs((mx * cy - my * cx) / len2) <= tol && along2 >= 0 && along2 <= len2 ? { c: [a0, b3], line: true } : null;
2511
3134
  }
2512
3135
  const ca = a.line ? [a0, a0, a3, a3] : a.c;
2513
3136
  const cb = b.line ? [a3, a3, b3, b3] : b.c;
@@ -2546,20 +3169,20 @@ function removeRedundantPoints(sp, tolerance) {
2546
3169
  }
2547
3170
 
2548
3171
  // src/vector/pathCut.ts
2549
- var loopArea = (l) => _chunkBGNPT5PEcjs.contourSignedArea.call(void 0, l.segs);
3172
+ var loopArea = (l) => _chunk7EDLVBOOcjs.contourSignedArea.call(void 0, l.segs);
2550
3173
  function loopSample(l) {
2551
3174
  const k = l.chord.findIndex((c) => !c);
2552
- return _chunkBGNPT5PEcjs.segmentPoint.call(void 0, l.segs[k >= 0 ? k : 0], 0.5);
3175
+ return _chunk7EDLVBOOcjs.segmentPoint.call(void 0, l.segs[k >= 0 ? k : 0], 0.5);
2553
3176
  }
2554
- var contains2 = (l, p) => _chunkBGNPT5PEcjs.windingNumber.call(void 0, [l.segs], p) !== 0;
3177
+ var contains2 = (l, p) => _chunk7EDLVBOOcjs.windingNumber.call(void 0, [l.segs], p) !== 0;
2555
3178
  function groupFaces(loops) {
2556
- const usable = loops.filter((l) => l.segs.length && Math.abs(loopArea(l)) > 1e-12);
2557
- if (!usable.length) return [];
2558
- const largest = usable.reduce((a, b) => Math.abs(loopArea(b)) > Math.abs(loopArea(a)) ? b : a);
3179
+ const usable2 = loops.filter((l) => l.segs.length && Math.abs(loopArea(l)) > 1e-12);
3180
+ if (!usable2.length) return [];
3181
+ const largest = usable2.reduce((a, b) => Math.abs(loopArea(b)) > Math.abs(loopArea(a)) ? b : a);
2559
3182
  const sign = Math.sign(loopArea(largest));
2560
- const outers = usable.filter((l) => Math.sign(loopArea(l)) === sign).sort((a, b) => Math.abs(loopArea(a)) - Math.abs(loopArea(b)));
3183
+ const outers = usable2.filter((l) => Math.sign(loopArea(l)) === sign).sort((a, b) => Math.abs(loopArea(a)) - Math.abs(loopArea(b)));
2561
3184
  const faces = new Map(outers.map((o) => [o, { loops: [o] }]));
2562
- for (const hole of usable.filter((l) => Math.sign(loopArea(l)) !== sign)) {
3185
+ for (const hole of usable2.filter((l) => Math.sign(loopArea(l)) !== sign)) {
2563
3186
  const p = loopSample(hole);
2564
3187
  const parent = outers.find((o) => Math.abs(loopArea(o)) > Math.abs(loopArea(hole)) && contains2(o, p));
2565
3188
  if (parent) faces.get(parent).loops.push(hole);
@@ -2567,12 +3190,12 @@ function groupFaces(loops) {
2567
3190
  return outers.reverse().map((o) => faces.get(o));
2568
3191
  }
2569
3192
  function faceToPath(face) {
2570
- return _chunkBGNPT5PEcjs.contoursToPath.call(void 0, face.loops.map((l) => ({ segments: l.segs, closed: true })));
3193
+ return _chunk7EDLVBOOcjs.contoursToPath.call(void 0, face.loops.map((l) => ({ segments: l.segs, closed: true })));
2571
3194
  }
2572
3195
  function facesOf(path, fillRule) {
2573
- const normalized = _chunkBGNPT5PEcjs.normalizeBezierPath.call(void 0, path, fillRule);
2574
- const loops = _chunkBGNPT5PEcjs.pathToContours.call(void 0, normalized).map((c) => {
2575
- const segs = _chunkBGNPT5PEcjs.closeContour.call(void 0, c, true).map((s) => s.slice());
3196
+ const normalized = _chunk7EDLVBOOcjs.normalizeBezierPath.call(void 0, path, fillRule);
3197
+ const loops = _chunk7EDLVBOOcjs.pathToContours.call(void 0, normalized).map((c) => {
3198
+ const segs = _chunk7EDLVBOOcjs.closeContour.call(void 0, c, true).map((s) => s.slice());
2576
3199
  return { segs, chord: segs.map(() => false) };
2577
3200
  });
2578
3201
  return groupFaces(loops);
@@ -2642,7 +3265,7 @@ function lineHits(p, q, seg) {
2642
3265
  const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
2643
3266
  const out = [];
2644
3267
  for (const t of rootsInUnit(...coeffs, scale)) {
2645
- const point = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
3268
+ const point = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, t);
2646
3269
  const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
2647
3270
  if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
2648
3271
  }
@@ -2655,7 +3278,7 @@ function arc(l, a, b, eps) {
2655
3278
  const segs = [];
2656
3279
  const chord = [];
2657
3280
  const push = (k, t0, t1) => {
2658
- const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : _chunkBGNPT5PEcjs.subSegment.call(void 0, l.segs[k], t0, t1);
3281
+ const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : _chunk7EDLVBOOcjs.subSegment.call(void 0, l.segs[k], t0, t1);
2659
3282
  if (!degenerate(piece, eps)) {
2660
3283
  segs.push(piece);
2661
3284
  chord.push(l.chord[k]);
@@ -2680,7 +3303,7 @@ function knifePath(path, fillRule, knife) {
2680
3303
  if (pts.length < 2) return null;
2681
3304
  let faces = facesOf(path, fillRule);
2682
3305
  if (!faces.length) return null;
2683
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, path);
3306
+ const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, path);
2684
3307
  const size = b ? Math.max(b.maxX - b.minX, b.maxY - b.minY, 1) : 1;
2685
3308
  const eps = size * 1e-9;
2686
3309
  let cursor = -Infinity;
@@ -2694,7 +3317,7 @@ function knifePath(path, fillRule, knife) {
2694
3317
  const kb = { minX: Math.min(pts[m][0], pts[m + 1][0]), maxX: Math.max(pts[m][0], pts[m + 1][0]), minY: Math.min(pts[m][1], pts[m + 1][1]), maxY: Math.max(pts[m][1], pts[m + 1][1]) };
2695
3318
  face2.loops.forEach(
2696
3319
  (l, li) => l.segs.forEach((seg, si) => {
2697
- const sb = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, seg);
3320
+ const sb = _chunk7EDLVBOOcjs.segmentBounds.call(void 0, seg);
2698
3321
  if (sb.maxX < kb.minX - eps || sb.minX > kb.maxX + eps || sb.maxY < kb.minY - eps || sb.minY > kb.maxY + eps) return;
2699
3322
  for (const h of lineHits(pts[m], pts[m + 1], seg)) {
2700
3323
  const s = m + h.s;
@@ -2709,7 +3332,7 @@ function knifePath(path, fillRule, knife) {
2709
3332
  const m = Math.min(pts.length - 2, Math.floor(s));
2710
3333
  const u = s - m;
2711
3334
  const q = [pts[m][0] + (pts[m + 1][0] - pts[m][0]) * u, pts[m][1] + (pts[m + 1][1] - pts[m][1]) * u];
2712
- return _chunkBGNPT5PEcjs.windingNumber.call(void 0, face2.loops.map((l) => l.segs), q) !== 0;
3335
+ return _chunk7EDLVBOOcjs.windingNumber.call(void 0, face2.loops.map((l) => l.segs), q) !== 0;
2713
3336
  };
2714
3337
  for (let i = 0; i < unique.length - 1; i++) {
2715
3338
  const from2 = unique[i];
@@ -2769,33 +3392,33 @@ function eraserRegion(points, width) {
2769
3392
  ];
2770
3393
  }
2771
3394
  const line2 = [{ type: "M", x: pts[0][0], y: pts[0][1] }, ...pts.slice(1).map((p) => ({ type: "L", x: p[0], y: p[1] }))];
2772
- return _chunkBGNPT5PEcjs.outlineStroke.call(void 0, line2, { width, cap: "round", join: "round" });
3395
+ return _chunk7EDLVBOOcjs.outlineStroke.call(void 0, line2, { width, cap: "round", join: "round" });
2773
3396
  }
2774
3397
  function eraseFromFill(path, fillRule, region) {
2775
- const overlap = _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "intersection", [{ path, fillRule }, { path: region }]).path;
2776
- if (!_chunkBGNPT5PEcjs.pathToContours.call(void 0, overlap).some((c) => Math.abs(_chunkBGNPT5PEcjs.contourSignedArea.call(void 0, _chunkBGNPT5PEcjs.closeContour.call(void 0, c, true))) > 1e-9)) return null;
2777
- const rest = _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "difference", [{ path, fillRule }, { path: region }]).path;
3398
+ const overlap = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "intersection", [{ path, fillRule }, { path: region }]).path;
3399
+ if (!_chunk7EDLVBOOcjs.pathToContours.call(void 0, overlap).some((c) => Math.abs(_chunk7EDLVBOOcjs.contourSignedArea.call(void 0, _chunk7EDLVBOOcjs.closeContour.call(void 0, c, true))) > 1e-9)) return null;
3400
+ const rest = _chunk7EDLVBOOcjs.pathBoolean.call(void 0, "difference", [{ path, fillRule }, { path: region }]).path;
2778
3401
  return splitIntoFaces(rest);
2779
3402
  }
2780
3403
  function eraseFromOutline(sp, region) {
2781
- const regionSegs = _chunkBGNPT5PEcjs.pathToContours.call(void 0, region).flatMap((c) => _chunkBGNPT5PEcjs.closeContour.call(void 0, c, true));
3404
+ const regionSegs = _chunk7EDLVBOOcjs.pathToContours.call(void 0, region).flatMap((c) => _chunk7EDLVBOOcjs.closeContour.call(void 0, c, true));
2782
3405
  if (!regionSegs.length) return null;
2783
- const rb = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, region);
3406
+ const rb = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, region);
2784
3407
  const count = subpathSegmentCount(sp);
2785
3408
  if (!rb || count === 0) return null;
2786
- const removed = (p) => _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
3409
+ const removed = (p) => _chunk7EDLVBOOcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
2787
3410
  const runs = [];
2788
3411
  let cur = [];
2789
3412
  let any = false;
2790
3413
  for (let i = 0; i < count; i++) {
2791
3414
  const seg = subpathSegment(sp, i);
2792
- const sb = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, seg);
3415
+ const sb = _chunk7EDLVBOOcjs.segmentBounds.call(void 0, seg);
2793
3416
  const ts = [0, 1];
2794
3417
  if (!(sb.maxX < rb.minX || sb.minX > rb.maxX || sb.maxY < rb.minY || sb.minY > rb.maxY)) {
2795
3418
  for (const r of regionSegs) {
2796
- const b = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, r);
3419
+ const b = _chunk7EDLVBOOcjs.segmentBounds.call(void 0, r);
2797
3420
  if (b.maxX < sb.minX || b.minX > sb.maxX || b.maxY < sb.minY || b.minY > sb.maxY) continue;
2798
- for (const hit of _chunkBGNPT5PEcjs.segmentIntersections.call(void 0, seg, r)) if (hit.ta > 1e-9 && hit.ta < 1 - 1e-9) ts.push(hit.ta);
3421
+ for (const hit of _chunk7EDLVBOOcjs.segmentIntersections.call(void 0, seg, r)) if (hit.ta > 1e-9 && hit.ta < 1 - 1e-9) ts.push(hit.ta);
2799
3422
  }
2800
3423
  }
2801
3424
  ts.sort((a, b) => a - b);
@@ -2803,7 +3426,7 @@ function eraseFromOutline(sp, region) {
2803
3426
  const t0 = ts[k];
2804
3427
  const t1 = ts[k + 1];
2805
3428
  if (t1 - t0 < 1e-9) continue;
2806
- if (removed(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
3429
+ if (removed(_chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
2807
3430
  any = true;
2808
3431
  if (cur.length) runs.push(cur);
2809
3432
  cur = [];
@@ -2822,7 +3445,7 @@ function eraseFromOutline(sp, region) {
2822
3445
  for (const run of runs) {
2823
3446
  const anchors = [];
2824
3447
  for (const { seg, t0, t1 } of run) {
2825
- const c = _chunkBGNPT5PEcjs.subSegment.call(void 0, subpathSegment(sp, seg), t0, t1);
3448
+ const c = _chunk7EDLVBOOcjs.subSegment.call(void 0, subpathSegment(sp, seg), t0, t1);
2826
3449
  const line2 = c.length === 2;
2827
3450
  const start = c[0];
2828
3451
  const end = c[c.length - 1];
@@ -2939,7 +3562,7 @@ function vectorInkShapeName(shape) {
2939
3562
  return shape.points.length === 3 ? "triangle" : "polygon";
2940
3563
  }
2941
3564
  }
2942
- var dist2 = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
3565
+ var dist3 = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
2943
3566
  function bbox(pts) {
2944
3567
  if (!pts.length) return [0, 0, 0, 0];
2945
3568
  let [x0, y0] = pts[0];
@@ -2957,7 +3580,7 @@ function segDistance(a, b, p) {
2957
3580
  const dy = b[1] - a[1];
2958
3581
  const len2 = dx * dx + dy * dy;
2959
3582
  const t = len2 > 0 ? Math.min(1, Math.max(0, ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / len2)) : 0;
2960
- return dist2([a[0] + t * dx, a[1] + t * dy], p);
3583
+ return dist3([a[0] + t * dx, a[1] + t * dy], p);
2961
3584
  }
2962
3585
  function polygonDistance(c, p) {
2963
3586
  let best = Infinity;
@@ -3019,7 +3642,7 @@ function corners(p, diag) {
3019
3642
  let far = 0;
3020
3643
  let farD = 0;
3021
3644
  p.forEach((q, i) => {
3022
- const d = dist2(start, q);
3645
+ const d = dist3(start, q);
3023
3646
  if (d > farD) {
3024
3647
  farD = d;
3025
3648
  far = i;
@@ -3046,7 +3669,7 @@ function corners(p, diag) {
3046
3669
  }
3047
3670
  let short = -1;
3048
3671
  for (let j = 0; j < n; j++) {
3049
- if (dist2(ring[j], ring[(j + 1) % n]) < 0.1 * diag) {
3672
+ if (dist3(ring[j], ring[(j + 1) % n]) < 0.1 * diag) {
3050
3673
  short = j;
3051
3674
  break;
3052
3675
  }
@@ -3086,8 +3709,8 @@ function recognizeInkShape(points, options = {}) {
3086
3709
  const diag = Math.hypot(w, h);
3087
3710
  if (size < minSize) return null;
3088
3711
  let length2 = 0;
3089
- for (let i = 1; i < p.length; i++) length2 += dist2(p[i - 1], p[i]);
3090
- const gap = dist2(first, last);
3712
+ for (let i = 1; i < p.length; i++) length2 += dist3(p[i - 1], p[i]);
3713
+ const gap = dist3(first, last);
3091
3714
  if (gap > 0.85 * length2 && gap >= minSize) {
3092
3715
  const off = Math.max(...p.map((q) => segDistance(first, last, q)));
3093
3716
  if (off <= Math.max(0.06 * gap, 1.5 * minSize / 6)) return { kind: "line", a: first, b: last };
@@ -3111,9 +3734,188 @@ function recognizeInkShape(points, options = {}) {
3111
3734
  return roundish && rms < 0.1 ? ellipse() : null;
3112
3735
  }
3113
3736
 
3737
+ // src/vector/builder.ts
3738
+ function vectorPathEntries(doc, roots, options = {}) {
3739
+ const out = [];
3740
+ const visit = (node, parent, inside) => {
3741
+ if (node.visible === false || node.locked && !options.locked) return;
3742
+ const picked = inside || roots === null || roots.includes(node.id);
3743
+ if (node.type === "group") {
3744
+ const m = multiplyMatrices(nodeMatrix(node), parent);
3745
+ for (const c of node.children) visit(c, m, picked);
3746
+ } else if (picked) out.push({ node, parent });
3747
+ };
3748
+ for (const n of doc.nodes) visit(n, IDENTITY_MATRIX, false);
3749
+ return out;
3750
+ }
3751
+ var usable = (sp) => sp.anchors.length > 1;
3752
+ function worldOf(entry, subpaths) {
3753
+ return vectorPathWorldBezier({ ...entry.node, subpaths }, entry.parent);
3754
+ }
3755
+ function subpathEnclosesArea(sp) {
3756
+ const contour = _chunk7EDLVBOOcjs.pathToContours.call(void 0, subpathsToBezier([{ ...sp, closed: true }]))[0];
3757
+ if (!contour) return false;
3758
+ const segs = _chunk7EDLVBOOcjs.closeContour.call(void 0, contour, true);
3759
+ const b = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, _chunk7EDLVBOOcjs.contoursToPath.call(void 0, [{ segments: segs, closed: true }]));
3760
+ if (!b) return false;
3761
+ const w = b.maxX - b.minX;
3762
+ const h = b.maxY - b.minY;
3763
+ return Math.abs(_chunk7EDLVBOOcjs.contourSignedArea.call(void 0, segs)) > 1e-9 * (w * w + h * h);
3764
+ }
3765
+ var paintKey = (paint) => typeof paint === "string" ? paint.trim().toLowerCase() : JSON.stringify(_nullishCoalesce(paint, () => ( null)));
3766
+ function vectorPathfinderShape(entry, op) {
3767
+ const { node } = entry;
3768
+ const subpaths = node.subpaths.filter(usable);
3769
+ const closed = subpaths.map((s) => ({ ...s, closed: true }));
3770
+ const fillRule = _nullishCoalesce(node.fillRule, () => ( "nonzero"));
3771
+ if (hasPaint(node.fill)) return { path: worldOf(entry, closed), fillRule, key: `fill:${paintKey(node.fill)}`, paint: "fill" };
3772
+ const open = subpaths.length > 0 && subpaths.every((s) => !s.closed);
3773
+ if (op === "divide" && open) return { path: worldOf(entry, subpaths), line: true, paint: "line" };
3774
+ if (hasPaint(node.stroke) && (_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0) {
3775
+ const path = vectorStrokeOutline({ ...node, subpaths }, entry.parent);
3776
+ return { path, fillRule: "nonzero", key: `fill:${paintKey(node.stroke)}`, paint: "stroke" };
3777
+ }
3778
+ return { path: worldOf(entry, closed), fillRule, key: "none", paint: "outline" };
3779
+ }
3780
+ var STROKE_KEYS = [
3781
+ "stroke",
3782
+ "strokeWidth",
3783
+ "strokeLinecap",
3784
+ "strokeLinejoin",
3785
+ "strokeDasharray",
3786
+ "strokeAlign",
3787
+ "startArrow",
3788
+ "endArrow",
3789
+ "arrowAlign",
3790
+ "startArrowScale",
3791
+ "endArrowScale",
3792
+ "strokeWidthProfile"
3793
+ ];
3794
+ function vectorResultStyle(node, paint, edge = false) {
3795
+ const { id: _id, subpaths: _s, transform: _t, ...rest } = node;
3796
+ const base = { ...rest, fillRule: "nonzero" };
3797
+ if (edge) {
3798
+ for (const k of STROKE_KEYS) delete base[k];
3799
+ const stroke = paint === "stroke" || !hasPaint(node.fill) ? node.stroke : node.fill;
3800
+ return { ...base, fill: null, stroke: _nullishCoalesce(stroke, () => ( "#000000")), strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)) };
3801
+ }
3802
+ if (paint === "stroke") {
3803
+ for (const k of STROKE_KEYS) delete base[k];
3804
+ return { ...base, fill: _nullishCoalesce(node.stroke, () => ( null)), stroke: null };
3805
+ }
3806
+ return base;
3807
+ }
3808
+ function vectorShapeBuilderMap(entries) {
3809
+ const lines = [];
3810
+ const inputs = entries.map((entry) => {
3811
+ const filled = hasPaint(entry.node.fill);
3812
+ const subpaths = entry.node.subpaths.filter(usable).map((sp) => !sp.closed && filled && subpathEnclosesArea(sp) ? { ...sp, closed: true } : sp);
3813
+ lines.push(!subpaths.some((s) => s.closed));
3814
+ return { path: worldOf(entry, subpaths), fillRule: _nullishCoalesce(entry.node.fillRule, () => ( "nonzero")) };
3815
+ });
3816
+ const map = _chunk7EDLVBOOcjs.PlanarMap.build(inputs);
3817
+ if (!map) return null;
3818
+ const pieces = map.chains().filter((c) => lines[c.source] && !c.closed).map((c) => {
3819
+ const path = _chunk7EDLVBOOcjs.contoursToPath.call(void 0, [{ segments: c.segments, closed: false }]);
3820
+ return { path, segments: c.segments, source: c.source, bounds: _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, path) };
3821
+ });
3822
+ return { entries: [...entries], map, lines, pieces };
3823
+ }
3824
+ function vectorShapeBuilderHit(m, p, lineTolerance = null) {
3825
+ const point = [p.x, p.y];
3826
+ if (lineTolerance !== null && lineTolerance > 0) {
3827
+ let best = -1;
3828
+ let bestD = lineTolerance;
3829
+ m.pieces.forEach((piece, i) => {
3830
+ const b = piece.bounds;
3831
+ if (p.x < b.minX - bestD || p.x > b.maxX + bestD || p.y < b.minY - bestD || p.y > b.maxY + bestD) return;
3832
+ const d = _chunk7EDLVBOOcjs.distanceToBezierPath.call(void 0, piece.path, point);
3833
+ if (d <= bestD) {
3834
+ best = i;
3835
+ bestD = d;
3836
+ }
3837
+ });
3838
+ if (best >= 0) return { line: best };
3839
+ }
3840
+ const face = m.map.faceAt(point);
3841
+ return face >= 0 ? { face } : null;
3842
+ }
3843
+ function samplePolyline(points, step) {
3844
+ const out = [];
3845
+ const s = Math.max(step, 1e-3);
3846
+ points.forEach((p, i) => {
3847
+ if (i > 0) {
3848
+ const a = points[i - 1];
3849
+ const n = Math.min(4e3, Math.ceil(Math.hypot(p.x - a.x, p.y - a.y) / s));
3850
+ for (let k = 1; k < n; k++) out.push({ x: a.x + (p.x - a.x) * k / n, y: a.y + (p.y - a.y) * k / n });
3851
+ }
3852
+ out.push(p);
3853
+ });
3854
+ return out;
3855
+ }
3856
+ function addBuilderHit(touched, hit) {
3857
+ if (!hit) return false;
3858
+ const [list, i] = "face" in hit ? [touched.faces, hit.face] : [touched.lines, hit.line];
3859
+ if (list.includes(i)) return false;
3860
+ list.push(i);
3861
+ return true;
3862
+ }
3863
+ function sides(m, piece) {
3864
+ const seg = piece.segments[Math.floor(piece.segments.length / 2)];
3865
+ const mid = _chunk7EDLVBOOcjs.segmentPoint.call(void 0, seg, 0.5);
3866
+ const t = _chunk7EDLVBOOcjs.segmentTangent.call(void 0, seg, 0.5);
3867
+ const chord = Math.hypot(seg[seg.length - 1][0] - seg[0][0], seg[seg.length - 1][1] - seg[0][1]);
3868
+ const off = Math.max(1e-6, Math.min(0.05, 0.05 * chord));
3869
+ return [m.map.faceAt([mid[0] - t[1] * off, mid[1] + t[0] * off]), m.map.faceAt([mid[0] + t[1] * off, mid[1] - t[0] * off])];
3870
+ }
3871
+ function planShapeBuilder(m, touched, erase) {
3872
+ const replace = /* @__PURE__ */ new Map();
3873
+ const picked = new Set(touched.faces);
3874
+ const faces = m.map.faces;
3875
+ const sourcesOf = faces.map((_, i) => m.map.faceSources(i));
3876
+ m.entries.forEach((_, i) => {
3877
+ if (m.lines[i]) {
3878
+ const own = m.pieces.flatMap((piece, k) => piece.source === i ? [k] : []);
3879
+ const keep = own.filter((k) => !touched.lines.includes(k) && !sides(m, m.pieces[k]).every((f) => f >= 0 && picked.has(f)));
3880
+ if (keep.length === own.length) return;
3881
+ replace.set(i, keep.length ? { kind: "pieces", paths: keep.map((k) => m.pieces[k].path) } : null);
3882
+ return;
3883
+ }
3884
+ if (!touched.faces.some((f) => sourcesOf[f].includes(i))) return;
3885
+ const kept = faces.flatMap((_2, f) => sourcesOf[f].includes(i) && !picked.has(f) ? [f] : []);
3886
+ const rest = kept.length ? m.map.merge(kept) : [];
3887
+ replace.set(i, rest.length ? { kind: "fill", path: rest } : null);
3888
+ });
3889
+ let merged = null;
3890
+ if (!erase && touched.faces.length) {
3891
+ const path = m.map.merge(touched.faces);
3892
+ const covered = touched.faces.map((f) => sourcesOf[f]).find((s) => s.length);
3893
+ const lastShape = m.lines.lastIndexOf(false);
3894
+ const style = covered ? covered[covered.length - 1] : lastShape >= 0 ? lastShape : m.entries.length - 1;
3895
+ if (path.length) merged = { path, style, currentFill: !covered };
3896
+ }
3897
+ return { replace, merged };
3898
+ }
3899
+ function vectorFillMap(doc, gap = 0) {
3900
+ const entries = vectorPathEntries(doc, null, { locked: true });
3901
+ return _chunk7EDLVBOOcjs.PlanarMap.build(
3902
+ entries.map((e) => ({ path: worldOf(e, e.node.subpaths.filter(usable)), fills: false })),
3903
+ { gap, maxSegments: 2e4 }
3904
+ );
3905
+ }
3906
+
3114
3907
  // src/vector/controller.ts
3115
3908
  var DEFAULT_VECTOR_SNAP = { grid: 0, objects: true, guides: true, threshold: 5, angles: VECTOR_CONSTRUCTION_ANGLES };
3116
- var DEFAULT_VECTOR_PATH_EDIT = { keepShapeOnDelete: true, curvatureMoves: false, pencilSmoothing: 0, recognizeShapes: false, eraserSize: 10 };
3909
+ var VECTOR_FILL_GAP = 6;
3910
+ var DEFAULT_VECTOR_PATH_EDIT = {
3911
+ keepShapeOnDelete: true,
3912
+ curvatureMoves: false,
3913
+ pencilSmoothing: 0,
3914
+ recognizeShapes: false,
3915
+ eraserSize: 10,
3916
+ pencilPressure: "pen",
3917
+ fillGap: 0
3918
+ };
3117
3919
  var VECTOR_PENCIL_STRING_PX = 100;
3118
3920
  var VECTOR_STYLE_KEYS = [
3119
3921
  "fill",
@@ -3128,7 +3930,8 @@ var VECTOR_STYLE_KEYS = [
3128
3930
  "endArrow",
3129
3931
  "arrowAlign",
3130
3932
  "startArrowScale",
3131
- "endArrowScale"
3933
+ "endArrowScale",
3934
+ "strokeWidthProfile"
3132
3935
  ];
3133
3936
  var VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
3134
3937
  var DEFAULT_DEFAULTS = {
@@ -3157,7 +3960,7 @@ function getEditablePathIds(state) {
3157
3960
  }
3158
3961
  }
3159
3962
  }
3160
- return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _38 => _38(state.document.nodes, id), 'optionalAccess', _39 => _39.type]) === "path");
3963
+ return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _63 => _63(state.document.nodes, id), 'optionalAccess', _64 => _64.type]) === "path");
3161
3964
  }
3162
3965
  function getVisibleAnchors(state) {
3163
3966
  const out = [];
@@ -3172,7 +3975,7 @@ function getVisibleHandles(state) {
3172
3975
  const seen = /* @__PURE__ */ new Set();
3173
3976
  const push = (node, s, i, handle) => {
3174
3977
  const sp = node.subpaths[s];
3175
- const a = _optionalChain([sp, 'optionalAccess', _40 => _40.anchors, 'access', _41 => _41[i]]);
3978
+ const a = _optionalChain([sp, 'optionalAccess', _65 => _65.anchors, 'access', _66 => _66[i]]);
3176
3979
  if (!a || !a[handle]) return;
3177
3980
  const key = `${node.id}:${s}:${i}:${handle}`;
3178
3981
  if (seen.has(key)) return;
@@ -3181,8 +3984,8 @@ function getVisibleHandles(state) {
3181
3984
  };
3182
3985
  if (state.tool === "pen" && state.pen) {
3183
3986
  const node = findVectorNode(state.document.nodes, state.pen.nodeId);
3184
- const sp = _optionalChain([node, 'optionalAccess', _42 => _42.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
3185
- if (_optionalChain([node, 'optionalAccess', _43 => _43.type]) === "path" && sp) {
3987
+ const sp = _optionalChain([node, 'optionalAccess', _67 => _67.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
3988
+ if (_optionalChain([node, 'optionalAccess', _68 => _68.type]) === "path" && sp) {
3186
3989
  const last = sp.anchors.length - 1;
3187
3990
  push(node, state.pen.subpath, last, "in");
3188
3991
  push(node, state.pen.subpath, last, "out");
@@ -3190,9 +3993,9 @@ function getVisibleHandles(state) {
3190
3993
  return out;
3191
3994
  }
3192
3995
  if (state.tool !== "direct") return out;
3193
- const editable = new Set(getEditablePathIds(state));
3996
+ const editable2 = new Set(getEditablePathIds(state));
3194
3997
  for (const ref of state.selectedAnchors) {
3195
- if (!editable.has(ref.nodeId)) continue;
3998
+ if (!editable2.has(ref.nodeId)) continue;
3196
3999
  const node = findVectorNode(state.document.nodes, ref.nodeId);
3197
4000
  const sp = node.subpaths[ref.subpath];
3198
4001
  if (!sp) continue;
@@ -3220,18 +4023,68 @@ function getVectorCornerWidgets(state) {
3220
4023
  const side = Math.min(c.lu, c.lv);
3221
4024
  if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
3222
4025
  const a = node.subpaths[c.subpath].anchors[c.index];
3223
- const radius = _nullishCoalesce(_optionalChain([a, 'access', _44 => _44.corner, 'optionalAccess', _45 => _45.radius]), () => ( 0));
4026
+ const radius = _nullishCoalesce(_optionalChain([a, 'access', _69 => _69.corner, 'optionalAccess', _70 => _70.radius]), () => ( 0));
3224
4027
  const drawn = fitCornerRadius(c, radius);
3225
4028
  const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
3226
4029
  const p = applyMatrix(m, cornerBisectorPoint(c, k));
3227
- out.push({ ref: { nodeId: id, subpath: c.subpath, index: c.index }, x: p.x, y: p.y, radius, drawnRadius: drawn, kind: _nullishCoalesce(_optionalChain([a, 'access', _46 => _46.corner, 'optionalAccess', _47 => _47.kind]), () => ( "round")), corner: c });
4030
+ out.push({ ref: { nodeId: id, subpath: c.subpath, index: c.index }, x: p.x, y: p.y, radius, drawnRadius: drawn, kind: _nullishCoalesce(_optionalChain([a, 'access', _71 => _71.corner, 'optionalAccess', _72 => _72.kind]), () => ( "round")), corner: c });
4031
+ }
4032
+ }
4033
+ return out;
4034
+ }
4035
+ function vectorStrokeWidthGeometry(node) {
4036
+ if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return null;
4037
+ const parts = vectorStrokeParts(subpathsToBezier(node.subpaths), node);
4038
+ return parts.line.length ? { line: parts.line, unit: parts.drawWidth / 2 } : null;
4039
+ }
4040
+ function getVectorWidthHandles(state) {
4041
+ if (state.tool !== "width") return [];
4042
+ const ids = /* @__PURE__ */ new Set();
4043
+ for (const id of state.selection) {
4044
+ const n = findVectorNode(state.document.nodes, id);
4045
+ if (n) for (const p of collectPathNodes(n)) ids.add(p.id);
4046
+ }
4047
+ if (_optionalChain([state, 'access', _73 => _73.hover, 'optionalAccess', _74 => _74.nodeId])) ids.add(state.hover.nodeId);
4048
+ if (_optionalChain([state, 'access', _75 => _75.interaction, 'optionalAccess', _76 => _76.type]) === "width") ids.add(state.interaction.ref.nodeId);
4049
+ if (state.selectedWidthPoint) ids.add(state.selectedWidthPoint.nodeId);
4050
+ const out = [];
4051
+ for (const id of ids) {
4052
+ const node = findVectorNode(state.document.nodes, id);
4053
+ if (_optionalChain([node, 'optionalAccess', _77 => _77.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _78 => _78.strokeWidthProfile, 'optionalAccess', _79 => _79.length])) continue;
4054
+ const g = vectorStrokeWidthGeometry(node);
4055
+ if (!g) continue;
4056
+ const profile = sanitizeWidthProfile(node.strokeWidthProfile);
4057
+ const m = _nullishCoalesce(vectorWorldMatrix(state.document.nodes, id), () => ( IDENTITY_MATRIX));
4058
+ const contours = node.subpaths.length;
4059
+ for (let sp = 0; sp < contours; sp++) {
4060
+ profile.forEach((w, index) => {
4061
+ const at2 = strokePositionPoint(g.line, sp, w.t);
4062
+ if (!at2) return;
4063
+ const n = { x: at2.tangent[1], y: -at2.tangent[0] };
4064
+ const c = { x: at2.point[0], y: at2.point[1] };
4065
+ const p = applyMatrix(m, c);
4066
+ const l = applyMatrix(m, { x: c.x + n.x * w.left * g.unit, y: c.y + n.y * w.left * g.unit });
4067
+ const r = applyMatrix(m, { x: c.x - n.x * w.right * g.unit, y: c.y - n.y * w.right * g.unit });
4068
+ const sel = state.selectedWidthPoint;
4069
+ out.push({
4070
+ ref: { nodeId: id, index, subpath: sp },
4071
+ x: p.x,
4072
+ y: p.y,
4073
+ left: l,
4074
+ right: r,
4075
+ t: w.t,
4076
+ leftScale: w.left,
4077
+ rightScale: w.right,
4078
+ selected: !!sel && sel.nodeId === id && sel.index === index
4079
+ });
4080
+ });
3228
4081
  }
3229
4082
  }
3230
4083
  return out;
3231
4084
  }
3232
4085
  function getAnchor(doc, ref) {
3233
4086
  const node = findVectorNode(doc.nodes, ref.nodeId);
3234
- return _optionalChain([node, 'optionalAccess', _48 => _48.type]) === "path" ? _optionalChain([node, 'access', _49 => _49.subpaths, 'access', _50 => _50[ref.subpath], 'optionalAccess', _51 => _51.anchors, 'access', _52 => _52[ref.index]]) : void 0;
4087
+ return _optionalChain([node, 'optionalAccess', _80 => _80.type]) === "path" ? _optionalChain([node, 'access', _81 => _81.subpaths, 'access', _82 => _82[ref.subpath], 'optionalAccess', _83 => _83.anchors, 'access', _84 => _84[ref.index]]) : void 0;
3235
4088
  }
3236
4089
  function getAnchorWorld(doc, ref) {
3237
4090
  const a = getAnchor(doc, ref);
@@ -3299,9 +4152,28 @@ var TOOL_LABELS = {
3299
4152
  line: "Line",
3300
4153
  scissors: "Scissors",
3301
4154
  knife: "Knife",
3302
- eraser: "Eraser"
4155
+ eraser: "Eraser",
4156
+ width: "Width",
4157
+ shapeBuilder: "Shape Builder",
4158
+ paintBucket: "Paint Bucket"
4159
+ };
4160
+ var TOOL_KEYS = {
4161
+ v: "select",
4162
+ a: "direct",
4163
+ p: "pen",
4164
+ n: "pencil",
4165
+ r: "rect",
4166
+ o: "ellipse",
4167
+ y: "polygon",
4168
+ s: "star",
4169
+ l: "line",
4170
+ c: "scissors",
4171
+ k: "knife",
4172
+ e: "eraser",
4173
+ w: "width",
4174
+ m: "shapeBuilder",
4175
+ b: "paintBucket"
3303
4176
  };
3304
- var TOOL_KEYS = { v: "select", a: "direct", p: "pen", n: "pencil", r: "rect", o: "ellipse", y: "polygon", s: "star", l: "line", c: "scissors", k: "knife", e: "eraser" };
3305
4177
  function vectorToolLabel(tool) {
3306
4178
  return TOOL_LABELS[tool];
3307
4179
  }
@@ -3317,7 +4189,10 @@ var VECTOR_TOOL_SHORTCUTS = {
3317
4189
  line: "L",
3318
4190
  scissors: "C",
3319
4191
  knife: "K",
3320
- eraser: "E"
4192
+ eraser: "E",
4193
+ width: "W",
4194
+ shapeBuilder: "M",
4195
+ paintBucket: "B"
3321
4196
  };
3322
4197
  var VECTOR_TOOL_GLYPHS = {
3323
4198
  select: "\u2196",
@@ -3331,7 +4206,10 @@ var VECTOR_TOOL_GLYPHS = {
3331
4206
  line: "\u2571",
3332
4207
  scissors: "\u2702",
3333
4208
  knife: "\u27CB",
3334
- eraser: "\u232B"
4209
+ eraser: "\u232B",
4210
+ width: "\u21F9",
4211
+ shapeBuilder: "\u2295",
4212
+ paintBucket: "\u25CD"
3335
4213
  };
3336
4214
  var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
3337
4215
  var BOOLEAN_OPS = {
@@ -3341,6 +4219,14 @@ var BOOLEAN_OPS = {
3341
4219
  exclude: "xor"
3342
4220
  };
3343
4221
  var BOOLEAN_LABELS = { union: "Union", subtract: "Subtract", intersect: "Intersect", exclude: "Exclude" };
4222
+ var VECTOR_PATHFINDER_LABELS = {
4223
+ divide: "Divide",
4224
+ trim: "Trim",
4225
+ merge: "Merge",
4226
+ crop: "Crop",
4227
+ outline: "Outline",
4228
+ minusBack: "Minus Back"
4229
+ };
3344
4230
  function mapTree(nodes, fn) {
3345
4231
  let changed = false;
3346
4232
  const out = [];
@@ -3385,35 +4271,43 @@ var VectorEditorController = (_class3 = class {
3385
4271
 
3386
4272
  __init9() {this.selection = []}
3387
4273
  __init10() {this.anchors = []}
3388
- __init11() {this.editingId = null}
3389
- __init12() {this.pen = null}
4274
+ __init11() {this.widthPoint = null}
4275
+ __init12() {this.editingId = null}
4276
+ __init13() {this.pen = null}
3390
4277
 
3391
4278
 
3392
4279
 
3393
4280
 
3394
4281
 
3395
4282
 
3396
- __init13() {this.hover = null}
3397
- __init14() {this.pointer = null}
3398
- __init15() {this.drag = null}
3399
- __init16() {this.interaction = null}
3400
- __init17() {this.guides = null}
4283
+ __init14() {this.hover = null}
4284
+ __init15() {this.pointer = null}
4285
+ __init16() {this.drag = null}
4286
+ __init17() {this.interaction = null}
4287
+ __init18() {this.guides = null}
3401
4288
  /** Whether the last `snapDrawn` landed on an object (guides may be hidden). */
3402
- __init18() {this.snappedToObject = false}
4289
+ __init19() {this.snappedToObject = false}
3403
4290
  /** Drawing targets for hovering the pen and shape tools, kept while the document and the excluded path stay the same. */
3404
- __init19() {this.hoverTargets = null}
3405
- __init20() {this.spaceHeld = false}
3406
- __init21() {this.clipboard = null}
4291
+ __init20() {this.hoverTargets = null}
4292
+ __init21() {this.spaceHeld = false}
4293
+ __init22() {this.clipboard = null}
4294
+ /** Shape Builder map of the selection, kept while the document and the selection stay the same. */
4295
+ __init23() {this.builderCache = null}
4296
+ /** Paint bucket map of the visible outlines. */
4297
+ __init24() {this.fillCache = null}
4298
+ __init25() {this.builderPreview = null}
4299
+ /** What the highlight shows, to skip notifying when the pointer stays over the same face. */
4300
+ __init26() {this.builderHoverKey = null}
3407
4301
 
3408
4302
 
3409
- __init22() {this.listeners = /* @__PURE__ */ new Set()}
3410
- __init23() {this.cleanups = []}
3411
- __init24() {this.applying = false}
4303
+ __init27() {this.listeners = /* @__PURE__ */ new Set()}
4304
+ __init28() {this.cleanups = []}
4305
+ __init29() {this.applying = false}
3412
4306
  /** Set while a commit records a change that was already applied, so `restore` still reports it. */
3413
- __init25() {this.committing = false}
3414
- __init26() {this.revision = 0}
4307
+ __init30() {this.committing = false}
4308
+ __init31() {this.revision = 0}
3415
4309
 
3416
- constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);
4310
+ constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);_class3.prototype.__init29.call(this);_class3.prototype.__init30.call(this);_class3.prototype.__init31.call(this);_class3.prototype.__init32.call(this);_class3.prototype.__init33.call(this);
3417
4311
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
3418
4312
  this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
3419
4313
  this.tool = _nullishCoalesce(options.tool, () => ( "select"));
@@ -3442,9 +4336,9 @@ var VectorEditorController = (_class3 = class {
3442
4336
  // State
3443
4337
  // -------------------------------------------------------------------------
3444
4338
  /** The current snapshot (stable until the next change). */
3445
- __init27() {this.getState = () => this.state}
4339
+ __init32() {this.getState = () => this.state}
3446
4340
  /** Calls `listener` after every change. Returns an unsubscribe function. */
3447
- __init28() {this.subscribe = (listener) => {
4341
+ __init33() {this.subscribe = (listener) => {
3448
4342
  this.listeners.add(listener);
3449
4343
  return () => {
3450
4344
  this.listeners.delete(listener);
@@ -3463,6 +4357,7 @@ var VectorEditorController = (_class3 = class {
3463
4357
  tool: this.tool,
3464
4358
  selection: this.selection,
3465
4359
  selectedAnchors: this.anchors,
4360
+ selectedWidthPoint: this.widthPoint,
3466
4361
  editingId: this.editingId,
3467
4362
  selectionBounds: this.boundsOf(this.selection),
3468
4363
  selectionFrame: this.frameOf(this.selectedRoots()),
@@ -3477,6 +4372,7 @@ var VectorEditorController = (_class3 = class {
3477
4372
  interaction: this.interaction,
3478
4373
  pen: this.pen,
3479
4374
  pointer: this.pointer,
4375
+ builder: this.builderPreview,
3480
4376
  canUndo: h.canUndo,
3481
4377
  canRedo: h.canRedo,
3482
4378
  undoLabel: h.undoName,
@@ -3487,7 +4383,7 @@ var VectorEditorController = (_class3 = class {
3487
4383
  notify(docChanged = false) {
3488
4384
  this.revision++;
3489
4385
  this.state = this.computeState();
3490
- if (docChanged) _optionalChain([this, 'access', _53 => _53.onChange, 'optionalCall', _54 => _54(this.doc)]);
4386
+ if (docChanged) _optionalChain([this, 'access', _85 => _85.onChange, 'optionalCall', _86 => _86(this.doc)]);
3491
4387
  const state = this.state;
3492
4388
  this.listeners.forEach((listener) => {
3493
4389
  try {
@@ -3517,20 +4413,29 @@ var VectorEditorController = (_class3 = class {
3517
4413
  return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
3518
4414
  }
3519
4415
  cursorFor() {
3520
- if (_optionalChain([this, 'access', _55 => _55.drag, 'optionalAccess', _56 => _56.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
3521
- if (_optionalChain([this, 'access', _57 => _57.drag, 'optionalAccess', _58 => _58.kind]) === "rotate") return "grabbing";
3522
- if (_optionalChain([this, 'access', _59 => _59.drag, 'optionalAccess', _60 => _60.kind]) === "move") return "move";
4416
+ if (_optionalChain([this, 'access', _87 => _87.drag, 'optionalAccess', _88 => _88.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
4417
+ if (_optionalChain([this, 'access', _89 => _89.drag, 'optionalAccess', _90 => _90.kind]) === "rotate") return "grabbing";
4418
+ if (_optionalChain([this, 'access', _91 => _91.drag, 'optionalAccess', _92 => _92.kind]) === "move") return "move";
3523
4419
  const h = this.hover;
3524
4420
  switch (this.tool) {
3525
4421
  case "select":
3526
- if (_optionalChain([h, 'optionalAccess', _61 => _61.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _62 => _62.frameOf, 'call', _63 => _63(this.selectedRoots()), 'optionalAccess', _64 => _64.angle]), () => ( 0)));
3527
- return _optionalChain([h, 'optionalAccess', _65 => _65.nodeId]) ? "move" : "default";
4422
+ if (_optionalChain([h, 'optionalAccess', _93 => _93.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _94 => _94.frameOf, 'call', _95 => _95(this.selectedRoots()), 'optionalAccess', _96 => _96.angle]), () => ( 0)));
4423
+ return _optionalChain([h, 'optionalAccess', _97 => _97.nodeId]) ? "move" : "default";
3528
4424
  case "direct":
3529
- if (_optionalChain([h, 'optionalAccess', _66 => _66.anchor]) || _optionalChain([h, 'optionalAccess', _67 => _67.handle]) || _optionalChain([h, 'optionalAccess', _68 => _68.corner])) return "pointer";
3530
- if (_optionalChain([h, 'optionalAccess', _69 => _69.segment])) return "copy";
3531
- return _optionalChain([h, 'optionalAccess', _70 => _70.nodeId]) ? "move" : "default";
4425
+ if (_optionalChain([h, 'optionalAccess', _98 => _98.anchor]) || _optionalChain([h, 'optionalAccess', _99 => _99.handle]) || _optionalChain([h, 'optionalAccess', _100 => _100.corner])) return "pointer";
4426
+ if (_optionalChain([h, 'optionalAccess', _101 => _101.segment])) return "copy";
4427
+ return _optionalChain([h, 'optionalAccess', _102 => _102.nodeId]) ? "move" : "default";
3532
4428
  case "pen":
3533
- return _optionalChain([h, 'optionalAccess', _71 => _71.anchor]) ? "pointer" : "crosshair";
4429
+ return _optionalChain([h, 'optionalAccess', _103 => _103.anchor]) ? "pointer" : "crosshair";
4430
+ case "width":
4431
+ if (_optionalChain([this, 'access', _104 => _104.drag, 'optionalAccess', _105 => _105.kind]) === "width") return this.drag.mode === "move" ? "grabbing" : "ew-resize";
4432
+ if (_optionalChain([h, 'optionalAccess', _106 => _106.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
4433
+ return _optionalChain([h, 'optionalAccess', _107 => _107.strokeSpot]) ? "copy" : "default";
4434
+ case "shapeBuilder":
4435
+ if (!_optionalChain([this, 'access', _108 => _108.builderPreview, 'optionalAccess', _109 => _109.faces, 'access', _110 => _110.length]) && !_optionalChain([this, 'access', _111 => _111.builderPreview, 'optionalAccess', _112 => _112.lines, 'access', _113 => _113.length])) return "crosshair";
4436
+ return this.builderPreview.mode === "erase" ? "not-allowed" : "cell";
4437
+ case "paintBucket":
4438
+ return this.builderPreview ? "cell" : "crosshair";
3534
4439
  default:
3535
4440
  return "crosshair";
3536
4441
  }
@@ -3550,7 +4455,7 @@ var VectorEditorController = (_class3 = class {
3550
4455
  this.selection = s.selection.filter((id) => this.getNode(id));
3551
4456
  this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
3552
4457
  this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
3553
- this.pen = s.pen && _optionalChain([this, 'access', _72 => _72.getNode, 'call', _73 => _73(s.pen.nodeId), 'optionalAccess', _74 => _74.subpaths, 'optionalAccess', _75 => _75[s.pen.subpath]]) ? s.pen : null;
4458
+ this.pen = s.pen && _optionalChain([this, 'access', _114 => _114.getNode, 'call', _115 => _115(s.pen.nodeId), 'optionalAccess', _116 => _116.subpaths, 'optionalAccess', _117 => _117[s.pen.subpath]]) ? s.pen : null;
3554
4459
  } finally {
3555
4460
  this.applying = false;
3556
4461
  }
@@ -3656,6 +4561,9 @@ var VectorEditorController = (_class3 = class {
3656
4561
  this.tool = tool;
3657
4562
  this.hover = null;
3658
4563
  this.guides = null;
4564
+ this.widthPoint = null;
4565
+ this.builderPreview = null;
4566
+ this.builderHoverKey = null;
3659
4567
  if (tool !== "direct") {
3660
4568
  this.anchors = [];
3661
4569
  this.editingId = null;
@@ -3714,7 +4622,7 @@ var VectorEditorController = (_class3 = class {
3714
4622
  if (res.kind) {
3715
4623
  this.snappedToObject = true;
3716
4624
  this.setGuides(res);
3717
- return _optionalChain([leave, 'optionalAccess', _76 => _76.constrain]) ? res.point : { x: res.x ? res.point.x : this.gridValue(res.point.x), y: res.y ? res.point.y : this.gridValue(res.point.y) };
4625
+ return _optionalChain([leave, 'optionalAccess', _118 => _118.constrain]) ? res.point : { x: res.x ? res.point.x : this.gridValue(res.point.x), y: res.y ? res.point.y : this.gridValue(res.point.y) };
3718
4626
  }
3719
4627
  }
3720
4628
  this.setGuides(null);
@@ -3732,14 +4640,14 @@ var VectorEditorController = (_class3 = class {
3732
4640
  /** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
3733
4641
  hoverSnap(p, input) {
3734
4642
  if (this.tool === "pen") {
3735
- const sp = this.pen ? _optionalChain([this, 'access', _77 => _77.getNode, 'call', _78 => _78(this.pen.nodeId), 'optionalAccess', _79 => _79.subpaths, 'access', _80 => _80[this.pen.subpath]]) : void 0;
3736
- if (this.pen && sp && _optionalChain([this, 'access', _81 => _81.hover, 'optionalAccess', _82 => _82.anchor])) {
4643
+ const sp = this.pen ? _optionalChain([this, 'access', _119 => _119.getNode, 'call', _120 => _120(this.pen.nodeId), 'optionalAccess', _121 => _121.subpaths, 'access', _122 => _122[this.pen.subpath]]) : void 0;
4644
+ if (this.pen && sp && _optionalChain([this, 'access', _123 => _123.hover, 'optionalAccess', _124 => _124.anchor])) {
3737
4645
  this.setGuides(null);
3738
4646
  return p;
3739
4647
  }
3740
4648
  const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
3741
4649
  const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
3742
- return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _83 => _83.pen, 'optionalAccess', _84 => _84.nodeId]), () => ( null))));
4650
+ return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _125 => _125.pen, 'optionalAccess', _126 => _126.nodeId]), () => ( null))));
3743
4651
  }
3744
4652
  if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
3745
4653
  this.setGuides(null);
@@ -3887,6 +4795,7 @@ var VectorEditorController = (_class3 = class {
3887
4795
  if (d.fillRule) out.fillRule = d.fillRule;
3888
4796
  if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
3889
4797
  for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
4798
+ if (_optionalChain([d, 'access', _127 => _127.strokeWidthProfile, 'optionalAccess', _128 => _128.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
3890
4799
  return out;
3891
4800
  }
3892
4801
  /** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
@@ -3907,10 +4816,39 @@ var VectorEditorController = (_class3 = class {
3907
4816
  }
3908
4817
  if (!targets.size) return { ok: false, message: "Nothing is selected." };
3909
4818
  this.change(_nullishCoalesce(options.label, () => ( "Change Style")), () => {
3910
- this.setDoc(mapTree(this.doc.nodes, (n) => targets.has(n.id) ? { ...n, ...style } : n));
4819
+ this.setDoc(mapTree(this.doc.nodes, (n) => targets.has(n.id) ? withStyle(n, style) : n));
3911
4820
  });
3912
4821
  return { ok: true };
3913
4822
  }
4823
+ /**
4824
+ * Sets the variable-width profile of the selected (or given) paths: a preset id
4825
+ * (`uniform` removes the profile), points, or a parametric taper / wave. Returns an error for
4826
+ * an unknown preset.
4827
+ */
4828
+ setWidthProfile(profile, options = {}) {
4829
+ let points;
4830
+ if (profile === null) points = [];
4831
+ else if (typeof profile === "string") {
4832
+ const preset = widthProfilePreset(profile);
4833
+ if (!preset) return { ok: false, message: `There is no width profile "${profile}".` };
4834
+ points = preset;
4835
+ } else if (Array.isArray(profile)) points = sanitizeWidthProfile(profile);
4836
+ else {
4837
+ const p = profile;
4838
+ points = p.taper || p.wave ? generateWidthProfile(p) : [];
4839
+ }
4840
+ this.widthPoint = null;
4841
+ return this.setStyle({ strokeWidthProfile: points.length ? points : null }, _nullishCoalesce(options.ids, () => ( this.selection)), { label: points.length ? "Width Profile" : "Uniform Width" });
4842
+ }
4843
+ /** Removes one width point of a path's profile (the width tool's Delete). */
4844
+ removeWidthPoint(ref) {
4845
+ const node = this.getNode(ref.nodeId);
4846
+ if (_optionalChain([node, 'optionalAccess', _129 => _129.type]) !== "path" || !_optionalChain([node, 'access', _130 => _130.strokeWidthProfile, 'optionalAccess', _131 => _131[ref.index]])) return { ok: false, message: "There is no such width point." };
4847
+ const rest = removeWidthPoints(sanitizeWidthProfile(node.strokeWidthProfile), [ref.index]);
4848
+ this.widthPoint = null;
4849
+ this.change("Delete Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: rest.length ? rest : null })));
4850
+ return { ok: true };
4851
+ }
3914
4852
  /**
3915
4853
  * Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
3916
4854
  * anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
@@ -3930,7 +4868,7 @@ var VectorEditorController = (_class3 = class {
3930
4868
  const refs = this.cornerTargets(options);
3931
4869
  if (!refs.length) return { ok: false, message: "Nothing is selected." };
3932
4870
  this.change("Corner Kind", () => {
3933
- this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _85 => _85.corner, 'optionalAccess', _86 => _86.radius]), () => ( 0)), kind));
4871
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _132 => _132.corner, 'optionalAccess', _133 => _133.radius]), () => ( 0)), kind));
3934
4872
  });
3935
4873
  return { ok: true };
3936
4874
  }
@@ -3981,6 +4919,7 @@ var VectorEditorController = (_class3 = class {
3981
4919
  arrowAlign: _al,
3982
4920
  startArrowScale: _ss,
3983
4921
  endArrowScale: _es,
4922
+ strokeWidthProfile: _wp,
3984
4923
  ...rest
3985
4924
  } = cloneVectorNode(p);
3986
4925
  const id = this.newId();
@@ -4068,10 +5007,10 @@ var VectorEditorController = (_class3 = class {
4068
5007
  return this.selection.filter((id) => {
4069
5008
  let covered = false;
4070
5009
  const loc = locateVectorNode(this.doc.nodes, id);
4071
- let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _87 => _87.parent]), () => ( null));
5010
+ let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _134 => _134.parent]), () => ( null));
4072
5011
  while (parent) {
4073
5012
  if (this.selection.includes(parent.id)) covered = true;
4074
- parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _88 => _88(this.doc.nodes, parent.id), 'optionalAccess', _89 => _89.parent]), () => ( null));
5013
+ parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _135 => _135(this.doc.nodes, parent.id), 'optionalAccess', _136 => _136.parent]), () => ( null));
4075
5014
  }
4076
5015
  return !covered;
4077
5016
  });
@@ -4254,8 +5193,8 @@ var VectorEditorController = (_class3 = class {
4254
5193
  if (!ids.length) return null;
4255
5194
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
4256
5195
  if (locs.some((l) => !l)) return null;
4257
- const parent = _nullishCoalesce(_optionalChain([locs, 'access', _90 => _90[0], 'access', _91 => _91.parent, 'optionalAccess', _92 => _92.id]), () => ( null));
4258
- if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _93 => _93.parent, 'optionalAccess', _94 => _94.id]), () => ( null))) !== parent)) return null;
5196
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _137 => _137[0], 'access', _138 => _138.parent, 'optionalAccess', _139 => _139.id]), () => ( null));
5197
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _140 => _140.parent, 'optionalAccess', _141 => _141.id]), () => ( null))) !== parent)) return null;
4259
5198
  const id = this.newId();
4260
5199
  this.change("Group", () => {
4261
5200
  const set = new Set(ids);
@@ -4278,11 +5217,11 @@ var VectorEditorController = (_class3 = class {
4278
5217
  }
4279
5218
  /** Replaces selected groups with their children. */
4280
5219
  ungroup(options = {}) {
4281
- const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _95 => _95.type]) === "group");
5220
+ const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _142 => _142.type]) === "group");
4282
5221
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
4283
5222
  const set = new Set(groups.map((g) => g.id));
4284
5223
  this.change("Ungroup", () => {
4285
- const flatten = (list) => {
5224
+ const flatten2 = (list) => {
4286
5225
  const out = [];
4287
5226
  let changed = false;
4288
5227
  for (const n of list) {
@@ -4294,14 +5233,14 @@ var VectorEditorController = (_class3 = class {
4294
5233
  ...n.children.map((c) => n.transform ? transformVectorNode(c, m) : c).map((c) => opacity !== void 0 && opacity < 1 ? { ...c, opacity: (_nullishCoalesce(c.opacity, () => ( 1))) * opacity } : c)
4295
5234
  );
4296
5235
  } else if (n.type === "group") {
4297
- const children = flatten(n.children);
5236
+ const children = flatten2(n.children);
4298
5237
  if (children !== n.children) changed = true;
4299
5238
  out.push(children !== n.children ? { ...n, children } : n);
4300
5239
  } else out.push(n);
4301
5240
  }
4302
5241
  return changed ? out : list;
4303
5242
  };
4304
- this.setDoc(flatten(this.doc.nodes));
5243
+ this.setDoc(flatten2(this.doc.nodes));
4305
5244
  this.selection = groups.flatMap((g) => g.children.map((c) => c.id));
4306
5245
  });
4307
5246
  return { ok: true };
@@ -4338,7 +5277,7 @@ var VectorEditorController = (_class3 = class {
4338
5277
  });
4339
5278
  const paths = [...new Set(order)].map((id2) => this.getNode(id2));
4340
5279
  if (paths.length < 2) return null;
4341
- const result = _chunkBGNPT5PEcjs.pathBoolean.call(void 0,
5280
+ const result = _chunk7EDLVBOOcjs.pathBoolean.call(void 0,
4342
5281
  BOOLEAN_OPS[op],
4343
5282
  paths.map((p) => ({
4344
5283
  path: vectorPathWorldBezier(
@@ -4388,11 +5327,11 @@ var VectorEditorController = (_class3 = class {
4388
5327
  /** A document-space path mapped into the parent space of node `id`. */
4389
5328
  intoParentSpace(id, path) {
4390
5329
  const parent = _nullishCoalesce(vectorParentMatrix(this.doc.nodes, id), () => ( IDENTITY_MATRIX));
4391
- return isRoot(parent) ? path : _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, path, invertMatrix(parent));
5330
+ return isRoot(parent) ? path : _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, invertMatrix(parent));
4392
5331
  }
4393
5332
  /** Splits selected compound paths into one path per subpath. */
4394
5333
  breakApart(options = {}) {
4395
- const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _96 => _96.type]) === "path" && n.subpaths.length > 1);
5334
+ const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _143 => _143.type]) === "path" && n.subpaths.length > 1);
4396
5335
  if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
4397
5336
  const created = [];
4398
5337
  const set = new Set(paths.map((p) => p.id));
@@ -4412,6 +5351,248 @@ var VectorEditorController = (_class3 = class {
4412
5351
  return { ok: true };
4413
5352
  }
4414
5353
  // -------------------------------------------------------------------------
5354
+ // Pathfinder, Shape Builder and paint bucket
5355
+ // -------------------------------------------------------------------------
5356
+ /**
5357
+ * Pathfinder: Divide, Trim, Merge, Crop, Outline or Minus Back over the selected (or given)
5358
+ * paths, in document space (rotated nodes and groups included, live corners cut). Unfilled
5359
+ * stroked paths take part as the area their stroke paints (Divide: open ones cut instead).
5360
+ * The results replace the operands at the place of the top-most one, grouped when there are
5361
+ * several; each keeps the style of the operand whose area it is. Returns the new path ids,
5362
+ * or an empty list when nothing came out.
5363
+ */
5364
+ pathfinder(op, options = {}) {
5365
+ const roots = _nullishCoalesce(options.ids, () => ( this.selectedRoots()));
5366
+ const entries = vectorPathEntries(this.doc, roots);
5367
+ if (entries.length < (op === "divide" || op === "outline" ? 1 : 2)) return [];
5368
+ const shapes = entries.map((e) => vectorPathfinderShape(e, op));
5369
+ const result = _chunk7EDLVBOOcjs.pathfinder.call(void 0, op, shapes);
5370
+ if (result.error || !result.pieces.length) return [];
5371
+ const last = entries[entries.length - 1].node.id;
5372
+ const topRoot = _nullishCoalesce([...roots].reverse().find((r) => {
5373
+ const n = this.getNode(r);
5374
+ return !!n && !!findVectorNode([n], last);
5375
+ }), () => ( last));
5376
+ const nodes = [];
5377
+ for (const piece of result.pieces) {
5378
+ const subpaths = bezierToSubpaths(this.intoParentSpace(topRoot, piece.path)).map((sp) => piece.open ? { ...sp, closed: false } : sp);
5379
+ if (!subpaths.length) continue;
5380
+ const source = entries[piece.source].node;
5381
+ const style = vectorResultStyle(source, shapes[piece.source].paint, op === "outline");
5382
+ nodes.push({ ...style, type: "path", id: this.newId(), name: _nullishCoalesce(source.name, () => ( "Path")), subpaths });
5383
+ }
5384
+ if (!nodes.length) return [];
5385
+ const label = VECTOR_PATHFINDER_LABELS[op];
5386
+ const container = nodes.length > 1 ? { id: this.newId(), type: "group", name: label, children: nodes } : nodes[0];
5387
+ const drop = /* @__PURE__ */ new Set([...entries.map((e) => e.node.id), ...roots]);
5388
+ this.change(label, () => {
5389
+ this.setDoc(mapTree(this.doc.nodes, (n) => n.id === topRoot ? container : drop.has(n.id) ? null : n));
5390
+ this.selection = [container.id];
5391
+ this.anchors = [];
5392
+ this.editingId = null;
5393
+ });
5394
+ return nodes.map((n) => n.id);
5395
+ }
5396
+ /** The Shape Builder map of the selected (or given) paths, cached per document and selection. */
5397
+ shapeBuilderMap(ids) {
5398
+ const roots = _nullishCoalesce(ids, () => ( this.selectedRoots()));
5399
+ const key = roots.join("\0");
5400
+ if (_optionalChain([this, 'access', _144 => _144.builderCache, 'optionalAccess', _145 => _145.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
5401
+ const entries = roots.length ? vectorPathEntries(this.doc, roots) : [];
5402
+ const map = entries.length ? vectorShapeBuilderMap(entries) : null;
5403
+ this.builderCache = { doc: this.doc, key, map };
5404
+ return map;
5405
+ }
5406
+ /** The faces the Shape Builder offers for the selected (or given) paths (document units). */
5407
+ getShapeBuilderFaces(options = {}) {
5408
+ const m = this.shapeBuilderMap(options.ids);
5409
+ if (!m) return [];
5410
+ return m.map.faces.map((f, index) => {
5411
+ const p = m.map.faceInterior(index);
5412
+ return {
5413
+ index,
5414
+ d: _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, f.path, { precision: 3 }),
5415
+ area: f.area,
5416
+ sources: m.map.faceSources(index).map((i) => m.entries[i].node.id),
5417
+ point: p ? { x: p[0], y: p[1] } : null
5418
+ };
5419
+ });
5420
+ }
5421
+ /** Pick distance of the Shape Builder's erase for line pieces (document units). */
5422
+ builderLineTolerance() {
5423
+ return Math.max(this.hitRadius, 4) / this.viewport.zoom;
5424
+ }
5425
+ /**
5426
+ * Shape Builder gesture along `points` (document units; one point is a click) over the faces
5427
+ * the selected (or given) paths make. Merges the faces touched into one shape styled like the
5428
+ * top-most shape over the first face (the current fill for a face only outlines enclose);
5429
+ * with `erase` deletes them, and the pieces of unfilled open paths within `tolerance`. Every
5430
+ * shape covering a touched face loses that area. One undo step.
5431
+ */
5432
+ shapeBuilder(points, options = {}) {
5433
+ const none = (message) => ({ ok: false, message, merged: null, faces: 0, lines: 0 });
5434
+ if (!points.length || !points.every((p) => Number.isFinite(p.x) && Number.isFinite(p.y))) return none("Give the points of the gesture.");
5435
+ const m = this.shapeBuilderMap(options.ids);
5436
+ if (!m) return none("Select paths that make faces.");
5437
+ const erase = !!options.erase;
5438
+ const tol = erase ? _nullishCoalesce(options.tolerance, () => ( this.builderLineTolerance())) : null;
5439
+ const touched = { faces: [], lines: [] };
5440
+ const size = m.map.faces.reduce((s, f) => Math.max(s, f.bounds.maxX - f.bounds.minX, f.bounds.maxY - f.bounds.minY), 1);
5441
+ for (const p of samplePolyline(points, Math.max(size / 2e3, 0.25 / this.viewport.zoom))) addBuilderHit(touched, vectorShapeBuilderHit(m, p, tol));
5442
+ if (!touched.faces.length && !touched.lines.length) return none("There is no face under the pointer.");
5443
+ return this.applyShapeBuilder(m, touched, erase);
5444
+ }
5445
+ applyShapeBuilder(m, touched, erase) {
5446
+ const plan = planShapeBuilder(m, touched, erase);
5447
+ let mergedId = null;
5448
+ const created = /* @__PURE__ */ new Map();
5449
+ this.change(erase ? "Shape Builder Delete" : "Shape Builder", () => {
5450
+ let nodes = this.doc.nodes;
5451
+ m.entries.forEach((entry, i) => {
5452
+ const node = entry.node;
5453
+ const toLocal = (path) => _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, invertMatrix(multiplyMatrices(nodeMatrix(node), entry.parent)));
5454
+ const list = [];
5455
+ const r = plan.replace.get(i);
5456
+ if (r === void 0) list.push(node);
5457
+ else if (_optionalChain([r, 'optionalAccess', _146 => _146.kind]) === "fill") list.push({ ...node, fillRule: "nonzero", subpaths: bezierToSubpaths(toLocal(r.path)) });
5458
+ else if (_optionalChain([r, 'optionalAccess', _147 => _147.kind]) === "pieces") {
5459
+ r.paths.forEach(
5460
+ (path, k) => list.push({ ...node, id: k === 0 ? node.id : this.newId(), subpaths: bezierToSubpaths(toLocal(path)).map((sp) => ({ ...sp, closed: false })) })
5461
+ );
5462
+ }
5463
+ if (plan.merged && plan.merged.style === i) {
5464
+ mergedId = this.newId();
5465
+ const merged = {
5466
+ ...vectorResultStyle(node, "fill"),
5467
+ type: "path",
5468
+ id: mergedId,
5469
+ name: "Shape",
5470
+ ...node.transform ? { transform: node.transform } : {},
5471
+ subpaths: bezierToSubpaths(toLocal(plan.merged.path))
5472
+ };
5473
+ if (plan.merged.currentFill || !hasPaint(merged.fill)) merged.fill = hasPaint(this.defaults.fill) ? this.defaults.fill : "#808080";
5474
+ list.push(merged);
5475
+ }
5476
+ if (r === void 0 && list.length === 1) return;
5477
+ created.set(node.id, list.map((n) => n.id));
5478
+ nodes = spliceTree(nodes, node.id, list);
5479
+ });
5480
+ this.setDoc(nodes);
5481
+ const selection = this.selection.flatMap((id) => _nullishCoalesce(created.get(id), () => ( [id])));
5482
+ if (mergedId && !selection.includes(mergedId)) selection.push(mergedId);
5483
+ this.selection = selection.filter((id, k) => !!this.getNode(id) && selection.indexOf(id) === k);
5484
+ this.anchors = [];
5485
+ this.editingId = null;
5486
+ });
5487
+ this.builderCache = null;
5488
+ return { ok: true, merged: mergedId, faces: touched.faces.length, lines: touched.lines.length };
5489
+ }
5490
+ /** The paint bucket's map of the visible outlines, cached per document and gap. */
5491
+ fillMap(gap) {
5492
+ if (_optionalChain([this, 'access', _148 => _148.fillCache, 'optionalAccess', _149 => _149.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
5493
+ const map = vectorFillMap(this.doc, gap);
5494
+ this.fillCache = { doc: this.doc, gap, map };
5495
+ return map;
5496
+ }
5497
+ /**
5498
+ * Paint bucket: the region the visible outlines enclose around `point` (document units),
5499
+ * crossings split and islands left as holes, becomes a new path filled with the current fill
5500
+ * default, placed just below the selection (or on top when nothing is selected). `gap`
5501
+ * (default `editing.fillGap`) closes gaps up to that size between loose ends. Returns the new
5502
+ * path's id, or null when nothing encloses the point.
5503
+ */
5504
+ fillRegion(point, options = {}) {
5505
+ if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) return null;
5506
+ const region = _optionalChain([this, 'access', _150 => _150.fillMap, 'call', _151 => _151(_nullishCoalesce(options.gap, () => ( this.editing.fillGap))), 'optionalAccess', _152 => _152.regionAt, 'call', _153 => _153([point.x, point.y])]);
5507
+ if (!_optionalChain([region, 'optionalAccess', _154 => _154.length])) return null;
5508
+ let below = null;
5509
+ const roots = new Set(this.selectedRoots());
5510
+ walkVectorNodes(this.doc.nodes, (n) => {
5511
+ if (!below && roots.has(n.id)) below = n.id;
5512
+ });
5513
+ const subpaths = bezierToSubpaths(below ? this.intoParentSpace(below, region) : region);
5514
+ if (!subpaths.length) return null;
5515
+ const id = this.newId();
5516
+ const node = { id, type: "path", name: "Fill", ...this.styleFor(false), stroke: null, fillRule: "nonzero", subpaths };
5517
+ if (!hasPaint(node.fill)) node.fill = "#808080";
5518
+ const target = below;
5519
+ this.change("Fill Region", () => {
5520
+ if (target) {
5521
+ const current = this.getNode(target);
5522
+ this.setDoc(spliceTree(this.doc.nodes, target, [node, current]));
5523
+ } else this.setDoc([...this.doc.nodes, node]);
5524
+ if (!target) this.selection = [id];
5525
+ this.anchors = [];
5526
+ this.editingId = null;
5527
+ });
5528
+ return id;
5529
+ }
5530
+ /** Updates the Shape Builder / paint bucket highlight for the pointer at `p`; true when it changed. */
5531
+ hoverBuilder(p, alt) {
5532
+ if (this.tool === "paintBucket") {
5533
+ const region = _nullishCoalesce(_optionalChain([this, 'access', _155 => _155.fillMap, 'call', _156 => _156(this.editing.fillGap), 'optionalAccess', _157 => _157.regionAt, 'call', _158 => _158([p.x, p.y])]), () => ( null));
5534
+ const key2 = region ? _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, region, { precision: 2 }) : null;
5535
+ if (key2 === this.builderHoverKey) return false;
5536
+ this.builderHoverKey = key2;
5537
+ this.builderPreview = region ? { mode: "fill", faces: [region], lines: [], trail: null } : null;
5538
+ return true;
5539
+ }
5540
+ const m = this.selectedRoots().length ? this.shapeBuilderMap() : null;
5541
+ const hit = m ? vectorShapeBuilderHit(m, p, alt ? this.builderLineTolerance() : null) : null;
5542
+ const key = hit ? `${alt ? "erase" : "merge"}:${JSON.stringify(hit)}` : null;
5543
+ if (key === this.builderHoverKey) return false;
5544
+ this.builderHoverKey = key;
5545
+ this.builderPreview = hit && m ? this.builderPreviewOf(m, "face" in hit ? { faces: [hit.face], lines: [] } : { faces: [], lines: [hit.line] }, alt, null) : null;
5546
+ return true;
5547
+ }
5548
+ builderPreviewOf(m, touched, erase, trail) {
5549
+ const faces = m.map.faces;
5550
+ return {
5551
+ mode: erase ? "erase" : "merge",
5552
+ faces: touched.faces.map((f) => faces[f].path),
5553
+ lines: touched.lines.map((k) => m.pieces[k].path),
5554
+ trail
5555
+ };
5556
+ }
5557
+ downBuilder(p, input) {
5558
+ this.builderHoverKey = null;
5559
+ if (this.tool === "paintBucket") {
5560
+ this.fillRegion(p);
5561
+ this.builderPreview = null;
5562
+ this.hoverBuilder(p, false);
5563
+ this.notify();
5564
+ return;
5565
+ }
5566
+ const m = this.selectedRoots().length ? this.shapeBuilderMap() : null;
5567
+ if (!m) return;
5568
+ const erase = !!input.alt;
5569
+ const touched = { faces: [], lines: [] };
5570
+ addBuilderHit(touched, vectorShapeBuilderHit(m, p, erase ? this.builderLineTolerance() : null));
5571
+ this.drag = { kind: "builder", erase, points: [p], touched, map: m };
5572
+ this.interaction = { type: "builder", mode: erase ? "erase" : "merge", points: [p] };
5573
+ this.builderPreview = this.builderPreviewOf(m, touched, erase, [p]);
5574
+ this.notify();
5575
+ }
5576
+ moveBuilder(drag, p) {
5577
+ const last = drag.points[drag.points.length - 1];
5578
+ if (Math.hypot(p.x - last.x, p.y - last.y) * this.viewport.zoom < 1) return;
5579
+ const tol = drag.erase ? this.builderLineTolerance() : null;
5580
+ for (const q of samplePolyline([last, p], 2 / this.viewport.zoom)) addBuilderHit(drag.touched, vectorShapeBuilderHit(drag.map, q, tol));
5581
+ drag.points.push(p);
5582
+ this.interaction = { type: "builder", mode: drag.erase ? "erase" : "merge", points: drag.points.slice() };
5583
+ this.builderPreview = this.builderPreviewOf(drag.map, drag.touched, drag.erase, drag.points.slice());
5584
+ this.notify();
5585
+ }
5586
+ upBuilder(drag) {
5587
+ this.builderPreview = null;
5588
+ this.builderHoverKey = null;
5589
+ if (!drag.touched.faces.length && !drag.touched.lines.length) {
5590
+ this.notify();
5591
+ return;
5592
+ }
5593
+ this.applyShapeBuilder(drag.map, drag.touched, drag.erase);
5594
+ }
5595
+ // -------------------------------------------------------------------------
4415
5596
  // Anchor editing
4416
5597
  // -------------------------------------------------------------------------
4417
5598
  updateAnchors(doc, refs, fn) {
@@ -4454,7 +5635,7 @@ var VectorEditorController = (_class3 = class {
4454
5635
  /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
4455
5636
  curvatureMoved(doc, ref, to) {
4456
5637
  const node = findVectorNode(doc.nodes, ref.nodeId);
4457
- if (_optionalChain([node, 'optionalAccess', _97 => _97.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5638
+ if (_optionalChain([node, 'optionalAccess', _159 => _159.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
4458
5639
  const q = this.toLocal(ref.nodeId, to, doc);
4459
5640
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
4460
5641
  const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
@@ -4463,7 +5644,7 @@ var VectorEditorController = (_class3 = class {
4463
5644
  /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
4464
5645
  bent(doc, ref, t, p) {
4465
5646
  const node = findVectorNode(doc.nodes, ref.nodeId);
4466
- if (_optionalChain([node, 'optionalAccess', _98 => _98.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5647
+ if (_optionalChain([node, 'optionalAccess', _160 => _160.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
4467
5648
  const q = this.toLocal(ref.nodeId, p, doc);
4468
5649
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
4469
5650
  return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
@@ -4474,7 +5655,7 @@ var VectorEditorController = (_class3 = class {
4474
5655
  */
4475
5656
  bendSegment(ref, to, t = 0.5) {
4476
5657
  const node = this.getNode(ref.nodeId);
4477
- const sp = _optionalChain([node, 'optionalAccess', _99 => _99.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
5658
+ const sp = _optionalChain([node, 'optionalAccess', _161 => _161.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
4478
5659
  if (!sp || ref.segment < 0 || ref.segment >= (sp.closed ? sp.anchors.length : sp.anchors.length - 1) || sp.anchors.length < 2) return { ok: false, message: "There is no such segment." };
4479
5660
  if (node.locked) return { ok: false, message: "The path is locked." };
4480
5661
  this.change("Bend Segment", () => {
@@ -4509,7 +5690,7 @@ var VectorEditorController = (_class3 = class {
4509
5690
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
4510
5691
  insertAnchorAt(nodeId, point) {
4511
5692
  const node = this.getNode(nodeId);
4512
- if (_optionalChain([node, 'optionalAccess', _100 => _100.type]) !== "path") return null;
5693
+ if (_optionalChain([node, 'optionalAccess', _162 => _162.type]) !== "path") return null;
4513
5694
  const hit = nearestSegment(node, this.toLocal(nodeId, point));
4514
5695
  if (!hit) return null;
4515
5696
  const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
@@ -4553,7 +5734,7 @@ var VectorEditorController = (_class3 = class {
4553
5734
  setClosed(closed, nodeId) {
4554
5735
  const targets = nodeId ? (() => {
4555
5736
  const n = this.getNode(nodeId);
4556
- return _optionalChain([n, 'optionalAccess', _101 => _101.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
5737
+ return _optionalChain([n, 'optionalAccess', _163 => _163.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
4557
5738
  })() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
4558
5739
  if (!targets.length) return { ok: false, message: "Select a path or points." };
4559
5740
  this.change(closed ? "Close Path" : "Open Path", () => {
@@ -4571,7 +5752,12 @@ var VectorEditorController = (_class3 = class {
4571
5752
  const set = new Set(ids.flatMap((id) => this.getNode(id) ? collectPathNodes(this.getNode(id)) : []).map((p) => p.id));
4572
5753
  if (!set.size) return { ok: false, message: "Select a path." };
4573
5754
  this.change("Reverse Path", () => {
4574
- this.setDoc(mapTree(this.doc.nodes, (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath) } : n));
5755
+ this.setDoc(
5756
+ mapTree(
5757
+ this.doc.nodes,
5758
+ (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _164 => _164.strokeWidthProfile, 'optionalAccess', _165 => _165.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
5759
+ )
5760
+ );
4575
5761
  this.anchors = [];
4576
5762
  });
4577
5763
  return { ok: true };
@@ -4652,9 +5838,9 @@ var VectorEditorController = (_class3 = class {
4652
5838
  const cx = points.reduce((s, p) => s + p.at.x, 0) / points.length;
4653
5839
  const cy = points.reduce((s, p) => s + p.at.y, 0) / points.length;
4654
5840
  this.change("Average", () => {
4655
- for (const { ref, at } of points) {
4656
- const dx = axis === "horizontal" ? 0 : cx - at.x;
4657
- const dy = axis === "vertical" ? 0 : cy - at.y;
5841
+ for (const { ref, at: at2 } of points) {
5842
+ const dx = axis === "horizontal" ? 0 : cx - at2.x;
5843
+ const dy = axis === "vertical" ? 0 : cy - at2.y;
4658
5844
  if (dx || dy) this.doc = this.movedAnchors(this.doc, [ref], dx, dy);
4659
5845
  }
4660
5846
  });
@@ -4707,7 +5893,7 @@ var VectorEditorController = (_class3 = class {
4707
5893
  */
4708
5894
  cutPathAt(nodeId, point) {
4709
5895
  const node = this.getNode(nodeId);
4710
- if (_optionalChain([node, 'optionalAccess', _102 => _102.type]) !== "path" || node.locked) return null;
5896
+ if (_optionalChain([node, 'optionalAccess', _166 => _166.type]) !== "path" || node.locked) return null;
4711
5897
  const local = this.toLocal(nodeId, point);
4712
5898
  const hit = nearestSegment(node, local);
4713
5899
  if (!hit) return null;
@@ -4738,8 +5924,8 @@ var VectorEditorController = (_class3 = class {
4738
5924
  const node = this.getNode(id);
4739
5925
  if (node.locked) continue;
4740
5926
  const ids = this.applyCuts(node, (sp, i) => {
4741
- const at = list.filter((r) => r.subpath === i).map((r) => r.index);
4742
- const pieces = at.length ? cutSubpathAt(sp, at) : null;
5927
+ const at2 = list.filter((r) => r.subpath === i).map((r) => r.index);
5928
+ const pieces = at2.length ? cutSubpathAt(sp, at2) : null;
4743
5929
  return pieces && !(pieces.length === 1 && pieces[0].closed === sp.closed) ? pieces : null;
4744
5930
  });
4745
5931
  if (ids) created.push(...ids);
@@ -4757,7 +5943,7 @@ var VectorEditorController = (_class3 = class {
4757
5943
  if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
4758
5944
  });
4759
5945
  const visible = (p) => !blocked.has(p.id);
4760
- if (_optionalChain([ids, 'optionalAccess', _103 => _103.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
5946
+ if (_optionalChain([ids, 'optionalAccess', _167 => _167.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
4761
5947
  const box = polylineBounds(points.map(toPoint2), pad);
4762
5948
  if (!box) return [];
4763
5949
  const out = [];
@@ -4813,10 +5999,10 @@ var VectorEditorController = (_class3 = class {
4813
5999
  const region = eraserRegion(pts, width);
4814
6000
  if (!region.length) return [];
4815
6001
  const plan = [];
4816
- const rb = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, region);
6002
+ const rb = _chunk7EDLVBOOcjs.bezierPathBounds.call(void 0, region);
4817
6003
  const reach = rb ? [{ x: rb.minX, y: rb.minY }, { x: rb.maxX, y: rb.maxY }] : points;
4818
6004
  for (const node of this.strokeTargets(reach, options.ids, 0)) {
4819
- const local = _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, region, invertMatrix(this.worldOf(node.id)));
6005
+ const local = _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, region, invertMatrix(this.worldOf(node.id)));
4820
6006
  const live = node.subpaths.filter((sp) => sp.anchors.length > 1);
4821
6007
  if (!live.length) continue;
4822
6008
  if (hasPaint(node.fill) && live.every((sp) => sp.closed)) {
@@ -4843,7 +6029,7 @@ var VectorEditorController = (_class3 = class {
4843
6029
  finishPath() {
4844
6030
  if (!this.pen) return;
4845
6031
  const node = this.getNode(this.pen.nodeId);
4846
- const sp = _optionalChain([node, 'optionalAccess', _104 => _104.subpaths, 'access', _105 => _105[this.pen.subpath]]);
6032
+ const sp = _optionalChain([node, 'optionalAccess', _168 => _168.subpaths, 'access', _169 => _169[this.pen.subpath]]);
4847
6033
  const pen = this.pen;
4848
6034
  this.pen = null;
4849
6035
  this.pointer = null;
@@ -4887,19 +6073,19 @@ var VectorEditorController = (_class3 = class {
4887
6073
  if (trimmed.startsWith("{")) {
4888
6074
  try {
4889
6075
  const data = JSON.parse(trimmed);
4890
- if (_optionalChain([data, 'optionalAccess', _106 => _106.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
4891
- } catch (e2) {
6076
+ if (_optionalChain([data, 'optionalAccess', _170 => _170.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
6077
+ } catch (e3) {
4892
6078
  nodes = null;
4893
6079
  }
4894
6080
  } else if (/<svg[\s>]/i.test(trimmed)) {
4895
6081
  try {
4896
6082
  nodes = parseVectorSvg(trimmed).nodes;
4897
- } catch (e3) {
6083
+ } catch (e4) {
4898
6084
  nodes = null;
4899
6085
  }
4900
6086
  }
4901
6087
  } else nodes = this.clipboard;
4902
- if (!_optionalChain([nodes, 'optionalAccess', _107 => _107.length])) return [];
6088
+ if (!_optionalChain([nodes, 'optionalAccess', _171 => _171.length])) return [];
4903
6089
  const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
4904
6090
  const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
4905
6091
  this.change("Paste", () => {
@@ -4943,14 +6129,14 @@ var VectorEditorController = (_class3 = class {
4943
6129
  if (!frame) return null;
4944
6130
  const { box, handles } = getVectorSelectionBoxGeometry(frame, this.viewport);
4945
6131
  const r = this.hitRadius + 2;
4946
- const usable = box.width < 2 * r || box.height < 2 * r ? handles.filter((h) => h.handle.length === 2 || h.handle === "rotate") : handles;
4947
- for (const h of usable) if (Math.hypot(h.screen.x - screen.x, h.screen.y - screen.y) <= r * Math.SQRT2) return h.handle;
6132
+ const usable2 = box.width < 2 * r || box.height < 2 * r ? handles.filter((h) => h.handle.length === 2 || h.handle === "rotate") : handles;
6133
+ for (const h of usable2) if (Math.hypot(h.screen.x - screen.x, h.screen.y - screen.y) <= r * Math.SQRT2) return h.handle;
4948
6134
  return null;
4949
6135
  }
4950
6136
  hitHandle(screen) {
4951
6137
  for (const ref of getVisibleHandles(this.view()).reverse()) {
4952
6138
  const a = getAnchorWorld(this.doc, ref);
4953
- const h = _optionalChain([a, 'optionalAccess', _108 => _108[ref.handle]]);
6139
+ const h = _optionalChain([a, 'optionalAccess', _172 => _172[ref.handle]]);
4954
6140
  if (!h) continue;
4955
6141
  const s = this.documentToScreen(h);
4956
6142
  if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
@@ -5053,10 +6239,131 @@ var VectorEditorController = (_class3 = class {
5053
6239
  const seg = this.nearestOutline(p);
5054
6240
  return seg ? { nodeId: seg.nodeId, segment: seg } : null;
5055
6241
  }
6242
+ case "width": {
6243
+ const hit = this.hitWidthHandle(screen);
6244
+ if (hit) return { nodeId: hit.ref.nodeId, widthPoint: hit.ref };
6245
+ const spot = this.strokeSpotAt(p);
6246
+ return spot ? { nodeId: spot.nodeId, strokeSpot: spot } : null;
6247
+ }
5056
6248
  default:
5057
6249
  return null;
5058
6250
  }
5059
6251
  }
6252
+ widthView() {
6253
+ return { document: this.doc, selection: this.selection, tool: this.tool, hover: this.hover, interaction: this.interaction, selectedWidthPoint: this.widthPoint };
6254
+ }
6255
+ /** The width point centre or side end under `screen` (width tool). */
6256
+ hitWidthHandle(screen) {
6257
+ let best = null;
6258
+ let bestD = Infinity;
6259
+ for (const h of getVectorWidthHandles(this.widthView())) {
6260
+ for (const [part, q] of [["center", h], ["left", h.left], ["right", h.right]]) {
6261
+ const s = this.documentToScreen(q);
6262
+ const d = Math.hypot(s.x - screen.x, s.y - screen.y);
6263
+ if (d <= this.hitRadius && d < bestD - 1e-9) {
6264
+ best = { ref: { ...h.ref, part } };
6265
+ bestD = d;
6266
+ }
6267
+ }
6268
+ }
6269
+ return best;
6270
+ }
6271
+ /** The nearest stroked, unlocked visible path whose stroke is under document point `p` (width tool). */
6272
+ strokeSpotAt(p) {
6273
+ let best = null;
6274
+ const tol = this.tolerance();
6275
+ walkVectorNodes(this.doc.nodes, (n) => {
6276
+ if (n.type !== "path" || n.visible === false || n.locked) return;
6277
+ if (hiddenOrLocked(this.doc.nodes, n.id)) return;
6278
+ const g = vectorStrokeWidthGeometry(n);
6279
+ if (!g) return;
6280
+ const m = this.worldOf(n.id);
6281
+ const local = applyMatrix(invertMatrix(m), p);
6282
+ const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
6283
+ if (!near2) return;
6284
+ const profile = strokeWidthProfileOf(n);
6285
+ const w = widthProfileAt(profile, near2.t);
6286
+ const reach = Math.max(near2.side === "left" ? w.left : w.right, 0) * g.unit + tol;
6287
+ if (near2.distance > reach || near2.distance > widthProfileMaxScale(profile) * g.unit + tol) return;
6288
+ if (best && near2.distance >= best.distance) return;
6289
+ const nx = near2.tangent[1];
6290
+ const ny = -near2.tangent[0];
6291
+ const [cx, cy] = near2.point;
6292
+ best = {
6293
+ nodeId: n.id,
6294
+ subpath: near2.contour,
6295
+ t: near2.t,
6296
+ point: applyMatrix(m, { x: cx, y: cy }),
6297
+ left: applyMatrix(m, { x: cx + nx * w.left * g.unit, y: cy + ny * w.left * g.unit }),
6298
+ right: applyMatrix(m, { x: cx - nx * w.right * g.unit, y: cy - ny * w.right * g.unit }),
6299
+ distance: near2.distance
6300
+ };
6301
+ });
6302
+ if (!best) return null;
6303
+ const { distance: _d, ...spot } = best;
6304
+ return spot;
6305
+ }
6306
+ downWidth(screen, p, input) {
6307
+ const hit = this.hitWidthHandle(screen);
6308
+ const spot = hit ? null : this.strokeSpotAt(p);
6309
+ const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _173 => _173.ref, 'access', _174 => _174.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _175 => _175.nodeId])));
6310
+ const node = nodeId ? this.getNode(nodeId) : void 0;
6311
+ const g = _optionalChain([node, 'optionalAccess', _176 => _176.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
6312
+ if (!node || node.type !== "path" || !g) {
6313
+ if (this.widthPoint) {
6314
+ this.widthPoint = null;
6315
+ this.notify();
6316
+ }
6317
+ return;
6318
+ }
6319
+ const before = this.snapshot();
6320
+ const local = this.toLocal(node.id, p);
6321
+ let profile;
6322
+ let ref;
6323
+ let mode = "width";
6324
+ let side = "both";
6325
+ let t;
6326
+ let subpath;
6327
+ if (hit) {
6328
+ profile = sanitizeWidthProfile(node.strokeWidthProfile);
6329
+ ref = { nodeId: node.id, index: hit.ref.index, subpath: hit.ref.subpath };
6330
+ t = _nullishCoalesce(_optionalChain([profile, 'access', _177 => _177[ref.index], 'optionalAccess', _178 => _178.t]), () => ( 0));
6331
+ subpath = hit.ref.subpath;
6332
+ if (hit.ref.part === "center") mode = "move";
6333
+ else if (input.alt) side = hit.ref.part;
6334
+ } else {
6335
+ const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
6336
+ const inserted = insertWidthPoint(strokeWidthProfileOf(node), near2.t);
6337
+ profile = inserted.points;
6338
+ subpath = near2.contour;
6339
+ t = near2.t;
6340
+ ref = { nodeId: node.id, index: inserted.index, subpath };
6341
+ if (input.alt) side = near2.side;
6342
+ }
6343
+ const at2 = strokePositionPoint(g.line, subpath, t);
6344
+ if (!at2) return;
6345
+ const base = { ...this.doc, nodes: mapTree(this.doc.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
6346
+ this.doc = base;
6347
+ this.widthPoint = { nodeId: node.id, index: ref.index, subpath };
6348
+ this.drag = {
6349
+ kind: "width",
6350
+ ref,
6351
+ mode,
6352
+ side,
6353
+ origin: { x: at2.point[0], y: at2.point[1] },
6354
+ normal: { x: at2.tangent[1], y: -at2.tangent[0] },
6355
+ unit: g.unit,
6356
+ profile,
6357
+ before,
6358
+ base,
6359
+ start: p,
6360
+ moved: false,
6361
+ created: !hit
6362
+ };
6363
+ const w = profile[ref.index];
6364
+ this.interaction = { type: "width", ref, mode, width: (w.left + w.right) * g.unit };
6365
+ this.notify(!hit);
6366
+ }
5060
6367
  /** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
5061
6368
  nearestOutline(p) {
5062
6369
  let best = null;
@@ -5104,7 +6411,12 @@ var VectorEditorController = (_class3 = class {
5104
6411
  case "pen":
5105
6412
  return this.downPen(screen, p, input);
5106
6413
  case "pencil":
5107
- return this.downPencil(p);
6414
+ return this.downPencil(p, input);
6415
+ case "width":
6416
+ return this.downWidth(screen, p, input);
6417
+ case "shapeBuilder":
6418
+ case "paintBucket":
6419
+ return this.downBuilder(p, input);
5108
6420
  case "scissors": {
5109
6421
  const seg = this.nearestOutline(p);
5110
6422
  if (seg) this.cutPathAt(seg.nodeId, seg.point);
@@ -5240,7 +6552,7 @@ var VectorEditorController = (_class3 = class {
5240
6552
  }
5241
6553
  downPen(screen, p, input) {
5242
6554
  const before = this.snapshot();
5243
- const sp = this.pen ? _optionalChain([this, 'access', _109 => _109.getNode, 'call', _110 => _110(this.pen.nodeId), 'optionalAccess', _111 => _111.subpaths, 'access', _112 => _112[this.pen.subpath]]) : void 0;
6555
+ const sp = this.pen ? _optionalChain([this, 'access', _179 => _179.getNode, 'call', _180 => _180(this.pen.nodeId), 'optionalAccess', _181 => _181.subpaths, 'access', _182 => _182[this.pen.subpath]]) : void 0;
5244
6556
  if (this.pen && sp) {
5245
6557
  const pen = this.pen;
5246
6558
  const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
@@ -5297,7 +6609,7 @@ var VectorEditorController = (_class3 = class {
5297
6609
  this.interaction = { type: "pen", nodeId: node.id };
5298
6610
  this.notify(true);
5299
6611
  }
5300
- downPencil(p) {
6612
+ downPencil(p, input = {}) {
5301
6613
  const before = this.snapshot();
5302
6614
  const node = {
5303
6615
  id: this.newId(),
@@ -5311,7 +6623,11 @@ var VectorEditorController = (_class3 = class {
5311
6623
  this.setDoc([...this.doc.nodes, node]);
5312
6624
  const lazy = new LazyMouse(this.editing.pencilSmoothing * VECTOR_PENCIL_STRING_PX / this.viewport.zoom);
5313
6625
  lazy.push([p.x, p.y]);
5314
- this.drag = { kind: "pencil", nodeId: node.id, points: [[p.x, p.y]], before, lazy };
6626
+ const mode = this.editing.pencilPressure;
6627
+ const hasPressure = typeof input.pressure === "number" && Number.isFinite(input.pressure);
6628
+ const record = hasPressure && (mode === "all" || mode === "pen" && input.pointerType === "pen");
6629
+ const pressure = hasPressure ? input.pressure : 0.5;
6630
+ this.drag = { kind: "pencil", nodeId: node.id, points: [[p.x, p.y]], before, lazy, pressures: record ? [pressure] : null, pressure };
5315
6631
  this.interaction = { type: "draw", tool: "pencil", nodeId: node.id, bounds: { x: p.x, y: p.y, width: 0, height: 0 } };
5316
6632
  this.notify(true);
5317
6633
  }
@@ -5330,6 +6646,11 @@ var VectorEditorController = (_class3 = class {
5330
6646
  pointerMove(screen, input = {}) {
5331
6647
  const p = this.screenToDocument(screen);
5332
6648
  const drag = this.drag;
6649
+ if (!drag && (this.tool === "shapeBuilder" || this.tool === "paintBucket")) {
6650
+ this.pointer = p;
6651
+ if (this.hoverBuilder(p, !!input.alt)) this.notify();
6652
+ return;
6653
+ }
5333
6654
  if (!drag) {
5334
6655
  const hover = this.computeHover(screen);
5335
6656
  const pointerMatters = this.tool === "pen" && !!this.pen;
@@ -5461,6 +6782,8 @@ var VectorEditorController = (_class3 = class {
5461
6782
  this.notify(true);
5462
6783
  return;
5463
6784
  }
6785
+ case "builder":
6786
+ return this.moveBuilder(drag, p);
5464
6787
  case "stroke": {
5465
6788
  const last = drag.points[drag.points.length - 1];
5466
6789
  if (Math.hypot(p.x - last.x, p.y - last.y) * this.viewport.zoom < 1.5) return;
@@ -5491,7 +6814,7 @@ var VectorEditorController = (_class3 = class {
5491
6814
  }
5492
6815
  case "shape": {
5493
6816
  const node = this.getNode(drag.nodeId);
5494
- if (_optionalChain([node, 'optionalAccess', _113 => _113.type]) !== "path") return;
6817
+ if (_optionalChain([node, 'optionalAccess', _183 => _183.type]) !== "path") return;
5495
6818
  const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
5496
6819
  let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
5497
6820
  if (!this.snappedToObject && drag.tool !== "line" && !input.shift && !input.mod && this.snap.objects && drag.targets) end = this.matchShapeSize(drag.targets, drag.start, end, input.alt);
@@ -5520,19 +6843,43 @@ var VectorEditorController = (_class3 = class {
5520
6843
  this.notify(true);
5521
6844
  return;
5522
6845
  }
6846
+ case "width": {
6847
+ if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 2) return;
6848
+ drag.moved = true;
6849
+ const node = findVectorNode(drag.base.nodes, drag.ref.nodeId);
6850
+ const local = this.toLocal(node.id, p, drag.base);
6851
+ let profile;
6852
+ if (drag.mode === "move") {
6853
+ const g = vectorStrokeWidthGeometry(node);
6854
+ const near2 = g ? nearestStrokePosition(g.line, [local.x, local.y], drag.ref.subpath) : null;
6855
+ if (!near2) return;
6856
+ profile = setWidthPoint(drag.profile, drag.ref.index, { t: near2.t });
6857
+ } else {
6858
+ const off = ((local.x - drag.origin.x) * drag.normal.x + (local.y - drag.origin.y) * drag.normal.y) / drag.unit;
6859
+ const patch = drag.side === "both" ? { left: Math.abs(off), right: Math.abs(off) } : drag.side === "left" ? { left: Math.max(0, off) } : { right: Math.max(0, -off) };
6860
+ profile = setWidthPoint(drag.profile, drag.ref.index, patch);
6861
+ }
6862
+ this.doc = { ...drag.base, nodes: mapTree(drag.base.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
6863
+ const w = profile[drag.ref.index];
6864
+ this.interaction = { type: "width", ref: drag.ref, mode: drag.mode, width: (w.left + w.right) * drag.unit };
6865
+ this.notify(true);
6866
+ return;
6867
+ }
5523
6868
  case "pencil": {
5524
6869
  const last = drag.points[drag.points.length - 1];
5525
6870
  let moved = false;
6871
+ if (typeof input.pressure === "number" && Number.isFinite(input.pressure)) drag.pressure = input.pressure;
5526
6872
  for (const q of drag.lazy.push([p.x, p.y])) {
5527
6873
  if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
5528
6874
  drag.points.push(q);
6875
+ _optionalChain([drag, 'access', _184 => _184.pressures, 'optionalAccess', _185 => _185.push, 'call', _186 => _186(drag.pressure)]);
5529
6876
  moved = true;
5530
6877
  }
5531
6878
  const string = drag.lazy.string();
5532
6879
  const node = this.getNode(drag.nodeId);
5533
- if (_optionalChain([node, 'optionalAccess', _114 => _114.type]) !== "path") return;
6880
+ if (_optionalChain([node, 'optionalAccess', _187 => _187.type]) !== "path") return;
5534
6881
  if (!moved) {
5535
- if (string && _optionalChain([this, 'access', _115 => _115.interaction, 'optionalAccess', _116 => _116.type]) === "draw") {
6882
+ if (string && _optionalChain([this, 'access', _188 => _188.interaction, 'optionalAccess', _189 => _189.type]) === "draw") {
5536
6883
  this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
5537
6884
  this.notify();
5538
6885
  }
@@ -5568,8 +6915,8 @@ var VectorEditorController = (_class3 = class {
5568
6915
  }
5569
6916
  this.setGuides(res);
5570
6917
  return {
5571
- dx: _optionalChain([res, 'optionalAccess', _117 => _117.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
5572
- dy: _optionalChain([res, 'optionalAccess', _118 => _118.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
6918
+ dx: _optionalChain([res, 'optionalAccess', _190 => _190.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
6919
+ dy: _optionalChain([res, 'optionalAccess', _191 => _191.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
5573
6920
  };
5574
6921
  }
5575
6922
  /** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
@@ -5606,7 +6953,7 @@ var VectorEditorController = (_class3 = class {
5606
6953
  }
5607
6954
  /** Primary button released. */
5608
6955
  pointerUp(screen, input = {}) {
5609
- if (screen && this.drag && this.drag.kind !== "pan") this.pointerMove(screen, input);
6956
+ if (screen && this.drag && this.drag.kind !== "pan") this.pointerMove(screen, { ...input, pressure: void 0 });
5610
6957
  const drag = this.drag;
5611
6958
  this.drag = null;
5612
6959
  this.interaction = null;
@@ -5648,7 +6995,7 @@ var VectorEditorController = (_class3 = class {
5648
6995
  if (drag.moved) return this.commit("Corner Radius", drag.before);
5649
6996
  if (drag.alt) {
5650
6997
  const kind = nextVectorCornerKind(drag.widget.kind);
5651
- this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _119 => _119.corner, 'optionalAccess', _120 => _120.radius]), () => ( 0)), kind));
6998
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _192 => _192.corner, 'optionalAccess', _193 => _193.radius]), () => ( 0)), kind));
5652
6999
  return this.commit("Corner Kind", drag.before);
5653
7000
  }
5654
7001
  const ref = drag.widget.ref;
@@ -5658,6 +7005,17 @@ var VectorEditorController = (_class3 = class {
5658
7005
  this.notify();
5659
7006
  return;
5660
7007
  }
7008
+ case "width": {
7009
+ if (drag.moved) return this.commit(drag.mode === "move" ? "Move Width Point" : "Width Point", drag.before);
7010
+ if (drag.created) {
7011
+ this.doc = drag.before.doc;
7012
+ this.widthPoint = null;
7013
+ this.notify(true);
7014
+ } else this.notify();
7015
+ return;
7016
+ }
7017
+ case "builder":
7018
+ return this.upBuilder(drag);
5661
7019
  case "stroke": {
5662
7020
  this.notify();
5663
7021
  if (drag.tool === "knife") this.knife(drag.points);
@@ -5683,19 +7041,22 @@ var VectorEditorController = (_class3 = class {
5683
7041
  }
5684
7042
  case "pencil": {
5685
7043
  const node = this.getNode(drag.nodeId);
5686
- if (_optionalChain([node, 'optionalAccess', _121 => _121.type]) !== "path" || drag.points.length < 2) {
7044
+ if (_optionalChain([node, 'optionalAccess', _194 => _194.type]) !== "path" || drag.points.length < 2) {
5687
7045
  this.doc = drag.before.doc;
5688
7046
  this.notify(true);
5689
7047
  return;
5690
7048
  }
5691
- for (const q of drag.lazy.finish()) drag.points.push(q);
7049
+ for (const q of drag.lazy.finish()) {
7050
+ drag.points.push(q);
7051
+ _optionalChain([drag, 'access', _195 => _195.pressures, 'optionalAccess', _196 => _196.push, 'call', _197 => _197(drag.pressure)]);
7052
+ }
5692
7053
  const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
5693
7054
  if (shape) {
5694
7055
  this.replaceNode(drag.nodeId, { ...node, name: inkShapeLabel(shape), subpaths: [inkShapeSubpath(shape)] });
5695
7056
  this.selection = [drag.nodeId];
5696
7057
  return this.commit(`Draw ${inkShapeLabel(shape)}`, drag.before);
5697
7058
  }
5698
- const fitted = _chunkBGNPT5PEcjs.fitCubics.call(void 0, drag.points, 1.5 / this.viewport.zoom, { cornerAngleDeg: 60 });
7059
+ const fitted = _chunk7EDLVBOOcjs.fitCubics.call(void 0, drag.points, 1.5 / this.viewport.zoom, { cornerAngleDeg: 60 });
5699
7060
  const anchors = [];
5700
7061
  for (const c of fitted) {
5701
7062
  if (!anchors.length) anchors.push({ x: c.p0[0], y: c.p0[1] });
@@ -5703,7 +7064,8 @@ var VectorEditorController = (_class3 = class {
5703
7064
  anchors.push({ x: c.p3[0], y: c.p3[1], in: { x: c.p2[0], y: c.p2[1] } });
5704
7065
  }
5705
7066
  const subpaths = anchors.length > 1 ? bezierToSubpaths(subpathsToBezier([{ anchors, closed: false }])) : node.subpaths;
5706
- this.replaceNode(drag.nodeId, { ...node, subpaths });
7067
+ const profile = drag.pressures ? pencilPressureProfile(drag.points, drag.pressures) : [];
7068
+ this.replaceNode(drag.nodeId, { ...node, subpaths, ...profile.length ? { strokeWidthProfile: profile } : {} });
5707
7069
  this.selection = [drag.nodeId];
5708
7070
  return this.commit("Draw", drag.before);
5709
7071
  }
@@ -5725,7 +7087,8 @@ var VectorEditorController = (_class3 = class {
5725
7087
  this.drag = null;
5726
7088
  this.interaction = null;
5727
7089
  this.guides = null;
5728
- if (drag.kind === "pan") {
7090
+ if (drag.kind === "pan" || drag.kind === "builder") {
7091
+ if (drag.kind === "builder") this.builderPreview = null;
5729
7092
  this.notify();
5730
7093
  return;
5731
7094
  }
@@ -5757,7 +7120,7 @@ var VectorEditorController = (_class3 = class {
5757
7120
  this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
5758
7121
  return;
5759
7122
  }
5760
- const id = _nullishCoalesce(_optionalChain([this, 'access', _122 => _122.hover, 'optionalAccess', _123 => _123.segment, 'optionalAccess', _124 => _124.nodeId]), () => ( this.editingId));
7123
+ const id = _nullishCoalesce(_optionalChain([this, 'access', _198 => _198.hover, 'optionalAccess', _199 => _199.segment, 'optionalAccess', _200 => _200.nodeId]), () => ( this.editingId));
5761
7124
  if (id) {
5762
7125
  const node = this.getNode(id);
5763
7126
  const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
@@ -5825,7 +7188,10 @@ var VectorEditorController = (_class3 = class {
5825
7188
  switch (key) {
5826
7189
  case "Escape":
5827
7190
  if (this.drag) this.cancelDrag();
5828
- else if (this.pen) this.finishPath();
7191
+ else if (this.tool === "width" && this.widthPoint) {
7192
+ this.widthPoint = null;
7193
+ this.notify();
7194
+ } else if (this.pen) this.finishPath();
5829
7195
  else if (this.tool === "direct" && this.anchors.length) {
5830
7196
  this.anchors = [];
5831
7197
  this.notify();
@@ -5837,7 +7203,7 @@ var VectorEditorController = (_class3 = class {
5837
7203
  this.finishPath();
5838
7204
  return true;
5839
7205
  }
5840
- if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _125 => _125.getNode, 'call', _126 => _126(this.selection[0]), 'optionalAccess', _127 => _127.type]) === "path") {
7206
+ if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _201 => _201.getNode, 'call', _202 => _202(this.selection[0]), 'optionalAccess', _203 => _203.type]) === "path") {
5841
7207
  this.tool = "direct";
5842
7208
  this.editingId = this.selection[0];
5843
7209
  this.notify();
@@ -5846,6 +7212,7 @@ var VectorEditorController = (_class3 = class {
5846
7212
  return false;
5847
7213
  case "Delete":
5848
7214
  case "Backspace":
7215
+ if (this.tool === "width" && this.widthPoint) return this.removeWidthPoint(this.widthPoint).ok;
5849
7216
  if (this.pen) {
5850
7217
  const node = this.getNode(this.pen.nodeId);
5851
7218
  const sp = node.subpaths[this.pen.subpath];
@@ -5927,6 +7294,31 @@ function scaleFor(handle, b, p, mods = {}) {
5927
7294
  }
5928
7295
  return { sx, sy, origin };
5929
7296
  }
7297
+ function withStyle(node, style) {
7298
+ const next = { ...node, ...style };
7299
+ if ("strokeWidthProfile" in style) {
7300
+ const p = style.strokeWidthProfile ? sanitizeWidthProfile(style.strokeWidthProfile) : [];
7301
+ if (p.length) next.strokeWidthProfile = p;
7302
+ else delete next.strokeWidthProfile;
7303
+ }
7304
+ return next;
7305
+ }
7306
+ function hiddenOrLocked(nodes, id) {
7307
+ const loc = locateVectorNode(nodes, id);
7308
+ if (!loc) return false;
7309
+ const n = loc.list[loc.index];
7310
+ if (n.locked || n.visible === false) return true;
7311
+ return loc.parent ? hiddenOrLocked(nodes, loc.parent.id) : false;
7312
+ }
7313
+ function pencilPressureProfile(points, pressures) {
7314
+ const n = Math.min(points.length, pressures.length);
7315
+ if (n < 2) return [];
7316
+ const s = [0];
7317
+ for (let i = 1; i < n; i++) s.push(s[i - 1] + Math.hypot(points[i][0] - points[i - 1][0], points[i][1] - points[i - 1][1]));
7318
+ const total = s[n - 1];
7319
+ if (!(total > 0)) return [];
7320
+ return pressureWidthProfile(s.map((d, i) => ({ t: d / total, pressure: pressures[i] })));
7321
+ }
5930
7322
 
5931
7323
  // src/vector/render.ts
5932
7324
  var fmt = (n) => Math.round(n * 100) / 100;
@@ -5953,7 +7345,7 @@ function renderNode(node, defs, idPrefix) {
5953
7345
  strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
5954
7346
  ...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
5955
7347
  ...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
5956
- ..._optionalChain([node, 'access', _128 => _128.strokeDasharray, 'optionalAccess', _129 => _129.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
7348
+ ..._optionalChain([node, 'access', _204 => _204.strokeDasharray, 'optionalAccess', _205 => _205.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
5957
7349
  fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
5958
7350
  opacity: _nullishCoalesce(node.opacity, () => ( 1))
5959
7351
  };
@@ -5977,7 +7369,7 @@ function screenOutline(state, node) {
5977
7369
  const v2 = state.viewport;
5978
7370
  const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
5979
7371
  const parent = _nullishCoalesce(vectorParentMatrix(state.document.nodes, node.id), () => ( IDENTITY_MATRIX));
5980
- return collectWithMatrix(node, parent).map(({ path, parent: m }) => _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, vectorPathWorldBezier(path, m), view), { precision: 2 })).join(" ");
7372
+ return collectWithMatrix(node, parent).map(({ path, parent: m }) => _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, vectorPathWorldBezier(path, m), view), { precision: 2 })).join(" ");
5981
7373
  }
5982
7374
  function collectWithMatrix(node, parent) {
5983
7375
  if (node.type === "path") return [{ path: node, parent }];
@@ -6002,9 +7394,9 @@ function getVectorOverlayRenderData(state) {
6002
7394
  };
6003
7395
  for (const id of state.selection) outline(id, true);
6004
7396
  if (state.editingId) outline(state.editingId, true);
6005
- if (_optionalChain([state, 'access', _130 => _130.hover, 'optionalAccess', _131 => _131.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
7397
+ if (_optionalChain([state, 'access', _206 => _206.hover, 'optionalAccess', _207 => _207.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
6006
7398
  let selectionBox = null;
6007
- if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _132 => _132.interaction, 'optionalAccess', _133 => _133.type]) !== "marquee") {
7399
+ if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _208 => _208.interaction, 'optionalAccess', _209 => _209.type]) !== "marquee") {
6008
7400
  const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
6009
7401
  selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
6010
7402
  }
@@ -6014,9 +7406,9 @@ function getVectorOverlayRenderData(state) {
6014
7406
  const a = getAnchorWorld(doc, ref);
6015
7407
  const s = toScreen(state, a);
6016
7408
  const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
6017
- return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _134 => _134.hover, 'optionalAccess', _135 => _135.anchor]), ref), closes };
7409
+ return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _210 => _210.hover, 'optionalAccess', _211 => _211.anchor]), ref), closes };
6018
7410
  });
6019
- if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _136 => _136.hover, 'optionalAccess', _137 => _137.anchor])) {
7411
+ if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _212 => _212.hover, 'optionalAccess', _213 => _213.anchor])) {
6020
7412
  const a = getAnchorWorld(doc, state.hover.anchor);
6021
7413
  if (a) {
6022
7414
  const s = toScreen(state, a);
@@ -6025,15 +7417,15 @@ function getVectorOverlayRenderData(state) {
6025
7417
  }
6026
7418
  const handles = getVisibleHandles(state).flatMap((ref) => {
6027
7419
  const a = getAnchorWorld(doc, ref);
6028
- const h = _optionalChain([a, 'optionalAccess', _138 => _138[ref.handle]]);
7420
+ const h = _optionalChain([a, 'optionalAccess', _214 => _214[ref.handle]]);
6029
7421
  if (!a || !h) return [];
6030
7422
  const s = toScreen(state, h);
6031
7423
  const sa = toScreen(state, a);
6032
- const hovered = !!_optionalChain([state, 'access', _139 => _139.hover, 'optionalAccess', _140 => _140.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
7424
+ const hovered = !!_optionalChain([state, 'access', _215 => _215.hover, 'optionalAccess', _216 => _216.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
6033
7425
  return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
6034
7426
  });
6035
7427
  let marquee = null;
6036
- if (_optionalChain([state, 'access', _141 => _141.interaction, 'optionalAccess', _142 => _142.type]) === "marquee") {
7428
+ if (_optionalChain([state, 'access', _217 => _217.interaction, 'optionalAccess', _218 => _218.type]) === "marquee") {
6037
7429
  const b = state.interaction.bounds;
6038
7430
  const tl = toScreen(state, b);
6039
7431
  marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
@@ -6041,7 +7433,7 @@ function getVectorOverlayRenderData(state) {
6041
7433
  let penPreview = null;
6042
7434
  if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
6043
7435
  const node = findVectorNode(doc.nodes, state.pen.nodeId);
6044
- const sp = _optionalChain([node, 'optionalAccess', _143 => _143.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
7436
+ const sp = _optionalChain([node, 'optionalAccess', _219 => _219.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
6045
7437
  const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
6046
7438
  if (last) {
6047
7439
  const a = toScreen(state, last);
@@ -6054,9 +7446,9 @@ function getVectorOverlayRenderData(state) {
6054
7446
  const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
6055
7447
  let status = null;
6056
7448
  const it = state.interaction;
6057
- if (_optionalChain([it, 'optionalAccess', _144 => _144.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
6058
- else if (_optionalChain([it, 'optionalAccess', _145 => _145.type]) === "move" || _optionalChain([it, 'optionalAccess', _146 => _146.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
6059
- else if (_optionalChain([it, 'optionalAccess', _147 => _147.type]) === "scale" || _optionalChain([it, 'optionalAccess', _148 => _148.type]) === "draw") {
7449
+ if (_optionalChain([it, 'optionalAccess', _220 => _220.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
7450
+ else if (_optionalChain([it, 'optionalAccess', _221 => _221.type]) === "move" || _optionalChain([it, 'optionalAccess', _222 => _222.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
7451
+ else if (_optionalChain([it, 'optionalAccess', _223 => _223.type]) === "scale" || _optionalChain([it, 'optionalAccess', _224 => _224.type]) === "draw") {
6060
7452
  const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
6061
7453
  if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
6062
7454
  }
@@ -6073,25 +7465,51 @@ function getVectorOverlayRenderData(state) {
6073
7465
  }
6074
7466
  }
6075
7467
  let toolStroke = null;
6076
- if (_optionalChain([it, 'optionalAccess', _149 => _149.type]) === "stroke") {
7468
+ if (_optionalChain([it, 'optionalAccess', _225 => _225.type]) === "stroke") {
6077
7469
  const pts = it.points.map((q) => toScreen(state, q));
6078
7470
  const d = pts.map((q, i) => `${i ? "L" : "M"}${fmt(q.x)} ${fmt(q.y)}`).join("") + (pts.length === 1 ? `L${fmt(pts[0].x)} ${fmt(pts[0].y)}` : "");
6079
7471
  toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
6080
7472
  }
6081
7473
  let pencilString = null;
6082
- if (_optionalChain([it, 'optionalAccess', _150 => _150.type]) === "draw" && it.string) {
7474
+ if (_optionalChain([it, 'optionalAccess', _226 => _226.type]) === "draw" && it.string) {
6083
7475
  const a = toScreen(state, it.string[0]);
6084
7476
  const b = toScreen(state, it.string[1]);
6085
7477
  pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
6086
7478
  }
6087
- const dragged = _optionalChain([it, 'optionalAccess', _151 => _151.type]) === "corner" ? it.ref : null;
7479
+ const dragged = _optionalChain([it, 'optionalAccess', _227 => _227.type]) === "corner" ? it.ref : null;
6088
7480
  const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
6089
7481
  const s = toScreen(state, w);
6090
- return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _152 => _152.hover, 'optionalAccess', _153 => _153.corner]), w.ref), active: !!dragged };
7482
+ return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _228 => _228.hover, 'optionalAccess', _229 => _229.corner]), w.ref), active: !!dragged };
7483
+ });
7484
+ if (_optionalChain([it, 'optionalAccess', _230 => _230.type]) === "corner") status = `R ${fmt(it.radius)}`;
7485
+ const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _231 => _231.hover, 'optionalAccess', _232 => _232.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
7486
+ const sc = (p) => {
7487
+ const q = toScreen(state, p);
7488
+ return { x: fmt(q.x), y: fmt(q.y) };
7489
+ };
7490
+ const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
7491
+ const widthPoints = getVectorWidthHandles(state).map((h) => {
7492
+ const c = sc(h);
7493
+ const hw = _optionalChain([state, 'access', _233 => _233.hover, 'optionalAccess', _234 => _234.widthPoint]);
7494
+ const hovered = hw && sameWidth(hw, h.ref) && hw.subpath === h.ref.subpath ? hw.part : null;
7495
+ return { ref: h.ref, x: c.x, y: c.y, left: sc(h.left), right: sc(h.right), selected: h.selected, hovered, active: _optionalChain([it, 'optionalAccess', _235 => _235.type]) === "width" && sameWidth(it.ref, h.ref) };
6091
7496
  });
6092
- if (_optionalChain([it, 'optionalAccess', _154 => _154.type]) === "corner") status = `R ${fmt(it.radius)}`;
6093
- const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _155 => _155.hover, 'optionalAccess', _156 => _156.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
6094
- return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets };
7497
+ const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _236 => _236.hover, 'optionalAccess', _237 => _237.strokeSpot]) : null;
7498
+ const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
7499
+ if (_optionalChain([it, 'optionalAccess', _238 => _238.type]) === "width") status = it.mode === "move" ? "Move width point" : `W ${fmt(it.width)}`;
7500
+ let builder = null;
7501
+ if (state.builder) {
7502
+ const v2 = state.viewport;
7503
+ const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
7504
+ const d = (path) => _chunk7EDLVBOOcjs.bezierPathToSvg.call(void 0, _chunk7EDLVBOOcjs.transformBezierPath.call(void 0, path, view), { precision: 2 });
7505
+ const trail = state.builder.trail && state.builder.trail.length > 1 ? state.builder.trail.map((q, i) => `${i ? "L" : "M"}${sc(q).x} ${sc(q).y}`).join("") : null;
7506
+ builder = { mode: state.builder.mode, faces: state.builder.faces.map(d), lines: state.builder.lines.map(d), trail };
7507
+ if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _239 => _239.type]) === "builder") {
7508
+ const n = state.builder.faces.length;
7509
+ status = state.builder.mode === "erase" ? `Delete ${n} ${n === 1 ? "face" : "faces"}` : `Merge ${n} ${n === 1 ? "face" : "faces"}`;
7510
+ }
7511
+ }
7512
+ return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot, builder };
6095
7513
  }
6096
7514
  var VECTOR_OVERLAY_THEME = {
6097
7515
  accent: "#0ea5e9",
@@ -6105,13 +7523,28 @@ var VECTOR_OVERLAY_THEME = {
6105
7523
  anchorSize: 7,
6106
7524
  handleRadius: 3.5,
6107
7525
  /** Radius of a live corner widget (a ring with a dot). */
6108
- cornerWidgetRadius: 4
7526
+ cornerWidgetRadius: 4,
7527
+ /** Half the diagonal of a width point's diamond. */
7528
+ widthPointSize: 5,
7529
+ /** Shape Builder faces to merge and paint bucket regions. */
7530
+ builderFill: "rgba(14, 165, 233, 0.22)",
7531
+ /** Shape Builder faces and line pieces an Alt gesture deletes. */
7532
+ builderErase: "#ef4444",
7533
+ builderEraseFill: "rgba(239, 68, 68, 0.22)"
6109
7534
  };
7535
+ function vectorDiamondPath(x, y, r = VECTOR_OVERLAY_THEME.widthPointSize) {
7536
+ return `M${fmt(x)} ${fmt(y - r)}L${fmt(x + r)} ${fmt(y)}L${fmt(x)} ${fmt(y + r)}L${fmt(x - r)} ${fmt(y)}Z`;
7537
+ }
6110
7538
  function toVectorModifiers(event) {
6111
7539
  return { shift: !!event.shiftKey, alt: !!event.altKey, mod: !!(event.metaKey || event.ctrlKey) };
6112
7540
  }
6113
7541
  function toVectorPointerInput(event) {
6114
- return { ...toVectorModifiers(event), button: event.button };
7542
+ return {
7543
+ ...toVectorModifiers(event),
7544
+ button: event.button,
7545
+ ...typeof event.pressure === "number" ? { pressure: event.pressure } : {},
7546
+ ...event.pointerType ? { pointerType: event.pointerType } : {}
7547
+ };
6115
7548
  }
6116
7549
  function toVectorKeyInput(event) {
6117
7550
  return { key: event.key, ...toVectorModifiers(event) };
@@ -6121,7 +7554,7 @@ function isVectorTextTarget(target) {
6121
7554
  if (!el || typeof el !== "object") return false;
6122
7555
  const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
6123
7556
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
6124
- return !!el.isContentEditable || _optionalChain([el, 'access', _157 => _157.getAttribute, 'optionalCall', _158 => _158("contenteditable")]) === "true";
7557
+ return !!el.isContentEditable || _optionalChain([el, 'access', _240 => _240.getAttribute, 'optionalCall', _241 => _241("contenteditable")]) === "true";
6125
7558
  }
6126
7559
  function getVectorSelectionStyle(state) {
6127
7560
  const paths = [];
@@ -6129,12 +7562,12 @@ function getVectorSelectionStyle(state) {
6129
7562
  const node = findVectorNode(state.document.nodes, id);
6130
7563
  if (node) paths.push(...collect(node));
6131
7564
  }
6132
- const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _159 => _159.selectedAnchors, 'optionalAccess', _160 => _160.length]);
7565
+ const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _242 => _242.selectedAnchors, 'optionalAccess', _243 => _243.length]);
6133
7566
  if (anchorMode) {
6134
7567
  for (const r of state.selectedAnchors) {
6135
7568
  if (paths.some((p) => p.id === r.nodeId)) continue;
6136
7569
  const node = findVectorNode(state.document.nodes, r.nodeId);
6137
- if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) === "path") paths.push(node);
7570
+ if (_optionalChain([node, 'optionalAccess', _244 => _244.type]) === "path") paths.push(node);
6138
7571
  }
6139
7572
  }
6140
7573
  if (!paths.length) return null;
@@ -6145,7 +7578,7 @@ function getVectorSelectionStyle(state) {
6145
7578
  for (const c of vectorCorners(p.subpaths)) {
6146
7579
  if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
6147
7580
  const corner = p.subpaths[c.subpath].anchors[c.index].corner;
6148
- corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _162 => _162.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _163 => _163.kind]), () => ( "round")) });
7581
+ corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _245 => _245.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _246 => _246.kind]), () => ( "round")) });
6149
7582
  }
6150
7583
  }
6151
7584
  const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
@@ -6160,6 +7593,8 @@ function getVectorSelectionStyle(state) {
6160
7593
  arrowAlign: _nullishCoalesce(first.arrowAlign, () => ( "extend")),
6161
7594
  startArrowScale: _nullishCoalesce(first.startArrowScale, () => ( 1)),
6162
7595
  endArrowScale: _nullishCoalesce(first.endArrowScale, () => ( 1)),
7596
+ strokeWidthProfile: profileOf(first),
7597
+ widthProfile: widthProfilePresetId(profileOf(first)),
6163
7598
  cornerRadius: c0.radius,
6164
7599
  cornerKind: c0.kind,
6165
7600
  corners: corners2.length,
@@ -6175,12 +7610,17 @@ function getVectorSelectionStyle(state) {
6175
7610
  arrowAlign: !same((p) => _nullishCoalesce(p.arrowAlign, () => ( "extend"))),
6176
7611
  startArrowScale: !same((p) => _nullishCoalesce(p.startArrowScale, () => ( 1))),
6177
7612
  endArrowScale: !same((p) => _nullishCoalesce(p.endArrowScale, () => ( 1))),
7613
+ widthProfile: !same(profileOf),
6178
7614
  cornerRadius: corners2.some((c) => c.radius !== c0.radius),
6179
7615
  cornerKind: corners2.some((c) => c.kind !== c0.kind)
6180
7616
  },
6181
7617
  pathIds: paths.map((p) => p.id)
6182
7618
  };
6183
7619
  }
7620
+ function profileOf(p) {
7621
+ const points = sanitizeWidthProfile(p.strokeWidthProfile);
7622
+ return points.length && widthProfilePresetId(points) !== "uniform" ? points : null;
7623
+ }
6184
7624
  var VECTOR_STROKE_ALIGN_LABELS = { center: "Center", inside: "Inside", outside: "Outside" };
6185
7625
  var VECTOR_CORNER_KIND_LABELS = { round: "Round", inverted: "Inverted", chamfer: "Chamfer" };
6186
7626
  var VECTOR_ARROWHEAD_LABELS = {
@@ -6206,6 +7646,14 @@ var VECTOR_PATH_ACTIONS = [
6206
7646
  { id: "smooth", label: "\u223F", title: "Smooth", enabled: (s, p) => s + p > 0, run: (c) => c.smoothPaths() },
6207
7647
  { id: "outline", label: "\u22A1", title: "Outline Stroke", enabled: (s) => s > 0, run: (c) => c.outlineStroke() }
6208
7648
  ];
7649
+ var VECTOR_PATHFINDER_ACTIONS = [
7650
+ { id: "divide", label: "\u229E", title: "Divide", min: 1 },
7651
+ { id: "trim", label: "\u29C9", title: "Trim", min: 2 },
7652
+ { id: "merge", label: "\u29C8", title: "Merge", min: 2 },
7653
+ { id: "crop", label: "\u2317", title: "Crop", min: 2 },
7654
+ { id: "outline", label: "\u2B1A", title: "Outline", min: 1 },
7655
+ { id: "minusBack", label: "\u2296", title: "Minus Back", min: 2 }
7656
+ ];
6209
7657
  var VECTOR_EDIT_TOGGLES = [
6210
7658
  { key: "keepShapeOnDelete", label: "Keep", title: "Keep shape when deleting points" },
6211
7659
  { key: "curvatureMoves", label: "Curv", title: "Curvature point moves (Alt flips while dragging)" },
@@ -6232,6 +7680,19 @@ var POINTS = {
6232
7680
  minItems: 1,
6233
7681
  description: "Polyline [[x, y], \u2026] in document units."
6234
7682
  };
7683
+ var WIDTH_POINTS = {
7684
+ type: ["array", "null"],
7685
+ items: {
7686
+ type: "object",
7687
+ properties: {
7688
+ t: { type: "number", minimum: 0, maximum: 1, description: "Position along each subpath by length (0 start, 1 end)." },
7689
+ left: { type: "number", minimum: 0, description: "Left side as a multiple of half the stroke width (1 = uniform)." },
7690
+ right: { type: "number", minimum: 0, description: "Right side (looking along the path, y down)." }
7691
+ },
7692
+ required: ["t", "left", "right"]
7693
+ },
7694
+ description: "Variable-width profile points, interpolated smoothly (monotone cubic); two points at one `t` make a step. `null` or `[]` strokes at a uniform width."
7695
+ };
6235
7696
  var PAINT = {
6236
7697
  description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear", angle, stops: [{ offset, color }] }`.'
6237
7698
  };
@@ -6248,7 +7709,8 @@ var STYLE_PROPS = {
6248
7709
  endArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the end of an open path, or null for none." },
6249
7710
  arrowAlign: { type: "string", enum: ["extend", "tip"], description: "`extend`: the tip sits past the end point; `tip`: on it, the stroke shortened under the head." },
6250
7711
  startArrowScale: { type: "number", exclusiveMinimum: 0, description: "Start head size as a multiple of the stroke width (default 1)." },
6251
- endArrowScale: { type: "number", exclusiveMinimum: 0, description: "End head size as a multiple of the stroke width (default 1)." }
7712
+ endArrowScale: { type: "number", exclusiveMinimum: 0, description: "End head size as a multiple of the stroke width (default 1)." },
7713
+ strokeWidthProfile: WIDTH_POINTS
6252
7714
  };
6253
7715
  var pickStyle = (params) => {
6254
7716
  const out = {};
@@ -6264,17 +7726,17 @@ function createVectorCommands(controller, options = {}) {
6264
7726
  const base = { group, ...options.scope ? { scope: options.scope } : {} };
6265
7727
  const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
6266
7728
  const ids = (params) => {
6267
- const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _164 => _164.ids]), () => ( controller.getState().selection));
7729
+ const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _247 => _247.ids]), () => ( controller.getState().selection));
6268
7730
  for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
6269
7731
  if (!list2.length) fail("Nothing is selected; pass ids.");
6270
7732
  return list2;
6271
7733
  };
6272
- const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _165 => _165.ids, 'optionalAccess', _166 => _166.length]) ? true : hasSelection();
7734
+ const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _248 => _248.ids, 'optionalAccess', _249 => _249.length]) ? true : hasSelection();
6273
7735
  const anchorsOf = (refs) => {
6274
7736
  if (!refs) return [...controller.getState().selectedAnchors];
6275
7737
  for (const r of refs) {
6276
7738
  const node = controller.getNode(r.nodeId);
6277
- if (_optionalChain([node, 'optionalAccess', _167 => _167.type]) !== "path" || !_optionalChain([node, 'access', _168 => _168.subpaths, 'access', _169 => _169[r.subpath], 'optionalAccess', _170 => _170.anchors, 'access', _171 => _171[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
7739
+ if (_optionalChain([node, 'optionalAccess', _250 => _250.type]) !== "path" || !_optionalChain([node, 'access', _251 => _251.subpaths, 'access', _252 => _252[r.subpath], 'optionalAccess', _253 => _253.anchors, 'access', _254 => _254[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
6278
7740
  }
6279
7741
  return refs;
6280
7742
  };
@@ -6365,7 +7827,7 @@ function createVectorCommands(controller, options = {}) {
6365
7827
  run: (_ctx, params) => {
6366
7828
  const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
6367
7829
  const node = controller.getNode(id);
6368
- if (_optionalChain([node, 'optionalAccess', _172 => _172.type]) === "path" && !node.subpaths.length) {
7830
+ if (_optionalChain([node, 'optionalAccess', _255 => _255.type]) === "path" && !node.subpaths.length) {
6369
7831
  controller.undo();
6370
7832
  fail("The path data has no points.");
6371
7833
  }
@@ -6397,14 +7859,52 @@ function createVectorCommands(controller, options = {}) {
6397
7859
  params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
6398
7860
  undoable: true,
6399
7861
  run: (_ctx, params) => {
6400
- const refs = _optionalChain([params, 'optionalAccess', _173 => _173.anchors]) ? anchorsOf(params.anchors) : void 0;
6401
- const target = { refs, ids: _optionalChain([params, 'optionalAccess', _174 => _174.ids]) ? ids(params) : void 0 };
6402
- if (_optionalChain([params, 'optionalAccess', _175 => _175.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
6403
- else if (_optionalChain([params, 'optionalAccess', _176 => _176.kind])) check(controller.setCornerKind(params.kind, target));
7862
+ const refs = _optionalChain([params, 'optionalAccess', _256 => _256.anchors]) ? anchorsOf(params.anchors) : void 0;
7863
+ const target = { refs, ids: _optionalChain([params, 'optionalAccess', _257 => _257.ids]) ? ids(params) : void 0 };
7864
+ if (_optionalChain([params, 'optionalAccess', _258 => _258.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
7865
+ else if (_optionalChain([params, 'optionalAccess', _259 => _259.kind])) check(controller.setCornerKind(params.kind, target));
6404
7866
  else fail("Pass `radius` and / or `kind`.");
6405
7867
  return { ok: true };
6406
7868
  }
6407
7869
  },
7870
+ {
7871
+ ...base,
7872
+ id: `${prefix}setWidthProfile`,
7873
+ label: "Width Profile\u2026",
7874
+ keywords: ["variable width", "taper", "tapered", "pressure", "profile", "brush", "calligraphy", "bulge", "thick", "thin"],
7875
+ description: "Gives the stroke of paths a variable width: a `preset` (`uniform` removes the profile), explicit `points` ({ t, left, right }: side scales along each subpath), or a parametric `taper` (startLength / endLength as fractions of the path, startWidth / endWidth 0\u20131, startEase / endEase 0\u20131) and / or `wave` (amount 0\u20131, cycles, phase in degrees). Profiled strokes draw as filled outlines; Outline Stroke bakes them.",
7876
+ params: {
7877
+ type: "object",
7878
+ properties: {
7879
+ ids: IDS,
7880
+ preset: { type: "string", enum: VECTOR_WIDTH_PRESET_IDS },
7881
+ points: WIDTH_POINTS,
7882
+ taper: {
7883
+ type: "object",
7884
+ properties: {
7885
+ startLength: { type: "number", minimum: 0, maximum: 1 },
7886
+ endLength: { type: "number", minimum: 0, maximum: 1 },
7887
+ startWidth: { type: "number", minimum: 0, maximum: 1 },
7888
+ endWidth: { type: "number", minimum: 0, maximum: 1 },
7889
+ startEase: { type: "number", minimum: 0, maximum: 1 },
7890
+ endEase: { type: "number", minimum: 0, maximum: 1 }
7891
+ }
7892
+ },
7893
+ wave: { type: "object", properties: { amount: { type: "number", minimum: 0, maximum: 1 }, cycles: { type: "number", exclusiveMinimum: 0 }, phase: { type: "number" } } }
7894
+ }
7895
+ },
7896
+ enabled: enabledFor,
7897
+ undoable: true,
7898
+ run: (_ctx, params) => {
7899
+ const target = { ids: ids(params) };
7900
+ if (_optionalChain([params, 'optionalAccess', _260 => _260.preset])) check(controller.setWidthProfile(params.preset, target));
7901
+ else if (_optionalChain([params, 'optionalAccess', _261 => _261.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
7902
+ else if (_optionalChain([params, 'optionalAccess', _262 => _262.taper]) || _optionalChain([params, 'optionalAccess', _263 => _263.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
7903
+ else fail("Pass `preset`, `points`, `taper` or `wave`.");
7904
+ const node = controller.getNode(target.ids[0]);
7905
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _264 => _264.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
7906
+ }
7907
+ },
6408
7908
  {
6409
7909
  ...base,
6410
7910
  id: `${prefix}outlineStroke`,
@@ -6415,7 +7915,7 @@ function createVectorCommands(controller, options = {}) {
6415
7915
  enabled: enabledFor,
6416
7916
  undoable: true,
6417
7917
  run: (_ctx, params) => {
6418
- const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _177 => _177.ids]) ? ids(params) : void 0 });
7918
+ const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _265 => _265.ids]) ? ids(params) : void 0 });
6419
7919
  if (!created.length) fail("No selected path has a stroke.");
6420
7920
  return { ids: created };
6421
7921
  }
@@ -6477,6 +7977,62 @@ function createVectorCommands(controller, options = {}) {
6477
7977
  return { id };
6478
7978
  }
6479
7979
  },
7980
+ {
7981
+ ...base,
7982
+ id: `${prefix}pathfinder`,
7983
+ label: "Pathfinder\u2026",
7984
+ keywords: ["divide", "trim", "merge", "crop", "outline", "minus back", "split", "faces"],
7985
+ description: "Pathfinder over the given paths (bottom to top): divide (every face its own shape, painted like the top-most shape over it; open unfilled paths cut), trim (hidden parts removed), merge (trim, then touching parts of one fill joined), crop (the top shape crops the others), outline (outlines cut into open strokes where they meet) or minusBack (the top shape minus everything below). Results replace the paths, grouped when there are several.",
7986
+ params: { type: "object", properties: { operation: { type: "string", enum: [...VECTOR_PATHFINDER_OPERATIONS] }, ids: IDS }, required: ["operation"] },
7987
+ undoable: true,
7988
+ run: (_ctx, params) => {
7989
+ if (!VECTOR_PATHFINDER_OPERATIONS.includes(params.operation)) fail(`Unknown Pathfinder operation "${params.operation}".`);
7990
+ const created = controller.pathfinder(params.operation, { ids: ids(params) });
7991
+ if (!created.length) fail("Nothing came out: select paths that overlap (two or more, except for divide and outline).");
7992
+ return { ids: created, selection: [...controller.getState().selection] };
7993
+ }
7994
+ },
7995
+ {
7996
+ ...base,
7997
+ id: `${prefix}shapeBuilderFaces`,
7998
+ label: "List Shape Builder Faces",
7999
+ keywords: ["shape builder", "regions", "faces", "planar"],
8000
+ description: "Lists the faces the given paths divide the plane into: path data, area, a point inside and the ids of the paths filling each (empty when only outlines enclose it).",
8001
+ params: { type: "object", properties: { ids: IDS } },
8002
+ run: (_ctx, params) => ({ faces: controller.getShapeBuilderFaces({ ids: ids(params) }) })
8003
+ },
8004
+ {
8005
+ ...base,
8006
+ id: `${prefix}shapeBuilder`,
8007
+ label: "Shape Builder",
8008
+ keywords: ["merge faces", "build shape", "combine regions", "delete region"],
8009
+ description: "Shape Builder gesture over the faces of the given paths: `points` is the pointer path in document units (one point is a click; [[x, y], \u2026]; use the points from shapeBuilderFaces). Merges the faces it touches into one shape, or with `erase: true` deletes them and the pieces of unfilled open paths within `tolerance`. Shapes covering those faces lose that area.",
8010
+ params: {
8011
+ type: "object",
8012
+ properties: { points: POINTS, erase: { type: "boolean" }, tolerance: { type: "number", minimum: 0 }, ids: IDS },
8013
+ required: ["points"]
8014
+ },
8015
+ undoable: true,
8016
+ run: (_ctx, params) => {
8017
+ const result = controller.shapeBuilder(params.points.map(([x, y]) => ({ x, y })), { ids: ids(params), erase: params.erase, tolerance: params.tolerance });
8018
+ check(result);
8019
+ return { merged: result.merged, faces: result.faces, lines: result.lines };
8020
+ }
8021
+ },
8022
+ {
8023
+ ...base,
8024
+ id: `${prefix}fillRegion`,
8025
+ label: "Fill Region",
8026
+ keywords: ["paint bucket", "live paint", "fill area", "enclosed"],
8027
+ description: "Paint bucket: fills the area the visible outlines enclose around `point` (crossings split, nested shapes left as holes) with a new path in the current fill, below the selection or on top. `gap` closes gaps up to that size between loose ends.",
8028
+ params: { type: "object", properties: { point: { type: "array", items: { type: "number" }, minItems: 2, maxItems: 2, description: "[x, y] in document units." }, gap: { type: "number", minimum: 0 } }, required: ["point"] },
8029
+ undoable: true,
8030
+ run: (_ctx, params) => {
8031
+ const id = controller.fillRegion({ x: params.point[0], y: params.point[1] }, { gap: params.gap });
8032
+ if (!id) fail("No closed region surrounds that point.");
8033
+ return { id };
8034
+ }
8035
+ },
6480
8036
  {
6481
8037
  ...base,
6482
8038
  id: `${prefix}align`,
@@ -6530,7 +8086,7 @@ function createVectorCommands(controller, options = {}) {
6530
8086
  enabled: enabledFor,
6531
8087
  undoable: true,
6532
8088
  run: (_ctx, params) => {
6533
- const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _178 => _178.name]) });
8089
+ const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _266 => _266.name]) });
6534
8090
  if (!id) fail("Only nodes that share a parent can be grouped.");
6535
8091
  return { id };
6536
8092
  }
@@ -6558,7 +8114,7 @@ function createVectorCommands(controller, options = {}) {
6558
8114
  params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
6559
8115
  undoable: true,
6560
8116
  run: (_ctx, params) => {
6561
- check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _179 => _179.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _180 => _180.keepShape]) }));
8117
+ check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _267 => _267.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _268 => _268.keepShape]) }));
6562
8118
  return { ok: true };
6563
8119
  }
6564
8120
  },
@@ -6613,7 +8169,7 @@ function createVectorCommands(controller, options = {}) {
6613
8169
  params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
6614
8170
  undoable: true,
6615
8171
  run: (_ctx, params) => {
6616
- check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _181 => _181.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _182 => _182.tolerance]) }));
8172
+ check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _269 => _269.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _270 => _270.tolerance]) }));
6617
8173
  return { selection: controller.getState().selection };
6618
8174
  }
6619
8175
  },
@@ -6627,7 +8183,7 @@ function createVectorCommands(controller, options = {}) {
6627
8183
  params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
6628
8184
  undoable: true,
6629
8185
  run: (_ctx, params) => {
6630
- check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _183 => _183.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _184 => _184.anchors]))));
8186
+ check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _271 => _271.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _272 => _272.anchors]))));
6631
8187
  return { ok: true };
6632
8188
  }
6633
8189
  },
@@ -6641,7 +8197,7 @@ function createVectorCommands(controller, options = {}) {
6641
8197
  enabled: enabledFor,
6642
8198
  undoable: true,
6643
8199
  run: (_ctx, params) => {
6644
- check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _185 => _185.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _186 => _186.tolerance]) }));
8200
+ check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _273 => _273.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _274 => _274.tolerance]) }));
6645
8201
  return { ok: true };
6646
8202
  }
6647
8203
  },
@@ -6654,7 +8210,7 @@ function createVectorCommands(controller, options = {}) {
6654
8210
  params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
6655
8211
  undoable: true,
6656
8212
  run: (_ctx, params) => {
6657
- check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _187 => _187.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _188 => _188.amount]), refs: _optionalChain([params, 'optionalAccess', _189 => _189.anchors]) }));
8213
+ check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _275 => _275.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _276 => _276.amount]), refs: _optionalChain([params, 'optionalAccess', _277 => _277.anchors]) }));
6658
8214
  return { ok: true };
6659
8215
  }
6660
8216
  },
@@ -6667,14 +8223,14 @@ function createVectorCommands(controller, options = {}) {
6667
8223
  params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
6668
8224
  undoable: true,
6669
8225
  run: (_ctx, params) => {
6670
- if (_optionalChain([params, 'optionalAccess', _190 => _190.nodeId]) !== void 0) {
8226
+ if (_optionalChain([params, 'optionalAccess', _278 => _278.nodeId]) !== void 0) {
6671
8227
  if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
6672
8228
  if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
6673
8229
  const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
6674
8230
  if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
6675
8231
  return { ids: created };
6676
8232
  }
6677
- check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _191 => _191.anchors]))));
8233
+ check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _279 => _279.anchors]))));
6678
8234
  return { ids: controller.getState().selection };
6679
8235
  }
6680
8236
  },
@@ -6711,7 +8267,7 @@ function createVectorCommands(controller, options = {}) {
6711
8267
  id: `${prefix}setEditing`,
6712
8268
  label: "Path Editing Options\u2026",
6713
8269
  keywords: ["settings", "curvature", "pencil", "smoothing", "lazy mouse", "shapes", "eraser"],
6714
- description: "Sets how editing behaves: `keepShapeOnDelete`, `curvatureMoves` (anchor drags bend like the Curvature tool), `pencilSmoothing` (lazy mouse, 0\u20131), `recognizeShapes` (pencil scribbles become lines, rectangles, ellipses, polygons) and `eraserSize`.",
8270
+ description: "Sets how editing behaves: `keepShapeOnDelete`, `curvatureMoves` (anchor drags bend like the Curvature tool), `pencilSmoothing` (lazy mouse, 0\u20131), `recognizeShapes` (pencil scribbles become lines, rectangles, ellipses, polygons), `eraserSize` and `pencilPressure` (`pen`: record pen pressure on pencil strokes as a width profile, `all`: any pointer reporting pressure, `off`).",
6715
8271
  params: {
6716
8272
  type: "object",
6717
8273
  properties: {
@@ -6719,12 +8275,13 @@ function createVectorCommands(controller, options = {}) {
6719
8275
  curvatureMoves: { type: "boolean" },
6720
8276
  pencilSmoothing: { type: "number", minimum: 0, maximum: 1 },
6721
8277
  recognizeShapes: { type: "boolean" },
6722
- eraserSize: { type: "number", exclusiveMinimum: 0 }
8278
+ eraserSize: { type: "number", exclusiveMinimum: 0 },
8279
+ pencilPressure: { type: "string", enum: ["pen", "all", "off"] }
6723
8280
  }
6724
8281
  },
6725
8282
  run: (_ctx, params) => {
6726
8283
  const patch = {};
6727
- for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize"]) if (_optionalChain([params, 'optionalAccess', _192 => _192[key]]) !== void 0) patch[key] = params[key];
8284
+ for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _280 => _280[key]]) !== void 0) patch[key] = params[key];
6728
8285
  controller.setEditing(patch);
6729
8286
  return { editing: controller.getState().editing };
6730
8287
  }
@@ -6749,7 +8306,7 @@ function createVectorCommands(controller, options = {}) {
6749
8306
  keywords: ["download", "save", "markup"],
6750
8307
  description: "Returns the document as SVG markup.",
6751
8308
  params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
6752
- run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _193 => _193.precision]) }) })
8309
+ run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _281 => _281.precision]) }) })
6753
8310
  },
6754
8311
  {
6755
8312
  ...base,
@@ -6915,5 +8472,51 @@ function createVectorCommands(controller, options = {}) {
6915
8472
 
6916
8473
 
6917
8474
 
6918
- exports.DEFAULT_VECTOR_PATH_EDIT = DEFAULT_VECTOR_PATH_EDIT; exports.DEFAULT_VECTOR_SNAP = DEFAULT_VECTOR_SNAP; exports.IDENTITY_MATRIX = IDENTITY_MATRIX; exports.KAPPA = KAPPA2; exports.LazyMouse = LazyMouse; exports.VECTOR_ARROWHEADS = VECTOR_ARROWHEADS; exports.VECTOR_ARROWHEAD_LABELS = VECTOR_ARROWHEAD_LABELS; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.cloneAnchor = cloneAnchor; exports.cloneSubpath = cloneSubpath; exports.cloneVectorDocument = cloneVectorDocument; exports.cloneVectorNode = cloneVectorNode; exports.collectPathNodes = collectPathNodes; exports.constrainToLeave = constrainToLeave; exports.convertAnchor = convertAnchor; exports.cornerBisectorPoint = cornerBisectorPoint; exports.cornerRadiusChange = cornerRadiusChange; exports.cornerSetback = cornerSetback; exports.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.decomposeRigid = decomposeRigid; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.reverseSubpath = reverseSubpath; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes;
8475
+
8476
+
8477
+
8478
+
8479
+
8480
+
8481
+
8482
+
8483
+
8484
+
8485
+
8486
+
8487
+
8488
+
8489
+
8490
+
8491
+
8492
+
8493
+
8494
+
8495
+
8496
+
8497
+
8498
+
8499
+
8500
+
8501
+
8502
+
8503
+
8504
+
8505
+
8506
+
8507
+
8508
+
8509
+
8510
+
8511
+
8512
+
8513
+
8514
+
8515
+
8516
+
8517
+
8518
+
8519
+
8520
+
8521
+ exports.DEFAULT_VECTOR_PATH_EDIT = DEFAULT_VECTOR_PATH_EDIT; exports.DEFAULT_VECTOR_SNAP = DEFAULT_VECTOR_SNAP; exports.IDENTITY_MATRIX = IDENTITY_MATRIX; exports.KAPPA = KAPPA2; exports.LazyMouse = LazyMouse; exports.VECTOR_ARROWHEADS = VECTOR_ARROWHEADS; exports.VECTOR_ARROWHEAD_LABELS = VECTOR_ARROWHEAD_LABELS; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_FILL_GAP = VECTOR_FILL_GAP; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATHFINDER_ACTIONS = VECTOR_PATHFINDER_ACTIONS; exports.VECTOR_PATHFINDER_LABELS = VECTOR_PATHFINDER_LABELS; exports.VECTOR_PATHFINDER_OPERATIONS = VECTOR_PATHFINDER_OPERATIONS; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.addBuilderHit = addBuilderHit; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.cloneAnchor = cloneAnchor; exports.cloneSubpath = cloneSubpath; exports.cloneVectorDocument = cloneVectorDocument; exports.cloneVectorNode = cloneVectorNode; exports.collectPathNodes = collectPathNodes; exports.constrainToLeave = constrainToLeave; exports.convertAnchor = convertAnchor; exports.cornerBisectorPoint = cornerBisectorPoint; exports.cornerRadiusChange = cornerRadiusChange; exports.cornerSetback = cornerSetback; exports.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.dashIntervals = dashIntervals; exports.decomposeRigid = decomposeRigid; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVectorWidthHandles = getVectorWidthHandles; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.insertWidthPoint = insertWidthPoint; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mirrorWidthProfile = mirrorWidthProfile; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.planShapeBuilder = planShapeBuilder; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeWidthPoints = removeWidthPoints; exports.reverseSubpath = reverseSubpath; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.samplePolyline = samplePolyline; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathEnclosesArea = subpathEnclosesArea; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.taperWaveFactor = taperWaveFactor; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorFillMap = vectorFillMap; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathEntries = vectorPathEntries; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorPathfinderShape = vectorPathfinderShape; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorResultStyle = vectorResultStyle; exports.vectorShapeBuilderHit = vectorShapeBuilderHit; exports.vectorShapeBuilderMap = vectorShapeBuilderMap; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute;
6919
8522
  //# sourceMappingURL=index.cjs.map