/**************************************************************************** * VCGLib o o * * Visual and Computer Graphics Library o o * * _ O _ * * Copyright(C) 2004 \/)\/ * * 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 __VCGLIB_CURVE_ON_SURF_H #define __VCGLIB_CURVE_ON_SURF_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace vcg { namespace tri { /// \ingroup trimesh /// \brief A class for managing curves on a 2manifold. /** This class is used to project/simplify/smooth polylines over a triangulated surface. */ template class CoM { public: typedef typename MeshType::ScalarType ScalarType; typedef typename MeshType::VertexType VertexType; typedef typename MeshType::VertexPointer VertexPointer; typedef typename MeshType::VertexIterator VertexIterator; typedef typename MeshType::EdgeIterator EdgeIterator; typedef typename MeshType::EdgeType EdgeType; typedef typename MeshType::FaceType FaceType; typedef typename MeshType::FacePointer FacePointer; typedef typename MeshType::FaceIterator FaceIterator; typedef Box3 Box3Type; typedef typename vcg::GridStaticPtr MeshGrid; typedef typename vcg::GridStaticPtr EdgeGrid; typedef typename face::Pos PosType; class Param { public: ScalarType surfDistThr; // Distance between surface and curve; used in simplify and refine ScalarType polyDistThr; // Distance between the ScalarType minRefEdgeLen; // Minimal admitted Edge Lenght (used in refine: never make edge shorther than this value) ScalarType maxSimpEdgeLen; // Minimal admitted Edge Lenght (used in simplify: never make edges longer than this value) ScalarType maxSmoothDelta; // The maximum movement that is admitted during smoothing. Param(MeshType &m) { Default(m);} void Default(MeshType &m) { surfDistThr = m.bbox.Diag()/10000.0; polyDistThr = m.bbox.Diag()/1000.0; minRefEdgeLen = m.bbox.Diag()/2000.0; maxSimpEdgeLen = m.bbox.Diag()/1000.0; maxSmoothDelta = m.bbox.Diag()/100.0; } void Dump() const { printf("surfDistThr = %6.3f\n",surfDistThr ); printf("polyDistThr = %6.3f\n",polyDistThr ); printf("minEdgeLen = %6.3f\n",minRefEdgeLen ); printf("maxSmoothDelta = %6.3f\n",maxSmoothDelta); } }; // The Data Members MeshType &base; MeshGrid uniformGrid; Param par; CoM(MeshType &_m) :base(_m),par(_m){} //******** CUT TREE GENERATION int countNonVisitedEdges(VertexType * vp, EdgeType * &ep) { std::vector starVec; edge::VEStarVE(&*vp,starVec); int cnt =0; for(size_t i=0;iIsV()) { cnt++; ep = starVec[i]; } } return cnt; } void Retract(MeshType &t) { tri::Clean::RemoveDuplicateVertex(t); printf("Retracting a mesh of %i edges and %i vertices\n",t.en,t.vn); tri::UpdateTopology::VertexEdge(t); std::stack vertStack; for(VertexIterator vi=t.vert.begin();vi!=t.vert.end();++vi) { std::vector starVec; edge::VEStarVE(&*vi,starVec); if(starVec.size()==1) vertStack.push(&*vi); } tri::UpdateFlags::EdgeClearV(t); tri::UpdateFlags::VertexClearV(t); while(!vertStack.empty()) { VertexType *vp = vertStack.top(); vertStack.pop(); vp->C()=Color4b::Blue; EdgeType *ep=0; int eCnt = countNonVisitedEdges(vp,ep); if(eCnt==1) // We have only one non visited edge over vp { assert(!ep->IsV()); ep->SetV(); VertexType *otherVertP; if(ep->V(0)==vp) otherVertP = ep->V(1); else otherVertP = ep->V(0); vertStack.push(otherVertP); } } for(size_t i =0; i::DeleteEdge(t,t.edge[i]); tri::Clean::RemoveUnreferencedVertex(t); tri::Allocator::CompactEveryVector(t); } // void BuildVisitTree(MeshType &dualMesh) { tri::UpdateTopology::FaceFace(base); tri::UpdateFlags::FaceClearV(base); std::vector > visitStack; // the stack contain the pos on the 'starting' face. MeshType treeMesh; // this mesh will contain the set of the non traversed edge base.face[0].SetV(); for(int i=0;i<3;++i) visitStack.push_back(PosType(&(base.face[0]),i,base.face[0].V(i))); int cnt=1; while(!visitStack.empty()) { std::swap(visitStack.back(),visitStack[rand()%visitStack.size()]); PosType c = visitStack.back(); visitStack.pop_back(); assert(c.F()->IsV()); c.F()->C() = Color4b::ColorRamp(0,base.fn,cnt); c.FlipF(); if(!c.F()->IsV()) { tri::Allocator::AddEdge(treeMesh,Barycenter(*(c.FFlip())),Barycenter(*(c.F()))); ++cnt; c.F()->SetV(); c.FlipE();c.FlipV(); visitStack.push_back(c); c.FlipE();c.FlipV(); visitStack.push_back(c); } else { tri::Allocator::AddEdge(dualMesh,c.V()->P(),c.VFlip()->P()); } } assert(cnt==base.fn); Retract(dualMesh); } // Given an edge of a mesh, supposedly intersecting the polyline, // we search on it the closest point to the polyline static float MinDistOnEdge(VertexType *v0,VertexType *v1, EdgeGrid &edgeGrid, MeshType &poly, Point3f &closestPoint) { float minPolyDist = std::numeric_limits::max(); const float sampleNum = 10; const float maxDist = poly.bbox.Diag()/10.0; for(float k = 0;kP()*k +v1->P()*(sampleNum-k))/sampleNum; EdgeType *cep = vcg::tri::GetClosestEdgeBase(poly,edgeGrid,samplePnt,maxDist,polyDist,closestPPoly); if(polyDist < minPolyDist) { minPolyDist = polyDist; closestPoint = closestPPoly; } } return minPolyDist; } class QualitySign { public: EdgeGrid &edgeGrid; MeshType &poly; Param ∥ QualitySign(EdgeGrid &_e,MeshType &_poly, Param &_par):edgeGrid(_e),poly(_poly),par(_par) {}; bool operator()(face::Pos ep) const { VertexType *v0 = ep.V(); VertexType *v1 = ep.VFlip(); if(v0->Q() * v1->Q() < 0) { //Point3f pp = CoS::QLerp(v0,v1); Point3f closestP; float minDist = MinDistOnEdge(v0,v1,edgeGrid,poly,closestP); if(minDist < par.polyDistThr) return true; } return false; } }; static Point3f QLerp(VertexType *v0, VertexType *v1) { float qSum = fabs(v0->Q())+fabs(v1->Q()); float w0 = (qSum - fabs(v0->Q()))/qSum; float w1 = (qSum - fabs(v1->Q()))/qSum; return v0->P()*w0 + v1->P()*w1; } struct QualitySignSplit : public std::unary_function , Point3f> { EdgeGrid &edgeGrid; MeshType &poly; Param ∥ QualitySignSplit(EdgeGrid &_e,MeshType &_p, Param &_par):edgeGrid(_e),poly(_p),par(_par) {}; void operator()(VertexType &nv, face::Pos ep) { VertexType *v0 = ep.V(); VertexType *v1 = ep.VFlip(); Point3f closestP; float minDist = MinDistOnEdge(v0,v1,edgeGrid,poly,closestP); nv.P()=closestP; // nv.P() = CoS::QLerp(v0,v1); } Color4b WedgeInterp(Color4b &c0, Color4b &c1) { Color4b cc; cc.lerp(c0,c1,0.5f); return Color4b::Red; } TexCoord2f WedgeInterp(TexCoord2f &t0, TexCoord2f &t1) { TexCoord2f tmp; assert(t0.n()== t1.n()); tmp.n()=t0.n(); tmp.t()=(t0.t()+t1.t())/2.0; return tmp; } }; void DumpPlanes(MeshType &poly, std::vector &planeVec) { MeshType full; for(int i=0;i::Mesh(full,t); } tri::io::ExporterPLY::Save(full,"planes.ply"); } Plane3f ComputeEdgePlane(VertexType *v0, VertexType *v1) { Plane3f pl; Point3f mid = (v0->P()+v1->P())/2.0; Point3f delta = (v1->P()-v0->P()).Normalize(); Point3f n0 = (v0->N() ^ delta).Normalize(); Point3f n1 = (v1->N() ^ delta).Normalize(); Point3f n = (n0+n1)/2.0; pl.Init(mid,n); return pl; } void ComputePlaneField(MeshType &poly, EdgeGrid &edgeGrid) { // First Compute per-edge planes std::vector planeVec(poly.en); for(int i=0;i::VertexClear(base); for(VertexIterator vi=base.vert.begin();vi!=base.vert.end();++vi) { Point3 p = vi->P(); float minDist=maxDist; Point3f closestP; EdgeType *cep = vcg::tri::GetClosestEdgeBase(poly,edgeGrid,p,maxDist,minDist,closestP); if(cep) { int ind = tri::Index(poly,cep); vi->Q() = SignedDistancePlanePoint(planeVec[ind],p); Point3f proj = planeVec[ind].Projection(p); // if(Distance(proj,closestP)>edge::Length(*cep)) // { // vi->Q() =1; // vi->SetS(); // } } else { vi->Q() =1; vi->SetS(); } } } void CutAlongPolyLineUsingField(MeshType &poly,EdgeGrid &edgeGrid) { QualitySign qsPred(edgeGrid,poly,par); QualitySignSplit qsSplit(edgeGrid,poly,par); tri::UpdateTopology::FaceFace(base); tri::RefineE(base,qsSplit,qsPred); } /* * Make an edge mesh 1-manifold by splitting all the * vertexes that have more than two incident edges * * It performs the split in three steps. * First it collects and counts the vertices to be splitten. * Then it adds the vertices to the mesh and lastly it updates the poly with the newly added vertices. * * singSplitFlag allow to ubersplit each singularity in a number of vertex of the same order of its degreee. * */ void DecomposeNonManifoldTree(MeshType &poly, bool singSplitFlag = true) { std::vector degreeVec(poly.vn); tri::UpdateTopology::VertexEdge(poly); int neededVert=0; int delta; if(singSplitFlag) delta = 1; else delta =2; for(VertexIterator vi=poly.vert.begin(); vi!=poly.vert.end();++vi) { std::vector starVec; edge::VEStarVE(&*vi,starVec); degreeVec[tri::Index(poly, *vi)] = starVec.size(); if(starVec.size()>2) neededVert += starVec.size()-delta; } VertexIterator firstVi = tri::Allocator::AddVertices(poly,neededVert); for(size_t i=0;i2) { std::vector edgeStarVec; edge::VEStarVE(&(poly.vert[i]),edgeStarVec); assert(edgeStarVec.size() == degreeVec[i]); for(size_t j=delta;jV(0)) == i) ind = 0; else ind = 1; ep->V(ind) = &*firstVi; ep->V(ind)->P() = poly.vert[i].P(); ep->V(ind)->N() = poly.vert[i].N(); ++firstVi; } } } } /* * Given a manifold edgemesh it returns the boundary/terminal endpoints. */ static void FindTerminalPoints(MeshType &poly, std::vector &vec) { tri::UpdateTopology::VertexEdge(poly); for(VertexIterator vi=poly.vert.begin(); vi!=poly.vert.end();++vi) { if(edge::VEDegree(&*vi)==1) vec.push_back(&*vi); } } // This function will decompose the input edge mesh into a set of // connected components. // the vector will contain, for each connected component, a vector with all the edge indexes. void BuildConnectedComponentVectors(MeshType &poly, std::vector< std::vector< int> > &ccVec) { tri::UpdateFlags::EdgeClearV(poly); tri::UpdateTopology::EdgeEdge(poly); int visitedEdgeNum=0 ; int ccCnt=0; EdgeIterator eIt = poly.edge.begin(); while(visitedEdgeNum < poly.en) { ccVec.resize(ccVec.size()+1); while(eIt->IsV()) ++eIt; EdgeType *startE = &*eIt; EdgeType *curEp = &*eIt; int curEi = 0; // printf("Starting Visit of connected Component %i from edge %i\n",ccCnt,tri::Index(m,*eIt)); while( (curEp->EEp(curEi) != startE) && (curEp->EEp(curEi) != curEp) ) { EdgeType *nextEp = curEp->EEp(curEi); int nextEi = (curEp->EEi(curEi)+1)%2; curEp = nextEp; curEi = nextEi; } curEp->SetV(); curEi = (curEi +1)%2; // Flip the visit direction! visitedEdgeNum++; ccVec[ccCnt].push_back(tri::Index(poly,curEp)); while(! curEp->EEp(curEi)->IsV()) { EdgeType *nextEp = curEp->EEp(curEi); int nextEi = (curEp->EEi(curEi)+1)%2; curEp->SetV(); curEp = nextEp; curEi = nextEi; curEp->V(curEi)->C() = Color4b::Scatter(30,ccCnt%30); if(!curEp->IsV()) { ccVec[ccCnt].push_back(tri::Index(poly,curEp)); visitedEdgeNum++; } } ccCnt++; } printf("en %i - VisitedEdgeNum = %i\n",poly.en, visitedEdgeNum); } void ExtractSubMesh(MeshType &poly, std::vector &ind, MeshType &subPoly) { subPoly.Clear(); std::vector remap(poly.vert.size(),-1); for(size_t i=0;i::AddVertex(subPoly,poly.vert[v0].P(),poly.vert[v0].N()); } if(remap[v1]==-1) { remap[v1]=subPoly.vn; tri::Allocator::AddVertex(subPoly,poly.vert[v1].P(),poly.vert[v1].N()); } tri::Allocator::AddEdge(subPoly, &subPoly.vert[remap[v0]], &subPoly.vert[remap[v1]]); } } void Reorient(MeshType &poly, std::vector< std::vector< int> > &ccVec) { UpdateTopology::EdgeEdge(poly); for(size_t i=0;i toFlipVec(ccVec[i].size(),false); for(int j=0;jEEp(0) != prev) { toFlipVec[j] = true; assert(cur->EEp(1) == prev); } } for(int j=0;j &vec) { printf("Splitting with %i vertices\n",vec.size()); int faceToAdd=0; int vertToAdd=0; // For each splitting point we save the index of the face to be splitten and the "kind" of split to do: // 3 -> means classical 1 to 3 face split // 2 -> means edge split. // 0 -> means no need of a split (e.g. the point is coincident with a vertex) std::vector< std::pair > toSplitVec(vec.size(), std::make_pair(0,0)); MeshGrid uniformGrid; uniformGrid.Set(m.face.begin(), m.face.end()); const float eps=0.01f; const float maxDist = m.bbox.Diag()/10.0; for(size_t i =0; iP(); float minDist; Point3f closestP,ip; FaceType *f = vcg::tri::GetClosestFaceBase(m,uniformGrid,newP,maxDist, minDist, closestP); assert(f); // if(ip[0]::AddFaces(m,faceToAdd); VertexIterator newVi = tri::Allocator::AddVertices(m,vertToAdd); tri::UpdateColor::PerFaceConstant(m,Color4b::White); for(size_t i =0; iP() = vec[i]->P(); VertexType *vp0 = fp0->V(0); VertexType *vp1 = fp0->V(1); VertexType *vp2 = fp0->V(2); fp0->V(0) = vp0; fp0->V(1) = vp1; fp0->V(2) = vp; fp1->V(0) = vp1; fp1->V(1) = vp2; fp1->V(2) = vp; fp2->V(0) = vp2; fp2->V(1) = vp0; fp2->V(2) = vp; fp0->C() = Color4b::Green; fp1->C() = Color4b::Green; fp2->C() = Color4b::Green; } } } void Init() { // Construction of the uniform grid uniformGrid.Set(base.face.begin(), base.face.end()); UpdateNormal::PerFaceNormalized(base); UpdateTopology::FaceFace(base); uniformGrid.Set(base.face.begin(), base.face.end()); } // Point3f GeodesicFlatten(PosType &p) // { // Point3f N0 = p.F()->N(); // Point3f N1 = p.FFlip()->N(); // PosType t=p; // t.FlipF(); // t.FlipE(); // t.FlipV(); // // Now rotate the other point around the edge so that we get the two triangles on the same plane // Eigen::Vector3f otherPoint,sharedEdgeDir; // t.P().ToEigenVector(otherPoint); // (p.V().P() - p.VFlip()->P()).Normalize().ToEigenVector(sharedEdgeDir); // ScalarType dihedralAngleRad = vcg::face::DihedralAngleRad(*p.F(),t.F()); // Eigen::Matrix3f mRot = Eigen::AngleAxisf(dihedralAngleRad,sharedEdgeDir).matrix(); // Point3f otherPointRot; // otherPointRot.FromEigenVector(mRot*otherPoint); // Plane3f facePlane; facePlane.Init(p->F()->P0(),p->F()->P1(),p->F()->P2()); // float dist = SignedDistancePlanePoint(facePlane,otherPointRot); // } void Simplify( MeshType &poly) { int startEn = poly.en; Distribution hist; for(int i =0; i::VertexEdge(poly); for(int i =0; i starVecVp; edge::VVStarVE(&(poly.vert[i]),starVecVp); if(starVecVp.size()==2) { float newSegLen = Distance(starVecVp[0]->P(), starVecVp[1]->P()); Segment3f seg(starVecVp[0]->P(),starVecVp[1]->P()); float segDist; Point3f closestPSeg; SegmentPointDistance(seg,poly.vert[i].cP(),closestPSeg,segDist); Point3f fp,fn; float maxSurfDist = MaxSegDist(starVecVp[0], starVecVp[1],fp,fn); if(maxSurfDist < par.surfDistThr && (newSegLen < par.maxSimpEdgeLen) ) { edge::VEEdgeCollapse(poly,&(poly.vert[i])); } } } tri::Allocator::CompactEveryVector(poly); printf("Simplify %i -> %i\n",startEn,poly.en); } void EvaluateHausdorffDistance(MeshType &poly, Distribution &dist) { dist.Clear(); tri::UpdateTopology::VertexEdge(poly); tri::UpdateQuality::VertexConstant(poly,0); for(int i =0; iQ()+= maxDist; poly.edge[i].V(1)->Q()+= maxDist; } for(int i=0;i(&poly.vert[i]); poly.vert[i].Q()/=deg; } tri::UpdateColor::PerVertexQualityRamp(poly,0,dist.Max()); } // Given a segment find the maximum distance from it to the original surface. float MaxSegDist(VertexType *v0, VertexType *v1, Point3f &farthestPointOnSurf, Point3f &farthestN, Distribution *dist=0) { float maxSurfDist = 0; const float sampleNum = 10; const float maxDist = base.bbox.Diag()/10.0; for(float k = 1;kP()*k +v1->P()*(sampleNum-k))/sampleNum; FaceType *f = vcg::tri::GetClosestFaceBase(base,uniformGrid,samplePnt,maxDist, surfDist, closestPSurf); if(dist) dist->Add(surfDist); assert(f); if(surfDist > maxSurfDist) { maxSurfDist = surfDist; farthestPointOnSurf = closestPSurf; farthestN = f->N(); } } return maxSurfDist; } void Refine( MeshType &poly) { tri::Allocator::CompactEveryVector(poly); int startEdgeSize = poly.en; for(int i =0; ipar.minRefEdgeLen) { Point3f farthestP, farthestN; float maxDist = MaxSegDist(poly.edge[i].V(0),poly.edge[i].V(1),farthestP, farthestN); if(maxDist > par.surfDistThr) { VertexIterator vi = Allocator::AddVertex(poly,farthestP, farthestN); Allocator::AddEdge(poly,poly.edge[i].V(0),&*vi); Allocator::AddEdge(poly,&*vi,poly.edge[i].V(1)); Allocator::DeleteEdge(poly,poly.edge[i]); } } } tri::Allocator::CompactEveryVector(poly); printf("Refine %i -> %i\n",startEdgeSize,poly.en);fflush(stdout); } void SmoothProject(MeshType &poly, int iterNum, ScalarType smoothWeight, ScalarType projectWeight) { assert(poly.en>0 && base.fn>0); for(int k=0;k posVec(poly.vn,Point3f(0,0,0)); std::vector cntVec(poly.vn,0); for(int i =0; iP(); cntVec[vertInd] += 1; } } const float maxDist = base.bbox.Diag()/10.0; for(int i=0; i par.maxSmoothDelta) { newP = poly.vert[i].P() + ( delta / delta.Norm()) * maxDist*0.5; } float minDist; Point3f closestP; FaceType *f = vcg::tri::GetClosestFaceBase(base,uniformGrid,newP,maxDist, minDist, closestP); assert(f); poly.vert[i].P() = newP*(1.0-projectWeight) +closestP*projectWeight; poly.vert[i].N() = f->N(); } Refine(poly); Refine(poly); Simplify(poly); } } }; } // end namespace tri } // end namespace vcg #endif // __VCGLIB_CURVE_ON_SURF_H