toolkit/javascript/d3/src/geom/quadtree.js
changeset 47 c0b4a8b5a012
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolkit/javascript/d3/src/geom/quadtree.js	Thu Apr 10 14:20:23 2014 +0200
@@ -0,0 +1,129 @@
+// Constructs a new quadtree for the specified array of points. A quadtree is a
+// two-dimensional recursive spatial subdivision. This implementation uses
+// square partitions, dividing each square into four equally-sized squares. Each
+// point exists in a unique node; if multiple points are in the same position,
+// some points may be stored on internal nodes rather than leaf nodes. Quadtrees
+// can be used to accelerate various spatial operations, such as the Barnes-Hut
+// approximation for computing n-body forces, or collision detection.
+d3.geom.quadtree = function(points, x1, y1, x2, y2) {
+  var p,
+      i = -1,
+      n = points.length;
+
+  // Type conversion for deprecated API.
+  if (n && isNaN(points[0].x)) points = points.map(d3_geom_quadtreePoint);
+
+  // Allow bounds to be specified explicitly.
+  if (arguments.length < 5) {
+    if (arguments.length === 3) {
+      y2 = x2 = y1;
+      y1 = x1;
+    } else {
+      x1 = y1 = Infinity;
+      x2 = y2 = -Infinity;
+
+      // Compute bounds.
+      while (++i < n) {
+        p = points[i];
+        if (p.x < x1) x1 = p.x;
+        if (p.y < y1) y1 = p.y;
+        if (p.x > x2) x2 = p.x;
+        if (p.y > y2) y2 = p.y;
+      }
+
+      // Squarify the bounds.
+      var dx = x2 - x1,
+          dy = y2 - y1;
+      if (dx > dy) y2 = y1 + dx;
+      else x2 = x1 + dy;
+    }
+  }
+
+  // Recursively inserts the specified point p at the node n or one of its
+  // descendants. The bounds are defined by [x1, x2] and [y1, y2].
+  function insert(n, p, x1, y1, x2, y2) {
+    if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid points
+    if (n.leaf) {
+      var v = n.point;
+      if (v) {
+        // If the point at this leaf node is at the same position as the new
+        // point we are adding, we leave the point associated with the
+        // internal node while adding the new point to a child node. This
+        // avoids infinite recursion.
+        if ((Math.abs(v.x - p.x) + Math.abs(v.y - p.y)) < .01) {
+          insertChild(n, p, x1, y1, x2, y2);
+        } else {
+          n.point = null;
+          insertChild(n, v, x1, y1, x2, y2);
+          insertChild(n, p, x1, y1, x2, y2);
+        }
+      } else {
+        n.point = p;
+      }
+    } else {
+      insertChild(n, p, x1, y1, x2, y2);
+    }
+  }
+
+  // Recursively inserts the specified point p into a descendant of node n. The
+  // bounds are defined by [x1, x2] and [y1, y2].
+  function insertChild(n, p, x1, y1, x2, y2) {
+    // Compute the split point, and the quadrant in which to insert p.
+    var sx = (x1 + x2) * .5,
+        sy = (y1 + y2) * .5,
+        right = p.x >= sx,
+        bottom = p.y >= sy,
+        i = (bottom << 1) + right;
+
+    // Recursively insert into the child node.
+    n.leaf = false;
+    n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
+
+    // Update the bounds as we recurse.
+    if (right) x1 = sx; else x2 = sx;
+    if (bottom) y1 = sy; else y2 = sy;
+    insert(n, p, x1, y1, x2, y2);
+  }
+
+  // Create the root node.
+  var root = d3_geom_quadtreeNode();
+
+  root.add = function(p) {
+    insert(root, p, x1, y1, x2, y2);
+  };
+
+  root.visit = function(f) {
+    d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2);
+  };
+
+  // Insert all points.
+  points.forEach(root.add);
+  return root;
+};
+
+function d3_geom_quadtreeNode() {
+  return {
+    leaf: true,
+    nodes: [],
+    point: null
+  };
+}
+
+function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
+  if (!f(node, x1, y1, x2, y2)) {
+    var sx = (x1 + x2) * .5,
+        sy = (y1 + y2) * .5,
+        children = node.nodes;
+    if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
+    if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
+    if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
+    if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
+  }
+}
+
+function d3_geom_quadtreePoint(p) {
+  return {
+    x: p[0],
+    y: p[1]
+  };
+}