@pixerate/editor 0.40.0 → 0.41.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.
@@ -40,9 +40,19 @@ var _chunkQKLRAA4Tcjs = require('../chunk-QKLRAA4T.cjs');
40
40
 
41
41
  var _chunkVNYRI265cjs = require('../chunk-VNYRI265.cjs');
42
42
 
43
+
44
+ var _chunkMR3Z4OQIcjs = require('../chunk-MR3Z4OQI.cjs');
45
+
46
+
47
+ var _chunkXZ4L3R4Kcjs = require('../chunk-XZ4L3R4K.cjs');
48
+
49
+
50
+ var _chunkA6PHCAGXcjs = require('../chunk-A6PHCAGX.cjs');
51
+
43
52
  // src/vector/types.ts
44
53
  var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
45
54
  var VECTOR_ARROWHEADS = ["triangle", "arrow", "open", "circle", "square", "bar"];
55
+ var VECTOR_BLEND_SPACINGS = ["steps", "distance", "smoothColor"];
46
56
  var VECTOR_TOOLS = [
47
57
  "select",
48
58
  "direct",
@@ -58,7 +68,8 @@ var VECTOR_TOOLS = [
58
68
  "eraser",
59
69
  "width",
60
70
  "shapeBuilder",
61
- "paintBucket"
71
+ "paintBucket",
72
+ "blend"
62
73
  ];
63
74
  var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
64
75
  var VECTOR_PATHFINDER_OPERATIONS = ["divide", "trim", "merge", "crop", "outline", "minusBack"];
@@ -1327,25 +1338,33 @@ function decomposeRigid(m) {
1327
1338
  residual: [cos * a + sin * b, -sin * a + cos * b, cos * c + sin * d, -sin * c + cos * d, 0, 0]
1328
1339
  };
1329
1340
  }
1341
+ function mapAnchor(a, m) {
1342
+ const p = applyMatrix(m, a);
1343
+ const next = { x: p.x, y: p.y };
1344
+ if (a.in) next.in = applyMatrix(m, a.in);
1345
+ if (a.out) next.out = applyMatrix(m, a.out);
1346
+ if (a.type) next.type = a.type;
1347
+ if (a.corner) next.corner = { ...a.corner };
1348
+ return next;
1349
+ }
1330
1350
  function bakeIntoGeometry(node, m) {
1331
1351
  if (isIdentityMatrix(m)) return node;
1332
- if (node.type === "group") return { ...node, children: node.children.map((c) => transformVectorNode(c, m)) };
1352
+ if (node.type === "group") {
1353
+ const group = node;
1354
+ const children = group.children.map((c) => transformVectorNode(c, m));
1355
+ const spine = _optionalChain([group, 'access', _28 => _28.blend, 'optionalAccess', _29 => _29.spine]);
1356
+ if (!spine) return { ...group, children };
1357
+ const moved = { closed: spine.closed, anchors: spine.anchors.map((a) => mapAnchor(a, m)) };
1358
+ return { ...group, children, blend: { ...group.blend, spine: moved } };
1359
+ }
1333
1360
  const path = node;
1334
- const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _28 => _28.strokeWidthProfile, 'optionalAccess', _29 => _29.length]);
1361
+ const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _30 => _30.strokeWidthProfile, 'optionalAccess', _31 => _31.length]);
1335
1362
  return {
1336
1363
  ...path,
1337
1364
  ...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
1338
1365
  subpaths: path.subpaths.map((sp) => ({
1339
1366
  closed: sp.closed,
1340
- anchors: sp.anchors.map((a) => {
1341
- const p = applyMatrix(m, a);
1342
- const next = { x: p.x, y: p.y };
1343
- if (a.in) next.in = applyMatrix(m, a.in);
1344
- if (a.out) next.out = applyMatrix(m, a.out);
1345
- if (a.type) next.type = a.type;
1346
- if (a.corner) next.corner = { ...a.corner };
1347
- return next;
1348
- })
1367
+ anchors: sp.anchors.map((a) => mapAnchor(a, m))
1349
1368
  }))
1350
1369
  };
1351
1370
  }
@@ -1550,7 +1569,7 @@ function applyVectorCornersAttribute(subpaths, value) {
1550
1569
  for (const item of value.trim().split(/\s+/)) {
1551
1570
  const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
1552
1571
  if (!m) continue;
1553
- const a = _optionalChain([subpaths, 'access', _30 => _30[+m[1]], 'optionalAccess', _31 => _31.anchors, 'access', _32 => _32[+m[2]]]);
1572
+ const a = _optionalChain([subpaths, 'access', _32 => _32[+m[1]], 'optionalAccess', _33 => _33.anchors, 'access', _34 => _34[+m[2]]]);
1554
1573
  const radius = parseFloat(m[3]);
1555
1574
  if (!a || !(radius > 0)) continue;
1556
1575
  const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
@@ -1613,6 +1632,33 @@ function gradientMarkup(def) {
1613
1632
  const stops = def.stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeAttr(s.stopColor)}"${s.stopOpacity !== void 0 ? ` stop-opacity="${s.stopOpacity}"` : ""}/>`).join("");
1614
1633
  return `<${tag} id="${escapeAttr(def.id)}"${attrs}>${stops}</${tag}>`;
1615
1634
  }
1635
+ function blendFromAttribute(value, m) {
1636
+ if (!value) return null;
1637
+ let raw;
1638
+ try {
1639
+ raw = JSON.parse(value);
1640
+ } catch (e3) {
1641
+ return null;
1642
+ }
1643
+ if (!raw || typeof raw !== "object") return null;
1644
+ const r = raw;
1645
+ const out = {};
1646
+ if (r.spacing === "steps" || r.spacing === "distance" || r.spacing === "smoothColor") out.spacing = r.spacing;
1647
+ if (typeof r.steps === "number" && Number.isFinite(r.steps)) out.steps = Math.min(1e3, Math.max(1, Math.round(r.steps)));
1648
+ if (typeof r.distance === "number" && r.distance > 0) out.distance = r.distance;
1649
+ if (typeof r.orientToSpine === "boolean") out.orientToSpine = r.orientToSpine;
1650
+ if (typeof r.linear === "boolean") out.linear = r.linear;
1651
+ if (r.starts && typeof r.starts === "object") {
1652
+ const starts = Object.fromEntries(Object.entries(r.starts).filter((e) => Number.isInteger(e[1]) && e[1] >= 0));
1653
+ if (Object.keys(starts).length) out.starts = starts;
1654
+ }
1655
+ const spine = r.spine;
1656
+ if (spine && Array.isArray(spine.anchors) && spine.anchors.every((a) => a && Number.isFinite(a.x) && Number.isFinite(a.y))) {
1657
+ const sp = { closed: !!spine.closed, anchors: spine.anchors.map((a) => ({ x: a.x, y: a.y, ...a.in ? { in: { x: a.in.x, y: a.in.y } } : {}, ...a.out ? { out: { x: a.out.x, y: a.out.y } } : {} })) };
1658
+ out.spine = m === IDENTITY_MATRIX ? sp : transformVectorNode({ id: "", type: "path", subpaths: [sp] }, m).subpaths[0];
1659
+ }
1660
+ return out;
1661
+ }
1616
1662
  function vectorDocumentToSvg(doc, options = {}) {
1617
1663
  const precision = _nullishCoalesce(options.precision, () => ( 3));
1618
1664
  const prefix = _nullishCoalesce(options.idPrefix, () => ( "vg-"));
@@ -1626,6 +1672,8 @@ function vectorDocumentToSvg(doc, options = {}) {
1626
1672
  if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
1627
1673
  if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
1628
1674
  if (node.visible === false) out2 += ` display="none"`;
1675
+ if (node.blendStep) out2 += ` data-vector-blend-step=""`;
1676
+ if (node.type === "group" && node.blend) out2 += ` data-vector-blend="${escapeAttr(JSON.stringify(node.blend))}"`;
1629
1677
  return out2;
1630
1678
  };
1631
1679
  const emit = (node, depth) => {
@@ -1648,7 +1696,7 @@ function vectorDocumentToSvg(doc, options = {}) {
1648
1696
  let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
1649
1697
  if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
1650
1698
  if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
1651
- if (_optionalChain([node, 'access', _33 => _33.strokeDasharray, 'optionalAccess', _34 => _34.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1699
+ if (_optionalChain([node, 'access', _35 => _35.strokeDasharray, 'optionalAccess', _36 => _36.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1652
1700
  return out2;
1653
1701
  };
1654
1702
  const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
@@ -1668,7 +1716,7 @@ function vectorDocumentToSvg(doc, options = {}) {
1668
1716
  data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
1669
1717
  if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
1670
1718
  if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
1671
- if (_optionalChain([node, 'access', _35 => _35.strokeDasharray, 'optionalAccess', _36 => _36.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1719
+ if (_optionalChain([node, 'access', _37 => _37.strokeDasharray, 'optionalAccess', _38 => _38.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
1672
1720
  }
1673
1721
  lines.push(`${indent(depth)}<g${common(node)}${data}>`);
1674
1722
  lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
@@ -1837,7 +1885,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
1837
1885
  seen.add(id);
1838
1886
  let stops = readStops(el);
1839
1887
  const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
1840
- 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]), () => ( []));
1888
+ if (!stops.length && _optionalChain([href, 'optionalAccess', _39 => _39.startsWith, 'call', _40 => _40("#")])) stops = _nullishCoalesce(_optionalChain([resolveGradient, 'call', _41 => _41(href.slice(1), raw, seen), 'optionalAccess', _42 => _42.stops]), () => ( []));
1841
1889
  if (!stops.length) return null;
1842
1890
  if (el.name === "radialGradient") {
1843
1891
  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 };
@@ -1941,7 +1989,7 @@ function parseVectorSvg(svg, options = {}) {
1941
1989
  const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
1942
1990
  const fillEl = part("fill");
1943
1991
  const subpaths = fillEl ? shapeOf(fillEl) : null;
1944
- if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _41 => _41.length])) return null;
1992
+ if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _43 => _43.length])) return null;
1945
1993
  const a = g.attrs;
1946
1994
  const fs = readStyle(fillEl, style);
1947
1995
  const strokeEl = part("stroke");
@@ -1978,7 +2026,7 @@ function parseVectorSvg(svg, options = {}) {
1978
2026
  ...profile.length ? { strokeWidthProfile: profile } : {},
1979
2027
  ...keptCap ? { strokeLinecap: keptCap } : {},
1980
2028
  ...keptJoin ? { strokeLinejoin: keptJoin } : {},
1981
- ..._optionalChain([keptDash, 'optionalAccess', _42 => _42.length]) ? { strokeDasharray: keptDash } : {}
2029
+ ..._optionalChain([keptDash, 'optionalAccess', _44 => _44.length]) ? { strokeDasharray: keptDash } : {}
1982
2030
  };
1983
2031
  return placed(node, m);
1984
2032
  };
@@ -1995,6 +2043,7 @@ function parseVectorSvg(svg, options = {}) {
1995
2043
  ...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
1996
2044
  ...hidden ? { visible: false } : {}
1997
2045
  };
2046
+ if (child.attrs["data-vector-blend-step"] !== void 0) continue;
1998
2047
  if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
1999
2048
  const node2 = mergedPath(child, s, m, meta);
2000
2049
  if (node2) out.push(node2);
@@ -2004,6 +2053,8 @@ function parseVectorSvg(svg, options = {}) {
2004
2053
  const children = visit(child, s, m);
2005
2054
  if (!children.length) continue;
2006
2055
  const group = { id: idFor(child), type: "group", ...meta, children };
2056
+ const blend = child.name === "g" ? blendFromAttribute(child.attrs["data-vector-blend"], m) : null;
2057
+ if (blend) group.blend = blend;
2007
2058
  out.push(group);
2008
2059
  continue;
2009
2060
  }
@@ -2029,10 +2080,914 @@ function parseVectorSvg(svg, options = {}) {
2029
2080
  return { width, height, nodes };
2030
2081
  }
2031
2082
 
2083
+ // src/vector/blend.ts
2084
+ var lerpN = (a, b, t) => a + (b - a) * t;
2085
+ var lerpP = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
2086
+ var toP = (v2) => [v2.x, v2.y];
2087
+ var mapP = (m, p) => [m[0] * p[0] + m[2] * p[1] + m[4], m[1] * p[0] + m[3] * p[1] + m[5]];
2088
+ var curvesOf = (c) => Math.max(0, (c.pts.length - 1) / 3) | 0;
2089
+ function subpathToMorphContour(sp, m = IDENTITY_MATRIX) {
2090
+ const n = sp.anchors.length;
2091
+ if (!n) return null;
2092
+ const first = mapP(m, toP(sp.anchors[0]));
2093
+ const pts = [first];
2094
+ const count = n < 2 ? 0 : sp.closed ? n : n - 1;
2095
+ for (let i = 0; i < count; i++) {
2096
+ const a = sp.anchors[i];
2097
+ const b = sp.anchors[(i + 1) % n];
2098
+ const pa = toP(a);
2099
+ const pb = toP(b);
2100
+ const straight = (!a.out || a.out.x === a.x && a.out.y === a.y) && (!b.in || b.in.x === b.x && b.in.y === b.y);
2101
+ const c1 = straight ? lerpP(pa, pb, 1 / 3) : toP(_nullishCoalesce(a.out, () => ( a)));
2102
+ const c2 = straight ? lerpP(pa, pb, 2 / 3) : toP(_nullishCoalesce(b.in, () => ( b)));
2103
+ pts.push(mapP(m, c1), mapP(m, c2), i === count - 1 && sp.closed ? first : mapP(m, pb));
2104
+ }
2105
+ return { closed: sp.closed && count > 0, pts };
2106
+ }
2107
+ function isStraight(p0, p1, p2, p3) {
2108
+ const dx = p3[0] - p0[0];
2109
+ const dy = p3[1] - p0[1];
2110
+ const len2 = Math.hypot(dx, dy);
2111
+ const tol = Math.max(1e-7, len2 * 1e-7);
2112
+ if (len2 < 1e-12) return Math.hypot(p1[0] - p0[0], p1[1] - p0[1]) < tol && Math.hypot(p2[0] - p0[0], p2[1] - p0[1]) < tol;
2113
+ const off = (p) => Math.abs((p[0] - p0[0]) * dy - (p[1] - p0[1]) * dx) / len2;
2114
+ const along2 = (p) => ((p[0] - p0[0]) * dx + (p[1] - p0[1]) * dy) / (len2 * len2);
2115
+ const u1 = along2(p1);
2116
+ const u2 = along2(p2);
2117
+ return off(p1) < tol && off(p2) < tol && u1 >= -1e-9 && u1 <= 1 + 1e-9 && u2 >= -1e-9 && u2 <= 1 + 1e-9;
2118
+ }
2119
+ function morphContourToSubpath(c, m = IDENTITY_MATRIX, closed = c.closed) {
2120
+ const pts = isIdentityMatrix(m) ? c.pts : c.pts.map((p) => mapP(m, p));
2121
+ const curves = curvesOf(c);
2122
+ const dropLast = closed && c.closed && curves > 0;
2123
+ const count = dropLast ? curves : curves + 1;
2124
+ const anchors = [];
2125
+ for (let k = 0; k < count; k++) anchors.push({ x: pts[3 * k][0], y: pts[3 * k][1] });
2126
+ for (let k = 0; k < curves; k++) {
2127
+ const p0 = pts[3 * k];
2128
+ const p1 = pts[3 * k + 1];
2129
+ const p2 = pts[3 * k + 2];
2130
+ const p3 = pts[3 * k + 3];
2131
+ if (isStraight(p0, p1, p2, p3)) continue;
2132
+ anchors[k].out = { x: p1[0], y: p1[1] };
2133
+ anchors[(k + 1) % count].in = { x: p2[0], y: p2[1] };
2134
+ }
2135
+ for (const a of anchors) {
2136
+ const type = inferAnchorType(a);
2137
+ if (type !== "corner") a.type = type;
2138
+ }
2139
+ const isClosed = closed && curves > 0;
2140
+ return { anchors: dropCollinear(anchors, isClosed), closed: isClosed };
2141
+ }
2142
+ function dropCollinear(anchors, closed) {
2143
+ const out = anchors.slice();
2144
+ const min = closed ? 3 : 2;
2145
+ let i = closed ? 0 : 1;
2146
+ while (out.length > min && i < out.length - (closed ? 0 : 1)) {
2147
+ const n = out.length;
2148
+ const a = out[(i - 1 + n) % n];
2149
+ const b = out[i];
2150
+ const c = out[(i + 1) % n];
2151
+ const plain3 = !b.in && !b.out && !a.out && !c.in;
2152
+ const ux = b.x - a.x;
2153
+ const uy = b.y - a.y;
2154
+ const vx = c.x - b.x;
2155
+ const vy = c.y - b.y;
2156
+ const len2 = Math.hypot(ux, uy) + Math.hypot(vx, vy);
2157
+ if (plain3 && Math.abs(ux * vy - uy * vx) <= 1e-9 * Math.max(1, len2 * len2) && ux * vx + uy * vy >= 0) out.splice(i, 1);
2158
+ else i++;
2159
+ }
2160
+ return out;
2161
+ }
2162
+ var segOf = (c, k) => c.pts.slice(3 * k, 3 * k + 4);
2163
+ function refineMorphContour(c, segments) {
2164
+ const have = curvesOf(c);
2165
+ if (have === 0) return { contour: { closed: c.closed, pts: Array.from({ length: 1 + 3 * segments }, () => [c.pts[0][0], c.pts[0][1]]) }, anchors: [0] };
2166
+ const identity = Array.from({ length: c.closed ? have : have + 1 }, (_, i) => i);
2167
+ if (have >= segments) return { contour: c, anchors: identity };
2168
+ const segs = Array.from({ length: have }, (_, k) => segOf(c, k));
2169
+ const lens = segs.map((s) => _chunkVNYRI265cjs.segmentLength.call(void 0, s));
2170
+ const sum = lens.reduce((a, b) => a + b, 0);
2171
+ const extra = segments - have;
2172
+ const want = lens.map((l) => sum > 1e-12 ? l / sum * extra : extra / have);
2173
+ const parts = want.map((w) => 1 + Math.floor(w));
2174
+ let left = segments - parts.reduce((a, b) => a + b, 0);
2175
+ const frac = want.map((w) => w - Math.floor(w));
2176
+ const order = parts.map((_, i) => i).sort((a, b) => Math.abs(frac[b] - frac[a]) > 1e-9 ? frac[b] - frac[a] : a - b);
2177
+ for (let i = 0; left > 0; i = (i + 1) % order.length, left--) parts[order[i]]++;
2178
+ const pts = [c.pts[0]];
2179
+ const anchors = [0];
2180
+ let at2 = 0;
2181
+ segs.forEach((seg, k) => {
2182
+ const pieces = parts[k];
2183
+ let rest = seg.slice();
2184
+ let t0 = 0;
2185
+ for (let j = 1; j < pieces; j++) {
2186
+ const t = _chunkVNYRI265cjs.segmentParamAtLength.call(void 0, seg, lens[k] * j / pieces, lens[k]);
2187
+ const u = Math.min(1, Math.max(0, (t - t0) / (1 - t0 || 1)));
2188
+ const [l, r] = _chunkVNYRI265cjs.splitSegment.call(void 0, rest, u);
2189
+ pts.push(l[1], l[2], l[3]);
2190
+ rest = r;
2191
+ t0 = t;
2192
+ }
2193
+ pts.push(rest[1], rest[2], k === have - 1 && c.closed ? c.pts[0] : rest[3]);
2194
+ at2 += pieces;
2195
+ if (k < have - 1 || !c.closed) anchors.push(at2);
2196
+ });
2197
+ return { contour: { closed: c.closed, pts }, anchors };
2198
+ }
2199
+ function reverseMorphContour(c) {
2200
+ return { closed: c.closed, pts: c.pts.slice().reverse() };
2201
+ }
2202
+ function turnMorphContour(c, s) {
2203
+ const n = curvesOf(c);
2204
+ if (!c.closed || n === 0) return c;
2205
+ const k = (s % n + n) % n;
2206
+ if (k === 0) return c;
2207
+ const body = c.pts.slice(1);
2208
+ return { closed: true, pts: [c.pts[3 * k], ...body.slice(3 * k), ...body.slice(0, 3 * k)] };
2209
+ }
2210
+ function anchorPoints(c) {
2211
+ const n = curvesOf(c);
2212
+ const count = c.closed ? n : n + 1;
2213
+ return Array.from({ length: Math.max(1, count) }, (_, k) => c.pts[3 * k]);
2214
+ }
2215
+ function polygonArea(pts) {
2216
+ let s = 0;
2217
+ for (let i = 0; i < pts.length; i++) {
2218
+ const a = pts[i];
2219
+ const b = pts[(i + 1) % pts.length];
2220
+ s += a[0] * b[1] - b[0] * a[1];
2221
+ }
2222
+ return s / 2;
2223
+ }
2224
+ function frame(v2) {
2225
+ const n = Math.max(1, v2.length);
2226
+ let cx = 0;
2227
+ let cy = 0;
2228
+ for (const p of v2) {
2229
+ cx += p[0];
2230
+ cy += p[1];
2231
+ }
2232
+ cx /= n;
2233
+ cy /= n;
2234
+ let s = 0;
2235
+ for (const p of v2) s += (p[0] - cx) ** 2 + (p[1] - cy) ** 2;
2236
+ return { c: [cx, cy], r: Math.max(1e-9, Math.sqrt(s / n)) };
2237
+ }
2238
+ var RBINS = 5;
2239
+ var ABINS = 12;
2240
+ var TURN_WEIGHT = 0.1;
2241
+ function shapeContexts(v2, closed) {
2242
+ const { r } = frame(v2);
2243
+ const n = v2.length;
2244
+ return v2.map((p, i) => {
2245
+ const prev = v2[closed ? (i - 1 + n) % n : Math.max(0, i - 1)];
2246
+ const next = v2[closed ? (i + 1) % n : Math.min(n - 1, i + 1)];
2247
+ const dir = Math.atan2(next[1] - prev[1], next[0] - prev[0]);
2248
+ const h = new Float64Array(RBINS * ABINS);
2249
+ let total = 0;
2250
+ for (let j = 0; j < n; j++) {
2251
+ if (i === j) continue;
2252
+ const dx = v2[j][0] - p[0];
2253
+ const dy = v2[j][1] - p[1];
2254
+ const d = Math.hypot(dx, dy) / r;
2255
+ const lr = (Math.log2(Math.max(d, 1e-6)) + 3) / 4 * RBINS;
2256
+ if (!(lr >= 0 && lr < RBINS)) continue;
2257
+ let ang = Math.atan2(dy, dx) - dir;
2258
+ ang = (ang % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
2259
+ h[Math.floor(lr) * ABINS + Math.min(ABINS - 1, Math.floor(ang / (2 * Math.PI) * ABINS))] += 1;
2260
+ total++;
2261
+ }
2262
+ if (total > 0) for (let k = 0; k < h.length; k++) h[k] /= total;
2263
+ return h;
2264
+ });
2265
+ }
2266
+ function chi2(a, b) {
2267
+ let s = 0;
2268
+ for (let k = 0; k < a.length; k++) {
2269
+ const t = a[k] + b[k];
2270
+ if (t > 1e-12) s += (a[k] - b[k]) * (a[k] - b[k]) / t;
2271
+ }
2272
+ return s / 2;
2273
+ }
2274
+ function bestStart(fixed, free, useShapeContext) {
2275
+ const fa = anchorPoints(fixed);
2276
+ const ga = anchorPoints(free);
2277
+ const n = ga.length;
2278
+ const ff = frame(fa);
2279
+ const gf = frame(ga);
2280
+ const fn = fa.map((p) => [(p[0] - ff.c[0]) / ff.r, (p[1] - ff.c[1]) / ff.r]);
2281
+ const gn = ga.map((p) => [(p[0] - gf.c[0]) / gf.r, (p[1] - gf.c[1]) / gf.r]);
2282
+ const fsc = useShapeContext ? shapeContexts(fa, fixed.closed) : null;
2283
+ const gsc = useShapeContext ? shapeContexts(ga, free.closed) : null;
2284
+ const m = Math.max(1, fn.length);
2285
+ const cost = (s) => {
2286
+ let dot2 = 0;
2287
+ let cross2 = 0;
2288
+ let sc = 0;
2289
+ for (let i = 0; i < fn.length; i++) {
2290
+ const j = free.closed ? (i + s) % n : i;
2291
+ const p = fn[i];
2292
+ const q = gn[j];
2293
+ dot2 += p[0] * q[0] + p[1] * q[1];
2294
+ cross2 += p[0] * q[1] - p[1] * q[0];
2295
+ if (fsc && gsc) sc += chi2(fsc[i], gsc[j]);
2296
+ }
2297
+ const residual = Math.max(0, 1 - (dot2 * dot2 + cross2 * cross2) / (m * m));
2298
+ const turn = 1 - Math.cos(Math.atan2(cross2, dot2));
2299
+ return residual + TURN_WEIGHT * turn + sc / m;
2300
+ };
2301
+ if (!free.closed) return { start: 0, cost: cost(0) };
2302
+ const stride = n > 96 ? Math.ceil(n / 48) : 1;
2303
+ let best = { start: 0, cost: Infinity };
2304
+ for (let s = 0; s < n; s += stride) {
2305
+ const c = cost(s);
2306
+ if (c < best.cost - 1e-12) best = { start: s, cost: c };
2307
+ }
2308
+ if (stride > 1) {
2309
+ const s0 = best.start;
2310
+ for (let d = 1; d < stride; d++)
2311
+ for (const s of [(s0 + d) % n, (s0 - d + n) % n]) {
2312
+ const c = cost(s);
2313
+ if (c < best.cost - 1e-12) best = { start: s, cost: c };
2314
+ }
2315
+ }
2316
+ return best;
2317
+ }
2318
+ function placeIn(c, from, to) {
2319
+ const b = unionBounds([boundsOfPoints(c.pts)]);
2320
+ const p = b ? [b.x + b.width / 2, b.y + b.height / 2] : c.pts[0];
2321
+ if (!from || !to) return p;
2322
+ const fx = from.width > 0 ? (p[0] - from.x) / from.width : 0.5;
2323
+ const fy = from.height > 0 ? (p[1] - from.y) / from.height : 0.5;
2324
+ return [to.x + to.width * fx, to.y + to.height * fy];
2325
+ }
2326
+ function boundsOfPoints(pts) {
2327
+ if (!pts.length) return null;
2328
+ let x0 = Infinity;
2329
+ let y0 = Infinity;
2330
+ let x1 = -Infinity;
2331
+ let y1 = -Infinity;
2332
+ for (const p of pts) {
2333
+ x0 = Math.min(x0, p[0]);
2334
+ y0 = Math.min(y0, p[1]);
2335
+ x1 = Math.max(x1, p[0]);
2336
+ y1 = Math.max(y1, p[1]);
2337
+ }
2338
+ return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
2339
+ }
2340
+ function correspondMorphContours(a, b, options = {}) {
2341
+ const minSeg = Math.max(1, Math.round(_nullishCoalesce(options.minSegments, () => ( 16))));
2342
+ const maxSeg = Math.max(minSeg, Math.round(_nullishCoalesce(options.maxSegments, () => ( 400))));
2343
+ const spacing = options.spacing && options.spacing > 0 ? options.spacing : null;
2344
+ const useSC = options.shapeContext !== false;
2345
+ const match = options.matchStart !== false;
2346
+ const ba = boundsOfPoints(a.flatMap((c) => c.pts));
2347
+ const bb = boundsOfPoints(b.flatMap((c) => c.pts));
2348
+ const contourLength = (c) => {
2349
+ let l = 0;
2350
+ for (let k = 0; k < curvesOf(c); k++) l += _chunkVNYRI265cjs.segmentLength.call(void 0, segOf(c, k));
2351
+ return l;
2352
+ };
2353
+ const pairs = [];
2354
+ const [sa, sb] = _nullishCoalesce(options.starts, () => ( []));
2355
+ for (let k = 0; k < Math.max(a.length, b.length); k++) {
2356
+ const ca = a[k];
2357
+ const cb = b[k];
2358
+ const explicitA = k === 0 && sa !== null && sa !== void 0 ? sa : null;
2359
+ const explicitB = k === 0 && sb !== null && sb !== void 0 ? sb : null;
2360
+ const counts = [ca ? curvesOf(ca) : 0, cb ? curvesOf(cb) : 0];
2361
+ const longest = Math.max(ca ? contourLength(ca) : 0, cb ? contourLength(cb) : 0);
2362
+ let n = Math.max(counts[0], counts[1], minSeg, spacing ? Math.ceil(longest / spacing) : 0);
2363
+ n = Math.max(counts[0], counts[1], Math.min(n, maxSeg));
2364
+ const point = (c, from, to) => {
2365
+ const p = placeIn(c, from, to);
2366
+ return { closed: c.closed, pts: Array.from({ length: 1 + 3 * n }, () => [p[0], p[1]]) };
2367
+ };
2368
+ if (ca && cb) {
2369
+ const ra = refineMorphContour(ca, n);
2370
+ const rb = refineMorphContour(cb, n);
2371
+ pairs.push({
2372
+ x: ra.contour,
2373
+ y: rb.contour,
2374
+ point: false,
2375
+ startX: explicitA === null ? null : _nullishCoalesce(ra.anchors[explicitA], () => ( null)),
2376
+ startY: explicitB === null ? null : explicitB === rb.anchors.length - 1 && !cb.closed ? -1 : _nullishCoalesce(rb.anchors[explicitB], () => ( null))
2377
+ });
2378
+ if (explicitA !== null && !ca.closed && explicitA === ra.anchors.length - 1 && explicitA > 0) pairs[pairs.length - 1].startX = -1;
2379
+ } else if (ca) pairs.push({ x: refineMorphContour(ca, n).contour, y: point(ca, ba, bb), point: true, startX: null, startY: null });
2380
+ else if (cb) pairs.push({ x: point(cb, bb, ba), y: refineMorphContour(cb, n).contour, point: true, startX: null, startY: null });
2381
+ }
2382
+ const startAt = (c, s) => {
2383
+ if (s === null) return c;
2384
+ if (s === -1) return reverseMorphContour(c);
2385
+ return c.closed ? turnMorphContour(c, s) : c;
2386
+ };
2387
+ const explicit = pairs.length > 0 && (pairs[0].startX !== null || pairs[0].startY !== null);
2388
+ const ref = pairs.find((p) => !p.point && p.x.closed && p.y.closed);
2389
+ const fixedBack = ref ? Math.sign(polygonArea(anchorPoints(ref.x))) !== Math.sign(polygonArea(anchorPoints(ref.y))) && Math.abs(polygonArea(anchorPoints(ref.y))) > 1e-12 : explicit ? false : null;
2390
+ let best = null;
2391
+ for (const back of fixedBack === null ? [false, true] : [fixedBack]) {
2392
+ const xs = [];
2393
+ const ys = [];
2394
+ let cost = 0;
2395
+ pairs.forEach((p, k) => {
2396
+ let x = k === 0 ? startAt(p.x, p.startX) : p.x;
2397
+ let y = k === 0 ? startAt(p.y, p.startY) : p.y;
2398
+ if (back) y = reverseMorphContour(y);
2399
+ if (p.point) {
2400
+ xs.push(x);
2401
+ ys.push(y);
2402
+ return;
2403
+ }
2404
+ const pinned = k === 0 && explicit;
2405
+ const turnX = !y.closed && x.closed;
2406
+ if (match && !pinned && (y.closed || turnX)) {
2407
+ const r = turnX ? bestStart(y, x, useSC) : bestStart(x, y, useSC);
2408
+ if (turnX) x = turnMorphContour(x, r.start);
2409
+ else y = turnMorphContour(y, r.start);
2410
+ cost += r.cost;
2411
+ } else cost += bestStart(x, { ...y, closed: false }, useSC).cost;
2412
+ xs.push(x);
2413
+ ys.push(y);
2414
+ });
2415
+ if (!best || cost < best.cost - 1e-9) best = { xs, ys, cost, back };
2416
+ }
2417
+ return best ? { a: best.xs, b: best.ys, reversed: best.back } : { a: [], b: [], reversed: false };
2418
+ }
2419
+ function morphPathsCross(a, b) {
2420
+ let count = 0;
2421
+ const d = (p, q, r) => (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]);
2422
+ for (let k = 0; k < Math.min(a.length, b.length); k++) {
2423
+ const pa = anchorPoints(a[k]);
2424
+ const pb = anchorPoints(b[k]);
2425
+ const n = Math.min(pa.length, pb.length);
2426
+ for (let i = 0; i < n; i++)
2427
+ for (let j = i + 1; j < n; j++) {
2428
+ const d1 = d(pa[j], pb[j], pa[i]);
2429
+ const d2 = d(pa[j], pb[j], pb[i]);
2430
+ const d3 = d(pa[i], pb[i], pa[j]);
2431
+ const d4 = d(pa[i], pb[i], pb[j]);
2432
+ if (d1 * d2 < 0 && d3 * d4 < 0) count++;
2433
+ }
2434
+ }
2435
+ return count;
2436
+ }
2437
+ function procrustes(a, b) {
2438
+ const pa = a.flatMap(anchorPoints);
2439
+ const pb = b.flatMap(anchorPoints);
2440
+ const ca = frame(pa).c;
2441
+ const cb = frame(pb).c;
2442
+ let ss = 0;
2443
+ let dot2 = 0;
2444
+ let cross2 = 0;
2445
+ for (let i = 0; i < Math.min(pa.length, pb.length); i++) {
2446
+ const p = [pa[i][0] - ca[0], pa[i][1] - ca[1]];
2447
+ const q = [pb[i][0] - cb[0], pb[i][1] - cb[1]];
2448
+ ss += p[0] * p[0] + p[1] * p[1];
2449
+ dot2 += p[0] * q[0] + p[1] * q[1];
2450
+ cross2 += p[0] * q[1] - p[1] * q[0];
2451
+ }
2452
+ return { ca, cb, theta: Math.atan2(cross2, dot2), s: ss > 1e-12 ? Math.hypot(dot2, cross2) / ss : 1 };
2453
+ }
2454
+ function interpolateMorphContours(a, b, u, options = {}) {
2455
+ const w = options.linear ? 0 : Math.min(1, Math.max(0, _nullishCoalesce(options.rigidity, () => ( 1))));
2456
+ const sim = w > 0 ? _nullishCoalesce(options.similarity, () => ( procrustes(a, b))) : null;
2457
+ const rot = (p, ang, k) => {
2458
+ const c = Math.cos(ang);
2459
+ const s = Math.sin(ang);
2460
+ return [k * (c * p[0] - s * p[1]), k * (s * p[0] + c * p[1])];
2461
+ };
2462
+ const thetaU = sim ? sim.theta * u : 0;
2463
+ const sU = sim ? Math.max(1e-9, sim.s) ** u : 1;
2464
+ const cU = sim ? lerpP(sim.ca, sim.cb, u) : [0, 0];
2465
+ return a.map((ca, k) => {
2466
+ const cb = b[k];
2467
+ const pts = ca.pts.map((p, i) => {
2468
+ const q = _nullishCoalesce(cb.pts[i], () => ( p));
2469
+ const linear = lerpP(p, q, u);
2470
+ if (!sim) return linear;
2471
+ const d = [p[0] - sim.ca[0], p[1] - sim.ca[1]];
2472
+ const full = rot(d, sim.theta, sim.s);
2473
+ const resid = [q[0] - (full[0] + sim.cb[0]), q[1] - (full[1] + sim.cb[1])];
2474
+ const part = rot(d, thetaU, sU);
2475
+ const rigid = [part[0] + cU[0] + resid[0] * u, part[1] + cU[1] + resid[1] * u];
2476
+ return lerpP(linear, rigid, w);
2477
+ });
2478
+ return { closed: ca.closed && cb.closed, pts };
2479
+ });
2480
+ }
2481
+ function interpolateRigidMatrix(a, b, t) {
2482
+ const decompose = (m) => {
2483
+ const sx2 = Math.hypot(m[0], m[1]);
2484
+ const ang2 = sx2 > 1e-12 ? Math.atan2(m[1], m[0]) : 0;
2485
+ const c2 = Math.cos(ang2);
2486
+ const s2 = Math.sin(ang2);
2487
+ return { e: m[4], f: m[5], ang: ang2, sx: sx2, sh: c2 * m[2] + s2 * m[3], sy: -s2 * m[2] + c2 * m[3] };
2488
+ };
2489
+ const p = decompose(a);
2490
+ const q = decompose(b);
2491
+ let turn = q.ang - p.ang;
2492
+ turn = ((turn + Math.PI) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI) - Math.PI;
2493
+ const ang = p.ang + turn * t;
2494
+ const sx = lerpN(p.sx, q.sx, t);
2495
+ const sh = lerpN(p.sh, q.sh, t);
2496
+ const sy = lerpN(p.sy, q.sy, t);
2497
+ const c = Math.cos(ang);
2498
+ const s = Math.sin(ang);
2499
+ return [c * sx, s * sx, c * sh - s * sy, s * sh + c * sy, lerpN(p.e, q.e, t), lerpN(p.f, q.f, t)];
2500
+ }
2501
+ function interpolateVectorColor(a, b, t) {
2502
+ if (t <= 0) return a;
2503
+ if (t >= 1) return b;
2504
+ return _nullishCoalesce(_chunkMR3Z4OQIcjs.mixColorsOklab.call(void 0, a, b, t), () => ( (t < 0.5 ? a : b)));
2505
+ }
2506
+ function sortedStops(g) {
2507
+ return g.stops.slice().sort((x, y) => x.offset - y.offset);
2508
+ }
2509
+ function sampleGradient(stops, offset) {
2510
+ if (!stops.length) return { color: "transparent", opacity: 1 };
2511
+ const first = stops[0];
2512
+ const last = stops[stops.length - 1];
2513
+ if (offset <= first.offset) return { color: first.color, opacity: _nullishCoalesce(first.opacity, () => ( 1)) };
2514
+ if (offset >= last.offset) return { color: last.color, opacity: _nullishCoalesce(last.opacity, () => ( 1)) };
2515
+ let i = 0;
2516
+ while (i + 1 < stops.length && stops[i + 1].offset < offset) i++;
2517
+ const x = stops[i];
2518
+ const y = stops[i + 1];
2519
+ const u = y.offset - x.offset > 1e-12 ? (offset - x.offset) / (y.offset - x.offset) : 1;
2520
+ return { color: interpolateVectorColor(x.color, y.color, u), opacity: lerpN(_nullishCoalesce(x.opacity, () => ( 1)), _nullishCoalesce(y.opacity, () => ( 1)), u) };
2521
+ }
2522
+ function resampleGradientStops(g, offsets) {
2523
+ const stops = sortedStops(g);
2524
+ return offsets.map((offset) => {
2525
+ const s = sampleGradient(stops, offset);
2526
+ return s.opacity < 1 ? { offset, color: s.color, opacity: s.opacity } : { offset, color: s.color };
2527
+ });
2528
+ }
2529
+ function lerpAngle(a, b, t) {
2530
+ const d = ((b - a + 180) % 360 + 360) % 360 - 180;
2531
+ return a + d * t;
2532
+ }
2533
+ function lerpGradient(a, b, t) {
2534
+ if (a.type !== b.type) return null;
2535
+ let stopsA = sortedStops(a);
2536
+ let stopsB = sortedStops(b);
2537
+ if (stopsA.length !== stopsB.length) {
2538
+ const offs = [...stopsA, ...stopsB].map((s) => s.offset).filter(Number.isFinite).sort((x, y) => x - y);
2539
+ const common = offs.filter((o, i) => i === 0 || o - offs[i - 1] > 1e-4);
2540
+ stopsA = resampleGradientStops(a, common);
2541
+ stopsB = resampleGradientStops(b, common);
2542
+ }
2543
+ const stops = stopsA.map((x, i) => {
2544
+ const y = stopsB[i];
2545
+ const opacity = lerpN(_nullishCoalesce(x.opacity, () => ( 1)), _nullishCoalesce(y.opacity, () => ( 1)), t);
2546
+ const stop = { offset: lerpN(x.offset, y.offset, t), color: interpolateVectorColor(x.color, y.color, t) };
2547
+ if (opacity < 1) stop.opacity = opacity;
2548
+ return stop;
2549
+ });
2550
+ if (a.type === "linear" && b.type === "linear") return { type: "linear", angle: lerpAngle(_nullishCoalesce(a.angle, () => ( 90)), _nullishCoalesce(b.angle, () => ( 90)), t), stops };
2551
+ const ra = a;
2552
+ const rb = b;
2553
+ return { type: "radial", cx: lerpN(_nullishCoalesce(ra.cx, () => ( 0.5)), _nullishCoalesce(rb.cx, () => ( 0.5)), t), cy: lerpN(_nullishCoalesce(ra.cy, () => ( 0.5)), _nullishCoalesce(rb.cy, () => ( 0.5)), t), r: lerpN(_nullishCoalesce(ra.r, () => ( 0.5)), _nullishCoalesce(rb.r, () => ( 0.5)), t), stops };
2554
+ }
2555
+ function solidLike(color, like) {
2556
+ return { ...like, stops: like.stops.map((s) => color === null ? { offset: s.offset, color: s.color, opacity: 0 } : { offset: s.offset, color }) };
2557
+ }
2558
+ function interpolateVectorPaint(a, b, t) {
2559
+ if (t <= 0) return a;
2560
+ if (t >= 1) return b;
2561
+ const pa = hasPaint(a);
2562
+ const pb = hasPaint(b);
2563
+ if (!pa && !pb) return t < 0.5 ? a : b;
2564
+ if (typeof a === "string" && pa && typeof b === "string" && pb) return interpolateVectorColor(a, b, t);
2565
+ if (!pa && typeof b === "string") return interpolateVectorColor("transparent", b, t);
2566
+ if (!pb && typeof a === "string") return interpolateVectorColor(a, "transparent", t);
2567
+ const ga = pa && typeof a === "object" && a ? a : null;
2568
+ const gb = pb && typeof b === "object" && b ? b : null;
2569
+ if (ga && gb) return _nullishCoalesce(lerpGradient(ga, gb, t), () => ( (t < 0.5 ? a : b)));
2570
+ if (gb) return _nullishCoalesce(lerpGradient(solidLike(pa ? a : null, gb), gb, t), () => ( b));
2571
+ if (ga) return _nullishCoalesce(lerpGradient(ga, solidLike(pb ? b : null, ga), t), () => ( a));
2572
+ return t < 0.5 ? a : b;
2573
+ }
2574
+ function interpolateWidthProfiles(a, b, t) {
2575
+ if (!_optionalChain([a, 'optionalAccess', _45 => _45.length]) && !_optionalChain([b, 'optionalAccess', _46 => _46.length])) return null;
2576
+ const at2 = [..._nullishCoalesce(a, () => ( [])), ..._nullishCoalesce(b, () => ( [])), { t: 0 }, { t: 1 }].map((p) => p.t).sort((x, y) => x - y);
2577
+ const ts = at2.filter((v2, i) => i === 0 || v2 - at2[i - 1] > 1e-6);
2578
+ return ts.map((pos) => {
2579
+ const x = widthProfileAt(a, pos);
2580
+ const y = widthProfileAt(b, pos);
2581
+ return { t: pos, left: lerpN(x.left, y.left, t), right: lerpN(x.right, y.right, t) };
2582
+ });
2583
+ }
2584
+ function lerpDashes(a, b, t) {
2585
+ const dashed = (d) => !!_optionalChain([d, 'optionalAccess', _47 => _47.length]) && d.some((v2) => v2 > 0);
2586
+ if (!dashed(a) && !dashed(b)) return t < 0.5 ? a : b;
2587
+ const even = (p) => p.length % 2 ? [...p, ...p] : p;
2588
+ const solid = (p) => {
2589
+ const e = even(p);
2590
+ const out = [];
2591
+ for (let i = 0; i < e.length; i += 2) out.push(e[i] + e[i + 1], 0);
2592
+ return out;
2593
+ };
2594
+ const x = dashed(a) ? even(a) : solid(b);
2595
+ const y = dashed(b) ? even(b) : solid(a);
2596
+ if (x.length !== y.length) return t < 0.5 ? a : b;
2597
+ return x.map((v2, i) => lerpN(v2, y[i], t));
2598
+ }
2599
+ var SWITCHED_KEYS = ["strokeLinecap", "strokeLinejoin", "fillRule", "strokeAlign", "startArrow", "endArrow", "arrowAlign"];
2600
+ function interpolateVectorStyle(a, b, t) {
2601
+ const near2 = t < 0.5 ? a : b;
2602
+ const out = {};
2603
+ const fill = interpolateVectorPaint(a.fill, b.fill, t);
2604
+ if (fill !== void 0) out.fill = fill;
2605
+ const stroke = interpolateVectorPaint(a.stroke, b.stroke, t);
2606
+ if (stroke !== void 0) out.stroke = stroke;
2607
+ if (a.strokeWidth !== void 0 || b.strokeWidth !== void 0) out.strokeWidth = lerpN(_nullishCoalesce(a.strokeWidth, () => ( 1)), _nullishCoalesce(b.strokeWidth, () => ( 1)), t);
2608
+ for (const key of SWITCHED_KEYS) if (near2[key] !== void 0 && near2[key] !== null) out[key] = near2[key];
2609
+ for (const key of ["startArrowScale", "endArrowScale"])
2610
+ if (a[key] !== void 0 || b[key] !== void 0) out[key] = lerpN(_nullishCoalesce(a[key], () => ( 1)), _nullishCoalesce(b[key], () => ( 1)), t);
2611
+ const dash = lerpDashes(a.strokeDasharray, b.strokeDasharray, t);
2612
+ if (_optionalChain([dash, 'optionalAccess', _48 => _48.length])) out.strokeDasharray = dash;
2613
+ const profile = interpolateWidthProfiles(a.strokeWidthProfile, b.strokeWidthProfile, t);
2614
+ if (profile) out.strokeWidthProfile = profile;
2615
+ return out;
2616
+ }
2617
+ function reversedStyle(s) {
2618
+ const out = { ...s };
2619
+ out.startArrow = s.endArrow;
2620
+ out.endArrow = s.startArrow;
2621
+ out.startArrowScale = s.endArrowScale;
2622
+ out.endArrowScale = s.startArrowScale;
2623
+ if (_optionalChain([s, 'access', _49 => _49.strokeWidthProfile, 'optionalAccess', _50 => _50.length])) out.strokeWidthProfile = reverseWidthProfile(s.strokeWidthProfile);
2624
+ for (const k of ["startArrow", "endArrow", "startArrowScale", "endArrowScale"]) if (out[k] === void 0) delete out[k];
2625
+ return out;
2626
+ }
2627
+ var STYLE_KEYS2 = [
2628
+ "fill",
2629
+ "stroke",
2630
+ "strokeWidth",
2631
+ "strokeLinecap",
2632
+ "strokeLinejoin",
2633
+ "strokeDasharray",
2634
+ "fillRule",
2635
+ "strokeAlign",
2636
+ "startArrow",
2637
+ "endArrow",
2638
+ "arrowAlign",
2639
+ "startArrowScale",
2640
+ "endArrowScale",
2641
+ "strokeWidthProfile"
2642
+ ];
2643
+ function styleOf(n) {
2644
+ const out = {};
2645
+ for (const k of STYLE_KEYS2) if (n[k] !== void 0) out[k] = n[k];
2646
+ return out;
2647
+ }
2648
+ function contoursOf(node, m) {
2649
+ return expandVectorCorners(node.subpaths).map((sp) => subpathToMorphContour(sp, m)).filter((c) => !!c);
2650
+ }
2651
+ function preparePathMorph(a, b, options, ma = nodeMatrix(a), mb = nodeMatrix(b)) {
2652
+ const corr = correspondMorphContours(contoursOf(a, ma), contoursOf(b, mb), options);
2653
+ const sim = procrustes(corr.a, corr.b);
2654
+ const styleA = styleOf(a);
2655
+ const styleB = corr.reversed ? reversedStyle(styleOf(b)) : styleOf(b);
2656
+ const rigid = !isIdentityMatrix(ma) || !isIdentityMatrix(mb);
2657
+ const id = _nullishCoalesce(options.id, () => ( a.id));
2658
+ return (t) => {
2659
+ const contours = interpolateMorphContours(corr.a, corr.b, t, { linear: options.linear, rigidity: options.rigidity, similarity: sim });
2660
+ const m = rigid ? interpolateRigidMatrix(ma, mb, t) : IDENTITY_MATRIX;
2661
+ const back = rigid ? invertMatrix(m) : IDENTITY_MATRIX;
2662
+ const subpaths = contours.map((c, k) => {
2663
+ const closed = t <= 0 ? corr.a[k].closed : t >= 1 ? corr.b[k].closed : corr.a[k].closed && corr.b[k].closed;
2664
+ return morphContourToSubpath({ closed: corr.a[k].closed && corr.b[k].closed, pts: c.pts }, back, closed);
2665
+ });
2666
+ const node = { id, type: "path", subpaths, ...interpolateVectorStyle(styleA, styleB, t) };
2667
+ const opacity = lerpN(_nullishCoalesce(a.opacity, () => ( 1)), _nullishCoalesce(b.opacity, () => ( 1)), t);
2668
+ if (opacity < 1) node.opacity = opacity;
2669
+ if (rigid && !isIdentityMatrix(m)) node.transform = m;
2670
+ return node;
2671
+ };
2672
+ }
2673
+ function flattenPaths(node, parent = IDENTITY_MATRIX) {
2674
+ const m = multiplyMatrices(nodeMatrix(node), parent);
2675
+ if (node.type === "path") return [{ path: node, m, opacity: 1 }];
2676
+ const o = _nullishCoalesce(node.opacity, () => ( 1));
2677
+ return node.children.filter((c) => c.visible !== false).flatMap((c) => flattenPaths(c, m)).map((e) => ({ ...e, opacity: e.opacity * o }));
2678
+ }
2679
+ var placedBounds = (path, m) => boundsOfPoints(contoursOf(path, m).flatMap((c) => c.pts));
2680
+ function collapsedPath(path, m, from, to) {
2681
+ const own = placedBounds(path, m);
2682
+ const c = own ? [own.x + own.width / 2, own.y + own.height / 2] : [0, 0];
2683
+ const fx = from && from.width > 0 ? (c[0] - from.x) / from.width : 0.5;
2684
+ const fy = from && from.height > 0 ? (c[1] - from.y) / from.height : 0.5;
2685
+ const p = to ? { x: to.x + to.width * fx, y: to.y + to.height * fy } : { x: c[0], y: c[1] };
2686
+ const subpaths = path.subpaths.map((sp) => ({ closed: sp.closed, anchors: sp.anchors.map(() => ({ x: p.x, y: p.y })) }));
2687
+ const { transform: _t, ...rest } = path;
2688
+ return { ...rest, subpaths };
2689
+ }
2690
+ function createVectorMorph(a, b, options = {}) {
2691
+ if (a.type === "path" && b.type === "path") {
2692
+ const at2 = preparePathMorph(a, b, options);
2693
+ return { at: at2 };
2694
+ }
2695
+ const pa = flattenPaths(a);
2696
+ const pb = flattenPaths(b);
2697
+ const ba = unionBounds(pa.map((e) => placedBounds(e.path, e.m)));
2698
+ const bb = unionBounds(pb.map((e) => placedBounds(e.path, e.m)));
2699
+ const id = _nullishCoalesce(options.id, () => ( a.id));
2700
+ const parts = [];
2701
+ const opacities = [];
2702
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
2703
+ const x = pa[i];
2704
+ const y = pb[i];
2705
+ const sub3 = { ...options, id: `${id}-${i}`, starts: void 0 };
2706
+ if (x && y) {
2707
+ parts.push(preparePathMorph(x.path, y.path, sub3, x.m, y.m));
2708
+ opacities.push([x.opacity, y.opacity]);
2709
+ } else if (x) {
2710
+ parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub3, x.m, IDENTITY_MATRIX));
2711
+ opacities.push([x.opacity, x.opacity]);
2712
+ } else if (y) {
2713
+ parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub3, IDENTITY_MATRIX, y.m));
2714
+ opacities.push([y.opacity, y.opacity]);
2715
+ }
2716
+ }
2717
+ return {
2718
+ at: (t) => {
2719
+ const children = parts.map((p, i) => {
2720
+ const n = p(t);
2721
+ const o = lerpN(opacities[i][0], opacities[i][1], t) * (_nullishCoalesce(n.opacity, () => ( 1)));
2722
+ if (o < 1) n.opacity = o;
2723
+ else delete n.opacity;
2724
+ return n;
2725
+ });
2726
+ const group = { id, type: "group", children };
2727
+ return group;
2728
+ }
2729
+ };
2730
+ }
2731
+ function interpolateVectorPaths(a, b, t, options = {}) {
2732
+ return createVectorMorph(a, b, options).at(t);
2733
+ }
2734
+ var keyframeMorphs = /* @__PURE__ */ new WeakMap();
2735
+ function evaluateVectorPathKeyframes(keyframes, time, options = {}) {
2736
+ const keys = keyframes.filter((k) => Number.isFinite(k.time)).sort((x, y) => x.time - y.time);
2737
+ if (!keys.length) return null;
2738
+ if (!(time > keys[0].time)) return keys[0].value;
2739
+ const last = keys[keys.length - 1];
2740
+ if (time >= last.time) return last.value;
2741
+ let i = 0;
2742
+ while (i + 1 < keys.length && keys[i + 1].time <= time) i++;
2743
+ const a = keys[i];
2744
+ const b = keys[i + 1];
2745
+ if (time === a.time) return a.value;
2746
+ if (a.easing === "hold") return a.value;
2747
+ const span = b.time - a.time;
2748
+ const u = Math.min(1, Math.max(0, _chunkXZ4L3R4Kcjs.evaluateEasing.call(void 0, a.easing, (time - a.time) / span, span)));
2749
+ let inner = keyframeMorphs.get(a.value);
2750
+ if (!inner) keyframeMorphs.set(a.value, inner = /* @__PURE__ */ new WeakMap());
2751
+ let morph = inner.get(b.value);
2752
+ if (!morph) inner.set(b.value, morph = createVectorMorph(a.value, b.value, options));
2753
+ return morph.at(u);
2754
+ }
2755
+
2756
+ // src/vector/blendGroup.ts
2757
+ var DEFAULT_VECTOR_BLEND_STEPS = 5;
2758
+ var DEFAULT_VECTOR_BLEND_DISTANCE = 20;
2759
+ var VECTOR_BLEND_MAX_STEPS = 1e3;
2760
+ var VECTOR_BLEND_MAX_SMOOTH_STEPS = 256;
2761
+ function isVectorBlendGroup(node) {
2762
+ return !!node && node.type === "group" && !!node.blend;
2763
+ }
2764
+ function vectorBlendKeys(group) {
2765
+ return group.children.filter((c) => !c.blendStep);
2766
+ }
2767
+ function resolveVectorBlendOptions(options) {
2768
+ return {
2769
+ spacing: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _51 => _51.spacing]), () => ( "steps")),
2770
+ steps: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _52 => _52.steps]), () => ( DEFAULT_VECTOR_BLEND_STEPS)),
2771
+ distance: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _53 => _53.distance]), () => ( DEFAULT_VECTOR_BLEND_DISTANCE)),
2772
+ orientToSpine: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _54 => _54.orientToSpine]), () => ( false)),
2773
+ linear: _nullishCoalesce(_optionalChain([options, 'optionalAccess', _55 => _55.linear]), () => ( false))
2774
+ };
2775
+ }
2776
+ function paintColors(p) {
2777
+ if (typeof p === "string") return [p];
2778
+ if (p && typeof p === "object") return p.stops.map((s) => s.color);
2779
+ return [];
2780
+ }
2781
+ function vectorBlendColorDistance(a, b) {
2782
+ const pa = collectPathNodes(a)[0];
2783
+ const pb = collectPathNodes(b)[0];
2784
+ if (!pa || !pb) return 0;
2785
+ let d = 0;
2786
+ for (const key of ["fill", "stroke"]) {
2787
+ const ca = paintColors(pa[key]);
2788
+ const cb = paintColors(pb[key]);
2789
+ ca.forEach((c, i) => {
2790
+ const other = _nullishCoalesce(cb[i], () => ( cb[0]));
2791
+ if (other === void 0) return;
2792
+ const x = _chunkA6PHCAGXcjs.hexToRgba.call(void 0, c);
2793
+ const y = _chunkA6PHCAGXcjs.hexToRgba.call(void 0, other);
2794
+ if (!x || !y) return;
2795
+ d = Math.max(d, Math.abs(x.r - y.r), Math.abs(x.g - y.g), Math.abs(x.b - y.b), Math.abs(x.a - y.a));
2796
+ });
2797
+ }
2798
+ return d;
2799
+ }
2800
+ var centreOf = (n) => {
2801
+ const b = vectorNodeBounds(n);
2802
+ return b ? [b.x + b.width / 2, b.y + b.height / 2] : [0, 0];
2803
+ };
2804
+ function vectorBlendStepCount(a, b, options, length2) {
2805
+ const o = resolveVectorBlendOptions(options);
2806
+ if (o.spacing === "distance") {
2807
+ const d = Math.max(0.01, o.distance);
2808
+ return Math.min(VECTOR_BLEND_MAX_STEPS, Math.max(0, Math.round(length2 / d) - 1));
2809
+ }
2810
+ if (o.spacing === "smoothColor") {
2811
+ const cd = vectorBlendColorDistance(a, b);
2812
+ if (cd > 1 / 255) return Math.min(VECTOR_BLEND_MAX_SMOOTH_STEPS, Math.max(1, Math.ceil(cd * 255 / 2)));
2813
+ const ba = vectorNodeBounds(a);
2814
+ const bb = vectorNodeBounds(b);
2815
+ const dist4 = ba && bb ? Math.max(Math.abs(ba.x - bb.x), Math.abs(ba.y - bb.y), Math.abs(ba.x + ba.width - bb.x - bb.width), Math.abs(ba.y + ba.height - bb.y - bb.height)) : length2;
2816
+ return Math.min(VECTOR_BLEND_MAX_SMOOTH_STEPS, Math.max(1, Math.ceil(dist4 / 2)));
2817
+ }
2818
+ return Math.min(VECTOR_BLEND_MAX_STEPS, Math.max(1, Math.round(Number.isFinite(o.steps) ? o.steps : DEFAULT_VECTOR_BLEND_STEPS)));
2819
+ }
2820
+ function vectorBlendRail(sp) {
2821
+ const pts = [];
2822
+ const angles = [];
2823
+ const push = (p, tangent2) => {
2824
+ const last = pts[pts.length - 1];
2825
+ if (last && Math.hypot(p[0] - last[0], p[1] - last[1]) < 1e-9) return;
2826
+ pts.push(p);
2827
+ angles.push(Math.atan2(tangent2[1], tangent2[0]));
2828
+ };
2829
+ const count = subpathSegmentCount(sp);
2830
+ for (let i = 0; i < count; i++) {
2831
+ const seg = subpathSegment(sp, i);
2832
+ const samples = seg.length === 2 ? 1 : 32;
2833
+ if (i === 0) push(seg[0], _chunkVNYRI265cjs.segmentTangent.call(void 0, seg, 0));
2834
+ for (let k = 1; k <= samples; k++) push(_chunkVNYRI265cjs.segmentPoint.call(void 0, seg, k / samples), _chunkVNYRI265cjs.segmentTangent.call(void 0, seg, k / samples));
2835
+ }
2836
+ if (pts.length < 2) return null;
2837
+ const cum = [0];
2838
+ for (let i = 1; i < pts.length; i++) cum.push(cum[i - 1] + Math.hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1]));
2839
+ return { pts, cum, angles, total: cum[cum.length - 1], closed: sp.closed };
2840
+ }
2841
+ function vectorBlendRailAt(rail, s) {
2842
+ let len2 = Number.isFinite(s) ? s : 0;
2843
+ if (rail.closed && rail.total > 0) len2 = (len2 % rail.total + rail.total) % rail.total;
2844
+ len2 = Math.min(rail.total, Math.max(0, len2));
2845
+ let lo = 0;
2846
+ let hi = rail.cum.length - 1;
2847
+ while (hi - lo > 1) {
2848
+ const mid = lo + hi >> 1;
2849
+ if (rail.cum[mid] <= len2) lo = mid;
2850
+ else hi = mid;
2851
+ }
2852
+ const span = rail.cum[hi] - rail.cum[lo];
2853
+ const u = span > 1e-12 ? (len2 - rail.cum[lo]) / span : 0;
2854
+ const a = rail.pts[lo];
2855
+ const b = rail.pts[hi];
2856
+ return { point: [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u], angle: Math.atan2(b[1] - a[1], b[0] - a[0]) };
2857
+ }
2858
+ function vectorBlendSpine(group) {
2859
+ const keys = vectorBlendKeys(group);
2860
+ if (keys.length < 2) return null;
2861
+ const stored = _optionalChain([group, 'access', _56 => _56.blend, 'optionalAccess', _57 => _57.spine]);
2862
+ if (stored && stored.anchors.length >= 2) {
2863
+ const rail2 = vectorBlendRail(stored);
2864
+ if (rail2) {
2865
+ const end = rail2.closed ? rail2.total * (1 - 1 / keys.length) : rail2.total;
2866
+ return { spine: stored, rail: rail2, stops: keys.map((_, i) => end * i / (keys.length - 1)), replaced: true };
2867
+ }
2868
+ }
2869
+ const centres = keys.map(centreOf);
2870
+ const spine = { closed: false, anchors: centres.map((c) => ({ x: c[0], y: c[1] })) };
2871
+ const stops = [0];
2872
+ for (let i = 1; i < centres.length; i++) stops.push(stops[i - 1] + Math.hypot(centres[i][0] - centres[i - 1][0], centres[i][1] - centres[i - 1][1]));
2873
+ const rail = _nullishCoalesce(vectorBlendRail(spine), () => ( { pts: centres, cum: stops, angles: centres.map(() => 0), total: stops[stops.length - 1], closed: false }));
2874
+ return { spine, rail, stops, replaced: false };
2875
+ }
2876
+ function vectorBlendKeyTargets(group) {
2877
+ const s = vectorBlendSpine(group);
2878
+ return s ? s.stops.map((stop) => vectorBlendRailAt(s.rail, stop).point) : [];
2879
+ }
2880
+ var morphCache = /* @__PURE__ */ new WeakMap();
2881
+ function morphFor(a, b, blend) {
2882
+ const starts = [_nullishCoalesce(_optionalChain([blend, 'access', _58 => _58.starts, 'optionalAccess', _59 => _59[a.id]]), () => ( null)), _nullishCoalesce(_optionalChain([blend, 'access', _60 => _60.starts, 'optionalAccess', _61 => _61[b.id]]), () => ( null))];
2883
+ const key = JSON.stringify([!!blend.linear, starts]);
2884
+ let inner = morphCache.get(a);
2885
+ if (!inner) morphCache.set(a, inner = /* @__PURE__ */ new WeakMap());
2886
+ let byKey = inner.get(b);
2887
+ if (!byKey) inner.set(b, byKey = /* @__PURE__ */ new Map());
2888
+ let morph = byKey.get(key);
2889
+ if (!morph) byKey.set(key, morph = createVectorMorph(a, b, { linear: blend.linear, starts }));
2890
+ return morph;
2891
+ }
2892
+ var wrapAngle = (a) => Math.atan2(Math.sin(a), Math.cos(a));
2893
+ function asStep(node, id) {
2894
+ const rename = (n, nid) => n.type === "group" ? { ...n, id: nid, children: n.children.map((c, i) => rename(c, `${nid}.${i}`)) } : { ...n, id: nid };
2895
+ return { ...rename(node, id), blendStep: true, locked: true };
2896
+ }
2897
+ function generateVectorBlendChildren(group) {
2898
+ const keys = vectorBlendKeys(group);
2899
+ const blend = group.blend;
2900
+ if (!blend || keys.length < 2) return keys;
2901
+ const spine = vectorBlendSpine(group);
2902
+ if (!spine) return keys;
2903
+ const out = [];
2904
+ let counter = 0;
2905
+ keys.forEach((a, i) => {
2906
+ out.push(a);
2907
+ const b = keys[i + 1];
2908
+ if (!b) return;
2909
+ const s0 = spine.stops[i];
2910
+ const s1 = spine.stops[i + 1];
2911
+ const n = vectorBlendStepCount(a, b, blend, Math.abs(s1 - s0));
2912
+ const morph = morphFor(a, b, blend);
2913
+ const p0 = vectorBlendRailAt(spine.rail, s0);
2914
+ const p1 = vectorBlendRailAt(spine.rail, s1);
2915
+ for (let j = 1; j <= n; j++) {
2916
+ const t = j / (n + 1);
2917
+ let step = morph.at(t);
2918
+ if (spine.replaced) {
2919
+ const p = vectorBlendRailAt(spine.rail, s0 + (s1 - s0) * t);
2920
+ const dx = p.point[0] - (p0.point[0] + (p1.point[0] - p0.point[0]) * t);
2921
+ const dy = p.point[1] - (p0.point[1] + (p1.point[1] - p0.point[1]) * t);
2922
+ let m = Math.hypot(dx, dy) > 1e-9 ? translateMatrix(dx, dy) : IDENTITY_MATRIX;
2923
+ if (blend.orientToSpine) {
2924
+ const turn = wrapAngle(p.angle - (p0.angle + wrapAngle(p1.angle - p0.angle) * t));
2925
+ if (Math.abs(turn) > 1e-9) {
2926
+ const c = centreOf(step);
2927
+ const origin = { x: c[0] + m[4], y: c[1] + m[5] };
2928
+ m = multiplyMatrices(m, rotateMatrix(turn * 180 / Math.PI, origin));
2929
+ }
2930
+ }
2931
+ if (m !== IDENTITY_MATRIX) step = transformVectorNode(step, m);
2932
+ }
2933
+ out.push(asStep(step, `${group.id}~${++counter}`));
2934
+ }
2935
+ });
2936
+ return out;
2937
+ }
2938
+ var childrenCache = /* @__PURE__ */ new WeakMap();
2939
+ var sameList = (a, b) => a.length === b.length && a.every((n, i) => n === b[i]);
2940
+ function blendChildren(group) {
2941
+ const keys = vectorBlendKeys(group);
2942
+ const sig = `${group.id}|${JSON.stringify(group.blend)}`;
2943
+ const cached = keys[0] ? childrenCache.get(keys[0]) : void 0;
2944
+ let children;
2945
+ if (cached && cached.sig === sig && sameList(cached.keys, keys)) children = cached.children;
2946
+ else {
2947
+ children = generateVectorBlendChildren(group);
2948
+ if (keys[0]) childrenCache.set(keys[0], { sig, keys, children });
2949
+ }
2950
+ return sameList(children, group.children) ? group.children : children;
2951
+ }
2952
+ function refreshVectorBlends(nodes) {
2953
+ let changed = false;
2954
+ const out = nodes.map((n) => {
2955
+ if (n.type !== "group") return n;
2956
+ const inner = refreshVectorBlends(n.children);
2957
+ let next = inner === n.children ? n : { ...n, children: inner };
2958
+ if (next.blend) {
2959
+ const children = blendChildren(next);
2960
+ if (children !== next.children) next = { ...next, children };
2961
+ }
2962
+ if (next !== n) changed = true;
2963
+ return next;
2964
+ });
2965
+ return changed ? out : nodes;
2966
+ }
2967
+ function plain2(node, newId) {
2968
+ const { blendStep: _s, locked: _l, ...rest } = node;
2969
+ const renamed = { ...rest, id: newId() };
2970
+ if (renamed.type === "group") renamed.children = renamed.children.map((c) => plain2(c, newId));
2971
+ return renamed;
2972
+ }
2973
+ function expandVectorBlend(group, newId) {
2974
+ const { blend: _b, ...rest } = group;
2975
+ return { ...rest, children: group.children.map((c) => c.blendStep ? plain2(c, newId) : c) };
2976
+ }
2977
+ function releaseVectorBlend(group, newId) {
2978
+ const m = nodeMatrix(group);
2979
+ const opacity = group.opacity;
2980
+ const keys = vectorBlendKeys(group).map((c) => group.transform ? transformVectorNode(c, m) : c).map((c) => opacity !== void 0 && opacity < 1 ? { ...c, opacity: (_nullishCoalesce(c.opacity, () => ( 1))) * opacity } : c);
2981
+ const spine = _optionalChain([group, 'access', _62 => _62.blend, 'optionalAccess', _63 => _63.spine]);
2982
+ if (!spine || spine.anchors.length < 2) return keys;
2983
+ const path = { id: newId(), type: "path", name: "Spine", fill: null, stroke: null, subpaths: [{ closed: spine.closed, anchors: spine.anchors.map((a) => ({ ...a })) }] };
2984
+ return [...keys, group.transform ? transformVectorNode(path, m) : path];
2985
+ }
2986
+
2032
2987
  // src/vector/snap.ts
