vcglib/vcg/complex/trimesh/clean.h

774 lines
20 KiB
C++

/****************************************************************************
* VCGLib o o *
* Visual and Computer Graphics Library o o *
* _ O _ *
* Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
/****************************************************************************
History
$Log: not supported by cvs2svn $
Revision 1.25 2005/12/21 13:09:03 corsini
Modify genus computation
Revision 1.24 2005/12/19 15:13:06 corsini
Fix IsOrientedMesh
Revision 1.23 2005/12/16 13:13:44 cignoni
Reimplemented SelfIntersection
Revision 1.22 2005/12/16 10:54:59 corsini
Reimplement isOrientedMesh
Revision 1.21 2005/12/16 10:53:39 corsini
Take account for deletion in isComplexManifold
Revision 1.20 2005/12/16 10:51:43 corsini
Take account for deletion in isRegularMesh
Revision 1.19 2005/12/15 13:53:13 corsini
Reimplement isComplexManifold
Reimplement isRegular
Revision 1.18 2005/12/14 14:04:35 corsini
Fix genus computation
Revision 1.17 2005/12/12 12:11:40 cignoni
Removed unuseful detectunreferenced
Revision 1.16 2005/12/04 00:25:00 cignoni
Changed DegeneratedFaces -> RemoveZeroAreaFaces
Revision 1.15 2005/12/03 22:34:25 cignoni
Added missing include and sdt:: (tnx to Mario Latronico)
Revision 1.14 2005/12/02 00:14:43 cignoni
Removed some pointer vs iterator issues that prevented gcc compilation
Revision 1.13 2005/11/22 14:04:10 rita_borgo
Completed and tested self-intersection routine
Revision 1.12 2005/11/17 00:41:07 cignoni
Removed Initialize use updateflags::Clear() instead.
Revision 1.11 2005/11/16 16:33:23 rita_borgo
Changed ComputeSelfintersection
Revision 1.10 2005/11/15 12:16:34 rita_borgo
Changed DegeneratedFaces, sets the D flags for each faces
that is found to be degenerated.
CounEdges and ConnectedComponents check now if a face IsD()
else for degenerated faces many asserts fail.
Revision 1.9 2005/11/14 09:28:18 cignoni
changed access to face functions (border, area)
removed some typecast warnings
Revision 1.8 2005/10/11 16:03:40 rita_borgo
Added new functions belonging to triMeshInfo
Started the Self-Intersection routine
Revision 1.7 2005/10/03 15:57:53 rita_borgo
Alligned with TriMeshInfo Code
Revision 1.6 2005/01/28 11:59:35 cignoni
Add std:: to stl containers
Revision 1.5 2004/09/20 08:37:57 cignoni
Better Doxygen docs
Revision 1.4 2004/08/25 15:15:26 ganovelli
minor changes to comply gcc compiler (typename's and stuff)
Revision 1.3 2004/07/18 06:55:37 cignoni
NewUserBit -> NewBitFlag
Revision 1.2 2004/07/09 15:48:37 tarini
Added an include (<algorithm>)
Revision 1.1 2004/06/24 08:03:59 cignoni
Initial Release
****************************************************************************/
#ifndef __VCGLIB_CLEAN
#define __VCGLIB_CLEAN
// Standard headers
#include <map>
#include <algorithm>
#include <stack>
// VCG headers
#include <vcg/simplex/face/face.h>
#include <vcg/simplex/face/pos.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/closest.h>
#include <vcg/space/index/grid_static_ptr.h>
#include<vcg/complex/trimesh/allocate.h>
namespace vcg {
namespace tri{
///
/** \addtogroup trimesh */
/*@{*/
/// Class of static functions to clean/correct/restore meshs.
template <class CleanMeshType>
class Clean
{
public:
typedef CleanMeshType MeshType;
typedef typename MeshType::VertexType VertexType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::FaceType FaceType;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::FaceIterator FaceIterator;
typedef typename MeshType::FaceContainer FaceContainer;
typedef GridStaticPtr<FaceType, ScalarType > TriMeshGrid;
typedef Point3<ScalarType> Point3x;
TriMeshGrid gM;
FaceIterator fi;
FaceIterator gi;
vcg::face::Pos<FaceType> he;
vcg::face::Pos<FaceType> hei;
/* classe di confronto per l'algoritmo di eliminazione vertici duplicati*/
class RemoveDuplicateVert_Compare{
public:
inline bool operator()(VertexPointer const &a, VertexPointer const &b)
{
return (*a).cP() < (*b).cP();
}
};
/** This function removes all duplicate vertices of the mesh by looking only at their spatial positions.
Note that it does not update any topology relation that could be affected by this like the VT or TT relation.
the reason this function is usually performed BEFORE building any topology information.
*/
static int RemoveDuplicateVertex( MeshType & m ) // V1.0
{
if(m.vert.size()==0 || m.vn==0) return 0;
std::map<VertexPointer, VertexPointer> mp;
int i,j;
VertexIterator vi;
int deleted=0;
int k=0;
size_t num_vert = m.vert.size();
std::vector<VertexPointer> perm(num_vert);
for(vi=m.vert.begin(); vi!=m.vert.end(); ++vi, ++k)
perm[k] = &(*vi);
RemoveDuplicateVert_Compare c_obj;
std::sort(perm.begin(),perm.end(),c_obj);
j = 0;
i = j;
mp[perm[i]] = perm[j];
++i;
for(;i!=num_vert;)
{
if( (! (*perm[i]).IsD()) &&
(! (*perm[j]).IsD()) &&
(*perm[i]).P() == (*perm[j]).cP() )
{
VertexPointer t = perm[i];
mp[perm[i]] = perm[j];
++i;
(*t).SetD();
deleted++;
}
else
{
j = i;
++i;
}
}
FaceIterator fi;
for(fi = m.face.begin(); fi!=m.face.end(); ++fi)
for(k = 0; k < 3; ++k)
if( !(*fi).IsD() )
if( mp.find( (typename MeshType::VertexPointer)(*fi).V(k) ) != mp.end() )
{
(*fi).V(k) = &*mp[ (*fi).V(k) ];
}
m.vn -= deleted;
return deleted;
}
/** This function removes that are not referenced by any face. The function updates the vn counter.
@param m The mesh
@return The number of removed vertices
*/
static int RemoveUnreferencedVertex( MeshType& m, bool DeleteVertexFlag=true) // V1.0
{
FaceIterator fi;
VertexIterator vi;
int referredBit = VertexType::NewBitFlag();
int j;
int deleted = 0;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
(*vi).ClearUserBit(referredBit);
for(fi=m.face.begin();fi!=m.face.end();++fi)
if( !(*fi).IsD() )
for(j=0;j<3;++j)
(*fi).V(j)->SetUserBit(referredBit);
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if( (!(*vi).IsD()) && (!(*vi).IsUserBit(referredBit)))
{
if(DeleteVertexFlag)
{
(*vi).SetD();
--m.vn;
}
++deleted;
}
VertexType::DeleteBitFlag(referredBit);
return deleted;
}
static int RemoveZeroAreaFace(MeshType& m, ScalarType epsilon=0)
{
FaceIterator fi;
int count_fd = 0;
for(fi=m.face.begin(); fi!=m.face.end();++fi)
if(Area<FaceType>(*fi) <= epsilon)
{
count_fd++;
fi->SetD();
m.fn--;
}
return count_fd;
}
/**
* Check if the mesh is a manifold.
*
* First of all, for each face the FF condition is checked.
* Then, a second test is performed: for each vertex the
* number of face found have to be the same of the number of
* face found with the VF walk trough.
*/
static bool IsComplexManifold( MeshType & m )
{
bool flagManifold = true;
VertexIterator vi;
FaceIterator fi;
// First Test
assert(m.HasFFTopology());
for (fi = m.face.begin(); fi != m.face.end(); ++fi)
{
if (!fi->IsD())
{
if ((!IsManifold(*fi,0))||
(!IsManifold(*fi,1))||
(!IsManifold(*fi,2)))
{
flagManifold = false;
break;
}
}
}
// Second Test
if (flagManifold)
{
assert(m.HasVFTopology());
face::VFIterator<FaceType> vfi;
int starSizeFF;
int starSizeVF;
for (vi = m.vert.begin(); vi != m.vert.end(); ++vi)
{
if (!vi->IsD())
{
face::VFIterator<FaceType> vfi(&*vi);
face::Pos<FaceType> pos((*vi).VFp(), &*vi);
starSizeFF = pos.StarSize();
starSizeVF = 0;
while(!vfi.End())
{
++vfi;
starSizeVF++;
}
if (starSizeFF != starSizeVF)
{
flagManifold = false;
break;
}
}
}
}
return flagManifold;
}
static void CountEdges( MeshType & m, int &count_e, int &boundary_e )
{
FaceIterator fi;
vcg::face::Pos<FaceType> he;
vcg::face::Pos<FaceType> hei;
bool counted =false;
for(fi=m.face.begin();fi!=m.face.end();fi++)
{
if(!((*fi).IsD()))
{
(*fi).SetS();
count_e +=3; //assume that we have to increase the number of edges with three
for(int j=0; j<3; j++)
{
if (face::IsBorder(*fi,j)) //If this edge is a border edge
boundary_e++; // then increase the number of boundary edges
else if (IsManifold(*fi,j))//If this edge is manifold
{
if((*fi).FFp(j)->IsS()) //If the face on the other side of the edge is already selected
count_e--; // we counted one edge twice
}
else//We have a non-manifold edge
{
hei.Set(&(*fi), j , fi->V(j));
he=hei;
he.NextF();
while (he.f!=hei.f)// so we have to iterate all faces that are connected to this edge
{
if (he.f->IsS())// if one of the other faces was already visited than this edge was counted already.
{
counted=true;
break;
}
else
{
he.NextF();
}
}
if (counted)
{
count_e--;
counted=false;
}
}
}
}
}
}
static int CountHoles( MeshType & m)
{
int numholes=0;
int numholev=0;
int BEdges=0;
FaceIterator fi;
FaceIterator gi;
vcg::face::Pos<FaceType> he;
vcg::face::Pos<FaceType> hei;
vector<vector<Point3x> > holes; //indices of vertices
for(fi=m.face.begin();fi!=m.face.end();++fi)
(*fi).ClearS();
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
{
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
vector<Point3x> 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.
{
Point3x newpoint = he.v->P(); //select its vertex.
if(he.v->IsS())//check if this vertex was selected already, because then we have an additional hole.
{
//cut and paste the additional hole.
vector<Point3x> hole2;
int index = 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);
numholev++;
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());
}
static int BorderEdges( MeshType & m, int numholes)
{
int BEdges = 0;
for(int i=0; i<numholes; i++)
{
if(i==numholes-1)
BEdges++;
else
BEdges++;
}
return BEdges;
}
static int ConnectedComponents(MeshType &m)
{
FaceIterator fi;
//FaceIterator gi;
vcg::face::Pos<FaceType> he;
vcg::face::Pos<FaceType> hei;
vector<int> nrfaces;
nrfaces.reserve(1);
for(fi=m.face.begin();fi!=m.face.end();++fi)
(*fi).ClearS();
//gi=m.face.begin(); fi=gi;
int Compindex=0;
std::stack<FacePointer> sf;
FacePointer gi=&*(m.face.begin());
FaceType *l;
for(fi=m.face.begin();fi!=m.face.end();++fi)
{
if(!((*fi).IsD()))
{
if (!(*fi).IsS())
{
(*fi).SetS();
//(*fi).Q()=Compindex;
nrfaces.push_back(1);
sf.push(&*fi);
while (!sf.empty())
{
gi=sf.top();
he.Set(&(*gi),0,gi->V(0));
sf.pop();
for(int j=0;j<3;++j)
{
if( !face::IsBorder(*gi,j) )
{
l=he.f->FFp(j);
if( !(*l).IsS() )
{
(*l).SetS();
sf.push(l);
}
}
}
}
Compindex++;
}
}
}
return Compindex;
}
/**
GENUS.
A topologically invariant property of a surface defined as
the largest number of non-intersecting simple closed curves that can be
drawn on the surface without separating it.
Roughly speaking, it is the number of holes in a surface.
The genus g of a closed surface, also called the geometric genus, is related to the
Euler characteristic by the relation $chi$ by $chi==2-2g$.
The genus of a connected, orientable surface is an integer representing the maximum
number of cuttings along closed simple curves without rendering the resultant
manifold disconnected. It is equal to the number of handles on it.
For general polyhedra the <em>Euler Formula</em> is:
V + F - E = 2 - 2G - B
where V is the number of vertices, F is the number of faces, E is the
number of edges, G is the genus and B is the number of <em>boundary polygons</em>.
The above formula is valid for a mesh with one single connected component.
By considering multiple connected components the formula becomes:
V + F - E = 2C - 2Gs - B
where C is the number of connected components and Gs is the sum of
the genus of all connected components.
*/
static int MeshGenus(MeshType &m, int numholes, int numcomponents, int count_e)
{
int V = m.vn;
int F = m.fn;
int E = count_e;
return -((V + F - E + numholes - 2 * numcomponents) / 2);
}
/**
* Check if the given mesh is regular, semi-regular or irregular.
*
* Each vertex of a \em regular mesh has valence 6 except for border vertices
* which have valence 4.
*
* A \em semi-regular mesh is derived from an irregular one applying
* 1-to-4 subdivision recursively. (not checked for now)
*
* All other meshes are \em irregular.
*/
static void IsRegularMesh(MeshType &m, bool &Regular, bool &Semiregular)
{
// This algorithm requires Vertex-Face topology
assert(m.HasVFTopology());
Regular = true;
VertexIterator vi;
// for each vertex the number of edges are count
for (vi = m.vert.begin(); vi != m.vert.end(); ++vi)
{
if (!vi->IsD())
{
face::Pos<FaceType> he((*vi).VFp(), &*vi);
face::Pos<FaceType> ht = he;
int n=0;
bool border=false;
do
{
++n;
ht.NextE();
if (ht.IsBorder())
border=true;
}
while (ht != he);
if (border)
n = n/2;
if ((n != 6)&&(!border && n != 4))
{
Regular = false;
break;
}
}
}
if (!Regular)
Semiregular = false;
else
{
// For now we do not account for semi-regularity
Semiregular = false;
}
}
static void IsOrientedMesh(MeshType &m, bool &Oriented, bool &Orientable)
{
// This algorithms requires FF topology
assert(m.HasFFTopology());
Orientable = true;
Oriented = true;
// Ensure that each face is deselected
FaceIterator fi;
for (fi = m.face.begin(); fi != m.face.end(); ++fi)
fi->ClearS();
// initialize stack
stack<FacePointer> faces;
// for each face of the mesh
FacePointer fp,fpaux;
int iaux;
for (fi = m.face.begin(); fi != m.face.end(); ++fi)
{
if (!fi->IsD() && !fi->IsS())
{
// each face put in the stack is selected (and oriented)
fi->SetS();
faces.push(&(*fi));
// empty the stack
while (!faces.empty())
{
fp = faces.top();
faces.pop();
// make consistently oriented the adjacent faces
for (int j = 0; j < 3; j++)
{
// get one of the adjacent face
fpaux = fp->FFp(j);
iaux = fp->FFi(j);
if (!fpaux->IsD() && fpaux != fp)
{
if (!CheckOrientation(*fpaux, iaux))
{
Oriented = false;
if (!fpaux->IsS())
{
SwapEdge(*fpaux, iaux);
assert(CheckOrientation(*fpaux, iaux));
}
else
Orientable = false;
}
// put the oriented face into the stack
if (!fpaux->IsS())
{
fpaux->SetS();
faces.push(fpaux);
}
}
}
}
}
if (!Orientable)
break;
}
}
static bool SelfIntersections(MeshType &m, std::vector<FaceType*> &ret)
{
assert(FaceType::HasMark()); // Needed by the UG
Box3< ScalarType> bbox;
TriMeshGrid gM;
ret.clear();
FaceIterator fi;
int referredBit = FaceType::NewBitFlag();
int deleted = 0;
for(fi=m.face.begin();fi!=m.face.end();++fi)
(*fi).ClearUserBit(referredBit);
std::vector<FaceType*> inBox;
gM.Set(m.face.begin(),m.face.end());
for(fi=m.face.begin();fi!=m.face.end();++fi)
{
(*fi).SetUserBit(referredBit);
(*fi).GetBBox(bbox);
vcg::trimesh::GetInBoxFace(m, gM, bbox,inBox);
bool Intersected=false;
std::vector<FaceType*>::iterator fib;
for(fib=inBox.begin();fib!=inBox.end();++fib)
{
if(!(*fib)->IsUserBit(referredBit) && (*fib != &*fi) )
if(TestIntersection(&*fi,*fib)){
ret.push_back(*fib);
if(!Intersected) {
ret.push_back(&*fi);
Intersected=true;
}
}
}
inBox.clear();
}
FaceType::DeleteBitFlag(referredBit);
return (ret.size()>0);
}
//test real intersection between faces
static bool TestIntersection(FaceType *f0,FaceType *f1)
{
assert((!f0->IsD())&&(!f1->IsD()));
//no adiacent faces
if ( (f0!=f1) && (!ShareEdge(f0,f1))
&& (!ShareVertex(f0,f1)) )
return (vcg::Intersection<FaceType>((*f0),(*f1)));
return false;
}
//control if two faces share an edge
static bool ShareEdge(FaceType *f0,FaceType *f1)
{
assert((!f0->IsD())&&(!f1->IsD()));
for (int i=0;i<3;i++)
if (f0->FFp(i)==f1)
return (true);
return(false);
}
//control if two faces share a vertex
static bool ShareVertex(FaceType *f0,FaceType *f1)
{
assert((!f0->IsD())&&(!f1->IsD()));
for (int i=0;i<3;i++)
for (int j=0;j<3;j++)
if (f0->V(i)==f1->V(j))
return (true);
return(false);
}
}; // end class
/*@}*/
} //End Namespace Tri
} // End Namespace vcg
#endif