Cleaned up the CoM (curve on manifold) class and revised the sample using it
This commit is contained in:
parent
18e424b7c0
commit
c627b31e59
|
@ -39,57 +39,63 @@ struct MyUsedTypes : public UsedTypes<Use<MyVertex>::AsVertexType, Use<MyEdge>::
|
|||
|
||||
class MyVertex : public Vertex< MyUsedTypes, vertex::Coord3f, vertex::Normal3f, vertex::Qualityf, vertex::Color4b, vertex::VEAdj, vertex::VFAdj,vertex::BitFlags >{};
|
||||
class MyEdge : public Edge< MyUsedTypes, edge::VertexRef, edge::VEAdj, edge::EEAdj, edge::BitFlags> {};
|
||||
class MyFace : public Face < MyUsedTypes, face::VertexRef, face::Normal3f, face::VFAdj, face::FFAdj, face::Mark, face::Color4b, face::BitFlags > {};
|
||||
class MyFace : public Face < MyUsedTypes, face::VertexRef, face::Normal3f, face::Qualityf, face::Color4b, face::VFAdj, face::FFAdj, face::Mark, face::Color4b, face::BitFlags > {};
|
||||
class MyMesh : public tri::TriMesh< std::vector<MyVertex>, std::vector<MyEdge>, std::vector<MyFace> >{};
|
||||
|
||||
|
||||
/**
|
||||
* In this sample we take a torus we compute a poly line on it that open it into a disk and we open it.
|
||||
* Then using the COM (Curve On Manifold) framework we smooth this polyline keeping
|
||||
* it on the surface of the torus and then first we refine the torus surface with this
|
||||
* smooth polyline and then we open it along these new edges.
|
||||
*
|
||||
* Optionally you can use your own mesh and polyline by passing them as parameters.
|
||||
*/
|
||||
int main(int argc,char ** argv )
|
||||
{
|
||||
MyMesh base, basecopy, poly;
|
||||
int ret0 = tri::io::Importer<MyMesh>::Open(base,argv[1]);
|
||||
int ret1 = 0;
|
||||
if(argc>2) ret1 = tri::io::Importer<MyMesh>::Open(poly,argv[2]);
|
||||
if(ret0 != 0 || ret1 != 0)
|
||||
{
|
||||
printf("Failed Loading\n");
|
||||
exit(-1);
|
||||
}
|
||||
int ret0=0, ret1=0;
|
||||
if(argc>1) ret0 = tri::io::Importer<MyMesh>::Open(base,argv[1]);
|
||||
|
||||
if(base.FN() == 0) Torus(base,10,4,48,24);
|
||||
|
||||
if(argc>2) ret1 = tri::io::Importer<MyMesh>::Open(poly,argv[2]);
|
||||
tri::UpdateBounding<MyMesh>::Box(base);
|
||||
printf( "Mesh %s has %i vert and %i faces\n", argv[1], base.VN(), base.FN() );
|
||||
printf( "Poly %s has %i vert and %i edges\n", argv[2], poly.VN(), poly.EN() );
|
||||
if(poly.EN()==0) {
|
||||
srand(time(0));
|
||||
if(poly.EN() == 0) {
|
||||
srand(time(nullptr));
|
||||
tri::CutTree<MyMesh> ct(base);
|
||||
ct.BuildVisitTree(poly,rand()%base.fn);
|
||||
ct.Build(poly,rand()%base.fn);
|
||||
}
|
||||
tri::io::ExporterPLY<MyMesh>::Save(poly,"0_cut_tree.ply",tri::io::Mask::IOM_EDGEINDEX);
|
||||
|
||||
tri::CoM<MyMesh> cc(base);
|
||||
cc.Init();
|
||||
cc.MarkFauxEdgeWithPolyLine(poly);
|
||||
bool ret = cc.TagFaceEdgeSelWithPolyLine(poly);
|
||||
if(ret)
|
||||
{
|
||||
tri::Append<MyMesh,MyMesh>::MeshCopy(basecopy,base);
|
||||
tri::UpdateTopology<MyMesh>::FaceFace(basecopy);
|
||||
tri::CutMeshAlongNonFauxEdges<MyMesh>(basecopy);
|
||||
tri::CutMeshAlongSelectedFaceEdges<MyMesh>(basecopy);
|
||||
tri::io::ExporterPLY<MyMesh>::Save(basecopy,"base_cut_with_tree.ply");
|
||||
|
||||
}
|
||||
// Selected vertices are 'locked' during the smoothing.
|
||||
cc.SelectBoundaryVertex(poly);
|
||||
cc.SelectUniformlyDistributed(poly,20); // lock some vertices uniformly just for fun
|
||||
// cc.SelectUniformlyDistributed(poly,10); // lock some vertices uniformly just for fun
|
||||
|
||||
// Two smoothing runs,
|
||||
// the first that allows fast movement over the surface (long edges that can skim surface details)
|
||||
cc.par.surfDistThr = base.bbox.Diag()/100.0;
|
||||
cc.par.maxSimpEdgeLen = base.bbox.Diag()/50.0;
|
||||
cc.par.minRefEdgeLen = base.bbox.Diag()/100.0;
|
||||
cc.SmoothProject(poly,10,0.7,.3);
|
||||
cc.par.surfDistThr = base.bbox.Diag()/100.0f;
|
||||
cc.par.maxSimpEdgeLen = base.bbox.Diag()/50.0f;
|
||||
cc.par.minRefEdgeLen = base.bbox.Diag()/100.0f;
|
||||
cc.SmoothProject(poly,30,0.7f,.3f);
|
||||
tri::io::ExporterPLY<MyMesh>::Save(poly,"1_poly_smooth.ply",tri::io::Mask::IOM_EDGEINDEX+tri::io::Mask::IOM_VERTCOLOR+tri::io::Mask::IOM_VERTQUALITY);
|
||||
|
||||
// The second smooting run more accurate to adapt to the surface
|
||||
cc.par.surfDistThr = base.bbox.Diag()/1000.0;
|
||||
cc.par.maxSimpEdgeLen = base.bbox.Diag()/1000.0;
|
||||
cc.par.minRefEdgeLen = base.bbox.Diag()/2000.0;
|
||||
cc.SmoothProject(poly,10,0.01,.99);
|
||||
cc.par.surfDistThr = base.bbox.Diag()/1000.0f;
|
||||
cc.par.maxSimpEdgeLen = base.bbox.Diag()/1000.0f;
|
||||
cc.par.minRefEdgeLen = base.bbox.Diag()/2000.0f;
|
||||
cc.SmoothProject(poly,10,0.01f,.99f);
|
||||
tri::io::ExporterPLY<MyMesh>::Save(poly,"2_poly_smooth.ply",tri::io::Mask::IOM_EDGEINDEX+tri::io::Mask::IOM_VERTCOLOR+tri::io::Mask::IOM_VERTQUALITY);
|
||||
|
||||
Distribution<float> dist;
|
||||
|
@ -102,8 +108,8 @@ int main(int argc,char ** argv )
|
|||
cc.SplitMeshWithPolyline(poly);
|
||||
tri::io::ExporterPLY<MyMesh>::Save(base,"3_mesh_refined.ply",tri::io::Mask::IOM_VERTCOLOR+tri::io::Mask::IOM_VERTQUALITY);
|
||||
// Now the two meshes should have coincident edges
|
||||
cc.MarkFauxEdgeWithPolyLine(poly);
|
||||
CutMeshAlongNonFauxEdges(base);
|
||||
cc.TagFaceEdgeSelWithPolyLine(poly);
|
||||
CutMeshAlongSelectedFaceEdges(base);
|
||||
tri::io::ExporterPLY<MyMesh>::Save(base,"4_mesh_cut.ply",tri::io::Mask::IOM_VERTCOLOR+tri::io::Mask::IOM_VERTQUALITY);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -184,9 +184,9 @@ public:
|
|||
*
|
||||
*/
|
||||
|
||||
bool MarkFauxEdgeWithPolyLine(MeshType &poly,bool markFlag=true)
|
||||
bool TagFaceEdgeSelWithPolyLine(MeshType &poly,bool markFlag=true)
|
||||
{
|
||||
if(markFlag) tri::UpdateFlags<MeshType>::FaceSetF(base);
|
||||
if(markFlag) tri::UpdateFlags<MeshType>::FaceClearFaceEdgeS(base);
|
||||
tri::UpdateTopology<MeshType>::VertexFace(base);
|
||||
tri::UpdateTopology<MeshType>::FaceFace(base);
|
||||
|
||||
|
@ -210,8 +210,8 @@ public:
|
|||
if(ret){
|
||||
assert(ret);
|
||||
assert(ff0->V(e0)==v0 || ff0->V(e0)==v1);
|
||||
ff0->ClearF(e0);
|
||||
ff1->ClearF(e1);
|
||||
ff0->SetFaceEdgeS(e0);
|
||||
ff1->SetFaceEdgeS(e1);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
|
|
|
@ -23,6 +23,11 @@
|
|||
#ifndef CUT_TREE_H
|
||||
#define CUT_TREE_H
|
||||
|
||||
#include<vcg/complex/complex.h>
|
||||
#include <vcg/space/index/kdtree/kdtree.h>
|
||||
#include<vcg/complex/algorithms/update/quality.h>
|
||||
#include<vcg/complex/algorithms/update/color.h>
|
||||
|
||||
namespace vcg {
|
||||
namespace tri {
|
||||
|
||||
|
@ -40,19 +45,18 @@ public:
|
|||
typedef typename MeshType::FaceType FaceType;
|
||||
typedef typename MeshType::FacePointer FacePointer;
|
||||
typedef typename MeshType::FaceIterator FaceIterator;
|
||||
typedef Box3 <ScalarType> Box3Type;
|
||||
typedef typename vcg::GridStaticPtr<FaceType, ScalarType> MeshGrid;
|
||||
typedef typename vcg::GridStaticPtr<EdgeType, ScalarType> EdgeGrid;
|
||||
typedef Box3<ScalarType> Box3Type;
|
||||
typedef typename face::Pos<FaceType> PosType;
|
||||
typedef typename tri::UpdateTopology<MeshType>::PEdge PEdge;
|
||||
|
||||
MeshType &base;
|
||||
// MeshGrid uniformGrid;
|
||||
|
||||
// Param par;
|
||||
CutTree(MeshType &_m) :base(_m){}
|
||||
|
||||
|
||||
// Perform a simple optimization of the three applying simple shortcuts:
|
||||
// if the endpoints of two consecutive edges are connected by an edge existing on base mesh just use that edges
|
||||
|
||||
void OptimizeTree(KdTree<ScalarType> &kdtree, MeshType &t)
|
||||
{
|
||||
tri::Allocator<MeshType>::CompactEveryVector(t);
|
||||
|
@ -67,7 +71,7 @@ void OptimizeTree(KdTree<ScalarType> &kdtree, MeshType &t)
|
|||
{
|
||||
std::vector<VertexType *> starVec;
|
||||
edge::VVStarVE(&*vi,starVec);
|
||||
if(starVec.size()==2)
|
||||
if(starVec.size()==2) // middle vertex has to be 1-manifold
|
||||
{
|
||||
PosType pos;
|
||||
if(ExistEdge(kdtree,starVec[0]->P(),starVec[1]->P(),pos))
|
||||
|
@ -196,6 +200,143 @@ void Retract(KdTree<ScalarType> &kdtree, MeshType &t)
|
|||
tri::Allocator<MeshType>::CompactEveryVector(t);
|
||||
}
|
||||
|
||||
/** \brief Main function
|
||||
*
|
||||
* It builds a cut tree that open the mesh into a topological disk
|
||||
*
|
||||
*
|
||||
*/
|
||||
void Build(MeshType &dualMesh, int startingFaceInd=0)
|
||||
{
|
||||
tri::UpdateTopology<MeshType>::FaceFace(base);
|
||||
tri::UpdateTopology<MeshType>::VertexFace(base);
|
||||
|
||||
BuildVisitTree(dualMesh,startingFaceInd);
|
||||
// BuildDijkstraVisitTree(dualMesh,startingFaceInd);
|
||||
|
||||
VertexConstDataWrapper<MeshType > vdw(base);
|
||||
KdTree<ScalarType> kdtree(vdw);
|
||||
Retract(kdtree,dualMesh);
|
||||
OptimizeTree(kdtree, dualMesh);
|
||||
tri::UpdateBounding<MeshType>::Box(dualMesh);
|
||||
}
|
||||
|
||||
/* Auxiliary class for keeping the heap of vertices to visit and their estimated distance */
|
||||
struct FaceDist{
|
||||
FaceDist(FacePointer _f):f(_f),dist(_f->Q()){}
|
||||
FacePointer f;
|
||||
ScalarType dist;
|
||||
bool operator < (const FaceDist &o) const
|
||||
{
|
||||
if( dist != o.dist)
|
||||
return dist > o.dist;
|
||||
return f<o.f;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void BuildDijkstraVisitTree(MeshType &dualMesh, int startingFaceInd=0, ScalarType maxDistanceThr=std::numeric_limits<ScalarType>::max())
|
||||
{
|
||||
tri::RequireFFAdjacency(base);
|
||||
tri::RequirePerFaceMark(base);
|
||||
tri::RequirePerFaceQuality(base);
|
||||
typename MeshType::template PerFaceAttributeHandle<FacePointer> parentHandle
|
||||
= tri::Allocator<MeshType>::template GetPerFaceAttribute<FacePointer>(base, "parent");
|
||||
|
||||
std::vector<FacePointer> seedVec;
|
||||
seedVec.push_back(&base.face[startingFaceInd]);
|
||||
|
||||
std::vector<FaceDist> Heap;
|
||||
tri::UnMarkAll(base);
|
||||
tri::UpdateQuality<MeshType>::FaceConstant(base,0);
|
||||
ForEachVertex(base, [&](VertexType &v){
|
||||
tri::Allocator<MeshType>::AddVertex(dualMesh,v.cP());
|
||||
});
|
||||
|
||||
// Initialize the face heap;
|
||||
// All faces in the heap are already marked; Q() store the distance from the source faces;
|
||||
for(size_t i=0;i<seedVec.size();++i)
|
||||
{
|
||||
seedVec[i]->Q()=0;
|
||||
Heap.push_back(FaceDist(seedVec[i]));
|
||||
}
|
||||
// Main Loop
|
||||
int boundary=0;
|
||||
std::make_heap(Heap.begin(),Heap.end());
|
||||
|
||||
int vCnt=0;
|
||||
int eCnt=0;
|
||||
int fCnt=0;
|
||||
|
||||
// The main idea is that in the heap we maintain all the faces to be visited.
|
||||
int nonDiskCnt=0;
|
||||
while(!Heap.empty() && nonDiskCnt<10)
|
||||
{
|
||||
int eulerChi= vCnt-eCnt+fCnt;
|
||||
if(eulerChi==1) nonDiskCnt=0;
|
||||
else ++nonDiskCnt;
|
||||
// printf("HeapSize %i: %i - %i + %i = %i\n",Heap.size(), vCnt,eCnt,fCnt,eulerChi);
|
||||
pop_heap(Heap.begin(),Heap.end());
|
||||
FacePointer currFp = (Heap.back()).f;
|
||||
if(tri::IsMarked(base,currFp))
|
||||
{
|
||||
// printf("Found an already visited face %f %f \n",Heap.back().dist, Heap.back().f->Q());
|
||||
//assert(Heap.back().dist != currFp->Q());
|
||||
|
||||
Heap.pop_back();
|
||||
continue;
|
||||
}
|
||||
Heap.pop_back();
|
||||
++fCnt;
|
||||
eCnt+=3;
|
||||
tri::Mark(base,currFp);
|
||||
|
||||
// printf("pop face %i \n", tri::Index(base,currFp));
|
||||
for(int i=0;i<3;++i)
|
||||
{
|
||||
if(!currFp->V(i)->IsV()) {++vCnt; currFp->V(i)->SetV();}
|
||||
|
||||
FacePointer nextFp = currFp->FFp(i);
|
||||
if( tri::IsMarked(base,nextFp) )
|
||||
{
|
||||
eCnt-=1;
|
||||
printf("is marked\n");
|
||||
if(nextFp != parentHandle[currFp] )
|
||||
{
|
||||
if(currFp>nextFp){
|
||||
tri::Allocator<MeshType>::AddEdge(dualMesh,tri::Index(base,currFp->V0(i)), tri::Index(base,currFp->V1(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else // add it to the heap;
|
||||
{
|
||||
// printf("is NOT marked\n");
|
||||
parentHandle[nextFp] = currFp;
|
||||
ScalarType nextDist = currFp->Q() + Distance(Barycenter(*currFp),Barycenter(*nextFp));
|
||||
int adjMarkedNum=0;
|
||||
for(int k=0;k<3;++k) if(tri::IsMarked(base,nextFp->FFp(k))) ++adjMarkedNum;
|
||||
if(nextDist < maxDistanceThr || adjMarkedNum>1)
|
||||
{
|
||||
nextFp->Q() = nextDist;
|
||||
Heap.push_back(FaceDist(nextFp));
|
||||
push_heap(Heap.begin(),Heap.end());
|
||||
}
|
||||
else {
|
||||
// printf("boundary %i\n",++boundary);
|
||||
tri::Allocator<MeshType>::AddEdge(dualMesh,tri::Index(base,currFp->V0(i)), tri::Index(base,currFp->V1(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // End while
|
||||
printf("fulltree %i vn %i en \n",dualMesh.vn, dualMesh.en);
|
||||
int dupVert=tri::Clean<MeshType>::RemoveDuplicateVertex(dualMesh,false); printf("Removed %i dup vert\n",dupVert);
|
||||
int dupEdge=tri::Clean<MeshType>::RemoveDuplicateEdge(dualMesh); printf("Removed %i dup edges %i\n",dupEdge,dualMesh.EN());
|
||||
tri::Clean<MeshType>::RemoveUnreferencedVertex(dualMesh);
|
||||
|
||||
tri::io::ExporterPLY<MeshType>::Save(dualMesh,"fulltree.ply",tri::io::Mask::IOM_EDGEINDEX);
|
||||
tri::UpdateColor<MeshType>::PerFaceQualityRamp(base);
|
||||
tri::io::ExporterPLY<MeshType>::Save(base,"colored_Bydistance.ply",tri::io::Mask::IOM_FACECOLOR);
|
||||
}
|
||||
|
||||
// \brief This function build a cut tree.
|
||||
//
|
||||
|
@ -207,9 +348,6 @@ void Retract(KdTree<ScalarType> &kdtree, MeshType &t)
|
|||
|
||||
void BuildVisitTree(MeshType &dualMesh, int startingFaceInd=0)
|
||||
{
|
||||
tri::UpdateTopology<MeshType>::FaceFace(base);
|
||||
tri::UpdateTopology<MeshType>::VertexFace(base);
|
||||
|
||||
tri::UpdateFlags<MeshType>::FaceClearV(base);
|
||||
tri::UpdateFlags<MeshType>::VertexBorderFromFaceAdj(base);
|
||||
std::vector<face::Pos<FaceType> > visitStack; // the stack contain the pos on the 'starting' face.
|
||||
|
@ -244,15 +382,8 @@ void BuildVisitTree(MeshType &dualMesh, int startingFaceInd=0)
|
|||
}
|
||||
assert(cnt==base.fn);
|
||||
|
||||
VertexConstDataWrapper<MeshType > vdw(base);
|
||||
KdTree<ScalarType> kdtree(vdw);
|
||||
|
||||
tri::Clean<MeshType>::RemoveDuplicateVertex(dualMesh);
|
||||
// tri::io::ExporterPLY<MeshType>::Save(dualMesh,"fulltree.ply",tri::io::Mask::IOM_EDGEINDEX);
|
||||
|
||||
Retract(kdtree,dualMesh);
|
||||
OptimizeTree(kdtree, dualMesh);
|
||||
tri::UpdateBounding<MeshType>::Box(dualMesh);
|
||||
tri::io::ExporterPLY<MeshType>::Save(dualMesh,"fulltree.ply",tri::io::Mask::IOM_EDGEINDEX);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue