@pixerate/editor 0.42.0 → 0.43.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/canvas/index.d.cts +2 -2
- package/dist/canvas/index.d.ts +2 -2
- package/dist/{pathfinder-D7hXenzu.d.ts → pathfinder-D98S2lV5.d.ts} +1 -1
- package/dist/{pathfinder-BSW2ZTO6.d.cts → pathfinder-DKRcXiLS.d.cts} +1 -1
- package/dist/vector/index.cjs +2041 -424
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +606 -18
- package/dist/vector/index.d.ts +606 -18
- package/dist/vector/index.js +1820 -203
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
package/dist/vector/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
} from "../chunk-QVZZGT4X.js";
|
|
4
4
|
import {
|
|
5
5
|
PlanarMap,
|
|
6
|
+
angleToSvgLinearCoords,
|
|
6
7
|
bezierPathBounds,
|
|
7
8
|
bezierPathToSvg,
|
|
8
9
|
closeContour,
|
|
@@ -14,7 +15,6 @@ import {
|
|
|
14
15
|
fitCubics,
|
|
15
16
|
flattenChain,
|
|
16
17
|
formatNumber,
|
|
17
|
-
gradientToSvgDef,
|
|
18
18
|
hatchPath,
|
|
19
19
|
isStraightCubic,
|
|
20
20
|
normalizeBezierPath,
|
|
@@ -41,17 +41,25 @@ import {
|
|
|
41
41
|
windingNumber
|
|
42
42
|
} from "../chunk-3LHWTGHW.js";
|
|
43
43
|
import {
|
|
44
|
-
mixColorsOklab
|
|
44
|
+
mixColorsOklab,
|
|
45
|
+
oklabToRgb,
|
|
46
|
+
rgbToOklab
|
|
45
47
|
} from "../chunk-ZRJMJIQU.js";
|
|
46
48
|
import {
|
|
47
49
|
evaluateEasing
|
|
48
50
|
} from "../chunk-UB2OY7FD.js";
|
|
49
51
|
import {
|
|
50
|
-
hexToRgba
|
|
52
|
+
hexToRgba,
|
|
53
|
+
normalizeTokenColor,
|
|
54
|
+
rgbaToHex
|
|
51
55
|
} from "../chunk-TGD52FEZ.js";
|
|
52
56
|
|
|
53
57
|
// src/vector/types.ts
|
|
54
58
|
var VECTOR_CORNER_KINDS = ["round", "inverted", "chamfer"];
|
|
59
|
+
var VECTOR_GRADIENT_TYPES = ["linear", "radial", "conic", "reflected", "diamond"];
|
|
60
|
+
var VECTOR_GRADIENT_SPREADS = ["pad", "reflect", "repeat"];
|
|
61
|
+
var VECTOR_GRADIENT_INTERPOLATIONS = ["srgb", "linear", "oklab"];
|
|
62
|
+
var VECTOR_STROKE_GRADIENT_MODES = ["within", "along", "across"];
|
|
55
63
|
var VECTOR_ARROWHEADS = ["triangle", "arrow", "open", "circle", "square", "bar"];
|
|
56
64
|
var VECTOR_BLEND_SPACINGS = ["steps", "distance", "smoothColor"];
|
|
57
65
|
var VECTOR_TOOLS = [
|
|
@@ -70,20 +78,824 @@ var VECTOR_TOOLS = [
|
|
|
70
78
|
"width",
|
|
71
79
|
"shapeBuilder",
|
|
72
80
|
"paintBucket",
|
|
73
|
-
"blend"
|
|
81
|
+
"blend",
|
|
82
|
+
"gradient"
|
|
74
83
|
];
|
|
75
84
|
var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
|
|
76
85
|
var VECTOR_PATHFINDER_OPERATIONS = ["divide", "trim", "merge", "crop", "outline", "minusBack"];
|
|
77
86
|
var VECTOR_EFFECT_TYPES = ["trim", "roundCorners", "puckerBloat", "twist", "zigzag", "wiggle", "offset", "merge", "repeater"];
|
|
78
87
|
var VECTOR_MERGE_MODES = ["merge", "add", "subtract", "intersect", "exclude"];
|
|
79
88
|
|
|
80
|
-
// src/vector/
|
|
81
|
-
var
|
|
82
|
-
var
|
|
89
|
+
// src/vector/gradient.ts
|
|
90
|
+
var clamp = (v2, lo, hi) => Math.min(hi, Math.max(lo, v2));
|
|
91
|
+
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
92
|
+
var mul = (a, b) => [
|
|
93
|
+
b[0] * a[0] + b[2] * a[1],
|
|
94
|
+
b[1] * a[0] + b[3] * a[1],
|
|
95
|
+
b[0] * a[2] + b[2] * a[3],
|
|
96
|
+
b[1] * a[2] + b[3] * a[3],
|
|
97
|
+
b[0] * a[4] + b[2] * a[5] + b[4],
|
|
98
|
+
b[1] * a[4] + b[3] * a[5] + b[5]
|
|
99
|
+
];
|
|
100
|
+
var apply = (m, p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] });
|
|
101
|
+
var invert = (m) => {
|
|
102
|
+
const [a, b, c, d, e, f] = m;
|
|
103
|
+
const det = a * d - b * c;
|
|
104
|
+
if (!Number.isFinite(det) || Math.abs(det) < 1e-18) return null;
|
|
105
|
+
return [d / det, -b / det, -c / det, a / det, (c * f - d * e) / det, (b * e - a * f) / det];
|
|
106
|
+
};
|
|
107
|
+
var isIdentity = (m) => m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0;
|
|
83
108
|
var sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
84
109
|
var add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
85
|
-
var
|
|
110
|
+
var scale = (a, k) => ({ x: a.x * k, y: a.y * k });
|
|
86
111
|
var dot = (a, b) => a.x * b.x + a.y * b.y;
|
|
112
|
+
var perp = (v2) => ({ x: -v2.y, y: v2.x });
|
|
113
|
+
var finitePoint = (p) => !!p && typeof p === "object" && Number.isFinite(p.x) && Number.isFinite(p.y);
|
|
114
|
+
var fmt = (n, digits = 4) => {
|
|
115
|
+
const k = 10 ** digits;
|
|
116
|
+
const v2 = Math.round(n * k) / k;
|
|
117
|
+
return Object.is(v2, -0) ? "0" : String(v2);
|
|
118
|
+
};
|
|
119
|
+
var fmtMatrix = (m) => `matrix(${m.map((n) => fmt(n, 6)).join(" ")})`;
|
|
120
|
+
function isVectorGradient(paint) {
|
|
121
|
+
return !!paint && typeof paint === "object" && VECTOR_GRADIENT_TYPES.includes(paint.type) && Array.isArray(paint.stops);
|
|
122
|
+
}
|
|
123
|
+
function parseVectorColor(color) {
|
|
124
|
+
const hex = normalizeTokenColor(color);
|
|
125
|
+
if (hex === null) return null;
|
|
126
|
+
const c = hexToRgba(hex);
|
|
127
|
+
return [c.r, c.g, c.b, c.a];
|
|
128
|
+
}
|
|
129
|
+
var toLinear = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
130
|
+
var toGamma = (c) => {
|
|
131
|
+
const v2 = clamp(c, 0, 1);
|
|
132
|
+
return v2 <= 31308e-7 ? 12.92 * v2 : 1.055 * v2 ** (1 / 2.4) - 0.055;
|
|
133
|
+
};
|
|
134
|
+
function intoSpace(space, [r, g, b]) {
|
|
135
|
+
if (space === "linear") return [toLinear(r), toLinear(g), toLinear(b)];
|
|
136
|
+
if (space === "oklab") {
|
|
137
|
+
const c = rgbToOklab(r, g, b);
|
|
138
|
+
return [c.l, c.a, c.b];
|
|
139
|
+
}
|
|
140
|
+
return [r, g, b];
|
|
141
|
+
}
|
|
142
|
+
function outOfSpace(space, [x, y, z]) {
|
|
143
|
+
if (space === "linear") return [toGamma(x), toGamma(y), toGamma(z)];
|
|
144
|
+
if (space === "oklab") {
|
|
145
|
+
const c = oklabToRgb({ l: x, a: y, b: z, alpha: 1 });
|
|
146
|
+
return [c.r, c.g, c.b];
|
|
147
|
+
}
|
|
148
|
+
return [clamp(x, 0, 1), clamp(y, 0, 1), clamp(z, 0, 1)];
|
|
149
|
+
}
|
|
150
|
+
var MID_MIN = 0.01;
|
|
151
|
+
var MID_MAX = 0.99;
|
|
152
|
+
var VECTOR_GRADIENT_MIN_STOPS = 2;
|
|
153
|
+
function gradientMidpointRemap(u, m) {
|
|
154
|
+
const mid = clamp(m, MID_MIN, MID_MAX);
|
|
155
|
+
if (Math.abs(mid - 0.5) < 1e-6) return u;
|
|
156
|
+
return u <= mid ? 0.5 * u / mid : 0.5 + 0.5 * (u - mid) / (1 - mid);
|
|
157
|
+
}
|
|
158
|
+
var hasMidpoint = (s) => s.midpoint !== void 0 && Number.isFinite(s.midpoint) && Math.abs(s.midpoint - 0.5) > 1e-3;
|
|
159
|
+
function sortedVectorGradientStops(g) {
|
|
160
|
+
return (g.stops ?? []).filter((s) => s && Number.isFinite(s.offset) && typeof s.color === "string").map((s, i) => ({ s: { ...s, offset: clamp(s.offset, 0, 1) }, i })).sort((a, b) => a.s.offset - b.s.offset || a.i - b.i).map((e) => e.s);
|
|
161
|
+
}
|
|
162
|
+
function vectorGradientNeedsExpansion(g) {
|
|
163
|
+
return (g.interpolation ?? "srgb") !== "srgb" || (g.smoothness ?? 0) > 0 || (g.stops ?? []).some(hasMidpoint);
|
|
164
|
+
}
|
|
165
|
+
function vectorGradientRamp(g) {
|
|
166
|
+
const space = g.interpolation && VECTOR_GRADIENT_INTERPOLATIONS.includes(g.interpolation) ? g.interpolation : "srgb";
|
|
167
|
+
const smooth = clamp(Number.isFinite(g.smoothness) ? g.smoothness : 0, 0, 1);
|
|
168
|
+
const pts = [];
|
|
169
|
+
for (const s of sortedVectorGradientStops(g)) {
|
|
170
|
+
const c = parseVectorColor(s.color);
|
|
171
|
+
if (!c) return null;
|
|
172
|
+
pts.push({ t: s.offset, v: intoSpace(space, c), a: c[3] * clamp(s.opacity ?? 1, 0, 1), mid: s.midpoint ?? 0.5 });
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
sample(t) {
|
|
176
|
+
const n = pts.length;
|
|
177
|
+
if (!n) return [0, 0, 0, 0];
|
|
178
|
+
const first = pts[0];
|
|
179
|
+
const last = pts[n - 1];
|
|
180
|
+
const at2 = (p) => [...outOfSpace(space, p.v), p.a];
|
|
181
|
+
if (!(t > first.t) || n < 2) return at2(first);
|
|
182
|
+
if (t >= last.t) return at2(last);
|
|
183
|
+
let i = 0;
|
|
184
|
+
while (i + 2 < n && !(t >= pts[i].t && t <= pts[i + 1].t)) i++;
|
|
185
|
+
const p0 = pts[i];
|
|
186
|
+
const p1 = pts[i + 1];
|
|
187
|
+
const h = Math.max(p1.t - p0.t, 1e-6);
|
|
188
|
+
const u = gradientMidpointRemap((t - p0.t) / h, p0.mid);
|
|
189
|
+
const alpha = p0.a + (p1.a - p0.a) * u;
|
|
190
|
+
if (smooth <= 0) return [...outOfSpace(space, [0, 1, 2].map((c) => p0.v[c] + (p1.v[c] - p0.v[c]) * u)), alpha];
|
|
191
|
+
const tangent2 = (k) => {
|
|
192
|
+
const [a, b] = k === 0 ? [0, 1] : k === n - 1 ? [n - 2, n - 1] : [k - 1, k + 1];
|
|
193
|
+
const d = Math.max(pts[b].t - pts[a].t, 1e-6);
|
|
194
|
+
const half = k === 0 || k === n - 1 ? 0.5 : 1;
|
|
195
|
+
return [0, 1, 2].map((c) => (pts[b].v[c] - pts[a].v[c]) / d * half);
|
|
196
|
+
};
|
|
197
|
+
const m0 = tangent2(i);
|
|
198
|
+
const m1 = tangent2(i + 1);
|
|
199
|
+
const u2 = u * u;
|
|
200
|
+
const u3 = u2 * u;
|
|
201
|
+
const h00 = 2 * u3 - 3 * u2 + 1;
|
|
202
|
+
const h10 = u3 - 2 * u2 + u;
|
|
203
|
+
const h01 = -2 * u3 + 3 * u2;
|
|
204
|
+
const h11 = u3 - u2;
|
|
205
|
+
const v2 = [0, 1, 2].map((c) => {
|
|
206
|
+
const linear = p0.v[c] + (p1.v[c] - p0.v[c]) * u;
|
|
207
|
+
const spline = h00 * p0.v[c] + h10 * h * m0[c] + h01 * p1.v[c] + h11 * h * m1[c];
|
|
208
|
+
const value = linear + (spline - linear) * smooth;
|
|
209
|
+
return clamp(value, Math.min(p0.v[c], p1.v[c]), Math.max(p0.v[c], p1.v[c]));
|
|
210
|
+
});
|
|
211
|
+
return [...outOfSpace(space, v2), alpha];
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function sampleVectorGradient(g, t) {
|
|
216
|
+
const ramp = vectorGradientRamp(g);
|
|
217
|
+
if (ramp) {
|
|
218
|
+
const [r, gg, b, a] = ramp.sample(t);
|
|
219
|
+
return { color: rgbaToHex(r, gg, b), opacity: a };
|
|
220
|
+
}
|
|
221
|
+
const stops = sortedVectorGradientStops(g);
|
|
222
|
+
const s = [...stops].reverse().find((x) => x.offset <= t) ?? stops[0];
|
|
223
|
+
return s ? { color: s.color, opacity: s.opacity ?? 1 } : { color: "#000000", opacity: 0 };
|
|
224
|
+
}
|
|
225
|
+
var stopOf = (offset, c) => {
|
|
226
|
+
const color = rgbaToHex(c[0], c[1], c[2]);
|
|
227
|
+
const opacity = Math.round(c[3] * 1e4) / 1e4;
|
|
228
|
+
return opacity < 1 ? { offset, color, opacity } : { offset, color };
|
|
229
|
+
};
|
|
230
|
+
var maxDiff = (a, b) => Math.max(Math.abs(a[0] - b[0]), Math.abs(a[1] - b[1]), Math.abs(a[2] - b[2]), Math.abs(a[3] - b[3]));
|
|
231
|
+
var lerpRgba = (a, b, u) => [0, 1, 2, 3].map((c) => a[c] + (b[c] - a[c]) * u);
|
|
232
|
+
function expandVectorGradientStops(g, options = {}) {
|
|
233
|
+
const stops = sortedVectorGradientStops(g);
|
|
234
|
+
const plain3 = () => stops.map(({ offset, color, opacity }) => opacity !== void 0 && opacity !== 1 ? { offset, color, opacity } : { offset, color });
|
|
235
|
+
if (!vectorGradientNeedsExpansion(g)) return plain3();
|
|
236
|
+
const ramp = vectorGradientRamp(g);
|
|
237
|
+
if (!ramp || stops.length < 2) return plain3();
|
|
238
|
+
const tol = options.tolerance ?? 0.5 / 255;
|
|
239
|
+
const sRgbOnly = (g.interpolation ?? "srgb") === "srgb" && !((g.smoothness ?? 0) > 0);
|
|
240
|
+
const own = (s) => {
|
|
241
|
+
const c = parseVectorColor(s.color);
|
|
242
|
+
return [c[0], c[1], c[2], c[3] * clamp(s.opacity ?? 1, 0, 1)];
|
|
243
|
+
};
|
|
244
|
+
const out = [];
|
|
245
|
+
const push = (offset, c) => out.push(stopOf(offset, c));
|
|
246
|
+
for (let i = 0; i < stops.length; i++) {
|
|
247
|
+
const a = stops[i];
|
|
248
|
+
const b = stops[i + 1];
|
|
249
|
+
push(a.offset, own(a));
|
|
250
|
+
if (!b || b.offset - a.offset < 1e-9) continue;
|
|
251
|
+
const knots = [a.offset];
|
|
252
|
+
if (hasMidpoint(a)) knots.push(a.offset + (b.offset - a.offset) * clamp(a.midpoint, MID_MIN, MID_MAX));
|
|
253
|
+
knots.push(b.offset);
|
|
254
|
+
const inside = (t) => ramp.sample(clamp(t, a.offset + 1e-9, b.offset - 1e-9));
|
|
255
|
+
for (let k = 0; k + 1 < knots.length; k++) {
|
|
256
|
+
const t0 = knots[k];
|
|
257
|
+
const t1 = knots[k + 1];
|
|
258
|
+
if (k > 0) push(t0, inside(t0));
|
|
259
|
+
if (sRgbOnly) continue;
|
|
260
|
+
const refine = (x0, c0, x1, c1, depth) => {
|
|
261
|
+
const xm = (x0 + x1) / 2;
|
|
262
|
+
const cm = inside(xm);
|
|
263
|
+
const ok = depth >= 8 || [0.03, 0.1, 0.25, 0.5, 0.75, 0.9, 0.97].every((f) => maxDiff(inside(x0 + (x1 - x0) * f), lerpRgba(c0, c1, f)) <= tol);
|
|
264
|
+
if (ok) return;
|
|
265
|
+
refine(x0, c0, xm, cm, depth + 1);
|
|
266
|
+
push(xm, cm);
|
|
267
|
+
refine(xm, cm, x1, c1, depth + 1);
|
|
268
|
+
};
|
|
269
|
+
refine(t0, inside(t0), t1, inside(t1), 0);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
function samplePlainStops(stops, t) {
|
|
275
|
+
const parsed = stops.map((s) => {
|
|
276
|
+
const c = parseVectorColor(s.color) ?? [0, 0, 0, 1];
|
|
277
|
+
return { t: s.offset, c: [c[0], c[1], c[2], c[3] * (s.opacity ?? 1)] };
|
|
278
|
+
});
|
|
279
|
+
if (!parsed.length) return [0, 0, 0, 0];
|
|
280
|
+
if (t <= parsed[0].t) return parsed[0].c;
|
|
281
|
+
for (let i = 0; i + 1 < parsed.length; i++) {
|
|
282
|
+
const a = parsed[i];
|
|
283
|
+
const b = parsed[i + 1];
|
|
284
|
+
if (t <= b.t) return b.t - a.t < 1e-12 ? b.c : lerpRgba(a.c, b.c, (t - a.t) / (b.t - a.t));
|
|
285
|
+
}
|
|
286
|
+
return parsed[parsed.length - 1].c;
|
|
287
|
+
}
|
|
288
|
+
function reverseVectorGradient(g) {
|
|
289
|
+
const stops = sortedVectorGradientStops(g).reverse();
|
|
290
|
+
const next = stops.map((s, i) => {
|
|
291
|
+
const following = stops[i + 1];
|
|
292
|
+
const mid = following ? 1 - (following.midpoint ?? 0.5) : 0.5;
|
|
293
|
+
const { midpoint: _m, ...rest } = s;
|
|
294
|
+
return { ...rest, offset: 1 - s.offset, ...Math.abs(mid - 0.5) > 1e-6 ? { midpoint: mid } : {} };
|
|
295
|
+
});
|
|
296
|
+
return { ...g, stops: next };
|
|
297
|
+
}
|
|
298
|
+
function placeStop(stops, stop) {
|
|
299
|
+
let i = stops.findIndex((o) => o.offset > stop.offset);
|
|
300
|
+
if (i < 0) i = stops.length;
|
|
301
|
+
stops.splice(i, 0, stop);
|
|
302
|
+
return i;
|
|
303
|
+
}
|
|
304
|
+
function insertVectorGradientStop(g, offset) {
|
|
305
|
+
const at2 = clamp(offset, 0, 1);
|
|
306
|
+
const { color, opacity } = sampleVectorGradient(g, at2);
|
|
307
|
+
const stops = sortedVectorGradientStops(g);
|
|
308
|
+
const index = placeStop(stops, opacity < 1 ? { offset: at2, color, opacity } : { offset: at2, color });
|
|
309
|
+
return { stops, index };
|
|
310
|
+
}
|
|
311
|
+
function moveVectorGradientStop(stops, index, offset) {
|
|
312
|
+
const next = stops.map((s2) => ({ ...s2 }));
|
|
313
|
+
if (index < 0 || index >= next.length) return { stops: next, index };
|
|
314
|
+
const [s] = next.splice(index, 1);
|
|
315
|
+
return { stops: next, index: placeStop(next, { ...s, offset: clamp(offset, 0, 1) }) };
|
|
316
|
+
}
|
|
317
|
+
function removeVectorGradientStop(stops, index) {
|
|
318
|
+
if (stops.length <= VECTOR_GRADIENT_MIN_STOPS || index < 0 || index >= stops.length) return null;
|
|
319
|
+
return stops.filter((_, i) => i !== index);
|
|
320
|
+
}
|
|
321
|
+
function vectorGradientMidpointPosition(stops, index) {
|
|
322
|
+
const a = stops[index];
|
|
323
|
+
const b = stops[index + 1];
|
|
324
|
+
if (!a || !b) return null;
|
|
325
|
+
return a.offset + (b.offset - a.offset) * (a.midpoint ?? 0.5);
|
|
326
|
+
}
|
|
327
|
+
function vectorGradientMidpointFromPosition(stops, index, pos) {
|
|
328
|
+
const a = stops[index];
|
|
329
|
+
const b = stops[index + 1];
|
|
330
|
+
if (!a || !b) return null;
|
|
331
|
+
const span = Math.max(b.offset - a.offset, 1e-6);
|
|
332
|
+
return clamp((pos - a.offset) / span, 0.05, 0.95);
|
|
333
|
+
}
|
|
334
|
+
function spanVectorGradientStops(g, from, to, expanded = expandVectorGradientStops(g)) {
|
|
335
|
+
if (to < from) {
|
|
336
|
+
const s = spanVectorGradientStops(g, to, from, expanded);
|
|
337
|
+
return s.map((x) => ({ ...x, offset: 1 - x.offset })).reverse();
|
|
338
|
+
}
|
|
339
|
+
const d = to - from;
|
|
340
|
+
const at2 = (offset, t) => stopOf(offset, samplePlainStops(expanded, t));
|
|
341
|
+
const out = [at2(0, from)];
|
|
342
|
+
if (d > 1e-9) {
|
|
343
|
+
for (const s of expanded) if (s.offset > from && s.offset < to) out.push({ ...s, offset: (s.offset - from) / d });
|
|
344
|
+
}
|
|
345
|
+
out.push(at2(1, to));
|
|
346
|
+
return out;
|
|
347
|
+
}
|
|
348
|
+
var hasExplicitGradientGeometry = (g) => finitePoint(g.start) && finitePoint(g.end);
|
|
349
|
+
function shorthandPoints(g) {
|
|
350
|
+
if (g.type === "linear" || g.type === "reflected") {
|
|
351
|
+
const c = angleToSvgLinearCoords(Number.isFinite(g.angle) ? g.angle : 180);
|
|
352
|
+
const start2 = g.type === "linear" ? { x: c.x1, y: c.y1 } : { x: 0.5, y: 0.5 };
|
|
353
|
+
const end = { x: c.x2, y: c.y2 };
|
|
354
|
+
return { start: start2, end, width: add(start2, perp(sub(end, start2))) };
|
|
355
|
+
}
|
|
356
|
+
const start = { x: Number.isFinite(g.cx) ? g.cx : 0.5, y: Number.isFinite(g.cy) ? g.cy : 0.5 };
|
|
357
|
+
const r = Number.isFinite(g.r) ? g.r : 0.5;
|
|
358
|
+
if (g.type === "radial") return { start, end: { x: start.x + r, y: start.y }, width: { x: start.x, y: start.y + r } };
|
|
359
|
+
const a = (Number.isFinite(g.angle) ? g.angle : 90) * Math.PI / 180;
|
|
360
|
+
const dir = { x: Math.sin(a), y: -Math.cos(a) };
|
|
361
|
+
return { start, end: add(start, scale(dir, r)), width: add(start, scale(perp(dir), r)) };
|
|
362
|
+
}
|
|
363
|
+
function vectorGradientPoints(g) {
|
|
364
|
+
const explicit = hasExplicitGradientGeometry(g);
|
|
365
|
+
const base = explicit ? null : shorthandPoints(g);
|
|
366
|
+
const start = explicit ? { x: g.start.x, y: g.start.y } : base.start;
|
|
367
|
+
const end = explicit ? { x: g.end.x, y: g.end.y } : base.end;
|
|
368
|
+
const width = explicit ? finitePoint(g.width) ? { x: g.width.x, y: g.width.y } : add(start, perp(sub(end, start))) : base.width;
|
|
369
|
+
return {
|
|
370
|
+
start,
|
|
371
|
+
end,
|
|
372
|
+
width,
|
|
373
|
+
focal: g.type === "radial" && finitePoint(g.focal) ? { x: g.focal.x, y: g.focal.y } : null,
|
|
374
|
+
focalRadius: g.type === "radial" ? clamp(Number.isFinite(g.focalRadius) ? g.focalRadius : 0, 0, 0.99) : 0,
|
|
375
|
+
units: explicit && g.units === "userSpaceOnUse" ? "userSpaceOnUse" : "objectBoundingBox"
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function vectorBoxMatrix(bounds) {
|
|
379
|
+
if (!bounds) return IDENTITY;
|
|
380
|
+
let { x, y, width: w, height: h } = bounds;
|
|
381
|
+
const fallback = Math.max(w, h, 1e-9) > 1e-9 ? Math.max(w, h) : 1;
|
|
382
|
+
if (!(w > 1e-9)) {
|
|
383
|
+
x -= fallback / 2 - w / 2;
|
|
384
|
+
w = fallback;
|
|
385
|
+
}
|
|
386
|
+
if (!(h > 1e-9)) {
|
|
387
|
+
y -= fallback / 2 - h / 2;
|
|
388
|
+
h = fallback;
|
|
389
|
+
}
|
|
390
|
+
return [w, 0, 0, h, x, y];
|
|
391
|
+
}
|
|
392
|
+
var framePointsMatrix = (p) => [p.end.x - p.start.x, p.end.y - p.start.y, p.width.x - p.start.x, p.width.y - p.start.y, p.start.x, p.start.y];
|
|
393
|
+
function vectorGradientUnitsMatrix(g, bounds) {
|
|
394
|
+
return vectorGradientPoints(g).units === "objectBoundingBox" ? vectorBoxMatrix(bounds) : IDENTITY;
|
|
395
|
+
}
|
|
396
|
+
function vectorGradientMatrix(g, bounds) {
|
|
397
|
+
return mul(framePointsMatrix(vectorGradientPoints(g)), vectorGradientUnitsMatrix(g, bounds));
|
|
398
|
+
}
|
|
399
|
+
var FOCAL_MAX = 0.99;
|
|
400
|
+
function unitFocal(g) {
|
|
401
|
+
const p = vectorGradientPoints(g);
|
|
402
|
+
if (!p.focal && !(p.focalRadius > 0)) return null;
|
|
403
|
+
const inv = invert(framePointsMatrix(p));
|
|
404
|
+
if (!inv) return null;
|
|
405
|
+
let f = p.focal ? apply(inv, p.focal) : { x: 0, y: 0 };
|
|
406
|
+
const reach = FOCAL_MAX * (1 - p.focalRadius);
|
|
407
|
+
const r = Math.hypot(f.x, f.y);
|
|
408
|
+
if (r > reach) f = scale(f, reach / r);
|
|
409
|
+
return { f, fr: p.focalRadius };
|
|
410
|
+
}
|
|
411
|
+
function vectorGradientFocalParam(q, f = { x: 0, y: 0 }, fr = 0) {
|
|
412
|
+
const d = sub(q, f);
|
|
413
|
+
const dr = 1 - fr;
|
|
414
|
+
const a = dot(f, f) - dr * dr;
|
|
415
|
+
const b = dot(d, f) - fr * dr;
|
|
416
|
+
const c = dot(d, d) - fr * fr;
|
|
417
|
+
if (Math.abs(a) < 1e-12) return Math.abs(b) < 1e-12 ? 0 : -c / (2 * b);
|
|
418
|
+
return (-b - Math.sqrt(Math.max(0, b * b - a * c))) / a;
|
|
419
|
+
}
|
|
420
|
+
function vectorGradientParam(type, q, focal) {
|
|
421
|
+
switch (type) {
|
|
422
|
+
case "linear":
|
|
423
|
+
return q.x;
|
|
424
|
+
case "reflected":
|
|
425
|
+
return Math.abs(q.x);
|
|
426
|
+
case "diamond":
|
|
427
|
+
return Math.abs(q.x) + Math.abs(q.y);
|
|
428
|
+
case "conic": {
|
|
429
|
+
const turn = Math.atan2(q.y, q.x) / (2 * Math.PI);
|
|
430
|
+
return turn - Math.floor(turn);
|
|
431
|
+
}
|
|
432
|
+
case "radial":
|
|
433
|
+
return focal ? vectorGradientFocalParam(q, focal.f, focal.fr) : Math.hypot(q.x, q.y);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function applyVectorGradientSpread(t, spread = "pad") {
|
|
437
|
+
if (!Number.isFinite(t)) return 0;
|
|
438
|
+
if (spread === "repeat") return t - Math.floor(t);
|
|
439
|
+
if (spread === "reflect") {
|
|
440
|
+
const m = (t % 2 + 2) % 2;
|
|
441
|
+
return m <= 1 ? m : 2 - m;
|
|
442
|
+
}
|
|
443
|
+
return clamp(t, 0, 1);
|
|
444
|
+
}
|
|
445
|
+
function vectorGradientTAt(g, bounds, p) {
|
|
446
|
+
const inv = invert(vectorGradientMatrix(g, bounds));
|
|
447
|
+
if (!inv) return 0;
|
|
448
|
+
const raw = vectorGradientParam(g.type, apply(inv, p), g.type === "radial" ? unitFocal(g) : null);
|
|
449
|
+
return g.type === "conic" ? raw : applyVectorGradientSpread(raw, g.spread ?? "pad");
|
|
450
|
+
}
|
|
451
|
+
function sampleVectorGradientAt(g, bounds, p) {
|
|
452
|
+
const t = vectorGradientTAt(g, bounds, p);
|
|
453
|
+
const ramp = vectorGradientRamp(g);
|
|
454
|
+
return ramp ? ramp.sample(t) : samplePlainStops(sortedVectorGradientStops(g), t);
|
|
455
|
+
}
|
|
456
|
+
function vectorGradientAspect(g) {
|
|
457
|
+
const p = vectorGradientPoints(g);
|
|
458
|
+
const u = sub(p.end, p.start);
|
|
459
|
+
const l2 = dot(u, u);
|
|
460
|
+
return l2 > 1e-18 ? dot(sub(p.width, p.start), perp(u)) / l2 : 1;
|
|
461
|
+
}
|
|
462
|
+
function explicitVectorGradient(g) {
|
|
463
|
+
const p = vectorGradientPoints(g);
|
|
464
|
+
const { angle: _a, cx: _cx, cy: _cy, r: _r, ...rest } = g;
|
|
465
|
+
return {
|
|
466
|
+
...rest,
|
|
467
|
+
start: p.start,
|
|
468
|
+
end: p.end,
|
|
469
|
+
width: p.width,
|
|
470
|
+
...p.focal ? { focal: p.focal } : {},
|
|
471
|
+
units: p.units
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
function pinVectorGradient(g, bounds) {
|
|
475
|
+
const e = explicitVectorGradient(g);
|
|
476
|
+
if (e.units === "userSpaceOnUse") return e;
|
|
477
|
+
const m = vectorBoxMatrix(bounds);
|
|
478
|
+
return { ...e, start: apply(m, e.start), end: apply(m, e.end), width: apply(m, e.width), ...e.focal ? { focal: apply(m, e.focal) } : {}, units: "userSpaceOnUse" };
|
|
479
|
+
}
|
|
480
|
+
function transformVectorGradient(g, m) {
|
|
481
|
+
if (!hasExplicitGradientGeometry(g) || g.units !== "userSpaceOnUse" || isIdentity(m)) return g;
|
|
482
|
+
const p = vectorGradientPoints(g);
|
|
483
|
+
return { ...g, start: apply(m, p.start), end: apply(m, p.end), width: apply(m, p.width), ...p.focal ? { focal: apply(m, p.focal) } : {} };
|
|
484
|
+
}
|
|
485
|
+
function transformVectorPaint(paint, m) {
|
|
486
|
+
return isVectorGradient(paint) ? transformVectorGradient(paint, m) : paint;
|
|
487
|
+
}
|
|
488
|
+
var isCentredVectorGradient = (type) => type === "radial" || type === "conic" || type === "diamond";
|
|
489
|
+
function vectorGradientHandles(g, bounds, toWorld = IDENTITY) {
|
|
490
|
+
const p = vectorGradientPoints(g);
|
|
491
|
+
const m = mul(vectorGradientUnitsMatrix(g, bounds), toWorld);
|
|
492
|
+
const f = g.type === "radial" ? unitFocal(g) : null;
|
|
493
|
+
const frame2 = mul(framePointsMatrix(p), m);
|
|
494
|
+
return {
|
|
495
|
+
start: apply(m, p.start),
|
|
496
|
+
end: apply(m, p.end),
|
|
497
|
+
aspect: isCentredVectorGradient(g.type) ? apply(m, p.width) : null,
|
|
498
|
+
focal: g.type === "radial" ? apply(frame2, f?.f ?? { x: 0, y: 0 }) : null
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
function withMainAxis(p, start, end) {
|
|
502
|
+
const before = invert(framePointsMatrix(p));
|
|
503
|
+
const u = sub(p.end, p.start);
|
|
504
|
+
const l2 = dot(u, u);
|
|
505
|
+
const w = sub(p.width, p.start);
|
|
506
|
+
const rel = l2 > 1e-18 ? { a: dot(w, u) / l2, b: dot(w, perp(u)) / l2 } : { a: 0, b: 1 };
|
|
507
|
+
const u2 = sub(end, start);
|
|
508
|
+
const width = add(start, add(scale(u2, rel.a), scale(perp(u2), rel.b)));
|
|
509
|
+
const next = { start, end, width };
|
|
510
|
+
const focal = p.focal && before ? apply(framePointsMatrix(next), apply(before, p.focal)) : p.focal;
|
|
511
|
+
return { ...next, focal };
|
|
512
|
+
}
|
|
513
|
+
function moveVectorGradientHandle(g, bounds, handle, to, from = to) {
|
|
514
|
+
const pinned = pinVectorGradient(g, bounds);
|
|
515
|
+
const p = vectorGradientPoints(pinned);
|
|
516
|
+
const set = (q) => {
|
|
517
|
+
const next = { ...pinned, ...q };
|
|
518
|
+
if (next.focal === null || next.focal === void 0) delete next.focal;
|
|
519
|
+
return next;
|
|
520
|
+
};
|
|
521
|
+
switch (handle) {
|
|
522
|
+
case "start": {
|
|
523
|
+
const d = sub(to, from);
|
|
524
|
+
return set({ start: add(p.start, d), end: add(p.end, d), width: add(p.width, d), focal: p.focal ? add(p.focal, d) : null });
|
|
525
|
+
}
|
|
526
|
+
case "end":
|
|
527
|
+
case "radius": {
|
|
528
|
+
if (Math.hypot(to.x - p.start.x, to.y - p.start.y) < 1e-6) return pinned;
|
|
529
|
+
const n = withMainAxis(p, p.start, to);
|
|
530
|
+
return set({ end: n.end, width: n.width, focal: n.focal });
|
|
531
|
+
}
|
|
532
|
+
case "aspect": {
|
|
533
|
+
const u = sub(p.end, p.start);
|
|
534
|
+
const l2 = dot(u, u);
|
|
535
|
+
if (l2 < 1e-18) return pinned;
|
|
536
|
+
let aspect = dot(sub(to, p.start), perp(u)) / l2;
|
|
537
|
+
if (Math.abs(aspect) < 0.01) aspect = aspect < 0 ? -0.01 : 0.01;
|
|
538
|
+
const before = invert(framePointsMatrix(p));
|
|
539
|
+
const width = add(p.start, scale(perp(u), aspect));
|
|
540
|
+
const focal = p.focal && before ? apply(framePointsMatrix({ start: p.start, end: p.end, width }), apply(before, p.focal)) : null;
|
|
541
|
+
return set({ width, focal });
|
|
542
|
+
}
|
|
543
|
+
case "focal": {
|
|
544
|
+
const frame2 = framePointsMatrix(p);
|
|
545
|
+
const inv = invert(frame2);
|
|
546
|
+
if (!inv) return pinned;
|
|
547
|
+
let q = apply(inv, to);
|
|
548
|
+
const r = Math.hypot(q.x, q.y);
|
|
549
|
+
if (r < 1e-6) return set({ focal: null });
|
|
550
|
+
const reach = FOCAL_MAX * (1 - p.focalRadius);
|
|
551
|
+
if (r > reach) q = scale(q, reach / r);
|
|
552
|
+
return set({ focal: apply(frame2, q) });
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
function setVectorGradientVector(g, bounds, start, end) {
|
|
557
|
+
const pinned = pinVectorGradient(g, bounds);
|
|
558
|
+
const p = vectorGradientPoints(pinned);
|
|
559
|
+
const aspect = Math.abs(vectorGradientAspect(pinned)) > 1e-6 ? vectorGradientAspect(pinned) : 1;
|
|
560
|
+
const u = sub(end, start);
|
|
561
|
+
const width = add(start, scale(perp(u), aspect));
|
|
562
|
+
const before = invert(framePointsMatrix(p));
|
|
563
|
+
const focal = p.focal && before ? apply(framePointsMatrix({ start, end, width }), apply(before, p.focal)) : null;
|
|
564
|
+
const next = { ...pinned, start, end, width };
|
|
565
|
+
if (focal) next.focal = focal;
|
|
566
|
+
else delete next.focal;
|
|
567
|
+
return next;
|
|
568
|
+
}
|
|
569
|
+
var VECTOR_GRADIENT_MAX_PERIODS = 64;
|
|
570
|
+
function wholePeriod(stops) {
|
|
571
|
+
const out = stops.map((s) => ({ ...s }));
|
|
572
|
+
if (out.length && out[0].offset > 0) out.unshift({ ...out[0], offset: 0 });
|
|
573
|
+
const last = out[out.length - 1];
|
|
574
|
+
if (last && last.offset < 1) out.push({ ...last, offset: 1 });
|
|
575
|
+
return out;
|
|
576
|
+
}
|
|
577
|
+
function periodicVectorGradientStops(stops, lo, hi, mirrored) {
|
|
578
|
+
const period = wholePeriod(stops);
|
|
579
|
+
const back = reverseVectorGradient({ stops: period }).stops;
|
|
580
|
+
const n = Math.max(1, hi - lo);
|
|
581
|
+
const out = [];
|
|
582
|
+
for (let j = 0; j < n; j++) {
|
|
583
|
+
const one = mirrored(lo + j) ? back : period;
|
|
584
|
+
for (const s of one) out.push({ ...s, offset: (j + s.offset) / n });
|
|
585
|
+
}
|
|
586
|
+
return out;
|
|
587
|
+
}
|
|
588
|
+
function expandVectorGradientSpread(g, bounds, area) {
|
|
589
|
+
const m = vectorGradientMatrix(g, bounds);
|
|
590
|
+
const inv = invert(m);
|
|
591
|
+
const spread = g.spread ?? "pad";
|
|
592
|
+
const reflected = g.type === "reflected";
|
|
593
|
+
if (!inv || !reflected && spread === "pad") return null;
|
|
594
|
+
const odd = (k) => (k % 2 + 2) % 2 === 1;
|
|
595
|
+
const stops = expandVectorGradientStops(g);
|
|
596
|
+
const along2 = (a, b, mirrored2, capped2 = false) => ({
|
|
597
|
+
start: apply(m, { x: a, y: 0 }),
|
|
598
|
+
end: apply(m, { x: b, y: 0 }),
|
|
599
|
+
from: a,
|
|
600
|
+
to: b,
|
|
601
|
+
matrix: m,
|
|
602
|
+
stops: periodicVectorGradientStops(stops, a, b, mirrored2),
|
|
603
|
+
capped: capped2
|
|
604
|
+
});
|
|
605
|
+
if (reflected && spread === "pad") return along2(-1, 1, (k) => k < 0);
|
|
606
|
+
const us = boxCorners(area).map((c) => apply(inv, c).x);
|
|
607
|
+
let lo = Math.floor(Math.min(...us));
|
|
608
|
+
let hi = Math.ceil(Math.max(...us));
|
|
609
|
+
if (reflected) {
|
|
610
|
+
lo = Math.min(lo, -1);
|
|
611
|
+
hi = Math.max(hi, 1);
|
|
612
|
+
}
|
|
613
|
+
hi = Math.max(hi, lo + 1);
|
|
614
|
+
if (!reflected && lo >= 0 && hi <= 1) return null;
|
|
615
|
+
const capped = hi - lo > VECTOR_GRADIENT_MAX_PERIODS;
|
|
616
|
+
if (capped) {
|
|
617
|
+
lo = Math.max(lo, -VECTOR_GRADIENT_MAX_PERIODS / 2);
|
|
618
|
+
hi = lo + VECTOR_GRADIENT_MAX_PERIODS;
|
|
619
|
+
}
|
|
620
|
+
const mirrored = reflected ? (k) => {
|
|
621
|
+
const j = k >= 0 ? k : -k - 1;
|
|
622
|
+
const own = spread === "reflect" && odd(j);
|
|
623
|
+
return k < 0 ? !own : own;
|
|
624
|
+
} : (k) => spread === "reflect" && odd(k);
|
|
625
|
+
return along2(lo, hi, mirrored, capped);
|
|
626
|
+
}
|
|
627
|
+
var boxCorners = (b) => [
|
|
628
|
+
{ x: b.x, y: b.y },
|
|
629
|
+
{ x: b.x + b.width, y: b.y },
|
|
630
|
+
{ x: b.x + b.width, y: b.y + b.height },
|
|
631
|
+
{ x: b.x, y: b.y + b.height }
|
|
632
|
+
];
|
|
633
|
+
var toSvgStops = (stops) => stops.map((s) => ({ offset: `${fmt(s.offset * 100, 3)}%`, stopColor: s.color, ...s.opacity !== void 0 && s.opacity !== 1 ? { stopOpacity: Math.round(s.opacity * 1e4) / 1e4 } : {} }));
|
|
634
|
+
var polygonD = (pts, precision) => `M${pts.map((p) => `${fmt(p.x, precision)} ${fmt(p.y, precision)}`).join("L")}Z`;
|
|
635
|
+
var padBounds = (b, pad) => ({ x: b.x - pad, y: b.y - pad, width: b.width + 2 * pad, height: b.height + 2 * pad });
|
|
636
|
+
var vectorGradientArea = (bounds) => padBounds(bounds ?? { x: 0, y: 0, width: 1, height: 1 }, 1);
|
|
637
|
+
var transformAttr = (m) => isIdentity(m) ? {} : { gradientTransform: fmtMatrix(m) };
|
|
638
|
+
function minScale(m) {
|
|
639
|
+
const [a, b, c, d] = m;
|
|
640
|
+
const p = a * a + b * b;
|
|
641
|
+
const q = c * c + d * d;
|
|
642
|
+
const r = a * c + b * d;
|
|
643
|
+
return Math.sqrt(Math.max(0, (p + q - Math.sqrt((p - q) ** 2 + 4 * r * r)) / 2));
|
|
644
|
+
}
|
|
645
|
+
function patternAttrs(area) {
|
|
646
|
+
const [x, y, w, h] = [area.x, area.y, Math.max(area.width, 1e-3), Math.max(area.height, 1e-3)].map((n) => fmt(n, 3));
|
|
647
|
+
return { patternUnits: "userSpaceOnUse", x, y, width: w, height: h, viewBox: `${x} ${y} ${w} ${h}` };
|
|
648
|
+
}
|
|
649
|
+
function patternDef(id, area, gradients, tiles) {
|
|
650
|
+
return {
|
|
651
|
+
id,
|
|
652
|
+
type: "pattern",
|
|
653
|
+
attrs: patternAttrs(area),
|
|
654
|
+
gradients,
|
|
655
|
+
tiles
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function vectorGradientSvgDefs(g, id, options = {}) {
|
|
659
|
+
const precision = options.precision ?? 3;
|
|
660
|
+
const bounds = options.bounds ?? null;
|
|
661
|
+
const area = options.area ?? vectorGradientArea(bounds);
|
|
662
|
+
const spread = g.spread && VECTOR_GRADIENT_SPREADS.includes(g.spread) ? g.spread : "pad";
|
|
663
|
+
const stops = expandVectorGradientStops(g);
|
|
664
|
+
const p = vectorGradientPoints(g);
|
|
665
|
+
const unitsM = vectorGradientUnitsMatrix(g, bounds);
|
|
666
|
+
const frameM = mul(framePointsMatrix(p), unitsM);
|
|
667
|
+
const spreadAttr = (s) => s === "pad" ? {} : { spreadMethod: s };
|
|
668
|
+
const base = { gradientUnits: "userSpaceOnUse" };
|
|
669
|
+
switch (g.type) {
|
|
670
|
+
case "linear":
|
|
671
|
+
case "reflected": {
|
|
672
|
+
if (g.type === "reflected" && spread !== "reflect") {
|
|
673
|
+
const ex = expandVectorGradientSpread(g, bounds, area);
|
|
674
|
+
const [a, b] = ex ? [ex.from, ex.to] : [-1, 1];
|
|
675
|
+
const st = ex?.stops ?? periodicVectorGradientStops(stops, -1, 1, (k) => k < 0);
|
|
676
|
+
return [{ id, type: "linear", attrs: { ...base, x1: a, y1: 0, x2: b, y2: 0, gradientTransform: fmtMatrix(frameM) }, stops: toSvgStops(st) }];
|
|
677
|
+
}
|
|
678
|
+
const u = sub(p.end, p.start);
|
|
679
|
+
const w = sub(p.width, p.start);
|
|
680
|
+
const square = Math.abs(dot(u, w)) <= 1e-9 * Math.max(dot(u, u), dot(w, w), 1e-18);
|
|
681
|
+
const coords = square ? { x1: fmt(p.start.x, 6), y1: fmt(p.start.y, 6), x2: fmt(p.end.x, 6), y2: fmt(p.end.y, 6), ...transformAttr(unitsM) } : { x1: 0, y1: 0, x2: 1, y2: 0, gradientTransform: fmtMatrix(frameM) };
|
|
682
|
+
return [{ id, type: "linear", attrs: { ...base, ...coords, ...spreadAttr(spread) }, stops: toSvgStops(stops) }];
|
|
683
|
+
}
|
|
684
|
+
case "radial": {
|
|
685
|
+
const f = unitFocal(g);
|
|
686
|
+
const attrs = { ...base, cx: 0, cy: 0, r: 1, gradientTransform: fmtMatrix(frameM), ...spreadAttr(spread) };
|
|
687
|
+
if (f && (Math.abs(f.f.x) > 1e-9 || Math.abs(f.f.y) > 1e-9)) {
|
|
688
|
+
attrs.fx = fmt(f.f.x, 6);
|
|
689
|
+
attrs.fy = fmt(f.f.y, 6);
|
|
690
|
+
}
|
|
691
|
+
if (f && f.fr > 0) attrs.fr = fmt(f.fr, 6);
|
|
692
|
+
return [{ id, type: "radial", attrs, stops: toSvgStops(stops) }];
|
|
693
|
+
}
|
|
694
|
+
case "diamond": {
|
|
695
|
+
const inv = invert(frameM);
|
|
696
|
+
if (!inv) return [patternDef(id, area, [], [{ d: polygonD(boxCorners(area), precision), fill: stops[stops.length - 1]?.color ?? "none" }])];
|
|
697
|
+
const reach = 2 * Math.max(...boxCorners(area).map((c) => {
|
|
698
|
+
const q = apply(inv, c);
|
|
699
|
+
return Math.abs(q.x) + Math.abs(q.y);
|
|
700
|
+
})) + 1;
|
|
701
|
+
const delta = Math.min(0.05 * reach, 0.75 / Math.max(minScale(frameM), 1e-9));
|
|
702
|
+
const gradients = [];
|
|
703
|
+
const tiles = [];
|
|
704
|
+
[[1, 1], [-1, 1], [-1, -1], [1, -1]].forEach(([sx, sy], k) => {
|
|
705
|
+
const gid = `${id}-q${k}`;
|
|
706
|
+
gradients.push({ id: gid, type: "linear", attrs: { ...base, x1: 0, y1: 0, x2: sx / 2, y2: sy / 2, gradientTransform: fmtMatrix(frameM), ...spreadAttr(spread) }, stops: toSvgStops(stops) });
|
|
707
|
+
const quad = [{ x: -delta * sx, y: -delta * sy }, { x: reach * sx, y: -delta * sy }, { x: -delta * sx, y: reach * sy }].map((q) => apply(frameM, q));
|
|
708
|
+
tiles.push({ d: polygonD(quad, precision), fill: `url(#${gid})` });
|
|
709
|
+
});
|
|
710
|
+
return [patternDef(id, area, gradients, tiles)];
|
|
711
|
+
}
|
|
712
|
+
case "conic":
|
|
713
|
+
return [conicPattern(id, frameM, stops, area, options.wedges ?? 72, precision)];
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
function conicPattern(id, frameM, stops, area, wedges, precision) {
|
|
717
|
+
const inv = invert(frameM);
|
|
718
|
+
const n = Math.max(8, Math.min(720, Math.round(wedges)));
|
|
719
|
+
const half = Math.PI / n;
|
|
720
|
+
if (!inv || !stops.length) return patternDef(id, area, [], [{ d: polygonD(boxCorners(area), precision), fill: stops[0]?.color ?? "none" }]);
|
|
721
|
+
const reach = (Math.max(...boxCorners(area).map((c) => Math.hypot(apply(inv, c).x, apply(inv, c).y))) + 1) / Math.cos(half);
|
|
722
|
+
const overlap = Math.min(half, 2e-3);
|
|
723
|
+
const gradients = [];
|
|
724
|
+
const tiles = [];
|
|
725
|
+
for (let k = 0; k < n; k++) {
|
|
726
|
+
const a0 = 2 * Math.PI * k / n;
|
|
727
|
+
const a1 = 2 * Math.PI * (k + 1) / n;
|
|
728
|
+
const mid = (a0 + a1) / 2;
|
|
729
|
+
const tau = { x: -Math.sin(mid), y: Math.cos(mid) };
|
|
730
|
+
const s = Math.sin(half);
|
|
731
|
+
const at2 = (angle) => (Math.sin(angle - mid) / s + 1) / 2;
|
|
732
|
+
const t0 = k / n;
|
|
733
|
+
const t1 = (k + 1) / n;
|
|
734
|
+
const own = [stopOf(0, samplePlainStops(stops, t0))];
|
|
735
|
+
for (const st of stops) if (st.offset > t0 && st.offset < t1) own.push({ ...st, offset: at2(st.offset * 2 * Math.PI) });
|
|
736
|
+
own.push(stopOf(1, samplePlainStops(stops, t1)));
|
|
737
|
+
const gid = `${id}-w${k}`;
|
|
738
|
+
gradients.push({
|
|
739
|
+
id: gid,
|
|
740
|
+
type: "linear",
|
|
741
|
+
attrs: { gradientUnits: "userSpaceOnUse", x1: fmt(-s * tau.x, 6), y1: fmt(-s * tau.y, 6), x2: fmt(s * tau.x, 6), y2: fmt(s * tau.y, 6), gradientTransform: fmtMatrix(frameM) },
|
|
742
|
+
stops: toSvgStops(own)
|
|
743
|
+
});
|
|
744
|
+
const from = a0 - (k === 0 ? 0 : overlap);
|
|
745
|
+
const pts = [{ x: 0, y: 0 }, { x: reach * Math.cos(from), y: reach * Math.sin(from) }, { x: reach * Math.cos(a1), y: reach * Math.sin(a1) }].map((q) => apply(frameM, q));
|
|
746
|
+
tiles.push({ d: polygonD(pts, precision), fill: `url(#${gid})` });
|
|
747
|
+
}
|
|
748
|
+
return patternDef(id, area, gradients, tiles);
|
|
749
|
+
}
|
|
750
|
+
var num = (v2, fallback = 0) => {
|
|
751
|
+
if (v2 === void 0) return fallback;
|
|
752
|
+
const n = typeof v2 === "number" ? v2 : parseFloat(v2);
|
|
753
|
+
return Number.isFinite(n) ? n : fallback;
|
|
754
|
+
};
|
|
755
|
+
var parseMatrixAttr = (v2) => {
|
|
756
|
+
const m = typeof v2 === "string" ? /^matrix\(([^)]*)\)$/.exec(v2.trim()) : null;
|
|
757
|
+
if (!m) return IDENTITY;
|
|
758
|
+
const n = m[1].split(/[\s,]+/).map(Number);
|
|
759
|
+
return n.length === 6 && n.every(Number.isFinite) ? n : IDENTITY;
|
|
760
|
+
};
|
|
761
|
+
var plainOf = (stops) => stops.map((s) => ({ offset: parseFloat(s.offset) / (s.offset.endsWith("%") ? 100 : 1), color: s.stopColor, ...s.stopOpacity !== void 0 ? { opacity: s.stopOpacity } : {} }));
|
|
762
|
+
function inTile(d, p) {
|
|
763
|
+
const n = d.match(/-?[\d.]+(?:e-?\d+)?/gi)?.map(Number) ?? [];
|
|
764
|
+
let inside = false;
|
|
765
|
+
for (let i = 0, j = n.length - 2; i + 1 < n.length; j = i, i += 2) {
|
|
766
|
+
const [xi, yi, xj, yj] = [n[i], n[i + 1], n[j], n[j + 1]];
|
|
767
|
+
if (yi > p.y !== yj > p.y && p.x < (xj - xi) * (p.y - yi) / (yj - yi) + xi) inside = !inside;
|
|
768
|
+
}
|
|
769
|
+
return inside;
|
|
770
|
+
}
|
|
771
|
+
function sampleVectorPaintDefAt(defs, id, p) {
|
|
772
|
+
const all = /* @__PURE__ */ new Map();
|
|
773
|
+
for (const d of defs) {
|
|
774
|
+
all.set(d.id, d);
|
|
775
|
+
if (d.type === "pattern") for (const gd of d.gradients) all.set(gd.id, gd);
|
|
776
|
+
}
|
|
777
|
+
const paint = (ref, q) => {
|
|
778
|
+
const url = /^url\(#(.+)\)$/.exec(ref);
|
|
779
|
+
if (!url) return parseVectorColor(ref);
|
|
780
|
+
const def = all.get(url[1]);
|
|
781
|
+
if (!def) return null;
|
|
782
|
+
if (def.type === "pattern") {
|
|
783
|
+
for (let i = def.tiles.length - 1; i >= 0; i--) if (inTile(def.tiles[i].d, q)) return paint(def.tiles[i].fill, q);
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
const inv = invert(parseMatrixAttr(def.attrs.gradientTransform)) ?? IDENTITY;
|
|
787
|
+
const local = apply(inv, q);
|
|
788
|
+
const spread = def.attrs.spreadMethod ?? "pad";
|
|
789
|
+
let t;
|
|
790
|
+
if (def.type === "radial") {
|
|
791
|
+
const r = num(def.attrs.r, 0.5);
|
|
792
|
+
const c = { x: num(def.attrs.cx), y: num(def.attrs.cy) };
|
|
793
|
+
const f = { x: num(def.attrs.fx, c.x), y: num(def.attrs.fy, c.y) };
|
|
794
|
+
const u = { x: (local.x - c.x) / r, y: (local.y - c.y) / r };
|
|
795
|
+
t = vectorGradientFocalParam(u, { x: (f.x - c.x) / r, y: (f.y - c.y) / r }, num(def.attrs.fr) / r);
|
|
796
|
+
} else {
|
|
797
|
+
const a = { x: num(def.attrs.x1), y: num(def.attrs.y1) };
|
|
798
|
+
const b = { x: num(def.attrs.x2, 1), y: num(def.attrs.y2) };
|
|
799
|
+
const v2 = sub(b, a);
|
|
800
|
+
const l2 = dot(v2, v2);
|
|
801
|
+
t = l2 > 1e-18 ? dot(sub(local, a), v2) / l2 : 0;
|
|
802
|
+
}
|
|
803
|
+
return samplePlainStops(plainOf(def.stops), applyVectorGradientSpread(t, spread));
|
|
804
|
+
};
|
|
805
|
+
return paint(`url(#${id})`, p);
|
|
806
|
+
}
|
|
807
|
+
var readPoint = (v2) => finitePoint(v2) ? { x: v2.x, y: v2.y } : void 0;
|
|
808
|
+
function sanitizeVectorGradient(input) {
|
|
809
|
+
if (!input || typeof input !== "object") return null;
|
|
810
|
+
const r = input;
|
|
811
|
+
if (!VECTOR_GRADIENT_TYPES.includes(r.type) || !Array.isArray(r.stops)) return null;
|
|
812
|
+
const stops = [];
|
|
813
|
+
for (const s of r.stops) {
|
|
814
|
+
if (!s || typeof s !== "object") continue;
|
|
815
|
+
const x = s;
|
|
816
|
+
if (typeof x.color !== "string" || typeof x.offset !== "number" || !Number.isFinite(x.offset)) continue;
|
|
817
|
+
const stop = { offset: clamp(x.offset, 0, 1), color: x.color.slice(0, 100) };
|
|
818
|
+
if (typeof x.opacity === "number" && Number.isFinite(x.opacity) && x.opacity !== 1) stop.opacity = clamp(x.opacity, 0, 1);
|
|
819
|
+
if (typeof x.midpoint === "number" && Number.isFinite(x.midpoint) && Math.abs(x.midpoint - 0.5) > 1e-6) stop.midpoint = clamp(x.midpoint, 0.05, 0.95);
|
|
820
|
+
stops.push(stop);
|
|
821
|
+
}
|
|
822
|
+
if (!stops.length) return null;
|
|
823
|
+
const g = { type: r.type, stops };
|
|
824
|
+
for (const k of ["angle", "cx", "cy", "r"]) if (typeof r[k] === "number" && Number.isFinite(r[k])) g[k] = r[k];
|
|
825
|
+
const start = readPoint(r.start);
|
|
826
|
+
const end = readPoint(r.end);
|
|
827
|
+
if (start && end) {
|
|
828
|
+
g.start = start;
|
|
829
|
+
g.end = end;
|
|
830
|
+
const width = readPoint(r.width);
|
|
831
|
+
if (width) g.width = width;
|
|
832
|
+
if (r.units === "userSpaceOnUse" || r.units === "objectBoundingBox") g.units = r.units;
|
|
833
|
+
}
|
|
834
|
+
const focal = readPoint(r.focal);
|
|
835
|
+
if (focal && g.type === "radial") g.focal = focal;
|
|
836
|
+
if (typeof r.focalRadius === "number" && r.focalRadius > 0 && g.type === "radial") g.focalRadius = clamp(r.focalRadius, 0, 0.99);
|
|
837
|
+
if (VECTOR_GRADIENT_SPREADS.includes(r.spread) && r.spread !== "pad") g.spread = r.spread;
|
|
838
|
+
if (VECTOR_GRADIENT_INTERPOLATIONS.includes(r.interpolation) && r.interpolation !== "srgb") g.interpolation = r.interpolation;
|
|
839
|
+
if (typeof r.smoothness === "number" && r.smoothness > 0) g.smoothness = clamp(r.smoothness, 0, 1);
|
|
840
|
+
if (VECTOR_STROKE_GRADIENT_MODES.includes(r.strokeMode) && r.strokeMode !== "within") g.strokeMode = r.strokeMode;
|
|
841
|
+
return g;
|
|
842
|
+
}
|
|
843
|
+
function convertVectorGradient(g, type) {
|
|
844
|
+
if (g.type === type) return g;
|
|
845
|
+
const next = { ...g, type };
|
|
846
|
+
if (type !== "radial") {
|
|
847
|
+
delete next.focal;
|
|
848
|
+
delete next.focalRadius;
|
|
849
|
+
}
|
|
850
|
+
if (!hasExplicitGradientGeometry(g)) {
|
|
851
|
+
if (isCentredVectorGradient(type) !== isCentredVectorGradient(g.type)) {
|
|
852
|
+
delete next.angle;
|
|
853
|
+
delete next.cx;
|
|
854
|
+
delete next.cy;
|
|
855
|
+
delete next.r;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return next;
|
|
859
|
+
}
|
|
860
|
+
function defaultVectorGradient(type = "linear", from) {
|
|
861
|
+
if (isVectorGradient(from)) return convertVectorGradient(from, type);
|
|
862
|
+
const colour = typeof from === "string" && parseVectorColor(from) ? from : "#000000";
|
|
863
|
+
return { type, stops: [{ offset: 0, color: colour }, { offset: 1, color: "#ffffff" }] };
|
|
864
|
+
}
|
|
865
|
+
function applyVectorGradientPatch(g, patch) {
|
|
866
|
+
const { aspect, reverse: reverse2, type, ...fields } = patch;
|
|
867
|
+
let next = type && type !== g.type ? convertVectorGradient(g, type) : { ...g };
|
|
868
|
+
const record = next;
|
|
869
|
+
for (const [k, v2] of Object.entries(fields)) {
|
|
870
|
+
if (v2 === void 0) continue;
|
|
871
|
+
if (v2 === null) delete record[k];
|
|
872
|
+
else record[k] = v2;
|
|
873
|
+
}
|
|
874
|
+
if ((fields.start || fields.end) && !hasExplicitGradientGeometry(next)) {
|
|
875
|
+
const p = vectorGradientPoints({ ...g, start: void 0, end: void 0 });
|
|
876
|
+
next.start = next.start ?? p.start;
|
|
877
|
+
next.end = next.end ?? p.end;
|
|
878
|
+
}
|
|
879
|
+
if (aspect !== void 0 && Number.isFinite(aspect)) {
|
|
880
|
+
const e = explicitVectorGradient(next);
|
|
881
|
+
const u = sub(e.end, e.start);
|
|
882
|
+
const a = Math.abs(aspect) < 0.01 ? aspect < 0 ? -0.01 : 0.01 : aspect;
|
|
883
|
+
next = { ...e, width: add(e.start, scale(perp(u), a)) };
|
|
884
|
+
}
|
|
885
|
+
if (reverse2) next = reverseVectorGradient(next);
|
|
886
|
+
const clean2 = sanitizeVectorGradient(next);
|
|
887
|
+
if (!clean2) return null;
|
|
888
|
+
clean2.stops = sortedVectorGradientStops(clean2);
|
|
889
|
+
return clean2;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// src/vector/corners.ts
|
|
893
|
+
var EPS = 1e-9;
|
|
894
|
+
var MIN_SINE = 1e-6;
|
|
895
|
+
var sub2 = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
896
|
+
var add2 = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
897
|
+
var mul2 = (a, k) => ({ x: a.x * k, y: a.y * k });
|
|
898
|
+
var dot2 = (a, b) => a.x * b.x + a.y * b.y;
|
|
87
899
|
var cross = (a, b) => a.x * b.y - a.y * b.x;
|
|
88
900
|
var len = (a) => Math.hypot(a.x, a.y);
|
|
89
901
|
var pt = (p) => [p.x, p.y];
|
|
@@ -102,9 +914,9 @@ var Side = class {
|
|
|
102
914
|
direction() {
|
|
103
915
|
const p0 = vec(this.curve[0]);
|
|
104
916
|
for (const q of this.curve.slice(1)) {
|
|
105
|
-
const d =
|
|
917
|
+
const d = sub2(vec(q), p0);
|
|
106
918
|
const l = len(d);
|
|
107
|
-
if (l > EPS) return
|
|
919
|
+
if (l > EPS) return mul2(d, 1 / l);
|
|
108
920
|
}
|
|
109
921
|
return null;
|
|
110
922
|
}
|
|
@@ -112,12 +924,12 @@ var Side = class {
|
|
|
112
924
|
at(s, u) {
|
|
113
925
|
if (this.straight) {
|
|
114
926
|
const t2 = this.length > EPS ? Math.min(1, Math.max(0, s / this.length)) : 0;
|
|
115
|
-
return { t: t2, p:
|
|
927
|
+
return { t: t2, p: add2(vec(this.curve[0]), mul2(u, s)), dir: u };
|
|
116
928
|
}
|
|
117
929
|
const t = segmentParamAtLength(this.curve, s, this.length);
|
|
118
930
|
const d = vec(segmentDerivative(this.curve, t));
|
|
119
931
|
const l = len(d);
|
|
120
|
-
return { t, p: vec(segmentPoint(this.curve, t)), dir: l > EPS ?
|
|
932
|
+
return { t, p: vec(segmentPoint(this.curve, t)), dir: l > EPS ? mul2(d, 1 / l) : u };
|
|
121
933
|
}
|
|
122
934
|
};
|
|
123
935
|
function cornerOf(sp, s, i) {
|
|
@@ -136,7 +948,7 @@ function cornerOf(sp, s, i) {
|
|
|
136
948
|
const lu = sideU.length;
|
|
137
949
|
const lv = sideV.length;
|
|
138
950
|
if (!(lu > EPS && lv > EPS) || !Number.isFinite(lu) || !Number.isFinite(lv)) return null;
|
|
139
|
-
const cos = Math.max(-1, Math.min(1,
|
|
951
|
+
const cos = Math.max(-1, Math.min(1, dot2(u, v2)));
|
|
140
952
|
const sin = Math.abs(cross(u, v2));
|
|
141
953
|
if (sin <= MIN_SINE) return null;
|
|
142
954
|
return { subpath: s, index: i, at: { x: a.x, y: a.y }, u, v: v2, lu, lv, cos, sin, angle: Math.atan2(sin, cos) * 180 / Math.PI, maxRadius: Math.min(lu, lv) / 2 * sin / (1 + cos), sideU, sideV };
|
|
@@ -158,8 +970,8 @@ function vectorCorners(subpaths) {
|
|
|
158
970
|
}
|
|
159
971
|
var cornerSetback = (c, r) => r * (1 + c.cos) / c.sin;
|
|
160
972
|
var fitCornerRadius = (c, radius) => Number.isFinite(radius) ? Math.min(Math.max(0, radius), c.maxRadius) : 0;
|
|
161
|
-
var cornerBisectorPoint = (c, k) =>
|
|
162
|
-
var cornerRadiusChange = (c, d) =>
|
|
973
|
+
var cornerBisectorPoint = (c, k) => add2(c.at, mul2(add2(c.u, c.v), k));
|
|
974
|
+
var cornerRadiusChange = (c, d) => dot2(d, add2(c.u, c.v)) * c.sin / (2 + 2 * c.cos);
|
|
163
975
|
function arcHandle(cos) {
|
|
164
976
|
const c = Math.max(-1, Math.min(1, cos));
|
|
165
977
|
const sinHalf = Math.sqrt(Math.max(0, (1 - c) / 2));
|
|
@@ -167,9 +979,9 @@ function arcHandle(cos) {
|
|
|
167
979
|
return 4 / 3 * sinHalf / (1 + cosHalf);
|
|
168
980
|
}
|
|
169
981
|
function squareTo(a, b) {
|
|
170
|
-
const w =
|
|
982
|
+
const w = sub2(b, mul2(a, dot2(a, b)));
|
|
171
983
|
const l = len(w);
|
|
172
|
-
return l > EPS ?
|
|
984
|
+
return l > EPS ? mul2(w, 1 / l) : { x: 0, y: 0 };
|
|
173
985
|
}
|
|
174
986
|
function cutCorner(c, radius, kind) {
|
|
175
987
|
const s = Math.min(cornerSetback(c, radius), Math.min(c.lu, c.lv) / 2);
|
|
@@ -178,16 +990,16 @@ function cutCorner(c, radius, kind) {
|
|
|
178
990
|
const b = c.sideV.at(s, c.v);
|
|
179
991
|
if (kind === "chamfer") return { tu: a.t, tv: b.t, start: a.p, out: null, in: null, end: b.p };
|
|
180
992
|
if (kind === "inverted") {
|
|
181
|
-
const h = s * arcHandle(
|
|
182
|
-
return { tu: a.t, tv: b.t, start: a.p, out:
|
|
993
|
+
const h = s * arcHandle(dot2(a.dir, b.dir));
|
|
994
|
+
return { tu: a.t, tv: b.t, start: a.p, out: add2(a.p, mul2(squareTo(a.dir, b.dir), h)), in: add2(b.p, mul2(squareTo(b.dir, a.dir), h)), end: b.p };
|
|
183
995
|
}
|
|
184
|
-
const d1 =
|
|
185
|
-
const d2 =
|
|
186
|
-
const turn = Math.acos(Math.max(-1, Math.min(1, -
|
|
187
|
-
const chord = len(
|
|
996
|
+
const d1 = mul2(a.dir, -1);
|
|
997
|
+
const d2 = mul2(b.dir, -1);
|
|
998
|
+
const turn = Math.acos(Math.max(-1, Math.min(1, -dot2(d1, d2))));
|
|
999
|
+
const chord = len(sub2(b.p, a.p));
|
|
188
1000
|
const k = turn < 1e-9 ? chord / 3 : 4 / 3 * Math.tan(turn / 4) * chord / (2 * Math.sin(turn / 2));
|
|
189
1001
|
if (!Number.isFinite(k)) return null;
|
|
190
|
-
return { tu: a.t, tv: b.t, start: a.p, out:
|
|
1002
|
+
return { tu: a.t, tv: b.t, start: a.p, out: add2(a.p, mul2(d1, k)), in: add2(b.p, mul2(d2, k)), end: b.p };
|
|
191
1003
|
}
|
|
192
1004
|
var radiusOf = (a) => {
|
|
193
1005
|
const r = a.corner?.radius;
|
|
@@ -499,7 +1311,7 @@ function pressureWidthProfile(samples, options = {}) {
|
|
|
499
1311
|
// src/vector/varstroke.ts
|
|
500
1312
|
var SAMPLES_PER_SUBPATH = 128;
|
|
501
1313
|
var MAX_DASHES = 5e3;
|
|
502
|
-
var
|
|
1314
|
+
var sub3 = (a, b) => [a[0] - b[0], a[1] - b[1]];
|
|
503
1315
|
var along = (p, v2, k) => [p[0] + v2[0] * k, p[1] + v2[1] * k];
|
|
504
1316
|
var lerp = (a, b, u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u];
|
|
505
1317
|
var dist = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1]);
|
|
@@ -590,11 +1402,11 @@ function arcPoints(c, a0, sweep, r0, r1, tol) {
|
|
|
590
1402
|
return out;
|
|
591
1403
|
}
|
|
592
1404
|
function crossing(a0, a1, b0, b1) {
|
|
593
|
-
const r =
|
|
594
|
-
const q =
|
|
1405
|
+
const r = sub3(a1, a0);
|
|
1406
|
+
const q = sub3(b1, b0);
|
|
595
1407
|
const den = r[0] * q[1] - r[1] * q[0];
|
|
596
1408
|
if (Math.abs(den) < 1e-12) return null;
|
|
597
|
-
const w =
|
|
1409
|
+
const w = sub3(b0, a0);
|
|
598
1410
|
const t = (w[0] * q[1] - w[1] * q[0]) / den;
|
|
599
1411
|
const u = (w[0] * r[1] - w[1] * r[0]) / den;
|
|
600
1412
|
return t > 1e-9 && t <= 1 && u >= 0 && u < 1 - 1e-9 ? [a0[0] + r[0] * t, a0[1] + r[1] * t] : null;
|
|
@@ -638,7 +1450,7 @@ function variableStrokeRings(path, options) {
|
|
|
638
1450
|
const closed = whole && poly.closed;
|
|
639
1451
|
const m = run.length - 1;
|
|
640
1452
|
const dirs = [];
|
|
641
|
-
for (let i = 0; i < m; i++) dirs.push(unitOf(
|
|
1453
|
+
for (let i = 0; i < m; i++) dirs.push(unitOf(sub3(run[i + 1].p, run[i].p)));
|
|
642
1454
|
const normals = dirs.map(leftNormal);
|
|
643
1455
|
const w = run.map((q) => sides2(q.s));
|
|
644
1456
|
const side = (sigma) => {
|
|
@@ -657,12 +1469,12 @@ function variableStrokeRings(path, options) {
|
|
|
657
1469
|
const d0 = dirs[i];
|
|
658
1470
|
const d1 = dirs[j];
|
|
659
1471
|
const cross2 = d0[0] * d1[1] - d0[1] * d1[0];
|
|
660
|
-
const
|
|
1472
|
+
const dot3 = d0[0] * d1[0] + d0[1] * d1[1];
|
|
661
1473
|
const r0 = w[i + 1][k];
|
|
662
1474
|
const r1 = w[j][k];
|
|
663
1475
|
const e = E(i);
|
|
664
1476
|
const st = S(j);
|
|
665
|
-
if (Math.abs(cross2) < 1e-12 &&
|
|
1477
|
+
if (Math.abs(cross2) < 1e-12 && dot3 > 0) {
|
|
666
1478
|
push(out, e);
|
|
667
1479
|
if (!seam) push(out, st);
|
|
668
1480
|
continue;
|
|
@@ -680,7 +1492,7 @@ function variableStrokeRings(path, options) {
|
|
|
680
1492
|
continue;
|
|
681
1493
|
}
|
|
682
1494
|
push(out, e);
|
|
683
|
-
const theta = uturn ? Math.PI : Math.atan2(cross2,
|
|
1495
|
+
const theta = uturn ? Math.PI : Math.atan2(cross2, dot3);
|
|
684
1496
|
const r = (r0 + r1) / 2;
|
|
685
1497
|
const kind = run[i + 1].corner || seam && poly.corner[0] ? join : "round";
|
|
686
1498
|
if (r > 1e-9) {
|
|
@@ -700,10 +1512,10 @@ function variableStrokeRings(path, options) {
|
|
|
700
1512
|
if (closed && out.length > 1 && dist(out[0], out[out.length - 1]) <= 1e-12) out.pop();
|
|
701
1513
|
return out;
|
|
702
1514
|
};
|
|
703
|
-
const
|
|
1515
|
+
const left2 = side(1);
|
|
704
1516
|
const right = side(-1);
|
|
705
1517
|
if (closed) {
|
|
706
|
-
rings.push(
|
|
1518
|
+
rings.push(left2, right.reverse());
|
|
707
1519
|
continue;
|
|
708
1520
|
}
|
|
709
1521
|
const capPoints = (atStart) => {
|
|
@@ -725,7 +1537,7 @@ function variableStrokeRings(path, options) {
|
|
|
725
1537
|
return atStart ? [along(R, o, ext), along(L, o, ext)] : [along(L, o, ext), along(R, o, ext)];
|
|
726
1538
|
};
|
|
727
1539
|
const ring = [];
|
|
728
|
-
for (const q of [...
|
|
1540
|
+
for (const q of [...left2, ...capPoints(false), ...right.reverse(), ...capPoints(true)]) push(ring, q);
|
|
729
1541
|
if (ring.length > 2) rings.push(ring);
|
|
730
1542
|
}
|
|
731
1543
|
}
|
|
@@ -841,8 +1653,8 @@ function vectorStrokeLineArea(parts, style, tolerance) {
|
|
|
841
1653
|
}
|
|
842
1654
|
var capExtent = (style, width) => style.strokeLinecap === "round" || style.strokeLinecap === "square" ? width / 2 : 0;
|
|
843
1655
|
function arrowheadWeight(style, end) {
|
|
844
|
-
const
|
|
845
|
-
const s = typeof
|
|
1656
|
+
const scale3 = end === "start" ? style.startArrowScale : style.endArrowScale;
|
|
1657
|
+
const s = typeof scale3 === "number" && scale3 > 0 && Number.isFinite(scale3) ? scale3 : 1;
|
|
846
1658
|
const width = style.strokeWidth ?? 1;
|
|
847
1659
|
const profile = strokeWidthProfileOf(style);
|
|
848
1660
|
const at2 = profile ? widthProfileAt(profile, end === "start" ? 0 : 1) : null;
|
|
@@ -1082,7 +1894,7 @@ function tangentAt(s, t) {
|
|
|
1082
1894
|
const d = segmentDerivative(s, t);
|
|
1083
1895
|
return unit2(d[0], d[1]) ?? startTangent(s);
|
|
1084
1896
|
}
|
|
1085
|
-
var
|
|
1897
|
+
var perp2 = (v2) => [-v2[1], v2[0]];
|
|
1086
1898
|
function effectPathLength(path) {
|
|
1087
1899
|
let total = 0;
|
|
1088
1900
|
for (const s of subsOf(path)) for (const seg of s.segs) total += segmentLength(seg);
|
|
@@ -1252,9 +2064,9 @@ function twistPaths(paths, angle, center) {
|
|
|
1252
2064
|
};
|
|
1253
2065
|
return paths.map((p) => {
|
|
1254
2066
|
const out = [];
|
|
1255
|
-
for (const
|
|
2067
|
+
for (const sub5 of subsOf(p)) {
|
|
1256
2068
|
const segs = [];
|
|
1257
|
-
for (const seg of
|
|
2069
|
+
for (const seg of sub5.segs) {
|
|
1258
2070
|
const cubic = seg.length === 2 ? [seg[0], seg[0], seg[1], seg[1]] : seg;
|
|
1259
2071
|
const len2 = segmentLength(seg);
|
|
1260
2072
|
const m = Math.min(64, Math.max(1, Math.ceil(Math.abs(a) * len2 / rmax / 0.15 + len2 / 50)));
|
|
@@ -1267,13 +2079,13 @@ function twistPaths(paths, angle, center) {
|
|
|
1267
2079
|
segs.push([p0, [p0[0] + h1[0], p0[1] + h1[1]], [p3[0] + h2[0], p3[1] + h2[1]], p3]);
|
|
1268
2080
|
}
|
|
1269
2081
|
}
|
|
1270
|
-
out.push({ segs, closed:
|
|
2082
|
+
out.push({ segs, closed: sub5.closed });
|
|
1271
2083
|
}
|
|
1272
2084
|
return subsToPath(out);
|
|
1273
2085
|
});
|
|
1274
2086
|
}
|
|
1275
|
-
function resample(
|
|
1276
|
-
const segs =
|
|
2087
|
+
function resample(sub5, m) {
|
|
2088
|
+
const segs = sub5.segs;
|
|
1277
2089
|
const n = segs.length;
|
|
1278
2090
|
const lens = segs.map((s) => segmentLength(s));
|
|
1279
2091
|
const out = [];
|
|
@@ -1282,7 +2094,7 @@ function resample(sub3, m) {
|
|
|
1282
2094
|
for (let k = 0; k < m; k++) {
|
|
1283
2095
|
if (k === 0) {
|
|
1284
2096
|
const tout = startTangent(segs[j]);
|
|
1285
|
-
const prev =
|
|
2097
|
+
const prev = sub5.closed || j > 0 ? (j + n - 1) % n : null;
|
|
1286
2098
|
let tan = tout;
|
|
1287
2099
|
if (prev !== null) {
|
|
1288
2100
|
const tin = endTangent(segs[prev]);
|
|
@@ -1295,7 +2107,7 @@ function resample(sub3, m) {
|
|
|
1295
2107
|
}
|
|
1296
2108
|
}
|
|
1297
2109
|
}
|
|
1298
|
-
if (!
|
|
2110
|
+
if (!sub5.closed) {
|
|
1299
2111
|
const last = segs[n - 1];
|
|
1300
2112
|
out.push({ p: last[last.length - 1], tan: endTangent(last), lenPrev: lens[n - 1] / m, lenNext: 0 });
|
|
1301
2113
|
}
|
|
@@ -1323,14 +2135,14 @@ function zigzagPaths(paths, size, ridges, smooth = false) {
|
|
|
1323
2135
|
const m = Math.min(1e3, Math.max(0, Math.round(Number.isFinite(ridges) ? ridges : 0))) + 1;
|
|
1324
2136
|
return paths.map((p) => {
|
|
1325
2137
|
const out = [];
|
|
1326
|
-
for (const
|
|
1327
|
-
const s = resample(
|
|
2138
|
+
for (const sub5 of subsOf(p)) {
|
|
2139
|
+
const s = resample(sub5, m);
|
|
1328
2140
|
const disp = s.map((x, i) => {
|
|
1329
|
-
const n =
|
|
2141
|
+
const n = perp2(x.tan);
|
|
1330
2142
|
const d = i % 2 === 0 ? size : -size;
|
|
1331
2143
|
return [n[0] * d, n[1] * d];
|
|
1332
2144
|
});
|
|
1333
|
-
const e = emit(s, disp,
|
|
2145
|
+
const e = emit(s, disp, sub5.closed, smooth, 0.36);
|
|
1334
2146
|
if (e) out.push(e);
|
|
1335
2147
|
}
|
|
1336
2148
|
return subsToPath(out);
|
|
@@ -1369,8 +2181,8 @@ function wigglePaths(paths, w) {
|
|
|
1369
2181
|
let idx = 0;
|
|
1370
2182
|
return paths.map((p) => {
|
|
1371
2183
|
const out = [];
|
|
1372
|
-
for (const
|
|
1373
|
-
const s = resample(
|
|
2184
|
+
for (const sub5 of subsOf(p)) {
|
|
2185
|
+
const s = resample(sub5, m);
|
|
1374
2186
|
const disp = s.map(() => {
|
|
1375
2187
|
const i = BigInt(idx);
|
|
1376
2188
|
const ox = noise(seed, i * 2n, t + idx * 0.37);
|
|
@@ -1378,7 +2190,7 @@ function wigglePaths(paths, w) {
|
|
|
1378
2190
|
idx++;
|
|
1379
2191
|
return [(sx * corr + ox * (1 - corr)) * w.size, (sy * corr + oy * (1 - corr)) * w.size];
|
|
1380
2192
|
});
|
|
1381
|
-
const e = emit(s, disp,
|
|
2193
|
+
const e = emit(s, disp, sub5.closed, w.smooth, 1 / 3);
|
|
1382
2194
|
if (e) out.push(e);
|
|
1383
2195
|
}
|
|
1384
2196
|
return subsToPath(out);
|
|
@@ -1691,7 +2503,7 @@ var compareOrder = (a, b) => {
|
|
|
1691
2503
|
for (let i = 0; i < n; i++) if (a[i] !== b[i]) return a[i] - b[i];
|
|
1692
2504
|
return a.length - b.length;
|
|
1693
2505
|
};
|
|
1694
|
-
var
|
|
2506
|
+
var add3 = (a, b) => ({ x: a.x + (b?.x ?? 0), y: a.y + (b?.y ?? 0) });
|
|
1695
2507
|
var mapPaths = (shapes, fn) => {
|
|
1696
2508
|
const out = fn(shapes.map((s) => s.path));
|
|
1697
2509
|
return shapes.map((s, i) => ({ ...s, path: out[i] ?? [] }));
|
|
@@ -1713,7 +2525,7 @@ function applyVectorEffects(shapes, effects) {
|
|
|
1713
2525
|
list = mapPaths(list, (p) => puckerBloatPaths(p, e.params.amount));
|
|
1714
2526
|
break;
|
|
1715
2527
|
case "twist":
|
|
1716
|
-
list = mapPaths(list, (p) => twistPaths(p, e.params.angle,
|
|
2528
|
+
list = mapPaths(list, (p) => twistPaths(p, e.params.angle, add3(pathsCenter(p), e.params.center)));
|
|
1717
2529
|
break;
|
|
1718
2530
|
case "zigzag":
|
|
1719
2531
|
list = mapPaths(list, (p) => zigzagPaths(p, e.params.size, e.params.ridges, e.params.smooth));
|
|
@@ -1736,7 +2548,7 @@ function applyVectorEffects(shapes, effects) {
|
|
|
1736
2548
|
const copies = repeatPaths(
|
|
1737
2549
|
list.map((s) => s.path),
|
|
1738
2550
|
r,
|
|
1739
|
-
|
|
2551
|
+
add3(pathsCenter(list.map((s) => s.path)), r.anchor)
|
|
1740
2552
|
);
|
|
1741
2553
|
const n = copies.length;
|
|
1742
2554
|
const next = [];
|
|
@@ -1982,8 +2794,8 @@ function inferAnchorType(a) {
|
|
|
1982
2794
|
const li = Math.hypot(ix, iy);
|
|
1983
2795
|
const lo = Math.hypot(ox, oy);
|
|
1984
2796
|
const cross2 = (ix * oy - iy * ox) / (li * lo);
|
|
1985
|
-
const
|
|
1986
|
-
if (Math.abs(cross2) > 1e-3 ||
|
|
2797
|
+
const dot3 = (ix * ox + iy * oy) / (li * lo);
|
|
2798
|
+
if (Math.abs(cross2) > 1e-3 || dot3 > 0) return "corner";
|
|
1987
2799
|
return Math.abs(li - lo) <= 1e-3 * Math.max(li, lo) ? "symmetric" : "smooth";
|
|
1988
2800
|
}
|
|
1989
2801
|
function bezierToSubpaths(input) {
|
|
@@ -2232,6 +3044,9 @@ function bakeIntoGeometry(node, m) {
|
|
|
2232
3044
|
return {
|
|
2233
3045
|
...path,
|
|
2234
3046
|
...mirrored ? { strokeWidthProfile: mirrorWidthProfile(path.strokeWidthProfile) } : {},
|
|
3047
|
+
// Gradients in the node's own space move with its geometry.
|
|
3048
|
+
...isVectorGradient(path.fill) ? { fill: transformVectorPaint(path.fill, m) } : {},
|
|
3049
|
+
...isVectorGradient(path.stroke) ? { stroke: transformVectorPaint(path.stroke, m) } : {},
|
|
2235
3050
|
subpaths: path.subpaths.map((sp) => ({
|
|
2236
3051
|
closed: sp.closed,
|
|
2237
3052
|
anchors: sp.anchors.map((a) => mapAnchor(a, m))
|
|
@@ -2334,12 +3149,12 @@ function insertAnchor(sp, segment, t) {
|
|
|
2334
3149
|
const ia = segment;
|
|
2335
3150
|
const ib = (segment + 1) % n;
|
|
2336
3151
|
const seg = subpathSegment(sp, segment);
|
|
2337
|
-
const [
|
|
2338
|
-
const mid =
|
|
3152
|
+
const [left2, right] = splitSegment(seg, t);
|
|
3153
|
+
const mid = left2[left2.length - 1];
|
|
2339
3154
|
const anchor = { x: mid[0], y: mid[1] };
|
|
2340
3155
|
if (seg.length === 4) {
|
|
2341
|
-
anchors[ia].out = { x:
|
|
2342
|
-
anchor.in = { x:
|
|
3156
|
+
anchors[ia].out = { x: left2[1][0], y: left2[1][1] };
|
|
3157
|
+
anchor.in = { x: left2[2][0], y: left2[2][1] };
|
|
2343
3158
|
anchor.out = { x: right[1][0], y: right[1][1] };
|
|
2344
3159
|
anchor.type = "smooth";
|
|
2345
3160
|
anchors[ib].in = { x: right[2][0], y: right[2][1] };
|
|
@@ -2424,6 +3239,166 @@ function roundSubpaths(subpaths, precision = 3) {
|
|
|
2424
3239
|
}));
|
|
2425
3240
|
}
|
|
2426
3241
|
|
|
3242
|
+
// src/vector/gradientStroke.ts
|
|
3243
|
+
var SHARP = 1.25;
|
|
3244
|
+
var TOLERANCE = 0.2;
|
|
3245
|
+
var OVERLAP = 0.5;
|
|
3246
|
+
var fmt2 = (n, digits = 3) => {
|
|
3247
|
+
const k = 10 ** digits;
|
|
3248
|
+
const v2 = Math.round(n * k) / k;
|
|
3249
|
+
return Object.is(v2, -0) ? "0" : String(v2);
|
|
3250
|
+
};
|
|
3251
|
+
var toSvgStops2 = (stops) => stops.map((s) => ({ offset: `${fmt2(s.offset * 100)}%`, stopColor: s.color, ...s.opacity !== void 0 && s.opacity !== 1 ? { stopOpacity: Math.round(s.opacity * 1e4) / 1e4 } : {} }));
|
|
3252
|
+
var polygonD2 = (pts, precision) => `M${pts.map((p) => `${fmt2(p.x, precision)} ${fmt2(p.y, precision)}`).join("L")}Z`;
|
|
3253
|
+
var add4 = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
3254
|
+
var sub4 = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
3255
|
+
var scale2 = (a, k) => ({ x: a.x * k, y: a.y * k });
|
|
3256
|
+
var left = (d) => ({ x: d.y, y: -d.x });
|
|
3257
|
+
var unit3 = (v2) => {
|
|
3258
|
+
const l = Math.hypot(v2.x, v2.y);
|
|
3259
|
+
return l > 1e-12 ? { x: v2.x / l, y: v2.y / l } : { x: 1, y: 0 };
|
|
3260
|
+
};
|
|
3261
|
+
function flattenedSubpaths(node) {
|
|
3262
|
+
const out = [];
|
|
3263
|
+
for (const contour of pathToContours(subpathsToBezier(node.subpaths))) {
|
|
3264
|
+
const segs = closeContour(contour);
|
|
3265
|
+
if (!segs.length) continue;
|
|
3266
|
+
const ring = flattenChain(segs, TOLERANCE);
|
|
3267
|
+
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);
|
|
3268
|
+
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();
|
|
3269
|
+
if (pts.length >= 2) out.push({ pts, closed: contour.closed && pts.length > 2 });
|
|
3270
|
+
}
|
|
3271
|
+
return out;
|
|
3272
|
+
}
|
|
3273
|
+
var signedArea = (pts) => {
|
|
3274
|
+
let a = 0;
|
|
3275
|
+
for (let i = 0; i < pts.length; i++) {
|
|
3276
|
+
const p = pts[i];
|
|
3277
|
+
const q = pts[(i + 1) % pts.length];
|
|
3278
|
+
a += p.x * q.y - q.x * p.y;
|
|
3279
|
+
}
|
|
3280
|
+
return a / 2;
|
|
3281
|
+
};
|
|
3282
|
+
function vectorStrokeGradientDefs(g, id, node, options = {}) {
|
|
3283
|
+
const precision = options.precision ?? 3;
|
|
3284
|
+
const mode = g.strokeMode === "across" ? "across" : "along";
|
|
3285
|
+
const expanded = expandVectorGradientStops(g);
|
|
3286
|
+
const reach = vectorStrokeReach(node) + 1;
|
|
3287
|
+
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 });
|
|
3288
|
+
const width = node.strokeWidth ?? 1;
|
|
3289
|
+
const align = node.strokeAlign ?? "center";
|
|
3290
|
+
const profile = strokeWidthProfileOf(node);
|
|
3291
|
+
const gradients = [];
|
|
3292
|
+
const patches = [];
|
|
3293
|
+
const slices = [];
|
|
3294
|
+
const colourAt = (t) => {
|
|
3295
|
+
const s = spanVectorGradientStops(g, t, t, expanded)[0];
|
|
3296
|
+
return s;
|
|
3297
|
+
};
|
|
3298
|
+
let counter = 0;
|
|
3299
|
+
const solid = (pts, t) => {
|
|
3300
|
+
const s = colourAt(t);
|
|
3301
|
+
const gid = `${id}-p${counter++}`;
|
|
3302
|
+
gradients.push({ id: gid, type: "linear", attrs: { gradientUnits: "userSpaceOnUse", x1: 0, y1: 0, x2: 1, y2: 0 }, stops: toSvgStops2([{ ...s, offset: 0 }]) });
|
|
3303
|
+
patches.push({ d: polygonD2(pts, precision), fill: `url(#${gid})` });
|
|
3304
|
+
};
|
|
3305
|
+
const disc = (c, r) => Array.from({ length: 16 }, (_, k) => add4(c, scale2({ x: Math.cos(k * Math.PI / 8), y: Math.sin(k * Math.PI / 8) }, r)));
|
|
3306
|
+
for (const { pts, closed } of flattenedSubpaths(node)) {
|
|
3307
|
+
const n = pts.length;
|
|
3308
|
+
const segCount = closed ? n : n - 1;
|
|
3309
|
+
const dirs = [];
|
|
3310
|
+
const lengths = [0];
|
|
3311
|
+
for (let i = 0; i < segCount; i++) {
|
|
3312
|
+
const a = pts[i];
|
|
3313
|
+
const b = pts[(i + 1) % n];
|
|
3314
|
+
dirs.push(unit3(sub4(b, a)));
|
|
3315
|
+
lengths.push(lengths[i] + Math.hypot(b.x - a.x, b.y - a.y));
|
|
3316
|
+
}
|
|
3317
|
+
const total = lengths[segCount];
|
|
3318
|
+
if (!(total > 1e-9)) continue;
|
|
3319
|
+
const tAt = (i) => lengths[i] / total;
|
|
3320
|
+
const clockwise = closed && signedArea(pts) > 0;
|
|
3321
|
+
const visibleLeft = align === "center" || !closed ? null : align === "inside" ? !clockwise : clockwise;
|
|
3322
|
+
const ribs = [];
|
|
3323
|
+
for (let i = 0; i < n; i++) {
|
|
3324
|
+
const p = pts[i];
|
|
3325
|
+
const din = i > 0 || closed ? dirs[(i - 1 + segCount) % segCount] : null;
|
|
3326
|
+
const dout = i < segCount ? dirs[i] : null;
|
|
3327
|
+
const square = (d) => ({ l: add4(p, scale2(left(d), reach)), r: sub4(p, scale2(left(d), reach)) });
|
|
3328
|
+
if (!din || !dout) {
|
|
3329
|
+
const rib = square(din ?? dout);
|
|
3330
|
+
ribs.push({ before: rib, after: rib });
|
|
3331
|
+
continue;
|
|
3332
|
+
}
|
|
3333
|
+
const nin = left(din);
|
|
3334
|
+
const nout = left(dout);
|
|
3335
|
+
const den = 1 + nin.x * nout.x + nin.y * nout.y;
|
|
3336
|
+
const stretch = den > 1e-12 ? Math.sqrt(2 / den) : Infinity;
|
|
3337
|
+
if (stretch <= SHARP) {
|
|
3338
|
+
const m = scale2(add4(nin, nout), reach / den);
|
|
3339
|
+
const rib = { l: add4(p, m), r: sub4(p, m) };
|
|
3340
|
+
ribs.push({ before: rib, after: rib });
|
|
3341
|
+
} else {
|
|
3342
|
+
ribs.push({ before: square(din), after: square(dout) });
|
|
3343
|
+
solid(disc(p, reach * 1.5), tAt(i));
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
if (!closed) {
|
|
3347
|
+
const d0 = dirs[0];
|
|
3348
|
+
const d1 = dirs[segCount - 1];
|
|
3349
|
+
const a = pts[0];
|
|
3350
|
+
const b = pts[n - 1];
|
|
3351
|
+
const cap = (p, d) => [add4(p, scale2(left(d), reach)), add4(add4(p, scale2(d, reach)), scale2(left(d), reach)), sub4(add4(p, scale2(d, reach)), scale2(left(d), reach)), sub4(p, scale2(left(d), reach))];
|
|
3352
|
+
solid(cap(a, scale2(d0, -1)), 0);
|
|
3353
|
+
solid(cap(b, d1), 1);
|
|
3354
|
+
}
|
|
3355
|
+
for (let i = 0; i < segCount; i++) {
|
|
3356
|
+
const j = (i + 1) % n;
|
|
3357
|
+
const a = pts[i];
|
|
3358
|
+
const b = pts[j];
|
|
3359
|
+
const d = dirs[i];
|
|
3360
|
+
const from = ribs[i].after;
|
|
3361
|
+
const to = ribs[j].before;
|
|
3362
|
+
const ext = scale2(d, OVERLAP);
|
|
3363
|
+
const quad = [from.l, add4(to.l, ext), add4(to.r, ext), from.r];
|
|
3364
|
+
const gid = `${id}-s${counter++}`;
|
|
3365
|
+
const t0 = tAt(i);
|
|
3366
|
+
const t1 = closed && i === segCount - 1 ? 1 : tAt(i + 1);
|
|
3367
|
+
if (mode === "along") {
|
|
3368
|
+
gradients.push({
|
|
3369
|
+
id: gid,
|
|
3370
|
+
type: "linear",
|
|
3371
|
+
attrs: { gradientUnits: "userSpaceOnUse", x1: fmt2(a.x, 4), y1: fmt2(a.y, 4), x2: fmt2(b.x, 4), y2: fmt2(b.y, 4) },
|
|
3372
|
+
stops: toSvgStops2(spanVectorGradientStops(g, t0, t1, expanded))
|
|
3373
|
+
});
|
|
3374
|
+
} else {
|
|
3375
|
+
const mid = scale2(add4(a, b), 0.5);
|
|
3376
|
+
const side = widthProfileAt(profile, (t0 + t1) / 2);
|
|
3377
|
+
const nrm = left(d);
|
|
3378
|
+
const reachL = visibleLeft === null ? width / 2 * side.left : visibleLeft ? width * side.left : 0;
|
|
3379
|
+
const reachR = visibleLeft === null ? width / 2 * side.right : visibleLeft ? 0 : width * side.right;
|
|
3380
|
+
const p0 = add4(mid, scale2(nrm, reachL));
|
|
3381
|
+
const p1 = sub4(mid, scale2(nrm, reachR));
|
|
3382
|
+
gradients.push({
|
|
3383
|
+
id: gid,
|
|
3384
|
+
type: "linear",
|
|
3385
|
+
attrs: { gradientUnits: "userSpaceOnUse", x1: fmt2(p0.x, 4), y1: fmt2(p0.y, 4), x2: fmt2(p1.x, 4), y2: fmt2(p1.y, 4) },
|
|
3386
|
+
stops: toSvgStops2(expanded)
|
|
3387
|
+
});
|
|
3388
|
+
}
|
|
3389
|
+
slices.push({ d: polygonD2(quad, precision), fill: `url(#${gid})` });
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
const def = {
|
|
3393
|
+
id,
|
|
3394
|
+
type: "pattern",
|
|
3395
|
+
attrs: patternAttrs(area),
|
|
3396
|
+
gradients,
|
|
3397
|
+
tiles: [...patches, ...slices]
|
|
3398
|
+
};
|
|
3399
|
+
return [def];
|
|
3400
|
+
}
|
|
3401
|
+
|
|
2427
3402
|
// src/vector/svg.ts
|
|
2428
3403
|
function vectorCornersAttribute(subpaths, precision = 3) {
|
|
2429
3404
|
const out = [];
|
|
@@ -2448,10 +3423,30 @@ function applyVectorCornersAttribute(subpaths, value) {
|
|
|
2448
3423
|
return subpaths;
|
|
2449
3424
|
}
|
|
2450
3425
|
var escapeAttr = (value) => value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
2451
|
-
function
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
3426
|
+
function vectorPathLocalBounds(node) {
|
|
3427
|
+
const b = bezierPathBounds(subpathsToBezier(node.subpaths));
|
|
3428
|
+
return b ? { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY } : null;
|
|
3429
|
+
}
|
|
3430
|
+
var paintCache = /* @__PURE__ */ new WeakMap();
|
|
3431
|
+
function vectorPaintToSvg(paint, gradientId, options = {}) {
|
|
3432
|
+
if (!hasPaint(paint)) return { value: "none", defs: [] };
|
|
3433
|
+
if (typeof paint === "string") return { value: paint, defs: [] };
|
|
3434
|
+
if (!isVectorGradient(paint)) return { value: "none", defs: [] };
|
|
3435
|
+
const role = options.role ?? "fill";
|
|
3436
|
+
const precision = options.precision ?? 3;
|
|
3437
|
+
const key = `${gradientId}|${role}|${precision}`;
|
|
3438
|
+
const owner = options.node ?? paint;
|
|
3439
|
+
const cached = paintCache.get(owner)?.get(key);
|
|
3440
|
+
if (cached && (!options.node || (role === "fill" ? options.node.fill : options.node.stroke) === paint)) return { value: `url(#${gradientId})`, defs: cached };
|
|
3441
|
+
const node = options.node;
|
|
3442
|
+
const bounds = node ? vectorPathLocalBounds(node) : null;
|
|
3443
|
+
const reach = node && role === "stroke" ? vectorStrokeReach(node) + 1 : 1;
|
|
3444
|
+
const area = bounds ? { x: bounds.x - reach, y: bounds.y - reach, width: bounds.width + 2 * reach, height: bounds.height + 2 * reach } : null;
|
|
3445
|
+
const defs = node && role === "stroke" && (paint.strokeMode === "along" || paint.strokeMode === "across") ? vectorStrokeGradientDefs(paint, gradientId, node, { bounds, area, precision }) : vectorGradientSvgDefs(paint, gradientId, { bounds, area, precision });
|
|
3446
|
+
let byKey = paintCache.get(owner);
|
|
3447
|
+
if (!byKey) paintCache.set(owner, byKey = /* @__PURE__ */ new Map());
|
|
3448
|
+
byKey.set(key, defs);
|
|
3449
|
+
return { value: `url(#${gradientId})`, defs };
|
|
2455
3450
|
}
|
|
2456
3451
|
var partsCache = /* @__PURE__ */ new WeakMap();
|
|
2457
3452
|
function vectorStrokeSvgParts(node, clipId, precision = 3) {
|
|
@@ -2490,18 +3485,26 @@ function widthProfileFromAttribute(value) {
|
|
|
2490
3485
|
if (!value) return [];
|
|
2491
3486
|
return sanitizeWidthProfile(
|
|
2492
3487
|
value.split(";").map((part) => {
|
|
2493
|
-
const [t,
|
|
2494
|
-
return { t, left, right: Number.isFinite(right) ? right :
|
|
3488
|
+
const [t, left2, right] = part.split(",").map((n) => parseFloat(n));
|
|
3489
|
+
return { t, left: left2, right: Number.isFinite(right) ? right : left2 };
|
|
2495
3490
|
})
|
|
2496
3491
|
);
|
|
2497
3492
|
}
|
|
2498
3493
|
var round2 = (n) => Math.round(n * 100) / 100;
|
|
2499
|
-
function gradientMarkup(def) {
|
|
3494
|
+
function gradientMarkup(def, extra = "") {
|
|
2500
3495
|
const tag = def.type === "radial" ? "radialGradient" : "linearGradient";
|
|
2501
3496
|
const attrs = Object.entries(def.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
|
|
2502
3497
|
const stops = def.stops.map((s) => `<stop offset="${s.offset}" stop-color="${escapeAttr(s.stopColor)}"${s.stopOpacity !== void 0 ? ` stop-opacity="${s.stopOpacity}"` : ""}/>`).join("");
|
|
2503
|
-
return `<${tag} id="${escapeAttr(def.id)}"${attrs}>${stops}</${tag}>`;
|
|
3498
|
+
return `<${tag} id="${escapeAttr(def.id)}"${attrs}${extra}>${stops}</${tag}>`;
|
|
3499
|
+
}
|
|
3500
|
+
function paintDefMarkup(def, gradient) {
|
|
3501
|
+
const extra = gradient ? ` data-vector-gradient="${escapeAttr(JSON.stringify(gradient))}"` : "";
|
|
3502
|
+
if (def.type !== "pattern") return gradientMarkup(def, extra);
|
|
3503
|
+
const attrs = Object.entries(def.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
|
|
3504
|
+
const inner = def.gradients.map((g) => gradientMarkup(g)).join("") + def.tiles.map((t) => `<path d="${t.d}" fill="${escapeAttr(t.fill)}"/>`).join("");
|
|
3505
|
+
return `<pattern id="${escapeAttr(def.id)}"${attrs}${extra}>${inner}</pattern>`;
|
|
2504
3506
|
}
|
|
3507
|
+
var paintMarkup = (paint, defs) => defs.map((d, i) => paintDefMarkup(d, i === 0 && isVectorGradient(paint) ? paint : void 0));
|
|
2505
3508
|
function blendFromAttribute(value, m) {
|
|
2506
3509
|
if (!value) return null;
|
|
2507
3510
|
let raw;
|
|
@@ -2535,12 +3538,12 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
2535
3538
|
const pretty = options.pretty !== false;
|
|
2536
3539
|
const defs = [];
|
|
2537
3540
|
const lines = [];
|
|
2538
|
-
const
|
|
3541
|
+
const num2 = (n) => formatNumber(n, precision);
|
|
2539
3542
|
const indent = (depth) => pretty ? " ".repeat(depth) : "";
|
|
2540
3543
|
const common = (node) => {
|
|
2541
3544
|
let out2 = ` id="${escapeAttr(node.id)}"`;
|
|
2542
3545
|
if (node.transform) out2 += ` transform="matrix(${node.transform.map((n) => formatNumber(n, 6)).join(" ")})"`;
|
|
2543
|
-
if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${
|
|
3546
|
+
if (node.opacity !== void 0 && node.opacity < 1) out2 += ` opacity="${num2(node.opacity)}"`;
|
|
2544
3547
|
if (node.visible === false) out2 += ` display="none"`;
|
|
2545
3548
|
if (node.blendStep) out2 += ` data-vector-blend-step=""`;
|
|
2546
3549
|
if (node.type === "group" && node.blend) out2 += ` data-vector-blend="${escapeAttr(JSON.stringify(node.blend))}"`;
|
|
@@ -2574,19 +3577,18 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
2574
3577
|
lines.push(`${indent(depth)}</g>`);
|
|
2575
3578
|
return;
|
|
2576
3579
|
}
|
|
2577
|
-
const fill = vectorPaintToSvg(node.fill, `${prefix}fill-${node.id}
|
|
2578
|
-
const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}
|
|
2579
|
-
|
|
2580
|
-
if (stroke.gradient) defs.push(gradientMarkup(stroke.gradient));
|
|
3580
|
+
const fill = vectorPaintToSvg(node.fill, `${prefix}fill-${node.id}`, { node, precision });
|
|
3581
|
+
const stroke = vectorPaintToSvg(node.stroke, `${prefix}stroke-${node.id}`, { node, role: "stroke", precision });
|
|
3582
|
+
defs.push(...paintMarkup(node.fill, fill.defs), ...paintMarkup(node.stroke, stroke.defs));
|
|
2581
3583
|
const d = vectorPathData(node, precision);
|
|
2582
3584
|
const corners2 = vectorCornersAttribute(node.subpaths, precision);
|
|
2583
3585
|
const live = corners2 ? ` data-vector-d="${bezierPathToSvg(subpathsToBezier(node.subpaths, { corners: false }), { precision })}" data-vector-corners="${corners2}"` : "";
|
|
2584
3586
|
const fillAttrs = ` fill="${escapeAttr(fill.value)}"${node.fillRule === "evenodd" ? ` fill-rule="evenodd"` : ""}`;
|
|
2585
3587
|
const lineAttrs = (width) => {
|
|
2586
|
-
let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${
|
|
3588
|
+
let out2 = ` stroke="${escapeAttr(stroke.value)}" stroke-width="${num2(width)}"`;
|
|
2587
3589
|
if (node.strokeLinecap && node.strokeLinecap !== "butt") out2 += ` stroke-linecap="${node.strokeLinecap}"`;
|
|
2588
3590
|
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") out2 += ` stroke-linejoin="${node.strokeLinejoin}"`;
|
|
2589
|
-
if (node.strokeDasharray?.length) out2 += ` stroke-dasharray="${node.strokeDasharray.map(
|
|
3591
|
+
if (node.strokeDasharray?.length) out2 += ` stroke-dasharray="${node.strokeDasharray.map(num2).join(" ")}"`;
|
|
2590
3592
|
return out2;
|
|
2591
3593
|
};
|
|
2592
3594
|
const parts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${prefix}clip-${node.id}`, precision) : void 0;
|
|
@@ -2594,19 +3596,19 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
2594
3596
|
lines.push(`${indent(depth)}<path${common(node)} d="${d}"${fillAttrs}${stroke.value !== "none" ? lineAttrs(node.strokeWidth ?? 1) : ""}${live}/>`);
|
|
2595
3597
|
return;
|
|
2596
3598
|
}
|
|
2597
|
-
let data = ` data-vector-path="" data-vector-stroke-width="${
|
|
3599
|
+
let data = ` data-vector-path="" data-vector-stroke-width="${num2(node.strokeWidth ?? 1)}"`;
|
|
2598
3600
|
if (parts.align !== "center") data += ` data-vector-stroke-align="${parts.align}"`;
|
|
2599
3601
|
if (node.startArrow) data += ` data-vector-start-arrow="${node.startArrow}"`;
|
|
2600
3602
|
if (node.endArrow) data += ` data-vector-end-arrow="${node.endArrow}"`;
|
|
2601
3603
|
if (node.arrowAlign && node.arrowAlign !== "extend") data += ` data-vector-arrow-align="${node.arrowAlign}"`;
|
|
2602
|
-
if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${
|
|
2603
|
-
if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${
|
|
3604
|
+
if (node.startArrowScale !== void 0 && node.startArrowScale !== 1) data += ` data-vector-start-arrow-scale="${num2(node.startArrowScale)}"`;
|
|
3605
|
+
if (node.endArrowScale !== void 0 && node.endArrowScale !== 1) data += ` data-vector-end-arrow-scale="${num2(node.endArrowScale)}"`;
|
|
2604
3606
|
const profile = strokeWidthProfileOf(node);
|
|
2605
3607
|
if (profile) {
|
|
2606
3608
|
data += ` data-vector-width-profile="${widthProfileToAttribute(profile)}"`;
|
|
2607
3609
|
if (node.strokeLinecap && node.strokeLinecap !== "butt") data += ` data-vector-stroke-linecap="${node.strokeLinecap}"`;
|
|
2608
3610
|
if (node.strokeLinejoin && node.strokeLinejoin !== "miter") data += ` data-vector-stroke-linejoin="${node.strokeLinejoin}"`;
|
|
2609
|
-
if (node.strokeDasharray?.length) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(
|
|
3611
|
+
if (node.strokeDasharray?.length) data += ` data-vector-stroke-dasharray="${node.strokeDasharray.map(num2).join(" ")}"`;
|
|
2610
3612
|
}
|
|
2611
3613
|
lines.push(`${indent(depth)}<g${common(node)}${data}>`);
|
|
2612
3614
|
lines.push(`${indent(depth + 1)}<path data-vector-part="fill" d="${d}"${fillAttrs}${live}/>`);
|
|
@@ -2623,7 +3625,7 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
2623
3625
|
clip = ` clip-path="url(#${id})"`;
|
|
2624
3626
|
} else {
|
|
2625
3627
|
const b = parts.clip.bounds;
|
|
2626
|
-
const box = `x="${
|
|
3628
|
+
const box = `x="${num2(b.x)}" y="${num2(b.y)}" width="${num2(b.width)}" height="${num2(b.height)}"`;
|
|
2627
3629
|
defs.push(`<mask id="${id}" maskUnits="userSpaceOnUse" ${box}><rect ${box} fill="#fff"/><path d="${d}" fill="#000" fill-rule="${rule}"/></mask>`);
|
|
2628
3630
|
clip = ` mask="url(#${id})"`;
|
|
2629
3631
|
}
|
|
@@ -2635,8 +3637,8 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
2635
3637
|
lines.push(`${indent(depth)}</g>`);
|
|
2636
3638
|
};
|
|
2637
3639
|
for (const node of doc.nodes) emit2(node, 1);
|
|
2638
|
-
const w =
|
|
2639
|
-
const h =
|
|
3640
|
+
const w = num2(doc.width);
|
|
3641
|
+
const h = num2(doc.height);
|
|
2640
3642
|
const nl = pretty ? "\n" : "";
|
|
2641
3643
|
const out = [`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`];
|
|
2642
3644
|
if (defs.length) out.push(`${indent(1)}<defs>${defs.join("")}</defs>`);
|
|
@@ -2766,28 +3768,89 @@ var fraction = (s, fallback) => {
|
|
|
2766
3768
|
if (!Number.isFinite(n)) return fallback;
|
|
2767
3769
|
return s.trim().endsWith("%") ? n / 100 : n;
|
|
2768
3770
|
};
|
|
3771
|
+
var PAINT_SERVERS = /* @__PURE__ */ new Set(["linearGradient", "radialGradient", "pattern"]);
|
|
2769
3772
|
function collectGradients(el, raw) {
|
|
2770
|
-
if ((el.name
|
|
3773
|
+
if (PAINT_SERVERS.has(el.name) && el.attrs.id) raw.set(el.attrs.id, el);
|
|
2771
3774
|
for (const c of el.children) collectGradients(c, raw);
|
|
2772
3775
|
}
|
|
2773
|
-
function
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
if (!stops.length && href?.startsWith("#")) stops = resolveGradient(href.slice(1), raw, seen)?.stops ?? [];
|
|
2780
|
-
if (!stops.length) return null;
|
|
2781
|
-
if (el.name === "radialGradient") {
|
|
2782
|
-
return { type: "radial", cx: fraction(el.attrs.cx, 0.5), cy: fraction(el.attrs.cy, 0.5), r: fraction(el.attrs.r, 0.5), stops };
|
|
3776
|
+
function gradientFromAttribute(value) {
|
|
3777
|
+
if (!value) return null;
|
|
3778
|
+
try {
|
|
3779
|
+
return sanitizeVectorGradient(JSON.parse(value));
|
|
3780
|
+
} catch {
|
|
3781
|
+
return null;
|
|
2783
3782
|
}
|
|
2784
|
-
const x1 = fraction(el.attrs.x1, 0);
|
|
2785
|
-
const y1 = fraction(el.attrs.y1, 0);
|
|
2786
|
-
const x2 = fraction(el.attrs.x2, 1);
|
|
2787
|
-
const y2 = fraction(el.attrs.y2, 0);
|
|
2788
|
-
const angle = Math.round((Math.atan2(x2 - x1, -(y2 - y1)) * 180 / Math.PI + 360) % 360);
|
|
2789
|
-
return { type: "linear", angle, stops };
|
|
2790
3783
|
}
|
|
3784
|
+
function resolveGradient(id, raw) {
|
|
3785
|
+
const first = raw.get(id);
|
|
3786
|
+
if (!first) return null;
|
|
3787
|
+
const own = gradientFromAttribute(first.attrs["data-vector-gradient"]);
|
|
3788
|
+
if (own) return own;
|
|
3789
|
+
if (first.name === "pattern") return null;
|
|
3790
|
+
const chain = [];
|
|
3791
|
+
for (let el = first; el && !chain.includes(el) && chain.length < 32; ) {
|
|
3792
|
+
chain.push(el);
|
|
3793
|
+
const href = el.attrs.href ?? el.attrs["xlink:href"];
|
|
3794
|
+
el = href?.startsWith("#") ? raw.get(href.slice(1)) : void 0;
|
|
3795
|
+
}
|
|
3796
|
+
const attr = (name) => chain.find((el) => el.attrs[name] !== void 0)?.attrs[name];
|
|
3797
|
+
const stops = chain.map(readStops).find((s) => s.length) ?? [];
|
|
3798
|
+
if (!stops.length) return null;
|
|
3799
|
+
const user = attr("gradientUnits") === "userSpaceOnUse";
|
|
3800
|
+
const t = parseSvgTransform(attr("gradientTransform"));
|
|
3801
|
+
const plain3 = t === IDENTITY_MATRIX || t[0] === 1 && t[1] === 0 && t[2] === 0 && t[3] === 1 && t[4] === 0 && t[5] === 0;
|
|
3802
|
+
const at2 = (name, fallback) => user ? length(attr(name), fallback) : fraction(attr(name), fallback);
|
|
3803
|
+
const spreadAttr = attr("spreadMethod");
|
|
3804
|
+
const spread = spreadAttr === "reflect" || spreadAttr === "repeat" ? { spread: spreadAttr } : {};
|
|
3805
|
+
const map = (p) => applySvgMatrix(t, p);
|
|
3806
|
+
if (first.name === "radialGradient") {
|
|
3807
|
+
const cx = at2("cx", user ? 0 : 0.5);
|
|
3808
|
+
const cy = at2("cy", user ? 0 : 0.5);
|
|
3809
|
+
const r = at2("r", user ? 0 : 0.5);
|
|
3810
|
+
const fx = attr("fx") !== void 0 ? at2("fx", cx) : cx;
|
|
3811
|
+
const fy = attr("fy") !== void 0 ? at2("fy", cy) : cy;
|
|
3812
|
+
const fr = attr("fr") !== void 0 ? at2("fr", 0) : 0;
|
|
3813
|
+
const focal = Math.abs(fx - cx) > 1e-9 || Math.abs(fy - cy) > 1e-9;
|
|
3814
|
+
if (!user && plain3 && !focal && !(fr > 0)) return { type: "radial", cx, cy, r, stops, ...spread };
|
|
3815
|
+
const g = {
|
|
3816
|
+
type: "radial",
|
|
3817
|
+
stops,
|
|
3818
|
+
start: map({ x: cx, y: cy }),
|
|
3819
|
+
end: map({ x: cx + r, y: cy }),
|
|
3820
|
+
width: map({ x: cx, y: cy + r }),
|
|
3821
|
+
units: user ? "userSpaceOnUse" : "objectBoundingBox",
|
|
3822
|
+
...spread
|
|
3823
|
+
};
|
|
3824
|
+
if (focal) g.focal = map({ x: fx, y: fy });
|
|
3825
|
+
if (fr > 0 && r > 0) g.focalRadius = Math.min(0.99, fr / r);
|
|
3826
|
+
return g;
|
|
3827
|
+
}
|
|
3828
|
+
const x1 = at2("x1", 0);
|
|
3829
|
+
const y1 = at2("y1", 0);
|
|
3830
|
+
const x2 = attr("x2") !== void 0 ? at2("x2", 1) : user ? 0 : 1;
|
|
3831
|
+
const y2 = at2("y2", 0);
|
|
3832
|
+
if (!user && plain3) {
|
|
3833
|
+
const angle = Math.round((Math.atan2(x2 - x1, -(y2 - y1)) * 180 / Math.PI + 360) % 360);
|
|
3834
|
+
const c = angleToSvgLinearCoords(angle);
|
|
3835
|
+
const len2 = Math.hypot(x2 - x1, y2 - y1);
|
|
3836
|
+
const d = len2 > 0 ? { x: (x2 - x1) / len2, y: (y2 - y1) / len2 } : { x: 0, y: 1 };
|
|
3837
|
+
const along2 = (x, y) => x * d.x + y * d.y;
|
|
3838
|
+
const same = len2 > 0 && Math.abs((c.x2 - c.x1) * d.y - (c.y2 - c.y1) * d.x) < 1e-3 && Math.abs(along2(c.x1, c.y1) - along2(x1, y1)) < 1e-3 && Math.abs(along2(c.x2, c.y2) - along2(x2, y2)) < 1e-3;
|
|
3839
|
+
if (same) return { type: "linear", angle, stops, ...spread };
|
|
3840
|
+
}
|
|
3841
|
+
const start = { x: x1, y: y1 };
|
|
3842
|
+
const end = { x: x2, y: y2 };
|
|
3843
|
+
return {
|
|
3844
|
+
type: "linear",
|
|
3845
|
+
stops,
|
|
3846
|
+
start: map(start),
|
|
3847
|
+
end: map(end),
|
|
3848
|
+
width: map({ x: x1 - (y2 - y1), y: y1 + (x2 - x1) }),
|
|
3849
|
+
units: user ? "userSpaceOnUse" : "objectBoundingBox",
|
|
3850
|
+
...spread
|
|
3851
|
+
};
|
|
3852
|
+
}
|
|
3853
|
+
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] });
|
|
2791
3854
|
function parseVectorSvg(svg, options = {}) {
|
|
2792
3855
|
const root = parseXml(svg);
|
|
2793
3856
|
if (!root) throw new Error("The markup has no <svg> element.");
|
|
@@ -2888,7 +3951,7 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
2888
3951
|
const ss = strokeEl ? readStyle(strokeEl, style) : null;
|
|
2889
3952
|
const width2 = length(a["data-vector-stroke-width"], ss ? length(ss.strokeWidth, 1) : 1);
|
|
2890
3953
|
const arrow = (v2) => VECTOR_ARROWHEADS.includes(v2) ? v2 : void 0;
|
|
2891
|
-
const
|
|
3954
|
+
const scale3 = (v2) => {
|
|
2892
3955
|
const n = v2 === void 0 ? NaN : parseFloat(v2);
|
|
2893
3956
|
return n > 0 ? n : void 0;
|
|
2894
3957
|
};
|
|
@@ -2912,8 +3975,8 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
2912
3975
|
...arrow(a["data-vector-start-arrow"]) ? { startArrow: arrow(a["data-vector-start-arrow"]) } : {},
|
|
2913
3976
|
...arrow(a["data-vector-end-arrow"]) ? { endArrow: arrow(a["data-vector-end-arrow"]) } : {},
|
|
2914
3977
|
...a["data-vector-arrow-align"] === "tip" ? { arrowAlign: "tip" } : {},
|
|
2915
|
-
...
|
|
2916
|
-
...
|
|
3978
|
+
...scale3(a["data-vector-start-arrow-scale"]) ? { startArrowScale: scale3(a["data-vector-start-arrow-scale"]) } : {},
|
|
3979
|
+
...scale3(a["data-vector-end-arrow-scale"]) ? { endArrowScale: scale3(a["data-vector-end-arrow-scale"]) } : {},
|
|
2917
3980
|
...profile.length ? { strokeWidthProfile: profile } : {},
|
|
2918
3981
|
...keptCap ? { strokeLinecap: keptCap } : {},
|
|
2919
3982
|
...keptJoin ? { strokeLinejoin: keptJoin } : {},
|
|
@@ -3006,6 +4069,72 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
3006
4069
|
return { width, height, nodes };
|
|
3007
4070
|
}
|
|
3008
4071
|
|
|
4072
|
+
// src/vector/gradientTool.ts
|
|
4073
|
+
var VECTOR_GRADIENT_STOP_GAP = 12;
|
|
4074
|
+
var VECTOR_GRADIENT_MIDPOINT_GAP = 9;
|
|
4075
|
+
var VECTOR_GRADIENT_OFF_BAR = 24;
|
|
4076
|
+
var STOP_HIT = 6;
|
|
4077
|
+
var MIDPOINT_HIT = 5;
|
|
4078
|
+
var HANDLE_HIT = 6;
|
|
4079
|
+
var BAR_HIT = 5;
|
|
4080
|
+
var fmt3 = (n) => Math.round(n * 100) / 100;
|
|
4081
|
+
var apply2 = (m, p) => ({ x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] });
|
|
4082
|
+
function vectorGradientTargetPath(doc, selection, target) {
|
|
4083
|
+
for (const id of selection) {
|
|
4084
|
+
const node = findVectorNode(doc.nodes, id);
|
|
4085
|
+
if (!node) continue;
|
|
4086
|
+
const path = collectPathNodes(node).find((p) => isVectorGradient(p[target]));
|
|
4087
|
+
if (path) return path;
|
|
4088
|
+
}
|
|
4089
|
+
return null;
|
|
4090
|
+
}
|
|
4091
|
+
function getVectorGradientAnnotator(doc, selection, viewport, target) {
|
|
4092
|
+
const path = vectorGradientTargetPath(doc, selection, target);
|
|
4093
|
+
if (!path) return null;
|
|
4094
|
+
const gradient = path[target];
|
|
4095
|
+
const world = vectorWorldMatrix(doc.nodes, path.id) ?? IDENTITY_MATRIX;
|
|
4096
|
+
const screen = multiplyMatrices(world, [viewport.zoom, 0, 0, viewport.zoom, viewport.x, viewport.y]);
|
|
4097
|
+
const bounds = vectorPathLocalBounds(path);
|
|
4098
|
+
const h = vectorGradientHandles(gradient, bounds, screen);
|
|
4099
|
+
const v2 = { x: h.end.x - h.start.x, y: h.end.y - h.start.y };
|
|
4100
|
+
const len2 = Math.hypot(v2.x, v2.y);
|
|
4101
|
+
const normal = len2 > 1e-9 ? { x: -v2.y / len2, y: v2.x / len2 } : { x: 0, y: 1 };
|
|
4102
|
+
const at2 = (t, gap) => ({ x: h.start.x + v2.x * t + normal.x * gap, y: h.start.y + v2.y * t + normal.y * gap });
|
|
4103
|
+
const sorted = sortedVectorGradientStops(gradient);
|
|
4104
|
+
const stops = sorted.map((s, index) => ({ ...s, index, ...at2(s.offset, VECTOR_GRADIENT_STOP_GAP) }));
|
|
4105
|
+
const midpoints = sorted.slice(0, -1).flatMap((s, index) => {
|
|
4106
|
+
const pos = vectorGradientMidpointPosition(sorted, index);
|
|
4107
|
+
return pos === null || sorted[index + 1].offset - s.offset < 1e-6 ? [] : [{ index, midpoint: s.midpoint ?? 0.5, ...at2(pos, -VECTOR_GRADIENT_MIDPOINT_GAP) }];
|
|
4108
|
+
});
|
|
4109
|
+
let extent = null;
|
|
4110
|
+
if (isCentredVectorGradient(gradient.type)) {
|
|
4111
|
+
const frame2 = multiplyMatrices(vectorGradientMatrix(gradient, bounds), screen);
|
|
4112
|
+
const pts = gradient.type === "diamond" ? [{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 0, y: -1 }] : Array.from({ length: 48 }, (_, k) => ({ x: Math.cos(k * Math.PI / 24), y: Math.sin(k * Math.PI / 24) }));
|
|
4113
|
+
extent = `M${pts.map((p) => apply2(frame2, p)).map((p) => `${fmt3(p.x)} ${fmt3(p.y)}`).join("L")}Z`;
|
|
4114
|
+
}
|
|
4115
|
+
return { nodeId: path.id, target, gradient, bounds, world, start: h.start, end: h.end, aspect: h.aspect, focal: h.focal, normal, stops, midpoints, extent };
|
|
4116
|
+
}
|
|
4117
|
+
function vectorGradientBarPosition(a, p) {
|
|
4118
|
+
const v2 = { x: a.end.x - a.start.x, y: a.end.y - a.start.y };
|
|
4119
|
+
const l2 = v2.x * v2.x + v2.y * v2.y;
|
|
4120
|
+
const d = { x: p.x - a.start.x, y: p.y - a.start.y };
|
|
4121
|
+
if (l2 < 1e-12) return { offset: 0, across: Math.hypot(d.x, d.y) };
|
|
4122
|
+
return { offset: (d.x * v2.x + d.y * v2.y) / l2, across: d.x * a.normal.x + d.y * a.normal.y };
|
|
4123
|
+
}
|
|
4124
|
+
function hitVectorGradientAnnotator(a, p) {
|
|
4125
|
+
const near2 = (q, r) => !!q && Math.hypot(q.x - p.x, q.y - p.y) <= r;
|
|
4126
|
+
for (let i = a.stops.length - 1; i >= 0; i--) if (near2(a.stops[i], STOP_HIT)) return { kind: "stop", index: a.stops[i].index };
|
|
4127
|
+
for (const m of a.midpoints) if (near2(m, MIDPOINT_HIT)) return { kind: "midpoint", index: m.index };
|
|
4128
|
+
const centred = isCentredVectorGradient(a.gradient.type);
|
|
4129
|
+
if (near2(a.end, HANDLE_HIT)) return { kind: "handle", handle: centred ? "radius" : "end" };
|
|
4130
|
+
if (a.focal && near2(a.focal, HANDLE_HIT) && !near2(a.start, 1)) return { kind: "handle", handle: "focal" };
|
|
4131
|
+
if (near2(a.start, HANDLE_HIT)) return { kind: "handle", handle: "start" };
|
|
4132
|
+
if (near2(a.aspect, HANDLE_HIT)) return { kind: "handle", handle: "aspect" };
|
|
4133
|
+
const { offset, across } = vectorGradientBarPosition(a, p);
|
|
4134
|
+
if (offset >= 0 && offset <= 1 && across >= -BAR_HIT && across <= VECTOR_GRADIENT_STOP_GAP + STOP_HIT) return { kind: "bar", offset };
|
|
4135
|
+
return null;
|
|
4136
|
+
}
|
|
4137
|
+
|
|
3009
4138
|
// src/vector/blend.ts
|
|
3010
4139
|
var lerpN = (a, b, t) => a + (b - a) * t;
|
|
3011
4140
|
var lerpP = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
@@ -3097,10 +4226,10 @@ function refineMorphContour(c, segments) {
|
|
|
3097
4226
|
const extra = segments - have;
|
|
3098
4227
|
const want = lens.map((l) => sum > 1e-12 ? l / sum * extra : extra / have);
|
|
3099
4228
|
const parts = want.map((w) => 1 + Math.floor(w));
|
|
3100
|
-
let
|
|
4229
|
+
let left2 = segments - parts.reduce((a, b) => a + b, 0);
|
|
3101
4230
|
const frac = want.map((w) => w - Math.floor(w));
|
|
3102
4231
|
const order = parts.map((_, i) => i).sort((a, b) => Math.abs(frac[b] - frac[a]) > 1e-9 ? frac[b] - frac[a] : a - b);
|
|
3103
|
-
for (let i = 0;
|
|
4232
|
+
for (let i = 0; left2 > 0; i = (i + 1) % order.length, left2--) parts[order[i]]++;
|
|
3104
4233
|
const pts = [c.pts[0]];
|
|
3105
4234
|
const anchors = [0];
|
|
3106
4235
|
let at2 = 0;
|
|
@@ -3209,19 +4338,19 @@ function bestStart(fixed, free, useShapeContext) {
|
|
|
3209
4338
|
const gsc = useShapeContext ? shapeContexts(ga, free.closed) : null;
|
|
3210
4339
|
const m = Math.max(1, fn.length);
|
|
3211
4340
|
const cost = (s) => {
|
|
3212
|
-
let
|
|
4341
|
+
let dot3 = 0;
|
|
3213
4342
|
let cross2 = 0;
|
|
3214
4343
|
let sc = 0;
|
|
3215
4344
|
for (let i = 0; i < fn.length; i++) {
|
|
3216
4345
|
const j = free.closed ? (i + s) % n : i;
|
|
3217
4346
|
const p = fn[i];
|
|
3218
4347
|
const q = gn[j];
|
|
3219
|
-
|
|
4348
|
+
dot3 += p[0] * q[0] + p[1] * q[1];
|
|
3220
4349
|
cross2 += p[0] * q[1] - p[1] * q[0];
|
|
3221
4350
|
if (fsc && gsc) sc += chi2(fsc[i], gsc[j]);
|
|
3222
4351
|
}
|
|
3223
|
-
const residual = Math.max(0, 1 - (
|
|
3224
|
-
const turn = 1 - Math.cos(Math.atan2(cross2,
|
|
4352
|
+
const residual = Math.max(0, 1 - (dot3 * dot3 + cross2 * cross2) / (m * m));
|
|
4353
|
+
const turn = 1 - Math.cos(Math.atan2(cross2, dot3));
|
|
3225
4354
|
return residual + TURN_WEIGHT * turn + sc / m;
|
|
3226
4355
|
};
|
|
3227
4356
|
if (!free.closed) return { start: 0, cost: cost(0) };
|
|
@@ -3366,16 +4495,16 @@ function procrustes(a, b) {
|
|
|
3366
4495
|
const ca = frame(pa).c;
|
|
3367
4496
|
const cb = frame(pb).c;
|
|
3368
4497
|
let ss = 0;
|
|
3369
|
-
let
|
|
4498
|
+
let dot3 = 0;
|
|
3370
4499
|
let cross2 = 0;
|
|
3371
4500
|
for (let i = 0; i < Math.min(pa.length, pb.length); i++) {
|
|
3372
4501
|
const p = [pa[i][0] - ca[0], pa[i][1] - ca[1]];
|
|
3373
4502
|
const q = [pb[i][0] - cb[0], pb[i][1] - cb[1]];
|
|
3374
4503
|
ss += p[0] * p[0] + p[1] * p[1];
|
|
3375
|
-
|
|
4504
|
+
dot3 += p[0] * q[0] + p[1] * q[1];
|
|
3376
4505
|
cross2 += p[0] * q[1] - p[1] * q[0];
|
|
3377
4506
|
}
|
|
3378
|
-
return { ca, cb, theta: Math.atan2(cross2,
|
|
4507
|
+
return { ca, cb, theta: Math.atan2(cross2, dot3), s: ss > 1e-12 ? Math.hypot(dot3, cross2) / ss : 1 };
|
|
3379
4508
|
}
|
|
3380
4509
|
function interpolateMorphContours(a, b, u, options = {}) {
|
|
3381
4510
|
const w = options.linear ? 0 : Math.min(1, Math.max(0, options.rigidity ?? 1));
|
|
@@ -3429,39 +4558,24 @@ function interpolateVectorColor(a, b, t) {
|
|
|
3429
4558
|
if (t >= 1) return b;
|
|
3430
4559
|
return mixColorsOklab(a, b, t) ?? (t < 0.5 ? a : b);
|
|
3431
4560
|
}
|
|
3432
|
-
function sortedStops(g) {
|
|
3433
|
-
return g.stops.slice().sort((x, y) => x.offset - y.offset);
|
|
3434
|
-
}
|
|
3435
|
-
function sampleGradient(stops, offset) {
|
|
3436
|
-
if (!stops.length) return { color: "transparent", opacity: 1 };
|
|
3437
|
-
const first = stops[0];
|
|
3438
|
-
const last = stops[stops.length - 1];
|
|
3439
|
-
if (offset <= first.offset) return { color: first.color, opacity: first.opacity ?? 1 };
|
|
3440
|
-
if (offset >= last.offset) return { color: last.color, opacity: last.opacity ?? 1 };
|
|
3441
|
-
let i = 0;
|
|
3442
|
-
while (i + 1 < stops.length && stops[i + 1].offset < offset) i++;
|
|
3443
|
-
const x = stops[i];
|
|
3444
|
-
const y = stops[i + 1];
|
|
3445
|
-
const u = y.offset - x.offset > 1e-12 ? (offset - x.offset) / (y.offset - x.offset) : 1;
|
|
3446
|
-
return { color: interpolateVectorColor(x.color, y.color, u), opacity: lerpN(x.opacity ?? 1, y.opacity ?? 1, u) };
|
|
3447
|
-
}
|
|
3448
4561
|
function resampleGradientStops(g, offsets) {
|
|
3449
|
-
const stops = sortedStops(g);
|
|
3450
4562
|
return offsets.map((offset) => {
|
|
3451
|
-
const s =
|
|
3452
|
-
return s.opacity < 1 ? { offset, color: s.color, opacity: s.opacity } : { offset, color: s.color };
|
|
4563
|
+
const s = sampleVectorGradient(g, offset);
|
|
4564
|
+
return s.opacity < 1 ? { offset, color: s.color, opacity: Math.round(s.opacity * 1e4) / 1e4 } : { offset, color: s.color };
|
|
3453
4565
|
});
|
|
3454
4566
|
}
|
|
3455
4567
|
function lerpAngle(a, b, t) {
|
|
3456
4568
|
const d = ((b - a + 180) % 360 + 360) % 360 - 180;
|
|
3457
4569
|
return a + d * t;
|
|
3458
4570
|
}
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
let
|
|
3463
|
-
|
|
3464
|
-
|
|
4571
|
+
var lerpPoint = (a, b, t) => ({ x: lerpN(a.x, b.x, t), y: lerpN(a.y, b.y, t) });
|
|
4572
|
+
var sameColourModel = (a, b) => (a.interpolation ?? "srgb") === (b.interpolation ?? "srgb") && (a.smoothness ?? 0) === (b.smoothness ?? 0);
|
|
4573
|
+
function lerpStops(a, b, t) {
|
|
4574
|
+
let stopsA = sortedVectorGradientStops(a);
|
|
4575
|
+
let stopsB = sortedVectorGradientStops(b);
|
|
4576
|
+
const resampled = stopsA.length !== stopsB.length || !sameColourModel(a, b);
|
|
4577
|
+
if (resampled) {
|
|
4578
|
+
const offs = [...expandVectorGradientStops(a), ...expandVectorGradientStops(b), ...stopsA, ...stopsB].map((s) => s.offset).filter(Number.isFinite).sort((x, y) => x - y);
|
|
3465
4579
|
const common = offs.filter((o, i) => i === 0 || o - offs[i - 1] > 1e-4);
|
|
3466
4580
|
stopsA = resampleGradientStops(a, common);
|
|
3467
4581
|
stopsB = resampleGradientStops(b, common);
|
|
@@ -3471,12 +4585,53 @@ function lerpGradient(a, b, t) {
|
|
|
3471
4585
|
const opacity = lerpN(x.opacity ?? 1, y.opacity ?? 1, t);
|
|
3472
4586
|
const stop = { offset: lerpN(x.offset, y.offset, t), color: interpolateVectorColor(x.color, y.color, t) };
|
|
3473
4587
|
if (opacity < 1) stop.opacity = opacity;
|
|
4588
|
+
const mid = lerpN(x.midpoint ?? 0.5, y.midpoint ?? 0.5, t);
|
|
4589
|
+
if (Math.abs(mid - 0.5) > 1e-6) stop.midpoint = mid;
|
|
3474
4590
|
return stop;
|
|
3475
4591
|
});
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
4592
|
+
return { stops, resampled };
|
|
4593
|
+
}
|
|
4594
|
+
function lerpGradient(a, b, t) {
|
|
4595
|
+
if (a.type !== b.type) return null;
|
|
4596
|
+
const { stops, resampled } = lerpStops(a, b, t);
|
|
4597
|
+
const near2 = t < 0.5 ? a : b;
|
|
4598
|
+
const out = { type: a.type, stops };
|
|
4599
|
+
if (!resampled) {
|
|
4600
|
+
if (a.interpolation && a.interpolation !== "srgb") out.interpolation = a.interpolation;
|
|
4601
|
+
if (a.smoothness) out.smoothness = a.smoothness;
|
|
4602
|
+
}
|
|
4603
|
+
if (near2.spread && near2.spread !== "pad") out.spread = near2.spread;
|
|
4604
|
+
if (near2.strokeMode && near2.strokeMode !== "within") out.strokeMode = near2.strokeMode;
|
|
4605
|
+
const ea = hasExplicitGradientGeometry(a);
|
|
4606
|
+
const eb = hasExplicitGradientGeometry(b);
|
|
4607
|
+
if (!ea && !eb) {
|
|
4608
|
+
if (a.angle !== void 0 || b.angle !== void 0 || a.type === "linear" || a.type === "reflected") {
|
|
4609
|
+
const fallback = a.type === "linear" || a.type === "reflected" ? 180 : 90;
|
|
4610
|
+
out.angle = lerpAngle(a.angle ?? fallback, b.angle ?? fallback, t);
|
|
4611
|
+
}
|
|
4612
|
+
if (a.type !== "linear" && a.type !== "reflected") {
|
|
4613
|
+
out.cx = lerpN(a.cx ?? 0.5, b.cx ?? 0.5, t);
|
|
4614
|
+
out.cy = lerpN(a.cy ?? 0.5, b.cy ?? 0.5, t);
|
|
4615
|
+
out.r = lerpN(a.r ?? 0.5, b.r ?? 0.5, t);
|
|
4616
|
+
}
|
|
4617
|
+
return out;
|
|
4618
|
+
}
|
|
4619
|
+
const pa = vectorGradientPoints(a);
|
|
4620
|
+
const pb = vectorGradientPoints(b);
|
|
4621
|
+
if (pa.units !== pb.units) {
|
|
4622
|
+
const src = explicitVectorGradient(near2);
|
|
4623
|
+
return { ...src, stops: out.stops, ...out.interpolation ? { interpolation: out.interpolation } : {}, ...out.smoothness ? { smoothness: out.smoothness } : {} };
|
|
4624
|
+
}
|
|
4625
|
+
out.start = lerpPoint(pa.start, pb.start, t);
|
|
4626
|
+
out.end = lerpPoint(pa.end, pb.end, t);
|
|
4627
|
+
out.width = lerpPoint(pa.width, pb.width, t);
|
|
4628
|
+
out.units = pa.units;
|
|
4629
|
+
if (a.type === "radial") {
|
|
4630
|
+
if (pa.focal || pb.focal) out.focal = lerpPoint(pa.focal ?? pa.start, pb.focal ?? pb.start, t);
|
|
4631
|
+
const fr = lerpN(pa.focalRadius, pb.focalRadius, t);
|
|
4632
|
+
if (fr > 0) out.focalRadius = fr;
|
|
4633
|
+
}
|
|
4634
|
+
return out;
|
|
3480
4635
|
}
|
|
3481
4636
|
function solidLike(color, like) {
|
|
3482
4637
|
return { ...like, stops: like.stops.map((s) => color === null ? { offset: s.offset, color: s.color, opacity: 0 } : { offset: s.offset, color }) };
|
|
@@ -3490,8 +4645,8 @@ function interpolateVectorPaint(a, b, t) {
|
|
|
3490
4645
|
if (typeof a === "string" && pa && typeof b === "string" && pb) return interpolateVectorColor(a, b, t);
|
|
3491
4646
|
if (!pa && typeof b === "string") return interpolateVectorColor("transparent", b, t);
|
|
3492
4647
|
if (!pb && typeof a === "string") return interpolateVectorColor(a, "transparent", t);
|
|
3493
|
-
const ga = pa &&
|
|
3494
|
-
const gb = pb &&
|
|
4648
|
+
const ga = pa && isVectorGradient(a) ? a : null;
|
|
4649
|
+
const gb = pb && isVectorGradient(b) ? b : null;
|
|
3495
4650
|
if (ga && gb) return lerpGradient(ga, gb, t) ?? (t < 0.5 ? a : b);
|
|
3496
4651
|
if (gb) return lerpGradient(solidLike(pa ? a : null, gb), gb, t) ?? b;
|
|
3497
4652
|
if (ga) return lerpGradient(ga, solidLike(pb ? b : null, ga), t) ?? a;
|
|
@@ -3628,15 +4783,15 @@ function createVectorMorph(a, b, options = {}) {
|
|
|
3628
4783
|
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
3629
4784
|
const x = pa[i];
|
|
3630
4785
|
const y = pb[i];
|
|
3631
|
-
const
|
|
4786
|
+
const sub5 = { ...options, id: `${id}-${i}`, starts: void 0 };
|
|
3632
4787
|
if (x && y) {
|
|
3633
|
-
parts.push(preparePathMorph(x.path, y.path,
|
|
4788
|
+
parts.push(preparePathMorph(x.path, y.path, sub5, x.m, y.m));
|
|
3634
4789
|
opacities.push([x.opacity, y.opacity]);
|
|
3635
4790
|
} else if (x) {
|
|
3636
|
-
parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb),
|
|
4791
|
+
parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub5, x.m, IDENTITY_MATRIX));
|
|
3637
4792
|
opacities.push([x.opacity, x.opacity]);
|
|
3638
4793
|
} else if (y) {
|
|
3639
|
-
parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path,
|
|
4794
|
+
parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub5, IDENTITY_MATRIX, y.m));
|
|
3640
4795
|
opacities.push([y.opacity, y.opacity]);
|
|
3641
4796
|
}
|
|
3642
4797
|
}
|
|
@@ -4621,16 +5776,16 @@ function solveOnSegment(seg, base, cond) {
|
|
|
4621
5776
|
return cond([p[0] - b[0], p[1] - b[1]], segmentDerivative(seg, t));
|
|
4622
5777
|
};
|
|
4623
5778
|
const vals = [];
|
|
4624
|
-
let
|
|
5779
|
+
let scale3 = 0;
|
|
4625
5780
|
for (let i = 0; i <= CURVE_SAMPLES; i++) {
|
|
4626
5781
|
const t = i / CURVE_SAMPLES;
|
|
4627
5782
|
const p = segmentPoint(seg, t);
|
|
4628
5783
|
const d = segmentDerivative(seg, t);
|
|
4629
5784
|
const r = [p[0] - b[0], p[1] - b[1]];
|
|
4630
5785
|
vals.push(cond(r, d));
|
|
4631
|
-
|
|
5786
|
+
scale3 = Math.max(scale3, Math.hypot(r[0], r[1]) * Math.hypot(d[0], d[1]));
|
|
4632
5787
|
}
|
|
4633
|
-
if (!(
|
|
5788
|
+
if (!(scale3 > 0) || vals.every((v2) => Math.abs(v2) <= scale3 * 1e-12)) return [];
|
|
4634
5789
|
const out = [];
|
|
4635
5790
|
for (let i = 0; i < CURVE_SAMPLES; i++) {
|
|
4636
5791
|
const ga = vals[i];
|
|
@@ -4664,7 +5819,7 @@ function solveOnSegment(seg, base, cond) {
|
|
|
4664
5819
|
|
|
4665
5820
|
// src/vector/pathEdit.ts
|
|
4666
5821
|
var EPS4 = 1e-9;
|
|
4667
|
-
var
|
|
5822
|
+
var unit4 = (x, y) => {
|
|
4668
5823
|
const l = Math.hypot(x, y);
|
|
4669
5824
|
return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
|
|
4670
5825
|
};
|
|
@@ -4677,14 +5832,14 @@ function cubicOf(sp, i) {
|
|
|
4677
5832
|
var isLine = (sp, i) => subpathSegment(sp, i).length === 2;
|
|
4678
5833
|
function startTangent2(c) {
|
|
4679
5834
|
for (let k = 1; k < 4; k++) {
|
|
4680
|
-
const u =
|
|
5835
|
+
const u = unit4(c[k][0] - c[0][0], c[k][1] - c[0][1]);
|
|
4681
5836
|
if (u) return u;
|
|
4682
5837
|
}
|
|
4683
5838
|
return [1, 0];
|
|
4684
5839
|
}
|
|
4685
5840
|
function endTangent2(c) {
|
|
4686
5841
|
for (let k = 2; k >= 0; k--) {
|
|
4687
|
-
const u =
|
|
5842
|
+
const u = unit4(c[3][0] - c[k][0], c[3][1] - c[k][1]);
|
|
4688
5843
|
if (u) return u;
|
|
4689
5844
|
}
|
|
4690
5845
|
return [1, 0];
|
|
@@ -4748,13 +5903,13 @@ function removeAnchorKeepingShape(sp, index) {
|
|
|
4748
5903
|
const pi = (index + n - 1) % n;
|
|
4749
5904
|
const ni = (index + 1) % n;
|
|
4750
5905
|
if (!(isLine(sp, pi) && isLine(sp, index))) {
|
|
4751
|
-
const
|
|
5906
|
+
const left2 = cubicOf(sp, pi);
|
|
4752
5907
|
const right = cubicOf(sp, index);
|
|
4753
5908
|
const N = 16;
|
|
4754
5909
|
const pts = [];
|
|
4755
|
-
sample(
|
|
5910
|
+
sample(left2, N, pts, true);
|
|
4756
5911
|
sample(right, N, pts, false);
|
|
4757
|
-
const t0 = startTangent2(
|
|
5912
|
+
const t0 = startTangent2(left2);
|
|
4758
5913
|
const t1 = endTangent2(right);
|
|
4759
5914
|
const { curve } = fitAcross(pts, N, t0, t1);
|
|
4760
5915
|
if ([curve.p1, curve.p2].every((p) => Number.isFinite(p[0]) && Number.isFinite(p[1]))) {
|
|
@@ -4956,7 +6111,7 @@ function smoothSubpath(sp, amount = 1, only) {
|
|
|
4956
6111
|
const prev = orig[(i + n - 1) % n];
|
|
4957
6112
|
const next = orig[(i + 1) % n];
|
|
4958
6113
|
const p = orig[i];
|
|
4959
|
-
const dir =
|
|
6114
|
+
const dir = unit4(next.x - prev.x, next.y - prev.y);
|
|
4960
6115
|
if (!dir) continue;
|
|
4961
6116
|
const li = dist2(p, prev) / 3;
|
|
4962
6117
|
const lo = dist2(p, next) / 3;
|
|
@@ -5091,9 +6246,9 @@ function facesOf(path, fillRule) {
|
|
|
5091
6246
|
function splitIntoFaces(path, fillRule = "nonzero") {
|
|
5092
6247
|
return facesOf(path, fillRule).map(faceToPath);
|
|
5093
6248
|
}
|
|
5094
|
-
function rootsInUnit(a, b, c, d,
|
|
6249
|
+
function rootsInUnit(a, b, c, d, scale3) {
|
|
5095
6250
|
const f = (t) => ((a * t + b) * t + c) * t + d;
|
|
5096
|
-
const tol = 1e-12 * Math.max(1,
|
|
6251
|
+
const tol = 1e-12 * Math.max(1, scale3);
|
|
5097
6252
|
const cuts = [0];
|
|
5098
6253
|
const qa = 3 * a;
|
|
5099
6254
|
const qb = 2 * b;
|
|
@@ -5150,9 +6305,9 @@ function lineHits(p, q, seg) {
|
|
|
5150
6305
|
coeffs = [-h0 + 3 * h1 - 3 * h2 + h3, 3 * h0 - 6 * h1 + 3 * h2, -3 * h0 + 3 * h1, h0];
|
|
5151
6306
|
}
|
|
5152
6307
|
if (coeffs.every((c) => Math.abs(c) < 1e-12 * Math.max(1, len2))) return [];
|
|
5153
|
-
const
|
|
6308
|
+
const scale3 = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
|
|
5154
6309
|
const out = [];
|
|
5155
|
-
for (const t of rootsInUnit(...coeffs,
|
|
6310
|
+
for (const t of rootsInUnit(...coeffs, scale3)) {
|
|
5156
6311
|
const point = segmentPoint(seg, t);
|
|
5157
6312
|
const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
|
|
5158
6313
|
if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
|
|
@@ -6098,7 +7253,8 @@ var TOOL_LABELS = {
|
|
|
6098
7253
|
width: "Width",
|
|
6099
7254
|
shapeBuilder: "Shape Builder",
|
|
6100
7255
|
paintBucket: "Paint Bucket",
|
|
6101
|
-
blend: "Blend"
|
|
7256
|
+
blend: "Blend",
|
|
7257
|
+
gradient: "Gradient"
|
|
6102
7258
|
};
|
|
6103
7259
|
var TOOL_KEYS = {
|
|
6104
7260
|
v: "select",
|
|
@@ -6116,7 +7272,8 @@ var TOOL_KEYS = {
|
|
|
6116
7272
|
w: "width",
|
|
6117
7273
|
m: "shapeBuilder",
|
|
6118
7274
|
b: "paintBucket",
|
|
6119
|
-
j: "blend"
|
|
7275
|
+
j: "blend",
|
|
7276
|
+
g: "gradient"
|
|
6120
7277
|
};
|
|
6121
7278
|
function vectorToolLabel(tool) {
|
|
6122
7279
|
return TOOL_LABELS[tool];
|
|
@@ -6137,7 +7294,8 @@ var VECTOR_TOOL_SHORTCUTS = {
|
|
|
6137
7294
|
width: "W",
|
|
6138
7295
|
shapeBuilder: "M",
|
|
6139
7296
|
paintBucket: "B",
|
|
6140
|
-
blend: "J"
|
|
7297
|
+
blend: "J",
|
|
7298
|
+
gradient: "G"
|
|
6141
7299
|
};
|
|
6142
7300
|
var VECTOR_TOOL_GLYPHS = {
|
|
6143
7301
|
select: "\u2196",
|
|
@@ -6155,7 +7313,8 @@ var VECTOR_TOOL_GLYPHS = {
|
|
|
6155
7313
|
width: "\u21F9",
|
|
6156
7314
|
shapeBuilder: "\u2295",
|
|
6157
7315
|
paintBucket: "\u25CD",
|
|
6158
|
-
blend: "\u29D3"
|
|
7316
|
+
blend: "\u29D3",
|
|
7317
|
+
gradient: "\u25D0"
|
|
6159
7318
|
};
|
|
6160
7319
|
var SHAPE_TOOLS = /* @__PURE__ */ new Set(["rect", "ellipse", "polygon", "star", "line"]);
|
|
6161
7320
|
var BOOLEAN_OPS = {
|
|
@@ -6252,6 +7411,7 @@ var VectorEditorController = class {
|
|
|
6252
7411
|
blendStart = null;
|
|
6253
7412
|
/** Options new blends get (the toolbars edit them along with the selected blend's). */
|
|
6254
7413
|
blendDefaults = {};
|
|
7414
|
+
gradientEdit = { target: "fill", stop: null };
|
|
6255
7415
|
/** What the highlight shows, to skip notifying when the pointer stays over the same face. */
|
|
6256
7416
|
builderHoverKey = null;
|
|
6257
7417
|
createId;
|
|
@@ -6276,15 +7436,26 @@ var VectorEditorController = class {
|
|
|
6276
7436
|
this.createId = options.createId ?? defaultCreateId;
|
|
6277
7437
|
this.onChange = options.onChange;
|
|
6278
7438
|
this.state = this.computeState();
|
|
7439
|
+
this.connect();
|
|
7440
|
+
}
|
|
7441
|
+
/** Listens to the history (once; again after `destroy`). */
|
|
7442
|
+
connect() {
|
|
7443
|
+
if (this.cleanups.length) return;
|
|
6279
7444
|
this.cleanups.push(
|
|
6280
7445
|
this.history.subscribe((h) => {
|
|
6281
|
-
|
|
6282
|
-
if (this.applying || s.canUndo === h.canUndo && s.canRedo === h.canRedo && s.undoLabel === h.undoName && s.redoLabel === h.redoName) return;
|
|
6283
|
-
this.notify();
|
|
7446
|
+
if (!this.applying && this.historyDiffers(h)) this.notify();
|
|
6284
7447
|
})
|
|
6285
7448
|
);
|
|
7449
|
+
if (this.historyDiffers(this.history.getState())) this.state = this.computeState();
|
|
6286
7450
|
}
|
|
6287
|
-
|
|
7451
|
+
historyDiffers(h) {
|
|
7452
|
+
const s = this.state;
|
|
7453
|
+
return s.canUndo !== h.canUndo || s.canRedo !== h.canRedo || s.undoLabel !== h.undoName || s.redoLabel !== h.redoName;
|
|
7454
|
+
}
|
|
7455
|
+
/**
|
|
7456
|
+
* Stops listening to the history and drops all listeners. A later `subscribe` reconnects, so a
|
|
7457
|
+
* controller torn down by React StrictMode's double effect keeps working when it is reused.
|
|
7458
|
+
*/
|
|
6288
7459
|
destroy() {
|
|
6289
7460
|
this.cleanups.splice(0).forEach((fn) => fn());
|
|
6290
7461
|
this.listeners.clear();
|
|
@@ -6296,6 +7467,7 @@ var VectorEditorController = class {
|
|
|
6296
7467
|
getState = () => this.state;
|
|
6297
7468
|
/** Calls `listener` after every change. Returns an unsubscribe function. */
|
|
6298
7469
|
subscribe = (listener) => {
|
|
7470
|
+
this.connect();
|
|
6299
7471
|
this.listeners.add(listener);
|
|
6300
7472
|
return () => {
|
|
6301
7473
|
this.listeners.delete(listener);
|
|
@@ -6333,6 +7505,7 @@ var VectorEditorController = class {
|
|
|
6333
7505
|
builder: this.builderPreview,
|
|
6334
7506
|
blendStart: this.blendStart,
|
|
6335
7507
|
blendDefaults: this.blendDefaults,
|
|
7508
|
+
gradientEdit: this.gradientEdit,
|
|
6336
7509
|
canUndo: h.canUndo,
|
|
6337
7510
|
canRedo: h.canRedo,
|
|
6338
7511
|
undoLabel: h.undoName,
|
|
@@ -6406,6 +7579,13 @@ var VectorEditorController = class {
|
|
|
6406
7579
|
return this.builderPreview ? "cell" : "crosshair";
|
|
6407
7580
|
case "blend":
|
|
6408
7581
|
return h?.nodeId ? this.blendStart && this.blendStart.nodeId !== h.nodeId ? "copy" : "pointer" : "crosshair";
|
|
7582
|
+
case "gradient": {
|
|
7583
|
+
const part = this.drag?.kind === "gradient" ? this.drag.part : h?.gradient;
|
|
7584
|
+
if (!part) return "crosshair";
|
|
7585
|
+
if (part.kind === "bar") return this.drag ? "grabbing" : "copy";
|
|
7586
|
+
if (part.kind === "handle" && part.handle === "start") return this.drag ? "grabbing" : "move";
|
|
7587
|
+
return "pointer";
|
|
7588
|
+
}
|
|
6409
7589
|
default:
|
|
6410
7590
|
return "crosshair";
|
|
6411
7591
|
}
|
|
@@ -6831,6 +8011,233 @@ var VectorEditorController = class {
|
|
|
6831
8011
|
this.widthEdit = null;
|
|
6832
8012
|
return this.setStyle({ strokeWidthProfile: points.length ? points : null }, options.ids ?? this.selection, { label: points.length ? "Width Profile" : "Uniform Width" });
|
|
6833
8013
|
}
|
|
8014
|
+
// -------------------------------------------------------------------------
|
|
8015
|
+
// Gradients
|
|
8016
|
+
// -------------------------------------------------------------------------
|
|
8017
|
+
/** Which paint (fill or stroke) the gradient tool and editor work on. */
|
|
8018
|
+
setGradientTarget(target) {
|
|
8019
|
+
if (target === this.gradientEdit.target) return;
|
|
8020
|
+
this.gradientEdit = { target, stop: null };
|
|
8021
|
+
this.notify();
|
|
8022
|
+
}
|
|
8023
|
+
/** Selects a stop of the gradient being edited (index into its sorted stops), or none. */
|
|
8024
|
+
selectGradientStop(index) {
|
|
8025
|
+
if (index === this.gradientEdit.stop) return;
|
|
8026
|
+
this.gradientEdit = { ...this.gradientEdit, stop: index };
|
|
8027
|
+
this.notify();
|
|
8028
|
+
}
|
|
8029
|
+
/** The gradient tool's annotator for the selection (screen pixels), or null when its paint is not a gradient. */
|
|
8030
|
+
getGradientAnnotator() {
|
|
8031
|
+
return getVectorGradientAnnotator(this.doc, this.selection, this.viewport, this.gradientEdit.target);
|
|
8032
|
+
}
|
|
8033
|
+
/** Unlocked paths of `ids` (default: the selection). */
|
|
8034
|
+
gradientPaths(ids = this.selection) {
|
|
8035
|
+
const out = /* @__PURE__ */ new Map();
|
|
8036
|
+
for (const id of ids) {
|
|
8037
|
+
const node = this.getNode(id);
|
|
8038
|
+
if (node && !node.locked) {
|
|
8039
|
+
for (const p of collectPathNodes(node)) if (!p.locked) out.set(p.id, p);
|
|
8040
|
+
}
|
|
8041
|
+
}
|
|
8042
|
+
return [...out.values()];
|
|
8043
|
+
}
|
|
8044
|
+
/**
|
|
8045
|
+
* Edits the gradient of the `target` paint (default: the editor's) of the selected (or given)
|
|
8046
|
+
* paths with `patch`: a colour or no paint becomes a gradient (`patch.type`, default linear,
|
|
8047
|
+
* from the colour to white). Consecutive edits with the same `merge` key are one undo step.
|
|
8048
|
+
*/
|
|
8049
|
+
setGradient(patch, options = {}) {
|
|
8050
|
+
const target = options.target ?? this.gradientEdit.target;
|
|
8051
|
+
const paths = this.gradientPaths(options.ids ?? this.selection);
|
|
8052
|
+
if (!paths.length) return { ok: false, message: "Nothing is selected." };
|
|
8053
|
+
const next = /* @__PURE__ */ new Map();
|
|
8054
|
+
for (const path of paths) {
|
|
8055
|
+
const paint = path[target];
|
|
8056
|
+
const base = isVectorGradient(paint) ? paint : defaultVectorGradient(patch.type ?? "linear", paint);
|
|
8057
|
+
const g = applyVectorGradientPatch(base, patch);
|
|
8058
|
+
if (!g) return { ok: false, message: "That is not a valid gradient: it needs at least one stop with an offset and a colour." };
|
|
8059
|
+
next.set(path.id, { ...path, [target]: g });
|
|
8060
|
+
}
|
|
8061
|
+
this.change(options.label ?? "Gradient", () => this.setDoc(mapTree(this.doc.nodes, (n) => next.get(n.id) ?? n)), options.merge);
|
|
8062
|
+
const stops = next.values().next().value?.[target]?.stops.length ?? 0;
|
|
8063
|
+
if (this.gradientEdit.stop !== null && this.gradientEdit.stop >= stops) this.gradientEdit = { ...this.gradientEdit, stop: null };
|
|
8064
|
+
return { ok: true };
|
|
8065
|
+
}
|
|
8066
|
+
/** Applies `fn` to every target gradient of the selection (paths whose paint is not a gradient are skipped). */
|
|
8067
|
+
editGradients(label, fn, merge) {
|
|
8068
|
+
const target = this.gradientEdit.target;
|
|
8069
|
+
const paths = this.gradientPaths().filter((p) => isVectorGradient(p[target]));
|
|
8070
|
+
if (!paths.length) return { ok: false, message: "The selection has no gradient." };
|
|
8071
|
+
const next = /* @__PURE__ */ new Map();
|
|
8072
|
+
for (const path of paths) {
|
|
8073
|
+
const g = path[target];
|
|
8074
|
+
const stops = fn(sortedVectorGradientStops(g));
|
|
8075
|
+
if (!stops) return { ok: false, message: "A gradient keeps at least two stops." };
|
|
8076
|
+
next.set(path.id, { ...path, [target]: { ...g, stops } });
|
|
8077
|
+
}
|
|
8078
|
+
this.change(label, () => this.setDoc(mapTree(this.doc.nodes, (n) => next.get(n.id) ?? n)), merge);
|
|
8079
|
+
return { ok: true };
|
|
8080
|
+
}
|
|
8081
|
+
/** Changes stop `index` (colour, opacity, offset or midpoint) of the selection's gradients; selects it. */
|
|
8082
|
+
setGradientStop(index, patch, options = {}) {
|
|
8083
|
+
let moved = index;
|
|
8084
|
+
const result = this.editGradients(
|
|
8085
|
+
"Gradient Stop",
|
|
8086
|
+
(stops) => {
|
|
8087
|
+
if (!stops[index]) return stops;
|
|
8088
|
+
let next = stops.map((s, i) => {
|
|
8089
|
+
if (i !== index) return s;
|
|
8090
|
+
const out = { ...s };
|
|
8091
|
+
if (patch.color !== void 0) out.color = patch.color;
|
|
8092
|
+
if (patch.opacity !== void 0 && Number.isFinite(patch.opacity)) {
|
|
8093
|
+
const o = Math.min(1, Math.max(0, patch.opacity));
|
|
8094
|
+
if (o < 1) out.opacity = o;
|
|
8095
|
+
else delete out.opacity;
|
|
8096
|
+
}
|
|
8097
|
+
if (patch.midpoint !== void 0 && Number.isFinite(patch.midpoint)) {
|
|
8098
|
+
const m = Math.min(0.95, Math.max(0.05, patch.midpoint));
|
|
8099
|
+
if (Math.abs(m - 0.5) > 1e-3) out.midpoint = m;
|
|
8100
|
+
else delete out.midpoint;
|
|
8101
|
+
}
|
|
8102
|
+
return out;
|
|
8103
|
+
});
|
|
8104
|
+
if (patch.offset !== void 0 && Number.isFinite(patch.offset)) ({ stops: next, index: moved } = moveVectorGradientStop(next, index, patch.offset));
|
|
8105
|
+
return next;
|
|
8106
|
+
},
|
|
8107
|
+
options.merge
|
|
8108
|
+
);
|
|
8109
|
+
if (result.ok) this.selectGradientStop(moved);
|
|
8110
|
+
return result;
|
|
8111
|
+
}
|
|
8112
|
+
/** Adds a stop at `offset` (painting what the gradient paints there) to the selection's gradients and selects it. */
|
|
8113
|
+
addGradientStop(offset) {
|
|
8114
|
+
let index = 0;
|
|
8115
|
+
const target = this.gradientEdit.target;
|
|
8116
|
+
const result = this.editGradients("Add Gradient Stop", (stops) => {
|
|
8117
|
+
const path = this.gradientPaths().find((p) => isVectorGradient(p[target]));
|
|
8118
|
+
const r = insertVectorGradientStop({ ...path[target], stops }, offset);
|
|
8119
|
+
index = r.index;
|
|
8120
|
+
return r.stops;
|
|
8121
|
+
});
|
|
8122
|
+
if (result.ok) this.selectGradientStop(index);
|
|
8123
|
+
return result;
|
|
8124
|
+
}
|
|
8125
|
+
/** Removes stop `index` of the selection's gradients (never below two stops). */
|
|
8126
|
+
removeGradientStop(index) {
|
|
8127
|
+
const result = this.editGradients("Delete Gradient Stop", (stops) => removeVectorGradientStop(stops, index));
|
|
8128
|
+
if (result.ok) this.selectGradientStop(null);
|
|
8129
|
+
return result;
|
|
8130
|
+
}
|
|
8131
|
+
/** Reverses the selection's gradients (stops and midpoints). */
|
|
8132
|
+
reverseGradient() {
|
|
8133
|
+
return this.setGradient({ reverse: true }, { label: "Reverse Gradient" });
|
|
8134
|
+
}
|
|
8135
|
+
/** `doc` with path `id`'s `target` paint replaced. */
|
|
8136
|
+
withGradient(doc, id, target, g) {
|
|
8137
|
+
return { ...doc, nodes: mapTree(doc.nodes, (n) => n.id === id ? { ...n, [target]: g } : n) };
|
|
8138
|
+
}
|
|
8139
|
+
downGradient(screen, p, input) {
|
|
8140
|
+
const before = this.snapshot();
|
|
8141
|
+
const a = this.getGradientAnnotator();
|
|
8142
|
+
let part = a ? hitVectorGradientAnnotator(a, screen) : null;
|
|
8143
|
+
if (a && part) {
|
|
8144
|
+
if (part.kind === "handle" && part.handle === "start" && input.alt && a.gradient.type === "radial") part = { kind: "handle", handle: "focal" };
|
|
8145
|
+
if (part.kind === "stop") this.gradientEdit = { ...this.gradientEdit, stop: part.index };
|
|
8146
|
+
this.drag = { kind: "gradient", part, nodeId: a.nodeId, targets: [a.nodeId], annotator: a, start: screen, startDoc: p, before, base: this.doc, moved: false, stop: part.kind === "stop" ? part.index : null };
|
|
8147
|
+
this.interaction = { type: "gradient", part: part.kind, ...part.kind === "handle" ? { handle: part.handle } : {} };
|
|
8148
|
+
this.notify();
|
|
8149
|
+
return;
|
|
8150
|
+
}
|
|
8151
|
+
const hit = this.hitPath(p);
|
|
8152
|
+
if (hit && !this.gradientPaths().some((n) => n.id === hit.id)) {
|
|
8153
|
+
this.selection = [hit.id];
|
|
8154
|
+
this.gradientEdit = { ...this.gradientEdit, stop: null };
|
|
8155
|
+
}
|
|
8156
|
+
const targets = this.gradientPaths().map((n) => n.id);
|
|
8157
|
+
if (!targets.length) {
|
|
8158
|
+
if (this.selection.length) this.clearSelection();
|
|
8159
|
+
return;
|
|
8160
|
+
}
|
|
8161
|
+
this.drag = { kind: "gradient", part: null, nodeId: null, targets, annotator: null, start: screen, startDoc: p, before, base: this.doc, moved: false, stop: null };
|
|
8162
|
+
this.interaction = { type: "gradient", part: "create" };
|
|
8163
|
+
this.notify();
|
|
8164
|
+
}
|
|
8165
|
+
moveGradient(drag, screen, p, input) {
|
|
8166
|
+
if (!drag.moved && Math.hypot(screen.x - drag.start.x, screen.y - drag.start.y) < 3) return;
|
|
8167
|
+
drag.moved = true;
|
|
8168
|
+
const target = this.gradientEdit.target;
|
|
8169
|
+
const base = drag.base;
|
|
8170
|
+
const constrained = (from, to) => {
|
|
8171
|
+
if (!input.shift) return to;
|
|
8172
|
+
const ang = Math.round(Math.atan2(to.y - from.y, to.x - from.x) / (Math.PI / 4)) * (Math.PI / 4);
|
|
8173
|
+
const len2 = Math.hypot(to.x - from.x, to.y - from.y);
|
|
8174
|
+
return { x: from.x + Math.cos(ang) * len2, y: from.y + Math.sin(ang) * len2 };
|
|
8175
|
+
};
|
|
8176
|
+
if (!drag.part) {
|
|
8177
|
+
const end = constrained(drag.startDoc, p);
|
|
8178
|
+
let doc = base;
|
|
8179
|
+
for (const id2 of drag.targets) {
|
|
8180
|
+
const node2 = findVectorNode(base.nodes, id2);
|
|
8181
|
+
if (!node2) continue;
|
|
8182
|
+
const paint = node2[target];
|
|
8183
|
+
const g2 = isVectorGradient(paint) ? paint : defaultVectorGradient("linear", paint);
|
|
8184
|
+
doc = this.withGradient(doc, id2, target, setVectorGradientVector(g2, vectorPathLocalBounds(node2), this.toLocal(id2, drag.startDoc, base), this.toLocal(id2, end, base)));
|
|
8185
|
+
}
|
|
8186
|
+
this.doc = doc;
|
|
8187
|
+
this.notify(true);
|
|
8188
|
+
return;
|
|
8189
|
+
}
|
|
8190
|
+
const id = drag.nodeId;
|
|
8191
|
+
const node = findVectorNode(base.nodes, id);
|
|
8192
|
+
const g = node[target];
|
|
8193
|
+
const bounds = vectorPathLocalBounds(node);
|
|
8194
|
+
const local = (q) => this.toLocal(id, q, base);
|
|
8195
|
+
const part = drag.part;
|
|
8196
|
+
let next = g;
|
|
8197
|
+
if (part.kind === "handle") {
|
|
8198
|
+
let to = p;
|
|
8199
|
+
if (part.handle === "end" || part.handle === "radius") to = constrained(this.screenToDocument(drag.annotator.start), p);
|
|
8200
|
+
next = moveVectorGradientHandle(g, bounds, part.handle, local(to), local(drag.startDoc));
|
|
8201
|
+
} else if (part.kind === "bar") {
|
|
8202
|
+
next = moveVectorGradientHandle(g, bounds, "start", local(p), local(drag.startDoc));
|
|
8203
|
+
} else {
|
|
8204
|
+
const a = drag.annotator;
|
|
8205
|
+
const pos = vectorGradientBarPosition(a, screen);
|
|
8206
|
+
const stops = sortedVectorGradientStops(g);
|
|
8207
|
+
if (part.kind === "stop") {
|
|
8208
|
+
const off = Math.abs(pos.across - VECTOR_GRADIENT_STOP_GAP) > VECTOR_GRADIENT_OFF_BAR;
|
|
8209
|
+
const removed = off ? removeVectorGradientStop(stops, part.index) : null;
|
|
8210
|
+
if (removed) {
|
|
8211
|
+
next = { ...g, stops: removed };
|
|
8212
|
+
drag.stop = null;
|
|
8213
|
+
} else {
|
|
8214
|
+
const r = moveVectorGradientStop(stops, part.index, Math.min(1, Math.max(0, pos.offset)));
|
|
8215
|
+
next = { ...g, stops: r.stops };
|
|
8216
|
+
drag.stop = r.index;
|
|
8217
|
+
}
|
|
8218
|
+
this.interaction = { type: "gradient", part: "stop", ...removed ? { removing: true } : {} };
|
|
8219
|
+
this.gradientEdit = { ...this.gradientEdit, stop: drag.stop };
|
|
8220
|
+
} else {
|
|
8221
|
+
const m = vectorGradientMidpointFromPosition(stops, part.index, pos.offset);
|
|
8222
|
+
if (m !== null) next = { ...g, stops: stops.map((s, i) => i !== part.index ? s : Math.abs(m - 0.5) < 1e-3 ? (({ midpoint: _m, ...rest }) => rest)(s) : { ...s, midpoint: m }) };
|
|
8223
|
+
}
|
|
8224
|
+
}
|
|
8225
|
+
this.doc = this.withGradient(base, id, target, next);
|
|
8226
|
+
this.notify(true);
|
|
8227
|
+
}
|
|
8228
|
+
upGradient(drag) {
|
|
8229
|
+
const part = drag.part;
|
|
8230
|
+
if (!drag.moved) {
|
|
8231
|
+
if (part?.kind === "bar") {
|
|
8232
|
+
this.addGradientStop(part.offset);
|
|
8233
|
+
return;
|
|
8234
|
+
}
|
|
8235
|
+
this.notify();
|
|
8236
|
+
return;
|
|
8237
|
+
}
|
|
8238
|
+
const label = !part ? "Gradient" : part.kind === "stop" ? drag.stop === null ? "Delete Gradient Stop" : "Move Gradient Stop" : part.kind === "midpoint" ? "Gradient Midpoint" : part.kind === "bar" || part.kind === "handle" && part.handle === "start" ? "Move Gradient" : "Gradient Handle";
|
|
8239
|
+
return this.commit(label, drag.before);
|
|
8240
|
+
}
|
|
6834
8241
|
/** Removes one width point of a path's profile. */
|
|
6835
8242
|
removeWidthPoint(ref) {
|
|
6836
8243
|
return this.removeWidthPoints([ref]);
|
|
@@ -6881,11 +8288,11 @@ var VectorEditorController = class {
|
|
|
6881
8288
|
if (node?.type !== "path") return { ok: false, message: "There is no such path." };
|
|
6882
8289
|
const g = vectorStrokeWidthGeometry(node);
|
|
6883
8290
|
if (!g || !(g.unit > 0)) return { ok: false, message: "The path has no visible stroke." };
|
|
6884
|
-
const
|
|
6885
|
-
const t =
|
|
6886
|
-
const
|
|
6887
|
-
const right =
|
|
6888
|
-
const
|
|
8291
|
+
const num2 = (v2) => typeof v2 === "number" && Number.isFinite(v2) ? v2 : void 0;
|
|
8292
|
+
const t = num2(values.t);
|
|
8293
|
+
const left2 = num2(values.left);
|
|
8294
|
+
const right = num2(values.right);
|
|
8295
|
+
const scale3 = (w) => w === void 0 ? void 0 : Math.max(0, w) / g.unit;
|
|
6889
8296
|
const profile = sanitizeWidthProfile(node.strokeWidthProfile);
|
|
6890
8297
|
let points;
|
|
6891
8298
|
let index;
|
|
@@ -6893,11 +8300,11 @@ var VectorEditorController = class {
|
|
|
6893
8300
|
if (existing === null) {
|
|
6894
8301
|
if (t === void 0) return { ok: false, message: "A new width point needs a position t." };
|
|
6895
8302
|
if (profile.length >= VECTOR_WIDTH_PROFILE_MAX_POINTS) return { ok: false, message: "The profile has too many width points." };
|
|
6896
|
-
({ points, index } = insertWidthPoint(profile.length ? profile : null, t, { left:
|
|
8303
|
+
({ points, index } = insertWidthPoint(profile.length ? profile : null, t, { left: scale3(left2), right: scale3(right) }, { step: true }));
|
|
6897
8304
|
} else {
|
|
6898
8305
|
const p = profile[existing];
|
|
6899
8306
|
if (!p) return { ok: false, message: "There is no such width point." };
|
|
6900
|
-
const sides2 = { left:
|
|
8307
|
+
const sides2 = { left: scale3(left2) ?? p.left, right: scale3(right) ?? p.right };
|
|
6901
8308
|
const tt = t === void 0 ? p.t : Math.min(Math.max(t, 0), 1);
|
|
6902
8309
|
if (Math.abs(tt - p.t) <= VECTOR_WIDTH_SAME_T) {
|
|
6903
8310
|
points = setWidthPoint(profile, existing, sides2);
|
|
@@ -8714,6 +10121,13 @@ var VectorEditorController = class {
|
|
|
8714
10121
|
const spot = this.strokeSpotAt(p);
|
|
8715
10122
|
return spot ? { nodeId: spot.nodeId, strokeSpot: spot } : null;
|
|
8716
10123
|
}
|
|
10124
|
+
case "gradient": {
|
|
10125
|
+
const a = this.getGradientAnnotator();
|
|
10126
|
+
const part = a ? hitVectorGradientAnnotator(a, screen) : null;
|
|
10127
|
+
if (a && part) return { nodeId: a.nodeId, gradient: part };
|
|
10128
|
+
const path = this.hitPath(p);
|
|
10129
|
+
return path ? { nodeId: path.id } : null;
|
|
10130
|
+
}
|
|
8717
10131
|
default:
|
|
8718
10132
|
return null;
|
|
8719
10133
|
}
|
|
@@ -8936,6 +10350,8 @@ var VectorEditorController = class {
|
|
|
8936
10350
|
return this.downBuilder(p, input);
|
|
8937
10351
|
case "blend":
|
|
8938
10352
|
return this.downBlend(screen, p);
|
|
10353
|
+
case "gradient":
|
|
10354
|
+
return this.downGradient(screen, p, input);
|
|
8939
10355
|
case "scissors": {
|
|
8940
10356
|
const seg = this.nearestOutline(p);
|
|
8941
10357
|
if (seg) this.cutPathAt(seg.nodeId, seg.point);
|
|
@@ -9227,6 +10643,8 @@ var VectorEditorController = class {
|
|
|
9227
10643
|
this.viewport = { ...drag.viewport, x: drag.viewport.x + screen.x - drag.start.x, y: drag.viewport.y + screen.y - drag.start.y };
|
|
9228
10644
|
this.notify();
|
|
9229
10645
|
return;
|
|
10646
|
+
case "gradient":
|
|
10647
|
+
return this.moveGradient(drag, screen, p, input);
|
|
9230
10648
|
case "move": {
|
|
9231
10649
|
let dx = p.x - drag.start.x;
|
|
9232
10650
|
let dy = p.y - drag.start.y;
|
|
@@ -9602,6 +11020,8 @@ var VectorEditorController = class {
|
|
|
9602
11020
|
}
|
|
9603
11021
|
case "builder":
|
|
9604
11022
|
return this.upBuilder(drag);
|
|
11023
|
+
case "gradient":
|
|
11024
|
+
return this.upGradient(drag);
|
|
9605
11025
|
case "stroke": {
|
|
9606
11026
|
this.notify();
|
|
9607
11027
|
if (drag.tool === "knife") this.knife(drag.points);
|
|
@@ -9816,6 +11236,7 @@ var VectorEditorController = class {
|
|
|
9816
11236
|
case "Delete":
|
|
9817
11237
|
case "Backspace":
|
|
9818
11238
|
if (this.tool === "width" && this.widthPoints.length) return this.removeWidthPoints().ok;
|
|
11239
|
+
if (this.tool === "gradient" && this.gradientEdit.stop !== null) return this.removeGradientStop(this.gradientEdit.stop).ok;
|
|
9819
11240
|
if (this.pen) {
|
|
9820
11241
|
const node = this.getNode(this.pen.nodeId);
|
|
9821
11242
|
const sp = node.subpaths[this.pen.subpath];
|
|
@@ -9924,25 +11345,24 @@ function pencilPressureProfile(points, pressures) {
|
|
|
9924
11345
|
}
|
|
9925
11346
|
|
|
9926
11347
|
// src/vector/render.ts
|
|
9927
|
-
var
|
|
9928
|
-
var
|
|
11348
|
+
var fmt4 = (n) => Math.round(n * 100) / 100;
|
|
11349
|
+
var fmtMatrix2 = (m) => `matrix(${m.map((n) => Math.round(n * 1e6) / 1e6).join(" ")})`;
|
|
9929
11350
|
var VECTOR_BUILDER_HATCH_GAP = 6;
|
|
9930
11351
|
function renderNode(stored, defs, idPrefix) {
|
|
9931
11352
|
if (stored.visible === false) return null;
|
|
9932
11353
|
const node = expandVectorAppearance(stored);
|
|
9933
11354
|
if (node.type === "group") {
|
|
9934
11355
|
const children = node.children.map((c) => renderNode(c, defs, idPrefix)).filter((c) => !!c);
|
|
9935
|
-
return { kind: "group", id: node.id, ...node.transform ? { transform:
|
|
11356
|
+
return { kind: "group", id: node.id, ...node.transform ? { transform: fmtMatrix2(node.transform) } : {}, opacity: node.opacity ?? 1, children };
|
|
9936
11357
|
}
|
|
9937
|
-
const fill = vectorPaintToSvg(node.fill, `${idPrefix}fill-${node.id}
|
|
9938
|
-
const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}
|
|
9939
|
-
|
|
9940
|
-
if (stroke.gradient) defs.push(stroke.gradient);
|
|
11358
|
+
const fill = vectorPaintToSvg(node.fill, `${idPrefix}fill-${node.id}`, { node });
|
|
11359
|
+
const stroke = vectorPaintToSvg(node.stroke, `${idPrefix}stroke-${node.id}`, { node, role: "stroke" });
|
|
11360
|
+
defs.push(...fill.defs, ...stroke.defs);
|
|
9941
11361
|
const strokeParts = stroke.value !== "none" ? vectorStrokeSvgParts(node, `${idPrefix}clip-${node.id}`) : void 0;
|
|
9942
11362
|
return {
|
|
9943
11363
|
kind: "path",
|
|
9944
11364
|
id: node.id,
|
|
9945
|
-
...node.transform ? { transform:
|
|
11365
|
+
...node.transform ? { transform: fmtMatrix2(node.transform) } : {},
|
|
9946
11366
|
...strokeParts ? { strokeParts } : {},
|
|
9947
11367
|
d: vectorPathData(node),
|
|
9948
11368
|
fill: fill.value,
|
|
@@ -9961,7 +11381,7 @@ function getVectorSceneRenderData(state, options = {}) {
|
|
|
9961
11381
|
const nodes = state.document.nodes.map((n) => renderNode(n, defs, prefix)).filter((n) => !!n);
|
|
9962
11382
|
const v2 = state.viewport;
|
|
9963
11383
|
return {
|
|
9964
|
-
transform: `translate(${
|
|
11384
|
+
transform: `translate(${fmt4(v2.x)} ${fmt4(v2.y)}) scale(${v2.zoom})`,
|
|
9965
11385
|
width: state.document.width,
|
|
9966
11386
|
height: state.document.height,
|
|
9967
11387
|
...state.document.background ? { background: state.document.background } : {},
|
|
@@ -10049,7 +11469,7 @@ function getVectorOverlayRenderData(state) {
|
|
|
10049
11469
|
const a = toScreen(state, last);
|
|
10050
11470
|
const p = toScreen(state, state.pointer);
|
|
10051
11471
|
const c = last.out ? toScreen(state, last.out) : null;
|
|
10052
|
-
penPreview = c ? `M${
|
|
11472
|
+
penPreview = c ? `M${fmt4(a.x)} ${fmt4(a.y)}Q${fmt4(c.x)} ${fmt4(c.y)} ${fmt4(p.x)} ${fmt4(p.y)}` : `M${fmt4(a.x)} ${fmt4(a.y)}L${fmt4(p.x)} ${fmt4(p.y)}`;
|
|
10053
11473
|
}
|
|
10054
11474
|
}
|
|
10055
11475
|
const origin = toScreen(state, { x: 0, y: 0 });
|
|
@@ -10057,45 +11477,45 @@ function getVectorOverlayRenderData(state) {
|
|
|
10057
11477
|
let status = null;
|
|
10058
11478
|
const it = state.interaction;
|
|
10059
11479
|
if (it?.type === "rotate") status = `${Math.round(it.angle)}\xB0`;
|
|
10060
|
-
else if (it?.type === "move" || it?.type === "anchors") status = `\u0394 ${
|
|
11480
|
+
else if (it?.type === "move" || it?.type === "anchors") status = `\u0394 ${fmt4(it.dx)}, ${fmt4(it.dy)}`;
|
|
10061
11481
|
else if (it?.type === "scale" || it?.type === "draw") {
|
|
10062
11482
|
const b = it.type === "draw" ? findVectorNode(doc.nodes, it.nodeId) ? vectorNodeBounds(findVectorNode(doc.nodes, it.nodeId)) : null : state.selectionFrame;
|
|
10063
|
-
if (b) status = `W ${
|
|
11483
|
+
if (b) status = `W ${fmt4(b.width)} \xD7 H ${fmt4(b.height)}`;
|
|
10064
11484
|
}
|
|
10065
11485
|
const guides = { lines: [], labels: [] };
|
|
10066
11486
|
if (state.guides) {
|
|
10067
11487
|
for (const l of state.guides.lines) {
|
|
10068
11488
|
const a = toScreen(state, { x: l.x1, y: l.y1 });
|
|
10069
11489
|
const b = toScreen(state, { x: l.x2, y: l.y2 });
|
|
10070
|
-
guides.lines.push({ kind: l.kind, x1:
|
|
11490
|
+
guides.lines.push({ kind: l.kind, x1: fmt4(a.x), y1: fmt4(a.y), x2: fmt4(b.x), y2: fmt4(b.y) });
|
|
10071
11491
|
}
|
|
10072
11492
|
for (const l of state.guides.labels) {
|
|
10073
11493
|
const p = toScreen(state, l);
|
|
10074
|
-
guides.labels.push({ kind: l.kind, x:
|
|
11494
|
+
guides.labels.push({ kind: l.kind, x: fmt4(p.x), y: fmt4(p.y), text: l.text });
|
|
10075
11495
|
}
|
|
10076
11496
|
}
|
|
10077
11497
|
let toolStroke = null;
|
|
10078
11498
|
if (it?.type === "stroke") {
|
|
10079
11499
|
const pts = it.points.map((q) => toScreen(state, q));
|
|
10080
|
-
const d = pts.map((q, i) => `${i ? "L" : "M"}${
|
|
10081
|
-
toolStroke = { tool: it.tool, d, width:
|
|
11500
|
+
const d = pts.map((q, i) => `${i ? "L" : "M"}${fmt4(q.x)} ${fmt4(q.y)}`).join("") + (pts.length === 1 ? `L${fmt4(pts[0].x)} ${fmt4(pts[0].y)}` : "");
|
|
11501
|
+
toolStroke = { tool: it.tool, d, width: fmt4(it.width * z) };
|
|
10082
11502
|
}
|
|
10083
11503
|
let pencilString = null;
|
|
10084
11504
|
if (it?.type === "draw" && it.string) {
|
|
10085
11505
|
const a = toScreen(state, it.string[0]);
|
|
10086
11506
|
const b = toScreen(state, it.string[1]);
|
|
10087
|
-
pencilString = { x1:
|
|
11507
|
+
pencilString = { x1: fmt4(a.x), y1: fmt4(a.y), x2: fmt4(b.x), y2: fmt4(b.y) };
|
|
10088
11508
|
}
|
|
10089
11509
|
const dragged = it?.type === "corner" ? it.ref : null;
|
|
10090
11510
|
const cornerWidgets = getVectorCornerWidgets(state).map((w) => {
|
|
10091
11511
|
const s = toScreen(state, w);
|
|
10092
|
-
return { ref: w.ref, x:
|
|
11512
|
+
return { ref: w.ref, x: fmt4(s.x), y: fmt4(s.y), radius: w.radius, kind: w.kind, hovered: sameAnchor(state.hover?.corner, w.ref), active: !!dragged };
|
|
10093
11513
|
});
|
|
10094
|
-
if (it?.type === "corner") status = `R ${
|
|
10095
|
-
const cutPoint = state.tool === "scissors" && state.hover?.segment && !it ? (({ x, y }) => ({ x:
|
|
11514
|
+
if (it?.type === "corner") status = `R ${fmt4(it.radius)}`;
|
|
11515
|
+
const cutPoint = state.tool === "scissors" && state.hover?.segment && !it ? (({ x, y }) => ({ x: fmt4(x), y: fmt4(y) }))(toScreen(state, state.hover.segment.point)) : null;
|
|
10096
11516
|
const sc = (p) => {
|
|
10097
11517
|
const q = toScreen(state, p);
|
|
10098
|
-
return { x:
|
|
11518
|
+
return { x: fmt4(q.x), y: fmt4(q.y) };
|
|
10099
11519
|
};
|
|
10100
11520
|
const sameWidth = (a, b) => !!a && a.nodeId === b.nodeId && a.index === b.index;
|
|
10101
11521
|
const widthPoints = getVectorWidthHandles(state).map((h) => {
|
|
@@ -10106,7 +11526,7 @@ function getVectorOverlayRenderData(state) {
|
|
|
10106
11526
|
});
|
|
10107
11527
|
const spot = state.tool === "width" && !it ? state.hover?.strokeSpot : null;
|
|
10108
11528
|
const widthSpot = spot ? { ...sc(spot.point), left: sc(spot.left), right: sc(spot.right) } : null;
|
|
10109
|
-
if (it?.type === "width") status = it.mode === "move" ? "Move width point" : it.mode === "copy" ? "Copy width point" : `W ${
|
|
11529
|
+
if (it?.type === "width") status = it.mode === "move" ? "Move width point" : it.mode === "copy" ? "Copy width point" : `W ${fmt4(it.width)}`;
|
|
10110
11530
|
let builder = null;
|
|
10111
11531
|
if (state.builder) {
|
|
10112
11532
|
const v2 = state.viewport;
|
|
@@ -10129,7 +11549,43 @@ function getVectorOverlayRenderData(state) {
|
|
|
10129
11549
|
}
|
|
10130
11550
|
const blend = blendOverlay(state);
|
|
10131
11551
|
if (state.tool === "blend" && !it) status = state.blendStart ? "Click another shape to blend into" : "Click a shape to blend from";
|
|
10132
|
-
|
|
11552
|
+
let gradient = null;
|
|
11553
|
+
if (state.tool === "gradient") {
|
|
11554
|
+
gradient = gradientOverlay(state);
|
|
11555
|
+
if (!gradient && !it) status = "Drag across a shape to set a gradient";
|
|
11556
|
+
else if (it?.type === "gradient" && it.removing) status = "Release to delete the stop";
|
|
11557
|
+
}
|
|
11558
|
+
return { outlines, selectionBox, anchors, handles, marquee, penPreview, artboard, guides, status, toolStroke, pencilString, cutPoint, cornerWidgets, widthPoints, widthSpot, builder, blend, gradient };
|
|
11559
|
+
}
|
|
11560
|
+
function gradientOverlay(state) {
|
|
11561
|
+
const a = getVectorGradientAnnotator(state.document, state.selection, state.viewport, state.gradientEdit.target);
|
|
11562
|
+
if (!a) return null;
|
|
11563
|
+
const hover = state.interaction ? null : state.hover?.gradient;
|
|
11564
|
+
const handleHovered = (h) => hover?.kind === "handle" && hover.handle === h;
|
|
11565
|
+
const pt2 = (p) => ({ x: fmt4(p.x), y: fmt4(p.y) });
|
|
11566
|
+
const centred = isCentredVectorGradient(a.gradient.type);
|
|
11567
|
+
const removing = state.interaction?.type === "gradient" && !!state.interaction.removing;
|
|
11568
|
+
return {
|
|
11569
|
+
nodeId: a.nodeId,
|
|
11570
|
+
type: a.gradient.type,
|
|
11571
|
+
start: { ...pt2(a.start), hovered: handleHovered("start") },
|
|
11572
|
+
end: { ...pt2(a.end), hovered: handleHovered(centred ? "radius" : "end"), handle: centred ? "radius" : "end" },
|
|
11573
|
+
aspect: a.aspect ? { ...pt2(a.aspect), hovered: handleHovered("aspect") } : null,
|
|
11574
|
+
focal: a.focal ? { ...pt2(a.focal), hovered: handleHovered("focal") } : null,
|
|
11575
|
+
stops: a.stops.map((s) => ({
|
|
11576
|
+
index: s.index,
|
|
11577
|
+
...pt2(s),
|
|
11578
|
+
barX: fmt4(s.x - a.normal.x * VECTOR_GRADIENT_STOP_GAP),
|
|
11579
|
+
barY: fmt4(s.y - a.normal.y * VECTOR_GRADIENT_STOP_GAP),
|
|
11580
|
+
color: s.color,
|
|
11581
|
+
opacity: s.opacity ?? 1,
|
|
11582
|
+
selected: state.gradientEdit.stop === s.index,
|
|
11583
|
+
hovered: hover?.kind === "stop" && hover.index === s.index
|
|
11584
|
+
})),
|
|
11585
|
+
midpoints: a.midpoints.map((m) => ({ index: m.index, ...pt2(m), hovered: hover?.kind === "midpoint" && hover.index === m.index })),
|
|
11586
|
+
extent: a.extent,
|
|
11587
|
+
removing
|
|
11588
|
+
};
|
|
10133
11589
|
}
|
|
10134
11590
|
function selectedBlends(state) {
|
|
10135
11591
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -10146,7 +11602,7 @@ function blendOverlay(state) {
|
|
|
10146
11602
|
const doc = state.document;
|
|
10147
11603
|
const sc = (p, m) => {
|
|
10148
11604
|
const q = { x: m[0] * p.x + m[2] * p.y + m[4], y: m[1] * p.x + m[3] * p.y + m[5] };
|
|
10149
|
-
return { x:
|
|
11605
|
+
return { x: fmt4(q.x), y: fmt4(q.y) };
|
|
10150
11606
|
};
|
|
10151
11607
|
const spines = selectedBlends(state).flatMap((g) => {
|
|
10152
11608
|
const s = vectorBlendSpine(g);
|
|
@@ -10224,7 +11680,7 @@ var VECTOR_OVERLAY_THEME = {
|
|
|
10224
11680
|
builderHatchOpacity: 0.6
|
|
10225
11681
|
};
|
|
10226
11682
|
function vectorDiamondPath(x, y, r = VECTOR_OVERLAY_THEME.widthPointSize) {
|
|
10227
|
-
return `M${
|
|
11683
|
+
return `M${fmt4(x)} ${fmt4(y - r)}L${fmt4(x + r)} ${fmt4(y)}L${fmt4(x)} ${fmt4(y + r)}L${fmt4(x - r)} ${fmt4(y)}Z`;
|
|
10228
11684
|
}
|
|
10229
11685
|
function toVectorModifiers(event) {
|
|
10230
11686
|
return { shift: !!event.shiftKey, alt: !!event.altKey, mod: !!(event.metaKey || event.ctrlKey) };
|
|
@@ -10329,6 +11785,45 @@ function vectorPaintToHex(paint) {
|
|
|
10329
11785
|
const hex = m[1].length === 3 ? m[1].replace(/./g, (c) => c + c) : m[1];
|
|
10330
11786
|
return `#${hex.toLowerCase()}`;
|
|
10331
11787
|
}
|
|
11788
|
+
var VECTOR_GRADIENT_TYPE_LABELS = { linear: "Linear", radial: "Radial", conic: "Conic", reflected: "Reflected", diamond: "Diamond" };
|
|
11789
|
+
var VECTOR_GRADIENT_SPREAD_LABELS = { pad: "Pad", reflect: "Reflect", repeat: "Repeat" };
|
|
11790
|
+
var VECTOR_GRADIENT_INTERPOLATION_LABELS = { srgb: "sRGB", linear: "Linear light", oklab: "Oklab" };
|
|
11791
|
+
var VECTOR_STROKE_GRADIENT_MODE_LABELS = { within: "Within stroke", along: "Along stroke", across: "Across stroke" };
|
|
11792
|
+
function getVectorGradientEditor(state) {
|
|
11793
|
+
const paths = [];
|
|
11794
|
+
for (const id of state.selection) {
|
|
11795
|
+
const node = findVectorNode(state.document.nodes, id);
|
|
11796
|
+
if (node) paths.push(...collect(node));
|
|
11797
|
+
}
|
|
11798
|
+
if (!paths.length) return null;
|
|
11799
|
+
const target = state.gradientEdit.target;
|
|
11800
|
+
const paint = paths[0][target];
|
|
11801
|
+
const gradient = isVectorGradient(paint) ? paint : null;
|
|
11802
|
+
const sorted = gradient ? sortedVectorGradientStops(gradient) : [];
|
|
11803
|
+
const stopIndex = state.gradientEdit.stop;
|
|
11804
|
+
const stops = sorted.map((s, index) => ({ ...s, index, selected: index === stopIndex }));
|
|
11805
|
+
const sel = stopIndex !== null && sorted[stopIndex] ? { ...sorted[stopIndex], index: stopIndex, last: stopIndex === sorted.length - 1 } : null;
|
|
11806
|
+
const preview = gradient ? `linear-gradient(to right, ${expandVectorGradientStops(gradient).map((s) => {
|
|
11807
|
+
const c = parseVectorColor(s.color);
|
|
11808
|
+
const a = (c?.[3] ?? 1) * (s.opacity ?? 1);
|
|
11809
|
+
const color = c ? `rgba(${Math.round(c[0] * 255)}, ${Math.round(c[1] * 255)}, ${Math.round(c[2] * 255)}, ${Math.round(a * 1e3) / 1e3})` : s.color;
|
|
11810
|
+
return `${color} ${Math.round(s.offset * 1e4) / 100}%`;
|
|
11811
|
+
}).join(", ")})` : null;
|
|
11812
|
+
return {
|
|
11813
|
+
target,
|
|
11814
|
+
gradient,
|
|
11815
|
+
color: vectorPaintToHex(paint),
|
|
11816
|
+
stops,
|
|
11817
|
+
selected: sel,
|
|
11818
|
+
preview,
|
|
11819
|
+
type: gradient?.type ?? null,
|
|
11820
|
+
spread: gradient?.spread ?? "pad",
|
|
11821
|
+
interpolation: gradient?.interpolation ?? "srgb",
|
|
11822
|
+
smoothness: gradient?.smoothness ?? 0,
|
|
11823
|
+
strokeMode: gradient?.strokeMode ?? "within",
|
|
11824
|
+
mixed: paths.some((p) => JSON.stringify(p[target] ?? null) !== JSON.stringify(paint ?? null))
|
|
11825
|
+
};
|
|
11826
|
+
}
|
|
10332
11827
|
var VECTOR_PATH_ACTIONS = [
|
|
10333
11828
|
{ id: "join", label: "\u22C8", title: "Join", shortcut: "J", enabled: (s, p) => s + p > 0, run: (c) => c.joinPaths() },
|
|
10334
11829
|
{ id: "average", label: "\u22B9", title: "Average Points", shortcut: "Alt+J", enabled: (_s, p) => p > 1, run: (c) => c.averageAnchors("both") },
|
|
@@ -10416,8 +11911,41 @@ var checkBlendOptions = (params) => {
|
|
|
10416
11911
|
if (params?.steps !== void 0 && !(Number.isFinite(params.steps) && params.steps >= 1)) fail("steps must be at least 1.");
|
|
10417
11912
|
if (params?.distance !== void 0 && !(Number.isFinite(params.distance) && params.distance > 0)) fail("distance must be positive.");
|
|
10418
11913
|
};
|
|
11914
|
+
var POINT = { type: "object", properties: { x: { type: "number" }, y: { type: "number" } }, required: ["x", "y"] };
|
|
11915
|
+
var GRADIENT_PROPS = {
|
|
11916
|
+
type: { type: "string", enum: VECTOR_GRADIENT_TYPES, description: "linear, radial, conic (an angle sweep), reflected (linear mirrored about the start) or diamond." },
|
|
11917
|
+
stops: {
|
|
11918
|
+
type: "array",
|
|
11919
|
+
minItems: 1,
|
|
11920
|
+
items: {
|
|
11921
|
+
type: "object",
|
|
11922
|
+
properties: {
|
|
11923
|
+
offset: { type: "number", minimum: 0, maximum: 1 },
|
|
11924
|
+
color: { type: "string", description: "A CSS colour." },
|
|
11925
|
+
opacity: { type: "number", minimum: 0, maximum: 1 },
|
|
11926
|
+
midpoint: { type: "number", minimum: 0.05, maximum: 0.95, description: "Where between this stop and the next the colours are half-mixed (default 0.5)." }
|
|
11927
|
+
},
|
|
11928
|
+
required: ["offset", "color"]
|
|
11929
|
+
}
|
|
11930
|
+
},
|
|
11931
|
+
angle: { type: "number", description: "Shorthand direction in degrees (0 up, clockwise): linear / reflected direction across the box, conic start angle, diamond turn." },
|
|
11932
|
+
cx: { type: "number", description: "Shorthand centre x of radial / conic / diamond gradients, a fraction of the box (default 0.5)." },
|
|
11933
|
+
cy: { type: "number" },
|
|
11934
|
+
r: { type: "number", description: "Shorthand radius, a fraction of the box (default 0.5)." },
|
|
11935
|
+
start: { ...POINT, description: "Explicit start (centre of radial / conic / diamond), in `units`. Wins over the shorthand." },
|
|
11936
|
+
end: { ...POINT, description: "Explicit end of the main axis (the radius of radial / conic / diamond)." },
|
|
11937
|
+
width: { ...POINT, description: "End of the secondary axis (aspect and turn of radial / conic / diamond); default square to the main axis, as long." },
|
|
11938
|
+
focal: { ...POINT, description: "Radial focal point (where t = 0), in `units`." },
|
|
11939
|
+
focalRadius: { type: "number", minimum: 0, maximum: 0.99, description: "Radial focal circle radius as a fraction of the radius (SVG fr)." },
|
|
11940
|
+
units: { type: "string", enum: ["objectBoundingBox", "userSpaceOnUse"], description: "Space of start / end / width / focal: fractions of the shape box (default) or the node's own coordinates." },
|
|
11941
|
+
spread: { type: "string", enum: VECTOR_GRADIENT_SPREADS, description: "What paints past the ends: pad (default), reflect or repeat." },
|
|
11942
|
+
interpolation: { type: "string", enum: VECTOR_GRADIENT_INTERPOLATIONS, description: "Colour mixing space: srgb (default), linear (light) or oklab." },
|
|
11943
|
+
smoothness: { type: "number", minimum: 0, maximum: 1, description: "Bends the ramp along a smooth spline through the stops (0 = straight)." },
|
|
11944
|
+
strokeMode: { type: "string", enum: VECTOR_STROKE_GRADIENT_MODES, description: "On strokes: within (default, like a fill), along the path or across it." }
|
|
11945
|
+
};
|
|
10419
11946
|
var PAINT = {
|
|
10420
|
-
description: 'A CSS colour, `null` for none, or a gradient `{ type: "linear"
|
|
11947
|
+
description: 'A CSS colour, `null` for none, or a gradient object: `{ type: "linear" | "radial" | "conic" | "reflected" | "diamond", stops: [{ offset, color, opacity?, midpoint? }] }` plus the shorthand `angle` / `cx, cy, r` or explicit `start`, `end`, `width`, `focal`, `focalRadius`, `units`, and `spread`, `interpolation`, `smoothness`, `strokeMode` (see vector.setGradient).',
|
|
11948
|
+
anyOf: [{ type: "string" }, { type: "null" }, { type: "object", properties: GRADIENT_PROPS, required: ["type", "stops"] }]
|
|
10421
11949
|
};
|
|
10422
11950
|
var STYLE_PROPS = {
|
|
10423
11951
|
fill: PAINT,
|
|
@@ -10438,6 +11966,10 @@ var STYLE_PROPS = {
|
|
|
10438
11966
|
var pickStyle = (params) => {
|
|
10439
11967
|
const out = {};
|
|
10440
11968
|
for (const key of Object.keys(STYLE_PROPS)) if (params[key] !== void 0) out[key] = params[key];
|
|
11969
|
+
for (const key of ["fill", "stroke"]) {
|
|
11970
|
+
const paint = out[key];
|
|
11971
|
+
if (paint && typeof paint === "object") out[key] = sanitizeVectorGradient(paint) ?? fail(`${key} is not a valid gradient: give a type and at least one stop { offset, color }.`);
|
|
11972
|
+
}
|
|
10441
11973
|
return out;
|
|
10442
11974
|
};
|
|
10443
11975
|
var check = (result) => {
|
|
@@ -10578,6 +12110,26 @@ function createVectorCommands(controller, options = {}) {
|
|
|
10578
12110
|
return { ok: true };
|
|
10579
12111
|
}
|
|
10580
12112
|
},
|
|
12113
|
+
{
|
|
12114
|
+
...base,
|
|
12115
|
+
id: `${prefix}setGradient`,
|
|
12116
|
+
label: "Gradient\u2026",
|
|
12117
|
+
keywords: ["gradient", "linear", "radial", "conic", "angle", "reflected", "diamond", "stops", "midpoint", "oklab", "spread", "focal", "along stroke", "across stroke", "reverse"],
|
|
12118
|
+
description: "Edits the gradient of the fill (default) or stroke of paths (groups apply to every path inside): any gradient field (missing ones keep their value; a colour becomes a gradient from it to white), `aspect` (secondary axis over main axis), `reverse` (flip the stops and midpoints), `focal` / `width` null to remove them. Midpoints, smoothness and oklab / linear interpolation export to SVG as extra stops; conic and diamond gradients as patterns.",
|
|
12119
|
+
params: { type: "object", properties: { ids: IDS, target: { type: "string", enum: ["fill", "stroke"] }, ...GRADIENT_PROPS, aspect: { type: "number" }, reverse: { type: "boolean" } } },
|
|
12120
|
+
enabled: enabledFor,
|
|
12121
|
+
undoable: true,
|
|
12122
|
+
run: (_ctx, params) => {
|
|
12123
|
+
const { ids: _i, target, ...patch } = params ?? {};
|
|
12124
|
+
if (patch.type !== void 0 && !VECTOR_GRADIENT_TYPES.includes(patch.type)) fail(`Unknown gradient type "${patch.type}".`);
|
|
12125
|
+
if (patch.stops !== void 0 && !(Array.isArray(patch.stops) && patch.stops.length)) fail("stops needs at least one { offset, color }.");
|
|
12126
|
+
check(controller.setGradient(patch, { ids: ids(params), target: target ?? "fill" }));
|
|
12127
|
+
const id = ids(params)[0];
|
|
12128
|
+
const node = id ? controller.getNode(id) : void 0;
|
|
12129
|
+
const path = node?.type === "path" ? node : void 0;
|
|
12130
|
+
return { ok: true, ...path ? { gradient: path[target ?? "fill"] } : {} };
|
|
12131
|
+
}
|
|
12132
|
+
},
|
|
10581
12133
|
{
|
|
10582
12134
|
...base,
|
|
10583
12135
|
id: `${prefix}setCorners`,
|
|
@@ -11269,6 +12821,17 @@ export {
|
|
|
11269
12821
|
VECTOR_EFFECT_PARAM_SPECS,
|
|
11270
12822
|
VECTOR_EFFECT_TYPES,
|
|
11271
12823
|
VECTOR_FILL_GAP,
|
|
12824
|
+
VECTOR_GRADIENT_INTERPOLATIONS,
|
|
12825
|
+
VECTOR_GRADIENT_INTERPOLATION_LABELS,
|
|
12826
|
+
VECTOR_GRADIENT_MAX_PERIODS,
|
|
12827
|
+
VECTOR_GRADIENT_MIDPOINT_GAP,
|
|
12828
|
+
VECTOR_GRADIENT_MIN_STOPS,
|
|
12829
|
+
VECTOR_GRADIENT_OFF_BAR,
|
|
12830
|
+
VECTOR_GRADIENT_SPREADS,
|
|
12831
|
+
VECTOR_GRADIENT_SPREAD_LABELS,
|
|
12832
|
+
VECTOR_GRADIENT_STOP_GAP,
|
|
12833
|
+
VECTOR_GRADIENT_TYPES,
|
|
12834
|
+
VECTOR_GRADIENT_TYPE_LABELS,
|
|
11272
12835
|
VECTOR_INK_MAX_CORNERS,
|
|
11273
12836
|
VECTOR_MERGE_MODES,
|
|
11274
12837
|
VECTOR_OVERLAY_THEME,
|
|
@@ -11280,6 +12843,8 @@ export {
|
|
|
11280
12843
|
VECTOR_PRESSURE_NOMINAL,
|
|
11281
12844
|
VECTOR_ROTATE_HANDLE_OFFSET,
|
|
11282
12845
|
VECTOR_STROKE_ALIGN_LABELS,
|
|
12846
|
+
VECTOR_STROKE_GRADIENT_MODES,
|
|
12847
|
+
VECTOR_STROKE_GRADIENT_MODE_LABELS,
|
|
11283
12848
|
VECTOR_STYLE_KEYS,
|
|
11284
12849
|
VECTOR_TOOLS,
|
|
11285
12850
|
VECTOR_TOOL_GLYPHS,
|
|
@@ -11299,6 +12864,8 @@ export {
|
|
|
11299
12864
|
applyVectorBlendOptions,
|
|
11300
12865
|
applyVectorCornersAttribute,
|
|
11301
12866
|
applyVectorEffects,
|
|
12867
|
+
applyVectorGradientPatch,
|
|
12868
|
+
applyVectorGradientSpread,
|
|
11302
12869
|
arrowheadInset,
|
|
11303
12870
|
arrowheadPath,
|
|
11304
12871
|
arrowheadWeight,
|
|
@@ -11314,6 +12881,7 @@ export {
|
|
|
11314
12881
|
collectPathNodes,
|
|
11315
12882
|
constrainToLeave,
|
|
11316
12883
|
convertAnchor,
|
|
12884
|
+
convertVectorGradient,
|
|
11317
12885
|
cornerBisectorPoint,
|
|
11318
12886
|
cornerRadiusChange,
|
|
11319
12887
|
cornerSetback,
|
|
@@ -11325,6 +12893,7 @@ export {
|
|
|
11325
12893
|
cutSubpathAt,
|
|
11326
12894
|
dashIntervals,
|
|
11327
12895
|
decomposeRigid,
|
|
12896
|
+
defaultVectorGradient,
|
|
11328
12897
|
effectPathLength,
|
|
11329
12898
|
effectPathVertexCount,
|
|
11330
12899
|
effectiveStrokeAlign,
|
|
@@ -11338,6 +12907,9 @@ export {
|
|
|
11338
12907
|
expandVectorBlend,
|
|
11339
12908
|
expandVectorCorners,
|
|
11340
12909
|
expandVectorCornersOf,
|
|
12910
|
+
expandVectorGradientSpread,
|
|
12911
|
+
expandVectorGradientStops,
|
|
12912
|
+
explicitVectorGradient,
|
|
11341
12913
|
findVectorNode,
|
|
11342
12914
|
fitCornerRadius,
|
|
11343
12915
|
foldVectorBooleanOperands,
|
|
@@ -11351,6 +12923,8 @@ export {
|
|
|
11351
12923
|
getVectorCornerWidgets,
|
|
11352
12924
|
getVectorEffectsPanel,
|
|
11353
12925
|
getVectorEnteredGroups,
|
|
12926
|
+
getVectorGradientAnnotator,
|
|
12927
|
+
getVectorGradientEditor,
|
|
11354
12928
|
getVectorOverlayRenderData,
|
|
11355
12929
|
getVectorSceneRenderData,
|
|
11356
12930
|
getVectorSelectionBoxGeometry,
|
|
@@ -11359,6 +12933,8 @@ export {
|
|
|
11359
12933
|
getVectorWidthHandles,
|
|
11360
12934
|
getVisibleAnchors,
|
|
11361
12935
|
getVisibleHandles,
|
|
12936
|
+
gradientMidpointRemap,
|
|
12937
|
+
hasExplicitGradientGeometry,
|
|
11362
12938
|
hasOwnVectorAppearance,
|
|
11363
12939
|
hasPaint,
|
|
11364
12940
|
hasStrokeExtras,
|
|
@@ -11367,8 +12943,10 @@ export {
|
|
|
11367
12943
|
hitTestVectorNode,
|
|
11368
12944
|
hitTestVectorNodes,
|
|
11369
12945
|
hitTestVectorStroke,
|
|
12946
|
+
hitVectorGradientAnnotator,
|
|
11370
12947
|
inferAnchorType,
|
|
11371
12948
|
insertAnchor,
|
|
12949
|
+
insertVectorGradientStop,
|
|
11372
12950
|
insertWidthPoint,
|
|
11373
12951
|
interpolateMorphContours,
|
|
11374
12952
|
interpolateRigidMatrix,
|
|
@@ -11378,10 +12956,12 @@ export {
|
|
|
11378
12956
|
interpolateVectorStyle,
|
|
11379
12957
|
interpolateWidthProfiles,
|
|
11380
12958
|
invertMatrix,
|
|
12959
|
+
isCentredVectorGradient,
|
|
11381
12960
|
isClosedBezierShape,
|
|
11382
12961
|
isIdentityMatrix,
|
|
11383
12962
|
isUniformWidthProfile,
|
|
11384
12963
|
isVectorBlendGroup,
|
|
12964
|
+
isVectorGradient,
|
|
11385
12965
|
isVectorLiveBoolean,
|
|
11386
12966
|
isVectorTextTarget,
|
|
11387
12967
|
joinSubpaths,
|
|
@@ -11394,6 +12974,8 @@ export {
|
|
|
11394
12974
|
morphContourToSubpath,
|
|
11395
12975
|
morphPathsCross,
|
|
11396
12976
|
moveAnchorHandle,
|
|
12977
|
+
moveVectorGradientHandle,
|
|
12978
|
+
moveVectorGradientStop,
|
|
11397
12979
|
multiplyMatrices,
|
|
11398
12980
|
nearestSegment,
|
|
11399
12981
|
nearestStrokePosition,
|
|
@@ -11402,8 +12984,12 @@ export {
|
|
|
11402
12984
|
normalizeVectorEffectParams,
|
|
11403
12985
|
offsetPaths,
|
|
11404
12986
|
parseSvgTransform,
|
|
12987
|
+
parseVectorColor,
|
|
11405
12988
|
parseVectorSvg,
|
|
11406
12989
|
pathsCenter,
|
|
12990
|
+
patternAttrs,
|
|
12991
|
+
periodicVectorGradientStops,
|
|
12992
|
+
pinVectorGradient,
|
|
11407
12993
|
planShapeBuilder,
|
|
11408
12994
|
polygonSubpath,
|
|
11409
12995
|
polylineBounds,
|
|
@@ -11418,26 +13004,36 @@ export {
|
|
|
11418
13004
|
removeAnchors,
|
|
11419
13005
|
removeAnchorsKeepingShape,
|
|
11420
13006
|
removeRedundantPoints,
|
|
13007
|
+
removeVectorGradientStop,
|
|
11421
13008
|
removeWidthPoints,
|
|
11422
13009
|
repeatPaths,
|
|
11423
13010
|
resampleGradientStops,
|
|
11424
13011
|
resolveVectorBlendOptions,
|
|
11425
13012
|
reverseMorphContour,
|
|
11426
13013
|
reverseSubpath,
|
|
13014
|
+
reverseVectorGradient,
|
|
11427
13015
|
reverseWidthProfile,
|
|
11428
13016
|
rotateMatrix,
|
|
11429
13017
|
roundCornerPaths,
|
|
11430
13018
|
roundSubpaths,
|
|
13019
|
+
samplePlainStops,
|
|
11431
13020
|
samplePolyline,
|
|
13021
|
+
sampleVectorGradient,
|
|
13022
|
+
sampleVectorGradientAt,
|
|
13023
|
+
sampleVectorPaintDefAt,
|
|
11432
13024
|
sanitizeVectorEffects,
|
|
13025
|
+
sanitizeVectorGradient,
|
|
11433
13026
|
sanitizeWidthProfile,
|
|
11434
13027
|
scaleFor,
|
|
11435
13028
|
scaleMatrix,
|
|
11436
13029
|
setAnchorCorner,
|
|
11437
13030
|
setSubpathCorners,
|
|
13031
|
+
setVectorGradientVector,
|
|
11438
13032
|
setWidthPoint,
|
|
11439
13033
|
simplifyPolyline,
|
|
11440
13034
|
smoothSubpath,
|
|
13035
|
+
sortedVectorGradientStops,
|
|
13036
|
+
spanVectorGradientStops,
|
|
11441
13037
|
splitIntoFaces,
|
|
11442
13038
|
splitSubpathAt,
|
|
11443
13039
|
starSubpath,
|
|
@@ -11454,7 +13050,9 @@ export {
|
|
|
11454
13050
|
toVectorModifiers,
|
|
11455
13051
|
toVectorPointerInput,
|
|
11456
13052
|
transformVectorEffects,
|
|
13053
|
+
transformVectorGradient,
|
|
11457
13054
|
transformVectorNode,
|
|
13055
|
+
transformVectorPaint,
|
|
11458
13056
|
translateAnchor,
|
|
11459
13057
|
translateMatrix,
|
|
11460
13058
|
trimIntervals,
|
|
@@ -11475,6 +13073,7 @@ export {
|
|
|
11475
13073
|
vectorBlendRailAt,
|
|
11476
13074
|
vectorBlendSpine,
|
|
11477
13075
|
vectorBlendStepCount,
|
|
13076
|
+
vectorBoxMatrix,
|
|
11478
13077
|
vectorBuilderErasedEdges,
|
|
11479
13078
|
vectorCornerAt,
|
|
11480
13079
|
vectorCorners,
|
|
@@ -11483,6 +13082,22 @@ export {
|
|
|
11483
13082
|
vectorDocumentToSvg,
|
|
11484
13083
|
vectorEnteredGroups,
|
|
11485
13084
|
vectorFillMap,
|
|
13085
|
+
vectorGradientArea,
|
|
13086
|
+
vectorGradientAspect,
|
|
13087
|
+
vectorGradientBarPosition,
|
|
13088
|
+
vectorGradientFocalParam,
|
|
13089
|
+
vectorGradientHandles,
|
|
13090
|
+
vectorGradientMatrix,
|
|
13091
|
+
vectorGradientMidpointFromPosition,
|
|
13092
|
+
vectorGradientMidpointPosition,
|
|
13093
|
+
vectorGradientNeedsExpansion,
|
|
13094
|
+
vectorGradientParam,
|
|
13095
|
+
vectorGradientPoints,
|
|
13096
|
+
vectorGradientRamp,
|
|
13097
|
+
vectorGradientSvgDefs,
|
|
13098
|
+
vectorGradientTAt,
|
|
13099
|
+
vectorGradientTargetPath,
|
|
13100
|
+
vectorGradientUnitsMatrix,
|
|
11486
13101
|
vectorInkShapeName,
|
|
11487
13102
|
vectorLiveBooleanSteps,
|
|
11488
13103
|
vectorNodeBounds,
|
|
@@ -11494,6 +13109,7 @@ export {
|
|
|
11494
13109
|
vectorParentMatrix,
|
|
11495
13110
|
vectorPathData,
|
|
11496
13111
|
vectorPathEntries,
|
|
13112
|
+
vectorPathLocalBounds,
|
|
11497
13113
|
vectorPathWorldBezier,
|
|
11498
13114
|
vectorPathfinderShape,
|
|
11499
13115
|
vectorRepeaterCopies,
|
|
@@ -11501,6 +13117,7 @@ export {
|
|
|
11501
13117
|
vectorResultStyle,
|
|
11502
13118
|
vectorShapeBuilderHit,
|
|
11503
13119
|
vectorShapeBuilderMap,
|
|
13120
|
+
vectorStrokeGradientDefs,
|
|
11504
13121
|
vectorStrokeLineArea,
|
|
11505
13122
|
vectorStrokeOutline,
|
|
11506
13123
|
vectorStrokeOutlinePath,
|