@pixerate/editor 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,20 +27,994 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
27
27
 
28
28
 
29
29
 
30
- var _chunkBGNPT5PEcjs = require('../chunk-BGNPT5PE.cjs');
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+ var _chunkGVB7ZPLZcjs = require('../chunk-GVB7ZPLZ.cjs');
31
38
 
32
39
  // src/vector/types.ts
33
- var VECTOR_TOOLS = ["select", "direct", "pen", "pencil", "rect", "ellipse", "polygon", "star", "line", "scissors", "knife", "eraser"];
40
+ var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
41
+ var VECTOR_ARROWHEADS = ["triangle", "arrow", "open", "circle", "square", "bar"];
42
+ var VECTOR_TOOLS = ["select", "direct", "pen", "pencil", "rect", "ellipse", "polygon", "star", "line", "scissors", "knife", "eraser", "width"];
34
43
  var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
35
44
 
36
- // src/vector/geometry.ts
45
+ // src/vector/corners.ts
37
46
  var EPS = 1e-9;
47
+ var MIN_SINE = 1e-6;
48
+ var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
49
+ var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
50
+ var mul = (a, k) => ({ x: a.x * k, y: a.y * k });
51
+ var dot = (a, b) => a.x * b.x + a.y * b.y;
52
+ var cross = (a, b) => a.x * b.y - a.y * b.x;
53
+ var len = (a) => Math.hypot(a.x, a.y);
54
+ var pt = (p) => [p.x, p.y];
55
+ var vec = (p) => ({ x: p[0], y: p[1] });
56
+ var onAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS && Math.abs(h.y - a.y) < EPS;
57
+ var Side = class {
58
+
59
+
60
+
61
+ constructor(a, h1, h2, b) {
62
+ this.straight = onAnchor(a, h1) && onAnchor(b, h2);
63
+ this.curve = this.straight ? [pt(a), pt(b)] : [pt(a), pt(_nullishCoalesce(h1, () => ( a))), pt(_nullishCoalesce(h2, () => ( b))), pt(b)];
64
+ this.length = this.straight ? Math.hypot(b.x - a.x, b.y - a.y) : _chunkGVB7ZPLZcjs.segmentLength.call(void 0, this.curve);
65
+ }
66
+ /** Unit direction the side leaves the corner in (towards its first control point off the corner). */
67
+ direction() {
68
+ const p0 = vec(this.curve[0]);
69
+ for (const q of this.curve.slice(1)) {
70
+ const d = sub(vec(q), p0);
71
+ const l = len(d);
72
+ if (l > EPS) return mul(d, 1 / l);
73
+ }
74
+ return null;
75
+ }
76
+ /** The point `s` along the side from the corner: its parameter, position and the direction the side runs on in there. */
77
+ at(s, u) {
78
+ if (this.straight) {
79
+ const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
80
+ return { t: t2, p: add(vec(this.curve[0]), mul(u, s)), dir: u };
81
+ }
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));
84
+ const l = len(d);
85
+ return { t, p: vec(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, this.curve, t)), dir: l > EPS ? mul(d, 1 / l) : u };
86
+ }
87
+ };
88
+ function cornerOf(sp, s, i) {
89
+ const n = sp.anchors.length;
90
+ if (n < 2 || !sp.closed && (i === 0 || i === n - 1)) return null;
91
+ if (n === 2 && sp.closed) return null;
92
+ const a = sp.anchors[i];
93
+ if (a.type === "smooth" || a.type === "symmetric") return null;
94
+ const prev = sp.anchors[(i - 1 + n) % n];
95
+ const next = sp.anchors[(i + 1) % n];
96
+ const sideU = new Side(a, a.in, prev.out, prev);
97
+ const sideV = new Side(a, a.out, next.in, next);
98
+ const u = sideU.direction();
99
+ const v2 = sideV.direction();
100
+ if (!u || !v2) return null;
101
+ const lu = sideU.length;
102
+ const lv = sideV.length;
103
+ if (!(lu > EPS && lv > EPS) || !Number.isFinite(lu) || !Number.isFinite(lv)) return null;
104
+ const cos = Math.max(-1, Math.min(1, dot(u, v2)));
105
+ const sin = Math.abs(cross(u, v2));
106
+ if (sin <= MIN_SINE) return null;
107
+ return { subpath: s, index: i, at: { x: a.x, y: a.y }, u, v: v2, lu, lv, cos, sin, angle: Math.atan2(sin, cos) * 180 / Math.PI, maxRadius: Math.min(lu, lv) / 2 * sin / (1 + cos), sideU, sideV };
108
+ }
109
+ var strip = ({ sideU: _u, sideV: _v, ...info }) => info;
110
+ function vectorCornerAt(sp, index, subpath = 0) {
111
+ const c = cornerOf(sp, subpath, index);
112
+ return c ? strip(c) : null;
113
+ }
114
+ function vectorCorners(subpaths) {
115
+ const out = [];
116
+ subpaths.forEach((sp, s) => {
117
+ for (let i = 0; i < sp.anchors.length; i++) {
118
+ const c = cornerOf(sp, s, i);
119
+ if (c) out.push(strip(c));
120
+ }
121
+ });
122
+ return out;
123
+ }
124
+ var cornerSetback = (c, r) => r * (1 + c.cos) / c.sin;
125
+ var fitCornerRadius = (c, radius) => Number.isFinite(radius) ? Math.min(Math.max(0, radius), c.maxRadius) : 0;
126
+ var cornerBisectorPoint = (c, k) => add(c.at, mul(add(c.u, c.v), k));
127
+ var cornerRadiusChange = (c, d) => dot(d, add(c.u, c.v)) * c.sin / (2 + 2 * c.cos);
128
+ function arcHandle(cos) {
129
+ const c = Math.max(-1, Math.min(1, cos));
130
+ const sinHalf = Math.sqrt(Math.max(0, (1 - c) / 2));
131
+ const cosHalf = Math.sqrt(Math.max(0, (1 + c) / 2));
132
+ return 4 / 3 * sinHalf / (1 + cosHalf);
133
+ }
134
+ function squareTo(a, b) {
135
+ const w = sub(b, mul(a, dot(a, b)));
136
+ const l = len(w);
137
+ return l > EPS ? mul(w, 1 / l) : { x: 0, y: 0 };
138
+ }
139
+ function cutCorner(c, radius, kind) {
140
+ const s = Math.min(cornerSetback(c, radius), Math.min(c.lu, c.lv) / 2);
141
+ if (!(s > EPS)) return null;
142
+ const a = c.sideU.at(s, c.u);
143
+ const b = c.sideV.at(s, c.v);
144
+ if (kind === "chamfer") return { tu: a.t, tv: b.t, start: a.p, out: null, in: null, end: b.p };
145
+ if (kind === "inverted") {
146
+ const h = s * arcHandle(dot(a.dir, b.dir));
147
+ return { tu: a.t, tv: b.t, start: a.p, out: add(a.p, mul(squareTo(a.dir, b.dir), h)), in: add(b.p, mul(squareTo(b.dir, a.dir), h)), end: b.p };
148
+ }
149
+ const d1 = mul(a.dir, -1);
150
+ const d2 = mul(b.dir, -1);
151
+ const turn = Math.acos(Math.max(-1, Math.min(1, -dot(d1, d2))));
152
+ const chord = len(sub(b.p, a.p));
153
+ const k = turn < 1e-9 ? chord / 3 : 4 / 3 * Math.tan(turn / 4) * chord / (2 * Math.sin(turn / 2));
154
+ if (!Number.isFinite(k)) return null;
155
+ return { tu: a.t, tv: b.t, start: a.p, out: add(a.p, mul(d1, k)), in: add(b.p, mul(d2, k)), end: b.p };
156
+ }
157
+ var radiusOf = (a) => {
158
+ const r = _optionalChain([a, 'access', _2 => _2.corner, 'optionalAccess', _3 => _3.radius]);
159
+ return typeof r === "number" && r > 0 && Number.isFinite(r) ? r : 0;
160
+ };
161
+ function hasVectorCorners(subpaths) {
162
+ return subpaths.some((sp) => sp.anchors.some((a) => radiusOf(a) > 0));
163
+ }
164
+ function expandVectorCornersOf(sp, subpath = 0) {
165
+ const n = sp.anchors.length;
166
+ const cuts = sp.anchors.map((a, i) => {
167
+ const r = radiusOf(a);
168
+ if (!r) return null;
169
+ const c = cornerOf(sp, subpath, i);
170
+ if (!c) return null;
171
+ const fitted = fitCornerRadius(c, r);
172
+ return fitted > EPS ? cutCorner(c, fitted, _nullishCoalesce(_optionalChain([a, 'access', _4 => _4.corner, 'optionalAccess', _5 => _5.kind]), () => ( "round"))) : null;
173
+ });
174
+ if (!cuts.some(Boolean)) return { subpath: { closed: sp.closed, anchors: sp.anchors.map(plain) }, sources: sp.anchors.map((_, i) => i) };
175
+ const trimmed = sp.anchors.map((a, j) => {
176
+ const k = (j + 1) % n;
177
+ if (k === 0 && !sp.closed) return void 0;
178
+ const b = sp.anchors[k];
179
+ const from = cuts[j];
180
+ const to = cuts[k];
181
+ if (!from && !to) return void 0;
182
+ if (onAnchor(a, a.out) && onAnchor(b, b.in)) return { out: null, in: null };
183
+ const s0 = from ? from.tv : 0;
184
+ const s1 = to ? 1 - to.tu : 1;
185
+ const seg = [pt(a), pt(_nullishCoalesce(a.out, () => ( a))), pt(_nullishCoalesce(b.in, () => ( b))), pt(b)];
186
+ const part = _chunkGVB7ZPLZcjs.subSegment.call(void 0, seg, s0, Math.max(s0, s1));
187
+ return { out: vec(part[1]), in: vec(part[2]) };
188
+ });
189
+ const anchors = [];
190
+ const sources = [];
191
+ let closing = null;
192
+ const handleIn = (i, a) => {
193
+ const t = trimmed[(i - 1 + n) % n];
194
+ return t ? t.in : _nullishCoalesce(a.in, () => ( null));
195
+ };
196
+ const handleOut = (i, a) => {
197
+ const t = trimmed[i];
198
+ return t ? t.out : _nullishCoalesce(a.out, () => ( null));
199
+ };
200
+ sp.anchors.forEach((a, i) => {
201
+ const c = cuts[i];
202
+ if (!c) {
203
+ anchors.push(withHandles({ x: a.x, y: a.y, ...a.type ? { type: a.type } : {} }, handleIn(i, a), handleOut(i, a)));
204
+ sources.push(i);
205
+ return;
206
+ }
207
+ const start = withHandles({ x: c.start.x, y: c.start.y }, handleIn(i, a), c.out);
208
+ const end = withHandles({ x: c.end.x, y: c.end.y }, c.in, handleOut(i, a));
209
+ if (i === 0 && sp.closed) closing = start;
210
+ else {
211
+ anchors.push(start);
212
+ sources.push(i);
213
+ }
214
+ anchors.push(end);
215
+ sources.push(i);
216
+ });
217
+ if (closing) {
218
+ anchors.push(closing);
219
+ sources.push(0);
220
+ }
221
+ return { subpath: { closed: sp.closed, anchors }, sources };
222
+ }
223
+ function withHandles(a, hin, hout) {
224
+ if (hin && !onAnchor(a, hin)) a.in = { x: hin.x, y: hin.y };
225
+ if (hout && !onAnchor(a, hout)) a.out = { x: hout.x, y: hout.y };
226
+ return a;
227
+ }
228
+ function plain(a) {
229
+ if (!("corner" in a)) return a;
230
+ const { corner: _c, ...rest } = a;
231
+ return rest;
232
+ }
233
+ function expandVectorCorners(subpaths) {
234
+ if (!hasVectorCorners(subpaths)) return subpaths;
235
+ return subpaths.map((sp, s) => sp.anchors.some((a) => radiusOf(a) > 0) ? expandVectorCornersOf(sp, s).subpath : sp);
236
+ }
237
+ function setAnchorCorner(a, radius, kind) {
238
+ const { corner, ...rest } = a;
239
+ const r = Number.isFinite(radius) ? Math.max(0, radius) : 0;
240
+ const k = _nullishCoalesce(kind, () => ( _optionalChain([corner, 'optionalAccess', _6 => _6.kind])));
241
+ if (r <= 0 && !kind) return rest;
242
+ return { ...rest, corner: { radius: r, ...k && k !== "round" ? { kind: k } : {} } };
243
+ }
244
+ function nextVectorCornerKind(kind) {
245
+ return kind === "inverted" ? "chamfer" : kind === "chamfer" ? "round" : "inverted";
246
+ }
247
+ function setSubpathCorners(sp, radius, kind) {
248
+ return { closed: sp.closed, anchors: sp.anchors.map((a) => setAnchorCorner(a, radius, kind)) };
249
+ }
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
+
780
+ // src/vector/stroke.ts
781
+ var KAPPA = 0.5522847498307936;
782
+ var NOTCH = 0.7;
783
+ function isClosedBezierShape(path) {
784
+ const contours = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
785
+ return contours.length > 0 && contours.every((c) => c.closed);
786
+ }
787
+ function effectiveStrokeAlign(style, closedShape) {
788
+ const a = _nullishCoalesce(style.strokeAlign, () => ( "center"));
789
+ return closedShape ? a : "center";
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
+ }
796
+ function hasStrokeExtras(style) {
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 } : {} });
806
+ }
807
+ var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
808
+ function arrowheadWeight(style, end) {
809
+ const scale = end === "start" ? style.startArrowScale : style.endArrowScale;
810
+ const s = typeof scale === "number" && scale > 0 && Number.isFinite(scale) ? scale : 1;
811
+ return Math.max(0.25, (_nullishCoalesce(style.strokeWidth, () => ( 1))) * s);
812
+ }
813
+ function arrowheadInset(kind, hw, cap) {
814
+ const l = 4 * hw;
815
+ const solid = (back) => Math.max(back / 2, cap);
816
+ switch (kind) {
817
+ case "triangle":
818
+ case "circle":
819
+ case "square":
820
+ return solid(l);
821
+ case "arrow":
822
+ return solid(NOTCH * l);
823
+ case "bar":
824
+ return solid(0.75 * hw);
825
+ case "open":
826
+ return Math.max(hw * Math.sqrt(l * l + l * l / 4) / (l / 2), cap);
827
+ }
828
+ }
829
+ var v = (x, y) => ({ x, y });
830
+ var plus = (a, b, k = 1) => ({ x: a.x + b.x * k, y: a.y + b.y * k });
831
+ var unit = (a) => {
832
+ const l = Math.hypot(a.x, a.y);
833
+ return l > 0 ? { x: a.x / l, y: a.y / l } : { x: 1, y: 0 };
834
+ };
835
+ function polygon(pts) {
836
+ return [{ type: "M", x: pts[0].x, y: pts[0].y }, ...pts.slice(1).map((p) => ({ type: "L", x: p.x, y: p.y })), { type: "Z" }];
837
+ }
838
+ function circle(c, r) {
839
+ const k = r * KAPPA;
840
+ const { x, y } = c;
841
+ return [
842
+ { type: "M", x: x + r, y },
843
+ { type: "C", x1: x + r, y1: y + k, x2: x + k, y2: y + r, x, y: y + r },
844
+ { type: "C", x1: x - k, y1: y + r, x2: x - r, y2: y + k, x: x - r, y },
845
+ { type: "C", x1: x - r, y1: y - k, x2: x - k, y2: y - r, x, y: y - r },
846
+ { type: "C", x1: x + k, y1: y - r, x2: x + r, y2: y - k, x: x + r, y },
847
+ { type: "Z" }
848
+ ];
849
+ }
850
+ function arrowheadPath(kind, tip, dir, hw) {
851
+ const n = v(-dir.y, dir.x);
852
+ const l = 4 * hw;
853
+ const half = l / 2;
854
+ const base = plus(tip, dir, -l);
855
+ switch (kind) {
856
+ case "triangle":
857
+ return polygon([tip, plus(base, n, half), plus(base, n, -half)]);
858
+ case "arrow":
859
+ return polygon([tip, plus(base, n, half), plus(tip, dir, -NOTCH * l), plus(base, n, -half)]);
860
+ case "open": {
861
+ const t = hw;
862
+ const a = plus(base, n, half);
863
+ const b = plus(base, n, -half);
864
+ const ua = unit(plus(a, tip, -1));
865
+ const ub = unit(plus(b, tip, -1));
866
+ const na = v(-ua.y, ua.x);
867
+ const nb = v(ub.y, -ub.x);
868
+ const inner = plus(tip, dir, -(t * Math.hypot(a.x - tip.x, a.y - tip.y)) / half);
869
+ return polygon([tip, a, plus(a, na, t), inner, plus(b, nb, t), b]);
870
+ }
871
+ case "circle":
872
+ return circle(plus(tip, dir, -half), half);
873
+ case "square":
874
+ return polygon([plus(tip, n, half), plus(base, n, half), plus(base, n, -half), plus(tip, n, -half)]);
875
+ case "bar": {
876
+ const t = 0.75 * hw;
877
+ return polygon([plus(tip, n, half), plus(plus(tip, n, half), dir, -t), plus(plus(tip, n, -half), dir, -t), plus(tip, n, -half)]);
878
+ }
879
+ }
880
+ }
881
+ var toVec = (p) => ({ x: p[0], y: p[1] });
882
+ var segLen = (s) => _chunkGVB7ZPLZcjs.segmentLength.call(void 0, s);
883
+ function endOf(segs, atStart) {
884
+ const long = (s2) => segLen(s2) > 1e-9;
885
+ if (atStart) {
886
+ const s2 = _nullishCoalesce(segs.find(long), () => ( segs[0]));
887
+ const t2 = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, s2, 0);
888
+ return { p: toVec(segs[0][0]), out: v(-t2[0], -t2[1]) };
889
+ }
890
+ const s = _nullishCoalesce([...segs].reverse().find(long), () => ( segs[segs.length - 1]));
891
+ const t = _chunkGVB7ZPLZcjs.segmentTangent.call(void 0, s, 1);
892
+ const last = segs[segs.length - 1];
893
+ return { p: toVec(last[last.length - 1]), out: v(t[0], t[1]) };
894
+ }
895
+ function pointAlong(segs, fromStart, dist4) {
896
+ const order = fromStart ? segs : [...segs].reverse();
897
+ let acc = 0;
898
+ for (const s of order) {
899
+ const l = segLen(s);
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);
903
+ const p = pts.length ? pts[0][pts[0].length - 1] : s[0];
904
+ return toVec(p);
905
+ }
906
+ acc += l;
907
+ }
908
+ return null;
909
+ }
910
+ function vectorStrokeParts(path, style) {
911
+ const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
912
+ const contours = _chunkGVB7ZPLZcjs.pathToContours.call(void 0, path).filter((c) => c.segments.length > 0);
913
+ const closedShape = contours.length > 0 && contours.every((c) => c.closed);
914
+ const align = effectiveStrokeAlign(style, closedShape);
915
+ const drawWidth = align === "center" ? width : width * 2;
916
+ const heads = [];
917
+ if (!contours.length || !style.startArrow && !style.endArrow) return { align, line: path, drawWidth, heads };
918
+ const first = contours[0];
919
+ const last = contours[contours.length - 1];
920
+ const cap = capExtent(style, width);
921
+ const tipMode = style.arrowAlign === "tip";
922
+ const trims = contours.map(() => [0, 0]);
923
+ const place = (end, contour, ci) => {
924
+ const kind = end === "start" ? style.startArrow : style.endArrow;
925
+ if (!kind || contour.closed) return;
926
+ const hw = arrowheadWeight(style, end);
927
+ const inset = arrowheadInset(kind, hw, cap);
928
+ const atStart = end === "start";
929
+ const { p, out } = endOf(contour.segments, atStart);
930
+ let tip;
931
+ let dir;
932
+ if (tipMode) {
933
+ const q = pointAlong(contour.segments, atStart, inset);
934
+ const d = q ? v(p.x - q.x, p.y - q.y) : null;
935
+ dir = d && Math.hypot(d.x, d.y) > 1e-9 ? unit(d) : out;
936
+ tip = p;
937
+ trims[ci][atStart ? 0 : 1] = inset;
938
+ } else {
939
+ tip = plus(p, out, inset);
940
+ dir = out;
941
+ }
942
+ heads.push({ end, kind, tip, dir, inset, path: arrowheadPath(kind, tip, dir, hw) });
943
+ };
944
+ place("start", first, 0);
945
+ place("end", last, contours.length - 1);
946
+ if (!tipMode || !heads.length) return { align, line: path, drawWidth, heads };
947
+ const trimmed = contours.map((c, i) => {
948
+ const [s0, s1] = trims[i];
949
+ if (!s0 && !s1) return c;
950
+ const lengths = c.segments.map(segLen);
951
+ const total = lengths.reduce((a, b) => a + b, 0);
952
+ return { closed: false, segments: total - s1 > s0 ? _chunkGVB7ZPLZcjs.trimSegments.call(void 0, c.segments, s0, total - s1, lengths) : [] };
953
+ });
954
+ return { align, line: _chunkGVB7ZPLZcjs.contoursToPath.call(void 0, trimmed), drawWidth, heads };
955
+ }
956
+ function vectorStrokeOutlinePath(path, style) {
957
+ const parts = vectorStrokeParts(path, style);
958
+ const stroke = vectorStrokeLineArea(parts, style);
959
+ const fill = { path, fillRule: _nullishCoalesce(style.fillRule, () => ( "nonzero")) };
960
+ let area = stroke;
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;
963
+ if (!parts.heads.length) return area;
964
+ return _chunkGVB7ZPLZcjs.pathBoolean.call(void 0, "union", [area, ...parts.heads.map((h) => h.path)]).path;
965
+ }
966
+ function hitTestVectorStroke(path, p, style, tolerance = 0) {
967
+ const parts = vectorStrokeParts(path, style);
968
+ const point = [p.x, p.y];
969
+ const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
970
+ for (const h of parts.heads) {
971
+ if (_chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, h.path, point) || tolerance > 0 && _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, h.path, point) <= tolerance) return true;
972
+ }
973
+ if (!parts.line.length) 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;
994
+ if (d <= tolerance) return true;
995
+ const inside = _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(style.fillRule, () => ( "nonzero")));
996
+ return parts.align === "inside" ? inside : !inside;
997
+ }
998
+ function vectorStrokeReach(style) {
999
+ const width = (_nullishCoalesce(style.strokeWidth, () => ( 1))) * widthProfileMaxScale(strokeWidthProfileOf(style));
1000
+ const align = _nullishCoalesce(style.strokeAlign, () => ( "center"));
1001
+ let reach = align === "center" ? width / 2 : align === "outside" ? width : 0;
1002
+ if (style.strokeLinejoin !== "round" && style.strokeLinejoin !== "bevel") reach *= 4;
1003
+ for (const end of ["start", "end"]) {
1004
+ if (end === "start" ? style.startArrow : style.endArrow) reach = Math.max(reach, 8 * arrowheadWeight(style, end) + width);
1005
+ }
1006
+ return reach;
1007
+ }
1008
+
1009
+ // src/vector/geometry.ts
1010
+ var EPS2 = 1e-9;
38
1011
  var cloneAnchor = (a) => ({
39
1012
  x: a.x,
40
1013
  y: a.y,
41
1014
  ...a.in ? { in: { x: a.in.x, y: a.in.y } } : {},
42
1015
  ...a.out ? { out: { x: a.out.x, y: a.out.y } } : {},
43
- ...a.type && a.type !== "corner" ? { type: a.type } : {}
1016
+ ...a.type && a.type !== "corner" ? { type: a.type } : {},
1017
+ ...a.corner ? { corner: { ...a.corner } } : {}
44
1018
  });
45
1019
  var cloneSubpath = (s) => ({ anchors: s.anchors.map(cloneAnchor), closed: s.closed });
46
1020
  function cloneVectorNode(node) {
@@ -58,7 +1032,7 @@ function hasPaint(paint) {
58
1032
  return true;
59
1033
  }
60
1034
  var handleOf = (a, side) => _nullishCoalesce(a[side], () => ( { x: a.x, y: a.y }));
61
- var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS && Math.abs(h.y - a.y) < EPS;
1035
+ var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS2 && Math.abs(h.y - a.y) < EPS2;
62
1036
  function subpathSegmentCount(sp) {
63
1037
  const n = sp.anchors.length;
64
1038
  if (n < 2) return 0;
@@ -72,9 +1046,9 @@ function subpathSegment(sp, i) {
72
1046
  const c2 = handleOf(b, "in");
73
1047
  return [[a.x, a.y], [c1.x, c1.y], [c2.x, c2.y], [b.x, b.y]];
74
1048
  }
75
- function subpathsToBezier(subpaths) {
1049
+ function subpathsToBezier(subpaths, options = {}) {
76
1050
  const out = [];
77
- for (const sp of subpaths) {
1051
+ for (const sp of options.corners === false ? subpaths : expandVectorCorners(subpaths)) {
78
1052
  if (sp.anchors.length === 0) continue;
79
1053
  const first = sp.anchors[0];
80
1054
  out.push({ type: "M", x: first.x, y: first.y });
@@ -94,7 +1068,7 @@ function vectorNodeToBezier(node) {
94
1068
  }
95
1069
  function vectorPathData(input, precision = 3) {
96
1070
  const subpaths = Array.isArray(input) ? input : input.subpaths;
97
- return _chunkBGNPT5PEcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
1071
+ return _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, subpathsToBezier(subpaths), { precision });
98
1072
  }
99
1073
  function inferAnchorType(a) {
100
1074
  if (isOnAnchor(a, a.in) || isOnAnchor(a, a.out)) return "corner";
@@ -104,13 +1078,13 @@ function inferAnchorType(a) {
104
1078
  const oy = a.out.y - a.y;
105
1079
  const li = Math.hypot(ix, iy);
106
1080
  const lo = Math.hypot(ox, oy);
107
- const cross = (ix * oy - iy * ox) / (li * lo);
108
- const dot = (ix * ox + iy * oy) / (li * lo);
109
- if (Math.abs(cross) > 1e-3 || dot > 0) return "corner";
1081
+ const cross2 = (ix * oy - iy * ox) / (li * lo);
1082
+ const dot2 = (ix * ox + iy * oy) / (li * lo);
1083
+ if (Math.abs(cross2) > 1e-3 || dot2 > 0) return "corner";
110
1084
  return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
111
1085
  }
112
1086
  function bezierToSubpaths(input) {
113
- const path = _chunkBGNPT5PEcjs.toBezierPath.call(void 0, input);
1087
+ const path = _chunkGVB7ZPLZcjs.toBezierPath.call(void 0, input);
114
1088
  const out = [];
115
1089
  let current = null;
116
1090
  const last = () => current.anchors[current.anchors.length - 1];
@@ -207,14 +1181,14 @@ function nodeMatrix(node) {
207
1181
  function vectorPathWorldBezier(node, parent = IDENTITY_MATRIX) {
208
1182
  const m = multiplyMatrices(nodeMatrix(node), parent);
209
1183
  const path = subpathsToBezier(node.subpaths);
210
- return isIdentityMatrix(m) ? path : _chunkBGNPT5PEcjs.transformBezierPath.call(void 0, path, m);
1184
+ return isIdentityMatrix(m) ? path : _chunkGVB7ZPLZcjs.transformBezierPath.call(void 0, path, m);
211
1185
  }
212
1186
  function vectorNodeBounds(node, parent = IDENTITY_MATRIX) {
213
1187
  if (node.type === "group") {
214
1188
  const m = multiplyMatrices(nodeMatrix(node), parent);
215
1189
  return unionBounds(node.children.map((c) => vectorNodeBounds(c, m)));
216
1190
  }
217
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
1191
+ const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, vectorPathWorldBezier(node, parent));
218
1192
  if (!b) return null;
219
1193
  return { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY };
220
1194
  }
@@ -248,9 +1222,13 @@ function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX) {
248
1222
  const path = vectorPathWorldBezier(node, parent);
249
1223
  if (path.length === 0) return false;
250
1224
  const point = [p.x, p.y];
251
- if (hasPaint(node.fill) && _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
252
- const reach = (hasPaint(node.stroke) ? (_nullishCoalesce(node.strokeWidth, () => ( 1))) / 2 : 0) + tolerance;
253
- return _chunkBGNPT5PEcjs.distanceToBezierPath.call(void 0, path, point) <= reach;
1225
+ if (hasPaint(node.fill) && _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, path, point, _nullishCoalesce(node.fillRule, () => ( "nonzero")))) return true;
1226
+ if (hasPaint(node.stroke)) return hitTestVectorStroke(path, p, node, tolerance);
1227
+ return _chunkGVB7ZPLZcjs.distanceToBezierPath.call(void 0, path, point) <= tolerance;
1228
+ }
1229
+ function vectorStrokeOutline(node, parent = IDENTITY_MATRIX) {
1230
+ if (!hasPaint(node.stroke) || !((_nullishCoalesce(node.strokeWidth, () => ( 1))) > 0)) return [];
1231
+ return vectorStrokeOutlinePath(vectorPathWorldBezier(node, parent), node);
254
1232
  }
255
1233
  function hitTestVectorNodes(nodes, p, tolerance = 0) {
256
1234
  for (let i = nodes.length - 1; i >= 0; i--) {
@@ -266,10 +1244,10 @@ function nearestSegment(node, p) {
266
1244
  const count = subpathSegmentCount(sp);
267
1245
  for (let i = 0; i < count; i++) {
268
1246
  const seg = subpathSegment(sp, i);
269
- 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]);
270
1248
  if (!best || hit.distance < best.distance) {
271
- const pt = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, hit.t);
272
- best = { subpath: s, segment: i, t: hit.t, point: { x: pt[0], y: pt[1] }, distance: hit.distance };
1249
+ const pt2 = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, hit.t);
1250
+ best = { subpath: s, segment: i, t: hit.t, point: { x: pt2[0], y: pt2[1] }, distance: hit.distance };
273
1251
  }
274
1252
  }
275
1253
  });
@@ -303,8 +1281,8 @@ function invertMatrix(m) {
303
1281
  if (!det || !Number.isFinite(det)) return IDENTITY_MATRIX;
304
1282
  return [d / det, -b / det, -c / det, a / det, (c * f - d * e) / det, (b * e - a * f) / det];
305
1283
  }
306
- function applyMatrixLinear(m, v) {
307
- return { x: m[0] * v.x + m[2] * v.y, y: m[1] * v.x + m[3] * v.y };
1284
+ function applyMatrixLinear(m, v2) {
1285
+ return { x: m[0] * v2.x + m[2] * v2.y, y: m[1] * v2.x + m[3] * v2.y };
308
1286
  }
309
1287
  function matrixRotation(m) {
310
1288
  const [a, b, c, d] = m;
@@ -328,8 +1306,10 @@ function bakeIntoGeometry(node, m) {
328
1306
  if (isIdentityMatrix(m)) return node;
329
1307
  if (node.type === "group") return { ...node, children: node.children.map((c) => transformVectorNode(c, m)) };
330
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]);
331
1310
  return {
332
1311
  ...path,
1312
+ ...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
333
1313
  subpaths: path.subpaths.map((sp) => ({
334
1314
  closed: sp.closed,
335
1315
  anchors: sp.anchors.map((a) => {
@@ -338,6 +1318,7 @@ function bakeIntoGeometry(node, m) {
338
1318
  if (a.in) next.in = applyMatrix(m, a.in);
339
1319
  if (a.out) next.out = applyMatrix(m, a.out);
340
1320
  if (a.type) next.type = a.type;
1321
+ if (a.corner) next.corner = { ...a.corner };
341
1322
  return next;
342
1323
  })
343
1324
  }))
@@ -389,12 +1370,12 @@ function moveAnchorHandle(a, side, pos, breakHandles = false) {
389
1370
  if (breakHandles) delete next.type;
390
1371
  const dx = pos.x - a.x;
391
1372
  const dy = pos.y - a.y;
392
- const len = Math.hypot(dx, dy);
1373
+ const len2 = Math.hypot(dx, dy);
393
1374
  if (type === "symmetric") next[other] = { x: a.x - dx, y: a.y - dy };
394
- else if (type === "smooth" && len > EPS) {
1375
+ else if (type === "smooth" && len2 > EPS2) {
395
1376
  const o = a[other];
396
- const olen = o ? Math.hypot(o.x - a.x, o.y - a.y) : len;
397
- next[other] = { x: a.x - dx / len * olen, y: a.y - dy / len * olen };
1377
+ const olen = o ? Math.hypot(o.x - a.x, o.y - a.y) : len2;
1378
+ next[other] = { x: a.x - dx / len2 * olen, y: a.y - dy / len2 * olen };
398
1379
  }
399
1380
  if (isOnAnchor(next, next.in)) delete next.in;
400
1381
  if (isOnAnchor(next, next.out)) delete next.out;
@@ -421,15 +1402,15 @@ function convertAnchor(sp, index, type) {
421
1402
  else if (prev && next) dir = { x: next.x - prev.x, y: next.y - prev.y };
422
1403
  else if (next) dir = { x: next.x - a.x, y: next.y - a.y };
423
1404
  else if (prev) dir = { x: a.x - prev.x, y: a.y - prev.y };
424
- if (!dir || Math.hypot(dir.x, dir.y) < EPS) return { anchors, closed: sp.closed };
1405
+ if (!dir || Math.hypot(dir.x, dir.y) < EPS2) return { anchors, closed: sp.closed };
425
1406
  const dl = Math.hypot(dir.x, dir.y);
426
1407
  const ux = dir.x / dl;
427
1408
  const uy = dir.y / dl;
428
1409
  lenIn = a.in && !isOnAnchor(a, a.in) ? Math.hypot(a.in.x - a.x, a.in.y - a.y) : prev ? Math.hypot(prev.x - a.x, prev.y - a.y) / 3 : 0;
429
1410
  lenOut = a.out && !isOnAnchor(a, a.out) ? Math.hypot(a.out.x - a.x, a.out.y - a.y) : next ? Math.hypot(next.x - a.x, next.y - a.y) / 3 : 0;
430
1411
  if (type === "symmetric") lenIn = lenOut = (lenIn + lenOut) / 2 || Math.max(lenIn, lenOut);
431
- if (lenIn > EPS) a.in = { x: a.x - ux * lenIn, y: a.y - uy * lenIn };
432
- if (lenOut > EPS) a.out = { x: a.x + ux * lenOut, y: a.y + uy * lenOut };
1412
+ if (lenIn > EPS2) a.in = { x: a.x - ux * lenIn, y: a.y - uy * lenIn };
1413
+ if (lenOut > EPS2) a.out = { x: a.x + ux * lenOut, y: a.y + uy * lenOut };
433
1414
  a.type = type;
434
1415
  return { anchors, closed: sp.closed };
435
1416
  }
@@ -439,7 +1420,7 @@ function insertAnchor(sp, segment, t) {
439
1420
  const ia = segment;
440
1421
  const ib = (segment + 1) % n;
441
1422
  const seg = subpathSegment(sp, segment);
442
- const [left, right] = _chunkBGNPT5PEcjs.splitSegment.call(void 0, seg, t);
1423
+ const [left, right] = _chunkGVB7ZPLZcjs.splitSegment.call(void 0, seg, t);
443
1424
  const mid = left[left.length - 1];
444
1425
  const anchor = { x: mid[0], y: mid[1] };
445
1426
  if (seg.length === 4) {
@@ -465,37 +1446,24 @@ function reverseSubpath(sp) {
465
1446
  if (a.out) next.in = { ...a.out };
466
1447
  if (a.in) next.out = { ...a.in };
467
1448
  if (a.type) next.type = a.type;
1449
+ if (a.corner) next.corner = { ...a.corner };
468
1450
  return next;
469
1451
  });
470
1452
  return { anchors, closed: sp.closed };
471
1453
  }
472
- var KAPPA = 0.5522847498307936;
473
- function rectSubpath(b, radius = 0) {
1454
+ var KAPPA2 = 0.5522847498307936;
1455
+ function rectSubpath(b, radius = 0, kind) {
474
1456
  const { x, y, width: w, height: h } = b;
475
- const r = Math.max(0, Math.min(radius, w / 2, h / 2));
476
- if (r <= EPS) {
477
- return { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
478
- }
479
- const k = r * KAPPA;
480
- const anchors = [
481
- { x: x + r, y, in: { x: x + r - k, y } },
482
- { x: x + w - r, y, out: { x: x + w - r + k, y } },
483
- { x: x + w, y: y + r, in: { x: x + w, y: y + r - k } },
484
- { x: x + w, y: y + h - r, out: { x: x + w, y: y + h - r + k } },
485
- { x: x + w - r, y: y + h, in: { x: x + w - r + k, y: y + h } },
486
- { x: x + r, y: y + h, out: { x: x + r - k, y: y + h } },
487
- { x, y: y + h - r, in: { x, y: y + h - r + k } },
488
- { x, y: y + r, out: { x, y: y + r - k } }
489
- ];
490
- return { anchors, closed: true };
1457
+ const sp = { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
1458
+ return radius > 0 ? setSubpathCorners(sp, radius, kind) : sp;
491
1459
  }
492
1460
  function ellipseSubpath(b) {
493
1461
  const rx = b.width / 2;
494
1462
  const ry = b.height / 2;
495
1463
  const cx = b.x + rx;
496
1464
  const cy = b.y + ry;
497
- const kx = rx * KAPPA;
498
- const ky = ry * KAPPA;
1465
+ const kx = rx * KAPPA2;
1466
+ const ky = ry * KAPPA2;
499
1467
  return {
500
1468
  closed: true,
501
1469
  anchors: [
@@ -543,15 +1511,80 @@ function roundSubpaths(subpaths, precision = 3) {
543
1511
  }
544
1512
 
545
1513
  // src/vector/svg.ts
1514
+ function vectorCornersAttribute(subpaths, precision = 3) {
1515
+ const out = [];
1516
+ subpaths.forEach(
1517
+ (sp, s) => sp.anchors.forEach((a, i) => {
1518
+ if (!a.corner || !(a.corner.radius > 0)) return;
1519
+ out.push(`${s}.${i}:${_chunkGVB7ZPLZcjs.formatNumber.call(void 0, a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
1520
+ })
1521
+ );
1522
+ return out.join(" ");
1523
+ }
1524
+ function applyVectorCornersAttribute(subpaths, value) {
1525
+ for (const item of value.trim().split(/\s+/)) {
1526
+ const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
1527
+ if (!m) continue;
1528
+ const a = _optionalChain([subpaths, 'access', _30 => _30[+m[1]], 'optionalAccess', _31 => _31.anchors, 'access', _32 => _32[+m[2]]]);
1529
+ const radius = parseFloat(m[3]);
1530
+ if (!a || !(radius > 0)) continue;
1531
+ const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
1532
+ a.corner = { radius, ...kind && kind !== "round" ? { kind } : {} };
1533
+ }
1534
+ return subpaths;
1535
+ }
546
1536
  var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
547
1537
  function vectorPaintToSvg(paint, gradientId) {
548
1538
  if (!hasPaint(paint)) return { value: "none", gradient: null };
549
1539
  if (typeof paint === "string") return { value: paint, gradient: null };
550
- 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) };
1541
+ }
1542
+ var partsCache = /* @__PURE__ */ new WeakMap();
1543
+ function vectorStrokeSvgParts(node, clipId, precision = 3) {
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) {
1552
+ const path = subpathsToBezier(node.subpaths);
1553
+ const parts = vectorStrokeParts(path, node);
1554
+ const profiled = !!strokeWidthProfileOf(node);
1555
+ if (parts.align === "center" && !parts.heads.length && !profiled) return void 0;
1556
+ let clip = null;
1557
+ if (parts.align !== "center") {
1558
+ const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, path);
1559
+ const pad = vectorStrokeReach(node) + 1;
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 };
1561
+ clip = { kind: parts.align === "inside" ? "clip" : "mask", id: clipId, bounds };
1562
+ }
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
+ };
551
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
+ );
1583
+ }
1584
+ var round2 = (n) => Math.round(n * 100) / 100;
552
1585
  function gradientMarkup(def) {
553
1586
  const tag = def.type === "radial" ? "radialGradient" : "linearGradient";
554
- const attrs = Object.entries(def.attrs).map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`).join("");
1587
+ const attrs = Object.entries(def.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
555
1588
  const stops = def.stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeAttr(s.stopColor)}"${s.stopOpacity !== void 0 ? ` stop-opacity="${s.stopOpacity}"` : ""}/>`).join("");
556
1589
  return `<${tag} id="${escapeAttr(def.id)}"${attrs}>${stops}</${tag}>`;
557
1590
  }
@@ -561,11 +1594,11 @@ function vectorDocumentToSvg(doc, options = {}) {
561
1594
  const pretty = options.pretty !== false;
562
1595
  const defs = [];
563
1596
  const lines = [];
564
- const num = (n) => _chunkBGNPT5PEcjs.formatNumber.call(void 0, n, precision);
1597
+ const num = (n) => _chunkGVB7ZPLZcjs.formatNumber.call(void 0, n, precision);
565
1598
  const indent = (depth) => pretty ? " ".repeat(depth) : "";
566
1599
  const common = (node) => {
567
1600
  let out2 = ` id="${escapeAttr(node.id)}"`;
568
- 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(" ")})"`;
569
1602
  if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
570
1603
  if (node.visible === false) out2 += ` display="none"`;
571
1604
  return out2;
@@ -582,15 +1615,61 @@ function vectorDocumentToSvg(doc, options = {}) {
582
1615
  const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}`);
583
1616
  if (fill.gradient) defs.push(gradientMarkup(fill.gradient));
584
1617
  if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
585
- let attrs = `${common(node)} d="${vectorPathData(node, precision)}" fill="${escapeAttr(fill.value)}"`;
586
- if (node.fillRule === "evenodd") attrs += ` fill-rule="evenodd"`;
587
- if (stroke.value !== "none") {
588
- attrs += ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(_nullishCoalesce(node.strokeWidth, () => ( 1)))}"`;
589
- if (node.strokeLinecap && node.strokeLinecap !== "butt") attrs += ` stroke-linecap="${node.strokeLinecap}"`;
590
- if (node.strokeLinejoin && node.strokeLinejoin !== "miter") attrs += ` stroke-linejoin="${node.strokeLinejoin}"`;
591
- if (_optionalChain([node, 'access', _2 => _2.strokeDasharray, 'optionalAccess', _3 => _3.length])) attrs += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
592
- }
593
- lines.push(`${indent(depth)}<path${attrs}/>`);
1618
+ const d = vectorPathData(node, precision);
1619
+ const corners2 = vectorCornersAttribute(node.subpaths, precision);
1620
+ const live = corners2 ? ` data-vector-d="${_chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
1621
+ const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
1622
+ const lineAttrs = (width) => {
1623
+ let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
1624
+ if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
1625
+ if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
1626
+ if (_optionalChain([node, 'access', _33 => _33.strokeDasharray, 'optionalAccess', _34 => _34.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1627
+ return out2;
1628
+ };
1629
+ const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
1630
+ if (!parts) {
1631
+ lines.push(`${indent(depth)}<path${common(node)} d="${d}"${fillAttrs}${stroke.value !== "none" ? lineAttrs(_nullishCoalesce(node.strokeWidth, () => ( 1))) : ""}${live}/>`);
1632
+ return;
1633
+ }
1634
+ let data = ` data-vector-path="" data-vector-stroke-width="${num(_nullishCoalesce(node.strokeWidth, () => ( 1)))}"`;
1635
+ if (parts.align !== "center") data += ` data-vector-stroke-align="${parts.align}"`;
1636
+ if (node.startArrow) data += ` data-vector-start-arrow="${node.startArrow}"`;
1637
+ if (node.endArrow) data += ` data-vector-end-arrow="${node.endArrow}"`;
1638
+ if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
1639
+ if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num(node.startArrowScale)}"`;
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
+ }
1648
+ lines.push(`${indent(depth)}<g${common(node)}${data}>`);
1649
+ lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
1650
+ if (options.strokes === "outline") {
1651
+ const area = _chunkGVB7ZPLZcjs.bezierPathToSvg.call(void 0, vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
1652
+ lines.push(`${indent(depth + 1)}<path data-vector-part="stroke-outline" d="${area}" fill="${escapeAttr(stroke.value)}"/>`);
1653
+ } else {
1654
+ let clip = "";
1655
+ if (parts.clip) {
1656
+ const rule = node.fillRule === "evenodd" ? "evenodd" : "nonzero";
1657
+ const id = escapeAttr(parts.clip.id);
1658
+ if (parts.clip.kind === "clip") {
1659
+ defs.push(`<clipPath id="${id}"><path d="${d}" clip-rule="${rule}"/></clipPath>`);
1660
+ clip = ` clip-path="url(#${id})"`;
1661
+ } else {
1662
+ const b = parts.clip.bounds;
1663
+ const box = `x="${num(b.x)}" y="${num(b.y)}" width="${num(b.width)}" height="${num(b.height)}"`;
1664
+ defs.push(`<mask id="${id}" maskUnits="userSpaceOnUse" ${box}><rect ${box} fill="#fff"/><path d="${d}" fill="#000" fill-rule="${rule}"/></mask>`);
1665
+ clip = ` mask="url(#${id})"`;
1666
+ }
1667
+ }
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}/>`);
1670
+ for (const h2 of parts.heads) lines.push(`${indent(depth + 1)}<path data-vector-part="head" d="${h2}" fill="${escapeAttr(stroke.value)}"/>`);
1671
+ }
1672
+ lines.push(`${indent(depth)}</g>`);
594
1673
  };
595
1674
  for (const node of doc.nodes) emit(node, 1);
596
1675
  const w = num(doc.width);
@@ -733,7 +1812,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
733
1812
  seen.add(id);
734
1813
  let stops = readStops(el);
735
1814
  const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
736
- if (!stops.length && _optionalChain([href, 'optionalAccess', _4 => _4.startsWith, 'call', _5 => _5("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _6 => _6(href.slice(1), raw, seen), 'optionalAccess', _7 => _7.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]), () => ( []));
737
1816
  if (!stops.length) return null;
738
1817
  if (el.name === "radialGradient") {
739
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 };
@@ -765,11 +1844,11 @@ function parseVectorSvg(svg, options = {}) {
765
1844
  const rawGradients = /* @__PURE__ */ new Map();
766
1845
  collectGradients(root, rawGradients);
767
1846
  const paint = (value) => {
768
- const v = value.trim();
769
- const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(v);
1847
+ const v2 = value.trim();
1848
+ const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(v2);
770
1849
  if (url) return _nullishCoalesce(resolveGradient(url[1], rawGradients), () => ( null));
771
- if (v === "none" || v === "") return null;
772
- return v;
1850
+ if (v2 === "none" || v2 === "") return null;
1851
+ return v2;
773
1852
  };
774
1853
  const viewBox = numberList(root.attrs.viewBox);
775
1854
  const width = viewBox.length === 4 ? viewBox[2] : length(root.attrs.width, 300);
@@ -779,8 +1858,13 @@ function parseVectorSvg(svg, options = {}) {
779
1858
  const shapeOf = (el) => {
780
1859
  const a = el.attrs;
781
1860
  switch (el.name) {
782
- case "path":
1861
+ case "path": {
1862
+ if (a["data-vector-d"] && a["data-vector-corners"]) {
1863
+ const sharp = bezierToSubpaths(a["data-vector-d"]);
1864
+ if (sharp.length) return applyVectorCornersAttribute(sharp, a["data-vector-corners"]);
1865
+ }
783
1866
  return a.d ? bezierToSubpaths(a.d) : null;
1867
+ }
784
1868
  case "rect": {
785
1869
  const w = length(a.width);
786
1870
  const h = length(a.height);
@@ -814,6 +1898,65 @@ function parseVectorSvg(svg, options = {}) {
814
1898
  }
815
1899
  return null;
816
1900
  };
1901
+ const strokeStyle = (s) => ({
1902
+ ...s.strokeWidth ? { strokeWidth: length(s.strokeWidth, 1) } : {},
1903
+ ...s.strokeLinecap ? { strokeLinecap: s.strokeLinecap } : {},
1904
+ ...s.strokeLinejoin ? { strokeLinejoin: s.strokeLinejoin } : {},
1905
+ ...s.strokeDasharray && s.strokeDasharray !== "none" ? { strokeDasharray: numberList(s.strokeDasharray) } : {},
1906
+ ...s.fillRule === "evenodd" ? { fillRule: "evenodd" } : {}
1907
+ });
1908
+ const placed = (node, m) => {
1909
+ if (m === IDENTITY_MATRIX) return node;
1910
+ const next = transformVectorNode(node, m);
1911
+ const strokeScale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
1912
+ if (hasPaint(next.stroke) && strokeScale > 0 && Math.abs(strokeScale - 1) > 1e-9) next.strokeWidth = (_nullishCoalesce(next.strokeWidth, () => ( 1))) * strokeScale;
1913
+ return next;
1914
+ };
1915
+ const mergedPath = (g, style, m, meta) => {
1916
+ const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
1917
+ const fillEl = part("fill");
1918
+ const subpaths = fillEl ? shapeOf(fillEl) : null;
1919
+ if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _41 => _41.length])) return null;
1920
+ const a = g.attrs;
1921
+ const fs = readStyle(fillEl, style);
1922
+ const strokeEl = part("stroke");
1923
+ const outlineEl = part("stroke-outline");
1924
+ const ss = strokeEl ? readStyle(strokeEl, style) : null;
1925
+ const width2 = length(a["data-vector-stroke-width"], ss ? length(ss.strokeWidth, 1) : 1);
1926
+ const arrow = (v2) => VECTOR_ARROWHEADS.includes(v2) ? v2 : void 0;
1927
+ const scale = (v2) => {
1928
+ const n = v2 === void 0 ? NaN : parseFloat(v2);
1929
+ return n > 0 ? n : void 0;
1930
+ };
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;
1937
+ const node = {
1938
+ id: idFor(g),
1939
+ type: "path",
1940
+ ...meta,
1941
+ subpaths,
1942
+ fill: paint(fs.fill),
1943
+ stroke: ss ? paint(ss.stroke) : outlineEl ? paint(readStyle(outlineEl, style).fill) : null,
1944
+ ...ss ? strokeStyle(ss) : {},
1945
+ ...fs.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
1946
+ strokeWidth: width2,
1947
+ ...align === "inside" || align === "outside" ? { strokeAlign: align } : {},
1948
+ ...arrow(a["data-vector-start-arrow"]) ? { startArrow: arrow(a["data-vector-start-arrow"]) } : {},
1949
+ ...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
1950
+ ...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
1951
+ ...scale(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale(a["data-vector-start-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 } : {}
1957
+ };
1958
+ return placed(node, m);
1959
+ };
817
1960
  const visit = (el, style, matrix) => {
818
1961
  const out = [];
819
1962
  for (const child of el.children) {
@@ -827,6 +1970,11 @@ function parseVectorSvg(svg, options = {}) {
827
1970
  ...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
828
1971
  ...hidden ? { visible: false } : {}
829
1972
  };
1973
+ if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
1974
+ const node2 = mergedPath(child, s, m, meta);
1975
+ if (node2) out.push(node2);
1976
+ continue;
1977
+ }
830
1978
  if (child.name === "g" || child.name === "svg" || child.name === "a" || child.name === "switch") {
831
1979
  const children = visit(child, s, m);
832
1980
  if (!children.length) continue;
@@ -836,24 +1984,18 @@ function parseVectorSvg(svg, options = {}) {
836
1984
  }
837
1985
  const subpaths = shapeOf(child);
838
1986
  if (!subpaths || !subpaths.length) continue;
839
- let node = {
840
- id: idFor(child),
841
- type: "path",
842
- ...meta,
843
- subpaths,
844
- fill: child.name === "line" ? null : paint(s.fill),
845
- stroke: paint(s.stroke),
846
- ...s.strokeWidth ? { strokeWidth: length(s.strokeWidth, 1) } : {},
847
- ...s.strokeLinecap ? { strokeLinecap: s.strokeLinecap } : {},
848
- ...s.strokeLinejoin ? { strokeLinejoin: s.strokeLinejoin } : {},
849
- ...s.strokeDasharray && s.strokeDasharray !== "none" ? { strokeDasharray: numberList(s.strokeDasharray) } : {},
850
- ...s.fillRule === "evenodd" ? { fillRule: "evenodd" } : {}
851
- };
852
- if (m !== IDENTITY_MATRIX) {
853
- node = transformVectorNode(node, m);
854
- const strokeScale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
855
- if (hasPaint(node.stroke) && strokeScale > 0 && Math.abs(strokeScale - 1) > 1e-9) node.strokeWidth = (_nullishCoalesce(node.strokeWidth, () => ( 1))) * strokeScale;
856
- }
1987
+ const node = placed(
1988
+ {
1989
+ id: idFor(child),
1990
+ type: "path",
1991
+ ...meta,
1992
+ subpaths,
1993
+ fill: child.name === "line" ? null : paint(s.fill),
1994
+ stroke: paint(s.stroke),
1995
+ ...strokeStyle(s)
1996
+ },
1997
+ m
1998
+ );
857
1999
  out.push(node);
858
2000
  }
859
2001
  return out;
@@ -865,7 +2007,7 @@ function parseVectorSvg(svg, options = {}) {
865
2007
  // src/vector/snap.ts
866
2008
  var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
867
2009
  function constrainToLeave(leave, p) {
868
- if (!_optionalChain([leave, 'optionalAccess', _8 => _8.constrain])) return p;
2010
+ if (!_optionalChain([leave, 'optionalAccess', _43 => _43.constrain])) return p;
869
2011
  const dx = p.x - leave.at.x;
870
2012
  const dy = p.y - leave.at.y;
871
2013
  switch (leave.constrain) {
@@ -876,9 +2018,9 @@ function constrainToLeave(leave, p) {
876
2018
  return { x: leave.at.x + Math.sign(dx || 1) * s, y: leave.at.y + Math.sign(dy || 1) * s };
877
2019
  }
878
2020
  default: {
879
- const len = Math.hypot(dx, dy);
2021
+ const len2 = Math.hypot(dx, dy);
880
2022
  const a = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
881
- return { x: leave.at.x + Math.cos(a) * len, y: leave.at.y + Math.sin(a) * len };
2023
+ return { x: leave.at.x + Math.cos(a) * len2, y: leave.at.y + Math.sin(a) * len2 };
882
2024
  }
883
2025
  }
884
2026
  }
@@ -887,24 +2029,24 @@ var CROSSING_PAIRS = 32;
887
2029
  var CURVE_SAMPLES = 48;
888
2030
  var BISECT = 50;
889
2031
  var EQUAL = 1e-6;
890
- function bound(sorted, key, v, strict) {
2032
+ function bound(sorted, key, v2, strict) {
891
2033
  let lo = 0;
892
2034
  let hi = sorted.length;
893
2035
  while (lo < hi) {
894
2036
  const mid = lo + hi >>> 1;
895
2037
  const k = key(sorted[mid]);
896
- if (strict ? k <= v : k < v) lo = mid + 1;
2038
+ if (strict ? k <= v2 : k < v2) lo = mid + 1;
897
2039
  else hi = mid;
898
2040
  }
899
2041
  return lo;
900
2042
  }
901
- function within(sorted, key, v, tol) {
902
- return sorted.slice(bound(sorted, key, v - tol, false), bound(sorted, key, v + tol, true));
2043
+ function within(sorted, key, v2, tol) {
2044
+ return sorted.slice(bound(sorted, key, v2 - tol, false), bound(sorted, key, v2 + tol, true));
903
2045
  }
904
- var linesNear = (lines, v, tol) => within(lines, (l) => l.v, v, tol);
2046
+ var linesNear = (lines, v2, tol) => within(lines, (l) => l.v, v2, tol);
905
2047
  var distance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
906
2048
  var toPoint = (p) => [p.x, p.y];
907
- var toVec = (p) => ({ x: p[0], y: p[1] });
2049
+ var toVec2 = (p) => ({ x: p[0], y: p[1] });
908
2050
  var LABELS = {
909
2051
  anchor: "anchor",
910
2052
  centre: "centre",
@@ -976,7 +2118,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
976
2118
  moving.set(r.nodeId, set);
977
2119
  }
978
2120
  const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
979
- t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _9 => _9.get, 'call', _10 => _10(id), 'optionalAccess', _11 => _11.has, 'call', _12 => _12(`${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);
980
2122
  return t.index();
981
2123
  }
982
2124
  /**
@@ -1022,7 +2164,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1022
2164
  const count = subpathSegmentCount(sp);
1023
2165
  for (let i = 0; i < count && budget > 0; i++) {
1024
2166
  if (moving(i) || moving((i + 1) % n)) continue;
1025
- const seg = subpathSegment(sp, i).map((q) => toPoint(applyMatrix(m, toVec(q))));
2167
+ const seg = subpathSegment(sp, i).map((q) => toPoint(applyMatrix(m, toVec2(q))));
1026
2168
  let minX = Infinity;
1027
2169
  let minY = Infinity;
1028
2170
  let maxX = -Infinity;
@@ -1094,7 +2236,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1094
2236
  }
1095
2237
  const b = vectorNodeBounds(n, parent);
1096
2238
  const f = nodeFrame(n, parent, b);
1097
- if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _13 => _13.centre]), () => ( null)));
2239
+ if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _48 => _48.centre]), () => ( null)));
1098
2240
  }
1099
2241
  };
1100
2242
  visit(doc.nodes, IDENTITY_MATRIX);
@@ -1131,7 +2273,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1131
2273
  */
1132
2274
  snapFrom(p, leave, tol) {
1133
2275
  const none = (q) => ({ point: q, kind: null, x: false, y: false, lines: [], labels: [] });
1134
- const at = (q, kind, lines2 = []) => ({
2276
+ const at2 = (q, kind, lines2 = []) => ({
1135
2277
  point: q,
1136
2278
  kind,
1137
2279
  x: true,
@@ -1139,23 +2281,23 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1139
2281
  lines: lines2,
1140
2282
  labels: [this.label(q, kind)]
1141
2283
  });
1142
- if (_optionalChain([leave, 'optionalAccess', _14 => _14.constrain])) {
2284
+ if (_optionalChain([leave, 'optionalAccess', _49 => _49.constrain])) {
1143
2285
  const q = constrainToLeave(leave, p);
1144
2286
  const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
1145
2287
  return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
1146
2288
  }
1147
2289
  const point = this.nearestPoint(p, tol);
1148
- if (point) return at(point.p, point.kind);
2290
+ if (point) return at2(point.p, point.kind);
1149
2291
  const near2 = this.segmentsNear(p, tol);
1150
- const crossing = this.crossing(near2, p, tol);
1151
- if (crossing) return at(crossing, "intersect");
2292
+ const crossing2 = this.crossing(near2, p, tol);
2293
+ if (crossing2) return at2(crossing2, "intersect");
1152
2294
  if (leave && leave.curves !== false) {
1153
2295
  const foot = this.curvePoint(near2, leave.at, p, tol);
1154
- 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)]);
1155
2297
  }
1156
2298
  const on = this.onSegment(near2);
1157
- if (on) return at(on, "path");
1158
- if (_optionalChain([leave, 'optionalAccess', _15 => _15.angles, 'optionalAccess', _16 => _16.length])) {
2299
+ if (on) return at2(on, "path");
2300
+ if (_optionalChain([leave, 'optionalAccess', _50 => _50.angles, 'optionalAccess', _51 => _51.length])) {
1159
2301
  const c = construction(leave, p, tol);
1160
2302
  if (c) {
1161
2303
  const hit = this.slide(leave.at, c.dir, c.point, tol, false);
@@ -1166,9 +2308,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1166
2308
  return { point: c.point, kind: "construction", x: true, y: true, lines: [line("construction", leave.at, c.point)], labels: [] };
1167
2309
  }
1168
2310
  }
1169
- const nearest = (lines2, v) => {
2311
+ const nearest = (lines2, v2) => {
1170
2312
  let best = null;
1171
- for (const l of linesNear(lines2, v, tol)) if (!best || Math.abs(l.v - v) < Math.abs(best.v - v)) best = l;
2313
+ for (const l of linesNear(lines2, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.v - v2)) best = l;
1172
2314
  return best;
1173
2315
  };
1174
2316
  const bx = nearest(this.xs, p.x);
@@ -1201,7 +2343,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1201
2343
  * the nearest target line crosses it, at most twice `tol` away: where it lands and the
1202
2344
  * target point that line comes from.
1203
2345
  */
1204
- slide(at, dir, q, tol, ray) {
2346
+ slide(at2, dir, q, tol, ray) {
1205
2347
  const len2 = dir.x * dir.x + dir.y * dir.y;
1206
2348
  if (!Number.isFinite(len2) || len2 <= 1e-18) return null;
1207
2349
  const reach = 2 * tol;
@@ -1209,10 +2351,10 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1209
2351
  for (const vertical of [true, false]) {
1210
2352
  const d = vertical ? dir.x : dir.y;
1211
2353
  if (Math.abs(d) <= 1e-9) continue;
1212
- const a = vertical ? at.x : at.y;
2354
+ const a = vertical ? at2.x : at2.y;
1213
2355
  for (const l of linesNear(vertical ? this.xs : this.ys, vertical ? q.x : q.y, reach)) {
1214
2356
  const s = (l.v - a) / d;
1215
- 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 };
1216
2358
  const moved = distance(hit, q);
1217
2359
  if (moved <= reach && (!ray || s > 0) && (!best || moved < best.moved)) best = { moved, point: hit, from: l.from };
1218
2360
  }
@@ -1228,16 +2370,16 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1228
2370
  /** Target segments whose control box reaches `p` and which pass within `tol` of it, nearest first. */
1229
2371
  segmentsNear(p, tol) {
1230
2372
  const out = [];
1231
- const pt = toPoint(p);
2373
+ const pt2 = toPoint(p);
1232
2374
  for (const s of this.segments) {
1233
2375
  if (p.x < s.minX - tol || p.x > s.maxX + tol || p.y < s.minY - tol || p.y > s.maxY + tol) continue;
1234
- const hit = _chunkBGNPT5PEcjs.segmentNearest.call(void 0, s.seg, pt);
2376
+ const hit = _chunkGVB7ZPLZcjs.segmentNearest.call(void 0, s.seg, pt2);
1235
2377
  if (hit.distance <= tol) out.push({ seg: s.seg, t: hit.t, distance: hit.distance });
1236
2378
  }
1237
2379
  return out.sort((a, b) => a.distance - b.distance);
1238
2380
  }
1239
2381
  onSegment(near2) {
1240
- return near2.length ? toVec(_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;
1241
2383
  }
1242
2384
  /** Where two of the target segments near `p` cross, the crossing nearest `p` within `tol`. */
1243
2385
  crossing(near2, p, tol) {
@@ -1246,8 +2388,8 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1246
2388
  let bestD = Infinity;
1247
2389
  for (let i = 0; i < list.length; i++) {
1248
2390
  for (let j = i + 1; j < list.length; j++) {
1249
- for (const hit of _chunkBGNPT5PEcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
1250
- const q = toVec(hit.point);
2391
+ for (const hit of _chunkGVB7ZPLZcjs.segmentIntersections.call(void 0, list[i].seg, list[j].seg)) {
2392
+ const q = toVec2(hit.point);
1251
2393
  const d = distance(q, p);
1252
2394
  if (d <= tol && d < bestD) {
1253
2395
  best = q;
@@ -1285,15 +2427,15 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1285
2427
  * with nothing spaces the box evenly in its row or column. Returns the correction and guides.
1286
2428
  */
1287
2429
  snapRect(r, tol, options = {}) {
1288
- const axes = { x: _optionalChain([options, 'access', _17 => _17.axes, 'optionalAccess', _18 => _18.x]) !== false, y: _optionalChain([options, 'access', _19 => _19.axes, 'optionalAccess', _20 => _20.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 };
1289
2431
  const nearest = (lines, values) => {
1290
2432
  let best = null;
1291
- for (const v of values) for (const l of linesNear(lines, v, tol)) if (!best || Math.abs(l.v - v) < Math.abs(best.d)) best = { d: l.v - v, from: l.from };
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 };
1292
2434
  return best;
1293
2435
  };
1294
2436
  const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
1295
2437
  const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
1296
- const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _21 => _21.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _22 => _22.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: [] };
1297
2439
  const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
1298
2440
  if (bx) {
1299
2441
  const x = bx.from.x;
@@ -1331,7 +2473,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1331
2473
  evenSpacing(r, horizontal, tol) {
1332
2474
  const span = (b, x) => x ? [b.x, b.x + b.width] : [b.y, b.y + b.height];
1333
2475
  const [r0, r1] = span(r, horizontal);
1334
- const len = r1 - r0;
2476
+ const len2 = r1 - r0;
1335
2477
  const [c0, c1] = span(r, !horizontal);
1336
2478
  const row = this.boxes.map((b) => ({ b, a0: span(b, horizontal)[0], a1: span(b, horizontal)[1] })).filter((e) => span(e.b, !horizontal)[0] < c1 && span(e.b, !horizontal)[1] > c0 && (e.a1 <= r0 + tol || e.a0 >= r1 - tol)).sort((a, b) => a.a0 - b.a0);
1337
2479
  if (!row.length) return null;
@@ -1351,20 +2493,20 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1351
2493
  if (e.a0 >= r1 - tol && (!after || e.a0 < after.a0)) after = e;
1352
2494
  }
1353
2495
  const starts = [];
1354
- if (before && after && after.a0 - before.a1 > len) starts.push({ start: (before.a1 + after.a0 - len) / 2, repeats: null });
2496
+ if (before && after && after.a0 - before.a1 > len2) starts.push({ start: (before.a1 + after.a0 - len2) / 2, repeats: null });
1355
2497
  for (const { g } of pairs) {
1356
2498
  if (before) starts.push({ start: before.a1 + g, repeats: g });
1357
- if (after) starts.push({ start: after.a0 - g - len, repeats: g });
2499
+ if (after) starts.push({ start: after.a0 - g - len2, repeats: g });
1358
2500
  }
1359
2501
  let pick = null;
1360
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;
1361
2503
  if (!pick) return null;
1362
2504
  const { start, repeats } = pick;
1363
- const at = horizontal ? { ...r, x: start } : { ...r, y: start };
1364
- const end = start + len;
2505
+ const at2 = horizontal ? { ...r, x: start } : { ...r, y: start };
2506
+ const end = start + len2;
1365
2507
  const gaps = [];
1366
- if (before && (repeats === null || Math.abs(start - before.a1 - repeats) <= EQUAL)) gaps.push([before.a1, start, mid(before.b, at)]);
1367
- 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)]);
1368
2510
  if (repeats !== null) {
1369
2511
  for (const p of pairs) if (Math.abs(p.g - repeats) <= EQUAL) gaps.push(p.gap);
1370
2512
  }
@@ -1372,9 +2514,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1372
2514
  }
1373
2515
  /** A gap mark: a line across the gap with a tick at each end, and its length as a label. */
1374
2516
  gapMarks([u0, u1, c], horizontal, tick, out) {
1375
- const pt = (u, v) => horizontal ? { x: u, y: v } : { x: v, y: u };
1376
- out.lines.push(line("spacing", pt(u0, c), pt(u1, c)), line("spacing", pt(u0, c - tick), pt(u0, c + tick)), line("spacing", pt(u1, c - tick), pt(u1, c + tick)));
1377
- out.labels.push(this.label(pt((u0 + u1) / 2, c), "spacing", fmtLength(u1 - u0)));
2517
+ const pt2 = (u, v2) => horizontal ? { x: u, y: v2 } : { x: v2, y: u };
2518
+ out.lines.push(line("spacing", pt2(u0, c), pt2(u1, c)), line("spacing", pt2(u0, c - tick), pt2(u0, c + tick)), line("spacing", pt2(u1, c - tick), pt2(u1, c + tick)));
2519
+ out.labels.push(this.label(pt2((u0 + u1) / 2, c), "spacing", fmtLength(u1 - u0)));
1378
2520
  }
1379
2521
  /** The other objects' side length nearest `length`, within `tol`, or null. */
1380
2522
  matchSize(length2, tol) {
@@ -1401,9 +2543,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1401
2543
  const hp = handlePos(handle, frame);
1402
2544
  const d0 = { x: hp.x - origin.x, y: hp.y - origin.y };
1403
2545
  const valid = (n, s) => Number.isFinite(n) && n * s > 0;
1404
- const nearest = (v, lines) => {
2546
+ const nearest = (v2, lines) => {
1405
2547
  let best = null;
1406
- for (const l of linesNear(lines, v, tol)) if (!best || Math.abs(l.v - v) < Math.abs(best.v - v)) best = l;
2548
+ for (const l of linesNear(lines, v2, tol)) if (!best || Math.abs(l.v - v2) < Math.abs(best.v - v2)) best = l;
1407
2549
  return best;
1408
2550
  };
1409
2551
  let nx = sx;
@@ -1416,9 +2558,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1416
2558
  const on = this.nearestPoint(p, tol);
1417
2559
  const tries = [];
1418
2560
  if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
1419
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _23 => _23.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _24 => _24.v]), () => ( null)), s: { hx, hy, on: null } });
1420
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _25 => _25.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
1421
- tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _26 => _26.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 } });
1422
2564
  for (const { tx, ty, s } of tries) {
1423
2565
  if (tx === null && ty === null) continue;
1424
2566
  const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
@@ -1454,9 +2596,9 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1454
2596
  };
1455
2597
  for (const h of [handle, ...ends]) {
1456
2598
  const hv = handlePos(h, frame);
1457
- const v = { x: hv.x - origin.x, y: hv.y - origin.y };
1458
- const base = applyMatrix(toDoc, { x: origin.x + c.x * v.x, y: origin.y + c.y * v.y });
1459
- const dir = applyMatrix(toDoc, { x: e.x * v.x, y: e.y * v.y });
2599
+ const v2 = { x: hv.x - origin.x, y: hv.y - origin.y };
2600
+ const base = applyMatrix(toDoc, { x: origin.x + c.x * v2.x, y: origin.y + c.y * v2.y });
2601
+ const dir = applyMatrix(toDoc, { x: e.x * v2.x, y: e.y * v2.y });
1460
2602
  const p = { x: base.x + dir.x * s0, y: base.y + dir.y * s0 };
1461
2603
  const l2 = dir.x * dir.x + dir.y * dir.y;
1462
2604
  const on = l2 > 1e-18 ? this.nearestPoint(p, tol) : null;
@@ -1489,16 +2631,16 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
1489
2631
  const sized = [];
1490
2632
  if (!proportional) {
1491
2633
  if (movesX && !snap.hx && !(frame.angle && snap.hy) && frame.width > 1e-9) {
1492
- const len = this.matchSize(Math.abs(out.sx) * frame.width, tol);
1493
- if (len !== null && len > 0) {
1494
- out.sx = Math.sign(out.sx || 1) * (len / frame.width);
2634
+ const len2 = this.matchSize(Math.abs(out.sx) * frame.width, tol);
2635
+ if (len2 !== null && len2 > 0) {
2636
+ out.sx = Math.sign(out.sx || 1) * (len2 / frame.width);
1495
2637
  sized.push("x");
1496
2638
  }
1497
2639
  }
1498
2640
  if (movesY && !snap.hy && !(frame.angle && snap.hx) && frame.height > 1e-9) {
1499
- const len = this.matchSize(Math.abs(out.sy) * frame.height, tol);
1500
- if (len !== null && len > 0) {
1501
- out.sy = Math.sign(out.sy || 1) * (len / frame.height);
2641
+ const len2 = this.matchSize(Math.abs(out.sy) * frame.height, tol);
2642
+ if (len2 !== null && len2 > 0) {
2643
+ out.sy = Math.sign(out.sy || 1) * (len2 / frame.height);
1502
2644
  sized.push("y");
1503
2645
  }
1504
2646
  }
@@ -1549,14 +2691,14 @@ function line(kind, a, b) {
1549
2691
  return { kind, x1: a.x, y1: a.y, x2: b.x, y2: b.y };
1550
2692
  }
1551
2693
  function construction(leave, p, tol) {
1552
- const v = { x: p.x - leave.at.x, y: p.y - leave.at.y };
1553
- if (Math.hypot(v.x, v.y) <= tol) return null;
2694
+ const v2 = { x: p.x - leave.at.x, y: p.y - leave.at.y };
2695
+ if (Math.hypot(v2.x, v2.y) <= tol) return null;
1554
2696
  let best = null;
1555
2697
  for (const a of _nullishCoalesce(leave.angles, () => ( []))) {
1556
2698
  const r = a * Math.PI / 180;
1557
2699
  const exact = (n) => Math.abs(n) < 1e-12 ? 0 : n;
1558
2700
  const dir = { x: exact(Math.cos(r)), y: exact(-Math.sin(r)) };
1559
- const k = v.x * dir.x + v.y * dir.y;
2701
+ const k = v2.x * dir.x + v2.y * dir.y;
1560
2702
  const point = { x: leave.at.x + dir.x * k, y: leave.at.y + dir.y * k };
1561
2703
  const d = distance(point, p);
1562
2704
  if (d <= tol && (!best || d < best.d)) best = { dir, point, d };
@@ -1568,20 +2710,20 @@ var crossCondition = (r, d) => r[0] * d[1] - r[1] * d[0];
1568
2710
  function solveOnSegment(seg, base, cond) {
1569
2711
  const b = [base.x, base.y];
1570
2712
  const g = (t) => {
1571
- const p = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
1572
- 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));
1573
2715
  };
1574
2716
  const vals = [];
1575
2717
  let scale = 0;
1576
2718
  for (let i = 0; i <= CURVE_SAMPLES; i++) {
1577
2719
  const t = i / CURVE_SAMPLES;
1578
- const p = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
1579
- 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);
1580
2722
  const r = [p[0] - b[0], p[1] - b[1]];
1581
2723
  vals.push(cond(r, d));
1582
2724
  scale = Math.max(scale, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
1583
2725
  }
1584
- if (!(scale > 0) || vals.every((v) => Math.abs(v) <= scale * 1e-12)) return [];
2726
+ if (!(scale > 0) || vals.every((v2) => Math.abs(v2) <= scale * 1e-12)) return [];
1585
2727
  const out = [];
1586
2728
  for (let i = 0; i < CURVE_SAMPLES; i++) {
1587
2729
  const ga = vals[i];
@@ -1603,8 +2745,8 @@ function solveOnSegment(seg, base, cond) {
1603
2745
  } else hi = m;
1604
2746
  }
1605
2747
  const t = (lo + hi) / 2;
1606
- const p = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
1607
- 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);
1608
2750
  const r = Math.hypot(p[0] - b[0], p[1] - b[1]);
1609
2751
  const dl = Math.hypot(dv[0], dv[1]);
1610
2752
  if (!(r > 1e-9 && dl > 0) || Math.abs(cond([p[0] - b[0], p[1] - b[1]], dv)) > 1e-6 * r * dl) continue;
@@ -1614,8 +2756,8 @@ function solveOnSegment(seg, base, cond) {
1614
2756
  }
1615
2757
 
1616
2758
  // src/vector/pathEdit.ts
1617
- var EPS2 = 1e-9;
1618
- var unit = (x, y) => {
2759
+ var EPS3 = 1e-9;
2760
+ var unit2 = (x, y) => {
1619
2761
  const l = Math.hypot(x, y);
1620
2762
  return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
1621
2763
  };
@@ -1628,26 +2770,26 @@ function cubicOf(sp, i) {
1628
2770
  var isLine = (sp, i) => subpathSegment(sp, i).length === 2;
1629
2771
  function startTangent(c) {
1630
2772
  for (let k = 1; k < 4; k++) {
1631
- const u = unit(c[k][0] - c[0][0], c[k][1] - c[0][1]);
2773
+ const u = unit2(c[k][0] - c[0][0], c[k][1] - c[0][1]);
1632
2774
  if (u) return u;
1633
2775
  }
1634
2776
  return [1, 0];
1635
2777
  }
1636
2778
  function endTangent(c) {
1637
2779
  for (let k = 2; k >= 0; k--) {
1638
- const u = unit(c[3][0] - c[k][0], c[3][1] - c[k][1]);
2780
+ const u = unit2(c[3][0] - c[k][0], c[3][1] - c[k][1]);
1639
2781
  if (u) return u;
1640
2782
  }
1641
2783
  return [1, 0];
1642
2784
  }
1643
2785
  function sample(c, n, out, withStart) {
1644
- 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));
1645
2787
  }
1646
- var onAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS2 && Math.abs(h.y - a.y) < EPS2;
2788
+ var onAnchor2 = (a, h) => !h || Math.abs(h.x - a.x) < EPS3 && Math.abs(h.y - a.y) < EPS3;
1647
2789
  function tidy(a, keepType = true) {
1648
2790
  const next = cloneAnchor(a);
1649
- if (onAnchor(next, next.in)) delete next.in;
1650
- if (onAnchor(next, next.out)) delete next.out;
2791
+ if (onAnchor2(next, next.in)) delete next.in;
2792
+ if (onAnchor2(next, next.out)) delete next.out;
1651
2793
  const shown = inferAnchorType(next);
1652
2794
  const type = keepType && a.type ? a.type === "symmetric" && shown !== "symmetric" ? shown === "corner" ? "corner" : "smooth" : a.type === "smooth" && shown === "corner" ? "corner" : a.type : shown;
1653
2795
  if (type === "corner") delete next.type;
@@ -1687,7 +2829,7 @@ function fitAcross(pts, N, t0, t1) {
1687
2829
  const u = [];
1688
2830
  for (let i = 0; i <= N; i++) u.push(k * i / N);
1689
2831
  for (let i = 1; i <= N; i++) u.push(k + (1 - k) * i / N);
1690
- return _chunkBGNPT5PEcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
2832
+ return _chunkGVB7ZPLZcjs.fitCubicWithTangents.call(void 0, pts, u, t0, t1);
1691
2833
  };
1692
2834
  return goldenMin(fit, 0, 1);
1693
2835
  }
@@ -1747,7 +2889,7 @@ function bendSegment(sp, segment, t, p) {
1747
2889
  return { anchors, closed: sp.closed };
1748
2890
  }
1749
2891
  function subpathPointAt(sp, segment, t) {
1750
- 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);
1751
2893
  return { x, y };
1752
2894
  }
1753
2895
  function curvatureMoveAnchor(sp, index, q) {
@@ -1755,8 +2897,8 @@ function curvatureMoveAnchor(sp, index, q) {
1755
2897
  const a = sp.anchors[index];
1756
2898
  if (!a) return { anchors, closed: sp.closed };
1757
2899
  const own = [
1758
- a.in && !onAnchor(a, a.in) ? { x: a.in.x - a.x, y: a.in.y - a.y } : null,
1759
- a.out && !onAnchor(a, a.out) ? { x: a.out.x - a.x, y: a.out.y - a.y } : null
2900
+ a.in && !onAnchor2(a, a.in) ? { x: a.in.x - a.x, y: a.in.y - a.y } : null,
2901
+ a.out && !onAnchor2(a, a.out) ? { x: a.out.x - a.x, y: a.out.y - a.y } : null
1760
2902
  ];
1761
2903
  const ends = [null, null];
1762
2904
  neighbours(sp, index).forEach((j, side) => {
@@ -1775,15 +2917,16 @@ function curvatureMoveAnchor(sp, index, q) {
1775
2917
  if (own[0]) moved.in = { x: q.x + own[0].x, y: q.y + own[0].y };
1776
2918
  if (own[1]) moved.out = { x: q.x + own[1].x, y: q.y + own[1].y };
1777
2919
  if (a.type) moved.type = a.type;
2920
+ if (a.corner) moved.corner = { ...a.corner };
1778
2921
  const [prev, next] = ends;
1779
2922
  if (a.type && a.type !== "corner" && prev && next) {
1780
2923
  const dx = next.x - prev.x;
1781
2924
  const dy = next.y - prev.y;
1782
2925
  const l = Math.hypot(dx, dy);
1783
2926
  if (l > 1e-9) {
1784
- const len = (h, to) => h ? Math.hypot(h.x, h.y) : Math.hypot(q.x - to.x, q.y - to.y) / 3;
1785
- let li = len(own[0], prev);
1786
- let lo = len(own[1], next);
2927
+ const len2 = (h, to) => h ? Math.hypot(h.x, h.y) : Math.hypot(q.x - to.x, q.y - to.y) / 3;
2928
+ let li = len2(own[0], prev);
2929
+ let lo = len2(own[1], next);
1787
2930
  if (a.type === "symmetric") li = lo = (li + lo) / 2;
1788
2931
  moved.in = { x: q.x - dx * li / l, y: q.y - dy * li / l };
1789
2932
  moved.out = { x: q.x + dx * lo / l, y: q.y + dy * lo / l };
@@ -1792,9 +2935,9 @@ function curvatureMoveAnchor(sp, index, q) {
1792
2935
  anchors[index] = moved;
1793
2936
  return { anchors, closed: sp.closed };
1794
2937
  }
1795
- function cutSubpathAt(sp, at) {
2938
+ function cutSubpathAt(sp, at2) {
1796
2939
  const n = sp.anchors.length;
1797
- 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);
1798
2941
  if (!cuts.length || n < 2) return [{ anchors: sp.anchors.map(cloneAnchor), closed: sp.closed }];
1799
2942
  const first = cuts[0];
1800
2943
  const run = sp.closed ? Array.from({ length: n + 1 }, (_, i) => sp.anchors[(first + i) % n]) : sp.anchors;
@@ -1824,8 +2967,8 @@ function splitSubpathAt(sp, segment, t, endTolerance = 1e-6) {
1824
2967
  const { subpath, index } = insertAnchor(sp, segment, t);
1825
2968
  return cutSubpathAt(subpath, [index]);
1826
2969
  }
1827
- var endOf = (sp, last) => sp.anchors[last ? sp.anchors.length - 1 : 0];
1828
- var dist = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
2970
+ var endOf2 = (sp, last) => sp.anchors[last ? sp.anchors.length - 1 : 0];
2971
+ var dist2 = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
1829
2972
  function reverse(sp) {
1830
2973
  return {
1831
2974
  closed: sp.closed,
@@ -1834,6 +2977,7 @@ function reverse(sp) {
1834
2977
  if (a.out) next.in = { ...a.out };
1835
2978
  if (a.in) next.out = { ...a.in };
1836
2979
  if (a.type) next.type = a.type;
2980
+ if (a.corner) next.corner = { ...a.corner };
1837
2981
  return next;
1838
2982
  })
1839
2983
  };
@@ -1851,7 +2995,7 @@ function joinSubpaths(subpaths, tolerance) {
1851
2995
  const sp = open[0];
1852
2996
  const first = sp.anchors[0];
1853
2997
  const last = sp.anchors[sp.anchors.length - 1];
1854
- if (sp.anchors.length > 2 && dist(first, last) <= tolerance) {
2998
+ if (sp.anchors.length > 2 && dist2(first, last) <= tolerance) {
1855
2999
  sp.anchors.pop();
1856
3000
  sp.anchors[0] = tidy({ ...first, in: _nullishCoalesce(last.in, () => ( null)) }, false);
1857
3001
  } else {
@@ -1867,7 +3011,7 @@ function joinSubpaths(subpaths, tolerance) {
1867
3011
  for (let j = i + 1; j < open.length; j++) {
1868
3012
  for (const ei of [false, true]) {
1869
3013
  for (const ej of [false, true]) {
1870
- const d = dist(endOf(open[i], ei), endOf(open[j], ej));
3014
+ const d = dist2(endOf2(open[i], ei), endOf2(open[j], ej));
1871
3015
  if (d < best.d) best = { d, i, j, ei, ej };
1872
3016
  }
1873
3017
  }
@@ -1905,10 +3049,10 @@ function smoothSubpath(sp, amount = 1, only) {
1905
3049
  const prev = orig[(i + n - 1) % n];
1906
3050
  const next = orig[(i + 1) % n];
1907
3051
  const p = orig[i];
1908
- const dir = unit(next.x - prev.x, next.y - prev.y);
3052
+ const dir = unit2(next.x - prev.x, next.y - prev.y);
1909
3053
  if (!dir) continue;
1910
- const li = dist(p, prev) / 3;
1911
- const lo = dist(p, next) / 3;
3054
+ const li = dist2(p, prev) / 3;
3055
+ const lo = dist2(p, next) / 3;
1912
3056
  const hin = _nullishCoalesce(p.in, () => ( p));
1913
3057
  const hout = _nullishCoalesce(p.out, () => ( p));
1914
3058
  const target = (h, x, y) => ({ x: h.x + (x - h.x) * t, y: h.y + (y - h.y) * t });
@@ -1935,12 +3079,12 @@ function segsOf(sp) {
1935
3079
  function segsToSubpath(segs, closed, types) {
1936
3080
  if (!segs.length) return null;
1937
3081
  const anchors = [];
1938
- const at = (p) => ({ x: p[0], y: p[1] });
1939
- 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]));
1940
3084
  for (const s of segs) {
1941
3085
  const end = s.c[s.c.length - 1];
1942
3086
  if (!s.line) anchors[anchors.length - 1].out = { x: s.c[1][0], y: s.c[1][1] };
1943
- const next = at(end);
3087
+ const next = at2(end);
1944
3088
  if (!s.line) next.in = { x: s.c[2][0], y: s.c[2][1] };
1945
3089
  anchors.push(next);
1946
3090
  }
@@ -1962,12 +3106,12 @@ function mergePair(a, b, tol) {
1962
3106
  if (a.line && b.line) {
1963
3107
  const cx = b3[0] - a0[0];
1964
3108
  const cy = b3[1] - a0[1];
1965
- const len = Math.hypot(cx, cy);
1966
- if (len < 1e-12) return null;
3109
+ const len2 = Math.hypot(cx, cy);
3110
+ if (len2 < 1e-12) return null;
1967
3111
  const mx = a3[0] - a0[0];
1968
3112
  const my = a3[1] - a0[1];
1969
- const along = (mx * cx + my * cy) / len;
1970
- return Math.abs((mx * cy - my * cx) / len) <= tol && along >= 0 && along <= len ? { 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;
1971
3115
  }
1972
3116
  const ca = a.line ? [a0, a0, a3, a3] : a.c;
1973
3117
  const cb = b.line ? [a3, a3, b3, b3] : b.c;
@@ -2006,12 +3150,12 @@ function removeRedundantPoints(sp, tolerance) {
2006
3150
  }
2007
3151
 
2008
3152
  // src/vector/pathCut.ts
2009
- var loopArea = (l) => _chunkBGNPT5PEcjs.contourSignedArea.call(void 0, l.segs);
3153
+ var loopArea = (l) => _chunkGVB7ZPLZcjs.contourSignedArea.call(void 0, l.segs);
2010
3154
  function loopSample(l) {
2011
3155
  const k = l.chord.findIndex((c) => !c);
2012
- 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);
2013
3157
  }
2014
- 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;
2015
3159
  function groupFaces(loops) {
2016
3160
  const usable = loops.filter((l) => l.segs.length && Math.abs(loopArea(l)) > 1e-12);
2017
3161
  if (!usable.length) return [];
@@ -2027,12 +3171,12 @@ function groupFaces(loops) {
2027
3171
  return outers.reverse().map((o) => faces.get(o));
2028
3172
  }
2029
3173
  function faceToPath(face) {
2030
- 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 })));
2031
3175
  }
2032
3176
  function facesOf(path, fillRule) {
2033
- const normalized = _chunkBGNPT5PEcjs.normalizeBezierPath.call(void 0, path, fillRule);
2034
- const loops = _chunkBGNPT5PEcjs.pathToContours.call(void 0, normalized).map((c) => {
2035
- 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());
2036
3180
  return { segs, chord: segs.map(() => false) };
2037
3181
  });
2038
3182
  return groupFaces(loops);
@@ -2091,7 +3235,7 @@ function lineHits(p, q, seg) {
2091
3235
  const dy = q[1] - p[1];
2092
3236
  const len2 = dx * dx + dy * dy;
2093
3237
  if (len2 < 1e-24) return [];
2094
- const h = (pt) => (pt[0] - p[0]) * -dy + (pt[1] - p[1]) * dx;
3238
+ const h = (pt2) => (pt2[0] - p[0]) * -dy + (pt2[1] - p[1]) * dx;
2095
3239
  let coeffs;
2096
3240
  if (seg.length === 2) coeffs = [0, 0, h(seg[1]) - h(seg[0]), h(seg[0])];
2097
3241
  else {
@@ -2099,10 +3243,10 @@ function lineHits(p, q, seg) {
2099
3243
  coeffs = [-h0 + 3 * h1 - 3 * h2 + h3, 3 * h0 - 6 * h1 + 3 * h2, -3 * h0 + 3 * h1, h0];
2100
3244
  }
2101
3245
  if (coeffs.every((c) => Math.abs(c) < 1e-12 * Math.max(1, len2))) return [];
2102
- const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt) => Math.abs(pt[0]) + Math.abs(pt[1])));
3246
+ const scale = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
2103
3247
  const out = [];
2104
3248
  for (const t of rootsInUnit(...coeffs, scale)) {
2105
- const point = _chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, t);
3249
+ const point = _chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, t);
2106
3250
  const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
2107
3251
  if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
2108
3252
  }
@@ -2115,7 +3259,7 @@ function arc(l, a, b, eps) {
2115
3259
  const segs = [];
2116
3260
  const chord = [];
2117
3261
  const push = (k, t0, t1) => {
2118
- 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);
2119
3263
  if (!degenerate(piece, eps)) {
2120
3264
  segs.push(piece);
2121
3265
  chord.push(l.chord[k]);
@@ -2140,7 +3284,7 @@ function knifePath(path, fillRule, knife) {
2140
3284
  if (pts.length < 2) return null;
2141
3285
  let faces = facesOf(path, fillRule);
2142
3286
  if (!faces.length) return null;
2143
- const b = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, path);
3287
+ const b = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, path);
2144
3288
  const size = b ? Math.max(b.maxX - b.minX, b.maxY - b.minY, 1) : 1;
2145
3289
  const eps = size * 1e-9;
2146
3290
  let cursor = -Infinity;
@@ -2154,7 +3298,7 @@ function knifePath(path, fillRule, knife) {
2154
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]) };
2155
3299
  face2.loops.forEach(
2156
3300
  (l, li) => l.segs.forEach((seg, si) => {
2157
- const sb = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, seg);
3301
+ const sb = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, seg);
2158
3302
  if (sb.maxX < kb.minX - eps || sb.minX > kb.maxX + eps || sb.maxY < kb.minY - eps || sb.minY > kb.maxY + eps) return;
2159
3303
  for (const h of lineHits(pts[m], pts[m + 1], seg)) {
2160
3304
  const s = m + h.s;
@@ -2169,7 +3313,7 @@ function knifePath(path, fillRule, knife) {
2169
3313
  const m = Math.min(pts.length - 2, Math.floor(s));
2170
3314
  const u = s - m;
2171
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];
2172
- 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;
2173
3317
  };
2174
3318
  for (let i = 0; i < unique.length - 1; i++) {
2175
3319
  const from2 = unique[i];
@@ -2229,33 +3373,33 @@ function eraserRegion(points, width) {
2229
3373
  ];
2230
3374
  }
2231
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] }))];
2232
- 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" });
2233
3377
  }
2234
3378
  function eraseFromFill(path, fillRule, region) {
2235
- const overlap = _chunkBGNPT5PEcjs.pathBoolean.call(void 0, "intersection", [{ path, fillRule }, { path: region }]).path;
2236
- 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;
2237
- 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;
2238
3382
  return splitIntoFaces(rest);
2239
3383
  }
2240
3384
  function eraseFromOutline(sp, region) {
2241
- 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));
2242
3386
  if (!regionSegs.length) return null;
2243
- const rb = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, region);
3387
+ const rb = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, region);
2244
3388
  const count = subpathSegmentCount(sp);
2245
3389
  if (!rb || count === 0) return null;
2246
- const removed = (p) => _chunkBGNPT5PEcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
3390
+ const removed = (p) => _chunkGVB7ZPLZcjs.pointInBezierPath.call(void 0, region, p, "nonzero");
2247
3391
  const runs = [];
2248
3392
  let cur = [];
2249
3393
  let any = false;
2250
3394
  for (let i = 0; i < count; i++) {
2251
3395
  const seg = subpathSegment(sp, i);
2252
- const sb = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, seg);
3396
+ const sb = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, seg);
2253
3397
  const ts = [0, 1];
2254
3398
  if (!(sb.maxX < rb.minX || sb.minX > rb.maxX || sb.maxY < rb.minY || sb.minY > rb.maxY)) {
2255
3399
  for (const r of regionSegs) {
2256
- const b = _chunkBGNPT5PEcjs.segmentBounds.call(void 0, r);
3400
+ const b = _chunkGVB7ZPLZcjs.segmentBounds.call(void 0, r);
2257
3401
  if (b.maxX < sb.minX || b.minX > sb.maxX || b.maxY < sb.minY || b.minY > sb.maxY) continue;
2258
- 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);
2259
3403
  }
2260
3404
  }
2261
3405
  ts.sort((a, b) => a - b);
@@ -2263,7 +3407,7 @@ function eraseFromOutline(sp, region) {
2263
3407
  const t0 = ts[k];
2264
3408
  const t1 = ts[k + 1];
2265
3409
  if (t1 - t0 < 1e-9) continue;
2266
- if (removed(_chunkBGNPT5PEcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
3410
+ if (removed(_chunkGVB7ZPLZcjs.segmentPoint.call(void 0, seg, (t0 + t1) / 2))) {
2267
3411
  any = true;
2268
3412
  if (cur.length) runs.push(cur);
2269
3413
  cur = [];
@@ -2282,7 +3426,7 @@ function eraseFromOutline(sp, region) {
2282
3426
  for (const run of runs) {
2283
3427
  const anchors = [];
2284
3428
  for (const { seg, t0, t1 } of run) {
2285
- 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);
2286
3430
  const line2 = c.length === 2;
2287
3431
  const start = c[0];
2288
3432
  const end = c[c.length - 1];
@@ -2326,12 +3470,12 @@ function simplifyPolyline(points, tolerance) {
2326
3470
  const [ax, ay] = points[a];
2327
3471
  const dx = points[b][0] - ax;
2328
3472
  const dy = points[b][1] - ay;
2329
- const len = Math.hypot(dx, dy);
3473
+ const len2 = Math.hypot(dx, dy);
2330
3474
  let far = -1;
2331
3475
  let farD = 0;
2332
3476
  for (let i = a + 1; i < b; i++) {
2333
3477
  const [px, py] = points[i];
2334
- const d = len < 1e-12 ? Math.hypot(px - ax, py - ay) : Math.abs((px - ax) * dy - (py - ay) * dx) / len;
3478
+ const d = len2 < 1e-12 ? Math.hypot(px - ax, py - ay) : Math.abs((px - ax) * dy - (py - ay) * dx) / len2;
2335
3479
  if (d > farD) {
2336
3480
  farD = d;
2337
3481
  far = i;
@@ -2399,7 +3543,7 @@ function vectorInkShapeName(shape) {
2399
3543
  return shape.points.length === 3 ? "triangle" : "polygon";
2400
3544
  }
2401
3545
  }
2402
- 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]);
2403
3547
  function bbox(pts) {
2404
3548
  if (!pts.length) return [0, 0, 0, 0];
2405
3549
  let [x0, y0] = pts[0];
@@ -2417,7 +3561,7 @@ function segDistance(a, b, p) {
2417
3561
  const dy = b[1] - a[1];
2418
3562
  const len2 = dx * dx + dy * dy;
2419
3563
  const t = len2 > 0 ? Math.min(1, Math.max(0, ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / len2)) : 0;
2420
- return dist2([a[0] + t * dx, a[1] + t * dy], p);
3564
+ return dist3([a[0] + t * dx, a[1] + t * dy], p);
2421
3565
  }
2422
3566
  function polygonDistance(c, p) {
2423
3567
  let best = Infinity;
@@ -2443,11 +3587,11 @@ function snapSquare(x, y, w, h) {
2443
3587
  }
2444
3588
  function turning(a, b, c) {
2445
3589
  const u = [b[0] - a[0], b[1] - a[1]];
2446
- const v = [c[0] - b[0], c[1] - b[1]];
3590
+ const v2 = [c[0] - b[0], c[1] - b[1]];
2447
3591
  const lu = Math.hypot(u[0], u[1]);
2448
- const lv = Math.hypot(v[0], v[1]);
3592
+ const lv = Math.hypot(v2[0], v2[1]);
2449
3593
  if (lu <= 0 || lv <= 0) return 0;
2450
- return Math.acos(Math.min(1, Math.max(-1, (u[0] * v[0] + u[1] * v[1]) / (lu * lv)))) * 180 / Math.PI;
3594
+ return Math.acos(Math.min(1, Math.max(-1, (u[0] * v2[0] + u[1] * v2[1]) / (lu * lv)))) * 180 / Math.PI;
2451
3595
  }
2452
3596
  function rdp(p, eps) {
2453
3597
  const n = p.length;
@@ -2479,7 +3623,7 @@ function corners(p, diag) {
2479
3623
  let far = 0;
2480
3624
  let farD = 0;
2481
3625
  p.forEach((q, i) => {
2482
- const d = dist2(start, q);
3626
+ const d = dist3(start, q);
2483
3627
  if (d > farD) {
2484
3628
  farD = d;
2485
3629
  far = i;
@@ -2506,7 +3650,7 @@ function corners(p, diag) {
2506
3650
  }
2507
3651
  let short = -1;
2508
3652
  for (let j = 0; j < n; j++) {
2509
- if (dist2(ring[j], ring[(j + 1) % n]) < 0.1 * diag) {
3653
+ if (dist3(ring[j], ring[(j + 1) % n]) < 0.1 * diag) {
2510
3654
  short = j;
2511
3655
  break;
2512
3656
  }
@@ -2546,8 +3690,8 @@ function recognizeInkShape(points, options = {}) {
2546
3690
  const diag = Math.hypot(w, h);
2547
3691
  if (size < minSize) return null;
2548
3692
  let length2 = 0;
2549
- for (let i = 1; i < p.length; i++) length2 += dist2(p[i - 1], p[i]);
2550
- 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);
2551
3695
  if (gap > 0.85 * length2 && gap >= minSize) {
2552
3696
  const off = Math.max(...p.map((q) => segDistance(first, last, q)));
2553
3697
  if (off <= Math.max(0.06 * gap, 1.5 * minSize / 6)) return { kind: "line", a: first, b: last };
@@ -2573,8 +3717,31 @@ function recognizeInkShape(points, options = {}) {
2573
3717
 
2574
3718
  // src/vector/controller.ts
2575
3719
  var DEFAULT_VECTOR_SNAP = { grid: 0, objects: true, guides: true, threshold: 5, angles: VECTOR_CONSTRUCTION_ANGLES };
2576
- 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
+ };
2577
3728
  var VECTOR_PENCIL_STRING_PX = 100;
3729
+ var VECTOR_STYLE_KEYS = [
3730
+ "fill",
3731
+ "stroke",
3732
+ "strokeWidth",
3733
+ "strokeLinecap",
3734
+ "strokeLinejoin",
3735
+ "strokeDasharray",
3736
+ "fillRule",
3737
+ "strokeAlign",
3738
+ "startArrow",
3739
+ "endArrow",
3740
+ "arrowAlign",
3741
+ "startArrowScale",
3742
+ "endArrowScale",
3743
+ "strokeWidthProfile"
3744
+ ];
2578
3745
  var VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
2579
3746
  var DEFAULT_DEFAULTS = {
2580
3747
  fill: "#d4d4d8",
@@ -2602,7 +3769,7 @@ function getEditablePathIds(state) {
2602
3769
  }
2603
3770
  }
2604
3771
  }
2605
- return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _27 => _27(state.document.nodes, id), 'optionalAccess', _28 => _28.type]) === "path");
3772
+ return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _62 => _62(state.document.nodes, id), 'optionalAccess', _63 => _63.type]) === "path");
2606
3773
  }
2607
3774
  function getVisibleAnchors(state) {
2608
3775
  const out = [];
@@ -2617,7 +3784,7 @@ function getVisibleHandles(state) {
2617
3784
  const seen = /* @__PURE__ */ new Set();
2618
3785
  const push = (node, s, i, handle) => {
2619
3786
  const sp = node.subpaths[s];
2620
- const a = _optionalChain([sp, 'optionalAccess', _29 => _29.anchors, 'access', _30 => _30[i]]);
3787
+ const a = _optionalChain([sp, 'optionalAccess', _64 => _64.anchors, 'access', _65 => _65[i]]);
2621
3788
  if (!a || !a[handle]) return;
2622
3789
  const key = `${node.id}:${s}:${i}:${handle}`;
2623
3790
  if (seen.has(key)) return;
@@ -2626,8 +3793,8 @@ function getVisibleHandles(state) {
2626
3793
  };
2627
3794
  if (state.tool === "pen" && state.pen) {
2628
3795
  const node = findVectorNode(state.document.nodes, state.pen.nodeId);
2629
- const sp = _optionalChain([node, 'optionalAccess', _31 => _31.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
2630
- if (_optionalChain([node, 'optionalAccess', _32 => _32.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) {
2631
3798
  const last = sp.anchors.length - 1;
2632
3799
  push(node, state.pen.subpath, last, "in");
2633
3800
  push(node, state.pen.subpath, last, "out");
@@ -2635,9 +3802,9 @@ function getVisibleHandles(state) {
2635
3802
  return out;
2636
3803
  }
2637
3804
  if (state.tool !== "direct") return out;
2638
- const editable = new Set(getEditablePathIds(state));
3805
+ const editable2 = new Set(getEditablePathIds(state));
2639
3806
  for (const ref of state.selectedAnchors) {
2640
- if (!editable.has(ref.nodeId)) continue;
3807
+ if (!editable2.has(ref.nodeId)) continue;
2641
3808
  const node = findVectorNode(state.document.nodes, ref.nodeId);
2642
3809
  const sp = node.subpaths[ref.subpath];
2643
3810
  if (!sp) continue;
@@ -2651,9 +3818,82 @@ function getVisibleHandles(state) {
2651
3818
  }
2652
3819
  return out;
2653
3820
  }
3821
+ var VECTOR_CORNER_WIDGET_INSET = 10;
3822
+ function getVectorCornerWidgets(state) {
3823
+ if (state.tool !== "direct" || state.pen) return [];
3824
+ const zoom = state.viewport.zoom;
3825
+ const out = [];
3826
+ for (const id of getEditablePathIds(state)) {
3827
+ const node = findVectorNode(state.document.nodes, id);
3828
+ const picked = state.selectedAnchors.filter((r) => r.nodeId === id);
3829
+ const m = _nullishCoalesce(vectorWorldMatrix(state.document.nodes, id), () => ( IDENTITY_MATRIX));
3830
+ for (const c of vectorCorners(node.subpaths)) {
3831
+ if (picked.length && !picked.some((r) => r.subpath === c.subpath && r.index === c.index)) continue;
3832
+ const side = Math.min(c.lu, c.lv);
3833
+ if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
3834
+ const a = node.subpaths[c.subpath].anchors[c.index];
3835
+ const radius = _nullishCoalesce(_optionalChain([a, 'access', _68 => _68.corner, 'optionalAccess', _69 => _69.radius]), () => ( 0));
3836
+ const drawn = fitCornerRadius(c, radius);
3837
+ const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
3838
+ const p = applyMatrix(m, cornerBisectorPoint(c, k));
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
+ });
3890
+ }
3891
+ }
3892
+ return out;
3893
+ }
2654
3894
  function getAnchor(doc, ref) {
2655
3895
  const node = findVectorNode(doc.nodes, ref.nodeId);
2656
- return _optionalChain([node, 'optionalAccess', _33 => _33.type]) === "path" ? _optionalChain([node, 'access', _34 => _34.subpaths, 'access', _35 => _35[ref.subpath], 'optionalAccess', _36 => _36.anchors, 'access', _37 => _37[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;
2657
3897
  }
2658
3898
  function getAnchorWorld(doc, ref) {
2659
3899
  const a = getAnchor(doc, ref);
@@ -2721,9 +3961,10 @@ var TOOL_LABELS = {
2721
3961
  line: "Line",
2722
3962
  scissors: "Scissors",
2723
3963
  knife: "Knife",
2724
- eraser: "Eraser"
3964
+ eraser: "Eraser",
3965
+ width: "Width"
2725
3966
  };
2726
- 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" };
2727
3968
  function vectorToolLabel(tool) {
2728
3969
  return TOOL_LABELS[tool];
2729
3970
  }
@@ -2739,7 +3980,8 @@ var VECTOR_TOOL_SHORTCUTS = {
2739
3980
  line: "L",
2740
3981
  scissors: "C",
2741
3982
  knife: "K",
2742
- eraser: "E"
3983
+ eraser: "E",
3984
+ width: "W"
2743
3985
  };
2744
3986
  var VECTOR_TOOL_GLYPHS = {
2745
3987
  select: "\u2196",
@@ -2753,7 +3995,8 @@ var VECTOR_TOOL_GLYPHS = {
2753
3995
  line: "\u2571",
2754
3996
  scissors: "\u2702",
2755
3997
  knife: "\u27CB",
2756
- eraser: "\u232B"
3998
+ eraser: "\u232B",
3999
+ width: "\u21F9"
2757
4000
  };
2758
4001
  var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
2759
4002
  var BOOLEAN_OPS = {
@@ -2807,35 +4050,36 @@ var VectorEditorController = (_class3 = class {
2807
4050
 
2808
4051
  __init9() {this.selection = []}
2809
4052
  __init10() {this.anchors = []}
2810
- __init11() {this.editingId = null}
2811
- __init12() {this.pen = null}
4053
+ __init11() {this.widthPoint = null}
4054
+ __init12() {this.editingId = null}
4055
+ __init13() {this.pen = null}
2812
4056
 
2813
4057
 
2814
4058
 
2815
4059
 
2816
4060
 
2817
4061
 
2818
- __init13() {this.hover = null}
2819
- __init14() {this.pointer = null}
2820
- __init15() {this.drag = null}
2821
- __init16() {this.interaction = null}
2822
- __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}
2823
4067
  /** Whether the last `snapDrawn` landed on an object (guides may be hidden). */
2824
- __init18() {this.snappedToObject = false}
4068
+ __init19() {this.snappedToObject = false}
2825
4069
  /** Drawing targets for hovering the pen and shape tools, kept while the document and the excluded path stay the same. */
2826
- __init19() {this.hoverTargets = null}
2827
- __init20() {this.spaceHeld = false}
2828
- __init21() {this.clipboard = null}
4070
+ __init20() {this.hoverTargets = null}
4071
+ __init21() {this.spaceHeld = false}
4072
+ __init22() {this.clipboard = null}
2829
4073
 
2830
4074
 
2831
- __init22() {this.listeners = /* @__PURE__ */ new Set()}
2832
- __init23() {this.cleanups = []}
2833
- __init24() {this.applying = false}
4075
+ __init23() {this.listeners = /* @__PURE__ */ new Set()}
4076
+ __init24() {this.cleanups = []}
4077
+ __init25() {this.applying = false}
2834
4078
  /** Set while a commit records a change that was already applied, so `restore` still reports it. */
2835
- __init25() {this.committing = false}
2836
- __init26() {this.revision = 0}
4079
+ __init26() {this.committing = false}
4080
+ __init27() {this.revision = 0}
2837
4081
 
2838
- 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);
2839
4083
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
2840
4084
  this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
2841
4085
  this.tool = _nullishCoalesce(options.tool, () => ( "select"));
@@ -2864,9 +4108,9 @@ var VectorEditorController = (_class3 = class {
2864
4108
  // State
2865
4109
  // -------------------------------------------------------------------------
2866
4110
  /** The current snapshot (stable until the next change). */
2867
- __init27() {this.getState = () => this.state}
4111
+ __init28() {this.getState = () => this.state}
2868
4112
  /** Calls `listener` after every change. Returns an unsubscribe function. */
2869
- __init28() {this.subscribe = (listener) => {
4113
+ __init29() {this.subscribe = (listener) => {
2870
4114
  this.listeners.add(listener);
2871
4115
  return () => {
2872
4116
  this.listeners.delete(listener);
@@ -2885,6 +4129,7 @@ var VectorEditorController = (_class3 = class {
2885
4129
  tool: this.tool,
2886
4130
  selection: this.selection,
2887
4131
  selectedAnchors: this.anchors,
4132
+ selectedWidthPoint: this.widthPoint,
2888
4133
  editingId: this.editingId,
2889
4134
  selectionBounds: this.boundsOf(this.selection),
2890
4135
  selectionFrame: this.frameOf(this.selectedRoots()),
@@ -2909,7 +4154,7 @@ var VectorEditorController = (_class3 = class {
2909
4154
  notify(docChanged = false) {
2910
4155
  this.revision++;
2911
4156
  this.state = this.computeState();
2912
- if (docChanged) _optionalChain([this, 'access', _38 => _38.onChange, 'optionalCall', _39 => _39(this.doc)]);
4157
+ if (docChanged) _optionalChain([this, 'access', _84 => _84.onChange, 'optionalCall', _85 => _85(this.doc)]);
2913
4158
  const state = this.state;
2914
4159
  this.listeners.forEach((listener) => {
2915
4160
  try {
@@ -2939,20 +4184,24 @@ var VectorEditorController = (_class3 = class {
2939
4184
  return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
2940
4185
  }
2941
4186
  cursorFor() {
2942
- if (_optionalChain([this, 'access', _40 => _40.drag, 'optionalAccess', _41 => _41.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
2943
- if (_optionalChain([this, 'access', _42 => _42.drag, 'optionalAccess', _43 => _43.kind]) === "rotate") return "grabbing";
2944
- if (_optionalChain([this, 'access', _44 => _44.drag, 'optionalAccess', _45 => _45.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";
2945
4190
  const h = this.hover;
2946
4191
  switch (this.tool) {
2947
4192
  case "select":
2948
- if (_optionalChain([h, 'optionalAccess', _46 => _46.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _47 => _47.frameOf, 'call', _48 => _48(this.selectedRoots()), 'optionalAccess', _49 => _49.angle]), () => ( 0)));
2949
- return _optionalChain([h, 'optionalAccess', _50 => _50.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";
2950
4195
  case "direct":
2951
- if (_optionalChain([h, 'optionalAccess', _51 => _51.anchor]) || _optionalChain([h, 'optionalAccess', _52 => _52.handle])) return "pointer";
2952
- if (_optionalChain([h, 'optionalAccess', _53 => _53.segment])) return "copy";
2953
- return _optionalChain([h, 'optionalAccess', _54 => _54.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";
2954
4199
  case "pen":
2955
- return _optionalChain([h, 'optionalAccess', _55 => _55.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";
2956
4205
  default:
2957
4206
  return "crosshair";
2958
4207
  }
@@ -2972,7 +4221,7 @@ var VectorEditorController = (_class3 = class {
2972
4221
  this.selection = s.selection.filter((id) => this.getNode(id));
2973
4222
  this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
2974
4223
  this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
2975
- this.pen = s.pen && _optionalChain([this, 'access', _56 => _56.getNode, 'call', _57 => _57(s.pen.nodeId), 'optionalAccess', _58 => _58.subpaths, 'optionalAccess', _59 => _59[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;
2976
4225
  } finally {
2977
4226
  this.applying = false;
2978
4227
  }
@@ -3033,12 +4282,12 @@ var VectorEditorController = (_class3 = class {
3033
4282
  // Viewport
3034
4283
  // -------------------------------------------------------------------------
3035
4284
  screenToDocument(p) {
3036
- const v = this.viewport;
3037
- return { x: (p.x - v.x) / v.zoom, y: (p.y - v.y) / v.zoom };
4285
+ const v2 = this.viewport;
4286
+ return { x: (p.x - v2.x) / v2.zoom, y: (p.y - v2.y) / v2.zoom };
3038
4287
  }
3039
4288
  documentToScreen(p) {
3040
- const v = this.viewport;
3041
- return { x: p.x * v.zoom + v.x, y: p.y * v.zoom + v.y };
4289
+ const v2 = this.viewport;
4290
+ return { x: p.x * v2.zoom + v2.x, y: p.y * v2.zoom + v2.y };
3042
4291
  }
3043
4292
  setViewport(viewport) {
3044
4293
  const next = { ...this.viewport, ...viewport };
@@ -3078,6 +4327,7 @@ var VectorEditorController = (_class3 = class {
3078
4327
  this.tool = tool;
3079
4328
  this.hover = null;
3080
4329
  this.guides = null;
4330
+ this.widthPoint = null;
3081
4331
  if (tool !== "direct") {
3082
4332
  this.anchors = [];
3083
4333
  this.editingId = null;
@@ -3105,9 +4355,9 @@ var VectorEditorController = (_class3 = class {
3105
4355
  toggleSnapping(on = !this.snap.objects) {
3106
4356
  this.setSnap({ objects: on });
3107
4357
  }
3108
- gridValue(v) {
4358
+ gridValue(v2) {
3109
4359
  const g = this.snap.grid;
3110
- return g > 0 ? Math.round(v / g) * g : v;
4360
+ return g > 0 ? Math.round(v2 / g) * g : v2;
3111
4361
  }
3112
4362
  snapPoint(p) {
3113
4363
  return { x: this.gridValue(p.x), y: this.gridValue(p.y) };
@@ -3136,7 +4386,7 @@ var VectorEditorController = (_class3 = class {
3136
4386
  if (res.kind) {
3137
4387
  this.snappedToObject = true;
3138
4388
  this.setGuides(res);
3139
- return _optionalChain([leave, 'optionalAccess', _60 => _60.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) };
3140
4390
  }
3141
4391
  }
3142
4392
  this.setGuides(null);
@@ -3154,14 +4404,14 @@ var VectorEditorController = (_class3 = class {
3154
4404
  /** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
3155
4405
  hoverSnap(p, input) {
3156
4406
  if (this.tool === "pen") {
3157
- const sp = this.pen ? _optionalChain([this, 'access', _61 => _61.getNode, 'call', _62 => _62(this.pen.nodeId), 'optionalAccess', _63 => _63.subpaths, 'access', _64 => _64[this.pen.subpath]]) : void 0;
3158
- if (this.pen && sp && _optionalChain([this, 'access', _65 => _65.hover, 'optionalAccess', _66 => _66.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])) {
3159
4409
  this.setGuides(null);
3160
4410
  return p;
3161
4411
  }
3162
4412
  const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
3163
4413
  const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
3164
- return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _67 => _67.pen, 'optionalAccess', _68 => _68.nodeId]), () => ( null))));
4414
+ return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _118 => _118.pen, 'optionalAccess', _119 => _119.nodeId]), () => ( null))));
3165
4415
  }
3166
4416
  if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
3167
4417
  this.setGuides(null);
@@ -3263,7 +4513,7 @@ var VectorEditorController = (_class3 = class {
3263
4513
  let subpath;
3264
4514
  switch (input.kind) {
3265
4515
  case "rect":
3266
- subpath = rectSubpath(b, _nullishCoalesce(input.cornerRadius, () => ( d.cornerRadius)));
4516
+ subpath = rectSubpath(b);
3267
4517
  break;
3268
4518
  case "ellipse":
3269
4519
  subpath = ellipseSubpath(b);
@@ -3278,10 +4528,13 @@ var VectorEditorController = (_class3 = class {
3278
4528
  subpath = lineSubpath({ x: b.x, y: b.y }, { x: b.x + b.width, y: b.y + b.height });
3279
4529
  break;
3280
4530
  }
4531
+ const radius = _nullishCoalesce(input.cornerRadius, () => ( d.cornerRadius));
4532
+ const kind = _nullishCoalesce(input.cornerKind, () => ( d.cornerKind));
4533
+ if ((input.kind === "rect" || input.kind === "polygon" || input.kind === "star") && radius > 0) subpath = setSubpathCorners(subpath, radius, kind);
3281
4534
  const names = { rect: "Rectangle", ellipse: "Ellipse", polygon: "Polygon", star: "Star", line: "Line" };
3282
4535
  const style = this.styleFor(input.kind === "line");
3283
4536
  const own = {};
3284
- for (const key of ["fill", "stroke", "strokeWidth", "strokeLinecap", "strokeLinejoin", "strokeDasharray", "fillRule"]) {
4537
+ for (const key of VECTOR_STYLE_KEYS) {
3285
4538
  if (input[key] !== void 0) own[key] = input[key];
3286
4539
  }
3287
4540
  return { id: this.newId(), type: "path", name: _nullishCoalesce(input.name, () => ( names[input.kind])), ...style, ...own, subpaths: [subpath] };
@@ -3304,6 +4557,9 @@ var VectorEditorController = (_class3 = class {
3304
4557
  if (d.strokeLinejoin) out.strokeLinejoin = d.strokeLinejoin;
3305
4558
  if (d.strokeDasharray) out.strokeDasharray = d.strokeDasharray;
3306
4559
  if (d.fillRule) out.fillRule = d.fillRule;
4560
+ if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
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 }));
3307
4563
  return out;
3308
4564
  }
3309
4565
  /** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
@@ -3324,10 +4580,129 @@ var VectorEditorController = (_class3 = class {
3324
4580
  }
3325
4581
  if (!targets.size) return { ok: false, message: "Nothing is selected." };
3326
4582
  this.change(_nullishCoalesce(options.label, () => ( "Change Style")), () => {
3327
- 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));
4584
+ });
4585
+ return { ok: true };
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
+ }
4616
+ /**
4617
+ * Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
4618
+ * anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
4619
+ * corners sharp again. Corners shrink to fit when drawn, so any radius is accepted.
4620
+ */
4621
+ setCornerRadius(radius, options = {}) {
4622
+ if (!Number.isFinite(radius) || radius < 0) return { ok: false, message: "The radius must be a number of at least 0." };
4623
+ const refs = this.cornerTargets(options);
4624
+ if (!refs.length) return { ok: false, message: "Nothing is selected." };
4625
+ this.change(radius > 0 ? "Corner Radius" : "Sharpen Corners", () => {
4626
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, radius, options.kind));
4627
+ });
4628
+ return { ok: true };
4629
+ }
4630
+ /** Sets the live corner kind of the same anchors as `setCornerRadius`, keeping their radii. */
4631
+ setCornerKind(kind, options = {}) {
4632
+ const refs = this.cornerTargets(options);
4633
+ if (!refs.length) return { ok: false, message: "Nothing is selected." };
4634
+ this.change("Corner 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));
3328
4636
  });
3329
4637
  return { ok: true };
3330
4638
  }
4639
+ /** Anchors a corner change applies to (see `setCornerRadius`). */
4640
+ cornerTargets(options) {
4641
+ if (options.refs) return options.refs.filter((r) => getAnchor(this.doc, r));
4642
+ if (!options.ids && this.tool === "direct" && this.anchors.length) return this.anchors.slice();
4643
+ const out = [];
4644
+ for (const id of _nullishCoalesce(options.ids, () => ( this.selection))) {
4645
+ const node = this.getNode(id);
4646
+ if (!node) continue;
4647
+ for (const p of collectPathNodes(node)) {
4648
+ if (p.locked) continue;
4649
+ p.subpaths.forEach((sp, s) => sp.anchors.forEach((_, i) => out.push({ nodeId: p.id, subpath: s, index: i })));
4650
+ }
4651
+ }
4652
+ return out;
4653
+ }
4654
+ /**
4655
+ * Outline Stroke: replaces each selected (or given) path's stroke with a filled path of the
4656
+ * area it paints (alignment, joins, caps, dashes and arrowheads included), placed above a copy
4657
+ * of the path that keeps the fill (left out when there is none). Returns the new paths' ids.
4658
+ */
4659
+ outlineStroke(options = {}) {
4660
+ const roots = _nullishCoalesce(options.ids, () => ( this.selectedRoots()));
4661
+ const paths = [];
4662
+ for (const id of roots) {
4663
+ const node = this.getNode(id);
4664
+ if (node) {
4665
+ for (const p of collectPathNodes(node)) if (!p.locked && hasPaint(p.stroke) && (_nullishCoalesce(p.strokeWidth, () => ( 1))) > 0) paths.push(p);
4666
+ }
4667
+ }
4668
+ if (!paths.length) return [];
4669
+ const created = [];
4670
+ const replace = /* @__PURE__ */ new Map();
4671
+ for (const p of paths) {
4672
+ const outline = bezierToSubpaths(vectorStrokeOutlinePath(subpathsToBezier(p.subpaths), p));
4673
+ if (!outline.length) continue;
4674
+ const {
4675
+ stroke,
4676
+ strokeWidth: _w,
4677
+ strokeLinecap: _c,
4678
+ strokeLinejoin: _j,
4679
+ strokeDasharray: _d,
4680
+ strokeAlign: _a,
4681
+ startArrow: _s,
4682
+ endArrow: _e,
4683
+ arrowAlign: _al,
4684
+ startArrowScale: _ss,
4685
+ endArrowScale: _es,
4686
+ strokeWidthProfile: _wp,
4687
+ ...rest
4688
+ } = cloneVectorNode(p);
4689
+ const id = this.newId();
4690
+ const strokeNode = { ...rest, id, name: `${_nullishCoalesce(p.name, () => ( "Path"))} Stroke`, subpaths: outline, fill: _nullishCoalesce(stroke, () => ( null)), stroke: null, fillRule: "nonzero" };
4691
+ const list = hasPaint(p.fill) ? [{ ...rest, stroke: null }, strokeNode] : [strokeNode];
4692
+ created.push(id);
4693
+ replace.set(p.id, list);
4694
+ }
4695
+ if (!created.length) return [];
4696
+ this.change("Outline Stroke", () => {
4697
+ let nodes = this.doc.nodes;
4698
+ for (const [id, list] of replace) nodes = spliceTree(nodes, id, list);
4699
+ this.setDoc(nodes);
4700
+ this.selection = created;
4701
+ this.anchors = [];
4702
+ this.editingId = null;
4703
+ });
4704
+ return created;
4705
+ }
3331
4706
  rename(id, name) {
3332
4707
  return this.updateNode(id, { name }, { label: "Rename" });
3333
4708
  }
@@ -3396,10 +4771,10 @@ var VectorEditorController = (_class3 = class {
3396
4771
  return this.selection.filter((id) => {
3397
4772
  let covered = false;
3398
4773
  const loc = locateVectorNode(this.doc.nodes, id);
3399
- let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _69 => _69.parent]), () => ( null));
4774
+ let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _127 => _127.parent]), () => ( null));
3400
4775
  while (parent) {
3401
4776
  if (this.selection.includes(parent.id)) covered = true;
3402
- parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _70 => _70(this.doc.nodes, parent.id), 'optionalAccess', _71 => _71.parent]), () => ( null));
4777
+ parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _128 => _128(this.doc.nodes, parent.id), 'optionalAccess', _129 => _129.parent]), () => ( null));
3403
4778
  }
3404
4779
  return !covered;
3405
4780
  });
@@ -3582,8 +4957,8 @@ var VectorEditorController = (_class3 = class {
3582
4957
  if (!ids.length) return null;
3583
4958
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
3584
4959
  if (locs.some((l) => !l)) return null;
3585
- const parent = _nullishCoalesce(_optionalChain([locs, 'access', _72 => _72[0], 'access', _73 => _73.parent, 'optionalAccess', _74 => _74.id]), () => ( null));
3586
- if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _75 => _75.parent, 'optionalAccess', _76 => _76.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;
3587
4962
  const id = this.newId();
3588
4963
  this.change("Group", () => {
3589
4964
  const set = new Set(ids);
@@ -3606,11 +4981,11 @@ var VectorEditorController = (_class3 = class {
3606
4981
  }
3607
4982
  /** Replaces selected groups with their children. */
3608
4983
  ungroup(options = {}) {
3609
- const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _77 => _77.type]) === "group");
4984
+ const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _135 => _135.type]) === "group");
3610
4985
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
3611
4986
  const set = new Set(groups.map((g) => g.id));
3612
4987
  this.change("Ungroup", () => {
3613
- const flatten = (list) => {
4988
+ const flatten2 = (list) => {
3614
4989
  const out = [];
3615
4990
  let changed = false;
3616
4991
  for (const n of list) {
@@ -3622,14 +4997,14 @@ var VectorEditorController = (_class3 = class {
3622
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)
3623
4998
  );
3624
4999
  } else if (n.type === "group") {
3625
- const children = flatten(n.children);
5000
+ const children = flatten2(n.children);
3626
5001
  if (children !== n.children) changed = true;
3627
5002
  out.push(children !== n.children ? { ...n, children } : n);
3628
5003
  } else out.push(n);
3629
5004
  }
3630
5005
  return changed ? out : list;
3631
5006
  };
3632
- this.setDoc(flatten(this.doc.nodes));
5007
+ this.setDoc(flatten2(this.doc.nodes));
3633
5008
  this.selection = groups.flatMap((g) => g.children.map((c) => c.id));
3634
5009
  });
3635
5010
  return { ok: true };
@@ -3666,7 +5041,7 @@ var VectorEditorController = (_class3 = class {
3666
5041
  });
3667
5042
  const paths = [...new Set(order)].map((id2) => this.getNode(id2));
3668
5043
  if (paths.length < 2) return null;
3669
- const result = _chunkBGNPT5PEcjs.pathBoolean.call(void 0,
5044
+ const result = _chunkGVB7ZPLZcjs.pathBoolean.call(void 0,
3670
5045
  BOOLEAN_OPS[op],
3671
5046
  paths.map((p) => ({
3672
5047
  path: vectorPathWorldBezier(
@@ -3716,11 +5091,11 @@ var VectorEditorController = (_class3 = class {
3716
5091
  /** A document-space path mapped into the parent space of node `id`. */
3717
5092
  intoParentSpace(id, path) {
3718
5093
  const parent = _nullishCoalesce(vectorParentMatrix(this.doc.nodes, id), () => ( IDENTITY_MATRIX));
3719
- 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));
3720
5095
  }
3721
5096
  /** Splits selected compound paths into one path per subpath. */
3722
5097
  breakApart(options = {}) {
3723
- const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _78 => _78.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);
3724
5099
  if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
3725
5100
  const created = [];
3726
5101
  const set = new Set(paths.map((p) => p.id));
@@ -3782,7 +5157,7 @@ var VectorEditorController = (_class3 = class {
3782
5157
  /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
3783
5158
  curvatureMoved(doc, ref, to) {
3784
5159
  const node = findVectorNode(doc.nodes, ref.nodeId);
3785
- if (_optionalChain([node, 'optionalAccess', _79 => _79.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;
3786
5161
  const q = this.toLocal(ref.nodeId, to, doc);
3787
5162
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
3788
5163
  const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
@@ -3791,7 +5166,7 @@ var VectorEditorController = (_class3 = class {
3791
5166
  /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
3792
5167
  bent(doc, ref, t, p) {
3793
5168
  const node = findVectorNode(doc.nodes, ref.nodeId);
3794
- if (_optionalChain([node, 'optionalAccess', _80 => _80.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;
3795
5170
  const q = this.toLocal(ref.nodeId, p, doc);
3796
5171
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
3797
5172
  return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
@@ -3802,7 +5177,7 @@ var VectorEditorController = (_class3 = class {
3802
5177
  */
3803
5178
  bendSegment(ref, to, t = 0.5) {
3804
5179
  const node = this.getNode(ref.nodeId);
3805
- const sp = _optionalChain([node, 'optionalAccess', _81 => _81.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
5180
+ const sp = _optionalChain([node, 'optionalAccess', _139 => _139.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
3806
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." };
3807
5182
  if (node.locked) return { ok: false, message: "The path is locked." };
3808
5183
  this.change("Bend Segment", () => {
@@ -3837,7 +5212,7 @@ var VectorEditorController = (_class3 = class {
3837
5212
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
3838
5213
  insertAnchorAt(nodeId, point) {
3839
5214
  const node = this.getNode(nodeId);
3840
- if (_optionalChain([node, 'optionalAccess', _82 => _82.type]) !== "path") return null;
5215
+ if (_optionalChain([node, 'optionalAccess', _140 => _140.type]) !== "path") return null;
3841
5216
  const hit = nearestSegment(node, this.toLocal(nodeId, point));
3842
5217
  if (!hit) return null;
3843
5218
  const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
@@ -3881,7 +5256,7 @@ var VectorEditorController = (_class3 = class {
3881
5256
  setClosed(closed, nodeId) {
3882
5257
  const targets = nodeId ? (() => {
3883
5258
  const n = this.getNode(nodeId);
3884
- return _optionalChain([n, 'optionalAccess', _83 => _83.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 })) : [];
3885
5260
  })() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
3886
5261
  if (!targets.length) return { ok: false, message: "Select a path or points." };
3887
5262
  this.change(closed ? "Close Path" : "Open Path", () => {
@@ -3899,7 +5274,12 @@ var VectorEditorController = (_class3 = class {
3899
5274
  const set = new Set(ids.flatMap((id) => this.getNode(id) ? collectPathNodes(this.getNode(id)) : []).map((p) => p.id));
3900
5275
  if (!set.size) return { ok: false, message: "Select a path." };
3901
5276
  this.change("Reverse Path", () => {
3902
- 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
+ );
3903
5283
  this.anchors = [];
3904
5284
  });
3905
5285
  return { ok: true };
@@ -3930,7 +5310,7 @@ var VectorEditorController = (_class3 = class {
3930
5310
  const map = (p) => applyMatrix(m, p);
3931
5311
  return from.subpaths.map((sp) => ({
3932
5312
  closed: sp.closed,
3933
- anchors: sp.anchors.map((a) => ({ ...map(a), ...a.in ? { in: map(a.in) } : {}, ...a.out ? { out: map(a.out) } : {}, ...a.type ? { type: a.type } : {} }))
5313
+ anchors: sp.anchors.map((a) => ({ ...map(a), ...a.in ? { in: map(a.in) } : {}, ...a.out ? { out: map(a.out) } : {}, ...a.type ? { type: a.type } : {}, ...a.corner ? { corner: { ...a.corner } } : {} }))
3934
5314
  }));
3935
5315
  }
3936
5316
  /**
@@ -3980,9 +5360,9 @@ var VectorEditorController = (_class3 = class {
3980
5360
  const cx = points.reduce((s, p) => s + p.at.x, 0) / points.length;
3981
5361
  const cy = points.reduce((s, p) => s + p.at.y, 0) / points.length;
3982
5362
  this.change("Average", () => {
3983
- for (const { ref, at } of points) {
3984
- const dx = axis === "horizontal" ? 0 : cx - at.x;
3985
- 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;
3986
5366
  if (dx || dy) this.doc = this.movedAnchors(this.doc, [ref], dx, dy);
3987
5367
  }
3988
5368
  });
@@ -4035,7 +5415,7 @@ var VectorEditorController = (_class3 = class {
4035
5415
  */
4036
5416
  cutPathAt(nodeId, point) {
4037
5417
  const node = this.getNode(nodeId);
4038
- if (_optionalChain([node, 'optionalAccess', _84 => _84.type]) !== "path" || node.locked) return null;
5418
+ if (_optionalChain([node, 'optionalAccess', _144 => _144.type]) !== "path" || node.locked) return null;
4039
5419
  const local = this.toLocal(nodeId, point);
4040
5420
  const hit = nearestSegment(node, local);
4041
5421
  if (!hit) return null;
@@ -4066,8 +5446,8 @@ var VectorEditorController = (_class3 = class {
4066
5446
  const node = this.getNode(id);
4067
5447
  if (node.locked) continue;
4068
5448
  const ids = this.applyCuts(node, (sp, i) => {
4069
- const at = list.filter((r) => r.subpath === i).map((r) => r.index);
4070
- 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;
4071
5451
  return pieces && !(pieces.length === 1 && pieces[0].closed === sp.closed) ? pieces : null;
4072
5452
  });
4073
5453
  if (ids) created.push(...ids);
@@ -4085,7 +5465,7 @@ var VectorEditorController = (_class3 = class {
4085
5465
  if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
4086
5466
  });
4087
5467
  const visible = (p) => !blocked.has(p.id);
4088
- if (_optionalChain([ids, 'optionalAccess', _85 => _85.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);
4089
5469
  const box = polylineBounds(points.map(toPoint2), pad);
4090
5470
  if (!box) return [];
4091
5471
  const out = [];
@@ -4141,10 +5521,10 @@ var VectorEditorController = (_class3 = class {
4141
5521
  const region = eraserRegion(pts, width);
4142
5522
  if (!region.length) return [];
4143
5523
  const plan = [];
4144
- const rb = _chunkBGNPT5PEcjs.bezierPathBounds.call(void 0, region);
5524
+ const rb = _chunkGVB7ZPLZcjs.bezierPathBounds.call(void 0, region);
4145
5525
  const reach = rb ? [{ x: rb.minX, y: rb.minY }, { x: rb.maxX, y: rb.maxY }] : points;
4146
5526
  for (const node of this.strokeTargets(reach, options.ids, 0)) {
4147
- 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)));
4148
5528
  const live = node.subpaths.filter((sp) => sp.anchors.length > 1);
4149
5529
  if (!live.length) continue;
4150
5530
  if (hasPaint(node.fill) && live.every((sp) => sp.closed)) {
@@ -4171,7 +5551,7 @@ var VectorEditorController = (_class3 = class {
4171
5551
  finishPath() {
4172
5552
  if (!this.pen) return;
4173
5553
  const node = this.getNode(this.pen.nodeId);
4174
- const sp = _optionalChain([node, 'optionalAccess', _86 => _86.subpaths, 'access', _87 => _87[this.pen.subpath]]);
5554
+ const sp = _optionalChain([node, 'optionalAccess', _146 => _146.subpaths, 'access', _147 => _147[this.pen.subpath]]);
4175
5555
  const pen = this.pen;
4176
5556
  this.pen = null;
4177
5557
  this.pointer = null;
@@ -4215,19 +5595,19 @@ var VectorEditorController = (_class3 = class {
4215
5595
  if (trimmed.startsWith("{")) {
4216
5596
  try {
4217
5597
  const data = JSON.parse(trimmed);
4218
- if (_optionalChain([data, 'optionalAccess', _88 => _88.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
4219
- } catch (e2) {
5598
+ if (_optionalChain([data, 'optionalAccess', _148 => _148.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
5599
+ } catch (e3) {
4220
5600
  nodes = null;
4221
5601
  }
4222
5602
  } else if (/<svg[\s>]/i.test(trimmed)) {
4223
5603
  try {
4224
5604
  nodes = parseVectorSvg(trimmed).nodes;
4225
- } catch (e3) {
5605
+ } catch (e4) {
4226
5606
  nodes = null;
4227
5607
  }
4228
5608
  }
4229
5609
  } else nodes = this.clipboard;
4230
- if (!_optionalChain([nodes, 'optionalAccess', _89 => _89.length])) return [];
5610
+ if (!_optionalChain([nodes, 'optionalAccess', _149 => _149.length])) return [];
4231
5611
  const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
4232
5612
  const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
4233
5613
  this.change("Paste", () => {
@@ -4278,13 +5658,29 @@ var VectorEditorController = (_class3 = class {
4278
5658
  hitHandle(screen) {
4279
5659
  for (const ref of getVisibleHandles(this.view()).reverse()) {
4280
5660
  const a = getAnchorWorld(this.doc, ref);
4281
- const h = _optionalChain([a, 'optionalAccess', _90 => _90[ref.handle]]);
5661
+ const h = _optionalChain([a, 'optionalAccess', _150 => _150[ref.handle]]);
4282
5662
  if (!h) continue;
4283
5663
  const s = this.documentToScreen(h);
4284
5664
  if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
4285
5665
  }
4286
5666
  return null;
4287
5667
  }
5668
+ cornerView() {
5669
+ return { ...this.view(), viewport: this.viewport };
5670
+ }
5671
+ hitCornerWidget(screen) {
5672
+ let best = null;
5673
+ let bestD = Infinity;
5674
+ for (const w of getVectorCornerWidgets(this.cornerView())) {
5675
+ const s = this.documentToScreen(w);
5676
+ const d = Math.hypot(s.x - screen.x, s.y - screen.y);
5677
+ if (d <= this.hitRadius && d < bestD) {
5678
+ best = w;
5679
+ bestD = d;
5680
+ }
5681
+ }
5682
+ return best;
5683
+ }
4288
5684
  hitAnchor(screen, refs) {
4289
5685
  let best = null;
4290
5686
  let bestD = Infinity;
@@ -4343,6 +5739,8 @@ var VectorEditorController = (_class3 = class {
4343
5739
  if (handle) return { nodeId: handle.nodeId, handle };
4344
5740
  const anchor = this.hitAnchor(screen, getVisibleAnchors(this.view()));
4345
5741
  if (anchor) return { nodeId: anchor.nodeId, anchor };
5742
+ const widget = this.hitCornerWidget(screen);
5743
+ if (widget) return { nodeId: widget.ref.nodeId, corner: widget.ref };
4346
5744
  for (const id of getEditablePathIds(this.view())) {
4347
5745
  const node = this.getNode(id);
4348
5746
  const seg = nearestSegment(node, this.toLocal(id, p));
@@ -4363,10 +5761,131 @@ var VectorEditorController = (_class3 = class {
4363
5761
  const seg = this.nearestOutline(p);
4364
5762
  return seg ? { nodeId: seg.nodeId, segment: seg } : null;
4365
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
+ }
4366
5770
  default:
4367
5771
  return null;
4368
5772
  }
4369
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
+ }
4370
5889
  /** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
4371
5890
  nearestOutline(p) {
4372
5891
  let best = null;
@@ -4414,7 +5933,9 @@ var VectorEditorController = (_class3 = class {
4414
5933
  case "pen":
4415
5934
  return this.downPen(screen, p, input);
4416
5935
  case "pencil":
4417
- return this.downPencil(p);
5936
+ return this.downPencil(p, input);
5937
+ case "width":
5938
+ return this.downWidth(screen, p, input);
4418
5939
  case "scissors": {
4419
5940
  const seg = this.nearestOutline(p);
4420
5941
  if (seg) this.cutPathAt(seg.nodeId, seg.point);
@@ -4500,6 +6021,13 @@ var VectorEditorController = (_class3 = class {
4500
6021
  this.notify();
4501
6022
  return;
4502
6023
  }
6024
+ const widget = this.hitCornerWidget(screen);
6025
+ if (widget) {
6026
+ const refs = getVectorCornerWidgets(this.cornerView()).map((w) => w.ref);
6027
+ this.drag = { kind: "corner", widget, refs, start: p, before: this.snapshot(), base: this.doc, moved: false, alt: !!input.alt, shift: !!input.shift };
6028
+ this.notify();
6029
+ return;
6030
+ }
4503
6031
  for (const id of getEditablePathIds(this.view())) {
4504
6032
  const node = this.getNode(id);
4505
6033
  const seg = nearestSegment(node, this.toLocal(id, p));
@@ -4543,7 +6071,7 @@ var VectorEditorController = (_class3 = class {
4543
6071
  }
4544
6072
  downPen(screen, p, input) {
4545
6073
  const before = this.snapshot();
4546
- const sp = this.pen ? _optionalChain([this, 'access', _91 => _91.getNode, 'call', _92 => _92(this.pen.nodeId), 'optionalAccess', _93 => _93.subpaths, 'access', _94 => _94[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;
4547
6075
  if (this.pen && sp) {
4548
6076
  const pen = this.pen;
4549
6077
  const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
@@ -4557,10 +6085,10 @@ var VectorEditorController = (_class3 = class {
4557
6085
  }
4558
6086
  const last = getAnchorWorld(this.doc, { nodeId: pen.nodeId, subpath: pen.subpath, index: sp.anchors.length - 1 });
4559
6087
  const leave = { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null };
4560
- let pt2 = this.snapDrawn(p, leave, input, () => this.drawingTargets(pen.nodeId));
4561
- pt2 = this.toLocal(pen.nodeId, pt2);
6088
+ let pt3 = this.snapDrawn(p, leave, input, () => this.drawingTargets(pen.nodeId));
6089
+ pt3 = this.toLocal(pen.nodeId, pt3);
4562
6090
  const node2 = this.getNode(pen.nodeId);
4563
- const anchors = [...sp.anchors, { x: pt2.x, y: pt2.y }];
6091
+ const anchors = [...sp.anchors, { x: pt3.x, y: pt3.y }];
4564
6092
  this.replaceNode(pen.nodeId, { ...node2, subpaths: node2.subpaths.map((s, i) => i === pen.subpath ? { ...s, anchors } : s) });
4565
6093
  const ref2 = { nodeId: pen.nodeId, subpath: pen.subpath, index: anchors.length - 1 };
4566
6094
  this.anchors = [ref2];
@@ -4588,8 +6116,8 @@ var VectorEditorController = (_class3 = class {
4588
6116
  this.notify(this.doc !== before.doc);
4589
6117
  return;
4590
6118
  }
4591
- const pt = this.snapDrawn(p, null, input, () => this.drawingTargets(null));
4592
- const node = { id: this.newId(), type: "path", name: "Path", ...this.styleFor(true), subpaths: [{ anchors: [{ x: pt.x, y: pt.y }], closed: false }] };
6119
+ const pt2 = this.snapDrawn(p, null, input, () => this.drawingTargets(null));
6120
+ const node = { id: this.newId(), type: "path", name: "Path", ...this.styleFor(true), subpaths: [{ anchors: [{ x: pt2.x, y: pt2.y }], closed: false }] };
4593
6121
  this.setDoc([...this.doc.nodes, node]);
4594
6122
  this.pen = { nodeId: node.id, subpath: 0 };
4595
6123
  this.selection = [node.id];
@@ -4600,7 +6128,7 @@ var VectorEditorController = (_class3 = class {
4600
6128
  this.interaction = { type: "pen", nodeId: node.id };
4601
6129
  this.notify(true);
4602
6130
  }
4603
- downPencil(p) {
6131
+ downPencil(p, input = {}) {
4604
6132
  const before = this.snapshot();
4605
6133
  const node = {
4606
6134
  id: this.newId(),
@@ -4614,7 +6142,11 @@ var VectorEditorController = (_class3 = class {
4614
6142
  this.setDoc([...this.doc.nodes, node]);
4615
6143
  const lazy = new LazyMouse(this.editing.pencilSmoothing * VECTOR_PENCIL_STRING_PX / this.viewport.zoom);
4616
6144
  lazy.push([p.x, p.y]);
4617
- 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 };
4618
6150
  this.interaction = { type: "draw", tool: "pencil", nodeId: node.id, bounds: { x: p.x, y: p.y, width: 0, height: 0 } };
4619
6151
  this.notify(true);
4620
6152
  }
@@ -4737,13 +6269,25 @@ var VectorEditorController = (_class3 = class {
4737
6269
  case "handle": {
4738
6270
  const anchor = getAnchorWorld(drag.base, drag.ref);
4739
6271
  const leave = anchor ? { at: { x: anchor.x, y: anchor.y }, constrain: input.shift ? "angle" : null } : null;
4740
- const pt = this.snapDrawn(p, leave, input, () => drag.targets ??= VectorSnapTargets.forHandle(drag.base, drag.ref.nodeId));
4741
- const local = this.toLocal(drag.ref.nodeId, pt, drag.base);
6272
+ const pt2 = this.snapDrawn(p, leave, input, () => drag.targets ??= VectorSnapTargets.forHandle(drag.base, drag.ref.nodeId));
6273
+ const local = this.toLocal(drag.ref.nodeId, pt2, drag.base);
4742
6274
  this.doc = this.updateAnchors(drag.base, [drag.ref], (a) => moveAnchorHandle(a, drag.ref.handle, local, input.alt));
4743
6275
  this.interaction = { type: "handle", ref: drag.ref };
4744
6276
  this.notify(true);
4745
6277
  return;
4746
6278
  }
6279
+ case "corner": {
6280
+ if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 3) return;
6281
+ drag.moved = true;
6282
+ const c = drag.widget.corner;
6283
+ const inv = invertMatrix(this.worldOf(drag.widget.ref.nodeId, drag.base));
6284
+ const d = applyMatrixLinear(inv, { x: p.x - drag.start.x, y: p.y - drag.start.y });
6285
+ const radius = Math.max(0, Math.min(c.maxRadius, drag.widget.drawnRadius + cornerRadiusChange(c, d)));
6286
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, radius));
6287
+ this.interaction = { type: "corner", ref: drag.widget.ref, radius };
6288
+ this.notify(true);
6289
+ return;
6290
+ }
4747
6291
  case "bend": {
4748
6292
  if (!drag.moved && Math.hypot(p.x - drag.start.x, p.y - drag.start.y) * this.viewport.zoom < 2) return;
4749
6293
  drag.moved = true;
@@ -4769,10 +6313,10 @@ var VectorEditorController = (_class3 = class {
4769
6313
  return;
4770
6314
  }
4771
6315
  this.guides = null;
4772
- let pt = p;
4773
- if (input.shift) pt = constrain45(anchor, pt);
6316
+ let pt2 = p;
6317
+ if (input.shift) pt2 = constrain45(anchor, pt2);
4774
6318
  const side = drag.closing ? "in" : "out";
4775
- const pos = this.toLocal(drag.ref.nodeId, drag.closing ? { x: 2 * anchor.x - pt.x, y: 2 * anchor.y - pt.y } : pt, drag.base);
6319
+ const pos = this.toLocal(drag.ref.nodeId, drag.closing ? { x: 2 * anchor.x - pt2.x, y: 2 * anchor.y - pt2.y } : pt2, drag.base);
4776
6320
  this.doc = this.updateAnchors(drag.base, [drag.ref], (a) => {
4777
6321
  if (drag.outOnly || drag.altBreaks && input.alt) return moveAnchorHandle(a, side, pos, true);
4778
6322
  return moveAnchorHandle({ ...a, type: "symmetric" }, side, pos);
@@ -4782,7 +6326,7 @@ var VectorEditorController = (_class3 = class {
4782
6326
  }
4783
6327
  case "shape": {
4784
6328
  const node = this.getNode(drag.nodeId);
4785
- if (_optionalChain([node, 'optionalAccess', _95 => _95.type]) !== "path") return;
6329
+ if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) !== "path") return;
4786
6330
  const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
4787
6331
  let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
4788
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);
@@ -4811,19 +6355,43 @@ var VectorEditorController = (_class3 = class {
4811
6355
  this.notify(true);
4812
6356
  return;
4813
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
+ }
4814
6380
  case "pencil": {
4815
6381
  const last = drag.points[drag.points.length - 1];
4816
6382
  let moved = false;
6383
+ if (typeof input.pressure === "number" && Number.isFinite(input.pressure)) drag.pressure = input.pressure;
4817
6384
  for (const q of drag.lazy.push([p.x, p.y])) {
4818
6385
  if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
4819
6386
  drag.points.push(q);
6387
+ _optionalChain([drag, 'access', _162 => _162.pressures, 'optionalAccess', _163 => _163.push, 'call', _164 => _164(drag.pressure)]);
4820
6388
  moved = true;
4821
6389
  }
4822
6390
  const string = drag.lazy.string();
4823
6391
  const node = this.getNode(drag.nodeId);
4824
- if (_optionalChain([node, 'optionalAccess', _96 => _96.type]) !== "path") return;
6392
+ if (_optionalChain([node, 'optionalAccess', _165 => _165.type]) !== "path") return;
4825
6393
  if (!moved) {
4826
- if (string && _optionalChain([this, 'access', _97 => _97.interaction, 'optionalAccess', _98 => _98.type]) === "draw") {
6394
+ if (string && _optionalChain([this, 'access', _166 => _166.interaction, 'optionalAccess', _167 => _167.type]) === "draw") {
4827
6395
  this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
4828
6396
  this.notify();
4829
6397
  }
@@ -4859,8 +6427,8 @@ var VectorEditorController = (_class3 = class {
4859
6427
  }
4860
6428
  this.setGuides(res);
4861
6429
  return {
4862
- dx: _optionalChain([res, 'optionalAccess', _99 => _99.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
4863
- dy: _optionalChain([res, 'optionalAccess', _100 => _100.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
4864
6432
  };
4865
6433
  }
4866
6434
  /** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
@@ -4885,19 +6453,19 @@ var VectorEditorController = (_class3 = class {
4885
6453
  const guides = { lines: [], labels: [] };
4886
6454
  for (const axis of ["x", "y"]) {
4887
6455
  const d = end[axis] - start[axis];
4888
- const len = Math.abs(d) > 1e-9 ? targets.matchSize(Math.abs(d) * k, tol) : null;
4889
- if (len === null || len <= 0) continue;
4890
- out[axis] = start[axis] + Math.sign(d) * len / k;
6456
+ const len2 = Math.abs(d) > 1e-9 ? targets.matchSize(Math.abs(d) * k, tol) : null;
6457
+ if (len2 === null || len2 <= 0) continue;
6458
+ out[axis] = start[axis] + Math.sign(d) * len2 / k;
4891
6459
  const from = { ...out, [axis]: fromCentre ? 2 * start[axis] - out[axis] : start[axis] };
4892
6460
  guides.lines.push({ kind: "size", x1: from.x, y1: from.y, x2: out.x, y2: out.y });
4893
- guides.labels.push({ kind: "size", x: (from.x + out.x) / 2, y: (from.y + out.y) / 2, text: `${axis === "x" ? "W" : "H"} ${Math.round(len * 100) / 100}` });
6461
+ guides.labels.push({ kind: "size", x: (from.x + out.x) / 2, y: (from.y + out.y) / 2, text: `${axis === "x" ? "W" : "H"} ${Math.round(len2 * 100) / 100}` });
4894
6462
  }
4895
6463
  this.setGuides(guides);
4896
6464
  return out;
4897
6465
  }
4898
6466
  /** Primary button released. */
4899
6467
  pointerUp(screen, input = {}) {
4900
- 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 });
4901
6469
  const drag = this.drag;
4902
6470
  this.drag = null;
4903
6471
  this.interaction = null;
@@ -4935,6 +6503,29 @@ var VectorEditorController = (_class3 = class {
4935
6503
  return;
4936
6504
  }
4937
6505
  return this.commit("Bend Segment", drag.before);
6506
+ case "corner": {
6507
+ if (drag.moved) return this.commit("Corner Radius", drag.before);
6508
+ if (drag.alt) {
6509
+ const kind = nextVectorCornerKind(drag.widget.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));
6511
+ return this.commit("Corner Kind", drag.before);
6512
+ }
6513
+ const ref = drag.widget.ref;
6514
+ const has = this.anchors.some((a) => sameRef(a, ref));
6515
+ this.editingId = ref.nodeId;
6516
+ this.anchors = drag.shift ? has ? this.anchors.filter((a) => !sameRef(a, ref)) : [...this.anchors, ref] : [ref];
6517
+ this.notify();
6518
+ return;
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
+ }
4938
6529
  case "stroke": {
4939
6530
  this.notify();
4940
6531
  if (drag.tool === "knife") this.knife(drag.points);
@@ -4960,19 +6551,22 @@ var VectorEditorController = (_class3 = class {
4960
6551
  }
4961
6552
  case "pencil": {
4962
6553
  const node = this.getNode(drag.nodeId);
4963
- if (_optionalChain([node, 'optionalAccess', _101 => _101.type]) !== "path" || drag.points.length < 2) {
6554
+ if (_optionalChain([node, 'optionalAccess', _172 => _172.type]) !== "path" || drag.points.length < 2) {
4964
6555
  this.doc = drag.before.doc;
4965
6556
  this.notify(true);
4966
6557
  return;
4967
6558
  }
4968
- 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
+ }
4969
6563
  const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
4970
6564
  if (shape) {
4971
6565
  this.replaceNode(drag.nodeId, { ...node, name: inkShapeLabel(shape), subpaths: [inkShapeSubpath(shape)] });
4972
6566
  this.selection = [drag.nodeId];
4973
6567
  return this.commit(`Draw ${inkShapeLabel(shape)}`, drag.before);
4974
6568
  }
4975
- 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 });
4976
6570
  const anchors = [];
4977
6571
  for (const c of fitted) {
4978
6572
  if (!anchors.length) anchors.push({ x: c.p0[0], y: c.p0[1] });
@@ -4980,7 +6574,8 @@ var VectorEditorController = (_class3 = class {
4980
6574
  anchors.push({ x: c.p3[0], y: c.p3[1], in: { x: c.p2[0], y: c.p2[1] } });
4981
6575
  }
4982
6576
  const subpaths = anchors.length > 1 ? bezierToSubpaths(subpathsToBezier([{ anchors, closed: false }])) : node.subpaths;
4983
- 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 } : {} });
4984
6579
  this.selection = [drag.nodeId];
4985
6580
  return this.commit("Draw", drag.before);
4986
6581
  }
@@ -5034,7 +6629,7 @@ var VectorEditorController = (_class3 = class {
5034
6629
  this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
5035
6630
  return;
5036
6631
  }
5037
- const id = _nullishCoalesce(_optionalChain([this, 'access', _102 => _102.hover, 'optionalAccess', _103 => _103.segment, 'optionalAccess', _104 => _104.nodeId]), () => ( this.editingId));
6632
+ const id = _nullishCoalesce(_optionalChain([this, 'access', _176 => _176.hover, 'optionalAccess', _177 => _177.segment, 'optionalAccess', _178 => _178.nodeId]), () => ( this.editingId));
5038
6633
  if (id) {
5039
6634
  const node = this.getNode(id);
5040
6635
  const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
@@ -5102,7 +6697,10 @@ var VectorEditorController = (_class3 = class {
5102
6697
  switch (key) {
5103
6698
  case "Escape":
5104
6699
  if (this.drag) this.cancelDrag();
5105
- 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();
5106
6704
  else if (this.tool === "direct" && this.anchors.length) {
5107
6705
  this.anchors = [];
5108
6706
  this.notify();
@@ -5114,7 +6712,7 @@ var VectorEditorController = (_class3 = class {
5114
6712
  this.finishPath();
5115
6713
  return true;
5116
6714
  }
5117
- if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _105 => _105.getNode, 'call', _106 => _106(this.selection[0]), 'optionalAccess', _107 => _107.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") {
5118
6716
  this.tool = "direct";
5119
6717
  this.editingId = this.selection[0];
5120
6718
  this.notify();
@@ -5123,6 +6721,7 @@ var VectorEditorController = (_class3 = class {
5123
6721
  return false;
5124
6722
  case "Delete":
5125
6723
  case "Backspace":
6724
+ if (this.tool === "width" && this.widthPoint) return this.removeWidthPoint(this.widthPoint).ok;
5126
6725
  if (this.pen) {
5127
6726
  const node = this.getNode(this.pen.nodeId);
5128
6727
  const sp = node.subpaths[this.pen.subpath];
@@ -5179,9 +6778,9 @@ function inkShapeSubpath(shape) {
5179
6778
  function constrain45(origin, p) {
5180
6779
  const dx = p.x - origin.x;
5181
6780
  const dy = p.y - origin.y;
5182
- const len = Math.hypot(dx, dy);
6781
+ const len2 = Math.hypot(dx, dy);
5183
6782
  const a = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * (Math.PI / 4);
5184
- return { x: origin.x + Math.cos(a) * len, y: origin.y + Math.sin(a) * len };
6783
+ return { x: origin.x + Math.cos(a) * len2, y: origin.y + Math.sin(a) * len2 };
5185
6784
  }
5186
6785
  function scaleFor(handle, b, p, mods = {}) {
5187
6786
  const west = handle.includes("w");
@@ -5204,6 +6803,31 @@ function scaleFor(handle, b, p, mods = {}) {
5204
6803
  }
5205
6804
  return { sx, sy, origin };
5206
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
+ }
5207
6831
 
5208
6832
  // src/vector/render.ts
5209
6833
  var fmt = (n) => Math.round(n * 100) / 100;
@@ -5218,17 +6842,19 @@ function renderNode(node, defs, idPrefix) {
5218
6842
  const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}`);
5219
6843
  if (fill.gradient) defs.push(fill.gradient);
5220
6844
  if (stroke.gradient) defs.push(stroke.gradient);
6845
+ const strokeParts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${idPrefix}clip-${node.id}`) : void 0;
5221
6846
  return {
5222
6847
  kind: "path",
5223
6848
  id: node.id,
5224
6849
  ...node.transform ? { transform: fmtMatrix(node.transform) } : {},
6850
+ ...strokeParts ? { strokeParts } : {},
5225
6851
  d: vectorPathData(node),
5226
6852
  fill: fill.value,
5227
6853
  stroke: stroke.value,
5228
6854
  strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
5229
6855
  ...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
5230
6856
  ...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
5231
- ..._optionalChain([node, 'access', _108 => _108.strokeDasharray, 'optionalAccess', _109 => _109.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
6857
+ ..._optionalChain([node, 'access', _182 => _182.strokeDasharray, 'optionalAccess', _183 => _183.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
5232
6858
  fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
5233
6859
  opacity: _nullishCoalesce(node.opacity, () => ( 1))
5234
6860
  };
@@ -5237,9 +6863,9 @@ function getVectorSceneRenderData(state, options = {}) {
5237
6863
  const defs = [];
5238
6864
  const prefix = _nullishCoalesce(options.idPrefix, () => ( "ve-"));
5239
6865
  const nodes = state.document.nodes.map((n) => renderNode(n, defs, prefix)).filter((n) => !!n);
5240
- const v = state.viewport;
6866
+ const v2 = state.viewport;
5241
6867
  return {
5242
- transform: `translate(${fmt(v.x)} ${fmt(v.y)}) scale(${v.zoom})`,
6868
+ transform: `translate(${fmt(v2.x)} ${fmt(v2.y)}) scale(${v2.zoom})`,
5243
6869
  width: state.document.width,
5244
6870
  height: state.document.height,
5245
6871
  ...state.document.background ? { background: state.document.background } : {},
@@ -5249,10 +6875,10 @@ function getVectorSceneRenderData(state, options = {}) {
5249
6875
  }
5250
6876
  var toScreen = (state, p) => ({ x: p.x * state.viewport.zoom + state.viewport.x, y: p.y * state.viewport.zoom + state.viewport.y });
5251
6877
  function screenOutline(state, node) {
5252
- const v = state.viewport;
5253
- const view = [v.zoom, 0, 0, v.zoom, v.x, v.y];
6878
+ const v2 = state.viewport;
6879
+ const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
5254
6880
  const parent = _nullishCoalesce(vectorParentMatrix(state.document.nodes, node.id), () => ( IDENTITY_MATRIX));
5255
- 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(" ");
5256
6882
  }
5257
6883
  function collectWithMatrix(node, parent) {
5258
6884
  if (node.type === "path") return [{ path: node, parent }];
@@ -5277,9 +6903,9 @@ function getVectorOverlayRenderData(state) {
5277
6903
  };
5278
6904
  for (const id of state.selection) outline(id, true);
5279
6905
  if (state.editingId) outline(state.editingId, true);
5280
- if (_optionalChain([state, 'access', _110 => _110.hover, 'optionalAccess', _111 => _111.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);
5281
6907
  let selectionBox = null;
5282
- if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _112 => _112.interaction, 'optionalAccess', _113 => _113.type]) !== "marquee") {
6908
+ if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _186 => _186.interaction, 'optionalAccess', _187 => _187.type]) !== "marquee") {
5283
6909
  const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
5284
6910
  selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
5285
6911
  }
@@ -5289,9 +6915,9 @@ function getVectorOverlayRenderData(state) {
5289
6915
  const a = getAnchorWorld(doc, ref);
5290
6916
  const s = toScreen(state, a);
5291
6917
  const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
5292
- return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _114 => _114.hover, 'optionalAccess', _115 => _115.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 };
5293
6919
  });
5294
- if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _116 => _116.hover, 'optionalAccess', _117 => _117.anchor])) {
6920
+ if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _190 => _190.hover, 'optionalAccess', _191 => _191.anchor])) {
5295
6921
  const a = getAnchorWorld(doc, state.hover.anchor);
5296
6922
  if (a) {
5297
6923
  const s = toScreen(state, a);
@@ -5300,15 +6926,15 @@ function getVectorOverlayRenderData(state) {
5300
6926
  }
5301
6927
  const handles = getVisibleHandles(state).flatMap((ref) => {
5302
6928
  const a = getAnchorWorld(doc, ref);
5303
- const h = _optionalChain([a, 'optionalAccess', _118 => _118[ref.handle]]);
6929
+ const h = _optionalChain([a, 'optionalAccess', _192 => _192[ref.handle]]);
5304
6930
  if (!a || !h) return [];
5305
6931
  const s = toScreen(state, h);
5306
6932
  const sa = toScreen(state, a);
5307
- const hovered = !!_optionalChain([state, 'access', _119 => _119.hover, 'optionalAccess', _120 => _120.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;
5308
6934
  return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
5309
6935
  });
5310
6936
  let marquee = null;
5311
- if (_optionalChain([state, 'access', _121 => _121.interaction, 'optionalAccess', _122 => _122.type]) === "marquee") {
6937
+ if (_optionalChain([state, 'access', _195 => _195.interaction, 'optionalAccess', _196 => _196.type]) === "marquee") {
5312
6938
  const b = state.interaction.bounds;
5313
6939
  const tl = toScreen(state, b);
5314
6940
  marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
@@ -5316,7 +6942,7 @@ function getVectorOverlayRenderData(state) {
5316
6942
  let penPreview = null;
5317
6943
  if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
5318
6944
  const node = findVectorNode(doc.nodes, state.pen.nodeId);
5319
- const sp = _optionalChain([node, 'optionalAccess', _123 => _123.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;
5320
6946
  const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
5321
6947
  if (last) {
5322
6948
  const a = toScreen(state, last);
@@ -5329,9 +6955,9 @@ function getVectorOverlayRenderData(state) {
5329
6955
  const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
5330
6956
  let status = null;
5331
6957
  const it = state.interaction;
5332
- if (_optionalChain([it, 'optionalAccess', _124 => _124.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
5333
- else if (_optionalChain([it, 'optionalAccess', _125 => _125.type]) === "move" || _optionalChain([it, 'optionalAccess', _126 => _126.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
5334
- else if (_optionalChain([it, 'optionalAccess', _127 => _127.type]) === "scale" || _optionalChain([it, 'optionalAccess', _128 => _128.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") {
5335
6961
  const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
5336
6962
  if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
5337
6963
  }
@@ -5348,19 +6974,39 @@ function getVectorOverlayRenderData(state) {
5348
6974
  }
5349
6975
  }
5350
6976
  let toolStroke = null;
5351
- if (_optionalChain([it, 'optionalAccess', _129 => _129.type]) === "stroke") {
6977
+ if (_optionalChain([it, 'optionalAccess', _203 => _203.type]) === "stroke") {
5352
6978
  const pts = it.points.map((q) => toScreen(state, q));
5353
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)}` : "");
5354
6980
  toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
5355
6981
  }
5356
6982
  let pencilString = null;
5357
- if (_optionalChain([it, 'optionalAccess', _130 => _130.type]) === "draw" && it.string) {
6983
+ if (_optionalChain([it, 'optionalAccess', _204 => _204.type]) === "draw" && it.string) {
5358
6984
  const a = toScreen(state, it.string[0]);
5359
6985
  const b = toScreen(state, it.string[1]);
5360
6986
  pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
5361
6987
  }
5362
- const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _131 => _131.hover, 'optionalAccess', _132 => _132.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
5363
- return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint };
6988
+ const dragged = _optionalChain([it, 'optionalAccess', _205 => _205.type]) === "corner" ? it.ref : null;
6989
+ const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
6990
+ const s = toScreen(state, w);
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 };
6992
+ });
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 };
5364
7010
  }
5365
7011
  var VECTOR_OVERLAY_THEME = {
5366
7012
  accent: "#0ea5e9",
@@ -5372,13 +7018,25 @@ var VECTOR_OVERLAY_THEME = {
5372
7018
  eraserFill: "rgba(239, 68, 68, 0.25)",
5373
7019
  transformHandleSize: 8,
5374
7020
  anchorSize: 7,
5375
- handleRadius: 3.5
7021
+ handleRadius: 3.5,
7022
+ /** Radius of a live corner widget (a ring with a dot). */
7023
+ cornerWidgetRadius: 4,
7024
+ /** Half the diagonal of a width point's diamond. */
7025
+ widthPointSize: 5
5376
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
+ }
5377
7030
  function toVectorModifiers(event) {
5378
7031
  return { shift: !!event.shiftKey, alt: !!event.altKey, mod: !!(event.metaKey || event.ctrlKey) };
5379
7032
  }
5380
7033
  function toVectorPointerInput(event) {
5381
- 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
+ };
5382
7040
  }
5383
7041
  function toVectorKeyInput(event) {
5384
7042
  return { key: event.key, ...toVectorModifiers(event) };
@@ -5388,7 +7046,7 @@ function isVectorTextTarget(target) {
5388
7046
  if (!el || typeof el !== "object") return false;
5389
7047
  const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
5390
7048
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
5391
- return !!el.isContentEditable || _optionalChain([el, 'access', _133 => _133.getAttribute, 'optionalCall', _134 => _134("contenteditable")]) === "true";
7049
+ return !!el.isContentEditable || _optionalChain([el, 'access', _217 => _217.getAttribute, 'optionalCall', _218 => _218("contenteditable")]) === "true";
5392
7050
  }
5393
7051
  function getVectorSelectionStyle(state) {
5394
7052
  const paths = [];
@@ -5396,18 +7054,75 @@ function getVectorSelectionStyle(state) {
5396
7054
  const node = findVectorNode(state.document.nodes, id);
5397
7055
  if (node) paths.push(...collect(node));
5398
7056
  }
7057
+ const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _219 => _219.selectedAnchors, 'optionalAccess', _220 => _220.length]);
7058
+ if (anchorMode) {
7059
+ for (const r of state.selectedAnchors) {
7060
+ if (paths.some((p) => p.id === r.nodeId)) continue;
7061
+ const node = findVectorNode(state.document.nodes, r.nodeId);
7062
+ if (_optionalChain([node, 'optionalAccess', _221 => _221.type]) === "path") paths.push(node);
7063
+ }
7064
+ }
5399
7065
  if (!paths.length) return null;
5400
7066
  const first = paths[0];
5401
7067
  const same = (get) => paths.every((p) => JSON.stringify(_nullishCoalesce(get(p), () => ( null))) === JSON.stringify(_nullishCoalesce(get(first), () => ( null))));
7068
+ const corners2 = [];
7069
+ for (const p of paths) {
7070
+ for (const c of vectorCorners(p.subpaths)) {
7071
+ if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
7072
+ const corner = p.subpaths[c.subpath].anchors[c.index].corner;
7073
+ corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _222 => _222.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _223 => _223.kind]), () => ( "round")) });
7074
+ }
7075
+ }
7076
+ const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
5402
7077
  return {
5403
7078
  fill: _nullishCoalesce(first.fill, () => ( null)),
5404
7079
  stroke: _nullishCoalesce(first.stroke, () => ( null)),
5405
7080
  strokeWidth: _nullishCoalesce(first.strokeWidth, () => ( 1)),
5406
7081
  opacity: _nullishCoalesce(first.opacity, () => ( 1)),
5407
- mixed: { fill: !same((p) => p.fill), stroke: !same((p) => p.stroke), strokeWidth: !same((p) => _nullishCoalesce(p.strokeWidth, () => ( 1))), opacity: !same((p) => _nullishCoalesce(p.opacity, () => ( 1))) },
7082
+ strokeAlign: _nullishCoalesce(first.strokeAlign, () => ( "center")),
7083
+ startArrow: _nullishCoalesce(first.startArrow, () => ( null)),
7084
+ endArrow: _nullishCoalesce(first.endArrow, () => ( null)),
7085
+ arrowAlign: _nullishCoalesce(first.arrowAlign, () => ( "extend")),
7086
+ startArrowScale: _nullishCoalesce(first.startArrowScale, () => ( 1)),
7087
+ endArrowScale: _nullishCoalesce(first.endArrowScale, () => ( 1)),
7088
+ strokeWidthProfile: profileOf(first),
7089
+ widthProfile: widthProfilePresetId(profileOf(first)),
7090
+ cornerRadius: c0.radius,
7091
+ cornerKind: c0.kind,
7092
+ corners: corners2.length,
7093
+ hasOpenPath: paths.some((p) => p.subpaths.some((sp) => !sp.closed && sp.anchors.length > 1)),
7094
+ mixed: {
7095
+ fill: !same((p) => p.fill),
7096
+ stroke: !same((p) => p.stroke),
7097
+ strokeWidth: !same((p) => _nullishCoalesce(p.strokeWidth, () => ( 1))),
7098
+ opacity: !same((p) => _nullishCoalesce(p.opacity, () => ( 1))),
7099
+ strokeAlign: !same((p) => _nullishCoalesce(p.strokeAlign, () => ( "center"))),
7100
+ startArrow: !same((p) => _nullishCoalesce(p.startArrow, () => ( null))),
7101
+ endArrow: !same((p) => _nullishCoalesce(p.endArrow, () => ( null))),
7102
+ arrowAlign: !same((p) => _nullishCoalesce(p.arrowAlign, () => ( "extend"))),
7103
+ startArrowScale: !same((p) => _nullishCoalesce(p.startArrowScale, () => ( 1))),
7104
+ endArrowScale: !same((p) => _nullishCoalesce(p.endArrowScale, () => ( 1))),
7105
+ widthProfile: !same(profileOf),
7106
+ cornerRadius: corners2.some((c) => c.radius !== c0.radius),
7107
+ cornerKind: corners2.some((c) => c.kind !== c0.kind)
7108
+ },
5408
7109
  pathIds: paths.map((p) => p.id)
5409
7110
  };
5410
7111
  }
7112
+ function profileOf(p) {
7113
+ const points = sanitizeWidthProfile(p.strokeWidthProfile);
7114
+ return points.length && widthProfilePresetId(points) !== "uniform" ? points : null;
7115
+ }
7116
+ var VECTOR_STROKE_ALIGN_LABELS = { center: "Center", inside: "Inside", outside: "Outside" };
7117
+ var VECTOR_CORNER_KIND_LABELS = { round: "Round", inverted: "Inverted", chamfer: "Chamfer" };
7118
+ var VECTOR_ARROWHEAD_LABELS = {
7119
+ triangle: "Triangle",
7120
+ arrow: "Arrow",
7121
+ open: "Open arrow",
7122
+ circle: "Circle",
7123
+ square: "Square",
7124
+ bar: "Bar"
7125
+ };
5411
7126
  function vectorPaintToHex(paint) {
5412
7127
  if (typeof paint !== "string") return null;
5413
7128
  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(paint.trim());
@@ -5420,7 +7135,8 @@ var VECTOR_PATH_ACTIONS = [
5420
7135
  { id: "average", label: "\u22B9", title: "Average Points", shortcut: "Alt+J", enabled: (_s, p) => p > 1, run: (c) => c.averageAnchors("both") },
5421
7136
  { id: "cut", label: "\u2702\xB7", title: "Cut at Points", enabled: (_s, p) => p > 0, run: (c) => c.cutAtAnchors() },
5422
7137
  { id: "clean", label: "\u2301", title: "Remove Redundant Points", enabled: (s, p) => s + p > 0, run: (c) => c.removeRedundantPoints() },
5423
- { id: "smooth", label: "\u223F", title: "Smooth", enabled: (s, p) => s + p > 0, run: (c) => c.smoothPaths() }
7138
+ { id: "smooth", label: "\u223F", title: "Smooth", enabled: (s, p) => s + p > 0, run: (c) => c.smoothPaths() },
7139
+ { id: "outline", label: "\u22A1", title: "Outline Stroke", enabled: (s) => s > 0, run: (c) => c.outlineStroke() }
5424
7140
  ];
5425
7141
  var VECTOR_EDIT_TOGGLES = [
5426
7142
  { key: "keepShapeOnDelete", label: "Keep", title: "Keep shape when deleting points" },
@@ -5448,6 +7164,19 @@ var POINTS = {
5448
7164
  minItems: 1,
5449
7165
  description: "Polyline [[x, y], \u2026] in document units."
5450
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
+ };
5451
7180
  var PAINT = {
5452
7181
  description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear", angle, stops: [{ offset, color }] }`.'
5453
7182
  };
@@ -5458,7 +7187,14 @@ var STYLE_PROPS = {
5458
7187
  strokeLinecap: { type: "string", enum: ["butt", "round", "square"] },
5459
7188
  strokeLinejoin: { type: "string", enum: ["miter", "round", "bevel"] },
5460
7189
  strokeDasharray: { type: "array", items: { type: "number" } },
5461
- fillRule: { type: "string", enum: ["nonzero", "evenodd"] }
7190
+ fillRule: { type: "string", enum: ["nonzero", "evenodd"] },
7191
+ strokeAlign: { type: "string", enum: ["center", "inside", "outside"], description: "Where the stroke sits on the outline (closed shapes only; open paths stroke centred)." },
7192
+ startArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the start of an open path, or null for none." },
7193
+ endArrow: { type: ["string", "null"], enum: [...VECTOR_ARROWHEADS, null], description: "Head at the end of an open path, or null for none." },
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." },
7195
+ startArrowScale: { type: "number", exclusiveMinimum: 0, description: "Start 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
5462
7198
  };
5463
7199
  var pickStyle = (params) => {
5464
7200
  const out = {};
@@ -5474,17 +7210,17 @@ function createVectorCommands(controller, options = {}) {
5474
7210
  const base = { group, ...options.scope ? { scope: options.scope } : {} };
5475
7211
  const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
5476
7212
  const ids = (params) => {
5477
- const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _135 => _135.ids]), () => ( controller.getState().selection));
7213
+ const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _224 => _224.ids]), () => ( controller.getState().selection));
5478
7214
  for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
5479
7215
  if (!list2.length) fail("Nothing is selected; pass ids.");
5480
7216
  return list2;
5481
7217
  };
5482
- const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _136 => _136.ids, 'optionalAccess', _137 => _137.length]) ? true : hasSelection();
7218
+ const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _225 => _225.ids, 'optionalAccess', _226 => _226.length]) ? true : hasSelection();
5483
7219
  const anchorsOf = (refs) => {
5484
7220
  if (!refs) return [...controller.getState().selectedAnchors];
5485
7221
  for (const r of refs) {
5486
7222
  const node = controller.getNode(r.nodeId);
5487
- if (_optionalChain([node, 'optionalAccess', _138 => _138.type]) !== "path" || !_optionalChain([node, 'access', _139 => _139.subpaths, 'access', _140 => _140[r.subpath], 'optionalAccess', _141 => _141.anchors, 'access', _142 => _142[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}".`);
5488
7224
  }
5489
7225
  return refs;
5490
7226
  };
@@ -5551,7 +7287,8 @@ function createVectorCommands(controller, options = {}) {
5551
7287
  y: { type: "number" },
5552
7288
  width: { type: "number", description: "For a line: the x offset of its end." },
5553
7289
  height: { type: "number", description: "For a line: the y offset of its end." },
5554
- cornerRadius: { type: "number", minimum: 0 },
7290
+ cornerRadius: { type: "number", minimum: 0, description: "Live corner radius of a rect, polygon or star." },
7291
+ cornerKind: { type: "string", enum: VECTOR_CORNER_KINDS },
5555
7292
  sides: { type: "integer", minimum: 3 },
5556
7293
  points: { type: "integer", minimum: 3 },
5557
7294
  innerRatio: { type: "number", minimum: 0, maximum: 1 },
@@ -5574,7 +7311,7 @@ function createVectorCommands(controller, options = {}) {
5574
7311
  run: (_ctx, params) => {
5575
7312
  const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
5576
7313
  const node = controller.getNode(id);
5577
- if (_optionalChain([node, 'optionalAccess', _143 => _143.type]) === "path" && !node.subpaths.length) {
7314
+ if (_optionalChain([node, 'optionalAccess', _232 => _232.type]) === "path" && !node.subpaths.length) {
5578
7315
  controller.undo();
5579
7316
  fail("The path data has no points.");
5580
7317
  }
@@ -5597,6 +7334,76 @@ function createVectorCommands(controller, options = {}) {
5597
7334
  return { ok: true };
5598
7335
  }
5599
7336
  },
7337
+ {
7338
+ ...base,
7339
+ id: `${prefix}setCorners`,
7340
+ label: "Corner Radius\u2026",
7341
+ keywords: ["round", "rounded", "corners", "radius", "fillet", "chamfer", "live corners"],
7342
+ description: "Sets the live corner radius (and optionally `kind`: round, inverted or chamfer) of `anchors`, else the selected points (direct tool), else every corner of `ids` / the selection. Radius 0 makes corners sharp; radii shrink to fit short sides. The anchors stay editable.",
7343
+ params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
7344
+ undoable: true,
7345
+ run: (_ctx, params) => {
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));
7350
+ else fail("Pass `radius` and / or `kind`.");
7351
+ return { ok: true };
7352
+ }
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
+ },
7392
+ {
7393
+ ...base,
7394
+ id: `${prefix}outlineStroke`,
7395
+ label: "Outline Stroke",
7396
+ keywords: ["expand", "stroke to path", "outline", "convert stroke"],
7397
+ description: "Turns the stroke of paths into filled shapes of the area it paints (alignment, caps, joins, dashes and arrowheads included). Returns the new ids.",
7398
+ params: { type: "object", properties: { ids: IDS } },
7399
+ enabled: enabledFor,
7400
+ undoable: true,
7401
+ run: (_ctx, params) => {
7402
+ const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _242 => _242.ids]) ? ids(params) : void 0 });
7403
+ if (!created.length) fail("No selected path has a stroke.");
7404
+ return { ids: created };
7405
+ }
7406
+ },
5600
7407
  {
5601
7408
  ...base,
5602
7409
  id: `${prefix}transform`,
@@ -5707,7 +7514,7 @@ function createVectorCommands(controller, options = {}) {
5707
7514
  enabled: enabledFor,
5708
7515
  undoable: true,
5709
7516
  run: (_ctx, params) => {
5710
- const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _144 => _144.name]) });
7517
+ const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _243 => _243.name]) });
5711
7518
  if (!id) fail("Only nodes that share a parent can be grouped.");
5712
7519
  return { id };
5713
7520
  }
@@ -5735,7 +7542,7 @@ function createVectorCommands(controller, options = {}) {
5735
7542
  params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
5736
7543
  undoable: true,
5737
7544
  run: (_ctx, params) => {
5738
- check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _145 => _145.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _146 => _146.keepShape]) }));
7545
+ check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _244 => _244.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _245 => _245.keepShape]) }));
5739
7546
  return { ok: true };
5740
7547
  }
5741
7548
  },
@@ -5790,7 +7597,7 @@ function createVectorCommands(controller, options = {}) {
5790
7597
  params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
5791
7598
  undoable: true,
5792
7599
  run: (_ctx, params) => {
5793
- check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _147 => _147.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _148 => _148.tolerance]) }));
7600
+ check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _246 => _246.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _247 => _247.tolerance]) }));
5794
7601
  return { selection: controller.getState().selection };
5795
7602
  }
5796
7603
  },
@@ -5804,7 +7611,7 @@ function createVectorCommands(controller, options = {}) {
5804
7611
  params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
5805
7612
  undoable: true,
5806
7613
  run: (_ctx, params) => {
5807
- check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _149 => _149.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _150 => _150.anchors]))));
7614
+ check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _248 => _248.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _249 => _249.anchors]))));
5808
7615
  return { ok: true };
5809
7616
  }
5810
7617
  },
@@ -5818,7 +7625,7 @@ function createVectorCommands(controller, options = {}) {
5818
7625
  enabled: enabledFor,
5819
7626
  undoable: true,
5820
7627
  run: (_ctx, params) => {
5821
- check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _151 => _151.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _152 => _152.tolerance]) }));
7628
+ check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _250 => _250.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _251 => _251.tolerance]) }));
5822
7629
  return { ok: true };
5823
7630
  }
5824
7631
  },
@@ -5831,7 +7638,7 @@ function createVectorCommands(controller, options = {}) {
5831
7638
  params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
5832
7639
  undoable: true,
5833
7640
  run: (_ctx, params) => {
5834
- check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _153 => _153.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _154 => _154.amount]), refs: _optionalChain([params, 'optionalAccess', _155 => _155.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]) }));
5835
7642
  return { ok: true };
5836
7643
  }
5837
7644
  },
@@ -5844,14 +7651,14 @@ function createVectorCommands(controller, options = {}) {
5844
7651
  params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
5845
7652
  undoable: true,
5846
7653
  run: (_ctx, params) => {
5847
- if (_optionalChain([params, 'optionalAccess', _156 => _156.nodeId]) !== void 0) {
7654
+ if (_optionalChain([params, 'optionalAccess', _255 => _255.nodeId]) !== void 0) {
5848
7655
  if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
5849
7656
  if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
5850
7657
  const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
5851
7658
  if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
5852
7659
  return { ids: created };
5853
7660
  }
5854
- check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _157 => _157.anchors]))));
7661
+ check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _256 => _256.anchors]))));
5855
7662
  return { ids: controller.getState().selection };
5856
7663
  }
5857
7664
  },
@@ -5888,7 +7695,7 @@ function createVectorCommands(controller, options = {}) {
5888
7695
  id: `${prefix}setEditing`,
5889
7696
  label: "Path Editing Options\u2026",
5890
7697
  keywords: ["settings", "curvature", "pencil", "smoothing", "lazy mouse", "shapes", "eraser"],
5891
- 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`).",
5892
7699
  params: {
5893
7700
  type: "object",
5894
7701
  properties: {
@@ -5896,12 +7703,13 @@ function createVectorCommands(controller, options = {}) {
5896
7703
  curvatureMoves: { type: "boolean" },
5897
7704
  pencilSmoothing: { type: "number", minimum: 0, maximum: 1 },
5898
7705
  recognizeShapes: { type: "boolean" },
5899
- eraserSize: { type: "number", exclusiveMinimum: 0 }
7706
+ eraserSize: { type: "number", exclusiveMinimum: 0 },
7707
+ pencilPressure: { type: "string", enum: ["pen", "all", "off"] }
5900
7708
  }
5901
7709
  },
5902
7710
  run: (_ctx, params) => {
5903
7711
  const patch = {};
5904
- for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize"]) if (_optionalChain([params, 'optionalAccess', _158 => _158[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];
5905
7713
  controller.setEditing(patch);
5906
7714
  return { editing: controller.getState().editing };
5907
7715
  }
@@ -5926,7 +7734,7 @@ function createVectorCommands(controller, options = {}) {
5926
7734
  keywords: ["download", "save", "markup"],
5927
7735
  description: "Returns the document as SVG markup.",
5928
7736
  params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
5929
- run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _159 => _159.precision]) }) })
7737
+ run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _258 => _258.precision]) }) })
5930
7738
  },
5931
7739
  {
5932
7740
  ...base,
@@ -6058,5 +7866,71 @@ function createVectorCommands(controller, options = {}) {
6058
7866
 
6059
7867
 
6060
7868
 
6061
- exports.DEFAULT_VECTOR_PATH_EDIT = DEFAULT_VECTOR_PATH_EDIT; exports.DEFAULT_VECTOR_SNAP = DEFAULT_VECTOR_SNAP; exports.IDENTITY_MATRIX = IDENTITY_MATRIX; exports.KAPPA = KAPPA; exports.LazyMouse = LazyMouse; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; 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_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.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.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.decomposeRigid = decomposeRigid; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.findVectorNode = findVectorNode; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; 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.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.invertMatrix = invertMatrix; 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.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.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.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.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes;
7869
+
7870
+
7871
+
7872
+
7873
+
7874
+
7875
+
7876
+
7877
+
7878
+
7879
+
7880
+
7881
+
7882
+
7883
+
7884
+
7885
+
7886
+
7887
+
7888
+
7889
+
7890
+
7891
+
7892
+
7893
+
7894
+
7895
+
7896
+
7897
+
7898
+
7899
+
7900
+
7901
+
7902
+
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;
6062
7936
  //# sourceMappingURL=index.cjs.map