BWAPI
|
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