mirror of https://github.com/CGAL/cgal
2410 lines
99 KiB
Plaintext
2410 lines
99 KiB
Plaintext
%------------------------------------------------------------------------------
|
|
%KILLSTART
|
|
%LDEL TRACE.*?\)\;
|
|
\documentclass[a4paper]{article}
|
|
\usepackage{amsmath,MyLweb,version}
|
|
\input{defs}
|
|
\excludeversion{ignorecodeparts}
|
|
|
|
\begin{document}
|
|
\title{Sphere Map Overlay}
|
|
\author{Michael Seel}
|
|
\maketitle
|
|
\tableofcontents
|
|
\newpage
|
|
|
|
\section{The manual page}
|
|
|
|
\input{SM_overlayer.man}
|
|
\newpage
|
|
|
|
\section{Implementation details}
|
|
%KILLEND
|
|
|
|
In this module we present the implementation of the overlay of sphere
|
|
segments and sphere maps. We first give a formal introduction to the
|
|
notions and difficulties concerning hemisphere geometry, overlay and
|
|
support. We show how we divide the problem into two hemispherical
|
|
problems along a equator of the sphere. We solve these hemispherical
|
|
problems similar to the planar case and finally unify the solutions
|
|
per hemisphere to a complete spherical solution in a stitching process
|
|
along the equator.
|
|
|
|
Following the above hemispherical ideas we present the overlay
|
|
calculation of a set of spherical segments. We use a generic sweep
|
|
module to produce the 1-skeleton of the output sphere map. In a
|
|
different section we show how to add face objects to the 1-skeleton to
|
|
complete the output structure. The second operation concerns the
|
|
overlay of two sphere maps. We use the same generic sweep module with
|
|
slightly more elaborate adaptation to obtain again the 1-skeleton. The
|
|
face production phase will be the same as before. In case of the
|
|
second overlay operation our sweep adds a transfer of information
|
|
assigned to the objects of the two input sphere maps to corresponding
|
|
objects in the output structure. This allows us to use the module for
|
|
binary set operations on sphere map structures. Such set operations
|
|
use a selection phase on the transfered information items. The
|
|
selection phase is descibed below in an additional section. The last
|
|
section in this document concerns structural simplification of the
|
|
output sphere map. We will see that there can be substructures in the
|
|
output sphere map that can be simplified without loosing any
|
|
information when the sphere map is interpreted as a point
|
|
set. Especially the partitioning process per hemisphere introduces
|
|
additional objects that can be removed.
|
|
|
|
We use template member methods to introduce flexibility. All situation
|
|
dependend functionality is wrapped in so called data accessors.
|
|
|
|
\subsection{Notions and definitions}
|
|
|
|
We first have to clarify what kind of spherical geometry we use in the
|
|
surface of our unit sphere $S_2$.
|
|
|
|
In our kernel we introduce geometric objects that are part of the
|
|
spherical surface $S_2$ and operations on them. We define types
|
|
|Sphere_point|, |Sphere_circle|, |Sphere_segment|, |Sphere_direction|,
|
|
and |Sphere_triangle|. |Sphere_point|s are points on $S_2$,
|
|
|Sphere_circle|s are oriented great circles of $S_2$,
|
|
|Sphere_segment|s are oriented parts of |Sphere_circles| bounded by a
|
|
pair of |Sphere_point|s, and |Sphere_direction|s are directions that
|
|
are part of great circles (a direction is usually defined to be a
|
|
vector without length, that floats around in its underlying space and
|
|
can be used to specify a movement at any point of the underlying
|
|
space; in our case we use directions only at points that are part of
|
|
the great circle that underlies also the direction.)
|
|
|
|
Note that we have to consider special geometric properties of the
|
|
objects. For example two points that are part of a great circle define
|
|
two |Sphere_segment|s, and two arbitrary |Sphere_segment|s can
|
|
intersect in two points. The |Sphere_segment| interface account for
|
|
all the operations that are a result of the above ambiguity. For the
|
|
overlay procedures we restrict ourselves to hemispherical sets that
|
|
avoid the ambiguities.
|
|
|
|
If we restrict our geometric objects to a so-called \emph{perfect
|
|
hemisphere} of $S_2$\footnote{A perfect hemisphere of $S_2$ is an open
|
|
half-sphere plus an open half-circle in the boundary of the open
|
|
half-sphere plus one endpoint of the half-circle.} then the restricted
|
|
objects behave like in classical geometry, e.g., two points define
|
|
exactly one segment, two segments intersect in at most one interior
|
|
point (non-degenerately), or three non-cocircular sphere points can be
|
|
qualified as being positively or negatively oriented. As our objects
|
|
are only parts of great circles the underlying kernel operations can
|
|
be realized robustly \cite{schwerdt01}.
|
|
|
|
\begin{figure}[thbp]
|
|
\begin{center}
|
|
\epsfig{file=inputs/inputsegments.ps,width=5cm}\quad
|
|
\epsfig{file=inputs/outputsegmap.ps,width=5cm}
|
|
\end{center}
|
|
\caption{\small The overlay of a set of segments. On the left side the
|
|
input segments have blue endpoints, on the right side the calculated
|
|
sphere map has yellow vertices.}
|
|
\label{fig:overlay}
|
|
\end{figure}
|
|
|
|
\newcommand{\SM}{\ensuremath{M = (V,E,L,F)}}
|
|
\newcommand{\SMi}{\ensuremath{M_i = (V_i,E_i,L_i,F_i)}}
|
|
\newcommand{\Mi}{\ensuremath{M_i}}
|
|
\newcommand{\ST}{\ensuremath{S_2}}
|
|
|
|
If we consider our overlay process as a transformation of input
|
|
objects to output objects then we can define the support relationsship
|
|
as follows.
|
|
\begin{deff}[support]
|
|
Consider an algorithm $T$ that transforms a set of input objects $A$
|
|
to a set of output objects $B$ where each $a \in A$ and $b \in B$
|
|
represents a subset of $S_2$. We say that \emph{$a$ supports $b$} if
|
|
$b$ is a subset of $a$ with respect to the represented point sets.
|
|
\end{deff}
|
|
We will anchor this notion in the following.
|
|
|
|
\textbf{Overlay of a set of spherical segments}\quad For a segment $s
|
|
= (p,q)$, |p = source(s)|, |q = target(s)| and $p$, $q$ are called the
|
|
endpoints of $s$. Let's consider $s$ as a disjoint union of its
|
|
endpoints and its relative interior |relint(s)|. A set of segments $S$
|
|
partitions the sphere into cells of different dimensions. For each
|
|
point $r \in \ST$ it can happen that (i) $r$ is equal to some
|
|
endpoint $p$ of some segment $s$, (ii) $r$ is part of the relative
|
|
interior of some segment $s$, or (iii) $r$ is not part of any segment
|
|
at all.
|
|
|
|
Note that (i) and (ii) do not exclude each other. Now consider the
|
|
geometric structure built by all segments. The \emph{overlay} of all
|
|
segments is the subdivision of all points in $\ST$ with respect to
|
|
the three criteria (i) to (iii) above including their topological
|
|
neighborhood and the knowledge how parts of the segments in $S$
|
|
support the cells of the subdivision.
|
|
|
|
We store the overlay of $S$ in a sphere map \SM{} in the standard
|
|
way. For each point $r$ in (i) there's a vertex $v$ in $V$ where the
|
|
endpoint of the segment supports the vertex. If $r$ is additionally in
|
|
the relative interior of some other segment according to (ii) then
|
|
that segment also supports $v$. For each point in (ii) that is the
|
|
unique intersection point of the relative interior of two segments
|
|
(that don't overlap) there's a vertex in $V$ and the relative interior
|
|
of the two segments support that vertex. Between two vertices in $V$
|
|
there is a uedge $e$ in $E$ if there's a segment $s$ that supports the
|
|
straight line embedding of $e$ according to (ii) and there's no
|
|
further vertex in the relative interior of $e$. The latter can happen
|
|
for several segments that overlap. Any point of (iii) belongs to one
|
|
of the maximal connected sets of $\ST - S$, forming the faces of $M$
|
|
and is thus not supported by any segment at all.
|
|
|
|
Note that sphere circle as input objecst introduce objects that can be
|
|
considered as segments without ends. The notion of support can be
|
|
easily extended. Spherical maps have therefore objects called loops
|
|
that that are embed via such circles.
|
|
|
|
\textbf{Overlay of two sphere maps}\quad Let $M_i = (V_i,E_i,L_i,F_i),
|
|
i=0,1$ be two sphere map structures. The overlay of two sphere maps
|
|
$M_0$, $M_1$ is the sphere map $M$ representing the subdivision of the
|
|
sphere obtained by interpreting the skeleton objects of \Mi{}
|
|
according to their embedding as trivial and non-trivial segments or
|
|
great circles, constructing the overlay of these objects and adding
|
|
the faces. To make this structure really helpfull we store the support
|
|
relationship.
|
|
|
|
\begin{figure}[thbp]
|
|
\begin{center}
|
|
\epsfig{file=inputs/nef1.ps,width=4cm}\
|
|
\epsfig{file=inputs/nef2.ps,width=4cm}\
|
|
\epsfig{file=inputs/nef3.ps,width=4cm}
|
|
\end{center}
|
|
\caption{\small The overlay of two spherical maps. On the left side the
|
|
input polyhedra, on the right side the calculated symmetric difference.}
|
|
\label{fig:sm_overlay}
|
|
\end{figure}
|
|
|
|
In general each point in the sphere is supported by that object of a
|
|
sphere map whose corresponding point set contains it. The support
|
|
relationsship between \Mi{} and $M$ comes in two steps. Each skeleton
|
|
object of \Mi{} relates to the endpoint or relative interior of a
|
|
segment (or circle) that supports a skeleton object in $M$. Reversely
|
|
each object of $M$ (vertex, edge, loop, or face) is supported by a
|
|
unique supporting object in each of the two structures \Mi{}
|
|
$(i=0,1)$. We show that this relationship is well-defined.
|
|
|
|
\begin{lemma}
|
|
Any object of $M$ has exactly one supporting object in each of the
|
|
$M_i$.
|
|
\end{lemma}
|
|
\begin{proof}
|
|
Obviously each point of the sphere is supported by an object of
|
|
$M_i$. Therefore we only have to argue why no two objects of $M_i$ can
|
|
support an object of $M$. For vertices this is trivial. For a uedge
|
|
$e$ in $M$ there can be only one uedge or one face of $M_i$ that
|
|
supports it. Assume the embedding of $e$ covers points from more than
|
|
one object of $M_i$. Then $e$ either contains a vertex or crosses an
|
|
edge in its interior. But then the corresponding subdivision would
|
|
have prevented the creation of $e$ in $M$ in the first place. The same
|
|
holds for a loop $l$ of $M$.
|
|
|
|
For a face $f$ of $M$ there can be only one face $f'$ of $M_i$ that
|
|
supports it. Assume otherwise, that $f$ contains points from different
|
|
objects of $M_i$. As $f$ is an open connected point set, $f$ has to
|
|
cover points of at least one boundary object from the 1-skeleton of
|
|
$M_i$. But this object is part of the 1-skeleton of $M$ and can
|
|
therefore never be part of $f$.
|
|
\end{proof}
|
|
|
|
In our implementation we determine the support relationship in two
|
|
phases. Any vertex $v$ in $V$ can be supported by a vertex $v_i$, a
|
|
uedge $e_i$, a uloop $l_i$, or a face $f_i$ of \Mi. If $v$ is
|
|
supported by $v_i$, $e_i$, or $l_i$ we obtain this information in the
|
|
sweep process. Assume $v$ is supported by a face $f_i$ (then $v$ is
|
|
supported by a vertex $v_{1-i}$). During the sweep process the
|
|
determination of a support of $f_i$ is hard, as the face objects are
|
|
not in reach. We determine $f_i$ in a post processing phase by a
|
|
simple iteration over all vertices. Any edge $e$ in $E$ can be
|
|
supported by a uedge $e_i$, a uloop $l_i$, or a face $f_i$ of \Mi. A
|
|
possible support by $e_i$/$l_i$ is handled during the sweep
|
|
process. In case $e$ is part of a face $f_i$ (again $e$ is then
|
|
supported by an edge $e_{1-i}$ or $l_{1-i}$) we determine $f_i$ also
|
|
in the postprocessing phase.
|
|
|
|
For a face $f$ in $F$ assume that each edge $e$ (or loop $l$) in $E$
|
|
knows the faces $f_i$ supporting points in a small neighborhood on its
|
|
left side. Then $f$ can determine its two supporting faces $f_i$ via
|
|
any edge in its boundary cycle. We will enrich the edges of $E$ by
|
|
such support information.
|
|
|
|
\subsection{The class design}
|
|
|
|
We start with the design of the class object. Our generic overlay
|
|
class can be adapted via two interface concepts. We interface the
|
|
underlying sphere map via a sphere map decorator |Decorator_|. We inherit
|
|
from |Decorator_| to obtain its interface methods.
|
|
<<SM overlayer>>=
|
|
|
|
/*{\Manpage {SM_overlayer}{Decorator_}{Overlay in the sphere}{O}}*/
|
|
|
|
template <typename Decorator_>
|
|
class SM_overlayer : public Decorator_ {
|
|
public:
|
|
<<overlayer local types>>
|
|
protected:
|
|
<<overlayer members>>
|
|
public:
|
|
<<overlayer helping methods>>
|
|
<<overlayer interface methods>>
|
|
<<overlayer methods>>
|
|
}; // SM_overlayer<Decorator_>
|
|
|
|
@ \begin{ignorecodeparts}
|
|
<<overlayer local types>>=
|
|
/*{\Mdefinition An instance |\Mvar| of data type |\Mname| is a
|
|
decorator object offering sphere map overlay calculation. Overlay is
|
|
either calculated from two sphere maps or from a set of halfspaces.
|
|
The result is stored in a sphere map |M| that carries the geometry and
|
|
the topology of the overlay.
|
|
|
|
The template parameter provides the underlying topological interface
|
|
to sphere maps. The template parameter |Decorator_| has to be a model
|
|
conforming to our map decorator concept |SM_decorator|. The concept
|
|
also describes the interface how the topological information stored in
|
|
|M| can be extracted or extended.
|
|
|
|
The overlay of a set of sphere segments $S$ is stored in a sphere map
|
|
$M = (V,E,L,F)$. Vertices are either the endpoints of segments (trivial
|
|
segments are allowed) or the result of the internal intersection of
|
|
two segments. Between two vertices there's an edge if there's a
|
|
segment that supports the spherical embedding of $e$ and if there's no
|
|
vertex in the relative interior of the embedding of $e$.
|
|
|
|
The faces refer to the maximal connected open point sets of the
|
|
spherical subdivision implied by the embedding of the vertices and
|
|
edges. Faces are bounded by possibly several face cycles\footnote{For
|
|
the definition of sphere maps and their concepts see the manual page
|
|
of |SM_decorator|.} including isolated vertices. The overlay process
|
|
in the method |create_from_segments| creates the objects and the
|
|
topology of the result. The method starts from zero- and
|
|
one-dimensional geometric objects in $S$ and produces a spherical
|
|
structure where each point of the sphere can be assigned to an object
|
|
(vertex, edge, loop, or face) of |M|.
|
|
|
|
The overlay of two sphere maps $M_i = (V_i, E_i, L_i, F_i)$ has the
|
|
additional aspect that we already start from two spherical
|
|
subdivisions. We use the index $i=0,1$ defining the reference to
|
|
$M_i$, unindexed variables refer to the resulting sphere map $M$. The
|
|
$1$-skeleta of the two maps subdivide the edges, loops, and faces of
|
|
the complementary structure into smaller units. This means vertices,
|
|
edges, and loops of $M_i$ can split edges and loops of $M_{1-i}$ and
|
|
face cycles of $M_i$ subdivide faces of $M_{1-i}$. The 1-skeleton $G$
|
|
of $M$ is defined by the overlay of the embedding of the 1-skeleta of
|
|
$M_0$ and $M_1$ (Take a trivial segment for each vertex and a segment
|
|
for each edge, and a circle for a loop, and use the overlay definition
|
|
of a set of segments and loops above). The faces of $M$ refer to the
|
|
maximal connected open point sets of the spherical subdivision implied
|
|
by the embedding of $G$. Each object from the output tuple $(V,E,F)$
|
|
has a \emph{supporting} object $u_i$ in each of the two input
|
|
structures. Imagine the two maps to be transparent balls, where one
|
|
contains the other. Then each point of the sphere is covered by an
|
|
object from each of the input structures. This support relationship
|
|
from the input structures to the output structure defines an
|
|
information flow. Each supporting object $u_i$ of $u$ $(i=0,1)$
|
|
carries an associated information $|mark|(u_i)$. After the subdivision
|
|
operation this information is attributed to the output object $u$ by
|
|
$|mark|(u,i)$.}*/
|
|
|
|
<<overlayer local types>>=
|
|
typedef Decorator_ Base;
|
|
typedef typename Base::Sphere_map Sphere_map;
|
|
typedef SM_overlayer<Decorator_> Self;
|
|
CGAL_USING(Vertex_handle);
|
|
CGAL_USING(Halfedge_handle);
|
|
CGAL_USING(Halfloop_handle);
|
|
CGAL_USING(Face_handle);
|
|
CGAL_USING(Vertex_iterator);
|
|
CGAL_USING(Halfedge_iterator);
|
|
CGAL_USING(Face_iterator);
|
|
CGAL_USING(Object_handle);
|
|
CGAL_USING(Halfedge_around_vertex_circulator);
|
|
CGAL_USING(Halfedge_around_face_circulator);
|
|
typedef std::pair<Halfedge_handle,Halfedge_handle> Halfedge_pair;
|
|
|
|
/*{\Mtypes 3}*/
|
|
typedef Base SM_decorator;
|
|
/*{\Mtypemember the sphere map decorator.}*/
|
|
|
|
typedef typename Base::Kernel Kernel;
|
|
/*{\Mtypemember the geometry kernel.}*/
|
|
|
|
typedef typename Kernel::Sphere_point Sphere_point;
|
|
/*{\Mtypemember the point type of the sphere geometry.}*/
|
|
typedef typename Kernel::Sphere_segment Sphere_segment;
|
|
/*{\Mtypemember the segment type of the sphere geometry.}*/
|
|
typedef typename Kernel::Sphere_circle Sphere_circle;
|
|
/*{\Mtypemember the circle type of the sphere geometry.}*/
|
|
typedef typename SM_decorator::Mark Mark;
|
|
/*{\Mtypemember the mark of sphere map objects.}*/
|
|
|
|
/*{\Mgeneralization SM_decorator}*/
|
|
|
|
<<overlayer methods>>=
|
|
/*{\Mcreation 6}*/
|
|
SM_overlayer(SM_decorator D,
|
|
const Kernel& G = Kernel()) : Base(D), K(G) {}
|
|
/*{\Mcreate |\Mvar| is a decorator object manipulating the map
|
|
decorated by |D|.}*/
|
|
|
|
/*{\Moperations 1.1 1}*/
|
|
template <typename Iterator>
|
|
void subdivide_segments(Iterator start, Iterator end) const;
|
|
template <typename Iterator, typename T>
|
|
void partition_to_halfsphere(Iterator start, Iterator end,
|
|
Seg_list& L, CGAL::Unique_hash_map<Iterator,T>& M, int pos) const;
|
|
template <typename Mark_accessor>
|
|
void merge_halfsphere_maps(Vertex_handle v1, Vertex_handle v2,
|
|
const Mark_accessor& D) const;
|
|
template <typename Mark_accessor>
|
|
void merge_nodes(Halfedge_handle e1, Halfedge_handle e2,
|
|
const Mark_accessor& D) const;
|
|
|
|
template <typename Below_accessor, typename Halfsphere_geometry>
|
|
void create_face_objects(Halfedge_iterator e_start, Halfedge_iterator e_end,
|
|
Vertex_iterator v_start, Vertex_iterator v_end,
|
|
const Below_accessor& D,
|
|
const Halfsphere_geometry& SG) const;
|
|
template <typename Below_accessor>
|
|
void complete_face_support(Vertex_iterator v_start, Vertex_iterator v_end,
|
|
const Below_accessor& D, int pos) const;
|
|
|
|
void dump(std::ostream& os = std::cerr) const
|
|
{ SM_io_parser<Base>::dump(*this,os); }
|
|
|
|
<<overlayer members>>=
|
|
SM_decorator PI[2];
|
|
const Kernel& K;
|
|
|
|
<<overlayer interface methods>>=
|
|
template <typename Forward_iterator>
|
|
void create_from_segments(
|
|
Forward_iterator start, Forward_iterator end) const;
|
|
/*{\Mop produces the sphere map which is the overlay of the
|
|
segments from the iterator range |[start,end)|. \precond
|
|
|Forward_iterator| has value type |Sphere_segment|.}*/
|
|
|
|
template <typename Forward_iterator>
|
|
void create_from_circles(
|
|
Forward_iterator start, Forward_iterator end) const;
|
|
/*{\Mop produces the sphere map which is the overlay of the
|
|
circles from the iterator range |[start,end)|. \precond
|
|
|Forward_iterator| has value type |Sphere_circle|.}*/
|
|
|
|
void create(const Sphere_circle& c) const;
|
|
/*{\Mop produces the sphere map which consists of one loop
|
|
and the two halfspheres incident to it.}*/
|
|
|
|
void subdivide(const Sphere_map& M0, const Sphere_map& M1);
|
|
/*{\Mop constructs the overlay of the sphere maps |M0| and |M1| in
|
|
|M|, where all objects (vertices, halfedges, faces) of |M| are
|
|
\emph{enriched} by the marks of the supporting objects of the two
|
|
input structures: e.g. let |v| be a vertex supported by a node |v0| in
|
|
|M0| and by a face |f1| in |M1| and |D0|, |D1| be decorators of
|
|
type |SM_decorator| on |M0|,|M1|. Then |\Mvar.mark(v,0) = D0.mark(v0)|
|
|
and |\Mvar.mark(v,1) = D1.mark(f1)|.}*/
|
|
|
|
template <typename Selection>
|
|
void select(const Selection& SP) const;
|
|
/*{\Mop sets the marks of all objects according to the selection
|
|
predicate |SP|. |Selection| has to be a function object type with a
|
|
function operator\\
|
|
[[Mark operator()(Mark m0, Mark m1) const]]\\
|
|
For each object |u| of |M| enriched by the marks of the supporting
|
|
objects according to the previous procedure |subdivide|, after this
|
|
operation |\Mvar.mark(u) = SP ( \Mvar.mark(u,0),\Mvar.mark(u,1)
|
|
)|. The additional marks are invalidated afterwards. }*/
|
|
|
|
void simplify() const;
|
|
/*{\Mop simplifies the structure of |M| according to the marks of
|
|
its objects. An edge |e| separating two faces |f1| and |f2| and equal
|
|
marks |mark(e) == mark(f1) == mark(f2)| is removed and the faces are
|
|
unified. An isolated vertex |v| in a face |f| with |mark(v)==mark(f)|
|
|
is removed. A vertex |v| with outdegree two, two collinear out-edges
|
|
|e1|,|e2| and equal marks |mark(v) == mark(e1) == mark(e2)| is removed
|
|
and the edges are unified.}*/
|
|
|
|
@ \end{ignorecodeparts}
|
|
@ \subsection{Overlay calculation of a list of segments}
|
|
|
|
We want to calculate the sphere map $M$ representing the overlay of a
|
|
set of segments. For the overlay action we use the generic segment
|
|
sweep module as presented in the technical report
|
|
\cite{TR:nefimplementation}. In that report we have presented a
|
|
generic traits model |Segment_overlay_traits| for the generic sweep
|
|
framework. To instantiate this model we have to provide the three
|
|
components (input, output, geometry). In this instance the input is an
|
|
iterator pair, the geometry is defined per perfect hemisphere and
|
|
defined in the spherical geometry kernel. Only for the output type we
|
|
have to work a little more. We define a class |SMO_from_segs| below
|
|
triggering the correct update operations on the output sphere map
|
|
during the sweep. |SMO_from_segs| is a template class on global scope
|
|
as several compilers don't like class scope type dependency. The first
|
|
part of |SMO_from_segs| takes care of the sphere map extension by new
|
|
vertices and edges. The second part forwards the treatment of new
|
|
objects like vertices and edges by means of a data accessor.
|
|
|
|
At this point you can take the module\\
|
|
$|generic_sweep< Segment_overlay_traits<SMO_from_segs<... >... >>|$\\
|
|
used below as a black box producing the $1$-skeleton of $M$ with the
|
|
properties described in section \ref{creating face objects}. The
|
|
specification of |Segment_overlay_traits| guarantees the properties
|
|
of $M$ as the following decorator model fits the requirements defined
|
|
there.
|
|
<<SM traits classes for segment overlay>>=
|
|
template <typename Decorator_, typename I>
|
|
struct SMO_from_segs {
|
|
typedef Decorator_ SM_decorator;
|
|
typedef typename Decorator_::Vertex_handle Vertex_handle;
|
|
typedef typename Decorator_::Halfedge_handle Halfedge_handle;
|
|
typedef typename Decorator_::Sphere_point Point;
|
|
typedef typename Decorator_::Sphere_segment Segment;
|
|
typedef CGAL::Unique_hash_map<I,bool> Iterator_map;
|
|
SM_decorator G;
|
|
const Iterator_map& M;
|
|
SMO_from_segs(Decorator_ Gi, const Iterator_map& Mi) : G(Gi),M(Mi) {}
|
|
|
|
<<SMO_from_segs segment overlay model interface>>
|
|
<<SMO_from_segs face creation model interface>>
|
|
<<SMO_from_segs additional interface>>
|
|
}; // SMO_from_segs
|
|
|
|
|
|
@ The creation of new objects is forwarded to the |SM_decorator| object
|
|
|G|. The methods of |G| are members as described in the |SM_decorator|
|
|
concept. The following methods are called during the sweep at its
|
|
event points. The first three methods trigger object creation in $M$.
|
|
We associate a |Halfedge_handle| to each vertex |v| via its generic
|
|
storage slot $|GenPtr& info(v)|$. We use a scheme in analogy to LEDA
|
|
\cite[chapter 13]{ledabook}, where information (in form of a built-in
|
|
or class type) is stored directly in the pointer if it has size not
|
|
larger than the size of a standard word. If it does not fit, the
|
|
pointer is used to reference a newly allocated information object on
|
|
the heap. The scheme is bundled in a class called |geninfo<T>|. For
|
|
more information see that manual page in the appendix.
|
|
<<SMO_from_segs segment overlay model interface>>=
|
|
Vertex_handle new_vertex(const Point& p)
|
|
{ Vertex_handle v = G.new_vertex(p);
|
|
geninfo<Halfedge_handle>::create(G.info(v));
|
|
return v;
|
|
}
|
|
|
|
void link_as_target_and_append(Vertex_handle v, Halfedge_handle e)
|
|
{ G.link_as_target_and_append(v,e); }
|
|
|
|
Halfedge_handle new_halfedge_pair_at_source(Vertex_handle v)
|
|
{ Halfedge_handle e =
|
|
G.new_edge_pair_at_source(v,SM_decorator::BEFORE);
|
|
return e;
|
|
}
|
|
|
|
@ The marks of new objects are depending on the value of |M|.
|
|
<<SMO_from_segs segment overlay model interface>>=
|
|
void supporting_segment(Halfedge_handle e, I it) const
|
|
{ if ( M[it] ) G.mark(e) = true; }
|
|
|
|
void trivial_segment(Vertex_handle v, I it) const
|
|
{ if ( M[it] ) G.mark(v) = true; }
|
|
|
|
void starting_segment(Vertex_handle v, I it) const
|
|
{ if ( M[it] ) G.mark(v) = true; }
|
|
|
|
void passing_segment(Vertex_handle v, I it) const
|
|
{ if ( M[it] ) G.mark(v) = true; }
|
|
|
|
void ending_segment(Vertex_handle v, I it) const
|
|
{ if ( M[it] ) G.mark(v) = true; }
|
|
|
|
void halfedge_below(Vertex_handle v, Halfedge_handle e) const
|
|
{ geninfo<Halfedge_handle>::access(G.info(v)) = e; }
|
|
|
|
@ |SMO_from_segs| fits also the concept for the face creation
|
|
data access defined in section \ref{creating face objects}.
|
|
<<SMO_from_segs face creation model interface>>=
|
|
Halfedge_handle halfedge_below(Vertex_handle v) const
|
|
{ return geninfo<Halfedge_handle>::access(G.info(v)); }
|
|
|
|
@ We finally add a clean-up operation discarding the temporary
|
|
storage.
|
|
<<SMO_from_segs additional interface>>=
|
|
void assert_equal_marks(Vertex_handle v1, Vertex_handle v2) const
|
|
{ CGAL_assertion(G.mark(v1)==G.mark(v2)); }
|
|
void discard_info(Vertex_handle v) const
|
|
{ geninfo<Halfedge_handle>::clear(G.info(v)); }
|
|
|
|
void assert_equal_marks(Halfedge_handle e1, Halfedge_handle e2) const
|
|
{ CGAL_assertion(G.mark(e1)==G.mark(e2)); }
|
|
void transfer_marks(Halfedge_handle e) const
|
|
{ G.unify_tmp_marks(e); }
|
|
void discard_info(Halfedge_handle e) const {}
|
|
|
|
void clear_temporary_vertex_info() const
|
|
{ Vertex_handle v;
|
|
for(v = G.vertices_begin(); v != G.vertices_end(); ++v)
|
|
geninfo<Halfedge_handle>::clear(G.info(v));
|
|
}
|
|
|
|
|
|
@ Now the overlay creation is trivial. We partion the segments per
|
|
perfect hemisphere in a first phase. We then create the overlay per
|
|
perfec hemisphere. Finally we stich the two hemispherical maps
|
|
together at a equator to obtain one complete sphere map.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Forward_iterator>
|
|
void SM_overlayer<Decorator_>::
|
|
create_from_segments(Forward_iterator start, Forward_iterator end) const
|
|
{
|
|
TRACEN("creating from segment iterator range");
|
|
<<partitioning the input segment list per halfsphere>>
|
|
<<sweeping and creating the faces per halfsphere>>
|
|
<<unifying the halfspheres>>
|
|
O.clear_temporary_vertex_info();
|
|
}
|
|
|
|
@ The partitioning is delegated to the subroutine
|
|
|partition_to_halfsphere|. The global list $L$ stores all segments,
|
|
the lists |L_pos| and |L_neg| store geometric object that are
|
|
contained in the corresponding hemisphere. The hash map |From_input|
|
|
marks input objects as we will introduce additional geometric objects
|
|
to bound the hemispheres. The last parameter of
|
|
|partition_to_halfsphere| selects to which of the complementary
|
|
hemispheres the input object have to be restricted.
|
|
<<partitioning the input segment list per halfsphere>>=
|
|
Seg_list L(start,end);
|
|
Unique_hash_map<Seg_iterator,bool> From_input(false);
|
|
Seg_iterator it;
|
|
CGAL_forall_iterators(it,L) From_input[it]=true;
|
|
Seg_list L_pos,L_neg;
|
|
partition_to_halfsphere(L.begin(), L.end(), L_pos, From_input, +1);
|
|
partition_to_halfsphere(L.begin(), L.end(), L_neg, From_input, -1);
|
|
|
|
@ For the sweep we just create an output decorator object |O| working
|
|
on the sphere map maintained by |SM_overlayer| and plug it into the
|
|
segment sweep overlay framework |Segment_overlay_traits|. The
|
|
properties of the output sphere map are defined by that algorithmic
|
|
module. The used geometry is different per hemisphere but predefined
|
|
in the geometric kernel |K| as referenced by |SM_overlayer|.
|
|
|
|
Note that the information |halfedge_below| information collected
|
|
during the sweep is associated to the vertices of the output map.
|
|
Note also that the class |SMO_from_segs| in this chunk fits two
|
|
concepts simultaneously. The corresponding object |Out| triggers the
|
|
output of the sweep and provides the halfedge-below information for
|
|
the face creation in |create_face_objects()|.
|
|
|
|
The two hemispherical sweeps create two hemispherical maps that have a
|
|
common equator spanned by one face cycle of at least four edges. The
|
|
objects that are created per sweep are continuosly maintained in the
|
|
global vertex and edge lists of the sphere map. We store the vertex
|
|
|v| and edge |e| where the two object lists can be partitioned per
|
|
hemisphere.
|
|
<<sweeping and creating the faces per halfsphere>>=
|
|
typedef SMO_from_segs<Self,Seg_iterator> SM_output;
|
|
typedef typename Kernel::Positive_halfsphere_geometry PH_geometry;
|
|
typedef CGAL::Segment_overlay_traits<
|
|
Seg_iterator, SM_output, PH_geometry> PHS_traits;
|
|
typedef CGAL::generic_sweep<PHS_traits> Positive_halfsphere_sweep;
|
|
|
|
typedef typename Kernel::Negative_halfsphere_geometry NH_geometry;
|
|
typedef CGAL::Segment_overlay_traits<
|
|
Seg_iterator, SM_output, NH_geometry> NHS_traits;
|
|
typedef CGAL::generic_sweep<NHS_traits> Negative_halfsphere_sweep;
|
|
|
|
Vertex_iterator v;
|
|
Halfedge_iterator e;
|
|
SM_output O(*this,From_input);
|
|
|
|
typedef typename PHS_traits::INPUT Input_range;
|
|
Positive_halfsphere_sweep SP(
|
|
Input_range(L_pos.begin(),L_pos.end()),O,
|
|
K.get_positive_halfsphere_geometry());
|
|
SP.sweep();
|
|
//TRACEN("POS SWEEP\n"<<(dump(std::cerr),""));
|
|
v=--vertices_end(); e=--halfedges_end();
|
|
|
|
Negative_halfsphere_sweep SM(
|
|
Input_range(L_neg.begin(),L_neg.end()),O,
|
|
K.get_negative_halfsphere_geometry());
|
|
SM.sweep();
|
|
//TRACEN("NEG SWEEP\n"<<(dump(std::cerr),""));
|
|
++v; ++e;
|
|
// now two CCs of sphere graph are calculated
|
|
// v = first vertex of CC in negative x-sphere
|
|
// e = first edge of CC in negative x-sphere
|
|
|
|
@ We now create the face objects per hemisphere.
|
|
<<sweeping and creating the faces per halfsphere>>=
|
|
create_face_objects(halfedges_begin(), e, vertices_begin(), v, O,
|
|
K.get_positive_halfsphere_geometry());
|
|
create_face_objects(e, halfedges_end(), v, vertices_end(), O,
|
|
K.get_negative_halfsphere_geometry());
|
|
|
|
@ Still missing are the sphere circles that support the edges of the
|
|
sphere map. Note that due to our restriction to perfect hemispheres
|
|
the circles can be easily constructed per edge.
|
|
<<sweeping and creating the faces per halfsphere>>=
|
|
Halfedge_iterator u;
|
|
CGAL_forall_edges(u,*this) {
|
|
Sphere_segment s(point(source(u)),point(target(u)));
|
|
circle(u) = s.sphere_circle();
|
|
circle(twin(u)) = s.sphere_circle().opposite();
|
|
}
|
|
|
|
@ \subsection{Overlay calculation of a list of circles}
|
|
|
|
We now modify the above coding scheme to calculate the sphere map
|
|
$M$ representing the overlay of a set of great circles. We basically
|
|
only recode the first phase.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Forward_iterator>
|
|
void SM_overlayer<Decorator_>::
|
|
create_from_circles(Forward_iterator start, Forward_iterator end) const
|
|
{
|
|
TRACEN("creating from circle iterator range");
|
|
<<partitioning the input circle list per halfsphere>>
|
|
<<sweeping and creating the faces per halfsphere>>
|
|
<<unifying the halfspheres>>
|
|
O.clear_temporary_vertex_info();
|
|
}
|
|
|
|
@ We just split great circles to halfcircles that are contained in
|
|
each of the two hemispheres. Note that this realization contains some
|
|
redundancy as the |partition_to_halfsphere| again analyzes the
|
|
geometry of each segment. One could find a more efficient approach.
|
|
<<partitioning the input circle list per halfsphere>>=
|
|
Seg_list L;
|
|
Unique_hash_map<Seg_iterator,bool> From_input(false);
|
|
for ( ; start != end; ++start ) {
|
|
std::pair<Sphere_segment,Sphere_segment> spair =
|
|
start->split_at_xy_plane();
|
|
L.push_back(spair.first); L.push_back(spair.second);
|
|
}
|
|
Seg_iterator it;
|
|
CGAL_forall_iterators(it,L) From_input[it]=true;
|
|
Seg_list L_pos,L_neg;
|
|
partition_to_halfsphere(L.begin(), L.end(), L_pos, From_input, +1);
|
|
partition_to_halfsphere(L.begin(), L.end(), L_neg, From_input, -1);
|
|
|
|
|
|
@ \subsection{Create a sphere map from a great circle}
|
|
|
|
We create a map consisting of a single loop and two faces representing
|
|
two complementary half-spheres.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
void SM_overlayer<Decorator_>::
|
|
create(const Sphere_circle& c) const
|
|
{ Halfloop_handle l1 = new_loop_pair();
|
|
Halfloop_handle l2 = twin(l1);
|
|
circle(l1) = c; circle(l2) = c.opposite();
|
|
Face_handle f1 = new_face();
|
|
Face_handle f2 = new_face();
|
|
link_as_loop(l1,f1);
|
|
link_as_loop(l2,f2);
|
|
}
|
|
|
|
@ \subsection{Overlay calculation of two sphere maps}
|
|
\label{Overlay calculation of two sphere maps}
|
|
|
|
We calculate the overlay $M$ of two sphere maps $M_0$ and $M_1$.
|
|
Both input structures are two correctly defined sphere maps including
|
|
topology, geometry, markers. In the following we use the index $i=0,1$
|
|
showing a reference to $M_i = (V_i,E_i,L_i,F_i)$, unindexed variables
|
|
refer to $M$.
|
|
|
|
The $1$-skeleta of the two maps $M_0$ and $M_1$ subdivide the edges,
|
|
loops, and faces of the complementary structures into smaller
|
|
units. This means vertices, edges, and loops of $M_i$ can split edges
|
|
and loops of $M_{1-i}$ and face cycles of $M_i$ subdivide faces of
|
|
$M_{1-i}$. The 1-skeleton $G$ of $M$ is defined by the overlay of the
|
|
embedding of the 1-skeleton of $M_0$ and $M_1$ (Take a trivial segment
|
|
for each vertex and a segment for each edge and a circle for a loop
|
|
and use the overlay definition of a set of segments
|
|
above). Additionally we require that $M$ has the correct order in each
|
|
adjacency list such that it is order-preserving regarding the
|
|
embedding of the vertices.
|
|
|
|
Finally the faces of $M$ refer to the maximal connected open point
|
|
sets of the spherical subdivision implied by the embedding of $G$. The
|
|
construction of the faces $F$ from $G$ is described in section
|
|
\ref{creating face objects}. Each object $u$ from the output tuple
|
|
$(V,E,L,F)$ has a \emph{supporting} object $u_i, i=0,1$ in each of the
|
|
two input structures. Imagine the two maps to be transparent balls,
|
|
stacked one on top of the other. Then each point of the sphere is
|
|
covered by an object from each of the input structures. We analyse the
|
|
support relationship from input to output in order to transfer the
|
|
attributes from $u_i$ to $u$.
|
|
|
|
According to our specification each object $u_i$ of a sphere map
|
|
carries an associated information $|mark|(u_i)$. Thus we associate
|
|
this information to the output object $u$ by $|mark|(u,i)$. This two
|
|
tuple of information can then be processed by some combining operation
|
|
to a single value |mark(u)| later on.
|
|
|
|
We fix the following properties for our input structures $M_i$. Both
|
|
sphere maps $(V_i, E_i, L_i, F_i)$ consist of vertices, edges, loops,
|
|
and faces whose topology is accessible by our sphere map
|
|
interface. Additionally each such object $u$ carries an information
|
|
item |mark(u)|. The sphere maps have an \emph{order-preserving}
|
|
embedding. Actually we do not use this property of the input sphere
|
|
maps at this point but it's a general invariant of our sphere map
|
|
structures that makes some intermediate actions more efficient.
|
|
|
|
The overlay process consist of several phases. We determine the
|
|
segments that are the embedding of the edges and partition these
|
|
segments into two hemispheres. We also subdivide any loop into two
|
|
halfcircle segments. We then proceed per halfsphere: We produce the
|
|
1-skeleton by a spherical sweep action. Afterwards we create the face
|
|
objects. Then we analyse the support relationship and transfer the
|
|
marks of the input objects to the output objects. Finally we unify the
|
|
two maps along the equator that is the common boundary of both
|
|
hemispheres.
|
|
|
|
We use our generic segment overlay framework to calculate the overlay
|
|
of a set of sphere segments $S$. The set $S$ consists of all segments
|
|
that are the embedding of edges in $E_i$ and additionally trivial
|
|
segments representing all isolated vertices in $V_i$ and two
|
|
half-circle segments per loop. The output structure $M' = (V', E')$
|
|
of the sweep phase is just the 1-skeleton of the output sphere map
|
|
$M$\footnote{There are no loops in the hemispherically subdivided
|
|
represention of the skeleton $M'$.}. But of course including an
|
|
order-preserving embedding. The objects of the $1$-skeleton carry
|
|
additional structural information:
|
|
|
|
\begin{enumerate}
|
|
\renewcommand{\labelenumi}{I\theenumi.}
|
|
\item Each vertex $v$ in $V'$ knows a halfedge $e \in E' : e =
|
|
|halfedge_below(v)|$ which is determined by the property that a
|
|
vertical ray shoot from |v| along the negative $y$-axis hits |e|
|
|
first. Degeneracies are broken with an implicit perturbation scheme:
|
|
during the ray shooting all edges include their source vertex.
|
|
\label{halfedge below}
|
|
\item For each object $u \in V' \cup E'$ there's a mapping to the
|
|
supporting 1-skeleton objects from the input structures. The support
|
|
information is incomplete with respect to face support.
|
|
\label{skeleton support}
|
|
\end{enumerate}
|
|
|
|
The next phase after the sweep has to complete the sphere map $M$. We
|
|
basically have to create the face objects and construct their
|
|
incidence structure. The face creation is done as presented in section
|
|
\ref{creating face objects} and uses only I\ref{halfedge below}.
|
|
Afterwards the transfer of marks uses the embedding of the vertex list
|
|
of $M$ and the additional information I\ref{halfedge below} and
|
|
I\ref{skeleton support} to define $|mark|(u,i)$ for all objects $u$ in
|
|
$p$. Finally the two hemispherically separate sphere map structure are
|
|
then unified along an equator.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
void SM_overlayer<Decorator_>::
|
|
subdivide(const Sphere_map& M0, const Sphere_map& M1)
|
|
{
|
|
PI[0] = SM_decorator(const_cast<Sphere_map&>(M0));
|
|
PI[1] = SM_decorator(const_cast<Sphere_map&>(M1));
|
|
<<filling the input segment lists per halfsphere>>
|
|
<<sweeping the segments and creating the faces per halfsphere>>
|
|
<<transfering the marks of supporting objects per halfsphere>>
|
|
<<unifying the halfspheres>>
|
|
}
|
|
|
|
|
|
@ \subsection*{Temporary information associated to objects}
|
|
|
|
The usage of generic programming modules implies that
|
|
we have to provide temporary information containers for
|
|
the accumulated information.
|
|
|
|
First we need a container to bind segments used in the sweep phase to
|
|
objects of the input structures. Non-trivial segments know an edge or
|
|
loop, trivial segments know a vertex, and all segments know the index
|
|
of the input structure which contains the object.
|
|
<<overlayer helping methods>>=
|
|
struct Seg_info { // to transport information from input to output
|
|
Object_handle _o; int _from;
|
|
|
|
Seg_info() : _o(), _from(-1) {}
|
|
Seg_info(Vertex_handle v, int i)
|
|
{ _o=Object_handle(v); _from=i; }
|
|
Seg_info(Halfedge_handle e, int i)
|
|
{ _o=Object_handle(e); _from=i; }
|
|
Seg_info(Halfloop_handle l, int i)
|
|
{ _o=Object_handle(l); _from=i; }
|
|
Seg_info(const Seg_info& si)
|
|
{ _o=si._o; _from=si._from; }
|
|
Seg_info& operator=(const Seg_info& si)
|
|
{ _o=si._o; _from=si._from; return *this; }
|
|
LEDA_MEMORY(Seg_info)
|
|
};
|
|
|
|
typedef std::list<Sphere_segment> Seg_list;
|
|
typedef typename Seg_list::iterator Seg_iterator;
|
|
typedef std::pair<Seg_iterator,Seg_iterator> Seg_it_pair;
|
|
typedef std::pair<Sphere_segment,Sphere_segment> Seg_pair;
|
|
typedef CGAL::Unique_hash_map<Seg_iterator,Seg_info> Seg_map;
|
|
|
|
@ In the first phase we fill the segment input list with a non-trivial
|
|
segment underlying each edge and with a trivial segment for each
|
|
isolated vertex of the two input structures. If the input map has a
|
|
loop we take the underlying great circle split it into two segments
|
|
and proceed as in case of edges. Additionally we store hashed links
|
|
from the iterators to the edges/loops/vertices to store where they
|
|
come from.
|
|
<<filling the input segment lists per halfsphere>>=
|
|
Seg_list L;
|
|
Seg_map From;
|
|
for (int i=0; i<2; ++i) {
|
|
Vertex_iterator v;
|
|
CGAL_forall_vertices(v,PI[i]) {
|
|
if ( !PI[i].is_isolated(v) ) continue;
|
|
L.push_back(trivial_segment(PI[i],v));
|
|
From[--L.end()] = Seg_info(v,i);
|
|
}
|
|
Halfedge_iterator e;
|
|
CGAL_forall_edges(e,PI[i]) {
|
|
if ( source(e) == target(e) ) {
|
|
Seg_pair p = two_segments(PI[i],e);
|
|
L.push_back(p.first);
|
|
L.push_back(p.second);
|
|
From[--L.end()] = From[--(--L.end())] = Seg_info(e,i);
|
|
} else {
|
|
L.push_back(segment(PI[i],e));
|
|
From[--L.end()] = Seg_info(e,i);
|
|
}
|
|
}
|
|
if ( PI[i].has_loop() ) {
|
|
Seg_pair p = two_segments(PI[i],PI[i].halfloop());
|
|
L.push_back(p.first);
|
|
L.push_back(p.second);
|
|
From[--L.end()] = From[--(--L.end())] =
|
|
Seg_info(PI[i].halfloop(),i);
|
|
}
|
|
}
|
|
|
|
@ We split segments into parts that are fully contained in the closed
|
|
hemispheres above or below the xy-plane. By taking the closed
|
|
hemispheres we ensure that the topology on the boundary is the same
|
|
and unifying both halfsphere map structures is easy. Note that such
|
|
closed hemispheres contradict carry geometric ambiguity for segments
|
|
that are defined by two opposite points on the equator.
|
|
<<filling the input segment lists per halfsphere>>=
|
|
Seg_list L_pos,L_neg;
|
|
partition_to_halfsphere(L.begin(), L.end(), L_pos, From, +1);
|
|
partition_to_halfsphere(L.begin(), L.end(), L_neg, From, -1);
|
|
|
|
@ Conside the standard Cartesian $xyz$-coordinate system. The points
|
|
where the $y$-axis pierces $\ST$ are denoted $y^-$ and $y^+$. The
|
|
great circle that is the intersection of $\ST$ and the $xy$-plane is
|
|
called \emph{equator}. Our hemispheres are bounded by the
|
|
xy-coordinate plane ($z=0$). The \emph{positive perfect hemisphere}
|
|
is defined to be the $z$-positive open hemisphere\footnote{$S_2 \cap
|
|
\{z>0\}$} unified with the open $x$-negative half-equator and
|
|
$y^-$. The \emph{negative perfect hemisphere} is the complementary
|
|
set.
|
|
|
|
We start from a set of spherical segments. We determine the connected
|
|
subcomponents that are part of the closed half-spaces above and below
|
|
the xy-plane depending on the |pos| parameter. Segments that are
|
|
totally part of the equator are handled differently. We split such
|
|
segments that additionally contain the y-axis in their relative
|
|
interior at those points.
|
|
|
|
Our perfect hemispheres avoid ambiguities with respect to the
|
|
definition of spherical segments. A semicircle that has its endpoints
|
|
on the equator is not fully part of a perfect hemisphere. Therefore we
|
|
algorithmically consider points on the $x$-positive half-equator as
|
|
slightly perturbed up. To generally avoid problems with semicircles we
|
|
split such segments again at their midpoint. This increases the
|
|
complexety of the sphere map structure by a constant factor but makes
|
|
support determination easier, as for each sphere segment we can find
|
|
an endpoint in the interior of the perfect hemisphere.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Iterator, typename T>
|
|
void SM_overlayer<Decorator_>::
|
|
partition_to_halfsphere(Iterator start, Iterator beyond, Seg_list& L,
|
|
CGAL::Unique_hash_map<Iterator,T>& M, int pos) const
|
|
{ TRACEN("partition_to_halfsphere ");
|
|
CGAL_assertion(pos!=0);
|
|
Sphere_segment s1,s2;
|
|
Sphere_circle xycircle(0,0,pos);
|
|
while ( start != beyond ) {
|
|
int i = start->intersection(xycircle,s1,s2);
|
|
if (i>1) { L.push_back(s2); M[--L.end()] = M[start]; }
|
|
if (i>0) { L.push_back(s1); M[--L.end()] = M[start]; }
|
|
++start;
|
|
}
|
|
// now all segments are split into hemispheres
|
|
// we still have to:
|
|
// - split segments containing our special poles y^-, y^+
|
|
// - split halfcircles
|
|
// - add four equator segments
|
|
Sphere_point S(0,-1,0),N(0,1,0);
|
|
Sphere_circle yzcircle(1,0,0);
|
|
typename Seg_list::iterator it, itl;
|
|
|
|
CGAL_forall_iterators(it,L) { TRACEN(" "<<*it);
|
|
if ( equal_as_sets(it->sphere_circle(),xycircle) ) {
|
|
TRACEN(" splitting xy seg "<<*it);
|
|
int n1 = it->intersection(yzcircle,s1,s2);
|
|
if (n1 > 1 && !s2.is_degenerate())
|
|
{ M[ L.insert(it,s2) ] = M[it]; }
|
|
if (n1 > 0 && !s1.is_degenerate())
|
|
{ M[ L.insert(it,s1) ] = M[it]; }
|
|
int n2 = it->intersection(yzcircle.opposite(),s1,s2);
|
|
if (n2 > 1 && !s2.is_degenerate())
|
|
{ M[ L.insert(it,s2) ] = M[it]; }
|
|
if (n2 > 0 && !s1.is_degenerate())
|
|
{ M[ L.insert(it,s1) ] = M[it]; }
|
|
itl = it; --it; L.erase(itl); M[itl] = T();
|
|
// at least one item was appended
|
|
}
|
|
}
|
|
CGAL_forall_iterators(it,L) {
|
|
if ( it->is_halfcircle() ) {
|
|
TRACEN(" splitting halfcircle "<<*it);
|
|
Sphere_segment s1,s2;
|
|
it->split_halfcircle(s1,s2);
|
|
*it = s2;
|
|
M[ L.insert(it,s1) ] = M[it];
|
|
}
|
|
}
|
|
// append 4 xy-equator segments:
|
|
Sphere_segment sp(S,N,xycircle);
|
|
Sphere_segment sm(S,N,xycircle.opposite());
|
|
Sphere_segment s[4];
|
|
sp.split_halfcircle(s[0],s[1]);
|
|
sm.split_halfcircle(s[2],s[3]);
|
|
L.insert(L.end(),s,s+4);
|
|
}
|
|
|
|
@ For the sweep phase we need to associate some additional information
|
|
attributes to the objects of the output sphere map. We use a trick via
|
|
generic pointers |GenPtr| (equals |void*|). Each object |u| of |M| has
|
|
such a slot accessible via $|GenPtr& info(u)|$. We use the pointer to
|
|
reference an object storing the temporary information until the
|
|
postprocessing does not need it anymore. We have to ensure that we do
|
|
not mess around with the memory. The temporary information is
|
|
collected during the sweep operation. After the selection operation
|
|
the temporary information is discarded. To avoid superflous
|
|
indirection for the temporary information we use a scheme in analogy
|
|
to LEDA \cite[chapter 13]{ledabook}, where information (in form of a
|
|
built-in or class type) is stored directly in the pointer if it has
|
|
size not larger than the size of a standard word. If it does not fit,
|
|
the pointer is used to reference a newly allocated information object
|
|
on the heap. This scheme leaves the vertex, halfedge, and face objects
|
|
minimal in the sense that we only have one additional pointer in each
|
|
of them. The scheme is bundled in a class called |geninfo<T>|. For
|
|
more information see that manual page in the appendix.
|
|
|
|
We associate the following class to a vertex $v$. The methods below
|
|
are added to the interface of |SM_overlayer|. |vertex_info| can store
|
|
the possible supporting skeleton objects in a generic handle |o_supp|,
|
|
marks |m| corresponding to the two supporting objects and a halfedge
|
|
|e_below| of the output structure that is vertically below $v$.
|
|
<<overlayer helping methods>>=
|
|
struct vertex_info {
|
|
Mark m[2];
|
|
Object_handle o_supp[2];
|
|
Halfedge_handle e_below;
|
|
vertex_info()
|
|
{ o_supp[0]=o_supp[1]=Object_handle(); }
|
|
LEDA_MEMORY(vertex_info)
|
|
};
|
|
|
|
void assoc_info(Vertex_handle v) const
|
|
{ geninfo<vertex_info>::create(info(v)); }
|
|
|
|
void discard_info(Vertex_handle v) const
|
|
{ geninfo<vertex_info>::clear(info(v)); }
|
|
|
|
vertex_info& ginfo(Vertex_handle v) const
|
|
{ return geninfo<vertex_info>::access(info(v)); }
|
|
|
|
Mark& mark(Vertex_handle v, int i) const
|
|
{ return ginfo(v).m[i]; }
|
|
|
|
Object_handle& supp_object(Vertex_handle v, int i) const
|
|
{ return ginfo(v).o_supp[i]; }
|
|
|
|
Halfedge_handle& halfedge_below(Vertex_handle v) const
|
|
{ return ginfo(v).e_below; }
|
|
|
|
@ For each halfedge we store the following class. Again we provide its
|
|
interface in |SM_overlayer|. The |edge_info| objects store information
|
|
common to both halfedge twins (information for the uedge) and
|
|
information only concerning one halfedge. In the first case we store
|
|
the information only in the halfedge determined by the smaller memory
|
|
address which makes access unique. We provide storage |o_supp| for the
|
|
possible two input edges or input loops supporting an output edge, the
|
|
marks |m| of the two input objects supporting an output edge, and
|
|
temporary storage |mf| for the marks of the two input faces supporting
|
|
points in a small neighborhood of the edge. The boolean flag |forw|
|
|
just caches the geometric property if an edge is forward oriented (the
|
|
source is lexicographically smaller than the target).
|
|
<<overlayer helping methods>>=
|
|
struct edge_info {
|
|
Mark m[2];
|
|
Mark mf[2];
|
|
Object_handle o_supp[2];
|
|
bool forw;
|
|
edge_info()
|
|
{ m[0]=m[1]=mf[0]=mf[1]=Mark();
|
|
o_supp[0]=o_supp[1]=Object_handle();
|
|
forw=false; }
|
|
LEDA_MEMORY(edge_info)
|
|
};
|
|
|
|
void assoc_info(Halfedge_handle e) const
|
|
{ geninfo<edge_info>::create(info(e));
|
|
geninfo<edge_info>::create(info(twin(e))); }
|
|
|
|
void discard_info(Halfedge_handle e) const
|
|
{ geninfo<edge_info>::clear(info(e));
|
|
geninfo<edge_info>::clear(info(twin(e))); }
|
|
|
|
edge_info& ginfo(Halfedge_handle e) const
|
|
{ return geninfo<edge_info>::access(info(e)); }
|
|
|
|
Mark& mark(Halfedge_handle e, int i) const
|
|
// uedge information we store in the smaller one
|
|
{ if (&*e < &*(twin(e))) return ginfo(e).m[i];
|
|
else return ginfo(twin(e)).m[i]; }
|
|
|
|
Object_handle& supp_object(Halfedge_handle e, int i) const
|
|
// uedge information we store in the smaller one
|
|
{ if (&*e < &*(twin(e))) return ginfo(e).o_supp[i];
|
|
else return ginfo(twin(e)).o_supp[i]; }
|
|
|
|
Mark& incident_mark(Halfedge_handle e, int i) const
|
|
// biedge information we store in the edge
|
|
{ return ginfo(e).mf[i]; }
|
|
|
|
bool& is_forward(Halfedge_handle e) const
|
|
// biedge information we store in the edge
|
|
{ return ginfo(e).forw; }
|
|
|
|
@ A face just obtains two mark slots |m|.
|
|
<<overlayer helping methods>>=
|
|
struct face_info {
|
|
Mark m[2];
|
|
face_info() { m[0]=m[1]=Mark(); }
|
|
LEDA_MEMORY(face_info)
|
|
};
|
|
|
|
void assoc_info(Face_handle f) const
|
|
{ geninfo<face_info>::create(info(f)); }
|
|
|
|
void discard_info(Face_handle f) const
|
|
{ geninfo<face_info>::clear(info(f)); }
|
|
|
|
face_info& ginfo(Face_handle f) const
|
|
{ return geninfo<face_info>::access(info(f)); }
|
|
|
|
Mark& mark(Face_handle f, int i) const
|
|
{ return ginfo(f).m[i]; }
|
|
|
|
|
|
@ \subsection*{The sweep instantiation}
|
|
|
|
We have to provide the three components (input, output, geometry)
|
|
necessary to instantiate the traits model |Segment_\-overlay_\-traits|
|
|
for our generic sweep framework. The input is an iterator pair, the
|
|
geometry is forwarded from the current class scope. Only for the
|
|
output type we have to work a little more. We define a class
|
|
|SMO_from_sm| below which allows us to track the support relationship
|
|
from input objects (segments handled via iterators) to the output
|
|
objects (vertices and halfedges) via the call-back methods triggered
|
|
during the sweep. Please refer to the description of
|
|
|Segment_\-overlay_\-traits| in \cite{TR:nefimplementation}.
|
|
|
|
The member methods of |SMO_from_sm| fit the output concept
|
|
requirements of |Segment_\-overlay_\-traits|. The functionality is
|
|
such that the skeleton is created and the support information is
|
|
associated to the newly created objects. |SMO_from_sm| is a class
|
|
template on the global implementation scope as the usage of local
|
|
class types (within the scope of |SM_overlayer|) is not allowed by
|
|
some current C++ compilers.
|
|
|
|
Note that we forward a reference to our hash map |From| to the output
|
|
decorator object. Thus we can update the support linkage from output
|
|
skeleton objects via iterators to input objects on the fly when the
|
|
sweep frameworks calls the corresponding methods of |SMO_from_sm|.
|
|
<<SM traits classes for segment overlay>>=
|
|
template <typename Decorator_, typename IT, typename INFO>
|
|
struct SMO_from_sm {
|
|
<<importing decorators, handles, and point type from Decorator_>>
|
|
SM_overlayer G;
|
|
SM_decorator* pGI;
|
|
CGAL::Unique_hash_map<IT,INFO>& M;
|
|
SMO_from_sm(SM_overlayer Gi,
|
|
SM_decorator* pGIi,
|
|
CGAL::Unique_hash_map<IT,INFO>& Mi) :
|
|
G(Gi), pGI(pGIi), M(Mi) {}
|
|
|
|
<<SMO_from_sm topological updates>>
|
|
<<SMO_from_sm vertical ray shoot knowledge>>
|
|
<<SMO_from_sm support knowledge>>
|
|
<<SMO_from_sm face creation model interface>>
|
|
<<SMO_from_sm additional interface>>
|
|
|
|
}; // SMO_from_sm
|
|
|
|
@ \begin{ignorecodeparts}
|
|
<<importing decorators, handles, and point type from Decorator_>>=
|
|
typedef Decorator_ SM_overlayer;
|
|
typedef typename Decorator_::SM_decorator SM_decorator;
|
|
typedef typename SM_decorator::Vertex_handle Vertex_handle;
|
|
typedef typename SM_decorator::Halfedge_handle Halfedge_handle;
|
|
typedef typename SM_decorator::Sphere_point Point;
|
|
typedef typename SM_decorator::Sphere_segment Segment;
|
|
@ \end{ignorecodeparts}
|
|
<<SMO_from_sm additional interface>>=
|
|
void assert_equal_marks(Vertex_handle v1, Vertex_handle v2) const
|
|
{ TRACEV(G.mark(v1,0));TRACEV(G.mark(v1,1));
|
|
TRACEV(G.mark(v2,0));TRACEV(G.mark(v2,1));
|
|
CGAL_assertion(G.mark(v1,0)==G.mark(v2,0)&&
|
|
G.mark(v1,1)==G.mark(v2,1)); }
|
|
void discard_info(Vertex_handle v) const
|
|
{ G.discard_info(v); }
|
|
|
|
void assert_equal_marks(Halfedge_handle e1, Halfedge_handle e2) const
|
|
{ CGAL_assertion(G.mark(e1,0)==G.mark(e2,0) &&
|
|
G.mark(e1,1)==G.mark(e2,1)); }
|
|
void transfer_marks(Halfedge_handle e) const
|
|
{ Halfedge_handle et = G.twin(e);
|
|
if (&*e < &*et) std::swap(e,et);
|
|
for(int i=0; i<2; ++i) G.ginfo(e).m[i] = G.ginfo(et).m[i];
|
|
}
|
|
|
|
void discard_info(Halfedge_handle e) const
|
|
{ G.discard_info(e); }
|
|
|
|
|
|
|
|
@ New vertices and halfedges are created in the sphere map via a call
|
|
to corresponding creation methods of the decorator. Note that we
|
|
initialize a temporary storage slot in the objects by a call to
|
|
|assoc_info|.
|
|
<<SMO_from_sm topological updates>>=
|
|
Vertex_handle new_vertex(const Point& p) const
|
|
{ Vertex_handle v = G.new_vertex(p);
|
|
G.assoc_info(v);
|
|
return v;
|
|
}
|
|
|
|
void link_as_target_and_append(Vertex_handle v, Halfedge_handle e) const
|
|
{ G.link_as_target_and_append(v,e); }
|
|
|
|
Halfedge_handle new_halfedge_pair_at_source(Vertex_handle v) const
|
|
{ Halfedge_handle e =
|
|
G.new_edge_pair_at_source(v,SM_decorator::BEFORE);
|
|
G.assoc_info(e);
|
|
return e;
|
|
}
|
|
|
|
@ The halfedge vertically below a vertex is stored in a slot of the
|
|
temporarily associated information container of type
|
|
|vertex_info|. Access is done via the |SM_overlayer| operation
|
|
|halfedge_below()|.
|
|
<<SMO_from_sm vertical ray shoot knowledge>>=
|
|
void halfedge_below(Vertex_handle v, Halfedge_handle e) const
|
|
{ G.halfedge_below(v) = e; }
|
|
|
|
@ For a new halfedge |e| we get to know all segments |*it|
|
|
that support it. We store the information via the decorator.
|
|
There can be at most two input segments supporting an edge.
|
|
<<SMO_from_sm support knowledge>>=
|
|
void supporting_segment(Halfedge_handle e, IT it) const
|
|
{ INFO& si = M[it];
|
|
G.is_forward(e) = true;
|
|
if ( si._from == -1 ) return; // equatorial segment
|
|
G.supp_object(e,si._from) = si._o;
|
|
TRACEN(" supporting "<<si._from<<" "<<*it);
|
|
}
|
|
|
|
|
|
@ For a vertex |v| we get to know support information. There are two
|
|
possibilities: |v| can be the endpoint of the object transported via
|
|
|it|, or |v| can be part of its relative interior. We sort the
|
|
situation out when we create the marks. Here we only forward the
|
|
support information.
|
|
<<SMO_from_sm support knowledge>>=
|
|
void trivial_segment(Vertex_handle v, IT it) const
|
|
{ INFO& si = M[it];
|
|
CGAL_assertion( si._o != NULL );
|
|
G.supp_object(v,si._from) = si._o;
|
|
}
|
|
|
|
void starting_segment(Vertex_handle v, IT it) const
|
|
{ INFO& si = M[it];
|
|
if ( si._from == -1 ) return;
|
|
G.supp_object(v,si._from) = si._o;
|
|
}
|
|
|
|
void ending_segment(Vertex_handle v, IT it) const
|
|
{ INFO& si = M[it];
|
|
if ( si._from == -1 ) return;
|
|
G.supp_object(v,si._from) = si._o;
|
|
}
|
|
|
|
void passing_segment(Vertex_handle v, IT it) const
|
|
{ INFO& si = M[it];
|
|
if ( si._from == -1 ) return;
|
|
G.supp_object(v,si._from) = si._o;
|
|
}
|
|
|
|
@ |SMO_from_sm| also provides data access in the face creation
|
|
phase. Therefore this operation that redirects the access to
|
|
|SM_overlayer| object |G|.
|
|
<<SMO_from_sm face creation model interface>>=
|
|
Halfedge_handle halfedge_below(Vertex_handle v) const
|
|
{ return G.halfedge_below(v); }
|
|
|
|
@ Now executing the sweep is a trivial plugging of types into the
|
|
generic sweep framework, a creation of the sweep object with input,
|
|
output and geometry references, and a final execution the sweep. We do
|
|
this per hemisphere.
|
|
<<sweeping the segments and creating the faces per halfsphere>>=
|
|
typedef SMO_from_sm<Self,Seg_iterator,Seg_info> SM_output;
|
|
typedef typename Kernel::Positive_halfsphere_geometry PH_geometry;
|
|
typedef CGAL::Segment_overlay_traits<
|
|
Seg_iterator, SM_output, PH_geometry> PHS_traits;
|
|
typedef CGAL::generic_sweep<PHS_traits> Positive_halfsphere_sweep;
|
|
|
|
typedef typename Kernel::Negative_halfsphere_geometry NH_geometry;
|
|
typedef CGAL::Segment_overlay_traits<
|
|
Seg_iterator, SM_output, NH_geometry> NHS_traits;
|
|
typedef CGAL::generic_sweep<NHS_traits> Negative_halfsphere_sweep;
|
|
|
|
Vertex_handle v;
|
|
Halfedge_handle e;
|
|
SM_output O(*this,PI,From);
|
|
|
|
typedef typename PHS_traits::INPUT Input_range;
|
|
Positive_halfsphere_sweep SP(
|
|
Input_range(L_pos.begin(),L_pos.end()),O,
|
|
K.get_positive_halfsphere_geometry());
|
|
SP.sweep();
|
|
v=--vertices_end(); e=--halfedges_end();
|
|
|
|
Negative_halfsphere_sweep SM(
|
|
Input_range(L_neg.begin(),L_neg.end()),O,
|
|
K.get_negative_halfsphere_geometry());
|
|
SM.sweep();
|
|
++v; ++e;
|
|
// now two CCs of sphere graph are calculated
|
|
// v = first vertex of CC in negative x-sphere
|
|
// e = first edge of CC in negative x-sphere
|
|
|
|
create_face_objects(halfedges_begin(), e, vertices_begin(), v, O,
|
|
PH_geometry());
|
|
create_face_objects(e, halfedges_end(), v, vertices_end(), O,
|
|
NH_geometry());
|
|
|
|
Halfedge_iterator u;
|
|
CGAL_forall_edges(u,*this) {
|
|
Sphere_segment s(point(source(u)),point(target(u)));
|
|
circle(u) = s.sphere_circle();
|
|
circle(twin(u)) = s.sphere_circle().opposite();
|
|
}
|
|
|
|
|
|
|
|
@ \subsection{Creating face objects}\label{creating face objects}
|
|
|
|
Input to this section is the 1-skeleton of a sphere map $M' = (V',E')$
|
|
whose embedding is \emph{order-preserving}.
|
|
|
|
The objective of this section is to create face objects. The correct
|
|
output structure $M = (V,E,L,F)$ of this section is a sphere map with
|
|
the additional property that there are face objects $f$ in $F$
|
|
corresponding to maximal connected point sets which are a result of
|
|
the partitioning of the sphere by the $1$-skeleton $M'$. All faces are
|
|
defined via their bounding face cycles and each face object has links
|
|
to them (a representing halfedge for each non-trivial face cycle and
|
|
all isolated vertices in their interior).
|
|
|
|
To create the face objects we need to know for two face cycles |fc1|
|
|
and |fc2| if we can connect them by a path of the sphere which does
|
|
not cross any other face cycle.
|
|
|
|
We adapt an idea from \cite{mmmo:CG}. The path connectivity making
|
|
disjoint face cycles bounding the same face, can be modelled by a
|
|
visibility graph of the minimal vertices\footnote{minimal with respect
|
|
to the lexicographic order of the point coordinates of their
|
|
embedding} of each face cycle. We create faces and assign face cycles
|
|
based on this property and transfer $M'$ to $M$ thereby.
|
|
|
|
Let $\cal{F}$ be a set of face cycles of the sphere map skeleton. For
|
|
each face cycle |c| let |MinimalHalfedge[c]| be the halfedge |e|
|
|
whose target vertex has lexicographic minimal coordinates. Let
|
|
|FaceCycle[e]| be the face cycle containing |e|. We examine the
|
|
implicit following implicit graph $H$. Each face cycle of $M'$ is a
|
|
node of $H$. Let's link two face cycles $c_1$ and $c_2$ by an edge of
|
|
$H$ if $|target|(|MinimalHalfedge|[c_1])$ has a vertical view down on
|
|
an edge of $c_2$ (in $M'$) or vice versa. Note that face cycles consist
|
|
of halfedges and thus we have to refer to the correct one of the two
|
|
paired halfedges respecting the embedding when looking at face cycles
|
|
(our faces are left of the directed halfedges, thus consider to
|
|
separate paired halfedges by an infinitesimal distance, then the
|
|
visibility is uniquely defined). Note that we do not explicitly model
|
|
the visibility graph. Instead the recursive behavior of the operation
|
|
|determine_face()| used below imitates a DFS walk on the visibility
|
|
graph.
|
|
|
|
In the following method we have the visibility along a sweep line
|
|
coded via a data accessor $D$ providing for all vertices $v \in V$ the
|
|
knowledge about the halfedge below $v$. |D.halfedge_below(v)| either
|
|
provides the halfedge of $E$ that is hit first by a vertical ray
|
|
downwards or an uninitialized halfedge if there is none.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Below_accessor, typename Halfsphere_geometry>
|
|
void SM_overlayer<Decorator_>::
|
|
create_face_objects(Halfedge_iterator e_start, Halfedge_iterator e_end,
|
|
Vertex_iterator v_start, Vertex_iterator v_end,
|
|
const Below_accessor& D,
|
|
const Halfsphere_geometry& SG) const
|
|
{
|
|
TRACEN("create_face_objects()");
|
|
CGAL::Unique_hash_map<Halfedge_handle,int> FaceCycle(-1);
|
|
std::vector<Halfedge_handle> MinimalHalfedge;
|
|
<<link edges to face cycles and determine minimal edges>>
|
|
<<create face objects for outer face cycles and create links>>
|
|
<<link holes and isolated vertices to face objects>>
|
|
}
|
|
|
|
@ We iterate all halfedges and assign a number for each face cycle.
|
|
After that iteration for a halfedge |e| the number of its face cycle is
|
|
|FaceCycle[e]| and for a face cycle |c| we know |MinimalHalfedge[c]|.
|
|
We first mark the outer face cycle with number 0. There we don't want
|
|
to create a face object.
|
|
<<link edges to face cycles and determine minimal edges>>=
|
|
Halfedge_around_face_circulator hfc(last_out_edge(v_start)),hend(hfc);
|
|
TRACEN("equator cycle "<<PH(hfc));
|
|
CGAL_For_all(hfc,hend) FaceCycle[hfc]=0; // outer face cycle = 0
|
|
MinimalHalfedge.push_back(twin(first_out_edge(v_start)));
|
|
int i=1;
|
|
for (Halfedge_iterator e = e_start; e != e_end; ++e) {
|
|
if ( FaceCycle[e] >= 0 ) continue; // already assigned
|
|
Halfedge_around_face_circulator hfc(e),hend(hfc);
|
|
Halfedge_handle e_min = e;
|
|
TRACEN(" face cycle numbering "<<i);
|
|
CGAL_For_all(hfc,hend) {
|
|
FaceCycle[hfc]=i; // assign face cycle number
|
|
if ( SG.compare_xy(point(target(hfc)), point(target(e_min))) < 0 )
|
|
e_min = hfc;
|
|
TRACE(PH(hfc));
|
|
} TRACEN("");
|
|
MinimalHalfedge.push_back(e_min);
|
|
++i;
|
|
}
|
|
|
|
@ We now know the number of face cycles |i| and we have a minimal
|
|
halfedge |e| for each face cycle. We just check the geometric
|
|
embedding of |e| and |next(e)| to characterize the face cycle (outer
|
|
or hole). Note that the two edges cannot be collinear due to the
|
|
minimality of |e| (the lexicographic minimality of the embedding of
|
|
its target vertex). Outer face cycles obtain face objects right away.
|
|
Hole cycles whose halfedge below is undefined are associated to the
|
|
unique outer face. After this chunk |f_outer| is the first face object
|
|
|faces_begin()| in the list of all face objects, and all outer face
|
|
cycles have face objects with the corresponding mark info associated.
|
|
<<create face objects for outer face cycles and create links>>=
|
|
for (int j=1; j<i; ++j) {
|
|
Halfedge_handle e = MinimalHalfedge[j];
|
|
TRACEN(" face cycle "<<j<<" minimal halfedge "<<PH(e));
|
|
Sphere_point p1 = point(source(e)),
|
|
p2 = point(target(e)),
|
|
p3 = point(target(next(e)));
|
|
if ( SG.orientation(p1,p2,p3) > 0 ) { // left_turn => outer face cycle
|
|
Face_handle f = new_face();
|
|
link_as_face_cycle(e,f);
|
|
TRACEN(" creating new face object "<<&*f<<" bd "<<&*e);
|
|
}
|
|
}
|
|
|
|
@ Now the only halfedges not linked are those on hole face cycles and
|
|
the equator face cycle. We use a recursive scheme to find the
|
|
bounding cycle providing the face object and finally iterate over all
|
|
isolated vertices to link them accordingly to their containing face
|
|
object. Note that in that final iteration all halfedges already have
|
|
face links. Thus that ensures termination. The recursive operation
|
|
$|determine_face|(e,\ldots)$ returns the face containing the hole
|
|
cycle of |e|. See the specification in the next section. As a
|
|
postcondition of this chunk we have all edges and isolated vertices
|
|
linked to face objects, and all face objects know their bounding face
|
|
cycles.
|
|
<<link holes and isolated vertices to face objects>>=
|
|
for (Halfedge_iterator e = e_start; e != e_end; ++e) {
|
|
if ( face(e) != Face_handle() ) continue;
|
|
if ( FaceCycle[e] == 0 ) continue;
|
|
TRACEN("linking hole "<<PH(e));
|
|
Face_handle f = determine_face(e,MinimalHalfedge,FaceCycle,D);
|
|
link_as_face_cycle(e,f);
|
|
}
|
|
for (Vertex_iterator v = v_start; v != v_end; ++v) {
|
|
if ( !is_isolated(v) ) continue;
|
|
Halfedge_handle e_below = D.halfedge_below(v);
|
|
CGAL_assertion( e_below != Halfedge_handle() );
|
|
link_as_isolated_vertex(v,face(e_below));
|
|
}
|
|
|
|
@ When we call |determine_face|$(e,\ldots)$ we know that the halfedge
|
|
|e| is not linked to a face object yet and thus no halfedge in its
|
|
face cycle is linked. Thus we jump to the minimal halfedge and look
|
|
down. |determine_face| is only called for edges in the interior of
|
|
the halfsphere, therefore every vertex has a halfedge below.
|
|
If we see a halfedge we ask for its face. If it does not
|
|
have one we recurse. Note that the target vertex of the minimal
|
|
halfedge actually has a view downwards as we examine a hole face
|
|
cycle. The method |link_as_face_cycle| does the linkage between the
|
|
face object and all edges of the face cycle. Its cost is linear in the
|
|
size of the face cycle. Note also that we do the linking bottom up
|
|
along the recursion stack for all visited hole cycles. Thus we visit
|
|
each hole face cycle only once as afterwards each edge of the face
|
|
cycle is incident to a face.
|
|
|
|
\displayeps{facecycles}{Face cycles and vertical visibility.}{7cm}
|
|
|
|
Look at our example in figure \figref{facecycles}. When
|
|
|determine_face| is called on an edge |e| of face cycle c3, then the
|
|
procedure first finds an edge of c4. If c4 was not visited yet by an
|
|
earlier call, then the method recurses to c4 before it finds the
|
|
correct face object via the outer face cycle c1.
|
|
<<overlayer helping methods>>=
|
|
template <typename Below_accessor>
|
|
Face_handle determine_face(Halfedge_handle e,
|
|
const std::vector<Halfedge_handle>& MinimalHalfedge,
|
|
const CGAL::Unique_hash_map<Halfedge_handle,int>& FaceCycle,
|
|
const Below_accessor& D) const
|
|
{ TRACEN("determine_face "<<PH(e));
|
|
int fc = FaceCycle[e];
|
|
Halfedge_handle e_min = MinimalHalfedge[fc];
|
|
Halfedge_handle e_below = D.halfedge_below(target(e_min));
|
|
CGAL_assertion( e_below != Halfedge_handle() );
|
|
Face_handle f = face(e_below);
|
|
if ( f != Face_handle() ) return f; // has already a face
|
|
// e_below also has no face
|
|
f = determine_face(e_below, MinimalHalfedge, FaceCycle,D);
|
|
link_as_face_cycle(e_below,f);
|
|
return f;
|
|
}
|
|
|
|
|
|
@ \subsection*{Transfering face support marks}
|
|
|
|
After the sweep and the face creation the input for this phase is a
|
|
sphere map $M =(V,E,L,F)$ enriched by additional information
|
|
attributed to the 1-skeleton objects of $M$. The output vertices in
|
|
$V$ are linked to their supporting skeleton input objects. The output
|
|
edges in $E$ are linked to their supporting input edges. Still missing
|
|
is the support knowledge with respect to input faces. In the following
|
|
we analyse this support but do not store it explicitly. Instead we
|
|
only transfer the marks. There are several properties of the
|
|
constructed subdivision |M| which help us to do this.
|
|
\begin{itemize}
|
|
\item the vertices are constructed in the order of the sweep. By
|
|
iterating them in their construction order we can rely on the fact
|
|
that we iterate according to the lexicographic order of their
|
|
embedding.
|
|
\item the halfedges out of a vertex |v| are ordered around |v|
|
|
counterclockwise (with respect to the embedding of their target).
|
|
We can therefore use a forward iteration to propagate face
|
|
information from bottom to top (on forward oriented edges).
|
|
\item we know markage information for two dedicated points of $M$,
|
|
$M_0$, and $M_1$.
|
|
\end{itemize}
|
|
<<transfering the marks of supporting objects per halfsphere>>=
|
|
complete_face_support(vertices_begin(), v, O, +1);
|
|
complete_face_support(v, vertices_end(), O, -1);
|
|
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Below_accessor>
|
|
void SM_overlayer<Decorator_>::
|
|
complete_face_support(Vertex_iterator v_start, Vertex_iterator v_end,
|
|
const Below_accessor& D, int pos) const
|
|
{ TRACEN("complete_face_support");
|
|
for (Vertex_iterator v = v_start; v != v_end; ++v) {
|
|
<<determine mark of v in both maps>>
|
|
<<complete marks of vertex v>>
|
|
<<handle all forward oriented edges starting in v>>
|
|
TRACEN(" mark of "<<PH(v)<<" "<<mark(v,0)<<" "<<mark(v,1));
|
|
}
|
|
<<transfering the marks to face objects>>
|
|
}
|
|
|
|
@ The transfer of face support marks is based on the following fact.
|
|
We treat the face support per hemisphere. To allow this method we
|
|
maintain two dedicated points close to the minimal vertex of each
|
|
iteration (one point for each hemispherical iteration).
|
|
|
|
\displaylps{support}{ The face support iteration unrolled. We examine
|
|
a position $p$ within the structure of the sphere map $M$ with respect
|
|
to the support by a face in the sphere map $M_i$. We have two vertices
|
|
$v_1$ and $v_2$ from $V$ with their forward-oriented edge bundle. The
|
|
face that supports $p$ with respect to $M_i$ can be determined by
|
|
following the dotted path until the outer unbounded face of $M_i$ is
|
|
reached.}
|
|
|
|
\begin{fact}
|
|
Let $p$ be a point of a hemisphere not part of the 1-skeleton of
|
|
$M_i$, $q$ be a point just right and below of $y^-$ outside the
|
|
hemiphere, and $\rho$ be any curve from $p$ to $q$ not containing any
|
|
vertex of $M_i$. Let $e$ be the first edge that $\rho$ intersects
|
|
when walking from $p$ to $q$ (if any). If $\rho$ does not intersect
|
|
the 1-skeleton, then $p$ and $q$ are part of the same face of $M_i$.
|
|
Otherwise $p$ is part of the face incident to $e$.
|
|
\end{fact}
|
|
The above fact is a consequence of the connectedness property of the
|
|
faces of $M_i$. We now consider point $p$ as part of $M$. For $p$ we
|
|
consider a path as depicted in figure \figref{support}. We walk along
|
|
a vertical ray down. If we cross a bundle of edges incident to a
|
|
vertex $v$ the path turn just below the lowest edge and follows the
|
|
lowest edges in parallel until it is just below $v$, and then iterates
|
|
this construction until it ends in a point $q$ outside the actual
|
|
hemisphere of $M$. Each edge $e$ that is crossed by $\rho$ is either
|
|
supported by an edge from $M_i$ or $M_{1-i}$. In the former case the
|
|
first such edge determines the face $f_i$ supporting $p$. If there's
|
|
no such edge then $p$ is supported by the point $q$ of $M_i$. We want
|
|
to determine face support for many vertices and edges, thus we don't
|
|
want to pay such a walk for each query point $p$. Instead we associate
|
|
for each edge $e$ face support knowledge in the two slots |mark(e,i)|
|
|
and |incident_mark(e,i)|. The idea is that these slots store the
|
|
knowledge obtained from a reversal walk from $q$ to $p$. Whenever our
|
|
path $\rho$ crosses an edge $e$ in $M$ that is supported by an edge
|
|
$e_i$ in $M_i$ then we associate the mark knowledge plus the mark of
|
|
the supporting faces from $M_i$ (of a small neighborhood left and
|
|
right of $e$) with $e$. If the information is already constructed for
|
|
all edges below a query point $p$, we can obtain that information in
|
|
constant time.
|
|
|
|
We now come to the coding. We want to complete the support marks for a
|
|
vertex |v| and the edges |e| of the adjacency list of |v| that are
|
|
forward oriented. Consider to follow $\rho$ reversely with a pen, then
|
|
|m_below[2]| always stores the marks of the faces of $M_i$ that
|
|
support the position of the pen. In the beginning |m_below[i]| stores
|
|
the mark of the face $f_i$ just slightly below and right of
|
|
$y^-$. Note that we obtain both marks for $i=0,1$ either on
|
|
initialization from the input sphere maps or from the halfedge below
|
|
|v|. If |e_below| exists then it was already treated as a forward
|
|
oriented edge of a vertex already handled in the vertex iteration.
|
|
|
|
Note that the iteration over all vertices |v| has the invariant that
|
|
either |v| has no halfedge below, or if it has a halfedge |e_below|
|
|
then |e_below| has all marks correctly assigned (|mark(e_below,i)| and
|
|
|incident_mark(e_below,i)| are set for both $i=0,1$). Note that the
|
|
only vertices that don't have edges below are our poles $y^-$, $y^+$
|
|
and all vertices on the x-positive equator $xy^+$. We have to treat
|
|
these vertices separately. For our first vertex $y^-$ we use the mark
|
|
information from the local graph |mark_of_halfsphere(int i)|. Note
|
|
that all others have at most one forward edge along the equator
|
|
(besides $y^+$) and at least one ingoing edge. For those vertices we
|
|
use the support path from ``above'' via the ingoing edge. That edge
|
|
has the support information |incident_mark()| set.
|
|
<<determine mark of v in both maps>>=
|
|
TRACEN("VERTEX = "<<PH(v));
|
|
Mark m_buffer[2];
|
|
Halfedge_handle e_below = halfedge_below(v);
|
|
if ( v == v_start ) {
|
|
for (int i=0; i<2; ++i)
|
|
m_buffer[i] = PI[i].mark_of_halfsphere(-pos);
|
|
} else if ( e_below != Halfedge_handle() ) {
|
|
for (int i=0; i<2; ++i)
|
|
m_buffer[i] = incident_mark(e_below,i);
|
|
} else { // e_below does not exist
|
|
CGAL_assertion( point(v).hz() == 0 &&
|
|
( pos > 0 ? (point(v).hx() >= 0) : (point(v).hx()<=0)) );
|
|
for (int i=0; i<2; ++i)
|
|
m_buffer[i] = incident_mark(previous(first_out_edge(v)),i);
|
|
} TRACEN(" faces right and below "<<m_buffer[0]<<" "<<m_buffer[1]);
|
|
|
|
@ If the vertex |v| is supported by a skeleton object of $M_i$ we have
|
|
to figure out which kind of skeleton object. In case of vertices and
|
|
loops the situation is clear. Only an edge $e$ has to be examined
|
|
furtheron. In that case $v$ can be embedded at |source(e)|,
|
|
|target(e)|, or in the relative interior of $e$. Otherwise $v$ is
|
|
supported by a face. We obtain the mark of the face from |m_buffer| in
|
|
this case.
|
|
<<complete marks of vertex v>>=
|
|
for (int i=0; i<2; ++i) {
|
|
Object_handle o = supp_object(v,i);
|
|
if ( o == NULL ) { mark(v,i) = m_buffer[i]; continue; }
|
|
Vertex_handle vs;
|
|
Halfedge_handle es;
|
|
Halfloop_handle ls;
|
|
if ( assign(vs,o) ) { mark(v,i) = PI[i].mark(vs); continue; }
|
|
if ( assign(es,supp_object(v,i)) ) {
|
|
if ( point(source(es)) == point(v) )
|
|
{ mark(v,i) = PI[i].mark(source(es)); continue; }
|
|
if ( point(target(es)) == point(v) )
|
|
{ mark(v,i) = PI[i].mark(target(es)); continue; }
|
|
mark(v,i) = PI[i].mark(es); continue;
|
|
}
|
|
if ( assign(ls,o) ) { mark(v,i) = PI[i].mark(ls); continue; }
|
|
CGAL_assertion_msg(0,"wrong handle");
|
|
} TRACEN(" vertex marks "<<mark(v,0)<<" "<<mark(v,1));
|
|
|
|
@ We have to complete the mark information for all edges of |M|. We
|
|
do the job for all forward oriented edges in the adjacency list of
|
|
each vertex |v|. How does a halfedge |e| of |M| obtain mark
|
|
information with respect to the two input structures $M_i$? We just
|
|
have to determine the supporting objects (edge or face) from each of
|
|
both. Note that our edges can be supported by an input edge, an input
|
|
loop, or an input face. This in all combinations. Note that
|
|
$xy$-equator edges can be supported purely by faces. They will be
|
|
simplified away lateron.
|
|
|
|
Note that a supporting edge/loop $e_i$ allows access to its mark and
|
|
to the two faces incident to it and its twin. The supporting edge
|
|
$e_i$ of |e| can be obtained via |supp_object(e,i)|. If $e$ is not
|
|
supported by an edge in $M_i$ then the mark of the input face is
|
|
stored in |m_buffer[i]|. Each supporting input edge of |e| changes
|
|
|m_buffer[i]| for the next output edge in the bundle iteration. If
|
|
|e| is not supported by an edge in $M_i$ then the supporting face
|
|
determines the mark of |e| and the two |incident_mark| entries. The
|
|
invariant for all edges $e$ in the iteration below is: if $e$ is not
|
|
supported by an edge $e_i$ of $M_i$ then |m_buffer[i]| contains the
|
|
mark of the face supporting $e$ in $M_i$.
|
|
<<handle all forward oriented edges starting in v>>=
|
|
if ( is_isolated(v) ) continue;
|
|
Halfedge_around_vertex_circulator e(first_out_edge(v)), hend(e);
|
|
CGAL_For_all(e,hend) {
|
|
if ( !is_forward(e) ) break;
|
|
TRACEN(" forward edge "<<PH(e));
|
|
for (int i=0; i<2; ++i) {
|
|
if ( supp_object(e,i) != NULL ) {
|
|
Halfedge_handle ei;
|
|
if ( assign(ei,supp_object(e,i)) ) {
|
|
if ( PI[i].circle(ei) != circle(e) ) { ei = PI[i].twin(ei); }
|
|
CGAL_assertion( PI[i].circle(ei) == circle(e) );
|
|
TRACEN(" supporting edge "<<i<<" "<<PH(ei));
|
|
incident_mark(twin(e),i) =
|
|
PI[i].mark(PI[i].face(PI[i].twin(ei)));
|
|
mark(e,i) = PI[i].mark(ei);
|
|
incident_mark(e,i) = m_buffer[i] =
|
|
PI[i].mark(PI[i].face(ei));
|
|
}
|
|
Halfloop_handle li;
|
|
if ( assign(li,supp_object(e,i)) ) {
|
|
if ( PI[i].circle(li) != circle(e) ) { li = PI[i].twin(li); }
|
|
CGAL_assertion( PI[i].circle(li) == circle(e) );
|
|
TRACEN(" supporting loop "<<i<<" "<<PH(li));
|
|
incident_mark(twin(e),i) =
|
|
PI[i].mark(PI[i].face(PI[i].twin(li)));
|
|
mark(e,i) = PI[i].mark(li);
|
|
incident_mark(e,i) = m_buffer[i] =
|
|
PI[i].mark(PI[i].face(li));
|
|
}
|
|
} else { TRACEN(" support from face below "<<i);
|
|
incident_mark(twin(e),i) = mark(e,i) =
|
|
incident_mark(e,i) = m_buffer[i];
|
|
}
|
|
} TRACEN(" face marks "<<m_buffer[0]<<" "<<m_buffer[1]);
|
|
}
|
|
|
|
@ The last chunk of this section transfers the support marks to the
|
|
face object. For all bounded faces $f$ we just transfer the marks from
|
|
the bounding face cycle to the face. As all edges $e$ carry the
|
|
|incident_mark(e,i)| attribute this completes the structure.
|
|
<<transfering the marks to face objects>>=
|
|
Face_iterator f;
|
|
for (f = faces_begin(); f != faces_end(); ++f) {
|
|
assoc_info(f);
|
|
Object_handle boundary_object = face_cycles_begin(f);
|
|
CGAL_assertion(boundary_object != NULL);
|
|
Halfedge_handle e;
|
|
if ( !assign(e,boundary_object) )
|
|
CGAL_assertion_msg(0,"Outer face cycle should be first.");
|
|
for (int i=0; i<2; ++i) mark(f,i) = incident_mark(e,i);
|
|
}
|
|
|
|
|
|
@ \subsection{Unifying the halfspheres}
|
|
|
|
Now we have two embedded hemispherical maps that overlap at the
|
|
equator of $S_2$. We have to stitch the two halfsphere maps along the
|
|
equator without loosing any information.
|
|
<<unifying the halfspheres>>=
|
|
merge_halfsphere_maps(vertices_begin(),v,O);
|
|
check_integrity_and_topological_planarity();
|
|
|
|
@ Let $e_1$ be an edge in the outer boundary of the positive
|
|
hemispherical map, let $e_2$ be a corresponding edge of the negative
|
|
hemispherical map. Both edges are embedded into the equator. Let $v_1
|
|
= |source|(e_1)$ and $v_2 = |target|(e_2)$. Both vertices have the
|
|
same embedding on the sphere surface and have to be identified while
|
|
keeping the adjacency list order-preserving. Note that $A(v_1)$ and
|
|
$A(v_2)$ are ccw-order-preserving. For both edges the complementary
|
|
hemisphere face is on their left side. The properties of our map
|
|
output regarding the maps in the two halfspheres tell us that
|
|
$|twin|(|prev|(e_1))$ is the first edge and $e_1$ is the last edge in
|
|
$A(v_1)$. In the other halfsphere $|twin|(e_2)$ is the first edge and
|
|
$|next|(e_2)$ is the last edge of $A(v_2)$. After the identification
|
|
$A(v_1)$ contains all edges, $v_2$ is isolated and can be deleted.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Mark_accessor>
|
|
void SM_overlayer<Decorator_>::
|
|
merge_nodes(Halfedge_handle e1, Halfedge_handle e2,
|
|
const Mark_accessor& D) const
|
|
{
|
|
Vertex_handle v1 = source(e1), v2 = target(e2);
|
|
TRACEN("merge_nodes "<<PH(v1)<<PH(v2));
|
|
CGAL_assertion(point(v1)==point(v2));
|
|
Halfedge_handle ep1 = previous(e1), en2 = next(e2);
|
|
Halfedge_around_vertex_circulator eav(out_edges(v2)),ee(eav);
|
|
CGAL_For_all(eav,ee) { set_source(eav,v1); }
|
|
link_as_prev_next_pair(e2,e1);
|
|
link_as_prev_next_pair(ep1,en2);
|
|
D.assert_equal_marks(v1,v2);
|
|
D.discard_info(v2);
|
|
delete_vertex_only(v2);
|
|
}
|
|
|
|
@ $v_1$ and $v_2$ are the definite vertices of both halfsphere maps
|
|
where the negative y-axis pierces the sphere. The last outedge of
|
|
$v_1$ bounds the face corresponding to the whole negative halfsphere.
|
|
The symmetric condition holds for the twin of the first outedge of
|
|
$v_2$. (Remember that the negative view can be obtained by a
|
|
half-rotation around the y-axis.)
|
|
|
|
What is left, is to remove trivial face cycles that where produced by
|
|
the glueing of parallel edges at the xy-equator. Such face cycles
|
|
consist of only two edges and can be easily removed.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Mark_accessor>
|
|
void SM_overlayer<Decorator_>::
|
|
merge_halfsphere_maps(Vertex_handle v1, Vertex_handle v2,
|
|
const Mark_accessor& D) const
|
|
{ TRACEN("merging halfspheres "<<PH(v1)<<PH(v2));
|
|
CGAL_assertion(point(v1)==point(v2));
|
|
std::list<Halfedge_pair> L_equator;
|
|
Halfedge_around_face_circulator
|
|
ep(last_out_edge(v1)), en(twin(first_out_edge(v2)));
|
|
do {
|
|
L_equator.push_back(Halfedge_pair(ep,en));
|
|
merge_nodes(ep,en,D); ++ep; --en;
|
|
} while ( source(ep) != v1 );
|
|
|
|
typename std::list<Halfedge_pair>::iterator it;
|
|
CGAL_forall_iterators(it,L_equator) {
|
|
Halfedge_handle e1 = it->first, e2 = it->second;
|
|
Halfedge_handle e1t = twin(e1), e2t = twin(e2);
|
|
TRACEV(PH(e1));TRACEV(PH(e2));
|
|
Halfedge_handle e2tp = previous(e2t);
|
|
Halfedge_handle e2tn = next(e2t);
|
|
link_as_prev_next_pair(e2tp,e1);
|
|
link_as_prev_next_pair(e1,e2tn);
|
|
Face_handle f = face(e2t);
|
|
if ( is_boundary_object(e2t) )
|
|
{ undo_boundary_object(e2t,f); store_boundary_object(e1,f); }
|
|
set_face(e1,f);
|
|
if ( e2 == first_out_edge(source(e2)) )
|
|
set_first_out_edge(source(e2),e1t);
|
|
D.discard_info(e2);
|
|
delete_edge_pair_only(e2);
|
|
}
|
|
}
|
|
|
|
@ \subsection{Selecting marks}
|
|
|
|
For the selection we just iterate over all objects read the marks
|
|
refering to the two input structures, apply our selection operation
|
|
and store the mark back into the object. At this place we discard the
|
|
additional information which was accumulated during the subdivision.
|
|
The flexibility of the operation is achieved by a template function
|
|
object type |Selection|. An object |SP| of type |Selection| must
|
|
provide a binary function operator returning a new mark object. The
|
|
runtime of the selection phase is obviously linear in the size of the
|
|
sphere map |P|.
|
|
|
|
We know the marks of two dedicated points on $S_2$.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Selection>
|
|
void SM_overlayer<Decorator_>::
|
|
select(const Selection& SP) const
|
|
{
|
|
Vertex_iterator v;
|
|
for(v = vertices_begin(); v != vertices_end(); ++v) {
|
|
mark(v) = SP(mark(v,0),mark(v,1));
|
|
discard_info(v);
|
|
}
|
|
Halfedge_iterator e;
|
|
for(e = halfedges_begin(); e != halfedges_end(); ++e) {
|
|
if ( info(e) == 0 ) continue; // twin
|
|
mark(e) = SP(mark(e,0),mark(e,1));
|
|
discard_info(e);
|
|
}
|
|
Face_iterator f;
|
|
for(f = faces_begin(); f != faces_end(); ++f) {
|
|
mark(f) = SP(mark(f,0),mark(f,1));
|
|
discard_info(f);
|
|
}
|
|
mark_of_halfsphere(-1) = SP(PI[0].mark_of_halfsphere(-1),
|
|
PI[1].mark_of_halfsphere(-1));
|
|
mark_of_halfsphere(+1) = SP(PI[0].mark_of_halfsphere(+1),
|
|
PI[1].mark_of_halfsphere(+1));
|
|
}
|
|
|
|
@ \subsection{Simplification of attributed sphere maps}
|
|
|
|
\displaylps{simplification}{The possible configurations for simplification.}
|
|
|
|
In this section we examine the task to simplify a given sphere map to
|
|
reach a minimimal representation (minimimal number of objects of the
|
|
sphere map structure, while the underlying attributed point set stays
|
|
the same). There are three situations where one can imagine to
|
|
simplify the structure (see figure \figref{simplification}):
|
|
\begin{enumerate}
|
|
\item a vertex which is incident to exactly two edges both supported
|
|
by the same line where all three objects have the same mark can be
|
|
unified into one edge without changing the stored point set.
|
|
(figure \figref{simplification},A)
|
|
\item a uedge $e$ which has the same mark as the two faces $f_1$ and
|
|
$f_2$ incident to it does not contribute any structural information
|
|
and thus can be removed (figure \figref{simplification},B).
|
|
\item a vertex $v$ where all the edges of its adjacency list and also
|
|
all incident faces have the same mark as the vertex also carry no
|
|
structural information (figure \figref{simplification},C,D).
|
|
\end{enumerate}
|
|
|
|
Note that if we first remove edges of the second case then the
|
|
vertices of case three have no incident edges at all and thus can be
|
|
easily identified as isolated vertices whose surounding face has the
|
|
same mark. The first case does only play a role if one of the faces
|
|
incident to the edges carries a diffent mark than the edges.
|
|
|
|
We can thus easily formulate the simplification routine. However there
|
|
are some problems with the update operations of the sphere map
|
|
structure. How can we maintain the face objects and incidence links to
|
|
halfedges and vertices if we are unifying faces by deleting edges? The
|
|
trivial way does not work within our time bound. We cannot afford to
|
|
maintain the face objects in a correct status in each step of the
|
|
simplification, as this would mean to repeatedly iterate total face
|
|
cycles. (Note that we cannot use a similar scheme as the one based on
|
|
the |halfedge_below| information of section \ref{creating face
|
|
objects} due to the fact that such edges might be deleted in the
|
|
simplification process. Thereby the face creation as described in
|
|
section \ref{creating face objects} is not possible without a new
|
|
sweep. We take a different approach. Instead of the geometrically
|
|
defined below links as a criterium for linking face cycles to face
|
|
objects, we use a unification history stored in a partition data
|
|
structure.
|
|
|
|
All face cycles reference face objects. When we have to union two
|
|
different faces due to the deletion of a halfedge separating them we
|
|
store that fact by a union operation in a partition data
|
|
structure. The finally assigned face to all face cycles is the face
|
|
corresponding to the representative partition item.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
void SM_overlayer<Decorator_>::simplify() const
|
|
{
|
|
TRACEN("simplifying");
|
|
typedef typename CGAL::Partition<Face_handle>::item partition_item;
|
|
CGAL::Unique_hash_map<Face_handle,partition_item> Pitem;
|
|
CGAL::Partition<Face_handle> FP;
|
|
<<initialize blocks corresponding to faces>>
|
|
<<simplify via non-separating halfedges>>
|
|
<<recollect face cycles per blocks>>
|
|
<<simplify via vertices>>
|
|
<<remove superflous face objects>>
|
|
}
|
|
|
|
@ We assign one partition item to each face object and make the item
|
|
accessible to the face via a hash map. During the assignment of face
|
|
cycles to face objects we will only use links from skeleton
|
|
objects like vertices and edges to faces. We therefore can discard
|
|
all face cycle entries of the face objects.
|
|
<<initialize blocks corresponding to faces>>=
|
|
Face_iterator f;
|
|
for (f = faces_begin(); f != faces_end(); ++f) {
|
|
Pitem[f] = FP.make_block(f);
|
|
clear_face_cycle_entries(f);
|
|
}
|
|
|
|
@ In this chunk we take care of the simplification critereon 2. We
|
|
only iterate halfedge pairs (uedges). When the marks of the incident
|
|
faces agree with the mark of the face, we union the items of the faces
|
|
if they are different. Special treatment is required for incident
|
|
vertices if they become isolated when the uedge is deleted.
|
|
<<simplify via non-separating halfedges>>=
|
|
Halfedge_iterator e, en;
|
|
for(e = halfedges_begin(); e != halfedges_end(); e = en) {
|
|
en = e; ++en; if ( en==twin(e) ) ++en;
|
|
if ( mark(e) == mark(face(e)) &&
|
|
mark(e) == mark(face(twin(e))) ) {
|
|
TRACEN("deleting "<<PH(e));
|
|
if ( !FP.same_block(Pitem[face(e)],
|
|
Pitem[face(twin(e))]) ) {
|
|
FP.union_blocks( Pitem[face(e)],
|
|
Pitem[face(twin(e))] );
|
|
TRACEN("unioning disjoint faces");
|
|
}
|
|
if ( is_closed_at_source(e) )
|
|
set_face(source(e),face(e));
|
|
if ( is_closed_at_source(twin(e)) )
|
|
set_face(target(e),face(e));
|
|
delete_edge_pair(e);
|
|
}
|
|
}
|
|
|
|
@ Now we recollect all face cycles and assign them to the face object
|
|
|f| that refers to the partition item obtained by a find operation. We
|
|
associate all edges in the face cycle with |f|.
|
|
<<recollect face cycles per blocks>>=
|
|
CGAL::Unique_hash_map<Halfedge_handle,bool> linked(false);
|
|
for (e = halfedges_begin(); e != halfedges_end(); ++e) {
|
|
if ( linked[e] ) continue;
|
|
Halfedge_around_face_circulator hfc(e),hend(hfc);
|
|
Face_handle f = FP.inf(FP.find(Pitem[face(e)]));
|
|
CGAL_For_all(hfc,hend) { set_face(hfc,f); linked[hfc]=true; }
|
|
store_boundary_object(e,f);
|
|
}
|
|
if ( has_loop() ) {
|
|
Halfloop_handle l = halfloop();
|
|
Face_handle f = FP.inf(FP.find(Pitem[face(l)]));
|
|
link_as_loop(l,f);
|
|
f = FP.inf(FP.find(Pitem[face(twin(l))]));
|
|
link_as_loop(twin(l),f);
|
|
}
|
|
|
|
|
|
@ After the previous simplification we still have to take care of the
|
|
vertex related simplifications 1 and 3. In case a vertex has
|
|
out-degree two, the two incident edges are embedded collinearly, and
|
|
all three objects have the same mark we remove the vertex by joining
|
|
the two uedges into one. In case that the vertex is isolated we either
|
|
remove it if the mark agrees with the incident face, otherwise we
|
|
anchor the vertex in the face by adding it to the isolated vertex
|
|
list. Note that the face link of each isolated vertex was either set
|
|
already in the face creation phase, or in the chunk
|
|
$\langle$\textit{simplify via non-separating halfedges}$\rangle$ when
|
|
the last incident edge was deleted.
|
|
<<simplify via vertices>>=
|
|
Vertex_iterator v,vn;
|
|
for(v = vertices_begin(); v != vertices_end(); v=vn) {
|
|
vn=v; ++vn;
|
|
if ( is_isolated(v) ) {
|
|
if ( mark(v) == mark(face(v)) ) {
|
|
TRACEN("removing isolated vertex"<<PH(v));
|
|
delete_vertex_only(v);
|
|
} else
|
|
store_boundary_object(v,face(v)); // isolated, but should stay
|
|
} else { // v not isolated
|
|
Halfedge_handle e2 = first_out_edge(v), e1 = previous(e2);
|
|
if ( has_outdeg_two(v) &&
|
|
mark(v) == mark(e1) && mark(v) == mark(e2) &&
|
|
circle(e1) == circle(e2) ) {
|
|
TRACEN("collinear at "<<PH(v)<<PH(e1)<<PH(e2));
|
|
if ( e1 == e2 ) convert_edge_to_loop(e1);
|
|
else merge_edge_pairs_at_target(e1);
|
|
}
|
|
}
|
|
}
|
|
|
|
@ Finally we discard all face objects that have been victims to
|
|
unification but do not represent the unified face.
|
|
<<remove superflous face objects>>=
|
|
Face_iterator fn;
|
|
for (f = fn = faces_begin(); f != faces_end(); f=fn) {
|
|
++fn;
|
|
partition_item pit = Pitem[f];
|
|
if ( FP.find(pit) != pit )
|
|
delete_face_only(f);
|
|
}
|
|
|
|
|
|
@ \begin{ignorecodeparts}
|
|
The following operations just wrap some basic primitives which make
|
|
our code more readable.
|
|
<<overlayer helping methods>>=
|
|
Sphere_segment segment(SM_decorator N,
|
|
Halfedge_handle e) const
|
|
{ return K.construct_segment(
|
|
N.point(N.source(e)),N.point(N.target(e)),N.circle(e)); }
|
|
|
|
Sphere_segment trivial_segment(SM_decorator N,
|
|
Vertex_handle v) const
|
|
{ Sphere_point p = N.point(v);
|
|
return K.construct_segment(p,p); }
|
|
|
|
Seg_pair two_segments(SM_decorator N,
|
|
Halfedge_handle e) const
|
|
// we know that source(e)==target(e)
|
|
{ return N.circle(e).split_at(N.point(N.source(e))); }
|
|
|
|
Seg_pair two_segments(SM_decorator N,
|
|
Halfloop_handle l) const
|
|
{ return N.circle(l).split_at_xy_plane(); }
|
|
|
|
|
|
Mark& mark(Vertex_handle h) const
|
|
{ return Base::mark(h); }
|
|
Mark& mark(Halfedge_handle h) const
|
|
{ return Base::mark(h); }
|
|
Mark& mark(Halfloop_handle h) const
|
|
{ return Base::mark(h); }
|
|
Mark& mark(Face_handle h) const
|
|
{ return Base::mark(h); }
|
|
|
|
|
|
|
|
@ \end{ignorecodeparts}
|
|
@ \subsection{Runtime results}
|
|
|
|
We can now summarize the calculated overlay properties
|
|
\begin{lemma}
|
|
Assume that $M_0$, $M_1$ each are sphere maps whose embedding is
|
|
order-preserving and the adjacency lists have a forward prefix then
|
|
$|subdivide|(P_0, P_1)$ constructs in $M = (V,E,L,F)$ the overlay
|
|
sphere map of $M_0$ and $M_1$ and each object $u \in V \cup E \cup F$
|
|
carries the mark information |mark(u,i)| from the corresponding
|
|
supporting object of the input sphere map. The runtime of the overlay
|
|
is dominated by the sweep of the skeleton objects of $M_0$ and $M_1$.
|
|
\end{lemma}
|
|
|
|
The following analysis of the partition data structure is due
|
|
to Tarjan \cite{tarjan83}.
|
|
\begin{fact}
|
|
A sequence of $m$ union and find operations on a partition data
|
|
structure using union by rank and path compression can be done in time
|
|
$O(m \alpha(m,n))$, where $\alpha$ is the very slowly growing inverse
|
|
of a suitably defined Ackermann function.
|
|
\end{fact}
|
|
|
|
We can therefore summarize the runtime of the simplification action.
|
|
\begin{lemma}
|
|
Assume that |P| is a sphere map with the properties cited in the
|
|
introduction of this section. Then the method |simplify()| runs in
|
|
time $O(n\ \alpha(kn,n))$ where $n$ is the size of $M$, $kn$ is a
|
|
bound for the number of face unifications and find operations, and
|
|
$\alpha$ is the function mentioned above.
|
|
\end{lemma}
|
|
\begin{proof}
|
|
The number of edges and faces of $M$ is linear in $n$. The number of
|
|
union operations is bounded by the number of faces, and the number of
|
|
find operations is bounded by three times\footnote{look for the
|
|
|find()| and |same_block()| operations above. The latter uses two find
|
|
operations.} the number of edges plus the number of faces.
|
|
\end{proof}
|
|
The time bound shows that the cost of the simplification phase is
|
|
subsumed in the $O(n \log n)$ cost of the construction of the
|
|
subdivision. Note that after the simplification the sphere map output
|
|
has again the input properties of the overlay calculation operation
|
|
from section \ref{Overlay calculation of two sphere maps}.
|
|
\begin{ignorecodeparts}
|
|
|
|
@ \subsection{Subdividing a set of sphere segments}
|
|
Subdivision is done in phases. First we partition all segments into
|
|
the pieces corresponding to the two halfspheres above and below the
|
|
xy-plane. Then we sweep both halfspheres separate by a symmetric
|
|
algorithmic module. We ensure that the boundary of the resulting two
|
|
halfsphere maps carry the same topology. We unify the maps embedded
|
|
into both halfspheres at the boundary and create one map embedded into
|
|
the sphere.
|
|
<<SM overlayer implementation>>=
|
|
template <typename Decorator_>
|
|
template <typename Iterator>
|
|
void SM_overlayer<Decorator_>::
|
|
subdivide_segments(Iterator start, Iterator end) const
|
|
{
|
|
typedef SMO_decorator<SM_decorator,Iterator> SM_output;
|
|
typedef typename Kernel::Positive_halfsphere_geometry PH_geometry;
|
|
typedef CGAL::Segment_overlay_traits<
|
|
Iterator, SM_output, PH_geometry> PHS_traits;
|
|
typedef CGAL::generic_sweep<PHS_traits> Positive_halfsphere_sweep;
|
|
|
|
typedef typename Kernel::Negative_halfsphere_geometry NH_geometry;
|
|
typedef CGAL::Segment_overlay_traits<
|
|
Iterator, SM_output, NH_geometry> NHS_traits;
|
|
typedef CGAL::generic_sweep<NHS_traits> Negative_halfsphere_sweep;
|
|
|
|
std::list<Sphere_segment> Lp,Lm;
|
|
partition_xy( start, end, Lp , +1);
|
|
partition_xy( start, end, Lm , -1);
|
|
// both lists initialized with four quarter segments
|
|
// supporting the xy-equator thereby separating the
|
|
// two halfspheres
|
|
// all other segments in the range are split into their
|
|
// connected components with respect to the xy-plane.
|
|
|
|
Vertex_handle v1,v2;
|
|
SM_output O(*this);
|
|
typedef typename PHS_traits::INPUT Input_range;
|
|
Positive_halfsphere_sweep SP(Input_range(Lp.begin(),Lp.end()),O);
|
|
SP.sweep();
|
|
//TRACEN("POS SWEEP\n"<<(dump(std::cerr),""));
|
|
v1= vertices_begin(); v2=--vertices_end();
|
|
Negative_halfsphere_sweep SM(Input_range(Lm.begin(),Lm.end()),O);
|
|
SM.sweep();
|
|
//TRACEN("NEG SWEEP\n"<<(dump(std::cerr),""));
|
|
++v2;
|
|
// now two CCs of sphere graph calculated
|
|
// v1 = first node of CC in positive xy-sphere
|
|
// v2 = first node of CC in negative xy-sphere
|
|
|
|
merge_halfsphere_maps(v1,v2,O);
|
|
check_integrity_and_topological_planarity(false);
|
|
}
|
|
|
|
<<SM traits classes for segment overlay>>=
|
|
template <typename Decorator_, typename ITERATOR>
|
|
class SMO_decorator {
|
|
public:
|
|
typedef Decorator_ Graph;
|
|
typedef typename Decorator_::Vertex_handle Vertex_handle;
|
|
typedef typename Decorator_::Halfedge_handle Halfedge_handle;
|
|
typedef typename Decorator_::Sphere_point Point_2;
|
|
typedef typename Decorator_::Sphere_segment Segment_2;
|
|
Decorator_ G;
|
|
|
|
SMO_decorator(Graph Gi) : G(Gi) {}
|
|
|
|
Vertex_handle new_vertex(const Point_2& p)
|
|
{ return G.new_vertex(p); }
|
|
|
|
void link_as_target_and_append(Vertex_handle v, Halfedge_handle e)
|
|
{ G.link_as_target_and_append(v,e); }
|
|
|
|
Halfedge_handle new_halfedge_pair_at_source(Vertex_handle v)
|
|
{ return G.new_edge_pair_at_source(v,Graph::BEFORE); }
|
|
|
|
void supporting_segment(Halfedge_handle e, ITERATOR it) {}
|
|
void halfedge_below(Vertex_handle v, Halfedge_handle e) {}
|
|
void trivial_segment(Vertex_handle v, ITERATOR it) {}
|
|
void starting_segment(Vertex_handle v, ITERATOR it) {}
|
|
void passing_segment(Vertex_handle v, ITERATOR it) {}
|
|
void ending_segment(Vertex_handle v, ITERATOR it) {}
|
|
|
|
|
|
}; // SMO_decorator
|
|
|
|
|
|
|
|
|
|
@ \begin{ignorecodeparts}
|
|
<<SM_overlayer.h>>=
|
|
<<CGAL Header>>
|
|
#ifndef CGAL_SM_OVERLAYER_H
|
|
#define CGAL_SM_OVERLAYER_H
|
|
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Partition.h>
|
|
#include <CGAL/Nef_2/Segment_overlay_traits.h>
|
|
#include <CGAL/Nef_2/geninfo.h>
|
|
#include <CGAL/Nef_S2/Sphere_geometry.h>
|
|
#include <CGAL/Nef_S2/SM_decorator.h>
|
|
#include <CGAL/Nef_S2/SM_io_parser.h>
|
|
#undef _DEBUG
|
|
#define _DEBUG 131
|
|
#include <CGAL/Nef_S2/debug.h>
|
|
|
|
#define CGAL_USING(t) typedef typename Base::t t
|
|
#ifndef CGAL_USE_LEDA
|
|
#define LEDA_MEMORY(t)
|
|
#endif
|
|
namespace CGAL {
|
|
|
|
<<SM traits classes for segment overlay>>
|
|
<<SM overlayer>>
|
|
<<SM overlayer implementation>>
|
|
|
|
} //namespace CGAL
|
|
#undef CGAL_USING
|
|
#endif //CGAL_SM_OVERLAYER_H
|
|
|
|
|
|
<<SM_overlayer-test.C>>=
|
|
#include <LOCAL/CGALH3.h>
|
|
#include <CGAL/Nef_S2/Sphere_map.h>
|
|
#include <CGAL/Nef_S2/SM_decorator.h>
|
|
#include <CGAL/Nef_S2/SM_io_parser.h>
|
|
#include <CGAL/Nef_S2/SM_overlayer.h>
|
|
|
|
typedef CGAL::Sphere_geometry<HKernel> SKernel;
|
|
typedef CGAL::Sphere_map<SKernel> Sphere_map;
|
|
typedef Sphere_map::Vertex_handle Vertex_handle;
|
|
typedef Sphere_map::Halfedge_handle Halfedge_handle;
|
|
typedef Sphere_map::Halfloop_handle Halfloop_handle;
|
|
typedef Sphere_map::Face_handle Face_handle;
|
|
typedef CGAL::SM_decorator<Sphere_map,SKernel> SM_decorator;
|
|
typedef CGAL::SM_overlayer<SM_decorator> SM_overlayer;
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
CGAL::set_pretty_mode ( std::cerr );
|
|
SETDTHREAD(101);
|
|
// Sphere_geometry 11
|
|
// Sphere_geometry_OGL 13
|
|
// Segment_overlay 23
|
|
// leda_sphere_map 31
|
|
Point_3 p(0,0,0);
|
|
Sphere_map M;
|
|
Vertex_handle v = M.new_vertex(p);
|
|
SM_overlayer D(M);
|
|
return 0;
|
|
}
|
|
|
|
<<SM_overlayer-demo.C>>=
|
|
#include <LOCAL/CGALH3.h>
|
|
#include <CGAL/algorithm.h>
|
|
#include <CGAL/random_selection.h>
|
|
#include <CGAL/point_generators_3.h>
|
|
#if CGAL_LEDA_VERSION < 500
|
|
#include <LEDA/param_handler.h>
|
|
#else
|
|
#include <LEDA/system/param_handler.h>
|
|
#endif
|
|
#include <CGAL/Nef_S2/Sphere_map.h>
|
|
#include <CGAL/Nef_S2/SM_decorator.h>
|
|
#include <CGAL/Nef_S2/SM_io_parser.h>
|
|
#include "SM_overlayer.h"
|
|
|
|
typedef CGAL::Sphere_geometry<HKernel> SKernel;
|
|
typedef CGAL::Sphere_map<SKernel> Sphere_map;
|
|
|
|
typedef Sphere_map::Vertex_handle Vertex_handle;
|
|
typedef Sphere_map::Halfedge_handle Halfedge_handle;
|
|
typedef Sphere_map::Face_handle Face_handle;
|
|
typedef CGAL::SM_decorator<Sphere_map,SKernel> SM_decorator;
|
|
typedef CGAL::SM_overlayer<SM_decorator> SM_overlayer;
|
|
|
|
typedef CGAL::Creator_uniform_3<RT,Point_3> Creator;
|
|
typedef CGAL::Random_points_in_cube_3<Point_3,Creator> Point_source;
|
|
typedef SKernel::Sphere_point SPoint;
|
|
typedef SKernel::Sphere_segment SSegment;
|
|
|
|
struct OR {
|
|
bool operator()(bool b1, bool b2) const
|
|
{ return b1||b2; }
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
CGAL::set_pretty_mode ( std::cerr );
|
|
SETDTHREAD(911);
|
|
// Sphere_geometry 11
|
|
// Sphere_geometry_OGL 13
|
|
// Segment_overlay 23
|
|
// SM_overlayer 53
|
|
Point_3 p(0,0,0);
|
|
Sphere_map E1,E2,E3;
|
|
SM_decorator D1(E1),D2(E2),D3(E3);
|
|
SM_overlayer O1(D1),O2(D2),O3(D3);
|
|
|
|
int n;
|
|
leda_string input_file;
|
|
leda_param_handler H(argc,argv,".sg",false);
|
|
H.add_parameter("number_of_lines:-n:int:10");
|
|
H.add_parameter("file_of_segments:-i:string:");
|
|
leda_param_handler::init_all();
|
|
H.get_parameter("-n",n);
|
|
H.get_parameter("-i",input_file);
|
|
|
|
CGAL_assertion_msg(n>0,"-n value must be postive.");
|
|
|
|
std::list<SSegment> L;
|
|
if ( input_file == "" ) {
|
|
Point_source S(5);
|
|
Point_3 p1,p2,ph;
|
|
Point_3 o(0,0,0);
|
|
while ( n-- > 0 ) {
|
|
do { ph = *S++; } while ( ph == o );
|
|
Plane_3 h(o,(ph-CGAL::ORIGIN).direction());
|
|
do { p1 = *S++; }
|
|
while ( p1 == o || h.projection(p1) == o );
|
|
do { p2 = *S++; }
|
|
while ( p2 == o || h.projection(p2) == o );
|
|
SPoint p3(h.projection(p1)),p4(h.projection(p2));
|
|
int which = CGAL::default_random.get_int(0,3);
|
|
if ( p3 == p4 ) which = 3;
|
|
if ( p3 == p4.opposite() ) which = 2;
|
|
switch ( which ) {
|
|
case 0: // short
|
|
L.push_back( SSegment(p3,p4,true) ); break;
|
|
case 1: // long
|
|
L.push_back( SSegment(p3,p4,false) ); break;
|
|
case 2: // halfcircle
|
|
L.push_back( SSegment(p3,p3.opposite(),h) ); break;
|
|
case 3: // trivial
|
|
L.push_back( SSegment(p3,p3,h) ); break;
|
|
}
|
|
}
|
|
} else {
|
|
std::ifstream input(input_file);
|
|
CGAL_assertion_msg(input,"no input log.");
|
|
SSegment s;
|
|
while ( input >> s ) L.push_back(s);
|
|
}
|
|
std::ofstream output("smo-demo.log");
|
|
std::list<SSegment>::iterator it;
|
|
CGAL_forall_iterators(it,L) output << *it;
|
|
output << std::endl;
|
|
output.close();
|
|
std::list<SSegment> L1,L2;
|
|
int b=0;
|
|
CGAL_forall_iterators(it,L) {
|
|
if ( b == 0 ) L1.push_back(*it);
|
|
else L2.push_back(*it);
|
|
b = 1-b;
|
|
}
|
|
O1.create_from_segments(L1.begin(),L1.end());
|
|
O1.simplify(); // O1.dump(std::cerr);
|
|
O2.create_from_segments(L2.begin(),L2.end());
|
|
O2.simplify(); // O2.dump(std::cerr);
|
|
O3.subdivide(E1,E2);
|
|
O3.select(OR());
|
|
O3.simplify(); // O3.dump(std::cerr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
<<CGAL Header>>=
|
|
// ============================================================================
|
|
//
|
|
// Copyright (c) 1997-2000 The CGAL Consortium
|
|
//
|
|
// This software and related documentation is part of an INTERNAL release
|
|
// of the Computational Geometry Algorithms Library (CGAL). It is not
|
|
// intended for general use.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// release : $CGAL_Revision$
|
|
// release_date : $CGAL_Date$
|
|
//
|
|
// file : include/CGAL/Nef_S2/SM_overlayer.h
|
|
// package : Nef_S2
|
|
// chapter : Nef Polyhedra
|
|
//
|
|
// source : nef_s2/SM_overlayer.lw
|
|
// revision : $Id$
|
|
// revision_date : $Date$
|
|
//
|
|
// author(s) : Michael Seel <seel@mpi-sb.mpg.de>
|
|
// maintainer : Michael Seel <seel@mpi-sb.mpg.de>
|
|
// coordinator : Michael Seel <seel@mpi-sb.mpg.de>
|
|
//
|
|
// implementation: Overlay module for sphere maps
|
|
// ============================================================================
|
|
|
|
@ \end{ignorecodeparts}
|
|
%KILLSTART
|
|
\bibliographystyle{alpha}
|
|
\bibliography{diss,general,geo_mod,comp_geo}
|
|
\newpage
|
|
\section{Appendix}
|
|
%\input{SM_decorator.man}
|
|
%\input{geninfo.man}
|
|
|
|
\end{document}
|
|
%KILLEND
|
|
|