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.
1777 lines
61 KiB
1777 lines
61 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 VCG_HEDGE_TOPOLOGY |
|
#define VCG_HEDGE_TOPOLOGY |
|
|
|
#include <vcg/connectors/halfedge_pos.h> |
|
|
|
namespace vcg |
|
{ |
|
namespace tri |
|
{ |
|
|
|
/*! |
|
* \brief Class containing functions to modify the topology of a halfedge based mesh |
|
* |
|
*/ |
|
template <class MeshType> class HalfEdgeTopology |
|
{ |
|
public: |
|
|
|
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; |
|
|
|
|
|
/*! |
|
* Collpases an edge shared by two quads, generating only quads. |
|
* Made by a series of a vertex rotation and a diagonal collapse. |
|
* |
|
* \param m Mesh |
|
* \param hp hegde to be collapsed |
|
* \param vp Vertex that will be rotated |
|
* |
|
* \return Pointer to the new vertex |
|
*/ |
|
static VertexPointer edge_collapse_quad(MeshType &m, HEdgePointer hp, VertexPointer vp) |
|
{ |
|
assert(vp); |
|
assert(hp); |
|
assert(MeshType::HEdgeType::HasHVAdjacency()); |
|
assert(hp->HVp() == vp || hp->HOp()->HVp() == vp); |
|
assert(hp->HFp()->VN() == 4); |
|
assert(hp->HOp()->HFp()->VN() == 4); |
|
|
|
|
|
VertexPointer vp_opp; |
|
FacePointer fp; |
|
|
|
if( hp->HVp() == vp ) |
|
hp = hp->HOp(); |
|
|
|
//retrieve opposite vertex and right face for diagonal collapse |
|
vp_opp = hp->HVp(); |
|
fp = hp->HFp(); |
|
|
|
VertexPointer vp_rot = vertex_rotate_quad( vp ); |
|
|
|
assert(vp_rot == vp); |
|
|
|
return diagonal_collapse_quad( m, fp, vp ); |
|
} |
|
|
|
/*! |
|
* Collpases a diagonal in a quad. |
|
* |
|
* |
|
* \param m Mesh |
|
* \param fp Face where diagonal resides |
|
* \param vp One of the two vertices of the diagonal |
|
* |
|
* \return Pointer to the new vertex |
|
*/ |
|
static VertexPointer diagonal_collapse_quad(MeshType &m, FacePointer fp, VertexPointer vp) |
|
{ |
|
|
|
assert(MeshType::VertexType::HasVHAdjacency()); |
|
assert(MeshType::FaceType::HasFHAdjacency()); |
|
assert(MeshType::HEdgeType::HasHVAdjacency()); |
|
assert(MeshType::HEdgeType::HasHFAdjacency()); |
|
assert(MeshType::HEdgeType::HasHOppAdjacency()); |
|
assert(MeshType::HEdgeType::HasHPrevAdjacency()); |
|
|
|
assert(fp); |
|
assert(fp->FHp()); |
|
assert(fp->VN() == 4); |
|
assert(!fp->IsD()); |
|
|
|
assert( !has_doublet_quad(fp) ); |
|
assert(!is_singlet_quad(fp)); |
|
|
|
bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); |
|
bool HasEH = MeshType::EdgeType::HasEHAdjacency(); |
|
|
|
HEdgePointer hp; |
|
|
|
std::vector<VertexPointer> vps = getVertices(fp); |
|
|
|
assert(vps.size()==4); |
|
|
|
for(unsigned int i = 0; i< vps.size(); i++) |
|
{ |
|
// all vertices must be different |
|
assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); |
|
} |
|
|
|
hp = fp->FHp(); |
|
|
|
while(hp->HVp() != vp) |
|
hp= hp->HNp(); |
|
|
|
std::vector<HEdgePointer> hps = getHEdges(fp,hp); |
|
|
|
assert(vp == hps[0]->HVp()); |
|
|
|
VertexPointer opposite_vertex = hps[2]->HVp(); |
|
|
|
vp->P() = (vp->P() + opposite_vertex->P() )/2; |
|
change_vertex(opposite_vertex, vp); |
|
|
|
hps[0]->HOp()->HOp()=hps[1]->HOp(); |
|
hps[1]->HOp()->HOp()=hps[0]->HOp(); |
|
|
|
hps[2]->HOp()->HOp()=hps[3]->HOp(); |
|
hps[3]->HOp()->HOp()=hps[2]->HOp(); |
|
|
|
for(int i=0; i<4; i++) |
|
{ |
|
if(hps[i]->HVp()->VHp() == hps[i]) |
|
hps[i]->HVp()->VHp() = hps[(i+4-1)%4]->HOp(); |
|
} |
|
|
|
if(HasHE) |
|
{ |
|
hps[1]->HOp()->HEp()=hps[0]->HEp(); |
|
hps[3]->HOp()->HEp()=hps[2]->HEp(); |
|
} |
|
|
|
if(HasEH && HasHE) |
|
{ |
|
for(int i=0; i<3; i+=2) |
|
{ |
|
if(hps[i]->HEp()->EHp() == hps[i]) |
|
hps[i]->HEp()->EHp() = hps[i]->HOp(); |
|
} |
|
} |
|
|
|
// there are no faces, remove hedges and edge |
|
/* |
|
/\ |
|
hps[1]->HOp() / \ hps[0]->HOp() |
|
/ \ |
|
------ ------ |
|
| \ / | |
|
| \ / | |
|
|_______\/_______| |
|
*/ |
|
|
|
bool b[2]; |
|
|
|
b[0] = (hps[0]->HOp()->HFp() == NULL && hps[1]->HOp()->HFp() == NULL); |
|
b[1] = (hps[2]->HOp()->HFp() == NULL && hps[3]->HOp()->HFp() == NULL); |
|
|
|
for( int i=0, j=0; i < 4; i+=2, j++ ) |
|
{ |
|
if(b[j]) |
|
{ |
|
if(HasHE) |
|
vcg::tri::Allocator<MeshType>::DeleteEdge(m, *(hps[i]->HEp()) ); |
|
|
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hps[i]->HOp()) ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hps[i+1]->HOp()) ); |
|
|
|
hps[i+1]->HVp()->VHp() = NULL; |
|
|
|
if(!b[(j+1)%2]) |
|
{ |
|
hps[i]->HOp()->HNp()->HPp() = hps[i+1]->HOp()->HPp(); |
|
hps[i+1]->HOp()->HPp()->HNp() = hps[i]->HOp()->HNp(); |
|
|
|
if(vp->VHp() == hps[i+1]->HOp()) |
|
vp->VHp() = hps[(i+3)%4]->HOp(); |
|
} |
|
|
|
else |
|
vp->VHp() = NULL; |
|
|
|
} |
|
} |
|
|
|
|
|
vcg::tri::Allocator<MeshType>::DeleteFace(m, *(fp) ); |
|
vcg::tri::Allocator<MeshType>::DeleteVertex(m, *(opposite_vertex) ); |
|
if(HasHE) |
|
{ |
|
vcg::tri::Allocator<MeshType>::DeleteEdge(m, *(hps[1]->HEp()) ); |
|
vcg::tri::Allocator<MeshType>::DeleteEdge(m, *(hps[3]->HEp()) ); |
|
} |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hps[0]) ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hps[1]) ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hps[2]) ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hps[3]) ); |
|
|
|
return vp; |
|
|
|
} |
|
|
|
/*! |
|
* Removes a doublet merging the two quads in one |
|
* |
|
* \param m Mesh |
|
* \param vp Vertex shared by the two consecutive edges of the doublet |
|
* |
|
* \return Pointer to the new face |
|
*/ |
|
static FacePointer doublet_remove_quad(MeshType &m, VertexPointer vp) |
|
{ |
|
assert(vp); |
|
|
|
HEdgePointer hp = vp->VHp(); |
|
|
|
assert(hp); |
|
|
|
FacePointer fp1 = hp->HFp(); |
|
FacePointer fp2 = hp->HOp()->HFp(); |
|
|
|
assert(!is_singlet_quad(fp1)); |
|
assert(!is_singlet_quad(fp2)); |
|
|
|
|
|
assert( fp1 ); |
|
assert( fp2 ); |
|
|
|
assert( hp->HOp()->HNp()->HOp() == hp->HPp() ); |
|
|
|
assert( fp1->VN() == 4); |
|
assert( fp2->VN() == 4); |
|
|
|
// end of check |
|
|
|
hp->HNp()->HPp() = hp->HOp()->HPp(); |
|
hp->HOp()->HPp()->HNp() = hp->HNp(); |
|
|
|
hp->HPp()->HPp()->HNp() = hp->HOp()->HNp()->HNp(); |
|
hp->HOp()->HNp()->HNp()->HPp() = hp->HPp()->HPp(); |
|
|
|
|
|
if( hp->HOp()->HVp()->VHp() == hp->HOp() ) |
|
hp->HOp()->HVp()->VHp() = hp->HNp(); |
|
|
|
if( hp->HPp()->HVp()->VHp() == hp->HPp() ) |
|
hp->HPp()->HVp()->VHp() = hp->HPp()->HPp()->HNp(); |
|
|
|
|
|
hp->HNp()->HPp()->HFp() = fp1; |
|
hp->HOp()->HNp()->HNp()->HFp() = fp1; |
|
|
|
if(fp1->FHp() == hp || fp1->FHp() == hp->HPp()) |
|
fp1->FHp() = hp->HNp(); |
|
|
|
|
|
vcg::tri::Allocator<MeshType>::DeleteVertex(m, *vp); |
|
if(MeshType::HEdgeType::HasHEAdjacency()) |
|
{ |
|
vcg::tri::Allocator<MeshType>::DeleteEdge(m, *(hp->HEp()) ); |
|
vcg::tri::Allocator<MeshType>::DeleteEdge(m, *(hp->HPp()->HEp()) ); |
|
} |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *hp ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hp->HOp()) ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hp->HPp()) ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m, *(hp->HPp()->HOp()) ); |
|
vcg::tri::Allocator<MeshType>::DeleteFace(m, *fp2 ); |
|
|
|
|
|
return fp1; |
|
|
|
|
|
} |
|
|
|
|
|
/*! |
|
* Removes a singlet replacing it with an edge |
|
* |
|
* \param m Mesh |
|
* \param fp Face that should be a singlet quad |
|
* |
|
* \return Pointer to an halfdedge representing the new edge |
|
*/ |
|
static HEdgePointer singlet_remove_quad(MeshType &m, FacePointer fp) |
|
{ |
|
/* |
|
2 |
|
/ \ |
|
/ \ |
|
| | |
|
| | |
|
| 1 | |
|
| /\ | |
|
\ | | / |
|
\ \/ / |
|
3 |
|
*/ |
|
|
|
assert( is_singlet_quad(fp) ); |
|
|
|
bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); |
|
bool HasEH = MeshType::EdgeType::HasEHAdjacency(); |
|
|
|
std::vector<HEdgePointer> ext_hedges; |
|
|
|
std::vector<HEdgePointer> int_hedges = getHEdges(fp); |
|
|
|
vcg::tri::Allocator<MeshType>::DeleteFace( m, *(fp) ); |
|
|
|
for(typename std::vector<HEdgePointer>::iterator hi = int_hedges.begin(); hi != int_hedges.end();++hi) |
|
{ |
|
|
|
if((*hi)->HOp()->HFp() != fp) |
|
ext_hedges.push_back((*hi)->HOp()); |
|
|
|
else if(vertex_valence((*hi)->HVp()) == 1) |
|
{ |
|
vcg::tri::Allocator<MeshType>::DeleteVertex( m, *((*hi)->HVp()) ); |
|
if(HasHE) |
|
vcg::tri::Allocator<MeshType>::DeleteEdge( m, *((*hi)->HEp()) ); |
|
} |
|
|
|
} |
|
|
|
for(typename std::vector<HEdgePointer>::iterator hi = int_hedges.begin(); hi != int_hedges.end();++hi) |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge( m, *(*hi) ); |
|
|
|
assert(ext_hedges.size() == 2); |
|
|
|
|
|
if(ext_hedges[0]->HFp() || ext_hedges[1]->HFp()) |
|
{ |
|
ext_hedges[0]->HOp() = ext_hedges[1]; |
|
ext_hedges[1]->HOp() = ext_hedges[0]; |
|
|
|
if(HasHE) |
|
{ |
|
vcg::tri::Allocator<MeshType>::DeleteEdge( m, *(ext_hedges[1]->HEp()) ); |
|
|
|
ext_hedges[1]->HEp() = ext_hedges[0]->HEp(); |
|
|
|
if(HasEH) |
|
ext_hedges[0]->HEp()->EHp() = ext_hedges[0]; |
|
} |
|
|
|
ext_hedges[0]->HVp()->VHp() = ext_hedges[0]; |
|
ext_hedges[1]->HVp()->VHp() = ext_hedges[1]; |
|
|
|
return ext_hedges[0]; |
|
} |
|
|
|
else |
|
{ |
|
ext_hedges[0]->HVp()->VHp() = NULL; |
|
ext_hedges[1]->HVp()->VHp() = NULL; |
|
|
|
if(HasHE) |
|
{ |
|
vcg::tri::Allocator<MeshType>::DeleteEdge( m, *( ext_hedges[0]->HEp()) ); |
|
vcg::tri::Allocator<MeshType>::DeleteEdge( m, *( ext_hedges[1]->HEp()) ); |
|
} |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge( m, *( ext_hedges[0]) ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge( m, *( ext_hedges[1]) ); |
|
|
|
return NULL; |
|
} |
|
|
|
} |
|
|
|
|
|
/*! |
|
* Rotates a non-border edge shared by two quads |
|
* |
|
* \param hp Edge to be rotated |
|
* \param cw flag denoting a clockwise or counter-clockwise rotation |
|
* |
|
* \return Pointer to the rotated edge |
|
*/ |
|
static HEdgePointer edge_rotate_quad(HEdgePointer hp, bool cw) |
|
{ |
|
|
|
assert( MeshType::HEdgeType::HasHFAdjacency() ); |
|
assert( MeshType::HEdgeType::HasHOppAdjacency() ); |
|
assert( MeshType::FaceType::HasFHAdjacency() ); |
|
|
|
|
|
FacePointer fp1 = hp->HFp(); |
|
FacePointer fp2 = hp->HOp()->HFp(); |
|
|
|
|
|
assert( fp1 ); |
|
assert( fp1->VN() == 4 ); |
|
|
|
assert( fp2 ); |
|
assert( fp2->VN() == 4 ); |
|
|
|
assert(!is_singlet_quad(fp1)); |
|
assert(!is_singlet_quad(fp2)); |
|
|
|
assert(!has_doublet_quad(fp1)); |
|
assert(!has_doublet_quad(fp2)); |
|
|
|
std::vector<FacePointer> fps; |
|
typedef std::vector<HEdgePointer> hedge_vect; |
|
std::vector<hedge_vect> hps; |
|
|
|
fps.push_back(fp1); |
|
fps.push_back(fp2); |
|
|
|
|
|
hps.push_back( getHEdges( fp1, hp ) ); |
|
hps.push_back( getHEdges( fp2, hp->HOp() ) ); |
|
|
|
|
|
for(int i=0; i< 2; i++) |
|
{ |
|
|
|
int j = (i+1)%2; |
|
|
|
// uguali sia per cw che per ccw |
|
hps[i][1]->HPp() = hps[j][3]; |
|
hps[j][3]->HNp() = hps[i][1]; |
|
|
|
if(hps[j][0]->HVp()->VHp() == hps[j][0]) |
|
hps[j][0]->HVp()->VHp() = hps[i][1]; |
|
|
|
if(cw) |
|
{ |
|
hps[i][0]->HNp() = hps[j][3]; |
|
hps[j][3]->HPp() = hps[i][0]; |
|
|
|
hps[j][2]->HNp() = hps[j][0]; |
|
hps[j][0]->HPp() = hps[j][2]; |
|
|
|
hps[j][0]->HVp() = hps[j][3]->HVp(); |
|
|
|
hps[j][3]->HFp() = fps[i]; |
|
|
|
if(fps[j]->FHp() == hps[j][3]) |
|
fps[j]->FHp() = hps[j][0]; |
|
} |
|
else |
|
{ |
|
hps[i][0]->HNp() = hps[i][2]; |
|
hps[i][2]->HPp() = hps[i][0]; |
|
|
|
hps[i][1]->HNp() = hps[j][0]; |
|
hps[j][0]->HPp() = hps[i][1]; |
|
|
|
hps[j][0]->HVp() = hps[i][2]->HVp(); |
|
|
|
hps[i][1]->HFp() = fps[j]; |
|
|
|
if(fps[i]->FHp() == hps[i][1]) |
|
fps[i]->FHp() = hps[i][0]; |
|
} |
|
|
|
} |
|
|
|
return hp; |
|
|
|
} |
|
|
|
|
|
|
|
/*! |
|
* Rotates a non-border vertex shared by only quads |
|
* |
|
* \param vp Vertex to be rotated |
|
* |
|
* \return Pointer to the rotated vertex |
|
*/ |
|
static VertexPointer vertex_rotate_quad(VertexPointer vp) |
|
{ |
|
|
|
assert(MeshType::VertexType::HasVHAdjacency()); |
|
assert( vp->VHp() ); |
|
|
|
vcg::hedge::Pos<MeshType> p(vp->VHp(), true); |
|
|
|
HEdgePointer hep = p.HE(); |
|
|
|
typedef std::vector<HEdgePointer> hedge_vect; |
|
std::vector<hedge_vect> hedges; |
|
|
|
do |
|
{ |
|
assert( p.F() ); |
|
assert( p.F()->VN() == 4); |
|
|
|
hedges.push_back(getHEdges(p.F(), p.HE())); |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
}while(p.HE() != hep); |
|
|
|
|
|
int size = hedges.size(); |
|
|
|
for(int i=0; i< size; i++) |
|
{ |
|
hedges[i][0]->HNp() = hedges[i][2]; |
|
hedges[i][2]->HPp() = hedges[i][0]; |
|
|
|
assert(hedges[i][0]->HOp() == hedges[(i+size-1)%size][3]); |
|
|
|
hedges[i][2]->HNp() = hedges[(i+1)%size][1]; |
|
hedges[(i+1)%size][1]->HPp() = hedges[i][2]; |
|
|
|
hedges[(i+1)%size][1]->HNp() = hedges[i][3]; |
|
hedges[i][3]->HPp() = hedges[(i+1)%size][1]; |
|
|
|
hedges[(i+1)%size][1]->HFp() = hedges[i][3]->HFp(); |
|
|
|
if(hedges[i][3]->HVp()->VHp() == hedges[i][3]) |
|
hedges[i][3]->HVp()->VHp() = hedges[(i+1)%size][1]; |
|
|
|
hedges[i][3]->HVp() = hedges[(i+1)%size][2]->HVp(); |
|
|
|
if(hedges[i][0]->HFp()->FHp() == hedges[i][1]) |
|
hedges[i][0]->HFp()->FHp() = hedges[i][0]; |
|
|
|
} |
|
return vp; |
|
|
|
} |
|
|
|
|
|
/*! |
|
* Collapses a generic edge |
|
* |
|
* \param m Mesh |
|
* \param hp Edge to be collapsed |
|
* \param vp Vertex to be deleted |
|
* |
|
* \return Pointer to the other vertex belonging to the collapsed edge |
|
*/ |
|
static VertexPointer edge_collapse(MeshType &m, HEdgePointer hp, VertexPointer vp) |
|
{ |
|
|
|
assert(MeshType::VertexType::HasVHAdjacency()); |
|
assert(MeshType::HEdgeType::HasHOppAdjacency()); |
|
assert(MeshType::HEdgeType::HasHVAdjacency()); |
|
assert(MeshType::HEdgeType::HasHPrevAdjacency()); |
|
|
|
if( hp->HFp() ) |
|
assert(hp->HFp()->VN() > 3); |
|
|
|
if( hp->HOp()->HFp()) |
|
assert(hp->HOp()->HFp()->VN() > 3); |
|
|
|
assert(hp->HFp() || hp->HOp()->HFp()); |
|
assert(hp->HVp() == vp || hp->HOp()->HVp() == vp); |
|
|
|
|
|
HEdgePointer hopp = hp->HOp(); |
|
|
|
VertexPointer vp1; |
|
|
|
if( hp->HVp() == vp ) |
|
vp1 = hopp->HVp(); |
|
else |
|
vp1 = hp->HVp(); |
|
|
|
change_vertex( vp, vp1); |
|
|
|
//HP |
|
hp->HNp()->HPp() = hp->HPp(); |
|
hopp->HNp()->HPp() = hopp->HPp(); |
|
|
|
//HN |
|
hp->HPp()->HNp() = hp->HNp(); |
|
hopp->HPp()->HNp() = hopp->HNp(); |
|
|
|
//FH |
|
if( hp->HFp() ) |
|
if( hp->HFp()->FHp() == hp ) |
|
hp->HFp()->FHp() = hp->HNp(); |
|
|
|
if( hopp->HFp() ) |
|
if( hopp->HFp()->FHp() == hopp ) |
|
hopp->HFp()->FHp() = hopp->HNp(); |
|
|
|
// VH |
|
if( vp1->VHp() == hopp ) |
|
vp1->VHp() = hopp->HNp(); |
|
|
|
if(HasHEAdjacency(m)) |
|
vcg::tri::Allocator<MeshType>::DeleteEdge(m,*(hp->HEp())); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m,*hp); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge(m,*hopp); |
|
vcg::tri::Allocator<MeshType>::DeleteVertex(m,*vp); |
|
|
|
return vp1; |
|
|
|
} |
|
|
|
/*! |
|
* Adds a face in a mesh, checking if the operation is possible. |
|
* |
|
* \param m Mesh |
|
* \param vps Vector of vertices (in ccw order) that will belong to the new face |
|
* |
|
* \return Pointer to the new face if it has been inserted, NULL otherwise |
|
*/ |
|
static FacePointer add_face(MeshType &m, std::vector<VertexPointer> &vps) |
|
{ |
|
|
|
assert(MeshType::VertexType::HasVHAdjacency()); |
|
assert(MeshType::HEdgeType::HasHVAdjacency()); |
|
assert(MeshType::HEdgeType::HasHFAdjacency()); |
|
assert(MeshType::HEdgeType::HasHOppAdjacency()); |
|
assert(MeshType::HEdgeType::HasHPrevAdjacency()); |
|
|
|
unsigned int size = vps.size(); |
|
|
|
assert(size >= 3); //there must be at least 3 vertices |
|
|
|
for(unsigned int i = 0; i< size; i++) |
|
{ |
|
// all vertices must be different |
|
assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); |
|
} |
|
|
|
std::vector<HEdgePointer> hps; |
|
|
|
while(hps.size() < size) |
|
if( !can_add_hedge(vps, hps) ) |
|
return NULL; |
|
|
|
std::vector<bool> non_manifold_vertices(size, false); |
|
|
|
return add_face_unsafe( m,vps, hps, non_manifold_vertices); |
|
|
|
} |
|
|
|
/*! |
|
* Removes a face in a mesh, checking if the operation is possible |
|
* |
|
* \param m Mesh |
|
* \param fp face to be removed |
|
* |
|
* \retval true if face has been removed |
|
* \retval false otherwise |
|
*/ |
|
static bool remove_face(MeshType &m, FacePointer fp) |
|
{ |
|
|
|
assert(MeshType::VertexType::HasVHAdjacency()); |
|
assert(MeshType::FaceType::HasFHAdjacency()); |
|
assert(MeshType::HEdgeType::HasHVAdjacency()); |
|
assert(MeshType::HEdgeType::HasHFAdjacency()); |
|
assert(MeshType::HEdgeType::HasHOppAdjacency()); |
|
assert(MeshType::HEdgeType::HasHPrevAdjacency()); |
|
|
|
if( can_remove_face(fp) ) |
|
{ |
|
remove_face_unsafe(m, fp); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
protected: |
|
|
|
/*! |
|
* Adds a face in a mesh without any check |
|
* |
|
* \param m Mesh |
|
* \param vps Vector of vertices (in ccw order) that will belong to the new face |
|
* |
|
* \return Pointer to the new face |
|
*/ |
|
static FacePointer add_face_unsafe(MeshType &m, std::vector<VertexPointer> &vps) |
|
{ |
|
unsigned int size = vps.size(); |
|
|
|
std::vector<HEdgePointer> hps; |
|
std::vector<bool> non_manifold_vertices; |
|
|
|
while(hps.size() < size) |
|
{ |
|
if( can_add_hedge(vps, hps) ) |
|
non_manifold_vertices.push_back( false ); |
|
else |
|
non_manifold_vertices.push_back( hps.back() == NULL ); |
|
} |
|
|
|
return add_face_unsafe(m,vps,hps, non_manifold_vertices); |
|
} |
|
|
|
|
|
/*! |
|
* Adds a face in a mesh without any check |
|
* |
|
* \param m Mesh |
|
* \param vps Vector of vertices (in ccw order) that will belong to the new face |
|
* \param hps Vector of hedges (in ccw order) that will belong to the new face |
|
* \param non_manifold_vertices Vector of booleans denoting on the i-th position if the i-th vertex is non-manifold |
|
* |
|
* \return Pointer to the new face |
|
*/ |
|
static FacePointer add_face_unsafe(MeshType &m, std::vector<VertexPointer> &vps, std::vector<HEdgePointer> &hps, std::vector<bool> &non_manifold_vertices) |
|
{ |
|
|
|
assert(MeshType::VertexType::HasVHAdjacency()); |
|
assert(MeshType::HEdgeType::HasHVAdjacency()); |
|
assert(MeshType::HEdgeType::HasHFAdjacency()); |
|
assert(MeshType::HEdgeType::HasHOppAdjacency()); |
|
assert(MeshType::HEdgeType::HasHPrevAdjacency()); |
|
|
|
unsigned int size = vps.size(); |
|
|
|
assert(size >= 3); //there must be at least 3 vertices |
|
|
|
// for(unsigned int i = 0; i< size; i++) |
|
// { |
|
// // all vertices must be different |
|
// assert( count(vps.begin(), vps.end(), vps[i]) == 1 ); |
|
// } |
|
|
|
bool HasHE = MeshType::HEdgeType::HasHEAdjacency(); |
|
bool HasEH = MeshType::EdgeType::HasEHAdjacency(); |
|
|
|
HEdgeIterator hi; |
|
|
|
assert(hps.size() == size); |
|
|
|
HEdgePointer nullPointer = NULL; |
|
int edge_n = count(hps.begin(), hps.end(), nullPointer); |
|
|
|
FacePointer fp; |
|
|
|
FaceIterator fi = vcg::tri::Allocator<MeshType>::AddFaces(m,1); |
|
(*fi).Alloc( size ); |
|
fp = &(*fi); |
|
|
|
if(edge_n > 0) |
|
{ |
|
|
|
EdgeIterator ei; |
|
|
|
fp->SetD(); |
|
|
|
if(HasEH || HasHE) |
|
{ |
|
ei = vcg::tri::Allocator<MeshType>::AddEdges(m,edge_n); |
|
for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1) |
|
(*ei1).SetD(); |
|
} |
|
|
|
typename vcg::tri::Allocator<MeshType>::template PointerUpdater<HEdgePointer> pu; |
|
|
|
if(m.hedge.empty()) |
|
pu.oldBase = 0; |
|
else |
|
{ |
|
pu.oldBase = &*(m.hedge.begin()); |
|
pu.oldEnd = &m.hedge.back()+1; |
|
} |
|
|
|
hi = vcg::tri::Allocator<MeshType>::AddHEdges(m,2*edge_n); |
|
|
|
pu.newBase = &*(m.hedge.begin()); |
|
pu.newEnd = &m.hedge.back()+1; |
|
|
|
//undelete face |
|
fp->ClearD(); |
|
|
|
//undelete edges |
|
for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1) |
|
(*ei1).ClearD(); |
|
|
|
// update hedge pointers (if needed) |
|
if( pu.NeedUpdate() ) |
|
for(typename std::vector<HEdgePointer>::iterator hpsi = hps.begin(); hpsi != hps.end(); ++hpsi) |
|
{ |
|
if((*hpsi)) |
|
pu.Update(*hpsi); |
|
} |
|
|
|
|
|
HEdgeIterator hi1 = hi; |
|
HEdgeIterator hi2 = hi; |
|
|
|
++hi2; |
|
|
|
EdgeIterator ei1 = ei; |
|
|
|
for(; hi2 != m.hedge.end(); ++hi1, ++hi2) |
|
{ |
|
// EH |
|
if(HasEH) |
|
(*ei1).EHp() = &(*hi1); |
|
|
|
// HE |
|
if(HasHE) |
|
{ |
|
(*hi1).HEp() = &(*ei1); |
|
(*hi2).HEp() = &(*ei1); |
|
} |
|
|
|
//HO |
|
(*hi1).HOp() = &(*hi2); |
|
(*hi2).HOp() = &(*hi1); |
|
|
|
// HF |
|
(*hi1).HFp() = fp; |
|
|
|
++hi1; |
|
++hi2; |
|
} |
|
} |
|
|
|
std::vector<HEdgePointer> hps1; |
|
|
|
for(unsigned int i = 0; i < size; i++) |
|
{ |
|
if(hps[i] == NULL) |
|
{ |
|
hps1.push_back(&(*hi)); |
|
++hi; |
|
++hi; |
|
} |
|
else |
|
hps1.push_back(hps[i]); |
|
} |
|
|
|
assert( hps1.size() == size ); |
|
|
|
for(unsigned int i = 0; i < size; i++) |
|
{ |
|
|
|
int next = (i+1)%size; |
|
|
|
// hedge already exisitng |
|
if(hps[i]) |
|
{ |
|
hps1[i]->HFp() = fp; |
|
|
|
// next hedge was disconnected |
|
if(!hps[next]) |
|
{ |
|
|
|
hps1[next]->HOp()->HNp() = hps1[i]->HNp(); |
|
|
|
hps1[i]->HNp()->HPp() = hps1[next]->HOp(); |
|
|
|
hps1[i]->HNp() = hps1[next]; |
|
|
|
hps1[next]->HPp() = hps1[i]; |
|
} |
|
} |
|
|
|
// hedge wasn't existing, vertex was disconnected |
|
else |
|
{ |
|
//HV |
|
hps1[i]->HVp() = vps[i]; |
|
hps1[i]->HOp()->HVp() = vps[next]; |
|
|
|
|
|
hps1[i]->HNp() = hps1[next]; |
|
|
|
// next hedge was existing (vertex was disconnected) |
|
if(hps[next]) |
|
{ |
|
hps1[i]->HOp()->HPp() = hps1[next]->HPp(); |
|
hps1[next]->HPp()->HNp() = hps1[i]->HOp(); |
|
} |
|
|
|
//vertex was detached |
|
else |
|
{ |
|
// after face insertion vertex will become non-manifold |
|
if(non_manifold_vertices[next]) |
|
{ |
|
vcg::hedge::Pos<MeshType> p(vps[next]->VHp(), true); |
|
|
|
while(p.F()) |
|
{ |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
if(p.HE() == vps[next]->VHp()) |
|
assert(0); //can't add a connection, there is no space |
|
} |
|
|
|
|
|
p.HE()->HPp()->HNp() = hps1[i]->HOp(); |
|
hps1[i]->HOp()->HPp() = p.HE()->HPp(); |
|
|
|
p.HE()->HPp() = hps1[next]->HOp(); |
|
hps1[next]->HOp()->HNp() = p.HE(); |
|
|
|
} |
|
else |
|
{ |
|
hps1[i]->HOp()->HPp() = hps1[next]->HOp(); |
|
hps1[next]->HOp()->HNp() = hps1[i]->HOp(); |
|
} |
|
|
|
} |
|
|
|
|
|
hps1[next]->HPp() = hps1[i]; |
|
|
|
//VH |
|
if( !vps[i]->VHp()) |
|
vps[i]->VHp() = hps1[i]; |
|
} |
|
} |
|
|
|
//FH |
|
fp->FHp() = hps1.front(); |
|
|
|
return fp; |
|
|
|
} |
|
|
|
/*! |
|
* Removes a face in a mesh, without any check |
|
* |
|
* \param m Mesh |
|
* \param fp Face to be removed |
|
* |
|
*/ |
|
static void remove_face_unsafe (MeshType &m, FacePointer fp) |
|
{ |
|
|
|
std::vector<HEdgePointer> hps = getHEdges(fp); |
|
|
|
int size = hps.size(); |
|
|
|
for( int i = 0; i< size; i++ ) |
|
{ |
|
if( hps[i]->HOp()->HFp() ) |
|
{ |
|
hps[i]->HFp() = NULL; |
|
|
|
if( !hps[(i+size-1)%size]->HOp()->HFp() ) |
|
{ |
|
// HP |
|
hps[i]->HPp() = hps[(i+size-1)%size]->HOp()->HPp(); |
|
hps[(i+size-1)%size]->HOp()->HPp()->HNp() = hps[i]; |
|
} |
|
|
|
if( !hps[(i+1)%size]->HOp()->HFp() ) |
|
{ |
|
// HN |
|
hps[i]->HNp() = hps[(i+1)%size]->HOp()->HNp(); |
|
hps[(i+1)%size]->HOp()->HNp()->HPp() = hps[i]; |
|
} |
|
} |
|
else |
|
{ |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge( m, *hps[i] ); |
|
vcg::tri::Allocator<MeshType>::DeleteHEdge( m, *(hps[i]->HOp()) ); |
|
|
|
if(MeshType::HEdgeType::HasHEAdjacency()) |
|
vcg::tri::Allocator<MeshType>::DeleteEdge( m, *(hps[i]->HEp()) ); |
|
|
|
if( !hps[(i+size-1)%size]->HOp()->HFp() ) |
|
{ |
|
hps[i]->HOp()->HNp()->HPp() = hps[(i+size-1)%size]->HOp()->HPp(); |
|
hps[(i+size-1)%size]->HOp()->HPp()->HNp() = hps[i]->HOp()->HNp(); |
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
for( int i = 0; i< size; i++ ) |
|
{ |
|
if( hps[i]->HVp()->VHp()->IsD() ) |
|
{ |
|
if( !hps[i]->IsD() ) |
|
hps[i]->HVp()->VHp() = hps[i]; |
|
|
|
else if( !hps[(i+size-1)%size]->IsD() ) |
|
hps[i]->HVp()->VHp() = hps[(i+size-1)%size]->HOp(); |
|
|
|
else //search for a hedge (hedge can be found only if the vertex is non-manifold) |
|
{ |
|
bool manifold = true; |
|
|
|
vcg::hedge::Pos<MeshType> p(hps[i]->HVp()->VHp(), true); |
|
|
|
p.HE()->SetV(); |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
while( !p.HE()->IsV() ) |
|
{ |
|
if( !p.HE()->IsD() ) |
|
{ |
|
manifold = false; |
|
hps[i]->HVp()->VHp() = p.HE(); |
|
break; |
|
} |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
} |
|
|
|
p.HE()->ClearV(); |
|
|
|
if(manifold) |
|
hps[i]->HVp()->VHp() = NULL; |
|
|
|
} |
|
} |
|
|
|
} |
|
|
|
vcg::tri::Allocator<MeshType>::DeleteFace(m,*fp); |
|
|
|
} |
|
|
|
/*! |
|
* Checks if the next hedge can be inserted into hps. |
|
* If true, inserts the hedge into hps. If false, inserts NULL. |
|
* |
|
* \param vps Vector of vertices (in ccw order) that will belong to the new face |
|
* \param hps Vector of hedges already checked |
|
* |
|
* \retval true if hedge can be inserted |
|
* \retval false otherwise |
|
*/ |
|
static bool can_add_hedge( std::vector<VertexPointer> &vps, std::vector<HEdgePointer> &hps ) |
|
{ |
|
|
|
unsigned int i = hps.size(); |
|
|
|
assert( i < vps.size() ); |
|
|
|
HEdgePointer he = vps[i]->VHp(); |
|
|
|
if(!he) //vertex is detached |
|
{ |
|
hps.push_back(NULL); |
|
return true; |
|
} |
|
else |
|
{ |
|
bool disconnected = false; |
|
|
|
bool hasEdge = false; |
|
|
|
unsigned int size = vps.size(); |
|
|
|
vcg::hedge::Pos<MeshType> p(he, false); |
|
|
|
he->SetV(); |
|
|
|
while(p.V() != vps[(i+1)%size]) |
|
{ |
|
if(!hasEdge) |
|
hasEdge= ( find( vps.begin(), vps.end(), p.V()) != (vps.end() ) ); |
|
|
|
p.FlipV(); |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
p.FlipV(); |
|
|
|
if(p.HE()->IsV()) |
|
{ |
|
disconnected = true; |
|
break; |
|
} |
|
|
|
} |
|
|
|
he->ClearV(); |
|
|
|
if(disconnected) // edge does not exist |
|
{ |
|
hps.push_back(NULL); |
|
|
|
// if hasEdge is false after inserting the face there will be a non-manifold vertex |
|
return hasEdge; |
|
} |
|
|
|
else //edge already existing |
|
{ |
|
// try to insert consecutve hedges if they will belong to the new face |
|
while( (p.V() == vps[(i+1)%size]) && (i < size) ) |
|
{ |
|
hps.push_back( p.HE() ); |
|
|
|
if(p.HE()->HFp() != NULL) |
|
return false; |
|
|
|
i++; |
|
p.FlipE(); |
|
p.FlipV(); |
|
} |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
public: |
|
/*! |
|
* Checks if a face can be removed |
|
* |
|
* \param fp Face to check |
|
* |
|
* \retval true if the face can be removed |
|
* \retval false otherwise |
|
*/ |
|
static bool can_remove_face(FacePointer fp) |
|
{ |
|
|
|
assert(fp); |
|
assert(!fp->IsD()); |
|
|
|
vcg::hedge::Pos<MeshType> p(fp->FHp(), true); |
|
|
|
do |
|
{ |
|
std::vector<FacePointer> incident_faces = get_incident_faces( p.V() ); |
|
|
|
unsigned int size = incident_faces.size(); |
|
|
|
if(size > 2) |
|
{ |
|
for(unsigned int i = 0; i < size; i++) |
|
{ |
|
if(incident_faces[i] == NULL) |
|
if(incident_faces[(i+1)%size] != fp && incident_faces[((i+size)-1)%size] != fp ) |
|
return false; |
|
} |
|
} |
|
|
|
p.FlipV(); |
|
p.FlipE(); |
|
|
|
}while( p.HE() != fp->FHp() ); |
|
|
|
return true; |
|
} |
|
|
|
/*! |
|
* Checks if a diagonal can be collapsed |
|
* |
|
* \param hp Hedge whose vertex is one of the two vertices of the diagonal |
|
* |
|
* \retval true if diagonal can be collapsed |
|
* \retval false if diagonal cannot be collapsed |
|
*/ |
|
static bool check_diagonal_collapse_quad(HEdgePointer hp) |
|
{ |
|
|
|
assert(hp); |
|
assert(hp->HFp()); |
|
assert(hp->HFp()->VN() == 4); |
|
assert(!hp->IsD()); |
|
|
|
std::vector<FacePointer> faces; |
|
|
|
HEdgePointer hopp = hp->HNp()->HNp(); |
|
std::vector<FacePointer> faces1 = get_incident_faces(hp->HVp(), hp); |
|
std::vector<FacePointer> faces2 = get_incident_faces(hp->HNp()->HNp()->HVp(), hopp); |
|
|
|
faces.assign(faces1.begin()+1, faces1.end()); |
|
faces.assign(faces2.begin()+1, faces2.end()); |
|
|
|
|
|
// First check: |
|
|
|
unsigned int size = faces.size(); |
|
bool null_face = false; |
|
|
|
|
|
// if size <=2 check is ok |
|
if(size > 2) |
|
{ |
|
for(unsigned int i = 0; i < size; i++) |
|
{ |
|
if(faces[i] == NULL) |
|
{ |
|
if(faces[(i+1)%size] != NULL && faces[((i+size)-1)%size] != NULL ) |
|
{ |
|
if(null_face) |
|
return false; |
|
|
|
null_face=true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// End of first check |
|
|
|
|
|
// Second check |
|
|
|
std::set<VertexPointer> set1; |
|
std::set<VertexPointer> set2; |
|
|
|
std::vector<VertexPointer> vect1 = getVertices(hp->HVp()); |
|
std::vector<VertexPointer> vect2 = getVertices(hp->HNp()->HNp()->HVp()); |
|
|
|
set1.insert(vect1.begin(), vect1.end()); |
|
set2.insert(vect2.begin(), vect2.end()); |
|
|
|
size = vect1.size(); |
|
if(vect2.size() < size) |
|
size = vect2.size(); |
|
|
|
std::vector<VertexPointer> intersection(size); |
|
|
|
typename std::vector<VertexPointer>::iterator it; |
|
it = set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), intersection.begin()); |
|
|
|
size = it- intersection.begin(); |
|
|
|
assert( size >= 2 ); |
|
|
|
return (size==2); |
|
|
|
// End of second check |
|
|
|
} |
|
|
|
/*! |
|
* Checks if a vertex is non-manifold, comparing local and global information (slow) |
|
* |
|
* \param m Mesh |
|
* \param vp Vertex to check |
|
* |
|
* \retval true if vertex is non-manifold |
|
* \retval false if verex is manifold |
|
*/ |
|
static bool is_nonManifold_vertex(MeshType &m, VertexPointer vp) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
std::set<HEdgePointer> set1; |
|
for(HEdgeIterator hi = m.hedge.begin(); hi != m.hedge.end(); ++hi) |
|
{ |
|
if(!(*hi).IsD() && (*hi).HVp() == vp) |
|
set1.insert(&(*hi)); |
|
} |
|
|
|
|
|
std::vector<HEdgePointer> vect2 = get_incident_hedges(vp); |
|
|
|
std::set<HEdgePointer> set2; |
|
set2.insert(vect2.begin(), vect2.end()); |
|
|
|
return !equal(set1.begin(), set1.end(), set2.begin()); |
|
|
|
} |
|
|
|
/*! |
|
* Checks if a vertex is non-manifold, based only on local informations |
|
* |
|
* \param vp Vertex to check |
|
* |
|
* \retval true if vertex is non-manifold |
|
* \retval false if verex is manifold |
|
*/ |
|
static bool is_nonManifold_vertex(VertexPointer vp) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
std::vector<FacePointer> faces = get_incident_faces(vp); |
|
|
|
unsigned int size = faces.size(); |
|
int null_count = 0; |
|
|
|
if(size > 2) |
|
{ |
|
for(unsigned int i = 0; i < size; i++) |
|
{ |
|
if(faces[i] == NULL) |
|
{ |
|
if(null_count > 0) |
|
return true; |
|
else |
|
null_count++; |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
/*! |
|
* Shortcut to get the second vertex of an edge |
|
* |
|
* \param hp Hedge |
|
* |
|
* \return Opposite vertex |
|
*/ |
|
static VertexPointer opp_vert(HEdgePointer hp) |
|
{ |
|
return hp->HOp()->HVp(); |
|
} |
|
|
|
/*! |
|
* Gets vertices on the 1-ring of a vertex |
|
* |
|
* \param vp Vertex. It must be a non-border vertex. |
|
* |
|
* \return Vector containing vertices |
|
*/ |
|
static std::vector<VertexPointer> getVertices(VertexPointer vp) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
HEdgePointer hp = vp->VHp(); |
|
|
|
std::vector<VertexPointer> ret; |
|
|
|
if( !hp ) |
|
return ret; |
|
|
|
vcg::hedge::Pos<MeshType> p(hp); |
|
|
|
do |
|
{ |
|
if(p.F()) |
|
{ |
|
assert(!p.F()->IsD()); |
|
|
|
ret.push_back( opp_vert( p.HE() ) ); |
|
|
|
ret.push_back( opp_vert( p.HE()->HNp() ) ); |
|
|
|
|
|
} |
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
}while( p.HE() != hp); |
|
|
|
return ret; |
|
} |
|
|
|
|
|
/*! |
|
* Gets faces on the 1-ring of a vertex |
|
* |
|
* \param vp Vertex |
|
* |
|
* \return Set containing faces |
|
*/ |
|
static std::set<FacePointer> getFaces(VertexPointer vp) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
std::set<FacePointer> ret; |
|
|
|
std::vector<VertexPointer> vertices = getVertices(vp); |
|
|
|
for(typename std::vector<VertexPointer>::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi) |
|
{ |
|
std::vector<FacePointer> incident_faces = get_incident_faces(*vi); |
|
ret.insert(incident_faces.begin(), incident_faces.end()); |
|
} |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
/*! |
|
* Checks if a face is a singlet |
|
* |
|
* \param fp Face to check |
|
* |
|
* \retval true if face is a singlet |
|
* \retval false if face isn't a singlet |
|
*/ |
|
static bool is_singlet_quad(FacePointer fp) |
|
{ |
|
assert(fp); |
|
assert(fp->FHp()); |
|
assert(!fp->IsD()); |
|
|
|
vcg::hedge::Pos<MeshType> p( fp->FHp() ); |
|
|
|
do |
|
{ |
|
if( vertex_valence(p.V()) == 1 ) |
|
return true; |
|
|
|
p.FlipV(); |
|
p.FlipE(); |
|
|
|
}while(p.HE() != fp->FHp()); |
|
|
|
return false; |
|
|
|
} |
|
|
|
/*! |
|
* Gets all vertices incident to a face |
|
* |
|
* \param fp Face |
|
* \param starting_he A hedge in the face from which to start |
|
* |
|
* \return Vector containing the incident vertices |
|
*/ |
|
static std::vector<VertexPointer> getVertices(FacePointer fp, HEdgePointer starting_he = NULL) |
|
{ |
|
assert(fp); |
|
assert(!fp->IsD()); |
|
|
|
if(!starting_he) |
|
starting_he = fp->FHp(); |
|
|
|
assert( starting_he->HFp() == fp ); |
|
|
|
vcg::hedge::Pos<MeshType> p( starting_he, true ); |
|
|
|
std::vector<VertexPointer> ret; |
|
|
|
|
|
do |
|
{ |
|
assert(!(p.V()->IsD())); |
|
|
|
ret.push_back( p.V() ); |
|
|
|
p.FlipV(); |
|
p.FlipE(); |
|
|
|
assert(ret.size() <= (unsigned int)(fp->VN())); |
|
|
|
}while(p.HE() != starting_he); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
protected: |
|
/*! |
|
* Gets all hedges incident to a face |
|
* |
|
* \param fp Face |
|
* \param starting_he A hedge in the face from which to start |
|
* |
|
* \return Vector containing the incident hedges |
|
*/ |
|
static std::vector<HEdgePointer> getHEdges(FacePointer fp, HEdgePointer starting_he = NULL) |
|
{ |
|
assert(fp); |
|
assert(!fp->IsD()); |
|
|
|
if(starting_he) |
|
assert( starting_he->HFp() == fp ); |
|
else |
|
starting_he = fp->FHp(); |
|
|
|
vcg::hedge::Pos<MeshType> p( starting_he, true ); |
|
|
|
std::vector<HEdgePointer> ret; |
|
|
|
do |
|
{ |
|
ret.push_back( p.HE() ); |
|
|
|
p.FlipV(); |
|
p.FlipE(); |
|
|
|
assert(ret.size() <= (unsigned int) (fp->VN())); |
|
|
|
}while(p.HE() != starting_he); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
public: |
|
|
|
/*! |
|
* Gets all faces incident to a vertex |
|
* |
|
* \param vp Vertex |
|
* \param starting_he A hedge from which to start |
|
* |
|
* \return Vector containing the incident faces |
|
*/ |
|
static std::vector<FacePointer> get_incident_faces(VertexPointer vp, HEdgePointer starting_he = NULL) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
if(starting_he) |
|
assert( starting_he->HVp() == vp ); |
|
else |
|
starting_he = vp->VHp(); |
|
|
|
std::vector<FacePointer> ret; |
|
|
|
if(!starting_he) |
|
return ret; |
|
|
|
vcg::hedge::Pos<MeshType> p( starting_he, true ); |
|
|
|
do |
|
{ |
|
ret.push_back( p.F() ); |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
}while(p.HE() != starting_he); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
static std::vector<FacePointer> get_adjacent_faces(FacePointer fp) |
|
{ |
|
assert(fp); |
|
assert(!fp->IsD()); |
|
|
|
std::vector<FacePointer> ret; |
|
|
|
vcg::hedge::Pos<MeshType> p( fp->FHp() ); |
|
assert(p.F() == fp); |
|
|
|
do |
|
{ |
|
p.FlipF(); |
|
ret.push_back( p.F() ); |
|
p.FlipF(); |
|
|
|
p.FlipV(); |
|
p.FlipE(); |
|
|
|
} while(p.HE() != fp->FHp()); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
/*! |
|
* Gets all hedges incident to a vertex |
|
* |
|
* \param vp Vertex |
|
* \param starting_he A hedge from which to start navigation |
|
* |
|
* \return Vector containing the incident hedges |
|
*/ |
|
static std::vector<HEdgePointer> get_incident_hedges(VertexPointer vp, HEdgePointer starting_he = NULL) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
if(starting_he) |
|
assert( starting_he->HVp() == vp ); |
|
else |
|
starting_he = vp->VHp(); |
|
|
|
std::vector<HEdgePointer> ret; |
|
|
|
if(!starting_he) |
|
return ret; |
|
|
|
vcg::hedge::Pos<MeshType> p( starting_he, true ); |
|
|
|
do |
|
{ |
|
assert(!p.HE()->IsD()); |
|
|
|
ret.push_back( p.HE() ); |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
|
|
}while(p.HE() != starting_he); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
/*! |
|
* Checks if a face has doublets |
|
* |
|
* \param fp Face to check |
|
* |
|
* \retval true if face has at least a doublet |
|
* \retval false if face hasn't any doublet |
|
*/ |
|
static bool has_doublet_quad(FacePointer fp) |
|
{ |
|
return ( !find_doublet_hedges_quad(fp).empty() ); |
|
} |
|
|
|
/*! |
|
* Gets all hedges whose vertex is into a doublet |
|
* |
|
* \param fp Face to check |
|
* |
|
* \return Vector containing the hedges |
|
*/ |
|
static std::vector<HEdgePointer> find_doublet_hedges_quad(FacePointer fp) |
|
{ |
|
assert(fp); |
|
assert(fp->FHp()); |
|
assert(!fp->IsD()); |
|
|
|
std::vector<HEdgePointer> ret; |
|
|
|
vcg::hedge::Pos<MeshType> p( fp->FHp(), true ); |
|
|
|
do |
|
{ |
|
|
|
if(vertex_valence(p.V()) == 2 && !isBorderVertex(p.V())) |
|
ret.push_back(p.HE()); |
|
|
|
assert(ret.size() <= 4); |
|
|
|
p.FlipV(); |
|
p.FlipE(); |
|
|
|
}while(p.HE() != fp->FHp()); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
/*! |
|
* Checks if a vertex is a border vertex |
|
* |
|
* \param vp Vertex to check |
|
* |
|
* \retval true if vertex is a border vertex |
|
* \retval false if vertex isn't a border vertex |
|
*/ |
|
static bool isBorderVertex(VertexPointer vp) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
if( !(vp->VHp()) ) |
|
return true; |
|
|
|
vcg::hedge::Pos<MeshType> p( vp->VHp() ); |
|
|
|
do |
|
{ |
|
if(!p.F()) |
|
return true; |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
}while(p.HE() != vp->VHp()); |
|
|
|
return false; |
|
} |
|
|
|
/*! |
|
* Computes valence of a vertex |
|
* |
|
* \param vp Vertex |
|
* |
|
* \return Vertex valence |
|
*/ |
|
static int vertex_valence(VertexPointer vp) |
|
{ |
|
assert(vp); |
|
assert(!vp->IsD()); |
|
|
|
if( !(vp->VHp()) ) |
|
return 0; |
|
|
|
int ret = 0; |
|
|
|
vcg::hedge::Pos<MeshType> p( vp->VHp() ); |
|
|
|
do |
|
{ |
|
assert(!p.HE()->IsD()); |
|
ret++; |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
}while(p.HE() != vp->VHp()); |
|
|
|
return ret; |
|
} |
|
|
|
/*! |
|
* Connects to a new vertex all hedges incident to a vertex |
|
* |
|
* \param old_vp the old vertex to be disconnected |
|
* \param new_vp the new vertex to be connected |
|
* |
|
*/ |
|
protected: |
|
static void change_vertex(VertexPointer old_vp, VertexPointer new_vp) |
|
{ |
|
assert(old_vp); |
|
assert(new_vp); |
|
assert(old_vp != new_vp); |
|
assert(!old_vp->IsD()); |
|
|
|
vcg::hedge::Pos<MeshType> p(old_vp->VHp(),true); |
|
|
|
p.HE()->SetV(); |
|
|
|
do |
|
{ |
|
p.HE()->HVp() = new_vp; |
|
|
|
p.FlipE(); |
|
p.FlipF(); |
|
|
|
}while( !p.HE()->IsV() ); |
|
|
|
p.HE()->ClearV(); |
|
|
|
if( !new_vp->VHp() ) |
|
new_vp->VHp() = old_vp->VHp(); |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
} |
|
|
|
#endif // VCG_HEDGE_TOPOLOGY |
|
|
|
|