@pixerate/editor 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,12 +31,15 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
31
31
 
32
32
 
33
33
 
34
- var _chunkBGNPT5PEcjs = require('../chunk-BGNPT5PE.cjs');
34
+
35
+
36
+
37
+ var _chunkGVB7ZPLZcjs = require('../chunk-GVB7ZPLZ.cjs');
35
38
 
36
39
  // src/vector/types.ts
37
40
  var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
38
41
  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"];
42
+ var VECTOR_TOOLS = ["select", "direct", "pen", "pencil", "rect", "ellipse", "polygon", "star", "line", "scissors", "knife", "eraser", "width"];
40
43
  var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
41
44
 
42
45
  // src/vector/corners.ts
@@ -58,7 +61,7 @@ var Side = class {
58
61
  constructor(a, h1, h2, b) {
59
62
  this.straight = onAnchor(a, h1) && onAnchor(b, h2);
60
63
  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);
64
+ this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunkGVB7ZPLZcjs.segmentLength.call(void 0, this.curve);
62
65
  }
63
66
  /** Unit direction the side leaves the corner in (towards its first control point off the corner). */
64
67
  direction() {
@@ -76,10 +79,10 @@ var Side = class {
76
79
  const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
77
80
  return { t: t2, p: add(vec(this.curve[0]), mul(u, s)), dir: u };
78
81
  }
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));
82
+ const t = _chunkGVB7ZPLZcjs.segmentParamAtLength.call(void 0, this.curve, s, this.length);
83
+ const d = vec(_chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, this.curve, t));
81
84
  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 };
85
+ return { t, p: vec(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
83
86
  }
84
87
  };
85
88
  function cornerOf(sp, s, i) {
@@ -180,7 +183,7 @@ function expandVectorCornersOf(sp, subpath = 0) {
180
183
  const s0 = from ? from.tv : 0;
181
184
  const s1 = to ? 1 - to.tu : 1;
182
185
  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));
186
+ const part = _chunkGVB7ZPLZcjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
184
187
  return { out: vec(part[1]), in: vec(part[2]) };
185
188
  });
186
189
  const anchors = [];
@@ -245,19 +248,561 @@ function setSubpathCorners(sp, radius, kind) {
245
248
  return { closed: sp.closed, anchors: sp.anchors.map((a) => setAnchorCorner(a, radius, kind)) };
246
249
  }
247
250
 
251
+ // src/vector/width.ts
252
+ var VECTOR_WIDTH_PROFILE_MAX_POINTS = 64;
253
+ var VECTOR_WIDTH_PROFILE_MAX_SCALE = 1e3;
254
+ var VECTOR_WIDTH_SAME_T = 1e-6;
255
+ var sym = (t, s) => ({ t, left: s, right: s });
256
+ var VECTOR_WIDTH_PRESETS = [
257
+ { id: "uniform", label: "Uniform", points: [] },
258
+ { id: "taperStart", label: "Taper start", points: [sym(0, 0), sym(1, 1)] },
259
+ { id: "taperEnd", label: "Taper end", points: [sym(0, 1), sym(1, 0)] },
260
+ { id: "taperBoth", label: "Taper both", points: [sym(0, 0), sym(0.5, 1), sym(1, 0)] },
261
+ // Full width at the ends, swelling to twice the width in the middle.
262
+ { id: "bulge", label: "Bulge", points: [sym(0, 1), sym(0.5, 2), sym(1, 1)] },
263
+ // Full width at the ends, pinched to a quarter in the middle.
264
+ { id: "pinch", label: "Pinch", points: [sym(0, 1), sym(0.5, 0.25), sym(1, 1)] },
265
+ // A round head a fifth of the way along, then a long taper to the end.
266
+ { id: "teardrop", label: "Teardrop", points: [sym(0, 0), sym(0.2, 1), sym(1, 0)] },
267
+ // Two swells between narrow necks.
268
+ { 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)] },
269
+ // A pen stroke: a quick press, a slow lift.
270
+ { 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)] }
271
+ ];
272
+ var VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESETS.map((p) => p.id);
273
+ var clean = (v2, max) => typeof v2 === "number" && Number.isFinite(v2) ? Math.min(Math.max(v2, 0), max) : null;
274
+ function sanitizeWidthProfile(points) {
275
+ if (!Array.isArray(points)) return [];
276
+ const out = [];
277
+ for (const p of points) {
278
+ if (!p || typeof p !== "object") continue;
279
+ const t = clean(p.t, 1);
280
+ const l = clean(p.left, VECTOR_WIDTH_PROFILE_MAX_SCALE);
281
+ const r = clean(p.right, VECTOR_WIDTH_PROFILE_MAX_SCALE);
282
+ if (t === null || l === null && r === null) continue;
283
+ out.push({ t, left: _nullishCoalesce(l, () => ( r)), right: _nullishCoalesce(r, () => ( l)) });
284
+ if (out.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) break;
285
+ }
286
+ return out.map((p, i) => [p, i]).sort((a, b) => a[0].t - b[0].t || a[1] - b[1]).map(([p]) => p);
287
+ }
288
+ function isUniformWidthProfile(points) {
289
+ return !_optionalChain([points, 'optionalAccess', _7 => _7.length]) || points.every((p) => p.left === 1 && p.right === 1);
290
+ }
291
+ function secant(pts, i, k) {
292
+ const a = pts[i];
293
+ const b = pts[i + 1];
294
+ return a && b && b.t - a.t > 1e-12 ? (b[k] - a[k]) / (b.t - a.t) : 0;
295
+ }
296
+ function tangent(pts, i, k) {
297
+ const last = pts.length - 1;
298
+ if (i <= 0) return secant(pts, 0, k);
299
+ if (i >= last) return secant(pts, last - 1, k);
300
+ const d0 = secant(pts, i - 1, k);
301
+ const d1 = secant(pts, i, k);
302
+ return d0 * d1 <= 0 ? 0 : 2 * d0 * d1 / (d0 + d1);
303
+ }
304
+ function interp(pts, t, k) {
305
+ const first = pts[0];
306
+ const last = pts[pts.length - 1];
307
+ if (!(t > first.t)) return first[k];
308
+ if (t >= last.t) return last[k];
309
+ let i = 0;
310
+ while (i + 1 < pts.length && pts[i + 1].t <= t) i++;
311
+ const a = pts[i];
312
+ const b = pts[i + 1];
313
+ const h = b.t - a.t;
314
+ if (h <= 1e-12) return b[k];
315
+ const u = (t - a.t) / h;
316
+ const u2 = u * u;
317
+ const u3 = u2 * u;
318
+ 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);
319
+ return Math.max(0, w);
320
+ }
321
+ function widthProfileAt(points, t) {
322
+ if (!_optionalChain([points, 'optionalAccess', _8 => _8.length])) return { left: 1, right: 1 };
323
+ return { left: interp(points, t, "left"), right: interp(points, t, "right") };
324
+ }
325
+ function widthProfileMaxScale(points) {
326
+ if (!_optionalChain([points, 'optionalAccess', _9 => _9.length])) return 1;
327
+ return points.reduce((m, p) => Math.max(m, p.left, p.right), 0);
328
+ }
329
+ function widthProfilePreset(id) {
330
+ const p = VECTOR_WIDTH_PRESETS.find((q) => q.id === id);
331
+ return p ? p.points.map((q) => ({ ...q })) : null;
332
+ }
333
+ function widthProfilePresetId(points) {
334
+ if (isUniformWidthProfile(points)) return "uniform";
335
+ 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);
336
+ return _nullishCoalesce(_optionalChain([VECTOR_WIDTH_PRESETS, 'access', _10 => _10.find, 'call', _11 => _11((p) => p.points.length && same(points, p.points)), 'optionalAccess', _12 => _12.id]), () => ( "custom"));
337
+ }
338
+ function reverseWidthProfile(points) {
339
+ return points.map((p) => ({ t: 1 - p.t, left: p.right, right: p.left })).reverse();
340
+ }
341
+ function mirrorWidthProfile(points) {
342
+ return points.map((p) => ({ t: p.t, left: p.right, right: p.left }));
343
+ }
344
+ function editable(points) {
345
+ return _optionalChain([points, 'optionalAccess', _13 => _13.length]) ? points.map((p) => ({ ...p })) : [sym(0, 1), sym(1, 1)];
346
+ }
347
+ function insertWidthPoint(points, t, sides = {}, options = {}) {
348
+ const pts = editable(points);
349
+ const tt = Math.min(Math.max(Number.isFinite(t) ? t : 0, 0), 1);
350
+ const here = widthProfileAt(_optionalChain([points, 'optionalAccess', _14 => _14.length]) ? points : null, tt);
351
+ const p = {
352
+ t: tt,
353
+ left: _nullishCoalesce(clean(sides.left, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.left)),
354
+ right: _nullishCoalesce(clean(sides.right, VECTOR_WIDTH_PROFILE_MAX_SCALE), () => ( here.right))
355
+ };
356
+ const same = pts.findIndex((q) => Math.abs(q.t - tt) <= VECTOR_WIDTH_SAME_T);
357
+ if (same >= 0 && !options.step) {
358
+ pts[same] = { ...p, t: pts[same].t };
359
+ return { points: pts, index: same };
360
+ }
361
+ let i = 0;
362
+ while (i < pts.length && pts[i].t <= tt) i++;
363
+ if (pts.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) return { points: pts, index: Math.max(0, i - 1) };
364
+ pts.splice(i, 0, p);
365
+ return { points: pts, index: i };
366
+ }
367
+ function setWidthPoint(points, index, patch) {
368
+ const pts = points.map((p2) => ({ ...p2 }));
369
+ const p = pts[index];
370
+ if (!p) return pts;
371
+ if (typeof patch.t === "number" && Number.isFinite(patch.t)) {
372
+ const lo = index > 0 ? pts[index - 1].t : 0;
373
+ const hi = index < pts.length - 1 ? pts[index + 1].t : 1;
374
+ p.t = Math.min(Math.max(patch.t, lo), Math.max(hi, lo));
375
+ }
376
+ const l = clean(patch.left, VECTOR_WIDTH_PROFILE_MAX_SCALE);
377
+ const r = clean(patch.right, VECTOR_WIDTH_PROFILE_MAX_SCALE);
378
+ if (l !== null) p.left = l;
379
+ if (r !== null) p.right = r;
380
+ return pts;
381
+ }
382
+ function removeWidthPoints(points, indices) {
383
+ const drop = new Set(indices);
384
+ return points.filter((_, i) => !drop.has(i)).map((p) => ({ ...p }));
385
+ }
386
+ function ease(u, e) {
387
+ const x = Math.min(Math.max(u, 0), 1);
388
+ const round = Math.sqrt(Math.max(0, 1 - (1 - x) * (1 - x)));
389
+ return x + (round - x) * Math.min(Math.max(e, 0), 1);
390
+ }
391
+ function taperWaveFactor(t, taper, wave) {
392
+ let k = 1;
393
+ if (taper) {
394
+ let ls = Math.max(0, _nullishCoalesce(taper.startLength, () => ( 0)));
395
+ let le = Math.max(0, _nullishCoalesce(taper.endLength, () => ( 0)));
396
+ if (ls + le > 1) {
397
+ const f = 1 / (ls + le);
398
+ ls *= f;
399
+ le *= f;
400
+ }
401
+ const sw = Math.min(Math.max(_nullishCoalesce(taper.startWidth, () => ( 0)), 0), 1);
402
+ const ew = Math.min(Math.max(_nullishCoalesce(taper.endWidth, () => ( 0)), 0), 1);
403
+ if (ls > 0 && t < ls) k = Math.min(k, sw + (1 - sw) * ease(t / ls, _nullishCoalesce(taper.startEase, () => ( 0))));
404
+ if (le > 0 && t > 1 - le) k = Math.min(k, ew + (1 - ew) * ease((1 - t) / le, _nullishCoalesce(taper.endEase, () => ( 0))));
405
+ }
406
+ if (wave) {
407
+ const cycles = _nullishCoalesce(wave.cycles, () => ( 4));
408
+ const amount = Math.min(Math.max(_nullishCoalesce(wave.amount, () => ( 0.5)), 0), 1);
409
+ if (cycles > 0 && amount > 0) {
410
+ const ph = 2 * Math.PI * t * cycles + (_nullishCoalesce(wave.phase, () => ( 0))) * Math.PI / 180;
411
+ k *= 1 - amount * (0.5 - 0.5 * Math.cos(ph));
412
+ }
413
+ }
414
+ return k;
415
+ }
416
+ function generateWidthProfile(options) {
417
+ const cycles = options.wave ? Math.max(0, _nullishCoalesce(options.wave.cycles, () => ( 4))) : 0;
418
+ const n = Math.max(8, Math.min(VECTOR_WIDTH_PROFILE_MAX_POINTS * 4, _nullishCoalesce(options.samples, () => ( Math.ceil(32 + cycles * 16)))));
419
+ const raw = [];
420
+ for (let i = 0; i <= n; i++) raw.push([i / n, taperWaveFactor(i / n, options.taper, options.wave)]);
421
+ const ts = [_optionalChain([options, 'access', _15 => _15.taper, 'optionalAccess', _16 => _16.startLength]), _optionalChain([options, 'access', _17 => _17.taper, 'optionalAccess', _18 => _18.endLength]) !== void 0 ? 1 - options.taper.endLength : void 0];
422
+ for (const t of ts) if (typeof t === "number" && t > 0 && t < 1) raw.push([t, taperWaveFactor(t, options.taper, options.wave)]);
423
+ raw.sort((a, b) => a[0] - b[0]);
424
+ const kept = simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.01)), VECTOR_WIDTH_PROFILE_MAX_POINTS);
425
+ return kept.map(([t, k]) => sym(t, k));
426
+ }
427
+ function simplifySamples(samples, tolerance, max) {
428
+ if (samples.length <= 2) return samples.slice();
429
+ const keep = new Array(samples.length).fill(false);
430
+ keep[0] = keep[samples.length - 1] = true;
431
+ const stack = [[0, samples.length - 1]];
432
+ while (stack.length) {
433
+ const [a, b] = stack.pop();
434
+ const [ta, va] = samples[a];
435
+ const [tb, vb] = samples[b];
436
+ let worst = -1;
437
+ let err = tolerance;
438
+ for (let i = a + 1; i < b; i++) {
439
+ const [t, v2] = samples[i];
440
+ const lin = tb - ta > 1e-12 ? va + (vb - va) * (t - ta) / (tb - ta) : va;
441
+ const e = Math.abs(v2 - lin);
442
+ if (e > err) {
443
+ err = e;
444
+ worst = i;
445
+ }
446
+ }
447
+ if (worst >= 0) {
448
+ keep[worst] = true;
449
+ stack.push([a, worst], [worst, b]);
450
+ }
451
+ }
452
+ let out = samples.filter((_, i) => keep[i]);
453
+ while (out.length > max) out = out.filter((_, i) => i === 0 || i === out.length - 1 || i % 2 === 0);
454
+ return out;
455
+ }
456
+ var VECTOR_PRESSURE_NOMINAL = 0.5;
457
+ function pressureWidthProfile(samples, options = {}) {
458
+ 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)]);
459
+ if (raw.length < 2) return [];
460
+ if (raw.every((s) => Math.abs(s[1] - raw[0][1]) < 1e-6)) return [];
461
+ return simplifySamples(raw, _nullishCoalesce(options.tolerance, () => ( 0.02)), VECTOR_WIDTH_PROFILE_MAX_POINTS).map(([t, k]) => sym(t, Math.round(k * 1e3) / 1e3));
462
+ }
463
+
464
+ // src/vector/varstroke.ts
465
+ var SAMPLES_PER_SUBPATH = 128;
466
+ var MAX_DASHES = 5e3;
467
+ var sub2 = (a, b) => [a[0] - b[0], a[1] - b[1]];
468
+ var along = (p, v2, k) => [p[0] + v2[0] * k, p[1] + v2[1] * k];
469
+ var lerp = (a, b, u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u];
470
+ var dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
471
+ var unitOf = (v2) => {
472
+ const l = Math.hypot(v2[0], v2[1]);
473
+ return l > 0 ? [v2[0] / l, v2[1] / l] : [1, 0];
474
+ };
475
+ var leftNormal = (d) => [d[1], -d[0]];
476
+ function flatten(segments, closed, tol) {
477
+ const pts = [];
478
+ const corner = [];
479
+ const push = (p, isCorner) => {
480
+ const last = pts[pts.length - 1];
481
+ if (last && dist(last, p) <= 1e-9) {
482
+ corner[corner.length - 1] ||= isCorner;
483
+ return;
484
+ }
485
+ pts.push(p);
486
+ corner.push(isCorner);
487
+ };
488
+ for (const seg of segments) {
489
+ const flat = _chunkGVB7ZPLZcjs.flattenChain.call(void 0, [seg], tol);
490
+ if (dist(seg[0], seg[seg.length - 1]) <= 1e-12 && flat.length > 1) flat.push(seg[seg.length - 1]);
491
+ flat.forEach((p, i) => push(p, i === 0 || i === flat.length - 1));
492
+ }
493
+ if (closed && pts.length > 2 && dist(pts[0], pts[pts.length - 1]) > 1e-9) push(pts[0], true);
494
+ if (pts.length < 2) return null;
495
+ let total = 0;
496
+ for (let i = 1; i < pts.length; i++) total += dist(pts[i - 1], pts[i]);
497
+ if (!(total > 0)) return null;
498
+ const step = Math.max(total / SAMPLES_PER_SUBPATH, tol);
499
+ const fine = [pts[0]];
500
+ const fineCorner = [corner[0]];
501
+ for (let i = 1; i < pts.length; i++) {
502
+ const n = Math.max(1, Math.ceil(dist(pts[i - 1], pts[i]) / step));
503
+ for (let k = 1; k <= n; k++) {
504
+ fine.push(k === n ? pts[i] : lerp(pts[i - 1], pts[i], k / n));
505
+ fineCorner.push(k === n ? corner[i] : false);
506
+ }
507
+ }
508
+ const s = [0];
509
+ for (let i = 1; i < fine.length; i++) s.push(s[i - 1] + dist(fine[i - 1], fine[i]));
510
+ return { pts: fine, s, corner: fineCorner, closed };
511
+ }
512
+ function dashIntervals(pattern, offset, len2) {
513
+ let pat = pattern.map((v2) => Number.isFinite(v2) ? Math.max(0, v2) : 0);
514
+ if (pat.length % 2 === 1) pat = [...pat, ...pat];
515
+ const period = pat.reduce((a, b) => a + b, 0);
516
+ if (pat.length < 2 || !(period > 1e-9) || !(len2 > 0)) return [[0, len2]];
517
+ let pos = ((Number.isFinite(offset) ? offset : 0) % period + period) % period;
518
+ let idx = 0;
519
+ while (pos >= pat[idx]) {
520
+ pos -= pat[idx];
521
+ idx = (idx + 1) % pat.length;
522
+ }
523
+ const out = [];
524
+ let s = 0;
525
+ let guard = 0;
526
+ while (s < len2 && guard++ < MAX_DASHES * pat.length) {
527
+ const e = Math.min(s + pat[idx] - pos, len2);
528
+ if (idx % 2 === 0 && e >= s) out.push([s, e]);
529
+ s += pat[idx] - pos;
530
+ pos = 0;
531
+ idx = (idx + 1) % pat.length;
532
+ }
533
+ return out;
534
+ }
535
+ function at(poly, t) {
536
+ let i = 1;
537
+ while (i < poly.pts.length - 1 && poly.s[i] <= t) i++;
538
+ const a = poly.s[i - 1];
539
+ const b = poly.s[i];
540
+ const u = b > a ? Math.min(Math.max((t - a) / (b - a), 0), 1) : 0;
541
+ return [lerp(poly.pts[i - 1], poly.pts[i], u), i];
542
+ }
543
+ function arcPoints(c, a0, sweep, r0, r1, tol) {
544
+ const r = Math.max(r0, r1);
545
+ if (!(r > tol * 0.5) || !Number.isFinite(sweep)) return [];
546
+ const stepAngle = 2 * Math.acos(Math.min(Math.max(1 - tol / r, -1), 1));
547
+ const n = Math.min(128, Math.max(2, Math.ceil(Math.abs(sweep) / Math.max(stepAngle, 1e-3))));
548
+ const out = [];
549
+ for (let k = 1; k < n; k++) {
550
+ const f = k / n;
551
+ const a = a0 + sweep * f;
552
+ const rr = r0 + (r1 - r0) * f;
553
+ out.push([c[0] + Math.cos(a) * rr, c[1] + Math.sin(a) * rr]);
554
+ }
555
+ return out;
556
+ }
557
+ function crossing(a0, a1, b0, b1) {
558
+ const r = sub2(a1, a0);
559
+ const q = sub2(b1, b0);
560
+ const den = r[0] * q[1] - r[1] * q[0];
561
+ if (Math.abs(den) < 1e-12) return null;
562
+ const w = sub2(b0, a0);
563
+ const t = (w[0] * q[1] - w[1] * q[0]) / den;
564
+ const u = (w[0] * r[1] - w[1] * r[0]) / den;
565
+ return t > 1e-9 && t <= 1 && u >= 0 && u < 1 - 1e-9 ? [a0[0] + r[0] * t, a0[1] + r[1] * t] : null;
566
+ }
567
+ function variableStrokeRings(path, options) {
568
+ const half = options.width / 2;
569
+ if (!(half > 0) || !Number.isFinite(half)) return [];
570
+ const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.05;
571
+ const join = _nullishCoalesce(options.join, () => ( "miter"));
572
+ const cap = _nullishCoalesce(options.cap, () => ( "butt"));
573
+ const miterLimit = options.miterLimit && options.miterLimit >= 1 ? options.miterLimit : 4;
574
+ const rings = [];
575
+ const push = (out, p) => {
576
+ const last = out[out.length - 1];
577
+ if (!last || dist(last, p) > 1e-12) out.push(p);
578
+ };
579
+ for (const contour of _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path)) {
580
+ if (!contour.segments.length) continue;
581
+ const poly = flatten(contour.segments, contour.closed, Math.min(tol, half) / 4);
582
+ if (!poly) continue;
583
+ const len2 = poly.s[poly.s.length - 1];
584
+ const sides = (s) => {
585
+ const w = widthProfileAt(options.profile, s / len2);
586
+ return [w.left * half, w.right * half];
587
+ };
588
+ const intervals = _optionalChain([options, 'access', _19 => _19.dash, 'optionalAccess', _20 => _20.some, 'call', _21 => _21((v2) => v2 > 0)]) ? dashIntervals(options.dash, _nullishCoalesce(options.dashOffset, () => ( 0)), len2) : [[0, len2]];
589
+ const whole = intervals.length === 1 && intervals[0][0] <= 0 && intervals[0][1] >= len2;
590
+ for (const [a, b] of intervals) {
591
+ const [pa, ia] = at(poly, a);
592
+ const [pb, ib] = at(poly, b);
593
+ const pts = [{ p: pa, s: a, corner: poly.corner[0] && a <= 0 }];
594
+ for (let i = ia; i < ib; i++) pts.push({ p: poly.pts[i], s: poly.s[i], corner: poly.corner[i] });
595
+ pts.push({ p: pb, s: b, corner: false });
596
+ const run = pts.filter((q, i) => i === 0 || dist(q.p, pts[i - 1].p) > 1e-9);
597
+ if (run.length < 2) {
598
+ const [l, r] = sides(a);
599
+ const w2 = (l + r) / 2;
600
+ if (cap === "round" && w2 > 0) rings.push([[pa[0] + w2, pa[1]], ...arcPoints(pa, 0, 2 * Math.PI, w2, w2, tol)]);
601
+ continue;
602
+ }
603
+ const closed = whole && poly.closed;
604
+ const m = run.length - 1;
605
+ const dirs = [];
606
+ for (let i = 0; i < m; i++) dirs.push(unitOf(sub2(run[i + 1].p, run[i].p)));
607
+ const normals = dirs.map(leftNormal);
608
+ const w = run.map((q) => sides(q.s));
609
+ const side = (sigma) => {
610
+ const k = sigma > 0 ? 0 : 1;
611
+ const S = (i) => along(run[i].p, normals[i], sigma * w[i][k]);
612
+ const E = (i) => along(run[i + 1].p, normals[i], sigma * w[i + 1][k]);
613
+ const out = [S(0)];
614
+ for (let i = 0; i < m; i++) {
615
+ const seam = i === m - 1;
616
+ if (seam && !closed) {
617
+ push(out, E(i));
618
+ break;
619
+ }
620
+ const j = seam ? 0 : i + 1;
621
+ const p = run[i + 1].p;
622
+ const d0 = dirs[i];
623
+ const d1 = dirs[j];
624
+ const cross2 = d0[0] * d1[1] - d0[1] * d1[0];
625
+ const dot2 = d0[0] * d1[0] + d0[1] * d1[1];
626
+ const r0 = w[i + 1][k];
627
+ const r1 = w[j][k];
628
+ const e = E(i);
629
+ const st = S(j);
630
+ if (Math.abs(cross2) < 1e-12 && dot2 > 0) {
631
+ push(out, e);
632
+ if (!seam) push(out, st);
633
+ continue;
634
+ }
635
+ const uturn = Math.abs(cross2) < 1e-12;
636
+ const outer = uturn ? sigma > 0 : cross2 * sigma > 0;
637
+ if (!outer) {
638
+ const x = seam ? null : crossing(out[out.length - 1], e, st, E(j));
639
+ if (x) push(out, x);
640
+ else {
641
+ push(out, e);
642
+ push(out, p);
643
+ if (!seam) push(out, st);
644
+ }
645
+ continue;
646
+ }
647
+ push(out, e);
648
+ const theta = uturn ? Math.PI : Math.atan2(cross2, dot2);
649
+ const r = (r0 + r1) / 2;
650
+ const kind = run[i + 1].corner || seam && poly.corner[0] ? join : "round";
651
+ if (r > 1e-9) {
652
+ if (kind === "round") {
653
+ if (r * (1 - Math.cos(Math.abs(theta) / 2)) > tol * 0.25) {
654
+ for (const q of arcPoints(p, Math.atan2(e[1] - p[1], e[0] - p[0]), theta, r0, r1, tol)) push(out, q);
655
+ }
656
+ } else if (kind === "miter") {
657
+ const halfCos = Math.cos(theta / 2);
658
+ const bis = [normals[i][0] + normals[j][0], normals[i][1] + normals[j][1]];
659
+ const bl = Math.hypot(bis[0], bis[1]);
660
+ if (halfCos > 1e-6 && 1 / halfCos <= miterLimit && bl > 1e-9) push(out, along(p, bis, sigma * r / halfCos / bl));
661
+ }
662
+ }
663
+ if (!seam) push(out, st);
664
+ }
665
+ if (closed && out.length > 1 && dist(out[0], out[out.length - 1]) <= 1e-12) out.pop();
666
+ return out;
667
+ };
668
+ const left = side(1);
669
+ const right = side(-1);
670
+ if (closed) {
671
+ rings.push(left, right.reverse());
672
+ continue;
673
+ }
674
+ const capPoints = (atStart) => {
675
+ if (cap === "butt") return [];
676
+ const i = atStart ? 0 : m;
677
+ const d = atStart ? dirs[0] : dirs[m - 1];
678
+ const [l, r] = w[i];
679
+ if (!(l + r > 1e-9)) return [];
680
+ const p = run[i].p;
681
+ const n = leftNormal(d);
682
+ const L = along(p, n, l);
683
+ const R = along(p, n, -r);
684
+ if (cap === "round") {
685
+ const a0 = Math.atan2(d[1], d[0]) + (atStart ? Math.PI / 2 : -Math.PI / 2);
686
+ return arcPoints(p, a0, Math.PI, atStart ? r : l, atStart ? l : r, tol);
687
+ }
688
+ const ext = (l + r) / 2;
689
+ const o = atStart ? [-d[0], -d[1]] : d;
690
+ return atStart ? [along(R, o, ext), along(L, o, ext)] : [along(L, o, ext), along(R, o, ext)];
691
+ };
692
+ const ring = [];
693
+ for (const q of [...left, ...capPoints(false), ...right.reverse(), ...capPoints(true)]) push(ring, q);
694
+ if (ring.length > 2) rings.push(ring);
695
+ }
696
+ }
697
+ return rings;
698
+ }
699
+ function variableStrokeOutline(path, options) {
700
+ const pieces = variableStrokeRings(path, options);
701
+ if (!pieces.length) return [];
702
+ const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.05;
703
+ let polygons;
704
+ try {
705
+ polygons = _chunkGVB7ZPLZcjs.polygonBoolean.call(void 0, "union", [{ rings: pieces, fillRule: "nonzero" }]);
706
+ } catch (e2) {
707
+ return [];
708
+ }
709
+ const out = [];
710
+ for (const polygon2 of polygons) {
711
+ for (const ring of polygon2) {
712
+ if (ring.length < 3) continue;
713
+ const cubics = _chunkGVB7ZPLZcjs.fitCubics.call(void 0, ring, tol, { closed: true, cornerAngleDeg: 25 });
714
+ if (!cubics.length) continue;
715
+ out.push({ type: "M", x: cubics[0].p0[0], y: cubics[0].p0[1] });
716
+ cubics.forEach((c, i) => {
717
+ if (_chunkGVB7ZPLZcjs.isStraightCubic.call(void 0, c, tol * 0.02)) {
718
+ if (i < cubics.length - 1) out.push({ type: "L", x: c.p3[0], y: c.p3[1] });
719
+ } 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] });
720
+ });
721
+ out.push({ type: "Z" });
722
+ }
723
+ }
724
+ return out;
725
+ }
726
+ function nearestStrokePosition(path, p, only) {
727
+ let best = null;
728
+ _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).forEach((c, ci) => {
729
+ if (!c.segments.length || only !== void 0 && ci !== only) return;
730
+ const lengths = c.segments.map((s) => _chunkGVB7ZPLZcjs.segmentLength.call(void 0, s));
731
+ const total = lengths.reduce((a, b) => a + b, 0);
732
+ if (!(total > 0)) return;
733
+ let acc = 0;
734
+ c.segments.forEach((seg, si) => {
735
+ const near2 = _chunkGVB7ZPLZcjs.segmentNearest.call(void 0, seg, p);
736
+ if (!best || near2.distance < best.distance) {
737
+ const s = acc + _chunkGVB7ZPLZcjs.segmentLength.call(void 0, seg, 0, near2.t);
738
+ const tan = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, seg, near2.t);
739
+ const pt2 = seg.length === 2 ? lerp(seg[0], seg[1], near2.t) : evalSeg(seg, near2.t);
740
+ const n = leftNormal([tan[0], tan[1]]);
741
+ const side = (p[0] - pt2[0]) * n[0] + (p[1] - pt2[1]) * n[1] >= 0 ? "left" : "right";
742
+ 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 };
743
+ }
744
+ acc += lengths[si];
745
+ });
746
+ });
747
+ return best;
748
+ }
749
+ function evalSeg(seg, t) {
750
+ let pts = seg.map((q) => [q[0], q[1]]);
751
+ while (pts.length > 1) pts = pts.slice(1).map((q, i) => lerp(pts[i], q, t));
752
+ return pts[0];
753
+ }
754
+ function strokePositionPoint(path, index, t) {
755
+ const c = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path)[index];
756
+ if (!_optionalChain([c, 'optionalAccess', _22 => _22.segments, 'access', _23 => _23.length])) return null;
757
+ const lengths = c.segments.map((s) => _chunkGVB7ZPLZcjs.segmentLength.call(void 0, s));
758
+ const total = lengths.reduce((a, b) => a + b, 0);
759
+ let want = Math.min(Math.max(t, 0), 1) * total;
760
+ for (let i = 0; i < c.segments.length; i++) {
761
+ const seg = c.segments[i];
762
+ if (want <= lengths[i] + 1e-12 || i === c.segments.length - 1) {
763
+ let lo = 0;
764
+ let hi = 1;
765
+ const target = Math.min(want, lengths[i]);
766
+ for (let k = 0; k < 40; k++) {
767
+ const mid = (lo + hi) / 2;
768
+ if (_chunkGVB7ZPLZcjs.segmentLength.call(void 0, seg, 0, mid) < target) lo = mid;
769
+ else hi = mid;
770
+ }
771
+ const u = (lo + hi) / 2;
772
+ const tan = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, seg, u);
773
+ return { point: evalSeg(seg, u), tangent: [tan[0], tan[1]] };
774
+ }
775
+ want -= lengths[i];
776
+ }
777
+ return null;
778
+ }
779
+
248
780
  // src/vector/stroke.ts
