mirror of https://github.com/CGAL/cgal
671 lines
29 KiB
TeX
671 lines
29 KiB
TeX
Based on the techniques and functionalities described in the previous
|
|
sections, we now show how to design and implement a subdivision
|
|
library for a generic CGAL polyhedron. This library is named
|
|
\emph{C}ombinatorial \emph{S}ubdivision
|
|
\emph{L}ibrary, short CSL. CSL contains a set of refinement
|
|
functions and geometry smoothing rules that are user-customizable.
|
|
Subdivisions in CSL are specialized as a proper combination of the
|
|
refinement functions and the geometry smoothing rules.
|
|
%The goal of CSL are the
|
|
%\emph{generic polyhedron data},
|
|
%\emph{user-friendly functional forms},
|
|
%\emph{user-customizable subdivisions}, and a
|
|
%\emph{extendible library}.
|
|
|
|
CSL follows in its desing the ideas of policy-based design
|
|
\cite{Alexandrescu:2001:MCD}. The policy-based design assembles a class
|
|
(called \emph{host}) with complex behavior out of many small and
|
|
generic behaviors (called \emph{policies}). Each policy defines an
|
|
\emph{interface} for a specific behavior and is customizable by the
|
|
user. Policies are usually implemented as functions or functors.
|
|
One gentle example is the \CodeFmt{for\_each} algorithm in STL
|
|
\footnote{\path|http://www.sgi.com/tech/stl/for_each.html|}.
|
|
\begin{lstlisting}
|
|
template <class InputIterator, class UnaryFunction>
|
|
UnaryFunction for_each(InputIterator first, InputIterator last, UnaryFunction f);
|
|
\end{lstlisting}
|
|
The \CodeFmt{for\_each} is the algorithm host and the
|
|
\CodeFmt{UnaryFunction f} is the generic behavior customizable
|
|
by the user. To use it, one has to provide a policy functor
|
|
or function that meets the interface requirement of an unary function.
|
|
|
|
Based on the policy-based design, CSL
|
|
is designed to support both \emph{generic types}, i.e.\ the polyhedron,
|
|
and \emph{generic behaviors}, i.e.\ the subdivisions.
|
|
%This means users can customize the subdivisions at compile time.
|
|
The generic type is specified to follow the interface of the \cgalpoly \ that
|
|
specifies both the connectivity and the geometry interface.
|
|
The connectivity interface has to support the circulators over primitives, or
|
|
the adjacency pointers of an halfedge. The geometry interface
|
|
has to provide the \CodeFmt{Point} type of a vertex item. The operational
|
|
interface of the \CodeFmt{Point} is not specified by CSL and can
|
|
be non-CGAL style. For a non-CGAL \CodeFmt{Point} type, users should
|
|
provide user-defined policies that perform the point operations.
|
|
|
|
A subdivision algorithm has three key behaviors: \emph{refinement},
|
|
\emph{smoothing}, and \emph{stencil correspondence}. The refinement is
|
|
acted as a \CodeFmt{for\_each} algorithm on the source \emph{and}
|
|
the refined polyhedron while applying the smoothing behaviors. CSL
|
|
implement the refinements as the host functions with the smoothing
|
|
rules as the policies. Some major refinement schemes are shown in
|
|
\figurename\ \ref{fig:RefSchemes}. The tutorial accompanying CSL only
|
|
provides PQQ, PTQ and DQQ schemes. The refinement configurations
|
|
also define the stencil correspondences; stencils of PQQ and DQQ
|
|
schemes are shown in \figurename\ \ref{fig:RefMap}.
|
|
These stencil correspondences
|
|
specified the functional interface between the refinement hosts
|
|
and the geometry smoothing policies.
|
|
|
|
%% \cgalpoly\ provides two
|
|
%% mechanisms to implement refinements as introduced in the previous
|
|
%% sections. The geometry smoothing is mixed with the refinement
|
|
%% in the previous examples and hence implicitly maintain
|
|
%% the stencil correspondence. CSL extracts the geometry smoothing
|
|
%% from the refinement as policies and maintain the stencil
|
|
%% correspondence explicitly.
|
|
|
|
|
|
%-----------------------------------------------------------------------
|
|
\subsubsection*{Primal Quad Quadralization}
|
|
|
|
\begin{figure}[htb]
|
|
\centering{\includegraphics[width=12.0cm]{figs/TriCube_CC.eps}}
|
|
\caption{Catmull-Clark subdivision of the box polyhedron.}
|
|
\label{fig:cc}
|
|
\end{figure}
|
|
|
|
A subdivision algorithm in CSL is constructed as a
|
|
\emph{refinement function} parameterized with a set of the
|
|
\emph{geometry smoothing rules}. The rule set is specified as
|
|
a template policy class. For example, Catmull-Clark
|
|
subdivision in CSL is instantiated as the PQQ scheme
|
|
parameterized with a Catmull-Clark geometry policy class.
|
|
\begin{lstlisting}
|
|
void CatmullClark_subdivision(Polyhedron& p, int step = 1) {
|
|
quad_quadralize_polyhedron(p, CatmullClark_rule<Polyhedron>(), step);
|
|
}
|
|
\end{lstlisting}
|
|
The \CodeFmt{quad\_quadralize\_polyhedron} is the refinement host
|
|
that refines the control polyhedron using PQQ scheme and the
|
|
\CodeFmt{CatmullClark\_rule} is the template geometry policy class.
|
|
\\
|
|
%The refinement host applies the smoothing stencils provided by the
|
|
%geometry policy class.
|
|
|
|
\noindent \textbf{Geometry policies} are represented as
|
|
the policy functions of the policy class. Each policy function
|
|
receive a \emph{primitive handle} of the represented 1-ring submesh
|
|
of the control polyhedron; and a reference of the smoothing point
|
|
on the refined polyhedron. The interface of a policy class for a PQQ
|
|
refinement host is shown below.
|
|
\begin{lstlisting}
|
|
template <class _Poly>
|
|
class quadralize_rule {
|
|
public:
|
|
void face_point_rule(Facet_handle, Point&) {};
|
|
void edge_point_rule(Halfedge_handle, Point&) {};
|
|
void vertex_point_rule(Vertex_handle, Point&) {};
|
|
};
|
|
\end{lstlisting}
|
|
The interface is defined according to the stencil correspondence of
|
|
the refinement scheme. A PQQ scheme contains three stencils that are
|
|
shown in \figurename\ \ref{fig:RefMap} (a--c). Each of them
|
|
defines a policy function, which of the \CodeFmt{quadralize\_rule}
|
|
is the \CodeFmt{facet\_rule()},
|
|
the \CodeFmt{edge\_rule()}, and the \CodeFmt{vertex\_rule()}
|
|
respectively. Any customized policy class of the geometry
|
|
smoothing rules are required to provide the proper functions.
|
|
To assure the interface consistence, CSL provides
|
|
a geometry rule class for each refinement scheme. To create a new
|
|
geometry policy class, the class inheritance is used.
|
|
\begin{lstlisting}
|
|
// Specialized a Catmull-Clark rule by inheriting the quadralize_rule.
|
|
template <class _Poly>
|
|
class CatmullClark_rule : public quadralize_rule<_Poly> {...}
|
|
\end{lstlisting}
|
|
|
|
The smoothing points of a refined polyhedron is generated by calling
|
|
the corresponding geometry policies. Inside each policy, applying the
|
|
stencil is simplified into the mesh traversal of a 1-ring
|
|
neighborhood. It can be done with a primitive circulator or a simple
|
|
sequence of the adjacency pointers of the halfedges. The
|
|
\CodeFmt{face\_point\_rule} for Catmull-Clark subdivision demonstrates
|
|
the usage of a facet circulator for stenciling.
|
|
\begin{lstlisting}
|
|
void face_point_rule(Facet_handle facet, Point& pt) {
|
|
// Facet circulator is used to traverse the 1-ring of a facet.
|
|
Halfedge_around_facet_circulator hcir = facet->facet_begin();
|
|
int n = 0;
|
|
Kernel::FT p[] = {0,0,0};
|
|
// Apply the stencil while circulating around the facet.
|
|
do {
|
|
Point t = hcir->vertex()->point();
|
|
p[0] += t[0], p[1] += t[1], p[2] += t[2];
|
|
++n;
|
|
} while (++hcir != facet->facet_begin());
|
|
// Assign the smoothing point.
|
|
pt = Point(p[0]/n, p[1]/n, p[2]/n);
|
|
}
|
|
\end{lstlisting}
|
|
The facet circulator provides a convenient way to
|
|
traverse and collect the points. The point calculation use
|
|
the conventional interface \CodeFmt{[i]} of the point type.
|
|
For \CodeFmt{Point} not equipped with the index access
|
|
\CodeFmt{[i]}, a user-implemented policy class need to be
|
|
provided. The CGAL \CodeFmt{Point\_3}/\CodeFmt{Vector\_3}
|
|
computation can be used if the \CodeFmt{Point} is the equivalent
|
|
type of \CodeFmt{Point\_3} which is shown below.
|
|
\begin{lstlisting}
|
|
void face_point_rule(Facet_handle facet, Point& pt) {
|
|
Halfedge_around_facet_circulator hcir = facet->facet_begin();
|
|
// Use CGAL::ORIGIN to transform Point into Vector.
|
|
Vector vec = hcir->vertex()->point() - CGAL::ORIGIN;
|
|
++hcir;
|
|
do {
|
|
// Vector is a computational class
|
|
vec = vec + hcir->vertex()->point();
|
|
} while (++hcir != facet->facet_begin());
|
|
// Use CGAL::ORIGIN to transform Vector back to Point.
|
|
pt = CGAL::ORIGIN + vec/circulator_size(hcir);
|
|
}
|
|
\end{lstlisting}
|
|
|
|
The \CodeFmt{edge\_point\_rule()} of
|
|
Catmull-Clark subdivision requires the low lever
|
|
halfedge traversal that is the \CodeFmt{next()},
|
|
the \CodeFmt{prev()}, and the \CodeFmt{opposite()}
|
|
of the halfedge item.
|
|
\begin{lstlisting}
|
|
void edge_point_rule(Halfedge_handle edge, Point& pt) {
|
|
Point p1 = edge->vertex()->point();
|
|
Point p2 = edge->opposite()->vertex()->point();
|
|
Point f1, f2;
|
|
face_point_rule(edge->facet(), f1);
|
|
face_point_rule(edge->opposite()->facet(), f2);
|
|
pt = Point((p1[0]+p2[0]+f1[0]+f2[0])/4,
|
|
(p1[1]+p2[1]+f1[1]+f2[1])/4,
|
|
(p1[2]+p2[2]+f1[2]+f2[2])/4 );
|
|
}
|
|
\end{lstlisting}
|
|
The \CodeFmt{edge->opposite()} is used to locate the opposite point
|
|
and the opposite facet. Instead of using the facet circulator for each
|
|
facet after obtaining the facet handle, the
|
|
\CodeFmt{face\_point\_rule} is called to calculate the facet
|
|
centroids. The smoothing point is then assigned as the centroid of the
|
|
two opposite points and the two facet centroids.
|
|
|
|
The \CodeFmt{vertex\_point\_rule} for Catmull-Clark subdivision
|
|
is more complicated than the other two policy functions. Unlike
|
|
the facet and edge rules, vertex rule is not static in the scenes
|
|
of the stencil weights. The weights are functions of the vertex
|
|
valence and it introduces more geometry computations.
|
|
Nonetheless, the connectivity traversal is still homomorphic to
|
|
a vertex circulation.
|
|
\begin{lstlisting}
|
|
void vertex_point_rule(Vertex_handle vertex, Point& pt) {
|
|
// Only a vertex circulator is needed to collect the submesh.
|
|
Halfedge_around_vertex_circulator vcir = vertex->vertex_begin();
|
|
// The vertex valence is used to calculate the stencil weights.
|
|
int n = circulator_size(vcir);
|
|
|
|
float Q[] = {0.0, 0.0, 0.0}, R[] = {0.0, 0.0, 0.0};
|
|
Point& S = vertex->point(); // The center vertex
|
|
|
|
Point q;
|
|
for (int i = 0; i < n; i++, ++vcir) {
|
|
Point& p2 = vcir->opposite()->vertex()->point();
|
|
R[0] += (S[0]+p2[0])/2; R[1] += (S[1]+p2[1])/2; R[2] += (S[2]+p2[2])/2;
|
|
face_point_rule(vcir->facet(), q);
|
|
Q[0] += q[0]; Q[1] += q[1]; Q[2] += q[2];
|
|
}
|
|
R[0] /= n; R[1] /= n; R[2] /= n;
|
|
Q[0] /= n; Q[1] /= n; Q[2] /= n;
|
|
|
|
// Assign the smoothing point.
|
|
pt = Point((Q[0] + 2*R[0] + S[0]*(n-3))/n,
|
|
(Q[1] + 2*R[1] + S[1]*(n-3))/n,
|
|
(Q[2] + 2*R[2] + S[2]*(n-3))/n );
|
|
}
|
|
\end{lstlisting}
|
|
\vspace{0.5cm}
|
|
|
|
\noindent \textbf{Connectivity refinement} in CSL is
|
|
design as a host function. A refinement host refines the input
|
|
control polyhedron, maintains the stencil correspondence
|
|
and assign the smoothed points.
|
|
The \CodeFmt{quad\_quadralize\_polyhedron} is the refinement host
|
|
for a PQQ scheme. It redirects the refinement by
|
|
repeating the \CodeFmt{quad\_quadralize\_1step()} that does
|
|
one-step polyhedron refinement.
|
|
\begin{lstlisting}
|
|
// RULE is a template parameter specifying the geometry stencils.
|
|
template <template <typename> class RULE>
|
|
void quad_quadralize_polyhedron(Polyhedron& p, RULE<Polyhedron> rule, int d) {
|
|
// Do d times refinement.
|
|
for (int i = 0; i < d; i++) quad_quadralize_1step(p, rule);
|
|
}
|
|
\end{lstlisting}
|
|
The \CodeFmt{quad\_quadralize\_1step()} is implemented
|
|
based on a sequence of the Euler operations which
|
|
incrementally modify the connectivity.
|
|
\figurename\ \ref{fig:CCRefinement} illustrates the
|
|
incremental modifications for a PQQ scheme.
|
|
\begin{lstlisting}
|
|
// Build the connectivity using insert_vertex() and insert_edge()
|
|
|
|
// Step1. Insert edge-vertices on all edges and set them to new positions.
|
|
for (int i = 0; i < num_edge; i++, ++eitr) {
|
|
Vertex_handle vh = insert_vertex(p, eitr);
|
|
vh->point() = edge_point_buffer[i]; // Points are obtained with the edge rule.
|
|
}
|
|
fitr = p.facets_begin();
|
|
for (int i = 0; i < num_facet; i++, ++fitr) {
|
|
Halfedge_around_facet_circulator hcir_begin = fitr->facet_begin();
|
|
Halfedge_around_facet_circulator hcir = hcir_begin;
|
|
|
|
// Step2. Insert a cut-edge between 2 randomly selected incident edge-vertices.
|
|
Halfedge_handle e1 = ++hcir;
|
|
++hcir;
|
|
Halfedge_handle e2 = ++hcir;
|
|
++hcir; // Must move the cir before inserts the new edge !!
|
|
Halfedge_handle newe = insert_edge(p, e1, e2);
|
|
|
|
// Step3. Insert a facet-vertex on the cut-edge and set it to the new position
|
|
Halfedge_handle newv = insert_vertex_return_edge(p, newe);
|
|
newv = newv->opposite()->prev(); // change newv to the larger face and
|
|
// still points to the newly inserted
|
|
// vertex
|
|
// Update the geometry data of the face-vertex
|
|
newv->vertex()->point() = face_point_buffer[i]; // Points are obtained with the facet rule.
|
|
|
|
// Step4. Insert the facet-edges between the edge-vertices and the facet-vertex.
|
|
while (hcir != hcir_begin) {
|
|
e1 = ++hcir;
|
|
++hcir; // Must move the cir before inserts the new edge !!
|
|
insert_edge(p, e1, newv);
|
|
}
|
|
}
|
|
// Update the geometry data of the vertex-vertices
|
|
vitr = p.vertices_begin();
|
|
for (int i = 0; i < num_vertex; i++, ++vitr)
|
|
vitr->point() = vertex_point_buffer[i]; // Points are obtained with the vertex rule.
|
|
\end{lstlisting}
|
|
The details of the Step2 and Step3 are shown in
|
|
\figurename\ \ref{fig:CCRefinementStep23}. Note that the
|
|
\CodeFmt{insert\_vertex()} and \CodeFmt{insert\_edge()} are simple
|
|
connectivity functions composed of the Euler operators provided
|
|
by \cgalpoly . Details about these two functions, readers should refer
|
|
to the \emph{lib/SurfLab/Polyhedron\_decorator.h}.
|
|
\\
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\psfrag{A}[]{Step1}
|
|
\psfrag{B}[]{Step2}
|
|
\psfrag{C}[]{Step3}
|
|
\psfrag{D}[]{Step4}
|
|
\epsfig{file=figs/CCRefinement.eps, width=10cm}
|
|
\caption{A PQQ refinement of a facet is encoded into a sequence of
|
|
vertex insertions and edge insertions. Red indicates the inserted
|
|
vertices and edges in each step.}
|
|
\label{fig:CCRefinement}
|
|
\vspace{0.6cm}
|
|
\epsfig{file=figs/CCRefinementStep23.eps, width=10cm}
|
|
\caption{The Euler operations for the Step2 and Step3 of
|
|
the PQQ refinement.}
|
|
\label{fig:CCRefinementStep23}
|
|
\end{figure}
|
|
|
|
\noindent \textbf{Stencil correspondence} is another key
|
|
behavior for a subdivision algorithm.
|
|
CSL refinement hosts employ the geometry policies
|
|
to generate the smoothing points.
|
|
Three temporary point buffers, \CodeFmt{vertex\_point\_buffer},
|
|
\CodeFmt{edge\_point\_buffer} and \CodeFmt{face\_point\_buffer},
|
|
are used in the refinement host to store the points generated
|
|
by the geometry policies. These points are then assigned
|
|
to the corresponding refined vertices. In the Quad-Triangle
|
|
implementation, the customized item flags are used
|
|
to register the stencil correspondence.
|
|
Since CSL is designed to accept a generic \cgalpoly ,
|
|
customized item flags (witch results a specific \cgalpoly)
|
|
is not a feasible option for CSL. To maintain the stencil
|
|
correspondence, CSL implicitly matches the storage
|
|
order and operation order. The operation order is the
|
|
order of creating the vertices through the connectivity
|
|
operation in the refinement host. This order is demonstrated
|
|
in the \figurename\ \ref{fig:CCRefinement} and the related
|
|
source code. Note \cgalpoly\ allocates new
|
|
geometry items by appending them at the end of the underlying
|
|
containers, in most cases the linked-list or the vector.
|
|
So the operation order is equivalent to the storage of the
|
|
vertex items, hence the storage order of the points.
|
|
CSL arranges the calling order of the geometry policies
|
|
to meet the operation order, which ensures the
|
|
correspondence between the stencils and the points.
|
|
\begin{lstlisting}
|
|
// Build a new vertices buffer has the following structure:
|
|
// 0 1 ... e_begin ... f_begin ... (end_of_buffer)
|
|
// 0 ... e_begin-1 : store the points of the vertex-vertices
|
|
// e_begin ... f_begin-1 : store the points of the edge-vertices
|
|
// f_begin ... (end) : store the points of the face-vertices
|
|
int num_vertex = p.size_of_vertices();
|
|
int num_edge = p.size_of_halfedges()/2;
|
|
int num_facet = p.size_of_facets();
|
|
|
|
// If Polyhedron is using vector, we need to reserve the memory to prevent
|
|
// the CGAL_assertion. We assume p is a quad-polyhedron.
|
|
// This function for polyhedron using list is VOID.
|
|
p.reserve(num_vertex+num_edge+num_facet, 4*2*num_edge, 4*num_edge/2);
|
|
|
|
// Allocate the temporary point buffers.
|
|
Point* vertex_point_buffer = new Point[num_vertex + num_edge + num_facet];
|
|
Point* edge_point_buffer = vertex_point_buffer + num_vertex;
|
|
Point* face_point_buffer = edge_point_buffer + num_edge;
|
|
|
|
std::vector<bool> v_onborder(num_vertex);
|
|
|
|
// Generate the facet points in the operation order.
|
|
Facet_iterator fitr = p.facets_begin();
|
|
for (int i = 0; i < num_facet; i++, ++fitr)
|
|
rule.face_point_rule(fitr, face_point_buffer[i]);
|
|
|
|
int sb = p.size_of_border_edges();
|
|
|
|
// Generate the edge points in the operation order.
|
|
Edge_iterator eitr = p.edges_begin();
|
|
for (int i = 0; i < num_edge-sb; i++, ++eitr)
|
|
rule.edge_point_rule(eitr, edge_point_buffer[i]);
|
|
|
|
// Take care border point as another geometry policy.
|
|
for (int i = num_edge-sb; i < num_edge; i++, ++eitr) {
|
|
int v = std::distance(p.vertices_begin(), eitr->vertex());
|
|
v_onborder[v] = true;
|
|
rule.border_point_rule(eitr, edge_point_buffer[i], vertex_point_buffer[v]);
|
|
}
|
|
|
|
// Generate the vertex points in the operation order.
|
|
Vertex_iterator vitr = p.vertices_begin();
|
|
for (int i = 0; i < num_vertex; i++, ++vitr)
|
|
if (!v_onborder[i]) rule.vertex_point_rule(vitr, vertex_point_buffer[i]);
|
|
\end{lstlisting}
|
|
A border point policy is introduced to support the boundary case.
|
|
Border points usually have special stencil that in general degenerated from
|
|
2-variable surface to 1-variable curve. The full list of the refinement
|
|
host and the geometry policies
|
|
(including \CodeFmt{border\_point\_rule()}) can be found in the
|
|
accompanying source code.
|
|
|
|
|
|
%-----------------------------------------------------------------------
|
|
\subsubsection*{Dual Quad Quadralization}
|
|
|
|
\begin{figure}[htb]
|
|
\centering{\includegraphics[width=12.0cm]{figs/TriCube_DS.eps}}
|
|
\caption{Doo-Sabin subdivision of the box polyhedron.}
|
|
\label{fig:ds}
|
|
\end{figure}
|
|
|
|
Primal schemes, such as PQQ, PTQ and $\sqrt{3}$ refinement, reserve
|
|
the control vertices or even the control polyhedron.
|
|
We can devise a sequence of Euler operations to incrementally
|
|
manipulate the connectivity while maintaining the stencil
|
|
correspondence for these schemes. Dual
|
|
schemes, such as DQQ refinement, exchange the vertex and facet in the
|
|
process. These schemes lost the control vertices and hence are hard to
|
|
devise incremental manipulations. The modifier callback mechanism
|
|
supported by \cgalpoly\ is then used to implement such refinement
|
|
schemes.
|
|
|
|
CSL represents Doo-Sabin subdivision as a DQQ refinement
|
|
parameterized with Doo-Sabin smoothing rules.
|
|
\begin{lstlisting}
|
|
void DooSabin_subdivision(Polyhedron& p, int step = 1) {
|
|
dualize_polyhedron(p, DooSabin_rule<Polyhedron>(), step);
|
|
}
|
|
\end{lstlisting}
|
|
The \CodeFmt{dualize\_polyhedron()} is the refinement host and the
|
|
\CodeFmt{DooSabin\_rule} is a policy class supporting
|
|
Doo-Sabin stencils.
|
|
\\
|
|
|
|
\noindent The \textbf{geometry policy} of a DQQ scheme is shown in
|
|
\figurename\ \ref{fig:RefMap} (d) where the stencil centers around
|
|
a corner (as a facet-vertex pair). Only one geometry policy, i.e.\ the
|
|
corner point, is needed for a DQQ scheme.
|
|
\begin{lstlisting}
|
|
template <class _Poly>
|
|
class dualize_rule {
|
|
public:
|
|
// The corner is pointed by a halfedge handle.
|
|
void point_rule(Halfedge_handle edge, Point& pt) {};
|
|
};
|
|
\end{lstlisting}
|
|
The \CodeFmt{Halfedge\_handle edge} points to the
|
|
corner centered in the stencil submesh; and the
|
|
\CodeFmt{Point\& pt} refers to the smoothing point
|
|
of the refined polyhedron. The implementation of the
|
|
Doo-Sabin stencil is shown below.
|
|
\begin{lstlisting}
|
|
template <class _Poly>
|
|
class DooSabin_rule : public dualize_rule<_Poly> {
|
|
public:
|
|
void point_rule(Halfedge_handle he, Point& pt) {
|
|
// The Doo-Sabin rule is a function of the facet degree.
|
|
int n = CGAL::circulator_size(he->facet()->facet_begin());
|
|
|
|
// CGAL Vector computation is used for simple coding.
|
|
Vector cv(0,0,0), t;
|
|
if (n == 4) { // Regular facet.
|
|
cv = cv + (he->vertex()->point()-CGAL::ORIGIN)*9;
|
|
cv = cv + (he->next()->vertex()->point()-CGAL::ORIGIN)*3;
|
|
cv = cv + (he->next()->next()->vertex()->point()-CGAL::ORIGIN);
|
|
cv = cv + (he->prev()->vertex()->point()-CGAL::ORIGIN)*3;
|
|
cv = cv/16;
|
|
} else { // Extraordinary facet.
|
|
double a;
|
|
for (int k = 0; k < n; ++k, he = he->next()) {
|
|
if (k == 0) a = ((double)5/n) + 1;
|
|
else a = (3+2*std::cos(2*k*3.141593/n))/n;
|
|
cv = cv + (he->vertex()->point()-CGAL::ORIGIN)*a;
|
|
}
|
|
cv = cv/4;
|
|
}
|
|
// Assign the smoothing point.
|
|
pt = CGAL::ORIGIN + cv;
|
|
}
|
|
};
|
|
\end{lstlisting}
|
|
The \CodeFmt{next()} of the halfedges around the
|
|
facet is the only connectivity functionality needed
|
|
to support Doo-Sabin stencil.
|
|
Instead of using the conventional interface \CodeFmt{[i]}
|
|
of the point type, we demonstrate the CGAL
|
|
\CodeFmt{Point\_3}/\CodeFmt{Vector\_3} computation that gives
|
|
more succinct codes.
|
|
\\
|
|
|
|
\noindent \textbf{Connectivity refinement} is implemented
|
|
based on the modifier callback mechanism (MCM). In the
|
|
demonstration of Quad-Triangle subdivision, MCM is used to devise
|
|
customized Euler-like atomic operators. In CSL, MCM is used to
|
|
\emph{rebuild} the refinement polyhedron based on
|
|
a complete facet-vertex index list. This method is called \emph{wholesale}
|
|
scheme in contrast to the \emph{incremental} scheme of Euler operations.
|
|
%The facet-vertex index list is constructed on the encoded order
|
|
%of the connectivity.
|
|
The refinement host of a DQQ scheme is represented as the
|
|
\CodeFmt{dualize\_polyhedron()} and it
|
|
redirects the refinement by repeating a one-step
|
|
refinement function \CodeFmt{dualize\_1step()}.
|
|
\begin{lstlisting}
|
|
template <template <typename> class RULE>
|
|
void dualize_polyhedron(Polyhedron& p, RULE<Polyhedron> rule, int d = 1) {
|
|
for (int i = 0; i < d; ++i) dualize_1step(p, rule);
|
|
}
|
|
\end{lstlisting}
|
|
The \CodeFmt{dualize\_1step()} first constructs a facet-vertex list
|
|
that is similar to the format of a OFF file or the OpenGL vertex
|
|
array. A facet-vertex list contains two buffers: a point buffer and a
|
|
facet index buffer. The point buffer stores the smoothing points
|
|
generated by the geometry policy, i.e.\ the \CodeFmt{point\_rule()}.
|
|
The points are generated in the order of the halfedge iterator. Note
|
|
that each halfedge points to a corner that is a vertex on the refined
|
|
polyhedron. The facet index buffer contains a list of the vertex
|
|
indices which depict facet polygons of the refined polyhedron. The
|
|
vertex indices point to the storage position in the point
|
|
buffer. Since each facet of the refined polyhedron is mapped into a
|
|
geometry primitive, i.e.\ vertex, edge, and facet, of the source
|
|
polyhedron, the facet order is defined by (and equal to) the iterator
|
|
order of the primitives in a \cgalpoly .
|
|
\begin{lstlisting}
|
|
template <class _P> template <template <typename> class RULE>
|
|
void Polyhedron_subdivision<_P>::dualize_1step(_P& p, RULE<_P> rule) {
|
|
int num_v = p.size_of_vertices();
|
|
int num_e = p.size_of_halfedges()/2; // Number of edges.
|
|
int num_f = p.size_of_facets();
|
|
int num_facet = num_v + num_e + num_f;
|
|
|
|
// Init the facet-vertex list for the refined polyhedron.
|
|
Point* point_buffer = new Point[num_e*2];
|
|
int** facet_buffer = new int*[num_facet];
|
|
for (int i = 0; i < num_facet; ++i) facet_buffer[i] = NULL;
|
|
|
|
// Build the point buffer in the order of the halfedge iterator.
|
|
Halfedge_iterator he_itr = p.halfedges_begin();
|
|
for (int i = 0; i < num_e*2; ++i, ++he_itr) {
|
|
Halfedge_around_facet_circulator cir = he_itr->facet_begin();
|
|
// Generate the point with the geometry policy.
|
|
rule.point_rule(cir, point_buffer[i]);
|
|
}
|
|
|
|
he_itr = p.halfedges_begin(); // Used to calculate the vertex index.
|
|
// The vertex index is the distance of the halfedge iterator to its begin iterator.
|
|
|
|
// Build the facet_buffer. Each refined facet corresponds to a control primitive.
|
|
// Construct the facet-facet.
|
|
Facet_iterator fitr = p.facets_begin();
|
|
for (int i = 0; i < num_f; ++i, ++fitr) {
|
|
Halfedge_around_facet_circulator cir = fitr->facet_begin();
|
|
int n = CGAL::circulator_size(cir); // Can be an extraordinary facet.
|
|
facet_buffer[i] = new int[n+1];
|
|
facet_buffer[i][0] = n;
|
|
for (int j = 1; j < n+1; ++j, ++cir)
|
|
facet_buffer[i][j] =
|
|
std::distance(he_itr, Halfedge_handle(cir.operator->()));
|
|
}
|
|
// Construct the edge-facet.
|
|
Halfedge_iterator eitr = p.halfedges_begin();
|
|
for (int i = num_f; i < num_f+num_e; ++i, ++eitr) {
|
|
facet_buffer[i] = new int[4+1];
|
|
facet_buffer[i][0] = 4;
|
|
facet_buffer[i][1] = (i-num_f)*2;
|
|
facet_buffer[i][2] = std::distance(he_itr, eitr->prev());
|
|
++eitr;
|
|
facet_buffer[i][3] = (i-num_f)*2+1;
|
|
facet_buffer[i][4] = std::distance(he_itr, eitr->prev());
|
|
}
|
|
// Construct the vertex-facet.
|
|
Vertex_iterator vitr = p.vertices_begin();
|
|
for (int i = num_f+num_e; i < num_f+num_e+num_v; ++i, ++vitr) {
|
|
Halfedge_around_vertex_circulator cir = vitr->vertex_begin();
|
|
int n = CGAL::circulator_size(cir); // Can be an extraordinary vertex.
|
|
facet_buffer[i] = new int[n+1];
|
|
facet_buffer[i][0] = n;
|
|
|
|
for (int j = 1; j < n+1; ++j, --cir)
|
|
facet_buffer[i][j] =
|
|
std::distance(he_itr, Halfedge_handle(cir.operator->()));
|
|
}
|
|
|
|
// Rebuild the refined polyhedron.
|
|
p.clear();
|
|
Polyhedron_memory_builder<Polyhedron> pb(num_e*2, point_buffer,
|
|
num_f+num_e+num_v, facet_buffer);
|
|
p.delegate(pb);
|
|
|
|
// release the buffer of the new level
|
|
for (int i = 0; i < num_facet; ++i) delete[] facet_buffer[i];
|
|
delete[] facet_buffer;
|
|
delete[] point_buffer;
|
|
}
|
|
\end{lstlisting}
|
|
After the facet-vertex list is build,
|
|
the refined polyhedron is rebuild from the list with the
|
|
modifier and the incremental builder.
|
|
\begin{lstlisting}
|
|
Point* p = (Point*) point_buffer;
|
|
pb.begin_surface(num_point, num_facet); {
|
|
for (int i = 0; i < num_point; ++i) pb.add_vertex(p[i]);
|
|
for (int i = 0; i < num_facet; ++i) {
|
|
pb.begin_facet(); {
|
|
for (int n = 0; n < facet_buffer[i][0]; ++n)
|
|
pb.add_vertex_to_facet(facet_buffer[i][n+1]);
|
|
}
|
|
pb.end_facet();
|
|
}
|
|
}
|
|
pb.end_surface();
|
|
\end{lstlisting}
|
|
The \CodeFmt{pb} is an object of the
|
|
\CodeFmt{CGAL::Polyhedron\_incremental\_builder\_3}.
|
|
|
|
%-----------------------------------------------------------------------
|
|
\subsubsection*{CSL}
|
|
CSL's policy-based approach offers a convenient way to specialize a
|
|
subdivision with a template geometry policy class. The accompanying
|
|
source code of CSL supports Catmull-Clark, Loop, and Doo-Sabin
|
|
geometry policies and hence the subdivisions. Each of the subdivision
|
|
is constructed by a proper combination of the refinement host and the
|
|
subdivision rules. A customized subdivision can be easily created with a
|
|
user-customized policy class. For example, a linear subdivision with
|
|
PQQ configuration is parameterized with average geometry rules.
|
|
\begin{lstlisting}
|
|
template <class _Poly>
|
|
class average_rule : public quadralize_rule<_Poly> {
|
|
public:
|
|
// Generate the facet centroid.
|
|
void face_point_rule(Facet_handle facet, Point& pt) {
|
|
Halfedge_around_facet_circulator hcir = facet->facet_begin();
|
|
int n = 0;
|
|
FT p[] = {0,0,0};
|
|
do {
|
|
Point t = hcir->vertex()->point();
|
|
p[0] += t[0], p[1] += t[1], p[2] += t[2];
|
|
++n;
|
|
} while (++hcir != facet->facet_begin());
|
|
pt = Point(p[0]/n, p[1]/n, p[2]/n);
|
|
}
|
|
// Generate the edge midpoint.
|
|
void edge_point_rule(Halfedge_handle edge, Point& pt) {
|
|
Point p1 = edge->vertex()->point();
|
|
Point p2 = edge->opposite()->vertex()->point();
|
|
pt = Point((p1[0]+p2[0])/2, (p1[1]+p2[1])/2, (p1[2]+p2[2])/2);
|
|
}
|
|
// Return the vertex itself.
|
|
void vertex_point_rule(Vertex_handle vertex, Point& pt) {
|
|
pt = vertex->point();
|
|
}
|
|
};
|
|
\end{lstlisting}
|
|
Following function call invokes this simple linear PQQ subdivision.
|
|
\begin{lstlisting}
|
|
quad_quadralize_polyhedron(poly, average_rule<Polyhedron>(), step);
|
|
\end{lstlisting}
|
|
|
|
Though demonstrated with a specific enriched \poly\ in our polyhedron
|
|
viewer, CSL accepts any polyhedron mesh specialized from the \poly\
|
|
. The only geometry requirement is the \CodeFmt{Point} type defined in
|
|
the vertex item. Subdivisions in CSL are build as proper combinations
|
|
of the refinement functions and the geometry policy classes (hence the
|
|
name \emph{Combinatory} SL). The proper combination is constrained by
|
|
the stencil correspondence and checked in the compiler time.
|
|
|
|
The tutorial version of CSL only supports the geometry modification of the
|
|
vertex (hence only the isotrophic subdivision). Boundary can be easily
|
|
supported by introducing the boundary policy. But for anisotropic
|
|
subdivisions (e.g.\ Pixar's crease rules), data modifications of the
|
|
halfedge are required. It can be done by introducing halfedge policy,
|
|
though a much complex structure is needed in the refinement host.
|
|
|