mirror of https://github.com/CGAL/cgal
197 lines
8.1 KiB
TeX
197 lines
8.1 KiB
TeX
Policy-based design \cite{Alexandrescu:2001:MCD} assembles a
|
|
class (called \emph{host}) with complex behavior out of many
|
|
little behaviors (called \emph{policies}). Each policy establishes
|
|
an interface pertaining to a specific behavior.
|
|
Based on this design, we implement a subdivision solution as
|
|
a \emph{refinement function} parameterized with the
|
|
\emph{stencils}. The host, i.e.\ the refinement function,
|
|
conducts the \tr\ and maintains the stencils. The policy,
|
|
i.e.\ the stencil, attends the \gm s. By mixing and matching
|
|
the host and policy, a combinatory library of the subdivisions
|
|
is then constructed.
|
|
|
|
In the following paragraph, we implement the Catmull-Clark
|
|
subdivision and Doo-Sabin subdivision based on the
|
|
policy-based design. For the sake of simplicity, all
|
|
namespaces, template declarations
|
|
and typedefs have been excluded. The sample codes are only
|
|
given for illustration purposes.
|
|
|
|
A Catmull-Clark subdivision function is structured
|
|
as a \emph{primal quadralization function} parameterized
|
|
with the \emph{Catmull-Clark stencil rules}.
|
|
\begin{lstlisting}
|
|
void CatmullClark_subdivision(Polyhedron& p) {
|
|
quadralize_polyhedron<CatmullClark_rule<Polyhedron>>(p);
|
|
}
|
|
|
|
class CatmullClark_rule {
|
|
public:
|
|
void facet_rule(Facet_handle facet, Point& pt);
|
|
void edge_rule(Halfedge_handle edge, Point& pt);
|
|
void vertex_rule(Vertex_handle vertex, Point& pt);
|
|
};
|
|
\end{lstlisting}
|
|
The \lstinline!quadralize_polyhedron<>()! is the host function
|
|
refining the input mesh
|
|
and the \lstinline!CatmullClark_rule!
|
|
is the policy class applying the Catmull-Clark stencils.
|
|
\lstinline!Polyhedron! is the typedef of a user-specialized
|
|
\cgalpoly . The \lstinline!quadralize_polyhedron<>()!
|
|
refines the source mesh while maintaining the
|
|
correspondence of the stencil, i.e.\ the submesh centered
|
|
around the given facet, edge, or
|
|
vertex, and the smoothing vertex. The smoothing point
|
|
is calculated by calling the policies, i.e.\
|
|
the \lstinline!facet_rule()!, the \lstinline!edge_rule()!,
|
|
and the \lstinline!vertex_rule()!.
|
|
Different refinement scheme may require a
|
|
different set of stencils. A PQQ scheme needs the
|
|
facet-, edge- and vertex-stencils whereas a DQQ scheme
|
|
only need the corner-stencils (Figure \ref{fig:RefMap}).
|
|
|
|
\noindent\textbf{Geometry Policies}.
|
|
Inside a policy, applying stencil is simplified as
|
|
the mesh traversal of only the 1-ring neighborhood.
|
|
Following example shows the policy of the facet-stencil
|
|
in the Catmull-Clark subdivision.
|
|
\begin{lstlisting}
|
|
void facet_rule(Facet_handle facet, Point& pt) {
|
|
Halfedge_around_facet_circulator hcir = facet->facet_begin();
|
|
Vector vec = hcir->vertex()->point() - CGAL::ORIGIN;
|
|
++hcir;
|
|
do {
|
|
vec = vec + hcir->vertex()->point();
|
|
} while (++hcir != facet->facet_begin());
|
|
pt = CGAL::ORIGIN + vec/circulator_size(hcir);
|
|
}
|
|
\end{lstlisting}
|
|
The \lstinline!Facet_handle facet! points to the
|
|
center facet of the stencil and the \lstinline!Point& pt!
|
|
specifies the smoothing point. For this specific stencil,
|
|
we compute the centroid with a loop based on a circulator
|
|
over the halfedges surrounding a facet. The CGAL kernel
|
|
geometry, i.e. points and vectors computation, is used.
|
|
Though for a specialized kernel, special computation may be
|
|
applied in the user-defined policies.
|
|
|
|
\noindent\textbf{The refinement host: Euler operations}.
|
|
Implementing a topology refinement is a rather complex job. One
|
|
approach is to encode the refinement into \emph{a sequence of Euler
|
|
operations}. For Catmull-Clark subdivision, the refinement is encoded
|
|
as edge-vertex insertions, edge insertion between two neighboring
|
|
edge-vertices, facet-vertex insertion on the inserted edge, and then
|
|
edge insertions between the facet-vertex and the edge-vertices.
|
|
The Euler sequence is demonstrated in Figure \ref{fig:CCRefinement}.
|
|
Note that the vertex and edge insertions can be easily
|
|
implemented based on the Euler operations supported by \cgalpoly.
|
|
\begin{figure}
|
|
\centering
|
|
\epsfig{file=figs/CCRefinement.eps, width=7cm}
|
|
\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}
|
|
\end{figure}
|
|
|
|
The stencil correspondence of the refinement is assured
|
|
by matching the \emph{traversal sequences} of the mesh.
|
|
The refinement host has a two pass, hence two traversal,
|
|
algorithm. The first pass generates the points by
|
|
calling the policies. The second pass
|
|
refines the mesh with a sequence of the Euler operations.
|
|
The points generated in the first pass are stored in a
|
|
point buffer in the order of the stencil
|
|
traversal. The sequence of the vertex insertions
|
|
is maintained to match the stencil traversal, i.e.\
|
|
the storage order of the point buffer. It assures
|
|
the stencil correspondence of the refinement.
|
|
|
|
\noindent\textbf{The refinement host: modifier callback mechanism}.
|
|
Most primal refinement schemes can be translated into a sequence of
|
|
Euler operations. Though dual schemes, e.g.\ Doo-Sabin subdivision,
|
|
have no simple translation of Euler operations. A sequence
|
|
of Euler operations for a DQQ scheme consists of two times
|
|
of the midedge refinement \cite{Peters:1997:SSS} and
|
|
result an inefficient implementation. This multipass
|
|
refinement scheme, called subdivision operator factorization,
|
|
is proposed in \cite{Peter:2003:CPDSS}.
|
|
To support such schemes efficiently, we use the modifier
|
|
callback mechanism of \cgalpoly\ to rebuild the mesh
|
|
connectivity. In addition to the point buffer, we
|
|
also create a facet list based on the source mesh. Note that in a DQQ
|
|
scheme, every new facet corresponds to a vertex, edge or facet. The
|
|
combination of the points buffer and facet list represents a
|
|
facet-vertex index list which indexes the vertices and enumerates each
|
|
facet as an index sequence. A modifier creating a polyhedron from a
|
|
facet-vertex index list is then a simple task.
|
|
\begin{lstlisting}
|
|
pb.begin_surface(num_point, num_facet); {
|
|
for (int i = 0; i < num_point; ++i)
|
|
pb.add_vertex(Point(point_buffer[i*3+0],
|
|
point_buffer[i*3+1],
|
|
point_buffer[i*3+2]));
|
|
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}
|
|
|
|
|
|
\begin{figure}
|
|
\centering
|
|
\epsfig{file=figs/plane0.eps, width=3.5cm}\\
|
|
\epsfig{file=figs/planeCC1.eps, width=3.5cm}
|
|
\vspace{0.3cm}
|
|
\epsfig{file=figs/planeDS1.eps, width=3.5cm}\\
|
|
\epsfig{file=figs/planeCC2.eps, width=3.5cm}
|
|
\vspace{0.3cm}
|
|
\epsfig{file=figs/planeDS2.eps, width=3.5cm}\\
|
|
\epsfig{file=figs/planeCC.eps, width=3.5cm}
|
|
\vspace{0.3cm}
|
|
\epsfig{file=figs/planeDS.eps, width=3.5cm}
|
|
\caption{ With the input plohedron (first raw),
|
|
the subdivision sequence of the
|
|
the Catmull-Clark subdivision (\IL) and
|
|
the Doo-Sabin subdiviison (\IR).}
|
|
\label{fig:SubExample}
|
|
\end{figure}
|
|
|
|
Our subdivision solution, decoupling the geometry rules from the
|
|
refinement, grants users flexible control of the stencils.
|
|
Variants of the subdivisions can be devised by simply mixing
|
|
and matching the refinement hosts and geometry policies.
|
|
A combinatory subdivision set is provided withn our subdivision
|
|
solution. It includes the Catmull-Clark subdivision, Doo-Sabin
|
|
subdivision, Loop subdivision, Quad-Triangle subdivision and
|
|
$\sqrt{3}$ subdivision. Since
|
|
\cgalpoly\ supports mesh of non-uniform configurations
|
|
(in contrast to the quad-tree or patch-based
|
|
implementation), chain of different refinements
|
|
is naturally supported.
|
|
%\begin{lstlisting}
|
|
%void MySubdivision(Polyhedron& p) {
|
|
% quadralize_polyhedron<Myrule_1<Polyhedron>>(p);
|
|
% dualize_polyhedron<Myrule_2<Polyhedron>>(p);
|
|
%}
|
|
%\end{lstlisting}
|
|
More importantly, our solution accepts a user-specialized
|
|
polyhedron. No special flag or attribute is required
|
|
to assist the \tr . This generality make our solution
|
|
naturally fit in a modeling pipeline or a multipass modeling
|
|
environment. In some applications, efficiency is more
|
|
important than flexibility. An efficient implementation
|
|
of $\sqrt{3}$ based on \cgalpoly\ is introduced in next
|
|
section.
|
|
|
|
%TODO: since the writing memory is not overlapped, multi-threaded
|
|
%supporting is easily done.
|
|
|
|
%TODO: trade-off between generic and efficient.
|
|
|