249
781
  var KAPPA = 0.5522847498307936;
250
782
  var NOTCH = 0.7;
251
783
  function isClosedBezierShape(path) {
252
- const contours = _chunkBGNPT5PEcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
784
+ const contours = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
253
785
  return contours.length > 0 && contours.every((c) => c.closed);
254
786
  }
255
787
  function effectiveStrokeAlign(style, closedShape) {
256
788
  const a = _nullishCoalesce(style.strokeAlign, () => ( "center"));
257
789
  return closedShape ? a : "center";
258
790
  }
791
+ function strokeWidthProfileOf(style) {
792
+ if (!_optionalChain([style, 'access', _24 => _24.strokeWidthProfile, 'optionalAccess', _25 => _25.length])) return null;
793
+ const p = sanitizeWidthProfile(style.strokeWidthProfile);
794
+ return isUniformWidthProfile(p) ? null : p;
795
+ }
259
796
  function hasStrokeExtras(style) {
260
- return (_nullishCoalesce(style.strokeAlign, () => ( "center"))) !== "center" || !!style.startArrow || !!style.endArrow;
797
+ return (_nullishCoalesce(style.strokeAlign, () => ( "center"))) !== "center" || !!style.startArrow || !!style.endArrow || !!strokeWidthProfileOf(style);
798
+ }
799
+ function vectorStrokeLineArea(parts, style, tolerance) {
800
+ const profile = strokeWidthProfileOf(style);
801
+ const dash = _optionalChain([style, 'access', _26 => _26.strokeDasharray, 'optionalAccess', _27 => _27.length]) ? { dash: style.strokeDasharray } : {};
802
+ const join = _nullishCoalesce(style.strokeLinejoin, () => ( "miter"));
803
+ const cap = _nullishCoalesce(style.strokeLinecap, () => ( "butt"));
804
+ if (profile) return variableStrokeOutline(parts.line, { width: parts.drawWidth, profile, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
805
+ return _chunkGVB7ZPLZcjs.outlineStroke.call(void 0, parts.line, { width: parts.drawWidth, join, cap, ...dash, ...tolerance ? { tolerance } : {} });
261
806
  }
262
807
  var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
263
808
  function arrowheadWeight(style, end) {
@@ -334,27 +879,27 @@ function arrowheadPath(kind, tip, dir, hw) {
334
879
  }
335
880
  }
336
881
  var toVec = (p) => ({ x: p[0], y: p[1] });
337
- var segLen = (s) => _chunkBGNPT5PEcjs.segmentLength.call(void 0, s);
882
+ var segLen = (s) => _chunkGVB7ZPLZcjs.segmentLength.call(void 0, s);
338
883
  function endOf(segs, atStart) {
339
884
  const long = (s2) => segLen(s2) > 1e-9;
340
885
  if (atStart) {
341
886
  const s2 = _nullishCoalesce(segs.find(long), () => ( segs[0]));
342
- const t2 = _chunkBGNPT5PEcjs.segmentTangent.call(void 0, s2, 0);
887
+ const t2 = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, s2, 0);
343
888
  return { p: toVec(segs[0][0]), out: v(-t2[0], -t2[1]) };
344
889
  }
345
890
  const s = _nullishCoalesce([...segs].reverse().find(long), () => ( segs[segs.length - 1]));
346
- const t = _chunkBGNPT5PEcjs.segmentTangent.call(void 0, s, 1);
891
+ const t = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, s, 1);
347
892
  const last = segs[segs.length - 1];
348
893
  return { p: toVec(last[last.length - 1]), out: v(t[0], t[1]) };
349
894
  }
350
- function pointAlong(segs, fromStart, dist3) {
895
+ function pointAlong(segs, fromStart, dist4) {
351
896
  const order = fromStart ? segs : [...segs].reverse();
352
897
  let acc = 0;
353
898
  for (const s of order) {
354
899
  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);
900
+ if (acc + l >= dist4) {
901
+ const want = fromStart ? dist4 - acc : l - (dist4 - acc);
902
+ const pts = _chunkGVB7ZPLZcjs.trimSegments.call(void 0, [s], 0, want);
358
903
  const p = pts.length ? pts[0][pts[0].length - 1] : s[0];
359
904
  return toVec(p);
360
905
  }
@@ -364,7 +909,7 @@ function pointAlong(segs, fromStart, dist3) {
364
909
  }
365
910
  function vectorStrokeParts(path, style) {
366
911
  const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
367
- const contours = _chunkBGNPT5PEcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
912
+ const contours = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
368
913
  const closedShape = contours.length > 0 && contours.every((c) => c.closed);
369
914
  const align = effectiveStrokeAlign(style, closedShape);
370
915
  const drawWidth = align === "center" ? width : width * 2;
@@ -404,42 +949,54 @@ function vectorStrokeParts(path, style) {
404
949
  if (!s0 && !s1) return c;
405
950
  const lengths = c.segments.map(segLen);
406
951
  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) : [] };
952
+ return { closed: false, segments: total - s1 > s0 ? _chunkGVB7ZPLZcjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
408
953
  });
409
- return { align, line: _chunkBGNPT5PEcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
954
+ return { align, line: _chunkGVB7ZPLZcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
410
955
  }
411
956
  function vectorStrokeOutlinePath(path, style) {
412
957
  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
- });
958
+ const stroke = vectorStrokeLineArea(parts, style);
419
959
  const fill = { path, fillRule: _nullishCoalesce(style.fillRule, () => ( "nonzero")) };
420
960
  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;
961
+ if (parts.align === "inside") area = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "intersection", [stroke, fill]).path;
962
+ else if (parts.align === "outside") area = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "difference", [stroke, fill]).path;
423
963
  if (!parts.heads.length) return area;
424
- return _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
964
+ return _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
425
965
  }
426
966
  function hitTestVectorStroke(path, p, style, tolerance = 0) {
427
967
  const parts = vectorStrokeParts(path, style);
428
968
  const point = [p.x, p.y];
429
969
  const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
430
970
  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;
971
+ if (_chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
432
972
  }
433
973
  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;
974
+ const profile = strokeWidthProfileOf(style);
975
+ let d;
976
+ let reach = width / 2;
977
+ if (profile) {
978
+ const near2 = nearestStrokePosition(parts.line, point);
979
+ if (!near2) return false;
980
+ d = near2.distance;
981
+ const w = widthProfileAt(profile, near2.t);
982
+ reach = width / 2 * (near2.side === "left" ? w.left : w.right);
983
+ if (parts.align !== "center") reach *= 2;
984
+ const atEnd = near2.t <= 1e-9 || near2.t >= 1 - 1e-9;
985
+ if (atEnd && (style.strokeLinecap === "round" || style.strokeLinecap === "square") && d > reach) {
986
+ reach = Math.max(reach, width / 2 * (w.left + w.right) / 2);
987
+ }
988
+ } else {
989
+ d = _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, parts.line, point);
990
+ if (parts.align !== "center") reach = width;
991
+ }
992
+ if (parts.align === "center") return d <= reach + tolerance;
993
+ if (d > reach + tolerance) return false;
437
994
  if (d <= tolerance) return true;
438
- const inside = _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
995
+ const inside = _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
439
996
  return parts.align === "inside" ? inside : !inside;
440
997
  }
