@mce/bigesj 0.23.0 → 0.24.1

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.
Files changed (2) hide show
  1. package/dist/index.js +2452 -119
  2. package/package.json +4 -3
package/dist/index.js CHANGED
@@ -4,6 +4,29 @@ import { clearUndef, idGenerator, isGradient, isGradientFill, normalizeCRLF, nor
4
4
  import { OoxmlNode, parsePresetShapeDefinition } from "modern-openxml";
5
5
  import { assets } from "modern-canvas";
6
6
  import { gunzipSync } from "fflate";
7
+ //#region \0rolldown/runtime.js
8
+ var __create = Object.create;
9
+ var __defProp = Object.defineProperty;
10
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __getProtoOf = Object.getPrototypeOf;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
17
+ key = keys[i];
18
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
19
+ get: ((k) => from[k]).bind(null, key),
20
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
21
+ });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
26
+ value: mod,
27
+ enumerable: true
28
+ }) : target, mod));
29
+ //#endregion
7
30
  //#region src/composables/useFonts.ts
8
31
  var bigeFonts = ref([]);
9
32
  function levenshteinDistance(a, b) {
@@ -1657,8 +1680,1953 @@ function signedArea(data, start, end, dim) {
1657
1680
  }
1658
1681
  return sum;
1659
1682
  }
1683
+ /**
1684
+ * splaytree v3.1.2
1685
+ * Fast Splay tree for Node and browser
1686
+ *
1687
+ * @author Alexander Milevski <info@w8r.name>
1688
+ * @license MIT
1689
+ * @preserve
1690
+ */
1691
+ /*! *****************************************************************************
1692
+ Copyright (c) Microsoft Corporation. All rights reserved.
1693
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
1694
+ this file except in compliance with the License. You may obtain a copy of the
1695
+ License at http://www.apache.org/licenses/LICENSE-2.0
1696
+
1697
+ THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1698
+ KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
1699
+ WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
1700
+ MERCHANTABLITY OR NON-INFRINGEMENT.
1701
+
1702
+ See the Apache Version 2.0 License for specific language governing permissions
1703
+ and limitations under the License.
1704
+ ***************************************************************************** */
1660
1705
  //#endregion
