1310 lines
46 KiB
C++
1310 lines
46 KiB
C++
#ifndef VCG_HEDGE_TOPOLOGY
|
|
#define VCG_HEDGE_TOPOLOGY
|
|
|
|
#include <vcg/connectors/halfedge_pos.h>
|
|
#include <vcg/complex/trimesh/allocate.h>
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
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 ep Edge to be collapsed
|
|
* \param vp Vertex that will be rotated
|
|
*
|
|
* \return Pointer to the new vertex
|
|
*/
|
|
static VertexPointer edge_collapse_quad(MeshType &m, EdgePointer ep, VertexPointer vp)
|
|
{
|
|
assert(vp);
|
|
assert(ep);
|
|
assert(MeshType::EdgeType::HasEHAdjacency());
|
|
assert(MeshType::HEdgeType::HasHEAdjacency());
|
|
assert(MeshType::HEdgeType::HasHVAdjacency());
|
|
assert(ep->EHp()->HVp() == vp || ep->EHp()->HOp()->HVp() == vp);
|
|
assert(ep->EHp()->HFp()->VN() == 4);
|
|
assert(ep->EHp()->HOp()->HFp()->VN() == 4);
|
|
|
|
VertexPointer vp_opp = ep->EHp()->HOp()->HVp();
|
|
|
|
VertexPointer vp_rot = vertex_rotate( m, vp );
|
|
|
|
assert(vp_rot == vp);
|
|
|
|
FacePointer fp;
|
|
|
|
//retrieve right face
|
|
Pos<MeshType> p(vp->VHp(),true);
|
|
|
|
while(p.HE()->HNp->HOp()->HVp() != vp_opp)
|
|
{
|
|
p.FlipE();
|
|
p.FlipF();
|
|
}
|
|
|
|
fp = p.F();
|
|
|
|
|
|
return diagonal_collapse( 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(MeshType &m, FacePointer fp, VertexPointer vp)
|
|
{
|
|
|
|
assert(MeshType::VertexType::HasVHAdjacency());
|
|
assert(MeshType::EdgeType::HasEHAdjacency());
|
|
assert(MeshType::FaceType::HasFHAdjacency());
|
|
assert(MeshType::HEdgeType::HasHVAdjacency());
|
|
assert(MeshType::HEdgeType::HasHEAdjacency());
|
|
assert(MeshType::HEdgeType::HasHFAdjacency());
|
|
assert(MeshType::HEdgeType::HasHOppAdjacency());
|
|
assert(MeshType::HEdgeType::HasHPrevAdjacency());
|
|
|
|
assert(fp);
|
|
assert(fp->FHp());
|
|
assert(fp->VN() == 4);
|
|
|
|
if( !can_remove_face(fp) )
|
|
return NULL;
|
|
|
|
HEdgePointer hp;
|
|
|
|
vector<VertexPointer> vps = getVertices(fp);
|
|
VertexPointer opp_vert = NULL;
|
|
|
|
for(unsigned int i = 0; i< vps.size(); i++)
|
|
if(vps[i] == vp)
|
|
opp_vert = vps[(i+2)%vps.size()];
|
|
|
|
assert(opp_vert);
|
|
|
|
if( fp->FHp()->HVp() == vp || fp->FHp()->HVp() == opp_vert)
|
|
hp = fp->FHp();
|
|
else
|
|
hp = fp->FHp()->HNp();
|
|
|
|
vector<HEdgePointer> hps = getHEdges(fp,hp);
|
|
|
|
int edge_cnt = 0;
|
|
|
|
if(hps[0]->HOp()->HFp() || hps[1]->HOp()->HFp())
|
|
edge_cnt++;
|
|
if(hps[2]->HOp()->HFp() || hps[3]->HOp()->HFp())
|
|
edge_cnt++;
|
|
|
|
VertexIterator vi;
|
|
|
|
if(edge_cnt > 0)
|
|
{
|
|
typename Allocator<MeshType>::template PointerUpdater<VertexPointer> puv;
|
|
|
|
if(m.vert.empty())
|
|
puv.oldBase = 0;
|
|
else
|
|
{
|
|
puv.oldBase = &*(m.vert.begin());
|
|
puv.oldEnd = &m.vert.back()+1;
|
|
}
|
|
|
|
vi = Allocator<MeshType>::AddVertices(m,1);
|
|
|
|
EdgeIterator ei = Allocator<MeshType>::AddEdges(m,edge_cnt);
|
|
|
|
puv.newBase = &*(m.vert.begin());
|
|
puv.newEnd = &m.vert.back()+1;
|
|
|
|
if( puv.NeedUpdate() )
|
|
{
|
|
puv.Update(vp);
|
|
puv.Update(opp_vert);
|
|
for(typename vector<VertexPointer>::iterator vpi = vps.begin(); vpi != vps.end(); ++vpi)
|
|
puv.Update(*vpi);
|
|
}
|
|
|
|
typename Allocator<MeshType>::template PointerUpdater<HEdgePointer> puh;
|
|
|
|
if(m.hedge.empty())
|
|
puh.oldBase = 0;
|
|
else
|
|
{
|
|
puh.oldBase = &*(m.hedge.begin());
|
|
puh.oldEnd = &m.hedge.back()+1;
|
|
}
|
|
|
|
HEdgeIterator hi = Allocator<MeshType>::AddHEdges(m,2*edge_cnt);
|
|
|
|
puh.newBase = &*(m.hedge.begin());
|
|
puh.newEnd = &m.hedge.back()+1;
|
|
|
|
if( puh.NeedUpdate() )
|
|
for(typename vector<HEdgePointer>::iterator hpi = hps.begin(); hpi != hps.end(); ++hpi)
|
|
puh.Update(*hpi);
|
|
|
|
|
|
|
|
HEdgeIterator hi1 = hi;
|
|
HEdgeIterator hi2 = hi;
|
|
++hi2;
|
|
|
|
(*vi).VHp() = &(*hi1);
|
|
|
|
change_vertex( hps[0]->HVp(), &(*vi));
|
|
change_vertex( hps[2]->HVp(), &(*vi));
|
|
|
|
for( int count = 0; count < 2; count++ )
|
|
{
|
|
|
|
int i = 2*count;
|
|
|
|
FacePointer fp1 = hps[i+1]->HOp()->HFp();
|
|
FacePointer fp2 = hps[i]->HOp()->HFp();
|
|
|
|
if( fp1 || fp2 )
|
|
{
|
|
|
|
// HOp
|
|
(*hi1).HOp() = &(*hi2);
|
|
(*hi2).HOp() = &(*hi1);
|
|
|
|
// EH
|
|
(*ei).EHp() = &(*hi1);
|
|
|
|
// HE
|
|
(*hi1).HEp() = &(*ei);
|
|
(*hi2).HEp() = &(*ei);
|
|
|
|
// HV
|
|
(*hi1).HVp() = &(*vi);
|
|
(*hi2).HVp() = hps[i+1]->HVp();
|
|
|
|
// FH
|
|
if( fp1 && ( fp1->FHp() == hps[i+1]->HOp() ) )
|
|
fp1->FHp() = &(*hi1);
|
|
|
|
if( fp2 && ( fp2->FHp() == hps[i]->HOp() ) )
|
|
fp2->FHp() = &(*hi2);
|
|
|
|
//HF
|
|
(*hi1).HFp() = fp1;
|
|
(*hi2).HFp() = fp2;
|
|
|
|
//HNp
|
|
(*hi1).HNp() = hps[i+1]->HOp()->HNp();
|
|
(*hi2).HNp() = hps[i]->HOp()->HNp();
|
|
|
|
(*hi1).HNp()->HPp() = &(*hi1);
|
|
(*hi2).HNp()->HPp() = &(*hi2);
|
|
|
|
//HPp
|
|
(*hi1).HPp() = hps[i+1]->HOp()->HPp();
|
|
(*hi2).HPp() = hps[i]->HOp()->HPp();
|
|
|
|
(*hi1).HPp()->HNp() = &(*hi1);
|
|
(*hi2).HPp()->HNp() = &(*hi2);
|
|
|
|
//VH
|
|
VertexPointer tmp = hps[i+1]->HVp();
|
|
if( tmp->VHp() == hps[i+1] || tmp->VHp() == hps[i]->HOp() )
|
|
tmp->VHp() = &(*hi2);
|
|
|
|
++ei;
|
|
|
|
++hi1;
|
|
++hi1;
|
|
|
|
++hi2;
|
|
++hi2;
|
|
|
|
}
|
|
else
|
|
{
|
|
hps[i+1]->HOp()->HPp()->HNp() = hps[i]->HOp()->HNp();
|
|
hps[i]->HOp()->HNp()->HPp() = hps[i+1]->HOp()->HPp();
|
|
|
|
hps[i+1]->HVp()->VHp() = NULL;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Allocator<MeshType>::DeleteFace(m, *(fp) );
|
|
Allocator<MeshType>::DeleteVertex(m, *(vp) );
|
|
Allocator<MeshType>::DeleteVertex(m, *(opp_vert) );
|
|
|
|
for(typename vector<HEdgePointer>::iterator hpi = hps.begin(); hpi != hps.end(); ++hpi)
|
|
{
|
|
if(! (*hpi)->HEp()->IsD() )
|
|
Allocator<MeshType>::DeleteEdge(m, *((*hpi)->HEp()) );
|
|
|
|
if(! (*hpi)->IsD())
|
|
{
|
|
Allocator<MeshType>::DeleteHEdge(m, *(*hpi) );
|
|
Allocator<MeshType>::DeleteHEdge(m, *((*hpi)->HOp()) );
|
|
}
|
|
}
|
|
|
|
if(edge_cnt > 0)
|
|
return &(*vi);
|
|
|
|
for(typename vector<VertexPointer>::iterator vpi = vps.begin(); vpi != vps.end(); ++vpi)
|
|
if(!(*vpi)->IsD())
|
|
Allocator<MeshType>::DeleteVertex(m, *(*vpi) );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*!
|
|
* 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(MeshType &m, VertexPointer vp)
|
|
{
|
|
assert(vp);
|
|
|
|
HEdgePointer hp = vp->VHp();
|
|
|
|
assert(hp);
|
|
|
|
FacePointer fp1 = hp->HFp();
|
|
FacePointer fp2 = hp->HOp()->HFp();
|
|
|
|
// check if face is a doublet
|
|
|
|
assert( fp1 );
|
|
assert( fp1 == hp->HPp()->HFp() );
|
|
|
|
assert( fp2 );
|
|
assert( fp2 == hp->HOp()->HNp()->HFp() );
|
|
|
|
assert( hp->HOp()->HNp()->HOp() == hp->HPp() );
|
|
|
|
assert( fp1->VN() == 4);
|
|
assert( fp2->VN() == 4);
|
|
|
|
// end of check
|
|
|
|
vector<VertexPointer> vert_face1 = getVertices(fp1, hp);
|
|
vector<VertexPointer> vert_face2 = getVertices(fp2, hp->HOp()->HNp());
|
|
|
|
remove_face_unsafe(m, fp1 );
|
|
remove_face_unsafe(m, fp2 );
|
|
|
|
Allocator<MeshType>::DeleteVertex(m, *vp);
|
|
|
|
|
|
vector<VertexPointer> new_face_vert;
|
|
|
|
new_face_vert.push_back( vert_face1[1] );
|
|
new_face_vert.push_back( vert_face1[2] );
|
|
new_face_vert.push_back( vert_face1[3] );
|
|
new_face_vert.push_back( vert_face2[2] );
|
|
|
|
return add_face_unsafe(m, new_face_vert );
|
|
|
|
}
|
|
|
|
/*!
|
|
* Removes a singlet replacing it with an edge
|
|
*
|
|
* \param m Mesh
|
|
* \param vp Vertex shared by the two consecutive edges inside the singlet
|
|
*
|
|
* \return Pointer to the new edge
|
|
*/
|
|
static EdgePointer singlet_remove(MeshType &m, VertexPointer vp)
|
|
{
|
|
assert( vp );
|
|
|
|
HEdgePointer hp = vp->VHp();
|
|
assert( hp );
|
|
|
|
FacePointer fp1 = hp->HFp();
|
|
FacePointer fp2 = hp->HOp()->HFp();
|
|
|
|
assert( fp1 && fp2 && fp1 == fp2 ); // the faces pointed by the halfedges must be the same
|
|
|
|
HEdgePointer hp1 = hp->HNp()->HOp();
|
|
HEdgePointer hp2 = hp->HNp()->HNp()->HOp();
|
|
|
|
// pointers to the near faces
|
|
FacePointer fp3 = hp1->HFp();
|
|
FacePointer fp4 = hp2->HFp();
|
|
|
|
Allocator<MeshType>::DeleteFace( m, *fp1 );
|
|
|
|
Allocator<MeshType>::DeleteEdge( m, *(hp->HEp()) );
|
|
|
|
Allocator<MeshType>::DeleteHEdge( m, *hp );
|
|
Allocator<MeshType>::DeleteHEdge( m, *(hp->HOp()) );
|
|
|
|
Allocator<MeshType>::DeleteVertex(m, *(vp) );
|
|
|
|
Allocator<MeshType>::DeleteEdge(m, *(hp1->HEp()) );
|
|
Allocator<MeshType>::DeleteEdge(m, *(hp2->HEp()) );
|
|
|
|
Allocator<MeshType>::DeleteHEdge( m, *(hp1->HOp()) );
|
|
Allocator<MeshType>::DeleteHEdge( m, *(hp2->HOp()) );
|
|
|
|
if(!fp3 && !fp4) // there are no faces, nothing has to be created
|
|
{
|
|
Allocator<MeshType>::DeleteHEdge( m, *hp1 );
|
|
Allocator<MeshType>::DeleteHEdge( m, *hp2 );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EdgeIterator ei = Allocator<MeshType>::AddEdges(m,1);
|
|
|
|
(*ei).EHp() = hp1;
|
|
|
|
hp1->HEp() = &(*ei);
|
|
hp2->HEp() = &(*ei);
|
|
|
|
hp1->HOp() = hp2;
|
|
hp2->HOp() = hp1;
|
|
|
|
return &(*ei);
|
|
|
|
}
|
|
|
|
/*!
|
|
* Rotates a non-border edge shared by two quads
|
|
*
|
|
* \param m Mesh
|
|
* \param ep Edge to be rotated
|
|
* \param cw flag denoting a clockwise or counter-clockwise rotation
|
|
*
|
|
* \return Pointer to the rotated edge
|
|
*/
|
|
static EdgePointer edge_rotate(MeshType &m, EdgePointer ep, bool cw)
|
|
{
|
|
assert( MeshType::EdgeType::HasEHAdjacency() );
|
|
assert( MeshType::HEdgeType::HasHFAdjacency() );
|
|
assert( MeshType::HEdgeType::HasHOppAdjacency() );
|
|
assert( MeshType::FaceType::HasFHAdjacency() );
|
|
|
|
assert( ep->EHp()->HFp() );
|
|
|
|
assert( ep->EHp()->HFp()->VN() == 4 );
|
|
|
|
assert( ep->EHp()->HOp()->HFp() );
|
|
assert( ep->EHp()->HOp()->HFp()->VN() == 4 );
|
|
|
|
FacePointer fp1 = ep->EHp()->HFp();
|
|
FacePointer fp2 = ep->EHp()->HOp()->HFp();
|
|
|
|
vector<VertexPointer> old_face1 = getVertices( fp1, ep->EHp() );
|
|
vector<VertexPointer> old_face2 = getVertices( fp2, ep->EHp()->HOp() );
|
|
|
|
remove_face_unsafe(m, fp1);
|
|
remove_face_unsafe(m, fp2);
|
|
|
|
vector<VertexPointer> new_face1;
|
|
vector<VertexPointer> new_face2;
|
|
|
|
if(cw)
|
|
{
|
|
new_face1.push_back( old_face1[3] );
|
|
new_face1.push_back( old_face2[3] );
|
|
new_face1.push_back( old_face1[1] );
|
|
new_face1.push_back( old_face1[2] );
|
|
|
|
new_face2.push_back( old_face2[3] );
|
|
new_face2.push_back( old_face1[3] );
|
|
new_face2.push_back( old_face2[1] );
|
|
new_face2.push_back( old_face2[2] );
|
|
|
|
}
|
|
else
|
|
{
|
|
new_face1.push_back( old_face1[2] );
|
|
new_face1.push_back( old_face1[3] );
|
|
new_face1.push_back( old_face1[0] );
|
|
new_face1.push_back( old_face2[2] );
|
|
|
|
new_face2.push_back( old_face2[2] );
|
|
new_face2.push_back( old_face2[3] );
|
|
new_face2.push_back( old_face2[0] );
|
|
new_face2.push_back( old_face1[2] );
|
|
}
|
|
|
|
fp1 = add_face_unsafe(m, new_face1);
|
|
fp2 = add_face_unsafe(m, new_face2);
|
|
|
|
|
|
// retrieve inserted edge
|
|
Pos<MeshType> p1( fp1->FHp(), true);
|
|
|
|
while( p1.HE()->HOp()->HFp() != fp2 )
|
|
{
|
|
p1.FlipV();
|
|
p1.FlipE();
|
|
}
|
|
|
|
return p1.E();
|
|
|
|
}
|
|
|
|
/*!
|
|
* Rotates a non-border vertex shared by only quads
|
|
*
|
|
* \param m Mesh
|
|
* \param vp Vertex to be rotated
|
|
*
|
|
* \return Pointer to the rotated vertex
|
|
*/
|
|
static VertexPointer vertex_rotate(MeshType &m, VertexPointer vp)
|
|
{
|
|
|
|
assert(MeshType::VertexType::HasVHAdjacency());
|
|
|
|
vector<FacePointer> old_faces;
|
|
|
|
typedef vector<VertexPointer> vert_vect;
|
|
vector< vert_vect > old_face_verts;
|
|
|
|
assert( vp->VHp() );
|
|
|
|
Pos<MeshType> p(vp->VHp(), true);
|
|
|
|
HEdgePointer hep = p.HE();
|
|
|
|
do
|
|
{
|
|
assert( p.F() );
|
|
assert( p.F()->VN() == 4);
|
|
|
|
old_faces.push_back(p.F());
|
|
|
|
old_face_verts.push_back( getVertices( p.F(), p.HE() ) );
|
|
|
|
p.FlipE();
|
|
p.FlipF();
|
|
|
|
}while(p.HE() != hep);
|
|
|
|
assert( old_faces.size() == old_face_verts.size() );
|
|
|
|
int size = old_faces.size();
|
|
|
|
vector<vert_vect> new_face_verts;
|
|
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
new_face_verts.push_back(vector<VertexPointer>());
|
|
|
|
new_face_verts.back().push_back( old_face_verts[i][0] );
|
|
new_face_verts.back().push_back( old_face_verts[i][2] );
|
|
new_face_verts.back().push_back( old_face_verts[i][3] );
|
|
new_face_verts.back().push_back( old_face_verts[(i+1)%size][2] );
|
|
|
|
}
|
|
|
|
for(typename vector<FacePointer>::iterator fi = old_faces.begin(); fi != old_faces.end(); ++fi)
|
|
remove_face_unsafe( m, *fi );
|
|
|
|
for(typename vector<vert_vect>::iterator vi = new_face_verts.begin(); vi != new_face_verts.end(); ++vi)
|
|
add_face_unsafe(m, *vi);
|
|
|
|
return vp;
|
|
|
|
}
|
|
|
|
/*!
|
|
* Collapses a generic edge
|
|
*
|
|
* \param m Mesh
|
|
* \param ep 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, EdgePointer ep, VertexPointer vp)
|
|
{
|
|
|
|
assert(MeshType::EdgeType::HasEHAdjacency());
|
|
assert(MeshType::VertexType::HasVHAdjacency());
|
|
assert(MeshType::HEdgeType::HasHOppAdjacency());
|
|
assert(MeshType::HEdgeType::HasHVAdjacency());
|
|
assert(MeshType::HEdgeType::HasHPrevAdjacency());
|
|
|
|
if( ep->EHp()->HFp() )
|
|
assert(ep->EHp()->HFp()->VN() > 3);
|
|
|
|
if( ep->EHp()->HOp()->HFp())
|
|
assert(ep->EHp()->HOp()->HFp()->VN() > 3);
|
|
|
|
assert(ep->EHp()->HFp() || ep->EHp()->HOp()->HFp());
|
|
assert(ep->EHp()->HVp() == vp || ep->EHp()->HOp()->HVp() == vp);
|
|
|
|
|
|
HEdgePointer he = ep->EHp();
|
|
HEdgePointer hopp = he->HOp();
|
|
|
|
VertexPointer vp1;
|
|
|
|
if( he->HVp() == vp )
|
|
vp1 = hopp->HVp();
|
|
else
|
|
vp1 = he->HVp();
|
|
|
|
change_vertex( vp, vp1);
|
|
|
|
//HP
|
|
he->HNp()->HPp() = he->HPp();
|
|
hopp->HNp()->HPp() = hopp->HPp();
|
|
|
|
//HN
|
|
he->HPp()->HNp() = he->HNp();
|
|
hopp->HPp()->HNp() = hopp->HNp();
|
|
|
|
//FH
|
|
if( he->HFp() )
|
|
if( he->HFp()->FHp() == he )
|
|
he->HFp()->FHp() = he->HNp();
|
|
|
|
if( hopp->HFp() )
|
|
if( hopp->HFp()->FHp() == hopp )
|
|
hopp->HFp()->FHp() = hopp->HNp();
|
|
|
|
// VH
|
|
if( vp1->VHp() == hopp )
|
|
vp1->VHp() = hopp->HNp();
|
|
|
|
Allocator<MeshType>::DeleteEdge(m,*ep);
|
|
Allocator<MeshType>::DeleteHEdge(m,*he);
|
|
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::EdgeType::HasEHAdjacency());
|
|
assert(MeshType::HEdgeType::HasHVAdjacency());
|
|
assert(MeshType::HEdgeType::HasHEAdjacency());
|
|
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::EdgeType::HasEHAdjacency());
|
|
assert(MeshType::FaceType::HasFHAdjacency());
|
|
assert(MeshType::HEdgeType::HasHVAdjacency());
|
|
assert(MeshType::HEdgeType::HasHEAdjacency());
|
|
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 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::EdgeType::HasEHAdjacency());
|
|
assert(MeshType::HEdgeType::HasHVAdjacency());
|
|
assert(MeshType::HEdgeType::HasHEAdjacency());
|
|
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 );
|
|
// }
|
|
|
|
HEdgeIterator hi;
|
|
|
|
assert(hps.size() == size);
|
|
|
|
HEdgePointer nullPointer = NULL;
|
|
int edge_n = count(hps.begin(), hps.end(), nullPointer);
|
|
|
|
FaceIterator fi = Allocator<MeshType>::AddFaces(m,1);
|
|
m.face.back().Alloc( size );
|
|
|
|
if(edge_n > 0)
|
|
{
|
|
(*fi).SetD();
|
|
|
|
EdgeIterator 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
|
|
(*fi).ClearD();
|
|
|
|
//undelete edges
|
|
for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1)
|
|
(*ei1).ClearD();
|
|
|
|
|
|
HEdgeIterator hi1 = hi;
|
|
HEdgeIterator hi2 = hi;
|
|
|
|
++hi2;
|
|
|
|
|
|
for(EdgeIterator ei1 = ei; ei1 != m.edge.end(); ++ei1, ++hi1, ++hi2)
|
|
{
|
|
// EH
|
|
(*ei1).EHp() = &(*hi1);
|
|
|
|
// HE
|
|
(*hi1).HEp() = &(*ei1);
|
|
(*hi2).HEp() = &(*ei1);
|
|
|
|
//HO
|
|
(*hi1).HOp() = &(*hi2);
|
|
(*hi2).HOp() = &(*hi1);
|
|
|
|
// HF
|
|
(*hi1).HFp() = &(*fi);
|
|
|
|
++hi1;
|
|
++hi2;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
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() = &(*fi);
|
|
|
|
// 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
|
|
(*fi).FHp() = hps1.front();
|
|
|
|
return &(*fi);
|
|
|
|
}
|
|
|
|
/*!
|
|
* 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()) );
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* 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)
|
|
{
|
|
|
|
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;
|
|
}
|
|
|
|
/*!
|
|
* 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)
|
|
{
|
|
if(starting_he)
|
|
assert( starting_he->HFp() == fp );
|
|
else
|
|
starting_he = fp->FHp();
|
|
|
|
Pos<MeshType> p( starting_he, true );
|
|
|
|
vector<VertexPointer> ret;
|
|
|
|
if( fp )
|
|
{
|
|
do
|
|
{
|
|
ret.push_back( p.V() );
|
|
|
|
p.FlipV();
|
|
p.FlipE();
|
|
|
|
}while(p.HE() != starting_he);
|
|
}
|
|
return ret;
|
|
|
|
}
|
|
|
|
/*!
|
|
* Gets all edges incident to a face
|
|
*
|
|
* \param fp Face
|
|
* \param starting_he A hedge in the face from which to start
|
|
*
|
|
* \return Vector containing the incident edges
|
|
*/
|
|
static vector<HEdgePointer> getHEdges(FacePointer fp, HEdgePointer starting_he = NULL)
|
|
{
|
|
if(starting_he)
|
|
assert( starting_he->HFp() == fp );
|
|
else
|
|
starting_he = fp->FHp();
|
|
|
|
Pos<MeshType> p( starting_he, true );
|
|
|
|
vector<HEdgePointer> ret;
|
|
|
|
if( fp )
|
|
{
|
|
do
|
|
{
|
|
ret.push_back( p.HE() );
|
|
|
|
p.FlipV();
|
|
p.FlipE();
|
|
|
|
}while(p.HE() != starting_he);
|
|
}
|
|
return ret;
|
|
|
|
}
|
|
|
|
/*!
|
|
* Gets all faces incident to a vertex
|
|
*
|
|
* \param fp 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);
|
|
|
|
if(starting_he)
|
|
assert( starting_he->HVp() == vp );
|
|
else
|
|
starting_he = vp->VHp();
|
|
|
|
Pos<MeshType> p( starting_he, true );
|
|
|
|
vector<FacePointer> ret;
|
|
|
|
do
|
|
{
|
|
ret.push_back( p.F() );
|
|
|
|
p.FlipE();
|
|
p.FlipF();
|
|
|
|
}while(p.HE() != starting_he);
|
|
|
|
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
|
|
*
|
|
*/
|
|
static void change_vertex(VertexPointer old_vp, VertexPointer new_vp)
|
|
{
|
|
assert(old_vp);
|
|
assert(new_vp);
|
|
assert(old_vp != new_vp);
|
|
|
|
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
|
|
|