1782 lines
60 KiB
C++
1782 lines
60 KiB
C++
/****************************************************************************
|
|
* 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>
|
|
|
|
using namespace std;
|
|
using namespace vcg::hedge;
|
|
using namespace vcg::tri;
|
|
|
|
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;
|
|
|
|
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();
|
|
|
|
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)
|
|
Allocator<MeshType>::DeleteEdge(m, *(hps[i]->HEp()) );
|
|
|
|
Allocator<MeshType>::DeleteHEdge(m, *(hps[i]->HOp()) );
|
|
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;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
Allocator<MeshType>::DeleteFace(m, *(fp) );
|
|
Allocator<MeshType>::DeleteVertex(m, *(opposite_vertex) );
|
|
if(HasHE)
|
|
{
|
|
Allocator<MeshType>::DeleteEdge(m, *(hps[1]->HEp()) );
|
|
Allocator<MeshType>::DeleteEdge(m, *(hps[3]->HEp()) );
|
|
}
|
|
Allocator<MeshType>::DeleteHEdge(m, *(hps[0]) );
|
|
Allocator<MeshType>::DeleteHEdge(m, *(hps[1]) );
|
|
Allocator<MeshType>::DeleteHEdge(m, *(hps[2]) );
|
|
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();
|
|
|
|
|
|
Allocator<MeshType>::DeleteVertex(m, *vp);
|
|
if(MeshType::HEdgeType::HasHEAdjacency())
|
|
{
|
|
Allocator<MeshType>::DeleteEdge(m, *(hp->HEp()) );
|
|
Allocator<MeshType>::DeleteEdge(m, *(hp->HPp()->HEp()) );
|
|
}
|
|
Allocator<MeshType>::DeleteHEdge(m, *hp );
|
|
Allocator<MeshType>::DeleteHEdge(m, *(hp->HOp()) );
|
|
Allocator<MeshType>::DeleteHEdge(m, *(hp->HPp()) );
|
|
Allocator<MeshType>::DeleteHEdge(m, *(hp->HPp()->HOp()) );
|
|
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();
|
|
|
|
vector<HEdgePointer> ext_hedges;
|
|
|
|
vector<HEdgePointer> int_hedges = getHEdges(fp);
|
|
|
|
Allocator<MeshType>::DeleteFace( m, *(fp) );
|
|
|
|
for(typename 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)
|
|
{
|
|
Allocator<MeshType>::DeleteVertex( m, *((*hi)->HVp()) );
|
|
if(HasHE)
|
|
Allocator<MeshType>::DeleteEdge( m, *((*hi)->HEp()) );
|
|
}
|
|
|
|
}
|
|
|
|
for(typename vector<HEdgePointer>::iterator hi = int_hedges.begin(); hi != int_hedges.end();++hi)
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
Allocator<MeshType>::DeleteEdge( m, *( ext_hedges[0]->HEp()) );
|
|
Allocator<MeshType>::DeleteEdge( m, *( ext_hedges[1]->HEp()) );
|
|
}
|
|
Allocator<MeshType>::DeleteHEdge( m, *( ext_hedges[0]) );
|
|
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));
|
|
|
|
vector<FacePointer> fps;
|
|
typedef vector<HEdgePointer> hedge_vect;
|
|
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() );
|
|
|
|
Pos<MeshType> p(vp->VHp(), true);
|
|
|
|
HEdgePointer hep = p.HE();
|
|
|
|
typedef vector<HEdgePointer> hedge_vect;
|
|
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))
|
|
Allocator<MeshType>::DeleteEdge(m,*(hp->HEp()));
|
|
Allocator<MeshType>::DeleteHEdge(m,*hp);
|
|
Allocator<MeshType>::DeleteHEdge(m,*hopp);
|
|
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, 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 );
|
|
}
|
|
|
|
vector<HEdgePointer> hps;
|
|
|
|
while(hps.size() < size)
|
|
if( !can_add_hedge(vps, hps) )
|
|
return NULL;
|
|
|
|
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, vector<VertexPointer> &vps)
|
|
{
|
|
unsigned int size = vps.size();
|
|
|
|
vector<HEdgePointer> hps;
|
|
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, vector<VertexPointer> &vps, vector<HEdgePointer> &hps, 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 = Allocator<MeshType>::AddFaces(m,1);
|
|
(*fi).Alloc( size );
|
|
fp = &(*fi);
|
|
|
|
if(edge_n > 0)
|
|
{
|
|
|
|
EdgeIterator ei;
|
|
|
|
fp->SetD();
|
|
|
|
if(HasEH || HasHE)
|
|
{
|
|
ei = Allocator<MeshType>::AddEdges(m,edge_n);
|
|
for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1)
|
|
(*ei1).SetD();
|
|
}
|
|
|
|
typename 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 = 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 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;
|
|
}
|
|
}
|
|
|
|
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])
|
|
{
|
|
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)
|
|
{
|
|
|
|
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
|
|
{
|
|
Allocator<MeshType>::DeleteHEdge( m, *hps[i] );
|
|
Allocator<MeshType>::DeleteHEdge( m, *(hps[i]->HOp()) );
|
|
|
|
if(MeshType::HEdgeType::HasHEAdjacency())
|
|
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;
|
|
|
|
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;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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( vector<VertexPointer> &vps, 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();
|
|
|
|
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());
|
|
|
|
Pos<MeshType> p(fp->FHp(), true);
|
|
|
|
do
|
|
{
|
|
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());
|
|
|
|
vector<FacePointer> faces;
|
|
|
|
HEdgePointer hopp = hp->HNp()->HNp();
|
|
vector<FacePointer> faces1 = get_incident_faces(hp->HVp(), hp);
|
|
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
|
|
|
|
set<VertexPointer> set1;
|
|
set<VertexPointer> set2;
|
|
|
|
vector<VertexPointer> vect1 = getVertices(hp->HVp());
|
|
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();
|
|
|
|
vector<VertexPointer> intersection(size);
|
|
|
|
typename 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());
|
|
|
|
set<HEdgePointer> set1;
|
|
for(HEdgeIterator hi = m.hedge.begin(); hi != m.hedge.end(); ++hi)
|
|
{
|
|
if(!(*hi).IsD() && (*hi).HVp() == vp)
|
|
set1.insert(&(*hi));
|
|
}
|
|
|
|
|
|
vector<HEdgePointer> vect2 = get_incident_hedges(vp);
|
|
|
|
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());
|
|
|
|
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 vector<VertexPointer> getVertices(VertexPointer vp)
|
|
{
|
|
assert(vp);
|
|
assert(!vp->IsD());
|
|
|
|
HEdgePointer hp = vp->VHp();
|
|
|
|
vector<VertexPointer> ret;
|
|
|
|
if( !hp )
|
|
return ret;
|
|
|
|
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 set<FacePointer> getFaces(VertexPointer vp)
|
|
{
|
|
assert(vp);
|
|
assert(!vp->IsD());
|
|
|
|
set<FacePointer> ret;
|
|
|
|
vector<VertexPointer> vertices = getVertices(vp);
|
|
|
|
for(typename vector<VertexPointer>::iterator vi = vertices.begin(); vi!= vertices.end(); ++vi)
|
|
{
|
|
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());
|
|
|
|
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 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 );
|
|
|
|
Pos<MeshType> p( starting_he, true );
|
|
|
|
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 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();
|
|
|
|
Pos<MeshType> p( starting_he, true );
|
|
|
|
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 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();
|
|
|
|
vector<FacePointer> ret;
|
|
|
|
if(!starting_he)
|
|
return ret;
|
|
|
|
Pos<MeshType> p( starting_he, true );
|
|
|
|
do
|
|
{
|
|
ret.push_back( p.F() );
|
|
|
|
p.FlipE();
|
|
p.FlipF();
|
|
|
|
}while(p.HE() != starting_he);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
static vector<FacePointer> get_adjacent_faces(FacePointer fp)
|
|
{
|
|
assert(fp);
|
|
assert(!fp->IsD());
|
|
|
|
vector<FacePointer> ret;
|
|
|
|
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 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();
|
|
|
|
vector<HEdgePointer> ret;
|
|
|
|
if(!starting_he)
|
|
return ret;
|
|
|
|
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 vector<HEdgePointer> find_doublet_hedges_quad(FacePointer fp)
|
|
{
|
|
assert(fp);
|
|
assert(fp->FHp());
|
|
assert(!fp->IsD());
|
|
|
|
vector<HEdgePointer> ret;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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());
|
|
|
|
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
|
|
|