contrib/brl/bbas/imesh/algo/imesh_imls_surface.cxx
Go to the documentation of this file.
00001 // This is brl/bbas/imesh/algo/imesh_imls_surface.cxx
00002 #include "imesh_imls_surface.h"
00003 //:
00004 // \file
00005 
00006 #include <imesh/imesh_operations.h>
00007 #include <imesh/algo/imesh_intersect.h>
00008 #include <imesh/algo/imesh_kd_tree.txx>
00009 #include <vcl_cmath.h>
00010 #include <vcl_limits.h>
00011 #include <vcl_cassert.h>
00012 
00013 #include <vcl_iostream.h>
00014 #include <vnl/vnl_math.h>
00015 #include <vnl/vnl_double_3.h>
00016 
00017 
00018 //: Constructor
00019 imesh_imls_surface::imesh_imls_surface(const imesh_mesh& mesh, double eps, double lambda,
00020                                        bool enforce_bounded,
00021                                        const vcl_set<unsigned int>& no_normal_faces)
00022   : verts_(mesh.num_verts()), phi_(mesh.num_verts(),0.0),
00023     eps2_(eps*eps), lambda_(lambda), iso_level_(0.0), bounded_(enforce_bounded)
00024 {
00025   const imesh_vertex_array<3>& v = mesh.vertices<3>();
00026 
00027   for (unsigned int i=0; i<v.size(); ++i)
00028     verts_[i] = vgl_point_3d<double>(v[i]);
00029 
00030   if (mesh.faces().regularity() != 3)
00031     triangles_ = imesh_triangulate(mesh.faces());
00032   else
00033     triangles_.reset(static_cast<imesh_regular_face_array<3>*>
00034                      (mesh.faces().clone()));
00035 
00036   if (bounded_)
00037   {
00038     // build enclosure
00039     vgl_box_3d<double> box;
00040     for (unsigned int i=0; i<verts_.size(); ++i) {
00041       box.add(verts_[i]);
00042     }
00043     box.expand_about_centroid(1);
00044     unsigned int base = verts_.size();
00045     verts_.push_back(vgl_point_3d<double>(box.min_x(),box.min_y(),box.min_z()));
00046     verts_.push_back(vgl_point_3d<double>(box.min_x(),box.min_y(),box.max_z()));
00047     verts_.push_back(vgl_point_3d<double>(box.min_x(),box.max_y(),box.min_z()));
00048     verts_.push_back(vgl_point_3d<double>(box.min_x(),box.max_y(),box.max_z()));
00049     verts_.push_back(vgl_point_3d<double>(box.max_x(),box.min_y(),box.min_z()));
00050     verts_.push_back(vgl_point_3d<double>(box.max_x(),box.min_y(),box.max_z()));
00051     verts_.push_back(vgl_point_3d<double>(box.max_x(),box.max_y(),box.min_z()));
00052     verts_.push_back(vgl_point_3d<double>(box.max_x(),box.max_y(),box.max_z()));
00053     triangles_->push_back(imesh_tri(base+0,base+1,base+2));
00054     triangles_->push_back(imesh_tri(base+1,base+3,base+2));
00055     triangles_->push_back(imesh_tri(base+0,base+6,base+4));
00056     triangles_->push_back(imesh_tri(base+0,base+2,base+6));
00057     triangles_->push_back(imesh_tri(base+2,base+7,base+6));
00058     triangles_->push_back(imesh_tri(base+2,base+3,base+7));
00059     triangles_->push_back(imesh_tri(base+3,base+5,base+7));
00060     triangles_->push_back(imesh_tri(base+3,base+1,base+5));
00061     triangles_->push_back(imesh_tri(base+1,base+4,base+5));
00062     triangles_->push_back(imesh_tri(base+1,base+0,base+4));
00063     triangles_->push_back(imesh_tri(base+4,base+7,base+5));
00064     triangles_->push_back(imesh_tri(base+4,base+6,base+7));
00065     phi_.resize(verts_.size(),1.0);
00066   }
00067 
00068   vcl_vector<vgl_box_3d<double> > boxes(triangles_->size());
00069   for ( unsigned int i=0; i<boxes.size(); ++i ) {
00070     const imesh_regular_face<3>& tri = (*triangles_)[i];
00071     boxes[i].add(verts_[tri[0]]);
00072     boxes[i].add(verts_[tri[1]]);
00073     boxes[i].add(verts_[tri[2]]);
00074   }
00075 
00076   kd_tree_ = imesh_build_kd_tree(boxes);
00077   unsigned num_tree_nodes = triangles_->size()*2-1;
00078 
00079   unweighted_.resize(num_tree_nodes);
00080   centroid_.resize(num_tree_nodes);
00081   normals_.resize(num_tree_nodes);
00082   normal_len_.resize(num_tree_nodes);
00083   area_.resize(num_tree_nodes);
00084 
00085 
00086   compute_centroids_rec(kd_tree_, no_normal_faces);
00087   compute_unweighed_rec(kd_tree_);
00088 
00089   compute_enclosing_phi();
00090 }
00091 
00092 
00093 //: Copy Constructor
00094 imesh_imls_surface::imesh_imls_surface(const imesh_imls_surface& other)
00095   : verts_(other.verts_),
00096     triangles_(other.triangles_.get() ?
00097                new imesh_regular_face_array<3>(*other.triangles_) :
00098                0),
00099     kd_tree_(other.kd_tree_.get() ?
00100              new imesh_kd_tree_node(*other.kd_tree_) :
00101              0),
00102     phi_(other.phi_),
00103     area_(other.area_),
00104     unweighted_(other.unweighted_),
00105     centroid_(other.centroid_),
00106     normals_(other.normals_),
00107     normal_len_(other.normal_len_),
00108     eps2_(other.eps2_),
00109     lambda_(other.lambda_),
00110     iso_level_(other.iso_level_)
00111 {
00112 }
00113 
00114 
00115 //: compute the iso value such that the mean value at the vertices is zero
00116 void imesh_imls_surface::compute_iso_level()
00117 {
00118   double mean = 0.0;
00119   for (unsigned int i=0; i<verts_.size(); ++i)
00120     mean += (*this)(verts_[i]);
00121   iso_level_ = mean / verts_.size();
00122 }
00123 
00124 
00125 //: adjust the phi values until all vertices are within the iso surface
00126 // Also computes the iso level
00127 void imesh_imls_surface::compute_enclosing_phi()
00128 {
00129   typedef vcl_pair<unsigned int,double> pair_id;
00130   vcl_vector<pair_id> outside;
00131   double mean = 0.0;
00132   const unsigned int num_verts = bounded_ ?
00133                                  verts_.size() :
00134                                  verts_.size() - 8;
00135   for (unsigned int i=0; i<num_verts; ++i) {
00136     double val = (*this)(verts_[i]);
00137     mean += val;
00138     if (val > 0)
00139       outside.push_back(pair_id(i,val));
00140   }
00141   iso_level_ = mean / verts_.size();
00142 
00143   for (double s=1.0; !outside.empty(); s*=2) {
00144     vcl_cout << outside.size() << " outside" << vcl_endl;
00145     for (vcl_vector<pair_id>::const_iterator i=outside.begin();
00146          i!=outside.end(); ++i) {
00147       phi_[i->first] -= s*i->second;
00148     }
00149     compute_unweighed_rec(kd_tree_);
00150     vcl_vector<pair_id> next_outside;
00151     for (vcl_vector<pair_id>::const_iterator i=outside.begin();
00152          i!=outside.end(); ++i) {
00153       double val = (*this)(verts_[i->first]);
00154       if (val > vcl_abs(vcl_numeric_limits<double>::epsilon()*phi_[i->first])) {
00155         next_outside.push_back(pair_id(i->first,val));
00156       }
00157     }
00158     outside.swap(next_outside);
00159   }
00160 }
00161 
00162 
00163 //: recursively compute the area weighted centroids
00164 void imesh_imls_surface::
00165 compute_centroids_rec(const vcl_auto_ptr<imesh_kd_tree_node>& node,
00166                       const vcl_set<unsigned int>& no_normal_faces)
00167 {
00168   const unsigned int& i = node->index_;
00169   if (node->is_leaf()) {
00170     const imesh_regular_face<3>& tri = (*triangles_)[i];
00171     const vgl_point_3d<double>& v0 = verts_[tri[0]];
00172     const vgl_point_3d<double>& v1 = verts_[tri[1]];
00173     const vgl_point_3d<double>& v2 = verts_[tri[2]];
00174     vgl_vector_3d<double>& n = normals_[i];
00175     n = cross_product(v1-v0,v2-v0)/2.0;
00176     normal_len_[i] = n.length();
00177     area_[i] = normal_len_[i];
00178     centroid_[i] = centre(v0,v1,v2);
00179     if (no_normal_faces.find(i) != no_normal_faces.end()) {
00180       n = vgl_vector_3d<double>(0,0,0);
00181       normal_len_[i] = 0.0;
00182     }
00183     return;
00184   }
00185 
00186   compute_centroids_rec(node->left_,no_normal_faces);
00187   compute_centroids_rec(node->right_,no_normal_faces);
00188 
00189   const unsigned int& i_left = node->left_->index_;
00190   const unsigned int& i_right = node->right_->index_;
00191 
00192   const vgl_point_3d<double>& cl = centroid_[i_left];
00193   const double& al = area_[i_left];
00194   const vgl_point_3d<double>& cr = centroid_[i_right];
00195   const double& ar = area_[i_right];
00196   double& at = area_[i];
00197   at = al + ar;
00198   centroid_[i].set((ar*cr.x() + al*cl.x()) / at,
00199                    (ar*cr.y() + al*cl.y()) / at,
00200                    (ar*cr.z() + al*cl.z()) / at);
00201   //normals_[i] = normalized(normals_[i_left]*al + normals_[i_right]*ar);
00202   normals_[i] = normals_[i_left] + normals_[i_right];
00203   normal_len_[i] = normals_[i].length();
00204 }
00205 
00206 
00207 //: recursively compute the unweighted integrals
00208 void imesh_imls_surface::
00209 compute_unweighed_rec(const vcl_auto_ptr<imesh_kd_tree_node>& node)
00210 {
00211   const unsigned int& i = node->index_;
00212   if (node->is_leaf()) {
00213     const imesh_regular_face<3>& tri = (*triangles_)[i];
00214     unweighted_[i] = (phi_[tri[0]]
00215                     + phi_[tri[1]]
00216                     + phi_[tri[2]])/3.0 * area_[i];
00217           //- dot_product(normals_[i],centroid_[i]-vgl_point_3d<double>(0,0,0));
00218     return;
00219   }
00220 
00221   compute_unweighed_rec(node->left_);
00222   compute_unweighed_rec(node->right_);
00223 
00224   const unsigned int& i_left = node->left_->index_;
00225   const unsigned int& i_right = node->right_->index_;
00226   unweighted_[i] = unweighted_[i_left] + unweighted_[i_right];
00227 }
00228 
00229 
00230 //: return a bounding box for the original input mesh
00231 vgl_box_3d<double> imesh_imls_surface::bounding_box() const
00232 {
00233   return kd_tree_->inner_box_;
00234 }
00235 
00236 
00237 //: change the epsilon (smoothness) of the surface
00238 void imesh_imls_surface::set_epsilon(double eps)
00239 {
00240   eps2_ = eps*eps;
00241   iso_level_ = 0.0;
00242 
00243   // reset the phi values at each vertex
00244   for (unsigned i=0; i<phi_.size(); ++i)
00245     phi_[i] = 0.0;
00246 
00247   compute_unweighed_rec(kd_tree_);
00248 
00249   compute_enclosing_phi();
00250 }
00251 
00252 
00253 namespace {
00254 class tri_dist_func
00255 {
00256  public:
00257   tri_dist_func(const vcl_vector<vgl_point_3d<double> >& v,
00258                 const imesh_regular_face_array<3>& t)
00259   : verts(v), tris(t), closest_index(static_cast<unsigned int>(-1)),
00260     closest_dist(vcl_numeric_limits<double>::infinity()) {}
00261   const vcl_vector<vgl_point_3d<double> >& verts;
00262   const imesh_regular_face_array<3>& tris;
00263   unsigned int closest_index;
00264   double closest_dist, closest_u, closest_v;
00265 
00266   double operator () (const vgl_point_3d<double>& pt, unsigned int i)
00267   {
00268     const imesh_regular_face<3>& tri = tris[i];
00269     double dist,u,v;
00270     /* unsigned char s = */
00271     imesh_triangle_closest_point(pt,
00272                                  verts[tri[0]], verts[tri[1]], verts[tri[2]],
00273                                  dist, u, v);
00274     if (dist < closest_dist) {
00275       closest_dist = dist;
00276       closest_index = i;
00277       closest_u = u;
00278       closest_v = v;
00279     }
00280     return dist;
00281   }
00282 };
00283 // end of namespace
00284 }
00285 
00286 
00287 //: evaluate the implicit surface at a point
00288 double imesh_imls_surface::operator() (const vgl_point_3d<double>& p) const
00289 {
00290   double sum=0.0, sum_phi = 0.0;
00291 
00292   vcl_vector<imesh_kd_tree_queue_entry> remain;
00293   tri_dist_func dist(verts_,*triangles_);
00294   /* unsigned int ind = */
00295   imesh_closest_index<tri_dist_func&>(p,kd_tree_,dist,&remain);
00296 
00297   // compute the (negative) maximum error of integration
00298   // stored negative so that the max error is first when sorted by <
00299   vcl_vector<imesh_kd_tree_queue_entry>::iterator itr = remain.begin();
00300   for (; itr != remain.end(); ++itr) {
00301     double min = w2(itr->val_);
00302     double max = w2(imesh_max_sq_dist(p,itr->node_->inner_box_));
00303     itr->val_ = (max - min)*area_[itr->node_->index_];
00304   }
00305   vcl_make_heap( remain.begin(), remain.end() );
00306 
00307   vcl_pop_heap( remain.begin(), remain.end() );
00308   while (!remain.empty() && -remain.back().val_ > lambda_*sum)
00309   {
00310     imesh_kd_tree_node* current = remain.back().node_;
00311     remain.pop_back();
00312 
00313     if (current->is_leaf())
00314     {
00315       unsigned int i = current->index_;
00316       const imesh_regular_face<3>& tri = (*triangles_)[i];
00317       unsigned int i1=tri[0], i2=tri[1], i3=tri[2];
00318       typedef vgl_vector_2d<double> T;
00319       typedef vgl_vector_2d<double> (*F) (const vgl_point_3d<double>&, const vgl_point_3d<double>&,
00320                                           const vgl_point_3d<double>&, const vgl_point_3d<double>&,
00321                                           double, double, double, double);
00322       vgl_vector_2d<double> I = triangle_quadrature<T,F>(split_triangle_quadrature,
00323                                                          p,verts_[i1],verts_[i2],verts_[i3],
00324                                                          normals_[i]*2.0,
00325                                                          phi_[i1],phi_[i2],phi_[i3],eps2_);
00326       assert(!vnl_math_isnan(I.x()) && !vnl_math_isnan(I.y()));
00327 
00328       sum_phi += I.x();
00329       if (normal_len_[i]>0.0)
00330         sum_phi += dot_product(normals_[i],p-verts_[i1]) * I.y() / normal_len_[i];
00331       sum += I.y();
00332     }
00333     else
00334     {
00335       double min = w2(imesh_min_sq_dist(p,current->left_->inner_box_));
00336       double max = w2(imesh_max_sq_dist(p,current->left_->inner_box_));
00337       remain.push_back(imesh_kd_tree_queue_entry((max - min)*area_[current->left_->index_],
00338                                                  current->left_.get()));
00339       vcl_push_heap(remain.begin(), remain.end());
00340 
00341       min = w2(imesh_min_sq_dist(p,current->right_->inner_box_));
00342       max = w2(imesh_max_sq_dist(p,current->right_->inner_box_));
00343       remain.push_back(imesh_kd_tree_queue_entry((max - min)*area_[current->right_->index_],
00344                                                  current->right_.get()));
00345       vcl_push_heap(remain.begin(), remain.end());
00346     }
00347     if (!remain.empty())
00348       vcl_pop_heap( remain.begin(), remain.end() );
00349   }
00350 
00351   // approximate the contribution from the remain nodes
00352   for (itr = remain.begin(); itr != remain.end(); ++itr) {
00353     // Use approximation assuming the weight is constant
00354     unsigned int i = itr->node_->index_;
00355     vgl_vector_3d<double> v(p-centroid_[i]);
00356     double w = w2(v.sqr_length());
00357 
00358     sum_phi += w*unweighted_[i] + dot_product(normals_[i],v)*w;
00359     sum += w*area_[i];
00360   }
00361 
00362   assert(sum != 0.0);
00363   return sum_phi / sum - iso_level_;
00364 }
00365 
00366 
00367 //: evaluate the function and its derivative (returned by reference)
00368 double imesh_imls_surface::deriv(const vgl_point_3d<double>& p,
00369                                  vgl_vector_3d<double>& dp) const
00370 {
00371   integral_data sums;
00372 
00373   vcl_vector<imesh_kd_tree_queue_entry> remain;
00374   tri_dist_func dist(verts_,*triangles_);
00375   /* unsigned int ind = */
00376   imesh_closest_index<tri_dist_func&>(p,kd_tree_,dist,&remain);
00377 
00378   // compute the (negative) maximum error of integration
00379   // stored negative so that the max error is first when sorted by <
00380   vcl_vector<imesh_kd_tree_queue_entry>::iterator itr = remain.begin();
00381   for (; itr != remain.end(); ++itr) {
00382     double min = w2(itr->val_);
00383     double max = w2(imesh_max_sq_dist(p,itr->node_->inner_box_));
00384     itr->val_ = (max - min)*area_[itr->node_->index_];
00385   }
00386   vcl_make_heap( remain.begin(), remain.end() );
00387 
00388   vcl_pop_heap( remain.begin(), remain.end() );
00389   while (!remain.empty() && -remain.back().val_ > lambda_*sums.I)
00390   {
00391     imesh_kd_tree_node* current = remain.back().node_;
00392     remain.pop_back();
00393 
00394     if (current->is_leaf())
00395     {
00396       unsigned int i = current->index_;
00397       const imesh_regular_face<3>& tri = (*triangles_)[i];
00398       unsigned int i1=tri[0], i2=tri[1], i3=tri[2];
00399       typedef integral_data T;
00400       typedef integral_data (*F) (const vgl_point_3d<double>&, const vgl_point_3d<double>&,
00401                                   const vgl_point_3d<double>&, const vgl_point_3d<double>&,
00402                                   double, double, double, double);
00403       integral_data Id = triangle_quadrature<T,F>(split_triangle_quadrature_with_deriv,
00404                                                   p,verts_[i1],verts_[i2],verts_[i3],
00405                                                   normals_[i]*2.0,
00406                                                   phi_[i1],phi_[i2],phi_[i3],eps2_);
00407       assert(!vnl_math_isnan(Id.I) && !vnl_math_isnan(Id.I_phi));
00408       //assert(!vnl_math_isnan(Id.dI) && !vnl_math_isnan(Id.dI_phi));
00409 
00410       sums.I += Id.I;
00411       sums.I_phi += Id.I_phi;
00412       sums.dI += Id.dI;
00413       sums.dI_phi += Id.dI_phi;
00414       // terms involving the normal constraints
00415       if (normal_len_[i]>0.0) {
00416         const vgl_vector_3d<double>& n = normals_[i];
00417         double plane_dist = dot_product(n,p-verts_[i1])/normal_len_[i];
00418         sums.I_phi += plane_dist*Id.I;
00419         sums.dI_phi += n*(Id.I/normal_len_[i]) + plane_dist*Id.dI;
00420       }
00421     }
00422     else
00423     {
00424       double min = w2(imesh_min_sq_dist(p,current->left_->inner_box_));
00425       double max = w2(imesh_max_sq_dist(p,current->left_->inner_box_));
00426       remain.push_back(imesh_kd_tree_queue_entry((max - min)*area_[current->left_->index_],
00427                                                  current->left_.get()));
00428       vcl_push_heap(remain.begin(), remain.end());
00429 
00430       min = w2(imesh_min_sq_dist(p,current->right_->inner_box_));
00431       max = w2(imesh_max_sq_dist(p,current->right_->inner_box_));
00432       remain.push_back(imesh_kd_tree_queue_entry((max - min)*area_[current->right_->index_],
00433                                                  current->right_.get()));
00434       vcl_push_heap(remain.begin(), remain.end());
00435     }
00436     if (!remain.empty())
00437       vcl_pop_heap( remain.begin(), remain.end() );
00438   }
00439 
00440   // approximate the contribution from the remain nodes
00441   for (itr = remain.begin(); itr != remain.end(); ++itr) {
00442     // Use approximation assuming the weight is constant
00443     unsigned int i = itr->node_->index_;
00444     vgl_vector_3d<double> v(p-centroid_[i]);
00445     double w = 1.0/(v.sqr_length() + eps2_);
00446     vgl_vector_3d<double> dw(v);
00447     dw *= -4*w;
00448     w *= w;
00449     dw *= w;
00450 
00451     const vgl_vector_3d<double>& n = normals_[i];
00452     double plane_dist = dot_product(n,v);
00453     sums.I += w*area_[i];
00454     sums.I_phi += w*unweighted_[i] + plane_dist*w;
00455     sums.dI += dw*area_[i];
00456     sums.dI_phi += n*w + dw*(unweighted_[i] + plane_dist);
00457   }
00458 
00459 
00460   assert(sums.I != 0.0);
00461   dp = (-sums.I_phi/(sums.I*sums.I))*sums.dI + sums.dI_phi/sums.I;
00462   return sums.I_phi / sums.I - iso_level_;
00463 }
00464 
00465 
00466 //: evaluate the function and its first and second derivatives (returned by reference)
00467 double imesh_imls_surface::deriv2(const vgl_point_3d<double>& p,
00468                                   vgl_vector_3d<double>& dp,
00469                                   vnl_double_3x3& ddp) const
00470 {
00471   double eps = 1e-8;
00472   double val = this->deriv(p,dp);
00473   vgl_vector_3d<double> dpx,dpy,dpz;
00474   this->deriv(p+vgl_vector_3d<double>(eps,0,0),dpx);
00475   this->deriv(p+vgl_vector_3d<double>(0,eps,0),dpy);
00476   this->deriv(p+vgl_vector_3d<double>(0,0,eps),dpz);
00477   dpx -= dp;
00478   dpx /= eps;
00479   dpy -= dp;
00480   dpy /= eps;
00481   dpz -= dp;
00482   dpz /= eps;
00483   ddp(0,0) = dpx.x();   ddp(0,1) = dpy.x();   ddp(0,2) = dpz.x();
00484   ddp(1,0) = dpx.y();   ddp(1,1) = dpy.y();   ddp(1,2) = dpz.y();
00485   ddp(2,0) = dpx.z();   ddp(2,1) = dpy.z();   ddp(2,2) = dpz.z();
00486   return val;
00487 }
00488 
00489 //: integrals of f(x)dx and x*f(x)dx over [0,1] where f(x)= 1/((x+k1)^2 + k2)^2
00490 //
00491 //  These equations are wrong in the paper, they should be (for a=1):
00492 // Beta = atan( k1/sqrt(k2) ) - atan( (k1+1)/sqrt(k2) )
00493 // I1 = -Beta * 1/(2*k2^(3/2))  + (k2 - k1*(k1+1)) / (2*k2*(k1^2+k2)*((k1+1)^2+k2))
00494 // Ix = Beta * k1/(2*k2^(3/2))  + (k1 + 1) / (2*k2*((k1+1)^2+k2))
00495 void
00496 imesh_imls_surface::line_integrals(double k1, double k2, double& I1, double& Ix)
00497 {
00498   double sqrt_k2 = vcl_sqrt(k2);
00499   double k1_p1 = k1+1.0;
00500   I1 = Ix = (vcl_atan(k1_p1/sqrt_k2) - vcl_atan(k1/sqrt_k2) ) / (2.0*k2*sqrt_k2);
00501   Ix *= -k1;
00502 
00503   double denom = 1.0/(2.0*k2*(k1_p1*k1_p1+k2));
00504 
00505   Ix += k1_p1 * denom;
00506   I1 += (k2 - k1*k1_p1)*denom / (k1*k1+k2);
00507 }
00508 
00509 
00510 //: integrals of f(x)dx and x*f(x)dx over [0,1] where f(x)= 1/((x+k1)^2 + k2)^2
00511 //  Also compute the integrals when f(x)=1/((x+k1)^2 + k2)^3 (for use in derivatives)
00512 //
00513 // Beta = atan( k1/sqrt(k2) ) - atan( (k1+1)/sqrt(k2) )
00514 // I1 = -Beta * 1/(2*k2^(3/2))  + (k2 - k1*(k1+1)) / (2*k2*(k1^2+k2)*((k1+1)^2+k2))
00515 // Ix = Beta * k1/(2*k2^(3/2))  + (k1 + 1) / (2*k2*((k1+1)^2+k2))
00516 // dI1 = 1/8 * ( -Beta*3/k2^(5/2) +
00517 //               (5*k2^3 - (k1*(k1+1)-3)*k2^2 - k1*(k1+1)*(9*k1*(k1+1)+5)*k2 - 3*k1^3*(k1+1)^3)
00518 //               / (k2^2*(k1^2+k2)^2*((k1+1)^2+k2)^2) )
00519 // dIx = 1/8 * ( Beta*3*k1/k2^(5/2) +
00520 //               ((k1^2+k2)*((3*(k1+1)+1)*k2^2 + (k1+1)*(6*k1^2+3*k1+2)*k2 + 3*k1^2*(k1+1)^3))
00521 //               /(k2^2*(k1^2+k2)^2*((k1+1)^2+k2)^2)  )
00522 // dIx2 = 1/8 * ( -Beta*(3*k1^2+k2)/k2^(5/2) -
00523 //                ((k1^2+k2)^2*(k2^2 + (k1+1)*(4*k1-1)*k2 + 3*k1*(k1+1)^3))
00524 //                /(k2^2*(k1^2+k2)^2*((k1+1)^2+k2)^2)  )
00525 void
00526 imesh_imls_surface::line_integrals(double k1, double k2,
00527                                    double& I1, double& Ix,
00528                                    double& dI1, double& dIx, double& dIx2)
00529 {
00530   double sqrt_k2 = vcl_sqrt(k2);
00531   double k1_p1 = k1+1.0;
00532   double k1_2 = k1*k1;
00533   double k2_2 = k2*k2;
00534   double k1_p1_2 = k1_p1*k1_p1;
00535   double t1 = k2 + k1_p1_2;
00536   double t2 = k2 + k1_2;
00537   double t3 = 3*k1_p1_2*k1_p1*k1;
00538 
00539 
00540   I1 = dI1 = (vcl_atan(k1_p1/sqrt_k2) - vcl_atan(k1/sqrt_k2) ) / (2.0*k2*sqrt_k2);
00541   dI1 *= 0.75/k2;
00542   Ix = -k1 * I1;
00543   dIx = -k1 * dI1;
00544   dIx2 = 0.25*I1*(3*k1_2+k2)/k2;
00545 
00546 
00547   double denom = 0.5/(k2*t1);
00548   double ddenom = 0.125/(k2_2*t2*t2*t1*t1);
00549 
00550   I1 += (k2 - k1*k1_p1)*denom / t2;
00551   Ix += k1_p1 * denom;
00552 
00553   dI1 += (5*k2*k2_2 - (k1*k1_p1-3)*k2_2 - k1*k1_p1*(9*k1*k1_p1+5)*k2 - k1_2*t3 )*ddenom;
00554   dIx += t2*((3*k1_p1+1)*k2_2 + k1_p1*(6*k1_2+3*k1+2)*k2 + k1*t3)*ddenom;
00555   dIx2 -= t2*t2*(k2_2 + k1_p1*(4*k1-1)*k2 + t3)*ddenom;
00556 }
00557 
00558 
00559 //: line integral of the squared weight function times a linear value on the line from p0 to p1
00560 //  (value at p0 is v0 and at p1 is v1)
00561 //  \a eps2 is epsilon^2
00562 double
00563 imesh_imls_surface::line_integral(const vgl_point_3d<double>& x,
00564                                   const vgl_point_3d<double>& p0,
00565                                   const vgl_point_3d<double>& p1,
00566                                   double v0, double v1, double eps2)
00567 {
00568   vgl_vector_3d<double> ab(p1-p0);
00569   vgl_vector_3d<double> xa(p0-x);
00570   double denom = 1.0/ab.sqr_length();
00571   double k1 = dot_product(ab,xa)*denom;
00572   double k2 = (xa.sqr_length()+eps2)*denom - k1*k1;
00573   double I1,Ix;
00574   line_integrals(k1,k2,I1,Ix);
00575   return (v0*I1 + (v1-v0)*Ix)*vcl_sqrt(denom)*denom;
00576 }
00577 
00578 
00579 //: The derivative of the line integral with respect to x
00580 vgl_vector_3d<double>
00581 imesh_imls_surface::line_integral_deriv(const vgl_point_3d<double>& x,
00582                                         const vgl_point_3d<double>& p0,
00583                                         const vgl_point_3d<double>& p1,
00584                                         double v0, double v1, double eps2)
00585 {
00586   vgl_vector_3d<double> ab(p1-p0);
00587   vgl_vector_3d<double> xa(p0-x);
00588   double denom = 1.0/ab.sqr_length();
00589   double k1 = dot_product(ab,xa)*denom;
00590   double k2 = (xa.sqr_length()+eps2)*denom - k1*k1;
00591   double I1,Ix,dI1,dIx,dIx2;
00592   line_integrals(k1,k2,I1,Ix,dI1,dIx,dIx2);
00593   denom = 4*vcl_sqrt(denom)*denom*denom;
00594 
00595   return (xa*(v0*dI1+(v1-v0)*dIx)
00596         + ab*(v0*dIx + (v1-v0)*dIx2))*denom;//4*vcl_sqrt(denom)*denom*denom;
00597 }
00598 
00599 
00600 //: area integral of the squared weight function times a linearly interpolated value
00601 //  \a m is the point closest point on the triangle to sample point \a x
00602 //  \a p0 and \a p1 are the other vertices
00603 //  call triangle_quadrature to first split an arbitrary triangle
00604 //  \a eps2 is epsilon^2
00605 vgl_vector_2d<double>
00606 imesh_imls_surface::split_triangle_quadrature(const vgl_point_3d<double>& x,
00607                                               const vgl_point_3d<double>& pm,
00608                                               const vgl_point_3d<double>& p1,
00609                                               const vgl_point_3d<double>& p2,
00610                                               double vm, double v1, double v2,
00611                                               double eps)
00612 {
00613   vgl_point_3d<double> pp(p1), pn(p2);
00614   double vp(v1), vn(v2);
00615   if ((pp-x).length() > (pn-x).length()) {
00616     // swap so that pp is closest to x
00617     pp = p2; pn = p1;
00618     vp = v2; vn = v1;
00619   }
00620 
00621   vgl_vector_3d<double> d1(pp-pm);
00622   vgl_vector_3d<double> d2(pn-pm);
00623   vgl_vector_3d<double> d3(pm-x);
00624 
00625   double t1 = d1.sqr_length();
00626   double t2 = dot_product(d1,d2);
00627   double t3 = dot_product(d1,d3);
00628   double t4 = d2.sqr_length();
00629   double t5 = dot_product(d2,d3) * 2;
00630   double t6 = d3.sqr_length() + eps;
00631 
00632   // compute height (divided by 2*sqrt(t1))
00633   // early exit if triangle flat
00634   double height = t4/t1 - t2*t2/(t1*t1);
00635   if (!(height > 0.0))
00636     return vgl_vector_2d<double>(0,0);
00637   height = vcl_sqrt(height)/2.0;
00638 
00639   double vt1 = vn-vm;
00640   double vt2 = vp-vm;
00641 
00642   double alpha = 2.0/3.0;
00643   double sum1 = 0.0, sum2 = 0.0;
00644   double weight = (1.0/alpha-alpha);
00645   double I1,Ix,u_1,denom,k1,k2;
00646   double u = alpha;
00647   double last_li1 = 0.0, last_li2 = 0.0;
00648   // integrate using the trapezoid rule with non-uniform sampling
00649   for (; u>0.01; u*=alpha) {
00650     sum1 += last_li1;
00651     sum2 += last_li2;
00652     u_1 = 1.0-u;
00653     denom = 1.0/(u_1*u_1*t1);
00654     k1 = (t3 + u*t2)*u_1*denom;
00655     k2 = (t6 + u*t5 + u*u*t4)*denom - k1*k1;
00656     line_integrals(k1,k2,I1,Ix);
00657     denom *= u / u_1;
00658     last_li1 = ((vm+u*vt1)*I1 + u_1*vt2*Ix) * denom;
00659     last_li2 = I1 * denom;
00660   }
00661   sum1 *= weight;
00662   sum1 += last_li1/alpha;
00663   sum2 *= weight;
00664   sum2 += last_li2/alpha;
00665 
00666   // add the last trapezoid covering the remaining area
00667   denom = 1.0/t1;
00668   k1 = t3*denom;
00669   k2 = t6*denom - k1*k1;
00670   line_integrals(k1,k2,I1,Ix);
00671   denom *= u/alpha;
00672   sum1 += (vm*I1 + vt2*Ix) * denom;
00673   sum2 += I1 * denom;
00674 
00675   sum1 *= height;
00676   sum2 *= height;
00677 
00678 
00679   return vgl_vector_2d<double>(sum1,sum2);
00680 }
00681 
00682 
00683 //: area integral of the squared weight function times a linearly interpolated value
00684 //  Also computes vector term used in the derivative
00685 //  \a m is the point closest point on the triangle to sample point \a x
00686 //  \a p0 and \a p1 are the other vertices
00687 //  call triangle_quadrature to first split an arbitrary triangle
00688 //  \a eps2 is epsilon^2
00689 imesh_imls_surface::integral_data imesh_imls_surface::
00690 split_triangle_quadrature_with_deriv(const vgl_point_3d<double>& x,
00691                                      const vgl_point_3d<double>& pm,
00692                                      const vgl_point_3d<double>& p1,
00693                                      const vgl_point_3d<double>& p2,
00694                                      double vm, double v1, double v2,
00695                                      double eps)
00696 {
00697   vgl_point_3d<double> pp(p1), pn(p2);
00698   double vp(v1), vn(v2);
00699   if ((pp-x).length() > (pn-x).length()) {
00700     // swap so that pp is closest to x
00701     pp = p2; pn = p1;
00702     vp = v2; vn = v1;
00703   }
00704 
00705   vgl_vector_3d<double> d1(pp-pm);
00706   vgl_vector_3d<double> d2(pn-pm);
00707   vgl_vector_3d<double> d3(pm-x);
00708 
00709   double t1 = d1.sqr_length();
00710   double t2 = dot_product(d1,d2);
00711   double t3 = dot_product(d1,d3);
00712   double t4 = d2.sqr_length();
00713   double t5 = dot_product(d2,d3) * 2;
00714   double t6 = d3.sqr_length() + eps;
00715 
00716   // compute height (divided by 2*sqrt(t1))
00717   // early exit if triangle flat
00718   double height = t4/t1 - t2*t2/(t1*t1);
00719   if (!(height > 0.0))
00720     return integral_data();
00721   height = vcl_sqrt(height)/2.0;
00722 
00723   double vt1 = vn-vm;
00724   double vt2 = vp-vm;
00725 
00726   double alpha = 2.0/3.0;
00727   integral_data i_data, last_i_data;
00728   double weight = (1.0/alpha-alpha);
00729   double I1,Ix,dI1,dIx,dIx2,u_1,denom,k1,k2;
00730   double u = alpha;
00731   // integrate using the trapezoid rule with non-uniform sampling
00732   const double lower_bound = 0.01;//((t6<t4)?(t6/t4):1.0) * 0.01;
00733   for (; u>lower_bound; u*=alpha) {
00734     i_data += last_i_data;
00735     u_1 = 1.0-u;
00736     denom = 1.0/(u_1*u_1*t1);
00737     k1 = (t3 + u*t2)*u_1*denom;
00738     k2 = (t6 + u*t5 + u*u*t4)*denom - k1*k1;
00739     line_integrals(k1,k2,I1,Ix,dI1,dIx,dIx2);
00740     double phi_c = vm+u*vt1;
00741     double phi_x = u_1*vt2;
00742     last_i_data.I = I1;
00743     last_i_data.I_phi = (phi_c*I1 + phi_x*Ix);
00744 
00745     vgl_vector_3d<double> d_c(d3+u*d2), d_x(d1*u_1);
00746     last_i_data.dI = (d_c*dI1 + d_x*dIx)*denom;
00747     last_i_data.dI_phi = ( d_c*(phi_c*dI1 + phi_x*dIx)
00748                          + d_x*(phi_c*dIx + phi_x*dIx2) ) * denom;
00749 
00750     denom *= u / u_1;
00751     last_i_data *= denom;
00752   }
00753   i_data *= weight;
00754   last_i_data *= 1.0/alpha;
00755   i_data += last_i_data;
00756 
00757 
00758   // add the last trapezoid covering the remaining area
00759   denom = 1.0/t1;
00760   k1 = t3*denom;
00761   k2 = t6*denom - k1*k1;
00762   line_integrals(k1,k2,I1,Ix,dI1,dIx,dIx2);
00763   denom *= u/alpha;
00764 
00765   i_data.I += I1 * denom;
00766   i_data.I_phi += (vm*I1 + vt2*Ix) * denom;
00767   denom /= t1;
00768   i_data.dI += (d3*dI1 + d1*dIx)*denom;
00769   i_data.dI_phi += ( d3*(vm*dI1 + vt2*dIx)
00770                    + d1*(vm*dIx + vt2*dIx2) ) * denom;
00771 
00772   i_data *= height;
00773   i_data.dI *= 4.0;
00774   i_data.dI_phi *= 4.0;
00775 
00776   return i_data;
00777 }
00778 
00779 
00780 //=============================================================================
00781 // External functions
00782 
00783 //: find the zero crossing point by bisection between positive point \a pp and negative point \a pn
00784 //  Stops searching when $||pp-pn|| < xeps$ or $|f(pm)| < feps$
00785 vgl_point_3d<double> bisect(const imesh_imls_surface& f,
00786                             vgl_point_3d<double> pp,
00787                             vgl_point_3d<double> pn,
00788                             double feps, double xeps)
00789 {
00790   assert(f(pp) > 0.0);
00791   assert(f(pn) < 0.0);
00792   vgl_point_3d<double> pm=centre(pp,pn);
00793   const unsigned num_itr =
00794       static_cast<unsigned>(vcl_ceil(vcl_log((pp-pn).length()
00795                                               / xeps)
00796                                      / 0.301029995663981)); // log_2
00797   vgl_vector_3d<double> dp;
00798   double val = f.deriv(pm,dp);
00799   val /= dp.length();
00800   for (unsigned int i=0; i<num_itr; ++i) {
00801     if (vcl_abs(val) < feps)
00802       return pm;
00803     else if (val > 0.0)
00804       pp = pm;
00805     else
00806       pn = pm;
00807     pm=centre(pp,pn);
00808     val = f.deriv(pm,dp);
00809     val /= dp.length();
00810   }
00811   return pm;
00812 }
00813 
00814 
00815 //: Move the point \a p along the gradient direction until reaching a zero crossing of \a f (within \a eps).
00816 //  Return true if successful
00817 bool snap_to_surface(const imesh_imls_surface& f,
00818                      vgl_point_3d<double>& p,
00819                      double step, double eps)
00820 {
00821   vgl_point_3d<double> p1(p);
00822   vgl_vector_3d<double> dp;
00823   double val1 = f.deriv(p1,dp);
00824   double dl = dp.length();
00825   val1 /= dl;
00826   if (vcl_abs(val1) < eps)
00827     return true;
00828 
00829   vgl_point_3d<double> p2 = p1 - (step*val1/dl)*dp;
00830   dl = dp.length();
00831   double val2 = f.deriv(p2,dp);
00832   val2 /= dl;
00833   unsigned int i=0;
00834   for (; i<100 && val1*val2 > 0.0; ++i) {
00835     p1 = p2;
00836     val1 = val2;
00837     p2 -= (step*val2/dl)*dp;
00838     val2 = f.deriv(p2,dp);
00839     dl = dp.length();
00840     val2 /= dl;
00841     if (vcl_abs(val2) < eps) {
00842       p = p2;
00843       return true;
00844     }
00845   }
00846   if (i >= 100)
00847     return false;
00848 
00849   if (val1 > 0.0)
00850     p = bisect(f,p1,p2,eps);
00851   else
00852     p = bisect(f,p2,p1,eps);
00853 
00854 
00855   return true;
00856 }
00857 
00858 
00859 namespace{
00860 double func(const vgl_vector_3d<double>& n,
00861             double v,
00862             const vgl_vector_3d<double>& dp)
00863 {
00864   v *= v;
00865   v /= dp.sqr_length();
00866   double tmp = dot_product(n,dp)/dp.length() - 1.0;
00867   return v + tmp*tmp;
00868 }
00869 
00870 vgl_vector_3d<double> dfunc(const vgl_vector_3d<double>& n,
00871                             double v,
00872                             const vgl_vector_3d<double>& dp,
00873                             const vnl_double_3x3& ddp)
00874 {
00875   vnl_double_3 nn(n.x(),n.y(),n.z());
00876   vnl_double_3 ndp(dp.x(), dp.y(), dp.z());
00877   double sqr_len = dp.sqr_length();
00878   double len = vcl_sqrt(sqr_len);
00879   double n_dot_dp = dot_product(nn,ndp);
00880   vnl_double_3 df = (2*v/sqr_len)*ndp;
00881   df += ddp.transpose() * ( ((-2*v*v/(sqr_len*sqr_len))*ndp) +
00882                             (2/len*(n_dot_dp/len - 1)*(nn - (n_dot_dp/sqr_len)*ndp)) );
00883   return vgl_vector_3d<double>(df[0],df[1],df[2]);
00884 }
00885 // end of namespace
00886 }
00887 
00888 //: Move the point \a p to minimize $(f^2 + (n*f' - 1)^2)/f'*f'$ a zero crossing of \a f (within \a eps).
00889 //  Return true if successful
00890 bool snap_to_surface_with_normal(const imesh_imls_surface& f,
00891                                  vgl_point_3d<double>& p,
00892                                  vgl_vector_3d<double> n,
00893                                  double step, double eps)
00894 {
00895   vgl_point_3d<double> p1(p);
00896   normalize(n);
00897   vgl_vector_3d<double> dp;
00898   vnl_double_3x3 ddp;
00899   double val = f.deriv2(p1, dp, ddp);
00900   double f1 = func(n,val,dp);
00901   if (f1 < eps)
00902     return true;
00903 
00904   vgl_vector_3d<double> df = dfunc(n,val,dp,ddp);
00905   double dl = df.sqr_length();
00906   unsigned int i;
00907   for (i=0; i<1000; ++i) {
00908     vgl_point_3d<double> p2 = p1 - (step*f1/dl)*df;
00909     val = f.deriv2(p2,dp,ddp);
00910     double f2 = func(n,val,dp);
00911     //vcl_cout << i<<" f: "<<f2<<" step: "<< step<<vcl_endl;
00912     if ( f2 > f1) {
00913       step /= 2;
00914       continue;
00915     }
00916     if (f2 < eps || step < eps) {
00917       p = p2;
00918       return true;
00919     }
00920     f1 = f2;
00921     p1 = p2;
00922     df = dfunc(n,val,dp,ddp);
00923     dl = df.sqr_length();
00924     step *= 2;
00925   }
00926   if (i >= 100)
00927     return true;
00928 
00929   p = p1;
00930   return true;
00931 }
00932 
00933 
00934 //: Move the point \a p along direction \a dir until reaching a zero crossing of \a f (within \a eps).
00935 //  Return true if successful
00936 bool snap_to_surface(const imesh_imls_surface& f,
00937                      vgl_vector_3d<double> dir,
00938                      vgl_point_3d<double>& p,
00939                      double step, double eps)
00940 {
00941   vgl_point_3d<double> p1(p);
00942   normalize(dir);
00943   vgl_vector_3d<double> dp;
00944   double val1 = f.deriv(p1,dp);
00945   dp = dir * dot_product(dp,dir);
00946   double dl = dp.length();
00947   val1 /= dl;
00948   if (vcl_abs(val1) < eps)
00949     return true;
00950 
00951   vgl_point_3d<double> p2 = p1 - (step*val1/dl)*dp;
00952   dl = dp.length();
00953   double val2 = f.deriv(p2,dp);
00954   val2 /= dl;
00955   unsigned int i=0;
00956   for (; i<100 && val1*val2 > 0.0; ++i) {
00957     p1 = p2;
00958     val1 = val2;
00959     p2 -= (step*val2/dl)*dp;
00960     val2 = f.deriv(p2,dp);
00961     dp = dir * dot_product(dp,dir);
00962     dl = dp.length();
00963     val2 /= dl;
00964     if (vcl_abs(val2) < eps) {
00965       p = p2;
00966       return true;
00967     }
00968   }
00969   if (i >= 100)
00970     return false;
00971 
00972   if (val1 > 0.0)
00973     p = bisect(f,p1,p2,eps);
00974   else
00975     p = bisect(f,p2,p1,eps);
00976 
00977 
00978   return true;
00979 }
00980 
00981 // Explicit instantiation needed in the implementations in this file:
00982 #include <imesh/algo/imesh_imls_surface.txx>
00983 IMESH_IMLS_SURFACE_INSTANTATE(vgl_vector_2d<double>,vgl_point_3d<double>);
00984 IMESH_IMLS_SURFACE_INSTANTATE(imesh_imls_surface::integral_data,vgl_point_3d<double>);