|
1 // Constructs a new quadtree for the specified array of points. A quadtree is a |
|
2 // two-dimensional recursive spatial subdivision. This implementation uses |
|
3 // square partitions, dividing each square into four equally-sized squares. Each |
|
4 // point exists in a unique node; if multiple points are in the same position, |
|
5 // some points may be stored on internal nodes rather than leaf nodes. Quadtrees |
|
6 // can be used to accelerate various spatial operations, such as the Barnes-Hut |
|
7 // approximation for computing n-body forces, or collision detection. |
|
8 d3.geom.quadtree = function(points, x1, y1, x2, y2) { |
|
9 var p, |
|
10 i = -1, |
|
11 n = points.length; |
|
12 |
|
13 // Type conversion for deprecated API. |
|
14 if (n && isNaN(points[0].x)) points = points.map(d3_geom_quadtreePoint); |
|
15 |
|
16 // Allow bounds to be specified explicitly. |
|
17 if (arguments.length < 5) { |
|
18 if (arguments.length === 3) { |
|
19 y2 = x2 = y1; |
|
20 y1 = x1; |
|
21 } else { |
|
22 x1 = y1 = Infinity; |
|
23 x2 = y2 = -Infinity; |
|
24 |
|
25 // Compute bounds. |
|
26 while (++i < n) { |
|
27 p = points[i]; |
|
28 if (p.x < x1) x1 = p.x; |
|
29 if (p.y < y1) y1 = p.y; |
|
30 if (p.x > x2) x2 = p.x; |
|
31 if (p.y > y2) y2 = p.y; |
|
32 } |
|
33 |
|
34 // Squarify the bounds. |
|
35 var dx = x2 - x1, |
|
36 dy = y2 - y1; |
|
37 if (dx > dy) y2 = y1 + dx; |
|
38 else x2 = x1 + dy; |
|
39 } |
|
40 } |
|
41 |
|
42 // Recursively inserts the specified point p at the node n or one of its |
|
43 // descendants. The bounds are defined by [x1, x2] and [y1, y2]. |
|
44 function insert(n, p, x1, y1, x2, y2) { |
|
45 if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid points |
|
46 if (n.leaf) { |
|
47 var v = n.point; |
|
48 if (v) { |
|
49 // If the point at this leaf node is at the same position as the new |
|
50 // point we are adding, we leave the point associated with the |
|
51 // internal node while adding the new point to a child node. This |
|
52 // avoids infinite recursion. |
|
53 if ((Math.abs(v.x - p.x) + Math.abs(v.y - p.y)) < .01) { |
|
54 insertChild(n, p, x1, y1, x2, y2); |
|
55 } else { |
|
56 n.point = null; |
|
57 insertChild(n, v, x1, y1, x2, y2); |
|
58 insertChild(n, p, x1, y1, x2, y2); |
|
59 } |
|
60 } else { |
|
61 n.point = p; |
|
62 } |
|
63 } else { |
|
64 insertChild(n, p, x1, y1, x2, y2); |
|
65 } |
|
66 } |
|
67 |
|
68 // Recursively inserts the specified point p into a descendant of node n. The |
|
69 // bounds are defined by [x1, x2] and [y1, y2]. |
|
70 function insertChild(n, p, x1, y1, x2, y2) { |
|
71 // Compute the split point, and the quadrant in which to insert p. |
|
72 var sx = (x1 + x2) * .5, |
|
73 sy = (y1 + y2) * .5, |
|
74 right = p.x >= sx, |
|
75 bottom = p.y >= sy, |
|
76 i = (bottom << 1) + right; |
|
77 |
|
78 // Recursively insert into the child node. |
|
79 n.leaf = false; |
|
80 n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); |
|
81 |
|
82 // Update the bounds as we recurse. |
|
83 if (right) x1 = sx; else x2 = sx; |
|
84 if (bottom) y1 = sy; else y2 = sy; |
|
85 insert(n, p, x1, y1, x2, y2); |
|
86 } |
|
87 |
|
88 // Create the root node. |
|
89 var root = d3_geom_quadtreeNode(); |
|
90 |
|
91 root.add = function(p) { |
|
92 insert(root, p, x1, y1, x2, y2); |
|
93 }; |
|
94 |
|
95 root.visit = function(f) { |
|
96 d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2); |
|
97 }; |
|
98 |
|
99 // Insert all points. |
|
100 points.forEach(root.add); |
|
101 return root; |
|
102 }; |
|
103 |
|
104 function d3_geom_quadtreeNode() { |
|
105 return { |
|
106 leaf: true, |
|
107 nodes: [], |
|
108 point: null |
|
109 }; |
|
110 } |
|
111 |
|
112 function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { |
|
113 if (!f(node, x1, y1, x2, y2)) { |
|
114 var sx = (x1 + x2) * .5, |
|
115 sy = (y1 + y2) * .5, |
|
116 children = node.nodes; |
|
117 if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); |
|
118 if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); |
|
119 if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); |
|
120 if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); |
|
121 } |
|
122 } |
|
123 |
|
124 function d3_geom_quadtreePoint(p) { |
|
125 return { |
|
126 x: p[0], |
|
127 y: p[1] |
|
128 }; |
|
129 } |