@pixerate/editor 0.118.0 → 0.120.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 +1 -0
- package/dist/animation-editor/index.cjs +54 -54
- package/dist/animation-editor/index.d.cts +3 -3
- package/dist/animation-editor/index.d.ts +3 -3
- package/dist/animation-editor/index.js +2 -2
- package/dist/{chunk-R2JE6SA7.js → chunk-BOWUU4LJ.js} +42 -7
- package/dist/chunk-BOWUU4LJ.js.map +1 -0
- package/dist/{chunk-M7O4BJRH.cjs → chunk-DFLJHLKA.cjs} +36 -36
- package/dist/{chunk-M7O4BJRH.cjs.map → chunk-DFLJHLKA.cjs.map} +1 -1
- package/dist/{chunk-2J3LS7JZ.cjs → chunk-HTAFDTNR.cjs} +221 -186
- package/dist/chunk-HTAFDTNR.cjs.map +1 -0
- package/dist/{chunk-L2T6EJ5S.cjs → chunk-L3PXFMOF.cjs} +38 -25
- package/dist/chunk-L3PXFMOF.cjs.map +1 -0
- package/dist/{chunk-IVSNUXW7.js → chunk-T7IZDNSL.js} +37 -24
- package/dist/chunk-T7IZDNSL.js.map +1 -0
- package/dist/{chunk-BYAWOAST.cjs → chunk-TS5IEUW6.cjs} +26 -25
- package/dist/chunk-TS5IEUW6.cjs.map +1 -0
- package/dist/{chunk-VD2UDVF7.js → chunk-UMVW2Q66.js} +21 -5
- package/dist/chunk-UMVW2Q66.js.map +1 -0
- package/dist/{chunk-FODKPD44.cjs → chunk-VYW7JBTM.cjs} +790 -549
- package/dist/chunk-VYW7JBTM.cjs.map +1 -0
- package/dist/{chunk-T2P5TIZ6.js → chunk-WAI6ATHC.js} +2 -2
- package/dist/{chunk-EBSYZRSW.cjs → chunk-WMRIG4XQ.cjs} +21 -5
- package/dist/chunk-WMRIG4XQ.cjs.map +1 -0
- package/dist/{chunk-7LNZAWSJ.js → chunk-WORWH5K5.js} +699 -458
- package/dist/chunk-WORWH5K5.js.map +1 -0
- package/dist/{chunk-JHXMNOMF.js → chunk-YHW26LS2.js} +14 -13
- package/dist/chunk-YHW26LS2.js.map +1 -0
- package/dist/{export-Dul1SbPv.d.ts → export-D2D8hb_u.d.ts} +18 -4
- package/dist/{export-DMOuzgZZ.d.cts → export-fP2hZYjm.d.cts} +18 -4
- package/dist/{flow-4-OC-UkQ.d.cts → flow-A2Dj-69w.d.cts} +21 -9
- package/dist/{flow-Cb7lkPm8.d.ts → flow-C95atNoQ.d.ts} +21 -9
- package/dist/image-editor/index.cjs +1104 -979
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +5 -5
- package/dist/image-editor/index.d.ts +5 -5
- package/dist/image-editor/index.js +157 -32
- package/dist/image-editor/index.js.map +1 -1
- package/dist/paint/index.cjs +5 -3
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +2 -2
- package/dist/paint/index.d.ts +2 -2
- package/dist/paint/index.js +4 -2
- package/dist/paint-editor/index.cjs +147 -147
- package/dist/paint-editor/index.d.cts +4 -4
- package/dist/paint-editor/index.d.ts +4 -4
- package/dist/paint-editor/index.js +4 -4
- package/dist/raster/index.cjs +2 -2
- package/dist/raster/index.js +1 -1
- package/dist/{svg-CVdFc80-.d.cts → svg-S9zzvqaZ.d.cts} +5 -1
- package/dist/{svg-DlEuhwoX.d.ts → svg-leZpRsJB.d.ts} +5 -1
- package/dist/{types-CDLGhAZp.d.ts → types-BgwxFWvn.d.ts} +59 -3
- package/dist/{types-Tfly4Aya.d.cts → types-DdRTDnza.d.cts} +59 -3
- package/dist/vector/index.cjs +1314 -1009
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +110 -5
- package/dist/vector/index.d.ts +110 -5
- package/dist/vector/index.js +319 -14
- package/dist/vector/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/pixerate-image-editor/references/photoshop-files.md +5 -2
- package/skills/pixerate-vector/SKILL.md +13 -1
- package/skills/pixerate-vector/references/commands.md +2 -0
- package/dist/chunk-2J3LS7JZ.cjs.map +0 -1
- package/dist/chunk-7LNZAWSJ.js.map +0 -1
- package/dist/chunk-BYAWOAST.cjs.map +0 -1
- package/dist/chunk-EBSYZRSW.cjs.map +0 -1
- package/dist/chunk-FODKPD44.cjs.map +0 -1
- package/dist/chunk-IVSNUXW7.js.map +0 -1
- package/dist/chunk-JHXMNOMF.js.map +0 -1
- package/dist/chunk-L2T6EJ5S.cjs.map +0 -1
- package/dist/chunk-R2JE6SA7.js.map +0 -1
- package/dist/chunk-VD2UDVF7.js.map +0 -1
- /package/dist/{chunk-T2P5TIZ6.js.map → chunk-WAI6ATHC.js.map} +0 -0
|
@@ -75,6 +75,7 @@ var VECTOR_CONNECTOR_SIDES = ["auto", "top", "right", "bottom", "left", "center"
|
|
|
75
75
|
var VECTOR_CONNECTOR_ROUTES = ["straight", "elbow", "curved"];
|
|
76
76
|
var VECTOR_BLEND_SPACINGS = ["steps", "distance", "smoothColor"];
|
|
77
77
|
var VECTOR_MASK_TYPES = ["clip", "alpha", "luminance"];
|
|
78
|
+
var VECTOR_SYMMETRY_MODES = ["vertical", "horizontal", "dual", "diagonal", "radial"];
|
|
78
79
|
var VECTOR_TEXT_ALIGNS = ["left", "center", "right", "justify"];
|
|
79
80
|
var VECTOR_PATH_TEXT_BASELINES = ["baseline", "ascender", "descender", "center"];
|
|
80
81
|
var VECTOR_CONSTRAINT_KINDS = [
|
|
@@ -121,7 +122,7 @@ var VECTOR_TOOLS = [
|
|
|
121
122
|
];
|
|
122
123
|
var VECTOR_TRANSFORM_HANDLES = ["nw", "n", "ne", "e", "se", "s", "sw", "w", "rotate"];
|
|
123
124
|
var VECTOR_PATHFINDER_OPERATIONS = ["divide", "trim", "merge", "crop", "outline", "minusBack"];
|
|
124
|
-
var VECTOR_EFFECT_TYPES = ["trim", "roundCorners", "puckerBloat", "twist", "zigzag", "wiggle", "offset", "merge", "repeater", "envelope"];
|
|
125
|
+
var VECTOR_EFFECT_TYPES = ["trim", "roundCorners", "puckerBloat", "twist", "zigzag", "wiggle", "offset", "merge", "repeater", "envelope", "contour", "blockShadow"];
|
|
125
126
|
var VECTOR_MERGE_MODES = ["merge", "add", "subtract", "intersect", "exclude"];
|
|
126
127
|
var VECTOR_FILTER_TYPES = ["dropShadow", "innerShadow", "outerGlow", "innerGlow", "layerBlur", "feather"];
|
|
127
128
|
|
|
@@ -465,8 +466,8 @@ function vectorGradientParam(type, q, focal) {
|
|
|
465
466
|
case "diamond":
|
|
466
467
|
return Math.abs(q.x) + Math.abs(q.y);
|
|
467
468
|
case "conic": {
|
|
468
|
-
const
|
|
469
|
-
return
|
|
469
|
+
const turn2 = Math.atan2(q.y, q.x) / (2 * Math.PI);
|
|
470
|
+
return turn2 - Math.floor(turn2);
|
|
470
471
|
}
|
|
471
472
|
case "radial":
|
|
472
473
|
return focal ? vectorGradientFocalParam(q, focal.f, focal.fr) : Math.hypot(q.x, q.y);
|
|
@@ -1451,12 +1452,12 @@ function squareTo(a, b) {
|
|
|
1451
1452
|
return l > EPS ? mul2(w, 1 / l) : { x: 0, y: 0 };
|
|
1452
1453
|
}
|
|
1453
1454
|
function smoothCut(c, radius, smoothing) {
|
|
1454
|
-
const
|
|
1455
|
+
const turn2 = Math.PI - Math.acos(Math.max(-1, Math.min(1, c.cos)));
|
|
1455
1456
|
const t = cornerSetback(c, radius);
|
|
1456
1457
|
const room = Math.min(c.lu, c.lv) / 2;
|
|
1457
1458
|
let s = Math.max(0, Math.min(1, smoothing));
|
|
1458
1459
|
if ((1 + s) * t > room) s = Math.max(0, Math.min(s, room / t - 1));
|
|
1459
|
-
const beta =
|
|
1460
|
+
const beta = turn2 * s / 2;
|
|
1460
1461
|
if (!(beta > 1e-6) || !(t > EPS)) return null;
|
|
1461
1462
|
const p = Math.min((1 + s) * t, room);
|
|
1462
1463
|
const qa = radius * Math.tan(beta / 2);
|
|
@@ -1471,7 +1472,7 @@ function smoothCut(c, radius, smoothing) {
|
|
|
1471
1472
|
};
|
|
1472
1473
|
const U = side(c.u, c.v);
|
|
1473
1474
|
const V2 = side(c.v, c.u);
|
|
1474
|
-
const sweep =
|
|
1475
|
+
const sweep = turn2 * (1 - s);
|
|
1475
1476
|
const h = radius * (4 / 3) * Math.tan(sweep / 4);
|
|
1476
1477
|
const tanU = sub2(U.a, U.q);
|
|
1477
1478
|
const tanV = sub2(V2.a, V2.q);
|
|
@@ -1510,9 +1511,9 @@ function cutCorner(c, radius, kind, smoothing = 0) {
|
|
|
1510
1511
|
}
|
|
1511
1512
|
const d1 = mul2(a.dir, -1);
|
|
1512
1513
|
const d2 = mul2(b.dir, -1);
|
|
1513
|
-
const
|
|
1514
|
+
const turn2 = Math.acos(Math.max(-1, Math.min(1, -dot2(d1, d2))));
|
|
1514
1515
|
const chord = len(sub2(b.p, a.p));
|
|
1515
|
-
const k =
|
|
1516
|
+
const k = turn2 < 1e-9 ? chord / 3 : 4 / 3 * Math.tan(turn2 / 4) * chord / (2 * Math.sin(turn2 / 2));
|
|
1516
1517
|
if (!Number.isFinite(k)) return null;
|
|
1517
1518
|
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 };
|
|
1518
1519
|
}
|
|
@@ -2649,6 +2650,398 @@ function withVectorTargetPaint(node, target, layer, paint) {
|
|
|
2649
2650
|
return target === "fill" ? withVectorFills(node, next) : withVectorStrokes(node, next);
|
|
2650
2651
|
}
|
|
2651
2652
|
|
|
2653
|
+
// src/vector/pathEffects.ts
|
|
2654
|
+
var EPS2 = 1e-9;
|
|
2655
|
+
var degenerate = (s) => s.every((p) => Math.abs(p[0] - s[0][0]) < EPS2 && Math.abs(p[1] - s[0][1]) < EPS2);
|
|
2656
|
+
function raise(seg) {
|
|
2657
|
+
if (seg.length !== 3) return seg.map((p) => [p[0], p[1]]);
|
|
2658
|
+
const [p0, p1, p2] = seg;
|
|
2659
|
+
return [p0, [p0[0] + 2 / 3 * (p1[0] - p0[0]), p0[1] + 2 / 3 * (p1[1] - p0[1])], [p2[0] + 2 / 3 * (p1[0] - p2[0]), p2[1] + 2 / 3 * (p1[1] - p2[1])], p2];
|
|
2660
|
+
}
|
|
2661
|
+
function subsOf(path) {
|
|
2662
|
+
return pathToContours(path).map((c) => ({ segs: closeContour(c).filter((s) => !degenerate(s)).map(raise), closed: c.closed })).filter((s) => s.segs.length > 0);
|
|
2663
|
+
}
|
|
2664
|
+
var subsToPath = (subs) => contoursToPath(subs.map((s) => ({ segments: s.segs, closed: s.closed })));
|
|
2665
|
+
var unit2 = (x, y) => {
|
|
2666
|
+
const l = Math.hypot(x, y);
|
|
2667
|
+
return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
|
|
2668
|
+
};
|
|
2669
|
+
function startTangent(s) {
|
|
2670
|
+
for (let i = 1; i < s.length; i++) {
|
|
2671
|
+
const u = unit2(s[i][0] - s[0][0], s[i][1] - s[0][1]);
|
|
2672
|
+
if (u) return u;
|
|
2673
|
+
}
|
|
2674
|
+
return [1, 0];
|
|
2675
|
+
}
|
|
2676
|
+
function endTangent(s) {
|
|
2677
|
+
const end = s[s.length - 1];
|
|
2678
|
+
for (let i = s.length - 2; i >= 0; i--) {
|
|
2679
|
+
const u = unit2(end[0] - s[i][0], end[1] - s[i][1]);
|
|
2680
|
+
if (u) return u;
|
|
2681
|
+
}
|
|
2682
|
+
return [1, 0];
|
|
2683
|
+
}
|
|
2684
|
+
function tangentAt(s, t) {
|
|
2685
|
+
if (t <= 1e-9) return startTangent(s);
|
|
2686
|
+
if (t >= 1 - 1e-9) return endTangent(s);
|
|
2687
|
+
const d = segmentDerivative(s, t);
|
|
2688
|
+
return unit2(d[0], d[1]) ?? startTangent(s);
|
|
2689
|
+
}
|
|
2690
|
+
var perp2 = (v2) => [-v2[1], v2[0]];
|
|
2691
|
+
function effectPathLength(path) {
|
|
2692
|
+
let total = 0;
|
|
2693
|
+
for (const s of subsOf(path)) for (const seg of s.segs) total += segmentLength(seg);
|
|
2694
|
+
return total;
|
|
2695
|
+
}
|
|
2696
|
+
function effectPathVertexCount(path) {
|
|
2697
|
+
return subsOf(path).reduce((n, s) => n + s.segs.length + (s.closed ? 0 : 1), 0);
|
|
2698
|
+
}
|
|
2699
|
+
var Measured = class {
|
|
2700
|
+
segs;
|
|
2701
|
+
lens;
|
|
2702
|
+
total;
|
|
2703
|
+
constructor(path) {
|
|
2704
|
+
this.segs = subsOf(path).flatMap((s) => s.segs);
|
|
2705
|
+
this.lens = this.segs.map((s) => segmentLength(s));
|
|
2706
|
+
this.total = this.lens.reduce((a, b) => a + b, 0);
|
|
2707
|
+
}
|
|
2708
|
+
/**
|
|
2709
|
+
* Appends the part between lengths `sa` and `sb`, continuing the last piece when this one
|
|
2710
|
+
* starts where it ends (across the start of a closed path).
|
|
2711
|
+
*/
|
|
2712
|
+
append(sa, sb, out) {
|
|
2713
|
+
for (const piece of trimSegments(this.segs, sa, sb, this.lens)) {
|
|
2714
|
+
if (degenerate(piece)) continue;
|
|
2715
|
+
const last = out[out.length - 1]?.segs;
|
|
2716
|
+
const end = last?.[last.length - 1]?.at(-1);
|
|
2717
|
+
if (!end || Math.hypot(end[0] - piece[0][0], end[1] - piece[0][1]) > 1e-6) out.push({ segs: [piece], closed: false });
|
|
2718
|
+
else last.push(piece);
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
};
|
|
2722
|
+
function trimIntervals(start, end, offsetDegrees) {
|
|
2723
|
+
let s = start / 100;
|
|
2724
|
+
let e = end / 100;
|
|
2725
|
+
if (!Number.isFinite(s) || !Number.isFinite(e)) return null;
|
|
2726
|
+
if (s > e) [s, e] = [e, s];
|
|
2727
|
+
s = Math.min(1, Math.max(0, s));
|
|
2728
|
+
e = Math.min(1, Math.max(0, e));
|
|
2729
|
+
if (e - s >= 1 - 1e-9) return null;
|
|
2730
|
+
if (e - s <= 1e-9) return [];
|
|
2731
|
+
const off = Number.isFinite(offsetDegrees) ? (offsetDegrees / 360 % 1 + 1) % 1 : 0;
|
|
2732
|
+
s += off;
|
|
2733
|
+
e += off;
|
|
2734
|
+
if (e <= 1) return [[s, e]];
|
|
2735
|
+
if (s >= 1) return [[s - 1, e - 1]];
|
|
2736
|
+
return [
|
|
2737
|
+
[s, 1],
|
|
2738
|
+
[0, e - 1]
|
|
2739
|
+
];
|
|
2740
|
+
}
|
|
2741
|
+
function trimPaths(paths, start, end, offsetDegrees = 0) {
|
|
2742
|
+
const iv = trimIntervals(start, end, offsetDegrees);
|
|
2743
|
+
if (!iv) return paths.slice();
|
|
2744
|
+
return paths.map((p) => {
|
|
2745
|
+
const m = new Measured(p);
|
|
2746
|
+
const out = [];
|
|
2747
|
+
if (m.total > 0) for (const [a, b] of iv) m.append(a * m.total, b * m.total, out);
|
|
2748
|
+
return subsToPath(out);
|
|
2749
|
+
});
|
|
2750
|
+
}
|
|
2751
|
+
function trimPathsIndividually(paths, start, end, offsetDegrees = 0) {
|
|
2752
|
+
const iv = trimIntervals(start, end, offsetDegrees);
|
|
2753
|
+
if (!iv) return paths.slice();
|
|
2754
|
+
const ms = paths.map((p) => new Measured(p));
|
|
2755
|
+
const total = ms.reduce((n, m) => n + m.total, 0);
|
|
2756
|
+
let base = 0;
|
|
2757
|
+
return ms.map((m) => {
|
|
2758
|
+
const out = [];
|
|
2759
|
+
for (const [a, b] of iv) {
|
|
2760
|
+
const la = Math.max(0, a * total - base);
|
|
2761
|
+
const lb = Math.min(m.total, b * total - base);
|
|
2762
|
+
if (lb > la) m.append(la, lb, out);
|
|
2763
|
+
}
|
|
2764
|
+
base += m.total;
|
|
2765
|
+
return subsToPath(out);
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2768
|
+
function puckerBloatPaths(paths, amount) {
|
|
2769
|
+
const a = amount / 100;
|
|
2770
|
+
if (!a || !Number.isFinite(a)) return paths.slice();
|
|
2771
|
+
return paths.map((p) => {
|
|
2772
|
+
const verts = p.filter((c) => c.type !== "Z");
|
|
2773
|
+
if (!verts.length) return p;
|
|
2774
|
+
const cx = verts.reduce((s, c) => s + c.x, 0) / verts.length;
|
|
2775
|
+
const cy = verts.reduce((s, c) => s + c.y, 0) / verts.length;
|
|
2776
|
+
const mv = (x, y) => ({ x: x + (cx - x) * a, y: y + (cy - y) * a });
|
|
2777
|
+
const mvh = (x, y) => ({ x: x + (x - cx) * a, y: y + (y - cy) * a });
|
|
2778
|
+
const out = [];
|
|
2779
|
+
let prev = { x: 0, y: 0 };
|
|
2780
|
+
let start = { x: 0, y: 0 };
|
|
2781
|
+
const curve = (c1, c2, q) => {
|
|
2782
|
+
const h1 = mvh(c1.x, c1.y);
|
|
2783
|
+
const h2 = mvh(c2.x, c2.y);
|
|
2784
|
+
const e = mv(q.x, q.y);
|
|
2785
|
+
out.push({ type: "C", x1: h1.x, y1: h1.y, x2: h2.x, y2: h2.y, x: e.x, y: e.y });
|
|
2786
|
+
};
|
|
2787
|
+
for (const c of p) {
|
|
2788
|
+
switch (c.type) {
|
|
2789
|
+
case "M": {
|
|
2790
|
+
const e = mv(c.x, c.y);
|
|
2791
|
+
out.push({ type: "M", x: e.x, y: e.y });
|
|
2792
|
+
prev = start = { x: c.x, y: c.y };
|
|
2793
|
+
break;
|
|
2794
|
+
}
|
|
2795
|
+
case "L":
|
|
2796
|
+
curve(prev, c, c);
|
|
2797
|
+
prev = { x: c.x, y: c.y };
|
|
2798
|
+
break;
|
|
2799
|
+
case "Q": {
|
|
2800
|
+
const c1 = { x: prev.x + 2 / 3 * (c.x1 - prev.x), y: prev.y + 2 / 3 * (c.y1 - prev.y) };
|
|
2801
|
+
const c2 = { x: c.x + 2 / 3 * (c.x1 - c.x), y: c.y + 2 / 3 * (c.y1 - c.y) };
|
|
2802
|
+
curve(c1, c2, c);
|
|
2803
|
+
prev = { x: c.x, y: c.y };
|
|
2804
|
+
break;
|
|
2805
|
+
}
|
|
2806
|
+
case "C":
|
|
2807
|
+
curve({ x: c.x1, y: c.y1 }, { x: c.x2, y: c.y2 }, c);
|
|
2808
|
+
prev = { x: c.x, y: c.y };
|
|
2809
|
+
break;
|
|
2810
|
+
case "Z":
|
|
2811
|
+
if (Math.hypot(prev.x - start.x, prev.y - start.y) > EPS2) curve(prev, start, start);
|
|
2812
|
+
out.push({ type: "Z" });
|
|
2813
|
+
prev = start;
|
|
2814
|
+
break;
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
return out;
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2820
|
+
function roundCornerPaths(paths, radius) {
|
|
2821
|
+
if (!(radius > 0) || !Number.isFinite(radius)) return paths.slice();
|
|
2822
|
+
return paths.map((p) => subpathsToBezier(bezierToSubpaths(p).map((sp) => setSubpathCorners(sp, radius))));
|
|
2823
|
+
}
|
|
2824
|
+
function offsetPaths(paths, amount, join = "miter", miterLimit = 4, copies = 1, copyOffset = 0) {
|
|
2825
|
+
const n = Math.min(1e3, Math.max(1, Math.round(Number.isFinite(copies) ? copies : 1)));
|
|
2826
|
+
return paths.map((p) => {
|
|
2827
|
+
const out = [];
|
|
2828
|
+
for (let i = 0; i < n; i++) out.push(...offsetPath(p, amount * (i + 1 + copyOffset), { join, miterLimit }));
|
|
2829
|
+
return out;
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
function sweepPath(path, dx, dy, fillRule = "nonzero", tolerance = 0.25) {
|
|
2833
|
+
if (!path.length) return [];
|
|
2834
|
+
if (!Number.isFinite(dx) || !Number.isFinite(dy) || Math.hypot(dx, dy) < 1e-9) return path.slice();
|
|
2835
|
+
const bands = [];
|
|
2836
|
+
for (const contour of pathToContours(path)) {
|
|
2837
|
+
const ring = flattenChain(closeContour(contour, true), Math.max(1e-3, tolerance));
|
|
2838
|
+
for (let i = 0; i < ring.length; i++) {
|
|
2839
|
+
let p = ring[i];
|
|
2840
|
+
let q = ring[(i + 1) % ring.length];
|
|
2841
|
+
const turn2 = (q[0] - p[0]) * dy - (q[1] - p[1]) * dx;
|
|
2842
|
+
if (Math.abs(turn2) < 1e-9) continue;
|
|
2843
|
+
if (turn2 < 0) [p, q] = [q, p];
|
|
2844
|
+
bands.push({ type: "M", x: p[0], y: p[1] }, { type: "L", x: q[0], y: q[1] }, { type: "L", x: q[0] + dx, y: q[1] + dy }, { type: "L", x: p[0] + dx, y: p[1] + dy }, { type: "Z" });
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
const moved = transformBezierPath(path, [1, 0, 0, 1, dx, dy]);
|
|
2848
|
+
return pathBoolean("union", [{ path, fillRule }, { path: moved, fillRule }, ...bands.length ? [{ path: bands, fillRule: "nonzero" }] : []]).path;
|
|
2849
|
+
}
|
|
2850
|
+
function twistPaths(paths, angle, center) {
|
|
2851
|
+
let rmax = 0;
|
|
2852
|
+
for (const p of paths) for (const c of p) if (c.type !== "Z") rmax = Math.max(rmax, Math.hypot(c.x - center.x, c.y - center.y));
|
|
2853
|
+
const a = angle * Math.PI / 180;
|
|
2854
|
+
if (!a || rmax <= 1e-9 || !Number.isFinite(a)) return paths.slice();
|
|
2855
|
+
const k = a / rmax;
|
|
2856
|
+
const rot = (v2, th) => {
|
|
2857
|
+
const s = Math.sin(th);
|
|
2858
|
+
const co = Math.cos(th);
|
|
2859
|
+
return [v2[0] * co - v2[1] * s, v2[0] * s + v2[1] * co];
|
|
2860
|
+
};
|
|
2861
|
+
const map = (p) => {
|
|
2862
|
+
const v2 = [p[0] - center.x, p[1] - center.y];
|
|
2863
|
+
const r = rot(v2, k * Math.hypot(v2[0], v2[1]));
|
|
2864
|
+
return [center.x + r[0], center.y + r[1]];
|
|
2865
|
+
};
|
|
2866
|
+
const jac = (p, dv) => {
|
|
2867
|
+
const v2 = [p[0] - center.x, p[1] - center.y];
|
|
2868
|
+
const r = Math.hypot(v2[0], v2[1]);
|
|
2869
|
+
let w = dv;
|
|
2870
|
+
if (r > 1e-12) {
|
|
2871
|
+
const f = k * (v2[0] * dv[0] + v2[1] * dv[1]) / r;
|
|
2872
|
+
w = [dv[0] - v2[1] * f, dv[1] + v2[0] * f];
|
|
2873
|
+
}
|
|
2874
|
+
return rot(w, k * r);
|
|
2875
|
+
};
|
|
2876
|
+
return paths.map((p) => {
|
|
2877
|
+
const out = [];
|
|
2878
|
+
for (const sub5 of subsOf(p)) {
|
|
2879
|
+
const segs = [];
|
|
2880
|
+
for (const seg of sub5.segs) {
|
|
2881
|
+
const cubic = seg.length === 2 ? [seg[0], seg[0], seg[1], seg[1]] : seg;
|
|
2882
|
+
const len2 = segmentLength(seg);
|
|
2883
|
+
const m = Math.min(64, Math.max(1, Math.ceil(Math.abs(a) * len2 / rmax / 0.15 + len2 / 50)));
|
|
2884
|
+
for (let i = 0; i < m; i++) {
|
|
2885
|
+
const q = subSegment(cubic, i / m, (i + 1) / m);
|
|
2886
|
+
const p0 = map(q[0]);
|
|
2887
|
+
const p3 = map(q[3]);
|
|
2888
|
+
const h1 = jac(q[0], [q[1][0] - q[0][0], q[1][1] - q[0][1]]);
|
|
2889
|
+
const h2 = jac(q[3], [q[2][0] - q[3][0], q[2][1] - q[3][1]]);
|
|
2890
|
+
segs.push([p0, [p0[0] + h1[0], p0[1] + h1[1]], [p3[0] + h2[0], p3[1] + h2[1]], p3]);
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
out.push({ segs, closed: sub5.closed });
|
|
2894
|
+
}
|
|
2895
|
+
return subsToPath(out);
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
function resample(sub5, m) {
|
|
2899
|
+
const segs = sub5.segs;
|
|
2900
|
+
const n = segs.length;
|
|
2901
|
+
const lens = segs.map((s) => segmentLength(s));
|
|
2902
|
+
const out = [];
|
|
2903
|
+
for (let j = 0; j < n; j++) {
|
|
2904
|
+
const piece = lens[j] / m;
|
|
2905
|
+
for (let k = 0; k < m; k++) {
|
|
2906
|
+
if (k === 0) {
|
|
2907
|
+
const tout = startTangent(segs[j]);
|
|
2908
|
+
const prev = sub5.closed || j > 0 ? (j + n - 1) % n : null;
|
|
2909
|
+
let tan = tout;
|
|
2910
|
+
if (prev !== null) {
|
|
2911
|
+
const tin = endTangent(segs[prev]);
|
|
2912
|
+
tan = unit2(tin[0] + tout[0], tin[1] + tout[1]) ?? tout;
|
|
2913
|
+
}
|
|
2914
|
+
out.push({ p: segs[j][0], tan, lenPrev: prev === null ? 0 : lens[prev] / m, lenNext: piece });
|
|
2915
|
+
} else {
|
|
2916
|
+
const t = segmentParamAtLength(segs[j], piece * k, lens[j]);
|
|
2917
|
+
out.push({ p: segmentPoint(segs[j], t), tan: tangentAt(segs[j], t), lenPrev: piece, lenNext: piece });
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
if (!sub5.closed) {
|
|
2922
|
+
const last = segs[n - 1];
|
|
2923
|
+
out.push({ p: last[last.length - 1], tan: endTangent(last), lenPrev: lens[n - 1] / m, lenNext: 0 });
|
|
2924
|
+
}
|
|
2925
|
+
return out;
|
|
2926
|
+
}
|
|
2927
|
+
function emit(samples, disp, closed, smooth, handle) {
|
|
2928
|
+
const n = samples.length;
|
|
2929
|
+
if (!n) return null;
|
|
2930
|
+
const pt2 = (i) => [samples[i].p[0] + disp[i][0], samples[i].p[1] + disp[i][1]];
|
|
2931
|
+
const segs = [];
|
|
2932
|
+
const count = closed ? n : n - 1;
|
|
2933
|
+
for (let i = 0; i < count; i++) {
|
|
2934
|
+
const j = (i + 1) % n;
|
|
2935
|
+
const a = samples[i];
|
|
2936
|
+
const b = samples[j];
|
|
2937
|
+
const p = pt2(i);
|
|
2938
|
+
const q = pt2(j);
|
|
2939
|
+
if (smooth) segs.push([p, [p[0] + a.tan[0] * a.lenNext * handle, p[1] + a.tan[1] * a.lenNext * handle], [q[0] - b.tan[0] * b.lenPrev * handle, q[1] - b.tan[1] * b.lenPrev * handle], q]);
|
|
2940
|
+
else segs.push([p, q]);
|
|
2941
|
+
}
|
|
2942
|
+
if (!segs.length) return null;
|
|
2943
|
+
return { segs, closed };
|
|
2944
|
+
}
|
|
2945
|
+
function zigzagPaths(paths, size, ridges, smooth = false) {
|
|
2946
|
+
const m = Math.min(1e3, Math.max(0, Math.round(Number.isFinite(ridges) ? ridges : 0))) + 1;
|
|
2947
|
+
return paths.map((p) => {
|
|
2948
|
+
const out = [];
|
|
2949
|
+
for (const sub5 of subsOf(p)) {
|
|
2950
|
+
const s = resample(sub5, m);
|
|
2951
|
+
const disp = s.map((x, i) => {
|
|
2952
|
+
const n = perp2(x.tan);
|
|
2953
|
+
const d = i % 2 === 0 ? size : -size;
|
|
2954
|
+
return [n[0] * d, n[1] * d];
|
|
2955
|
+
});
|
|
2956
|
+
const e = emit(s, disp, sub5.closed, smooth, 0.36);
|
|
2957
|
+
if (e) out.push(e);
|
|
2958
|
+
}
|
|
2959
|
+
return subsToPath(out);
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
var MASK64 = (1n << 64n) - 1n;
|
|
2963
|
+
function splitmix(x) {
|
|
2964
|
+
x = x + 0x9e3779b97f4a7c15n & MASK64;
|
|
2965
|
+
let z = x;
|
|
2966
|
+
z = (z ^ z >> 30n) * 0xbf58476d1ce4e5b9n & MASK64;
|
|
2967
|
+
z = (z ^ z >> 27n) * 0x94d049bb133111ebn & MASK64;
|
|
2968
|
+
return z ^ z >> 31n;
|
|
2969
|
+
}
|
|
2970
|
+
function hash(seed, idx, t) {
|
|
2971
|
+
const h = splitmix(splitmix(splitmix(seed) ^ idx) ^ BigInt.asUintN(64, BigInt(t)));
|
|
2972
|
+
return Number(h >> 11n) / 2 ** 53 * 2 - 1;
|
|
2973
|
+
}
|
|
2974
|
+
function noise(seed, idx, t) {
|
|
2975
|
+
const i = Math.floor(t);
|
|
2976
|
+
const f = t - i;
|
|
2977
|
+
const p0 = hash(seed, idx, i - 1);
|
|
2978
|
+
const p1 = hash(seed, idx, i);
|
|
2979
|
+
const p2 = hash(seed, idx, i + 1);
|
|
2980
|
+
const p3 = hash(seed, idx, i + 2);
|
|
2981
|
+
const v2 = 0.5 * (2 * p1 + (-p0 + p2) * f + (2 * p0 - 5 * p1 + 4 * p2 - p3) * f * f + (-p0 + 3 * p1 - 3 * p2 + p3) * f * f * f);
|
|
2982
|
+
return Math.min(1, Math.max(-1, v2));
|
|
2983
|
+
}
|
|
2984
|
+
function wigglePaths(paths, w) {
|
|
2985
|
+
if (!w.size || !Number.isFinite(w.size)) return paths.slice();
|
|
2986
|
+
const m = Math.min(1e3, Math.max(0, Math.round(Number.isFinite(w.detail) ? w.detail : 0))) + 1;
|
|
2987
|
+
const seed = BigInt.asUintN(64, BigInt(Math.round(Number.isFinite(w.seed) ? w.seed : 0)));
|
|
2988
|
+
const t = (Number.isFinite(w.time) ? w.time : 0) * (Number.isFinite(w.speed) ? w.speed : 0) + (Number.isFinite(w.phase) ? w.phase : 0) / 360;
|
|
2989
|
+
const corr = Math.min(1, Math.max(0, (Number.isFinite(w.correlation) ? w.correlation : 0) / 100));
|
|
2990
|
+
const sx = noise(seed, MASK64, t);
|
|
2991
|
+
const sy = noise(seed, MASK64 - 1n, t);
|
|
2992
|
+
let idx = 0;
|
|
2993
|
+
return paths.map((p) => {
|
|
2994
|
+
const out = [];
|
|
2995
|
+
for (const sub5 of subsOf(p)) {
|
|
2996
|
+
const s = resample(sub5, m);
|
|
2997
|
+
const disp = s.map(() => {
|
|
2998
|
+
const i = BigInt(idx);
|
|
2999
|
+
const ox = noise(seed, i * 2n, t + idx * 0.37);
|
|
3000
|
+
const oy = noise(seed, i * 2n + 1n, t + idx * 0.61);
|
|
3001
|
+
idx++;
|
|
3002
|
+
return [(sx * corr + ox * (1 - corr)) * w.size, (sy * corr + oy * (1 - corr)) * w.size];
|
|
3003
|
+
});
|
|
3004
|
+
const e = emit(s, disp, sub5.closed, w.smooth, 1 / 3);
|
|
3005
|
+
if (e) out.push(e);
|
|
3006
|
+
}
|
|
3007
|
+
return subsToPath(out);
|
|
3008
|
+
});
|
|
3009
|
+
}
|
|
3010
|
+
var MERGE_OPS = { add: "union", subtract: "difference", intersect: "intersection", exclude: "xor" };
|
|
3011
|
+
function mergePaths(paths, mode = "merge") {
|
|
3012
|
+
if (mode === "merge" || !(mode in MERGE_OPS)) return paths.flat();
|
|
3013
|
+
const inputs = paths.filter((p) => p.length > 0).map((path) => ({ path, fillRule: "nonzero" }));
|
|
3014
|
+
if (!inputs.length) return [];
|
|
3015
|
+
return pathBoolean(MERGE_OPS[mode], inputs).path;
|
|
3016
|
+
}
|
|
3017
|
+
function vectorRepeaterCopies(r, pivot) {
|
|
3018
|
+
const copies = Math.max(0, Number.isFinite(r.copies) ? Math.min(r.copies, 1e3) : 0);
|
|
3019
|
+
const n = Math.ceil(copies);
|
|
3020
|
+
const offset = Number.isFinite(r.offset) ? r.offset : 0;
|
|
3021
|
+
const so = (r.startOpacity ?? 100) / 100;
|
|
3022
|
+
const eo = (r.endOpacity ?? 100) / 100;
|
|
3023
|
+
const fract = copies - Math.floor(copies);
|
|
3024
|
+
const out = [];
|
|
3025
|
+
for (let i = 0; i < n; i++) {
|
|
3026
|
+
const k = i + offset;
|
|
3027
|
+
let m = translateMatrix(-pivot.x, -pivot.y);
|
|
3028
|
+
m = multiplyMatrices(m, scaleMatrix(Math.pow((r.scale?.x ?? 100) / 100, k), Math.pow((r.scale?.y ?? 100) / 100, k)));
|
|
3029
|
+
m = multiplyMatrices(m, rotateMatrix((r.rotation ?? 0) * k));
|
|
3030
|
+
m = multiplyMatrices(m, translateMatrix(pivot.x + (r.position?.x ?? 0) * k, pivot.y + (r.position?.y ?? 0) * k));
|
|
3031
|
+
const t = n > 1 ? i / (n - 1) : 0;
|
|
3032
|
+
const partial = i + 1 === n && fract > 0 ? fract : 1;
|
|
3033
|
+
out.push({ index: i, matrix: m, opacity: Math.min(1, Math.max(0, (so + (eo - so) * t) * partial)) });
|
|
3034
|
+
}
|
|
3035
|
+
return out;
|
|
3036
|
+
}
|
|
3037
|
+
function repeatPaths(paths, r, pivot) {
|
|
3038
|
+
return vectorRepeaterCopies(r, pivot).map((copy) => ({ copy, paths: paths.map((p) => transformBezierPath(p, copy.matrix)) }));
|
|
3039
|
+
}
|
|
3040
|
+
function pathsCenter(paths) {
|
|
3041
|
+
const b = bezierPathBounds(paths.flat());
|
|
3042
|
+
return b ? { x: (b.minX + b.maxX) / 2, y: (b.minY + b.maxY) / 2 } : { x: 0, y: 0 };
|
|
3043
|
+
}
|
|
3044
|
+
|
|
2652
3045
|
// src/vector/pathMap.ts
|
|
2653
3046
|
var MAX_PIECES = 1 << 20;
|
|
2654
3047
|
var MAX_DEPTH = 6;
|
|
@@ -3025,14 +3418,14 @@ function lineHits(p, q, seg) {
|
|
|
3025
3418
|
return out;
|
|
3026
3419
|
}
|
|
3027
3420
|
var near = (a, b, eps) => Math.abs(a[0] - b[0]) <= eps && Math.abs(a[1] - b[1]) <= eps;
|
|
3028
|
-
var
|
|
3421
|
+
var degenerate2 = (seg, eps) => seg.every((p) => near(p, seg[0], eps));
|
|
3029
3422
|
function arc(l, a, b, eps) {
|
|
3030
3423
|
const n = l.segs.length;
|
|
3031
3424
|
const segs = [];
|
|
3032
3425
|
const chord = [];
|
|
3033
3426
|
const push = (k, t0, t1) => {
|
|
3034
3427
|
const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : subSegment(l.segs[k], t0, t1);
|
|
3035
|
-
if (!
|
|
3428
|
+
if (!degenerate2(piece, eps)) {
|
|
3036
3429
|
segs.push(piece);
|
|
3037
3430
|
chord.push(l.chord[k]);
|
|
3038
3431
|
}
|
|
@@ -4109,8 +4502,8 @@ function createOutlineTextShaper(fonts, options = {}) {
|
|
|
4109
4502
|
const fallbackKey = (options.fallback ?? entries[0].family).toLowerCase();
|
|
4110
4503
|
const pick = (font) => {
|
|
4111
4504
|
if (font.postScriptName) {
|
|
4112
|
-
const
|
|
4113
|
-
if (
|
|
4505
|
+
const exact2 = entries.find((e) => e.postScript === font.postScriptName);
|
|
4506
|
+
if (exact2) return { entry: exact2, available: true };
|
|
4114
4507
|
}
|
|
4115
4508
|
const key = font.family.trim().toLowerCase();
|
|
4116
4509
|
let pool = entries.filter((e) => e.key === key);
|
|
@@ -5872,7 +6265,9 @@ var VECTOR_EFFECT_DEFAULTS = {
|
|
|
5872
6265
|
endOpacity: 100,
|
|
5873
6266
|
composite: "below"
|
|
5874
6267
|
},
|
|
5875
|
-
envelope: { style: "arc", bend: 50, horizontal: 0, vertical: 0, orientation: "horizontal", rows: 2, columns: 2, mesh: null }
|
|
6268
|
+
envelope: { style: "arc", bend: 50, horizontal: 0, vertical: 0, orientation: "horizontal", rows: 2, columns: 2, mesh: null },
|
|
6269
|
+
contour: { direction: "inside", steps: 4, offset: 6, join: "round", miterLimit: 4, fillColor: "#ffffff", strokeColor: null, acceleration: 0 },
|
|
6270
|
+
blockShadow: { angle: 45, distance: 20, color: "#1e293b", opacity: 100, endOpacity: 100 }
|
|
5876
6271
|
};
|
|
5877
6272
|
var VECTOR_EFFECT_LABELS = {
|
|
5878
6273
|
trim: "Trim Paths",
|
|
@@ -5884,7 +6279,9 @@ var VECTOR_EFFECT_LABELS = {
|
|
|
5884
6279
|
offset: "Offset Paths",
|
|
5885
6280
|
merge: "Merge Paths",
|
|
5886
6281
|
repeater: "Repeater",
|
|
5887
|
-
envelope: "Envelope"
|
|
6282
|
+
envelope: "Envelope",
|
|
6283
|
+
contour: "Contour",
|
|
6284
|
+
blockShadow: "Block Shadow"
|
|
5888
6285
|
};
|
|
5889
6286
|
var VECTOR_EFFECT_PARAM_SPECS = {
|
|
5890
6287
|
trim: [
|
|
@@ -5940,18 +6337,52 @@ var VECTOR_EFFECT_PARAM_SPECS = {
|
|
|
5940
6337
|
],
|
|
5941
6338
|
merge: [
|
|
5942
6339
|
{
|
|
5943
|
-
key: "mode",
|
|
5944
|
-
label: "Mode",
|
|
6340
|
+
key: "mode",
|
|
6341
|
+
label: "Mode",
|
|
6342
|
+
kind: "select",
|
|
6343
|
+
options: [
|
|
6344
|
+
{ value: "merge", label: "Merge" },
|
|
6345
|
+
{ value: "add", label: "Add" },
|
|
6346
|
+
{ value: "subtract", label: "Subtract" },
|
|
6347
|
+
{ value: "intersect", label: "Intersect" },
|
|
6348
|
+
{ value: "exclude", label: "Exclude" }
|
|
6349
|
+
]
|
|
6350
|
+
}
|
|
6351
|
+
],
|
|
6352
|
+
contour: [
|
|
6353
|
+
{
|
|
6354
|
+
key: "direction",
|
|
6355
|
+
label: "Direction",
|
|
6356
|
+
kind: "select",
|
|
6357
|
+
options: [
|
|
6358
|
+
{ value: "inside", label: "Inside" },
|
|
6359
|
+
{ value: "outside", label: "Outside" },
|
|
6360
|
+
{ value: "center", label: "To centre" }
|
|
6361
|
+
]
|
|
6362
|
+
},
|
|
6363
|
+
{ key: "steps", label: "Steps", kind: "number", min: 1, max: 200, step: 1, slider: true },
|
|
6364
|
+
{ key: "offset", label: "Offset", kind: "number", min: 0.1, max: 200, step: 0.5, slider: true },
|
|
6365
|
+
{ key: "fillColor", label: "Fill to", kind: "color" },
|
|
6366
|
+
{ key: "strokeColor", label: "Stroke to", kind: "color" },
|
|
6367
|
+
{ key: "acceleration", label: "Acceleration", kind: "number", min: -100, max: 100, step: 1, slider: true },
|
|
6368
|
+
{
|
|
6369
|
+
key: "join",
|
|
6370
|
+
label: "Join",
|
|
5945
6371
|
kind: "select",
|
|
5946
6372
|
options: [
|
|
5947
|
-
{ value: "
|
|
5948
|
-
{ value: "
|
|
5949
|
-
{ value: "
|
|
5950
|
-
{ value: "intersect", label: "Intersect" },
|
|
5951
|
-
{ value: "exclude", label: "Exclude" }
|
|
6373
|
+
{ value: "miter", label: "Miter" },
|
|
6374
|
+
{ value: "round", label: "Round" },
|
|
6375
|
+
{ value: "bevel", label: "Bevel" }
|
|
5952
6376
|
]
|
|
5953
6377
|
}
|
|
5954
6378
|
],
|
|
6379
|
+
blockShadow: [
|
|
6380
|
+
{ key: "angle", label: "Angle \xB0", kind: "number", min: -360, max: 360, step: 1, slider: true },
|
|
6381
|
+
{ key: "distance", label: "Distance", kind: "number", min: 0, max: 2e3, step: 1, slider: true },
|
|
6382
|
+
{ key: "color", label: "Colour", kind: "color" },
|
|
6383
|
+
{ key: "opacity", label: "Opacity %", kind: "number", min: 0, max: 100, step: 1, slider: true },
|
|
6384
|
+
{ key: "endOpacity", label: "End opacity %", kind: "number", min: 0, max: 100, step: 1, slider: true }
|
|
6385
|
+
],
|
|
5955
6386
|
repeater: [
|
|
5956
6387
|
{ key: "copies", label: "Copies", kind: "number", min: 0, max: 50, step: 0.1, slider: true },
|
|
5957
6388
|
{ key: "offset", label: "Offset", kind: "number", min: -10, max: 10, step: 0.1, slider: true },
|
|
@@ -6041,6 +6472,7 @@ function normalizeVectorEffectParams(type, params, base) {
|
|
|
6041
6472
|
else if (spec.kind === "boolean" && typeof v2 === "boolean") out[spec.key] = v2;
|
|
6042
6473
|
else if (spec.kind === "vec2" && isVec2(v2)) out[spec.key] = { x: v2.x, y: v2.y };
|
|
6043
6474
|
else if (spec.kind === "select" && spec.options.some((o) => o.value === v2)) out[spec.key] = v2;
|
|
6475
|
+
else if (spec.kind === "color" && (v2 === null || typeof v2 === "string" && parseVectorColor(v2))) out[spec.key] = v2;
|
|
6044
6476
|
}
|
|
6045
6477
|
if (type === "envelope") {
|
|
6046
6478
|
const o = out;
|
|
@@ -6066,7 +6498,7 @@ function activeVectorEffects(node) {
|
|
|
6066
6498
|
return node.effects?.filter(isEnabled) ?? [];
|
|
6067
6499
|
}
|
|
6068
6500
|
function hasOwnVectorAppearance(node) {
|
|
6069
|
-
return node.type === "text" || node.type === "instance" || node.type === "group" && !!node.liveBoolean || isVectorFrame(node) || !!node.effects?.some(isEnabled);
|
|
6501
|
+
return node.type === "text" || node.type === "instance" || node.type === "group" && !!node.liveBoolean || isVectorFrame(node) || isVectorSymmetryGroup(node) || !!node.effects?.some(isEnabled);
|
|
6070
6502
|
}
|
|
6071
6503
|
var liveCache = /* @__PURE__ */ new WeakMap();
|
|
6072
6504
|
function hasVectorAppearance(node) {
|
|
@@ -6200,6 +6632,73 @@ var mapPaths = (shapes, fn) => {
|
|
|
6200
6632
|
const out = fn(shapes.map((s) => s.path));
|
|
6201
6633
|
return shapes.map((s, i) => ({ ...s, path: out[i] ?? [] }));
|
|
6202
6634
|
};
|
|
6635
|
+
function blendPaint(from, to, t) {
|
|
6636
|
+
if (to === null || typeof from !== "string") return from;
|
|
6637
|
+
const a = parseVectorColor(from);
|
|
6638
|
+
const b = parseVectorColor(to);
|
|
6639
|
+
if (!a || !b) return from;
|
|
6640
|
+
const h = (v2) => Math.round(Math.min(1, Math.max(0, v2)) * 255).toString(16).padStart(2, "0");
|
|
6641
|
+
const m = a.map((v2, k) => v2 + (b[k] - v2) * t);
|
|
6642
|
+
return `#${h(m[0])}${h(m[1])}${h(m[2])}`;
|
|
6643
|
+
}
|
|
6644
|
+
function contourShapes(shapes, c) {
|
|
6645
|
+
const out = [];
|
|
6646
|
+
const step = Math.max(0.01, c.offset);
|
|
6647
|
+
const outside = c.direction === "outside";
|
|
6648
|
+
for (const s of shapes) {
|
|
6649
|
+
let n = Math.min(200, Math.max(1, Math.round(c.steps)));
|
|
6650
|
+
if (c.direction === "center") {
|
|
6651
|
+
const b = bezierPathBounds(s.path);
|
|
6652
|
+
n = b ? Math.min(200, Math.max(1, Math.ceil(Math.min(b.maxX - b.minX, b.maxY - b.minY) / 2 / step))) : 0;
|
|
6653
|
+
}
|
|
6654
|
+
out.push({ ...s, order: [outside ? n : 0, ...s.order] });
|
|
6655
|
+
const power = 2 ** (Math.min(100, Math.max(-100, c.acceleration)) / 50);
|
|
6656
|
+
for (let k = 1; k <= n; k++) {
|
|
6657
|
+
const [path] = offsetPaths([s.path], (outside ? 1 : -1) * step * k, c.join, c.miterLimit, 1, 0);
|
|
6658
|
+
if (!path?.length) break;
|
|
6659
|
+
const t = (k / n) ** power;
|
|
6660
|
+
const source = { ...s.source, fill: blendPaint(s.source.fill, c.fillColor, t) ?? null, stroke: blendPaint(s.source.stroke, c.strokeColor, t) ?? null, fills: void 0, strokes: void 0 };
|
|
6661
|
+
out.push({ source, path, opacity: s.opacity, order: [outside ? n - k : k, ...s.order] });
|
|
6662
|
+
}
|
|
6663
|
+
}
|
|
6664
|
+
return out;
|
|
6665
|
+
}
|
|
6666
|
+
function blockShadowShapes(shapes, b) {
|
|
6667
|
+
const rad2 = b.angle * Math.PI / 180;
|
|
6668
|
+
const u = { x: Math.cos(rad2), y: Math.sin(rad2) };
|
|
6669
|
+
const distance = Math.max(0, b.distance);
|
|
6670
|
+
const shadows = [];
|
|
6671
|
+
for (const s of shapes) {
|
|
6672
|
+
const color2 = b.color ?? (typeof s.source.fill === "string" && s.source.fill !== "none" ? s.source.fill : "#000000");
|
|
6673
|
+
const path = sweepPath(s.path, u.x * distance, u.y * distance, s.source.fillRule === "evenodd" ? "evenodd" : "nonzero");
|
|
6674
|
+
const box = bezierPathBounds(s.path);
|
|
6675
|
+
if (!path.length || !box) continue;
|
|
6676
|
+
let fill = color2;
|
|
6677
|
+
const near3 = Math.min(100, Math.max(0, b.opacity));
|
|
6678
|
+
const far = Math.min(100, Math.max(0, b.endOpacity));
|
|
6679
|
+
const top = Math.max(near3, far);
|
|
6680
|
+
if (near3 !== far && distance > 0) {
|
|
6681
|
+
const along2 = [box.minX * u.x + box.minY * u.y, box.maxX * u.x + box.minY * u.y, box.minX * u.x + box.maxY * u.y, box.maxX * u.x + box.maxY * u.y];
|
|
6682
|
+
const t0 = Math.min(...along2);
|
|
6683
|
+
const t1 = Math.max(...along2) + distance;
|
|
6684
|
+
const c = { x: (box.minX + box.maxX) / 2, y: (box.minY + box.maxY) / 2 };
|
|
6685
|
+
const k = c.x * u.x + c.y * u.y;
|
|
6686
|
+
fill = {
|
|
6687
|
+
type: "linear",
|
|
6688
|
+
units: "userSpaceOnUse",
|
|
6689
|
+
start: { x: c.x + u.x * (t0 - k), y: c.y + u.y * (t0 - k) },
|
|
6690
|
+
end: { x: c.x + u.x * (t1 - k), y: c.y + u.y * (t1 - k) },
|
|
6691
|
+
stops: [
|
|
6692
|
+
{ offset: 0, color: color2, opacity: near3 / top },
|
|
6693
|
+
{ offset: 1, color: color2, opacity: far / top }
|
|
6694
|
+
]
|
|
6695
|
+
};
|
|
6696
|
+
}
|
|
6697
|
+
const source = { ...s.source, fill, stroke: null, fills: void 0, strokes: void 0, fillRule: "nonzero" };
|
|
6698
|
+
shadows.push({ source, path, opacity: s.opacity * top / 100, order: [0, ...s.order] });
|
|
6699
|
+
}
|
|
6700
|
+
return [...shadows, ...shapes.map((s) => ({ ...s, order: [1, ...s.order] }))];
|
|
6701
|
+
}
|
|
6203
6702
|
function applyVectorEffects(shapes, effects) {
|
|
6204
6703
|
let list = shapes.slice();
|
|
6205
6704
|
for (const e of effects ?? []) {
|
|
@@ -6240,6 +6739,12 @@ function applyVectorEffects(shapes, effects) {
|
|
|
6240
6739
|
list = mapPaths(list, (p) => envelopePaths(p, params));
|
|
6241
6740
|
break;
|
|
6242
6741
|
}
|
|
6742
|
+
case "contour":
|
|
6743
|
+
list = contourShapes(list, e.params);
|
|
6744
|
+
break;
|
|
6745
|
+
case "blockShadow":
|
|
6746
|
+
list = blockShadowShapes(list, e.params);
|
|
6747
|
+
break;
|
|
6243
6748
|
case "repeater": {
|
|
6244
6749
|
const r = e.params;
|
|
6245
6750
|
const copies = repeatPaths(
|
|
@@ -6333,8 +6838,8 @@ function vectorAppearanceShapes(node) {
|
|
|
6333
6838
|
shapeCache.set(node, [vectorTextShaperGeneration(), shapes]);
|
|
6334
6839
|
return shapes;
|
|
6335
6840
|
}
|
|
6336
|
-
if (isVectorFrame(node)) {
|
|
6337
|
-
const shapes = vectorAppearanceShapes(vectorFrameDrawn(node));
|
|
6841
|
+
if (isVectorFrame(node) || isVectorSymmetryGroup(node)) {
|
|
6842
|
+
const shapes = vectorAppearanceShapes(isVectorFrame(node) ? vectorFrameDrawn(node) : vectorSymmetryDrawn(node));
|
|
6338
6843
|
shapeCache.set(node, [vectorTextShaperGeneration(), shapes]);
|
|
6339
6844
|
return shapes;
|
|
6340
6845
|
}
|
|
@@ -6396,8 +6901,8 @@ function expandVectorAppearance(node) {
|
|
|
6396
6901
|
expandCache.set(node, [vectorTextShaperGeneration(), drawn]);
|
|
6397
6902
|
return drawn;
|
|
6398
6903
|
}
|
|
6399
|
-
if (isVectorFrame(node)) {
|
|
6400
|
-
const drawn = vectorFrameDrawn(node);
|
|
6904
|
+
if (isVectorFrame(node) || isVectorSymmetryGroup(node)) {
|
|
6905
|
+
const drawn = isVectorFrame(node) ? vectorFrameDrawn(node) : vectorSymmetryDrawn(node);
|
|
6401
6906
|
const out2 = hasVectorAppearance(drawn) ? expandVectorAppearance(drawn) : drawn;
|
|
6402
6907
|
expandCache.set(node, [vectorTextShaperGeneration(), out2]);
|
|
6403
6908
|
return out2;
|
|
@@ -6789,7 +7294,7 @@ var add4 = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
|
|
|
6789
7294
|
var sub4 = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
|
|
6790
7295
|
var scale2 = (a, k) => ({ x: a.x * k, y: a.y * k });
|
|
6791
7296
|
var left = (d) => ({ x: d.y, y: -d.x });
|
|
6792
|
-
var
|
|
7297
|
+
var unit3 = (v2) => {
|
|
6793
7298
|
const l = Math.hypot(v2.x, v2.y);
|
|
6794
7299
|
return l > 1e-12 ? { x: v2.x / l, y: v2.y / l } : { x: 1, y: 0 };
|
|
6795
7300
|
};
|
|
@@ -6846,7 +7351,7 @@ function vectorStrokeGradientDefs(g, id, node, options = {}) {
|
|
|
6846
7351
|
for (let i = 0; i < segCount; i++) {
|
|
6847
7352
|
const a = pts[i];
|
|
6848
7353
|
const b = pts[(i + 1) % n];
|
|
6849
|
-
dirs.push(
|
|
7354
|
+
dirs.push(unit3(sub4(b, a)));
|
|
6850
7355
|
lengths.push(lengths[i] + Math.hypot(b.x - a.x, b.y - a.y));
|
|
6851
7356
|
}
|
|
6852
7357
|
const total = lengths[segCount];
|
|
@@ -7344,9 +7849,9 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
7344
7849
|
};
|
|
7345
7850
|
const emitInstance = (node, depth) => {
|
|
7346
7851
|
const data = { symbolId: node.symbolId, ...node.scaleStrokes === false ? { scaleStrokes: false } : {}, ...node.overrides ? { overrides: node.overrides } : {} };
|
|
7347
|
-
const
|
|
7852
|
+
const exact2 = !node.overrides && node.scaleStrokes !== false && !node.effects?.length && !node.filters?.length;
|
|
7348
7853
|
const ref = symbolRef(node.symbolId);
|
|
7349
|
-
if (
|
|
7854
|
+
if (exact2 && ref) {
|
|
7350
7855
|
lines.push(`${indent(depth)}<use${common(node)} href="#${escapeAttr2(ref)}" data-vector-instance="${escapeAttr2(JSON.stringify(data))}"/>`);
|
|
7351
7856
|
return;
|
|
7352
7857
|
}
|
|
@@ -7397,7 +7902,8 @@ function vectorDocumentToSvg(doc, options = {}) {
|
|
|
7397
7902
|
const live2 = {
|
|
7398
7903
|
kind: node.type,
|
|
7399
7904
|
...node.effects?.length ? { effects: node.effects } : {},
|
|
7400
|
-
...node.type === "group" && node.liveBoolean ? { liveBoolean: node.liveBoolean } : {}
|
|
7905
|
+
...node.type === "group" && node.liveBoolean ? { liveBoolean: node.liveBoolean } : {},
|
|
7906
|
+
...node.type === "group" && node.symmetry ? { symmetry: node.symmetry } : {}
|
|
7401
7907
|
};
|
|
7402
7908
|
lines.push(`${indent(depth)}<g${common(node)} data-vector-live="${escapeAttr2(JSON.stringify(live2))}">`);
|
|
7403
7909
|
lines.push(`${indent(depth + 1)}<g data-vector-part="appearance">`);
|
|
@@ -7960,6 +8466,8 @@ function parseVectorSvg(svg, options = {}) {
|
|
|
7960
8466
|
const group = { id, type: "group", ...meta, children: inner };
|
|
7961
8467
|
if (effects.length) group.effects = transformVectorEffects(effects, m);
|
|
7962
8468
|
if (liveBoolean) group.liveBoolean = liveBoolean;
|
|
8469
|
+
const symmetry = sanitizeVectorSymmetry(raw.symmetry);
|
|
8470
|
+
if (symmetry) group.symmetry = transformVectorSymmetry(symmetry, m);
|
|
7963
8471
|
const blend = blendFromAttribute(g.attrs["data-vector-blend"], m);
|
|
7964
8472
|
if (blend) group.blend = blend;
|
|
7965
8473
|
return group;
|
|
@@ -8424,7 +8932,7 @@ function vectorNodesNear(nodes, rect, pad = 0) {
|
|
|
8424
8932
|
}
|
|
8425
8933
|
|
|
8426
8934
|
// src/vector/geometry.ts
|
|
8427
|
-
var
|
|
8935
|
+
var EPS3 = 1e-9;
|
|
8428
8936
|
var cloneAnchor = (a) => ({
|
|
8429
8937
|
x: a.x,
|
|
8430
8938
|
y: a.y,
|
|
@@ -8450,7 +8958,7 @@ function hasPaint(paint) {
|
|
|
8450
8958
|
return true;
|
|
8451
8959
|
}
|
|
8452
8960
|
var handleOf = (a, side) => a[side] ?? { x: a.x, y: a.y };
|
|
8453
|
-
var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) <
|
|
8961
|
+
var isOnAnchor = (a, h) => !h || Math.abs(h.x - a.x) < EPS3 && Math.abs(h.y - a.y) < EPS3;
|
|
8454
8962
|
function subpathSegmentCount(sp) {
|
|
8455
8963
|
const n = sp.anchors.length;
|
|
8456
8964
|
if (n < 2) return 0;
|
|
@@ -8732,8 +9240,9 @@ function bakeIntoGeometry(node, m) {
|
|
|
8732
9240
|
if (node.type === "group") {
|
|
8733
9241
|
const group = node;
|
|
8734
9242
|
const children = group.children.map((c) => transformVectorNode(c, m));
|
|
9243
|
+
const sym2 = group.symmetry ? { symmetry: transformVectorSymmetry(group.symmetry, m) } : {};
|
|
8735
9244
|
const spine = group.blend?.spine;
|
|
8736
|
-
if (!spine) return { ...group, children };
|
|
9245
|
+
if (!spine) return { ...group, ...sym2, children };
|
|
8737
9246
|
const moved = { closed: spine.closed, anchors: spine.anchors.map((a) => mapAnchor(a, m)) };
|
|
8738
9247
|
return { ...group, children, blend: { ...group.blend, spine: moved } };
|
|
8739
9248
|
}
|
|
@@ -8837,7 +9346,7 @@ function moveAnchorHandle(a, side, pos, breakHandles = false) {
|
|
|
8837
9346
|
const dy = pos.y - a.y;
|
|
8838
9347
|
const len2 = Math.hypot(dx, dy);
|
|
8839
9348
|
if (type === "symmetric") next[other] = { x: a.x - dx, y: a.y - dy };
|
|
8840
|
-
else if (type === "smooth" && len2 >
|
|
9349
|
+
else if (type === "smooth" && len2 > EPS3) {
|
|
8841
9350
|
const o = a[other];
|
|
8842
9351
|
const olen = o ? Math.hypot(o.x - a.x, o.y - a.y) : len2;
|
|
8843
9352
|
next[other] = { x: a.x - dx / len2 * olen, y: a.y - dy / len2 * olen };
|
|
@@ -8867,15 +9376,15 @@ function convertAnchor(sp, index, type) {
|
|
|
8867
9376
|
else if (prev && next) dir = { x: next.x - prev.x, y: next.y - prev.y };
|
|
8868
9377
|
else if (next) dir = { x: next.x - a.x, y: next.y - a.y };
|
|
8869
9378
|
else if (prev) dir = { x: a.x - prev.x, y: a.y - prev.y };
|
|
8870
|
-
if (!dir || Math.hypot(dir.x, dir.y) <
|
|
9379
|
+
if (!dir || Math.hypot(dir.x, dir.y) < EPS3) return { anchors, closed: sp.closed };
|
|
8871
9380
|
const dl = Math.hypot(dir.x, dir.y);
|
|
8872
9381
|
const ux = dir.x / dl;
|
|
8873
9382
|
const uy = dir.y / dl;
|
|
8874
9383
|
lenIn = a.in && !isOnAnchor(a, a.in) ? Math.hypot(a.in.x - a.x, a.in.y - a.y) : prev ? Math.hypot(prev.x - a.x, prev.y - a.y) / 3 : 0;
|
|
8875
9384
|
lenOut = a.out && !isOnAnchor(a, a.out) ? Math.hypot(a.out.x - a.x, a.out.y - a.y) : next ? Math.hypot(next.x - a.x, next.y - a.y) / 3 : 0;
|
|
8876
9385
|
if (type === "symmetric") lenIn = lenOut = (lenIn + lenOut) / 2 || Math.max(lenIn, lenOut);
|
|
8877
|
-
if (lenIn >
|
|
8878
|
-
if (lenOut >
|
|
9386
|
+
if (lenIn > EPS3) a.in = { x: a.x - ux * lenIn, y: a.y - uy * lenIn };
|
|
9387
|
+
if (lenOut > EPS3) a.out = { x: a.x + ux * lenOut, y: a.y + uy * lenOut };
|
|
8879
9388
|
a.type = type;
|
|
8880
9389
|
return { anchors, closed: sp.closed };
|
|
8881
9390
|
}
|
|
@@ -8910,444 +9419,167 @@ function reverseSubpath(sp) {
|
|
|
8910
9419
|
const next = { x: a.x, y: a.y };
|
|
8911
9420
|
if (a.out) next.in = { ...a.out };
|
|
8912
9421
|
if (a.in) next.out = { ...a.in };
|
|
8913
|
-
if (a.type) next.type = a.type;
|
|
8914
|
-
if (a.corner) next.corner = { ...a.corner };
|
|
8915
|
-
if (a.id) next.id = a.id;
|
|
8916
|
-
return next;
|
|
8917
|
-
});
|
|
8918
|
-
return { anchors, closed: sp.closed };
|
|
8919
|
-
}
|
|
8920
|
-
var KAPPA2 = 0.5522847498307936;
|
|
8921
|
-
function rectSubpath(b, radius = 0, kind) {
|
|
8922
|
-
const { x, y, width: w, height: h } = b;
|
|
8923
|
-
const sp = { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
|
|
8924
|
-
return radius > 0 ? setSubpathCorners(sp, radius, kind) : sp;
|
|
8925
|
-
}
|
|
8926
|
-
function ellipseSubpath(b) {
|
|
8927
|
-
const rx = b.width / 2;
|
|
8928
|
-
const ry = b.height / 2;
|
|
8929
|
-
const cx = b.x + rx;
|
|
8930
|
-
const cy = b.y + ry;
|
|
8931
|
-
const kx = rx * KAPPA2;
|
|
8932
|
-
const ky = ry * KAPPA2;
|
|
8933
|
-
return {
|
|
8934
|
-
closed: true,
|
|
8935
|
-
anchors: [
|
|
8936
|
-
{ x: cx, y: cy - ry, in: { x: cx - kx, y: cy - ry }, out: { x: cx + kx, y: cy - ry }, type: "symmetric" },
|
|
8937
|
-
{ x: cx + rx, y: cy, in: { x: cx + rx, y: cy - ky }, out: { x: cx + rx, y: cy + ky }, type: "symmetric" },
|
|
8938
|
-
{ x: cx, y: cy + ry, in: { x: cx + kx, y: cy + ry }, out: { x: cx - kx, y: cy + ry }, type: "symmetric" },
|
|
8939
|
-
{ x: cx - rx, y: cy, in: { x: cx - rx, y: cy + ky }, out: { x: cx - rx, y: cy - ky }, type: "symmetric" }
|
|
8940
|
-
]
|
|
8941
|
-
};
|
|
8942
|
-
}
|
|
8943
|
-
function polygonSubpath(b, sides = 6) {
|
|
8944
|
-
const n = Math.max(3, Math.round(sides));
|
|
8945
|
-
const cx = b.x + b.width / 2;
|
|
8946
|
-
const cy = b.y + b.height / 2;
|
|
8947
|
-
const anchors = [];
|
|
8948
|
-
for (let i = 0; i < n; i++) {
|
|
8949
|
-
const t = -Math.PI / 2 + i * 2 * Math.PI / n;
|
|
8950
|
-
anchors.push({ x: cx + Math.cos(t) * b.width / 2, y: cy + Math.sin(t) * b.height / 2 });
|
|
8951
|
-
}
|
|
8952
|
-
return { anchors, closed: true };
|
|
8953
|
-
}
|
|
8954
|
-
function starSubpath(b, points = 5, innerRatio = 0.5) {
|
|
8955
|
-
const n = Math.max(3, Math.round(points));
|
|
8956
|
-
const cx = b.x + b.width / 2;
|
|
8957
|
-
const cy = b.y + b.height / 2;
|
|
8958
|
-
const anchors = [];
|
|
8959
|
-
for (let i = 0; i < n * 2; i++) {
|
|
8960
|
-
const t = -Math.PI / 2 + i * Math.PI / n;
|
|
8961
|
-
const r = i % 2 === 0 ? 1 : innerRatio;
|
|
8962
|
-
anchors.push({ x: cx + Math.cos(t) * r * b.width / 2, y: cy + Math.sin(t) * r * b.height / 2 });
|
|
8963
|
-
}
|
|
8964
|
-
return { anchors, closed: true };
|
|
8965
|
-
}
|
|
8966
|
-
function lineSubpath(a, b) {
|
|
8967
|
-
return { anchors: [{ x: a.x, y: a.y }, { x: b.x, y: b.y }], closed: false };
|
|
8968
|
-
}
|
|
8969
|
-
function roundSubpaths(subpaths, precision = 3) {
|
|
8970
|
-
const f = 10 ** precision;
|
|
8971
|
-
const r = (n) => Math.round(n * f) / f;
|
|
8972
|
-
const rp = (p) => ({ x: r(p.x), y: r(p.y) });
|
|
8973
|
-
return subpaths.map((sp) => ({
|
|
8974
|
-
closed: sp.closed,
|
|
8975
|
-
anchors: sp.anchors.map((a) => ({ ...cloneAnchor(a), x: r(a.x), y: r(a.y), ...a.in ? { in: rp(a.in) } : {}, ...a.out ? { out: rp(a.out) } : {} }))
|
|
8976
|
-
}));
|
|
8977
|
-
}
|
|
8978
|
-
|
|
8979
|
-
// src/vector/pathEffects.ts
|
|
8980
|
-
var EPS3 = 1e-9;
|
|
8981
|
-
var degenerate2 = (s) => s.every((p) => Math.abs(p[0] - s[0][0]) < EPS3 && Math.abs(p[1] - s[0][1]) < EPS3);
|
|
8982
|
-
function raise(seg) {
|
|
8983
|
-
if (seg.length !== 3) return seg.map((p) => [p[0], p[1]]);
|
|
8984
|
-
const [p0, p1, p2] = seg;
|
|
8985
|
-
return [p0, [p0[0] + 2 / 3 * (p1[0] - p0[0]), p0[1] + 2 / 3 * (p1[1] - p0[1])], [p2[0] + 2 / 3 * (p1[0] - p2[0]), p2[1] + 2 / 3 * (p1[1] - p2[1])], p2];
|
|
8986
|
-
}
|
|
8987
|
-
function subsOf(path) {
|
|
8988
|
-
return pathToContours(path).map((c) => ({ segs: closeContour(c).filter((s) => !degenerate2(s)).map(raise), closed: c.closed })).filter((s) => s.segs.length > 0);
|
|
8989
|
-
}
|
|
8990
|
-
var subsToPath = (subs) => contoursToPath(subs.map((s) => ({ segments: s.segs, closed: s.closed })));
|
|
8991
|
-
var unit3 = (x, y) => {
|
|
8992
|
-
const l = Math.hypot(x, y);
|
|
8993
|
-
return l > 1e-12 && Number.isFinite(l) ? [x / l, y / l] : null;
|
|
8994
|
-
};
|
|
8995
|
-
function startTangent(s) {
|
|
8996
|
-
for (let i = 1; i < s.length; i++) {
|
|
8997
|
-
const u = unit3(s[i][0] - s[0][0], s[i][1] - s[0][1]);
|
|
8998
|
-
if (u) return u;
|
|
8999
|
-
}
|
|
9000
|
-
return [1, 0];
|
|
9001
|
-
}
|
|
9002
|
-
function endTangent(s) {
|
|
9003
|
-
const end = s[s.length - 1];
|
|
9004
|
-
for (let i = s.length - 2; i >= 0; i--) {
|
|
9005
|
-
const u = unit3(end[0] - s[i][0], end[1] - s[i][1]);
|
|
9006
|
-
if (u) return u;
|
|
9007
|
-
}
|
|
9008
|
-
return [1, 0];
|
|
9009
|
-
}
|
|
9010
|
-
function tangentAt(s, t) {
|
|
9011
|
-
if (t <= 1e-9) return startTangent(s);
|
|
9012
|
-
if (t >= 1 - 1e-9) return endTangent(s);
|
|
9013
|
-
const d = segmentDerivative(s, t);
|
|
9014
|
-
return unit3(d[0], d[1]) ?? startTangent(s);
|
|
9015
|
-
}
|
|
9016
|
-
var perp2 = (v2) => [-v2[1], v2[0]];
|
|
9017
|
-
function effectPathLength(path) {
|
|
9018
|
-
let total = 0;
|
|
9019
|
-
for (const s of subsOf(path)) for (const seg of s.segs) total += segmentLength(seg);
|
|
9020
|
-
return total;
|
|
9021
|
-
}
|
|
9022
|
-
function effectPathVertexCount(path) {
|
|
9023
|
-
return subsOf(path).reduce((n, s) => n + s.segs.length + (s.closed ? 0 : 1), 0);
|
|
9024
|
-
}
|
|
9025
|
-
var Measured = class {
|
|
9026
|
-
segs;
|
|
9027
|
-
lens;
|
|
9028
|
-
total;
|
|
9029
|
-
constructor(path) {
|
|
9030
|
-
this.segs = subsOf(path).flatMap((s) => s.segs);
|
|
9031
|
-
this.lens = this.segs.map((s) => segmentLength(s));
|
|
9032
|
-
this.total = this.lens.reduce((a, b) => a + b, 0);
|
|
9033
|
-
}
|
|
9034
|
-
/**
|
|
9035
|
-
* Appends the part between lengths `sa` and `sb`, continuing the last piece when this one
|
|
9036
|
-
* starts where it ends (across the start of a closed path).
|
|
9037
|
-
*/
|
|
9038
|
-
append(sa, sb, out) {
|
|
9039
|
-
for (const piece of trimSegments(this.segs, sa, sb, this.lens)) {
|
|
9040
|
-
if (degenerate2(piece)) continue;
|
|
9041
|
-
const last = out[out.length - 1]?.segs;
|
|
9042
|
-
const end = last?.[last.length - 1]?.at(-1);
|
|
9043
|
-
if (!end || Math.hypot(end[0] - piece[0][0], end[1] - piece[0][1]) > 1e-6) out.push({ segs: [piece], closed: false });
|
|
9044
|
-
else last.push(piece);
|
|
9045
|
-
}
|
|
9046
|
-
}
|
|
9047
|
-
};
|
|
9048
|
-
function trimIntervals(start, end, offsetDegrees) {
|
|
9049
|
-
let s = start / 100;
|
|
9050
|
-
let e = end / 100;
|
|
9051
|
-
if (!Number.isFinite(s) || !Number.isFinite(e)) return null;
|
|
9052
|
-
if (s > e) [s, e] = [e, s];
|
|
9053
|
-
s = Math.min(1, Math.max(0, s));
|
|
9054
|
-
e = Math.min(1, Math.max(0, e));
|
|
9055
|
-
if (e - s >= 1 - 1e-9) return null;
|
|
9056
|
-
if (e - s <= 1e-9) return [];
|
|
9057
|
-
const off = Number.isFinite(offsetDegrees) ? (offsetDegrees / 360 % 1 + 1) % 1 : 0;
|
|
9058
|
-
s += off;
|
|
9059
|
-
e += off;
|
|
9060
|
-
if (e <= 1) return [[s, e]];
|
|
9061
|
-
if (s >= 1) return [[s - 1, e - 1]];
|
|
9062
|
-
return [
|
|
9063
|
-
[s, 1],
|
|
9064
|
-
[0, e - 1]
|
|
9065
|
-
];
|
|
9066
|
-
}
|
|
9067
|
-
function trimPaths(paths, start, end, offsetDegrees = 0) {
|
|
9068
|
-
const iv = trimIntervals(start, end, offsetDegrees);
|
|
9069
|
-
if (!iv) return paths.slice();
|
|
9070
|
-
return paths.map((p) => {
|
|
9071
|
-
const m = new Measured(p);
|
|
9072
|
-
const out = [];
|
|
9073
|
-
if (m.total > 0) for (const [a, b] of iv) m.append(a * m.total, b * m.total, out);
|
|
9074
|
-
return subsToPath(out);
|
|
9075
|
-
});
|
|
9076
|
-
}
|
|
9077
|
-
function trimPathsIndividually(paths, start, end, offsetDegrees = 0) {
|
|
9078
|
-
const iv = trimIntervals(start, end, offsetDegrees);
|
|
9079
|
-
if (!iv) return paths.slice();
|
|
9080
|
-
const ms = paths.map((p) => new Measured(p));
|
|
9081
|
-
const total = ms.reduce((n, m) => n + m.total, 0);
|
|
9082
|
-
let base = 0;
|
|
9083
|
-
return ms.map((m) => {
|
|
9084
|
-
const out = [];
|
|
9085
|
-
for (const [a, b] of iv) {
|
|
9086
|
-
const la = Math.max(0, a * total - base);
|
|
9087
|
-
const lb = Math.min(m.total, b * total - base);
|
|
9088
|
-
if (lb > la) m.append(la, lb, out);
|
|
9089
|
-
}
|
|
9090
|
-
base += m.total;
|
|
9091
|
-
return subsToPath(out);
|
|
9092
|
-
});
|
|
9093
|
-
}
|
|
9094
|
-
function puckerBloatPaths(paths, amount) {
|
|
9095
|
-
const a = amount / 100;
|
|
9096
|
-
if (!a || !Number.isFinite(a)) return paths.slice();
|
|
9097
|
-
return paths.map((p) => {
|
|
9098
|
-
const verts = p.filter((c) => c.type !== "Z");
|
|
9099
|
-
if (!verts.length) return p;
|
|
9100
|
-
const cx = verts.reduce((s, c) => s + c.x, 0) / verts.length;
|
|
9101
|
-
const cy = verts.reduce((s, c) => s + c.y, 0) / verts.length;
|
|
9102
|
-
const mv = (x, y) => ({ x: x + (cx - x) * a, y: y + (cy - y) * a });
|
|
9103
|
-
const mvh = (x, y) => ({ x: x + (x - cx) * a, y: y + (y - cy) * a });
|
|
9104
|
-
const out = [];
|
|
9105
|
-
let prev = { x: 0, y: 0 };
|
|
9106
|
-
let start = { x: 0, y: 0 };
|
|
9107
|
-
const curve = (c1, c2, q) => {
|
|
9108
|
-
const h1 = mvh(c1.x, c1.y);
|
|
9109
|
-
const h2 = mvh(c2.x, c2.y);
|
|
9110
|
-
const e = mv(q.x, q.y);
|
|
9111
|
-
out.push({ type: "C", x1: h1.x, y1: h1.y, x2: h2.x, y2: h2.y, x: e.x, y: e.y });
|
|
9112
|
-
};
|
|
9113
|
-
for (const c of p) {
|
|
9114
|
-
switch (c.type) {
|
|
9115
|
-
case "M": {
|
|
9116
|
-
const e = mv(c.x, c.y);
|
|
9117
|
-
out.push({ type: "M", x: e.x, y: e.y });
|
|
9118
|
-
prev = start = { x: c.x, y: c.y };
|
|
9119
|
-
break;
|
|
9120
|
-
}
|
|
9121
|
-
case "L":
|
|
9122
|
-
curve(prev, c, c);
|
|
9123
|
-
prev = { x: c.x, y: c.y };
|
|
9124
|
-
break;
|
|
9125
|
-
case "Q": {
|
|
9126
|
-
const c1 = { x: prev.x + 2 / 3 * (c.x1 - prev.x), y: prev.y + 2 / 3 * (c.y1 - prev.y) };
|
|
9127
|
-
const c2 = { x: c.x + 2 / 3 * (c.x1 - c.x), y: c.y + 2 / 3 * (c.y1 - c.y) };
|
|
9128
|
-
curve(c1, c2, c);
|
|
9129
|
-
prev = { x: c.x, y: c.y };
|
|
9130
|
-
break;
|
|
9131
|
-
}
|
|
9132
|
-
case "C":
|
|
9133
|
-
curve({ x: c.x1, y: c.y1 }, { x: c.x2, y: c.y2 }, c);
|
|
9134
|
-
prev = { x: c.x, y: c.y };
|
|
9135
|
-
break;
|
|
9136
|
-
case "Z":
|
|
9137
|
-
if (Math.hypot(prev.x - start.x, prev.y - start.y) > EPS3) curve(prev, start, start);
|
|
9138
|
-
out.push({ type: "Z" });
|
|
9139
|
-
prev = start;
|
|
9140
|
-
break;
|
|
9141
|
-
}
|
|
9142
|
-
}
|
|
9143
|
-
return out;
|
|
9422
|
+
if (a.type) next.type = a.type;
|
|
9423
|
+
if (a.corner) next.corner = { ...a.corner };
|
|
9424
|
+
if (a.id) next.id = a.id;
|
|
9425
|
+
return next;
|
|
9144
9426
|
});
|
|
9427
|
+
return { anchors, closed: sp.closed };
|
|
9145
9428
|
}
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
}
|
|
9150
|
-
|
|
9151
|
-
const n = Math.min(1e3, Math.max(1, Math.round(Number.isFinite(copies) ? copies : 1)));
|
|
9152
|
-
return paths.map((p) => {
|
|
9153
|
-
const out = [];
|
|
9154
|
-
for (let i = 0; i < n; i++) out.push(...offsetPath(p, amount * (i + 1 + copyOffset), { join, miterLimit }));
|
|
9155
|
-
return out;
|
|
9156
|
-
});
|
|
9429
|
+
var KAPPA2 = 0.5522847498307936;
|
|
9430
|
+
function rectSubpath(b, radius = 0, kind) {
|
|
9431
|
+
const { x, y, width: w, height: h } = b;
|
|
9432
|
+
const sp = { anchors: [{ x, y }, { x: x + w, y }, { x: x + w, y: y + h }, { x, y: y + h }], closed: true };
|
|
9433
|
+
return radius > 0 ? setSubpathCorners(sp, radius, kind) : sp;
|
|
9157
9434
|
}
|
|
9158
|
-
function
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
const
|
|
9162
|
-
|
|
9163
|
-
const
|
|
9164
|
-
const
|
|
9165
|
-
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
};
|
|
9174
|
-
const jac = (p, dv) => {
|
|
9175
|
-
const v2 = [p[0] - center.x, p[1] - center.y];
|
|
9176
|
-
const r = Math.hypot(v2[0], v2[1]);
|
|
9177
|
-
let w = dv;
|
|
9178
|
-
if (r > 1e-12) {
|
|
9179
|
-
const f = k * (v2[0] * dv[0] + v2[1] * dv[1]) / r;
|
|
9180
|
-
w = [dv[0] - v2[1] * f, dv[1] + v2[0] * f];
|
|
9181
|
-
}
|
|
9182
|
-
return rot(w, k * r);
|
|
9435
|
+
function ellipseSubpath(b) {
|
|
9436
|
+
const rx = b.width / 2;
|
|
9437
|
+
const ry = b.height / 2;
|
|
9438
|
+
const cx = b.x + rx;
|
|
9439
|
+
const cy = b.y + ry;
|
|
9440
|
+
const kx = rx * KAPPA2;
|
|
9441
|
+
const ky = ry * KAPPA2;
|
|
9442
|
+
return {
|
|
9443
|
+
closed: true,
|
|
9444
|
+
anchors: [
|
|
9445
|
+
{ x: cx, y: cy - ry, in: { x: cx - kx, y: cy - ry }, out: { x: cx + kx, y: cy - ry }, type: "symmetric" },
|
|
9446
|
+
{ x: cx + rx, y: cy, in: { x: cx + rx, y: cy - ky }, out: { x: cx + rx, y: cy + ky }, type: "symmetric" },
|
|
9447
|
+
{ x: cx, y: cy + ry, in: { x: cx + kx, y: cy + ry }, out: { x: cx - kx, y: cy + ry }, type: "symmetric" },
|
|
9448
|
+
{ x: cx - rx, y: cy, in: { x: cx - rx, y: cy + ky }, out: { x: cx - rx, y: cy - ky }, type: "symmetric" }
|
|
9449
|
+
]
|
|
9183
9450
|
};
|
|
9184
|
-
return paths.map((p) => {
|
|
9185
|
-
const out = [];
|
|
9186
|
-
for (const sub5 of subsOf(p)) {
|
|
9187
|
-
const segs = [];
|
|
9188
|
-
for (const seg of sub5.segs) {
|
|
9189
|
-
const cubic = seg.length === 2 ? [seg[0], seg[0], seg[1], seg[1]] : seg;
|
|
9190
|
-
const len2 = segmentLength(seg);
|
|
9191
|
-
const m = Math.min(64, Math.max(1, Math.ceil(Math.abs(a) * len2 / rmax / 0.15 + len2 / 50)));
|
|
9192
|
-
for (let i = 0; i < m; i++) {
|
|
9193
|
-
const q = subSegment(cubic, i / m, (i + 1) / m);
|
|
9194
|
-
const p0 = map(q[0]);
|
|
9195
|
-
const p3 = map(q[3]);
|
|
9196
|
-
const h1 = jac(q[0], [q[1][0] - q[0][0], q[1][1] - q[0][1]]);
|
|
9197
|
-
const h2 = jac(q[3], [q[2][0] - q[3][0], q[2][1] - q[3][1]]);
|
|
9198
|
-
segs.push([p0, [p0[0] + h1[0], p0[1] + h1[1]], [p3[0] + h2[0], p3[1] + h2[1]], p3]);
|
|
9199
|
-
}
|
|
9200
|
-
}
|
|
9201
|
-
out.push({ segs, closed: sub5.closed });
|
|
9202
|
-
}
|
|
9203
|
-
return subsToPath(out);
|
|
9204
|
-
});
|
|
9205
9451
|
}
|
|
9206
|
-
function
|
|
9207
|
-
const
|
|
9208
|
-
const
|
|
9209
|
-
const
|
|
9210
|
-
const
|
|
9211
|
-
for (let
|
|
9212
|
-
const
|
|
9213
|
-
|
|
9214
|
-
if (k === 0) {
|
|
9215
|
-
const tout = startTangent(segs[j]);
|
|
9216
|
-
const prev = sub5.closed || j > 0 ? (j + n - 1) % n : null;
|
|
9217
|
-
let tan = tout;
|
|
9218
|
-
if (prev !== null) {
|
|
9219
|
-
const tin = endTangent(segs[prev]);
|
|
9220
|
-
tan = unit3(tin[0] + tout[0], tin[1] + tout[1]) ?? tout;
|
|
9221
|
-
}
|
|
9222
|
-
out.push({ p: segs[j][0], tan, lenPrev: prev === null ? 0 : lens[prev] / m, lenNext: piece });
|
|
9223
|
-
} else {
|
|
9224
|
-
const t = segmentParamAtLength(segs[j], piece * k, lens[j]);
|
|
9225
|
-
out.push({ p: segmentPoint(segs[j], t), tan: tangentAt(segs[j], t), lenPrev: piece, lenNext: piece });
|
|
9226
|
-
}
|
|
9227
|
-
}
|
|
9228
|
-
}
|
|
9229
|
-
if (!sub5.closed) {
|
|
9230
|
-
const last = segs[n - 1];
|
|
9231
|
-
out.push({ p: last[last.length - 1], tan: endTangent(last), lenPrev: lens[n - 1] / m, lenNext: 0 });
|
|
9452
|
+
function polygonSubpath(b, sides = 6) {
|
|
9453
|
+
const n = Math.max(3, Math.round(sides));
|
|
9454
|
+
const cx = b.x + b.width / 2;
|
|
9455
|
+
const cy = b.y + b.height / 2;
|
|
9456
|
+
const anchors = [];
|
|
9457
|
+
for (let i = 0; i < n; i++) {
|
|
9458
|
+
const t = -Math.PI / 2 + i * 2 * Math.PI / n;
|
|
9459
|
+
anchors.push({ x: cx + Math.cos(t) * b.width / 2, y: cy + Math.sin(t) * b.height / 2 });
|
|
9232
9460
|
}
|
|
9233
|
-
return
|
|
9461
|
+
return { anchors, closed: true };
|
|
9234
9462
|
}
|
|
9235
|
-
function
|
|
9236
|
-
const n =
|
|
9237
|
-
|
|
9238
|
-
const
|
|
9239
|
-
const
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
const
|
|
9243
|
-
|
|
9244
|
-
const b = samples[j];
|
|
9245
|
-
const p = pt2(i);
|
|
9246
|
-
const q = pt2(j);
|
|
9247
|
-
if (smooth) segs.push([p, [p[0] + a.tan[0] * a.lenNext * handle, p[1] + a.tan[1] * a.lenNext * handle], [q[0] - b.tan[0] * b.lenPrev * handle, q[1] - b.tan[1] * b.lenPrev * handle], q]);
|
|
9248
|
-
else segs.push([p, q]);
|
|
9463
|
+
function starSubpath(b, points = 5, innerRatio = 0.5) {
|
|
9464
|
+
const n = Math.max(3, Math.round(points));
|
|
9465
|
+
const cx = b.x + b.width / 2;
|
|
9466
|
+
const cy = b.y + b.height / 2;
|
|
9467
|
+
const anchors = [];
|
|
9468
|
+
for (let i = 0; i < n * 2; i++) {
|
|
9469
|
+
const t = -Math.PI / 2 + i * Math.PI / n;
|
|
9470
|
+
const r = i % 2 === 0 ? 1 : innerRatio;
|
|
9471
|
+
anchors.push({ x: cx + Math.cos(t) * r * b.width / 2, y: cy + Math.sin(t) * r * b.height / 2 });
|
|
9249
9472
|
}
|
|
9250
|
-
|
|
9251
|
-
return { segs, closed };
|
|
9252
|
-
}
|
|
9253
|
-
function zigzagPaths(paths, size, ridges, smooth = false) {
|
|
9254
|
-
const m = Math.min(1e3, Math.max(0, Math.round(Number.isFinite(ridges) ? ridges : 0))) + 1;
|
|
9255
|
-
return paths.map((p) => {
|
|
9256
|
-
const out = [];
|
|
9257
|
-
for (const sub5 of subsOf(p)) {
|
|
9258
|
-
const s = resample(sub5, m);
|
|
9259
|
-
const disp = s.map((x, i) => {
|
|
9260
|
-
const n = perp2(x.tan);
|
|
9261
|
-
const d = i % 2 === 0 ? size : -size;
|
|
9262
|
-
return [n[0] * d, n[1] * d];
|
|
9263
|
-
});
|
|
9264
|
-
const e = emit(s, disp, sub5.closed, smooth, 0.36);
|
|
9265
|
-
if (e) out.push(e);
|
|
9266
|
-
}
|
|
9267
|
-
return subsToPath(out);
|
|
9268
|
-
});
|
|
9269
|
-
}
|
|
9270
|
-
var MASK64 = (1n << 64n) - 1n;
|
|
9271
|
-
function splitmix(x) {
|
|
9272
|
-
x = x + 0x9e3779b97f4a7c15n & MASK64;
|
|
9273
|
-
let z = x;
|
|
9274
|
-
z = (z ^ z >> 30n) * 0xbf58476d1ce4e5b9n & MASK64;
|
|
9275
|
-
z = (z ^ z >> 27n) * 0x94d049bb133111ebn & MASK64;
|
|
9276
|
-
return z ^ z >> 31n;
|
|
9473
|
+
return { anchors, closed: true };
|
|
9277
9474
|
}
|
|
9278
|
-
function
|
|
9279
|
-
|
|
9280
|
-
return Number(h >> 11n) / 2 ** 53 * 2 - 1;
|
|
9475
|
+
function lineSubpath(a, b) {
|
|
9476
|
+
return { anchors: [{ x: a.x, y: a.y }, { x: b.x, y: b.y }], closed: false };
|
|
9281
9477
|
}
|
|
9282
|
-
function
|
|
9283
|
-
const
|
|
9284
|
-
const
|
|
9285
|
-
const
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
return Math.min(1, Math.max(-1, v2));
|
|
9478
|
+
function roundSubpaths(subpaths, precision = 3) {
|
|
9479
|
+
const f = 10 ** precision;
|
|
9480
|
+
const r = (n) => Math.round(n * f) / f;
|
|
9481
|
+
const rp = (p) => ({ x: r(p.x), y: r(p.y) });
|
|
9482
|
+
return subpaths.map((sp) => ({
|
|
9483
|
+
closed: sp.closed,
|
|
9484
|
+
anchors: sp.anchors.map((a) => ({ ...cloneAnchor(a), x: r(a.x), y: r(a.y), ...a.in ? { in: rp(a.in) } : {}, ...a.out ? { out: rp(a.out) } : {} }))
|
|
9485
|
+
}));
|
|
9291
9486
|
}
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9487
|
+
|
|
9488
|
+
// src/vector/symmetry.ts
|
|
9489
|
+
function isVectorSymmetryGroup(node) {
|
|
9490
|
+
return node?.type === "group" && !!node.symmetry && !node.blend && !node.liveBoolean && !node.mask && !node.frame;
|
|
9491
|
+
}
|
|
9492
|
+
var rad = (deg) => deg * Math.PI / 180;
|
|
9493
|
+
var exact = (v2) => Math.abs(v2) < 1e-12 ? 0 : v2;
|
|
9494
|
+
var around = (c, m) => multiplyMatrices(multiplyMatrices([1, 0, 0, 1, -c.x, -c.y], m), [1, 0, 0, 1, c.x, c.y]);
|
|
9495
|
+
var reflect = (c, deg) => {
|
|
9496
|
+
const a = 2 * rad(deg);
|
|
9497
|
+
return around(c, [exact(Math.cos(a)), exact(Math.sin(a)), exact(Math.sin(a)), exact(-Math.cos(a)), 0, 0]);
|
|
9498
|
+
};
|
|
9499
|
+
var turn = (c, deg) => around(c, [exact(Math.cos(rad(deg))), exact(Math.sin(rad(deg))), exact(-Math.sin(rad(deg))), exact(Math.cos(rad(deg))), 0, 0]);
|
|
9500
|
+
var AXIS = { horizontal: [0], vertical: [90], diagonal: [45], dual: [90, 0] };
|
|
9501
|
+
function vectorSymmetryMatrices(s) {
|
|
9502
|
+
const c = s.center;
|
|
9503
|
+
const r = s.rotation ?? 0;
|
|
9504
|
+
if (s.mode === "radial") {
|
|
9505
|
+
const n = Math.min(64, Math.max(2, Math.round(s.copies ?? 6)));
|
|
9302
9506
|
const out = [];
|
|
9303
|
-
for (
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
idx++;
|
|
9310
|
-
return [(sx * corr + ox * (1 - corr)) * w.size, (sy * corr + oy * (1 - corr)) * w.size];
|
|
9311
|
-
});
|
|
9312
|
-
const e = emit(s, disp, sub5.closed, w.smooth, 1 / 3);
|
|
9313
|
-
if (e) out.push(e);
|
|
9314
|
-
}
|
|
9315
|
-
return subsToPath(out);
|
|
9316
|
-
});
|
|
9507
|
+
for (let k = 1; k < n; k++) out.push(turn(c, 360 * k / n));
|
|
9508
|
+
if (s.mirror) for (let k = 0; k < n; k++) out.push(reflect(c, r + 180 * k / n));
|
|
9509
|
+
return out;
|
|
9510
|
+
}
|
|
9511
|
+
const axes = AXIS[s.mode].map((a) => reflect(c, a + r));
|
|
9512
|
+
return s.mode === "dual" ? [...axes, turn(c, 180)] : axes;
|
|
9317
9513
|
}
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
if (
|
|
9321
|
-
|
|
9322
|
-
if (!inputs.length) return [];
|
|
9323
|
-
return pathBoolean(MERGE_OPS[mode], inputs).path;
|
|
9514
|
+
function renamed(node, k) {
|
|
9515
|
+
const id = `${node.id}~sym${k}`;
|
|
9516
|
+
if (node.type === "group") return { ...node, id, children: node.children.map((c) => renamed(c, k)) };
|
|
9517
|
+
return { ...node, id };
|
|
9324
9518
|
}
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
const
|
|
9330
|
-
const
|
|
9331
|
-
|
|
9519
|
+
var drawnCache2 = /* @__PURE__ */ new WeakMap();
|
|
9520
|
+
function vectorSymmetryDrawn(node) {
|
|
9521
|
+
let hit = drawnCache2.get(node);
|
|
9522
|
+
if (hit) return hit;
|
|
9523
|
+
const { symmetry, ...group } = node;
|
|
9524
|
+
const copies = vectorSymmetryMatrices(symmetry).flatMap((m, k) => node.children.map((c) => ({ ...renamed(transformVectorNode(c, m), k + 1), locked: true })));
|
|
9525
|
+
hit = { ...group, children: [...node.children, ...copies] };
|
|
9526
|
+
drawnCache2.set(node, hit);
|
|
9527
|
+
return hit;
|
|
9528
|
+
}
|
|
9529
|
+
function vectorSymmetryAxes(s, box) {
|
|
9530
|
+
const r = s.rotation ?? 0;
|
|
9531
|
+
const dirs = s.mode === "radial" ? Array.from({ length: Math.min(64, Math.max(2, Math.round(s.copies ?? 6))) }, (_, k) => r - 90 + 360 * k / Math.min(64, Math.max(2, Math.round(s.copies ?? 6)))) : AXIS[s.mode].map((a) => a + r);
|
|
9332
9532
|
const out = [];
|
|
9333
|
-
for (
|
|
9334
|
-
const
|
|
9335
|
-
let
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9533
|
+
for (const deg of dirs) {
|
|
9534
|
+
const d = { x: Math.cos(rad(deg)), y: Math.sin(rad(deg)) };
|
|
9535
|
+
let lo = s.mode === "radial" ? 0 : -Infinity;
|
|
9536
|
+
let hi = Infinity;
|
|
9537
|
+
let inside = true;
|
|
9538
|
+
for (const [o, dv, min, max] of [
|
|
9539
|
+
[s.center.x, d.x, box.x, box.x + box.width],
|
|
9540
|
+
[s.center.y, d.y, box.y, box.y + box.height]
|
|
9541
|
+
]) {
|
|
9542
|
+
if (Math.abs(dv) < 1e-12) {
|
|
9543
|
+
if (o < min || o > max) inside = false;
|
|
9544
|
+
} else {
|
|
9545
|
+
const a = (min - o) / dv;
|
|
9546
|
+
const b = (max - o) / dv;
|
|
9547
|
+
lo = Math.max(lo, Math.min(a, b));
|
|
9548
|
+
hi = Math.min(hi, Math.max(a, b));
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
9551
|
+
if (inside && Number.isFinite(lo) && Number.isFinite(hi) && hi > lo) out.push([{ x: s.center.x + lo * d.x, y: s.center.y + lo * d.y }, { x: s.center.x + hi * d.x, y: s.center.y + hi * d.y }]);
|
|
9342
9552
|
}
|
|
9343
9553
|
return out;
|
|
9344
9554
|
}
|
|
9345
|
-
function
|
|
9346
|
-
|
|
9555
|
+
function sanitizeVectorSymmetry(value) {
|
|
9556
|
+
if (!value || typeof value !== "object") return null;
|
|
9557
|
+
const r = value;
|
|
9558
|
+
if (!VECTOR_SYMMETRY_MODES.includes(r.mode)) return null;
|
|
9559
|
+
const c = r.center;
|
|
9560
|
+
if (!c || !Number.isFinite(c.x) || !Number.isFinite(c.y)) return null;
|
|
9561
|
+
const out = { mode: r.mode, center: { x: c.x, y: c.y } };
|
|
9562
|
+
if (typeof r.rotation === "number" && Number.isFinite(r.rotation) && r.rotation % 360 !== 0) out.rotation = (r.rotation % 360 + 360) % 360;
|
|
9563
|
+
if (out.mode === "radial") {
|
|
9564
|
+
if (typeof r.copies === "number" && Number.isFinite(r.copies)) out.copies = Math.min(64, Math.max(2, Math.round(r.copies)));
|
|
9565
|
+
if (r.mirror === true) out.mirror = true;
|
|
9566
|
+
}
|
|
9567
|
+
return out;
|
|
9347
9568
|
}
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9569
|
+
var VECTOR_SYMMETRY_MODE_LABELS = {
|
|
9570
|
+
vertical: "Mirror left / right",
|
|
9571
|
+
horizontal: "Mirror top / bottom",
|
|
9572
|
+
dual: "Four quarters",
|
|
9573
|
+
diagonal: "Mirror diagonally",
|
|
9574
|
+
radial: "Radial"
|
|
9575
|
+
};
|
|
9576
|
+
function transformVectorSymmetry(s, m) {
|
|
9577
|
+
const center = { x: m[0] * s.center.x + m[2] * s.center.y + m[4], y: m[1] * s.center.x + m[3] * s.center.y + m[5] };
|
|
9578
|
+
const det = m[0] * m[3] - m[1] * m[2];
|
|
9579
|
+
const angle = Math.atan2(m[1], m[0]) * 180 / Math.PI;
|
|
9580
|
+
const rotation = det < 0 ? -(s.rotation ?? 0) + 2 * angle : (s.rotation ?? 0) + angle;
|
|
9581
|
+
const norm = (rotation % 360 + 360) % 360;
|
|
9582
|
+
return { ...s, center, ...Math.abs(norm) > 1e-9 && Math.abs(norm - 360) > 1e-9 ? { rotation: norm } : { rotation: void 0 } };
|
|
9351
9583
|
}
|
|
9352
9584
|
|
|
9353
9585
|
export {
|
|
@@ -9384,6 +9616,7 @@ export {
|
|
|
9384
9616
|
VECTOR_CONNECTOR_ROUTES,
|
|
9385
9617
|
VECTOR_BLEND_SPACINGS,
|
|
9386
9618
|
VECTOR_MASK_TYPES,
|
|
9619
|
+
VECTOR_SYMMETRY_MODES,
|
|
9387
9620
|
VECTOR_TEXT_ALIGNS,
|
|
9388
9621
|
VECTOR_PATH_TEXT_BASELINES,
|
|
9389
9622
|
VECTOR_CONSTRAINT_KINDS,
|
|
@@ -9455,6 +9688,13 @@ export {
|
|
|
9455
9688
|
applyMatrixLinear,
|
|
9456
9689
|
matrixRotation,
|
|
9457
9690
|
decomposeRigid,
|
|
9691
|
+
isVectorSymmetryGroup,
|
|
9692
|
+
vectorSymmetryMatrices,
|
|
9693
|
+
vectorSymmetryDrawn,
|
|
9694
|
+
vectorSymmetryAxes,
|
|
9695
|
+
sanitizeVectorSymmetry,
|
|
9696
|
+
VECTOR_SYMMETRY_MODE_LABELS,
|
|
9697
|
+
transformVectorSymmetry,
|
|
9458
9698
|
vectorCornerAt,
|
|
9459
9699
|
vectorCorners,
|
|
9460
9700
|
cornerSetback,
|
|
@@ -9538,6 +9778,7 @@ export {
|
|
|
9538
9778
|
puckerBloatPaths,
|
|
9539
9779
|
roundCornerPaths,
|
|
9540
9780
|
offsetPaths,
|
|
9781
|
+
sweepPath,
|
|
9541
9782
|
twistPaths,
|
|
9542
9783
|
zigzagPaths,
|
|
9543
9784
|
wigglePaths,
|
|
@@ -9794,4 +10035,4 @@ export {
|
|
|
9794
10035
|
lineSubpath,
|
|
9795
10036
|
roundSubpaths
|
|
9796
10037
|
};
|
|
9797
|
-
//# sourceMappingURL=chunk-
|
|
10038
|
+
//# sourceMappingURL=chunk-WORWH5K5.js.map
|