@pixerate/editor 0.107.0 → 0.108.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.
@@ -3,6 +3,7 @@ import {
3
3
  bezierPathBounds,
4
4
  bezierPathToSvg,
5
5
  closeContour,
6
+ contourSignedArea,
6
7
  contoursToPath,
7
8
  distanceToBezierPath,
8
9
  fitCubicRun,
@@ -10,13 +11,16 @@ import {
10
11
  flattenChain,
11
12
  formatNumber,
12
13
  isStraightCubic,
14
+ normalizeBezierPath,
13
15
  offsetPath,
14
16
  outlineStroke,
15
17
  pathBoolean,
16
18
  pathToContours,
17
19
  pointInBezierPath,
18
20
  polygonBoolean,
21
+ segmentBounds,
19
22
  segmentDerivative,
23
+ segmentIntersections,
20
24
  segmentLength,
21
25
  segmentNearest,
22
26
  segmentParamAtLength,
@@ -26,7 +30,8 @@ import {
26
30
  subSegment,
27
31
  toBezierPath,
28
32
  transformBezierPath,
29
- trimSegments
33
+ trimSegments,
34
+ windingNumber
30
35
  } from "./chunk-YCSFS3GK.js";
31
36
  import {
32
37
  IDENTITY_MATRIX,
@@ -95,6 +100,7 @@ var VECTOR_BLEND_MODES = [
95
100
  "luminosity"
96
101
  ];
97
102
  var VECTOR_BLEND_SPACINGS = ["steps", "distance", "smoothColor"];
103
+ var VECTOR_MASK_TYPES = ["clip", "alpha", "luminance"];
98
104
  var VECTOR_TOOLS = [
99
105
  "select",
100
106
  "direct",
@@ -1622,14 +1628,14 @@ function nearestStrokePosition(path, p, only) {
1622
1628
  if (!(total > 0)) return;
1623
1629
  let acc = 0;
1624
1630
  c.segments.forEach((seg, si) => {
1625
- const near = segmentNearest(seg, p);
1626
- if (!best || near.distance < best.distance) {
1627
- const s = acc + segmentLength(seg, 0, near.t);
1628
- const tan = segmentTangent(seg, near.t);
1629
- const pt2 = seg.length === 2 ? lerp(seg[0], seg[1], near.t) : evalSeg(seg, near.t);
1631
+ const near2 = segmentNearest(seg, p);
1632
+ if (!best || near2.distance < best.distance) {
1633
+ const s = acc + segmentLength(seg, 0, near2.t);
1634
+ const tan = segmentTangent(seg, near2.t);
1635
+ const pt2 = seg.length === 2 ? lerp(seg[0], seg[1], near2.t) : evalSeg(seg, near2.t);
1630
1636
  const n = leftNormal([tan[0], tan[1]]);
1631
1637
  const side = (p[0] - pt2[0]) * n[0] + (p[1] - pt2[1]) * n[1] >= 0 ? "left" : "right";
1632
- best = { contour: ci, t: Math.min(Math.max(s / total, 0), 1), distance: near.distance, side, point: pt2, tangent: [tan[0], tan[1]], length: total };
1638
+ best = { contour: ci, t: Math.min(Math.max(s / total, 0), 1), distance: near2.distance, side, point: pt2, tangent: [tan[0], tan[1]], length: total };
1633
1639
  }
1634
1640
  acc += lengths[si];
1635
1641
  });
@@ -1814,7 +1820,7 @@ function vectorStrokeParts(path, style) {
1814
1820
  const cap = capExtent(style, width);
1815
1821
  const tipMode = style.arrowAlign === "tip";
1816
1822
  const trims = contours.map(() => [0, 0]);
1817
- const place = (end, contour, ci) => {
1823
+ const place2 = (end, contour, ci) => {
1818
1824
  const kind = end === "start" ? style.startArrow : style.endArrow;
1819
1825
  if (!kind || contour.closed) return;
1820
1826
  const hw = arrowheadWeight(style, end);
@@ -1835,8 +1841,8 @@ function vectorStrokeParts(path, style) {
1835
1841
  }
1836
1842
  heads.push({ end, kind, tip, dir, inset, path: arrowheadPath(kind, tip, dir, hw) });
1837
1843
  };
1838
- place("start", first, 0);
1839
- place("end", last, contours.length - 1);
1844
+ place2("start", first, 0);
1845
+ place2("end", last, contours.length - 1);
1840
1846
  if (!tipMode || !heads.length) return { align, line: path, drawWidth, heads };
1841
1847
  const trimmed = contours.map((c, i) => {
1842
1848
  const [s0, s1] = trims[i];
@@ -1869,13 +1875,13 @@ function hitTestVectorStroke(path, p, style, tolerance = 0) {
1869
1875
  let d;
1870
1876
  let reach = width / 2;
1871
1877
  if (profile) {
1872
- const near = nearestStrokePosition(parts.line, point);
1873
- if (!near) return false;
1874
- d = near.distance;
1875
- const w = widthProfileAt(profile, near.t);
1876
- reach = width / 2 * (near.side === "left" ? w.left : w.right);
1878
+ const near2 = nearestStrokePosition(parts.line, point);
1879
+ if (!near2) return false;
1880
+ d = near2.distance;
1881
+ const w = widthProfileAt(profile, near2.t);
1882
+ reach = width / 2 * (near2.side === "left" ? w.left : w.right);
1877
1883
  if (parts.align !== "center") reach *= 2;
1878
- const atEnd = near.t <= 1e-9 || near.t >= 1 - 1e-9;
1884
+ const atEnd = near2.t <= 1e-9 || near2.t >= 1 - 1e-9;
1879
1885
  if (atEnd && (style.strokeLinecap === "round" || style.strokeLinecap === "square") && d > reach) {
1880
1886
  reach = Math.max(reach, width / 2 * (w.left + w.right) / 2);
1881
1887
  }
@@ -2318,6 +2324,766 @@ function mapBezierPath(path, f, tolerance = 0.1, options = {}) {
2318
2324
  return subpathsToBezier(mapVectorSubpaths(bezierToSubpaths(path), f, tolerance, options), { corners: false });
2319
2325
  }
2320
2326
 
2327
+ // src/vector/pathCut.ts
2328
+ var loopArea = (l) => contourSignedArea(l.segs);
2329
+ function loopSample(l) {
2330
+ const k = l.chord.findIndex((c) => !c);
2331
+ return segmentPoint(l.segs[k >= 0 ? k : 0], 0.5);
2332
+ }
2333
+ var contains = (l, p) => windingNumber([l.segs], p) !== 0;
2334
+ function groupFaces(loops) {
2335
+ const usable = loops.filter((l) => l.segs.length && Math.abs(loopArea(l)) > 1e-12);
2336
+ if (!usable.length) return [];
2337
+ const largest = usable.reduce((a, b) => Math.abs(loopArea(b)) > Math.abs(loopArea(a)) ? b : a);
2338
+ const sign = Math.sign(loopArea(largest));
2339
+ const outers = usable.filter((l) => Math.sign(loopArea(l)) === sign).sort((a, b) => Math.abs(loopArea(a)) - Math.abs(loopArea(b)));
2340
+ const faces = new Map(outers.map((o) => [o, { loops: [o] }]));
2341
+ for (const hole of usable.filter((l) => Math.sign(loopArea(l)) !== sign)) {
2342
+ const p = loopSample(hole);
2343
+ const parent = outers.find((o) => Math.abs(loopArea(o)) > Math.abs(loopArea(hole)) && contains(o, p));
2344
+ if (parent) faces.get(parent).loops.push(hole);
2345
+ }
2346
+ return outers.reverse().map((o) => faces.get(o));
2347
+ }
2348
+ function faceToPath(face) {
2349
+ return contoursToPath(face.loops.map((l) => ({ segments: l.segs, closed: true })));
2350
+ }
2351
+ function facesOf(path, fillRule) {
2352
+ const normalized = normalizeBezierPath(path, fillRule);
2353
+ const loops = pathToContours(normalized).map((c) => {
2354
+ const segs = closeContour(c, true).map((s) => s.slice());
2355
+ return { segs, chord: segs.map(() => false) };
2356
+ });
2357
+ return groupFaces(loops);
2358
+ }
2359
+ function splitIntoFaces(path, fillRule = "nonzero") {
2360
+ return facesOf(path, fillRule).map(faceToPath);
2361
+ }
2362
+ function rootsInUnit(a, b, c, d, scale3) {
2363
+ const f = (t) => ((a * t + b) * t + c) * t + d;
2364
+ const tol = 1e-12 * Math.max(1, scale3);
2365
+ const cuts = [0];
2366
+ const qa = 3 * a;
2367
+ const qb = 2 * b;
2368
+ if (Math.abs(qa) > 1e-18) {
2369
+ const disc = qb * qb - 4 * qa * c;
2370
+ if (disc >= 0) {
2371
+ const r = Math.sqrt(disc);
2372
+ for (const t of [(-qb - r) / (2 * qa), (-qb + r) / (2 * qa)].sort((x, y) => x - y)) if (t > 0 && t < 1) cuts.push(t);
2373
+ }
2374
+ } else if (Math.abs(qb) > 1e-18) {
2375
+ const t = -c / qb;
2376
+ if (t > 0 && t < 1) cuts.push(t);
2377
+ }
2378
+ cuts.push(1);
2379
+ const roots = [];
2380
+ const push = (t) => {
2381
+ if (!roots.some((r) => Math.abs(r - t) < 1e-9)) roots.push(t);
2382
+ };
2383
+ for (let i = 0; i < cuts.length - 1; i++) {
2384
+ let lo = cuts[i];
2385
+ let hi = cuts[i + 1];
2386
+ let flo = f(lo);
2387
+ const fhi = f(hi);
2388
+ if (Math.abs(flo) <= tol) push(lo);
2389
+ if (Math.abs(fhi) <= tol) push(hi);
2390
+ if (flo * fhi >= 0) continue;
2391
+ for (let k = 0; k < 80; k++) {
2392
+ const m = (lo + hi) / 2;
2393
+ const fm = f(m);
2394
+ if (fm === 0) {
2395
+ lo = hi = m;
2396
+ break;
2397
+ }
2398
+ if (fm * flo < 0) hi = m;
2399
+ else {
2400
+ lo = m;
2401
+ flo = fm;
2402
+ }
2403
+ }
2404
+ push((lo + hi) / 2);
2405
+ }
2406
+ return roots.sort((x, y) => x - y);
2407
+ }
2408
+ function lineHits(p, q, seg) {
2409
+ const dx = q[0] - p[0];
2410
+ const dy = q[1] - p[1];
2411
+ const len2 = dx * dx + dy * dy;
2412
+ if (len2 < 1e-24) return [];
2413
+ const h = (pt2) => (pt2[0] - p[0]) * -dy + (pt2[1] - p[1]) * dx;
2414
+ let coeffs;
2415
+ if (seg.length === 2) coeffs = [0, 0, h(seg[1]) - h(seg[0]), h(seg[0])];
2416
+ else {
2417
+ const [h0, h1, h2, h3] = seg.map(h);
2418
+ coeffs = [-h0 + 3 * h1 - 3 * h2 + h3, 3 * h0 - 6 * h1 + 3 * h2, -3 * h0 + 3 * h1, h0];
2419
+ }
2420
+ if (coeffs.every((c) => Math.abs(c) < 1e-12 * Math.max(1, len2))) return [];
2421
+ const scale3 = Math.sqrt(len2) * Math.max(1, ...seg.map((pt2) => Math.abs(pt2[0]) + Math.abs(pt2[1])));
2422
+ const out = [];
2423
+ for (const t of rootsInUnit(...coeffs, scale3)) {
2424
+ const point = segmentPoint(seg, t);
2425
+ const s = ((point[0] - p[0]) * dx + (point[1] - p[1]) * dy) / len2;
2426
+ if (s >= -1e-9 && s <= 1 + 1e-9) out.push({ s: Math.min(1, Math.max(0, s)), t, point });
2427
+ }
2428
+ return out;
2429
+ }
2430
+ var near = (a, b, eps) => Math.abs(a[0] - b[0]) <= eps && Math.abs(a[1] - b[1]) <= eps;
2431
+ var degenerate = (seg, eps) => seg.every((p) => near(p, seg[0], eps));
2432
+ function arc(l, a, b, eps) {
2433
+ const n = l.segs.length;
2434
+ const segs = [];
2435
+ const chord = [];
2436
+ const push = (k, t0, t1) => {
2437
+ const piece = t0 <= 0 && t1 >= 1 ? l.segs[k].slice() : subSegment(l.segs[k], t0, t1);
2438
+ if (!degenerate(piece, eps)) {
2439
+ segs.push(piece);
2440
+ chord.push(l.chord[k]);
2441
+ }
2442
+ };
2443
+ if (a.seg === b.seg && b.t >= a.t + 1e-12) {
2444
+ push(a.seg, a.t, b.t);
2445
+ return { segs, chord };
2446
+ }
2447
+ push(a.seg, a.t, 1);
2448
+ for (let k = (a.seg + 1) % n; k !== b.seg; k = (k + 1) % n) push(k, 0, 1);
2449
+ push(b.seg, 0, b.t);
2450
+ return { segs, chord };
2451
+ }
2452
+ function polySegs(points, eps) {
2453
+ const out = [];
2454
+ for (let i = 1; i < points.length; i++) if (!near(points[i - 1], points[i], eps)) out.push([points[i - 1], points[i]]);
2455
+ return out;
2456
+ }
2457
+ function knifePath(path, fillRule, knife) {
2458
+ const pts = knife.filter((p) => Number.isFinite(p[0]) && Number.isFinite(p[1]));
2459
+ if (pts.length < 2) return null;
2460
+ let faces = facesOf(path, fillRule);
2461
+ if (!faces.length) return null;
2462
+ const b = bezierPathBounds(path);
2463
+ const size = b ? Math.max(b.maxX - b.minX, b.maxY - b.minY, 1) : 1;
2464
+ const eps = size * 1e-9;
2465
+ let cursor = -Infinity;
2466
+ let cuts = 0;
2467
+ for (let guard = 0; guard < 512; guard++) {
2468
+ let best = null;
2469
+ faces.forEach((face2, fi2) => {
2470
+ const hits = [];
2471
+ for (let m = 0; m < pts.length - 1; m++) {
2472
+ if (m + 1 < cursor - 1e-9) continue;
2473
+ const kb = { minX: Math.min(pts[m][0], pts[m + 1][0]), maxX: Math.max(pts[m][0], pts[m + 1][0]), minY: Math.min(pts[m][1], pts[m + 1][1]), maxY: Math.max(pts[m][1], pts[m + 1][1]) };
2474
+ face2.loops.forEach(
2475
+ (l, li) => l.segs.forEach((seg, si) => {
2476
+ const sb = segmentBounds(seg);
2477
+ if (sb.maxX < kb.minX - eps || sb.minX > kb.maxX + eps || sb.maxY < kb.minY - eps || sb.minY > kb.maxY + eps) return;
2478
+ for (const h of lineHits(pts[m], pts[m + 1], seg)) {
2479
+ const s = m + h.s;
2480
+ if (s >= cursor - 1e-9) hits.push({ s, loop: li, seg: si, t: h.t, point: h.point });
2481
+ }
2482
+ })
2483
+ );
2484
+ }
2485
+ hits.sort((x, y) => x.s - y.s);
2486
+ const unique = hits.filter((h, i) => i === 0 || h.s - hits[i - 1].s > 1e-9 || !near(h.point, hits[i - 1].point, eps * 10));
2487
+ const inside = (s) => {
2488
+ const m = Math.min(pts.length - 2, Math.floor(s));
2489
+ const u = s - m;
2490
+ const q = [pts[m][0] + (pts[m + 1][0] - pts[m][0]) * u, pts[m][1] + (pts[m + 1][1] - pts[m][1]) * u];
2491
+ return windingNumber(face2.loops.map((l) => l.segs), q) !== 0;
2492
+ };
2493
+ for (let i = 0; i < unique.length - 1; i++) {
2494
+ const from2 = unique[i];
2495
+ const to2 = unique[i + 1];
2496
+ if (near(from2.point, to2.point, eps * 10) || !inside((from2.s + to2.s) / 2)) continue;
2497
+ if (!best || from2.s < best.from.s) best = { face: fi2, from: from2, to: to2 };
2498
+ break;
2499
+ }
2500
+ });
2501
+ if (!best) break;
2502
+ const { face: fi, from, to } = best;
2503
+ const face = faces[fi];
2504
+ const corners = [from.point];
2505
+ for (let m = Math.floor(from.s) + 1; m <= to.s - 1e-12 && m < pts.length; m++) if (m > from.s) corners.push(pts[m]);
2506
+ corners.push(to.point);
2507
+ const forward = polySegs(corners, eps);
2508
+ const back = polySegs(corners.slice().reverse(), eps);
2509
+ const la = face.loops[from.loop];
2510
+ const loops = face.loops.filter((_, i) => i !== from.loop && i !== to.loop);
2511
+ if (from.loop === to.loop) {
2512
+ const ab = arc(la, from, to, eps);
2513
+ const ba = arc(la, to, from, eps);
2514
+ loops.push(
2515
+ { segs: [...ab.segs, ...back], chord: [...ab.chord, ...back.map(() => true)] },
2516
+ { segs: [...ba.segs, ...forward], chord: [...ba.chord, ...forward.map(() => true)] }
2517
+ );
2518
+ } else {
2519
+ const lb = face.loops[to.loop];
2520
+ const roundA = arc(la, from, from, eps);
2521
+ const roundB = arc(lb, to, to, eps);
2522
+ loops.push({
2523
+ segs: [...roundA.segs, ...forward, ...roundB.segs, ...back],
2524
+ chord: [...roundA.chord, ...forward.map(() => true), ...roundB.chord, ...back.map(() => true)]
2525
+ });
2526
+ }
2527
+ faces = [...faces.slice(0, fi), ...groupFaces(loops), ...faces.slice(fi + 1)];
2528
+ cursor = to.s;
2529
+ cuts++;
2530
+ }
2531
+ return cuts ? faces.map(faceToPath) : null;
2532
+ }
2533
+ function eraserRegion(points, width) {
2534
+ const pts = points.filter((p) => Number.isFinite(p[0]) && Number.isFinite(p[1]));
2535
+ const r = width / 2;
2536
+ if (!pts.length || !(r > 0)) return [];
2537
+ const moved = pts.some((p) => !near(p, pts[0], 1e-9));
2538
+ if (!moved) {
2539
+ const [cx, cy] = pts[0];
2540
+ const k = r * 0.5522847498307936;
2541
+ return [
2542
+ { type: "M", x: cx, y: cy - r },
2543
+ { type: "C", x1: cx + k, y1: cy - r, x2: cx + r, y2: cy - k, x: cx + r, y: cy },
2544
+ { type: "C", x1: cx + r, y1: cy + k, x2: cx + k, y2: cy + r, x: cx, y: cy + r },
2545
+ { type: "C", x1: cx - k, y1: cy + r, x2: cx - r, y2: cy + k, x: cx - r, y: cy },
2546
+ { type: "C", x1: cx - r, y1: cy - k, x2: cx - k, y2: cy - r, x: cx, y: cy - r },
2547
+ { type: "Z" }
2548
+ ];
2549
+ }
2550
+ const line = [{ type: "M", x: pts[0][0], y: pts[0][1] }, ...pts.slice(1).map((p) => ({ type: "L", x: p[0], y: p[1] }))];
2551
+ return outlineStroke(line, { width, cap: "round", join: "round" });
2552
+ }
2553
+ function eraseFromFill(path, fillRule, region) {
2554
+ const overlap = pathBoolean("intersection", [{ path, fillRule }, { path: region }]).path;
2555
+ if (!pathToContours(overlap).some((c) => Math.abs(contourSignedArea(closeContour(c, true))) > 1e-9)) return null;
2556
+ const rest = pathBoolean("difference", [{ path, fillRule }, { path: region }]).path;
2557
+ return splitIntoFaces(rest);
2558
+ }
2559
+ function eraseFromOutline(sp, region, options = {}) {
2560
+ const regionSegs = pathToContours(region).flatMap((c) => closeContour(c, true));
2561
+ if (!regionSegs.length) return null;
2562
+ const rb = bezierPathBounds(region);
2563
+ const count = subpathSegmentCount(sp);
2564
+ if (!rb || count === 0) return null;
2565
+ const inside = options.keep === "inside";
2566
+ const removed = (p) => pointInBezierPath(region, p, options.fillRule ?? "nonzero") !== inside;
2567
+ const runs = [];
2568
+ let cur = [];
2569
+ let any = false;
2570
+ for (let i = 0; i < count; i++) {
2571
+ const seg = subpathSegment(sp, i);
2572
+ const sb = segmentBounds(seg);
2573
+ const ts = [0, 1];
2574
+ if (!(sb.maxX < rb.minX || sb.minX > rb.maxX || sb.maxY < rb.minY || sb.minY > rb.maxY)) {
2575
+ for (const r of regionSegs) {
2576
+ const b = segmentBounds(r);
2577
+ if (b.maxX < sb.minX || b.minX > sb.maxX || b.maxY < sb.minY || b.minY > sb.maxY) continue;
2578
+ for (const hit of segmentIntersections(seg, r)) if (hit.ta > 1e-9 && hit.ta < 1 - 1e-9) ts.push(hit.ta);
2579
+ }
2580
+ }
2581
+ ts.sort((a, b) => a - b);
2582
+ for (let k = 0; k < ts.length - 1; k++) {
2583
+ const t0 = ts[k];
2584
+ const t1 = ts[k + 1];
2585
+ if (t1 - t0 < 1e-9) continue;
2586
+ if (removed(segmentPoint(seg, (t0 + t1) / 2))) {
2587
+ any = true;
2588
+ if (cur.length) runs.push(cur);
2589
+ cur = [];
2590
+ } else cur.push({ seg: i, t0, t1 });
2591
+ }
2592
+ }
2593
+ if (cur.length) runs.push(cur);
2594
+ if (!any) return null;
2595
+ if (sp.closed && runs.length >= 2) {
2596
+ const first = runs[0][0];
2597
+ const lastRun = runs[runs.length - 1];
2598
+ const last = lastRun[lastRun.length - 1];
2599
+ if (first.seg === 0 && first.t0 === 0 && last.seg === count - 1 && last.t1 === 1) lastRun.push(...runs.shift());
2600
+ }
2601
+ const out = [];
2602
+ for (const run of runs) {
2603
+ const anchors = [];
2604
+ for (const { seg, t0, t1 } of run) {
2605
+ const c = subSegment(subpathSegment(sp, seg), t0, t1);
2606
+ const line = c.length === 2;
2607
+ const start = c[0];
2608
+ const end = c[c.length - 1];
2609
+ if (!anchors.length) anchors.push({ x: start[0], y: start[1] });
2610
+ if (!line) anchors[anchors.length - 1].out = { x: c[1][0], y: c[1][1] };
2611
+ const next = { x: end[0], y: end[1] };
2612
+ if (!line) next.in = { x: c[2][0], y: c[2][1] };
2613
+ anchors.push(next);
2614
+ }
2615
+ if (anchors.length < 2) continue;
2616
+ for (const a of anchors) {
2617
+ const type = inferAnchorType(a);
2618
+ if (type !== "corner") a.type = type;
2619
+ }
2620
+ out.push({ anchors, closed: false });
2621
+ }
2622
+ return out;
2623
+ }
2624
+ function polylineBounds(points, pad = 0) {
2625
+ if (!points.length) return null;
2626
+ let minX = Infinity;
2627
+ let minY = Infinity;
2628
+ let maxX = -Infinity;
2629
+ let maxY = -Infinity;
2630
+ for (const [x, y] of points) {
2631
+ minX = Math.min(minX, x);
2632
+ minY = Math.min(minY, y);
2633
+ maxX = Math.max(maxX, x);
2634
+ maxY = Math.max(maxY, y);
2635
+ }
2636
+ return { x: minX - pad, y: minY - pad, width: maxX - minX + 2 * pad, height: maxY - minY + 2 * pad };
2637
+ }
2638
+ function simplifyPolyline(points, tolerance) {
2639
+ const n = points.length;
2640
+ if (n < 3) return points.slice();
2641
+ const keep = new Array(n).fill(false);
2642
+ keep[0] = keep[n - 1] = true;
2643
+ const stack = [[0, n - 1]];
2644
+ while (stack.length) {
2645
+ const [a, b] = stack.pop();
2646
+ const [ax, ay] = points[a];
2647
+ const dx = points[b][0] - ax;
2648
+ const dy = points[b][1] - ay;
2649
+ const len2 = Math.hypot(dx, dy);
2650
+ let far = -1;
2651
+ let farD = 0;
2652
+ for (let i = a + 1; i < b; i++) {
2653
+ const [px, py] = points[i];
2654
+ const d = len2 < 1e-12 ? Math.hypot(px - ax, py - ay) : Math.abs((px - ax) * dy - (py - ay) * dx) / len2;
2655
+ if (d > farD) {
2656
+ farD = d;
2657
+ far = i;
2658
+ }
2659
+ }
2660
+ if (far >= 0 && farD > tolerance) {
2661
+ keep[far] = true;
2662
+ stack.push([a, far], [far, b]);
2663
+ }
2664
+ }
2665
+ return points.filter((_, i) => keep[i]);
2666
+ }
2667
+
2668
+ // src/vector/filters.ts
2669
+ var VECTOR_FILTER_DEFAULTS = {
2670
+ dropShadow: { color: "#000000", opacity: 0.25, offsetX: 0, offsetY: 4, blur: 4, spread: 0 },
2671
+ innerShadow: { color: "#000000", opacity: 0.25, offsetX: 0, offsetY: 4, blur: 4, spread: 0 },
2672
+ outerGlow: { color: "#ffffbe", opacity: 0.75, blur: 10, spread: 0 },
2673
+ innerGlow: { color: "#ffffbe", opacity: 0.75, blur: 10, spread: 0 },
2674
+ layerBlur: { radius: 4 },
2675
+ feather: { radius: 8 }
2676
+ };
2677
+ var VECTOR_FILTER_LABELS = {
2678
+ dropShadow: "Drop Shadow",
2679
+ innerShadow: "Inner Shadow",
2680
+ outerGlow: "Outer Glow",
2681
+ innerGlow: "Inner Glow",
2682
+ layerBlur: "Layer Blur",
2683
+ feather: "Feather"
2684
+ };
2685
+ var SHADOW_SPECS = [
2686
+ { key: "color", label: "Color", kind: "color" },
2687
+ { key: "opacity", label: "Opacity", kind: "number", min: 0, max: 1, step: 0.01, slider: true },
2688
+ { key: "offsetX", label: "X", kind: "number", min: -1e3, max: 1e3, step: 1 },
2689
+ { key: "offsetY", label: "Y", kind: "number", min: -1e3, max: 1e3, step: 1 },
2690
+ { key: "blur", label: "Blur", kind: "number", min: 0, max: 500, step: 1 },
2691
+ { key: "spread", label: "Spread", kind: "number", min: 0, max: 500, step: 1 }
2692
+ ];
2693
+ var GLOW_SPECS = SHADOW_SPECS.filter((s) => s.key !== "offsetX" && s.key !== "offsetY");
2694
+ var VECTOR_FILTER_PARAM_SPECS = {
2695
+ dropShadow: SHADOW_SPECS,
2696
+ innerShadow: SHADOW_SPECS.map((s) => s.key === "spread" ? { ...s, label: "Choke" } : s),
2697
+ outerGlow: GLOW_SPECS,
2698
+ innerGlow: GLOW_SPECS.map((s) => s.key === "spread" ? { ...s, label: "Choke" } : s),
2699
+ layerBlur: [{ key: "radius", label: "Radius", kind: "number", min: 0, max: 500, step: 1, slider: true }],
2700
+ feather: [{ key: "radius", label: "Radius", kind: "number", min: 0, max: 500, step: 1, slider: true }]
2701
+ };
2702
+ function isVectorFilterType(value) {
2703
+ return typeof value === "string" && VECTOR_FILTER_TYPES.includes(value);
2704
+ }
2705
+ function normalizeVectorFilterParams(type, params, base) {
2706
+ if (!isVectorFilterType(type)) return null;
2707
+ const out = { ...VECTOR_FILTER_DEFAULTS[type], ...base ?? {} };
2708
+ for (const spec of VECTOR_FILTER_PARAM_SPECS[type]) {
2709
+ const v2 = params?.[spec.key];
2710
+ if (v2 === void 0) continue;
2711
+ if (spec.kind === "number" && typeof v2 === "number" && Number.isFinite(v2)) out[spec.key] = Math.min(spec.max ?? Infinity, Math.max(spec.min ?? -Infinity, v2));
2712
+ else if (spec.kind === "color" && typeof v2 === "string" && v2.trim()) out[spec.key] = v2.trim();
2713
+ }
2714
+ return out;
2715
+ }
2716
+ function vectorFilterParamKeys(type) {
2717
+ return VECTOR_FILTER_PARAM_SPECS[type].map((s) => s.key);
2718
+ }
2719
+ function createVectorFilter(type, id, params) {
2720
+ const p = normalizeVectorFilterParams(type, params);
2721
+ return p ? { id, type, params: p } : null;
2722
+ }
2723
+ function sanitizeVectorFilters(value) {
2724
+ if (!Array.isArray(value)) return [];
2725
+ const out = [];
2726
+ const used = /* @__PURE__ */ new Set();
2727
+ for (const raw of value) {
2728
+ if (!raw || typeof raw !== "object") continue;
2729
+ const r = raw;
2730
+ if (!isVectorFilterType(r.type)) continue;
2731
+ let id = typeof r.id === "string" && r.id && !used.has(r.id) ? r.id : "";
2732
+ for (let k = out.length + 1; !id; k++) if (!used.has(`filter${k}`)) id = `filter${k}`;
2733
+ used.add(id);
2734
+ const filter = createVectorFilter(r.type, id, r.params ?? {});
2735
+ out.push(r.enabled === false ? { ...filter, enabled: false } : filter);
2736
+ }
2737
+ return out;
2738
+ }
2739
+ function activeVectorFilters(node) {
2740
+ return (node.filters ?? []).filter((f) => {
2741
+ if (f.enabled === false || !isVectorFilterType(f.type)) return false;
2742
+ if (f.type === "layerBlur" || f.type === "feather") return f.params.radius > 0;
2743
+ return f.params.opacity > 0;
2744
+ });
2745
+ }
2746
+ function vectorFilterReach(filters) {
2747
+ let outer = 0;
2748
+ let blur = 0;
2749
+ for (const f of filters) {
2750
+ switch (f.type) {
2751
+ case "dropShadow":
2752
+ case "innerShadow":
2753
+ outer = Math.max(outer, Math.max(Math.abs(f.params.offsetX), Math.abs(f.params.offsetY)) + f.params.blur * 1.5 + f.params.spread);
2754
+ break;
2755
+ case "outerGlow":
2756
+ case "innerGlow":
2757
+ outer = Math.max(outer, f.params.blur * 1.5 + f.params.spread);
2758
+ break;
2759
+ case "layerBlur":
2760
+ blur += f.params.radius * 1.5;
2761
+ break;
2762
+ }
2763
+ }
2764
+ return outer + blur;
2765
+ }
2766
+ var round = (n) => Math.round(n * 1e3) / 1e3;
2767
+ var fe = (tag, attrs, children) => ({ tag, attrs, ...children ? { children } : {} });
2768
+ function vectorFilterSvg(filters, id, bounds) {
2769
+ const active = filters.filter((f) => activeVectorFilters({ filters: [f] }).length);
2770
+ if (!active.length || !bounds) return null;
2771
+ const primitives = [];
2772
+ let body = "SourceGraphic";
2773
+ let n = 0;
2774
+ const name = (part) => `${part}${n}`;
2775
+ for (const f of active) {
2776
+ if (f.type !== "innerShadow" && f.type !== "innerGlow") continue;
2777
+ n++;
2778
+ const p = f.params;
2779
+ primitives.push(fe("feComponentTransfer", { in: "SourceAlpha", result: name("inv") }, [fe("feFuncA", { type: "table", tableValues: "1 0" })]));
2780
+ let last = name("inv");
2781
+ if (f.type === "innerShadow" && (f.params.offsetX || f.params.offsetY)) {
2782
+ primitives.push(fe("feOffset", { in: last, dx: round(f.params.offsetX), dy: round(f.params.offsetY), result: name("off") }));
2783
+ last = name("off");
2784
+ }
2785
+ if (p.spread > 0) {
2786
+ primitives.push(fe("feMorphology", { in: last, operator: "dilate", radius: round(p.spread), result: name("grow") }));
2787
+ last = name("grow");
2788
+ }
2789
+ if (p.blur > 0) {
2790
+ primitives.push(fe("feGaussianBlur", { in: last, stdDeviation: round(p.blur / 2), result: name("blur") }));
2791
+ last = name("blur");
2792
+ }
2793
+ primitives.push(fe("feFlood", { "flood-color": p.color, "flood-opacity": round(p.opacity), result: name("color") }));
2794
+ primitives.push(fe("feComposite", { in: name("color"), in2: last, operator: "in", result: name("tint") }));
2795
+ primitives.push(fe("feComposite", { in: name("tint"), in2: "SourceAlpha", operator: "in", result: name("inner") }));
2796
+ primitives.push(fe("feComposite", { in: name("inner"), in2: body, operator: "over", result: name("body") }));
2797
+ body = name("body");
2798
+ }
2799
+ for (const f of active) {
2800
+ if (f.type !== "feather") continue;
2801
+ n++;
2802
+ const r = f.params.radius;
2803
+ primitives.push(fe("feMorphology", { in: "SourceAlpha", operator: "erode", radius: round(r / 2), result: name("core") }));
2804
+ primitives.push(fe("feGaussianBlur", { in: name("core"), stdDeviation: round(r / 4), result: name("fade") }));
2805
+ primitives.push(fe("feComposite", { in: body, in2: name("fade"), operator: "in", result: name("body") }));
2806
+ body = name("body");
2807
+ }
2808
+ const under = [];
2809
+ for (const f of active) {
2810
+ if (f.type !== "dropShadow" && f.type !== "outerGlow") continue;
2811
+ n++;
2812
+ const p = f.params;
2813
+ let last = "SourceAlpha";
2814
+ if (p.spread > 0) {
2815
+ primitives.push(fe("feMorphology", { in: last, operator: "dilate", radius: round(p.spread), result: name("grow") }));
2816
+ last = name("grow");
2817
+ }
2818
+ if (p.blur > 0) {
2819
+ primitives.push(fe("feGaussianBlur", { in: last, stdDeviation: round(p.blur / 2), result: name("blur") }));
2820
+ last = name("blur");
2821
+ }
2822
+ if (f.type === "dropShadow" && (f.params.offsetX || f.params.offsetY)) {
2823
+ primitives.push(fe("feOffset", { in: last, dx: round(f.params.offsetX), dy: round(f.params.offsetY), result: name("off") }));
2824
+ last = name("off");
2825
+ }
2826
+ primitives.push(fe("feFlood", { "flood-color": p.color, "flood-opacity": round(p.opacity), result: name("color") }));
2827
+ primitives.push(fe("feComposite", { in: name("color"), in2: last, operator: "in", result: name("shadow") }));
2828
+ under.push(name("shadow"));
2829
+ }
2830
+ if (under.length) {
2831
+ n++;
2832
+ primitives.push(fe("feMerge", { result: name("merged") }, [...under, body].map((i) => fe("feMergeNode", { in: i }))));
2833
+ body = name("merged");
2834
+ }
2835
+ for (const f of active) {
2836
+ if (f.type !== "layerBlur") continue;
2837
+ n++;
2838
+ primitives.push(fe("feGaussianBlur", { in: body, stdDeviation: round(f.params.radius / 2), result: name("body") }));
2839
+ body = name("body");
2840
+ }
2841
+ const lastResult = primitives[primitives.length - 1]?.attrs.result;
2842
+ if (lastResult !== body) primitives.push(fe("feMerge", {}, [fe("feMergeNode", { in: body })]));
2843
+ const pad = vectorFilterReach(active) + 2;
2844
+ return {
2845
+ id,
2846
+ x: round(bounds.x - pad),
2847
+ y: round(bounds.y - pad),
2848
+ width: round(bounds.width + 2 * pad),
2849
+ height: round(bounds.height + 2 * pad),
2850
+ primitives
2851
+ };
2852
+ }
2853
+ var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2854
+ function primitiveMarkup(p) {
2855
+ const attrs = Object.entries(p.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
2856
+ return p.children?.length ? `<${p.tag}${attrs}>${p.children.map(primitiveMarkup).join("")}</${p.tag}>` : `<${p.tag}${attrs}/>`;
2857
+ }
2858
+ function vectorFilterMarkup(filter, extra = "") {
2859
+ return `<filter id="${escapeAttr(filter.id)}" filterUnits="userSpaceOnUse" x="${filter.x}" y="${filter.y}" width="${filter.width}" height="${filter.height}" color-interpolation-filters="sRGB"${extra}>${filter.primitives.map(primitiveMarkup).join("")}</filter>`;
2860
+ }
2861
+ var VECTOR_FILTER_MENU = VECTOR_FILTER_TYPES.map((type) => ({ type, label: VECTOR_FILTER_LABELS[type] }));
2862
+
2863
+ // src/vector/mask.ts
2864
+ function isVectorMaskType(value) {
2865
+ return typeof value === "string" && VECTOR_MASK_TYPES.includes(value);
2866
+ }
2867
+ function sanitizeVectorMask(value) {
2868
+ if (!value || typeof value !== "object") return null;
2869
+ const r = value;
2870
+ if (!isVectorMaskType(r.type)) return null;
2871
+ return r.invert === true ? { type: r.type, invert: true } : { type: r.type };
2872
+ }
2873
+ function isVectorMaskGroup(node) {
2874
+ return node?.type === "group" && !!node.mask && isVectorMaskType(node.mask.type) && !node.blend && !node.liveBoolean && node.children.length > 0;
2875
+ }
2876
+ function isVectorMaskActive(node) {
2877
+ return isVectorMaskGroup(node) && node.children[0].visible !== false;
2878
+ }
2879
+ function vectorMaskSource(node) {
2880
+ return isVectorMaskGroup(node) ? node.children[0] : null;
2881
+ }
2882
+ function vectorMaskContents(group) {
2883
+ return isVectorMaskGroup(group) ? group.children.slice(1) : group.children;
2884
+ }
2885
+ function vectorMaskOwner(nodes, id) {
2886
+ for (const n of nodes) {
2887
+ if (n.type !== "group") continue;
2888
+ if (isVectorMaskGroup(n) && n.children[0].id === id) return n;
2889
+ const inner = vectorMaskOwner(n.children, id);
2890
+ if (inner) return inner;
2891
+ }
2892
+ return null;
2893
+ }
2894
+ function vectorMaskSummary(node) {
2895
+ if (!isVectorMaskGroup(node)) return null;
2896
+ return {
2897
+ type: node.mask.type,
2898
+ invert: !!node.mask.invert,
2899
+ maskId: node.children[0].id,
2900
+ contents: node.children.slice(1).map((c) => c.id),
2901
+ enabled: node.children[0].visible !== false
2902
+ };
2903
+ }
2904
+ var place = (path, m) => isIdentityMatrix(m) ? path : transformBezierPath(path, m);
2905
+ function vectorMaskRegionShapes(node, type, parent = IDENTITY_MATRIX, self = true) {
2906
+ if (!self && node.visible === false) return [];
2907
+ const m = multiplyMatrices(nodeMatrix(node), parent);
2908
+ if (isVectorMaskActive(node) || node.type === "group" && node.effects?.length) {
2909
+ return vectorAppearanceShapes(node).flatMap((s) => regionOfPath(s.path, s.source, type, m));
2910
+ }
2911
+ const drawn = expandVectorAppearance(node);
2912
+ if (drawn.type === "group") {
2913
+ const inner = multiplyMatrices(nodeMatrix(drawn), parent);
2914
+ return drawn.children.flatMap((c) => vectorMaskRegionShapes(c, type, inner, false));
2915
+ }
2916
+ return regionOfPath(subpathsToBezier(drawn.subpaths), drawn, type, m);
2917
+ }
2918
+ function regionOfPath(path, style, type, m) {
2919
+ if (!path.length) return [];
2920
+ const fillRule = style.fillRule ?? "nonzero";
2921
+ if (type === "clip") return [{ path: place(path, m), fillRule }];
2922
+ const out = [];
2923
+ let filled = false;
2924
+ for (const l of vectorPaintLayerNodes(style)) {
2925
+ if (!l.drawn) continue;
2926
+ if (l.kind === "fill") {
2927
+ if (!filled) out.push({ path: place(path, m), fillRule });
2928
+ filled = true;
2929
+ } else if ((l.node.strokeWidth ?? 1) > 0) {
2930
+ const area = vectorStrokeOutlinePath(path, l.node);
2931
+ if (area.length) out.push({ path: place(area, m), fillRule: "nonzero" });
2932
+ }
2933
+ }
2934
+ return out;
2935
+ }
2936
+ var regionCache = /* @__PURE__ */ new WeakMap();
2937
+ function vectorMaskRegion(group) {
2938
+ let hit = regionCache.get(group);
2939
+ if (!hit) {
2940
+ hit = vectorMaskRegionShapes(group.children[0], group.mask.type);
2941
+ regionCache.set(group, hit);
2942
+ }
2943
+ return hit;
2944
+ }
2945
+ var unionCache = /* @__PURE__ */ new WeakMap();
2946
+ function vectorMaskRegionPath(group) {
2947
+ let hit = unionCache.get(group);
2948
+ if (!hit) {
2949
+ const shapes = vectorMaskRegion(group);
2950
+ hit = !shapes.length ? [] : shapes.length === 1 && shapes[0].fillRule === "nonzero" ? shapes[0].path : pathBoolean("union", shapes.map((s) => ({ path: s.path, fillRule: s.fillRule }))).path;
2951
+ unionCache.set(group, hit);
2952
+ }
2953
+ return hit;
2954
+ }
2955
+ function inRegion(group, p, tolerance) {
2956
+ const pt2 = [p.x, p.y];
2957
+ for (const s of vectorMaskRegion(group)) {
2958
+ if (pointInBezierPath(s.path, pt2, s.fillRule)) return true;
2959
+ if (tolerance > 0 && distanceToBezierPath(s.path, pt2) <= tolerance) return true;
2960
+ }
2961
+ return false;
2962
+ }
2963
+ function vectorMaskContains(group, p, tolerance = 0) {
2964
+ if (!isVectorMaskActive(group)) return true;
2965
+ if (group.mask.type !== "clip") {
2966
+ const source = group.children[0];
2967
+ const hit = hitTestVectorNode(source.visible === false ? { ...source, visible: true } : source, p, group.mask.invert ? 0 : tolerance);
2968
+ return group.mask.invert ? !hit || tolerance > 0 && hitNearEdge(group, p, tolerance) : hit;
2969
+ }
2970
+ if (!group.mask.invert) return inRegion(group, p, tolerance);
2971
+ return !inRegion(group, p, 0) || hitNearEdge(group, p, tolerance);
2972
+ }
2973
+ function hitNearEdge(group, p, tolerance) {
2974
+ if (tolerance <= 0) return false;
2975
+ return vectorMaskRegion(group).some((s) => distanceToBezierPath(s.path, [p.x, p.y]) <= tolerance);
2976
+ }
2977
+ function clipOpen(path, region, invert2) {
2978
+ const out = [];
2979
+ for (const sp of bezierToSubpaths(path)) {
2980
+ const pieces = eraseFromOutline(sp, region, { keep: invert2 ? "outside" : "inside" });
2981
+ if (pieces === null) {
2982
+ const a = sp.anchors[0];
2983
+ const inside = !!a && pointInBezierPath(region, [a.x, a.y], "nonzero");
2984
+ if (inside !== invert2) out.push(...subpathsToBezier([sp]));
2985
+ } else out.push(...subpathsToBezier(pieces));
2986
+ }
2987
+ return out;
2988
+ }
2989
+ function clipShapePath(path, fillRule, region, invert2) {
2990
+ const subpaths = bezierToSubpaths(path);
2991
+ const closed = subpaths.filter((sp) => sp.closed);
2992
+ const open = subpaths.filter((sp) => !sp.closed);
2993
+ const out = [];
2994
+ if (closed.length) {
2995
+ const operands = [{ path: subpathsToBezier(closed), fillRule }, { path: region, fillRule: "nonzero" }];
2996
+ out.push(...region.length ? pathBoolean(invert2 ? "difference" : "intersection", operands).path : invert2 ? operands[0].path : []);
2997
+ }
2998
+ if (open.length) out.push(...region.length ? clipOpen(subpathsToBezier(open), region, invert2) : invert2 ? subpathsToBezier(open) : []);
2999
+ return out;
3000
+ }
3001
+ function vectorMaskedShapes(group) {
3002
+ const contents = group.children.flatMap((c, i) => i === 0 ? [] : vectorChildAppearanceShapes(c, i));
3003
+ const shapes = applyVectorEffects(contents, group.effects);
3004
+ if (!isVectorMaskActive(group)) return shapes;
3005
+ const region = vectorMaskRegionPath(group);
3006
+ const invert2 = !!group.mask.invert;
3007
+ const out = [];
3008
+ for (const s of shapes) {
3009
+ const path = clipShapePath(s.path, s.source.fillRule ?? "nonzero", region, invert2);
3010
+ if (path.length) out.push({ ...s, path, source: s.source.fillRule === "evenodd" ? { ...s.source, fillRule: "nonzero" } : s.source });
3011
+ }
3012
+ return out;
3013
+ }
3014
+ function vectorMaskedGeometry(node) {
3015
+ if (node.type !== "group") return node;
3016
+ if (isVectorMaskActive(node)) {
3017
+ return {
3018
+ id: node.id,
3019
+ type: "group",
3020
+ ...node.transform ? { transform: node.transform } : {},
3021
+ ...node.opacity !== void 0 ? { opacity: node.opacity } : {},
3022
+ ...node.visible === false ? { visible: false } : {},
3023
+ ...node.locked ? { locked: true } : {},
3024
+ children: vectorAppearanceNodes(node)
3025
+ };
3026
+ }
3027
+ let changed = false;
3028
+ const children = node.children.map((c) => {
3029
+ const next = vectorMaskedGeometry(c);
3030
+ if (next !== c) changed = true;
3031
+ return next;
3032
+ });
3033
+ return changed ? { ...node, children } : node;
3034
+ }
3035
+ function vectorMaskedLocalBounds(group) {
3036
+ const b = bezierPathBounds(vectorAppearanceShapes(group).flatMap((s) => s.path));
3037
+ return b ? { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY } : null;
3038
+ }
3039
+ function paintReach(node) {
3040
+ const own = vectorFilterReach(activeVectorFilters(node));
3041
+ if (node.type === "path") return own + vectorStrokeStackReach(node);
3042
+ return own + Math.max(0, ...node.children.map(paintReach));
3043
+ }
3044
+ function vectorMaskContentBounds(group) {
3045
+ const contents = group.children.slice(1);
3046
+ const b = unionBounds(contents.map((c) => vectorNodeBounds(c, IDENTITY_MATRIX)));
3047
+ if (!b) return null;
3048
+ const pad = Math.max(0, ...contents.map(paintReach)) + Math.max(b.width, b.height) * 0.05 + 4;
3049
+ return { x: b.x - pad, y: b.y - pad, width: b.width + 2 * pad, height: b.height + 2 * pad };
3050
+ }
3051
+ var fe2 = (tag, attrs) => ({ tag, attrs });
3052
+ var LUMA = "-0.2125 -0.7154 -0.0721 0 1";
3053
+ function invertFilter(type, id, bounds) {
3054
+ const primitives = type === "alpha" ? [fe2("feColorMatrix", { in: "SourceGraphic", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1" })] : [
3055
+ // Over black first, so a transparent area reads as dark (hidden) before inverting.
3056
+ fe2("feFlood", { "flood-color": "#000000", result: "black" }),
3057
+ fe2("feComposite", { in: "SourceGraphic", in2: "black", operator: "over", result: "flat" }),
3058
+ fe2("feColorMatrix", { in: "flat", type: "matrix", values: `${LUMA} ${LUMA} ${LUMA} 0 0 0 0 1` })
3059
+ ];
3060
+ const r = (n) => Math.round(n * 1e3) / 1e3;
3061
+ return { id, x: r(bounds.x), y: r(bounds.y), width: r(bounds.width), height: r(bounds.height), primitives };
3062
+ }
3063
+ var rectPath = (b) => [
3064
+ { type: "M", x: b.x, y: b.y },
3065
+ { type: "L", x: b.x + b.width, y: b.y },
3066
+ { type: "L", x: b.x + b.width, y: b.y + b.height },
3067
+ { type: "L", x: b.x, y: b.y + b.height },
3068
+ { type: "Z" }
3069
+ ];
3070
+ function vectorMaskRenderData(group, id, filterId, precision = 3) {
3071
+ if (!isVectorMaskActive(group) || group.children.length < 2) return null;
3072
+ const invert2 = !!group.mask.invert;
3073
+ const bounds = vectorMaskContentBounds(group);
3074
+ if (group.mask.type === "clip") {
3075
+ const shapes = vectorMaskRegion(group);
3076
+ if (!invert2) return { type: "clip", invert: invert2, id, clip: shapes.map((s) => ({ d: bezierPathToSvg(s.path, { precision }), clipRule: s.fillRule })) };
3077
+ const box = bounds ?? { x: 0, y: 0, width: 0, height: 0 };
3078
+ const region = vectorMaskRegionPath(group);
3079
+ return { type: "clip", invert: invert2, id, clip: [{ d: bezierPathToSvg([...rectPath(box), ...region], { precision }), clipRule: "evenodd" }] };
3080
+ }
3081
+ if (!bounds) return null;
3082
+ if (!invert2) return { type: group.mask.type, invert: invert2, id, bounds };
3083
+ const filter = invertFilter(group.mask.type, filterId, bounds);
3084
+ return { type: group.mask.type, invert: invert2, id, bounds, filter: `url(#${filterId})`, invertFilter: filter };
3085
+ }
3086
+
2321
3087
  // src/vector/warp.ts
2322
3088
  function polylineAt(pts) {
2323
3089
  const acc = [0];
@@ -2829,7 +3595,7 @@ function applyVectorEffects(shapes, effects) {
2829
3595
  }
2830
3596
  return list;
2831
3597
  }
2832
- function childShapes(child, index) {
3598
+ function vectorChildAppearanceShapes(child, index) {
2833
3599
  if (child.visible === false) return [];
2834
3600
  const m = child.transform;
2835
3601
  const k = child.opacity ?? 1;
@@ -2844,7 +3610,7 @@ function liveBooleanShapes(group) {
2844
3610
  const operands = [];
2845
3611
  let source = null;
2846
3612
  group.children.forEach((child, i) => {
2847
- const shapes = childShapes(child, i);
3613
+ const shapes = vectorChildAppearanceShapes(child, i);
2848
3614
  if (!shapes.length) return;
2849
3615
  source ??= shapes[0].source;
2850
3616
  const path = shapes.length === 1 ? shapes[0].path : pathBoolean("union", shapes.map((s) => ({ path: s.path, fillRule: s.source.fillRule ?? "nonzero" }))).path;
@@ -2858,7 +3624,8 @@ var shapeCache = /* @__PURE__ */ new WeakMap();
2858
3624
  function baseShapes(node) {
2859
3625
  if (node.type === "path") return [{ source: node, path: subpathsToBezier(node.subpaths), opacity: 1, order: [] }];
2860
3626
  if (node.liveBoolean) return liveBooleanShapes(node);
2861
- return node.children.flatMap((c, i) => childShapes(c, i));
3627
+ if (isVectorMaskGroup(node)) return node.children.flatMap((c, i) => i === 0 ? [] : vectorChildAppearanceShapes(c, i));
3628
+ return node.children.flatMap((c, i) => vectorChildAppearanceShapes(c, i));
2862
3629
  }
2863
3630
  function vectorEffectInputPaths(node, effectId) {
2864
3631
  const list = node.effects ?? [];
@@ -2883,7 +3650,7 @@ function vectorEnvelopeFrame(node, effectId) {
2883
3650
  function vectorAppearanceShapes(node) {
2884
3651
  const hit = shapeCache.get(node);
2885
3652
  if (hit) return hit;
2886
- const out = applyVectorEffects(baseShapes(node), node.effects).sort((a, b) => compareOrder(a.order, b.order));
3653
+ const out = (isVectorMaskGroup(node) ? vectorMaskedShapes(node) : applyVectorEffects(baseShapes(node), node.effects)).sort((a, b) => compareOrder(a.order, b.order));
2887
3654
  shapeCache.set(node, out);
2888
3655
  return out;
2889
3656
  }
@@ -2939,6 +3706,11 @@ function expandVectorAppearance(node) {
2939
3706
  if (!hasOwnVectorAppearance(node)) {
2940
3707
  const group = node;
2941
3708
  out = { ...group, children: group.children.map(expandVectorAppearance) };
3709
+ } else if (isVectorMaskGroup(node)) {
3710
+ const { effects, ...group } = node;
3711
+ const [mask, ...contents] = node.children;
3712
+ const wrapper = { id: `${node.id}~content`, type: "group", effects, children: contents };
3713
+ out = { ...group, children: [expandVectorAppearance(mask), ...contents.length ? [expandVectorAppearance(wrapper)] : []] };
2942
3714
  } else {
2943
3715
  const shapes = vectorAppearanceShapes(node);
2944
3716
  if (node.type === "path" && shapes.length === 1) {
@@ -3010,10 +3782,10 @@ function vectorEnteredGroups(nodes, ids) {
3010
3782
  const want = new Set(ids);
3011
3783
  const visit = (list, live) => {
3012
3784
  for (const n of list) {
3013
- const own = n.type === "group" && hasOwnVectorAppearance(n);
3785
+ const own = n.type === "group" && (hasOwnVectorAppearance(n) || isVectorMaskGroup(n));
3014
3786
  if (want.has(n.id)) {
3015
3787
  for (const id of live) out.add(id);
3016
- if (own) out.add(n.id);
3788
+ if (own && hasOwnVectorAppearance(n)) out.add(n.id);
3017
3789
  }
3018
3790
  if (n.type === "group") visit(n.children, own ? [...live, n.id] : live);
3019
3791
  }
@@ -3200,6 +3972,11 @@ function vectorPathWorldBezier(node, parent = IDENTITY_MATRIX, options = {}) {
3200
3972
  return isIdentityMatrix(m) ? path : transformBezierPath(path, m);
3201
3973
  }
3202
3974
  function vectorNodeBounds(node, parent = IDENTITY_MATRIX) {
3975
+ if (isVectorMaskActive(node)) {
3976
+ const m = multiplyMatrices(nodeMatrix(node), parent);
3977
+ const b2 = bezierPathBounds(vectorAppearanceShapes(node).flatMap((s) => isIdentityMatrix(m) ? s.path : transformBezierPath(s.path, m)));
3978
+ return b2 ? { x: b2.minX, y: b2.minY, width: b2.maxX - b2.minX, height: b2.maxY - b2.minY } : null;
3979
+ }
3203
3980
  node = expandVectorAppearance(node);
3204
3981
  if (node.type === "group") {
3205
3982
  const m = multiplyMatrices(nodeMatrix(node), parent);
@@ -3232,6 +4009,14 @@ function boundsFromPoints(a, b) {
3232
4009
  }
3233
4010
  function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX, options = {}) {
3234
4011
  if (node.visible === false) return false;
4012
+ if (isVectorMaskGroup(node)) {
4013
+ const m = multiplyMatrices(nodeMatrix(node), parent);
4014
+ const entered = !!options.entered?.has(node.id);
4015
+ if (!entered && isVectorMaskActive(node) && !vectorMaskContains(node, applyMatrix(invertMatrix(m), p), tolerance)) return false;
4016
+ const shown = !entered && hasOwnVectorAppearance(node) ? expandVectorAppearance(node).children : node.children;
4017
+ for (let i = shown.length - 1; i >= 1; i--) if (hitTestVectorNode(shown[i], p, tolerance, m, options)) return true;
4018
+ return entered && hitTestVectorMaskSource(node, p, tolerance, m, options);
4019
+ }
3235
4020
  if (!(node.type === "group" && (!hasOwnVectorAppearance(node) || options.entered?.has(node.id)))) node = expandVectorAppearance(node);
3236
4021
  if (node.type === "group") {
3237
4022
  const m = multiplyMatrices(nodeMatrix(node), parent);
@@ -3241,6 +4026,18 @@ function hitTestVectorNode(node, p, tolerance = 0, parent = IDENTITY_MATRIX, opt
3241
4026
  if (path.length === 0) return false;
3242
4027
  return hitTestVectorPaintStack(node, path, p, tolerance);
3243
4028
  }
4029
+ function hitTestVectorMaskSource(group, p, tolerance, m, options) {
4030
+ const source = group.children[0];
4031
+ if (source.visible === false) return false;
4032
+ if (group.mask?.type !== "clip") return hitTestVectorNode(source, p, tolerance, m, options);
4033
+ const paths = collectPathNodes(source).filter((n) => n.visible !== false);
4034
+ const pt2 = [p.x, p.y];
4035
+ return paths.some((n) => {
4036
+ const world = vectorParentMatrix([source], n.id);
4037
+ const path = vectorPathWorldBezier(n, multiplyMatrices(world ?? IDENTITY_MATRIX, m));
4038
+ return pointInBezierPath(path, pt2, n.fillRule ?? "nonzero") || distanceToBezierPath(path, pt2) <= tolerance;
4039
+ });
4040
+ }
3244
4041
  function vectorStrokeOutline(node, parent = IDENTITY_MATRIX) {
3245
4042
  if (!hasPaint(node.stroke) || !((node.strokeWidth ?? 1) > 0)) return [];
3246
4043
  return vectorStrokeOutlinePath(vectorPathWorldBezier(node, parent), node);
@@ -3498,14 +4295,14 @@ function roundSubpaths(subpaths, precision = 3) {
3498
4295
 
3499
4296
  // src/vector/pathEffects.ts
3500
4297
  var EPS3 = 1e-9;
3501
- var degenerate = (s) => s.every((p) => Math.abs(p[0] - s[0][0]) < EPS3 && Math.abs(p[1] - s[0][1]) < EPS3);
4298
+ var degenerate2 = (s) => s.every((p) => Math.abs(p[0] - s[0][0]) < EPS3 && Math.abs(p[1] - s[0][1]) < EPS3);
3502
4299
  function raise(seg) {
3503
4300
  if (seg.length !== 3) return seg.map((p) => [p[0], p[1]]);
3504
4301
  const [p0, p1, p2] = seg;
3505
4302
  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];
3506
4303
  }
3507
4304
  function subsOf(path) {
3508
- return pathToContours(path).map((c) => ({ segs: closeContour(c).filter((s) => !degenerate(s)).map(raise), closed: c.closed })).filter((s) => s.segs.length > 0);
4305
+ return pathToContours(path).map((c) => ({ segs: closeContour(c).filter((s) => !degenerate2(s)).map(raise), closed: c.closed })).filter((s) => s.segs.length > 0);
3509
4306
  }
3510
4307
  var subsToPath = (subs) => contoursToPath(subs.map((s) => ({ segments: s.segs, closed: s.closed })));
3511
4308
  var unit2 = (x, y) => {
@@ -3557,7 +4354,7 @@ var Measured = class {
3557
4354
  */
3558
4355
  append(sa, sb, out) {
3559
4356
  for (const piece of trimSegments(this.segs, sa, sb, this.lens)) {
3560
- if (degenerate(piece)) continue;
4357
+ if (degenerate2(piece)) continue;
3561
4358
  const last = out[out.length - 1]?.segs;
3562
4359
  const end = last?.[last.length - 1]?.at(-1);
3563
4360
  if (!end || Math.hypot(end[0] - piece[0][0], end[1] - piece[0][1]) > 1e-6) out.push({ segs: [piece], closed: false });
@@ -4329,14 +5126,14 @@ function lerpStops(a, b, t) {
4329
5126
  function lerpGradient(a, b, t) {
4330
5127
  if (a.type !== b.type) return null;
4331
5128
  const { stops, resampled } = lerpStops(a, b, t);
4332
- const near = t < 0.5 ? a : b;
5129
+ const near2 = t < 0.5 ? a : b;
4333
5130
  const out = { type: a.type, stops };
4334
5131
  if (!resampled) {
4335
5132
  if (a.interpolation && a.interpolation !== "srgb") out.interpolation = a.interpolation;
4336
5133
  if (a.smoothness) out.smoothness = a.smoothness;
4337
5134
  }
4338
- if (near.spread && near.spread !== "pad") out.spread = near.spread;
4339
- if (near.strokeMode && near.strokeMode !== "within") out.strokeMode = near.strokeMode;
5135
+ if (near2.spread && near2.spread !== "pad") out.spread = near2.spread;
5136
+ if (near2.strokeMode && near2.strokeMode !== "within") out.strokeMode = near2.strokeMode;
4340
5137
  const ea = hasExplicitGradientGeometry(a);
4341
5138
  const eb = hasExplicitGradientGeometry(b);
4342
5139
  if (!ea && !eb) {
@@ -4354,7 +5151,7 @@ function lerpGradient(a, b, t) {
4354
5151
  const pa = vectorGradientPoints(a);
4355
5152
  const pb = vectorGradientPoints(b);
4356
5153
  if (pa.units !== pb.units) {
4357
- const src = explicitVectorGradient(near);
5154
+ const src = explicitVectorGradient(near2);
4358
5155
  return { ...src, stops: out.stops, ...out.interpolation ? { interpolation: out.interpolation } : {}, ...out.smoothness ? { smoothness: out.smoothness } : {} };
4359
5156
  }
4360
5157
  out.start = lerpPoint(pa.start, pb.start, t);
@@ -4414,14 +5211,14 @@ function lerpDashes(a, b, t) {
4414
5211
  }
4415
5212
  var SWITCHED_KEYS = ["strokeLinecap", "strokeLinejoin", "fillRule", "strokeAlign", "startArrow", "endArrow", "arrowAlign"];
4416
5213
  function interpolateVectorStyle(a, b, t) {
4417
- const near = t < 0.5 ? a : b;
5214
+ const near2 = t < 0.5 ? a : b;
4418
5215
  const out = {};
4419
5216
  const fill = interpolateVectorPaint(a.fill, b.fill, t);
4420
5217
  if (fill !== void 0) out.fill = fill;
4421
5218
  const stroke = interpolateVectorPaint(a.stroke, b.stroke, t);
4422
5219
  if (stroke !== void 0) out.stroke = stroke;
4423
5220
  if (a.strokeWidth !== void 0 || b.strokeWidth !== void 0) out.strokeWidth = lerpN(a.strokeWidth ?? 1, b.strokeWidth ?? 1, t);
4424
- for (const key of SWITCHED_KEYS) if (near[key] !== void 0 && near[key] !== null) out[key] = near[key];
5221
+ for (const key of SWITCHED_KEYS) if (near2[key] !== void 0 && near2[key] !== null) out[key] = near2[key];
4425
5222
  for (const key of ["startArrowScale", "endArrowScale"])
4426
5223
  if (a[key] !== void 0 || b[key] !== void 0) out[key] = lerpN(a[key] ?? 1, b[key] ?? 1, t);
4427
5224
  const dash = lerpDashes(a.strokeDasharray, b.strokeDasharray, t);
@@ -4486,283 +5283,88 @@ function preparePathMorph(a, b, options, ma = nodeMatrix(a), mb = nodeMatrix(b))
4486
5283
  return node;
4487
5284
  };
4488
5285
  }
4489
- function flattenPaths(node, parent = IDENTITY_MATRIX) {
4490
- const m = multiplyMatrices(nodeMatrix(node), parent);
4491
- if (node.type === "path") return [{ path: node, m, opacity: 1 }];
4492
- const o = node.opacity ?? 1;
4493
- return node.children.filter((c) => c.visible !== false).flatMap((c) => flattenPaths(c, m)).map((e) => ({ ...e, opacity: e.opacity * o }));
4494
- }
4495
- var placedBounds = (path, m) => boundsOfPoints(contoursOf(path, m).flatMap((c) => c.pts));
4496
- function collapsedPath(path, m, from, to) {
4497
- const own = placedBounds(path, m);
4498
- const c = own ? [own.x + own.width / 2, own.y + own.height / 2] : [0, 0];
4499
- const fx = from && from.width > 0 ? (c[0] - from.x) / from.width : 0.5;
4500
- const fy = from && from.height > 0 ? (c[1] - from.y) / from.height : 0.5;
4501
- const p = to ? { x: to.x + to.width * fx, y: to.y + to.height * fy } : { x: c[0], y: c[1] };
4502
- const subpaths = path.subpaths.map((sp) => ({ closed: sp.closed, anchors: sp.anchors.map(() => ({ x: p.x, y: p.y })) }));
4503
- const { transform: _t, ...rest } = path;
4504
- return { ...rest, subpaths };
4505
- }
4506
- function createVectorMorph(a, b, options = {}) {
4507
- if (a.type === "path" && b.type === "path") {
4508
- const at2 = preparePathMorph(a, b, options);
4509
- return { at: at2 };
4510
- }
4511
- const pa = flattenPaths(a);
4512
- const pb = flattenPaths(b);
4513
- const ba = unionBounds(pa.map((e) => placedBounds(e.path, e.m)));
4514
- const bb = unionBounds(pb.map((e) => placedBounds(e.path, e.m)));
4515
- const id = options.id ?? a.id;
4516
- const parts = [];
4517
- const opacities = [];
4518
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
4519
- const x = pa[i];
4520
- const y = pb[i];
4521
- const sub5 = { ...options, id: `${id}-${i}`, starts: void 0 };
4522
- if (x && y) {
4523
- parts.push(preparePathMorph(x.path, y.path, sub5, x.m, y.m));
4524
- opacities.push([x.opacity, y.opacity]);
4525
- } else if (x) {
4526
- parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub5, x.m, IDENTITY_MATRIX));
4527
- opacities.push([x.opacity, x.opacity]);
4528
- } else if (y) {
4529
- parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub5, IDENTITY_MATRIX, y.m));
4530
- opacities.push([y.opacity, y.opacity]);
4531
- }
4532
- }
4533
- return {
4534
- at: (t) => {
4535
- const children = parts.map((p, i) => {
4536
- const n = p(t);
4537
- const o = lerpN(opacities[i][0], opacities[i][1], t) * (n.opacity ?? 1);
4538
- if (o < 1) n.opacity = o;
4539
- else delete n.opacity;
4540
- return n;
4541
- });
4542
- const group = { id, type: "group", children };
4543
- return group;
4544
- }
4545
- };
4546
- }
4547
- function interpolateVectorPaths(a, b, t, options = {}) {
4548
- return createVectorMorph(a, b, options).at(t);
4549
- }
4550
- var keyframeMorphs = /* @__PURE__ */ new WeakMap();
4551
- function evaluateVectorPathKeyframes(keyframes, time, options = {}) {
4552
- const keys = keyframes.filter((k) => Number.isFinite(k.time)).sort((x, y) => x.time - y.time);
4553
- if (!keys.length) return null;
4554
- if (!(time > keys[0].time)) return keys[0].value;
4555
- const last = keys[keys.length - 1];
4556
- if (time >= last.time) return last.value;
4557
- let i = 0;
4558
- while (i + 1 < keys.length && keys[i + 1].time <= time) i++;
4559
- const a = keys[i];
4560
- const b = keys[i + 1];
4561
- if (time === a.time) return a.value;
4562
- if (a.easing === "hold") return a.value;
4563
- const span = b.time - a.time;
4564
- const u = Math.min(1, Math.max(0, evaluateEasing(a.easing, (time - a.time) / span, span)));
4565
- let inner = keyframeMorphs.get(a.value);
4566
- if (!inner) keyframeMorphs.set(a.value, inner = /* @__PURE__ */ new WeakMap());
4567
- let morph = inner.get(b.value);
4568
- if (!morph) inner.set(b.value, morph = createVectorMorph(a.value, b.value, options));
4569
- return morph.at(u);
4570
- }
4571
-
4572
- // src/vector/filters.ts
4573
- var VECTOR_FILTER_DEFAULTS = {
4574
- dropShadow: { color: "#000000", opacity: 0.25, offsetX: 0, offsetY: 4, blur: 4, spread: 0 },
4575
- innerShadow: { color: "#000000", opacity: 0.25, offsetX: 0, offsetY: 4, blur: 4, spread: 0 },
4576
- outerGlow: { color: "#ffffbe", opacity: 0.75, blur: 10, spread: 0 },
4577
- innerGlow: { color: "#ffffbe", opacity: 0.75, blur: 10, spread: 0 },
4578
- layerBlur: { radius: 4 },
4579
- feather: { radius: 8 }
4580
- };
4581
- var VECTOR_FILTER_LABELS = {
4582
- dropShadow: "Drop Shadow",
4583
- innerShadow: "Inner Shadow",
4584
- outerGlow: "Outer Glow",
4585
- innerGlow: "Inner Glow",
4586
- layerBlur: "Layer Blur",
4587
- feather: "Feather"
4588
- };
4589
- var SHADOW_SPECS = [
4590
- { key: "color", label: "Color", kind: "color" },
4591
- { key: "opacity", label: "Opacity", kind: "number", min: 0, max: 1, step: 0.01, slider: true },
4592
- { key: "offsetX", label: "X", kind: "number", min: -1e3, max: 1e3, step: 1 },
4593
- { key: "offsetY", label: "Y", kind: "number", min: -1e3, max: 1e3, step: 1 },
4594
- { key: "blur", label: "Blur", kind: "number", min: 0, max: 500, step: 1 },
4595
- { key: "spread", label: "Spread", kind: "number", min: 0, max: 500, step: 1 }
4596
- ];
4597
- var GLOW_SPECS = SHADOW_SPECS.filter((s) => s.key !== "offsetX" && s.key !== "offsetY");
4598
- var VECTOR_FILTER_PARAM_SPECS = {
4599
- dropShadow: SHADOW_SPECS,
4600
- innerShadow: SHADOW_SPECS.map((s) => s.key === "spread" ? { ...s, label: "Choke" } : s),
4601
- outerGlow: GLOW_SPECS,
4602
- innerGlow: GLOW_SPECS.map((s) => s.key === "spread" ? { ...s, label: "Choke" } : s),
4603
- layerBlur: [{ key: "radius", label: "Radius", kind: "number", min: 0, max: 500, step: 1, slider: true }],
4604
- feather: [{ key: "radius", label: "Radius", kind: "number", min: 0, max: 500, step: 1, slider: true }]
4605
- };
4606
- function isVectorFilterType(value) {
4607
- return typeof value === "string" && VECTOR_FILTER_TYPES.includes(value);
4608
- }
4609
- function normalizeVectorFilterParams(type, params, base) {
4610
- if (!isVectorFilterType(type)) return null;
4611
- const out = { ...VECTOR_FILTER_DEFAULTS[type], ...base ?? {} };
4612
- for (const spec of VECTOR_FILTER_PARAM_SPECS[type]) {
4613
- const v2 = params?.[spec.key];
4614
- if (v2 === void 0) continue;
4615
- if (spec.kind === "number" && typeof v2 === "number" && Number.isFinite(v2)) out[spec.key] = Math.min(spec.max ?? Infinity, Math.max(spec.min ?? -Infinity, v2));
4616
- else if (spec.kind === "color" && typeof v2 === "string" && v2.trim()) out[spec.key] = v2.trim();
4617
- }
4618
- return out;
4619
- }
4620
- function vectorFilterParamKeys(type) {
4621
- return VECTOR_FILTER_PARAM_SPECS[type].map((s) => s.key);
4622
- }
4623
- function createVectorFilter(type, id, params) {
4624
- const p = normalizeVectorFilterParams(type, params);
4625
- return p ? { id, type, params: p } : null;
4626
- }
4627
- function sanitizeVectorFilters(value) {
4628
- if (!Array.isArray(value)) return [];
4629
- const out = [];
4630
- const used = /* @__PURE__ */ new Set();
4631
- for (const raw of value) {
4632
- if (!raw || typeof raw !== "object") continue;
4633
- const r = raw;
4634
- if (!isVectorFilterType(r.type)) continue;
4635
- let id = typeof r.id === "string" && r.id && !used.has(r.id) ? r.id : "";
4636
- for (let k = out.length + 1; !id; k++) if (!used.has(`filter${k}`)) id = `filter${k}`;
4637
- used.add(id);
4638
- const filter = createVectorFilter(r.type, id, r.params ?? {});
4639
- out.push(r.enabled === false ? { ...filter, enabled: false } : filter);
4640
- }
4641
- return out;
4642
- }
4643
- function activeVectorFilters(node) {
4644
- return (node.filters ?? []).filter((f) => {
4645
- if (f.enabled === false || !isVectorFilterType(f.type)) return false;
4646
- if (f.type === "layerBlur" || f.type === "feather") return f.params.radius > 0;
4647
- return f.params.opacity > 0;
4648
- });
5286
+ function flattenPaths(node, parent = IDENTITY_MATRIX) {
5287
+ const m = multiplyMatrices(nodeMatrix(node), parent);
5288
+ if (node.type === "path") return [{ path: node, m, opacity: 1 }];
5289
+ const o = node.opacity ?? 1;
5290
+ return node.children.filter((c) => c.visible !== false).flatMap((c) => flattenPaths(c, m)).map((e) => ({ ...e, opacity: e.opacity * o }));
4649
5291
  }
4650
- function vectorFilterReach(filters) {
4651
- let outer = 0;
4652
- let blur = 0;
4653
- for (const f of filters) {
4654
- switch (f.type) {
4655
- case "dropShadow":
4656
- case "innerShadow":
4657
- outer = Math.max(outer, Math.max(Math.abs(f.params.offsetX), Math.abs(f.params.offsetY)) + f.params.blur * 1.5 + f.params.spread);
4658
- break;
4659
- case "outerGlow":
4660
- case "innerGlow":
4661
- outer = Math.max(outer, f.params.blur * 1.5 + f.params.spread);
4662
- break;
4663
- case "layerBlur":
4664
- blur += f.params.radius * 1.5;
4665
- break;
4666
- }
4667
- }
4668
- return outer + blur;
5292
+ var placedBounds = (path, m) => boundsOfPoints(contoursOf(path, m).flatMap((c) => c.pts));
5293
+ function collapsedPath(path, m, from, to) {
5294
+ const own = placedBounds(path, m);
5295
+ const c = own ? [own.x + own.width / 2, own.y + own.height / 2] : [0, 0];
5296
+ const fx = from && from.width > 0 ? (c[0] - from.x) / from.width : 0.5;
5297
+ const fy = from && from.height > 0 ? (c[1] - from.y) / from.height : 0.5;
5298
+ const p = to ? { x: to.x + to.width * fx, y: to.y + to.height * fy } : { x: c[0], y: c[1] };
5299
+ const subpaths = path.subpaths.map((sp) => ({ closed: sp.closed, anchors: sp.anchors.map(() => ({ x: p.x, y: p.y })) }));
5300
+ const { transform: _t, ...rest } = path;
5301
+ return { ...rest, subpaths };
4669
5302
  }
4670
- var round = (n) => Math.round(n * 1e3) / 1e3;
4671
- var fe = (tag, attrs, children) => ({ tag, attrs, ...children ? { children } : {} });
4672
- function vectorFilterSvg(filters, id, bounds) {
4673
- const active = filters.filter((f) => activeVectorFilters({ filters: [f] }).length);
4674
- if (!active.length || !bounds) return null;
4675
- const primitives = [];
4676
- let body = "SourceGraphic";
4677
- let n = 0;
4678
- const name = (part) => `${part}${n}`;
4679
- for (const f of active) {
4680
- if (f.type !== "innerShadow" && f.type !== "innerGlow") continue;
4681
- n++;
4682
- const p = f.params;
4683
- primitives.push(fe("feComponentTransfer", { in: "SourceAlpha", result: name("inv") }, [fe("feFuncA", { type: "table", tableValues: "1 0" })]));
4684
- let last = name("inv");
4685
- if (f.type === "innerShadow" && (f.params.offsetX || f.params.offsetY)) {
4686
- primitives.push(fe("feOffset", { in: last, dx: round(f.params.offsetX), dy: round(f.params.offsetY), result: name("off") }));
4687
- last = name("off");
4688
- }
4689
- if (p.spread > 0) {
4690
- primitives.push(fe("feMorphology", { in: last, operator: "dilate", radius: round(p.spread), result: name("grow") }));
4691
- last = name("grow");
4692
- }
4693
- if (p.blur > 0) {
4694
- primitives.push(fe("feGaussianBlur", { in: last, stdDeviation: round(p.blur / 2), result: name("blur") }));
4695
- last = name("blur");
4696
- }
4697
- primitives.push(fe("feFlood", { "flood-color": p.color, "flood-opacity": round(p.opacity), result: name("color") }));
4698
- primitives.push(fe("feComposite", { in: name("color"), in2: last, operator: "in", result: name("tint") }));
4699
- primitives.push(fe("feComposite", { in: name("tint"), in2: "SourceAlpha", operator: "in", result: name("inner") }));
4700
- primitives.push(fe("feComposite", { in: name("inner"), in2: body, operator: "over", result: name("body") }));
4701
- body = name("body");
4702
- }
4703
- for (const f of active) {
4704
- if (f.type !== "feather") continue;
4705
- n++;
4706
- const r = f.params.radius;
4707
- primitives.push(fe("feMorphology", { in: "SourceAlpha", operator: "erode", radius: round(r / 2), result: name("core") }));
4708
- primitives.push(fe("feGaussianBlur", { in: name("core"), stdDeviation: round(r / 4), result: name("fade") }));
4709
- primitives.push(fe("feComposite", { in: body, in2: name("fade"), operator: "in", result: name("body") }));
4710
- body = name("body");
5303
+ function createVectorMorph(a, b, options = {}) {
5304
+ if (a.type === "path" && b.type === "path") {
5305
+ const at2 = preparePathMorph(a, b, options);
5306
+ return { at: at2 };
4711
5307
  }
4712
- const under = [];
4713
- for (const f of active) {
4714
- if (f.type !== "dropShadow" && f.type !== "outerGlow") continue;
4715
- n++;
4716
- const p = f.params;
4717
- let last = "SourceAlpha";
4718
- if (p.spread > 0) {
4719
- primitives.push(fe("feMorphology", { in: last, operator: "dilate", radius: round(p.spread), result: name("grow") }));
4720
- last = name("grow");
4721
- }
4722
- if (p.blur > 0) {
4723
- primitives.push(fe("feGaussianBlur", { in: last, stdDeviation: round(p.blur / 2), result: name("blur") }));
4724
- last = name("blur");
4725
- }
4726
- if (f.type === "dropShadow" && (f.params.offsetX || f.params.offsetY)) {
4727
- primitives.push(fe("feOffset", { in: last, dx: round(f.params.offsetX), dy: round(f.params.offsetY), result: name("off") }));
4728
- last = name("off");
5308
+ const pa = flattenPaths(a);
5309
+ const pb = flattenPaths(b);
5310
+ const ba = unionBounds(pa.map((e) => placedBounds(e.path, e.m)));
5311
+ const bb = unionBounds(pb.map((e) => placedBounds(e.path, e.m)));
5312
+ const id = options.id ?? a.id;
5313
+ const parts = [];
5314
+ const opacities = [];
5315
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
5316
+ const x = pa[i];
5317
+ const y = pb[i];
5318
+ const sub5 = { ...options, id: `${id}-${i}`, starts: void 0 };
5319
+ if (x && y) {
5320
+ parts.push(preparePathMorph(x.path, y.path, sub5, x.m, y.m));
5321
+ opacities.push([x.opacity, y.opacity]);
5322
+ } else if (x) {
5323
+ parts.push(preparePathMorph(x.path, collapsedPath(x.path, x.m, ba, bb), sub5, x.m, IDENTITY_MATRIX));
5324
+ opacities.push([x.opacity, x.opacity]);
5325
+ } else if (y) {
5326
+ parts.push(preparePathMorph(collapsedPath(y.path, y.m, bb, ba), y.path, sub5, IDENTITY_MATRIX, y.m));
5327
+ opacities.push([y.opacity, y.opacity]);
4729
5328
  }
4730
- primitives.push(fe("feFlood", { "flood-color": p.color, "flood-opacity": round(p.opacity), result: name("color") }));
4731
- primitives.push(fe("feComposite", { in: name("color"), in2: last, operator: "in", result: name("shadow") }));
4732
- under.push(name("shadow"));
4733
- }
4734
- if (under.length) {
4735
- n++;
4736
- primitives.push(fe("feMerge", { result: name("merged") }, [...under, body].map((i) => fe("feMergeNode", { in: i }))));
4737
- body = name("merged");
4738
- }
4739
- for (const f of active) {
4740
- if (f.type !== "layerBlur") continue;
4741
- n++;
4742
- primitives.push(fe("feGaussianBlur", { in: body, stdDeviation: round(f.params.radius / 2), result: name("body") }));
4743
- body = name("body");
4744
5329
  }
4745
- const lastResult = primitives[primitives.length - 1]?.attrs.result;
4746
- if (lastResult !== body) primitives.push(fe("feMerge", {}, [fe("feMergeNode", { in: body })]));
4747
- const pad = vectorFilterReach(active) + 2;
4748
5330
  return {
4749
- id,
4750
- x: round(bounds.x - pad),
4751
- y: round(bounds.y - pad),
4752
- width: round(bounds.width + 2 * pad),
4753
- height: round(bounds.height + 2 * pad),
4754
- primitives
5331
+ at: (t) => {
5332
+ const children = parts.map((p, i) => {
5333
+ const n = p(t);
5334
+ const o = lerpN(opacities[i][0], opacities[i][1], t) * (n.opacity ?? 1);
5335
+ if (o < 1) n.opacity = o;
5336
+ else delete n.opacity;
5337
+ return n;
5338
+ });
5339
+ const group = { id, type: "group", children };
5340
+ return group;
5341
+ }
4755
5342
  };
4756
5343
  }
4757
- var escapeAttr = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4758
- function primitiveMarkup(p) {
4759
- const attrs = Object.entries(p.attrs).map(([k, v2]) => ` ${k}="${escapeAttr(String(v2))}"`).join("");
4760
- return p.children?.length ? `<${p.tag}${attrs}>${p.children.map(primitiveMarkup).join("")}</${p.tag}>` : `<${p.tag}${attrs}/>`;
5344
+ function interpolateVectorPaths(a, b, t, options = {}) {
5345
+ return createVectorMorph(a, b, options).at(t);
4761
5346
  }
4762
- function vectorFilterMarkup(filter, extra = "") {
4763
- return `<filter id="${escapeAttr(filter.id)}" filterUnits="userSpaceOnUse" x="${filter.x}" y="${filter.y}" width="${filter.width}" height="${filter.height}" color-interpolation-filters="sRGB"${extra}>${filter.primitives.map(primitiveMarkup).join("")}</filter>`;
5347
+ var keyframeMorphs = /* @__PURE__ */ new WeakMap();
5348
+ function evaluateVectorPathKeyframes(keyframes, time, options = {}) {
5349
+ const keys = keyframes.filter((k) => Number.isFinite(k.time)).sort((x, y) => x.time - y.time);
5350
+ if (!keys.length) return null;
5351
+ if (!(time > keys[0].time)) return keys[0].value;
5352
+ const last = keys[keys.length - 1];
5353
+ if (time >= last.time) return last.value;
5354
+ let i = 0;
5355
+ while (i + 1 < keys.length && keys[i + 1].time <= time) i++;
5356
+ const a = keys[i];
5357
+ const b = keys[i + 1];
5358
+ if (time === a.time) return a.value;
5359
+ if (a.easing === "hold") return a.value;
5360
+ const span = b.time - a.time;
5361
+ const u = Math.min(1, Math.max(0, evaluateEasing(a.easing, (time - a.time) / span, span)));
5362
+ let inner = keyframeMorphs.get(a.value);
5363
+ if (!inner) keyframeMorphs.set(a.value, inner = /* @__PURE__ */ new WeakMap());
5364
+ let morph = inner.get(b.value);
5365
+ if (!morph) inner.set(b.value, morph = createVectorMorph(a.value, b.value, options));
5366
+ return morph.at(u);
4764
5367
  }
4765
- var VECTOR_FILTER_MENU = VECTOR_FILTER_TYPES.map((type) => ({ type, label: VECTOR_FILTER_LABELS[type] }));
4766
5368
 
4767
5369
  // src/vector/gradientStroke.ts
4768
5370
  var SHARP = 1.25;
@@ -5199,7 +5801,45 @@ function vectorDocumentToSvg(doc, options = {}) {
5199
5801
  emitNode(node, depth + wrappers.length);
5200
5802
  for (let i = wrappers.length - 1; i >= 0; i--) lines.push(`${indent(depth + i)}</g>`);
5201
5803
  };
5804
+ const capture = (fn) => {
5805
+ const at2 = lines.length;
5806
+ fn();
5807
+ return lines.splice(at2).map((l) => l.trim()).join("");
5808
+ };
5809
+ const emitMaskGroup = (node, depth) => {
5810
+ const [source, ...contents] = node.children;
5811
+ const body = node.effects?.length && contents.length ? [{ id: `${node.id}~content`, type: "group", effects: node.effects, children: contents }] : contents;
5812
+ const settings = { type: node.mask.type, ...node.mask.invert ? { invert: true } : {} };
5813
+ lines.push(`${indent(depth)}<g${common(node)} data-vector-mask="${escapeAttr2(JSON.stringify(settings))}">`);
5814
+ const mask = vectorMaskRenderData(node, `${prefix}mask-${node.id}`, `${prefix}mask-filter-${node.id}`, precision);
5815
+ let ref = "";
5816
+ if (mask && mask.type !== "clip") {
5817
+ const b = mask.bounds;
5818
+ const box = `x="${num2(b.x)}" y="${num2(b.y)}" width="${num2(b.width)}" height="${num2(b.height)}"`;
5819
+ if (mask.invertFilter) defs.push(vectorFilterMarkup(mask.invertFilter));
5820
+ const inner = capture(() => emitNode(source, depth + 1));
5821
+ const drawn = mask.filter ? `<g data-vector-part="mask-invert" filter="${escapeAttr2(mask.filter)}">${inner}</g>` : inner;
5822
+ defs.push(`<mask id="${escapeAttr2(mask.id)}" data-vector-part="mask-source" maskUnits="userSpaceOnUse" ${box}${mask.type === "alpha" ? ' mask-type="alpha"' : ""}>${drawn}</mask>`);
5823
+ ref = ` mask="url(#${escapeAttr2(mask.id)})"`;
5824
+ } else {
5825
+ lines.push(`${indent(depth + 1)}<g data-vector-part="mask-source" display="none">`);
5826
+ emitNode(source, depth + 2);
5827
+ lines.push(`${indent(depth + 1)}</g>`);
5828
+ if (mask?.clip) {
5829
+ defs.push(`<clipPath id="${escapeAttr2(mask.id)}">${mask.clip.map((c) => `<path d="${c.d}" clip-rule="${c.clipRule}"/>`).join("")}</clipPath>`);
5830
+ ref = ` clip-path="url(#${escapeAttr2(mask.id)})"`;
5831
+ }
5832
+ }
5833
+ lines.push(`${indent(depth + 1)}<g data-vector-part="masked"${ref}>`);
5834
+ for (const child of body) emit2(child, depth + 2);
5835
+ lines.push(`${indent(depth + 1)}</g>`);
5836
+ lines.push(`${indent(depth)}</g>`);
5837
+ };
5202
5838
  const emitNode = (node, depth) => {
5839
+ if (isVectorMaskGroup(node)) {
5840
+ emitMaskGroup(node, depth);
5841
+ return;
5842
+ }
5203
5843
  if (hasOwnVectorAppearance(node)) {
5204
5844
  const live2 = {
5205
5845
  kind: node.type,
@@ -5402,6 +6042,26 @@ function collectGradients(el, raw) {
5402
6042
  if (PAINT_SERVERS.has(el.name) && el.attrs.id) raw.set(el.attrs.id, el);
5403
6043
  for (const c of el.children) collectGradients(c, raw);
5404
6044
  }
6045
+ function collectMasks(el, raw) {
6046
+ if ((el.name === "clipPath" || el.name === "mask") && el.attrs.id) raw.set(el.attrs.id, el);
6047
+ for (const c of el.children) collectMasks(c, raw);
6048
+ }
6049
+ function attrOrStyle(el, name) {
6050
+ for (const decl of (el.attrs.style ?? "").split(";")) {
6051
+ const i = decl.indexOf(":");
6052
+ if (i >= 0 && decl.slice(0, i).trim() === name) return decl.slice(i + 1).replace(/!important/, "").trim();
6053
+ }
6054
+ return el.attrs[name];
6055
+ }
6056
+ function urlId(value) {
6057
+ return /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec((value ?? "").trim())?.[1] ?? null;
6058
+ }
6059
+ function clipRulesAsFillRules(el) {
6060
+ const attrs = { ...el.attrs };
6061
+ const rule = attrOrStyle(el, "clip-rule");
6062
+ if (rule) attrs["fill-rule"] = rule;
6063
+ return { ...el, attrs, children: el.children.map(clipRulesAsFillRules) };
6064
+ }
5405
6065
  function collectFilters(el, raw) {
5406
6066
  if (el.name === "filter" && el.attrs.id) raw.set(el.attrs.id, el);
5407
6067
  for (const c of el.children) collectFilters(c, raw);
@@ -5540,6 +6200,9 @@ function parseVectorSvg(svg, options = {}) {
5540
6200
  collectGradients(root, rawGradients);
5541
6201
  const rawFilters = /* @__PURE__ */ new Map();
5542
6202
  collectFilters(root, rawFilters);
6203
+ const rawMasks = /* @__PURE__ */ new Map();
6204
+ collectMasks(root, rawMasks);
6205
+ const resolving = /* @__PURE__ */ new Set();
5543
6206
  const filtersOf = (value) => {
5544
6207
  const url = /^url\(\s*['"]?#([^'")\s]+)['"]?\s*\)/.exec((value ?? "").trim());
5545
6208
  const el = url ? rawFilters.get(url[1]) : void 0;
@@ -5738,68 +6401,148 @@ function parseVectorSvg(svg, options = {}) {
5738
6401
  const out = [];
5739
6402
  for (const child of el.children) {
5740
6403
  if (SKIP.has(child.name)) continue;
5741
- const m = multiplyMatrices(parseSvgTransform(child.attrs.transform), matrix);
5742
- const s = readStyle(child, style);
5743
- const opacity = child.attrs.opacity !== void 0 ? parseFloat(child.attrs.opacity) : void 0;
5744
- const hidden = child.attrs.display === "none" || child.attrs.visibility === "hidden";
5745
- const blendMode = vectorBlendModeFromCss(s.mixBlendMode);
5746
- const filters = filtersOf(s.filter);
5747
- const meta = {
5748
- ...child.attrs["inkscape:label"] || child.attrs["data-name"] ? { name: child.attrs["inkscape:label"] ?? child.attrs["data-name"] } : {},
5749
- ...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
5750
- ...hidden ? { visible: false } : {},
5751
- ...blendMode && blendMode !== "normal" ? { blendMode } : {},
5752
- ...filters.length ? { filters } : {}
5753
- };
5754
- if (child.attrs["data-vector-blend-step"] !== void 0) continue;
5755
- const operation = child.attrs["data-vector-boolean-operation"];
5756
- if (BOOLEAN_OPERATIONS.includes(operation)) meta.booleanOperation = operation;
5757
- if (child.name === "g" && child.attrs["data-vector-live"] !== void 0) {
5758
- const node2 = liveNode(child, s, m, meta);
5759
- if (node2) out.push(node2);
5760
- continue;
5761
- }
5762
- if (child.name === "g" && child.attrs["data-vector-path"] !== void 0 && child.attrs["data-vector-fills"] !== void 0) {
5763
- const node2 = stackedPath(child, s, m, meta);
5764
- if (node2) out.push(node2);
5765
- continue;
5766
- }
5767
- if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
5768
- const node2 = mergedPath(child, s, m, meta);
5769
- if (node2) out.push(node2);
5770
- continue;
5771
- }
5772
- if (child.name === "g" || child.name === "svg" || child.name === "a" || child.name === "switch") {
5773
- const children = visit(child, s, m);
5774
- if (!children.length) continue;
5775
- const group = { id: idFor(child), type: "group", ...meta, children };
5776
- const blend = child.name === "g" ? blendFromAttribute(child.attrs["data-vector-blend"], m) : null;
5777
- if (blend) group.blend = blend;
5778
- out.push(group);
5779
- continue;
5780
- }
5781
- const subpaths = shapeOf(child);
5782
- if (!subpaths || !subpaths.length) continue;
5783
- const node = placed(
5784
- withOpacities(
5785
- {
5786
- id: idFor(child),
5787
- type: "path",
5788
- ...meta,
5789
- subpaths,
5790
- fill: child.name === "line" ? null : paint(s.fill),
5791
- stroke: paint(s.stroke),
5792
- ...strokeStyle(s)
5793
- },
5794
- s
5795
- ),
5796
- m
5797
- );
5798
- out.push(node);
6404
+ const nodes2 = visitOne(child, style, matrix);
6405
+ out.push(...nodes2.length ? masked(child, nodes2, multiplyMatrices(parseSvgTransform(child.attrs.transform), matrix)) : nodes2);
5799
6406
  }
5800
6407
  return options.mergeStackedShapes ? mergeStacked(out) : out;
5801
6408
  };
5802
- const nodes = visit(root, readStyle(root, { fill: "#000", stroke: "none", strokeWidth: "" }), rootMatrix);
6409
+ let rootStyle = { fill: "#000", stroke: "none", strokeWidth: "" };
6410
+ const bboxMatrix = (nodes2) => {
6411
+ const b = unionBounds(nodes2.map((n) => vectorNodeBounds(n)));
6412
+ return b && b.width > 0 && b.height > 0 ? [b.width, 0, 0, b.height, b.x, b.y] : null;
6413
+ };
6414
+ const maskNode = (el, m, nodes2, units) => {
6415
+ const id = el.attrs.id;
6416
+ if (!id || resolving.has(id)) return null;
6417
+ let space = m;
6418
+ if (units === "objectBoundingBox") {
6419
+ const box = bboxMatrix(nodes2);
6420
+ if (!box) return null;
6421
+ space = box;
6422
+ }
6423
+ resolving.add(id);
6424
+ const isClip = el.name === "clipPath";
6425
+ const content = isClip ? clipRulesAsFillRules(el) : el;
6426
+ const inner = visit(content, readStyle(content, rootStyle), multiplyMatrices(parseSvgTransform(el.attrs.transform), space));
6427
+ resolving.delete(id);
6428
+ const strip2 = (n) => n.type === "path" ? withVectorStrokes(withVectorFills({ ...n, fill: null, stroke: null }, []), []) : { ...n, children: n.children.map(strip2) };
6429
+ const list = isClip ? inner.map(strip2) : inner;
6430
+ if (!list.length) return null;
6431
+ return list.length === 1 ? list[0] : { id: idFor({ name: "g", attrs: {}, children: [] }), type: "group", name: isClip ? "Clip" : "Mask", children: list };
6432
+ };
6433
+ const masked = (el, nodes2, m) => {
6434
+ let out = nodes2;
6435
+ const clipEl = rawMasks.get(urlId(attrOrStyle(el, "clip-path")) ?? "");
6436
+ if (clipEl?.name === "clipPath") {
6437
+ const source = maskNode(clipEl, m, out, clipEl.attrs.clipPathUnits);
6438
+ if (source) out = [{ id: idFor({ name: "g", attrs: {}, children: [] }), type: "group", name: "Clip Group", mask: { type: "clip" }, children: [source, ...out] }];
6439
+ }
6440
+ const maskEl = rawMasks.get(urlId(attrOrStyle(el, "mask")) ?? "");
6441
+ if (maskEl?.name === "mask") {
6442
+ const source = maskNode(maskEl, m, out, maskEl.attrs.maskContentUnits);
6443
+ const type = attrOrStyle(maskEl, "mask-type") === "alpha" ? "alpha" : "luminance";
6444
+ if (source) out = [{ id: idFor({ name: "g", attrs: {}, children: [] }), type: "group", name: "Opacity Mask", mask: { type }, children: [source, ...out] }];
6445
+ }
6446
+ return out;
6447
+ };
6448
+ const maskGroup = (g, style, m, meta) => {
6449
+ let raw = null;
6450
+ try {
6451
+ raw = JSON.parse(g.attrs["data-vector-mask"]);
6452
+ } catch {
6453
+ raw = null;
6454
+ }
6455
+ const mask = sanitizeVectorMask(raw);
6456
+ const part = (name) => g.children.find((c) => c.name === "g" && c.attrs["data-vector-part"] === name);
6457
+ const maskedEl = part("masked");
6458
+ if (!mask || !maskedEl) return null;
6459
+ const id = idFor(g);
6460
+ let sourceEl = part("mask-source");
6461
+ if (!sourceEl) {
6462
+ const el = rawMasks.get(urlId(maskedEl.attrs.mask) ?? "");
6463
+ const invert2 = el?.children.find((c) => c.name === "g" && c.attrs["data-vector-part"] === "mask-invert");
6464
+ sourceEl = invert2 ?? el;
6465
+ }
6466
+ const source = sourceEl ? visit(sourceEl, style, m)[0] : void 0;
6467
+ let contents = visit(maskedEl, style, m);
6468
+ let effects;
6469
+ const wrapper = contents.length === 1 && contents[0].type === "group" && contents[0].id === `${id}~content` ? contents[0] : null;
6470
+ if (wrapper?.type === "group") {
6471
+ effects = wrapper.effects ?? void 0;
6472
+ contents = wrapper.children;
6473
+ }
6474
+ if (!source) return contents.length ? { id, type: "group", ...meta, children: contents } : null;
6475
+ return { id, type: "group", ...meta, mask, ...effects?.length ? { effects } : {}, children: [source, ...contents] };
6476
+ };
6477
+ const visitOne = (child, style, matrix) => {
6478
+ const out = [];
6479
+ const m = multiplyMatrices(parseSvgTransform(child.attrs.transform), matrix);
6480
+ const s = readStyle(child, style);
6481
+ const opacity = child.attrs.opacity !== void 0 ? parseFloat(child.attrs.opacity) : void 0;
6482
+ const hidden = child.attrs.display === "none" || child.attrs.visibility === "hidden";
6483
+ const blendMode = vectorBlendModeFromCss(s.mixBlendMode);
6484
+ const filters = filtersOf(s.filter);
6485
+ const meta = {
6486
+ ...child.attrs["inkscape:label"] || child.attrs["data-name"] ? { name: child.attrs["inkscape:label"] ?? child.attrs["data-name"] } : {},
6487
+ ...opacity !== void 0 && Number.isFinite(opacity) && opacity < 1 ? { opacity } : {},
6488
+ ...hidden ? { visible: false } : {},
6489
+ ...blendMode && blendMode !== "normal" ? { blendMode } : {},
6490
+ ...filters.length ? { filters } : {}
6491
+ };
6492
+ if (child.attrs["data-vector-blend-step"] !== void 0) return out;
6493
+ const operation = child.attrs["data-vector-boolean-operation"];
6494
+ if (BOOLEAN_OPERATIONS.includes(operation)) meta.booleanOperation = operation;
6495
+ if (child.name === "g" && child.attrs["data-vector-mask"] !== void 0) {
6496
+ const node2 = maskGroup(child, s, m, meta);
6497
+ if (node2) out.push(node2);
6498
+ return out;
6499
+ }
6500
+ if (child.name === "g" && child.attrs["data-vector-live"] !== void 0) {
6501
+ const node2 = liveNode(child, s, m, meta);
6502
+ if (node2) out.push(node2);
6503
+ return out;
6504
+ }
6505
+ if (child.name === "g" && child.attrs["data-vector-path"] !== void 0 && child.attrs["data-vector-fills"] !== void 0) {
6506
+ const node2 = stackedPath(child, s, m, meta);
6507
+ if (node2) out.push(node2);
6508
+ return out;
6509
+ }
6510
+ if (child.name === "g" && child.attrs["data-vector-path"] !== void 0) {
6511
+ const node2 = mergedPath(child, s, m, meta);
6512
+ if (node2) out.push(node2);
6513
+ return out;
6514
+ }
6515
+ if (child.name === "g" || child.name === "svg" || child.name === "a" || child.name === "switch") {
6516
+ const children = visit(child, s, m);
6517
+ if (!children.length) return out;
6518
+ const group = { id: idFor(child), type: "group", ...meta, children };
6519
+ const blend = child.name === "g" ? blendFromAttribute(child.attrs["data-vector-blend"], m) : null;
6520
+ if (blend) group.blend = blend;
6521
+ out.push(group);
6522
+ return out;
6523
+ }
6524
+ const subpaths = shapeOf(child);
6525
+ if (!subpaths || !subpaths.length) return out;
6526
+ const node = placed(
6527
+ withOpacities(
6528
+ {
6529
+ id: idFor(child),
6530
+ type: "path",
6531
+ ...meta,
6532
+ subpaths,
6533
+ fill: child.name === "line" ? null : paint(s.fill),
6534
+ stroke: paint(s.stroke),
6535
+ ...strokeStyle(s)
6536
+ },
6537
+ s
6538
+ ),
6539
+ m
6540
+ );
6541
+ out.push(node);
6542
+ return out;
6543
+ };
6544
+ rootStyle = readStyle(root, rootStyle);
6545
+ const nodes = visit(root, rootStyle, rootMatrix);
5803
6546
  return { width, height, nodes };
5804
6547
  }
5805
6548
 
@@ -5812,6 +6555,7 @@ export {
5812
6555
  VECTOR_ARROWHEADS,
5813
6556
  VECTOR_BLEND_MODES,
5814
6557
  VECTOR_BLEND_SPACINGS,
6558
+ VECTOR_MASK_TYPES,
5815
6559
  VECTOR_TOOLS,
5816
6560
  VECTOR_TRANSFORM_HANDLES,
5817
6561
  VECTOR_PATHFINDER_OPERATIONS,
@@ -5960,6 +6704,43 @@ export {
5960
6704
  mapVectorPoints,
5961
6705
  mapVectorSubpaths,
5962
6706
  mapBezierPath,
6707
+ splitIntoFaces,
6708
+ knifePath,
6709
+ eraserRegion,
6710
+ eraseFromFill,
6711
+ eraseFromOutline,
6712
+ polylineBounds,
6713
+ simplifyPolyline,
6714
+ VECTOR_FILTER_DEFAULTS,
6715
+ VECTOR_FILTER_LABELS,
6716
+ VECTOR_FILTER_PARAM_SPECS,
6717
+ isVectorFilterType,
6718
+ normalizeVectorFilterParams,
6719
+ vectorFilterParamKeys,
6720
+ createVectorFilter,
6721
+ sanitizeVectorFilters,
6722
+ activeVectorFilters,
6723
+ vectorFilterReach,
6724
+ vectorFilterSvg,
6725
+ vectorFilterMarkup,
6726
+ VECTOR_FILTER_MENU,
6727
+ isVectorMaskType,
6728
+ sanitizeVectorMask,
6729
+ isVectorMaskGroup,
6730
+ isVectorMaskActive,
6731
+ vectorMaskSource,
6732
+ vectorMaskContents,
6733
+ vectorMaskOwner,
6734
+ vectorMaskSummary,
6735
+ vectorMaskRegionShapes,
6736
+ vectorMaskRegion,
6737
+ vectorMaskRegionPath,
6738
+ vectorMaskContains,
6739
+ vectorMaskedShapes,
6740
+ vectorMaskedGeometry,
6741
+ vectorMaskedLocalBounds,
6742
+ vectorMaskContentBounds,
6743
+ vectorMaskRenderData,
5963
6744
  vectorMeshFromOutline,
5964
6745
  vectorMeshOutline,
5965
6746
  VECTOR_EFFECT_DEFAULTS,
@@ -5978,6 +6759,7 @@ export {
5978
6759
  clearVectorLiveBooleanCache,
5979
6760
  foldVectorBooleanOperands,
5980
6761
  applyVectorEffects,
6762
+ vectorChildAppearanceShapes,
5981
6763
  vectorEffectInputPaths,
5982
6764
  vectorEffectInputBounds,
5983
6765
  vectorNodeEnvelope,
@@ -6050,19 +6832,6 @@ export {
6050
6832
  createVectorMorph,
6051
6833
  interpolateVectorPaths,
6052
6834
  evaluateVectorPathKeyframes,
6053
- VECTOR_FILTER_DEFAULTS,
6054
- VECTOR_FILTER_LABELS,
6055
- VECTOR_FILTER_PARAM_SPECS,
6056
- isVectorFilterType,
6057
- normalizeVectorFilterParams,
6058
- vectorFilterParamKeys,
6059
- createVectorFilter,
6060
- sanitizeVectorFilters,
6061
- activeVectorFilters,
6062
- vectorFilterReach,
6063
- vectorFilterSvg,
6064
- vectorFilterMarkup,
6065
- VECTOR_FILTER_MENU,
6066
6835
  vectorStrokeGradientDefs,
6067
6836
  vectorCornersAttribute,
6068
6837
  applyVectorCornersAttribute,
@@ -6077,4 +6846,4 @@ export {
6077
6846
  parseSvgTransform,
6078
6847
  parseVectorSvg
6079
6848
  };
6080
- //# sourceMappingURL=chunk-63EFQCT5.js.map
6849
+ //# sourceMappingURL=chunk-HUQ7EHVI.js.map