cgma
KDDTree.cpp
Go to the documentation of this file.
00001 //-----------------------------------------------------------------
00002 //- Class:   KDDTree
00003 //- Author:  Kevin Albrecht 
00004 //- Created: 13 May 2003
00005 //- Updated: 8 Feb 2004
00006 //-
00007 //- Description:
00008 //-   Dynamic version of the k-d tree, where k=3.
00009 //-
00010 //- References:
00011 //-
00012 //-   Hanan Samet.  Design and Analysis of Spatial Data Structures.
00013 //-      Addison-Wesley, Reading, MA, 1990.
00014 //-
00015 //-   Jon Louis Bentley. Multidimensional binary search trees used
00016 //-     for associative searching. In Communications of the ACM,
00017 //-     18(9), pages 509-517, September 1975.
00018 //-----------------------------------------------------------------
00019 
00020 //---------------------------------
00021 // Include Files
00022 //---------------------------------
00023 
00024 #include <cstdlib>
00025 #include <cstdio>
00026 #include <time.h>
00027 
00028 #include "KDDTree.hpp"
00029 #include "KDDTreeNode.hpp"
00030 #include "CubitBox.hpp"
00031 #include "CubitVector.hpp"
00032 #include "DLIList.hpp"
00033 #include "PriorityQueue.hpp"
00034 
00035 //---------------------------------
00036 // Define Methods
00037 //---------------------------------
00038 
00039 //- Constructor
00040 //-  * The following only applies if self-balancing is turned on: if
00041 //-    selfBalancingDeletionTolerance is set to 0, then there is no limit
00042 //-    to the number of nodes that can be marked for deletion at a time;
00043 //-    otherwise, the tree will rebalance itself whenever the percentage
00044 //-    of nodes on the tree marked for deletion is greater than the
00045 //-    tolerance.
00046 template <class Z> KDDTree<Z>::KDDTree (double tol, CubitBoolean selfBalancingOn,
00047                                         double selfBalancingDeletionTolerance,
00048                                         CubitBoolean randomOn)
00049 {
00050   root = NULL;
00051 
00052   myTolerance = tol;
00053   mySelfBalancingOn = selfBalancingOn;
00054   myDeletionTolerance = selfBalancingDeletionTolerance;
00055   myMarkedNodes = 0;
00056   myRandomOn = randomOn;
00057 
00058   if (myRandomOn)
00059   {
00061     srand( (unsigned)time( NULL ) );
00062   }
00063 }
00064 
00065 //- Destructor
00066 template <class Z> KDDTree<Z>::~KDDTree()
00067 {
00068   int i;
00069   for (i = myAddList.size(); i > 0; i--)
00070   {
00071     delete myAddList.pop();
00072   }
00073   for (i = myNodeList.size(); i > 0; i--)
00074   {
00075     KDDTreeNode<Z> *node = myNodeList.pop();
00076     delete node;
00077   }
00078 }
00079 
00080 //- Immediately put all nodes on list onto the tree
00081 template <class Z> CubitStatus KDDTree<Z>::dump_list ()
00082 {
00083   while (myAddList.size() > 0)
00084   {
00085     KDDTreeNode<Z> *node = myAddList.pop();
00086     insert_node (node);
00087   }
00088 
00089   return CUBIT_SUCCESS;
00090 }
00091 
00092 //- "insert_node"
00093 //- Dynamically insert the data into the k-d tree 
00094 //- 
00095 //- Algorithm INSERT (From Bentley):
00096 //-   This algoritm is passed an object "data" of class "Z",
00097 //-   which has a bounding_box() method.  If there is already
00098 //-   a node in the tree with equal bounding box center point,
00099 //-   it is put in the right subtree.
00100 //-   I0. [Create new node] Create a node P with the bounding box
00101 //-       specified, and set P.LEFT <- null, P.RIGHT <- null, and
00102 //-       P.DISC <- null.
00103 //-   I1. [Check for null tree] If ROOT = null, then set ROOT <- P
00104 //-       and return CUBIT_SUCCESS; otherwise, set Q <- ROOT (Q
00105 //-       will move down the tree).
00106 //-   I2. [Compare] Compare the nodes and set the child in the
00107 //-       correct direction to T.
00108 //-   I3. [Move down] Set Q <- child of Q and go to I2.
00109 //-   I4. [Insert new node in tree] Set the child of Q to P, then
00110 //-       set the children of P to null. Set the discriminator of
00111 //-       P to be the discriminator after that in Q.
00112 //-
00113 template <class Z> CubitStatus KDDTree<Z>::insert_node (KDDTreeNode<Z>* P)
00114 {
00115   KDDTreeNode<Z> *F = NULL;      // father node
00116   KDDTreeNode<Z> *T;             // temp node
00117 
00118   if (root == NULL)
00119   {
00120     root = P;
00121     P->set_disc (DIMX);
00122   }
00123   else
00124   {
00125     T = root;
00126     DIRECTION direction = DIR_NULL;
00127 
00128     while (T != NULL)
00129     {
00130       F = T; // remember the father
00131       direction = P->compare (T);
00132 
00133       CubitVector tmin = T->safetyBox.minimum();
00134       CubitVector tmax = T->safetyBox.maximum();
00135       CubitVector pmin = P->safetyBox.minimum();
00136       CubitVector pmax = P->safetyBox.maximum();
00137 
00138       if (pmin.x() < tmin.x()) tmin.x (pmin.x());
00139       if (pmin.y() < tmin.y()) tmin.y (pmin.y());
00140       if (pmin.z() < tmin.z()) tmin.z (pmin.z());
00141       if (pmax.x() > tmax.x()) tmax.x (pmax.x());
00142       if (pmax.y() > tmax.y()) tmax.y (pmax.y());
00143       if (pmax.z() > tmax.z()) tmax.z (pmax.z());
00144 
00145       T->safetyBox.reset (tmin, tmax);
00146 
00147       T = T->get_child (direction);
00148     }
00149 
00150     F->set_child (P, direction);
00151     P->set_disc (F->next_disc());
00152     P->parent = F;
00153   }
00154   myNodeList.push (P);
00155 
00156   return CUBIT_SUCCESS;      
00157 }
00158 
00159 //- "modifind"
00160 //- Rearrange the array around the median point.
00161 //-
00162 //- Description:
00163 //-   This is the MODIFIND algorithm of V. Zabrodsky, but modified to choose a random
00164 //-   pivot point.  This is in turn a modified version of the Hoare FIND algorithm for
00165 //-   finding the median point.  Running time is O(n^2) in the worst case and O(n) in
00166 //-   the average case.
00167 //-
00168 //-   Zabrodsky's algorithm:
00169 //-     http://www.geocities.com/zabrodskyvlada/aat/a_modi.html
00170 //-     http://www.geocities.com/zabrodskyvlada/3alg.html
00171 //-
00172 //- Results:
00173 //-   Reordering of input array such that A[K] has the value it would have if A were
00174 //-   sorted, L<=I<=K will imply A[I]<=A[K], and K<=I<=R will imply A[I]>=A[K]
00175 template <class Z> int KDDTree<Z>::modifind (DIMENSION dim, int left, int right,
00176                                              KDDTreeNode<Z>* array[])
00177 {
00178   int K = ((right - left + 1) / 2) + left + 1;
00179   int L = left + 1;
00180   int R = right + 1;
00181   int I, J;
00182   KDDTreeNode<Z>* node; // "X" in MODIFIND
00183   KDDTreeNode<Z>* temp; // temp for swapping; "W" in MODIFIND
00184 
00186   if (myRandomOn == CUBIT_TRUE)
00187   {
00188     int pivot = ( rand() % (right - left) ) + left; // create random pivot between left and right
00189 
00191     temp = array[pivot];
00192     array[pivot] = array[K - 1];
00193     array[K - 1] = temp;
00194   }
00195 
00196   while (L < R)
00197   {
00198     node = array[K - 1];
00199     I = L;
00200     J = R;
00201     while (! ((J < K) || (K < I)) )
00202     {
00203       if (dim == DIMX)
00204       {
00205         while (array[I - 1]->x < node->x) {
00206           I++;
00207         }
00208         while (node->x < array[J - 1]->x) {
00209           J--;
00210         }
00211       }
00212       else if (dim == DIMY)
00213       {
00214         while (array[I - 1]->y < node->y) {
00215           I++;
00216         }
00217         while (node->y < array[J - 1]->y) {
00218           J--;
00219         }
00220       }
00221       else
00222       {
00223         while (array[I - 1]->z < node->z) {
00224           I++;
00225         }
00226         while (node->z < array[J - 1]->z) { 
00227           J--; 
00228         }
00229       }
00230 
00232       temp = array[I - 1];
00233       array[I - 1] = array[J - 1];
00234       array[J - 1] = temp;
00235 
00236       I++;
00237       J--;
00238     }
00239 
00240     if (J < K)
00241     {
00242       L = I;
00243     }
00244     if (K < I)
00245     {
00246       R = J;
00247     }
00248   }
00249 
00250   return K - 1;
00251 }
00252 
00253 //- "balance" and "recursive_balance"
00254 //- Create a balanced tree out of the nodes on the tree and in the Add List.
00255 //- This is used to balance the tree manually; it is also called by the
00256 //- find method when self-balancing is on.
00257 //-
00258 //- Description:
00259 //-   This is the OPTIMIZE algorithm of Bentley.  Total running time is
00260 //-   O(n lg n).  It uses the MODIFIND algorithm to find the median.
00261 //-
00262 //- Results:
00263 //-   The tree produced will be balanced so that all leaf nodes occur on
00264 //-   at most two adjacent levels.
00265 //-
00266 template <class Z> CubitStatus KDDTree<Z>::balance ()
00267 {
00268   int arraypos = 0;
00269   KDDTreeNode<Z> ** array = new KDDTreeNode<Z>*[myAddList.size () + myNodeList.size ()];
00270 
00271   root = NULL;
00272 
00273   while (myAddList.size () > 0)
00274   {
00275     array[arraypos] = myAddList.pop();
00276     arraypos++;
00277   }
00278   while (myNodeList.size () > 0)
00279   {
00280     array[arraypos] = myNodeList.pop();
00281 
00282     if (array[arraypos]->valid == CUBIT_FALSE)
00283     {
00284       array[arraypos]->left = NULL;
00285       array[arraypos]->right = NULL;
00286       delete array[arraypos];
00287     }
00288     else
00289     {
00290       arraypos++;
00291     }
00292   }
00293 
00294   int left = 0;
00295   int right = (arraypos - 1);
00296   root = recursive_balance (DIMX, left, right, array, NULL);
00297 
00298   myMarkedNodes = 0;
00299 
00300   delete [] array;
00301   return CUBIT_SUCCESS;
00302 }
00303 
00304 template <class Z> KDDTreeNode<Z> *KDDTree<Z>::recursive_balance
00305  (DIMENSION dim, int left, int right, KDDTreeNode<Z>** array, KDDTreeNode<Z>* parent)
00306 {
00307   if (left > right)
00308   {
00309     return NULL;
00310   }
00311   else
00312   {
00313     KDDTreeNode<Z>* P;
00314     int K;
00315 
00316     if (left != right)
00317     {
00318       K = modifind (dim, left, right, array);
00319       P = array[K];
00320     }
00321     else
00322     {
00323       K = left;
00324       P = array[left];
00325     }
00326 
00327     myNodeList.push (P);
00328 
00329     P->safetyBox.reset (P->boundingBox);
00330     for (int i = left; i <= right; i++)
00331     {
00332       CubitVector imin = array[i]->safetyBox.minimum();
00333       CubitVector imax = array[i]->safetyBox.maximum();
00334       CubitVector pmin = P->safetyBox.minimum();
00335       CubitVector pmax = P->safetyBox.maximum();
00336 
00337       if (imin.x() < pmin.x()) pmin.x (imin.x());
00338       if (imin.y() < pmin.y()) pmin.y (imin.y());
00339       if (imin.z() < pmin.z()) pmin.z (imin.z());
00340       if (imax.x() > pmax.x()) pmax.x (imax.x());
00341       if (imax.y() > pmax.y()) pmax.y (imax.y());
00342       if (imax.z() > pmax.z()) pmax.z (imax.z());
00343 
00344       P->safetyBox.reset (pmin, pmax);
00345     }
00346 
00347     DIMENSION nextDim;
00348     switch (dim)
00349     {
00350       case DIMX: nextDim = DIMY; break;
00351       case DIMY: nextDim = DIMZ; break;
00352       default:   nextDim = DIMX;
00353     }
00354 
00355     P->set_disc (dim);
00356     P->parent = parent;
00357 
00358     P->left = recursive_balance (nextDim, left, K - 1, array, P);
00359     P->right = recursive_balance (nextDim, K + 1, right, array, P);
00360 
00361     return P;
00362   }
00363 }
00364 
00365 //- Find the depth of the tree
00366 template <class Z> int KDDTree<Z>::find_max_height ()
00367 {
00368   int depth = 0, maxdepth = 0;
00369   recursive_find_max_height (root, depth, &maxdepth);
00370 
00371   return maxdepth;
00372 }
00373 
00374 //- Find the depth of the tree
00375 template <class Z> void KDDTree<Z>::recursive_find_max_height
00376  (KDDTreeNode<Z> *root, int depth, int *maxdepth)
00377 {
00378   if (root)
00379   {
00380     depth++;
00381     if (depth > *maxdepth)
00382     {
00383       *maxdepth = depth;
00384     }
00385     recursive_find_max_height (root->left, depth, maxdepth);
00386     recursive_find_max_height (root->right, depth, maxdepth);
00387   }
00388 }
00389 
00390 //- Add a node with the data to the list
00391 template <class Z> CubitStatus KDDTree<Z>::add (Z data)
00392 {
00393   KDDTreeNode<Z> *P = new KDDTreeNode<Z> (data);
00394   P->safetyBox.reset (data->bounding_box());
00395 
00396   myAddList.push (P);
00397 
00398   return CUBIT_SUCCESS;
00399 }
00400 
00401 //- Return a pointer to the node containing the specified data
00402 template <class Z> KDDTreeNode<Z> *KDDTree<Z>::find_node_containing_data (KDDTreeNode<Z> *subtreeRoot, Z data)
00403 {
00404   KDDTreeNode<Z> *T = new KDDTreeNode<Z>(data); // temp node to use in searching
00405   KDDTreeNode<Z> *P = subtreeRoot;              // node to delete
00406   DIRECTION D;
00407 
00409   while (P != NULL)
00410   {
00411     if ((P->boundingBox.minimum() == T->boundingBox.minimum()) &&
00412         (P->boundingBox.maximum() == T->boundingBox.maximum()))
00413     {
00414       if (P->valid == CUBIT_TRUE)
00415       {
00416         break; // the bounding boxes match and this node has not been deleted
00417       }
00418     }
00419 
00420     D = T->compare_with_equality (P);
00421 
00422     if (D == DIR_EITHER)
00423     {
00424       KDDTreeNode<Z> *leftResult = find_node_containing_data (P->get_child (DIR_LEFT), data);
00425 
00426       if (leftResult != NULL)
00427       {
00428         P = leftResult;
00429         break;
00430       }
00431 
00432       KDDTreeNode<Z> *rightResult = find_node_containing_data (P->get_child (DIR_RIGHT), data);
00433       
00434       if (rightResult != NULL)
00435       {
00436         P = rightResult;
00437         break;
00438       }
00439 
00440       P = NULL;
00441     }
00442     else
00443     {
00444       P = P->get_child (D);
00445     }
00446   }
00447 
00448   delete T;
00449 
00450   return P;
00451 }
00452 
00453 //- Remove the data member's entry in the tree. Returns CUBIT_TRUE
00454 //- if item removed, CUBIT_FALSE if item not in tree.
00455 template <class Z> CubitBoolean KDDTree<Z>::remove (Z data)
00456 {
00458   if (myAddList.size() > 0)
00459   {
00460     if (mySelfBalancingOn == CUBIT_TRUE) // self-balancing is on, so rebalance the tree
00461     {
00462       balance ();
00463     }
00464     else // self-balancing is off, so put everything in the Add List onto the tree
00465     {
00466       dump_list ();
00467     }
00468   }
00469 
00471   if (root == NULL)
00472   {
00473     return CUBIT_FALSE;
00474   }
00476   else
00477   {
00478     KDDTreeNode<Z> *P = find_node_containing_data (root, data);
00479 
00480     if (P == NULL) // no matching node was found
00481     {
00482       return CUBIT_FALSE;
00483     }
00484     else // mark the matching node for deletion
00485     {
00486       if (P->valid == CUBIT_FALSE)
00487       {
00488         return CUBIT_FALSE; // this node was already deleted
00489       }
00490 
00491       P->valid = CUBIT_FALSE; // set the node to be deleted
00492 
00493       myMarkedNodes++;
00494       if (myDeletionTolerance != 0)
00495       {
00496         if ( (((double)myMarkedNodes / myNodeList.size()) > myDeletionTolerance) &&
00497              (myMarkedNodes > 1)
00498            )
00499         {
00500           balance ();
00501         }
00502       }
00503 
00504       return CUBIT_TRUE;
00505     }
00506   }
00507 }
00508 
00509 //- Find members intersecting this range box
00510 template <class Z> CubitStatus KDDTree<Z>::find (const CubitBox &range_box, DLIList <Z> &range_members)
00511 {
00513   if (myAddList.size() > 0)
00514   {
00515     if (mySelfBalancingOn == CUBIT_TRUE) // self-balancing is on, so rebalance the tree
00516     {
00517       balance ();
00518     }
00519     else // self-balancing is off, so put everything in the Add List onto the tree
00520     {
00521       dump_list ();
00522     }
00523   }
00524 
00526   if (root != NULL)
00527   {
00528     recursive_find (root, range_box, range_members);
00529   }
00530 
00531   return CUBIT_SUCCESS;
00532 }
00533 
00534 //- Recursively find members intersecting this range box (called by "find")
00535 template <class Z> void KDDTree<Z>::recursive_find
00536   ( KDDTreeNode<Z> *rect_tree,
00537     const CubitBox &range_box,
00538     DLIList <Z> &range_members
00539   )
00540 {
00542   if ( ! range_box.overlap (myTolerance, rect_tree->safetyBox) )
00543   {
00544     return; // no overlap, return
00545   }
00546 
00548   if ( range_box.overlap (myTolerance, rect_tree->boundingBox) )
00549   {
00550     if (rect_tree->valid == CUBIT_TRUE)
00551     {
00552       range_members.append (rect_tree->data); // append the data to the list.
00553     }
00554   }
00555 
00556   if (rect_tree->left != NULL)
00557   {
00558     recursive_find (rect_tree->left, range_box, range_members);
00559   }
00560   if (rect_tree->right != NULL)
00561   {
00562     recursive_find (rect_tree->right, range_box, range_members);
00563   }
00564 
00565   return;
00566 }
00567 
00568 //- Finds the minimum distance squared between the given point and the box. If
00569 //-  the point is on or in the box, the min distance is zero.
00570 template <class Z> double KDDTree<Z>::min_dist_sq (CubitVector &q, CubitBox &b_box)
00571 {
00572   CubitVector b_min = b_box.minimum();
00573   CubitVector b_max = b_box.maximum();
00574   CubitVector r;
00575 
00577   if (q.x () < b_min.x ())
00578   {
00579     r.x (b_min.x ());
00580   }
00581   else if (q.x () > b_max.x ())
00582   {
00583     r.x (b_max.x ());
00584   }
00585   else
00586   {
00587     r.x (q.x ());
00588   }
00589   
00591   if (q.y () < b_min.y ())
00592   {
00593     r.y (b_min.y ());
00594   }
00595   else if (q.y () > b_max.y ())
00596   {
00597     r.y (b_max.y ());
00598   }
00599   else
00600   {
00601     r.y (q.y ());
00602   }
00603   
00605   if (q.z () < b_min.z ())
00606   {
00607     r.z (b_min.z ());
00608   }
00609   else if (q.z () > b_max.z ())
00610   {
00611     r.z (b_max.z ());
00612   }
00613   else
00614   {
00615     r.z (q.z ());
00616   }
00617   
00618   double dist = (q-r).length_squared();
00619 
00620   return dist;
00621 }
00622 
00623 template <class Z> bool KDDTree<Z>::less_than_func (KDDTreeNode<Z> *&node_a,
00624                                                     KDDTreeNode<Z> *&node_b)
00625 {
00626   if (node_a->get_dist() < node_b->get_dist ())
00627   {
00628     return true;
00629   }
00630   else
00631   {
00632     return false;
00633   }
00634 }
00635 
00636 //- "k_nearest_neighbor"
00637 //- Find the K nearest neighbors to a point.
00638 //-
00639 //- Description:
00640 //-   This algorithm is based on the best-first search.  The goal of this
00641 //-   algorithm is to minimize the number of nodes visited by using the
00642 //-   distance to each subtree's bounding box to avoid visiting subtrees
00643 //-   which could not possibly contain one of the k nearest objects.
00644 //-
00645 template <class Z> CubitStatus KDDTree<Z>::k_nearest_neighbor
00646   (CubitVector &q, int k, double &closest_dist, DLIList<Z> &nearest_neighbors,
00647    typename KDDTree<Z>::DistSqFunc dist_sq_point_data
00648   )  
00649 {
00651   PriorityQueue<KDDTreeNode<Z>*> *queue = new PriorityQueue<KDDTreeNode<Z>*> (KDDTree<Z>::less_than_func);
00652   PriorityQueue<KDDTreeNode<Z>*> *queueTemp = new PriorityQueue<KDDTreeNode<Z>*> (KDDTree<Z>::less_than_func);
00653 
00654 
00655   KDDTreeNode<Z> *element = root;
00656 
00657   // push this node on the queue
00658   element->set_dist (min_dist_sq (q, element->safetyBox));
00659   element->set_dist_data (DD_SAFETY);
00660   queue->push (element);
00661   
00662 
00663   // if the k closest nodes on the tree are not leaf-nodes, expand the closest
00664   //   non-leaf node
00665   while ( !queue->empty() )
00666   {
00667     element = queue->top();
00668     queue->pop();
00669 
00670     if (element->get_dist_data() == DD_LEAF)
00671     {
00672       // this node is a leaf, so it can be pushed onto the temporary queue
00673       queueTemp->push (element);
00674     }
00675     else
00676     {
00677       // one of the top k nodes is a non-leaf node, so expand it
00678       if (element->left)
00679       {
00680         element->left->set_dist (min_dist_sq (q, element->left->safetyBox));
00681         element->left->set_dist_data (DD_SAFETY);
00682         queue->push (element->left);
00683       }
00684       if (element->right)
00685       {
00686         element->right->set_dist (min_dist_sq (q, element->right->safetyBox));
00687         element->right->set_dist_data (DD_SAFETY);
00688         queue->push (element->right);
00689       }
00690       element->set_dist (dist_sq_point_data (q, element->data));
00691       element->set_dist_data (DD_LEAF);
00692       queue->push (element);
00693 
00694       // take all the elements in the temporary queue and reinsert them into
00695       //   the actual queue
00696       while ( !queueTemp->empty() )
00697       {
00698         queue->push ( queueTemp->top() );
00699         queueTemp->pop ();
00700       }
00701     }
00702 
00703     if (queueTemp->size() == k)
00704     {
00705       // success-- place the k nodes into the nearest_neighbors list
00706       element = queueTemp->top();
00707       queueTemp->pop();
00708       closest_dist = element->get_dist();
00709       nearest_neighbors.append (element->data);
00710 
00711       while ( !queueTemp->empty() )
00712       {
00713         nearest_neighbors.append ( queueTemp->top()->data );
00714         queueTemp->pop();
00715       }
00716      
00717       return CUBIT_SUCCESS;
00718     }
00719   }
00720   return CUBIT_FAILURE;
00721 }
00722 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines