mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Update rbush
This commit is contained in:
+89
-43
@@ -11,16 +11,23 @@ function rbush(maxEntries, format) {
|
||||
// jshint newcap: false, validthis: true
|
||||
if (!(this instanceof rbush)) { return new rbush(maxEntries, format); }
|
||||
|
||||
// max entries in a node is 9 by default; min node fill is 40% for best performance
|
||||
this._maxEntries = Math.max(4, maxEntries || 9);
|
||||
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
||||
|
||||
this._initFormat(format);
|
||||
if (format) {
|
||||
this._initFormat(format);
|
||||
}
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
rbush.prototype = {
|
||||
|
||||
all: function () {
|
||||
return this._all(this.data, []);
|
||||
},
|
||||
|
||||
search: function (bbox) {
|
||||
|
||||
var node = this.data,
|
||||
@@ -34,10 +41,19 @@ rbush.prototype = {
|
||||
while (node) {
|
||||
for (i = 0, len = node.children.length; i < len; i++) {
|
||||
child = node.children[i];
|
||||
childBBox = node.leaf ? this._toBBox(child) : child.bbox;
|
||||
childBBox = node.leaf ? this.toBBox(child) : child.bbox;
|
||||
|
||||
if (this._intersects(bbox, childBBox)) {
|
||||
(node.leaf ? result : nodesToSearch).push(child);
|
||||
|
||||
if (node.leaf) {
|
||||
result.push(child);
|
||||
|
||||
} else if (this._contains(bbox, childBBox)) {
|
||||
this._all(child, result);
|
||||
|
||||
} else {
|
||||
nodesToSearch.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +75,6 @@ rbush.prototype = {
|
||||
|
||||
// recursively build the tree with the given data from stratch using OMT algorithm
|
||||
var node = this._build(data.slice(), 0);
|
||||
this._calcBBoxes(node, true);
|
||||
|
||||
if (!this.data.children.length) {
|
||||
// save as is if tree is empty
|
||||
@@ -95,7 +110,7 @@ rbush.prototype = {
|
||||
this.data = {
|
||||
children: [],
|
||||
leaf: true,
|
||||
bbox: this._infinite(),
|
||||
bbox: this._empty(),
|
||||
height: 1
|
||||
};
|
||||
return this;
|
||||
@@ -105,7 +120,7 @@ rbush.prototype = {
|
||||
if (!item) { return this; }
|
||||
|
||||
var node = this.data,
|
||||
bbox = this._toBBox(item),
|
||||
bbox = this.toBBox(item),
|
||||
path = [],
|
||||
indexes = [],
|
||||
i, parent, index, goingUp;
|
||||
@@ -152,6 +167,11 @@ rbush.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
toBBox: function (item) { return item; },
|
||||
|
||||
compareMinX: function (a, b) { return a[0] - b[0]; },
|
||||
compareMinY: function (a, b) { return a[1] - b[1]; },
|
||||
|
||||
toJSON: function () { return this.data; },
|
||||
|
||||
fromJSON: function (data) {
|
||||
@@ -159,17 +179,33 @@ rbush.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
_all: function (node, result) {
|
||||
var nodesToSearch = [];
|
||||
while (node) {
|
||||
if (node.leaf) {
|
||||
result.push.apply(result, node.children);
|
||||
} else {
|
||||
nodesToSearch.push.apply(nodesToSearch, node.children);
|
||||
}
|
||||
node = nodesToSearch.pop();
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
_build: function (items, level, height) {
|
||||
|
||||
var N = items.length,
|
||||
M = this._maxEntries;
|
||||
M = this._maxEntries,
|
||||
node;
|
||||
|
||||
if (N <= M) {
|
||||
return {
|
||||
node = {
|
||||
children: items,
|
||||
leaf: true,
|
||||
height: 1
|
||||
};
|
||||
this._calcBBox(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!level) {
|
||||
@@ -179,19 +215,19 @@ rbush.prototype = {
|
||||
// target number of root entries to maximize storage utilization
|
||||
M = Math.ceil(N / Math.pow(M, height - 1));
|
||||
|
||||
items.sort(this._compareMinX);
|
||||
items.sort(this.compareMinX);
|
||||
}
|
||||
|
||||
// TODO eliminate recursion?
|
||||
|
||||
var node = {
|
||||
node = {
|
||||
children: [],
|
||||
height: height
|
||||
};
|
||||
|
||||
var N1 = Math.ceil(N / M) * Math.ceil(Math.sqrt(M)),
|
||||
N2 = Math.ceil(N / M),
|
||||
compare = level % 2 === 1 ? this._compareMinX : this._compareMinY,
|
||||
compare = level % 2 === 1 ? this.compareMinX : this.compareMinY,
|
||||
i, j, slice, sliceLen, childNode;
|
||||
|
||||
// split the items into M mostly square tiles
|
||||
@@ -205,6 +241,8 @@ rbush.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
this._calcBBox(node);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
@@ -247,7 +285,7 @@ rbush.prototype = {
|
||||
|
||||
_insert: function (item, level, isNode, root) {
|
||||
|
||||
var bbox = isNode ? item.bbox : this._toBBox(item),
|
||||
var bbox = isNode ? item.bbox : this.toBBox(item),
|
||||
insertPath = [];
|
||||
|
||||
// find the best node for accommodating the item, saving all nodes along the path too
|
||||
@@ -290,8 +328,8 @@ rbush.prototype = {
|
||||
newNode.leaf = true;
|
||||
}
|
||||
|
||||
this._calcBBoxes(node);
|
||||
this._calcBBoxes(newNode);
|
||||
this._calcBBox(node);
|
||||
this._calcBBox(newNode);
|
||||
|
||||
if (level) {
|
||||
insertPath[level - 1].children.push(newNode);
|
||||
@@ -305,7 +343,7 @@ rbush.prototype = {
|
||||
this.data = {};
|
||||
this.data.children = [node, newNode];
|
||||
this.data.height = node.height + 1;
|
||||
this._calcBBoxes(this.data);
|
||||
this._calcBBox(this.data);
|
||||
},
|
||||
|
||||
_chooseSplitIndex: function (node, m, M) {
|
||||
@@ -343,8 +381,8 @@ rbush.prototype = {
|
||||
// sorts node children by the best axis for split
|
||||
_chooseSplitAxis: function (node, m, M) {
|
||||
|
||||
var compareMinX = node.leaf ? this._compareMinX : this._compareNodeMinX,
|
||||
compareMinY = node.leaf ? this._compareMinY : this._compareNodeMinY,
|
||||
var compareMinX = node.leaf ? this.compareMinX : this._compareNodeMinX,
|
||||
compareMinY = node.leaf ? this.compareMinY : this._compareNodeMinY,
|
||||
xMargin = this._allDistMargin(node, m, M, compareMinX),
|
||||
yMargin = this._allDistMargin(node, m, M, compareMinY);
|
||||
|
||||
@@ -368,13 +406,13 @@ rbush.prototype = {
|
||||
|
||||
for (i = m; i < M - m; i++) {
|
||||
child = node.children[i];
|
||||
this._extend(leftBBox, node.leaf ? this._toBBox(child) : child.bbox);
|
||||
this._extend(leftBBox, node.leaf ? this.toBBox(child) : child.bbox);
|
||||
margin += this._margin(leftBBox);
|
||||
}
|
||||
|
||||
for (i = M - m - 1; i >= 0; i--) {
|
||||
child = node.children[i];
|
||||
this._extend(rightBBox, node.leaf ? this._toBBox(child) : child.bbox);
|
||||
this._extend(rightBBox, node.leaf ? this.toBBox(child) : child.bbox);
|
||||
margin += this._margin(rightBBox);
|
||||
}
|
||||
|
||||
@@ -383,31 +421,23 @@ rbush.prototype = {
|
||||
|
||||
// min bounding rectangle of node children from k to p-1
|
||||
_distBBox: function (node, k, p) {
|
||||
var bbox = this._infinite();
|
||||
var bbox = this._empty();
|
||||
|
||||
for (var i = k, child; i < p; i++) {
|
||||
child = node.children[i];
|
||||
this._extend(bbox, node.leaf ? this._toBBox(child) : child.bbox);
|
||||
this._extend(bbox, node.leaf ? this.toBBox(child) : child.bbox);
|
||||
}
|
||||
|
||||
return bbox;
|
||||
},
|
||||
|
||||
_calcBBoxes: function (node, recursive) {
|
||||
// TODO eliminate recursion
|
||||
node.bbox = this._infinite();
|
||||
// calculate node's bbox from bboxes of its children
|
||||
_calcBBox: function (node) {
|
||||
node.bbox = this._empty();
|
||||
|
||||
for (var i = 0, len = node.children.length, child; i < len; i++) {
|
||||
child = node.children[i];
|
||||
|
||||
if (node.leaf) {
|
||||
this._extend(node.bbox, this._toBBox(child));
|
||||
} else {
|
||||
if (recursive) {
|
||||
this._calcBBoxes(child, recursive);
|
||||
}
|
||||
this._extend(node.bbox, child.bbox);
|
||||
}
|
||||
this._extend(node.bbox, node.leaf ? this.toBBox(child) : child.bbox);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -421,15 +451,26 @@ rbush.prototype = {
|
||||
_condense: function (path) {
|
||||
// go through the path, removing empty nodes and updating bboxes
|
||||
for (var i = path.length - 1, parent; i >= 0; i--) {
|
||||
if (i > 0 && path[i].children.length === 0) {
|
||||
parent = path[i - 1].children;
|
||||
parent.splice(parent.indexOf(path[i]), 1);
|
||||
if (path[i].children.length === 0) {
|
||||
if (i > 0) {
|
||||
parent = path[i - 1].children;
|
||||
parent.splice(parent.indexOf(path[i]), 1);
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
} else {
|
||||
this._calcBBoxes(path[i]);
|
||||
this._calcBBox(path[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_contains: function(a, b) {
|
||||
return a[0] <= b[0] &&
|
||||
a[1] <= b[1] &&
|
||||
b[2] <= a[2] &&
|
||||
b[3] <= a[3];
|
||||
},
|
||||
|
||||
_intersects: function (a, b) {
|
||||
return b[0] <= a[2] &&
|
||||
b[1] <= a[3] &&
|
||||
@@ -463,14 +504,13 @@ rbush.prototype = {
|
||||
Math.max(0, maxY - minY);
|
||||
},
|
||||
|
||||
_infinite: function () { return [Infinity, Infinity, -Infinity, -Infinity]; },
|
||||
_empty: function () { return [Infinity, Infinity, -Infinity, -Infinity]; },
|
||||
|
||||
_compareNodeMinX: function (a, b) { return a.bbox[0] - b.bbox[0]; },
|
||||
_compareNodeMinY: function (a, b) { return a.bbox[1] - b.bbox[1]; },
|
||||
|
||||
_initFormat: function (format) {
|
||||
// data format (minX, minY, maxX, maxY accessors)
|
||||
format = format || ['[0]', '[1]', '[2]', '[3]'];
|
||||
|
||||
// uses eval-type function compilation instead of just accepting a toBBox function
|
||||
// because the algorithms are very sensitive to sorting functions performance,
|
||||
@@ -480,15 +520,21 @@ rbush.prototype = {
|
||||
|
||||
var compareArr = ['return a', ' - b', ';'];
|
||||
|
||||
this._compareMinX = new Function('a', 'b', compareArr.join(format[0]));
|
||||
this._compareMinY = new Function('a', 'b', compareArr.join(format[1]));
|
||||
this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
|
||||
this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
|
||||
|
||||
this._toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
|
||||
this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(function() {
|
||||
return rbush;
|
||||
});
|
||||
} else if (typeof module !== 'undefined') {
|
||||
module.exports = rbush;
|
||||
} else if (typeof self !== 'undefined') {
|
||||
self.rbush = rbush;
|
||||
} else {
|
||||
window.rbush = rbush;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user