diff --git a/vcg/complex/algorithms/clean.h b/vcg/complex/algorithms/clean.h index 706b5963..a11fa6f3 100644 --- a/vcg/complex/algorithms/clean.h +++ b/vcg/complex/algorithms/clean.h @@ -108,23 +108,22 @@ class Clean public: typedef CleanMeshType MeshType; - typedef typename MeshType::VertexType VertexType; - typedef typename MeshType::VertexPointer VertexPointer; - typedef typename MeshType::VertexIterator VertexIterator; - typedef typename MeshType::ConstVertexIterator ConstVertexIterator; - typedef typename MeshType::EdgeIterator EdgeIterator; - typedef typename MeshType::EdgePointer EdgePointer; - typedef typename MeshType::CoordType CoordType; - typedef typename MeshType::ScalarType ScalarType; - typedef typename MeshType::FaceType FaceType; - typedef typename MeshType::FacePointer FacePointer; - typedef typename MeshType::FaceIterator FaceIterator; - typedef typename MeshType::ConstFaceIterator ConstFaceIterator; - typedef typename MeshType::FaceContainer FaceContainer; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + typedef typename MeshType::ConstVertexIterator ConstVertexIterator; + typedef typename MeshType::EdgeIterator EdgeIterator; + typedef typename MeshType::EdgePointer EdgePointer; + typedef typename MeshType::CoordType CoordType; + typedef typename MeshType::ScalarType ScalarType; + typedef typename MeshType::FaceType FaceType; + typedef typename MeshType::FacePointer FacePointer; + typedef typename MeshType::FaceIterator FaceIterator; + typedef typename MeshType::ConstFaceIterator ConstFaceIterator; + typedef typename MeshType::FaceContainer FaceContainer; typedef typename vcg::Box3 Box3Type; typedef GridStaticPtr TriMeshGrid; - typedef Point3 Point3x; /* classe di confronto per l'algoritmo di eliminazione vertici duplicati*/ class RemoveDuplicateVert_Compare{ @@ -136,267 +135,264 @@ public: }; - /** This function removes all duplicate vertices of the mesh by looking only at their spatial positions. - Note that it does not update any topology relation that could be affected by this like the VT or TT relation. - the reason this function is usually performed BEFORE building any topology information. - */ - static int RemoveDuplicateVertex( MeshType & m, bool RemoveDegenerateFlag=true) // V1.0 - { - if(m.vert.size()==0 || m.vn==0) return 0; + /** This function removes all duplicate vertices of the mesh by looking only at their spatial positions. + * Note that it does not update any topology relation that could be affected by this like the VT or TT relation. + * the reason this function is usually performed BEFORE building any topology information. + */ + static int RemoveDuplicateVertex( MeshType & m, bool RemoveDegenerateFlag=true) // V1.0 + { + if(m.vert.size()==0 || m.vn==0) return 0; - std::map mp; - size_t i,j; - VertexIterator vi; - int deleted=0; - int k=0; - size_t num_vert = m.vert.size(); - std::vector perm(num_vert); - for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi, ++k) - perm[k] = &(*vi); + std::map mp; + size_t i,j; + VertexIterator vi; + int deleted=0; + int k=0; + size_t num_vert = m.vert.size(); + std::vector perm(num_vert); + for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi, ++k) + perm[k] = &(*vi); - RemoveDuplicateVert_Compare c_obj; + RemoveDuplicateVert_Compare c_obj; - std::sort(perm.begin(),perm.end(),c_obj); + std::sort(perm.begin(),perm.end(),c_obj); - j = 0; - i = j; - mp[perm[i]] = perm[j]; - ++i; - for(;i!=num_vert;) - { - if( (! (*perm[i]).IsD()) && - (! (*perm[j]).IsD()) && - (*perm[i]).P() == (*perm[j]).cP() ) - { - VertexPointer t = perm[i]; - mp[perm[i]] = perm[j]; - ++i; - Allocator::DeleteVertex(m,*t); - deleted++; - } - else - { - j = i; - ++i; - } - } - - for(FaceIterator fi = m.face.begin(); fi!=m.face.end(); ++fi) - if( !(*fi).IsD() ) - for(k = 0; k < (*fi).VN(); ++k) - if( mp.find( (typename MeshType::VertexPointer)(*fi).V(k) ) != mp.end() ) - { - (*fi).V(k) = &*mp[ (*fi).V(k) ]; - } - - - for(EdgeIterator ei = m.edge.begin(); ei!=m.edge.end(); ++ei) - if( !(*ei).IsD() ) - for(k = 0; k < 2; ++k) - if( mp.find( (typename MeshType::VertexPointer)(*ei).V(k) ) != mp.end() ) - { - (*ei).V(k) = &*mp[ (*ei).V(k) ]; - } - if(RemoveDegenerateFlag) RemoveDegenerateFace(m); - if(RemoveDegenerateFlag && m.en>0) { - RemoveDegenerateEdge(m); - RemoveDuplicateEdge(m); - } - return deleted; + j = 0; + i = j; + mp[perm[i]] = perm[j]; + ++i; + for(;i!=num_vert;) + { + if( (! (*perm[i]).IsD()) && + (! (*perm[j]).IsD()) && + (*perm[i]).P() == (*perm[j]).cP() ) + { + VertexPointer t = perm[i]; + mp[perm[i]] = perm[j]; + ++i; + Allocator::DeleteVertex(m,*t); + deleted++; } + else + { + j = i; + ++i; + } + } - class SortedPair - { - public: - SortedPair() {} - SortedPair(unsigned int v0, unsigned int v1, EdgePointer _fp) - { - v[0]=v0;v[1]=v1; - fp=_fp; - if(v[0]>v[1]) std::swap(v[0],v[1]); - } - bool operator < (const SortedPair &p) const - { - return (v[1]!=p.v[1])?(v[1]0) { + RemoveDegenerateEdge(m); + RemoveDuplicateEdge(m); + } + return deleted; + } + + class SortedPair + { + public: + SortedPair() {} + SortedPair(unsigned int v0, unsigned int v1, EdgePointer _fp) + { + v[0]=v0;v[1]=v1; + fp=_fp; + if(v[0]>v[1]) std::swap(v[0],v[1]); + } + bool operator < (const SortedPair &p) const + { + return (v[1]!=p.v[1])?(v[1] fvec; + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + if(!(*fi).IsD()) + { + fvec.push_back(SortedTriple( tri::Index(m,(*fi).V(0)), + tri::Index(m,(*fi).V(1)), + tri::Index(m,(*fi).V(2)), + &*fi)); + } + assert (size_t(m.fn) == fvec.size()); + std::sort(fvec.begin(),fvec.end()); + int total=0; + for(int i=0;i::DeleteFace(m, *(fvec[i].fp) ); + } + } + return total; + } + + /** This function removes all duplicate faces of the mesh by looking only at their vertex reference. So it should be called after unification of vertices. Note that it does not update any topology relation that could be affected by this like the VT or TT relation. the reason this function is usually performed BEFORE building any topology information. */ - static int RemoveDuplicateFace( MeshType & m) // V1.0 - { - FaceIterator fi; - std::vector fvec; - for(fi=m.face.begin();fi!=m.face.end();++fi) - if(!(*fi).IsD()) - { - fvec.push_back(SortedTriple( tri::Index(m,(*fi).V(0)), - tri::Index(m,(*fi).V(1)), - tri::Index(m,(*fi).V(2)), - &*fi)); - } - assert (size_t(m.fn) == fvec.size()); - //for(int i=0;i::DeleteFace(m, *(fvec[i].fp) ); - //qDebug("deleting face %i (pos in fvec %i)",tri::Index(m,fvec[i].fp) ,i); - } - } - return total; - } + static int RemoveDuplicateEdge( MeshType & m) // V1.0 + { + if (m.en==0) return 0; + std::vector eVec; + for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei) + if(!(*ei).IsD()) + { + eVec.push_back(SortedPair( tri::Index(m,(*ei).V(0)), tri::Index(m,(*ei).V(1)), &*ei)); + } + assert (size_t(m.en) == eVec.size()); + //for(int i=0;i::DeleteEdge(m, *(eVec[i].fp) ); + //qDebug("deleting face %i (pos in fvec %i)",tri::Index(m,fvec[i].fp) ,i); + } + } + return total; + } - /** This function removes all duplicate faces of the mesh by looking only at their vertex reference. - So it should be called after unification of vertices. - Note that it does not update any topology relation that could be affected by this like the VT or TT relation. - the reason this function is usually performed BEFORE building any topology information. - */ - static int RemoveDuplicateEdge( MeshType & m) // V1.0 - { - //assert(m.fn == 0 && m.en >0); // just to be sure we are using an edge mesh... - if (m.en==0)return 0; - std::vector eVec; - for(EdgeIterator ei=m.edge.begin();ei!=m.edge.end();++ei) - if(!(*ei).IsD()) - { - eVec.push_back(SortedPair( tri::Index(m,(*ei).V(0)), tri::Index(m,(*ei).V(1)), &*ei)); - } - assert (size_t(m.en) == eVec.size()); - //for(int i=0;i::DeleteEdge(m, *(eVec[i].fp) ); - //qDebug("deleting face %i (pos in fvec %i)",tri::Index(m,fvec[i].fp) ,i); - } - } - return total; - } - static int CountUnreferencedVertex( MeshType& m) - { - return RemoveUnreferencedVertex(m,false); - } + static int CountUnreferencedVertex( MeshType& m) + { + return RemoveUnreferencedVertex(m,false); + } - /** This function removes that are not referenced by any face. The function updates the vn counter. + /** This function removes that are not referenced by any face. The function updates the vn counter. @param m The mesh @return The number of removed vertices */ - static int RemoveUnreferencedVertex( MeshType& m, bool DeleteVertexFlag=true) // V1.0 - { - FaceIterator fi; - EdgeIterator ei; - VertexIterator vi; - int referredBit = VertexType::NewBitFlag(); + static int RemoveUnreferencedVertex( MeshType& m, bool DeleteVertexFlag=true) // V1.0 + { + FaceIterator fi; + EdgeIterator ei; + VertexIterator vi; + int referredBit = VertexType::NewBitFlag(); - int j; - int deleted = 0; + int j; + int deleted = 0; - for(vi=m.vert.begin();vi!=m.vert.end();++vi) - (*vi).ClearUserBit(referredBit); + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + (*vi).ClearUserBit(referredBit); - for(fi=m.face.begin();fi!=m.face.end();++fi) - if( !(*fi).IsD() ) - for(j=0;j<(*fi).VN();++j) - (*fi).V(j)->SetUserBit(referredBit); + for(fi=m.face.begin();fi!=m.face.end();++fi) + if( !(*fi).IsD() ) + for(j=0;j<(*fi).VN();++j) + (*fi).V(j)->SetUserBit(referredBit); - for(ei=m.edge.begin();ei!=m.edge.end();++ei) - if( !(*ei).IsD() ){ - (*ei).V(0)->SetUserBit(referredBit); - (*ei).V(1)->SetUserBit(referredBit); - } + for(ei=m.edge.begin();ei!=m.edge.end();++ei) + if( !(*ei).IsD() ){ + (*ei).V(0)->SetUserBit(referredBit); + (*ei).V(1)->SetUserBit(referredBit); + } - for(vi=m.vert.begin();vi!=m.vert.end();++vi) - if( (!(*vi).IsD()) && (!(*vi).IsUserBit(referredBit))) - { - if(DeleteVertexFlag) Allocator::DeleteVertex(m,*vi); - ++deleted; - } - VertexType::DeleteBitFlag(referredBit); - return deleted; - } + for(vi=m.vert.begin();vi!=m.vert.end();++vi) + if( (!(*vi).IsD()) && (!(*vi).IsUserBit(referredBit))) + { + if(DeleteVertexFlag) Allocator::DeleteVertex(m,*vi); + ++deleted; + } + VertexType::DeleteBitFlag(referredBit); + return deleted; + } - /** + /** Degenerate vertices are vertices that have coords with invalid floating point values, All the faces incident on deleted vertices are also deleted */ - static int RemoveDegenerateVertex(MeshType& m) + static int RemoveDegenerateVertex(MeshType& m) + { + VertexIterator vi; + int count_vd = 0; + + for(vi=m.vert.begin(); vi!=m.vert.end();++vi) + if(math::IsNAN( (*vi).P()[0]) || + math::IsNAN( (*vi).P()[1]) || + math::IsNAN( (*vi).P()[2]) ) { - VertexIterator vi; - int count_vd = 0; + count_vd++; + Allocator::DeleteVertex(m,*vi); + } - for(vi=m.vert.begin(); vi!=m.vert.end();++vi) - if(math::IsNAN( (*vi).P()[0]) || - math::IsNAN( (*vi).P()[1]) || - math::IsNAN( (*vi).P()[2]) ) - { - count_vd++; - Allocator::DeleteVertex(m,*vi); - } + FaceIterator fi; + int count_fd = 0; - FaceIterator fi; - int count_fd = 0; + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD()) + if( (*fi).V(0)->IsD() || + (*fi).V(1)->IsD() || + (*fi).V(2)->IsD() ) + { + count_fd++; + Allocator::DeleteFace(m,*fi); + } + return count_vd; + } - for(fi=m.face.begin(); fi!=m.face.end();++fi) - if(!(*fi).IsD()) - if( (*fi).V(0)->IsD() || - (*fi).V(1)->IsD() || - (*fi).V(2)->IsD() ) - { - count_fd++; - Allocator::DeleteFace(m,*fi); - } - return count_vd; - } - - /** + /** Degenerate faces are faces that are Topologically degenerate, i.e. have two or more vertex reference that link the same vertex (and not only two vertexes with the same coordinates). @@ -404,295 +400,293 @@ public: We do not take care of topology because when we have degenerate faces the topology calculation functions crash. */ - static int RemoveDegenerateFace(MeshType& m) + static int RemoveDegenerateFace(MeshType& m) + { + int count_fd = 0; + + for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD()) { - int count_fd = 0; - - for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) - if(!(*fi).IsD()) - { - if((*fi).V(0) == (*fi).V(1) || - (*fi).V(0) == (*fi).V(2) || - (*fi).V(1) == (*fi).V(2) ) - { - count_fd++; - Allocator::DeleteFace(m,*fi); - } - } - return count_fd; - } - - static int RemoveDegenerateEdge(MeshType& m) - { - int count_ed = 0; - - for(EdgeIterator ei=m.edge.begin(); ei!=m.edge.end();++ei) - if(!(*ei).IsD()) - { - if((*ei).V(0) == (*ei).V(1) ) - { - count_ed++; - Allocator::DeleteEdge(m,*ei); - } - } - return count_ed; - } - - static int RemoveNonManifoldVertex(MeshType& m) - { - /*int count_vd = */ - CountNonManifoldVertexFF(m,true); - /*int count_fd = */ - tri::UpdateSelection::FaceFromVertexLoose(m); - int count_removed = 0; - FaceIterator fi; - for(fi=m.face.begin(); fi!=m.face.end();++fi) - if(!(*fi).IsD() && (*fi).IsS()) - Allocator::DeleteFace(m,*fi); - VertexIterator vi; - for(vi=m.vert.begin(); vi!=m.vert.end();++vi) - if(!(*vi).IsD() && (*vi).IsS()) { - ++count_removed; - Allocator::DeleteVertex(m,*vi); - } - return count_removed; - } - - static int SplitSelectedVertexOnEdgeMesh(MeshType& m) - { - tri::RequireCompactness(m); - tri::UpdateFlags::VertexClearV(m); - for(size_t i=0;iIsS()) - { - if(!vp->IsV()) - m.edge[i].V(j) = &*(tri::Allocator::AddVertex(m,vp->P())); - else vp->SetV(); - } - } - } - } - - - static void SelectNonManifoldVertexOnEdgeMesh(MeshType &m) - { - tri::RequireCompactness(m); - tri::UpdateSelection::VertexClear(m); - std::vector cnt(m.vn,0); - - for(size_t i=0;i2) m.vert[i].SetS(); - } - - static void SelectCreaseVertexOnEdgeMesh(MeshType &m, ScalarType AngleRadThr) - { - tri::RequireCompactness(m); - tri::RequireVEAdjacency(m); - tri::UpdateTopology::VertexEdge(m); - for(size_t i=0;i VVStarVec; - edge::VVStarVE(&(m.vert[i]),VVStarVec); - if(VVStarVec.size()==2) - { - CoordType v0 = m.vert[i].P() - VVStarVec[0]->P(); - CoordType v1 = m.vert[i].P() - VVStarVec[1]->P(); - float angle = M_PI-vcg::Angle(v0,v1); - if(angle > AngleRadThr) m.vert[i].SetS(); - } - } - } - - - /// Removal of faces that were incident on a non manifold edge. - - // Given a mesh with FF adjacency - // it search for non manifold vertices and duplicate them. - // Duplicated vertices are moved apart according to the move threshold param. - // that is a percentage of the average vector from the non manifold vertex to the barycenter of the incident faces. - - static int SplitNonManifoldVertex(MeshType& m, ScalarType moveThreshold) - { - RequireFFAdjacency(m); - typedef std::pair FaceInt; // a face and the index of the vertex that we have to change - // - std::vector > >ToSplitVec; - - SelectionStack ss(m); - ss.push(); - CountNonManifoldVertexFF(m,true); - UpdateFlags::VertexClearV(m); - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + if((*fi).V(0) == (*fi).V(1) || + (*fi).V(0) == (*fi).V(2) || + (*fi).V(1) == (*fi).V(2) ) { - for(int i=0;i<3;i++) - if((*fi).V(i)->IsS() && !(*fi).V(i)->IsV()) - { - (*fi).V(i)->SetV(); - face::Pos startPos(&*fi,i); - face::Pos curPos = startPos; - std::set faceSet; - do - { - faceSet.insert(std::make_pair(curPos.F(),curPos.VInd())); - curPos.NextE(); - } while (curPos != startPos); - - ToSplitVec.push_back(make_pair((*fi).V(i),std::vector())); - - typename std::set::const_iterator iii; - - for(iii=faceSet.begin();iii!=faceSet.end();++iii) - ToSplitVec.back().second.push_back(*iii); - } + count_fd++; + Allocator::DeleteFace(m,*fi); } - ss.pop(); - // Second step actually add new vertices and split them. - typename tri::Allocator::template PointerUpdater pu; - VertexIterator firstVp = tri::Allocator::AddVertices(m,ToSplitVec.size(),pu); - for(size_t i =0;iImportData(*np); - // loop on the face to be changed, and also compute the movement vector; - CoordType delta(0,0,0); - for(size_t j=0;j::DeleteEdge(m,*ei); + } + } + return count_ed; + } + + static int RemoveNonManifoldVertex(MeshType& m) + { + CountNonManifoldVertexFF(m,true); + tri::UpdateSelection::FaceFromVertexLoose(m); + int count_removed = 0; + FaceIterator fi; + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD() && (*fi).IsS()) + Allocator::DeleteFace(m,*fi); + VertexIterator vi; + for(vi=m.vert.begin(); vi!=m.vert.end();++vi) + if(!(*vi).IsD() && (*vi).IsS()) { + ++count_removed; + Allocator::DeleteVertex(m,*vi); + } + return count_removed; + } + + static int SplitSelectedVertexOnEdgeMesh(MeshType& m) + { + tri::RequireCompactness(m); + tri::UpdateFlags::VertexClearV(m); + for(size_t i=0;iIsS()) + { + if(!vp->IsV()) + m.edge[i].V(j) = &*(tri::Allocator::AddVertex(m,vp->P())); + else vp->SetV(); + } + } + } + } + + + static void SelectNonManifoldVertexOnEdgeMesh(MeshType &m) + { + tri::RequireCompactness(m); + tri::UpdateSelection::VertexClear(m); + std::vector cnt(m.vn,0); + + for(size_t i=0;i2) m.vert[i].SetS(); + } + + static void SelectCreaseVertexOnEdgeMesh(MeshType &m, ScalarType AngleRadThr) + { + tri::RequireCompactness(m); + tri::RequireVEAdjacency(m); + tri::UpdateTopology::VertexEdge(m); + for(size_t i=0;i VVStarVec; + edge::VVStarVE(&(m.vert[i]),VVStarVec); + if(VVStarVec.size()==2) + { + CoordType v0 = m.vert[i].P() - VVStarVec[0]->P(); + CoordType v1 = m.vert[i].P() - VVStarVec[1]->P(); + float angle = M_PI-vcg::Angle(v0,v1); + if(angle > AngleRadThr) m.vert[i].SetS(); + } + } + } + + + /// Removal of faces that were incident on a non manifold edge. + + // Given a mesh with FF adjacency + // it search for non manifold vertices and duplicate them. + // Duplicated vertices are moved apart according to the move threshold param. + // that is a percentage of the average vector from the non manifold vertex to the barycenter of the incident faces. + + static int SplitNonManifoldVertex(MeshType& m, ScalarType moveThreshold) + { + RequireFFAdjacency(m); + typedef std::pair FaceInt; // a face and the index of the vertex that we have to change + // + std::vector > >ToSplitVec; + + SelectionStack ss(m); + ss.push(); + CountNonManifoldVertexFF(m,true); + UpdateFlags::VertexClearV(m); + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + for(int i=0;i<3;i++) + if((*fi).V(i)->IsS() && !(*fi).V(i)->IsV()) + { + (*fi).V(i)->SetV(); + face::Pos startPos(&*fi,i); + face::Pos curPos = startPos; + std::set faceSet; + do { - FaceInt ff=ToSplitVec[i].second[j]; - ff.first->V(ff.second)=&*firstVp; - delta+=Barycenter(*(ff.first))-np->cP(); - } - delta /= ToSplitVec[i].second.size(); - firstVp->P() = firstVp->P() + delta * moveThreshold; - firstVp++; - } + faceSet.insert(std::make_pair(curPos.F(),curPos.VInd())); + curPos.NextE(); + } while (curPos != startPos); - return ToSplitVec.size(); + ToSplitVec.push_back(make_pair((*fi).V(i),std::vector())); + + typename std::set::const_iterator iii; + + for(iii=faceSet.begin();iii!=faceSet.end();++iii) + ToSplitVec.back().second.push_back(*iii); + } + } + ss.pop(); + // Second step actually add new vertices and split them. + typename tri::Allocator::template PointerUpdater pu; + VertexIterator firstVp = tri::Allocator::AddVertices(m,ToSplitVec.size(),pu); + for(size_t i =0;iImportData(*np); + // loop on the face to be changed, and also compute the movement vector; + CoordType delta(0,0,0); + for(size_t j=0;jV(ff.second)=&*firstVp; + delta+=Barycenter(*(ff.first))-np->cP(); + } + delta /= ToSplitVec[i].second.size(); + firstVp->P() = firstVp->P() + delta * moveThreshold; + firstVp++; + } + + return ToSplitVec.size(); + } + + + // Auxiliary function for sorting the non manifold faces according to their area. Used in RemoveNonManifoldFace + struct CompareAreaFP { + bool operator ()(FacePointer const& f1, FacePointer const& f2) const { + return DoubleArea(*f1) < DoubleArea(*f2); + } + }; + + /// Removal of faces that were incident on a non manifold edge. + static int RemoveNonManifoldFace(MeshType& m) + { + FaceIterator fi; + int count_fd = 0; + std::vector ToDelVec; + + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if (!fi->IsD()) + { + if ((!IsManifold(*fi,0))|| + (!IsManifold(*fi,1))|| + (!IsManifold(*fi,2))) + ToDelVec.push_back(&*fi); } + std::sort(ToDelVec.begin(),ToDelVec.end(),CompareAreaFP()); - // Auxiliary function for sorting the non manifold faces according to their area. Used in RemoveNonManifoldFace - struct CompareAreaFP { - bool operator ()(FacePointer const& f1, FacePointer const& f2) const { - return DoubleArea(*f1) < DoubleArea(*f2); - } - }; - - /// Removal of faces that were incident on a non manifold edge. - static int RemoveNonManifoldFace(MeshType& m) + for(size_t i=0;iIsD()) { - FaceIterator fi; - int count_fd = 0; - std::vector ToDelVec; + FaceType &ff= *ToDelVec[i]; + if ((!IsManifold(ff,0))|| + (!IsManifold(ff,1))|| + (!IsManifold(ff,2))) + { + for(int j=0;j<3;++j) + if(!face::IsBorder(ff,j)) + vcg::face::FFDetach(ff,j); - for(fi=m.face.begin(); fi!=m.face.end();++fi) - if (!fi->IsD()) - { - if ((!IsManifold(*fi,0))|| - (!IsManifold(*fi,1))|| - (!IsManifold(*fi,2))) - ToDelVec.push_back(&*fi); - } + Allocator::DeleteFace(m,ff); + count_fd++; + } + } + } + return count_fd; + } - std::sort(ToDelVec.begin(),ToDelVec.end(),CompareAreaFP()); - - for(size_t i=0;iIsD()) - { - FaceType &ff= *ToDelVec[i]; - if ((!IsManifold(ff,0))|| - (!IsManifold(ff,1))|| - (!IsManifold(ff,2))) - { - for(int j=0;j<3;++j) - if(!face::IsBorder(ff,j)) - vcg::face::FFDetach(ff,j); - - Allocator::DeleteFace(m,ff); - count_fd++; - } - } - } - return count_fd; - } - - /* + /* The following functions remove faces that are geometrically "bad" according to edges and area criteria. They remove the faces that are out of a given range of area or edges (e.g. faces too large or too small, or with edges too short or too long) but that could be topologically correct. These functions can optionally take into account only the selected faces. */ - template - static int RemoveFaceOutOfRangeAreaSel(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) - { - FaceIterator fi; - int count_fd = 0; - MinAreaThr*=2; - MaxAreaThr*=2; - for(fi=m.face.begin(); fi!=m.face.end();++fi) - if(!(*fi).IsD()) - if(!Selected || (*fi).IsS()) - { - const ScalarType doubleArea=DoubleArea(*fi); - if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) ) - { - Allocator::DeleteFace(m,*fi); - count_fd++; - } - } - return count_fd; - } + template + static int RemoveFaceOutOfRangeAreaSel(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) + { + FaceIterator fi; + int count_fd = 0; + MinAreaThr*=2; + MaxAreaThr*=2; + for(fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD()) + if(!Selected || (*fi).IsS()) + { + const ScalarType doubleArea=DoubleArea(*fi); + if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) ) + { + Allocator::DeleteFace(m,*fi); + count_fd++; + } + } + return count_fd; + } - // alias for the old style. Kept for backward compatibility - static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m);} + // alias for the old style. Kept for backward compatibility + static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m);} - // Aliases for the functions that do not look at selection - static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) - { - return RemoveFaceOutOfRangeAreaSel(m,MinAreaThr,MaxAreaThr); - } + // Aliases for the functions that do not look at selection + static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)()) + { + return RemoveFaceOutOfRangeAreaSel(m,MinAreaThr,MaxAreaThr); + } - /** + /** * Is the mesh only composed by quadrilaterals? */ - static bool IsBitQuadOnly(const MeshType &m) + static bool IsBitQuadOnly(const MeshType &m) + { + typedef typename MeshType::FaceType F; + tri::RequirePerFaceFlags(m); + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { + unsigned int tmp = fi->Flags()&(F::FAUX0|F::FAUX1|F::FAUX2); + if ( tmp != F::FAUX0 && tmp != F::FAUX1 && tmp != F::FAUX2) return false; + } + return true; + } + + + static bool IsFaceFauxConsistent(MeshType &m) + { + RequirePerFaceFlags(m); + RequireFFAdjacency(m); + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + for(int z=0;z<(*fi).VN();++z) { - typedef typename MeshType::FaceType F; - tri::RequirePerFaceFlags(m); - for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { - unsigned int tmp = fi->Flags()&(F::FAUX0|F::FAUX1|F::FAUX2); - if ( tmp != F::FAUX0 && tmp != F::FAUX1 && tmp != F::FAUX2) return false; - } - return true; + FacePointer fp = fi->FFp(z); + int zp = fi->FFi(z); + if(fi->IsF(z) != fp->IsF(zp)) return false; } - - - static bool IsFaceFauxConsistent(MeshType &m) - { - RequirePerFaceFlags(m); - RequireFFAdjacency(m); - for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) - { - for(int z=0;z<(*fi).VN();++z) - { - FacePointer fp = fi->FFp(z); - int zp = fi->FFi(z); - if(fi->IsF(z) != fp->IsF(zp)) return false; - } - } - return true; - } + } + return true; + } /** * Is the mesh only composed by triangles? (non polygonal faces) @@ -876,139 +870,139 @@ public: return nonManifoldCnt; } - /** + /** * Count the number of non manifold edges in a mesh, e.g. the edges where there are more than 2 incident faces. * * Note that this test is not enough to say that a mesh is two manifold, * you have to count also the non manifold vertexes. */ - static int CountNonManifoldEdgeFF( MeshType & m, bool SelectFlag=false) + static int CountNonManifoldEdgeFF( MeshType & m, bool SelectFlag=false) + { + RequireFFAdjacency(m); + int nmfBit[3]; + nmfBit[0]= FaceType::NewBitFlag(); + nmfBit[1]= FaceType::NewBitFlag(); + nmfBit[2]= FaceType::NewBitFlag(); + + + UpdateFlags::FaceClear(m,nmfBit[0]+nmfBit[1]+nmfBit[2]); + + if(SelectFlag){ + UpdateSelection::VertexClear(m); + UpdateSelection::FaceClear(m); + } + + int edgeCnt = 0; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if (!fi->IsD()) { - RequireFFAdjacency(m); - int nmfBit[3]; - nmfBit[0]= FaceType::NewBitFlag(); - nmfBit[1]= FaceType::NewBitFlag(); - nmfBit[2]= FaceType::NewBitFlag(); - - - UpdateFlags::FaceClear(m,nmfBit[0]+nmfBit[1]+nmfBit[2]); - - if(SelectFlag){ - UpdateSelection::VertexClear(m); - UpdateSelection::FaceClear(m); - } - - int edgeCnt = 0; - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - { - if (!fi->IsD()) + for(int i=0;i<3;++i) + if(!IsManifold(*fi,i)) { - for(int i=0;i<3;++i) - if(!IsManifold(*fi,i)) + if(!(*fi).IsUserBit(nmfBit[i])) + { + ++edgeCnt; + if(SelectFlag) { - if(!(*fi).IsUserBit(nmfBit[i])) - { - ++edgeCnt; - if(SelectFlag) - { - (*fi).V0(i)->SetS(); - (*fi).V1(i)->SetS(); - } - // follow the ring of faces incident on edge i; - face::Pos nmf(&*fi,i); - do - { - if(SelectFlag) nmf.F()->SetS(); - nmf.F()->SetUserBit(nmfBit[nmf.E()]); - nmf.NextF(); - } - while(nmf.f != &*fi); - } + (*fi).V0(i)->SetS(); + (*fi).V1(i)->SetS(); } + // follow the ring of faces incident on edge i; + face::Pos nmf(&*fi,i); + do + { + if(SelectFlag) nmf.F()->SetS(); + nmf.F()->SetUserBit(nmfBit[nmf.E()]); + nmf.NextF(); + } + while(nmf.f != &*fi); } } - return edgeCnt; } + } + return edgeCnt; + } - /** Count (and eventually select) non 2-Manifold vertexes of a mesh + /** Count (and eventually select) non 2-Manifold vertexes of a mesh * e.g. the vertices with a non 2-manif. neighbourhood but that do not belong to not 2-manif edges. * typical situation two cones connected by one vertex. */ - static int CountNonManifoldVertexFF( MeshType & m, bool selectVert = true ) - { - RequireFFAdjacency(m); - if(selectVert) UpdateSelection::VertexClear(m); + static int CountNonManifoldVertexFF( MeshType & m, bool selectVert = true ) + { + RequireFFAdjacency(m); + if(selectVert) UpdateSelection::VertexClear(m); - int nonManifoldCnt=0; - SimpleTempData TD(m.vert,0); + int nonManifoldCnt=0; + SimpleTempData TD(m.vert,0); - // First Loop, just count how many faces are incident on a vertex and store it in the TemporaryData Counter. - FaceIterator fi; - for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) - { - TD[(*fi).V(0)]++; - TD[(*fi).V(1)]++; - TD[(*fi).V(2)]++; - } + // First Loop, just count how many faces are incident on a vertex and store it in the TemporaryData Counter. + FaceIterator fi; + for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + TD[(*fi).V(0)]++; + TD[(*fi).V(1)]++; + TD[(*fi).V(2)]++; + } - tri::UpdateFlags::VertexClearV(m); - // Second Loop. - // mark out of the game the vertexes that are incident on non manifold edges. - for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) - { - for(int i=0;i<3;++i) - if (!IsManifold(*fi,i)) { - (*fi).V0(i)->SetV(); - (*fi).V1(i)->SetV(); - } - } - // Third Loop, for safe vertexes, check that the number of faces that you can reach starting - // from it and using FF is the same of the previously counted. - for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) - { - for(int i=0;i<3;i++) if(!(*fi).V(i)->IsV()){ - (*fi).V(i)->SetV(); - face::Pos pos(&(*fi),i); + tri::UpdateFlags::VertexClearV(m); + // Second Loop. + // mark out of the game the vertexes that are incident on non manifold edges. + for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + for(int i=0;i<3;++i) + if (!IsManifold(*fi,i)) { + (*fi).V0(i)->SetV(); + (*fi).V1(i)->SetV(); + } + } + // Third Loop, for safe vertexes, check that the number of faces that you can reach starting + // from it and using FF is the same of the previously counted. + for (fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) + { + for(int i=0;i<3;i++) if(!(*fi).V(i)->IsV()){ + (*fi).V(i)->SetV(); + face::Pos pos(&(*fi),i); - int starSizeFF = pos.NumberOfIncidentFaces(); + int starSizeFF = pos.NumberOfIncidentFaces(); - if (starSizeFF != TD[(*fi).V(i)]) - { - if(selectVert) (*fi).V(i)->SetS(); - nonManifoldCnt++; - } - } - } - return nonManifoldCnt; - } - - static void CountEdgeNum( MeshType & m, int &total_e, int &boundary_e, int &non_manif_e ) - { - std::vector< typename tri::UpdateTopology::PEdge > edgeVec; - tri::UpdateTopology::FillEdgeVector(m,edgeVec,true); - sort(edgeVec.begin(), edgeVec.end()); // Lo ordino per vertici - total_e=0; - boundary_e=0; - non_manif_e=0; - - size_t f_on_cur_edge =1; - for(size_t i=0;i2) - ++non_manif_e; - f_on_cur_edge=1; - } - else - { - ++f_on_cur_edge; - } - } // end for + if(selectVert) (*fi).V(i)->SetS(); + nonManifoldCnt++; + } } + } + return nonManifoldCnt; + } + + static void CountEdgeNum( MeshType & m, int &total_e, int &boundary_e, int &non_manif_e ) + { + std::vector< typename tri::UpdateTopology::PEdge > edgeVec; + tri::UpdateTopology::FillEdgeVector(m,edgeVec,true); + sort(edgeVec.begin(), edgeVec.end()); // Lo ordino per vertici + total_e=0; + boundary_e=0; + non_manif_e=0; + + size_t f_on_cur_edge =1; + for(size_t i=0;i2) + ++non_manif_e; + f_on_cur_edge=1; + } + else + { + ++f_on_cur_edge; + } + } // end for + } @@ -1021,7 +1015,7 @@ public: vcg::face::Pos he; vcg::face::Pos hei; - std::vector< std::vector > holes; //indices of vertices + std::vector< std::vector > holes; //indices of vertices vcg::tri::UpdateFlags::VertexClearS(m); @@ -1036,7 +1030,7 @@ public: if(face::IsBorder(*fi,j))//found an unvisited border edge { he.Set(&(*fi),j,fi->V(j)); //set the face-face iterator to the current face, edge and vertex - std::vector hole; //start of a new hole + std::vector hole; //start of a new hole hole.push_back(fi->P(j)); // including the first vertex numholev++; he.v->SetS(); //set the current vertex as selected @@ -1045,11 +1039,11 @@ public: while(fi->V(j) != he.v)//will we do not encounter the first boundary edge. { - Point3x newpoint = he.v->P(); //select its vertex. + CoordType newpoint = he.v->P(); //select its vertex. if(he.v->IsS())//check if this vertex was selected already, because then we have an additional hole. { //cut and paste the additional hole. - std::vector hole2; + std::vector hole2; int index = static_cast(find(hole.begin(),hole.end(),newpoint) - hole.begin()); for(unsigned int i=index; i(holes.size()); } - /* + /* Compute the set of connected components of a given mesh it fills a vector of pair < int , faceptr > with, for each connecteed component its size and a represnant */ - static int CountConnectedComponents(MeshType &m) - { - std::vector< std::pair > CCV; - return ConnectedComponents(m,CCV); - } + static int CountConnectedComponents(MeshType &m) + { + std::vector< std::pair > CCV; + return ConnectedComponents(m,CCV); + } - static int ConnectedComponents(MeshType &m, std::vector< std::pair > &CCV) + static int ConnectedComponents(MeshType &m, std::vector< std::pair > &CCV) + { + tri::RequireFFAdjacency(m); + CCV.clear(); + tri::UpdateSelection::FaceClear(m); + std::stack sf; + FacePointer fpt=&*(m.face.begin()); + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + if(!((*fi).IsD()) && !(*fi).IsS()) { - tri::RequireFFAdjacency(m); - CCV.clear(); - tri::UpdateSelection::FaceClear(m); - std::stack sf; - FacePointer fpt=&*(m.face.begin()); - for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + (*fi).SetS(); + CCV.push_back(std::make_pair(0,&*fi)); + sf.push(&*fi); + while (!sf.empty()) { - if(!((*fi).IsD()) && !(*fi).IsS()) + fpt=sf.top(); + ++CCV.back().first; + sf.pop(); + for(int j=0;j<3;++j) { - (*fi).SetS(); - CCV.push_back(std::make_pair(0,&*fi)); - sf.push(&*fi); - while (!sf.empty()) + if( !face::IsBorder(*fpt,j) ) { - fpt=sf.top(); - ++CCV.back().first; - sf.pop(); - for(int j=0;j<3;++j) + FacePointer l = fpt->FFp(j); + if( !(*l).IsS() ) { - if( !face::IsBorder(*fpt,j) ) - { - FacePointer l = fpt->FFp(j); - if( !(*l).IsS() ) - { - (*l).SetS(); - sf.push(l); - } - } + (*l).SetS(); + sf.push(l); } } } } - return int(CCV.size()); } + } + return int(CCV.size()); + } - /** + /** GENUS. A topologically invariant property of a surface defined as @@ -1154,24 +1148,24 @@ public: */ - static int MeshGenus(int nvert,int nedges,int nfaces, int numholes, int numcomponents) - { - return -((nvert + nfaces - nedges + numholes - 2 * numcomponents) / 2); - } + static int MeshGenus(int nvert,int nedges,int nfaces, int numholes, int numcomponents) + { + return -((nvert + nfaces - nedges + numholes - 2 * numcomponents) / 2); + } - static int MeshGenus(MeshType &m) - { - int nvert=m.vn; - int nfaces=m.fn; - int boundary_e,total_e,nonmanif_e; - CountEdgeNum(m,total_e,boundary_e,nonmanif_e); - int numholes=CountHoles(m); - int numcomponents=CountConnectedComponents(m); - int G=MeshGenus(nvert,total_e,nfaces,numholes,numcomponents); - return G; - } + static int MeshGenus(MeshType &m) + { + int nvert=m.vn; + int nfaces=m.fn; + int boundary_e,total_e,nonmanif_e; + CountEdgeNum(m,total_e,boundary_e,nonmanif_e); + int numholes=CountHoles(m); + int numcomponents=CountConnectedComponents(m); + int G=MeshGenus(nvert,total_e,nfaces,numholes,numcomponents); + return G; + } - /** + /** * Check if the given mesh is regular, semi-regular or irregular. * * Each vertex of a \em regular mesh has valence 6 except for border vertices @@ -1182,51 +1176,51 @@ public: * * All other meshes are \em irregular. */ - static void IsRegularMesh(MeshType &m, bool &Regular, bool &Semiregular) - { - RequireVFAdjacency(m); - Regular = true; + static void IsRegularMesh(MeshType &m, bool &Regular, bool &Semiregular) + { + RequireVFAdjacency(m); + Regular = true; - VertexIterator vi; + VertexIterator vi; - // for each vertex the number of edges are count - for (vi = m.vert.begin(); vi != m.vert.end(); ++vi) - { - if (!vi->IsD()) - { - face::Pos he((*vi).VFp(), &*vi); - face::Pos ht = he; + // for each vertex the number of edges are count + for (vi = m.vert.begin(); vi != m.vert.end(); ++vi) + { + if (!vi->IsD()) + { + face::Pos he((*vi).VFp(), &*vi); + face::Pos ht = he; - int n=0; - bool border=false; - do - { - ++n; - ht.NextE(); - if (ht.IsBorder()) - border=true; - } - while (ht != he); + int n=0; + bool border=false; + do + { + ++n; + ht.NextE(); + if (ht.IsBorder()) + border=true; + } + while (ht != he); - if (border) - n = n/2; + if (border) + n = n/2; - if ((n != 6)&&(!border && n != 4)) - { - Regular = false; - break; - } - } - } + if ((n != 6)&&(!border && n != 4)) + { + Regular = false; + break; + } + } + } - if (!Regular) - Semiregular = false; - else - { - // For now we do not account for semi-regularity - Semiregular = false; - } - } + if (!Regular) + Semiregular = false; + else + { + // For now we do not account for semi-regularity + Semiregular = false; + } + } static bool IsCoherentlyOrientedMesh(MeshType &m) @@ -1308,319 +1302,324 @@ public: /// Flip the orientation of the whole mesh flipping all the faces (by swapping the first two vertices) - static void FlipMesh(MeshType &m, bool selected=false) + static void FlipMesh(MeshType &m, bool selected=false) + { + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) + if(!selected || (*fi).IsS()) { - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if(!(*fi).IsD()) - if(!selected || (*fi).IsS()) - { - face::SwapEdge((*fi), 0); - if (HasPerWedgeTexCoord(m)) - std::swap((*fi).WT(0),(*fi).WT(1)); - } + face::SwapEdge((*fi), 0); + if (HasPerWedgeTexCoord(m)) + std::swap((*fi).WT(0),(*fi).WT(1)); } - /// Flip a mesh so that its normals are orented outside. - /// Just for safety it uses a voting scheme. - /// It assumes that - /// mesh has already has coherent normals. - /// mesh is watertight and signle component. - static bool FlipNormalOutside(MeshType &m) + } + /// Flip a mesh so that its normals are orented outside. + /// Just for safety it uses a voting scheme. + /// It assumes that + /// mesh has already has coherent normals. + /// mesh is watertight and signle component. + static bool FlipNormalOutside(MeshType &m) + { + if(m.vert.empty()) return false; + + tri::UpdateNormal::PerVertexAngleWeighted(m); + tri::UpdateNormal::NormalizePerVertex(m); + + std::vector< VertexPointer > minVertVec; + std::vector< VertexPointer > maxVertVec; + + // The set of directions to be choosen + std::vector< CoordType > dirVec; + dirVec.push_back(CoordType(1,0,0)); + dirVec.push_back(CoordType(0,1,0)); + dirVec.push_back(CoordType(0,0,1)); + dirVec.push_back(CoordType( 1, 1,1)); + dirVec.push_back(CoordType(-1, 1,1)); + dirVec.push_back(CoordType(-1,-1,1)); + dirVec.push_back(CoordType( 1,-1,1)); + for(size_t i=0;iP().dot(dirVec[i])) minVertVec[i] = &*vi; + if( (*vi).cP().dot(dirVec[i]) > maxVertVec[i]->P().dot(dirVec[i])) maxVertVec[i] = &*vi; + } + } - tri::UpdateNormal::PerVertexAngleWeighted(m); - tri::UpdateNormal::NormalizePerVertex(m); + int voteCount=0; + ScalarType angleThreshold = cos(math::ToRad(85.0)); + for(size_t i=0;iP()[0],minVertVec[i]->P()[1],minVertVec[i]->P()[2]); + // qDebug("Max vert along (%f %f %f) is %f %f %f",dirVec[i][0],dirVec[i][1],dirVec[i][2],maxVertVec[i]->P()[0],maxVertVec[i]->P()[1],maxVertVec[i]->P()[2]); + if(minVertVec[i]->N().dot(dirVec[i]) > angleThreshold ) voteCount++; + if(maxVertVec[i]->N().dot(dirVec[i]) < -angleThreshold ) voteCount++; + } + // qDebug("votecount = %i",voteCount); + if(voteCount < int(dirVec.size())/2) return false; + FlipMesh(m); + return true; + } - std::vector< VertexPointer > minVertVec; - std::vector< VertexPointer > maxVertVec; + // Search and remove small single triangle folds + // - a face has normal opposite to all other faces + // - choose the edge that brings to the face f1 containing the vertex opposite to that edge. + static int RemoveFaceFoldByFlip(MeshType &m, float normalThresholdDeg=175, bool repeat=true) + { + RequireFFAdjacency(m); + RequirePerVertexMark(m); + //Counters for logging and convergence + int count, total = 0; - // The set of directions to be choosen - std::vector< Point3x > dirVec; - dirVec.push_back(Point3x(1,0,0)); - dirVec.push_back(Point3x(0,1,0)); - dirVec.push_back(Point3x(0,0,1)); - dirVec.push_back(Point3x( 1, 1,1)); - dirVec.push_back(Point3x(-1, 1,1)); - dirVec.push_back(Point3x(-1,-1,1)); - dirVec.push_back(Point3x( 1,-1,1)); - for(size_t i=0;i::FaceFace(m); + tri::UnMarkAll(m); + count = 0; + + ScalarType NormalThrRad = math::ToRad(normalThresholdDeg); + ScalarType eps = 0.0001; // this epsilon value is in absolute value. It is a distance from edge in baricentric coords. + //detection stage + for(FaceIterator fi=m.face.begin();fi!= m.face.end();++fi ) if(!(*fi).IsV()) + { Point3 NN = vcg::NormalizedNormal((*fi)); + if( vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(0))) > NormalThrRad && + vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(1))) > NormalThrRad && + vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(2))) > NormalThrRad ) { - Normalize(dirVec[i]); - minVertVec.push_back(&*m.vert.begin()); - maxVertVec.push_back(&*m.vert.begin()); - } - for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if(!(*vi).IsD()) - { - for(size_t i=0;iP().dot(dirVec[i])) minVertVec[i] = &*vi; - if( (*vi).cP().dot(dirVec[i]) > maxVertVec[i]->P().dot(dirVec[i])) maxVertVec[i] = &*vi; - } - } - - int voteCount=0; - ScalarType angleThreshold = cos(math::ToRad(85.0)); - for(size_t i=0;iP()[0],minVertVec[i]->P()[1],minVertVec[i]->P()[2]); -// qDebug("Max vert along (%f %f %f) is %f %f %f",dirVec[i][0],dirVec[i][1],dirVec[i][2],maxVertVec[i]->P()[0],maxVertVec[i]->P()[1],maxVertVec[i]->P()[2]); - if(minVertVec[i]->N().dot(dirVec[i]) > angleThreshold ) voteCount++; - if(maxVertVec[i]->N().dot(dirVec[i]) < -angleThreshold ) voteCount++; - } -// qDebug("votecount = %i",voteCount); - if(voteCount < int(dirVec.size())/2) return false; - FlipMesh(m); - return true; - } - - // Search and remove small single triangle folds - // - a face has normal opposite to all other faces - // - choose the edge that brings to the face f1 containing the vertex opposite to that edge. - static int RemoveFaceFoldByFlip(MeshType &m, float normalThresholdDeg=175, bool repeat=true) - { - RequireFFAdjacency(m); - RequirePerVertexMark(m); - //Counters for logging and convergence - int count, total = 0; - - do { - tri::UpdateTopology::FaceFace(m); - tri::UnMarkAll(m); - count = 0; - - ScalarType NormalThrRad = math::ToRad(normalThresholdDeg); - ScalarType eps = 0.0001; // this epsilon value is in absolute value. It is a distance from edge in baricentric coords. - //detection stage - for(FaceIterator fi=m.face.begin();fi!= m.face.end();++fi ) if(!(*fi).IsV()) - { Point3 NN = vcg::NormalizedNormal((*fi)); - if( vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(0))) > NormalThrRad && - vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(1))) > NormalThrRad && - vcg::Angle(NN,vcg::NormalizedNormal(*(*fi).FFp(2))) > NormalThrRad ) - { - (*fi).SetS(); - //(*fi).C()=Color4b(Color4b::Red); - // now search the best edge to flip - for(int i=0;i<3;i++) - { - Point3 &p=(*fi).P2(i); - Point3 L; - bool ret = vcg::InterpolationParameters((*(*fi).FFp(i)),vcg::Normal(*(*fi).FFp(i)),p,L); - if(ret && L[0]>eps && L[1]>eps && L[2]>eps) - { - (*fi).FFp(i)->SetS(); - (*fi).FFp(i)->SetV(); - //(*fi).FFp(i)->C()=Color4b(Color4b::Green); - if(face::CheckFlipEdge( *fi, i )) { - face::FlipEdge( *fi, i ); - ++count; ++total; - } - } - } - } - } - - // tri::UpdateNormal::PerFace(m); - } - while( repeat && count ); - return total; - } - - - static int RemoveTVertexByFlip(MeshType &m, float threshold=40, bool repeat=true) - { - RequireFFAdjacency(m); - RequirePerVertexMark(m); - //Counters for logging and convergence - int count, total = 0; - - do { - tri::UpdateTopology::FaceFace(m); - tri::UnMarkAll(m); - count = 0; - - //detection stage - for(unsigned int index = 0 ; index < m.face.size(); ++index ) + Point3 &p=(*fi).P2(i); + Point3 L; + bool ret = vcg::InterpolationParameters((*(*fi).FFp(i)),vcg::Normal(*(*fi).FFp(i)),p,L); + if(ret && L[0]>eps && L[1]>eps && L[2]>eps) { - FacePointer f = &(m.face[index]); float sides[3]; CoordType dummy; - sides[0] = Distance(f->P(0), f->P(1)); - sides[1] = Distance(f->P(1), f->P(2)); - sides[2] = Distance(f->P(2), f->P(0)); - // Find largest triangle side - int i = std::find(sides, sides+3, std::max( std::max(sides[0],sides[1]), sides[2])) - (sides); - if( tri::IsMarked(m,f->V2(i) )) continue; - - if( PSDist(f->P2(i),f->P(i),f->P1(i),dummy)*threshold <= sides[i] ) - { - tri::Mark(m,f->V2(i)); - if(face::CheckFlipEdge( *f, i )) { - // Check if EdgeFlipping improves quality - FacePointer g = f->FFp(i); int k = f->FFi(i); - Triangle3 t1(f->P(i), f->P1(i), f->P2(i)), t2(g->P(k), g->P1(k), g->P2(k)), - t3(f->P(i), g->P2(k), f->P2(i)), t4(g->P(k), f->P2(i), g->P2(k)); - - if ( std::min( QualityFace(t1), QualityFace(t2) ) < std::min( QualityFace(t3), QualityFace(t4) )) - { - face::FlipEdge( *f, i ); - ++count; ++total; - } - } - - } - } - - // tri::UpdateNormal::PerFace(m); - } - while( repeat && count ); - return total; - } - - static int RemoveTVertexByCollapse(MeshType &m, float threshold=40, bool repeat=true) - { - RequirePerVertexMark(m); - //Counters for logging and convergence - int count, total = 0; - - do { - tri::UnMarkAll(m); - count = 0; - - //detection stage - for(unsigned int index = 0 ; index < m.face.size(); ++index ) - { - FacePointer f = &(m.face[index]); - float sides[3]; - CoordType dummy; - - sides[0] = Distance(f->P(0), f->P(1)); - sides[1] = Distance(f->P(1), f->P(2)); - sides[2] = Distance(f->P(2), f->P(0)); - int i = std::find(sides, sides+3, std::max( std::max(sides[0],sides[1]), sides[2])) - (sides); - if( tri::IsMarked(m,f->V2(i) )) continue; - - if( PSDist(f->P2(i),f->P(i),f->P1(i),dummy)*threshold <= sides[i] ) - { - tri::Mark(m,f->V2(i)); - - int j = Distance(dummy,f->P(i))P1(i))?i:(i+1)%3; - f->P2(i) = f->P(j); tri::Mark(m,f->V(j)); - ++count; ++total; - } - } - - - tri::Clean::RemoveDuplicateVertex(m); - tri::Allocator::CompactFaceVector(m); - tri::Allocator::CompactVertexVector(m); - } - while( repeat && count ); - - return total; - } - - static bool SelfIntersections(MeshType &m, std::vector &ret) - { - RequirePerFaceMark(m); - ret.clear(); - int referredBit = FaceType::NewBitFlag(); - tri::UpdateFlags::FaceClear(m,referredBit); - - TriMeshGrid gM; - gM.Set(m.face.begin(),m.face.end()); - - for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) - { - (*fi).SetUserBit(referredBit); - Box3< ScalarType> bbox; - (*fi).GetBBox(bbox); - std::vector inBox; - vcg::tri::GetInBoxFace(m, gM, bbox,inBox); - bool Intersected=false; - typename std::vector::iterator fib; - for(fib=inBox.begin();fib!=inBox.end();++fib) - { - if(!(*fib)->IsUserBit(referredBit) && (*fib != &*fi) ) - if(Clean::TestFaceFaceIntersection(&*fi,*fib)){ - ret.push_back(*fib); - if(!Intersected) { - ret.push_back(&*fi); - Intersected=true; + (*fi).FFp(i)->SetS(); + (*fi).FFp(i)->SetV(); + //(*fi).FFp(i)->C()=Color4b(Color4b::Green); + if(face::CheckFlipEdge( *fi, i )) { + face::FlipEdge( *fi, i ); + ++count; ++total; } } + } } - inBox.clear(); } - FaceType::DeleteBitFlag(referredBit); - return (ret.size()>0); + // tri::UpdateNormal::PerFace(m); + } + while( repeat && count ); + return total; + } + + + static int RemoveTVertexByFlip(MeshType &m, float threshold=40, bool repeat=true) + { + RequireFFAdjacency(m); + RequirePerVertexMark(m); + //Counters for logging and convergence + int count, total = 0; + + do { + tri::UpdateTopology::FaceFace(m); + tri::UnMarkAll(m); + count = 0; + + //detection stage + for(unsigned int index = 0 ; index < m.face.size(); ++index ) + { + FacePointer f = &(m.face[index]); float sides[3]; CoordType dummy; + sides[0] = Distance(f->P(0), f->P(1)); + sides[1] = Distance(f->P(1), f->P(2)); + sides[2] = Distance(f->P(2), f->P(0)); + // Find largest triangle side + int i = std::find(sides, sides+3, std::max( std::max(sides[0],sides[1]), sides[2])) - (sides); + if( tri::IsMarked(m,f->V2(i) )) continue; + + if( PSDist(f->P2(i),f->P(i),f->P1(i),dummy)*threshold <= sides[i] ) + { + tri::Mark(m,f->V2(i)); + if(face::CheckFlipEdge( *f, i )) { + // Check if EdgeFlipping improves quality + FacePointer g = f->FFp(i); int k = f->FFi(i); + Triangle3 t1(f->P(i), f->P1(i), f->P2(i)), t2(g->P(k), g->P1(k), g->P2(k)), + t3(f->P(i), g->P2(k), f->P2(i)), t4(g->P(k), f->P2(i), g->P2(k)); + + if ( std::min( QualityFace(t1), QualityFace(t2) ) < std::min( QualityFace(t3), QualityFace(t4) )) + { + face::FlipEdge( *f, i ); + ++count; ++total; + } + } + + } + } + + // tri::UpdateNormal::PerFace(m); + } + while( repeat && count ); + return total; + } + + static int RemoveTVertexByCollapse(MeshType &m, float threshold=40, bool repeat=true) + { + RequirePerVertexMark(m); + //Counters for logging and convergence + int count, total = 0; + + do { + tri::UnMarkAll(m); + count = 0; + + //detection stage + for(unsigned int index = 0 ; index < m.face.size(); ++index ) + { + FacePointer f = &(m.face[index]); + float sides[3]; + CoordType dummy; + + sides[0] = Distance(f->P(0), f->P(1)); + sides[1] = Distance(f->P(1), f->P(2)); + sides[2] = Distance(f->P(2), f->P(0)); + int i = std::find(sides, sides+3, std::max( std::max(sides[0],sides[1]), sides[2])) - (sides); + if( tri::IsMarked(m,f->V2(i) )) continue; + + if( PSDist(f->P2(i),f->P(i),f->P1(i),dummy)*threshold <= sides[i] ) + { + tri::Mark(m,f->V2(i)); + + int j = Distance(dummy,f->P(i))P1(i))?i:(i+1)%3; + f->P2(i) = f->P(j); tri::Mark(m,f->V(j)); + ++count; ++total; + } + } + + + tri::Clean::RemoveDuplicateVertex(m); + tri::Allocator::CompactFaceVector(m); + tri::Allocator::CompactVertexVector(m); + } + while( repeat && count ); + + return total; + } + + static bool SelfIntersections(MeshType &m, std::vector &ret) + { + RequirePerFaceMark(m); + ret.clear(); + int referredBit = FaceType::NewBitFlag(); + tri::UpdateFlags::FaceClear(m,referredBit); + + TriMeshGrid gM; + gM.Set(m.face.begin(),m.face.end()); + + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD()) + { + (*fi).SetUserBit(referredBit); + Box3< ScalarType> bbox; + (*fi).GetBBox(bbox); + std::vector inBox; + vcg::tri::GetInBoxFace(m, gM, bbox,inBox); + bool Intersected=false; + typename std::vector::iterator fib; + for(fib=inBox.begin();fib!=inBox.end();++fib) + { + if(!(*fib)->IsUserBit(referredBit) && (*fib != &*fi) ) + if(Clean::TestFaceFaceIntersection(&*fi,*fib)){ + ret.push_back(*fib); + if(!Intersected) { + ret.push_back(&*fi); + Intersected=true; + } + } + } + inBox.clear(); } - /** + FaceType::DeleteBitFlag(referredBit); + return (ret.size()>0); + } + + /** This function simply test that the vn and fn counters be consistent with the size of the containers and the number of deleted simplexes. */ - static bool IsSizeConsistent(MeshType &m) - { - int DeletedVertexNum=0; - for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) - if((*vi).IsD()) DeletedVertexNum++; + static bool IsSizeConsistent(MeshType &m) + { + int DeletedVertNum=0; + for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if((*vi).IsD()) DeletedVertNum++; - int DeletedFaceNum=0; - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if((*fi).IsD()) DeletedFaceNum++; + int DeletedEdgeNum=0; + for (EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei) + if((*ei).IsD()) DeletedEdgeNum++; - if(size_t(m.vn+DeletedVertexNum) != m.vert.size()) return false; - if(size_t(m.fn+DeletedFaceNum) != m.face.size()) return false; + int DeletedFaceNum=0; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if((*fi).IsD()) DeletedFaceNum++; - return true; - } + if(size_t(m.vn+DeletedVertNum) != m.vert.size()) return false; + if(size_t(m.en+DeletedEdgeNum) != m.edge.size()) return false; + if(size_t(m.fn+DeletedFaceNum) != m.face.size()) return false; - /** + return true; + } + + /** This function simply test that all the faces have a consistent face-face topology relation. useful for checking that a topology modifying algorithm does not mess something. */ - static bool IsFFAdjacencyConsistent(MeshType &m) + static bool IsFFAdjacencyConsistent(MeshType &m) + { + RequireFFAdjacency(m); + + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) { - RequireFFAdjacency(m); - - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if(!(*fi).IsD()) - { - for(int i=0;i<3;++i) - if(!FFCorrectness(*fi, i)) return false; - } - return true; + for(int i=0;i<3;++i) + if(!FFCorrectness(*fi, i)) return false; } + return true; + } -/** + /** This function simply test that a mesh has some reasonable tex coord. */ - static bool HasConsistentPerWedgeTexCoord(MeshType &m) - { - tri::RequirePerFaceWedgeTexCoord(m); + static bool HasConsistentPerWedgeTexCoord(MeshType &m) + { + tri::RequirePerFaceWedgeTexCoord(m); - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if(!(*fi).IsD()) - { FaceType &f=(*fi); - if( ! ( (f.WT(0).N() == f.WT(1).N()) && (f.WT(0).N() == (*fi).WT(2).N()) ) ) - return false; // all the vertices must have the same index. + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { FaceType &f=(*fi); + if( ! ( (f.WT(0).N() == f.WT(1).N()) && (f.WT(0).N() == (*fi).WT(2).N()) ) ) + return false; // all the vertices must have the same index. - if((*fi).WT(0).N() <0) return false; // no undefined texture should be allowed - } - return true; + if((*fi).WT(0).N() <0) return false; // no undefined texture should be allowed } + return true; + } - /** + /** Simple check that there are no face with all collapsed tex coords. */ - static bool HasZeroTexCoordFace(MeshType &m) - { - tri::RequirePerFaceWedgeTexCoord(m); + static bool HasZeroTexCoordFace(MeshType &m) + { + tri::RequirePerFaceWedgeTexCoord(m); - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if(!(*fi).IsD()) - { - if( (*fi).WT(0).P() == (*fi).WT(1).P() && (*fi).WT(0).P() == (*fi).WT(2).P() ) return false; - } - return true; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + { + if( (*fi).WT(0).P() == (*fi).WT(1).P() && (*fi).WT(0).P() == (*fi).WT(2).P() ) return false; } + return true; + } /** @@ -1631,7 +1630,7 @@ public: if the faces share only a vertex, the opposite edge is tested against the face */ static bool TestFaceFaceIntersection(FaceType *f0,FaceType *f1) - { + { assert(f0!=f1); int sv = face::CountSharedVertex(f0,f1); if(sv==3) return true; @@ -1657,13 +1656,13 @@ public: return true; } - } - return false; } + return false; + } -/** + /** This function merge all the vertices that are closer than the given radius */ static int MergeCloseVertex(MeshType &m, const ScalarType radius) @@ -1711,94 +1710,94 @@ public: } -static std::pair RemoveSmallConnectedComponentsSize(MeshType &m, int maxCCSize) -{ - std::vector< std::pair > CCV; - int TotalCC=ConnectedComponents(m, CCV); - int DeletedCC=0; + static std::pair RemoveSmallConnectedComponentsSize(MeshType &m, int maxCCSize) + { + std::vector< std::pair > CCV; + int TotalCC=ConnectedComponents(m, CCV); + int DeletedCC=0; - ConnectedComponentIterator ci; - for(unsigned int i=0;i ci; + for(unsigned int i=0;i FPV; + if(CCV[i].first FPV; - if(CCV[i].first::iterator fpvi; - for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) - Allocator::DeleteFace(m,(**fpvi)); - } - } - return std::make_pair(TotalCC,DeletedCC); -} - - -/// Remove the connected components smaller than a given diameter -// it returns a pair with the number of connected components and the number of deleted ones. -static std::pair RemoveSmallConnectedComponentsDiameter(MeshType &m, ScalarType maxDiameter) -{ - std::vector< std::pair > CCV; - int TotalCC=ConnectedComponents(m, CCV); - int DeletedCC=0; - tri::ConnectedComponentIterator ci; - for(unsigned int i=0;i bb; - std::vector FPV; + DeletedCC++; for(ci.start(m,CCV[i].second);!ci.completed();++ci) - { - FPV.push_back(*ci); - bb.Add((*ci)->P(0)); - bb.Add((*ci)->P(1)); - bb.Add((*ci)->P(2)); - } - if(bb.Diag()::iterator fpvi; - for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) - tri::Allocator::DeleteFace(m,(**fpvi)); - } - } - return std::make_pair(TotalCC,DeletedCC); -} + FPV.push_back(*ci); -/// Remove the connected components greater than a given diameter -// it returns a pair with the number of connected components and the number of deleted ones. -static std::pair RemoveHugeConnectedComponentsDiameter(MeshType &m, ScalarType minDiameter) -{ - std::vector< std::pair > CCV; - int TotalCC=ConnectedComponents(m, CCV); - int DeletedCC=0; - tri::ConnectedComponentIterator ci; - for(unsigned int i=0;i::iterator fpvi; + for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) + Allocator::DeleteFace(m,(**fpvi)); + } + } + return std::make_pair(TotalCC,DeletedCC); + } + + + /// Remove the connected components smaller than a given diameter + // it returns a pair with the number of connected components and the number of deleted ones. + static std::pair RemoveSmallConnectedComponentsDiameter(MeshType &m, ScalarType maxDiameter) + { + std::vector< std::pair > CCV; + int TotalCC=ConnectedComponents(m, CCV); + int DeletedCC=0; + tri::ConnectedComponentIterator ci; + for(unsigned int i=0;i bb; + std::vector FPV; + for(ci.start(m,CCV[i].second);!ci.completed();++ci) { - Box3f bb; - std::vector FPV; - for(ci.start(m,CCV[i].second);!ci.completed();++ci) - { - FPV.push_back(*ci); - bb.Add((*ci)->P(0)); - bb.Add((*ci)->P(1)); - bb.Add((*ci)->P(2)); - } - if(bb.Diag()>minDiameter) - { - DeletedCC++; - typename std::vector::iterator fpvi; - for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) - tri::Allocator::DeleteFace(m,(**fpvi)); - } + FPV.push_back(*ci); + bb.Add((*ci)->P(0)); + bb.Add((*ci)->P(1)); + bb.Add((*ci)->P(2)); } - return std::make_pair(TotalCC,DeletedCC); -} + if(bb.Diag()::iterator fpvi; + for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) + tri::Allocator::DeleteFace(m,(**fpvi)); + } + } + return std::make_pair(TotalCC,DeletedCC); + } - }; // end class - /*@}*/ + /// Remove the connected components greater than a given diameter + // it returns a pair with the number of connected components and the number of deleted ones. + static std::pair RemoveHugeConnectedComponentsDiameter(MeshType &m, ScalarType minDiameter) + { + std::vector< std::pair > CCV; + int TotalCC=ConnectedComponents(m, CCV); + int DeletedCC=0; + tri::ConnectedComponentIterator ci; + for(unsigned int i=0;i FPV; + for(ci.start(m,CCV[i].second);!ci.completed();++ci) + { + FPV.push_back(*ci); + bb.Add((*ci)->P(0)); + bb.Add((*ci)->P(1)); + bb.Add((*ci)->P(2)); + } + if(bb.Diag()>minDiameter) + { + DeletedCC++; + typename std::vector::iterator fpvi; + for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) + tri::Allocator::DeleteFace(m,(**fpvi)); + } + } + return std::make_pair(TotalCC,DeletedCC); + } - } //End Namespace Tri +}; // end class +/*@}*/ + +} //End Namespace Tri } // End Namespace vcg #endif