This commit is contained in:
Marco Callieri 2018-05-09 14:06:55 +02:00
commit 1980a4a611
15 changed files with 470 additions and 187 deletions

View File

@ -89,7 +89,7 @@ int main( int argc, char **argv )
tri::TrivialSampler<MyMesh> mps(sampleVec);
tri::UpdateTopology<MyMesh>::FaceFace(m);
tri::UpdateNormal<MyMesh>::PerFace(m);
tri::UpdateFlags<MyMesh>::FaceFauxCrease(m,math::ToRad(40.0f));
tri::UpdateFlags<MyMesh>::FaceEdgeSelCrease(m,math::ToRad(40.0f));
tri::SurfaceSampling<MyMesh,tri::TrivialSampler<MyMesh> >::EdgeMontecarlo(m,mps,10000,false);
tri::BuildMeshFromCoordVector(MontecarloEdgeMesh,sampleVec);
tri::io::ExporterOFF<MyMesh>::Save(MontecarloEdgeMesh,"MontecarloEdgeMesh.off");

View File

@ -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));
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;

View File

@ -35,8 +35,8 @@ namespace tri {
template<class MESH_TYPE>
void CreaseCut(MESH_TYPE &m, float angleRad)
{
tri::UpdateFlags<MESH_TYPE>::FaceFauxSignedCrease(m, -angleRad, angleRad);
CutMeshAlongNonFauxEdges(m);
tri::UpdateFlags<MESH_TYPE>::FaceEdgeSelSignedCrease(m, -angleRad, angleRad);
CutMeshAlongSelectedFaceEdges(m);
}
/**
@ -48,7 +48,7 @@ void CreaseCut(MESH_TYPE &m, float angleRad)
*
*/
template<class MESH_TYPE>
void CutMeshAlongNonFauxEdges(MESH_TYPE &m)
void CutMeshAlongSelectedFaceEdges(MESH_TYPE &m)
{
typedef typename MESH_TYPE::FaceIterator FaceIterator;
typedef typename MESH_TYPE::FaceType FaceType;
@ -86,7 +86,7 @@ void CutMeshAlongNonFauxEdges(MESH_TYPE &m)
{
do {
curPos.FlipF();curPos.FlipE();
if(!curPos.IsFaux())
if(curPos.IsEdgeS())
break;
} while(curPos!=startPos);
startPos=curPos;
@ -100,7 +100,7 @@ void CutMeshAlongNonFauxEdges(MESH_TYPE &m)
size_t faceInd = Index(m,curPos.F());
indVec[faceInd*3+ curPos.VInd()] = curVertexCounter;
curPos.FlipE();
if(!curPos.IsFaux())
if(curPos.IsEdgeS())
{ //qDebug(" Crease FOUND");
++locCreaseCounter;
curVertexCounter=newVertexCounter;

View File

@ -768,11 +768,11 @@ void BuildMeshFromCoordVector( MeshType & in, const V & v)
template <class TriMeshType,class EdgeMeshType >
void BuildFromNonFaux(TriMeshType &in, EdgeMeshType &out)
void BuildFromFaceEdgeSel(TriMeshType &in, EdgeMeshType &out)
{
tri::RequireCompactness(in);
std::vector<typename tri::UpdateTopology<TriMeshType>::PEdge> edgevec;
tri::UpdateTopology<TriMeshType>::FillUniqueEdgeVector(in, edgevec, false);
tri::UpdateTopology<TriMeshType>::FillSelectedFaceEdgeVector(in, edgevec);
out.Clear();
for(size_t i=0;i<in.vert.size();++i)
tri::Allocator<EdgeMeshType>::AddVertex(out, in.vert[i].P());

View File

@ -184,9 +184,11 @@ 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);
@ -201,19 +203,21 @@ public:
VertexPointer v0 = FindVertexSnap(f0,ip0);
VertexPointer v1 = FindVertexSnap(f1,ip1);
if(v0==0 || v1==0) return false;
if(v0==v1) return false;
if(v0==0 || v1==0)
return false;
if(v0==v1)
return false;
FacePointer ff0,ff1;
int e0,e1;
bool ret=face::FindSharedFaces<FaceType>(v0,v1,ff0,ff1,e0,e1);
if(ret){
if(ret)
{
assert(ret);
assert(ff0->V(e0)==v0 || ff0->V(e0)==v1);
ff0->ClearF(e0);
ff1->ClearF(e1);
}
else {
ff0->SetFaceEdgeS(e0);
ff1->SetFaceEdgeS(e1);
} else {
return false;
}
}
@ -224,7 +228,6 @@ public:
return true;
}
ScalarType MinDistOnEdge(CoordType samplePnt, EdgeGrid &edgeGrid, MeshType &poly, CoordType &closestPoint)
{
ScalarType polyDist;

View File

@ -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 {
@ -41,18 +46,17 @@ public:
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 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);
}
};

View File

@ -475,14 +475,14 @@ private:
tri::UpdateFlags<MeshType>::VertexBorderFromNone(m);
for(auto fi=m.face.begin(); fi!=m.face.end(); ++fi)
if(!(*fi).IsD())
if(!(*fi).IsD() && (params.selectedOnly == false || fi->IsS()))
{
for(auto i=0; i<3; ++i)
{
PosType pi(&*fi, i);
++candidates;
VertexPair bp = VertexPair(pi.V(), pi.VFlip());
Point3<ScalarType> mp = (pi.V()->P()+pi.VFlip()->P())/2.f;;
Point3<ScalarType> mp = (pi.V()->P()+pi.VFlip()->P())/2.f;
bool boundary = false;
if(pi.V()->IsB() == pi.VFlip()->IsB())
@ -581,7 +581,7 @@ private:
int count = 0;
for(auto fi=m.face.begin(); fi!=m.face.end(); ++fi)
if(!(*fi).IsD())
if(!(*fi).IsD() && (params.selectedOnly == false || fi->IsS()))
{
for(auto i=0; i<3; ++i)
{

View File

@ -759,7 +759,7 @@ static void VertexUniform(MeshType & m, VertexSampler &ps, int sampleNum)
///
/// It assumes that the mesh is 1-manifold.
/// each connected component is sampled in a independent way.
/// For each component of lenght <L> we place on it floor(L/radius)+1 samples.
/// For each component of length <L> we place on it floor(L/radius)+1 samples.
/// (if conservative argument is false we place ceil(L/radius)+1 samples)
///
static void EdgeMeshUniform(MeshType &m, VertexSampler &ps, float radius, bool conservative = true)

View File

@ -1113,6 +1113,33 @@ public:
}
}
static ScalarType InitQualityFaceTorsion(PolyMeshType &poly_m)
{
UpdateFaceNormalByFitting(poly_m);
vcg::tri::UpdateNormal<PolyMeshType>::PerVertexFromCurrentFaceNormal(poly_m);
ScalarType MaxA=0;
for (size_t i=0;i<poly_m.face.size();i++)
{
poly_m.face[i].Q()=PolygonTorsion(poly_m.face[i]);
MaxA=std::max(MaxA,poly_m.face[i].Q());
}
return MaxA;
}
static ScalarType InitQualityFaceBending(PolyMeshType &poly_m)
{
UpdateFaceNormalByFitting(poly_m);
vcg::tri::UpdateNormal<PolyMeshType>::PerVertexFromCurrentFaceNormal(poly_m);
ScalarType MaxA=0;
for (size_t i=0;i<poly_m.face.size();i++)
{
poly_m.face[i].Q()=PolygonBending(poly_m.face[i]);
MaxA=std::max(MaxA,poly_m.face[i].Q());
}
return MaxA;
}
static void InitQualityVertEdgeLenght(PolyMeshType &poly_m)
{
for (size_t i=0;i<poly_m.vert.size();i++)

View File

@ -316,8 +316,8 @@ class RefinedFaceData
{
public:
RefinedFaceData(){
ep[0]=0;ep[1]=0;ep[2]=0;
vp[0]=0;vp[1]=0;vp[2]=0;
ep[0] = ep[1] = ep[2] = false;
vp[0] = vp[1] = vp[2] = NULL;
}
bool ep[3];
VertexPointer vp[3];
@ -443,11 +443,11 @@ bool RefineE(MESH_TYPE &m, MIDPOINT &mid, EDGEPRED &ep,bool RefineSelected=false
TexCoordType wtt[6]; // per ogni faccia sono al piu' tre i nuovi valori
// di texture per wedge (uno per ogni edge)
int fca=0,fcn =0;
int fca=0;
for(fi=m.face.begin();fi!=oldendf;++fi) if(!(*fi).IsD())
{
if(cb && (++step%PercStep)==0)(*cb)(step/PercStep,"Refining...");
fcn++;
if(cb && (++step%PercStep)==0)
(*cb)(step/PercStep,"Refining...");
vv[0]=(*fi).V(0);
vv[1]=(*fi).V(1);
vv[2]=(*fi).V(2);
@ -455,7 +455,7 @@ bool RefineE(MESH_TYPE &m, MIDPOINT &mid, EDGEPRED &ep,bool RefineSelected=false
vv[4] = RD[fi].vp[1];
vv[5] = RD[fi].vp[2];
int ind=((&*vv[3])?1:0)+((&*vv[4])?2:0)+((&*vv[5])?4:0);
int ind = ((vv[3] != NULL) ? 1 : 0) + ((vv[4] != NULL) ? 2 : 0) + ((vv[5] != NULL) ? 4 : 0);
nf[0]=&*fi;
int i;
@ -469,27 +469,39 @@ bool RefineE(MESH_TYPE &m, MIDPOINT &mid, EDGEPRED &ep,bool RefineSelected=false
if(tri::HasPerWedgeTexCoord(m))
for(i=0;i<3;++i) {
for(i=0;i<3;++i)
{
wtt[i]=(*fi).WT(i);
wtt[3+i]=mid.WedgeInterp((*fi).WT(i),(*fi).WT((i+1)%3));
}
int orgflag = (*fi).Flags();
for (i=0; i<SplitTab[ind].TriNum; ++i)
for(j=0;j<3;++j){
for(j=0;j<3;++j)
{
(*nf[i]).V(j)=&*vv[SplitTab[ind].TV[i][j]];
if(tri::HasPerWedgeTexCoord(m)) //analogo ai vertici...
(*nf[i]).WT(j) = wtt[SplitTab[ind].TV[i][j]];
assert((*nf[i]).V(j)!=0);
if(SplitTab[ind].TE[i][j]!=3){
if(SplitTab[ind].TE[i][j]!=3)
{
if(orgflag & (MESH_TYPE::FaceType::BORDER0<<(SplitTab[ind].TE[i][j])))
(*nf[i]).SetB(j);
else
(*nf[i]).ClearB(j);
if(orgflag & (MESH_TYPE::FaceType::FACEEDGESEL0<<(SplitTab[ind].TE[i][j])))
(*nf[i]).SetFaceEdgeS(j);
else
(*nf[i]).ClearFaceEdgeS(j);
}
else
{
(*nf[i]).ClearB(j);
(*nf[i]).ClearFaceEdgeS(j);
}
else (*nf[i]).ClearB(j);
}
if(SplitTab[ind].TriNum==3 &&
@ -507,6 +519,11 @@ bool RefineE(MESH_TYPE &m, MIDPOINT &mid, EDGEPRED &ep,bool RefineSelected=false
if((*nf[2]).IsB(0)) (*nf[1]).SetB(1); else (*nf[1]).ClearB(1);
(*nf[1]).ClearB(0);
(*nf[2]).ClearB(0);
if((*nf[1]).IsFaceEdgeS(0)) (*nf[2]).SetFaceEdgeS(1); else (*nf[2]).ClearFaceEdgeS(1);
if((*nf[2]).IsFaceEdgeS(0)) (*nf[1]).SetFaceEdgeS(1); else (*nf[1]).ClearFaceEdgeS(1);
(*nf[1]).ClearFaceEdgeS(0);
(*nf[2]).ClearFaceEdgeS(0);
}
}

View File

@ -122,10 +122,7 @@ public:
static void FaceClearB(MeshType &m) { FaceClear(m,FaceType::BORDER012);}
static void FaceClearS(MeshType &m) {FaceClear(m,FaceType::SELECTED);}
static void FaceClearF(MeshType &m) { FaceClear(m,FaceType::FAUX012);}
static void FaceClearCreases(MeshType &m) { FaceClear(m,FaceType::CREASE0);
FaceClear(m,FaceType::CREASE1);
FaceClear(m,FaceType::CREASE2);
}
static void FaceClearFaceEdgeS(MeshType &m) { FaceClear(m,FaceType::FACEEDGESEL012 ); }
static void EdgeSetV(MeshType &m) { EdgeSet(m,EdgeType::VISITED);}
static void VertexSetV(MeshType &m) { VertexSet(m,VertexType::VISITED);}
@ -380,20 +377,20 @@ public:
/// \brief Marks feature edges according to two signed dihedral angles.
/// Actually it marks as fauxedges all the non feature edges,
/// e.g. the edges where the signed dihedral angle between the normal of two incident faces ,
/// is between the two given thresholds.
/// In this way all the edges that are almost planar are marked as Faux Edges (e.g. edges to be ignored)
/// Actually it uses the face_edge selection bit on faces,
/// we select the edges where the signed dihedral angle between the normal of two incident faces ,
/// is outside the two given thresholds.
/// In this way all the edges that are almost planar are marked as non selected (e.g. edges to be ignored)
/// Note that it uses the signed dihedral angle convention (negative for concave edges and positive for convex ones);
///
/// Optionally it can also mark as feature edges also the boundary edges.
///
static void FaceFauxSignedCrease(MeshType &m, float AngleRadNeg, float AngleRadPos, bool MarkBorderFlag = false )
static void FaceEdgeSelSignedCrease(MeshType &m, float AngleRadNeg, float AngleRadPos, bool MarkBorderFlag = false )
{
RequirePerFaceFlags(m);
RequireFFAdjacency(m);
//initially Nothing is faux (e.g all crease)
FaceClearF(m);
FaceClearFaceEdgeS(m);
// Then mark faux only if the signed angle is the range.
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
{
@ -402,12 +399,12 @@ public:
if(!face::IsBorder(*fi,z) )
{
ScalarType angle = DihedralAngleRad(*fi,z);
if(angle>AngleRadNeg && angle<AngleRadPos)
(*fi).SetF(z);
if(angle<AngleRadNeg || angle>AngleRadPos)
(*fi).SetFaceEdgeS(z);
}
else
{
if(MarkBorderFlag) (*fi).SetF(z);
if(MarkBorderFlag) (*fi).SetFaceEdgeS(z);
}
}
}
@ -416,29 +413,30 @@ public:
/// \brief Marks feature edges according to border flag.
/// Actually it marks as fauxedges all the non border edges,
///
static void FaceFauxBorder(MeshType &m)
static void FaceEdgeSelBorder(MeshType &m)
{
RequirePerFaceFlags(m);
RequireFFAdjacency(m);
//initially Nothing is faux (e.g all crease)
FaceClearF(m);
FaceClearFaceEdgeS(m);
// Then mark faux only if the signed angle is the range.
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi) if(!(*fi).IsD())
{
for(int z=0;z<(*fi).VN();++z)
{
if(!face::IsBorder(*fi,z) ) (*fi).SetF(z);
if(!face::IsBorder(*fi,z) ) (*fi).SetFaceEdgeS(z);
}
}
}
/// \brief Marks feature edges according to a given angle
/// Actually it marks as fauxedges all the non feature edges,
/// e.g. the edge such that the angle between the normal of two faces sharing it is less than the given threshold.
/// In this way all the near planar edges are marked as Faux Edges (e.g. edges to be ignored)
static void FaceFauxCrease(MeshType &m,float AngleRad)
/// Actually it uses the face_edge selection bit on faces,
/// we select the edges where the dihedral angle between the normal of two incident faces is larger than ,
/// the given thresholds.
/// In this way all the near planar edges are marked remains not selected (e.g. edges to be ignored)
static void FaceEdgeSelCrease(MeshType &m,float AngleRad)
{
FaceFauxSignedCrease(m,-AngleRad,AngleRad);
FaceEdgeSelSignedCrease(m,-AngleRad,AngleRad);
}

View File

@ -138,6 +138,20 @@ static void FillUniqueEdgeVector(MeshType &m, std::vector<PEdge> &edgeVec, bool
edgeVec.resize(newEnd-edgeVec.begin()); // redundant! remove?
}
static void FillSelectedFaceEdgeVector(MeshType &m, std::vector<PEdge> &edgeVec)
{
edgeVec.reserve(m.fn*3);
ForEachFace(m, [&](FaceType &f){
for(int j=0;j<f.VN();++j)
if(f.IsFaceEdgeS(j))
edgeVec.push_back(PEdge(&f,j));
});
sort(edgeVec.begin(), edgeVec.end()); // oredering by vertex
edgeVec.erase(std::unique(edgeVec.begin(), edgeVec.end()),edgeVec.end());
}
/*! \brief Initialize the edge vector all the edges that can be inferred from current face vector, setting up all the current adjacency relations
*
@ -431,7 +445,6 @@ static void TestVertexEdge(MeshType &m)
int cnt =0;
for(edge::VEIterator<EdgeType> vei(&*vi);!vei.End();++vei)
cnt++;
EdgeType *vep = vi->VEp();
assert((numVertex[tri::Index(m,*vi)] == 0) == (vi->VEp()==0) );
assert(cnt==numVertex[tri::Index(m,*vi)]);
}

View File

@ -110,10 +110,11 @@ public:
NORMX = 0x00000200,
NORMY = 0x00000400,
NORMZ = 0x00000800,
// Crease _flags, it is assumed that CREASEi = CREASE0<<i
CREASE0 = 0x00008000,
CREASE1 = 0x00010000,
CREASE2 = 0x00020000,
// Face-Edge Selection Flags
FACEEDGESEL0 = 0x00008000,
FACEEDGESEL1 = 0x00010000,
FACEEDGESEL2 = 0x00020000,
FACEEDGESEL012 = FACEEDGESEL0 | FACEEDGESEL1 | FACEEDGESEL2 ,
// Faux edges. (semantics: when a mesh is polygonal, edges which are inside a polygonal face are "faux"
FAUX0 = 0x00040000,
FAUX1 = 0x00080000,
@ -175,12 +176,12 @@ public:
/// This funcion execute the inverse operation of SetS()
void ClearB(int i) {this->Flags() &= (~(BORDER0<<i));}
/// This function checks if the face is selected
bool IsCrease(int i) const {return (this->cFlags() & (CREASE0<<i)) != 0;}
/// This function select the face
void SetCrease(int i){this->Flags() |=(CREASE0<<i);}
/// This funcion execute the inverse operation of SetS()
void ClearCrease(int i) {this->Flags() &= (~(CREASE0<<i));}
/// This function checks if the i-th face-edge is selected
bool IsFaceEdgeS(int i) const {return (this->cFlags() & (FACEEDGESEL0<<i)) != 0;}
/// This function select the i-th face-edge
void SetFaceEdgeS(int i){this->Flags() |=(FACEEDGESEL0<<i);}
/// This function de-select the i-th face-edge
void ClearFaceEdgeS(int i) {this->Flags() &= (~(FACEEDGESEL0<<i));}
/// This function checks if a given side of the face is a feature/internal edge
/// it is used by some importer to mark internal

View File

@ -289,40 +289,12 @@ public:
//assert(f->FFp(z)==f); // f is border along j
}
/// Finds the next Crease half-edge border
/// TODO change crease flag with something more generic (per edge)
void NextCrease( )
{
assert(f->V(f->Prev(z))!=v && (f->V(f->Next(z))==v || f->V(z)==v));
assert(IsCrease()); // f is border along j
// Si deve cambiare faccia intorno allo stesso vertice v
//finche' non si trova una faccia di bordo.
do
{
FlipE();
if (!IsCrease()) FlipF();
}
while(!IsCrease());
// L'edge j e' di bordo e deve contenere v
assert(IsCrease() &&( f->V(z)==v || f->V(f->Next(z))==v ));
FlipV();
assert(f->V(f->Prev(z))!=v && (f->V(f->Next(z))==v || f->V(z)==v));
}
/// Checks if the half-edge is of border
bool IsBorder()const
{
return face::IsBorder(*f,z);
}
/// Checks if the half-edge is of crease
bool IsCrease() const
{
return f->IsCrease(z);
}
bool IsFaux() const
{
return (f->IsF(z));
@ -333,6 +305,10 @@ public:
return face::IsManifold(*f,z);
}
bool IsFaceS() const { return f->IsS();}
bool IsEdgeS() const { return f->IsFaceEdgeS(z);}
bool IsVertS() const { return v->IsS();}
/*!
* Returns the angle (in radiant) between the two edges incident on V.
*/

View File

@ -564,5 +564,116 @@ vcg::Box3<typename PolygonType::ScalarType> PolygonBox(const PolygonType &F)
bb.Add(F.V(j)->P());
return bb;
}
template<class PolygonType>
typename PolygonType::ScalarType PolygonTorsion(const PolygonType &F,int side)
{
typedef typename PolygonType::CoordType CoordType;
typedef typename PolygonType::ScalarType ScalarType;
assert(side>=0);
assert(side<2);
assert(F.VN()==4);
//get firts two edges directions
CoordType Dir0,Dir1;
if (side==0)
{
Dir0=F.cP(1)-F.cP(0);
Dir1=F.cP(2)-F.cP(3);
}
else
{
Dir0=F.cP(2)-F.cP(1);
Dir1=F.cP(3)-F.cP(0);
}
Dir0.Normalize();
Dir1.Normalize();
//then make them lying on face's Normal
CoordType DirPlane0=Dir0*0.5+Dir1*0.5;
CoordType DirPlane1=F.cN();
CoordType NormPlane=DirPlane0^DirPlane1;
NormPlane.Normalize();
CoordType subV0=NormPlane*(NormPlane*Dir0);
CoordType subV1=NormPlane*(NormPlane*Dir1);
Dir0-=subV0;
Dir1-=subV1;
Dir0.Normalize();
Dir1.Normalize();
ScalarType AngleVal=vcg::Angle(Dir0,Dir1);
return AngleVal;
}
template<class PolygonType>
typename PolygonType::ScalarType PolygonBending(const PolygonType &F,int side)
{
typedef typename PolygonType::CoordType CoordType;
typedef typename PolygonType::ScalarType ScalarType;
assert(side>=0);
assert(side<2);
assert(F.VN()==4);
//get firts two edges directions
CoordType Norm0,Norm1;
CoordType Avg0,Avg1;
if (side==0)
{
Norm0=F.V(0)->N()*0.5+F.V(1)->N()*0.5;
Avg0=F.cP(0)*0.5+F.cP(1)*0.5;
Norm1=F.V(2)->N()*0.5+F.V(3)->N()*0.5;
Avg1=F.cP(2)*0.5+F.cP(3)*0.5;
}
else
{
Norm0=F.V(2)->N()*0.5+F.V(1)->N()*0.5;
Avg0=F.cP(2)*0.5+F.cP(1)*0.5;
Norm1=F.V(3)->N()*0.5+F.V(0)->N()*0.5;
Avg1=F.cP(3)*0.5+F.cP(0)*0.5;
}
Norm0.Normalize();
Norm1.Normalize();
//then make them lying on face's Normal
CoordType DirPlane0=Avg0-Avg1;
DirPlane0.Normalize();
CoordType DirPlane1=F.cN();
CoordType NormPlane=DirPlane0^DirPlane1;
NormPlane.Normalize();
CoordType subV0=NormPlane*(NormPlane*Norm0);
CoordType subV1=NormPlane*(NormPlane*Norm1);
Norm0-=subV0;
Norm1-=subV1;
Norm0.Normalize();
Norm1.Normalize();
ScalarType AngleVal=vcg::Angle(Norm0,Norm1);
return AngleVal;
}
template<class PolygonType>
typename PolygonType::ScalarType PolygonBending(const PolygonType &F)
{
typedef typename PolygonType::ScalarType ScalarType;
ScalarType Bend0=PolygonBending(F,0);
ScalarType Bend1=PolygonBending(F,1);
assert(Bend0>=0);
assert(Bend1>=0);
return (std::max(Bend0,Bend1));
}
template<class PolygonType>
typename PolygonType::ScalarType PolygonTorsion(const PolygonType &F)
{
typedef typename PolygonType::ScalarType ScalarType;
ScalarType Torsion0=PolygonTorsion(F,0);
ScalarType Torsion1=PolygonTorsion(F,1);
assert(Torsion0>=0);
assert(Torsion1>=0);
return (std::max(Torsion0,Torsion1));
}
}
#endif // POLYGON_H