@pixerate/editor 0.113.0 → 0.114.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 +5 -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-YGIMK4NX.cjs → chunk-5OVJHMZQ.cjs} +757 -825
- package/dist/chunk-5OVJHMZQ.cjs.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-CZOXTEMN.js → chunk-EUVSQ25B.js} +336 -404
- package/dist/chunk-EUVSQ25B.js.map +1 -0
- package/dist/chunk-G5NMLUVF.cjs +2574 -0
- package/dist/chunk-G5NMLUVF.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-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-DIkoK8-r.d.ts → export-_1dUKwpF.d.ts} +217 -191
- package/dist/{export-CEXEzqBf.d.cts → export-b6NjCZ5-.d.cts} +217 -191
- package/dist/image-editor/index.cjs +1779 -1280
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +77 -93
- package/dist/image-editor/index.d.ts +77 -93
- package/dist/image-editor/index.js +740 -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 +146 -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 +6 -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 +6 -1
- package/skills/pixerate-image-editor/SKILL.md +12 -36
- package/skills/pixerate-image-editor/references/color-management.md +37 -0
- package/skills/pixerate-image-editor/references/photoshop-files.md +39 -0
- package/skills/pixerate-vector/SKILL.md +54 -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
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IDENTITY_MATRIX,
|
|
3
|
+
expandVectorAppearance,
|
|
4
|
+
expandVectorCorners,
|
|
5
|
+
expandVectorGradientStops,
|
|
6
|
+
explicitVectorGradient,
|
|
7
|
+
hasExplicitGradientGeometry,
|
|
8
|
+
hasPaint,
|
|
9
|
+
inferAnchorType,
|
|
10
|
+
invertMatrix,
|
|
11
|
+
isIdentityMatrix,
|
|
12
|
+
isVectorGradient,
|
|
13
|
+
multiplyMatrices,
|
|
14
|
+
nodeMatrix,
|
|
15
|
+
reverseWidthProfile,
|
|
16
|
+
sampleVectorGradient,
|
|
17
|
+
sortedVectorGradientStops,
|
|
18
|
+
unionBounds,
|
|
19
|
+
vectorGradientPoints,
|
|
20
|
+
widthProfileAt
|
|
21
|
+
} from "./chunk-VXR7XWMF.js";
|
|
22
|
+
import {
|
|
23
|
+
segmentLength,
|
|
24
|
+
segmentParamAtLength,
|
|
25
|
+
splitSegment
|
|
26
|
+
} from "./chunk-YCSFS3GK.js";
|
|
27
|
+
import {
|
|
28
|
+
evaluateEasing
|
|
29
|
+
} from "./chunk-AGR4P4UZ.js";
|
|
30
|
+
import {
|
|
31
|
+
mixColorsOklab
|
|
32
|
+
} from "./chunk-ZRJMJIQU.js";
|
|
33
|
+
|
|
34
|
+
// src/vector/blend.ts
|
|
35
|
+
var lerpN = (a, b, t) => a + (b - a) * t;
|
|
36
|
+
var lerpP = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
37
|
+
var toP = (v) => [v.x, v.y];
|
|
38
|
+
var mapP = (m, p) => [m[0] * p[0] + m[2] * p[1] + m[4], m[1] * p[0] + m[3] * p[1] + m[5]];
|
|
39
|
+
var curvesOf = (c) => Math.max(0, (c.pts.length - 1) / 3) | 0;
|
|
40
|
+
function subpathToMorphContour(sp, m = IDENTITY_MATRIX) {
|
|
41
|
+
const n = sp.anchors.length;
|
|
42
|
+
if (!n) return null;
|
|
43
|
+
const first = mapP(m, toP(sp.anchors[0]));
|
|
44
|
+
const pts = [first];
|
|
45
|
+
const count = n < 2 ? 0 : sp.closed ? n : n - 1;
|
|
46
|
+
for (let i = 0; i < count; i++) {
|
|
47
|
+
const a = sp.anchors[i];
|
|
48
|
+
const b = sp.anchors[(i + 1) % n];
|
|
49
|
+
const pa = toP(a);
|
|
50
|
+
const pb = toP(b);
|
|
51
|
+
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);
|
|
52
|
+
const c1 = straight ? lerpP(pa, pb, 1 / 3) : toP(a.out ?? a);
|
|
53
|
+
const c2 = straight ? lerpP(pa, pb, 2 / 3) : toP(b.in ?? b);
|
|
54
|
+
pts.push(mapP(m, c1), mapP(m, c2), i === count - 1 && sp.closed ? first : mapP(m, pb));
|
|
55
|
+
}
|
|
56
|
+
return { closed: sp.closed && count > 0, pts };
|
|
57
|
+
}
|
|
58
|
+
function isStraight(p0, p1, p2, p3) {
|
|
59
|
+
const dx = p3[0] - p0[0];
|
|
60
|
+
const dy = p3[1] - p0[1];
|
|
61
|
+
const len = Math.hypot(dx, dy);
|
|
62
|
+
const tol = Math.max(1e-7, len * 1e-7);
|
|
63
|
+
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;
|
|
64
|
+
const off = (p) => Math.abs((p[0] - p0[0]) * dy - (p[1] - p0[1]) * dx) / len;
|
|
65
|
+
const along = (p) => ((p[0] - p0[0]) * dx + (p[1] - p0[1]) * dy) / (len * len);
|
|
66
|
+
const u1 = along(p1);
|
|
67
|
+
const u2 = along(p2);
|
|
68
|
+
return off(p1) < tol && off(p2) < tol && u1 >= -1e-9 && u1 <= 1 + 1e-9 && u2 >= -1e-9 && u2 <= 1 + 1e-9;
|
|
69
|
+
}
|
|
70
|
+
function morphContourToSubpath(c, m = IDENTITY_MATRIX, closed = c.closed) {
|
|
71
|
+
const pts = isIdentityMatrix(m) ? c.pts : c.pts.map((p) => mapP(m, p));
|
|
72
|
+
const curves = curvesOf(c);
|
|
73
|
+
const dropLast = closed && c.closed && curves > 0;
|
|
74
|
+
const count = dropLast ? curves : curves + 1;
|
|
75
|
+
const anchors = [];
|
|
76
|
+
for (let k = 0; k < count; k++) anchors.push({ x: pts[3 * k][0], y: pts[3 * k][1] });
|
|
77
|
+
for (let k = 0; k < curves; k++) {
|
|
78
|
+
const p0 = pts[3 * k];
|
|
79
|
+
const p1 = pts[3 * k + 1];
|
|
80
|
+
const p2 = pts[3 * k + 2];
|
|
81
|
+
const p3 = pts[3 * k + 3];
|
|
82
|
+
if (isStraight(p0, p1, p2, p3)) continue;
|
|
83
|
+
anchors[k].out = { x: p1[0], y: p1[1] };
|
|
84
|
+
anchors[(k + 1) % count].in = { x: p2[0], y: p2[1] };
|
|
85
|
+
}
|
|
86
|
+
for (const a of anchors) {
|
|
87
|
+
const type = inferAnchorType(a);
|
|
88
|
+
if (type !== "corner") a.type = type;
|
|
89
|
+
}
|
|
90
|
+
const isClosed = closed && curves > 0;
|
|
91
|
+
return { anchors: dropCollinear(anchors, isClosed), closed: isClosed };
|
|
92
|
+
}
|
|
93
|
+
function dropCollinear(anchors, closed) {
|
|
94
|
+
const out = anchors.slice();
|
|
95
|
+
const min = closed ? 3 : 2;
|
|
96
|
+
let i = closed ? 0 : 1;
|
|
97
|
+
while (out.length > min && i < out.length - (closed ? 0 : 1)) {
|
|
98
|
+
const n = out.length;
|
|
99
|
+
const a = out[(i - 1 + n) % n];
|
|
100
|
+
const b = out[i];
|
|
101
|
+
const c = out[(i + 1) % n];
|
|
102
|
+
const plain = !b.in && !b.out && !a.out && !c.in;
|
|
103
|
+
const ux = b.x - a.x;
|
|
104
|
+
const uy = b.y - a.y;
|
|
105
|
+
const vx = c.x - b.x;
|
|
106
|
+
const vy = c.y - b.y;
|
|
107
|
+
const len = Math.hypot(ux, uy) + Math.hypot(vx, vy);
|
|
108
|
+
if (plain && Math.abs(ux * vy - uy * vx) <= 1e-9 * Math.max(1, len * len) && ux * vx + uy * vy >= 0) out.splice(i, 1);
|
|
109
|
+
else i++;
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
var segOf = (c, k) => c.pts.slice(3 * k, 3 * k + 4);
|
|
114
|
+
function refineMorphContour(c, segments) {
|
|
115
|
+
const have = curvesOf(c);
|
|
116
|
+
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] };
|
|
117
|
+
const identity = Array.from({ length: c.closed ? have : have + 1 }, (_, i) => i);
|
|
118
|
+
if (have >= segments) return { contour: c, anchors: identity };
|
|
119
|
+
const segs = Array.from({ length: have }, (_, k) => segOf(c, k));
|
|
120
|
+
const lens = segs.map((s) => segmentLength(s));
|
|
121
|
+
const sum = lens.reduce((a, b) => a + b, 0);
|
|
122
|
+
const extra = segments - have;
|
|
123
|
+
const want = lens.map((l) => sum > 1e-12 ? l / sum * extra : extra / have);
|
|
124
|
+
const parts = want.map((w) => 1 + Math.floor(w));
|
|
125
|
+
let left = segments - parts.reduce((a, b) => a + b, 0);
|
|
126
|
+
const frac = want.map((w) => w - Math.floor(w));
|
|
127
|
+
const order = parts.map((_, i) => i).sort((a, b) => Math.abs(frac[b] - frac[a]) > 1e-9 ? frac[b] - frac[a] : a - b);
|
|
128
|
+
for (let i = 0; left > 0; i = (i + 1) % order.length, left--) parts[order[i]]++;
|
|
129
|
+
const pts = [c.pts[0]];
|
|
130
|
+
const anchors = [0];
|
|
131
|
+
let at = 0;
|
|
132
|
+
segs.forEach((seg, k) => {
|
|
133
|
+
const pieces = parts[k];
|
|
134
|
+
let rest = seg.slice();
|
|
135
|
+
let t0 = 0;
|
|
136
|
+
for (let j = 1; j < pieces; j++) {
|
|
137
|
+
const t = segmentParamAtLength(seg, lens[k] * j / pieces, lens[k]);
|
|
138
|
+
const u = Math.min(1, Math.max(0, (t - t0) / (1 - t0 || 1)));
|
|
139
|
+
const [l, r] = splitSegment(rest, u);
|
|
140
|
+
pts.push(l[1], l[2], l[3]);
|
|
141
|
+
rest = r;
|
|
142
|
+
t0 = t;
|
|
143
|
+
}
|
|
144
|
+
pts.push(rest[1], rest[2], k === have - 1 && c.closed ? c.pts[0] : rest[3]);
|
|
145
|
+
at += pieces;
|
|
146
|
+
if (k < have - 1 || !c.closed) anchors.push(at);
|
|
147
|
+
});
|
|
148
|
+
return { contour: { closed: c.closed, pts }, anchors };
|
|
149
|
+
}
|
|
150
|
+
function reverseMorphContour(c) {
|
|
151
|
+
return { closed: c.closed, pts: c.pts.slice().reverse() };
|
|
152
|
+
}
|
|
153
|
+
function turnMorphContour(c, s) {
|
|
154
|
+
const n = curvesOf(c);
|
|
155
|
+
if (!c.closed || n === 0) return c;
|
|
156
|
+
const k = (s % n + n) % n;
|
|
157
|
+
if (k === 0) return c;
|
|
158
|
+
const body = c.pts.slice(1);
|
|
159
|
+
return { closed: true, pts: [c.pts[3 * k], ...body.slice(3 * k), ...body.slice(0, 3 * k)] };
|
|
160
|
+
}
|
|
161
|
+
function anchorPoints(c) {
|
|
162
|
+
const n = curvesOf(c);
|
|
163
|
+
const count = c.closed ? n : n + 1;
|
|
164
|
+
return Array.from({ length: Math.max(1, count) }, (_, k) => c.pts[3 * k]);
|
|
165
|
+
}
|
|
166
|
+
function polygonArea(pts) {
|
|
167
|
+
let s = 0;
|
|
168
|
+
for (let i = 0; i < pts.length; i++) {
|
|
169
|
+
const a = pts[i];
|
|
170
|
+
const b = pts[(i + 1) % pts.length];
|
|
171
|
+
s += a[0] * b[1] - b[0] * a[1];
|
|
172
|
+
}
|
|
173
|
+
return s / 2;
|
|
174
|
+
}
|
|
175
|
+
function frame(v) {
|
|
176
|
+
const n = Math.max(1, v.length);
|
|
177
|
+
let cx = 0;
|
|
178
|
+
let cy = 0;
|
|
179
|
+
for (const p of v) {
|
|
180
|
+
cx += p[0];
|
|
181
|
+
cy += p[1];
|
|
182
|
+
}
|
|
183
|
+
cx /= n;
|
|
184
|
+
cy /= n;
|
|
185
|
+
let s = 0;
|
|
186
|
+
for (const p of v) s += (p[0] - cx) ** 2 + (p[1] - cy) ** 2;
|
|
187
|
+
return { c: [cx, cy], r: Math.max(1e-9, Math.sqrt(s / n)) };
|
|
188
|
+
}
|
|
189
|
+
var RBINS = 5;
|
|
190
|
+
var ABINS = 12;
|
|
191
|
+
var TURN_WEIGHT = 0.1;
|
|
192
|
+
function shapeContexts(v, closed) {
|
|
193
|
+
const { r } = frame(v);
|
|
194
|
+
const n = v.length;
|
|
195
|
+
return v.map((p, i) => {
|
|
196
|
+
const prev = v[closed ? (i - 1 + n) % n : Math.max(0, i - 1)];
|
|
197
|
+
const next = v[closed ? (i + 1) % n : Math.min(n - 1, i + 1)];
|
|
198
|
+
const dir = Math.atan2(next[1] - prev[1], next[0] - prev[0]);
|
|
199
|
+
const h = new Float64Array(RBINS * ABINS);
|
|
200
|
+
let total = 0;
|
|
201
|
+
for (let j = 0; j < n; j++) {
|
|
202
|
+
if (i === j) continue;
|
|
203
|
+
const dx = v[j][0] - p[0];
|
|
204
|
+
const dy = v[j][1] - p[1];
|
|
205
|
+
const d = Math.hypot(dx, dy) / r;
|
|
206
|
+
const lr = (Math.log2(Math.max(d, 1e-6)) + 3) / 4 * RBINS;
|
|
207
|
+
if (!(lr >= 0 && lr < RBINS)) continue;
|
|
208
|
+
let ang = Math.atan2(dy, dx) - dir;
|
|
209
|
+
ang = (ang % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI);
|
|
210
|
+
h[Math.floor(lr) * ABINS + Math.min(ABINS - 1, Math.floor(ang / (2 * Math.PI) * ABINS))] += 1;
|
|
211
|
+
total++;
|
|
212
|
+
}
|
|
213
|
+
if (total > 0) for (let k = 0; k < h.length; k++) h[k] /= total;
|
|
214
|
+
return h;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function chi2(a, b) {
|
|
218
|
+
let s = 0;
|
|
219
|
+
for (let k = 0; k < a.length; k++) {
|
|
220
|
+
const t = a[k] + b[k];
|
|
221
|
+
if (t > 1e-12) s += (a[k] - b[k]) * (a[k] - b[k]) / t;
|
|
222
|
+
}
|
|
223
|
+
return s / 2;
|
|
224
|
+
}
|
|
225
|
+
function bestStart(fixed, free, useShapeContext) {
|
|
226
|
+
const fa = anchorPoints(fixed);
|
|
227
|
+
const ga = anchorPoints(free);
|
|
228
|
+
const n = ga.length;
|
|
229
|
+
const ff = frame(fa);
|
|
230
|
+
const gf = frame(ga);
|
|
231
|
+
const fn = fa.map((p) => [(p[0] - ff.c[0]) / ff.r, (p[1] - ff.c[1]) / ff.r]);
|
|
232
|
+
const gn = ga.map((p) => [(p[0] - gf.c[0]) / gf.r, (p[1] - gf.c[1]) / gf.r]);
|
|
233
|
+
const fsc = useShapeContext ? shapeContexts(fa, fixed.closed) : null;
|
|
234
|
+
const gsc = useShapeContext ? shapeContexts(ga, free.closed) : null;
|
|
235
|
+
const m = Math.max(1, fn.length);
|
|
236
|
+
const cost = (s) => {
|
|
237
|
+
let dot = 0;
|
|
238
|
+
let cross = 0;
|
|
239
|
+
let sc = 0;
|
|
240
|
+
for (let i = 0; i < fn.length; i++) {
|
|
241
|
+
const j = free.closed ? (i + s) % n : i;
|
|
242
|
+
const p = fn[i];
|
|
243
|
+
const q = gn[j];
|
|
244
|
+
dot += p[0] * q[0] + p[1] * q[1];
|
|
245
|
+
cross += p[0] * q[1] - p[1] * q[0];
|
|
246
|
+
if (fsc && gsc) sc += chi2(fsc[i], gsc[j]);
|
|
247
|
+
}
|
|
248
|
+
const residual = Math.max(0, 1 - (dot * dot + cross * cross) / (m * m));
|
|
249
|
+
const turn = 1 - Math.cos(Math.atan2(cross, dot));
|
|
250
|
+
return residual + TURN_WEIGHT * turn + sc / m;
|
|
251
|
+
};
|
|
252
|
+
if (!free.closed) return { start: 0, cost: cost(0) };
|
|
253
|
+
const stride = n > 96 ? Math.ceil(n / 48) : 1;
|
|
254
|
+
let best = { start: 0, cost: Infinity };
|
|
255
|
+
for (let s = 0; s < n; s += stride) {
|
|
256
|
+
const c = cost(s);
|
|
257
|
+
if (c < best.cost - 1e-12) best = { start: s, cost: c };
|
|
258
|
+
}
|
|
259
|
+
if (stride > 1) {
|
|
260
|
+
const s0 = best.start;
|
|
261
|
+
for (let d = 1; d < stride; d++)
|
|
262
|
+
for (const s of [(s0 + d) % n, (s0 - d + n) % n]) {
|
|
263
|
+
const c = cost(s);
|
|
264
|
+
if (c < best.cost - 1e-12) best = { start: s, cost: c };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return best;
|
|
268
|
+
}
|
|
269
|
+
function placeIn(c, from, to) {
|
|
270
|
+
const b = unionBounds([boundsOfPoints(c.pts)]);
|
|
271
|
+
const p = b ? [b.x + b.width / 2, b.y + b.height / 2] : c.pts[0];
|
|
272
|
+
if (!from || !to) return p;
|
|
273
|
+
const fx = from.width > 0 ? (p[0] - from.x) / from.width : 0.5;
|
|
274
|
+
const fy = from.height > 0 ? (p[1] - from.y) / from.height : 0.5;
|
|
275
|
+
return [to.x + to.width * fx, to.y + to.height * fy];
|
|
276
|
+
}
|
|
277
|
+
function boundsOfPoints(pts) {
|
|
278
|
+
if (!pts.length) return null;
|
|
279
|
+
let x0 = Infinity;
|
|
280
|
+
let y0 = Infinity;
|
|
281
|
+
let x1 = -Infinity;
|
|
282
|
+
let y1 = -Infinity;
|
|
283
|
+
for (const p of pts) {
|
|
284
|
+
x0 = Math.min(x0, p[0]);
|
|
285
|
+
y0 = Math.min(y0, p[1]);
|
|
286
|
+
x1 = Math.max(x1, p[0]);
|
|
287
|
+
y1 = Math.max(y1, p[1]);
|
|
288
|
+
}
|
|
289
|
+
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
290
|
+
}
|
|
291
|
+
function correspondMorphContours(a, b, options = {}) {
|
|
292
|
+
const minSeg = Math.max(1, Math.round(options.minSegments ?? 16));
|
|
293
|
+
const maxSeg = Math.max(minSeg, Math.round(options.maxSegments ?? 400));
|
|
294
|
+
const spacing = options.spacing && options.spacing > 0 ? options.spacing : null;
|
|
295
|
+
const useSC = options.shapeContext !== false;
|
|
296
|
+
const match = options.matchStart !== false;
|
|
297
|
+
const ba = boundsOfPoints(a.flatMap((c) => c.pts));
|
|
298
|
+
const bb = boundsOfPoints(b.flatMap((c) => c.pts));
|
|
299
|
+
const contourLength = (c) => {
|
|
300
|
+
let l = 0;
|
|
301
|
+
for (let k = 0; k < curvesOf(c); k++) l += segmentLength(segOf(c, k));
|
|
302
|
+
return l;
|
|
303
|
+
};
|
|
304
|
+
const pairs = [];
|
|
305
|
+
const [sa, sb] = options.starts ?? [];
|
|
306
|
+
for (let k = 0; k < Math.max(a.length, b.length); k++) {
|
|
307
|
+
const ca = a[k];
|
|
308
|
+
const cb = b[k];
|
|
309
|
+
const explicitA = k === 0 && sa !== null && sa !== void 0 ? sa : null;
|
|
310
|
+
const explicitB = k === 0 && sb !== null && sb !== void 0 ? sb : null;
|
|
311
|
+
const counts = [ca ? curvesOf(ca) : 0, cb ? curvesOf(cb) : 0];
|
|
312
|
+
const longest = Math.max(ca ? contourLength(ca) : 0, cb ? contourLength(cb) : 0);
|
|
313
|
+
let n = Math.max(counts[0], counts[1], minSeg, spacing ? Math.ceil(longest / spacing) : 0);
|
|
314
|
+
n = Math.max(counts[0], counts[1], Math.min(n, maxSeg));
|
|
315
|
+
const point = (c, from, to) => {
|
|
316
|
+
const p = placeIn(c, from, to);
|
|
317
|
+
return { closed: c.closed, pts: Array.from({ length: 1 + 3 * n }, () => [p[0], p[1]]) };
|
|
318
|
+
};
|
|
319
|
+
if (ca && cb) {
|
|
320
|
+
const ra = refineMorphContour(ca, n);
|
|
321
|
+
const rb = refineMorphContour(cb, n);
|
|
322
|
+
pairs.push({
|
|
323
|
+
x: ra.contour,
|
|
324
|
+
y: rb.contour,
|
|
325
|
+
point: false,
|
|
326
|
+
startX: explicitA === null ? null : ra.anchors[explicitA] ?? null,
|
|
327
|
+
startY: explicitB === null ? null : explicitB === rb.anchors.length - 1 && !cb.closed ? -1 : rb.anchors[explicitB] ?? null
|
|
328
|
+
});
|
|
329
|
+
if (explicitA !== null && !ca.closed && explicitA === ra.anchors.length - 1 && explicitA > 0) pairs[pairs.length - 1].startX = -1;
|
|
330
|
+
} else if (ca) pairs.push({ x: refineMorphContour(ca, n).contour, y: point(ca, ba, bb), point: true, startX: null, startY: null });
|
|
331
|
+
else if (cb) pairs.push({ x: point(cb, bb, ba), y: refineMorphContour(cb, n).contour, point: true, startX: null, startY: null });
|
|
332
|
+
}
|
|
333
|
+
const startAt = (c, s) => {
|
|
334
|
+
if (s === null) return c;
|
|
335
|
+
if (s === -1) return reverseMorphContour(c);
|
|
336
|
+
return c.closed ? turnMorphContour(c, s) : c;
|
|
337
|
+
};
|
|
338
|
+
const explicit = pairs.length > 0 && (pairs[0].startX !== null || pairs[0].startY !== null);
|
|
339
|
+
const ref = pairs.find((p) => !p.point && p.x.closed && p.y.closed);
|
|
340
|
+
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;
|
|
341
|
+
let best = null;
|
|
342
|
+
for (const back of fixedBack === null ? [false, true] : [fixedBack]) {
|
|
343
|
+
const xs = [];
|
|
344
|
+
const ys = [];
|
|
345
|
+
let cost = 0;
|
|
346
|
+
pairs.forEach((p, k) => {
|
|
347
|
+
let x = k === 0 ? startAt(p.x, p.startX) : p.x;
|
|
348
|
+
let y = k === 0 ? startAt(p.y, p.startY) : p.y;
|
|
349
|
+
if (back) y = reverseMorphContour(y);
|
|
350
|
+
if (p.point) {
|
|
351
|
+
xs.push(x);
|
|
352
|
+
ys.push(y);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const pinned = k === 0 && explicit;
|
|
356
|
+
const turnX = !y.closed && x.closed;
|
|
357
|
+
if (match && !pinned && (y.closed || turnX)) {
|
|
358
|
+
const r = turnX ? bestStart(y, x, useSC) : bestStart(x, y, useSC);
|
|
359
|
+
if (turnX) x = turnMorphContour(x, r.start);
|
|
360
|
+
else y = turnMorphContour(y, r.start);
|
|
361
|
+
cost += r.cost;
|
|
362
|
+
} else cost += bestStart(x, { ...y, closed: false }, useSC).cost;
|
|
363
|
+
xs.push(x);
|
|
364
|
+
ys.push(y);
|
|
365
|
+
});
|
|
366
|
+
if (!best || cost < best.cost - 1e-9) best = { xs, ys, cost, back };
|
|
367
|
+
}
|
|
368
|
+
return best ? { a: best.xs, b: best.ys, reversed: best.back } : { a: [], b: [], reversed: false };
|
|
369
|
+
}
|
|
370
|
+
function morphPathsCross(a, b) {
|
|
371
|
+
let count = 0;
|
|
372
|
+
const d = (p, q, r) => (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]);
|
|
373
|
+
for (let k = 0; k < Math.min(a.length, b.length); k++) {
|
|
374
|
+
const pa = anchorPoints(a[k]);
|
|
375
|
+
const pb = anchorPoints(b[k]);
|
|
376
|
+
const n = Math.min(pa.length, pb.length);
|
|
377
|
+
for (let i = 0; i < n; i++)
|
|
378
|
+
for (let j = i + 1; j < n; j++) {
|
|
379
|
+
const d1 = d(pa[j], pb[j], pa[i]);
|
|
380
|
+
const d2 = d(pa[j], pb[j], pb[i]);
|
|
381
|
+
const d3 = d(pa[i], pb[i], pa[j]);
|
|
382
|
+
const d4 = d(pa[i], pb[i], pb[j]);
|
|
383
|
+
if (d1 * d2 < 0 && d3 * d4 < 0) count++;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return count;
|
|
387
|
+
}
|
|
388
|
+
function procrustes(a, b) {
|
|
389
|
+
const pa = a.flatMap(anchorPoints);
|
|
390
|
+
const pb = b.flatMap(anchorPoints);
|
|
391
|
+
const ca = frame(pa).c;
|
|
392
|
+
const cb = frame(pb).c;
|
|
393
|
+
let ss = 0;
|
|
394
|
+
let dot = 0;
|
|
395
|
+
let cross = 0;
|
|
396
|
+
for (let i = 0; i < Math.min(pa.length, pb.length); i++) {
|
|
397
|
+
const p = [pa[i][0] - ca[0], pa[i][1] - ca[1]];
|
|
398
|
+
const q = [pb[i][0] - cb[0], pb[i][1] - cb[1]];
|
|
399
|
+
ss += p[0] * p[0] + p[1] * p[1];
|
|
400
|
+
dot += p[0] * q[0] + p[1] * q[1];
|
|
401
|
+
cross += p[0] * q[1] - p[1] * q[0];
|
|
402
|
+
}
|
|
403
|
+
return { ca, cb, theta: Math.atan2(cross, dot), s: ss > 1e-12 ? Math.hypot(dot, cross) / ss : 1 };
|
|
404
|
+
}
|
|
405
|
+
function interpolateMorphContours(a, b, u, options = {}) {
|
|
406
|
+
const w = options.linear ? 0 : Math.min(1, Math.max(0, options.rigidity ?? 1));
|
|
407
|
+
const sim = w > 0 ? options.similarity ?? procrustes(a, b) : null;
|
|
408
|
+
const rot = (p, ang, k) => {
|
|
409
|
+
const c = Math.cos(ang);
|
|
410
|
+
const s = Math.sin(ang);
|
|
411
|
+
return [k * (c * p[0] - s * p[1]), k * (s * p[0] + c * p[1])];
|
|
412
|
+
};
|
|
413
|
+
const thetaU = sim ? sim.theta * u : 0;
|
|
414
|
+
const sU = sim ? Math.max(1e-9, sim.s) ** u : 1;
|
|
415
|
+
const cU = sim ? lerpP(sim.ca, sim.cb, u) : [0, 0];
|
|
416
|
+
return a.map((ca, k) => {
|
|
417
|
+
const cb = b[k];
|
|
418
|
+
const pts = ca.pts.map((p, i) => {
|
|
419
|
+
const q = cb.pts[i] ?? p;
|
|
420
|
+
const linear = lerpP(p, q, u);
|
|
421
|
+
if (!sim) return linear;
|
|
422
|
+
const d = [p[0] - sim.ca[0], p[1] - sim.ca[1]];
|
|
423
|
+
const full = rot(d, sim.theta, sim.s);
|
|
424
|
+
const resid = [q[0] - (full[0] + sim.cb[0]), q[1] - (full[1] + sim.cb[1])];
|
|
425
|
+
const part = rot(d, thetaU, sU);
|
|
426
|
+
const rigid = [part[0] + cU[0] + resid[0] * u, part[1] + cU[1] + resid[1] * u];
|
|
427
|
+
return lerpP(linear, rigid, w);
|
|
428
|
+
});
|
|
429
|
+
return { closed: ca.closed && cb.closed, pts };
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function interpolateRigidMatrix(a, b, t) {
|
|
433
|
+
const decompose = (m) => {
|
|
434
|
+
const sx2 = Math.hypot(m[0], m[1]);
|
|
435
|
+
const ang2 = sx2 > 1e-12 ? Math.atan2(m[1], m[0]) : 0;
|
|
436
|
+
const c2 = Math.cos(ang2);
|
|
437
|
+
const s2 = Math.sin(ang2);
|
|
438
|
+
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] };
|
|
439
|
+
};
|
|
440
|
+
const p = decompose(a);
|
|
441
|
+
const q = decompose(b);
|
|
442
|
+
let turn = q.ang - p.ang;
|
|
443
|
+
turn = ((turn + Math.PI) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI) - Math.PI;
|
|
444
|
+
const ang = p.ang + turn * t;
|
|
445
|
+
const sx = lerpN(p.sx, q.sx, t);
|
|
446
|
+
const sh = lerpN(p.sh, q.sh, t);
|
|
447
|
+
const sy = lerpN(p.sy, q.sy, t);
|
|
448
|
+
const c = Math.cos(ang);
|
|
449
|
+
const s = Math.sin(ang);
|
|
450
|
+
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)];
|
|
451
|
+
}
|
|
452
|
+
function interpolateVectorColor(a, b, t) {
|
|
453
|
+
if (t <= 0) return a;
|
|
454
|
+
if (t >= 1) return b;
|
|
455
|
+
return mixColorsOklab(a, b, t) ?? (t < 0.5 ? a : b);
|
|
456
|
+
}
|
|
457
|
+
function resampleGradientStops(g, offsets) {
|
|
458
|
+
return offsets.map((offset) => {
|
|
459
|
+
const s = sampleVectorGradient(g, offset);
|
|
460
|
+
return s.opacity < 1 ? { offset, color: s.color, opacity: Math.round(s.opacity * 1e4) / 1e4 } : { offset, color: s.color };
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
function lerpAngle(a, b, t) {
|
|
464
|
+
const d = ((b - a + 180) % 360 + 360) % 360 - 180;
|
|
465
|
+
return a + d * t;
|
|
466
|
+
}
|
|
467
|
+
var lerpPoint = (a, b, t) => ({ x: lerpN(a.x, b.x, t), y: lerpN(a.y, b.y, t) });
|
|
468
|
+
var sameColourModel = (a, b) => (a.interpolation ?? "srgb") === (b.interpolation ?? "srgb") && (a.smoothness ?? 0) === (b.smoothness ?? 0);
|
|
469
|
+
function lerpStops(a, b, t) {
|
|
470
|
+
let stopsA = sortedVectorGradientStops(a);
|
|
471
|
+
let stopsB = sortedVectorGradientStops(b);
|
|
472
|
+
const resampled = stopsA.length !== stopsB.length || !sameColourModel(a, b);
|
|
473
|
+
if (resampled) {
|
|
474
|
+
const offs = [...expandVectorGradientStops(a), ...expandVectorGradientStops(b), ...stopsA, ...stopsB].map((s) => s.offset).filter(Number.isFinite).sort((x, y) => x - y);
|
|
475
|
+
const common = offs.filter((o, i) => i === 0 || o - offs[i - 1] > 1e-4);
|
|
476
|
+
stopsA = resampleGradientStops(a, common);
|
|
477
|
+
stopsB = resampleGradientStops(b, common);
|
|
478
|
+
}
|
|
479
|
+
const stops = stopsA.map((x, i) => {
|
|
480
|
+
const y = stopsB[i];
|
|
481
|
+
const opacity = lerpN(x.opacity ?? 1, y.opacity ?? 1, t);
|
|
482
|
+
const stop = { offset: lerpN(x.offset, y.offset, t), color: interpolateVectorColor(x.color, y.color, t) };
|
|
483
|
+
if (opacity < 1) stop.opacity = opacity;
|
|
484
|
+
const mid = lerpN(x.midpoint ?? 0.5, y.midpoint ?? 0.5, t);
|
|
485
|
+
if (Math.abs(mid - 0.5) > 1e-6) stop.midpoint = mid;
|
|
486
|
+
return stop;
|
|
487
|
+
});
|
|
488
|
+
return { stops, resampled };
|
|
489
|
+
}
|
|
490
|
+
function lerpGradient(a, b, t) {
|
|
491
|
+
if (a.type !== b.type) return null;
|
|
492
|
+
const { stops, resampled } = lerpStops(a, b, t);
|
|
493
|
+
const near = t < 0.5 ? a : b;
|
|
494
|
+
const out = { type: a.type, stops };
|
|
495
|
+
if (!resampled) {
|
|
496
|
+
if (a.interpolation && a.interpolation !== "srgb") out.interpolation = a.interpolation;
|
|
497
|
+
if (a.smoothness) out.smoothness = a.smoothness;
|
|
498
|
+
}
|
|
499
|
+
if (near.spread && near.spread !== "pad") out.spread = near.spread;
|
|
500
|
+
if (near.strokeMode && near.strokeMode !== "within") out.strokeMode = near.strokeMode;
|
|
501
|
+
const ea = hasExplicitGradientGeometry(a);
|
|
502
|
+
const eb = hasExplicitGradientGeometry(b);
|
|
503
|
+
if (!ea && !eb) {
|
|
504
|
+
if (a.angle !== void 0 || b.angle !== void 0 || a.type === "linear" || a.type === "reflected") {
|
|
505
|
+
const fallback = a.type === "linear" || a.type === "reflected" ? 180 : 90;
|
|
506
|
+
out.angle = lerpAngle(a.angle ?? fallback, b.angle ?? fallback, t);
|
|
507
|
+
}
|
|
508
|
+
if (a.type !== "linear" && a.type !== "reflected") {
|
|
509
|
+
out.cx = lerpN(a.cx ?? 0.5, b.cx ?? 0.5, t);
|
|
510
|
+
out.cy = lerpN(a.cy ?? 0.5, b.cy ?? 0.5, t);
|
|
511
|
+
out.r = lerpN(a.r ?? 0.5, b.r ?? 0.5, t);
|
|
512
|
+
}
|
|
513
|
+
return out;
|
|
514
|
+
}
|
|
515
|
+
const pa = vectorGradientPoints(a);
|
|
516
|
+
const pb = vectorGradientPoints(b);
|
|
517
|
+
if (pa.units !== pb.units) {
|
|
518
|
+
const src = explicitVectorGradient(near);
|
|
519
|
+
return { ...src, stops: out.stops, ...out.interpolation ? { interpolation: out.interpolation } : {}, ...out.smoothness ? { smoothness: out.smoothness } : {} };
|
|
520
|
+
}
|
|
521
|
+
out.start = lerpPoint(pa.start, pb.start, t);
|
|
522
|
+
out.end = lerpPoint(pa.end, pb.end, t);
|
|
523
|
+
out.width = lerpPoint(pa.width, pb.width, t);
|
|
524
|
+
out.units = pa.units;
|
|
525
|
+
if (a.type === "radial") {
|
|
526
|
+
if (pa.focal || pb.focal) out.focal = lerpPoint(pa.focal ?? pa.start, pb.focal ?? pb.start, t);
|
|
527
|
+
const fr = lerpN(pa.focalRadius, pb.focalRadius, t);
|
|
528
|
+
if (fr > 0) out.focalRadius = fr;
|
|
529
|
+
}
|
|
530
|
+
return out;
|
|
531
|
+
}
|
|
532
|
+
function solidLike(color, like) {
|
|
533
|
+
return { ...like, stops: like.stops.map((s) => color === null ? { offset: s.offset, color: s.color, opacity: 0 } : { offset: s.offset, color }) };
|
|
534
|
+
}
|
|
535
|
+
function interpolateVectorPaint(a, b, t) {
|
|
536
|
+
if (t <= 0) return a;
|
|
537
|
+
if (t >= 1) return b;
|
|
538
|
+
const pa = hasPaint(a);
|
|
539
|
+
const pb = hasPaint(b);
|
|
540
|
+
if (!pa && !pb) return t < 0.5 ? a : b;
|
|
541
|
+
if (typeof a === "string" && pa && typeof b === "string" && pb) return interpolateVectorColor(a, b, t);
|
|
542
|
+
if (!pa && typeof b === "string") return interpolateVectorColor("transparent", b, t);
|
|
543
|
+
if (!pb && typeof a === "string") return interpolateVectorColor(a, "transparent", t);
|
|
544
|
+
const ga = pa && isVectorGradient(a) ? a : null;
|
|
545
|
+
const gb = pb && isVectorGradient(b) ? b : null;
|
|
546
|
+
if (ga && gb) return lerpGradient(ga, gb, t) ?? (t < 0.5 ? a : b);
|
|
547
|
+
if (gb) return lerpGradient(solidLike(pa ? a : null, gb), gb, t) ?? b;
|
|
548
|
+
if (ga) return lerpGradient(ga, solidLike(pb ? b : null, ga), t) ?? a;
|
|
549
|
+
return t < 0.5 ? a : b;
|
|
550
|
+
}
|
|
551
|
+
function interpolateWidthProfiles(a, b, t) {
|
|
552
|
+
if (!a?.length && !b?.length) return null;
|
|
553
|
+
const at = [...a ?? [], ...b ?? [], { t: 0 }, { t: 1 }].map((p) => p.t).sort((x, y) => x - y);
|
|
554
|
+
const ts = at.filter((v, i) => i === 0 || v - at[i - 1] > 1e-6);
|
|
555
|
+
return ts.map((pos) => {
|
|
556
|
+
const x = widthProfileAt(a, pos);
|
|
557
|
+
const y = widthProfileAt(b, pos);
|
|
558
|
+
return { t: pos, left: lerpN(x.left, y.left, t), right: lerpN(x.right, y.right, t) };
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
function lerpDashes(a, b, t) {
|
|
562
|
+
const dashed = (d) => !!d?.length && d.some((v) => v > 0);
|
|
563
|
+
if (!dashed(a) && !dashed(b)) return t < 0.5 ? a : b;
|
|
564
|
+
const even = (p) => p.length % 2 ? [...p, ...p] : p;
|
|
565
|
+
const solid = (p) => {
|
|
566
|
+
const e = even(p);
|
|
567
|
+
const out = [];
|
|
568
|
+
for (let i = 0; i < e.length; i += 2) out.push(e[i] + e[i + 1], 0);
|
|
569
|
+
return out;
|
|
570
|
+
};
|
|
571
|
+
const x = dashed(a) ? even(a) : solid(b);
|
|
572
|
+
const y = dashed(b) ? even(b) : solid(a);
|
|
573
|
+
if (x.length !== y.length) return t < 0.5 ? a : b;
|
|
574
|
+
return x.map((v, i) => lerpN(v, y[i], t));
|
|
575
|
+
}
|
|
576
|
+
var SWITCHED_KEYS = ["strokeLinecap", "strokeLinejoin", "fillRule", "strokeAlign", "startArrow", "endArrow", "arrowAlign"];
|
|
577
|
+
function interpolateVectorStyle(a, b, t) {
|
|
578
|
+
const near = t < 0.5 ? a : b;
|
|
579
|
+
const out = {};
|
|
580
|
+
const fill = interpolateVectorPaint(a.fill, b.fill, t);
|
|
581
|
+
if (fill !== void 0) out.fill = fill;
|
|
582
|
+
const stroke = interpolateVectorPaint(a.stroke, b.stroke, t);
|
|
583
|
+
if (stroke !== void 0) out.stroke = stroke;
|
|
584
|
+
if (a.strokeWidth !== void 0 || b.strokeWidth !== void 0) out.strokeWidth = lerpN(a.strokeWidth ?? 1, b.strokeWidth ?? 1, t);
|
|
585
|
+
for (const key of SWITCHED_KEYS) if (near[key] !== void 0 && near[key] !== null) out[key] = near[key];
|
|
586
|
+
for (const key of ["startArrowScale", "endArrowScale"])
|
|
587
|
+
if (a[key] !== void 0 || b[key] !== void 0) out[key] = lerpN(a[key] ?? 1, b[key] ?? 1, t);
|
|
588
|
+
const dash = lerpDashes(a.strokeDasharray, b.strokeDasharray, t);
|
|
589
|
+
if (dash?.length) out.strokeDasharray = dash;
|
|
590
|
+
const profile = interpolateWidthProfiles(a.strokeWidthProfile, b.strokeWidthProfile, t);
|
|
591
|
+
if (profile) out.strokeWidthProfile = profile;
|
|
592
|
+
return out;
|
|
593
|
+
}
|
|
594
|
+
function reversedStyle(s) {
|
|
595
|
+
const out = { ...s };
|
|
596
|
+
out.startArrow = s.endArrow;
|
|
597
|
+
out.endArrow = s.startArrow;
|
|
598
|
+
out.startArrowScale = s.endArrowScale;
|
|
599
|
+
out.endArrowScale = s.startArrowScale;
|
|
600
|
+
if (s.strokeWidthProfile?.length) out.strokeWidthProfile = reverseWidthProfile(s.strokeWidthProfile);
|
|
601
|
+
for (const k of ["startArrow", "endArrow", "startArrowScale", "endArrowScale"]) if (out[k] === void 0) delete out[k];
|
|
602
|
+
return out;
|
|
603
|
+
}
|
|
604
|
+
var STYLE_KEYS = [
|
|
605
|
+
"fill",
|
|
606
|
+
"stroke",
|
|
607
|
+
"strokeWidth",
|
|
608
|
+
"strokeLinecap",
|
|
609
|
+
"strokeLinejoin",
|
|
610
|
+
"strokeDasharray",
|
|
611
|
+
"fillRule",
|
|
612
|
+
"strokeAlign",
|
|
613
|
+
"startArrow",
|
|
614
|
+
"endArrow",
|
|
615
|
+
"arrowAlign",
|
|
616
|
+
"startArrowScale",
|
|
617
|
+
"endArrowScale",
|
|
618
|
+
"strokeWidthProfile"
|
|
619
|
+
];
|
|
620
|
+
function styleOf(n) {
|
|
621
|
+
const out = {};
|
|
622
|
+
for (const k of STYLE_KEYS) if (n[k] !== void 0) out[k] = n[k];
|
|
623
|
+
return out;
|
|
624
|
+
}
|
|
625
|
+
function contoursOf(node, m) {
|
|
626
|
+
return expandVectorCorners(node.subpaths).map((sp) => subpathToMorphContour(sp, m)).filter((c) => !!c);
|
|
627
|
+
}
|
|
628
|
+
function preparePathMorph(a, b, options, ma = nodeMatrix(a), mb = nodeMatrix(b)) {
|
|
629
|
+
const corr = correspondMorphContours(contoursOf(a, ma), contoursOf(b, mb), options);
|
|
630
|
+
const sim = procrustes(corr.a, corr.b);
|
|
631
|
+
const styleA = styleOf(a);
|
|
632
|
+
const styleB = corr.reversed ? reversedStyle(styleOf(b)) : styleOf(b);
|
|
633
|
+
const rigid = !isIdentityMatrix(ma) || !isIdentityMatrix(mb);
|
|
634
|
+
const id = options.id ?? a.id;
|
|
635
|
+
return (t) => {
|
|
636
|
+
const contours = interpolateMorphContours(corr.a, corr.b, t, { linear: options.linear, rigidity: options.rigidity, similarity: sim });
|
|
637
|
+
const m = rigid ? interpolateRigidMatrix(ma, mb, t) : IDENTITY_MATRIX;
|
|
638
|
+
const back = rigid ? invertMatrix(m) : IDENTITY_MATRIX;
|
|
639
|
+
const subpaths = contours.map((c, k) => {
|
|
640
|
+
const closed = t <= 0 ? corr.a[k].closed : t >= 1 ? corr.b[k].closed : corr.a[k].closed && corr.b[k].closed;
|
|
641
|
+
return morphContourToSubpath({ closed: corr.a[k].closed && corr.b[k].closed, pts: c.pts }, back, closed);
|
|
642
|
+
});
|
|
643
|
+
const node = { id, type: "path", subpaths, ...interpolateVectorStyle(styleA, styleB, t) };
|
|
644
|
+
const opacity = lerpN(a.opacity ?? 1, b.opacity ?? 1, t);
|
|
645
|
+
if (opacity < 1) node.opacity = opacity;
|
|
646
|
+
if (rigid && !isIdentityMatrix(m)) node.transform = m;
|
|
647
|
+
return node;
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
function flattenPaths(node, parent = IDENTITY_MATRIX) {
|
|
651
|
+
const m = multiplyMatrices(nodeMatrix(node), parent);
|
|
652
|
+
if (node.type === "path") return [{ path: node, m, opacity: 1 }];
|
|
653
|
+
if (node.type === "text" || node.type === "instance") return flattenPaths(expandVectorAppearance(node), parent);
|
|
654
|
+
const o = node.opacity ?? 1;
|
|
655
|
+
return node.children.filter((c) => c.visible !== false).flatMap((c) => flattenPaths(c, m)).map((e) => ({ ...e, opacity: e.opacity * o }));
|
|
656
|
+
}
|
|
657
|
+
var placedBounds = (path, m) => boundsOfPoints(contoursOf(path, m).flatMap((c) => c.pts));
|
|
658
|
+
function collapsedPath(path, m, from, to) {
|
|
659
|
+
const own = placedBounds(path, m);
|
|
660
|
+
const c = own ? [own.x + own.width / 2, own.y + own.height / 2] : [0, 0];
|
|
661
|
+
const fx = from && from.width > 0 ? (c[0] - from.x) / from.width : 0.5;
|
|
662
|
+
const fy = from && from.height > 0 ? (c[1] - from.y) / from.height : 0.5;
|
|
663
|
+
const p = to ? { x: to.x + to.width * fx, y: to.y + to.height * fy } : { x: c[0], y: c[1] };
|
|
664
|
+
const subpaths = path.subpaths.map((sp) => ({ closed: sp.closed, anchors: sp.anchors.map(() => ({ x: p.x, y: p.y })) }));
|
|
665
|
+
const { transform: _t, ...rest } = path;
|
|
666
|
+
return { ...rest, subpaths };
|
|
667
|
+
}
|
|
668
|
+
function createVectorMorph(a, b, options = {}) {
|
|
669
|
+
if (a.type === "path" && b.type === "path") {
|
|
670
|
+
const at = preparePathMorph(a, b, options);
|
|
671
|
+
return { at };
|
|
672
|
+
}
|
|
673
|
+
const pa = flattenPaths(a);
|
|
674
|
+
const pb = flattenPaths(b);
|
|
675
|
+
const ba = unionBounds(pa.map((e) => placedBounds(e.path, e.m)));
|
|
676
|
+
const bb = unionBounds(pb.map((e) => placedBounds(e.path, e.m)));
|
|
677
|
+
const id = options.id ?? a.id;
|
|
678
|
+
const parts = [];
|
|
679
|
+
const opacities = [];
|
|
680
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
681
|
+
const x = pa[i];
|
|
682
|
+
const y = pb[i];
|
|
683
|
+
const sub = { ...options, id: `${id}-${i}`, starts: void 0 };
|
|
684
|
+
if (x && y) {
|
|
685
|
+
parts.push(preparePathMorph(x.path, y.path, sub, x.m, y.m));
|
|
686
|
+
opacities.push([x.opacity, y.opacity]);
|
|
687
|
+
} else if (x) {
|
|
688
|
+
parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub, x.m, IDENTITY_MATRIX));
|
|
689
|
+
opacities.push([x.opacity, x.opacity]);
|
|
690
|
+
} else if (y) {
|
|
691
|
+
parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub, IDENTITY_MATRIX, y.m));
|
|
692
|
+
opacities.push([y.opacity, y.opacity]);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
at: (t) => {
|
|
697
|
+
const children = parts.map((p, i) => {
|
|
698
|
+
const n = p(t);
|
|
699
|
+
const o = lerpN(opacities[i][0], opacities[i][1], t) * (n.opacity ?? 1);
|
|
700
|
+
if (o < 1) n.opacity = o;
|
|
701
|
+
else delete n.opacity;
|
|
702
|
+
return n;
|
|
703
|
+
});
|
|
704
|
+
const group = { id, type: "group", children };
|
|
705
|
+
return group;
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function interpolateVectorPaths(a, b, t, options = {}) {
|
|
710
|
+
return createVectorMorph(a, b, options).at(t);
|
|
711
|
+
}
|
|
712
|
+
var keyframeMorphs = /* @__PURE__ */ new WeakMap();
|
|
713
|
+
function evaluateVectorPathKeyframes(keyframes, time, options = {}) {
|
|
714
|
+
const keys = keyframes.filter((k) => Number.isFinite(k.time)).sort((x, y) => x.time - y.time);
|
|
715
|
+
if (!keys.length) return null;
|
|
716
|
+
if (!(time > keys[0].time)) return keys[0].value;
|
|
717
|
+
const last = keys[keys.length - 1];
|
|
718
|
+
if (time >= last.time) return last.value;
|
|
719
|
+
let i = 0;
|
|
720
|
+
while (i + 1 < keys.length && keys[i + 1].time <= time) i++;
|
|
721
|
+
const a = keys[i];
|
|
722
|
+
const b = keys[i + 1];
|
|
723
|
+
if (time === a.time) return a.value;
|
|
724
|
+
if (a.easing === "hold") return a.value;
|
|
725
|
+
const span = b.time - a.time;
|
|
726
|
+
const u = Math.min(1, Math.max(0, evaluateEasing(a.easing, (time - a.time) / span, span)));
|
|
727
|
+
let inner = keyframeMorphs.get(a.value);
|
|
728
|
+
if (!inner) keyframeMorphs.set(a.value, inner = /* @__PURE__ */ new WeakMap());
|
|
729
|
+
let morph = inner.get(b.value);
|
|
730
|
+
if (!morph) inner.set(b.value, morph = createVectorMorph(a.value, b.value, options));
|
|
731
|
+
return morph.at(u);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
export {
|
|
735
|
+
subpathToMorphContour,
|
|
736
|
+
morphContourToSubpath,
|
|
737
|
+
refineMorphContour,
|
|
738
|
+
reverseMorphContour,
|
|
739
|
+
turnMorphContour,
|
|
740
|
+
correspondMorphContours,
|
|
741
|
+
morphPathsCross,
|
|
742
|
+
interpolateMorphContours,
|
|
743
|
+
interpolateRigidMatrix,
|
|
744
|
+
interpolateVectorColor,
|
|
745
|
+
resampleGradientStops,
|
|
746
|
+
interpolateVectorPaint,
|
|
747
|
+
interpolateWidthProfiles,
|
|
748
|
+
interpolateVectorStyle,
|
|
749
|
+
createVectorMorph,
|
|
750
|
+
interpolateVectorPaths,
|
|
751
|
+
evaluateVectorPathKeyframes
|
|
752
|
+
};
|
|
753
|
+
//# sourceMappingURL=chunk-MMVHBKMM.js.map
|