cgal/Nef_2/noweb/PM_overlayer.lw

2097 lines
87 KiB
Plaintext

%------------------------------------------------------------------------------
%KILLSTART DISS REP
%LDEL TRACE.*?\)\;
%LDEL LEDA_MEMORY.*?\)\;
\documentclass[a4paper]{article}
\usepackage{MyLweb,version}
\input{defs}
\excludeversion{ignoreindiss}
\excludeversion{ignore}
\includeversion{onlyindiss}
\begin{document}
\title{Plane Map Overlay}
\author{Michael Seel}
\maketitle
\tableofcontents
%KILLEND REP
@ \section{The manual page}
\input manpages/PM_overlayer.man
@ \newpage
\section{Implementation}
%KILLEND DISS
In this section we present a software module for the overlay of
segments and plane maps. We first give a formal introduction to the
notions and difficulties concerning overlay and support. We then
present the overlay calculation of a set of segments. We show how we
use a generic sweep module to produce the 1-skeleton of the output
plane 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 plane maps. We use the same generic sweep
module with slightly more elaborate adaptation to obtain again the
1-skeleton of the overlay. 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 plane
maps to corresponding objects in the output structure. This allows us
to use the module for binary set operations on plane map
structures. Such set operations use a selection phase on the
transferred information items. The selection phase is descibed below
in an additional section. The last part concerns structural
simplification of the output plane map. We will see that there can be
substructures in the output plane map that can be simplified without
losing any information when the plane map is interpreted as a point
set.
\subsection{Notions and definitions}
\begin{figure}[thbp]
\begin{center}
\input{segoverlay.pstex_t}\quad\input{pmoverlay.pstex_t}
\end{center}
\caption{\small The overlay of a set of segments and of two plane maps.
The left figure shows a set of dashed segments. $v_1$ is an isolated
vertex, $v_2$ is an endpoint in the interior of another segment, $v_3$
is a vertex supported by two endpoints, $v_4$ is the intersection of
the relative interiors of two segments. The edges are drawn with solid
line segments. One bounded face is greyed. The right figure shows the
1-skeleta of two plane maps. Degenerate situations are identical
vertices, vertices in the interior of edges, and overlapping edges.}
\label{fig:overlay}
\end{figure}
\newcommand{\PM}{\ensuremath{P = (V,E,F)}}
\newcommand{\PMi}{\ensuremath{P_i = (V_i,E_i,F_i)}}
\renewcommand{\Pi}{\ensuremath{P_i}}
If we consider our overlay process as a transformation of input
objects to output objects then we can define the support relation 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 $R^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 segments}\quad For a segment $s = (p,q)$,
$p$, $q$ are called the endpoints of $s$ where |p = source(s)|, |q =
target(s)|. Let us consider $s$ as a disjoint union of its endpoints
and its relative interior $\relint s$. A set of segments $S$
partitions the plane into cells of different dimensions. For each
point $r \in \R^2$ it can happen that
\begin{enumerate}\parsep0ex plus 0.0ex minus 1.0ex%
\itemsep-0.5ex\topsep-0.5ex\renewcommand{\labelenumi}{(\roman{enumi})}%
\item $r$ is equal to an endpoint of some segment $s$, or
\item $r$ is part of the relative interior of some segment $s$, or
\item $r$ is not part of any segment at all.
\end{enumerate}
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 $\R^2$ with respect to
the three criteria (i) to (iii) above including their topological
neighborhood and the knowledge of how parts of the segments in $S$
support the one-dimensional cells of the subdivision.
We store the overlay of $S$ in a plane map \PM{} in the standard
way. For each point $r$ in (i) there is 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
this segment also supports $v$. For each point in (ii) that is the
unique intersection point of the relative interior of two segments
(that do not overlap) there is a vertex in $V$ and the relative
interior of each of the two segments supports that vertex. Between any
two vertices in $V$ there is a uedge $e$ in $E$ if there is a segment
$s$ that supports the straight-line embedding of $e$ according to (ii)
and there is 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\footnote{path-connected in the strong topological sense.} of
$\R^2 - S$ that form the faces of $P$ and is thus not supported by any
segment at all.
\textbf{Overlay of two plane maps}\quad Let $P_i = (V_i,E_i,F_i),
i=0,1$ be two plane map structures. The overlay of two plane maps
$P_0$, $P_1$ is the plane map $P$ representing the subdivision of the
plane obtained by interpreting the skeleton objects of \Pi{} according
to their embedding as trivial and non-trivial segments, constructing
the overlay of these segments and adding the faces. To make this
structure really helpful we explore the support relation between
objects of \Pi{} and $P$.
In general, each point $p$ in the plane is supported by that object of
a plane map whose corresponding point set contains $p$. The support
relation between \Pi{} and $P$ comes in two steps. Each 1-skeleton
object of \Pi{} relates to the endpoint or relative interior of a
segment that supports a skeleton object in $P$. Conversely, each
object of $P$ (vertex, edge, or face) is supported by a unique
supporting object in each of the two structures \Pi{} $(i=0,1)$. We
show that this relation is well defined.
\begin{lemma}\label{supporting lemma}
Any object of $P$ has exactly one supporting object in each of the
$P_i$.
\end{lemma}
\begin{proof}
Obviously, each point of the plane is supported by an object of
$P_i$. Therefore, we only have to argue why no two objects of $P_i$
can support one object of $P$. For vertices this is trivial. For a
uedge $e$ in $P$ there can be only one uedge or one face of $P_i$ that
supports $e$: assume that the embedding of $e$ covers points from more
than one object of $P_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 $P$ in the
first place.
For a face $f$ of $P$ there can be only one face $f'$ of $P_i$ that
supports $f$: assume otherwise, that $f$ contains points from
different objects of $P_i$. As $f$ is an open connected point set it
has to cover points of at least one boundary object from the
1-skeleton of $P_i$. But this object is part of the 1-skeleton of $P$
and can therefore never be part of $f$.
\end{proof}
In our implementation we determine the support relation in two
phases. Any vertex $v$ in $V$ can be supported by a vertex $v_i$, a
uedge $e_i$, or a face $f_i$ of \Pi. If $v$ is supported by $v_i$ or
$e_i$ we obtain this information in a plane sweep process. Assume that
$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 postprocessing phase by a simple iteration over all
vertices. Any edge $e$ in $E$ can be supported by a uedge $e_i$ or a
face $f_i$ of \Pi. A possible support by $e_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}$) we also determine $f_i$ in the
postprocessing phase.
The support for a face $f$ in $F$ can be determined as follows.
Assume that each directed edge $e$ in $E$ knows the faces $f_i$
supporting points in a small neighborhood on its left side ($i=0,1$).
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 and use it afterwards to transfer attributes from $f_i$ to
$f$.
\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 plane map via a plane map decorator |PM_decorator_|; we
interface the underlying geometry via a geometry kernel
|Geometry_|. We inherit from |PM_decorator_| to obtain its interface
methods.
<<PM overlayer>>=
/*{\Moptions print_title=yes }*/
/*{\Msubst
PM_decorator_#PMD
Geometry_#GEO
}*/
/*{\Manpage {PM_overlayer}{PMD,GEO}{Plane Map Overlay}{O}}*/
template <typename PM_decorator_, typename Geometry_>
class PM_overlayer : public PM_decorator_ {
typedef PM_decorator_ Base;
typedef PM_overlayer<PM_decorator_,Geometry_> Self;
const Geometry_& K; // geometry reference
/*{\Mdefinition An instance |\Mvar| of data type |\Mname| is a
decorator object offering plane map overlay calculation. Overlay is
either calculated from two plane maps or from a set of segments. The
result is stored in a plane map |P| that carries the geometry and the
topology of the overlay.
The two template parameters allow to adapt the overlay calculation
to different scenarios. The template parameter |PM_decorator_| has to
be a model conforming to our plane map decorator concept
|PMDecorator|. The concept describes the interface how the
topological information stored in |P| can be extracted. The geometry
|Geometry_| has to be a model conforming to the concept
|OverlayerGeometry_2|.
The overlay of a set of segments $S$ is stored in a plane map $P =
(V,E,F)$. Vertices are either the endpoints of segments (trivial
segments are allowed) or the result of a non-degenerate internal
intersection of two segments. Between two vertices there is an edge if
there is a segment that supports the straight line embedding of $e$ and
if there is no vertex in the relative interior of the embedding of $e$.
The faces refer to the maximal connected open point sets of the
planar subdivision implied by the embedding of the vertices and edges.
Faces are bounded by possibly several face cycles\footnote{For the
definition of plane maps and their concepts see the manual page of
|PMConstDecorator|.} including isolated vertices. The overlay process
in the method |create| creates the objects, the topology of the result
and allows to link the plane map objects to input segments by means of
a data accessor. The method starts from zero- and one-dimensional
geometric objects in $S$ and produces a plane map |P| where each point
of the plane can be assigned to an object (vertex, edge, or face) of
|P|.
The overlay of two plane maps $P_i = (V_i, E_i, F_i)$ has the
additional aspect that we already start from two planar subdivisions.
We use the index $i=0,1$ defining the reference to $P_i$, unindexed
variables refer to the resulting plane map $P$. The $1$-skeleta of
the two maps subdivide the edges and faces of the complementary
structure into smaller units. This means vertices and edges of $P_i$
can split edges of $P_{1-i}$ and face cycles of $P_i$ subdivide faces
of $P_{1-i}$. The 1-skeleton $P'$ of $P$ is defined by the overlay of
the embedding of the 1-skeleta of $P_0$ and $P_1$ (Take a trivial
segment for each vertex and a segment for each edge and use the
overlay definition of a set of segments above). The faces of $P$ refer
to the maximal connected open point sets of the planar subdivision
implied by the embedding of $P'$. 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 transparencies, which we
stack. Then each point of the plane is covered by an object from each
of the input structures. This support relation from the input
structures to the output structure defines an information flow. Each
supporting object $u_i$ of $u$ $(i=0,1)$ carries an attribute
$|mark|(u_i)$. After the subdivision operation this attribute
is associated to the output object $u$ by $|mark|(u,i)$.}*/
/*{\Mgeneralization PM_decorator_}*/
public:
/*{\Mtypes 8}*/
typedef PM_decorator_ Decorator;
/*{\Mtypemember the plane map decorator |PM_decorator_|.}*/
typedef typename Decorator::Plane_map Plane_map;
/*{\Mtypemember the plane map type decorated by |PM_decorator_|.}*/
typedef Geometry_ Geometry;
/*{\Mtypemember the geometry kernel |Geometry_|.}*/
typedef typename Geometry::Point_2 Point;
/*{\Mtypemember the point type of the geometric kernel,
\precond |Point| equals |Plane_map::Point|.}*/
typedef typename Geometry::Segment_2 Segment;
/*{\Mtypemember the segment type of the geometric kernel.}*/
typedef typename Decorator::Mark Mark;
/*{\Mtypemember the attribute type of plane map objects.}*/
<<handles, iterators, and circulators from Decorator>>
<<info type to link edges and segments>>
/*{\Mcreation 6}*/
PM_overlayer(Plane_map& P, const Geometry& g = Geometry()) :
/*{\Mcreate |\Mvar| is a decorator object manipulating |P|.}*/
Base(P), K(g) {}
<<subdivision>>
<<selection>>
<<simplification>>
<<helping operations>>
}; // PM_overlayer<PM_decorator_,Geometry_>
@ \begin{ignoreindiss}
<<handles, iterators, and circulators from Decorator>>=
#define CGAL_USING(t) typedef typename Decorator::t t
typedef typename Decorator::Base Const_decorator;
CGAL_USING(Halfedge_handle);
CGAL_USING(Vertex_handle);
CGAL_USING(Face_handle);
CGAL_USING(Vertex_iterator);
CGAL_USING(Halfedge_iterator);
CGAL_USING(Face_iterator);
CGAL_USING(Halfedge_const_handle);
CGAL_USING(Vertex_const_handle);
CGAL_USING(Face_const_handle);
CGAL_USING(Halfedge_const_iterator);
CGAL_USING(Vertex_const_iterator);
CGAL_USING(Face_const_iterator);
CGAL_USING(Halfedge_around_vertex_circulator);
CGAL_USING(Halfedge_around_face_circulator);
CGAL_USING(Hole_iterator);
CGAL_USING(Isolated_vertex_iterator);
#undef CGAL_USING
// C++ is really friendly:
#define USECMARK(t) const Mark& mark(t h) const { return Base::mark(h); }
#define USEMARK(t) Mark& mark(t h) const { return Base::mark(h); }
USEMARK(Vertex_handle)
USEMARK(Halfedge_handle)
USEMARK(Face_handle)
USECMARK(Vertex_const_handle)
USECMARK(Halfedge_const_handle)
USECMARK(Face_const_handle)
#undef USEMARK
#undef USECMARK
/*{\Moperations 1.1 1}*/
@ \end{ignoreindiss}
@ \subsection{Overlay calculation of a list of segments}
\label{segment overlay calculation}
We want to calculate the plane map $P$ representing the overlay of a
set $S$ of segments, some of which may be trivial. This task is
basically split in \textbf{two phases}:
\begin{description}\parsep0ex plus 0.0ex minus 1.0ex%
\itemsep0ex\topsep-0.5ex%
\item[overlay of segments]--- the calculation of the 1-skeleton
$P'=(V,E)$ of a plane map via the overlay of the segments in $S$ plus
the calculation of a map $|halfedge_below|:V \rightarrow E$
\item[face creation]--- the completion of the 1-skeleton $P'$ to a
full plane map $P = (V,E,F)$ by creating all faces while using the
information of the map |halfedge_below|.
\end{description}
\repdiss{For the overlay process we use the generic segment sweep
module as presented in the technical report
\cite{TR:nefimplementation}}{For the overlay process we use the
generic segment sweep module as presented in Section \ref{generic
plane sweep}}. There we presented a generic class
|Segment_overlay_traits| realizing a generic sweep framework. To
instantiate it we have to provide three components (input, output,
geometry). In this instance, the input is an iterator pair; the
geometry is forwarded from the current class scope. Only for the
output type do we have to work a little more. We define a class
|PMO_from_segs| that fits the output concept of
|Segment_overlay_traits| and at the same time is a model for the
|Below_info| concept required for the facet creation in Section
\ref{creating face objects}. (See Figure \figref{PMO_from_segs}.)
\displayeps{PMO_from_segs}{|PMO_from_segs| realizes the |Output|
concept of the generic sweep module and the |Below_info|
concept for the facet creation phase. In the figure, |Vertex_handle|,
|Halfedge_handle|, and |Iterator| have been replaced by the short
symbols |V|, |E|, and |I|.}{10cm}
Upon creation, an object of type |PMO_from_segs| references a plane
map via a decorator |G| and obtains a data accessor object |D| of type
|DA|. |PMO_from_segs|, as a model of |SegmentOverlayOutput|, triggers
the correct update operations on the output plane map during the
sweep. See the output concept in Figure \figref{PMO_from_segs}. The
method part O1 of |SegmentOverlayOutput| takes care of the plane map
extension by new vertices and edges. The part O2 allows one to obtain
information about how the creation of the objects is linked to the
input interators. In the implementation \repdiss{}{(which we do not
show)} of |PMO_from_segs| we forward this interface to methods of the
data accessor of type DA. Finally, part O3 can be used to collect the
additional information required for the facet creation. An edge |e|
that is immediately below\footnote{along the negative $y$-axis
parallel to our sweep line.} a vertex |v| is stored in the vertex
object in a temporarily assigned data slot and can be retrieved after
the sweep. |PMO_from_segs|, as a |Below_info| model, can thus
afterwards deliver the halfedge |halfedge_below(v)| for any vertex $v$
of the plane map.
At this point our readers should take the module
@c
generic_sweep< Segment_overlay_traits<PMO_from_segs<... >... >>
@ as a black box producing the 1-skeleton of $P$ with the properties
required in Section \ref{creating face objects}. The specification of
|Segment_overlay_traits| guarantees these properties of $P$ because
|PMO_from_segs| fits the requirements of the output concept of
|Segment_overlay_traits|.
\begin{ignoreindiss}
<<PM traits classes for segment overlay>>=
template <typename PMD, typename I, typename DA>
struct PMO_from_segs {
typedef PMD Decorator;
typedef typename Decorator::Vertex_handle Vertex_handle;
typedef typename Decorator::Halfedge_handle Halfedge_handle;
typedef typename Decorator::Point Point;
const Decorator& G;
DA& D;
PMO_from_segs(const Decorator& Gi, DA& Di) :
G(Gi),D(Di) {}
<<PMO_from_segs segment overlay model interface>>
<<PMO_from_segs face creation model interface>>
<<PMO_from_segs additional interface>>
}; // PMO_from_segs
@ The creation of new objects is forwarded to the |Decorator| object
|G|. The methods of |G| are members as described in the |PM_decorator|
concept. The following methods are called during the sweep at its
event points. The first three methods trigger object creation in $P$.
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.
<<PMO_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_halfedge_pair_at_source(v,Decorator::BEFORE);
return e;
}
@ The treatment of the new objects is forwarded to the |DA| object
|D|. Only the below link is stored via |G|. The following methods
allow us to hook methods of |D| into the plane sweep process.
<<PMO_from_segs segment overlay model interface>>=
void supporting_segment(Halfedge_handle e, I it) const
{ D.supporting_segment(e,it); }
void trivial_segment(Vertex_handle v, I it) const
{ D.trivial_segment(v,it); }
void starting_segment(Vertex_handle v, I it) const
{ D.starting_segment(v,it); }
void passing_segment(Vertex_handle v, I it) const
{ D.passing_segment(v,it); }
void ending_segment(Vertex_handle v, I it) const
{ D.ending_segment(v,it); }
void halfedge_below(Vertex_handle v, Halfedge_handle e) const
{ geninfo<Halfedge_handle>::access(G.info(v)) = e; }
@ |PMO_from_segs| fits also the concept |Below_info| for the face
creation defined in Section \ref{creating face objects}.
<<PMO_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.
<<PMO_from_segs additional interface>>=
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));
}
@ \end{ignoreindiss}
Now, the overlay creation is trivial. Just create an output decorator
object |Out| working on the plane map maintained by |PM_overlayer| and
plug it into the segment sweep overlay framework
|Segment_overlay_traits|. The used geometry is just forwarded from
|PM_overlayer|. The |create| method of |PM_overlayer| is
parameterized by the iterator type |Forward_iterator| and the data
accessor class |Object_data_accessor|.
Note that the |halfedge_below| information collected during the sweep
is associated with the vertices of the output map. The corresponding
object |Out| triggers the output creation during the sweep and
provides the halfedge-below information for the face creation in
|create_face_objects()|. |Out.clear_temporary_vertex_info()| just
discards the temporarily allocated information slots (internally
assigned to the vertices) on the heap.
<<subdivision>>=
template <typename Forward_iterator, typename Object_data_accessor>
void create(Forward_iterator start, Forward_iterator end,
Object_data_accessor& A) const
/*{\Mop produces in |P| the plane map consistent with the overlay
of the segments from the iterator range |[start,end)|. The data accessor
|A| allows to initialize created vertices and edges with respect to the
segments in the iterator range. |A| requires the following methods:\\
[[void supporting_segment(Halfedge_handle e, Forward_iterator it)]]\\
[[void trivial_segment(Vertex_handle v, Forward_iterator it)]]\\
[[void starting_segment(Vertex_handle v, Forward_iterator it)]]\\
[[void passing_segment(Vertex_handle v, Forward_iterator it)]]\\
[[void ending_segment(Vertex_handle v, Forward_iterator it)]]\\
where |supporting_segment| is called for each non-trivial segment |*it|
supporting a newly created edge |e|, |trivial_segment| is called for
each trivial segment |*it| supporting a newly created vertex |v|, and
the three last operations are called for each non-trivial segment
|*it| starting at/passing through/ending at the embedding of a newly
created vertex |v|.
\precond |Forward_iterator| has value type |Segment|.}*/
{
TRACEN("creating from iterator range");
typedef PMO_from_segs<Self,Forward_iterator,Object_data_accessor>
Output_from_segments;
typedef Segment_overlay_traits<
Forward_iterator, Output_from_segments, Geometry> seg_overlay;
typedef generic_sweep< seg_overlay > seg_overlay_sweep;
typedef typename seg_overlay::INPUT input_range;
Output_from_segments Out(*this, A);
seg_overlay_sweep SOS( input_range(start, end), Out, K);
SOS.sweep();
create_face_objects(Out);
Out.clear_temporary_vertex_info();
}
@ We summarize the calculated overlay properties and anticipate the
costs of face creation (Section \ref{creating face objects}) and of
the plane sweep phase. (see Lemma \ref{generic segment sweep
runtime}).
\begin{lemma}
Assume that |S = set [start,end)| is a set of segments and |A| is a
data accessor with the required methods (of constant cost). Then,
$|create|(start,end,A)$ constructs in $P = (V,E,F)$ the overlay plane
map of $S$. Let $n$ be the number of segments in $S$, $n_v =
\Labs{V}$, $n_e = \Labs{E}$, and $\bar{n}_e$ the sum of the support
multiplicity of each edge over all edges. Then the running time of the
overlay process is dominated by the plane sweep and is therefore
$O(n_v + \bar{n}_e + (n+n_v) \log (n+n_v))$.
\end{lemma}
@ \subsection{Overlay calculation of two plane maps}
\label{Overlay calculation of two plane maps}
We calculate the overlay $P$ of two plane maps $P_0$ and $P_1$. Both
input structures are correctly defined plane maps including incidence,
geometric embedding, and markers. In the following we use the index
$i=0,1$ showing a reference to $P_i = (V_i,E_i,F_i)$; non-indexed
variables refer to $P$.
The $1$-skeleta of the two maps $P_0$ and $P_1$ subdivide the edges
and faces of the complementary structures into smaller units. This
means vertices and edges of $P_i$ can split edges of $P_{1-i}$ and
face cycles of $P_i$ subdivide faces of $P_{1-i}$. The 1-skeleton $P'
= (V,E)$ of $P$ is defined by the overlay of the embedded 1-skeleta of
$P_0$ and $P_1$. (Take a trivial segment for each vertex and a segment
for each edge and use the overlay definition of a set of segments
above.) Additionally, we require that $P'$ has the correct order in
each adjacency list such that it is order-preserving with respect to
the embedding of the vertices.
Finally, the faces of $P$ refer to the maximal connected open point
sets of the planar subdivision implied by the embedding of $P'$. The
construction of the faces $F$ from $P'$ is described in Section
\ref{creating face objects}. Each object $u$ from the output tuple
$(V,E,F)$ has a \emph{supporting} object $u_i, i=0,1$ in each of the
two input structures. Imagine the two maps to be transparencies,
stacked one on top of the other. Then each point of the plane is
covered by an object from each of the input structures. We analyse the
support relation from input to output in order to transfer the
attributes from $u_i$ to $u$.
According to our specification, each object $u_i$ of $P_i$ carries an
attribute\footnote{we use a general attribute set, though with respect
to Nef polyhedra $|Mark| := \{|true|,|false|\}$.} $|mark|(u_i)$
($|mark|: (V_i \cup E_i \cup F_i) \to |Mark|$). We associate this
information with the output object $u$ by $|mark|(u,i)$ (an overloaded
function $|mark|: (V \cup E \cup F) \times \{0,1\} \to |Mark|$). This
two-tuple of information per object can then be processed by some
combining operation to a single value |mark(u)| later on.
We fix the following \textbf{input properties} for our structures
$P_i$. Both plane maps $(V_i, E_i, F_i)$ consist of vertices, edges,
and faces whose topology is accessible by our plane map interface and
additionally each object $u_i$ carries an attribute $|mark|(u_i)$. The
plane maps have an \emph{order-preserving} embedding and their
adjacency lists have a \emph{forward prefix}. Actually we do not use
this property of the input plane maps at this point, but it is a
general invariant of our plane map structures that makes some
intermediate actions more efficient. The overlay process consists of
\textbf{three phases}: The 1-skeleton $P'$ is produced by segment
overlay. Afterwards we create the face objects. Finally, we analyse
the support relation and transfer the marks of the input objects to
the output objects.
\begin{description}
\item[overlay of segments] --- We use our generic segment overlay
framework to calculate the overlay of a set of 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$. The output structure $P' = (V,E)$ of the sweep phase is
just the 1-skeleton of the output plane map $P$, but of course
including an order-preserving embedding and a forward-prefix in the
adjacency lists. The objects of the 1-skeleton carry additional
structural information:
\begin{enumerate}\parsep0ex plus 0.0ex minus 1.0ex%
\itemsep0ex\topsep-0.5ex\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 shot 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 but not their
target vertices.
\label{halfedge below}
\item For each object $u \in V \cup E$ there is a mapping to the
supporting 1-skeleton objects of the input structures. The support
information is incomplete with respect to face support.
\label{skeleton support}
\end{enumerate}
\item[face creation] --- The next phase after the sweep completes the
plane map $P$. We basically have to create the face objects and
construct their incidence structures. The face creation is done as
presented in Section \ref{creating face objects} and uses only
I\ref{halfedge below}.
\item[attribute transfer] --- The final transfer of marks uses the
embedding of the vertex list of $P$ and the additional information
I\ref{halfedge below} and I\ref{skeleton support} to define
$|mark|(u,i)$ for all objects $u$ in $P$.
\end{description}
<<subdivision>>=
void subdivide(const Plane_map& P0, const Plane_map& P1) const
/*{\Mop constructs the overlay of the plane maps |P0| and |P1| in
|P|, where all objects (vertices, halfedges, faces) of |P| 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
|P0| and by a face |f1| in |P1| and |D0|, |D1| be decorators of
type |PM_decorator| on |P0|,|P1|. Then |\Mvar.mark(v,0) = D0.mark(v0)|
and |\Mvar.mark(v,1) = D1.mark(f1)|.}*/
{
Const_decorator PI[2];
PI[0] = Const_decorator(P0); PI[1] = Const_decorator(P1);
<<filling the input segment list>>
<<sweeping the segments and creating the faces>>
<<transfering the marks of supporting objects>>
}
@ \subsection*{Temporary information associated with objects}
We have to associate temporary information with the objects of the
output plane map. In this section we abstractly use sets in a pseudo
code notation to underline the origin of plane map objects. The
objects from these sets are realized by the corresponding handle types
(and therefore their type does not allow us to mark their origin).
Undefined objects are detectible via default handles.
At first we interpret the input 1-skeleta geometrically. We collect a
set of trivial and non-trivial segments $S$. For each edge in $E_i$
we add a non-trivial segment to $S$ and for each isolated vertex of
$V_i$ we add a trivial segment to $S$. We store the origin of the
objects in $S$ via a function
\begin{eqnarray*}
|From| & : & S \to (V_{0,1} \cup E_{0,1}) \times \{0,1\}\\
|From|(s) & = & \begin{cases}
(v_i,i) & \text{if $s$ is a trivial segment refering to an isolated
vertex $v_i$ from $P_i$},\\
(e_i,i) & \text{if $s$ is a non-trivial segment refering to an edge
$e_i$ from $P_i$.}
\end{cases}
\end{eqnarray*}
|From| is implemented as a hash map |From| whose domain is iterators
(with value type segment) and whose value is a structure |Seg_info|
with members |v|, |e|, |i| storing the above pairs.
\begin{ignoreindiss}
<<info type to link edges and segments>>=
struct Seg_info { // to transport information from input to output
Halfedge_const_handle e;
Vertex_const_handle v;
int i;
Seg_info() : i(-1) {}
Seg_info(Halfedge_const_handle e_, int i_)
{ e=e_; i=i_; }
Seg_info(Vertex_const_handle v_, int i_)
{ v=v_; i=i_; }
Seg_info(const Seg_info& si)
{ e=si.e; v=si.v; i=si.i; }
Seg_info& operator=(const Seg_info& si)
{ e=si.e; v=si.v; i=si.i; return *this; }
LEDA_MEMORY(Seg_info)
};
@ \end{ignoreindiss}
<<info type to link edges and segments>>=
typedef std::list<Segment> Seg_list;
typedef typename Seg_list::const_iterator Seg_iterator;
typedef std::pair<Seg_iterator,Seg_iterator> Seg_it_pair;
@ In the first phase, we fill the segment input list with the
non-trivial segments underlying the edges and with a trivial segment
for each isolated vertex of the two input structures. Additionally, we
store hashed links from the iterators to the edges/vertices to store
their origins.
<<filling the input segment list>>=
Seg_list Segments; int i;
CGAL::Unique_hash_map<Seg_iterator,Seg_info> From;
for (i=0; i<2; ++i) {
Vertex_const_iterator v;
for(v = PI[i].vertices_begin(); v != PI[i].vertices_end(); ++v)
if ( PI[i].is_isolated(v) ) {
Segments.push_back(segment(PI[i],v));
From[--Segments.end()] = Seg_info(v,i);
}
Halfedge_const_iterator e;
for(e = PI[i].halfedges_begin(); e != PI[i].halfedges_end(); ++e)
if ( is_forward_edge(PI[i],e) ) {
Segments.push_back(segment(PI[i],e));
From[--Segments.end()] = Seg_info(e,i);
}
}
@ During the sweep phase we collect additional information in
temporary information containers associated with the objects $u \in V
\cup E \cup F$ of $P$.
\begin{tabbing}
|assoc_info(u)| \qquad\= creates the temporary object on the heap\\
|discard_info(u)| \> discards the object and frees the memory
\end{tabbing}
Within these objects we store the following pair of mark attributes
(indexed by $i$):
\begin{tabbing}
|Mark mark(u,i)| \qquad\= for $i=0,1$ and $u\in V \cup E \cup F$
\end{tabbing}
For each vertex $v$ we collect \emph{skeleton support} information.
\begin{tabbing}
$V_i\ |supp_vertex|(V v, int i)$ \qquad \= the vertex from $V_i$
supporting |v| if it exists, else undefined.\\ $E_i\ |supp_halfedge|(V
v, int i)$ \> the edge from $E_i$ supporting |e| if it exists, else
undefined.
\end{tabbing}
And for each edge $e$ we want to know
\begin{tabbing}
$E_i\ |supp_halfedge(E e, int i)|$\qquad\= the edge from $E_i$
supporting |e| if it exists, else undefined.\\
$|Mark incident_mark(E e, int i)|$\>
the mark of the face from $P_i$ supporting a small neighborhood
left of $e$.\\
\end{tabbing}
The information is collected during the sweep phase by a corresponding
model of the output concept used in our generic sweep framework.
\begin{onlyindiss}
We omit the realization of the above attribution. Details can be found
in the accompanying research report.
\end{onlyindiss}
\begin{ignoreindiss}
We show more details of the information association. We use a trick
via generic pointers |GenPtr| (equals |void*|). Each object |u| of |P|
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 |PM_overlayer|. |vertex_info| can store
the possible supporting skeleton objects |v_supp|, |e_supp| of the
input plane maps, marks |m| corresponding to the two supporting
objects and a halfedge |e_below| of the output structure that is
vertically below $v$.
<<helping operations>>=
struct vertex_info {
Mark m[2];
Vertex_const_handle v_supp[2];
Halfedge_const_handle e_supp[2];
Halfedge_handle e_below;
vertex_info()
{ v_supp[0]=v_supp[1]=Vertex_const_handle();
e_supp[0]=e_supp[1]=Halfedge_const_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]; }
Vertex_const_handle& supp_vertex(Vertex_handle v, int i) const
{ return ginfo(v).v_supp[i]; }
Halfedge_const_handle& supp_halfedge(Vertex_handle v, int i) const
{ return ginfo(v).e_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 an
interface in |PM_overlayer|. The |halfedge_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 |e_supp|
for the possible two input edges 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).
<<helping operations>>=
struct halfedge_info {
Mark m[2];
Mark mf[2];
Halfedge_const_handle e_supp[2];
bool forw;
halfedge_info()
{ m[0]=m[1]=mf[0]=mf[1]=Mark();
e_supp[0]=e_supp[1]=Halfedge_const_handle();
forw=false; }
LEDA_MEMORY(halfedge_info)
};
void assoc_info(Halfedge_handle e) const
{ geninfo<halfedge_info>::create(info(e));
geninfo<halfedge_info>::create(info(twin(e))); }
void discard_info(Halfedge_handle e) const
{ geninfo<halfedge_info>::clear(info(e));
geninfo<halfedge_info>::clear(info(twin(e))); }
halfedge_info& ginfo(Halfedge_handle e) const
{ return geninfo<halfedge_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]; }
Halfedge_const_handle& supp_halfedge(Halfedge_handle e, int i) const
// uedge information we store in the smaller one
{ if (&*e < &*(twin(e))) return ginfo(e).e_supp[i];
else return ginfo(twin(e)).e_supp[i]; }
Mark& incident_mark(Halfedge_handle e, int i) const
// biedge information we store in the halfedge
{ return ginfo(e).mf[i]; }
bool& is_forward(Halfedge_handle e) const
// biedge information we store in the halfedge
{ return ginfo(e).forw; }
@ A face just obtains two mark slots |m|.
<<helping operations>>=
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]; }
@ Finally we provide an operation cleaning up the temporary attributes
allocated above.
<<helping operations>>=
void clear_associated_info_of_all_objects() const
{
Vertex_iterator vit;
for (vit = vertices_begin(); vit != vertices_end(); ++vit)
discard_info(vit);
Halfedge_iterator hit;
for (hit = halfedges_begin(); hit != halfedges_end(); ++hit)
discard_info(hit);
Face_iterator fit;
for (fit = faces_begin(); fit != faces_end(); ++fit)
discard_info(fit);
}
@ \end{ignoreindiss}
\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 plane sweep framework. The input is an iterator pair;
the geometry is forwarded from the current class scope. Again we
elaborate on the output type. We define a class |PMO_from_pm| 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|.
The methods of |PMO_from_pm| fit the output concept requirements of
|Segment_\-overlay_\-traits|. The functionality is such that the
skeleton is created and the support information is associated with the
newly created objects. |PMO_from_pm| is a class template on the global
implementation scope as the usage of local class types (within the
scope of |PM_overlayer|) is not allowed by some current C++ compilers.
\displayeps{PMO_from_pm}{|PMO_from_pm| realizes the |Output|
concept of the generic sweep module and the |Below_info|
concept for the facet creation phase. In the figure |Vertex_handle|,
|Halfedge_handle|, and |Iterator| have been replaced by the short
symbols |V|, |E|, and |I|.}{10cm}%{14cm}
\begin{onlyindiss}
We shortly describe how the temporary information associated with the
skeleton objects is retrieved from the use of |PMO_from_pm|
(cf. Figure \figref{PMO_from_pm}) as an output model in the generic
sweep. The operations in section O1 create and link new objects in $P$
and additionally use |assoc_info()| to create the temporary storage
containers. The operations in section O2 accumulate the support
information. As an example, we show how the information for
|supp_halfedge(V,int)| is collected:
@c
void PMO_from_pm<PMD,I,FROM>::passing_segment(V v, I it)
{ int i = From[it].i;
supp_halfedge(v,i) = From[it].e;
}
@ At each event of the sweep, a vertex $v$ is created in $P$ and if
$v$ lies in the interior of the segment |*it| the above operation is
called. Thereby, after the sweep all edges from $E_i$ that support a
vertex $v$ from $V$ are associated with $v$. The remaining support is
determined similarily. The operation in section O3 is realized to
collect information that allows |PMO_from_pm| to serve as a model for
the |Below_info| concept.
\end{onlyindiss}
\begin{ignoreindiss}
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 |PMO_from_pm|.
<<PM traits classes for segment overlay>>=
template <typename PMD, typename IT, typename INFO>
struct PMO_from_pm {
<<importing decorators, handles, and point type from PMD>>
const Decorator& G;
const Const_decorator* pGI[2];
CGAL::Unique_hash_map<IT,INFO>& M;
PMO_from_pm(const Decorator& Gi,
const Const_decorator* pG0,
const Const_decorator* pG1,
CGAL::Unique_hash_map<IT,INFO>& Mi) : G(Gi),M(Mi)
{ pGI[0]=pG0; pGI[1]=pG1; }
<<PMO_from_pm topological updates>>
<<PMO_from_pm vertical ray shoot knowledge>>
<<PMO_from_pm support knowledge>>
<<PMO_from_pm face creation data access>>
}; // PMO_from_pm
<<importing decorators, handles, and point type from PMD>>=
typedef PMD Decorator;
typedef typename PMD::Const_decorator Const_decorator;
typedef typename Decorator::Vertex_handle Vertex_handle;
typedef typename Decorator::Halfedge_handle Halfedge_handle;
typedef typename Decorator::Vertex_const_handle Vertex_const_handle;
typedef typename Decorator::Halfedge_const_handle Halfedge_const_handle;
typedef typename Decorator::Point Point;
@ New vertices and halfedges are created in the plane 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|.
<<PMO_from_pm 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_halfedge_pair_at_source(v,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 |PM_overlayer| operation
|halfedge_below()|.
<<PMO_from_pm 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.
<<PMO_from_pm support knowledge>>=
void supporting_segment(Halfedge_handle e, IT it) const
{ INFO& si = M[it];
CGAL_assertion( si.e != Halfedge_const_handle() );
G.supp_halfedge(e,si.i) = si.e;
G.is_forward(e) = true;
}
@ For a vertex |v| we get to know support information. There are three
basic cases: |v| is supported by an isolated vertex, |v| is supported
by a vertex from one input structure which has incident edges
(starting or ending) during the sweep, or |v| comes to lie in the
relative interior of an input edge. In either case one of the
following operations attributes the correct support information.
<<PMO_from_pm support knowledge>>=
void trivial_segment(Vertex_handle v, IT it) const
{ INFO& si = M[it];
CGAL_assertion( si.v != Vertex_const_handle() );
G.supp_vertex(v,si.i) = si.v;
}
void starting_segment(Vertex_handle v, IT it) const
{ INFO& si = M[it];
G.supp_vertex(v,si.i) = pGI[si.i]->source(si.e);
}
void ending_segment(Vertex_handle v, IT it) const
{ INFO& si = M[it];
G.supp_vertex(v,si.i) = pGI[si.i]->target(si.e);
}
void passing_segment(Vertex_handle v, IT it) const
{ INFO& si = M[it];
G.supp_halfedge(v,si.i) = si.e;
}
@ |PMO_from_pm| also provides data access in the face creation
phase. Therefore this operation that redirects the access to
|PM_overlayer| object |G|.
<<PMO_from_pm face creation data access>>=
Halfedge_handle halfedge_below(Vertex_handle v) const
{ return G.halfedge_below(v); }
@ \end{ignoreindiss}
Now, creating the overlay is a trivial plugging of types into the
generic plane sweep framework, a creation of the sweep object with
input, output and geometry references, and a final execution of the
sweep. Afterwards, the faces are created.
<<sweeping the segments and creating the faces>>=
typedef PMO_from_pm<Self,Seg_iterator,Seg_info> Output_from_plane_maps;
typedef Segment_overlay_traits<
Seg_iterator, Output_from_plane_maps, Geometry> pm_overlay;
typedef generic_sweep< pm_overlay > pm_overlay_sweep;
Output_from_plane_maps Out(*this,&PI[0],&PI[1],From);
pm_overlay_sweep SOS(Seg_it_pair(Segments.begin(),Segments.end()),Out,K);
SOS.sweep();
create_face_objects(Out);
@ \subsection*{Transfering the marks}
After the sweep and the face creation, the input for this phase is a
plane map $P =(V,E,F)$ enriched by additional information attributed
to the 1-skeleton objects of $P$. The output vertices in $V$ are
linked to their supporting skeleton input objects (vertices and
edges). The output edges in $E$ are linked to their supporting input
edges. The support knowledge with respect to input faces is still
missing. 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 |P| that help us to do this.
\begin{itemize}\parsep0ex plus 0.0ex minus 1.0ex%
\itemsep0ex\topsep-0.5ex%
\item the vertices are constructed in the order of the sweep. By
iterating over 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 the first face $|faces_begin()|$ in the list of all faces is the
unbounded face. This holds for $P$, $P_0$, and $P_1$.
\end{itemize}
<<transfering the marks of supporting objects>>=
TRACEN("transfering marks");
<<initialize the outer face object>>
Vertex_iterator v, vend = vertices_end();
for (v = vertices_begin(); v != vend; ++v) {
TRACEN("mark at "<<PV(v));
<<determine mark of face below v>>
<<complete marks of vertex v>>
<<handle all forward oriented edges starting in v>>
}
<<transfer the marks to face objects>>
@ The transfer of face support marks is based on the following fact.
\displaylps{support}{The face support iteration unrolled. We examine
a position $p$ within the plane map $P$ and try to find the support by
a face $f_i$ in the plane map $P_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 $P_i$ can be determined by following the
dotted path until the outer unbounded face of $P_i$ is reached.}
\begin{fact}
Let $p$ be a point of the plane not part of the 1-skeleton of $P_i$,
$q$ be a point within the unbounded face of $P_i$, and $\rho$ be any
curve from $p$ to $q$ not containing any vertex of $P_i$. Assume
$\rho$ intersects an edge $e$ of the 1-skeleton of $P_i$ and let $e$
be the first such edge when following $\rho$ from $p$ to $q$. Then,
$p$ is part of the face incident to $e$. If $\rho$ does not intersect
the 1-skeleton, then $p$ is part of the unbounded face of $P_i$.
\end{fact}
The above fact is a consequence of the connectedness property of the
faces of $P_i$. We now consider point $p$ as part of $P$. For $p$ we
consider a special path $\rho$ as depicted in Figure
\figref{support}. We walk down along a vertical ray (in direction of
the negative $y$-axis). If we cross a bundle of edges incident to a
vertex $v$, the path turns just below the lowest edge and follows the
lowest edge in parallel until it is just below $v$. We iterate this
construction until it ends in a point $q$ in the unbounded face of
$P$. Each edge $e$ that is crossed by $\rho$ is supported by an edge
from either $P_i$ or $P_{1-i}$. In the former case, the first such
edge determines the face $f_i$ supporting $p$. If there is no such
edge then $p$ is supported by the unbounded face of $P_i$. We want to
determine face support for many vertices and edges, thus we do not
want to pay for such a walk for each query point $p$. Instead, we
associate with 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 $P$ that is
supported by an edge $e_i$ in $P_i$, then we associate the mark
knowledge from $e_i$ plus the mark of the supporting faces from $P_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 the support information in constant time for
vertices via their |halfedge_below| information.
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\footnote{Backward oriented edges have forward
oriented twins.}. Consider following $\rho$ in reverse with a pen
starting in $q$. Then |m_below[2]| always stores the marks of the
faces of $P_i$ that support the position of the pen. In the beginning
|m_below[i]| stores the mark of the face $f_i$ below |v|. Note that we
obtain both marks for $i=0,1$ either from the outer input faces
surrounding the plane maps $P_i$ 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.
We initialize face support for the faces of unbounded extent. These
are always the first faces of the face list of the plane map data
type.
<<initialize the outer face object>>=
Face_iterator f = faces_begin(); assoc_info(f);
for (i=0; i<2; ++i) mark(f,i) = PI[i].mark(PI[i].faces_begin());
@ Note that the iteration over all vertices |v| has the invariant that
either |v| has no halfedge below it, 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 each
vertex $v$ of $P$ knows the halfedge below it, thus the face-support
marks can be initialized in constant time.
<<determine mark of face below v>>=
Halfedge_handle e_below = halfedge_below(v);
Mark m_below[2];
if ( e_below != Halfedge_handle() ) {
for (int i=0; i<2; ++i) {
m_below[i] = incident_mark(e_below,i);
}
} else { // e_below does not exist
for (int i=0; i<2; ++i)
m_below[i] = PI[i].mark(PI[i].faces_begin());
}
@ If the vertex |v| is not supported by a skeleton object of $P_i$
then it is supported by a face. We obtain the mark of the face from
|m_below| in this case.
<<complete marks of vertex v>>=
for (i=0; i<2; ++i)
if ( supp_halfedge(v,i) != Halfedge_const_handle() ) {
mark(v,i) = PI[i].mark(supp_halfedge(v,i));
} else if ( supp_vertex(v,i) != Vertex_const_handle() ) {
mark(v,i) = PI[i].mark(supp_vertex(v,i));
} else {
mark(v,i) = m_below[i];
}
@ We have to complete the mark information for all edges of |P|. We
do the job for all forward-oriented edges in the adjacency list of
each vertex |v|. How does a halfedge |e| of |P| obtain mark
information with respect to the two input structures $P_i$? We just
have to determine the supporting objects (edge or face) from each of
both. It is either supported by two overlapping edges $e_0, e_1$ or
only supported by one edge $e_i$ and one face $f_{1-i}$. Note that a
supporting edge $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_halfedge(e,i)|. If $e$ is not supported by an edge
in $P_i$ then the mark of the input face can be obtained from
|m_below[i]|. Each supporting input edge $e_i$ of |e| changes
|m_below[i]| for the next output edge in the bundle iteration. If |e|
is not supported by an edge in $P_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 $P_i$ then |m_below[i]| contains the
mark of the face supporting $e$ in $P_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) ) {
TRACEN(" halfedge "<<PE(e));
Halfedge_const_handle ei;
bool supported;
for (int i=0; i<2; ++i) {
supported = ( supp_halfedge(e,i) != Halfedge_const_handle() );
if ( supported ) {
ei = supp_halfedge(e,i);
TRACEN(" supp halfedge "<<i<<" "<<PE(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_below[i] =
PI[i].mark(PI[i].face(ei));
} else { // no support from input PI[i]
incident_mark(twin(e),i) = mark(e,i) = incident_mark(e,i) =
m_below[i];
}
}
} else break;
}
@ 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.
<<transfer the marks to face objects>>=
for (f = ++faces_begin(); f != faces_end(); ++f) { // skip first face
assoc_info(f);
for (i=0; i<2; ++i) mark(f,i) = incident_mark(halfedge(f),i);
}
@ We can now summarize the calculated overlay properties. We
anticipate the costs of face creation as described in the next section
and the analysis of the sweep description in Lemma \ref{generic
segment sweep runtime}.
\begin{lemma}
Assume that $P_0$ and $P_1$ are plane maps whose embedding is
order-preserving and the adjacency lists have a forward prefix, then
$|subdivide|(P_0, P_1)$ constructs in $P = (V,E,F)$ the overlay plane
map of $P_0$ and $P_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 plane map.
Let $n_i$ be the size of $P_i$ and $n$ be the size of $P$. Then the
running time of the overlay process is dominated by the plane sweep of the
skeleton objects of $P_0$ and $P_1$ and is therefore $O((n_0+n_1+n)
\log (n_0+n_1+n))$.
\end{lemma}
@ \subsection{Creating face objects}\label{creating face objects}
Input to this section is the 1-skeleton of a plane map $P' = (V,E)$
whose embedding is \emph{order-preserving} and whose adjacency lists
have a \emph{forward-prefix}. The objective of this section is to
\emph{create the face objects} that complete $P'$. The correct output
structure $P = (V,E,F)$ of this section is a plane map with the
property that there are face objects $f$ in $F$ corresponding to
maximal connected point sets that are a result of the partitioning of
the plane by the 1-skeleton $P'$. All faces are defined via their
bounding face cycles. Each face object has one halfedge link into the
one unique outer face cycle (if existing), a list of halfedges each of
which represents interior hole face cycles and a list of isolated
vertices that represent trivial face cycles. To assign face cycles to
face objects we need to know two properties of the plane map skeleton:
\begin{itemize} \parsep0ex plus 0.0ex minus 1.0ex%
\itemsep0ex\topsep-0.5ex%
\item for each face cycle we need to know if it is an outer face cycle
or a hole face cycle.
\item for two face cycles |fc1| and |fc2| we need to know if we can
connect them by a path in the plane which does not cross any other
face cycle.
\end{itemize}
We adapt an idea from \cite{mmmo:CG}. The path connectivity making
disjoint face cycles bounding the same face, can be modeled by a
vertical 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 $P'$ to $P$ thereby.
Let $C$ be a set of face cycles of the plane map skeleton. For each
face cycle |c| let |MinimalHalfedge[c]| be the halfedge |e| whose
target vertex has minimal coordinates (lexicographically). Let
|FaceCycle[e]| be the face cycle containing |e|. We examine the
following implicitly defined graph $G$. Each face cycle of $P'$ is a
node of $G$. Let us link two face cycles $c_1$ and $c_2$ by an
undirected edge of $G$ if $|target|(|MinimalHalfedge|[c_1])$ has a
vertical view down to an edge of $c_2$ (in $P$). 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
the bidirected twins to be separated by an infinitesimal distance,
then the visibility is uniquely defined). Note that the embedding of a
face cycle $c$ at its minimal halfedge gives us the criterion to
separate outer face cycles and hole face cycles. Whenever the
underlying line segments of $e = |MinimalHalfedge[c]|$ and |next(e)|
form a left turn |c| is an outer face cycle. When they form a right
turn the vertex |target(e)| has a free view down and thus |e| belongs
to a hole.
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 vertical visibility 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.
\displayeps{facecycles}{Face cycles bounding a face. c1 is the outer
face cycle, c2, c3, and c4 are hole cycles, c5 is an isolated vertex.
The minimal vertices of each face cycle are the origins of the dashed
vertical arrows down.}{8cm}
The following template type parameter |Below_info| has to fit the
concept |Below_info| of the Figures \figref{PMO_from_segs} and
\figref{PMO_from_pm}.
<<helping operations>>=
template <typename Below_info>
void create_face_objects(const Below_info& D) const
{
TRACEN("create_face_objects()");
CGAL::Unique_hash_map<Halfedge_handle,int> FaceCycle(-1);
std::vector<Halfedge_handle> MinimalHalfedge;
<<link halfedges to face cycles and determine minimal halfedges>>
<<create face objects for outer face cycles and create links>>
<<link holes and isolated vertices to face objects>>
}
@ We iterate over all halfedges and assign a number for each face
cycle. After the iteration for a halfedge |e| the number of its face
cycle is |FaceCycle[e]| and for a face cycle |c| we know
|MinimalHalfedge[c]|.
<<link halfedges to face cycles and determine minimal halfedges>>=
int i=0;
Halfedge_iterator e, eend = halfedges_end();
for (e=halfedges_begin(); e != eend; ++e) {
if ( FaceCycle[e] >= 0 ) continue; // already assigned
Halfedge_around_face_circulator hfc(e),hend(hfc);
Halfedge_handle e_min = e;
TRACE("face cycle "<<i<<"\n");
CGAL_For_all(hfc,hend) {
FaceCycle[hfc]=i; // assign face cycle number
if ( K.compare_xy(point(target(hfc)), point(target(e_min))) < 0 )
e_min = hfc;
TRACE(PE(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| information is undefined are
associated with 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 temporary mark
information slots expanded.
<<create face objects for outer face cycles and create links>>=
Face_handle f_outer = new_face();
for (int j=0; j<i; ++j) {
Halfedge_handle e = MinimalHalfedge[j];
TRACEN(" face cycle "<<j);TRACEN(" minimal halfedge "<<PE(e));
Point p1 = point(source(e)),
p2 = point(target(e)),
p3 = point(target(next(e)));
if ( K.left_turn(p1,p2,p3) ) { // left_turn => outer face cycle
TRACEN(" creating new face object");
Face_handle f = new_face();
link_as_outer_face_cycle(f,e);
}
}
@ Now, the only halfedges not linked are those on hole face cycles.
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 this
final iteration all halfedges already have face links. This 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 (e = halfedges_begin(); e != eend; ++e) {
if ( face(e) != Face_handle() ) continue;
TRACEN("linking hole "<<PE(e));
Face_handle f = determine_face(e,MinimalHalfedge,FaceCycle,D);
link_as_hole(f,e);
}
Vertex_iterator v, v_end = vertices_end();
for (v = vertices_begin(); v != v_end; ++v) {
if ( !is_isolated(v) ) continue;
Halfedge_handle e_below = D.halfedge_below(v);
if ( e_below == Halfedge_handle() )
link_as_isolated_vertex(f_outer,v);
else
link_as_isolated_vertex(face(e_below),v);
}
@ When we call |determine_face|$(e,\ldots)$ we know that the halfedge
|e| is not yet linked to a face object and thus, no halfedge in its
face cycle is linked. Thus we jump to the minimal halfedge and look
down. If we see nirvana then we have to link the unlimited face
|f_outer|. If we see a halfedge we ask for its face. If it does not
have one we recur. 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_hole| 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.
Look at our example in Figure \figref{facecycles}. When
|determine_face| is called for 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 recurs to |c4| before it finds the
correct face object via the outer face cycle |c1|.
<<helping operations>>=
template <typename Below_info>
Face_handle determine_face(Halfedge_handle e,
const std::vector<Halfedge_handle>& MinimalHalfedge,
const CGAL::Unique_hash_map<Halfedge_handle,int>& FaceCycle,
const Below_info& D) const
{ TRACEN("determine_face "<<PE(e));
Halfedge_handle e_min = MinimalHalfedge[FaceCycle[e]];
Halfedge_handle e_below = D.halfedge_below(target(e_min));
if ( e_below == Halfedge_handle() ) // below is nirwana
return faces_begin();
Face_handle f = face(e_below);
if (f != Face_handle()) return f; // has face already
f = determine_face(e_below, MinimalHalfedge, FaceCycle,D);
link_as_hole(f,e_below);
return f;
}
@ The explanations of the recursion condition of |determine_face| should
convince you that:
\begin{lemma}
Assume that $P'$ is the 1-skeleton of a plane map whose embedding is
order-preserving and the adjacency lists have a forward prefix. Let
additionally all vertices know the halfedge visible along a vertical
ray shot down, then |create_face_objects()| completes |P| as a plane
map with running time \emph{linear} in the size of the 1-skeleton
|P'|.
\end{lemma}
@ \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 that was accumulated during the subdivision.
The flexibility of the operation is achieved by a template type
parameter |Selection|. An object |predicate| of type |Selection| must
provide a binary function operator returning a new mark object. The
running time of the selection phase is obviously linear in the size of
the plane map |P|. The method |discard_info| just discards the
temporarily allocated information containers associated with the
objects.
<<selection>>=
template <typename Selection>
void select(Selection& predicate) const
/*{\Mop sets the marks of all objects according to the selection
predicate |predicate|. |Selection| has to be a function object type
with a function operator\\ [[Mark operator()(Mark m0, Mark m1)]]\\ For
each object |u| of |P| enriched by the marks of the supporting objects
according to the previous procedure |subdivide|, after this operation
|\Mvar.mark(u) = predicate ( \Mvar.mark(u,0),\Mvar.mark(u,1) )|. The
additional marks are invalidated afterwards. }*/
{
Vertex_iterator vit = vertices_begin(),
vend = vertices_end();
for( ; vit != vend; ++vit) {
mark(vit) = predicate(mark(vit,0),mark(vit,1));
discard_info(vit);
}
Halfedge_iterator hit = halfedges_begin(),
hend = halfedges_end();
for(; hit != hend; ++(++hit)) {
mark(hit) = predicate(mark(hit,0),mark(hit,1));
discard_info(hit);
}
Face_iterator fit = faces_begin(),
fend = faces_end();
for(; fit != fend; ++fit) {
mark(fit) = predicate(mark(fit,0),mark(fit,1));
discard_info(fit);
}
}
@ Note that after this phase the plane map output has again the input
properties of the overlay calculation operation from Section
\ref{Overlay calculation of two plane maps}.
\begin{lemma}\label{cost of selection}
The selection phase has running time linear in the size of the plane
map.
\end{lemma}
@ \subsection{Simplification of attributed plane maps}
\displaylps{simplification}{The possible configurations for
simplification.}
In this section we examine the task of simplifying a given plane map
to reach a minimimal representation (minimimal number of objects of
the plane 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}\label{simplification criteria}
\item A vertex $v$ that is incident to two edges $e_1$, $e_2$ 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$ that 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 carries no
structural information (Figure \figref{simplification},C,D).
\end{enumerate}
\begin{onlyindiss}
Note that the simplification configurations map to the properties of
the local views of Nef faces in Lemma \ref{local view properties}.
\end{onlyindiss}
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 plays a role only if one of the faces incident to the edge
carries a different mark than the edge.
We can thus easily formulate the simplification routine. However,
there are some problems with the update operations of the plane 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 repeatedly iterating over face
cycles.
Note that we cannot just discard all faces and recreate them using a
similar scheme as the one based on the |halfedge_below| information
due to the fact that referenced edges might be deleted in the
simplification process. Thereby, face creation as described in
Section \ref{creating face objects} is not possible without a new
sweep. We take a different approach. We use a unification history
stored in a partition data structure instead of the geometrically
defined |halfedge_below| information as a criterium for linking face
cycles to face objects.
All face cycles (edges and isolated vertices) reference face
objects. When we have to unify two different faces due to the deletion
of an edge separating them, we store this fact by a union operation in
a partition data structure. The face that is finally assigned to all
the face cycles of the faces in one block is the one associated with
the canonical item of the block (obtained by the find operation).
<<simplification>>=
template <typename Keep_edge>
void simplify(const Keep_edge& keep) const
/*{\Mop simplifies the structure of |P| 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. The data accessor |keep| requires the function
call operator\\[[bool operator()(Halfedge_handle e)]]\\that allows to
avoid the simplification for edge pairs referenced by |e|.}*/
{
TRACEN("simplifying");
typedef typename CGAL::Partition<Face_handle>::item partition_item;
CGAL::Unique_hash_map<Face_iterator,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 in the faces (the links from face objects to skeleton
objects).
<<initialize blocks corresponding to faces>>=
Face_iterator f, fend = faces_end();
for (f = faces_begin(); f!= fend; ++f) {
Pitem[f] = FP.make_block(f);
clear_face_cycle_entries(f);
}
@ Now we take care of the simplification critereon (2.) of page
\pageref{simplification criteria}. We iterate over halfedge pairs
(uedges) only. When the marks of the incident faces agree with the
mark of the uedge, we union the items of the faces if they are
different. Special treatment is required for incident vertices if they
become isolated when their last incident uedge is deleted.
<<simplify via non-separating halfedges>>=
Halfedge_iterator e = halfedges_begin(), en,
eend = halfedges_end();
for(; en=e, ++(++en), e != eend; e=en) {
if ( keep(e) ) continue;
if ( mark(e) == mark(face(e)) &&
mark(e) == mark(face(twin(e))) ) {
TRACEN("deleting "<<PE(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_halfedge_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. In
each face cycle we determine the halfedge |e_min| whose target has a
minimal embedding (with respect to the lexicographic order on
points). If |e_min| and |next(e_min)| form a left turn, they are part
of an outer face cycle, otherwise they are part of a hole face
cycle. 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 != eend; ++e) {
if ( linked[e] ) continue;
Halfedge_around_face_circulator hfc(e),hend(hfc);
Halfedge_handle e_min = e;
Face_handle f = FP.inf(FP.find(Pitem[face(e)]));
CGAL_For_all(hfc,hend) {
set_face(hfc,f);
if ( K.compare_xy(point(target(hfc)), point(target(e_min))) < 0 )
e_min = hfc;
linked[hfc]=true;
}
Point p1 = point(source(e_min)),
p2 = point(target(e_min)),
p3 = point(target(next(e_min)));
if ( K.orientation(p1,p2,p3) > 0 ) set_halfedge(f,e_min); // outer
else set_hole(f,e_min); // store as inner
}
@ After the previous simplification we still have to take care of the
vertex-related simplifications (1.) and (3.). In the case that a
vertex has out-degree two, that the two incident edges are embedded
collinearly, and that all three objects have the same mark, we remove
the vertex by joining the two uedges into one. In case that a vertex
is isolated and its mark agrees with the incident face we remove the
vertex. 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 already set 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, vend = vertices_end();
for(v = vertices_begin(); v != vend; v=vn) { TRACEN("at vertex "<<PV(v));
vn=v; ++vn;
if ( is_isolated(v) ) {
if ( mark(v) == mark(face(v)) ) delete_vertex_only(v);
else set_isolated_vertex(face(v),v);
} else { // v not isolated
Halfedge_handle e2 = first_out_edge(v), e1 = previous(e2);
Point p1 = point(source(e1)), p2 = point(v),
p3 = point(target(e2));
if ( has_outdeg_two(v) &&
mark(v) == mark(e1) && mark(v) == mark(e2) &&
(K.orientation(p1,p2,p3) == 0) )
merge_halfedge_pairs_at_target(e1);
}
}
@ Finally we discard all face objects that have been victims of
unification but do not represent the unified face.
<<remove superflous face objects>>=
Face_iterator fn;
for (f = faces_begin(); f != fend; f=fn) {
fn=f; ++fn;
partition_item pit = Pitem[f];
if ( FP.find(pit) != pit ) delete_face(f);
}
@ \begin{ignoreindiss}
The following operations just wrap some basic primitives which make
our code more readable.
<<helping operations>>=
Segment segment(const Const_decorator& N,
Halfedge_const_handle e) const
{ return K.construct_segment(
N.point(N.source(e)),N.point(N.target(e))); }
Segment segment(const Const_decorator& N,
Vertex_const_handle v) const
{ Point p = N.point(v);
return K.construct_segment(p,p); }
bool is_forward_edge(const Const_decorator& N,
Halfedge_const_iterator hit) const
{ Point p1 = N.point(N.source(hit));
Point p2 = N.point(N.target(hit));
return (K.compare_xy(p1,p2) < 0); }
@ \end{ignoreindiss}
The following analysis of the partition data structure is due
to Tarjan \cite{tarjan83}.
\begin{fact}
A sequence of $m$ union and find operations starting from $n$
singleton blocks can be done in time $O(m \alpha(m,n))$ with a
partition data structure that is based on union by rank and path
compression. In this time bound $\alpha$ is the very slowly growing
inverse of a suitably defined Ackermann function.
\end{fact}
We can therefore summarize the running time of the simplification
action.
\begin{lemma}
Assume that |P| is a plane 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 $P$, $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 $P$ 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}
Note that, after the simplification, the plane map output has again
the input properties of the overlay calculation operation from Section
\ref{Overlay calculation of two plane maps}.
\begin{ignoreindiss}
<<helping operations>>=
void assert_type_precondition() const
{ typename PM_decorator_::Point p1; Point p2;
assert_equal_types(p1,p2); }
<<PM_overlayer.h>>=
<<CGAL Header>>
#ifndef CGAL_PM_OVERLAYER_H
#define CGAL_PM_OVERLAYER_H
#include <CGAL/basic.h>
#include <CGAL/Unique_hash_map.h>
#include <CGAL/Partition.h>
#include <CGAL/Nef_2/Segment_overlay_traits.h>
#include <CGAL/Nef_2/geninfo.h>
#undef _DEBUG
#define _DEBUG 13
#include <CGAL/Nef_2/debug.h>
#ifndef CGAL_USE_LEDA
#define LEDA_MEMORY(t)
#endif
CGAL_BEGIN_NAMESPACE
<<PM traits classes for segment overlay>>
<<PM overlayer>>
CGAL_END_NAMESPACE
#endif // CGAL_PM_OVERLAYER_H
@ \section{Test snippets}
\subsection{A Demo of the overlayer}
<<PM_overlayer-demo.C>>=
#include <CGAL/basic.h>
#include <CGAL/leda_integer.h>
#include <CGAL/Homogeneous.h>
#include <CGAL/IO/Window_stream.h>
#include <CGAL/Nef_2/HalfedgeDS_default.h>
#include <CGAL/Nef_2/HDS_items.h>
#include <CGAL/Nef_2/PM_decorator.h>
#include <CGAL/Nef_2/PM_io_parser.h>
#include <CGAL/Nef_2/PM_overlayer.h>
#include <CGAL/Nef_2/PM_visualizor.h>
#include <CGAL/test_macros.h>
#include "Affine_geometry.h"
// GEOMETRY:
typedef CGAL::Homogeneous<leda_integer> Hom_kernel;
typedef CGAL::Affine_geometry<Hom_kernel> Aff_kernel;
typedef Aff_kernel::Segment_2 Segment;
// PLANE MAP:
struct HDS_traits {
typedef Aff_kernel::Point_2 Point;
typedef bool Mark;
};
typedef HalfedgeDS_default<HDS_traits,HDS_items> HDS;
typedef CGAL::PM_decorator< HDS > PM_dec;
typedef CGAL::PM_overlayer< PM_dec, Aff_kernel > PM_aff_overlayer;
typedef PM_dec::Halfedge_handle Halfedge_handle;
typedef PM_dec::Vertex_handle Vertex_handle;
// INPUT:
typedef std::list<Segment>::const_iterator Iterator;
struct Object_DA {
const PM_dec& D;
Object_DA(const PM_dec& Di) : D(Di) {}
void supporting_segment(Halfedge_handle e, Iterator it) const
{ D.mark(e) = true; }
void trivial_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
void starting_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
void passing_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
void ending_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
};
struct BOP {
bool operator()(const bool& b1, const bool& b2) const
{ return b1||b2; }
};
int main(int argc, char* argv[])
{
SETDTHREAD(331);
CGAL::set_pretty_mode(cerr);
CGAL::Window_stream W;
W.init(-50,50,-50,1);
W.set_show_coordinates(true);
W.display();
HDS H[2], HO;
char C[2] = { '0','1' };
for (int i=0; i<2; ++i ) {
W.message("insert segments to construct a map.");
PM_aff_overlayer PMOV(H[i],Aff_kernel());
CGAL::PM_visualizor<PM_aff_overlayer,Aff_kernel> V(W,PMOV);
std::list<Segment> L;
Segment s;
std::string fname;
if ( argc == 2 ) {
fname = std::string(argv[1]);
fname += C[i];
std::ifstream log(fname.c_str());
while ( log >> s ) { L.push_back(s); W << s; }
}
while ( W >> s ) L.push_back(s);
fname = std::string(argv[0]);
fname += ".log";
fname += C[i];
std::ofstream log(fname.c_str());
for (Iterator sit = L.begin(); sit != L.end(); ++sit)
log << *sit << " ";
log.close();
Object_DA ODA(PMOV);
PMOV.create(L.begin(),L.end(),ODA);
CGAL::PM_io_parser<PM_aff_overlayer>::dump(PMOV);
V.draw_skeleton();
W.read_mouse();
W.clear();
}
PM_aff_overlayer PMOV(HO,Aff_kernel());
CGAL::PM_visualizor<PM_aff_overlayer,Aff_kernel> V(W,PMOV);
PMOV.subdivide(H[0],H[1]);
BOP bop;
PMOV.select(bop);
PMOV.simplify();
V.draw_skeleton();
CGAL::PM_io_parser<PM_aff_overlayer>::dump(PMOV);
W.read_mouse();
return 0;
}
@ \subsection{A Test of the overlayer}
<<PM_overlayer-test.C>>=
#include <CGAL/basic.h>
#include <CGAL/leda_integer.h>
#include <CGAL/Homogeneous.h>
#include <CGAL/IO/Window_stream.h>
#include <CGAL/Nef_2/HalfedgeDS_default.h>
#include <CGAL/Nef_2/HDS_items.h>
#include <CGAL/Nef_2/PM_decorator.h>
#include <CGAL/Nef_2/PM_io_parser.h>
#include <CGAL/Nef_2/PM_overlayer.h>
#include <CGAL/Nef_2/PM_visualizor.h>
#include <CGAL/test_macros.h>
#include "Affine_geometry.h"
// GEOMETRY:
typedef CGAL::Homogeneous<leda_integer> Hom_kernel;
typedef CGAL::Affine_geometry<Hom_kernel> Aff_kernel;
typedef Aff_kernel::Point_2 Point;
typedef Aff_kernel::Segment_2 Segment;
// PLANE MAP:
struct HDS_traits {
typedef Aff_kernel::Point_2 Point;
typedef bool Mark;
};
typedef CGAL::HalfedgeDS_default<HDS_traits,HDS_items> HDS;
typedef CGAL::PM_decorator< HDS > PM_dec;
typedef CGAL::PM_overlayer< PM_dec, Aff_kernel > PM_aff_overlayer;
typedef PM_dec::Halfedge_handle Halfedge_handle;
typedef PM_dec::Vertex_handle Vertex_handle;
// INPUT:
typedef std::list<Segment>::const_iterator Iterator;
struct Object_DA {
const PM_dec& D;
Object_DA(const PM_dec& Di) : D(Di) {}
void supporting_segment(Halfedge_handle e, Iterator it) const
{ D.mark(e) = true; }
void trivial_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
void starting_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
void passing_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
void ending_segment(Vertex_handle v, Iterator it) const
{ D.mark(v) = true; }
};
struct BOP {
bool operator()(const bool& b1, const bool& b2) const
{ return b1||b2; }
};
int main(int argc, char* argv[])
{
SETDTHREAD(131); // 13 = PM overlayer debug on
CGAL::set_pretty_mode(cerr);
CGAL_TEST_START;
//#define PMOVISUAL
#ifdef PMOVISUAL
CGAL::Window_stream W;
W.init(-50,50,-50,1);
W.set_show_coordinates(true);
W.display();
#endif
HDS H[2], HO, HOC;
std::list<Segment> L0,L1,L2;
L0.push_back(Segment(Point(-5,-5),Point(5,-5)));
L0.push_back(Segment(Point(5,-5),Point(5,5)));
L0.push_back(Segment(Point(5,5),Point(-5,5)));
L0.push_back(Segment(Point(-5,5),Point(-5,-5)));
L0.push_back(Segment(Point(0,0),Point(0,0)));
PM_aff_overlayer PMOV0(H[0],Aff_kernel());
Object_DA ODA0(PMOV0);
PMOV0.create(L0.begin(),L0.end(),ODA0);
//CGAL::PM_io_parser<PM_aff_overlayer>::dump(PMOV0);
L1.push_back(Segment(Point(-5,-5),Point(0,-5)));
L1.push_back(Segment(Point(0,-5),Point(0,0)));
L1.push_back(Segment(Point(0,0),Point(-5,0)));
L1.push_back(Segment(Point(-5,0),Point(-5,-5)));
L1.push_back(Segment(Point(-2,-2),Point(-2,-2)));
PM_aff_overlayer PMOV1(H[1],Aff_kernel());
Object_DA ODA1(PMOV1);
PMOV1.create(L1.begin(),L1.end(),ODA1);
//CGAL::PM_io_parser<PM_aff_overlayer>::dump(PMOV1);
PM_aff_overlayer PMOV(HO,Aff_kernel());
PMOV.subdivide(H[0],H[1]);
BOP bop;
PMOV.select(bop);
PMOV.simplify();
L2.insert(L2.end(),L0.begin(),L0.end());
L2.insert(L2.end(),L1.begin(),L1.end());
PM_aff_overlayer PMOV2(HOC,Aff_kernel());
Object_DA ODA2(PMOV2);
PMOV2.create(L2.begin(),L2.end(),ODA2);
CGAL_TEST(PMOV.number_of_vertices()==8);
CGAL_TEST(PMOV.number_of_edges()==8);
CGAL_TEST(PMOV.number_of_faces()==3);
#ifdef PMOVISUAL
CGAL::PM_visualizor<PM_aff_overlayer,Aff_kernel> V(W,PMOV);
CGAL::PM_io_parser<PM_aff_overlayer>::dump(PMOV);
V.draw_skeleton();
W.read_mouse();
#endif
CGAL_TEST_END;
return 0;
}
@ \begin{ignore}
<<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_2/PM_overlayer.h
// package : Nef_2
// chapter : Nef Polyhedra
//
// source : nef_2d/PM_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 plane maps
// ============================================================================
@ \end{ignore}
\end{ignoreindiss}
%KILLSTART DISS REP
\bibliographystyle{alpha}
\bibliography{diss,general,geo_mod,comp_geo}
\newpage
\section{Appendix}
\input manpages/OverlayerGeometry_2.man
\input manpages/PMConstDecorator.man
\input manpages/PMDecorator.man
\input manpages/geninfo.man
\end{document}
%KILLEND DISS REP