441
998
  function vectorStrokeReach(style) {
442
- const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
999
+ const width = (_nullishCoalesce(style.strokeWidth, () => ( 1))) * widthProfileMaxScale(strokeWidthProfileOf(style));
443
1000
  const align = _nullishCoalesce(style.strokeAlign, () => ( "center"));
444
1001
  let reach = align === "center" ? width / 2 : align === "outside" ? width : 0;
445
1002
  if (style.strokeLinejoin !== "round" && style.strokeLinejoin !== "bevel") reach *= 4;
@@ -511,7 +1068,7 @@ function vectorNodeToBezier(node) {
511
1068
  }
512
1069
  function vectorPathData(input, precision = 3) {
513
1070
  const subpaths = Array.isArray(input) ? input : input.subpaths;
514
- return _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
1071
+ return _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
515
1072
  }
516
1073
  function inferAnchorType(a) {
517
1074
  if (isOnAnchor(a, a.in) || isOnAnchor(a, a.out)) return "corner";
@@ -527,7 +1084,7 @@ function inferAnchorType(a) {
527
1084
  return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
528
1085
  }
529
1086
  function bezierToSubpaths(input) {
530
- const path = _chunkBGNPT5PEcjs.toBezierPath.call(void 0, input);
1087
+ const path = _chunkGVB7ZPLZcjs.toBezierPath.call(void 0, input);
531
1088
  const out = [];
532
1089
  let current = null;
533
1090
  const last = () => current.anchors[current.anchors.length - 1];
@@ -624,14 +1181,14 @@ function nodeMatrix(node) {
624
1181
  function vectorPathWorldBezier(node, parent = IDENTITY_MATRIX) {
625
1182
  const m = multiplyMatrices(nodeMatrix(node), parent);
626
1183
  const path = subpathsToBezier(node.subpaths);
627
- return isIdentityMatrix(m) ? path : _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, path, m);
1184
+ return isIdentityMatrix(m) ? path : _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, path, m);
628
1185
  }
629
1186
  function vectorNodeBounds(node, parent = IDENTITY_MATRIX) {
630
1187
  if (node.type === "group") {
631
1188
  const m = multiplyMatrices(nodeMatrix(node), parent);
632
1189
  return unionBounds(node.children.map((c) => vectorNodeBounds(c, m)));
633
1190
  }
634
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
1191
+ const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
635
1192
  if (!b) return null;
636
1193
  return { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY };
637
1194
  }
@@ -665,9 +1222,9 @@ function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX) {
665
1222
  const path = vectorPathWorldBezier(node, parent);
666
1223
  if (path.length === 0) return false;
667
1224
  const point = [p.x, p.y];
668
- if (hasPaint(node.fill) && _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
1225
+ if (hasPaint(node.fill) && _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
669
1226
  if (hasPaint(node.stroke)) return hitTestVectorStroke(path, p, node, tolerance);
670
- return _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
1227
+ return _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
671
1228
  }
672
1229
  function vectorStrokeOutline(node, parent = IDENTITY_MATRIX) {
673
1230
  if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return [];
@@ -687,9 +1244,9 @@ function nearestSegment(node, p) {
687
1244
  const count = subpathSegmentCount(sp);
688
1245
  for (let i = 0; i < count; i++) {
689
1246
  const seg = subpathSegment(sp, i);
690
- const hit = _chunkBGNPT5PEcjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
1247
+ const hit = _chunkGVB7ZPLZcjs.segmentNearest.call(void 0, seg, [p.x, p.y]);
691
1248
  if (!best || hit.distance < best.distance) {
692
- const pt2 = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, hit.t);
1249
+ const pt2 = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, hit.t);
693
1250
  best = { subpath: s, segment: i, t: hit.t, point: { x: pt2[0], y: pt2[1] }, distance: hit.distance };
694
1251
  }
695
1252
  }
@@ -749,8 +1306,10 @@ function bakeIntoGeometry(node, m) {
749
1306
  if (isIdentityMatrix(m)) return node;
750
1307
  if (node.type === "group") return { ...node, children: node.children.map((c) => transformVectorNode(c, m)) };
751
1308
  const path = node;
1309
+ const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _28 => _28.strokeWidthProfile, 'optionalAccess', _29 => _29.length]);
752
1310
  return {
753
1311
  ...path,
1312
+ ...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
754
1313
  subpaths: path.subpaths.map((sp) => ({
755
1314
  closed: sp.closed,
756
1315
  anchors: sp.anchors.map((a) => {
@@ -861,7 +1420,7 @@ function insertAnchor(sp, segment, t) {
861
1420
  const ia = segment;
862
1421
  const ib = (segment + 1) % n;
863
1422
  const seg = subpathSegment(sp, segment);
864
- const [left, right] = _chunkBGNPT5PEcjs.splitSegment.call(void 0, seg, t);
1423
+ const [left, right] = _chunkGVB7ZPLZcjs.splitSegment.call(void 0, seg, t);
865
1424
  const mid = left[left.length - 1];
866
1425
  const anchor = { x: mid[0], y: mid[1] };
867
1426
  if (seg.length === 4) {
@@ -957,7 +1516,7 @@ function vectorCornersAttribute(subpaths, precision = 3) {
957
1516
  subpaths.forEach(
958
1517
  (sp, s) => sp.anchors.forEach((a, i) => {
959
1518
  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}` : ""}`);
1519
+ out.push(`${s}.${i}:${_chunkGVB7ZPLZcjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
961
1520
  })
962
1521
  );
963
1522
  return out.join(" ");
@@ -966,7 +1525,7 @@ function applyVectorCornersAttribute(subpaths, value) {
966
1525
  for (const item of value.trim().split(/\s+/)) {
967
1526
  const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
968
1527
  if (!m) continue;
969
- const a = _optionalChain([subpaths, 'access', _9 => _9[+m[1]], 'optionalAccess', _10 => _10.anchors, 'access', _11 => _11[+m[2]]]);
1528
+ const a = _optionalChain([subpaths, 'access', _30 => _30[+m[1]], 'optionalAccess', _31 => _31.anchors, 'access', _32 => _32[+m[2]]]);
970
1529
  const radius = parseFloat(m[3]);
971
1530
  if (!a || !(radius > 0)) continue;
972
1531
  const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
@@ -978,21 +1537,49 @@ var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;")
978
1537
  function vectorPaintToSvg(paint, gradientId) {
979
1538
  if (!hasPaint(paint)) return { value: "none", gradient: null };
980
1539
  if (typeof paint === "string") return { value: paint, gradient: null };
981
- return { value: `url(#${gradientId})`, gradient: _chunkBGNPT5PEcjs.gradientToSvgDef.call(void 0, paint, gradientId) };
1540
+ return { value: `url(#${gradientId})`, gradient: _chunkGVB7ZPLZcjs.gradientToSvgDef.call(void 0, paint, gradientId) };
982
1541
  }
1542
+ var partsCache = /* @__PURE__ */ new WeakMap();
983
1543
  function vectorStrokeSvgParts(node, clipId, precision = 3) {
984
1544
  if (!hasPaint(node.stroke) || !hasStrokeExtras(node)) return void 0;
1545
+ const hit = partsCache.get(node);
1546
+ if (hit && hit.clipId === clipId && hit.precision === precision) return hit.parts;
1547
+ const parts = computeStrokeSvgParts(node, clipId, precision);
1548
+ partsCache.set(node, { clipId, precision, parts });
1549
+ return parts;
1550
+ }
1551
+ function computeStrokeSvgParts(node, clipId, precision) {
985
1552
  const path = subpathsToBezier(node.subpaths);
986
1553
  const parts = vectorStrokeParts(path, node);
987
- if (parts.align === "center" && !parts.heads.length) return void 0;
1554
+ const profiled = !!strokeWidthProfileOf(node);
1555
+ if (parts.align === "center" && !parts.heads.length && !profiled) return void 0;
988
1556
  let clip = null;
989
1557
  if (parts.align !== "center") {
990
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, path);
1558
+ const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, path);
991
1559
  const pad = vectorStrokeReach(node) + 1;
992
1560
  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
1561
  clip = { kind: parts.align === "inside" ? "clip" : "mask", id: clipId, bounds };
994
1562
  }
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 })) };
1563
+ return {
1564
+ align: parts.align,
1565
+ d: _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, parts.line, { precision }),
1566
+ width: parts.drawWidth,
1567
+ ...profiled && (_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0 ? { outline: _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, vectorStrokeLineArea(parts, node), { precision }) } : {},
1568
+ clip,
1569
+ heads: parts.heads.map((h) => _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, h.path, { precision }))
1570
+ };
1571
+ }
1572
+ function widthProfileToAttribute(points, precision = 4) {
1573
+ return points.map((p) => [p.t, p.left, p.right].map((n) => _chunkGVB7ZPLZcjs.formatNumber.call(void 0, n, precision)).join(",")).join(";");
1574
+ }
1575
+ function widthProfileFromAttribute(value) {
1576
+ if (!value) return [];
1577
+ return sanitizeWidthProfile(
1578
+ value.split(";").map((part) => {
1579
+ const [t, left, right] = part.split(",").map((n) => parseFloat(n));
1580
+ return { t, left, right: Number.isFinite(right) ? right : left };
1581
+ })
1582
+ );
996
1583
  }
997
1584
  var round2 = (n) => Math.round(n * 100) / 100;
998
1585
  function gradientMarkup(def) {
@@ -1007,11 +1594,11 @@ function vectorDocumentToSvg(doc, options = {}) {
1007
1594
  const pretty = options.pretty !== false;
1008
1595
  const defs = [];
1009
1596
  const lines = [];
1010
- const num = (n) => _chunkBGNPT5PEcjs.formatNumber.call(void 0, n, precision);
1597
+ const num = (n) => _chunkGVB7ZPLZcjs.formatNumber.call(void 0, n, precision);
1011
1598
  const indent = (depth) => pretty ? " ".repeat(depth) : "";
1012
1599
  const common = (node) => {
1013
1600
  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(" ")})"`;
1601
+ if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunkGVB7ZPLZcjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
1015
1602
  if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
1016
1603
  if (node.visible === false) out2 += ` display="none"`;
1017
1604
  return out2;
@@ -1030,13 +1617,13 @@ function vectorDocumentToSvg(doc, options = {}) {
1030
1617
  if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
1031
1618
  const d = vectorPathData(node, precision);
1032
1619
  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}"` : "";
1620
+ const live = corners2 ? ` data-vector-d="${_chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
1034
1621
  const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
1035
1622
  const lineAttrs = (width) => {
1036
1623
  let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
1037
1624
  if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
1038
1625
  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(" ")}"`;
1626
+ if (_optionalChain([node, 'access', _33 => _33.strokeDasharray, 'optionalAccess', _34 => _34.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1040
1627
  return out2;
1041
1628
  };
1042
1629
  const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
@@ -1051,10 +1638,17 @@ function vectorDocumentToSvg(doc, options = {}) {
1051
1638
  if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
1052
1639
  if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num(node.startArrowScale)}"`;
1053
1640
  if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num(node.endArrowScale)}"`;
1641
+ const profile = strokeWidthProfileOf(node);
1642
+ if (profile) {
1643
+ data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
1644
+ if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
1645
+ if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
1646
+ if (_optionalChain([node, 'access', _35 => _35.strokeDasharray, 'optionalAccess', _36 => _36.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1647
+ }
1054
1648
  lines.push(`${indent(depth)}<g${common(node)}${data}>`);
1055
1649
  lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
1056
1650
  if (options.strokes === "outline") {
1057
- const area = _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1651
+ const area = _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1058
1652
  lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${area}" fill="${escapeAttr(stroke.value)}"/>`);
1059
1653
  } else {
1060
1654
  let clip = "";
@@ -1071,7 +1665,8 @@ function vectorDocumentToSvg(doc, options = {}) {
1071
1665
  clip = ` mask="url(#${id})"`;
1072
1666
  }
1073
1667
  }
1074
- lines.push(`${indent(depth + 1)}<path data-vector-part="stroke" d="${parts.d}" fill="none"${lineAttrs(parts.width)}${clip}/>`);
1668
+ if (parts.outline !== void 0) lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${parts.outline}" fill="${escapeAttr(stroke.value)}"${clip}/>`);
1669
+ else lines.push(`${indent(depth + 1)}<path data-vector-part="stroke" d="${parts.d}" fill="none"${lineAttrs(parts.width)}${clip}/>`);
1075
1670
  for (const h2 of parts.heads) lines.push(`${indent(depth + 1)}<path data-vector-part="head" d="${h2}" fill="${escapeAttr(stroke.value)}"/>`);
1076
1671
  }
1077
1672
  lines.push(`${indent(depth)}</g>`);
@@ -1217,7 +1812,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
1217
1812
  seen.add(id);
1218
1813
  let stops = readStops(el);
1219
1814
  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]), () => ( []));
1815
+ if (!stops.length && _optionalChain([href, 'optionalAccess', _37 => _37.startsWith, 'call', _38 => _38("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _39 => _39(href.slice(1), raw, seen), 'optionalAccess', _40 => _40.stops]), () => ( []));
1221
1816
  if (!stops.length) return null;
1222
1817
  if (el.name === "radialGradient") {
1223
1818
  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 +1916,7 @@ function parseVectorSvg(svg, options = {}) {
1321
1916
  const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
1322
1917
  const fillEl = part("fill");
1323
1918
  const subpaths = fillEl ? shapeOf(fillEl) : null;
1324
- if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _18 => _18.length])) return null;
1919
+ if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _41 => _41.length])) return null;
1325
1920
  const a = g.attrs;
1326
1921
  const fs = readStyle(fillEl, style);
1327
1922
  const strokeEl = part("stroke");
@@ -1334,6 +1929,11 @@ function parseVectorSvg(svg, options = {}) {
1334
1929
  return n > 0 ? n : void 0;
1335
1930
  };
1336
1931
  const align = a["data-vector-stroke-align"];
1932
+ const profile = widthProfileFromAttribute(a["data-vector-width-profile"]);
1933
+ const kept = (key, values) => values.includes(a[key]) ? a[key] : void 0;
1934
+ const keptCap = kept("data-vector-stroke-linecap", ["round", "square"]);
1935
+ const keptJoin = kept("data-vector-stroke-linejoin", ["round", "bevel"]);
1936
+ const keptDash = a["data-vector-stroke-dasharray"] ? numberList(a["data-vector-stroke-dasharray"]) : void 0;
1337
1937
  const node = {
1338
1938
  id: idFor(g),
1339
1939
  type: "path",
@@ -1349,7 +1949,11 @@ function parseVectorSvg(svg, options = {}) {
1349
1949
  ...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
1350
1950
  ...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
1351
1951
  ...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"]) } : {}
1952
+ ...scale(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale(a["data-vector-end-arrow-scale"]) } : {},
1953
+ ...profile.length ? { strokeWidthProfile: profile } : {},
1954
+ ...keptCap ? { strokeLinecap: keptCap } : {},
1955
+ ...keptJoin ? { strokeLinejoin: keptJoin } : {},
1956
+ ..._optionalChain([keptDash, 'optionalAccess', _42 => _42.length]) ? { strokeDasharray: keptDash } : {}
1353
1957
  };
1354
1958
  return placed(node, m);
1355
1959
  };
@@ -1403,7 +2007,7 @@ function parseVectorSvg(svg, options = {}) {
1403
2007
  // src/vector/snap.ts
1404
2008
  var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
1405
2009
  function constrainToLeave(leave, p) {
1406
- if (!_optionalChain([leave, 'optionalAccess', _19 => _19.constrain])) return p;
2010
+ if (!_optionalChain([leave, 'optionalAccess', _43 => _43.constrain])) return p;
1407
2011
  const dx = p.x - leave.at.x;
1408
2012
  const dy = p.y - leave.at.y;
1409
2013
  switch (leave.constrain) {
@@ -1514,7 +2118,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1514
2118
  moving.set(r.nodeId, set);
1515
2119
  }
1516
2120
  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);
2121
+ t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _44 => _44.get, 'call', _45 => _45(id), 'optionalAccess', _46 => _46.has, 'call', _47 => _47(`${s}:${i}`)]), true);
1518
2122
  return t.index();
1519
2123
  }
1520
2124
  /**
@@ -1632,7 +2236,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1632
2236
  }
1633
2237
  const b = vectorNodeBounds(n, parent);
1634
2238
  const f = nodeFrame(n, parent, b);
1635
- if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _24 => _24.centre]), () => ( null)));
2239
+ if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _48 => _48.centre]), () => ( null)));
1636
2240
  }
1637
2241
  };
1638
2242
  visit(doc.nodes, IDENTITY_MATRIX);
@@ -1669,7 +2273,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1669
2273
  */
1670
2274
  snapFrom(p, leave, tol) {
1671
2275
  const none = (q) => ({ point: q, kind: null, x: false, y: false, lines: [], labels: [] });
1672
- const at = (q, kind, lines2 = []) => ({
2276
+ const at2 = (q, kind, lines2 = []) => ({
1673
2277
  point: q,
1674
2278
  kind,
1675
2279
  x: true,
@@ -1677,23 +2281,23 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1677
2281
  lines: lines2,
1678
2282
  labels: [this.label(q, kind)]
1679
2283
  });
1680
- if (_optionalChain([leave, 'optionalAccess', _25 => _25.constrain])) {
2284
+ if (_optionalChain([leave, 'optionalAccess', _49 => _49.constrain])) {
1681
2285
  const q = constrainToLeave(leave, p);
1682
2286
  const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
1683
2287
  return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
1684
2288
  }
1685
2289
  const point = this.nearestPoint(p, tol);
1686
- if (point) return at(point.p, point.kind);
2290
+ if (point) return at2(point.p, point.kind);
1687
2291
  const near2 = this.segmentsNear(p, tol);
1688
- const crossing = this.crossing(near2, p, tol);
1689
- if (crossing) return at(crossing, "intersect");
2292
+ const crossing2 = this.crossing(near2, p, tol);
2293
+ if (crossing2) return at2(crossing2, "intersect");
1690
2294
  if (leave && leave.curves !== false) {
1691
2295
  const foot = this.curvePoint(near2, leave.at, p, tol);
1692
- if (foot) return at(foot.point, foot.kind, [line("construction", leave.at, foot.point)]);
2296
+ if (foot) return at2(foot.point, foot.kind, [line("construction", leave.at, foot.point)]);
1693
2297
  }
1694
2298
  const on = this.onSegment(near2);
1695
- if (on) return at(on, "path");
1696
- if (_optionalChain([leave, 'optionalAccess', _26 => _26.angles, 'optionalAccess', _27 => _27.length])) {
2299
+ if (on) return at2(on, "path");
2300
+ if (_optionalChain([leave, 'optionalAccess', _50 => _50.angles, 'optionalAccess', _51 => _51.length])) {
1697
2301
  const c = construction(leave, p, tol);
1698
2302
  if (c) {
1699
2303
  const hit = this.slide(leave.at, c.dir, c.point, tol, false);
@@ -1739,7 +2343,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1739
2343
  * the nearest target line crosses it, at most twice `tol` away: where it lands and the
1740
2344
  * target point that line comes from.
1741
2345
  */
1742
- slide(at, dir, q, tol, ray) {
2346
+ slide(at2, dir, q, tol, ray) {
1743
2347
  const len2 = dir.x * dir.x + dir.y * dir.y;
1744
2348
  if (!Number.isFinite(len2) || len2 <= 1e-18) return null;
1745
2349
  const reach = 2 * tol;
@@ -1747,10 +2351,10 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1747
2351
  for (const vertical of [true, false]) {
1748
2352
  const d = vertical ? dir.x : dir.y;
1749
2353
  if (Math.abs(d) <= 1e-9) continue;
1750
- const a = vertical ? at.x : at.y;
2354
+ const a = vertical ? at2.x : at2.y;
1751
2355
  for (const l of linesNear(vertical ? this.xs : this.ys, vertical ? q.x : q.y, reach)) {
1752
2356
  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 };
2357
+ const hit = vertical ? { x: l.v, y: at2.y + dir.y * s } : { x: at2.x + dir.x * s, y: l.v };
1754
2358
  const moved = distance(hit, q);
1755
2359
  if (moved <= reach && (!ray || s > 0) && (!best || moved < best.moved)) best = { moved, point: hit, from: l.from };
1756
2360
  }
@@ -1769,13 +2373,13 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1769
2373
  const pt2 = toPoint(p);
1770
2374
  for (const s of this.segments) {
1771
2375
  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);
2376
+ const hit = _chunkGVB7ZPLZcjs.segmentNearest.call(void 0, s.seg, pt2);
1773
2377
  if (hit.distance <= tol) out.push({ seg: s.seg, t: hit.t, distance: hit.distance });
1774
2378
  }
1775
2379
  return out.sort((a, b) => a.distance - b.distance);
1776
2380
  }
1777
2381
  onSegment(near2) {
1778
- return near2.length ? toVec2(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
2382
+ return near2.length ? toVec2(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, near2[0].seg, near2[0].t)) : null;
1779
2383
  }
1780
2384
  /** Where two of the target segments near `p` cross, the crossing nearest `p` within `tol`. */
1781
2385
  crossing(near2, p, tol) {
@@ -1784,7 +2388,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1784
2388
  let bestD = Infinity;
1785
2389
  for (let i = 0; i < list.length; i++) {
1786
2390
  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)) {
2391
+ for (const hit of _chunkGVB7ZPLZcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
1788
2392
  const q = toVec2(hit.point);
1789
2393
  const d = distance(q, p);
1790
2394
  if (d <= tol && d < bestD) {
@@ -1823,7 +2427,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1823
2427
  * with nothing spaces the box evenly in its row or column. Returns the correction and guides.
1824
2428
  */
1825
2429
  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 };
2430
+ const axes = { x: _optionalChain([options, 'access', _52 => _52.axes, 'optionalAccess', _53 => _53.x]) !== false, y: _optionalChain([options, 'access', _54 => _54.axes, 'optionalAccess', _55 => _55.y]) !== false };
1827
2431
  const nearest = (lines, values) => {
1828
2432
  let best = null;
1829
2433
  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 +2435,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1831
2435
  };
1832
2436
  const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
1833
2437
  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: [] };
2438
+ const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _56 => _56.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _57 => _57.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
1835
2439
  const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
1836
2440
  if (bx) {
1837
2441
  const x = bx.from.x;
@@ -1898,11 +2502,11 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1898
2502
  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
2503
  if (!pick) return null;
1900
2504
  const { start, repeats } = pick;
1901
- const at = horizontal ? { ...r, x: start } : { ...r, y: start };
2505
+ const at2 = horizontal ? { ...r, x: start } : { ...r, y: start };
1902
2506
  const end = start + len2;
1903
2507
  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)]);
2508
+ if (before && (repeats === null || Math.abs(start - before.a1 - repeats) <= EQUAL)) gaps.push([before.a1, start, mid(before.b, at2)]);
2509
+ if (after && (repeats === null || Math.abs(after.a0 - end - repeats) <= EQUAL)) gaps.push([end, after.a0, mid(after.b, at2)]);
1906
2510
  if (repeats !== null) {
1907
2511
  for (const p of pairs) if (Math.abs(p.g - repeats) <= EQUAL) gaps.push(p.gap);
1908
2512
  }
@@ -1954,9 +2558,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1954
2558
  const on = this.nearestPoint(p, tol);
1955
2559
  const tries = [];
1956
2560
  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 } });
2561
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _58 => _58.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _59 => _59.v]), () => ( null)), s: { hx, hy, on: null } });
2562
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _60 => _60.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
2563
+ tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _61 => _61.v]), () => ( null)), s: { hx: null, hy, on: null } });
1960
2564
  for (const { tx, ty, s } of tries) {
1961
2565
  if (tx === null && ty === null) continue;
1962
2566
  const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
@@ -2106,15 +2710,15 @@ var crossCondition = (r, d) => r[0] * d[1] - r[1] * d[0];
2106
2710
  function solveOnSegment(seg, base, cond) {
2107
2711
  const b = [base.x, base.y];
2108
2712
  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));
