Revision 97111c3ee492e366da6122519fd0d4b9edaf22f9 authored by Luis PeƱaranda on 06 July 2012, 14:35:10 UTC, committed by Luis PeƱaranda on 06 July 2012, 14:35:10 UTC
1 parent fb7a7e2
SM_overlayer.lw
%------------------------------------------------------------------------------
%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
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...