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