2713
+ const p = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
2714
+ return cond([p[0] - b[0], p[1] - b[1]], _chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, seg, t));
2111
2715
  };
2112
2716
  const vals = [];
2113
2717
  let scale = 0;
2114
2718
  for (let i = 0; i <= CURVE_SAMPLES; i++) {
2115
2719
  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);
2720
+ const p = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
2721
+ const d = _chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, seg, t);
2118
2722
  const r = [p[0] - b[0], p[1] - b[1]];
2119
2723
  vals.push(cond(r, d));
2120
2724
  scale = Math.max(scale, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
@@ -2141,8 +2745,8 @@ function solveOnSegment(seg, base, cond) {
2141
2745
  } else hi = m;
2142
2746
  }
2143
2747
  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);
2748
+ const p = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
2749
+ const dv = _chunkGVB7ZPLZcjs.segmentDerivative.call(void 0, seg, t);
2146
2750
  const r = Math.hypot(p[0] - b[0], p[1] - b[1]);
2147
2751
  const dl = Math.hypot(dv[0], dv[1]);
2148
2752
  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 +2783,7 @@ function endTangent(c) {
2179
2783
  return [1, 0];
2180
2784
  }
2181
2785
  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));
2786
+ for (let i = withStart ? 0 : 1; i <= n; i++) out.push(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, c, i / n));
2183
2787
  }
2184
2788
  var onAnchor2 = (a, h) => !h || Math.abs(h.x - a.x) < EPS3 && Math.abs(h.y - a.y) < EPS3;
2185
2789
  function tidy(a, keepType = true) {
@@ -2225,7 +2829,7 @@ function fitAcross(pts, N, t0, t1) {
2225
2829
  const u = [];
2226
2830
  for (let i = 0; i <= N; i++) u.push(k * i / N);
2227
2831
  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);
2832
+ return _chunkGVB7ZPLZcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
2229
2833
  };
2230
2834
  return goldenMin(fit, 0, 1);
2231
2835
  }
@@ -2285,7 +2889,7 @@ function bendSegment(sp, segment, t, p) {
2285
2889
  return { anchors, closed: sp.closed };
2286
2890
  }
2287
2891
  function subpathPointAt(sp, segment, t) {
2288
- const [x, y] = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, subpathSegment(sp, segment), t);
2892
+ const [x, y] = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, subpathSegment(sp, segment), t);
2289
2893
  return { x, y };
2290
2894
  }
2291
2895
  function curvatureMoveAnchor(sp, index, q) {
@@ -2331,9 +2935,9 @@ function curvatureMoveAnchor(sp, index, q) {
2331
2935
  anchors[index] = moved;
2332
2936
  return { anchors, closed: sp.closed };
2333
2937
  }
2334
- function cutSubpathAt(sp, at) {
2938
+ function cutSubpathAt(sp, at2) {
2335
2939
  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);
2940
+ 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
2941
  if (!cuts.length || n < 2) return [{ anchors: sp.anchors.map(cloneAnchor), closed: sp.closed }];
2338
2942
  const first = cuts[0];
2339
2943
  const run = sp.closed ? Array.from({ length: n + 1 }, (_, i) => sp.anchors[(first + i) % n]) : sp.anchors;
@@ -2364,7 +2968,7 @@ function splitSubpathAt(sp, segment, t, endTolerance = 1e-6) {
2364
2968
  return cutSubpathAt(subpath, [index]);
2365
2969
  }
2366
2970
  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);
2971
+ var dist2 = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
2368
2972
  function reverse(sp) {
2369
2973
  return {
2370
2974
  closed: sp.closed,
@@ -2391,7 +2995,7 @@ function joinSubpaths(subpaths, tolerance) {
2391
2995
  const sp = open[0];
2392
2996
  const first = sp.anchors[0];
2393
2997
  const last = sp.anchors[sp.anchors.length - 1];
2394
- if (sp.anchors.length > 2 && dist(first, last) <= tolerance) {
2998
+ if (sp.anchors.length > 2 && dist2(first, last) <= tolerance) {
2395
2999
  sp.anchors.pop();
2396
3000
  sp.anchors[0] = tidy({ ...first, in: _nullishCoalesce(last.in, () => ( null)) }, false);
2397
3001
  } else {
@@ -2407,7 +3011,7 @@ function joinSubpaths(subpaths, tolerance) {
2407
3011
  for (let j = i + 1; j < open.length; j++) {
2408
3012
  for (const ei of [false, true]) {
2409
3013
  for (const ej of [false, true]) {
2410
- const d = dist(endOf2(open[i], ei), endOf2(open[j], ej));
3014
+ const d = dist2(endOf2(open[i], ei), endOf2(open[j], ej));
2411
3015
  if (d < best.d) best = { d, i, j, ei, ej };
2412
3016
  }
2413
3017
  }
@@ -2447,8 +3051,8 @@ function smoothSubpath(sp, amount = 1, only) {
2447
3051
  const p = orig[i];
2448
3052
  const dir = unit2(next.x - prev.x, next.y - prev.y);
2449
3053
  if (!dir) continue;
2450
- const li = dist(p, prev) / 3;
2451
- const lo = dist(p, next) / 3;
3054
+ const li = dist2(p, prev) / 3;
3055
+ const lo = dist2(p, next) / 3;
2452
3056
  const hin = _nullishCoalesce(p.in, () => ( p));
2453
3057
  const hout = _nullishCoalesce(p.out, () => ( p));
2454
3058
  const target = (h, x, y) => ({ x: h.x + (x - h.x) * t, y: h.y + (y - h.y) * t });
@@ -2475,12 +3079,12 @@ function segsOf(sp) {
2475
3079
  function segsToSubpath(segs, closed, types) {
2476
3080
  if (!segs.length) return null;
2477
3081
  const anchors = [];
2478
- const at = (p) => ({ x: p[0], y: p[1] });
2479
- anchors.push(at(segs[0].c[0]));
3082
+ const at2 = (p) => ({ x: p[0], y: p[1] });
3083
+ anchors.push(at2(segs[0].c[0]));
2480
3084
  for (const s of segs) {
2481
3085
  const end = s.c[s.c.length - 1];
2482
3086
  if (!s.line) anchors[anchors.length - 1].out = { x: s.c[1][0], y: s.c[1][1] };
2483
- const next = at(end);
3087
+ const next = at2(end);
2484
3088
  if (!s.line) next.in = { x: s.c[2][0], y: s.c[2][1] };
2485
3089
  anchors.push(next);
2486
3090
  }
@@ -2506,8 +3110,8 @@ function mergePair(a, b, tol) {
2506
3110
  if (len2 < 1e-12) return null;
2507
3111
  const mx = a3[0] - a0[0];
2508
3112
  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;
3113
+ const along2 = (mx * cx + my * cy) / len2;
3114
+ return Math.abs((mx * cy - my * cx) / len2) <= tol && along2 >= 0 && along2 <= len2 ? { c: [a0, b3], line: true } : null;
2511
3115
  }
2512
3116
  const ca = a.line ? [a0, a0, a3, a3] : a.c;
2513
3117
  const cb = b.line ? [a3, a3, b3, b3] : b.c;
@@ -2546,12 +3150,12 @@ function removeRedundantPoints(sp, tolerance) {
2546
3150
  }
2547
3151
 
2548
3152
  // src/vector/pathCut.ts
2549
- var loopArea = (l) => _chunkBGNPT5PEcjs.contourSignedArea.call(void 0, l.segs);
3153
+ var loopArea = (l) => _chunkGVB7ZPLZcjs.contourSignedArea.call(void 0, l.segs);
2550
3154
  function loopSample(l) {
2551
3155
  const k = l.chord.findIndex((c) => !c);
2552
- return _chunkBGNPT5PEcjs.segmentPoint.call(void 0, l.segs[k >= 0 ? k : 0], 0.5);
3156
+ return _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, l.segs[k >= 0 ? k : 0], 0.5);
2553
3157
  }
2554
- var contains2 = (l, p) => _chunkBGNPT5PEcjs.windingNumber.call(void 0, [l.segs], p) !== 0;
3158
+ var contains2 = (l, p) => _chunkGVB7ZPLZcjs.windingNumber.call(void 0, [l.segs], p) !== 0;
2555
3159
  function groupFaces(loops) {
2556
3160
  const usable = loops.filter((l) => l.segs.length && Math.abs(loopArea(l)) > 1e-12);
2557
3161
  if (!usable.length) return [];
@@ -2567,12 +3171,12 @@ function groupFaces(loops) {
2567
3171
  return outers.reverse().map((o) => faces.get(o));
2568
3172
  }
2569
3173
  function faceToPath(face) {
2570
- return _chunkBGNPT5PEcjs.contoursToPath.call(void 0, face.loops.map((l) => ({ segments: l.segs, closed: true })));
3174
+ return _chunkGVB7ZPLZcjs.contoursToPath.call(void 0, face.loops.map((l) => ({ segments: l.segs, closed: true })));
2571
3175
  }
2572
3176
  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());
3177
+ const normalized = _chunkGVB7ZPLZcjs.normalizeBezierPath.call(void 0, path, fillRule);
3178
+ const loops = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, normalized).map((c) => {
3179
+ const segs = _chunkGVB7ZPLZcjs.closeContour.call(void 0, c, true).map((s) => s.slice());
2576
3180
  return { segs, chord: segs.map(() => false) };
2577
3181
  });
2578
3182
  return groupFaces(loops);
@@ -2642,7 +3246,7 @@ function lineHits(p, q, seg) {
2642
3246
  const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
2643
3247
  const out = [];
2644
3248
  for (const t of rootsInUnit(...coeffs, scale)) {
2645
- const point = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
3249
+ const point = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
2646
3250
  const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
2647
3251
  if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
2648
3252
  }
@@ -2655,7 +3259,7 @@ function arc(l, a, b, eps) {
2655
3259
  const segs = [];
2656
3260
  const chord = [];
2657
3261
  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);
3262
+ const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : _chunkGVB7ZPLZcjs.subSegment.call(void 0, l.segs[k], t0, t1);
2659
3263
  if (!degenerate(piece, eps)) {
2660
3264
  segs.push(piece);
2661
3265
  chord.push(l.chord[k]);
@@ -2680,7 +3284,7 @@ function knifePath(path, fillRule, knife) {
2680
3284
  if (pts.length < 2) return null;
2681
3285
  let faces = facesOf(path, fillRule);
2682
3286
  if (!faces.length) return null;
2683
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, path);
3287
+ const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, path);
2684
3288
  const size = b ? Math.max(b.maxX - b.minX, b.maxY - b.minY, 1) : 1;
2685
3289
  const eps = size * 1e-9;
2686
3290
  let cursor = -Infinity;
@@ -2694,7 +3298,7 @@ function knifePath(path, fillRule, knife) {
2694
3298
  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
3299
  face2.loops.forEach(
2696
3300
  (l, li) => l.segs.forEach((seg, si) => {
2697
- const sb = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, seg);
3301
+ const sb = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, seg);
2698
3302
  if (sb.maxX < kb.minX - eps || sb.minX > kb.maxX + eps || sb.maxY < kb.minY - eps || sb.minY > kb.maxY + eps) return;
2699
3303
  for (const h of lineHits(pts[m], pts[m + 1], seg)) {
2700
3304
  const s = m + h.s;
@@ -2709,7 +3313,7 @@ function knifePath(path, fillRule, knife) {
2709
3313
  const m = Math.min(pts.length - 2, Math.floor(s));
2710
3314
  const u = s - m;
2711
3315
  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;
3316
+ return _chunkGVB7ZPLZcjs.windingNumber.call(void 0, face2.loops.map((l) => l.segs), q) !== 0;
2713
3317
  };
2714
3318
  for (let i = 0; i < unique.length - 1; i++) {
2715
3319
  const from2 = unique[i];
@@ -2769,33 +3373,33 @@ function eraserRegion(points, width) {
2769
3373
  ];
2770
3374
  }
2771
3375
  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" });
3376
+ return _chunkGVB7ZPLZcjs.outlineStroke.call(void 0, line2, { width, cap: "round", join: "round" });
2773
3377
  }
2774
3378
  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;
3379
+ const overlap = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "intersection", [{ path, fillRule }, { path: region }]).path;
3380
+ if (!_chunkGVB7ZPLZcjs.pathToContours.call(void 0, overlap).some((c) => Math.abs(_chunkGVB7ZPLZcjs.contourSignedArea.call(void 0, _chunkGVB7ZPLZcjs.closeContour.call(void 0, c, true))) > 1e-9)) return null;
3381
+ const rest = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "difference", [{ path, fillRule }, { path: region }]).path;
2778
3382
  return splitIntoFaces(rest);
2779
3383
  }
2780
3384
  function eraseFromOutline(sp, region) {
2781
- const regionSegs = _chunkBGNPT5PEcjs.pathToContours.call(void 0, region).flatMap((c) => _chunkBGNPT5PEcjs.closeContour.call(void 0, c, true));
3385
+ const regionSegs = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, region).flatMap((c) => _chunkGVB7ZPLZcjs.closeContour.call(void 0, c, true));
2782
3386
  if (!regionSegs.length) return null;
2783
- const rb = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, region);
3387
+ const rb = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, region);
2784
3388
  const count = subpathSegmentCount(sp);
2785
3389
  if (!rb || count === 0) return null;
2786
- const removed = (p) => _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
3390
+ const removed = (p) => _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
2787
3391
  const runs = [];
2788
3392
  let cur = [];
2789
3393
  let any = false;
2790
3394
  for (let i = 0; i < count; i++) {
2791
3395
  const seg = subpathSegment(sp, i);
2792
- const sb = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, seg);
3396
+ const sb = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, seg);
2793
3397
  const ts = [0, 1];
2794
3398
  if (!(sb.maxX < rb.minX || sb.minX > rb.maxX || sb.maxY < rb.minY || sb.minY > rb.maxY)) {
2795
3399
  for (const r of regionSegs) {
2796
- const b = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, r);
3400
+ const b = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, r);
2797
3401
  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);
3402
+ for (const hit of _chunkGVB7ZPLZcjs.segmentIntersections.call(void 0, seg, r)) if (hit.ta > 1e-9 && hit.ta < 1 - 1e-9) ts.push(hit.ta);
2799
3403
  }
2800
3404
  }
2801
3405
  ts.sort((a, b) => a - b);
@@ -2803,7 +3407,7 @@ function eraseFromOutline(sp, region) {
2803
3407
  const t0 = ts[k];
2804
3408
  const t1 = ts[k + 1];
2805
3409
  if (t1 - t0 < 1e-9) continue;
2806
- if (removed(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
3410
+ if (removed(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
2807
3411
  any = true;
2808
3412
  if (cur.length) runs.push(cur);
2809
3413
  cur = [];
@@ -2822,7 +3426,7 @@ function eraseFromOutline(sp, region) {
2822
3426
  for (const run of runs) {
2823
3427
  const anchors = [];
2824
3428
  for (const { seg, t0, t1 } of run) {
2825
- const c = _chunkBGNPT5PEcjs.subSegment.call(void 0, subpathSegment(sp, seg), t0, t1);
3429
+ const c = _chunkGVB7ZPLZcjs.subSegment.call(void 0, subpathSegment(sp, seg), t0, t1);
2826
3430
  const line2 = c.length === 2;
2827
3431
  const start = c[0];
2828
3432
  const end = c[c.length - 1];
@@ -2939,7 +3543,7 @@ function vectorInkShapeName(shape) {
2939
3543
  return shape.points.length === 3 ? "triangle" : "polygon";
2940
3544
  }
2941
3545
  }
2942
- var dist2 = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
3546
+ var dist3 = (a, b) => Math.hypot(b[0] - a[0], b[1] - a[1]);
2943
3547
  function bbox(pts) {
2944
3548
  if (!pts.length) return [0, 0, 0, 0];
2945
3549
  let [x0, y0] = pts[0];
@@ -2957,7 +3561,7 @@ function segDistance(a, b, p) {
2957
3561
  const dy = b[1] - a[1];
2958
3562
  const len2 = dx * dx + dy * dy;
2959
3563
  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);
3564
+ return dist3([a[0] + t * dx, a[1] + t * dy], p);
2961
3565
  }
2962
3566
  function polygonDistance(c, p) {
2963
3567
  let best = Infinity;
@@ -3019,7 +3623,7 @@ function corners(p, diag) {
3019
3623
  let far = 0;
3020
3624
  let farD = 0;
3021
3625
  p.forEach((q, i) => {
3022
- const d = dist2(start, q);
3626
+ const d = dist3(start, q);
3023
3627
  if (d > farD) {
3024
3628
  farD = d;
3025
3629
  far = i;
@@ -3046,7 +3650,7 @@ function corners(p, diag) {
3046
3650
  }
3047
3651
  let short = -1;
3048
3652
  for (let j = 0; j < n; j++) {
3049
- if (dist2(ring[j], ring[(j + 1) % n]) < 0.1 * diag) {
3653
+ if (dist3(ring[j], ring[(j + 1) % n]) < 0.1 * diag) {
3050
3654
  short = j;
3051
3655
  break;
3052
3656
  }
@@ -3086,8 +3690,8 @@ function recognizeInkShape(points, options = {}) {
3086
3690
  const diag = Math.hypot(w, h);
3087
3691
  if (size < minSize) return null;
3088
3692
  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);
3693
+ for (let i = 1; i < p.length; i++) length2 += dist3(p[i - 1], p[i]);
3694
+ const gap = dist3(first, last);
3091
3695
  if (gap > 0.85 * length2 && gap >= minSize) {
3092
3696
  const off = Math.max(...p.map((q) => segDistance(first, last, q)));
3093
3697
  if (off <= Math.max(0.06 * gap, 1.5 * minSize / 6)) return { kind: "line", a: first, b: last };
@@ -3113,7 +3717,14 @@ function recognizeInkShape(points, options = {}) {
3113
3717
 
3114
3718
  // src/vector/controller.ts
3115
3719
  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 };
3720
+ var DEFAULT_VECTOR_PATH_EDIT = {
3721
+ keepShapeOnDelete: true,
3722
+ curvatureMoves: false,
3723
+ pencilSmoothing: 0,
3724
+ recognizeShapes: false,
3725
+ eraserSize: 10,
3726
+ pencilPressure: "pen"
3727
+ };
3117
3728
  var VECTOR_PENCIL_STRING_PX = 100;
3118
3729
  var VECTOR_STYLE_KEYS = [
3119
3730
  "fill",
@@ -3128,7 +3739,8 @@ var VECTOR_STYLE_KEYS = [
3128
3739
  "endArrow",
3129
3740
  "arrowAlign",
3130
3741
  "startArrowScale",
3131
- "endArrowScale"
3742
+ "endArrowScale",
3743
+ "strokeWidthProfile"
3132
3744
  ];
3133
3745
  var VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
3134
3746
  var DEFAULT_DEFAULTS = {
@@ -3157,7 +3769,7 @@ function getEditablePathIds(state) {
3157
3769
  }
3158
3770
  }
3159
3771
  }
3160
- return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _38 => _38(state.document.nodes, id), 'optionalAccess', _39 => _39.type]) === "path");
3772
+ return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _62 => _62(state.document.nodes, id), 'optionalAccess', _63 => _63.type]) === "path");
3161
3773
  }
3162
3774
  function getVisibleAnchors(state) {
3163
3775
  const out = [];
@@ -3172,7 +3784,7 @@ function getVisibleHandles(state) {
3172
3784
  const seen = /* @__PURE__ */ new Set();
3173
3785
  const push = (node, s, i, handle) => {
3174
3786
  const sp = node.subpaths[s];
3175
- const a = _optionalChain([sp, 'optionalAccess', _40 => _40.anchors, 'access', _41 => _41[i]]);
3787
+ const a = _optionalChain([sp, 'optionalAccess', _64 => _64.anchors, 'access', _65 => _65[i]]);
3176
3788
  if (!a || !a[handle]) return;
3177
3789
  const key = `${node.id}:${s}:${i}:${handle}`;
3178
3790
  if (seen.has(key)) return;
@@ -3181,8 +3793,8 @@ function getVisibleHandles(state) {
3181
3793
  };
3182
3794
  if (state.tool === "pen" && state.pen) {
3183
3795
  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) {
3796
+ const sp = _optionalChain([node, 'optionalAccess', _66 => _66.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
3797
+ if (_optionalChain([node, 'optionalAccess', _67 => _67.type]) === "path" && sp) {
3186
3798
  const last = sp.anchors.length - 1;
3187
3799
  push(node, state.pen.subpath, last, "in");
3188
3800
  push(node, state.pen.subpath, last, "out");
@@ -3190,9 +3802,9 @@ function getVisibleHandles(state) {
3190
3802
  return out;
3191
3803
  }
3192
3804
  if (state.tool !== "direct") return out;
3193
- const editable = new Set(getEditablePathIds(state));
3805
+ const editable2 = new Set(getEditablePathIds(state));
3194
3806
  for (const ref of state.selectedAnchors) {
3195
- if (!editable.has(ref.nodeId)) continue;
3807
+ if (!editable2.has(ref.nodeId)) continue;
3196
3808
  const node = findVectorNode(state.document.nodes, ref.nodeId);
3197
3809
  const sp = node.subpaths[ref.subpath];
3198
3810
  if (!sp) continue;
@@ -3220,18 +3832,68 @@ function getVectorCornerWidgets(state) {
3220
3832
  const side = Math.min(c.lu, c.lv);
3221
3833
  if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
3222
3834
  const a = node.subpaths[c.subpath].anchors[c.index];
3223
- const radius = _nullishCoalesce(_optionalChain([a, 'access', _44 => _44.corner, 'optionalAccess', _45 => _45.radius]), () => ( 0));
3835
+ const radius = _nullishCoalesce(_optionalChain([a, 'access', _68 => _68.corner, 'optionalAccess', _69 => _69.radius]), () => ( 0));
3224
3836
  const drawn = fitCornerRadius(c, radius);
3225
3837
  const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
3226
3838
  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 });
3839
+ 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', _70 => _70.corner, 'optionalAccess', _71 => _71.kind]), () => ( "round")), corner: c });
3840
+ }
3841
+ }
3842
+ return out;
3843
+ }
3844
+ function vectorStrokeWidthGeometry(node) {
3845
+ if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return null;
3846
+ const parts = vectorStrokeParts(subpathsToBezier(node.subpaths), node);
3847
+ return parts.line.length ? { line: parts.line, unit: parts.drawWidth / 2 } : null;
3848
+ }
3849
+ function getVectorWidthHandles(state) {
3850
+ if (state.tool !== "width") return [];
3851
+ const ids = /* @__PURE__ */ new Set();
3852
+ for (const id of state.selection) {
3853
+ const n = findVectorNode(state.document.nodes, id);
3854
+ if (n) for (const p of collectPathNodes(n)) ids.add(p.id);
3855
+ }
3856
+ if (_optionalChain([state, 'access', _72 => _72.hover, 'optionalAccess', _73 => _73.nodeId])) ids.add(state.hover.nodeId);
3857
+ if (_optionalChain([state, 'access', _74 => _74.interaction, 'optionalAccess', _75 => _75.type]) === "width") ids.add(state.interaction.ref.nodeId);
3858
+ if (state.selectedWidthPoint) ids.add(state.selectedWidthPoint.nodeId);
3859
+ const out = [];
3860
+ for (const id of ids) {
3861
+ const node = findVectorNode(state.document.nodes, id);
3862
+ if (_optionalChain([node, 'optionalAccess', _76 => _76.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _77 => _77.strokeWidthProfile, 'optionalAccess', _78 => _78.length])) continue;
3863
+ const g = vectorStrokeWidthGeometry(node);
3864
+ if (!g) continue;
3865
+ const profile = sanitizeWidthProfile(node.strokeWidthProfile);
3866
+ const m = _nullishCoalesce(vectorWorldMatrix(state.document.nodes, id), () => ( IDENTITY_MATRIX));
3867
+ const contours = node.subpaths.length;
3868
+ for (let sp = 0; sp < contours; sp++) {
3869
+ profile.forEach((w, index) => {
3870
+ const at2 = strokePositionPoint(g.line, sp, w.t);
3871
+ if (!at2) return;
3872
+ const n = { x: at2.tangent[1], y: -at2.tangent[0] };
3873
+ const c = { x: at2.point[0], y: at2.point[1] };
3874
+ const p = applyMatrix(m, c);
3875
+ const l = applyMatrix(m, { x: c.x + n.x * w.left * g.unit, y: c.y + n.y * w.left * g.unit });
3876
+ const r = applyMatrix(m, { x: c.x - n.x * w.right * g.unit, y: c.y - n.y * w.right * g.unit });
3877
+ const sel = state.selectedWidthPoint;
3878
+ out.push({
3879
+ ref: { nodeId: id, index, subpath: sp },
3880
+ x: p.x,
3881
+ y: p.y,
3882
+ left: l,
3883
+ right: r,
3884
+ t: w.t,
3885
+ leftScale: w.left,
3886
+ rightScale: w.right,
3887
+ selected: !!sel && sel.nodeId === id && sel.index === index
3888
+ });
3889
+ });
3228
3890
  }
3229
3891
  }
3230
3892
  return out;
3231
3893
  }
3232
3894
  function getAnchor(doc, ref) {
3233
3895
  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;
3896
+ return _optionalChain([node, 'optionalAccess', _79 => _79.type]) === "path" ? _optionalChain([node, 'access', _80 => _80.subpaths, 'access', _81 => _81[ref.subpath], 'optionalAccess', _82 => _82.anchors, 'access', _83 => _83[ref.index]]) : void 0;
3235
3897
  }
3236
3898
  function getAnchorWorld(doc, ref) {
3237
3899
  const a = getAnchor(doc, ref);
@@ -3299,9 +3961,10 @@ var TOOL_LABELS = {
3299
3961
  line: "Line",
3300
3962
  scissors: "Scissors",
3301
3963
  knife: "Knife",
3302
- eraser: "Eraser"
3964
+ eraser: "Eraser",
3965
+ width: "Width"
3303
3966
  };
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" };
3967
+ 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", w: "width" };
3305
3968
  function vectorToolLabel(tool) {
3306
3969
  return TOOL_LABELS[tool];
3307
3970
  }
@@ -3317,7 +3980,8 @@ var VECTOR_TOOL_SHORTCUTS = {
3317
3980
  line: "L",
3318
3981
  scissors: "C",
3319
3982
  knife: "K",
3320
- eraser: "E"
3983
+ eraser: "E",
3984
+ width: "W"
3321
3985
  };
3322
3986
  var VECTOR_TOOL_GLYPHS = {
3323
3987
  select: "\u2196",
@@ -3331,7 +3995,8 @@ var VECTOR_TOOL_GLYPHS = {
3331
3995
  line: "\u2571",
3332
3996
  scissors: "\u2702",
3333
3997
  knife: "\u27CB",
3334
- eraser: "\u232B"
3998
+ eraser: "\u232B",
3999
+ width: "\u21F9"
3335
4000
  };
3336
4001
  var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
3337
4002
  var BOOLEAN_OPS = {
@@ -3385,35 +4050,36 @@ var VectorEditorController = (_class3 = class {
3385
4050
 
3386
4051
  __init9() {this.selection = []}
3387
4052
  __init10() {this.anchors = []}
3388
- __init11() {this.editingId = null}
3389
- __init12() {this.pen = null}
4053
+ __init11() {this.widthPoint = null}
4054
+ __init12() {this.editingId = null}
4055
+ __init13() {this.pen = null}
3390
4056
 
3391
4057
 
3392
4058
 
3393
4059
 
3394
4060
 
3395
4061
 
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}
4062
+ __init14() {this.hover = null}
4063
+ __init15() {this.pointer = null}
4064
+ __init16() {this.drag = null}
4065
+ __init17() {this.interaction = null}
4066
+ __init18() {this.guides = null}
3401
4067
  /** Whether the last `snapDrawn` landed on an object (guides may be hidden). */
3402
- __init18() {this.snappedToObject = false}
4068
+ __init19() {this.snappedToObject = false}
3403
4069
  /** 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}
4070
+ __init20() {this.hoverTargets = null}
4071
+ __init21() {this.spaceHeld = false}
4072
+ __init22() {this.clipboard = null}
3407
4073
 
3408
4074
 
3409
- __init22() {this.listeners = /* @__PURE__ */ new Set()}
3410
- __init23() {this.cleanups = []}
3411
- __init24() {this.applying = false}
4075
+ __init23() {this.listeners = /* @__PURE__ */ new Set()}
4076
+ __init24() {this.cleanups = []}
4077
+ __init25() {this.applying = false}
3412
4078
  /** 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}
4079
+ __init26() {this.committing = false}
4080
+ __init27() {this.revision = 0}
3415
4081
 
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);
4082
+ 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);
3417
4083
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
3418
4084
  this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
3419
4085
  this.tool = _nullishCoalesce(options.tool, () => ( "select"));
@@ -3442,9 +4108,9 @@ var VectorEditorController = (_class3 = class {
3442
4108
  // State
3443
4109
  // -------------------------------------------------------------------------
3444
4110
  /** The current snapshot (stable until the next change). */
3445
- __init27() {this.getState = () => this.state}
4111
+ __init28() {this.getState = () => this.state}
3446
4112
  /** Calls `listener` after every change. Returns an unsubscribe function. */
3447
- __init28() {this.subscribe = (listener) => {
4113
+ __init29() {this.subscribe = (listener) => {
3448
4114
  this.listeners.add(listener);
3449
4115
  return () => {
3450
4116
  this.listeners.delete(listener);
@@ -3463,6 +4129,7 @@ var VectorEditorController = (_class3 = class {
3463
4129
  tool: this.tool,
3464
4130
  selection: this.selection,
3465
4131
  selectedAnchors: this.anchors,
4132
+ selectedWidthPoint: this.widthPoint,
3466
4133
  editingId: this.editingId,
3467
4134
  selectionBounds: this.boundsOf(this.selection),
3468
4135
  selectionFrame: this.frameOf(this.selectedRoots()),
@@ -3487,7 +4154,7 @@ var VectorEditorController = (_class3 = class {
3487
4154
  notify(docChanged = false) {
3488
4155
  this.revision++;
3489
4156
  this.state = this.computeState();
3490
- if (docChanged) _optionalChain([this, 'access', _53 => _53.onChange, 'optionalCall', _54 => _54(this.doc)]);
4157
+ if (docChanged) _optionalChain([this, 'access', _84 => _84.onChange, 'optionalCall', _85 => _85(this.doc)]);
3491
4158
  const state = this.state;
3492
4159
  this.listeners.forEach((listener) => {
3493
4160
  try {
@@ -3517,20 +4184,24 @@ var VectorEditorController = (_class3 = class {
3517
4184
  return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
3518
4185
  }
3519
4186
  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";
4187
+ if (_optionalChain([this, 'access', _86 => _86.drag, 'optionalAccess', _87 => _87.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
4188
+ if (_optionalChain([this, 'access', _88 => _88.drag, 'optionalAccess', _89 => _89.kind]) === "rotate") return "grabbing";
4189
+ if (_optionalChain([this, 'access', _90 => _90.drag, 'optionalAccess', _91 => _91.kind]) === "move") return "move";
3523
4190
  const h = this.hover;
3524
4191
  switch (this.tool) {
3525
4192
  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";
4193
+ if (_optionalChain([h, 'optionalAccess', _92 => _92.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _93 => _93.frameOf, 'call', _94 => _94(this.selectedRoots()), 'optionalAccess', _95 => _95.angle]), () => ( 0)));
4194
+ return _optionalChain([h, 'optionalAccess', _96 => _96.nodeId]) ? "move" : "default";
3528
4195
  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";
4196
+ if (_optionalChain([h, 'optionalAccess', _97 => _97.anchor]) || _optionalChain([h, 'optionalAccess', _98 => _98.handle]) || _optionalChain([h, 'optionalAccess', _99 => _99.corner])) return "pointer";
4197
+ if (_optionalChain([h, 'optionalAccess', _100 => _100.segment])) return "copy";
4198
+ return _optionalChain([h, 'optionalAccess', _101 => _101.nodeId]) ? "move" : "default";
3532
4199
  case "pen":
3533
- return _optionalChain([h, 'optionalAccess', _71 => _71.anchor]) ? "pointer" : "crosshair";
4200
+ return _optionalChain([h, 'optionalAccess', _102 => _102.anchor]) ? "pointer" : "crosshair";
4201
+ case "width":
4202
+ if (_optionalChain([this, 'access', _103 => _103.drag, 'optionalAccess', _104 => _104.kind]) === "width") return this.drag.mode === "move" ? "grabbing" : "ew-resize";
4203
+ if (_optionalChain([h, 'optionalAccess', _105 => _105.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
4204
+ return _optionalChain([h, 'optionalAccess', _106 => _106.strokeSpot]) ? "copy" : "default";
3534
4205
  default:
3535
4206
  return "crosshair";
3536
4207
  }
@@ -3550,7 +4221,7 @@ var VectorEditorController = (_class3 = class {
3550
4221
  this.selection = s.selection.filter((id) => this.getNode(id));
3551
4222
  this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
3552
4223
  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;
4224
+ this.pen = s.pen && _optionalChain([this, 'access', _107 => _107.getNode, 'call', _108 => _108(s.pen.nodeId), 'optionalAccess', _109 => _109.subpaths, 'optionalAccess', _110 => _110[s.pen.subpath]]) ? s.pen : null;
3554
4225
  } finally {
3555
4226
  this.applying = false;
3556
4227
  }
@@ -3656,6 +4327,7 @@ var VectorEditorController = (_class3 = class {
3656
4327
  this.tool = tool;
3657
4328
  this.hover = null;
3658
4329
  this.guides = null;
4330
+ this.widthPoint = null;
3659
4331
  if (tool !== "direct") {
3660
4332
  this.anchors = [];
3661
4333
  this.editingId = null;
@@ -3714,7 +4386,7 @@ var VectorEditorController = (_class3 = class {
3714
4386
  if (res.kind) {
3715
4387
  this.snappedToObject = true;
3716
4388
  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) };
4389
+ return _optionalChain([leave, 'optionalAccess', _111 => _111.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
4390
  }
3719
4391
  }
3720
4392
  this.setGuides(null);
@@ -3732,14 +4404,14 @@ var VectorEditorController = (_class3 = class {
3732
4404
  /** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
3733
4405
  hoverSnap(p, input) {
3734
4406
  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])) {
4407
+ const sp = this.pen ? _optionalChain([this, 'access', _112 => _112.getNode, 'call', _113 => _113(this.pen.nodeId), 'optionalAccess', _114 => _114.subpaths, 'access', _115 => _115[this.pen.subpath]]) : void 0;
4408
+ if (this.pen && sp && _optionalChain([this, 'access', _116 => _116.hover, 'optionalAccess', _117 => _117.anchor])) {
3737
4409
  this.setGuides(null);
3738
4410
  return p;
3739
4411
  }
3740
4412
  const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
3741
4413
  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))));
4414
+ return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _118 => _118.pen, 'optionalAccess', _119 => _119.nodeId]), () => ( null))));
3743
4415
  }
3744
4416
  if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
3745
4417
  this.setGuides(null);
@@ -3887,6 +4559,7 @@ var VectorEditorController = (_class3 = class {
3887
4559
  if (d.fillRule) out.fillRule = d.fillRule;
3888
4560
  if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
3889
4561
  for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
4562
+ if (_optionalChain([d, 'access', _120 => _120.strokeWidthProfile, 'optionalAccess', _121 => _121.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
3890
4563
  return out;
3891
4564
  }
3892
4565
  /** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
@@ -3907,10 +4580,39 @@ var VectorEditorController = (_class3 = class {
3907
4580
  }
3908
4581
  if (!targets.size) return { ok: false, message: "Nothing is selected." };
3909
4582
  this.change(_nullishCoalesce(options.label, () => ( "Change Style")), () => {
3910
- this.setDoc(mapTree(this.doc.nodes, (n) => targets.has(n.id) ? { ...n, ...style } : n));
4583
+ this.setDoc(mapTree(this.doc.nodes, (n) => targets.has(n.id) ? withStyle(n, style) : n));
3911
4584
  });
3912
4585
  return { ok: true };
3913
4586
  }
4587
+ /**
4588
+ * Sets the variable-width profile of the selected (or given) paths: a preset id
4589
+ * (`uniform` removes the profile), points, or a parametric taper / wave. Returns an error for
4590
+ * an unknown preset.
4591
+ */
4592
+ setWidthProfile(profile, options = {}) {
4593
+ let points;
4594
+ if (profile === null) points = [];
4595
+ else if (typeof profile === "string") {
4596
+ const preset = widthProfilePreset(profile);
4597
+ if (!preset) return { ok: false, message: `There is no width profile "${profile}".` };
4598
+ points = preset;
4599
+ } else if (Array.isArray(profile)) points = sanitizeWidthProfile(profile);
4600
+ else {
4601
+ const p = profile;
4602
+ points = p.taper || p.wave ? generateWidthProfile(p) : [];
4603
+ }
4604
+ this.widthPoint = null;
4605
+ return this.setStyle({ strokeWidthProfile: points.length ? points : null }, _nullishCoalesce(options.ids, () => ( this.selection)), { label: points.length ? "Width Profile" : "Uniform Width" });
4606
+ }
4607
+ /** Removes one width point of a path's profile (the width tool's Delete). */
4608
+ removeWidthPoint(ref) {
4609
+ const node = this.getNode(ref.nodeId);
4610
+ if (_optionalChain([node, 'optionalAccess', _122 => _122.type]) !== "path" || !_optionalChain([node, 'access', _123 => _123.strokeWidthProfile, 'optionalAccess', _124 => _124[ref.index]])) return { ok: false, message: "There is no such width point." };
4611
+ const rest = removeWidthPoints(sanitizeWidthProfile(node.strokeWidthProfile), [ref.index]);
4612
+ this.widthPoint = null;
4613
+ this.change("Delete Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: rest.length ? rest : null })));
4614
+ return { ok: true };
4615
+ }
3914
4616
  /**
3915
4617
  * Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
3916
4618
  * anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
@@ -3930,7 +4632,7 @@ var VectorEditorController = (_class3 = class {
3930
4632
  const refs = this.cornerTargets(options);
3931
4633
  if (!refs.length) return { ok: false, message: "Nothing is selected." };
3932
4634
  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));
4635
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _125 => _125.corner, 'optionalAccess', _126 => _126.radius]), () => ( 0)), kind));
3934
4636
  });
3935
4637
  return { ok: true };
3936
4638
  }
@@ -3981,6 +4683,7 @@ var VectorEditorController = (_class3 = class {
3981
4683
  arrowAlign: _al,
3982
4684
  startArrowScale: _ss,
3983
4685
  endArrowScale: _es,
4686
+ strokeWidthProfile: _wp,
3984
4687
  ...rest
3985
4688
  } = cloneVectorNode(p);
3986
4689
  const id = this.newId();
@@ -4068,10 +4771,10 @@ var VectorEditorController = (_class3 = class {
4068
4771
  return this.selection.filter((id) => {
4069
4772
  let covered = false;
4070
4773
  const loc = locateVectorNode(this.doc.nodes, id);
4071
- let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _87 => _87.parent]), () => ( null));
4774
+ let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _127 => _127.parent]), () => ( null));
4072
4775
  while (parent) {
4073
4776
  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));
4777
+ parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _128 => _128(this.doc.nodes, parent.id), 'optionalAccess', _129 => _129.parent]), () => ( null));
4075
4778
  }
4076
4779
  return !covered;
4077
4780
  });
@@ -4254,8 +4957,8 @@ var VectorEditorController = (_class3 = class {
4254
4957
  if (!ids.length) return null;
4255
4958
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
4256
4959
  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;
4960
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _130 => _130[0], 'access', _131 => _131.parent, 'optionalAccess', _132 => _132.id]), () => ( null));
4961
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _133 => _133.parent, 'optionalAccess', _134 => _134.id]), () => ( null))) !== parent)) return null;
4259
4962
  const id = this.newId();
4260
4963
  this.change("Group", () => {
4261
4964
  const set = new Set(ids);
@@ -4278,11 +4981,11 @@ var VectorEditorController = (_class3 = class {
4278
4981
  }
4279
4982
  /** Replaces selected groups with their children. */
4280
4983
  ungroup(options = {}) {
4281
- const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _95 => _95.type]) === "group");
4984
+ const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _135 => _135.type]) === "group");
4282
4985
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
4283
4986
  const set = new Set(groups.map((g) => g.id));
4284
4987
  this.change("Ungroup", () => {
4285
- const flatten = (list) => {
4988
+ const flatten2 = (list) => {
4286
4989
  const out = [];
4287
4990
  let changed = false;
4288
4991
  for (const n of list) {
@@ -4294,14 +4997,14 @@ var VectorEditorController = (_class3 = class {
4294
4997
  ...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
4998
  );
4296
4999
  } else if (n.type === "group") {
4297
- const children = flatten(n.children);
5000
+ const children = flatten2(n.children);
4298
5001
  if (children !== n.children) changed = true;
4299
5002
  out.push(children !== n.children ? { ...n, children } : n);
4300
5003
  } else out.push(n);
4301
5004
  }
4302
5005
  return changed ? out : list;
4303
5006
  };
4304
- this.setDoc(flatten(this.doc.nodes));
5007
+ this.setDoc(flatten2(this.doc.nodes));
4305
5008
  this.selection = groups.flatMap((g) => g.children.map((c) => c.id));
4306
5009
  });
4307
5010
  return { ok: true };
@@ -4338,7 +5041,7 @@ var VectorEditorController = (_class3 = class {
4338
5041
  });
4339
5042
  const paths = [...new Set(order)].map((id2) => this.getNode(id2));
4340
5043
  if (paths.length < 2) return null;
4341
- const result = _chunkBGNPT5PEcjs.pathBoolean.call(void 0,
5044
+ const result = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0,
4342
5045
  BOOLEAN_OPS[op],
4343
5046
  paths.map((p) => ({
4344
5047
  path: vectorPathWorldBezier(
@@ -4388,11 +5091,11 @@ var VectorEditorController = (_class3 = class {
4388
5091
  /** A document-space path mapped into the parent space of node `id`. */
4389
5092
  intoParentSpace(id, path) {
4390
5093
  const parent = _nullishCoalesce(vectorParentMatrix(this.doc.nodes, id), () => ( IDENTITY_MATRIX));
4391
- return isRoot(parent) ? path : _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, path, invertMatrix(parent));
5094
+ return isRoot(parent) ? path : _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, path, invertMatrix(parent));
4392
5095
  }
4393
5096
  /** Splits selected compound paths into one path per subpath. */
4394
5097
  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);
5098
+ const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _136 => _136.type]) === "path" && n.subpaths.length > 1);
4396
5099
  if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
4397
5100
  const created = [];
4398
5101
  const set = new Set(paths.map((p) => p.id));
@@ -4454,7 +5157,7 @@ var VectorEditorController = (_class3 = class {
4454
5157
  /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
4455
5158
  curvatureMoved(doc, ref, to) {
4456
5159
  const node = findVectorNode(doc.nodes, ref.nodeId);
4457
- if (_optionalChain([node, 'optionalAccess', _97 => _97.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5160
+ if (_optionalChain([node, 'optionalAccess', _137 => _137.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
4458
5161
  const q = this.toLocal(ref.nodeId, to, doc);
4459
5162
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
4460
5163
  const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
@@ -4463,7 +5166,7 @@ var VectorEditorController = (_class3 = class {
4463
5166
  /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
4464
5167
  bent(doc, ref, t, p) {
4465
5168
  const node = findVectorNode(doc.nodes, ref.nodeId);
4466
- if (_optionalChain([node, 'optionalAccess', _98 => _98.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5169
+ if (_optionalChain([node, 'optionalAccess', _138 => _138.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
4467
5170
  const q = this.toLocal(ref.nodeId, p, doc);
4468
5171
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
4469
5172
  return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
@@ -4474,7 +5177,7 @@ var VectorEditorController = (_class3 = class {
4474
5177
  */
4475
5178
  bendSegment(ref, to, t = 0.5) {
4476
5179
  const node = this.getNode(ref.nodeId);
4477
- const sp = _optionalChain([node, 'optionalAccess', _99 => _99.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
5180
+ const sp = _optionalChain([node, 'optionalAccess', _139 => _139.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
4478
5181
  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
5182
  if (node.locked) return { ok: false, message: "The path is locked." };
4480
5183
  this.change("Bend Segment", () => {
@@ -4509,7 +5212,7 @@ var VectorEditorController = (_class3 = class {
4509
5212
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
4510
5213
  insertAnchorAt(nodeId, point) {
4511
5214
  const node = this.getNode(nodeId);
4512
- if (_optionalChain([node, 'optionalAccess', _100 => _100.type]) !== "path") return null;
5215
+ if (_optionalChain([node, 'optionalAccess', _140 => _140.type]) !== "path") return null;
4513
5216
  const hit = nearestSegment(node, this.toLocal(nodeId, point));
4514
5217
  if (!hit) return null;
4515
5218
  const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
@@ -4553,7 +5256,7 @@ var VectorEditorController = (_class3 = class {
4553
5256
  setClosed(closed, nodeId) {
4554
5257
  const targets = nodeId ? (() => {
4555
5258
  const n = this.getNode(nodeId);
4556
- return _optionalChain([n, 'optionalAccess', _101 => _101.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
5259
+ return _optionalChain([n, 'optionalAccess', _141 => _141.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
4557
5260
  })() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
4558
5261
  if (!targets.length) return { ok: false, message: "Select a path or points." };
4559
5262
  this.change(closed ? "Close Path" : "Open Path", () => {
@@ -4571,7 +5274,12 @@ var VectorEditorController = (_class3 = class {
4571
5274
  const set = new Set(ids.flatMap((id) => this.getNode(id) ? collectPathNodes(this.getNode(id)) : []).map((p) => p.id));
4572
5275
  if (!set.size) return { ok: false, message: "Select a path." };
4573
5276
  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));
5277
+ this.setDoc(
5278
+ mapTree(
5279
+ this.doc.nodes,
5280
+ (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _142 => _142.strokeWidthProfile, 'optionalAccess', _143 => _143.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
5281
+ )
5282
+ );
4575
5283
  this.anchors = [];
4576
5284
  });
4577
5285
  return { ok: true };
@@ -4652,9 +5360,9 @@ var VectorEditorController = (_class3 = class {
4652
5360
  const cx = points.reduce((s, p) => s + p.at.x, 0) / points.length;
4653
5361
  const cy = points.reduce((s, p) => s + p.at.y, 0) / points.length;
4654
5362
  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;
5363
+ for (const { ref, at: at2 } of points) {
5364
+ const dx = axis === "horizontal" ? 0 : cx - at2.x;
5365
+ const dy = axis === "vertical" ? 0 : cy - at2.y;
4658
5366
  if (dx || dy) this.doc = this.movedAnchors(this.doc, [ref], dx, dy);
4659
5367
  }
4660
5368
  });
@@ -4707,7 +5415,7 @@ var VectorEditorController = (_class3 = class {
4707
5415
  */
4708
5416
  cutPathAt(nodeId, point) {
4709
5417
  const node = this.getNode(nodeId);
4710
- if (_optionalChain([node, 'optionalAccess', _102 => _102.type]) !== "path" || node.locked) return null;
5418
+ if (_optionalChain([node, 'optionalAccess', _144 => _144.type]) !== "path" || node.locked) return null;
4711
5419
  const local = this.toLocal(nodeId, point);
4712
5420
  const hit = nearestSegment(node, local);
4713
5421
  if (!hit) return null;
@@ -4738,8 +5446,8 @@ var VectorEditorController = (_class3 = class {
4738
5446
  const node = this.getNode(id);
4739
5447
  if (node.locked) continue;
4740
5448
  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;
5449
+ const at2 = list.filter((r) => r.subpath === i).map((r) => r.index);
5450
+ const pieces = at2.length ? cutSubpathAt(sp, at2) : null;
4743
5451
  return pieces && !(pieces.length === 1 && pieces[0].closed === sp.closed) ? pieces : null;
4744
5452
  });
4745
5453
  if (ids) created.push(...ids);
@@ -4757,7 +5465,7 @@ var VectorEditorController = (_class3 = class {
4757
5465
  if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
4758
5466
  });
4759
5467
  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);
5468
+ if (_optionalChain([ids, 'optionalAccess', _145 => _145.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
4761
5469
  const box = polylineBounds(points.map(toPoint2), pad);
4762
5470
  if (!box) return [];
4763
5471
  const out = [];
@@ -4813,10 +5521,10 @@ var VectorEditorController = (_class3 = class {
4813
5521
  const region = eraserRegion(pts, width);
4814
5522
  if (!region.length) return [];
4815
5523
  const plan = [];
4816
- const rb = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, region);
5524
+ const rb = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, region);
4817
5525
  const reach = rb ? [{ x: rb.minX, y: rb.minY }, { x: rb.maxX, y: rb.maxY }] : points;
4818
5526
  for (const node of this.strokeTargets(reach, options.ids, 0)) {
4819
- const local = _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, region, invertMatrix(this.worldOf(node.id)));
5527
+ const local = _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, region, invertMatrix(this.worldOf(node.id)));
4820
5528
  const live = node.subpaths.filter((sp) => sp.anchors.length > 1);
4821
5529
  if (!live.length) continue;
4822
5530
  if (hasPaint(node.fill) && live.every((sp) => sp.closed)) {
@@ -4843,7 +5551,7 @@ var VectorEditorController = (_class3 = class {
4843
5551
  finishPath() {
4844
5552
  if (!this.pen) return;
4845
5553
  const node = this.getNode(this.pen.nodeId);
4846
- const sp = _optionalChain([node, 'optionalAccess', _104 => _104.subpaths, 'access', _105 => _105[this.pen.subpath]]);
5554
+ const sp = _optionalChain([node, 'optionalAccess', _146 => _146.subpaths, 'access', _147 => _147[this.pen.subpath]]);
4847
5555
  const pen = this.pen;
4848
5556
  this.pen = null;
4849
5557
  this.pointer = null;
@@ -4887,19 +5595,19 @@ var VectorEditorController = (_class3 = class {
4887
5595
  if (trimmed.startsWith("{")) {
4888
5596
  try {
4889
5597
  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) {
5598
+ if (_optionalChain([data, 'optionalAccess', _148 => _148.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
5599
+ } catch (e3) {
4892
5600
  nodes = null;
4893
5601
  }
4894
5602
  } else if (/<svg[\s>]/i.test(trimmed)) {
4895
5603
  try {
4896
5604
  nodes = parseVectorSvg(trimmed).nodes;
4897
- } catch (e3) {
5605
+ } catch (e4) {
4898
5606
  nodes = null;
4899
5607
  }
4900
5608
  }
4901
5609
  } else nodes = this.clipboard;
4902
- if (!_optionalChain([nodes, 'optionalAccess', _107 => _107.length])) return [];
5610
+ if (!_optionalChain([nodes, 'optionalAccess', _149 => _149.length])) return [];
4903
5611
  const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
4904
5612
  const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
4905
5613
  this.change("Paste", () => {
@@ -4950,7 +5658,7 @@ var VectorEditorController = (_class3 = class {
4950
5658
  hitHandle(screen) {
4951
5659
  for (const ref of getVisibleHandles(this.view()).reverse()) {
4952
5660
  const a = getAnchorWorld(this.doc, ref);
4953
- const h = _optionalChain([a, 'optionalAccess', _108 => _108[ref.handle]]);
5661
+ const h = _optionalChain([a, 'optionalAccess', _150 => _150[ref.handle]]);
4954
5662
  if (!h) continue;
4955
5663
  const s = this.documentToScreen(h);
4956
5664
  if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
@@ -5053,10 +5761,131 @@ var VectorEditorController = (_class3 = class {
5053
5761
  const seg = this.nearestOutline(p);
5054
5762
  return seg ? { nodeId: seg.nodeId, segment: seg } : null;
5055
5763
  }
5764
+ case "width": {
5765
+ const hit = this.hitWidthHandle(screen);
5766
+ if (hit) return { nodeId: hit.ref.nodeId, widthPoint: hit.ref };
5767
+ const spot = this.strokeSpotAt(p);
5768
+ return spot ? { nodeId: spot.nodeId, strokeSpot: spot } : null;
5769
+ }
5056
5770
  default:
5057
5771
  return null;
5058
5772
  }
5059
5773
  }
5774
+ widthView() {
5775
+ return { document: this.doc, selection: this.selection, tool: this.tool, hover: this.hover, interaction: this.interaction, selectedWidthPoint: this.widthPoint };
5776
+ }
5777
+ /** The width point centre or side end under `screen` (width tool). */
5778
+ hitWidthHandle(screen) {
5779
+ let best = null;
5780
+ let bestD = Infinity;
5781
+ for (const h of getVectorWidthHandles(this.widthView())) {
5782
+ for (const [part, q] of [["center", h], ["left", h.left], ["right", h.right]]) {
5783
+ const s = this.documentToScreen(q);
5784
+ const d = Math.hypot(s.x - screen.x, s.y - screen.y);
5785
+ if (d <= this.hitRadius && d < bestD - 1e-9) {
5786
+ best = { ref: { ...h.ref, part } };
5787
+ bestD = d;
5788
+ }
5789
+ }
5790
+ }
5791
+ return best;
5792
+ }
5793
+ /** The nearest stroked, unlocked visible path whose stroke is under document point `p` (width tool). */
5794
+ strokeSpotAt(p) {
5795
+ let best = null;
5796
+ const tol = this.tolerance();
5797
+ walkVectorNodes(this.doc.nodes, (n) => {
5798
+ if (n.type !== "path" || n.visible === false || n.locked) return;
5799
+ if (hiddenOrLocked(this.doc.nodes, n.id)) return;
5800
+ const g = vectorStrokeWidthGeometry(n);
5801
+ if (!g) return;
5802
+ const m = this.worldOf(n.id);
5803
+ const local = applyMatrix(invertMatrix(m), p);
5804
+ const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
5805
+ if (!near2) return;
5806
+ const profile = strokeWidthProfileOf(n);
5807
+ const w = widthProfileAt(profile, near2.t);
5808
+ const reach = Math.max(near2.side === "left" ? w.left : w.right, 0) * g.unit + tol;
5809
+ if (near2.distance > reach || near2.distance > widthProfileMaxScale(profile) * g.unit + tol) return;
5810
+ if (best && near2.distance >= best.distance) return;
5811
+ const nx = near2.tangent[1];
5812
+ const ny = -near2.tangent[0];
5813
+ const [cx, cy] = near2.point;
5814
+ best = {
5815
+ nodeId: n.id,
5816
+ subpath: near2.contour,
5817
+ t: near2.t,
5818
+ point: applyMatrix(m, { x: cx, y: cy }),
5819
+ left: applyMatrix(m, { x: cx + nx * w.left * g.unit, y: cy + ny * w.left * g.unit }),
5820
+ right: applyMatrix(m, { x: cx - nx * w.right * g.unit, y: cy - ny * w.right * g.unit }),
5821
+ distance: near2.distance
5822
+ };
5823
+ });
5824
+ if (!best) return null;
5825
+ const { distance: _d, ...spot } = best;
5826
+ return spot;
5827
+ }
5828
+ downWidth(screen, p, input) {
5829
+ const hit = this.hitWidthHandle(screen);
5830
+ const spot = hit ? null : this.strokeSpotAt(p);
5831
+ const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _151 => _151.ref, 'access', _152 => _152.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _153 => _153.nodeId])));
5832
+ const node = nodeId ? this.getNode(nodeId) : void 0;
5833
+ const g = _optionalChain([node, 'optionalAccess', _154 => _154.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
5834
+ if (!node || node.type !== "path" || !g) {
5835
+ if (this.widthPoint) {
5836
+ this.widthPoint = null;
5837
+ this.notify();
5838
+ }
5839
+ return;
5840
+ }
5841
+ const before = this.snapshot();
5842
+ const local = this.toLocal(node.id, p);
5843
+ let profile;
5844
+ let ref;
5845
+ let mode = "width";
5846
+ let side = "both";
5847
+ let t;
5848
+ let subpath;
5849
+ if (hit) {
5850
+ profile = sanitizeWidthProfile(node.strokeWidthProfile);
5851
+ ref = { nodeId: node.id, index: hit.ref.index, subpath: hit.ref.subpath };
5852
+ t = _nullishCoalesce(_optionalChain([profile, 'access', _155 => _155[ref.index], 'optionalAccess', _156 => _156.t]), () => ( 0));
5853
+ subpath = hit.ref.subpath;
5854
+ if (hit.ref.part === "center") mode = "move";
5855
+ else if (input.alt) side = hit.ref.part;
5856
+ } else {
5857
+ const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
5858
+ const inserted = insertWidthPoint(strokeWidthProfileOf(node), near2.t);
5859
+ profile = inserted.points;
5860
+ subpath = near2.contour;
5861
+ t = near2.t;
5862
+ ref = { nodeId: node.id, index: inserted.index, subpath };
5863
+ if (input.alt) side = near2.side;
5864
+ }
5865
+ const at2 = strokePositionPoint(g.line, subpath, t);
5866
+ if (!at2) return;
5867
+ const base = { ...this.doc, nodes: mapTree(this.doc.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
5868
+ this.doc = base;
5869
+ this.widthPoint = { nodeId: node.id, index: ref.index, subpath };
5870
+ this.drag = {
5871
+ kind: "width",
5872
+ ref,
5873
+ mode,
5874
+ side,
5875
+ origin: { x: at2.point[0], y: at2.point[1] },
5876
+ normal: { x: at2.tangent[1], y: -at2.tangent[0] },
5877
+ unit: g.unit,
5878
+ profile,
5879
+ before,
5880
+ base,
5881
+ start: p,
5882
+ moved: false,
5883
+ created: !hit
5884
+ };
5885
+ const w = profile[ref.index];
5886
+ this.interaction = { type: "width", ref, mode, width: (w.left + w.right) * g.unit };
5887
+ this.notify(!hit);
5888
+ }
5060
5889
  /** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
5061
5890
  nearestOutline(p) {
5062
5891
  let best = null;
@@ -5104,7 +5933,9 @@ var VectorEditorController = (_class3 = class {
5104
5933
  case "pen":
5105
5934
  return this.downPen(screen, p, input);
5106
5935
  case "pencil":
5107
- return this.downPencil(p);
5936
+ return this.downPencil(p, input);
5937
+ case "width":
5938
+ return this.downWidth(screen, p, input);
5108
5939
  case "scissors": {
5109
5940
  const seg = this.nearestOutline(p);
5110
5941
  if (seg) this.cutPathAt(seg.nodeId, seg.point);
@@ -5240,7 +6071,7 @@ var VectorEditorController = (_class3 = class {
5240
6071
  }
5241
6072
  downPen(screen, p, input) {
5242
6073
  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;
6074
+ const sp = this.pen ? _optionalChain([this, 'access', _157 => _157.getNode, 'call', _158 => _158(this.pen.nodeId), 'optionalAccess', _159 => _159.subpaths, 'access', _160 => _160[this.pen.subpath]]) : void 0;
5244
6075
  if (this.pen && sp) {
5245
6076
  const pen = this.pen;
5246
6077
  const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
@@ -5297,7 +6128,7 @@ var VectorEditorController = (_class3 = class {
5297
6128
  this.interaction = { type: "pen", nodeId: node.id };
5298
6129
  this.notify(true);
5299
6130
  }
5300
- downPencil(p) {
6131
+ downPencil(p, input = {}) {
5301
6132
  const before = this.snapshot();
5302
6133
  const node = {
5303
6134
  id: this.newId(),
@@ -5311,7 +6142,11 @@ var VectorEditorController = (_class3 = class {
5311
6142
  this.setDoc([...this.doc.nodes, node]);
5312
6143
  const lazy = new LazyMouse(this.editing.pencilSmoothing * VECTOR_PENCIL_STRING_PX / this.viewport.zoom);
5313
6144
  lazy.push([p.x, p.y]);
5314
- this.drag = { kind: "pencil", nodeId: node.id, points: [[p.x, p.y]], before, lazy };
6145
+ const mode = this.editing.pencilPressure;
6146
+ const hasPressure = typeof input.pressure === "number" && Number.isFinite(input.pressure);
6147
+ const record = hasPressure && (mode === "all" || mode === "pen" && input.pointerType === "pen");
6148
+ const pressure = hasPressure ? input.pressure : 0.5;
6149
+ this.drag = { kind: "pencil", nodeId: node.id, points: [[p.x, p.y]], before, lazy, pressures: record ? [pressure] : null, pressure };
5315
6150
  this.interaction = { type: "draw", tool: "pencil", nodeId: node.id, bounds: { x: p.x, y: p.y, width: 0, height: 0 } };
5316
6151
  this.notify(true);
5317
6152
  }
@@ -5491,7 +6326,7 @@ var VectorEditorController = (_class3 = class {
5491
6326
  }
5492
6327
  case "shape": {
5493
6328
  const node = this.getNode(drag.nodeId);
5494
- if (_optionalChain([node, 'optionalAccess', _113 => _113.type]) !== "path") return;
6329
+ if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) !== "path") return;
5495
6330
  const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
5496
6331
  let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
5497
6332
  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 +6355,43 @@ var VectorEditorController = (_class3 = class {
5520
6355
  this.notify(true);
5521
6356
  return;
5522
6357
  }
6358
+ case "width": {
6359
+ if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 2) return;
6360
+ drag.moved = true;
6361
+ const node = findVectorNode(drag.base.nodes, drag.ref.nodeId);
6362
+ const local = this.toLocal(node.id, p, drag.base);
6363
+ let profile;
6364
+ if (drag.mode === "move") {
6365
+ const g = vectorStrokeWidthGeometry(node);
6366
+ const near2 = g ? nearestStrokePosition(g.line, [local.x, local.y], drag.ref.subpath) : null;
6367
+ if (!near2) return;
6368
+ profile = setWidthPoint(drag.profile, drag.ref.index, { t: near2.t });
6369
+ } else {
6370
+ const off = ((local.x - drag.origin.x) * drag.normal.x + (local.y - drag.origin.y) * drag.normal.y) / drag.unit;
6371
+ 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) };
6372
+ profile = setWidthPoint(drag.profile, drag.ref.index, patch);
6373
+ }
6374
+ this.doc = { ...drag.base, nodes: mapTree(drag.base.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
6375
+ const w = profile[drag.ref.index];
6376
+ this.interaction = { type: "width", ref: drag.ref, mode: drag.mode, width: (w.left + w.right) * drag.unit };
6377
+ this.notify(true);
6378
+ return;
6379
+ }
5523
6380
  case "pencil": {
5524
6381
  const last = drag.points[drag.points.length - 1];
5525
6382
  let moved = false;
6383
+ if (typeof input.pressure === "number" && Number.isFinite(input.pressure)) drag.pressure = input.pressure;
5526
6384
  for (const q of drag.lazy.push([p.x, p.y])) {
5527
6385
  if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
5528
6386
  drag.points.push(q);
6387
+ _optionalChain([drag, 'access', _162 => _162.pressures, 'optionalAccess', _163 => _163.push, 'call', _164 => _164(drag.pressure)]);
5529
6388
  moved = true;
5530
6389
  }
5531
6390
  const string = drag.lazy.string();
5532
6391
  const node = this.getNode(drag.nodeId);
5533
- if (_optionalChain([node, 'optionalAccess', _114 => _114.type]) !== "path") return;
6392
+ if (_optionalChain([node, 'optionalAccess', _165 => _165.type]) !== "path") return;
5534
6393
  if (!moved) {
5535
- if (string && _optionalChain([this, 'access', _115 => _115.interaction, 'optionalAccess', _116 => _116.type]) === "draw") {
6394
+ if (string && _optionalChain([this, 'access', _166 => _166.interaction, 'optionalAccess', _167 => _167.type]) === "draw") {
5536
6395
  this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
5537
6396
  this.notify();
5538
6397
  }
@@ -5568,8 +6427,8 @@ var VectorEditorController = (_class3 = class {
5568
6427
  }
5569
6428
  this.setGuides(res);
5570
6429
  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
6430
+ dx: _optionalChain([res, 'optionalAccess', _168 => _168.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
6431
+ dy: _optionalChain([res, 'optionalAccess', _169 => _169.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
5573
6432
  };
5574
6433
  }
5575
6434
  /** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
@@ -5606,7 +6465,7 @@ var VectorEditorController = (_class3 = class {
5606
6465
  }
5607
6466
  /** Primary button released. */
5608
6467
  pointerUp(screen, input = {}) {
5609
- if (screen && this.drag && this.drag.kind !== "pan") this.pointerMove(screen, input);
6468
+ if (screen && this.drag && this.drag.kind !== "pan") this.pointerMove(screen, { ...input, pressure: void 0 });
5610
6469
  const drag = this.drag;
5611
6470
  this.drag = null;
5612
6471
  this.interaction = null;
@@ -5648,7 +6507,7 @@ var VectorEditorController = (_class3 = class {
5648
6507
  if (drag.moved) return this.commit("Corner Radius", drag.before);
5649
6508
  if (drag.alt) {
5650
6509
  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));
6510
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _170 => _170.corner, 'optionalAccess', _171 => _171.radius]), () => ( 0)), kind));
5652
6511
  return this.commit("Corner Kind", drag.before);
5653
6512
  }
5654
6513
  const ref = drag.widget.ref;
@@ -5658,6 +6517,15 @@ var VectorEditorController = (_class3 = class {
5658
6517
  this.notify();
5659
6518
  return;
5660
6519
  }
6520
+ case "width": {
6521
+ if (drag.moved) return this.commit(drag.mode === "move" ? "Move Width Point" : "Width Point", drag.before);
6522
+ if (drag.created) {
6523
+ this.doc = drag.before.doc;
6524
+ this.widthPoint = null;
6525
+ this.notify(true);
6526
+ } else this.notify();
6527
+ return;
6528
+ }
5661
6529
  case "stroke": {
5662
6530
  this.notify();
5663
6531
  if (drag.tool === "knife") this.knife(drag.points);
@@ -5683,19 +6551,22 @@ var VectorEditorController = (_class3 = class {
5683
6551
  }
5684
6552
  case "pencil": {
5685
6553
  const node = this.getNode(drag.nodeId);
5686
- if (_optionalChain([node, 'optionalAccess', _121 => _121.type]) !== "path" || drag.points.length < 2) {
6554
+ if (_optionalChain([node, 'optionalAccess', _172 => _172.type]) !== "path" || drag.points.length < 2) {
5687
6555
  this.doc = drag.before.doc;
5688
6556
  this.notify(true);
5689
6557
  return;
5690
6558
  }
5691
- for (const q of drag.lazy.finish()) drag.points.push(q);
6559
+ for (const q of drag.lazy.finish()) {
6560
+ drag.points.push(q);
6561
+ _optionalChain([drag, 'access', _173 => _173.pressures, 'optionalAccess', _174 => _174.push, 'call', _175 => _175(drag.pressure)]);
6562
+ }
5692
6563
  const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
5693
6564
  if (shape) {
5694
6565
  this.replaceNode(drag.nodeId, { ...node, name: inkShapeLabel(shape), subpaths: [inkShapeSubpath(shape)] });
5695
6566
  this.selection = [drag.nodeId];
5696
6567
  return this.commit(`Draw ${inkShapeLabel(shape)}`, drag.before);
5697
6568
  }
5698
- const fitted = _chunkBGNPT5PEcjs.fitCubics.call(void 0, drag.points, 1.5 / this.viewport.zoom, { cornerAngleDeg: 60 });
6569
+ const fitted = _chunkGVB7ZPLZcjs.fitCubics.call(void 0, drag.points, 1.5 / this.viewport.zoom, { cornerAngleDeg: 60 });
5699
6570
  const anchors = [];
5700
6571
  for (const c of fitted) {
5701
6572
  if (!anchors.length) anchors.push({ x: c.p0[0], y: c.p0[1] });
@@ -5703,7 +6574,8 @@ var VectorEditorController = (_class3 = class {
5703
6574
  anchors.push({ x: c.p3[0], y: c.p3[1], in: { x: c.p2[0], y: c.p2[1] } });
5704
6575
  }
5705
6576
  const subpaths = anchors.length > 1 ? bezierToSubpaths(subpathsToBezier([{ anchors, closed: false }])) : node.subpaths;
5706
- this.replaceNode(drag.nodeId, { ...node, subpaths });
6577
+ const profile = drag.pressures ? pencilPressureProfile(drag.points, drag.pressures) : [];
6578
+ this.replaceNode(drag.nodeId, { ...node, subpaths, ...profile.length ? { strokeWidthProfile: profile } : {} });
5707
6579
  this.selection = [drag.nodeId];
5708
6580
  return this.commit("Draw", drag.before);
5709
6581
  }
@@ -5757,7 +6629,7 @@ var VectorEditorController = (_class3 = class {
5757
6629
  this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
5758
6630
  return;
5759
6631
  }
5760
- const id = _nullishCoalesce(_optionalChain([this, 'access', _122 => _122.hover, 'optionalAccess', _123 => _123.segment, 'optionalAccess', _124 => _124.nodeId]), () => ( this.editingId));
6632
+ const id = _nullishCoalesce(_optionalChain([this, 'access', _176 => _176.hover, 'optionalAccess', _177 => _177.segment, 'optionalAccess', _178 => _178.nodeId]), () => ( this.editingId));
5761
6633
  if (id) {
5762
6634
  const node = this.getNode(id);
5763
6635
  const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
@@ -5825,7 +6697,10 @@ var VectorEditorController = (_class3 = class {
5825
6697
  switch (key) {
5826
6698
  case "Escape":
5827
6699
  if (this.drag) this.cancelDrag();
5828
- else if (this.pen) this.finishPath();
6700
+ else if (this.tool === "width" && this.widthPoint) {
6701
+ this.widthPoint = null;
6702
+ this.notify();
6703
+ } else if (this.pen) this.finishPath();
5829
6704
  else if (this.tool === "direct" && this.anchors.length) {
5830
6705
  this.anchors = [];
5831
6706
  this.notify();
@@ -5837,7 +6712,7 @@ var VectorEditorController = (_class3 = class {
5837
6712
  this.finishPath();
5838
6713
  return true;
5839
6714
  }
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") {
6715
+ if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _179 => _179.getNode, 'call', _180 => _180(this.selection[0]), 'optionalAccess', _181 => _181.type]) === "path") {
5841
6716
  this.tool = "direct";
5842
6717
  this.editingId = this.selection[0];
5843
6718
  this.notify();
@@ -5846,6 +6721,7 @@ var VectorEditorController = (_class3 = class {
5846
6721
  return false;
5847
6722
  case "Delete":
5848
6723
  case "Backspace":
6724
+ if (this.tool === "width" && this.widthPoint) return this.removeWidthPoint(this.widthPoint).ok;
5849
6725
  if (this.pen) {
5850
6726
  const node = this.getNode(this.pen.nodeId);
5851
6727
  const sp = node.subpaths[this.pen.subpath];
@@ -5927,6 +6803,31 @@ function scaleFor(handle, b, p, mods = {}) {
5927
6803
  }
5928
6804
  return { sx, sy, origin };
5929
6805
  }
6806
+ function withStyle(node, style) {
6807
+ const next = { ...node, ...style };
6808
+ if ("strokeWidthProfile" in style) {
6809
+ const p = style.strokeWidthProfile ? sanitizeWidthProfile(style.strokeWidthProfile) : [];
6810
+ if (p.length) next.strokeWidthProfile = p;
6811
+ else delete next.strokeWidthProfile;
6812
+ }
6813
+ return next;
6814
+ }
6815
+ function hiddenOrLocked(nodes, id) {
6816
+ const loc = locateVectorNode(nodes, id);
6817
+ if (!loc) return false;
6818
+ const n = loc.list[loc.index];
6819
+ if (n.locked || n.visible === false) return true;
6820
+ return loc.parent ? hiddenOrLocked(nodes, loc.parent.id) : false;
6821
+ }
6822
+ function pencilPressureProfile(points, pressures) {
6823
+ const n = Math.min(points.length, pressures.length);
6824
+ if (n < 2) return [];
6825
+ const s = [0];
6826
+ 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]));
6827
+ const total = s[n - 1];
6828
+ if (!(total > 0)) return [];
6829
+ return pressureWidthProfile(s.map((d, i) => ({ t: d / total, pressure: pressures[i] })));
6830
+ }
5930
6831
 
5931
6832
  // src/vector/render.ts
5932
6833
  var fmt = (n) => Math.round(n * 100) / 100;
@@ -5953,7 +6854,7 @@ function renderNode(node, defs, idPrefix) {
5953
6854
  strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
5954
6855
  ...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
5955
6856
  ...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
5956
- ..._optionalChain([node, 'access', _128 => _128.strokeDasharray, 'optionalAccess', _129 => _129.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
6857
+ ..._optionalChain([node, 'access', _182 => _182.strokeDasharray, 'optionalAccess', _183 => _183.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
5957
6858
  fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
5958
6859
  opacity: _nullishCoalesce(node.opacity, () => ( 1))
5959
6860
  };
@@ -5977,7 +6878,7 @@ function screenOutline(state, node) {
5977
6878
  const v2 = state.viewport;
5978
6879
  const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
5979
6880
  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(" ");
6881
+ return collectWithMatrix(node, parent).map(({ path, parent: m }) => _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, vectorPathWorldBezier(path, m), view), { precision: 2 })).join(" ");
5981
6882
  }
5982
6883
  function collectWithMatrix(node, parent) {
5983
6884
  if (node.type === "path") return [{ path: node, parent }];
@@ -6002,9 +6903,9 @@ function getVectorOverlayRenderData(state) {
6002
6903
  };
6003
6904
  for (const id of state.selection) outline(id, true);
6004
6905
  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);
6906
+ if (_optionalChain([state, 'access', _184 => _184.hover, 'optionalAccess', _185 => _185.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
6006
6907
  let selectionBox = null;
6007
- if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _132 => _132.interaction, 'optionalAccess', _133 => _133.type]) !== "marquee") {
6908
+ if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _186 => _186.interaction, 'optionalAccess', _187 => _187.type]) !== "marquee") {
6008
6909
  const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
6009
6910
  selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
6010
6911
  }
@@ -6014,9 +6915,9 @@ function getVectorOverlayRenderData(state) {
6014
6915
  const a = getAnchorWorld(doc, ref);
6015
6916
  const s = toScreen(state, a);
6016
6917
  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 };
6918
+ return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _188 => _188.hover, 'optionalAccess', _189 => _189.anchor]), ref), closes };
6018
6919
  });
6019
- if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _136 => _136.hover, 'optionalAccess', _137 => _137.anchor])) {
6920
+ if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _190 => _190.hover, 'optionalAccess', _191 => _191.anchor])) {
6020
6921
  const a = getAnchorWorld(doc, state.hover.anchor);
6021
6922
  if (a) {
6022
6923
  const s = toScreen(state, a);
@@ -6025,15 +6926,15 @@ function getVectorOverlayRenderData(state) {
6025
6926
  }
6026
6927
  const handles = getVisibleHandles(state).flatMap((ref) => {
6027
6928
  const a = getAnchorWorld(doc, ref);
6028
- const h = _optionalChain([a, 'optionalAccess', _138 => _138[ref.handle]]);
6929
+ const h = _optionalChain([a, 'optionalAccess', _192 => _192[ref.handle]]);
6029
6930
  if (!a || !h) return [];
6030
6931
  const s = toScreen(state, h);
6031
6932
  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;
6933
+ const hovered = !!_optionalChain([state, 'access', _193 => _193.hover, 'optionalAccess', _194 => _194.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
6033
6934
  return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
6034
6935
  });
6035
6936
  let marquee = null;
6036
- if (_optionalChain([state, 'access', _141 => _141.interaction, 'optionalAccess', _142 => _142.type]) === "marquee") {
6937
+ if (_optionalChain([state, 'access', _195 => _195.interaction, 'optionalAccess', _196 => _196.type]) === "marquee") {
6037
6938
  const b = state.interaction.bounds;
6038
6939
  const tl = toScreen(state, b);
6039
6940
  marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
@@ -6041,7 +6942,7 @@ function getVectorOverlayRenderData(state) {
6041
6942
  let penPreview = null;
6042
6943
  if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
6043
6944
  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;
6945
+ const sp = _optionalChain([node, 'optionalAccess', _197 => _197.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
6045
6946
  const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
6046
6947
  if (last) {
6047
6948
  const a = toScreen(state, last);
@@ -6054,9 +6955,9 @@ function getVectorOverlayRenderData(state) {
6054
6955
  const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
6055
6956
  let status = null;
6056
6957
  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") {
6958
+ if (_optionalChain([it, 'optionalAccess', _198 => _198.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
6959
+ else if (_optionalChain([it, 'optionalAccess', _199 => _199.type]) === "move" || _optionalChain([it, 'optionalAccess', _200 => _200.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
6960
+ else if (_optionalChain([it, 'optionalAccess', _201 => _201.type]) === "scale" || _optionalChain([it, 'optionalAccess', _202 => _202.type]) === "draw") {
6060
6961
  const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
6061
6962
  if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
6062
6963
  }
@@ -6073,25 +6974,39 @@ function getVectorOverlayRenderData(state) {
6073
6974
  }
6074
6975
  }
6075
6976
  let toolStroke = null;
6076
- if (_optionalChain([it, 'optionalAccess', _149 => _149.type]) === "stroke") {
6977
+ if (_optionalChain([it, 'optionalAccess', _203 => _203.type]) === "stroke") {
6077
6978
  const pts = it.points.map((q) => toScreen(state, q));
6078
6979
  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
6980
  toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
6080
6981
  }
6081
6982
  let pencilString = null;
6082
- if (_optionalChain([it, 'optionalAccess', _150 => _150.type]) === "draw" && it.string) {
6983
+ if (_optionalChain([it, 'optionalAccess', _204 => _204.type]) === "draw" && it.string) {
6083
6984
  const a = toScreen(state, it.string[0]);
6084
6985
  const b = toScreen(state, it.string[1]);
6085
6986
  pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
6086
6987
  }
6087
- const dragged = _optionalChain([it, 'optionalAccess', _151 => _151.type]) === "corner" ? it.ref : null;
6988
+ const dragged = _optionalChain([it, 'optionalAccess', _205 => _205.type]) === "corner" ? it.ref : null;
6088
6989
  const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
6089
6990
  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 };
6991
+ return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _206 => _206.hover, 'optionalAccess', _207 => _207.corner]), w.ref), active: !!dragged };
6091
6992
  });
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 };
6993
+ if (_optionalChain([it, 'optionalAccess', _208 => _208.type]) === "corner") status = `R ${fmt(it.radius)}`;
6994
+ const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _209 => _209.hover, 'optionalAccess', _210 => _210.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
6995
+ const sc = (p) => {
6996
+ const q = toScreen(state, p);
6997
+ return { x: fmt(q.x), y: fmt(q.y) };
6998
+ };
6999
+ const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
7000
+ const widthPoints = getVectorWidthHandles(state).map((h) => {
7001
+ const c = sc(h);
7002
+ const hw = _optionalChain([state, 'access', _211 => _211.hover, 'optionalAccess', _212 => _212.widthPoint]);
7003
+ const hovered = hw && sameWidth(hw, h.ref) && hw.subpath === h.ref.subpath ? hw.part : null;
7004
+ 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', _213 => _213.type]) === "width" && sameWidth(it.ref, h.ref) };
7005
+ });
7006
+ const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _214 => _214.hover, 'optionalAccess', _215 => _215.strokeSpot]) : null;
7007
+ const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
7008
+ if (_optionalChain([it, 'optionalAccess', _216 => _216.type]) === "width") status = it.mode === "move" ? "Move width point" : `W ${fmt(it.width)}`;
7009
+ return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot };
6095
7010
  }
6096
7011
  var VECTOR_OVERLAY_THEME = {
6097
7012
  accent: "#0ea5e9",
@@ -6105,13 +7020,23 @@ var VECTOR_OVERLAY_THEME = {
6105
7020
  anchorSize: 7,
6106
7021
  handleRadius: 3.5,
6107
7022
  /** Radius of a live corner widget (a ring with a dot). */
6108
- cornerWidgetRadius: 4
7023
+ cornerWidgetRadius: 4,
7024
+ /** Half the diagonal of a width point's diamond. */
7025
+ widthPointSize: 5
6109
7026
  };
7027
+ function vectorDiamondPath(x, y, r = VECTOR_OVERLAY_THEME.widthPointSize) {
7028
+ 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`;
7029
+ }
6110
7030
  function toVectorModifiers(event) {
6111
7031
  return { shift: !!event.shiftKey, alt: !!event.altKey, mod: !!(event.metaKey || event.ctrlKey) };
6112
7032
  }
6113
7033
  function toVectorPointerInput(event) {
6114
- return { ...toVectorModifiers(event), button: event.button };
7034
+ return {
7035
+ ...toVectorModifiers(event),
7036
+ button: event.button,
7037
+ ...typeof event.pressure === "number" ? { pressure: event.pressure } : {},
7038
+ ...event.pointerType ? { pointerType: event.pointerType } : {}
7039
+ };
6115
7040
  }
6116
7041
  function toVectorKeyInput(event) {
6117
7042
  return { key: event.key, ...toVectorModifiers(event) };
@@ -6121,7 +7046,7 @@ function isVectorTextTarget(target) {
6121
7046
  if (!el || typeof el !== "object") return false;
6122
7047
  const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
6123
7048
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
6124
- return !!el.isContentEditable || _optionalChain([el, 'access', _157 => _157.getAttribute, 'optionalCall', _158 => _158("contenteditable")]) === "true";
7049
+ return !!el.isContentEditable || _optionalChain([el, 'access', _217 => _217.getAttribute, 'optionalCall', _218 => _218("contenteditable")]) === "true";
6125
7050
  }
6126
7051
  function getVectorSelectionStyle(state) {
6127
7052
  const paths = [];
@@ -6129,12 +7054,12 @@ function getVectorSelectionStyle(state) {
6129
7054
  const node = findVectorNode(state.document.nodes, id);
6130
7055
  if (node) paths.push(...collect(node));
6131
7056
  }
6132
- const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _159 => _159.selectedAnchors, 'optionalAccess', _160 => _160.length]);
7057
+ const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _219 => _219.selectedAnchors, 'optionalAccess', _220 => _220.length]);
6133
7058
  if (anchorMode) {
6134
7059
  for (const r of state.selectedAnchors) {
6135
7060
  if (paths.some((p) => p.id === r.nodeId)) continue;
6136
7061
  const node = findVectorNode(state.document.nodes, r.nodeId);
6137
- if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) === "path") paths.push(node);
7062
+ if (_optionalChain([node, 'optionalAccess', _221 => _221.type]) === "path") paths.push(node);
6138
7063
  }
6139
7064
  }
6140
7065
  if (!paths.length) return null;
@@ -6145,7 +7070,7 @@ function getVectorSelectionStyle(state) {
6145
7070
  for (const c of vectorCorners(p.subpaths)) {
6146
7071
  if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
6147
7072
  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")) });
7073
+ corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _222 => _222.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _223 => _223.kind]), () => ( "round")) });
6149
7074
  }
6150
7075
  }
6151
7076
  const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
@@ -6160,6 +7085,8 @@ function getVectorSelectionStyle(state) {
6160
7085
  arrowAlign: _nullishCoalesce(first.arrowAlign, () => ( "extend")),
6161
7086
  startArrowScale: _nullishCoalesce(first.startArrowScale, () => ( 1)),
6162
7087
  endArrowScale: _nullishCoalesce(first.endArrowScale, () => ( 1)),
7088
+ strokeWidthProfile: profileOf(first),
7089
+ widthProfile: widthProfilePresetId(profileOf(first)),
6163
7090
  cornerRadius: c0.radius,
6164
7091
  cornerKind: c0.kind,
6165
7092
  corners: corners2.length,
@@ -6175,12 +7102,17 @@ function getVectorSelectionStyle(state) {
6175
7102
  arrowAlign: !same((p) => _nullishCoalesce(p.arrowAlign, () => ( "extend"))),
6176
7103
  startArrowScale: !same((p) => _nullishCoalesce(p.startArrowScale, () => ( 1))),
6177
7104
  endArrowScale: !same((p) => _nullishCoalesce(p.endArrowScale, () => ( 1))),
7105
+ widthProfile: !same(profileOf),
6178
7106
  cornerRadius: corners2.some((c) => c.radius !== c0.radius),
6179
7107
  cornerKind: corners2.some((c) => c.kind !== c0.kind)
6180
7108
  },
6181
7109
  pathIds: paths.map((p) => p.id)
6182
7110
  };
6183
7111
  }
7112
+ function profileOf(p) {
7113
+ const points = sanitizeWidthProfile(p.strokeWidthProfile);
7114
+ return points.length && widthProfilePresetId(points) !== "uniform" ? points : null;
7115
+ }
6184
7116
  var VECTOR_STROKE_ALIGN_LABELS = { center: "Center", inside: "Inside", outside: "Outside" };
6185
7117
  var VECTOR_CORNER_KIND_LABELS = { round: "Round", inverted: "Inverted", chamfer: "Chamfer" };
6186
7118
  var VECTOR_ARROWHEAD_LABELS = {
@@ -6232,6 +7164,19 @@ var POINTS = {
6232
7164
  minItems: 1,
6233
7165
  description: "Polyline [[x, y], \u2026] in document units."
6234
7166
  };
7167
+ var WIDTH_POINTS = {
7168
+ type: ["array", "null"],
7169
+ items: {
7170
+ type: "object",
7171
+ properties: {
7172
+ t: { type: "number", minimum: 0, maximum: 1, description: "Position along each subpath by length (0 start, 1 end)." },
7173
+ left: { type: "number", minimum: 0, description: "Left side as a multiple of half the stroke width (1 = uniform)." },
7174
+ right: { type: "number", minimum: 0, description: "Right side (looking along the path, y down)." }
7175
+ },
7176
+ required: ["t", "left", "right"]
7177
+ },
7178
+ description: "Variable-width profile points, interpolated smoothly (monotone cubic); two points at one `t` make a step. `null` or `[]` strokes at a uniform width."
7179
+ };
6235
7180
  var PAINT = {
6236
7181
  description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear", angle, stops: [{ offset, color }] }`.'
6237
7182
  };
@@ -6248,7 +7193,8 @@ var STYLE_PROPS = {
6248
7193
  endArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the end of an open path, or null for none." },
6249
7194
  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
7195
  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)." }
7196
+ endArrowScale: { type: "number", exclusiveMinimum: 0, description: "End head size as a multiple of the stroke width (default 1)." },
7197
+ strokeWidthProfile: WIDTH_POINTS
6252
7198
  };
6253
7199
  var pickStyle = (params) => {
6254
7200
  const out = {};
@@ -6264,17 +7210,17 @@ function createVectorCommands(controller, options = {}) {
6264
7210
  const base = { group, ...options.scope ? { scope: options.scope } : {} };
6265
7211
  const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
6266
7212
  const ids = (params) => {
6267
- const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _164 => _164.ids]), () => ( controller.getState().selection));
7213
+ const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _224 => _224.ids]), () => ( controller.getState().selection));
6268
7214
  for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
6269
7215
  if (!list2.length) fail("Nothing is selected; pass ids.");
6270
7216
  return list2;
6271
7217
  };
6272
- const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _165 => _165.ids, 'optionalAccess', _166 => _166.length]) ? true : hasSelection();
7218
+ const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _225 => _225.ids, 'optionalAccess', _226 => _226.length]) ? true : hasSelection();
6273
7219
  const anchorsOf = (refs) => {
6274
7220
  if (!refs) return [...controller.getState().selectedAnchors];
6275
7221
  for (const r of refs) {
6276
7222
  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}".`);
7223
+ if (_optionalChain([node, 'optionalAccess', _227 => _227.type]) !== "path" || !_optionalChain([node, 'access', _228 => _228.subpaths, 'access', _229 => _229[r.subpath], 'optionalAccess', _230 => _230.anchors, 'access', _231 => _231[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
6278
7224
  }
6279
7225
  return refs;
6280
7226
  };
@@ -6365,7 +7311,7 @@ function createVectorCommands(controller, options = {}) {
6365
7311
  run: (_ctx, params) => {
6366
7312
  const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
6367
7313
  const node = controller.getNode(id);
6368
- if (_optionalChain([node, 'optionalAccess', _172 => _172.type]) === "path" && !node.subpaths.length) {
7314
+ if (_optionalChain([node, 'optionalAccess', _232 => _232.type]) === "path" && !node.subpaths.length) {
6369
7315
  controller.undo();
6370
7316
  fail("The path data has no points.");
6371
7317
  }
@@ -6397,14 +7343,52 @@ function createVectorCommands(controller, options = {}) {
6397
7343
  params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
6398
7344
  undoable: true,
6399
7345
  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));
7346
+ const refs = _optionalChain([params, 'optionalAccess', _233 => _233.anchors]) ? anchorsOf(params.anchors) : void 0;
7347
+ const target = { refs, ids: _optionalChain([params, 'optionalAccess', _234 => _234.ids]) ? ids(params) : void 0 };
7348
+ if (_optionalChain([params, 'optionalAccess', _235 => _235.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
7349
+ else if (_optionalChain([params, 'optionalAccess', _236 => _236.kind])) check(controller.setCornerKind(params.kind, target));
6404
7350
  else fail("Pass `radius` and / or `kind`.");
6405
7351
  return { ok: true };
6406
7352
  }
6407
7353
  },
7354
+ {
7355
+ ...base,
7356
+ id: `${prefix}setWidthProfile`,
7357
+ label: "Width Profile\u2026",
7358
+ keywords: ["variable width", "taper", "tapered", "pressure", "profile", "brush", "calligraphy", "bulge", "thick", "thin"],
7359
+ 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.",
7360
+ params: {
7361
+ type: "object",
7362
+ properties: {
7363
+ ids: IDS,
7364
+ preset: { type: "string", enum: VECTOR_WIDTH_PRESET_IDS },
7365
+ points: WIDTH_POINTS,
7366
+ taper: {
7367
+ type: "object",
7368
+ properties: {
7369
+ startLength: { type: "number", minimum: 0, maximum: 1 },
7370
+ endLength: { type: "number", minimum: 0, maximum: 1 },
7371
+ startWidth: { type: "number", minimum: 0, maximum: 1 },
7372
+ endWidth: { type: "number", minimum: 0, maximum: 1 },
7373
+ startEase: { type: "number", minimum: 0, maximum: 1 },
7374
+ endEase: { type: "number", minimum: 0, maximum: 1 }
7375
+ }
7376
+ },
7377
+ wave: { type: "object", properties: { amount: { type: "number", minimum: 0, maximum: 1 }, cycles: { type: "number", exclusiveMinimum: 0 }, phase: { type: "number" } } }
7378
+ }
7379
+ },
7380
+ enabled: enabledFor,
7381
+ undoable: true,
7382
+ run: (_ctx, params) => {
7383
+ const target = { ids: ids(params) };
7384
+ if (_optionalChain([params, 'optionalAccess', _237 => _237.preset])) check(controller.setWidthProfile(params.preset, target));
7385
+ else if (_optionalChain([params, 'optionalAccess', _238 => _238.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
7386
+ else if (_optionalChain([params, 'optionalAccess', _239 => _239.taper]) || _optionalChain([params, 'optionalAccess', _240 => _240.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
7387
+ else fail("Pass `preset`, `points`, `taper` or `wave`.");
7388
+ const node = controller.getNode(target.ids[0]);
7389
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _241 => _241.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
7390
+ }
7391
+ },
6408
7392
  {
6409
7393
  ...base,
6410
7394
  id: `${prefix}outlineStroke`,
@@ -6415,7 +7399,7 @@ function createVectorCommands(controller, options = {}) {
6415
7399
  enabled: enabledFor,
6416
7400
  undoable: true,
6417
7401
  run: (_ctx, params) => {
6418
- const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _177 => _177.ids]) ? ids(params) : void 0 });
7402
+ const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _242 => _242.ids]) ? ids(params) : void 0 });
6419
7403
  if (!created.length) fail("No selected path has a stroke.");
6420
7404
  return { ids: created };
6421
7405
  }
@@ -6530,7 +7514,7 @@ function createVectorCommands(controller, options = {}) {
6530
7514
  enabled: enabledFor,
6531
7515
  undoable: true,
6532
7516
  run: (_ctx, params) => {
6533
- const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _178 => _178.name]) });
7517
+ const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _243 => _243.name]) });
6534
7518
  if (!id) fail("Only nodes that share a parent can be grouped.");
6535
7519
  return { id };
6536
7520
  }
@@ -6558,7 +7542,7 @@ function createVectorCommands(controller, options = {}) {
6558
7542
  params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
6559
7543
  undoable: true,
6560
7544
  run: (_ctx, params) => {
6561
- check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _179 => _179.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _180 => _180.keepShape]) }));
7545
+ check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _244 => _244.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _245 => _245.keepShape]) }));
6562
7546
  return { ok: true };
6563
7547
  }
6564
7548
  },
@@ -6613,7 +7597,7 @@ function createVectorCommands(controller, options = {}) {
6613
7597
  params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
6614
7598
  undoable: true,
6615
7599
  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]) }));
7600
+ check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _246 => _246.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _247 => _247.tolerance]) }));
6617
7601
  return { selection: controller.getState().selection };
6618
7602
  }
6619
7603
  },
@@ -6627,7 +7611,7 @@ function createVectorCommands(controller, options = {}) {
6627
7611
  params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
6628
7612
  undoable: true,
6629
7613
  run: (_ctx, params) => {
6630
- check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _183 => _183.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _184 => _184.anchors]))));
7614
+ check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _248 => _248.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _249 => _249.anchors]))));
6631
7615
  return { ok: true };
6632
7616
  }
6633
7617
  },
@@ -6641,7 +7625,7 @@ function createVectorCommands(controller, options = {}) {
6641
7625
  enabled: enabledFor,
6642
7626
  undoable: true,
6643
7627
  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]) }));
7628
+ check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _250 => _250.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _251 => _251.tolerance]) }));
6645
7629
  return { ok: true };
6646
7630
  }
6647
7631
  },
@@ -6654,7 +7638,7 @@ function createVectorCommands(controller, options = {}) {
6654
7638
  params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
6655
7639
  undoable: true,
6656
7640
  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]) }));
7641
+ check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _252 => _252.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _253 => _253.amount]), refs: _optionalChain([params, 'optionalAccess', _254 => _254.anchors]) }));
6658
7642
  return { ok: true };
6659
7643
  }
6660
7644
  },
@@ -6667,14 +7651,14 @@ function createVectorCommands(controller, options = {}) {
6667
7651
  params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
6668
7652
  undoable: true,
6669
7653
  run: (_ctx, params) => {
6670
- if (_optionalChain([params, 'optionalAccess', _190 => _190.nodeId]) !== void 0) {
7654
+ if (_optionalChain([params, 'optionalAccess', _255 => _255.nodeId]) !== void 0) {
6671
7655
  if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
6672
7656
  if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
6673
7657
  const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
6674
7658
  if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
6675
7659
  return { ids: created };
6676
7660
  }
6677
- check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _191 => _191.anchors]))));
7661
+ check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _256 => _256.anchors]))));
6678
7662
  return { ids: controller.getState().selection };
6679
7663
  }
6680
7664
  },
@@ -6711,7 +7695,7 @@ function createVectorCommands(controller, options = {}) {
6711
7695
  id: `${prefix}setEditing`,
6712
7696
  label: "Path Editing Options\u2026",
6713
7697
  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`.",
7698
+ 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
7699
  params: {
6716
7700
  type: "object",
6717
7701
  properties: {
@@ -6719,12 +7703,13 @@ function createVectorCommands(controller, options = {}) {
6719
7703
  curvatureMoves: { type: "boolean" },
6720
7704
  pencilSmoothing: { type: "number", minimum: 0, maximum: 1 },
6721
7705
  recognizeShapes: { type: "boolean" },
6722
- eraserSize: { type: "number", exclusiveMinimum: 0 }
7706
+ eraserSize: { type: "number", exclusiveMinimum: 0 },
7707
+ pencilPressure: { type: "string", enum: ["pen", "all", "off"] }
6723
7708
  }
6724
7709
  },
6725
7710
  run: (_ctx, params) => {
6726
7711
  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];
7712
+ for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _257 => _257[key]]) !== void 0) patch[key] = params[key];
6728
7713
  controller.setEditing(patch);
6729
7714
  return { editing: controller.getState().editing };
6730
7715
  }
@@ -6749,7 +7734,7 @@ function createVectorCommands(controller, options = {}) {
6749
7734
  keywords: ["download", "save", "markup"],
6750
7735
  description: "Returns the document as SVG markup.",
6751
7736
  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]) }) })
7737
+ run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _258 => _258.precision]) }) })
6753
7738
  },
6754
7739
  {
6755
7740
  ...base,
@@ -6915,5 +7900,37 @@ function createVectorCommands(controller, options = {}) {
6915
7900
 
6916
7901
 
6917
7902
 
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;
7903
+
7904
+
7905
+
7906
+
7907
+
7908
+
7909
+
7910
+
7911
+
7912
+
7913
+
7914
+
7915
+
7916
+
7917
+
7918
+
7919
+
7920
+
7921
+
7922
+
7923
+
7924
+
7925
+
7926
+
7927
+
7928
+
7929
+
7930
+
7931
+
7932
+
7933
+
7934
+
7935
+ 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_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.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.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.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.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.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.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
7936
  //# sourceMappingURL=index.cjs.map