BWAPI
SPAR/AIModule/BWTA/vendors/CGAL/CGAL/Surface_mesh_simplification/Detail/Edge_collapse_impl.h
Go to the documentation of this file.
00001 // Copyright (c) 2006  GeometryFactory (France). All rights reserved.
00002 //
00003 // This file is part of CGAL (www.cgal.org); you may redistribute it under
00004 // the terms of the Q Public License version 1.0.
00005 // See the file LICENSE.QPL distributed with CGAL.
00006 //
00007 // Licensees holding a valid commercial license may use this file in
00008 // accordance with the commercial license agreement provided with the software.
00009 //
00010 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00011 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00012 //
00013 // $URL: svn+ssh://scm.gforge.inria.fr/svn/cgal/branches/CGAL-3.5-branch/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Detail/Edge_collapse_impl.h $
00014 // $Id: Edge_collapse_impl.h 50078 2009-06-25 15:12:52Z fcacciola $
00015 //
00016 // Author(s)     : Fernando Cacciola <fernando.cacciola@geometryfactory.com>
00017 //
00018 #ifndef CGAL_SURFACE_MESH_SIMPLIFICATION_DETAIL_EDGE_COLLAPSE_IMPL_H
00019 #define CGAL_SURFACE_MESH_SIMPLIFICATION_DETAIL_EDGE_COLLAPSE_IMPL_H
00020 
00021 CGAL_BEGIN_NAMESPACE
00022 
00023 namespace Surface_mesh_simplification 
00024 {
00025 
00026 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00027 EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::EdgeCollapse( ECM&                    aSurface
00028                                                     , ShouldStop       const& aShould_stop
00029                                                     , VertexIndexMap   const& aVertex_index_map 
00030                                                     , EdgeIndexMap     const& aEdge_index_map 
00031                                                     , EdgeIsBorderMap  const& aEdge_is_border_map 
00032                                                     , GetCost          const& aGet_cost
00033                                                     , GetPlacement     const& aGet_placement
00034                                                     , VisitorT         const& aVisitor
00035                                                     )
00036   : 
00037    mSurface           (aSurface)
00038   ,Should_stop        (aShould_stop) 
00039   ,Vertex_index_map   (aVertex_index_map)
00040   ,Edge_index_map     (aEdge_index_map)
00041   ,Edge_is_border_map (aEdge_is_border_map)
00042   ,Get_cost           (aGet_cost)
00043   ,Get_placement      (aGet_placement)
00044   ,Visitor            (aVisitor)
00045 {
00046   const FT cMaxDihedralAngleCos = std::cos( 1.0 * CGAL_PI / 180.0 ) ;
00047   
00048   mcMaxDihedralAngleCos2 = cMaxDihedralAngleCos * cMaxDihedralAngleCos ;
00049   
00050   CGAL_ECMS_TRACE(0,"EdgeCollapse of ECM with " << (num_edges(aSurface)/2) << " edges" ); 
00051   
00052   CGAL_ECMS_DEBUG_CODE ( mStep = 0 ; )
00053   
00054 #ifdef CGAL_SURFACE_SIMPLIFICATION_ENABLE_TRACE
00055     vertex_iterator vb, ve ;     
00056     for ( boost::tie(vb,ve) = boost::vertices(mSurface) ; vb != ve ; ++ vb )  
00057       CGAL_ECMS_TRACE(1, vertex_to_string(*vb) ) ;
00058       
00059     undirected_edge_iterator eb, ee ;
00060     for ( boost::tie(eb,ee) = undirected_edges(mSurface); eb!=ee; ++eb )
00061       CGAL_ECMS_TRACE(1, edge_to_string(*eb) ) ;
00062 #endif
00063 }
00064 
00065 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00066 int EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::run()
00067 {
00068   CGAL_SURF_SIMPL_TEST_assertion( mSurface.is_valid() && mSurface.is_pure_triangle() ) ;
00069 
00070   Visitor.OnStarted(mSurface);
00071    
00072   // First collect all candidate edges in a PQ
00073   Collect(); 
00074   
00075   // Then proceed to collapse each edge in turn
00076   Loop();
00077 
00078   CGAL_ECMS_TRACE(0,"Finished: " << (mInitialEdgeCount - mCurrentEdgeCount) << " edges removed." ) ;
00079 
00080   int r = (int)(mInitialEdgeCount - mCurrentEdgeCount) ;
00081     
00082   Visitor.OnFinished(mSurface);
00083     
00084   return r ;
00085 }
00086 
00087 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00088 void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collect()
00089 {
00090   CGAL_ECMS_TRACE(0,"Collecting edges...");
00091 
00092   //
00093   // Loop over all the _undirected_ edges in the surface putting them in the PQ
00094   //
00095   
00096   Equal_3 equal_points = Kernel().equal_3_object();
00097     
00098   size_type lSize = num_edges(mSurface) / 2 ;
00099   
00100   CGAL_SURF_SIMPL_TEST_assertion( ( lSize * 2 ) == mSurface.size_of_halfedges() ) ;
00101 
00102   mInitialEdgeCount = mCurrentEdgeCount = lSize;
00103   
00104   mEdgeDataArray.reset( new Edge_data[lSize] ) ;
00105   
00106   mPQ.reset( new PQ (lSize, Compare_cost(this), Undirected_edge_id(this) ) ) ;
00107   
00108   std::size_t id = 0 ;
00109   
00110   CGAL_SURF_SIMPL_TEST_assertion_code ( size_type lInserted    = 0 ) ;
00111   CGAL_SURF_SIMPL_TEST_assertion_code ( size_type lNotInserted = 0 ) ;
00112 
00113   undirected_edge_iterator eb, ee ;
00114   for ( tie(eb,ee) = undirected_edges(mSurface); eb!=ee; ++eb )
00115   {
00116     edge_descriptor lEdge = *eb ;
00117   
00118     CGAL_assertion( get_directed_edge_id(lEdge) == id ) ;
00119     CGAL_assertion( get_directed_edge_id(opposite_edge(lEdge,mSurface)) == id+1 ) ;
00120 
00121     Profile const& lProfile = create_profile(lEdge);
00122           
00123     if ( !equal_points(lProfile.p0(),lProfile.p1()) )
00124     {
00125       Edge_data& lData = get_data(lEdge);
00126 
00127       lData.cost() = get_cost(lProfile) ;
00128       insert_in_PQ(lEdge,lData);
00129       
00130       Visitor.OnCollected(lProfile,lData.cost());
00131 
00132       CGAL_SURF_SIMPL_TEST_assertion_code ( ++ lInserted ) ;
00133     }
00134     else
00135     {
00136       CGAL_SURF_SIMPL_TEST_assertion_code ( ++ lNotInserted ) ;
00137     }
00138 
00139     
00140     CGAL_ECMS_TRACE(2,edge_to_string(lEdge));
00141     
00142     id += 2 ;
00143   }
00144  
00145   CGAL_SURF_SIMPL_TEST_assertion ( lInserted + lNotInserted == mInitialEdgeCount ) ;
00146 
00147   CGAL_ECMS_TRACE(0,"Initial edge count: " << mInitialEdgeCount ) ;
00148 }
00149 
00150 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00151 void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Loop()
00152 {
00153   CGAL_ECMS_TRACE(0,"Collapsing edges...") ;
00154 
00155   CGAL_SURF_SIMPL_TEST_assertion_code ( size_type lLoop_watchdog = 0 ) ;
00156   CGAL_SURF_SIMPL_TEST_assertion_code ( size_type lNonCollapsableCount = 0 ) ;
00157 
00158   //
00159   // Pops and processes each edge from the PQ
00160   //
00161   optional<edge_descriptor> lEdge ;
00162   while ( (lEdge = pop_from_PQ()) )
00163   {
00164     CGAL_SURF_SIMPL_TEST_assertion ( lLoop_watchdog ++ < mInitialEdgeCount ) ;
00165 
00166     CGAL_ECMS_TRACE(1,"Poped " << edge_to_string(*lEdge) ) ;
00167     
00168     Profile const& lProfile = create_profile(*lEdge);
00169       
00170     Cost_type lCost = get_data(*lEdge).cost();
00171     
00172     Visitor.OnSelected(lProfile,lCost,mInitialEdgeCount,mCurrentEdgeCount);
00173       
00174     if ( lCost ) 
00175     {
00176       if ( Should_stop(*lCost,lProfile,mInitialEdgeCount,mCurrentEdgeCount) )
00177       {
00178         Visitor.OnStopConditionReached(lProfile);
00179           
00180         CGAL_ECMS_TRACE(0,"Stop condition reached with InitialEdgeCount=" << mInitialEdgeCount
00181                        << " CurrentEdgeCount=" << mCurrentEdgeCount
00182                        << " Current Edge: " << edge_to_string(*lEdge)
00183                        );
00184         break ;
00185       }
00186         
00187       if ( Is_collapse_topologically_valid(lProfile) )
00188       {
00189         // The external function Get_new_vertex_point() is allowed to return an absent point if there is no way to place the vertex
00190         // satisfying its constrians. In that case the remaining vertex is simply left unmoved.
00191         Placement_type lPlacement = get_placement(lProfile);
00192         
00193         if ( Is_collapse_geometrically_valid(lProfile,lPlacement) )
00194           Collapse(lProfile,lPlacement);
00195       }
00196       else
00197       {
00198         CGAL_SURF_SIMPL_TEST_assertion_code ( lNonCollapsableCount++ ) ;
00199 
00200         Visitor.OnNonCollapsable(lProfile);
00201           
00202         CGAL_ECMS_TRACE(1,edge_to_string(*lEdge) << " NOT Collapsable"  );
00203       }  
00204     }
00205     else
00206     {
00207       CGAL_ECMS_TRACE(1,edge_to_string(*lEdge) << " uncomputable cost."  );
00208     }
00209   }
00210 }
00211 
00212 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00213 bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::is_border( const_vertex_descriptor const& aV ) const
00214 {
00215   bool rR = false ;
00216   
00217   const_in_edge_iterator eb, ee ; 
00218   for ( tie(eb,ee) = in_edges(aV,mSurface) ; eb != ee ; ++ eb )
00219   {
00220     const_edge_descriptor lEdge = *eb ;
00221     if ( is_undirected_edge_a_border(lEdge) )
00222     {
00223       rR = true ;
00224       break ;
00225     }
00226   }  
00227     
00228   return rR ;  
00229 }
00230 
00231 // Some edges are NOT collapsable: doing so would break the topological consistency of the mesh.
00232 // This function returns true if a edge 'p->q' can be collapsed.
00233 //
00234 // An edge p->q can be collapsed iff it satisfies the "link condition"
00235 // (as described in the "Mesh Optimization" article of Hoppe et al (1993))
00236 //
00237 // The link conidition is as follows: for every vertex 'k' adjacent to both 'p and 'q',
00238 // "p,k,q" is a facet of the mesh.
00239 //
00240 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00241 bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_topologically_valid( Profile const& aProfile )
00242 {
00243   bool rR = true ;
00244 
00245   CGAL_ECMS_TRACE(3,"Testing topological collapsabilty of p_q=V" << aProfile.v0()->id() << "(%" << aProfile.v0()->vertex_degree() << ")"
00246                  << "->V" << aProfile.v1()->id() << "(%" << aProfile.v1()->vertex_degree() << ")" 
00247                  );
00248                  
00249   CGAL_ECMS_TRACE(4, "is p_q border:" << aProfile.is_v0_v1_a_border() );
00250   CGAL_ECMS_TRACE(4, "is q_q border:" << aProfile.is_v1_v0_a_border() );
00251 
00252   out_edge_iterator eb1, ee1 ; 
00253   out_edge_iterator eb2, ee2 ; 
00254 
00255   CGAL_ECMS_TRACE(4,"  t=V" 
00256                    << ( aProfile.left_face_exists() ? aProfile.vL()->id() : -1 )
00257                    << "(%" 
00258                    << ( aProfile.left_face_exists() ? aProfile.vL()->vertex_degree() : 0 ) 
00259                    << ")" 
00260                  );
00261   CGAL_ECMS_TRACE(4,"  b=V" 
00262                    << ( aProfile.right_face_exists() ? aProfile.vR()->id() : -1 )
00263                    << "(%" 
00264                    << ( aProfile.right_face_exists() ? aProfile.vR()->vertex_degree() :0 ) 
00265                    << ")" 
00266                    );
00267 
00268   // The following loop checks the link condition for v0_v1.
00269   // Specifically, that for every vertex 'k' adjacent to both 'p and 'q', 'pkq' is a face of the mesh.
00270   // 
00271   for ( tie(eb1,ee1) = out_edges(aProfile.v0(),mSurface) ; rR && eb1 != ee1 ; ++ eb1 )
00272   {
00273     edge_descriptor v0_k = *eb1 ;
00274     
00275     if ( v0_k != aProfile.v0_v1() )
00276     {
00277       vertex_descriptor k = target(v0_k,mSurface);
00278       
00279       for ( tie(eb2,ee2) = out_edges(k,mSurface) ; rR && eb2 != ee2 ; ++ eb2 )
00280       {
00281         edge_descriptor k_v1 = *eb2 ;
00282 
00283         if ( target(k_v1,mSurface) == aProfile.v1() )
00284         {
00285           // At this point we know p-q-k are connected and we need to determine if this triangle is a face of the mesh.
00286           //
00287           // Since the mesh is known to be triangular there are at most two faces sharing the edge p-q.
00288           //
00289           // If p->q is NOT a border edge, the top face is p->q->t where t is target(next(p->q))
00290           // If q->p is NOT a border edge, the bottom face is q->p->b where b is target(next(q->p))
00291           //
00292           // If k is either t or b then p-q-k *might* be a face of the mesh. It won't be if k==t but p->q is border
00293           // or k==b but q->b is a border (because in that case even though there exists triangles p->q->t (or q->p->b)
00294           // they are holes, not faces)
00295           // 
00296      
00297           bool lIsFace =   ( aProfile.vL() == k && aProfile.left_face_exists () )
00298                         || ( aProfile.vR() == k && aProfile.right_face_exists() ) ;
00299                         
00300           CGAL_SURF_SIMPL_TEST_assertion_code
00301           ( 
00302             if ( lIsFace )
00303             {
00304               // Is k_v1 the halfedge bounding the face 'k-v1-v0'?
00305               if ( !k_v1->is_border() && k_v1->next()->vertex() == aProfile.v0() )
00306               {
00307                 CGAL_SURF_SIMPL_TEST_assertion( !k_v1->is_border() ) ;
00308                 CGAL_SURF_SIMPL_TEST_assertion(  k_v1                ->vertex() == aProfile.v1() ) ;
00309                 CGAL_SURF_SIMPL_TEST_assertion(  k_v1->next()        ->vertex() == aProfile.v0() ) ;
00310                 CGAL_SURF_SIMPL_TEST_assertion(  k_v1->next()->next()->vertex() == k ) ;
00311               }
00312               else // or is it the opposite?
00313               {
00314                 edge_descriptor v1_k = k_v1->opposite();
00315                 CGAL_SURF_SIMPL_TEST_assertion( !v1_k->is_border() ) ;
00316                 CGAL_SURF_SIMPL_TEST_assertion(  v1_k                ->vertex() == k ) ;
00317                 CGAL_SURF_SIMPL_TEST_assertion(  v1_k->next()        ->vertex() == aProfile.v0() ) ;
00318                 CGAL_SURF_SIMPL_TEST_assertion(  v1_k->next()->next()->vertex() == aProfile.v1() ) ;
00319               }
00320             }
00321           );
00322 
00323           if ( !lIsFace )
00324           {
00325             CGAL_ECMS_TRACE(3,"  k=V" << k->id() << " IS NOT in a face with p-q. NON-COLLAPSABLE edge." ) ;
00326             rR = false ;
00327             break ;
00328           }  
00329           else 
00330           {
00331             CGAL_ECMS_TRACE(4,"  k=V" << k->id() << " is in a face with p-q") ;
00332           }
00333         }
00334       }  
00335     }
00336   }   
00337      
00338   if ( rR )
00339   {
00340     if ( aProfile.is_v0_v1_a_border() )
00341     {
00342       if ( Is_open_triangle(aProfile.v0_v1()) )
00343       {
00344         rR = false ;
00345         CGAL_ECMS_TRACE(3,"  p-q belongs to an open triangle. NON-COLLAPSABLE edge." ) ;
00346       }
00347     }
00348     else if ( aProfile.is_v1_v0_a_border() )
00349     {
00350       if ( Is_open_triangle(aProfile.v1_v0()) )
00351       {
00352         rR = false ;
00353         CGAL_ECMS_TRACE(3,"  p-q belongs to an open triangle. NON-COLLAPSABLE edge." ) ;
00354       }
00355     }
00356     else
00357     {
00358       if ( is_border(aProfile.v0()) && is_border(aProfile.v1()) )
00359       {
00360         rR = false ;
00361         CGAL_ECMS_TRACE(3,"  both p and q are boundary vertices but p-q is not. NON-COLLAPSABLE edge." ) ;
00362       }  
00363       else
00364       {
00365         bool lTetra = Is_tetrahedron(aProfile.v0_v1());
00366 
00367         CGAL_SURF_SIMPL_TEST_assertion( lTetra == mSurface.is_tetrahedron(aProfile.v0_v1()) ) ;
00368 
00369         if ( lTetra )
00370         {
00371           rR = false ;
00372           CGAL_ECMS_TRACE(3,"  p-q belongs to a tetrahedron. NON-COLLAPSABLE edge." ) ;
00373         }
00374       }
00375     }
00376   }
00377   
00378   return rR ;
00379 }
00380 
00381 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00382 bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_tetrahedron( edge_descriptor const& h1 )
00383 {
00384   //
00385   // Code copied from Polyhedron_3::is_tetrahedron()
00386   //
00387   edge_descriptor h2 = next_edge(h1,mSurface);
00388   edge_descriptor h3 = next_edge(h2,mSurface);
00389   
00390   edge_descriptor h1o = opposite_edge(h1,mSurface) ;
00391   edge_descriptor h2o = opposite_edge(h2,mSurface) ;
00392   edge_descriptor h3o = opposite_edge(h3,mSurface) ;
00393   
00394   edge_descriptor h4 = next_edge(h1o,mSurface);
00395   edge_descriptor h5 = next_edge(h2o,mSurface);
00396   edge_descriptor h6 = next_edge(h3o,mSurface);
00397   
00398   edge_descriptor h4o = opposite_edge(h4,mSurface) ;
00399   edge_descriptor h5o = opposite_edge(h5,mSurface) ;
00400   edge_descriptor h6o = opposite_edge(h6,mSurface) ;
00401   
00402   // check halfedge combinatorics.
00403   // at least three edges at vertices 1, 2, 3.
00404   if ( h4 == h3o ) return false;
00405   if ( h5 == h1o ) return false;
00406   if ( h6 == h2o ) return false;
00407   
00408   // exact three edges at vertices 1, 2, 3.
00409   if ( next_edge(h4o,mSurface) != h3o ) return false;
00410   if ( next_edge(h5o,mSurface) != h1o ) return false;
00411   if ( next_edge(h6o,mSurface) != h2o ) return false;
00412   
00413   // three edges at v4.
00414   if ( opposite_edge(next_edge(h4,mSurface),mSurface) != h5) return false;
00415   if ( opposite_edge(next_edge(h5,mSurface),mSurface) != h6) return false;
00416   if ( opposite_edge(next_edge(h6,mSurface),mSurface) != h4) return false;
00417   
00418   // All facets are triangles.
00419   if ( next_edge(next_edge(next_edge(h1,mSurface),mSurface),mSurface) != h1) return false;
00420   if ( next_edge(next_edge(next_edge(h4,mSurface),mSurface),mSurface) != h4) return false;
00421   if ( next_edge(next_edge(next_edge(h5,mSurface),mSurface),mSurface) != h5) return false;
00422   if ( next_edge(next_edge(next_edge(h6,mSurface),mSurface),mSurface) != h6) return false;
00423   
00424   // all edges are non-border edges.
00425   if ( is_border(h1)) return false;  // implies h2 and h3
00426   if ( is_border(h4)) return false;
00427   if ( is_border(h5)) return false;
00428   if ( is_border(h6)) return false;
00429 
00430   return true;
00431 }
00432 
00433 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00434 bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_open_triangle( edge_descriptor const& h1 )
00435 {
00436   bool rR = false ;
00437   
00438   edge_descriptor h2 = next_edge(h1,mSurface);
00439   edge_descriptor h3 = next_edge(h2,mSurface);
00440   
00441   // First check if it is a triangle 
00442   if ( next_edge(h3,mSurface) == h1 )
00443   {
00444     // Now check if it is open
00445     CGAL_ECMS_TRACE(4,"  p-q is a border edge... checking E" << h2->id() << " and E" << h3->id() ) ;
00446     
00447     rR = is_border(h2) && is_border(h3);  
00448   
00449     CGAL_SURF_SIMPL_TEST_assertion( rR == (  h1->is_border()
00450                                           && h1->next()->is_border()
00451                                           && h1->next()->next()->is_border()
00452                                           ) 
00453                                   ) ;
00454   }
00455   
00456   return rR ;
00457 }
00458 
00459 // Given triangles 'p0,p1,p2' and 'p0,p2,p3', both shared along edge 'v0-v2',
00460 // determine if they are geometrically valid: that is, the ratio of their
00461 // respective areas is no greater than a max value and the internal
00462 // dihedral angle formed by their supporting planes is no greater than
00463 // a given threshold
00464 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00465 bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::are_shared_triangles_valid( Point const& p0, Point const& p1, Point const& p2, Point const& p3 ) const 
00466 {
00467   bool rR = false ;
00468   
00469   Vector e01 = p1 - p0 ;
00470   Vector e02 = p2 - p0 ;
00471   Vector e03 = p3 - p0 ;
00472   
00473   Vector n012 = cross_product(e01,e02);
00474   Vector n023 = cross_product(e02,e03);
00475   
00476   FT l012 = n012 * n012 ;
00477   FT l023 = n023 * n023 ;
00478   
00479   FT larger  = (std::max)(l012,l023);
00480   FT smaller = (std::min)(l012,l023);
00481   
00482   const double cMaxAreaRatio = 1e8 ;
00483   
00484   CGAL_ECMS_TRACE(4,"    Testing validity of shared triangles:"
00485                   << "\n      p0=" << xyz_to_string(p0) << "\n      p1=" << xyz_to_string(p1) << "\n      p2=" << xyz_to_string(p2) << "\n      p3=" << xyz_to_string(p3)
00486                   << "\n      e01=" << xyz_to_string(e01) << "\n      e02=" << xyz_to_string(e02) << "\n      e03=" << xyz_to_string(e03)
00487                   << "\n      n012=" << xyz_to_string(n012) << "\n      n023=" << xyz_to_string(n023)
00488                   << "\n      l012=" << n_to_string(l012) << "\n      l023=" << n_to_string(l023)
00489                  );
00490   
00491   if ( larger < cMaxAreaRatio * smaller )
00492   {
00493     FT l0123 = n012 * n023 ;
00494     CGAL_ECMS_TRACE(4,"\n      l0123=" << n_to_string(l0123) );
00495     
00496     if ( CGAL_NTS is_positive(l0123) )
00497     {
00498       rR = true ;
00499     }
00500     else
00501     {
00502       CGAL_ECMS_TRACE(4,"\n      lhs: " << n_to_string(( l0123 * l0123 ) / ( l012 * l023 )) << " <= rhs: " << mcMaxDihedralAngleCos2 ) ;
00503       
00504       if ( ( l0123 * l0123 ) <= mcMaxDihedralAngleCos2 * ( l012 * l023 ) )
00505       {
00506         rR = true ;
00507       }
00508     }
00509   }
00510   
00511   return rR ;
00512 }
00513 
00514 // Returns the directed halfedge connecting v0 to v1, if exists.
00515 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00516 typename EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::edge_descriptor
00517 EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::find_connection ( const_vertex_descriptor const& v0, const_vertex_descriptor const& v1 ) const
00518 {
00519   out_edge_iterator eb, ee ; 
00520   for ( tie(eb,ee) = out_edges(v0,mSurface) ; eb != ee ; ++ eb )
00521   {
00522     edge_descriptor out = *eb ;
00523     if ( target(out,mSurface) == v1 )
00524       return out ;
00525   }
00526       
00527   return edge_descriptor() ;  
00528 }
00529 
00530 // Given the edge 'e' around the link for the collapsinge edge "v0-v1", finds the vertex that makes a triangle adjacent to 'e' but exterior to the link (i.e not containing v0 nor v1)
00531 // If 'e' is a mnull handle OR 'e' is a border edge, there is no such triangle and a null handle is returned.
00532 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00533 typename EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::vertex_descriptor
00534 EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::find_exterior_link_triangle_3rd_vertex ( const_edge_descriptor const& e, const_vertex_descriptor const& v0, const_vertex_descriptor const& v1 ) const
00535 {
00536   vertex_descriptor r ;
00537   
00538   if ( handle_assigned(e) )
00539   {
00540     vertex_descriptor ra = target(next_edge(e, mSurface), mSurface);
00541     vertex_descriptor rb = source(prev_edge(e, mSurface), mSurface);
00542     
00543     if ( ra == rb && ra != v0 && ra != v1 )
00544     {
00545       r = ra ;
00546     }
00547     else
00548     {
00549       ra = target(next_edge(opposite_edge(e,mSurface), mSurface), mSurface);
00550       rb = source(prev_edge(opposite_edge(e,mSurface), mSurface), mSurface);
00551       
00552       if ( ra == rb && ra != v0 && ra != v1 )
00553       {
00554         r = ra ;
00555       }
00556     }
00557   }
00558   
00559   return r ;
00560 }
00561 
00562 
00563 // A collase is geometrically valid if, in the resulting local mesh no two adjacent triangles form an internal dihedral angle
00564 // greater than a fixed threshold (i.e. triangles do not "fold" into each other)
00565 //
00566 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00567 bool EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Is_collapse_geometrically_valid( Profile const& aProfile, Placement_type k0)
00568 {
00569   bool rR = true ;
00570   
00571   CGAL_ECMS_TRACE(3,"Testing geometrical collapsabilty of v0-v1=E" << aProfile.v0_v1()->id() );
00572   if ( k0 )
00573   {
00574     //
00575     // Use the current link to extract all local triangles incident to 'vx' in the collapsed mesh (which at this point doesn't exist yet)
00576     //
00577     typedef typename Profile::vertex_descriptor_vector::const_iterator const_link_iterator ;
00578     const_link_iterator linkb = aProfile.link().begin();
00579     const_link_iterator linke = aProfile.link().end  ();
00580     const_link_iterator linkl = predecessor(linke) ;
00581     
00582     for ( const_link_iterator l = linkb ; l != linke && rR ; ++ l )
00583     {
00584       const_link_iterator pv = ( l == linkb ? linkl : predecessor(l) );
00585       const_link_iterator nx = ( l == linkl ? linkb : successor  (l) ) ;
00586       
00587       // k0,k1 and k3 are three consecutive vertices along the link.
00588       vertex_descriptor k1 = *pv ;
00589       vertex_descriptor k2 = * l ;
00590       vertex_descriptor k3 = *nx ;
00591       
00592       CGAL_ECMS_TRACE(4,"  Screening link vertices k1=V" << k1->id() << " k2=V" << k2->id() << " k3=V" << k3->id() ) ;
00593       
00594       edge_descriptor e12 = find_connection(k1,k2);
00595       edge_descriptor e23 = k3 != k1 ? find_connection(k2,k3) : edge_descriptor() ;
00596       
00597       // If 'k1-k2-k3' are connected there will be two adjacent triangles 'k0,k1,k2' and 'k0,k2,k3' after the collapse.
00598       if ( handle_assigned(e12) && handle_assigned(e23) )
00599       {
00600         CGAL_ECMS_TRACE(4,"    Link triangles shared" ) ;
00601         
00602         if ( !are_shared_triangles_valid( *k0, get_point(k1), get_point(k2), get_point(k3) ) )
00603         {
00604           CGAL_ECMS_TRACE(3,"    Triangles VX-V" << k1->id() << "-V" << k2->id() << " and VX-V" << k3->id() << " are not geometrically valid. Collapse rejected");
00605           rR = false ;
00606         }
00607       }
00608       
00609       if ( rR )
00610       {
00611         // Also check the triangles 'k0,k1,k2' and it's adjacent along e12: 'k4,k2,k1', if exist
00612         vertex_descriptor k4 = find_exterior_link_triangle_3rd_vertex(e12,aProfile.v0(),aProfile.v1());
00613           
00614         // There is indeed a triangle shared along e12
00615         if ( handle_assigned(k4) )
00616         {
00617           CGAL_ECMS_TRACE(4,"    Found exterior link triangle shared along E" << e12->id() << " with third vertex: V" << k4->id() ) ;
00618           
00619           if ( !are_shared_triangles_valid( get_point(k1), get_point(k4), get_point(k2), *k0 ) )
00620           {
00621             CGAL_ECMS_TRACE(3,"    Triangles V" << k1->id() << "-V" << k4->id() << " and V" << k2->id() << "-VX are not geometrically valid. Collapse rejected");
00622             rR = false ;
00623           }
00624         }
00625       }
00626       
00627       if ( rR )
00628       {
00629         // And finally, check the triangles 'k0,k2,k3' and it's adjacent e23: 'k5,k3,k2' if exist
00630         vertex_descriptor k5 = find_exterior_link_triangle_3rd_vertex(e23,aProfile.v0(),aProfile.v1());
00631         
00632         // There is indeed a triangle shared along e12
00633         if ( handle_assigned(k5) )
00634         {
00635           CGAL_ECMS_TRACE(4,"    Found exterior link triangle shared along E" << e23->id() << " with third vertex: V" << k5->id() ) ;
00636           
00637           if ( !are_shared_triangles_valid( get_point(k2), get_point(k5), get_point(k3), *k0 ) )
00638           {
00639             CGAL_ECMS_TRACE(3,"    Triangles V" << k2->id() << "-V" << k5->id() << " and V" << k3->id() << "-VX are not geometrically valid. Collapse rejected");
00640             rR = false ;
00641           }
00642         }
00643       }
00644     } 
00645     
00646   }
00647   
00648   return rR ;
00649 }
00650 
00651 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00652 void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Collapse( Profile const& aProfile, Placement_type aPlacement )
00653 {
00654 
00655   CGAL_ECMS_TRACE(1,"S" << mStep << ". Collapsig " << edge_to_string(aProfile.v0_v1()) ) ;
00656   
00657   vertex_descriptor rResult ;
00658     
00659   Visitor.OnCollapsing(aProfile,aPlacement);
00660 
00661   -- mCurrentEdgeCount ;
00662       
00663   CGAL_SURF_SIMPL_TEST_assertion_code( size_type lResultingVertexCount = mSurface.size_of_vertices() ;
00664                                        size_type lResultingEdgeCount   = mSurface.size_of_halfedges() / 2 ;
00665                                      ) ; 
00666 
00667   // If the top/bottom facets exists, they are removed and the edges v0vt and Q-B along with them.
00668   // In that case their corresponding pairs must be pop off the queue
00669   
00670   if ( aProfile.left_face_exists() )
00671   {
00672     edge_descriptor lV0VL = primary_edge(aProfile.vL_v0());
00673     
00674     CGAL_ECMS_TRACE(3,"V0VL E" << lV0VL->id()
00675                    << "(V" <<  lV0VL->vertex()->id() << "->V" << lV0VL->opposite()->vertex()->id() << ")"
00676                    ) ;
00677                    
00678     Edge_data& lData = get_data(lV0VL) ;
00679     if ( lData.is_in_PQ() )
00680     {
00681       CGAL_ECMS_TRACE(2,"Removing E" << lV0VL->id() << " from PQ" ) ;
00682       remove_from_PQ(lV0VL,lData) ;
00683     }
00684 
00685     -- mCurrentEdgeCount ;
00686     CGAL_SURF_SIMPL_TEST_assertion_code( -- lResultingEdgeCount ) ; 
00687   }
00688   
00689   if ( aProfile.right_face_exists() )
00690   {
00691     edge_descriptor lVRV1 = primary_edge(aProfile.vR_v1());
00692     
00693     CGAL_ECMS_TRACE(3,"V1VRE" << lVRV1->id()
00694                    << "(V" <<  lVRV1->vertex()->id() << "->V" << lVRV1->opposite()->vertex()->id() << ")"
00695                    ) ;
00696                    
00697     Edge_data& lData = get_data(lVRV1) ;
00698     if ( lData.is_in_PQ() )
00699     {
00700       CGAL_ECMS_TRACE(2,"Removing E" << lVRV1->id() << " from PQ") ;
00701       remove_from_PQ(lVRV1,lData) ;
00702     }
00703     -- mCurrentEdgeCount ;
00704     CGAL_SURF_SIMPL_TEST_assertion_code( -- lResultingEdgeCount ) ; 
00705   }
00706 
00707   CGAL_ECMS_TRACE(1,"Removing:\n  v0v1: E" << aProfile.v0_v1()->id() << "(V" << aProfile.v0()->id() << "->V" << aProfile.v1()->id() << ")" );
00708   
00709     
00710   // Perform the actuall collapse.
00711   // This is an external function.
00712   // It's REQUIRED to remove ONLY 1 vertex (P or Q) and edges PQ,PT and QB
00713   // (PT and QB are removed if they are not null).
00714   // All other edges must be kept.
00715   // All directed edges incident to vertex removed are relink to the vertex kept.
00716   rResult = halfedge_collapse(aProfile.v0_v1(),mSurface);
00717 
00718   CGAL_SURF_SIMPL_TEST_assertion_code( -- lResultingEdgeCount ) ; 
00719 
00720   CGAL_SURF_SIMPL_TEST_assertion_code( -- lResultingVertexCount ) ; 
00721 
00722   CGAL_SURF_SIMPL_TEST_assertion( lResultingEdgeCount * 2 == mSurface.size_of_halfedges() ) ;
00723 
00724   CGAL_SURF_SIMPL_TEST_assertion( lResultingVertexCount == mSurface.size_of_vertices() ) ;
00725 
00726   CGAL_SURF_SIMPL_TEST_assertion( mSurface.is_valid() && mSurface.is_pure_triangle() ) ;
00727   
00728   CGAL_ECMS_TRACE(1,"V" << rResult->id() << " kept." ) ;
00729                  
00730 #ifdef CGAL_SURFACE_SIMPLIFICATION_ENABLE_TRACE
00731   out_edge_iterator eb1, ee1 ;     
00732   for ( tie(eb1,ee1) = out_edges(rResult,mSurface) ; eb1 != ee1 ; ++ eb1 )  
00733     CGAL_ECMS_TRACE(2, edge_to_string(*eb1) ) ;
00734 #endif
00735 
00736   if ( aPlacement )  
00737   {
00738     CGAL_ECMS_TRACE(1,"New vertex point: " << xyz_to_string(*aPlacement) ) ;
00739     put(vertex_point,mSurface,rResult,*aPlacement) ;
00740   }  
00741 
00742   Visitor.OnCollapsed(aProfile,rResult);
00743 
00744   Update_neighbors(rResult) ;
00745   
00746   CGAL_ECMS_DEBUG_CODE ( ++mStep ; )
00747 }
00748 
00749 template<class M,class SP, class VIM,class EIM,class EBM, class CF,class PF,class V>
00750 void EdgeCollapse<M,SP,VIM,EIM,EBM,CF,PF,V>::Update_neighbors( vertex_descriptor const& aKeptV )
00751 {
00752   CGAL_ECMS_TRACE(3,"Updating cost of neighboring edges..." ) ;
00753 
00754   //
00755   // (A) Collect all edges to update its cost: all those around each vertex adjacent to the vertex kept
00756   //  
00757   
00758   typedef std::set<edge_descriptor,Compare_id> edges ;
00759   
00760   edges lToUpdate(Compare_id(this)) ;
00761   
00762   // (A.1) Loop around all vertices adjacent to the vertex kept
00763   in_edge_iterator eb1, ee1 ; 
00764   for ( tie(eb1,ee1) = in_edges(aKeptV,mSurface) ; eb1 != ee1 ; ++ eb1 )
00765   {
00766     edge_descriptor lEdge1 = *eb1 ;
00767     
00768     vertex_descriptor lAdj_k = source(lEdge1,mSurface);
00769     
00770     // (A.2) Loop around all edges incident on each adjacent vertex
00771     in_edge_iterator eb2, ee2 ; 
00772     for ( tie(eb2,ee2) = in_edges(lAdj_k,mSurface) ; eb2 != ee2 ; ++ eb2 )
00773     {
00774       edge_descriptor lEdge2 = primary_edge(*eb2) ;
00775       
00776       Edge_data& lData2 = get_data(lEdge2);
00777       CGAL_ECMS_TRACE(4,"Inedge around V" << lAdj_k->id() << edge_to_string(lEdge2) ) ;
00778     
00779       // Only those edges still in the PQ _and_ not already collected are updated.
00780       if ( lData2.is_in_PQ() && lToUpdate.find(lEdge2) == lToUpdate.end() )
00781         lToUpdate.insert(lEdge2) ;
00782     } 
00783   }  
00784   
00785   //
00786   // (B) Proceed to update the costs.
00787   //
00788   
00789   for ( typename edges::iterator it = lToUpdate.begin(), eit = lToUpdate.end() ; it != eit ; ++ it )
00790   {
00791     edge_descriptor lEdge = *it;
00792     
00793     Edge_data& lData = get_data(lEdge);
00794     
00795     Profile const& lProfile = create_profile(lEdge);
00796     
00797     lData.cost() = get_cost(lProfile) ;
00798     
00799     CGAL_ECMS_TRACE(3, edge_to_string(lEdge) << " updated in the PQ") ;
00800     
00801     update_in_PQ(lEdge,lData);
00802   }
00803     
00804 }
00805 
00806 
00807 } // namespace Surface_mesh_simplification
00808 
00809 CGAL_END_NAMESPACE
00810 
00811 #endif // CGAL_SURFACE_MESH_SIMPLIFICATION_DETAIL_EDGE_COLLAPSE_IMPL_H //
00812 // EOF //
00813  
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines