The Cuik KD-Tree Library


cuik-kdtree.c

Go to the documentation of this file.
00001 #include "cuik-kdtree.h"
00002 
00003 #include "definitions.h"
00004 #include "vector.h"
00005 #include "random.h"
00006 #include "assert.h"
00007 
00042 #define TRUE_MEDIAN 0
00043 
00058 TKDtree *BuildKDTree(Trectangle *ambient,unsigned int m,double r,
00059                      unsigned int n,double **points,unsigned int *ids);
00060 
00075 void SearchKDtree(double dr2,unsigned int *topology,double *p,double *d2,
00076                   unsigned int *id,TKDtree *kdt);
00077 
00093 void SearchInBallKDtree(double dr2,unsigned int *topology,double *p,double r2,
00094                         unsigned int *n,unsigned int *m,
00095                         unsigned int **id,TKDtree *kdt);
00096 
00097 
00098 /************************************************************************/
00099 /************************************************************************/
00100 /************************************************************************/
00101 TKDtree *BuildKDTree(Trectangle *ambient,unsigned int m,double r,
00102                      unsigned int n,double **points,unsigned int *ids)
00103 {
00104   TKDtree *kdt;
00105 
00106   NEW(kdt,1,TKDtree);
00107 
00108   kdt->nd=GetRectangleDim(ambient);
00109   kdt->n=n;
00110   kdt->m=m;
00111   kdt->r=r;
00112   CopyRectangle(&(kdt->ambient),ambient);
00113 
00114   if (n<m)
00115     {
00116       unsigned int i;
00117 
00118       kdt->isLeaf=TRUE;
00119 
00120       kdt->height=1;
00121 
00122       NEW(kdt->p,kdt->m+1,double *);  /* +1 is useful when adding points */
00123       NEW(kdt->id,kdt->m+1,unsigned int);
00124 
00125       if (n==0)
00126         kdt->volume=0.0;
00127       else
00128         {
00129           for(i=0;i<n;i++)
00130             {
00131               kdt->p[i]=points[i];
00132               kdt->id[i]=ids[i];
00133               if (i==0)
00134                 InitRectangleFromPoint(kdt->nd,points[i],&(kdt->boundingBox));
00135               else
00136                 ExpandRectangle(points[i],&(kdt->boundingBox));
00137             }
00138           kdt->volume=EnlargeRectangleWithLimits(r,
00139                                                  &(kdt->ambient),
00140                                                  &(kdt->boundingBox),
00141                                                  &(kdt->samplingArea));
00142         }
00143       kdt->nextLeaf=NULL;
00144 
00145       #ifndef NDEBUG
00146         /* Reset the internal node info */
00147         kdt->splitDim=NO_UINT;
00148         kdt->splitValue=INF;
00149         kdt->left=NULL;
00150         kdt->right=NULL;
00151         kdt->leftLeaf=NULL;
00152         kdt->rightLeaf=NULL;
00153       #endif
00154     }
00155   else
00156     {
00157       signed int i,j;
00158       double *ps;
00159       unsigned int is;
00160       TKDtree *kdtLeft,*kdtRight;
00161       double u,l;
00162 
00163       kdt->isLeaf=FALSE;
00164 
00165       /* determine the split dimension and split point */
00166       kdt->splitDim=GetRectangleSplitDim(&(kdt->ambient));
00167 
00168       #if (TRUE_MEDIAN)
00169       {
00170         unsigned int med,right,left;
00171         double val;
00172 
00173         /* We adapt the quickselect algorithm to find the median in O(n) */
00174         med=(unsigned int)n/2; //position of the median
00175         left=0;
00176         right=n;
00177         i=0; // do not start the loop if r==l==med
00178         while (i!=med)
00179           {
00180             //we use the leftmost value as a pivot 
00181             //(can be any index betwee 'left' and 'right')
00182             val=points[left][kdt->splitDim]; 
00183             
00184             /*
00185             // this is only necessary if we pick a pivot value 
00186             // different from the left-most one
00187             if (pivot!=left)
00188               {
00189                 //Save the pivot value in the leftmost position
00190                 SWAP(points[left],points[i],ps);
00191                 SWAP(ids[left],ids[i],is);
00192               }
00193             */
00194 
00195             // for the rest of values, find the first value
00196             // larger than the pivot value
00197             i=left+1;
00198             while ((i<right)&&(points[i][kdt->splitDim]<val))
00199               i++;
00200             // classify the values after 'i'
00201             j=i+1;
00202             while (j<right)
00203               {
00204                 if (points[j][kdt->splitDim]<val)
00205                   {
00206                     SWAP(points[i],points[j],ps);
00207                     SWAP(ids[i],ids[j],is); 
00208                     i++;
00209                   }
00210                 j++;
00211               }
00212             if (left!=i-1)
00213               {
00214                 // insert the pivot just before the 1st value larger than it
00215                 SWAP(points[left],points[i-1],ps);
00216                 SWAP(ids[left],ids[i-1],is); 
00217               }
00218             if (i<med)
00219               left=i;
00220             else
00221               {
00222                 if (i>med)
00223                   right=i;
00224               }
00225           }
00226         kdt->splitValue=points[i][kdt->splitDim];
00227       }
00228       #else
00229         //approximate the median by the mean
00230         kdt->splitValue=0.0;
00231         for(i=0;i<n;i++)
00232           kdt->splitValue+=points[i][kdt->splitDim];
00233         kdt->splitValue/=(double)n;
00234 
00235 
00236         //split the points in two sub-sets according to the split point
00237         i=0; 
00238         j=n-1;
00239         do {
00240           while ((i<n)&&(points[i][kdt->splitDim]<kdt->splitValue))
00241             i++;
00242           while ((j>=0)&&(points[j][kdt->splitDim]>=kdt->splitValue))
00243             j--;
00244           if ((i<n)&&(j>=0)&&(i<j)&&
00245               (points[i][kdt->splitDim]>=kdt->splitValue)&&
00246               (points[j][kdt->splitDim]<kdt->splitValue))
00247             {
00248               SWAP(points[i],points[j],ps);
00249               SWAP(ids[i],ids[j],is); 
00250             }
00251         } while (i<j);      
00252       #endif
00253 
00254       /* at this point we have the split value and the vector of points is
00255          sorted with 'i' elements with values below the split value and
00256          n-i elements with values above it. */
00257 
00258       /* Recursively build kd-trees */
00259 
00260       GetRectangleLimits(kdt->splitDim,&l,&u,&(kdt->ambient));
00261 
00262       SetRectangleUpperLimit(kdt->splitDim,kdt->splitValue,&(kdt->ambient));
00263       kdtLeft=BuildKDTree(&(kdt->ambient),m,r,i,points,ids);
00264 
00265       SetRectangleLimits(kdt->splitDim,kdt->splitValue,u,&(kdt->ambient));
00266       kdtRight=BuildKDTree(&(kdt->ambient),m,r,n-i,&(points[i]),&(ids[i]));
00267 
00268       SetRectangleLowerLimit(kdt->splitDim,l,&(kdt->ambient));
00269       
00270       /* Assemble the sub-trees */ 
00271       kdt->left=(void *)kdtLeft;
00272       kdt->right=(void *)kdtRight;
00273 
00274       kdt->leftLeaf=(kdtLeft->isLeaf?(void*)kdtLeft:kdtLeft->leftLeaf);
00275       kdt->rightLeaf=(kdtRight->isLeaf?(void*)kdtRight:kdtRight->rightLeaf);
00276 
00277       /* Update the smapling area */
00278       kdt->volume=kdtLeft->volume+kdtRight->volume;
00279       
00280       /* Update the height */
00281       kdt->height=1+(kdtLeft->height>kdtRight->height?
00282                      kdtLeft->height:
00283                      kdtRight->height);
00284 
00285       /* Chain the sub-tree leaves */
00286       if (kdtLeft->isLeaf)
00287         {
00288           if (kdtRight->isLeaf)
00289             kdtLeft->nextLeaf=(void *)kdtRight;
00290           else
00291             kdtLeft->nextLeaf=kdtRight->leftLeaf;
00292         }
00293       else
00294         {
00295           if (kdtRight->isLeaf)
00296             KDTPTR(kdtLeft->rightLeaf)->nextLeaf=(void *)kdtRight;
00297           else
00298             KDTPTR(kdtLeft->rightLeaf)->nextLeaf=kdtRight->leftLeaf;
00299         }
00300 
00301       #ifndef NDEBUG
00302         /* Reset the leaf info */
00303         kdt->nextLeaf=NULL;
00304         kdt->p=NULL;
00305         kdt->id=NULL;
00306       #endif
00307     }
00308 
00309   return(kdt);
00310 }
00311 
00312 void SearchKDtree(double dr2,unsigned int *topology,double *p,double *d2,
00313                   unsigned int *id,TKDtree *kdt)
00314 {
00315   if (kdt->isLeaf)
00316     {
00317       if (SquaredDistanceToRectangle(*d2,p,topology,&(kdt->boundingBox))<*d2)
00318         {
00319           unsigned int i;
00320           double dp;
00321       
00322           for(i=0;i<kdt->n;i++)
00323             {
00324               dp=VectorSquaredDistanceTopologyMin(*d2,kdt->nd,topology,p,kdt->p[i]);
00325               if (dp<*d2)
00326                 {
00327                   *d2=dp;
00328                   *id=kdt->id[i];
00329                 }
00330             }
00331         }
00332     }
00333   else
00334     {
00335       TKDtree *t1,*t2;
00336       double dt0,dt1,dt2,d,v;
00337 
00338       v=p[kdt->splitDim];
00339       if (v<=kdt->splitValue)
00340         {
00341           /* The nearest point is most likely in the left branch */
00342           t1=KDTPTR(kdt->left);
00343           t2=KDTPTR(kdt->right);
00344         }
00345       else
00346         {
00347           /* The nearest point is most likely in the left branch */
00348           t1=KDTPTR(kdt->right);
00349           t2=KDTPTR(kdt->left);
00350         }
00351 
00352       dt0=dr2-SquaredDistanceToRectangleDimension(kdt->splitDim,v,topology,
00353                                                   &(kdt->ambient));
00354 
00355       dt1=SquaredDistanceToRectangleDimension(kdt->splitDim,v,topology,
00356                                               &(t1->ambient));
00357       d=dt0+dt1;
00358       if (d<*d2)
00359         SearchKDtree(d,topology,p,d2,id,t1);
00360 
00361       dt2=SquaredDistanceToRectangleDimension(kdt->splitDim,v,topology,
00362                                               &(t2->ambient));
00363       d=dt0+dt2;
00364       if (d<*d2)
00365         SearchKDtree(d,topology,p,d2,id,t2);
00366     }
00367 }
00368 
00369 void SearchInBallKDtree(double dr2,unsigned int *topology,double *p,double r2,
00370                         unsigned int *n,unsigned int *m,
00371                         unsigned int **id,TKDtree *kdt)
00372 {
00373   if (kdt->isLeaf)
00374     {
00375       if (SquaredDistanceToRectangle(r2,p,topology,&(kdt->boundingBox))<r2)
00376         {
00377           unsigned int i;
00378           
00379           for(i=0;i<kdt->n;i++)
00380             {
00381               if (VectorSquaredDistanceTopologyMin(r2,kdt->nd,topology,p,kdt->p[i])<r2)
00382                 {
00383                   if (*n==*m)
00384                     MEM_DUP(*id,*m,unsigned int);
00385                   (*id)[*n]=kdt->id[i];
00386                   (*n)++;
00387                 }
00388             }
00389         }
00390     }
00391   else
00392     {
00393       TKDtree *t;
00394       double d,dt,v;
00395 
00396       v=p[kdt->splitDim];
00397       dt=dr2-SquaredDistanceToRectangleDimension(kdt->splitDim,v,topology,
00398                                                  &(kdt->ambient));
00399        
00400       t=KDTPTR(kdt->left);
00401       d=dt+SquaredDistanceToRectangleDimension(kdt->splitDim,v,topology,
00402                                                &(t->ambient));
00403       if (d<r2)
00404         SearchInBallKDtree(d,topology,p,r2,n,m,id,t);
00405        
00406       t=KDTPTR(kdt->right);
00407       d=dt+SquaredDistanceToRectangleDimension(kdt->splitDim,v,topology,
00408                                                &(t->ambient));
00409       if (d<r2)
00410         SearchInBallKDtree(d,topology,p,r2,n,m,id,t);
00411     }
00412 }
00413 
00414 /************************************************************************/
00415 /************************************************************************/
00416 /************************************************************************/
00417 
00418 TKDtree *InitKDTree(Trectangle *ambient,unsigned int m,double r,
00419                     unsigned int n,double **points,unsigned int *ids)
00420 {
00421   assert(m>=2);
00422   assert(r>0);
00423 
00424   return(BuildKDTree(ambient,m,r,n,points,ids));
00425 }
00426 
00427 void AddPoint2KDtree(unsigned int *topology,double *point,unsigned int id,TKDtree **kdt)
00428 {
00429   TKDtree *t;
00430 
00431   t=*kdt;
00432 
00433   if (t->isLeaf)
00434     {
00435       t->p[t->n]=point;
00436       t->id[t->n]=id;
00437       t->n++;
00438       if (t->n>t->m)
00439         {
00440           TKDtree *nt;
00441 
00442           nt=BuildKDTree(&(t->ambient),t->m,t->r,t->n,t->p,t->id);
00443           DeleteKDtree(t);
00444           *kdt=nt;
00445         }
00446       else
00447         {
00448           if (t->n==1)
00449             InitRectangleFromPoint(t->nd,point,&(t->boundingBox));
00450           else
00451             ExpandRectangle(point,&(t->boundingBox));
00452           t->volume=EnlargeRectangleWithLimits(t->r,
00453                                                &(t->ambient),
00454                                                &(t->boundingBox),
00455                                                &(t->samplingArea));
00456         }
00457     }
00458   else
00459     {
00460       TKDtree *kdtLeft,*kdtRight;
00461 
00462       if (point[t->splitDim]<=t->splitValue)
00463         AddPoint2KDtree(topology,point,id,(TKDtree **)&(t->left));
00464       else
00465         AddPoint2KDtree(topology,point,id,(TKDtree **)&(t->right));
00466 
00467       t->n++;
00468 
00469       kdtLeft=KDTPTR(t->left);
00470       kdtRight=KDTPTR(t->right);
00471 
00472       /* One of the sub-trees changed -> update the summary at this level */
00473       
00474       /* The chain of leaves must be updated even if we re-balance.
00475          Before re-balancing, we need a correct chain of leaves. */
00476 
00477       t->leftLeaf=(kdtLeft->isLeaf?(void*)kdtLeft:kdtLeft->leftLeaf);
00478       t->rightLeaf=(kdtRight->isLeaf?(void*)kdtRight:kdtRight->rightLeaf);
00479 
00480       /* Chain the sub-tree leaves */
00481       if (kdtLeft->isLeaf)
00482         {
00483           if (kdtRight->isLeaf)
00484             kdtLeft->nextLeaf=(void *)kdtRight;
00485           else
00486             kdtLeft->nextLeaf=kdtRight->leftLeaf;
00487         }
00488       else
00489         {
00490           if (kdtRight->isLeaf)
00491             KDTPTR(kdtLeft->rightLeaf)->nextLeaf=(void *)kdtRight;
00492           else
00493             KDTPTR(kdtLeft->rightLeaf)->nextLeaf=kdtRight->leftLeaf;
00494         }
00495 
00496       /* If necessary, rebalance the tree -> re-build the tree */
00497       if ((KDTPTR(t->left)->height>KDTPTR(t->right)->height*2)||
00498           (KDTPTR(t->right)->height>KDTPTR(t->left)->height*2))
00499         {
00500           double **p;
00501           unsigned int *id;
00502           TKDtree *l,*nt;
00503           unsigned int i,j;
00504 
00505           /* Collect all the points in the leaves of the tree to rebuild */
00506           NEW(p,t->n,double *);
00507           NEW(id,t->n,unsigned int);
00508           l=KDTPTR(t->leftLeaf);
00509           i=0;
00510           while(i<t->n)
00511             {
00512               for(j=0;j<l->n;j++)
00513                 {
00514                   p[i]=l->p[j];
00515                   id[i]=l->id[j];
00516                   i++;
00517                 }
00518               l=KDTPTR(l->nextLeaf);
00519             }
00520           
00521           /* Re-build the tree */
00522           nt=BuildKDTree(&(t->ambient),t->m,t->r,t->n,p,id);
00523           DeleteKDtree(t);
00524           *kdt=nt;
00525 
00526           free(p);
00527           free(id);
00528         }
00529       else
00530         {
00531           /* Some "summary" information is only valid if we do not re-balance */
00532           t->volume=kdtLeft->volume+kdtRight->volume;     
00533           t->height=1+(kdtLeft->height>kdtRight->height?
00534                        kdtLeft->height:kdtRight->height);
00535         }
00536     }
00537 }
00538 
00539 unsigned int NearestNeighbour(unsigned int *topology,double *point,TKDtree *kdt)
00540 {
00541   unsigned int id;
00542   double d;
00543 
00544   assert(kdt->n>0);
00545 
00546   id=NO_UINT;
00547   d=INF;
00548   SearchKDtree(0.0,topology,point,&d,&id,kdt);
00549 
00550   assert(id!=NO_UINT);
00551 
00552   return(id);
00553 }
00554 
00555 void NeighboursInBall(unsigned int *topology,double *point,double r,
00556                       unsigned int *n,unsigned int **ids,TKDtree *kdt)
00557 {
00558   unsigned int m;
00559 
00560   assert(kdt->n>0);
00561 
00562   m=100;
00563   NEW(*ids,m,unsigned int);
00564   *n=0;
00565  
00566   SearchInBallKDtree(0.0,topology,point,r*r,n,&m,ids,kdt);
00567 
00568   if (*n==0)
00569     free(*ids);
00570 }
00571 
00572 void SampleInKDtree(double *point,TKDtree *kdt)
00573 {
00574   if (kdt->isLeaf)
00575     RandomPointInRectangle(point,&(kdt->samplingArea));
00576   else
00577     {
00578       double r;
00579 
00580       r=randomDouble()*kdt->volume;
00581       
00582       if (r<KDTPTR(kdt->left)->volume)
00583         SampleInKDtree(point,KDTPTR(kdt->left));
00584       else
00585         SampleInKDtree(point,KDTPTR(kdt->right));
00586     }
00587 }
00588 
00589 void DeleteKDtree(TKDtree *kdt)
00590 {
00591   if (kdt->isLeaf)
00592     {
00593       free(kdt->p);
00594       free(kdt->id);
00595       DeleteRectangle(&(kdt->boundingBox));
00596       DeleteRectangle(&(kdt->samplingArea));
00597     }
00598   else
00599     {
00600       DeleteKDtree(KDTPTR(kdt->left));
00601       DeleteKDtree(KDTPTR(kdt->right));
00602     }
00603   DeleteRectangle(&(kdt->ambient));
00604   free(kdt);
00605 }