1661
- //#region ../../node_modules/.pnpm/modern-path2d@1.7.0/node_modules/modern-path2d/dist/index.mjs
1706
+ //#region ../../node_modules/.pnpm/modern-path2d@1.8.5/node_modules/modern-path2d/dist/index.mjs
1707
+ var import_polygon_clipping_umd = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
1708
+ (function(global, factory) {
1709
+ typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.polygonClipping = factory());
1710
+ })(exports, (function() {
1711
+ "use strict";
1712
+ function __generator(thisArg, body) {
1713
+ var _ = {
1714
+ label: 0,
1715
+ sent: function() {
1716
+ if (t[0] & 1) throw t[1];
1717
+ return t[1];
1718
+ },
1719
+ trys: [],
1720
+ ops: []
1721
+ }, f, y, t, g;
1722
+ return g = {
1723
+ next: verb(0),
1724
+ "throw": verb(1),
1725
+ "return": verb(2)
1726
+ }, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
1727
+ return this;
1728
+ }), g;
1729
+ function verb(n) {
1730
+ return function(v) {
1731
+ return step([n, v]);
1732
+ };
1733
+ }
1734
+ function step(op) {
1735
+ if (f) throw new TypeError("Generator is already executing.");
1736
+ while (_) try {
1737
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
1738
+ if (y = 0, t) op = [op[0] & 2, t.value];
1739
+ switch (op[0]) {
1740
+ case 0:
1741
+ case 1:
1742
+ t = op;
1743
+ break;
1744
+ case 4:
1745
+ _.label++;
1746
+ return {
1747
+ value: op[1],
1748
+ done: false
1749
+ };
1750
+ case 5:
1751
+ _.label++;
1752
+ y = op[1];
1753
+ op = [0];
1754
+ continue;
1755
+ case 7:
1756
+ op = _.ops.pop();
1757
+ _.trys.pop();
1758
+ continue;
1759
+ default:
1760
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
1761
+ _ = 0;
1762
+ continue;
1763
+ }
1764
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
1765
+ _.label = op[1];
1766
+ break;
1767
+ }
1768
+ if (op[0] === 6 && _.label < t[1]) {
1769
+ _.label = t[1];
1770
+ t = op;
1771
+ break;
1772
+ }
1773
+ if (t && _.label < t[2]) {
1774
+ _.label = t[2];
1775
+ _.ops.push(op);
1776
+ break;
1777
+ }
1778
+ if (t[2]) _.ops.pop();
1779
+ _.trys.pop();
1780
+ continue;
1781
+ }
1782
+ op = body.call(thisArg, _);
1783
+ } catch (e) {
1784
+ op = [6, e];
1785
+ y = 0;
1786
+ } finally {
1787
+ f = t = 0;
1788
+ }
1789
+ if (op[0] & 5) throw op[1];
1790
+ return {
1791
+ value: op[0] ? op[1] : void 0,
1792
+ done: true
1793
+ };
1794
+ }
1795
+ }
1796
+ var Node = function() {
1797
+ function Node(key, data) {
1798
+ this.next = null;
1799
+ this.key = key;
1800
+ this.data = data;
1801
+ this.left = null;
1802
+ this.right = null;
1803
+ }
1804
+ return Node;
1805
+ }();
1806
+ function DEFAULT_COMPARE(a, b) {
1807
+ return a > b ? 1 : a < b ? -1 : 0;
1808
+ }
1809
+ /**
1810
+ * Simple top down splay, not requiring i to be in the tree t.
1811
+ */
1812
+ function splay(i, t, comparator) {
1813
+ var N = new Node(null, null);
1814
+ var l = N;
1815
+ var r = N;
1816
+ while (true) {
1817
+ var cmp = comparator(i, t.key);
1818
+ if (cmp < 0) {
1819
+ if (t.left === null) break;
1820
+ if (comparator(i, t.left.key) < 0) {
1821
+ var y = t.left;
1822
+ t.left = y.right;
1823
+ y.right = t;
1824
+ t = y;
1825
+ if (t.left === null) break;
1826
+ }
1827
+ r.left = t;
1828
+ r = t;
1829
+ t = t.left;
1830
+ } else if (cmp > 0) {
1831
+ if (t.right === null) break;
1832
+ if (comparator(i, t.right.key) > 0) {
1833
+ var y = t.right;
1834
+ t.right = y.left;
1835
+ y.left = t;
1836
+ t = y;
1837
+ if (t.right === null) break;
1838
+ }
1839
+ l.right = t;
1840
+ l = t;
1841
+ t = t.right;
1842
+ } else break;
1843
+ }
1844
+ l.right = t.left;
1845
+ r.left = t.right;
1846
+ t.left = N.right;
1847
+ t.right = N.left;
1848
+ return t;
1849
+ }
1850
+ function insert(i, data, t, comparator) {
1851
+ var node = new Node(i, data);
1852
+ if (t === null) {
1853
+ node.left = node.right = null;
1854
+ return node;
1855
+ }
1856
+ t = splay(i, t, comparator);
1857
+ var cmp = comparator(i, t.key);
1858
+ if (cmp < 0) {
1859
+ node.left = t.left;
1860
+ node.right = t;
1861
+ t.left = null;
1862
+ } else if (cmp >= 0) {
1863
+ node.right = t.right;
1864
+ node.left = t;
1865
+ t.right = null;
1866
+ }
1867
+ return node;
1868
+ }
1869
+ function split(key, v, comparator) {
1870
+ var left = null;
1871
+ var right = null;
1872
+ if (v) {
1873
+ v = splay(key, v, comparator);
1874
+ var cmp = comparator(v.key, key);
1875
+ if (cmp === 0) {
1876
+ left = v.left;
1877
+ right = v.right;
1878
+ } else if (cmp < 0) {
1879
+ right = v.right;
1880
+ v.right = null;
1881
+ left = v;
1882
+ } else {
1883
+ left = v.left;
1884
+ v.left = null;
1885
+ right = v;
1886
+ }
1887
+ }
1888
+ return {
1889
+ left,
1890
+ right
1891
+ };
1892
+ }
1893
+ function merge(left, right, comparator) {
1894
+ if (right === null) return left;
1895
+ if (left === null) return right;
1896
+ right = splay(left.key, right, comparator);
1897
+ right.left = left;
1898
+ return right;
1899
+ }
1900
+ /**
1901
+ * Prints level of the tree
1902
+ */
1903
+ function printRow(root, prefix, isTail, out, printNode) {
1904
+ if (root) {
1905
+ out("" + prefix + (isTail ? "└── " : "├── ") + printNode(root) + "\n");
1906
+ var indent = prefix + (isTail ? " " : "│ ");
1907
+ if (root.left) printRow(root.left, indent, false, out, printNode);
1908
+ if (root.right) printRow(root.right, indent, true, out, printNode);
1909
+ }
1910
+ }
1911
+ var Tree = function() {
1912
+ function Tree(comparator) {
1913
+ if (comparator === void 0) comparator = DEFAULT_COMPARE;
1914
+ this._root = null;
1915
+ this._size = 0;
1916
+ this._comparator = comparator;
1917
+ }
1918
+ /**
1919
+ * Inserts a key, allows duplicates
1920
+ */
1921
+ Tree.prototype.insert = function(key, data) {
1922
+ this._size++;
1923
+ return this._root = insert(key, data, this._root, this._comparator);
1924
+ };
1925
+ /**
1926
+ * Adds a key, if it is not present in the tree
1927
+ */
1928
+ Tree.prototype.add = function(key, data) {
1929
+ var node = new Node(key, data);
1930
+ if (this._root === null) {
1931
+ node.left = node.right = null;
1932
+ this._size++;
1933
+ this._root = node;
1934
+ }
1935
+ var comparator = this._comparator;
1936
+ var t = splay(key, this._root, comparator);
1937
+ var cmp = comparator(key, t.key);
1938
+ if (cmp === 0) this._root = t;
1939
+ else {
1940
+ if (cmp < 0) {
1941
+ node.left = t.left;
1942
+ node.right = t;
1943
+ t.left = null;
1944
+ } else if (cmp > 0) {
1945
+ node.right = t.right;
1946
+ node.left = t;
1947
+ t.right = null;
1948
+ }
1949
+ this._size++;
1950
+ this._root = node;
1951
+ }
1952
+ return this._root;
1953
+ };
1954
+ /**
1955
+ * @param {Key} key
1956
+ * @return {Node|null}
1957
+ */
1958
+ Tree.prototype.remove = function(key) {
1959
+ this._root = this._remove(key, this._root, this._comparator);
1960
+ };
1961
+ /**
1962
+ * Deletes i from the tree if it's there
1963
+ */
1964
+ Tree.prototype._remove = function(i, t, comparator) {
1965
+ var x;
1966
+ if (t === null) return null;
1967
+ t = splay(i, t, comparator);
1968
+ if (comparator(i, t.key) === 0) {
1969
+ if (t.left === null) x = t.right;
1970
+ else {
1971
+ x = splay(i, t.left, comparator);
1972
+ x.right = t.right;
1973
+ }
1974
+ this._size--;
1975
+ return x;
1976
+ }
1977
+ return t;
1978
+ };
1979
+ /**
1980
+ * Removes and returns the node with smallest key
1981
+ */
1982
+ Tree.prototype.pop = function() {
1983
+ var node = this._root;
1984
+ if (node) {
1985
+ while (node.left) node = node.left;
1986
+ this._root = splay(node.key, this._root, this._comparator);
1987
+ this._root = this._remove(node.key, this._root, this._comparator);
1988
+ return {
1989
+ key: node.key,
1990
+ data: node.data
1991
+ };
1992
+ }
1993
+ return null;
1994
+ };
1995
+ /**
1996
+ * Find without splaying
1997
+ */
1998
+ Tree.prototype.findStatic = function(key) {
1999
+ var current = this._root;
2000
+ var compare = this._comparator;
2001
+ while (current) {
2002
+ var cmp = compare(key, current.key);
2003
+ if (cmp === 0) return current;
2004
+ else if (cmp < 0) current = current.left;
2005
+ else current = current.right;
2006
+ }
2007
+ return null;
2008
+ };
2009
+ Tree.prototype.find = function(key) {
2010
+ if (this._root) {
2011
+ this._root = splay(key, this._root, this._comparator);
2012
+ if (this._comparator(key, this._root.key) !== 0) return null;
2013
+ }
2014
+ return this._root;
2015
+ };
2016
+ Tree.prototype.contains = function(key) {
2017
+ var current = this._root;
2018
+ var compare = this._comparator;
2019
+ while (current) {
2020
+ var cmp = compare(key, current.key);
2021
+ if (cmp === 0) return true;
2022
+ else if (cmp < 0) current = current.left;
2023
+ else current = current.right;
2024
+ }
2025
+ return false;
2026
+ };
2027
+ Tree.prototype.forEach = function(visitor, ctx) {
2028
+ var current = this._root;
2029
+ var Q = [];
2030
+ var done = false;
2031
+ while (!done) if (current !== null) {
2032
+ Q.push(current);
2033
+ current = current.left;
2034
+ } else if (Q.length !== 0) {
2035
+ current = Q.pop();
2036
+ visitor.call(ctx, current);
2037
+ current = current.right;
2038
+ } else done = true;
2039
+ return this;
2040
+ };
2041
+ /**
2042
+ * Walk key range from `low` to `high`. Stops if `fn` returns a value.
2043
+ */
2044
+ Tree.prototype.range = function(low, high, fn, ctx) {
2045
+ var Q = [];
2046
+ var compare = this._comparator;
2047
+ var node = this._root;
2048
+ var cmp;
2049
+ while (Q.length !== 0 || node) if (node) {
2050
+ Q.push(node);
2051
+ node = node.left;
2052
+ } else {
2053
+ node = Q.pop();
2054
+ cmp = compare(node.key, high);
2055
+ if (cmp > 0) break;
2056
+ else if (compare(node.key, low) >= 0) {
2057
+ if (fn.call(ctx, node)) return this;
2058
+ }
2059
+ node = node.right;
2060
+ }
2061
+ return this;
2062
+ };
2063
+ /**
2064
+ * Returns array of keys
2065
+ */
2066
+ Tree.prototype.keys = function() {
2067
+ var keys = [];
2068
+ this.forEach(function(_a) {
2069
+ var key = _a.key;
2070
+ return keys.push(key);
2071
+ });
2072
+ return keys;
2073
+ };
2074
+ /**
2075
+ * Returns array of all the data in the nodes
2076
+ */
2077
+ Tree.prototype.values = function() {
2078
+ var values = [];
2079
+ this.forEach(function(_a) {
2080
+ var data = _a.data;
2081
+ return values.push(data);
2082
+ });
2083
+ return values;
2084
+ };
2085
+ Tree.prototype.min = function() {
2086
+ if (this._root) return this.minNode(this._root).key;
2087
+ return null;
2088
+ };
2089
+ Tree.prototype.max = function() {
2090
+ if (this._root) return this.maxNode(this._root).key;
2091
+ return null;
2092
+ };
2093
+ Tree.prototype.minNode = function(t) {
2094
+ if (t === void 0) t = this._root;
2095
+ if (t) while (t.left) t = t.left;
2096
+ return t;
2097
+ };
2098
+ Tree.prototype.maxNode = function(t) {
2099
+ if (t === void 0) t = this._root;
2100
+ if (t) while (t.right) t = t.right;
2101
+ return t;
2102
+ };
2103
+ /**
2104
+ * Returns node at given index
2105
+ */
2106
+ Tree.prototype.at = function(index) {
2107
+ var current = this._root;
2108
+ var done = false;
2109
+ var i = 0;
2110
+ var Q = [];
2111
+ while (!done) if (current) {
2112
+ Q.push(current);
2113
+ current = current.left;
2114
+ } else if (Q.length > 0) {
2115
+ current = Q.pop();
2116
+ if (i === index) return current;
2117
+ i++;
2118
+ current = current.right;
2119
+ } else done = true;
2120
+ return null;
2121
+ };
2122
+ Tree.prototype.next = function(d) {
2123
+ var root = this._root;
2124
+ var successor = null;
2125
+ if (d.right) {
2126
+ successor = d.right;
2127
+ while (successor.left) successor = successor.left;
2128
+ return successor;
2129
+ }
2130
+ var comparator = this._comparator;
2131
+ while (root) {
2132
+ var cmp = comparator(d.key, root.key);
2133
+ if (cmp === 0) break;
2134
+ else if (cmp < 0) {
2135
+ successor = root;
2136
+ root = root.left;
2137
+ } else root = root.right;
2138
+ }
2139
+ return successor;
2140
+ };
2141
+ Tree.prototype.prev = function(d) {
2142
+ var root = this._root;
2143
+ var predecessor = null;
2144
+ if (d.left !== null) {
2145
+ predecessor = d.left;
2146
+ while (predecessor.right) predecessor = predecessor.right;
2147
+ return predecessor;
2148
+ }
2149
+ var comparator = this._comparator;
2150
+ while (root) {
2151
+ var cmp = comparator(d.key, root.key);
2152
+ if (cmp === 0) break;
2153
+ else if (cmp < 0) root = root.left;
2154
+ else {
2155
+ predecessor = root;
2156
+ root = root.right;
2157
+ }
2158
+ }
2159
+ return predecessor;
2160
+ };
2161
+ Tree.prototype.clear = function() {
2162
+ this._root = null;
2163
+ this._size = 0;
2164
+ return this;
2165
+ };
2166
+ Tree.prototype.toList = function() {
2167
+ return toList(this._root);
2168
+ };
2169
+ /**
2170
+ * Bulk-load items. Both array have to be same size
2171
+ */
2172
+ Tree.prototype.load = function(keys, values, presort) {
2173
+ if (values === void 0) values = [];
2174
+ if (presort === void 0) presort = false;
2175
+ var size = keys.length;
2176
+ var comparator = this._comparator;
2177
+ if (presort) sort(keys, values, 0, size - 1, comparator);
2178
+ if (this._root === null) {
2179
+ this._root = loadRecursive(keys, values, 0, size);
2180
+ this._size = size;
2181
+ } else {
2182
+ var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
2183
+ size = this._size + size;
2184
+ this._root = sortedListToBST({ head: mergedList }, 0, size);
2185
+ }
2186
+ return this;
2187
+ };
2188
+ Tree.prototype.isEmpty = function() {
2189
+ return this._root === null;
2190
+ };
2191
+ Object.defineProperty(Tree.prototype, "size", {
2192
+ get: function() {
2193
+ return this._size;
2194
+ },
2195
+ enumerable: true,
2196
+ configurable: true
2197
+ });
2198
+ Object.defineProperty(Tree.prototype, "root", {
2199
+ get: function() {
2200
+ return this._root;
2201
+ },
2202
+ enumerable: true,
2203
+ configurable: true
2204
+ });
2205
+ Tree.prototype.toString = function(printNode) {
2206
+ if (printNode === void 0) printNode = function(n) {
2207
+ return String(n.key);
2208
+ };
2209
+ var out = [];
2210
+ printRow(this._root, "", true, function(v) {
2211
+ return out.push(v);
2212
+ }, printNode);
2213
+ return out.join("");
2214
+ };
2215
+ Tree.prototype.update = function(key, newKey, newData) {
2216
+ var comparator = this._comparator;
2217
+ var _a = split(key, this._root, comparator), left = _a.left, right = _a.right;
2218
+ if (comparator(key, newKey) < 0) right = insert(newKey, newData, right, comparator);
2219
+ else left = insert(newKey, newData, left, comparator);
2220
+ this._root = merge(left, right, comparator);
2221
+ };
2222
+ Tree.prototype.split = function(key) {
2223
+ return split(key, this._root, this._comparator);
2224
+ };
2225
+ Tree.prototype[Symbol.iterator] = function() {
2226
+ var current, Q, done;
2227
+ return __generator(this, function(_a) {
2228
+ switch (_a.label) {
2229
+ case 0:
2230
+ current = this._root;
2231
+ Q = [];
2232
+ done = false;
2233
+ _a.label = 1;
2234
+ case 1:
2235
+ if (!!done) return [3, 6];
2236
+ if (!(current !== null)) return [3, 2];
2237
+ Q.push(current);
2238
+ current = current.left;
2239
+ return [3, 5];
2240
+ case 2:
2241
+ if (!(Q.length !== 0)) return [3, 4];
2242
+ current = Q.pop();
2243
+ return [4, current];
2244
+ case 3:
2245
+ _a.sent();
2246
+ current = current.right;
2247
+ return [3, 5];
2248
+ case 4:
2249
+ done = true;
2250
+ _a.label = 5;
2251
+ case 5: return [3, 1];
2252
+ case 6: return [2];
2253
+ }
2254
+ });
2255
+ };
2256
+ return Tree;
2257
+ }();
2258
+ function loadRecursive(keys, values, start, end) {
2259
+ var size = end - start;
2260
+ if (size > 0) {
2261
+ var middle = start + Math.floor(size / 2);
2262
+ var key = keys[middle];
2263
+ var data = values[middle];
2264
+ var node = new Node(key, data);
2265
+ node.left = loadRecursive(keys, values, start, middle);
2266
+ node.right = loadRecursive(keys, values, middle + 1, end);
2267
+ return node;
2268
+ }
2269
+ return null;
2270
+ }
2271
+ function createList(keys, values) {
2272
+ var head = new Node(null, null);
2273
+ var p = head;
2274
+ for (var i = 0; i < keys.length; i++) p = p.next = new Node(keys[i], values[i]);
2275
+ p.next = null;
2276
+ return head.next;
2277
+ }
2278
+ function toList(root) {
2279
+ var current = root;
2280
+ var Q = [];
2281
+ var done = false;
2282
+ var head = new Node(null, null);
2283
+ var p = head;
2284
+ while (!done) if (current) {
2285
+ Q.push(current);
2286
+ current = current.left;
2287
+ } else if (Q.length > 0) {
2288
+ current = p = p.next = Q.pop();
2289
+ current = current.right;
2290
+ } else done = true;
2291
+ p.next = null;
2292
+ return head.next;
2293
+ }
2294
+ function sortedListToBST(list, start, end) {
2295
+ var size = end - start;
2296
+ if (size > 0) {
2297
+ var middle = start + Math.floor(size / 2);
2298
+ var left = sortedListToBST(list, start, middle);
2299
+ var root = list.head;
2300
+ root.left = left;
2301
+ list.head = list.head.next;
2302
+ root.right = sortedListToBST(list, middle + 1, end);
2303
+ return root;
2304
+ }
2305
+ return null;
2306
+ }
2307
+ function mergeLists(l1, l2, compare) {
2308
+ var head = new Node(null, null);
2309
+ var p = head;
2310
+ var p1 = l1;
2311
+ var p2 = l2;
2312
+ while (p1 !== null && p2 !== null) {
2313
+ if (compare(p1.key, p2.key) < 0) {
2314
+ p.next = p1;
2315
+ p1 = p1.next;
2316
+ } else {
2317
+ p.next = p2;
2318
+ p2 = p2.next;
2319
+ }
2320
+ p = p.next;
2321
+ }
2322
+ if (p1 !== null) p.next = p1;
2323
+ else if (p2 !== null) p.next = p2;
2324
+ return head.next;
2325
+ }
2326
+ function sort(keys, values, left, right, compare) {
2327
+ if (left >= right) return;
2328
+ var pivot = keys[left + right >> 1];
2329
+ var i = left - 1;
2330
+ var j = right + 1;
2331
+ while (true) {
2332
+ do
2333
+ i++;
2334
+ while (compare(keys[i], pivot) < 0);
2335
+ do
2336
+ j--;
2337
+ while (compare(keys[j], pivot) > 0);
2338
+ if (i >= j) break;
2339
+ var tmp = keys[i];
2340
+ keys[i] = keys[j];
2341
+ keys[j] = tmp;
2342
+ tmp = values[i];
2343
+ values[i] = values[j];
2344
+ values[j] = tmp;
2345
+ }
2346
+ sort(keys, values, left, j, compare);
2347
+ sort(keys, values, j + 1, right, compare);
2348
+ }
2349
+ /**
2350
+ * A bounding box has the format:
2351
+ *
2352
+ * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
2353
+ *
2354
+ */
2355
+ const isInBbox = (bbox, point) => {
2356
+ return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
2357
+ };
2358
+ const getBboxOverlap = (b1, b2) => {
2359
+ if (b2.ur.x < b1.ll.x || b1.ur.x < b2.ll.x || b2.ur.y < b1.ll.y || b1.ur.y < b2.ll.y) return null;
2360
+ const lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
2361
+ const upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x;
2362
+ const lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
2363
+ const upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y;
2364
+ return {
2365
+ ll: {
2366
+ x: lowerX,
2367
+ y: lowerY
2368
+ },
2369
+ ur: {
2370
+ x: upperX,
2371
+ y: upperY
2372
+ }
2373
+ };
2374
+ };
2375
+ let epsilon$1 = Number.EPSILON;
2376
+ if (epsilon$1 === void 0) epsilon$1 = Math.pow(2, -52);
2377
+ const EPSILON_SQ = epsilon$1 * epsilon$1;
2378
+ const cmp = (a, b) => {
2379
+ if (-epsilon$1 < a && a < epsilon$1) {
2380
+ if (-epsilon$1 < b && b < epsilon$1) return 0;
2381
+ }
2382
+ const ab = a - b;
2383
+ if (ab * ab < EPSILON_SQ * a * b) return 0;
2384
+ return a < b ? -1 : 1;
2385
+ };
2386
+ /**
2387
+ * This class rounds incoming values sufficiently so that
2388
+ * floating points problems are, for the most part, avoided.
2389
+ *
2390
+ * Incoming points are have their x & y values tested against
2391
+ * all previously seen x & y values. If either is 'too close'
2392
+ * to a previously seen value, it's value is 'snapped' to the
2393
+ * previously seen value.
2394
+ *
2395
+ * All points should be rounded by this class before being
2396
+ * stored in any data structures in the rest of this algorithm.
2397
+ */
2398
+ class PtRounder {
2399
+ constructor() {
2400
+ this.reset();
2401
+ }
2402
+ reset() {
2403
+ this.xRounder = new CoordRounder();
2404
+ this.yRounder = new CoordRounder();
2405
+ }
2406
+ round(x, y) {
2407
+ return {
2408
+ x: this.xRounder.round(x),
2409
+ y: this.yRounder.round(y)
2410
+ };
2411
+ }
2412
+ }
2413
+ class CoordRounder {
2414
+ constructor() {
2415
+ this.tree = new Tree();
2416
+ this.round(0);
2417
+ }
2418
+ round(coord) {
2419
+ const node = this.tree.add(coord);
2420
+ const prevNode = this.tree.prev(node);
2421
+ if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
2422
+ this.tree.remove(coord);
2423
+ return prevNode.key;
2424
+ }
2425
+ const nextNode = this.tree.next(node);
2426
+ if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
2427
+ this.tree.remove(coord);
2428
+ return nextNode.key;
2429
+ }
2430
+ return coord;
2431
+ }
2432
+ }
2433
+ const rounder = new PtRounder();
2434
+ const epsilon = 11102230246251565e-32;
2435
+ const splitter = 134217729;
2436
+ const resulterrbound = 3.000000000000001 * epsilon;
2437
+ function sum(elen, e, flen, f, h) {
2438
+ let Q, Qnew, hh, bvirt;
2439
+ let enow = e[0];
2440
+ let fnow = f[0];
2441
+ let eindex = 0;
2442
+ let findex = 0;
2443
+ if (fnow > enow === fnow > -enow) {
2444
+ Q = enow;
2445
+ enow = e[++eindex];
2446
+ } else {
2447
+ Q = fnow;
2448
+ fnow = f[++findex];
2449
+ }
2450
+ let hindex = 0;
2451
+ if (eindex < elen && findex < flen) {
2452
+ if (fnow > enow === fnow > -enow) {
2453
+ Qnew = enow + Q;
2454
+ hh = Q - (Qnew - enow);
2455
+ enow = e[++eindex];
2456
+ } else {
2457
+ Qnew = fnow + Q;
2458
+ hh = Q - (Qnew - fnow);
2459
+ fnow = f[++findex];
2460
+ }
2461
+ Q = Qnew;
2462
+ if (hh !== 0) h[hindex++] = hh;
2463
+ while (eindex < elen && findex < flen) {
2464
+ if (fnow > enow === fnow > -enow) {
2465
+ Qnew = Q + enow;
2466
+ bvirt = Qnew - Q;
2467
+ hh = Q - (Qnew - bvirt) + (enow - bvirt);
2468
+ enow = e[++eindex];
2469
+ } else {
2470
+ Qnew = Q + fnow;
2471
+ bvirt = Qnew - Q;
2472
+ hh = Q - (Qnew - bvirt) + (fnow - bvirt);
2473
+ fnow = f[++findex];
2474
+ }
2475
+ Q = Qnew;
2476
+ if (hh !== 0) h[hindex++] = hh;
2477
+ }
2478
+ }
2479
+ while (eindex < elen) {
2480
+ Qnew = Q + enow;
2481
+ bvirt = Qnew - Q;
2482
+ hh = Q - (Qnew - bvirt) + (enow - bvirt);
2483
+ enow = e[++eindex];
2484
+ Q = Qnew;
2485
+ if (hh !== 0) h[hindex++] = hh;
2486
+ }
2487
+ while (findex < flen) {
2488
+ Qnew = Q + fnow;
2489
+ bvirt = Qnew - Q;
2490
+ hh = Q - (Qnew - bvirt) + (fnow - bvirt);
2491
+ fnow = f[++findex];
2492
+ Q = Qnew;
2493
+ if (hh !== 0) h[hindex++] = hh;
2494
+ }
2495
+ if (Q !== 0 || hindex === 0) h[hindex++] = Q;
2496
+ return hindex;
2497
+ }
2498
+ function estimate(elen, e) {
2499
+ let Q = e[0];
2500
+ for (let i = 1; i < elen; i++) Q += e[i];
2501
+ return Q;
2502
+ }
2503
+ function vec(n) {
2504
+ return new Float64Array(n);
2505
+ }
2506
+ const ccwerrboundA = 3.0000000000000018 * epsilon;
2507
+ const ccwerrboundB = 2.0000000000000013 * epsilon;
2508
+ const ccwerrboundC = 9.000000000000007 * epsilon * epsilon;
2509
+ const B = vec(4);
2510
+ const C1 = vec(8);
2511
+ const C2 = vec(12);
2512
+ const D = vec(16);
2513
+ const u = vec(4);
2514
+ function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
2515
+ let acxtail, acytail, bcxtail, bcytail;
2516
+ let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
2517
+ const acx = ax - cx;
2518
+ const bcx = bx - cx;
2519
+ const acy = ay - cy;
2520
+ const bcy = by - cy;
2521
+ s1 = acx * bcy;
2522
+ c = splitter * acx;
2523
+ ahi = c - (c - acx);
2524
+ alo = acx - ahi;
2525
+ c = splitter * bcy;
2526
+ bhi = c - (c - bcy);
2527
+ blo = bcy - bhi;
2528
+ s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
2529
+ t1 = acy * bcx;
2530
+ c = splitter * acy;
2531
+ ahi = c - (c - acy);
2532
+ alo = acy - ahi;
2533
+ c = splitter * bcx;
2534
+ bhi = c - (c - bcx);
2535
+ blo = bcx - bhi;
2536
+ t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
2537
+ _i = s0 - t0;
2538
+ bvirt = s0 - _i;
2539
+ B[0] = s0 - (_i + bvirt) + (bvirt - t0);
2540
+ _j = s1 + _i;
2541
+ bvirt = _j - s1;
2542
+ _0 = s1 - (_j - bvirt) + (_i - bvirt);
2543
+ _i = _0 - t1;
2544
+ bvirt = _0 - _i;
2545
+ B[1] = _0 - (_i + bvirt) + (bvirt - t1);
2546
+ u3 = _j + _i;
2547
+ bvirt = u3 - _j;
2548
+ B[2] = _j - (u3 - bvirt) + (_i - bvirt);
2549
+ B[3] = u3;
2550
+ let det = estimate(4, B);
2551
+ let errbound = ccwerrboundB * detsum;
2552
+ if (det >= errbound || -det >= errbound) return det;
2553
+ bvirt = ax - acx;
2554
+ acxtail = ax - (acx + bvirt) + (bvirt - cx);
2555
+ bvirt = bx - bcx;
2556
+ bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
2557
+ bvirt = ay - acy;
2558
+ acytail = ay - (acy + bvirt) + (bvirt - cy);
2559
+ bvirt = by - bcy;
2560
+ bcytail = by - (bcy + bvirt) + (bvirt - cy);
2561
+ if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) return det;
2562
+ errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
2563
+ det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
2564
+ if (det >= errbound || -det >= errbound) return det;
2565
+ s1 = acxtail * bcy;
2566
+ c = splitter * acxtail;
2567
+ ahi = c - (c - acxtail);
2568
+ alo = acxtail - ahi;
2569
+ c = splitter * bcy;
2570
+ bhi = c - (c - bcy);
2571
+ blo = bcy - bhi;
2572
+ s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
2573
+ t1 = acytail * bcx;
2574
+ c = splitter * acytail;
2575
+ ahi = c - (c - acytail);
2576
+ alo = acytail - ahi;
2577
+ c = splitter * bcx;
2578
+ bhi = c - (c - bcx);
2579
+ blo = bcx - bhi;
2580
+ t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
2581
+ _i = s0 - t0;
2582
+ bvirt = s0 - _i;
2583
+ u[0] = s0 - (_i + bvirt) + (bvirt - t0);
2584
+ _j = s1 + _i;
2585
+ bvirt = _j - s1;
2586
+ _0 = s1 - (_j - bvirt) + (_i - bvirt);
2587
+ _i = _0 - t1;
2588
+ bvirt = _0 - _i;
2589
+ u[1] = _0 - (_i + bvirt) + (bvirt - t1);
2590
+ u3 = _j + _i;
2591
+ bvirt = u3 - _j;
2592
+ u[2] = _j - (u3 - bvirt) + (_i - bvirt);
2593
+ u[3] = u3;
2594
+ const C1len = sum(4, B, 4, u, C1);
2595
+ s1 = acx * bcytail;
2596
+ c = splitter * acx;
2597
+ ahi = c - (c - acx);
2598
+ alo = acx - ahi;
2599
+ c = splitter * bcytail;
2600
+ bhi = c - (c - bcytail);
2601
+ blo = bcytail - bhi;
2602
+ s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
2603
+ t1 = acy * bcxtail;
2604
+ c = splitter * acy;
2605
+ ahi = c - (c - acy);
2606
+ alo = acy - ahi;
2607
+ c = splitter * bcxtail;
2608
+ bhi = c - (c - bcxtail);
2609
+ blo = bcxtail - bhi;
2610
+ t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
2611
+ _i = s0 - t0;
2612
+ bvirt = s0 - _i;
2613
+ u[0] = s0 - (_i + bvirt) + (bvirt - t0);
2614
+ _j = s1 + _i;
2615
+ bvirt = _j - s1;
2616
+ _0 = s1 - (_j - bvirt) + (_i - bvirt);
2617
+ _i = _0 - t1;
2618
+ bvirt = _0 - _i;
2619
+ u[1] = _0 - (_i + bvirt) + (bvirt - t1);
2620
+ u3 = _j + _i;
2621
+ bvirt = u3 - _j;
2622
+ u[2] = _j - (u3 - bvirt) + (_i - bvirt);
2623
+ u[3] = u3;
2624
+ const C2len = sum(C1len, C1, 4, u, C2);
2625
+ s1 = acxtail * bcytail;
2626
+ c = splitter * acxtail;
2627
+ ahi = c - (c - acxtail);
2628
+ alo = acxtail - ahi;
2629
+ c = splitter * bcytail;
2630
+ bhi = c - (c - bcytail);
2631
+ blo = bcytail - bhi;
2632
+ s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
2633
+ t1 = acytail * bcxtail;
2634
+ c = splitter * acytail;
2635
+ ahi = c - (c - acytail);
2636
+ alo = acytail - ahi;
2637
+ c = splitter * bcxtail;
2638
+ bhi = c - (c - bcxtail);
2639
+ blo = bcxtail - bhi;
2640
+ t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
2641
+ _i = s0 - t0;
2642
+ bvirt = s0 - _i;
2643
+ u[0] = s0 - (_i + bvirt) + (bvirt - t0);
2644
+ _j = s1 + _i;
2645
+ bvirt = _j - s1;
2646
+ _0 = s1 - (_j - bvirt) + (_i - bvirt);
2647
+ _i = _0 - t1;
2648
+ bvirt = _0 - _i;
2649
+ u[1] = _0 - (_i + bvirt) + (bvirt - t1);
2650
+ u3 = _j + _i;
2651
+ bvirt = u3 - _j;
2652
+ u[2] = _j - (u3 - bvirt) + (_i - bvirt);
2653
+ u[3] = u3;
2654
+ return D[sum(C2len, C2, 4, u, D) - 1];
2655
+ }
2656
+ function orient2d(ax, ay, bx, by, cx, cy) {
2657
+ const detleft = (ay - cy) * (bx - cx);
2658
+ const detright = (ax - cx) * (by - cy);
2659
+ const det = detleft - detright;
2660
+ const detsum = Math.abs(detleft + detright);
2661
+ if (Math.abs(det) >= ccwerrboundA * detsum) return det;
2662
+ return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
2663
+ }
2664
+ const crossProduct = (a, b) => a.x * b.y - a.y * b.x;
2665
+ const dotProduct = (a, b) => a.x * b.x + a.y * b.y;
2666
+ const compareVectorAngles = (basePt, endPt1, endPt2) => {
2667
+ const res = orient2d(basePt.x, basePt.y, endPt1.x, endPt1.y, endPt2.x, endPt2.y);
2668
+ if (res > 0) return -1;
2669
+ if (res < 0) return 1;
2670
+ return 0;
2671
+ };
2672
+ const length = (v) => Math.sqrt(dotProduct(v, v));
2673
+ const sineOfAngle = (pShared, pBase, pAngle) => {
2674
+ const vBase = {
2675
+ x: pBase.x - pShared.x,
2676
+ y: pBase.y - pShared.y
2677
+ };
2678
+ const vAngle = {
2679
+ x: pAngle.x - pShared.x,
2680
+ y: pAngle.y - pShared.y
2681
+ };
2682
+ return crossProduct(vAngle, vBase) / length(vAngle) / length(vBase);
2683
+ };
2684
+ const cosineOfAngle = (pShared, pBase, pAngle) => {
2685
+ const vBase = {
2686
+ x: pBase.x - pShared.x,
2687
+ y: pBase.y - pShared.y
2688
+ };
2689
+ const vAngle = {
2690
+ x: pAngle.x - pShared.x,
2691
+ y: pAngle.y - pShared.y
2692
+ };
2693
+ return dotProduct(vAngle, vBase) / length(vAngle) / length(vBase);
2694
+ };
2695
+ const horizontalIntersection = (pt, v, y) => {
2696
+ if (v.y === 0) return null;
2697
+ return {
2698
+ x: pt.x + v.x / v.y * (y - pt.y),
2699
+ y
2700
+ };
2701
+ };
2702
+ const verticalIntersection = (pt, v, x) => {
2703
+ if (v.x === 0) return null;
2704
+ return {
2705
+ x,
2706
+ y: pt.y + v.y / v.x * (x - pt.x)
2707
+ };
2708
+ };
2709
+ const intersection$1 = (pt1, v1, pt2, v2) => {
2710
+ if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
2711
+ if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
2712
+ if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
2713
+ if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y);
2714
+ const kross = crossProduct(v1, v2);
2715
+ if (kross == 0) return null;
2716
+ const ve = {
2717
+ x: pt2.x - pt1.x,
2718
+ y: pt2.y - pt1.y
2719
+ };
2720
+ const d1 = crossProduct(ve, v1) / kross;
2721
+ const d2 = crossProduct(ve, v2) / kross;
2722
+ const x1 = pt1.x + d2 * v1.x, x2 = pt2.x + d1 * v2.x;
2723
+ const y1 = pt1.y + d2 * v1.y, y2 = pt2.y + d1 * v2.y;
2724
+ return {
2725
+ x: (x1 + x2) / 2,
2726
+ y: (y1 + y2) / 2
2727
+ };
2728
+ };
2729
+ class SweepEvent {
2730
+ static compare(a, b) {
2731
+ const ptCmp = SweepEvent.comparePoints(a.point, b.point);
2732
+ if (ptCmp !== 0) return ptCmp;
2733
+ if (a.point !== b.point) a.link(b);
2734
+ if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1;
2735
+ return Segment.compare(a.segment, b.segment);
2736
+ }
2737
+ static comparePoints(aPt, bPt) {
2738
+ if (aPt.x < bPt.x) return -1;
2739
+ if (aPt.x > bPt.x) return 1;
2740
+ if (aPt.y < bPt.y) return -1;
2741
+ if (aPt.y > bPt.y) return 1;
2742
+ return 0;
2743
+ }
2744
+ constructor(point, isLeft) {
2745
+ if (point.events === void 0) point.events = [this];
2746
+ else point.events.push(this);
2747
+ this.point = point;
2748
+ this.isLeft = isLeft;
2749
+ }
2750
+ link(other) {
2751
+ if (other.point === this.point) throw new Error("Tried to link already linked events");
2752
+ const otherEvents = other.point.events;
2753
+ for (let i = 0, iMax = otherEvents.length; i < iMax; i++) {
2754
+ const evt = otherEvents[i];
2755
+ this.point.events.push(evt);
2756
+ evt.point = this.point;
2757
+ }
2758
+ this.checkForConsuming();
2759
+ }
2760
+ checkForConsuming() {
2761
+ const numEvents = this.point.events.length;
2762
+ for (let i = 0; i < numEvents; i++) {
2763
+ const evt1 = this.point.events[i];
2764
+ if (evt1.segment.consumedBy !== void 0) continue;
2765
+ for (let j = i + 1; j < numEvents; j++) {
2766
+ const evt2 = this.point.events[j];
2767
+ if (evt2.consumedBy !== void 0) continue;
2768
+ if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
2769
+ evt1.segment.consume(evt2.segment);
2770
+ }
2771
+ }
2772
+ }
2773
+ getAvailableLinkedEvents() {
2774
+ const events = [];
2775
+ for (let i = 0, iMax = this.point.events.length; i < iMax; i++) {
2776
+ const evt = this.point.events[i];
2777
+ if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) events.push(evt);
2778
+ }
2779
+ return events;
2780
+ }
2781
+ /**
2782
+ * Returns a comparator function for sorting linked events that will
2783
+ * favor the event that will give us the smallest left-side angle.
2784
+ * All ring construction starts as low as possible heading to the right,
2785
+ * so by always turning left as sharp as possible we'll get polygons
2786
+ * without uncessary loops & holes.
2787
+ *
2788
+ * The comparator function has a compute cache such that it avoids
2789
+ * re-computing already-computed values.
2790
+ */
2791
+ getLeftmostComparator(baseEvent) {
2792
+ const cache = /* @__PURE__ */ new Map();
2793
+ const fillCache = (linkedEvent) => {
2794
+ const nextEvent = linkedEvent.otherSE;
2795
+ cache.set(linkedEvent, {
2796
+ sine: sineOfAngle(this.point, baseEvent.point, nextEvent.point),
2797
+ cosine: cosineOfAngle(this.point, baseEvent.point, nextEvent.point)
2798
+ });
2799
+ };
2800
+ return (a, b) => {
2801
+ if (!cache.has(a)) fillCache(a);
2802
+ if (!cache.has(b)) fillCache(b);
2803
+ const { sine: asine, cosine: acosine } = cache.get(a);
2804
+ const { sine: bsine, cosine: bcosine } = cache.get(b);
2805
+ if (asine >= 0 && bsine >= 0) {
2806
+ if (acosine < bcosine) return 1;
2807
+ if (acosine > bcosine) return -1;
2808
+ return 0;
2809
+ }
2810
+ if (asine < 0 && bsine < 0) {
2811
+ if (acosine < bcosine) return -1;
2812
+ if (acosine > bcosine) return 1;
2813
+ return 0;
2814
+ }
2815
+ if (bsine < asine) return -1;
2816
+ if (bsine > asine) return 1;
2817
+ return 0;
2818
+ };
2819
+ }
2820
+ }
2821
+ let segmentId = 0;
2822
+ class Segment {
2823
+ static compare(a, b) {
2824
+ const alx = a.leftSE.point.x;
2825
+ const blx = b.leftSE.point.x;
2826
+ const arx = a.rightSE.point.x;
2827
+ const brx = b.rightSE.point.x;
2828
+ if (brx < alx) return 1;
2829
+ if (arx < blx) return -1;
2830
+ const aly = a.leftSE.point.y;
2831
+ const bly = b.leftSE.point.y;
2832
+ const ary = a.rightSE.point.y;
2833
+ const bry = b.rightSE.point.y;
2834
+ if (alx < blx) {
2835
+ if (bly < aly && bly < ary) return 1;
2836
+ if (bly > aly && bly > ary) return -1;
2837
+ const aCmpBLeft = a.comparePoint(b.leftSE.point);
2838
+ if (aCmpBLeft < 0) return 1;
2839
+ if (aCmpBLeft > 0) return -1;
2840
+ const bCmpARight = b.comparePoint(a.rightSE.point);
2841
+ if (bCmpARight !== 0) return bCmpARight;
2842
+ return -1;
2843
+ }
2844
+ if (alx > blx) {
2845
+ if (aly < bly && aly < bry) return -1;
2846
+ if (aly > bly && aly > bry) return 1;
2847
+ const bCmpALeft = b.comparePoint(a.leftSE.point);
2848
+ if (bCmpALeft !== 0) return bCmpALeft;
2849
+ const aCmpBRight = a.comparePoint(b.rightSE.point);
2850
+ if (aCmpBRight < 0) return 1;
2851
+ if (aCmpBRight > 0) return -1;
2852
+ return 1;
2853
+ }
2854
+ if (aly < bly) return -1;
2855
+ if (aly > bly) return 1;
2856
+ if (arx < brx) {
2857
+ const bCmpARight = b.comparePoint(a.rightSE.point);
2858
+ if (bCmpARight !== 0) return bCmpARight;
2859
+ }
2860
+ if (arx > brx) {
2861
+ const aCmpBRight = a.comparePoint(b.rightSE.point);
2862
+ if (aCmpBRight < 0) return 1;
2863
+ if (aCmpBRight > 0) return -1;
2864
+ }
2865
+ if (arx !== brx) {
2866
+ const ay = ary - aly;
2867
+ const ax = arx - alx;
2868
+ const by = bry - bly;
2869
+ const bx = brx - blx;
2870
+ if (ay > ax && by < bx) return 1;
2871
+ if (ay < ax && by > bx) return -1;
2872
+ }
2873
+ if (arx > brx) return 1;
2874
+ if (arx < brx) return -1;
2875
+ if (ary < bry) return -1;
2876
+ if (ary > bry) return 1;
2877
+ if (a.id < b.id) return -1;
2878
+ if (a.id > b.id) return 1;
2879
+ return 0;
2880
+ }
2881
+ constructor(leftSE, rightSE, rings, windings) {
2882
+ this.id = ++segmentId;
2883
+ this.leftSE = leftSE;
2884
+ leftSE.segment = this;
2885
+ leftSE.otherSE = rightSE;
2886
+ this.rightSE = rightSE;
2887
+ rightSE.segment = this;
2888
+ rightSE.otherSE = leftSE;
2889
+ this.rings = rings;
2890
+ this.windings = windings;
2891
+ }
2892
+ static fromRing(pt1, pt2, ring) {
2893
+ let leftPt, rightPt, winding;
2894
+ const cmpPts = SweepEvent.comparePoints(pt1, pt2);
2895
+ if (cmpPts < 0) {
2896
+ leftPt = pt1;
2897
+ rightPt = pt2;
2898
+ winding = 1;
2899
+ } else if (cmpPts > 0) {
2900
+ leftPt = pt2;
2901
+ rightPt = pt1;
2902
+ winding = -1;
2903
+ } else throw new Error(`Tried to create degenerate segment at [${pt1.x}, ${pt1.y}]`);
2904
+ return new Segment(new SweepEvent(leftPt, true), new SweepEvent(rightPt, false), [ring], [winding]);
2905
+ }
2906
+ replaceRightSE(newRightSE) {
2907
+ this.rightSE = newRightSE;
2908
+ this.rightSE.segment = this;
2909
+ this.rightSE.otherSE = this.leftSE;
2910
+ this.leftSE.otherSE = this.rightSE;
2911
+ }
2912
+ bbox() {
2913
+ const y1 = this.leftSE.point.y;
2914
+ const y2 = this.rightSE.point.y;
2915
+ return {
2916
+ ll: {
2917
+ x: this.leftSE.point.x,
2918
+ y: y1 < y2 ? y1 : y2
2919
+ },
2920
+ ur: {
2921
+ x: this.rightSE.point.x,
2922
+ y: y1 > y2 ? y1 : y2
2923
+ }
2924
+ };
2925
+ }
2926
+ vector() {
2927
+ return {
2928
+ x: this.rightSE.point.x - this.leftSE.point.x,
2929
+ y: this.rightSE.point.y - this.leftSE.point.y
2930
+ };
2931
+ }
2932
+ isAnEndpoint(pt) {
2933
+ return pt.x === this.leftSE.point.x && pt.y === this.leftSE.point.y || pt.x === this.rightSE.point.x && pt.y === this.rightSE.point.y;
2934
+ }
2935
+ comparePoint(point) {
2936
+ if (this.isAnEndpoint(point)) return 0;
2937
+ const lPt = this.leftSE.point;
2938
+ const rPt = this.rightSE.point;
2939
+ const v = this.vector();
2940
+ if (lPt.x === rPt.x) {
2941
+ if (point.x === lPt.x) return 0;
2942
+ return point.x < lPt.x ? 1 : -1;
2943
+ }
2944
+ const yDist = (point.y - lPt.y) / v.y;
2945
+ const xFromYDist = lPt.x + yDist * v.x;
2946
+ if (point.x === xFromYDist) return 0;
2947
+ const xDist = (point.x - lPt.x) / v.x;
2948
+ const yFromXDist = lPt.y + xDist * v.y;
2949
+ if (point.y === yFromXDist) return 0;
2950
+ return point.y < yFromXDist ? -1 : 1;
2951
+ }
2952
+ /**
2953
+ * Given another segment, returns the first non-trivial intersection
2954
+ * between the two segments (in terms of sweep line ordering), if it exists.
2955
+ *
2956
+ * A 'non-trivial' intersection is one that will cause one or both of the
2957
+ * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
2958
+ *
2959
+ * * endpoint of segA with endpoint of segB --> trivial
2960
+ * * endpoint of segA with point along segB --> non-trivial
2961
+ * * endpoint of segB with point along segA --> non-trivial
2962
+ * * point along segA with point along segB --> non-trivial
2963
+ *
2964
+ * If no non-trivial intersection exists, return null
2965
+ * Else, return null.
2966
+ */
2967
+ getIntersection(other) {
2968
+ const tBbox = this.bbox();
2969
+ const oBbox = other.bbox();
2970
+ const bboxOverlap = getBboxOverlap(tBbox, oBbox);
2971
+ if (bboxOverlap === null) return null;
2972
+ const tlp = this.leftSE.point;
2973
+ const trp = this.rightSE.point;
2974
+ const olp = other.leftSE.point;
2975
+ const orp = other.rightSE.point;
2976
+ const touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
2977
+ const touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
2978
+ const touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
2979
+ const touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0;
2980
+ if (touchesThisLSE && touchesOtherLSE) {
2981
+ if (touchesThisRSE && !touchesOtherRSE) return trp;
2982
+ if (!touchesThisRSE && touchesOtherRSE) return orp;
2983
+ return null;
2984
+ }
2985
+ if (touchesThisLSE) {
2986
+ if (touchesOtherRSE) {
2987
+ if (tlp.x === orp.x && tlp.y === orp.y) return null;
2988
+ }
2989
+ return tlp;
2990
+ }
2991
+ if (touchesOtherLSE) {
2992
+ if (touchesThisRSE) {
2993
+ if (trp.x === olp.x && trp.y === olp.y) return null;
2994
+ }
2995
+ return olp;
2996
+ }
2997
+ if (touchesThisRSE && touchesOtherRSE) return null;
2998
+ if (touchesThisRSE) return trp;
2999
+ if (touchesOtherRSE) return orp;
3000
+ const pt = intersection$1(tlp, this.vector(), olp, other.vector());
3001
+ if (pt === null) return null;
3002
+ if (!isInBbox(bboxOverlap, pt)) return null;
3003
+ return rounder.round(pt.x, pt.y);
3004
+ }
3005
+ /**
3006
+ * Split the given segment into multiple segments on the given points.
3007
+ * * Each existing segment will retain its leftSE and a new rightSE will be
3008
+ * generated for it.
3009
+ * * A new segment will be generated which will adopt the original segment's
3010
+ * rightSE, and a new leftSE will be generated for it.
3011
+ * * If there are more than two points given to split on, new segments
3012
+ * in the middle will be generated with new leftSE and rightSE's.
3013
+ * * An array of the newly generated SweepEvents will be returned.
3014
+ *
3015
+ * Warning: input array of points is modified
3016
+ */
3017
+ split(point) {
3018
+ const newEvents = [];
3019
+ const alreadyLinked = point.events !== void 0;
3020
+ const newLeftSE = new SweepEvent(point, true);
3021
+ const newRightSE = new SweepEvent(point, false);
3022
+ const oldRightSE = this.rightSE;
3023
+ this.replaceRightSE(newRightSE);
3024
+ newEvents.push(newRightSE);
3025
+ newEvents.push(newLeftSE);
3026
+ const newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice());
3027
+ if (SweepEvent.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) newSeg.swapEvents();
3028
+ if (SweepEvent.comparePoints(this.leftSE.point, this.rightSE.point) > 0) this.swapEvents();
3029
+ if (alreadyLinked) {
3030
+ newLeftSE.checkForConsuming();
3031
+ newRightSE.checkForConsuming();
3032
+ }
3033
+ return newEvents;
3034
+ }
3035
+ swapEvents() {
3036
+ const tmpEvt = this.rightSE;
3037
+ this.rightSE = this.leftSE;
3038
+ this.leftSE = tmpEvt;
3039
+ this.leftSE.isLeft = true;
3040
+ this.rightSE.isLeft = false;
3041
+ for (let i = 0, iMax = this.windings.length; i < iMax; i++) this.windings[i] *= -1;
3042
+ }
3043
+ consume(other) {
3044
+ let consumer = this;
3045
+ let consumee = other;
3046
+ while (consumer.consumedBy) consumer = consumer.consumedBy;
3047
+ while (consumee.consumedBy) consumee = consumee.consumedBy;
3048
+ const cmp = Segment.compare(consumer, consumee);
3049
+ if (cmp === 0) return;
3050
+ if (cmp > 0) {
3051
+ const tmp = consumer;
3052
+ consumer = consumee;
3053
+ consumee = tmp;
3054
+ }
3055
+ if (consumer.prev === consumee) {
3056
+ const tmp = consumer;
3057
+ consumer = consumee;
3058
+ consumee = tmp;
3059
+ }
3060
+ for (let i = 0, iMax = consumee.rings.length; i < iMax; i++) {
3061
+ const ring = consumee.rings[i];
3062
+ const winding = consumee.windings[i];
3063
+ const index = consumer.rings.indexOf(ring);
3064
+ if (index === -1) {
3065
+ consumer.rings.push(ring);
3066
+ consumer.windings.push(winding);
3067
+ } else consumer.windings[index] += winding;
3068
+ }
3069
+ consumee.rings = null;
3070
+ consumee.windings = null;
3071
+ consumee.consumedBy = consumer;
3072
+ consumee.leftSE.consumedBy = consumer.leftSE;
3073
+ consumee.rightSE.consumedBy = consumer.rightSE;
3074
+ }
3075
+ prevInResult() {
3076
+ if (this._prevInResult !== void 0) return this._prevInResult;
3077
+ if (!this.prev) this._prevInResult = null;
3078
+ else if (this.prev.isInResult()) this._prevInResult = this.prev;
3079
+ else this._prevInResult = this.prev.prevInResult();
3080
+ return this._prevInResult;
3081
+ }
3082
+ beforeState() {
3083
+ if (this._beforeState !== void 0) return this._beforeState;
3084
+ if (!this.prev) this._beforeState = {
3085
+ rings: [],
3086
+ windings: [],
3087
+ multiPolys: []
3088
+ };
3089
+ else {
3090
+ const seg = this.prev.consumedBy || this.prev;
3091
+ this._beforeState = seg.afterState();
3092
+ }
3093
+ return this._beforeState;
3094
+ }
3095
+ afterState() {
3096
+ if (this._afterState !== void 0) return this._afterState;
3097
+ const beforeState = this.beforeState();
3098
+ this._afterState = {
3099
+ rings: beforeState.rings.slice(0),
3100
+ windings: beforeState.windings.slice(0),
3101
+ multiPolys: []
3102
+ };
3103
+ const ringsAfter = this._afterState.rings;
3104
+ const windingsAfter = this._afterState.windings;
3105
+ const mpsAfter = this._afterState.multiPolys;
3106
+ for (let i = 0, iMax = this.rings.length; i < iMax; i++) {
3107
+ const ring = this.rings[i];
3108
+ const winding = this.windings[i];
3109
+ const index = ringsAfter.indexOf(ring);
3110
+ if (index === -1) {
3111
+ ringsAfter.push(ring);
3112
+ windingsAfter.push(winding);
3113
+ } else windingsAfter[index] += winding;
3114
+ }
3115
+ const polysAfter = [];
3116
+ const polysExclude = [];
3117
+ for (let i = 0, iMax = ringsAfter.length; i < iMax; i++) {
3118
+ if (windingsAfter[i] === 0) continue;
3119
+ const ring = ringsAfter[i];
3120
+ const poly = ring.poly;
3121
+ if (polysExclude.indexOf(poly) !== -1) continue;
3122
+ if (ring.isExterior) polysAfter.push(poly);
3123
+ else {
3124
+ if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
3125
+ const index = polysAfter.indexOf(ring.poly);
3126
+ if (index !== -1) polysAfter.splice(index, 1);
3127
+ }
3128
+ }
3129
+ for (let i = 0, iMax = polysAfter.length; i < iMax; i++) {
3130
+ const mp = polysAfter[i].multiPoly;
3131
+ if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
3132
+ }
3133
+ return this._afterState;
3134
+ }
3135
+ isInResult() {
3136
+ if (this.consumedBy) return false;
3137
+ if (this._isInResult !== void 0) return this._isInResult;
3138
+ const mpsBefore = this.beforeState().multiPolys;
3139
+ const mpsAfter = this.afterState().multiPolys;
3140
+ switch (operation.type) {
3141
+ case "union": {
3142
+ const noBefores = mpsBefore.length === 0;
3143
+ const noAfters = mpsAfter.length === 0;
3144
+ this._isInResult = noBefores !== noAfters;
3145
+ break;
3146
+ }
3147
+ case "intersection": {
3148
+ let least;
3149
+ let most;
3150
+ if (mpsBefore.length < mpsAfter.length) {
3151
+ least = mpsBefore.length;
3152
+ most = mpsAfter.length;
3153
+ } else {
3154
+ least = mpsAfter.length;
3155
+ most = mpsBefore.length;
3156
+ }
3157
+ this._isInResult = most === operation.numMultiPolys && least < most;
3158
+ break;
3159
+ }
3160
+ case "xor": {
3161
+ const diff = Math.abs(mpsBefore.length - mpsAfter.length);
3162
+ this._isInResult = diff % 2 === 1;
3163
+ break;
3164
+ }
3165
+ case "difference": {
3166
+ const isJustSubject = (mps) => mps.length === 1 && mps[0].isSubject;
3167
+ this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
3168
+ break;
3169
+ }
3170
+ default: throw new Error(`Unrecognized operation type found ${operation.type}`);
3171
+ }
3172
+ return this._isInResult;
3173
+ }
3174
+ }
3175
+ class RingIn {
3176
+ constructor(geomRing, poly, isExterior) {
3177
+ if (!Array.isArray(geomRing) || geomRing.length === 0) throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
3178
+ this.poly = poly;
3179
+ this.isExterior = isExterior;
3180
+ this.segments = [];
3181
+ if (typeof geomRing[0][0] !== "number" || typeof geomRing[0][1] !== "number") throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
3182
+ const firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
3183
+ this.bbox = {
3184
+ ll: {
3185
+ x: firstPoint.x,
3186
+ y: firstPoint.y
3187
+ },
3188
+ ur: {
3189
+ x: firstPoint.x,
3190
+ y: firstPoint.y
3191
+ }
3192
+ };
3193
+ let prevPoint = firstPoint;
3194
+ for (let i = 1, iMax = geomRing.length; i < iMax; i++) {
3195
+ if (typeof geomRing[i][0] !== "number" || typeof geomRing[i][1] !== "number") throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
3196
+ let point = rounder.round(geomRing[i][0], geomRing[i][1]);
3197
+ if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
3198
+ this.segments.push(Segment.fromRing(prevPoint, point, this));
3199
+ if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
3200
+ if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
3201
+ if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
3202
+ if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
3203
+ prevPoint = point;
3204
+ }
3205
+ if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
3206
+ }
3207
+ getSweepEvents() {
3208
+ const sweepEvents = [];
3209
+ for (let i = 0, iMax = this.segments.length; i < iMax; i++) {
3210
+ const segment = this.segments[i];
3211
+ sweepEvents.push(segment.leftSE);
3212
+ sweepEvents.push(segment.rightSE);
3213
+ }
3214
+ return sweepEvents;
3215
+ }
3216
+ }
3217
+ class PolyIn {
3218
+ constructor(geomPoly, multiPoly) {
3219
+ if (!Array.isArray(geomPoly)) throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
3220
+ this.exteriorRing = new RingIn(geomPoly[0], this, true);
3221
+ this.bbox = {
3222
+ ll: {
3223
+ x: this.exteriorRing.bbox.ll.x,
3224
+ y: this.exteriorRing.bbox.ll.y
3225
+ },
3226
+ ur: {
3227
+ x: this.exteriorRing.bbox.ur.x,
3228
+ y: this.exteriorRing.bbox.ur.y
3229
+ }
3230
+ };
3231
+ this.interiorRings = [];
3232
+ for (let i = 1, iMax = geomPoly.length; i < iMax; i++) {
3233
+ const ring = new RingIn(geomPoly[i], this, false);
3234
+ if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
3235
+ if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
3236
+ if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
3237
+ if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
3238
+ this.interiorRings.push(ring);
3239
+ }
3240
+ this.multiPoly = multiPoly;
3241
+ }
3242
+ getSweepEvents() {
3243
+ const sweepEvents = this.exteriorRing.getSweepEvents();
3244
+ for (let i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
3245
+ const ringSweepEvents = this.interiorRings[i].getSweepEvents();
3246
+ for (let j = 0, jMax = ringSweepEvents.length; j < jMax; j++) sweepEvents.push(ringSweepEvents[j]);
3247
+ }
3248
+ return sweepEvents;
3249
+ }
3250
+ }
3251
+ class MultiPolyIn {
3252
+ constructor(geom, isSubject) {
3253
+ if (!Array.isArray(geom)) throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
3254
+ try {
3255
+ if (typeof geom[0][0][0] === "number") geom = [geom];
3256
+ } catch (ex) {}
3257
+ this.polys = [];
3258
+ this.bbox = {
3259
+ ll: {
3260
+ x: Number.POSITIVE_INFINITY,
3261
+ y: Number.POSITIVE_INFINITY
3262
+ },
3263
+ ur: {
3264
+ x: Number.NEGATIVE_INFINITY,
3265
+ y: Number.NEGATIVE_INFINITY
3266
+ }
3267
+ };
3268
+ for (let i = 0, iMax = geom.length; i < iMax; i++) {
3269
+ const poly = new PolyIn(geom[i], this);
3270
+ if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
3271
+ if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
3272
+ if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
3273
+ if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
3274
+ this.polys.push(poly);
3275
+ }
3276
+ this.isSubject = isSubject;
3277
+ }
3278
+ getSweepEvents() {
3279
+ const sweepEvents = [];
3280
+ for (let i = 0, iMax = this.polys.length; i < iMax; i++) {
3281
+ const polySweepEvents = this.polys[i].getSweepEvents();
3282
+ for (let j = 0, jMax = polySweepEvents.length; j < jMax; j++) sweepEvents.push(polySweepEvents[j]);
3283
+ }
3284
+ return sweepEvents;
3285
+ }
3286
+ }
3287
+ class RingOut {
3288
+ static factory(allSegments) {
3289
+ const ringsOut = [];
3290
+ for (let i = 0, iMax = allSegments.length; i < iMax; i++) {
3291
+ const segment = allSegments[i];
3292
+ if (!segment.isInResult() || segment.ringOut) continue;
3293
+ let prevEvent = null;
3294
+ let event = segment.leftSE;
3295
+ let nextEvent = segment.rightSE;
3296
+ const events = [event];
3297
+ const startingPoint = event.point;
3298
+ const intersectionLEs = [];
3299
+ while (true) {
3300
+ prevEvent = event;
3301
+ event = nextEvent;
3302
+ events.push(event);
3303
+ if (event.point === startingPoint) break;
3304
+ while (true) {
3305
+ const availableLEs = event.getAvailableLinkedEvents();
3306
+ if (availableLEs.length === 0) {
3307
+ const firstPt = events[0].point;
3308
+ const lastPt = events[events.length - 1].point;
3309
+ throw new Error(`Unable to complete output ring starting at [${firstPt.x}, ${firstPt.y}]. Last matching segment found ends at [${lastPt.x}, ${lastPt.y}].`);
3310
+ }
3311
+ if (availableLEs.length === 1) {
3312
+ nextEvent = availableLEs[0].otherSE;
3313
+ break;
3314
+ }
3315
+ let indexLE = null;
3316
+ for (let j = 0, jMax = intersectionLEs.length; j < jMax; j++) if (intersectionLEs[j].point === event.point) {
3317
+ indexLE = j;
3318
+ break;
3319
+ }
3320
+ if (indexLE !== null) {
3321
+ const intersectionLE = intersectionLEs.splice(indexLE)[0];
3322
+ const ringEvents = events.splice(intersectionLE.index);
3323
+ ringEvents.unshift(ringEvents[0].otherSE);
3324
+ ringsOut.push(new RingOut(ringEvents.reverse()));
3325
+ continue;
3326
+ }
3327
+ intersectionLEs.push({
3328
+ index: events.length,
3329
+ point: event.point
3330
+ });
3331
+ const comparator = event.getLeftmostComparator(prevEvent);
3332
+ nextEvent = availableLEs.sort(comparator)[0].otherSE;
3333
+ break;
3334
+ }
3335
+ }
3336
+ ringsOut.push(new RingOut(events));
3337
+ }
3338
+ return ringsOut;
3339
+ }
3340
+ constructor(events) {
3341
+ this.events = events;
3342
+ for (let i = 0, iMax = events.length; i < iMax; i++) events[i].segment.ringOut = this;
3343
+ this.poly = null;
3344
+ }
3345
+ getGeom() {
3346
+ let prevPt = this.events[0].point;
3347
+ const points = [prevPt];
3348
+ for (let i = 1, iMax = this.events.length - 1; i < iMax; i++) {
3349
+ const pt = this.events[i].point;
3350
+ const nextPt = this.events[i + 1].point;
3351
+ if (compareVectorAngles(pt, prevPt, nextPt) === 0) continue;
3352
+ points.push(pt);
3353
+ prevPt = pt;
3354
+ }
3355
+ if (points.length === 1) return null;
3356
+ const pt = points[0];
3357
+ const nextPt = points[1];
3358
+ if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
3359
+ points.push(points[0]);
3360
+ const step = this.isExteriorRing() ? 1 : -1;
3361
+ const iStart = this.isExteriorRing() ? 0 : points.length - 1;
3362
+ const iEnd = this.isExteriorRing() ? points.length : -1;
3363
+ const orderedPoints = [];
3364
+ for (let i = iStart; i != iEnd; i += step) orderedPoints.push([points[i].x, points[i].y]);
3365
+ return orderedPoints;
3366
+ }
3367
+ isExteriorRing() {
3368
+ if (this._isExteriorRing === void 0) {
3369
+ const enclosing = this.enclosingRing();
3370
+ this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
3371
+ }
3372
+ return this._isExteriorRing;
3373
+ }
3374
+ enclosingRing() {
3375
+ if (this._enclosingRing === void 0) this._enclosingRing = this._calcEnclosingRing();
3376
+ return this._enclosingRing;
3377
+ }
3378
+ _calcEnclosingRing() {
3379
+ let leftMostEvt = this.events[0];
3380
+ for (let i = 1, iMax = this.events.length; i < iMax; i++) {
3381
+ const evt = this.events[i];
3382
+ if (SweepEvent.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
3383
+ }
3384
+ let prevSeg = leftMostEvt.segment.prevInResult();
3385
+ let prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
3386
+ while (true) {
3387
+ if (!prevSeg) return null;
3388
+ if (!prevPrevSeg) return prevSeg.ringOut;
3389
+ if (prevPrevSeg.ringOut !== prevSeg.ringOut) if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) return prevSeg.ringOut;
3390
+ else return prevSeg.ringOut.enclosingRing();
3391
+ prevSeg = prevPrevSeg.prevInResult();
3392
+ prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
3393
+ }
3394
+ }
3395
+ }
3396
+ class PolyOut {
3397
+ constructor(exteriorRing) {
3398
+ this.exteriorRing = exteriorRing;
3399
+ exteriorRing.poly = this;
3400
+ this.interiorRings = [];
3401
+ }
3402
+ addInterior(ring) {
3403
+ this.interiorRings.push(ring);
3404
+ ring.poly = this;
3405
+ }
3406
+ getGeom() {
3407
+ const geom = [this.exteriorRing.getGeom()];
3408
+ if (geom[0] === null) return null;
3409
+ for (let i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
3410
+ const ringGeom = this.interiorRings[i].getGeom();
3411
+ if (ringGeom === null) continue;
3412
+ geom.push(ringGeom);
3413
+ }
3414
+ return geom;
3415
+ }
3416
+ }
3417
+ class MultiPolyOut {
3418
+ constructor(rings) {
3419
+ this.rings = rings;
3420
+ this.polys = this._composePolys(rings);
3421
+ }
3422
+ getGeom() {
3423
+ const geom = [];
3424
+ for (let i = 0, iMax = this.polys.length; i < iMax; i++) {
3425
+ const polyGeom = this.polys[i].getGeom();
3426
+ if (polyGeom === null) continue;
3427
+ geom.push(polyGeom);
3428
+ }
3429
+ return geom;
3430
+ }
3431
+ _composePolys(rings) {
3432
+ const polys = [];
3433
+ for (let i = 0, iMax = rings.length; i < iMax; i++) {
3434
+ const ring = rings[i];
3435
+ if (ring.poly) continue;
3436
+ if (ring.isExteriorRing()) polys.push(new PolyOut(ring));
3437
+ else {
3438
+ const enclosingRing = ring.enclosingRing();
3439
+ if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
3440
+ enclosingRing.poly.addInterior(ring);
3441
+ }
3442
+ }
3443
+ return polys;
3444
+ }
3445
+ }
3446
+ /**
3447
+ * NOTE: We must be careful not to change any segments while
3448
+ * they are in the SplayTree. AFAIK, there's no way to tell
3449
+ * the tree to rebalance itself - thus before splitting
3450
+ * a segment that's in the tree, we remove it from the tree,
3451
+ * do the split, then re-insert it. (Even though splitting a
3452
+ * segment *shouldn't* change its correct position in the
3453
+ * sweep line tree, the reality is because of rounding errors,
3454
+ * it sometimes does.)
3455
+ */
3456
+ class SweepLine {
3457
+ constructor(queue) {
3458
+ let comparator = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : Segment.compare;
3459
+ this.queue = queue;
3460
+ this.tree = new Tree(comparator);
3461
+ this.segments = [];
3462
+ }
3463
+ process(event) {
3464
+ const segment = event.segment;
3465
+ const newEvents = [];
3466
+ if (event.consumedBy) {
3467
+ if (event.isLeft) this.queue.remove(event.otherSE);
3468
+ else this.tree.remove(segment);
3469
+ return newEvents;
3470
+ }
3471
+ const node = event.isLeft ? this.tree.add(segment) : this.tree.find(segment);
3472
+ if (!node) throw new Error(`Unable to find segment #${segment.id} [${segment.leftSE.point.x}, ${segment.leftSE.point.y}] -> [${segment.rightSE.point.x}, ${segment.rightSE.point.y}] in SweepLine tree.`);
3473
+ let prevNode = node;
3474
+ let nextNode = node;
3475
+ let prevSeg = void 0;
3476
+ let nextSeg = void 0;
3477
+ while (prevSeg === void 0) {
3478
+ prevNode = this.tree.prev(prevNode);
3479
+ if (prevNode === null) prevSeg = null;
3480
+ else if (prevNode.key.consumedBy === void 0) prevSeg = prevNode.key;
3481
+ }
3482
+ while (nextSeg === void 0) {
3483
+ nextNode = this.tree.next(nextNode);
3484
+ if (nextNode === null) nextSeg = null;
3485
+ else if (nextNode.key.consumedBy === void 0) nextSeg = nextNode.key;
3486
+ }
3487
+ if (event.isLeft) {
3488
+ let prevMySplitter = null;
3489
+ if (prevSeg) {
3490
+ const prevInter = prevSeg.getIntersection(segment);
3491
+ if (prevInter !== null) {
3492
+ if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
3493
+ if (!prevSeg.isAnEndpoint(prevInter)) {
3494
+ const newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
3495
+ for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) newEvents.push(newEventsFromSplit[i]);
3496
+ }
3497
+ }
3498
+ }
3499
+ let nextMySplitter = null;
3500
+ if (nextSeg) {
3501
+ const nextInter = nextSeg.getIntersection(segment);
3502
+ if (nextInter !== null) {
3503
+ if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
3504
+ if (!nextSeg.isAnEndpoint(nextInter)) {
3505
+ const newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
3506
+ for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) newEvents.push(newEventsFromSplit[i]);
3507
+ }
3508
+ }
3509
+ }
3510
+ if (prevMySplitter !== null || nextMySplitter !== null) {
3511
+ let mySplitter = null;
3512
+ if (prevMySplitter === null) mySplitter = nextMySplitter;
3513
+ else if (nextMySplitter === null) mySplitter = prevMySplitter;
3514
+ else mySplitter = SweepEvent.comparePoints(prevMySplitter, nextMySplitter) <= 0 ? prevMySplitter : nextMySplitter;
3515
+ this.queue.remove(segment.rightSE);
3516
+ newEvents.push(segment.rightSE);
3517
+ const newEventsFromSplit = segment.split(mySplitter);
3518
+ for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) newEvents.push(newEventsFromSplit[i]);
3519
+ }
3520
+ if (newEvents.length > 0) {
3521
+ this.tree.remove(segment);
3522
+ newEvents.push(event);
3523
+ } else {
3524
+ this.segments.push(segment);
3525
+ segment.prev = prevSeg;
3526
+ }
3527
+ } else {
3528
+ if (prevSeg && nextSeg) {
3529
+ const inter = prevSeg.getIntersection(nextSeg);
3530
+ if (inter !== null) {
3531
+ if (!prevSeg.isAnEndpoint(inter)) {
3532
+ const newEventsFromSplit = this._splitSafely(prevSeg, inter);
3533
+ for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) newEvents.push(newEventsFromSplit[i]);
3534
+ }
3535
+ if (!nextSeg.isAnEndpoint(inter)) {
3536
+ const newEventsFromSplit = this._splitSafely(nextSeg, inter);
3537
+ for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) newEvents.push(newEventsFromSplit[i]);
3538
+ }
3539
+ }
3540
+ }
3541
+ this.tree.remove(segment);
3542
+ }
3543
+ return newEvents;
3544
+ }
3545
+ _splitSafely(seg, pt) {
3546
+ this.tree.remove(seg);
3547
+ const rightSE = seg.rightSE;
3548
+ this.queue.remove(rightSE);
3549
+ const newEvents = seg.split(pt);
3550
+ newEvents.push(rightSE);
3551
+ if (seg.consumedBy === void 0) this.tree.add(seg);
3552
+ return newEvents;
3553
+ }
3554
+ }
3555
+ const POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== "undefined" && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1e6;
3556
+ const POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== "undefined" && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1e6;
3557
+ class Operation {
3558
+ run(type, geom, moreGeoms) {
3559
+ operation.type = type;
3560
+ rounder.reset();
3561
+ const multipolys = [new MultiPolyIn(geom, true)];
3562
+ for (let i = 0, iMax = moreGeoms.length; i < iMax; i++) multipolys.push(new MultiPolyIn(moreGeoms[i], false));
3563
+ operation.numMultiPolys = multipolys.length;
3564
+ if (operation.type === "difference") {
3565
+ const subject = multipolys[0];
3566
+ let i = 1;
3567
+ while (i < multipolys.length) if (getBboxOverlap(multipolys[i].bbox, subject.bbox) !== null) i++;
3568
+ else multipolys.splice(i, 1);
3569
+ }
3570
+ if (operation.type === "intersection") for (let i = 0, iMax = multipolys.length; i < iMax; i++) {
3571
+ const mpA = multipolys[i];
3572
+ for (let j = i + 1, jMax = multipolys.length; j < jMax; j++) if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
3573
+ }
3574
+ const queue = new Tree(SweepEvent.compare);
3575
+ for (let i = 0, iMax = multipolys.length; i < iMax; i++) {
3576
+ const sweepEvents = multipolys[i].getSweepEvents();
3577
+ for (let j = 0, jMax = sweepEvents.length; j < jMax; j++) {
3578
+ queue.insert(sweepEvents[j]);
3579
+ if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) throw new Error("Infinite loop when putting segment endpoints in a priority queue (queue size too big).");
3580
+ }
3581
+ }
3582
+ const sweepLine = new SweepLine(queue);
3583
+ let prevQueueSize = queue.size;
3584
+ let node = queue.pop();
3585
+ while (node) {
3586
+ const evt = node.key;
3587
+ if (queue.size === prevQueueSize) {
3588
+ const seg = evt.segment;
3589
+ throw new Error(`Unable to pop() ${evt.isLeft ? "left" : "right"} SweepEvent [${evt.point.x}, ${evt.point.y}] from segment #${seg.id} [${seg.leftSE.point.x}, ${seg.leftSE.point.y}] -> [${seg.rightSE.point.x}, ${seg.rightSE.point.y}] from queue.`);
3590
+ }
3591
+ if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) throw new Error("Infinite loop when passing sweep line over endpoints (queue size too big).");
3592
+ if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) throw new Error("Infinite loop when passing sweep line over endpoints (too many sweep line segments).");
3593
+ const newEvents = sweepLine.process(evt);
3594
+ for (let i = 0, iMax = newEvents.length; i < iMax; i++) {
3595
+ const evt = newEvents[i];
3596
+ if (evt.consumedBy === void 0) queue.insert(evt);
3597
+ }
3598
+ prevQueueSize = queue.size;
3599
+ node = queue.pop();
3600
+ }
3601
+ rounder.reset();
3602
+ return new MultiPolyOut(RingOut.factory(sweepLine.segments)).getGeom();
3603
+ }
3604
+ }
3605
+ const operation = new Operation();
3606
+ const union = function(geom) {
3607
+ for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) moreGeoms[_key - 1] = arguments[_key];
3608
+ return operation.run("union", geom, moreGeoms);
3609
+ };
3610
+ const intersection = function(geom) {
3611
+ for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) moreGeoms[_key2 - 1] = arguments[_key2];
3612
+ return operation.run("intersection", geom, moreGeoms);
3613
+ };
3614
+ const xor = function(geom) {
3615
+ for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) moreGeoms[_key3 - 1] = arguments[_key3];
3616
+ return operation.run("xor", geom, moreGeoms);
3617
+ };
3618
+ const difference = function(subjectGeom) {
3619
+ for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) clippingGeoms[_key4 - 1] = arguments[_key4];
3620
+ return operation.run("difference", subjectGeom, clippingGeoms);
3621
+ };
3622
+ return {
3623
+ union,
3624
+ intersection,
3625
+ xor,
3626
+ difference
3627
+ };
3628
+ }));
3629
+ })))(), 1);
1662
3630
  function drawPoint(ctx, x, y, options = {}) {
1663
3631
  const { radius = 1 } = options;
1664
3632
  ctx.moveTo(x, y);
@@ -1926,6 +3894,50 @@ var BoundingBox = class BoundingBox {
1926
3894
  return new BoundingBox(this.left, this.top, this.width, this.height);
1927
3895
  }
1928
3896
  };
3897
+ function flatRingToPairs(r) {
3898
+ const ring = [];
3899
+ for (let i = 0; i < r.length; i += 2) ring.push([r[i], r[i + 1]]);
3900
+ const first = ring[0];
3901
+ const last = ring[ring.length - 1];
3902
+ if (first && last && (first[0] !== last[0] || first[1] !== last[1])) ring.push([first[0], first[1]]);
3903
+ return ring;
3904
+ }
3905
+ function ringsToGeom(rings) {
3906
+ const valid = rings.filter((r) => r.length >= 6).map(flatRingToPairs);
3907
+ if (!valid.length) return [];
3908
+ let geom = [[valid[0]]];
3909
+ for (let i = 1; i < valid.length; i++) geom = import_polygon_clipping_umd.default.xor(geom, [[valid[i]]]);
3910
+ return geom;
3911
+ }
3912
+ function geomToRings(geom) {
3913
+ const out = [];
3914
+ for (const poly of geom) for (const ring of poly) {
3915
+ const flat = [];
3916
+ for (const [x, y] of ring) flat.push(x, y);
3917
+ out.push(flat);
3918
+ }
3919
+ return out;
3920
+ }
3921
+ function polygonBoolean(op, ringsA, ringsB) {
3922
+ const a = ringsToGeom(ringsA);
3923
+ const b = ringsToGeom(ringsB);
3924
+ let res;
3925
+ switch (op) {
3926
+ case "union":
3927
+ res = import_polygon_clipping_umd.default.union(a, b);
3928
+ break;
3929
+ case "intersection":
3930
+ res = import_polygon_clipping_umd.default.intersection(a, b);
3931
+ break;
3932
+ case "difference":
3933
+ res = import_polygon_clipping_umd.default.difference(a, b);
3934
+ break;
3935
+ case "xor":
3936
+ res = import_polygon_clipping_umd.default.xor(a, b);
3937
+ break;
3938
+ }
3939
+ return geomToRings(res);
3940
+ }
1929
3941
  function catmullRom(t, p0, p1, p2, p3) {
1930
3942
  const v0 = (p2 - p0) * .5;
1931
3943
  const v1 = (p3 - p1) * .5;
@@ -2014,27 +4026,209 @@ function parseCssArg(name, value, context = {}) {
2014
4026
  result.normalizedIntValue = result.intValue;
2015
4027
  break;
2016
4028
  }
2017
- return result;
4029
+ return result;
4030
+ }
4031
+ function cubicBezierP0(t, p) {
4032
+ const k = 1 - t;
4033
+ return k * k * k * p;
4034
+ }
4035
+ function cubicBezierP1(t, p) {
4036
+ const k = 1 - t;
4037
+ return 3 * k * k * t * p;
4038
+ }
4039
+ function cubicBezierP2(t, p) {
4040
+ return 3 * (1 - t) * t * t * p;
4041
+ }
4042
+ function cubicBezierP3(t, p) {
4043
+ return t * t * t * p;
4044
+ }
4045
+ function cubicBezier(t, p0, p1, p2, p3) {
4046
+ return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
4047
+ }
4048
+ function isLeft(ax, ay, bx, by, px, py) {
4049
+ return (bx - ax) * (py - ay) - (px - ax) * (by - ay);
4050
+ }
4051
+ function windingNumber$1(px, py, vertices) {
4052
+ const len = vertices.length;
4053
+ let wn = 0;
4054
+ for (let i = 0; i < len; i += 2) {
4055
+ const ax = vertices[i];
4056
+ const ay = vertices[i + 1];
4057
+ const k = (i + 2) % len;
4058
+ const bx = vertices[k];
4059
+ const by = vertices[k + 1];
4060
+ if (ay <= py) {
4061
+ if (by > py && isLeft(ax, ay, bx, by, px, py) > 0) wn++;
4062
+ } else if (by <= py && isLeft(ax, ay, bx, by, px, py) < 0) wn--;
4063
+ }
4064
+ return wn;
4065
+ }
4066
+ function crossingNumber(px, py, vertices) {
4067
+ const len = vertices.length;
4068
+ let cn = 0;
4069
+ for (let i = 0; i < len; i += 2) {
4070
+ const ax = vertices[i];
4071
+ const ay = vertices[i + 1];
4072
+ const k = (i + 2) % len;
4073
+ const bx = vertices[k];
4074
+ const by = vertices[k + 1];
4075
+ if (ay <= py && by > py || ay > py && by <= py) {
4076
+ if (px < ax + (py - ay) / (by - ay) * (bx - ax)) cn++;
4077
+ }
4078
+ }
4079
+ return cn;
2018
4080
  }
2019
- function cubicBezierP0(t, p) {
2020
- const k = 1 - t;
2021
- return k * k * k * p;
4081
+ function segmentDistance(px, py, ax, ay, bx, by) {
4082
+ const dx = bx - ax;
4083
+ const dy = by - ay;
4084
+ const lenSq = dx * dx + dy * dy;
4085
+ let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
4086
+ if (t < 0) t = 0;
4087
+ else if (t > 1) t = 1;
4088
+ const cx = ax + t * dx;
4089
+ const cy = ay + t * dy;
4090
+ return Math.hypot(px - cx, py - cy);
2022
4091
  }
2023
- function cubicBezierP1(t, p) {
2024
- const k = 1 - t;
2025
- return 3 * k * k * t * p;
4092
+ function pointInPolygon(point, vertices, fillRule = "nonzero") {
4093
+ if (vertices.length < 6) return false;
4094
+ if (fillRule === "evenodd") return (crossingNumber(point.x, point.y, vertices) & 1) === 1;
4095
+ return windingNumber$1(point.x, point.y, vertices) !== 0;
2026
4096
  }
2027
- function cubicBezierP2(t, p) {
2028
- return 3 * (1 - t) * t * t * p;
4097
+ function pointInPolygons(point, polygons, fillRule = "nonzero") {
4098
+ const { x, y } = point;
4099
+ if (fillRule === "evenodd") {
4100
+ let cn = 0;
4101
+ for (let i = 0, len = polygons.length; i < len; i++) {
4102
+ const ring = polygons[i];
4103
+ if (ring.length >= 6) cn += crossingNumber(x, y, ring);
4104
+ }
4105
+ return (cn & 1) === 1;
4106
+ }
4107
+ let wn = 0;
4108
+ for (let i = 0, len = polygons.length; i < len; i++) {
4109
+ const ring = polygons[i];
4110
+ if (ring.length >= 6) wn += windingNumber$1(x, y, ring);
4111
+ }
4112
+ return wn !== 0;
2029
4113
  }
2030
- function cubicBezierP3(t, p) {
2031
- return t * t * t * p;
4114
+ function pointToPolylineDistance(point, vertices, closed = false) {
4115
+ const len = vertices.length;
4116
+ if (len < 2) return Infinity;
4117
+ const { x: px, y: py } = point;
4118
+ if (len === 2) return Math.hypot(px - vertices[0], py - vertices[1]);
4119
+ let min = Infinity;
4120
+ for (let i = 0; i < len - 2; i += 2) {
4121
+ const d = segmentDistance(px, py, vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]);
4122
+ if (d < min) min = d;
4123
+ }
4124
+ if (closed && len >= 6) {
4125
+ const d = segmentDistance(px, py, vertices[len - 2], vertices[len - 1], vertices[0], vertices[1]);
4126
+ if (d < min) min = d;
4127
+ }
4128
+ return min;
2032
4129
  }
2033
- function cubicBezier(t, p0, p1, p2, p3) {
2034
- return cubicBezierP0(t, p0) + cubicBezierP1(t, p1) + cubicBezierP2(t, p2) + cubicBezierP3(t, p3);
4130
+ function boundsOf(ring) {
4131
+ if (ring.length < 6) return null;
4132
+ let minX = Infinity;
4133
+ let minY = Infinity;
4134
+ let maxX = -Infinity;
4135
+ let maxY = -Infinity;
4136
+ for (let i = 0; i < ring.length; i += 2) {
4137
+ const x = ring[i];
4138
+ const y = ring[i + 1];
4139
+ if (x < minX) minX = x;
4140
+ if (y < minY) minY = y;
4141
+ if (x > maxX) maxX = x;
4142
+ if (y > maxY) maxY = y;
4143
+ }
4144
+ return {
4145
+ minX,
4146
+ minY,
4147
+ maxX,
4148
+ maxY
4149
+ };
4150
+ }
4151
+ function bboxInside(inner, outer) {
4152
+ return inner.minX >= outer.minX && inner.maxX <= outer.maxX && inner.minY >= outer.minY && inner.maxY <= outer.maxY;
4153
+ }
4154
+ function ringInsideRing(inner, outer) {
4155
+ const n = inner.length / 2;
4156
+ const step = Math.max(1, Math.floor(n / 9));
4157
+ let tested = 0;
4158
+ let inside = 0;
4159
+ for (let i = 0; i < n; i += step) {
4160
+ tested++;
4161
+ if (pointInPolygon({
4162
+ x: inner[i * 2],
4163
+ y: inner[i * 2 + 1]
4164
+ }, outer, "evenodd")) inside++;
4165
+ }
4166
+ return tested > 0 && inside * 2 > tested;
4167
+ }
4168
+ function evenoddFillRule(paths) {
4169
+ const len = paths.length;
4170
+ const bboxes = paths.map(boundsOf);
4171
+ const depth = Array.from({ length: len }).fill(0);
4172
+ const containers = paths.map(() => []);
4173
+ for (let i = 0; i < len; i++) {
4174
+ const bi = bboxes[i];
4175
+ if (!bi) continue;
4176
+ for (let j = 0; j < len; j++) {
4177
+ if (i === j) continue;
4178
+ const bj = bboxes[j];
4179
+ if (!bj || !bboxInside(bi, bj)) continue;
4180
+ if (ringInsideRing(paths[i], paths[j])) {
4181
+ depth[i]++;
4182
+ containers[i].push(j);
4183
+ }
4184
+ }
4185
+ }
4186
+ return paths.map((_, i) => {
4187
+ let parentIndex = -1;
4188
+ let bestDepth = -1;
4189
+ for (const j of containers[i]) if (depth[j] > bestDepth) {
4190
+ bestDepth = depth[j];
4191
+ parentIndex = j;
4192
+ }
4193
+ return {
4194
+ index: i,
4195
+ depth: depth[i],
4196
+ parentIndex
4197
+ };
4198
+ });
4199
+ }
4200
+ var fillDedupeEps = 1e-7;
4201
+ function dedupeContours(points, holeIndices, eps) {
4202
+ const total = points.length / 2;
4203
+ const bounds = [
4204
+ 0,
4205
+ ...holeIndices,
4206
+ total
4207
+ ];
4208
+ const out = [];
4209
+ const newHoles = [];
4210
+ for (let s = 0; s < bounds.length - 1; s++) {
4211
+ const start = bounds[s];
4212
+ const end = bounds[s + 1];
4213
+ if (s > 0) newHoles.push(out.length / 2);
4214
+ const segStart = out.length;
4215
+ for (let vi = start; vi < end; vi++) {
4216
+ const x = points[vi * 2];
4217
+ const y = points[vi * 2 + 1];
4218
+ const m = out.length;
4219
+ if (m > segStart && Math.abs(x - out[m - 2]) < eps && Math.abs(y - out[m - 1]) < eps) continue;
4220
+ out.push(x, y);
4221
+ }
4222
+ if (out.length - segStart >= 4 && Math.abs(out[out.length - 2] - out[segStart]) < eps && Math.abs(out[out.length - 1] - out[segStart + 1]) < eps) out.length -= 2;
4223
+ }
4224
+ return {
4225
+ points: out,
4226
+ holes: newHoles
4227
+ };
2035
4228
  }
2036
4229
  function fillTriangulate(pointArray, options = {}) {
2037
4230
  let { vertices = [], indices = [], holes = [], verticesStride = 2, verticesOffset = vertices.length / verticesStride, indicesOffset = indices.length } = options;
4231
+ ({points: pointArray, holes} = dedupeContours(pointArray, holes, fillDedupeEps));
2038
4232
  const triangles = earcut(pointArray, holes, 2);
2039
4233
  if (triangles.length) {
2040
4234
  for (let i = 0; i < triangles.length; i += 3) {
@@ -2150,7 +4344,7 @@ function recursive(points, x1, y1, x2, y2, x3, y3, distanceTolerance, level) {
2150
4344
  function cross(ax, ay, bx, by, cx, cy) {
2151
4345
  return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
2152
4346
  }
2153
- function windingNumber$1(px, py, polygon) {
4347
+ function windingNumber(px, py, polygon) {
2154
4348
  const polygonLen = polygon.length;
2155
4349
  let wn = 0;
2156
4350
  for (let i = 0, j = polygonLen - 2; i < polygonLen; j = i, i += 2) {
@@ -2169,6 +4363,12 @@ function distance(p1, p2) {
2169
4363
  const dy = p2[1] - p1[1];
2170
4364
  return Math.sqrt(dx * dx + dy * dy);
2171
4365
  }
4366
+ function selfWindingSign(ring) {
4367
+ let a = 0;
4368
+ const n = ring.length;
4369
+ for (let i = 0, j = n - 2; i < n; j = i, i += 2) a += ring[j] * ring[i + 1] - ring[i] * ring[j + 1];
4370
+ return Math.sign(a);
4371
+ }
2172
4372
  function aabbIntersects(a, b) {
2173
4373
  return a.minX <= b.maxX && a.maxX >= b.minX && a.minY <= b.maxY && a.maxY >= b.minY;
2174
4374
  }
@@ -2253,7 +4453,7 @@ function nonzeroFillRule(paths) {
2253
4453
  const wnList = [];
2254
4454
  for (let p = 0, pLen = testPoints.length; p < pLen; p++) {
2255
4455
  const [x, y] = testPoints[p];
2256
- const winding = windingNumber$1(x, y, paths[j]);
4456
+ const winding = windingNumber(x, y, paths[j]);
2257
4457
  wnMap[winding] = (wnMap[winding] ?? 0) + 1;
2258
4458
  wnList.push(winding);
2259
4459
  }
@@ -2264,95 +4464,14 @@ function nonzeroFillRule(paths) {
2264
4464
  dist: distance(testPointsGroups[i][0], testPointsGroups[j][0])
2265
4465
  });
2266
4466
  }
2267
- if (_results.reduce((total, item) => total + item.winding, 0) !== 0) {
4467
+ const containerWinding = _results.reduce((total, item) => total + item.winding, 0);
4468
+ if (_results.length && containerWinding + selfWindingSign(paths[i]) === 0) {
2268
4469
  _results.sort((a, b) => a.dist - b.dist);
2269
4470
  results[i] = _results[0];
2270
4471
  }
2271
4472
  }
2272
4473
  return results;
2273
4474
  }
2274
- function isLeft(ax, ay, bx, by, px, py) {
2275
- return (bx - ax) * (py - ay) - (px - ax) * (by - ay);
2276
- }
2277
- function windingNumber(px, py, vertices) {
2278
- const len = vertices.length;
2279
- let wn = 0;
2280
- for (let i = 0; i < len; i += 2) {
2281
- const ax = vertices[i];
2282
- const ay = vertices[i + 1];
2283
- const k = (i + 2) % len;
2284
- const bx = vertices[k];
2285
- const by = vertices[k + 1];
2286
- if (ay <= py) {
2287
- if (by > py && isLeft(ax, ay, bx, by, px, py) > 0) wn++;
2288
- } else if (by <= py && isLeft(ax, ay, bx, by, px, py) < 0) wn--;
2289
- }
2290
- return wn;
2291
- }
2292
- function crossingNumber(px, py, vertices) {
2293
- const len = vertices.length;
2294
- let cn = 0;
2295
- for (let i = 0; i < len; i += 2) {
2296
- const ax = vertices[i];
2297
- const ay = vertices[i + 1];
2298
- const k = (i + 2) % len;
2299
- const bx = vertices[k];
2300
- const by = vertices[k + 1];
2301
- if (ay <= py && by > py || ay > py && by <= py) {
2302
- if (px < ax + (py - ay) / (by - ay) * (bx - ax)) cn++;
2303
- }
2304
- }
2305
- return cn;
2306
- }
2307
- function segmentDistance(px, py, ax, ay, bx, by) {
2308
- const dx = bx - ax;
2309
- const dy = by - ay;
2310
- const lenSq = dx * dx + dy * dy;
2311
- let t = lenSq === 0 ? 0 : ((px - ax) * dx + (py - ay) * dy) / lenSq;
2312
- if (t < 0) t = 0;
2313
- else if (t > 1) t = 1;
2314
- const cx = ax + t * dx;
2315
- const cy = ay + t * dy;
2316
- return Math.hypot(px - cx, py - cy);
2317
- }
2318
- function pointInPolygon(point, vertices, fillRule = "nonzero") {
2319
- if (vertices.length < 6) return false;
2320
- if (fillRule === "evenodd") return (crossingNumber(point.x, point.y, vertices) & 1) === 1;
2321
- return windingNumber(point.x, point.y, vertices) !== 0;
2322
- }
2323
- function pointInPolygons(point, polygons, fillRule = "nonzero") {
2324
- const { x, y } = point;
2325
- if (fillRule === "evenodd") {
2326
- let cn = 0;
2327
- for (let i = 0, len = polygons.length; i < len; i++) {
2328
- const ring = polygons[i];
2329
- if (ring.length >= 6) cn += crossingNumber(x, y, ring);
2330
- }
2331
- return (cn & 1) === 1;
2332
- }
2333
- let wn = 0;
2334
- for (let i = 0, len = polygons.length; i < len; i++) {
2335
- const ring = polygons[i];
2336
- if (ring.length >= 6) wn += windingNumber(x, y, ring);
2337
- }
2338
- return wn !== 0;
2339
- }
2340
- function pointToPolylineDistance(point, vertices, closed = false) {
2341
- const len = vertices.length;
2342
- if (len < 2) return Infinity;
2343
- const { x: px, y: py } = point;
2344
- if (len === 2) return Math.hypot(px - vertices[0], py - vertices[1]);
2345
- let min = Infinity;
2346
- for (let i = 0; i < len - 2; i += 2) {
2347
- const d = segmentDistance(px, py, vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3]);
2348
- if (d < min) min = d;
2349
- }
2350
- if (closed && len >= 6) {
2351
- const d = segmentDistance(px, py, vertices[len - 2], vertices[len - 1], vertices[0], vertices[1]);
2352
- if (d < min) min = d;
2353
- }
2354
- return min;
2355
- }
2356
4475
  function quadraticBezierP0(t, p) {
2357
4476
  const k = 1 - t;
2358
4477
  return k * k * p;
@@ -2366,21 +4485,38 @@ function quadraticBezierP2(t, p) {
2366
4485
  function quadraticBezier(t, p0, p1, p2) {
2367
4486
  return quadraticBezierP0(t, p0) + quadraticBezierP1(t, p1) + quadraticBezierP2(t, p2);
2368
4487
  }
4488
+ function resolveLineJoin(join) {
4489
+ switch (join) {
4490
+ case "round":
4491
+ case "bevel":
4492
+ case "miter": return join;
4493
+ default: return "miter";
4494
+ }
4495
+ }
4496
+ function resolveLineStyle(style) {
4497
+ return {
4498
+ width: style?.strokeWidth ?? 1,
4499
+ alignment: .5,
4500
+ join: resolveLineJoin(style?.strokeLinejoin),
4501
+ cap: style?.strokeLinecap ?? "butt",
4502
+ miterLimit: style?.strokeMiterlimit ?? 10
4503
+ };
4504
+ }
2369
4505
  var closePointEps = 1e-4;
2370
4506
  var curveEps = 1e-4;
2371
4507
  function strokeTriangulate(points, options = {}) {
2372
- const { vertices = [], indices = [], lineStyle = {
2373
- alignment: .5,
2374
- cap: "butt",
2375
- join: "miter",
2376
- width: 1,
2377
- miterLimit: 10
2378
- }, flipAlignment = false, closed = true } = options;
4508
+ const { vertices = [], indices = [], flipAlignment = false, closed = true } = options;
4509
+ const lineStyle = options.lineStyle ?? resolveLineStyle(options.style);
2379
4510
  const eps = closePointEps;
2380
4511
  if (points.length === 0) return {
2381
4512
  vertices,
2382
4513
  indices
2383
4514
  };
4515
+ points = dedupeConsecutivePoints(points, eps);
4516
+ if (points.length < 4) return {
4517
+ vertices,
4518
+ indices
4519
+ };
2384
4520
  const style = lineStyle;
2385
4521
  let alignment = style.alignment;
2386
4522
  if (lineStyle.alignment !== .5) {
@@ -2575,6 +4711,15 @@ function strokeTriangulate(points, options = {}) {
2575
4711
  indices
2576
4712
  };
2577
4713
  }
4714
+ function dedupeConsecutivePoints(points, eps) {
4715
+ const out = [points[0], points[1]];
4716
+ for (let i = 2; i < points.length; i += 2) {
4717
+ const x = points[i];
4718
+ const y = points[i + 1];
4719
+ if (Math.abs(x - out[out.length - 2]) >= eps || Math.abs(y - out[out.length - 1]) >= eps) out.push(x, y);
4720
+ }
4721
+ return out;
4722
+ }
2578
4723
  function getOrientationOfPoints(points) {
2579
4724
  const m = points.length;
2580
4725
  if (m < 6) return 1;
@@ -3900,6 +6045,20 @@ var Curve = class {
3900
6045
  getControlPointRefs() {
3901
6046
  return [];
3902
6047
  }
6048
+ /**
6049
+ * Reverse the traversal direction in place (start ↔ end, same geometry). The base
6050
+ * implementation reverses the order of the control-point *values*, which is correct for
6051
+ * line / Bézier / spline primitives whose {@link getControlPointRefs} order matches their
6052
+ * parametric order. {@link RoundCurve} (angle-based) and composites (child order) override it.
6053
+ */
6054
+ reverse() {
6055
+ const refs = this.getControlPointRefs();
6056
+ const n = refs.length;
6057
+ const snapshot = refs.map((p) => p.clone());
6058
+ for (let i = 0; i < n; i++) refs[i].copyFrom(snapshot[n - 1 - i]);
6059
+ this.invalidate();
6060
+ return this;
6061
+ }
3903
6062
  applyTransform(transform) {
3904
6063
  const isFunction = typeof transform === "function";
3905
6064
  this.getControlPointRefs().forEach((p) => {
@@ -4012,6 +6171,22 @@ var Curve = class {
4012
6171
  getTangentAt(u, output) {
4013
6172
  return this.getTangent(this.getUToTMapping(u), output);
4014
6173
  }
6174
+ /**
6175
+ * PathKit-style sample at an absolute arc-length `distance` along the curve: the point, the unit
6176
+ * tangent, and the tangent `angle` in radians. `distance` is clamped to `[0, getLength()]`, so
6177
+ * passing `0`/`getLength()` always yields the endpoints. See {@link PathMeasure} for a wrapper.
6178
+ */
6179
+ getPosTan(distance) {
6180
+ const length = this.getLength();
6181
+ const u = length > 0 ? Math.min(Math.max(distance / length, 0), 1) : 0;
6182
+ const t = this.getUToTMapping(u);
6183
+ const tangent = this.getTangent(t);
6184
+ return {
6185
+ position: this.getPoint(t),
6186
+ tangent,
6187
+ angle: Math.atan2(tangent.y, tangent.x)
6188
+ };
6189
+ }
4015
6190
  getNormal(t, output = new Vector2()) {
4016
6191
  this.getTangent(t, output);
4017
6192
  return output.set(-output.y, output.x).normalize();
@@ -4099,8 +6274,24 @@ var Curve = class {
4099
6274
  fillTriangulate(options) {
4100
6275
  return fillTriangulate(this.getFillVertices(options), options);
4101
6276
  }
6277
+ /**
6278
+ * Whether this curve forms a closed loop (its outline should be stroked without end caps,
6279
+ * stitching the last vertex back to the first). The base test is purely geometric — the first
6280
+ * sampled vertex coincides with the last. Curves that close without a duplicated endpoint
6281
+ * (a full-revolution {@link RoundCurve}, rectangles, polygons) override this.
6282
+ */
6283
+ isClosed() {
6284
+ const v = this._getCachedAdaptiveVertices();
6285
+ const len = v.length;
6286
+ if (len < 6) return false;
6287
+ const eps = 1e-4;
6288
+ return Math.abs(v[0] - v[len - 2]) < eps && Math.abs(v[1] - v[len - 1]) < eps;
6289
+ }
4102
6290
  strokeTriangulate(options) {
4103
- return strokeTriangulate(this.getAdaptiveVertices(), options);
6291
+ return strokeTriangulate(this.getAdaptiveVertices(), {
6292
+ ...options,
6293
+ closed: options?.closed ?? this.isClosed()
6294
+ });
4104
6295
  }
4105
6296
  toCommands() {
4106
6297
  const comds = [];
@@ -4198,6 +6389,22 @@ var RoundCurve = class extends Curve {
4198
6389
  isClockwise() {
4199
6390
  return this.clockwise;
4200
6391
  }
6392
+ /**
6393
+ * A circle/ellipse arc is closed when it sweeps (at least) a full revolution — the sampled
6394
+ * outline does not duplicate the start vertex, so the geometric first==last test in the base
6395
+ * class would wrongly report a full circle as open and leave a seam gap in the stroke.
6396
+ */
6397
+ isClosed() {
6398
+ return Math.abs(this.endAngle - this.startAngle) >= Math.PI * 2 - 1e-9 || super.isClosed();
6399
+ }
6400
+ reverse() {
6401
+ const { startAngle, endAngle } = this;
6402
+ this.startAngle = endAngle;
6403
+ this.endAngle = startAngle;
6404
+ this.clockwise = !this.clockwise;
6405
+ this.invalidate();
6406
+ return this;
6407
+ }
4201
6408
  _getDeltaAngle() {
4202
6409
  const PI_2 = Math.PI * 2;
4203
6410
  let deltaAngle = this.endAngle - this.startAngle;
@@ -4462,7 +6669,18 @@ var RoundCurve = class extends Curve {
4462
6669
  return output;
4463
6670
  }
4464
6671
  getAdaptiveVertices(output = []) {
4465
- if (this.startAngle === 0 && this.endAngle === Math.PI * 2) return this._getAdaptiveVerticesByCircle(output);
6672
+ const PI2 = Math.PI * 2;
6673
+ if (this.startAngle === 0 && this.endAngle === PI2) return this._getAdaptiveVerticesByCircle(output);
6674
+ if (Math.abs(this.endAngle - this.startAngle) >= PI2 - 1e-9) {
6675
+ const tmp = this._getAdaptiveVerticesByCircle([]);
6676
+ const n = tmp.length / 2;
6677
+ if (this.endAngle > this.startAngle) for (let i = 0; i < tmp.length; i++) output.push(tmp[i]);
6678
+ else {
6679
+ output.push(tmp[0], tmp[1]);
6680
+ for (let i = n - 1; i >= 1; i--) output.push(tmp[i * 2], tmp[i * 2 + 1]);
6681
+ }
6682
+ return output;
6683
+ }
4466
6684
  return this._getAdaptiveVerticesByArc(output);
4467
6685
  }
4468
6686
  copyFrom(source) {
@@ -4627,6 +6845,13 @@ var LineCurve = class LineCurve extends Curve {
4627
6845
  getControlPointRefs() {
4628
6846
  return [this.p1, this.p2];
4629
6847
  }
6848
+ reverse() {
6849
+ const { p1, p2 } = this;
6850
+ this.p1 = p2;
6851
+ this.p2 = p1;
6852
+ this.invalidate();
6853
+ return this;
6854
+ }
4630
6855
  getAdaptiveVertices(output = []) {
4631
6856
  output.push(this.p1.x, this.p1.y, this.p2.x, this.p2.y);
4632
6857
  return output;
@@ -4775,10 +7000,25 @@ var CompositeCurve = class CompositeCurve extends Curve {
4775
7000
  });
4776
7001
  return output;
4777
7002
  }
7003
+ /**
7004
+ * A composite is closed when its single child is closed (e.g. a lone full-circle arc), or when
7005
+ * its assembled outline returns to its start (rectangles, polygons, multi-segment loops).
7006
+ */
7007
+ isClosed() {
7008
+ if (this.curves.length === 1) return this.curves[0].isClosed();
7009
+ return super.isClosed();
7010
+ }
4778
7011
  strokeTriangulate(options) {
4779
7012
  if (this.curves.length === 1) return this.curves[0].strokeTriangulate(options);
4780
7013
  else return super.strokeTriangulate(options);
4781
7014
  }
7015
+ /** Reverse the sub-curve order and reverse each sub-curve, so the whole outline runs backwards. */
7016
+ reverse() {
7017
+ this.curves.reverse();
7018
+ this.curves.forEach((curve) => curve.reverse());
7019
+ this.invalidate();
7020
+ return this;
7021
+ }
4782
7022
  getFillVertices(options) {
4783
7023
  if (this.curves.length === 1) return this.curves[0].getFillVertices(options);
4784
7024
  else {
@@ -4854,6 +7094,15 @@ var CubicBezierCurve = class CubicBezierCurve extends Curve {
4854
7094
  this.p2
4855
7095
  ];
4856
7096
  }
7097
+ reverse() {
7098
+ const { p1, cp1, cp2, p2 } = this;
7099
+ this.p1 = p2;
7100
+ this.cp1 = cp2;
7101
+ this.cp2 = cp1;
7102
+ this.p2 = p1;
7103
+ this.invalidate();
7104
+ return this;
7105
+ }
4857
7106
  _solveQuadratic(a, b, c) {
4858
7107
  if (Math.abs(a) < 1e-12) {
4859
7108
  if (Math.abs(b) < 1e-12) return [];
@@ -4950,6 +7199,13 @@ var QuadraticBezierCurve = class QuadraticBezierCurve extends Curve {
4950
7199
  this.p2
4951
7200
  ];
4952
7201
  }
7202
+ reverse() {
7203
+ const { p1, p2 } = this;
7204
+ this.p1 = p2;
7205
+ this.p2 = p1;
7206
+ this.invalidate();
7207
+ return this;
7208
+ }
4953
7209
  getAdaptiveVertices(output = []) {
4954
7210
  return getAdaptiveQuadraticBezierCurvePoints(this.p1.x, this.p1.y, this.cp.x, this.cp.y, this.p2.x, this.p2.y, .5, output);
4955
7211
  }
@@ -5134,6 +7390,11 @@ var SplineCurve = class extends Curve {
5134
7390
  getControlPointRefs() {
5135
7391
  return this.points;
5136
7392
  }
7393
+ reverse() {
7394
+ this.points.reverse();
7395
+ this.invalidate();
7396
+ return this;
7397
+ }
5137
7398
  copyFrom(source) {
5138
7399
  super.copyFrom(source);
5139
7400
  this.points = [];
@@ -5165,6 +7426,22 @@ var CurvePath = class extends CompositeCurve {
5165
7426
  this.addCommands(svgPathDataToCommands(data));
5166
7427
  return this;
5167
7428
  }
7429
+ /**
7430
+ * A sub-path is closed if it was explicitly closed (`autoClose`, i.e. a `Z`/`closePath`), or if
7431
+ * it forms a geometric loop / wraps a single closed primitive (handled by the base class).
7432
+ */
7433
+ isClosed() {
7434
+ return this.autoClose || super.isClosed();
7435
+ }
7436
+ /** Reverse direction, then refresh the {@link startPoint}/{@link currentPoint} cursors. */
7437
+ reverse() {
7438
+ super.reverse();
7439
+ if (this.curves.length) {
7440
+ this.startPoint = this.getPoint(0);
7441
+ this.currentPoint = this.getPoint(1);
7442
+ }
7443
+ return this;
7444
+ }
5168
7445
  _closeVertices(output) {
5169
7446
  if (this.autoClose && output.length >= 4 && output[0] !== output[output.length - 2] && output[1] !== output[output.length - 1]) output.push(output[0], output[1]);
5170
7447
  return output;
@@ -5540,6 +7817,48 @@ var Path2D = class Path2D extends CompositeCurve {
5540
7817
  const fillRule = options.fillRule ?? this.style.fillRule ?? "nonzero";
5541
7818
  return pointInPolygons(point, this._getRings(), fillRule);
5542
7819
  }
7820
+ /** Build a `Path2D` from flat rings (`[x0,y0,…]` per sub-path); closed-and-filled as sub-paths. */
7821
+ static fromRings(rings, style = {}) {
7822
+ const path = new Path2D(void 0, style);
7823
+ for (const ring of rings) {
7824
+ if (ring.length < 6) continue;
7825
+ let end = ring.length;
7826
+ if (ring[0] === ring[end - 2] && ring[1] === ring[end - 1]) end -= 2;
7827
+ path.moveTo(ring[0], ring[1]);
7828
+ for (let i = 2; i < end; i += 2) path.lineTo(ring[i], ring[i + 1]);
7829
+ path.closePath();
7830
+ }
7831
+ return path;
7832
+ }
7833
+ /**
7834
+ * Boolean (path) operation against another path, returning a NEW `Path2D` whose outline is the
7835
+ * polygonal result. Curves are sampled before clipping, so the result is a polygonal
7836
+ * approximation (see {@link polygonBoolean}). The result inherits this path's `style` unless
7837
+ * overridden via `style`. Holes are emitted as oppositely-wound sub-paths (nonzero fill).
7838
+ */
7839
+ booleanOp(op, other, style) {
7840
+ const rings = polygonBoolean(op, this._getRings(), other._getRings());
7841
+ return Path2D.fromRings(rings, {
7842
+ ...this.style,
7843
+ ...style
7844
+ });
7845
+ }
7846
+ /** `this ∪ other` — the combined filled area. */
7847
+ union(other, style) {
7848
+ return this.booleanOp("union", other, style);
7849
+ }
7850
+ /** `this ∩ other` — only the overlapping area. */
7851
+ intersection(other, style) {
7852
+ return this.booleanOp("intersection", other, style);
7853
+ }
7854
+ /** `this − other` — this path with `other` cut away. */
7855
+ difference(other, style) {
7856
+ return this.booleanOp("difference", other, style);
7857
+ }
7858
+ /** `this ⊕ other` — areas covered by exactly one of the two paths. */
7859
+ xor(other, style) {
7860
+ return this.booleanOp("xor", other, style);
7861
+ }
5543
7862
  /**
5544
7863
  * Test whether a point lies on this path's stroke. A hit on any sub-path counts.
5545
7864
  *
@@ -5599,7 +7918,7 @@ var Path2D = class Path2D extends CompositeCurve {
5599
7918
  };
5600
7919
  const indices = _options.indices ?? [];
5601
7920
  const vertices = _options.vertices ?? [];
5602
- if ((_options.style.fillRule ?? "nonzero") === "nonzero") {
7921
+ if ((options?.fillRule ?? _options.style.fillRule ?? "nonzero") === "nonzero") {
5603
7922
  const paths = this.curves.map((curve) => curve.getFillVertices(_options));
5604
7923
  const groups = nonzeroFillRule(paths);
5605
7924
  const groupsLen = groups.length;
@@ -5624,14 +7943,28 @@ var Path2D = class Path2D extends CompositeCurve {
5624
7943
  style: { ...this.style }
5625
7944
  });
5626
7945
  }
5627
- } else this.curves.forEach((curve) => {
5628
- curve.fillTriangulate({
5629
- ...options,
5630
- indices,
5631
- vertices,
5632
- style: { ...this.style }
5633
- });
5634
- });
7946
+ } else {
7947
+ const paths = this.curves.map((curve) => curve.getFillVertices(_options));
7948
+ const groups = evenoddFillRule(paths);
7949
+ const groupsLen = groups.length;
7950
+ for (let i = 0; i < groupsLen; i++) {
7951
+ const pointArray = paths[i];
7952
+ if ((groups[i].depth & 1) === 1 || !pointArray.length) continue;
7953
+ const _pointArray = pointArray.slice();
7954
+ const holes = [];
7955
+ for (let j = 0; j < groupsLen; j++) if (groups[j].parentIndex === i && (groups[j].depth & 1) === 1) {
7956
+ holes.push(_pointArray.length / 2);
7957
+ _pointArray.push(...paths[j]);
7958
+ }
7959
+ fillTriangulate(_pointArray, {
7960
+ ...options,
7961
+ indices,
7962
+ vertices,
7963
+ holes,
7964
+ style: { ...this.style }
7965
+ });
7966
+ }
7967
+ }
5635
7968
  return {
5636
7969
  indices,
5637
7970
  vertices
@@ -5646,14 +7979,14 @@ var Path2D = class Path2D extends CompositeCurve {
5646
7979
  ...this.style,
5647
7980
  ...style
5648
7981
  };
5649
- const { fill = "#000", stroke = "none" } = style;
7982
+ const { fill = "#000", stroke = "none", fillRule = "nonzero" } = style;
5650
7983
  ctx.beginPath();
5651
7984
  ctx.save();
5652
7985
  setCanvasContext(ctx, style);
5653
7986
  this.curves.forEach((path) => {
5654
7987
  path.drawTo(ctx);
5655
7988
  });
5656
- if (fill !== "none") ctx.fill();
7989
+ if (fill !== "none") ctx.fill(fillRule);
5657
7990
  if (stroke !== "none") ctx.stroke();
5658
7991
  ctx.restore();
5659
7992
  return this;