@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.
- package/dist/index.js +2452 -119
- 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.
|
|
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
|
|
2020
|
-
const
|
|
2021
|
-
|
|
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
|
|
2024
|
-
|
|
2025
|
-
|
|
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
|
|
2028
|
-
|
|
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
|
|
2031
|
-
|
|
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
|
|
2034
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 = [],
|
|
2373
|
-
|
|
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(),
|
|
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
|
-
|
|
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
|
|
5628
|
-
curve.
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
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;
|