diff --git a/Surface_mesh_simplification/include/CGAL/Polyhedron_extended_BGL.h b/Surface_mesh_simplification/include/CGAL/Polyhedron_extended_BGL.h index 130c0208ac3..2f9e6aaaec0 100644 --- a/Surface_mesh_simplification/include/CGAL/Polyhedron_extended_BGL.h +++ b/Surface_mesh_simplification/include/CGAL/Polyhedron_extended_BGL.h @@ -31,6 +31,13 @@ CGAL_BEGIN_NAMESPACE +template +typename boost::graph_traits< Polyhedron_3 const>::edges_size_type +num_undirected_edges(const Polyhedron_3& p) +{ + return p.size_of_halfedges() / 2 ; +} + // // Const versions // diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Detail/Lindstrom_Turk_impl.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Detail/Lindstrom_Turk_impl.h index ad2a198bc38..4f02abbc7a3 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Detail/Lindstrom_Turk_impl.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/Detail/Lindstrom_Turk_impl.h @@ -323,8 +323,8 @@ void LindstromTurkImpl::Extract_triangles_and_link( Triangles& rTriangles, L if ( v2 != mQ ) { - if ( std::find(rLink.begin(),rLink.end(),v2) == rLink.end() ) - rLink.push_back(v2) ; + CGAL_expensive_assertion ( std::find(rLink.begin(),rLink.end(),v2) == rLink.end() ) ; + rLink.push_back(v2) ; } Extract_triangle(v0,v1,v2,e02,rTriangles); @@ -664,12 +664,14 @@ void LindstromTurkImpl::Constrians::Add_from_gradient ( Matrix const& H, Vec , A0.z()*A0.z() ); - Vector Q0 ; + Vector Q0; switch ( index_of_max_component(A02) ) { case 0: Q0 = Vector(- A0.z()/A0.x(),0 ,1 ); break; case 1: Q0 = Vector(0 ,- A0.z()/A0.y(),1 ); break; case 2: Q0 = Vector(1 ,0 ,- A0.x()/A0.z()); break; + + default : Q0 = NULL_VECTOR ; // This should never happen ; } Vector Q1 = cross_product(A0,Q0); diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/LindstromTurk_collapse_data.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/LindstromTurk_collapse_data.h index 91031c5c2da..bed3c616178 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/LindstromTurk_collapse_data.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Policies/LindstromTurk_collapse_data.h @@ -67,6 +67,17 @@ public: public : + LindstromTurk_collapse_data ( vertex_descriptor const& aP + , vertex_descriptor const& aQ + , bool aIsPFixed + , bool aIsQFixed + , edge_descriptor const& aEdge + , TSM& aSurface + ) + : + Base(aP,aQ,aIsPFixed,aIsQFixed,aEdge,aSurface) + {} + LindstromTurk_collapse_data ( vertex_descriptor const& aP , vertex_descriptor const& aQ , bool aIsPFixed diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/TSMS_common.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/TSMS_common.h index 84e84c4fe90..464feb8217b 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/TSMS_common.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/TSMS_common.h @@ -149,16 +149,6 @@ CGAL_END_NAMESPACE # define CGAL_TSMS_TRACE(l,m) #endif - - - -#ifdef CGAL_SURFACE_SIMPLIFICATION_ENABLE_AUDIT -# define CGAL_TSMS_AUDIT(p,q,e,c,v) CGAL_TSMS_audit(p,q,e,c,v) -#else -# define CGAL_TSMS_AUDIT(p,q,e,c,v) -#endif - - #undef CGAL_TSMS_ENABLE_TRACE #endif // CGAL_SURFACE_MESH_SIMPLIFICATION_TSMS_COMMON_H // diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse.h index 1f3890f0c94..828c406a3b8 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse.h @@ -46,6 +46,7 @@ template class VertexPairCollapse { @@ -56,6 +57,7 @@ public: typedef GetCost_ GetCost ; typedef GetNewVertexPoint_ GetNewVertexPoint ; typedef ShouldStop_ ShouldStop ; + typedef VisitorT_ VisitorT ; typedef VertexPairCollapse Self ; @@ -144,6 +146,16 @@ public: void reset_data( Collapse_data_ptr const& aData ) { mData = aData ; mCostStored = false ; } + void reset_data( vertex_descriptor const& new_p + , vertex_descriptor const& new_q + , bool is_new_p_fixed + , bool is_new_q_fixed + , edge_descriptor const& edge + ) + { + reset_data( Collapse_data_ptr( new Collapse_data(new_p,new_q,is_new_p_fixed,is_new_q_fixed,edge,surface()) ) ); + } + Collapse_data_ptr data () const { return mData ; } vertex_descriptor const& p () const { return mData->p() ; } vertex_descriptor const& q () const { return mData->q() ; } @@ -152,6 +164,8 @@ public: edge_descriptor const& edge () const { return mData->edge() ; } TSM& surface () const { return mData->surface() ; } + bool is_edge_fixed() const { return is_p_fixed() && is_q_fixed() ; } + bool is_edge() const { edge_descriptor null ; @@ -168,6 +182,7 @@ public: void set_PQ_handle( pq_handle h ) { mPQHandle = h ; } void reset_PQ_handle() { mPQHandle = null_PQ_handle() ; } + friend bool operator< ( shared_ptr const& a, shared_ptr const& b ) { @@ -195,10 +210,11 @@ public: if ( vp.is_p_in_surface() ) out << "V" << vp.p()->ID << " (" << vp.p()->point().x() << "," << vp.p()->point().y() << "," << vp.p()->point().z() << ")" ; else out << "##p() has been erased## " ; - out << "->" ; + out << ( vp.is_p_fixed() ? "[FIXED] " : "" ) << "->" ; if ( vp.is_q_in_surface() ) out << " V" << vp.q()->ID << "(" << vp.q()->point().x() << "," << vp.q()->point().y() << "," << vp.q()->point().z() << ")" ; else out << "##q() has been erased## " ; + out << ( vp.is_q_fixed() ? "[FIXED] " : "" ); if ( vp.is_edge_in_surface() ) out << " E" << vp.edge()->ID ; else out << "##e() has been erased## " ; @@ -259,6 +275,7 @@ public: , GetCost const& aGetCost , GetNewVertexPoint const& aGetVertexPoint , ShouldStop const& aShouldStop + , VisitorT* aVisitor , bool aIncludeNonEdgePairs ) ; @@ -270,7 +287,7 @@ private: void Loop(); bool Is_collapsable( vertex_descriptor const& p, vertex_descriptor const& q, edge_descriptor const& p_q ) ; void Collapse( vertex_pair_ptr const& aPair ) ; - void Update_neighbors( vertex_descriptor const& v ) ; + void Update_neighbors( vertex_pair_ptr const& aCollapsingPair ) ; vertex_pair_ptr get_pair ( edge_descriptor const& e ) { @@ -337,25 +354,6 @@ private: return get(is_vertex_fixed,mSurface,v) ; } - void GetVerticesIsFixedFlags( vertex_descriptor const& p - , vertex_descriptor const& q - , edge_descriptor const& p_q - , bool& aIsPFixed - , bool& aIsQFixed - ) - { - if (Is_collapsable(p,q,p_q)) - { - aIsPFixed = is_vertex_fixed(p); - aIsQFixed = is_vertex_fixed(q); - } - else - { - aIsPFixed = true ; - aIsQFixed = true ; - } - } - private: TSM& mSurface ; @@ -365,6 +363,7 @@ private: GetCost const& Get_cost ; GetNewVertexPoint const& Get_new_vertex_point ; ShouldStop const& Should_stop ; + VisitorT* Visitor ; bool mIncludeNonEdgePairs; diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse_impl.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse_impl.h index 2f3a7b80b58..86be62521d8 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse_impl.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification/Vertex_pair_collapse_impl.h @@ -23,15 +23,16 @@ CGAL_BEGIN_NAMESPACE namespace Triangulated_surface_mesh { namespace Simplification { -template -VertexPairCollapse::VertexPairCollapse( TSM& aSurface - , GetCollapseData const& aGet_collapse_data - , ParamsToGetCollapseData const* aParamsToGetCollapseData - , GetCost const& aGet_cost - , GetNewVertexPoint const& aGet_new_vertex_point - , ShouldStop const& aShould_stop - , bool aIncludeNonEdgePairs - ) +template +VertexPairCollapse::VertexPairCollapse( TSM& aSurface + , GetCollapseData const& aGet_collapse_data + , ParamsToGetCollapseData const* aParamsToGetCollapseData + , GetCost const& aGet_cost + , GetNewVertexPoint const& aGet_new_vertex_point + , ShouldStop const& aShould_stop + , VisitorT* aVisitor + , bool aIncludeNonEdgePairs + ) : mSurface (aSurface) ,mParamsToGetCollapseData(aParamsToGetCollapseData) @@ -40,15 +41,19 @@ VertexPairCollapse::VertexPairCollapse( TSM& ,Get_cost (aGet_cost) ,Get_new_vertex_point(aGet_new_vertex_point) ,Should_stop (aShould_stop) + ,Visitor (aVisitor) ,mIncludeNonEdgePairs(aIncludeNonEdgePairs) { - CGAL_TSMS_TRACE(0,"VertexPairCollapse of TSM with " << num_edges(aSurface)/2 << " edges" ); + CGAL_TSMS_TRACE(0,"VertexPairCollapse of TSM with " << num_undirected_edges(aSurface) << " edges" ); } -template -int VertexPairCollapse::run() +template +int VertexPairCollapse::run() { + if ( Visitor ) + Visitor->OnStarted(mSurface); + if ( num_vertices(mSurface) > 4 ) { // First collect all candidate edges in a PQ @@ -61,20 +66,35 @@ int VertexPairCollapse::run() } else { - mInitialPairCount = mCurrentPairCount = num_edges(mSurface) / 2 ; + if ( Visitor ) + Visitor->OnThetrahedronReached(mSurface); + + mInitialPairCount = mCurrentPairCount = num_undirected_edges(mSurface) ; CGAL_TSMS_TRACE(0,"A thetrahedron cannot be simplified any further."); } - - return (int)(mInitialPairCount - mCurrentPairCount) ; + + int r = (int)(mInitialPairCount - mCurrentPairCount) ; + + if ( Visitor ) + Visitor->OnFinished(mSurface); + + return r ; } -template -void VertexPairCollapse::Collect() +template +void VertexPairCollapse::Collect() { CGAL_TSMS_TRACE(0,"Collecting vertex-pairs..."); - // Loop over all the edges in the surface putting the accepted vertex-pairs in the PQ + // + // NOTE: This algorithm requires ALL directed edges to be mapped the corresponding vertex-pair. + // This must be true whether the pair is in the PQ or not + // + + // + // Loop over all the edges in the surface putting the corresponding vertex-pairs in the PQ + // Equal_3 equal_points = Kernel().equal_3_object(); @@ -88,34 +108,36 @@ void VertexPairCollapse::Collect() vertex_descriptor s = source(edge,mSurface); vertex_descriptor t = target(edge,mSurface); - Point_3 sp = get_point(s) ; - Point_3 tp = get_point(t) ; - - if ( ! equal_points(sp,tp) ) - { - bool is_s_fixed, is_t_fixed ; - GetVerticesIsFixedFlags(s,t,edge,is_s_fixed,is_t_fixed); + bool is_s_fixed = is_vertex_fixed(s) ; + bool is_t_fixed = is_vertex_fixed(t) ; - // The collapse always keeps the vertex 't' and removes vertex 's'. - // If the 's' is fixed (but not t) then swap the pair to keep (the original) 's' correctly fixed. - if ( is_s_fixed && !is_t_fixed ) - { - using std::swap ; - swap(s,t); - swap(is_s_fixed,is_t_fixed); - edge = opposite_edge(edge,mSurface); - } + // All directed edges must be mapped to vertex-pair whether they are put in the PQ or not. + // Thus, degenerate edges are marked as fixed. + if ( s == t || equal_points( get_point(s), get_point(t)) ) + is_s_fixed = is_t_fixed = true ; - Collapse_data_ptr lData = Get_collapse_data(s,t,is_s_fixed,is_t_fixed,edge,mSurface,mParamsToGetCollapseData) ; - vertex_pair_ptr lPair( new vertex_pair(lID++,lData,Get_cost) ) ; - set_pair(edge,lPair); - insert_in_PQ(lPair); - CGAL_TSMS_TRACE(3, (lPair->cost(),*lPair) << " accepted." ); - CGAL_TSMS_AUDIT(s,t,edge,lPair->cost(),Get_new_vertex_point(*lPair->data())); + // The collapse always keeps the vertex 't' and removes vertex 's'. + // If the 's' is fixed (but not t) then swap the pair to keep (the original) 's' correctly fixed. + if ( is_s_fixed && !is_t_fixed ) + { + using std::swap ; + swap(s,t); + swap(is_s_fixed,is_t_fixed); + edge = opposite_edge(edge,mSurface); } + + Collapse_data_ptr lData = Get_collapse_data(s,t,is_s_fixed,is_t_fixed,edge,mSurface,mParamsToGetCollapseData) ; + vertex_pair_ptr lPair( new vertex_pair(lID++,lData,Get_cost) ) ; + set_pair(edge,lPair); + if ( !lPair->is_edge_fixed() ) + insert_in_PQ(lPair); + + if ( Visitor ) + Visitor->OnCollected(mSurface,s,t,is_s_fixed,is_t_fixed,edge,lPair->cost(),Get_new_vertex_point(*lPair->data())); + + CGAL_TSMS_TRACE(3, (lPair->cost(),*lPair) ); } - - + // TODO: Collect non-edge pairs if requested mInitialPairCount = mCurrentPairCount = lID; @@ -123,8 +145,8 @@ void VertexPairCollapse::Collect() CGAL_TSMS_TRACE(0,"Initial pair count: " << mInitialPairCount ) ; } -template -void VertexPairCollapse::Loop() +template +void VertexPairCollapse::Loop() { CGAL_TSMS_TRACE(0,"Removing pairs...") ; @@ -135,16 +157,22 @@ void VertexPairCollapse::Loop() { CGAL_TSMS_TRACE(3,"Poped " << *lPair ) ; + bool lIsCollapsable = false ; + if ( lPair->cost() != none ) { if ( num_vertices(mSurface) <= 4 ) { + if ( Visitor ) + Visitor->OnThetrahedronReached(mSurface); CGAL_TSMS_TRACE(0,"Thetrahedron reached."); break ; } if ( Should_stop(*lPair->cost(),*lPair->data(),mInitialPairCount,mCurrentPairCount) ) { + if ( Visitor ) + Visitor->OnStopConditionReached(mSurface); CGAL_TSMS_TRACE(0,"Stop condition reached with InitialCount=" << mInitialPairCount << " CurrentPairCount=" << mCurrentPairCount << " CurrentPair: " << *lPair @@ -152,16 +180,41 @@ void VertexPairCollapse::Loop() break ; } - Collapse(lPair); + lIsCollapsable = Is_collapsable(lPair->p(),lPair->q(),lPair->edge()) ; + + if ( !lPair->is_edge_fixed() && lIsCollapsable ) + { + Collapse(lPair); + } + else + { + CGAL_TSMS_TRACE(1,*lPair << " NOT Collapsable" ); + } } + else + { + CGAL_TSMS_TRACE(1,*lPair << " uncomputable cost." ); + } + + if ( Visitor ) + Visitor->OnProcessed(mSurface + ,lPair->p() + ,lPair->q() + ,lPair->is_p_fixed() + ,lPair->is_q_fixed() + ,lPair->edge() + ,lPair->cost() + ,Get_new_vertex_point(*lPair->data()) + ,lIsCollapsable + ); } } // Some edges are NOT collapsable: doing so would break the topological consistency of the mesh. // This function returns true if a edge 'p->q' can be collapsed. // -template -bool VertexPairCollapse::Is_collapsable( vertex_descriptor const& p, vertex_descriptor const& q, edge_descriptor const& p_q ) +template +bool VertexPairCollapse::Is_collapsable( vertex_descriptor const& p, vertex_descriptor const& q, edge_descriptor const& p_q ) { bool rR = true ; @@ -172,6 +225,16 @@ bool VertexPairCollapse::Is_collapsable( vertex_descriptor const& p, edge_descriptor q_t = next_edge_cw (q_p,mSurface); edge_descriptor q_b = next_edge_ccw(q_p,mSurface); + CGAL_TSMS_TRACE(5, "Testing link condition:" + << "\np_q: V" << source(p_q,mSurface)->id() << "->V" << target(p_q,mSurface)->id() + << "\nq_p: V" << source(q_p,mSurface)->id() << "->V" << target(q_p,mSurface)->id() + << "\np_t: V" << source(p_t,mSurface)->id() << "->V" << target(p_t,mSurface)->id() + << "\np_b: V" << source(p_b,mSurface)->id() << "->V" << target(p_b,mSurface)->id() + << "\nq_t: V" << source(q_t,mSurface)->id() << "->V" << target(q_t,mSurface)->id() + << "\nq_n: V" << source(q_b,mSurface)->id() << "->V" << target(q_b,mSurface)->id() + ); + + // degree(p) and degree(q) >= 3 if ( target(p_t,mSurface) != q && target(p_b,mSurface) != q @@ -196,6 +259,12 @@ bool VertexPairCollapse::Is_collapsable( vertex_descriptor const& p, q_tn = next_edge_cw (q_tn,mSurface); q_bn = next_edge_ccw(q_bn,mSurface); + CGAL_TSMS_TRACE(5, "\n p_tn: V" << source(p_tn,mSurface)->id() << "->V" << target(p_tn,mSurface)->id() + << "\n q_tn: V" << source(q_tn,mSurface)->id() << "->V" << target(q_tn,mSurface)->id() + << "\n p_bn: V" << source(p_bn,mSurface)->id() << "->V" << target(p_bn,mSurface)->id() + << "\n q_bn: V" << source(q_bn,mSurface)->id() << "->V" << target(q_bn,mSurface)->id() + ); + if ( target(p_tn,mSurface) == target(q_tn,mSurface) || target(p_bn,mSurface) == target(q_bn,mSurface) ) @@ -206,15 +275,23 @@ bool VertexPairCollapse::Is_collapsable( vertex_descriptor const& p, } while ( p_tn != p_bn && q_tn != q_bn ) ; } - else rR = false ; + else + { + CGAL_TSMS_TRACE(5, "t and b vertices not shared by p and q"); + rR = false ; + } } - else rR = false ; + else + { + CGAL_TSMS_TRACE(5, "degree(p) or degree(q) < 3"); + rR = false ; + } return rR ; } -template -void VertexPairCollapse::Collapse( vertex_pair_ptr const& aPair ) +template +void VertexPairCollapse::Collapse( vertex_pair_ptr const& aPair ) { CGAL_TSMS_TRACE(1,"Collapsig " << *aPair ) ; @@ -223,7 +300,7 @@ void VertexPairCollapse::Collapse( vertex_pair_ptr const& aPair ) CGAL_assertion( lP != lQ ); - // This external function is allowed to return an absent point if there is no way to place the vertex + // The external function Get_new_vertex_point() is allowed to return an absent point if there is no way to place the vertex // satisfying its constrians. In that case the vertex-pair is simply not removed. Optional_vertex_point_type lNewVertexPoint = Get_new_vertex_point(*aPair->data()); if ( lNewVertexPoint ) @@ -242,74 +319,24 @@ void VertexPairCollapse::Collapse( vertex_pair_ptr const& aPair ) edge_descriptor lEdgeQB = next_edge_ccw(lEdgeQP,mSurface); CGAL_TSMS_TRACE(3,"EdgePQ E" << lEdgePQ->ID << " Opposite EdgePQ E" << lEdgePQ->opposite()->ID - << " V" << lEdgePQ->opposite()->vertex()->ID << "->V" << lEdgePQ->vertex()->ID ) ; + << " V" << lEdgePQ->opposite()->vertex()->ID << "->V" << lEdgePQ->vertex()->ID + ) ; CGAL_TSMS_TRACE(3,"EdgePT E" << lEdgePT->ID << " Opposite EdgePT E" << lEdgePT->opposite()->ID - << " V" << lEdgePT->opposite()->vertex()->ID << "->V" << lEdgePT->vertex()->ID ) ; + << " V" << lEdgePT->opposite()->vertex()->ID << "->V" << lEdgePT->vertex()->ID + ) ; - vertex_pair_vector lUpdated ; - - // Since vertex P will be removed during the collapse, all cached pairs linked to 'P' (either from or to) - // are updated to link to 'Q' instead. - out_edge_iterator eb, ee ; - for ( boost::tie(eb,ee) = boost::out_edges(lP,mSurface) ; eb != ee ; ++ eb ) - { - edge_descriptor outedge = *eb ; - CGAL_TSMS_TRACE(4,"Outedge around V" << lP->ID << " E" << outedge->ID << " Opposite E" << outedge->opposite()->ID - << " V" << outedge->opposite()->vertex()->ID << "->V" << outedge->vertex()->ID ) ; - - if ( outedge != lEdgePQ && outedge != lEdgePT ) - { - vertex_pair_ptr lPair = get_pair(outedge); - CGAL_TSMS_TRACE(4,"Updating vertex P in " << *lPair) ; - - CGAL_assertion( lPair->p() == lP || lPair->q() == lP ); - - bool is_v0_fixed, is_v1_fixed ; - vertex_descriptor v0, v1 ; - edge_descriptor edge ; - if ( lPair->p() == lP ) - { - v0 = lQ ; - v1 = lPair->q() ; - is_v0_fixed = aPair->is_q_fixed() ; - is_v1_fixed = lPair->is_q_fixed() ; - edge = outedge ; - } - else - { - v0 = lPair->p() ; - v1 = lQ ; - is_v0_fixed = lPair->is_p_fixed() ; - is_v1_fixed = aPair->is_q_fixed() ; - edge = opposite_edge(outedge,mSurface); - } - - if ( !Is_collapsable(v0,v1,edge) ) - is_v0_fixed = is_v1_fixed = true ; - - Collapse_data_ptr lNewData = Get_collapse_data(v0,v1,is_v0_fixed,is_v1_fixed,edge,mSurface,mParamsToGetCollapseData); - lPair->reset_data(lNewData); - lPair->mark() = 1 ; - lUpdated.push_back(lPair); - - CGAL_TSMS_TRACE(4,"...after update: " << *lPair ) ; - } - } - - // The collapse will remove the following edges from the surface so the corresponding pairs won't be valid anymore. - - vertex_pair_ptr lPairPT = get_pair(lEdgePT) ; + // The collapse will also remove QB so it must be pop off the PQ as well vertex_pair_ptr lPairQB = get_pair(lEdgeQB) ; + vertex_pair_ptr lPairPT = get_pair(lEdgePT) ; if ( lPairPT->is_in_PQ() ) { - CGAL_TSMS_TRACE(2,"Removing from PQ VP" << lPairPT->id() ) ; + CGAL_TSMS_TRACE(2,"Removing VP" << lPairPT->id() << " from PQ" ) ; remove_from_PQ(lPairPT) ; } - if ( lPairQB->is_in_PQ() ) { - CGAL_TSMS_TRACE(2,"Removing from PQ VP" << lPairQB->id() ) ; + CGAL_TSMS_TRACE(2,"Removing VP" << lPairQB->id() << " from PQ") ; remove_from_PQ(lPairQB) ; } @@ -319,19 +346,23 @@ void VertexPairCollapse::Collapse( vertex_pair_ptr const& aPair ) << " E" << lEdgeQB->ID ); - + if ( Visitor ) + Visitor->OnCollapsed(mSurface,lP,lEdgePQ,lEdgePT,lEdgeQB); + + // + // Perform the actuall collapse. + // This is an external function. + // It's REQUIRED to remove ONLY vertex P and edges PQ,PT and QB, keeping all other edges and + // to relink all directed edges incident to P with Q. + // The algorithm is based on the stability of the remaining edges. Collapse_triangulation_edge(lEdgePQ,lEdgePT,lEdgeQB,mSurface); // Reset the point of placement of Q (the vertex that "replaces" the collapsed edge) vertex_point_t vertex_point ; put(vertex_point,mSurface,lQ,*lNewVertexPoint) ; - // Updates the cost of all pairs in the PQ - Update_neighbors(lQ); + Update_neighbors(aPair); - for ( vertex_pair_vector_iterator it = lUpdated.begin(), eit = lUpdated.end() ; it != eit ; ++ it ) - (*it)->mark() = 0 ; - mCurrentPairCount -= 3 ; } else @@ -340,56 +371,70 @@ void VertexPairCollapse::Collapse( vertex_pair_ptr const& aPair ) } } - -template -void VertexPairCollapse::Update_neighbors( vertex_descriptor const& v ) +template +void VertexPairCollapse::Update_neighbors( vertex_pair_ptr const& aCollapsingPair ) { CGAL_TSMS_TRACE(3,"Updating cost of neighboring edges..." ) ; // - // (A) Collect all pairs to update its cost: all those around each vertex adjacent to v + // (A) Collect all pairs to update its cost: all those around each vertex adjacent to q // vertex_pair_vector lToUpdate ; - // (A.1) Loop around all vertices adjacent to v + // (A.1) Loop around all vertices adjacent to q in_edge_iterator eb1, ee1 ; - for ( tie(eb1,ee1) = in_edges(v,mSurface) ; eb1 != ee1 ; ++ eb1 ) + for ( tie(eb1,ee1) = in_edges(aCollapsingPair->q(),mSurface) ; eb1 != ee1 ; ++ eb1 ) { edge_descriptor edge1 = *eb1 ; - vertex_pair_ptr lPair1 = get_pair(edge1) ; - - CGAL_TSMS_TRACE(4,"Inedge around V" << v->ID << " E" << edge1->ID << " Opposite E" << edge1->opposite()->ID - << " V" << edge1->opposite()->vertex()->ID << "->V" << edge1->vertex()->ID - << "\n" << *lPair1 - ) ; - - - vertex_descriptor adj_v = source(edge1,mSurface); + vertex_descriptor adj_q = source(edge1,mSurface); // (A.2) Loop around all edges incident on each adjacent vertex in_edge_iterator eb2, ee2 ; - for ( tie(eb2,ee2) = in_edges(adj_v,mSurface) ; eb2 != ee2 ; ++ eb2 ) + for ( tie(eb2,ee2) = in_edges(adj_q,mSurface) ; eb2 != ee2 ; ++ eb2 ) { edge_descriptor edge2 = *eb2 ; - vertex_pair_ptr lPair2 = get_pair(edge2); + vertex_pair_ptr lPair = get_pair(edge2); - CGAL_TSMS_TRACE(4,"Inedge around V" << adj_v->ID << " E" << edge2->ID << " Opposite E" << edge2->opposite()->ID + if ( lPair->p() == aCollapsingPair->p() ) + { + CGAL_TSMS_TRACE(4,"Replacing lPair->p() with Q" ) ; + + lPair->reset_data(aCollapsingPair->q() + ,lPair ->q() + ,aCollapsingPair->is_q_fixed() + ,lPair ->is_q_fixed() + ,source(edge2,mSurface) == aCollapsingPair->q() ? edge2 : opposite_edge(edge2,mSurface) + ); + } + else if ( lPair->q() == aCollapsingPair->p() ) + { + CGAL_TSMS_TRACE(4,"Replacing lPair->q() with Q" ) ; + + lPair->reset_data(lPair ->p() + ,aCollapsingPair->q() + ,lPair ->is_p_fixed() + ,aCollapsingPair->is_q_fixed() + ,target(edge2,mSurface) == aCollapsingPair->q() ? edge2 : opposite_edge(edge2,mSurface) + ); + } + + CGAL_TSMS_TRACE(4,"Inedge around V" << adj_q->ID << " E" << edge2->ID << " Opposite E" << edge2->opposite()->ID << " V" << edge2->opposite()->vertex()->ID << "->V" << edge2->vertex()->ID - << "\n" << *lPair2 + << "\nPair:" << *lPair ) ; // Only those pairs still in the PQ are update. // The mark is used because in the way we loop here the same pair is found many times. - if ( lPair2->is_in_PQ() && lPair2->mark() == 0 ) + if ( lPair->is_in_PQ() && lPair->mark() == 0 ) { CGAL_TSMS_TRACE(4,"Pair registered for updating.") ; - lPair2->mark() = 1 ; - lToUpdate.push_back(lPair2); + lPair->mark() = 1 ; + lToUpdate.push_back(lPair); } } } @@ -421,6 +466,7 @@ void VertexPairCollapse::Update_neighbors( vertex_descriptor const& v } + } } // namespace Triangulated_surface_mesh::Simplification CGAL_END_NAMESPACE diff --git a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification_vertex_pair_collapse.h b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification_vertex_pair_collapse.h index f9638ee01a7..92d596db3f8 100644 --- a/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification_vertex_pair_collapse.h +++ b/Surface_mesh_simplification/include/CGAL/Surface_mesh_simplification_vertex_pair_collapse.h @@ -57,25 +57,27 @@ namespace Triangulated_surface_mesh { namespace Simplification // This global function returns the number of vertex-pairs removed or -1 if there was an error // (like the surface not being a valid triangulated surface mesh) // -template +template int vertex_pair_collapse ( TSM& aSurface , GetCollapseData const& aGet_collapse_data , ParamsToGetCollapseData const* aParamsToGetCollapseData // Can be NULL , GetCost const& aGet_cost , GetNewVertexPoint const& aGet_new_vertex_point , ShouldStop const& aShould_stop + , Visitor* aVisitor = 0 , bool aIncludeNonEdgePairs = false ) { if ( is_valid_triangulated_surface_mesh(aSurface) ) { - typedef VertexPairCollapse Algorithm ; + typedef VertexPairCollapse Algorithm ; Algorithm algorithm(aSurface ,aGet_collapse_data ,aParamsToGetCollapseData ,aGet_cost ,aGet_new_vertex_point ,aShould_stop + ,aVisitor ,aIncludeNonEdgePairs ) ; return algorithm.run(); diff --git a/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.cpp b/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.cpp index ba4f3244ded..4f0cc0ee2c8 100644 --- a/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.cpp +++ b/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.cpp @@ -24,9 +24,10 @@ #include #include -#define CGAL_CHECK_EXPENSIVE +//#define CGAL_CHECK_EXPENSIVE -#include +#include +#include #include #include #include @@ -35,8 +36,10 @@ #include //#define CGAL_SURFACE_SIMPLIFICATION_ENABLE_LT_TRACE 4 -//#define CGAL_SURFACE_SIMPLIFICATION_ENABLE_TRACE 3 -#define CGAL_SURFACE_SIMPLIFICATION_ENABLE_AUDIT +//#define CGAL_SURFACE_SIMPLIFICATION_ENABLE_TRACE 1 + +#define STATS +//#define AUDIT void Surface_simplification_external_trace( std::string s ) { @@ -59,7 +62,6 @@ using namespace std ; using namespace boost ; using namespace CGAL ; -//typedef Simple_homogeneous Kernel; typedef Simple_cartesian Kernel; typedef Kernel::Vector_3 Vector; typedef Kernel::Point_3 Point; @@ -143,6 +145,9 @@ struct External_polyhedron_get_is_vertex_fixed CGAL_END_NAMESPACE + +#ifdef AUDIT + double sCostMatchThreshold = 1e-2 ; double sVertexMatchThreshold = 1e-2 ; @@ -273,12 +278,12 @@ string to_string( optional const& p ) else return "NONE" ; } -void CGAL_TSMS_audit( Vertex_handle const& p - , Vertex_handle const& q - , Halfedge_handle const& e - , optional const& cost - , optional const& newv - ) +void register_collected_edge( Vertex_handle const& p + , Vertex_handle const& q + , Halfedge_handle const& e + , optional const& cost + , optional const& newv + ) { Audit_report_ptr lReport( new Audit_report(e->ID) ) ; @@ -312,6 +317,101 @@ void CGAL_TSMS_audit( Vertex_handle const& p sAuditReport.insert( make_pair(e->ID,lReport) ) ; } +#endif + +#ifdef STATS +int sInitial ; +int sCollected ; +int sProcessed ; +int sCollapsed ; +int sNonCollapsable ; +int sCostUncomputable ; +int sFixed ; +int sRemoved ; +#endif + + +struct Visitor +{ + void OnStarted( Polyhedron& aSurface ) + { +#ifdef STATS + sInitial = aSurface.size_of_halfedges() / 2 ; +#endif + } + + void OnFinished ( Polyhedron& aSurface ) + { +#ifdef STATS + printf("\n"); +#endif + } + + void OnThetrahedronReached ( Polyhedron& aSurface ) {} + void OnStopConditionReached( Polyhedron& aSurface ) {} + + void OnCollected( Polyhedron& aSurface + , Vertex_handle const& aP + , Vertex_handle const& aQ + , bool aIsPFixed + , bool aIsQFixed + , Halfedge_handle const& aEdge + , optional aCost + , optional aNewVertexPoint + ) + { +#ifdef AUDIT + register_collected_edge(aP,aQ,aEdge,aCost,aNewVertexPoint); +#endif +#ifdef STATS + ++sCollected ; + printf("\rEdges collected %d",sCollected); +#endif + } + + void OnProcessed( Polyhedron& aSurface + , Vertex_handle const& aP + , Vertex_handle const& aQ + , bool aIsPFixed + , bool aIsQFixed + , Halfedge_handle const& aEdge + , optional aCost + , optional aNewVertexPoint + , bool aIsCollapsable + ) + { +#ifdef STATS + if ( sProcessed == 0 ) + printf("\n"); + ++ sProcessed ; + if ( aIsPFixed && aIsQFixed ) + ++ sFixed ; + else if ( !aIsCollapsable ) + { + if ( !aCost ) + ++ sCostUncomputable ; + else ++ sNonCollapsable ; + } + else + ++ sCollapsed; +#endif + + } + + void OnCollapsed( Polyhedron& aSurface + , Vertex_handle const& aP + , Halfedge_handle const& aPQ + , Halfedge_handle const& aPT + , Halfedge_handle const& aQB + ) + { +#ifdef STATS + sRemoved += 3 ; + printf("\r%d%%",((int)(100.0*((double)sRemoved/(double)sInitial)))); +#endif + } + +} ; // This is here only to allow a breakpoint to be placed so I can trace back the problem. void error_handler ( char const* what, char const* expr, char const* file, int line, char const* msg ) @@ -331,7 +431,7 @@ char const* matched_alpha ( bool matched ) return matched ? "matched" : "UNMATCHED" ; } -bool Test ( string name ) +bool Test ( int aStop, string name ) { bool rSucceeded = false ; @@ -339,14 +439,16 @@ bool Test ( string name ) string audit_name = name+string(".audit"); string result_name = name+string(".out.off"); + cout << "Testing simplification of surface " << off_name << endl ; + ifstream off_is(off_name.c_str()); if ( off_is ) { Polyhedron lP; - off_is >> lP ; + scan_OFF(off_is,lP,true); - cout << "Testing Lindstrom Turk simplification of surace with " << (lP.size_of_halfedges()/2) << " edges..." << endl ; + cout << (lP.size_of_halfedges()/2) << " edges..." << endl ; cout << setprecision(19) ; @@ -362,33 +464,52 @@ bool Test ( string name ) for ( Polyhedron::Facet_iterator fi = lP.facets_begin(); fi != lP.facets_end() ; ++ fi ) fi->ID = lFacetID ++ ; - sAuditData.clear(); - //ParseAudit(audit_name); - +#ifdef AUDIT + sAuditData .clear(); + sAuditReport.clear(); + ParseAudit(audit_name); cout << "Audit data loaded." << endl ; +#endif typedef LindstromTurk_collapse_data Collapse_data ; Construct_LindstromTurk_collapse_data Construct_collapse_data ; LindstromTurk_cost Get_cost ; LindstromTurk_vertex_placement Get_vertex_point ; - Count_stop_condition Should_stop(0); + Count_stop_condition Should_stop(aStop); Collapse_data::Params lParams; - sAuditReport.clear(); - int r = vertex_pair_collapse(lP,Construct_collapse_data,&lParams,Get_cost,Get_vertex_point,Should_stop); + Visitor lVisitor ; + + Real_timer t ; t.start(); + int r = vertex_pair_collapse(lP,Construct_collapse_data,&lParams,Get_cost,Get_vertex_point,Should_stop,&lVisitor); + t.stop(); ofstream off_out(result_name.c_str(),ios::trunc); off_out << lP ; - cout << "Finished...\n" + cout << "\nFinished...\n" + << "Ellapsed time: " << t.time() << " seconds.\n" << r << " edges removed.\n" - << lP.size_of_vertices() << " vertices.\n" - << (lP.size_of_halfedges()/2) << " edges.\n" - << lP.size_of_facets() << " triangles.\n" + << endl + << lP.size_of_vertices() << " final vertices.\n" + << (lP.size_of_halfedges()/2) << " final edges.\n" + << lP.size_of_facets() << " final triangles.\n" << ( lP.is_valid() ? " valid\n" : " INVALID!!\n" ) ; +#ifdef STATS + cout << "\n------------STATS--------------\n" + << sProcessed << " edges processed.\n" + << sCollapsed << " edges collapsed.\n" + << sNonCollapsable << " non-collapsable edges.\n" + << sCostUncomputable << " non-computable edges.\n" + << sFixed << " fixed edges.\n" + << sRemoved << " edges removed.\n" + << (sRemoved/3) << " vertices removed." ; +#endif + +#ifdef AUDIT unsigned lMatches = 0 ; cout << "Audit report:\n" ; @@ -417,6 +538,10 @@ bool Test ( string name ) } rSucceeded = ( lMatches == sAuditReport.size() ) ; +#else + rSucceeded = true ; +#endif + } else { @@ -431,26 +556,37 @@ int main( int argc, char** argv ) set_error_handler (error_handler); set_warning_handler(error_handler); - vector cases ; - - for ( int i = 1 ; i < argc ; ++i ) - cases.push_back( string(argv[i]) ) ; - - if ( cases.size() == 0 ) - cases.push_back( string("data/sample5") ) ; - - unsigned lOK = 0 ; - for ( vector::const_iterator it = cases.begin(); it != cases.end() ; ++ it ) + if ( argc > 3 ) { - if ( Test(*it) ) - ++ lOK ; - } + vector cases ; - cout << endl - << lOK << " cases succedded." << endl - << (cases.size() - lOK ) << " cases failed." << endl ; - - return lOK == cases.size() ? 0 : 1 ; + int lStop = atoi(argv[1]); + + string lFolder(argv[2]); + lFolder += "/" ; + + for ( int i = 3 ; i < argc ; ++i ) + cases.push_back( string(argv[i]) ) ; + + unsigned lOK = 0 ; + for ( vector::const_iterator it = cases.begin(); it != cases.end() ; ++ it ) + { + if ( Test( lStop, lFolder + *it) ) + ++ lOK ; + } + + cout << endl + << lOK << " cases succedded." << endl + << (cases.size() - lOK ) << " cases failed." << endl ; + + return lOK == cases.size() ? 0 : 1 ; + } + else + { + cout << "USAGE: LT_edge_collapse_test final_edge_count folder file0 file1 file2 ..." << endl ; + + return 1 ; + } } // EOF // diff --git a/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.kdevelop b/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.kdevelop index 14398b0e8c8..2e2f91b1fd6 100644 --- a/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.kdevelop +++ b/Surface_mesh_simplification/test/Surface_mesh_simplification/LT_edge_collapse_test.kdevelop @@ -9,14 +9,14 @@ . false - + LT_edge_collapse_test - executable + build / - data/sample5 + data/elk false true @@ -26,8 +26,8 @@ 1 0 false - - + + CGAL_MAKEFILE=/home/fcacciola/Programming/CGAL/make/makefile_i686_Linux-2.6_g++-4.0.2 DEBUGGING=yes default @@ -36,17 +36,17 @@ make - + - - - - - - + + data/hedra + + + + true false false @@ -126,7 +126,7 @@ 250 - + set m_,_ theValue @@ -152,8 +152,8 @@ VisualBoyAdvance - - + + false false -f0 diff --git a/Surface_mesh_simplification/test/Surface_mesh_simplification/makefile b/Surface_mesh_simplification/test/Surface_mesh_simplification/makefile index b1a8509fbe8..d457cdc90d3 100644 --- a/Surface_mesh_simplification/test/Surface_mesh_simplification/makefile +++ b/Surface_mesh_simplification/test/Surface_mesh_simplification/makefile @@ -16,8 +16,7 @@ include $(CGAL_MAKEFILE) CXXFLAGS = -I../../include/\ $(TESTSUITE_CXXFLAGS) \ $(EXTRA_FLAGS) \ - $(CGAL_CXXFLAGS) \ - $(DEBUG_OPT) + $(CGAL_CXXFLAGS) #---------------------------------------------------------------------# # linker flags