@pixerate/editor 0.33.0 → 0.35.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.
@@ -2,7 +2,6 @@ import {
2
2
  CANVAS_GRADIENT_PRESETS,
3
3
  PathOpsError,
4
4
  SHAPE_KINDS,
5
- add,
6
5
  angleToSvgLinearCoords,
7
6
  arcToCubics,
8
7
  bezierPathArea,
@@ -15,20 +14,18 @@ import {
15
14
  contourSignedArea,
16
15
  contoursToPath,
17
16
  countPathSegments,
18
- cross,
19
17
  cubicPointAt,
20
18
  cubicsToPathData,
21
- dist,
19
+ dashContours,
22
20
  distanceToBezierPath,
23
21
  dividePaths,
24
22
  dividePolygons,
25
- dot,
26
23
  excludePaths,
27
24
  fitCubicRun,
25
+ fitCubicWithTangents,
28
26
  fitCubics,
29
27
  fitFreehandPath,
30
28
  fitPathData,
31
- flattenChain,
32
29
  flattenSvgPath,
33
30
  formatNumber,
34
31
  getShapeRenderData,
@@ -41,12 +38,12 @@ import {
41
38
  isStraightCubic,
42
39
  multiPolygonArea,
43
40
  multiPolygonBounds,
44
- nestedRingsToPath,
45
41
  normalizeBezierPath,
42
+ offsetPath,
43
+ outlineStroke,
46
44
  parseCssGradient,
47
45
  parseSvgPath,
48
46
  pathBoolean,
49
- pathBooleanRings,
50
47
  pathToContours,
51
48
  pointInBezierPath,
52
49
  pointInMultiPolygon,
@@ -56,12 +53,11 @@ import {
56
53
  polygonToSvgPath,
57
54
  resolveCanvasGradient,
58
55
  ringSignedArea,
59
- samePoint,
60
- scale,
61
56
  segmentBounds,
62
57
  segmentCurvature,
63
58
  segmentDerivative,
64
59
  segmentExtrema,
60
+ segmentIntersections,
65
61
  segmentLength,
66
62
  segmentNearest,
67
63
  segmentParamAtLength,
@@ -70,7 +66,7 @@ import {
70
66
  shapeToPolygon,
71
67
  simplifyRing,
72
68
  splitSegment,
73
- sub,
69
+ strokeOutlineRings,
74
70
  subSegment,
75
71
  subtractPaths,
76
72
  subtractPolygons,
@@ -81,7 +77,7 @@ import {
81
77
  unitePaths,
82
78
  windingNumber,
83
79
  xorPolygons
84
- } from "../chunk-GQTXV2EQ.js";
80
+ } from "../chunk-EGPIVUEF.js";
85
81
  import {
86
82
  DEFAULT_NODE_HEIGHT,
87
83
  DEFAULT_NODE_WIDTH,
@@ -183,11 +179,11 @@ function createFanOutTrajectory(options = {}) {
183
179
  const oneMinusT = 1 - t;
184
180
  const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;
185
181
  const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;
186
- const scale2 = startScale + (endScale - startScale) * progress;
182
+ const scale = startScale + (endScale - startScale) * progress;
187
183
  const opacity = startOpacity + (endOpacity - startOpacity) * progress;
188
184
  return {
189
185
  position: { x, y },
190
- scale: scale2,
186
+ scale,
191
187
  opacity
192
188
  };
193
189
  };
@@ -1214,7 +1210,7 @@ function spacingCandidate(rects, moving, horizontal, tolerance) {
1214
1210
  return best2;
1215
1211
  };
1216
1212
  const crossMid = (a, b) => (Math.max(crossStart(a), crossStart(b)) + Math.min(crossEnd(a), crossEnd(b))) / 2;
1217
- const segment = (from, to, cross2) => horizontal ? { orientation: "horizontal", kind: "spacing", x1: from, y1: cross2, x2: to, y2: cross2 } : { orientation: "vertical", kind: "spacing", x1: cross2, y1: from, x2: cross2, y2: to };
1213
+ const segment = (from, to, cross) => horizontal ? { orientation: "horizontal", kind: "spacing", x1: from, y1: cross, x2: to, y2: cross } : { orientation: "vertical", kind: "spacing", x1: cross, y1: from, x2: cross, y2: to };
1218
1214
  const candidates = [];
1219
1215
  if (before && after) {
1220
1216
  const b = before;
@@ -1726,447 +1722,6 @@ function fitViewportToNodes(nodes, size, ids, options = {}) {
1726
1722
  return bounds ? fitViewportToRect(bounds, size, options) : null;
1727
1723
  }
1728
1724
 
1729
- // src/canvas/pathops/stroke.ts
1730
- var DEFAULT_TOLERANCE = 0.01;
1731
- var normal = (t) => [-t[1], t[0]];
1732
- function raise(seg) {
1733
- if (seg.length !== 3) return [...seg];
1734
- const [p0, p1, p2] = seg;
1735
- 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];
1736
- }
1737
- function isDegenerate(seg, tol) {
1738
- return seg.every((p) => dist(p, seg[0]) <= tol);
1739
- }
1740
- function offsetSegment(seg, d, tol, out, depth = 0) {
1741
- if (seg.length === 2) {
1742
- const n = normal(segmentTangent(seg, 0));
1743
- out.push([add(seg[0], scale(n, d)), add(seg[1], scale(n, d))]);
1744
- return;
1745
- }
1746
- const c = raise(seg);
1747
- const t0 = segmentTangent(c, 0);
1748
- const t1 = segmentTangent(c, 1);
1749
- const q0 = add(c[0], scale(normal(t0), d));
1750
- const q3 = add(c[3], scale(normal(t1), d));
1751
- const k0 = segmentCurvature(c, 0);
1752
- const k1 = segmentCurvature(c, 1);
1753
- const f0 = Number.isFinite(k0) ? 1 - d * k0 : 1;
1754
- const f1 = Number.isFinite(k1) ? 1 - d * k1 : 1;
1755
- const q1 = add(q0, scale(sub(c[1], c[0]), f0));
1756
- const q2 = add(q3, scale(sub(c[2], c[3]), f1));
1757
- const approx = [q0, q1, q2, q3];
1758
- if (depth < 10) {
1759
- let err = 0;
1760
- for (const t of [0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875]) {
1761
- const target = add(segmentPoint(c, t), scale(normal(segmentTangent(c, t)), d));
1762
- err = Math.max(err, segmentNearest(approx, target).distance);
1763
- if (err > tol) break;
1764
- }
1765
- if (err <= tol) {
1766
- const m = segmentPoint(approx, 0.5);
1767
- const back = segmentNearest(c, m).distance;
1768
- err = Math.max(err, Math.abs(back - Math.abs(d)));
1769
- }
1770
- if (err > tol) {
1771
- const [a, b] = splitSegment(c, 0.5);
1772
- offsetSegment(a, d, tol, out, depth + 1);
1773
- offsetSegment(b, d, tol, out, depth + 1);
1774
- return;
1775
- }
1776
- }
1777
- out.push(approx);
1778
- }
1779
- function pushArc(out, center, radius, start, sweep, from, to) {
1780
- const n = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 2) - 1e-9));
1781
- const step = sweep / n;
1782
- const k = 4 / 3 * Math.tan(step / 4);
1783
- let prev = from;
1784
- for (let i = 0; i < n; i++) {
1785
- const a0 = start + step * i;
1786
- const a1 = a0 + step;
1787
- const c0 = Math.cos(a0);
1788
- const s0 = Math.sin(a0);
1789
- const c1 = Math.cos(a1);
1790
- const s1 = Math.sin(a1);
1791
- const end = i === n - 1 ? to : [center[0] + radius * c1, center[1] + radius * s1];
1792
- out.push([
1793
- prev,
1794
- [center[0] + radius * (c0 - k * s0), center[1] + radius * (s0 + k * c0)],
1795
- [center[0] + radius * (c1 + k * s1), center[1] + radius * (s1 - k * c1)],
1796
- end
1797
- ]);
1798
- prev = end;
1799
- }
1800
- }
1801
- function lineTo(out, from, to) {
1802
- if (!samePoint(from, to)) out.push([from, to]);
1803
- }
1804
- function addJoin(out, p, tin, tout, d, style) {
1805
- const from = add(p, scale(normal(tin), d));
1806
- const to = add(p, scale(normal(tout), d));
1807
- const turn = cross(tin, tout);
1808
- const cos = dot(tin, tout);
1809
- if (Math.abs(turn) < 1e-9 && cos > 0) {
1810
- lineTo(out, from, to);
1811
- return;
1812
- }
1813
- const reversal = Math.abs(turn) < 1e-9 && cos < 0;
1814
- const inner = !reversal && turn > 0 === d > 0;
1815
- if (inner) {
1816
- lineTo(out, from, p);
1817
- lineTo(out, p, to);
1818
- return;
1819
- }
1820
- const theta = reversal ? d > 0 ? -Math.PI : Math.PI : Math.atan2(turn, cos);
1821
- if (style.join === "round") {
1822
- const v = sub(from, p);
1823
- pushArc(out, p, Math.abs(d), Math.atan2(v[1], v[0]), theta, from, to);
1824
- return;
1825
- }
1826
- if (style.join === "miter" && !reversal) {
1827
- const phi = Math.PI - Math.abs(theta);
1828
- const ratio = 1 / Math.max(Math.sin(phi / 2), 1e-12);
1829
- if (ratio <= style.miterLimit) {
1830
- const nin = normal(tin);
1831
- const nout = normal(tout);
1832
- const s = d / (1 + dot(nin, nout));
1833
- const miter = add(p, scale(add(nin, nout), s));
1834
- lineTo(out, from, miter);
1835
- lineTo(out, miter, to);
1836
- return;
1837
- }
1838
- }
1839
- lineTo(out, from, to);
1840
- }
1841
- function offsetChain(segs, d, closed, style, tol) {
1842
- const out = [];
1843
- const pen = () => out.length > 0 ? out[out.length - 1][out[out.length - 1].length - 1] : null;
1844
- for (let i = 0; i < segs.length; i++) {
1845
- if (i > 0) addJoin(out, segs[i][0], segmentTangent(segs[i - 1], 1), segmentTangent(segs[i], 0), d, style);
1846
- const pieces = [];
1847
- offsetSegment(segs[i], d, tol, pieces);
1848
- for (const piece of pieces) {
1849
- const at = pen();
1850
- if (at && !samePoint(at, piece[0])) lineTo(out, at, piece[0]);
1851
- out.push(piece);
1852
- }
1853
- }
1854
- if (closed && segs.length > 0) {
1855
- const last = segs[segs.length - 1];
1856
- addJoin(out, segs[0][0], segmentTangent(last, 1), segmentTangent(segs[0], 0), d, style);
1857
- const at = pen();
1858
- if (at && out.length > 0 && !samePoint(at, out[0][0])) lineTo(out, at, out[0][0]);
1859
- }
1860
- return out;
1861
- }
1862
- function reverseChain(chain) {
1863
- return chain.map((s) => [...s].reverse()).reverse();
1864
- }
1865
- function addCap(out, p, t, h, cap) {
1866
- const n = normal(t);
1867
- const from = add(p, scale(n, h));
1868
- const to = add(p, scale(n, -h));
1869
- if (cap === "round") {
1870
- pushArc(out, p, h, Math.atan2(n[1], n[0]), -Math.PI, from, to);
1871
- } else if (cap === "square") {
1872
- const a = add(from, scale(t, h));
1873
- const b = add(to, scale(t, h));
1874
- lineTo(out, from, a);
1875
- lineTo(out, a, b);
1876
- lineTo(out, b, to);
1877
- } else {
1878
- lineTo(out, from, to);
1879
- }
1880
- }
1881
- function dotRings(p, t, h, cap) {
1882
- if (cap === "butt") return [];
1883
- const ring = [];
1884
- addCap(ring, p, t, h, cap);
1885
- addCap(ring, p, [-t[0], -t[1]], h, cap);
1886
- return [ring];
1887
- }
1888
- function strokeContour(contour, h, style, tol) {
1889
- const tiny = Math.max(1e-12, tol * 1e-6);
1890
- const all = contour.closed ? closeContour(contour) : contour.segments;
1891
- const segs = all.filter((s) => !isDegenerate(s, tiny));
1892
- if (segs.length === 0) {
1893
- const p = contour.segments[0]?.[0];
1894
- return p ? dotRings(p, [1, 0], h, style.cap) : [];
1895
- }
1896
- if (contour.closed) {
1897
- const left2 = offsetChain(segs, h, true, style, tol);
1898
- const right2 = offsetChain(segs, -h, true, style, tol);
1899
- return [left2, reverseChain(right2)];
1900
- }
1901
- const left = offsetChain(segs, h, false, style, tol);
1902
- const right = reverseChain(offsetChain(segs, -h, false, style, tol));
1903
- const ring = [...left];
1904
- const last = segs[segs.length - 1];
1905
- addCap(ring, last[last.length - 1], segmentTangent(last, 1), h, style.cap);
1906
- const joinTo = (p) => {
1907
- const at = ring[ring.length - 1][ring[ring.length - 1].length - 1];
1908
- lineTo(ring, at, p);
1909
- };
1910
- if (right.length > 0) joinTo(right[0][0]);
1911
- ring.push(...right);
1912
- const first = segs[0];
1913
- const t0 = segmentTangent(first, 0);
1914
- addCap(ring, first[0], [-t0[0], -t0[1]], h, style.cap);
1915
- if (ring.length > 0) joinTo(ring[0][0]);
1916
- return [ring];
1917
- }
1918
- function ringsToPath(rings) {
1919
- return contoursToPath(rings.filter((r) => r.length > 0).map((segments) => ({ segments, closed: true })));
1920
- }
1921
- var MAX_DASHES = 2e5;
1922
- var MIN_ENTRY = 1e-6;
1923
- function dashContours(contours, pattern, offset = 0) {
1924
- if (pattern.length === 0 || pattern.some((v) => !Number.isFinite(v) || v < 0) || !Number.isFinite(offset)) return null;
1925
- if (!pattern.some((v) => v > MIN_ENTRY)) return null;
1926
- const pat = pattern.length % 2 === 1 ? [...pattern, ...pattern] : [...pattern];
1927
- const period = pat.reduce((s, v) => s + v, 0);
1928
- const chains = contours.map((c) => {
1929
- const segs = closeContour(c);
1930
- const lengths = segs.map((s) => segmentLength(s));
1931
- return { segs, lengths, total: lengths.reduce((s, v) => s + v, 0), closed: c.closed };
1932
- });
1933
- const totalLength = chains.reduce((s, c) => s + c.total, 0);
1934
- if (totalLength / period * pat.length > MAX_DASHES) return null;
1935
- let off = (offset % period + period) % period;
1936
- let ix0 = 0;
1937
- while (off > pat[ix0]) {
1938
- off -= pat[ix0];
1939
- ix0 = (ix0 + 1) % pat.length;
1940
- }
1941
- const out = { contours: [], dots: [] };
1942
- for (const chain of chains) {
1943
- if (chain.segs.length === 0 || !(chain.total > 0)) continue;
1944
- let ix = ix0;
1945
- let rem = pat[ix0] - off;
1946
- let pos = 0;
1947
- const dashes = [];
1948
- let guard = 0;
1949
- while (pos < chain.total && guard++ < MAX_DASHES * 2) {
1950
- const step = Math.min(rem, chain.total - pos);
1951
- const on = ix % 2 === 0;
1952
- if (on) dashes.push({ from: pos, to: pos + step });
1953
- pos += step;
1954
- rem -= step;
1955
- if (rem <= 1e-12) {
1956
- ix = (ix + 1) % pat.length;
1957
- rem = pat[ix];
1958
- }
1959
- }
1960
- if (chain.closed && dashes.length > 1) {
1961
- const first = dashes[0];
1962
- const last = dashes[dashes.length - 1];
1963
- if (first.from <= 1e-9 && last.to >= chain.total - 1e-9 && first.to > first.from && last.to > last.from) {
1964
- dashes.pop();
1965
- const tail = trimSegments(chain.segs, last.from, chain.total, chain.lengths);
1966
- const head = trimSegments(chain.segs, 0, first.to, chain.lengths);
1967
- dashes.shift();
1968
- out.contours.push({ segments: [...tail, ...head], closed: false });
1969
- }
1970
- }
1971
- for (const dash of dashes) {
1972
- if (dash.to - dash.from <= 1e-12) {
1973
- const at = locate(chain.segs, chain.lengths, dash.from);
1974
- out.dots.push(at);
1975
- continue;
1976
- }
1977
- const segments = trimSegments(chain.segs, dash.from, dash.to, chain.lengths);
1978
- if (segments.length > 0) out.contours.push({ segments, closed: false });
1979
- }
1980
- }
1981
- return out;
1982
- }
1983
- function locate(segs, lengths, at) {
1984
- let acc = 0;
1985
- for (let i = 0; i < segs.length; i++) {
1986
- if (at <= acc + lengths[i] || i === segs.length - 1) {
1987
- const piece = trimSegments([segs[i]], 0, Math.max(0, at - acc), [lengths[i]]);
1988
- const t = piece.length > 0 ? 1 : 0;
1989
- const point = piece.length > 0 ? piece[piece.length - 1][piece[piece.length - 1].length - 1] : segs[i][0];
1990
- const tangent = segmentTangent(piece.length > 0 ? piece[piece.length - 1] : segs[i], t);
1991
- return { point, tangent };
1992
- }
1993
- acc += lengths[i];
1994
- }
1995
- return { point: segs[0][0], tangent: segmentTangent(segs[0], 0) };
1996
- }
1997
- function deepPoint(rings) {
1998
- let minX = Infinity;
1999
- let minY = Infinity;
2000
- let maxX = -Infinity;
2001
- let maxY = -Infinity;
2002
- for (const p of rings[0].flat()) {
2003
- minX = Math.min(minX, p[0]);
2004
- maxX = Math.max(maxX, p[0]);
2005
- minY = Math.min(minY, p[1]);
2006
- maxY = Math.max(maxY, p[1]);
2007
- }
2008
- const w = maxX - minX;
2009
- const h = maxY - minY;
2010
- if (!(w > 0 && h > 0)) return null;
2011
- const tolerance = Math.max(1e-6, 2e-3 * Math.min(w, h));
2012
- const polys = rings.map((r) => flattenChain(r, tolerance));
2013
- let best = null;
2014
- for (const f of [0.5, 0.3, 0.7, 0.15, 0.85]) {
2015
- const y = minY + h * f;
2016
- const xs = [];
2017
- for (const poly of polys) {
2018
- for (let i = 0; i < poly.length; i++) {
2019
- const a = poly[i];
2020
- const b = poly[(i + 1) % poly.length];
2021
- if (a[1] <= y !== b[1] <= y) xs.push(a[0] + (y - a[1]) / (b[1] - a[1]) * (b[0] - a[0]));
2022
- }
2023
- }
2024
- xs.sort((p, q) => p - q);
2025
- for (let i = 0; i + 1 < xs.length; i += 2) {
2026
- const span = xs[i + 1] - xs[i];
2027
- if (!best || span > best.span) best = { span, p: [(xs[i] + xs[i + 1]) / 2, y] };
2028
- }
2029
- }
2030
- return best?.p ?? null;
2031
- }
2032
- function dropSpurious(rings, spurious) {
2033
- const children = rings.map(() => []);
2034
- rings.forEach((r, i) => {
2035
- if (r.parent >= 0) children[r.parent].push(i);
2036
- });
2037
- const marked = rings.map((r, i) => spurious([r.segments, ...children[i].map((c) => rings[c].segments)], r));
2038
- const hasMarkedAncestor = (i) => {
2039
- for (let k = i; k >= 0; k = rings[k].parent) if (marked[k]) return true;
2040
- return false;
2041
- };
2042
- return nestedRingsToPath(rings, (i) => !hasMarkedAncestor(i));
2043
- }
2044
- function mergeSmoothRuns(path, tolerance) {
2045
- const contours = pathToContours(path).map((contour) => {
2046
- const segs = closeContour(contour, true).map((s) => s.length === 3 ? raise(s) : [...s]);
2047
- const n = segs.length;
2048
- if (n < 2) return { segments: segs, closed: true };
2049
- const smooth = (a, b) => {
2050
- if (a.length !== 4 || b.length !== 4) return false;
2051
- const ta = segmentTangent(a, 1);
2052
- const tb = segmentTangent(b, 0);
2053
- return dot(ta, tb) > 0.9998 && Math.abs(cross(ta, tb)) < 0.02;
2054
- };
2055
- let start = 0;
2056
- for (let k = 0; k < n; k++) {
2057
- if (!smooth(segs[(k + n - 1) % n], segs[k])) {
2058
- start = k;
2059
- break;
2060
- }
2061
- }
2062
- const ordered = [...segs.slice(start), ...segs.slice(0, start)];
2063
- const out = [];
2064
- let i = 0;
2065
- while (i < ordered.length) {
2066
- let current = ordered[i];
2067
- let j = i + 1;
2068
- while (j < ordered.length && smooth(ordered[j - 1], ordered[j])) {
2069
- const run = ordered.slice(i, j + 1);
2070
- const pts = [];
2071
- run.forEach((s, k) => {
2072
- for (let q = k === 0 ? 0 : 1; q <= 12; q++) pts.push(segmentPoint(s, q / 12));
2073
- });
2074
- const fit = fitCubicRun(pts, tolerance, segmentTangent(run[0], 0), segmentTangent(run[run.length - 1], 1));
2075
- if (fit.length !== 1) break;
2076
- const c = fit[0];
2077
- current = [run[0][0], c.p1, c.p2, run[run.length - 1][3]];
2078
- j++;
2079
- }
2080
- out.push(current);
2081
- i = j;
2082
- }
2083
- return { segments: out, closed: true };
2084
- });
2085
- return contoursToPath(contours);
2086
- }
2087
- function joinStyle(join, miterLimit) {
2088
- return {
2089
- join: join ?? "miter",
2090
- miterLimit: miterLimit !== void 0 && Number.isFinite(miterLimit) ? Math.max(1, miterLimit) : 4
2091
- };
2092
- }
2093
- function strokeOutlineRings(input, options) {
2094
- const h = options.width / 2;
2095
- if (!(h > 0) || !Number.isFinite(h)) return [];
2096
- const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : DEFAULT_TOLERANCE;
2097
- const style = { ...joinStyle(options.join, options.miterLimit), cap: options.cap ?? "butt" };
2098
- let contours = pathToContours(input);
2099
- const rings = [];
2100
- if (options.dash && options.dash.length > 0) {
2101
- const dashed = dashContours(contours, options.dash, options.dashOffset ?? 0);
2102
- if (dashed) {
2103
- contours = dashed.contours;
2104
- for (const dot2 of dashed.dots) rings.push(...dotRings(dot2.point, dot2.tangent, h, style.cap));
2105
- }
2106
- }
2107
- for (const contour of contours) rings.push(...strokeContour(contour, h, style, tol));
2108
- return ringsToPath(rings);
2109
- }
2110
- function outlineStroke(input, options) {
2111
- const h = options.width / 2;
2112
- if (!(h > 0) || !Number.isFinite(h)) return [];
2113
- let source;
2114
- try {
2115
- source = contoursToPath(pathToContours(input));
2116
- } catch {
2117
- return [];
2118
- }
2119
- if (source.some((c) => c.type !== "Z" && Object.values(c).some((v) => typeof v === "number" && !Number.isFinite(v)))) return [];
2120
- const raw = strokeOutlineRings(source, options);
2121
- if (raw.length === 0) return [];
2122
- const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : DEFAULT_TOLERANCE;
2123
- const sourceContours = pathToContours(source);
2124
- const operand = { path: raw, fillRule: "nonzero" };
2125
- try {
2126
- const rings = pathBooleanRings("union", [operand], options);
2127
- const cleaned = dropSpurious(rings, (face, ring) => {
2128
- if (ring.area >= 0) return false;
2129
- const p = deepPoint(face);
2130
- return !!p && distanceToBezierPath(sourceContours, p) < h * (1 - 1e-4) - 1e-6;
2131
- });
2132
- return mergeSmoothRuns(cleaned, tol);
2133
- } catch {
2134
- return pathBoolean("union", [operand], options).path;
2135
- }
2136
- }
2137
- function offsetPath(input, distance, options = {}) {
2138
- if (!Number.isFinite(distance)) return [];
2139
- const contours = pathToContours(input);
2140
- if (contours.some((c) => c.segments.some((s) => s.some((p) => !Number.isFinite(p[0]) || !Number.isFinite(p[1]))))) return [];
2141
- const closed = contours.filter((c) => c.closed && c.segments.length > 0);
2142
- const open = contours.filter((c) => !c.closed && c.segments.length > 0);
2143
- const fillRule = options.fillRule ?? "nonzero";
2144
- const fill = { path: contoursToPath(closed), fillRule };
2145
- const d = Math.abs(distance);
2146
- const tol = options.tolerance && options.tolerance > 0 ? options.tolerance : DEFAULT_TOLERANCE;
2147
- if (d === 0) return pathBoolean("union", [fill], options).path;
2148
- const strokeOptions = { width: 2 * d, join: options.join ?? "miter", miterLimit: options.miterLimit, cap: "butt", tolerance: tol };
2149
- const ring = { path: strokeOutlineRings(contoursToPath(closed), strokeOptions), fillRule: "nonzero" };
2150
- const openRing = {
2151
- path: distance > 0 ? strokeOutlineRings(contoursToPath(open), strokeOptions) : [],
2152
- fillRule: "nonzero"
2153
- };
2154
- const grow = distance > 0;
2155
- const operands = [fill, ring, openRing];
2156
- try {
2157
- const rings = pathBooleanRings(grow ? (m) => m[0] || m[1] || m[2] : (m) => m[0] && !m[1], operands, options);
2158
- const sourceContours = contours;
2159
- const cleaned = dropSpurious(rings, (face, r) => {
2160
- if (grow ? r.area >= 0 : r.area <= 0) return false;
2161
- const p = deepPoint(face);
2162
- return !!p && distanceToBezierPath(sourceContours, p) < d * (1 - 1e-4) - 1e-6;
2163
- });
2164
- return mergeSmoothRuns(cleaned, tol);
2165
- } catch {
2166
- return grow ? pathBoolean("union", operands, options).path : pathBoolean("difference", [fill, ring], options).path;
2167
- }
2168
- }
2169
-
2170
1725
  // src/canvas/shapeNodes.ts
2171
1726
  var SHAPE_DATA_KEY = "shape";
2172
1727
  var SHAPE_NODE_TYPE = "shape";
@@ -2655,8 +2210,8 @@ function snapResize(rect, handle, targets, options = {}) {
2655
2210
  const ratio = rect.height > 0 ? rect.width / rect.height : 0;
2656
2211
  if (options.keepAspectRatio && ratio > 0 && (hitX || hitY)) {
2657
2212
  if (axes.x !== 0 && axes.y !== 0) {
2658
- const dist2 = (h, horizontal) => h ? Math.abs(h.value - (horizontal ? axes.x < 0 ? x0 : x1 : axes.y < 0 ? y0 : y1)) : Infinity;
2659
- const useX = dist2(hitX, true) <= dist2(hitY, false);
2213
+ const dist = (h, horizontal) => h ? Math.abs(h.value - (horizontal ? axes.x < 0 ? x0 : x1 : axes.y < 0 ? y0 : y1)) : Infinity;
2214
+ const useX = dist(hitX, true) <= dist(hitY, false);
2660
2215
  if (useX && hitX) {
2661
2216
  setEdge(true, hitX.value);
2662
2217
  const height = (nx1 - nx0) / ratio;
@@ -3021,7 +2576,7 @@ function layoutFrameInner(s, frame) {
3021
2576
  const b = s.boxes.get(id);
3022
2577
  return horizontal ? [b.w, b.h] : [b.h, b.w];
3023
2578
  };
3024
- const resize = (it, main, cross2) => resizeChild(s, it.id, horizontal ? main : cross2, horizontal ? cross2 : main);
2579
+ const resize = (it, main, cross) => resizeChild(s, it.id, horizontal ? main : cross, horizontal ? cross : main);
3025
2580
  if (!wraps) {
3026
2581
  for (const it of items) {
3027
2582
  if (!it.fillCross || it.fillMain) continue;
@@ -3035,13 +2590,13 @@ function layoutFrameInner(s, frame) {
3035
2590
  let used = 0;
3036
2591
  items.forEach((it, i) => {
3037
2592
  const sz = it.fillMain ? it.minMain ?? 0 : it.main;
3038
- const add2 = cur.length === 0 ? sz : Math.max(gap, 0) + sz;
3039
- if (cur.length > 0 && used + add2 > innerMainFixed + 1e-6) {
2593
+ const add = cur.length === 0 ? sz : Math.max(gap, 0) + sz;
2594
+ if (cur.length > 0 && used + add > innerMainFixed + 1e-6) {
3040
2595
  lines.push(cur);
3041
2596
  cur = [];
3042
2597
  used = sz;
3043
2598
  } else {
3044
- used += add2;
2599
+ used += add;
3045
2600
  }
3046
2601
  cur.push(i);
3047
2602
  });
@@ -3057,8 +2612,8 @@ function layoutFrameInner(s, frame) {
3057
2612
  }
3058
2613
  for (const it of items) {
3059
2614
  if (!it.fillMain) continue;
3060
- const cross2 = it.fillCross && !wraps ? clampLayoutSize(innerCrossFixed, it.minCross, it.maxCross) : it.cross;
3061
- resize(it, it.main, cross2);
2615
+ const cross = it.fillCross && !wraps ? clampLayoutSize(innerCrossFixed, it.minCross, it.maxCross) : it.cross;
2616
+ resize(it, it.main, cross);
3062
2617
  it.cross = sizeOf2(it.id)[1];
3063
2618
  }
3064
2619
  if (baseline) {
@@ -3072,8 +2627,8 @@ function layoutFrameInner(s, frame) {
3072
2627
  const inLine = line2.map((i) => items[i]);
3073
2628
  const mainUsed = inLine.reduce((sum, it) => sum + it.main, 0) + gaps;
3074
2629
  const allFill = inLine.every((it) => it.fillCross);
3075
- const cross2 = inLine.filter((it) => allFill || !it.fillCross).reduce((m, it) => Math.max(m, it.baseOff + it.cross), 0);
3076
- extents.push([mainUsed, cross2]);
2630
+ const cross = inLine.filter((it) => allFill || !it.fillCross).reduce((m, it) => Math.max(m, it.baseOff + it.cross), 0);
2631
+ extents.push([mainUsed, cross]);
3077
2632
  }
3078
2633
  const contentMain = extents.reduce((m, l) => Math.max(m, l[0]), 0);
3079
2634
  let crossGap = al.rowGap !== void 0 ? finite(al.rowGap) : gap;
@@ -3555,13 +3110,13 @@ function getAutoLayoutInsertTarget(nodes, frameId, point, options = {}) {
3555
3110
  const horizontal = al.mode !== "vertical";
3556
3111
  const main = (b) => horizontal ? b.x : b.y;
3557
3112
  const mainSize = (b) => horizontal ? b.w : b.h;
3558
- const cross2 = (b) => horizontal ? b.y : b.x;
3113
+ const cross = (b) => horizontal ? b.y : b.x;
3559
3114
  const crossSize = (b) => horizontal ? b.h : b.w;
3560
3115
  const p = { x: px2, y: py };
3561
3116
  const multiLine = al.mode === "grid" || !!al.wrap;
3562
3117
  let index = 0;
3563
3118
  for (const b of siblings) {
3564
- const before = multiLine ? cross2(b) + crossSize(b) <= cross2(p) || cross2(b) <= cross2(p) && main(b) + mainSize(b) / 2 < main(p) : main(b) + mainSize(b) / 2 < main(p);
3119
+ const before = multiLine ? cross(b) + crossSize(b) <= cross(p) || cross(b) <= cross(p) && main(b) + mainSize(b) / 2 < main(p) : main(b) + mainSize(b) / 2 < main(p);
3565
3120
  if (before) index += 1;
3566
3121
  }
3567
3122
  const [pt, pr, pb, pl] = resolvePadding(al.padding);
@@ -3570,13 +3125,13 @@ function getAutoLayoutInsertTarget(nodes, frameId, point, options = {}) {
3570
3125
  const f = s.boxes.get(frameId);
3571
3126
  let guide;
3572
3127
  if (horizontal) {
3573
- const x = prev && next && Math.abs(cross2(prev) - cross2(next)) < 1e-6 ? (prev.x + prev.w + next.x) / 2 : prev ? prev.x + prev.w : next ? next.x : pl;
3128
+ const x = prev && next && Math.abs(cross(prev) - cross(next)) < 1e-6 ? (prev.x + prev.w + next.x) / 2 : prev ? prev.x + prev.w : next ? next.x : pl;
3574
3129
  const ref = next ?? prev;
3575
3130
  const y1 = ref ? ref.y : pt;
3576
3131
  const y2 = ref ? ref.y + ref.h : f.h - pb;
3577
3132
  guide = { orientation: "vertical", kind: "edge", x1: origin.x + x, y1: origin.y + y1, x2: origin.x + x, y2: origin.y + y2 };
3578
3133
  } else {
3579
- const y = prev && next && Math.abs(cross2(prev) - cross2(next)) < 1e-6 ? (prev.y + prev.h + next.y) / 2 : prev ? prev.y + prev.h : next ? next.y : pt;
3134
+ const y = prev && next && Math.abs(cross(prev) - cross(next)) < 1e-6 ? (prev.y + prev.h + next.y) / 2 : prev ? prev.y + prev.h : next ? next.y : pt;
3580
3135
  const ref = next ?? prev;
3581
3136
  const x1 = ref ? ref.x : pl;
3582
3137
  const x2 = ref ? ref.x + ref.w : f.w - pr;
@@ -6448,8 +6003,8 @@ function sizeOf(node) {
6448
6003
  return { width: node.width, height: node.height, styleWidth: s.width, styleHeight: s.height };
6449
6004
  }
6450
6005
  var splitGroup = (group) => {
6451
- const dot2 = group.indexOf(".");
6452
- return dot2 < 0 ? [group, ""] : [group.slice(0, dot2), group.slice(dot2 + 1)];
6006
+ const dot = group.indexOf(".");
6007
+ return dot < 0 ? [group, ""] : [group.slice(0, dot), group.slice(dot + 1)];
6453
6008
  };
6454
6009
  function readOverrideGroup(node, group) {
6455
6010
  const data = dataOf(node);
@@ -6562,7 +6117,7 @@ function listGroups(node, out) {
6562
6117
  if (LINK_KEYS.has(k)) continue;
6563
6118
  const prefix = NESTED_KEYS[k];
6564
6119
  if (prefix) {
6565
- if (isRecord(v)) for (const sub2 of Object.keys(v)) out.add(`${prefix}.${sub2}`);
6120
+ if (isRecord(v)) for (const sub of Object.keys(v)) out.add(`${prefix}.${sub}`);
6566
6121
  continue;
6567
6122
  }
6568
6123
  out.add(`data.${k}`);
@@ -7875,11 +7430,11 @@ function restoreCanvasMainComponent(nodes, id, options = {}) {
7875
7430
  if (g.get(inst.componentId)) return fail(nodes, "invalid", "The main component still exists.");
7876
7431
  if (enclosingInstanceIn(g, id) !== void 0) return fail(nodes, "nested-instance", "Select the outer instance.");
7877
7432
  const compId = inst.componentId;
7878
- const sub2 = g.subtree(id);
7433
+ const sub = g.subtree(id);
7879
7434
  const map = /* @__PURE__ */ new Map([[id, compId]]);
7880
7435
  const taken = new Set(nodes.map((n) => n.id));
7881
7436
  taken.add(compId);
7882
- for (const d of sub2) {
7437
+ for (const d of sub) {
7883
7438
  if (d === id) continue;
7884
7439
  const src = enclosingInstanceIn(g, d) === id ? getComponentSourceId(g.get(d)) : void 0;
7885
7440
  if (src && !taken.has(src)) {
@@ -7889,7 +7444,7 @@ function restoreCanvasMainComponent(nodes, id, options = {}) {
7889
7444
  }
7890
7445
  const nested = [];
7891
7446
  const replaced = /* @__PURE__ */ new Map();
7892
- for (const d of sub2) {
7447
+ for (const d of sub) {
7893
7448
  let n = g.get(d);
7894
7449
  const nid = map.get(d) ?? d;
7895
7450
  const parentId = n.parentId !== void 0 ? map.get(n.parentId) ?? n.parentId : void 0;
@@ -8056,15 +7611,15 @@ function addCanvasVariant(nodes, setId, options = {}) {
8056
7611
  const taken = new Set(g.byId.keys());
8057
7612
  taken.add(id);
8058
7613
  const map = /* @__PURE__ */ new Map([[fromId, id]]);
8059
- const sub2 = g.subtree(fromId);
8060
- for (const d of sub2) {
7614
+ const sub = g.subtree(fromId);
7615
+ for (const d of sub) {
8061
7616
  if (d === fromId) continue;
8062
7617
  const nid = uniqueId(`${id}-${d}`, taken);
8063
7618
  taken.add(nid);
8064
7619
  map.set(d, nid);
8065
7620
  }
8066
7621
  const order = variantOrder([...edit.variants.map((v) => v.id), id], edit.values);
8067
- const subSet = new Set(sub2);
7622
+ const subSet = new Set(sub);
8068
7623
  const copies = nodes.filter((n) => subSet.has(n.id)).map((n) => {
8069
7624
  const next = { ...n, id: map.get(n.id) };
8070
7625
  if (n.parentId !== void 0 && n.id !== fromId) next.parentId = map.get(n.parentId) ?? n.parentId;
@@ -8623,8 +8178,8 @@ function findStrokesAtPoint(point, nodes, radius = 12) {
8623
8178
  }
8624
8179
  const localPoint = [point[0] - rect.x, point[1] - rect.y];
8625
8180
  try {
8626
- const dist2 = distanceToBezierPath(shape.d, localPoint);
8627
- if (dist2 <= effectiveRadius) {
8181
+ const dist = distanceToBezierPath(shape.d, localPoint);
8182
+ if (dist <= effectiveRadius) {
8628
8183
  matches2.push(node);
8629
8184
  }
8630
8185
  } catch {
@@ -8929,6 +8484,7 @@ export {
8929
8484
  findStrokesAtPoint,
8930
8485
  findVariant,
8931
8486
  fitCubicRun,
8487
+ fitCubicWithTangents,
8932
8488
  fitCubics,
8933
8489
  fitFreehandPath,
8934
8490
  fitPathData,
@@ -9111,6 +8667,7 @@ export {
9111
8667
  segmentCurvature,
9112
8668
  segmentDerivative,
9113
8669
  segmentExtrema,
8670
+ segmentIntersections,
9114
8671
  segmentLength,
9115
8672
  segmentNearest,
9116
8673
  segmentParamAtLength,