KDindtree.cc
上传用户:kellyonhid
上传日期:2013-10-12
资源大小:932k
文件大小:7k
源码类别:

3D图形编程

开发平台:

Visual C++

  1. //############################################################
  2. // KDindtree.cc
  3. // Kari Pulli
  4. // 04/13/96
  5. //############################################################
  6. #include <iostream.h>
  7. #include "KDindtree.h"
  8. #include "Bbox.h"
  9. #include "defines.h"
  10. #define MEDIAN_SPLIT 0
  11. #if MEDIAN_SPLIT
  12. #include "Median.h"
  13. static Median<float> med;
  14. #endif
  15. // factory
  16. KDindtree*
  17. CreateKDindtree (const Pnt3* pts, const short* nrms, int nPts)
  18. {
  19.   cout << "Creating kdtree (" << nPts << " points)..." << flush;
  20.   if (!nPts)
  21.     return NULL;
  22.   assert (pts);
  23.   assert (nrms);
  24.   // allocate and fill temp index array
  25.   int *inds = new int[nPts];
  26.   for (int i = 0; i < nPts; i++) inds[i] = i; 
  27.   
  28.   // build tree
  29.   KDindtree* kdtree = new KDindtree(pts, nrms, inds, nPts);
  30.   // cleanup
  31.   delete[] inds;
  32.   cout << " done." << endl;
  33.   return kdtree;
  34. }
  35. static Pnt3
  36. GetNormalAsPnt3 (const short* nrms, int ofs)
  37. {
  38.   ofs *= 3;
  39.   Pnt3 n (nrms[ofs], nrms[ofs+1], nrms[ofs+2]);
  40.   n /= 32767.0;
  41.   return n;
  42. }
  43. static int
  44. divisionsort(const Pnt3 *data, int *p, int n,
  45.      int dim, float med)
  46. {
  47.   // move values <= med to left, the rest to right
  48.   int left = 0, right = n-1;
  49.   while (1) {
  50.     while (data[p[left]][dim] <= med && right > left) left++;
  51.     while (data[p[right]][dim] > med && right > left) right--;
  52.     int tmp = p[left];  // swap
  53.     p[left] = p[right]; p[right] = tmp;
  54.     if (left == right) {
  55.       if (data[p[right]][dim] <= med) right++;
  56.       break;
  57.     }
  58.     left++;
  59.   }
  60.   return right;
  61. }
  62. void
  63. merge_normal_cones(const Pnt3 &n1, float th1,
  64.    const Pnt3 &n2, float th2,
  65.    Pnt3 &n3, float &th3)
  66. {
  67.   // first, get the angle between the normals
  68.   th3 = .5 * (th1 + th2) + acos(dot(n1,n2));
  69.   // full sphere?
  70.   if (th3 >= M_PI) {
  71.     th3 = M_PI;
  72.     n3.set(0,0,1);
  73.     return;
  74.   }
  75.   // figure how much n1 should be rotated to become n3
  76.   float half_ang = .25*(th3-th1);
  77.   // create quaternion (real part and imaginary part)
  78.   // for rotation
  79.   float qr = cos(half_ang);
  80.   Pnt3 qim = cross(n1,n2);
  81.   qim *= sin(half_ang);
  82.   // apply rotation
  83.   (((n3 = n1) *= (qr*qr-qim.norm2()))
  84.    += (cross(qim, n1) *= (2.0*qr)))
  85.     += (qim *= (2.0*dot(n1,qim)));
  86.   //n3 = (qr*qr-qim.norm2())*n1 + (2*dot(n1,qim))*qim
  87.   //  + 2*qr*cross(qim,n1);  
  88. }
  89. KDindtree::KDindtree(const Pnt3 *pts, const short *nrms,
  90.      int *ind, int n, int first)
  91. : Nhere(0), element(NULL)
  92. {
  93.   int i;
  94.   // find the dimension of maximum range
  95.   min = max = pts[ind[0]];
  96.   int *end = ind+n;
  97.   for (int *ip=ind+1; ip<end; ip++) {
  98.     const Pnt3 &p = pts[*ip];
  99.     min.set_min(p);
  100.     max.set_max(p);
  101.   }
  102.   float dist = max[0] - min[0];
  103.   m_d = 0;
  104.   float tmp;
  105.   if ((tmp = max[1]-min[1]) > dist) {
  106.     m_d = 1; dist = tmp;
  107.   }
  108.   if ((tmp = max[2]-min[2]) > dist) {
  109.     m_d = 2; dist = tmp;
  110.   }
  111.   if (dist == 0.0) n = 1; // a single point several times
  112.   if (n > 16) {
  113. #if MEDIAN_SPLIT
  114. #define DATA(i) (pts[ind[i]])[m_d]
  115.     // find the median within that dimension
  116.     med.clear();
  117.     for (i=0; i<n; i++) med += DATA(i);
  118.     m_p = med.find();
  119.     int right = divisionsort(pts, ind, n, m_d, m_p);
  120.     if (right == n) {
  121.       // the median is also the largest, need new "median"
  122.       // find the next largest item for that
  123.       float nm = -9.e33;
  124.       for (i=0; i<n; i++) {
  125. if (DATA(i) != m_p && DATA(i) > nm) nm = DATA(i);
  126.       }
  127.       right = divisionsort(pts, ind, n, m_d, (m_p=nm));
  128.     }
  129.     assert(right != 0 && right != n);
  130. #undef DATA
  131. #else
  132.     m_p = .5*(max[m_d]+min[m_d]);
  133.     int right = divisionsort(pts, ind, n, m_d, m_p);
  134.     assert(right != 0 && right != n);
  135. #endif
  136.     // recurse
  137.     child[0] = new KDindtree(pts, nrms, ind, right, 0);
  138.     child[1] = new KDindtree(pts, nrms, &ind[right], n-right, 0);
  139.   } else {
  140.     // store data here
  141.     Nhere = n;
  142.     element = new int[n];
  143.     for (i=0; i<n; i++) element[i] = ind[i];
  144.     child[0] = child[1] = NULL;
  145.   }
  146. #if MEDIAN_SPLIT
  147.   if (first) med.zap(); // release memory after the tree's done
  148. #endif
  149.   if (nrms == NULL) return;
  150.   // now figure out bounds for the normals
  151.   if (Nhere) { 
  152.     // a terminal node
  153.     normal = GetNormalAsPnt3(nrms, element[0]);
  154.     theta  = 0.0;
  155.     for (i=1; i<Nhere; i++) {
  156.       merge_normal_cones(normal, theta,
  157.  GetNormalAsPnt3(nrms, element[i]), 0,
  158.  normal, theta);
  159.     }
  160.   } else {
  161.     // a non-terminal node
  162.     merge_normal_cones(child[0]->normal, child[0]->theta,
  163.        child[1]->normal, child[1]->theta,
  164.        normal, theta);
  165.   }
  166.   tmp = theta + M_PI * .25;
  167.   if (tmp > M_PI) cos_th_p_pi_over_4 = -1.0;
  168.   else            cos_th_p_pi_over_4 = cos(tmp);
  169. }
  170. KDindtree::~KDindtree(void)
  171. {
  172.   delete[] element;
  173.   delete child[0];
  174.   delete child[1];
  175. }
  176. int
  177. KDindtree::_search(const Pnt3 *pts, const short *nrms,
  178.    const Pnt3 &p, const Pnt3 &n,
  179.    int &ind, float &d) const
  180. {
  181.   assert(this);
  182.   if (dot(n, normal) < cos_th_p_pi_over_4)
  183.     return 0;
  184.   if (Nhere) { // terminal node
  185.     float l, d2 = d*d;
  186.     bool  need_sqrt = false;
  187.     int *el  = element;
  188.     int *end = el+Nhere;
  189.     for (; el<end; el++) {
  190.       l = dist2(pts[*el], p);
  191.       if (l < d2) {
  192. const short *sp = &nrms[(*el)*3]; 
  193. // 32767/sqrt(2)==23169.77
  194. if (n[0]*sp[0] + n[1]*sp[1] + n[2]*sp[2] > 23169.77) {
  195.   // normals also within 45 deg
  196.   d2=l; ind = *el; 
  197.   need_sqrt = true;
  198. }
  199.       }
  200.     }
  201.     if (need_sqrt) d = sqrtf(d2);
  202.     return ball_within_bounds(p,d,min,max);
  203.   }
  204.   
  205.   if (p[m_d] <= m_p) { // the point is left from partition
  206.     if (child[0]->_search(pts,nrms,p,n,ind,d)) 
  207.       return 1;
  208.     if (bounds_overlap_ball(p,d,child[1]->min,max)) {
  209.       if (child[1]->_search(pts,nrms,p,n,ind,d)) 
  210. return 1;
  211.     }
  212.   } else {             // the point is right from partition
  213.     if (child[1]->_search(pts,nrms,p,n,ind,d)) 
  214.       return 1;
  215.     if (bounds_overlap_ball(p,d,min,child[0]->max)) {
  216.       if (child[0]->_search(pts,nrms,p,n,ind,d)) 
  217. return 1;
  218.     }    
  219.   }
  220.   return ball_within_bounds(p,d,min,max);
  221. }
  222. int
  223. KDindtree::_search(const Pnt3 *pts, const Pnt3 &p,
  224.    int &ind, float &d) const
  225. {
  226.   assert(this);
  227.   if (Nhere) { // terminal node
  228.     float l, d2 = d*d;
  229.     bool  need_sqrt = false;
  230.     int *el  = element;
  231.     int *end = el+Nhere;
  232.     for (; el<end; el++) {
  233.       l = dist2(pts[*el], p);
  234.       if (l < d2) { 
  235. d2=l; ind = *el; 
  236. need_sqrt = true;
  237.       }
  238.     }
  239.     if (need_sqrt) d = sqrtf(d2);
  240.     return ball_within_bounds(p,d,min,max);
  241.   }
  242.   
  243.   if (p[m_d] <= m_p) { // the point is left from partition
  244.     if (child[0]->_search(pts,p,ind,d)) 
  245.       return 1;
  246.     if (bounds_overlap_ball(p,d,child[1]->min,max)) {
  247.       if (child[1]->_search(pts,p,ind,d)) 
  248. return 1;
  249.     }
  250.   } else {             // the point is right from partition
  251.     if (child[1]->_search(pts,p,ind,d)) 
  252.       return 1;
  253.     if (bounds_overlap_ball(p,d,min,child[0]->max)) {
  254.       if (child[0]->_search(pts,p,ind,d)) 
  255. return 1;
  256.     }    
  257.   }
  258.   return ball_within_bounds(p,d,min,max);
  259. }