From 0cc19679b0a5432f68429e782b0877e632fd2e02 Mon Sep 17 00:00:00 2001 From: "T.Alderighi" Date: Thu, 7 Jun 2018 10:58:10 +0200 Subject: [PATCH] handle tetras in removeunrefvert --- vcg/complex/algorithms/clean.h | 3073 ++++++++++++++++---------------- vcg/complex/base.h | 4 +- 2 files changed, 1543 insertions(+), 1534 deletions(-) diff --git a/vcg/complex/algorithms/clean.h b/vcg/complex/algorithms/clean.h index cea63d4c..9534cb48 100644 --- a/vcg/complex/algorithms/clean.h +++ b/vcg/complex/algorithms/clean.h @@ -41,55 +41,55 @@ template class ConnectedComponentIterator { public: - typedef ConnectedMeshType MeshType; - typedef typename MeshType::VertexType VertexType; - typedef typename MeshType::VertexPointer VertexPointer; - typedef typename MeshType::VertexIterator VertexIterator; - 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 ConnectedMeshType MeshType; + typedef typename MeshType::VertexType VertexType; + typedef typename MeshType::VertexPointer VertexPointer; + typedef typename MeshType::VertexIterator VertexIterator; + 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; public: - void operator ++() - { - FacePointer fpt=sf.top(); - sf.pop(); - for(int j=0; jVN(); ++j) - if( !face::IsBorder(*fpt,j) ) - { - FacePointer l=fpt->FFp(j); - if( !tri::IsMarked(*mp,l) ) - { - tri::Mark(*mp,l); - sf.push(l); - } - } - } + void operator ++() + { + FacePointer fpt=sf.top(); + sf.pop(); + for(int j=0; jVN(); ++j) + if( !face::IsBorder(*fpt,j) ) + { + FacePointer l=fpt->FFp(j); + if( !tri::IsMarked(*mp,l) ) + { + tri::Mark(*mp,l); + sf.push(l); + } + } + } - void start(MeshType &m, FacePointer p) - { - tri::RequirePerFaceMark(m); - mp=&m; - while(!sf.empty()) sf.pop(); - UnMarkAll(m); - tri::Mark(m,p); - sf.push(p); - } + void start(MeshType &m, FacePointer p) + { + tri::RequirePerFaceMark(m); + mp=&m; + while(!sf.empty()) sf.pop(); + UnMarkAll(m); + tri::Mark(m,p); + sf.push(p); + } - bool completed() { - return sf.empty(); - } + bool completed() { + return sf.empty(); + } - FacePointer operator *() - { - return sf.top(); - } + FacePointer operator *() + { + return sf.top(); + } private: - std::stack sf; - MeshType *mp; + std::stack sf; + MeshType *mp; }; @@ -102,297 +102,306 @@ 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::TetraType TetraType; - typedef typename MeshType::TetraPointer TetraPointer; - typedef typename MeshType::TetraIterator TetraIterator; - typedef typename MeshType::ConstTetraIterator ConstTetraIterator; + 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::TetraType TetraType; + typedef typename MeshType::TetraPointer TetraPointer; + typedef typename MeshType::TetraIterator TetraIterator; + typedef typename MeshType::ConstTetraIterator ConstTetraIterator; - typedef typename vcg::Box3 Box3Type; + typedef typename vcg::Box3 Box3Type; - typedef GridStaticPtr TriMeshGrid; + typedef GridStaticPtr TriMeshGrid; - /* classe di confronto per l'algoritmo di eliminazione vertici duplicati*/ - class RemoveDuplicateVert_Compare{ - public: - inline bool operator()(VertexPointer const &a, VertexPointer const &b) - { - return ((*a).cP() == (*b).cP()) ? (a 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; - - std::sort(perm.begin(),perm.end(),c_obj); - - j = 0; - i = j; - mp[perm[i]] = perm[j]; - ++i; - for(;i!=num_vert;) + static int RemoveDuplicateVertex( MeshType & m, bool RemoveDegenerateFlag=true) // V1.0 { - if( (! (*perm[i]).IsD()) && - (! (*perm[j]).IsD()) && - (*perm[i]).P() == (*perm[j]).cP() ) - { - VertexPointer t = perm[i]; + 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); + + RemoveDuplicateVert_Compare c_obj; + + std::sort(perm.begin(),perm.end(),c_obj); + + j = 0; + i = j; mp[perm[i]] = perm[j]; ++i; - Allocator::DeleteVertex(m,*t); - deleted++; - } - else - { - j = i; - ++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) ]; + } + + for (TetraIterator ti = m.tetra.begin(); ti != m.tetra.end(); ++ti) + if (!(*ti).IsD()) + for (k = 0; k < 4; ++k) + if (mp.find((typename MeshType::VertexPointer)(*ti).V(k)) != mp.end()) + (*ti).V(k) = &*mp[ (*ti).V(k) ]; + + if(RemoveDegenerateFlag) RemoveDegenerateFace(m); + if(RemoveDegenerateFlag && m.en>0) { + RemoveDegenerateEdge(m); + RemoveDuplicateEdge(m); + } + return deleted; } - 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) ]; - } - - for (TetraIterator ti = m.tetra.begin(); ti != m.tetra.end(); ++ti) - if (!(*ti).IsD()) - for (k = 0; k < 4; ++k) - if (mp.find((typename MeshType::VertexPointer)(*ti).V(k)) != mp.end()) - (*ti).V(k) = &*mp[ (*ti).V(k) ]; - - if(RemoveDegenerateFlag) RemoveDegenerateFace(m); - if(RemoveDegenerateFlag && m.en>0) { - RemoveDegenerateEdge(m); - RemoveDuplicateEdge(m); - } - return deleted; - } - - class SortedPair - { - public: - SortedPair() {} - SortedPair(unsigned int v0, unsigned int v1, EdgePointer _fp) + class SortedPair { - 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 + 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)); - } - std::sort(fvec.begin(),fvec.end()); - int total=0; - for(int i=0;i::DeleteFace(m, *(fvec[i].fp) ); - } + std::vector 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)); + } + std::sort(fvec.begin(),fvec.end()); + int total=0; + for(int i=0;i::DeleteFace(m, *(fvec[i].fp) ); + } + } + return total; } - return total; - } - /** This function removes all duplicate faces of the mesh by looking only at their vertex reference. + /** 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 - { - 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)); - } - std::sort(eVec.begin(),eVec.end()); - int total=0; - for(int i=0;i::DeleteEdge(m, *(eVec[i].fp) ); - } + 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)); + } + std::sort(eVec.begin(),eVec.end()); + int total=0; + for(int i=0;i::DeleteEdge(m, *(eVec[i].fp) ); + } + } + return total; } - return total; - } - static int CountUnreferencedVertex( MeshType& m) - { - return RemoveUnreferencedVertex(m,false); - } + static int CountUnreferencedVertex( MeshType& m) + { + return RemoveUnreferencedVertex(m,false); + } - /** This function removes vertices that are not referenced by any face or by any edge. + /** This function removes vertices that are not referenced by any face or by any edge. @param m The mesh @param DeleteVertexFlag if false prevent the vertex deletion and just count it. @return The number of removed vertices */ - static int RemoveUnreferencedVertex( MeshType& m, bool DeleteVertexFlag=true) // V1.0 - { - tri::RequirePerVertexFlags(m); - - std::vector referredVec(m.vert.size(),false); - int deleted = 0; + static int RemoveUnreferencedVertex( MeshType& m, bool DeleteVertexFlag=true) // V1.0 + { + tri::RequirePerVertexFlags(m); - for(auto fi=m.face.begin();fi!=m.face.end();++fi) - if( !(*fi).IsD() ) - for(auto j=0;j<(*fi).VN();++j) - referredVec[tri::Index(m,(*fi).V(j))]=true; + std::vector referredVec(m.vert.size(),false); + int deleted = 0; - for(auto ei=m.edge.begin();ei!=m.edge.end();++ei) - if( !(*ei).IsD() ){ - referredVec[tri::Index(m,(*ei).V(0))]=true; - referredVec[tri::Index(m,(*ei).V(1))]=true; - } - - if(!DeleteVertexFlag) - return std::count(referredVec.begin(),referredVec.end(),true); - - for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) - if( (!(*vi).IsD()) && (!referredVec[tri::Index(m,*vi)]) ) - { - Allocator::DeleteVertex(m,*vi); - ++deleted; - } - return deleted; - } + for(auto fi = m.face.begin(); fi != m.face.end(); ++fi) + if( !(*fi).IsD() ) + for(auto j=0; j < (*fi).VN(); ++j) + referredVec[tri::Index(m, (*fi).V(j))]=true; - /** + for(auto ei=m.edge.begin();ei!=m.edge.end();++ei) + if( !(*ei).IsD() ){ + referredVec[tri::Index(m, (*ei).V(0))]=true; + referredVec[tri::Index(m, (*ei).V(1))]=true; + } + + for(auto ti=m.tetra.begin(); ti!=m.tetra.end();++ti) + if( !(*ti).IsD() ){ + referredVec[tri::Index(m, (*ti).V(0))]=true; + referredVec[tri::Index(m, (*ti).V(1))]=true; + referredVec[tri::Index(m, (*ti).V(2))]=true; + referredVec[tri::Index(m, (*ti).V(3))]=true; + } + + + if(!DeleteVertexFlag) + return std::count(referredVec.begin(),referredVec.end(),true); + + for(auto vi=m.vert.begin();vi!=m.vert.end();++vi) + if( (!(*vi).IsD()) && (!referredVec[tri::Index(m,*vi)]) ) + { + Allocator::DeleteVertex(m,*vi); + ++deleted; + } + 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) - { - VertexIterator vi; - int count_vd = 0; + 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]) ) - { - 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). @@ -400,386 +409,386 @@ public: We do not take care of topology because when we have degenerate faces the topology calculation functions crash. */ - static int RemoveDegenerateFace(MeshType& m) - { - int count_fd = 0; + static int RemoveDegenerateFace(MeshType& m) + { + 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) ) + 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) + { + CountNonManifoldVertexFF(m,true); + tri::UpdateSelection::FaceFromVertexLoose(m); + int count_removed = 0; + for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) + if(!(*fi).IsD() && (*fi).IsS()) + Allocator::DeleteFace(m,*fi); + for(VertexIterator 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); + + // count selected vertices references + std::unordered_map refCount; // selected vertex index -> reference count + size_t countSplit = 0; + for (size_t i=0; i::DeleteFace(m,*fi); + for (int j=0; j<2; ++j) + { + const VertexPointer vp = m.edge[i].V(j); + if (vp->IsS()) + { + const size_t refs = ++refCount[Index(m, m.edge[i].V(j))]; + if (refs > 1) { + countSplit++; + } + } + } } - } - 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) ) + // actual split + if (countSplit > 0) { - count_ed++; - Allocator::DeleteEdge(m,*ei); + auto newVertIt = tri::Allocator::AddVertices(m, countSplit); + for (size_t i=0; iIsS()) + { + if (--refCount[vIdx] > 0) + { + newVertIt->ImportData(*vp); + m.edge[i].V(j) = &*(newVertIt++); + } + } + } + } } - } - return count_ed; - } - - static int RemoveNonManifoldVertex(MeshType& m) - { - CountNonManifoldVertexFF(m,true); - tri::UpdateSelection::FaceFromVertexLoose(m); - int count_removed = 0; - for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) - if(!(*fi).IsD() && (*fi).IsS()) - Allocator::DeleteFace(m,*fi); - for(VertexIterator 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); - - // count selected vertices references - std::unordered_map refCount; // selected vertex index -> reference count - size_t countSplit = 0; - for (size_t i=0; iIsS()) - { - const size_t refs = ++refCount[Index(m, m.edge[i].V(j))]; - if (refs > 1) { - countSplit++; - } - } - } - } - // actual split - if (countSplit > 0) - { - auto newVertIt = tri::Allocator::AddVertices(m, countSplit); - for (size_t i=0; iIsS()) - { - if (--refCount[vIdx] > 0) - { - newVertIt->ImportData(*vp); - m.edge[i].V(j) = &*(newVertIt++); - } - } - } - } - } - return int(countSplit); - } - - - 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); - tri::UpdateSelection::VertexClear(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; iVN(); 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); - } - } - 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 int(countSplit); } - return int(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()); - - for(size_t i=0;iIsD()) - { - FaceType &ff= *ToDelVec[i]; - if ((!IsManifold(ff,0))|| - (!IsManifold(ff,1))|| - (!IsManifold(ff,2))) + tri::RequireCompactness(m); + tri::UpdateSelection::VertexClear(m); + std::vector cnt(m.vn,0); + + for(size_t i=0;i(ff,j)) - vcg::face::FFDetach(ff,j); - - Allocator::DeleteFace(m,ff); - count_fd++; + cnt[tri::Index(m,m.edge[i].V(0))]++; + cnt[tri::Index(m,m.edge[i].V(1))]++; } - } + for(size_t i=0;i2) m.vert[i].SetS(); } - return count_fd; - } - /* Remove the faces that are out of a given range of area */ - static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)(), bool OnlyOnSelected=false) - { - int count_fd = 0; - MinAreaThr*=2; - MaxAreaThr*=2; - for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi){ - if(!(*fi).IsD()) - if(!OnlyOnSelected || (*fi).IsS()) + static void SelectCreaseVertexOnEdgeMesh(MeshType &m, ScalarType AngleRadThr) + { + tri::RequireCompactness(m); + tri::RequireVEAdjacency(m); + tri::UpdateTopology::VertexEdge(m); + tri::UpdateSelection::VertexClear(m); + for(size_t i=0;i(*fi); - if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) ) - { - Allocator::DeleteFace(m,*fi); - count_fd++; - } + std::vector 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(); + } } } - return count_fd; - } - static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m,0);} - + /// 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; iVN(); 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); + } + } + 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 int(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()); + + 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; + } + + /* Remove the faces that are out of a given range of area */ + static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits::max)(), bool OnlyOnSelected=false) + { + int count_fd = 0; + MinAreaThr*=2; + MaxAreaThr*=2; + for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi){ + if(!(*fi).IsD()) + if(!OnlyOnSelected || (*fi).IsS()) + { + const ScalarType doubleArea=DoubleArea(*fi); + if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) ) + { + Allocator::DeleteFace(m,*fi); + count_fd++; + } + } + } + return count_fd; + } + + static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m,0);} + + + + /** * Is the mesh only composed by quadrilaterals? */ - 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()) + static bool IsBitQuadOnly(const MeshType &m) { - 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; - } + 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; } - 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) + { + FacePointer fp = fi->FFp(z); + int zp = fi->FFi(z); + if(fi->IsF(z) != fp->IsF(zp)) return false; + } + } + return true; + } + + /** * Is the mesh only composed by triangles? (non polygonal faces) */ - static bool IsBitTriOnly(const MeshType &m) - { - tri::RequirePerFaceFlags(m); - for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) { - if ( !fi->IsD() && fi->IsAnyF() ) return false; + static bool IsBitTriOnly(const MeshType &m) + { + tri::RequirePerFaceFlags(m); + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) { + if ( !fi->IsD() && fi->IsAnyF() ) return false; + } + return true; } - return true; - } - static bool IsBitPolygonal(const MeshType &m){ - return !IsBitTriOnly(m); - } + static bool IsBitPolygonal(const MeshType &m){ + return !IsBitTriOnly(m); + } - /** + /** * Is the mesh only composed by quadrilaterals and triangles? (no pentas, etc) * It assumes that the bits are consistent. In that case there can be only a single faux edge. */ - static bool IsBitTriQuadOnly(const MeshType &m) - { - tri::RequirePerFaceFlags(m); - typedef typename MeshType::FaceType F; - for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { - unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2); - if ( tmp!=F::FAUX0 && tmp!=F::FAUX1 && tmp!=F::FAUX2 && tmp!=0 ) return false; + static bool IsBitTriQuadOnly(const MeshType &m) + { + tri::RequirePerFaceFlags(m); + typedef typename MeshType::FaceType F; + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { + unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2); + if ( tmp!=F::FAUX0 && tmp!=F::FAUX1 && tmp!=F::FAUX2 && tmp!=0 ) return false; + } + return true; } - return true; - } - /** + /** * How many quadrilaterals? * It assumes that the bits are consistent. In that case we count the tris with a single faux edge and divide by two. */ - static int CountBitQuads(const MeshType &m) - { - tri::RequirePerFaceFlags(m); - typedef typename MeshType::FaceType F; - int count=0; - for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { - unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2); - if ( tmp==F::FAUX0 || tmp==F::FAUX1 || tmp==F::FAUX2) count++; + static int CountBitQuads(const MeshType &m) + { + tri::RequirePerFaceFlags(m); + typedef typename MeshType::FaceType F; + int count=0; + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { + unsigned int tmp = fi->cFlags()&(F::FAUX0|F::FAUX1|F::FAUX2); + if ( tmp==F::FAUX0 || tmp==F::FAUX1 || tmp==F::FAUX2) count++; + } + return count / 2; } - return count / 2; - } - /** + /** * How many triangles? (non polygonal faces) */ - static int CountBitTris(const MeshType &m) - { - tri::RequirePerFaceFlags(m); - int count=0; - for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { - if (!(fi->IsAnyF())) count++; + static int CountBitTris(const MeshType &m) + { + tri::RequirePerFaceFlags(m); + int count=0; + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { + if (!(fi->IsAnyF())) count++; + } + return count; } - return count; - } - /** + /** * How many polygons of any kind? (including triangles) * it assumes that there are no faux vertexes (e.g vertices completely surrounded by faux edges) */ - static int CountBitPolygons(const MeshType &m) - { - tri::RequirePerFaceFlags(m); - int count = 0; - for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { - if (fi->IsF(0)) count++; - if (fi->IsF(1)) count++; - if (fi->IsF(2)) count++; + static int CountBitPolygons(const MeshType &m) + { + tri::RequirePerFaceFlags(m); + int count = 0; + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) if (!fi->IsD()) { + if (fi->IsF(0)) count++; + if (fi->IsF(1)) count++; + if (fi->IsF(2)) count++; + } + return m.fn - count/2; } - return m.fn - count/2; - } - /** + /** * The number of polygonal faces is * FN - EN_f (each faux edge hides exactly one triangular face or in other words a polygon of n edges has n-3 faux edges.) * In the general case where a The number of polygonal faces is @@ -790,340 +799,340 @@ public: * as a intuitive proof think to a internal vertex that is collapsed onto a border of a polygon: * it deletes 2 faces, 1 faux edges and 1 vertex so to keep the balance you have to add back the removed vertex. */ - static int CountBitLargePolygons(MeshType &m) - { - tri::RequirePerFaceFlags(m); - UpdateFlags::VertexSetV(m); - // First loop Clear all referenced vertices - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if (!fi->IsD()) - for(int i=0;i<3;++i) fi->V(i)->ClearV(); + static int CountBitLargePolygons(MeshType &m) + { + tri::RequirePerFaceFlags(m); + UpdateFlags::VertexSetV(m); + // First loop Clear all referenced vertices + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if (!fi->IsD()) + for(int i=0;i<3;++i) fi->V(i)->ClearV(); - // Second Loop, count (twice) faux edges and mark all vertices touched by non faux edges - // (e.g vertexes on the boundary of a polygon) - int countE = 0; - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if (!fi->IsD()) { - for(int i=0;i<3;++i) - { - if (fi->IsF(i)) - countE++; - else - { - fi->V0(i)->SetV(); - fi->V1(i)->SetV(); - } - } - } - // Third Loop, count the number of referenced vertexes that are completely surrounded by faux edges. + // Second Loop, count (twice) faux edges and mark all vertices touched by non faux edges + // (e.g vertexes on the boundary of a polygon) + int countE = 0; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if (!fi->IsD()) { + for(int i=0;i<3;++i) + { + if (fi->IsF(i)) + countE++; + else + { + fi->V0(i)->SetV(); + fi->V1(i)->SetV(); + } + } + } + // Third Loop, count the number of referenced vertexes that are completely surrounded by faux edges. - int countV = 0; - for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) - if (!vi->IsD() && !vi->IsV()) countV++; + int countV = 0; + for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if (!vi->IsD() && !vi->IsV()) countV++; - return m.fn - countE/2 + countV ; - } + return m.fn - countE/2 + countV ; + } - /** + /** * Checks that the mesh has consistent per-face faux edges * (the ones that merges triangles into larger polygons). * A border edge should never be faux, and faux edges should always be * reciprocated by another faux edges. * It requires FF adjacency. */ - static bool HasConsistentPerFaceFauxFlag(const MeshType &m) - { - RequireFFAdjacency(m); - RequirePerFaceFlags(m); + static bool HasConsistentPerFaceFauxFlag(const MeshType &m) + { + RequireFFAdjacency(m); + RequirePerFaceFlags(m); - for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if(!(*fi).IsD()) - for (int k=0; k<3; k++) - if( ( fi->IsF(k) != fi->cFFp(k)->IsF(fi->cFFi(k)) ) || - ( fi->IsF(k) && face::IsBorder(*fi,k)) ) - { - return false; - } - return true; - } + for (ConstFaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if(!(*fi).IsD()) + for (int k=0; k<3; k++) + if( ( fi->IsF(k) != fi->cFFp(k)->IsF(fi->cFFi(k)) ) || + ( fi->IsF(k) && face::IsBorder(*fi,k)) ) + { + return false; + } + return true; + } - /** + /** * Count the number of non manifold edges in a polylinemesh, e.g. the edges where there are more than 2 incident faces. * */ - static int CountNonManifoldEdgeEE( MeshType & m, bool SelectFlag=false) - { - MeshAssert::OnlyEdgeMesh(m); - RequireEEAdjacency(m); - tri::UpdateTopology::EdgeEdge(m); - - if(SelectFlag) UpdateSelection::VertexClear(m); - - 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. - EdgeIterator ei; - for (ei = m.edge.begin(); ei != m.edge.end(); ++ei) if (!ei->IsD()) + static int CountNonManifoldEdgeEE( MeshType & m, bool SelectFlag=false) { - TD[(*ei).V(0)]++; - TD[(*ei).V(1)]++; + MeshAssert::OnlyEdgeMesh(m); + RequireEEAdjacency(m); + tri::UpdateTopology::EdgeEdge(m); + + if(SelectFlag) UpdateSelection::VertexClear(m); + + 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. + EdgeIterator ei; + for (ei = m.edge.begin(); ei != m.edge.end(); ++ei) if (!ei->IsD()) + { + TD[(*ei).V(0)]++; + TD[(*ei).V(1)]++; + } + + tri::UpdateFlags::VertexClearV(m); + // Second Loop, Check that each vertex have been seen 1 or 2 times. + for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if (!vi->IsD()) + { + if( TD[vi] >2 ) + { + if(SelectFlag) (*vi).SetS(); + nonManifoldCnt++; + } + } + return nonManifoldCnt; } - tri::UpdateFlags::VertexClearV(m); - // Second Loop, Check that each vertex have been seen 1 or 2 times. - for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) if (!vi->IsD()) - { - if( TD[vi] >2 ) - { - if(SelectFlag) (*vi).SetS(); - nonManifoldCnt++; - } - } - 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) - { - 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) + static int CountNonManifoldEdgeFF( MeshType & m, bool SelectFlag=false) { - if (!fi->IsD()) - { - for(int i=0;i<3;++i) - if(!IsManifold(*fi,i)) - { - 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); - } - } - } - } - return edgeCnt; - } + RequireFFAdjacency(m); + int nmfBit[3]; + nmfBit[0]= FaceType::NewBitFlag(); + nmfBit[1]= FaceType::NewBitFlag(); + nmfBit[2]= FaceType::NewBitFlag(); - /** Count (and eventually select) non 2-Manifold vertexes of a mesh + + 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)) + { + 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); + } + } + } + } + return edgeCnt; + } + + /** 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); - - 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()) + static int CountNonManifoldVertexFF( MeshType & m, bool selectVert = true ) { - for (int k=0; kVN(); k++) - { - TD[(*fi).V(k)]++; - } - } + RequireFFAdjacency(m); + if(selectVert) UpdateSelection::VertexClear(m); - 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; iVN(); ++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; iVN(); i++) if (!(*fi).V(i)->IsV()) - { - (*fi).V(i)->SetV(); - face::Pos pos(&(*fi),i); + int nonManifoldCnt=0; + SimpleTempData TD(m.vert,0); - int starSizeFF = pos.NumberOfIncidentFaces(); - - if (starSizeFF != TD[(*fi).V(i)]) - { - if (selectVert) - (*fi).V(i)->SetS(); - nonManifoldCnt++; - } - } - } - return nonManifoldCnt; - } - /// Very simple test of water tightness. No boundary and no non manifold edges. - /// Assume that it is orientable. - /// It could be debated if a closed non orientable surface is watertight or not. - /// - /// The rationale of not testing orientability here is that - /// it requires FFAdj while this test do not require any adjacency. - /// - static bool IsWaterTight(MeshType & m) - { - int edgeNum=0,edgeBorderNum=0,edgeNonManifNum=0; - CountEdgeNum(m, edgeNum, edgeBorderNum,edgeNonManifNum); - return (edgeBorderNum==0) && (edgeNonManifNum==0); - } - - 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 - } - - - - static int CountHoles( MeshType & m) - { - UpdateFlags::FaceClearV(m); - int loopNum=0; - for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) if(!fi->IsD()) - { - for(int j=0;j<3;++j) + // 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()) { - if(!fi->IsV() && face::IsBorder(*fi,j)) - { - face::Pos startPos(&*fi,j); - face::Pos curPos=startPos; - do + for (int k=0; kVN(); k++) { - curPos.NextB(); - curPos.F()->SetV(); + TD[(*fi).V(k)]++; } - while(curPos!=startPos); - ++loopNum; - } } - } - return loopNum; - } - /* + 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; iVN(); ++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; iVN(); i++) if (!(*fi).V(i)->IsV()) + { + (*fi).V(i)->SetV(); + face::Pos pos(&(*fi),i); + + int starSizeFF = pos.NumberOfIncidentFaces(); + + if (starSizeFF != TD[(*fi).V(i)]) + { + if (selectVert) + (*fi).V(i)->SetS(); + nonManifoldCnt++; + } + } + } + return nonManifoldCnt; + } + /// Very simple test of water tightness. No boundary and no non manifold edges. + /// Assume that it is orientable. + /// It could be debated if a closed non orientable surface is watertight or not. + /// + /// The rationale of not testing orientability here is that + /// it requires FFAdj while this test do not require any adjacency. + /// + static bool IsWaterTight(MeshType & m) + { + int edgeNum=0,edgeBorderNum=0,edgeNonManifNum=0; + CountEdgeNum(m, edgeNum, edgeBorderNum,edgeNonManifNum); + return (edgeBorderNum==0) && (edgeNonManifNum==0); + } + + 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 + } + + + + static int CountHoles( MeshType & m) + { + UpdateFlags::FaceClearV(m); + int loopNum=0; + for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) if(!fi->IsD()) + { + for(int j=0;j<3;++j) + { + if(!fi->IsV() && face::IsBorder(*fi,j)) + { + face::Pos startPos(&*fi,j); + face::Pos curPos=startPos; + do + { + curPos.NextB(); + curPos.F()->SetV(); + } + while(curPos!=startPos); + ++loopNum; + } + } + } + return loopNum; + } + + /* 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 ConnectedComponents(MeshType &m, std::vector< std::pair > &CCV) - { - tri::RequireFFAdjacency(m); - CCV.clear(); - tri::UpdateFlags::FaceClearV(m); - std::stack sf; - FacePointer fpt=&*(m.face.begin()); - for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + static int CountConnectedComponents(MeshType &m) { - if(!((*fi).IsD()) && !(*fi).IsV()) - { - (*fi).SetV(); - CCV.push_back(std::make_pair(0,&*fi)); - sf.push(&*fi); - while (!sf.empty()) + std::vector< std::pair > CCV; + return ConnectedComponents(m,CCV); + } + + static int ConnectedComponents(MeshType &m, std::vector< std::pair > &CCV) + { + tri::RequireFFAdjacency(m); + CCV.clear(); + tri::UpdateFlags::FaceClearV(m); + std::stack sf; + FacePointer fpt=&*(m.face.begin()); + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { - fpt=sf.top(); - ++CCV.back().first; - sf.pop(); - for(int j=0; jVN(); ++j) - { - if( !face::IsBorder(*fpt,j) ) + if(!((*fi).IsD()) && !(*fi).IsV()) { - FacePointer l = fpt->FFp(j); - if( !(*l).IsV() ) - { - (*l).SetV(); - sf.push(l); - } + (*fi).SetV(); + CCV.push_back(std::make_pair(0,&*fi)); + sf.push(&*fi); + while (!sf.empty()) + { + fpt=sf.top(); + ++CCV.back().first; + sf.pop(); + for(int j=0; jVN(); ++j) + { + if( !face::IsBorder(*fpt,j) ) + { + FacePointer l = fpt->FFp(j); + if( !(*l).IsV() ) + { + (*l).SetV(); + sf.push(l); + } + } + } + } } - } } - } + return int(CCV.size()); } - return int(CCV.size()); - } - static void ComputeValence( MeshType &m, typename MeshType::PerVertexIntHandle &h) - { - for(VertexIterator vi=m.vert.begin(); vi!= m.vert.end();++vi) - h[vi]=0; - - for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + static void ComputeValence( MeshType &m, typename MeshType::PerVertexIntHandle &h) { - if(!((*fi).IsD())) - for(int j=0;jVN();j++) - ++h[tri::Index(m,fi->V(j))]; - } - } + for(VertexIterator vi=m.vert.begin(); vi!= m.vert.end();++vi) + h[vi]=0; - /** + for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) + { + if(!((*fi).IsD())) + for(int j=0;jVN();j++) + ++h[tri::Index(m,fi->V(j))]; + } + } + + /** GENUS. A topologically invariant property of a surface defined as @@ -1158,24 +1167,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 @@ -1186,708 +1195,708 @@ public: * * All other meshes are \em irregular. */ - static void IsRegularMesh(MeshType &m, bool &Regular, bool &Semiregular) - { - RequireVFAdjacency(m); - Regular = true; - - VertexIterator vi; - - // for each vertex the number of edges are count - for (vi = m.vert.begin(); vi != m.vert.end(); ++vi) + static void IsRegularMesh(MeshType &m, bool &Regular, bool &Semiregular) { - if (!vi->IsD()) - { - face::Pos he((*vi).VFp(), &*vi); - face::Pos ht = he; + RequireVFAdjacency(m); + Regular = true; - int n=0; - bool border=false; - do + VertexIterator vi; + + // for each vertex the number of edges are count + for (vi = m.vert.begin(); vi != m.vert.end(); ++vi) { - ++n; - ht.NextE(); - if (ht.IsBorder()) - border=true; - } - while (ht != he); - - if (border) - n = n/2; - - 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; - } - } - - - static bool IsCoherentlyOrientedMesh(MeshType &m) - { - RequireFFAdjacency(m); - MeshAssert::FFAdjacencyIsInitialized(m); - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if (!fi->IsD()) - for(int i=0;i<3;++i) - if(!face::CheckOrientation(*fi,i)) - return false; - - return true; - } - - static void OrientCoherentlyMesh(MeshType &m, bool &_IsOriented, bool &_IsOrientable) - { - RequireFFAdjacency(m); - MeshAssert::FFAdjacencyIsInitialized(m); - bool IsOrientable = true; - bool IsOriented = true; - - UpdateFlags::FaceClearV(m); - std::stack faces; - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - { - if (!fi->IsD() && !fi->IsV()) - { - // each face put in the stack is selected (and oriented) - fi->SetV(); - faces.push(&(*fi)); - while (!faces.empty()) - { - FacePointer fp = faces.top(); - faces.pop(); - - // make consistently oriented the adjacent faces - for (int j = 0; j < 3; j++) - { - if (!face::IsBorder(*fp,j) && face::IsManifold(*fp, j)) + if (!vi->IsD()) { - FacePointer fpaux = fp->FFp(j); - int iaux = fp->FFi(j); - if (!CheckOrientation(*fpaux, iaux)) - { - IsOriented = false; + face::Pos he((*vi).VFp(), &*vi); + face::Pos ht = he; - if (!fpaux->IsV()) - face::SwapEdge(*fpaux, iaux); - else + int n=0; + bool border=false; + do { - IsOrientable = false; - break; + ++n; + ht.NextE(); + if (ht.IsBorder()) + border=true; + } + while (ht != he); + + if (border) + n = n/2; + + if ((n != 6)&&(!border && n != 4)) + { + Regular = false; + break; } - } - if (!fpaux->IsV()) - { - fpaux->SetV(); - faces.push(fpaux); - } } - } } - } - if (!IsOrientable) break; - } - _IsOriented = IsOriented; - _IsOrientable = IsOrientable; - } - - /// 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) - { - 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)); - } - } - /// 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; - } - } - - 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 = ScalarType(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::TriangleNormal((*fi)).Normalize(); - if( vcg::AngleN(NN,TriangleNormal(*(*fi).FFp(0)).Normalize()) > NormalThrRad && - vcg::AngleN(NN,TriangleNormal(*(*fi).FFp(1)).Normalize()) > NormalThrRad && - vcg::AngleN(NN,TriangleNormal(*(*fi).FFp(2)).Normalize()) > NormalThrRad ) + if (!Regular) + Semiregular = false; + else { - (*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)),TriangleNormal(*(*fi).FFp(i)),p,L); - if(ret && L[0]>eps && L[1]>eps && L[2]>eps) + // For now we do not account for semi-regularity + Semiregular = false; + } + } + + + static bool IsCoherentlyOrientedMesh(MeshType &m) + { + RequireFFAdjacency(m); + MeshAssert::FFAdjacencyIsInitialized(m); + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if (!fi->IsD()) + for(int i=0;i<3;++i) + if(!face::CheckOrientation(*fi,i)) + return false; + + return true; + } + + static void OrientCoherentlyMesh(MeshType &m, bool &_IsOriented, bool &_IsOrientable) + { + RequireFFAdjacency(m); + MeshAssert::FFAdjacencyIsInitialized(m); + bool IsOrientable = true; + bool IsOriented = true; + + UpdateFlags::FaceClearV(m); + std::stack faces; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + { + if (!fi->IsD() && !fi->IsV()) { - (*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; - } + // each face put in the stack is selected (and oriented) + fi->SetV(); + faces.push(&(*fi)); + while (!faces.empty()) + { + FacePointer fp = faces.top(); + faces.pop(); + + // make consistently oriented the adjacent faces + for (int j = 0; j < 3; j++) + { + if (!face::IsBorder(*fp,j) && face::IsManifold(*fp, j)) + { + FacePointer fpaux = fp->FFp(j); + int iaux = fp->FFi(j); + if (!CheckOrientation(*fpaux, iaux)) + { + IsOriented = false; + + if (!fpaux->IsV()) + face::SwapEdge(*fpaux, iaux); + else + { + IsOrientable = false; + break; + } + } + if (!fpaux->IsV()) + { + fpaux->SetV(); + faces.push(fpaux); + } + } + } + } } - } + if (!IsOrientable) break; } - } - - // tri::UpdateNormal::PerFace(m); + _IsOriented = IsOriented; + _IsOrientable = IsOrientable; } - 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()) + /// 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) { - (*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; + 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)); } - } - } - inBox.clear(); + } + /// 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; + } + } + + 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; } - FaceType::DeleteBitFlag(referredBit); - return (ret.size()>0); - } + // 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 = ScalarType(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::TriangleNormal((*fi)).Normalize(); + if( vcg::AngleN(NN,TriangleNormal(*(*fi).FFp(0)).Normalize()) > NormalThrRad && + vcg::AngleN(NN,TriangleNormal(*(*fi).FFp(1)).Normalize()) > NormalThrRad && + vcg::AngleN(NN,TriangleNormal(*(*fi).FFp(2)).Normalize()) > 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)),TriangleNormal(*(*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 ) + { + 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 DeletedVertNum=0; - for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) - if((*vi).IsD()) DeletedVertNum++; + static bool IsSizeConsistent(MeshType &m) + { + int DeletedVertNum=0; + for (VertexIterator vi = m.vert.begin(); vi != m.vert.end(); ++vi) + if((*vi).IsD()) DeletedVertNum++; - int DeletedEdgeNum=0; - for (EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei) - if((*ei).IsD()) DeletedEdgeNum++; + int DeletedEdgeNum=0; + for (EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei) + if((*ei).IsD()) DeletedEdgeNum++; - int DeletedFaceNum=0; - for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) - if((*fi).IsD()) DeletedFaceNum++; + int DeletedFaceNum=0; + for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi) + if((*fi).IsD()) DeletedFaceNum++; - 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; + 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; - } + 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) - { - RequireFFAdjacency(m); + static bool IsFFAdjacencyConsistent(MeshType &m) + { + 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 (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; + } - /** + /** 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; + } - /** + /** This function test if two triangular faces of a mesh intersect. It assumes that the faces (as storage) are different (e.g different address) If the two faces are different but coincident (same set of vertexes) return true. if the faces share an edge no test is done. if the faces share only a vertex, the opposite edge is tested against the face */ - static bool TestFaceFaceIntersection(FaceType *f0,FaceType *f1) - { - int sv = face::CountSharedVertex(f0,f1); - if(sv==3) return true; - if(sv==0) return (vcg::IntersectionTriangleTriangle((*f0),(*f1))); - // if the faces share only a vertex, the opposite edge (as a segment) is tested against the face - // to avoid degenerate cases where the two triangles have the opposite edge on a common plane - // we offset the segment to test toward the shared vertex - if(sv==1) + static bool TestFaceFaceIntersection(FaceType *f0,FaceType *f1) { - int i0,i1; ScalarType a,b; - face::FindSharedVertex(f0,f1,i0,i1); - CoordType shP = f0->V(i0)->P()*0.5; - if(vcg::IntersectionSegmentTriangle(Segment3((*f0).V1(i0)->P()*0.5+shP,(*f0).V2(i0)->P()*0.5+shP), *f1, a, b) ) - { - // a,b are the param coords of the intersection point of the segment. - if(a+b>=1 || a<=EPSIL || b<=EPSIL ) return false; - return true; - } - if(vcg::IntersectionSegmentTriangle(Segment3((*f1).V1(i1)->P()*0.5+shP,(*f1).V2(i1)->P()*0.5+shP), *f0, a, b) ) - { - // a,b are the param coords of the intersection point of the segment. - if(a+b>=1 || a<=EPSIL || b<=EPSIL ) return false; - return true; - } + int sv = face::CountSharedVertex(f0,f1); + if(sv==3) return true; + if(sv==0) return (vcg::IntersectionTriangleTriangle((*f0),(*f1))); + // if the faces share only a vertex, the opposite edge (as a segment) is tested against the face + // to avoid degenerate cases where the two triangles have the opposite edge on a common plane + // we offset the segment to test toward the shared vertex + if(sv==1) + { + int i0,i1; ScalarType a,b; + face::FindSharedVertex(f0,f1,i0,i1); + CoordType shP = f0->V(i0)->P()*0.5; + if(vcg::IntersectionSegmentTriangle(Segment3((*f0).V1(i0)->P()*0.5+shP,(*f0).V2(i0)->P()*0.5+shP), *f1, a, b) ) + { + // a,b are the param coords of the intersection point of the segment. + if(a+b>=1 || a<=EPSIL || b<=EPSIL ) return false; + return true; + } + if(vcg::IntersectionSegmentTriangle(Segment3((*f1).V1(i1)->P()*0.5+shP,(*f1).V2(i1)->P()*0.5+shP), *f0, a, b) ) + { + // a,b are the param coords of the intersection point of the segment. + if(a+b>=1 || a<=EPSIL || b<=EPSIL ) return false; + 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) - { - int mergedCnt=0; - mergedCnt = ClusterVertex(m,radius); - RemoveDuplicateVertex(m,true); - return mergedCnt; - } + static int MergeCloseVertex(MeshType &m, const ScalarType radius) + { + int mergedCnt=0; + mergedCnt = ClusterVertex(m,radius); + RemoveDuplicateVertex(m,true); + return mergedCnt; + } - static int ClusterVertex(MeshType &m, const ScalarType radius) - { - if(m.vn==0) return 0; - // some spatial indexing structure does not work well with deleted vertices... - tri::Allocator::CompactVertexVector(m); - typedef vcg::SpatialHashTable SampleSHT; - SampleSHT sht; - tri::EmptyTMark markerFunctor; - std::vector closests; - int mergedCnt=0; - sht.Set(m.vert.begin(), m.vert.end()); - UpdateFlags::VertexClearV(m); - for(VertexIterator viv = m.vert.begin(); viv!= m.vert.end(); ++viv) - if(!(*viv).IsD() && !(*viv).IsV()) - { - (*viv).SetV(); - Point3 p = viv->cP(); - Box3 bb(p-Point3(radius,radius,radius),p+Point3(radius,radius,radius)); - GridGetInBox(sht, markerFunctor, bb, closests); - // qDebug("Vertex %i has %i closest", &*viv - &*m.vert.begin(),closests.size()); - for(size_t i=0; i::CompactVertexVector(m); + typedef vcg::SpatialHashTable SampleSHT; + SampleSHT sht; + tri::EmptyTMark markerFunctor; + std::vector closests; + int mergedCnt=0; + sht.Set(m.vert.begin(), m.vert.end()); + UpdateFlags::VertexClearV(m); + for(VertexIterator viv = m.vert.begin(); viv!= m.vert.end(); ++viv) + if(!(*viv).IsD() && !(*viv).IsV()) + { + (*viv).SetV(); + Point3 p = viv->cP(); + Box3 bb(p-Point3(radius,radius,radius),p+Point3(radius,radius,radius)); + GridGetInBox(sht, markerFunctor, bb, closests); + // qDebug("Vertex %i has %i closest", &*viv - &*m.vert.begin(),closests.size()); + for(size_t i=0; icP()); + if(dist < radius && !closests[i]->IsV()) + { + // printf("%f %f \n",dist,radius); + mergedCnt++; + closests[i]->SetV(); + closests[i]->P()=p; + } + } + } + return mergedCnt; + } + + + 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;icP()); - if(dist < radius && !closests[i]->IsV()) - { - // printf("%f %f \n",dist,radius); - mergedCnt++; - closests[i]->SetV(); - closests[i]->P()=p; - } + std::vector FPV; + if(CCV[i].first::iterator fpvi; + for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) + Allocator::DeleteFace(m,(**fpvi)); + } } - } - return mergedCnt; - } - - - 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 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); } - 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 RemoveSmallConnectedComponentsDiameter(MeshType &m, ScalarType maxDiameter) { - Box3 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()::iterator fpvi; - for(fpvi=FPV.begin(); fpvi!=FPV.end(); ++fpvi) - tri::Allocator::DeleteFace(m,(**fpvi)); - } + 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) + { + 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); } - return std::make_pair(TotalCC,DeletedCC); - } - /// 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 RemoveHugeConnectedComponentsDiameter(MeshType &m, ScalarType minDiameter) { - 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)); - } + 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); } - return std::make_pair(TotalCC,DeletedCC); - } - /** + /** Select the folded faces using an angle threshold on the face normal. The face is selected if the dot product between the face normal and the normal of the plane fitted using the vertices of the one ring faces is below the cosThreshold. The cosThreshold requires a negative cosine value (a positive value is clamp to zero). */ - static void SelectFoldedFaceFromOneRingFaces(MeshType &m, ScalarType cosThreshold) - { - typedef std::unordered_set VertexSet; - tri::RequireVFAdjacency(m); - tri::RequirePerFaceNormal(m); - tri::RequirePerVertexNormal(m); - vcg::tri::UpdateSelection::FaceClear(m); - vcg::tri::UpdateNormal::PerFaceNormalized(m); - vcg::tri::UpdateNormal::PerVertexNormalized(m); - vcg::tri::UpdateTopology::VertexFace(m); - if (cosThreshold > 0) - cosThreshold = 0; + static void SelectFoldedFaceFromOneRingFaces(MeshType &m, ScalarType cosThreshold) + { + typedef std::unordered_set VertexSet; + tri::RequireVFAdjacency(m); + tri::RequirePerFaceNormal(m); + tri::RequirePerVertexNormal(m); + vcg::tri::UpdateSelection::FaceClear(m); + vcg::tri::UpdateNormal::PerFaceNormalized(m); + vcg::tri::UpdateNormal::PerVertexNormalized(m); + vcg::tri::UpdateTopology::VertexFace(m); + if (cosThreshold > 0) + cosThreshold = 0; #pragma omp parallel for schedule(dynamic, 10) - for (int i = 0; i < m.face.size(); i++) - { - VertexSet nearVertex; - std::vector pointVec; - FacePointer f = &m.face[i]; - for (int j = 0; j < 3; j++) - { - std::vector temp; - vcg::face::VVStarVF(f->V(j), temp); - typename std::vector::iterator iter = temp.begin(); - for (; iter != temp.end(); iter++) + for (int i = 0; i < m.face.size(); i++) { - if ((*iter) != f->V1(j) && (*iter) != f->V2(j)) - { - if (nearVertex.insert((*iter)).second) - pointVec.push_back((*iter)->P()); - } - } - nearVertex.insert(f->V(j)); - pointVec.push_back(f->P(j)); - } + VertexSet nearVertex; + std::vector pointVec; + FacePointer f = &m.face[i]; + for (int j = 0; j < 3; j++) + { + std::vector temp; + vcg::face::VVStarVF(f->V(j), temp); + typename std::vector::iterator iter = temp.begin(); + for (; iter != temp.end(); iter++) + { + if ((*iter) != f->V1(j) && (*iter) != f->V2(j)) + { + if (nearVertex.insert((*iter)).second) + pointVec.push_back((*iter)->P()); + } + } + nearVertex.insert(f->V(j)); + pointVec.push_back(f->P(j)); + } - if (pointVec.size() > 3) - { - vcg::Plane3 plane; - vcg::FitPlaneToPointSet(pointVec, plane); - float avgDot = 0; - for (auto nvp : nearVertex) - avgDot += plane.Direction().dot(nvp->N()); - avgDot /= nearVertex.size(); - typename MeshType::VertexType::NormalType normal; - if (avgDot < 0) - normal = -plane.Direction(); - else - normal = plane.Direction(); - if (normal.dot(f->N()) < cosThreshold) - f->SetS(); - } + if (pointVec.size() > 3) + { + vcg::Plane3 plane; + vcg::FitPlaneToPointSet(pointVec, plane); + float avgDot = 0; + for (auto nvp : nearVertex) + avgDot += plane.Direction().dot(nvp->N()); + avgDot /= nearVertex.size(); + typename MeshType::VertexType::NormalType normal; + if (avgDot < 0) + normal = -plane.Direction(); + else + normal = plane.Direction(); + if (normal.dot(f->N()) < cosThreshold) + f->SetS(); + } + } } - } - /** + /** Select the faces on the first mesh that intersect the second mesh. It uses a grid for querying so a face::mark should be added. */ - static int SelectIntersectingFaces(MeshType &m1, MeshType &m2) - { - RequirePerFaceMark(m2); - RequireCompactness(m1); - RequireCompactness(m2); - - tri::UpdateSelection::FaceClear(m1); - - TriMeshGrid gM; - gM.Set(m2.face.begin(),m2.face.end()); - int selCnt=0; - for(auto fi=m1.face.begin();fi!=m1.face.end();++fi) + static int SelectIntersectingFaces(MeshType &m1, MeshType &m2) { - Box3< ScalarType> bbox; - (*fi).GetBBox(bbox); - std::vector inBox; - vcg::tri::GetInBoxFace(m2, gM, bbox,inBox); - for(auto fib=inBox.begin(); fib!=inBox.end(); ++fib) - { - if(Clean::TestFaceFaceIntersection(&*fi,*fib)){ - fi->SetS(); - ++selCnt; - } - } - inBox.clear(); + RequirePerFaceMark(m2); + RequireCompactness(m1); + RequireCompactness(m2); + + tri::UpdateSelection::FaceClear(m1); + + TriMeshGrid gM; + gM.Set(m2.face.begin(),m2.face.end()); + int selCnt=0; + for(auto fi=m1.face.begin();fi!=m1.face.end();++fi) + { + Box3< ScalarType> bbox; + (*fi).GetBBox(bbox); + std::vector inBox; + vcg::tri::GetInBoxFace(m2, gM, bbox,inBox); + for(auto fib=inBox.begin(); fib!=inBox.end(); ++fib) + { + if(Clean::TestFaceFaceIntersection(&*fi,*fib)){ + fi->SetS(); + ++selCnt; + } + } + inBox.clear(); + } + return selCnt; } - return selCnt; - } }; // end class /*@}*/ diff --git a/vcg/complex/base.h b/vcg/complex/base.h index 7014f12e..e269158a 100644 --- a/vcg/complex/base.h +++ b/vcg/complex/base.h @@ -382,7 +382,7 @@ public: Clear(); } - int Mem(const int & nv, const int & nf) const { + int Mem(const int & nv, const int & nf, const int & nt) const { typename std::set< PointerToAttribute>::const_iterator i; int size = 0; size += sizeof(TriMesh)+sizeof(VertexType)*nv+sizeof(FaceType)*nf; @@ -394,7 +394,7 @@ public: for( i = face_attr.begin(); i != face_attr.end(); ++i) size += ((SimpleTempDataBase*)(*i)._handle)->SizeOf()*nf; for (i = tetra_attr.begin(); i != tetra_attr.end(); ++i) - size += ((SimpleTempDataBase *)(*i)._handle)->SizeOf() * tn; + size += ((SimpleTempDataBase *)(*i)._handle)->SizeOf() * nt; for( i = mesh_attr.begin(); i != mesh_attr.end(); ++i) size += ((SimpleTempDataBase*)(*i)._handle)->SizeOf();