vcglib/vcg/complex/algorithms/halfedge_quad_clean.h

735 lines
26 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 HALFEDGEQUADCLEAN_H
#define HALFEDGEQUADCLEAN_H
#include <vcg/complex/algorithms/update/halfedge_topology.h>
#include <queue>
#include <set>
#include<vcg/math/base.h>
#include<valarray>
#include<cmath>
namespace vcg
{
namespace tri
{
/*!
* \brief The class provides methods for detecting doublets and singlets and removing them
*
*/
template<class MeshType> class HalfedgeQuadClean
{
protected:
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::EdgePointer EdgePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename MeshType::FacePointer FacePointer;
typedef typename MeshType::VertexIterator VertexIterator;
typedef typename MeshType::EdgeIterator EdgeIterator;
typedef typename MeshType::HEdgeIterator HEdgeIterator;
typedef typename MeshType::FaceIterator FaceIterator;
/*! Adds to a queue all faces on the 1-ring of a given vertex
*
* \param q Queue of faces
* \param vp Pointer to the vertex
*
*/
static void add_faces(queue<FacePointer> &q, VertexPointer vp)
{
vector<FacePointer> faces = HalfEdgeTopology<MeshType>::get_incident_faces(vp);
for(typename vector<FacePointer>::iterator fi = faces.begin(); fi != faces.end(); ++fi)
{
if(*fi)
q.push(*fi);
}
}
/*! Removes doublets from all the faces into a queue until the queue empties
*
* \param m Mesh
* \param faces Set of all faces modified by the removals
* \param q Queue of faces to clean
*
*/
static void remove_doublets(MeshType &m, set<FacePointer> &faces, queue<FacePointer> &q)
{
while(!q.empty())
{
FacePointer fp = q.front();
q.pop();
if( !fp->IsD() )
{
faces.insert(remove_doublet(m,fp, &q));
}
}
}
/*! Removes a doublet and all the other ones that the removal may have generated
*
* \param m Mesh
* \param fp Pointer to the face to clean from doublets
* \param Queue of faces to check for new doublets
*
* \return The new face generated after doublet removal
*/
static FacePointer remove_doublet(MeshType &m, FacePointer fp, queue<FacePointer> *q = NULL)
{
vector<HEdgePointer> hedges = HalfEdgeTopology<MeshType>::find_doublet_hedges_quad(fp);
assert(hedges.size() <= 4);
switch(hedges.size())
{
// No doublets
case 0:
return NULL;
// A single doublet
case 1:
if(q)
{
add_faces(*q, hedges[0]->HNp()->HVp());
add_faces(*q, hedges[0]->HPp()->HVp());
}
return HalfEdgeTopology<MeshType>::doublet_remove_quad(m, hedges[0]->HVp());
// Two doublets on the same face
case 2:
{
if(q)
{
add_faces(*q, hedges[0]->HNp()->HVp());
add_faces(*q, hedges[0]->HPp()->HVp());
}
FacePointer fp1 = HalfEdgeTopology<MeshType>::doublet_remove_quad(m, hedges[0]->HVp());
// Removal of the doublet may generate a singlet
if(HalfEdgeTopology<MeshType>::is_singlet_quad(fp1))
{
HEdgePointer hp = HalfEdgeTopology<MeshType>::singlet_remove_quad(m, fp1);
if(hp)
{
if(q)
{
if(hp->HFp())
q->push(hp->HFp());
if(hp->HOp()->HFp())
q->push(hp->HOp()->HFp());
}
int valence1, valence2;
valence1 = HalfEdgeTopology<MeshType>::vertex_valence(hp->HVp());
valence2 = HalfEdgeTopology<MeshType>::vertex_valence(hp->HOp()->HVp());
// Check if the removal of the singlet generated other singlets, then iteratively remove them
while(valence1 == 1 || valence2 == 1)
{
assert(! (valence1 == 1 && valence2 == 1));
FacePointer singlet_pointer;
if(valence1 == 1 )
singlet_pointer = hp->HFp();
else
singlet_pointer = hp->HOp()->HFp();
hp = HalfEdgeTopology<MeshType>::singlet_remove_quad(m, singlet_pointer);
if(!hp)
break;
if(q)
{
if(hp->HFp())
q->push(hp->HFp());
if(hp->HOp()->HFp())
q->push(hp->HOp()->HFp());
}
valence1 = HalfEdgeTopology<MeshType>::vertex_valence(hp->HVp());
valence2 = HalfEdgeTopology<MeshType>::vertex_valence(hp->HOp()->HVp());
}
}
}
return fp1;
}
// Four doublets: simply remove one of the two faces
case 4:
HalfEdgeTopology<MeshType>::remove_face(m,fp->FHp()->HOp()->HFp());
return fp;
default:
assert(0);
}
}
public:
/*! Removes doublets from all the faces on the 1-ring of the given vertices
*
* \param m Mesh
* \param Set of all faces modified by the removals
* \param vertices Vector of vertices that will be
*
*/
static void remove_doublets(MeshType &m, set<FacePointer> &faces, vector<VertexPointer> vertices)
{
queue<FacePointer> q;
for(typename vector<VertexPointer>::iterator vi = vertices.begin(); vi != vertices.end(); ++vi)
{
vector<FacePointer> inc_faces = HalfEdgeTopology<MeshType>::get_incident_faces(*vi);
for(typename vector<FacePointer>::iterator fi = inc_faces.begin(); fi != inc_faces.end(); ++fi)
{
if(*fi)
if( !((*fi)->IsD()) )
q.push(*fi);
}
}
remove_doublets(m, faces, q);
}
/*! Removes doublets from all the faces on the 1-ring of the given vertices
*
* \param m Mesh
* \param vertices Vector of vertices that will be
*
*/
static void remove_doublets(MeshType &m, vector<VertexPointer> vertices)
{
set<FacePointer> faces;
remove_doublets(m,faces,vertices);
}
/*! Removes doublets from a set of faces
*
* \param m Mesh
* \param set of faces to clean
*
*/
static void remove_doublets(MeshType &m, set<FacePointer> &faces)
{
queue<FacePointer> q;
for(typename set<FacePointer>::iterator fi = faces.begin(); fi != faces.end(); ++fi)
{
if(*fi)
if( !((*fi)->IsD()) )
q.push(*fi);
}
remove_doublets(m, faces, q);
}
/*! Removes all doublets from a mesh
*
* \param m Mesh
*
* \return Number of doublets removed
*/
static int remove_doublets(MeshType &m)
{
int count;
int removed = 0;
do
{
count = 0;
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
{
if( !((*fi).IsD()) )
{
if(remove_doublet(m, &(*fi)))
count++;
}
}
removed += count;
}while(count != 0);
return removed;
}
/*! Removes all singlets from a mesh
*
* \param m Mesh
*
* \return Number of singlets removed
*/
static int remove_singlets(MeshType &m)
{
int removed = 0;
int count;
do
{
count = 0;
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
{
if( !((*fi).IsD()) )
{
if( HalfEdgeTopology<MeshType>::is_singlet_quad(&(*fi)) )
{
HalfEdgeTopology<MeshType>::singlet_remove_quad(m, &(*fi));
count++;
}
}
}
removed += count;
}while(count != 0);
return removed;
}
/*! Checks if a mesh has singlets
*
* \param m Mesh
*
* \return Value indicating whether mesh has singlets
*/
static bool has_singlets(MeshType &m)
{
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
{
if( !((*fi).IsD()) )
{
if( HalfEdgeTopology<MeshType>::is_singlet_quad(&(*fi)) )
return true;
}
}
return false;
}
/*! Checks if a mesh has doublets
*
* \param m Mesh
*
* \return Value indicating whether mesh has doublets
*/
static bool has_doublets(MeshType &m)
{
for(FaceIterator fi = m.face.begin(); fi != m.face.end(); ++fi)
{
if( !((*fi).IsD()) )
{
if( HalfEdgeTopology<MeshType>::has_doublet_quad(&(*fi)) )
return true;
}
}
return false;
}
/*! Performs rotation of selected edges computing the best rotation
*
*
* \param m Mesh
* \param hedges Vector of halfedges representing the edges to rotate
* \param faces Set of modified faces (every modified face will be inserted into this set)
*
*/
template<class PriorityType>
static void flip_edges(MeshType &m, vector<HEdgePointer> &hedges, set<FacePointer> &faces)
{
for(typename vector<HEdgePointer>::iterator hi = hedges.begin(); hi != hedges.end(); ++hi)
{
// edge must be shared by two faces
if((*hi)->HFp() && (*hi)->HOp()->HFp())
{
if(!(*hi)->IsD() && !(*hi)->HFp()->IsD() && !(*hi)->HOp()->HFp()->IsD())
{
typename PriorityType::FlipType fliptype = PriorityType::best_flip( *hi );
if(fliptype != PriorityType::NO_FLIP)
{
vector<VertexPointer> vertices;
// Record old vertices for future removal of doublets
vertices.push_back((*hi)->HVp());
vertices.push_back((*hi)->HOp()->HVp());
// Add modified faces into the set of faces
faces.insert((*hi)->HFp());
faces.insert((*hi)->HOp()->HFp());
bool cw = (fliptype == PriorityType::CW_FLIP);
HalfEdgeTopology<MeshType>::edge_rotate_quad((*hi),cw);
// After a rotation doublets may have generated
remove_doublets(m, faces, vertices);
}
}
}
}
}
/*! Performs edge rotations on the entire mesh computing the best rotation for each edge
*
*
* \param m Mesh
*
*/
template <class PriorityType>
static int flip_edges(MeshType &m)
{
int count;
int ret=0;
do
{
count = 0;
for(typename MeshType::EdgeIterator ei = m.edge.begin(); ei != m.edge.end(); ++ei)
{
if( !(ei->IsD()) )
{
HEdgePointer hp = ei->EHp();
if(hp->HFp() && hp->HOp()->HFp())
{
typename PriorityType::FlipType fliptype = PriorityType::best_flip( hp );
if(fliptype != PriorityType::NO_FLIP)
{
vector<VertexPointer> vertices;
// Record old vertices for future removal of doublets
vertices.push_back(hp->HVp());
vertices.push_back(hp->HOp()->HVp());
bool cw = (fliptype == PriorityType::CW_FLIP);
HalfEdgeTopology<MeshType>::edge_rotate_quad(hp,cw);
// After a rotation doublets may have generated
remove_doublets(m, vertices);
count++;
}
}
}
}
ret+=count;
}while(count != 0);
return ret;
}
};
/*!
* \brief Generic priority for edge rotations
*
*/
template <class MeshType> class EdgeFlipPriority
{
public:
typedef typename MeshType::HEdgePointer HEdgePointer;
/// Possible types of rotation
enum FlipType { NO_FLIP, CW_FLIP, CCW_FLIP};
/*!
* Computes the best rotation to perform
*
* \param hp Pointer to an halfedge representing the edge to rotate
*
* \return The best type of rotation
*/
static FlipType best_flip( HEdgePointer hp);
};
/*!
* \brief Priority based on maximizing vertices regularity
*
*/
template <class MeshType> class VertReg: public EdgeFlipPriority<MeshType>
{
public:
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::EdgePointer EdgePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename MeshType::FacePointer FacePointer;
typedef EdgeFlipPriority<MeshType> Base;
typedef typename Base::FlipType FlipType;
/// Default Constructor
VertReg(){}
~VertReg(){}
/*!
* Computes the best rotation to perform for maximizing vertices regularity
*
* \param hp Pointer to an halfedge representing the edge to rotate
*
* \return The best type of rotation
*/
static FlipType best_flip( HEdgePointer hp)
{
assert(hp);
assert(!hp->IsD());
vector<VertexPointer> vps1 = HalfEdgeTopology<MeshType>::getVertices(hp->HFp(), hp);
vector<VertexPointer> vps2 = HalfEdgeTopology<MeshType>::getVertices(hp->HOp()->HFp(), hp->HOp());
valarray<double> valences(6);
/*
Indices of vertices into vector valences:
3-------2
| |
| f1 |
| |
0-------1
| |
| f2 |
| |
4-------5
*/
// Compute valencies of vertices
for(int i=0; i<4; i++)
valences[i] = HalfEdgeTopology<MeshType>::vertex_valence(vps1[i]) - 4 ;
valences[4] = HalfEdgeTopology<MeshType>::vertex_valence(vps2[2]) - 4;
valences[5] = HalfEdgeTopology<MeshType>::vertex_valence(vps2[3]) - 4;
// Vector containing sums of the valencies in all the possible cases: no rotation, ccw rotation, cw rotation
vector<int> sums;
// positions:
// sums[0]: now (No rotation)
// sums[1]: flipping ccw
// sums[2]: flipping cw
// No rotation
sums.push_back( pow(valences, 2.0).sum() );
// CCW
valences[0]--;
valences[1]--;
valences[2]++;
valences[4]++;
sums.push_back( pow(valences, 2.0).sum() );
// CW
valences[2]--;
valences[4]--;
valences[3]++;
valences[5]++;
sums.push_back( pow(valences, 2.0).sum() );
if( sums[2]<= sums[1] && sums[2]< sums[0] )
return Base::CW_FLIP;
else if( sums[1]< sums[2] && sums[1]< sums[0] )
return Base::CCW_FLIP;
return Base::NO_FLIP;
}
};
/*!
* \brief Priority based on minimizing homeometry
*
*/
template <class MeshType> class Homeometry: public EdgeFlipPriority<MeshType>
{
public:
typedef typename MeshType::VertexPointer VertexPointer;
typedef typename MeshType::EdgePointer EdgePointer;
typedef typename MeshType::HEdgePointer HEdgePointer;
typedef typename MeshType::FacePointer FacePointer;
typedef EdgeFlipPriority<MeshType> Base;
typedef typename Base::FlipType FlipType;
/// Default Constructor
Homeometry(){}
~Homeometry(){}
/*!
* Computes the best rotation to perform for minimizing the distance from homeometry
*
* \param hp Pointer to an halfedge representing the edge to rotate
*
* \return The best type of rotation
*/
static FlipType best_flip( HEdgePointer hp)
{
assert(hp);
assert(!hp->IsD());
vector<VertexPointer> face1 = HalfEdgeTopology<MeshType>::getVertices(hp->HFp(), hp);
vector<VertexPointer> face2 = HalfEdgeTopology<MeshType>::getVertices(hp->HOp()->HFp(), hp->HOp());
// Vector containing sums of the valencies in all the possible cases: no rotation, ccw rotation, cw rotation
vector<int> sums;
// positions:
// sums[0]: now (No rotation)
// sums[1]: flipping ccw
// sums[2]: flipping cw
// No rotation
sums.push_back( distance_from_homeometry(face1, face2, 0) );
// CCW
face1[1] = face2[2];
face2[1] = face1[2];
sums.push_back( distance_from_homeometry(face1, face2, 1) );
// CW
face1[2] = face2[3];
face2[2] = face1[3];
sums.push_back( distance_from_homeometry(face1, face2, 2) );
if( sums[2]<= sums[1] && sums[2]< sums[0] )
return Base::CW_FLIP;
else if( sums[1]< sums[2] && sums[1]< sums[0] )
return Base::CCW_FLIP;
return Base::NO_FLIP;
}
protected:
/*!
* Computes the area of a quad
*
* \param vertices Vector of the four vertices of the quad
*
* \return Area of the quad
*/
static float area(vector<VertexPointer> &vertices)
{
assert(vertices.size() == 4);
float tri1 = Norm( (vertices[1]->cP() - vertices[0]->cP()) ^ (vertices[2]->cP() - vertices[0]->cP()) );
float tri2 = Norm( (vertices[2]->cP() - vertices[0]->cP()) ^ (vertices[3]->cP() - vertices[0]->cP()) );
return (tri1+tri2) / 2;
}
/*!
* Computes the distance of two faces from being homeometirc
*
* \param face1 Vector of vertices belonging to the first face
* \param face2 Vector of vertices belonging to the second face
* \param i Index of the edge to compute
*
* \return The computed homeometry
*/
static float distance_from_homeometry(vector<VertexPointer> &face1, vector<VertexPointer> &face2, int i)
{
// Ideal edge length
float mu = sqrt( (area(face1) + area(face2)) / 2 );
// Length of the i-th edge (the edge changed with a rotation)
float edge_length = Distance( face1[i]->cP(), face1[i+1]->cP() );
// Length of the diagonals
valarray<float> diagonals(4);
diagonals[0] = Distance( face1[0]->cP(), face1[2]->cP() );
diagonals[1] = Distance( face1[1]->cP(), face1[3]->cP() );
diagonals[2] = Distance( face2[0]->cP(), face2[2]->cP() );
diagonals[3] = Distance( face2[1]->cP(), face2[3]->cP() );
// Ideal diagonal length
float ideal_diag_length = SQRT_TWO*mu;
float sum_diagonals = pow(diagonals - ideal_diag_length, 2.0).sum();
return (pow (edge_length - mu , static_cast<float>(2.0)) + sum_diagonals);
}
};
}
}
#endif // HALFEDGEQUADCLEAN_H