cgma
|
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