2033
2988
  var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
2034
2989
  function constrainToLeave(leave, p) {
2035
- if (!_optionalChain([leave, 'optionalAccess', _43 => _43.constrain])) return p;
2990
+ if (!_optionalChain([leave, 'optionalAccess', _64 => _64.constrain])) return p;
2036
2991
  const dx = p.x - leave.at.x;
2037
2992
  const dy = p.y - leave.at.y;
2038
2993
  switch (leave.constrain) {
@@ -2143,7 +3098,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2143
3098
  moving.set(r.nodeId, set);
2144
3099
  }
2145
3100
  const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
2146
- 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);
3101
+ t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access', _65 => _65.get, 'call', _66 => _66(id), 'optionalAccess', _67 => _67.has, 'call', _68 => _68(`${s}:${i}`)]), true);
2147
3102
  return t.index();
2148
3103
  }
2149
3104
  /**
@@ -2261,7 +3216,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2261
3216
  }
2262
3217
  const b = vectorNodeBounds(n, parent);
2263
3218
  const f = nodeFrame(n, parent, b);
2264
- if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _48 => _48.centre]), () => ( null)));
3219
+ if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _69 => _69.centre]), () => ( null)));
2265
3220
  }
2266
3221
  };
2267
3222
  visit(doc.nodes, IDENTITY_MATRIX);
@@ -2306,7 +3261,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2306
3261
  lines: lines2,
2307
3262
  labels: [this.label(q, kind)]
2308
3263
  });
2309
- if (_optionalChain([leave, 'optionalAccess', _49 => _49.constrain])) {
3264
+ if (_optionalChain([leave, 'optionalAccess', _70 => _70.constrain])) {
2310
3265
  const q = constrainToLeave(leave, p);
2311
3266
  const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
2312
3267
  return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
@@ -2322,7 +3277,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2322
3277
  }
2323
3278
  const on = this.onSegment(near2);
2324
3279
  if (on) return at2(on, "path");
2325
- if (_optionalChain([leave, 'optionalAccess', _50 => _50.angles, 'optionalAccess', _51 => _51.length])) {
3280
+ if (_optionalChain([leave, 'optionalAccess', _71 => _71.angles, 'optionalAccess', _72 => _72.length])) {
2326
3281
  const c = construction(leave, p, tol);
2327
3282
  if (c) {
2328
3283
  const hit = this.slide(leave.at, c.dir, c.point, tol, false);
@@ -2452,7 +3407,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2452
3407
  * with nothing spaces the box evenly in its row or column. Returns the correction and guides.
2453
3408
  */
2454
3409
  snapRect(r, tol, options = {}) {
2455
- 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 };
3410
+ const axes = { x: _optionalChain([options, 'access', _73 => _73.axes, 'optionalAccess', _74 => _74.x]) !== false, y: _optionalChain([options, 'access', _75 => _75.axes, 'optionalAccess', _76 => _76.y]) !== false };
2456
3411
  const nearest = (lines, values) => {
2457
3412
  let best = null;
2458
3413
  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 };
@@ -2460,7 +3415,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2460
3415
  };
2461
3416
  const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
2462
3417
  const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
2463
- 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: [] };
3418
+ const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess', _77 => _77.d]), () => ( 0)), dy: _nullishCoalesce(_optionalChain([by, 'optionalAccess', _78 => _78.d]), () => ( 0)), x: !!bx, y: !!by, lines: [], labels: [] };
2464
3419
  const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
2465
3420
  if (bx) {
2466
3421
  const x = bx.from.x;
@@ -2558,14 +3513,14 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2558
3513
  * corner) slides along it until one of them is on a target. An axis that snaps to nothing
2559
3514
  * may match another object's width or height instead.
2560
3515
  */
2561
- snapScale(frame, handle, sx, sy, mods, tol) {
2562
- const toDoc = frame.angle ? rotateMatrix(frame.angle) : IDENTITY_MATRIX;
2563
- const toLocal = frame.angle ? rotateMatrix(-frame.angle) : IDENTITY_MATRIX;
3516
+ snapScale(frame2, handle, sx, sy, mods, tol) {
3517
+ const toDoc = frame2.angle ? rotateMatrix(frame2.angle) : IDENTITY_MATRIX;
3518
+ const toLocal = frame2.angle ? rotateMatrix(-frame2.angle) : IDENTITY_MATRIX;
2564
3519
  const corner = handle.length === 2;
2565
3520
  const proportional = !!mods.shift;
2566
- const centre = { x: frame.x + frame.width / 2, y: frame.y + frame.height / 2 };
2567
- const origin = mods.alt ? centre : handlePos(opposite(handle), frame);
2568
- const hp = handlePos(handle, frame);
3521
+ const centre = { x: frame2.x + frame2.width / 2, y: frame2.y + frame2.height / 2 };
3522
+ const origin = mods.alt ? centre : handlePos(opposite(handle), frame2);
3523
+ const hp = handlePos(handle, frame2);
2569
3524
  const d0 = { x: hp.x - origin.x, y: hp.y - origin.y };
2570
3525
  const valid = (n, s) => Number.isFinite(n) && n * s > 0;
2571
3526
  const nearest = (v2, lines) => {
@@ -2583,15 +3538,15 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2583
3538
  const on = this.nearestPoint(p, tol);
2584
3539
  const tries = [];
2585
3540
  if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
2586
- 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 } });
2587
- tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _60 => _60.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
2588
- tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _61 => _61.v]), () => ( null)), s: { hx: null, hy, on: null } });
3541
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _79 => _79.v]), () => ( null)), ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _80 => _80.v]), () => ( null)), s: { hx, hy, on: null } });
3542
+ tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess', _81 => _81.v]), () => ( null)), ty: null, s: { hx, hy: null, on: null } });
3543
+ tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess', _82 => _82.v]), () => ( null)), s: { hx: null, hy, on: null } });
2589
3544
  for (const { tx, ty, s } of tries) {
2590
3545
  if (tx === null && ty === null) continue;
2591
3546
  const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
2592
3547
  let cx = (q.x - origin.x) / d0.x;
2593
3548
  let cy = (q.y - origin.y) / d0.y;
2594
- if (!frame.angle) {
3549
+ if (!frame2.angle) {
2595
3550
  if (tx === null) cx = sx;
2596
3551
  if (ty === null) cy = sy;
2597
3552
  }
@@ -2620,7 +3575,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2620
3575
  if (!best || (inLine === best.inLine ? moved < best.moved : !inLine)) best = { s, inLine, moved, snap: found2 };
2621
3576
  };
2622
3577
  for (const h of [handle, ...ends]) {
2623
- const hv = handlePos(h, frame);
3578
+ const hv = handlePos(h, frame2);
2624
3579
  const v2 = { x: hv.x - origin.x, y: hv.y - origin.y };
2625
3580
  const base = applyMatrix(toDoc, { x: origin.x + c.x * v2.x, y: origin.y + c.y * v2.y });
2626
3581
  const dir = applyMatrix(toDoc, { x: e.x * v2.x, y: e.y * v2.y });
@@ -2655,25 +3610,25 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
2655
3610
  const movesY = handle !== "e" && handle !== "w";
2656
3611
  const sized = [];
2657
3612
  if (!proportional) {
2658
- if (movesX && !snap.hx && !(frame.angle && snap.hy) && frame.width > 1e-9) {
2659
- const len2 = this.matchSize(Math.abs(out.sx) * frame.width, tol);
3613
+ if (movesX && !snap.hx && !(frame2.angle && snap.hy) && frame2.width > 1e-9) {
3614
+ const len2 = this.matchSize(Math.abs(out.sx) * frame2.width, tol);
2660
3615
  if (len2 !== null && len2 > 0) {
2661
- out.sx = Math.sign(out.sx || 1) * (len2 / frame.width);
3616
+ out.sx = Math.sign(out.sx || 1) * (len2 / frame2.width);
2662
3617
  sized.push("x");
2663
3618
  }
2664
3619
  }
2665
- if (movesY && !snap.hy && !(frame.angle && snap.hx) && frame.height > 1e-9) {
2666
- const len2 = this.matchSize(Math.abs(out.sy) * frame.height, tol);
3620
+ if (movesY && !snap.hy && !(frame2.angle && snap.hx) && frame2.height > 1e-9) {
3621
+ const len2 = this.matchSize(Math.abs(out.sy) * frame2.height, tol);
2667
3622
  if (len2 !== null && len2 > 0) {
2668
- out.sy = Math.sign(out.sy || 1) * (len2 / frame.height);
3623
+ out.sy = Math.sign(out.sy || 1) * (len2 / frame2.height);
2669
3624
  sized.push("y");
2670
3625
  }
2671
3626
  }
2672
3627
  }
2673
- const x0 = origin.x + (frame.x - origin.x) * out.sx;
2674
- const x1 = origin.x + (frame.x + frame.width - origin.x) * out.sx;
2675
- const y0 = origin.y + (frame.y - origin.y) * out.sy;
2676
- const y1 = origin.y + (frame.y + frame.height - origin.y) * out.sy;
3628
+ const x0 = origin.x + (frame2.x - origin.x) * out.sx;
3629
+ const x1 = origin.x + (frame2.x + frame2.width - origin.x) * out.sx;
3630
+ const y0 = origin.y + (frame2.y - origin.y) * out.sy;
3631
+ const y1 = origin.y + (frame2.y + frame2.height - origin.y) * out.sy;
2677
3632
  const box = { x0: Math.min(x0, x1), x1: Math.max(x0, x1), y0: Math.min(y0, y1), y1: Math.max(y0, y1) };
2678
3633
  const page = [
2679
3634
  { x: box.x0, y: box.y0 },
@@ -3962,6 +4917,15 @@ var DEFAULT_VECTOR_PATH_EDIT = {
3962
4917
  fillGap: 0
3963
4918
  };
3964
4919
  var VECTOR_PENCIL_STRING_PX = 100;
4920
+ function blendOptionsOf(input) {
4921
+ const out = {};
4922
+ if (input.spacing === "steps" || input.spacing === "distance" || input.spacing === "smoothColor") out.spacing = input.spacing;
4923
+ if (typeof input.steps === "number" && Number.isFinite(input.steps)) out.steps = Math.min(1e3, Math.max(1, Math.round(input.steps)));
4924
+ if (typeof input.distance === "number" && Number.isFinite(input.distance) && input.distance > 0) out.distance = input.distance;
4925
+ if (typeof input.orientToSpine === "boolean") out.orientToSpine = input.orientToSpine;
4926
+ if (typeof input.linear === "boolean") out.linear = input.linear;
4927
+ return out;
4928
+ }
3965
4929
  var VECTOR_STYLE_KEYS = [
3966
4930
  "fill",
3967
4931
  "stroke",
@@ -4005,7 +4969,7 @@ function getEditablePathIds(state) {
4005
4969
  }
4006
4970
  }
4007
4971
  }
4008
- return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _62 => _62(state.document.nodes, id), 'optionalAccess', _63 => _63.type]) === "path");
4972
+ return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _83 => _83(state.document.nodes, id), 'optionalAccess', _84 => _84.type]) === "path");
4009
4973
  }
4010
4974
  function getVisibleAnchors(state) {
4011
4975
  const out = [];
@@ -4020,7 +4984,7 @@ function getVisibleHandles(state) {
4020
4984
  const seen = /* @__PURE__ */ new Set();
4021
4985
  const push = (node, s, i, handle) => {
4022
4986
  const sp = node.subpaths[s];
4023
- const a = _optionalChain([sp, 'optionalAccess', _64 => _64.anchors, 'access', _65 => _65[i]]);
4987
+ const a = _optionalChain([sp, 'optionalAccess', _85 => _85.anchors, 'access', _86 => _86[i]]);
4024
4988
  if (!a || !a[handle]) return;
4025
4989
  const key = `${node.id}:${s}:${i}:${handle}`;
4026
4990
  if (seen.has(key)) return;
@@ -4029,8 +4993,8 @@ function getVisibleHandles(state) {
4029
4993
  };
4030
4994
  if (state.tool === "pen" && state.pen) {
4031
4995
  const node = findVectorNode(state.document.nodes, state.pen.nodeId);
4032
- const sp = _optionalChain([node, 'optionalAccess', _66 => _66.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
4033
- if (_optionalChain([node, 'optionalAccess', _67 => _67.type]) === "path" && sp) {
4996
+ const sp = _optionalChain([node, 'optionalAccess', _87 => _87.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
4997
+ if (_optionalChain([node, 'optionalAccess', _88 => _88.type]) === "path" && sp) {
4034
4998
  const last = sp.anchors.length - 1;
4035
4999
  push(node, state.pen.subpath, last, "in");
4036
5000
  push(node, state.pen.subpath, last, "out");
@@ -4068,11 +5032,11 @@ function getVectorCornerWidgets(state) {
4068
5032
  const side = Math.min(c.lu, c.lv);
4069
5033
  if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
4070
5034
  const a = node.subpaths[c.subpath].anchors[c.index];
4071
- const radius = _nullishCoalesce(_optionalChain([a, 'access', _68 => _68.corner, 'optionalAccess', _69 => _69.radius]), () => ( 0));
5035
+ const radius = _nullishCoalesce(_optionalChain([a, 'access', _89 => _89.corner, 'optionalAccess', _90 => _90.radius]), () => ( 0));
4072
5036
  const drawn = fitCornerRadius(c, radius);
4073
5037
  const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
4074
5038
  const p = applyMatrix(m, cornerBisectorPoint(c, k));
4075
- 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 });
5039
+ 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', _91 => _91.corner, 'optionalAccess', _92 => _92.kind]), () => ( "round")), corner: c });
4076
5040
  }
4077
5041
  }
4078
5042
  return out;
@@ -4095,13 +5059,13 @@ function getVectorWidthHandles(state) {
4095
5059
  const n = findVectorNode(state.document.nodes, id);
4096
5060
  if (n) for (const p of collectPathNodes(n)) ids.add(p.id);
4097
5061
  }
4098
- if (_optionalChain([state, 'access', _72 => _72.hover, 'optionalAccess', _73 => _73.nodeId])) ids.add(state.hover.nodeId);
4099
- if (_optionalChain([state, 'access', _74 => _74.interaction, 'optionalAccess', _75 => _75.type]) === "width") ids.add(state.interaction.ref.nodeId);
5062
+ if (_optionalChain([state, 'access', _93 => _93.hover, 'optionalAccess', _94 => _94.nodeId])) ids.add(state.hover.nodeId);
5063
+ if (_optionalChain([state, 'access', _95 => _95.interaction, 'optionalAccess', _96 => _96.type]) === "width") ids.add(state.interaction.ref.nodeId);
4100
5064
  for (const r of state.selectedWidthPoints) ids.add(r.nodeId);
4101
5065
  const out = [];
4102
5066
  for (const id of ids) {
4103
5067
  const node = findVectorNode(state.document.nodes, id);
4104
- if (_optionalChain([node, 'optionalAccess', _76 => _76.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _77 => _77.strokeWidthProfile, 'optionalAccess', _78 => _78.length])) continue;
5068
+ if (_optionalChain([node, 'optionalAccess', _97 => _97.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _98 => _98.strokeWidthProfile, 'optionalAccess', _99 => _99.length])) continue;
4105
5069
  const g = vectorStrokeWidthGeometry(node);
4106
5070
  if (!g) continue;
4107
5071
  const profile = sanitizeWidthProfile(node.strokeWidthProfile);
@@ -4134,7 +5098,7 @@ function getVectorWidthHandles(state) {
4134
5098
  }
4135
5099
  function getAnchor(doc, ref) {
4136
5100
  const node = findVectorNode(doc.nodes, ref.nodeId);
4137
- 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;
5101
+ return _optionalChain([node, 'optionalAccess', _100 => _100.type]) === "path" ? _optionalChain([node, 'access', _101 => _101.subpaths, 'access', _102 => _102[ref.subpath], 'optionalAccess', _103 => _103.anchors, 'access', _104 => _104[ref.index]]) : void 0;
4138
5102
  }
4139
5103
  function getAnchorWorld(doc, ref) {
4140
5104
  const a = getAnchor(doc, ref);
@@ -4168,11 +5132,11 @@ function getTransformHandlePositions(box) {
4168
5132
  { handle: "rotate", x: x + w / 2, y: y - VECTOR_ROTATE_HANDLE_OFFSET }
4169
5133
  ];
4170
5134
  }
4171
- function getVectorSelectionBoxGeometry(frame, viewport) {
5135
+ function getVectorSelectionBoxGeometry(frame2, viewport) {
4172
5136
  const z = viewport.zoom;
4173
- const box = { x: frame.x * z + viewport.x, y: frame.y * z + viewport.y, width: frame.width * z, height: frame.height * z };
4174
- const rot = rotateMatrix(frame.angle, { x: viewport.x, y: viewport.y });
4175
- const toScreen2 = (p) => frame.angle ? applyMatrix(rot, p) : { x: p.x, y: p.y };
5137
+ const box = { x: frame2.x * z + viewport.x, y: frame2.y * z + viewport.y, width: frame2.width * z, height: frame2.height * z };
5138
+ const rot = rotateMatrix(frame2.angle, { x: viewport.x, y: viewport.y });
5139
+ const toScreen2 = (p) => frame2.angle ? applyMatrix(rot, p) : { x: p.x, y: p.y };
4176
5140
  const handles = getTransformHandlePositions(box).map((h) => ({ ...h, screen: toScreen2(h) }));
4177
5141
  const corners2 = [
4178
5142
  { x: box.x, y: box.y },
@@ -4181,7 +5145,7 @@ function getVectorSelectionBoxGeometry(frame, viewport) {
4181
5145
  { x: box.x, y: box.y + box.height }
4182
5146
  ].map(toScreen2);
4183
5147
  const r = (n) => Math.round(n * 1e3) / 1e3;
4184
- return { box, angle: frame.angle, transform: frame.angle ? `rotate(${r(frame.angle)} ${r(viewport.x)} ${r(viewport.y)})` : "", handles, corners: corners2 };
5148
+ return { box, angle: frame2.angle, transform: frame2.angle ? `rotate(${r(frame2.angle)} ${r(viewport.x)} ${r(viewport.y)})` : "", handles, corners: corners2 };
4185
5149
  }
4186
5150
  var RESIZE_CURSOR_ORDER = ["ew-resize", "nwse-resize", "ns-resize", "nesw-resize"];
4187
5151
  var HANDLE_ANGLES = { e: 0, se: 45, s: 90, sw: 135, w: 180, nw: 225, n: 270, ne: 315 };
@@ -4205,7 +5169,8 @@ var TOOL_LABELS = {
4205
5169
  eraser: "Eraser",
4206
5170
  width: "Width",
4207
5171
  shapeBuilder: "Shape Builder",
4208
- paintBucket: "Paint Bucket"
5172
+ paintBucket: "Paint Bucket",
5173
+ blend: "Blend"
4209
5174
  };
4210
5175
  var TOOL_KEYS = {
4211
5176
  v: "select",
@@ -4222,7 +5187,8 @@ var TOOL_KEYS = {
4222
5187
  e: "eraser",
4223
5188
  w: "width",
4224
5189
  m: "shapeBuilder",
4225
- b: "paintBucket"
5190
+ b: "paintBucket",
5191
+ j: "blend"
4226
5192
  };
4227
5193
  function vectorToolLabel(tool) {
4228
5194
  return TOOL_LABELS[tool];
@@ -4242,7 +5208,8 @@ var VECTOR_TOOL_SHORTCUTS = {
4242
5208
  eraser: "E",
4243
5209
  width: "W",
4244
5210
  shapeBuilder: "M",
4245
- paintBucket: "B"
5211
+ paintBucket: "B",
5212
+ blend: "J"
4246
5213
  };
4247
5214
  var VECTOR_TOOL_GLYPHS = {
4248
5215
  select: "\u2196",
@@ -4259,7 +5226,8 @@ var VECTOR_TOOL_GLYPHS = {
4259
5226
  eraser: "\u232B",
4260
5227
  width: "\u21F9",
4261
5228
  shapeBuilder: "\u2295",
4262
- paintBucket: "\u25CD"
5229
+ paintBucket: "\u25CD",
5230
+ blend: "\u29D3"
4263
5231
  };
4264
5232
  var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
4265
5233
  var BOOLEAN_OPS = {
@@ -4347,20 +5315,24 @@ var VectorEditorController = (_class3 = class {
4347
5315
  /** Paint bucket map of the visible outlines. */
4348
5316
  __init25() {this.fillCache = null}
4349
5317
  __init26() {this.builderPreview = null}
5318
+ __init27() {this.blendStart = null}
5319
+ /** Options new blends get (the toolbars edit them along with the selected blend's). */
5320
+ __init28() {this.blendDefaults = {}}
4350
5321
  /** What the highlight shows, to skip notifying when the pointer stays over the same face. */
4351
- __init27() {this.builderHoverKey = null}
5322
+ __init29() {this.builderHoverKey = null}
4352
5323
 
4353
5324
 
4354
- __init28() {this.listeners = /* @__PURE__ */ new Set()}
4355
- __init29() {this.cleanups = []}
4356
- __init30() {this.applying = false}
5325
+ __init30() {this.listeners = /* @__PURE__ */ new Set()}
5326
+ __init31() {this.cleanups = []}
5327
+ __init32() {this.applying = false}
4357
5328
  /** Set while a commit records a change that was already applied, so `restore` still reports it. */
4358
- __init31() {this.committing = false}
4359
- __init32() {this.revision = 0}
5329
+ __init33() {this.committing = false}
5330
+ __init34() {this.revision = 0}
4360
5331
 
4361
- constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);_class3.prototype.__init29.call(this);_class3.prototype.__init30.call(this);_class3.prototype.__init31.call(this);_class3.prototype.__init32.call(this);_class3.prototype.__init33.call(this);_class3.prototype.__init34.call(this);
5332
+ constructor(options = {}) {;_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);_class3.prototype.__init25.call(this);_class3.prototype.__init26.call(this);_class3.prototype.__init27.call(this);_class3.prototype.__init28.call(this);_class3.prototype.__init29.call(this);_class3.prototype.__init30.call(this);_class3.prototype.__init31.call(this);_class3.prototype.__init32.call(this);_class3.prototype.__init33.call(this);_class3.prototype.__init34.call(this);_class3.prototype.__init35.call(this);_class3.prototype.__init36.call(this);
4362
5333
  this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
4363
5334
  this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
5335
+ this.doc = this.refreshed(this.doc);
4364
5336
  this.tool = _nullishCoalesce(options.tool, () => ( "select"));
4365
5337
  this.defaults = { ...DEFAULT_DEFAULTS, ..._nullishCoalesce(options.defaults, () => ( {})) };
4366
5338
  this.viewport = { x: 0, y: 0, zoom: 1, ..._nullishCoalesce(options.viewport, () => ( {})) };
@@ -4387,9 +5359,9 @@ var VectorEditorController = (_class3 = class {
4387
5359
  // State
4388
5360
  // -------------------------------------------------------------------------
4389
5361
  /** The current snapshot (stable until the next change). */
4390
- __init33() {this.getState = () => this.state}
5362
+ __init35() {this.getState = () => this.state}
4391
5363
  /** Calls `listener` after every change. Returns an unsubscribe function. */
4392
- __init34() {this.subscribe = (listener) => {
5364
+ __init36() {this.subscribe = (listener) => {
4393
5365
  this.listeners.add(listener);
4394
5366
  return () => {
4395
5367
  this.listeners.delete(listener);
@@ -4425,6 +5397,8 @@ var VectorEditorController = (_class3 = class {
4425
5397
  pen: this.pen,
4426
5398
  pointer: this.pointer,
4427
5399
  builder: this.builderPreview,
5400
+ blendStart: this.blendStart,
5401
+ blendDefaults: this.blendDefaults,
4428
5402
  canUndo: h.canUndo,
4429
5403
  canRedo: h.canRedo,
4430
5404
  undoLabel: h.undoName,
@@ -4432,10 +5406,18 @@ var VectorEditorController = (_class3 = class {
4432
5406
  revision: this.revision
4433
5407
  };
4434
5408
  }
5409
+ /** `doc` with its blends' steps brought up to date (the same object when they already are). */
5410
+ refreshed(doc) {
5411
+ const nodes = refreshVectorBlends(doc.nodes);
5412
+ return nodes === doc.nodes ? doc : { ...doc, nodes };
5413
+ }
4435
5414
  notify(docChanged = false) {
5415
+ const fresh = this.refreshed(this.doc);
5416
+ if (fresh !== this.doc) this.doc = fresh;
5417
+ if (this.blendStart && !this.getNode(this.blendStart.nodeId)) this.blendStart = null;
4436
5418
  this.revision++;
4437
5419
  this.state = this.computeState();
4438
- if (docChanged) _optionalChain([this, 'access', _84 => _84.onChange, 'optionalCall', _85 => _85(this.doc)]);
5420
+ if (docChanged) _optionalChain([this, 'access', _105 => _105.onChange, 'optionalCall', _106 => _106(this.doc)]);
4439
5421
  const state = this.state;
4440
5422
  this.listeners.forEach((listener) => {
4441
5423
  try {
@@ -4465,29 +5447,31 @@ var VectorEditorController = (_class3 = class {
4465
5447
  return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
4466
5448
  }
4467
5449
  cursorFor() {
4468
- if (_optionalChain([this, 'access', _86 => _86.drag, 'optionalAccess', _87 => _87.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
4469
- if (_optionalChain([this, 'access', _88 => _88.drag, 'optionalAccess', _89 => _89.kind]) === "rotate") return "grabbing";
4470
- if (_optionalChain([this, 'access', _90 => _90.drag, 'optionalAccess', _91 => _91.kind]) === "move") return "move";
5450
+ if (_optionalChain([this, 'access', _107 => _107.drag, 'optionalAccess', _108 => _108.kind]) === "pan" || this.spaceHeld) return this.drag ? "grabbing" : "grab";
5451
+ if (_optionalChain([this, 'access', _109 => _109.drag, 'optionalAccess', _110 => _110.kind]) === "rotate") return "grabbing";
5452
+ if (_optionalChain([this, 'access', _111 => _111.drag, 'optionalAccess', _112 => _112.kind]) === "move") return "move";
4471
5453
  const h = this.hover;
4472
5454
  switch (this.tool) {
4473
5455
  case "select":
4474
- 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)));
4475
- return _optionalChain([h, 'optionalAccess', _96 => _96.nodeId]) ? "move" : "default";
5456
+ if (_optionalChain([h, 'optionalAccess', _113 => _113.transformHandle])) return vectorResizeCursor(h.transformHandle, _nullishCoalesce(_optionalChain([this, 'access', _114 => _114.frameOf, 'call', _115 => _115(this.selectedRoots()), 'optionalAccess', _116 => _116.angle]), () => ( 0)));
5457
+ return _optionalChain([h, 'optionalAccess', _117 => _117.nodeId]) ? "move" : "default";
4476
5458
  case "direct":
4477
- if (_optionalChain([h, 'optionalAccess', _97 => _97.anchor]) || _optionalChain([h, 'optionalAccess', _98 => _98.handle]) || _optionalChain([h, 'optionalAccess', _99 => _99.corner])) return "pointer";
4478
- if (_optionalChain([h, 'optionalAccess', _100 => _100.segment])) return "copy";
4479
- return _optionalChain([h, 'optionalAccess', _101 => _101.nodeId]) ? "move" : "default";
5459
+ if (_optionalChain([h, 'optionalAccess', _118 => _118.anchor]) || _optionalChain([h, 'optionalAccess', _119 => _119.handle]) || _optionalChain([h, 'optionalAccess', _120 => _120.corner])) return "pointer";
5460
+ if (_optionalChain([h, 'optionalAccess', _121 => _121.segment])) return "copy";
5461
+ return _optionalChain([h, 'optionalAccess', _122 => _122.nodeId]) ? "move" : "default";
4480
5462
  case "pen":
4481
- return _optionalChain([h, 'optionalAccess', _102 => _102.anchor]) ? "pointer" : "crosshair";
5463
+ return _optionalChain([h, 'optionalAccess', _123 => _123.anchor]) ? "pointer" : "crosshair";
4482
5464
  case "width":
4483
- if (_optionalChain([this, 'access', _103 => _103.drag, 'optionalAccess', _104 => _104.kind]) === "width") return this.drag.mode === "width" ? "ew-resize" : "grabbing";
4484
- if (_optionalChain([h, 'optionalAccess', _105 => _105.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
4485
- return _optionalChain([h, 'optionalAccess', _106 => _106.strokeSpot]) ? "copy" : "default";
5465
+ if (_optionalChain([this, 'access', _124 => _124.drag, 'optionalAccess', _125 => _125.kind]) === "width") return this.drag.mode === "width" ? "ew-resize" : "grabbing";
5466
+ if (_optionalChain([h, 'optionalAccess', _126 => _126.widthPoint])) return h.widthPoint.part === "center" ? "grab" : "ew-resize";
5467
+ return _optionalChain([h, 'optionalAccess', _127 => _127.strokeSpot]) ? "copy" : "default";
4486
5468
  case "shapeBuilder":
4487
- if (!_optionalChain([this, 'access', _107 => _107.builderPreview, 'optionalAccess', _108 => _108.faces, 'access', _109 => _109.length]) && !_optionalChain([this, 'access', _110 => _110.builderPreview, 'optionalAccess', _111 => _111.lines, 'access', _112 => _112.length])) return "crosshair";
5469
+ if (!_optionalChain([this, 'access', _128 => _128.builderPreview, 'optionalAccess', _129 => _129.faces, 'access', _130 => _130.length]) && !_optionalChain([this, 'access', _131 => _131.builderPreview, 'optionalAccess', _132 => _132.lines, 'access', _133 => _133.length])) return "crosshair";
4488
5470
  return this.builderPreview.mode === "erase" ? "not-allowed" : "cell";
4489
5471
  case "paintBucket":
4490
5472
  return this.builderPreview ? "cell" : "crosshair";
5473
+ case "blend":
5474
+ return _optionalChain([h, 'optionalAccess', _134 => _134.nodeId]) ? this.blendStart && this.blendStart.nodeId !== h.nodeId ? "copy" : "pointer" : "crosshair";
4491
5475
  default:
4492
5476
  return "crosshair";
4493
5477
  }
@@ -4508,11 +5492,11 @@ var VectorEditorController = (_class3 = class {
4508
5492
  this.selection = s.selection.filter((id) => this.getNode(id));
4509
5493
  this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
4510
5494
  this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
4511
- this.pen = s.pen && _optionalChain([this, 'access', _113 => _113.getNode, 'call', _114 => _114(s.pen.nodeId), 'optionalAccess', _115 => _115.subpaths, 'optionalAccess', _116 => _116[s.pen.subpath]]) ? s.pen : null;
5495
+ this.pen = s.pen && _optionalChain([this, 'access', _135 => _135.getNode, 'call', _136 => _136(s.pen.nodeId), 'optionalAccess', _137 => _137.subpaths, 'optionalAccess', _138 => _138[s.pen.subpath]]) ? s.pen : null;
4512
5496
  if (this.widthPoints.length) {
4513
5497
  const kept = this.widthPoints.filter((r) => {
4514
5498
  const n = this.getNode(r.nodeId);
4515
- return _optionalChain([n, 'optionalAccess', _117 => _117.type]) === "path" && !!_optionalChain([n, 'access', _118 => _118.strokeWidthProfile, 'optionalAccess', _119 => _119[r.index]]);
5499
+ return _optionalChain([n, 'optionalAccess', _139 => _139.type]) === "path" && !!_optionalChain([n, 'access', _140 => _140.strokeWidthProfile, 'optionalAccess', _141 => _141[r.index]]);
4516
5500
  });
4517
5501
  if (kept.length !== this.widthPoints.length) this.widthPoints = kept;
4518
5502
  }
@@ -4523,6 +5507,7 @@ var VectorEditorController = (_class3 = class {
4523
5507
  }
4524
5508
  /** Records the change from `before` to the current state as one undo step. */
4525
5509
  commit(name, before) {
5510
+ this.doc = this.refreshed(this.doc);
4526
5511
  const after = this.snapshot();
4527
5512
  if (before.doc === after.doc) {
4528
5513
  this.notify();
@@ -4625,6 +5610,7 @@ var VectorEditorController = (_class3 = class {
4625
5610
  this.widthEdit = null;
4626
5611
  this.builderPreview = null;
4627
5612
  this.builderHoverKey = null;
5613
+ this.blendStart = null;
4628
5614
  if (tool !== "direct") {
4629
5615
  this.anchors = [];
4630
5616
  this.editingId = null;
@@ -4683,7 +5669,7 @@ var VectorEditorController = (_class3 = class {
4683
5669
  if (res.kind) {
4684
5670
  this.snappedToObject = true;
4685
5671
  this.setGuides(res);
4686
- return _optionalChain([leave, 'optionalAccess', _120 => _120.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) };
5672
+ return _optionalChain([leave, 'optionalAccess', _142 => _142.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) };
4687
5673
  }
4688
5674
  }
4689
5675
  this.setGuides(null);
@@ -4701,14 +5687,14 @@ var VectorEditorController = (_class3 = class {
4701
5687
  /** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
4702
5688
  hoverSnap(p, input) {
4703
5689
  if (this.tool === "pen") {
4704
- const sp = this.pen ? _optionalChain([this, 'access', _121 => _121.getNode, 'call', _122 => _122(this.pen.nodeId), 'optionalAccess', _123 => _123.subpaths, 'access', _124 => _124[this.pen.subpath]]) : void 0;
4705
- if (this.pen && sp && _optionalChain([this, 'access', _125 => _125.hover, 'optionalAccess', _126 => _126.anchor])) {
5690
+ const sp = this.pen ? _optionalChain([this, 'access', _143 => _143.getNode, 'call', _144 => _144(this.pen.nodeId), 'optionalAccess', _145 => _145.subpaths, 'access', _146 => _146[this.pen.subpath]]) : void 0;
5691
+ if (this.pen && sp && _optionalChain([this, 'access', _147 => _147.hover, 'optionalAccess', _148 => _148.anchor])) {
4706
5692
  this.setGuides(null);
4707
5693
  return p;
4708
5694
  }
4709
5695
  const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
4710
5696
  const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
4711
- return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _127 => _127.pen, 'optionalAccess', _128 => _128.nodeId]), () => ( null))));
5697
+ return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _149 => _149.pen, 'optionalAccess', _150 => _150.nodeId]), () => ( null))));
4712
5698
  }
4713
5699
  if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
4714
5700
  this.setGuides(null);
@@ -4856,7 +5842,7 @@ var VectorEditorController = (_class3 = class {
4856
5842
  if (d.fillRule) out.fillRule = d.fillRule;
4857
5843
  if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
4858
5844
  for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
4859
- if (_optionalChain([d, 'access', _129 => _129.strokeWidthProfile, 'optionalAccess', _130 => _130.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
5845
+ if (_optionalChain([d, 'access', _151 => _151.strokeWidthProfile, 'optionalAccess', _152 => _152.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
4860
5846
  return out;
4861
5847
  }
4862
5848
  /** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
@@ -4911,7 +5897,7 @@ var VectorEditorController = (_class3 = class {
4911
5897
  const byNode = /* @__PURE__ */ new Map();
4912
5898
  for (const r of refs) {
4913
5899
  const node = this.getNode(r.nodeId);
4914
- if (_optionalChain([node, 'optionalAccess', _131 => _131.type]) !== "path" || !_optionalChain([node, 'access', _132 => _132.strokeWidthProfile, 'optionalAccess', _133 => _133[r.index]])) continue;
5900
+ if (_optionalChain([node, 'optionalAccess', _153 => _153.type]) !== "path" || !_optionalChain([node, 'access', _154 => _154.strokeWidthProfile, 'optionalAccess', _155 => _155[r.index]])) continue;
4915
5901
  byNode.set(r.nodeId, [..._nullishCoalesce(byNode.get(r.nodeId), () => ( [])), r.index]);
4916
5902
  }
4917
5903
  if (!byNode.size) return { ok: false, message: "There is no such width point." };
@@ -4929,12 +5915,12 @@ var VectorEditorController = (_class3 = class {
4929
5915
  }
4930
5916
  /** Selects width points (width tool); points on other paths than the first are dropped. */
4931
5917
  selectWidthPoints(refs) {
4932
- const first = _optionalChain([refs, 'access', _134 => _134[0], 'optionalAccess', _135 => _135.nodeId]);
5918
+ const first = _optionalChain([refs, 'access', _156 => _156[0], 'optionalAccess', _157 => _157.nodeId]);
4933
5919
  const next = [];
4934
5920
  for (const r of refs) {
4935
5921
  if (r.nodeId !== first || next.some((q) => q.index === r.index)) continue;
4936
5922
  const n = this.getNode(r.nodeId);
4937
- if (_optionalChain([n, 'optionalAccess', _136 => _136.type]) === "path" && _optionalChain([n, 'access', _137 => _137.strokeWidthProfile, 'optionalAccess', _138 => _138[r.index]])) next.push({ nodeId: r.nodeId, index: r.index, subpath: _nullishCoalesce(r.subpath, () => ( 0)) });
5923
+ if (_optionalChain([n, 'optionalAccess', _158 => _158.type]) === "path" && _optionalChain([n, 'access', _159 => _159.strokeWidthProfile, 'optionalAccess', _160 => _160[r.index]])) next.push({ nodeId: r.nodeId, index: r.index, subpath: _nullishCoalesce(r.subpath, () => ( 0)) });
4938
5924
  }
4939
5925
  this.widthPoints = next;
4940
5926
  this.notify();
@@ -4949,7 +5935,7 @@ var VectorEditorController = (_class3 = class {
4949
5935
  */
4950
5936
  setWidthPoint(target, values) {
4951
5937
  const node = this.getNode(target.nodeId);
4952
- if (_optionalChain([node, 'optionalAccess', _139 => _139.type]) !== "path") return { ok: false, message: "There is no such path." };
5938
+ if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) !== "path") return { ok: false, message: "There is no such path." };
4953
5939
  const g = vectorStrokeWidthGeometry(node);
4954
5940
  if (!g || !(g.unit > 0)) return { ok: false, message: "The path has no visible stroke." };
4955
5941
  const num = (v2) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : void 0;
@@ -4982,11 +5968,11 @@ var VectorEditorController = (_class3 = class {
4982
5968
  }
4983
5969
  }
4984
5970
  }
4985
- const subpath = _optionalChain([this, 'access', _140 => _140.widthEdit, 'optionalAccess', _141 => _141.nodeId]) === node.id ? this.widthEdit.subpath : _nullishCoalesce(_optionalChain([this, 'access', _142 => _142.widthPoints, 'access', _143 => _143.find, 'call', _144 => _144((r) => r.nodeId === node.id), 'optionalAccess', _145 => _145.subpath]), () => ( 0));
5971
+ const subpath = _optionalChain([this, 'access', _162 => _162.widthEdit, 'optionalAccess', _163 => _163.nodeId]) === node.id ? this.widthEdit.subpath : _nullishCoalesce(_optionalChain([this, 'access', _164 => _164.widthPoints, 'access', _165 => _165.find, 'call', _166 => _166((r) => r.nodeId === node.id), 'optionalAccess', _167 => _167.subpath]), () => ( 0));
4986
5972
  const edit = this.widthEdit;
4987
5973
  this.change("Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: points })));
4988
5974
  this.widthPoints = [{ nodeId: node.id, index, subpath }];
4989
- if (_optionalChain([edit, 'optionalAccess', _146 => _146.nodeId]) === node.id && (edit.index === existing || existing === null)) this.widthEdit = this.widthEditFor(node.id, subpath, index, null);
5975
+ if (_optionalChain([edit, 'optionalAccess', _168 => _168.nodeId]) === node.id && (edit.index === existing || existing === null)) this.widthEdit = this.widthEditFor(node.id, subpath, index, null);
4990
5976
  this.notify();
4991
5977
  return { ok: true };
4992
5978
  }
@@ -5020,7 +6006,7 @@ var VectorEditorController = (_class3 = class {
5020
6006
  /** The editor's view of point `index` (or of the stroke at `t` when index is null) of `nodeId`. */
5021
6007
  widthEditFor(nodeId, subpath, index, t) {
5022
6008
  const node = this.getNode(nodeId);
5023
- if (_optionalChain([node, 'optionalAccess', _147 => _147.type]) !== "path") return null;
6009
+ if (_optionalChain([node, 'optionalAccess', _169 => _169.type]) !== "path") return null;
5024
6010
  const g = vectorStrokeWidthGeometry(node);
5025
6011
  if (!g) return null;
5026
6012
  const profile = sanitizeWidthProfile(node.strokeWidthProfile);
@@ -5054,7 +6040,7 @@ var VectorEditorController = (_class3 = class {
5054
6040
  const refs = this.cornerTargets(options);
5055
6041
  if (!refs.length) return { ok: false, message: "Nothing is selected." };
5056
6042
  this.change("Corner Kind", () => {
5057
- this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _148 => _148.corner, 'optionalAccess', _149 => _149.radius]), () => ( 0)), kind));
6043
+ this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _170 => _170.corner, 'optionalAccess', _171 => _171.radius]), () => ( 0)), kind));
5058
6044
  });
5059
6045
  return { ok: true };
5060
6046
  }
@@ -5193,10 +6179,10 @@ var VectorEditorController = (_class3 = class {
5193
6179
  return this.selection.filter((id) => {
5194
6180
  let covered = false;
5195
6181
  const loc = locateVectorNode(this.doc.nodes, id);
5196
- let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _150 => _150.parent]), () => ( null));
6182
+ let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _172 => _172.parent]), () => ( null));
5197
6183
  while (parent) {
5198
6184
  if (this.selection.includes(parent.id)) covered = true;
5199
- parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _151 => _151(this.doc.nodes, parent.id), 'optionalAccess', _152 => _152.parent]), () => ( null));
6185
+ parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _173 => _173(this.doc.nodes, parent.id), 'optionalAccess', _174 => _174.parent]), () => ( null));
5200
6186
  }
5201
6187
  return !covered;
5202
6188
  });
@@ -5249,24 +6235,24 @@ var VectorEditorController = (_class3 = class {
5249
6235
  return this.transform(translateMatrix(dx, dy), { ids: options.ids, label: "Move" });
5250
6236
  }
5251
6237
  /** The selection frame's centre in document space. */
5252
- frameCenter(frame) {
5253
- return applyMatrix(rotateMatrix(frame.angle), { x: frame.x + frame.width / 2, y: frame.y + frame.height / 2 });
6238
+ frameCenter(frame2) {
6239
+ return applyMatrix(rotateMatrix(frame2.angle), { x: frame2.x + frame2.width / 2, y: frame2.y + frame2.height / 2 });
5254
6240
  }
5255
6241
  /**
5256
6242
  * Scales along the selection box's own axes (rotated with the selection when its nodes
5257
6243
  * share a rotation) around `origin` (default: the box centre, in document space).
5258
6244
  */
5259
6245
  scale(sx, sy = sx, options = {}) {
5260
- const frame = this.frameOf(_nullishCoalesce(options.ids, () => ( this.selectedRoots())));
5261
- if (!frame) return { ok: false, message: "Nothing is selected." };
5262
- const origin = applyMatrix(rotateMatrix(-frame.angle), _nullishCoalesce(options.origin, () => ( this.frameCenter(frame))));
5263
- return this.transform(this.inFrame(frame.angle, scaleMatrix(sx, sy, origin)), { ids: options.ids, label: "Scale" });
6246
+ const frame2 = this.frameOf(_nullishCoalesce(options.ids, () => ( this.selectedRoots())));
6247
+ if (!frame2) return { ok: false, message: "Nothing is selected." };
6248
+ const origin = applyMatrix(rotateMatrix(-frame2.angle), _nullishCoalesce(options.origin, () => ( this.frameCenter(frame2))));
6249
+ return this.transform(this.inFrame(frame2.angle, scaleMatrix(sx, sy, origin)), { ids: options.ids, label: "Scale" });
5264
6250
  }
5265
6251
  /** Rotates clockwise by `degrees` around `origin` (default: the selection centre). */
5266
6252
  rotate(degrees, options = {}) {
5267
- const frame = this.frameOf(_nullishCoalesce(options.ids, () => ( this.selectedRoots())));
5268
- if (!frame) return { ok: false, message: "Nothing is selected." };
5269
- const origin = _nullishCoalesce(options.origin, () => ( this.frameCenter(frame)));
6253
+ const frame2 = this.frameOf(_nullishCoalesce(options.ids, () => ( this.selectedRoots())));
6254
+ if (!frame2) return { ok: false, message: "Nothing is selected." };
6255
+ const origin = _nullishCoalesce(options.origin, () => ( this.frameCenter(frame2)));
5270
6256
  return this.transform(rotateMatrix(degrees, origin), { ids: options.ids, label: "Rotate" });
5271
6257
  }
5272
6258
  /** Sets each selected (or given) node's rotation to `degrees`, turning it around its own centre. */
@@ -5275,21 +6261,21 @@ var VectorEditorController = (_class3 = class {
5275
6261
  if (!ids.length) return { ok: false, message: "Nothing is selected." };
5276
6262
  this.change("Set Rotation", () => {
5277
6263
  for (const id of ids) {
5278
- const frame = this.frameOf([id]);
5279
- if (!frame) continue;
5280
- const delta = normalizeAngle(degrees - frame.angle);
5281
- if (Math.abs(delta) > 1e-9) this.doc = this.transformed(this.doc, /* @__PURE__ */ new Set([id]), rotateMatrix(delta, this.frameCenter(frame)));
6264
+ const frame2 = this.frameOf([id]);
6265
+ if (!frame2) continue;
6266
+ const delta = normalizeAngle(degrees - frame2.angle);
6267
+ if (Math.abs(delta) > 1e-9) this.doc = this.transformed(this.doc, /* @__PURE__ */ new Set([id]), rotateMatrix(delta, this.frameCenter(frame2)));
5282
6268
  }
5283
6269
  });
5284
6270
  return { ok: true };
5285
6271
  }
5286
6272
  /** Mirrors along the selection box's own axes. */
5287
6273
  flip(axis, options = {}) {
5288
- const frame = this.frameOf(_nullishCoalesce(options.ids, () => ( this.selectedRoots())));
5289
- if (!frame) return { ok: false, message: "Nothing is selected." };
5290
- const c = { x: frame.x + frame.width / 2, y: frame.y + frame.height / 2 };
6274
+ const frame2 = this.frameOf(_nullishCoalesce(options.ids, () => ( this.selectedRoots())));
6275
+ if (!frame2) return { ok: false, message: "Nothing is selected." };
6276
+ const c = { x: frame2.x + frame2.width / 2, y: frame2.y + frame2.height / 2 };
5291
6277
  const m = axis === "horizontal" ? scaleMatrix(-1, 1, c) : scaleMatrix(1, -1, c);
5292
- return this.transform(this.inFrame(frame.angle, m), { ids: options.ids, label: axis === "horizontal" ? "Flip Horizontal" : "Flip Vertical" });
6278
+ return this.transform(this.inFrame(frame2.angle, m), { ids: options.ids, label: axis === "horizontal" ? "Flip Horizontal" : "Flip Vertical" });
5293
6279
  }
5294
6280
  /**
5295
6281
  * Aligns the selected nodes to their combined bounds, or to the artboard when one node is
@@ -5379,8 +6365,8 @@ var VectorEditorController = (_class3 = class {
5379
6365
  if (!ids.length) return null;
5380
6366
  const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
5381
6367
  if (locs.some((l) => !l)) return null;
5382
- const parent = _nullishCoalesce(_optionalChain([locs, 'access', _153 => _153[0], 'access', _154 => _154.parent, 'optionalAccess', _155 => _155.id]), () => ( null));
5383
- if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _156 => _156.parent, 'optionalAccess', _157 => _157.id]), () => ( null))) !== parent)) return null;
6368
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _175 => _175[0], 'access', _176 => _176.parent, 'optionalAccess', _177 => _177.id]), () => ( null));
6369
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _178 => _178.parent, 'optionalAccess', _179 => _179.id]), () => ( null))) !== parent)) return null;
5384
6370
  const id = this.newId();
5385
6371
  this.change("Group", () => {
5386
6372
  const set = new Set(ids);
@@ -5403,7 +6389,7 @@ var VectorEditorController = (_class3 = class {
5403
6389
  }
5404
6390
  /** Replaces selected groups with their children. */
5405
6391
  ungroup(options = {}) {
5406
- const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _158 => _158.type]) === "group");
6392
+ const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _180 => _180.type]) === "group");
5407
6393
  if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
5408
6394
  const set = new Set(groups.map((g) => g.id));
5409
6395
  this.change("Ungroup", () => {
@@ -5416,7 +6402,7 @@ var VectorEditorController = (_class3 = class {
5416
6402
  const opacity = n.opacity;
5417
6403
  const m = nodeMatrix(n);
5418
6404
  out.push(
5419
- ...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)
6405
+ ...n.children.map((c) => c.blendStep ? (({ blendStep: _s, locked: _l, ...rest }) => rest)(c) : c).map((c) => n.transform ? transformVectorNode(c, m) : c).map((c) => opacity !== void 0 && opacity < 1 ? { ...c, opacity: (_nullishCoalesce(c.opacity, () => ( 1))) * opacity } : c)
5420
6406
  );
5421
6407
  } else if (n.type === "group") {
5422
6408
  const children = flatten2(n.children);
@@ -5517,7 +6503,7 @@ var VectorEditorController = (_class3 = class {
5517
6503
  }
5518
6504
  /** Splits selected compound paths into one path per subpath. */
5519
6505
  breakApart(options = {}) {
5520
- const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _159 => _159.type]) === "path" && n.subpaths.length > 1);
6506
+ const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _181 => _181.type]) === "path" && n.subpaths.length > 1);
5521
6507
  if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
5522
6508
  const created = [];
5523
6509
  const set = new Set(paths.map((p) => p.id));
@@ -5580,10 +6566,177 @@ var VectorEditorController = (_class3 = class {
5580
6566
  return nodes.map((n) => n.id);
5581
6567
  }
5582
6568
  /** The Shape Builder map of the selected (or given) paths, cached per document and selection. */
6569
+ // -------------------------------------------------------------------------
6570
+ // Blends
6571
+ // -------------------------------------------------------------------------
6572
+ /** Selected (or given) blend groups; a selected key or step counts as its blend. */
6573
+ blendTargets(ids) {
6574
+ const out = /* @__PURE__ */ new Map();
6575
+ for (const id of _nullishCoalesce(ids, () => ( this.selection))) {
6576
+ const node = this.getNode(id);
6577
+ const group = isVectorBlendGroup(node) ? node : _optionalChain([locateVectorNode, 'call', _182 => _182(this.doc.nodes, id), 'optionalAccess', _183 => _183.parent]);
6578
+ if (isVectorBlendGroup(group)) out.set(group.id, group);
6579
+ }
6580
+ return [...out.values()];
6581
+ }
6582
+ /**
6583
+ * Blends two or more selected (or given) paths or groups: they become the keys of a live
6584
+ * blend group (bottom to top, or in the order given with `keepOrder`) whose in-betweens are
6585
+ * regenerated whenever a key or the options change. The keys must share a parent. Returns
6586
+ * the blend group's id, or null.
6587
+ */
6588
+ blend(input = {}) {
6589
+ const ids = [...new Set(_nullishCoalesce(input.ids, () => ( this.selectedRoots())))].filter((id2) => {
6590
+ const n = this.getNode(id2);
6591
+ return !!n && !n.blendStep && !n.locked;
6592
+ });
6593
+ if (ids.length < 2) return null;
6594
+ const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
6595
+ const parent = _nullishCoalesce(_optionalChain([locs, 'access', _184 => _184[0], 'access', _185 => _185.parent, 'optionalAccess', _186 => _186.id]), () => ( null));
6596
+ if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access', _187 => _187.parent, 'optionalAccess', _188 => _188.id]), () => ( null))) !== parent)) return null;
6597
+ const order = input.keepOrder ? ids : ids.slice().sort((a, b) => locs[ids.indexOf(a)].index - locs[ids.indexOf(b)].index);
6598
+ const blend = blendOptionsOf({ ...this.blendDefaults, ...input });
6599
+ if (input.starts) {
6600
+ const starts = Object.fromEntries(Object.entries(input.starts).filter(([k, v2]) => order.includes(k) && Number.isInteger(v2) && v2 >= 0));
6601
+ if (Object.keys(starts).length) blend.starts = starts;
6602
+ }
6603
+ const id = this.newId();
6604
+ this.change("Blend", () => {
6605
+ const set = new Set(order);
6606
+ const regroup = (list) => {
6607
+ const members = order.map((m) => list.find((n) => n.id === m));
6608
+ const top = Math.max(...members.map((m) => list.indexOf(m)));
6609
+ const out = [];
6610
+ list.forEach((n, i) => {
6611
+ if (i === top) out.push({ id, type: "group", name: _nullishCoalesce(input.name, () => ( "Blend")), blend, children: members });
6612
+ if (!set.has(n.id)) out.push(n);
6613
+ });
6614
+ return out;
6615
+ };
6616
+ if (parent === null) this.setDoc(regroup(this.doc.nodes));
6617
+ else this.setDoc(mapTree(this.doc.nodes, (n) => n.id === parent && n.type === "group" ? { ...n, children: regroup(n.children) } : n));
6618
+ this.selection = [id];
6619
+ this.anchors = [];
6620
+ this.editingId = null;
6621
+ });
6622
+ return id;
6623
+ }
6624
+ /** Sets the options new blends get (spacing, steps, distance, orientation, morph). */
6625
+ setBlendDefaults(patch) {
6626
+ this.blendDefaults = { ...this.blendDefaults, ...blendOptionsOf(patch) };
6627
+ this.notify();
6628
+ }
6629
+ /**
6630
+ * Adds a node as the last key of a blend (it must share the blend's parent); `start` is the
6631
+ * anchor of its first subpath the blend starts from. One undo step.
6632
+ */
6633
+ addBlendKey(blendId, nodeId, options = {}) {
6634
+ const group = this.getNode(blendId);
6635
+ const node = this.getNode(nodeId);
6636
+ if (!isVectorBlendGroup(group)) return { ok: false, message: `"${blendId}" is not a blend.` };
6637
+ if (!node || node.blendStep || node.locked || node.id === blendId) return { ok: false, message: `"${nodeId}" cannot be a key.` };
6638
+ const parentOf = (id) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _189 => _189(this.doc.nodes, id), 'optionalAccess', _190 => _190.parent, 'optionalAccess', _191 => _191.id]), () => ( null));
6639
+ if (parentOf(blendId) !== parentOf(nodeId)) return { ok: false, message: "The new key must sit beside the blend (same parent)." };
6640
+ const start = options.start;
6641
+ this.change("Blend", () => {
6642
+ const blend = start !== null && start !== void 0 ? { ...group.blend, starts: { ...group.blend.starts, [nodeId]: start } } : group.blend;
6643
+ const m = invertMatrix(nodeMatrix(group));
6644
+ const key = group.transform ? transformVectorNode(node, m) : node;
6645
+ const without = mapTree(this.doc.nodes, (n) => n.id === nodeId ? null : n);
6646
+ this.setDoc(spliceTree(without, blendId, [{ ...group, blend, children: [...group.children, key] }]));
6647
+ this.selection = [blendId];
6648
+ this.anchors = [];
6649
+ });
6650
+ return { ok: true };
6651
+ }
6652
+ /** Changes the spacing, steps, distance, orientation or morph of the selected (or given) blends. */
6653
+ setBlendOptions(patch, options = {}) {
6654
+ const groups = this.blendTargets(options.ids);
6655
+ if (!groups.length) return { ok: false, message: "Select a blend." };
6656
+ const clean2 = blendOptionsOf(patch);
6657
+ if (!Object.keys(clean2).length) return { ok: false, message: "No valid blend options were given." };
6658
+ const set = new Set(groups.map((g) => g.id));
6659
+ this.change("Blend Options", () => {
6660
+ this.setDoc(mapTree(this.doc.nodes, (n) => set.has(n.id) && n.type === "group" && n.blend ? { ...n, blend: { ...n.blend, ...clean2 } } : n));
6661
+ });
6662
+ return { ok: true };
6663
+ }
6664
+ /** Expand: turns the selected (or given) blends into plain groups of their keys and steps. Returns their ids. */
6665
+ expandBlend(options = {}) {
6666
+ const groups = this.blendTargets(options.ids);
6667
+ if (!groups.length) return [];
6668
+ const set = new Set(groups.map((g) => g.id));
6669
+ const used = /* @__PURE__ */ new Set();
6670
+ walkVectorNodes(this.doc.nodes, (n) => used.add(n.id));
6671
+ const fresh = () => {
6672
+ let id = this.createId();
6673
+ while (used.has(id)) id = this.createId();
6674
+ used.add(id);
6675
+ return id;
6676
+ };
6677
+ this.change("Expand Blend", () => {
6678
+ this.setDoc(mapTree(this.doc.nodes, (n) => set.has(n.id) && n.type === "group" ? expandVectorBlend(n, fresh) : n));
6679
+ this.selection = [...set];
6680
+ this.anchors = [];
6681
+ });
6682
+ return [...set];
6683
+ }
6684
+ /** Release: replaces the selected (or given) blends with their keys (and a replaced spine as a path). */
6685
+ releaseBlend(options = {}) {
6686
+ const groups = this.blendTargets(options.ids);
6687
+ if (!groups.length) return [];
6688
+ const released = [];
6689
+ this.change("Release Blend", () => {
6690
+ for (const g of groups) {
6691
+ const nodes = releaseVectorBlend(g, () => this.newId());
6692
+ released.push(...nodes.map((n) => n.id));
6693
+ this.setDoc(spliceTree(this.doc.nodes, g.id, nodes));
6694
+ }
6695
+ this.selection = released;
6696
+ this.anchors = [];
6697
+ });
6698
+ return released;
6699
+ }
6700
+ /**
6701
+ * Replace Spine: the selected blend's steps follow the selected path instead of the straight
6702
+ * lines between its keys. The path's first subpath becomes the spine (the path is removed)
6703
+ * and the keys move onto it, spread evenly by arc length.
6704
+ */
6705
+ replaceBlendSpine(options = {}) {
6706
+ const roots = _nullishCoalesce(options.ids, () => ( this.selectedRoots()));
6707
+ const groups = this.blendTargets(roots);
6708
+ const paths = roots.map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _192 => _192.type]) === "path" && !n.blendStep && !isVectorBlendGroup(_optionalChain([locateVectorNode, 'call', _193 => _193(this.doc.nodes, n.id), 'optionalAccess', _194 => _194.parent])));
6709
+ if (groups.length !== 1 || paths.length !== 1) return { ok: false, message: "Select one blend and one path to be its spine." };
6710
+ const group = groups[0];
6711
+ const path = paths[0];
6712
+ const first = path.subpaths.find((sp) => sp.anchors.length >= 2);
6713
+ if (!first) return { ok: false, message: "The spine needs a path with at least two points." };
6714
+ const m = multiplyMatrices(this.worldOf(path.id), invertMatrix(this.worldOf(group.id)));
6715
+ const spine = transformVectorNode({ id: "", type: "path", subpaths: [first] }, m).subpaths[0];
6716
+ this.change("Replace Spine", () => {
6717
+ const withSpine = { ...group, blend: { ...group.blend, spine } };
6718
+ const targets = vectorBlendKeyTargets(withSpine);
6719
+ let k = 0;
6720
+ const children = withSpine.children.map((c) => {
6721
+ if (c.blendStep) return c;
6722
+ const target = targets[k++];
6723
+ const b = vectorNodeBounds(c);
6724
+ if (!target || !b) return c;
6725
+ const dx = target[0] - (b.x + b.width / 2);
6726
+ const dy = target[1] - (b.y + b.height / 2);
6727
+ return Math.hypot(dx, dy) > 1e-9 ? transformVectorNode(c, translateMatrix(dx, dy)) : c;
6728
+ });
6729
+ this.setDoc(mapTree(this.doc.nodes, (n) => n.id === path.id ? null : n.id === group.id ? { ...withSpine, children } : n));
6730
+ this.selection = [group.id];
6731
+ this.anchors = [];
6732
+ if (this.editingId === path.id) this.editingId = null;
6733
+ });
6734
+ return { ok: true };
6735
+ }
5583
6736
  shapeBuilderMap(ids) {
5584
6737
  const roots = _nullishCoalesce(ids, () => ( this.selectedRoots()));
5585
6738
  const key = roots.join("\0");
5586
- if (_optionalChain([this, 'access', _160 => _160.builderCache, 'optionalAccess', _161 => _161.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
6739
+ if (_optionalChain([this, 'access', _195 => _195.builderCache, 'optionalAccess', _196 => _196.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
5587
6740
  const entries = roots.length ? vectorPathEntries(this.doc, roots) : [];
5588
6741
  const map = entries.length ? vectorShapeBuilderMap(entries) : null;
5589
6742
  this.builderCache = { doc: this.doc, key, map };
@@ -5643,9 +6796,9 @@ var VectorEditorController = (_class3 = class {
5643
6796
  const list = [];
5644
6797
  const r = plan.replace.get(i);
5645
6798
  if (r === void 0) list.push(node);
5646
- else if (_optionalChain([r, 'optionalAccess', _162 => _162.kind]) === "fill") list.push({ ...node, fillRule: "nonzero", subpaths: bezierToSubpaths(toLocal(r.path)) });
5647
- else if (_optionalChain([r, 'optionalAccess', _163 => _163.kind]) === "outline") list.push({ ...node, subpaths: bezierToSubpaths(toLocal(r.path)) });
5648
- else if (_optionalChain([r, 'optionalAccess', _164 => _164.kind]) === "pieces") {
6799
+ else if (_optionalChain([r, 'optionalAccess', _197 => _197.kind]) === "fill") list.push({ ...node, fillRule: "nonzero", subpaths: bezierToSubpaths(toLocal(r.path)) });
6800
+ else if (_optionalChain([r, 'optionalAccess', _198 => _198.kind]) === "outline") list.push({ ...node, subpaths: bezierToSubpaths(toLocal(r.path)) });
6801
+ else if (_optionalChain([r, 'optionalAccess', _199 => _199.kind]) === "pieces") {
5649
6802
  r.paths.forEach(
5650
6803
  (path, k) => list.push({ ...node, id: k === 0 ? node.id : this.newId(), subpaths: bezierToSubpaths(toLocal(path)).map((sp) => ({ ...sp, closed: false })) })
5651
6804
  );
@@ -5679,7 +6832,7 @@ var VectorEditorController = (_class3 = class {
5679
6832
  }
5680
6833
  /** The paint bucket's map of the visible outlines, cached per document and gap. */
5681
6834
  fillMap(gap) {
5682
- if (_optionalChain([this, 'access', _165 => _165.fillCache, 'optionalAccess', _166 => _166.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
6835
+ if (_optionalChain([this, 'access', _200 => _200.fillCache, 'optionalAccess', _201 => _201.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
5683
6836
  const map = vectorFillMap(this.doc, gap);
5684
6837
  this.fillCache = { doc: this.doc, gap, map };
5685
6838
  return map;
@@ -5693,8 +6846,8 @@ var VectorEditorController = (_class3 = class {
5693
6846
  */
5694
6847
  fillRegion(point, options = {}) {
5695
6848
  if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) return null;
5696
- const region = _optionalChain([this, 'access', _167 => _167.fillMap, 'call', _168 => _168(_nullishCoalesce(options.gap, () => ( this.editing.fillGap))), 'optionalAccess', _169 => _169.regionAt, 'call', _170 => _170([point.x, point.y])]);
5697
- if (!_optionalChain([region, 'optionalAccess', _171 => _171.length])) return null;
6849
+ const region = _optionalChain([this, 'access', _202 => _202.fillMap, 'call', _203 => _203(_nullishCoalesce(options.gap, () => ( this.editing.fillGap))), 'optionalAccess', _204 => _204.regionAt, 'call', _205 => _205([point.x, point.y])]);
6850
+ if (!_optionalChain([region, 'optionalAccess', _206 => _206.length])) return null;
5698
6851
  let below = null;
5699
6852
  const roots = new Set(this.selectedRoots());
5700
6853
  walkVectorNodes(this.doc.nodes, (n) => {
@@ -5720,7 +6873,7 @@ var VectorEditorController = (_class3 = class {
5720
6873
  /** Updates the Shape Builder / paint bucket highlight for the pointer at `p`; true when it changed. */
5721
6874
  hoverBuilder(p, alt) {
5722
6875
  if (this.tool === "paintBucket") {
5723
- const region = _nullishCoalesce(_optionalChain([this, 'access', _172 => _172.fillMap, 'call', _173 => _173(this.editing.fillGap), 'optionalAccess', _174 => _174.regionAt, 'call', _175 => _175([p.x, p.y])]), () => ( null));
6876
+ const region = _nullishCoalesce(_optionalChain([this, 'access', _207 => _207.fillMap, 'call', _208 => _208(this.editing.fillGap), 'optionalAccess', _209 => _209.regionAt, 'call', _210 => _210([p.x, p.y])]), () => ( null));
5724
6877
  const key2 = region ? _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, region, { precision: 2 }) : null;
5725
6878
  if (key2 === this.builderHoverKey) return false;
5726
6879
  this.builderHoverKey = key2;
@@ -5828,7 +6981,7 @@ var VectorEditorController = (_class3 = class {
5828
6981
  /** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
5829
6982
  curvatureMoved(doc, ref, to) {
5830
6983
  const node = findVectorNode(doc.nodes, ref.nodeId);
5831
- if (_optionalChain([node, 'optionalAccess', _176 => _176.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
6984
+ if (_optionalChain([node, 'optionalAccess', _211 => _211.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5832
6985
  const q = this.toLocal(ref.nodeId, to, doc);
5833
6986
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
5834
6987
  const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
@@ -5837,7 +6990,7 @@ var VectorEditorController = (_class3 = class {
5837
6990
  /** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
5838
6991
  bent(doc, ref, t, p) {
5839
6992
  const node = findVectorNode(doc.nodes, ref.nodeId);
5840
- if (_optionalChain([node, 'optionalAccess', _177 => _177.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
6993
+ if (_optionalChain([node, 'optionalAccess', _212 => _212.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
5841
6994
  const q = this.toLocal(ref.nodeId, p, doc);
5842
6995
  const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
5843
6996
  return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
@@ -5848,7 +7001,7 @@ var VectorEditorController = (_class3 = class {
5848
7001
  */
5849
7002
  bendSegment(ref, to, t = 0.5) {
5850
7003
  const node = this.getNode(ref.nodeId);
5851
- const sp = _optionalChain([node, 'optionalAccess', _178 => _178.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
7004
+ const sp = _optionalChain([node, 'optionalAccess', _213 => _213.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
5852
7005
  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." };
5853
7006
  if (node.locked) return { ok: false, message: "The path is locked." };
5854
7007
  this.change("Bend Segment", () => {
@@ -5883,7 +7036,7 @@ var VectorEditorController = (_class3 = class {
5883
7036
  /** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
5884
7037
  insertAnchorAt(nodeId, point) {
5885
7038
  const node = this.getNode(nodeId);
5886
- if (_optionalChain([node, 'optionalAccess', _179 => _179.type]) !== "path") return null;
7039
+ if (_optionalChain([node, 'optionalAccess', _214 => _214.type]) !== "path") return null;
5887
7040
  const hit = nearestSegment(node, this.toLocal(nodeId, point));
5888
7041
  if (!hit) return null;
5889
7042
  const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
@@ -5927,7 +7080,7 @@ var VectorEditorController = (_class3 = class {
5927
7080
  setClosed(closed, nodeId) {
5928
7081
  const targets = nodeId ? (() => {
5929
7082
  const n = this.getNode(nodeId);
5930
- return _optionalChain([n, 'optionalAccess', _180 => _180.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
7083
+ return _optionalChain([n, 'optionalAccess', _215 => _215.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
5931
7084
  })() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
5932
7085
  if (!targets.length) return { ok: false, message: "Select a path or points." };
5933
7086
  this.change(closed ? "Close Path" : "Open Path", () => {
@@ -5948,7 +7101,7 @@ var VectorEditorController = (_class3 = class {
5948
7101
  this.setDoc(
5949
7102
  mapTree(
5950
7103
  this.doc.nodes,
5951
- (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _181 => _181.strokeWidthProfile, 'optionalAccess', _182 => _182.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
7104
+ (n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access', _216 => _216.strokeWidthProfile, 'optionalAccess', _217 => _217.length]) ? { strokeWidthProfile: reverseWidthProfile(n.strokeWidthProfile) } : {} } : n
5952
7105
  )
5953
7106
  );
5954
7107
  this.anchors = [];
@@ -6086,7 +7239,7 @@ var VectorEditorController = (_class3 = class {
6086
7239
  */
6087
7240
  cutPathAt(nodeId, point) {
6088
7241
  const node = this.getNode(nodeId);
6089
- if (_optionalChain([node, 'optionalAccess', _183 => _183.type]) !== "path" || node.locked) return null;
7242
+ if (_optionalChain([node, 'optionalAccess', _218 => _218.type]) !== "path" || node.locked) return null;
6090
7243
  const local = this.toLocal(nodeId, point);
6091
7244
  const hit = nearestSegment(node, local);
6092
7245
  if (!hit) return null;
@@ -6136,7 +7289,7 @@ var VectorEditorController = (_class3 = class {
6136
7289
  if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
6137
7290
  });
6138
7291
  const visible = (p) => !blocked.has(p.id);
6139
- if (_optionalChain([ids, 'optionalAccess', _184 => _184.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
7292
+ if (_optionalChain([ids, 'optionalAccess', _219 => _219.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
6140
7293
  const box = polylineBounds(points.map(toPoint2), pad);
6141
7294
  if (!box) return [];
6142
7295
  const out = [];
@@ -6222,7 +7375,7 @@ var VectorEditorController = (_class3 = class {
6222
7375
  finishPath() {
6223
7376
  if (!this.pen) return;
6224
7377
  const node = this.getNode(this.pen.nodeId);
6225
- const sp = _optionalChain([node, 'optionalAccess', _185 => _185.subpaths, 'access', _186 => _186[this.pen.subpath]]);
7378
+ const sp = _optionalChain([node, 'optionalAccess', _220 => _220.subpaths, 'access', _221 => _221[this.pen.subpath]]);
6226
7379
  const pen = this.pen;
6227
7380
  this.pen = null;
6228
7381
  this.pointer = null;
@@ -6266,19 +7419,19 @@ var VectorEditorController = (_class3 = class {
6266
7419
  if (trimmed.startsWith("{")) {
6267
7420
  try {
6268
7421
  const data = JSON.parse(trimmed);
6269
- if (_optionalChain([data, 'optionalAccess', _187 => _187.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
6270
- } catch (e3) {
7422
+ if (_optionalChain([data, 'optionalAccess', _222 => _222.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
7423
+ } catch (e4) {
6271
7424
  nodes = null;
6272
7425
  }
6273
7426
  } else if (/<svg[\s>]/i.test(trimmed)) {
6274
7427
  try {
6275
7428
  nodes = parseVectorSvg(trimmed).nodes;
6276
- } catch (e4) {
7429
+ } catch (e5) {
6277
7430
  nodes = null;
6278
7431
  }
6279
7432
  }
6280
7433
  } else nodes = this.clipboard;
6281
- if (!_optionalChain([nodes, 'optionalAccess', _188 => _188.length])) return [];
7434
+ if (!_optionalChain([nodes, 'optionalAccess', _223 => _223.length])) return [];
6282
7435
  const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
6283
7436
  const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
6284
7437
  this.change("Paste", () => {
@@ -6318,9 +7471,9 @@ var VectorEditorController = (_class3 = class {
6318
7471
  }
6319
7472
  hitTransformHandle(screen) {
6320
7473
  if (this.tool !== "select" || !this.selection.length) return null;
6321
- const frame = this.frameOf(this.selectedRoots());
6322
- if (!frame) return null;
6323
- const { box, handles } = getVectorSelectionBoxGeometry(frame, this.viewport);
7474
+ const frame2 = this.frameOf(this.selectedRoots());
7475
+ if (!frame2) return null;
7476
+ const { box, handles } = getVectorSelectionBoxGeometry(frame2, this.viewport);
6324
7477
  const r = this.hitRadius + 2;
6325
7478
  const usable2 = box.width < 2 * r || box.height < 2 * r ? handles.filter((h) => h.handle.length === 2 || h.handle === "rotate") : handles;
6326
7479
  for (const h of usable2) if (Math.hypot(h.screen.x - screen.x, h.screen.y - screen.y) <= r * Math.SQRT2) return h.handle;
@@ -6329,7 +7482,7 @@ var VectorEditorController = (_class3 = class {
6329
7482
  hitHandle(screen) {
6330
7483
  for (const ref of getVisibleHandles(this.view()).reverse()) {
6331
7484
  const a = getAnchorWorld(this.doc, ref);
6332
- const h = _optionalChain([a, 'optionalAccess', _189 => _189[ref.handle]]);
7485
+ const h = _optionalChain([a, 'optionalAccess', _224 => _224[ref.handle]]);
6333
7486
  if (!h) continue;
6334
7487
  const s = this.documentToScreen(h);
6335
7488
  if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
@@ -6432,6 +7585,11 @@ var VectorEditorController = (_class3 = class {
6432
7585
  const seg = this.nearestOutline(p);
6433
7586
  return seg ? { nodeId: seg.nodeId, segment: seg } : null;
6434
7587
  }
7588
+ case "blend": {
7589
+ const hit = this.blendHit(screen, p);
7590
+ if (!hit) return null;
7591
+ return hit.anchor === null ? { nodeId: hit.nodeId } : { nodeId: hit.nodeId, anchor: { nodeId: hit.nodeId, subpath: 0, index: hit.anchor } };
7592
+ }
6435
7593
  case "width": {
6436
7594
  const hit = this.hitWidthHandle(screen);
6437
7595
  if (hit) return { nodeId: hit.ref.nodeId, widthPoint: hit.ref };
@@ -6450,7 +7608,7 @@ var VectorEditorController = (_class3 = class {
6450
7608
  const shown = this.hitWidthHandleIn(screen, this.widthView());
6451
7609
  if (shown) return shown;
6452
7610
  const spot = this.strokeSpotAt(this.screenToDocument(screen));
6453
- return spot && spot.nodeId !== _optionalChain([this, 'access', _190 => _190.hover, 'optionalAccess', _191 => _191.nodeId]) ? this.hitWidthHandleIn(screen, { ...this.widthView(), hover: { nodeId: spot.nodeId } }) : null;
7611
+ return spot && spot.nodeId !== _optionalChain([this, 'access', _225 => _225.hover, 'optionalAccess', _226 => _226.nodeId]) ? this.hitWidthHandleIn(screen, { ...this.widthView(), hover: { nodeId: spot.nodeId } }) : null;
6454
7612
  }
6455
7613
  hitWidthHandleIn(screen, view) {
6456
7614
  let best = null;
@@ -6505,9 +7663,9 @@ var VectorEditorController = (_class3 = class {
6505
7663
  downWidth(screen, p, input) {
6506
7664
  const hit = this.hitWidthHandle(screen);
6507
7665
  const spot = hit ? null : this.strokeSpotAt(p);
6508
- const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _192 => _192.ref, 'access', _193 => _193.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _194 => _194.nodeId])));
7666
+ const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _227 => _227.ref, 'access', _228 => _228.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _229 => _229.nodeId])));
6509
7667
  const node = nodeId ? this.getNode(nodeId) : void 0;
6510
- const g = _optionalChain([node, 'optionalAccess', _195 => _195.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
7668
+ const g = _optionalChain([node, 'optionalAccess', _230 => _230.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
6511
7669
  if (this.widthEdit) this.widthEdit = null;
6512
7670
  if (!node || node.type !== "path" || !g) {
6513
7671
  if (!input.shift) this.widthPoints = [];
@@ -6527,7 +7685,7 @@ var VectorEditorController = (_class3 = class {
6527
7685
  if (hit) {
6528
7686
  profile = sanitizeWidthProfile(node.strokeWidthProfile);
6529
7687
  ref = { nodeId: node.id, index: hit.ref.index, subpath: hit.ref.subpath };
6530
- t = _nullishCoalesce(_optionalChain([profile, 'access', _196 => _196[ref.index], 'optionalAccess', _197 => _197.t]), () => ( 0));
7688
+ t = _nullishCoalesce(_optionalChain([profile, 'access', _231 => _231[ref.index], 'optionalAccess', _232 => _232.t]), () => ( 0));
6531
7689
  subpath = hit.ref.subpath;
6532
7690
  if (hit.ref.part === "center") mode = input.alt && profile.length < VECTOR_WIDTH_PROFILE_MAX_POINTS ? "copy" : "move";
6533
7691
  else if (input.alt) side = hit.ref.part;
@@ -6658,6 +7816,8 @@ var VectorEditorController = (_class3 = class {
6658
7816
  case "shapeBuilder":
6659
7817
  case "paintBucket":
6660
7818
  return this.downBuilder(p, input);
7819
+ case "blend":
7820
+ return this.downBlend(screen, p);
6661
7821
  case "scissors": {
6662
7822
  const seg = this.nearestOutline(p);
6663
7823
  if (seg) this.cutPathAt(seg.nodeId, seg.point);
@@ -6673,13 +7833,53 @@ var VectorEditorController = (_class3 = class {
6673
7833
  return this.downShape(p, input);
6674
7834
  }
6675
7835
  }
7836
+ /** The shape under the pointer for the Blend tool, and the anchor of its first subpath under it. */
7837
+ blendHit(screen, p) {
7838
+ const node = hitTestVectorNodes(this.doc.nodes, p, this.tolerance());
7839
+ if (!node) return null;
7840
+ let anchor = null;
7841
+ if (node.type === "path" && node.subpaths[0]) {
7842
+ const refs = node.subpaths[0].anchors.map((_, index) => ({ nodeId: node.id, subpath: 0, index }));
7843
+ anchor = _nullishCoalesce(_optionalChain([this, 'access', _233 => _233.hitAnchor, 'call', _234 => _234(screen, refs), 'optionalAccess', _235 => _235.index]), () => ( null));
7844
+ }
7845
+ return { nodeId: node.id, anchor };
7846
+ }
7847
+ /** Blend tool: the first click picks the shape to blend from, the next one blends it into the shape clicked. */
7848
+ downBlend(screen, p) {
7849
+ const hit = this.blendHit(screen, p);
7850
+ const start = this.blendStart;
7851
+ if (!hit) {
7852
+ if (start) {
7853
+ this.blendStart = null;
7854
+ this.notify();
7855
+ }
7856
+ return;
7857
+ }
7858
+ if (!start || start.nodeId === hit.nodeId) {
7859
+ this.blendStart = hit;
7860
+ this.selection = [hit.nodeId];
7861
+ this.anchors = [];
7862
+ this.notify();
7863
+ return;
7864
+ }
7865
+ this.blendStart = null;
7866
+ const first = this.getNode(start.nodeId);
7867
+ if (isVectorBlendGroup(first)) {
7868
+ if (!this.addBlendKey(first.id, hit.nodeId, { start: hit.anchor }).ok) this.notify();
7869
+ return;
7870
+ }
7871
+ const starts = {};
7872
+ if (start.anchor !== null) starts[start.nodeId] = start.anchor;
7873
+ if (hit.anchor !== null) starts[hit.nodeId] = hit.anchor;
7874
+ if (!this.blend({ ids: [start.nodeId, hit.nodeId], keepOrder: true, starts })) this.notify();
7875
+ }
6676
7876
  downSelect(screen, p, input) {
6677
7877
  const handle = this.hitTransformHandle(screen);
6678
7878
  if (handle) {
6679
- const frame = this.frameOf(this.selectedRoots());
7879
+ const frame2 = this.frameOf(this.selectedRoots());
6680
7880
  const before = this.snapshot();
6681
- const center = applyMatrix(rotateMatrix(frame.angle), { x: frame.x + frame.width / 2, y: frame.y + frame.height / 2 });
6682
- this.drag = handle === "rotate" ? { kind: "rotate", start: p, center, before, base: this.doc } : { kind: "scale", handle, start: p, frame, before, base: this.doc };
7881
+ const center = applyMatrix(rotateMatrix(frame2.angle), { x: frame2.x + frame2.width / 2, y: frame2.y + frame2.height / 2 });
7882
+ this.drag = handle === "rotate" ? { kind: "rotate", start: p, center, before, base: this.doc } : { kind: "scale", handle, start: p, frame: frame2, before, base: this.doc };
6683
7883
  this.interaction = handle === "rotate" ? { type: "rotate", angle: 0 } : { type: "scale", handle, sx: 1, sy: 1 };
6684
7884
  this.notify();
6685
7885
  return;
@@ -6793,7 +7993,7 @@ var VectorEditorController = (_class3 = class {
6793
7993
  }
6794
7994
  downPen(screen, p, input) {
6795
7995
  const before = this.snapshot();
6796
- const sp = this.pen ? _optionalChain([this, 'access', _198 => _198.getNode, 'call', _199 => _199(this.pen.nodeId), 'optionalAccess', _200 => _200.subpaths, 'access', _201 => _201[this.pen.subpath]]) : void 0;
7996
+ const sp = this.pen ? _optionalChain([this, 'access', _236 => _236.getNode, 'call', _237 => _237(this.pen.nodeId), 'optionalAccess', _238 => _238.subpaths, 'access', _239 => _239[this.pen.subpath]]) : void 0;
6797
7997
  if (this.pen && sp) {
6798
7998
  const pen = this.pen;
6799
7999
  const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
@@ -7055,7 +8255,7 @@ var VectorEditorController = (_class3 = class {
7055
8255
  }
7056
8256
  case "shape": {
7057
8257
  const node = this.getNode(drag.nodeId);
7058
- if (_optionalChain([node, 'optionalAccess', _202 => _202.type]) !== "path") return;
8258
+ if (_optionalChain([node, 'optionalAccess', _240 => _240.type]) !== "path") return;
7059
8259
  const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
7060
8260
  let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
7061
8261
  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);
@@ -7129,14 +8329,14 @@ var VectorEditorController = (_class3 = class {
7129
8329
  for (const q of drag.lazy.push([p.x, p.y])) {
7130
8330
  if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
7131
8331
  drag.points.push(q);
7132
- _optionalChain([drag, 'access', _203 => _203.pressures, 'optionalAccess', _204 => _204.push, 'call', _205 => _205(drag.pressure)]);
8332
+ _optionalChain([drag, 'access', _241 => _241.pressures, 'optionalAccess', _242 => _242.push, 'call', _243 => _243(drag.pressure)]);
7133
8333
  moved = true;
7134
8334
  }
7135
8335
  const string = drag.lazy.string();
7136
8336
  const node = this.getNode(drag.nodeId);
7137
- if (_optionalChain([node, 'optionalAccess', _206 => _206.type]) !== "path") return;
8337
+ if (_optionalChain([node, 'optionalAccess', _244 => _244.type]) !== "path") return;
7138
8338
  if (!moved) {
7139
- if (string && _optionalChain([this, 'access', _207 => _207.interaction, 'optionalAccess', _208 => _208.type]) === "draw") {
8339
+ if (string && _optionalChain([this, 'access', _245 => _245.interaction, 'optionalAccess', _246 => _246.type]) === "draw") {
7140
8340
  this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
7141
8341
  this.notify();
7142
8342
  }
@@ -7172,8 +8372,8 @@ var VectorEditorController = (_class3 = class {
7172
8372
  }
7173
8373
  this.setGuides(res);
7174
8374
  return {
7175
- dx: _optionalChain([res, 'optionalAccess', _209 => _209.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
7176
- dy: _optionalChain([res, 'optionalAccess', _210 => _210.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
8375
+ dx: _optionalChain([res, 'optionalAccess', _247 => _247.x]) ? dx + res.dx : axes.x ? this.gridValue(origin.x + dx) - origin.x : dx,
8376
+ dy: _optionalChain([res, 'optionalAccess', _248 => _248.y]) ? dy + res.dy : axes.y ? this.gridValue(origin.y + dy) - origin.y : dy
7177
8377
  };
7178
8378
  }
7179
8379
  /** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
@@ -7252,7 +8452,7 @@ var VectorEditorController = (_class3 = class {
7252
8452
  if (drag.moved) return this.commit("Corner Radius", drag.before);
7253
8453
  if (drag.alt) {
7254
8454
  const kind = nextVectorCornerKind(drag.widget.kind);
7255
- this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _211 => _211.corner, 'optionalAccess', _212 => _212.radius]), () => ( 0)), kind));
8455
+ this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _249 => _249.corner, 'optionalAccess', _250 => _250.radius]), () => ( 0)), kind));
7256
8456
  return this.commit("Corner Kind", drag.before);
7257
8457
  }
7258
8458
  const ref = drag.widget.ref;
@@ -7267,7 +8467,7 @@ var VectorEditorController = (_class3 = class {
7267
8467
  const several = drag.group.length > 0;
7268
8468
  if (drag.mode === "copy") this.widthPoints = [drag.ref];
7269
8469
  else if (drag.mode === "move" && several) {
7270
- this.widthPoints = [drag.source, ...drag.group].map((i) => ({ ...drag.ref, index: _nullishCoalesce(_optionalChain([drag, 'access', _213 => _213.renumber, 'optionalAccess', _214 => _214.get, 'call', _215 => _215(i)]), () => ( i)) }));
8470
+ this.widthPoints = [drag.source, ...drag.group].map((i) => ({ ...drag.ref, index: _nullishCoalesce(_optionalChain([drag, 'access', _251 => _251.renumber, 'optionalAccess', _252 => _252.get, 'call', _253 => _253(i)]), () => ( i)) }));
7271
8471
  } else if (!several) this.widthPoints = [drag.ref];
7272
8472
  const label = drag.mode === "copy" ? "Copy Width Point" : drag.mode === "move" ? several ? "Move Width Points" : "Move Width Point" : several ? "Width Points" : "Width Point";
7273
8473
  return this.commit(label, drag.before);
@@ -7309,14 +8509,14 @@ var VectorEditorController = (_class3 = class {
7309
8509
  }
7310
8510
  case "pencil": {
7311
8511
  const node = this.getNode(drag.nodeId);
7312
- if (_optionalChain([node, 'optionalAccess', _216 => _216.type]) !== "path" || drag.points.length < 2) {
8512
+ if (_optionalChain([node, 'optionalAccess', _254 => _254.type]) !== "path" || drag.points.length < 2) {
7313
8513
  this.doc = drag.before.doc;
7314
8514
  this.notify(true);
7315
8515
  return;
7316
8516
  }
7317
8517
  for (const q of drag.lazy.finish()) {
7318
8518
  drag.points.push(q);
7319
- _optionalChain([drag, 'access', _217 => _217.pressures, 'optionalAccess', _218 => _218.push, 'call', _219 => _219(drag.pressure)]);
8519
+ _optionalChain([drag, 'access', _255 => _255.pressures, 'optionalAccess', _256 => _256.push, 'call', _257 => _257(drag.pressure)]);
7320
8520
  }
7321
8521
  const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
7322
8522
  if (shape) {
@@ -7391,7 +8591,7 @@ var VectorEditorController = (_class3 = class {
7391
8591
  this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
7392
8592
  return;
7393
8593
  }
7394
- const id = _nullishCoalesce(_optionalChain([this, 'access', _220 => _220.hover, 'optionalAccess', _221 => _221.segment, 'optionalAccess', _222 => _222.nodeId]), () => ( this.editingId));
8594
+ const id = _nullishCoalesce(_optionalChain([this, 'access', _258 => _258.hover, 'optionalAccess', _259 => _259.segment, 'optionalAccess', _260 => _260.nodeId]), () => ( this.editingId));
7395
8595
  if (id) {
7396
8596
  const node = this.getNode(id);
7397
8597
  const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
@@ -7474,7 +8674,10 @@ var VectorEditorController = (_class3 = class {
7474
8674
  this.widthPoints = [];
7475
8675
  this.notify();
7476
8676
  } else if (this.pen) this.finishPath();
7477
- else if (this.tool === "direct" && this.anchors.length) {
8677
+ else if (this.blendStart) {
8678
+ this.blendStart = null;
8679
+ this.notify();
8680
+ } else if (this.tool === "direct" && this.anchors.length) {
7478
8681
  this.anchors = [];
7479
8682
  this.notify();
7480
8683
  } else if (this.tool !== "select") this.setTool("select");
@@ -7485,7 +8688,7 @@ var VectorEditorController = (_class3 = class {
7485
8688
  this.finishPath();
7486
8689
  return true;
7487
8690
  }
7488
- if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _223 => _223.getNode, 'call', _224 => _224(this.selection[0]), 'optionalAccess', _225 => _225.type]) === "path") {
8691
+ if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access', _261 => _261.getNode, 'call', _262 => _262(this.selection[0]), 'optionalAccess', _263 => _263.type]) === "path") {
7489
8692
  this.tool = "direct";
7490
8693
  this.editingId = this.selection[0];
7491
8694
  this.notify();
@@ -7628,7 +8831,7 @@ function renderNode(node, defs, idPrefix) {
7628
8831
  strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
7629
8832
  ...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
7630
8833
  ...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
7631
- ..._optionalChain([node, 'access', _226 => _226.strokeDasharray, 'optionalAccess', _227 => _227.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
8834
+ ..._optionalChain([node, 'access', _264 => _264.strokeDasharray, 'optionalAccess', _265 => _265.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
7632
8835
  fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
7633
8836
  opacity: _nullishCoalesce(node.opacity, () => ( 1))
7634
8837
  };
@@ -7677,9 +8880,9 @@ function getVectorOverlayRenderData(state) {
7677
8880
  };
7678
8881
  for (const id of state.selection) outline(id, true);
7679
8882
  if (state.editingId) outline(state.editingId, true);
7680
- if (_optionalChain([state, 'access', _228 => _228.hover, 'optionalAccess', _229 => _229.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
8883
+ if (_optionalChain([state, 'access', _266 => _266.hover, 'optionalAccess', _267 => _267.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
7681
8884
  let selectionBox = null;
7682
- if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _230 => _230.interaction, 'optionalAccess', _231 => _231.type]) !== "marquee") {
8885
+ if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _268 => _268.interaction, 'optionalAccess', _269 => _269.type]) !== "marquee") {
7683
8886
  const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
7684
8887
  selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
7685
8888
  }
@@ -7689,9 +8892,9 @@ function getVectorOverlayRenderData(state) {
7689
8892
  const a = getAnchorWorld(doc, ref);
7690
8893
  const s = toScreen(state, a);
7691
8894
  const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
7692
- return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _232 => _232.hover, 'optionalAccess', _233 => _233.anchor]), ref), closes };
8895
+ return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access', _270 => _270.hover, 'optionalAccess', _271 => _271.anchor]), ref), closes };
7693
8896
  });
7694
- if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _234 => _234.hover, 'optionalAccess', _235 => _235.anchor])) {
8897
+ if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _272 => _272.hover, 'optionalAccess', _273 => _273.anchor])) {
7695
8898
  const a = getAnchorWorld(doc, state.hover.anchor);
7696
8899
  if (a) {
7697
8900
  const s = toScreen(state, a);
@@ -7700,15 +8903,15 @@ function getVectorOverlayRenderData(state) {
7700
8903
  }
7701
8904
  const handles = getVisibleHandles(state).flatMap((ref) => {
7702
8905
  const a = getAnchorWorld(doc, ref);
7703
- const h = _optionalChain([a, 'optionalAccess', _236 => _236[ref.handle]]);
8906
+ const h = _optionalChain([a, 'optionalAccess', _274 => _274[ref.handle]]);
7704
8907
  if (!a || !h) return [];
7705
8908
  const s = toScreen(state, h);
7706
8909
  const sa = toScreen(state, a);
7707
- const hovered = !!_optionalChain([state, 'access', _237 => _237.hover, 'optionalAccess', _238 => _238.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
8910
+ const hovered = !!_optionalChain([state, 'access', _275 => _275.hover, 'optionalAccess', _276 => _276.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
7708
8911
  return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
7709
8912
  });
7710
8913
  let marquee = null;
7711
- if (_optionalChain([state, 'access', _239 => _239.interaction, 'optionalAccess', _240 => _240.type]) === "marquee") {
8914
+ if (_optionalChain([state, 'access', _277 => _277.interaction, 'optionalAccess', _278 => _278.type]) === "marquee") {
7712
8915
  const b = state.interaction.bounds;
7713
8916
  const tl = toScreen(state, b);
7714
8917
  marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
@@ -7716,7 +8919,7 @@ function getVectorOverlayRenderData(state) {
7716
8919
  let penPreview = null;
7717
8920
  if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
7718
8921
  const node = findVectorNode(doc.nodes, state.pen.nodeId);
7719
- const sp = _optionalChain([node, 'optionalAccess', _241 => _241.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
8922
+ const sp = _optionalChain([node, 'optionalAccess', _279 => _279.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
7720
8923
  const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
7721
8924
  if (last) {
7722
8925
  const a = toScreen(state, last);
@@ -7729,9 +8932,9 @@ function getVectorOverlayRenderData(state) {
7729
8932
  const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
7730
8933
  let status = null;
7731
8934
  const it = state.interaction;
7732
- if (_optionalChain([it, 'optionalAccess', _242 => _242.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
7733
- else if (_optionalChain([it, 'optionalAccess', _243 => _243.type]) === "move" || _optionalChain([it, 'optionalAccess', _244 => _244.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
7734
- else if (_optionalChain([it, 'optionalAccess', _245 => _245.type]) === "scale" || _optionalChain([it, 'optionalAccess', _246 => _246.type]) === "draw") {
8935
+ if (_optionalChain([it, 'optionalAccess', _280 => _280.type]) === "rotate") status = `${Math.round(it.angle)}\xB0`;
8936
+ else if (_optionalChain([it, 'optionalAccess', _281 => _281.type]) === "move" || _optionalChain([it, 'optionalAccess', _282 => _282.type]) === "anchors") status = `\u0394 ${fmt(it.dx)}, ${fmt(it.dy)}`;
8937
+ else if (_optionalChain([it, 'optionalAccess', _283 => _283.type]) === "scale" || _optionalChain([it, 'optionalAccess', _284 => _284.type]) === "draw") {
7735
8938
  const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
7736
8939
  if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
7737
8940
  }
@@ -7748,24 +8951,24 @@ function getVectorOverlayRenderData(state) {
7748
8951
  }
7749
8952
  }
7750
8953
  let toolStroke = null;
7751
- if (_optionalChain([it, 'optionalAccess', _247 => _247.type]) === "stroke") {
8954
+ if (_optionalChain([it, 'optionalAccess', _285 => _285.type]) === "stroke") {
7752
8955
  const pts = it.points.map((q) => toScreen(state, q));
7753
8956
  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)}` : "");
7754
8957
  toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
7755
8958
  }
7756
8959
  let pencilString = null;
7757
- if (_optionalChain([it, 'optionalAccess', _248 => _248.type]) === "draw" && it.string) {
8960
+ if (_optionalChain([it, 'optionalAccess', _286 => _286.type]) === "draw" && it.string) {
7758
8961
  const a = toScreen(state, it.string[0]);
7759
8962
  const b = toScreen(state, it.string[1]);
7760
8963
  pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
7761
8964
  }
7762
- const dragged = _optionalChain([it, 'optionalAccess', _249 => _249.type]) === "corner" ? it.ref : null;
8965
+ const dragged = _optionalChain([it, 'optionalAccess', _287 => _287.type]) === "corner" ? it.ref : null;
7763
8966
  const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
7764
8967
  const s = toScreen(state, w);
7765
- return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _250 => _250.hover, 'optionalAccess', _251 => _251.corner]), w.ref), active: !!dragged };
8968
+ return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access', _288 => _288.hover, 'optionalAccess', _289 => _289.corner]), w.ref), active: !!dragged };
7766
8969
  });
7767
- if (_optionalChain([it, 'optionalAccess', _252 => _252.type]) === "corner") status = `R ${fmt(it.radius)}`;
7768
- const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _253 => _253.hover, 'optionalAccess', _254 => _254.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
8970
+ if (_optionalChain([it, 'optionalAccess', _290 => _290.type]) === "corner") status = `R ${fmt(it.radius)}`;
8971
+ const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access', _291 => _291.hover, 'optionalAccess', _292 => _292.segment]) && !it ? (({ x, y }) => ({ x: fmt(x), y: fmt(y) }))(toScreen(state, state.hover.segment.point)) : null;
7769
8972
  const sc = (p) => {
7770
8973
  const q = toScreen(state, p);
7771
8974
  return { x: fmt(q.x), y: fmt(q.y) };
@@ -7773,13 +8976,13 @@ function getVectorOverlayRenderData(state) {
7773
8976
  const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
7774
8977
  const widthPoints = getVectorWidthHandles(state).map((h) => {
7775
8978
  const c = sc(h);
7776
- const hw = _optionalChain([state, 'access', _255 => _255.hover, 'optionalAccess', _256 => _256.widthPoint]);
8979
+ const hw = _optionalChain([state, 'access', _293 => _293.hover, 'optionalAccess', _294 => _294.widthPoint]);
7777
8980
  const hovered = hw && sameWidth(hw, h.ref) && hw.subpath === h.ref.subpath ? hw.part : null;
7778
- 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', _257 => _257.type]) === "width" && sameWidth(it.ref, h.ref) };
8981
+ 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', _295 => _295.type]) === "width" && sameWidth(it.ref, h.ref) };
7779
8982
  });
7780
- const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _258 => _258.hover, 'optionalAccess', _259 => _259.strokeSpot]) : null;
8983
+ const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _296 => _296.hover, 'optionalAccess', _297 => _297.strokeSpot]) : null;
7781
8984
  const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
7782
- if (_optionalChain([it, 'optionalAccess', _260 => _260.type]) === "width") status = it.mode === "move" ? "Move width point" : it.mode === "copy" ? "Copy width point" : `W ${fmt(it.width)}`;
8985
+ if (_optionalChain([it, 'optionalAccess', _298 => _298.type]) === "width") status = it.mode === "move" ? "Move width point" : it.mode === "copy" ? "Copy width point" : `W ${fmt(it.width)}`;
7783
8986
  let builder = null;
7784
8987
  if (state.builder) {
7785
8988
  const v2 = state.viewport;
@@ -7795,13 +8998,84 @@ function getVectorOverlayRenderData(state) {
7795
8998
  lines: state.builder.lines.map((l) => svg(_chunkVNYRI265cjs.transformBezierPath.call(void 0, l, view))),
7796
8999
  trail
7797
9000
  };
7798
- if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _261 => _261.type]) === "builder") {
9001
+ if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _299 => _299.type]) === "builder") {
7799
9002
  const n = state.builder.faces.length;
7800
9003
  status = state.builder.mode === "erase" ? `Delete ${n} ${n === 1 ? "face" : "faces"}` : `Merge ${n} ${n === 1 ? "face" : "faces"}`;
7801
9004
  }
7802
9005
  }
7803
- return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot, builder };
9006
+ const blend = blendOverlay(state);
9007
+ if (state.tool === "blend" && !it) status = state.blendStart ? "Click another shape to blend into" : "Click a shape to blend from";
9008
+ return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot, builder, blend };
9009
+ }
9010
+ function selectedBlends(state) {
9011
+ const out = /* @__PURE__ */ new Map();
9012
+ for (const id of state.selection) {
9013
+ const node = findVectorNode(state.document.nodes, id);
9014
+ const group = isVectorBlendGroup(node) ? node : _optionalChain([locateVectorNode, 'call', _300 => _300(state.document.nodes, id), 'optionalAccess', _301 => _301.parent]);
9015
+ if (isVectorBlendGroup(group)) out.set(group.id, group);
9016
+ }
9017
+ return [...out.values()];
9018
+ }
9019
+ function blendOverlay(state) {
9020
+ const v2 = state.viewport;
9021
+ const view = [v2.zoom, 0, 0, v2.zoom, v2.x, v2.y];
9022
+ const doc = state.document;
9023
+ const sc = (p, m) => {
9024
+ const q = { x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] };
9025
+ return { x: fmt(q.x), y: fmt(q.y) };
9026
+ };
9027
+ const spines = selectedBlends(state).flatMap((g) => {
9028
+ const s = vectorBlendSpine(g);
9029
+ if (!s) return [];
9030
+ const m = multiplyMatrices(_nullishCoalesce(vectorWorldMatrix(doc.nodes, g.id), () => ( IDENTITY_MATRIX)), view);
9031
+ const d = _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, _chunkVNYRI265cjs.transformBezierPath.call(void 0, subpathsToBezier([s.spine], { corners: false }), m), { precision: 2 });
9032
+ const keys = s.stops.map((stop) => {
9033
+ const p = vectorBlendRailAt(s.rail, stop).point;
9034
+ return sc({ x: p[0], y: p[1] }, m);
9035
+ });
9036
+ return [{ id: g.id, d, keys, replaced: s.replaced }];
9037
+ });
9038
+ let start = null;
9039
+ const anchors = [];
9040
+ if (state.tool === "blend") {
9041
+ const from = state.blendStart ? findVectorNode(doc.nodes, state.blendStart.nodeId) : void 0;
9042
+ if (from && state.blendStart) {
9043
+ const a = state.blendStart.anchor === null ? void 0 : getAnchorWorld(doc, { nodeId: from.id, subpath: 0, index: state.blendStart.anchor });
9044
+ start = { d: screenOutline(state, from), anchor: a ? sc(a, view) : null };
9045
+ }
9046
+ const hovered = _optionalChain([state, 'access', _302 => _302.hover, 'optionalAccess', _303 => _303.nodeId]) ? findVectorNode(doc.nodes, state.hover.nodeId) : void 0;
9047
+ if (_optionalChain([hovered, 'optionalAccess', _304 => _304.type]) === "path" && hovered.subpaths[0]) {
9048
+ hovered.subpaths[0].anchors.forEach((_, index) => {
9049
+ const a = getAnchorWorld(doc, { nodeId: hovered.id, subpath: 0, index });
9050
+ if (a) anchors.push({ ...sc(a, view), hovered: _optionalChain([state, 'access', _305 => _305.hover, 'optionalAccess', _306 => _306.anchor, 'optionalAccess', _307 => _307.index]) === index });
9051
+ });
9052
+ }
9053
+ }
9054
+ return { spines, start, anchors };
9055
+ }
9056
+ function getVectorBlendControls(state) {
9057
+ const blends = selectedBlends(state);
9058
+ const doc = state.document;
9059
+ const roots = state.selection.filter((id) => {
9060
+ const node = findVectorNode(doc.nodes, id);
9061
+ return !!node && !node.blendStep && !node.locked && !state.selection.includes(_nullishCoalesce(_optionalChain([locateVectorNode, 'call', _308 => _308(doc.nodes, id), 'optionalAccess', _309 => _309.parent, 'optionalAccess', _310 => _310.id]), () => ( "")));
9062
+ });
9063
+ const parents = new Set(roots.map((id) => _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _311 => _311(doc.nodes, id), 'optionalAccess', _312 => _312.parent, 'optionalAccess', _313 => _313.id]), () => ( null))));
9064
+ const paths = roots.filter((id) => _optionalChain([findVectorNode, 'call', _314 => _314(doc.nodes, id), 'optionalAccess', _315 => _315.type]) === "path" && !isVectorBlendGroup(_optionalChain([locateVectorNode, 'call', _316 => _316(doc.nodes, id), 'optionalAccess', _317 => _317.parent])));
9065
+ const first = blends[0];
9066
+ return {
9067
+ ids: blends.map((g) => g.id),
9068
+ options: resolveVectorBlendOptions(first ? first.blend : state.blendDefaults),
9069
+ canBlend: roots.length >= 2 && parents.size === 1,
9070
+ canReplaceSpine: blends.length === 1 && paths.length === 1,
9071
+ hasSpine: !!_optionalChain([first, 'optionalAccess', _318 => _318.blend, 'optionalAccess', _319 => _319.spine])
9072
+ };
7804
9073
  }
9074
+ function applyVectorBlendOptions(controller, patch) {
9075
+ controller.setBlendDefaults(patch);
9076
+ if (getVectorBlendControls(controller.getState()).ids.length) controller.setBlendOptions(patch);
9077
+ }
9078
+ var VECTOR_BLEND_SPACING_LABELS = { steps: "Specified Steps", distance: "Specified Distance", smoothColor: "Smooth Color" };
7805
9079
  var VECTOR_OVERLAY_THEME = {
7806
9080
  accent: "#0ea5e9",
7807
9081
  /** Smart guide lines and labels. */
@@ -7847,7 +9121,7 @@ function isVectorTextTarget(target) {
7847
9121
  if (!el || typeof el !== "object") return false;
7848
9122
  const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
7849
9123
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
7850
- return !!el.isContentEditable || _optionalChain([el, 'access', _262 => _262.getAttribute, 'optionalCall', _263 => _263("contenteditable")]) === "true";
9124
+ return !!el.isContentEditable || _optionalChain([el, 'access', _320 => _320.getAttribute, 'optionalCall', _321 => _321("contenteditable")]) === "true";
7851
9125
  }
7852
9126
  function getVectorSelectionStyle(state) {
7853
9127
  const paths = [];
@@ -7855,12 +9129,12 @@ function getVectorSelectionStyle(state) {
7855
9129
  const node = findVectorNode(state.document.nodes, id);
7856
9130
  if (node) paths.push(...collect(node));
7857
9131
  }
7858
- const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _264 => _264.selectedAnchors, 'optionalAccess', _265 => _265.length]);
9132
+ const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _322 => _322.selectedAnchors, 'optionalAccess', _323 => _323.length]);
7859
9133
  if (anchorMode) {
7860
9134
  for (const r of state.selectedAnchors) {
7861
9135
  if (paths.some((p) => p.id === r.nodeId)) continue;
7862
9136
  const node = findVectorNode(state.document.nodes, r.nodeId);
7863
- if (_optionalChain([node, 'optionalAccess', _266 => _266.type]) === "path") paths.push(node);
9137
+ if (_optionalChain([node, 'optionalAccess', _324 => _324.type]) === "path") paths.push(node);
7864
9138
  }
7865
9139
  }
7866
9140
  if (!paths.length) return null;
@@ -7871,7 +9145,7 @@ function getVectorSelectionStyle(state) {
7871
9145
  for (const c of vectorCorners(p.subpaths)) {
7872
9146
  if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
7873
9147
  const corner = p.subpaths[c.subpath].anchors[c.index].corner;
7874
- corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _267 => _267.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _268 => _268.kind]), () => ( "round")) });
9148
+ corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _325 => _325.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _326 => _326.kind]), () => ( "round")) });
7875
9149
  }
7876
9150
  }
7877
9151
  const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
@@ -7986,6 +9260,18 @@ var WIDTH_POINTS = {
7986
9260
  },
7987
9261
  description: "Variable-width profile points, interpolated smoothly (monotone cubic); two points at one `t` make a step. `null` or `[]` strokes at a uniform width."
7988
9262
  };
9263
+ var BLEND_PROPS = {
9264
+ spacing: { type: "string", enum: ["steps", "distance", "smoothColor"], description: "How in-betweens are spaced (default steps)." },
9265
+ steps: { type: "integer", minimum: 1, maximum: 1e3, description: "In-betweens between each pair of keys (steps spacing; default 5)." },
9266
+ distance: { type: "number", exclusiveMinimum: 0, description: "Distance between in-betweens along the spine (distance spacing; default 20)." },
9267
+ orientToSpine: { type: "boolean", description: "Turn the in-betweens with a replaced spine." },
9268
+ linear: { type: "boolean", description: "Move anchors in straight lines instead of the rotation-aware morph." }
9269
+ };
9270
+ var checkBlendOptions = (params) => {
9271
+ if (_optionalChain([params, 'optionalAccess', _327 => _327.spacing]) !== void 0 && !VECTOR_BLEND_SPACINGS.includes(params.spacing)) fail(`Unknown blend spacing "${params.spacing}".`);
9272
+ if (_optionalChain([params, 'optionalAccess', _328 => _328.steps]) !== void 0 && !(Number.isFinite(params.steps) && params.steps >= 1)) fail("steps must be at least 1.");
9273
+ if (_optionalChain([params, 'optionalAccess', _329 => _329.distance]) !== void 0 && !(Number.isFinite(params.distance) && params.distance > 0)) fail("distance must be positive.");
9274
+ };
7989
9275
  var PAINT = {
7990
9276
  description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear", angle, stops: [{ offset, color }] }`.'
7991
9277
  };
@@ -8019,17 +9305,17 @@ function createVectorCommands(controller, options = {}) {
8019
9305
  const base = { group, ...options.scope ? { scope: options.scope } : {} };
8020
9306
  const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
8021
9307
  const ids = (params) => {
8022
- const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _269 => _269.ids]), () => ( controller.getState().selection));
9308
+ const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _330 => _330.ids]), () => ( controller.getState().selection));
8023
9309
  for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
8024
9310
  if (!list2.length) fail("Nothing is selected; pass ids.");
8025
9311
  return list2;
8026
9312
  };
8027
- const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _270 => _270.ids, 'optionalAccess', _271 => _271.length]) ? true : hasSelection();
9313
+ const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _331 => _331.ids, 'optionalAccess', _332 => _332.length]) ? true : hasSelection();
8028
9314
  const anchorsOf = (refs) => {
8029
9315
  if (!refs) return [...controller.getState().selectedAnchors];
8030
9316
  for (const r of refs) {
8031
9317
  const node = controller.getNode(r.nodeId);
8032
- if (_optionalChain([node, 'optionalAccess', _272 => _272.type]) !== "path" || !_optionalChain([node, 'access', _273 => _273.subpaths, 'access', _274 => _274[r.subpath], 'optionalAccess', _275 => _275.anchors, 'access', _276 => _276[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
9318
+ if (_optionalChain([node, 'optionalAccess', _333 => _333.type]) !== "path" || !_optionalChain([node, 'access', _334 => _334.subpaths, 'access', _335 => _335[r.subpath], 'optionalAccess', _336 => _336.anchors, 'access', _337 => _337[r.index]])) fail(`There is no anchor ${r.index} on subpath ${r.subpath} of "${r.nodeId}".`);
8033
9319
  }
8034
9320
  return refs;
8035
9321
  };
@@ -8050,7 +9336,9 @@ function createVectorCommands(controller, options = {}) {
8050
9336
  visible: n.visible !== false,
8051
9337
  locked: !!n.locked,
8052
9338
  selected: state.selection.includes(n.id),
8053
- ...n.type === "path" ? { fill: _nullishCoalesce(n.fill, () => ( null)), stroke: _nullishCoalesce(n.stroke, () => ( null)), strokeWidth: _nullishCoalesce(n.strokeWidth, () => ( 1)), d: vectorPathData(n, 2) } : { children: n.children.length }
9339
+ ...n.type === "path" ? { fill: _nullishCoalesce(n.fill, () => ( null)), stroke: _nullishCoalesce(n.stroke, () => ( null)), strokeWidth: _nullishCoalesce(n.strokeWidth, () => ( 1)), d: vectorPathData(n, 2) } : { children: n.children.length },
9340
+ ...n.type === "group" && n.blend ? { blend: { ...resolveVectorBlendOptions(n.blend), spine: !!n.blend.spine, keys: vectorBlendKeys(n).map((k) => k.id) } } : {},
9341
+ ...n.blendStep ? { blendStep: true } : {}
8054
9342
  });
8055
9343
  if (n.type === "group") visit(n.children, depth + 1);
8056
9344
  }
@@ -8120,7 +9408,7 @@ function createVectorCommands(controller, options = {}) {
8120
9408
  run: (_ctx, params) => {
8121
9409
  const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
8122
9410
  const node = controller.getNode(id);
8123
- if (_optionalChain([node, 'optionalAccess', _277 => _277.type]) === "path" && !node.subpaths.length) {
9411
+ if (_optionalChain([node, 'optionalAccess', _338 => _338.type]) === "path" && !node.subpaths.length) {
8124
9412
  controller.undo();
8125
9413
  fail("The path data has no points.");
8126
9414
  }
@@ -8152,10 +9440,10 @@ function createVectorCommands(controller, options = {}) {
8152
9440
  params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
8153
9441
  undoable: true,
8154
9442
  run: (_ctx, params) => {
8155
- const refs = _optionalChain([params, 'optionalAccess', _278 => _278.anchors]) ? anchorsOf(params.anchors) : void 0;
8156
- const target = { refs, ids: _optionalChain([params, 'optionalAccess', _279 => _279.ids]) ? ids(params) : void 0 };
8157
- if (_optionalChain([params, 'optionalAccess', _280 => _280.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
8158
- else if (_optionalChain([params, 'optionalAccess', _281 => _281.kind])) check(controller.setCornerKind(params.kind, target));
9443
+ const refs = _optionalChain([params, 'optionalAccess', _339 => _339.anchors]) ? anchorsOf(params.anchors) : void 0;
9444
+ const target = { refs, ids: _optionalChain([params, 'optionalAccess', _340 => _340.ids]) ? ids(params) : void 0 };
9445
+ if (_optionalChain([params, 'optionalAccess', _341 => _341.radius]) !== void 0) check(controller.setCornerRadius(params.radius, { ...target, kind: params.kind }));
9446
+ else if (_optionalChain([params, 'optionalAccess', _342 => _342.kind])) check(controller.setCornerKind(params.kind, target));
8159
9447
  else fail("Pass `radius` and / or `kind`.");
8160
9448
  return { ok: true };
8161
9449
  }
@@ -8190,12 +9478,12 @@ function createVectorCommands(controller, options = {}) {
8190
9478
  undoable: true,
8191
9479
  run: (_ctx, params) => {
8192
9480
  const target = { ids: ids(params) };
8193
- if (_optionalChain([params, 'optionalAccess', _282 => _282.preset])) check(controller.setWidthProfile(params.preset, target));
8194
- else if (_optionalChain([params, 'optionalAccess', _283 => _283.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
8195
- else if (_optionalChain([params, 'optionalAccess', _284 => _284.taper]) || _optionalChain([params, 'optionalAccess', _285 => _285.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
9481
+ if (_optionalChain([params, 'optionalAccess', _343 => _343.preset])) check(controller.setWidthProfile(params.preset, target));
9482
+ else if (_optionalChain([params, 'optionalAccess', _344 => _344.points]) !== void 0) check(controller.setWidthProfile(_nullishCoalesce(params.points, () => ( null)), target));
9483
+ else if (_optionalChain([params, 'optionalAccess', _345 => _345.taper]) || _optionalChain([params, 'optionalAccess', _346 => _346.wave])) check(controller.setWidthProfile({ taper: params.taper, wave: params.wave }, target));
8196
9484
  else fail("Pass `preset`, `points`, `taper` or `wave`.");
8197
9485
  const node = controller.getNode(target.ids[0]);
8198
- return { ok: true, points: _optionalChain([node, 'optionalAccess', _286 => _286.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
9486
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _347 => _347.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
8199
9487
  }
8200
9488
  },
8201
9489
  {
@@ -8217,10 +9505,10 @@ function createVectorCommands(controller, options = {}) {
8217
9505
  },
8218
9506
  undoable: true,
8219
9507
  run: (_ctx, params) => {
8220
- if (!_optionalChain([params, 'optionalAccess', _287 => _287.id])) fail("Pass the path `id`.");
9508
+ if (!_optionalChain([params, 'optionalAccess', _348 => _348.id])) fail("Pass the path `id`.");
8221
9509
  check(controller.setWidthPoint({ nodeId: params.id, index: _nullishCoalesce(params.index, () => ( null)) }, { t: params.t, left: params.left, right: params.right }));
8222
9510
  const node = controller.getNode(params.id);
8223
- return { ok: true, points: _optionalChain([node, 'optionalAccess', _288 => _288.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
9511
+ return { ok: true, points: _optionalChain([node, 'optionalAccess', _349 => _349.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
8224
9512
  }
8225
9513
  },
8226
9514
  {
@@ -8233,7 +9521,7 @@ function createVectorCommands(controller, options = {}) {
8233
9521
  enabled: enabledFor,
8234
9522
  undoable: true,
8235
9523
  run: (_ctx, params) => {
8236
- const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _289 => _289.ids]) ? ids(params) : void 0 });
9524
+ const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _350 => _350.ids]) ? ids(params) : void 0 });
8237
9525
  if (!created.length) fail("No selected path has a stroke.");
8238
9526
  return { ids: created };
8239
9527
  }
@@ -8295,6 +9583,86 @@ function createVectorCommands(controller, options = {}) {
8295
9583
  return { id };
8296
9584
  }
8297
9585
  },
9586
+ {
9587
+ ...base,
9588
+ id: `${prefix}blend`,
9589
+ label: "Make Blend\u2026",
9590
+ keywords: ["blend", "morph", "tween", "interpolate", "in-between", "steps", "transition"],
9591
+ description: "Blends two or more paths or groups (sharing a parent; bottom to top, or in the order given with keepOrder) into a live blend group: the in-betweens morph shape (anchors matched by arc length, the start and winding that twist least, rotation-aware so turning shapes turn) and paint (OKLab colours, gradients, stroke width, opacity, width profiles), and regenerate whenever a key changes. Spacing: `steps` (count between each pair of keys), `distance` (along the spine) or `smoothColor`. `starts` maps key ids to the anchor of their first subpath the blend starts from. Returns the blend id.",
9592
+ params: {
9593
+ type: "object",
9594
+ properties: {
9595
+ ids: IDS,
9596
+ ...BLEND_PROPS,
9597
+ keepOrder: { type: "boolean", description: "Blend in the order of `ids` instead of stacking order." },
9598
+ starts: { type: "object", additionalProperties: { type: "integer", minimum: 0 }, description: "Start anchor per key id." }
9599
+ }
9600
+ },
9601
+ undoable: true,
9602
+ run: (_ctx, params) => {
9603
+ checkBlendOptions(params);
9604
+ const id = controller.blend({ ...params, ids: ids(params) });
9605
+ if (!id) fail("Select two or more shapes that share a parent.");
9606
+ return { id };
9607
+ }
9608
+ },
9609
+ {
9610
+ ...base,
9611
+ id: `${prefix}blendOptions`,
9612
+ label: "Blend Options\u2026",
9613
+ keywords: ["blend", "steps", "spacing", "distance", "smooth color", "orientation", "align to path"],
9614
+ description: "Changes the spacing (steps, distance or smoothColor), step count, distance, orient-to-spine and linear morph of the given (or selected) blends; their steps regenerate.",
9615
+ params: { type: "object", properties: { ids: IDS, ...BLEND_PROPS } },
9616
+ undoable: true,
9617
+ run: (_ctx, params) => {
9618
+ checkBlendOptions(params);
9619
+ const r = controller.setBlendOptions(_nullishCoalesce(params, () => ( {})), { ids: _optionalChain([params, 'optionalAccess', _351 => _351.ids]) });
9620
+ if (!r.ok) fail(r.message);
9621
+ return { ok: true };
9622
+ }
9623
+ },
9624
+ {
9625
+ ...base,
9626
+ id: `${prefix}expandBlend`,
9627
+ label: "Expand Blend",
9628
+ keywords: ["blend", "expand", "flatten", "bake"],
9629
+ description: "Expands the given (or selected) blends into plain groups of their keys and in-betweens (editable paths with fresh ids). Returns the group ids.",
9630
+ params: { type: "object", properties: { ids: IDS } },
9631
+ undoable: true,
9632
+ run: (_ctx, params) => {
9633
+ const groups = controller.expandBlend({ ids: _optionalChain([params, 'optionalAccess', _352 => _352.ids]) });
9634
+ if (!groups.length) fail("Select a blend to expand.");
9635
+ return { ids: groups };
9636
+ }
9637
+ },
9638
+ {
9639
+ ...base,
9640
+ id: `${prefix}releaseBlend`,
9641
+ label: "Release Blend",
9642
+ keywords: ["blend", "release", "undo blend"],
9643
+ description: "Releases the given (or selected) blends: the in-betweens go and the keys (plus a replaced spine, as an unpainted path) take the blend's place. Returns their ids.",
9644
+ params: { type: "object", properties: { ids: IDS } },
9645
+ undoable: true,
9646
+ run: (_ctx, params) => {
9647
+ const released = controller.releaseBlend({ ids: _optionalChain([params, 'optionalAccess', _353 => _353.ids]) });
9648
+ if (!released.length) fail("Select a blend to release.");
9649
+ return { ids: released };
9650
+ }
9651
+ },
9652
+ {
9653
+ ...base,
9654
+ id: `${prefix}replaceSpine`,
9655
+ label: "Replace Spine",
9656
+ keywords: ["blend", "spine", "path", "follow", "along path"],
9657
+ description: "With one blend and one path given (or selected): the path becomes the blend's spine (it is removed) and the keys move onto it, spread evenly by arc length; the in-betweens follow it.",
9658
+ params: { type: "object", properties: { ids: IDS } },
9659
+ undoable: true,
9660
+ run: (_ctx, params) => {
9661
+ const r = controller.replaceBlendSpine({ ids: _optionalChain([params, 'optionalAccess', _354 => _354.ids]) });
9662
+ if (!r.ok) fail(r.message);
9663
+ return { ok: true };
9664
+ }
9665
+ },
8298
9666
  {
8299
9667
  ...base,
8300
9668
  id: `${prefix}pathfinder`,
@@ -8404,7 +9772,7 @@ function createVectorCommands(controller, options = {}) {
8404
9772
  enabled: enabledFor,
8405
9773
  undoable: true,
8406
9774
  run: (_ctx, params) => {
8407
- const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _290 => _290.name]) });
9775
+ const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _355 => _355.name]) });
8408
9776
  if (!id) fail("Only nodes that share a parent can be grouped.");
8409
9777
  return { id };
8410
9778
  }
@@ -8432,7 +9800,7 @@ function createVectorCommands(controller, options = {}) {
8432
9800
  params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
8433
9801
  undoable: true,
8434
9802
  run: (_ctx, params) => {
8435
- check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _291 => _291.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _292 => _292.keepShape]) }));
9803
+ check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _356 => _356.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _357 => _357.keepShape]) }));
8436
9804
  return { ok: true };
8437
9805
  }
8438
9806
  },
@@ -8487,7 +9855,7 @@ function createVectorCommands(controller, options = {}) {
8487
9855
  params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
8488
9856
  undoable: true,
8489
9857
  run: (_ctx, params) => {
8490
- check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _293 => _293.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _294 => _294.tolerance]) }));
9858
+ check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _358 => _358.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _359 => _359.tolerance]) }));
8491
9859
  return { selection: controller.getState().selection };
8492
9860
  }
8493
9861
  },
@@ -8501,7 +9869,7 @@ function createVectorCommands(controller, options = {}) {
8501
9869
  params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
8502
9870
  undoable: true,
8503
9871
  run: (_ctx, params) => {
8504
- check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _295 => _295.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _296 => _296.anchors]))));
9872
+ check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _360 => _360.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _361 => _361.anchors]))));
8505
9873
  return { ok: true };
8506
9874
  }
8507
9875
  },
@@ -8515,7 +9883,7 @@ function createVectorCommands(controller, options = {}) {
8515
9883
  enabled: enabledFor,
8516
9884
  undoable: true,
8517
9885
  run: (_ctx, params) => {
8518
- check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _297 => _297.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _298 => _298.tolerance]) }));
9886
+ check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _362 => _362.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _363 => _363.tolerance]) }));
8519
9887
  return { ok: true };
8520
9888
  }
8521
9889
  },
@@ -8528,7 +9896,7 @@ function createVectorCommands(controller, options = {}) {
8528
9896
  params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
8529
9897
  undoable: true,
8530
9898
  run: (_ctx, params) => {
8531
- check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _299 => _299.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _300 => _300.amount]), refs: _optionalChain([params, 'optionalAccess', _301 => _301.anchors]) }));
9899
+ check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess', _364 => _364.ids]) ? ids(params) : void 0, amount: _optionalChain([params, 'optionalAccess', _365 => _365.amount]), refs: _optionalChain([params, 'optionalAccess', _366 => _366.anchors]) }));
8532
9900
  return { ok: true };
8533
9901
  }
8534
9902
  },
@@ -8541,14 +9909,14 @@ function createVectorCommands(controller, options = {}) {
8541
9909
  params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
8542
9910
  undoable: true,
8543
9911
  run: (_ctx, params) => {
8544
- if (_optionalChain([params, 'optionalAccess', _302 => _302.nodeId]) !== void 0) {
9912
+ if (_optionalChain([params, 'optionalAccess', _367 => _367.nodeId]) !== void 0) {
8545
9913
  if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
8546
9914
  if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
8547
9915
  const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
8548
9916
  if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
8549
9917
  return { ids: created };
8550
9918
  }
8551
- check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _303 => _303.anchors]))));
9919
+ check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _368 => _368.anchors]))));
8552
9920
  return { ids: controller.getState().selection };
8553
9921
  }
8554
9922
  },
@@ -8599,7 +9967,7 @@ function createVectorCommands(controller, options = {}) {
8599
9967
  },
8600
9968
  run: (_ctx, params) => {
8601
9969
  const patch = {};
8602
- for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _304 => _304[key]]) !== void 0) patch[key] = params[key];
9970
+ for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _369 => _369[key]]) !== void 0) patch[key] = params[key];
8603
9971
  controller.setEditing(patch);
8604
9972
  return { editing: controller.getState().editing };
8605
9973
  }
@@ -8624,7 +9992,7 @@ function createVectorCommands(controller, options = {}) {
8624
9992
  keywords: ["download", "save", "markup"],
8625
9993
  description: "Returns the document as SVG markup.",
8626
9994
  params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
8627
- run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _305 => _305.precision]) }) })
9995
+ run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _370 => _370.precision]) }) })
8628
9996
  },
8629
9997
  {
8630
9998
  ...base,
@@ -8838,5 +10206,43 @@ function createVectorCommands(controller, options = {}) {
8838
10206
 
8839
10207
 
8840
10208
 
8841
- 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_BUILDER_HATCH_GAP = VECTOR_BUILDER_HATCH_GAP; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_FILL_GAP = VECTOR_FILL_GAP; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATHFINDER_ACTIONS = VECTOR_PATHFINDER_ACTIONS; exports.VECTOR_PATHFINDER_LABELS = VECTOR_PATHFINDER_LABELS; exports.VECTOR_PATHFINDER_OPERATIONS = VECTOR_PATHFINDER_OPERATIONS; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.addBuilderHit = addBuilderHit; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorCornersAttribute = applyVectorCornersAttribute; exports.arrowheadInset = arrowheadInset; exports.arrowheadPath = arrowheadPath; exports.arrowheadWeight = arrowheadWeight; exports.bendSegment = bendSegment; exports.bezierToSubpaths = bezierToSubpaths; exports.boundsFromPoints = boundsFromPoints; exports.boundsIntersect = boundsIntersect; exports.cloneAnchor = cloneAnchor; exports.cloneSubpath = cloneSubpath; exports.cloneVectorDocument = cloneVectorDocument; exports.cloneVectorNode = cloneVectorNode; exports.collectPathNodes = collectPathNodes; exports.constrainToLeave = constrainToLeave; exports.convertAnchor = convertAnchor; exports.cornerBisectorPoint = cornerBisectorPoint; exports.cornerRadiusChange = cornerRadiusChange; exports.cornerSetback = cornerSetback; exports.createVectorCommands = createVectorCommands; exports.curvatureMoveAnchor = curvatureMoveAnchor; exports.cutSubpathAt = cutSubpathAt; exports.dashIntervals = dashIntervals; exports.decomposeRigid = decomposeRigid; exports.effectiveStrokeAlign = effectiveStrokeAlign; exports.ellipseSubpath = ellipseSubpath; exports.eraseFromFill = eraseFromFill; exports.eraseFromOutline = eraseFromOutline; exports.eraserRegion = eraserRegion; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorCornerWidgets = getVectorCornerWidgets; exports.getVectorOverlayRenderData = getVectorOverlayRenderData; exports.getVectorSceneRenderData = getVectorSceneRenderData; exports.getVectorSelectionBoxGeometry = getVectorSelectionBoxGeometry; exports.getVectorSelectionFrame = getVectorSelectionFrame; exports.getVectorSelectionStyle = getVectorSelectionStyle; exports.getVectorWidthHandles = getVectorWidthHandles; exports.getVisibleAnchors = getVisibleAnchors; exports.getVisibleHandles = getVisibleHandles; exports.hasPaint = hasPaint; exports.hasStrokeExtras = hasStrokeExtras; exports.hasVectorCorners = hasVectorCorners; exports.hitTestVectorNode = hitTestVectorNode; exports.hitTestVectorNodes = hitTestVectorNodes; exports.hitTestVectorStroke = hitTestVectorStroke; exports.inferAnchorType = inferAnchorType; exports.insertAnchor = insertAnchor; exports.insertWidthPoint = insertWidthPoint; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mirrorWidthProfile = mirrorWidthProfile; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.planShapeBuilder = planShapeBuilder; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeWidthPoints = removeWidthPoints; exports.reverseSubpath = reverseSubpath; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.samplePolyline = samplePolyline; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathEnclosesArea = subpathEnclosesArea; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathsToBezier = subpathsToBezier; exports.taperWaveFactor = taperWaveFactor; exports.toVectorKeyInput = toVectorKeyInput; exports.toVectorModifiers = toVectorModifiers; exports.toVectorPointerInput = toVectorPointerInput; exports.transformVectorNode = transformVectorNode; exports.translateAnchor = translateAnchor; exports.translateMatrix = translateMatrix; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorBuilderErasedEdges = vectorBuilderErasedEdges; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorFillMap = vectorFillMap; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathEntries = vectorPathEntries; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorPathfinderShape = vectorPathfinderShape; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorResultStyle = vectorResultStyle; exports.vectorShapeBuilderHit = vectorShapeBuilderHit; exports.vectorShapeBuilderMap = vectorShapeBuilderMap; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute;
10209
+
10210
+
10211
+
10212
+
10213
+
10214
+
10215
+
10216
+
10217
+
10218
+
10219
+
10220
+
10221
+
10222
+
10223
+
10224
+
10225
+
10226
+
10227
+
10228
+
10229
+
10230
+
10231
+
10232
+
10233
+
10234
+
10235
+
10236
+
10237
+
10238
+
10239
+
10240
+
10241
+
10242
+
10243
+
10244
+
10245
+
10246
+
10247
+ exports.DEFAULT_VECTOR_BLEND_DISTANCE = DEFAULT_VECTOR_BLEND_DISTANCE; exports.DEFAULT_VECTOR_BLEND_STEPS = DEFAULT_VECTOR_BLEND_STEPS; 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_BLEND_MAX_SMOOTH_STEPS = VECTOR_BLEND_MAX_SMOOTH_STEPS; exports.VECTOR_BLEND_MAX_STEPS = VECTOR_BLEND_MAX_STEPS; exports.VECTOR_BLEND_SPACINGS = VECTOR_BLEND_SPACINGS; exports.VECTOR_BLEND_SPACING_LABELS = VECTOR_BLEND_SPACING_LABELS; exports.VECTOR_BUILDER_HATCH_GAP = VECTOR_BUILDER_HATCH_GAP; exports.VECTOR_CLIPBOARD_TYPE = VECTOR_CLIPBOARD_TYPE; exports.VECTOR_CONSTRUCTION_ANGLES = VECTOR_CONSTRUCTION_ANGLES; exports.VECTOR_CORNER_KINDS = VECTOR_CORNER_KINDS; exports.VECTOR_CORNER_KIND_LABELS = VECTOR_CORNER_KIND_LABELS; exports.VECTOR_CORNER_WIDGET_INSET = VECTOR_CORNER_WIDGET_INSET; exports.VECTOR_EDIT_TOGGLES = VECTOR_EDIT_TOGGLES; exports.VECTOR_FILL_GAP = VECTOR_FILL_GAP; exports.VECTOR_INK_MAX_CORNERS = VECTOR_INK_MAX_CORNERS; exports.VECTOR_OVERLAY_THEME = VECTOR_OVERLAY_THEME; exports.VECTOR_PATHFINDER_ACTIONS = VECTOR_PATHFINDER_ACTIONS; exports.VECTOR_PATHFINDER_LABELS = VECTOR_PATHFINDER_LABELS; exports.VECTOR_PATHFINDER_OPERATIONS = VECTOR_PATHFINDER_OPERATIONS; exports.VECTOR_PATH_ACTIONS = VECTOR_PATH_ACTIONS; exports.VECTOR_PENCIL_STRING_PX = VECTOR_PENCIL_STRING_PX; exports.VECTOR_PRESSURE_NOMINAL = VECTOR_PRESSURE_NOMINAL; exports.VECTOR_ROTATE_HANDLE_OFFSET = VECTOR_ROTATE_HANDLE_OFFSET; exports.VECTOR_STROKE_ALIGN_LABELS = VECTOR_STROKE_ALIGN_LABELS; exports.VECTOR_STYLE_KEYS = VECTOR_STYLE_KEYS; exports.VECTOR_TOOLS = VECTOR_TOOLS; exports.VECTOR_TOOL_GLYPHS = VECTOR_TOOL_GLYPHS; exports.VECTOR_TOOL_SHORTCUTS = VECTOR_TOOL_SHORTCUTS; exports.VECTOR_TRANSFORM_HANDLES = VECTOR_TRANSFORM_HANDLES; exports.VECTOR_WIDTH_PRESETS = VECTOR_WIDTH_PRESETS; exports.VECTOR_WIDTH_PRESET_IDS = VECTOR_WIDTH_PRESET_IDS; exports.VECTOR_WIDTH_PROFILE_MAX_POINTS = VECTOR_WIDTH_PROFILE_MAX_POINTS; exports.VECTOR_WIDTH_PROFILE_MAX_SCALE = VECTOR_WIDTH_PROFILE_MAX_SCALE; exports.VECTOR_WIDTH_SAME_T = VECTOR_WIDTH_SAME_T; exports.VectorEditorController = VectorEditorController; exports.VectorSnapTargets = VectorSnapTargets; exports.addBuilderHit = addBuilderHit; exports.applyMatrix = applyMatrix; exports.applyMatrixLinear = applyMatrixLinear; exports.applyVectorBlendOptions = applyVectorBlendOptions; 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.correspondMorphContours = correspondMorphContours; exports.createVectorCommands = createVectorCommands; exports.createVectorMorph = createVectorMorph; 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.evaluateVectorPathKeyframes = evaluateVectorPathKeyframes; exports.expandVectorBlend = expandVectorBlend; exports.expandVectorCorners = expandVectorCorners; exports.expandVectorCornersOf = expandVectorCornersOf; exports.findVectorNode = findVectorNode; exports.fitCornerRadius = fitCornerRadius; exports.generateVectorBlendChildren = generateVectorBlendChildren; exports.generateWidthProfile = generateWidthProfile; exports.getAnchor = getAnchor; exports.getAnchorWorld = getAnchorWorld; exports.getEditablePathIds = getEditablePathIds; exports.getTransformHandlePositions = getTransformHandlePositions; exports.getVectorBlendControls = getVectorBlendControls; 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.interpolateMorphContours = interpolateMorphContours; exports.interpolateRigidMatrix = interpolateRigidMatrix; exports.interpolateVectorColor = interpolateVectorColor; exports.interpolateVectorPaint = interpolateVectorPaint; exports.interpolateVectorPaths = interpolateVectorPaths; exports.interpolateVectorStyle = interpolateVectorStyle; exports.interpolateWidthProfiles = interpolateWidthProfiles; exports.invertMatrix = invertMatrix; exports.isClosedBezierShape = isClosedBezierShape; exports.isIdentityMatrix = isIdentityMatrix; exports.isUniformWidthProfile = isUniformWidthProfile; exports.isVectorBlendGroup = isVectorBlendGroup; exports.isVectorTextTarget = isVectorTextTarget; exports.joinSubpaths = joinSubpaths; exports.knifePath = knifePath; exports.lineSubpath = lineSubpath; exports.locateVectorNode = locateVectorNode; exports.matrixRotation = matrixRotation; exports.mirrorWidthProfile = mirrorWidthProfile; exports.morphContourToSubpath = morphContourToSubpath; exports.morphPathsCross = morphPathsCross; exports.moveAnchorHandle = moveAnchorHandle; exports.multiplyMatrices = multiplyMatrices; exports.nearestSegment = nearestSegment; exports.nearestStrokePosition = nearestStrokePosition; exports.nextVectorCornerKind = nextVectorCornerKind; exports.nodeMatrix = nodeMatrix; exports.parseSvgTransform = parseSvgTransform; exports.parseVectorSvg = parseVectorSvg; exports.planShapeBuilder = planShapeBuilder; exports.polygonSubpath = polygonSubpath; exports.polylineBounds = polylineBounds; exports.pressureWidthProfile = pressureWidthProfile; exports.recognizeInkShape = recognizeInkShape; exports.rectSubpath = rectSubpath; exports.refineMorphContour = refineMorphContour; exports.refreshVectorBlends = refreshVectorBlends; exports.releaseVectorBlend = releaseVectorBlend; exports.removeAnchorKeepingShape = removeAnchorKeepingShape; exports.removeAnchors = removeAnchors; exports.removeAnchorsKeepingShape = removeAnchorsKeepingShape; exports.removeRedundantPoints = removeRedundantPoints; exports.removeWidthPoints = removeWidthPoints; exports.resampleGradientStops = resampleGradientStops; exports.resolveVectorBlendOptions = resolveVectorBlendOptions; exports.reverseMorphContour = reverseMorphContour; exports.reverseSubpath = reverseSubpath; exports.reverseWidthProfile = reverseWidthProfile; exports.rotateMatrix = rotateMatrix; exports.roundSubpaths = roundSubpaths; exports.samplePolyline = samplePolyline; exports.sanitizeWidthProfile = sanitizeWidthProfile; exports.scaleFor = scaleFor; exports.scaleMatrix = scaleMatrix; exports.setAnchorCorner = setAnchorCorner; exports.setSubpathCorners = setSubpathCorners; exports.setWidthPoint = setWidthPoint; exports.simplifyPolyline = simplifyPolyline; exports.smoothSubpath = smoothSubpath; exports.splitIntoFaces = splitIntoFaces; exports.splitSubpathAt = splitSubpathAt; exports.starSubpath = starSubpath; exports.strokePositionPoint = strokePositionPoint; exports.strokeWidthProfileOf = strokeWidthProfileOf; exports.subpathEnclosesArea = subpathEnclosesArea; exports.subpathPointAt = subpathPointAt; exports.subpathSegment = subpathSegment; exports.subpathSegmentCount = subpathSegmentCount; exports.subpathToMorphContour = subpathToMorphContour; 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.turnMorphContour = turnMorphContour; exports.unionBounds = unionBounds; exports.variableStrokeOutline = variableStrokeOutline; exports.variableStrokeRings = variableStrokeRings; exports.vectorBlendColorDistance = vectorBlendColorDistance; exports.vectorBlendKeyTargets = vectorBlendKeyTargets; exports.vectorBlendKeys = vectorBlendKeys; exports.vectorBlendRail = vectorBlendRail; exports.vectorBlendRailAt = vectorBlendRailAt; exports.vectorBlendSpine = vectorBlendSpine; exports.vectorBlendStepCount = vectorBlendStepCount; exports.vectorBuilderErasedEdges = vectorBuilderErasedEdges; exports.vectorCornerAt = vectorCornerAt; exports.vectorCorners = vectorCorners; exports.vectorCornersAttribute = vectorCornersAttribute; exports.vectorDiamondPath = vectorDiamondPath; exports.vectorDocumentToSvg = vectorDocumentToSvg; exports.vectorFillMap = vectorFillMap; exports.vectorInkShapeName = vectorInkShapeName; exports.vectorNodeBounds = vectorNodeBounds; exports.vectorNodeRotation = vectorNodeRotation; exports.vectorNodeToBezier = vectorNodeToBezier; exports.vectorPaintToHex = vectorPaintToHex; exports.vectorPaintToSvg = vectorPaintToSvg; exports.vectorParentMatrix = vectorParentMatrix; exports.vectorPathData = vectorPathData; exports.vectorPathEntries = vectorPathEntries; exports.vectorPathWorldBezier = vectorPathWorldBezier; exports.vectorPathfinderShape = vectorPathfinderShape; exports.vectorResizeCursor = vectorResizeCursor; exports.vectorResultStyle = vectorResultStyle; exports.vectorShapeBuilderHit = vectorShapeBuilderHit; exports.vectorShapeBuilderMap = vectorShapeBuilderMap; exports.vectorStrokeLineArea = vectorStrokeLineArea; exports.vectorStrokeOutline = vectorStrokeOutline; exports.vectorStrokeOutlinePath = vectorStrokeOutlinePath; exports.vectorStrokeParts = vectorStrokeParts; exports.vectorStrokeReach = vectorStrokeReach; exports.vectorStrokeSvgParts = vectorStrokeSvgParts; exports.vectorStrokeWidthGeometry = vectorStrokeWidthGeometry; exports.vectorToolLabel = vectorToolLabel; exports.vectorWorldMatrix = vectorWorldMatrix; exports.walkVectorNodes = walkVectorNodes; exports.widthProfileAt = widthProfileAt; exports.widthProfileFromAttribute = widthProfileFromAttribute; exports.widthProfileMaxScale = widthProfileMaxScale; exports.widthProfilePreset = widthProfilePreset; exports.widthProfilePresetId = widthProfilePresetId; exports.widthProfileToAttribute = widthProfileToAttribute;
8842
10248
  //# sourceMappingURL=index.cjs.map