650 lines
23 KiB
C++
Executable File
650 lines
23 KiB
C++
Executable File
/****************************************************************************
|
|
* VCGLib o o *
|
|
* Visual and Computer Graphics Library o o *
|
|
* _ O _ *
|
|
* Copyright(C) 2004-2016 \/)\/ *
|
|
* 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 QUAD_DIAGONAL_COLLAPSE_H
|
|
#define QUAD_DIAGONAL_COLLAPSE_H
|
|
|
|
#include<vcg/connectors/halfedge_pos.h>
|
|
#include<vcg/complex/algorithms/local_optimization.h>
|
|
#include<vcg/complex/algorithms/smooth.h>
|
|
|
|
#include<set>
|
|
|
|
#include<vcg/space/ray3.h>
|
|
|
|
#include<vcg/complex/algorithms/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 length = 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 length <= 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
|