Added a test to avoid degenerative flip which produce two identical overlapping faces.

Little code refactoring.
The planar swap now try to improve the average quality of faces, instead of improving the quality of the worst face.
This commit is contained in:
Paolo Cignoni 2008-03-20 15:45:54 +00:00
parent c2bec8758d
commit 3e090a41ff
1 changed files with 365 additions and 351 deletions

View File

@ -1,25 +1,25 @@
/**************************************************************************** /****************************************************************************
* VCGLib o o * * VCGLib o o *
* Visual and Computer Graphics Library o o * * Visual and Computer Graphics Library o o *
* _ O _ * * _ O _ *
* Copyright(C) 2004 \/)\/ * * Copyright(C) 2004 \/)\/ *
* Visual Computing Lab /\/| * * Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | * * ISTI - Italian National Research Council | *
* \ * * \ *
* All rights reserved. * * All rights reserved. *
* * * *
* This program is free software; you can redistribute it and/or modify * * 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 * * it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or * * the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. * * (at your option) any later version. *
* * * *
* This program is distributed in the hope that it will be useful, * * This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of * * but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. * * for more details. *
* * * *
****************************************************************************/ ****************************************************************************/
#include <vcg/complex/local_optimization.h> #include <vcg/complex/local_optimization.h>
#include <vcg/simplex/face/topology.h> #include <vcg/simplex/face/topology.h>
@ -28,371 +28,385 @@
namespace vcg namespace vcg
{ {
namespace tri namespace tri
{
/** \addtogroup trimesh */
/* @{ */
/*!
* This Class is specialization of LocalModification for the edge flip
* It wraps the atomic operation EdgeFlip to be used in a optimization routine.
* Note that it has knowledge of the heap of the class LocalOptimization because
* it is responsible of updating it after a flip has been performed
* This is the simplest edge flipping class.
* It flips an edge only if two adjacent faces are coplanar and the
* quality of the faces improves after the flip.
*/
template <class TRIMESH_TYPE, class MYTYPE> class PlanarEdgeFlip :
public LocalOptimization< TRIMESH_TYPE>::LocModType
{
protected:
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::VertexType VertexType;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef vcg::face::Pos<FaceType> PosType;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapElem HeapElem;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapType HeapType;
/*!
* the pos of the flipping
*/
PosType _pos;
/*!
* priority in the heap
*/
ScalarType _priority;
/*!
* Mark for updating
*/
int _localMark;
/*!
* mark for up_dating
*/
static int& GlobalMark()
{ {
/** \addtogroup trimesh */ static int im = 0;
/* @{ */ return im;
}
/*! public:
* This Class is specialization of LocalModification for the edge flip /*!
* It wraps the atomic operation EdgeFlip to be used in a optimization routine. * Default constructor
* Note that it has knowledge of the heap of the class LocalOptimization because */
* it is responsible of updating it after a flip has been performed inline PlanarEdgeFlip()
* This is the simplest edge flipping class. {
* It flips an edge only if two adjacent faces are coplanar and the }
* quality of the faces improves after the flip.
*/
template <class TRIMESH_TYPE, class MYTYPE>
class PlanarEdgeFlip : public LocalOptimization< TRIMESH_TYPE >::LocModType
{
protected:
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::VertexType VertexType;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef vcg::face::Pos<FaceType> PosType;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapElem HeapElem;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapType HeapType;
/*!
* the pos of the flipping
*/
PosType _pos;
/*!
* priority in the heap
*/
ScalarType _priority;
/*!
* Mark for updating
*/
int _localMark;
/*!
* mark for up_dating
*/
static int& GlobalMark()
{
static int im = 0;
return im;
}
public: /*!
/*! * Constructor with <I>pos</I> type
* Default constructor */
*/ inline PlanarEdgeFlip(PosType pos, int mark)
inline PlanarEdgeFlip() {
{} _pos = pos;
_localMark = mark;
/*! _priority = ComputePriority();
* Constructor with <I>pos</I> type }
*/
inline PlanarEdgeFlip(PosType pos, int mark)
{
_pos = pos;
_localMark = mark;
_priority = ComputePriority();
}
/*!
* Copy Constructor
*/
inline PlanarEdgeFlip(const PlanarEdgeFlip &par)
{
_pos = par.GetPos();
_localMark = par.GetMark();
_priority = par.Priority();
}
/*! /*!
*/ * Copy Constructor
~PlanarEdgeFlip() */
{ inline PlanarEdgeFlip(const PlanarEdgeFlip &par)
} {
_pos = par.GetPos();
_localMark = par.GetMark();
_priority = par.Priority();
}
/*! /*!
* Parameter */
*/ ~PlanarEdgeFlip()
static ScalarType &CoplanarAngleThresholdDeg() {
{ }
static ScalarType _CoplanarAngleThresholdDeg = 0.01f;
return _CoplanarAngleThresholdDeg;
}
inline PosType GetPos() {return _pos;}
inline int GetMark(){return _localMark;}
/*!
* Return the LocalOptimization type
*/
ModifierType IsOfType()
{
return TriEdgeFlipOp;
}
/*!
* Check if the pos is updated
*/
bool IsUpToDate()
{
int MostRecentVertexMark = _pos.F()->V(0)->IMark();
MostRecentVertexMark = vcg::math::Max<ScalarType>(MostRecentVertexMark, _pos.F()->V(1)->IMark());
MostRecentVertexMark = vcg::math::Max<ScalarType>(MostRecentVertexMark, _pos.F()->V(2)->IMark());
return ( _localMark >= MostRecentVertexMark );
}
/*!
*
Check if this flipping operation can be performed.
It is a topological and geometrical check.
*/
virtual bool IsFeasible()
{
if( math::ToDeg( Angle( _pos.FFlip()->cN() , _pos.F()->cN() ) ) > CoplanarAngleThresholdDeg() ) return false;
return vcg::face::CheckFlipEdge(*_pos.f, _pos.z);
}
/*!
* Parameter
*/
static ScalarType &CoplanarAngleThresholdDeg()
{
static ScalarType _CoplanarAngleThresholdDeg = 0.01f;
return _CoplanarAngleThresholdDeg;
}
/*! inline PosType GetPos()
* Compute the priority of this optimization {
*/ return _pos;
/* }
0
/|\
/ | \
1 | 3
\ | /
\|/
2
*/
virtual ScalarType ComputePriority()
{
CoordType v0,v1,v2,v3; inline int GetMark()
PosType app = _pos; {
return _localMark;
}
v0 = app.v->P();
app.FlipE(); app.FlipV();
v1 = app.v->P();
app.FlipE(); app.FlipV();
v2 = app.v->P();
app.FlipE(); app.FlipF(); app.FlipE(); app.FlipV();
v3 = app.v->P();
ScalarType Qa = Quality(v0,v1,v2); /*!
ScalarType Qb = Quality(v0,v2,v3); * Return the LocalOptimization type
*/
ModifierType IsOfType()
{
return TriEdgeFlipOp;
}
ScalarType QaAfter = Quality(v0,v1,v3);
ScalarType QbAfter = Quality(v1,v2,v3);
// higher the quality better the triangle. /*!
// swaps that improve the worst quality more are performed before * Check if the pos is updated
// (e.g. they have an higher priority) */
_priority = vcg::math::Max<ScalarType>(QaAfter,QbAfter) - vcg::math::Min<ScalarType>(Qa,Qb) ; bool IsUpToDate()
_priority *=-1; {
return _priority; int MostRecentVertexMark = _pos.F()->V(0)->IMark();
} MostRecentVertexMark = vcg::math::Max<ScalarType>(MostRecentVertexMark, _pos.F()->V(1)->IMark());
MostRecentVertexMark = vcg::math::Max<ScalarType>(MostRecentVertexMark, _pos.F()->V(2)->IMark());
/*! return ( _localMark >= MostRecentVertexMark );
* Return the priority of this optimization }
*/
virtual ScalarType Priority() const
{
return _priority;
}
/*! /*!
* Execute the flipping of the edge *
*/ Check if this flipping operation can be performed.
void Execute(TRIMESH_TYPE &m) It is a topological and geometrical check.
{ */
int z = _pos.z; virtual bool IsFeasible()
vcg::face::FlipEdge(*_pos.f, z); {
} if( math::ToDeg( Angle( _pos.FFlip()->cN() , _pos.F()->cN() ) )> CoplanarAngleThresholdDeg() ) return false;
/*! CoordType v0, v1, v2, v3;
*/ PosType app = _pos;
const char* Info(TRIMESH_TYPE &m) int i = _pos.I();
{ v0 = app.F()->V0(i)->P();
static char dump[60]; v1 = app.F()->V1(i)->P();
sprintf(dump,"%i -> %i %g\n", _pos.F()->V(0)-&m.vert[0], _pos.F()->V(1)-&m.vert[0],-_priority); v2 = app.F()->V2(i)->P();
return dump; app.FlipF(); app.FlipE(); app.FlipV();
} v3 = app.V()->P();
/*! // Take the parallelogram formed by the adjacent faces of edge
*/ // If a corner of the parallelogram on extreme of edge to flip is >= 180
static void Init(TRIMESH_TYPE &mesh, HeapType &heap) // the flip produce two identical faces - avoid this
{ if( (Angle(v2 - v0, v1 - v0) + Angle(v3 - v0, v1 - v0) >= M_PI) ||
heap.clear(); (Angle(v2 - v1, v0 - v1) + Angle(v3 - v1, v0 - v1) >= M_PI))
FaceIterator f_iter; return false;
for (f_iter = mesh.face.begin(); f_iter!=mesh.face.end(); ++f_iter)
{ return vcg::face::CheckFlipEdge(*_pos.f, _pos.z);
if (! (*f_iter).IsD() ) }
{
//if(!(Selected && !(*f_iter).IsS())) /*!
if( (*f_iter).V(0)->IsW() && (*f_iter).V(1)->IsW() && (*f_iter).V(2)->IsW()) * Compute the priority of this optimization
{ */
for (unsigned int i=0; i<3; i++) /*
{ 1
if( !(*f_iter).IsB(i) && (*f_iter).FFp(i)->V2((*f_iter).FFi(i) )->IsW() ) /|\
{ / | \
VertexPointer v0 = (*f_iter).V0(i); 2 | 3
VertexPointer v1 = (*f_iter).V1(i); \ | /
if (v1-v0 > 0) \|/
{ 0
heap.push_back( HeapElem( new MYTYPE(PosType(&*f_iter, i), mesh.IMark() )) ); */
} virtual ScalarType ComputePriority()
} //endif {
} //endfor CoordType v0, v1, v2, v3;
} PosType app = _pos;
} // endif int i = _pos.I();
v0 = app.F()->V0(i)->P();
v1 = app.F()->V1(i)->P();
v2 = app.F()->V2(i)->P();
app.FlipF(); app.FlipE(); app.FlipV();
v3 = app.V()->P();
ScalarType Qa = Quality(v0,v1,v2);
ScalarType Qb = Quality(v0,v3,v1);
ScalarType QaAfter = Quality(v1,v2,v3);
ScalarType QbAfter = Quality(v0,v3,v2);
// higher the quality better the triangle.
// swaps that improve the worst quality more are performed before
// (e.g. they have an higher priority)
/*_priority = vcg::math::Max<ScalarType>(QaAfter,QbAfter) - vcg::math::Min<ScalarType>(Qa,Qb) ;
_priority *= -1;*/
// < 0 if the average quality of faces improves after flip
_priority = ((Qa + Qb) / 2.0) - ((QaAfter + QbAfter) / 2.0);
return _priority;
}
/*!
* Return the priority of this optimization
*/
virtual ScalarType Priority() const
{
return _priority;
}
/*!
* Execute the flipping of the edge
*/
void Execute(TRIMESH_TYPE &m)
{
int z = _pos.z;
vcg::face::FlipEdge(*_pos.f, z);
}
/*!
*/
const char* Info(TRIMESH_TYPE &m)
{
static char dump[60];
sprintf(dump,"%i -> %i %g\n", _pos.F()->V(0)-&m.vert[0], _pos.F()->V(1)-&m.vert[0],-_priority);
return dump;
}
/*!
*/
static void Init(TRIMESH_TYPE &mesh, HeapType &heap)
{
heap.clear();
FaceIterator fi;
for(fi = mesh.face.begin(); fi != mesh.face.end(); ++fi) {
if(!(*fi).IsD() && (*fi).V(0)->IsW() && (*fi).V(1)->IsW() && (*fi).V(2)->IsW()) {
for(unsigned int i = 0; i < 3; i++) {
if( !(*fi).IsB(i) && (*fi).FFp(i)->V2((*fi).FFi(i))->IsW() ) {
if((*fi).V1(i) - (*fi).V0(i) > 0)
heap.push_back( HeapElem( new MYTYPE(PosType(&*fi, i), mesh.IMark() )) );
} //endif
} //endfor } //endfor
} }
} //endfor
}
/*! /*!
*/ */
void UpdateHeap(HeapType &heap) virtual void UpdateHeap(HeapType &heap)
{ {
GlobalMark()++; GlobalMark()++;
PosType pos(_pos.f, _pos.z); PosType pos(_pos.f, _pos.z);
pos.FlipF(); pos.FlipF();
_pos.F()->V(0)->IMark() = GlobalMark(); _pos.F()->V(0)->IMark() = GlobalMark();
_pos.F()->V(1)->IMark() = GlobalMark(); _pos.F()->V(1)->IMark() = GlobalMark();
_pos.F()->V(2)->IMark() = GlobalMark(); _pos.F()->V(2)->IMark() = GlobalMark();
pos.F()->V(2)->IMark() = GlobalMark(); pos.F()->V(2)->IMark() = GlobalMark();
PosType poss(_pos.f, _pos.z); PosType poss(_pos.f, _pos.z);
poss.FlipE();
if(!poss.IsBorder())
{
heap.push_back( HeapElem( new MYTYPE( PosType(poss.f, poss.z), GlobalMark() ) ) );
}
poss.FlipE(); poss.FlipV(); poss.FlipE(); poss.FlipV(); poss.FlipE();
if(!poss.IsBorder() ) if(!poss.IsBorder())
{ heap.push_back(HeapElem(new MYTYPE(poss, GlobalMark())));
heap.push_back( HeapElem( new MYTYPE( PosType(poss.f, poss.z), GlobalMark() ) ) );
}
pos.FlipE(); poss.FlipV(); poss.FlipE();
if(!poss.IsBorder()) if(!poss.IsBorder())
{ heap.push_back(HeapElem(new MYTYPE(poss, GlobalMark())));
heap.push_back( HeapElem( new MYTYPE( PosType(pos.f, pos.z), GlobalMark() ) ) );
}
pos.FlipE(); pos.FlipV(); pos.FlipE(); poss.FlipV(); poss.FlipE();
if(!poss.IsBorder()) poss.FlipF(); poss.FlipE();
{ if(!poss.IsBorder())
heap.push_back( HeapElem( new MYTYPE( PosType(pos.f, pos.z), GlobalMark() ) ) ); heap.push_back(HeapElem(new MYTYPE(poss, GlobalMark())));
}
std::push_heap(heap.begin(),heap.end()); poss.FlipV(); poss.FlipE();
} if(!poss.IsBorder())
}; // end of PlanarEdgeFlip class heap.push_back(HeapElem(new MYTYPE(poss, GlobalMark())));
std::push_heap(heap.begin(),heap.end());
}
}; // end of PlanarEdgeFlip class
template <class TRIMESH_TYPE, class MYTYPE> template <class TRIMESH_TYPE, class MYTYPE>
class TriEdgeFlip : public PlanarEdgeFlip<TRIMESH_TYPE, MYTYPE> class TriEdgeFlip : public PlanarEdgeFlip<TRIMESH_TYPE, MYTYPE>
{ {
protected: protected:
typedef typename TRIMESH_TYPE::FaceType FaceType; typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer; typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator; typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::VertexType VertexType; typedef typename TRIMESH_TYPE::VertexType VertexType;
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer; typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::ScalarType ScalarType; typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::CoordType CoordType; typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef vcg::face::Pos<FaceType> PosType; typedef vcg::face::Pos<FaceType> PosType;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapElem HeapElem; typedef typename LocalOptimization<TRIMESH_TYPE>::HeapElem HeapElem;
typedef typename LocalOptimization<TRIMESH_TYPE>::HeapType HeapType; typedef typename LocalOptimization<TRIMESH_TYPE>::HeapType HeapType;
typedef typename vcg::Triangle3<ScalarType> TriangleType; typedef typename vcg::Triangle3<ScalarType> TriangleType;
public: public:
/*! /*!
* Default constructor * Default constructor
*/ */
inline TriEdgeFlip() {} inline TriEdgeFlip() {}
/*! /*!
* Constructor with <I>pos</I> type * Constructor with <I>pos</I> type
*/ */
inline TriEdgeFlip(const PosType pos, int mark) inline TriEdgeFlip(const PosType pos, int mark)
{ {
this->_pos = pos; this->_pos = pos;
this->_localMark = mark; this->_localMark = mark;
this->_priority = ComputePriority(); this->_priority = ComputePriority();
} }
/*! /*!
* Copy Constructor * Copy Constructor
*/ */
inline TriEdgeFlip(const TriEdgeFlip &par) inline TriEdgeFlip(const TriEdgeFlip &par)
{ {
this->_pos = par.GetPos(); this->_pos = par.GetPos();
this->_localMark = par.GetMark(); this->_localMark = par.GetMark();
this->_priority = par.Priority(); this->_priority = par.Priority();
} }
//only topology check
/*bool IsFeasible()
{
return vcg::face::CheckFlipEdge(*this->_pos.f, this->_pos.z);
}*/
//only topology check ScalarType ComputePriority()
bool IsFeasible() {
{ /*
return vcg::face::CheckFlipEdge(*this->_pos.f, this->_pos.z); 1
} /|\
/ | \
2 | 3
\ | /
\|/
0
*/
CoordType v0, v1, v2, v3;
PosType app = this->_pos;
int i = this->_pos.I();
v0 = app.F()->V0(i)->P();
v1 = app.F()->V1(i)->P();
v2 = app.F()->V2(i)->P();
app.FlipF(); app.FlipE(); app.FlipV();
v3 = app.V()->P();
//CoordType CircumCenter = vcg::Circumcenter(*(app.F()));
CoordType circumcenter = vcg::Circumcenter(*(this->_pos.F()));
ScalarType ComputePriority() /*ScalarType Radius= Distance(v0,CircumCenter);
{ ScalarType Radius1= Distance(v1,CircumCenter);
/* ScalarType Radius2= Distance(v2,CircumCenter);
0
/|\
/ | \
1 | 3
\ | /
\|/
2
*/
CoordType v0,v1,v2,v3;
PosType app = this->_pos;
v0 = app.v->P(); assert( fabs(Radius-Radius1) < 0.1 );
app.FlipE(); app.FlipV(); assert( fabs(Radius-Radius2) < 0.1 );*/
v1 = app.v->P();
app.FlipE(); app.FlipV();
v2 = app.v->P();
app.FlipE(); app.FlipF(); app.FlipE(); app.FlipV();
v3 = app.v->P();
CoordType CircumCenter = vcg::Circumcenter(*(app.F())); ScalarType radius = Distance(v0, circumcenter);
ScalarType radius1 = Distance(v1, circumcenter);
ScalarType radius2 = Distance(v2, circumcenter);
ScalarType Radius= Distance(v0,CircumCenter); assert( fabs(radius - radius1) < 0.1 );
ScalarType Radius1= Distance(v1,CircumCenter); assert( fabs(radius - radius2) < 0.1 );
ScalarType Radius2= Distance(v2,CircumCenter);
assert( fabs(Radius-Radius1) < 0.1 ); ///Return the difference of radius and the distance of v3 and the CircumCenter
assert( fabs(Radius-Radius2) < 0.1 ); /*this->_priority = (radius2 - Distance(v3, circumcenter));
this->_priority *= -1;*/
///Return the difference of radius and the distance of v3 and the CircumCenter this->_priority = (Distance(v3, circumcenter) - radius2);
this->_priority = (Radius - Distance(v3,CircumCenter));
this->_priority *=-1;
return this->_priority; return this->_priority;
} }
}; };
/*! @} */ /*! @} */
}; // end of namespace tri }; // end of namespace tri
}; // end of namespace vcg }; // end of namespace vcg