/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004-2016 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef VCG_HEDGE_TOPOLOGY #define VCG_HEDGE_TOPOLOGY #include using namespace std; using namespace vcg::hedge; using namespace vcg::tri; namespace vcg { namespace tri { /*! * \brief Class containing functions to modify the topology of a halfedge based mesh * */ template class HalfEdgeTopology { public: typedef typename MeshType::VertexPointer VertexPointer; typedef typename MeshType::EdgePointer EdgePointer; typedef typename MeshType::HEdgePointer HEdgePointer; typedef typename MeshType::FacePointer FacePointer; typedef typename MeshType::VertexIterator VertexIterator; typedef typename MeshType::EdgeIterator EdgeIterator; typedef typename MeshType::HEdgeIterator HEdgeIterator; typedef typename MeshType::FaceIterator FaceIterator; /*! * Collpases an edge shared by two quads, generating only quads. * Made by a series of a vertex rotation and a diagonal collapse. * * \param m Mesh * \param hp hegde to be collapsed * \param vp Vertex that will be rotated * * \return Pointer to the new vertex */ static VertexPointer edge_collapse_quad(MeshType &m, HEdgePointer hp, VertexPointer vp) { assert(vp); assert(hp); assert(MeshType::HEdgeType::HasHVAdjacency()); assert(hp->HVp() == vp || hp->HOp()->HVp() == vp); assert(hp->HFp()->VN() == 4); assert(hp->HOp()->HFp()->VN() == 4); VertexPointer vp_opp; FacePointer fp; if( hp->HVp() == vp ) hp = hp->HOp(); //retrieve opposite vertex and right face for diagonal collapse vp_opp = hp->HVp(); fp = hp->HFp(); VertexPointer vp_rot = vertex_rotate_quad( vp ); assert(vp_rot == vp); return diagonal_collapse_quad( m, fp, vp ); } /*! * Collpases a diagonal in a quad. * * * \param m Mesh * \param fp Face where diagonal resides * \param vp One of the two vertices of the diagonal * * \return Pointer to the new vertex */ static VertexPointer diagonal_collapse_quad(MeshType &m, FacePointer fp, VertexPointer vp) { assert(MeshType::VertexType::HasVHAdjacency()); assert(MeshType::FaceType::HasFHAdjacency()); assert(MeshType::HEdgeType::HasHVAdjacency()); assert(MeshType::HEdgeType::HasHFAdjacency()); assert(MeshType::HEdgeType::HasHOppAdjacency()); assert(MeshType::HEdgeType::HasHPrevAdjacency()); assert(fp); assert(fp->FHp()); assert(fp->VN() == 4); assert(!fp->IsD()); assert( !has_doublet_quad(fp) ); assert(!is_singlet_quad(fp)); bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); bool HasEH = MeshType::EdgeType::HasEHAdjacency(); HEdgePointer hp; vector vps = getVertices(fp); assert(vps.size()==4); for(unsigned int i = 0; i< vps.size(); i++) { // all vertices must be different assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); } hp = fp->FHp(); while(hp->HVp() != vp) hp= hp->HNp(); vector hps = getHEdges(fp,hp); assert(vp == hps[0]->HVp()); VertexPointer opposite_vertex = hps[2]->HVp(); vp->P() = (vp->P() + opposite_vertex->P() )/2; change_vertex(opposite_vertex, vp); hps[0]->HOp()->HOp()=hps[1]->HOp(); hps[1]->HOp()->HOp()=hps[0]->HOp(); hps[2]->HOp()->HOp()=hps[3]->HOp(); hps[3]->HOp()->HOp()=hps[2]->HOp(); for(int i=0; i<4; i++) { if(hps[i]->HVp()->VHp() == hps[i]) hps[i]->HVp()->VHp() = hps[(i+4-1)%4]->HOp(); } if(HasHE) { hps[1]->HOp()->HEp()=hps[0]->HEp(); hps[3]->HOp()->HEp()=hps[2]->HEp(); } if(HasEH && HasHE) { for(int i=0; i<3; i+=2) { if(hps[i]->HEp()->EHp() == hps[i]) hps[i]->HEp()->EHp() = hps[i]->HOp(); } } // there are no faces, remove hedges and edge /* /\ hps[1]->HOp() / \ hps[0]->HOp() / \ ------ ------ | \ / | | \ / | |_______\/_______| */ bool b[2]; b[0] = (hps[0]->HOp()->HFp() == NULL && hps[1]->HOp()->HFp() == NULL); b[1] = (hps[2]->HOp()->HFp() == NULL && hps[3]->HOp()->HFp() == NULL); for( int i=0, j=0; i < 4; i+=2, j++ ) { if(b[j]) { if(HasHE) Allocator::DeleteEdge(m, *(hps[i]->HEp()) ); Allocator::DeleteHEdge(m, *(hps[i]->HOp()) ); Allocator::DeleteHEdge(m, *(hps[i+1]->HOp()) ); hps[i+1]->HVp()->VHp() = NULL; if(!b[(j+1)%2]) { hps[i]->HOp()->HNp()->HPp() = hps[i+1]->HOp()->HPp(); hps[i+1]->HOp()->HPp()->HNp() = hps[i]->HOp()->HNp(); if(vp->VHp() == hps[i+1]->HOp()) vp->VHp() = hps[(i+3)%4]->HOp(); } else vp->VHp() = NULL; } } Allocator::DeleteFace(m, *(fp) ); Allocator::DeleteVertex(m, *(opposite_vertex) ); if(HasHE) { Allocator::DeleteEdge(m, *(hps[1]->HEp()) ); Allocator::DeleteEdge(m, *(hps[3]->HEp()) ); } Allocator::DeleteHEdge(m, *(hps[0]) ); Allocator::DeleteHEdge(m, *(hps[1]) ); Allocator::DeleteHEdge(m, *(hps[2]) ); Allocator::DeleteHEdge(m, *(hps[3]) ); return vp; } /*! * Removes a doublet merging the two quads in one * * \param m Mesh * \param vp Vertex shared by the two consecutive edges of the doublet * * \return Pointer to the new face */ static FacePointer doublet_remove_quad(MeshType &m, VertexPointer vp) { assert(vp); HEdgePointer hp = vp->VHp(); assert(hp); FacePointer fp1 = hp->HFp(); FacePointer fp2 = hp->HOp()->HFp(); assert(!is_singlet_quad(fp1)); assert(!is_singlet_quad(fp2)); assert( fp1 ); assert( fp2 ); assert( hp->HOp()->HNp()->HOp() == hp->HPp() ); assert( fp1->VN() == 4); assert( fp2->VN() == 4); // end of check hp->HNp()->HPp() = hp->HOp()->HPp(); hp->HOp()->HPp()->HNp() = hp->HNp(); hp->HPp()->HPp()->HNp() = hp->HOp()->HNp()->HNp(); hp->HOp()->HNp()->HNp()->HPp() = hp->HPp()->HPp(); if( hp->HOp()->HVp()->VHp() == hp->HOp() ) hp->HOp()->HVp()->VHp() = hp->HNp(); if( hp->HPp()->HVp()->VHp() == hp->HPp() ) hp->HPp()->HVp()->VHp() = hp->HPp()->HPp()->HNp(); hp->HNp()->HPp()->HFp() = fp1; hp->HOp()->HNp()->HNp()->HFp() = fp1; if(fp1->FHp() == hp || fp1->FHp() == hp->HPp()) fp1->FHp() = hp->HNp(); Allocator::DeleteVertex(m, *vp); if(MeshType::HEdgeType::HasHEAdjacency()) { Allocator::DeleteEdge(m, *(hp->HEp()) ); Allocator::DeleteEdge(m, *(hp->HPp()->HEp()) ); } Allocator::DeleteHEdge(m, *hp ); Allocator::DeleteHEdge(m, *(hp->HOp()) ); Allocator::DeleteHEdge(m, *(hp->HPp()) ); Allocator::DeleteHEdge(m, *(hp->HPp()->HOp()) ); Allocator::DeleteFace(m, *fp2 ); return fp1; } /*! * Removes a singlet replacing it with an edge * * \param m Mesh * \param fp Face that should be a singlet quad * * \return Pointer to an halfdedge representing the new edge */ static HEdgePointer singlet_remove_quad(MeshType &m, FacePointer fp) { /* 2 / \ / \ | | | | | 1 | | /\ | \ | | / \ \/ / 3 */ assert( is_singlet_quad(fp) ); bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); bool HasEH = MeshType::EdgeType::HasEHAdjacency(); vector ext_hedges; vector int_hedges = getHEdges(fp); Allocator::DeleteFace( m, *(fp) ); for(typename vector::iterator hi = int_hedges.begin(); hi != int_hedges.end();++hi) { if((*hi)->HOp()->HFp() != fp) ext_hedges.push_back((*hi)->HOp()); else if(vertex_valence((*hi)->HVp()) == 1) { Allocator::DeleteVertex( m, *((*hi)->HVp()) ); if(HasHE) Allocator::DeleteEdge( m, *((*hi)->HEp()) ); } } for(typename vector::iterator hi = int_hedges.begin(); hi != int_hedges.end();++hi) Allocator::DeleteHEdge( m, *(*hi) ); assert(ext_hedges.size() == 2); if(ext_hedges[0]->HFp() || ext_hedges[1]->HFp()) { ext_hedges[0]->HOp() = ext_hedges[1]; ext_hedges[1]->HOp() = ext_hedges[0]; if(HasHE) { Allocator::DeleteEdge( m, *(ext_hedges[1]->HEp()) ); ext_hedges[1]->HEp() = ext_hedges[0]->HEp(); if(HasEH) ext_hedges[0]->HEp()->EHp() = ext_hedges[0]; } ext_hedges[0]->HVp()->VHp() = ext_hedges[0]; ext_hedges[1]->HVp()->VHp() = ext_hedges[1]; return ext_hedges[0]; } else { ext_hedges[0]->HVp()->VHp() = NULL; ext_hedges[1]->HVp()->VHp() = NULL; if(HasHE) { Allocator::DeleteEdge( m, *( ext_hedges[0]->HEp()) ); Allocator::DeleteEdge( m, *( ext_hedges[1]->HEp()) ); } Allocator::DeleteHEdge( m, *( ext_hedges[0]) ); Allocator::DeleteHEdge( m, *( ext_hedges[1]) ); return NULL; } } /*! * Rotates a non-border edge shared by two quads * * \param hp Edge to be rotated * \param cw flag denoting a clockwise or counter-clockwise rotation * * \return Pointer to the rotated edge */ static HEdgePointer edge_rotate_quad(HEdgePointer hp, bool cw) { assert( MeshType::HEdgeType::HasHFAdjacency() ); assert( MeshType::HEdgeType::HasHOppAdjacency() ); assert( MeshType::FaceType::HasFHAdjacency() ); FacePointer fp1 = hp->HFp(); FacePointer fp2 = hp->HOp()->HFp(); assert( fp1 ); assert( fp1->VN() == 4 ); assert( fp2 ); assert( fp2->VN() == 4 ); assert(!is_singlet_quad(fp1)); assert(!is_singlet_quad(fp2)); assert(!has_doublet_quad(fp1)); assert(!has_doublet_quad(fp2)); vector fps; typedef vector hedge_vect; vector hps; fps.push_back(fp1); fps.push_back(fp2); hps.push_back( getHEdges( fp1, hp ) ); hps.push_back( getHEdges( fp2, hp->HOp() ) ); for(int i=0; i< 2; i++) { int j = (i+1)%2; // uguali sia per cw che per ccw hps[i][1]->HPp() = hps[j][3]; hps[j][3]->HNp() = hps[i][1]; if(hps[j][0]->HVp()->VHp() == hps[j][0]) hps[j][0]->HVp()->VHp() = hps[i][1]; if(cw) { hps[i][0]->HNp() = hps[j][3]; hps[j][3]->HPp() = hps[i][0]; hps[j][2]->HNp() = hps[j][0]; hps[j][0]->HPp() = hps[j][2]; hps[j][0]->HVp() = hps[j][3]->HVp(); hps[j][3]->HFp() = fps[i]; if(fps[j]->FHp() == hps[j][3]) fps[j]->FHp() = hps[j][0]; } else { hps[i][0]->HNp() = hps[i][2]; hps[i][2]->HPp() = hps[i][0]; hps[i][1]->HNp() = hps[j][0]; hps[j][0]->HPp() = hps[i][1]; hps[j][0]->HVp() = hps[i][2]->HVp(); hps[i][1]->HFp() = fps[j]; if(fps[i]->FHp() == hps[i][1]) fps[i]->FHp() = hps[i][0]; } } return hp; } /*! * Rotates a non-border vertex shared by only quads * * \param vp Vertex to be rotated * * \return Pointer to the rotated vertex */ static VertexPointer vertex_rotate_quad(VertexPointer vp) { assert(MeshType::VertexType::HasVHAdjacency()); assert( vp->VHp() ); Pos p(vp->VHp(), true); HEdgePointer hep = p.HE(); typedef vector hedge_vect; vector hedges; do { assert( p.F() ); assert( p.F()->VN() == 4); hedges.push_back(getHEdges(p.F(), p.HE())); p.FlipE(); p.FlipF(); }while(p.HE() != hep); int size = hedges.size(); for(int i=0; i< size; i++) { hedges[i][0]->HNp() = hedges[i][2]; hedges[i][2]->HPp() = hedges[i][0]; assert(hedges[i][0]->HOp() == hedges[(i+size-1)%size][3]); hedges[i][2]->HNp() = hedges[(i+1)%size][1]; hedges[(i+1)%size][1]->HPp() = hedges[i][2]; hedges[(i+1)%size][1]->HNp() = hedges[i][3]; hedges[i][3]->HPp() = hedges[(i+1)%size][1]; hedges[(i+1)%size][1]->HFp() = hedges[i][3]->HFp(); if(hedges[i][3]->HVp()->VHp() == hedges[i][3]) hedges[i][3]->HVp()->VHp() = hedges[(i+1)%size][1]; hedges[i][3]->HVp() = hedges[(i+1)%size][2]->HVp(); if(hedges[i][0]->HFp()->FHp() == hedges[i][1]) hedges[i][0]->HFp()->FHp() = hedges[i][0]; } return vp; } /*! * Collapses a generic edge * * \param m Mesh * \param hp Edge to be collapsed * \param vp Vertex to be deleted * * \return Pointer to the other vertex belonging to the collapsed edge */ static VertexPointer edge_collapse(MeshType &m, HEdgePointer hp, VertexPointer vp) { assert(MeshType::VertexType::HasVHAdjacency()); assert(MeshType::HEdgeType::HasHOppAdjacency()); assert(MeshType::HEdgeType::HasHVAdjacency()); assert(MeshType::HEdgeType::HasHPrevAdjacency()); if( hp->HFp() ) assert(hp->HFp()->VN() > 3); if( hp->HOp()->HFp()) assert(hp->HOp()->HFp()->VN() > 3); assert(hp->HFp() || hp->HOp()->HFp()); assert(hp->HVp() == vp || hp->HOp()->HVp() == vp); HEdgePointer hopp = hp->HOp(); VertexPointer vp1; if( hp->HVp() == vp ) vp1 = hopp->HVp(); else vp1 = hp->HVp(); change_vertex( vp, vp1); //HP hp->HNp()->HPp() = hp->HPp(); hopp->HNp()->HPp() = hopp->HPp(); //HN hp->HPp()->HNp() = hp->HNp(); hopp->HPp()->HNp() = hopp->HNp(); //FH if( hp->HFp() ) if( hp->HFp()->FHp() == hp ) hp->HFp()->FHp() = hp->HNp(); if( hopp->HFp() ) if( hopp->HFp()->FHp() == hopp ) hopp->HFp()->FHp() = hopp->HNp(); // VH if( vp1->VHp() == hopp ) vp1->VHp() = hopp->HNp(); if(HasHEAdjacency(m)) Allocator::DeleteEdge(m,*(hp->HEp())); Allocator::DeleteHEdge(m,*hp); Allocator::DeleteHEdge(m,*hopp); Allocator::DeleteVertex(m,*vp); return vp1; } /*! * Adds a face in a mesh, checking if the operation is possible. * * \param m Mesh * \param vps Vector of vertices (in ccw order) that will belong to the new face * * \return Pointer to the new face if it has been inserted, NULL otherwise */ static FacePointer add_face(MeshType &m, vector &vps) { assert(MeshType::VertexType::HasVHAdjacency()); assert(MeshType::HEdgeType::HasHVAdjacency()); assert(MeshType::HEdgeType::HasHFAdjacency()); assert(MeshType::HEdgeType::HasHOppAdjacency()); assert(MeshType::HEdgeType::HasHPrevAdjacency()); unsigned int size = vps.size(); assert(size >= 3); //there must be at least 3 vertices for(unsigned int i = 0; i< size; i++) { // all vertices must be different assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); } vector hps; while(hps.size() < size) if( !can_add_hedge(vps, hps) ) return NULL; vector non_manifold_vertices(size, false); return add_face_unsafe( m,vps, hps, non_manifold_vertices); } /*! * Removes a face in a mesh, checking if the operation is possible * * \param m Mesh * \param fp face to be removed * * \retval true if face has been removed * \retval false otherwise */ static bool remove_face(MeshType &m, FacePointer fp) { assert(MeshType::VertexType::HasVHAdjacency()); assert(MeshType::FaceType::HasFHAdjacency()); assert(MeshType::HEdgeType::HasHVAdjacency()); assert(MeshType::HEdgeType::HasHFAdjacency()); assert(MeshType::HEdgeType::HasHOppAdjacency()); assert(MeshType::HEdgeType::HasHPrevAdjacency()); if( can_remove_face(fp) ) { remove_face_unsafe(m, fp); return true; } return false; } protected: /*! * Adds a face in a mesh without any check * * \param m Mesh * \param vps Vector of vertices (in ccw order) that will belong to the new face * * \return Pointer to the new face */ static FacePointer add_face_unsafe(MeshType &m, vector &vps) { unsigned int size = vps.size(); vector hps; vector non_manifold_vertices; while(hps.size() < size) { if( can_add_hedge(vps, hps) ) non_manifold_vertices.push_back( false ); else non_manifold_vertices.push_back( hps.back() == NULL ); } return add_face_unsafe(m,vps,hps, non_manifold_vertices); } /*! * Adds a face in a mesh without any check * * \param m Mesh * \param vps Vector of vertices (in ccw order) that will belong to the new face * \param hps Vector of hedges (in ccw order) that will belong to the new face * \param non_manifold_vertices Vector of booleans denoting on the i-th position if the i-th vertex is non-manifold * * \return Pointer to the new face */ static FacePointer add_face_unsafe(MeshType &m, vector &vps, vector &hps, vector &non_manifold_vertices) { assert(MeshType::VertexType::HasVHAdjacency()); assert(MeshType::HEdgeType::HasHVAdjacency()); assert(MeshType::HEdgeType::HasHFAdjacency()); assert(MeshType::HEdgeType::HasHOppAdjacency()); assert(MeshType::HEdgeType::HasHPrevAdjacency()); unsigned int size = vps.size(); assert(size >= 3); //there must be at least 3 vertices // for(unsigned int i = 0; i< size; i++) // { // // all vertices must be different // assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); // } bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); bool HasEH = MeshType::EdgeType::HasEHAdjacency(); HEdgeIterator hi; assert(hps.size() == size); HEdgePointer nullPointer = NULL; int edge_n = count(hps.begin(), hps.end(), nullPointer); FacePointer fp; FaceIterator fi = Allocator::AddFaces(m,1); (*fi).Alloc( size ); fp = &(*fi); if(edge_n > 0) { EdgeIterator ei; fp->SetD(); if(HasEH || HasHE) { ei = Allocator::AddEdges(m,edge_n); for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1) (*ei1).SetD(); } typename Allocator::template PointerUpdater pu; if(m.hedge.empty()) pu.oldBase = 0; else { pu.oldBase = &*(m.hedge.begin()); pu.oldEnd = &m.hedge.back()+1; } hi = Allocator::AddHEdges(m,2*edge_n); pu.newBase = &*(m.hedge.begin()); pu.newEnd = &m.hedge.back()+1; //undelete face fp->ClearD(); //undelete edges for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1) (*ei1).ClearD(); // update hedge pointers (if needed) if( pu.NeedUpdate() ) for(typename vector::iterator hpsi = hps.begin(); hpsi != hps.end(); ++hpsi) { if((*hpsi)) pu.Update(*hpsi); } HEdgeIterator hi1 = hi; HEdgeIterator hi2 = hi; ++hi2; EdgeIterator ei1 = ei; for(; hi2 != m.hedge.end(); ++hi1, ++hi2) { // EH if(HasEH) (*ei1).EHp() = &(*hi1); // HE if(HasHE) { (*hi1).HEp() = &(*ei1); (*hi2).HEp() = &(*ei1); } //HO (*hi1).HOp() = &(*hi2); (*hi2).HOp() = &(*hi1); // HF (*hi1).HFp() = fp; ++hi1; ++hi2; } } vector hps1; for(unsigned int i = 0; i < size; i++) { if(hps[i] == NULL) { hps1.push_back(&(*hi)); ++hi; ++hi; } else hps1.push_back(hps[i]); } assert( hps1.size() == size ); for(unsigned int i = 0; i < size; i++) { int next = (i+1)%size; // hedge already exisitng if(hps[i]) { hps1[i]->HFp() = fp; // next hedge was disconnected if(!hps[next]) { hps1[next]->HOp()->HNp() = hps1[i]->HNp(); hps1[i]->HNp()->HPp() = hps1[next]->HOp(); hps1[i]->HNp() = hps1[next]; hps1[next]->HPp() = hps1[i]; } } // hedge wasn't existing, vertex was disconnected else { //HV hps1[i]->HVp() = vps[i]; hps1[i]->HOp()->HVp() = vps[next]; hps1[i]->HNp() = hps1[next]; // next hedge was existing (vertex was disconnected) if(hps[next]) { hps1[i]->HOp()->HPp() = hps1[next]->HPp(); hps1[next]->HPp()->HNp() = hps1[i]->HOp(); } //vertex was detached else { // after face insertion vertex will become non-manifold if(non_manifold_vertices[next]) { Pos p(vps[next]->VHp(), true); while(p.F()) { p.FlipE(); p.FlipF(); if(p.HE() == vps[next]->VHp()) assert(0); //can't add a connection, there is no space } p.HE()->HPp()->HNp() = hps1[i]->HOp(); hps1[i]->HOp()->HPp() = p.HE()->HPp(); p.HE()->HPp() = hps1[next]->HOp(); hps1[next]->HOp()->HNp() = p.HE(); } else { hps1[i]->HOp()->HPp() = hps1[next]->HOp(); hps1[next]->HOp()->HNp() = hps1[i]->HOp(); } } hps1[next]->HPp() = hps1[i]; //VH if( !vps[i]->VHp()) vps[i]->VHp() = hps1[i]; } } //FH fp->FHp() = hps1.front(); return fp; } /*! * Removes a face in a mesh, without any check * * \param m Mesh * \param fp Face to be removed * */ static void remove_face_unsafe (MeshType &m, FacePointer fp) { vector hps = getHEdges(fp); int size = hps.size(); for( int i = 0; i< size; i++ ) { if( hps[i]->HOp()->HFp() ) { hps[i]->HFp() = NULL; if( !hps[(i+size-1)%size]->HOp()->HFp() ) { // HP hps[i]->HPp() = hps[(i+size-1)%size]->HOp()->HPp(); hps[(i+size-1)%size]->HOp()->HPp()->HNp() = hps[i]; } if( !hps[(i+1)%size]->HOp()->HFp() ) { // HN hps[i]->HNp() = hps[(i+1)%size]->HOp()->HNp(); hps[(i+1)%size]->HOp()->HNp()->HPp() = hps[i]; } } else { Allocator::DeleteHEdge( m, *hps[i] ); Allocator::DeleteHEdge( m, *(hps[i]->HOp()) ); if(MeshType::HEdgeType::HasHEAdjacency()) Allocator::DeleteEdge( m, *(hps[i]->HEp()) ); if( !hps[(i+size-1)%size]->HOp()->HFp() ) { hps[i]->HOp()->HNp()->HPp() = hps[(i+size-1)%size]->HOp()->HPp(); hps[(i+size-1)%size]->HOp()->HPp()->HNp() = hps[i]->HOp()->HNp(); } } } for( int i = 0; i< size; i++ ) { if( hps[i]->HVp()->VHp()->IsD() ) { if( !hps[i]->IsD() ) hps[i]->HVp()->VHp() = hps[i]; else if( !hps[(i+size-1)%size]->IsD() ) hps[i]->HVp()->VHp() = hps[(i+size-1)%size]->HOp(); else //search for a hedge (hedge can be found only if the vertex is non-manifold) { bool manifold = true; Pos p(hps[i]->HVp()->VHp(), true); p.HE()->SetV(); p.FlipE(); p.FlipF(); while( !p.HE()->IsV() ) { if( !p.HE()->IsD() ) { manifold = false; hps[i]->HVp()->VHp() = p.HE(); break; } p.FlipE(); p.FlipF(); } p.HE()->ClearV(); if(manifold) hps[i]->HVp()->VHp() = NULL; } } } Allocator::DeleteFace(m,*fp); } /*! * Checks if the next hedge can be inserted into hps. * If true, inserts the hedge into hps. If false, inserts NULL. * * \param vps Vector of vertices (in ccw order) that will belong to the new face * \param hps Vector of hedges already checked * * \retval true if hedge can be inserted * \retval false otherwise */ static bool can_add_hedge( vector &vps, vector &hps ) { unsigned int i = hps.size(); assert( i < vps.size() ); HEdgePointer he = vps[i]->VHp(); if(!he) //vertex is detached { hps.push_back(NULL); return true; } else { bool disconnected = false; bool hasEdge = false; unsigned int size = vps.size(); Pos p(he, false); he->SetV(); while(p.V() != vps[(i+1)%size]) { if(!hasEdge) hasEdge= ( find( vps.begin(), vps.end(), p.V()) != (vps.end() ) ); p.FlipV(); p.FlipE(); p.FlipF(); p.FlipV(); if(p.HE()->IsV()) { disconnected = true; break; } } he->ClearV(); if(disconnected) // edge does not exist { hps.push_back(NULL); // if hasEdge is false after inserting the face there will be a non-manifold vertex return hasEdge; } else //edge already existing { // try to insert consecutve hedges if they will belong to the new face while( (p.V() == vps[(i+1)%size]) && (i < size) ) { hps.push_back( p.HE() ); if(p.HE()->HFp() != NULL) return false; i++; p.FlipE(); p.FlipV(); } return true; } } } public: /*! * Checks if a face can be removed * * \param fp Face to check * * \retval true if the face can be removed * \retval false otherwise */ static bool can_remove_face(FacePointer fp) { assert(fp); assert(!fp->IsD()); Pos p(fp->FHp(), true); do { vector incident_faces = get_incident_faces( p.V() ); unsigned int size = incident_faces.size(); if(size > 2) { for(unsigned int i = 0; i < size; i++) { if(incident_faces[i] == NULL) if(incident_faces[(i+1)%size] != fp && incident_faces[((i+size)-1)%size] != fp ) return false; } } p.FlipV(); p.FlipE(); }while( p.HE() != fp->FHp() ); return true; } /*! * Checks if a diagonal can be collapsed * * \param hp Hedge whose vertex is one of the two vertices of the diagonal * * \retval true if diagonal can be collapsed * \retval false if diagonal cannot be collapsed */ static bool check_diagonal_collapse_quad(HEdgePointer hp) { assert(hp); assert(hp->HFp()); assert(hp->HFp()->VN() == 4); assert(!hp->IsD()); vector faces; HEdgePointer hopp = hp->HNp()->HNp(); vector faces1 = get_incident_faces(hp->HVp(), hp); vector faces2 = get_incident_faces(hp->HNp()->HNp()->HVp(), hopp); faces.assign(faces1.begin()+1, faces1.end()); faces.assign(faces2.begin()+1, faces2.end()); // First check: unsigned int size = faces.size(); bool null_face = false; // if size <=2 check is ok if(size > 2) { for(unsigned int i = 0; i < size; i++) { if(faces[i] == NULL) { if(faces[(i+1)%size] != NULL && faces[((i+size)-1)%size] != NULL ) { if(null_face) return false; null_face=true; } } } } // End of first check // Second check set set1; set set2; vector vect1 = getVertices(hp->HVp()); vector vect2 = getVertices(hp->HNp()->HNp()->HVp()); set1.insert(vect1.begin(), vect1.end()); set2.insert(vect2.begin(), vect2.end()); size = vect1.size(); if(vect2.size() < size) size = vect2.size(); vector intersection(size); typename vector::iterator it; it = set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), intersection.begin()); size = it- intersection.begin(); assert( size >= 2 ); return (size==2); // End of second check } /*! * Checks if a vertex is non-manifold, comparing local and global information (slow) * * \param m Mesh * \param vp Vertex to check * * \retval true if vertex is non-manifold * \retval false if verex is manifold */ static bool is_nonManifold_vertex(MeshType &m, VertexPointer vp) { assert(vp); assert(!vp->IsD()); set set1; for(HEdgeIterator hi = m.hedge.begin(); hi != m.hedge.end(); ++hi) { if(!(*hi).IsD() && (*hi).HVp() == vp) set1.insert(&(*hi)); } vector vect2 = get_incident_hedges(vp); set set2; set2.insert(vect2.begin(), vect2.end()); return !equal(set1.begin(), set1.end(), set2.begin()); } /*! * Checks if a vertex is non-manifold, based only on local informations * * \param vp Vertex to check * * \retval true if vertex is non-manifold * \retval false if verex is manifold */ static bool is_nonManifold_vertex(VertexPointer vp) { assert(vp); assert(!vp->IsD()); vector faces = get_incident_faces(vp); unsigned int size = faces.size(); int null_count = 0; if(size > 2) { for(unsigned int i = 0; i < size; i++) { if(faces[i] == NULL) { if(null_count > 0) return true; else null_count++; } } } return false; } /*! * Shortcut to get the second vertex of an edge * * \param hp Hedge * * \return Opposite vertex */ static VertexPointer opp_vert(HEdgePointer hp) { return hp->HOp()->HVp(); } /*! * Gets vertices on the 1-ring of a vertex * * \param vp Vertex. It must be a non-border vertex. * * \return Vector containing vertices */ static vector getVertices(VertexPointer vp) { assert(vp); assert(!vp->IsD()); HEdgePointer hp = vp->VHp(); vector ret; if( !hp ) return ret; Pos p(hp); do { if(p.F()) { assert(!p.F()->IsD()); ret.push_back( opp_vert( p.HE() ) ); ret.push_back( opp_vert( p.HE()->HNp() ) ); } p.FlipE(); p.FlipF(); }while( p.HE() != hp); return ret; } /*! * Gets faces on the 1-ring of a vertex * * \param vp Vertex * * \return Set containing faces */ static set getFaces(VertexPointer vp) { assert(vp); assert(!vp->IsD()); set ret; vector vertices = getVertices(vp); for(typename vector::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi) { vector incident_faces = get_incident_faces(*vi); ret.insert(incident_faces.begin(), incident_faces.end()); } return ret; } /*! * Checks if a face is a singlet * * \param fp Face to check * * \retval true if face is a singlet * \retval false if face isn't a singlet */ static bool is_singlet_quad(FacePointer fp) { assert(fp); assert(fp->FHp()); assert(!fp->IsD()); Pos p( fp->FHp() ); do { if( vertex_valence(p.V()) == 1 ) return true; p.FlipV(); p.FlipE(); }while(p.HE() != fp->FHp()); return false; } /*! * Gets all vertices incident to a face * * \param fp Face * \param starting_he A hedge in the face from which to start * * \return Vector containing the incident vertices */ static vector getVertices(FacePointer fp, HEdgePointer starting_he = NULL) { assert(fp); assert(!fp->IsD()); if(!starting_he) starting_he = fp->FHp(); assert( starting_he->HFp() == fp ); Pos p( starting_he, true ); vector ret; do { assert(!(p.V()->IsD())); ret.push_back( p.V() ); p.FlipV(); p.FlipE(); assert(ret.size() <= (unsigned int)(fp->VN())); }while(p.HE() != starting_he); return ret; } protected: /*! * Gets all hedges incident to a face * * \param fp Face * \param starting_he A hedge in the face from which to start * * \return Vector containing the incident hedges */ static vector getHEdges(FacePointer fp, HEdgePointer starting_he = NULL) { assert(fp); assert(!fp->IsD()); if(starting_he) assert( starting_he->HFp() == fp ); else starting_he = fp->FHp(); Pos p( starting_he, true ); vector ret; do { ret.push_back( p.HE() ); p.FlipV(); p.FlipE(); assert(ret.size() <= (unsigned int) (fp->VN())); }while(p.HE() != starting_he); return ret; } public: /*! * Gets all faces incident to a vertex * * \param vp Vertex * \param starting_he A hedge from which to start * * \return Vector containing the incident faces */ static vector get_incident_faces(VertexPointer vp, HEdgePointer starting_he = NULL) { assert(vp); assert(!vp->IsD()); if(starting_he) assert( starting_he->HVp() == vp ); else starting_he = vp->VHp(); vector ret; if(!starting_he) return ret; Pos p( starting_he, true ); do { ret.push_back( p.F() ); p.FlipE(); p.FlipF(); }while(p.HE() != starting_he); return ret; } static vector get_adjacent_faces(FacePointer fp) { assert(fp); assert(!fp->IsD()); vector ret; Pos p( fp->FHp() ); assert(p.F() == fp); do { p.FlipF(); ret.push_back( p.F() ); p.FlipF(); p.FlipV(); p.FlipE(); } while(p.HE() != fp->FHp()); return ret; } /*! * Gets all hedges incident to a vertex * * \param vp Vertex * \param starting_he A hedge from which to start navigation * * \return Vector containing the incident hedges */ static vector get_incident_hedges(VertexPointer vp, HEdgePointer starting_he = NULL) { assert(vp); assert(!vp->IsD()); if(starting_he) assert( starting_he->HVp() == vp ); else starting_he = vp->VHp(); vector ret; if(!starting_he) return ret; Pos p( starting_he, true ); do { assert(!p.HE()->IsD()); ret.push_back( p.HE() ); p.FlipE(); p.FlipF(); }while(p.HE() != starting_he); return ret; } /*! * Checks if a face has doublets * * \param fp Face to check * * \retval true if face has at least a doublet * \retval false if face hasn't any doublet */ static bool has_doublet_quad(FacePointer fp) { return ( !find_doublet_hedges_quad(fp).empty() ); } /*! * Gets all hedges whose vertex is into a doublet * * \param fp Face to check * * \return Vector containing the hedges */ static vector find_doublet_hedges_quad(FacePointer fp) { assert(fp); assert(fp->FHp()); assert(!fp->IsD()); vector ret; Pos p( fp->FHp(), true ); do { if(vertex_valence(p.V()) == 2 && !isBorderVertex(p.V())) ret.push_back(p.HE()); assert(ret.size() <= 4); p.FlipV(); p.FlipE(); }while(p.HE() != fp->FHp()); return ret; } /*! * Checks if a vertex is a border vertex * * \param vp Vertex to check * * \retval true if vertex is a border vertex * \retval false if vertex isn't a border vertex */ static bool isBorderVertex(VertexPointer vp) { assert(vp); assert(!vp->IsD()); if( !(vp->VHp()) ) return true; Pos p( vp->VHp() ); do { if(!p.F()) return true; p.FlipE(); p.FlipF(); }while(p.HE() != vp->VHp()); return false; } /*! * Computes valence of a vertex * * \param vp Vertex * * \return Vertex valence */ static int vertex_valence(VertexPointer vp) { assert(vp); assert(!vp->IsD()); if( !(vp->VHp()) ) return 0; int ret = 0; Pos p( vp->VHp() ); do { assert(!p.HE()->IsD()); ret++; p.FlipE(); p.FlipF(); }while(p.HE() != vp->VHp()); return ret; } /*! * Connects to a new vertex all hedges incident to a vertex * * \param old_vp the old vertex to be disconnected * \param new_vp the new vertex to be connected * */ protected: static void change_vertex(VertexPointer old_vp, VertexPointer new_vp) { assert(old_vp); assert(new_vp); assert(old_vp != new_vp); assert(!old_vp->IsD()); Pos p(old_vp->VHp(),true); p.HE()->SetV(); do { p.HE()->HVp() = new_vp; p.FlipE(); p.FlipF(); }while( !p.HE()->IsV() ); p.HE()->ClearV(); if( !new_vp->VHp() ) new_vp->VHp() = old_vp->VHp(); } }; } } #endif // VCG_HEDGE_TOPOLOGY