contrib/brl/bbas/imesh/algo/imesh_project.cxx
Go to the documentation of this file.
00001 // This is brl/bbas/imesh/algo/imesh_project.cxx
00002 #include "imesh_project.h"
00003 //:
00004 // \file
00005 
00006 
00007 #include <imesh/imesh_operations.h>
00008 #include <imesh/algo/imesh_intersect.h>
00009 #include <vgl/vgl_point_2d.h>
00010 #include <vgl/vgl_homg_point_2d.h>
00011 #include <vgl/vgl_homg_point_3d.h>
00012 #include <vgl/vgl_vector_3d.h>
00013 #include <vgl/vgl_plane_3d.h>
00014 #include <vgl/vgl_line_3d_2_points.h>
00015 #include <vgl/vgl_triangle_test.h>
00016 #include <vgl/vgl_intersection.h>
00017 #include <vgl/vgl_distance.h>
00018 #include <vgl/vgl_triangle_scan_iterator.h>
00019 #include <vcl_algorithm.h>
00020 #include <vcl_limits.h>
00021 #include <vcl_cmath.h>
00022 #include <vcl_cassert.h>
00023 
00024 
00025 void
00026 imesh_project_verts(const imesh_vertex_array<3>& verts3d,
00027                     const vpgl_proj_camera<double>& camera,
00028                     vcl_vector<vgl_point_2d<double> >& verts2d)
00029 {
00030   verts2d.resize(verts3d.size());
00031   for (unsigned int i=0; i<verts3d.size(); ++i)
00032     verts2d[i] = camera.project(verts3d[i]);
00033 }
00034 
00035 void
00036 imesh_project_verts(const vcl_vector<vgl_point_3d<double> >& verts3d,
00037                     const vpgl_proj_camera<double>& camera,
00038                     vcl_vector<vgl_point_2d<double> >& verts2d)
00039 {
00040   verts2d.resize(verts3d.size());
00041   for (unsigned int i=0; i<verts3d.size(); ++i)
00042     verts2d[i] = camera.project(verts3d[i]);
00043 }
00044 
00045 
00046 void
00047 imesh_project_verts(const vcl_vector<vgl_point_3d<double> >& verts3d,
00048                     const vpgl_proj_camera<double>& camera,
00049                     vcl_vector<vgl_point_2d<double> >& verts2d,
00050                     vcl_vector<double>& depths)
00051 {
00052   verts2d.resize(verts3d.size());
00053   depths.resize(verts3d.size());
00054   for (unsigned int i=0; i<verts3d.size(); ++i) {
00055     vgl_homg_point_2d<double> pt = camera.project(verts3d[i]);
00056     verts2d[i] = pt;
00057     depths[i] = pt.w();
00058   }
00059 }
00060 
00061 
00062 void
00063 imesh_project_verts(const imesh_vertex_array<3>& verts3d,
00064                     const vpgl_proj_camera<double>& camera,
00065                     vcl_vector<vgl_point_2d<double> >& verts2d,
00066                     vcl_vector<double>& depths)
00067 {
00068   verts2d.resize(verts3d.size());
00069   depths.resize(verts3d.size());
00070   for (unsigned int i=0; i<verts3d.size(); ++i) {
00071     vgl_homg_point_2d<double> pt = camera.project(verts3d[i]);
00072     verts2d[i] = pt;
00073     depths[i] = pt.w();
00074   }
00075 }
00076 
00077 
00078 void
00079 imesh_distort_verts(const vcl_vector<vgl_point_2d<double> >& in_verts,
00080                     const bpgl_lens_distortion<double>& lens,
00081                     vcl_vector<vgl_point_2d<double> >& out_verts)
00082 {
00083   out_verts.resize(in_verts.size());
00084   for (unsigned int i=0; i<in_verts.size(); ++i)
00085     out_verts[i] = lens.undistort(vgl_homg_point_2d<double>(in_verts[i]));
00086 }
00087 
00088 
00089 //: Render a triangle defined by its vertices
00090 void imesh_render_triangle(const vgl_point_2d<double>& v1,
00091                            const vgl_point_2d<double>& v2,
00092                            const vgl_point_2d<double>& v3,
00093                            vil_image_view<bool>& image)
00094 {
00095   vgl_triangle_scan_iterator<double> tsi;
00096   tsi.a.x = v1.x();  tsi.a.y = v1.y();
00097   tsi.b.x = v2.x();  tsi.b.y = v2.y();
00098   tsi.c.x = v3.x();  tsi.c.y = v3.y();
00099   for (tsi.reset(); tsi.next(); ) {
00100     int y = tsi.scany();
00101     if (y<0 || y>=int(image.nj())) continue;
00102     int min_x = tsi.startx();
00103     int max_x = tsi.endx();
00104     if (min_x >= (int)image.ni() || max_x < 0)
00105       continue;
00106     if (min_x < 0) min_x = 0;
00107     if (max_x >= (int)image.ni()) max_x = image.ni()-1;
00108     for (int x = min_x; x <= max_x; ++x) {
00109       image(x,y) = true;
00110     }
00111   }
00112 }
00113 
00114 
00115 //: Render a triangle defined by its vertices
00116 void imesh_render_triangle_interp(const vgl_point_2d<double>& v1,
00117                                   const vgl_point_2d<double>& v2,
00118                                   const vgl_point_2d<double>& v3,
00119                                   const double& i1, const double& i2, const double& i3,
00120                                   vil_image_view<double>& image)
00121 {
00122   vgl_triangle_scan_iterator<double> tsi;
00123   tsi.a.x= v1.x();  tsi.a.y = v1.y();
00124   tsi.b.x = v2.x();  tsi.b.y = v2.y();
00125   tsi.c.x = v3.x();  tsi.c.y = v3.y();
00126   vgl_vector_3d<double> b1(v2.x()-v1.x(), v2.y()-v1.y(), i2-i1);
00127   vgl_vector_3d<double> b2(v3.x()-v1.x(), v3.y()-v1.y(), i3-i1);
00128   vgl_vector_3d<double> n = cross_product(b1,b2);
00129   double A = -n.x()/n.z();
00130   double B = -n.y()/n.z();
00131   double C = (v1.x()*n.x() + v1.y()*n.y() + i1*n.z())/n.z();
00132   for (tsi.reset(); tsi.next(); ) {
00133     int y = tsi.scany();
00134     if (y<0 || y>=int(image.nj())) continue;
00135     int min_x = tsi.startx();
00136     int max_x = tsi.endx();
00137     if (min_x >= (int)image.ni() || max_x < 0)
00138       continue;
00139     if (min_x < 0) min_x = 0;
00140     if (max_x >= (int)image.ni()) max_x = image.ni()-1;
00141     double new_i = B*y+C;
00142     for (int x = min_x; x <= max_x; ++x) {
00143       double depth = new_i + A*x;
00144       if (depth < image(x,y))
00145         image(x,y) = depth;
00146     }
00147   }
00148 }
00149 
00150 
00151 void
00152 imesh_render_triangles(const imesh_regular_face_array<3>& tris,
00153                        const vcl_vector<vgl_point_2d<double> >& img_verts,
00154                        vil_image_view<bool>& image)
00155 {
00156   for (unsigned i=0; i<tris.size(); ++i) {
00157     imesh_render_triangle(img_verts[tris(i,0)],
00158                           img_verts[tris(i,1)],
00159                           img_verts[tris(i,2)],
00160                           image);
00161   }
00162 }
00163 
00164 
00165 void
00166 imesh_render_faces(const imesh_mesh& mesh,
00167                    const vcl_vector<vgl_point_2d<double> >& img_verts,
00168                    vil_image_view<bool>& image)
00169 {
00170   const imesh_face_array_base& faces = mesh.faces();
00171   vcl_auto_ptr<imesh_regular_face_array<3> > tri_data;
00172   const imesh_regular_face_array<3>* tris;
00173   if (faces.regularity() != 3) {
00174     tri_data = imesh_triangulate(faces);
00175     tris = tri_data.get();
00176   }
00177   else {
00178     tris = static_cast<const imesh_regular_face_array<3>*>(&faces);
00179   }
00180   imesh_render_triangles(*tris,img_verts,image);
00181 }
00182 
00183 
00184 void
00185 imesh_render_triangles_interp(const imesh_regular_face_array<3>& tris,
00186                               const vcl_vector<vgl_point_2d<double> >& img_verts,
00187                               const vcl_vector<double>& vals,
00188                               vil_image_view<double>& image)
00189 {
00190   for (unsigned i=0; i<tris.size(); ++i) {
00191     const imesh_regular_face<3>& tri = tris[i];
00192     imesh_render_triangle_interp(img_verts[tri[0]],
00193                                  img_verts[tri[1]],
00194                                  img_verts[tri[2]],
00195                                  vals[tri[0]],
00196                                  vals[tri[1]],
00197                                  vals[tri[2]],
00198                                  image);
00199   }
00200 }
00201 
00202 void
00203 imesh_render_faces_interp(const imesh_mesh& mesh,
00204                           const vcl_vector<vgl_point_2d<double> >& img_verts,
00205                           const vcl_vector<double>& vals,
00206                           vil_image_view<double>& image)
00207 {
00208   const imesh_face_array_base& faces = mesh.faces();
00209   vcl_auto_ptr<imesh_regular_face_array<3> > tri_data;
00210   const imesh_regular_face_array<3>* tris;
00211   if (faces.regularity() != 3) {
00212     tri_data = imesh_triangulate(faces);
00213     tris = tri_data.get();
00214   }
00215   else {
00216     tris = static_cast<const imesh_regular_face_array<3>*>(&faces);
00217   }
00218   imesh_render_triangles_interp(*tris,img_verts,vals,image);
00219 }
00220 
00221 
00222 //: project the mesh onto the image plane using the camera and lens distortion
00223 //  Set each pixel of the image to true if the mesh project onto it
00224 void imesh_project(const imesh_mesh& mesh,
00225                    const vpgl_proj_camera<double>& camera,
00226                    const bpgl_lens_distortion<double>& lens,
00227                    vil_image_view<bool>& image)
00228 {
00229   assert(mesh.vertices().dim() == 3);
00230   const imesh_vertex_array<3>& verts3d =
00231       static_cast<const imesh_vertex_array<3>&>(mesh.vertices());
00232   vcl_vector<vgl_point_2d<double> > verts2d;
00233   imesh_project_verts(verts3d, camera, verts2d);
00234   imesh_distort_verts(verts2d, lens, verts2d);
00235   imesh_render_faces(mesh, verts2d, image);
00236 }
00237 
00238 
00239 //: project the front-facing triangles of the mesh onto the image plane
00240 //  Using the camera and lens distortion
00241 //  Set each pixel of the image to true if the mesh projects onto it
00242 void imesh_project(const imesh_mesh& mesh,
00243                    const vcl_vector<vgl_vector_3d<double> >& normals,
00244                    const vpgl_proj_camera<double>& camera,
00245                    const bpgl_lens_distortion<double>& lens,
00246                    vil_image_view<bool>& image,
00247                    vgl_box_2d<unsigned int>* bbox)
00248 {
00249   const imesh_face_array_base& faces = mesh.faces();
00250   vcl_auto_ptr<imesh_regular_face_array<3> > tri_data;
00251   const imesh_regular_face_array<3>* tri_ptr;
00252   if (faces.regularity() != 3) {
00253     tri_data = imesh_triangulate(faces);
00254     tri_ptr = tri_data.get();
00255   }
00256   else {
00257     tri_ptr = static_cast<const imesh_regular_face_array<3>*>(&faces);
00258   }
00259   const imesh_regular_face_array<3>& tris = *tri_ptr;
00260   const imesh_vertex_array<3>& verts3d = mesh.vertices<3>();
00261 
00262   vcl_vector<vgl_point_2d<double> > verts2d;
00263   imesh_project_verts(verts3d, camera, verts2d);
00264   imesh_distort_verts(verts2d, lens, verts2d);
00265 
00266   if (bbox) {
00267     bbox->set_min_x(0);  bbox->set_min_y(0);
00268     bbox->set_max_x(image.ni()-1);  bbox->set_max_y(image.nj()-1);
00269     imesh_projection_bounds(verts2d,*bbox);
00270   }
00271 
00272   vgl_homg_point_3d<double> c(camera.camera_center());
00273   if (c.w() < 0.0)
00274     c.rescale_w(-c.w());
00275 
00276   typedef vcl_vector<vgl_vector_3d<double> >::const_iterator itr_n;
00277   itr_n n = normals.begin();
00278   for (unsigned int i=0; i<tris.size(); ++i, ++n) {
00279     const vgl_point_3d<double>& v1 = verts3d[tris(i,0)];
00280     vgl_vector_3d<double> d(c.x()-v1.x()*c.w(),
00281                             c.y()-v1.y()*c.w(),
00282                             c.z()-v1.z()*c.w());
00283     if (dot_product(*n,d) < 0.0)
00284       continue;
00285     imesh_render_triangle(verts2d[tris(i,0)],
00286                           verts2d[tris(i,1)],
00287                           verts2d[tris(i,2)],
00288                           image);
00289   }
00290 }
00291 
00292 
00293 //: project the mesh onto the image plane using the camera
00294 //  Set each pixel of the image to true if the mesh projects onto it
00295 void imesh_project(const imesh_mesh& mesh,
00296                    const vpgl_proj_camera<double>& camera,
00297                    vil_image_view<bool>& image)
00298 {
00299   assert(mesh.vertices().dim() == 3);
00300   vcl_vector<vgl_point_2d<double> > verts2d;
00301   imesh_project_verts(mesh.vertices<3>(), camera, verts2d);
00302   imesh_render_faces(mesh, verts2d, image);
00303 }
00304 
00305 
00306 //: project the mesh onto the image plane using the camera
00307 //  Set each pixel of the image to the depth to the mesh
00308 void imesh_project_depth(const imesh_mesh& mesh,
00309                          const vpgl_proj_camera<double>& camera,
00310                          vil_image_view<double>& image)
00311 {
00312   assert(mesh.vertices().dim() == 3);
00313   vcl_vector<vgl_point_2d<double> > verts2d;
00314   vcl_vector<double> depths;
00315   image.fill(vcl_numeric_limits<double>::infinity());
00316   imesh_project_verts(mesh.vertices<3>(), camera, verts2d, depths);
00317   imesh_render_faces_interp(mesh, verts2d, depths, image);
00318 }
00319 
00320 
00321 //: Compute the bounds of the projection of a set of image points
00322 //  The returned bounds are the intersection of the input bounds
00323 // and the bounding box of the points
00324 void imesh_projection_bounds(const vcl_vector<vgl_point_2d<double> >& img_pts,
00325                              vgl_box_2d<unsigned int>& bbox)
00326 {
00327   assert(!img_pts.empty());
00328   typedef vcl_vector<vgl_point_2d<double> >::const_iterator itr_p;
00329 
00330   int i0 = bbox.max_x(), i1 = bbox.min_x(),
00331       j0 = bbox.max_y(), j1 = bbox.min_y();
00332   for (itr_p p = img_pts.begin(); p != img_pts.end(); ++p) {
00333     double x = p->x(), y = p->y();
00334     int v = static_cast<int>(vcl_ceil(x))-1;
00335     if (v < i0) i0 = v;
00336     v = static_cast<int>(vcl_floor(x))+1;
00337     if (v > i1) i1 = v;
00338 
00339     v = static_cast<int>(vcl_ceil(y))-1;
00340     if (v < j0) j0 = v;
00341     v = static_cast<int>(vcl_floor(y))+1;
00342     if (v > j1) j1 = v;
00343   }
00344   if (i0 > static_cast<int>(bbox.min_x())) bbox.set_min_x(static_cast<unsigned int>(i0));
00345   if (i1 < static_cast<int>(bbox.max_x())) bbox.set_max_x(static_cast<unsigned int>(i1));
00346   if (j0 > static_cast<int>(bbox.min_y())) bbox.set_min_y(static_cast<unsigned int>(j0));
00347   if (j1 < static_cast<int>(bbox.max_y())) bbox.set_max_y(static_cast<unsigned int>(j1));
00348 }
00349 
00350 
00351 //: back project an image point onto the mesh using the camera
00352 //  Return true if the ray intersects the mesh
00353 int imesh_project_onto_mesh(const imesh_mesh& mesh,
00354                             const vcl_vector<vgl_vector_3d<double> >& normals,
00355                             const vcl_vector<vgl_point_2d<double> >& verts2d,
00356                             const vpgl_perspective_camera<double>& camera,
00357                             const vgl_point_2d<double>& pt_2d,
00358                             vgl_point_3d<double>& pt_3d)
00359 {
00360   int tri_idx = -1;
00361 
00362   vgl_line_3d_2_points<double> ray = camera.backproject(pt_2d);
00363   vgl_vector_3d<double> d = ray.direction();
00364   normalize(d);
00365 
00366   vgl_point_3d<double> center = camera.get_camera_center();
00367 
00368   // get mesh faces as triangles
00369   assert(mesh.faces().regularity() == 3);
00370   const imesh_regular_face_array<3>& tris =
00371       static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00372 
00373   assert(mesh.vertices().dim() == 3);
00374   const imesh_vertex_array<3>& verts3d = mesh.vertices<3>();
00375 
00376   typedef imesh_regular_face_array<3>::const_iterator itr_t;
00377   typedef vcl_vector<vgl_vector_3d<double> >::const_iterator itr_n;
00378   itr_n n = normals.begin();
00379   double depth = vcl_numeric_limits<double>::infinity();
00380   int i = 0;
00381   for (itr_t itr = tris.begin(); itr != tris.end(); ++itr, ++n, ++i) {
00382     if (dot_product(*n,d) > 0.0)
00383       continue;
00384     const vgl_point_2d<double>& a = verts2d[(*itr)[0]];
00385     const vgl_point_2d<double>& b = verts2d[(*itr)[1]];
00386     const vgl_point_2d<double>& c = verts2d[(*itr)[2]];
00387     if (vgl_triangle_test_inside<double>(a.x(), a.y(), b.x(), b.y(),
00388                                         c.x(), c.y(), pt_2d.x(), pt_2d.y())) {
00389       vgl_plane_3d<double> tri(verts3d[(*itr)[0]],
00390                                verts3d[(*itr)[1]],
00391                                verts3d[(*itr)[2]]);
00392       vgl_point_3d<double> pt3 = vgl_intersection(ray, tri);
00393       double dist = vgl_distance(pt3,center);
00394       if (dist < depth) {
00395         depth = dist;
00396         pt_3d = pt3;
00397         tri_idx = i;
00398       }
00399     }
00400   }
00401   return tri_idx;
00402 }
00403 
00404 
00405 //: back project image points onto the mesh using the camera
00406 //  Returns a vector of all valid 3d points and indices to corresponding 2d points
00407 void imesh_project_onto_mesh(const imesh_mesh& mesh,
00408                              const vcl_vector<vgl_vector_3d<double> >& normals,
00409                              const vpgl_perspective_camera<double>& camera,
00410                              const vcl_vector< vgl_point_2d<double> >& pts_2d,
00411                              vcl_vector<unsigned int >& idx_2d,
00412                              vcl_vector<vgl_point_3d<double> >& pts_3d)
00413 {
00414   vcl_vector<vgl_point_2d<double> > verts2d;
00415   assert(mesh.vertices().dim() == 3);
00416   imesh_project_verts(mesh.vertices<3>(), camera, verts2d);
00417 
00418   vgl_point_3d<double> pt_3d;
00419   for (unsigned int i=0; i<pts_2d.size(); ++i)
00420   {
00421     if (imesh_project_onto_mesh(mesh, normals, verts2d, camera, pts_2d[i], pt_3d)) {
00422       pts_3d.push_back(pt_3d);
00423       idx_2d.push_back(i);
00424     }
00425   }
00426 }
00427 
00428 
00429 // anonymous namespace
00430 namespace{
00431 
00432 void
00433 xy_to_barycentric(const vgl_point_2d<double>& pt_xy,
00434                   const vgl_point_2d<double>& a,
00435                   const vgl_point_2d<double>& b,
00436                   const vgl_point_2d<double>& c,
00437                   vgl_point_2d<double>& pt_bary)
00438 {
00439   vgl_vector_2d<double> v1(b-a), v2(c-a), vp(pt_xy-a);
00440   vgl_vector_2d<double> v1n(-v1.y(), v1.x());
00441   vgl_vector_2d<double> v2n(v2.y(), -v2.x());
00442   double s = 1.0/(v2.y()*v1.x() - v2.x()*v1.y());
00443   pt_bary.y() = s * dot_product(v1n,vp);
00444   pt_bary.x() = s * dot_product(v2n,vp);
00445 }
00446 
00447 void
00448 barycentric_to_xy(const vgl_point_2d<double>& pt_bary,
00449                   const vgl_point_2d<double>& a,
00450                   const vgl_point_2d<double>& b,
00451                   const vgl_point_2d<double>& c,
00452                   vgl_point_2d<double>& pt_xy)
00453 {
00454   double z = 1.0 - pt_bary.x() - pt_bary.y();
00455   pt_xy.x() = z*a.x() + pt_bary.x()*b.x() + pt_bary.y()*c.x();
00456   pt_xy.y() = z*a.y() + pt_bary.x()*b.y() + pt_bary.y()*c.y();
00457 }
00458 // end of namespace
00459 };
00460 
00461 
00462 //: back project an image point onto the mesh using the camera
00463 //  The resulting point is in barycentric coordinates for the returned triangle
00464 //  Returns the index of the intersected triangle, or -1 for no intersection
00465 int imesh_project_onto_mesh_barycentric(const imesh_mesh& mesh,
00466                                         const vcl_vector<vgl_vector_3d<double> >& normals,
00467                                         const vcl_vector<vgl_point_2d<double> >& verts2d,
00468                                         const vpgl_perspective_camera<double>& camera,
00469                                         const vgl_point_2d<double>& pt_img,
00470                                         vgl_point_2d<double>& pt_bary)
00471 {
00472   vgl_point_3d<double> pt_3d;
00473 
00474   // get mesh faces as triangles
00475   assert(mesh.faces().regularity() == 3);
00476   const imesh_regular_face_array<3>& tris =
00477       static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00478 
00479   int tri_idx = imesh_project_onto_mesh(mesh, normals, verts2d, camera, pt_img, pt_3d);
00480   if (tri_idx >= 0) {
00481     const imesh_regular_face<3>& tri = tris[tri_idx];
00482     const vgl_point_2d<double>& a = verts2d[tri[0]];
00483     const vgl_point_2d<double>& b = verts2d[tri[1]];
00484     const vgl_point_2d<double>& c = verts2d[tri[2]];
00485     xy_to_barycentric(pt_img,a,b,c,pt_bary);
00486   }
00487 
00488   return tri_idx;
00489 }
00490 
00491 //: back project an image point onto the mesh using the camera
00492 //  Then project from the mesh into normalized texture coordinate (UV)
00493 //  Assumes the mesh has both normals and texture coordinates
00494 //  \returns the index of the intersected triangle, or -1 for no intersection
00495 int imesh_project_onto_mesh_texture(const imesh_mesh& mesh,
00496                                     const vcl_vector<vgl_point_2d<double> >& verts2d,
00497                                     const vpgl_perspective_camera<double>& camera,
00498                                     const vgl_point_2d<double>& pt_img,
00499                                     vgl_point_2d<double>& pt_uv)
00500 {
00501   if (!mesh.faces().has_normals() || !mesh.has_tex_coords())
00502     return -1;
00503 
00504   // get mesh faces as triangles
00505   assert(mesh.faces().regularity() == 3);
00506   const imesh_regular_face_array<3>& tris =
00507       static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00508 
00509   const vcl_vector<vgl_point_2d<double> >& tex_coords = mesh.tex_coords();
00510 
00511   vgl_point_2d<double> pt_bary;
00512   int tri_idx = imesh_project_onto_mesh_barycentric(mesh, tris.normals(),
00513                                                     verts2d, camera,
00514                                                     pt_img, pt_bary);
00515   unsigned int i1, i2, i3;
00516   if (mesh.has_tex_coords() == imesh_mesh::TEX_COORD_ON_VERT) {
00517     const imesh_regular_face<3>& tri = tris[tri_idx];
00518     i1 = tri[0];
00519     i2 = tri[1];
00520     i3 = tri[2];
00521   }
00522   else {
00523     imesh_half_edge_set::f_const_iterator fi = mesh.half_edges().face_begin(tri_idx);
00524     assert(tris[tri_idx][0] == fi->vert_index());
00525     i1 = fi->edge_index();
00526     i2 = (++fi)->edge_index();
00527     i3 = (++fi)->edge_index();
00528   }
00529   if (tri_idx >= 0) {
00530     const vgl_point_2d<double>& a = tex_coords[i1];
00531     const vgl_point_2d<double>& b = tex_coords[i2];
00532     const vgl_point_2d<double>& c = tex_coords[i3];
00533     barycentric_to_xy(pt_bary,a,b,c,pt_uv);
00534   }
00535 
00536   return tri_idx;
00537 }
00538 
00539 
00540 //: project a texture point onto a mesh face index with barycentric coordinates
00541 //  \returns the index of the intersected triangle, or -1 for no intersection
00542 int imesh_project_texture_to_barycentric(const imesh_mesh& mesh,
00543                                          const vgl_point_2d<double>& pt_2d,
00544                                          vgl_point_2d<double>& pt_uv)
00545 {
00546   // get mesh faces as triangles
00547   assert(mesh.faces().regularity() == 3);
00548   const imesh_regular_face_array<3>& tris =
00549       static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00550   typedef imesh_regular_face_array<3>::const_iterator itr_t;
00551 
00552   if (!mesh.has_tex_coords())
00553     return imesh_invalid_idx;
00554 
00555   const vcl_vector<vgl_point_2d<double> >& tc = mesh.tex_coords();
00556 
00557   if (mesh.has_tex_coords() == imesh_mesh::TEX_COORD_ON_VERT)
00558   {
00559     int i = 0;
00560     for (itr_t itr = tris.begin(); itr != tris.end(); ++itr, ++i) {
00561       const vgl_point_2d<double>& a = tc[(*itr)[0]];
00562       const vgl_point_2d<double>& b = tc[(*itr)[1]];
00563       const vgl_point_2d<double>& c = tc[(*itr)[2]];
00564       double dsc = vgl_triangle_test_discriminant(a.x(),a.y(),b.x(),b.y(),c.x(),c.y());
00565       if (dsc <= 0.0) continue;
00566       xy_to_barycentric(pt_2d,a,b,c,pt_uv);
00567       if (pt_uv.x() < 0.0) continue;
00568       if (pt_uv.y() < 0.0) continue;
00569       if (pt_uv.x()+pt_uv.y() >1.0) continue;
00570 
00571       return i;
00572     }
00573   }
00574 
00575   return imesh_invalid_idx;
00576 }
00577 
00578 #if 1
00579 namespace{
00580 
00581 // intersect a straight line in texture space with the texture triangles
00582 bool trace_texture(const imesh_mesh& mesh,
00583                    const vgl_point_2d<double>& end_xy,
00584                    vcl_vector<unsigned long>& idxs,
00585                    vcl_vector<vgl_point_2d<double> >& isect_bary)
00586 {
00587   const vcl_vector<vgl_point_2d<double> >& tc = mesh.tex_coords();
00588   assert(mesh.faces().regularity() == 3);
00589   const imesh_regular_face_array<3>& tris =
00590       static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00591   assert(mesh.has_half_edges());
00592   const imesh_half_edge_set& he = mesh.half_edges();
00593   assert(!isect_bary.empty());
00594 
00595   double eps = 1e-2;
00596   double eps2 = eps*eps;
00597 
00598   vgl_point_2d<double> nbary;
00599 
00600   // get the last point and start from here
00601   unsigned long tidx = idxs.back();
00602   vgl_point_2d<double> bary = isect_bary.back();
00603 
00604   // split into intersection type and half edge index
00605   unsigned long  heidx = tidx>>2;
00606   //unsigned char type = tidx & 3;
00607 
00608   for (;;)
00609   {
00610     unsigned int fidx = he[heidx].face_index();
00611     const vgl_point_2d<double>& a = tc[tris[fidx][0]];
00612     const vgl_point_2d<double>& b = tc[tris[fidx][1]];
00613     const vgl_point_2d<double>& c = tc[tris[fidx][2]];
00614     // compute barycentric coordinates of the end point
00615     xy_to_barycentric(end_xy,a,b,c,nbary);
00616     // compute the line direction in the current barycentric coordinates
00617     vgl_vector_2d<double> duv(bary-nbary);
00618     // if the last point was close enough to the end
00619     if (duv.sqr_length() <eps2)
00620       return true;
00621 
00622     // if the end point is inside the triangle then we are done
00623     if (nbary.x() > 0 && nbary.y() > 0 && 1.0-nbary.x()-nbary.y()>0) {
00624       idxs.push_back(heidx<<2); // this is a face point
00625       isect_bary.push_back(nbary);
00626       return true;
00627     }
00628     else if ((nbary.x()==0.0 && nbary.y()>0 && nbary.y()<1) ||
00629             (nbary.y()==0.0 && nbary.x()>0 && nbary.x()<1) ||
00630             (nbary.x()+nbary.y()==1.0 && nbary.x()>0 && nbary.y()>0)) {
00631       idxs.push_back((heidx<<2) + 1); // this is an edge point
00632       isect_bary.push_back(nbary);
00633       return true;
00634     }
00635 
00636     // intersect the line with the with the current triangle
00637     unsigned char s = imesh_triangle_intersect(bary.x(),bary.y(),duv.x(),duv.y(), eps);
00638     typedef imesh_half_edge_set::f_const_iterator fitr;
00639     fitr fi = he.face_begin(fidx);
00640     assert(fi->face_index() == fidx);
00641     if (s == 3 || s == 5 || s == 6) // intersects with an edge
00642     {
00643       double t = 1 - bary.x();
00644       if (s==6) {
00645         ++fi;
00646         t = bary.x();
00647       }
00648       if (s==5) {
00649         ++fi;
00650         ++fi;
00651         t = bary.y();
00652       }
00653 
00654 
00655       // switch to the triangle on the other side of the edge
00656       heidx = fi->pair_index();
00657       fidx = he[heidx].face_index();
00658 
00659       // check if the next triangle is out of bounds
00660       if (fidx == imesh_invalid_idx ||
00661           (!mesh.valid_tex_faces().empty() &&
00662            !mesh.valid_tex_faces()[fidx]) )
00663       {
00664         // add the boundary point and exit
00665         idxs.push_back((fi->half_edge_index()<<2) + 1); // this is an edge point
00666         isect_bary.push_back(bary);
00667         return false;
00668       }
00669 
00670       // map the barycentric coordinates into those of the adjacent triangle
00671       fi = he.face_begin(fidx);
00672       unsigned char s2 = 0;
00673       for (; fi->half_edge_index() != heidx && s2<4; ++s2, ++fi)
00674         ;
00675       bary.set(0,0);
00676       switch (s2)
00677       {
00678         case 0: bary.x() = t; break;
00679         case 1: bary.x() = 1-t; bary.y()=t; break;
00680         case 2: bary.y() = 1-t; break;
00681         default:
00682           assert(false);
00683       }
00684 
00685       // insert the intersection
00686       idxs.push_back((heidx<<2) + 1); // this is an edge point
00687       isect_bary.push_back(bary);
00688     }
00689     else if (s == 1 || s == 2 || s == 4) // intersects with a vertex
00690     {
00691       if (s==2) {
00692         ++fi;
00693       }
00694       if (s==4) {
00695         ++fi;
00696         ++fi;
00697       }
00698       assert(fi->vert_index() == tris[fidx][(s==4)?2:s-1]);
00699 
00700       // find the direction vector in texture coordinates
00701       vgl_vector_2d<double> dir(end_xy-tc[fi->vert_index()]);
00702       normalize(dir);
00703 
00704       // find the face adjacent to the vertex that contains the outgoing vector
00705       imesh_half_edge_set::v_const_iterator vi(fi);
00706       imesh_half_edge_set::v_const_iterator vend(vi);
00707       ++vi;
00708       double max_ang = -2*vnl_math::pi;
00709       unsigned long max_heidx = 0;
00710       for (; vi!=vend; ++vi) {
00711         vgl_vector_2d<double> d(tc[he[vi->pair_index()].vert_index()]
00712                                 - tc[vi->vert_index()]);
00713         // rotate to compute angle relative to dir
00714         double ang = vcl_atan2(d.y()*dir.x()-d.x()*dir.y(),
00715                                d.x()*dir.x()+d.y()*dir.y());
00716         if (ang > 0) ang -= 2*vnl_math::pi;
00717         if (ang > max_ang) {
00718           max_ang = ang;
00719           max_heidx = vi->half_edge_index();
00720         }
00721       }
00722       // switch to the triangle on the other side of the vertex
00723       heidx = max_heidx;
00724       fidx = he[heidx].face_index();
00725 
00726       // check if the next triangle is out of bounds
00727       if (fidx == imesh_invalid_idx ||
00728           (!mesh.valid_tex_faces().empty() &&
00729            !mesh.valid_tex_faces()[fidx]) )
00730       {
00731         // add the boundary point and exit
00732         idxs.push_back((fi->half_edge_index()<<2) + 2); // this is an edge point
00733         isect_bary.push_back(bary);
00734         return false;
00735       }
00736 
00737       // map the barycentric coordinates into those of the next triangle
00738       unsigned vidx = fi->vert_index();
00739       fi = he.face_begin(fidx);
00740       unsigned char s2 = 0;
00741       for (; fi->vert_index() != vidx && s2<4; ++s2, ++fi)
00742         ;
00743       bary.set(0,0);
00744       switch (s2)
00745       {
00746         case 0: break;
00747         case 1: bary.x() = 1; break;
00748         case 2: bary.y() = 1; break;
00749         default:
00750           assert(false);
00751       }
00752 
00753       // insert the intersection
00754       idxs.push_back((heidx<<2) + 2); // this is a vertex point
00755       isect_bary.push_back(bary);
00756     }
00757     else
00758     {
00759       vcl_cout << "invalid intersection" << vcl_endl;
00760       return false;
00761     }
00762   }
00763   return false; // will never be reached! -- just avoids a compiler warning
00764 }
00765 // end of namespace
00766 }
00767 #endif
00768 
00769 //: project a texture polygon into barycentric coordinates
00770 //  \returns true if the polygon is not clipped by the mesh texture
00771 //  \returns the vector of barycentric points by reference
00772 //  \returns a vector of coded half edge indices
00773 //  the half edge indices are scaled by a factor of 4
00774 //  the last two bits indicate the intersection type:
00775 //  - 0 for face
00776 //  - 1 for edge
00777 //  - 2 for vertex
00778 //  barycentric coordinate refer to the adjacent triangle
00779 //  \returns a mapping from each original point into barycentric points.
00780 //  if an original point is not mapped the value is -1
00781 bool imesh_project_texture_to_barycentric(const imesh_mesh& mesh,
00782                                           const vcl_vector<vgl_point_2d<double> >& pts_2d,
00783                                           vcl_vector<vgl_point_2d<double> >& pts_uv,
00784                                           vcl_vector<unsigned long>& idxs,
00785                                           vcl_vector<int>& map_back)
00786 {
00787   bool clipped = false;
00788   const unsigned int npts = pts_2d.size();
00789   pts_uv.clear();
00790   idxs.clear();
00791   map_back.clear();
00792   map_back.resize(npts,-1);
00793   assert(mesh.faces().regularity() == 3);
00794   assert(mesh.has_half_edges());
00795   const imesh_half_edge_set& he = mesh.half_edges();
00796 
00797   // find a starting point
00798   unsigned int i;
00799   unsigned int idx=0;
00800   vgl_point_2d<double> pt_uv;
00801   for (i=0; i<npts; ++i)
00802   {
00803     const vgl_point_2d<double>& pt = pts_2d[i];
00804     if (pt.x() >= 0.0 && pt.x() <= 1.0 && pt.y() >= 0.0 && pt.y() <= 1.0)
00805     {
00806       idx = imesh_project_texture_to_barycentric(mesh,pt,pt_uv);
00807       if (idx != imesh_invalid_idx)
00808         break;
00809     }
00810     clipped = true;
00811   }
00812   if (i==npts) // includes the case npts==0; in which case idx would be uninitialized
00813     return false;
00814 
00815   idxs.push_back(he.face_begin(idx)->half_edge_index()<<2);
00816   pts_uv.push_back(pt_uv);
00817   map_back[i] = 0;
00818 
00819   // trace the rest of the polygon
00820   bool valid = true;
00821   int num_found = 0;
00822   for (unsigned int j=(i+1)%npts; j!=i; j=(j+1)%npts, ++num_found )
00823   {
00824     valid = trace_texture(mesh,pts_2d[j],idxs,pts_uv);
00825     if (!valid)
00826       break; // curve is clipped so exit here
00827     map_back[j] = idxs.size()-1;
00828   }
00829   // if the curve was clipped, try tracing in reverse to get the rest
00830   if (!valid)
00831   {
00832     vcl_vector<vgl_point_2d<double> > rev_pts_uv(1,pts_uv.front());
00833     vcl_vector<unsigned long> rev_idxs(1,idxs.front());
00834     vcl_vector<int> rev_map_back(pts_2d.size(),-1);
00835     rev_map_back[i]=0;
00836     const unsigned int step = npts-1;
00837     for (unsigned int j=(i+step)%npts; j!=i; j=(j+step)%npts )
00838     {
00839       valid = trace_texture(mesh,pts_2d[j],rev_idxs,rev_pts_uv);
00840       if (!valid)
00841         break; // curve is clipped so exit here
00842       rev_map_back[j] = rev_idxs.size()-1;
00843     }
00844 
00845     // reverse the reverse searching arrays and append the original search
00846     for (unsigned int j=0; j<npts; ++j)
00847     {
00848       if (map_back[j] >=0)
00849         map_back[j] += rev_idxs.size()-1;
00850       else if (rev_map_back[j] >= 0)
00851         map_back[j] = rev_idxs.size()-1 - rev_map_back[j];
00852     }
00853 
00854     vcl_reverse(rev_pts_uv.begin(),rev_pts_uv.end());
00855     vcl_reverse(rev_idxs.begin(),rev_idxs.end());
00856     unsigned int num_new_pts = rev_pts_uv.size();
00857     rev_pts_uv.resize(rev_pts_uv.size()+pts_uv.size()-1);
00858     vcl_copy(pts_uv.begin()+1, pts_uv.end(), rev_pts_uv.begin()+num_new_pts);
00859     rev_idxs.resize(rev_idxs.size()+idxs.size()-1);
00860     vcl_copy(idxs.begin()+1, idxs.end(), rev_idxs.begin()+num_new_pts);
00861 
00862 
00863     idxs.swap(rev_idxs);
00864     pts_uv.swap(rev_pts_uv);
00865 
00866     return true; // return true to indicate clipping
00867   }
00868 
00869   // connect back to the first point, but remove the duplicated first point
00870   valid = trace_texture(mesh,pts_2d[i],idxs,pts_uv);
00871   if (!valid)
00872     return true;
00873 
00874   idxs.pop_back();
00875   pts_uv.pop_back();
00876 
00877   assert(pts_uv.size() == idxs.size());
00878 
00879   return clipped;
00880 }
00881 
00882 
00883 //: compute the matrix that maps texture points to 3-d for a given triangle index
00884 // $(u,v,1)$ maps into 3-d $(x,y,z)$
00885 vnl_matrix_fixed<double,3,3>
00886 imesh_project_texture_to_3d_map(const imesh_mesh& mesh, unsigned int tidx)
00887 {
00888   assert(mesh.has_tex_coords());
00889   const imesh_regular_face_array<3>& triangles =
00890       static_cast<const imesh_regular_face_array<3>&>(mesh.faces());
00891   const imesh_regular_face<3>& tri = triangles[tidx];
00892   const vcl_vector<vgl_point_2d<double> >& tex = mesh.tex_coords();
00893   const imesh_vertex_array<3>& verts = mesh.vertices<3>();
00894 
00895   vnl_matrix_fixed<double,3,3> M1;
00896   {
00897     const vgl_point_2d<double>& a = tex[tri[0]];
00898     const vgl_point_2d<double>& b = tex[tri[1]];
00899     const vgl_point_2d<double>& c = tex[tri[2]];
00900     vgl_vector_2d<double> v1(b-a), v2(c-a);
00901     double s = 1.0 / (v2.y()*v1.x() - v2.x()*v1.y());
00902     M1(0,0) = v2.y()*s;   M1(0,1) = -v2.x()*s;   M1(0,2) = -a.x()*s;
00903     M1(1,0) = -v1.y()*s;  M1(1,1) = v1.x()*s;    M1(1,2) = -a.y()*s;
00904     M1(2,0) = 0.0;        M1(2,1) = 0.0;         M1(2,2) = 1.0;
00905   }
00906 
00907   vnl_matrix_fixed<double,3,3> M2;
00908   {
00909     const imesh_vertex<3>& a = verts[tri[0]];
00910     const imesh_vertex<3>& b = verts[tri[1]];
00911     const imesh_vertex<3>& c = verts[tri[2]];
00912     vgl_vector_3d<double> v1(b[0]-a[0],b[1]-a[1],b[2]-a[2]),
00913                           v2(c[0]-a[0],c[1]-a[1],c[2]-a[2]);
00914     M2(0,0) = v1.x();   M2(0,1) = v2.x();   M2(0,2) = a[0];
00915     M2(1,0) = v1.y();   M2(1,1) = v2.y();   M2(1,2) = a[1];
00916     M2(2,0) = v1.z();   M2(2,1) = v2.z();   M2(2,2) = a[2];
00917   }
00918 
00919 
00920   return M2*M1;
00921 }
00922 
00923 
00924 //: compute the affine matrix that maps triangle (a1,b1,c1) to (a2,b2,c2)
00925 vnl_matrix_fixed<double,3,3>
00926 imesh_affine_map(const vgl_point_2d<double>& a1,
00927                  const vgl_point_2d<double>& b1,
00928                  const vgl_point_2d<double>& c1,
00929                  const vgl_point_2d<double>& a2,
00930                  const vgl_point_2d<double>& b2,
00931                  const vgl_point_2d<double>& c2)
00932 {
00933   // triangle 1 to barycentric coordinates
00934   vnl_matrix_fixed<double,3,3> M1;
00935   {
00936     vgl_vector_2d<double> v1(b1-a1), v2(c1-a1);
00937     double s = 1.0 / (v2.y()*v1.x() - v2.x()*v1.y());
00938     M1(0,0) = v2.y()*s;   M1(0,1) = -v2.x()*s;   M1(0,2) = (a1.y()*c1.x()-a1.x()*c1.y())*s;
00939     M1(1,0) = -v1.y()*s;  M1(1,1) = v1.x()*s;    M1(1,2) = (a1.x()*b1.y()-a1.y()*b1.x())*s;
00940     M1(2,0) = 0.0;        M1(2,1) = 0.0;         M1(2,2) = 1.0;
00941   }
00942 
00943   // barycentric coordinates to triangle 2
00944   vnl_matrix_fixed<double,3,3> M2;
00945   {
00946     vgl_vector_2d<double> v1(b2-a2), v2(c2-a2);
00947     M2(0,0) = v1.x();   M2(0,1) = v2.x();   M2(0,2) = a2.x();
00948     M2(1,0) = v1.y();   M2(1,1) = v2.y();   M2(1,2) = a2.y();
00949     M2(2,0) = 0.0;      M2(2,1) = 0.0;      M2(2,2) = 1.0;
00950   }
00951 
00952   return M2*M1;
00953 }
00954 
00955 
00956 //: project barycentric coordinates with an index to texture space
00957 //  \param idx is the face index
00958 vgl_point_2d<double>
00959 imesh_project_barycentric_to_texture(const imesh_mesh& mesh,
00960                                      const vgl_point_2d<double>& pt_uv,
00961                                      unsigned int idx)
00962 {
00963   assert(mesh.has_tex_coords());
00964   const vcl_vector<vgl_point_2d<double> >& tc = mesh.tex_coords();
00965   const vgl_point_2d<double>& a = tc[mesh.faces()(idx,0)];
00966   const vgl_point_2d<double>& b = tc[mesh.faces()(idx,1)];
00967   const vgl_point_2d<double>& c = tc[mesh.faces()(idx,2)];
00968   vgl_point_2d<double> p;
00969   barycentric_to_xy(pt_uv,a,b,c,p);
00970   return p;
00971 }
00972 
00973 
00974 //: project barycentric coordinates with an index the mesh surface (3D)
00975 //  \param idx is the face index
00976 vgl_point_3d<double>
00977 imesh_project_barycentric_to_mesh(const imesh_mesh& mesh,
00978                                   const vgl_point_2d<double>& pt_uv,
00979                                   unsigned int idx)
00980 {
00981   vgl_point_3d<double> pt(0,0,0);
00982   unsigned int v1 = mesh.faces()(idx,0);
00983   unsigned int v2 = mesh.faces()(idx,1);
00984   unsigned int v3 = mesh.faces()(idx,2);
00985   const imesh_vertex_array<3>& verts = mesh.vertices<3>();
00986   pt += (1-pt_uv.x()-pt_uv.y())*vgl_vector_3d<double>(verts[v1][0],verts[v1][1],verts[v1][2]);
00987   pt += pt_uv.x()*vgl_vector_3d<double>(verts[v2][0],verts[v2][1],verts[v2][2]);
00988   pt += pt_uv.y()*vgl_vector_3d<double>(verts[v3][0],verts[v3][1],verts[v3][2]);
00989 
00990   return pt;
00991 }