contrib/brl/bbas/imesh/algo/imesh_kd_tree.cxx
Go to the documentation of this file.
00001 // This is brl/bbas/imesh/algo/imesh_kd_tree.cxx
00002 #include "imesh_kd_tree.h"
00003 #include "imesh_kd_tree.txx"
00004 //:
00005 // \file
00006 
00007 #include <vgl/vgl_box_3d.h>
00008 #include <vgl/vgl_point_3d.h>
00009 #include <imesh/algo/imesh_intersect.h>
00010 #include <vcl_algorithm.h>
00011 #include <vcl_limits.h>
00012 #include <vcl_cmath.h>
00013 
00014 
00015 //: The minimum square distance between \a p and any point in \a b
00016 double imesh_min_sq_dist(const vgl_point_3d<double>& p,
00017                          const vgl_box_3d<double>& b)
00018 {
00019   double sum_sq = 0;
00020 
00021   if (p.x() < b.min_x()) {
00022     double dist = b.min_x() - p.x();
00023     sum_sq += dist * dist;
00024   }
00025   else if (p.x() > b.max_x()) {
00026     double dist = b.max_x() - p.x();
00027     sum_sq += dist*dist;
00028   }
00029 
00030   if (p.y() < b.min_y()) {
00031     double dist = b.min_y() - p.y();
00032     sum_sq += dist * dist;
00033   }
00034   else if (p.y() > b.max_y()) {
00035     double dist = b.max_y() - p.y();
00036     sum_sq += dist*dist;
00037   }
00038 
00039   if (p.z() < b.min_z()) {
00040     double dist = b.min_z() - p.z();
00041     sum_sq += dist * dist;
00042   }
00043   else if (p.z() > b.max_z()) {
00044     double dist = b.max_z() - p.z();
00045     sum_sq += dist*dist;
00046   }
00047 
00048   return sum_sq;
00049 }
00050 
00051 
00052 //: The maximum square distance between \a p and any point in \a b
00053 double imesh_max_sq_dist(const vgl_point_3d<double>& p,
00054                          const vgl_box_3d<double>& b)
00055 {
00056   vgl_point_3d<double> mp( (p.x() > b.centroid_x()) ? b.min_x() : b.max_x(),
00057                            (p.y() > b.centroid_y()) ? b.min_y() : b.max_y(),
00058                            (p.z() > b.centroid_z()) ? b.min_z() : b.max_z() );
00059   return (mp - p).sqr_length();
00060 }
00061 
00062 
00063 namespace {
00064 
00065 //: Functor for sorting boxes by increasing X centroid
00066 class less_box_centroid_x
00067 {
00068   vcl_vector<vgl_box_3d<double> > boxes;
00069  public:
00070   less_box_centroid_x(const vcl_vector<vgl_box_3d<double> >& b) : boxes(b) {}
00071   bool operator () (unsigned int i1, unsigned int i2) const
00072   {
00073     return boxes[i1].centroid_x() < boxes[i2].centroid_x();
00074   }
00075 };
00076 
00077 //: Functor for sorting boxes by increasing Y centroid
00078 class less_box_centroid_y
00079 {
00080   vcl_vector<vgl_box_3d<double> > boxes;
00081  public:
00082   less_box_centroid_y(const vcl_vector<vgl_box_3d<double> >& b) : boxes(b) {}
00083   bool operator () (unsigned int i1, unsigned int i2) const
00084   {
00085     return boxes[i1].centroid_y() < boxes[i2].centroid_y();
00086   }
00087 };
00088 
00089 //: Functor for sorting boxes by increasing Z centroid
00090 class less_box_centroid_z
00091 {
00092   vcl_vector<vgl_box_3d<double> > boxes;
00093  public:
00094   less_box_centroid_z(const vcl_vector<vgl_box_3d<double> >& b) : boxes(b) {}
00095   bool operator () (unsigned int i1, unsigned int i2) const
00096   {
00097     return boxes[i1].centroid_z() < boxes[i2].centroid_z();
00098   }
00099 };
00100 
00101 bool
00102 best_split(const vcl_vector<vgl_box_3d<double> >& boxes,
00103            const vcl_vector<unsigned int>& indices,
00104            double& min_volume, unsigned int& split_ind)
00105 {
00106   const unsigned int ind_size = indices.size();
00107   vcl_vector<double> volume_seq;
00108   vgl_box_3d<double> box(boxes[indices[0]]);
00109   for (unsigned int i=1; i<ind_size; ++i)
00110   {
00111     volume_seq.push_back(box.volume());
00112     box.add(boxes[indices[i]]);
00113   }
00114   box = boxes[indices[ind_size-1]];
00115   bool found_min = false;
00116   double best_ratio_factor = 1.0;
00117   for (unsigned int i=ind_size-1; i>0;)
00118   {
00119     double volume = box.volume();
00120     box.add(boxes[indices[--i]]);
00121     volume += volume_seq[i];
00122     if (volume <= min_volume) {
00123       double ratio_factor =  vcl_abs(0.5 - double(i+1)/ind_size);
00124       if ( ratio_factor < 0.25 && ratio_factor < best_ratio_factor) {
00125         min_volume = volume;
00126         split_ind = i+1;
00127         found_min = true;
00128         best_ratio_factor = ratio_factor;
00129       }
00130     }
00131   }
00132   return found_min;
00133 }
00134 // end of namespace
00135 };
00136 
00137 
00138 //: recursively construct a kd-tree of mesh triangles
00139 vcl_auto_ptr<imesh_kd_tree_node>
00140 imesh_build_kd_tree_rec(const vcl_vector<vgl_box_3d<double> >& boxes,
00141                         const vgl_box_3d<double>& outer_box,
00142                         const vgl_box_3d<double>& inner_box,
00143                         const vcl_vector< unsigned int >& indices)
00144 {
00145   // If only one triangle is left, create and return a leaf node.
00146   if (indices.size() == 1) {
00147     return vcl_auto_ptr<imesh_kd_tree_node>(new imesh_kd_tree_node( outer_box,
00148                                                                     inner_box,
00149                                                                     indices[0] ));
00150   }
00151 
00152   // find best direction for splitting
00153   vcl_vector<vcl_vector<unsigned int> > sorted(3,indices);
00154   vcl_sort(sorted[0].begin(),sorted[0].end(), less_box_centroid_x(boxes));
00155   vcl_sort(sorted[1].begin(),sorted[1].end(), less_box_centroid_y(boxes));
00156   vcl_sort(sorted[2].begin(),sorted[2].end(), less_box_centroid_z(boxes));
00157   double min_volume = vcl_numeric_limits<double>::infinity();
00158   unsigned int split_ind=0;
00159   int dim = -1;
00160   if (best_split(boxes,sorted[0],min_volume,split_ind))
00161     dim = 0;
00162   if (best_split(boxes,sorted[1],min_volume,split_ind))
00163     dim = 1;
00164   if (best_split(boxes,sorted[2],min_volume,split_ind))
00165     dim = 2;
00166 
00167   // split the sorted indices
00168   vcl_vector<unsigned int> left_indices(sorted[dim].begin(),
00169                                         sorted[dim].begin()+split_ind);
00170   vcl_vector<unsigned int> right_indices(sorted[dim].begin()+split_ind,
00171                                          sorted[dim].end());
00172 
00173   // compute the left and right inner and outer boxes
00174   vgl_box_3d<double> left_inner_box, right_inner_box;
00175   typedef vcl_vector<unsigned int>::const_iterator index_iterator;
00176   for ( index_iterator itr=left_indices.begin(); itr!=left_indices.end(); ++itr )
00177     left_inner_box.add(boxes[*itr]);
00178   for ( index_iterator itr=right_indices.begin(); itr!=right_indices.end(); ++itr )
00179     right_inner_box.add(boxes[*itr]);
00180   vgl_box_3d<double> left_outer_box(outer_box), right_outer_box(outer_box);
00181   switch (dim)
00182   {
00183     case 0:
00184       left_outer_box.set_max_x(left_inner_box.max_x());
00185       right_outer_box.set_min_x(right_inner_box.min_x());
00186       if (left_outer_box.max_x() < right_outer_box.min_x()) {
00187         double mean = (left_outer_box.max_x() + right_outer_box.min_x())/2.0;
00188         left_outer_box.set_max_x(mean);
00189         right_outer_box.set_min_x(mean);
00190       }
00191       break;
00192     case 1:
00193       left_outer_box.set_max_y(left_inner_box.max_y());
00194       right_outer_box.set_min_y(right_inner_box.min_y());
00195       if (left_outer_box.max_y() < right_outer_box.min_y()) {
00196         double mean = (left_outer_box.max_y() + right_outer_box.min_y())/2.0;
00197         left_outer_box.set_max_y(mean);
00198         right_outer_box.set_min_y(mean);
00199       }
00200       break;
00201     case 2:
00202       left_outer_box.set_max_z(left_inner_box.max_z());
00203       right_outer_box.set_min_z(right_inner_box.min_z());
00204       if (left_outer_box.max_z() < right_outer_box.min_z()) {
00205         double mean = (left_outer_box.max_z() + right_outer_box.min_z())/2.0;
00206         left_outer_box.set_max_z(mean);
00207         right_outer_box.set_min_z(mean);
00208       }
00209       break;
00210     default:
00211       break;
00212   }
00213 
00214   // recursively construct child nodes
00215   vcl_auto_ptr<imesh_kd_tree_node>
00216       left_child(imesh_build_kd_tree_rec(boxes,left_outer_box,left_inner_box,left_indices));
00217   vcl_auto_ptr<imesh_kd_tree_node>
00218       right_child(imesh_build_kd_tree_rec(boxes,right_outer_box,right_inner_box,right_indices));
00219 
00220   return vcl_auto_ptr<imesh_kd_tree_node>(new imesh_kd_tree_node(outer_box,
00221                                                                  inner_box,
00222                                                                  left_child,
00223                                                                  right_child));
00224 }
00225 
00226 
00227 //: Construct a kd-tree (in 3d) of axis aligned boxes
00228 vcl_auto_ptr<imesh_kd_tree_node>
00229 imesh_build_kd_tree(const vcl_vector<vgl_box_3d<double> >& boxes)
00230 {
00231   // make the outer box
00232   double minv = -vcl_numeric_limits<double>::infinity();
00233   double maxv = vcl_numeric_limits<double>::infinity();
00234   vgl_box_3d<double> outer_box(minv,minv,minv, maxv,maxv,maxv);
00235 
00236   // create the vector of ids
00237   vcl_vector< unsigned int > indices( boxes.size() );
00238   for ( unsigned int i=0; i<indices.size(); ++i)
00239     indices[i] = i;
00240 
00241   // make the inner box
00242   vgl_box_3d<double> inner_box;
00243   for ( unsigned int i=0; i<boxes.size(); ++i ) {
00244     inner_box.add(boxes[i]);
00245   }
00246 
00247   // call recursive function to do the real work
00248   vcl_auto_ptr<imesh_kd_tree_node> root(imesh_build_kd_tree_rec(boxes, outer_box, inner_box, indices));
00249 
00250   // assign additional indices to internal nodes
00251   // reverse breadth first (root gets last index)
00252   vcl_vector<imesh_kd_tree_node*> internal_nodes;
00253   internal_nodes.push_back(root.get());
00254   for (unsigned int c=0; c<internal_nodes.size(); ++c) {
00255     if (!internal_nodes[c]->left_->is_leaf())
00256       internal_nodes.push_back(internal_nodes[c]->left_.get());
00257     if (!internal_nodes[c]->right_->is_leaf())
00258       internal_nodes.push_back(internal_nodes[c]->right_.get());
00259   }
00260   typedef vcl_vector<imesh_kd_tree_node*>::const_reverse_iterator critr;
00261   unsigned int index = boxes.size();
00262   for (critr itr = internal_nodes.rbegin(); itr != critr(internal_nodes.rend()); ++itr)
00263     (*itr)->index_ = index++;
00264 
00265   return root;
00266 }
00267 
00268 
00269 //: construct a kd-tree of mesh faces
00270 vcl_auto_ptr<imesh_kd_tree_node>
00271 imesh_build_kd_tree(const imesh_vertex_array<3>& verts,
00272                     const imesh_face_array_base& faces)
00273 {
00274   // build face bounding boxes
00275   vcl_vector<vgl_box_3d<double> > boxes(faces.size());
00276   for ( unsigned int i=0; i<boxes.size(); ++i ) {
00277     for ( unsigned int j=0; j<faces.num_verts(i); ++j) {
00278       boxes[i].add(verts[faces(i,j)]);
00279     }
00280   }
00281 
00282   // construct a kd-tree using the boxes
00283   return imesh_build_kd_tree(boxes);
00284 }
00285 
00286 
00287 namespace {
00288 class tri_dist_func
00289 {
00290  public:
00291   tri_dist_func(const imesh_vertex_array<3>& v,
00292                 const imesh_regular_face_array<3>& t)
00293   : verts(v), tris(t), closest_index(static_cast<unsigned int>(-1)),
00294     closest_dist(vcl_numeric_limits<double>::infinity()),
00295     closest_u(0), closest_v(0) {}
00296   const imesh_vertex_array<3>& verts;
00297   const imesh_regular_face_array<3>& tris;
00298   unsigned int closest_index;
00299   double closest_dist, closest_u, closest_v;
00300 
00301   vgl_point_3d<double> closest_point() const
00302   {
00303     const imesh_regular_face<3>& tri = tris[closest_index];
00304     const double& u = closest_u;
00305     const double& v = closest_v;
00306     double t = 1.0-u-v;
00307     const imesh_vertex<3>& p0 = verts[tri[0]];
00308     const imesh_vertex<3>& p1 = verts[tri[1]];
00309     const imesh_vertex<3>& p2 = verts[tri[2]];
00310     return vgl_point_3d<double>(t*p0[0] + u*p1[0] + v*p2[0],
00311                                 t*p0[1] + u*p1[1] + v*p2[1],
00312                                 t*p0[2] + u*p1[2] + v*p2[2]);
00313   }
00314 
00315   double operator () (const vgl_point_3d<double>& pt, unsigned int i)
00316   {
00317     const imesh_regular_face<3>& tri = tris[i];
00318     double dist,u,v;
00319     /* unsigned char s = */
00320         imesh_triangle_closest_point(pt, verts[tri[0]],
00321                                      verts[tri[1]], verts[tri[2]],
00322                                      dist, u, v);
00323     if (dist < closest_dist) {
00324       closest_dist = dist;
00325       closest_index = i;
00326       closest_u = u;
00327       closest_v = v;
00328     }
00329     return dist;
00330   }
00331 };
00332 // end of namespace
00333 }
00334 
00335 //: compute the closest point on the mesh using the kd-tree
00336 //  \returns the index of the closest triangle
00337 unsigned int
00338 imesh_kd_tree_closest_point(const vgl_point_3d<double>& query,
00339                             const imesh_mesh& mesh,
00340                             const vcl_auto_ptr<imesh_kd_tree_node>& root,
00341                             vgl_point_3d<double>& cp,
00342                             vcl_vector<imesh_kd_tree_queue_entry>* dists)
00343 {
00344   const imesh_vertex_array<3>& verts = mesh.vertices<3>();
00345   const imesh_regular_face_array<3>& faces = static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00346   tri_dist_func dist(verts,faces);
00347   unsigned int ind = imesh_closest_index<tri_dist_func&>(query,root,dist,dists);
00348   cp = dist.closest_point();
00349   return ind;
00350 }
00351 
00352 
00353 //: Traverse the tree looking for leaf nodes that contain the query
00354 //  Returns a vector of leaf nodes paired with min square distance to the inner box
00355 //  Also returns a vector of the unexplored internal node-distance^2 pairs
00356 //  Resulting vectors are unsorted
00357 void imesh_kd_tree_traverse(const vgl_point_3d<double>& query,
00358                             const vcl_auto_ptr<imesh_kd_tree_node>& root,
00359                             vcl_vector<imesh_kd_tree_queue_entry>& leaf,
00360                             vcl_vector<imesh_kd_tree_queue_entry>& internal_node)
00361 {
00362   // find the root leaves containing the query point
00363   vcl_vector<imesh_kd_tree_node*> to_examine;
00364   to_examine.push_back(root.get());
00365   internal_node.clear();
00366   leaf.clear();
00367   for (unsigned int i=0; i<to_examine.size(); ++i) {
00368     imesh_kd_tree_node* current = to_examine[i];
00369     if (current->is_leaf()) {
00370       leaf.push_back(imesh_kd_tree_queue_entry(
00371                      imesh_min_sq_dist(query,current->inner_box_),
00372                      current) );
00373       continue;
00374     }
00375 
00376     if (imesh_min_sq_dist(query,current->left_->outer_box_) <=0)
00377       to_examine.push_back(current->left_.get());
00378     else
00379       internal_node.push_back(imesh_kd_tree_queue_entry(
00380                               imesh_min_sq_dist(query,current->left_->inner_box_),
00381                               current->left_.get()));
00382 
00383     if (imesh_min_sq_dist(query,current->right_->outer_box_) <=0)
00384       to_examine.push_back(current->right_.get());
00385     else
00386       internal_node.push_back(imesh_kd_tree_queue_entry(
00387                               imesh_min_sq_dist(query,current->right_->inner_box_),
00388                               current->right_.get()));
00389   }
00390 }
00391