@pixerate/editor 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -13
- package/dist/agent/index.cjs +8 -8
- package/dist/agent/index.d.cts +2 -2
- package/dist/agent/index.d.ts +2 -2
- package/dist/agent/index.js +2 -2
- package/dist/canvas/index.cjs +1269 -13
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +432 -6
- package/dist/canvas/index.d.ts +432 -6
- package/dist/canvas/index.js +1264 -8
- package/dist/canvas/index.js.map +1 -1
- package/dist/{chunk-U6NDQFOK.cjs → chunk-6M3BCTBD.cjs} +427 -138
- package/dist/chunk-6M3BCTBD.cjs.map +1 -0
- package/dist/{chunk-TVRC4KVT.cjs → chunk-AA4ZEVCC.cjs} +845 -90
- package/dist/chunk-AA4ZEVCC.cjs.map +1 -0
- package/dist/{chunk-LA7PEGU3.cjs → chunk-CL37JGFW.cjs} +4 -4
- package/dist/{chunk-LA7PEGU3.cjs.map → chunk-CL37JGFW.cjs.map} +1 -1
- package/dist/{chunk-A7DKIA72.js → chunk-DENSCKFC.js} +642 -69
- package/dist/chunk-DENSCKFC.js.map +1 -0
- package/dist/{chunk-4QPLEUL2.cjs → chunk-GVSMRPHB.cjs} +1046 -54
- package/dist/chunk-GVSMRPHB.cjs.map +1 -0
- package/dist/{chunk-KYRZDWM4.js → chunk-JJDLPEWL.js} +2 -2
- package/dist/{chunk-3TYMNULT.js → chunk-MJZBNWG7.js} +807 -52
- package/dist/chunk-MJZBNWG7.js.map +1 -0
- package/dist/{chunk-CYFJCKC4.js → chunk-QGHGJ56O.js} +997 -5
- package/dist/chunk-QGHGJ56O.js.map +1 -0
- package/dist/{chunk-GVW4P7BG.js → chunk-RU52I7QO.js} +305 -16
- package/dist/chunk-RU52I7QO.js.map +1 -0
- package/dist/{chunk-JYRIOFEN.cjs → chunk-SAZRENEZ.cjs} +647 -74
- package/dist/chunk-SAZRENEZ.cjs.map +1 -0
- package/dist/{controller-OUdxPY-X.d.ts → controller-B7GIybc4.d.cts} +139 -7
- package/dist/{controller-OUdxPY-X.d.cts → controller-B7GIybc4.d.ts} +139 -7
- package/dist/extensions/index.cjs +14 -4
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +3 -3
- package/dist/extensions/index.d.ts +3 -3
- package/dist/extensions/index.js +13 -3
- package/dist/{index-k3mFbPx_.d.cts → index-CCr_z9kF.d.cts} +3 -3
- package/dist/{index-Ng1pG7JE.d.ts → index-Ck6ajwXh.d.ts} +3 -3
- package/dist/index.cjs +250 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +294 -8
- package/dist/index.d.ts +294 -8
- package/dist/index.js +241 -7
- package/dist/index.js.map +1 -1
- package/dist/{registry-iAHbqkYt.d.cts → registry-GJI4gToU.d.cts} +15 -4
- package/dist/{registry-iAHbqkYt.d.ts → registry-GJI4gToU.d.ts} +15 -4
- package/dist/serializers/index.cjs +3 -3
- package/dist/serializers/index.d.cts +1 -1
- package/dist/serializers/index.d.ts +1 -1
- package/dist/serializers/index.js +2 -2
- package/dist/spreadsheet/index.cjs +7 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +33 -4
- package/dist/spreadsheet/index.d.ts +33 -4
- package/dist/spreadsheet/index.js +6 -2
- package/dist/{track-changes-G8JcWB8b.d.cts → track-changes-BSF58RK0.d.cts} +92 -9
- package/dist/{track-changes-G8JcWB8b.d.ts → track-changes-BSF58RK0.d.ts} +92 -9
- package/dist/{types-DMe0vFB1.d.cts → types-CJ4U-HHI.d.cts} +1 -1
- package/dist/{types-DMe0vFB1.d.ts → types-CJ4U-HHI.d.ts} +1 -1
- package/package.json +11 -4
- package/dist/chunk-3TYMNULT.js.map +0 -1
- package/dist/chunk-4QPLEUL2.cjs.map +0 -1
- package/dist/chunk-A7DKIA72.js.map +0 -1
- package/dist/chunk-CYFJCKC4.js.map +0 -1
- package/dist/chunk-GVW4P7BG.js.map +0 -1
- package/dist/chunk-JYRIOFEN.cjs.map +0 -1
- package/dist/chunk-TVRC4KVT.cjs.map +0 -1
- package/dist/chunk-U6NDQFOK.cjs.map +0 -1
- /package/dist/{chunk-KYRZDWM4.js.map → chunk-JJDLPEWL.js.map} +0 -0
package/dist/canvas/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
@@ -1705,16 +1705,1188 @@ function matchArrangeShortcut(event, bindings = DEFAULT_ARRANGE_SHORTCUTS) {
|
|
|
1705
1705
|
return null;
|
|
1706
1706
|
}
|
|
1707
1707
|
|
|
1708
|
+
// src/canvas/polygonBoolean.ts
|
|
1709
|
+
function ringSignedArea(ring) {
|
|
1710
|
+
let sum = 0;
|
|
1711
|
+
const n = ring.length;
|
|
1712
|
+
for (let i = 0; i < n; i++) {
|
|
1713
|
+
const [x1, y1] = ring[i];
|
|
1714
|
+
const [x2, y2] = ring[(i + 1) % n];
|
|
1715
|
+
sum += x1 * y2 - x2 * y1;
|
|
1716
|
+
}
|
|
1717
|
+
return sum / 2;
|
|
1718
|
+
}
|
|
1719
|
+
function polygonArea(polygon) {
|
|
1720
|
+
if (polygon.length === 0) return 0;
|
|
1721
|
+
let area = Math.abs(ringSignedArea(polygon[0]));
|
|
1722
|
+
for (let i = 1; i < polygon.length; i++) area -= Math.abs(ringSignedArea(polygon[i]));
|
|
1723
|
+
return area;
|
|
1724
|
+
}
|
|
1725
|
+
function multiPolygonArea(multi) {
|
|
1726
|
+
let total = 0;
|
|
1727
|
+
for (const polygon of multi) total += polygonArea(polygon);
|
|
1728
|
+
return total;
|
|
1729
|
+
}
|
|
1730
|
+
function multiPolygonBounds(input) {
|
|
1731
|
+
let minX = Infinity;
|
|
1732
|
+
let minY = Infinity;
|
|
1733
|
+
let maxX = -Infinity;
|
|
1734
|
+
let maxY = -Infinity;
|
|
1735
|
+
const visit = (ring) => {
|
|
1736
|
+
for (const [x, y] of ring) {
|
|
1737
|
+
if (x < minX) minX = x;
|
|
1738
|
+
if (y < minY) minY = y;
|
|
1739
|
+
if (x > maxX) maxX = x;
|
|
1740
|
+
if (y > maxY) maxY = y;
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
for (const item of input) {
|
|
1744
|
+
if (item.length > 0 && Array.isArray(item[0]) && Array.isArray(item[0][0])) {
|
|
1745
|
+
for (const ring of item) visit(ring);
|
|
1746
|
+
} else {
|
|
1747
|
+
visit(item);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return minX === Infinity ? null : { minX, minY, maxX, maxY };
|
|
1751
|
+
}
|
|
1752
|
+
function pointInRings(point, rings, fillRule = "nonzero") {
|
|
1753
|
+
const [px, py] = point;
|
|
1754
|
+
let winding = 0;
|
|
1755
|
+
for (const ring of rings) {
|
|
1756
|
+
const n = ring.length;
|
|
1757
|
+
for (let i = 0; i < n; i++) {
|
|
1758
|
+
const [ax, ay] = ring[i];
|
|
1759
|
+
const [bx, by] = ring[(i + 1) % n];
|
|
1760
|
+
if (ay <= py && by > py) {
|
|
1761
|
+
if (ax + (py - ay) / (by - ay) * (bx - ax) > px) winding++;
|
|
1762
|
+
} else if (by <= py && ay > py) {
|
|
1763
|
+
if (ax + (py - ay) / (by - ay) * (bx - ax) > px) winding--;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return fillRule === "evenodd" ? (winding & 1) === 1 : winding !== 0;
|
|
1768
|
+
}
|
|
1769
|
+
function pointInMultiPolygon(point, multi) {
|
|
1770
|
+
return multi.some((polygon) => pointInRings(point, polygon, "evenodd"));
|
|
1771
|
+
}
|
|
1772
|
+
function cleanRing(ring) {
|
|
1773
|
+
const out = [];
|
|
1774
|
+
for (const p of ring) {
|
|
1775
|
+
if (!Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
|
|
1776
|
+
const last = out[out.length - 1];
|
|
1777
|
+
if (last && last[0] === p[0] && last[1] === p[1]) continue;
|
|
1778
|
+
out.push([p[0], p[1]]);
|
|
1779
|
+
}
|
|
1780
|
+
while (out.length > 1) {
|
|
1781
|
+
const first = out[0];
|
|
1782
|
+
const last = out[out.length - 1];
|
|
1783
|
+
if (first[0] === last[0] && first[1] === last[1]) out.pop();
|
|
1784
|
+
else break;
|
|
1785
|
+
}
|
|
1786
|
+
return out;
|
|
1787
|
+
}
|
|
1788
|
+
function isOperand(input) {
|
|
1789
|
+
return !Array.isArray(input) && typeof input === "object" && input !== null && Array.isArray(input.rings);
|
|
1790
|
+
}
|
|
1791
|
+
function normalizeInput(input) {
|
|
1792
|
+
if (isOperand(input)) {
|
|
1793
|
+
return {
|
|
1794
|
+
rings: input.rings.map(cleanRing).filter((r) => r.length >= 3),
|
|
1795
|
+
fillRule: _nullishCoalesce(input.fillRule, () => ( "nonzero"))
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
const rings = [];
|
|
1799
|
+
for (const polygon of _nullishCoalesce(input, () => ( []))) {
|
|
1800
|
+
polygon.forEach((raw, index) => {
|
|
1801
|
+
const ring = cleanRing(raw);
|
|
1802
|
+
if (ring.length < 3) return;
|
|
1803
|
+
const area = ringSignedArea(ring);
|
|
1804
|
+
if (area === 0) return;
|
|
1805
|
+
const wantPositive = index === 0;
|
|
1806
|
+
rings.push(area > 0 === wantPositive ? ring : ring.reverse());
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
return { rings, fillRule: "nonzero" };
|
|
1810
|
+
}
|
|
1811
|
+
var PointPool = (_class4 = class {
|
|
1812
|
+
constructor(eps) {;_class4.prototype.__init12.call(this);_class4.prototype.__init13.call(this);
|
|
1813
|
+
this.eps = eps;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
__init12() {this.vertices = []}
|
|
1817
|
+
__init13() {this.cells = /* @__PURE__ */ new Map()}
|
|
1818
|
+
get(x, y) {
|
|
1819
|
+
const cx = Math.floor(x / this.eps);
|
|
1820
|
+
const cy = Math.floor(y / this.eps);
|
|
1821
|
+
let best = null;
|
|
1822
|
+
let bestDist = Infinity;
|
|
1823
|
+
for (let dx = -1; dx <= 1; dx++) {
|
|
1824
|
+
for (let dy = -1; dy <= 1; dy++) {
|
|
1825
|
+
const ids = this.cells.get(`${cx + dx},${cy + dy}`);
|
|
1826
|
+
if (!ids) continue;
|
|
1827
|
+
for (const id of ids) {
|
|
1828
|
+
const v = this.vertices[id];
|
|
1829
|
+
const d = Math.hypot(v.x - x, v.y - y);
|
|
1830
|
+
if (d <= this.eps && d < bestDist) {
|
|
1831
|
+
best = v;
|
|
1832
|
+
bestDist = d;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
if (best) return best;
|
|
1838
|
+
const vertex = { id: this.vertices.length, x, y };
|
|
1839
|
+
this.vertices.push(vertex);
|
|
1840
|
+
const key = `${cx},${cy}`;
|
|
1841
|
+
const bucket = this.cells.get(key);
|
|
1842
|
+
if (bucket) bucket.push(vertex.id);
|
|
1843
|
+
else this.cells.set(key, [vertex.id]);
|
|
1844
|
+
return vertex;
|
|
1845
|
+
}
|
|
1846
|
+
}, _class4);
|
|
1847
|
+
function project(a, b, p) {
|
|
1848
|
+
const dx = b.x - a.x;
|
|
1849
|
+
const dy = b.y - a.y;
|
|
1850
|
+
const len2 = dx * dx + dy * dy;
|
|
1851
|
+
const t = len2 === 0 ? 0 : ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
|
|
1852
|
+
const cx = a.x + t * dx;
|
|
1853
|
+
const cy = a.y + t * dy;
|
|
1854
|
+
return { t, dist: Math.hypot(p.x - cx, p.y - cy) };
|
|
1855
|
+
}
|
|
1856
|
+
function addSplit(splits, t, v) {
|
|
1857
|
+
const { a, b } = splits.segment;
|
|
1858
|
+
if (v.id === a.id || v.id === b.id) return;
|
|
1859
|
+
if (!splits.points.has(v.id)) splits.points.set(v.id, { t, v });
|
|
1860
|
+
}
|
|
1861
|
+
function splitPass(segments, pool, eps) {
|
|
1862
|
+
const order = segments.map((s, i) => ({
|
|
1863
|
+
i,
|
|
1864
|
+
minX: Math.min(s.a.x, s.b.x),
|
|
1865
|
+
maxX: Math.max(s.a.x, s.b.x),
|
|
1866
|
+
minY: Math.min(s.a.y, s.b.y),
|
|
1867
|
+
maxY: Math.max(s.a.y, s.b.y)
|
|
1868
|
+
})).sort((p, q) => p.minX - q.minX);
|
|
1869
|
+
const splits = segments.map((segment) => ({ segment, points: /* @__PURE__ */ new Map() }));
|
|
1870
|
+
for (let oi = 0; oi < order.length; oi++) {
|
|
1871
|
+
const bi = order[oi];
|
|
1872
|
+
const s1 = segments[bi.i];
|
|
1873
|
+
for (let oj = oi + 1; oj < order.length; oj++) {
|
|
1874
|
+
const bj = order[oj];
|
|
1875
|
+
if (bj.minX > bi.maxX + eps) break;
|
|
1876
|
+
if (bj.minY > bi.maxY + eps || bj.maxY < bi.minY - eps) continue;
|
|
1877
|
+
const s2 = segments[bj.i];
|
|
1878
|
+
const sharedA = s1.a.id === s2.a.id || s1.a.id === s2.b.id;
|
|
1879
|
+
const sharedB = s1.b.id === s2.a.id || s1.b.id === s2.b.id;
|
|
1880
|
+
if (sharedA && sharedB) continue;
|
|
1881
|
+
let touched = false;
|
|
1882
|
+
for (const [seg, other, split] of [
|
|
1883
|
+
[s1, s2, splits[bi.i]],
|
|
1884
|
+
[s2, s1, splits[bj.i]]
|
|
1885
|
+
]) {
|
|
1886
|
+
for (const p of [other.a, other.b]) {
|
|
1887
|
+
if (p.id === seg.a.id || p.id === seg.b.id) continue;
|
|
1888
|
+
const { t: t2, dist } = project(seg.a, seg.b, p);
|
|
1889
|
+
const len = Math.hypot(seg.b.x - seg.a.x, seg.b.y - seg.a.y);
|
|
1890
|
+
if (dist <= eps && t2 * len > eps && (1 - t2) * len > eps) {
|
|
1891
|
+
addSplit(split, t2, p);
|
|
1892
|
+
touched = true;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
if (touched) continue;
|
|
1897
|
+
const d1x = s1.b.x - s1.a.x;
|
|
1898
|
+
const d1y = s1.b.y - s1.a.y;
|
|
1899
|
+
const d2x = s2.b.x - s2.a.x;
|
|
1900
|
+
const d2y = s2.b.y - s2.a.y;
|
|
1901
|
+
const denom = d1x * d2y - d1y * d2x;
|
|
1902
|
+
const len1 = Math.hypot(d1x, d1y);
|
|
1903
|
+
const len2 = Math.hypot(d2x, d2y);
|
|
1904
|
+
if (Math.abs(denom) <= 1e-12 * len1 * len2) continue;
|
|
1905
|
+
const ex = s2.a.x - s1.a.x;
|
|
1906
|
+
const ey = s2.a.y - s1.a.y;
|
|
1907
|
+
const t = (ex * d2y - ey * d2x) / denom;
|
|
1908
|
+
const u = (ex * d1y - ey * d1x) / denom;
|
|
1909
|
+
const tolT = eps / len1;
|
|
1910
|
+
const tolU = eps / len2;
|
|
1911
|
+
if (t < -tolT || t > 1 + tolT || u < -tolU || u > 1 + tolU) continue;
|
|
1912
|
+
if (sharedA || sharedB) continue;
|
|
1913
|
+
const v = pool.get(s1.a.x + t * d1x, s1.a.y + t * d1y);
|
|
1914
|
+
addSplit(splits[bi.i], project(s1.a, s1.b, v).t, v);
|
|
1915
|
+
addSplit(splits[bj.i], project(s2.a, s2.b, v).t, v);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
let changed = false;
|
|
1919
|
+
const out = [];
|
|
1920
|
+
for (const { segment, points } of splits) {
|
|
1921
|
+
if (points.size === 0) {
|
|
1922
|
+
out.push(segment);
|
|
1923
|
+
continue;
|
|
1924
|
+
}
|
|
1925
|
+
changed = true;
|
|
1926
|
+
const ordered = [...points.values()].sort((p, q) => p.t - q.t);
|
|
1927
|
+
let prev = segment.a;
|
|
1928
|
+
for (const { v } of ordered) {
|
|
1929
|
+
if (v.id !== prev.id) out.push({ a: prev, b: v, operand: segment.operand });
|
|
1930
|
+
prev = v;
|
|
1931
|
+
}
|
|
1932
|
+
if (prev.id !== segment.b.id) out.push({ a: prev, b: segment.b, operand: segment.operand });
|
|
1933
|
+
}
|
|
1934
|
+
return { segments: out, changed };
|
|
1935
|
+
}
|
|
1936
|
+
function defaultEpsilon(operands) {
|
|
1937
|
+
let max = 0;
|
|
1938
|
+
for (const op of operands) {
|
|
1939
|
+
for (const ring of op.rings) {
|
|
1940
|
+
for (const [x, y] of ring) {
|
|
1941
|
+
const m = Math.max(Math.abs(x), Math.abs(y));
|
|
1942
|
+
if (m > max) max = m;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
return Math.max(1e-9, max * 1e-9);
|
|
1947
|
+
}
|
|
1948
|
+
function buildOverlay(inputs, options) {
|
|
1949
|
+
const operands = inputs.map(normalizeInput);
|
|
1950
|
+
const eps = options.epsilon && options.epsilon > 0 ? options.epsilon : defaultEpsilon(operands);
|
|
1951
|
+
const pool = new PointPool(eps);
|
|
1952
|
+
let segments = [];
|
|
1953
|
+
operands.forEach((op, operand) => {
|
|
1954
|
+
for (const ring of op.rings) {
|
|
1955
|
+
const verts = ring.map(([x, y]) => pool.get(x, y));
|
|
1956
|
+
for (let i = 0; i < verts.length; i++) {
|
|
1957
|
+
const a = verts[i];
|
|
1958
|
+
const b = verts[(i + 1) % verts.length];
|
|
1959
|
+
if (a.id !== b.id) segments.push({ a, b, operand });
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
});
|
|
1963
|
+
for (let pass = 0; pass < 8; pass++) {
|
|
1964
|
+
const result = splitPass(segments, pool, eps);
|
|
1965
|
+
segments = result.segments;
|
|
1966
|
+
if (!result.changed) break;
|
|
1967
|
+
}
|
|
1968
|
+
const count = operands.length;
|
|
1969
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1970
|
+
for (const s of segments) {
|
|
1971
|
+
const forward = s.a.id < s.b.id;
|
|
1972
|
+
const a = forward ? s.a : s.b;
|
|
1973
|
+
const b = forward ? s.b : s.a;
|
|
1974
|
+
const key = `${a.id},${b.id}`;
|
|
1975
|
+
let edge = byKey.get(key);
|
|
1976
|
+
if (!edge) {
|
|
1977
|
+
edge = { a, b, mult: new Array(count).fill(0) };
|
|
1978
|
+
byKey.set(key, edge);
|
|
1979
|
+
}
|
|
1980
|
+
edge.mult[s.operand] += forward ? 1 : -1;
|
|
1981
|
+
}
|
|
1982
|
+
const edges = [...byKey.values()].filter((e) => e.mult.some((m) => m !== 0));
|
|
1983
|
+
const right = [];
|
|
1984
|
+
const left = [];
|
|
1985
|
+
for (let i = 0; i < edges.length; i++) {
|
|
1986
|
+
const edge = edges[i];
|
|
1987
|
+
const horizontal = Math.abs(edge.b.y - edge.a.y) < Math.abs(edge.b.x - edge.a.x);
|
|
1988
|
+
const tx = (v) => horizontal ? v.y : v.x;
|
|
1989
|
+
const ty = (v) => horizontal ? -v.x : v.y;
|
|
1990
|
+
const ax = tx(edge.a);
|
|
1991
|
+
const ay = ty(edge.a);
|
|
1992
|
+
const bx = tx(edge.b);
|
|
1993
|
+
const by = ty(edge.b);
|
|
1994
|
+
const mx = (ax + bx) / 2;
|
|
1995
|
+
const my = (ay + by) / 2;
|
|
1996
|
+
const winding = new Array(count).fill(0);
|
|
1997
|
+
for (let j = 0; j < edges.length; j++) {
|
|
1998
|
+
if (j === i) continue;
|
|
1999
|
+
const other = edges[j];
|
|
2000
|
+
const oax = tx(other.a);
|
|
2001
|
+
const oay = ty(other.a);
|
|
2002
|
+
const obx = tx(other.b);
|
|
2003
|
+
const oby = ty(other.b);
|
|
2004
|
+
let dir = 0;
|
|
2005
|
+
if (oay <= my && oby > my) dir = 1;
|
|
2006
|
+
else if (oby <= my && oay > my) dir = -1;
|
|
2007
|
+
else continue;
|
|
2008
|
+
const x = oax + (my - oay) / (oby - oay) * (obx - oax);
|
|
2009
|
+
if (x <= mx) continue;
|
|
2010
|
+
for (let k = 0; k < count; k++) {
|
|
2011
|
+
if (other.mult[k] !== 0) winding[k] += dir * other.mult[k];
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
const sideIsRight = by > ay;
|
|
2015
|
+
const r = sideIsRight ? winding : winding.map((w, k) => w - edge.mult[k]);
|
|
2016
|
+
right.push(r);
|
|
2017
|
+
left.push(r.map((w, k) => w + edge.mult[k]));
|
|
2018
|
+
}
|
|
2019
|
+
return { edges, right, left, fillRules: operands.map((o) => o.fillRule), eps };
|
|
2020
|
+
}
|
|
2021
|
+
function insideFlags(windings, fillRules) {
|
|
2022
|
+
return windings.map((w, k) => fillRules[k] === "evenodd" ? (w & 1) === 1 : w !== 0);
|
|
2023
|
+
}
|
|
2024
|
+
function traceRings(directed) {
|
|
2025
|
+
const outgoing = /* @__PURE__ */ new Map();
|
|
2026
|
+
for (const e of directed) {
|
|
2027
|
+
const list = outgoing.get(e.from.id);
|
|
2028
|
+
if (list) list.push(e);
|
|
2029
|
+
else outgoing.set(e.from.id, [e]);
|
|
2030
|
+
}
|
|
2031
|
+
const TWO_PI = Math.PI * 2;
|
|
2032
|
+
const next = (incoming) => {
|
|
2033
|
+
const candidates = outgoing.get(incoming.to.id);
|
|
2034
|
+
if (!candidates) return void 0;
|
|
2035
|
+
const back = Math.atan2(incoming.from.y - incoming.to.y, incoming.from.x - incoming.to.x);
|
|
2036
|
+
let best;
|
|
2037
|
+
let bestTurn = Infinity;
|
|
2038
|
+
for (const c of candidates) {
|
|
2039
|
+
let turn = back - c.angle;
|
|
2040
|
+
turn -= Math.floor(turn / TWO_PI) * TWO_PI;
|
|
2041
|
+
if (turn <= 1e-15) turn = TWO_PI;
|
|
2042
|
+
if (turn < bestTurn) {
|
|
2043
|
+
bestTurn = turn;
|
|
2044
|
+
best = c;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return best;
|
|
2048
|
+
};
|
|
2049
|
+
const used = /* @__PURE__ */ new Set();
|
|
2050
|
+
const rings = [];
|
|
2051
|
+
for (const start of directed) {
|
|
2052
|
+
if (used.has(start)) continue;
|
|
2053
|
+
const ring = [];
|
|
2054
|
+
let edge = start;
|
|
2055
|
+
let guard = directed.length + 1;
|
|
2056
|
+
while (edge && !used.has(edge) && guard-- > 0) {
|
|
2057
|
+
used.add(edge);
|
|
2058
|
+
ring.push(edge.from);
|
|
2059
|
+
edge = next(edge);
|
|
2060
|
+
}
|
|
2061
|
+
if (ring.length >= 3) rings.push(ring);
|
|
2062
|
+
}
|
|
2063
|
+
return rings;
|
|
2064
|
+
}
|
|
2065
|
+
function removeCollinear(ring, eps) {
|
|
2066
|
+
let points = ring;
|
|
2067
|
+
let changed = true;
|
|
2068
|
+
while (changed && points.length > 3) {
|
|
2069
|
+
changed = false;
|
|
2070
|
+
const out = [];
|
|
2071
|
+
const n = points.length;
|
|
2072
|
+
for (let i = 0; i < n; i++) {
|
|
2073
|
+
const prev = out.length > 0 ? out[out.length - 1] : points[(i - 1 + n) % n];
|
|
2074
|
+
const cur = points[i];
|
|
2075
|
+
const nxt = points[(i + 1) % n];
|
|
2076
|
+
const dx = nxt[0] - prev[0];
|
|
2077
|
+
const dy = nxt[1] - prev[1];
|
|
2078
|
+
const len = Math.hypot(dx, dy);
|
|
2079
|
+
const cross = (cur[0] - prev[0]) * dy - (cur[1] - prev[1]) * dx;
|
|
2080
|
+
const dot = (cur[0] - prev[0]) * dx + (cur[1] - prev[1]) * dy;
|
|
2081
|
+
if (len > 0 && Math.abs(cross) / len <= eps && dot > 0 && dot < len * len) {
|
|
2082
|
+
changed = true;
|
|
2083
|
+
continue;
|
|
2084
|
+
}
|
|
2085
|
+
out.push(cur);
|
|
2086
|
+
}
|
|
2087
|
+
if (out.length < 3) return out;
|
|
2088
|
+
points = out;
|
|
2089
|
+
}
|
|
2090
|
+
return points;
|
|
2091
|
+
}
|
|
2092
|
+
function assemble(rings, eps) {
|
|
2093
|
+
const outers = [];
|
|
2094
|
+
const holes = [];
|
|
2095
|
+
const minArea = eps * eps;
|
|
2096
|
+
for (const vertices of rings) {
|
|
2097
|
+
const ring = removeCollinear(
|
|
2098
|
+
vertices.map((v) => [v.x, v.y]),
|
|
2099
|
+
eps
|
|
2100
|
+
);
|
|
2101
|
+
if (ring.length < 3) continue;
|
|
2102
|
+
const area = ringSignedArea(ring);
|
|
2103
|
+
if (Math.abs(area) <= minArea) continue;
|
|
2104
|
+
(area > 0 ? outers : holes).push({ ring, area: Math.abs(area) });
|
|
2105
|
+
}
|
|
2106
|
+
outers.sort((a, b) => b.area - a.area);
|
|
2107
|
+
const polygons = outers.map((o) => [o.ring]);
|
|
2108
|
+
for (const hole of holes) {
|
|
2109
|
+
const [p, q] = [hole.ring[0], hole.ring[1]];
|
|
2110
|
+
const sample = [(p[0] + q[0]) / 2, (p[1] + q[1]) / 2];
|
|
2111
|
+
let owner = -1;
|
|
2112
|
+
for (let i = outers.length - 1; i >= 0; i--) {
|
|
2113
|
+
if (outers[i].area >= hole.area && pointInRings(sample, [outers[i].ring], "evenodd")) {
|
|
2114
|
+
owner = i;
|
|
2115
|
+
break;
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
if (owner >= 0) polygons[owner].push(hole.ring);
|
|
2119
|
+
}
|
|
2120
|
+
return polygons;
|
|
2121
|
+
}
|
|
2122
|
+
function extract(overlay, isInside) {
|
|
2123
|
+
const directed = [];
|
|
2124
|
+
overlay.edges.forEach((edge, i) => {
|
|
2125
|
+
const l = isInside(insideFlags(overlay.left[i], overlay.fillRules));
|
|
2126
|
+
const r = isInside(insideFlags(overlay.right[i], overlay.fillRules));
|
|
2127
|
+
if (l === r) return;
|
|
2128
|
+
const [from, to] = l ? [edge.a, edge.b] : [edge.b, edge.a];
|
|
2129
|
+
directed.push({ from, to, angle: Math.atan2(to.y - from.y, to.x - from.x) });
|
|
2130
|
+
});
|
|
2131
|
+
return assemble(traceRings(directed), overlay.eps);
|
|
2132
|
+
}
|
|
2133
|
+
function predicate(operation) {
|
|
2134
|
+
switch (operation) {
|
|
2135
|
+
case "union":
|
|
2136
|
+
return (f) => f.some(Boolean);
|
|
2137
|
+
case "intersection":
|
|
2138
|
+
return (f) => f.length > 0 && f.every(Boolean);
|
|
2139
|
+
case "difference":
|
|
2140
|
+
return (f) => f[0] === true && !f.slice(1).some(Boolean);
|
|
2141
|
+
case "xor":
|
|
2142
|
+
return (f) => f.filter(Boolean).length % 2 === 1;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function polygonBoolean(operation, operands, options = {}) {
|
|
2146
|
+
if (operands.length === 0) return [];
|
|
2147
|
+
return extract(buildOverlay(operands, options), predicate(operation));
|
|
2148
|
+
}
|
|
2149
|
+
var unionPolygons = (a, b, options) => polygonBoolean("union", [a, b], options);
|
|
2150
|
+
var intersectPolygons = (a, b, options) => polygonBoolean("intersection", [a, b], options);
|
|
2151
|
+
var subtractPolygons = (a, b, options) => polygonBoolean("difference", [a, b], options);
|
|
2152
|
+
var xorPolygons = (a, b, options) => polygonBoolean("xor", [a, b], options);
|
|
2153
|
+
function dividePolygons(operands, options = {}) {
|
|
2154
|
+
if (operands.length === 0) return [];
|
|
2155
|
+
const overlay = buildOverlay(operands, options);
|
|
2156
|
+
const signatures = /* @__PURE__ */ new Map();
|
|
2157
|
+
const note = (windings) => {
|
|
2158
|
+
const flags = insideFlags(windings, overlay.fillRules);
|
|
2159
|
+
if (!flags.some(Boolean)) return;
|
|
2160
|
+
const key = flags.map((f) => f ? "1" : "0").join("");
|
|
2161
|
+
if (!signatures.has(key)) signatures.set(key, flags.flatMap((f, k) => f ? [k] : []));
|
|
2162
|
+
};
|
|
2163
|
+
overlay.edges.forEach((_, i) => {
|
|
2164
|
+
note(overlay.left[i]);
|
|
2165
|
+
note(overlay.right[i]);
|
|
2166
|
+
});
|
|
2167
|
+
const pieces = [];
|
|
2168
|
+
const keys = [...signatures.keys()].sort();
|
|
2169
|
+
for (const key of keys) {
|
|
2170
|
+
const polygons = extract(overlay, (flags) => flags.map((f) => f ? "1" : "0").join("") === key);
|
|
2171
|
+
polygons.sort((a, b) => polygonArea(b) - polygonArea(a));
|
|
2172
|
+
for (const polygon of polygons) pieces.push({ polygon, operands: signatures.get(key) });
|
|
2173
|
+
}
|
|
2174
|
+
return pieces;
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
// src/canvas/shapeGeometry.ts
|
|
2178
|
+
var SHAPE_KINDS = ["rect", "ellipse", "polygon", "star", "path"];
|
|
2179
|
+
function isShapeDescription(value) {
|
|
2180
|
+
if (!value || typeof value !== "object") return false;
|
|
2181
|
+
const kind = value.kind;
|
|
2182
|
+
if (!SHAPE_KINDS.includes(kind)) return false;
|
|
2183
|
+
return kind !== "path" || typeof value.d === "string";
|
|
2184
|
+
}
|
|
2185
|
+
var PARAM_COUNTS = { m: 2, l: 2, h: 1, v: 1, c: 6, s: 4, q: 4, t: 2, a: 7, z: 0 };
|
|
2186
|
+
var PathScanner = (_class5 = class {
|
|
2187
|
+
constructor(text) {;_class5.prototype.__init14.call(this);
|
|
2188
|
+
this.text = text;
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
__init14() {this.pos = 0}
|
|
2192
|
+
skip() {
|
|
2193
|
+
while (this.pos < this.text.length && /[\s,]/.test(this.text[this.pos])) this.pos++;
|
|
2194
|
+
}
|
|
2195
|
+
/** The next command letter, or `null` when a number (or the end) follows. */
|
|
2196
|
+
command() {
|
|
2197
|
+
this.skip();
|
|
2198
|
+
const ch = this.text[this.pos];
|
|
2199
|
+
if (ch && /[MmLlHhVvCcSsQqTtAaZz]/.test(ch)) {
|
|
2200
|
+
this.pos++;
|
|
2201
|
+
return ch;
|
|
2202
|
+
}
|
|
2203
|
+
return null;
|
|
2204
|
+
}
|
|
2205
|
+
done() {
|
|
2206
|
+
this.skip();
|
|
2207
|
+
return this.pos >= this.text.length;
|
|
2208
|
+
}
|
|
2209
|
+
number() {
|
|
2210
|
+
this.skip();
|
|
2211
|
+
const match = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(this.text.slice(this.pos, this.pos + 64));
|
|
2212
|
+
if (!match) return null;
|
|
2213
|
+
this.pos += match[0].length;
|
|
2214
|
+
const value = Number(match[0]);
|
|
2215
|
+
return Number.isFinite(value) ? value : null;
|
|
2216
|
+
}
|
|
2217
|
+
flag() {
|
|
2218
|
+
this.skip();
|
|
2219
|
+
const ch = this.text[this.pos];
|
|
2220
|
+
if (ch === "0" || ch === "1") {
|
|
2221
|
+
this.pos++;
|
|
2222
|
+
return ch === "1" ? 1 : 0;
|
|
2223
|
+
}
|
|
2224
|
+
return null;
|
|
2225
|
+
}
|
|
2226
|
+
}, _class5);
|
|
2227
|
+
function parseSvgPath(d) {
|
|
2228
|
+
const scanner = new PathScanner(_nullishCoalesce(d, () => ( "")));
|
|
2229
|
+
const commands = [];
|
|
2230
|
+
let cx = 0;
|
|
2231
|
+
let cy = 0;
|
|
2232
|
+
let startX = 0;
|
|
2233
|
+
let startY = 0;
|
|
2234
|
+
let lastControl = null;
|
|
2235
|
+
let command = "";
|
|
2236
|
+
while (!scanner.done()) {
|
|
2237
|
+
const letter = scanner.command();
|
|
2238
|
+
if (letter) {
|
|
2239
|
+
command = letter;
|
|
2240
|
+
if (command === "z" || command === "Z") {
|
|
2241
|
+
commands.push({ type: "Z" });
|
|
2242
|
+
cx = startX;
|
|
2243
|
+
cy = startY;
|
|
2244
|
+
lastControl = null;
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
} else if (!command || command === "z" || command === "Z") {
|
|
2248
|
+
break;
|
|
2249
|
+
}
|
|
2250
|
+
const lower = command.toLowerCase();
|
|
2251
|
+
const count = PARAM_COUNTS[lower];
|
|
2252
|
+
const args = [];
|
|
2253
|
+
for (let k = 0; k < count; k++) {
|
|
2254
|
+
const value = lower === "a" && (k === 3 || k === 4) ? scanner.flag() : scanner.number();
|
|
2255
|
+
if (value === null) return commands;
|
|
2256
|
+
args.push(value);
|
|
2257
|
+
}
|
|
2258
|
+
const rel = command !== command.toUpperCase();
|
|
2259
|
+
const ox = rel ? cx : 0;
|
|
2260
|
+
const oy = rel ? cy : 0;
|
|
2261
|
+
switch (lower) {
|
|
2262
|
+
case "m": {
|
|
2263
|
+
cx = args[0] + ox;
|
|
2264
|
+
cy = args[1] + oy;
|
|
2265
|
+
startX = cx;
|
|
2266
|
+
startY = cy;
|
|
2267
|
+
commands.push({ type: "M", x: cx, y: cy });
|
|
2268
|
+
lastControl = null;
|
|
2269
|
+
command = rel ? "l" : "L";
|
|
2270
|
+
break;
|
|
2271
|
+
}
|
|
2272
|
+
case "l":
|
|
2273
|
+
cx = args[0] + ox;
|
|
2274
|
+
cy = args[1] + oy;
|
|
2275
|
+
commands.push({ type: "L", x: cx, y: cy });
|
|
2276
|
+
lastControl = null;
|
|
2277
|
+
break;
|
|
2278
|
+
case "h":
|
|
2279
|
+
cx = args[0] + (rel ? cx : 0);
|
|
2280
|
+
commands.push({ type: "L", x: cx, y: cy });
|
|
2281
|
+
lastControl = null;
|
|
2282
|
+
break;
|
|
2283
|
+
case "v":
|
|
2284
|
+
cy = args[0] + (rel ? cy : 0);
|
|
2285
|
+
commands.push({ type: "L", x: cx, y: cy });
|
|
2286
|
+
lastControl = null;
|
|
2287
|
+
break;
|
|
2288
|
+
case "c": {
|
|
2289
|
+
const c = {
|
|
2290
|
+
type: "C",
|
|
2291
|
+
x1: args[0] + ox,
|
|
2292
|
+
y1: args[1] + oy,
|
|
2293
|
+
x2: args[2] + ox,
|
|
2294
|
+
y2: args[3] + oy,
|
|
2295
|
+
x: args[4] + ox,
|
|
2296
|
+
y: args[5] + oy
|
|
2297
|
+
};
|
|
2298
|
+
commands.push(c);
|
|
2299
|
+
lastControl = { x: c.x2, y: c.y2, type: "C" };
|
|
2300
|
+
cx = c.x;
|
|
2301
|
+
cy = c.y;
|
|
2302
|
+
break;
|
|
2303
|
+
}
|
|
2304
|
+
case "s": {
|
|
2305
|
+
const x1 = _optionalChain([lastControl, 'optionalAccess', _51 => _51.type]) === "C" ? 2 * cx - lastControl.x : cx;
|
|
2306
|
+
const y1 = _optionalChain([lastControl, 'optionalAccess', _52 => _52.type]) === "C" ? 2 * cy - lastControl.y : cy;
|
|
2307
|
+
const c = {
|
|
2308
|
+
type: "C",
|
|
2309
|
+
x1,
|
|
2310
|
+
y1,
|
|
2311
|
+
x2: args[0] + ox,
|
|
2312
|
+
y2: args[1] + oy,
|
|
2313
|
+
x: args[2] + ox,
|
|
2314
|
+
y: args[3] + oy
|
|
2315
|
+
};
|
|
2316
|
+
commands.push(c);
|
|
2317
|
+
lastControl = { x: c.x2, y: c.y2, type: "C" };
|
|
2318
|
+
cx = c.x;
|
|
2319
|
+
cy = c.y;
|
|
2320
|
+
break;
|
|
2321
|
+
}
|
|
2322
|
+
case "q": {
|
|
2323
|
+
const c = { type: "Q", x1: args[0] + ox, y1: args[1] + oy, x: args[2] + ox, y: args[3] + oy };
|
|
2324
|
+
commands.push(c);
|
|
2325
|
+
lastControl = { x: c.x1, y: c.y1, type: "Q" };
|
|
2326
|
+
cx = c.x;
|
|
2327
|
+
cy = c.y;
|
|
2328
|
+
break;
|
|
2329
|
+
}
|
|
2330
|
+
case "t": {
|
|
2331
|
+
const x1 = _optionalChain([lastControl, 'optionalAccess', _53 => _53.type]) === "Q" ? 2 * cx - lastControl.x : cx;
|
|
2332
|
+
const y1 = _optionalChain([lastControl, 'optionalAccess', _54 => _54.type]) === "Q" ? 2 * cy - lastControl.y : cy;
|
|
2333
|
+
const c = { type: "Q", x1, y1, x: args[0] + ox, y: args[1] + oy };
|
|
2334
|
+
commands.push(c);
|
|
2335
|
+
lastControl = { x: x1, y: y1, type: "Q" };
|
|
2336
|
+
cx = c.x;
|
|
2337
|
+
cy = c.y;
|
|
2338
|
+
break;
|
|
2339
|
+
}
|
|
2340
|
+
case "a": {
|
|
2341
|
+
const c = {
|
|
2342
|
+
type: "A",
|
|
2343
|
+
rx: Math.abs(args[0]),
|
|
2344
|
+
ry: Math.abs(args[1]),
|
|
2345
|
+
rotation: args[2],
|
|
2346
|
+
largeArc: args[3] !== 0,
|
|
2347
|
+
sweep: args[4] !== 0,
|
|
2348
|
+
x: args[5] + ox,
|
|
2349
|
+
y: args[6] + oy
|
|
2350
|
+
};
|
|
2351
|
+
commands.push(c);
|
|
2352
|
+
lastControl = null;
|
|
2353
|
+
cx = c.x;
|
|
2354
|
+
cy = c.y;
|
|
2355
|
+
break;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return commands;
|
|
2360
|
+
}
|
|
2361
|
+
function segmentCount(curvature, tolerance, max) {
|
|
2362
|
+
if (!(curvature > 0)) return 1;
|
|
2363
|
+
return Math.max(1, Math.min(max, Math.ceil(Math.sqrt(curvature / tolerance))));
|
|
2364
|
+
}
|
|
2365
|
+
function flattenCubic(out, p0, x1, y1, x2, y2, x, y, tolerance, max) {
|
|
2366
|
+
const dd = Math.max(
|
|
2367
|
+
Math.hypot(p0[0] - 2 * x1 + x2, p0[1] - 2 * y1 + y2),
|
|
2368
|
+
Math.hypot(x1 - 2 * x2 + x, y1 - 2 * y2 + y)
|
|
2369
|
+
);
|
|
2370
|
+
const n = segmentCount(6 * dd / 8, tolerance, max);
|
|
2371
|
+
for (let k = 1; k <= n; k++) {
|
|
2372
|
+
const t = k / n;
|
|
2373
|
+
const mt = 1 - t;
|
|
2374
|
+
const a = mt * mt * mt;
|
|
2375
|
+
const b = 3 * mt * mt * t;
|
|
2376
|
+
const c = 3 * mt * t * t;
|
|
2377
|
+
const e = t * t * t;
|
|
2378
|
+
out.push([a * p0[0] + b * x1 + c * x2 + e * x, a * p0[1] + b * y1 + c * y2 + e * y]);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
function flattenQuadratic(out, p0, x1, y1, x, y, tolerance, max) {
|
|
2382
|
+
const dd = Math.hypot(p0[0] - 2 * x1 + x, p0[1] - 2 * y1 + y);
|
|
2383
|
+
const n = segmentCount(2 * dd / 8, tolerance, max);
|
|
2384
|
+
for (let k = 1; k <= n; k++) {
|
|
2385
|
+
const t = k / n;
|
|
2386
|
+
const mt = 1 - t;
|
|
2387
|
+
out.push([mt * mt * p0[0] + 2 * mt * t * x1 + t * t * x, mt * mt * p0[1] + 2 * mt * t * y1 + t * t * y]);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
function arcSteps(radius, sweep, tolerance, max) {
|
|
2391
|
+
if (radius <= tolerance) return Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 2)));
|
|
2392
|
+
const step = 2 * Math.acos(1 - tolerance / radius);
|
|
2393
|
+
return Math.max(1, Math.min(max, Math.ceil(Math.abs(sweep) / step)));
|
|
2394
|
+
}
|
|
2395
|
+
function flattenArc(out, p0, cmd, tolerance, max) {
|
|
2396
|
+
const [x0, y0] = p0;
|
|
2397
|
+
let { rx, ry } = cmd;
|
|
2398
|
+
const { x, y } = cmd;
|
|
2399
|
+
if (x0 === x && y0 === y) return;
|
|
2400
|
+
if (rx === 0 || ry === 0) {
|
|
2401
|
+
out.push([x, y]);
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
const phi = cmd.rotation * Math.PI / 180;
|
|
2405
|
+
const cos = Math.cos(phi);
|
|
2406
|
+
const sin = Math.sin(phi);
|
|
2407
|
+
const dx = (x0 - x) / 2;
|
|
2408
|
+
const dy = (y0 - y) / 2;
|
|
2409
|
+
const x1p = cos * dx + sin * dy;
|
|
2410
|
+
const y1p = -sin * dx + cos * dy;
|
|
2411
|
+
const lambda = x1p * x1p / (rx * rx) + y1p * y1p / (ry * ry);
|
|
2412
|
+
if (lambda > 1) {
|
|
2413
|
+
const s = Math.sqrt(lambda);
|
|
2414
|
+
rx *= s;
|
|
2415
|
+
ry *= s;
|
|
2416
|
+
}
|
|
2417
|
+
const num = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;
|
|
2418
|
+
const den = rx * rx * y1p * y1p + ry * ry * x1p * x1p;
|
|
2419
|
+
let coef = den === 0 ? 0 : Math.sqrt(Math.max(0, num / den));
|
|
2420
|
+
if (cmd.largeArc === cmd.sweep) coef = -coef;
|
|
2421
|
+
const cxp = coef * rx * y1p / ry;
|
|
2422
|
+
const cyp = -coef * ry * x1p / rx;
|
|
2423
|
+
const cx = cos * cxp - sin * cyp + (x0 + x) / 2;
|
|
2424
|
+
const cy = sin * cxp + cos * cyp + (y0 + y) / 2;
|
|
2425
|
+
const angle = (ux, uy, vx, vy) => {
|
|
2426
|
+
const a = Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);
|
|
2427
|
+
return a;
|
|
2428
|
+
};
|
|
2429
|
+
const theta1 = angle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
|
|
2430
|
+
let delta = angle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry);
|
|
2431
|
+
if (!cmd.sweep && delta > 0) delta -= 2 * Math.PI;
|
|
2432
|
+
else if (cmd.sweep && delta < 0) delta += 2 * Math.PI;
|
|
2433
|
+
const n = arcSteps(Math.max(rx, ry), delta, tolerance, max);
|
|
2434
|
+
for (let k = 1; k < n; k++) {
|
|
2435
|
+
const t = theta1 + delta * k / n;
|
|
2436
|
+
const ex = rx * Math.cos(t);
|
|
2437
|
+
const ey = ry * Math.sin(t);
|
|
2438
|
+
out.push([cos * ex - sin * ey + cx, sin * ex + cos * ey + cy]);
|
|
2439
|
+
}
|
|
2440
|
+
out.push([x, y]);
|
|
2441
|
+
}
|
|
2442
|
+
function flattenSvgPath(d, options = {}) {
|
|
2443
|
+
const tolerance = options.tolerance && options.tolerance > 0 ? options.tolerance : 0.25;
|
|
2444
|
+
const max = _nullishCoalesce(options.maxSegments, () => ( 512));
|
|
2445
|
+
const commands = typeof d === "string" ? parseSvgPath(d) : d;
|
|
2446
|
+
const rings = [];
|
|
2447
|
+
let current = [];
|
|
2448
|
+
let start = [0, 0];
|
|
2449
|
+
const flush = () => {
|
|
2450
|
+
if (current.length >= 3) rings.push(current);
|
|
2451
|
+
current = [];
|
|
2452
|
+
};
|
|
2453
|
+
const last = () => _nullishCoalesce(current[current.length - 1], () => ( start));
|
|
2454
|
+
for (const cmd of commands) {
|
|
2455
|
+
switch (cmd.type) {
|
|
2456
|
+
case "M":
|
|
2457
|
+
flush();
|
|
2458
|
+
start = [cmd.x, cmd.y];
|
|
2459
|
+
current = [start];
|
|
2460
|
+
break;
|
|
2461
|
+
case "L":
|
|
2462
|
+
if (current.length === 0) current = [start];
|
|
2463
|
+
current.push([cmd.x, cmd.y]);
|
|
2464
|
+
break;
|
|
2465
|
+
case "C":
|
|
2466
|
+
if (current.length === 0) current = [start];
|
|
2467
|
+
flattenCubic(current, last(), cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y, tolerance, max);
|
|
2468
|
+
break;
|
|
2469
|
+
case "Q":
|
|
2470
|
+
if (current.length === 0) current = [start];
|
|
2471
|
+
flattenQuadratic(current, last(), cmd.x1, cmd.y1, cmd.x, cmd.y, tolerance, max);
|
|
2472
|
+
break;
|
|
2473
|
+
case "A":
|
|
2474
|
+
if (current.length === 0) current = [start];
|
|
2475
|
+
flattenArc(current, last(), cmd, tolerance, max);
|
|
2476
|
+
break;
|
|
2477
|
+
case "Z":
|
|
2478
|
+
flush();
|
|
2479
|
+
break;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
flush();
|
|
2483
|
+
return rings.map(dedupeRing).filter((r) => r.length >= 3);
|
|
2484
|
+
}
|
|
2485
|
+
function dedupeRing(ring) {
|
|
2486
|
+
const out = [];
|
|
2487
|
+
for (const p of ring) {
|
|
2488
|
+
const prev = out[out.length - 1];
|
|
2489
|
+
if (!prev || prev[0] !== p[0] || prev[1] !== p[1]) out.push(p);
|
|
2490
|
+
}
|
|
2491
|
+
while (out.length > 1 && out[0][0] === out[out.length - 1][0] && out[0][1] === out[out.length - 1][1]) out.pop();
|
|
2492
|
+
return out;
|
|
2493
|
+
}
|
|
2494
|
+
function cornerRadii(shape, width, height) {
|
|
2495
|
+
const raw = _nullishCoalesce(shape.cornerRadius, () => ( 0));
|
|
2496
|
+
let radii = (typeof raw === "number" ? [raw, raw, raw, raw] : [...raw]).map(
|
|
2497
|
+
(r) => Number.isFinite(r) && r > 0 ? r : 0
|
|
2498
|
+
);
|
|
2499
|
+
const factor = Math.min(
|
|
2500
|
+
1,
|
|
2501
|
+
width / Math.max(radii[0] + radii[1], 1e-12),
|
|
2502
|
+
height / Math.max(radii[1] + radii[2], 1e-12),
|
|
2503
|
+
width / Math.max(radii[2] + radii[3], 1e-12),
|
|
2504
|
+
height / Math.max(radii[3] + radii[0], 1e-12)
|
|
2505
|
+
);
|
|
2506
|
+
if (factor < 1) radii = radii.map((r) => r * factor);
|
|
2507
|
+
return radii;
|
|
2508
|
+
}
|
|
2509
|
+
function polygonVertices(count, inner, width, height) {
|
|
2510
|
+
const raw = [];
|
|
2511
|
+
const total = inner === null ? count : count * 2;
|
|
2512
|
+
for (let k = 0; k < total; k++) {
|
|
2513
|
+
const angle = -Math.PI / 2 + k * 2 * Math.PI / total;
|
|
2514
|
+
const r = inner !== null && k % 2 === 1 ? inner : 1;
|
|
2515
|
+
raw.push([r * Math.cos(angle), r * Math.sin(angle)]);
|
|
2516
|
+
}
|
|
2517
|
+
let minX = Infinity;
|
|
2518
|
+
let minY = Infinity;
|
|
2519
|
+
let maxX = -Infinity;
|
|
2520
|
+
let maxY = -Infinity;
|
|
2521
|
+
for (const [x, y] of raw) {
|
|
2522
|
+
minX = Math.min(minX, x);
|
|
2523
|
+
minY = Math.min(minY, y);
|
|
2524
|
+
maxX = Math.max(maxX, x);
|
|
2525
|
+
maxY = Math.max(maxY, y);
|
|
2526
|
+
}
|
|
2527
|
+
const sx = width / (maxX - minX || 1);
|
|
2528
|
+
const sy = height / (maxY - minY || 1);
|
|
2529
|
+
return raw.map(([x, y]) => [(x - minX) * sx, (y - minY) * sy]);
|
|
2530
|
+
}
|
|
2531
|
+
function rotationTransform(shape, size) {
|
|
2532
|
+
const deg = _nullishCoalesce(shape.rotation, () => ( 0));
|
|
2533
|
+
if (!deg || !Number.isFinite(deg)) return null;
|
|
2534
|
+
const rad = deg * Math.PI / 180;
|
|
2535
|
+
const cos = Math.cos(rad);
|
|
2536
|
+
const sin = Math.sin(rad);
|
|
2537
|
+
const cx = size.width / 2;
|
|
2538
|
+
const cy = size.height / 2;
|
|
2539
|
+
return ([x, y]) => [cx + (x - cx) * cos - (y - cy) * sin, cy + (x - cx) * sin + (y - cy) * cos];
|
|
2540
|
+
}
|
|
2541
|
+
function getShapeSvg(shape, size) {
|
|
2542
|
+
const { width: w, height: h } = size;
|
|
2543
|
+
const f = (n) => formatNumber(n, 3);
|
|
2544
|
+
const transforms = [];
|
|
2545
|
+
if (shape.rotation) transforms.push(`rotate(${f(shape.rotation)} ${f(w / 2)} ${f(h / 2)})`);
|
|
2546
|
+
let d = "";
|
|
2547
|
+
let fillRule = "nonzero";
|
|
2548
|
+
switch (shape.kind) {
|
|
2549
|
+
case "rect": {
|
|
2550
|
+
const [tl, tr, br, bl] = cornerRadii(shape, w, h);
|
|
2551
|
+
if (tl + tr + br + bl === 0) {
|
|
2552
|
+
d = `M0 0H${f(w)}V${f(h)}H0Z`;
|
|
2553
|
+
} else {
|
|
2554
|
+
const arc = (r, x, y) => r > 0 ? `A${f(r)} ${f(r)} 0 0 1 ${f(x)} ${f(y)}` : "";
|
|
2555
|
+
d = `M${f(tl)} 0H${f(w - tr)}${arc(tr, w, tr)}V${f(h - br)}${arc(br, w - br, h)}H${f(bl)}${arc(bl, 0, h - bl)}V${f(tl)}${arc(tl, tl, 0)}Z`;
|
|
2556
|
+
}
|
|
2557
|
+
break;
|
|
2558
|
+
}
|
|
2559
|
+
case "ellipse": {
|
|
2560
|
+
const rx = w / 2;
|
|
2561
|
+
const ry = h / 2;
|
|
2562
|
+
d = `M0 ${f(ry)}A${f(rx)} ${f(ry)} 0 1 1 ${f(w)} ${f(ry)}A${f(rx)} ${f(ry)} 0 1 1 0 ${f(ry)}Z`;
|
|
2563
|
+
break;
|
|
2564
|
+
}
|
|
2565
|
+
case "polygon":
|
|
2566
|
+
case "star": {
|
|
2567
|
+
const points = shape.kind === "polygon" ? polygonVertices(Math.max(3, Math.floor(_nullishCoalesce(shape.sides, () => ( 6)))), null, w, h) : polygonVertices(Math.max(3, Math.floor(_nullishCoalesce(shape.points, () => ( 5)))), clampRatio(shape.innerRadius), w, h);
|
|
2568
|
+
d = ringsToPath([points], f);
|
|
2569
|
+
break;
|
|
2570
|
+
}
|
|
2571
|
+
case "path": {
|
|
2572
|
+
d = shape.d;
|
|
2573
|
+
fillRule = _nullishCoalesce(shape.fillRule, () => ( "nonzero"));
|
|
2574
|
+
const vb = validViewBox(shape.viewBox);
|
|
2575
|
+
if (vb) {
|
|
2576
|
+
transforms.push(
|
|
2577
|
+
`scale(${f(w / vb[2])} ${f(h / vb[3])})`,
|
|
2578
|
+
`translate(${f(-vb[0])} ${f(-vb[1])})`
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
break;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
return transforms.length > 0 ? { d, transform: transforms.join(" "), fillRule } : { d, fillRule };
|
|
2585
|
+
}
|
|
2586
|
+
function getShapeRenderData(shape, size = {}) {
|
|
2587
|
+
const vb = shape.kind === "path" ? validViewBox(shape.viewBox) : null;
|
|
2588
|
+
const width = size.width && size.width > 0 ? size.width : vb ? vb[2] : 100;
|
|
2589
|
+
const height = size.height && size.height > 0 ? size.height : vb ? vb[3] : 100;
|
|
2590
|
+
const svg = getShapeSvg(shape, { width, height });
|
|
2591
|
+
const data = {
|
|
2592
|
+
viewBox: `0 0 ${formatNumber(width, 3)} ${formatNumber(height, 3)}`,
|
|
2593
|
+
width,
|
|
2594
|
+
height,
|
|
2595
|
+
d: svg.d,
|
|
2596
|
+
fillRule: svg.fillRule,
|
|
2597
|
+
fill: _nullishCoalesce(shape.fill, () => ( "currentColor")),
|
|
2598
|
+
stroke: _nullishCoalesce(shape.stroke, () => ( "none")),
|
|
2599
|
+
strokeWidth: _nullishCoalesce(shape.strokeWidth, () => ( 1))
|
|
2600
|
+
};
|
|
2601
|
+
if (svg.transform) data.transform = svg.transform;
|
|
2602
|
+
if (shape.opacity !== void 0) data.opacity = shape.opacity;
|
|
2603
|
+
return data;
|
|
2604
|
+
}
|
|
2605
|
+
function clampRatio(value) {
|
|
2606
|
+
if (value === void 0 || !Number.isFinite(value)) return 0.5;
|
|
2607
|
+
return Math.min(1, Math.max(0.01, value));
|
|
2608
|
+
}
|
|
2609
|
+
function validViewBox(vb) {
|
|
2610
|
+
if (!vb || vb.length !== 4 || !vb.every(Number.isFinite) || vb[2] <= 0 || vb[3] <= 0) return null;
|
|
2611
|
+
return vb;
|
|
2612
|
+
}
|
|
2613
|
+
function shapeToPolygon(shape, size, options = {}) {
|
|
2614
|
+
const { width: w, height: h } = size;
|
|
2615
|
+
if (!(w > 0) || !(h > 0)) return { rings: [], fillRule: "nonzero" };
|
|
2616
|
+
let rings;
|
|
2617
|
+
let fillRule = "nonzero";
|
|
2618
|
+
if (shape.kind === "path") {
|
|
2619
|
+
fillRule = _nullishCoalesce(shape.fillRule, () => ( "nonzero"));
|
|
2620
|
+
const vb = validViewBox(shape.viewBox);
|
|
2621
|
+
if (vb) {
|
|
2622
|
+
const sx = w / vb[2];
|
|
2623
|
+
const sy = h / vb[3];
|
|
2624
|
+
const tolerance = (_nullishCoalesce(options.tolerance, () => ( 0.25))) / Math.max(sx, sy);
|
|
2625
|
+
rings = flattenSvgPath(shape.d, { ...options, tolerance }).map(
|
|
2626
|
+
(ring) => ring.map(([x, y]) => [(x - vb[0]) * sx, (y - vb[1]) * sy])
|
|
2627
|
+
);
|
|
2628
|
+
} else {
|
|
2629
|
+
rings = flattenSvgPath(shape.d, options);
|
|
2630
|
+
}
|
|
2631
|
+
} else {
|
|
2632
|
+
const { d } = getShapeSvg({ ...shape, rotation: 0 }, size);
|
|
2633
|
+
rings = flattenSvgPath(d, options);
|
|
2634
|
+
}
|
|
2635
|
+
const rotate = rotationTransform(shape, size);
|
|
2636
|
+
if (rotate) rings = rings.map((ring) => ring.map(rotate));
|
|
2637
|
+
return { rings, fillRule };
|
|
2638
|
+
}
|
|
2639
|
+
function formatNumber(value, precision = 3) {
|
|
2640
|
+
const factor = 10 ** precision;
|
|
2641
|
+
const rounded = Math.round(value * factor) / factor;
|
|
2642
|
+
return Object.is(rounded, -0) ? "0" : String(rounded);
|
|
2643
|
+
}
|
|
2644
|
+
function ringsToPath(rings, f) {
|
|
2645
|
+
let d = "";
|
|
2646
|
+
for (const ring of rings) {
|
|
2647
|
+
if (ring.length < 2) continue;
|
|
2648
|
+
d += `M${f(ring[0][0])} ${f(ring[0][1])}`;
|
|
2649
|
+
for (let i = 1; i < ring.length; i++) d += `L${f(ring[i][0])} ${f(ring[i][1])}`;
|
|
2650
|
+
d += "Z";
|
|
2651
|
+
}
|
|
2652
|
+
return d;
|
|
2653
|
+
}
|
|
2654
|
+
function segmentDistance(p, a, b) {
|
|
2655
|
+
const dx = b[0] - a[0];
|
|
2656
|
+
const dy = b[1] - a[1];
|
|
2657
|
+
const len2 = dx * dx + dy * dy;
|
|
2658
|
+
if (len2 === 0) return Math.hypot(p[0] - a[0], p[1] - a[1]);
|
|
2659
|
+
const t = Math.max(0, Math.min(1, ((p[0] - a[0]) * dx + (p[1] - a[1]) * dy) / len2));
|
|
2660
|
+
return Math.hypot(p[0] - (a[0] + t * dx), p[1] - (a[1] + t * dy));
|
|
2661
|
+
}
|
|
2662
|
+
function rdp(points, tolerance) {
|
|
2663
|
+
if (points.length <= 2) return points;
|
|
2664
|
+
const keep = new Uint8Array(points.length);
|
|
2665
|
+
keep[0] = 1;
|
|
2666
|
+
keep[points.length - 1] = 1;
|
|
2667
|
+
const stack = [[0, points.length - 1]];
|
|
2668
|
+
while (stack.length) {
|
|
2669
|
+
const [s, e] = stack.pop();
|
|
2670
|
+
let maxDist = -1;
|
|
2671
|
+
let index = -1;
|
|
2672
|
+
for (let i = s + 1; i < e; i++) {
|
|
2673
|
+
const dist = segmentDistance(points[i], points[s], points[e]);
|
|
2674
|
+
if (dist > maxDist) {
|
|
2675
|
+
maxDist = dist;
|
|
2676
|
+
index = i;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
if (index !== -1 && maxDist > tolerance) {
|
|
2680
|
+
keep[index] = 1;
|
|
2681
|
+
stack.push([s, index], [index, e]);
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
return points.filter((_, i) => keep[i]);
|
|
2685
|
+
}
|
|
2686
|
+
function simplifyRing(ring, tolerance) {
|
|
2687
|
+
if (ring.length <= 3 || !(tolerance > 0)) return [...ring];
|
|
2688
|
+
let far = 0;
|
|
2689
|
+
let farDist = -1;
|
|
2690
|
+
for (let i = 1; i < ring.length; i++) {
|
|
2691
|
+
const dist = Math.hypot(ring[i][0] - ring[0][0], ring[i][1] - ring[0][1]);
|
|
2692
|
+
if (dist > farDist) {
|
|
2693
|
+
farDist = dist;
|
|
2694
|
+
far = i;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
const first = rdp(ring.slice(0, far + 1), tolerance);
|
|
2698
|
+
const second = rdp([...ring.slice(far), ring[0]], tolerance);
|
|
2699
|
+
const out = [...first.slice(0, -1), ...second.slice(0, -1)];
|
|
2700
|
+
return out.length >= 3 ? out : [];
|
|
2701
|
+
}
|
|
2702
|
+
function polygonToSvgPath(input, options = {}) {
|
|
2703
|
+
const precision = _nullishCoalesce(options.precision, () => ( 3));
|
|
2704
|
+
const f = (n) => formatNumber(n, precision);
|
|
2705
|
+
const dx = _nullishCoalesce(_optionalChain([options, 'access', _55 => _55.offset, 'optionalAccess', _56 => _56.x]), () => ( 0));
|
|
2706
|
+
const dy = _nullishCoalesce(_optionalChain([options, 'access', _57 => _57.offset, 'optionalAccess', _58 => _58.y]), () => ( 0));
|
|
2707
|
+
const rings = [];
|
|
2708
|
+
for (const item of input) {
|
|
2709
|
+
const list = item.length > 0 && Array.isArray(item[0]) && Array.isArray(item[0][0]) ? item : [item];
|
|
2710
|
+
for (const ring of list) {
|
|
2711
|
+
let r = ring;
|
|
2712
|
+
if (options.simplify && options.simplify > 0) r = simplifyRing(r, options.simplify);
|
|
2713
|
+
if (r.length < 3) continue;
|
|
2714
|
+
rings.push(dx || dy ? r.map(([x, y]) => [x + dx, y + dy]) : r);
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
return ringsToPath(rings, f);
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
// src/canvas/shapeNodes.ts
|
|
2721
|
+
var SHAPE_DATA_KEY = "shape";
|
|
2722
|
+
var SHAPE_NODE_TYPE = "shape";
|
|
2723
|
+
var SHAPE_OPERATIONS = ["unite", "intersect", "subtract", "exclude", "divide"];
|
|
2724
|
+
var BOOLEAN_FOR = {
|
|
2725
|
+
unite: "union",
|
|
2726
|
+
intersect: "intersection",
|
|
2727
|
+
subtract: "difference",
|
|
2728
|
+
exclude: "xor"
|
|
2729
|
+
};
|
|
2730
|
+
function isShapeNode(node) {
|
|
2731
|
+
return !!node && isShapeDescription(_optionalChain([node, 'access', _59 => _59.data, 'optionalAccess', _60 => _60[SHAPE_DATA_KEY]]));
|
|
2732
|
+
}
|
|
2733
|
+
function getNodeShape(node) {
|
|
2734
|
+
const shape = _optionalChain([node, 'optionalAccess', _61 => _61.data, 'optionalAccess', _62 => _62[SHAPE_DATA_KEY]]);
|
|
2735
|
+
return isShapeDescription(shape) ? shape : null;
|
|
2736
|
+
}
|
|
2737
|
+
function shapeNodeToPolygon(node, lookup, options = {}) {
|
|
2738
|
+
const shape = getNodeShape(node);
|
|
2739
|
+
if (!shape) return null;
|
|
2740
|
+
const size = getNodeSize(node, options);
|
|
2741
|
+
const origin = node.parentId && lookup ? getAbsolutePosition(node, lookup) : node.position;
|
|
2742
|
+
const { rings, fillRule } = shapeToPolygon(shape, size, { tolerance: options.tolerance });
|
|
2743
|
+
return {
|
|
2744
|
+
rings: rings.map((ring) => ring.map(([x, y]) => [x + origin.x, y + origin.y])),
|
|
2745
|
+
fillRule
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
var idCounter = 0;
|
|
2749
|
+
function defaultId2(index) {
|
|
2750
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
2751
|
+
idCounter += 1;
|
|
2752
|
+
return `shape-${Date.now().toString(36)}-${idCounter}-${index}`;
|
|
2753
|
+
}
|
|
2754
|
+
function stackOrder(nodes, ids) {
|
|
2755
|
+
return nodes.map((node, index) => ({ node, index, z: typeof node.zIndex === "number" ? node.zIndex : 0 })).filter((entry) => ids.has(entry.node.id)).sort((a, b) => a.z - b.z || a.index - b.index).map((entry) => entry.node);
|
|
2756
|
+
}
|
|
2757
|
+
function canCombineShapeNodes(nodes, ids) {
|
|
2758
|
+
const idSet = new Set(ids);
|
|
2759
|
+
const shapes = nodes.filter((n) => idSet.has(n.id) && isShapeNode(n));
|
|
2760
|
+
if (shapes.some((n) => isLocked(n))) return { ok: false, reason: "locked" };
|
|
2761
|
+
if (shapes.length < 2) return { ok: false, reason: "too-few" };
|
|
2762
|
+
return { ok: true, shapeIds: shapes.map((n) => n.id) };
|
|
2763
|
+
}
|
|
2764
|
+
function combineShapeNodes(nodes, ids, operation, options = {}) {
|
|
2765
|
+
const idList = [...ids];
|
|
2766
|
+
const check = canCombineShapeNodes(nodes, idList);
|
|
2767
|
+
if (!check.ok) return { ok: false, operation, reason: check.reason };
|
|
2768
|
+
const lookup = createNodeLookup(nodes);
|
|
2769
|
+
const shapeIds = new Set(check.shapeIds);
|
|
2770
|
+
const ordered = stackOrder(nodes, shapeIds);
|
|
2771
|
+
const operands = ordered.map((node) => shapeNodeToPolygon(node, lookup, options));
|
|
2772
|
+
const parentIds = new Set(ordered.map((n) => _nullishCoalesce(n.parentId, () => ( ""))));
|
|
2773
|
+
const sharedParent = parentIds.size === 1 ? ordered[0].parentId : void 0;
|
|
2774
|
+
const parentOrigin = sharedParent && lookup.get(sharedParent) ? getAbsolutePosition(lookup.get(sharedParent), lookup) : { x: 0, y: 0 };
|
|
2775
|
+
const pickSource = () => {
|
|
2776
|
+
const style = _nullishCoalesce(options.styleFrom, () => ( "bottom"));
|
|
2777
|
+
if (style === "top") return ordered[ordered.length - 1];
|
|
2778
|
+
if (style === "first") {
|
|
2779
|
+
const first = idList.find((id) => shapeIds.has(id));
|
|
2780
|
+
return first && lookup.get(first) || ordered[0];
|
|
2781
|
+
}
|
|
2782
|
+
if (typeof style === "object") return shapeIds.has(style.id) && lookup.get(style.id) || ordered[0];
|
|
2783
|
+
return ordered[0];
|
|
2784
|
+
};
|
|
2785
|
+
const createId = _nullishCoalesce(options.createId, () => ( defaultId2));
|
|
2786
|
+
const build = (polygon, source, index) => {
|
|
2787
|
+
const bounds = multiPolygonBounds(polygon);
|
|
2788
|
+
if (!bounds) return null;
|
|
2789
|
+
const width = bounds.maxX - bounds.minX;
|
|
2790
|
+
const height = bounds.maxY - bounds.minY;
|
|
2791
|
+
if (!(width > 0) || !(height > 0)) return null;
|
|
2792
|
+
const d = polygonToSvgPath(polygon, {
|
|
2793
|
+
precision: options.precision,
|
|
2794
|
+
simplify: options.simplify,
|
|
2795
|
+
offset: { x: -bounds.minX, y: -bounds.minY }
|
|
2796
|
+
});
|
|
2797
|
+
if (!d) return null;
|
|
2798
|
+
const sourceShape = getNodeShape(source);
|
|
2799
|
+
const shape = {
|
|
2800
|
+
kind: "path",
|
|
2801
|
+
d,
|
|
2802
|
+
viewBox: [0, 0, roundSize(width), roundSize(height)],
|
|
2803
|
+
fillRule: "nonzero"
|
|
2804
|
+
};
|
|
2805
|
+
for (const key of ["fill", "stroke", "strokeWidth", "opacity"]) {
|
|
2806
|
+
if (sourceShape[key] !== void 0) shape[key] = sourceShape[key];
|
|
2807
|
+
}
|
|
2808
|
+
const {
|
|
2809
|
+
[LOCKED_DATA_KEY]: _locked,
|
|
2810
|
+
[LOCKED_PREVIOUS_FLAGS_KEY]: _previous,
|
|
2811
|
+
...data
|
|
2812
|
+
} = _nullishCoalesce(source.data, () => ( {}));
|
|
2813
|
+
const {
|
|
2814
|
+
measured: _measured,
|
|
2815
|
+
selected: _selected,
|
|
2816
|
+
dragging: _dragging,
|
|
2817
|
+
positionAbsolute: _positionAbsolute,
|
|
2818
|
+
internals: _internals,
|
|
2819
|
+
parentId: _parentId,
|
|
2820
|
+
...rest
|
|
2821
|
+
} = source;
|
|
2822
|
+
const node = {
|
|
2823
|
+
...rest,
|
|
2824
|
+
id: createId(index),
|
|
2825
|
+
position: { x: bounds.minX - parentOrigin.x, y: bounds.minY - parentOrigin.y },
|
|
2826
|
+
width,
|
|
2827
|
+
height,
|
|
2828
|
+
data: { ...data, [SHAPE_DATA_KEY]: shape }
|
|
2829
|
+
};
|
|
2830
|
+
if (sharedParent) node.parentId = sharedParent;
|
|
2831
|
+
if (options.nodeType !== void 0) node.type = options.nodeType;
|
|
2832
|
+
if (source.style) node.style = withStyleSize(source.style, { width, height });
|
|
2833
|
+
if (options.select !== false) node.selected = true;
|
|
2834
|
+
return node;
|
|
2835
|
+
};
|
|
2836
|
+
const created = [];
|
|
2837
|
+
if (operation === "divide") {
|
|
2838
|
+
const pieces = dividePolygons(operands, { epsilon: options.epsilon });
|
|
2839
|
+
pieces.forEach((piece) => {
|
|
2840
|
+
const source = ordered[piece.operands[piece.operands.length - 1]];
|
|
2841
|
+
const node = build([piece.polygon], source, created.length);
|
|
2842
|
+
if (node) created.push(node);
|
|
2843
|
+
});
|
|
2844
|
+
} else {
|
|
2845
|
+
const result = polygonBoolean(BOOLEAN_FOR[operation], operands, { epsilon: options.epsilon });
|
|
2846
|
+
const node = build(result, pickSource(), 0);
|
|
2847
|
+
if (node) created.push(node);
|
|
2848
|
+
}
|
|
2849
|
+
if (created.length === 0) return { ok: false, operation, reason: "empty" };
|
|
2850
|
+
return { ok: true, operation, removeIds: ordered.map((n) => n.id), nodes: created };
|
|
2851
|
+
}
|
|
2852
|
+
function roundSize(value) {
|
|
2853
|
+
return Math.round(value * 1e3) / 1e3;
|
|
2854
|
+
}
|
|
2855
|
+
function applyShapeCombination(nodes, change, options = {}) {
|
|
2856
|
+
const remove = new Set(change.removeIds);
|
|
2857
|
+
const insertAt = nodes.findIndex((n) => remove.has(n.id));
|
|
2858
|
+
const deselect = options.deselect !== false && change.nodes.some((n) => n.selected);
|
|
2859
|
+
const out = [];
|
|
2860
|
+
nodes.forEach((node, index) => {
|
|
2861
|
+
if (index === insertAt) out.push(...change.nodes);
|
|
2862
|
+
if (remove.has(node.id)) return;
|
|
2863
|
+
out.push(deselect && node.selected ? { ...node, selected: false } : node);
|
|
2864
|
+
});
|
|
2865
|
+
if (insertAt === -1) out.push(...change.nodes);
|
|
2866
|
+
return out;
|
|
2867
|
+
}
|
|
2868
|
+
function revertShapeCombination(nodes, change, previous) {
|
|
2869
|
+
const created = new Set(change.nodes.map((n) => n.id));
|
|
2870
|
+
const removed = new Set(change.removeIds);
|
|
2871
|
+
const out = nodes.filter((n) => !created.has(n.id));
|
|
2872
|
+
previous.forEach((node, index) => {
|
|
2873
|
+
if (!removed.has(node.id) || out.some((n) => n.id === node.id)) return;
|
|
2874
|
+
out.splice(Math.min(index, out.length), 0, node);
|
|
2875
|
+
});
|
|
2876
|
+
return out;
|
|
2877
|
+
}
|
|
2878
|
+
|
|
1708
2879
|
// src/canvas/arrangeController.ts
|
|
1709
2880
|
function createCanvasArrangeController(options) {
|
|
1710
2881
|
let guides = [];
|
|
1711
2882
|
let dragIndex = null;
|
|
1712
2883
|
let dragKey = "";
|
|
1713
|
-
|
|
2884
|
+
let lastSnapped = /* @__PURE__ */ new Map();
|
|
2885
|
+
const selectedIds = (ids) => ids ? [...ids] : _nullishCoalesce(_optionalChain([options, 'access', _63 => _63.getSelectedIds, 'optionalCall', _64 => _64()]), () => ( options.getNodes().filter((n) => n.selected).map((n) => n.id)));
|
|
1714
2886
|
const setGuides = (next) => {
|
|
1715
2887
|
if (next.length === 0 && guides.length === 0) return;
|
|
1716
2888
|
guides = next;
|
|
1717
|
-
_optionalChain([options, 'access',
|
|
2889
|
+
_optionalChain([options, 'access', _65 => _65.onGuidesChange, 'optionalCall', _66 => _66(guides)]);
|
|
1718
2890
|
};
|
|
1719
2891
|
function apply(name, updates) {
|
|
1720
2892
|
if (updates.length === 0) return updates;
|
|
@@ -1777,6 +2949,30 @@ function createCanvasArrangeController(options) {
|
|
|
1777
2949
|
return anyUnlocked ? controller.lock(list) : controller.unlock(list);
|
|
1778
2950
|
},
|
|
1779
2951
|
isLocked: (id) => isLocked(options.getNodes().find((n) => n.id === id)),
|
|
2952
|
+
/**
|
|
2953
|
+
* Freeform shape operation (Unite / Intersect / Subtract / Exclude / Divide) on the
|
|
2954
|
+
* shape nodes among `ids` (default: the selection). Replaces them with the result as
|
|
2955
|
+
* one undoable step. Refuses without changing anything when a selected shape is
|
|
2956
|
+
* locked, fewer than two shapes are selected, or the result is empty.
|
|
2957
|
+
*/
|
|
2958
|
+
combineShapes(operation, ids, combineOptions = {}) {
|
|
2959
|
+
const before = options.getNodes();
|
|
2960
|
+
const result = combineShapeNodes(before, selectedIds(ids), operation, { ...rect(), ...combineOptions });
|
|
2961
|
+
if (!result.ok) return result;
|
|
2962
|
+
const name = `${operation[0].toUpperCase()}${operation.slice(1)} Shapes`;
|
|
2963
|
+
const run = () => options.setNodes(applyShapeCombination(options.getNodes(), result));
|
|
2964
|
+
const undo = () => options.setNodes(revertShapeCombination(options.getNodes(), result, before));
|
|
2965
|
+
if (options.history) options.history.execute({ name, execute: run, undo });
|
|
2966
|
+
else run();
|
|
2967
|
+
return result;
|
|
2968
|
+
},
|
|
2969
|
+
unite: (ids, o) => controller.combineShapes("unite", ids, o),
|
|
2970
|
+
intersect: (ids, o) => controller.combineShapes("intersect", ids, o),
|
|
2971
|
+
subtract: (ids, o) => controller.combineShapes("subtract", ids, o),
|
|
2972
|
+
exclude: (ids, o) => controller.combineShapes("exclude", ids, o),
|
|
2973
|
+
divide: (ids, o) => controller.combineShapes("divide", ids, o),
|
|
2974
|
+
/** Whether the shape operations can run on `ids` (default: the selection). */
|
|
2975
|
+
canCombineShapes: (ids) => canCombineShapeNodes(options.getNodes(), selectedIds(ids)).ok,
|
|
1780
2976
|
/**
|
|
1781
2977
|
* Runs a shortcut action. Scene actions are not handled here (see the scenes store);
|
|
1782
2978
|
* returns `false` for them and for unknown actions.
|
|
@@ -1848,11 +3044,13 @@ function createCanvasArrangeController(options) {
|
|
|
1848
3044
|
const result = snapNodeDrag(options.getNodes(), list, {
|
|
1849
3045
|
...rect(),
|
|
1850
3046
|
...options.guides,
|
|
1851
|
-
zoom: _nullishCoalesce(_optionalChain([options, 'access',
|
|
3047
|
+
zoom: _nullishCoalesce(_optionalChain([options, 'access', _67 => _67.getZoom, 'optionalCall', _68 => _68()]), () => ( 1)),
|
|
1852
3048
|
index: dragIndex
|
|
1853
3049
|
});
|
|
3050
|
+
lastSnapped = /* @__PURE__ */ new Map();
|
|
1854
3051
|
if (result.updates.length > 0) {
|
|
1855
3052
|
options.setNodes(applyNodeUpdates(options.getNodes(), result.updates));
|
|
3053
|
+
for (const u of result.updates) if (u.position) lastSnapped.set(u.id, u.position);
|
|
1856
3054
|
}
|
|
1857
3055
|
setGuides(result.guides);
|
|
1858
3056
|
return result;
|
|
@@ -1863,10 +3061,17 @@ function createCanvasArrangeController(options) {
|
|
|
1863
3061
|
* A batch without a dragging position change ends the drag and clears the guides.
|
|
1864
3062
|
*/
|
|
1865
3063
|
snapPositionChanges(changes) {
|
|
1866
|
-
const dragging = changes.filter(
|
|
3064
|
+
const dragging = changes.filter(
|
|
3065
|
+
(c) => c.type === "position" && typeof c.id === "string" && !!c.position && !!c.dragging
|
|
3066
|
+
);
|
|
1867
3067
|
if (dragging.length === 0) {
|
|
1868
|
-
if (changes.some((c) => c.type === "position" && c.dragging === false))
|
|
1869
|
-
|
|
3068
|
+
if (!changes.some((c) => c.type === "position" && c.dragging === false)) return changes;
|
|
3069
|
+
const snapped = lastSnapped;
|
|
3070
|
+
lastSnapped = /* @__PURE__ */ new Map();
|
|
3071
|
+
controller.endDrag();
|
|
3072
|
+
const keep = (c) => c.type === "position" && c.dragging === false && !!c.position && c.id !== void 0 && snapped.has(c.id);
|
|
3073
|
+
if (!changes.some(keep)) return changes;
|
|
3074
|
+
return changes.map((c) => keep(c) ? { ...c, position: snapped.get(c.id) } : c);
|
|
1870
3075
|
}
|
|
1871
3076
|
if (options.snapping === false) return changes;
|
|
1872
3077
|
const ids = dragging.map((c) => c.id);
|
|
@@ -1877,23 +3082,42 @@ function createCanvasArrangeController(options) {
|
|
|
1877
3082
|
const result = snapNodeDrag(nodes, ids, {
|
|
1878
3083
|
...rect(),
|
|
1879
3084
|
...options.guides,
|
|
1880
|
-
zoom: _nullishCoalesce(_optionalChain([options, 'access',
|
|
3085
|
+
zoom: _nullishCoalesce(_optionalChain([options, 'access', _69 => _69.getZoom, 'optionalCall', _70 => _70()]), () => ( 1)),
|
|
1881
3086
|
index: dragIndex
|
|
1882
3087
|
});
|
|
1883
3088
|
setGuides(result.guides);
|
|
1884
3089
|
const { x: dx, y: dy } = result.offset;
|
|
3090
|
+
lastSnapped = /* @__PURE__ */ new Map();
|
|
1885
3091
|
if (dx === 0 && dy === 0) return changes;
|
|
1886
3092
|
const parents = new Map(nodes.map((n) => [n.id, n.parentId]));
|
|
1887
3093
|
const shift = new Set(ids.filter((id) => !proposed.has(_nullishCoalesce(parents.get(id), () => ( "")))));
|
|
1888
|
-
return changes.map(
|
|
1889
|
-
(c
|
|
1890
|
-
|
|
3094
|
+
return changes.map((c) => {
|
|
3095
|
+
if (!(c.type === "position" && c.position && c.dragging && c.id !== void 0 && shift.has(c.id))) return c;
|
|
3096
|
+
const position = { x: c.position.x + dx, y: c.position.y + dy };
|
|
3097
|
+
lastSnapped.set(c.id, position);
|
|
3098
|
+
return { ...c, position };
|
|
3099
|
+
});
|
|
1891
3100
|
},
|
|
1892
|
-
/**
|
|
3101
|
+
/**
|
|
3102
|
+
* Clears guides and the cached snap targets. Call it from the flow's drag-stop
|
|
3103
|
+
* handler: flow libraries write the raw pointer position when a drag ends, so the
|
|
3104
|
+
* last snapped positions from `drag` are written back here.
|
|
3105
|
+
*/
|
|
1893
3106
|
endDrag() {
|
|
3107
|
+
const snapped = lastSnapped;
|
|
1894
3108
|
dragIndex = null;
|
|
1895
3109
|
dragKey = "";
|
|
3110
|
+
lastSnapped = /* @__PURE__ */ new Map();
|
|
1896
3111
|
setGuides([]);
|
|
3112
|
+
if (snapped.size === 0) return;
|
|
3113
|
+
const nodes = options.getNodes();
|
|
3114
|
+
const drift = nodes.some((n) => {
|
|
3115
|
+
const p = snapped.get(n.id);
|
|
3116
|
+
return !!p && (n.position.x !== p.x || n.position.y !== p.y);
|
|
3117
|
+
});
|
|
3118
|
+
if (drift) {
|
|
3119
|
+
options.setNodes(applyNodeUpdates(nodes, [...snapped].map(([id, position]) => ({ id, position }))));
|
|
3120
|
+
}
|
|
1897
3121
|
}
|
|
1898
3122
|
};
|
|
1899
3123
|
return controller;
|
|
@@ -1978,5 +3202,37 @@ function createCanvasArrangeController(options) {
|
|
|
1978
3202
|
|
|
1979
3203
|
|
|
1980
3204
|
|
|
1981
|
-
|
|
3205
|
+
|
|
3206
|
+
|
|
3207
|
+
|
|
3208
|
+
|
|
3209
|
+
|
|
3210
|
+
|
|
3211
|
+
|
|
3212
|
+
|
|
3213
|
+
|
|
3214
|
+
|
|
3215
|
+
|
|
3216
|
+
|
|
3217
|
+
|
|
3218
|
+
|
|
3219
|
+
|
|
3220
|
+
|
|
3221
|
+
|
|
3222
|
+
|
|
3223
|
+
|
|
3224
|
+
|
|
3225
|
+
|
|
3226
|
+
|
|
3227
|
+
|
|
3228
|
+
|
|
3229
|
+
|
|
3230
|
+
|
|
3231
|
+
|
|
3232
|
+
|
|
3233
|
+
|
|
3234
|
+
|
|
3235
|
+
|
|
3236
|
+
|
|
3237
|
+
exports.CONNECTION_LINE_TYPES = CONNECTION_LINE_TYPES; exports.CanvasScenesStore = CanvasScenesStore; exports.DEFAULT_ARRANGE_SHORTCUTS = DEFAULT_ARRANGE_SHORTCUTS; exports.DEFAULT_NODE_HEIGHT = _chunkWADCJ7A7cjs.DEFAULT_NODE_HEIGHT; exports.DEFAULT_NODE_WIDTH = _chunkWADCJ7A7cjs.DEFAULT_NODE_WIDTH; exports.DEFAULT_PLACEMENT_GAP = _chunkWADCJ7A7cjs.DEFAULT_PLACEMENT_GAP; exports.LOCKED_DATA_KEY = LOCKED_DATA_KEY; exports.LOCKED_PREVIOUS_FLAGS_KEY = LOCKED_PREVIOUS_FLAGS_KEY; exports.ManualClock = ManualClock; exports.SHAPE_DATA_KEY = SHAPE_DATA_KEY; exports.SHAPE_KINDS = SHAPE_KINDS; exports.SHAPE_NODE_TYPE = SHAPE_NODE_TYPE; exports.SHAPE_OPERATIONS = SHAPE_OPERATIONS; exports.StreamingLayoutManager = StreamingLayoutManager; exports.alignNodes = alignNodes; exports.applyNodeUpdates = applyNodeUpdates; exports.applyShapeCombination = applyShapeCombination; exports.blendLayout = blendLayout; exports.boundsOf = _chunkWADCJ7A7cjs.boundsOf; exports.calculateDagreReflowDisplacement = calculateDagreReflowDisplacement; exports.calculateDirectionalDisplacement = calculateDirectionalDisplacement; exports.canCombineShapeNodes = canCombineShapeNodes; exports.canvasHeight = canvasHeight; exports.centerNodes = _chunkWADCJ7A7cjs.centerNodes; exports.centre = _chunkWADCJ7A7cjs.centre; exports.collectDescendantIds = collectDescendantIds; exports.combineShapeNodes = combineShapeNodes; exports.computeNodesBoundingBox = computeNodesBoundingBox; exports.computeSnapGuides = computeSnapGuides; exports.connectedOnly = connectedOnly; exports.createBezierTrajectory = createBezierTrajectory; exports.createCanvasArrangeController = createCanvasArrangeController; exports.createCanvasScenesStore = createCanvasScenesStore; exports.createFanOutTrajectory = createFanOutTrajectory; exports.createGuideIndex = createGuideIndex; exports.createNodeGuideIndex = createNodeGuideIndex; exports.createNodeLookup = createNodeLookup; exports.defaultEasing = defaultEasing; exports.distributeNodes = distributeNodes; exports.dividePolygons = dividePolygons; exports.ensureLayout = _chunkWADCJ7A7cjs.ensureLayout; exports.estimateCanvasHeight = estimateCanvasHeight; exports.estimateNodeSize = _chunkWADCJ7A7cjs.estimateNodeSize; exports.findConnectorSnap = findConnectorSnap; exports.fitViewportToNodes = fitViewportToNodes; exports.fitViewportToRect = fitViewportToRect; exports.flattenSvgPath = flattenSvgPath; exports.flowSide = _chunkWADCJ7A7cjs.flowSide; exports.formatNumber = formatNumber; exports.getAbsolutePosition = getAbsolutePosition; exports.getCenteredNodePosition = _chunkWADCJ7A7cjs.getCenteredNodePosition; exports.getLayoutedNodes = _chunkWADCJ7A7cjs.getLayoutedNodes; exports.getNodeRect = getNodeRect; exports.getNodeShape = getNodeShape; exports.getNodeSize = getNodeSize; exports.getShapeRenderData = getShapeRenderData; exports.getShapeSvg = getShapeSvg; exports.handlePoint = handlePoint; exports.intersectPolygons = intersectPolygons; exports.invertNodeUpdates = invertNodeUpdates; exports.isEventFromTextInput = isEventFromTextInput; exports.isLocked = isLocked; exports.isShapeDescription = isShapeDescription; exports.isShapeNode = isShapeNode; exports.isValidCanvasEdge = isValidCanvasEdge; exports.isValidCanvasNode = isValidCanvasNode; exports.isValidCanvasScene = isValidCanvasScene; exports.isValidUrl = isValidUrl; exports.layoutBounds = layoutBounds; exports.linearPositionTrajectory = linearPositionTrajectory; exports.linearTrajectory = linearTrajectory; exports.lockNodes = lockNodes; exports.matchArrangeShortcut = matchArrangeShortcut; exports.matchSize = matchSize; exports.multiPolygonArea = multiPolygonArea; exports.multiPolygonBounds = multiPolygonBounds; exports.nearestHandlePositions = nearestHandlePositions; exports.oppositePosition = oppositePosition; exports.overlaps = _chunkWADCJ7A7cjs.overlaps; exports.pageHidden = pageHidden; exports.parseCanvasClipboardData = parseCanvasClipboardData; exports.parseCanvasScenes = parseCanvasScenes; exports.parseSvgPath = parseSvgPath; exports.placeBeside = _chunkWADCJ7A7cjs.placeBeside; exports.pointInMultiPolygon = pointInMultiPolygon; exports.pointInRings = pointInRings; exports.polygonArea = polygonArea; exports.polygonBoolean = polygonBoolean; exports.polygonToSvgPath = polygonToSvgPath; exports.positionToSide = positionToSide; exports.readStyleSize = readStyleSize; exports.realClock = realClock; exports.remapPastedNodes = remapPastedNodes; exports.reorderNodes = reorderNodes; exports.repositionNodesTo = repositionNodesTo; exports.revertShapeCombination = revertShapeCombination; exports.ringSignedArea = ringSignedArea; exports.runMultiNodeTransition = runMultiNodeTransition; exports.serializeCanvasNodes = serializeCanvasNodes; exports.shapeNodeToPolygon = shapeNodeToPolygon; exports.shapeToPolygon = shapeToPolygon; exports.sideToPosition = sideToPosition; exports.simplifyRing = simplifyRing; exports.snapNodeDrag = snapNodeDrag; exports.subtractPolygons = subtractPolygons; exports.toggleLockNodes = toggleLockNodes; exports.unionPolygons = unionPolygons; exports.unlockNodes = unlockNodes; exports.withStyleOpacity = withStyleOpacity; exports.withStyleSize = withStyleSize; exports.xorPolygons = xorPolygons;
|
|
1982
3238
|
//# sourceMappingURL=index.cjs.map
|