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:
Paolo Cignoni 2016-11-02 12:11:18 +01:00
parent 0b135dbc01
commit f9169b8ec2
1 changed files with 48 additions and 111 deletions

View File

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