Ongoing Rearrangement of filepath

delete old trimesh content
This commit is contained in:
ganovelli 2011-04-01 17:17:42 +00:00
parent 3c7efa7bff
commit 6c0c32ecfe
5 changed files with 0 additions and 2450 deletions

View File

@ -1,627 +0,0 @@
#ifndef QUAD_DIAGONAL_COLLAPSE_H
#define QUAD_DIAGONAL_COLLAPSE_H
#include<vcg/connectors/halfedge_pos.h>
#include<vcg/complex/local_optimization.h>
#include<vcg/complex/trimesh/smooth.h>
#include<set>
#include<vcg/space/ray3.h>
#include<vcg/complex/trimesh/halfedge_quad_clean.h>
namespace vcg{
namespace tri{
/*!
* \brief Generic class for checking feasibility of collapses
*
*/
template<class MeshType, class TriMeshType >
class FeasibilityCheck
{
public:
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
typedef typename TriMeshType::CoordType CoordType;
static bool check_feasible(HEdgePointer hp, CoordType &V1, CoordType &V2, TriMeshType &tm, GRID &grid);
};
/*!
* \brief Generic class for weighting collapses
*
*/
template<class MeshType, class TriMeshType >
class OperationWeight
{
public:
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
typedef typename TriMeshType::CoordType CoordType;
static float compute_weight(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid);
};
/*!
* \brief Class that provides methods for checking and weighting collapses using fitmaps
*
*/
template<class MeshType, class TriMeshType >
class FitmapsCollapse : public FeasibilityCheck<MeshType, TriMeshType>, public OperationWeight<MeshType, TriMeshType>
{
protected:
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
typedef typename TriMeshType::CoordType CoordType;
typedef typename TriMeshType::ScalarType ScalarType;
typedef typename TriMeshType::FacePointer FacePointer;
typedef typename TriMeshType::template PerVertexAttributeHandle<float> Fitmap_attr;
public:
/// Coefficient that will be multiplied with the value of the M-Fitmap
static float& Mfit_coeff()
{
static float coeff = 1.5;
return coeff;
}
/*! Checks if an operation is feasible using M-Fitmap
*
* \param hp Pointer to an halfedge that identifies a diagonal
* \param V1 Coordinates of the first point of the diagonal
* \param V2 Coordinates of the second point of the diagonal
* \param tm Reference mesh
* \param grid Spatial index used for raycasting
*
* \return Value indicating whether diagnoal is collapsible
*/
static bool check_feasible(HEdgePointer hp, CoordType &V1, CoordType &V2, TriMeshType &tm, GRID &grid)
{
float lenght = Distance( V1, V2 );
Fitmap_attr M_Fit = tri::Allocator<TriMeshType>::template GetPerVertexAttribute<float>(tm,"M-Fitmap");
CoordType P = (V1+V2)/2;
float fitmap = compute_fitmap(hp, P, tm, grid, M_Fit);
return lenght <= fitmap/Mfit_coeff();
}
/*! Computes the weight of a diagonal using S-Fitmap
*
* \param hp Pointer to an halfedge that identifies a diagonal
* \param P Coordinates of the point on which fitmap will be computed
* \param tm Reference mesh
* \param grid Spatial index used for raycasting
*
* \return Computed weight
*/
static float compute_weight(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid)
{
Fitmap_attr S_Fit = tri::Allocator<TriMeshType>::template GetPerVertexAttribute<float>(tm,"S-Fitmap");
return compute_fitmap(hp, P, tm, grid, S_Fit);
}
protected:
/*!
* Performs the computation of a fitmap on a given point
*
* \param hp Pointer to an halfedge that identifies a diagonal
* \param P Coordinates of the point on which fitmap will be computed
* \param tm Reference mesh
* \param grid Spatial index used for raycasting
* \param attr Fitmap type (S or M)
*
* \return Computed value of the fitmap
*/
static float compute_fitmap(HEdgePointer hp, CoordType &P, TriMeshType &tm, GRID &grid, Fitmap_attr &attr)
{
CoordType N(0,0,0);
vector<VertexPointer> vertices = HalfEdgeTopology<MeshType>::getVertices(hp->HFp());
assert(vertices.size() == 4);
N += vcg::Normal<typename MeshType::CoordType>(vertices[0]->cP(), vertices[1]->cP(), vertices[2]->cP());
N += vcg::Normal<typename MeshType::CoordType>(vertices[2]->cP(), vertices[3]->cP(), vertices[0]->cP());
N.Normalize();
CoordType C(0,0,0);
FacePointer T = getClosestFaceRay(tm, grid, P, N, &C, NULL);
float result = 1.0;
if(T)
{
float w0;
float w1;
float w2;
vcg::InterpolationParameters(*T, N, C, w0, w1, w2);
float s0 = attr[T->V(0)];
float s1 = attr[T->V(1)];
float s2 = attr[T->V(2)];
result = (w0*s0 + w1*s1 + w2*s2)/(w0+w1+w2);
}
return result;
}
static FacePointer getClosestFaceRay(TriMeshType &m, GRID &grid, CoordType P, CoordType raydir, CoordType* closest, ScalarType* minDist)
{
ScalarType diag = m.bbox.Diag();
raydir.Normalize();
Ray3<typename GRID::ScalarType> ray;
ray.SetOrigin(P);
ScalarType t;
FacePointer f = 0;
FacePointer fr = 0;
vector<CoordType> closests;
vector<ScalarType> minDists;
vector<FacePointer> faces;
ray.SetDirection(-raydir);
f = vcg::tri::DoRay<TriMeshType,GRID>(m, grid, ray, diag/4.0, t);
if (f)
{
closests.push_back(ray.Origin() + ray.Direction()*t);
minDists.push_back(fabs(t));
faces.push_back(f);
}
ray.SetDirection(raydir);
fr = vcg::tri::DoRay<TriMeshType,GRID>(m, grid, ray, diag/4.0, t);
if (fr)
{
closests.push_back(ray.Origin() + ray.Direction()*t);
minDists.push_back(fabs(t));
faces.push_back(fr);
}
if (fr)
if (fr->N()*raydir<0)
fr=0; // discard: inverse normal;
if (minDists.size() == 0)
{
if (closest) *closest=P;
if (minDist) *minDist=0;
f = 0;
}
else
{
int minI = std::min_element(minDists.begin(),minDists.end()) - minDists.begin();
if (closest) *closest= closests[minI];
if (minDist) *minDist= minDists[minI];
f = faces[minI];
}
return f;
}
};
/*!
* \brief Class implementing simplification of quad meshes by diagonal collapses
*
*/
template<class MeshType, class MYTYPE, class TriMeshType, class OptimizationType>
class QuadDiagonalCollapseBase: public LocalOptimization<MeshType>::LocModType
{
protected:
typedef Pos<MeshType> PosType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename LocalOptimization<MeshType>::HeapElem HeapElem;
typedef typename LocalOptimization<MeshType>::HeapType HeapType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
/// Vertex returned by the collapse
VertexPointer ret;
/// Global mark for updating
static int& GlobalMark()
{
static int im=0;
return im;
}
/// Local mark for updating
int localMark;
/// Priority in the heap
ScalarType _priority;
/// Set of modified faces
set<FacePointer> faces;
/// Halfedge that identifies the diagonal to collapse
HEdgePointer hp;
public:
/// Reference mesh used for smoothing
static TriMeshType* &refMesh()
{
static TriMeshType* m = NULL;
return m;
}
/// Spatial index for smoothing
static GRID* &grid()
{
static GRID* grid = NULL;
return grid;
}
/// Number of smoothing iterations to be performed
static unsigned int &smoothing_iterations()
{
static unsigned int iterations = 5;
return iterations;
}
/// Default Constructor
QuadDiagonalCollapseBase(){}
/*!
* Constructor
*
* \param he Pointer to an halfedge representing a diagonal
* \param mark Temporal mark of the operation
*/
QuadDiagonalCollapseBase(HEdgePointer he, int mark)
{
localMark = mark;
hp = he;
_priority = ComputePriority();
}
~QuadDiagonalCollapseBase()
{
faces.clear();
}
/*!
* Computes priority of the operation as the length of the diagonal
*
* \return Priority
*/
ScalarType ComputePriority()
{
CoordType V1 = hp->HVp()->cP();
CoordType V2 = hp->HNp()->HNp()->HVp()->cP();
_priority = Distance( V1, V2 );
return _priority;
}
virtual const char *Info(MeshType &m)
{
static char buf[60];
sprintf(buf,"(%d - %d) %g\n", hp->HVp()-&m.vert[0], hp->HNp()->HNp()->HVp()-&m.vert[0], -_priority);
return buf;
}
/*!
* Performs the collapse and the optimizations
*
* \param m Mesh
*
*/
inline void Execute(MeshType &m)
{
// Collapse the diagonal
ret = HalfEdgeTopology<MeshType>::diagonal_collapse_quad(m,hp->HFp(), hp->HVp());
if(ret->VHp())
{
set<FacePointer> tmp = HalfEdgeTopology<MeshType>::getFaces(ret);
vector<FacePointer> incident_faces = HalfEdgeTopology<MeshType>::get_incident_faces(ret,ret->VHp());
faces.insert(incident_faces.begin(), incident_faces.end());
HalfedgeQuadClean<MeshType>::remove_doublets(m, faces);
// Optimization by edge rotations
if(!ret->IsD())
{
vector<HEdgePointer> hedges = HalfEdgeTopology<MeshType>::get_incident_hedges(ret,ret->VHp());
HalfedgeQuadClean<MeshType>:: template flip_edges<OptimizationType>(m, hedges, faces);
}
faces.insert(tmp.begin(), tmp.end());
// Set of all vertices to smooth
set<VertexPointer> vertices;
for(typename set<FacePointer>::iterator fi = faces.begin(); fi != faces.end(); ++fi)
{
if(*fi)
{
if( !((*fi)->IsD()))
{
vector<VertexPointer> aux = HalfEdgeTopology<MeshType>::getVertices(*fi);
vertices.insert(aux.begin(),aux.end());
}
}
}
// Smoothing
for(unsigned int i = 0; i < smoothing_iterations(); i++)
{
for(typename set<VertexPointer>::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi)
if(!HalfEdgeTopology<MeshType>::isBorderVertex(*vi))
Smooth<MeshType>::VertexCoordLaplacianReproject(m,*grid(), *refMesh(),*vi);
}
// Add all faces modified by smoothing into the set of modified faces
for(typename set<VertexPointer>::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi)
{
vector<FacePointer> tmp_faces = HalfEdgeTopology<MeshType>::get_incident_faces(*vi);
faces.insert(tmp_faces.begin(), tmp_faces.end());
}
}
}
/*!
* Updates the heap of operations.
* For each modified face inserts into the heap two consecutive halfedges representing the two diagonals
*
* \param h_ret Heap to be updated
*
*/
inline void UpdateHeap(HeapType & h_ret)
{
GlobalMark()++;
for(typename set<FacePointer>::iterator fi = faces.begin(); fi != faces.end(); ++fi)
{
if(*fi)
{
if( !((*fi)->IsD()))
{
(*fi)->IMark() = GlobalMark();
HEdgePointer start_he = (*fi)->FHp();
h_ret.push_back( HeapElem( new MYTYPE( start_he, GlobalMark() ) ) );
std::push_heap( h_ret.begin(),h_ret.end() );
h_ret.push_back( HeapElem( new MYTYPE( start_he->HNp(), GlobalMark() ) ) );
std::push_heap( h_ret.begin(),h_ret.end() );
}
}
}
}
ModifierType IsOfType()
{
return QuadDiagCollapseOp;
}
/*!
* Checks if the operation can be done without generation of degenerate configurations
*
* \return Value indicating whether the operation can be performed
*/
inline bool IsFeasible()
{
FacePointer fp = hp->HFp();
if(!fp)
return false;
if(fp->IsD() || fp->VN() !=4)
return false;
return ( HalfEdgeTopology<MeshType>::check_diagonal_collapse_quad(hp));
}
/*!
* Checks if the operation is up to date
*
* \return Value indicating whether operation is up to date
*/
inline bool IsUpToDate()
{
FacePointer fp = hp->HFp();
if(fp)
return (!hp->IsD() && localMark >= fp->IMark() );
return false;
}
/*!
* Gets the priority of the operation
*
* \return Value indicating the priority
*/
virtual ScalarType Priority() const
{
return _priority;
}
/*!
* Initializes a heap with all the possible diagonal collapses of the mesh
* For each face inserts two consecutive halfedges representing the two diagonals
*
* \param m Mesh
* \param h_ret heap to be initialized
*
*/
static void Init(MeshType &m,HeapType &h_ret)
{
// Grid and reference mesh must be initialized
assert(grid());
assert(refMesh());
assert(!HalfedgeQuadClean<MeshType>::has_doublets(m));
assert(!HalfedgeQuadClean<MeshType>::has_singlets(m));
vcg::tri::InitFaceIMark(m);
h_ret.clear();
typename MeshType::FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end();++fi)
{
if(!(*fi).IsD())
{
h_ret.push_back( HeapElem(new MYTYPE( (*fi).FHp(), IMark(m))));
h_ret.push_back( HeapElem(new MYTYPE( (*fi).FHp()->HNp(), IMark(m))));
}
}
}
};
/*!
* \brief Class implementing simplification of quad meshes by diagonal collapses
* priority of the operations is weighted with a value computed by class WeightType
* Feasibility is checked with class CheckType
*
*/
template<class MeshType, class MYTYPE, class TriMeshType, class OptimizationType, class WeightType, class CheckType >
class QuadDiagonalCollapse: public QuadDiagonalCollapseBase<MeshType, MYTYPE, TriMeshType, OptimizationType>
{
protected:
typedef Pos<MeshType> PosType;
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename LocalOptimization<MeshType>::HeapElem HeapElem;
typedef typename LocalOptimization<MeshType>::HeapType HeapType;
typedef typename MeshType::ScalarType ScalarType;
typedef typename MeshType::CoordType CoordType;
typedef typename TriMeshType::FaceType TriFaceType;
typedef typename vcg::GridStaticPtr<TriFaceType, typename TriFaceType::ScalarType> GRID;
public:
/// Default constructor
QuadDiagonalCollapse(){}
/*!
* Constructor
*
* \param he Pointer to an halfedge representing a diagonal
* \param mark Temporal mark of the operation
*/
QuadDiagonalCollapse(HEdgePointer he, int mark)
{
this->localMark = mark;
this->hp = he;
this->_priority = this->ComputePriority();
}
/*!
* Computes priority of the operation as length * weight
*
* \return Priority
*/
ScalarType ComputePriority()
{
CoordType V1 = this->hp->HVp()->cP();
CoordType V2 = this->hp->HNp()->HNp()->HVp()->cP();
CoordType P = (V1+V2)/2;
float weight = WeightType::compute_weight(this->hp, P, *(this->refMesh()), *(this->grid()));
this->_priority = Distance( V1, V2 ) * weight;
return this->_priority;
}
/*!
* Checks if the operation can be done without generation of degenerate configurations
*
* \return Value indicating whether the operation can be performed
*/
bool IsFeasible()
{
FacePointer fp = this->hp->HFp();
if(!fp)
return false;
if(fp->IsD() || fp->VN() !=4)
return false;
if(!HalfEdgeTopology<MeshType>::check_diagonal_collapse_quad(this->hp))
return false;
CoordType V1 = this->hp->HVp()->cP();
CoordType V2 = this->hp->HNp()->HNp()->HVp()->cP();
return CheckType::check_feasible(this->hp, V1, V2, *(this->refMesh()), *(this->grid()));
}
};
}//end namespace tri
}//end namespace vcg
#endif // QUAD_DIAGONAL_COLLAPSE_H

View File

@ -1,288 +0,0 @@
/****************************************************************************
* 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
****************************************************************************/
#ifndef __VCG_DECIMATION_COLLAPSE
#define __VCG_DECIMATION_COLLAPSE
#include<vcg/complex/local_optimization.h>
#include<vcg/simplex/tetrahedron/pos.h>
#include<vcg/complex/tetramesh/edge_collapse.h>
#include<vcg/space/point3.h>
struct FAIL{
static int VOL(){static int vol=0; return vol++;}
static int LKF(){static int lkf=0; return lkf++;}
static int LKE(){static int lke=0; return lke++;}
static int LKV(){static int lkv=0; return lkv++;}
static int OFD(){static int ofd=0; return ofd++;}
static int BOR(){static int bor=0; return bor++;}
};
namespace vcg{
namespace tetra{
/** \addtogroup tetramesh */
/*@{*/
/// This Class is specialization of LocalModification for the edge collapse
/// It wraps the atomic operation EdgeCollapse to be used in a optimizatin routine.
/// Note that it has knowledge of the heap of the class LocalOptimization because
/// it is responsible of updating it after a collapse has been performed
template<class TETRA_MESH_TYPE>
class TetraEdgeCollapse: public LocalOptimization<TETRA_MESH_TYPE>::LocModType
{
/// The tetrahedral mesh type
//typedef typename TETRA_MESH_TYPE TETRA_MESH_TYPE;
/// The tetrahedron type
typedef typename TETRA_MESH_TYPE::TetraType TetraType;
/// The vertex type
typedef typename TetraType::VertexType VertexType;
/// The coordinate type
typedef typename TetraType::VertexType::CoordType CoordType;
/// The scalar type
typedef typename TETRA_MESH_TYPE::VertexType::ScalarType ScalarType;
/////the base type class
//typedef typename vcg::tri::LocalModification LocalMod;
/// The HEdgePos type
typedef Pos<TetraType> PosType;
/// The HEdgePos Loop type
typedef PosLoop<TetraType> PosLType;
/// definition of the heap element
typedef typename LocalOptimization<TETRA_MESH_TYPE>::HeapElem HeapElem;
private:
///the new point that substitute the edge
Point3<ScalarType> _NewPoint;
///the pointer to edge collapser method
vcg::tetra::EdgeCollapse<TETRA_MESH_TYPE> _EC;
///mark for up_dating
static int& _Imark(){ static int im=0; return im;}
///the pos of collapse
PosType pos;
///pointer to vertex that remain
VertexType *vrem;
/// priority in the heap
ScalarType _priority;
public:
/// Default Constructor
TetraEdgeCollapse()
{}
///Constructor with postype
TetraEdgeCollapse(PosType p,int mark)
{
_Imark() = mark;
pos=p;
_priority = _AspectRatioMedia(p);
}
~TetraEdgeCollapse()
{}
private:
///Return the aspect Ratio media of the tetrahedrons
///that share the adge to collapse
ScalarType _AspectRatioMedia(PosType p)
{
PosLType posl=PosLType(p.T(),p.F(),p.E(),p.V());
posl.Reset();
int num=0;
ScalarType ratio_media=0.f;
while(!posl.LoopEnd())
{
ratio_media+=posl.T()->AspectRatio();
posl.NextT();
num++;
}
ratio_media=ratio_media/num;
return (ratio_media);
}
///Modify pos and alfa to obtain the collapse that minimize the error
ScalarType _VolumePreservingError(PosType &pos,CoordType &new_point,int nsteps)
{
VertexType *ve0=(pos.T()->V(Tetra::VofE(pos.E(),0)));
VertexType *ve1=(pos.T()->V(Tetra::VofE(pos.E(),1)));
bool ext_v0=ve0->IsB();
bool ext_v1=ve1->IsB();
ScalarType best_error=0.f;
if ((ext_v0)&&(!ext_v1))
new_point=ve0->P();
else
if ((!ext_v0)&&(ext_v1))
new_point=ve1->P();
else
if ((!ext_v0)&&(!ext_v1))
{/*CoordType g;
g.SetZero();
g+=ve0->cP();
g+=ve1->cP();
g/=2;*/
new_point=(ve0->cP()+ve1->cP())/2.f;
}
else
if ((ext_v0)&&(ext_v1))//both are external vertex
{
ScalarType step=1.f/(nsteps-1);
ScalarType Vol_Original=_EC.VolumeOriginal();
for (int i=0;i<nsteps;i++)
{
best_error=1000000.f;
ScalarType alfatemp=step*((ScalarType)i);
//CoordType g;
// g.SetZero();
//g+=ve0->cP()*alfatemp;
//g+=ve1->cP()*(1-alfatemp);
//CoordType newPTemp=g;
CoordType newPTemp=(ve0->cP()*alfatemp) +(ve1->cP()*(1.f-alfatemp));
//the error is the absolute value of difference of volumes
ScalarType error=fabs(Vol_Original-_EC.VolumeSimulateCollapse(pos,newPTemp));
if(error<best_error)
{
new_point=newPTemp;
best_error=error;
}
}
}
return (best_error);
}
public:
virtual const char *Info(TETRA_MESH_TYPE &m) {
static char buf[60];
//sprintf(buf,"collapse %i -> %i %f\n", pos.()-&m.vert[0], pos.VFlip()-&m.vert[0],_priority);
return buf;
}
ScalarType ComputePriority()
{
return (_priority = _AspectRatioMedia(this->pos));
}
ScalarType ComputeError()
{
vrem=(pos.T()->V(Tetra::VofE(pos.E(),0)));
return (_VolumePreservingError(pos,_NewPoint,5));// magic number....parametrize!
}
void Execute(TETRA_MESH_TYPE &tm)
{
// _EC.FindSets(pos);
assert(!vrem->IsD());
int del=_EC.DoCollapse(pos,_NewPoint);
tm.tn-=del;
tm.vn-=1;
}
void UpdateHeap(typename LocalOptimization<TETRA_MESH_TYPE>::HeapType & h_ret)
{
assert(!vrem->IsD());
_Imark()++;
VTIterator<TetraType> VTi(vrem->VTb(),vrem->VTi());
while (!VTi.End())
{
VTi.Vt()->ComputeVolume();
for (int j=0;j<6;j++)
{
vcg::tetra::Pos<TetraType> p=Pos<TetraType>(VTi.Vt(),Tetra::FofE(j,0),j,Tetra::VofE(j,0));
assert(!p.T()->V(p.V())->IsD());
assert(!p.T()->IsD());
h_ret.push_back(HeapElem(new TetraEdgeCollapse<TETRA_MESH_TYPE>(p,_Imark())));
std::push_heap(h_ret.begin(),h_ret.end());
// update the mark of the vertices
VTi.Vt()->V(Tetra::VofE(j,0))->IMark() = _Imark();
}
++VTi;
}
}
/// return the type of operation
ModifierType IsOfType(){ return TetraEdgeCollapseOp;}
bool IsFeasible(){
vcg::tetra::EdgeCollapse<TETRA_MESH_TYPE>::Reset();
_EC.FindSets(pos);
ComputeError();
return(_EC.CheckPreconditions(pos,_NewPoint));
}
bool IsUpToDate(){
if (!pos.T()->IsD())
{
VertexType *v0=pos.T()->V(Tetra::VofE(pos.E(),0));
VertexType *v1=pos.T()->V(Tetra::VofE(pos.E(),1));
assert(!v0->IsD());
assert(!v1->IsD());
if(! (( (!v0->IsD()) && (!v1->IsD())) &&
_Imark()>=v0->IMark() &&
_Imark()>=v1->IMark()))
{
FAIL::OFD();
return false;
}
else
return true;
}
else
return false;
}
virtual ScalarType Priority() const {
return _priority;
}
/// perform initialization
static void Init(TETRA_MESH_TYPE &m,typename LocalOptimization<TETRA_MESH_TYPE>::HeapType& h_ret){
h_ret.clear();
typename TETRA_MESH_TYPE::TetraIterator ti;
for(ti = m.tetra.begin(); ti != m.tetra.end();++ti)
if(!(*ti).IsD()){
(*ti).ComputeVolume();
for (int j=0;j<6;j++)
{
PosType p=PosType(&*ti,Tetra::FofE(j,0),j,Tetra::VofE(j,0));
assert(!p.T()->V(p.V())->IsD());
assert(!p.T()->IsD());
h_ret.push_back(HeapElem(new TetraEdgeCollapse<TETRA_MESH_TYPE>(p,m.IMark)));
}
}
}
};
}//end namespace tetra
}//end namespace vcg
#endif

