Refactored a number of cleaning algorithms
Issues resolved: - removed assert and used the correct meshassert exceptions - removed wrong use of selection instead of visiting flag (various filters destroyed selection when called) - rewrote a totally clumsy count hole.
This commit is contained in:
parent
0b135dbc01
commit
f9169b8ec2
|
|
@ -79,8 +79,6 @@ public:
|
||||||
mp=&m;
|
mp=&m;
|
||||||
while(!sf.empty()) sf.pop();
|
while(!sf.empty()) sf.pop();
|
||||||
UnMarkAll(m);
|
UnMarkAll(m);
|
||||||
assert(p);
|
|
||||||
assert(!p->IsD());
|
|
||||||
tri::Mark(m,p);
|
tri::Mark(m,p);
|
||||||
sf.push(p);
|
sf.push(p);
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +270,6 @@ public:
|
||||||
tri::Index(m,(*fi).V(2)),
|
tri::Index(m,(*fi).V(2)),
|
||||||
&*fi));
|
&*fi));
|
||||||
}
|
}
|
||||||
assert (size_t(m.fn) == fvec.size());
|
|
||||||
std::sort(fvec.begin(),fvec.end());
|
std::sort(fvec.begin(),fvec.end());
|
||||||
int total=0;
|
int total=0;
|
||||||
for(int i=0;i<int(fvec.size())-1;++i)
|
for(int i=0;i<int(fvec.size())-1;++i)
|
||||||
|
|
@ -300,8 +297,6 @@ public:
|
||||||
{
|
{
|
||||||
eVec.push_back(SortedPair( tri::Index(m,(*ei).V(0)), tri::Index(m,(*ei).V(1)), &*ei));
|
eVec.push_back(SortedPair( tri::Index(m,(*ei).V(0)), tri::Index(m,(*ei).V(1)), &*ei));
|
||||||
}
|
}
|
||||||
assert (size_t(m.en) == eVec.size());
|
|
||||||
//for(int i=0;i<fvec.size();++i) qDebug("fvec[%i] = (%i %i %i)(%i)",i,fvec[i].v[0],fvec[i].v[1],fvec[i].v[2],tri::Index(m,fvec[i].fp));
|
|
||||||
std::sort(eVec.begin(),eVec.end());
|
std::sort(eVec.begin(),eVec.end());
|
||||||
int total=0;
|
int total=0;
|
||||||
for(int i=0;i<int(eVec.size())-1;++i)
|
for(int i=0;i<int(eVec.size())-1;++i)
|
||||||
|
|
@ -310,7 +305,6 @@ public:
|
||||||
{
|
{
|
||||||
total++;
|
total++;
|
||||||
tri::Allocator<MeshType>::DeleteEdge(m, *(eVec[i].fp) );
|
tri::Allocator<MeshType>::DeleteEdge(m, *(eVec[i].fp) );
|
||||||
//qDebug("deleting face %i (pos in fvec %i)",tri::Index(m,fvec[i].fp) ,i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
|
|
@ -440,12 +434,10 @@ public:
|
||||||
CountNonManifoldVertexFF(m,true);
|
CountNonManifoldVertexFF(m,true);
|
||||||
tri::UpdateSelection<MeshType>::FaceFromVertexLoose(m);
|
tri::UpdateSelection<MeshType>::FaceFromVertexLoose(m);
|
||||||
int count_removed = 0;
|
int count_removed = 0;
|
||||||
FaceIterator fi;
|
for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi)
|
||||||
for(fi=m.face.begin(); fi!=m.face.end();++fi)
|
|
||||||
if(!(*fi).IsD() && (*fi).IsS())
|
if(!(*fi).IsD() && (*fi).IsS())
|
||||||
Allocator<MeshType>::DeleteFace(m,*fi);
|
Allocator<MeshType>::DeleteFace(m,*fi);
|
||||||
VertexIterator vi;
|
for(VertexIterator vi=m.vert.begin(); vi!=m.vert.end();++vi)
|
||||||
for(vi=m.vert.begin(); vi!=m.vert.end();++vi)
|
|
||||||
if(!(*vi).IsD() && (*vi).IsS()) {
|
if(!(*vi).IsD() && (*vi).IsS()) {
|
||||||
++count_removed;
|
++count_removed;
|
||||||
Allocator<MeshType>::DeleteVertex(m,*vi);
|
Allocator<MeshType>::DeleteVertex(m,*vi);
|
||||||
|
|
@ -631,22 +623,15 @@ public:
|
||||||
return count_fd;
|
return count_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Remove the faces that are out of a given range of area */
|
||||||
The following functions remove faces that are geometrically "bad" according to edges and area criteria.
|
static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits<ScalarType>::max)(), bool OnlyOnSelected=false)
|
||||||
They remove the faces that are out of a given range of area or edges (e.g. faces too large or too small, or with edges too short or too long)
|
|
||||||
but that could be topologically correct.
|
|
||||||
These functions can optionally take into account only the selected faces.
|
|
||||||
*/
|
|
||||||
template<bool Selected>
|
|
||||||
static int RemoveFaceOutOfRangeAreaSel(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits<ScalarType>::max)())
|
|
||||||
{
|
{
|
||||||
FaceIterator fi;
|
|
||||||
int count_fd = 0;
|
int count_fd = 0;
|
||||||
MinAreaThr*=2;
|
MinAreaThr*=2;
|
||||||
MaxAreaThr*=2;
|
MaxAreaThr*=2;
|
||||||
for(fi=m.face.begin(); fi!=m.face.end();++fi)
|
for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi){
|
||||||
if(!(*fi).IsD())
|
if(!(*fi).IsD())
|
||||||
if(!Selected || (*fi).IsS())
|
if(!OnlyOnSelected || (*fi).IsS())
|
||||||
{
|
{
|
||||||
const ScalarType doubleArea=DoubleArea<FaceType>(*fi);
|
const ScalarType doubleArea=DoubleArea<FaceType>(*fi);
|
||||||
if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) )
|
if((doubleArea<=MinAreaThr) || (doubleArea>=MaxAreaThr) )
|
||||||
|
|
@ -655,17 +640,13 @@ public:
|
||||||
count_fd++;
|
count_fd++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return count_fd;
|
return count_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// alias for the old style. Kept for backward compatibility
|
static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m,0);}
|
||||||
static int RemoveZeroAreaFace(MeshType& m) { return RemoveFaceOutOfRangeArea(m);}
|
|
||||||
|
|
||||||
// Aliases for the functions that do not look at selection
|
|
||||||
static int RemoveFaceOutOfRangeArea(MeshType& m, ScalarType MinAreaThr=0, ScalarType MaxAreaThr=(std::numeric_limits<ScalarType>::max)())
|
|
||||||
{
|
|
||||||
return RemoveFaceOutOfRangeAreaSel<false>(m,MinAreaThr,MaxAreaThr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the mesh only composed by quadrilaterals?
|
* Is the mesh only composed by quadrilaterals?
|
||||||
|
|
@ -850,7 +831,7 @@ public:
|
||||||
*/
|
*/
|
||||||
static int CountNonManifoldEdgeEE( MeshType & m, bool SelectFlag=false)
|
static int CountNonManifoldEdgeEE( MeshType & m, bool SelectFlag=false)
|
||||||
{
|
{
|
||||||
assert(m.fn == 0 && m.en >0); // just to be sure we are using an edge mesh...
|
MeshAssert<MeshType>::OnlyEdgeMesh(m);
|
||||||
RequireEEAdjacency(m);
|
RequireEEAdjacency(m);
|
||||||
tri::UpdateTopology<MeshType>::EdgeEdge(m);
|
tri::UpdateTopology<MeshType>::EdgeEdge(m);
|
||||||
|
|
||||||
|
|
@ -1031,61 +1012,27 @@ public:
|
||||||
|
|
||||||
static int CountHoles( MeshType & m)
|
static int CountHoles( MeshType & m)
|
||||||
{
|
{
|
||||||
int numholev=0;
|
UpdateFlags<MeshType>::FaceClearV(m);
|
||||||
FaceIterator fi;
|
int loopNum=0;
|
||||||
|
for(FaceIterator fi=m.face.begin(); fi!=m.face.end();++fi) if(!fi->IsD())
|
||||||
FaceIterator gi;
|
|
||||||
vcg::face::Pos<FaceType> he;
|
|
||||||
vcg::face::Pos<FaceType> hei;
|
|
||||||
|
|
||||||
std::vector< std::vector<CoordType> > holes; //indices of vertices
|
|
||||||
|
|
||||||
vcg::tri::UpdateFlags<MeshType>::VertexClearS(m);
|
|
||||||
|
|
||||||
gi=m.face.begin(); fi=gi;
|
|
||||||
|
|
||||||
for(fi=m.face.begin();fi!=m.face.end();fi++)//for all faces do
|
|
||||||
{
|
{
|
||||||
for(int j=0;j<3;j++)//for all edges
|
for(int j=0;j<3;++j)
|
||||||
{
|
|
||||||
if(fi->V(j)->IsS()) continue;
|
|
||||||
|
|
||||||
if(face::IsBorder(*fi,j))//found an unvisited border edge
|
|
||||||
{
|
{
|
||||||
he.Set(&(*fi),j,fi->V(j)); //set the face-face iterator to the current face, edge and vertex
|
if(!fi->IsV() && face::IsBorder(*fi,j))
|
||||||
std::vector<CoordType> hole; //start of a new hole
|
|
||||||
hole.push_back(fi->P(j)); // including the first vertex
|
|
||||||
numholev++;
|
|
||||||
he.v->SetS(); //set the current vertex as selected
|
|
||||||
he.NextB(); //go to the next boundary edge
|
|
||||||
|
|
||||||
|
|
||||||
while(fi->V(j) != he.v)//will we do not encounter the first boundary edge.
|
|
||||||
{
|
{
|
||||||
CoordType newpoint = he.v->P(); //select its vertex.
|
face::Pos<FaceType> startPos(&*fi,j);
|
||||||
if(he.v->IsS())//check if this vertex was selected already, because then we have an additional hole.
|
face::Pos<FaceType> curPos=startPos;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
//cut and paste the additional hole.
|
curPos.NextB();
|
||||||
std::vector<CoordType> hole2;
|
curPos.F()->SetV();
|
||||||
int index = static_cast<int>(find(hole.begin(),hole.end(),newpoint)
|
|
||||||
- hole.begin());
|
|
||||||
for(unsigned int i=index; i<hole.size(); i++)
|
|
||||||
hole2.push_back(hole[i]);
|
|
||||||
|
|
||||||
hole.resize(index);
|
|
||||||
if(hole2.size()!=0) //annoying in degenerate cases
|
|
||||||
holes.push_back(hole2);
|
|
||||||
}
|
}
|
||||||
hole.push_back(newpoint);
|
while(curPos!=startPos);
|
||||||
numholev++;
|
++loopNum;
|
||||||
he.v->SetS(); //set the current vertex as selected
|
|
||||||
he.NextB(); //go to the next boundary edge
|
|
||||||
}
|
}
|
||||||
holes.push_back(hole);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return static_cast<int>(holes.size());
|
return loopNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -1102,14 +1049,14 @@ public:
|
||||||
{
|
{
|
||||||
tri::RequireFFAdjacency(m);
|
tri::RequireFFAdjacency(m);
|
||||||
CCV.clear();
|
CCV.clear();
|
||||||
tri::UpdateSelection<MeshType>::FaceClear(m);
|
tri::UpdateFlags<MeshType>::FaceClearV(m);
|
||||||
std::stack<FacePointer> sf;
|
std::stack<FacePointer> sf;
|
||||||
FacePointer fpt=&*(m.face.begin());
|
FacePointer fpt=&*(m.face.begin());
|
||||||
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
for(FaceIterator fi=m.face.begin();fi!=m.face.end();++fi)
|
||||||
{
|
{
|
||||||
if(!((*fi).IsD()) && !(*fi).IsS())
|
if(!((*fi).IsD()) && !(*fi).IsV())
|
||||||
{
|
{
|
||||||
(*fi).SetS();
|
(*fi).SetV();
|
||||||
CCV.push_back(std::make_pair(0,&*fi));
|
CCV.push_back(std::make_pair(0,&*fi));
|
||||||
sf.push(&*fi);
|
sf.push(&*fi);
|
||||||
while (!sf.empty())
|
while (!sf.empty())
|
||||||
|
|
@ -1122,9 +1069,9 @@ public:
|
||||||
if( !face::IsBorder(*fpt,j) )
|
if( !face::IsBorder(*fpt,j) )
|
||||||
{
|
{
|
||||||
FacePointer l = fpt->FFp(j);
|
FacePointer l = fpt->FFp(j);
|
||||||
if( !(*l).IsS() )
|
if( !(*l).IsV() )
|
||||||
{
|
{
|
||||||
(*l).SetS();
|
(*l).SetV();
|
||||||
sf.push(l);
|
sf.push(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1260,6 +1207,8 @@ public:
|
||||||
|
|
||||||
static bool IsCoherentlyOrientedMesh(MeshType &m)
|
static bool IsCoherentlyOrientedMesh(MeshType &m)
|
||||||
{
|
{
|
||||||
|
RequireFFAdjacency(m);
|
||||||
|
MeshAssert<MeshType>::FFAdjacencyIsInitialized(m);
|
||||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||||
if (!fi->IsD())
|
if (!fi->IsD())
|
||||||
for(int i=0;i<3;++i)
|
for(int i=0;i<3;++i)
|
||||||
|
|
@ -1269,26 +1218,22 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OrientCoherentlyMesh(MeshType &m, bool &Oriented, bool &Orientable)
|
static void OrientCoherentlyMesh(MeshType &m, bool &_IsOriented, bool &_IsOrientable)
|
||||||
{
|
{
|
||||||
RequireFFAdjacency(m);
|
RequireFFAdjacency(m);
|
||||||
assert(&Oriented != &Orientable);
|
MeshAssert<MeshType>::FFAdjacencyIsInitialized(m);
|
||||||
assert(m.face.back().FFp(0)); // This algorithms require FF topology initialized
|
bool IsOrientable = true;
|
||||||
|
bool IsOriented = true;
|
||||||
|
|
||||||
Orientable = true;
|
UpdateFlags<MeshType>::FaceClearV(m);
|
||||||
Oriented = true;
|
|
||||||
|
|
||||||
tri::UpdateSelection<MeshType>::FaceClear(m);
|
|
||||||
std::stack<FacePointer> faces;
|
std::stack<FacePointer> faces;
|
||||||
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
for (FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
|
||||||
{
|
{
|
||||||
if (!fi->IsD() && !fi->IsS())
|
if (!fi->IsD() && !fi->IsV())
|
||||||
{
|
{
|
||||||
// each face put in the stack is selected (and oriented)
|
// each face put in the stack is selected (and oriented)
|
||||||
fi->SetS();
|
fi->SetV();
|
||||||
faces.push(&(*fi));
|
faces.push(&(*fi));
|
||||||
|
|
||||||
// empty the stack
|
|
||||||
while (!faces.empty())
|
while (!faces.empty())
|
||||||
{
|
{
|
||||||
FacePointer fp = faces.top();
|
FacePointer fp = faces.top();
|
||||||
|
|
@ -1297,42 +1242,35 @@ public:
|
||||||
// make consistently oriented the adjacent faces
|
// make consistently oriented the adjacent faces
|
||||||
for (int j = 0; j < 3; j++)
|
for (int j = 0; j < 3; j++)
|
||||||
{
|
{
|
||||||
// get one of the adjacent face
|
if (!face::IsBorder(*fp,j) && face::IsManifold<FaceType>(*fp, j))
|
||||||
FacePointer fpaux = fp->FFp(j);
|
|
||||||
int iaux = fp->FFi(j);
|
|
||||||
|
|
||||||
if (!fpaux->IsD() && fpaux != fp && face::IsManifold<FaceType>(*fp, j))
|
|
||||||
{
|
{
|
||||||
|
FacePointer fpaux = fp->FFp(j);
|
||||||
|
int iaux = fp->FFi(j);
|
||||||
if (!CheckOrientation(*fpaux, iaux))
|
if (!CheckOrientation(*fpaux, iaux))
|
||||||
{
|
{
|
||||||
Oriented = false;
|
IsOriented = false;
|
||||||
|
|
||||||
if (!fpaux->IsS())
|
if (!fpaux->IsV())
|
||||||
{
|
|
||||||
face::SwapEdge<FaceType,true>(*fpaux, iaux);
|
face::SwapEdge<FaceType,true>(*fpaux, iaux);
|
||||||
assert(CheckOrientation(*fpaux, iaux));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Orientable = false;
|
IsOrientable = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!fpaux->IsV())
|
||||||
// put the oriented face into the stack
|
|
||||||
|
|
||||||
if (!fpaux->IsS())
|
|
||||||
{
|
{
|
||||||
fpaux->SetS();
|
fpaux->SetV();
|
||||||
faces.push(fpaux);
|
faces.push(fpaux);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!IsOrientable) break;
|
||||||
if (!Orientable) break;
|
|
||||||
}
|
}
|
||||||
|
_IsOriented = IsOriented;
|
||||||
|
_IsOrientable = IsOrientable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1666,7 +1604,6 @@ public:
|
||||||
*/
|
*/
|
||||||
static bool TestFaceFaceIntersection(FaceType *f0,FaceType *f1)
|
static bool TestFaceFaceIntersection(FaceType *f0,FaceType *f1)
|
||||||
{
|
{
|
||||||
assert(f0!=f1);
|
|
||||||
int sv = face::CountSharedVertex(f0,f1);
|
int sv = face::CountSharedVertex(f0,f1);
|
||||||
if(sv==3) return true;
|
if(sv==3) return true;
|
||||||
if(sv==0) return (vcg::IntersectionTriangleTriangle<FaceType>((*f0),(*f1)));
|
if(sv==0) return (vcg::IntersectionTriangleTriangle<FaceType>((*f0),(*f1)));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue