You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
435 lines
12 KiB
435 lines
12 KiB
// https://d3js.org/d3-quadtree/ v1.0.5 Copyright 2018 Mike Bostock |
|
(function (global, factory) { |
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : |
|
typeof define === 'function' && define.amd ? define(['exports'], factory) : |
|
(factory((global.d3 = global.d3 || {}))); |
|
}(this, (function (exports) { 'use strict'; |
|
|
|
function tree_add(d) { |
|
var x = +this._x.call(null, d), |
|
y = +this._y.call(null, d); |
|
return add(this.cover(x, y), x, y, d); |
|
} |
|
|
|
function add(tree, x, y, d) { |
|
if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points |
|
|
|
var parent, |
|
node = tree._root, |
|
leaf = {data: d}, |
|
x0 = tree._x0, |
|
y0 = tree._y0, |
|
x1 = tree._x1, |
|
y1 = tree._y1, |
|
xm, |
|
ym, |
|
xp, |
|
yp, |
|
right, |
|
bottom, |
|
i, |
|
j; |
|
|
|
// If the tree is empty, initialize the root as a leaf. |
|
if (!node) return tree._root = leaf, tree; |
|
|
|
// Find the existing leaf for the new point, or add it. |
|
while (node.length) { |
|
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; |
|
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; |
|
if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree; |
|
} |
|
|
|
// Is the new point is exactly coincident with the existing point? |
|
xp = +tree._x.call(null, node.data); |
|
yp = +tree._y.call(null, node.data); |
|
if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree; |
|
|
|
// Otherwise, split the leaf node until the old and new point are separated. |
|
do { |
|
parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4); |
|
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; |
|
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; |
|
} while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm))); |
|
return parent[j] = node, parent[i] = leaf, tree; |
|
} |
|
|
|
function addAll(data) { |
|
var d, i, n = data.length, |
|
x, |
|
y, |
|
xz = new Array(n), |
|
yz = new Array(n), |
|
x0 = Infinity, |
|
y0 = Infinity, |
|
x1 = -Infinity, |
|
y1 = -Infinity; |
|
|
|
// Compute the points and their extent. |
|
for (i = 0; i < n; ++i) { |
|
if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue; |
|
xz[i] = x; |
|
yz[i] = y; |
|
if (x < x0) x0 = x; |
|
if (x > x1) x1 = x; |
|
if (y < y0) y0 = y; |
|
if (y > y1) y1 = y; |
|
} |
|
|
|
// If there were no (valid) points, inherit the existing extent. |
|
if (x1 < x0) x0 = this._x0, x1 = this._x1; |
|
if (y1 < y0) y0 = this._y0, y1 = this._y1; |
|
|
|
// Expand the tree to cover the new points. |
|
this.cover(x0, y0).cover(x1, y1); |
|
|
|
// Add the new points. |
|
for (i = 0; i < n; ++i) { |
|
add(this, xz[i], yz[i], data[i]); |
|
} |
|
|
|
return this; |
|
} |
|
|
|
function tree_cover(x, y) { |
|
if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points |
|
|
|
var x0 = this._x0, |
|
y0 = this._y0, |
|
x1 = this._x1, |
|
y1 = this._y1; |
|
|
|
// If the quadtree has no extent, initialize them. |
|
// Integer extent are necessary so that if we later double the extent, |
|
// the existing quadrant boundaries don’t change due to floating point error! |
|
if (isNaN(x0)) { |
|
x1 = (x0 = Math.floor(x)) + 1; |
|
y1 = (y0 = Math.floor(y)) + 1; |
|
} |
|
|
|
// Otherwise, double repeatedly to cover. |
|
else if (x0 > x || x > x1 || y0 > y || y > y1) { |
|
var z = x1 - x0, |
|
node = this._root, |
|
parent, |
|
i; |
|
|
|
switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) { |
|
case 0: { |
|
do parent = new Array(4), parent[i] = node, node = parent; |
|
while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1); |
|
break; |
|
} |
|
case 1: { |
|
do parent = new Array(4), parent[i] = node, node = parent; |
|
while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1); |
|
break; |
|
} |
|
case 2: { |
|
do parent = new Array(4), parent[i] = node, node = parent; |
|
while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y); |
|
break; |
|
} |
|
case 3: { |
|
do parent = new Array(4), parent[i] = node, node = parent; |
|
while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y); |
|
break; |
|
} |
|
} |
|
|
|
if (this._root && this._root.length) this._root = node; |
|
} |
|
|
|
// If the quadtree covers the point already, just return. |
|
else return this; |
|
|
|
this._x0 = x0; |
|
this._y0 = y0; |
|
this._x1 = x1; |
|
this._y1 = y1; |
|
return this; |
|
} |
|
|
|
function tree_data() { |
|
var data = []; |
|
this.visit(function(node) { |
|
if (!node.length) do data.push(node.data); while (node = node.next) |
|
}); |
|
return data; |
|
} |
|
|
|
function tree_extent(_) { |
|
return arguments.length |
|
? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1]) |
|
: isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]]; |
|
} |
|
|
|
function Quad(node, x0, y0, x1, y1) { |
|
this.node = node; |
|
this.x0 = x0; |
|
this.y0 = y0; |
|
this.x1 = x1; |
|
this.y1 = y1; |
|
} |
|
|
|
function tree_find(x, y, radius) { |
|
var data, |
|
x0 = this._x0, |
|
y0 = this._y0, |
|
x1, |
|
y1, |
|
x2, |
|
y2, |
|
x3 = this._x1, |
|
y3 = this._y1, |
|
quads = [], |
|
node = this._root, |
|
q, |
|
i; |
|
|
|
if (node) quads.push(new Quad(node, x0, y0, x3, y3)); |
|
if (radius == null) radius = Infinity; |
|
else { |
|
x0 = x - radius, y0 = y - radius; |
|
x3 = x + radius, y3 = y + radius; |
|
radius *= radius; |
|
} |
|
|
|
while (q = quads.pop()) { |
|
|
|
// Stop searching if this quadrant can’t contain a closer node. |
|
if (!(node = q.node) |
|
|| (x1 = q.x0) > x3 |
|
|| (y1 = q.y0) > y3 |
|
|| (x2 = q.x1) < x0 |
|
|| (y2 = q.y1) < y0) continue; |
|
|
|
// Bisect the current quadrant. |
|
if (node.length) { |
|
var xm = (x1 + x2) / 2, |
|
ym = (y1 + y2) / 2; |
|
|
|
quads.push( |
|
new Quad(node[3], xm, ym, x2, y2), |
|
new Quad(node[2], x1, ym, xm, y2), |
|
new Quad(node[1], xm, y1, x2, ym), |
|
new Quad(node[0], x1, y1, xm, ym) |
|
); |
|
|
|
// Visit the closest quadrant first. |
|
if (i = (y >= ym) << 1 | (x >= xm)) { |
|
q = quads[quads.length - 1]; |
|
quads[quads.length - 1] = quads[quads.length - 1 - i]; |
|
quads[quads.length - 1 - i] = q; |
|
} |
|
} |
|
|
|
// Visit this point. (Visiting coincident points isn’t necessary!) |
|
else { |
|
var dx = x - +this._x.call(null, node.data), |
|
dy = y - +this._y.call(null, node.data), |
|
d2 = dx * dx + dy * dy; |
|
if (d2 < radius) { |
|
var d = Math.sqrt(radius = d2); |
|
x0 = x - d, y0 = y - d; |
|
x3 = x + d, y3 = y + d; |
|
data = node.data; |
|
} |
|
} |
|
} |
|
|
|
return data; |
|
} |
|
|
|
function tree_remove(d) { |
|
if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points |
|
|
|
var parent, |
|
node = this._root, |
|
retainer, |
|
previous, |
|
next, |
|
x0 = this._x0, |
|
y0 = this._y0, |
|
x1 = this._x1, |
|
y1 = this._y1, |
|
x, |
|
y, |
|
xm, |
|
ym, |
|
right, |
|
bottom, |
|
i, |
|
j; |
|
|
|
// If the tree is empty, initialize the root as a leaf. |
|
if (!node) return this; |
|
|
|
// Find the leaf node for the point. |
|
// While descending, also retain the deepest parent with a non-removed sibling. |
|
if (node.length) while (true) { |
|
if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm; |
|
if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym; |
|
if (!(parent = node, node = node[i = bottom << 1 | right])) return this; |
|
if (!node.length) break; |
|
if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i; |
|
} |
|
|
|
// Find the point to remove. |
|
while (node.data !== d) if (!(previous = node, node = node.next)) return this; |
|
if (next = node.next) delete node.next; |
|
|
|
// If there are multiple coincident points, remove just the point. |
|
if (previous) return (next ? previous.next = next : delete previous.next), this; |
|
|
|
// If this is the root point, remove it. |
|
if (!parent) return this._root = next, this; |
|
|
|
// Remove this leaf. |
|
next ? parent[i] = next : delete parent[i]; |
|
|
|
// If the parent now contains exactly one leaf, collapse superfluous parents. |
|
if ((node = parent[0] || parent[1] || parent[2] || parent[3]) |
|
&& node === (parent[3] || parent[2] || parent[1] || parent[0]) |
|
&& !node.length) { |
|
if (retainer) retainer[j] = node; |
|
else this._root = node; |
|
} |
|
|
|
return this; |
|
} |
|
|
|
function removeAll(data) { |
|
for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]); |
|
return this; |
|
} |
|
|
|
function tree_root() { |
|
return this._root; |
|
} |
|
|
|
function tree_size() { |
|
var size = 0; |
|
this.visit(function(node) { |
|
if (!node.length) do ++size; while (node = node.next) |
|
}); |
|
return size; |
|
} |
|
|
|
function tree_visit(callback) { |
|
var quads = [], q, node = this._root, child, x0, y0, x1, y1; |
|
if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1)); |
|
while (q = quads.pop()) { |
|
if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) { |
|
var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; |
|
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); |
|
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); |
|
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); |
|
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
function tree_visitAfter(callback) { |
|
var quads = [], next = [], q; |
|
if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1)); |
|
while (q = quads.pop()) { |
|
var node = q.node; |
|
if (node.length) { |
|
var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2; |
|
if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym)); |
|
if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym)); |
|
if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1)); |
|
if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1)); |
|
} |
|
next.push(q); |
|
} |
|
while (q = next.pop()) { |
|
callback(q.node, q.x0, q.y0, q.x1, q.y1); |
|
} |
|
return this; |
|
} |
|
|
|
function defaultX(d) { |
|
return d[0]; |
|
} |
|
|
|
function tree_x(_) { |
|
return arguments.length ? (this._x = _, this) : this._x; |
|
} |
|
|
|
function defaultY(d) { |
|
return d[1]; |
|
} |
|
|
|
function tree_y(_) { |
|
return arguments.length ? (this._y = _, this) : this._y; |
|
} |
|
|
|
function quadtree(nodes, x, y) { |
|
var tree = new Quadtree(x == null ? defaultX : x, y == null ? defaultY : y, NaN, NaN, NaN, NaN); |
|
return nodes == null ? tree : tree.addAll(nodes); |
|
} |
|
|
|
function Quadtree(x, y, x0, y0, x1, y1) { |
|
this._x = x; |
|
this._y = y; |
|
this._x0 = x0; |
|
this._y0 = y0; |
|
this._x1 = x1; |
|
this._y1 = y1; |
|
this._root = undefined; |
|
} |
|
|
|
function leaf_copy(leaf) { |
|
var copy = {data: leaf.data}, next = copy; |
|
while (leaf = leaf.next) next = next.next = {data: leaf.data}; |
|
return copy; |
|
} |
|
|
|
var treeProto = quadtree.prototype = Quadtree.prototype; |
|
|
|
treeProto.copy = function() { |
|
var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1), |
|
node = this._root, |
|
nodes, |
|
child; |
|
|
|
if (!node) return copy; |
|
|
|
if (!node.length) return copy._root = leaf_copy(node), copy; |
|
|
|
nodes = [{source: node, target: copy._root = new Array(4)}]; |
|
while (node = nodes.pop()) { |
|
for (var i = 0; i < 4; ++i) { |
|
if (child = node.source[i]) { |
|
if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)}); |
|
else node.target[i] = leaf_copy(child); |
|
} |
|
} |
|
} |
|
|
|
return copy; |
|
}; |
|
|
|
treeProto.add = tree_add; |
|
treeProto.addAll = addAll; |
|
treeProto.cover = tree_cover; |
|
treeProto.data = tree_data; |
|
treeProto.extent = tree_extent; |
|
treeProto.find = tree_find; |
|
treeProto.remove = tree_remove; |
|
treeProto.removeAll = removeAll; |
|
treeProto.root = tree_root; |
|
treeProto.size = tree_size; |
|
treeProto.visit = tree_visit; |
|
treeProto.visitAfter = tree_visitAfter; |
|
treeProto.x = tree_x; |
|
treeProto.y = tree_y; |
|
|
|
exports.quadtree = quadtree; |
|
|
|
Object.defineProperty(exports, '__esModule', { value: true }); |
|
|
|
})));
|
|
|