View File

@ -1,290 +0,0 @@
/****************************************************************************
* 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. *
* *
****************************************************************************/
/****************************************************************************
$Log: not supported by cvs2svn $
Revision 1.19 2006/10/15 07:31:21 cignoni
typenames and qualifiers for gcc compliance
Revision 1.18 2006/10/09 20:09:40 cignoni
Changed some access to VertexFaceIterator to reflect the shorter new operators.
Revision 1.17 2005/10/12 10:44:01 cignoni
Now creation of new edge use Ordered() constructor to comply the fact that the basic collapse is simmetric.
Revision 1.16 2005/01/19 10:35:28 cignoni
Better management of symmetric/asymmetric edge collapses
Revision 1.15 2004/12/10 01:03:53 cignoni
better comments and removed logging
Revision 1.14 2004/11/23 10:34:23 cignoni
passed parameters by reference in many funcs and gcc cleaning
Revision 1.13 2004/10/25 16:28:32 ganovelli
pos to edge
Revision 1.12 2004/09/15 11:16:02 ganovelli
changed P() to cP()
Revision 1.11 2004/09/09 13:23:01 ponchio
Header guards typo
Revision 1.10 2004/09/09 13:01:12 ponchio
Linux compatible path in #include
Revision 1.9 2004/09/08 15:13:29 ganovelli
changes for gc
Revision 1.8 2004/09/08 14:33:31 ganovelli
*** empty log message ***
****************************************************************************/
#ifndef __VCG_DECIMATION_TRICOLLAPSE
#define __VCG_DECIMATION_TRICOLLAPSE
#include<vcg/complex/trimesh/edge_collapse.h>
#include<vcg/simplex/face/pos.h>
#include<vcg/complex/local_optimization.h>
namespace vcg{
namespace tri{
/** \addtogroup trimesh */
/*@{*/
/// This Class is specialization of LocalModification for the edge collapse.
/// It wraps the atomic operation EdgeCollapse to be used in a optimizatin routine.
/// Note that it has knowledge of the heap of the class LocalOptimization because
/// it is responsible of updating it after a collapse has been performed;
/// This is the base class of all the specialized collapse classes like for example Quadric Edge Collapse.
/// Each derived class
template<class TriMeshType, class MYTYPE>
class TriEdgeCollapse: public LocalOptimization<TriMeshType>::LocModType , public EdgeCollapse<TriMeshType>
{
public:
/// static data to gather statistical information about the reasons of collapse failures
class FailStat {
public:
static int &Volume() {static int vol=0; return vol;}
static int &LinkConditionFace(){static int lkf=0; return lkf;}
static int &LinkConditionEdge(){static int lke=0; return lke;}
static int &LinkConditionVert(){static int lkv=0; return lkv;}
static int &OutOfDate() {static int ofd=0; return ofd;}
static int &Border() {static int bor=0; return bor;}
static void Init()
{
Volume() =0;
LinkConditionFace()=0;
LinkConditionEdge()=0;
LinkConditionVert()=0;
OutOfDate() =0;
Border() =0;
}
};
protected:
typedef typename TriMeshType::FaceType FaceType;
typedef typename TriMeshType::FaceType::VertexType VertexType;
typedef typename VertexType::EdgeType EdgeType;
typedef typename FaceType::VertexType::CoordType CoordType;
typedef typename TriMeshType::VertexType::ScalarType ScalarType;
typedef typename LocalOptimization<TriMeshType>::HeapElem HeapElem;
typedef typename LocalOptimization<TriMeshType>::HeapType HeapType;
TriMeshType *mt;
///the pair to collapse
EdgeType pos;
///mark for up_dating
static int& GlobalMark(){ static int im=0; return im;}
///mark for up_dating
int localMark;
/// priority in the heap
ScalarType _priority;
public:
/// Default Constructor
inline TriEdgeCollapse()
{}
///Constructor with postype
inline TriEdgeCollapse(const EdgeType &p, int mark)
{
localMark = mark;
pos=p;
_priority = ComputePriority();
}
~TriEdgeCollapse()
{}
private:
public:
inline ScalarType ComputePriority()
{
_priority = Distance(pos.V(0)->cP(),pos.V(1)->cP());
return _priority;
}
virtual const char *Info(TriMeshType &m) {
mt = &m;
static char buf[60];
sprintf(buf,"%i -> %i %g\n", int(pos.V(0)-&m.vert[0]), int(pos.V(1)-&m.vert[0]),-_priority);
return buf;
}
inline void Execute(TriMeshType &m)
{
CoordType MidPoint=(pos.V(0)->P()+pos.V(1)->P())/2.0;
DoCollapse(m, pos, MidPoint);
}
static bool IsSymmetric() { return true;}
// This function is called after an action to re-add in the heap elements whose priority could have been changed.
// in the plain case we just put again in the heap all the edges around the vertex resulting from the previous collapse: v[1].
// if the collapse is not symmetric you should add also backward edges (because v0->v1 collapse could be different from v1->v0)
inline void UpdateHeap(HeapType & h_ret)
{
GlobalMark()++; int nn=0;
VertexType *v[2];
v[0]= pos.V(0);v[1]=pos.V(1);
v[1]->IMark() = GlobalMark();
// First loop around the remaining vertex to unmark visited flags
vcg::face::VFIterator<FaceType> vfi(v[1]);
while (!vfi.End()){
vfi.V1()->ClearV();
vfi.V2()->ClearV();
++vfi;
}
// Second Loop: add all the outgoing edges around v[1]
// for each face add the two edges outgoing from v[1] and not visited.
vfi = face::VFIterator<FaceType>(v[1]);
while (!vfi.End())
{
assert(!vfi.F()->IsD());
for (int j=0;j<3;j++)
{
if( !(vfi.V1()->IsV()) && (vfi.V1()->IsRW()))
{
vfi.V1()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType( vfi.V(),vfi.V1() ),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(! this->IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType( vfi.V1(),vfi.V()),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
if( !(vfi.V2()->IsV()) && (vfi.V2()->IsRW()))
{
vfi.V2()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.F()->V(vfi.I()),vfi.F()->V2(vfi.I())),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(! this->IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType (vfi.F()->V1(vfi.I()),vfi.F()->V(vfi.I())),GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
// if(vfi.V1()->IsRW() && vfi.V2()->IsRW() )
// {
// h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V2()),this->GlobalMark())));
// std::push_heap(h_ret.begin(),h_ret.end());
// if(IsSymmetric()){
// h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V1()), this->GlobalMark())));
// std::push_heap(h_ret.begin(),h_ret.end());
// }
// }
}
++vfi;nn++;
}
// printf("ADDED %d\n",nn);
}
ModifierType IsOfType(){ return TriEdgeCollapseOp;}
inline bool IsFeasible(){
return LinkConditions(pos);
}
inline bool IsUpToDate(){
// if(pos.V(1)->IsD()) {
// ++FailStat::OutOfDate();
// return false;
//}
//
// if(pos.V(1)->IsD()) {
// ++FailStat::OutOfDate();
// return false;
//}
VertexType *v0=pos.V(0);
VertexType *v1=pos.V(1);
//if(! (( (!v0->IsD()) && (!v1->IsD())) &&
// localMark>=v0->IMark() &&
// localMark>=v1->IMark()))
if( v0->IsD() || v1->IsD() ||
localMark < v0->IMark() ||
localMark < v1->IMark() )
{
++FailStat::OutOfDate();
return false;
}
return true;
}
virtual ScalarType Priority() const {
return _priority;
}
static void Init(TriMeshType&m,HeapType&h_ret){
h_ret.clear();
typename TriMeshType::FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end();++fi)
if(!(*fi).IsD()){
for (int j=0;j<3;j++)
{
EdgeType p=EdgeType::OrderedEdge((*fi).V(j),(*fi).V((j+1)%3));
h_ret.push_back(HeapElem(new MYTYPE(p, IMark(m))));
//printf("Inserting in heap coll %3i ->%3i %f\n",p.V()-&m.vert[0],p.VFlip()-&m.vert[0],h_ret.back().locModPtr->Priority());
}
}
}
};
}//end namespace tri
}//end namespace vcg
#endif

View File

@ -1,636 +0,0 @@
/****************************************************************************
* 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.14 2007/03/22 11:07:16 cignoni
Solved an issue related to different casting double-float between gcc 3 and gcc 4
Revision 1.13 2007/02/25 09:20:10 cignoni
Added Rad to the NormalThr Option and removed a bug in multiple exectuion of non optimal simplification (missing an isD check)
Revision 1.12 2007/01/19 09:13:14 cignoni
Added Finalize() method to the interface, corrected minor bugs on border preserving and postsimplification cleanup
Avoided double make_heap (it is done only in the local_optimization init)
Revision 1.11 2006/10/15 07:31:21 cignoni
typenames and qualifiers for gcc compliance
Revision 1.10 2006/10/09 20:12:55 cignoni
Heavyly restructured for meshlab inclusion. Now the access to the quadric elements are mediated by a static helper class.
Revision 1.9 2006/10/07 17:20:25 cignoni
Updated to the new style face->Normal() becomes Normal(face)
Revision 1.8 2005/10/02 23:19:36 cignoni
Changed the sign of the priority of a collapse. Now it is its the error as it should (and not -error)
Revision 1.7 2005/04/14 11:35:07 ponchio
*** empty log message ***
Revision 1.6 2005/01/19 10:35:28 cignoni
Better management of symmetric/asymmetric edge collapses
Revision 1.5 2004/12/10 01:07:15 cignoni
Moved param classes inside; added support for optimal placement and symmetric; added update heap also here (not only in the base class)
Revision 1.4 2004/11/23 10:34:23 cignoni
passed parameters by reference in many funcs and gcc cleaning
Revision 1.3 2004/10/25 07:07:56 ganovelli
A vcg.::Pos was used to implement the collapse type. CHanged
to vcg::Edge
Revision 1.2 2004/09/29 17:08:16 ganovelli
corrected error in -error (see localoptimization)
****************************************************************************/
#ifndef __VCG_TRIMESHCOLLAPSE_QUADRIC__
#define __VCG_TRIMESHCOLLAPSE_QUADRIC__
#include<vcg/math/quadric.h>
#include<vcg/simplex/face/pos.h>
#include<vcg/complex/trimesh/update/flag.h>
#include<vcg/complex/trimesh/update/topology.h>
#include<vcg/complex/trimesh/update/bounding.h>
#include<vcg/complex/local_optimization/tri_edge_collapse.h>
#include<vcg/complex/local_optimization.h>
namespace vcg{
namespace tri{
/**
This class describe Quadric based collapse operation.
Requirements:
Vertex
must have:
incremental mark
VF topology
must have:
members
QuadricType Qd();
ScalarType W() const;
A per-vertex Weight that can be used in simplification
lower weight means that error is lowered,
standard: return W==1.0
void Merge(MESH_TYPE::vertex_type const & v);
Merges the attributes of the current vertex with the ones of v
(e.g. its weight with the one of the given vertex, the color ect).
Standard: void function;
OtherWise the class should be templated with a static helper class that helps to retrieve these functions.
If the vertex class exposes these functions a default static helper class is provided.
*/
//**Helper CLASSES**//
template <class VERTEX_TYPE>
class QInfoStandard
{
public:
QInfoStandard(){};
static void Init(){};
static math::Quadric<double> &Qd(VERTEX_TYPE &v) {return v.Qd();}
static math::Quadric<double> &Qd(VERTEX_TYPE *v) {return v->Qd();}
static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE */*v*/) {return 1.0;};
static typename VERTEX_TYPE::ScalarType W(VERTEX_TYPE &/*v*/) {return 1.0;};
static void Merge(VERTEX_TYPE & v_dest, VERTEX_TYPE const & v_del){};
};
class TriEdgeCollapseQuadricParameter
{
public:
double QualityThr; // all
double BoundaryWeight;
double NormalThrRad;
double CosineThr;
double QuadricEpsilon;
double ScaleFactor;
bool UseArea;
bool UseVertexWeight;
bool NormalCheck;
bool QualityCheck;
bool OptimalPlacement;
bool MemoryLess;
bool QualityWeight;
bool ScaleIndependent;
//***********************
bool QualityQuadric; // During the initialization manage all the edges as border edges adding a set of additional quadrics that are useful mostly for keeping face aspect ratio good.
bool PreserveTopology;
bool PreserveBoundary;
bool MarkComplex;
bool FastPreserveBoundary;
bool SafeHeapUpdate;
};
template<class TriMeshType,class MYTYPE, class HelperType = QInfoStandard<typename TriMeshType::VertexType> >
class TriEdgeCollapseQuadric: public TriEdgeCollapse< TriMeshType, MYTYPE>
{
public:
typedef typename vcg::tri::TriEdgeCollapse< TriMeshType, MYTYPE > TEC;
typedef typename TEC::EdgeType EdgeType;
typedef typename TriEdgeCollapse<TriMeshType, MYTYPE>::HeapType HeapType;
typedef typename TriEdgeCollapse<TriMeshType, MYTYPE>::HeapElem HeapElem;
typedef typename TriMeshType::CoordType CoordType;
typedef typename TriMeshType::ScalarType ScalarType;
typedef math::Quadric< double > QuadricType;
typedef typename TriMeshType::FaceType FaceType;
typedef typename TriMeshType::VertexType VertexType;
typedef TriEdgeCollapseQuadricParameter QParameter;
typedef HelperType QH;
static QParameter & Params(){
static QParameter p;
return p;
}
enum Hint {
HNHasFFTopology = 0x0001, // La mesh arriva con la topologia ff gia'fatta
HNHasVFTopology = 0x0002, // La mesh arriva con la topologia bf gia'fatta
HNHasBorderFlag = 0x0004 // La mesh arriva con i flag di bordo gia' settati
};
static int & Hnt(){static int hnt; return hnt;} // the current hints
static void SetHint(Hint hn) { Hnt() |= hn; }
static void ClearHint(Hint hn) { Hnt()&=(~hn);}
static bool IsSetHint(Hint hn) { return (Hnt()&hn)!=0; }
// puntatori ai vertici che sono stati messi non-w per preservare il boundary
static std::vector<typename TriMeshType::VertexPointer> & WV(){
static std::vector<typename TriMeshType::VertexPointer> _WV; return _WV;
};
inline TriEdgeCollapseQuadric(const EdgeType &p, int i)
//:TEC(p,i){}
{
this->localMark = i;
this->pos=p;
this->_priority = ComputePriority();
}
inline bool IsFeasible(){
bool res = ( !Params().PreserveTopology || LinkConditions(this->pos) );
if(!res) ++( TriEdgeCollapse< TriMeshType,MYTYPE>::FailStat::LinkConditionEdge() );
return res;
}
void Execute(TriMeshType &m)
{ CoordType newPos;
if(Params().OptimalPlacement) newPos= static_cast<MYTYPE*>(this)->ComputeMinimal();
else newPos=this->pos.V(1)->P();
//this->pos.V(1)->Qd()+=this->pos.V(0)->Qd();
QH::Qd(this->pos.V(1))+=QH::Qd(this->pos.V(0));
//int FaceDel=
DoCollapse(m, this->pos, newPos); // v0 is deleted and v1 take the new position
//m.fn-=FaceDel;
//--m.vn;
}
// Final Clean up after the end of the simplification process
static void Finalize(TriMeshType &m, HeapType& /*h_ret*/)
{
// if the mesh was prepared with precomputed borderflags
// correctly set them again.
if(IsSetHint(HNHasBorderFlag) )
vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromVF(m);
// If we had the boundary preservation we should clean up the writable flags
if(Params().FastPreserveBoundary)
{
typename TriMeshType::VertexIterator vi;
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD()) (*vi).SetW();
}
if(Params().PreserveBoundary)
{
typename std::vector<typename TriMeshType::VertexPointer>::iterator wvi;
for(wvi=WV().begin();wvi!=WV().end();++wvi)
if(!(*wvi)->IsD()) (*wvi)->SetW();
}
}
static void Init(TriMeshType &m,HeapType&h_ret){
typename TriMeshType::VertexIterator vi;
typename TriMeshType::FaceIterator pf;
EdgeType av0,av1,av01;
Params().CosineThr=cos(Params().NormalThrRad);
if(!IsSetHint(HNHasVFTopology) ) vcg::tri::UpdateTopology<TriMeshType>::VertexFace(m);
if(Params().MarkComplex) {
vcg::tri::UpdateTopology<TriMeshType>::FaceFace(m);
vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromFF(m);
vcg::tri::UpdateTopology<TriMeshType>::VertexFace(m);
} // e' un po' piu' lenta ma marca i vertici complex
else
if(!IsSetHint(HNHasBorderFlag) )
vcg::tri::UpdateFlags<TriMeshType>::FaceBorderFromVF(m);
if(Params().FastPreserveBoundary)
{
for(pf=m.face.begin();pf!=m.face.end();++pf)
if( !(*pf).IsD() && (*pf).IsW() )
for(int j=0;j<3;++j)
if((*pf).IsB(j))
{
(*pf).V(j)->ClearW();
(*pf).V1(j)->ClearW();
}
}
if(Params().PreserveBoundary)
{
WV().clear();
for(pf=m.face.begin();pf!=m.face.end();++pf)
if( !(*pf).IsD() && (*pf).IsW() )
for(int j=0;j<3;++j)
if((*pf).IsB(j))
{
if((*pf).V(j)->IsW()) {(*pf).V(j)->ClearW(); WV().push_back((*pf).V(j));}
if((*pf).V1(j)->IsW()) {(*pf).V1(j)->ClearW();WV().push_back((*pf).V1(j));}
}
}
InitQuadric(m);
// Initialize the heap with all the possible collapses
if(IsSymmetric())
{ // if the collapse is symmetric (e.g. u->v == v->u)
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD() && (*vi).IsRW())
{
vcg::face::VFIterator<FaceType> x;
for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x){
x.V1()->ClearV();
x.V2()->ClearV();
}
for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++x )
{
assert(x.F()->V(x.I())==&(*vi));
if((x.V0()<x.V1()) && x.V1()->IsRW() && !x.V1()->IsV()){
x.V1()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(x.V0(),x.V1()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark() )));
}
if((x.V0()<x.V2()) && x.V2()->IsRW()&& !x.V2()->IsV()){
x.V2()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(x.V0(),x.V2()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark() )));
}
}
}
}
else
{ // if the collapse is A-symmetric (e.g. u->v != v->u)
for(vi=m.vert.begin();vi!=m.vert.end();++vi)
if(!(*vi).IsD() && (*vi).IsRW())
{
vcg::face::VFIterator<FaceType> x;
UnMarkAll(m);
for( x.F() = (*vi).VFp(), x.I() = (*vi).VFi(); x.F()!=0; ++ x)
{
assert(x.F()->V(x.I())==&(*vi));
if(x.V()->IsRW() && x.V1()->IsRW() && !IsMarked(m,x.F()->V1(x.I()))){
h_ret.push_back( HeapElem( new MYTYPE( EdgeType (x.V(),x.V1()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark())));
}
if(x.V()->IsRW() && x.V2()->IsRW() && !IsMarked(m,x.F()->V2(x.I()))){
h_ret.push_back( HeapElem( new MYTYPE( EdgeType (x.V(),x.V2()),TriEdgeCollapse< TriMeshType,MYTYPE>::GlobalMark())));
}
}
}
}
}
static float HeapSimplexRatio() {return IsSymmetric()?5.0f:9.0f;}
static bool IsSymmetric() {return Params().OptimalPlacement;}
static bool IsVertexStable() {return !Params().OptimalPlacement;}
static void SetDefaultParams(){
Params().UseArea=true;
Params().UseVertexWeight=false;
Params().NormalCheck=false;
Params().NormalThrRad=M_PI/2;
Params().QualityCheck=true;
Params().QualityThr=.1;
Params().BoundaryWeight=.5;
Params().QualityQuadric=false;
Params().OptimalPlacement=true;
Params().ScaleIndependent=true;
Params().QualityWeight=false;
Params().QuadricEpsilon =1e-15;
Params().ScaleFactor=1.0;
Params().PreserveTopology = false;
}
///*
// Funzione principale di valutazione dell'errore del collasso.
// In pratica simula il collasso vero e proprio.
//
// Da ottimizzare il ciclo sulle normali (deve sparire on e si deve usare per face normals)
//*/
ScalarType ComputePriority() {
ScalarType error;
typename vcg::face::VFIterator<FaceType> x;
std::vector<CoordType> on; // original normals
typename TriMeshType::VertexType * v[2];
v[0] = this->pos.V(0);
v[1] = this->pos.V(1);
if(Params().NormalCheck){ // Compute maximal normal variation
// store the old normals for non-collapsed face in v0
for(x.F() = v[0]->VFp(), x.I() = v[0]->VFi(); x.F()!=0; ++x ) // for all faces in v0
if(x.F()->V(0)!=v[1] && x.F()->V(1)!=v[1] && x.F()->V(2)!=v[1] ) // skip faces with v1
on.push_back(NormalizedNormal(*x.F()));
// store the old normals for non-collapsed face in v1
for(x.F() = v[1]->VFp(), x.I() = v[1]->VFi(); x.F()!=0; ++x ) // for all faces in v1
if(x.F()->V(0)!=v[0] && x.F()->V(1)!=v[0] && x.F()->V(2)!=v[0] ) // skip faces with v0
on.push_back(NormalizedNormal(*x.F()));
}
//// Move the two vertexe into new position (storing the old ones)
CoordType OldPos0=v[0]->P();
CoordType OldPos1=v[1]->P();
if(Params().OptimalPlacement) { v[0]->P() = ComputeMinimal(); v[1]->P()=v[0]->P();}
else v[0]->P() = v[1]->P();
//// Rescan faces and compute quality and difference between normals
int i;
double ndiff,MinCos = 1e100; // minimo coseno di variazione di una normale della faccia
// (e.g. max angle) Mincos varia da 1 (normali coincidenti) a
// -1 (normali opposte);
double qt, MinQual = 1e100;
CoordType nn;
for(x.F() = v[0]->VFp(), x.I() = v[0]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v0
if(x.F()->V(0)!=v[1] && x.F()->V(1)!=v[1] && x.F()->V(2)!=v[1] ) // skip faces with v1
{
if(Params().NormalCheck){
nn=NormalizedNormal(*x.F());
ndiff=nn.dot(on[i++]);
if(ndiff<MinCos) MinCos=ndiff;
}
if(Params().QualityCheck){
qt= QualityFace(*x.F());
if(qt<MinQual) MinQual=qt;
}
}
for(x.F() = v[1]->VFp(), x.I() = v[1]->VFi(),i=0; x.F()!=0; ++x ) // for all faces in v1
if(x.F()->V(0)!=v[0] && x.F()->V(1)!=v[0] && x.F()->V(2)!=v[0] ) // skip faces with v0
{
if(Params().NormalCheck){
nn=NormalizedNormal(*x.F());
ndiff=nn.dot(on[i++]);
if(ndiff<MinCos) MinCos=ndiff;
}
if(Params().QualityCheck){
qt= QualityFace(*x.F());
if(qt<MinQual) MinQual=qt;
}
}
QuadricType qq=QH::Qd(v[0]);
qq+=QH::Qd(v[1]);
Point3d tpd=Point3d::Construct(v[1]->P());
double QuadErr = Params().ScaleFactor*qq.Apply(tpd);
// All collapses involving triangles with quality larger than <QualityThr> has no penalty;
if(MinQual>Params().QualityThr) MinQual=Params().QualityThr;
if(Params().NormalCheck){
// All collapses where the normal vary less than <NormalThr> (e.g. more than CosineThr)
// have no penalty
if(MinCos>Params().CosineThr) MinCos=Params().CosineThr;
MinCos=(MinCos+1)/2.0; // Now it is in the range 0..1 with 0 very dangerous!
}
if(QuadErr<Params().QuadricEpsilon) QuadErr=Params().QuadricEpsilon;
if( Params().UseVertexWeight ) QuadErr *= (QH::W(v[1])+QH::W(v[0]))/2;
if(!Params().QualityCheck && !Params().NormalCheck) error = (ScalarType)(QuadErr);
if( Params().QualityCheck && !Params().NormalCheck) error = (ScalarType)(QuadErr / MinQual);
if(!Params().QualityCheck && Params().NormalCheck) error = (ScalarType)(QuadErr / MinCos);
if( Params().QualityCheck && Params().NormalCheck) error = (ScalarType)(QuadErr / (MinQual*MinCos));
//Rrestore old position of v0 and v1
v[0]->P()=OldPos0;
v[1]->P()=OldPos1;
this->_priority = error;
return this->_priority;
}
//
//static double MaxError() {return 1e100;}
//
inline void UpdateHeap(HeapType & h_ret)
{
this->GlobalMark()++;
VertexType *v[2];
v[0]= this->pos.V(0);
v[1]= this->pos.V(1);
v[1]->IMark() = this->GlobalMark();
// First loop around the remaining vertex to unmark visited flags
vcg::face::VFIterator<FaceType> vfi(v[1]);
while (!vfi.End()){
vfi.V1()->ClearV();
vfi.V2()->ClearV();
++vfi;
}
// Second Loop
vfi = face::VFIterator<FaceType>(v[1]);
while (!vfi.End())
{
assert(!vfi.F()->IsD());
for (int j=0;j<3;j++)
{
if( !(vfi.V1()->IsV()) && vfi.V1()->IsRW())
{
vfi.V1()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V0(),vfi.V1()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(!IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V0()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
if( !(vfi.V2()->IsV()) && vfi.V2()->IsRW())
{
vfi.V2()->SetV();
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V0(),vfi.V2()),this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(!IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V0()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
if(Params().SafeHeapUpdate && vfi.V1()->IsRW() && vfi.V2()->IsRW() )
{
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V1(),vfi.V2()),this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
if(!IsSymmetric()){
h_ret.push_back(HeapElem(new MYTYPE(EdgeType(vfi.V2(),vfi.V1()), this->GlobalMark())));
std::push_heap(h_ret.begin(),h_ret.end());
}
}
}
++vfi;
}
}
static void InitQuadric(TriMeshType &m)
{
typename TriMeshType::FaceIterator pf;
typename TriMeshType::VertexIterator pv;
int j;
QH::Init();
// m.ClearFlags();
for(pv=m.vert.begin();pv!=m.vert.end();++pv) // Azzero le quadriche
if( ! (*pv).IsD() && (*pv).IsW())
QH::Qd(*pv).SetZero();
for(pf=m.face.begin();pf!=m.face.end();++pf)
if( !(*pf).IsD() && (*pf).IsR() )
if((*pf).V(0)->IsR() &&(*pf).V(1)->IsR() &&(*pf).V(2)->IsR())
{
QuadricType q;
Plane3<ScalarType,false> p;
// Calcolo piano
p.SetDirection( ( (*pf).V(1)->cP() - (*pf).V(0)->cP() ) ^ ( (*pf).V(2)->cP() - (*pf).V(0)->cP() ));
// Se normalizzo non dipende dall'area
if(!Params().UseArea)
p.Normalize();
p.SetOffset( p.Direction().dot((*pf).V(0)->cP()));
// Calcolo quadrica delle facce
q.ByPlane(p);
for(j=0;j<3;++j)
if( (*pf).V(j)->IsW() )
{
if(Params().QualityWeight)
q*=(*pf).V(j)->Q();
QH::Qd((*pf).V(j)) += q; // Sommo la quadrica ai vertici
}
for(j=0;j<3;++j)
if( (*pf).IsB(j) || Params().QualityQuadric ) // Bordo!
{
Plane3<ScalarType,false> pb; // Piano di bordo
// Calcolo la normale al piano di bordo e la sua distanza
// Nota che la lunghezza dell'edge DEVE essere Normalizzata
// poiche' la pesatura in funzione dell'area e'gia fatta in p.Direction()
// Senza la normalize il bordo e' pesato in funzione della grandezza della mesh (mesh grandi non decimano sul bordo)
pb.SetDirection(p.Direction() ^ ( (*pf).V1(j)->cP() - (*pf).V(j)->cP() ).normalized());
if( (*pf).IsB(j) ) pb.SetDirection(pb.Direction()* (ScalarType)Params().BoundaryWeight); // amplify border planes
else pb.SetDirection(pb.Direction()* (ScalarType)(Params().BoundaryWeight/100.0)); // and consider much less quadric for quality
pb.SetOffset(pb.Direction().dot((*pf).V(j)->cP()));
q.ByPlane(pb);
if( (*pf).V (j)->IsW() ) QH::Qd((*pf).V (j)) += q; // Sommo le quadriche
if( (*pf).V1(j)->IsW() ) QH::Qd((*pf).V1(j)) += q;
}
}
if(Params().ScaleIndependent)
{
vcg::tri::UpdateBounding<TriMeshType>::Box(m);
//Make all quadric independent from mesh size
Params().ScaleFactor = 1e8*pow(1.0/m.bbox.Diag(),6); // scaling factor
//Params().ScaleFactor *=Params().ScaleFactor ;
//Params().ScaleFactor *=Params().ScaleFactor ;
//printf("Scale factor =%f\n",Params().ScaleFactor );
//printf("bb (%5.2f %5.2f %5.2f)-(%5.2f %5.2f %5.2f) Diag %f\n",m.bbox.min[0],m.bbox.min[1],m.bbox.min[2],m.bbox.max[0],m.bbox.max[1],m.bbox.max[2],m.bbox.Diag());
}
}
//
//
//
//
//
//
//static void InitMesh(MESH_TYPE &m){
// Params().CosineThr=cos(Params().NormalThr);
// InitQuadric(m);
// //m.Topology();
// //OldInitQuadric(m,UseArea);
// }
//
CoordType ComputeMinimal()
{
typename TriMeshType::VertexType * v[2];
v[0] = this->pos.V(0);
v[1] = this->pos.V(1);
QuadricType q=QH::Qd(v[0]);
q+=QH::Qd(v[1]);
Point3<QuadricType::ScalarType> x;
bool rt=q.Minimum(x);
if(!rt) { // if the computation of the minimum fails we choose between the two edge points and the middle one.
Point3<QuadricType::ScalarType> x0=Point3d::Construct(v[0]->P());
Point3<QuadricType::ScalarType> x1=Point3d::Construct(v[1]->P());
x.Import((v[0]->P()+v[1]->P())/2);
double qvx=q.Apply(x);
double qv0=q.Apply(x0);
double qv1=q.Apply(x1);
if(qv0<qvx) x=x0;
if(qv1<qvx && qv1<qv0) x=x1;
}
return CoordType::Construct(x);
}
//
//
};
} // namespace tri
} // namespace vcg
#endif

