Merge branch 'devel' of https://github.com/cnr-isti-vclab/vcglib into devel
This commit is contained in:
commit
1980a4a611
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -184,9 +184,11 @@ public:
|
|||
*
|
||||
*/
|
||||
|
||||
bool MarkFauxEdgeWithPolyLine(MeshType &poly,bool markFlag=true)
|
||||
{
|
||||
if(markFlag) tri::UpdateFlags<MeshType>::FaceSetF(base);
|
||||
bool TagFaceEdgeSelWithPolyLine(MeshType &poly,bool markFlag=true)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -222,8 +226,7 @@ public:
|
|||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ScalarType MinDistOnEdge(CoordType samplePnt, EdgeGrid &edgeGrid, MeshType &poly, CoordType &closestPoint)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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++)
|
||||
|
|
|
@ -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){
|
||||
int orgflag = (*fi).Flags();
|
||||
for (i=0; i<SplitTab[ind].TriNum; ++i)
|
||||
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]];
|
||||
(*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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)]);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue