A Tutorial On CGAL Polyhedron For Subdivision Algorithms
A Tutorial On CGAL Polyhedron For Subdivision Algorithms
Pierre Alliez
Radu Ursu
Lutz Kettner
Abstract
This document is a tutorial on how to get started with the polyhedron structure provided by CGAL, the
Computational Geometry Algorithm Library. Assuming the reader to be familiar with the C++ template
mechanisms and the key concepts of the STL (Standard Template Library), we first demonstrate
two different approaches for implementing mesh subdivision schemes. Euler operators is applied for 3 subdivision
and the modifier callback mechanism is applied for the Quad-Triangle subdivision. Both approaches are
based on the build-in functionalities of the CGAL polyhedron. We then introduce a combinatory subdivision library (CSL) with increasing level of sophistication and abstraction. CSL offers a convenient way to
design user-customized subdivision schemes through the definition of rule templates. Catmull-Clark and
Doo-Sabin schemes are used to demonstrate the design and implementation of CSL. Two companion applications based on OpenGL, one developed with Windows MFC, and the other developed with Qt, showcase
the subdivision schemes listed above, as well as several functionalities for interaction and visualization.
Keywords: CGAL
library, tutorial, halfedge data structure, polyhedron structure, subdivision surfaces,
1 Introduction
Polyhedron data structures based on the concept of halfedges have been very successful for the design of
general algorithms on meshes. Common practice is to develop such data structure from scratch, since clearly
a first implementation is at the level of a students homework assignment. But then, these data structures
consist almost entirely of pointers for all sort of incidence informations. Maintaining them consistently
during mesh operations is not anymore a trivial linked-list update operation. So, moving from a students
exercise to a reliable research implementation, including maintaining and optimizing it, is a respectable
software task.
What is common practice for simple data structures, such as linked lists, should be common practice
even more so for mesh data structures, namely, to use a good, flexible, and efficient library implementation. In C++ the Standard Template Library, S TL, is an excellent address for our analog example of the
linked lists [Aus99], and we argue that the Polyhedron data structure in C GAL is such a flexible mesh data
structure [Ket99], and it comes with a rich and versatile infrastructure for mesh algorithms. C GAL, the Computational Geometry Algorithms Library, is a C++ library available from www.cgal.org [FGK+ 00].
We strongly believe that this tutorial with its wealth of information will give a head start to new researches and implementations of mesh algorithms. We also believe that it will raise the quality of implementations. Firstly, it encourages the use of well tested and over time matured implementations, e.g.,
SurfLab,
University of Florida
INRIA Sophia-Antipolis
Geometry Factory, Sophia-Antipolis
MPII, Saarbr
ucken
GEOMETRICA,
Figure 1 The polyhedron viewer running on Windows. A coarse polygon mesh is subdivided
using the Quad-Triangle subdivision scheme.
CGAL::Polyhedron 3 in its current design was publicly released in 1999 and used since then. Secondly, it documents good implementation choices, e.g., the example programs can be used as starting points
for evolutionary software development. Thirdly, it offers easy access to additional functionality, such as the
efficient self intersection test, that otherwise could be expandable in a research prototype.
The tutorial is organized around subdivision surfaces in a polyhedron viewer. The polyhedron viewer
(Figure 1) demonstrates the basic functionalities of the CGAL::Polyhedron 3 and some extended functionalities such as file I/O, mesh superimposition, and trackball manipulation. Several
subdivision surfaces
are supported in the polyhedron viewer, including Catmull-Clark, Loop, Doo-Sabin, 3 and Quad-Triangle
subdivisions. The tutorial shows how to implement subdivision surfaces in two different mechanisms
pro
vided by CGAL::Polyhedron 3: Euler operators and modifier callback mechanism. A 3 subdivision
implementation is designed based on the Euler operators and a Quad-Triangle subdivision implementation is designed based on overloading the modifier. Extended from the previous design, a combinatorial
subdivision library (CSL) is then proposed with increased sophistication and abstraction. CSL abstracts
the geometry operations from the refinements. Subdivisions in CSL are build from refinement host with
a template geometry policy. Several fundamental refinement schemes are provided within CSL. They are
instantiated with a geometry policy that can be user defined.
The goal of this tutorial is to show how to use CGAL::Polyhedron 3 on basic graphics functionalities, such as rendering and interactive trackball manipulation, and how to design and implement algorithms
around meshes. Since connectivity and geometry operations are the primal implementation components
in mesh algorithms, subdivisions are chosen to demonstrate both operations on CGAL::Polyhedron 3.
Hence, readers designing and implementing mesh algorithms other than subdivisions will also benefit from
the tutorial.
Intended Audience
The intended audience of the tutorial are researchers, developers or students developing algorithms around
polyhedron meshes. Knowledge of the halfedge data structure and subdivisions are prerequisites. Short
introductions of these two topics are given in the tutorial. The tutorial assumes familiarity with the C++
template mechanism and the key concepts of generic programming [Aus99].
Figure 2 One halfedge and its incident primitives. The next halfedge, the opposite halfedge,
and the incident vertex are mandatory, the remaining elements are optional.
What are the potential obstacles in using CGAL and CGAL::Polyhedron 3?
1. Is it fast enough? Yes. C GAL, coming from the field of Computational Geometry, might have a
reputation of using slow exact arithmetic to be on the safe side, but nonetheless, we know where
to apply the right techniques of exact arithmetic to gain robustness and yet not to loose efficiency.
In addition, C GAL uses generic programming and compile-time polymorphism to realize flexibility
without affecting optimal runtime.
2. Is it small enough? Yes. CGAL::Polyhedron 3 can be tailored to store exactly the required
incidences and other required data, not more and not less.
3. Is it flexible enough? Yes, certainly within its design space of oriented 2-manifold meshes with
boundary that was sufficient for the range of applications illustrated with our example programs.
4. Is it easy enough to use? Yes. The full tutorial with its example programs are exactly the starting
point for using CGAL::Polyhedron 3. The example programs are short and easy to understand.
There is certainly a learning curve for mastering C++ to the level of using templates, but it has to be
emphasized that using templates is far easier then developing templated code.
PSfrag replacements
PQQ
PTQ
DQQ
and DQQ scheme in Doo-Sabin subdivision [DS78]. We also demonstrate Loop [Loo94], 3 [Kob00] and
Quad-Triangle [SL03] subdivisions in this tutorial. For further details about subdivisions, readers should
refer to [WW02] and [ZS00].
3 Polyhedron Viewer
This tutorial implements an interactive basic polyhedron viewer based on the CGAL::Polyhedron 3
with the default configuration.
This viewer demonstrates basic functionalities of a
CGAL::Polyhedron 3. We show the mesh traversal based on the iterators and the circulators
during the assembly of facet polygons for basic OpenGL rendering. The viewer is then extended by
customizing the Polyhedron 3 with extra attributes and functionalities. This enriched polyhedron
supports facet and vertex normals for rendering, an axis-aligned bounding box of the polyhedron, and
provides geometry items specialized with algorithmic flags. A number of rendering modes are available to
the user depending on the choices of lighting, shading and edge superimposing. The superimposition of the
control mesh on the subdivision surfaces is implemented for the quad-triangle scheme with a boolean flag
of the halfedge item, this flag being automatically propagated to the subdivided edges during subdivision
(Figure 7).
The tutorial demonstrates basic combinatorial algorithms on the connectivity of the polyhedron by
counting the number of connected components and boundaries, and deducing the combinatorial genus of
the active polyhedron.
PSfrag replacements
(a)
(b)
(c)
(d)
Figure 4 The stencil (top blue) and its vertex (bottom red) in Catmull-Clark subdivision (ac) and Doo-Sabin subdivision (d). Catmull-Clark subdivision has three stencils: facet-stencil
(a), edge-stencil (b) and vertex-stencil (c). Doo-Sabin subdivision has only corner-stencil
(d). The stencil weights are not shown.
In addition to the build-in features of OFF file I/O in C GAL, we show how to import a polyhedron file
in the OBJ format based on the modifier callback mechanism and the incremental builder. The OBJ file
exporting is simply based on mesh traversal.
The camera and transformation states are automatically adjusted when a new polyhedron is loaded so as
to originally view the model in all. A function snapshots the camera and transformation states for the sake
of comparing two models with the same viewpoint.
The viewer also features a raster output of the current client image to the clipboard, as well as a vectorial
output to a postscript file. Note however that the latter functionality is not based on the painter algorithm
and performs instead a simple z-sorting of the polygons based on each facet barycenter and the current
viewpoint.
4 Subdivision Surfaces
The second part of the tutorial focuses on the design and the implementation of 3 (Figure 5), QuadTriangle subdivision (Figure 7) and our combinatory subdivision library (CSL).
In addition to its importance in the surface modeling, we choose subdivision algorithms to demonstrate both the connectivity operation (refinement) and the geometry operation (smoothing) of a
CGAL::Polyhedron 3. These two operations are the primary implementation components required
by algorithms on polyhedron meshes. Readers intended to design and implement mesh algorithms other
than subdivisions will also be benefited from the techniques we proposed here.
The key to implement a subdivision algorithm is to efficiently support the refinement, i.e. the connectivity modifications. Two approaches are introduced to support the refinement: the Euler operators (operator
scheme) and the modifier callback mechanism (modifier
scheme). The operator scheme reconfigures the
connectivity with a combination of Euler operators.
3 subdivision [Kob00] is used to demonstrate this
scheme. We also compare our implementation with the 3 subdivision provided in OpenMesh library.
Though simple and efficient in some refinements, e.g. 3 subdivision, the correct combination of the
operators is hard to find for some refinements, e.g. Doo-Sabin subdivision [DS78]. The modifier scheme
solves the problem by letting the programmers create their own combinatorial operators using the polyhedron incremental builder. Quad-Triangle subdivision [SL03, Lev03] is used to demonstrate this scheme.
4.1
3 Subdivision
This scheme was introduced as an adaptive scheme [Kob00], but we restrict our example program to a single
uniform subdivision step, see Fig. 5 for an example of a subdivision sequence and Fig. 6 for a closeup on
the refinement.
The subdivision step takes a triangle mesh as input and splits each facet at its centroid into three triangles.
We write a function that creates the centroid for one triangle. The topology refinement part exists already as
an Euler operator in CGAL::Polyhedron 3, we only have to compute the coordinates of the new vertex.
Since the facet is a triangle, we access the 1-ring of the centroid directly without any loops or branching
decisions (in general, we could use the circulator loop shown in the render function).
Figure 5
Next, all edges of the initial mesh are flipped to join two adjacent centroids. It is part of the
CGAL::Polyhedron 3 interface.
Finally, each initial vertex is replaced by a barycentric combination of its neighbors. However, the mesh
has already been subdivided, so the original neighbors of a vertex are actually every other vertex in the
1-ring. We write a function object for the smoothing step that can be used with the std::transform
function.
Figure 6 The
and edge flips.
In the final subdivision program we exploit that newly created items are appended at the end of the sequences, so that we can keep valid iterators telling us where the old items end and where the new items
start. We are as economical as possible with the extra storage needed in this method, which is an extra array
for the smoothed coordinates of original vertices. We start by creating the centroids, then smooth the old
vertices, and conclude with flipping the old edges.
The O PEN M ESH library Release 1.0.0-beta4 comes with a demo application
for the subdivision algorithms
that are available in O PEN M ESH. Since L. Kobbelt, the author of the 3-subdivision, is the head of the
group developing O PEN M ESH, it is natural to find his algorithm in the library. We compared it with our
example implementation on a laptop with an Intel Mobile Pentium4 running at 1.80GHz with 512KB cache
3-subdivision
Lion vase: step 1
Lion vase: step 2
C GAL
float double
0.95
1.22
3.90
23.73
O PEN M ESH
float
1.27
128.00
The result is clearly encouraging for the C GAL implementation, but it should be interpreted cautiously.
For example, the O PEN M ESH implementation was obviously running into swap problems in the second
refinement step, which is not expected when studying the example program and reading the manual about
the default space requirements of this implementation. Nonetheless, the simple and easy customization
possible with the CGAL::Polyhedron 3 resulted in a short, readable, and competitive implementation
for this algorithm without great efforts. It is also the first result showing that the abstraction of Euler
operations does not necessarily harm your performance, and they clearly simplify things.
The prime example for this backdoor use is an alternative way of describing meshes in the indexed-facet-set
format that is common in file formats: Points are defined with coordinates, then facets are defined by the
points on their boundary, but the points are given as indices to the already given list of points.
In the example below we make use of the incremental builder to assemble a subdivided polyhedron from
an input polyhedron. Our implementation requires enriched halfedge, vertex and facet primitives with an
integer tag that recovers the vertex indices of the subdivided model.
NewMesh . d e l e g a t e ( b u i l d e r ) ;
/ / smooth
b u i l d e r . smooth (&NewMesh , s m o o t h b o u n d a r y ) ;
};
t e m p l a t e < c l a s s I n p u t I t e r a t o r , c l a s s U n a r y F u n c t i o n>
UnaryFunction for each ( I n p u t I t e r a t o r f i r s t , I n p u t I t e r a t o r l a s t , UnaryFunction f ) ;
The for each is the algorithm host and the 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 generic types, i.e. the polyhedron,
and generic behaviors, i.e. the subdivisions. The generic type is specified to follow the interface of the
CGAL::Polyhedron 3 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 Point type of a vertex item. The operational interface of the Point
is not specified by CSL and can be non-CGAL style. For a non-CGAL Point type, users should provide
user-defined policies that perform the point operations.
A subdivision algorithm has three key behaviors: refinement, smoothing, and stencil correspondence.
The refinement is acted as a for each algorithm on the source 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 Figure 3. 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 Figure 4. These stencil correspondences specified
the functional interface between the refinement hosts and the geometry smoothing policies.
The quad quadralize polyhedron is the refinement host that refines the control polyhedron using
PQQ scheme and the CatmullClark rule is the template geometry policy class.
Geometry policies are represented as the policy functions of the policy class. Each policy function receive
a 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.
t e m p l a t e < c l a s s P o l y>
class quadralize rule {
public :
void f a c e p o i n t r u l e ( Facet handle , P o i n t &) {};
void e d g e p o i n t r u l e ( Halfedge handle , P o i n t &) {};
void v e r t e x p o i n t r u l e ( Vertex handle , P o i n t &) {};
};
The interface is defined according to the stencil correspondence of the refinement scheme. A PQQ scheme
contains three stencils that are shown in Figure 4 (ac). Each of them defines a policy function, which of the
quadralize rule is the facet rule(), the edge rule(), and the 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.
/ / S p e c i a l i z e d a C a t m u l lC l a r k r u l e by i n h e r i t i n g t h e q u a d r a l i z e r u l e .
t e m p l a t e < c l a s s P o l y>
c l a s s C a t m u l l C l a r k r u l e : public q u a d r a l i z e r u l e < Poly > {...}
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 face point rule for Catmull-Clark subdivision demonstrates the usage of a facet circulator for
stenciling.
void f a c e p o i n t r u l e ( F a c e t h a n d l e f a c e t , P o i n t & pt ) {
/ / F a c e t c i r c u l a t o r i s u s e d t o t r a v e r s e t h e 1 r i n g o f a f a c e t .
H a l f e d g e a r o u n d f a c e t c i r c u l a t o r h c i r = f a c e t >f a c e t b e g i n ( ) ;
int n = 0;
K e r n e l : : FT p [ ] = { 0 , 0 , 0 } ;
/ / Apply t h e s t e n c i l w h i l e c i r c u l a t i n g a r o u n d t h e f a c e t .
do {
P o i n t t = h c i r >v e r t e x ()> p o i n t ( ) ;
p[0] += t [0] , p[1] += t [1] , p[2] += t [2];
++n ;
} w h i l e ( + + h c i r ! = f a c e t >f a c e t b e g i n ( ) ) ;
/ / Assign the smoothing p o i n t .
pt = Point ( p [ 0 ] / n , p [ 1 ] / n , p [ 2 ] / n ) ;
}
The facet circulator provides a convenient way to traverse and collect the points. The point calculation use
the conventional interface [i] of the point type. For Point not equipped with the index access [i], a
user-implemented policy class need to be provided. The CGAL Point 3/Vector 3 computation can be
used if the Point is the equivalent type of Point 3 which is shown below.
void f a c e p o i n t r u l e ( F a c e t h a n d l e f a c e t , P o i n t & pt ) {
H a l f e d g e a r o u n d f a c e t c i r c u l a t o r h c i r = f a c e t >f a c e t b e g i n ( ) ;
/ / Use CGAL : : ORIGIN t o t r a n s f o r m P o i n t i n t o V e c t o r .
V e c t o r v e c = h c i r >v e r t e x ()> p o i n t ( ) CGAL : : ORIGIN ;
++ h c i r ;
do {
/ / Vector i s a computational c l a s s
v e c = v e c + h c i r >v e r t e x ()> p o i n t ( ) ;
} w h i l e ( + + h c i r ! = f a c e t >f a c e t b e g i n ( ) ) ;
/ / Use CGAL : : ORIGIN t o t r a n s f o r m V e c t o r b a c k t o P o i n t .
p t = CGAL : : ORIGIN + v e c / c i r c u l a t o r s i z e ( h c i r ) ;
}
The edge point rule() of Catmull-Clark subdivision requires the low lever halfedge traversal that
is the next(), the prev(), and the opposite() of the halfedge item.
v o i d e d g e p o i n t r u l e ( H a l f e d g e h a n d l e edge , P o i n t & p t ) {
P o i n t p1 = edge>v e r t e x ()> p o i n t ( ) ;
P o i n t p2 = edge>o p p o s i t e ()> v e r t e x ()> p o i n t ( ) ;
P o i n t f1 , f 2 ;
f a c e p o i n t r u l e ( edge>f a c e t ( ) , f 1 ) ;
f a c e p o i n t r u l e ( edge>o p p o s i t e ()> f a c e t ( ) , f 2 ) ;
p t = P o i n t ( ( p1 [ 0 ] + p2 [ 0 ] + f 1 [ 0 ] + f 2 [ 0 ] ) / 4 ,
( p1 [ 1 ] + p2 [ 1 ] + f 1 [ 1 ] + f 2 [ 1 ] ) / 4 ,
( p1 [ 2 ] + p2 [ 2 ] + f 1 [ 2 ] + f 2 [ 2 ] ) / 4 ) ;
}
The 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 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 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.
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
quad quadralize polyhedron is the refinement host for a PQQ scheme. It redirects the refinement
by repeating the quad quadralize 1step() that does one-step polyhedron refinement.
/ / RULE i s a t e m p l a t e p a r a m e t e r s p e c i f y i n g t h e g e o m e t r y s t e n c i l s .
t e m p l a t e < t e m p l a t e <typename > c l a s s RULE>
v o i d q u a d q u a d r a l i z e p o l y h e d r o n ( P o l y h e d r o n & p , RULE<P o l y h e d r o n > r u l e , i n t d ) {
/ / Do d t i m e s r e f i n e m e n t .
for ( int i = 0 ; i < d ; i ++) quad quadralize 1step (p , ru le ) ;
}
The quad quadralize 1step() is implemented based on a sequence of the Euler operations which
incrementally modify the connectivity. Figure 9 illustrates the incremental modifications for a PQQ scheme.
/ / B u i l d t h e c o n n e c t i v i t y u s i n g i n s e r t v e r t e x ( ) and i n s e r t e d g e ( )
/ / S t e p 1 . I n s e r t edgev e r t i c e s on a l l e d g e s and s e t them t o new p o s i t i o n s .
f o r ( i n t i = 0 ; i < num edge ; i + + , + + e i t r ) {
V e r t e x h a n d l e vh = i n s e r t v e r t e x ( p , e i t r ) ;
vh>p o i n t ( ) = e d g e p o i n t b u f f e r [ i ] ; / / P o i n t s a r e o b t a i n e d w i t h t h e e d g e r u l e .
}
f i t r = p. facets begin ();
for ( i n t i = 0 ; i < num facet ; i ++ , ++ f i t r ) {
H a l f e d g e a r o u n d f a c e t c i r c u l a t o r h c i r b e g i n = f i t r >f a c e t b e g i n ( ) ;
Halfedge around facet circulator hcir = hcir begin ;
/ / S t e p 2 . I n s e r t a c u te d g e b e t w e e n 2 r a n d o m l y s e l e c t e d i n c i d e n t edgev e r t i c e s .
H a l f e d g e h a n d l e e1 = + + h c i r ;
++ h c i r ;
H a l f e d g e h a n d l e e2 = + + h c i r ;
++ h c i r ; / / Must move t h e c i r b e f o r e i n s e r t s t h e new e d g e ! !
H a l f e d g e h a n d l e newe = i n s e r t e d g e ( p , e1 , e2 ) ;
/ / S t e p 3 . I n s e r t a f a c e t v e r t e x on t h e c u te d g e and s e t i t t o
H a l f e d g e h a n d l e newv = i n s e r t v e r t e x r e t u r n e d g e ( p , newe ) ;
newv = newv>o p p o s i t e ()> p r e v ( ) ; / / c h a n g e newv t o t h e l a r g e r
/ / s t i l l p o i n t s t o t h e newly
/ / vertex
/ / U p d a t e t h e g e o m e t r y d a t a o f t h e f a c ev e r t e x
newv>v e r t e x ()> p o i n t ( ) = f a c e p o i n t b u f f e r [ i ] ; / / P o i n t s a r e
t h e new p o s i t i o n
f a c e and
inserted
/ / S t e p 4 . I n s e r t t h e f a c e t e d g e s b e t w e e n t h e edgev e r t i c e s and t h e f a c e t v e r t e x .
while ( h c i r ! = h c i r b e g i n ) {
e1 = + + h c i r ;
++ h c i r ; / / Must move t h e c i r b e f o r e i n s e r t s t h e new e d g e ! !
i n s e r t e d g e ( p , e1 , newv ) ;
}
}
/ / U p d a t e t h e g e o m e t r y d a t a o f t h e v e r t e x v e r t i c e s
vitr = p. vertices begin ();
for ( i n t i = 0 ; i < num vertex ; i ++ , ++ v i t r )
v i t r >p o i n t ( ) = v e r t e x p o i n t b u f f e r [ i ] ; / / P o i n t s a r e o b t a i n e d w i t h t h e v e r t e x r u l e .
The details of the Step2 and Step3 are shown in Figure 10. Note that the insert vertex() and
insert edge() are simple connectivity functions composed of the Euler operators provided by
CGAL::Polyhedron 3. Details about these two functions, readers should refer to the lib/SurfLab/Polyhedron decorator.h.
PSfrag replacements
Step1
Step2
Step3
Step4
Figure 9 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.
PSfrag replacements
Step1
Step2
Step3
Step4
e2
hcir
e1
newe
newv
Figure 10 The Euler operations for the Step2 and Step3 of the PQQ refinement.
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, vertex point buffer, edge point buffer and 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 CGAL::Polyhedron 3,
customized item flags (witch results a specific CGAL::Polyhedron 3) 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 Figure 9 and the related source code. Note CGAL::Polyhedron 3 al-
locates 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.
/ / B u i l d a new v e r t i c e s b u f f e r h a s t h e f o l l o w i n g s t r u c t u r e :
/ / 0 1 . . . e begin . . . f begin . . . ( end of buffer )
: s t o r e t h e p o i n t s o f t h e v e r t e x v e r t i c e s
/ / 0 . . . e b e g i n 1
/ / e b e g i n . . . f b e g i n 1 : s t o r e t h e p o i n t s o f t h e edgev e r t i c e s
/ / f b e g i n . . . ( end )
: s t o r e t h e p o i n t s o f t h e f a c ev e r t i c e s
int num vertex = p . s i z e o f v e r t i c e s ( ) ;
i n t num edge = p . s i z e o f h a l f e d g e s ( ) / 2 ;
int num facet = p . s i z e o f f a c e t s ( ) ;
//
//
//
p.
I f P o l y h e d r o n i s u s i n g v e c t o r , we n e e d t o r e s e r v e t h e memory t o p r e v e n t
t h e C G A L a s s e r t i o n . We assume p i s a quadp o l y h e d r o n .
T h i s f u n c t i o n f o r p o l y h e d r o n u s i n g l i s t i s VOID .
r e s e r v e ( n u m v e r t e x + num edge + n u m f a c e t , 4 2 num edge , 4 num edge / 2 ) ;
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 border point rule()) can be found in the accompanying source
code.
Primal schemes, such as PQQ, PTQ and 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 CGAL::Polyhedron 3
is then used to implement such refinement schemes.
The dualize polyhedron() is the refinement host and the DooSabin rule is a policy class
supporting Doo-Sabin stencils.
The geometry policy of a DQQ scheme is shown in Figure 4 (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.
t e m p l a t e < c l a s s P o l y>
class dualize rule {
public :
/ / The c o r n e r i s p o i n t e d by a h a l f e d g e h a n d l e .
v o i d p o i n t r u l e ( H a l f e d g e h a n d l e edge , P o i n t & p t ) { } ;
};
The Halfedge handle edge points to the corner centered in the stencil submesh; and the Point&
pt refers to the smoothing point of the refined polyhedron. The implementation of the Doo-Sabin stencil is
shown below.
t e m p l a t e < c l a s s P o l y>
c l a s s DooSabin rule : public d u a l i z e r u l e < Poly > {
public :
v o i d p o i n t r u l e ( H a l f e d g e h a n d l e he , P o i n t & p t ) {
/ / The DooS a b i n r u l e i s a f u n c t i o n o f t h e f a c e t d e g r e e .
i n t n = CGAL : : c i r c u l a t o r s i z e ( he>f a c e t ()> f a c e t b e g i n ( ) ) ;
/ / CGAL V e c t o r c o m p u t a t i o n i s u s e d f o r s i m p l e c o d i n g .
V e c t o r cv ( 0 , 0 , 0 ) , t ;
i f ( n = = 4 ) { / / Regular f a c e t .
cv = cv + ( he>v e r t e x ()> p o i n t ()CGAL : : ORIGIN ) 9 ;
cv = cv + ( he>n e x t ()> v e r t e x ()> p o i n t ()CGAL : : ORIGIN ) 3 ;
cv = cv + ( he>n e x t ()> n e x t ()> v e r t e x ()> p o i n t ()CGAL : : ORIGIN ) ;
cv = cv + ( he>p r e v ()> v e r t e x ()> p o i n t ()CGAL : : ORIGIN ) 3 ;
cv = cv / 1 6 ;
} else { / / Extraordinary facet .
double a ;
f o r ( i n t k = 0 ; k < n ; + + k , he = he>n e x t ( ) ) {
i f ( k = = 0 ) a = ( ( double ) 5 / n ) + 1 ;
e l s e a = ( 3 + 2 s t d : : c o s (2 k 3 . 1 4 1 5 9 3 / n ) ) / n ;
cv = cv + ( he>v e r t e x ()> p o i n t ()CGAL : : ORIGIN ) a ;
}
cv = cv / 4 ;
}
/ / Assign the smoothing p o i n t .
p t = CGAL : : ORIGIN + cv ;
};
The next() of the halfedges around the facet is the only connectivity functionality needed to support
Doo-Sabin stencil. Instead of using the conventional interface [i] of the point type, we demonstrate the
CGAL Point 3/Vector 3 computation that gives more succinct codes.
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 rebuild the refinement polyhedron based on a complete facet-vertex index list. This
method is called wholesale scheme in contrast to the incremental scheme of Euler operations. The refinement host of a DQQ scheme is represented as the dualize polyhedron() and it redirects the
refinement by repeating a one-step refinement function dualize 1step().
The 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 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
CGAL::Polyhedron 3.
I n i t t h e f a c e t v e r t e x l i s t f o r t h e r e f i n e d p o l y h e d r o n .
P o i n t p o i n t b u f f e r = new P o i n t [ num e 2 ] ;
i n t f a c e t b u f f e r = new i n t [ n u m f a c e t ] ;
f o r ( i n t i = 0 ; i < n u m f a c e t ; + + i ) f a c e t b u f f e r [ i ] = NULL ;
/ / Build the point b u f f e r in the order of the halfedge i t e r a t o r .
Halfedge iterator he itr = p . halfedges begin ( ) ;
f o r ( i n t i = 0 ; i < num e 2 ; + + i , + + h e i t r ) {
H a l f e d g e a r o u n d f a c e t c i r c u l a t o r c i r = h e i t r >f a c e t b e g i n ( ) ;
/ / Generate the p o i n t with the geometry p o l i c y .
rule . point rule ( cir , point buffer [ i ] ) ;
}
h e i t r = p . h a l f e d g e s b e g i n ( ) ; / / Used t o c a l c u l a t e t h e v e r t e x i n d e x .
/ / The v e r t e x i n d e x i s t h e d i s t a n c e o f t h e h a l f e d g e i t e r a t o r t o i t s b e g i n i t e r a t o r .
/ / B u i l d t h e f a c e t b u f f e r . Each r e f i n e d f a c e t c o r r e s p o n d s t o a c o n t r o l p r i m i t i v e .
/ / C o n s t r u c t t h e f a c e t f a c e t .
Facet iterator f i t r = p. facets begin ();
f o r ( i n t i = 0 ; i < num f ; + + i , + + f i t r ) {
Halfedge around facet circulator
c i r = f i t r >f a c e t b e g i n ( ) ;
i n t n = CGAL : : c i r c u l a t o r s i z e ( c i r ) ; / / Can be an e x t r a o r d i n a r y f a c e t .
f a c e t b u f f e r [ i ] = new i n t [ n + 1 ] ;
facet buffer [ i ][0] = n;
for ( int j = 1 ; j < n +1; ++ j , ++ c i r )
facet buffer [ i ][ j ] =
s t d : : d i s t a n c e ( h e i t r , H a l f e d g e h a n d l e ( c i r . o p e r a t o r >()));
}
/ / C o n s t r u c t t h e edgef a c e t .
Halfedge iterator e i t r = p . halfedges begin ( ) ;
f o r ( i n t i = num f ; i < num f +num e ; + + i , + + e i t r ) {
f a c e t b u f f e r [ i ] = new i n t [ 4 + 1 ] ;
facet buffer [ i ][0] = 4;
f a c e t b u f f e r [ i ] [ 1 ] = ( inum f ) 2 ;
f a c e t b u f f e r [ i ] [ 2 ] = s t d : : d i s t a n c e ( h e i t r , e i t r >p r e v ( ) ) ;
++ e i t r ;
f a c e t b u f f e r [ i ] [ 3 ] = ( inum f ) 2 + 1 ;
f a c e t b u f f e r [ i ] [ 4 ] = s t d : : d i s t a n c e ( h e i t r , e i t r >p r e v ( ) ) ;
}
/ / C o n s t r u c t t h e v e r t e x f a c e t .
Vertex iterator vitr = p. vertices begin ();
f o r ( i n t i = num f +num e ; i < num f +num e+num v ; + + i , + + v i t r ) {
Halfedge around vertex circulator
c i r = v i t r >v e r t e x b e g i n ( ) ;
i n t n = CGAL : : c i r c u l a t o r s i z e ( c i r ) ; / / Can be an e x t r a o r d i n a r y v e r t e x .
f a c e t b u f f e r [ i ] = new i n t [ n + 1 ] ;
facet buffer [ i ][0] = n;
f o r ( i n t j = 1 ; j < n + 1 ; + + j , c i r )
facet buffer [ i ][ j ] =
s t d : : d i s t a n c e ( h e i t r , H a l f e d g e h a n d l e ( c i r . o p e r a t o r >()));
After the facet-vertex list is build, the refined polyhedron is rebuild from the list with the modifier and the
incremental builder.
f o r ( i n t i = 0 ; i < n u m p o i n t ; + + i ) pb . a d d v e r t e x ( p [ i ] ) ;
for ( int i = 0 ; i < num facet ; ++ i ) {
pb . b e g i n f a c e t ( ) ; {
for ( int n = 0 ; n < f a c e t b u f f e r [ i ] [ 0 ] ; + + n )
pb . a d d v e r t e x t o f a c e t ( f a c e t b u f f e r [ i ] [ n + 1 ] ) ;
}
pb . e n d f a c e t ( ) ;
}
}
pb . e n d s u r f a c e ( ) ;
CSL
CSLs 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.
t e m p l a t e < c l a s s P o l y>
c l a s s a v e r a g e r u l e : public q u a d r a l i z e r u l e < Poly > {
public :
/ / Generate the f a c e t centroid .
void f a c e p o i n t r u l e ( F a c e t h a n d l e f a c e t , P o i n t & pt ) {
H a l f e d g e a r o u n d f a c e t c i r c u l a t o r h c i r = f a c e t >f a c e t b e g i n ( ) ;
int n = 0;
FT p [ ] = { 0 , 0 , 0 } ;
do {
P o i n t t = h c i r >v e r t e x ()> p o i n t ( ) ;
p[0] += t [0] , p[1] += t [1] , p[2] += t [2];
++n ;
} w h i l e ( + + h c i r ! = f a c e t >f a c e t b e g i n ( ) ) ;
pt = Point ( p [ 0 ] / n , p [ 1 ] / n , p [ 2 ] / n ) ;
}
/ / G e n e r a t e t h e edge midpoint .
v o i d e d g e p o i n t r u l e ( H a l f e d g e h a n d l e edge , P o i n t & p t ) {
P o i n t p1 = edge>v e r t e x ()> p o i n t ( ) ;
P o i n t p2 = edge>o p p o s i t e ()> v e r t e x ()> p o i n t ( ) ;
p t = P o i n t ( ( p1 [ 0 ] + p2 [ 0 ] ) / 2 , ( p1 [ 1 ] + p2 [ 1 ] ) / 2 , ( p1 [ 2 ] + p2 [ 2 ] ) / 2 ) ;
}
/ / Return the vertex i t s e l f .
void v e r t e x p o i n t r u l e ( Vertex handle vertex , P o i n t & pt ) {
p t = v e r t e x >p o i n t ( ) ;
}
};
q u a d q u a d r a l i z e p o l y h e d r o n ( p o l y , a v e r a g e r u l e <P o l y h e d r o n > ( ) , s t e p ) ;
Though demonstrated with a specific enriched Polyhedron 3 in our polyhedron viewer, CSL accepts
any polyhedron mesh specialized from the Polyhedron 3 . The only geometry requirement is the 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 Combinatory SL). The proper combination is
constrained by the stencil correspondence and checked in the compiler time.
Figure 12 Models used for benchmarking. Complexity (in triangles): Bunny: 69,451, Lion
vase: 400k, David (simplified version): 700k, Raptor: 2M.
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. Pixars 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.
is fast, see the table below, though not sufficient for interactive use. Nevertheless, a final quality check is
possible for quite large meshes.
See the full program in examples/intersection.C, we discuss a shortened version here that
detects self intersections among a triangle soup, i.e., we ignore the extra handling for the mesh and the
incidences that are no intersections.
// file :
# include
# include
# include
# include
# include
# include
# include
# include
# include
typedef
typedef
typedef
typedef
typedef
typedef
examples / B o x i n t e r s e c t i o n d / t r i a n g l e s e l f i n t e r s e c t .C
<CGAL/ E x a c t p r e d i c a t e s i n e x a c t c o n s t r u c t i o n s k e r n e l . h>
<CGAL/ i n t e r s e c t i o n s . h>
<CGAL/ p o i n t g e n e r a t o r s 3 . h>
<CGAL/ Bbox 3 . h>
<CGAL/ b o x i n t e r s e c t i o n d . h>
<CGAL/ f u n c t i o n o b j e c t s . h>
<CGAL/ J o i n i n p u t i t e r a t o r . h>
<CGAL/ c o p y n . h>
<vector>
CGAL : : E x a c t p r e d i c a t e s i n e x a c t c o n s t r u c t i o n s k e r n e l
Kernel : : Point 3
Kernel : : Triangle 3
s t d : : v e c t o r <T r i a n g l e 3 >
Triangles : : i t e r a t o r
CGAL : : B o x i n t e r s e c t i o n d : : B o x w i t h h a n d l e d <double , 3 ,
Kernel ;
Point 3 ;
Triangle 3 ;
Triangles ;
Iterator ;
I t e r a t o r > Box ;
T r i a n g l e s t r i a n g l e s ; / / global vector of a l l t r i a n g l e s
Up to here we have included all necessary include files, defined our triangle type and a std::vector
storing all triangles, and the Box type. As a specialty, the chosen box type with the double type for its
coordinates and in dimension three has special support for accepting CGAL::Bbox 3 in a constructor. The
box type additionally stores an iterator to the triangle that it encloses.
Next, we define a callback function for the box intersection. It accepts two boxes as arguments and
will be called when the algorithm detects that these two boxes are intersecting. The algorithm uses an idfunction of the boxes, here mapped to the address of the triangles, to avoid reporting pairs of boxes twice.
The trivial intersection of a box with itself is not reported. Now, the boxes are placeholders for triangles;
we access the triangles and test whether the triangles intersect as well.
The main function creates some random triangles, fills a std::vector of boxes with the corresponding C GAL bounding boxes of all triangles, and calls the box self intersection d function of C GAL.
i n t main ( ) {
/ / C r e a t e 1 0 random t r i a n g l e s
t y p e d e f CGAL : : R a n d o m p o i n t s i n c u b e 3 <P o i n t 3 >
Pts ;
t y p e d e f CGAL : : C r e a t o r u n i f o r m 3 < P o i n t 3 , T r i a n g l e 3 >
Creator ;
t y p e d e f CGAL : : J o i n i n p u t i t e r a t o r 3 <P t s , P t s , P t s , C r e a t o r > T r i a n g l e g e n ;
Pts
p o i n t s ( 1 ) ; / / i n c e n t e r e d cube [ 1 ,1)3
Triangle gen triangle gen ( points , points , points ) ;
CGAL : : c o p y n ( t r i a n g l e g e n , 1 0 , s t d : : b a c k i n s e r t e r ( t r i a n g l e s ) ) ;
For the full program example using triangulated meshes, the callback is more involved checking for
triangles that intersect geometrically but that should not be reportted as intersecting since they intersect only
at the common endpoint or common edge with some proper neighbor. Since the callback is more costly, the
tradeoff between calling the box intersection algorithm with the boxes themselfes or with pointers to boxes
(bot variants are supported by default) shift in faviour for the pointer version. So we create an additional
std::vector with pointers to the boxes and run the algorithm on those. For better practical performance,
one also has to consider a cutoff parameter of the algorithm. A higher value than the currently chosen
default turns out to be quite a bit faster for our meshes.
Kernel ;
Instead, when picking the exact number type, we use the kernel:
Kernel ;
Since we are only interested in the points in the vertices of the polyhedron, we choose a version of the
polyhedron with small memory footprint:
The chosen version of the algorithm, CGAL::Min sphere of spheres d, is actually designed to
compute the smallest enclosing sphere of spheres, so it could be used to construct hierarchical schemes
of bounding spheres, but we use it here just for points, i.e., spheres with a degenerate zero radius. The
necessary conversion from points to spheres requires an explicit step.
The algorithm is working in arbitrary dimension, and in the exact case with roots in the coordinates.
This makes the access to the resulting sphere less intuitive, e.g., the center point is accessed with an iterator
over the coordinates, and the following code works only for the double version of the program.
// ...
c o u t << C e n t e r p o i n t
: ;
s t d : : copy ( m i n s p h e r e . c e n t e r c a r t e s i a n b e g i n ( ) ,
min sphere . c e n t e r c a r t e s i a n e n d ( ) ,
s t d : : o s t r e a m i t e r a t o r <double >( c o u t , ) ) ;
c o u t << e n d l ;
c o u t << Double r a d i u s : << m i n s p h e r e . r a d i u s () < < e n d l ;
}
typedef
typedef
typedef
typedef
typedef
CGAL : : E x a c t p r e d i c a t e s i n e x a c t c o n s t r u c t i o n s k e r n e l
Kernel : : Vector 3
Kernel : : Point 3
CGAL : : P o l y h e d r o n 3 <K e r n e l >
Polyhedron : : P o i n t c o n s t i t e r a t o r
void c o n v e x h u l l ( const
CGAL : : c o n v e x h u l l 3
c e r r << # v e r t i c e s
c e r r << # f a c e t s
c e r r << # e d g e s
}
Kernel ;
Vector ;
Point ;
Polyhedron ;
Point iterator ;
P o l y h e d r o n & P , P o l y h e d r o n & Q) {
( P . points begin ( ) , P . points end ( ) , Q) ;
: << Q . s i z e o f v e r t i c e s () < < e n d l ;
: << Q . s i z e o f f a c e t s () < < e n d l ;
: << (Q . s i z e o f h a l f e d g e s ( ) / 2 ) < < e n d l ;
In fact we are more interested in the convex hull as a preprocessing to another optimization algorithm,
the width of a point set. The minimum width is obtained by two parallel planes of smallest possible distance
that enclose all points between them. The printing time for three-dimensional stereo-lithographic printer
is proportional to the height of the object printed. Minimizing this height can be done by computing the
normal direction that minimizes the width between the two planes, and then align this normal direction with
the printer height direction.
The width algorithm requires an exact number type, so we use the result of the convex hull computation,
convert all vertices to exact points, recompute the convex hull, and run the width algorithm. The runtimes in
the table above are for the conversion, recomputing of the convex hull and the width computation together,
but excluding the first convex hull computation that runs on the full data set.
typedef
typedef
typedef
typedef
typedef
CGAL : : E x a c t p r e d i c a t e s e x a c t c o n s t r u c t i o n s k e r n e l
CGAL : : P o l y h e d r o n 3 <EKernel >
EKernel : : P o i n t 3
CGAL : : W i d t h d e f a u l t t r a i t s 3 <EKernel >
CGAL : : Width 3<W i d t h t r a i t s >
EKernel ;
EPolyhedron ;
EPoint ;
Width traits ;
Width ;
6 Application demo
The application demo runs on windows and features a standard MFC multi document-view architecture. It
features a trackball to interactively manipulate the polyhedron. Accepted mesh file formats are ASCII OFF
and OBJ (1-based vertex indices for the latter).
Mouse interaction
Left: rotation
Right: translation
Both: zoom.
Main keys
Press s to subdivide the mesh using the quad-triangle scheme.
Press r to choose a rendering mode. Note that superimposing the control edges during subdivision is only
available for the quad-triangle subdivision scheme.
Press ctrl+c to generate a 24-bits raster image output to the clipboard.
Compiling on Windows
The application has been compiled on MS .NET 2003 using CGAL. Apply the following steps to compile
the demo:
Install the last release of CGAL.
Define an environment variable CGALROOT with the path to the CGAL folder. If you installed
CGAL with the standard wizard installation, the CGALROOT variable was defined at that time.
Compile the CGAL library in multithread mode. Go in CGALROOT/src directory and open
the cgallib project. Open from the main menu, Project Properties. Go to C/C++, Code Generation, Runtime Library and choose Multi-threaded Debug (/MTd). Go to Librarian, General,
Output File and modify to ../lib/msvc7/CGAL MT DEBUG.LIB. If you want to build in release
mode, choose at the Project Properties Configuration Release, and do the same steps as before
but at the Runtime Library you choose Multi-threaded (/MT) and in the output of Librarian put
../lib/msvc7/CGAL MT RELEASE.LIB.
To build debug and release mode, you need to choose the mode in the main menu Build at the Configuration manager.
Open the Mesh project in the tutorial/Polyhedron/MFC/Subdivision/ directory. Choose from the Configuration manager the mode (release or debug) you want to build your application and then go to the
next step.
Rebuild all.
References
[Ale01]
Andrei Alexandrescu. Modern C++ Design: Generic Programming and Design Patterns Applied. Addison-Wesley, 2001.
[Aus99]
Matthew H. Austern. Generic programming and the STL: using and extending the C++ Standard Template Library. Addison-Wessley, 1999.
[BDH96]
C. Bradford Barber, David P. Dobkin, and Hannu Huhdanpaa. The Quickhull algorithm for
convex hulls. ACM Trans. Math. Softw., 22(4):469483, December 1996.
[CC78]
[DS78]
D. Doo and M. Sabin. Behaviour of recursive division surfaces near extraordinary points.
Computer-Aided Design, 10:356360, September 1978.
[FG03]
K. Fischer and B. Gartner. The smallest enclosing ball of balls: Combinatorial structure and
algorithms. In Proc. of ACM Sympos. Comput. Geom., pages 292301, 2003.
[FGK+ 00] A. Fabri, G.-J. Giezeman, L. Kettner, S. Schirra, and S. Scho nherr. On the Design of CGAL, a
Computational Geometry Algorithms Library. Softw. Pract. Exp., 30(11):11671202, 2000.
[Gra96]
T. Granlund. GNU MP, The GNU Multiple Precision Arithmetic Library, version 2.0.2, June
1996.
[Ket99]
L. Kettner. Using generic programming for designing a data structure for polyhedral surfaces.
Comput. Geom. Theory Appl., 13:6590, 1999.
[Kob00]
[Lev03]
[Loo94]
C. Loop. Smooth spline surfaces over irregular meshes. In Proceedings of SIGGRAPH, pages
303310, 1994.
[SL03]
[WW02]
J. Warren and H. Weimer. Subdivision Methods for Geometric Design. Morgan Kaufmann,
2002.
[ZE02]
Afra Zomorodian and Herbert Edelsbrunner. Fast software for box intersection. Int. J. Comput.
Geom. Appl., 12:143172, 2002.
[ZS00]
D. Zorin and P. Schroder, editors. Subdivision for Modeling and Animation, Course Notes.
ACM SIGGRAPH, 2000.