View File

@ -1,609 +0,0 @@
/****************************************************************************
* 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. *
* *
****************************************************************************/
#ifndef __VCG_DECIMATION_TRIFLIP
#define __VCG_DECIMATION_TRIFLIP
#include <vcg/complex/local_optimization.h>
#include <vcg/simplex/face/topology.h>
#include <vcg/space/triangle3.h>
namespace vcg
{
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,
typename TRIMESH_TYPE::ScalarType (*QualityFunc)(
Point3<typename TRIMESH_TYPE::ScalarType> const & p0,
Point3<typename TRIMESH_TYPE::ScalarType> const & p1,
Point3<typename TRIMESH_TYPE::ScalarType> const & p2) = Quality>
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;
}
static void Insert(HeapType& heap, PosType& p, int mark)
{
if(!p.IsBorder() && p.F()->IsW() && p.FFlip()->IsW()) {
MYTYPE* newflip = new MYTYPE(p, mark);
heap.push_back(HeapElem(newflip));
std::push_heap(heap.begin(), heap.end());
}
}
public:
/*!
* Default constructor
*/
inline PlanarEdgeFlip()
{
}
/*!
* Constructor with <I>pos</I> type
*/
inline PlanarEdgeFlip(PosType pos, int mark)
{
_pos = pos;
_localMark = mark;
_priority = this->ComputePriority();
}
/*!
* Copy Constructor
*/
inline PlanarEdgeFlip(const PlanarEdgeFlip &par)
{
_pos = par.GetPos();
_localMark = par.GetMark();
_priority = par.Priority();
}
/*!
*/
~PlanarEdgeFlip()
{
}
/*!
* Parameter
*/
static ScalarType &CoplanarAngleThresholdDeg()
{
static ScalarType _CoplanarAngleThresholdDeg = 0.01f;
return _CoplanarAngleThresholdDeg;
}
inline PosType GetPos() const
{
return _pos;
}
inline int GetMark()const
{
return _localMark;
}
/*!
* Return the LocalOptimization type
*/
ModifierType IsOfType()
{
return TriEdgeFlipOp;
}
/*!
* Check if the pos is updated
*/
bool IsUpToDate()
{
int lastMark = _pos.F()->V(0)->IMark();
lastMark = std::max<int>(lastMark, _pos.F()->V(1)->IMark());
lastMark = std::max<int>(lastMark, _pos.F()->V(2)->IMark());
return ( _localMark >= lastMark );
}
/*!
*
Check if this flipping operation can be performed.
It is a topological and geometrical check.
*/
virtual bool IsFeasible()
{
if(!vcg::face::CheckFlipEdge(*this->_pos.F(), this->_pos.E()))
return false;
if( math::ToDeg( Angle(_pos.FFlip()->cN(), _pos.F()->cN()) ) > CoplanarAngleThresholdDeg() )
return false;
CoordType v0, v1, v2, v3;
int i = _pos.E();
v0 = _pos.F()->P0(i);
v1 = _pos.F()->P1(i);
v2 = _pos.F()->P2(i);
v3 = _pos.F()->FFp(i)->P2(_pos.F()->FFi(i));
// Take the parallelogram formed by the adjacent faces of edge
// If a corner of the parallelogram on extreme of edge to flip is >= 180
// the flip produce two identical faces - avoid this
if( (Angle(v2 - v0, v1 - v0) + Angle(v3 - v0, v1 - v0) >= M_PI) ||
(Angle(v2 - v1, v0 - v1) + Angle(v3 - v1, v0 - v1) >= M_PI))
return false;
// if any of two faces adj to edge in non writable, the flip is unfeasible
if(!_pos.F()->IsW() || !_pos.F()->FFp(i)->IsW())
return false;
return true;
}
/*!
* Compute the priority of this optimization
*/
/*
1
/|\
/ | \
2 | 3
\ | /
\|/
0
*/
ScalarType ComputePriority()
{
CoordType v0, v1, v2, v3;
int i = _pos.E();
v0 = _pos.F()->P0(i);
v1 = _pos.F()->P1(i);
v2 = _pos.F()->P2(i);
v3 = _pos.F()->FFp(i)->P2(_pos.F()->FFi(i));
ScalarType Qa = QualityFunc(v0, v1, v2);
ScalarType Qb = QualityFunc(v0, v3, v1);
ScalarType QaAfter = QualityFunc(v1, v2, v3);
ScalarType QbAfter = QualityFunc(v0, v3, v2);
// < 0 if the average quality of faces improves after flip
_priority = (Qa + Qb - QaAfter - QbAfter) / (ScalarType)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 i = _pos.E();
int j = _pos.F()->FFi(i);
FacePointer f1 = _pos.F();
FacePointer f2 = _pos.F()->FFp(i);
vcg::face::FlipEdge(*_pos.F(), _pos.E());
// avoid texture coordinates swap after flip
if(tri::HasPerWedgeTexCoord(m)) {
f2->WT((j + 1) % 3) = f1->WT((i + 2) % 3);
f1->WT((i + 1) % 3) = f2->WT((j + 2) % 3);
}
}
/*!
*/
const char* Info(TRIMESH_TYPE &m)
{
static char dump[60];
sprintf(dump,"%d -> %d %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).IsW()) {
for(unsigned int i = 0; i < 3; i++) {
if( !(*fi).IsB(i) && !((*fi).FFp(i)->IsD()) && (*fi).FFp(i)->IsW() ) {
if((*fi).V1(i) - (*fi).V0(i) > 0) {
PosType p(&*fi, i);
Insert(heap, p, IMark(mesh));
}
//heap.push_back( HeapElem( new MYTYPE(PosType(&*fi, i), mesh.IMark() )) );
} //endif
} //endfor
}
} //endfor
}
/*!
*/
virtual void UpdateHeap(HeapType &heap)
{
GlobalMark()++;
// after flip, the new edge just created is the next edge
int flipped = (_pos.E() + 1) % 3;
PosType pos(_pos.F(), flipped);
pos.F()->V(0)->IMark() = GlobalMark();
pos.F()->V(1)->IMark() = GlobalMark();
pos.F()->V(2)->IMark() = GlobalMark();
pos.F()->FFp(flipped)->V2(pos.F()->FFi(flipped))->IMark() = GlobalMark();
pos.FlipF(); pos.FlipE();
Insert(heap, pos, GlobalMark());
pos.FlipV(); pos.FlipE();
Insert(heap, pos, GlobalMark());
pos.FlipV(); pos.FlipE();
pos.FlipF(); pos.FlipE();
Insert(heap, pos, GlobalMark());
pos.FlipV(); pos.FlipE();
Insert(heap, pos, GlobalMark());
}
}; // end of PlanarEdgeFlip class
template <class TRIMESH_TYPE, class MYTYPE>
class TriEdgeFlip : public PlanarEdgeFlip<TRIMESH_TYPE, MYTYPE>
{
protected:
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
typedef typename TRIMESH_TYPE::CoordType CoordType;
typedef vcg::face::Pos<FaceType> PosType;
public:
/*!
* Default constructor
*/
inline TriEdgeFlip() {}
/*!
* Constructor with <I>pos</I> type
*/
inline TriEdgeFlip(const PosType pos, int mark)
{
this->_pos = pos;
this->_localMark = mark;
this->_priority = ComputePriority();
}
/*!
* Copy Constructor
*/
inline TriEdgeFlip(const TriEdgeFlip &par)
{
this->_pos = par.GetPos();
this->_localMark = par.GetMark();
this->_priority = par.Priority();
}
ScalarType ComputePriority()
{
/*
1
/|\
/ | \
2 | 3
\ | /
\|/
0
*/
CoordType v0, v1, v2, v3;
int i = this->_pos.E();
v0 = this->_pos.F()->P0(i);
v1 = this->_pos.F()->P1(i);
v2 = this->_pos.F()->P2(i);
v3 = this->_pos.F()->FFp(i)->P2(this->_pos.F()->FFi(i));
// if the sum of angles in v2 e v3 is > 180, then the triangle
// pair is not a delaunay triangulation
ScalarType alpha = math::Abs(Angle(v0 - v2, v1 - v2));
ScalarType beta = math::Abs(Angle(v0 - v3, v1 - v3));
this->_priority = 180 - math::ToDeg((alpha + beta));
return this->_priority;
}
};
// This kind of flip minimize the variance of number of incident faces
// on the vertices of two faces involved in the flip
template <class TRIMESH_TYPE, class MYTYPE>
class TopoEdgeFlip : public PlanarEdgeFlip<TRIMESH_TYPE, MYTYPE>
{
protected:
typedef typename TRIMESH_TYPE::VertexPointer VertexPointer;
typedef typename TRIMESH_TYPE::FaceType FaceType;
typedef typename TRIMESH_TYPE::FacePointer FacePointer;
typedef typename TRIMESH_TYPE::ScalarType ScalarType;
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;
typedef typename TRIMESH_TYPE::FaceIterator FaceIterator;
typedef typename TRIMESH_TYPE::VertexIterator VertexIterator;
public:
/*!
* Default constructor
*/
inline TopoEdgeFlip() {}
/*!
* Constructor with <I>pos</I> type
*/
inline TopoEdgeFlip(const PosType pos, int mark)
{
this->_pos = pos;
this->_localMark = mark;
this->_priority = ComputePriority();
}
/*!
* Copy Constructor
*/
inline TopoEdgeFlip(const TopoEdgeFlip &par)
{
this->_pos = par.GetPos();
this->_localMark = par.GetMark();
this->_priority = par.Priority();
}
ScalarType ComputePriority()
{
/*
1
/|\
/ | \
2 | 3
\ | /
\|/
0
*/
VertexPointer v0, v1, v2, v3;
int i = this->_pos.E();
v0 = this->_pos.F()->V0(i);
v1 = this->_pos.F()->V1(i);
v2 = this->_pos.F()->V2(i);
v3 = this->_pos.F()->FFp(i)->V2(this->_pos.F()->FFi(i));
// This kind of flip minimize the variance of number of incident faces
// on the vertices of two faces involved in the flip
ScalarType avg = (v0->Q() + v1->Q() + v2->Q() + v3->Q()) / 4.0;
ScalarType varbefore = (powf(v0->Q() - avg, 2.0) +
powf(v1->Q() - avg, 2.0) +
powf(v2->Q() - avg, 2.0) +
powf(v3->Q() - avg, 2.0)) / 4.0;
ScalarType varafter = (powf(v0->Q() - 1 - avg, 2.0) +
powf(v1->Q() - 1 - avg, 2.0) +
powf(v2->Q() + 1 - avg, 2.0) +
powf(v3->Q() + 1 - avg, 2.0)) / 4.0;
this->_priority = varafter - varbefore;
return this->_priority;
}
/*!
* Execute the flipping of the edge
*/
void Execute(TRIMESH_TYPE &m)
{
int i = this->_pos.E();
FacePointer f1 = this->_pos.F();
FacePointer f2 = f1->FFp(i);
int j = f1->FFi(i);
// update the number of faces adjacent to vertices
f1->V0(i)->Q()--;
f1->V1(i)->Q()--;
f1->V2(i)->Q()++;
f2->V2(j)->Q()++;
// do the flip
vcg::face::FlipEdge(*this->_pos.F(), this->_pos.E());
// avoid texture coordinates swap after flip
if (tri::HasPerWedgeTexCoord(m)) {
f2->WT((j + 1) % 3) = f1->WT((i + 2) % 3);
f1->WT((i + 1) % 3) = f2->WT((j + 2) % 3);
}
}
static void Init(TRIMESH_TYPE &m, HeapType &heap)
{
// reset quality field for each vertex
VertexIterator vi;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi)
if(!(*vi).IsD())
(*vi).Q() = 0;
// for each vertex, put the number of incident faces in quality field
FaceIterator fi;
for(fi = m.face.begin(); fi != m.face.end(); ++fi)
if(!(*fi).IsD())
for(int i = 0; i < 3; i++)
(*fi).V(i)->Q()++;
TriEdgeFlip<TRIMESH_TYPE, MYTYPE>::Init(m, heap);
}
void UpdateHeap(HeapType &heap)
{
this->GlobalMark()++;
VertexPointer v0, v1, v2, v3;
int flipped = (this->_pos.E() + 1) % 3;
FacePointer f1 = this->_pos.F();
FacePointer f2 = this->_pos.F()->FFp(flipped);
v0 = f1->V0(flipped);
v1 = f1->V1(flipped);
v2 = f1->V2(flipped);
v3 = f2->V2(f1->FFi(flipped));
v0->IMark() = this->GlobalMark();
v1->IMark() = this->GlobalMark();
v2->IMark() = this->GlobalMark();
v3->IMark() = this->GlobalMark();
// edges of the first face, except the flipped edge
for(int i = 0; i < 3; i++) if(i != flipped) {
PosType newpos(f1, i);
Insert(heap, newpos, this->GlobalMark());
}
// edges of the second face, except the flipped edge
for(int i = 0; i < 3; i++) if(i != f1->FFi(flipped)) {
PosType newpos(f2, i);
Insert(heap, newpos, this->GlobalMark());
}
// every edge with v0, v1 v3 of f1
for(int i = 0; i < 3; i++) {
PosType startpos(f1, i);
PosType pos(startpos);
do { // go to the first border (if there is one)
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
// if a border is reached, set startpos here
if(pos.IsBorder())
startpos = pos;
do {
VertexPointer v = pos.VFlip();
if(v != v0 && v != v1 && v != v2 && v != v3)
Insert(heap, pos, this->GlobalMark());
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
}
PosType startpos(f2, (f1->FFi(flipped) + 2) % 3);
PosType pos(startpos);
do { // go to the first border (if there is one)
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
// if a border is reached, set startpos here
if(pos.IsBorder())
startpos = pos;
do {
VertexPointer v = pos.VFlip();
if(v != v0 && v != v1 && v != v2 && v != v3)
Insert(heap, pos, this->GlobalMark());
pos.NextE();
} while(pos != startpos && !pos.IsBorder());
}
};
} // end of namespace tri
} // end of namespace vcg
#endif