cgal/Tutorial/tutorial/Polyhedron/Qt/Subdivision/tool/quad-triangle.h

417 lines
12 KiB
C++

/***************************************************************************
quad-triangle.h - quad/triangle subdivision
----------------------------------------------------------------------------
begin : march 2003
copyright : (C) 2003 by Pierre Alliez - INRIA
email : pierre.alliez@sophia.inria.fr
***************************************************************************/
#ifndef QUAD_TRIANGLE
#define QUAD_TRIANGLE
#include "enriched_polyhedron.h"
#include "builder.h"
// Implemented from:
// Stam and Loop. Quad/Triangle Subdivision.
// Computer Graphics Forum.
// Volume 22, Issue 1, March 2003.
// Another reference is:
// Levin. Polynomial Generation and Quasi-Interpolation
// in Stationary Non-Uniform Subdivision.
// Computer Aided Geometric Design.
// pages 41-60, volume 20(1), 2003.
template <class HDS,class Polyhedron,class kernel>
class CModifierQuadTriangle : public CGAL::Modifier_base<HDS>
{
private:
typedef typename HDS::Vertex Vertex;
typedef typename Vertex::Point Point;
typedef typename HDS::Face_handle Face_handle;
typedef typename HDS::Halfedge_handle Halfedge_handle;
typedef typename CGAL::Enriched_polyhedron_incremental_builder_3<HDS> builder;
Polyhedron *m_pMesh;
public:
// life cycle
CModifierQuadTriangle(Polyhedron *pMesh)
{
CGAL_assertion(pMesh != NULL);
m_pMesh = pMesh;
}
~CModifierQuadTriangle() {}
// subdivision
void operator()( HDS& hds)
{
builder B(hds,true);
B.begin_surface(3,1,6);
add_vertices(B);
add_facets(B);
B.end_surface();
}
// add vertices
void add_vertices(builder &B)
{
// put original vertices
int index = 0;
Polyhedron::Vertex_iterator pVertex;
for(pVertex = m_pMesh->vertices_begin();
pVertex != m_pMesh->vertices_end();
pVertex++)
{
pVertex->tag(index);
B.add_vertex(pVertex->point());
index++;
}
// as many as #edges
m_pMesh->tag_halfedges(-1);
Polyhedron::Halfedge_iterator pHalfedge;
for(pHalfedge = m_pMesh->halfedges_begin();
pHalfedge != m_pMesh->halfedges_end();
pHalfedge++)
{
if(pHalfedge->tag() != -1)
continue;
// simple edge bisection
const Point& p1 = pHalfedge->vertex()->point();
const Point& p2 = pHalfedge->opposite()->vertex()->point();
Point point = Point(0.5f*(p1.x()+p2.x()),
0.5f*(p1.y()+p2.y()),
0.5f*(p1.z()+p2.z()));
B.add_vertex(point);
// put vertex indices on both halfedges
pHalfedge->tag(index);
pHalfedge->opposite()->tag(index);
index++;
}
// and as many as #facets with degree > 3
m_pMesh->tag_facets(-1);
Polyhedron::Facet_iterator pFacet;
for(pFacet = m_pMesh->facets_begin();
pFacet != m_pMesh->facets_end();
pFacet++)
{
unsigned int degree = Polyhedron::degree(pFacet);
CGAL_assertion(degree >= 3);
// forget about triangles, they are
// simply 1-to-4 subdivided by edge bisection
if(degree == 3)
continue;
// barycentric subdivision
Point barycenter;
m_pMesh->compute_facet_center(pFacet,barycenter);
B.add_vertex(barycenter);
pFacet->tag(index);
index++;
}
}
// add facets
void add_facets(builder &B)
{
Polyhedron::Facet_iterator pFacet;
for(pFacet = m_pMesh->facets_begin();
pFacet != m_pMesh->facets_end();
pFacet++)
{
unsigned int degree = Polyhedron::degree(pFacet);
CGAL_assertion(degree >= 3);
if(degree == 3)
{
Polyhedron::Halfedge_handle pHalfedge = pFacet->halfedge();
int i0 = pHalfedge->tag();
int i1 = pHalfedge->vertex()->tag();
int i2 = pHalfedge->next()->tag();
int i3 = pHalfedge->next()->vertex()->tag();
int i4 = pHalfedge->next()->next()->tag();
int i5 = pHalfedge->next()->next()->vertex()->tag();
CGAL_assertion(i0 >= 0);
CGAL_assertion(i1 >= 0);
CGAL_assertion(i2 >= 0);
CGAL_assertion(i3 >= 0);
CGAL_assertion(i4 >= 0);
CGAL_assertion(i5 >= 0);
// create 4 triangles
B.begin_facet();
B.add_vertex_to_facet(i1);
B.add_vertex_to_facet(i2);
B.add_vertex_to_facet(i0);
const Halfedge_handle& h1 = B.end_facet();
B.begin_facet();
B.add_vertex_to_facet(i3);
B.add_vertex_to_facet(i4);
B.add_vertex_to_facet(i2);
const Halfedge_handle& h2 = B.end_facet();
B.begin_facet();
B.add_vertex_to_facet(i5);
B.add_vertex_to_facet(i0);
B.add_vertex_to_facet(i4);
const Halfedge_handle& h3 = B.end_facet();
// center face
B.begin_facet();
B.add_vertex_to_facet(i0);
B.add_vertex_to_facet(i2);
B.add_vertex_to_facet(i4);
Halfedge_handle h4 = B.end_facet();
h1->control_edge(false);
h1->next()->control_edge(false);
h1->next()->next()->control_edge(false);
h2->control_edge(false);
h2->next()->control_edge(false);
h2->next()->next()->control_edge(false);
h3->control_edge(false);
h3->next()->control_edge(false);
h3->next()->next()->control_edge(false);
h4->control_edge(false);
h4->next()->control_edge(false);
h4->next()->next()->control_edge(false);
if(pHalfedge->control_edge())
{
h1->control_edge(true);
h3->next()->control_edge(true);
}
if(pHalfedge->next()->control_edge())
{
h1->next()->control_edge(true);
h2->control_edge(true);
}
if(pHalfedge->next()->next()->control_edge())
{
h2->next()->control_edge(true);
h3->control_edge(true);
}
}
else
{
// i1: index of barycenter vertex
int i1 = pFacet->tag();
CGAL_assertion(i1 >= 0);
// for each halfedge
Polyhedron::Halfedge_around_facet_circulator h;
h = pFacet->facet_begin();
do
{
// i2,3,4: indices of three consecutive
// vertices on halfedges
int i2 = h->tag();
int i3 = h->vertex()->tag();
int i4 = h->next()->tag();
CGAL_assertion(i2 >= 0);
CGAL_assertion(i3 >= 0);
CGAL_assertion(i4 >= 0);
// create a quad
B.begin_facet();
B.add_vertex_to_facet(i3);
B.add_vertex_to_facet(i4);
B.add_vertex_to_facet(i1);
B.add_vertex_to_facet(i2);
const Halfedge_handle& pNewHalfedge = B.end_facet();
pNewHalfedge->control_edge(false);
pNewHalfedge->next()->control_edge(false);
pNewHalfedge->next()->next()->control_edge(false);
pNewHalfedge->next()->next()->next()->control_edge(false);
if(h->control_edge())
pNewHalfedge->control_edge(true);
if(h->next()->control_edge())
pNewHalfedge->next()->control_edge(true);
}
while(++h != pFacet->facet_begin());
}
}
}
// correcting factor
static float correcting_factor(unsigned int ne,
unsigned int nq)
{
if(ne == 2 && nq == 1)
return -0.20505f;
if(ne == 3 && nq == 1)
return 0.80597f;
if(ne == 3 && nq == 2)
return 0.61539f;
if(ne == 4 && nq == 1)
return 0.34792f;
if(ne == 4 && nq == 2)
return 0.21380f;
if(ne == 4 && nq == 3)
return 0.10550f;
return 0.0f;
}
// smooth vertex positions
static void smooth(Polyhedron *pMesh,
bool smooth_boundary = true)
{
CGAL_assertion(pMesh != NULL);
// alloc position vectors
unsigned int nb_vertices = pMesh->size_of_vertices();
kernel::FT *pPos = new kernel::FT[3*nb_vertices];
CGAL_assertion(pPos != NULL);
// compute new positions
unsigned int index = 0;
Polyhedron::Vertex_iterator pVertex;
for(pVertex = pMesh->vertices_begin();
pVertex != pMesh->vertices_end();
pVertex++)
{
// border vertices will not move
if(Polyhedron::is_border(pVertex))
{
// do not smooth it
const Point& curr = pVertex->point();
if(!smooth_boundary)
{
pPos[3*index] = curr.x();
pPos[3*index+1] = curr.y();
pPos[3*index+2] = curr.z();
}
// smooth using [1/4 1/2 1/4] cubic B-spline averaging mask
else
{
const Polyhedron::Halfedge_handle& pHalfedge =
pMesh->get_border_halfedge(pVertex);
CGAL_assertion(pHalfedge != NULL);
const Point& next = pHalfedge->next()->vertex()->point();
const Point& prev = pHalfedge->prev()->vertex()->point();
pPos[3*index] = 0.25f*prev.x() + 0.5f*curr.x() + 0.25f*next.x();
pPos[3*index+1] = 0.25f*prev.y() + 0.5f*curr.y() + 0.25f*next.y();
pPos[3*index+2] = 0.25f*prev.z() + 0.5f*curr.z() + 0.25f*next.z();
}
} // end is border
else
{
unsigned int nb_quads = 0;
unsigned int nb_edges = 0;
// rotate around vertex to count #edges and #quads
Polyhedron::Halfedge_around_vertex_circulator
pHalfEdge = pVertex->vertex_begin();
Polyhedron::Halfedge_around_vertex_circulator end = pHalfEdge;
CGAL_For_all(pHalfEdge,end)
{
const Polyhedron::Facet_handle& pFacet = pHalfEdge->facet();
CGAL_assertion(pFacet != NULL);
unsigned int degree = Polyhedron::degree(pFacet);
CGAL_assertion(degree == 4 || degree == 3);
if(degree == 4)
nb_quads++;
nb_edges++;
}
// compute coefficients
kernel::FT ne = (kernel::FT)nb_edges;
kernel::FT nq = (kernel::FT)nb_quads;
kernel::FT alpha = 1.0f / (1.0f + ne/2.0f + nq/4.0f);
kernel::FT beta = alpha / 2.0f; // edges
kernel::FT gamma = alpha / 4.0f; // corners of incident quads
kernel::FT eta = correcting_factor(nb_edges,nb_quads);
// new position
pPos[3*index] = alpha * pVertex->point().x();
pPos[3*index+1] = alpha * pVertex->point().y();
pPos[3*index+2] = alpha * pVertex->point().z();
// rotate around vertex to compute new position
pHalfEdge = pVertex->vertex_begin();
end = pHalfEdge;
CGAL_For_all(pHalfEdge,end)
{
const Polyhedron::Facet_handle& pFacet = pHalfEdge->facet();
CGAL_assertion(pFacet != NULL);
unsigned int degree = Polyhedron::degree(pFacet);
CGAL_assertion(degree == 4 || degree == 3);
// add edge-vertex contribution
const Point& point = pHalfEdge->prev()->vertex()->point();
pPos[3*index] += beta * point.x();
pPos[3*index+1] += beta * point.y();
pPos[3*index+2] += beta * point.z();
// add corner vertex contribution
if(degree == 4)
{
const Point& corner = pHalfEdge->next()->next()->vertex()->point();
pPos[3*index] += gamma * corner.x();
pPos[3*index+1] += gamma * corner.y();
pPos[3*index+2] += gamma * corner.z();
}
}
// apply correction
pPos[3*index] = pPos[3*index] +
eta*(pPos[3*index]-pVertex->point().x());
pPos[3*index+1] = pPos[3*index+1] +
eta*(pPos[3*index+1]-pVertex->point().y());
pPos[3*index+2] = pPos[3*index+2] +
eta*(pPos[3*index+2]-pVertex->point().z());
} // end !is border
index++;
}
// set new positions
index = 0;
for(pVertex = pMesh->vertices_begin();
pVertex != pMesh->vertices_end();
pVertex++)
{
Point& point = pVertex->point();
point = Point(pPos[3*index],
pPos[3*index+1],
pPos[3*index+2]);
index++;
}
// cleanup
delete [] pPos;
}
};
template <class Polyhedron,class kernel>
class CSubdivider_quad_triangle
{
public:
typedef typename Polyhedron::HalfedgeDS HalfedgeDS;
CSubdivider_quad_triangle() {}
~CSubdivider_quad_triangle() {}
public:
void subdivide(Polyhedron &OriginalMesh,
Polyhedron &NewMesh,
const bool smooth_boundary = true)
{
// subdivide, then smooth
CModifierQuadTriangle<HalfedgeDS,Polyhedron,kernel> builder(&OriginalMesh);
NewMesh.delegate(builder);
builder.smooth(&NewMesh,smooth_boundary);
}
};
#endif // QUAD_TRIANGLE