@pixerate/editor 0.113.0 → 0.115.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 +8 -0
- package/dist/animation-editor/index.cjs +60 -54
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +3 -3
- package/dist/animation-editor/index.d.ts +3 -3
- package/dist/animation-editor/index.js +14 -8
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/builtin-CY97ppKM.d.cts +354 -0
- package/dist/builtin-CY97ppKM.d.ts +354 -0
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.js +9 -9
- package/dist/chunk-26PGXHY7.cjs +753 -0
- package/dist/chunk-26PGXHY7.cjs.map +1 -0
- package/dist/chunk-2OJACTPR.cjs +929 -0
- package/dist/chunk-2OJACTPR.cjs.map +1 -0
- package/dist/chunk-3UFGNHSP.js +929 -0
- package/dist/chunk-3UFGNHSP.js.map +1 -0
- package/dist/chunk-643B2HKC.cjs +534 -0
- package/dist/chunk-643B2HKC.cjs.map +1 -0
- package/dist/chunk-6FCKOS2B.js +534 -0
- package/dist/chunk-6FCKOS2B.js.map +1 -0
- package/dist/chunk-BRLYOPLT.js +2574 -0
- package/dist/chunk-BRLYOPLT.js.map +1 -0
- package/dist/{chunk-LQEEDHOP.js → chunk-C6C5PDKU.js} +759 -761
- package/dist/chunk-C6C5PDKU.js.map +1 -0
- package/dist/{chunk-4KLT7ZYS.cjs → chunk-DFQEX3AM.cjs} +29 -29
- package/dist/{chunk-4KLT7ZYS.cjs.map → chunk-DFQEX3AM.cjs.map} +1 -1
- package/dist/chunk-G5NMLUVF.cjs +2574 -0
- package/dist/chunk-G5NMLUVF.cjs.map +1 -0
- package/dist/{chunk-YGIMK4NX.cjs → chunk-I7FQBWMQ.cjs} +813 -842
- package/dist/chunk-I7FQBWMQ.cjs.map +1 -0
- package/dist/{chunk-LPYOIVLA.js → chunk-LKCYANMW.js} +3 -3
- package/dist/{chunk-W3GXSUSQ.cjs → chunk-LPLCWZTM.cjs} +5087 -1283
- package/dist/chunk-LPLCWZTM.cjs.map +1 -0
- package/dist/chunk-MMVHBKMM.js +753 -0
- package/dist/chunk-MMVHBKMM.js.map +1 -0
- package/dist/{chunk-POGBUYWE.cjs → chunk-ST7E3NEE.cjs} +857 -859
- package/dist/chunk-ST7E3NEE.cjs.map +1 -0
- package/dist/{chunk-7VOHOPLW.cjs → chunk-TCKIXI46.cjs} +59 -59
- package/dist/{chunk-7VOHOPLW.cjs.map → chunk-TCKIXI46.cjs.map} +1 -1
- package/dist/{chunk-XIOMMGPD.cjs → chunk-TFOPHZ3U.cjs} +1 -1
- package/dist/chunk-TFOPHZ3U.cjs.map +1 -0
- package/dist/{chunk-Z4BC6M74.js → chunk-UBIXVXPR.js} +2 -2
- package/dist/{chunk-BLWCAQCW.js → chunk-VXR7XWMF.js} +4574 -770
- package/dist/chunk-VXR7XWMF.js.map +1 -0
- package/dist/{chunk-CZOXTEMN.js → chunk-XICA4PVO.js} +392 -421
- package/dist/chunk-XICA4PVO.js.map +1 -0
- package/dist/{chunk-HNS7UHCM.js → chunk-XK5LZKBD.js} +1 -1
- package/dist/chunk-XK5LZKBD.js.map +1 -0
- package/dist/cms/index.cjs +146 -0
- package/dist/cms/index.cjs.map +1 -0
- package/dist/cms/index.d.cts +51 -0
- package/dist/cms/index.d.ts +51 -0
- package/dist/cms/index.js +146 -0
- package/dist/cms/index.js.map +1 -0
- package/dist/{export-CEXEzqBf.d.cts → export-BJE8T0YA.d.cts} +279 -191
- package/dist/{export-DIkoK8-r.d.ts → export-Con3UKkm.d.ts} +279 -191
- package/dist/image-editor/index.cjs +1848 -1218
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +85 -93
- package/dist/image-editor/index.d.ts +85 -93
- package/dist/image-editor/index.js +871 -241
- package/dist/image-editor/index.js.map +1 -1
- package/dist/opentype-BZEOSOX3.d.ts +188 -0
- package/dist/opentype-Dpe75vL5.d.cts +188 -0
- package/dist/paint/index.cjs +4 -4
- package/dist/paint/index.d.cts +2 -2
- package/dist/paint/index.d.ts +2 -2
- package/dist/paint/index.js +3 -3
- package/dist/paint-editor/index.cjs +147 -145
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +9 -8
- package/dist/paint-editor/index.d.ts +9 -8
- package/dist/paint-editor/index.js +7 -5
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.cjs +53 -3
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.d.cts +62 -91
- package/dist/raster/index.d.ts +62 -91
- package/dist/raster/index.js +59 -9
- package/dist/{render-BEriFuqK.d.cts → render-B1K_OoLy.d.cts} +87 -2
- package/dist/{render-tjzKaik-.d.ts → render-CrmHOgpo.d.ts} +87 -2
- package/dist/{retouch-CEIKR22W.d.cts → retouch-Bxkuc0KH.d.cts} +1 -1
- package/dist/{retouch-BXmeSCaB.d.ts → retouch-Do6tBs2H.d.ts} +1 -1
- package/dist/{surface-C8RRg_Bh.d.cts → surface-Cn30xIV6.d.cts} +6 -1
- package/dist/{surface-C8RRg_Bh.d.ts → surface-Cn30xIV6.d.ts} +6 -1
- package/dist/{svg-D_cfkhlB.d.cts → svg-BnRj0q8i.d.cts} +16 -4
- package/dist/{svg-wIaKLrU0.d.ts → svg-CmmBFfGT.d.ts} +16 -4
- package/dist/{types-DtL_qLFz.d.ts → types-1SUQKDAw.d.ts} +273 -4
- package/dist/{types-DyQq-WPY.d.cts → types-yINF17Ta.d.cts} +273 -4
- package/dist/vector/index.cjs +12170 -7343
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +1579 -16
- package/dist/vector/index.d.ts +1579 -16
- package/dist/vector/index.js +6303 -1476
- package/dist/vector/index.js.map +1 -1
- package/dist/warp-B39VsOtd.d.cts +348 -0
- package/dist/warp-DMN30aY6.d.ts +348 -0
- package/dist/zlib-dxQdgHyN.d.cts +95 -0
- package/dist/zlib-dxQdgHyN.d.ts +95 -0
- package/package.json +9 -2
- package/skills/pixerate-image-editor/SKILL.md +13 -36
- package/skills/pixerate-image-editor/references/color-management.md +42 -0
- package/skills/pixerate-image-editor/references/photoshop-files.md +41 -0
- package/skills/pixerate-paint-editor/SKILL.md +136 -4
- package/skills/pixerate-vector/SKILL.md +72 -2
- package/dist/chunk-BLWCAQCW.js.map +0 -1
- package/dist/chunk-CZOXTEMN.js.map +0 -1
- package/dist/chunk-HNS7UHCM.js.map +0 -1
- package/dist/chunk-JLYX4V57.cjs +0 -2001
- package/dist/chunk-JLYX4V57.cjs.map +0 -1
- package/dist/chunk-KB2YBCVS.js +0 -2001
- package/dist/chunk-KB2YBCVS.js.map +0 -1
- package/dist/chunk-LQEEDHOP.js.map +0 -1
- package/dist/chunk-POGBUYWE.cjs.map +0 -1
- package/dist/chunk-W3GXSUSQ.cjs.map +0 -1
- package/dist/chunk-XIOMMGPD.cjs.map +0 -1
- package/dist/chunk-YGIMK4NX.cjs.map +0 -1
- package/dist/homography-CZ-tFjay.d.cts +0 -50
- package/dist/homography-Ctfjoc1b.d.ts +0 -50
- package/dist/warp-CWHZzE3x.d.ts +0 -118
- package/dist/warp-D5jz_tN7.d.cts +0 -118
- /package/dist/{chunk-LPYOIVLA.js.map → chunk-LKCYANMW.js.map} +0 -0
- /package/dist/{chunk-Z4BC6M74.js.map → chunk-UBIXVXPR.js.map} +0 -0
package/dist/chunk-KB2YBCVS.js
DELETED
|
@@ -1,2001 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IDENTITY_MATRIX,
|
|
3
|
-
VECTOR_ARROWHEADS,
|
|
4
|
-
VECTOR_CORNER_KINDS,
|
|
5
|
-
activeVectorFilters,
|
|
6
|
-
bezierToSubpaths,
|
|
7
|
-
decomposeRigid,
|
|
8
|
-
ellipseSubpath,
|
|
9
|
-
expandVectorAppearance,
|
|
10
|
-
expandVectorCorners,
|
|
11
|
-
expandVectorGradientStops,
|
|
12
|
-
explicitVectorGradient,
|
|
13
|
-
hasExplicitGradientGeometry,
|
|
14
|
-
hasOwnVectorAppearance,
|
|
15
|
-
hasPaint,
|
|
16
|
-
hasStrokeExtras,
|
|
17
|
-
inferAnchorType,
|
|
18
|
-
invertMatrix,
|
|
19
|
-
isIdentityMatrix,
|
|
20
|
-
isVectorGradient,
|
|
21
|
-
isVectorMaskGroup,
|
|
22
|
-
lineSubpath,
|
|
23
|
-
multiplyMatrices,
|
|
24
|
-
needsVectorPaintLayers,
|
|
25
|
-
nodeMatrix,
|
|
26
|
-
normalizeVectorPaintStacks,
|
|
27
|
-
patternAttrs,
|
|
28
|
-
rectSubpath,
|
|
29
|
-
reverseWidthProfile,
|
|
30
|
-
rotateMatrix,
|
|
31
|
-
sampleVectorGradient,
|
|
32
|
-
sanitizeVectorEffects,
|
|
33
|
-
sanitizeVectorFills,
|
|
34
|
-
sanitizeVectorFilters,
|
|
35
|
-
sanitizeVectorGradient,
|
|
36
|
-
sanitizeVectorMask,
|
|
37
|
-
sanitizeVectorStrokes,
|
|
38
|
-
sanitizeWidthProfile,
|
|
39
|
-
scaleMatrix,
|
|
40
|
-
sortedVectorGradientStops,
|
|
41
|
-
spanVectorGradientStops,
|
|
42
|
-
strokeWidthProfileOf,
|
|
43
|
-
subpathsToBezier,
|
|
44
|
-
transformVectorEffects,
|
|
45
|
-
transformVectorNode,
|
|
46
|
-
translateMatrix,
|
|
47
|
-
unionBounds,
|
|
48
|
-
vectorAppearanceNodes,
|
|
49
|
-
vectorBlendModeCss,
|
|
50
|
-
vectorBlendModeFromCss,
|
|
51
|
-
vectorFills,
|
|
52
|
-
vectorFilterMarkup,
|
|
53
|
-
vectorFilterSvg,
|
|
54
|
-
vectorGradientArea,
|
|
55
|
-
vectorGradientPoints,
|
|
56
|
-
vectorGradientSvgDefs,
|
|
57
|
-
vectorMaskRenderData,
|
|
58
|
-
vectorNodeBounds,
|
|
59
|
-
vectorPaintLayerNodes,
|
|
60
|
-
vectorPathData,
|
|
61
|
-
vectorStrokeLineArea,
|
|
62
|
-
vectorStrokeOutlinePath,
|
|
63
|
-
vectorStrokeParts,
|
|
64
|
-
vectorStrokeReach,
|
|
65
|
-
vectorStrokeStackReach,
|
|
66
|
-
vectorStrokes,
|
|
67
|
-
widthProfileAt,
|
|
68
|
-
withVectorFills,
|
|
69
|
-
withVectorStrokes
|
|
70
|
-
} from "./chunk-BLWCAQCW.js";
|
|
71
|
-
import {
|
|
72
|
-
angleToSvgLinearCoords,
|
|
73
|
-
bezierPathBounds,
|
|
74
|
-
bezierPathToSvg,
|
|
75
|
-
closeContour,
|
|
76
|
-
flattenChain,
|
|
77
|
-
formatNumber,
|
|
78
|
-
pathToContours,
|
|
79
|
-
segmentLength,
|
|
80
|
-
segmentParamAtLength,
|
|
81
|
-
splitSegment
|
|
82
|
-
} from "./chunk-YCSFS3GK.js";
|
|
83
|
-
import {
|
|
84
|
-
evaluateEasing
|
|
85
|
-
} from "./chunk-AGR4P4UZ.js";
|
|
86
|
-
import {
|
|
87
|
-
mixColorsOklab
|
|
88
|
-
} from "./chunk-ZRJMJIQU.js";
|
|
89
|
-
|
|
90
|
-
// src/vector/blend.ts
|
|
91
|
-
var lerpN = (a, b, t) => a + (b - a) * t;
|
|
92
|
-
var lerpP = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
93
|
-
var toP = (v) => [v.x, v.y];
|
|
94
|
-
var mapP = (m, p) => [m[0] * p[0] + m[2] * p[1] + m[4], m[1] * p[0] + m[3] * p[1] + m[5]];
|
|
95
|
-
var curvesOf = (c) => Math.max(0, (c.pts.length - 1) / 3) | 0;
|
|
96
|
-
function subpathToMorphContour(sp, m = IDENTITY_MATRIX) {
|
|
97
|
-
const n = sp.anchors.length;
|
|
98
|
-
if (!n) return null;
|
|
99
|
-
const first = mapP(m, toP(sp.anchors[0]));
|
|
100
|
-
const pts = [first];
|
|
101
|
-
const count = n < 2 ? 0 : sp.closed ? n : n - 1;
|
|
102
|
-
for (let i = 0; i < count; i++) {
|
|
103
|
-
const a = sp.anchors[i];
|
|
104
|
-
const b = sp.anchors[(i + 1) % n];
|
|
105
|
-
const pa = toP(a);
|
|
106
|
-
const pb = toP(b);
|
|
107
|
-
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);
|
|
108
|
-
const c1 = straight ? lerpP(pa, pb, 1 / 3) : toP(a.out ?? a);
|
|
109
|
-
const c2 = straight ? lerpP(pa, pb, 2 / 3) : toP(b.in ?? b);
|
|
110
|
-
pts.push(mapP(m, c1), mapP(m, c2), i === count - 1 && sp.closed ? first : mapP(m, pb));
|
|
111
|
-
}
|
|
112
|
-
return { closed: sp.closed && count > 0, pts };
|
|
113
|
-
}
|
|
114
|
-
function isStraight(p0, p1, p2, p3) {
|
|
115
|
-
const dx = p3[0] - p0[0];
|
|
116
|
-
const dy = p3[1] - p0[1];
|
|
117
|
-
const len = Math.hypot(dx, dy);
|
|
118
|
-
const tol = Math.max(1e-7, len * 1e-7);
|
|
119
|
-
if (len < 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;
|
|
120
|
-
const off = (p) => Math.abs((p[0] - p0[0]) * dy - (p[1] - p0[1]) * dx) / len;
|
|
121
|
-
const along = (p) => ((p[0] - p0[0]) * dx + (p[1] - p0[1]) * dy) / (len * len);
|
|
122
|
-
const u1 = along(p1);
|
|
123
|
-
const u2 = along(p2);
|
|
124
|
-
return off(p1) < tol && off(p2) < tol && u1 >= -1e-9 && u1 <= 1 + 1e-9 && u2 >= -1e-9 && u2 <= 1 + 1e-9;
|
|
125
|
-
}
|
|
126
|
-
function morphContourToSubpath(c, m = IDENTITY_MATRIX, closed = c.closed) {
|
|
127
|
-
const pts = isIdentityMatrix(m) ? c.pts : c.pts.map((p) => mapP(m, p));
|
|
128
|
-
const curves = curvesOf(c);
|
|
129
|
-
const dropLast = closed && c.closed && curves > 0;
|
|
130
|
-
const count = dropLast ? curves : curves + 1;
|
|
131
|
-
const anchors = [];
|
|
132
|
-
for (let k = 0; k < count; k++) anchors.push({ x: pts[3 * k][0], y: pts[3 * k][1] });
|
|
133
|
-
for (let k = 0; k < curves; k++) {
|
|
134
|
-
const p0 = pts[3 * k];
|
|
135
|
-
const p1 = pts[3 * k + 1];
|
|
136
|
-
const p2 = pts[3 * k + 2];
|
|
137
|
-
const p3 = pts[3 * k + 3];
|
|
138
|
-
if (isStraight(p0, p1, p2, p3)) continue;
|
|
139
|
-
anchors[k].out = { x: p1[0], y: p1[1] };
|
|
140
|
-
anchors[(k + 1) % count].in = { x: p2[0], y: p2[1] };
|
|
141
|
-
}
|
|
142
|
-
for (const a of anchors) {
|
|
143
|
-
const type = inferAnchorType(a);
|
|
144
|
-
if (type !== "corner") a.type = type;
|
|
145
|
-
}
|
|
146
|
-
const isClosed = closed && curves > 0;
|
|
147
|
-
return { anchors: dropCollinear(anchors, isClosed), closed: isClosed };
|
|
148
|
-
}
|
|
149
|
-
function dropCollinear(anchors, closed) {
|
|
150
|
-
const out = anchors.slice();
|
|
151
|
-
const min = closed ? 3 : 2;
|
|
152
|
-
let i = closed ? 0 : 1;
|
|
153
|
-
while (out.length > min && i < out.length - (closed ? 0 : 1)) {
|
|
154
|
-
const n = out.length;
|
|
155
|
-
const a = out[(i - 1 + n) % n];
|
|
156
|
-
const b = out[i];
|
|
157
|
-
const c = out[(i + 1) % n];
|
|
158
|
-
const plain = !b.in && !b.out && !a.out && !c.in;
|
|
159
|
-
const ux = b.x - a.x;
|
|
160
|
-
const uy = b.y - a.y;
|
|
161
|
-
const vx = c.x - b.x;
|
|
162
|
-
const vy = c.y - b.y;
|
|
163
|
-
const len = Math.hypot(ux, uy) + Math.hypot(vx, vy);
|
|
164
|
-
if (plain && Math.abs(ux * vy - uy * vx) <= 1e-9 * Math.max(1, len * len) && ux * vx + uy * vy >= 0) out.splice(i, 1);
|
|
165
|
-
else i++;
|
|
166
|
-
}
|
|
167
|
-
return out;
|
|
168
|
-
}
|
|
169
|
-
var segOf = (c, k) => c.pts.slice(3 * k, 3 * k + 4);
|
|
170
|
-
function refineMorphContour(c, segments) {
|
|
171
|
-
const have = curvesOf(c);
|
|
172
|
-
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] };
|
|
173
|
-
const identity = Array.from({ length: c.closed ? have : have + 1 }, (_, i) => i);
|
|
174
|
-
if (have >= segments) return { contour: c, anchors: identity };
|
|
175
|
-
const segs = Array.from({ length: have }, (_, k) => segOf(c, k));
|
|
176
|
-
const lens = segs.map((s) => segmentLength(s));
|
|
177
|
-
const sum = lens.reduce((a, b) => a + b, 0);
|
|
178
|
-
const extra = segments - have;
|
|
179
|
-
const want = lens.map((l) => sum > 1e-12 ? l / sum * extra : extra / have);
|
|
180
|
-
const parts = want.map((w) => 1 + Math.floor(w));
|
|
181
|
-
let left2 = segments - parts.reduce((a, b) => a + b, 0);
|
|
182
|
-
const frac = want.map((w) => w - Math.floor(w));
|
|
183
|
-
const order = parts.map((_, i) => i).sort((a, b) => Math.abs(frac[b] - frac[a]) > 1e-9 ? frac[b] - frac[a] : a - b);
|
|
184
|
-
for (let i = 0; left2 > 0; i = (i + 1) % order.length, left2--) parts[order[i]]++;
|
|
185
|
-
const pts = [c.pts[0]];
|
|
186
|
-
const anchors = [0];
|
|
187
|
-
let at = 0;
|
|
188
|
-
segs.forEach((seg, k) => {
|
|
189
|
-
const pieces = parts[k];
|
|
190
|
-
let rest = seg.slice();
|
|
191
|
-
let t0 = 0;
|
|
192
|
-
for (let j = 1; j < pieces; j++) {
|
|
193
|
-
const t = segmentParamAtLength(seg, lens[k] * j / pieces, lens[k]);
|
|
194
|
-
const u = Math.min(1, Math.max(0, (t - t0) / (1 - t0 || 1)));
|
|
195
|
-
const [l, r] = splitSegment(rest, u);
|
|
196
|
-
pts.push(l[1], l[2], l[3]);
|
|
197
|
-
rest = r;
|
|
198
|
-
t0 = t;
|
|
199
|
-
}
|
|
200
|
-
pts.push(rest[1], rest[2], k === have - 1 && c.closed ? c.pts[0] : rest[3]);
|
|
201
|
-
at += pieces;
|
|
202
|
-
if (k < have - 1 || !c.closed) anchors.push(at);
|
|
203
|
-
});
|
|
204
|
-
return { contour: { closed: c.closed, pts }, anchors };
|
|
205
|
-
}
|
|
206
|
-
function reverseMorphContour(c) {
|
|
207
|
-
return { closed: c.closed, pts: c.pts.slice().reverse() };
|
|
208
|
-
}
|
|
209
|
-
function turnMorphContour(c, s) {
|
|
210
|
-
const n = curvesOf(c);
|
|
211
|
-
if (!c.closed || n === 0) return c;
|
|
212
|
-
const k = (s % n + n) % n;
|
|
213
|
-
if (k === 0) return c;
|
|
214
|
-
const body = c.pts.slice(1);
|
|
215
|
-
return { closed: true, pts: [c.pts[3 * k], ...body.slice(3 * k), ...body.slice(0, 3 * k)] };
|
|
216
|
-
}
|
|
217
|
-
function anchorPoints(c) {
|
|
218
|
-
const n = curvesOf(c);
|
|
219
|
-
const count = c.closed ? n : n + 1;
|
|
220
|
-
return Array.from({ length: Math.max(1, count) }, (_, k) => c.pts[3 * k]);
|
|
221
|
-
}
|
|
222
|
-
function polygonArea(pts) {
|
|
223
|
-
let s = 0;
|
|
224
|
-
for (let i = 0; i < pts.length; i++) {
|
|
225
|
-
const a = pts[i];
|
|
226
|
-
const b = pts[(i + 1) % pts.length];
|
|
227
|
-
s += a[0] * b[1] - b[0] * a[1];
|
|
228
|
-
}
|
|
229
|
-
return s / 2;
|
|
230
|
-
}
|
|
231
|
-
function frame(v) {
|
|
232
|
-
const n = Math.max(1, v.length);
|
|
233
|
-
let cx = 0;
|
|
234
|
-
let cy = 0;
|
|
235
|
-
for (const p of v) {
|
|
236
|
-
cx += p[0];
|
|
237
|
-
cy += p[1];
|
|
238
|
-
}
|
|
239
|
-
cx /= n;
|
|
240
|
-
cy /= n;
|
|
241
|
-
let s = 0;
|
|
242
|
-
for (const p of v) s += (p[0] - cx) ** 2 + (p[1] - cy) ** 2;
|
|
243
|
-
return { c: [cx, cy], r: Math.max(1e-9, Math.sqrt(s / n)) };
|
|
244
|
-
}
|
|
245
|
-
var RBINS = 5;
|
|
246
|
-
var ABINS = 12;
|
|
247
|
-
var TURN_WEIGHT = 0.1;
|
|
248
|
-
function shapeContexts(v, closed) {
|
|
249
|
-
const { r } = frame(v);
|
|
250
|
-
const n = v.length;
|
|
251
|
-
return v.map((p, i) => {
|
|
252
|
-
const prev = v[closed ? (i - 1 + n) % n : Math.max(0, i - 1)];
|
|
253
|
-
const next = v[closed ? (i + 1) % n : Math.min(n - 1, i + 1)];
|
|
254
|
-
const dir = Math.atan2(next[1] - prev[1], next[0] - prev[0]);
|
|
255
|
-
const h = new Float64Array(RBINS * ABINS);
|
|
256
|
-
let total = 0;
|
|
257
|
-
for (let j = 0; j < n; j++) {
|
|
258
|
-
if (i === j) continue;
|
|
259
|
-
const dx = v[j][0] - p[0];
|
|
260
|
-
const dy = v[j][1] - p[1];
|
|
261
|
-
const d = Math.hypot(dx, dy) / r;
|
|
262
|
-
const lr = (Math.log2(Math.max(d, 1e-6)) + 3) / 4 * RBINS;
|
|
263
|
-
if (!(lr >= 0 && lr < RBINS)) continue;
|
|
264
|
-
let ang = Math.atan2(dy, dx) - dir;
|
|
265
|
-
ang = (ang % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
|
266
|
-
h[Math.floor(lr) * ABINS + Math.min(ABINS - 1, Math.floor(ang / (2 * Math.PI) * ABINS))] += 1;
|
|
267
|
-
total++;
|
|
268
|
-
}
|
|
269
|
-
if (total > 0) for (let k = 0; k < h.length; k++) h[k] /= total;
|
|
270
|
-
return h;
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
function chi2(a, b) {
|
|
274
|
-
let s = 0;
|
|
275
|
-
for (let k = 0; k < a.length; k++) {
|
|
276
|
-
const t = a[k] + b[k];
|
|
277
|
-
if (t > 1e-12) s += (a[k] - b[k]) * (a[k] - b[k]) / t;
|
|
278
|
-
}
|
|
279
|
-
return s / 2;
|
|
280
|
-
}
|
|
281
|
-
function bestStart(fixed, free, useShapeContext) {
|
|
282
|
-
const fa = anchorPoints(fixed);
|
|
283
|
-
const ga = anchorPoints(free);
|
|
284
|
-
const n = ga.length;
|
|
285
|
-
const ff = frame(fa);
|
|
286
|
-
const gf = frame(ga);
|
|
287
|
-
const fn = fa.map((p) => [(p[0] - ff.c[0]) / ff.r, (p[1] - ff.c[1]) / ff.r]);
|
|
288
|
-
const gn = ga.map((p) => [(p[0] - gf.c[0]) / gf.r, (p[1] - gf.c[1]) / gf.r]);
|
|
289
|
-
const fsc = useShapeContext ? shapeContexts(fa, fixed.closed) : null;
|
|
290
|
-
const gsc = useShapeContext ? shapeContexts(ga, free.closed) : null;
|
|
291
|
-
const m = Math.max(1, fn.length);
|
|
292
|
-
const cost = (s) => {
|
|
293
|
-
let dot = 0;
|
|
294
|
-
let cross = 0;
|
|
295
|
-
let sc = 0;
|
|
296
|
-
for (let i = 0; i < fn.length; i++) {
|
|
297
|
-
const j = free.closed ? (i + s) % n : i;
|
|
298
|
-
const p = fn[i];
|
|
299
|
-
const q = gn[j];
|
|
300
|
-
dot += p[0] * q[0] + p[1] * q[1];
|
|
301
|
-
cross += p[0] * q[1] - p[1] * q[0];
|
|
302
|
-
if (fsc && gsc) sc += chi2(fsc[i], gsc[j]);
|
|
303
|
-
}
|
|
304
|
-
const residual = Math.max(0, 1 - (dot * dot + cross * cross) / (m * m));
|
|
305
|
-
const turn = 1 - Math.cos(Math.atan2(cross, dot));
|
|
306
|
-
return residual + TURN_WEIGHT * turn + sc / m;
|
|
307
|
-
};
|
|
308
|
-
if (!free.closed) return { start: 0, cost: cost(0) };
|
|
309
|
-
const stride = n > 96 ? Math.ceil(n / 48) : 1;
|
|
310
|
-
let best = { start: 0, cost: Infinity };
|
|
311
|
-
for (let s = 0; s < n; s += stride) {
|
|
312
|
-
const c = cost(s);
|
|
313
|
-
if (c < best.cost - 1e-12) best = { start: s, cost: c };
|
|
314
|
-
}
|
|
315
|
-
if (stride > 1) {
|
|
316
|
-
const s0 = best.start;
|
|
317
|
-
for (let d = 1; d < stride; d++)
|
|
318
|
-
for (const s of [(s0 + d) % n, (s0 - d + n) % n]) {
|
|
319
|
-
const c = cost(s);
|
|
320
|
-
if (c < best.cost - 1e-12) best = { start: s, cost: c };
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return best;
|
|
324
|
-
}
|
|
325
|
-
function placeIn(c, from, to) {
|
|
326
|
-
const b = unionBounds([boundsOfPoints(c.pts)]);
|
|
327
|
-
const p = b ? [b.x + b.width / 2, b.y + b.height / 2] : c.pts[0];
|
|
328
|
-
if (!from || !to) return p;
|
|
329
|
-
const fx = from.width > 0 ? (p[0] - from.x) / from.width : 0.5;
|
|
330
|
-
const fy = from.height > 0 ? (p[1] - from.y) / from.height : 0.5;
|
|
331
|
-
return [to.x + to.width * fx, to.y + to.height * fy];
|
|
332
|
-
}
|
|
333
|
-
function boundsOfPoints(pts) {
|
|
334
|
-
if (!pts.length) return null;
|
|
335
|
-
let x0 = Infinity;
|
|
336
|
-
let y0 = Infinity;
|
|
337
|
-
let x1 = -Infinity;
|
|
338
|
-
let y1 = -Infinity;
|
|
339
|
-
for (const p of pts) {
|
|
340
|
-
x0 = Math.min(x0, p[0]);
|
|
341
|
-
y0 = Math.min(y0, p[1]);
|
|
342
|
-
x1 = Math.max(x1, p[0]);
|
|
343
|
-
y1 = Math.max(y1, p[1]);
|
|
344
|
-
}
|
|
345
|
-
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
346
|
-
}
|
|
347
|
-
function correspondMorphContours(a, b, options = {}) {
|
|
348
|
-
const minSeg = Math.max(1, Math.round(options.minSegments ?? 16));
|
|
349
|
-
const maxSeg = Math.max(minSeg, Math.round(options.maxSegments ?? 400));
|
|
350
|
-
const spacing = options.spacing && options.spacing > 0 ? options.spacing : null;
|
|
351
|
-
const useSC = options.shapeContext !== false;
|
|
352
|
-
const match = options.matchStart !== false;
|
|
353
|
-
const ba = boundsOfPoints(a.flatMap((c) => c.pts));
|
|
354
|
-
const bb = boundsOfPoints(b.flatMap((c) => c.pts));
|
|
355
|
-
const contourLength = (c) => {
|
|
356
|
-
let l = 0;
|
|
357
|
-
for (let k = 0; k < curvesOf(c); k++) l += segmentLength(segOf(c, k));
|
|
358
|
-
return l;
|
|
359
|
-
};
|
|
360
|
-
const pairs = [];
|
|
361
|
-
const [sa, sb] = options.starts ?? [];
|
|
362
|
-
for (let k = 0; k < Math.max(a.length, b.length); k++) {
|
|
363
|
-
const ca = a[k];
|
|
364
|
-
const cb = b[k];
|
|
365
|
-
const explicitA = k === 0 && sa !== null && sa !== void 0 ? sa : null;
|
|
366
|
-
const explicitB = k === 0 && sb !== null && sb !== void 0 ? sb : null;
|
|
367
|
-
const counts = [ca ? curvesOf(ca) : 0, cb ? curvesOf(cb) : 0];
|
|
368
|
-
const longest = Math.max(ca ? contourLength(ca) : 0, cb ? contourLength(cb) : 0);
|
|
369
|
-
let n = Math.max(counts[0], counts[1], minSeg, spacing ? Math.ceil(longest / spacing) : 0);
|
|
370
|
-
n = Math.max(counts[0], counts[1], Math.min(n, maxSeg));
|
|
371
|
-
const point = (c, from, to) => {
|
|
372
|
-
const p = placeIn(c, from, to);
|
|
373
|
-
return { closed: c.closed, pts: Array.from({ length: 1 + 3 * n }, () => [p[0], p[1]]) };
|
|
374
|
-
};
|
|
375
|
-
if (ca && cb) {
|
|
376
|
-
const ra = refineMorphContour(ca, n);
|
|
377
|
-
const rb = refineMorphContour(cb, n);
|
|
378
|
-
pairs.push({
|
|
379
|
-
x: ra.contour,
|
|
380
|
-
y: rb.contour,
|
|
381
|
-
point: false,
|
|
382
|
-
startX: explicitA === null ? null : ra.anchors[explicitA] ?? null,
|
|
383
|
-
startY: explicitB === null ? null : explicitB === rb.anchors.length - 1 && !cb.closed ? -1 : rb.anchors[explicitB] ?? null
|
|
384
|
-
});
|
|
385
|
-
if (explicitA !== null && !ca.closed && explicitA === ra.anchors.length - 1 && explicitA > 0) pairs[pairs.length - 1].startX = -1;
|
|
386
|
-
} else if (ca) pairs.push({ x: refineMorphContour(ca, n).contour, y: point(ca, ba, bb), point: true, startX: null, startY: null });
|
|
387
|
-
else if (cb) pairs.push({ x: point(cb, bb, ba), y: refineMorphContour(cb, n).contour, point: true, startX: null, startY: null });
|
|
388
|
-
}
|
|
389
|
-
const startAt = (c, s) => {
|
|
390
|
-
if (s === null) return c;
|
|
391
|
-
if (s === -1) return reverseMorphContour(c);
|
|
392
|
-
return c.closed ? turnMorphContour(c, s) : c;
|
|
393
|
-
};
|
|
394
|
-
const explicit = pairs.length > 0 && (pairs[0].startX !== null || pairs[0].startY !== null);
|
|
395
|
-
const ref = pairs.find((p) => !p.point && p.x.closed && p.y.closed);
|
|
396
|
-
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;
|
|
397
|
-
let best = null;
|
|
398
|
-
for (const back of fixedBack === null ? [false, true] : [fixedBack]) {
|
|
399
|
-
const xs = [];
|
|
400
|
-
const ys = [];
|
|
401
|
-
let cost = 0;
|
|
402
|
-
pairs.forEach((p, k) => {
|
|
403
|
-
let x = k === 0 ? startAt(p.x, p.startX) : p.x;
|
|
404
|
-
let y = k === 0 ? startAt(p.y, p.startY) : p.y;
|
|
405
|
-
if (back) y = reverseMorphContour(y);
|
|
406
|
-
if (p.point) {
|
|
407
|
-
xs.push(x);
|
|
408
|
-
ys.push(y);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
const pinned = k === 0 && explicit;
|
|
412
|
-
const turnX = !y.closed && x.closed;
|
|
413
|
-
if (match && !pinned && (y.closed || turnX)) {
|
|
414
|
-
const r = turnX ? bestStart(y, x, useSC) : bestStart(x, y, useSC);
|
|
415
|
-
if (turnX) x = turnMorphContour(x, r.start);
|
|
416
|
-
else y = turnMorphContour(y, r.start);
|
|
417
|
-
cost += r.cost;
|
|
418
|
-
} else cost += bestStart(x, { ...y, closed: false }, useSC).cost;
|
|
419
|
-
xs.push(x);
|
|
420
|
-
ys.push(y);
|
|
421
|
-
});
|
|
422
|
-
if (!best || cost < best.cost - 1e-9) best = { xs, ys, cost, back };
|
|
423
|
-
}
|
|
424
|
-
return best ? { a: best.xs, b: best.ys, reversed: best.back } : { a: [], b: [], reversed: false };
|
|
425
|
-
}
|
|
426
|
-
function morphPathsCross(a, b) {
|
|
427
|
-
let count = 0;
|
|
428
|
-
const d = (p, q, r) => (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]);
|
|
429
|
-
for (let k = 0; k < Math.min(a.length, b.length); k++) {
|
|
430
|
-
const pa = anchorPoints(a[k]);
|
|
431
|
-
const pb = anchorPoints(b[k]);
|
|
432
|
-
const n = Math.min(pa.length, pb.length);
|
|
433
|
-
for (let i = 0; i < n; i++)
|
|
434
|
-
for (let j = i + 1; j < n; j++) {
|
|
435
|
-
const d1 = d(pa[j], pb[j], pa[i]);
|
|
436
|
-
const d2 = d(pa[j], pb[j], pb[i]);
|
|
437
|
-
const d3 = d(pa[i], pb[i], pa[j]);
|
|
438
|
-
const d4 = d(pa[i], pb[i], pb[j]);
|
|
439
|
-
if (d1 * d2 < 0 && d3 * d4 < 0) count++;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return count;
|
|
443
|
-
}
|
|
444
|
-
function procrustes(a, b) {
|
|
445
|
-
const pa = a.flatMap(anchorPoints);
|
|
446
|
-
const pb = b.flatMap(anchorPoints);
|
|
447
|
-
const ca = frame(pa).c;
|
|
448
|
-
const cb = frame(pb).c;
|
|
449
|
-
let ss = 0;
|
|
450
|
-
let dot = 0;
|
|
451
|
-
let cross = 0;
|
|
452
|
-
for (let i = 0; i < Math.min(pa.length, pb.length); i++) {
|
|
453
|
-
const p = [pa[i][0] - ca[0], pa[i][1] - ca[1]];
|
|
454
|
-
const q = [pb[i][0] - cb[0], pb[i][1] - cb[1]];
|
|
455
|
-
ss += p[0] * p[0] + p[1] * p[1];
|
|
456
|
-
dot += p[0] * q[0] + p[1] * q[1];
|
|
457
|
-
cross += p[0] * q[1] - p[1] * q[0];
|
|
458
|
-
}
|
|
459
|
-
return { ca, cb, theta: Math.atan2(cross, dot), s: ss > 1e-12 ? Math.hypot(dot, cross) / ss : 1 };
|
|
460
|
-
}
|
|
461
|
-
function interpolateMorphContours(a, b, u, options = {}) {
|
|
462
|
-
const w = options.linear ? 0 : Math.min(1, Math.max(0, options.rigidity ?? 1));
|
|
463
|
-
const sim = w > 0 ? options.similarity ?? procrustes(a, b) : null;
|
|
464
|
-
const rot = (p, ang, k) => {
|
|
465
|
-
const c = Math.cos(ang);
|
|
466
|
-
const s = Math.sin(ang);
|
|
467
|
-
return [k * (c * p[0] - s * p[1]), k * (s * p[0] + c * p[1])];
|
|
468
|
-
};
|
|
469
|
-
const thetaU = sim ? sim.theta * u : 0;
|
|
470
|
-
const sU = sim ? Math.max(1e-9, sim.s) ** u : 1;
|
|
471
|
-
const cU = sim ? lerpP(sim.ca, sim.cb, u) : [0, 0];
|
|
472
|
-
return a.map((ca, k) => {
|
|
473
|
-
const cb = b[k];
|
|
474
|
-
const pts = ca.pts.map((p, i) => {
|
|
475
|
-
const q = cb.pts[i] ?? p;
|
|
476
|
-
const linear = lerpP(p, q, u);
|
|
477
|
-
if (!sim) return linear;
|
|
478
|
-
const d = [p[0] - sim.ca[0], p[1] - sim.ca[1]];
|
|
479
|
-
const full = rot(d, sim.theta, sim.s);
|
|
480
|
-
const resid = [q[0] - (full[0] + sim.cb[0]), q[1] - (full[1] + sim.cb[1])];
|
|
481
|
-
const part = rot(d, thetaU, sU);
|
|
482
|
-
const rigid = [part[0] + cU[0] + resid[0] * u, part[1] + cU[1] + resid[1] * u];
|
|
483
|
-
return lerpP(linear, rigid, w);
|
|
484
|
-
});
|
|
485
|
-
return { closed: ca.closed && cb.closed, pts };
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
function interpolateRigidMatrix(a, b, t) {
|
|
489
|
-
const decompose = (m) => {
|
|
490
|
-
const sx2 = Math.hypot(m[0], m[1]);
|
|
491
|
-
const ang2 = sx2 > 1e-12 ? Math.atan2(m[1], m[0]) : 0;
|
|
492
|
-
const c2 = Math.cos(ang2);
|
|
493
|
-
const s2 = Math.sin(ang2);
|
|
494
|
-
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] };
|
|
495
|
-
};
|
|
496
|
-
const p = decompose(a);
|
|
497
|
-
const q = decompose(b);
|
|
498
|
-
let turn = q.ang - p.ang;
|
|
499
|
-
turn = ((turn + Math.PI) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI) - Math.PI;
|
|
500
|
-
const ang = p.ang + turn * t;
|
|
501
|
-
const sx = lerpN(p.sx, q.sx, t);
|
|
502
|
-
const sh = lerpN(p.sh, q.sh, t);
|
|
503
|
-
const sy = lerpN(p.sy, q.sy, t);
|
|
504
|
-
const c = Math.cos(ang);
|
|
505
|
-
const s = Math.sin(ang);
|
|
506
|
-
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)];
|
|
507
|
-
}
|
|
508
|
-
function interpolateVectorColor(a, b, t) {
|
|
509
|
-
if (t <= 0) return a;
|
|
510
|
-
if (t >= 1) return b;
|
|
511
|
-
return mixColorsOklab(a, b, t) ?? (t < 0.5 ? a : b);
|
|
512
|
-
}
|
|
513
|
-
function resampleGradientStops(g, offsets) {
|
|
514
|
-
return offsets.map((offset) => {
|
|
515
|
-
const s = sampleVectorGradient(g, offset);
|
|
516
|
-
return s.opacity < 1 ? { offset, color: s.color, opacity: Math.round(s.opacity * 1e4) / 1e4 } : { offset, color: s.color };
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
function lerpAngle(a, b, t) {
|
|
520
|
-
const d = ((b - a + 180) % 360 + 360) % 360 - 180;
|
|
521
|
-
return a + d * t;
|
|
522
|
-
}
|
|
523
|
-
var lerpPoint = (a, b, t) => ({ x: lerpN(a.x, b.x, t), y: lerpN(a.y, b.y, t) });
|
|
524
|
-
var sameColourModel = (a, b) => (a.interpolation ?? "srgb") === (b.interpolation ?? "srgb") && (a.smoothness ?? 0) === (b.smoothness ?? 0);
|
|
525
|
-
function lerpStops(a, b, t) {
|
|
526
|
-
let stopsA = sortedVectorGradientStops(a);
|
|
527
|
-
let stopsB = sortedVectorGradientStops(b);
|
|
528
|
-
const resampled = stopsA.length !== stopsB.length || !sameColourModel(a, b);
|
|
529
|
-
if (resampled) {
|
|
530
|
-
const offs = [...expandVectorGradientStops(a), ...expandVectorGradientStops(b), ...stopsA, ...stopsB].map((s) => s.offset).filter(Number.isFinite).sort((x, y) => x - y);
|
|
531
|
-
const common = offs.filter((o, i) => i === 0 || o - offs[i - 1] > 1e-4);
|
|
532
|
-
stopsA = resampleGradientStops(a, common);
|
|
533
|
-
stopsB = resampleGradientStops(b, common);
|
|
534
|
-
}
|
|
535
|
-
const stops = stopsA.map((x, i) => {
|
|
536
|
-
const y = stopsB[i];
|
|
537
|
-
const opacity = lerpN(x.opacity ?? 1, y.opacity ?? 1, t);
|
|
538
|
-
const stop = { offset: lerpN(x.offset, y.offset, t), color: interpolateVectorColor(x.color, y.color, t) };
|
|
539
|
-
if (opacity < 1) stop.opacity = opacity;
|
|
540
|
-
const mid = lerpN(x.midpoint ?? 0.5, y.midpoint ?? 0.5, t);
|
|
541
|
-
if (Math.abs(mid - 0.5) > 1e-6) stop.midpoint = mid;
|
|
542
|
-
return stop;
|
|
543
|
-
});
|
|
544
|
-
return { stops, resampled };
|
|
545
|
-
}
|
|
546
|
-
function lerpGradient(a, b, t) {
|
|
547
|
-
if (a.type !== b.type) return null;
|
|
548
|
-
const { stops, resampled } = lerpStops(a, b, t);
|
|
549
|
-
const near = t < 0.5 ? a : b;
|
|
550
|
-
const out = { type: a.type, stops };
|
|
551
|
-
if (!resampled) {
|
|
552
|
-
if (a.interpolation && a.interpolation !== "srgb") out.interpolation = a.interpolation;
|
|
553
|
-
if (a.smoothness) out.smoothness = a.smoothness;
|
|
554
|
-
}
|
|
555
|
-
if (near.spread && near.spread !== "pad") out.spread = near.spread;
|
|
556
|
-
if (near.strokeMode && near.strokeMode !== "within") out.strokeMode = near.strokeMode;
|
|
557
|
-
const ea = hasExplicitGradientGeometry(a);
|
|
558
|
-
const eb = hasExplicitGradientGeometry(b);
|
|
559
|
-
if (!ea && !eb) {
|
|
560
|
-
if (a.angle !== void 0 || b.angle !== void 0 || a.type === "linear" || a.type === "reflected") {
|
|
561
|
-
const fallback = a.type === "linear" || a.type === "reflected" ? 180 : 90;
|
|
562
|
-
out.angle = lerpAngle(a.angle ?? fallback, b.angle ?? fallback, t);
|
|
563
|
-
}
|
|
564
|
-
if (a.type !== "linear" && a.type !== "reflected") {
|
|
565
|
-
out.cx = lerpN(a.cx ?? 0.5, b.cx ?? 0.5, t);
|
|
566
|
-
out.cy = lerpN(a.cy ?? 0.5, b.cy ?? 0.5, t);
|
|
567
|
-
out.r = lerpN(a.r ?? 0.5, b.r ?? 0.5, t);
|
|
568
|
-
}
|
|
569
|
-
return out;
|
|
570
|
-
}
|
|
571
|
-
const pa = vectorGradientPoints(a);
|
|
572
|
-
const pb = vectorGradientPoints(b);
|
|
573
|
-
if (pa.units !== pb.units) {
|
|
574
|
-
const src = explicitVectorGradient(near);
|
|
575
|
-
return { ...src, stops: out.stops, ...out.interpolation ? { interpolation: out.interpolation } : {}, ...out.smoothness ? { smoothness: out.smoothness } : {} };
|
|
576
|
-
}
|
|
577
|
-
out.start = lerpPoint(pa.start, pb.start, t);
|
|
578
|
-
out.end = lerpPoint(pa.end, pb.end, t);
|
|
579
|
-
out.width = lerpPoint(pa.width, pb.width, t);
|
|
580
|
-
out.units = pa.units;
|
|
581
|
-
if (a.type === "radial") {
|
|
582
|
-
if (pa.focal || pb.focal) out.focal = lerpPoint(pa.focal ?? pa.start, pb.focal ?? pb.start, t);
|
|
583
|
-
const fr = lerpN(pa.focalRadius, pb.focalRadius, t);
|
|
584
|
-
if (fr > 0) out.focalRadius = fr;
|
|
585
|
-
}
|
|
586
|
-
return out;
|
|
587
|
-
}
|
|
588
|
-
function solidLike(color, like) {
|
|
589
|
-
return { ...like, stops: like.stops.map((s) => color === null ? { offset: s.offset, color: s.color, opacity: 0 } : { offset: s.offset, color }) };
|
|
590
|
-
}
|
|
591
|
-
function interpolateVectorPaint(a, b, t) {
|
|
592
|
-
if (t <= 0) return a;
|
|
593
|
-
if (t >= 1) return b;
|
|
594
|
-
const pa = hasPaint(a);
|
|
595
|
-
const pb = hasPaint(b);
|
|
596
|
-
if (!pa && !pb) return t < 0.5 ? a : b;
|
|
597
|
-
if (typeof a === "string" && pa && typeof b === "string" && pb) return interpolateVectorColor(a, b, t);
|
|
598
|
-
if (!pa && typeof b === "string") return interpolateVectorColor("transparent", b, t);
|
|
599
|
-
if (!pb && typeof a === "string") return interpolateVectorColor(a, "transparent", t);
|
|
600
|
-
const ga = pa && isVectorGradient(a) ? a : null;
|
|
601
|
-
const gb = pb && isVectorGradient(b) ? b : null;
|
|
602
|
-
if (ga && gb) return lerpGradient(ga, gb, t) ?? (t < 0.5 ? a : b);
|
|
603
|
-
if (gb) return lerpGradient(solidLike(pa ? a : null, gb), gb, t) ?? b;
|
|
604
|
-
if (ga) return lerpGradient(ga, solidLike(pb ? b : null, ga), t) ?? a;
|
|
605
|
-
return t < 0.5 ? a : b;
|
|
606
|
-
}
|
|
607
|
-
function interpolateWidthProfiles(a, b, t) {
|
|
608
|
-
if (!a?.length && !b?.length) return null;
|
|
609
|
-
const at = [...a ?? [], ...b ?? [], { t: 0 }, { t: 1 }].map((p) => p.t).sort((x, y) => x - y);
|
|
610
|
-
const ts = at.filter((v, i) => i === 0 || v - at[i - 1] > 1e-6);
|
|
611
|
-
return ts.map((pos) => {
|
|
612
|
-
const x = widthProfileAt(a, pos);
|
|
613
|
-
const y = widthProfileAt(b, pos);
|
|
614
|
-
return { t: pos, left: lerpN(x.left, y.left, t), right: lerpN(x.right, y.right, t) };
|
|
615
|
-
});
|
|
616
|
-
}
|
|
617
|
-
function lerpDashes(a, b, t) {
|
|
618
|
-
const dashed = (d) => !!d?.length && d.some((v) => v > 0);
|
|
619
|
-
if (!dashed(a) && !dashed(b)) return t < 0.5 ? a : b;
|
|
620
|
-
const even = (p) => p.length % 2 ? [...p, ...p] : p;
|
|
621
|
-
const solid = (p) => {
|
|
622
|
-
const e = even(p);
|
|
623
|
-
const out = [];
|
|
624
|
-
for (let i = 0; i < e.length; i += 2) out.push(e[i] + e[i + 1], 0);
|
|
625
|
-
return out;
|
|
626
|
-
};
|
|
627
|
-
const x = dashed(a) ? even(a) : solid(b);
|
|
628
|
-
const y = dashed(b) ? even(b) : solid(a);
|
|
629
|
-
if (x.length !== y.length) return t < 0.5 ? a : b;
|
|
630
|
-
return x.map((v, i) => lerpN(v, y[i], t));
|
|
631
|
-
}
|
|
632
|
-
var SWITCHED_KEYS = ["strokeLinecap", "strokeLinejoin", "fillRule", "strokeAlign", "startArrow", "endArrow", "arrowAlign"];
|
|
633
|
-
function interpolateVectorStyle(a, b, t) {
|
|
634
|
-
const near = t < 0.5 ? a : b;
|
|
635
|
-
const out = {};
|
|
636
|
-
const fill = interpolateVectorPaint(a.fill, b.fill, t);
|
|
637
|
-
if (fill !== void 0) out.fill = fill;
|
|
638
|
-
const stroke = interpolateVectorPaint(a.stroke, b.stroke, t);
|
|
639
|
-
if (stroke !== void 0) out.stroke = stroke;
|
|
640
|
-
if (a.strokeWidth !== void 0 || b.strokeWidth !== void 0) out.strokeWidth = lerpN(a.strokeWidth ?? 1, b.strokeWidth ?? 1, t);
|
|
641
|
-
for (const key of SWITCHED_KEYS) if (near[key] !== void 0 && near[key] !== null) out[key] = near[key];
|
|
642
|
-
for (const key of ["startArrowScale", "endArrowScale"])
|
|
643
|
-
if (a[key] !== void 0 || b[key] !== void 0) out[key] = lerpN(a[key] ?? 1, b[key] ?? 1, t);
|
|
644
|
-
const dash = lerpDashes(a.strokeDasharray, b.strokeDasharray, t);
|
|
645
|
-
if (dash?.length) out.strokeDasharray = dash;
|
|
646
|
-
const profile = interpolateWidthProfiles(a.strokeWidthProfile, b.strokeWidthProfile, t);
|
|
647
|
-
if (profile) out.strokeWidthProfile = profile;
|
|
648
|
-
return out;
|
|
649
|
-
}
|
|
650
|
-
function reversedStyle(s) {
|
|
651
|
-
const out = { ...s };
|
|
652
|
-
out.startArrow = s.endArrow;
|
|
653
|
-
out.endArrow = s.startArrow;
|
|
654
|
-
out.startArrowScale = s.endArrowScale;
|
|
655
|
-
out.endArrowScale = s.startArrowScale;
|
|
656
|
-
if (s.strokeWidthProfile?.length) out.strokeWidthProfile = reverseWidthProfile(s.strokeWidthProfile);
|
|
657
|
-
for (const k of ["startArrow", "endArrow", "startArrowScale", "endArrowScale"]) if (out[k] === void 0) delete out[k];
|
|
658
|
-
return out;
|
|
659
|
-
}
|
|
660
|
-
var STYLE_KEYS = [
|
|
661
|
-
"fill",
|
|
662
|
-
"stroke",
|
|
663
|
-
"strokeWidth",
|
|
664
|
-
"strokeLinecap",
|
|
665
|
-
"strokeLinejoin",
|
|
666
|
-
"strokeDasharray",
|
|
667
|
-
"fillRule",
|
|
668
|
-
"strokeAlign",
|
|
669
|
-
"startArrow",
|
|
670
|
-
"endArrow",
|
|
671
|
-
"arrowAlign",
|
|
672
|
-
"startArrowScale",
|
|
673
|
-
"endArrowScale",
|
|
674
|
-
"strokeWidthProfile"
|
|
675
|
-
];
|
|
676
|
-
function styleOf(n) {
|
|
677
|
-
const out = {};
|
|
678
|
-
for (const k of STYLE_KEYS) if (n[k] !== void 0) out[k] = n[k];
|
|
679
|
-
return out;
|
|
680
|
-
}
|
|
681
|
-
function contoursOf(node, m) {
|
|
682
|
-
return expandVectorCorners(node.subpaths).map((sp) => subpathToMorphContour(sp, m)).filter((c) => !!c);
|
|
683
|
-
}
|
|
684
|
-
function preparePathMorph(a, b, options, ma = nodeMatrix(a), mb = nodeMatrix(b)) {
|
|
685
|
-
const corr = correspondMorphContours(contoursOf(a, ma), contoursOf(b, mb), options);
|
|
686
|
-
const sim = procrustes(corr.a, corr.b);
|
|
687
|
-
const styleA = styleOf(a);
|
|
688
|
-
const styleB = corr.reversed ? reversedStyle(styleOf(b)) : styleOf(b);
|
|
689
|
-
const rigid = !isIdentityMatrix(ma) || !isIdentityMatrix(mb);
|
|
690
|
-
const id = options.id ?? a.id;
|
|
691
|
-
return (t) => {
|
|
692
|
-
const contours = interpolateMorphContours(corr.a, corr.b, t, { linear: options.linear, rigidity: options.rigidity, similarity: sim });
|
|
693
|
-
const m = rigid ? interpolateRigidMatrix(ma, mb, t) : IDENTITY_MATRIX;
|
|
694
|
-
const back = rigid ? invertMatrix(m) : IDENTITY_MATRIX;
|
|
695
|
-
const subpaths = contours.map((c, k) => {
|
|
696
|
-
const closed = t <= 0 ? corr.a[k].closed : t >= 1 ? corr.b[k].closed : corr.a[k].closed && corr.b[k].closed;
|
|
697
|
-
return morphContourToSubpath({ closed: corr.a[k].closed && corr.b[k].closed, pts: c.pts }, back, closed);
|
|
698
|
-
});
|
|
699
|
-
const node = { id, type: "path", subpaths, ...interpolateVectorStyle(styleA, styleB, t) };
|
|
700
|
-
const opacity = lerpN(a.opacity ?? 1, b.opacity ?? 1, t);
|
|
701
|
-
if (opacity < 1) node.opacity = opacity;
|
|
702
|
-
if (rigid && !isIdentityMatrix(m)) node.transform = m;
|
|
703
|
-
return node;
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
function flattenPaths(node, parent = IDENTITY_MATRIX) {
|
|
707
|
-
const m = multiplyMatrices(nodeMatrix(node), parent);
|
|
708
|
-
if (node.type === "path") return [{ path: node, m, opacity: 1 }];
|
|
709
|
-
const o = node.opacity ?? 1;
|
|
710
|
-
return node.children.filter((c) => c.visible !== false).flatMap((c) => flattenPaths(c, m)).map((e) => ({ ...e, opacity: e.opacity * o }));
|
|
711
|
-
}
|
|
712
|
-
var placedBounds = (path, m) => boundsOfPoints(contoursOf(path, m).flatMap((c) => c.pts));
|
|
713
|
-
function collapsedPath(path, m, from, to) {
|
|
714
|
-
const own = placedBounds(path, m);
|
|
715
|
-
const c = own ? [own.x + own.width / 2, own.y + own.height / 2] : [0, 0];
|
|
716
|
-
const fx = from && from.width > 0 ? (c[0] - from.x) / from.width : 0.5;
|
|
717
|
-
const fy = from && from.height > 0 ? (c[1] - from.y) / from.height : 0.5;
|
|
718
|
-
const p = to ? { x: to.x + to.width * fx, y: to.y + to.height * fy } : { x: c[0], y: c[1] };
|
|
719
|
-
const subpaths = path.subpaths.map((sp) => ({ closed: sp.closed, anchors: sp.anchors.map(() => ({ x: p.x, y: p.y })) }));
|
|
720
|
-
const { transform: _t, ...rest } = path;
|
|
721
|
-
return { ...rest, subpaths };
|
|
722
|
-
}
|
|
723
|
-
function createVectorMorph(a, b, options = {}) {
|
|
724
|
-
if (a.type === "path" && b.type === "path") {
|
|
725
|
-
const at = preparePathMorph(a, b, options);
|
|
726
|
-
return { at };
|
|
727
|
-
}
|
|
728
|
-
const pa = flattenPaths(a);
|
|
729
|
-
const pb = flattenPaths(b);
|
|
730
|
-
const ba = unionBounds(pa.map((e) => placedBounds(e.path, e.m)));
|
|
731
|
-
const bb = unionBounds(pb.map((e) => placedBounds(e.path, e.m)));
|
|
732
|
-
const id = options.id ?? a.id;
|
|
733
|
-
const parts = [];
|
|
734
|
-
const opacities = [];
|
|
735
|
-
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
736
|
-
const x = pa[i];
|
|
737
|
-
const y = pb[i];
|
|
738
|
-
const sub2 = { ...options, id: `${id}-${i}`, starts: void 0 };
|
|
739
|
-
if (x && y) {
|
|
740
|
-
parts.push(preparePathMorph(x.path, y.path, sub2, x.m, y.m));
|
|
741
|
-
opacities.push([x.opacity, y.opacity]);
|
|
742
|
-
} else if (x) {
|
|
743
|
-
parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub2, x.m, IDENTITY_MATRIX));
|
|
744
|
-
opacities.push([x.opacity, x.opacity]);
|
|
745
|
-
} else if (y) {
|
|
746
|
-
parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub2, IDENTITY_MATRIX, y.m));
|
|
747
|
-
opacities.push([y.opacity, y.opacity]);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return {
|
|
751
|
-
at: (t) => {
|
|
752
|
-
const children = parts.map((p, i) => {
|
|
753
|
-
const n = p(t);
|
|
754
|
-
const o = lerpN(opacities[i][0], opacities[i][1], t) * (n.opacity ?? 1);
|
|
755
|
-
if (o < 1) n.opacity = o;
|
|
756
|
-
else delete n.opacity;
|
|
757
|
-
return n;
|
|
758
|
-
});
|
|
759
|
-
const group = { id, type: "group", children };
|
|
760
|
-
return group;
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
function interpolateVectorPaths(a, b, t, options = {}) {
|
|
765
|
-
return createVectorMorph(a, b, options).at(t);
|
|
766
|
-
}
|
|
767
|
-
var keyframeMorphs = /* @__PURE__ */ new WeakMap();
|
|
768
|
-
function evaluateVectorPathKeyframes(keyframes, time, options = {}) {
|
|
769
|
-
const keys = keyframes.filter((k) => Number.isFinite(k.time)).sort((x, y) => x.time - y.time);
|
|
770
|
-
if (!keys.length) return null;
|
|
771
|
-
if (!(time > keys[0].time)) return keys[0].value;
|
|
772
|
-
const last = keys[keys.length - 1];
|
|
773
|
-
if (time >= last.time) return last.value;
|
|
774
|
-
let i = 0;
|
|
775
|
-
while (i + 1 < keys.length && keys[i + 1].time <= time) i++;
|
|
776
|
-
const a = keys[i];
|
|
777
|
-
const b = keys[i + 1];
|
|
778
|
-
if (time === a.time) return a.value;
|
|
779
|
-
if (a.easing === "hold") return a.value;
|
|
780
|
-
const span = b.time - a.time;
|
|
781
|
-
const u = Math.min(1, Math.max(0, evaluateEasing(a.easing, (time - a.time) / span, span)));
|
|
782
|
-
let inner = keyframeMorphs.get(a.value);
|
|
783
|
-
if (!inner) keyframeMorphs.set(a.value, inner = /* @__PURE__ */ new WeakMap());
|
|
784
|
-
let morph = inner.get(b.value);
|
|
785
|
-
if (!morph) inner.set(b.value, morph = createVectorMorph(a.value, b.value, options));
|
|
786
|
-
return morph.at(u);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// src/vector/gradientStroke.ts
|
|
790
|
-
var SHARP = 1.25;
|
|
791
|
-
var TOLERANCE = 0.2;
|
|
792
|
-
var OVERLAP = 0.5;
|
|
793
|
-
var fmt = (n, digits = 3) => {
|
|
794
|
-
const k = 10 ** digits;
|
|
795
|
-
const v = Math.round(n * k) / k;
|
|
796
|
-
return Object.is(v, -0) ? "0" : String(v);
|
|
797
|
-
};
|
|
798
|
-
var toSvgStops = (stops) => stops.map((s) => ({ offset: `${fmt(s.offset * 100)}%`, stopColor: s.color, ...s.opacity !== void 0 && s.opacity !== 1 ? { stopOpacity: Math.round(s.opacity * 1e4) / 1e4 } : {} }));
|
|
799
|
-
var polygonD = (pts, precision) => `M${pts.map((p) => `${fmt(p.x, precision)} ${fmt(p.y, precision)}`).join("L")}Z`;
|
|
800
|
-
var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
801
|
-
var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
802
|
-
var scale = (a, k) => ({ x: a.x * k, y: a.y * k });
|
|
803
|
-
var left = (d) => ({ x: d.y, y: -d.x });
|
|
804
|
-
var unit = (v) => {
|
|
805
|
-
const l = Math.hypot(v.x, v.y);
|
|
806
|
-
return l > 1e-12 ? { x: v.x / l, y: v.y / l } : { x: 1, y: 0 };
|
|
807
|
-
};
|
|
808
|
-
function flattenedSubpaths(node) {
|
|
809
|
-
const out = [];
|
|
810
|
-
for (const contour of pathToContours(subpathsToBezier(node.subpaths))) {
|
|
811
|
-
const segs = closeContour(contour);
|
|
812
|
-
if (!segs.length) continue;
|
|
813
|
-
const ring = flattenChain(segs, TOLERANCE);
|
|
814
|
-
const pts = ring.map(([x, y]) => ({ x, y })).filter((p, i, a) => i === 0 || Math.hypot(p.x - a[i - 1].x, p.y - a[i - 1].y) > 1e-9);
|
|
815
|
-
if (contour.closed && pts.length > 2 && Math.hypot(pts[0].x - pts[pts.length - 1].x, pts[0].y - pts[pts.length - 1].y) < 1e-9) pts.pop();
|
|
816
|
-
if (pts.length >= 2) out.push({ pts, closed: contour.closed && pts.length > 2 });
|
|
817
|
-
}
|
|
818
|
-
return out;
|
|
819
|
-
}
|
|
820
|
-
var signedArea = (pts) => {
|
|
821
|
-
let a = 0;
|
|
822
|
-
for (let i = 0; i < pts.length; i++) {
|
|
823
|
-
const p = pts[i];
|
|
824
|
-
const q = pts[(i + 1) % pts.length];
|
|
825
|
-
a += p.x * q.y - q.x * p.y;
|
|
826
|
-
}
|
|
827
|
-
return a / 2;
|
|
828
|
-
};
|
|
829
|
-
function vectorStrokeGradientDefs(g, id, node, options = {}) {
|
|
830
|
-
const precision = options.precision ?? 3;
|
|
831
|
-
const mode = g.strokeMode === "across" ? "across" : "along";
|
|
832
|
-
const expanded = expandVectorGradientStops(g);
|
|
833
|
-
const reach = vectorStrokeReach(node) + 1;
|
|
834
|
-
const area = options.area ?? vectorGradientArea(options.bounds && { x: options.bounds.x - reach, y: options.bounds.y - reach, width: options.bounds.width + 2 * reach, height: options.bounds.height + 2 * reach });
|
|
835
|
-
const width = node.strokeWidth ?? 1;
|
|
836
|
-
const align = node.strokeAlign ?? "center";
|
|
837
|
-
const profile = strokeWidthProfileOf(node);
|
|
838
|
-
const gradients = [];
|
|
839
|
-
const patches = [];
|
|
840
|
-
const slices = [];
|
|
841
|
-
const colourAt = (t) => {
|
|
842
|
-
const s = spanVectorGradientStops(g, t, t, expanded)[0];
|
|
843
|
-
return s;
|
|
844
|
-
};
|
|
845
|
-
let counter = 0;
|
|
846
|
-
const solid = (pts, t) => {
|
|
847
|
-
const s = colourAt(t);
|
|
848
|
-
const gid = `${id}-p${counter++}`;
|
|
849
|
-
gradients.push({ id: gid, type: "linear", attrs: { gradientUnits: "userSpaceOnUse", x1: 0, y1: 0, x2: 1, y2: 0 }, stops: toSvgStops([{ ...s, offset: 0 }]) });
|
|
850
|
-
patches.push({ d: polygonD(pts, precision), fill: `url(#${gid})` });
|
|
851
|
-
};
|
|
852
|
-
const disc = (c, r) => Array.from({ length: 16 }, (_, k) => add(c, scale({ x: Math.cos(k * Math.PI / 8), y: Math.sin(k * Math.PI / 8) }, r)));
|
|
853
|
-
for (const { pts, closed } of flattenedSubpaths(node)) {
|
|
854
|
-
const n = pts.length;
|
|
855
|
-
const segCount = closed ? n : n - 1;
|
|
856
|
-
const dirs = [];
|
|
857
|
-
const lengths = [0];
|
|
858
|
-
for (let i = 0; i < segCount; i++) {
|
|
859
|
-
const a = pts[i];
|
|
860
|
-
const b = pts[(i + 1) % n];
|
|
861
|
-
dirs.push(unit(sub(b, a)));
|
|
862
|
-
lengths.push(lengths[i] + Math.hypot(b.x - a.x, b.y - a.y));
|
|
863
|
-
}
|
|
864
|
-
const total = lengths[segCount];
|
|
865
|
-
if (!(total > 1e-9)) continue;
|
|
866
|
-
const tAt = (i) => lengths[i] / total;
|
|
867
|
-
const clockwise = closed && signedArea(pts) > 0;
|
|
868
|
-
const visibleLeft = align === "center" || !closed ? null : align === "inside" ? !clockwise : clockwise;
|
|
869
|
-
const ribs = [];
|
|
870
|
-
for (let i = 0; i < n; i++) {
|
|
871
|
-
const p = pts[i];
|
|
872
|
-
const din = i > 0 || closed ? dirs[(i - 1 + segCount) % segCount] : null;
|
|
873
|
-
const dout = i < segCount ? dirs[i] : null;
|
|
874
|
-
const square = (d) => ({ l: add(p, scale(left(d), reach)), r: sub(p, scale(left(d), reach)) });
|
|
875
|
-
if (!din || !dout) {
|
|
876
|
-
const rib = square(din ?? dout);
|
|
877
|
-
ribs.push({ before: rib, after: rib });
|
|
878
|
-
continue;
|
|
879
|
-
}
|
|
880
|
-
const nin = left(din);
|
|
881
|
-
const nout = left(dout);
|
|
882
|
-
const den = 1 + nin.x * nout.x + nin.y * nout.y;
|
|
883
|
-
const stretch = den > 1e-12 ? Math.sqrt(2 / den) : Infinity;
|
|
884
|
-
if (stretch <= SHARP) {
|
|
885
|
-
const m = scale(add(nin, nout), reach / den);
|
|
886
|
-
const rib = { l: add(p, m), r: sub(p, m) };
|
|
887
|
-
ribs.push({ before: rib, after: rib });
|
|
888
|
-
} else {
|
|
889
|
-
ribs.push({ before: square(din), after: square(dout) });
|
|
890
|
-
solid(disc(p, reach * 1.5), tAt(i));
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
if (!closed) {
|
|
894
|
-
const d0 = dirs[0];
|
|
895
|
-
const d1 = dirs[segCount - 1];
|
|
896
|
-
const a = pts[0];
|
|
897
|
-
const b = pts[n - 1];
|
|
898
|
-
const cap = (p, d) => [add(p, scale(left(d), reach)), add(add(p, scale(d, reach)), scale(left(d), reach)), sub(add(p, scale(d, reach)), scale(left(d), reach)), sub(p, scale(left(d), reach))];
|
|
899
|
-
solid(cap(a, scale(d0, -1)), 0);
|
|
900
|
-
solid(cap(b, d1), 1);
|
|
901
|
-
}
|
|
902
|
-
for (let i = 0; i < segCount; i++) {
|
|
903
|
-
const j = (i + 1) % n;
|
|
904
|
-
const a = pts[i];
|
|
905
|
-
const b = pts[j];
|
|
906
|
-
const d = dirs[i];
|
|
907
|
-
const from = ribs[i].after;
|
|
908
|
-
const to = ribs[j].before;
|
|
909
|
-
const ext = scale(d, OVERLAP);
|
|
910
|
-
const quad = [from.l, add(to.l, ext), add(to.r, ext), from.r];
|
|
911
|
-
const gid = `${id}-s${counter++}`;
|
|
912
|
-
const t0 = tAt(i);
|
|
913
|
-
const t1 = closed && i === segCount - 1 ? 1 : tAt(i + 1);
|
|
914
|
-
if (mode === "along") {
|
|
915
|
-
gradients.push({
|
|
916
|
-
id: gid,
|
|
917
|
-
type: "linear",
|
|
918
|
-
attrs: { gradientUnits: "userSpaceOnUse", x1: fmt(a.x, 4), y1: fmt(a.y, 4), x2: fmt(b.x, 4), y2: fmt(b.y, 4) },
|
|
919
|
-
stops: toSvgStops(spanVectorGradientStops(g, t0, t1, expanded))
|
|
920
|
-
});
|
|
921
|
-
} else {
|
|
922
|
-
const mid = scale(add(a, b), 0.5);
|
|
923
|
-
const side = widthProfileAt(profile, (t0 + t1) / 2);
|
|
924
|
-
const nrm = left(d);
|
|
925
|
-
const reachL = visibleLeft === null ? width / 2 * side.left : visibleLeft ? width * side.left : 0;
|
|
926
|
-
const reachR = visibleLeft === null ? width / 2 * side.right : visibleLeft ? 0 : width * side.right;
|
|
927
|
-
const p0 = add(mid, scale(nrm, reachL));
|
|
928
|
-
const p1 = sub(mid, scale(nrm, reachR));
|
|
929
|
-
gradients.push({
|
|
930
|
-
id: gid,
|
|
931
|
-
type: "linear",
|
|
932
|
-
attrs: { gradientUnits: "userSpaceOnUse", x1: fmt(p0.x, 4), y1: fmt(p0.y, 4), x2: fmt(p1.x, 4), y2: fmt(p1.y, 4) },
|
|
933
|
-
stops: toSvgStops(expanded)
|
|
934
|
-
});
|
|
935
|
-
}
|
|
936
|
-
slices.push({ d: polygonD(quad, precision), fill: `url(#${gid})` });
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
const def = {
|
|
940
|
-
id,
|
|
941
|
-
type: "pattern",
|
|
942
|
-
attrs: patternAttrs(area),
|
|
943
|
-
gradients,
|
|
944
|
-
tiles: [...patches, ...slices]
|
|
945
|
-
};
|
|
946
|
-
return [def];
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
// src/vector/svg.ts
|
|
950
|
-
function vectorCornersAttribute(subpaths, precision = 3) {
|
|
951
|
-
const out = [];
|
|
952
|
-
subpaths.forEach(
|
|
953
|
-
(sp, s) => sp.anchors.forEach((a, i) => {
|
|
954
|
-
if (!a.corner || !(a.corner.radius > 0)) return;
|
|
955
|
-
out.push(`${s}.${i}:${formatNumber(a.corner.radius, precision)}${a.corner.kind && a.corner.kind !== "round" ? `:${a.corner.kind}` : ""}`);
|
|
956
|
-
})
|
|
957
|
-
);
|
|
958
|
-
return out.join(" ");
|
|
959
|
-
}
|
|
960
|
-
function applyVectorCornersAttribute(subpaths, value) {
|
|
961
|
-
for (const item of value.trim().split(/\s+/)) {
|
|
962
|
-
const m = /^(\d+)\.(\d+):([\d.eE+-]+)(?::([a-z]+))?$/.exec(item);
|
|
963
|
-
if (!m) continue;
|
|
964
|
-
const a = subpaths[+m[1]]?.anchors[+m[2]];
|
|
965
|
-
const radius = parseFloat(m[3]);
|
|
966
|
-
if (!a || !(radius > 0)) continue;
|
|
967
|
-
const kind = VECTOR_CORNER_KINDS.includes(m[4]) ? m[4] : void 0;
|
|
968
|
-
a.corner = { radius, ...kind && kind !== "round" ? { kind } : {} };
|
|
969
|
-
}
|
|
970
|
-
return subpaths;
|
|
971
|
-
}
|
|
972
|
-
var escapeAttr = (value) => value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
973
|
-
function vectorPathLocalBounds(node) {
|
|
974
|
-
const b = bezierPathBounds(subpathsToBezier(node.subpaths));
|
|
975
|
-
return b ? { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY } : null;
|
|
976
|
-
}
|
|
977
|
-
var paintCache = /* @__PURE__ */ new WeakMap();
|
|
978
|
-
function vectorPaintToSvg(paint, gradientId, options = {}) {
|
|
979
|
-
if (!hasPaint(paint)) return { value: "none", defs: [] };
|
|
980
|
-
if (typeof paint === "string") return { value: paint, defs: [] };
|
|
981
|
-
if (!isVectorGradient(paint)) return { value: "none", defs: [] };
|
|
982
|
-
const role = options.role ?? "fill";
|
|
983
|
-
const precision = options.precision ?? 3;
|
|
984
|
-
const key = `${gradientId}|${role}|${precision}`;
|
|
985
|
-
const owner = options.node ?? paint;
|
|
986
|
-
const cached = paintCache.get(owner)?.get(key);
|
|
987
|
-
if (cached && (!options.node || (role === "fill" ? options.node.fill : options.node.stroke) === paint)) return { value: `url(#${gradientId})`, defs: cached };
|
|
988
|
-
const node = options.node;
|
|
989
|
-
const bounds = node ? vectorPathLocalBounds(node) : null;
|
|
990
|
-
const reach = node && role === "stroke" ? vectorStrokeReach(node) + 1 : 1;
|
|
991
|
-
const area = bounds ? { x: bounds.x - reach, y: bounds.y - reach, width: bounds.width + 2 * reach, height: bounds.height + 2 * reach } : null;
|
|
992
|
-
const defs = node && role === "stroke" && (paint.strokeMode === "along" || paint.strokeMode === "across") ? vectorStrokeGradientDefs(paint, gradientId, node, { bounds, area, precision }) : vectorGradientSvgDefs(paint, gradientId, { bounds, area, precision });
|
|
993
|
-
let byKey = paintCache.get(owner);
|
|
994
|
-
if (!byKey) paintCache.set(owner, byKey = /* @__PURE__ */ new Map());
|
|
995
|
-
byKey.set(key, defs);
|
|
996
|
-
return { value: `url(#${gradientId})`, defs };
|
|
997
|
-
}
|
|
998
|
-
var partsCache = /* @__PURE__ */ new WeakMap();
|
|
999
|
-
function vectorStrokeSvgParts(node, clipId, precision = 3) {
|
|
1000
|
-
if (!hasPaint(node.stroke) || !hasStrokeExtras(node)) return void 0;
|
|
1001
|
-
const hit = partsCache.get(node);
|
|
1002
|
-
if (hit && hit.clipId === clipId && hit.precision === precision) return hit.parts;
|
|
1003
|
-
const parts = computeStrokeSvgParts(node, clipId, precision);
|
|
1004
|
-
partsCache.set(node, { clipId, precision, parts });
|
|
1005
|
-
return parts;
|
|
1006
|
-
}
|
|
1007
|
-
function computeStrokeSvgParts(node, clipId, precision) {
|
|
1008
|
-
const path = subpathsToBezier(node.subpaths);
|
|
1009
|
-
const parts = vectorStrokeParts(path, node);
|
|
1010
|
-
const profiled = !!strokeWidthProfileOf(node);
|
|
1011
|
-
if (parts.align === "center" && !parts.heads.length && !profiled) return void 0;
|
|
1012
|
-
let clip = null;
|
|
1013
|
-
if (parts.align !== "center") {
|
|
1014
|
-
const b = bezierPathBounds(path);
|
|
1015
|
-
const pad = vectorStrokeReach(node) + 1;
|
|
1016
|
-
const bounds = b ? { x: round2(b.minX - pad), y: round2(b.minY - pad), width: round2(b.maxX - b.minX + 2 * pad), height: round2(b.maxY - b.minY + 2 * pad) } : { x: 0, y: 0, width: 0, height: 0 };
|
|
1017
|
-
clip = { kind: parts.align === "inside" ? "clip" : "mask", id: clipId, bounds };
|
|
1018
|
-
}
|
|
1019
|
-
return {
|
|
1020
|
-
align: parts.align,
|
|
1021
|
-
d: bezierPathToSvg(parts.line, { precision }),
|
|
1022
|
-
width: parts.drawWidth,
|
|
1023
|
-
...profiled && (node.strokeWidth ?? 1) > 0 ? { outline: bezierPathToSvg(vectorStrokeLineArea(parts, node), { precision }) } : {},
|
|
1024
|
-
clip,
|
|
1025
|
-
heads: parts.heads.map((h) => bezierPathToSvg(h.path, { precision }))
|
|
1026
|
-
};
|
|
1027
|
-
}
|
|
1028
|
-
function vectorNodeLocalPaintBounds(node) {
|
|
1029
|
-
const drawn = expandVectorAppearance(node);
|
|
1030
|
-
const reach = (n) => n.type === "path" ? vectorStrokeStackReach(n) : Math.max(0, ...n.children.map(reach));
|
|
1031
|
-
let b;
|
|
1032
|
-
if (drawn.type === "path") b = vectorPathLocalBounds(drawn);
|
|
1033
|
-
else b = unionBounds(drawn.children.filter((c) => c.visible !== false).map((c) => vectorNodeBounds(c, IDENTITY_MATRIX)));
|
|
1034
|
-
if (!b) return null;
|
|
1035
|
-
const pad = reach(drawn);
|
|
1036
|
-
return { x: b.x - pad, y: b.y - pad, width: b.width + 2 * pad, height: b.height + 2 * pad };
|
|
1037
|
-
}
|
|
1038
|
-
var filterCache = /* @__PURE__ */ new WeakMap();
|
|
1039
|
-
function vectorNodeFilterSvg(node, id) {
|
|
1040
|
-
if (!node.filters?.length) return null;
|
|
1041
|
-
const hit = filterCache.get(node);
|
|
1042
|
-
if (hit && hit.id === id) return hit.filter;
|
|
1043
|
-
const active = activeVectorFilters(node);
|
|
1044
|
-
const filter = active.length ? vectorFilterSvg(active, id, vectorNodeLocalPaintBounds(node)) : null;
|
|
1045
|
-
filterCache.set(node, { id, filter });
|
|
1046
|
-
return filter;
|
|
1047
|
-
}
|
|
1048
|
-
function widthProfileToAttribute(points, precision = 4) {
|
|
1049
|
-
return points.map((p) => [p.t, p.left, p.right].map((n) => formatNumber(n, precision)).join(",")).join(";");
|
|
1050
|
-
}
|
|
1051
|
-
function widthProfileFromAttribute(value) {
|
|
1052
|
-
if (!value) return [];
|
|
1053
|
-
return sanitizeWidthProfile(
|
|
1054
|
-
value.split(";").map((part) => {
|
|
1055
|
-
const [t, left2, right] = part.split(",").map((n) => parseFloat(n));
|
|
1056
|
-
return { t, left: left2, right: Number.isFinite(right) ? right : left2 };
|
|
1057
|
-
})
|
|
1058
|
-
);
|
|
1059
|
-
}
|
|
1060
|
-
var round2 = (n) => Math.round(n * 100) / 100;
|
|
1061
|
-
function gradientMarkup(def, extra = "") {
|
|
1062
|
-
const tag = def.type === "radial" ? "radialGradient" : "linearGradient";
|
|
1063
|
-
const attrs = Object.entries(def.attrs).map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`).join("");
|
|
1064
|
-
const stops = def.stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeAttr(s.stopColor)}"${s.stopOpacity !== void 0 ? ` stop-opacity="${s.stopOpacity}"` : ""}/>`).join("");
|
|
1065
|
-
return `<${tag} id="${escapeAttr(def.id)}"${attrs}${extra}>${stops}</${tag}>`;
|
|
1066
|
-
}
|
|
1067
|
-
function paintDefMarkup(def, gradient) {
|
|
1068
|
-
const extra = gradient ? ` data-vector-gradient="${escapeAttr(JSON.stringify(gradient))}"` : "";
|
|
1069
|
-
if (def.type !== "pattern") return gradientMarkup(def, extra);
|
|
1070
|
-
const attrs = Object.entries(def.attrs).map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`).join("");
|
|
1071
|
-
const inner = def.gradients.map((g) => gradientMarkup(g)).join("") + def.tiles.map((t) => `<path d="${t.d}" fill="${escapeAttr(t.fill)}"/>`).join("");
|
|
1072
|
-
return `<pattern id="${escapeAttr(def.id)}"${attrs}${extra}>${inner}</pattern>`;
|
|
1073
|
-
}
|
|
1074
|
-
var paintMarkup = (paint, defs) => defs.map((d, i) => paintDefMarkup(d, i === 0 && isVectorGradient(paint) ? paint : void 0));
|
|
1075
|
-
function blendFromAttribute(value, m) {
|
|
1076
|
-
if (!value) return null;
|
|
1077
|
-
let raw;
|
|
1078
|
-
try {
|
|
1079
|
-
raw = JSON.parse(value);
|
|
1080
|
-
} catch {
|
|
1081
|
-
return null;
|
|
1082
|
-
}
|
|
1083
|
-
if (!raw || typeof raw !== "object") return null;
|
|
1084
|
-
const r = raw;
|
|
1085
|
-
const out = {};
|
|
1086
|
-
if (r.spacing === "steps" || r.spacing === "distance" || r.spacing === "smoothColor") out.spacing = r.spacing;
|
|
1087
|
-
if (typeof r.steps === "number" && Number.isFinite(r.steps)) out.steps = Math.min(1e3, Math.max(1, Math.round(r.steps)));
|
|
1088
|
-
if (typeof r.distance === "number" && r.distance > 0) out.distance = r.distance;
|
|
1089
|
-
if (typeof r.orientToSpine === "boolean") out.orientToSpine = r.orientToSpine;
|
|
1090
|
-
if (typeof r.linear === "boolean") out.linear = r.linear;
|
|
1091
|
-
if (r.starts && typeof r.starts === "object") {
|
|
1092
|
-
const starts = Object.fromEntries(Object.entries(r.starts).filter((e) => Number.isInteger(e[1]) && e[1] >= 0));
|
|
1093
|
-
if (Object.keys(starts).length) out.starts = starts;
|
|
1094
|
-
}
|
|
1095
|
-
const spine = r.spine;
|
|
1096
|
-
if (spine && Array.isArray(spine.anchors) && spine.anchors.every((a) => a && Number.isFinite(a.x) && Number.isFinite(a.y))) {
|
|
1097
|
-
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 } } : {} })) };
|
|
1098
|
-
out.spine = m === IDENTITY_MATRIX ? sp : transformVectorNode({ id: "", type: "path", subpaths: [sp] }, m).subpaths[0];
|
|
1099
|
-
}
|
|
1100
|
-
return out;
|
|
1101
|
-
}
|
|
1102
|
-
function vectorDocumentToSvg(doc, options = {}) {
|
|
1103
|
-
const precision = options.precision ?? 3;
|
|
1104
|
-
const prefix = options.idPrefix ?? "vg-";
|
|
1105
|
-
const pretty = options.pretty !== false;
|
|
1106
|
-
const defs = [];
|
|
1107
|
-
const lines = [];
|
|
1108
|
-
const num = (n) => formatNumber(n, precision);
|
|
1109
|
-
const indent = (depth) => pretty ? " ".repeat(depth) : "";
|
|
1110
|
-
const common = (node) => {
|
|
1111
|
-
let out2 = ` id="${escapeAttr(node.id)}"`;
|
|
1112
|
-
if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => formatNumber(n, 6)).join(" ")})"`;
|
|
1113
|
-
if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num(node.opacity)}"`;
|
|
1114
|
-
if (node.visible === false) out2 += ` display="none"`;
|
|
1115
|
-
if (node.blendStep) out2 += ` data-vector-blend-step=""`;
|
|
1116
|
-
if (node.type === "group" && node.blend) out2 += ` data-vector-blend="${escapeAttr(JSON.stringify(node.blend))}"`;
|
|
1117
|
-
if (node.booleanOperation) out2 += ` data-vector-boolean-operation="${node.booleanOperation}"`;
|
|
1118
|
-
const blend = vectorBlendModeCss(node.blendMode);
|
|
1119
|
-
if (blend) out2 += ` style="mix-blend-mode:${blend}"`;
|
|
1120
|
-
if (node.filters?.length) {
|
|
1121
|
-
const filter = vectorNodeFilterSvg(node, `${prefix}filter-${node.id}`);
|
|
1122
|
-
const data = ` data-vector-filters="${escapeAttr(JSON.stringify(node.filters))}"`;
|
|
1123
|
-
if (filter) {
|
|
1124
|
-
defs.push(vectorFilterMarkup(filter, data));
|
|
1125
|
-
out2 += ` filter="url(#${escapeAttr(filter.id)})"`;
|
|
1126
|
-
} else out2 += data;
|
|
1127
|
-
}
|
|
1128
|
-
return out2;
|
|
1129
|
-
};
|
|
1130
|
-
const layerAttrs = (layer) => {
|
|
1131
|
-
let out2 = "";
|
|
1132
|
-
if (layer.opacity !== void 0 && layer.opacity < 1) out2 += ` opacity="${num(layer.opacity)}"`;
|
|
1133
|
-
const blend = vectorBlendModeCss(layer.blendMode);
|
|
1134
|
-
if (blend) out2 += ` style="mix-blend-mode:${blend}"`;
|
|
1135
|
-
return out2;
|
|
1136
|
-
};
|
|
1137
|
-
const strokePartLines = (node, parts, strokeValue, d, lineAttrs, depth) => {
|
|
1138
|
-
const out2 = [];
|
|
1139
|
-
if (options.strokes === "outline") {
|
|
1140
|
-
const area = bezierPathToSvg(vectorStrokeOutlinePath(subpathsToBezier(node.subpaths), node), { precision });
|
|
1141
|
-
out2.push(`${indent(depth)}<path data-vector-part="stroke-outline" d="${area}" fill="${escapeAttr(strokeValue)}"/>`);
|
|
1142
|
-
return out2;
|
|
1143
|
-
}
|
|
1144
|
-
let clip = "";
|
|
1145
|
-
if (parts.clip) {
|
|
1146
|
-
const rule = node.fillRule === "evenodd" ? "evenodd" : "nonzero";
|
|
1147
|
-
const id = escapeAttr(parts.clip.id);
|
|
1148
|
-
if (parts.clip.kind === "clip") {
|
|
1149
|
-
defs.push(`<clipPath id="${id}"><path d="${d}" clip-rule="${rule}"/></clipPath>`);
|
|
1150
|
-
clip = ` clip-path="url(#${id})"`;
|
|
1151
|
-
} else {
|
|
1152
|
-
const b = parts.clip.bounds;
|
|
1153
|
-
const box = `x="${num(b.x)}" y="${num(b.y)}" width="${num(b.width)}" height="${num(b.height)}"`;
|
|
1154
|
-
defs.push(`<mask id="${id}" maskUnits="userSpaceOnUse" ${box}><rect ${box} fill="#fff"/><path d="${d}" fill="#000" fill-rule="${rule}"/></mask>`);
|
|
1155
|
-
clip = ` mask="url(#${id})"`;
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
if (parts.outline !== void 0) out2.push(`${indent(depth)}<path data-vector-part="stroke-outline" d="${parts.outline}" fill="${escapeAttr(strokeValue)}"${clip}/>`);
|
|
1159
|
-
else out2.push(`${indent(depth)}<path data-vector-part="stroke" d="${parts.d}" fill="none"${lineAttrs(parts.width)}${clip}/>`);
|
|
1160
|
-
for (const h2 of parts.heads) out2.push(`${indent(depth)}<path data-vector-part="head" d="${h2}" fill="${escapeAttr(strokeValue)}"/>`);
|
|
1161
|
-
return out2;
|
|
1162
|
-
};
|
|
1163
|
-
const lineAttrsOf = (node, strokeValue) => (width) => {
|
|
1164
|
-
let out2 = ` stroke="${escapeAttr(strokeValue)}" stroke-width="${num(width)}"`;
|
|
1165
|
-
if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
|
|
1166
|
-
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
|
|
1167
|
-
if (node.strokeDasharray?.length) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
|
|
1168
|
-
return out2;
|
|
1169
|
-
};
|
|
1170
|
-
const liveCorners = (node) => {
|
|
1171
|
-
const corners = vectorCornersAttribute(node.subpaths, precision);
|
|
1172
|
-
return corners ? ` data-vector-d="${bezierPathToSvg(subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners}"` : "";
|
|
1173
|
-
};
|
|
1174
|
-
const emitStacked = (node, depth) => {
|
|
1175
|
-
const d = vectorPathData(node, precision);
|
|
1176
|
-
const rule = node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : "";
|
|
1177
|
-
let data = ` data-vector-path="" data-vector-fills="${escapeAttr(JSON.stringify(vectorFills(node)))}" data-vector-strokes="${escapeAttr(JSON.stringify(vectorStrokes(node)))}"`;
|
|
1178
|
-
if (node.fillRule === "evenodd") data += ` data-vector-fill-rule="evenodd"`;
|
|
1179
|
-
lines.push(`${indent(depth)}<g${common(node)}${data}>`);
|
|
1180
|
-
const layers = vectorPaintLayerNodes(node).filter((l) => l.drawn);
|
|
1181
|
-
let geometry = liveCorners(node);
|
|
1182
|
-
if (!layers.some((l) => l.kind === "fill")) {
|
|
1183
|
-
lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}" fill="none"${rule}${geometry}/>`);
|
|
1184
|
-
geometry = "";
|
|
1185
|
-
}
|
|
1186
|
-
for (const l of layers) {
|
|
1187
|
-
const id = `${prefix}${l.kind}-${node.id}-${l.index}`;
|
|
1188
|
-
const paint = vectorPaintToSvg(l.layer.paint, id, { node: l.node, precision, ...l.kind === "stroke" ? { role: "stroke" } : {} });
|
|
1189
|
-
defs.push(...paintMarkup(l.layer.paint, paint.defs));
|
|
1190
|
-
const attrs = ` data-vector-layer="${l.index}"${layerAttrs(l.layer)}`;
|
|
1191
|
-
if (l.kind === "fill") {
|
|
1192
|
-
lines.push(`${indent(depth + 1)}<path data-vector-part="fill"${attrs} d="${d}" fill="${escapeAttr(paint.value)}"${rule}${geometry}/>`);
|
|
1193
|
-
geometry = "";
|
|
1194
|
-
continue;
|
|
1195
|
-
}
|
|
1196
|
-
const lineAttrs = lineAttrsOf(l.node, paint.value);
|
|
1197
|
-
const parts = vectorStrokeSvgParts(l.node, `${prefix}clip-${node.id}-${l.index}`, precision);
|
|
1198
|
-
if (!parts) {
|
|
1199
|
-
lines.push(`${indent(depth + 1)}<path data-vector-part="stroke"${attrs} d="${d}" fill="none"${lineAttrs(l.node.strokeWidth ?? 1)}/>`);
|
|
1200
|
-
continue;
|
|
1201
|
-
}
|
|
1202
|
-
lines.push(`${indent(depth + 1)}<g data-vector-part="stroke-layer"${attrs}>`);
|
|
1203
|
-
lines.push(...strokePartLines(l.node, parts, paint.value, d, lineAttrs, depth + 2));
|
|
1204
|
-
lines.push(`${indent(depth + 1)}</g>`);
|
|
1205
|
-
}
|
|
1206
|
-
lines.push(`${indent(depth)}</g>`);
|
|
1207
|
-
};
|
|
1208
|
-
const emit = (node, depth) => {
|
|
1209
|
-
if (node.visible === false && !options.includeHidden) return;
|
|
1210
|
-
const wrappers = options.wrap?.[node.id] ?? [];
|
|
1211
|
-
wrappers.forEach((w2, i) => {
|
|
1212
|
-
let attrs = w2.id !== void 0 ? ` id="${escapeAttr(w2.id)}"` : "";
|
|
1213
|
-
if (w2.transform) attrs += ` transform="${escapeAttr(w2.transform)}"`;
|
|
1214
|
-
if (w2.clip) {
|
|
1215
|
-
const shape = Object.entries(w2.clip.attrs).map(([k, v]) => ` ${k}="${formatNumber(v, 4)}"`).join("");
|
|
1216
|
-
defs.push(`<clipPath id="${escapeAttr(w2.clip.id)}" clipPathUnits="objectBoundingBox"><${w2.clip.shape}${w2.clip.shapeId !== void 0 ? ` id="${escapeAttr(w2.clip.shapeId)}"` : ""}${shape}/></clipPath>`);
|
|
1217
|
-
attrs += ` clip-path="url(#${escapeAttr(w2.clip.id)})"`;
|
|
1218
|
-
}
|
|
1219
|
-
lines.push(`${indent(depth + i)}<g${attrs}>`);
|
|
1220
|
-
});
|
|
1221
|
-
emitNode(node, depth + wrappers.length);
|
|
1222
|
-
for (let i = wrappers.length - 1; i >= 0; i--) lines.push(`${indent(depth + i)}</g>`);
|
|
1223
|
-
};
|
|
1224
|
-
const capture = (fn) => {
|
|
1225
|
-
const at = lines.length;
|
|
1226
|
-
fn();
|
|
1227
|
-
return lines.splice(at).map((l) => l.trim()).join("");
|
|
1228
|
-
};
|
|
1229
|
-
const emitMaskGroup = (node, depth) => {
|
|
1230
|
-
const [source, ...contents] = node.children;
|
|
1231
|
-
const body = node.effects?.length && contents.length ? [{ id: `${node.id}~content`, type: "group", effects: node.effects, children: contents }] : contents;
|
|
1232
|
-
const settings = { type: node.mask.type, ...node.mask.invert ? { invert: true } : {} };
|
|
1233
|
-
lines.push(`${indent(depth)}<g${common(node)} data-vector-mask="${escapeAttr(JSON.stringify(settings))}">`);
|
|
1234
|
-
const mask = vectorMaskRenderData(node, `${prefix}mask-${node.id}`, `${prefix}mask-filter-${node.id}`, precision);
|
|
1235
|
-
let ref = "";
|
|
1236
|
-
if (mask && mask.type !== "clip") {
|
|
1237
|
-
const b = mask.bounds;
|
|
1238
|
-
const box = `x="${num(b.x)}" y="${num(b.y)}" width="${num(b.width)}" height="${num(b.height)}"`;
|
|
1239
|
-
if (mask.invertFilter) defs.push(vectorFilterMarkup(mask.invertFilter));
|
|
1240
|
-
const inner = capture(() => emitNode(source, depth + 1));
|
|
1241
|
-
const drawn = mask.filter ? `<g data-vector-part="mask-invert" filter="${escapeAttr(mask.filter)}">${inner}</g>` : inner;
|
|
1242
|
-
defs.push(`<mask id="${escapeAttr(mask.id)}" data-vector-part="mask-source" maskUnits="userSpaceOnUse" ${box}${mask.type === "alpha" ? ' mask-type="alpha"' : ""}>${drawn}</mask>`);
|
|
1243
|
-
ref = ` mask="url(#${escapeAttr(mask.id)})"`;
|
|
1244
|
-
} else {
|
|
1245
|
-
lines.push(`${indent(depth + 1)}<g data-vector-part="mask-source" display="none">`);
|
|
1246
|
-
emitNode(source, depth + 2);
|
|
1247
|
-
lines.push(`${indent(depth + 1)}</g>`);
|
|
1248
|
-
if (mask?.clip) {
|
|
1249
|
-
defs.push(`<clipPath id="${escapeAttr(mask.id)}">${mask.clip.map((c) => `<path d="${c.d}" clip-rule="${c.clipRule}"/>`).join("")}</clipPath>`);
|
|
1250
|
-
ref = ` clip-path="url(#${escapeAttr(mask.id)})"`;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
lines.push(`${indent(depth + 1)}<g data-vector-part="masked"${ref}>`);
|
|
1254
|
-
for (const child of body) emit(child, depth + 2);
|
|
1255
|
-
lines.push(`${indent(depth + 1)}</g>`);
|
|
1256
|
-
lines.push(`${indent(depth)}</g>`);
|
|
1257
|
-
};
|
|
1258
|
-
const emitNode = (node, depth) => {
|
|
1259
|
-
if (isVectorMaskGroup(node)) {
|
|
1260
|
-
emitMaskGroup(node, depth);
|
|
1261
|
-
return;
|
|
1262
|
-
}
|
|
1263
|
-
if (hasOwnVectorAppearance(node)) {
|
|
1264
|
-
const live2 = {
|
|
1265
|
-
kind: node.type,
|
|
1266
|
-
...node.effects?.length ? { effects: node.effects } : {},
|
|
1267
|
-
...node.type === "group" && node.liveBoolean ? { liveBoolean: node.liveBoolean } : {}
|
|
1268
|
-
};
|
|
1269
|
-
lines.push(`${indent(depth)}<g${common(node)} data-vector-live="${escapeAttr(JSON.stringify(live2))}">`);
|
|
1270
|
-
lines.push(`${indent(depth + 1)}<g data-vector-part="appearance">`);
|
|
1271
|
-
for (const shape of vectorAppearanceNodes(node)) emit(shape, depth + 2);
|
|
1272
|
-
lines.push(`${indent(depth + 1)}</g>`);
|
|
1273
|
-
lines.push(`${indent(depth + 1)}<g data-vector-part="source" display="none">`);
|
|
1274
|
-
if (node.type === "path") {
|
|
1275
|
-
const { effects: _e, transform: _t, opacity: _o, visible: _v, booleanOperation: _b, blendStep: _s, blendMode: _bm, filters: _f, ...source } = node;
|
|
1276
|
-
emit({ ...source, id: `${node.id}~src` }, depth + 2);
|
|
1277
|
-
} else for (const child of node.children) emit(child, depth + 2);
|
|
1278
|
-
lines.push(`${indent(depth + 1)}</g>`);
|
|
1279
|
-
lines.push(`${indent(depth)}</g>`);
|
|
1280
|
-
return;
|
|
1281
|
-
}
|
|
1282
|
-
if (node.type === "group") {
|
|
1283
|
-
lines.push(`${indent(depth)}<g${common(node)}>`);
|
|
1284
|
-
for (const child of node.children) emit(child, depth + 1);
|
|
1285
|
-
lines.push(`${indent(depth)}</g>`);
|
|
1286
|
-
return;
|
|
1287
|
-
}
|
|
1288
|
-
if (needsVectorPaintLayers(node)) {
|
|
1289
|
-
emitStacked(node, depth);
|
|
1290
|
-
return;
|
|
1291
|
-
}
|
|
1292
|
-
const fill = vectorPaintToSvg(node.fill, `${prefix}fill-${node.id}`, { node, precision });
|
|
1293
|
-
const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}`, { node, role: "stroke", precision });
|
|
1294
|
-
defs.push(...paintMarkup(node.fill, fill.defs), ...paintMarkup(node.stroke, stroke.defs));
|
|
1295
|
-
const d = vectorPathData(node, precision);
|
|
1296
|
-
const live = liveCorners(node);
|
|
1297
|
-
const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
|
|
1298
|
-
const lineAttrs = lineAttrsOf(node, stroke.value);
|
|
1299
|
-
const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
|
|
1300
|
-
if (!parts) {
|
|
1301
|
-
lines.push(`${indent(depth)}<path${common(node)} d="${d}"${fillAttrs}${stroke.value !== "none" ? lineAttrs(node.strokeWidth ?? 1) : ""}${live}/>`);
|
|
1302
|
-
return;
|
|
1303
|
-
}
|
|
1304
|
-
let data = ` data-vector-path="" data-vector-stroke-width="${num(node.strokeWidth ?? 1)}"`;
|
|
1305
|
-
if (parts.align !== "center") data += ` data-vector-stroke-align="${parts.align}"`;
|
|
1306
|
-
if (node.startArrow) data += ` data-vector-start-arrow="${node.startArrow}"`;
|
|
1307
|
-
if (node.endArrow) data += ` data-vector-end-arrow="${node.endArrow}"`;
|
|
1308
|
-
if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
|
|
1309
|
-
if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num(node.startArrowScale)}"`;
|
|
1310
|
-
if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num(node.endArrowScale)}"`;
|
|
1311
|
-
const profile = strokeWidthProfileOf(node);
|
|
1312
|
-
if (profile) {
|
|
1313
|
-
data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
|
|
1314
|
-
if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
|
|
1315
|
-
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
|
|
1316
|
-
if (node.strokeDasharray?.length) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num).join(" ")}"`;
|
|
1317
|
-
}
|
|
1318
|
-
lines.push(`${indent(depth)}<g${common(node)}${data}>`);
|
|
1319
|
-
lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
|
|
1320
|
-
lines.push(...strokePartLines(node, parts, stroke.value, d, lineAttrs, depth + 1));
|
|
1321
|
-
lines.push(`${indent(depth)}</g>`);
|
|
1322
|
-
};
|
|
1323
|
-
for (const node of doc.nodes) emit(node, 1);
|
|
1324
|
-
const w = num(doc.width);
|
|
1325
|
-
const h = num(doc.height);
|
|
1326
|
-
const nl = pretty ? "\n" : "";
|
|
1327
|
-
const out = [`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`];
|
|
1328
|
-
if (defs.length) out.push(`${indent(1)}<defs>${defs.join("")}</defs>`);
|
|
1329
|
-
if (doc.background && options.background !== false && hasPaint(doc.background)) {
|
|
1330
|
-
out.push(`${indent(1)}<rect width="${w}" height="${h}" fill="${escapeAttr(doc.background)}"/>`);
|
|
1331
|
-
}
|
|
1332
|
-
out.push(...lines, "</svg>");
|
|
1333
|
-
return out.join(nl);
|
|
1334
|
-
}
|
|
1335
|
-
var BOOLEAN_OPERATIONS = ["union", "subtract", "intersect", "exclude"];
|
|
1336
|
-
function parseXml(source) {
|
|
1337
|
-
const text = source.replace(/<!--[\s\S]*?-->/g, "").replace(/<\?[\s\S]*?\?>/g, "").replace(/<!\[CDATA\[[\s\S]*?\]\]>/g, "").replace(/<!DOCTYPE[^>[]*(\[[\s\S]*?\])?\s*>/gi, "");
|
|
1338
|
-
const root = { name: "#root", attrs: {}, children: [] };
|
|
1339
|
-
const stack = [root];
|
|
1340
|
-
const tag = /<(\/?)([A-Za-z_][\w:.-]*)((?:\s+[^\s=/>]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?)*)\s*(\/?)>/g;
|
|
1341
|
-
const attr = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g;
|
|
1342
|
-
let m;
|
|
1343
|
-
while (m = tag.exec(text)) {
|
|
1344
|
-
const [, closing, rawName, rawAttrs, selfClosing] = m;
|
|
1345
|
-
const name = rawName.replace(/^svg:/, "");
|
|
1346
|
-
if (closing) {
|
|
1347
|
-
for (let i = stack.length - 1; i > 0; i--) {
|
|
1348
|
-
if (stack[i].name === name) {
|
|
1349
|
-
stack.length = i;
|
|
1350
|
-
break;
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
continue;
|
|
1354
|
-
}
|
|
1355
|
-
const attrs = {};
|
|
1356
|
-
let a;
|
|
1357
|
-
attr.lastIndex = 0;
|
|
1358
|
-
while (a = attr.exec(rawAttrs)) attrs[a[1]] = decodeEntities(a[2] ?? a[3] ?? a[4] ?? "");
|
|
1359
|
-
const el = { name, attrs, children: [] };
|
|
1360
|
-
stack[stack.length - 1].children.push(el);
|
|
1361
|
-
if (!selfClosing) stack.push(el);
|
|
1362
|
-
}
|
|
1363
|
-
const find = (el) => {
|
|
1364
|
-
if (el.name === "svg") return el;
|
|
1365
|
-
for (const c of el.children) {
|
|
1366
|
-
const f = find(c);
|
|
1367
|
-
if (f) return f;
|
|
1368
|
-
}
|
|
1369
|
-
return null;
|
|
1370
|
-
};
|
|
1371
|
-
return find(root);
|
|
1372
|
-
}
|
|
1373
|
-
function decodeEntities(s) {
|
|
1374
|
-
return s.replace(/&#x([0-9a-f]+);/gi, (_, h) => String.fromCodePoint(parseInt(h, 16))).replace(/&#(\d+);/g, (_, d) => String.fromCodePoint(parseInt(d, 10))).replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
1375
|
-
}
|
|
1376
|
-
var numberList = (s) => (s ?? "").trim().split(/[\s,]+|(?=-)(?<![eE])/).filter(Boolean).map(Number).filter((n) => Number.isFinite(n));
|
|
1377
|
-
var length = (s, fallback = 0) => {
|
|
1378
|
-
if (s === void 0) return fallback;
|
|
1379
|
-
const n = parseFloat(s);
|
|
1380
|
-
return Number.isFinite(n) ? n : fallback;
|
|
1381
|
-
};
|
|
1382
|
-
function parseSvgTransform(value) {
|
|
1383
|
-
let m = IDENTITY_MATRIX;
|
|
1384
|
-
if (!value) return m;
|
|
1385
|
-
const re = /(matrix|translate|scale|rotate|skewX|skewY)\s*\(([^)]*)\)/g;
|
|
1386
|
-
let t;
|
|
1387
|
-
while (t = re.exec(value)) {
|
|
1388
|
-
const n = numberList(t[2]);
|
|
1389
|
-
let step = IDENTITY_MATRIX;
|
|
1390
|
-
switch (t[1]) {
|
|
1391
|
-
case "matrix":
|
|
1392
|
-
if (n.length >= 6) step = [n[0], n[1], n[2], n[3], n[4], n[5]];
|
|
1393
|
-
break;
|
|
1394
|
-
case "translate":
|
|
1395
|
-
step = translateMatrix(n[0] ?? 0, n[1] ?? 0);
|
|
1396
|
-
break;
|
|
1397
|
-
case "scale":
|
|
1398
|
-
step = scaleMatrix(n[0] ?? 1, n[1] ?? n[0] ?? 1);
|
|
1399
|
-
break;
|
|
1400
|
-
case "rotate":
|
|
1401
|
-
step = rotateMatrix(n[0] ?? 0, { x: n[1] ?? 0, y: n[2] ?? 0 });
|
|
1402
|
-
break;
|
|
1403
|
-
case "skewX":
|
|
1404
|
-
step = [1, 0, Math.tan((n[0] ?? 0) * Math.PI / 180), 1, 0, 0];
|
|
1405
|
-
break;
|
|
1406
|
-
case "skewY":
|
|
1407
|
-
step = [1, Math.tan((n[0] ?? 0) * Math.PI / 180), 0, 1, 0, 0];
|
|
1408
|
-
break;
|
|
1409
|
-
}
|
|
1410
|
-
m = multiplyMatrices(step, m);
|
|
1411
|
-
}
|
|
1412
|
-
return m;
|
|
1413
|
-
}
|
|
1414
|
-
var STYLE_KEYS2 = {
|
|
1415
|
-
fill: "fill",
|
|
1416
|
-
stroke: "stroke",
|
|
1417
|
-
"stroke-width": "strokeWidth",
|
|
1418
|
-
"stroke-linecap": "strokeLinecap",
|
|
1419
|
-
"stroke-linejoin": "strokeLinejoin",
|
|
1420
|
-
"stroke-dasharray": "strokeDasharray",
|
|
1421
|
-
"fill-rule": "fillRule",
|
|
1422
|
-
"fill-opacity": "fillOpacity",
|
|
1423
|
-
"stroke-opacity": "strokeOpacity",
|
|
1424
|
-
"mix-blend-mode": "mixBlendMode",
|
|
1425
|
-
filter: "filter"
|
|
1426
|
-
};
|
|
1427
|
-
function readStyle(el, parent) {
|
|
1428
|
-
const { mixBlendMode: _m, filter: _f, ...inherited } = parent;
|
|
1429
|
-
const out = { ...inherited };
|
|
1430
|
-
for (const [attr, key] of Object.entries(STYLE_KEYS2)) if (el.attrs[attr] !== void 0) out[key] = el.attrs[attr].trim();
|
|
1431
|
-
for (const decl of (el.attrs.style ?? "").split(";")) {
|
|
1432
|
-
const i = decl.indexOf(":");
|
|
1433
|
-
if (i < 0) continue;
|
|
1434
|
-
const key = STYLE_KEYS2[decl.slice(0, i).trim()];
|
|
1435
|
-
if (key) out[key] = decl.slice(i + 1).replace(/!important/, "").trim();
|
|
1436
|
-
}
|
|
1437
|
-
return out;
|
|
1438
|
-
}
|
|
1439
|
-
function readStops(el) {
|
|
1440
|
-
return el.children.filter((c) => c.name === "stop").map((c) => {
|
|
1441
|
-
const style = Object.fromEntries(
|
|
1442
|
-
(c.attrs.style ?? "").split(";").map((d) => d.split(":").map((s) => s.trim())).filter((d) => d.length === 2)
|
|
1443
|
-
);
|
|
1444
|
-
const raw = c.attrs.offset ?? "0";
|
|
1445
|
-
const offset = raw.endsWith("%") ? parseFloat(raw) / 100 : parseFloat(raw);
|
|
1446
|
-
const opacity = style["stop-opacity"] ?? c.attrs["stop-opacity"];
|
|
1447
|
-
return {
|
|
1448
|
-
offset: Number.isFinite(offset) ? Math.max(0, Math.min(1, offset)) : 0,
|
|
1449
|
-
color: style["stop-color"] ?? c.attrs["stop-color"] ?? "#000",
|
|
1450
|
-
...opacity !== void 0 ? { opacity: parseFloat(opacity) } : {}
|
|
1451
|
-
};
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
var fraction = (s, fallback) => {
|
|
1455
|
-
if (s === void 0) return fallback;
|
|
1456
|
-
const n = parseFloat(s);
|
|
1457
|
-
if (!Number.isFinite(n)) return fallback;
|
|
1458
|
-
return s.trim().endsWith("%") ? n / 100 : n;
|
|
1459
|
-
};
|
|
1460
|
-
var PAINT_SERVERS = /* @__PURE__ */ new Set(["linearGradient", "radialGradient", "pattern"]);
|
|
1461
|
-
function collectGradients(el, raw) {
|
|
1462
|
-
if (PAINT_SERVERS.has(el.name) && el.attrs.id) raw.set(el.attrs.id, el);
|
|
1463
|
-
for (const c of el.children) collectGradients(c, raw);
|
|
1464
|
-
}
|
|
1465
|
-
function collectMasks(el, raw) {
|
|
1466
|
-
if ((el.name === "clipPath" || el.name === "mask") && el.attrs.id) raw.set(el.attrs.id, el);
|
|
1467
|
-
for (const c of el.children) collectMasks(c, raw);
|
|
1468
|
-
}
|
|
1469
|
-
function attrOrStyle(el, name) {
|
|
1470
|
-
for (const decl of (el.attrs.style ?? "").split(";")) {
|
|
1471
|
-
const i = decl.indexOf(":");
|
|
1472
|
-
if (i >= 0 && decl.slice(0, i).trim() === name) return decl.slice(i + 1).replace(/!important/, "").trim();
|
|
1473
|
-
}
|
|
1474
|
-
return el.attrs[name];
|
|
1475
|
-
}
|
|
1476
|
-
function urlId(value) {
|
|
1477
|
-
return /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec((value ?? "").trim())?.[1] ?? null;
|
|
1478
|
-
}
|
|
1479
|
-
function clipRulesAsFillRules(el) {
|
|
1480
|
-
const attrs = { ...el.attrs };
|
|
1481
|
-
const rule = attrOrStyle(el, "clip-rule");
|
|
1482
|
-
if (rule) attrs["fill-rule"] = rule;
|
|
1483
|
-
return { ...el, attrs, children: el.children.map(clipRulesAsFillRules) };
|
|
1484
|
-
}
|
|
1485
|
-
function collectFilters(el, raw) {
|
|
1486
|
-
if (el.name === "filter" && el.attrs.id) raw.set(el.attrs.id, el);
|
|
1487
|
-
for (const c of el.children) collectFilters(c, raw);
|
|
1488
|
-
}
|
|
1489
|
-
function filtersFromElement(el) {
|
|
1490
|
-
const own = el.attrs["data-vector-filters"];
|
|
1491
|
-
if (own !== void 0) {
|
|
1492
|
-
try {
|
|
1493
|
-
return sanitizeVectorFilters(JSON.parse(own));
|
|
1494
|
-
} catch {
|
|
1495
|
-
return [];
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
const prims = el.children.filter((c) => c.name.startsWith("fe"));
|
|
1499
|
-
const out = [];
|
|
1500
|
-
for (const p of prims) {
|
|
1501
|
-
const std = numberList(p.attrs.stdDeviation)[0] ?? 0;
|
|
1502
|
-
if (p.name === "feDropShadow") {
|
|
1503
|
-
const style = Object.fromEntries(
|
|
1504
|
-
(p.attrs.style ?? "").split(";").map((d) => d.split(":").map((x) => x.trim())).filter((d) => d.length === 2)
|
|
1505
|
-
);
|
|
1506
|
-
const opacity = parseFloat(style["flood-opacity"] ?? p.attrs["flood-opacity"] ?? "1");
|
|
1507
|
-
out.push({
|
|
1508
|
-
type: "dropShadow",
|
|
1509
|
-
params: {
|
|
1510
|
-
color: style["flood-color"] ?? p.attrs["flood-color"] ?? "#000000",
|
|
1511
|
-
opacity: Number.isFinite(opacity) ? opacity : 1,
|
|
1512
|
-
offsetX: length(p.attrs.dx, 2),
|
|
1513
|
-
offsetY: length(p.attrs.dy, 2),
|
|
1514
|
-
blur: std * 2,
|
|
1515
|
-
spread: 0
|
|
1516
|
-
}
|
|
1517
|
-
});
|
|
1518
|
-
} else if (p.name === "feGaussianBlur" && prims.length === 1 && (p.attrs.in === void 0 || p.attrs.in === "SourceGraphic")) {
|
|
1519
|
-
out.push({ type: "layerBlur", params: { radius: std * 2 } });
|
|
1520
|
-
} else return [];
|
|
1521
|
-
}
|
|
1522
|
-
return sanitizeVectorFilters(out);
|
|
1523
|
-
}
|
|
1524
|
-
function gradientFromAttribute(value) {
|
|
1525
|
-
if (!value) return null;
|
|
1526
|
-
try {
|
|
1527
|
-
return sanitizeVectorGradient(JSON.parse(value));
|
|
1528
|
-
} catch {
|
|
1529
|
-
return null;
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
function resolveGradient(id, raw) {
|
|
1533
|
-
const first = raw.get(id);
|
|
1534
|
-
if (!first) return null;
|
|
1535
|
-
const own = gradientFromAttribute(first.attrs["data-vector-gradient"]);
|
|
1536
|
-
if (own) return own;
|
|
1537
|
-
if (first.name === "pattern") return null;
|
|
1538
|
-
const chain = [];
|
|
1539
|
-
for (let el = first; el && !chain.includes(el) && chain.length < 32; ) {
|
|
1540
|
-
chain.push(el);
|
|
1541
|
-
const href = el.attrs.href ?? el.attrs["xlink:href"];
|
|
1542
|
-
el = href?.startsWith("#") ? raw.get(href.slice(1)) : void 0;
|
|
1543
|
-
}
|
|
1544
|
-
const attr = (name) => chain.find((el) => el.attrs[name] !== void 0)?.attrs[name];
|
|
1545
|
-
const stops = chain.map(readStops).find((s) => s.length) ?? [];
|
|
1546
|
-
if (!stops.length) return null;
|
|
1547
|
-
const user = attr("gradientUnits") === "userSpaceOnUse";
|
|
1548
|
-
const t = parseSvgTransform(attr("gradientTransform"));
|
|
1549
|
-
const plain = t === IDENTITY_MATRIX || t[0] === 1 && t[1] === 0 && t[2] === 0 && t[3] === 1 && t[4] === 0 && t[5] === 0;
|
|
1550
|
-
const at = (name, fallback) => user ? length(attr(name), fallback) : fraction(attr(name), fallback);
|
|
1551
|
-
const spreadAttr = attr("spreadMethod");
|
|
1552
|
-
const spread = spreadAttr === "reflect" || spreadAttr === "repeat" ? { spread: spreadAttr } : {};
|
|
1553
|
-
const map = (p) => applySvgMatrix(t, p);
|
|
1554
|
-
if (first.name === "radialGradient") {
|
|
1555
|
-
const cx = at("cx", user ? 0 : 0.5);
|
|
1556
|
-
const cy = at("cy", user ? 0 : 0.5);
|
|
1557
|
-
const r = at("r", user ? 0 : 0.5);
|
|
1558
|
-
const fx = attr("fx") !== void 0 ? at("fx", cx) : cx;
|
|
1559
|
-
const fy = attr("fy") !== void 0 ? at("fy", cy) : cy;
|
|
1560
|
-
const fr = attr("fr") !== void 0 ? at("fr", 0) : 0;
|
|
1561
|
-
const focal = Math.abs(fx - cx) > 1e-9 || Math.abs(fy - cy) > 1e-9;
|
|
1562
|
-
if (!user && plain && !focal && !(fr > 0)) return { type: "radial", cx, cy, r, stops, ...spread };
|
|
1563
|
-
const g = {
|
|
1564
|
-
type: "radial",
|
|
1565
|
-
stops,
|
|
1566
|
-
start: map({ x: cx, y: cy }),
|
|
1567
|
-
end: map({ x: cx + r, y: cy }),
|
|
1568
|
-
width: map({ x: cx, y: cy + r }),
|
|
1569
|
-
units: user ? "userSpaceOnUse" : "objectBoundingBox",
|
|
1570
|
-
...spread
|
|
1571
|
-
};
|
|
1572
|
-
if (focal) g.focal = map({ x: fx, y: fy });
|
|
1573
|
-
if (fr > 0 && r > 0) g.focalRadius = Math.min(0.99, fr / r);
|
|
1574
|
-
return g;
|
|
1575
|
-
}
|
|
1576
|
-
const x1 = at("x1", 0);
|
|
1577
|
-
const y1 = at("y1", 0);
|
|
1578
|
-
const x2 = attr("x2") !== void 0 ? at("x2", 1) : user ? 0 : 1;
|
|
1579
|
-
const y2 = at("y2", 0);
|
|
1580
|
-
if (!user && plain) {
|
|
1581
|
-
const angle = Math.round((Math.atan2(x2 - x1, -(y2 - y1)) * 180 / Math.PI + 360) % 360);
|
|
1582
|
-
const c = angleToSvgLinearCoords(angle);
|
|
1583
|
-
const len = Math.hypot(x2 - x1, y2 - y1);
|
|
1584
|
-
const d = len > 0 ? { x: (x2 - x1) / len, y: (y2 - y1) / len } : { x: 0, y: 1 };
|
|
1585
|
-
const along = (x, y) => x * d.x + y * d.y;
|
|
1586
|
-
const same = len > 0 && Math.abs((c.x2 - c.x1) * d.y - (c.y2 - c.y1) * d.x) < 1e-3 && Math.abs(along(c.x1, c.y1) - along(x1, y1)) < 1e-3 && Math.abs(along(c.x2, c.y2) - along(x2, y2)) < 1e-3;
|
|
1587
|
-
if (same) return { type: "linear", angle, stops, ...spread };
|
|
1588
|
-
}
|
|
1589
|
-
const start = { x: x1, y: y1 };
|
|
1590
|
-
const end = { x: x2, y: y2 };
|
|
1591
|
-
return {
|
|
1592
|
-
type: "linear",
|
|
1593
|
-
stops,
|
|
1594
|
-
start: map(start),
|
|
1595
|
-
end: map(end),
|
|
1596
|
-
width: map({ x: x1 - (y2 - y1), y: y1 + (x2 - x1) }),
|
|
1597
|
-
units: user ? "userSpaceOnUse" : "objectBoundingBox",
|
|
1598
|
-
...spread
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
var applySvgMatrix = (m, p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] });
|
|
1602
|
-
function parseVectorSvg(svg, options = {}) {
|
|
1603
|
-
const root = parseXml(svg);
|
|
1604
|
-
if (!root) throw new Error("The markup has no <svg> element.");
|
|
1605
|
-
let counter = 0;
|
|
1606
|
-
const usedIds = /* @__PURE__ */ new Set();
|
|
1607
|
-
const createId = options.createId ?? (() => `node-${++counter}`);
|
|
1608
|
-
const idFor = (el) => {
|
|
1609
|
-
const id = el.attrs.id;
|
|
1610
|
-
if (id && !usedIds.has(id)) {
|
|
1611
|
-
usedIds.add(id);
|
|
1612
|
-
return id;
|
|
1613
|
-
}
|
|
1614
|
-
let next = createId();
|
|
1615
|
-
while (usedIds.has(next)) next = createId();
|
|
1616
|
-
usedIds.add(next);
|
|
1617
|
-
return next;
|
|
1618
|
-
};
|
|
1619
|
-
const rawGradients = /* @__PURE__ */ new Map();
|
|
1620
|
-
collectGradients(root, rawGradients);
|
|
1621
|
-
const rawFilters = /* @__PURE__ */ new Map();
|
|
1622
|
-
collectFilters(root, rawFilters);
|
|
1623
|
-
const rawMasks = /* @__PURE__ */ new Map();
|
|
1624
|
-
collectMasks(root, rawMasks);
|
|
1625
|
-
const resolving = /* @__PURE__ */ new Set();
|
|
1626
|
-
const filtersOf = (value) => {
|
|
1627
|
-
const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec((value ?? "").trim());
|
|
1628
|
-
const el = url ? rawFilters.get(url[1]) : void 0;
|
|
1629
|
-
return el ? filtersFromElement(el) : [];
|
|
1630
|
-
};
|
|
1631
|
-
const paint = (value) => {
|
|
1632
|
-
const v = value.trim();
|
|
1633
|
-
const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec(v);
|
|
1634
|
-
if (url) return resolveGradient(url[1], rawGradients) ?? null;
|
|
1635
|
-
if (v === "none" || v === "") return null;
|
|
1636
|
-
return v;
|
|
1637
|
-
};
|
|
1638
|
-
const viewBox = numberList(root.attrs.viewBox);
|
|
1639
|
-
const width = viewBox.length === 4 ? viewBox[2] : length(root.attrs.width, 300);
|
|
1640
|
-
const height = viewBox.length === 4 ? viewBox[3] : length(root.attrs.height, 150);
|
|
1641
|
-
const rootMatrix = viewBox.length === 4 ? translateMatrix(-viewBox[0], -viewBox[1]) : IDENTITY_MATRIX;
|
|
1642
|
-
const SKIP = /* @__PURE__ */ new Set(["defs", "clipPath", "mask", "symbol", "style", "title", "desc", "metadata", "pattern", "marker", "filter", "text", "image", "use", "linearGradient", "radialGradient", "script", "foreignObject"]);
|
|
1643
|
-
const shapeOf = (el) => {
|
|
1644
|
-
const a = el.attrs;
|
|
1645
|
-
switch (el.name) {
|
|
1646
|
-
case "path": {
|
|
1647
|
-
if (a["data-vector-d"] && a["data-vector-corners"]) {
|
|
1648
|
-
const sharp = bezierToSubpaths(a["data-vector-d"]);
|
|
1649
|
-
if (sharp.length) return applyVectorCornersAttribute(sharp, a["data-vector-corners"]);
|
|
1650
|
-
}
|
|
1651
|
-
return a.d ? bezierToSubpaths(a.d) : null;
|
|
1652
|
-
}
|
|
1653
|
-
case "rect": {
|
|
1654
|
-
const w = length(a.width);
|
|
1655
|
-
const h = length(a.height);
|
|
1656
|
-
if (w <= 0 || h <= 0) return null;
|
|
1657
|
-
const r = length(a.rx ?? a.ry, 0);
|
|
1658
|
-
return [rectSubpath({ x: length(a.x), y: length(a.y), width: w, height: h }, r)];
|
|
1659
|
-
}
|
|
1660
|
-
case "circle": {
|
|
1661
|
-
const r = length(a.r);
|
|
1662
|
-
if (r <= 0) return null;
|
|
1663
|
-
const cx = length(a.cx);
|
|
1664
|
-
const cy = length(a.cy);
|
|
1665
|
-
return [ellipseSubpath({ x: cx - r, y: cy - r, width: 2 * r, height: 2 * r })];
|
|
1666
|
-
}
|
|
1667
|
-
case "ellipse": {
|
|
1668
|
-
const rx = length(a.rx);
|
|
1669
|
-
const ry = length(a.ry);
|
|
1670
|
-
if (rx <= 0 || ry <= 0) return null;
|
|
1671
|
-
return [ellipseSubpath({ x: length(a.cx) - rx, y: length(a.cy) - ry, width: 2 * rx, height: 2 * ry })];
|
|
1672
|
-
}
|
|
1673
|
-
case "line":
|
|
1674
|
-
return [lineSubpath({ x: length(a.x1), y: length(a.y1) }, { x: length(a.x2), y: length(a.y2) })];
|
|
1675
|
-
case "polyline":
|
|
1676
|
-
case "polygon": {
|
|
1677
|
-
const n = numberList(a.points);
|
|
1678
|
-
if (n.length < 4) return null;
|
|
1679
|
-
const anchors = [];
|
|
1680
|
-
for (let i = 0; i + 1 < n.length; i += 2) anchors.push({ x: n[i], y: n[i + 1] });
|
|
1681
|
-
return [{ anchors, closed: el.name === "polygon" }];
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
return null;
|
|
1685
|
-
};
|
|
1686
|
-
const strokeStyle = (s) => ({
|
|
1687
|
-
...s.strokeWidth ? { strokeWidth: length(s.strokeWidth, 1) } : {},
|
|
1688
|
-
...s.strokeLinecap ? { strokeLinecap: s.strokeLinecap } : {},
|
|
1689
|
-
...s.strokeLinejoin ? { strokeLinejoin: s.strokeLinejoin } : {},
|
|
1690
|
-
...s.strokeDasharray && s.strokeDasharray !== "none" ? { strokeDasharray: numberList(s.strokeDasharray) } : {},
|
|
1691
|
-
...s.fillRule === "evenodd" ? { fillRule: "evenodd" } : {}
|
|
1692
|
-
});
|
|
1693
|
-
const placed = (node, m) => {
|
|
1694
|
-
if (m === IDENTITY_MATRIX) return node;
|
|
1695
|
-
const next = transformVectorNode(node, m);
|
|
1696
|
-
const strokeScale = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2]));
|
|
1697
|
-
if (strokeScale > 0 && Math.abs(strokeScale - 1) > 1e-9) {
|
|
1698
|
-
if (hasPaint(next.stroke)) next.strokeWidth = (next.strokeWidth ?? 1) * strokeScale;
|
|
1699
|
-
if (Array.isArray(next.strokes)) next.strokes = next.strokes.map((l) => (l.kind ?? "paint") === "paint" ? { ...l, strokeWidth: (l.strokeWidth ?? 1) * strokeScale } : l);
|
|
1700
|
-
}
|
|
1701
|
-
return normalizeVectorPaintStacks(next, "stack");
|
|
1702
|
-
};
|
|
1703
|
-
const withOpacities = (node, s) => {
|
|
1704
|
-
let out = node;
|
|
1705
|
-
const fo = s.fillOpacity !== void 0 ? fraction(s.fillOpacity, 1) : 1;
|
|
1706
|
-
const so = s.strokeOpacity !== void 0 ? fraction(s.strokeOpacity, 1) : 1;
|
|
1707
|
-
if (fo < 1 && hasPaint(out.fill)) out = withVectorFills(out, [{ paint: out.fill, opacity: Math.max(0, fo) }]);
|
|
1708
|
-
if (so < 1 && hasPaint(out.stroke)) out = withVectorStrokes(out, vectorStrokes(out).map((l) => ({ ...l, opacity: Math.max(0, so) })));
|
|
1709
|
-
return out;
|
|
1710
|
-
};
|
|
1711
|
-
const stackedPath = (g, _style, m, meta) => {
|
|
1712
|
-
const fillEl = g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === "fill");
|
|
1713
|
-
const subpaths = fillEl ? shapeOf(fillEl) : null;
|
|
1714
|
-
if (!subpaths?.length) return null;
|
|
1715
|
-
const parse = (v) => {
|
|
1716
|
-
try {
|
|
1717
|
-
return v ? JSON.parse(v) : [];
|
|
1718
|
-
} catch {
|
|
1719
|
-
return [];
|
|
1720
|
-
}
|
|
1721
|
-
};
|
|
1722
|
-
let node = { id: idFor(g), type: "path", ...meta, subpaths, fill: null, stroke: null };
|
|
1723
|
-
if (g.attrs["data-vector-fill-rule"] === "evenodd") node.fillRule = "evenodd";
|
|
1724
|
-
node = withVectorFills(node, sanitizeVectorFills(parse(g.attrs["data-vector-fills"])));
|
|
1725
|
-
node = withVectorStrokes(node, sanitizeVectorStrokes(parse(g.attrs["data-vector-strokes"])));
|
|
1726
|
-
return placed(node, m);
|
|
1727
|
-
};
|
|
1728
|
-
const mergeStacked = (nodes2) => {
|
|
1729
|
-
const out = [];
|
|
1730
|
-
const key = (n) => JSON.stringify([n.subpaths, n.transform ?? null, n.opacity ?? 1, n.visible !== false, n.filters ?? null, n.blendMode ?? "normal", n.fillRule ?? "nonzero", n.booleanOperation ?? null]);
|
|
1731
|
-
for (const n of nodes2) {
|
|
1732
|
-
const prev = out[out.length - 1];
|
|
1733
|
-
if (prev?.type === "path" && n.type === "path" && key(prev) === key(n)) {
|
|
1734
|
-
const fills = vectorFills(n);
|
|
1735
|
-
const strokesBefore = vectorStrokes(prev);
|
|
1736
|
-
if (!(fills.length && strokesBefore.length)) {
|
|
1737
|
-
let merged = withVectorFills(prev, [...vectorFills(prev), ...fills]);
|
|
1738
|
-
merged = withVectorStrokes(merged, [...strokesBefore, ...vectorStrokes(n)]);
|
|
1739
|
-
out[out.length - 1] = merged;
|
|
1740
|
-
continue;
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
out.push(n);
|
|
1744
|
-
}
|
|
1745
|
-
return out;
|
|
1746
|
-
};
|
|
1747
|
-
const mergedPath = (g, style, m, meta) => {
|
|
1748
|
-
const part = (name) => g.children.find((c) => c.name === "path" && c.attrs["data-vector-part"] === name);
|
|
1749
|
-
const fillEl = part("fill");
|
|
1750
|
-
const subpaths = fillEl ? shapeOf(fillEl) : null;
|
|
1751
|
-
if (!fillEl || !subpaths?.length) return null;
|
|
1752
|
-
const a = g.attrs;
|
|
1753
|
-
const fs = readStyle(fillEl, style);
|
|
1754
|
-
const strokeEl = part("stroke");
|
|
1755
|
-
const outlineEl = part("stroke-outline");
|
|
1756
|
-
const ss = strokeEl ? readStyle(strokeEl, style) : null;
|
|
1757
|
-
const width2 = length(a["data-vector-stroke-width"], ss ? length(ss.strokeWidth, 1) : 1);
|
|
1758
|
-
const arrow = (v) => VECTOR_ARROWHEADS.includes(v) ? v : void 0;
|
|
1759
|
-
const scale2 = (v) => {
|
|
1760
|
-
const n = v === void 0 ? NaN : parseFloat(v);
|
|
1761
|
-
return n > 0 ? n : void 0;
|
|
1762
|
-
};
|
|
1763
|
-
const align = a["data-vector-stroke-align"];
|
|
1764
|
-
const profile = widthProfileFromAttribute(a["data-vector-width-profile"]);
|
|
1765
|
-
const kept = (key, values) => values.includes(a[key]) ? a[key] : void 0;
|
|
1766
|
-
const keptCap = kept("data-vector-stroke-linecap", ["round", "square"]);
|
|
1767
|
-
const keptJoin = kept("data-vector-stroke-linejoin", ["round", "bevel"]);
|
|
1768
|
-
const keptDash = a["data-vector-stroke-dasharray"] ? numberList(a["data-vector-stroke-dasharray"]) : void 0;
|
|
1769
|
-
const node = {
|
|
1770
|
-
id: idFor(g),
|
|
1771
|
-
type: "path",
|
|
1772
|
-
...meta,
|
|
1773
|
-
subpaths,
|
|
1774
|
-
fill: paint(fs.fill),
|
|
1775
|
-
stroke: ss ? paint(ss.stroke) : outlineEl ? paint(readStyle(outlineEl, style).fill) : null,
|
|
1776
|
-
...ss ? strokeStyle(ss) : {},
|
|
1777
|
-
...fs.fillRule === "evenodd" ? { fillRule: "evenodd" } : {},
|
|
1778
|
-
strokeWidth: width2,
|
|
1779
|
-
...align === "inside" || align === "outside" ? { strokeAlign: align } : {},
|
|
1780
|
-
...arrow(a["data-vector-start-arrow"]) ? { startArrow: arrow(a["data-vector-start-arrow"]) } : {},
|
|
1781
|
-
...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
|
|
1782
|
-
...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
|
|
1783
|
-
...scale2(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale2(a["data-vector-start-arrow-scale"]) } : {},
|
|
1784
|
-
...scale2(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale2(a["data-vector-end-arrow-scale"]) } : {},
|
|
1785
|
-
...profile.length ? { strokeWidthProfile: profile } : {},
|
|
1786
|
-
...keptCap ? { strokeLinecap: keptCap } : {},
|
|
1787
|
-
...keptJoin ? { strokeLinejoin: keptJoin } : {},
|
|
1788
|
-
...keptDash?.length ? { strokeDasharray: keptDash } : {}
|
|
1789
|
-
};
|
|
1790
|
-
return placed(node, m);
|
|
1791
|
-
};
|
|
1792
|
-
const liveNode = (g, style, m, meta) => {
|
|
1793
|
-
let raw = {};
|
|
1794
|
-
try {
|
|
1795
|
-
raw = JSON.parse(g.attrs["data-vector-live"]) ?? {};
|
|
1796
|
-
} catch {
|
|
1797
|
-
raw = {};
|
|
1798
|
-
}
|
|
1799
|
-
const sourceEl = g.children.find((c) => c.name === "g" && c.attrs["data-vector-part"] === "source");
|
|
1800
|
-
if (!sourceEl) return null;
|
|
1801
|
-
const inner = visit(sourceEl, style, m);
|
|
1802
|
-
const effects = sanitizeVectorEffects(raw.effects);
|
|
1803
|
-
const liveBoolean = BOOLEAN_OPERATIONS.includes(raw.liveBoolean) ? raw.liveBoolean : null;
|
|
1804
|
-
const id = idFor(g);
|
|
1805
|
-
if (raw.kind === "path") {
|
|
1806
|
-
const path = inner.find((n) => n.type === "path");
|
|
1807
|
-
if (!path) return null;
|
|
1808
|
-
const { residual } = decomposeRigid(m);
|
|
1809
|
-
const { visible: _v, ...rest } = path;
|
|
1810
|
-
return { ...rest, id, ...meta, ...effects.length ? { effects: transformVectorEffects(effects, residual) } : {} };
|
|
1811
|
-
}
|
|
1812
|
-
if (!inner.length) return null;
|
|
1813
|
-
const group = { id, type: "group", ...meta, children: inner };
|
|
1814
|
-
if (effects.length) group.effects = transformVectorEffects(effects, m);
|
|
1815
|
-
if (liveBoolean) group.liveBoolean = liveBoolean;
|
|
1816
|
-
const blend = blendFromAttribute(g.attrs["data-vector-blend"], m);
|
|
1817
|
-
if (blend) group.blend = blend;
|
|
1818
|
-
return group;
|
|
1819
|
-
};
|
|
1820
|
-
const visit = (el, style, matrix) => {
|
|
1821
|
-
const out = [];
|
|
1822
|
-
for (const child of el.children) {
|
|
1823
|
-
if (SKIP.has(child.name)) continue;
|
|
1824
|
-
const nodes2 = visitOne(child, style, matrix);
|
|
1825
|
-
out.push(...nodes2.length ? masked(child, nodes2, multiplyMatrices(parseSvgTransform(child.attrs.transform), matrix)) : nodes2);
|
|
1826
|
-
}
|
|
1827
|
-
return options.mergeStackedShapes ? mergeStacked(out) : out;
|
|
1828
|
-
};
|
|
1829
|
-
let rootStyle = { fill: "#000", stroke: "none", strokeWidth: "" };
|
|
1830
|
-
const bboxMatrix = (nodes2) => {
|
|
1831
|
-
const b = unionBounds(nodes2.map((n) => vectorNodeBounds(n)));
|
|
1832
|
-
return b && b.width > 0 && b.height > 0 ? [b.width, 0, 0, b.height, b.x, b.y] : null;
|
|
1833
|
-
};
|
|
1834
|
-
const maskNode = (el, m, nodes2, units) => {
|
|
1835
|
-
const id = el.attrs.id;
|
|
1836
|
-
if (!id || resolving.has(id)) return null;
|
|
1837
|
-
let space = m;
|
|
1838
|
-
if (units === "objectBoundingBox") {
|
|
1839
|
-
const box = bboxMatrix(nodes2);
|
|
1840
|
-
if (!box) return null;
|
|
1841
|
-
space = box;
|
|
1842
|
-
}
|
|
1843
|
-
resolving.add(id);
|
|
1844
|
-
const isClip = el.name === "clipPath";
|
|
1845
|
-
const content = isClip ? clipRulesAsFillRules(el) : el;
|
|
1846
|
-
const inner = visit(content, readStyle(content, rootStyle), multiplyMatrices(parseSvgTransform(el.attrs.transform), space));
|
|
1847
|
-
resolving.delete(id);
|
|
1848
|
-
const strip = (n) => n.type === "path" ? withVectorStrokes(withVectorFills({ ...n, fill: null, stroke: null }, []), []) : { ...n, children: n.children.map(strip) };
|
|
1849
|
-
const list = isClip ? inner.map(strip) : inner;
|
|
1850
|
-
if (!list.length) return null;
|
|
1851
|
-
return list.length === 1 ? list[0] : { id: idFor({ name: "g", attrs: {}, children: [] }), type: "group", name: isClip ? "Clip" : "Mask", children: list };
|
|
1852
|
-
};
|
|
1853
|
-
const masked = (el, nodes2, m) => {
|
|
1854
|
-
let out = nodes2;
|
|
1855
|
-
const clipEl = rawMasks.get(urlId(attrOrStyle(el, "clip-path")) ?? "");
|
|
1856
|
-
if (clipEl?.name === "clipPath") {
|
|
1857
|
-
const source = maskNode(clipEl, m, out, clipEl.attrs.clipPathUnits);
|
|
1858
|
-
if (source) out = [{ id: idFor({ name: "g", attrs: {}, children: [] }), type: "group", name: "Clip Group", mask: { type: "clip" }, children: [source, ...out] }];
|
|
1859
|
-
}
|
|
1860
|
-
const maskEl = rawMasks.get(urlId(attrOrStyle(el, "mask")) ?? "");
|
|
1861
|
-
if (maskEl?.name === "mask") {
|
|
1862
|
-
const source = maskNode(maskEl, m, out, maskEl.attrs.maskContentUnits);
|
|
1863
|
-
const type = attrOrStyle(maskEl, "mask-type") === "alpha" ? "alpha" : "luminance";
|
|
1864
|
-
if (source) out = [{ id: idFor({ name: "g", attrs: {}, children: [] }), type: "group", name: "Opacity Mask", mask: { type }, children: [source, ...out] }];
|
|
1865
|
-
}
|
|
1866
|
-
return out;
|
|
1867
|
-
};
|
|
1868
|
-
const maskGroup = (g, style, m, meta) => {
|
|
1869
|
-
let raw = null;
|
|
1870
|
-
try {
|
|
1871
|
-
raw = JSON.parse(g.attrs["data-vector-mask"]);
|
|
1872
|
-
} catch {
|
|
1873
|
-
raw = null;
|
|
1874
|
-
}
|
|
1875
|
-
const mask = sanitizeVectorMask(raw);
|
|
1876
|
-
const part = (name) => g.children.find((c) => c.name === "g" && c.attrs["data-vector-part"] === name);
|
|
1877
|
-
const maskedEl = part("masked");
|
|
1878
|
-
if (!mask || !maskedEl) return null;
|
|
1879
|
-
const id = idFor(g);
|
|
1880
|
-
let sourceEl = part("mask-source");
|
|
1881
|
-
if (!sourceEl) {
|
|
1882
|
-
const el = rawMasks.get(urlId(maskedEl.attrs.mask) ?? "");
|
|
1883
|
-
const invert = el?.children.find((c) => c.name === "g" && c.attrs["data-vector-part"] === "mask-invert");
|
|
1884
|
-
sourceEl = invert ?? el;
|
|
1885
|
-
}
|
|
1886
|
-
const source = sourceEl ? visit(sourceEl, style, m)[0] : void 0;
|
|
1887
|
-
let contents = visit(maskedEl, style, m);
|
|
1888
|
-
let effects;
|
|
1889
|
-
const wrapper = contents.length === 1 && contents[0].type === "group" && contents[0].id === `${id}~content` ? contents[0] : null;
|
|
1890
|
-
if (wrapper?.type === "group") {
|
|
1891
|
-
effects = wrapper.effects ?? void 0;
|
|
1892
|
-
contents = wrapper.children;
|
|
1893
|
-
}
|
|
1894
|
-
if (!source) return contents.length ? { id, type: "group", ...meta, children: contents } : null;
|
|
1895
|
-
return { id, type: "group", ...meta, mask, ...effects?.length ? { effects } : {}, children: [source, ...contents] };
|
|
1896
|
-
};
|
|
1897
|
-
const visitOne = (child, style, matrix) => {
|
|
1898
|
-
const out = [];
|
|
1899
|
-
const m = multiplyMatrices(parseSvgTransform(child.attrs.transform), matrix);
|
|
1900
|
-
const s = readStyle(child, style);
|
|
1901
|
-
const opacity = child.attrs.opacity !== void 0 ? parseFloat(child.attrs.opacity) : void 0;
|
|
1902
|
-
const hidden = child.attrs.display === "none" || child.attrs.visibility === "hidden";
|
|
1903
|
-
const blendMode = vectorBlendModeFromCss(s.mixBlendMode);
|
|
1904
|
-
const filters = filtersOf(s.filter);
|
|
1905
|
-
const meta = {
|
|
1906
|
-
...child.attrs["inkscape:label"] || child.attrs["data-name"] ? { name: child.attrs["inkscape:label"] ?? child.attrs["data-name"] } : {},
|
|
1907
|
-
...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
|
|
1908
|
-
...hidden ? { visible: false } : {},
|
|
1909
|
-
...blendMode && blendMode !== "normal" ? { blendMode } : {},
|
|
1910
|
-
...filters.length ? { filters } : {}
|
|
1911
|
-
};
|
|
1912
|
-
if (child.attrs["data-vector-blend-step"] !== void 0) return out;
|
|
1913
|
-
const operation = child.attrs["data-vector-boolean-operation"];
|
|
1914
|
-
if (BOOLEAN_OPERATIONS.includes(operation)) meta.booleanOperation = operation;
|
|
1915
|
-
if (child.name === "g" && child.attrs["data-vector-mask"] !== void 0) {
|
|
1916
|
-
const node2 = maskGroup(child, s, m, meta);
|
|
1917
|
-
if (node2) out.push(node2);
|
|
1918
|
-
return out;
|
|
1919
|
-
}
|
|
1920
|
-
if (child.name === "g" && child.attrs["data-vector-live"] !== void 0) {
|
|
1921
|
-
const node2 = liveNode(child, s, m, meta);
|
|
1922
|
-
if (node2) out.push(node2);
|
|
1923
|
-
return out;
|
|
1924
|
-
}
|
|
1925
|
-
if (child.name === "g" && child.attrs["data-vector-path"] !== void 0 && child.attrs["data-vector-fills"] !== void 0) {
|
|
1926
|
-
const node2 = stackedPath(child, s, m, meta);
|
|
1927
|
-
if (node2) out.push(node2);
|
|
1928
|
-
return out;
|
|
1929
|
-
}
|
|
1930
|
-
if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
|
|
1931
|
-
const node2 = mergedPath(child, s, m, meta);
|
|
1932
|
-
if (node2) out.push(node2);
|
|
1933
|
-
return out;
|
|
1934
|
-
}
|
|
1935
|
-
if (child.name === "g" || child.name === "svg" || child.name === "a" || child.name === "switch") {
|
|
1936
|
-
const children = visit(child, s, m);
|
|
1937
|
-
if (!children.length) return out;
|
|
1938
|
-
const group = { id: idFor(child), type: "group", ...meta, children };
|
|
1939
|
-
const blend = child.name === "g" ? blendFromAttribute(child.attrs["data-vector-blend"], m) : null;
|
|
1940
|
-
if (blend) group.blend = blend;
|
|
1941
|
-
out.push(group);
|
|
1942
|
-
return out;
|
|
1943
|
-
}
|
|
1944
|
-
const subpaths = shapeOf(child);
|
|
1945
|
-
if (!subpaths || !subpaths.length) return out;
|
|
1946
|
-
const node = placed(
|
|
1947
|
-
withOpacities(
|
|
1948
|
-
{
|
|
1949
|
-
id: idFor(child),
|
|
1950
|
-
type: "path",
|
|
1951
|
-
...meta,
|
|
1952
|
-
subpaths,
|
|
1953
|
-
fill: child.name === "line" ? null : paint(s.fill),
|
|
1954
|
-
stroke: paint(s.stroke),
|
|
1955
|
-
...strokeStyle(s)
|
|
1956
|
-
},
|
|
1957
|
-
s
|
|
1958
|
-
),
|
|
1959
|
-
m
|
|
1960
|
-
);
|
|
1961
|
-
out.push(node);
|
|
1962
|
-
return out;
|
|
1963
|
-
};
|
|
1964
|
-
rootStyle = readStyle(root, rootStyle);
|
|
1965
|
-
const nodes = visit(root, rootStyle, rootMatrix);
|
|
1966
|
-
return { width, height, nodes };
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
export {
|
|
1970
|
-
subpathToMorphContour,
|
|
1971
|
-
morphContourToSubpath,
|
|
1972
|
-
refineMorphContour,
|
|
1973
|
-
reverseMorphContour,
|
|
1974
|
-
turnMorphContour,
|
|
1975
|
-
correspondMorphContours,
|
|
1976
|
-
morphPathsCross,
|
|
1977
|
-
interpolateMorphContours,
|
|
1978
|
-
interpolateRigidMatrix,
|
|
1979
|
-
interpolateVectorColor,
|
|
1980
|
-
resampleGradientStops,
|
|
1981
|
-
interpolateVectorPaint,
|
|
1982
|
-
interpolateWidthProfiles,
|
|
1983
|
-
interpolateVectorStyle,
|
|
1984
|
-
createVectorMorph,
|
|
1985
|
-
interpolateVectorPaths,
|
|
1986
|
-
evaluateVectorPathKeyframes,
|
|
1987
|
-
vectorStrokeGradientDefs,
|
|
1988
|
-
vectorCornersAttribute,
|
|
1989
|
-
applyVectorCornersAttribute,
|
|
1990
|
-
vectorPathLocalBounds,
|
|
1991
|
-
vectorPaintToSvg,
|
|
1992
|
-
vectorStrokeSvgParts,
|
|
1993
|
-
vectorNodeLocalPaintBounds,
|
|
1994
|
-
vectorNodeFilterSvg,
|
|
1995
|
-
widthProfileToAttribute,
|
|
1996
|
-
widthProfileFromAttribute,
|
|
1997
|
-
vectorDocumentToSvg,
|
|
1998
|
-
parseSvgTransform,
|
|
1999
|
-
parseVectorSvg
|
|
2000
|
-
};
|
|
2001
|
-
//# sourceMappingURL=chunk-KB2YBCVS.js.map
|