diff --git a/.gitignore b/.gitignore index 64a90a3..4849b3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .DS_Store node_modules /dist -/build /demo/outline/ /demo/interactive/public/lines.json diff --git a/README.md b/README.md index 785e889..47a409a 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,12 @@ Install the module from npm: npm i isect ``` +Or download from CDN: + + + +If you download from CDN the library will be available under `isect` global name. + ## Basic usage The code below detects all intersections between segments in the array: diff --git a/build/isect.js b/build/isect.js new file mode 100644 index 0000000..dbafa0d --- /dev/null +++ b/build/isect.js @@ -0,0 +1,1742 @@ +/*! + * isect v1.0.0 + * (c) 2018 Andrei Kashcha. + * Released under the MIT License. + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.isect = {}))); +}(this, (function (exports) { 'use strict'; + + /* follows "An implementation of top-down splaying" + * by D. Sleator March 1992 + */ + + /** + * @typedef {*} Key + */ + + + /** + * @typedef {*} Value + */ + + + /** + * @typedef {function(node:Node):void} Visitor + */ + + + /** + * @typedef {function(a:Key, b:Key):number} Comparator + */ + + + /** + * @param {function(node:Node):string} NodePrinter + */ + + + /** + * @typedef {Object} Node + * @property {Key} Key + * @property {Value=} data + * @property {Node} left + * @property {Node} right + */ + + var Node = function Node (key, data) { + this.key = key; + this.data = data; + this.left = null; + this.right= null; + }; + + function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; } + + + /** + * Simple top down splay, not requiring i to be in the tree t. + * @param {Key} i + * @param {Node?} t + * @param {Comparator} comparator + */ + function splay (i, t, comparator) { + if (t === null) { return t; } + var l, r, y; + var N = new Node(); + l = r = N; + + while (true) { + var cmp = comparator(i, t.key); + //if (i < t.key) { + if (cmp < 0) { + if (t.left === null) { break; } + //if (i < t.left.key) { + if (comparator(i, t.left.key) < 0) { + y = t.left; /* rotate right */ + t.left = y.right; + y.right = t; + t = y; + if (t.left === null) { break; } + } + r.left = t; /* link right */ + r = t; + t = t.left; + //} else if (i > t.key) { + } else if (cmp > 0) { + if (t.right === null) { break; } + //if (i > t.right.key) { + if (comparator(i, t.right.key) > 0) { + y = t.right; /* rotate left */ + t.right = y.left; + y.left = t; + t = y; + if (t.right === null) { break; } + } + l.right = t; /* link left */ + l = t; + t = t.right; + } else { + break; + } + } + /* assemble */ + l.right = t.left; + r.left = t.right; + t.left = N.right; + t.right = N.left; + return t; + } + + + /** + * @param {Key} i + * @param {Value} data + * @param {Comparator} comparator + * @param {Tree} tree + * @return {Node} root + */ + function insert (i, data, t, comparator, tree) { + var node = new Node(i, data); + + tree._size++; + + if (t === null) { + node.left = node.right = null; + return node; + } + + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + if (cmp < 0) { + node.left = t.left; + node.right = t; + t.left = null; + } else if (cmp >= 0) { + node.right = t.right; + node.left = t; + t.right = null; + } + return node; + } + + + /** + * Insert i into the tree t, unless it's already there. + * @param {Key} i + * @param {Value} data + * @param {Comparator} comparator + * @param {Tree} tree + * @return {Node} root + */ + function add (i, data, t, comparator, tree) { + var node = new Node(i, data); + + if (t === null) { + node.left = node.right = null; + tree._size++; + return node; + } + + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + if (cmp === 0) { return t; } + else { + if (cmp < 0) { + node.left = t.left; + node.right = t; + t.left = null; + } else if (cmp > 0) { + node.right = t.right; + node.left = t; + t.right = null; + } + tree._size++; + return node; + } + } + + + /** + * Deletes i from the tree if it's there + * @param {Key} i + * @param {Tree} tree + * @param {Comparator} comparator + * @param {Tree} tree + * @return {Node} new root + */ + function remove (i, t, comparator, tree) { + var x; + if (t === null) { return null; } + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + if (cmp === 0) { /* found it */ + if (t.left === null) { + x = t.right; + } else { + x = splay(i, t.left, comparator); + x.right = t.right; + } + tree._size--; + return x; + } + return t; /* It wasn't there */ + } + + + function split (key, v, comparator) { + var left, right; + if (v === null) { + left = right = null; + } else { + v = splay(key, v, comparator); + + var cmp = comparator(v.key, key); + if (cmp === 0) { + left = v.left; + right = v.right; + } else if (cmp < 0) { + right = v.right; + v.right = null; + left = v; + } else { + left = v.left; + v.left = null; + right = v; + } + } + return { left: left, right: right }; + } + + + function merge (left, right, comparator) { + if (right === null) { return left; } + if (left === null) { return right; } + + right = splay(left.key, right, comparator); + right.left = left; + return right; + } + + + /** + * Prints level of the tree + * @param {Node} root + * @param {String} prefix + * @param {Boolean} isTail + * @param {Array} out + * @param {Function(node:Node):String} printNode + */ + function printRow (root, prefix, isTail, out, printNode) { + if (root) { + out(("" + prefix + (isTail ? '└── ' : '├── ') + (printNode(root)) + "\n")); + var indent = prefix + (isTail ? ' ' : '│ '); + if (root.left) { printRow(root.left, indent, false, out, printNode); } + if (root.right) { printRow(root.right, indent, true, out, printNode); } + } + } + + + var Tree = function Tree (comparator) { + if ( comparator === void 0 ) comparator = DEFAULT_COMPARE; + + this._comparator = comparator; + this._root = null; + this._size = 0; + }; + + var prototypeAccessors = { size: { configurable: true } }; + + + /** + * Inserts a key, allows duplicates + * @param{Key} key + * @param{Value=} data + * @return {Node|null} + */ + Tree.prototype.insert = function insert$1 (key, data) { + return this._root = insert(key, data, this._root, this._comparator, this); + }; + + + /** + * Adds a key, if it is not present in the tree + * @param{Key} key + * @param{Value=} data + * @return {Node|null} + */ + Tree.prototype.add = function add$1 (key, data) { + return this._root = add(key, data, this._root, this._comparator, this); + }; + + + /** + * @param{Key} key + * @return {Node|null} + */ + Tree.prototype.remove = function remove$1 (key) { + this._root = remove(key, this._root, this._comparator, this); + }; + + + /** + * Removes and returns the node with smallest key + * @return {?Node} + */ + Tree.prototype.pop = function pop () { + var node = this._root; + if (node) { + while (node.left) { node = node.left; } + this._root = splay(node.key,this._root, this._comparator); + this._root = remove(node.key, this._root, this._comparator, this); + return { key: node.key, data: node.data }; + } + return null; + }; + + + /** + * @param{Key} key + * @return {Node|null} + */ + Tree.prototype.findStatic = function findStatic (key) { + var current = this._root; + var compare = this._comparator; + while (current) { + var cmp = compare(key, current.key); + if (cmp === 0) { return current; } + else if (cmp < 0) { current = current.left; } + else { current = current.right; } + } + return null; + }; + + + /** + * @param{Key} key + * @return {Node|null} + */ + Tree.prototype.find = function find (key) { + if (this._root) { + this._root = splay(key, this._root, this._comparator); + if (this._comparator(key, this._root.key) !== 0) { return null; } + } + return this._root; + }; + + + /** + * @param{Key} key + * @return {Boolean} + */ + Tree.prototype.contains = function contains (key) { + var current = this._root; + var compare = this._comparator; + while (current) { + var cmp = compare(key, current.key); + if (cmp === 0) { return true; } + else if (cmp < 0) { current = current.left; } + else { current = current.right; } + } + return false; + }; + + + /** + * @param{Visitor} visitor + * @param{*=} ctx + * @return {SplayTree} + */ + Tree.prototype.forEach = function forEach (visitor, ctx) { + var current = this._root; + var Q = [];/* Initialize stack s */ + var done = false; + + while (!done) { + if (current !==null) { + Q.push(current); + current = current.left; + } else { + if (Q.length !== 0) { + current = Q.pop(); + visitor.call(ctx, current); + + current = current.right; + } else { done = true; } + } + } + return this; + }; + + + /** + * Walk key range from `low` to `high`. Stops if `fn` returns a value. + * @param{Key} low + * @param{Key} high + * @param{Function} fn + * @param{*?} ctx + * @return {SplayTree} + */ + Tree.prototype.range = function range (low, high, fn, ctx) { + var this$1 = this; + + var Q = []; + var compare = this._comparator; + var node = this._root, cmp; + + while (Q.length !== 0 || node) { + if (node) { + Q.push(node); + node = node.left; + } else { + node = Q.pop(); + cmp = compare(node.key, high); + if (cmp > 0) { + break; + } else if (compare(node.key, low) >= 0) { + if (fn.call(ctx, node)) { return this$1; } // stop if smth is returned + } + node = node.right; + } + } + return this; + }; + + + /** + * Returns array of keys + * @return {Array} + */ + Tree.prototype.keys = function keys () { + var keys = []; + this.forEach(function (ref) { + var key = ref.key; + + return keys.push(key); + }); + return keys; + }; + + + /** + * Returns array of all the data in the nodes + * @return {Array} + */ + Tree.prototype.values = function values () { + var values = []; + this.forEach(function (ref) { + var data = ref.data; + + return values.push(data); + }); + return values; + }; + + + /** + * @return {Key|null} + */ + Tree.prototype.min = function min () { + if (this._root) { return this.minNode(this._root).key; } + return null; + }; + + + /** + * @return {Key|null} + */ + Tree.prototype.max = function max () { + if (this._root) { return this.maxNode(this._root).key; } + return null; + }; + + + /** + * @return {Node|null} + */ + Tree.prototype.minNode = function minNode (t) { + if ( t === void 0 ) t = this._root; + + if (t) { while (t.left) { t = t.left; } } + return t; + }; + + + /** + * @return {Node|null} + */ + Tree.prototype.maxNode = function maxNode (t) { + if ( t === void 0 ) t = this._root; + + if (t) { while (t.right) { t = t.right; } } + return t; + }; + + + /** + * Returns node at given index + * @param{number} index + * @return {?Node} + */ + Tree.prototype.at = function at (index) { + var current = this._root, done = false, i = 0; + var Q = []; + + while (!done) { + if (current) { + Q.push(current); + current = current.left; + } else { + if (Q.length > 0) { + current = Q.pop(); + if (i === index) { return current; } + i++; + current = current.right; + } else { done = true; } + } + } + return null; + }; + + + /** + * @param{Node} d + * @return {Node|null} + */ + Tree.prototype.next = function next (d) { + var root = this._root; + var successor = null; + + if (d.right) { + successor = d.right; + while (successor.left) { successor = successor.left; } + return successor; + } + + var comparator = this._comparator; + while (root) { + var cmp = comparator(d.key, root.key); + if (cmp === 0) { break; } + else if (cmp < 0) { + successor = root; + root = root.left; + } else { root = root.right; } + } + + return successor; + }; + + + /** + * @param{Node} d + * @return {Node|null} + */ + Tree.prototype.prev = function prev (d) { + var root = this._root; + var predecessor = null; + + if (d.left !== null) { + predecessor = d.left; + while (predecessor.right) { predecessor = predecessor.right; } + return predecessor; + } + + var comparator = this._comparator; + while (root) { + var cmp = comparator(d.key, root.key); + if (cmp === 0) { break; } + else if (cmp < 0) { root = root.left; } + else { + predecessor = root; + root = root.right; + } + } + return predecessor; + }; + + + /** + * @return {SplayTree} + */ + Tree.prototype.clear = function clear () { + this._root = null; + this._size = 0; + return this; + }; + + + /** + * @return {NodeList} + */ + Tree.prototype.toList = function toList$1 () { + return toList(this._root); + }; + + + /** + * Bulk-load items. Both array have to be same size + * @param{Array} keys + * @param{Array}[values] + * @param{Boolean} [presort=false] Pre-sort keys and values, using + * tree's comparator. Sorting is done + * in-place + * @return {AVLTree} + */ + Tree.prototype.load = function load (keys, values, presort) { + if ( keys === void 0 ) keys = []; + if ( values === void 0 ) values = []; + if ( presort === void 0 ) presort = false; + + var size = keys.length; + var comparator = this._comparator; + + // sort if needed + if (presort) { sort(keys, values, 0, size - 1, comparator); } + + if (this._root === null) { // empty tree + this._root = loadRecursive(this._root, keys, values, 0, size); + this._size = size; + } else { // that re-builds the whole tree from two in-order traversals + var mergedList = mergeLists(this.toList(), createList(keys, values), comparator); + size = this._size + size; + this._root = sortedListToBST({ head: mergedList }, 0, size); + } + return this; + }; + + + /** + * @return {Boolean} + */ + Tree.prototype.isEmpty = function isEmpty () { return this._root === null; }; + + prototypeAccessors.size.get = function () { return this._size; }; + + + /** + * @param{NodePrinter=} printNode + * @return {String} + */ + Tree.prototype.toString = function toString (printNode) { + if ( printNode === void 0 ) printNode = function (n) { return n.key; }; + + var out = []; + printRow(this._root, '', true, function (v) { return out.push(v); }, printNode); + return out.join(''); + }; + + + Tree.prototype.update = function update (key, newKey, newData) { + var comparator = this._comparator; + var ref = split(key, this._root, comparator); + var left = ref.left; + var right = ref.right; + this._size--; + if (comparator(key, newKey) < 0) { + right = insert(newKey, newData, right, comparator, this); + } else { + left = insert(newKey, newData, left, comparator, this); + } + this._root = merge(left, right, comparator); + }; + + + Tree.prototype.split = function split$1 (key) { + return split(key, this._root, this._comparator); + }; + + Object.defineProperties( Tree.prototype, prototypeAccessors ); + + + function loadRecursive (parent, keys, values, start, end) { + var size = end - start; + if (size > 0) { + var middle = start + Math.floor(size / 2); + var key = keys[middle]; + var data = values[middle]; + var node = { key: key, data: data, parent: parent }; + node.left = loadRecursive(node, keys, values, start, middle); + node.right = loadRecursive(node, keys, values, middle + 1, end); + return node; + } + return null; + } + + + function createList(keys, values) { + var head = { next: null }; + var p = head; + for (var i = 0; i < keys.length; i++) { + p = p.next = { key: keys[i], data: values[i] }; + } + p.next = null; + return head.next; + } + + + function toList (root) { + var current = root; + var Q = [], done = false; + + var head = { next: null }; + var p = head; + + while (!done) { + if (current) { + Q.push(current); + current = current.left; + } else { + if (Q.length > 0) { + current = p = p.next = Q.pop(); + current = current.right; + } else { done = true; } + } + } + p.next = null; // that'll work even if the tree was empty + return head.next; + } + + + function sortedListToBST(list, start, end) { + var size = end - start; + if (size > 0) { + var middle = start + Math.floor(size / 2); + var left = sortedListToBST(list, start, middle); + + var root = list.head; + root.left = left; + + list.head = list.head.next; + + root.right = sortedListToBST(list, middle + 1, end); + return root; + } + return null; + } + + + function mergeLists (l1, l2, compare) { + if ( compare === void 0 ) compare = function (a, b) { return a - b; }; + + var head = {}; // dummy + var p = head; + + var p1 = l1; + var p2 = l2; + + while (p1 !== null && p2 !== null) { + if (compare(p1.key, p2.key) < 0) { + p.next = p1; + p1 = p1.next; + } else { + p.next = p2; + p2 = p2.next; + } + p = p.next; + } + + if (p1 !== null) { p.next = p1; } + else if (p2 !== null) { p.next = p2; } + + return head.next; + } + + + function sort(keys, values, left, right, compare) { + if (left >= right) { return; } + + var pivot = keys[(left + right) >> 1]; + var i = left - 1; + var j = right + 1; + + while (true) { + do { i++; } while (compare(keys[i], pivot) < 0); + do { j--; } while (compare(keys[j], pivot) > 0); + if (i >= j) { break; } + + var tmp = keys[i]; + keys[i] = keys[j]; + keys[j] = tmp; + + tmp = values[i]; + values[i] = values[j]; + values[j] = tmp; + } + + sort(keys, values, left, j, compare); + sort(keys, values, j + 1, right, compare); + } + + function createEventQueue(byY) { + var q = new Tree(byY); + + return { + isEmpty: isEmpty, + size: size, + pop: pop, + find: find, + insert: insert + } + + function find(p) { + return q.find(p); + } + + function size() { + return q.size; + } + + function isEmpty() { + return q.isEmpty(); + } + + function insert(event) { + // debugger; + q.add(event.point, event); + } + + function pop() { + var node = q.pop(); + return node && node.data; + } + } + + /** + * Just a collection of geometry related utilities + */ + + // This is used for precision checking (e.g. two numbers are equal + // if their difference is smaller than this number). The value is + // chosen empirically. We still may run into precision related issues. + // TODO: we should allow consumers to configure this. + var EPS = 1e-9;//10; + + function getIntersectionXPoint(segment, xPos, yPos) { + var dy1 = segment.from.y - yPos; + var dy2 = yPos - segment.to.y; + var dy = segment.to.y - segment.from.y; + if (Math.abs(dy1) < EPS) { + // The segment starts on the sweep line + if (Math.abs(dy) < EPS) { + // the segment is horizontal. Intersection is at the point + if (xPos <= segment.from.x) { return segment.from.x; } + if (xPos > segment.to.x) { return segment.to.x; } + return xPos; + } + return segment.from.x; + } + + var dx = (segment.to.x - segment.from.x); + var xOffset; + if (dy1 >= dy2) { + xOffset = dy1 * (dx / dy); + return (segment.from.x - xOffset); + } + xOffset = dy2 * (dx / dy); + return (segment.to.x + xOffset); + } + + function angle(dx, dy) { + // https://stackoverflow.com/questions/16542042/fastest-way-to-sort-vectors-by-angle-without-actually-computing-that-angle + var p = dx/(Math.abs(dx) + Math.abs(dy)); // -1 .. 1 increasing with x + + if (dy < 0) { return p - 1; } // -2 .. 0 increasing with x + return 1 - p // 0 .. 2 decreasing with x + } + + function intersectSegments(a, b) { + // https://stackoverflow.com/a/1968345/125351 + var aStart = a.from, bStart = b.from; + var p0_x = aStart.x, p0_y = aStart.y, + p2_x = bStart.x, p2_y = bStart.y; + + var s1_x = a.dx, s1_y = a.dy, s2_x = b.dx, s2_y = b.dy; + var div = s1_x * s2_y - s2_x * s1_y; + + var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div; + if (s < 0 || s > 1) { return; } + + var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div; + + if (t >= 0 && t <= 1) { + return { + x: p0_x - (t * s1_x), + y: p0_y - (t * s1_y) + } + } + } + + function samePoint(a, b) { + return Math.abs(a.x - b.x) < EPS && Math.abs(a.y - b.y) < EPS; + } + + /** + * Creates a new sweep status data structure. + */ + function createSweepStatus(onError, EPS$$1) { + var lastPointY, prevY; + var lastPointX, prevX; + var useBelow = false; + var status = new Tree(compareSegments); + + // To save on GC we return mutable object. + var currentBoundary = { + beforeLeft: null, + left: null, + right: null, + afterRight: null, + }; + + var currentLeftRight = {left: null, right: null}; + + return { + /** + * Add new segments into the status tree. + */ + insertSegments: insertSegments, + + /** + * Remove segments from the status tree. + */ + deleteSegments: deleteSegments, + + /** + * Returns segments that are to the left and right from a given point. + */ + getLeftRightPoint: getLeftRightPoint, + + /** + * For a given collections of segments finds the most left and the most right + * segments. Also returns segments immediately before left, and after right segments. + */ + getBoundarySegments: getBoundarySegments, + + findSegmentsWithPoint: findSegmentsWithPoint, + + /** + * Current binary search tree with segments + */ + status: status, + + /** + * Introspection method that verifies if there are duplicates in the segment tree. + * If there are - `onError()` is called. + */ + checkDuplicate: checkDuplicate, + + /** + * Prints current segments in order of their intersection with sweep line. Introspection method. + */ + printStatus: printStatus, + + /** + * Returns current position of the sweep line. + */ + getLastPoint: function getLastPoint() { + return {x: lastPointX, y: lastPointY}; + } + } + + function compareSegments(a, b) { + if (a === b) { return 0; } + + var ak = getIntersectionXPoint(a, lastPointX, lastPointY); + var bk = getIntersectionXPoint(b, lastPointX, lastPointY); + + var res = ak - bk; + if (Math.abs(res) >= EPS$$1) { + // We are okay fine. Intersection distance between two segments + // is good to give conclusive answer + return res; + } + + var aIsHorizontal = Math.abs(a.dy) < EPS$$1; + var bIsHorizontal = Math.abs(b.dy) < EPS$$1; + if (aIsHorizontal && bIsHorizontal) { + return b.to.x - a.to.x; + } + // TODO: What if both a and b is horizontal? + // move horizontal to end + if (aIsHorizontal) { + return useBelow ? -1 : 1; + } + + if (bIsHorizontal) { + if (useBelow) { + return (b.from.x >= lastPointX) ? -1 : 1 + } + return -1; + // return useBelow ? 1 : -1; + } + var pa = a.angle; + var pb = b.angle; + if (Math.abs(pa - pb) >= EPS$$1) { + return useBelow ? pa - pb : pb - pa; + } + + var segDist = a.from.y - b.from.y; + if (Math.abs(segDist) >= EPS$$1) { + return -segDist; + } + segDist = a.to.y - b.to.y; + if (Math.abs(segDist) >= EPS$$1) { + // TODO: Is this accurate? + return -segDist; + } + + return 0; + // Could also use: + // var aAngle = Math.atan2(a.from.y - a.to.y, a.from.x - a.to.x); + // var bAngle = Math.atan2(b.from.y - b.to.y, b.from.x - b.to.x); + // return useBelow ? bAngle - aAngle : aAngle - bAngle; + } + + function getBoundarySegments(upper, interior) { + var leftMost, rightMost, i; + var uLength = upper.length; + + if (uLength > 0) { + leftMost = rightMost = upper[0]; + } else { + leftMost = rightMost = interior[0]; + } + + for (i = 1; i < uLength; ++i) { + var s = upper[i]; + var cmp = compareSegments(leftMost, s); + if (cmp > 0) { leftMost = s; } + + cmp = compareSegments(rightMost, s); + if (cmp < 0) { rightMost = s; } + } + + var startFrom = uLength > 0 ? 0 : 1; + for (i = startFrom; i < interior.length; ++i) { + s = interior[i]; + cmp = compareSegments(leftMost, s); + if (cmp > 0) { leftMost = s; } + + cmp = compareSegments(rightMost, s); + if (cmp < 0) { rightMost = s; } + } + + // at this point we have our left/right segments in the status. + // Let's find their prev/next elements and report them back: + var left = status.find(leftMost); + if (!left) { + onError('Left is missing. Precision error?'); + } + + var right = status.find(rightMost); + if (!right) { + onError('Right is missing. Precision error?'); + } + + var beforeLeft = left && status.prev(left); + var afterRight = right && status.next(right); + + while (afterRight && right.key.dy === 0 && afterRight.key.dy === 0) { + // horizontal segments are special :( + afterRight = status.next(afterRight); + } + + currentBoundary.beforeLeft = beforeLeft && beforeLeft.key; + currentBoundary.left = left && left.key; + currentBoundary.right = right && right.key; + currentBoundary.afterRight = afterRight && afterRight.key; + + return currentBoundary; + } + + function getLeftRightPoint(p) { + // We are trying to find left and right segments that are nearest to the + // point p. For this we traverse the binary search tree, and remember + // node with the shortest distance to p. + var lastLeft; + var current = status._root; + var minX = Number.POSITIVE_INFINITY; + while (current) { + var x = getIntersectionXPoint(current.key, p.x, p.y); + var dx = p.x - x; + if (dx >= 0) { + if (dx < minX) { + minX = dx; + lastLeft = current; + current = current.left; + } else { + break; + } + } else { + if (-dx < minX) { + minX = -dx; + lastLeft = current; + current = current.right; + } else { + break; + } + } + } + + currentLeftRight.left = lastLeft && lastLeft.key; + var next = lastLeft && status.next(lastLeft); + currentLeftRight.right = next && next.key; + return currentLeftRight; + + // Conceptually, the code above should be equivalent to the code below; + // The code below is easier to understand, but intuitively, the code above + // should have better performance (as we do not traverse the entire status + // tree) + + // var right, left, x; + // var all = status.keys() + // for (var i = 0; i < all.length; ++i) { + // var segment = all[i]; + // x = getIntersectionXPoint(segment, p.x, p.y); + // if (x > p.x && !right) { + // right = segment; + // break; + // } else if (x < p.x) { + // left = segment; + // } + // } + + // currentLeftRight.left = left; + // currentLeftRight.right = right; + + // return currentLeftRight; + } + + function findSegmentsWithPoint(p, onFound) { + // Option 1. + // var arrResults = []; + // status.forEach(current => { + // var x = getIntersectionXPoint(current.key, p.x, p.y); + // var dx = p.x - x; + // if (Math.abs(dx) < EPS) { + // onFound(current.key); + // // arrResults.push(current.key) + // } + // }); + // return arrResults; + + // Option 2. + + // let current = status._root; + // const Q = []; /* Initialize stack s */ + // let done = false; + // var res = []; + // var breakEarly = false; + + // while (!done) { + // if (current !== null) { + // Q.push(current); + // current = current.left; + // } else { + // if (Q.length !== 0) { + // current = Q.pop(); + + // var x = getIntersectionXPoint(current.key, p.x, p.y); + // var dx = p.x - x; + // if (Math.abs(dx) < EPS) { + // res.push(current.key) + // breakEarly = true; + // } else if (breakEarly) { + // done = true; + // } + + // current = current.right; + // } else done = true; + // } + // } + + // return res; + + // option 3. + var current = status._root; + + while (current) { + var x = getIntersectionXPoint(current.key, p.x, p.y); + var dx = p.x - x; + if (Math.abs(dx) < EPS$$1) { + collectAdjacentNodes(current, p, onFound); + break; + } else if (dx < 0) { + current = current.left; + } else { + current = current.right; + } + } + } + + function collectAdjacentNodes(root, p, onFound) { + onFound(root.key); + goOverPredecessors(root.left, p, onFound); + goOverSuccessors(root.right, p, onFound); + } + + function goOverPredecessors(root, p, res) { + if (!root) { return; } + var x = getIntersectionXPoint(root.key, p.x, p.y); + var dx = p.x - x; + if (Math.abs(dx) < EPS$$1) { + collectAdjacentNodes(root, p, res); + } else { + goOverPredecessors(root.right, p, res); + } + } + + function goOverSuccessors(root, p, res) { + if (!root) { return; } + var x = getIntersectionXPoint(root.key, p.x, p.y); + var dx = p.x - x; + if (Math.abs(dx) < EPS$$1) { + collectAdjacentNodes(root, p, res); + } else { + goOverSuccessors(root.left, p, res); + } + } + + function checkDuplicate() { + var prev; + status.forEach(function (node) { + var current = node.key; + + if (prev) { + if (samePoint(prev.from, current.from) && samePoint(prev.to, current.to)) { + // Likely you have received error before during segment removal. + onError('Duplicate key in the status! This may be caused by Floating Point rounding error'); + } + } + prev = current; + }); + } + + function printStatus(prefix) { + if ( prefix === void 0 ) prefix = ''; + + // eslint-disable-next-line + console.log(prefix, 'status line: ', lastPointX, lastPointY); + status.forEach(function (node) { + var x = getIntersectionXPoint(node.key, lastPointX, lastPointY); + // eslint-disable-next-line + console.log(x + ' ' + node.key.name); + }); + } + + function insertSegments(interior, upper, sweepLinePos) { + lastPointY = sweepLinePos.y; + lastPointX = sweepLinePos.x; + var key; + + for (var i = 0; i < interior.length; ++i) { + key = interior[i]; + status.add(key); + } + for (i = 0; i < upper.length; ++i) { + key = upper[i]; + status.add(key); + } + } + + function deleteSegments(lower, interior, sweepLinePos) { + // I spent most of the time debugging this method. Depending on the + // algorithm state we can run into situation when dynamic keys of the + // `status` tree predict wrong branch, and thus we are not able to find + // the segment that needs to be deleted. If that happens I'm trying to + // use previous point and repeat the process. This may result in + // incorrect state. In that case I report an error. + var i; + var prevCount = status._size; + prevX = lastPointX; + prevY = lastPointY; + lastPointY = sweepLinePos.y; + lastPointX = sweepLinePos.x; + + useBelow = true; + for(i = 0; i < lower.length; ++i) { + removeSegment(lower[i], sweepLinePos); + } + for(i = 0; i < interior.length; ++i) { + removeSegment(interior[i], sweepLinePos); + } + useBelow = false; + + if (status._size !== prevCount - interior.length - lower.length) { + // This can happen when rounding error occurs. You can try scaling your input + onError('Segments were not removed from a tree properly. Precision error?'); + } + } + + function removeSegment(key, sweepLinePos) { + if (status.find(key)) { + status.remove(key); + } else { + lastPointX = prevX; + lastPointY = prevY; + if (status.find(key)) { + status.remove(key); + } + lastPointY = sweepLinePos.y; + lastPointX = sweepLinePos.x; + } + } + } + + /** + * Represents a single event in the sweep-line algorithm + */ + var SweepEvent = function SweepEvent(point, segment) { + this.point = point; + if (segment) { this.from = [segment]; } + }; + + /** + * A point on a line + * + * @typedef {Object} Point + * @property {number} x coordinate + * @property {number} y coordinate + */ + + + /** + * @typedef {Object} Segment + * @property {Point} from start of the segment + * @property {Point} to end of the segment + */ + + /** + * @typedef {function(point : Point, interior : Segment[], lower : Segment[], upper : Segment[])} ReportIntersectionCallback + */ + + /** + * @typedef {Object} ISectOptions + * @property {ReportIntersectionCallback} onFound + */ + + /** + * @typedef {Object} ISectResult + */ + + // We use EMPTY array to avoid pressure on garbage collector. Need to be + // very cautious to not mutate this array. + var EMPTY = []; + + /** + * Finds all intersections among given segments. + * + * The algorithm follows "Computation Geometry, Algorithms and Applications" book + * by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars. + * + * Line is swept top-down + * + * @param {Segment[]} segments + * @param {ISectOptions=} options + * @returns {ISectResult} + */ + function isect(segments, options) { + var results = []; + var reportIntersection = (options && options.onFound) || defaultIntersectionReporter; + + var onError = (options && options.onError) || defaultErrorReporter; + + var eventQueue = createEventQueue(byY); + var sweepStatus = createSweepStatus(onError, EPS); + var lower, interior, lastPoint; + + segments.forEach(addSegment); + + return { + /** + * Find all intersections synchronously. + * + * @returns array of found intersections. + */ + run: run, + + /** + * Performs a single step in the sweep line algorithm + * + * @returns true if there was something to process; False if no more work to do + */ + step: step, + + // Methods below are low level API for fine-grained control. + // Don't use it unless you understand this code thoroughly + + /** + * Add segment into the + */ + addSegment: addSegment, + + /** + * Direct access to event queue. Queue contains segment endpoints and + * pending detected intersections. + */ + eventQueue: eventQueue, + + /** + * Direct access to sweep line status. "Status" holds information about + * all intersected segments. + */ + sweepStatus: sweepStatus, + + /** + * Access to results array. Works only when you use default onFound() handler + */ + results: results + } + + function run() { + while (!eventQueue.isEmpty()) { + var eventPoint = eventQueue.pop(); + if (handleEventPoint(eventPoint)) { + // they decided to stop. + return; + } } + + return results; + } + + function step() { + if (!eventQueue.isEmpty()) { + var eventPoint = eventQueue.pop(); + handleEventPoint(eventPoint); + // Note: we don't check results of `handleEventPoint()` + // assumption is that client controls `step()` and thus they + // know better if they want to stop. + return true; + } + return false; + } + + function handleEventPoint(p) { + lastPoint = p.point; + var upper = p.from || EMPTY; + + lower = interior = undefined; + // TODO: move lower/interior into sweep status method? + + sweepStatus.findSegmentsWithPoint(lastPoint, addLowerOrInterior); + // if (segmentsWithPoint) { + // segmentsWithPoint.forEach() + // } + + if (!lower) { lower = EMPTY; } + if (!interior) { interior = EMPTY; } + + var uLength = upper.length; + var iLength = interior.length; + var lLength = lower.length; + var hasIntersection = uLength + iLength + lLength > 1; + var hasPointIntersection = !hasIntersection && (uLength === 0 && lLength === 0 && iLength > 0); + + if (hasIntersection || hasPointIntersection) { + p.isReported = true; + if (reportIntersection(lastPoint, interior, lower, upper)) { + return true; + } + } + + sweepStatus.deleteSegments(lower, interior, lastPoint); + sweepStatus.insertSegments(interior, upper, lastPoint); + + var sLeft, sRight; + + var hasNoCrossing = (uLength + iLength === 0); + + if (hasNoCrossing) { + var leftRight = sweepStatus.getLeftRightPoint(lastPoint); + sLeft = leftRight.left; + if (!sLeft) { return; } + + sRight = leftRight.right; + if (!sRight) { return; } + + findNewEvent(sLeft, sRight, p); + } else { + var boundarySegments = sweepStatus.getBoundarySegments(upper, interior); + + findNewEvent(boundarySegments.beforeLeft, boundarySegments.left, p); + findNewEvent(boundarySegments.right, boundarySegments.afterRight, p); + } + + return false; + } + + function addLowerOrInterior(s) { + if (samePoint(s.to, lastPoint)) { + if (!lower) { lower = [s]; } + else { lower.push(s); } + } else if (!samePoint(s.from, lastPoint)) { + if (!interior) { interior = [s]; } + else { interior.push(s); } + } + } + + function findNewEvent(left, right, p) { + if (!left || !right) { return; } + + var intersection = intersectSegments(left, right); + if (!intersection) { + return; + } + + var dy = p.point.y - intersection.y; + // TODO: should I add dy to intersection.y? + if (dy < -EPS) { + // this means intersection happened after the sweep line. + // We already processed it. + return; + } + if (Math.abs(dy) < EPS && intersection.x <= p.point.x) { + return; + } + + // Need to adjust floating point for this special case, + // since otherwise it gives rounding errors: + roundNearZero(intersection); + + var current = eventQueue.find(intersection); + + if (current && current.isReported) { + // We already reported this event. No need to add it one more time + // TODO: Is this case even possible? + onError('We already reported this event.'); + return; + } + + if (!current) { + var event = new SweepEvent(intersection); + eventQueue.insert(event); + } + } + + function defaultIntersectionReporter(p, interior, lower, upper) { + results.push({ + point: p, + segments: union(union(interior, lower), upper) + }); + } + + function addSegment(segment) { + var from = segment.from; + var to = segment.to; + + // Small numbers give more precision errors. Rounding them to 0. + roundNearZero(from); + roundNearZero(to); + + var dy = from.y - to.y; + + // Note: dy is much smaller then EPS on purpose. I found that higher + // precision here does less good - getting way more rounding errors. + if (Math.abs(dy) < 1e-5) { + from.y = to.y; + segment.dy = 0; + } + if ((from.y < to.y) || ( + (from.y === to.y) && (from.x > to.x)) + ) { + var temp = from; + from = segment.from = to; + to = segment.to = temp; + } + + // We pre-compute some immutable properties of the segment + // They are used quite often in the tree traversal, and pre-computation + // gives significant boost: + segment.dy = from.y - to.y; + segment.dx = from.x - to.x; + segment.angle = angle(segment.dy, segment.dx); + + var isPoint = segment.dy === segment.dx && segment.dy === 0; + var prev = eventQueue.find(from); + if (prev && !isPoint) { + // this detects identical segments early. Without this check + // the algorithm would break since sweep line has no means to + // detect identical segments. + var prevFrom = prev.data.from; + if (prevFrom) { + for (var i = 0; i < prevFrom.length; ++i) { + var s = prevFrom[i]; + if (samePoint(s.to, to)) { + reportIntersection(s.from, [], s.from, s.to); + reportIntersection(s.to, [], s.from, s.to); + return; + } + } + } + } + + if (!isPoint) { + if (prev) { + if (prev.data.from) { prev.data.from.push(segment); } + else { prev.data.from = [segment]; } + } else { + var e = new SweepEvent(from, segment); + eventQueue.insert(e); + } + var event = new SweepEvent(to); + eventQueue.insert(event); + } else { + var event = new SweepEvent(to); + eventQueue.insert(event); + } + } + } + + function roundNearZero(point) { + if (Math.abs(point.x) < EPS) { point.x = 0; } + if (Math.abs(point.y) < EPS) { point.y = 0; } + } + + function defaultErrorReporter(errorMessage) { + throw new Error(errorMessage); + } + + function union(a, b) { + if (!a) { return b; } + if (!b) { return a; } + + return a.concat(b); + } + + function byY(a, b) { + // decreasing Y + var res = b.y - a.y; + // TODO: This might mess up the status tree. + if (Math.abs(res) < EPS) { + // increasing x. + res = a.x - b.x; + if (Math.abs(res) < EPS) { res = 0; } + } + + return res; + } + + /** + * This is a brute force solution with O(n^2) performance. + * (`n` is number of segments). + * + * Use this when number of lines is low, and number of intersections + * is high. + */ + + function brute(lines, options) { + var results = []; + var reportIntersection = (options && options.onFound) || + defaultIntersectionReporter; + var asyncState; + + return { + /** + * Execute brute force of the segment intersection search + */ + run: run, + /** + * Access to results array. Works only when you use default onFound() handler + */ + results: results, + + /** + * Performs a single step in the brute force algorithm () + */ + step: step + } + + function step() { + if (!asyncState) { + asyncState = { + i: 0 + }; + } + var test = lines[asyncState.i]; + for (var j = asyncState.i + 1; j < lines.length; ++j) { + var other = lines[j]; + var pt = intersectSegments$1(test, other); + if (pt) { + if (reportIntersection(pt, [test, other])) { + return; + } + } + } + asyncState.i += 1; + return asyncState.i < lines.length; + } + + function run() { + for(var i = 0; i < lines.length; ++i) { + var test = lines[i]; + for (var j = i + 1; j < lines.length; ++j) { + var other = lines[j]; + var pt = intersectSegments$1(test, other); + if (pt) { + if (reportIntersection(pt, [test, other])) { + return; + } + } + } + } + return results; + } + + function defaultIntersectionReporter(p, interior) { + results.push({ + point: p, + segments: interior + }); + } + } + + function intersectSegments$1(a, b) { + // https://stackoverflow.com/a/1968345/125351 + var aStart = a.from, bStart = b.from; + var p0_x = aStart.x, p0_y = aStart.y, + p2_x = bStart.x, p2_y = bStart.y; + + var s1_x = a.from.x - a.to.x, s1_y = a.from.y - a.to.y, s2_x = b.from.x - b.to.x, s2_y = b.from.y - b.to.y; + var div = s1_x * s2_y - s2_x * s1_y; + + var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div; + if (s < 0 || s > 1) { return; } + + var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div; + + if (t >= 0 && t <= 1) { + return { + x: p0_x - (t * s1_x), + y: p0_y - (t * s1_y) + } + } + } + + exports.sweep = isect; + exports.brute = brute; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); +//# sourceMappingURL=isect.js.map diff --git a/build/isect.js.map b/build/isect.js.map new file mode 100644 index 0000000..e898a9a --- /dev/null +++ b/build/isect.js.map @@ -0,0 +1 @@ +{"version":3,"file":"isect.js","sources":["../node_modules/splaytree/index.js","../src/createEventQueue.js","../src/geom.js","../src/sweepStatus.js","../src/SweepEvent.js","../src/sweep.js","../src/brute.js"],"sourcesContent":["/* follows \"An implementation of top-down splaying\"\n * by D. Sleator March 1992\n */\n\n/**\n * @typedef {*} Key\n */\n\n\n/**\n * @typedef {*} Value\n */\n\n\n/**\n * @typedef {function(node:Node):void} Visitor\n */\n\n\n/**\n * @typedef {function(a:Key, b:Key):number} Comparator\n */\n\n\n/**\n * @param {function(node:Node):string} NodePrinter\n */\n\n\n/**\n * @typedef {Object} Node\n * @property {Key} Key\n * @property {Value=} data\n * @property {Node} left\n * @property {Node} right\n */\n\nclass Node {\n\n constructor (key, data) {\n this.key = key;\n this.data = data;\n this.left = null;\n this.right = null;\n }\n}\n\nfunction DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }\n\n\n/**\n * Simple top down splay, not requiring i to be in the tree t.\n * @param {Key} i\n * @param {Node?} t\n * @param {Comparator} comparator\n */\nfunction splay (i, t, comparator) {\n if (t === null) return t;\n let l, r, y;\n const N = new Node();\n l = r = N;\n\n while (true) {\n const cmp = comparator(i, t.key);\n //if (i < t.key) {\n if (cmp < 0) {\n if (t.left === null) break;\n //if (i < t.left.key) {\n if (comparator(i, t.left.key) < 0) {\n y = t.left; /* rotate right */\n t.left = y.right;\n y.right = t;\n t = y;\n if (t.left === null) break;\n }\n r.left = t; /* link right */\n r = t;\n t = t.left;\n //} else if (i > t.key) {\n } else if (cmp > 0) {\n if (t.right === null) break;\n //if (i > t.right.key) {\n if (comparator(i, t.right.key) > 0) {\n y = t.right; /* rotate left */\n t.right = y.left;\n y.left = t;\n t = y;\n if (t.right === null) break;\n }\n l.right = t; /* link left */\n l = t;\n t = t.right;\n } else {\n break;\n }\n }\n /* assemble */\n l.right = t.left;\n r.left = t.right;\n t.left = N.right;\n t.right = N.left;\n return t;\n}\n\n\n/**\n * @param {Key} i\n * @param {Value} data\n * @param {Comparator} comparator\n * @param {Tree} tree\n * @return {Node} root\n */\nfunction insert (i, data, t, comparator, tree) {\n const node = new Node(i, data);\n\n tree._size++;\n\n if (t === null) {\n node.left = node.right = null;\n return node;\n }\n\n t = splay(i, t, comparator);\n const cmp = comparator(i, t.key);\n if (cmp < 0) {\n node.left = t.left;\n node.right = t;\n t.left = null;\n } else if (cmp >= 0) {\n node.right = t.right;\n node.left = t;\n t.right = null;\n }\n return node;\n}\n\n\n/**\n * Insert i into the tree t, unless it's already there.\n * @param {Key} i\n * @param {Value} data\n * @param {Comparator} comparator\n * @param {Tree} tree\n * @return {Node} root\n */\nfunction add (i, data, t, comparator, tree) {\n const node = new Node(i, data);\n\n if (t === null) {\n node.left = node.right = null;\n tree._size++;\n return node;\n }\n\n t = splay(i, t, comparator);\n const cmp = comparator(i, t.key);\n if (cmp === 0) return t;\n else {\n if (cmp < 0) {\n node.left = t.left;\n node.right = t;\n t.left = null;\n } else if (cmp > 0) {\n node.right = t.right;\n node.left = t;\n t.right = null;\n }\n tree._size++;\n return node;\n }\n}\n\n\n/**\n * Deletes i from the tree if it's there\n * @param {Key} i\n * @param {Tree} tree\n * @param {Comparator} comparator\n * @param {Tree} tree\n * @return {Node} new root\n */\nfunction remove (i, t, comparator, tree) {\n let x;\n if (t === null) return null;\n t = splay(i, t, comparator);\n var cmp = comparator(i, t.key);\n if (cmp === 0) { /* found it */\n if (t.left === null) {\n x = t.right;\n } else {\n x = splay(i, t.left, comparator);\n x.right = t.right;\n }\n tree._size--;\n return x;\n }\n return t; /* It wasn't there */\n}\n\n\nfunction split (key, v, comparator) {\n let left, right;\n if (v === null) {\n left = right = null;\n } else {\n v = splay(key, v, comparator);\n\n const cmp = comparator(v.key, key);\n if (cmp === 0) {\n left = v.left;\n right = v.right;\n } else if (cmp < 0) {\n right = v.right;\n v.right = null;\n left = v;\n } else {\n left = v.left;\n v.left = null;\n right = v;\n }\n }\n return { left, right };\n}\n\n\nfunction merge (left, right, comparator) {\n if (right === null) return left;\n if (left === null) return right;\n\n right = splay(left.key, right, comparator);\n right.left = left;\n return right;\n}\n\n\n/**\n * Prints level of the tree\n * @param {Node} root\n * @param {String} prefix\n * @param {Boolean} isTail\n * @param {Array} out\n * @param {Function(node:Node):String} printNode\n */\nfunction printRow (root, prefix, isTail, out, printNode) {\n if (root) {\n out(`${ prefix }${ isTail ? '└── ' : '├── ' }${ printNode(root) }\\n`);\n const indent = prefix + (isTail ? ' ' : '│ ');\n if (root.left) printRow(root.left, indent, false, out, printNode);\n if (root.right) printRow(root.right, indent, true, out, printNode);\n }\n}\n\n\nexport default class Tree {\n\n constructor (comparator = DEFAULT_COMPARE) {\n this._comparator = comparator;\n this._root = null;\n this._size = 0;\n }\n\n\n /**\n * Inserts a key, allows duplicates\n * @param {Key} key\n * @param {Value=} data\n * @return {Node|null}\n */\n insert (key, data) {\n return this._root = insert(key, data, this._root, this._comparator, this);\n }\n\n\n /**\n * Adds a key, if it is not present in the tree\n * @param {Key} key\n * @param {Value=} data\n * @return {Node|null}\n */\n add (key, data) {\n return this._root = add(key, data, this._root, this._comparator, this);\n }\n\n\n /**\n * @param {Key} key\n * @return {Node|null}\n */\n remove (key) {\n this._root = remove(key, this._root, this._comparator, this);\n }\n\n\n /**\n * Removes and returns the node with smallest key\n * @return {?Node}\n */\n pop () {\n let node = this._root;\n if (node) {\n while (node.left) node = node.left;\n this._root = splay(node.key, this._root, this._comparator);\n this._root = remove(node.key, this._root, this._comparator, this);\n return { key: node.key, data: node.data };\n }\n return null;\n }\n\n\n /**\n * @param {Key} key\n * @return {Node|null}\n */\n findStatic (key) {\n let current = this._root;\n const compare = this._comparator;\n while (current) {\n const cmp = compare(key, current.key);\n if (cmp === 0) return current;\n else if (cmp < 0) current = current.left;\n else current = current.right;\n }\n return null;\n }\n\n\n /**\n * @param {Key} key\n * @return {Node|null}\n */\n find (key) {\n if (this._root) {\n this._root = splay(key, this._root, this._comparator);\n if (this._comparator(key, this._root.key) !== 0) return null;\n }\n return this._root;\n }\n\n\n /**\n * @param {Key} key\n * @return {Boolean}\n */\n contains (key) {\n let current = this._root;\n const compare = this._comparator;\n while (current) {\n const cmp = compare(key, current.key);\n if (cmp === 0) return true;\n else if (cmp < 0) current = current.left;\n else current = current.right;\n }\n return false;\n }\n\n\n /**\n * @param {Visitor} visitor\n * @param {*=} ctx\n * @return {SplayTree}\n */\n forEach (visitor, ctx) {\n let current = this._root;\n const Q = []; /* Initialize stack s */\n let done = false;\n\n while (!done) {\n if (current !== null) {\n Q.push(current);\n current = current.left;\n } else {\n if (Q.length !== 0) {\n current = Q.pop();\n visitor.call(ctx, current);\n\n current = current.right;\n } else done = true;\n }\n }\n return this;\n }\n\n\n /**\n * Walk key range from `low` to `high`. Stops if `fn` returns a value.\n * @param {Key} low\n * @param {Key} high\n * @param {Function} fn\n * @param {*?} ctx\n * @return {SplayTree}\n */\n range (low, high, fn, ctx) {\n const Q = [];\n const compare = this._comparator;\n let node = this._root, cmp;\n\n while (Q.length !== 0 || node) {\n if (node) {\n Q.push(node);\n node = node.left;\n } else {\n node = Q.pop();\n cmp = compare(node.key, high);\n if (cmp > 0) {\n break;\n } else if (compare(node.key, low) >= 0) {\n if (fn.call(ctx, node)) return this; // stop if smth is returned\n }\n node = node.right;\n }\n }\n return this;\n }\n\n\n /**\n * Returns array of keys\n * @return {Array}\n */\n keys () {\n const keys = [];\n this.forEach(({ key }) => keys.push(key));\n return keys;\n }\n\n\n /**\n * Returns array of all the data in the nodes\n * @return {Array}\n */\n values () {\n const values = [];\n this.forEach(({ data }) => values.push(data));\n return values;\n }\n\n\n /**\n * @return {Key|null}\n */\n min() {\n if (this._root) return this.minNode(this._root).key;\n return null;\n }\n\n\n /**\n * @return {Key|null}\n */\n max() {\n if (this._root) return this.maxNode(this._root).key;\n return null;\n }\n\n\n /**\n * @return {Node|null}\n */\n minNode(t = this._root) {\n if (t) while (t.left) t = t.left;\n return t;\n }\n\n\n /**\n * @return {Node|null}\n */\n maxNode(t = this._root) {\n if (t) while (t.right) t = t.right;\n return t;\n }\n\n\n /**\n * Returns node at given index\n * @param {number} index\n * @return {?Node}\n */\n at (index) {\n let current = this._root, done = false, i = 0;\n const Q = [];\n\n while (!done) {\n if (current) {\n Q.push(current);\n current = current.left;\n } else {\n if (Q.length > 0) {\n current = Q.pop();\n if (i === index) return current;\n i++;\n current = current.right;\n } else done = true;\n }\n }\n return null;\n }\n\n\n /**\n * @param {Node} d\n * @return {Node|null}\n */\n next (d) {\n let root = this._root;\n let successor = null;\n\n if (d.right) {\n successor = d.right;\n while (successor.left) successor = successor.left;\n return successor;\n }\n\n const comparator = this._comparator;\n while (root) {\n const cmp = comparator(d.key, root.key);\n if (cmp === 0) break;\n else if (cmp < 0) {\n successor = root;\n root = root.left;\n } else root = root.right;\n }\n\n return successor;\n }\n\n\n /**\n * @param {Node} d\n * @return {Node|null}\n */\n prev (d) {\n let root = this._root;\n let predecessor = null;\n\n if (d.left !== null) {\n predecessor = d.left;\n while (predecessor.right) predecessor = predecessor.right;\n return predecessor;\n }\n\n const comparator = this._comparator;\n while (root) {\n const cmp = comparator(d.key, root.key);\n if (cmp === 0) break;\n else if (cmp < 0) root = root.left;\n else {\n predecessor = root;\n root = root.right;\n }\n }\n return predecessor;\n }\n\n\n /**\n * @return {SplayTree}\n */\n clear() {\n this._root = null;\n this._size = 0;\n return this;\n }\n\n\n /**\n * @return {NodeList}\n */\n toList() {\n return toList(this._root);\n }\n\n\n /**\n * Bulk-load items. Both array have to be same size\n * @param {Array} keys\n * @param {Array} [values]\n * @param {Boolean} [presort=false] Pre-sort keys and values, using\n * tree's comparator. Sorting is done\n * in-place\n * @return {AVLTree}\n */\n load (keys = [], values = [], presort = false) {\n let size = keys.length;\n const comparator = this._comparator;\n\n // sort if needed\n if (presort) sort(keys, values, 0, size - 1, comparator);\n\n if (this._root === null) { // empty tree\n this._root = loadRecursive(this._root, keys, values, 0, size);\n this._size = size;\n } else { // that re-builds the whole tree from two in-order traversals\n const mergedList = mergeLists(this.toList(), createList(keys, values), comparator);\n size = this._size + size;\n this._root = sortedListToBST({ head: mergedList }, 0, size);\n }\n return this;\n }\n\n\n /**\n * @return {Boolean}\n */\n isEmpty() { return this._root === null; }\n\n get size () { return this._size; }\n\n\n /**\n * @param {NodePrinter=} printNode\n * @return {String}\n */\n toString (printNode = (n) => n.key) {\n const out = [];\n printRow(this._root, '', true, (v) => out.push(v), printNode);\n return out.join('');\n }\n\n\n update (key, newKey, newData) {\n const comparator = this._comparator;\n let { left, right } = split(key, this._root, comparator);\n this._size--;\n if (comparator(key, newKey) < 0) {\n right = insert(newKey, newData, right, comparator, this);\n } else {\n left = insert(newKey, newData, left, comparator, this);\n }\n this._root = merge(left, right, comparator);\n }\n\n\n split(key) {\n return split(key, this._root, this._comparator);\n }\n}\n\n\nfunction loadRecursive (parent, keys, values, start, end) {\n const size = end - start;\n if (size > 0) {\n const middle = start + Math.floor(size / 2);\n const key = keys[middle];\n const data = values[middle];\n const node = { key, data, parent };\n node.left = loadRecursive(node, keys, values, start, middle);\n node.right = loadRecursive(node, keys, values, middle + 1, end);\n return node;\n }\n return null;\n}\n\n\nfunction createList(keys, values) {\n const head = { next: null };\n let p = head;\n for (let i = 0; i < keys.length; i++) {\n p = p.next = { key: keys[i], data: values[i] };\n }\n p.next = null;\n return head.next;\n}\n\n\nfunction toList (root) {\n var current = root;\n var Q = [], done = false;\n\n const head = { next: null };\n let p = head;\n\n while (!done) {\n if (current) {\n Q.push(current);\n current = current.left;\n } else {\n if (Q.length > 0) {\n current = p = p.next = Q.pop();\n current = current.right;\n } else done = true;\n }\n }\n p.next = null; // that'll work even if the tree was empty\n return head.next;\n}\n\n\nfunction sortedListToBST(list, start, end) {\n const size = end - start;\n if (size > 0) {\n const middle = start + Math.floor(size / 2);\n const left = sortedListToBST(list, start, middle);\n\n const root = list.head;\n root.left = left;\n\n list.head = list.head.next;\n\n root.right = sortedListToBST(list, middle + 1, end);\n return root;\n }\n return null;\n}\n\n\nfunction mergeLists (l1, l2, compare = (a, b) => a - b) {\n const head = {}; // dummy\n let p = head;\n\n let p1 = l1;\n let p2 = l2;\n\n while (p1 !== null && p2 !== null) {\n if (compare(p1.key, p2.key) < 0) {\n p.next = p1;\n p1 = p1.next;\n } else {\n p.next = p2;\n p2 = p2.next;\n }\n p = p.next;\n }\n\n if (p1 !== null) p.next = p1;\n else if (p2 !== null) p.next = p2;\n\n return head.next;\n}\n\n\nfunction sort(keys, values, left, right, compare) {\n if (left >= right) return;\n\n const pivot = keys[(left + right) >> 1];\n let i = left - 1;\n let j = right + 1;\n\n while (true) {\n do i++; while (compare(keys[i], pivot) < 0);\n do j--; while (compare(keys[j], pivot) > 0);\n if (i >= j) break;\n\n let tmp = keys[i];\n keys[i] = keys[j];\n keys[j] = tmp;\n\n tmp = values[i];\n values[i] = values[j];\n values[j] = tmp;\n }\n\n sort(keys, values, left, j, compare);\n sort(keys, values, j + 1, right, compare);\n}\n","import SplayTree from 'splaytree';\n\nexport default function createEventQueue(byY) {\n const q = new SplayTree(byY);\n\n return {\n isEmpty: isEmpty,\n size: size,\n pop: pop,\n find: find,\n insert: insert\n }\n\n function find(p) {\n return q.find(p);\n }\n\n function size() {\n return q.size;\n }\n\n function isEmpty() {\n return q.isEmpty();\n }\n\n function insert(event) {\n // debugger;\n q.add(event.point, event);\n }\n\n function pop() {\n var node = q.pop();\n return node && node.data;\n }\n}\n","/**\n * Just a collection of geometry related utilities\n */\n\n// This is used for precision checking (e.g. two numbers are equal\n// if their difference is smaller than this number). The value is \n// chosen empirically. We still may run into precision related issues.\n// TODO: we should allow consumers to configure this.\nexport const EPS = 1e-9;//10;\n\nexport function getIntersectionXPoint(segment, xPos, yPos) {\n var dy1 = segment.from.y - yPos;\n var dy2 = yPos - segment.to.y;\n var dy = segment.to.y - segment.from.y;\n if (Math.abs(dy1) < EPS) {\n // The segment starts on the sweep line\n if (Math.abs(dy) < EPS) {\n // the segment is horizontal. Intersection is at the point\n if (xPos <= segment.from.x) return segment.from.x;\n if (xPos > segment.to.x) return segment.to.x;\n return xPos;\n }\n return segment.from.x;\n }\n \n var dx = (segment.to.x - segment.from.x); \n var xOffset; \n if (dy1 >= dy2) {\n xOffset = dy1 * (dx / dy); \n return (segment.from.x - xOffset);\n } \n xOffset = dy2 * (dx / dy);\n return (segment.to.x + xOffset);\n}\n\nexport function angle(dx, dy) {\n // https://stackoverflow.com/questions/16542042/fastest-way-to-sort-vectors-by-angle-without-actually-computing-that-angle\n var p = dx/(Math.abs(dx) + Math.abs(dy)) // -1 .. 1 increasing with x\n\n if (dy < 0) return p - 1; // -2 .. 0 increasing with x\n return 1 - p // 0 .. 2 decreasing with x\n}\n\nexport function intersectSegments(a, b) {\n // https://stackoverflow.com/a/1968345/125351\n var aStart = a.from, bStart = b.from;\n var p0_x = aStart.x, p0_y = aStart.y,\n p2_x = bStart.x, p2_y = bStart.y;\n\n var s1_x = a.dx, s1_y = a.dy, s2_x = b.dx, s2_y = b.dy;\n var div = s1_x * s2_y - s2_x * s1_y;\n\n var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div;\n if (s < 0 || s > 1) return;\n\n var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div;\n\n if (t >= 0 && t <= 1) {\n return {\n x: p0_x - (t * s1_x),\n y: p0_y - (t * s1_y)\n }\n }\n}\n\nexport function same(x0, x1, y0, y1) {\n return Math.abs(x0 - x1) < EPS && Math.abs(y0 - y1) < EPS;\n}\n\nexport function samePoint(a, b) {\n return Math.abs(a.x - b.x) < EPS && Math.abs(a.y - b.y) < EPS;\n}\n","import SplayTree from 'splaytree';\nimport {samePoint, getIntersectionXPoint} from './geom'\n\n/**\n * Creates a new sweep status data structure.\n */\nexport default function createSweepStatus(onError, EPS) {\n var lastPointY, prevY;\n var lastPointX, prevX;\n var useBelow = false;\n var status = new SplayTree(compareSegments);\n\n // To save on GC we return mutable object.\n var currentBoundary = {\n beforeLeft: null,\n left: null,\n right: null,\n afterRight: null,\n }\n\n var currentLeftRight = {left: null, right: null};\n\n return {\n /**\n * Add new segments into the status tree.\n */\n insertSegments,\n\n /**\n * Remove segments from the status tree.\n */\n deleteSegments,\n\n /**\n * Returns segments that are to the left and right from a given point.\n */\n getLeftRightPoint,\n\n /**\n * For a given collections of segments finds the most left and the most right\n * segments. Also returns segments immediately before left, and after right segments.\n */\n getBoundarySegments,\n\n findSegmentsWithPoint,\n\n /**\n * Current binary search tree with segments\n */\n status,\n\n /**\n * Introspection method that verifies if there are duplicates in the segment tree.\n * If there are - `onError()` is called.\n */\n checkDuplicate,\n\n /**\n * Prints current segments in order of their intersection with sweep line. Introspection method.\n */\n printStatus,\n\n /**\n * Returns current position of the sweep line.\n */\n getLastPoint() {\n return {x: lastPointX, y: lastPointY};\n }\n }\n\n function compareSegments(a, b) {\n if (a === b) return 0;\n\n var ak = getIntersectionXPoint(a, lastPointX, lastPointY);\n var bk = getIntersectionXPoint(b, lastPointX, lastPointY);\n\n var res = ak - bk;\n if (Math.abs(res) >= EPS) {\n // We are okay fine. Intersection distance between two segments\n // is good to give conclusive answer\n return res;\n }\n\n var aIsHorizontal = Math.abs(a.dy) < EPS;\n var bIsHorizontal = Math.abs(b.dy) < EPS;\n if (aIsHorizontal && bIsHorizontal) {\n return b.to.x - a.to.x;\n }\n // TODO: What if both a and b is horizontal?\n // move horizontal to end\n if (aIsHorizontal) { \n return useBelow ? -1 : 1;\n }\n\n if (bIsHorizontal) {\n if (useBelow) {\n return (b.from.x >= lastPointX) ? -1 : 1\n }\n return -1;\n // return useBelow ? 1 : -1;\n }\n var pa = a.angle;\n var pb = b.angle;\n if (Math.abs(pa - pb) >= EPS) {\n return useBelow ? pa - pb : pb - pa;\n }\n\n var segDist = a.from.y - b.from.y;\n if (Math.abs(segDist) >= EPS) {\n return -segDist;\n }\n segDist = a.to.y - b.to.y;\n if (Math.abs(segDist) >= EPS) {\n // TODO: Is this accurate?\n return -segDist;\n }\n\n return 0;\n // Could also use:\n // var aAngle = Math.atan2(a.from.y - a.to.y, a.from.x - a.to.x);\n // var bAngle = Math.atan2(b.from.y - b.to.y, b.from.x - b.to.x);\n // return useBelow ? bAngle - aAngle : aAngle - bAngle;\n }\n\n function getBoundarySegments(upper, interior) {\n var leftMost, rightMost, i;\n var uLength = upper.length;\n\n if (uLength > 0) {\n leftMost = rightMost = upper[0];\n } else {\n leftMost = rightMost = interior[0];\n }\n\n for (i = 1; i < uLength; ++i) {\n var s = upper[i];\n var cmp = compareSegments(leftMost, s);\n if (cmp > 0) leftMost = s;\n\n cmp = compareSegments(rightMost, s);\n if (cmp < 0) rightMost = s;\n }\n\n var startFrom = uLength > 0 ? 0 : 1;\n for (i = startFrom; i < interior.length; ++i) {\n s = interior[i];\n cmp = compareSegments(leftMost, s);\n if (cmp > 0) leftMost = s;\n\n cmp = compareSegments(rightMost, s);\n if (cmp < 0) rightMost = s;\n }\n\n // at this point we have our left/right segments in the status.\n // Let's find their prev/next elements and report them back:\n var left = status.find(leftMost);\n if (!left) {\n onError('Left is missing. Precision error?');\n }\n\n var right = status.find(rightMost);\n if (!right) {\n onError('Right is missing. Precision error?');\n }\n\n var beforeLeft = left && status.prev(left);\n var afterRight = right && status.next(right);\n\n while (afterRight && right.key.dy === 0 && afterRight.key.dy === 0) {\n // horizontal segments are special :(\n afterRight = status.next(afterRight);\n }\n\n currentBoundary.beforeLeft = beforeLeft && beforeLeft.key;\n currentBoundary.left = left && left.key;\n currentBoundary.right = right && right.key;\n currentBoundary.afterRight = afterRight && afterRight.key;\n\n return currentBoundary;\n }\n\n function getLeftRightPoint(p) {\n // We are trying to find left and right segments that are nearest to the\n // point p. For this we traverse the binary search tree, and remember\n // node with the shortest distance to p.\n var lastLeft;\n var current = status._root;\n var minX = Number.POSITIVE_INFINITY;\n\n var useNext = false;\n while (current) {\n var x = getIntersectionXPoint(current.key, p.x, p.y);\n var dx = p.x - x;\n if (dx >= 0) {\n if (dx < minX) {\n minX = dx;\n lastLeft = current;\n current = current.left;\n useNext = false;\n } else {\n break;\n }\n } else {\n if (-dx < minX) {\n useNext = true;\n minX = -dx;\n lastLeft = current;\n current = current.right;\n } else {\n break;\n }\n }\n }\n if (useNext) {\n // I'm not sure why I did this. I don't this this is right now.\n // lastLeft = status.next(lastLeft);\n }\n\n currentLeftRight.left = lastLeft && lastLeft.key\n var next = lastLeft && status.next(lastLeft);\n currentLeftRight.right = next && next.key\n return currentLeftRight;\n\n // Conceptually, the code above should be equivalent to the code below;\n // The code below is easier to understand, but intuitively, the code above\n // should have better performance (as we do not traverse the entire status\n // tree)\n\n // var right, left, x;\n // var all = status.keys()\n // for (var i = 0; i < all.length; ++i) {\n // var segment = all[i];\n // x = getIntersectionXPoint(segment, p.x, p.y);\n // if (x > p.x && !right) {\n // right = segment;\n // break;\n // } else if (x < p.x) {\n // left = segment;\n // }\n // }\n\n // currentLeftRight.left = left;\n // currentLeftRight.right = right;\n\n // return currentLeftRight;\n }\n\n function findSegmentsWithPoint(p, onFound) {\n // Option 1.\n // var arrResults = [];\n // status.forEach(current => {\n // var x = getIntersectionXPoint(current.key, p.x, p.y);\n // var dx = p.x - x;\n // if (Math.abs(dx) < EPS) {\n // onFound(current.key);\n // // arrResults.push(current.key)\n // }\n // });\n // return arrResults;\n\n // Option 2.\n\n // let current = status._root;\n // const Q = []; /* Initialize stack s */\n // let done = false;\n // var res = [];\n // var breakEarly = false;\n\n // while (!done) {\n // if (current !== null) {\n // Q.push(current);\n // current = current.left;\n // } else {\n // if (Q.length !== 0) {\n // current = Q.pop();\n\n // var x = getIntersectionXPoint(current.key, p.x, p.y);\n // var dx = p.x - x;\n // if (Math.abs(dx) < EPS) {\n // res.push(current.key)\n // breakEarly = true;\n // } else if (breakEarly) {\n // done = true;\n // }\n\n // current = current.right;\n // } else done = true;\n // }\n // }\n\n // return res;\n\n // option 3.\n var current = status._root;\n\n while (current) {\n var x = getIntersectionXPoint(current.key, p.x, p.y);\n var dx = p.x - x;\n if (Math.abs(dx) < EPS) {\n collectAdjacentNodes(current, p, onFound);\n break;\n } else if (dx < 0) {\n current = current.left;\n } else {\n current = current.right;\n }\n }\n }\n\n function collectAdjacentNodes(root, p, onFound) {\n onFound(root.key);\n goOverPredecessors(root.left, p, onFound);\n goOverSuccessors(root.right, p, onFound);\n }\n\n function goOverPredecessors(root, p, res) {\n if (!root) return;\n var x = getIntersectionXPoint(root.key, p.x, p.y);\n var dx = p.x - x;\n if (Math.abs(dx) < EPS) {\n collectAdjacentNodes(root, p, res);\n } else {\n goOverPredecessors(root.right, p, res);\n }\n }\n\n function goOverSuccessors(root, p, res) {\n if (!root) return;\n var x = getIntersectionXPoint(root.key, p.x, p.y);\n var dx = p.x - x;\n if (Math.abs(dx) < EPS) {\n collectAdjacentNodes(root, p, res);\n } else {\n goOverSuccessors(root.left, p, res);\n }\n }\n\n function checkDuplicate() {\n var prev;\n status.forEach(node => {\n var current = node.key;\n\n if (prev) {\n if (samePoint(prev.from, current.from) && samePoint(prev.to, current.to)) {\n // Likely you have received error before during segment removal.\n onError('Duplicate key in the status! This may be caused by Floating Point rounding error')\n }\n }\n prev = current;\n });\n }\n\n function printStatus(prefix = '') {\n // eslint-disable-next-line\n console.log(prefix, 'status line: ', lastPointX, lastPointY);\n status.forEach(node => {\n var x = getIntersectionXPoint(node.key, lastPointX, lastPointY);\n // eslint-disable-next-line\n console.log(x + ' ' + node.key.name);\n })\n }\n\n function insertSegments(interior, upper, sweepLinePos) {\n lastPointY = sweepLinePos.y;\n lastPointX = sweepLinePos.x;\n var key;\n\n for (var i = 0; i < interior.length; ++i) {\n key = interior[i];\n status.add(key);\n }\n for (i = 0; i < upper.length; ++i) {\n key = upper[i]\n status.add(key);\n }\n }\n\n function deleteSegments(lower, interior, sweepLinePos) {\n // I spent most of the time debugging this method. Depending on the\n // algorithm state we can run into situation when dynamic keys of the\n // `status` tree predict wrong branch, and thus we are not able to find\n // the segment that needs to be deleted. If that happens I'm trying to\n // use previous point and repeat the process. This may result in \n // incorrect state. In that case I report an error. \n var i;\n var prevCount = status._size;\n prevX = lastPointX;\n prevY = lastPointY;\n lastPointY = sweepLinePos.y;\n lastPointX = sweepLinePos.x;\n\n useBelow = true;\n for(i = 0; i < lower.length; ++i) {\n removeSegment(lower[i], sweepLinePos)\n }\n for(i = 0; i < interior.length; ++i) {\n removeSegment(interior[i], sweepLinePos)\n }\n useBelow = false;\n\n if (status._size !== prevCount - interior.length - lower.length) {\n // This can happen when rounding error occurs. You can try scaling your input\n onError('Segments were not removed from a tree properly. Precision error?');\n }\n }\n\n function removeSegment(key, sweepLinePos) {\n if (status.find(key)) {\n status.remove(key);\n } else {\n lastPointX = prevX;\n lastPointY = prevY;\n if (status.find(key)) {\n status.remove(key);\n } else {\n // They will get an error :(\n }\n lastPointY = sweepLinePos.y;\n lastPointX = sweepLinePos.x;\n }\n }\n}","/**\n * Represents a single event in the sweep-line algorithm\n */\nexport default class SweepEvent {\n /**\n * Creates new sweep event of a given kind.\n */\n constructor(point, segment) {\n this.point = point;\n if (segment) this.from = [segment];\n }\n}\n","import createEventQueue from './createEventQueue';\nimport createSweepStatus from './sweepStatus';\nimport SweepEvent from './SweepEvent';\n\nimport {intersectSegments, EPS, angle, samePoint} from './geom';\n\n/**\n * A point on a line\n * \n * @typedef {Object} Point\n * @property {number} x coordinate\n * @property {number} y coordinate\n */\n\n\n/**\n * @typedef {Object} Segment \n * @property {Point} from start of the segment\n * @property {Point} to end of the segment\n */\n\n/**\n * @typedef {function(point : Point, interior : Segment[], lower : Segment[], upper : Segment[])} ReportIntersectionCallback\n */\n\n/**\n * @typedef {Object} ISectOptions \n * @property {ReportIntersectionCallback} onFound \n */\n\n /**\n * @typedef {Object} ISectResult\n */\n\n// We use EMPTY array to avoid pressure on garbage collector. Need to be\n// very cautious to not mutate this array.\nvar EMPTY = [];\n\n/**\n * Finds all intersections among given segments.\n * \n * The algorithm follows \"Computation Geometry, Algorithms and Applications\" book\n * by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars.\n * \n * Line is swept top-down\n * \n * @param {Segment[]} segments\n * @param {ISectOptions=} options\n * @returns {ISectResult}\n */\nexport default function isect(segments, options) {\n var results = [];\n var reportIntersection = (options && options.onFound) || defaultIntersectionReporter;\n\n var onError = (options && options.onError) || defaultErrorReporter;\n\n var eventQueue = createEventQueue(byY);\n var sweepStatus = createSweepStatus(onError, EPS);\n var lower, interior, lastPoint;\n\n segments.forEach(addSegment);\n\n return {\n /**\n * Find all intersections synchronously.\n * \n * @returns array of found intersections.\n */\n run,\n\n /**\n * Performs a single step in the sweep line algorithm\n * \n * @returns true if there was something to process; False if no more work to do\n */\n step,\n\n // Methods below are low level API for fine-grained control.\n // Don't use it unless you understand this code thoroughly\n\n /**\n * Add segment into the \n */\n addSegment,\n\n /**\n * Direct access to event queue. Queue contains segment endpoints and\n * pending detected intersections.\n */\n eventQueue, \n\n /**\n * Direct access to sweep line status. \"Status\" holds information about\n * all intersected segments.\n */\n sweepStatus,\n\n /**\n * Access to results array. Works only when you use default onFound() handler\n */\n results\n }\n\n function run() {\n while (!eventQueue.isEmpty()) {\n var eventPoint = eventQueue.pop();\n if (handleEventPoint(eventPoint)) {\n // they decided to stop.\n return;\n };\n }\n\n return results;\n }\n\n function step() {\n if (!eventQueue.isEmpty()) {\n var eventPoint = eventQueue.pop();\n handleEventPoint(eventPoint);\n // Note: we don't check results of `handleEventPoint()`\n // assumption is that client controls `step()` and thus they \n // know better if they want to stop.\n return true;\n }\n return false;\n }\n\n function handleEventPoint(p) {\n lastPoint = p.point;\n var upper = p.from || EMPTY;\n\n lower = interior = undefined;\n // TODO: move lower/interior into sweep status method?\n\n sweepStatus.findSegmentsWithPoint(lastPoint, addLowerOrInterior);\n // if (segmentsWithPoint) {\n // segmentsWithPoint.forEach()\n // } \n\n if (!lower) lower = EMPTY;\n if (!interior) interior = EMPTY;\n\n var uLength = upper.length;\n var iLength = interior.length;\n var lLength = lower.length;\n var hasIntersection = uLength + iLength + lLength > 1;\n var hasPointIntersection = !hasIntersection && (uLength === 0 && lLength === 0 && iLength > 0);\n\n if (hasIntersection || hasPointIntersection) {\n p.isReported = true;\n if (reportIntersection(lastPoint, interior, lower, upper)) {\n return true;\n }\n }\n\n sweepStatus.deleteSegments(lower, interior, lastPoint);\n sweepStatus.insertSegments(interior, upper, lastPoint);\n\n var sLeft, sRight;\n\n var hasNoCrossing = (uLength + iLength === 0);\n\n if (hasNoCrossing) {\n var leftRight = sweepStatus.getLeftRightPoint(lastPoint);\n sLeft = leftRight.left;\n if (!sLeft) return;\n\n sRight = leftRight.right;\n if (!sRight) return;\n\n findNewEvent(sLeft, sRight, p);\n } else {\n var boundarySegments = sweepStatus.getBoundarySegments(upper, interior);\n\n findNewEvent(boundarySegments.beforeLeft, boundarySegments.left, p);\n findNewEvent(boundarySegments.right, boundarySegments.afterRight, p);\n }\n\n return false;\n }\n\n function addLowerOrInterior(s) {\n if (samePoint(s.to, lastPoint)) {\n if (!lower) lower = [s];\n else lower.push(s);\n } else if (!samePoint(s.from, lastPoint)) {\n if (!interior) interior = [s];\n else interior.push(s);\n }\n }\n\n function findNewEvent(left, right, p) {\n if (!left || !right) return;\n\n var intersection = intersectSegments(left, right);\n if (!intersection) {\n return;\n }\n\n var dy = p.point.y - intersection.y\n // TODO: should I add dy to intersection.y?\n if (dy < -EPS) {\n // this means intersection happened after the sweep line. \n // We already processed it.\n return;\n }\n if (Math.abs(dy) < EPS && intersection.x <= p.point.x) {\n return;\n }\n\n // Need to adjust floating point for this special case,\n // since otherwise it gives rounding errors:\n roundNearZero(intersection);\n\n var current = eventQueue.find(intersection);\n\n if (current && current.isReported) {\n // We already reported this event. No need to add it one more time\n // TODO: Is this case even possible?\n onError('We already reported this event.');\n return;\n }\n\n if (!current) {\n var event = new SweepEvent(intersection)\n eventQueue.insert(event);\n }\n }\n\n function defaultIntersectionReporter(p, interior, lower, upper) {\n results.push({\n point: p, \n segments: union(union(interior, lower), upper)\n });\n }\n\n function addSegment(segment) {\n var from = segment.from;\n var to = segment.to;\n\n // Small numbers give more precision errors. Rounding them to 0.\n roundNearZero(from);\n roundNearZero(to);\n\n var dy = from.y - to.y;\n\n // Note: dy is much smaller then EPS on purpose. I found that higher\n // precision here does less good - getting way more rounding errors.\n if (Math.abs(dy) < 1e-5) {\n from.y = to.y;\n segment.dy = 0;\n }\n if ((from.y < to.y) || (\n (from.y === to.y) && (from.x > to.x))\n ) {\n var temp = from;\n from = segment.from = to; \n to = segment.to = temp;\n }\n\n // We pre-compute some immutable properties of the segment\n // They are used quite often in the tree traversal, and pre-computation\n // gives significant boost:\n segment.dy = from.y - to.y;\n segment.dx = from.x - to.x;\n segment.angle = angle(segment.dy, segment.dx);\n\n var isPoint = segment.dy === segment.dx && segment.dy === 0;\n var prev = eventQueue.find(from)\n if (prev && !isPoint) {\n // this detects identical segments early. Without this check\n // the algorithm would break since sweep line has no means to\n // detect identical segments.\n var prevFrom = prev.data.from;\n if (prevFrom) {\n for (var i = 0; i < prevFrom.length; ++i) {\n var s = prevFrom[i];\n if (samePoint(s.to, to)) {\n reportIntersection(s.from, [], s.from, s.to);\n reportIntersection(s.to, [], s.from, s.to);\n return;\n }\n }\n }\n }\n\n if (!isPoint) {\n if (prev) {\n if (prev.data.from) prev.data.from.push(segment);\n else prev.data.from = [segment];\n } else {\n var e = new SweepEvent(from, segment)\n eventQueue.insert(e);\n }\n var event = new SweepEvent(to)\n eventQueue.insert(event)\n } else {\n var event = new SweepEvent(to)\n eventQueue.insert(event)\n }\n } \n}\n\nfunction roundNearZero(point) {\n if (Math.abs(point.x) < EPS) point.x = 0;\n if (Math.abs(point.y) < EPS) point.y = 0;\n}\n\nfunction defaultErrorReporter(errorMessage) {\n throw new Error(errorMessage);\n}\n\nfunction union(a, b) {\n if (!a) return b;\n if (!b) return a;\n\n return a.concat(b);\n}\n\nfunction byY(a, b) {\n // decreasing Y \n var res = b.y - a.y;\n // TODO: This might mess up the status tree.\n if (Math.abs(res) < EPS) {\n // increasing x.\n res = a.x - b.x;\n if (Math.abs(res) < EPS) res = 0;\n }\n\n return res;\n}","/**\n * This is a brute force solution with O(n^2) performance.\n * (`n` is number of segments).\n * \n * Use this when number of lines is low, and number of intersections\n * is high.\n */\n\nexport default function brute(lines, options) {\n var results = [];\n var reportIntersection = (options && options.onFound) || \n defaultIntersectionReporter;\n var asyncState;\n\n return {\n /**\n * Execute brute force of the segment intersection search\n */\n run,\n /**\n * Access to results array. Works only when you use default onFound() handler\n */\n results,\n\n /**\n * Performs a single step in the brute force algorithm ()\n */\n step\n }\n\n function step() {\n if (!asyncState) {\n asyncState = {\n i: 0\n }\n }\n var test = lines[asyncState.i];\n for (var j = asyncState.i + 1; j < lines.length; ++j) {\n var other = lines[j];\n var pt = intersectSegments(test, other);\n if (pt) {\n if (reportIntersection(pt, [test, other])) {\n return;\n }\n }\n }\n asyncState.i += 1;\n return asyncState.i < lines.length;\n }\n\n function run() {\n for(var i = 0; i < lines.length; ++i) {\n var test = lines[i];\n for (var j = i + 1; j < lines.length; ++j) {\n var other = lines[j];\n var pt = intersectSegments(test, other);\n if (pt) {\n if (reportIntersection(pt, [test, other])) {\n return;\n }\n }\n }\n }\n return results;\n }\n\n function defaultIntersectionReporter(p, interior) {\n results.push({\n point: p, \n segments: interior\n });\n }\n}\n\nfunction intersectSegments(a, b) {\n // https://stackoverflow.com/a/1968345/125351\n var aStart = a.from, bStart = b.from;\n var p0_x = aStart.x, p0_y = aStart.y,\n p2_x = bStart.x, p2_y = bStart.y;\n\n var s1_x = a.from.x - a.to.x, s1_y = a.from.y - a.to.y, s2_x = b.from.x - b.to.x, s2_y = b.from.y - b.to.y;\n var div = s1_x * s2_y - s2_x * s1_y;\n\n var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div;\n if (s < 0 || s > 1) return;\n\n var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div;\n\n if (t >= 0 && t <= 1) {\n return {\n x: p0_x - (t * s1_x),\n y: p0_y - (t * s1_y)\n }\n }\n}\n"],"names":["let","const","this","SplayTree","EPS","intersectSegments"],"mappings":"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"} \ No newline at end of file diff --git a/build/isect.min.js b/build/isect.min.js new file mode 100644 index 0000000..6c3c17b --- /dev/null +++ b/build/isect.min.js @@ -0,0 +1,2 @@ +!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(t.isect={})}(this,function(t){"use strict";var a=function(t,r){this.key=t,this.data=r,this.left=null,this.right=null};function r(t,r){return r>1];var u=n-1;var a=o+1;for(;;){for(;i(r[++u],f)<0;);for(;0t.to.x?t.to.x:r:t.from.x;var f,u=t.to.x-t.from.x;return o<=n?(f=n*(u/i),t.from.x-f):(f=o*(u/i),t.to.x+f)}function _(t,r){return Math.abs(t.x-r.x)=a)return e;var n=Math.abs(t.dy)=h?-1:1:-1;var i=t.angle,f=r.angle;if(Math.abs(i-f)>=a)return s?i-f:f-i;var u=t.from.y-r.from.y;return Math.abs(u)>=a?-u:(u=t.to.y-r.to.y,Math.abs(u)>=a?-u:0)}function g(t,r,e){e(t.key),function t(r,e,n){if(!r)return;var o=x(r.key,e.x,e.y);var i=e.x-o;Math.abs(i)e.x){var u=r;r=t.from=e,e=t.to=u}t.dy=r.y-e.y,t.dx=r.x-e.x,t.angle=(n=t.dy,o=t.dx,i=n/(Math.abs(n)+Math.abs(o)),o<0?i-1:1-i);var a=t.dy===t.dx&&0===t.dy,l=g.find(r);if(l&&!a){var h=l.data.from;if(h)for(var s=0;s March 1992\n */\n\n/**\n * @typedef {*} Key\n */\n\n\n/**\n * @typedef {*} Value\n */\n\n\n/**\n * @typedef {function(node:Node):void} Visitor\n */\n\n\n/**\n * @typedef {function(a:Key, b:Key):number} Comparator\n */\n\n\n/**\n * @param {function(node:Node):string} NodePrinter\n */\n\n\n/**\n * @typedef {Object} Node\n * @property {Key} Key\n * @property {Value=} data\n * @property {Node} left\n * @property {Node} right\n */\n\nclass Node {\n\n constructor (key, data) {\n this.key = key;\n this.data = data;\n this.left = null;\n this.right = null;\n }\n}\n\nfunction DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }\n\n\n/**\n * Simple top down splay, not requiring i to be in the tree t.\n * @param {Key} i\n * @param {Node?} t\n * @param {Comparator} comparator\n */\nfunction splay (i, t, comparator) {\n if (t === null) return t;\n let l, r, y;\n const N = new Node();\n l = r = N;\n\n while (true) {\n const cmp = comparator(i, t.key);\n //if (i < t.key) {\n if (cmp < 0) {\n if (t.left === null) break;\n //if (i < t.left.key) {\n if (comparator(i, t.left.key) < 0) {\n y = t.left; /* rotate right */\n t.left = y.right;\n y.right = t;\n t = y;\n if (t.left === null) break;\n }\n r.left = t; /* link right */\n r = t;\n t = t.left;\n //} else if (i > t.key) {\n } else if (cmp > 0) {\n if (t.right === null) break;\n //if (i > t.right.key) {\n if (comparator(i, t.right.key) > 0) {\n y = t.right; /* rotate left */\n t.right = y.left;\n y.left = t;\n t = y;\n if (t.right === null) break;\n }\n l.right = t; /* link left */\n l = t;\n t = t.right;\n } else {\n break;\n }\n }\n /* assemble */\n l.right = t.left;\n r.left = t.right;\n t.left = N.right;\n t.right = N.left;\n return t;\n}\n\n\n/**\n * @param {Key} i\n * @param {Value} data\n * @param {Comparator} comparator\n * @param {Tree} tree\n * @return {Node} root\n */\nfunction insert (i, data, t, comparator, tree) {\n const node = new Node(i, data);\n\n tree._size++;\n\n if (t === null) {\n node.left = node.right = null;\n return node;\n }\n\n t = splay(i, t, comparator);\n const cmp = comparator(i, t.key);\n if (cmp < 0) {\n node.left = t.left;\n node.right = t;\n t.left = null;\n } else if (cmp >= 0) {\n node.right = t.right;\n node.left = t;\n t.right = null;\n }\n return node;\n}\n\n\n/**\n * Insert i into the tree t, unless it's already there.\n * @param {Key} i\n * @param {Value} data\n * @param {Comparator} comparator\n * @param {Tree} tree\n * @return {Node} root\n */\nfunction add (i, data, t, comparator, tree) {\n const node = new Node(i, data);\n\n if (t === null) {\n node.left = node.right = null;\n tree._size++;\n return node;\n }\n\n t = splay(i, t, comparator);\n const cmp = comparator(i, t.key);\n if (cmp === 0) return t;\n else {\n if (cmp < 0) {\n node.left = t.left;\n node.right = t;\n t.left = null;\n } else if (cmp > 0) {\n node.right = t.right;\n node.left = t;\n t.right = null;\n }\n tree._size++;\n return node;\n }\n}\n\n\n/**\n * Deletes i from the tree if it's there\n * @param {Key} i\n * @param {Tree} tree\n * @param {Comparator} comparator\n * @param {Tree} tree\n * @return {Node} new root\n */\nfunction remove (i, t, comparator, tree) {\n let x;\n if (t === null) return null;\n t = splay(i, t, comparator);\n var cmp = comparator(i, t.key);\n if (cmp === 0) { /* found it */\n if (t.left === null) {\n x = t.right;\n } else {\n x = splay(i, t.left, comparator);\n x.right = t.right;\n }\n tree._size--;\n return x;\n }\n return t; /* It wasn't there */\n}\n\n\nfunction split (key, v, comparator) {\n let left, right;\n if (v === null) {\n left = right = null;\n } else {\n v = splay(key, v, comparator);\n\n const cmp = comparator(v.key, key);\n if (cmp === 0) {\n left = v.left;\n right = v.right;\n } else if (cmp < 0) {\n right = v.right;\n v.right = null;\n left = v;\n } else {\n left = v.left;\n v.left = null;\n right = v;\n }\n }\n return { left, right };\n}\n\n\nfunction merge (left, right, comparator) {\n if (right === null) return left;\n if (left === null) return right;\n\n right = splay(left.key, right, comparator);\n right.left = left;\n return right;\n}\n\n\n/**\n * Prints level of the tree\n * @param {Node} root\n * @param {String} prefix\n * @param {Boolean} isTail\n * @param {Array} out\n * @param {Function(node:Node):String} printNode\n */\nfunction printRow (root, prefix, isTail, out, printNode) {\n if (root) {\n out(`${ prefix }${ isTail ? '└── ' : '├── ' }${ printNode(root) }\\n`);\n const indent = prefix + (isTail ? ' ' : '│ ');\n if (root.left) printRow(root.left, indent, false, out, printNode);\n if (root.right) printRow(root.right, indent, true, out, printNode);\n }\n}\n\n\nexport default class Tree {\n\n constructor (comparator = DEFAULT_COMPARE) {\n this._comparator = comparator;\n this._root = null;\n this._size = 0;\n }\n\n\n /**\n * Inserts a key, allows duplicates\n * @param {Key} key\n * @param {Value=} data\n * @return {Node|null}\n */\n insert (key, data) {\n return this._root = insert(key, data, this._root, this._comparator, this);\n }\n\n\n /**\n * Adds a key, if it is not present in the tree\n * @param {Key} key\n * @param {Value=} data\n * @return {Node|null}\n */\n add (key, data) {\n return this._root = add(key, data, this._root, this._comparator, this);\n }\n\n\n /**\n * @param {Key} key\n * @return {Node|null}\n */\n remove (key) {\n this._root = remove(key, this._root, this._comparator, this);\n }\n\n\n /**\n * Removes and returns the node with smallest key\n * @return {?Node}\n */\n pop () {\n let node = this._root;\n if (node) {\n while (node.left) node = node.left;\n this._root = splay(node.key, this._root, this._comparator);\n this._root = remove(node.key, this._root, this._comparator, this);\n return { key: node.key, data: node.data };\n }\n return null;\n }\n\n\n /**\n * @param {Key} key\n * @return {Node|null}\n */\n findStatic (key) {\n let current = this._root;\n const compare = this._comparator;\n while (current) {\n const cmp = compare(key, current.key);\n if (cmp === 0) return current;\n else if (cmp < 0) current = current.left;\n else current = current.right;\n }\n return null;\n }\n\n\n /**\n * @param {Key} key\n * @return {Node|null}\n */\n find (key) {\n if (this._root) {\n this._root = splay(key, this._root, this._comparator);\n if (this._comparator(key, this._root.key) !== 0) return null;\n }\n return this._root;\n }\n\n\n /**\n * @param {Key} key\n * @return {Boolean}\n */\n contains (key) {\n let current = this._root;\n const compare = this._comparator;\n while (current) {\n const cmp = compare(key, current.key);\n if (cmp === 0) return true;\n else if (cmp < 0) current = current.left;\n else current = current.right;\n }\n return false;\n }\n\n\n /**\n * @param {Visitor} visitor\n * @param {*=} ctx\n * @return {SplayTree}\n */\n forEach (visitor, ctx) {\n let current = this._root;\n const Q = []; /* Initialize stack s */\n let done = false;\n\n while (!done) {\n if (current !== null) {\n Q.push(current);\n current = current.left;\n } else {\n if (Q.length !== 0) {\n current = Q.pop();\n visitor.call(ctx, current);\n\n current = current.right;\n } else done = true;\n }\n }\n return this;\n }\n\n\n /**\n * Walk key range from `low` to `high`. Stops if `fn` returns a value.\n * @param {Key} low\n * @param {Key} high\n * @param {Function} fn\n * @param {*?} ctx\n * @return {SplayTree}\n */\n range (low, high, fn, ctx) {\n const Q = [];\n const compare = this._comparator;\n let node = this._root, cmp;\n\n while (Q.length !== 0 || node) {\n if (node) {\n Q.push(node);\n node = node.left;\n } else {\n node = Q.pop();\n cmp = compare(node.key, high);\n if (cmp > 0) {\n break;\n } else if (compare(node.key, low) >= 0) {\n if (fn.call(ctx, node)) return this; // stop if smth is returned\n }\n node = node.right;\n }\n }\n return this;\n }\n\n\n /**\n * Returns array of keys\n * @return {Array}\n */\n keys () {\n const keys = [];\n this.forEach(({ key }) => keys.push(key));\n return keys;\n }\n\n\n /**\n * Returns array of all the data in the nodes\n * @return {Array}\n */\n values () {\n const values = [];\n this.forEach(({ data }) => values.push(data));\n return values;\n }\n\n\n /**\n * @return {Key|null}\n */\n min() {\n if (this._root) return this.minNode(this._root).key;\n return null;\n }\n\n\n /**\n * @return {Key|null}\n */\n max() {\n if (this._root) return this.maxNode(this._root).key;\n return null;\n }\n\n\n /**\n * @return {Node|null}\n */\n minNode(t = this._root) {\n if (t) while (t.left) t = t.left;\n return t;\n }\n\n\n /**\n * @return {Node|null}\n */\n maxNode(t = this._root) {\n if (t) while (t.right) t = t.right;\n return t;\n }\n\n\n /**\n * Returns node at given index\n * @param {number} index\n * @return {?Node}\n */\n at (index) {\n let current = this._root, done = false, i = 0;\n const Q = [];\n\n while (!done) {\n if (current) {\n Q.push(current);\n current = current.left;\n } else {\n if (Q.length > 0) {\n current = Q.pop();\n if (i === index) return current;\n i++;\n current = current.right;\n } else done = true;\n }\n }\n return null;\n }\n\n\n /**\n * @param {Node} d\n * @return {Node|null}\n */\n next (d) {\n let root = this._root;\n let successor = null;\n\n if (d.right) {\n successor = d.right;\n while (successor.left) successor = successor.left;\n return successor;\n }\n\n const comparator = this._comparator;\n while (root) {\n const cmp = comparator(d.key, root.key);\n if (cmp === 0) break;\n else if (cmp < 0) {\n successor = root;\n root = root.left;\n } else root = root.right;\n }\n\n return successor;\n }\n\n\n /**\n * @param {Node} d\n * @return {Node|null}\n */\n prev (d) {\n let root = this._root;\n let predecessor = null;\n\n if (d.left !== null) {\n predecessor = d.left;\n while (predecessor.right) predecessor = predecessor.right;\n return predecessor;\n }\n\n const comparator = this._comparator;\n while (root) {\n const cmp = comparator(d.key, root.key);\n if (cmp === 0) break;\n else if (cmp < 0) root = root.left;\n else {\n predecessor = root;\n root = root.right;\n }\n }\n return predecessor;\n }\n\n\n /**\n * @return {SplayTree}\n */\n clear() {\n this._root = null;\n this._size = 0;\n return this;\n }\n\n\n /**\n * @return {NodeList}\n */\n toList() {\n return toList(this._root);\n }\n\n\n /**\n * Bulk-load items. Both array have to be same size\n * @param {Array} keys\n * @param {Array} [values]\n * @param {Boolean} [presort=false] Pre-sort keys and values, using\n * tree's comparator. Sorting is done\n * in-place\n * @return {AVLTree}\n */\n load (keys = [], values = [], presort = false) {\n let size = keys.length;\n const comparator = this._comparator;\n\n // sort if needed\n if (presort) sort(keys, values, 0, size - 1, comparator);\n\n if (this._root === null) { // empty tree\n this._root = loadRecursive(this._root, keys, values, 0, size);\n this._size = size;\n } else { // that re-builds the whole tree from two in-order traversals\n const mergedList = mergeLists(this.toList(), createList(keys, values), comparator);\n size = this._size + size;\n this._root = sortedListToBST({ head: mergedList }, 0, size);\n }\n return this;\n }\n\n\n /**\n * @return {Boolean}\n */\n isEmpty() { return this._root === null; }\n\n get size () { return this._size; }\n\n\n /**\n * @param {NodePrinter=} printNode\n * @return {String}\n */\n toString (printNode = (n) => n.key) {\n const out = [];\n printRow(this._root, '', true, (v) => out.push(v), printNode);\n return out.join('');\n }\n\n\n update (key, newKey, newData) {\n const comparator = this._comparator;\n let { left, right } = split(key, this._root, comparator);\n this._size--;\n if (comparator(key, newKey) < 0) {\n right = insert(newKey, newData, right, comparator, this);\n } else {\n left = insert(newKey, newData, left, comparator, this);\n }\n this._root = merge(left, right, comparator);\n }\n\n\n split(key) {\n return split(key, this._root, this._comparator);\n }\n}\n\n\nfunction loadRecursive (parent, keys, values, start, end) {\n const size = end - start;\n if (size > 0) {\n const middle = start + Math.floor(size / 2);\n const key = keys[middle];\n const data = values[middle];\n const node = { key, data, parent };\n node.left = loadRecursive(node, keys, values, start, middle);\n node.right = loadRecursive(node, keys, values, middle + 1, end);\n return node;\n }\n return null;\n}\n\n\nfunction createList(keys, values) {\n const head = { next: null };\n let p = head;\n for (let i = 0; i < keys.length; i++) {\n p = p.next = { key: keys[i], data: values[i] };\n }\n p.next = null;\n return head.next;\n}\n\n\nfunction toList (root) {\n var current = root;\n var Q = [], done = false;\n\n const head = { next: null };\n let p = head;\n\n while (!done) {\n if (current) {\n Q.push(current);\n current = current.left;\n } else {\n if (Q.length > 0) {\n current = p = p.next = Q.pop();\n current = current.right;\n } else done = true;\n }\n }\n p.next = null; // that'll work even if the tree was empty\n return head.next;\n}\n\n\nfunction sortedListToBST(list, start, end) {\n const size = end - start;\n if (size > 0) {\n const middle = start + Math.floor(size / 2);\n const left = sortedListToBST(list, start, middle);\n\n const root = list.head;\n root.left = left;\n\n list.head = list.head.next;\n\n root.right = sortedListToBST(list, middle + 1, end);\n return root;\n }\n return null;\n}\n\n\nfunction mergeLists (l1, l2, compare = (a, b) => a - b) {\n const head = {}; // dummy\n let p = head;\n\n let p1 = l1;\n let p2 = l2;\n\n while (p1 !== null && p2 !== null) {\n if (compare(p1.key, p2.key) < 0) {\n p.next = p1;\n p1 = p1.next;\n } else {\n p.next = p2;\n p2 = p2.next;\n }\n p = p.next;\n }\n\n if (p1 !== null) p.next = p1;\n else if (p2 !== null) p.next = p2;\n\n return head.next;\n}\n\n\nfunction sort(keys, values, left, right, compare) {\n if (left >= right) return;\n\n const pivot = keys[(left + right) >> 1];\n let i = left - 1;\n let j = right + 1;\n\n while (true) {\n do i++; while (compare(keys[i], pivot) < 0);\n do j--; while (compare(keys[j], pivot) > 0);\n if (i >= j) break;\n\n let tmp = keys[i];\n keys[i] = keys[j];\n keys[j] = tmp;\n\n tmp = values[i];\n values[i] = values[j];\n values[j] = tmp;\n }\n\n sort(keys, values, left, j, compare);\n sort(keys, values, j + 1, right, compare);\n}\n","/**\n * Just a collection of geometry related utilities\n */\n\n// This is used for precision checking (e.g. two numbers are equal\n// if their difference is smaller than this number). The value is \n// chosen empirically. We still may run into precision related issues.\n// TODO: we should allow consumers to configure this.\nexport const EPS = 1e-9;//10;\n\nexport function getIntersectionXPoint(segment, xPos, yPos) {\n var dy1 = segment.from.y - yPos;\n var dy2 = yPos - segment.to.y;\n var dy = segment.to.y - segment.from.y;\n if (Math.abs(dy1) < EPS) {\n // The segment starts on the sweep line\n if (Math.abs(dy) < EPS) {\n // the segment is horizontal. Intersection is at the point\n if (xPos <= segment.from.x) return segment.from.x;\n if (xPos > segment.to.x) return segment.to.x;\n return xPos;\n }\n return segment.from.x;\n }\n \n var dx = (segment.to.x - segment.from.x); \n var xOffset; \n if (dy1 >= dy2) {\n xOffset = dy1 * (dx / dy); \n return (segment.from.x - xOffset);\n } \n xOffset = dy2 * (dx / dy);\n return (segment.to.x + xOffset);\n}\n\nexport function angle(dx, dy) {\n // https://stackoverflow.com/questions/16542042/fastest-way-to-sort-vectors-by-angle-without-actually-computing-that-angle\n var p = dx/(Math.abs(dx) + Math.abs(dy)) // -1 .. 1 increasing with x\n\n if (dy < 0) return p - 1; // -2 .. 0 increasing with x\n return 1 - p // 0 .. 2 decreasing with x\n}\n\nexport function intersectSegments(a, b) {\n // https://stackoverflow.com/a/1968345/125351\n var aStart = a.from, bStart = b.from;\n var p0_x = aStart.x, p0_y = aStart.y,\n p2_x = bStart.x, p2_y = bStart.y;\n\n var s1_x = a.dx, s1_y = a.dy, s2_x = b.dx, s2_y = b.dy;\n var div = s1_x * s2_y - s2_x * s1_y;\n\n var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div;\n if (s < 0 || s > 1) return;\n\n var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div;\n\n if (t >= 0 && t <= 1) {\n return {\n x: p0_x - (t * s1_x),\n y: p0_y - (t * s1_y)\n }\n }\n}\n\nexport function same(x0, x1, y0, y1) {\n return Math.abs(x0 - x1) < EPS && Math.abs(y0 - y1) < EPS;\n}\n\nexport function samePoint(a, b) {\n return Math.abs(a.x - b.x) < EPS && Math.abs(a.y - b.y) < EPS;\n}\n","import SplayTree from 'splaytree';\nimport {samePoint, getIntersectionXPoint} from './geom'\n\n/**\n * Creates a new sweep status data structure.\n */\nexport default function createSweepStatus(onError, EPS) {\n var lastPointY, prevY;\n var lastPointX, prevX;\n var useBelow = false;\n var status = new SplayTree(compareSegments);\n\n // To save on GC we return mutable object.\n var currentBoundary = {\n beforeLeft: null,\n left: null,\n right: null,\n afterRight: null,\n }\n\n var currentLeftRight = {left: null, right: null};\n\n return {\n /**\n * Add new segments into the status tree.\n */\n insertSegments,\n\n /**\n * Remove segments from the status tree.\n */\n deleteSegments,\n\n /**\n * Returns segments that are to the left and right from a given point.\n */\n getLeftRightPoint,\n\n /**\n * For a given collections of segments finds the most left and the most right\n * segments. Also returns segments immediately before left, and after right segments.\n */\n getBoundarySegments,\n\n findSegmentsWithPoint,\n\n /**\n * Current binary search tree with segments\n */\n status,\n\n /**\n * Introspection method that verifies if there are duplicates in the segment tree.\n * If there are - `onError()` is called.\n */\n checkDuplicate,\n\n /**\n * Prints current segments in order of their intersection with sweep line. Introspection method.\n */\n printStatus,\n\n /**\n * Returns current position of the sweep line.\n */\n getLastPoint() {\n return {x: lastPointX, y: lastPointY};\n }\n }\n\n function compareSegments(a, b) {\n if (a === b) return 0;\n\n var ak = getIntersectionXPoint(a, lastPointX, lastPointY);\n var bk = getIntersectionXPoint(b, lastPointX, lastPointY);\n\n var res = ak - bk;\n if (Math.abs(res) >= EPS) {\n // We are okay fine. Intersection distance between two segments\n // is good to give conclusive answer\n return res;\n }\n\n var aIsHorizontal = Math.abs(a.dy) < EPS;\n var bIsHorizontal = Math.abs(b.dy) < EPS;\n if (aIsHorizontal && bIsHorizontal) {\n return b.to.x - a.to.x;\n }\n // TODO: What if both a and b is horizontal?\n // move horizontal to end\n if (aIsHorizontal) { \n return useBelow ? -1 : 1;\n }\n\n if (bIsHorizontal) {\n if (useBelow) {\n return (b.from.x >= lastPointX) ? -1 : 1\n }\n return -1;\n // return useBelow ? 1 : -1;\n }\n var pa = a.angle;\n var pb = b.angle;\n if (Math.abs(pa - pb) >= EPS) {\n return useBelow ? pa - pb : pb - pa;\n }\n\n var segDist = a.from.y - b.from.y;\n if (Math.abs(segDist) >= EPS) {\n return -segDist;\n }\n segDist = a.to.y - b.to.y;\n if (Math.abs(segDist) >= EPS) {\n // TODO: Is this accurate?\n return -segDist;\n }\n\n return 0;\n // Could also use:\n // var aAngle = Math.atan2(a.from.y - a.to.y, a.from.x - a.to.x);\n // var bAngle = Math.atan2(b.from.y - b.to.y, b.from.x - b.to.x);\n // return useBelow ? bAngle - aAngle : aAngle - bAngle;\n }\n\n function getBoundarySegments(upper, interior) {\n var leftMost, rightMost, i;\n var uLength = upper.length;\n\n if (uLength > 0) {\n leftMost = rightMost = upper[0];\n } else {\n leftMost = rightMost = interior[0];\n }\n\n for (i = 1; i < uLength; ++i) {\n var s = upper[i];\n var cmp = compareSegments(leftMost, s);\n if (cmp > 0) leftMost = s;\n\n cmp = compareSegments(rightMost, s);\n if (cmp < 0) rightMost = s;\n }\n\n var startFrom = uLength > 0 ? 0 : 1;\n for (i = startFrom; i < interior.length; ++i) {\n s = interior[i];\n cmp = compareSegments(leftMost, s);\n if (cmp > 0) leftMost = s;\n\n cmp = compareSegments(rightMost, s);\n if (cmp < 0) rightMost = s;\n }\n\n // at this point we have our left/right segments in the status.\n // Let's find their prev/next elements and report them back:\n var left = status.find(leftMost);\n if (!left) {\n onError('Left is missing. Precision error?');\n }\n\n var right = status.find(rightMost);\n if (!right) {\n onError('Right is missing. Precision error?');\n }\n\n var beforeLeft = left && status.prev(left);\n var afterRight = right && status.next(right);\n\n while (afterRight && right.key.dy === 0 && afterRight.key.dy === 0) {\n // horizontal segments are special :(\n afterRight = status.next(afterRight);\n }\n\n currentBoundary.beforeLeft = beforeLeft && beforeLeft.key;\n currentBoundary.left = left && left.key;\n currentBoundary.right = right && right.key;\n currentBoundary.afterRight = afterRight && afterRight.key;\n\n return currentBoundary;\n }\n\n function getLeftRightPoint(p) {\n // We are trying to find left and right segments that are nearest to the\n // point p. For this we traverse the binary search tree, and remember\n // node with the shortest distance to p.\n var lastLeft;\n var current = status._root;\n var minX = Number.POSITIVE_INFINITY;\n\n var useNext = false;\n while (current) {\n var x = getIntersectionXPoint(current.key, p.x, p.y);\n var dx = p.x - x;\n if (dx >= 0) {\n if (dx < minX) {\n minX = dx;\n lastLeft = current;\n current = current.left;\n useNext = false;\n } else {\n break;\n }\n } else {\n if (-dx < minX) {\n useNext = true;\n minX = -dx;\n lastLeft = current;\n current = current.right;\n } else {\n break;\n }\n }\n }\n if (useNext) {\n // I'm not sure why I did this. I don't this this is right now.\n // lastLeft = status.next(lastLeft);\n }\n\n currentLeftRight.left = lastLeft && lastLeft.key\n var next = lastLeft && status.next(lastLeft);\n currentLeftRight.right = next && next.key\n return currentLeftRight;\n\n // Conceptually, the code above should be equivalent to the code below;\n // The code below is easier to understand, but intuitively, the code above\n // should have better performance (as we do not traverse the entire status\n // tree)\n\n // var right, left, x;\n // var all = status.keys()\n // for (var i = 0; i < all.length; ++i) {\n // var segment = all[i];\n // x = getIntersectionXPoint(segment, p.x, p.y);\n // if (x > p.x && !right) {\n // right = segment;\n // break;\n // } else if (x < p.x) {\n // left = segment;\n // }\n // }\n\n // currentLeftRight.left = left;\n // currentLeftRight.right = right;\n\n // return currentLeftRight;\n }\n\n function findSegmentsWithPoint(p, onFound) {\n // Option 1.\n // var arrResults = [];\n // status.forEach(current => {\n // var x = getIntersectionXPoint(current.key, p.x, p.y);\n // var dx = p.x - x;\n // if (Math.abs(dx) < EPS) {\n // onFound(current.key);\n // // arrResults.push(current.key)\n // }\n // });\n // return arrResults;\n\n // Option 2.\n\n // let current = status._root;\n // const Q = []; /* Initialize stack s */\n // let done = false;\n // var res = [];\n // var breakEarly = false;\n\n // while (!done) {\n // if (current !== null) {\n // Q.push(current);\n // current = current.left;\n // } else {\n // if (Q.length !== 0) {\n // current = Q.pop();\n\n // var x = getIntersectionXPoint(current.key, p.x, p.y);\n // var dx = p.x - x;\n // if (Math.abs(dx) < EPS) {\n // res.push(current.key)\n // breakEarly = true;\n // } else if (breakEarly) {\n // done = true;\n // }\n\n // current = current.right;\n // } else done = true;\n // }\n // }\n\n // return res;\n\n // option 3.\n var current = status._root;\n\n while (current) {\n var x = getIntersectionXPoint(current.key, p.x, p.y);\n var dx = p.x - x;\n if (Math.abs(dx) < EPS) {\n collectAdjacentNodes(current, p, onFound);\n break;\n } else if (dx < 0) {\n current = current.left;\n } else {\n current = current.right;\n }\n }\n }\n\n function collectAdjacentNodes(root, p, onFound) {\n onFound(root.key);\n goOverPredecessors(root.left, p, onFound);\n goOverSuccessors(root.right, p, onFound);\n }\n\n function goOverPredecessors(root, p, res) {\n if (!root) return;\n var x = getIntersectionXPoint(root.key, p.x, p.y);\n var dx = p.x - x;\n if (Math.abs(dx) < EPS) {\n collectAdjacentNodes(root, p, res);\n } else {\n goOverPredecessors(root.right, p, res);\n }\n }\n\n function goOverSuccessors(root, p, res) {\n if (!root) return;\n var x = getIntersectionXPoint(root.key, p.x, p.y);\n var dx = p.x - x;\n if (Math.abs(dx) < EPS) {\n collectAdjacentNodes(root, p, res);\n } else {\n goOverSuccessors(root.left, p, res);\n }\n }\n\n function checkDuplicate() {\n var prev;\n status.forEach(node => {\n var current = node.key;\n\n if (prev) {\n if (samePoint(prev.from, current.from) && samePoint(prev.to, current.to)) {\n // Likely you have received error before during segment removal.\n onError('Duplicate key in the status! This may be caused by Floating Point rounding error')\n }\n }\n prev = current;\n });\n }\n\n function printStatus(prefix = '') {\n // eslint-disable-next-line\n console.log(prefix, 'status line: ', lastPointX, lastPointY);\n status.forEach(node => {\n var x = getIntersectionXPoint(node.key, lastPointX, lastPointY);\n // eslint-disable-next-line\n console.log(x + ' ' + node.key.name);\n })\n }\n\n function insertSegments(interior, upper, sweepLinePos) {\n lastPointY = sweepLinePos.y;\n lastPointX = sweepLinePos.x;\n var key;\n\n for (var i = 0; i < interior.length; ++i) {\n key = interior[i];\n status.add(key);\n }\n for (i = 0; i < upper.length; ++i) {\n key = upper[i]\n status.add(key);\n }\n }\n\n function deleteSegments(lower, interior, sweepLinePos) {\n // I spent most of the time debugging this method. Depending on the\n // algorithm state we can run into situation when dynamic keys of the\n // `status` tree predict wrong branch, and thus we are not able to find\n // the segment that needs to be deleted. If that happens I'm trying to\n // use previous point and repeat the process. This may result in \n // incorrect state. In that case I report an error. \n var i;\n var prevCount = status._size;\n prevX = lastPointX;\n prevY = lastPointY;\n lastPointY = sweepLinePos.y;\n lastPointX = sweepLinePos.x;\n\n useBelow = true;\n for(i = 0; i < lower.length; ++i) {\n removeSegment(lower[i], sweepLinePos)\n }\n for(i = 0; i < interior.length; ++i) {\n removeSegment(interior[i], sweepLinePos)\n }\n useBelow = false;\n\n if (status._size !== prevCount - interior.length - lower.length) {\n // This can happen when rounding error occurs. You can try scaling your input\n onError('Segments were not removed from a tree properly. Precision error?');\n }\n }\n\n function removeSegment(key, sweepLinePos) {\n if (status.find(key)) {\n status.remove(key);\n } else {\n lastPointX = prevX;\n lastPointY = prevY;\n if (status.find(key)) {\n status.remove(key);\n } else {\n // They will get an error :(\n }\n lastPointY = sweepLinePos.y;\n lastPointX = sweepLinePos.x;\n }\n }\n}","/**\n * Represents a single event in the sweep-line algorithm\n */\nexport default class SweepEvent {\n /**\n * Creates new sweep event of a given kind.\n */\n constructor(point, segment) {\n this.point = point;\n if (segment) this.from = [segment];\n }\n}\n","import createEventQueue from './createEventQueue';\nimport createSweepStatus from './sweepStatus';\nimport SweepEvent from './SweepEvent';\n\nimport {intersectSegments, EPS, angle, samePoint} from './geom';\n\n/**\n * A point on a line\n * \n * @typedef {Object} Point\n * @property {number} x coordinate\n * @property {number} y coordinate\n */\n\n\n/**\n * @typedef {Object} Segment \n * @property {Point} from start of the segment\n * @property {Point} to end of the segment\n */\n\n/**\n * @typedef {function(point : Point, interior : Segment[], lower : Segment[], upper : Segment[])} ReportIntersectionCallback\n */\n\n/**\n * @typedef {Object} ISectOptions \n * @property {ReportIntersectionCallback} onFound \n */\n\n /**\n * @typedef {Object} ISectResult\n */\n\n// We use EMPTY array to avoid pressure on garbage collector. Need to be\n// very cautious to not mutate this array.\nvar EMPTY = [];\n\n/**\n * Finds all intersections among given segments.\n * \n * The algorithm follows \"Computation Geometry, Algorithms and Applications\" book\n * by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars.\n * \n * Line is swept top-down\n * \n * @param {Segment[]} segments\n * @param {ISectOptions=} options\n * @returns {ISectResult}\n */\nexport default function isect(segments, options) {\n var results = [];\n var reportIntersection = (options && options.onFound) || defaultIntersectionReporter;\n\n var onError = (options && options.onError) || defaultErrorReporter;\n\n var eventQueue = createEventQueue(byY);\n var sweepStatus = createSweepStatus(onError, EPS);\n var lower, interior, lastPoint;\n\n segments.forEach(addSegment);\n\n return {\n /**\n * Find all intersections synchronously.\n * \n * @returns array of found intersections.\n */\n run,\n\n /**\n * Performs a single step in the sweep line algorithm\n * \n * @returns true if there was something to process; False if no more work to do\n */\n step,\n\n // Methods below are low level API for fine-grained control.\n // Don't use it unless you understand this code thoroughly\n\n /**\n * Add segment into the \n */\n addSegment,\n\n /**\n * Direct access to event queue. Queue contains segment endpoints and\n * pending detected intersections.\n */\n eventQueue, \n\n /**\n * Direct access to sweep line status. \"Status\" holds information about\n * all intersected segments.\n */\n sweepStatus,\n\n /**\n * Access to results array. Works only when you use default onFound() handler\n */\n results\n }\n\n function run() {\n while (!eventQueue.isEmpty()) {\n var eventPoint = eventQueue.pop();\n if (handleEventPoint(eventPoint)) {\n // they decided to stop.\n return;\n };\n }\n\n return results;\n }\n\n function step() {\n if (!eventQueue.isEmpty()) {\n var eventPoint = eventQueue.pop();\n handleEventPoint(eventPoint);\n // Note: we don't check results of `handleEventPoint()`\n // assumption is that client controls `step()` and thus they \n // know better if they want to stop.\n return true;\n }\n return false;\n }\n\n function handleEventPoint(p) {\n lastPoint = p.point;\n var upper = p.from || EMPTY;\n\n lower = interior = undefined;\n // TODO: move lower/interior into sweep status method?\n\n sweepStatus.findSegmentsWithPoint(lastPoint, addLowerOrInterior);\n // if (segmentsWithPoint) {\n // segmentsWithPoint.forEach()\n // } \n\n if (!lower) lower = EMPTY;\n if (!interior) interior = EMPTY;\n\n var uLength = upper.length;\n var iLength = interior.length;\n var lLength = lower.length;\n var hasIntersection = uLength + iLength + lLength > 1;\n var hasPointIntersection = !hasIntersection && (uLength === 0 && lLength === 0 && iLength > 0);\n\n if (hasIntersection || hasPointIntersection) {\n p.isReported = true;\n if (reportIntersection(lastPoint, interior, lower, upper)) {\n return true;\n }\n }\n\n sweepStatus.deleteSegments(lower, interior, lastPoint);\n sweepStatus.insertSegments(interior, upper, lastPoint);\n\n var sLeft, sRight;\n\n var hasNoCrossing = (uLength + iLength === 0);\n\n if (hasNoCrossing) {\n var leftRight = sweepStatus.getLeftRightPoint(lastPoint);\n sLeft = leftRight.left;\n if (!sLeft) return;\n\n sRight = leftRight.right;\n if (!sRight) return;\n\n findNewEvent(sLeft, sRight, p);\n } else {\n var boundarySegments = sweepStatus.getBoundarySegments(upper, interior);\n\n findNewEvent(boundarySegments.beforeLeft, boundarySegments.left, p);\n findNewEvent(boundarySegments.right, boundarySegments.afterRight, p);\n }\n\n return false;\n }\n\n function addLowerOrInterior(s) {\n if (samePoint(s.to, lastPoint)) {\n if (!lower) lower = [s];\n else lower.push(s);\n } else if (!samePoint(s.from, lastPoint)) {\n if (!interior) interior = [s];\n else interior.push(s);\n }\n }\n\n function findNewEvent(left, right, p) {\n if (!left || !right) return;\n\n var intersection = intersectSegments(left, right);\n if (!intersection) {\n return;\n }\n\n var dy = p.point.y - intersection.y\n // TODO: should I add dy to intersection.y?\n if (dy < -EPS) {\n // this means intersection happened after the sweep line. \n // We already processed it.\n return;\n }\n if (Math.abs(dy) < EPS && intersection.x <= p.point.x) {\n return;\n }\n\n // Need to adjust floating point for this special case,\n // since otherwise it gives rounding errors:\n roundNearZero(intersection);\n\n var current = eventQueue.find(intersection);\n\n if (current && current.isReported) {\n // We already reported this event. No need to add it one more time\n // TODO: Is this case even possible?\n onError('We already reported this event.');\n return;\n }\n\n if (!current) {\n var event = new SweepEvent(intersection)\n eventQueue.insert(event);\n }\n }\n\n function defaultIntersectionReporter(p, interior, lower, upper) {\n results.push({\n point: p, \n segments: union(union(interior, lower), upper)\n });\n }\n\n function addSegment(segment) {\n var from = segment.from;\n var to = segment.to;\n\n // Small numbers give more precision errors. Rounding them to 0.\n roundNearZero(from);\n roundNearZero(to);\n\n var dy = from.y - to.y;\n\n // Note: dy is much smaller then EPS on purpose. I found that higher\n // precision here does less good - getting way more rounding errors.\n if (Math.abs(dy) < 1e-5) {\n from.y = to.y;\n segment.dy = 0;\n }\n if ((from.y < to.y) || (\n (from.y === to.y) && (from.x > to.x))\n ) {\n var temp = from;\n from = segment.from = to; \n to = segment.to = temp;\n }\n\n // We pre-compute some immutable properties of the segment\n // They are used quite often in the tree traversal, and pre-computation\n // gives significant boost:\n segment.dy = from.y - to.y;\n segment.dx = from.x - to.x;\n segment.angle = angle(segment.dy, segment.dx);\n\n var isPoint = segment.dy === segment.dx && segment.dy === 0;\n var prev = eventQueue.find(from)\n if (prev && !isPoint) {\n // this detects identical segments early. Without this check\n // the algorithm would break since sweep line has no means to\n // detect identical segments.\n var prevFrom = prev.data.from;\n if (prevFrom) {\n for (var i = 0; i < prevFrom.length; ++i) {\n var s = prevFrom[i];\n if (samePoint(s.to, to)) {\n reportIntersection(s.from, [], s.from, s.to);\n reportIntersection(s.to, [], s.from, s.to);\n return;\n }\n }\n }\n }\n\n if (!isPoint) {\n if (prev) {\n if (prev.data.from) prev.data.from.push(segment);\n else prev.data.from = [segment];\n } else {\n var e = new SweepEvent(from, segment)\n eventQueue.insert(e);\n }\n var event = new SweepEvent(to)\n eventQueue.insert(event)\n } else {\n var event = new SweepEvent(to)\n eventQueue.insert(event)\n }\n } \n}\n\nfunction roundNearZero(point) {\n if (Math.abs(point.x) < EPS) point.x = 0;\n if (Math.abs(point.y) < EPS) point.y = 0;\n}\n\nfunction defaultErrorReporter(errorMessage) {\n throw new Error(errorMessage);\n}\n\nfunction union(a, b) {\n if (!a) return b;\n if (!b) return a;\n\n return a.concat(b);\n}\n\nfunction byY(a, b) {\n // decreasing Y \n var res = b.y - a.y;\n // TODO: This might mess up the status tree.\n if (Math.abs(res) < EPS) {\n // increasing x.\n res = a.x - b.x;\n if (Math.abs(res) < EPS) res = 0;\n }\n\n return res;\n}","/**\n * This is a brute force solution with O(n^2) performance.\n * (`n` is number of segments).\n * \n * Use this when number of lines is low, and number of intersections\n * is high.\n */\n\nexport default function brute(lines, options) {\n var results = [];\n var reportIntersection = (options && options.onFound) || \n defaultIntersectionReporter;\n var asyncState;\n\n return {\n /**\n * Execute brute force of the segment intersection search\n */\n run,\n /**\n * Access to results array. Works only when you use default onFound() handler\n */\n results,\n\n /**\n * Performs a single step in the brute force algorithm ()\n */\n step\n }\n\n function step() {\n if (!asyncState) {\n asyncState = {\n i: 0\n }\n }\n var test = lines[asyncState.i];\n for (var j = asyncState.i + 1; j < lines.length; ++j) {\n var other = lines[j];\n var pt = intersectSegments(test, other);\n if (pt) {\n if (reportIntersection(pt, [test, other])) {\n return;\n }\n }\n }\n asyncState.i += 1;\n return asyncState.i < lines.length;\n }\n\n function run() {\n for(var i = 0; i < lines.length; ++i) {\n var test = lines[i];\n for (var j = i + 1; j < lines.length; ++j) {\n var other = lines[j];\n var pt = intersectSegments(test, other);\n if (pt) {\n if (reportIntersection(pt, [test, other])) {\n return;\n }\n }\n }\n }\n return results;\n }\n\n function defaultIntersectionReporter(p, interior) {\n results.push({\n point: p, \n segments: interior\n });\n }\n}\n\nfunction intersectSegments(a, b) {\n // https://stackoverflow.com/a/1968345/125351\n var aStart = a.from, bStart = b.from;\n var p0_x = aStart.x, p0_y = aStart.y,\n p2_x = bStart.x, p2_y = bStart.y;\n\n var s1_x = a.from.x - a.to.x, s1_y = a.from.y - a.to.y, s2_x = b.from.x - b.to.x, s2_y = b.from.y - b.to.y;\n var div = s1_x * s2_y - s2_x * s1_y;\n\n var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div;\n if (s < 0 || s > 1) return;\n\n var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div;\n\n if (t >= 0 && t <= 1) {\n return {\n x: p0_x - (t * s1_x),\n y: p0_y - (t * s1_y)\n }\n }\n}\n","import SplayTree from 'splaytree';\n\nexport default function createEventQueue(byY) {\n const q = new SplayTree(byY);\n\n return {\n isEmpty: isEmpty,\n size: size,\n pop: pop,\n find: find,\n insert: insert\n }\n\n function find(p) {\n return q.find(p);\n }\n\n function size() {\n return q.size;\n }\n\n function isEmpty() {\n return q.isEmpty();\n }\n\n function insert(event) {\n // debugger;\n q.add(event.point, event);\n }\n\n function pop() {\n var node = q.pop();\n return node && node.data;\n }\n}\n"],"names":["Node","key","data","this","left","right","DEFAULT_COMPARE","a","b","splay","i","t","comparator","let","l","r","y","N","const","cmp","insert","tree","node","_size","remove","x","split","v","Tree","_comparator","_root","add","pop","findStatic","current","compare","find","contains","forEach","visitor","ctx","Q","done","push","length","call","range","low","high","fn","keys","ref","values","min","minNode","max","maxNode","at","index","next","d","root","successor","prev","predecessor","clear","toList","head","p","load","presort","size","sort","pivot","j","tmp","loadRecursive","parent","start","end","middle","Math","floor","mergedList","l1","l2","p1","p2","mergeLists","createList","sortedListToBST","list","isEmpty","prototypeAccessors","toString","printNode","n","out","printRow","prefix","isTail","indent","join","update","newKey","newData","EPS","getIntersectionXPoint","segment","xPos","yPos","dy1","from","dy2","to","dy","abs","xOffset","dx","samePoint","createSweepStatus","onError","lastPointY","prevY","lastPointX","prevX","useBelow","status","SplayTree","compareSegments","currentBoundary","beforeLeft","afterRight","currentLeftRight","interior","upper","sweepLinePos","lower","prevCount","removeSegment","lastLeft","minX","Number","POSITIVE_INFINITY","leftMost","rightMost","uLength","s","onFound","collectAdjacentNodes","console","log","name","getLastPoint","res","aIsHorizontal","bIsHorizontal","pa","angle","pb","segDist","goOverPredecessors","goOverSuccessors","SweepEvent","point","EMPTY","roundNearZero","defaultErrorReporter","errorMessage","Error","union","concat","byY","intersectSegments","aStart","bStart","p0_x","p0_y","p2_x","p2_y","s1_x","s1_y","s2_x","s2_y","div","segments","options","q","lastPoint","results","reportIntersection","eventQueue","event","sweepStatus","addSegment","eventPoint","handleEventPoint","undefined","findSegmentsWithPoint","addLowerOrInterior","sLeft","sRight","iLength","lLength","hasIntersection","isReported","deleteSegments","insertSegments","leftRight","getLeftRightPoint","findNewEvent","boundarySegments","getBoundarySegments","intersection","temp","isPoint","prevFrom","e","lines","asyncState","test","other","pt"],"mappings":"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"} \ No newline at end of file diff --git a/build/isect.module.js b/build/isect.module.js new file mode 100644 index 0000000..b2f2c5e --- /dev/null +++ b/build/isect.module.js @@ -0,0 +1,1725 @@ +/* follows "An implementation of top-down splaying" + * by D. Sleator March 1992 + */ + +/** + * @typedef {*} Key + */ + + +/** + * @typedef {*} Value + */ + + +/** + * @typedef {function(node:Node):void} Visitor + */ + + +/** + * @typedef {function(a:Key, b:Key):number} Comparator + */ + + +/** + * @param {function(node:Node):string} NodePrinter + */ + + +/** + * @typedef {Object} Node + * @property {Key} Key + * @property {Value=} data + * @property {Node} left + * @property {Node} right + */ + +var Node = function Node (key, data) { + this.key = key; + this.data = data; + this.left = null; + this.right= null; +}; + +function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; } + + +/** + * Simple top down splay, not requiring i to be in the tree t. + * @param {Key} i + * @param {Node?} t + * @param {Comparator} comparator + */ +function splay (i, t, comparator) { + if (t === null) { return t; } + var l, r, y; + var N = new Node(); + l = r = N; + + while (true) { + var cmp = comparator(i, t.key); + //if (i < t.key) { + if (cmp < 0) { + if (t.left === null) { break; } + //if (i < t.left.key) { + if (comparator(i, t.left.key) < 0) { + y = t.left; /* rotate right */ + t.left = y.right; + y.right = t; + t = y; + if (t.left === null) { break; } + } + r.left = t; /* link right */ + r = t; + t = t.left; + //} else if (i > t.key) { + } else if (cmp > 0) { + if (t.right === null) { break; } + //if (i > t.right.key) { + if (comparator(i, t.right.key) > 0) { + y = t.right; /* rotate left */ + t.right = y.left; + y.left = t; + t = y; + if (t.right === null) { break; } + } + l.right = t; /* link left */ + l = t; + t = t.right; + } else { + break; + } + } + /* assemble */ + l.right = t.left; + r.left = t.right; + t.left = N.right; + t.right = N.left; + return t; +} + + +/** + * @param {Key} i + * @param {Value} data + * @param {Comparator} comparator + * @param {Tree} tree + * @return {Node} root + */ +function insert (i, data, t, comparator, tree) { + var node = new Node(i, data); + + tree._size++; + + if (t === null) { + node.left = node.right = null; + return node; + } + + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + if (cmp < 0) { + node.left = t.left; + node.right = t; + t.left = null; + } else if (cmp >= 0) { + node.right = t.right; + node.left = t; + t.right = null; + } + return node; +} + + +/** + * Insert i into the tree t, unless it's already there. + * @param {Key} i + * @param {Value} data + * @param {Comparator} comparator + * @param {Tree} tree + * @return {Node} root + */ +function add (i, data, t, comparator, tree) { + var node = new Node(i, data); + + if (t === null) { + node.left = node.right = null; + tree._size++; + return node; + } + + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + if (cmp === 0) { return t; } + else { + if (cmp < 0) { + node.left = t.left; + node.right = t; + t.left = null; + } else if (cmp > 0) { + node.right = t.right; + node.left = t; + t.right = null; + } + tree._size++; + return node; + } +} + + +/** + * Deletes i from the tree if it's there + * @param {Key} i + * @param {Tree} tree + * @param {Comparator} comparator + * @param {Tree} tree + * @return {Node} new root + */ +function remove (i, t, comparator, tree) { + var x; + if (t === null) { return null; } + t = splay(i, t, comparator); + var cmp = comparator(i, t.key); + if (cmp === 0) { /* found it */ + if (t.left === null) { + x = t.right; + } else { + x = splay(i, t.left, comparator); + x.right = t.right; + } + tree._size--; + return x; + } + return t; /* It wasn't there */ +} + + +function split (key, v, comparator) { + var left, right; + if (v === null) { + left = right = null; + } else { + v = splay(key, v, comparator); + + var cmp = comparator(v.key, key); + if (cmp === 0) { + left = v.left; + right = v.right; + } else if (cmp < 0) { + right = v.right; + v.right = null; + left = v; + } else { + left = v.left; + v.left = null; + right = v; + } + } + return { left: left, right: right }; +} + + +function merge (left, right, comparator) { + if (right === null) { return left; } + if (left === null) { return right; } + + right = splay(left.key, right, comparator); + right.left = left; + return right; +} + + +/** + * Prints level of the tree + * @param {Node} root + * @param {String} prefix + * @param {Boolean} isTail + * @param {Array} out + * @param {Function(node:Node):String} printNode + */ +function printRow (root, prefix, isTail, out, printNode) { + if (root) { + out(("" + prefix + (isTail ? '└── ' : '├── ') + (printNode(root)) + "\n")); + var indent = prefix + (isTail ? ' ' : '│ '); + if (root.left) { printRow(root.left, indent, false, out, printNode); } + if (root.right) { printRow(root.right, indent, true, out, printNode); } + } +} + + +var Tree = function Tree (comparator) { + if ( comparator === void 0 ) comparator = DEFAULT_COMPARE; + + this._comparator = comparator; + this._root = null; + this._size = 0; +}; + +var prototypeAccessors = { size: { configurable: true } }; + + +/** + * Inserts a key, allows duplicates + * @param{Key} key + * @param{Value=} data + * @return {Node|null} + */ +Tree.prototype.insert = function insert$1 (key, data) { + return this._root = insert(key, data, this._root, this._comparator, this); +}; + + +/** + * Adds a key, if it is not present in the tree + * @param{Key} key + * @param{Value=} data + * @return {Node|null} + */ +Tree.prototype.add = function add$1 (key, data) { + return this._root = add(key, data, this._root, this._comparator, this); +}; + + +/** + * @param{Key} key + * @return {Node|null} + */ +Tree.prototype.remove = function remove$1 (key) { + this._root = remove(key, this._root, this._comparator, this); +}; + + +/** + * Removes and returns the node with smallest key + * @return {?Node} + */ +Tree.prototype.pop = function pop () { + var node = this._root; + if (node) { + while (node.left) { node = node.left; } + this._root = splay(node.key,this._root, this._comparator); + this._root = remove(node.key, this._root, this._comparator, this); + return { key: node.key, data: node.data }; + } + return null; +}; + + +/** + * @param{Key} key + * @return {Node|null} + */ +Tree.prototype.findStatic = function findStatic (key) { + var current = this._root; + var compare = this._comparator; + while (current) { + var cmp = compare(key, current.key); + if (cmp === 0) { return current; } + else if (cmp < 0) { current = current.left; } + else { current = current.right; } + } + return null; +}; + + +/** + * @param{Key} key + * @return {Node|null} + */ +Tree.prototype.find = function find (key) { + if (this._root) { + this._root = splay(key, this._root, this._comparator); + if (this._comparator(key, this._root.key) !== 0) { return null; } + } + return this._root; +}; + + +/** + * @param{Key} key + * @return {Boolean} + */ +Tree.prototype.contains = function contains (key) { + var current = this._root; + var compare = this._comparator; + while (current) { + var cmp = compare(key, current.key); + if (cmp === 0) { return true; } + else if (cmp < 0) { current = current.left; } + else { current = current.right; } + } + return false; +}; + + +/** + * @param{Visitor} visitor + * @param{*=} ctx + * @return {SplayTree} + */ +Tree.prototype.forEach = function forEach (visitor, ctx) { + var current = this._root; + var Q = [];/* Initialize stack s */ + var done = false; + + while (!done) { + if (current !==null) { + Q.push(current); + current = current.left; + } else { + if (Q.length !== 0) { + current = Q.pop(); + visitor.call(ctx, current); + + current = current.right; + } else { done = true; } + } + } + return this; +}; + + +/** + * Walk key range from `low` to `high`. Stops if `fn` returns a value. + * @param{Key} low + * @param{Key} high + * @param{Function} fn + * @param{*?} ctx + * @return {SplayTree} + */ +Tree.prototype.range = function range (low, high, fn, ctx) { + var this$1 = this; + + var Q = []; + var compare = this._comparator; + var node = this._root, cmp; + + while (Q.length !== 0 || node) { + if (node) { + Q.push(node); + node = node.left; + } else { + node = Q.pop(); + cmp = compare(node.key, high); + if (cmp > 0) { + break; + } else if (compare(node.key, low) >= 0) { + if (fn.call(ctx, node)) { return this$1; } // stop if smth is returned + } + node = node.right; + } + } + return this; +}; + + +/** + * Returns array of keys + * @return {Array} + */ +Tree.prototype.keys = function keys () { + var keys = []; + this.forEach(function (ref) { + var key = ref.key; + + return keys.push(key); + }); + return keys; +}; + + +/** + * Returns array of all the data in the nodes + * @return {Array} + */ +Tree.prototype.values = function values () { + var values = []; + this.forEach(function (ref) { + var data = ref.data; + + return values.push(data); + }); + return values; +}; + + +/** + * @return {Key|null} + */ +Tree.prototype.min = function min () { + if (this._root) { return this.minNode(this._root).key; } + return null; +}; + + +/** + * @return {Key|null} + */ +Tree.prototype.max = function max () { + if (this._root) { return this.maxNode(this._root).key; } + return null; +}; + + +/** + * @return {Node|null} + */ +Tree.prototype.minNode = function minNode (t) { + if ( t === void 0 ) t = this._root; + + if (t) { while (t.left) { t = t.left; } } + return t; +}; + + +/** + * @return {Node|null} + */ +Tree.prototype.maxNode = function maxNode (t) { + if ( t === void 0 ) t = this._root; + + if (t) { while (t.right) { t = t.right; } } + return t; +}; + + +/** + * Returns node at given index + * @param{number} index + * @return {?Node} + */ +Tree.prototype.at = function at (index) { + var current = this._root, done = false, i = 0; + var Q = []; + + while (!done) { + if (current) { + Q.push(current); + current = current.left; + } else { + if (Q.length > 0) { + current = Q.pop(); + if (i === index) { return current; } + i++; + current = current.right; + } else { done = true; } + } + } + return null; +}; + + +/** + * @param{Node} d + * @return {Node|null} + */ +Tree.prototype.next = function next (d) { + var root = this._root; + var successor = null; + + if (d.right) { + successor = d.right; + while (successor.left) { successor = successor.left; } + return successor; + } + + var comparator = this._comparator; + while (root) { + var cmp = comparator(d.key, root.key); + if (cmp === 0) { break; } + else if (cmp < 0) { + successor = root; + root = root.left; + } else { root = root.right; } + } + + return successor; +}; + + +/** + * @param{Node} d + * @return {Node|null} + */ +Tree.prototype.prev = function prev (d) { + var root = this._root; + var predecessor = null; + + if (d.left !== null) { + predecessor = d.left; + while (predecessor.right) { predecessor = predecessor.right; } + return predecessor; + } + + var comparator = this._comparator; + while (root) { + var cmp = comparator(d.key, root.key); + if (cmp === 0) { break; } + else if (cmp < 0) { root = root.left; } + else { + predecessor = root; + root = root.right; + } + } + return predecessor; +}; + + +/** + * @return {SplayTree} + */ +Tree.prototype.clear = function clear () { + this._root = null; + this._size = 0; + return this; +}; + + +/** + * @return {NodeList} + */ +Tree.prototype.toList = function toList$1 () { + return toList(this._root); +}; + + +/** + * Bulk-load items. Both array have to be same size + * @param{Array} keys + * @param{Array}[values] + * @param{Boolean} [presort=false] Pre-sort keys and values, using + * tree's comparator. Sorting is done + * in-place + * @return {AVLTree} + */ +Tree.prototype.load = function load (keys, values, presort) { + if ( keys === void 0 ) keys = []; + if ( values === void 0 ) values = []; + if ( presort === void 0 ) presort = false; + + var size = keys.length; + var comparator = this._comparator; + + // sort if needed + if (presort) { sort(keys, values, 0, size - 1, comparator); } + + if (this._root === null) { // empty tree + this._root = loadRecursive(this._root, keys, values, 0, size); + this._size = size; + } else { // that re-builds the whole tree from two in-order traversals + var mergedList = mergeLists(this.toList(), createList(keys, values), comparator); + size = this._size + size; + this._root = sortedListToBST({ head: mergedList }, 0, size); + } + return this; +}; + + +/** + * @return {Boolean} + */ +Tree.prototype.isEmpty = function isEmpty () { return this._root === null; }; + +prototypeAccessors.size.get = function () { return this._size; }; + + +/** + * @param{NodePrinter=} printNode + * @return {String} + */ +Tree.prototype.toString = function toString (printNode) { + if ( printNode === void 0 ) printNode = function (n) { return n.key; }; + + var out = []; + printRow(this._root, '', true, function (v) { return out.push(v); }, printNode); + return out.join(''); +}; + + +Tree.prototype.update = function update (key, newKey, newData) { + var comparator = this._comparator; + var ref = split(key, this._root, comparator); + var left = ref.left; + var right = ref.right; + this._size--; + if (comparator(key, newKey) < 0) { + right = insert(newKey, newData, right, comparator, this); + } else { + left = insert(newKey, newData, left, comparator, this); + } + this._root = merge(left, right, comparator); +}; + + +Tree.prototype.split = function split$1 (key) { + return split(key, this._root, this._comparator); +}; + +Object.defineProperties( Tree.prototype, prototypeAccessors ); + + +function loadRecursive (parent, keys, values, start, end) { + var size = end - start; + if (size > 0) { + var middle = start + Math.floor(size / 2); + var key = keys[middle]; + var data = values[middle]; + var node = { key: key, data: data, parent: parent }; + node.left = loadRecursive(node, keys, values, start, middle); + node.right = loadRecursive(node, keys, values, middle + 1, end); + return node; + } + return null; +} + + +function createList(keys, values) { + var head = { next: null }; + var p = head; + for (var i = 0; i < keys.length; i++) { + p = p.next = { key: keys[i], data: values[i] }; + } + p.next = null; + return head.next; +} + + +function toList (root) { + var current = root; + var Q = [], done = false; + + var head = { next: null }; + var p = head; + + while (!done) { + if (current) { + Q.push(current); + current = current.left; + } else { + if (Q.length > 0) { + current = p = p.next = Q.pop(); + current = current.right; + } else { done = true; } + } + } + p.next = null; // that'll work even if the tree was empty + return head.next; +} + + +function sortedListToBST(list, start, end) { + var size = end - start; + if (size > 0) { + var middle = start + Math.floor(size / 2); + var left = sortedListToBST(list, start, middle); + + var root = list.head; + root.left = left; + + list.head = list.head.next; + + root.right = sortedListToBST(list, middle + 1, end); + return root; + } + return null; +} + + +function mergeLists (l1, l2, compare) { + if ( compare === void 0 ) compare = function (a, b) { return a - b; }; + + var head = {}; // dummy + var p = head; + + var p1 = l1; + var p2 = l2; + + while (p1 !== null && p2 !== null) { + if (compare(p1.key, p2.key) < 0) { + p.next = p1; + p1 = p1.next; + } else { + p.next = p2; + p2 = p2.next; + } + p = p.next; + } + + if (p1 !== null) { p.next = p1; } + else if (p2 !== null) { p.next = p2; } + + return head.next; +} + + +function sort(keys, values, left, right, compare) { + if (left >= right) { return; } + + var pivot = keys[(left + right) >> 1]; + var i = left - 1; + var j = right + 1; + + while (true) { + do { i++; } while (compare(keys[i], pivot) < 0); + do { j--; } while (compare(keys[j], pivot) > 0); + if (i >= j) { break; } + + var tmp = keys[i]; + keys[i] = keys[j]; + keys[j] = tmp; + + tmp = values[i]; + values[i] = values[j]; + values[j] = tmp; + } + + sort(keys, values, left, j, compare); + sort(keys, values, j + 1, right, compare); +} + +function createEventQueue(byY) { + var q = new Tree(byY); + + return { + isEmpty: isEmpty, + size: size, + pop: pop, + find: find, + insert: insert + } + + function find(p) { + return q.find(p); + } + + function size() { + return q.size; + } + + function isEmpty() { + return q.isEmpty(); + } + + function insert(event) { + // debugger; + q.add(event.point, event); + } + + function pop() { + var node = q.pop(); + return node && node.data; + } +} + +/** + * Just a collection of geometry related utilities + */ + +// This is used for precision checking (e.g. two numbers are equal +// if their difference is smaller than this number). The value is +// chosen empirically. We still may run into precision related issues. +// TODO: we should allow consumers to configure this. +var EPS = 1e-9;//10; + +function getIntersectionXPoint(segment, xPos, yPos) { + var dy1 = segment.from.y - yPos; + var dy2 = yPos - segment.to.y; + var dy = segment.to.y - segment.from.y; + if (Math.abs(dy1) < EPS) { + // The segment starts on the sweep line + if (Math.abs(dy) < EPS) { + // the segment is horizontal. Intersection is at the point + if (xPos <= segment.from.x) { return segment.from.x; } + if (xPos > segment.to.x) { return segment.to.x; } + return xPos; + } + return segment.from.x; + } + + var dx = (segment.to.x - segment.from.x); + var xOffset; + if (dy1 >= dy2) { + xOffset = dy1 * (dx / dy); + return (segment.from.x - xOffset); + } + xOffset = dy2 * (dx / dy); + return (segment.to.x + xOffset); +} + +function angle(dx, dy) { + // https://stackoverflow.com/questions/16542042/fastest-way-to-sort-vectors-by-angle-without-actually-computing-that-angle + var p = dx/(Math.abs(dx) + Math.abs(dy)); // -1 .. 1 increasing with x + + if (dy < 0) { return p - 1; } // -2 .. 0 increasing with x + return 1 - p // 0 .. 2 decreasing with x +} + +function intersectSegments(a, b) { + // https://stackoverflow.com/a/1968345/125351 + var aStart = a.from, bStart = b.from; + var p0_x = aStart.x, p0_y = aStart.y, + p2_x = bStart.x, p2_y = bStart.y; + + var s1_x = a.dx, s1_y = a.dy, s2_x = b.dx, s2_y = b.dy; + var div = s1_x * s2_y - s2_x * s1_y; + + var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div; + if (s < 0 || s > 1) { return; } + + var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div; + + if (t >= 0 && t <= 1) { + return { + x: p0_x - (t * s1_x), + y: p0_y - (t * s1_y) + } + } +} + +function samePoint(a, b) { + return Math.abs(a.x - b.x) < EPS && Math.abs(a.y - b.y) < EPS; +} + +/** + * Creates a new sweep status data structure. + */ +function createSweepStatus(onError, EPS$$1) { + var lastPointY, prevY; + var lastPointX, prevX; + var useBelow = false; + var status = new Tree(compareSegments); + + // To save on GC we return mutable object. + var currentBoundary = { + beforeLeft: null, + left: null, + right: null, + afterRight: null, + }; + + var currentLeftRight = {left: null, right: null}; + + return { + /** + * Add new segments into the status tree. + */ + insertSegments: insertSegments, + + /** + * Remove segments from the status tree. + */ + deleteSegments: deleteSegments, + + /** + * Returns segments that are to the left and right from a given point. + */ + getLeftRightPoint: getLeftRightPoint, + + /** + * For a given collections of segments finds the most left and the most right + * segments. Also returns segments immediately before left, and after right segments. + */ + getBoundarySegments: getBoundarySegments, + + findSegmentsWithPoint: findSegmentsWithPoint, + + /** + * Current binary search tree with segments + */ + status: status, + + /** + * Introspection method that verifies if there are duplicates in the segment tree. + * If there are - `onError()` is called. + */ + checkDuplicate: checkDuplicate, + + /** + * Prints current segments in order of their intersection with sweep line. Introspection method. + */ + printStatus: printStatus, + + /** + * Returns current position of the sweep line. + */ + getLastPoint: function getLastPoint() { + return {x: lastPointX, y: lastPointY}; + } + } + + function compareSegments(a, b) { + if (a === b) { return 0; } + + var ak = getIntersectionXPoint(a, lastPointX, lastPointY); + var bk = getIntersectionXPoint(b, lastPointX, lastPointY); + + var res = ak - bk; + if (Math.abs(res) >= EPS$$1) { + // We are okay fine. Intersection distance between two segments + // is good to give conclusive answer + return res; + } + + var aIsHorizontal = Math.abs(a.dy) < EPS$$1; + var bIsHorizontal = Math.abs(b.dy) < EPS$$1; + if (aIsHorizontal && bIsHorizontal) { + return b.to.x - a.to.x; + } + // TODO: What if both a and b is horizontal? + // move horizontal to end + if (aIsHorizontal) { + return useBelow ? -1 : 1; + } + + if (bIsHorizontal) { + if (useBelow) { + return (b.from.x >= lastPointX) ? -1 : 1 + } + return -1; + // return useBelow ? 1 : -1; + } + var pa = a.angle; + var pb = b.angle; + if (Math.abs(pa - pb) >= EPS$$1) { + return useBelow ? pa - pb : pb - pa; + } + + var segDist = a.from.y - b.from.y; + if (Math.abs(segDist) >= EPS$$1) { + return -segDist; + } + segDist = a.to.y - b.to.y; + if (Math.abs(segDist) >= EPS$$1) { + // TODO: Is this accurate? + return -segDist; + } + + return 0; + // Could also use: + // var aAngle = Math.atan2(a.from.y - a.to.y, a.from.x - a.to.x); + // var bAngle = Math.atan2(b.from.y - b.to.y, b.from.x - b.to.x); + // return useBelow ? bAngle - aAngle : aAngle - bAngle; + } + + function getBoundarySegments(upper, interior) { + var leftMost, rightMost, i; + var uLength = upper.length; + + if (uLength > 0) { + leftMost = rightMost = upper[0]; + } else { + leftMost = rightMost = interior[0]; + } + + for (i = 1; i < uLength; ++i) { + var s = upper[i]; + var cmp = compareSegments(leftMost, s); + if (cmp > 0) { leftMost = s; } + + cmp = compareSegments(rightMost, s); + if (cmp < 0) { rightMost = s; } + } + + var startFrom = uLength > 0 ? 0 : 1; + for (i = startFrom; i < interior.length; ++i) { + s = interior[i]; + cmp = compareSegments(leftMost, s); + if (cmp > 0) { leftMost = s; } + + cmp = compareSegments(rightMost, s); + if (cmp < 0) { rightMost = s; } + } + + // at this point we have our left/right segments in the status. + // Let's find their prev/next elements and report them back: + var left = status.find(leftMost); + if (!left) { + onError('Left is missing. Precision error?'); + } + + var right = status.find(rightMost); + if (!right) { + onError('Right is missing. Precision error?'); + } + + var beforeLeft = left && status.prev(left); + var afterRight = right && status.next(right); + + while (afterRight && right.key.dy === 0 && afterRight.key.dy === 0) { + // horizontal segments are special :( + afterRight = status.next(afterRight); + } + + currentBoundary.beforeLeft = beforeLeft && beforeLeft.key; + currentBoundary.left = left && left.key; + currentBoundary.right = right && right.key; + currentBoundary.afterRight = afterRight && afterRight.key; + + return currentBoundary; + } + + function getLeftRightPoint(p) { + // We are trying to find left and right segments that are nearest to the + // point p. For this we traverse the binary search tree, and remember + // node with the shortest distance to p. + var lastLeft; + var current = status._root; + var minX = Number.POSITIVE_INFINITY; + while (current) { + var x = getIntersectionXPoint(current.key, p.x, p.y); + var dx = p.x - x; + if (dx >= 0) { + if (dx < minX) { + minX = dx; + lastLeft = current; + current = current.left; + } else { + break; + } + } else { + if (-dx < minX) { + minX = -dx; + lastLeft = current; + current = current.right; + } else { + break; + } + } + } + + currentLeftRight.left = lastLeft && lastLeft.key; + var next = lastLeft && status.next(lastLeft); + currentLeftRight.right = next && next.key; + return currentLeftRight; + + // Conceptually, the code above should be equivalent to the code below; + // The code below is easier to understand, but intuitively, the code above + // should have better performance (as we do not traverse the entire status + // tree) + + // var right, left, x; + // var all = status.keys() + // for (var i = 0; i < all.length; ++i) { + // var segment = all[i]; + // x = getIntersectionXPoint(segment, p.x, p.y); + // if (x > p.x && !right) { + // right = segment; + // break; + // } else if (x < p.x) { + // left = segment; + // } + // } + + // currentLeftRight.left = left; + // currentLeftRight.right = right; + + // return currentLeftRight; + } + + function findSegmentsWithPoint(p, onFound) { + // Option 1. + // var arrResults = []; + // status.forEach(current => { + // var x = getIntersectionXPoint(current.key, p.x, p.y); + // var dx = p.x - x; + // if (Math.abs(dx) < EPS) { + // onFound(current.key); + // // arrResults.push(current.key) + // } + // }); + // return arrResults; + + // Option 2. + + // let current = status._root; + // const Q = []; /* Initialize stack s */ + // let done = false; + // var res = []; + // var breakEarly = false; + + // while (!done) { + // if (current !== null) { + // Q.push(current); + // current = current.left; + // } else { + // if (Q.length !== 0) { + // current = Q.pop(); + + // var x = getIntersectionXPoint(current.key, p.x, p.y); + // var dx = p.x - x; + // if (Math.abs(dx) < EPS) { + // res.push(current.key) + // breakEarly = true; + // } else if (breakEarly) { + // done = true; + // } + + // current = current.right; + // } else done = true; + // } + // } + + // return res; + + // option 3. + var current = status._root; + + while (current) { + var x = getIntersectionXPoint(current.key, p.x, p.y); + var dx = p.x - x; + if (Math.abs(dx) < EPS$$1) { + collectAdjacentNodes(current, p, onFound); + break; + } else if (dx < 0) { + current = current.left; + } else { + current = current.right; + } + } + } + + function collectAdjacentNodes(root, p, onFound) { + onFound(root.key); + goOverPredecessors(root.left, p, onFound); + goOverSuccessors(root.right, p, onFound); + } + + function goOverPredecessors(root, p, res) { + if (!root) { return; } + var x = getIntersectionXPoint(root.key, p.x, p.y); + var dx = p.x - x; + if (Math.abs(dx) < EPS$$1) { + collectAdjacentNodes(root, p, res); + } else { + goOverPredecessors(root.right, p, res); + } + } + + function goOverSuccessors(root, p, res) { + if (!root) { return; } + var x = getIntersectionXPoint(root.key, p.x, p.y); + var dx = p.x - x; + if (Math.abs(dx) < EPS$$1) { + collectAdjacentNodes(root, p, res); + } else { + goOverSuccessors(root.left, p, res); + } + } + + function checkDuplicate() { + var prev; + status.forEach(function (node) { + var current = node.key; + + if (prev) { + if (samePoint(prev.from, current.from) && samePoint(prev.to, current.to)) { + // Likely you have received error before during segment removal. + onError('Duplicate key in the status! This may be caused by Floating Point rounding error'); + } + } + prev = current; + }); + } + + function printStatus(prefix) { + if ( prefix === void 0 ) prefix = ''; + + // eslint-disable-next-line + console.log(prefix, 'status line: ', lastPointX, lastPointY); + status.forEach(function (node) { + var x = getIntersectionXPoint(node.key, lastPointX, lastPointY); + // eslint-disable-next-line + console.log(x + ' ' + node.key.name); + }); + } + + function insertSegments(interior, upper, sweepLinePos) { + lastPointY = sweepLinePos.y; + lastPointX = sweepLinePos.x; + var key; + + for (var i = 0; i < interior.length; ++i) { + key = interior[i]; + status.add(key); + } + for (i = 0; i < upper.length; ++i) { + key = upper[i]; + status.add(key); + } + } + + function deleteSegments(lower, interior, sweepLinePos) { + // I spent most of the time debugging this method. Depending on the + // algorithm state we can run into situation when dynamic keys of the + // `status` tree predict wrong branch, and thus we are not able to find + // the segment that needs to be deleted. If that happens I'm trying to + // use previous point and repeat the process. This may result in + // incorrect state. In that case I report an error. + var i; + var prevCount = status._size; + prevX = lastPointX; + prevY = lastPointY; + lastPointY = sweepLinePos.y; + lastPointX = sweepLinePos.x; + + useBelow = true; + for(i = 0; i < lower.length; ++i) { + removeSegment(lower[i], sweepLinePos); + } + for(i = 0; i < interior.length; ++i) { + removeSegment(interior[i], sweepLinePos); + } + useBelow = false; + + if (status._size !== prevCount - interior.length - lower.length) { + // This can happen when rounding error occurs. You can try scaling your input + onError('Segments were not removed from a tree properly. Precision error?'); + } + } + + function removeSegment(key, sweepLinePos) { + if (status.find(key)) { + status.remove(key); + } else { + lastPointX = prevX; + lastPointY = prevY; + if (status.find(key)) { + status.remove(key); + } + lastPointY = sweepLinePos.y; + lastPointX = sweepLinePos.x; + } + } +} + +/** + * Represents a single event in the sweep-line algorithm + */ +var SweepEvent = function SweepEvent(point, segment) { + this.point = point; + if (segment) { this.from = [segment]; } +}; + +/** + * A point on a line + * + * @typedef {Object} Point + * @property {number} x coordinate + * @property {number} y coordinate + */ + + +/** + * @typedef {Object} Segment + * @property {Point} from start of the segment + * @property {Point} to end of the segment + */ + +/** + * @typedef {function(point : Point, interior : Segment[], lower : Segment[], upper : Segment[])} ReportIntersectionCallback + */ + +/** + * @typedef {Object} ISectOptions + * @property {ReportIntersectionCallback} onFound + */ + + /** + * @typedef {Object} ISectResult + */ + +// We use EMPTY array to avoid pressure on garbage collector. Need to be +// very cautious to not mutate this array. +var EMPTY = []; + +/** + * Finds all intersections among given segments. + * + * The algorithm follows "Computation Geometry, Algorithms and Applications" book + * by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars. + * + * Line is swept top-down + * + * @param {Segment[]} segments + * @param {ISectOptions=} options + * @returns {ISectResult} + */ +function isect(segments, options) { + var results = []; + var reportIntersection = (options && options.onFound) || defaultIntersectionReporter; + + var onError = (options && options.onError) || defaultErrorReporter; + + var eventQueue = createEventQueue(byY); + var sweepStatus = createSweepStatus(onError, EPS); + var lower, interior, lastPoint; + + segments.forEach(addSegment); + + return { + /** + * Find all intersections synchronously. + * + * @returns array of found intersections. + */ + run: run, + + /** + * Performs a single step in the sweep line algorithm + * + * @returns true if there was something to process; False if no more work to do + */ + step: step, + + // Methods below are low level API for fine-grained control. + // Don't use it unless you understand this code thoroughly + + /** + * Add segment into the + */ + addSegment: addSegment, + + /** + * Direct access to event queue. Queue contains segment endpoints and + * pending detected intersections. + */ + eventQueue: eventQueue, + + /** + * Direct access to sweep line status. "Status" holds information about + * all intersected segments. + */ + sweepStatus: sweepStatus, + + /** + * Access to results array. Works only when you use default onFound() handler + */ + results: results + } + + function run() { + while (!eventQueue.isEmpty()) { + var eventPoint = eventQueue.pop(); + if (handleEventPoint(eventPoint)) { + // they decided to stop. + return; + } } + + return results; + } + + function step() { + if (!eventQueue.isEmpty()) { + var eventPoint = eventQueue.pop(); + handleEventPoint(eventPoint); + // Note: we don't check results of `handleEventPoint()` + // assumption is that client controls `step()` and thus they + // know better if they want to stop. + return true; + } + return false; + } + + function handleEventPoint(p) { + lastPoint = p.point; + var upper = p.from || EMPTY; + + lower = interior = undefined; + // TODO: move lower/interior into sweep status method? + + sweepStatus.findSegmentsWithPoint(lastPoint, addLowerOrInterior); + // if (segmentsWithPoint) { + // segmentsWithPoint.forEach() + // } + + if (!lower) { lower = EMPTY; } + if (!interior) { interior = EMPTY; } + + var uLength = upper.length; + var iLength = interior.length; + var lLength = lower.length; + var hasIntersection = uLength + iLength + lLength > 1; + var hasPointIntersection = !hasIntersection && (uLength === 0 && lLength === 0 && iLength > 0); + + if (hasIntersection || hasPointIntersection) { + p.isReported = true; + if (reportIntersection(lastPoint, interior, lower, upper)) { + return true; + } + } + + sweepStatus.deleteSegments(lower, interior, lastPoint); + sweepStatus.insertSegments(interior, upper, lastPoint); + + var sLeft, sRight; + + var hasNoCrossing = (uLength + iLength === 0); + + if (hasNoCrossing) { + var leftRight = sweepStatus.getLeftRightPoint(lastPoint); + sLeft = leftRight.left; + if (!sLeft) { return; } + + sRight = leftRight.right; + if (!sRight) { return; } + + findNewEvent(sLeft, sRight, p); + } else { + var boundarySegments = sweepStatus.getBoundarySegments(upper, interior); + + findNewEvent(boundarySegments.beforeLeft, boundarySegments.left, p); + findNewEvent(boundarySegments.right, boundarySegments.afterRight, p); + } + + return false; + } + + function addLowerOrInterior(s) { + if (samePoint(s.to, lastPoint)) { + if (!lower) { lower = [s]; } + else { lower.push(s); } + } else if (!samePoint(s.from, lastPoint)) { + if (!interior) { interior = [s]; } + else { interior.push(s); } + } + } + + function findNewEvent(left, right, p) { + if (!left || !right) { return; } + + var intersection = intersectSegments(left, right); + if (!intersection) { + return; + } + + var dy = p.point.y - intersection.y; + // TODO: should I add dy to intersection.y? + if (dy < -EPS) { + // this means intersection happened after the sweep line. + // We already processed it. + return; + } + if (Math.abs(dy) < EPS && intersection.x <= p.point.x) { + return; + } + + // Need to adjust floating point for this special case, + // since otherwise it gives rounding errors: + roundNearZero(intersection); + + var current = eventQueue.find(intersection); + + if (current && current.isReported) { + // We already reported this event. No need to add it one more time + // TODO: Is this case even possible? + onError('We already reported this event.'); + return; + } + + if (!current) { + var event = new SweepEvent(intersection); + eventQueue.insert(event); + } + } + + function defaultIntersectionReporter(p, interior, lower, upper) { + results.push({ + point: p, + segments: union(union(interior, lower), upper) + }); + } + + function addSegment(segment) { + var from = segment.from; + var to = segment.to; + + // Small numbers give more precision errors. Rounding them to 0. + roundNearZero(from); + roundNearZero(to); + + var dy = from.y - to.y; + + // Note: dy is much smaller then EPS on purpose. I found that higher + // precision here does less good - getting way more rounding errors. + if (Math.abs(dy) < 1e-5) { + from.y = to.y; + segment.dy = 0; + } + if ((from.y < to.y) || ( + (from.y === to.y) && (from.x > to.x)) + ) { + var temp = from; + from = segment.from = to; + to = segment.to = temp; + } + + // We pre-compute some immutable properties of the segment + // They are used quite often in the tree traversal, and pre-computation + // gives significant boost: + segment.dy = from.y - to.y; + segment.dx = from.x - to.x; + segment.angle = angle(segment.dy, segment.dx); + + var isPoint = segment.dy === segment.dx && segment.dy === 0; + var prev = eventQueue.find(from); + if (prev && !isPoint) { + // this detects identical segments early. Without this check + // the algorithm would break since sweep line has no means to + // detect identical segments. + var prevFrom = prev.data.from; + if (prevFrom) { + for (var i = 0; i < prevFrom.length; ++i) { + var s = prevFrom[i]; + if (samePoint(s.to, to)) { + reportIntersection(s.from, [], s.from, s.to); + reportIntersection(s.to, [], s.from, s.to); + return; + } + } + } + } + + if (!isPoint) { + if (prev) { + if (prev.data.from) { prev.data.from.push(segment); } + else { prev.data.from = [segment]; } + } else { + var e = new SweepEvent(from, segment); + eventQueue.insert(e); + } + var event = new SweepEvent(to); + eventQueue.insert(event); + } else { + var event = new SweepEvent(to); + eventQueue.insert(event); + } + } +} + +function roundNearZero(point) { + if (Math.abs(point.x) < EPS) { point.x = 0; } + if (Math.abs(point.y) < EPS) { point.y = 0; } +} + +function defaultErrorReporter(errorMessage) { + throw new Error(errorMessage); +} + +function union(a, b) { + if (!a) { return b; } + if (!b) { return a; } + + return a.concat(b); +} + +function byY(a, b) { + // decreasing Y + var res = b.y - a.y; + // TODO: This might mess up the status tree. + if (Math.abs(res) < EPS) { + // increasing x. + res = a.x - b.x; + if (Math.abs(res) < EPS) { res = 0; } + } + + return res; +} + +/** + * This is a brute force solution with O(n^2) performance. + * (`n` is number of segments). + * + * Use this when number of lines is low, and number of intersections + * is high. + */ + +function brute(lines, options) { + var results = []; + var reportIntersection = (options && options.onFound) || + defaultIntersectionReporter; + var asyncState; + + return { + /** + * Execute brute force of the segment intersection search + */ + run: run, + /** + * Access to results array. Works only when you use default onFound() handler + */ + results: results, + + /** + * Performs a single step in the brute force algorithm () + */ + step: step + } + + function step() { + if (!asyncState) { + asyncState = { + i: 0 + }; + } + var test = lines[asyncState.i]; + for (var j = asyncState.i + 1; j < lines.length; ++j) { + var other = lines[j]; + var pt = intersectSegments$1(test, other); + if (pt) { + if (reportIntersection(pt, [test, other])) { + return; + } + } + } + asyncState.i += 1; + return asyncState.i < lines.length; + } + + function run() { + for(var i = 0; i < lines.length; ++i) { + var test = lines[i]; + for (var j = i + 1; j < lines.length; ++j) { + var other = lines[j]; + var pt = intersectSegments$1(test, other); + if (pt) { + if (reportIntersection(pt, [test, other])) { + return; + } + } + } + } + return results; + } + + function defaultIntersectionReporter(p, interior) { + results.push({ + point: p, + segments: interior + }); + } +} + +function intersectSegments$1(a, b) { + // https://stackoverflow.com/a/1968345/125351 + var aStart = a.from, bStart = b.from; + var p0_x = aStart.x, p0_y = aStart.y, + p2_x = bStart.x, p2_y = bStart.y; + + var s1_x = a.from.x - a.to.x, s1_y = a.from.y - a.to.y, s2_x = b.from.x - b.to.x, s2_y = b.from.y - b.to.y; + var div = s1_x * s2_y - s2_x * s1_y; + + var s = (s1_y * (p0_x - p2_x) - s1_x * (p0_y - p2_y)) / div; + if (s < 0 || s > 1) { return; } + + var t = (s2_x * (p2_y - p0_y) + s2_y * (p0_x - p2_x)) / div; + + if (t >= 0 && t <= 1) { + return { + x: p0_x - (t * s1_x), + y: p0_y - (t * s1_y) + } + } +} + +export { isect as sweep, brute }; diff --git a/package-lock.json b/package-lock.json index 84b0d72..73a816c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -512,6 +512,12 @@ "delayed-stream": "1.0.0" } }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1351,6 +1357,15 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "jest-worker": { + "version": "23.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-23.2.0.tgz", + "integrity": "sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=", + "dev": true, + "requires": { + "merge-stream": "1.0.1" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1486,6 +1501,15 @@ "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w=", "dev": true }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "2.3.6" + } + }, "micromatch": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", @@ -4425,8 +4449,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true, - "optional": true + "dev": true }, "progress": { "version": "2.0.0", @@ -4488,7 +4511,6 @@ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, - "optional": true, "requires": { "core-util-is": "1.0.2", "inherits": "2.0.3", @@ -4647,6 +4669,18 @@ "resolve": "1.8.1" } }, + "rollup-plugin-uglify": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-uglify/-/rollup-plugin-uglify-6.0.0.tgz", + "integrity": "sha512-XtzZd159QuOaXNvcxyBcbUCSoBsv5YYWK+7ZwUyujSmISst8avRfjWlp7cGu8T2O52OJnpEBvl+D4WLV1k1iQQ==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0", + "jest-worker": "23.2.0", + "serialize-javascript": "1.5.0", + "uglify-js": "3.4.9" + } + }, "rollup-pluginutils": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.3.3.tgz", @@ -4693,6 +4727,12 @@ "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true }, + "serialize-javascript": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", + "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "dev": true + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -4805,7 +4845,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "optional": true, "requires": { "safe-buffer": "5.1.2" } @@ -5044,6 +5083,16 @@ "prelude-ls": "1.1.2" } }, + "uglify-js": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "dev": true, + "requires": { + "commander": "2.17.1", + "source-map": "0.6.1" + } + }, "unicode-length": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/unicode-length/-/unicode-length-1.0.3.tgz", @@ -5075,8 +5124,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true + "dev": true }, "uuid": { "version": "3.3.2", diff --git a/package.json b/package.json index 544c6be..95eb5b6 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "pretest": "rollup -c", "test": "tap test/*.js", - "start": "rm -rf build && rollup -c", + "start": "rm -rf build && rollup -c && rollup -c rollup.config-min.js", "prepublish": "npm run start" }, "dependencies": { @@ -23,6 +23,7 @@ "rollup-plugin-buble": "^0.19.2", "rollup-plugin-commonjs": "^9.1.8", "rollup-plugin-node-resolve": "^3.4.0", + "rollup-plugin-uglify": "^6.0.0", "tap": "^12.0.1" }, "eslintConfig": { diff --git a/rollup.config-min.js b/rollup.config-min.js new file mode 100644 index 0000000..511f08b --- /dev/null +++ b/rollup.config-min.js @@ -0,0 +1,32 @@ +const buble = require('rollup-plugin-buble'); +const cjs = require('rollup-plugin-commonjs'); +const node = require('rollup-plugin-node-resolve'); + +const uglify = require('rollup-plugin-uglify').uglify; + +const version = process.env.VERSION || require('./package.json').version + +const banner = + '/*!\n' + + ' * isect v' + version + '\n' + + ' * (c) 2018 Andrei Kashcha.\n' + + ' * Released under the MIT License.\n' + + ' */' +export default { + input: 'src/index.js', + plugins: [ + node(), + cjs(), + buble(), + uglify() + ], + output: [{ + sourcemap: true, + format: 'umd', + name: 'isect', + file: 'build/isect.min.js', + banner, + plugins: [] + } + ], +} diff --git a/rollup.config.js b/rollup.config.js index b584c93..8974c20 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,6 +2,8 @@ const buble = require('rollup-plugin-buble'); const cjs = require('rollup-plugin-commonjs'); const node = require('rollup-plugin-node-resolve'); +const uglify = require('rollup-plugin-uglify').uglify; + const version = process.env.VERSION || require('./package.json').version const banner = @@ -24,9 +26,17 @@ export default { file: 'build/isect.js', banner }, + { + sourcemap: true, + format: 'umd', + name: 'isect', + file: 'build/isect.min.js', + banner, + plugins: [uglify()] + }, { format: 'es', file: 'build/isect.module.js' } - ], + ], }