/**************************************************************************** * MeshLab o o * * A versatile mesh processing toolbox o o * * _ O _ * * Copyright(C) 2005 \/)\/ * * Visual Computing Lab /\/| * * ISTI - Italian National Research Council | * * \ * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * for more details. * * * ****************************************************************************/ #ifndef VORONOI_PROCESSING_H #define VORONOI_PROCESSING_H #include #include namespace vcg { namespace tri { template class ClusteringSampler { public: typedef typename MeshType::VertexType VertexType; ClusteringSampler(std::vector &_vec): sampleVec(_vec) { sampleVec = _vec; } std::vector &sampleVec; void AddVert(const VertexType &p) { sampleVec.push_back((VertexType *)(&p)); } }; // end class ClusteringSampler template class VoronoiProcessing { typedef typename MeshType::CoordType CoordType; typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::VertexPointer VertexPointer; typedef typename MeshType::VertexIterator VertexIterator; typedef typename MeshType::FacePointer FacePointer; typedef typename MeshType::FaceIterator FaceIterator; typedef typename MeshType::FaceType FaceType; typedef typename MeshType::FaceContainer FaceContainer; public: // Given a vector of point3f it finds the closest vertices on the mesh. static void SeedToVertexConversion(MeshType &m,std::vector &seedPVec,std::vector &seedVVec) { typedef typename vcg::SpatialHashTable HashVertexGrid; seedVVec.clear(); HashVertexGrid HG; HG.Set(m.vert.begin(),m.vert.end()); const float dist_upper_bound=m.bbox.Diag()/10.0; typename std::vector::iterator pi; for(pi=seedPVec.begin();pi!=seedPVec.end();++pi) { float dist; VertexPointer vp; vp=tri::GetClosestVertex(m, HG, *pi, dist_upper_bound, dist); if(vp) { seedVVec.push_back(vp); } } } typedef typename MeshType::template PerVertexAttributeHandle PerVertexPointerHandle; typedef typename MeshType::template PerFaceAttributeHandle PerFacePointerHandle; static void ComputePerVertexSources(MeshType &m, std::vector &seedVec) { tri::Allocator::DeletePerVertexAttribute(m,"sources"); // delete any conflicting handle regardless of the type... PerVertexPointerHandle vertexSources = tri::Allocator:: template AddPerVertexAttribute (m,"sources"); tri::Allocator::DeletePerFaceAttribute(m,"sources"); // delete any conflicting handle regardless of the type... PerFacePointerHandle faceSources = tri::Allocator:: template AddPerFaceAttribute (m,"sources"); assert(tri::Allocator::IsValidHandle(m,vertexSources)); tri::Geodesic::Compute(m,seedVec,std::numeric_limits::max(),0,&vertexSources); } static void VoronoiColoring(MeshType &m, std::vector &seedVec, bool frontierFlag=true) { PerVertexPointerHandle sources = tri::Allocator:: template GetPerVertexAttribute (m,"sources"); assert(tri::Allocator::IsValidHandle(m,sources)); tri::Geodesic g; VertexPointer farthest; if(frontierFlag) { //static_cast(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers. //The error should have been removed from MSVS2012 std::pair zz(0.0f,static_cast(NULL)); std::vector< std::pair > regionArea(m.vert.size(),zz); std::vector borderVec; GetAreaAndFrontier(m, sources, regionArea, borderVec); tri::Geodesic::Compute(m,borderVec); } tri::UpdateColor::PerVertexQualityRamp(m); } // It associates the faces with a given vertex according to the vertex associations // // It READS the PerVertex attribute 'sources' // It WRITES the PerFace attribute 'sources' static void FaceAssociateRegion(MeshType &m) { PerFacePointerHandle faceSources = tri::Allocator:: template GetPerFaceAttribute (m,"sources"); PerVertexPointerHandle vertexSources = tri::Allocator:: template GetPerVertexAttribute (m,"sources"); for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { faceSources[fi]=0; std::vector vp(3); for(int i=0;i<3;++i) vp[i]=vertexSources[fi->V(i)]; for(int i=0;i<3;++i) // First try to associate to the most reached vertex { if(vp[0]==vp[1] && vp[0]==vp[2]) faceSources[fi] = vp[0]; else { if(vp[0]==vp[1] && vp[0]->Q()< vp[2]->Q()) faceSources[fi] = vp[0]; if(vp[0]==vp[2] && vp[0]->Q()< vp[1]->Q()) faceSources[fi] = vp[0]; if(vp[1]==vp[2] && vp[1]->Q()< vp[0]->Q()) faceSources[fi] = vp[1]; } } } tri::UpdateTopology::FaceFace(m); int unassCnt=0; do { unassCnt=0; for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { if(faceSources[fi]==0) { std::vector vp(3); for(int i=0;i<3;++i) vp[i]=faceSources[fi->FFp(i)]; if(vp[0]!=0 && (vp[0]==vp[1] || vp[0]==vp[2])) faceSources[fi] = vp[0]; else if(vp[1]!=0 && (vp[1]==vp[2])) faceSources[fi] = vp[1]; else faceSources[fi] = std::max(vp[0],std::max(vp[1],vp[2])); if(faceSources[fi]==0) unassCnt++; } } } while(unassCnt>0); } // Select all the faces with a given source vertex // It reads the PerFace attribute 'sources' static int FaceSelectAssociateRegion(MeshType &m, VertexPointer vp) { PerFacePointerHandle sources = tri::Allocator:: template FindPerFaceAttribute (m,"sources"); assert(tri::Allocator::IsValidHandle(m,sources)); tri::UpdateSelection::Clear(m); int selCnt=0; for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { if(sources[fi]==vp) { fi->SetS(); ++selCnt; } } return selCnt; } // Given a seed , it selects all the faces that have the minimal distance vertex sourced by the given . // can be null (it search for unreached faces...) // returns the number of selected faces; // // It reads the PerVertex attribute 'sources' static int FaceSelectRegion(MeshType &m, VertexPointer vp) { PerVertexPointerHandle sources = tri::Allocator:: template GetPerVertexAttribute (m,"sources"); assert(tri::Allocator::IsValidHandle(m,sources)); tri::UpdateSelection::Clear(m); int selCnt=0; for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { int minInd = 0; float minVal=std::numeric_limits::max(); for(int i=0;i<3;++i) { if((*fi).V(i)->Q()Q(); } } if( sources[(*fi).V(minInd)] == vp) { fi->SetS(); selCnt++; } } return selCnt; } // find the vertexes of frontier faces // and compute Area of all the regions static void GetAreaAndFrontier(MeshType &m, PerVertexPointerHandle &sources, std::vector< std::pair > ®ionArea, std::vector &borderVec) { tri::UpdateFlags::VertexClearV(m); for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) { if( sources[(*fi).V(0)] != sources[(*fi).V(1)] || sources[(*fi).V(0)] != sources[(*fi).V(2)] ) { for(int i=0;i<3;++i) { (*fi).V(i)->SetV(); (*fi).V(i)->C() = Color4b::Black; } } else // the face belongs to a single region; accumulate area; { if(sources[(*fi).V(0)] != 0) { int seedIndex = sources[(*fi).V(0)] - &*m.vert.begin(); regionArea[seedIndex].first+=DoubleArea(*fi); regionArea[seedIndex].second=sources[(*fi).V(0)]; } } } // Collect the frontier vertexes borderVec.clear(); for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) if((*vi).IsV()) borderVec.push_back(&*vi); } static void VoronoiRelaxing(MeshType &m, std::vector &seedVec, int relaxIter, int /*percentileClamping*/, vcg::CallBackPos *cb=0) { tri::RequireVFAdjacency(m); typename MeshType::template PerVertexAttributeHandle sources; sources = tri::Allocator:: template AddPerVertexAttribute (m,"sources"); for(int iter=0;iter::Compute(m,seedVec,std::numeric_limits::max(),0,&sources); // Delete all the (hopefully) small regions that have not been reached by the seeds; tri::UpdateFlags::VertexClearV(m); for(int i=0;iV(0)->IsV() || fi->V(1)->IsV() || fi->V(2)->IsV() ) { face::VFDetach(*fi); tri::Allocator::DeleteFace(m,*fi); } // qDebug("Deleted faces not reached: %i -> %i",int(m.face.size()),m.fn); tri::Clean::RemoveUnreferencedVertex(m); tri::Allocator::CompactFaceVector(m); tri::Allocator::CompactVertexVector(m); //static_cast(NULL) has been introduced just to avoid an error in the MSVS2010's compiler confusing pointer with int. You could use nullptr to avoid it, but it's not supported by all compilers. //The error should have been removed from MSVS2012 std::pair zz(0.0f,static_cast(NULL)); std::vector< std::pair > regionArea(m.vert.size(),zz); std::vector borderVec; GetAreaAndFrontier(m, sources, regionArea, borderVec); // Smaller area region are discarded Distribution H; for(size_t i=0;i::Compute(m,borderVec); tri::UpdateColor::PerVertexQualityRamp(m); // Search the local maxima for each region and use them as new seeds std::vector< std::pair > seedMaxima(m.vert.size(),zz); for(VertexIterator vi=m.vert.begin();vi!=m.vert.end();++vi) { int seedIndex = tri::Index(m,sources[vi]); if(seedMaxima[seedIndex].first < (*vi).Q()) { seedMaxima[seedIndex].first=(*vi).Q(); seedMaxima[seedIndex].second=&*vi; } } std::vector newSeeds; for(size_t i=0;iC() = Color4b::Gray; if(regionArea[i].first >= areaThreshold) newSeeds.push_back(seedMaxima[i].second); } tri::UpdateColor::PerVertexQualityRamp(m); for(size_t i=0;iC() = Color4b::Black; for(size_t i=0;iC() = Color4b::Gray; swap(newSeeds,seedVec); for(size_t i=0;iC() = Color4b::White; } tri::Allocator::DeletePerVertexAttribute (m,"sources"); } // Base vertex voronoi coloring algorithm. // it assumes VF adjacency. No attempt of computing real geodesic distnace is done. Just a BFS visit starting from the seeds. static void TopologicalVertexColoring(MeshType &m, std::vector &seedVec) { std::queue VQ; tri::UpdateQuality::VertexConstant(m,0); for(size_t i=0;iQ()=i+1; } while(!VQ.empty()) { VertexPointer vp = VQ.front(); VQ.pop(); std::vector vertStar; vcg::face::VVStarVF(vp,vertStar); for(typename std::vector::iterator vv = vertStar.begin();vv!=vertStar.end();++vv) { if((*vv)->Q()==0) { (*vv)->Q()=vp->Q(); VQ.push(*vv); } } } // end while(!VQ.empty()) } // Drastic Simplification algorithm. // Similar in philosopy to the classic grid clustering but using a voronoi partition instead of the regular grid. // // This function assumes that in the mOld mesh, for each vertex you have a quality that denotes the index of the cluster // mNew is created by collasping onto a single vertex all the vertices that lies in the same cluster. // Non degenerate triangles are preserved. static void VoronoiClustering(MeshType &mOld, MeshType &mNew, std::vector &seedVec) { std::set clusteredFace; FaceIterator fi; for(fi=mOld.face.begin();fi!=mOld.face.end();++fi) { if( (fi->V(0)->Q() != fi->V(1)->Q() ) && (fi->V(0)->Q() != fi->V(2)->Q() ) && (fi->V(1)->Q() != fi->V(2)->Q() ) ) clusteredFace.insert( Point3i(int(fi->V(0)->Q()), int(fi->V(1)->Q()), int(fi->V(2)->Q()))); } tri::Allocator::AddVertices(mNew,seedVec.size()); for(size_t i=0;i< seedVec.size();++i) mNew.vert[i].ImportData(*(seedVec[i])); tri::Allocator::AddFaces(mNew,clusteredFace.size()); std::set::iterator fsi; ; for(fi=mNew.face.begin(),fsi=clusteredFace.begin(); fsi!=clusteredFace.end();++fsi,++fi) { (*fi).V(0) = & mNew.vert[(int)(fsi->V(0)-1)]; (*fi).V(1) = & mNew.vert[(int)(fsi->V(1)-1)]; (*fi).V(2) = & mNew.vert[(int)(fsi->V(2)-1)]; } } }; // end class VoronoiProcessing } // end namespace tri } // end namespace vcg #endif