From 9a1b7628ba247c6b976198826fe3f64aa5bfaf98 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 11 Feb 2016 21:23:52 -0500 Subject: [PATCH] Bump rbush to 1.4.2 --- js/lib/rbush.js | 153 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 110 insertions(+), 43 deletions(-) diff --git a/js/lib/rbush.js b/js/lib/rbush.js index 19b841a8a..f6975dfe8 100644 --- a/js/lib/rbush.js +++ b/js/lib/rbush.js @@ -1,10 +1,11 @@ /* - (c) 2013, Vladimir Agafonkin + (c) 2015, Vladimir Agafonkin RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles. https://github.com/mourner/rbush */ -(function () { 'use strict'; +(function () { +'use strict'; function rbush(maxEntries, format) { @@ -57,6 +58,33 @@ rbush.prototype = { return result; }, + collides: function (bbox) { + + var node = this.data, + toBBox = this.toBBox; + + if (!intersects(bbox, node.bbox)) return false; + + var nodesToSearch = [], + i, len, child, childBBox; + + while (node) { + for (i = 0, len = node.children.length; i < len; i++) { + + child = node.children[i]; + childBBox = node.leaf ? toBBox(child) : child.bbox; + + if (intersects(bbox, childBBox)) { + if (node.leaf || contains(bbox, childBBox)) return true; + nodesToSearch.push(child); + } + } + node = nodesToSearch.pop(); + } + + return false; + }, + load: function (data) { if (!(data && data.length)) return this; @@ -180,13 +208,14 @@ rbush.prototype = { return result; }, - _build: function (items, left, right, level, height) { + _build: function (items, left, right, height) { var N = right - left + 1, M = this._maxEntries, node; if (N <= M) { + // reached leaf level; return leaf node = { children: items.slice(left, right + 1), height: 1, @@ -197,7 +226,7 @@ rbush.prototype = { return node; } - if (!level) { + if (!height) { // target height of the bulk-loaded tree height = Math.ceil(Math.log(N) / Math.log(M)); @@ -205,31 +234,33 @@ rbush.prototype = { M = Math.ceil(N / Math.pow(M, height - 1)); } - // TODO eliminate recursion? - node = { children: [], height: height, - bbox: null + bbox: null, + leaf: false }; + // split the items into M mostly square tiles + var N2 = Math.ceil(N / M), N1 = N2 * Math.ceil(Math.sqrt(M)), - i, j, right2, childNode; + i, j, right2, right3; + + multiSelect(items, left, right, N1, this.compareMinX); - // split the items into M mostly square tiles for (i = left; i <= right; i += N1) { - if (i + N1 <= right) partitionSort(items, i, right, i + N1, this.compareMinX); right2 = Math.min(i + N1 - 1, right); + multiSelect(items, i, right2, N2, this.compareMinY); + for (j = i; j <= right2; j += N2) { - if (j + N2 <= right2) partitionSort(items, j, right2, j + N2, this.compareMinY); + right3 = Math.min(j + N2 - 1, right2); // pack each entry recursively - childNode = this._build(items, j, Math.min(j + N2 - 1, right2), level + 1, height - 1); - node.children.push(childNode); + node.children.push(this._build(items, j, right3, height - 1)); } } @@ -309,9 +340,13 @@ rbush.prototype = { this._chooseSplitAxis(node, m, M); + var splitIndex = this._chooseSplitIndex(node, m, M); + var newNode = { - children: node.children.splice(this._chooseSplitIndex(node, m, M)), - height: node.height + children: node.children.splice(splitIndex, node.children.length - splitIndex), + height: node.height, + bbox: null, + leaf: false }; if (node.leaf) newNode.leaf = true; @@ -327,7 +362,9 @@ rbush.prototype = { // split root node this.data = { children: [node, newNode], - height: node.height + 1 + height: node.height + 1, + bbox: null, + leaf: false }; calcBBox(this.data, this.toBBox); }, @@ -442,6 +479,7 @@ rbush.prototype = { } }; + // calculate node's bbox from bboxes of its children function calcBBox(node, toBBox) { node.bbox = distBBox(node, 0, node.children.length, toBBox); @@ -459,7 +497,6 @@ function distBBox(node, k, p, toBBox) { return bbox; } - function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; } function extend(a, b) { @@ -481,7 +518,7 @@ function enlargedArea(a, b) { (Math.max(b[3], a[3]) - Math.min(b[1], a[1])); } -function intersectionArea (a, b) { +function intersectionArea(a, b) { var minX = Math.max(a[0], b[0]), minY = Math.max(a[1], b[1]), maxX = Math.min(a[2], b[2]), @@ -498,44 +535,74 @@ function contains(a, b) { b[3] <= a[3]; } -function intersects (a, b) { +function intersects(a, b) { return b[0] <= a[2] && b[1] <= a[3] && b[2] >= a[0] && b[3] >= a[1]; } +// sort an array so that items come in groups of n unsorted items, with groups sorted between each other; +// combines selection algorithm with binary divide & conquer approach -function partitionSort(arr, left, right, k, compare) { - var pivot; +function multiSelect(arr, left, right, n, compare) { + var stack = [left, right], + mid; - while (true) { - pivot = Math.floor((left + right) / 2); - pivot = partition(arr, left, right, pivot, compare); + while (stack.length) { + right = stack.pop(); + left = stack.pop(); - if (k === pivot) break; - else if (k < pivot) right = pivot - 1; - else left = pivot + 1; + if (right - left <= n) continue; + + mid = left + Math.ceil((right - left) / n / 2) * n; + select(arr, left, right, mid, compare); + + stack.push(left, mid, mid, right); } - - partition(arr, left, right, k, compare); } -function partition(arr, left, right, pivot, compare) { - var k = left, - value = arr[pivot]; +// Floyd-Rivest selection algorithm: +// sort an array between left and right (inclusive) so that the smallest k elements come first (unordered) +function select(arr, left, right, k, compare) { + var n, i, z, s, sd, newLeft, newRight, t, j; - swap(arr, pivot, right); - - for (var i = left; i < right; i++) { - if (compare(arr[i], value) < 0) { - swap(arr, k, i); - k++; + while (right > left) { + if (right - left > 600) { + n = right - left + 1; + i = k - left + 1; + z = Math.log(n); + s = 0.5 * Math.exp(2 * z / 3); + sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1); + newLeft = Math.max(left, Math.floor(k - i * s / n + sd)); + newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd)); + select(arr, newLeft, newRight, k, compare); } - } - swap(arr, right, k); - return k; + t = arr[k]; + i = left; + j = right; + + swap(arr, left, k); + if (compare(arr[right], t) > 0) swap(arr, left, right); + + while (i < j) { + swap(arr, i, j); + i++; + j--; + while (compare(arr[i], t) < 0) i++; + while (compare(arr[j], t) > 0) j--; + } + + if (compare(arr[left], t) === 0) swap(arr, left, j); + else { + j++; + swap(arr, j, right); + } + + if (j <= k) left = j + 1; + if (k <= j) right = j - 1; + } } function swap(arr, i, j) { @@ -546,9 +613,9 @@ function swap(arr, i, j) { // export as AMD/CommonJS module or global variable -if (typeof define === 'function' && define.amd) define(function() { return rbush; }); +if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; }); else if (typeof module !== 'undefined') module.exports = rbush; else if (typeof self !== 'undefined') self.rbush = rbush; else window.rbush = rbush; -})(); \ No newline at end of file +})();