polygon-clipping.umd.js 87 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.polygonClipping = factory());
  5. })(this, (function () { 'use strict';
  6. /**
  7. * splaytree v3.1.2
  8. * Fast Splay tree for Node and browser
  9. *
  10. * @author Alexander Milevski <info@w8r.name>
  11. * @license MIT
  12. * @preserve
  13. */
  14. /*! *****************************************************************************
  15. Copyright (c) Microsoft Corporation. All rights reserved.
  16. Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  17. this file except in compliance with the License. You may obtain a copy of the
  18. License at http://www.apache.org/licenses/LICENSE-2.0
  19. THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  20. KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  21. WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  22. MERCHANTABLITY OR NON-INFRINGEMENT.
  23. See the Apache Version 2.0 License for specific language governing permissions
  24. and limitations under the License.
  25. ***************************************************************************** */
  26. function __generator(thisArg, body) {
  27. var _ = {
  28. label: 0,
  29. sent: function () {
  30. if (t[0] & 1) throw t[1];
  31. return t[1];
  32. },
  33. trys: [],
  34. ops: []
  35. },
  36. f,
  37. y,
  38. t,
  39. g;
  40. return g = {
  41. next: verb(0),
  42. "throw": verb(1),
  43. "return": verb(2)
  44. }, typeof Symbol === "function" && (g[Symbol.iterator] = function () {
  45. return this;
  46. }), g;
  47. function verb(n) {
  48. return function (v) {
  49. return step([n, v]);
  50. };
  51. }
  52. function step(op) {
  53. if (f) throw new TypeError("Generator is already executing.");
  54. while (_) try {
  55. 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;
  56. if (y = 0, t) op = [op[0] & 2, t.value];
  57. switch (op[0]) {
  58. case 0:
  59. case 1:
  60. t = op;
  61. break;
  62. case 4:
  63. _.label++;
  64. return {
  65. value: op[1],
  66. done: false
  67. };
  68. case 5:
  69. _.label++;
  70. y = op[1];
  71. op = [0];
  72. continue;
  73. case 7:
  74. op = _.ops.pop();
  75. _.trys.pop();
  76. continue;
  77. default:
  78. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
  79. _ = 0;
  80. continue;
  81. }
  82. if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
  83. _.label = op[1];
  84. break;
  85. }
  86. if (op[0] === 6 && _.label < t[1]) {
  87. _.label = t[1];
  88. t = op;
  89. break;
  90. }
  91. if (t && _.label < t[2]) {
  92. _.label = t[2];
  93. _.ops.push(op);
  94. break;
  95. }
  96. if (t[2]) _.ops.pop();
  97. _.trys.pop();
  98. continue;
  99. }
  100. op = body.call(thisArg, _);
  101. } catch (e) {
  102. op = [6, e];
  103. y = 0;
  104. } finally {
  105. f = t = 0;
  106. }
  107. if (op[0] & 5) throw op[1];
  108. return {
  109. value: op[0] ? op[1] : void 0,
  110. done: true
  111. };
  112. }
  113. }
  114. var Node = /** @class */function () {
  115. function Node(key, data) {
  116. this.next = null;
  117. this.key = key;
  118. this.data = data;
  119. this.left = null;
  120. this.right = null;
  121. }
  122. return Node;
  123. }();
  124. /* follows "An implementation of top-down splaying"
  125. * by D. Sleator <sleator@cs.cmu.edu> March 1992
  126. */
  127. function DEFAULT_COMPARE(a, b) {
  128. return a > b ? 1 : a < b ? -1 : 0;
  129. }
  130. /**
  131. * Simple top down splay, not requiring i to be in the tree t.
  132. */
  133. function splay(i, t, comparator) {
  134. var N = new Node(null, null);
  135. var l = N;
  136. var r = N;
  137. while (true) {
  138. var cmp = comparator(i, t.key);
  139. //if (i < t.key) {
  140. if (cmp < 0) {
  141. if (t.left === null) break;
  142. //if (i < t.left.key) {
  143. if (comparator(i, t.left.key) < 0) {
  144. var y = t.left; /* rotate right */
  145. t.left = y.right;
  146. y.right = t;
  147. t = y;
  148. if (t.left === null) break;
  149. }
  150. r.left = t; /* link right */
  151. r = t;
  152. t = t.left;
  153. //} else if (i > t.key) {
  154. } else if (cmp > 0) {
  155. if (t.right === null) break;
  156. //if (i > t.right.key) {
  157. if (comparator(i, t.right.key) > 0) {
  158. var y = t.right; /* rotate left */
  159. t.right = y.left;
  160. y.left = t;
  161. t = y;
  162. if (t.right === null) break;
  163. }
  164. l.right = t; /* link left */
  165. l = t;
  166. t = t.right;
  167. } else break;
  168. }
  169. /* assemble */
  170. l.right = t.left;
  171. r.left = t.right;
  172. t.left = N.right;
  173. t.right = N.left;
  174. return t;
  175. }
  176. function insert(i, data, t, comparator) {
  177. var node = new Node(i, data);
  178. if (t === null) {
  179. node.left = node.right = null;
  180. return node;
  181. }
  182. t = splay(i, t, comparator);
  183. var cmp = comparator(i, t.key);
  184. if (cmp < 0) {
  185. node.left = t.left;
  186. node.right = t;
  187. t.left = null;
  188. } else if (cmp >= 0) {
  189. node.right = t.right;
  190. node.left = t;
  191. t.right = null;
  192. }
  193. return node;
  194. }
  195. function split(key, v, comparator) {
  196. var left = null;
  197. var right = null;
  198. if (v) {
  199. v = splay(key, v, comparator);
  200. var cmp = comparator(v.key, key);
  201. if (cmp === 0) {
  202. left = v.left;
  203. right = v.right;
  204. } else if (cmp < 0) {
  205. right = v.right;
  206. v.right = null;
  207. left = v;
  208. } else {
  209. left = v.left;
  210. v.left = null;
  211. right = v;
  212. }
  213. }
  214. return {
  215. left: left,
  216. right: right
  217. };
  218. }
  219. function merge(left, right, comparator) {
  220. if (right === null) return left;
  221. if (left === null) return right;
  222. right = splay(left.key, right, comparator);
  223. right.left = left;
  224. return right;
  225. }
  226. /**
  227. * Prints level of the tree
  228. */
  229. function printRow(root, prefix, isTail, out, printNode) {
  230. if (root) {
  231. out("" + prefix + (isTail ? '└── ' : '├── ') + printNode(root) + "\n");
  232. var indent = prefix + (isTail ? ' ' : '│ ');
  233. if (root.left) printRow(root.left, indent, false, out, printNode);
  234. if (root.right) printRow(root.right, indent, true, out, printNode);
  235. }
  236. }
  237. var Tree = /** @class */function () {
  238. function Tree(comparator) {
  239. if (comparator === void 0) {
  240. comparator = DEFAULT_COMPARE;
  241. }
  242. this._root = null;
  243. this._size = 0;
  244. this._comparator = comparator;
  245. }
  246. /**
  247. * Inserts a key, allows duplicates
  248. */
  249. Tree.prototype.insert = function (key, data) {
  250. this._size++;
  251. return this._root = insert(key, data, this._root, this._comparator);
  252. };
  253. /**
  254. * Adds a key, if it is not present in the tree
  255. */
  256. Tree.prototype.add = function (key, data) {
  257. var node = new Node(key, data);
  258. if (this._root === null) {
  259. node.left = node.right = null;
  260. this._size++;
  261. this._root = node;
  262. }
  263. var comparator = this._comparator;
  264. var t = splay(key, this._root, comparator);
  265. var cmp = comparator(key, t.key);
  266. if (cmp === 0) this._root = t;else {
  267. if (cmp < 0) {
  268. node.left = t.left;
  269. node.right = t;
  270. t.left = null;
  271. } else if (cmp > 0) {
  272. node.right = t.right;
  273. node.left = t;
  274. t.right = null;
  275. }
  276. this._size++;
  277. this._root = node;
  278. }
  279. return this._root;
  280. };
  281. /**
  282. * @param {Key} key
  283. * @return {Node|null}
  284. */
  285. Tree.prototype.remove = function (key) {
  286. this._root = this._remove(key, this._root, this._comparator);
  287. };
  288. /**
  289. * Deletes i from the tree if it's there
  290. */
  291. Tree.prototype._remove = function (i, t, comparator) {
  292. var x;
  293. if (t === null) return null;
  294. t = splay(i, t, comparator);
  295. var cmp = comparator(i, t.key);
  296. if (cmp === 0) {
  297. /* found it */
  298. if (t.left === null) {
  299. x = t.right;
  300. } else {
  301. x = splay(i, t.left, comparator);
  302. x.right = t.right;
  303. }
  304. this._size--;
  305. return x;
  306. }
  307. return t; /* It wasn't there */
  308. };
  309. /**
  310. * Removes and returns the node with smallest key
  311. */
  312. Tree.prototype.pop = function () {
  313. var node = this._root;
  314. if (node) {
  315. while (node.left) node = node.left;
  316. this._root = splay(node.key, this._root, this._comparator);
  317. this._root = this._remove(node.key, this._root, this._comparator);
  318. return {
  319. key: node.key,
  320. data: node.data
  321. };
  322. }
  323. return null;
  324. };
  325. /**
  326. * Find without splaying
  327. */
  328. Tree.prototype.findStatic = function (key) {
  329. var current = this._root;
  330. var compare = this._comparator;
  331. while (current) {
  332. var cmp = compare(key, current.key);
  333. if (cmp === 0) return current;else if (cmp < 0) current = current.left;else current = current.right;
  334. }
  335. return null;
  336. };
  337. Tree.prototype.find = function (key) {
  338. if (this._root) {
  339. this._root = splay(key, this._root, this._comparator);
  340. if (this._comparator(key, this._root.key) !== 0) return null;
  341. }
  342. return this._root;
  343. };
  344. Tree.prototype.contains = function (key) {
  345. var current = this._root;
  346. var compare = this._comparator;
  347. while (current) {
  348. var cmp = compare(key, current.key);
  349. if (cmp === 0) return true;else if (cmp < 0) current = current.left;else current = current.right;
  350. }
  351. return false;
  352. };
  353. Tree.prototype.forEach = function (visitor, ctx) {
  354. var current = this._root;
  355. var Q = []; /* Initialize stack s */
  356. var done = false;
  357. while (!done) {
  358. if (current !== null) {
  359. Q.push(current);
  360. current = current.left;
  361. } else {
  362. if (Q.length !== 0) {
  363. current = Q.pop();
  364. visitor.call(ctx, current);
  365. current = current.right;
  366. } else done = true;
  367. }
  368. }
  369. return this;
  370. };
  371. /**
  372. * Walk key range from `low` to `high`. Stops if `fn` returns a value.
  373. */
  374. Tree.prototype.range = function (low, high, fn, ctx) {
  375. var Q = [];
  376. var compare = this._comparator;
  377. var node = this._root;
  378. var cmp;
  379. while (Q.length !== 0 || node) {
  380. if (node) {
  381. Q.push(node);
  382. node = node.left;
  383. } else {
  384. node = Q.pop();
  385. cmp = compare(node.key, high);
  386. if (cmp > 0) {
  387. break;
  388. } else if (compare(node.key, low) >= 0) {
  389. if (fn.call(ctx, node)) return this; // stop if smth is returned
  390. }
  391. node = node.right;
  392. }
  393. }
  394. return this;
  395. };
  396. /**
  397. * Returns array of keys
  398. */
  399. Tree.prototype.keys = function () {
  400. var keys = [];
  401. this.forEach(function (_a) {
  402. var key = _a.key;
  403. return keys.push(key);
  404. });
  405. return keys;
  406. };
  407. /**
  408. * Returns array of all the data in the nodes
  409. */
  410. Tree.prototype.values = function () {
  411. var values = [];
  412. this.forEach(function (_a) {
  413. var data = _a.data;
  414. return values.push(data);
  415. });
  416. return values;
  417. };
  418. Tree.prototype.min = function () {
  419. if (this._root) return this.minNode(this._root).key;
  420. return null;
  421. };
  422. Tree.prototype.max = function () {
  423. if (this._root) return this.maxNode(this._root).key;
  424. return null;
  425. };
  426. Tree.prototype.minNode = function (t) {
  427. if (t === void 0) {
  428. t = this._root;
  429. }
  430. if (t) while (t.left) t = t.left;
  431. return t;
  432. };
  433. Tree.prototype.maxNode = function (t) {
  434. if (t === void 0) {
  435. t = this._root;
  436. }
  437. if (t) while (t.right) t = t.right;
  438. return t;
  439. };
  440. /**
  441. * Returns node at given index
  442. */
  443. Tree.prototype.at = function (index) {
  444. var current = this._root;
  445. var done = false;
  446. var i = 0;
  447. var Q = [];
  448. while (!done) {
  449. if (current) {
  450. Q.push(current);
  451. current = current.left;
  452. } else {
  453. if (Q.length > 0) {
  454. current = Q.pop();
  455. if (i === index) return current;
  456. i++;
  457. current = current.right;
  458. } else done = true;
  459. }
  460. }
  461. return null;
  462. };
  463. Tree.prototype.next = function (d) {
  464. var root = this._root;
  465. var successor = null;
  466. if (d.right) {
  467. successor = d.right;
  468. while (successor.left) successor = successor.left;
  469. return successor;
  470. }
  471. var comparator = this._comparator;
  472. while (root) {
  473. var cmp = comparator(d.key, root.key);
  474. if (cmp === 0) break;else if (cmp < 0) {
  475. successor = root;
  476. root = root.left;
  477. } else root = root.right;
  478. }
  479. return successor;
  480. };
  481. Tree.prototype.prev = function (d) {
  482. var root = this._root;
  483. var predecessor = null;
  484. if (d.left !== null) {
  485. predecessor = d.left;
  486. while (predecessor.right) predecessor = predecessor.right;
  487. return predecessor;
  488. }
  489. var comparator = this._comparator;
  490. while (root) {
  491. var cmp = comparator(d.key, root.key);
  492. if (cmp === 0) break;else if (cmp < 0) root = root.left;else {
  493. predecessor = root;
  494. root = root.right;
  495. }
  496. }
  497. return predecessor;
  498. };
  499. Tree.prototype.clear = function () {
  500. this._root = null;
  501. this._size = 0;
  502. return this;
  503. };
  504. Tree.prototype.toList = function () {
  505. return toList(this._root);
  506. };
  507. /**
  508. * Bulk-load items. Both array have to be same size
  509. */
  510. Tree.prototype.load = function (keys, values, presort) {
  511. if (values === void 0) {
  512. values = [];
  513. }
  514. if (presort === void 0) {
  515. presort = false;
  516. }
  517. var size = keys.length;
  518. var comparator = this._comparator;
  519. // sort if needed
  520. if (presort) sort(keys, values, 0, size - 1, comparator);
  521. if (this._root === null) {
  522. // empty tree
  523. this._root = loadRecursive(keys, values, 0, size);
  524. this._size = size;
  525. } else {
  526. // that re-builds the whole tree from two in-order traversals
  527. var mergedList = mergeLists(this.toList(), createList(keys, values), comparator);
  528. size = this._size + size;
  529. this._root = sortedListToBST({
  530. head: mergedList
  531. }, 0, size);
  532. }
  533. return this;
  534. };
  535. Tree.prototype.isEmpty = function () {
  536. return this._root === null;
  537. };
  538. Object.defineProperty(Tree.prototype, "size", {
  539. get: function () {
  540. return this._size;
  541. },
  542. enumerable: true,
  543. configurable: true
  544. });
  545. Object.defineProperty(Tree.prototype, "root", {
  546. get: function () {
  547. return this._root;
  548. },
  549. enumerable: true,
  550. configurable: true
  551. });
  552. Tree.prototype.toString = function (printNode) {
  553. if (printNode === void 0) {
  554. printNode = function (n) {
  555. return String(n.key);
  556. };
  557. }
  558. var out = [];
  559. printRow(this._root, '', true, function (v) {
  560. return out.push(v);
  561. }, printNode);
  562. return out.join('');
  563. };
  564. Tree.prototype.update = function (key, newKey, newData) {
  565. var comparator = this._comparator;
  566. var _a = split(key, this._root, comparator),
  567. left = _a.left,
  568. right = _a.right;
  569. if (comparator(key, newKey) < 0) {
  570. right = insert(newKey, newData, right, comparator);
  571. } else {
  572. left = insert(newKey, newData, left, comparator);
  573. }
  574. this._root = merge(left, right, comparator);
  575. };
  576. Tree.prototype.split = function (key) {
  577. return split(key, this._root, this._comparator);
  578. };
  579. Tree.prototype[Symbol.iterator] = function () {
  580. var current, Q, done;
  581. return __generator(this, function (_a) {
  582. switch (_a.label) {
  583. case 0:
  584. current = this._root;
  585. Q = [];
  586. done = false;
  587. _a.label = 1;
  588. case 1:
  589. if (!!done) return [3 /*break*/, 6];
  590. if (!(current !== null)) return [3 /*break*/, 2];
  591. Q.push(current);
  592. current = current.left;
  593. return [3 /*break*/, 5];
  594. case 2:
  595. if (!(Q.length !== 0)) return [3 /*break*/, 4];
  596. current = Q.pop();
  597. return [4 /*yield*/, current];
  598. case 3:
  599. _a.sent();
  600. current = current.right;
  601. return [3 /*break*/, 5];
  602. case 4:
  603. done = true;
  604. _a.label = 5;
  605. case 5:
  606. return [3 /*break*/, 1];
  607. case 6:
  608. return [2 /*return*/];
  609. }
  610. });
  611. };
  612. return Tree;
  613. }();
  614. function loadRecursive(keys, values, start, end) {
  615. var size = end - start;
  616. if (size > 0) {
  617. var middle = start + Math.floor(size / 2);
  618. var key = keys[middle];
  619. var data = values[middle];
  620. var node = new Node(key, data);
  621. node.left = loadRecursive(keys, values, start, middle);
  622. node.right = loadRecursive(keys, values, middle + 1, end);
  623. return node;
  624. }
  625. return null;
  626. }
  627. function createList(keys, values) {
  628. var head = new Node(null, null);
  629. var p = head;
  630. for (var i = 0; i < keys.length; i++) {
  631. p = p.next = new Node(keys[i], values[i]);
  632. }
  633. p.next = null;
  634. return head.next;
  635. }
  636. function toList(root) {
  637. var current = root;
  638. var Q = [];
  639. var done = false;
  640. var head = new Node(null, null);
  641. var p = head;
  642. while (!done) {
  643. if (current) {
  644. Q.push(current);
  645. current = current.left;
  646. } else {
  647. if (Q.length > 0) {
  648. current = p = p.next = Q.pop();
  649. current = current.right;
  650. } else done = true;
  651. }
  652. }
  653. p.next = null; // that'll work even if the tree was empty
  654. return head.next;
  655. }
  656. function sortedListToBST(list, start, end) {
  657. var size = end - start;
  658. if (size > 0) {
  659. var middle = start + Math.floor(size / 2);
  660. var left = sortedListToBST(list, start, middle);
  661. var root = list.head;
  662. root.left = left;
  663. list.head = list.head.next;
  664. root.right = sortedListToBST(list, middle + 1, end);
  665. return root;
  666. }
  667. return null;
  668. }
  669. function mergeLists(l1, l2, compare) {
  670. var head = new Node(null, null); // dummy
  671. var p = head;
  672. var p1 = l1;
  673. var p2 = l2;
  674. while (p1 !== null && p2 !== null) {
  675. if (compare(p1.key, p2.key) < 0) {
  676. p.next = p1;
  677. p1 = p1.next;
  678. } else {
  679. p.next = p2;
  680. p2 = p2.next;
  681. }
  682. p = p.next;
  683. }
  684. if (p1 !== null) {
  685. p.next = p1;
  686. } else if (p2 !== null) {
  687. p.next = p2;
  688. }
  689. return head.next;
  690. }
  691. function sort(keys, values, left, right, compare) {
  692. if (left >= right) return;
  693. var pivot = keys[left + right >> 1];
  694. var i = left - 1;
  695. var j = right + 1;
  696. while (true) {
  697. do i++; while (compare(keys[i], pivot) < 0);
  698. do j--; while (compare(keys[j], pivot) > 0);
  699. if (i >= j) break;
  700. var tmp = keys[i];
  701. keys[i] = keys[j];
  702. keys[j] = tmp;
  703. tmp = values[i];
  704. values[i] = values[j];
  705. values[j] = tmp;
  706. }
  707. sort(keys, values, left, j, compare);
  708. sort(keys, values, j + 1, right, compare);
  709. }
  710. /**
  711. * A bounding box has the format:
  712. *
  713. * { ll: { x: xmin, y: ymin }, ur: { x: xmax, y: ymax } }
  714. *
  715. */
  716. const isInBbox = (bbox, point) => {
  717. return bbox.ll.x <= point.x && point.x <= bbox.ur.x && bbox.ll.y <= point.y && point.y <= bbox.ur.y;
  718. };
  719. /* Returns either null, or a bbox (aka an ordered pair of points)
  720. * If there is only one point of overlap, a bbox with identical points
  721. * will be returned */
  722. const getBboxOverlap = (b1, b2) => {
  723. // check if the bboxes overlap at all
  724. 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;
  725. // find the middle two X values
  726. const lowerX = b1.ll.x < b2.ll.x ? b2.ll.x : b1.ll.x;
  727. const upperX = b1.ur.x < b2.ur.x ? b1.ur.x : b2.ur.x;
  728. // find the middle two Y values
  729. const lowerY = b1.ll.y < b2.ll.y ? b2.ll.y : b1.ll.y;
  730. const upperY = b1.ur.y < b2.ur.y ? b1.ur.y : b2.ur.y;
  731. // put those middle values together to get the overlap
  732. return {
  733. ll: {
  734. x: lowerX,
  735. y: lowerY
  736. },
  737. ur: {
  738. x: upperX,
  739. y: upperY
  740. }
  741. };
  742. };
  743. /* Javascript doesn't do integer math. Everything is
  744. * floating point with percision Number.EPSILON.
  745. *
  746. * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON
  747. */
  748. let epsilon$1 = Number.EPSILON;
  749. // IE Polyfill
  750. if (epsilon$1 === undefined) epsilon$1 = Math.pow(2, -52);
  751. const EPSILON_SQ = epsilon$1 * epsilon$1;
  752. /* FLP comparator */
  753. const cmp = (a, b) => {
  754. // check if they're both 0
  755. if (-epsilon$1 < a && a < epsilon$1) {
  756. if (-epsilon$1 < b && b < epsilon$1) {
  757. return 0;
  758. }
  759. }
  760. // check if they're flp equal
  761. const ab = a - b;
  762. if (ab * ab < EPSILON_SQ * a * b) {
  763. return 0;
  764. }
  765. // normal comparison
  766. return a < b ? -1 : 1;
  767. };
  768. /**
  769. * This class rounds incoming values sufficiently so that
  770. * floating points problems are, for the most part, avoided.
  771. *
  772. * Incoming points are have their x & y values tested against
  773. * all previously seen x & y values. If either is 'too close'
  774. * to a previously seen value, it's value is 'snapped' to the
  775. * previously seen value.
  776. *
  777. * All points should be rounded by this class before being
  778. * stored in any data structures in the rest of this algorithm.
  779. */
  780. class PtRounder {
  781. constructor() {
  782. this.reset();
  783. }
  784. reset() {
  785. this.xRounder = new CoordRounder();
  786. this.yRounder = new CoordRounder();
  787. }
  788. round(x, y) {
  789. return {
  790. x: this.xRounder.round(x),
  791. y: this.yRounder.round(y)
  792. };
  793. }
  794. }
  795. class CoordRounder {
  796. constructor() {
  797. this.tree = new Tree();
  798. // preseed with 0 so we don't end up with values < Number.EPSILON
  799. this.round(0);
  800. }
  801. // Note: this can rounds input values backwards or forwards.
  802. // You might ask, why not restrict this to just rounding
  803. // forwards? Wouldn't that allow left endpoints to always
  804. // remain left endpoints during splitting (never change to
  805. // right). No - it wouldn't, because we snap intersections
  806. // to endpoints (to establish independence from the segment
  807. // angle for t-intersections).
  808. round(coord) {
  809. const node = this.tree.add(coord);
  810. const prevNode = this.tree.prev(node);
  811. if (prevNode !== null && cmp(node.key, prevNode.key) === 0) {
  812. this.tree.remove(coord);
  813. return prevNode.key;
  814. }
  815. const nextNode = this.tree.next(node);
  816. if (nextNode !== null && cmp(node.key, nextNode.key) === 0) {
  817. this.tree.remove(coord);
  818. return nextNode.key;
  819. }
  820. return coord;
  821. }
  822. }
  823. // singleton available by import
  824. const rounder = new PtRounder();
  825. const epsilon = 1.1102230246251565e-16;
  826. const splitter = 134217729;
  827. const resulterrbound = (3 + 8 * epsilon) * epsilon;
  828. // fast_expansion_sum_zeroelim routine from oritinal code
  829. function sum(elen, e, flen, f, h) {
  830. let Q, Qnew, hh, bvirt;
  831. let enow = e[0];
  832. let fnow = f[0];
  833. let eindex = 0;
  834. let findex = 0;
  835. if (fnow > enow === fnow > -enow) {
  836. Q = enow;
  837. enow = e[++eindex];
  838. } else {
  839. Q = fnow;
  840. fnow = f[++findex];
  841. }
  842. let hindex = 0;
  843. if (eindex < elen && findex < flen) {
  844. if (fnow > enow === fnow > -enow) {
  845. Qnew = enow + Q;
  846. hh = Q - (Qnew - enow);
  847. enow = e[++eindex];
  848. } else {
  849. Qnew = fnow + Q;
  850. hh = Q - (Qnew - fnow);
  851. fnow = f[++findex];
  852. }
  853. Q = Qnew;
  854. if (hh !== 0) {
  855. h[hindex++] = hh;
  856. }
  857. while (eindex < elen && findex < flen) {
  858. if (fnow > enow === fnow > -enow) {
  859. Qnew = Q + enow;
  860. bvirt = Qnew - Q;
  861. hh = Q - (Qnew - bvirt) + (enow - bvirt);
  862. enow = e[++eindex];
  863. } else {
  864. Qnew = Q + fnow;
  865. bvirt = Qnew - Q;
  866. hh = Q - (Qnew - bvirt) + (fnow - bvirt);
  867. fnow = f[++findex];
  868. }
  869. Q = Qnew;
  870. if (hh !== 0) {
  871. h[hindex++] = hh;
  872. }
  873. }
  874. }
  875. while (eindex < elen) {
  876. Qnew = Q + enow;
  877. bvirt = Qnew - Q;
  878. hh = Q - (Qnew - bvirt) + (enow - bvirt);
  879. enow = e[++eindex];
  880. Q = Qnew;
  881. if (hh !== 0) {
  882. h[hindex++] = hh;
  883. }
  884. }
  885. while (findex < flen) {
  886. Qnew = Q + fnow;
  887. bvirt = Qnew - Q;
  888. hh = Q - (Qnew - bvirt) + (fnow - bvirt);
  889. fnow = f[++findex];
  890. Q = Qnew;
  891. if (hh !== 0) {
  892. h[hindex++] = hh;
  893. }
  894. }
  895. if (Q !== 0 || hindex === 0) {
  896. h[hindex++] = Q;
  897. }
  898. return hindex;
  899. }
  900. function estimate(elen, e) {
  901. let Q = e[0];
  902. for (let i = 1; i < elen; i++) Q += e[i];
  903. return Q;
  904. }
  905. function vec(n) {
  906. return new Float64Array(n);
  907. }
  908. const ccwerrboundA = (3 + 16 * epsilon) * epsilon;
  909. const ccwerrboundB = (2 + 12 * epsilon) * epsilon;
  910. const ccwerrboundC = (9 + 64 * epsilon) * epsilon * epsilon;
  911. const B = vec(4);
  912. const C1 = vec(8);
  913. const C2 = vec(12);
  914. const D = vec(16);
  915. const u = vec(4);
  916. function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
  917. let acxtail, acytail, bcxtail, bcytail;
  918. let bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
  919. const acx = ax - cx;
  920. const bcx = bx - cx;
  921. const acy = ay - cy;
  922. const bcy = by - cy;
  923. s1 = acx * bcy;
  924. c = splitter * acx;
  925. ahi = c - (c - acx);
  926. alo = acx - ahi;
  927. c = splitter * bcy;
  928. bhi = c - (c - bcy);
  929. blo = bcy - bhi;
  930. s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
  931. t1 = acy * bcx;
  932. c = splitter * acy;
  933. ahi = c - (c - acy);
  934. alo = acy - ahi;
  935. c = splitter * bcx;
  936. bhi = c - (c - bcx);
  937. blo = bcx - bhi;
  938. t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
  939. _i = s0 - t0;
  940. bvirt = s0 - _i;
  941. B[0] = s0 - (_i + bvirt) + (bvirt - t0);
  942. _j = s1 + _i;
  943. bvirt = _j - s1;
  944. _0 = s1 - (_j - bvirt) + (_i - bvirt);
  945. _i = _0 - t1;
  946. bvirt = _0 - _i;
  947. B[1] = _0 - (_i + bvirt) + (bvirt - t1);
  948. u3 = _j + _i;
  949. bvirt = u3 - _j;
  950. B[2] = _j - (u3 - bvirt) + (_i - bvirt);
  951. B[3] = u3;
  952. let det = estimate(4, B);
  953. let errbound = ccwerrboundB * detsum;
  954. if (det >= errbound || -det >= errbound) {
  955. return det;
  956. }
  957. bvirt = ax - acx;
  958. acxtail = ax - (acx + bvirt) + (bvirt - cx);
  959. bvirt = bx - bcx;
  960. bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
  961. bvirt = ay - acy;
  962. acytail = ay - (acy + bvirt) + (bvirt - cy);
  963. bvirt = by - bcy;
  964. bcytail = by - (bcy + bvirt) + (bvirt - cy);
  965. if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
  966. return det;
  967. }
  968. errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
  969. det += acx * bcytail + bcy * acxtail - (acy * bcxtail + bcx * acytail);
  970. if (det >= errbound || -det >= errbound) return det;
  971. s1 = acxtail * bcy;
  972. c = splitter * acxtail;
  973. ahi = c - (c - acxtail);
  974. alo = acxtail - ahi;
  975. c = splitter * bcy;
  976. bhi = c - (c - bcy);
  977. blo = bcy - bhi;
  978. s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
  979. t1 = acytail * bcx;
  980. c = splitter * acytail;
  981. ahi = c - (c - acytail);
  982. alo = acytail - ahi;
  983. c = splitter * bcx;
  984. bhi = c - (c - bcx);
  985. blo = bcx - bhi;
  986. t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
  987. _i = s0 - t0;
  988. bvirt = s0 - _i;
  989. u[0] = s0 - (_i + bvirt) + (bvirt - t0);
  990. _j = s1 + _i;
  991. bvirt = _j - s1;
  992. _0 = s1 - (_j - bvirt) + (_i - bvirt);
  993. _i = _0 - t1;
  994. bvirt = _0 - _i;
  995. u[1] = _0 - (_i + bvirt) + (bvirt - t1);
  996. u3 = _j + _i;
  997. bvirt = u3 - _j;
  998. u[2] = _j - (u3 - bvirt) + (_i - bvirt);
  999. u[3] = u3;
  1000. const C1len = sum(4, B, 4, u, C1);
  1001. s1 = acx * bcytail;
  1002. c = splitter * acx;
  1003. ahi = c - (c - acx);
  1004. alo = acx - ahi;
  1005. c = splitter * bcytail;
  1006. bhi = c - (c - bcytail);
  1007. blo = bcytail - bhi;
  1008. s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
  1009. t1 = acy * bcxtail;
  1010. c = splitter * acy;
  1011. ahi = c - (c - acy);
  1012. alo = acy - ahi;
  1013. c = splitter * bcxtail;
  1014. bhi = c - (c - bcxtail);
  1015. blo = bcxtail - bhi;
  1016. t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
  1017. _i = s0 - t0;
  1018. bvirt = s0 - _i;
  1019. u[0] = s0 - (_i + bvirt) + (bvirt - t0);
  1020. _j = s1 + _i;
  1021. bvirt = _j - s1;
  1022. _0 = s1 - (_j - bvirt) + (_i - bvirt);
  1023. _i = _0 - t1;
  1024. bvirt = _0 - _i;
  1025. u[1] = _0 - (_i + bvirt) + (bvirt - t1);
  1026. u3 = _j + _i;
  1027. bvirt = u3 - _j;
  1028. u[2] = _j - (u3 - bvirt) + (_i - bvirt);
  1029. u[3] = u3;
  1030. const C2len = sum(C1len, C1, 4, u, C2);
  1031. s1 = acxtail * bcytail;
  1032. c = splitter * acxtail;
  1033. ahi = c - (c - acxtail);
  1034. alo = acxtail - ahi;
  1035. c = splitter * bcytail;
  1036. bhi = c - (c - bcytail);
  1037. blo = bcytail - bhi;
  1038. s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
  1039. t1 = acytail * bcxtail;
  1040. c = splitter * acytail;
  1041. ahi = c - (c - acytail);
  1042. alo = acytail - ahi;
  1043. c = splitter * bcxtail;
  1044. bhi = c - (c - bcxtail);
  1045. blo = bcxtail - bhi;
  1046. t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
  1047. _i = s0 - t0;
  1048. bvirt = s0 - _i;
  1049. u[0] = s0 - (_i + bvirt) + (bvirt - t0);
  1050. _j = s1 + _i;
  1051. bvirt = _j - s1;
  1052. _0 = s1 - (_j - bvirt) + (_i - bvirt);
  1053. _i = _0 - t1;
  1054. bvirt = _0 - _i;
  1055. u[1] = _0 - (_i + bvirt) + (bvirt - t1);
  1056. u3 = _j + _i;
  1057. bvirt = u3 - _j;
  1058. u[2] = _j - (u3 - bvirt) + (_i - bvirt);
  1059. u[3] = u3;
  1060. const Dlen = sum(C2len, C2, 4, u, D);
  1061. return D[Dlen - 1];
  1062. }
  1063. function orient2d(ax, ay, bx, by, cx, cy) {
  1064. const detleft = (ay - cy) * (bx - cx);
  1065. const detright = (ax - cx) * (by - cy);
  1066. const det = detleft - detright;
  1067. const detsum = Math.abs(detleft + detright);
  1068. if (Math.abs(det) >= ccwerrboundA * detsum) return det;
  1069. return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
  1070. }
  1071. /* Cross Product of two vectors with first point at origin */
  1072. const crossProduct = (a, b) => a.x * b.y - a.y * b.x;
  1073. /* Dot Product of two vectors with first point at origin */
  1074. const dotProduct = (a, b) => a.x * b.x + a.y * b.y;
  1075. /* Comparator for two vectors with same starting point */
  1076. const compareVectorAngles = (basePt, endPt1, endPt2) => {
  1077. const res = orient2d(basePt.x, basePt.y, endPt1.x, endPt1.y, endPt2.x, endPt2.y);
  1078. if (res > 0) return -1;
  1079. if (res < 0) return 1;
  1080. return 0;
  1081. };
  1082. const length = v => Math.sqrt(dotProduct(v, v));
  1083. /* Get the sine of the angle from pShared -> pAngle to pShaed -> pBase */
  1084. const sineOfAngle = (pShared, pBase, pAngle) => {
  1085. const vBase = {
  1086. x: pBase.x - pShared.x,
  1087. y: pBase.y - pShared.y
  1088. };
  1089. const vAngle = {
  1090. x: pAngle.x - pShared.x,
  1091. y: pAngle.y - pShared.y
  1092. };
  1093. return crossProduct(vAngle, vBase) / length(vAngle) / length(vBase);
  1094. };
  1095. /* Get the cosine of the angle from pShared -> pAngle to pShaed -> pBase */
  1096. const cosineOfAngle = (pShared, pBase, pAngle) => {
  1097. const vBase = {
  1098. x: pBase.x - pShared.x,
  1099. y: pBase.y - pShared.y
  1100. };
  1101. const vAngle = {
  1102. x: pAngle.x - pShared.x,
  1103. y: pAngle.y - pShared.y
  1104. };
  1105. return dotProduct(vAngle, vBase) / length(vAngle) / length(vBase);
  1106. };
  1107. /* Get the x coordinate where the given line (defined by a point and vector)
  1108. * crosses the horizontal line with the given y coordiante.
  1109. * In the case of parrallel lines (including overlapping ones) returns null. */
  1110. const horizontalIntersection = (pt, v, y) => {
  1111. if (v.y === 0) return null;
  1112. return {
  1113. x: pt.x + v.x / v.y * (y - pt.y),
  1114. y: y
  1115. };
  1116. };
  1117. /* Get the y coordinate where the given line (defined by a point and vector)
  1118. * crosses the vertical line with the given x coordiante.
  1119. * In the case of parrallel lines (including overlapping ones) returns null. */
  1120. const verticalIntersection = (pt, v, x) => {
  1121. if (v.x === 0) return null;
  1122. return {
  1123. x: x,
  1124. y: pt.y + v.y / v.x * (x - pt.x)
  1125. };
  1126. };
  1127. /* Get the intersection of two lines, each defined by a base point and a vector.
  1128. * In the case of parrallel lines (including overlapping ones) returns null. */
  1129. const intersection$1 = (pt1, v1, pt2, v2) => {
  1130. // take some shortcuts for vertical and horizontal lines
  1131. // this also ensures we don't calculate an intersection and then discover
  1132. // it's actually outside the bounding box of the line
  1133. if (v1.x === 0) return verticalIntersection(pt2, v2, pt1.x);
  1134. if (v2.x === 0) return verticalIntersection(pt1, v1, pt2.x);
  1135. if (v1.y === 0) return horizontalIntersection(pt2, v2, pt1.y);
  1136. if (v2.y === 0) return horizontalIntersection(pt1, v1, pt2.y);
  1137. // General case for non-overlapping segments.
  1138. // This algorithm is based on Schneider and Eberly.
  1139. // http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf - pg 244
  1140. const kross = crossProduct(v1, v2);
  1141. if (kross == 0) return null;
  1142. const ve = {
  1143. x: pt2.x - pt1.x,
  1144. y: pt2.y - pt1.y
  1145. };
  1146. const d1 = crossProduct(ve, v1) / kross;
  1147. const d2 = crossProduct(ve, v2) / kross;
  1148. // take the average of the two calculations to minimize rounding error
  1149. const x1 = pt1.x + d2 * v1.x,
  1150. x2 = pt2.x + d1 * v2.x;
  1151. const y1 = pt1.y + d2 * v1.y,
  1152. y2 = pt2.y + d1 * v2.y;
  1153. const x = (x1 + x2) / 2;
  1154. const y = (y1 + y2) / 2;
  1155. return {
  1156. x: x,
  1157. y: y
  1158. };
  1159. };
  1160. class SweepEvent {
  1161. // for ordering sweep events in the sweep event queue
  1162. static compare(a, b) {
  1163. // favor event with a point that the sweep line hits first
  1164. const ptCmp = SweepEvent.comparePoints(a.point, b.point);
  1165. if (ptCmp !== 0) return ptCmp;
  1166. // the points are the same, so link them if needed
  1167. if (a.point !== b.point) a.link(b);
  1168. // favor right events over left
  1169. if (a.isLeft !== b.isLeft) return a.isLeft ? 1 : -1;
  1170. // we have two matching left or right endpoints
  1171. // ordering of this case is the same as for their segments
  1172. return Segment.compare(a.segment, b.segment);
  1173. }
  1174. // for ordering points in sweep line order
  1175. static comparePoints(aPt, bPt) {
  1176. if (aPt.x < bPt.x) return -1;
  1177. if (aPt.x > bPt.x) return 1;
  1178. if (aPt.y < bPt.y) return -1;
  1179. if (aPt.y > bPt.y) return 1;
  1180. return 0;
  1181. }
  1182. // Warning: 'point' input will be modified and re-used (for performance)
  1183. constructor(point, isLeft) {
  1184. if (point.events === undefined) point.events = [this];else point.events.push(this);
  1185. this.point = point;
  1186. this.isLeft = isLeft;
  1187. // this.segment, this.otherSE set by factory
  1188. }
  1189. link(other) {
  1190. if (other.point === this.point) {
  1191. throw new Error("Tried to link already linked events");
  1192. }
  1193. const otherEvents = other.point.events;
  1194. for (let i = 0, iMax = otherEvents.length; i < iMax; i++) {
  1195. const evt = otherEvents[i];
  1196. this.point.events.push(evt);
  1197. evt.point = this.point;
  1198. }
  1199. this.checkForConsuming();
  1200. }
  1201. /* Do a pass over our linked events and check to see if any pair
  1202. * of segments match, and should be consumed. */
  1203. checkForConsuming() {
  1204. // FIXME: The loops in this method run O(n^2) => no good.
  1205. // Maintain little ordered sweep event trees?
  1206. // Can we maintaining an ordering that avoids the need
  1207. // for the re-sorting with getLeftmostComparator in geom-out?
  1208. // Compare each pair of events to see if other events also match
  1209. const numEvents = this.point.events.length;
  1210. for (let i = 0; i < numEvents; i++) {
  1211. const evt1 = this.point.events[i];
  1212. if (evt1.segment.consumedBy !== undefined) continue;
  1213. for (let j = i + 1; j < numEvents; j++) {
  1214. const evt2 = this.point.events[j];
  1215. if (evt2.consumedBy !== undefined) continue;
  1216. if (evt1.otherSE.point.events !== evt2.otherSE.point.events) continue;
  1217. evt1.segment.consume(evt2.segment);
  1218. }
  1219. }
  1220. }
  1221. getAvailableLinkedEvents() {
  1222. // point.events is always of length 2 or greater
  1223. const events = [];
  1224. for (let i = 0, iMax = this.point.events.length; i < iMax; i++) {
  1225. const evt = this.point.events[i];
  1226. if (evt !== this && !evt.segment.ringOut && evt.segment.isInResult()) {
  1227. events.push(evt);
  1228. }
  1229. }
  1230. return events;
  1231. }
  1232. /**
  1233. * Returns a comparator function for sorting linked events that will
  1234. * favor the event that will give us the smallest left-side angle.
  1235. * All ring construction starts as low as possible heading to the right,
  1236. * so by always turning left as sharp as possible we'll get polygons
  1237. * without uncessary loops & holes.
  1238. *
  1239. * The comparator function has a compute cache such that it avoids
  1240. * re-computing already-computed values.
  1241. */
  1242. getLeftmostComparator(baseEvent) {
  1243. const cache = new Map();
  1244. const fillCache = linkedEvent => {
  1245. const nextEvent = linkedEvent.otherSE;
  1246. cache.set(linkedEvent, {
  1247. sine: sineOfAngle(this.point, baseEvent.point, nextEvent.point),
  1248. cosine: cosineOfAngle(this.point, baseEvent.point, nextEvent.point)
  1249. });
  1250. };
  1251. return (a, b) => {
  1252. if (!cache.has(a)) fillCache(a);
  1253. if (!cache.has(b)) fillCache(b);
  1254. const {
  1255. sine: asine,
  1256. cosine: acosine
  1257. } = cache.get(a);
  1258. const {
  1259. sine: bsine,
  1260. cosine: bcosine
  1261. } = cache.get(b);
  1262. // both on or above x-axis
  1263. if (asine >= 0 && bsine >= 0) {
  1264. if (acosine < bcosine) return 1;
  1265. if (acosine > bcosine) return -1;
  1266. return 0;
  1267. }
  1268. // both below x-axis
  1269. if (asine < 0 && bsine < 0) {
  1270. if (acosine < bcosine) return -1;
  1271. if (acosine > bcosine) return 1;
  1272. return 0;
  1273. }
  1274. // one above x-axis, one below
  1275. if (bsine < asine) return -1;
  1276. if (bsine > asine) return 1;
  1277. return 0;
  1278. };
  1279. }
  1280. }
  1281. // Give segments unique ID's to get consistent sorting of
  1282. // segments and sweep events when all else is identical
  1283. let segmentId = 0;
  1284. class Segment {
  1285. /* This compare() function is for ordering segments in the sweep
  1286. * line tree, and does so according to the following criteria:
  1287. *
  1288. * Consider the vertical line that lies an infinestimal step to the
  1289. * right of the right-more of the two left endpoints of the input
  1290. * segments. Imagine slowly moving a point up from negative infinity
  1291. * in the increasing y direction. Which of the two segments will that
  1292. * point intersect first? That segment comes 'before' the other one.
  1293. *
  1294. * If neither segment would be intersected by such a line, (if one
  1295. * or more of the segments are vertical) then the line to be considered
  1296. * is directly on the right-more of the two left inputs.
  1297. */
  1298. static compare(a, b) {
  1299. const alx = a.leftSE.point.x;
  1300. const blx = b.leftSE.point.x;
  1301. const arx = a.rightSE.point.x;
  1302. const brx = b.rightSE.point.x;
  1303. // check if they're even in the same vertical plane
  1304. if (brx < alx) return 1;
  1305. if (arx < blx) return -1;
  1306. const aly = a.leftSE.point.y;
  1307. const bly = b.leftSE.point.y;
  1308. const ary = a.rightSE.point.y;
  1309. const bry = b.rightSE.point.y;
  1310. // is left endpoint of segment B the right-more?
  1311. if (alx < blx) {
  1312. // are the two segments in the same horizontal plane?
  1313. if (bly < aly && bly < ary) return 1;
  1314. if (bly > aly && bly > ary) return -1;
  1315. // is the B left endpoint colinear to segment A?
  1316. const aCmpBLeft = a.comparePoint(b.leftSE.point);
  1317. if (aCmpBLeft < 0) return 1;
  1318. if (aCmpBLeft > 0) return -1;
  1319. // is the A right endpoint colinear to segment B ?
  1320. const bCmpARight = b.comparePoint(a.rightSE.point);
  1321. if (bCmpARight !== 0) return bCmpARight;
  1322. // colinear segments, consider the one with left-more
  1323. // left endpoint to be first (arbitrary?)
  1324. return -1;
  1325. }
  1326. // is left endpoint of segment A the right-more?
  1327. if (alx > blx) {
  1328. if (aly < bly && aly < bry) return -1;
  1329. if (aly > bly && aly > bry) return 1;
  1330. // is the A left endpoint colinear to segment B?
  1331. const bCmpALeft = b.comparePoint(a.leftSE.point);
  1332. if (bCmpALeft !== 0) return bCmpALeft;
  1333. // is the B right endpoint colinear to segment A?
  1334. const aCmpBRight = a.comparePoint(b.rightSE.point);
  1335. if (aCmpBRight < 0) return 1;
  1336. if (aCmpBRight > 0) return -1;
  1337. // colinear segments, consider the one with left-more
  1338. // left endpoint to be first (arbitrary?)
  1339. return 1;
  1340. }
  1341. // if we get here, the two left endpoints are in the same
  1342. // vertical plane, ie alx === blx
  1343. // consider the lower left-endpoint to come first
  1344. if (aly < bly) return -1;
  1345. if (aly > bly) return 1;
  1346. // left endpoints are identical
  1347. // check for colinearity by using the left-more right endpoint
  1348. // is the A right endpoint more left-more?
  1349. if (arx < brx) {
  1350. const bCmpARight = b.comparePoint(a.rightSE.point);
  1351. if (bCmpARight !== 0) return bCmpARight;
  1352. }
  1353. // is the B right endpoint more left-more?
  1354. if (arx > brx) {
  1355. const aCmpBRight = a.comparePoint(b.rightSE.point);
  1356. if (aCmpBRight < 0) return 1;
  1357. if (aCmpBRight > 0) return -1;
  1358. }
  1359. if (arx !== brx) {
  1360. // are these two [almost] vertical segments with opposite orientation?
  1361. // if so, the one with the lower right endpoint comes first
  1362. const ay = ary - aly;
  1363. const ax = arx - alx;
  1364. const by = bry - bly;
  1365. const bx = brx - blx;
  1366. if (ay > ax && by < bx) return 1;
  1367. if (ay < ax && by > bx) return -1;
  1368. }
  1369. // we have colinear segments with matching orientation
  1370. // consider the one with more left-more right endpoint to be first
  1371. if (arx > brx) return 1;
  1372. if (arx < brx) return -1;
  1373. // if we get here, two two right endpoints are in the same
  1374. // vertical plane, ie arx === brx
  1375. // consider the lower right-endpoint to come first
  1376. if (ary < bry) return -1;
  1377. if (ary > bry) return 1;
  1378. // right endpoints identical as well, so the segments are idential
  1379. // fall back on creation order as consistent tie-breaker
  1380. if (a.id < b.id) return -1;
  1381. if (a.id > b.id) return 1;
  1382. // identical segment, ie a === b
  1383. return 0;
  1384. }
  1385. /* Warning: a reference to ringWindings input will be stored,
  1386. * and possibly will be later modified */
  1387. constructor(leftSE, rightSE, rings, windings) {
  1388. this.id = ++segmentId;
  1389. this.leftSE = leftSE;
  1390. leftSE.segment = this;
  1391. leftSE.otherSE = rightSE;
  1392. this.rightSE = rightSE;
  1393. rightSE.segment = this;
  1394. rightSE.otherSE = leftSE;
  1395. this.rings = rings;
  1396. this.windings = windings;
  1397. // left unset for performance, set later in algorithm
  1398. // this.ringOut, this.consumedBy, this.prev
  1399. }
  1400. static fromRing(pt1, pt2, ring) {
  1401. let leftPt, rightPt, winding;
  1402. // ordering the two points according to sweep line ordering
  1403. const cmpPts = SweepEvent.comparePoints(pt1, pt2);
  1404. if (cmpPts < 0) {
  1405. leftPt = pt1;
  1406. rightPt = pt2;
  1407. winding = 1;
  1408. } else if (cmpPts > 0) {
  1409. leftPt = pt2;
  1410. rightPt = pt1;
  1411. winding = -1;
  1412. } else throw new Error(`Tried to create degenerate segment at [${pt1.x}, ${pt1.y}]`);
  1413. const leftSE = new SweepEvent(leftPt, true);
  1414. const rightSE = new SweepEvent(rightPt, false);
  1415. return new Segment(leftSE, rightSE, [ring], [winding]);
  1416. }
  1417. /* When a segment is split, the rightSE is replaced with a new sweep event */
  1418. replaceRightSE(newRightSE) {
  1419. this.rightSE = newRightSE;
  1420. this.rightSE.segment = this;
  1421. this.rightSE.otherSE = this.leftSE;
  1422. this.leftSE.otherSE = this.rightSE;
  1423. }
  1424. bbox() {
  1425. const y1 = this.leftSE.point.y;
  1426. const y2 = this.rightSE.point.y;
  1427. return {
  1428. ll: {
  1429. x: this.leftSE.point.x,
  1430. y: y1 < y2 ? y1 : y2
  1431. },
  1432. ur: {
  1433. x: this.rightSE.point.x,
  1434. y: y1 > y2 ? y1 : y2
  1435. }
  1436. };
  1437. }
  1438. /* A vector from the left point to the right */
  1439. vector() {
  1440. return {
  1441. x: this.rightSE.point.x - this.leftSE.point.x,
  1442. y: this.rightSE.point.y - this.leftSE.point.y
  1443. };
  1444. }
  1445. isAnEndpoint(pt) {
  1446. 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;
  1447. }
  1448. /* Compare this segment with a point.
  1449. *
  1450. * A point P is considered to be colinear to a segment if there
  1451. * exists a distance D such that if we travel along the segment
  1452. * from one * endpoint towards the other a distance D, we find
  1453. * ourselves at point P.
  1454. *
  1455. * Return value indicates:
  1456. *
  1457. * 1: point lies above the segment (to the left of vertical)
  1458. * 0: point is colinear to segment
  1459. * -1: point lies below the segment (to the right of vertical)
  1460. */
  1461. comparePoint(point) {
  1462. if (this.isAnEndpoint(point)) return 0;
  1463. const lPt = this.leftSE.point;
  1464. const rPt = this.rightSE.point;
  1465. const v = this.vector();
  1466. // Exactly vertical segments.
  1467. if (lPt.x === rPt.x) {
  1468. if (point.x === lPt.x) return 0;
  1469. return point.x < lPt.x ? 1 : -1;
  1470. }
  1471. // Nearly vertical segments with an intersection.
  1472. // Check to see where a point on the line with matching Y coordinate is.
  1473. const yDist = (point.y - lPt.y) / v.y;
  1474. const xFromYDist = lPt.x + yDist * v.x;
  1475. if (point.x === xFromYDist) return 0;
  1476. // General case.
  1477. // Check to see where a point on the line with matching X coordinate is.
  1478. const xDist = (point.x - lPt.x) / v.x;
  1479. const yFromXDist = lPt.y + xDist * v.y;
  1480. if (point.y === yFromXDist) return 0;
  1481. return point.y < yFromXDist ? -1 : 1;
  1482. }
  1483. /**
  1484. * Given another segment, returns the first non-trivial intersection
  1485. * between the two segments (in terms of sweep line ordering), if it exists.
  1486. *
  1487. * A 'non-trivial' intersection is one that will cause one or both of the
  1488. * segments to be split(). As such, 'trivial' vs. 'non-trivial' intersection:
  1489. *
  1490. * * endpoint of segA with endpoint of segB --> trivial
  1491. * * endpoint of segA with point along segB --> non-trivial
  1492. * * endpoint of segB with point along segA --> non-trivial
  1493. * * point along segA with point along segB --> non-trivial
  1494. *
  1495. * If no non-trivial intersection exists, return null
  1496. * Else, return null.
  1497. */
  1498. getIntersection(other) {
  1499. // If bboxes don't overlap, there can't be any intersections
  1500. const tBbox = this.bbox();
  1501. const oBbox = other.bbox();
  1502. const bboxOverlap = getBboxOverlap(tBbox, oBbox);
  1503. if (bboxOverlap === null) return null;
  1504. // We first check to see if the endpoints can be considered intersections.
  1505. // This will 'snap' intersections to endpoints if possible, and will
  1506. // handle cases of colinearity.
  1507. const tlp = this.leftSE.point;
  1508. const trp = this.rightSE.point;
  1509. const olp = other.leftSE.point;
  1510. const orp = other.rightSE.point;
  1511. // does each endpoint touch the other segment?
  1512. // note that we restrict the 'touching' definition to only allow segments
  1513. // to touch endpoints that lie forward from where we are in the sweep line pass
  1514. const touchesOtherLSE = isInBbox(tBbox, olp) && this.comparePoint(olp) === 0;
  1515. const touchesThisLSE = isInBbox(oBbox, tlp) && other.comparePoint(tlp) === 0;
  1516. const touchesOtherRSE = isInBbox(tBbox, orp) && this.comparePoint(orp) === 0;
  1517. const touchesThisRSE = isInBbox(oBbox, trp) && other.comparePoint(trp) === 0;
  1518. // do left endpoints match?
  1519. if (touchesThisLSE && touchesOtherLSE) {
  1520. // these two cases are for colinear segments with matching left
  1521. // endpoints, and one segment being longer than the other
  1522. if (touchesThisRSE && !touchesOtherRSE) return trp;
  1523. if (!touchesThisRSE && touchesOtherRSE) return orp;
  1524. // either the two segments match exactly (two trival intersections)
  1525. // or just on their left endpoint (one trivial intersection
  1526. return null;
  1527. }
  1528. // does this left endpoint matches (other doesn't)
  1529. if (touchesThisLSE) {
  1530. // check for segments that just intersect on opposing endpoints
  1531. if (touchesOtherRSE) {
  1532. if (tlp.x === orp.x && tlp.y === orp.y) return null;
  1533. }
  1534. // t-intersection on left endpoint
  1535. return tlp;
  1536. }
  1537. // does other left endpoint matches (this doesn't)
  1538. if (touchesOtherLSE) {
  1539. // check for segments that just intersect on opposing endpoints
  1540. if (touchesThisRSE) {
  1541. if (trp.x === olp.x && trp.y === olp.y) return null;
  1542. }
  1543. // t-intersection on left endpoint
  1544. return olp;
  1545. }
  1546. // trivial intersection on right endpoints
  1547. if (touchesThisRSE && touchesOtherRSE) return null;
  1548. // t-intersections on just one right endpoint
  1549. if (touchesThisRSE) return trp;
  1550. if (touchesOtherRSE) return orp;
  1551. // None of our endpoints intersect. Look for a general intersection between
  1552. // infinite lines laid over the segments
  1553. const pt = intersection$1(tlp, this.vector(), olp, other.vector());
  1554. // are the segments parrallel? Note that if they were colinear with overlap,
  1555. // they would have an endpoint intersection and that case was already handled above
  1556. if (pt === null) return null;
  1557. // is the intersection found between the lines not on the segments?
  1558. if (!isInBbox(bboxOverlap, pt)) return null;
  1559. // round the the computed point if needed
  1560. return rounder.round(pt.x, pt.y);
  1561. }
  1562. /**
  1563. * Split the given segment into multiple segments on the given points.
  1564. * * Each existing segment will retain its leftSE and a new rightSE will be
  1565. * generated for it.
  1566. * * A new segment will be generated which will adopt the original segment's
  1567. * rightSE, and a new leftSE will be generated for it.
  1568. * * If there are more than two points given to split on, new segments
  1569. * in the middle will be generated with new leftSE and rightSE's.
  1570. * * An array of the newly generated SweepEvents will be returned.
  1571. *
  1572. * Warning: input array of points is modified
  1573. */
  1574. split(point) {
  1575. const newEvents = [];
  1576. const alreadyLinked = point.events !== undefined;
  1577. const newLeftSE = new SweepEvent(point, true);
  1578. const newRightSE = new SweepEvent(point, false);
  1579. const oldRightSE = this.rightSE;
  1580. this.replaceRightSE(newRightSE);
  1581. newEvents.push(newRightSE);
  1582. newEvents.push(newLeftSE);
  1583. const newSeg = new Segment(newLeftSE, oldRightSE, this.rings.slice(), this.windings.slice());
  1584. // when splitting a nearly vertical downward-facing segment,
  1585. // sometimes one of the resulting new segments is vertical, in which
  1586. // case its left and right events may need to be swapped
  1587. if (SweepEvent.comparePoints(newSeg.leftSE.point, newSeg.rightSE.point) > 0) {
  1588. newSeg.swapEvents();
  1589. }
  1590. if (SweepEvent.comparePoints(this.leftSE.point, this.rightSE.point) > 0) {
  1591. this.swapEvents();
  1592. }
  1593. // in the point we just used to create new sweep events with was already
  1594. // linked to other events, we need to check if either of the affected
  1595. // segments should be consumed
  1596. if (alreadyLinked) {
  1597. newLeftSE.checkForConsuming();
  1598. newRightSE.checkForConsuming();
  1599. }
  1600. return newEvents;
  1601. }
  1602. /* Swap which event is left and right */
  1603. swapEvents() {
  1604. const tmpEvt = this.rightSE;
  1605. this.rightSE = this.leftSE;
  1606. this.leftSE = tmpEvt;
  1607. this.leftSE.isLeft = true;
  1608. this.rightSE.isLeft = false;
  1609. for (let i = 0, iMax = this.windings.length; i < iMax; i++) {
  1610. this.windings[i] *= -1;
  1611. }
  1612. }
  1613. /* Consume another segment. We take their rings under our wing
  1614. * and mark them as consumed. Use for perfectly overlapping segments */
  1615. consume(other) {
  1616. let consumer = this;
  1617. let consumee = other;
  1618. while (consumer.consumedBy) consumer = consumer.consumedBy;
  1619. while (consumee.consumedBy) consumee = consumee.consumedBy;
  1620. const cmp = Segment.compare(consumer, consumee);
  1621. if (cmp === 0) return; // already consumed
  1622. // the winner of the consumption is the earlier segment
  1623. // according to sweep line ordering
  1624. if (cmp > 0) {
  1625. const tmp = consumer;
  1626. consumer = consumee;
  1627. consumee = tmp;
  1628. }
  1629. // make sure a segment doesn't consume it's prev
  1630. if (consumer.prev === consumee) {
  1631. const tmp = consumer;
  1632. consumer = consumee;
  1633. consumee = tmp;
  1634. }
  1635. for (let i = 0, iMax = consumee.rings.length; i < iMax; i++) {
  1636. const ring = consumee.rings[i];
  1637. const winding = consumee.windings[i];
  1638. const index = consumer.rings.indexOf(ring);
  1639. if (index === -1) {
  1640. consumer.rings.push(ring);
  1641. consumer.windings.push(winding);
  1642. } else consumer.windings[index] += winding;
  1643. }
  1644. consumee.rings = null;
  1645. consumee.windings = null;
  1646. consumee.consumedBy = consumer;
  1647. // mark sweep events consumed as to maintain ordering in sweep event queue
  1648. consumee.leftSE.consumedBy = consumer.leftSE;
  1649. consumee.rightSE.consumedBy = consumer.rightSE;
  1650. }
  1651. /* The first segment previous segment chain that is in the result */
  1652. prevInResult() {
  1653. if (this._prevInResult !== undefined) return this._prevInResult;
  1654. if (!this.prev) this._prevInResult = null;else if (this.prev.isInResult()) this._prevInResult = this.prev;else this._prevInResult = this.prev.prevInResult();
  1655. return this._prevInResult;
  1656. }
  1657. beforeState() {
  1658. if (this._beforeState !== undefined) return this._beforeState;
  1659. if (!this.prev) this._beforeState = {
  1660. rings: [],
  1661. windings: [],
  1662. multiPolys: []
  1663. };else {
  1664. const seg = this.prev.consumedBy || this.prev;
  1665. this._beforeState = seg.afterState();
  1666. }
  1667. return this._beforeState;
  1668. }
  1669. afterState() {
  1670. if (this._afterState !== undefined) return this._afterState;
  1671. const beforeState = this.beforeState();
  1672. this._afterState = {
  1673. rings: beforeState.rings.slice(0),
  1674. windings: beforeState.windings.slice(0),
  1675. multiPolys: []
  1676. };
  1677. const ringsAfter = this._afterState.rings;
  1678. const windingsAfter = this._afterState.windings;
  1679. const mpsAfter = this._afterState.multiPolys;
  1680. // calculate ringsAfter, windingsAfter
  1681. for (let i = 0, iMax = this.rings.length; i < iMax; i++) {
  1682. const ring = this.rings[i];
  1683. const winding = this.windings[i];
  1684. const index = ringsAfter.indexOf(ring);
  1685. if (index === -1) {
  1686. ringsAfter.push(ring);
  1687. windingsAfter.push(winding);
  1688. } else windingsAfter[index] += winding;
  1689. }
  1690. // calcualte polysAfter
  1691. const polysAfter = [];
  1692. const polysExclude = [];
  1693. for (let i = 0, iMax = ringsAfter.length; i < iMax; i++) {
  1694. if (windingsAfter[i] === 0) continue; // non-zero rule
  1695. const ring = ringsAfter[i];
  1696. const poly = ring.poly;
  1697. if (polysExclude.indexOf(poly) !== -1) continue;
  1698. if (ring.isExterior) polysAfter.push(poly);else {
  1699. if (polysExclude.indexOf(poly) === -1) polysExclude.push(poly);
  1700. const index = polysAfter.indexOf(ring.poly);
  1701. if (index !== -1) polysAfter.splice(index, 1);
  1702. }
  1703. }
  1704. // calculate multiPolysAfter
  1705. for (let i = 0, iMax = polysAfter.length; i < iMax; i++) {
  1706. const mp = polysAfter[i].multiPoly;
  1707. if (mpsAfter.indexOf(mp) === -1) mpsAfter.push(mp);
  1708. }
  1709. return this._afterState;
  1710. }
  1711. /* Is this segment part of the final result? */
  1712. isInResult() {
  1713. // if we've been consumed, we're not in the result
  1714. if (this.consumedBy) return false;
  1715. if (this._isInResult !== undefined) return this._isInResult;
  1716. const mpsBefore = this.beforeState().multiPolys;
  1717. const mpsAfter = this.afterState().multiPolys;
  1718. switch (operation.type) {
  1719. case "union":
  1720. {
  1721. // UNION - included iff:
  1722. // * On one side of us there is 0 poly interiors AND
  1723. // * On the other side there is 1 or more.
  1724. const noBefores = mpsBefore.length === 0;
  1725. const noAfters = mpsAfter.length === 0;
  1726. this._isInResult = noBefores !== noAfters;
  1727. break;
  1728. }
  1729. case "intersection":
  1730. {
  1731. // INTERSECTION - included iff:
  1732. // * on one side of us all multipolys are rep. with poly interiors AND
  1733. // * on the other side of us, not all multipolys are repsented
  1734. // with poly interiors
  1735. let least;
  1736. let most;
  1737. if (mpsBefore.length < mpsAfter.length) {
  1738. least = mpsBefore.length;
  1739. most = mpsAfter.length;
  1740. } else {
  1741. least = mpsAfter.length;
  1742. most = mpsBefore.length;
  1743. }
  1744. this._isInResult = most === operation.numMultiPolys && least < most;
  1745. break;
  1746. }
  1747. case "xor":
  1748. {
  1749. // XOR - included iff:
  1750. // * the difference between the number of multipolys represented
  1751. // with poly interiors on our two sides is an odd number
  1752. const diff = Math.abs(mpsBefore.length - mpsAfter.length);
  1753. this._isInResult = diff % 2 === 1;
  1754. break;
  1755. }
  1756. case "difference":
  1757. {
  1758. // DIFFERENCE included iff:
  1759. // * on exactly one side, we have just the subject
  1760. const isJustSubject = mps => mps.length === 1 && mps[0].isSubject;
  1761. this._isInResult = isJustSubject(mpsBefore) !== isJustSubject(mpsAfter);
  1762. break;
  1763. }
  1764. default:
  1765. throw new Error(`Unrecognized operation type found ${operation.type}`);
  1766. }
  1767. return this._isInResult;
  1768. }
  1769. }
  1770. class RingIn {
  1771. constructor(geomRing, poly, isExterior) {
  1772. if (!Array.isArray(geomRing) || geomRing.length === 0) {
  1773. throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
  1774. }
  1775. this.poly = poly;
  1776. this.isExterior = isExterior;
  1777. this.segments = [];
  1778. if (typeof geomRing[0][0] !== "number" || typeof geomRing[0][1] !== "number") {
  1779. throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
  1780. }
  1781. const firstPoint = rounder.round(geomRing[0][0], geomRing[0][1]);
  1782. this.bbox = {
  1783. ll: {
  1784. x: firstPoint.x,
  1785. y: firstPoint.y
  1786. },
  1787. ur: {
  1788. x: firstPoint.x,
  1789. y: firstPoint.y
  1790. }
  1791. };
  1792. let prevPoint = firstPoint;
  1793. for (let i = 1, iMax = geomRing.length; i < iMax; i++) {
  1794. if (typeof geomRing[i][0] !== "number" || typeof geomRing[i][1] !== "number") {
  1795. throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
  1796. }
  1797. let point = rounder.round(geomRing[i][0], geomRing[i][1]);
  1798. // skip repeated points
  1799. if (point.x === prevPoint.x && point.y === prevPoint.y) continue;
  1800. this.segments.push(Segment.fromRing(prevPoint, point, this));
  1801. if (point.x < this.bbox.ll.x) this.bbox.ll.x = point.x;
  1802. if (point.y < this.bbox.ll.y) this.bbox.ll.y = point.y;
  1803. if (point.x > this.bbox.ur.x) this.bbox.ur.x = point.x;
  1804. if (point.y > this.bbox.ur.y) this.bbox.ur.y = point.y;
  1805. prevPoint = point;
  1806. }
  1807. // add segment from last to first if last is not the same as first
  1808. if (firstPoint.x !== prevPoint.x || firstPoint.y !== prevPoint.y) {
  1809. this.segments.push(Segment.fromRing(prevPoint, firstPoint, this));
  1810. }
  1811. }
  1812. getSweepEvents() {
  1813. const sweepEvents = [];
  1814. for (let i = 0, iMax = this.segments.length; i < iMax; i++) {
  1815. const segment = this.segments[i];
  1816. sweepEvents.push(segment.leftSE);
  1817. sweepEvents.push(segment.rightSE);
  1818. }
  1819. return sweepEvents;
  1820. }
  1821. }
  1822. class PolyIn {
  1823. constructor(geomPoly, multiPoly) {
  1824. if (!Array.isArray(geomPoly)) {
  1825. throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
  1826. }
  1827. this.exteriorRing = new RingIn(geomPoly[0], this, true);
  1828. // copy by value
  1829. this.bbox = {
  1830. ll: {
  1831. x: this.exteriorRing.bbox.ll.x,
  1832. y: this.exteriorRing.bbox.ll.y
  1833. },
  1834. ur: {
  1835. x: this.exteriorRing.bbox.ur.x,
  1836. y: this.exteriorRing.bbox.ur.y
  1837. }
  1838. };
  1839. this.interiorRings = [];
  1840. for (let i = 1, iMax = geomPoly.length; i < iMax; i++) {
  1841. const ring = new RingIn(geomPoly[i], this, false);
  1842. if (ring.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = ring.bbox.ll.x;
  1843. if (ring.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = ring.bbox.ll.y;
  1844. if (ring.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = ring.bbox.ur.x;
  1845. if (ring.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = ring.bbox.ur.y;
  1846. this.interiorRings.push(ring);
  1847. }
  1848. this.multiPoly = multiPoly;
  1849. }
  1850. getSweepEvents() {
  1851. const sweepEvents = this.exteriorRing.getSweepEvents();
  1852. for (let i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
  1853. const ringSweepEvents = this.interiorRings[i].getSweepEvents();
  1854. for (let j = 0, jMax = ringSweepEvents.length; j < jMax; j++) {
  1855. sweepEvents.push(ringSweepEvents[j]);
  1856. }
  1857. }
  1858. return sweepEvents;
  1859. }
  1860. }
  1861. class MultiPolyIn {
  1862. constructor(geom, isSubject) {
  1863. if (!Array.isArray(geom)) {
  1864. throw new Error("Input geometry is not a valid Polygon or MultiPolygon");
  1865. }
  1866. try {
  1867. // if the input looks like a polygon, convert it to a multipolygon
  1868. if (typeof geom[0][0][0] === "number") geom = [geom];
  1869. } catch (ex) {
  1870. // The input is either malformed or has empty arrays.
  1871. // In either case, it will be handled later on.
  1872. }
  1873. this.polys = [];
  1874. this.bbox = {
  1875. ll: {
  1876. x: Number.POSITIVE_INFINITY,
  1877. y: Number.POSITIVE_INFINITY
  1878. },
  1879. ur: {
  1880. x: Number.NEGATIVE_INFINITY,
  1881. y: Number.NEGATIVE_INFINITY
  1882. }
  1883. };
  1884. for (let i = 0, iMax = geom.length; i < iMax; i++) {
  1885. const poly = new PolyIn(geom[i], this);
  1886. if (poly.bbox.ll.x < this.bbox.ll.x) this.bbox.ll.x = poly.bbox.ll.x;
  1887. if (poly.bbox.ll.y < this.bbox.ll.y) this.bbox.ll.y = poly.bbox.ll.y;
  1888. if (poly.bbox.ur.x > this.bbox.ur.x) this.bbox.ur.x = poly.bbox.ur.x;
  1889. if (poly.bbox.ur.y > this.bbox.ur.y) this.bbox.ur.y = poly.bbox.ur.y;
  1890. this.polys.push(poly);
  1891. }
  1892. this.isSubject = isSubject;
  1893. }
  1894. getSweepEvents() {
  1895. const sweepEvents = [];
  1896. for (let i = 0, iMax = this.polys.length; i < iMax; i++) {
  1897. const polySweepEvents = this.polys[i].getSweepEvents();
  1898. for (let j = 0, jMax = polySweepEvents.length; j < jMax; j++) {
  1899. sweepEvents.push(polySweepEvents[j]);
  1900. }
  1901. }
  1902. return sweepEvents;
  1903. }
  1904. }
  1905. class RingOut {
  1906. /* Given the segments from the sweep line pass, compute & return a series
  1907. * of closed rings from all the segments marked to be part of the result */
  1908. static factory(allSegments) {
  1909. const ringsOut = [];
  1910. for (let i = 0, iMax = allSegments.length; i < iMax; i++) {
  1911. const segment = allSegments[i];
  1912. if (!segment.isInResult() || segment.ringOut) continue;
  1913. let prevEvent = null;
  1914. let event = segment.leftSE;
  1915. let nextEvent = segment.rightSE;
  1916. const events = [event];
  1917. const startingPoint = event.point;
  1918. const intersectionLEs = [];
  1919. /* Walk the chain of linked events to form a closed ring */
  1920. while (true) {
  1921. prevEvent = event;
  1922. event = nextEvent;
  1923. events.push(event);
  1924. /* Is the ring complete? */
  1925. if (event.point === startingPoint) break;
  1926. while (true) {
  1927. const availableLEs = event.getAvailableLinkedEvents();
  1928. /* Did we hit a dead end? This shouldn't happen.
  1929. * Indicates some earlier part of the algorithm malfunctioned. */
  1930. if (availableLEs.length === 0) {
  1931. const firstPt = events[0].point;
  1932. const lastPt = events[events.length - 1].point;
  1933. throw new Error(`Unable to complete output ring starting at [${firstPt.x},` + ` ${firstPt.y}]. Last matching segment found ends at` + ` [${lastPt.x}, ${lastPt.y}].`);
  1934. }
  1935. /* Only one way to go, so cotinue on the path */
  1936. if (availableLEs.length === 1) {
  1937. nextEvent = availableLEs[0].otherSE;
  1938. break;
  1939. }
  1940. /* We must have an intersection. Check for a completed loop */
  1941. let indexLE = null;
  1942. for (let j = 0, jMax = intersectionLEs.length; j < jMax; j++) {
  1943. if (intersectionLEs[j].point === event.point) {
  1944. indexLE = j;
  1945. break;
  1946. }
  1947. }
  1948. /* Found a completed loop. Cut that off and make a ring */
  1949. if (indexLE !== null) {
  1950. const intersectionLE = intersectionLEs.splice(indexLE)[0];
  1951. const ringEvents = events.splice(intersectionLE.index);
  1952. ringEvents.unshift(ringEvents[0].otherSE);
  1953. ringsOut.push(new RingOut(ringEvents.reverse()));
  1954. continue;
  1955. }
  1956. /* register the intersection */
  1957. intersectionLEs.push({
  1958. index: events.length,
  1959. point: event.point
  1960. });
  1961. /* Choose the left-most option to continue the walk */
  1962. const comparator = event.getLeftmostComparator(prevEvent);
  1963. nextEvent = availableLEs.sort(comparator)[0].otherSE;
  1964. break;
  1965. }
  1966. }
  1967. ringsOut.push(new RingOut(events));
  1968. }
  1969. return ringsOut;
  1970. }
  1971. constructor(events) {
  1972. this.events = events;
  1973. for (let i = 0, iMax = events.length; i < iMax; i++) {
  1974. events[i].segment.ringOut = this;
  1975. }
  1976. this.poly = null;
  1977. }
  1978. getGeom() {
  1979. // Remove superfluous points (ie extra points along a straight line),
  1980. let prevPt = this.events[0].point;
  1981. const points = [prevPt];
  1982. for (let i = 1, iMax = this.events.length - 1; i < iMax; i++) {
  1983. const pt = this.events[i].point;
  1984. const nextPt = this.events[i + 1].point;
  1985. if (compareVectorAngles(pt, prevPt, nextPt) === 0) continue;
  1986. points.push(pt);
  1987. prevPt = pt;
  1988. }
  1989. // ring was all (within rounding error of angle calc) colinear points
  1990. if (points.length === 1) return null;
  1991. // check if the starting point is necessary
  1992. const pt = points[0];
  1993. const nextPt = points[1];
  1994. if (compareVectorAngles(pt, prevPt, nextPt) === 0) points.shift();
  1995. points.push(points[0]);
  1996. const step = this.isExteriorRing() ? 1 : -1;
  1997. const iStart = this.isExteriorRing() ? 0 : points.length - 1;
  1998. const iEnd = this.isExteriorRing() ? points.length : -1;
  1999. const orderedPoints = [];
  2000. for (let i = iStart; i != iEnd; i += step) orderedPoints.push([points[i].x, points[i].y]);
  2001. return orderedPoints;
  2002. }
  2003. isExteriorRing() {
  2004. if (this._isExteriorRing === undefined) {
  2005. const enclosing = this.enclosingRing();
  2006. this._isExteriorRing = enclosing ? !enclosing.isExteriorRing() : true;
  2007. }
  2008. return this._isExteriorRing;
  2009. }
  2010. enclosingRing() {
  2011. if (this._enclosingRing === undefined) {
  2012. this._enclosingRing = this._calcEnclosingRing();
  2013. }
  2014. return this._enclosingRing;
  2015. }
  2016. /* Returns the ring that encloses this one, if any */
  2017. _calcEnclosingRing() {
  2018. // start with the ealier sweep line event so that the prevSeg
  2019. // chain doesn't lead us inside of a loop of ours
  2020. let leftMostEvt = this.events[0];
  2021. for (let i = 1, iMax = this.events.length; i < iMax; i++) {
  2022. const evt = this.events[i];
  2023. if (SweepEvent.compare(leftMostEvt, evt) > 0) leftMostEvt = evt;
  2024. }
  2025. let prevSeg = leftMostEvt.segment.prevInResult();
  2026. let prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
  2027. while (true) {
  2028. // no segment found, thus no ring can enclose us
  2029. if (!prevSeg) return null;
  2030. // no segments below prev segment found, thus the ring of the prev
  2031. // segment must loop back around and enclose us
  2032. if (!prevPrevSeg) return prevSeg.ringOut;
  2033. // if the two segments are of different rings, the ring of the prev
  2034. // segment must either loop around us or the ring of the prev prev
  2035. // seg, which would make us and the ring of the prev peers
  2036. if (prevPrevSeg.ringOut !== prevSeg.ringOut) {
  2037. if (prevPrevSeg.ringOut.enclosingRing() !== prevSeg.ringOut) {
  2038. return prevSeg.ringOut;
  2039. } else return prevSeg.ringOut.enclosingRing();
  2040. }
  2041. // two segments are from the same ring, so this was a penisula
  2042. // of that ring. iterate downward, keep searching
  2043. prevSeg = prevPrevSeg.prevInResult();
  2044. prevPrevSeg = prevSeg ? prevSeg.prevInResult() : null;
  2045. }
  2046. }
  2047. }
  2048. class PolyOut {
  2049. constructor(exteriorRing) {
  2050. this.exteriorRing = exteriorRing;
  2051. exteriorRing.poly = this;
  2052. this.interiorRings = [];
  2053. }
  2054. addInterior(ring) {
  2055. this.interiorRings.push(ring);
  2056. ring.poly = this;
  2057. }
  2058. getGeom() {
  2059. const geom = [this.exteriorRing.getGeom()];
  2060. // exterior ring was all (within rounding error of angle calc) colinear points
  2061. if (geom[0] === null) return null;
  2062. for (let i = 0, iMax = this.interiorRings.length; i < iMax; i++) {
  2063. const ringGeom = this.interiorRings[i].getGeom();
  2064. // interior ring was all (within rounding error of angle calc) colinear points
  2065. if (ringGeom === null) continue;
  2066. geom.push(ringGeom);
  2067. }
  2068. return geom;
  2069. }
  2070. }
  2071. class MultiPolyOut {
  2072. constructor(rings) {
  2073. this.rings = rings;
  2074. this.polys = this._composePolys(rings);
  2075. }
  2076. getGeom() {
  2077. const geom = [];
  2078. for (let i = 0, iMax = this.polys.length; i < iMax; i++) {
  2079. const polyGeom = this.polys[i].getGeom();
  2080. // exterior ring was all (within rounding error of angle calc) colinear points
  2081. if (polyGeom === null) continue;
  2082. geom.push(polyGeom);
  2083. }
  2084. return geom;
  2085. }
  2086. _composePolys(rings) {
  2087. const polys = [];
  2088. for (let i = 0, iMax = rings.length; i < iMax; i++) {
  2089. const ring = rings[i];
  2090. if (ring.poly) continue;
  2091. if (ring.isExteriorRing()) polys.push(new PolyOut(ring));else {
  2092. const enclosingRing = ring.enclosingRing();
  2093. if (!enclosingRing.poly) polys.push(new PolyOut(enclosingRing));
  2094. enclosingRing.poly.addInterior(ring);
  2095. }
  2096. }
  2097. return polys;
  2098. }
  2099. }
  2100. /**
  2101. * NOTE: We must be careful not to change any segments while
  2102. * they are in the SplayTree. AFAIK, there's no way to tell
  2103. * the tree to rebalance itself - thus before splitting
  2104. * a segment that's in the tree, we remove it from the tree,
  2105. * do the split, then re-insert it. (Even though splitting a
  2106. * segment *shouldn't* change its correct position in the
  2107. * sweep line tree, the reality is because of rounding errors,
  2108. * it sometimes does.)
  2109. */
  2110. class SweepLine {
  2111. constructor(queue) {
  2112. let comparator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Segment.compare;
  2113. this.queue = queue;
  2114. this.tree = new Tree(comparator);
  2115. this.segments = [];
  2116. }
  2117. process(event) {
  2118. const segment = event.segment;
  2119. const newEvents = [];
  2120. // if we've already been consumed by another segment,
  2121. // clean up our body parts and get out
  2122. if (event.consumedBy) {
  2123. if (event.isLeft) this.queue.remove(event.otherSE);else this.tree.remove(segment);
  2124. return newEvents;
  2125. }
  2126. const node = event.isLeft ? this.tree.add(segment) : this.tree.find(segment);
  2127. 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.");
  2128. let prevNode = node;
  2129. let nextNode = node;
  2130. let prevSeg = undefined;
  2131. let nextSeg = undefined;
  2132. // skip consumed segments still in tree
  2133. while (prevSeg === undefined) {
  2134. prevNode = this.tree.prev(prevNode);
  2135. if (prevNode === null) prevSeg = null;else if (prevNode.key.consumedBy === undefined) prevSeg = prevNode.key;
  2136. }
  2137. // skip consumed segments still in tree
  2138. while (nextSeg === undefined) {
  2139. nextNode = this.tree.next(nextNode);
  2140. if (nextNode === null) nextSeg = null;else if (nextNode.key.consumedBy === undefined) nextSeg = nextNode.key;
  2141. }
  2142. if (event.isLeft) {
  2143. // Check for intersections against the previous segment in the sweep line
  2144. let prevMySplitter = null;
  2145. if (prevSeg) {
  2146. const prevInter = prevSeg.getIntersection(segment);
  2147. if (prevInter !== null) {
  2148. if (!segment.isAnEndpoint(prevInter)) prevMySplitter = prevInter;
  2149. if (!prevSeg.isAnEndpoint(prevInter)) {
  2150. const newEventsFromSplit = this._splitSafely(prevSeg, prevInter);
  2151. for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
  2152. newEvents.push(newEventsFromSplit[i]);
  2153. }
  2154. }
  2155. }
  2156. }
  2157. // Check for intersections against the next segment in the sweep line
  2158. let nextMySplitter = null;
  2159. if (nextSeg) {
  2160. const nextInter = nextSeg.getIntersection(segment);
  2161. if (nextInter !== null) {
  2162. if (!segment.isAnEndpoint(nextInter)) nextMySplitter = nextInter;
  2163. if (!nextSeg.isAnEndpoint(nextInter)) {
  2164. const newEventsFromSplit = this._splitSafely(nextSeg, nextInter);
  2165. for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
  2166. newEvents.push(newEventsFromSplit[i]);
  2167. }
  2168. }
  2169. }
  2170. }
  2171. // For simplicity, even if we find more than one intersection we only
  2172. // spilt on the 'earliest' (sweep-line style) of the intersections.
  2173. // The other intersection will be handled in a future process().
  2174. if (prevMySplitter !== null || nextMySplitter !== null) {
  2175. let mySplitter = null;
  2176. if (prevMySplitter === null) mySplitter = nextMySplitter;else if (nextMySplitter === null) mySplitter = prevMySplitter;else {
  2177. const cmpSplitters = SweepEvent.comparePoints(prevMySplitter, nextMySplitter);
  2178. mySplitter = cmpSplitters <= 0 ? prevMySplitter : nextMySplitter;
  2179. }
  2180. // Rounding errors can cause changes in ordering,
  2181. // so remove afected segments and right sweep events before splitting
  2182. this.queue.remove(segment.rightSE);
  2183. newEvents.push(segment.rightSE);
  2184. const newEventsFromSplit = segment.split(mySplitter);
  2185. for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
  2186. newEvents.push(newEventsFromSplit[i]);
  2187. }
  2188. }
  2189. if (newEvents.length > 0) {
  2190. // We found some intersections, so re-do the current event to
  2191. // make sure sweep line ordering is totally consistent for later
  2192. // use with the segment 'prev' pointers
  2193. this.tree.remove(segment);
  2194. newEvents.push(event);
  2195. } else {
  2196. // done with left event
  2197. this.segments.push(segment);
  2198. segment.prev = prevSeg;
  2199. }
  2200. } else {
  2201. // event.isRight
  2202. // since we're about to be removed from the sweep line, check for
  2203. // intersections between our previous and next segments
  2204. if (prevSeg && nextSeg) {
  2205. const inter = prevSeg.getIntersection(nextSeg);
  2206. if (inter !== null) {
  2207. if (!prevSeg.isAnEndpoint(inter)) {
  2208. const newEventsFromSplit = this._splitSafely(prevSeg, inter);
  2209. for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
  2210. newEvents.push(newEventsFromSplit[i]);
  2211. }
  2212. }
  2213. if (!nextSeg.isAnEndpoint(inter)) {
  2214. const newEventsFromSplit = this._splitSafely(nextSeg, inter);
  2215. for (let i = 0, iMax = newEventsFromSplit.length; i < iMax; i++) {
  2216. newEvents.push(newEventsFromSplit[i]);
  2217. }
  2218. }
  2219. }
  2220. }
  2221. this.tree.remove(segment);
  2222. }
  2223. return newEvents;
  2224. }
  2225. /* Safely split a segment that is currently in the datastructures
  2226. * IE - a segment other than the one that is currently being processed. */
  2227. _splitSafely(seg, pt) {
  2228. // Rounding errors can cause changes in ordering,
  2229. // so remove afected segments and right sweep events before splitting
  2230. // removeNode() doesn't work, so have re-find the seg
  2231. // https://github.com/w8r/splay-tree/pull/5
  2232. this.tree.remove(seg);
  2233. const rightSE = seg.rightSE;
  2234. this.queue.remove(rightSE);
  2235. const newEvents = seg.split(pt);
  2236. newEvents.push(rightSE);
  2237. // splitting can trigger consumption
  2238. if (seg.consumedBy === undefined) this.tree.add(seg);
  2239. return newEvents;
  2240. }
  2241. }
  2242. // Limits on iterative processes to prevent infinite loops - usually caused by floating-point math round-off errors.
  2243. const POLYGON_CLIPPING_MAX_QUEUE_SIZE = typeof process !== "undefined" && process.env.POLYGON_CLIPPING_MAX_QUEUE_SIZE || 1000000;
  2244. const POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS = typeof process !== "undefined" && process.env.POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS || 1000000;
  2245. class Operation {
  2246. run(type, geom, moreGeoms) {
  2247. operation.type = type;
  2248. rounder.reset();
  2249. /* Convert inputs to MultiPoly objects */
  2250. const multipolys = [new MultiPolyIn(geom, true)];
  2251. for (let i = 0, iMax = moreGeoms.length; i < iMax; i++) {
  2252. multipolys.push(new MultiPolyIn(moreGeoms[i], false));
  2253. }
  2254. operation.numMultiPolys = multipolys.length;
  2255. /* BBox optimization for difference operation
  2256. * If the bbox of a multipolygon that's part of the clipping doesn't
  2257. * intersect the bbox of the subject at all, we can just drop that
  2258. * multiploygon. */
  2259. if (operation.type === "difference") {
  2260. // in place removal
  2261. const subject = multipolys[0];
  2262. let i = 1;
  2263. while (i < multipolys.length) {
  2264. if (getBboxOverlap(multipolys[i].bbox, subject.bbox) !== null) i++;else multipolys.splice(i, 1);
  2265. }
  2266. }
  2267. /* BBox optimization for intersection operation
  2268. * If we can find any pair of multipolygons whose bbox does not overlap,
  2269. * then the result will be empty. */
  2270. if (operation.type === "intersection") {
  2271. // TODO: this is O(n^2) in number of polygons. By sorting the bboxes,
  2272. // it could be optimized to O(n * ln(n))
  2273. for (let i = 0, iMax = multipolys.length; i < iMax; i++) {
  2274. const mpA = multipolys[i];
  2275. for (let j = i + 1, jMax = multipolys.length; j < jMax; j++) {
  2276. if (getBboxOverlap(mpA.bbox, multipolys[j].bbox) === null) return [];
  2277. }
  2278. }
  2279. }
  2280. /* Put segment endpoints in a priority queue */
  2281. const queue = new Tree(SweepEvent.compare);
  2282. for (let i = 0, iMax = multipolys.length; i < iMax; i++) {
  2283. const sweepEvents = multipolys[i].getSweepEvents();
  2284. for (let j = 0, jMax = sweepEvents.length; j < jMax; j++) {
  2285. queue.insert(sweepEvents[j]);
  2286. if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
  2287. // prevents an infinite loop, an otherwise common manifestation of bugs
  2288. throw new Error("Infinite loop when putting segment endpoints in a priority queue " + "(queue size too big).");
  2289. }
  2290. }
  2291. }
  2292. /* Pass the sweep line over those endpoints */
  2293. const sweepLine = new SweepLine(queue);
  2294. let prevQueueSize = queue.size;
  2295. let node = queue.pop();
  2296. while (node) {
  2297. const evt = node.key;
  2298. if (queue.size === prevQueueSize) {
  2299. // prevents an infinite loop, an otherwise common manifestation of bugs
  2300. const seg = evt.segment;
  2301. 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.`);
  2302. }
  2303. if (queue.size > POLYGON_CLIPPING_MAX_QUEUE_SIZE) {
  2304. // prevents an infinite loop, an otherwise common manifestation of bugs
  2305. throw new Error("Infinite loop when passing sweep line over endpoints " + "(queue size too big).");
  2306. }
  2307. if (sweepLine.segments.length > POLYGON_CLIPPING_MAX_SWEEPLINE_SEGMENTS) {
  2308. // prevents an infinite loop, an otherwise common manifestation of bugs
  2309. throw new Error("Infinite loop when passing sweep line over endpoints " + "(too many sweep line segments).");
  2310. }
  2311. const newEvents = sweepLine.process(evt);
  2312. for (let i = 0, iMax = newEvents.length; i < iMax; i++) {
  2313. const evt = newEvents[i];
  2314. if (evt.consumedBy === undefined) queue.insert(evt);
  2315. }
  2316. prevQueueSize = queue.size;
  2317. node = queue.pop();
  2318. }
  2319. // free some memory we don't need anymore
  2320. rounder.reset();
  2321. /* Collect and compile segments we're keeping into a multipolygon */
  2322. const ringsOut = RingOut.factory(sweepLine.segments);
  2323. const result = new MultiPolyOut(ringsOut);
  2324. return result.getGeom();
  2325. }
  2326. }
  2327. // singleton available by import
  2328. const operation = new Operation();
  2329. const union = function (geom) {
  2330. for (var _len = arguments.length, moreGeoms = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  2331. moreGeoms[_key - 1] = arguments[_key];
  2332. }
  2333. return operation.run("union", geom, moreGeoms);
  2334. };
  2335. const intersection = function (geom) {
  2336. for (var _len2 = arguments.length, moreGeoms = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
  2337. moreGeoms[_key2 - 1] = arguments[_key2];
  2338. }
  2339. return operation.run("intersection", geom, moreGeoms);
  2340. };
  2341. const xor = function (geom) {
  2342. for (var _len3 = arguments.length, moreGeoms = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
  2343. moreGeoms[_key3 - 1] = arguments[_key3];
  2344. }
  2345. return operation.run("xor", geom, moreGeoms);
  2346. };
  2347. const difference = function (subjectGeom) {
  2348. for (var _len4 = arguments.length, clippingGeoms = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
  2349. clippingGeoms[_key4 - 1] = arguments[_key4];
  2350. }
  2351. return operation.run("difference", subjectGeom, clippingGeoms);
  2352. };
  2353. var index = {
  2354. union: union,
  2355. intersection: intersection,
  2356. xor: xor,
  2357. difference: difference
  2358. };
  2359. return index;
  2360. }));