Ongoing Rearrangement of filepath
delete old trimesh content
This commit is contained in:
parent
3c7efa7bff
commit
6c0c32ecfe
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue