toolkit/javascript/d3/src/geom/quadtree.js
changeset 47 c0b4a8b5a012
equal deleted inserted replaced
46:efd9c589177a 47:c0b4a8b5a012
       
     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 }