You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
734 lines
26 KiB
734 lines
26 KiB
/**************************************************************************** |
|
* 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
|
|
|