@pixerate/editor 0.39.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.
- package/THIRD_PARTY_NOTICES.md +9 -1
- package/dist/animation/index.cjs +4 -2
- package/dist/animation/index.cjs.map +1 -1
- package/dist/animation/index.js +8 -6
- package/dist/canvas/index.cjs +26 -24
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +2 -2
- package/dist/canvas/index.d.ts +2 -2
- package/dist/canvas/index.js +8 -6
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-MR3Z4OQI.cjs +66 -0
- package/dist/chunk-MR3Z4OQI.cjs.map +1 -0
- package/dist/{chunk-XHRZSB77.cjs → chunk-O4ONBPZW.cjs} +5 -64
- package/dist/chunk-O4ONBPZW.cjs.map +1 -0
- package/dist/{chunk-W7BOBWZD.js → chunk-SHOVAHUB.js} +4 -63
- package/dist/chunk-SHOVAHUB.js.map +1 -0
- package/dist/chunk-ZRJMJIQU.js +66 -0
- package/dist/chunk-ZRJMJIQU.js.map +1 -0
- package/dist/{pathfinder-DKRcXiLS.d.cts → pathfinder-BSW2ZTO6.d.cts} +1 -1
- package/dist/{pathfinder-D98S2lV5.d.ts → pathfinder-D7hXenzu.d.ts} +1 -1
- package/dist/vector/index.cjs +1929 -264
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +489 -12
- package/dist/vector/index.d.ts +489 -12
- package/dist/vector/index.js +1766 -101
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-W7BOBWZD.js.map +0 -1
- package/dist/chunk-XHRZSB77.cjs.map +0 -1
package/dist/vector/index.cjs
CHANGED
|
@@ -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"];
|
|
@@ -829,7 +840,11 @@ var capExtent = (style, width) => style.strokeLinecap === "round" || style.strok
|
|
|
829
840
|
function arrowheadWeight(style, end) {
|
|
830
841
|
const scale = end === "start" ? style.startArrowScale : style.endArrowScale;
|
|
831
842
|
const s = typeof scale === "number" && scale > 0 && Number.isFinite(scale) ? scale : 1;
|
|
832
|
-
|
|
843
|
+
const width = _nullishCoalesce(style.strokeWidth, () => ( 1));
|
|
844
|
+
const profile = strokeWidthProfileOf(style);
|
|
845
|
+
const at2 = profile ? widthProfileAt(profile, end === "start" ? 0 : 1) : null;
|
|
846
|
+
const here = at2 ? Math.max(width, width * (at2.left + at2.right) / 2) : width;
|
|
847
|
+
return Math.max(0.25, here * s);
|
|
833
848
|
}
|
|
834
849
|
function arrowheadInset(kind, hw, cap) {
|
|
835
850
|
const l = 4 * hw;
|
|
@@ -1323,25 +1338,33 @@ function decomposeRigid(m) {
|
|
|
1323
1338
|
residual: [cos * a + sin * b, -sin * a + cos * b, cos * c + sin * d, -sin * c + cos * d, 0, 0]
|
|
1324
1339
|
};
|
|
1325
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
|
+
}
|
|
1326
1350
|
function bakeIntoGeometry(node, m) {
|
|
1327
1351
|
if (isIdentityMatrix(m)) return node;
|
|
1328
|
-
if (node.type === "group")
|
|
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
|
+
}
|
|
1329
1360
|
const path = node;
|
|
1330
|
-
const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access',
|
|
1361
|
+
const mirrored = m[0] * m[3] - m[1] * m[2] < 0 && _optionalChain([path, 'access', _30 => _30.strokeWidthProfile, 'optionalAccess', _31 => _31.length]);
|
|
1331
1362
|
return {
|
|
1332
1363
|
...path,
|
|
1333
1364
|
...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
|
|
1334
1365
|
subpaths: path.subpaths.map((sp) => ({
|
|
1335
1366
|
closed: sp.closed,
|
|
1336
|
-
anchors: sp.anchors.map((a) =>
|
|
1337
|
-
const p = applyMatrix(m, a);
|
|
1338
|
-
const next = { x: p.x, y: p.y };
|
|
1339
|
-
if (a.in) next.in = applyMatrix(m, a.in);
|
|
1340
|
-
if (a.out) next.out = applyMatrix(m, a.out);
|
|
1341
|
-
if (a.type) next.type = a.type;
|
|
1342
|
-
if (a.corner) next.corner = { ...a.corner };
|
|
1343
|
-
return next;
|
|
1344
|
-
})
|
|
1367
|
+
anchors: sp.anchors.map((a) => mapAnchor(a, m))
|
|
1345
1368
|
}))
|
|
1346
1369
|
};
|
|
1347
1370
|
}
|
|
@@ -1546,7 +1569,7 @@ function applyVectorCornersAttribute(subpaths, value) {
|
|
|
1546
1569
|
for (const item of value.trim().split(/\s+/)) {
|
|
1547
1570
|
const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
|
|
1548
1571
|
if (!m) continue;
|
|
1549
|
-
const a = _optionalChain([subpaths, 'access',
|
|
1572
|
+
const a = _optionalChain([subpaths, 'access', _32 => _32[+m[1]], 'optionalAccess', _33 => _33.anchors, 'access', _34 => _34[+m[2]]]);
|
|
1550
1573
|
const radius = parseFloat(m[3]);
|
|
1551
1574
|
if (!a || !(radius > 0)) continue;
|
|
1552
1575
|
const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
|
|
@@ -1609,6 +1632,33 @@ function gradientMarkup(def) {
|
|
|
1609
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("");
|
|
1610
1633
|
return `<${tag} id="${escapeAttr(def.id)}"${attrs}>${stops}</${tag}>`;
|
|
1611
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
|
+
}
|
|
1612
1662
|
function vectorDocumentToSvg(doc, options = {}) {
|
|
1613
1663
|
const precision = _nullishCoalesce(options.precision, () => ( 3));
|
|
1614
1664
|
const prefix = _nullishCoalesce(options.idPrefix, () => ( "vg-"));
|
|
@@ -1622,6 +1672,8 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
1622
1672
|
if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => _chunkVNYRI265cjs.formatNumber.call(void 0, n, 6)).join(" ")})"`;
|
|
1623
1673
|
if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
|
|
1624
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))}"`;
|
|
1625
1677
|
return out2;
|
|
1626
1678
|
};
|
|
1627
1679
|
const emit = (node, depth) => {
|
|
@@ -1644,7 +1696,7 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
1644
1696
|
let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num(width)}"`;
|
|
1645
1697
|
if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
|
|
1646
1698
|
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
|
|
1647
|
-
if (_optionalChain([node, 'access',
|
|
1699
|
+
if (_optionalChain([node, 'access', _35 => _35.strokeDasharray, 'optionalAccess', _36 => _36.length])) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
|
|
1648
1700
|
return out2;
|
|
1649
1701
|
};
|
|
1650
1702
|
const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
|
|
@@ -1664,7 +1716,7 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
1664
1716
|
data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
|
|
1665
1717
|
if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
|
|
1666
1718
|
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
|
|
1667
|
-
if (_optionalChain([node, 'access',
|
|
1719
|
+
if (_optionalChain([node, 'access', _37 => _37.strokeDasharray, 'optionalAccess', _38 => _38.length])) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
|
|
1668
1720
|
}
|
|
1669
1721
|
lines.push(`${indent(depth)}<g${common(node)}${data}>`);
|
|
1670
1722
|
lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
|
|
@@ -1833,7 +1885,7 @@ function resolveGradient(id, raw, seen = /* @__PURE__ */ new Set()) {
|
|
|
1833
1885
|
seen.add(id);
|
|
1834
1886
|
let stops = readStops(el);
|
|
1835
1887
|
const href = _nullishCoalesce(el.attrs.href, () => ( el.attrs["xlink:href"]));
|
|
1836
|
-
if (!stops.length && _optionalChain([href, 'optionalAccess',
|
|
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]), () => ( []));
|
|
1837
1889
|
if (!stops.length) return null;
|
|
1838
1890
|
if (el.name === "radialGradient") {
|
|
1839
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 };
|
|
@@ -1937,7 +1989,7 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
1937
1989
|
const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
|
|
1938
1990
|
const fillEl = part("fill");
|
|
1939
1991
|
const subpaths = fillEl ? shapeOf(fillEl) : null;
|
|
1940
|
-
if (!fillEl || !_optionalChain([subpaths, 'optionalAccess',
|
|
1992
|
+
if (!fillEl || !_optionalChain([subpaths, 'optionalAccess', _43 => _43.length])) return null;
|
|
1941
1993
|
const a = g.attrs;
|
|
1942
1994
|
const fs = readStyle(fillEl, style);
|
|
1943
1995
|
const strokeEl = part("stroke");
|
|
@@ -1974,7 +2026,7 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
1974
2026
|
...profile.length ? { strokeWidthProfile: profile } : {},
|
|
1975
2027
|
...keptCap ? { strokeLinecap: keptCap } : {},
|
|
1976
2028
|
...keptJoin ? { strokeLinejoin: keptJoin } : {},
|
|
1977
|
-
..._optionalChain([keptDash, 'optionalAccess',
|
|
2029
|
+
..._optionalChain([keptDash, 'optionalAccess', _44 => _44.length]) ? { strokeDasharray: keptDash } : {}
|
|
1978
2030
|
};
|
|
1979
2031
|
return placed(node, m);
|
|
1980
2032
|
};
|
|
@@ -1991,6 +2043,7 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
1991
2043
|
...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
|
|
1992
2044
|
...hidden ? { visible: false } : {}
|
|
1993
2045
|
};
|
|
2046
|
+
if (child.attrs["data-vector-blend-step"] !== void 0) continue;
|
|
1994
2047
|
if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
|
|
1995
2048
|
const node2 = mergedPath(child, s, m, meta);
|
|
1996
2049
|
if (node2) out.push(node2);
|
|
@@ -2000,6 +2053,8 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
2000
2053
|
const children = visit(child, s, m);
|
|
2001
2054
|
if (!children.length) continue;
|
|
2002
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;
|
|
2003
2058
|
out.push(group);
|
|
2004
2059
|
continue;
|
|
2005
2060
|
}
|
|
@@ -2025,10 +2080,914 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
2025
2080
|
return { width, height, nodes };
|
|
2026
2081
|
}
|
|
2027
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
|
+
|
|
2028
2987
|
// src/vector/snap.ts
|
|
2029
2988
|
var VECTOR_CONSTRUCTION_ANGLES = [0, 45, 90, 135];
|
|
2030
2989
|
function constrainToLeave(leave, p) {
|
|
2031
|
-
if (!_optionalChain([leave, 'optionalAccess',
|
|
2990
|
+
if (!_optionalChain([leave, 'optionalAccess', _64 => _64.constrain])) return p;
|
|
2032
2991
|
const dx = p.x - leave.at.x;
|
|
2033
2992
|
const dy = p.y - leave.at.y;
|
|
2034
2993
|
switch (leave.constrain) {
|
|
@@ -2139,7 +3098,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2139
3098
|
moving.set(r.nodeId, set);
|
|
2140
3099
|
}
|
|
2141
3100
|
const t = _VectorSnapTargets.collect(doc, new Set(moving.keys()), "segments", false);
|
|
2142
|
-
t.pushPaths(doc, new Set(moving.keys()), (id, s, i) => !!_optionalChain([moving, 'access',
|
|
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);
|
|
2143
3102
|
return t.index();
|
|
2144
3103
|
}
|
|
2145
3104
|
/**
|
|
@@ -2257,7 +3216,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2257
3216
|
}
|
|
2258
3217
|
const b = vectorNodeBounds(n, parent);
|
|
2259
3218
|
const f = nodeFrame(n, parent, b);
|
|
2260
|
-
if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess',
|
|
3219
|
+
if (b) self.pushRect(b, "edge", _nullishCoalesce(_optionalChain([f, 'optionalAccess', _69 => _69.centre]), () => ( null)));
|
|
2261
3220
|
}
|
|
2262
3221
|
};
|
|
2263
3222
|
visit(doc.nodes, IDENTITY_MATRIX);
|
|
@@ -2302,7 +3261,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2302
3261
|
lines: lines2,
|
|
2303
3262
|
labels: [this.label(q, kind)]
|
|
2304
3263
|
});
|
|
2305
|
-
if (_optionalChain([leave, 'optionalAccess',
|
|
3264
|
+
if (_optionalChain([leave, 'optionalAccess', _70 => _70.constrain])) {
|
|
2306
3265
|
const q = constrainToLeave(leave, p);
|
|
2307
3266
|
const hit = this.slide(leave.at, { x: q.x - leave.at.x, y: q.y - leave.at.y }, q, tol, true);
|
|
2308
3267
|
return hit ? { point: hit.point, x: true, y: true, ...this.linedUp(hit.point, hit.from) } : none(q);
|
|
@@ -2318,7 +3277,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2318
3277
|
}
|
|
2319
3278
|
const on = this.onSegment(near2);
|
|
2320
3279
|
if (on) return at2(on, "path");
|
|
2321
|
-
if (_optionalChain([leave, 'optionalAccess',
|
|
3280
|
+
if (_optionalChain([leave, 'optionalAccess', _71 => _71.angles, 'optionalAccess', _72 => _72.length])) {
|
|
2322
3281
|
const c = construction(leave, p, tol);
|
|
2323
3282
|
if (c) {
|
|
2324
3283
|
const hit = this.slide(leave.at, c.dir, c.point, tol, false);
|
|
@@ -2448,7 +3407,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2448
3407
|
* with nothing spaces the box evenly in its row or column. Returns the correction and guides.
|
|
2449
3408
|
*/
|
|
2450
3409
|
snapRect(r, tol, options = {}) {
|
|
2451
|
-
const axes = { x: _optionalChain([options, 'access',
|
|
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 };
|
|
2452
3411
|
const nearest = (lines, values) => {
|
|
2453
3412
|
let best = null;
|
|
2454
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 };
|
|
@@ -2456,7 +3415,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2456
3415
|
};
|
|
2457
3416
|
const bx = axes.x ? nearest(this.xs, [r.x, r.x + r.width / 2, r.x + r.width]) : null;
|
|
2458
3417
|
const by = axes.y ? nearest(this.ys, [r.y, r.y + r.height / 2, r.y + r.height]) : null;
|
|
2459
|
-
const out = { dx: _nullishCoalesce(_optionalChain([bx, 'optionalAccess',
|
|
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: [] };
|
|
2460
3419
|
const done = { ...r, x: r.x + out.dx, y: r.y + out.dy };
|
|
2461
3420
|
if (bx) {
|
|
2462
3421
|
const x = bx.from.x;
|
|
@@ -2554,14 +3513,14 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2554
3513
|
* corner) slides along it until one of them is on a target. An axis that snaps to nothing
|
|
2555
3514
|
* may match another object's width or height instead.
|
|
2556
3515
|
*/
|
|
2557
|
-
snapScale(
|
|
2558
|
-
const toDoc =
|
|
2559
|
-
const toLocal =
|
|
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;
|
|
2560
3519
|
const corner = handle.length === 2;
|
|
2561
3520
|
const proportional = !!mods.shift;
|
|
2562
|
-
const centre = { x:
|
|
2563
|
-
const origin = mods.alt ? centre : handlePos(opposite(handle),
|
|
2564
|
-
const hp = handlePos(handle,
|
|
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);
|
|
2565
3524
|
const d0 = { x: hp.x - origin.x, y: hp.y - origin.y };
|
|
2566
3525
|
const valid = (n, s) => Number.isFinite(n) && n * s > 0;
|
|
2567
3526
|
const nearest = (v2, lines) => {
|
|
@@ -2579,15 +3538,15 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2579
3538
|
const on = this.nearestPoint(p, tol);
|
|
2580
3539
|
const tries = [];
|
|
2581
3540
|
if (on) tries.push({ tx: on.p.x, ty: on.p.y, s: { hx: null, hy: null, on } });
|
|
2582
|
-
tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess',
|
|
2583
|
-
tries.push({ tx: _nullishCoalesce(_optionalChain([hx, 'optionalAccess',
|
|
2584
|
-
tries.push({ tx: null, ty: _nullishCoalesce(_optionalChain([hy, 'optionalAccess',
|
|
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 } });
|
|
2585
3544
|
for (const { tx, ty, s } of tries) {
|
|
2586
3545
|
if (tx === null && ty === null) continue;
|
|
2587
3546
|
const q = applyMatrix(toLocal, { x: _nullishCoalesce(tx, () => ( p.x)), y: _nullishCoalesce(ty, () => ( p.y)) });
|
|
2588
3547
|
let cx = (q.x - origin.x) / d0.x;
|
|
2589
3548
|
let cy = (q.y - origin.y) / d0.y;
|
|
2590
|
-
if (!
|
|
3549
|
+
if (!frame2.angle) {
|
|
2591
3550
|
if (tx === null) cx = sx;
|
|
2592
3551
|
if (ty === null) cy = sy;
|
|
2593
3552
|
}
|
|
@@ -2616,7 +3575,7 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2616
3575
|
if (!best || (inLine === best.inLine ? moved < best.moved : !inLine)) best = { s, inLine, moved, snap: found2 };
|
|
2617
3576
|
};
|
|
2618
3577
|
for (const h of [handle, ...ends]) {
|
|
2619
|
-
const hv = handlePos(h,
|
|
3578
|
+
const hv = handlePos(h, frame2);
|
|
2620
3579
|
const v2 = { x: hv.x - origin.x, y: hv.y - origin.y };
|
|
2621
3580
|
const base = applyMatrix(toDoc, { x: origin.x + c.x * v2.x, y: origin.y + c.y * v2.y });
|
|
2622
3581
|
const dir = applyMatrix(toDoc, { x: e.x * v2.x, y: e.y * v2.y });
|
|
@@ -2651,25 +3610,25 @@ var VectorSnapTargets = (_class = class _VectorSnapTargets {
|
|
|
2651
3610
|
const movesY = handle !== "e" && handle !== "w";
|
|
2652
3611
|
const sized = [];
|
|
2653
3612
|
if (!proportional) {
|
|
2654
|
-
if (movesX && !snap.hx && !(
|
|
2655
|
-
const len2 = this.matchSize(Math.abs(out.sx) *
|
|
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);
|
|
2656
3615
|
if (len2 !== null && len2 > 0) {
|
|
2657
|
-
out.sx = Math.sign(out.sx || 1) * (len2 /
|
|
3616
|
+
out.sx = Math.sign(out.sx || 1) * (len2 / frame2.width);
|
|
2658
3617
|
sized.push("x");
|
|
2659
3618
|
}
|
|
2660
3619
|
}
|
|
2661
|
-
if (movesY && !snap.hy && !(
|
|
2662
|
-
const len2 = this.matchSize(Math.abs(out.sy) *
|
|
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);
|
|
2663
3622
|
if (len2 !== null && len2 > 0) {
|
|
2664
|
-
out.sy = Math.sign(out.sy || 1) * (len2 /
|
|
3623
|
+
out.sy = Math.sign(out.sy || 1) * (len2 / frame2.height);
|
|
2665
3624
|
sized.push("y");
|
|
2666
3625
|
}
|
|
2667
3626
|
}
|
|
2668
3627
|
}
|
|
2669
|
-
const x0 = origin.x + (
|
|
2670
|
-
const x1 = origin.x + (
|
|
2671
|
-
const y0 = origin.y + (
|
|
2672
|
-
const y1 = origin.y + (
|
|
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;
|
|
2673
3632
|
const box = { x0: Math.min(x0, x1), x1: Math.max(x0, x1), y0: Math.min(y0, y1), y1: Math.max(y0, y1) };
|
|
2674
3633
|
const page = [
|
|
2675
3634
|
{ x: box.x0, y: box.y0 },
|
|
@@ -3958,6 +4917,15 @@ var DEFAULT_VECTOR_PATH_EDIT = {
|
|
|
3958
4917
|
fillGap: 0
|
|
3959
4918
|
};
|
|
3960
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
|
+
}
|
|
3961
4929
|
var VECTOR_STYLE_KEYS = [
|
|
3962
4930
|
"fill",
|
|
3963
4931
|
"stroke",
|
|
@@ -4001,7 +4969,7 @@ function getEditablePathIds(state) {
|
|
|
4001
4969
|
}
|
|
4002
4970
|
}
|
|
4003
4971
|
}
|
|
4004
|
-
return [...ids].filter((id) => _optionalChain([findVectorNode, 'call',
|
|
4972
|
+
return [...ids].filter((id) => _optionalChain([findVectorNode, 'call', _83 => _83(state.document.nodes, id), 'optionalAccess', _84 => _84.type]) === "path");
|
|
4005
4973
|
}
|
|
4006
4974
|
function getVisibleAnchors(state) {
|
|
4007
4975
|
const out = [];
|
|
@@ -4016,7 +4984,7 @@ function getVisibleHandles(state) {
|
|
|
4016
4984
|
const seen = /* @__PURE__ */ new Set();
|
|
4017
4985
|
const push = (node, s, i, handle) => {
|
|
4018
4986
|
const sp = node.subpaths[s];
|
|
4019
|
-
const a = _optionalChain([sp, 'optionalAccess',
|
|
4987
|
+
const a = _optionalChain([sp, 'optionalAccess', _85 => _85.anchors, 'access', _86 => _86[i]]);
|
|
4020
4988
|
if (!a || !a[handle]) return;
|
|
4021
4989
|
const key = `${node.id}:${s}:${i}:${handle}`;
|
|
4022
4990
|
if (seen.has(key)) return;
|
|
@@ -4025,8 +4993,8 @@ function getVisibleHandles(state) {
|
|
|
4025
4993
|
};
|
|
4026
4994
|
if (state.tool === "pen" && state.pen) {
|
|
4027
4995
|
const node = findVectorNode(state.document.nodes, state.pen.nodeId);
|
|
4028
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
4029
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
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) {
|
|
4030
4998
|
const last = sp.anchors.length - 1;
|
|
4031
4999
|
push(node, state.pen.subpath, last, "in");
|
|
4032
5000
|
push(node, state.pen.subpath, last, "out");
|
|
@@ -4064,11 +5032,11 @@ function getVectorCornerWidgets(state) {
|
|
|
4064
5032
|
const side = Math.min(c.lu, c.lv);
|
|
4065
5033
|
if (side * zoom < 3 * VECTOR_CORNER_WIDGET_INSET) continue;
|
|
4066
5034
|
const a = node.subpaths[c.subpath].anchors[c.index];
|
|
4067
|
-
const radius = _nullishCoalesce(_optionalChain([a, 'access',
|
|
5035
|
+
const radius = _nullishCoalesce(_optionalChain([a, 'access', _89 => _89.corner, 'optionalAccess', _90 => _90.radius]), () => ( 0));
|
|
4068
5036
|
const drawn = fitCornerRadius(c, radius);
|
|
4069
5037
|
const k = Math.min(Math.max(drawn / c.sin, VECTOR_CORNER_WIDGET_INSET / (zoom * Math.sqrt(1 + c.cos))), side / 2);
|
|
4070
5038
|
const p = applyMatrix(m, cornerBisectorPoint(c, k));
|
|
4071
|
-
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',
|
|
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 });
|
|
4072
5040
|
}
|
|
4073
5041
|
}
|
|
4074
5042
|
return out;
|
|
@@ -4078,6 +5046,12 @@ function vectorStrokeWidthGeometry(node) {
|
|
|
4078
5046
|
const parts = vectorStrokeParts(subpathsToBezier(node.subpaths), node);
|
|
4079
5047
|
return parts.line.length ? { line: parts.line, unit: parts.drawWidth / 2 } : null;
|
|
4080
5048
|
}
|
|
5049
|
+
function shiftWidthPoints(profile, indices, dt) {
|
|
5050
|
+
const moving = new Set(indices);
|
|
5051
|
+
const tagged = profile.map((p, i) => ({ p: moving.has(i) ? { ...p, t: Math.min(Math.max(p.t + dt, 0), 1) } : { ...p }, i }));
|
|
5052
|
+
tagged.sort((a, b) => a.p.t - b.p.t || a.i - b.i);
|
|
5053
|
+
return { points: tagged.map((x) => x.p), renumber: new Map(tagged.map((x, k) => [x.i, k])) };
|
|
5054
|
+
}
|
|
4081
5055
|
function getVectorWidthHandles(state) {
|
|
4082
5056
|
if (state.tool !== "width") return [];
|
|
4083
5057
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -4085,13 +5059,13 @@ function getVectorWidthHandles(state) {
|
|
|
4085
5059
|
const n = findVectorNode(state.document.nodes, id);
|
|
4086
5060
|
if (n) for (const p of collectPathNodes(n)) ids.add(p.id);
|
|
4087
5061
|
}
|
|
4088
|
-
if (_optionalChain([state, 'access',
|
|
4089
|
-
if (_optionalChain([state, 'access',
|
|
4090
|
-
|
|
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);
|
|
5064
|
+
for (const r of state.selectedWidthPoints) ids.add(r.nodeId);
|
|
4091
5065
|
const out = [];
|
|
4092
5066
|
for (const id of ids) {
|
|
4093
5067
|
const node = findVectorNode(state.document.nodes, id);
|
|
4094
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
5068
|
+
if (_optionalChain([node, 'optionalAccess', _97 => _97.type]) !== "path" || node.visible === false || !_optionalChain([node, 'access', _98 => _98.strokeWidthProfile, 'optionalAccess', _99 => _99.length])) continue;
|
|
4095
5069
|
const g = vectorStrokeWidthGeometry(node);
|
|
4096
5070
|
if (!g) continue;
|
|
4097
5071
|
const profile = sanitizeWidthProfile(node.strokeWidthProfile);
|
|
@@ -4106,7 +5080,6 @@ function getVectorWidthHandles(state) {
|
|
|
4106
5080
|
const p = applyMatrix(m, c);
|
|
4107
5081
|
const l = applyMatrix(m, { x: c.x + n.x * w.left * g.unit, y: c.y + n.y * w.left * g.unit });
|
|
4108
5082
|
const r = applyMatrix(m, { x: c.x - n.x * w.right * g.unit, y: c.y - n.y * w.right * g.unit });
|
|
4109
|
-
const sel = state.selectedWidthPoint;
|
|
4110
5083
|
out.push({
|
|
4111
5084
|
ref: { nodeId: id, index, subpath: sp },
|
|
4112
5085
|
x: p.x,
|
|
@@ -4116,7 +5089,7 @@ function getVectorWidthHandles(state) {
|
|
|
4116
5089
|
t: w.t,
|
|
4117
5090
|
leftScale: w.left,
|
|
4118
5091
|
rightScale: w.right,
|
|
4119
|
-
selected:
|
|
5092
|
+
selected: state.selectedWidthPoints.some((r2) => r2.nodeId === id && r2.index === index)
|
|
4120
5093
|
});
|
|
4121
5094
|
});
|
|
4122
5095
|
}
|
|
@@ -4125,7 +5098,7 @@ function getVectorWidthHandles(state) {
|
|
|
4125
5098
|
}
|
|
4126
5099
|
function getAnchor(doc, ref) {
|
|
4127
5100
|
const node = findVectorNode(doc.nodes, ref.nodeId);
|
|
4128
|
-
return _optionalChain([node, 'optionalAccess',
|
|
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;
|
|
4129
5102
|
}
|
|
4130
5103
|
function getAnchorWorld(doc, ref) {
|
|
4131
5104
|
const a = getAnchor(doc, ref);
|
|
@@ -4159,11 +5132,11 @@ function getTransformHandlePositions(box) {
|
|
|
4159
5132
|
{ handle: "rotate", x: x + w / 2, y: y - VECTOR_ROTATE_HANDLE_OFFSET }
|
|
4160
5133
|
];
|
|
4161
5134
|
}
|
|
4162
|
-
function getVectorSelectionBoxGeometry(
|
|
5135
|
+
function getVectorSelectionBoxGeometry(frame2, viewport) {
|
|
4163
5136
|
const z = viewport.zoom;
|
|
4164
|
-
const box = { x:
|
|
4165
|
-
const rot = rotateMatrix(
|
|
4166
|
-
const toScreen2 = (p) =>
|
|
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 };
|
|
4167
5140
|
const handles = getTransformHandlePositions(box).map((h) => ({ ...h, screen: toScreen2(h) }));
|
|
4168
5141
|
const corners2 = [
|
|
4169
5142
|
{ x: box.x, y: box.y },
|
|
@@ -4172,7 +5145,7 @@ function getVectorSelectionBoxGeometry(frame, viewport) {
|
|
|
4172
5145
|
{ x: box.x, y: box.y + box.height }
|
|
4173
5146
|
].map(toScreen2);
|
|
4174
5147
|
const r = (n) => Math.round(n * 1e3) / 1e3;
|
|
4175
|
-
return { box, angle:
|
|
5148
|
+
return { box, angle: frame2.angle, transform: frame2.angle ? `rotate(${r(frame2.angle)} ${r(viewport.x)} ${r(viewport.y)})` : "", handles, corners: corners2 };
|
|
4176
5149
|
}
|
|
4177
5150
|
var RESIZE_CURSOR_ORDER = ["ew-resize", "nwse-resize", "ns-resize", "nesw-resize"];
|
|
4178
5151
|
var HANDLE_ANGLES = { e: 0, se: 45, s: 90, sw: 135, w: 180, nw: 225, n: 270, ne: 315 };
|
|
@@ -4196,7 +5169,8 @@ var TOOL_LABELS = {
|
|
|
4196
5169
|
eraser: "Eraser",
|
|
4197
5170
|
width: "Width",
|
|
4198
5171
|
shapeBuilder: "Shape Builder",
|
|
4199
|
-
paintBucket: "Paint Bucket"
|
|
5172
|
+
paintBucket: "Paint Bucket",
|
|
5173
|
+
blend: "Blend"
|
|
4200
5174
|
};
|
|
4201
5175
|
var TOOL_KEYS = {
|
|
4202
5176
|
v: "select",
|
|
@@ -4213,7 +5187,8 @@ var TOOL_KEYS = {
|
|
|
4213
5187
|
e: "eraser",
|
|
4214
5188
|
w: "width",
|
|
4215
5189
|
m: "shapeBuilder",
|
|
4216
|
-
b: "paintBucket"
|
|
5190
|
+
b: "paintBucket",
|
|
5191
|
+
j: "blend"
|
|
4217
5192
|
};
|
|
4218
5193
|
function vectorToolLabel(tool) {
|
|
4219
5194
|
return TOOL_LABELS[tool];
|
|
@@ -4233,7 +5208,8 @@ var VECTOR_TOOL_SHORTCUTS = {
|
|
|
4233
5208
|
eraser: "E",
|
|
4234
5209
|
width: "W",
|
|
4235
5210
|
shapeBuilder: "M",
|
|
4236
|
-
paintBucket: "B"
|
|
5211
|
+
paintBucket: "B",
|
|
5212
|
+
blend: "J"
|
|
4237
5213
|
};
|
|
4238
5214
|
var VECTOR_TOOL_GLYPHS = {
|
|
4239
5215
|
select: "\u2196",
|
|
@@ -4250,7 +5226,8 @@ var VECTOR_TOOL_GLYPHS = {
|
|
|
4250
5226
|
eraser: "\u232B",
|
|
4251
5227
|
width: "\u21F9",
|
|
4252
5228
|
shapeBuilder: "\u2295",
|
|
4253
|
-
paintBucket: "\u25CD"
|
|
5229
|
+
paintBucket: "\u25CD",
|
|
5230
|
+
blend: "\u29D3"
|
|
4254
5231
|
};
|
|
4255
5232
|
var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
|
|
4256
5233
|
var BOOLEAN_OPS = {
|
|
@@ -4312,45 +5289,50 @@ var VectorEditorController = (_class3 = class {
|
|
|
4312
5289
|
|
|
4313
5290
|
__init9() {this.selection = []}
|
|
4314
5291
|
__init10() {this.anchors = []}
|
|
4315
|
-
__init11() {this.
|
|
4316
|
-
__init12() {this.
|
|
4317
|
-
__init13() {this.
|
|
5292
|
+
__init11() {this.widthPoints = []}
|
|
5293
|
+
__init12() {this.widthEdit = null}
|
|
5294
|
+
__init13() {this.editingId = null}
|
|
5295
|
+
__init14() {this.pen = null}
|
|
4318
5296
|
|
|
4319
5297
|
|
|
4320
5298
|
|
|
4321
5299
|
|
|
4322
5300
|
|
|
4323
5301
|
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
5302
|
+
__init15() {this.hover = null}
|
|
5303
|
+
__init16() {this.pointer = null}
|
|
5304
|
+
__init17() {this.drag = null}
|
|
5305
|
+
__init18() {this.interaction = null}
|
|
5306
|
+
__init19() {this.guides = null}
|
|
4329
5307
|
/** Whether the last `snapDrawn` landed on an object (guides may be hidden). */
|
|
4330
|
-
|
|
5308
|
+
__init20() {this.snappedToObject = false}
|
|
4331
5309
|
/** Drawing targets for hovering the pen and shape tools, kept while the document and the excluded path stay the same. */
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
5310
|
+
__init21() {this.hoverTargets = null}
|
|
5311
|
+
__init22() {this.spaceHeld = false}
|
|
5312
|
+
__init23() {this.clipboard = null}
|
|
4335
5313
|
/** Shape Builder map of the selection, kept while the document and the selection stay the same. */
|
|
4336
|
-
|
|
5314
|
+
__init24() {this.builderCache = null}
|
|
4337
5315
|
/** Paint bucket map of the visible outlines. */
|
|
4338
|
-
|
|
4339
|
-
|
|
5316
|
+
__init25() {this.fillCache = null}
|
|
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 = {}}
|
|
4340
5321
|
/** What the highlight shows, to skip notifying when the pointer stays over the same face. */
|
|
4341
|
-
|
|
5322
|
+
__init29() {this.builderHoverKey = null}
|
|
4342
5323
|
|
|
4343
5324
|
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
5325
|
+
__init30() {this.listeners = /* @__PURE__ */ new Set()}
|
|
5326
|
+
__init31() {this.cleanups = []}
|
|
5327
|
+
__init32() {this.applying = false}
|
|
4347
5328
|
/** Set while a commit records a change that was already applied, so `restore` still reports it. */
|
|
4348
|
-
|
|
4349
|
-
|
|
5329
|
+
__init33() {this.committing = false}
|
|
5330
|
+
__init34() {this.revision = 0}
|
|
4350
5331
|
|
|
4351
|
-
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);
|
|
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);
|
|
4352
5333
|
this.history = _nullishCoalesce(options.history, () => ( new (0, _chunkQKLRAA4Tcjs.HistoryManager)()));
|
|
4353
5334
|
this.doc = options.document ? cloneVectorDocument(options.document) : { width: 800, height: 600, nodes: [] };
|
|
5335
|
+
this.doc = this.refreshed(this.doc);
|
|
4354
5336
|
this.tool = _nullishCoalesce(options.tool, () => ( "select"));
|
|
4355
5337
|
this.defaults = { ...DEFAULT_DEFAULTS, ..._nullishCoalesce(options.defaults, () => ( {})) };
|
|
4356
5338
|
this.viewport = { x: 0, y: 0, zoom: 1, ..._nullishCoalesce(options.viewport, () => ( {})) };
|
|
@@ -4377,9 +5359,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
4377
5359
|
// State
|
|
4378
5360
|
// -------------------------------------------------------------------------
|
|
4379
5361
|
/** The current snapshot (stable until the next change). */
|
|
4380
|
-
|
|
5362
|
+
__init35() {this.getState = () => this.state}
|
|
4381
5363
|
/** Calls `listener` after every change. Returns an unsubscribe function. */
|
|
4382
|
-
|
|
5364
|
+
__init36() {this.subscribe = (listener) => {
|
|
4383
5365
|
this.listeners.add(listener);
|
|
4384
5366
|
return () => {
|
|
4385
5367
|
this.listeners.delete(listener);
|
|
@@ -4398,7 +5380,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
4398
5380
|
tool: this.tool,
|
|
4399
5381
|
selection: this.selection,
|
|
4400
5382
|
selectedAnchors: this.anchors,
|
|
4401
|
-
|
|
5383
|
+
selectedWidthPoints: this.widthPoints,
|
|
5384
|
+
widthPointEdit: this.widthEdit,
|
|
4402
5385
|
editingId: this.editingId,
|
|
4403
5386
|
selectionBounds: this.boundsOf(this.selection),
|
|
4404
5387
|
selectionFrame: this.frameOf(this.selectedRoots()),
|
|
@@ -4414,6 +5397,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
4414
5397
|
pen: this.pen,
|
|
4415
5398
|
pointer: this.pointer,
|
|
4416
5399
|
builder: this.builderPreview,
|
|
5400
|
+
blendStart: this.blendStart,
|
|
5401
|
+
blendDefaults: this.blendDefaults,
|
|
4417
5402
|
canUndo: h.canUndo,
|
|
4418
5403
|
canRedo: h.canRedo,
|
|
4419
5404
|
undoLabel: h.undoName,
|
|
@@ -4421,10 +5406,18 @@ var VectorEditorController = (_class3 = class {
|
|
|
4421
5406
|
revision: this.revision
|
|
4422
5407
|
};
|
|
4423
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
|
+
}
|
|
4424
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;
|
|
4425
5418
|
this.revision++;
|
|
4426
5419
|
this.state = this.computeState();
|
|
4427
|
-
if (docChanged) _optionalChain([this, 'access',
|
|
5420
|
+
if (docChanged) _optionalChain([this, 'access', _105 => _105.onChange, 'optionalCall', _106 => _106(this.doc)]);
|
|
4428
5421
|
const state = this.state;
|
|
4429
5422
|
this.listeners.forEach((listener) => {
|
|
4430
5423
|
try {
|
|
@@ -4454,29 +5447,31 @@ var VectorEditorController = (_class3 = class {
|
|
|
4454
5447
|
return applyMatrix(invertMatrix(this.worldOf(id, doc)), p);
|
|
4455
5448
|
}
|
|
4456
5449
|
cursorFor() {
|
|
4457
|
-
if (_optionalChain([this, 'access',
|
|
4458
|
-
if (_optionalChain([this, 'access',
|
|
4459
|
-
if (_optionalChain([this, 'access',
|
|
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";
|
|
4460
5453
|
const h = this.hover;
|
|
4461
5454
|
switch (this.tool) {
|
|
4462
5455
|
case "select":
|
|
4463
|
-
if (_optionalChain([h, 'optionalAccess',
|
|
4464
|
-
return _optionalChain([h, 'optionalAccess',
|
|
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";
|
|
4465
5458
|
case "direct":
|
|
4466
|
-
if (_optionalChain([h, 'optionalAccess',
|
|
4467
|
-
if (_optionalChain([h, 'optionalAccess',
|
|
4468
|
-
return _optionalChain([h, 'optionalAccess',
|
|
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";
|
|
4469
5462
|
case "pen":
|
|
4470
|
-
return _optionalChain([h, 'optionalAccess',
|
|
5463
|
+
return _optionalChain([h, 'optionalAccess', _123 => _123.anchor]) ? "pointer" : "crosshair";
|
|
4471
5464
|
case "width":
|
|
4472
|
-
if (_optionalChain([this, 'access',
|
|
4473
|
-
if (_optionalChain([h, 'optionalAccess',
|
|
4474
|
-
return _optionalChain([h, 'optionalAccess',
|
|
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";
|
|
4475
5468
|
case "shapeBuilder":
|
|
4476
|
-
if (!_optionalChain([this, 'access',
|
|
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";
|
|
4477
5470
|
return this.builderPreview.mode === "erase" ? "not-allowed" : "cell";
|
|
4478
5471
|
case "paintBucket":
|
|
4479
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";
|
|
4480
5475
|
default:
|
|
4481
5476
|
return "crosshair";
|
|
4482
5477
|
}
|
|
@@ -4489,6 +5484,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4489
5484
|
}
|
|
4490
5485
|
restore(s) {
|
|
4491
5486
|
const docChanged = s.doc !== this.doc || this.committing;
|
|
5487
|
+
if (!this.committing) this.widthEdit = null;
|
|
4492
5488
|
this.committing = false;
|
|
4493
5489
|
this.applying = true;
|
|
4494
5490
|
try {
|
|
@@ -4496,7 +5492,14 @@ var VectorEditorController = (_class3 = class {
|
|
|
4496
5492
|
this.selection = s.selection.filter((id) => this.getNode(id));
|
|
4497
5493
|
this.anchors = s.anchors.filter((r) => getAnchor(this.doc, r));
|
|
4498
5494
|
this.editingId = s.editingId && this.getNode(s.editingId) ? s.editingId : null;
|
|
4499
|
-
this.pen = s.pen && _optionalChain([this, 'access',
|
|
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;
|
|
5496
|
+
if (this.widthPoints.length) {
|
|
5497
|
+
const kept = this.widthPoints.filter((r) => {
|
|
5498
|
+
const n = this.getNode(r.nodeId);
|
|
5499
|
+
return _optionalChain([n, 'optionalAccess', _139 => _139.type]) === "path" && !!_optionalChain([n, 'access', _140 => _140.strokeWidthProfile, 'optionalAccess', _141 => _141[r.index]]);
|
|
5500
|
+
});
|
|
5501
|
+
if (kept.length !== this.widthPoints.length) this.widthPoints = kept;
|
|
5502
|
+
}
|
|
4500
5503
|
} finally {
|
|
4501
5504
|
this.applying = false;
|
|
4502
5505
|
}
|
|
@@ -4504,6 +5507,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4504
5507
|
}
|
|
4505
5508
|
/** Records the change from `before` to the current state as one undo step. */
|
|
4506
5509
|
commit(name, before) {
|
|
5510
|
+
this.doc = this.refreshed(this.doc);
|
|
4507
5511
|
const after = this.snapshot();
|
|
4508
5512
|
if (before.doc === after.doc) {
|
|
4509
5513
|
this.notify();
|
|
@@ -4602,9 +5606,11 @@ var VectorEditorController = (_class3 = class {
|
|
|
4602
5606
|
this.tool = tool;
|
|
4603
5607
|
this.hover = null;
|
|
4604
5608
|
this.guides = null;
|
|
4605
|
-
this.
|
|
5609
|
+
this.widthPoints = [];
|
|
5610
|
+
this.widthEdit = null;
|
|
4606
5611
|
this.builderPreview = null;
|
|
4607
5612
|
this.builderHoverKey = null;
|
|
5613
|
+
this.blendStart = null;
|
|
4608
5614
|
if (tool !== "direct") {
|
|
4609
5615
|
this.anchors = [];
|
|
4610
5616
|
this.editingId = null;
|
|
@@ -4663,7 +5669,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4663
5669
|
if (res.kind) {
|
|
4664
5670
|
this.snappedToObject = true;
|
|
4665
5671
|
this.setGuides(res);
|
|
4666
|
-
return _optionalChain([leave, 'optionalAccess',
|
|
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) };
|
|
4667
5673
|
}
|
|
4668
5674
|
}
|
|
4669
5675
|
this.setGuides(null);
|
|
@@ -4681,14 +5687,14 @@ var VectorEditorController = (_class3 = class {
|
|
|
4681
5687
|
/** Where the pointer at `p` would place a point with the pen or a shape tool (and the guides showing why). */
|
|
4682
5688
|
hoverSnap(p, input) {
|
|
4683
5689
|
if (this.tool === "pen") {
|
|
4684
|
-
const sp = this.pen ? _optionalChain([this, 'access',
|
|
4685
|
-
if (this.pen && sp && _optionalChain([this, 'access',
|
|
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])) {
|
|
4686
5692
|
this.setGuides(null);
|
|
4687
5693
|
return p;
|
|
4688
5694
|
}
|
|
4689
5695
|
const last = this.pen && sp ? getAnchorWorld(this.doc, { nodeId: this.pen.nodeId, subpath: this.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
|
|
4690
5696
|
const leave = last ? { at: { x: last.x, y: last.y }, constrain: input.shift ? "angle" : null } : null;
|
|
4691
|
-
return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access',
|
|
5697
|
+
return this.snapDrawn(p, leave, input, () => this.drawingTargets(_nullishCoalesce(_optionalChain([this, 'access', _149 => _149.pen, 'optionalAccess', _150 => _150.nodeId]), () => ( null))));
|
|
4692
5698
|
}
|
|
4693
5699
|
if (SHAPE_TOOLS.has(this.tool)) return this.snapDrawn(p, null, input, () => this.drawingTargets(null));
|
|
4694
5700
|
this.setGuides(null);
|
|
@@ -4836,7 +5842,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4836
5842
|
if (d.fillRule) out.fillRule = d.fillRule;
|
|
4837
5843
|
if (d.strokeAlign && d.strokeAlign !== "center") out.strokeAlign = d.strokeAlign;
|
|
4838
5844
|
for (const key of ["startArrow", "endArrow", "arrowAlign", "startArrowScale", "endArrowScale"]) if (d[key]) out[key] = d[key];
|
|
4839
|
-
if (_optionalChain([d, 'access',
|
|
5845
|
+
if (_optionalChain([d, 'access', _151 => _151.strokeWidthProfile, 'optionalAccess', _152 => _152.length])) out.strokeWidthProfile = d.strokeWidthProfile.map((p) => ({ ...p }));
|
|
4840
5846
|
return out;
|
|
4841
5847
|
}
|
|
4842
5848
|
/** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
|
|
@@ -4878,18 +5884,143 @@ var VectorEditorController = (_class3 = class {
|
|
|
4878
5884
|
const p = profile;
|
|
4879
5885
|
points = p.taper || p.wave ? generateWidthProfile(p) : [];
|
|
4880
5886
|
}
|
|
4881
|
-
this.
|
|
5887
|
+
this.widthPoints = [];
|
|
5888
|
+
this.widthEdit = null;
|
|
4882
5889
|
return this.setStyle({ strokeWidthProfile: points.length ? points : null }, _nullishCoalesce(options.ids, () => ( this.selection)), { label: points.length ? "Width Profile" : "Uniform Width" });
|
|
4883
5890
|
}
|
|
4884
|
-
/** Removes one width point of a path's profile
|
|
5891
|
+
/** Removes one width point of a path's profile. */
|
|
4885
5892
|
removeWidthPoint(ref) {
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
5893
|
+
return this.removeWidthPoints([ref]);
|
|
5894
|
+
}
|
|
5895
|
+
/** Removes width points (default: the selected ones — the width tool's Delete), as one undo step. */
|
|
5896
|
+
removeWidthPoints(refs = this.widthPoints) {
|
|
5897
|
+
const byNode = /* @__PURE__ */ new Map();
|
|
5898
|
+
for (const r of refs) {
|
|
5899
|
+
const node = this.getNode(r.nodeId);
|
|
5900
|
+
if (_optionalChain([node, 'optionalAccess', _153 => _153.type]) !== "path" || !_optionalChain([node, 'access', _154 => _154.strokeWidthProfile, 'optionalAccess', _155 => _155[r.index]])) continue;
|
|
5901
|
+
byNode.set(r.nodeId, [..._nullishCoalesce(byNode.get(r.nodeId), () => ( [])), r.index]);
|
|
5902
|
+
}
|
|
5903
|
+
if (!byNode.size) return { ok: false, message: "There is no such width point." };
|
|
5904
|
+
const count = [...byNode.values()].reduce((n, l) => n + new Set(l).size, 0);
|
|
5905
|
+
this.widthPoints = [];
|
|
5906
|
+
this.widthEdit = null;
|
|
5907
|
+
this.change(count > 1 ? "Delete Width Points" : "Delete Width Point", () => {
|
|
5908
|
+
for (const [id, indices] of byNode) {
|
|
5909
|
+
const node = this.getNode(id);
|
|
5910
|
+
const rest = removeWidthPoints(sanitizeWidthProfile(node.strokeWidthProfile), indices);
|
|
5911
|
+
this.replaceNode(id, withStyle(node, { strokeWidthProfile: rest.length ? rest : null }));
|
|
5912
|
+
}
|
|
5913
|
+
});
|
|
5914
|
+
return { ok: true };
|
|
5915
|
+
}
|
|
5916
|
+
/** Selects width points (width tool); points on other paths than the first are dropped. */
|
|
5917
|
+
selectWidthPoints(refs) {
|
|
5918
|
+
const first = _optionalChain([refs, 'access', _156 => _156[0], 'optionalAccess', _157 => _157.nodeId]);
|
|
5919
|
+
const next = [];
|
|
5920
|
+
for (const r of refs) {
|
|
5921
|
+
if (r.nodeId !== first || next.some((q) => q.index === r.index)) continue;
|
|
5922
|
+
const n = this.getNode(r.nodeId);
|
|
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)) });
|
|
5924
|
+
}
|
|
5925
|
+
this.widthPoints = next;
|
|
5926
|
+
this.notify();
|
|
5927
|
+
}
|
|
5928
|
+
/**
|
|
5929
|
+
* Sets one width point numerically: `index` names an existing point of path `nodeId`, null
|
|
5930
|
+
* (or left out) adds one at `values.t`. `left` / `right` are side widths in document units
|
|
5931
|
+
* (how far the stroke reaches on each side of the path), `t` the position along each subpath
|
|
5932
|
+
* (0–1). A point moved onto another one's `t` makes a step there. Selects the point and
|
|
5933
|
+
* refreshes an open width-point editor; one undo step. Ported from ArtCraft vectorcraft's
|
|
5934
|
+
* Width Point Edit dialog (`crates/tools/src/distort/width.rs`, MIT OR Apache-2.0).
|
|
5935
|
+
*/
|
|
5936
|
+
setWidthPoint(target, values) {
|
|
5937
|
+
const node = this.getNode(target.nodeId);
|
|
5938
|
+
if (_optionalChain([node, 'optionalAccess', _161 => _161.type]) !== "path") return { ok: false, message: "There is no such path." };
|
|
5939
|
+
const g = vectorStrokeWidthGeometry(node);
|
|
5940
|
+
if (!g || !(g.unit > 0)) return { ok: false, message: "The path has no visible stroke." };
|
|
5941
|
+
const num = (v2) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : void 0;
|
|
5942
|
+
const t = num(values.t);
|
|
5943
|
+
const left = num(values.left);
|
|
5944
|
+
const right = num(values.right);
|
|
5945
|
+
const scale = (w) => w === void 0 ? void 0 : Math.max(0, w) / g.unit;
|
|
5946
|
+
const profile = sanitizeWidthProfile(node.strokeWidthProfile);
|
|
5947
|
+
let points;
|
|
5948
|
+
let index;
|
|
5949
|
+
const existing = _nullishCoalesce(target.index, () => ( null));
|
|
5950
|
+
if (existing === null) {
|
|
5951
|
+
if (t === void 0) return { ok: false, message: "A new width point needs a position t." };
|
|
5952
|
+
if (profile.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) return { ok: false, message: "The profile has too many width points." };
|
|
5953
|
+
({ points, index } = insertWidthPoint(profile.length ? profile : null, t, { left: scale(left), right: scale(right) }, { step: true }));
|
|
5954
|
+
} else {
|
|
5955
|
+
const p = profile[existing];
|
|
5956
|
+
if (!p) return { ok: false, message: "There is no such width point." };
|
|
5957
|
+
const sides2 = { left: _nullishCoalesce(scale(left), () => ( p.left)), right: _nullishCoalesce(scale(right), () => ( p.right)) };
|
|
5958
|
+
const tt = t === void 0 ? p.t : Math.min(Math.max(t, 0), 1);
|
|
5959
|
+
if (Math.abs(tt - p.t) <= VECTOR_WIDTH_SAME_T) {
|
|
5960
|
+
points = setWidthPoint(profile, existing, sides2);
|
|
5961
|
+
index = existing;
|
|
5962
|
+
} else {
|
|
5963
|
+
const rest = removeWidthPoints(profile, [existing]);
|
|
5964
|
+
if (rest.length) ({ points, index } = insertWidthPoint(rest, tt, sides2, { step: true }));
|
|
5965
|
+
else {
|
|
5966
|
+
points = [{ t: tt, ...sides2 }];
|
|
5967
|
+
index = 0;
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
}
|
|
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));
|
|
5972
|
+
const edit = this.widthEdit;
|
|
5973
|
+
this.change("Width Point", () => this.replaceNode(node.id, withStyle(node, { strokeWidthProfile: points })));
|
|
5974
|
+
this.widthPoints = [{ nodeId: node.id, index, subpath }];
|
|
5975
|
+
if (_optionalChain([edit, 'optionalAccess', _168 => _168.nodeId]) === node.id && (edit.index === existing || existing === null)) this.widthEdit = this.widthEditFor(node.id, subpath, index, null);
|
|
5976
|
+
this.notify();
|
|
4891
5977
|
return { ok: true };
|
|
4892
5978
|
}
|
|
5979
|
+
/**
|
|
5980
|
+
* Opens the width-point editor (what a double-click with the width tool does) on a width
|
|
5981
|
+
* point, or on a spot of a stroke (`t` without `index`: applying it adds a point there).
|
|
5982
|
+
* Returns false when the path has no visible stroke.
|
|
5983
|
+
*/
|
|
5984
|
+
openWidthPointEdit(target) {
|
|
5985
|
+
const edit = this.widthEditFor(target.nodeId, _nullishCoalesce(target.subpath, () => ( 0)), _nullishCoalesce(target.index, () => ( null)), _nullishCoalesce(target.t, () => ( null)));
|
|
5986
|
+
if (!edit) return false;
|
|
5987
|
+
this.widthEdit = edit;
|
|
5988
|
+
this.widthPoints = edit.index === null ? [] : [{ nodeId: edit.nodeId, index: edit.index, subpath: edit.subpath }];
|
|
5989
|
+
this.notify();
|
|
5990
|
+
return true;
|
|
5991
|
+
}
|
|
5992
|
+
/** Closes the width-point editor without changing anything. */
|
|
5993
|
+
closeWidthPointEdit() {
|
|
5994
|
+
if (!this.widthEdit) return;
|
|
5995
|
+
this.widthEdit = null;
|
|
5996
|
+
this.notify();
|
|
5997
|
+
}
|
|
5998
|
+
/** Applies `values` to the width point being edited (see `setWidthPoint`) and closes the editor. */
|
|
5999
|
+
commitWidthPointEdit(values) {
|
|
6000
|
+
const edit = this.widthEdit;
|
|
6001
|
+
if (!edit) return { ok: false, message: "No width point is being edited." };
|
|
6002
|
+
const r = this.setWidthPoint({ nodeId: edit.nodeId, index: edit.index }, edit.index === null ? { t: edit.t, ...values } : values);
|
|
6003
|
+
if (r.ok) this.closeWidthPointEdit();
|
|
6004
|
+
return r;
|
|
6005
|
+
}
|
|
6006
|
+
/** The editor's view of point `index` (or of the stroke at `t` when index is null) of `nodeId`. */
|
|
6007
|
+
widthEditFor(nodeId, subpath, index, t) {
|
|
6008
|
+
const node = this.getNode(nodeId);
|
|
6009
|
+
if (_optionalChain([node, 'optionalAccess', _169 => _169.type]) !== "path") return null;
|
|
6010
|
+
const g = vectorStrokeWidthGeometry(node);
|
|
6011
|
+
if (!g) return null;
|
|
6012
|
+
const profile = sanitizeWidthProfile(node.strokeWidthProfile);
|
|
6013
|
+
const p = index === null ? null : profile[index];
|
|
6014
|
+
if (index !== null && !p) return null;
|
|
6015
|
+
const tt = p ? p.t : Math.min(Math.max(_nullishCoalesce(t, () => ( 0)), 0), 1);
|
|
6016
|
+
const sides2 = _nullishCoalesce(p, () => ( widthProfileAt(profile.length ? profile : null, tt)));
|
|
6017
|
+
const sp = Math.min(Math.max(0, subpath), Math.max(0, node.subpaths.length - 1));
|
|
6018
|
+
const at2 = strokePositionPoint(g.line, sp, tt);
|
|
6019
|
+
const round = (v2) => Math.round(v2 * 1e6) / 1e6;
|
|
6020
|
+
const q = at2 ? applyMatrix(this.worldOf(nodeId), { x: at2.point[0], y: at2.point[1] }) : { x: 0, y: 0 };
|
|
6021
|
+
const point = { x: round(q.x), y: round(q.y) };
|
|
6022
|
+
return { nodeId, subpath: sp, index, t: round(tt), left: round(sides2.left * g.unit), right: round(sides2.right * g.unit), point };
|
|
6023
|
+
}
|
|
4893
6024
|
/**
|
|
4894
6025
|
* Sets the live corner radius (and optionally the kind) of anchors: `refs`, else the selected
|
|
4895
6026
|
* anchors (direct tool), else every anchor of the `ids` / selected paths. Radius 0 makes the
|
|
@@ -4909,7 +6040,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
4909
6040
|
const refs = this.cornerTargets(options);
|
|
4910
6041
|
if (!refs.length) return { ok: false, message: "Nothing is selected." };
|
|
4911
6042
|
this.change("Corner Kind", () => {
|
|
4912
|
-
this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access',
|
|
6043
|
+
this.doc = this.updateAnchors(this.doc, refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access', _170 => _170.corner, 'optionalAccess', _171 => _171.radius]), () => ( 0)), kind));
|
|
4913
6044
|
});
|
|
4914
6045
|
return { ok: true };
|
|
4915
6046
|
}
|
|
@@ -5048,10 +6179,10 @@ var VectorEditorController = (_class3 = class {
|
|
|
5048
6179
|
return this.selection.filter((id) => {
|
|
5049
6180
|
let covered = false;
|
|
5050
6181
|
const loc = locateVectorNode(this.doc.nodes, id);
|
|
5051
|
-
let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess',
|
|
6182
|
+
let parent = _nullishCoalesce(_optionalChain([loc, 'optionalAccess', _172 => _172.parent]), () => ( null));
|
|
5052
6183
|
while (parent) {
|
|
5053
6184
|
if (this.selection.includes(parent.id)) covered = true;
|
|
5054
|
-
parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call',
|
|
6185
|
+
parent = _nullishCoalesce(_optionalChain([locateVectorNode, 'call', _173 => _173(this.doc.nodes, parent.id), 'optionalAccess', _174 => _174.parent]), () => ( null));
|
|
5055
6186
|
}
|
|
5056
6187
|
return !covered;
|
|
5057
6188
|
});
|
|
@@ -5104,24 +6235,24 @@ var VectorEditorController = (_class3 = class {
|
|
|
5104
6235
|
return this.transform(translateMatrix(dx, dy), { ids: options.ids, label: "Move" });
|
|
5105
6236
|
}
|
|
5106
6237
|
/** The selection frame's centre in document space. */
|
|
5107
|
-
frameCenter(
|
|
5108
|
-
return applyMatrix(rotateMatrix(
|
|
6238
|
+
frameCenter(frame2) {
|
|
6239
|
+
return applyMatrix(rotateMatrix(frame2.angle), { x: frame2.x + frame2.width / 2, y: frame2.y + frame2.height / 2 });
|
|
5109
6240
|
}
|
|
5110
6241
|
/**
|
|
5111
6242
|
* Scales along the selection box's own axes (rotated with the selection when its nodes
|
|
5112
6243
|
* share a rotation) around `origin` (default: the box centre, in document space).
|
|
5113
6244
|
*/
|
|
5114
6245
|
scale(sx, sy = sx, options = {}) {
|
|
5115
|
-
const
|
|
5116
|
-
if (!
|
|
5117
|
-
const origin = applyMatrix(rotateMatrix(-
|
|
5118
|
-
return this.transform(this.inFrame(
|
|
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" });
|
|
5119
6250
|
}
|
|
5120
6251
|
/** Rotates clockwise by `degrees` around `origin` (default: the selection centre). */
|
|
5121
6252
|
rotate(degrees, options = {}) {
|
|
5122
|
-
const
|
|
5123
|
-
if (!
|
|
5124
|
-
const origin = _nullishCoalesce(options.origin, () => ( this.frameCenter(
|
|
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)));
|
|
5125
6256
|
return this.transform(rotateMatrix(degrees, origin), { ids: options.ids, label: "Rotate" });
|
|
5126
6257
|
}
|
|
5127
6258
|
/** Sets each selected (or given) node's rotation to `degrees`, turning it around its own centre. */
|
|
@@ -5130,21 +6261,21 @@ var VectorEditorController = (_class3 = class {
|
|
|
5130
6261
|
if (!ids.length) return { ok: false, message: "Nothing is selected." };
|
|
5131
6262
|
this.change("Set Rotation", () => {
|
|
5132
6263
|
for (const id of ids) {
|
|
5133
|
-
const
|
|
5134
|
-
if (!
|
|
5135
|
-
const delta = normalizeAngle(degrees -
|
|
5136
|
-
if (Math.abs(delta) > 1e-9) this.doc = this.transformed(this.doc, /* @__PURE__ */ new Set([id]), rotateMatrix(delta, this.frameCenter(
|
|
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)));
|
|
5137
6268
|
}
|
|
5138
6269
|
});
|
|
5139
6270
|
return { ok: true };
|
|
5140
6271
|
}
|
|
5141
6272
|
/** Mirrors along the selection box's own axes. */
|
|
5142
6273
|
flip(axis, options = {}) {
|
|
5143
|
-
const
|
|
5144
|
-
if (!
|
|
5145
|
-
const c = { x:
|
|
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 };
|
|
5146
6277
|
const m = axis === "horizontal" ? scaleMatrix(-1, 1, c) : scaleMatrix(1, -1, c);
|
|
5147
|
-
return this.transform(this.inFrame(
|
|
6278
|
+
return this.transform(this.inFrame(frame2.angle, m), { ids: options.ids, label: axis === "horizontal" ? "Flip Horizontal" : "Flip Vertical" });
|
|
5148
6279
|
}
|
|
5149
6280
|
/**
|
|
5150
6281
|
* Aligns the selected nodes to their combined bounds, or to the artboard when one node is
|
|
@@ -5234,8 +6365,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
5234
6365
|
if (!ids.length) return null;
|
|
5235
6366
|
const locs = ids.map((id2) => locateVectorNode(this.doc.nodes, id2));
|
|
5236
6367
|
if (locs.some((l) => !l)) return null;
|
|
5237
|
-
const parent = _nullishCoalesce(_optionalChain([locs, 'access',
|
|
5238
|
-
if (locs.some((l) => (_nullishCoalesce(_optionalChain([l, 'access',
|
|
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;
|
|
5239
6370
|
const id = this.newId();
|
|
5240
6371
|
this.change("Group", () => {
|
|
5241
6372
|
const set = new Set(ids);
|
|
@@ -5258,7 +6389,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5258
6389
|
}
|
|
5259
6390
|
/** Replaces selected groups with their children. */
|
|
5260
6391
|
ungroup(options = {}) {
|
|
5261
|
-
const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess',
|
|
6392
|
+
const groups = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess', _180 => _180.type]) === "group");
|
|
5262
6393
|
if (!groups.length) return { ok: false, message: "Select a group to ungroup." };
|
|
5263
6394
|
const set = new Set(groups.map((g) => g.id));
|
|
5264
6395
|
this.change("Ungroup", () => {
|
|
@@ -5271,7 +6402,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5271
6402
|
const opacity = n.opacity;
|
|
5272
6403
|
const m = nodeMatrix(n);
|
|
5273
6404
|
out.push(
|
|
5274
|
-
...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)
|
|
5275
6406
|
);
|
|
5276
6407
|
} else if (n.type === "group") {
|
|
5277
6408
|
const children = flatten2(n.children);
|
|
@@ -5372,7 +6503,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5372
6503
|
}
|
|
5373
6504
|
/** Splits selected compound paths into one path per subpath. */
|
|
5374
6505
|
breakApart(options = {}) {
|
|
5375
|
-
const paths = (_nullishCoalesce(options.ids, () => ( this.selection))).map((id) => this.getNode(id)).filter((n) => _optionalChain([n, 'optionalAccess',
|
|
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);
|
|
5376
6507
|
if (!paths.length) return { ok: false, message: "Select a compound path to break apart." };
|
|
5377
6508
|
const created = [];
|
|
5378
6509
|
const set = new Set(paths.map((p) => p.id));
|
|
@@ -5435,10 +6566,177 @@ var VectorEditorController = (_class3 = class {
|
|
|
5435
6566
|
return nodes.map((n) => n.id);
|
|
5436
6567
|
}
|
|
5437
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
|
+
}
|
|
5438
6736
|
shapeBuilderMap(ids) {
|
|
5439
6737
|
const roots = _nullishCoalesce(ids, () => ( this.selectedRoots()));
|
|
5440
6738
|
const key = roots.join("\0");
|
|
5441
|
-
if (_optionalChain([this, 'access',
|
|
6739
|
+
if (_optionalChain([this, 'access', _195 => _195.builderCache, 'optionalAccess', _196 => _196.doc]) === this.doc && this.builderCache.key === key) return this.builderCache.map;
|
|
5442
6740
|
const entries = roots.length ? vectorPathEntries(this.doc, roots) : [];
|
|
5443
6741
|
const map = entries.length ? vectorShapeBuilderMap(entries) : null;
|
|
5444
6742
|
this.builderCache = { doc: this.doc, key, map };
|
|
@@ -5498,9 +6796,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
5498
6796
|
const list = [];
|
|
5499
6797
|
const r = plan.replace.get(i);
|
|
5500
6798
|
if (r === void 0) list.push(node);
|
|
5501
|
-
else if (_optionalChain([r, 'optionalAccess',
|
|
5502
|
-
else if (_optionalChain([r, 'optionalAccess',
|
|
5503
|
-
else if (_optionalChain([r, 'optionalAccess',
|
|
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") {
|
|
5504
6802
|
r.paths.forEach(
|
|
5505
6803
|
(path, k) => list.push({ ...node, id: k === 0 ? node.id : this.newId(), subpaths: bezierToSubpaths(toLocal(path)).map((sp) => ({ ...sp, closed: false })) })
|
|
5506
6804
|
);
|
|
@@ -5534,7 +6832,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5534
6832
|
}
|
|
5535
6833
|
/** The paint bucket's map of the visible outlines, cached per document and gap. */
|
|
5536
6834
|
fillMap(gap) {
|
|
5537
|
-
if (_optionalChain([this, 'access',
|
|
6835
|
+
if (_optionalChain([this, 'access', _200 => _200.fillCache, 'optionalAccess', _201 => _201.doc]) === this.doc && this.fillCache.gap === gap) return this.fillCache.map;
|
|
5538
6836
|
const map = vectorFillMap(this.doc, gap);
|
|
5539
6837
|
this.fillCache = { doc: this.doc, gap, map };
|
|
5540
6838
|
return map;
|
|
@@ -5548,8 +6846,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
5548
6846
|
*/
|
|
5549
6847
|
fillRegion(point, options = {}) {
|
|
5550
6848
|
if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) return null;
|
|
5551
|
-
const region = _optionalChain([this, 'access',
|
|
5552
|
-
if (!_optionalChain([region, 'optionalAccess',
|
|
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;
|
|
5553
6851
|
let below = null;
|
|
5554
6852
|
const roots = new Set(this.selectedRoots());
|
|
5555
6853
|
walkVectorNodes(this.doc.nodes, (n) => {
|
|
@@ -5575,7 +6873,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5575
6873
|
/** Updates the Shape Builder / paint bucket highlight for the pointer at `p`; true when it changed. */
|
|
5576
6874
|
hoverBuilder(p, alt) {
|
|
5577
6875
|
if (this.tool === "paintBucket") {
|
|
5578
|
-
const region = _nullishCoalesce(_optionalChain([this, 'access',
|
|
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));
|
|
5579
6877
|
const key2 = region ? _chunkVNYRI265cjs.bezierPathToSvg.call(void 0, region, { precision: 2 }) : null;
|
|
5580
6878
|
if (key2 === this.builderHoverKey) return false;
|
|
5581
6879
|
this.builderHoverKey = key2;
|
|
@@ -5683,7 +6981,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5683
6981
|
/** `doc` with anchor `ref` moved to document point `to` by a Curvature-tool move. */
|
|
5684
6982
|
curvatureMoved(doc, ref, to) {
|
|
5685
6983
|
const node = findVectorNode(doc.nodes, ref.nodeId);
|
|
5686
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
6984
|
+
if (_optionalChain([node, 'optionalAccess', _211 => _211.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
|
|
5687
6985
|
const q = this.toLocal(ref.nodeId, to, doc);
|
|
5688
6986
|
const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? curvatureMoveAnchor(sp, ref.index, q) : sp);
|
|
5689
6987
|
const nodes = mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n);
|
|
@@ -5692,7 +6990,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5692
6990
|
/** `doc` with segment `ref` bent through document point `p` at parameter `t`. */
|
|
5693
6991
|
bent(doc, ref, t, p) {
|
|
5694
6992
|
const node = findVectorNode(doc.nodes, ref.nodeId);
|
|
5695
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
6993
|
+
if (_optionalChain([node, 'optionalAccess', _212 => _212.type]) !== "path" || node.locked || !node.subpaths[ref.subpath]) return doc;
|
|
5696
6994
|
const q = this.toLocal(ref.nodeId, p, doc);
|
|
5697
6995
|
const subpaths = node.subpaths.map((sp, i) => i === ref.subpath ? bendSegment(sp, ref.segment, t, q) : sp);
|
|
5698
6996
|
return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === ref.nodeId ? { ...node, subpaths } : n) };
|
|
@@ -5703,7 +7001,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5703
7001
|
*/
|
|
5704
7002
|
bendSegment(ref, to, t = 0.5) {
|
|
5705
7003
|
const node = this.getNode(ref.nodeId);
|
|
5706
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
7004
|
+
const sp = _optionalChain([node, 'optionalAccess', _213 => _213.type]) === "path" ? node.subpaths[ref.subpath] : void 0;
|
|
5707
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." };
|
|
5708
7006
|
if (node.locked) return { ok: false, message: "The path is locked." };
|
|
5709
7007
|
this.change("Bend Segment", () => {
|
|
@@ -5738,7 +7036,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5738
7036
|
/** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
|
|
5739
7037
|
insertAnchorAt(nodeId, point) {
|
|
5740
7038
|
const node = this.getNode(nodeId);
|
|
5741
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
7039
|
+
if (_optionalChain([node, 'optionalAccess', _214 => _214.type]) !== "path") return null;
|
|
5742
7040
|
const hit = nearestSegment(node, this.toLocal(nodeId, point));
|
|
5743
7041
|
if (!hit) return null;
|
|
5744
7042
|
const { subpath, index } = insertAnchor(node.subpaths[hit.subpath], hit.segment, hit.t);
|
|
@@ -5782,7 +7080,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5782
7080
|
setClosed(closed, nodeId) {
|
|
5783
7081
|
const targets = nodeId ? (() => {
|
|
5784
7082
|
const n = this.getNode(nodeId);
|
|
5785
|
-
return _optionalChain([n, 'optionalAccess',
|
|
7083
|
+
return _optionalChain([n, 'optionalAccess', _215 => _215.type]) === "path" ? n.subpaths.map((_, s) => ({ nodeId, subpath: s })) : [];
|
|
5786
7084
|
})() : this.anchors.map((r) => ({ nodeId: r.nodeId, subpath: r.subpath }));
|
|
5787
7085
|
if (!targets.length) return { ok: false, message: "Select a path or points." };
|
|
5788
7086
|
this.change(closed ? "Close Path" : "Open Path", () => {
|
|
@@ -5803,7 +7101,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5803
7101
|
this.setDoc(
|
|
5804
7102
|
mapTree(
|
|
5805
7103
|
this.doc.nodes,
|
|
5806
|
-
(n) => n.type === "path" && set.has(n.id) ? { ...n, subpaths: n.subpaths.map(reverseSubpath), ..._optionalChain([n, 'access',
|
|
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
|
|
5807
7105
|
)
|
|
5808
7106
|
);
|
|
5809
7107
|
this.anchors = [];
|
|
@@ -5941,7 +7239,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5941
7239
|
*/
|
|
5942
7240
|
cutPathAt(nodeId, point) {
|
|
5943
7241
|
const node = this.getNode(nodeId);
|
|
5944
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
7242
|
+
if (_optionalChain([node, 'optionalAccess', _218 => _218.type]) !== "path" || node.locked) return null;
|
|
5945
7243
|
const local = this.toLocal(nodeId, point);
|
|
5946
7244
|
const hit = nearestSegment(node, local);
|
|
5947
7245
|
if (!hit) return null;
|
|
@@ -5991,7 +7289,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
5991
7289
|
if (n.visible === false || n.locked || parent && blocked.has(parent.id)) blocked.add(n.id);
|
|
5992
7290
|
});
|
|
5993
7291
|
const visible = (p) => !blocked.has(p.id);
|
|
5994
|
-
if (_optionalChain([ids, 'optionalAccess',
|
|
7292
|
+
if (_optionalChain([ids, 'optionalAccess', _219 => _219.length]) || this.selection.length) return this.pathTargets(_nullishCoalesce(ids, () => ( this.selection))).filter(visible);
|
|
5995
7293
|
const box = polylineBounds(points.map(toPoint2), pad);
|
|
5996
7294
|
if (!box) return [];
|
|
5997
7295
|
const out = [];
|
|
@@ -6077,7 +7375,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
6077
7375
|
finishPath() {
|
|
6078
7376
|
if (!this.pen) return;
|
|
6079
7377
|
const node = this.getNode(this.pen.nodeId);
|
|
6080
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
7378
|
+
const sp = _optionalChain([node, 'optionalAccess', _220 => _220.subpaths, 'access', _221 => _221[this.pen.subpath]]);
|
|
6081
7379
|
const pen = this.pen;
|
|
6082
7380
|
this.pen = null;
|
|
6083
7381
|
this.pointer = null;
|
|
@@ -6121,19 +7419,19 @@ var VectorEditorController = (_class3 = class {
|
|
|
6121
7419
|
if (trimmed.startsWith("{")) {
|
|
6122
7420
|
try {
|
|
6123
7421
|
const data = JSON.parse(trimmed);
|
|
6124
|
-
if (_optionalChain([data, 'optionalAccess',
|
|
6125
|
-
} catch (
|
|
7422
|
+
if (_optionalChain([data, 'optionalAccess', _222 => _222.type]) === VECTOR_CLIPBOARD_TYPE && Array.isArray(data.nodes)) nodes = data.nodes;
|
|
7423
|
+
} catch (e4) {
|
|
6126
7424
|
nodes = null;
|
|
6127
7425
|
}
|
|
6128
7426
|
} else if (/<svg[\s>]/i.test(trimmed)) {
|
|
6129
7427
|
try {
|
|
6130
7428
|
nodes = parseVectorSvg(trimmed).nodes;
|
|
6131
|
-
} catch (
|
|
7429
|
+
} catch (e5) {
|
|
6132
7430
|
nodes = null;
|
|
6133
7431
|
}
|
|
6134
7432
|
}
|
|
6135
7433
|
} else nodes = this.clipboard;
|
|
6136
|
-
if (!_optionalChain([nodes, 'optionalAccess',
|
|
7434
|
+
if (!_optionalChain([nodes, 'optionalAccess', _223 => _223.length])) return [];
|
|
6137
7435
|
const offset = _nullishCoalesce(options.offset, () => ( (text && !text.trim().startsWith("{") ? { x: 0, y: 0 } : { x: 10, y: 10 })));
|
|
6138
7436
|
const copies = nodes.map((n) => transformVectorNode(this.freshIds(n), translateMatrix(offset.x, offset.y)));
|
|
6139
7437
|
this.change("Paste", () => {
|
|
@@ -6173,9 +7471,9 @@ var VectorEditorController = (_class3 = class {
|
|
|
6173
7471
|
}
|
|
6174
7472
|
hitTransformHandle(screen) {
|
|
6175
7473
|
if (this.tool !== "select" || !this.selection.length) return null;
|
|
6176
|
-
const
|
|
6177
|
-
if (!
|
|
6178
|
-
const { box, handles } = getVectorSelectionBoxGeometry(
|
|
7474
|
+
const frame2 = this.frameOf(this.selectedRoots());
|
|
7475
|
+
if (!frame2) return null;
|
|
7476
|
+
const { box, handles } = getVectorSelectionBoxGeometry(frame2, this.viewport);
|
|
6179
7477
|
const r = this.hitRadius + 2;
|
|
6180
7478
|
const usable2 = box.width < 2 * r || box.height < 2 * r ? handles.filter((h) => h.handle.length === 2 || h.handle === "rotate") : handles;
|
|
6181
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;
|
|
@@ -6184,7 +7482,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
6184
7482
|
hitHandle(screen) {
|
|
6185
7483
|
for (const ref of getVisibleHandles(this.view()).reverse()) {
|
|
6186
7484
|
const a = getAnchorWorld(this.doc, ref);
|
|
6187
|
-
const h = _optionalChain([a, 'optionalAccess',
|
|
7485
|
+
const h = _optionalChain([a, 'optionalAccess', _224 => _224[ref.handle]]);
|
|
6188
7486
|
if (!h) continue;
|
|
6189
7487
|
const s = this.documentToScreen(h);
|
|
6190
7488
|
if (Math.hypot(s.x - screen.x, s.y - screen.y) <= this.hitRadius) return ref;
|
|
@@ -6287,6 +7585,11 @@ var VectorEditorController = (_class3 = class {
|
|
|
6287
7585
|
const seg = this.nearestOutline(p);
|
|
6288
7586
|
return seg ? { nodeId: seg.nodeId, segment: seg } : null;
|
|
6289
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
|
+
}
|
|
6290
7593
|
case "width": {
|
|
6291
7594
|
const hit = this.hitWidthHandle(screen);
|
|
6292
7595
|
if (hit) return { nodeId: hit.ref.nodeId, widthPoint: hit.ref };
|
|
@@ -6298,13 +7601,19 @@ var VectorEditorController = (_class3 = class {
|
|
|
6298
7601
|
}
|
|
6299
7602
|
}
|
|
6300
7603
|
widthView() {
|
|
6301
|
-
return { document: this.doc, selection: this.selection, tool: this.tool, hover: this.hover, interaction: this.interaction,
|
|
7604
|
+
return { document: this.doc, selection: this.selection, tool: this.tool, hover: this.hover, interaction: this.interaction, selectedWidthPoints: this.widthPoints };
|
|
6302
7605
|
}
|
|
6303
7606
|
/** The width point centre or side end under `screen` (width tool). */
|
|
6304
7607
|
hitWidthHandle(screen) {
|
|
7608
|
+
const shown = this.hitWidthHandleIn(screen, this.widthView());
|
|
7609
|
+
if (shown) return shown;
|
|
7610
|
+
const spot = this.strokeSpotAt(this.screenToDocument(screen));
|
|
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;
|
|
7612
|
+
}
|
|
7613
|
+
hitWidthHandleIn(screen, view) {
|
|
6305
7614
|
let best = null;
|
|
6306
7615
|
let bestD = Infinity;
|
|
6307
|
-
for (const h of getVectorWidthHandles(
|
|
7616
|
+
for (const h of getVectorWidthHandles(view)) {
|
|
6308
7617
|
for (const [part, q] of [["center", h], ["left", h.left], ["right", h.right]]) {
|
|
6309
7618
|
const s = this.documentToScreen(q);
|
|
6310
7619
|
const d = Math.hypot(s.x - screen.x, s.y - screen.y);
|
|
@@ -6354,14 +7663,13 @@ var VectorEditorController = (_class3 = class {
|
|
|
6354
7663
|
downWidth(screen, p, input) {
|
|
6355
7664
|
const hit = this.hitWidthHandle(screen);
|
|
6356
7665
|
const spot = hit ? null : this.strokeSpotAt(p);
|
|
6357
|
-
const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess',
|
|
7666
|
+
const nodeId = _nullishCoalesce(_optionalChain([hit, 'optionalAccess', _227 => _227.ref, 'access', _228 => _228.nodeId]), () => ( _optionalChain([spot, 'optionalAccess', _229 => _229.nodeId])));
|
|
6358
7667
|
const node = nodeId ? this.getNode(nodeId) : void 0;
|
|
6359
|
-
const g = _optionalChain([node, 'optionalAccess',
|
|
7668
|
+
const g = _optionalChain([node, 'optionalAccess', _230 => _230.type]) === "path" ? vectorStrokeWidthGeometry(node) : null;
|
|
7669
|
+
if (this.widthEdit) this.widthEdit = null;
|
|
6360
7670
|
if (!node || node.type !== "path" || !g) {
|
|
6361
|
-
if (
|
|
6362
|
-
|
|
6363
|
-
this.notify();
|
|
6364
|
-
}
|
|
7671
|
+
if (!input.shift) this.widthPoints = [];
|
|
7672
|
+
this.notify();
|
|
6365
7673
|
return;
|
|
6366
7674
|
}
|
|
6367
7675
|
const before = this.snapshot();
|
|
@@ -6370,15 +7678,29 @@ var VectorEditorController = (_class3 = class {
|
|
|
6370
7678
|
let ref;
|
|
6371
7679
|
let mode = "width";
|
|
6372
7680
|
let side = "both";
|
|
7681
|
+
let group = [];
|
|
7682
|
+
let shift = false;
|
|
6373
7683
|
let t;
|
|
6374
7684
|
let subpath;
|
|
6375
7685
|
if (hit) {
|
|
6376
7686
|
profile = sanitizeWidthProfile(node.strokeWidthProfile);
|
|
6377
7687
|
ref = { nodeId: node.id, index: hit.ref.index, subpath: hit.ref.subpath };
|
|
6378
|
-
t = _nullishCoalesce(_optionalChain([profile, 'access',
|
|
7688
|
+
t = _nullishCoalesce(_optionalChain([profile, 'access', _231 => _231[ref.index], 'optionalAccess', _232 => _232.t]), () => ( 0));
|
|
6379
7689
|
subpath = hit.ref.subpath;
|
|
6380
|
-
if (hit.ref.part === "center") mode = "move";
|
|
7690
|
+
if (hit.ref.part === "center") mode = input.alt && profile.length < VECTOR_WIDTH_PROFILE_MAX_POINTS ? "copy" : "move";
|
|
6381
7691
|
else if (input.alt) side = hit.ref.part;
|
|
7692
|
+
const same = this.widthPoints.length > 0 && this.widthPoints[0].nodeId === node.id;
|
|
7693
|
+
const selected = same && this.widthPoints.some((r) => r.index === ref.index);
|
|
7694
|
+
if (input.shift && mode !== "copy") {
|
|
7695
|
+
if (selected) {
|
|
7696
|
+
this.widthPoints = this.widthPoints.filter((r) => r.index !== ref.index);
|
|
7697
|
+
this.notify();
|
|
7698
|
+
return;
|
|
7699
|
+
}
|
|
7700
|
+
this.widthPoints = same ? [...this.widthPoints, ref] : [ref];
|
|
7701
|
+
shift = true;
|
|
7702
|
+
} else if (mode === "copy" || !selected) this.widthPoints = [ref];
|
|
7703
|
+
if (mode !== "copy") group = this.widthPoints.filter((r) => r.index !== ref.index).map((r) => r.index);
|
|
6382
7704
|
} else {
|
|
6383
7705
|
const near2 = nearestStrokePosition(g.line, [local.x, local.y]);
|
|
6384
7706
|
const inserted = insertWidthPoint(strokeWidthProfileOf(node), near2.t);
|
|
@@ -6387,17 +7709,21 @@ var VectorEditorController = (_class3 = class {
|
|
|
6387
7709
|
t = near2.t;
|
|
6388
7710
|
ref = { nodeId: node.id, index: inserted.index, subpath };
|
|
6389
7711
|
if (input.alt) side = near2.side;
|
|
7712
|
+
this.widthPoints = [ref];
|
|
6390
7713
|
}
|
|
6391
7714
|
const at2 = strokePositionPoint(g.line, subpath, t);
|
|
6392
7715
|
if (!at2) return;
|
|
6393
7716
|
const base = { ...this.doc, nodes: mapTree(this.doc.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
|
|
6394
7717
|
this.doc = base;
|
|
6395
|
-
this.widthPoint = { nodeId: node.id, index: ref.index, subpath };
|
|
6396
7718
|
this.drag = {
|
|
6397
7719
|
kind: "width",
|
|
6398
7720
|
ref,
|
|
6399
7721
|
mode,
|
|
6400
7722
|
side,
|
|
7723
|
+
source: ref.index,
|
|
7724
|
+
group,
|
|
7725
|
+
shift,
|
|
7726
|
+
renumber: null,
|
|
6401
7727
|
origin: { x: at2.point[0], y: at2.point[1] },
|
|
6402
7728
|
normal: { x: at2.tangent[1], y: -at2.tangent[0] },
|
|
6403
7729
|
unit: g.unit,
|
|
@@ -6412,6 +7738,31 @@ var VectorEditorController = (_class3 = class {
|
|
|
6412
7738
|
this.interaction = { type: "width", ref, mode, width: (w.left + w.right) * g.unit };
|
|
6413
7739
|
this.notify(!hit);
|
|
6414
7740
|
}
|
|
7741
|
+
/**
|
|
7742
|
+
* A width-tool slide to `t` on the dragged point's subpath lands exactly on another width
|
|
7743
|
+
* point (not `skip`) within the pick distance on screen: dropping a point onto another makes
|
|
7744
|
+
* a discontinuous point (two points at one `t`, the width steps there).
|
|
7745
|
+
*/
|
|
7746
|
+
snapWidthT(drag, node, line2, t, skip) {
|
|
7747
|
+
const m = this.worldOf(node.id, drag.base);
|
|
7748
|
+
const at2 = strokePositionPoint(line2, drag.ref.subpath, t);
|
|
7749
|
+
if (!at2) return t;
|
|
7750
|
+
const here = this.documentToScreen(applyMatrix(m, { x: at2.point[0], y: at2.point[1] }));
|
|
7751
|
+
let best = t;
|
|
7752
|
+
let bestD = this.hitRadius;
|
|
7753
|
+
drag.profile.forEach((w, i) => {
|
|
7754
|
+
if (i === skip || drag.group.includes(i)) return;
|
|
7755
|
+
const q = strokePositionPoint(line2, drag.ref.subpath, w.t);
|
|
7756
|
+
if (!q) return;
|
|
7757
|
+
const s = this.documentToScreen(applyMatrix(m, { x: q.point[0], y: q.point[1] }));
|
|
7758
|
+
const d = Math.hypot(s.x - here.x, s.y - here.y);
|
|
7759
|
+
if (d <= bestD) {
|
|
7760
|
+
best = w.t;
|
|
7761
|
+
bestD = d;
|
|
7762
|
+
}
|
|
7763
|
+
});
|
|
7764
|
+
return best;
|
|
7765
|
+
}
|
|
6415
7766
|
/** The nearest outline point of any visible, unlocked path within the pick distance of document point `p`. */
|
|
6416
7767
|
nearestOutline(p) {
|
|
6417
7768
|
let best = null;
|
|
@@ -6465,6 +7816,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
6465
7816
|
case "shapeBuilder":
|
|
6466
7817
|
case "paintBucket":
|
|
6467
7818
|
return this.downBuilder(p, input);
|
|
7819
|
+
case "blend":
|
|
7820
|
+
return this.downBlend(screen, p);
|
|
6468
7821
|
case "scissors": {
|
|
6469
7822
|
const seg = this.nearestOutline(p);
|
|
6470
7823
|
if (seg) this.cutPathAt(seg.nodeId, seg.point);
|
|
@@ -6480,13 +7833,53 @@ var VectorEditorController = (_class3 = class {
|
|
|
6480
7833
|
return this.downShape(p, input);
|
|
6481
7834
|
}
|
|
6482
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
|
+
}
|
|
6483
7876
|
downSelect(screen, p, input) {
|
|
6484
7877
|
const handle = this.hitTransformHandle(screen);
|
|
6485
7878
|
if (handle) {
|
|
6486
|
-
const
|
|
7879
|
+
const frame2 = this.frameOf(this.selectedRoots());
|
|
6487
7880
|
const before = this.snapshot();
|
|
6488
|
-
const center = applyMatrix(rotateMatrix(
|
|
6489
|
-
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 };
|
|
6490
7883
|
this.interaction = handle === "rotate" ? { type: "rotate", angle: 0 } : { type: "scale", handle, sx: 1, sy: 1 };
|
|
6491
7884
|
this.notify();
|
|
6492
7885
|
return;
|
|
@@ -6600,7 +7993,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
6600
7993
|
}
|
|
6601
7994
|
downPen(screen, p, input) {
|
|
6602
7995
|
const before = this.snapshot();
|
|
6603
|
-
const sp = this.pen ? _optionalChain([this, 'access',
|
|
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;
|
|
6604
7997
|
if (this.pen && sp) {
|
|
6605
7998
|
const pen = this.pen;
|
|
6606
7999
|
const first = { nodeId: pen.nodeId, subpath: pen.subpath, index: 0 };
|
|
@@ -6862,7 +8255,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
6862
8255
|
}
|
|
6863
8256
|
case "shape": {
|
|
6864
8257
|
const node = this.getNode(drag.nodeId);
|
|
6865
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
8258
|
+
if (_optionalChain([node, 'optionalAccess', _240 => _240.type]) !== "path") return;
|
|
6866
8259
|
const leave = { at: drag.start, constrain: input.shift ? drag.tool === "line" ? "angle" : "square" : null, curves: false };
|
|
6867
8260
|
let end = this.snapDrawn(p, leave, input, () => drag.targets ??= this.drawingTargets(null));
|
|
6868
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);
|
|
@@ -6896,19 +8289,35 @@ var VectorEditorController = (_class3 = class {
|
|
|
6896
8289
|
drag.moved = true;
|
|
6897
8290
|
const node = findVectorNode(drag.base.nodes, drag.ref.nodeId);
|
|
6898
8291
|
const local = this.toLocal(node.id, p, drag.base);
|
|
8292
|
+
const src = drag.profile[drag.source];
|
|
6899
8293
|
let profile;
|
|
6900
|
-
|
|
8294
|
+
let index = drag.source;
|
|
8295
|
+
if (drag.mode !== "width") {
|
|
6901
8296
|
const g = vectorStrokeWidthGeometry(node);
|
|
6902
8297
|
const near2 = g ? nearestStrokePosition(g.line, [local.x, local.y], drag.ref.subpath) : null;
|
|
6903
|
-
if (!near2) return;
|
|
6904
|
-
|
|
8298
|
+
if (!g || !near2) return;
|
|
8299
|
+
const t = drag.group.length ? near2.t : this.snapWidthT(drag, node, g.line, near2.t, drag.source);
|
|
8300
|
+
if (drag.mode === "copy") {
|
|
8301
|
+
({ points: profile, index } = insertWidthPoint(drag.profile, t, { left: src.left, right: src.right }, { step: true }));
|
|
8302
|
+
} else if (drag.group.length) {
|
|
8303
|
+
const shifted = shiftWidthPoints(drag.profile, [drag.source, ...drag.group], t - src.t);
|
|
8304
|
+
profile = shifted.points;
|
|
8305
|
+
drag.renumber = shifted.renumber;
|
|
8306
|
+
index = _nullishCoalesce(shifted.renumber.get(drag.source), () => ( drag.source));
|
|
8307
|
+
} else profile = setWidthPoint(drag.profile, drag.source, { t });
|
|
6905
8308
|
} else {
|
|
6906
8309
|
const off = ((local.x - drag.origin.x) * drag.normal.x + (local.y - drag.origin.y) * drag.normal.y) / drag.unit;
|
|
6907
8310
|
const patch = drag.side === "both" ? { left: Math.abs(off), right: Math.abs(off) } : drag.side === "left" ? { left: Math.max(0, off) } : { right: Math.max(0, -off) };
|
|
6908
|
-
profile = setWidthPoint(drag.profile, drag.
|
|
8311
|
+
profile = setWidthPoint(drag.profile, drag.source, patch);
|
|
8312
|
+
if (drag.group.length) {
|
|
8313
|
+
const dl = profile[drag.source].left - src.left;
|
|
8314
|
+
const dr = profile[drag.source].right - src.right;
|
|
8315
|
+
for (const i of drag.group) profile = setWidthPoint(profile, i, { left: Math.max(0, drag.profile[i].left + dl), right: Math.max(0, drag.profile[i].right + dr) });
|
|
8316
|
+
}
|
|
6909
8317
|
}
|
|
6910
8318
|
this.doc = { ...drag.base, nodes: mapTree(drag.base.nodes, (n) => n.id === node.id ? { ...n, strokeWidthProfile: profile } : n) };
|
|
6911
|
-
|
|
8319
|
+
drag.ref = { ...drag.ref, index };
|
|
8320
|
+
const w = profile[index];
|
|
6912
8321
|
this.interaction = { type: "width", ref: drag.ref, mode: drag.mode, width: (w.left + w.right) * drag.unit };
|
|
6913
8322
|
this.notify(true);
|
|
6914
8323
|
return;
|
|
@@ -6920,14 +8329,14 @@ var VectorEditorController = (_class3 = class {
|
|
|
6920
8329
|
for (const q of drag.lazy.push([p.x, p.y])) {
|
|
6921
8330
|
if (Math.hypot(q[0] - last[0], q[1] - last[1]) * this.viewport.zoom < 1.5) continue;
|
|
6922
8331
|
drag.points.push(q);
|
|
6923
|
-
_optionalChain([drag, 'access',
|
|
8332
|
+
_optionalChain([drag, 'access', _241 => _241.pressures, 'optionalAccess', _242 => _242.push, 'call', _243 => _243(drag.pressure)]);
|
|
6924
8333
|
moved = true;
|
|
6925
8334
|
}
|
|
6926
8335
|
const string = drag.lazy.string();
|
|
6927
8336
|
const node = this.getNode(drag.nodeId);
|
|
6928
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
8337
|
+
if (_optionalChain([node, 'optionalAccess', _244 => _244.type]) !== "path") return;
|
|
6929
8338
|
if (!moved) {
|
|
6930
|
-
if (string && _optionalChain([this, 'access',
|
|
8339
|
+
if (string && _optionalChain([this, 'access', _245 => _245.interaction, 'optionalAccess', _246 => _246.type]) === "draw") {
|
|
6931
8340
|
this.interaction = { ...this.interaction, string: [{ x: string[0][0], y: string[0][1] }, { x: string[1][0], y: string[1][1] }] };
|
|
6932
8341
|
this.notify();
|
|
6933
8342
|
}
|
|
@@ -6963,8 +8372,8 @@ var VectorEditorController = (_class3 = class {
|
|
|
6963
8372
|
}
|
|
6964
8373
|
this.setGuides(res);
|
|
6965
8374
|
return {
|
|
6966
|
-
dx: _optionalChain([res, 'optionalAccess',
|
|
6967
|
-
dy: _optionalChain([res, 'optionalAccess',
|
|
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
|
|
6968
8377
|
};
|
|
6969
8378
|
}
|
|
6970
8379
|
/** The anchor a point drag snaps by, where it started: the grabbed one, else the moving one nearest the press. */
|
|
@@ -7043,7 +8452,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
7043
8452
|
if (drag.moved) return this.commit("Corner Radius", drag.before);
|
|
7044
8453
|
if (drag.alt) {
|
|
7045
8454
|
const kind = nextVectorCornerKind(drag.widget.kind);
|
|
7046
|
-
this.doc = this.updateAnchors(drag.base, drag.refs, (a) => setAnchorCorner(a, _nullishCoalesce(_optionalChain([a, 'access',
|
|
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));
|
|
7047
8456
|
return this.commit("Corner Kind", drag.before);
|
|
7048
8457
|
}
|
|
7049
8458
|
const ref = drag.widget.ref;
|
|
@@ -7054,12 +8463,23 @@ var VectorEditorController = (_class3 = class {
|
|
|
7054
8463
|
return;
|
|
7055
8464
|
}
|
|
7056
8465
|
case "width": {
|
|
7057
|
-
if (drag.moved)
|
|
8466
|
+
if (drag.moved) {
|
|
8467
|
+
const several = drag.group.length > 0;
|
|
8468
|
+
if (drag.mode === "copy") this.widthPoints = [drag.ref];
|
|
8469
|
+
else if (drag.mode === "move" && several) {
|
|
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)) }));
|
|
8471
|
+
} else if (!several) this.widthPoints = [drag.ref];
|
|
8472
|
+
const label = drag.mode === "copy" ? "Copy Width Point" : drag.mode === "move" ? several ? "Move Width Points" : "Move Width Point" : several ? "Width Points" : "Width Point";
|
|
8473
|
+
return this.commit(label, drag.before);
|
|
8474
|
+
}
|
|
7058
8475
|
if (drag.created) {
|
|
7059
8476
|
this.doc = drag.before.doc;
|
|
7060
|
-
this.
|
|
8477
|
+
this.widthPoints = [];
|
|
7061
8478
|
this.notify(true);
|
|
7062
|
-
} else
|
|
8479
|
+
} else {
|
|
8480
|
+
if (!drag.shift && drag.group.length) this.widthPoints = [drag.ref];
|
|
8481
|
+
this.notify();
|
|
8482
|
+
}
|
|
7063
8483
|
return;
|
|
7064
8484
|
}
|
|
7065
8485
|
case "builder":
|
|
@@ -7089,14 +8509,14 @@ var VectorEditorController = (_class3 = class {
|
|
|
7089
8509
|
}
|
|
7090
8510
|
case "pencil": {
|
|
7091
8511
|
const node = this.getNode(drag.nodeId);
|
|
7092
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
8512
|
+
if (_optionalChain([node, 'optionalAccess', _254 => _254.type]) !== "path" || drag.points.length < 2) {
|
|
7093
8513
|
this.doc = drag.before.doc;
|
|
7094
8514
|
this.notify(true);
|
|
7095
8515
|
return;
|
|
7096
8516
|
}
|
|
7097
8517
|
for (const q of drag.lazy.finish()) {
|
|
7098
8518
|
drag.points.push(q);
|
|
7099
|
-
_optionalChain([drag, 'access',
|
|
8519
|
+
_optionalChain([drag, 'access', _255 => _255.pressures, 'optionalAccess', _256 => _256.push, 'call', _257 => _257(drag.pressure)]);
|
|
7100
8520
|
}
|
|
7101
8521
|
const shape = this.editing.recognizeShapes ? recognizeInkShape(drag.points, { minSize: 6 / this.viewport.zoom }) : null;
|
|
7102
8522
|
if (shape) {
|
|
@@ -7148,7 +8568,10 @@ var VectorEditorController = (_class3 = class {
|
|
|
7148
8568
|
}
|
|
7149
8569
|
this.restore(drag.before);
|
|
7150
8570
|
}
|
|
7151
|
-
/**
|
|
8571
|
+
/**
|
|
8572
|
+
* Double click: select tool enters path editing, direct tool toggles an anchor or adds one,
|
|
8573
|
+
* width tool opens the width-point editor (`widthPointEdit`).
|
|
8574
|
+
*/
|
|
7152
8575
|
doubleClick(screen) {
|
|
7153
8576
|
const p = this.screenToDocument(screen);
|
|
7154
8577
|
if (this.tool === "select") {
|
|
@@ -7168,7 +8591,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
7168
8591
|
this.convertAnchors(a.in || a.out ? "corner" : "smooth", [anchor]);
|
|
7169
8592
|
return;
|
|
7170
8593
|
}
|
|
7171
|
-
const id = _nullishCoalesce(_optionalChain([this, 'access',
|
|
8594
|
+
const id = _nullishCoalesce(_optionalChain([this, 'access', _258 => _258.hover, 'optionalAccess', _259 => _259.segment, 'optionalAccess', _260 => _260.nodeId]), () => ( this.editingId));
|
|
7172
8595
|
if (id) {
|
|
7173
8596
|
const node = this.getNode(id);
|
|
7174
8597
|
const seg = node ? nearestSegment(node, this.toLocal(id, p)) : null;
|
|
@@ -7176,6 +8599,16 @@ var VectorEditorController = (_class3 = class {
|
|
|
7176
8599
|
}
|
|
7177
8600
|
return;
|
|
7178
8601
|
}
|
|
8602
|
+
if (this.tool === "width") {
|
|
8603
|
+
const hit = this.hitWidthHandle(screen);
|
|
8604
|
+
if (hit) {
|
|
8605
|
+
this.openWidthPointEdit(hit.ref);
|
|
8606
|
+
return;
|
|
8607
|
+
}
|
|
8608
|
+
const spot = this.strokeSpotAt(p);
|
|
8609
|
+
if (spot) this.openWidthPointEdit({ nodeId: spot.nodeId, subpath: spot.subpath, t: spot.t });
|
|
8610
|
+
return;
|
|
8611
|
+
}
|
|
7179
8612
|
if (this.tool === "pen") this.finishPath();
|
|
7180
8613
|
}
|
|
7181
8614
|
// -------------------------------------------------------------------------
|
|
@@ -7236,11 +8669,15 @@ var VectorEditorController = (_class3 = class {
|
|
|
7236
8669
|
switch (key) {
|
|
7237
8670
|
case "Escape":
|
|
7238
8671
|
if (this.drag) this.cancelDrag();
|
|
7239
|
-
else if (this.
|
|
7240
|
-
|
|
8672
|
+
else if (this.widthEdit) this.closeWidthPointEdit();
|
|
8673
|
+
else if (this.tool === "width" && this.widthPoints.length) {
|
|
8674
|
+
this.widthPoints = [];
|
|
7241
8675
|
this.notify();
|
|
7242
8676
|
} else if (this.pen) this.finishPath();
|
|
7243
|
-
else if (this.
|
|
8677
|
+
else if (this.blendStart) {
|
|
8678
|
+
this.blendStart = null;
|
|
8679
|
+
this.notify();
|
|
8680
|
+
} else if (this.tool === "direct" && this.anchors.length) {
|
|
7244
8681
|
this.anchors = [];
|
|
7245
8682
|
this.notify();
|
|
7246
8683
|
} else if (this.tool !== "select") this.setTool("select");
|
|
@@ -7251,7 +8688,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
7251
8688
|
this.finishPath();
|
|
7252
8689
|
return true;
|
|
7253
8690
|
}
|
|
7254
|
-
if (this.tool === "select" && this.selection.length === 1 && _optionalChain([this, 'access',
|
|
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") {
|
|
7255
8692
|
this.tool = "direct";
|
|
7256
8693
|
this.editingId = this.selection[0];
|
|
7257
8694
|
this.notify();
|
|
@@ -7260,7 +8697,7 @@ var VectorEditorController = (_class3 = class {
|
|
|
7260
8697
|
return false;
|
|
7261
8698
|
case "Delete":
|
|
7262
8699
|
case "Backspace":
|
|
7263
|
-
if (this.tool === "width" && this.
|
|
8700
|
+
if (this.tool === "width" && this.widthPoints.length) return this.removeWidthPoints().ok;
|
|
7264
8701
|
if (this.pen) {
|
|
7265
8702
|
const node = this.getNode(this.pen.nodeId);
|
|
7266
8703
|
const sp = node.subpaths[this.pen.subpath];
|
|
@@ -7394,7 +8831,7 @@ function renderNode(node, defs, idPrefix) {
|
|
|
7394
8831
|
strokeWidth: _nullishCoalesce(node.strokeWidth, () => ( 1)),
|
|
7395
8832
|
...node.strokeLinecap ? { strokeLinecap: node.strokeLinecap } : {},
|
|
7396
8833
|
...node.strokeLinejoin ? { strokeLinejoin: node.strokeLinejoin } : {},
|
|
7397
|
-
..._optionalChain([node, 'access',
|
|
8834
|
+
..._optionalChain([node, 'access', _264 => _264.strokeDasharray, 'optionalAccess', _265 => _265.length]) ? { strokeDasharray: node.strokeDasharray.join(" ") } : {},
|
|
7398
8835
|
fillRule: _nullishCoalesce(node.fillRule, () => ( "nonzero")),
|
|
7399
8836
|
opacity: _nullishCoalesce(node.opacity, () => ( 1))
|
|
7400
8837
|
};
|
|
@@ -7443,9 +8880,9 @@ function getVectorOverlayRenderData(state) {
|
|
|
7443
8880
|
};
|
|
7444
8881
|
for (const id of state.selection) outline(id, true);
|
|
7445
8882
|
if (state.editingId) outline(state.editingId, true);
|
|
7446
|
-
if (_optionalChain([state, 'access',
|
|
8883
|
+
if (_optionalChain([state, 'access', _266 => _266.hover, 'optionalAccess', _267 => _267.nodeId]) && !state.interaction) outline(state.hover.nodeId, false);
|
|
7447
8884
|
let selectionBox = null;
|
|
7448
|
-
if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access',
|
|
8885
|
+
if (state.tool === "select" && state.selectionFrame && _optionalChain([state, 'access', _268 => _268.interaction, 'optionalAccess', _269 => _269.type]) !== "marquee") {
|
|
7449
8886
|
const g = getVectorSelectionBoxGeometry(state.selectionFrame, state.viewport);
|
|
7450
8887
|
selectionBox = { ...g.box, angle: g.angle, transform: g.transform, corners: g.corners, handles: g.handles };
|
|
7451
8888
|
}
|
|
@@ -7455,9 +8892,9 @@ function getVectorOverlayRenderData(state) {
|
|
|
7455
8892
|
const a = getAnchorWorld(doc, ref);
|
|
7456
8893
|
const s = toScreen(state, a);
|
|
7457
8894
|
const closes = !!state.pen && state.pen.nodeId === ref.nodeId && state.pen.subpath === ref.subpath && ref.index === 0;
|
|
7458
|
-
return { ref, x: s.x, y: s.y, type: _nullishCoalesce(a.type, () => ( inferAnchorType(a))), selected: selected(ref), hovered: sameAnchor(_optionalChain([state, 'access',
|
|
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 };
|
|
7459
8896
|
});
|
|
7460
|
-
if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access',
|
|
8897
|
+
if (state.tool === "pen" && !state.pen && _optionalChain([state, 'access', _272 => _272.hover, 'optionalAccess', _273 => _273.anchor])) {
|
|
7461
8898
|
const a = getAnchorWorld(doc, state.hover.anchor);
|
|
7462
8899
|
if (a) {
|
|
7463
8900
|
const s = toScreen(state, a);
|
|
@@ -7466,15 +8903,15 @@ function getVectorOverlayRenderData(state) {
|
|
|
7466
8903
|
}
|
|
7467
8904
|
const handles = getVisibleHandles(state).flatMap((ref) => {
|
|
7468
8905
|
const a = getAnchorWorld(doc, ref);
|
|
7469
|
-
const h = _optionalChain([a, 'optionalAccess',
|
|
8906
|
+
const h = _optionalChain([a, 'optionalAccess', _274 => _274[ref.handle]]);
|
|
7470
8907
|
if (!a || !h) return [];
|
|
7471
8908
|
const s = toScreen(state, h);
|
|
7472
8909
|
const sa = toScreen(state, a);
|
|
7473
|
-
const hovered = !!_optionalChain([state, 'access',
|
|
8910
|
+
const hovered = !!_optionalChain([state, 'access', _275 => _275.hover, 'optionalAccess', _276 => _276.handle]) && sameAnchor(state.hover.handle, ref) && state.hover.handle.handle === ref.handle;
|
|
7474
8911
|
return [{ ref, x: s.x, y: s.y, anchorX: sa.x, anchorY: sa.y, hovered }];
|
|
7475
8912
|
});
|
|
7476
8913
|
let marquee = null;
|
|
7477
|
-
if (_optionalChain([state, 'access',
|
|
8914
|
+
if (_optionalChain([state, 'access', _277 => _277.interaction, 'optionalAccess', _278 => _278.type]) === "marquee") {
|
|
7478
8915
|
const b = state.interaction.bounds;
|
|
7479
8916
|
const tl = toScreen(state, b);
|
|
7480
8917
|
marquee = { x: tl.x, y: tl.y, width: b.width * z, height: b.height * z };
|
|
@@ -7482,7 +8919,7 @@ function getVectorOverlayRenderData(state) {
|
|
|
7482
8919
|
let penPreview = null;
|
|
7483
8920
|
if (state.tool === "pen" && state.pen && state.pointer && !state.interaction) {
|
|
7484
8921
|
const node = findVectorNode(doc.nodes, state.pen.nodeId);
|
|
7485
|
-
const sp = _optionalChain([node, 'optionalAccess',
|
|
8922
|
+
const sp = _optionalChain([node, 'optionalAccess', _279 => _279.type]) === "path" ? node.subpaths[state.pen.subpath] : void 0;
|
|
7486
8923
|
const last = sp ? getAnchorWorld(doc, { nodeId: state.pen.nodeId, subpath: state.pen.subpath, index: sp.anchors.length - 1 }) : void 0;
|
|
7487
8924
|
if (last) {
|
|
7488
8925
|
const a = toScreen(state, last);
|
|
@@ -7495,9 +8932,9 @@ function getVectorOverlayRenderData(state) {
|
|
|
7495
8932
|
const artboard = { x: origin.x, y: origin.y, width: doc.width * z, height: doc.height * z };
|
|
7496
8933
|
let status = null;
|
|
7497
8934
|
const it = state.interaction;
|
|
7498
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
7499
|
-
else if (_optionalChain([it, 'optionalAccess',
|
|
7500
|
-
else if (_optionalChain([it, 'optionalAccess',
|
|
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") {
|
|
7501
8938
|
const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
|
|
7502
8939
|
if (b) status = `W ${fmt(b.width)} \xD7 H ${fmt(b.height)}`;
|
|
7503
8940
|
}
|
|
@@ -7514,24 +8951,24 @@ function getVectorOverlayRenderData(state) {
|
|
|
7514
8951
|
}
|
|
7515
8952
|
}
|
|
7516
8953
|
let toolStroke = null;
|
|
7517
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
8954
|
+
if (_optionalChain([it, 'optionalAccess', _285 => _285.type]) === "stroke") {
|
|
7518
8955
|
const pts = it.points.map((q) => toScreen(state, q));
|
|
7519
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)}` : "");
|
|
7520
8957
|
toolStroke = { tool: it.tool, d, width: fmt(it.width * z) };
|
|
7521
8958
|
}
|
|
7522
8959
|
let pencilString = null;
|
|
7523
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
8960
|
+
if (_optionalChain([it, 'optionalAccess', _286 => _286.type]) === "draw" && it.string) {
|
|
7524
8961
|
const a = toScreen(state, it.string[0]);
|
|
7525
8962
|
const b = toScreen(state, it.string[1]);
|
|
7526
8963
|
pencilString = { x1: fmt(a.x), y1: fmt(a.y), x2: fmt(b.x), y2: fmt(b.y) };
|
|
7527
8964
|
}
|
|
7528
|
-
const dragged = _optionalChain([it, 'optionalAccess',
|
|
8965
|
+
const dragged = _optionalChain([it, 'optionalAccess', _287 => _287.type]) === "corner" ? it.ref : null;
|
|
7529
8966
|
const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
|
|
7530
8967
|
const s = toScreen(state, w);
|
|
7531
|
-
return { ref: w.ref, x: fmt(s.x), y: fmt(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(_optionalChain([state, 'access',
|
|
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 };
|
|
7532
8969
|
});
|
|
7533
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
7534
|
-
const cutPoint = state.tool === "scissors" && _optionalChain([state, 'access',
|
|
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;
|
|
7535
8972
|
const sc = (p) => {
|
|
7536
8973
|
const q = toScreen(state, p);
|
|
7537
8974
|
return { x: fmt(q.x), y: fmt(q.y) };
|
|
@@ -7539,13 +8976,13 @@ function getVectorOverlayRenderData(state) {
|
|
|
7539
8976
|
const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
|
|
7540
8977
|
const widthPoints = getVectorWidthHandles(state).map((h) => {
|
|
7541
8978
|
const c = sc(h);
|
|
7542
|
-
const hw = _optionalChain([state, 'access',
|
|
8979
|
+
const hw = _optionalChain([state, 'access', _293 => _293.hover, 'optionalAccess', _294 => _294.widthPoint]);
|
|
7543
8980
|
const hovered = hw && sameWidth(hw, h.ref) && hw.subpath === h.ref.subpath ? hw.part : null;
|
|
7544
|
-
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',
|
|
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) };
|
|
7545
8982
|
});
|
|
7546
|
-
const spot = state.tool === "width" && !it ? _optionalChain([state, 'access',
|
|
8983
|
+
const spot = state.tool === "width" && !it ? _optionalChain([state, 'access', _296 => _296.hover, 'optionalAccess', _297 => _297.strokeSpot]) : null;
|
|
7547
8984
|
const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
|
|
7548
|
-
if (_optionalChain([it, 'optionalAccess',
|
|
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)}`;
|
|
7549
8986
|
let builder = null;
|
|
7550
8987
|
if (state.builder) {
|
|
7551
8988
|
const v2 = state.viewport;
|
|
@@ -7561,13 +8998,84 @@ function getVectorOverlayRenderData(state) {
|
|
|
7561
8998
|
lines: state.builder.lines.map((l) => svg(_chunkVNYRI265cjs.transformBezierPath.call(void 0, l, view))),
|
|
7562
8999
|
trail
|
|
7563
9000
|
};
|
|
7564
|
-
if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess',
|
|
9001
|
+
if (state.builder.mode !== "fill" && _optionalChain([it, 'optionalAccess', _299 => _299.type]) === "builder") {
|
|
7565
9002
|
const n = state.builder.faces.length;
|
|
7566
9003
|
status = state.builder.mode === "erase" ? `Delete ${n} ${n === 1 ? "face" : "faces"}` : `Merge ${n} ${n === 1 ? "face" : "faces"}`;
|
|
7567
9004
|
}
|
|
7568
9005
|
}
|
|
7569
|
-
|
|
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 };
|
|
7570
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
|
+
};
|
|
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" };
|
|
7571
9079
|
var VECTOR_OVERLAY_THEME = {
|
|
7572
9080
|
accent: "#0ea5e9",
|
|
7573
9081
|
/** Smart guide lines and labels. */
|
|
@@ -7613,7 +9121,7 @@ function isVectorTextTarget(target) {
|
|
|
7613
9121
|
if (!el || typeof el !== "object") return false;
|
|
7614
9122
|
const tag = (_nullishCoalesce(el.tagName, () => ( ""))).toUpperCase();
|
|
7615
9123
|
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
7616
|
-
return !!el.isContentEditable || _optionalChain([el, 'access',
|
|
9124
|
+
return !!el.isContentEditable || _optionalChain([el, 'access', _320 => _320.getAttribute, 'optionalCall', _321 => _321("contenteditable")]) === "true";
|
|
7617
9125
|
}
|
|
7618
9126
|
function getVectorSelectionStyle(state) {
|
|
7619
9127
|
const paths = [];
|
|
@@ -7621,12 +9129,12 @@ function getVectorSelectionStyle(state) {
|
|
|
7621
9129
|
const node = findVectorNode(state.document.nodes, id);
|
|
7622
9130
|
if (node) paths.push(...collect(node));
|
|
7623
9131
|
}
|
|
7624
|
-
const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access',
|
|
9132
|
+
const anchorMode = state.tool === "direct" && !!_optionalChain([state, 'access', _322 => _322.selectedAnchors, 'optionalAccess', _323 => _323.length]);
|
|
7625
9133
|
if (anchorMode) {
|
|
7626
9134
|
for (const r of state.selectedAnchors) {
|
|
7627
9135
|
if (paths.some((p) => p.id === r.nodeId)) continue;
|
|
7628
9136
|
const node = findVectorNode(state.document.nodes, r.nodeId);
|
|
7629
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
9137
|
+
if (_optionalChain([node, 'optionalAccess', _324 => _324.type]) === "path") paths.push(node);
|
|
7630
9138
|
}
|
|
7631
9139
|
}
|
|
7632
9140
|
if (!paths.length) return null;
|
|
@@ -7637,7 +9145,7 @@ function getVectorSelectionStyle(state) {
|
|
|
7637
9145
|
for (const c of vectorCorners(p.subpaths)) {
|
|
7638
9146
|
if (anchorMode && !state.selectedAnchors.some((r) => r.nodeId === p.id && r.subpath === c.subpath && r.index === c.index)) continue;
|
|
7639
9147
|
const corner = p.subpaths[c.subpath].anchors[c.index].corner;
|
|
7640
|
-
corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess',
|
|
9148
|
+
corners2.push({ radius: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _325 => _325.radius]), () => ( 0)), kind: _nullishCoalesce(_optionalChain([corner, 'optionalAccess', _326 => _326.kind]), () => ( "round")) });
|
|
7641
9149
|
}
|
|
7642
9150
|
}
|
|
7643
9151
|
const c0 = _nullishCoalesce(corners2[0], () => ( { radius: 0, kind: "round" }));
|
|
@@ -7752,6 +9260,18 @@ var WIDTH_POINTS = {
|
|
|
7752
9260
|
},
|
|
7753
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."
|
|
7754
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
|
+
};
|
|
7755
9275
|
var PAINT = {
|
|
7756
9276
|
description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear", angle, stops: [{ offset, color }] }`.'
|
|
7757
9277
|
};
|
|
@@ -7785,17 +9305,17 @@ function createVectorCommands(controller, options = {}) {
|
|
|
7785
9305
|
const base = { group, ...options.scope ? { scope: options.scope } : {} };
|
|
7786
9306
|
const hasSelection = () => controller.getState().selection.length ? true : "Nothing is selected.";
|
|
7787
9307
|
const ids = (params) => {
|
|
7788
|
-
const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess',
|
|
9308
|
+
const list2 = _nullishCoalesce(_optionalChain([params, 'optionalAccess', _330 => _330.ids]), () => ( controller.getState().selection));
|
|
7789
9309
|
for (const id of list2) if (!controller.getNode(id)) fail(`There is no node "${id}".`);
|
|
7790
9310
|
if (!list2.length) fail("Nothing is selected; pass ids.");
|
|
7791
9311
|
return list2;
|
|
7792
9312
|
};
|
|
7793
|
-
const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess',
|
|
9313
|
+
const enabledFor = (_, params) => _optionalChain([params, 'optionalAccess', _331 => _331.ids, 'optionalAccess', _332 => _332.length]) ? true : hasSelection();
|
|
7794
9314
|
const anchorsOf = (refs) => {
|
|
7795
9315
|
if (!refs) return [...controller.getState().selectedAnchors];
|
|
7796
9316
|
for (const r of refs) {
|
|
7797
9317
|
const node = controller.getNode(r.nodeId);
|
|
7798
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
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}".`);
|
|
7799
9319
|
}
|
|
7800
9320
|
return refs;
|
|
7801
9321
|
};
|
|
@@ -7816,7 +9336,9 @@ function createVectorCommands(controller, options = {}) {
|
|
|
7816
9336
|
visible: n.visible !== false,
|
|
7817
9337
|
locked: !!n.locked,
|
|
7818
9338
|
selected: state.selection.includes(n.id),
|
|
7819
|
-
...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 } : {}
|
|
7820
9342
|
});
|
|
7821
9343
|
if (n.type === "group") visit(n.children, depth + 1);
|
|
7822
9344
|
}
|
|
@@ -7886,7 +9408,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
7886
9408
|
run: (_ctx, params) => {
|
|
7887
9409
|
const id = controller.addPath(params.d, { ...pickStyle(params), ...params.name ? { name: params.name } : {} });
|
|
7888
9410
|
const node = controller.getNode(id);
|
|
7889
|
-
if (_optionalChain([node, 'optionalAccess',
|
|
9411
|
+
if (_optionalChain([node, 'optionalAccess', _338 => _338.type]) === "path" && !node.subpaths.length) {
|
|
7890
9412
|
controller.undo();
|
|
7891
9413
|
fail("The path data has no points.");
|
|
7892
9414
|
}
|
|
@@ -7918,10 +9440,10 @@ function createVectorCommands(controller, options = {}) {
|
|
|
7918
9440
|
params: { type: "object", properties: { radius: { type: "number", minimum: 0 }, kind: { type: "string", enum: VECTOR_CORNER_KINDS }, ids: IDS, anchors: ANCHORS } },
|
|
7919
9441
|
undoable: true,
|
|
7920
9442
|
run: (_ctx, params) => {
|
|
7921
|
-
const refs = _optionalChain([params, 'optionalAccess',
|
|
7922
|
-
const target = { refs, ids: _optionalChain([params, 'optionalAccess',
|
|
7923
|
-
if (_optionalChain([params, 'optionalAccess',
|
|
7924
|
-
else if (_optionalChain([params, 'optionalAccess',
|
|
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));
|
|
7925
9447
|
else fail("Pass `radius` and / or `kind`.");
|
|
7926
9448
|
return { ok: true };
|
|
7927
9449
|
}
|
|
@@ -7956,12 +9478,37 @@ function createVectorCommands(controller, options = {}) {
|
|
|
7956
9478
|
undoable: true,
|
|
7957
9479
|
run: (_ctx, params) => {
|
|
7958
9480
|
const target = { ids: ids(params) };
|
|
7959
|
-
if (_optionalChain([params, 'optionalAccess',
|
|
7960
|
-
else if (_optionalChain([params, 'optionalAccess',
|
|
7961
|
-
else if (_optionalChain([params, 'optionalAccess',
|
|
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));
|
|
7962
9484
|
else fail("Pass `preset`, `points`, `taper` or `wave`.");
|
|
7963
9485
|
const node = controller.getNode(target.ids[0]);
|
|
7964
|
-
return { ok: true, points: _optionalChain([node, 'optionalAccess',
|
|
9486
|
+
return { ok: true, points: _optionalChain([node, 'optionalAccess', _347 => _347.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
|
|
9487
|
+
}
|
|
9488
|
+
},
|
|
9489
|
+
{
|
|
9490
|
+
...base,
|
|
9491
|
+
id: `${prefix}setWidthPoint`,
|
|
9492
|
+
label: "Width Point\u2026",
|
|
9493
|
+
keywords: ["width point", "variable width", "stroke width at", "taper", "profile"],
|
|
9494
|
+
description: "Sets one width point of a path's stroke profile numerically: `index` names an existing point (see `vector.setWidthProfile`'s result), leave it out to add one at `t`. `left` / `right` are side widths in document units (how far the stroke reaches on each side), `t` the position along each subpath (0\u20131); a point moved onto another's `t` makes a step. Returns the profile.",
|
|
9495
|
+
params: {
|
|
9496
|
+
type: "object",
|
|
9497
|
+
properties: {
|
|
9498
|
+
id: { type: "string", description: "The path." },
|
|
9499
|
+
index: { type: "integer", minimum: 0 },
|
|
9500
|
+
t: { type: "number", minimum: 0, maximum: 1 },
|
|
9501
|
+
left: { type: "number", minimum: 0 },
|
|
9502
|
+
right: { type: "number", minimum: 0 }
|
|
9503
|
+
},
|
|
9504
|
+
required: ["id"]
|
|
9505
|
+
},
|
|
9506
|
+
undoable: true,
|
|
9507
|
+
run: (_ctx, params) => {
|
|
9508
|
+
if (!_optionalChain([params, 'optionalAccess', _348 => _348.id])) fail("Pass the path `id`.");
|
|
9509
|
+
check(controller.setWidthPoint({ nodeId: params.id, index: _nullishCoalesce(params.index, () => ( null)) }, { t: params.t, left: params.left, right: params.right }));
|
|
9510
|
+
const node = controller.getNode(params.id);
|
|
9511
|
+
return { ok: true, points: _optionalChain([node, 'optionalAccess', _349 => _349.type]) === "path" ? _nullishCoalesce(node.strokeWidthProfile, () => ( [])) : [] };
|
|
7965
9512
|
}
|
|
7966
9513
|
},
|
|
7967
9514
|
{
|
|
@@ -7974,7 +9521,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
7974
9521
|
enabled: enabledFor,
|
|
7975
9522
|
undoable: true,
|
|
7976
9523
|
run: (_ctx, params) => {
|
|
7977
|
-
const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess',
|
|
9524
|
+
const created = controller.outlineStroke({ ids: _optionalChain([params, 'optionalAccess', _350 => _350.ids]) ? ids(params) : void 0 });
|
|
7978
9525
|
if (!created.length) fail("No selected path has a stroke.");
|
|
7979
9526
|
return { ids: created };
|
|
7980
9527
|
}
|
|
@@ -8036,6 +9583,86 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8036
9583
|
return { id };
|
|
8037
9584
|
}
|
|
8038
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
|
+
},
|
|
8039
9666
|
{
|
|
8040
9667
|
...base,
|
|
8041
9668
|
id: `${prefix}pathfinder`,
|
|
@@ -8145,7 +9772,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8145
9772
|
enabled: enabledFor,
|
|
8146
9773
|
undoable: true,
|
|
8147
9774
|
run: (_ctx, params) => {
|
|
8148
|
-
const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess',
|
|
9775
|
+
const id = controller.group({ ids: ids(params), name: _optionalChain([params, 'optionalAccess', _355 => _355.name]) });
|
|
8149
9776
|
if (!id) fail("Only nodes that share a parent can be grouped.");
|
|
8150
9777
|
return { id };
|
|
8151
9778
|
}
|
|
@@ -8173,7 +9800,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8173
9800
|
params: { type: "object", properties: { anchors: ANCHORS, keepShape: { type: "boolean" } } },
|
|
8174
9801
|
undoable: true,
|
|
8175
9802
|
run: (_ctx, params) => {
|
|
8176
|
-
check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess',
|
|
9803
|
+
check(controller.deleteAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _356 => _356.anchors])), { keepShape: _optionalChain([params, 'optionalAccess', _357 => _357.keepShape]) }));
|
|
8177
9804
|
return { ok: true };
|
|
8178
9805
|
}
|
|
8179
9806
|
},
|
|
@@ -8228,7 +9855,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8228
9855
|
params: { type: "object", properties: { ids: IDS, tolerance: { type: "number", minimum: 0 } } },
|
|
8229
9856
|
undoable: true,
|
|
8230
9857
|
run: (_ctx, params) => {
|
|
8231
|
-
check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess',
|
|
9858
|
+
check(controller.joinPaths({ ids: _optionalChain([params, 'optionalAccess', _358 => _358.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _359 => _359.tolerance]) }));
|
|
8232
9859
|
return { selection: controller.getState().selection };
|
|
8233
9860
|
}
|
|
8234
9861
|
},
|
|
@@ -8242,7 +9869,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8242
9869
|
params: { type: "object", properties: { axis: { type: "string", enum: ["horizontal", "vertical", "both"] }, anchors: ANCHORS } },
|
|
8243
9870
|
undoable: true,
|
|
8244
9871
|
run: (_ctx, params) => {
|
|
8245
|
-
check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess',
|
|
9872
|
+
check(controller.averageAnchors(_nullishCoalesce(_optionalChain([params, 'optionalAccess', _360 => _360.axis]), () => ( "both")), anchorsOf(_optionalChain([params, 'optionalAccess', _361 => _361.anchors]))));
|
|
8246
9873
|
return { ok: true };
|
|
8247
9874
|
}
|
|
8248
9875
|
},
|
|
@@ -8256,7 +9883,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8256
9883
|
enabled: enabledFor,
|
|
8257
9884
|
undoable: true,
|
|
8258
9885
|
run: (_ctx, params) => {
|
|
8259
|
-
check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess',
|
|
9886
|
+
check(controller.removeRedundantPoints({ ids: _optionalChain([params, 'optionalAccess', _362 => _362.ids]) ? ids(params) : void 0, tolerance: _optionalChain([params, 'optionalAccess', _363 => _363.tolerance]) }));
|
|
8260
9887
|
return { ok: true };
|
|
8261
9888
|
}
|
|
8262
9889
|
},
|
|
@@ -8269,7 +9896,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8269
9896
|
params: { type: "object", properties: { ids: IDS, amount: { type: "number", minimum: 0, maximum: 1 }, anchors: ANCHORS } },
|
|
8270
9897
|
undoable: true,
|
|
8271
9898
|
run: (_ctx, params) => {
|
|
8272
|
-
check(controller.smoothPaths({ ids: _optionalChain([params, 'optionalAccess',
|
|
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]) }));
|
|
8273
9900
|
return { ok: true };
|
|
8274
9901
|
}
|
|
8275
9902
|
},
|
|
@@ -8282,14 +9909,14 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8282
9909
|
params: { type: "object", properties: { nodeId: { type: "string" }, x: { type: "number" }, y: { type: "number" }, anchors: ANCHORS } },
|
|
8283
9910
|
undoable: true,
|
|
8284
9911
|
run: (_ctx, params) => {
|
|
8285
|
-
if (_optionalChain([params, 'optionalAccess',
|
|
9912
|
+
if (_optionalChain([params, 'optionalAccess', _367 => _367.nodeId]) !== void 0) {
|
|
8286
9913
|
if (!controller.getNode(params.nodeId)) fail(`There is no node "${params.nodeId}".`);
|
|
8287
9914
|
if (params.x === void 0 || params.y === void 0) fail("Pass x and y with nodeId.");
|
|
8288
9915
|
const created = controller.cutPathAt(params.nodeId, { x: params.x, y: params.y });
|
|
8289
9916
|
if (!created) fail("Nothing to cut there (the end of an open path, or a locked path).");
|
|
8290
9917
|
return { ids: created };
|
|
8291
9918
|
}
|
|
8292
|
-
check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess',
|
|
9919
|
+
check(controller.cutAtAnchors(anchorsOf(_optionalChain([params, 'optionalAccess', _368 => _368.anchors]))));
|
|
8293
9920
|
return { ids: controller.getState().selection };
|
|
8294
9921
|
}
|
|
8295
9922
|
},
|
|
@@ -8340,7 +9967,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8340
9967
|
},
|
|
8341
9968
|
run: (_ctx, params) => {
|
|
8342
9969
|
const patch = {};
|
|
8343
|
-
for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess',
|
|
9970
|
+
for (const key of ["keepShapeOnDelete", "curvatureMoves", "pencilSmoothing", "recognizeShapes", "eraserSize", "pencilPressure"]) if (_optionalChain([params, 'optionalAccess', _369 => _369[key]]) !== void 0) patch[key] = params[key];
|
|
8344
9971
|
controller.setEditing(patch);
|
|
8345
9972
|
return { editing: controller.getState().editing };
|
|
8346
9973
|
}
|
|
@@ -8365,7 +9992,7 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8365
9992
|
keywords: ["download", "save", "markup"],
|
|
8366
9993
|
description: "Returns the document as SVG markup.",
|
|
8367
9994
|
params: { type: "object", properties: { precision: { type: "integer", minimum: 0, maximum: 8 } } },
|
|
8368
|
-
run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess',
|
|
9995
|
+
run: (_ctx, params) => ({ svg: controller.toSvg({ precision: _optionalChain([params, 'optionalAccess', _370 => _370.precision]) }) })
|
|
8369
9996
|
},
|
|
8370
9997
|
{
|
|
8371
9998
|
...base,
|
|
@@ -8579,5 +10206,43 @@ function createVectorCommands(controller, options = {}) {
|
|
|
8579
10206
|
|
|
8580
10207
|
|
|
8581
10208
|
|
|
8582
|
-
|
|
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;
|
|
8583
10248
|
//# sourceMappingURL=index.cjs.map
|