contrib/brl/bbas/imesh/algo/imesh_intersect.cxx
Go to the documentation of this file.
00001 // This is brl/bbas/imesh/algo/imesh_intersect.cxx
00002 #include "imesh_intersect.h"
00003 //:
00004 // \file
00005 
00006 #include <vcl_limits.h>
00007 #include <vcl_cassert.h>
00008 
00009 #include <vgl/vgl_triangle_3d.h>
00010 #include <vgl/vgl_distance.h>
00011 
00012 
00013 namespace {
00014 
00015 //: compute barycentric coordinates (u,v) assuming that \a p is in the plane of triangle (a,b,c)
00016 void compute_barycentric(const vgl_point_3d<double>& p,
00017                          const vgl_point_3d<double>& a,
00018                          const vgl_point_3d<double>& b,
00019                          const vgl_point_3d<double>& c,
00020                          double& u, double& v)
00021 {
00022   vgl_vector_3d<double> vp(p-a);
00023   vgl_vector_3d<double> vu(b-a);
00024   vgl_vector_3d<double> vv(c-a);
00025   vgl_vector_3d<double> n(cross_product(vu,vv));
00026   vgl_vector_3d<double> nxu(cross_product(n,vu));
00027   vgl_vector_3d<double> nxv(cross_product(n,vv));
00028   u = dot_product(vp,nxv)/dot_product(vu,nxv);
00029   v = dot_product(vp,nxu)/dot_product(vv,nxu);
00030 }
00031 // end of namespace
00032 };
00033 
00034 
00035 //: Intersect the ray from point p with direction d and the triangle defined by a,b,c
00036 //  \returns true if intersection occurs
00037 //  \param dist is the distance to the triangle (returned by reference)
00038 //  \param u and \param v are the barycentric coordinates of the intersection
00039 bool imesh_intersect_triangle(const vgl_point_3d<double>& p,
00040                               const vgl_vector_3d<double>& d,
00041                               const vgl_point_3d<double>& a,
00042                               const vgl_point_3d<double>& b,
00043                               const vgl_point_3d<double>& c,
00044                               double& dist,
00045                               double& u, double& v)
00046 {
00047   vgl_vector_3d<double> n(cross_product(b-a,c-a));
00048   return imesh_intersect_triangle(p,d,a,b,c,n,dist,u,v);
00049 }
00050 
00051 
00052 //: Intersect the ray from point p with direction d and the triangle defined by a,b,c
00053 //  The un-normalized normal vector (b-a)x(c-a) is precomputed and also passed in
00054 //  \returns true if intersection occurs
00055 //  \param dist is the distance to the triangle (returned by reference)
00056 //  \param u and \param v are the barycentric coordinates of the intersection
00057 bool imesh_intersect_triangle(const vgl_point_3d<double>& p,
00058                               const vgl_vector_3d<double>& d,
00059                               const vgl_point_3d<double>& a,
00060                               const vgl_point_3d<double>& b,
00061                               const vgl_point_3d<double>& c,
00062                               const vgl_vector_3d<double>& n,
00063                               double& dist,
00064                               double& u, double& v)
00065 {
00066   double denom = -dot_product(d,n);
00067   if (denom <= 0) // back facing triangles
00068     return false;
00069 
00070   vgl_vector_3d<double> ap(p-a);
00071   vgl_vector_3d<double> t(cross_product(d,ap));
00072   v = dot_product(b-p,t);
00073   if (v < 0.0 || v>denom)
00074     return false;
00075 
00076   u = -dot_product(c-p,t);
00077   if (u < 0.0 || u+v > denom)
00078     return false;
00079 
00080   dist = dot_product(ap,n);
00081   if (dist < 0.0)
00082     return false;
00083 
00084   u /= denom;
00085   v /= denom;
00086   dist /= denom;
00087 
00088   return true;
00089 }
00090 
00091 
00092 //: Intersect the ray from point p with direction d and the triangle defined by a,b,c
00093 //  The un-normalized normal vector (b-a)x(c-a) is precomputed and also passed in
00094 //  \returns true if intersection occurs and the new dist is less than the old distance (but > 0)
00095 //  \param dist is the distance to the triangle (returned by reference)
00096 //  \param u and \param v are the barycentric coordinates of the intersection
00097 bool imesh_intersect_triangle_min_dist(const vgl_point_3d<double>& p,
00098                                        const vgl_vector_3d<double>& d,
00099                                        const vgl_point_3d<double>& a,
00100                                        const vgl_point_3d<double>& b,
00101                                        const vgl_point_3d<double>& c,
00102                                        const vgl_vector_3d<double>& n,
00103                                        double& dist,
00104                                        double& u, double& v)
00105 {
00106   double denom = -dot_product(d,n);
00107   if (denom <= 0) // back facing triangles
00108     return false;
00109 
00110   vgl_vector_3d<double> ap(p-a);
00111   double new_dist = dot_product(ap,n)/denom;
00112   if (new_dist < 0.0 || new_dist > dist)
00113     return false;
00114 
00115   vgl_vector_3d<double> t(cross_product(d,ap));
00116   v = dot_product(b-p,t);
00117   if (v < 0.0 || v>denom)
00118     return false;
00119 
00120   u = -dot_product(c-p,t);
00121   if (u < 0.0 || u+v > denom)
00122     return false;
00123 
00124   dist = new_dist;
00125   u /= denom;
00126   v /= denom;
00127 
00128   return true;
00129 }
00130 
00131 
00132 //: Intersect the ray from point p with direction d and the triangulated mesh
00133 //  \returns the face index of the closest intersecting triangle
00134 //  \param dist is the distance to the triangle (returned by reference)
00135 //  \param u and \param v (optional) are the barycentric coordinates of the intersection
00136 int imesh_intersect_min_dist(const vgl_point_3d<double>& p,
00137                              const vgl_vector_3d<double>& d,
00138                              const imesh_mesh& mesh,
00139                              double& dist, double* u, double* v)
00140 {
00141   double ut, vt; // temporary u and v
00142 
00143   assert(mesh.faces().regularity() == 3);
00144   const imesh_regular_face_array<3>& faces
00145       = static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00146 
00147   const imesh_vertex_array<3>& verts = mesh.vertices<3>();
00148 
00149   assert(faces.has_normals());
00150   assert((cross_product(vgl_point_3d<double>(verts[faces[0][1]])-vgl_point_3d<double>(verts[faces[0][0]]),
00151                         vgl_point_3d<double>(verts[faces[0][2]])-vgl_point_3d<double>(verts[faces[0][0]]))
00152          -faces.normal(0)).length() < 1e-14);
00153 
00154   int isect = -1;
00155   dist = vcl_numeric_limits<double>::infinity();
00156   for (unsigned int i=0; i<faces.size(); ++i)
00157   {
00158     const imesh_regular_face<3>& f = faces[i];
00159     if (imesh_intersect_triangle_min_dist(p,d,verts[f[0]],verts[f[1]],verts[f[2]],
00160                                          faces.normal(i),dist,ut,vt))
00161     {
00162       isect = i;
00163       if (u) *u = ut;
00164       if (v) *v = vt;
00165     }
00166   }
00167   return isect;
00168 }
00169 
00170 
00171 //: Find the closest point on the triangle a,b,c to point p
00172 //  The un-normalized normal vector (b-a)x(c-a) is precomputed and also passed in
00173 //  \returns a code indicating that the closest point:
00174 //  - 0 does not exist (should not occur)
00175 //  - 1 is \a a
00176 //  - 2 is \a b
00177 //  - 3 is on the edge from \a a to \a b
00178 //  - 4 is \a c
00179 //  - 5 is on the edge from \a a to \a c
00180 //  - 6 is on the edge from \a b to \a c
00181 //  - 7 is on the face of the triangle
00182 //  \param dist is the distance to the triangle (returned by reference)
00183 //  \param u and \param v are the barycentric coordinates of the closest point
00184 unsigned char
00185 imesh_triangle_closest_point(const vgl_point_3d<double>& p,
00186                              const vgl_point_3d<double>& a,
00187                              const vgl_point_3d<double>& b,
00188                              const vgl_point_3d<double>& c,
00189                              const vgl_vector_3d<double>& n,
00190                              double& dist,
00191                              double& u, double& v)
00192 {
00193   double denom = 1.0/dot_product(n,n);
00194 
00195   vgl_vector_3d<double> ap(p-a);
00196   vgl_vector_3d<double> bp(p-b);
00197   vgl_vector_3d<double> cp(p-c);
00198 
00199   vgl_vector_3d<double> t(cross_product(n,ap));
00200   v = dot_product(bp,t) * denom;
00201   u = -dot_product(cp,t) * denom;
00202 
00203   vgl_vector_3d<double> ab(b-a);
00204   vgl_vector_3d<double> bc(c-b);
00205   vgl_vector_3d<double> ca(a-c);
00206 
00207   double eps = vcl_numeric_limits<double>::epsilon();
00208 
00209   unsigned char state = 0;
00210   double uv;
00211   if (u <= eps) {
00212     double p_v = v - u * dot_product(ab,ca)/dot_product(ca,ca);
00213     if (p_v <= eps) {
00214       state = 1;
00215     }
00216     else if (p_v >= 1.0) {
00217       state = 4;
00218     }
00219     else {
00220       u = 0.0; v = p_v;
00221       dist = ((1-v)*ap + v*cp).length();
00222       return 5;
00223     }
00224   }
00225   if (v <= eps) {
00226     double p_u = u - v * dot_product(ca,ab)/dot_product(ab,ab);
00227     if (p_u <= eps) {
00228       state = 1;
00229     }
00230     else if (p_u >= 1.0) {
00231       state = 2;
00232     }
00233     else {
00234       u = p_u; v = 0.0;
00235       dist = ((1-u)*ap + u*bp).length();
00236       return 3;
00237     }
00238   }
00239   if ((uv = 1.0-u-v) <= eps) {
00240     double s = -dot_product(ca,bc)/dot_product(bc,bc);
00241     double p_u = u + uv * s;
00242     double p_v = v + uv * (1.0-s);
00243     if (p_v <= eps) {
00244       state = 2;
00245     }
00246     else if (p_u <= eps) {
00247       state = 4;
00248     }
00249     else {
00250       u=p_u; v=p_v;
00251       dist = (u*bp + v*cp).length();
00252       return 6;
00253     }
00254   }
00255 
00256   switch (state)
00257   {
00258     case 1:
00259       u=0.0; v=0.0;
00260       dist = ap.length();
00261       return 1;
00262     case 2:
00263       u=1.0; v=0.0;
00264       dist = bp.length();
00265       return 2;
00266     case 4:
00267       u=0.0; v=1.0;
00268       dist = cp.length();
00269       return 4;
00270     default:
00271       dist = vcl_abs(dot_product(ap,n) * vcl_sqrt(denom));
00272       return 7;
00273   }
00274 
00275   return 0;
00276 }
00277 
00278 
00279 //: Find the closest point on the triangle a,b,c to point p
00280 //  \returns a code same as other version of this function
00281 //  \param dist is the distance to the triangle (returned by reference)
00282 //  \param u and \param v are the barycentric coordinates of the closest point
00283 unsigned char
00284 imesh_triangle_closest_point(const vgl_point_3d<double>& p,
00285                              const vgl_point_3d<double>& a,
00286                              const vgl_point_3d<double>& b,
00287                              const vgl_point_3d<double>& c,
00288                              double& dist,
00289                              double& u, double& v)
00290 {
00291   vgl_vector_3d<double> n(cross_product(b-a,c-a));
00292   return imesh_triangle_closest_point(p,a,b,c,n,dist,u,v);
00293 }
00294 
00295 
00296 //: Find the closest point on the triangle a,b,c to point p
00297 //  \param dist is the distance to the triangle (returned by reference)
00298 vgl_point_3d<double>
00299 imesh_triangle_closest_point(const vgl_point_3d<double>& p,
00300                              const vgl_point_3d<double>& a,
00301                              const vgl_point_3d<double>& b,
00302                              const vgl_point_3d<double>& c,
00303                              double& dist)
00304 {
00305   double u,v;
00306   imesh_triangle_closest_point(p,a,b,c,dist,u,v);
00307   double t = 1-u-v;
00308   return vgl_point_3d<double>(t*a.x() + u*b.x() + v*c.x(),
00309                               t*a.y() + u*b.y() + v*c.y(),
00310                               t*a.z() + u*b.z() + v*c.z());
00311 }
00312 
00313 //: Find the closest point on the triangulated mesh to point p
00314 //  \returns the face index of the closest triangle (one of them if on an edge or vertex)
00315 //  \param cp is the closest point on the mesh (returned by reference)
00316 //  \param u and \param v (optional) are the barycentric coordinates of the closest point
00317 int imesh_closest_point(const vgl_point_3d<double>& p,
00318                         const imesh_mesh& mesh,
00319                         vgl_point_3d<double>& cp,
00320                         double* u, double* v)
00321 {
00322   assert(mesh.faces().regularity() == 3);
00323   const imesh_regular_face_array<3>& faces
00324       = static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00325 
00326   const imesh_vertex_array<3>& verts = mesh.vertices<3>();
00327 
00328   int isect = -1;
00329   double dist = vcl_numeric_limits<double>::infinity();
00330   for (unsigned int i=0; i<faces.size(); ++i)
00331   {
00332     const imesh_regular_face<3>& f = faces[i];
00333     vgl_point_3d<double> cpt = vgl_triangle_3d_closest_point(p,verts[f[0]],verts[f[1]],verts[f[2]]);
00334     double cp_dist = vgl_distance(cpt,p);
00335     if (cp_dist < dist)
00336     {
00337       isect = i;
00338       dist = cp_dist;
00339       cp = cpt;
00340       double ut, vt;
00341       compute_barycentric(cp,verts[f[0]],verts[f[1]],verts[f[2]],ut,vt);
00342       if (u) *u = ut;
00343       if (v) *v = vt;
00344     }
00345   }
00346   return isect;
00347 }
00348 
00349 
00350 //: Find the closest intersection point from p along d with triangle a,b,c
00351 //  \returns a code indicating that the intersection point:
00352 //  - 0 does not exist
00353 //  - 1 is \a a
00354 //  - 2 is \a b
00355 //  - 3 is on the edge from \a a to \a b
00356 //  - 4 is \a c
00357 //  - 5 is on the edge from \a a to \a c
00358 //  - 6 is on the edge from \a b to \a c
00359 //  - 7 could not be computed (error)
00360 //  \param u and \param v are the barycentric coordinates of the intersection
00361 unsigned char
00362 imesh_triangle_intersect(const vgl_point_2d<double>& p,
00363                          const vgl_vector_2d<double>& d,
00364                          const vgl_point_2d<double>& a,
00365                          const vgl_point_2d<double>& b,
00366                          const vgl_point_2d<double>& c,
00367                          double& u, double& v)
00368 {
00369   vgl_vector_2d<double> v1(b-a), v2(c-a), vp(p-a);
00370   vgl_vector_2d<double> v1n(v1.y(), -v1.x());
00371   v1n /= dot_product(v1n,v2);
00372   vgl_vector_2d<double> v2n(v2.y(), -v2.x());
00373   v2n /= dot_product(v2n,v1);
00374   v = dot_product(v1n,vp);
00375   u = dot_product(v2n,vp);
00376 
00377   double dv = dot_product(d,v1n);
00378   double du = dot_product(d,v2n);
00379 
00380   return imesh_triangle_intersect(u,v,du,dv);
00381 }
00382 
00383 
00384 //: Find the closest intersection point in barycentric coordinates along the vector (du,dv) in barycentric coordinates
00385 //  \returns a code indicating that the intersection point:
00386 //  - 0 does not exist
00387 //  - 1 is at corner (0,0)
00388 //  - 2 is at corner (1,0)
00389 //  - 3 is on the edge v=0
00390 //  - 4 is at corner (0,1)
00391 //  - 5 is on the edge from u=0
00392 //  - 6 is on the edge from u+v=1
00393 //  - 7 could not be computed (error)
00394 //  \param u and \param v are updated to the coordinates of the intersection
00395 unsigned char
00396 imesh_triangle_intersect(double& u, double& v,
00397                          const double& du, const double& dv,
00398                          const double& eps)
00399 {
00400   unsigned char state = 0;
00401   double best_t = vcl_numeric_limits<double>::infinity();
00402   if (du > 0.0)
00403   {
00404     double t = -u/du;
00405     double vi = v + t*dv;
00406     if (vi > eps && vi+eps < 1.0)
00407     {
00408       best_t = t;
00409       state = 5;
00410     }
00411     else if (vcl_abs(vi) <= eps)
00412     {
00413       best_t = t;
00414       state = 1;
00415     }
00416     else if (vcl_abs(1-vi) <= eps)
00417     {
00418       best_t = t;
00419       state = 4;
00420     }
00421   }
00422   if (dv > 0.0)
00423   {
00424     double t = -v/dv;
00425     double ui = u + t*du;
00426     if (ui > eps && ui+eps < 1.0)
00427     {
00428       best_t = t;
00429       state = 3;
00430     }
00431     else if (vcl_abs(ui) <= eps)
00432     {
00433       best_t = t;
00434       state = 1;
00435     }
00436     else if (vcl_abs(1-ui) <= eps)
00437     {
00438       best_t = t;
00439       state = 2;
00440     }
00441   }
00442   if (du+dv < 0.0)
00443   {
00444     double t = (1-u-v)/(du+dv);
00445     double ui = u + t*du;
00446     double vi = v + t*dv;
00447     if (ui > eps && vi > eps)
00448     {
00449       best_t = t;
00450       state = 6;
00451     }
00452     else if (vcl_abs(ui) <= eps)
00453     {
00454       best_t = t;
00455       state = 4;
00456     }
00457     else if (vcl_abs(vi) <= eps)
00458     {
00459       best_t = t;
00460       state = 2;
00461     }
00462   }
00463 
00464   u+=best_t*du;
00465   v+=best_t*dv;
00466 
00467   return state;
00468 }