/*************************************************************************** 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 CModifierQuadTriangle : public CGAL::Modifier_base { 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 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 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 builder(&OriginalMesh); NewMesh.delegate(builder); builder.smooth(&NewMesh,smooth_boundary); } }; #endif // QUAD_TRIANGLE