mirror of https://github.com/CGAL/cgal
1120 lines
44 KiB
Plaintext
1120 lines
44 KiB
Plaintext
%------------------------------------------------------------------------------
|
|
%KILLSTART DISS REP
|
|
%LDEL TRACE.*?\)\;
|
|
%LDEL CGAL_BEGIN_NAMESPACE.*
|
|
%LDEL CGAL_END_NAMESPACE.*
|
|
\documentclass[a4paper]{article}
|
|
\usepackage{MyLweb}
|
|
\input{defs}
|
|
\excludeversion{ignoreindiss}
|
|
\excludeversion{ignore}
|
|
|
|
\begin{document}
|
|
\title{The constrained triangulation of a plane map}
|
|
\author{M. Seel}
|
|
\date{}
|
|
\maketitle
|
|
|
|
\begin{abstract} We implement a plane sweep algorithm to compute the
|
|
constrained triangulation of a plane map. We use a generic sweep
|
|
approach which is based on a geometric kernel concept and on a plane
|
|
map decorator concept.
|
|
\end{abstract}
|
|
\tableofcontents
|
|
|
|
%KILLEND REP
|
|
|
|
\section{Introduction}
|
|
|
|
We first introduce the notions or triangulation and constraining
|
|
segments. Then we present the algorithmic ideas to construct a
|
|
constrained triangulation by a plane sweep algorithm. In the next
|
|
section the concrete implementation is shown.
|
|
|
|
\begin{deff}[Triangulation \cite{prep-sham:CG}]
|
|
A planar subdivision is a triangulation $T$ if all its bounded regions
|
|
are triangles. A triangulation of a finite set $S$ of points is a
|
|
planar graph $T(S)$ with the maximum number of edges, which is
|
|
equivalent to saying that $T(S)$ is obtained by joining the points of
|
|
$S$ by non-intersecting straight line segments so that every region
|
|
internal to the convex hull of $S$ is a triangle. \end{deff}
|
|
|
|
\begin{deff}[Constrained Triangulation]
|
|
Let $S$ be a finite set of segments which have no pairwise common
|
|
points except in their endpoints. A constrained triangulation $CT(S)$
|
|
is a triangulation of the endpoints of the segments in $S$ such that
|
|
each non-trivial segment of $S$ corresponds to an edge in
|
|
$CT(S)$. Note that we allow trivial segments. \end{deff}
|
|
|
|
\newcommand{\seg}{\mathit{segment}}
|
|
\newcommand{\point}{\mathit{point}}
|
|
\newcommand{\rev}{\mathit{rev}}
|
|
\newcommand{\source}{\mathit{source}}
|
|
\newcommand{\target}{\mathit{target}}
|
|
|
|
We sweep the edges and vertices of of a plane map $G=(V,E)$ to compute
|
|
the constrained triangulation $CT(G) := CT(S = \seg(E)\cup
|
|
\point(V))$\footnote{We slopily write $\point(V)$ for the set of all
|
|
embedding points of the vertices in $V$ and $\seg(E)$ for the set of
|
|
segments spanned by the end vertices of the edges in $E$.}. Actually
|
|
we extend $G_{in}$ until finally $G_{out} \equiv CT(G_{in})$.
|
|
|
|
What does this mean? We want to obtain a triangulation of the point
|
|
set $\point(V)$ which additionally obeys the constraints given by the
|
|
segments $\seg(E)$ of the graph. In the following description we
|
|
identify the vertices with their positions and the edges with the
|
|
segments spanned by the positions of their end points.
|
|
|
|
If the graph only contains isolated vertices we calculate the standard
|
|
triangulation given by the standard sweep triangulation algorithm.
|
|
(see for example a description in the LEDA book \cite{ledabook}).
|
|
This triangulation has the property that any vertical line $l$
|
|
subdivides the triangulation into a correctly constructed part left of
|
|
it and the rest. (Fixing $l$ just remove all triangles which are
|
|
intersected by it in their interior or which are right of it). This is
|
|
just the result of the invariant kept during the sweep.
|
|
|
|
So what is different if we calculate the constrained triangulation?
|
|
Our sweepline |SL| contains all edges (segments) that currently
|
|
intersect it ordered by their intersection points from bottom to
|
|
top. We use a sorted sequence to store the edges. Let's conceptually
|
|
identify the line with the data structure. Note that edges might
|
|
touch in their endpoints as we work on a correctly embedded plane
|
|
map. Also the vertices do not come to lie in the interior of a
|
|
non-adjacent edge. We sweep the graph $G$ and extend it by additional
|
|
edges to create the triangulation. Each input edge encountered during
|
|
the sweep from $-\infty$ to $\infty$ (along the x-axis) enters |SL|
|
|
that stores its position in the sweepline. At the top and the bottom
|
|
of |SL| we use sentinel segments to avoid the handling of boundary
|
|
cases. Note that with the above idea all edges in the sweepline are
|
|
already connected via face cycles (paths) in the extended graph. At
|
|
the end of the sweep we remove the sentinel edges.
|
|
|
|
What is our basic invariant? We have already calculated a correct
|
|
constrained triangulation of all vertices and edges that are fully
|
|
left of the sweepline including a closure implied by the
|
|
sweepline. Let's first assume that we don't face degeneracies like
|
|
equal $x$-coordinates or vertical segments. We will comment on the
|
|
removal of this restriction later.
|
|
|
|
\begin{deff}[Restricted Constrained Triangulation]
|
|
Let |SL| be the vertical sweepline defined by an event point $p$ in
|
|
the plane. Let $S[<|SL|] \subseteq S$ be the finite set of segments
|
|
already handled completely (all vertices and edges of our input graph
|
|
fully left of |SL|). Let $S[SL]$ be the set of segments spanned by
|
|
edges intersecting |SL|, but restricted to the closed halfplane left
|
|
of |SL|. We define the restricted contrained triangulation of the
|
|
edges encountered so far to be $CT[\leq|SL|] := CT(S[<|SL|] \cup
|
|
S[|SL|])$. Accordingly we talk about the plane map $G[\leq|SL|]$
|
|
restricted to the closed halfplane left of |SL|, consisting of all
|
|
vertices in the closed halfplane and edges connecting them.
|
|
\end{deff}
|
|
|
|
Note that $CT[\leq|SL|]$ is a valid constrained triangulation that has
|
|
a part of its hull on |SL|. We store this triangulation in a special
|
|
way. All triangulation edges which are finished are already stored in
|
|
our output graph $G[\leq|SL|]$. Finished here means that they connect
|
|
vertices in $G[\leq|SL|]$ totally left of |SL|. The missing edges
|
|
completing $G[\leq|SL|]$ to $CT(S[\leq|SL|])$ can be seen as stored
|
|
implicitly in our structures. We will explain this in a moment.
|
|
|
|
\displayeps{ct1}{The triangles already finished during a
|
|
sweep. Constraining segments are black. Additional triangulation edges
|
|
are grey.}{6cm}
|
|
|
|
\displaylps{chain}{The chain between two segments in |SL| at a point
|
|
|p|.}
|
|
|
|
When looking at the actions at a sweep event we'll see that we extend
|
|
$G[\leq|SL|]$ in a way that it always represents a maximal subgraph of
|
|
the final triangulation. It holds for example that $G[\leq|SL|]$ is
|
|
connected. Each edge $e$ in |SL| is connected in $G[\leq|SL|]$ to the
|
|
edge $e_s$ representing the successor edge in |SL|. Actually there's a
|
|
face cycle $C = e_1, \ldots , e_k$ where $e_1 = |next|(|twin|(e_s))$
|
|
and $e_k = $|previous|$(e)$ in our bidirected representation of $G$
|
|
which proves connectivity. See figure \figref{chain}. This chain of
|
|
edges has the additional property that it can be split into two
|
|
x-monotone parts $C_1 = e_1,\ldots,e_i$ and $C_2 = e_{i+1},\ldots,e_k$
|
|
where the vertex $v_{vis} = source(e_{i+1})$ is a point of maximal
|
|
x-coordinate visible by any point on |SL| between $e$ and its
|
|
successor $e_s$ in |SL|. Moreover the slope of the lines that support
|
|
the edges in $C_1$ and $C_2$ is non-increasing.
|
|
|
|
We associate the edge $e_{vis} = e_{i+1}$ with source $v_{vis}$ to $e$
|
|
to allow fast access into the chain for any pair of neigbored segments
|
|
in |SL|. By $e_{vis}$ we have a starting point for a visibility search
|
|
along the x-monotone chains in which $v_{vis}$ is an extreme vertex.
|
|
|
|
We come back to our representation of
|
|
$CT(S[\leq|SL|])$. $CT(S[\leq|SL|])$ consists of the explicitly
|
|
constructed part $G[\leq|SL|]$ and an implicitly possible
|
|
triangulation between each pair of edges $e$, $e_s$ as depicted dashed
|
|
in figure \figref{chain}. Just connect
|
|
$|target|(|segment|(e)[\leq|SL|])$ to all vertices of $C_2$ and
|
|
symmetrically $|target|(|segment|(e_s)[\leq|SL|])$ to all vertices of
|
|
$C_1$ and both via |SL|.
|
|
|
|
We will show for the event handling procedure that we keep the
|
|
explicit and implicit structure consistent. Thus when reaching the end
|
|
of our scenery where only our global sentinel edges are present (which
|
|
frame the whole scenery) then the explicit part of $G$ contains the
|
|
correct constrained triangulation of the input.
|
|
|
|
\subsection*{Invariants}\label{invariants}
|
|
We recapitulate our implementation invariants:
|
|
\begin{enumerate}
|
|
\item All edges intersecting the vertical line through the current
|
|
event point are stored in |SL| ordered according to their
|
|
intersection points bottom-up. We create hash links from these edges
|
|
to their item in |SL| realized by a hash map |SLItem|. This map
|
|
serves also as a flag of the edges intersected by the sweepline. We
|
|
set this flag when entering and reset it to the default when leaving
|
|
|SL|.
|
|
\item For two edges $e, e_s$ that are neighbors in |SL| we
|
|
maintain the chain of edges $C = e_1, \ldots, e_k$ stored as a face
|
|
cycle in the output graph where $|source|(e_1) == |source|(e_s)$ and
|
|
$|target|(e_k) == |source|(e)$. When closing $G[\leq|SL|]$ with our
|
|
implicit construction, then we obtain a valid constrained
|
|
triangulation of the objects in the closed halfspace left of |SL|.
|
|
|
|
\item Each edge $e$ in |SL| knows a vertex $v_{vis}$ visible
|
|
by any point on |SL| between $e$ and its successor $e_s$ in
|
|
|SL|. We realize this knowledge by associating an edge
|
|
$e_{vis} \in C$ to it with source $v_{vis}$. By $e_{vis}$ we have a
|
|
starting point for a visibility search along the chain $C$ in which
|
|
$v_{vis}$ is an extreme vertex.
|
|
\end{enumerate}
|
|
All kinds of degeneracies like equal x-coordinates and vertical
|
|
segments can be integrated into the code if you imagine to twist the
|
|
whole scenery clockwise by an infinitesimal angle. This implies that
|
|
vertices are ordered lexicographically and vertical segments belong to
|
|
a starting bundle of edges. Segments touching in one point are handled
|
|
explicitly by an iteration over all edges starting in a vertex of the
|
|
input graph.
|
|
|
|
\section{Implementation}
|
|
|
|
We use our generic sweep framework but open the algorithm furtheron by
|
|
three template parameters of the sweep traits class
|
|
|Constrained_triang_traits<>|. See the concept |GenericSweepTraits|
|
|
that has to be implemented in the appendix section
|
|
\ref{GenericSweepTraits}. We want to factor out the manipulation of
|
|
the plane map, the geometric types and predicates used and the action
|
|
which can be invoked on the additionally created triangulation edges
|
|
in |CT|. Thus we add a concept for a plane map decorator |PMDEC|, a
|
|
concept for a geometric kernel |GEOM| and a concept for a new-edge
|
|
data accessor |NEWEDGE|. Note that the class provides the types and
|
|
members according to the generic plane sweep traits concept
|
|
|GenericSweepTraits|.
|
|
|
|
\displayeps{Constrained_triang}{The design of the constrained
|
|
triangulation module. |Constrained_triang_traits<>| implements the
|
|
concept |GenericSweepTraits|. The three template parameters allow an
|
|
adaptation of the input/output plane map, the geometry, and the
|
|
processing of new edges.}{10cm}
|
|
<<class Constrained_triang_traits>>=
|
|
|
|
template <typename PMDEC, typename GEOM,
|
|
typename NEWEDGE = Do_nothing>
|
|
class Constrained_triang_traits : public PMDEC {
|
|
public:
|
|
<<type definition for the class scope>>
|
|
<<order predicate definition>>
|
|
<<local types for the sweep>>
|
|
<<helping operations>>
|
|
|
|
<<ct event handling>>
|
|
<<ct initialization>>
|
|
<<ct cleaning up>>
|
|
<<ct checking>>
|
|
}; // Constrained_triang_traits<PMDEC,GEOM,NEWEDGE>
|
|
|
|
@ \begin{ignoreindiss}
|
|
<<Constrained_triang_traits.h>>=
|
|
<<CGAL Header1>>
|
|
// file : include/CGAL/Nef_2/Constrained_triang_traits.h
|
|
<<CGAL Header2>>
|
|
#ifndef CGAL_PM_CONSTR_TRIANG_TRAITS_H
|
|
#define CGAL_PM_CONSTR_TRIANG_TRAITS_H
|
|
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/Unique_hash_map.h>
|
|
#include <CGAL/generic_sweep.h>
|
|
#include <CGAL/Nef_2/PM_checker.h>
|
|
#include <string>
|
|
#include <map>
|
|
#include <set>
|
|
#undef _DEBUG
|
|
#define _DEBUG 19
|
|
#include <CGAL/Nef_2/debug.h>
|
|
|
|
CGAL_BEGIN_NAMESPACE
|
|
<<some predefinitions>>
|
|
<<class Constrained_triang_traits>>
|
|
CGAL_END_NAMESPACE
|
|
#endif // CGAL_PM_CONSTR_TRIANG_TRAITS_H
|
|
|
|
@ \end{ignoreindiss} The geometry concept |GEOM| allows us to import
|
|
the geometric types into |Constrained_triang_traits| and offers the
|
|
necessary predicates as methods. See |AffineGeometryTraits_2| in the
|
|
appendix for the concept description. The plane map decorator |PMDEC|
|
|
exports the handle, iterator and circulator types into the class scope
|
|
and provides all plane map exploration and manipulation methods. See
|
|
|PMDecorator| in the appendix for the concept description. We inherit
|
|
from |PMDEC| to obtain its methods into the class scope.
|
|
<<type definition for the class scope>>=
|
|
typedef Constrained_triang_traits<PMDEC,GEOM,NEWEDGE> Self;
|
|
typedef PMDEC Base;
|
|
|
|
// the types interfacing the sweep:
|
|
typedef NEWEDGE INPUT;
|
|
typedef typename PMDEC::Plane_map OUTPUT;
|
|
typedef GEOM GEOMETRY;
|
|
|
|
typedef typename GEOM::Point_2 Point;
|
|
typedef typename GEOM::Segment_2 Segment;
|
|
typedef typename GEOM::Direction_2 Direction;
|
|
|
|
typedef typename Base::Halfedge_handle Halfedge_handle;
|
|
typedef typename Base::Vertex_handle Vertex_handle;
|
|
typedef typename Base::Face_handle Face_handle;
|
|
typedef typename Base::Halfedge_iterator Halfedge_iterator;
|
|
typedef typename Base::Vertex_iterator Vertex_iterator;
|
|
typedef typename Base::Face_iterator Face_iterator;
|
|
typedef typename Base::Halfedge_base Halfedge_base;
|
|
typedef typename Base::Halfedge_around_vertex_circulator
|
|
Halfedge_around_vertex_circulator;
|
|
|
|
@ We have two sentinel edges which are minimum and maximum in our
|
|
order by identity, we store them in a reference. Note that we need
|
|
access to our plane map decorator and to our geometric kernel. The
|
|
point |p| determines the sweepline position.
|
|
<<order predicate definition>>=
|
|
class lt_edges_in_sweepline : public PMDEC
|
|
{ const Point& p;
|
|
const Halfedge_handle& e_bottom;
|
|
const Halfedge_handle& e_top;
|
|
const GEOMETRY& K;
|
|
public:
|
|
lt_edges_in_sweepline(const Point& pi,
|
|
const Halfedge_handle& e1, const Halfedge_handle& e2,
|
|
const PMDEC& D, const GEOMETRY& k) :
|
|
PMDEC(D), p(pi), e_bottom(e1), e_top(e2), K(k) {}
|
|
|
|
lt_edges_in_sweepline(const lt_edges_in_sweepline& lt) :
|
|
PMDEC(lt), p(lt.p), e_bottom(lt.e_bottom), e_top(lt.e_top), K(lt.K) {}
|
|
|
|
Segment seg(const Halfedge_handle& e) const
|
|
{ return K.construct_segment(point(source(e)),point(target(e))); }
|
|
|
|
int orientation(Halfedge_handle e, const Point& p) const
|
|
{ return K.orientation(point(source(e)),point(target(e)),p); }
|
|
|
|
<<function call member for order of two halfedges>>
|
|
|
|
}; // lt_edges_in_sweepline
|
|
|
|
@ The order predicate on edges is only based on point equality and the
|
|
orientation predicate on points. We have two sentinel edges which are
|
|
minimum and maximum in our order by identity. For all geometric cases
|
|
we have the precondition that the source vertex of one of the edges is
|
|
equal to the current event point |p_sweep|. The order predicate is
|
|
only used in search operation at |p_sweep| and for the insertion of
|
|
new edges starting there, thus our requirement is legal.
|
|
<<function call member for order of two halfedges>>=
|
|
bool operator()(const Halfedge_handle& e1, const Halfedge_handle& e2) const
|
|
{ // Precondition:
|
|
// [[p]] is identical to the source of either [[e1]] or [[e2]].
|
|
if (e1 == e_bottom || e2 == e_top) return true;
|
|
if (e2 == e_bottom || e1 == e_top) return false;
|
|
if ( e1 == e2 ) return 0;
|
|
int s = 0;
|
|
if ( p == point(source(e1)) ) s = orientation(e2,p);
|
|
else if ( p == point(source(e2)) ) s = - orientation(e1,p);
|
|
else error_handler(1,"compare error in sweep.");
|
|
if ( s || source(e1) == target(e1) || source(e2) == target(e2) )
|
|
return ( s < 0 );
|
|
s = orientation(e2,point(target(e1)));
|
|
if (s==0) error_handler(1,"parallel edges not allowed.");
|
|
return ( s < 0 );
|
|
}
|
|
|
|
@ The order predicate on vertices maps to the lexicographic order on
|
|
their embedding.
|
|
<<order predicate definition>>=
|
|
class lt_pnts_xy : public PMDEC
|
|
{ const GEOMETRY& K;
|
|
public:
|
|
lt_pnts_xy(const PMDEC& D, const GEOMETRY& k) : PMDEC(D), K(k) {}
|
|
lt_pnts_xy(const lt_pnts_xy& lt) : PMDEC(lt), K(lt.K) {}
|
|
int operator()(const Vertex_handle& v1, const Vertex_handle& v2) const
|
|
{ return K.compare_xy(point(v1),point(v2)) < 0; }
|
|
}; // lt_pnts_xy
|
|
|
|
|
|
@ We use an STL map for the |SL| and a set for the |event_Q|.
|
|
Note that we use the iterators for local movement in |SL| but
|
|
also as handles to the items therein which store the halfedge
|
|
objects. Whenever we talk about items we misuse iterators for that
|
|
concept. We store always the forward oriented halfedges.
|
|
<<local types for the sweep>>=
|
|
typedef std::map<Halfedge_handle, Halfedge_handle, lt_edges_in_sweepline>
|
|
Sweep_status_structure;
|
|
typedef typename Sweep_status_structure::iterator ss_iterator;
|
|
typedef typename Sweep_status_structure::value_type ss_pair;
|
|
typedef std::set<Vertex_iterator,lt_pnts_xy> Event_Q;
|
|
typedef typename Event_Q::const_iterator event_iterator;
|
|
|
|
const GEOMETRY& K;
|
|
Event_Q event_Q;
|
|
event_iterator event_it;
|
|
Vertex_handle event;
|
|
Point p_sweep;
|
|
Sweep_status_structure SL;
|
|
CGAL::Unique_hash_map<Halfedge_handle,ss_iterator> SLItem;
|
|
const NEWEDGE& Treat_new_edge;
|
|
Halfedge_handle e_low,e_high; // framing edges !
|
|
Halfedge_handle e_search;
|
|
|
|
Constrained_triang_traits(const INPUT& in, OUTPUT& out, const GEOMETRY& k)
|
|
: Base(out), K(k), event_Q(lt_pnts_xy(*this,K)),
|
|
SL(lt_edges_in_sweepline(p_sweep,e_low,e_high,*this,K)),
|
|
SLItem(SL.end()), Treat_new_edge(in)
|
|
{ TRACEN("Constrained Triangulation Sweep"); }
|
|
|
|
|
|
@ \subsection{Event Handling}
|
|
|
|
\newcommand{\pred}{\mathit{pred}}
|
|
\renewcommand{\succ}{\mathit{succ}}
|
|
\newcommand{\vis}{\mathit{vis}}
|
|
|
|
We have to traverse a vertex $v$ of our input graph by the
|
|
sweepline. Let's first assume this is an \emph{isolated vertex}
|
|
appearing between two edges in |SL|. Note that between the two edges
|
|
$e, e_s$ we have a local chain of edges connecting $v_1 = \source(e)$
|
|
and $v_2 = \source(e_s)$ which is similar to the global convex hull
|
|
chain of the unconstrained triangulation problem. Note that this chain
|
|
can be even empty if $v_1 = v_2$. In between the segments in the
|
|
sweepline we know a vertex $v_{\vis}$ of maximal coordinates which is
|
|
visible from any point on |SL| between $e$ and $e_s$. What happens if
|
|
we encounter a new event $v$ (a vertex at |p_sweep|)? We locate the
|
|
edge $e$ below $v$, and its successor $e_s$. We obtain the correct
|
|
$v_{\vis}$ and an edge $e_{\vis}$ out of $v_{\vis}$ which is in the
|
|
face cycle partly visible from $v$. Then we determine the visible
|
|
chain of edges starting in $e_{\vis}$ as seen from $v$. Both ends are
|
|
determined either by a non-visible edge or by an edge intersecting the
|
|
sweepline (thus equal to $e$ or $e_s$). For all the edges in the chain
|
|
we have to produce new triangles with apex $v$.
|
|
|
|
\displaylps{ct_conf}{The four sweep configurations}
|
|
|
|
Now the general case where the vertex $v$ can be the \emph{center of a
|
|
star of edges}. Note that all edges are embedded around $v$ in
|
|
counterclockwise order. This means that some subsequence of the
|
|
adjacency list is a sequence of starting edges and the corresponding
|
|
complement is the sequence of ending edges. For the actions we have to
|
|
consider four configurations as shown in figure \figref{ct_conf}.
|
|
Note that in case \textbf{A} we encounter an ending bundle. We have to
|
|
triangulate up and down along the limiting edges along the arrows as
|
|
long as the edges are visible or until they are crossing the
|
|
sweepline. We also have to remove the ending edges from the
|
|
sweepline. For the edge in |SL| below the sweep point we have to
|
|
update the event vertex $v$ as its visible entry vertex into the
|
|
graph. In case \textbf{B} we have only a starting bundle of edges. We
|
|
first have to link $v$ (at the sweep point) to the visible vertex
|
|
$v_{\vis}$ in the edge chain connecting the segments above and below
|
|
the sweep point. Then we start the same actions as in case \textbf{A}
|
|
along the chain of visible edges. Finally we insert all starting edges
|
|
into |SL|. The cases \textbf{C} and \textbf{D} are combinations of the
|
|
above. In \textbf{C} we first act as in \textbf{A} and then as in
|
|
\textbf{B}. The isolated vertex in \textbf{D} is handled as in
|
|
\textbf{B} but there are no edges starting.
|
|
|
|
\displaylps{preevent}{The three geometric configurations of |v| at
|
|
|p_sweep| with respect to $e$ and $e_s$ and the face cycle in between
|
|
before the triangular extension.}
|
|
|
|
What are we doing concerning our invariants?
|
|
\begin{itemize}
|
|
\item Between each pair of edges $e, e_s$ in |SL| we extend the chain
|
|
where the target of $e$ or $e_s$ is the event vertex or $e$ is just
|
|
below |p_sweep|, by triangles with apex $v$. Now $v$ becomes the
|
|
visible vertex in at least one face cycle between two edges in
|
|
|SL|. See figure \figref{preevent}. $v$ can be isolated as in
|
|
\textbf{A}, or connected to $G$ by constraining edges as in \textbf{B}
|
|
or \textbf{B}. There's a symmetric case for \textbf{B} when
|
|
$|target|(e_s) == v$. The latter cases can occur combined if the
|
|
ending bundle of edges consists of more than one edge.
|
|
|
|
If you look into the code below, convince yourself that the chains are
|
|
afterwards again x-monotone and follow our slope criterion. Note also
|
|
that in case \textbf{A} we connect $v$ to $G[\leq|SL|]$ by new
|
|
triangles and thus the whole graph is again connected.
|
|
|
|
Now look at figure \figref{postevent} for the situation after the
|
|
event. In all three cases our implicit structure $CT[\leq|SL|]$ is
|
|
again consistently defined between $e$ and $e_s$. Note that
|
|
combinations of the cases \textbf{B} and \textbf{C} are possible if
|
|
the outgoing bundle of edges consists of more than one edge.
|
|
|
|
\item We remove the edges ending at |v| from |SL| and reset the
|
|
|SLItem| link to the default; we insert some edges starting at |v|
|
|
into |SL| and set the |SLItem| link accordingly. Thus invariant 1
|
|
holds.
|
|
|
|
\item We have to adjust the visibility information of those edges in
|
|
|SL| that are below or above $v$ or that have just been inserted into
|
|
|SL|. Thus invariant 3 is ensured.
|
|
\end{itemize}
|
|
|
|
\displaylps{postevent}{The three geometric configurations of |v| at
|
|
|p_sweep| with respect to $e$ and $e_s$ and the visible vertex
|
|
invariant after the insertion of new edges.}
|
|
|
|
Now let's do some coding. For the visibility predicate we use the
|
|
geometric orientation test provided by the geometric kernel.
|
|
<<ct event handling>>=
|
|
bool edge_is_visible_from(Vertex_handle v, Halfedge_handle e)
|
|
{
|
|
Point p = point(v);
|
|
Point p1 = point(source(e));
|
|
Point p2 = point(target(e));
|
|
return ( K.orientation(p1,p2,p)>0 ); // left_turn
|
|
}
|
|
|
|
@ The following operations are used to enrich the output plane map by
|
|
all the edges completing the triangulation. We only create triangles
|
|
looking backward from an event. We always start from an edge |e_apex|
|
|
linking the event vertex to the up to now triangulated plane map. We
|
|
triangulate away from |e_apex| until we can't see the examined face
|
|
cycle edge or until the edge crosses the sweepline from left to
|
|
right. For the visibility test we use the above predicate, for the
|
|
"edge-crosses-sweepline" test we can use our map |SLItem| as a flag.
|
|
<<ct event handling>>=
|
|
void triangulate_up(Halfedge_handle& e_apex)
|
|
{
|
|
TRACEN("triangulate_up "<<seg(e_apex));
|
|
Vertex_handle v_apex = source(e_apex);
|
|
while (true) {
|
|
Halfedge_handle e_vis = previous(twin(e_apex));
|
|
bool in_sweep_line = (SLItem[e_vis] != SL.end());
|
|
bool not_visible = !edge_is_visible_from(v_apex,e_vis);
|
|
TRACEN(" checking "<<in_sweep_line<<not_visible<<" "<<seg(e_vis));
|
|
if ( in_sweep_line || not_visible) {
|
|
TRACEN(" STOP"); return;
|
|
}
|
|
Halfedge_handle e_back = new_bi_edge(e_apex,e_vis);
|
|
if ( !is_forward(e_vis) ) make_first_out_edge(twin(e_back));
|
|
e_apex = e_back;
|
|
TRACEN(" produced " << seg(e_apex));
|
|
}
|
|
}
|
|
|
|
void triangulate_down(Halfedge_handle& e_apex)
|
|
{
|
|
TRACEN("triangulate_down "<<seg(e_apex));
|
|
Vertex_handle v_apex = source(e_apex);
|
|
while (true) {
|
|
Halfedge_handle e_vis = next(e_apex);
|
|
bool in_sweep_line = (SLItem[e_vis] != SL.end());
|
|
bool not_visible = !edge_is_visible_from(v_apex,e_vis);
|
|
TRACEN(" checking "<<in_sweep_line<<not_visible<<" "<<seg(e_vis));
|
|
if ( in_sweep_line || not_visible) {
|
|
TRACEN(" STOP"); return;
|
|
}
|
|
Halfedge_handle e_vis_rev = twin(e_vis);
|
|
Halfedge_handle e_forw = new_bi_edge(e_vis_rev,e_apex);
|
|
e_apex = twin(e_forw);
|
|
TRACEN(" produced " << seg(e_apex));
|
|
}
|
|
}
|
|
|
|
@ In this chunk we provide the operation for triangulation of the
|
|
region left between two edges |e_upper| and |e_lower| with source |v|
|
|
in the ending bundle of vertex |v|. Note that |v| can see the whole
|
|
chain of edges calculated so far between |target(e_upper)| and
|
|
|target(e_lower)|.
|
|
<<ct event handling>>=
|
|
void triangulate_between(Halfedge_handle e_upper, Halfedge_handle e_lower)
|
|
{
|
|
// we triangulate the interior of the whole chain between
|
|
// target(e_upper) and target(e_lower)
|
|
assert(source(e_upper)==source(e_lower));
|
|
TRACE("triangulate_between\n "<<seg(e_upper));
|
|
TRACEN("\n "<<seg(e_lower));
|
|
Halfedge_handle e_end = twin(e_lower);
|
|
while (true) {
|
|
Halfedge_handle e_vis = next(e_upper);
|
|
Halfedge_handle en_vis = next(e_vis);
|
|
TRACEN(" working on base e_vis " << seg(e_vis));
|
|
TRACEN(" next is " << seg(en_vis));
|
|
if (en_vis == e_end) return;
|
|
e_upper = twin(new_bi_edge(twin(e_vis),e_upper));
|
|
TRACEN(" produced " << seg(e_upper));
|
|
}
|
|
}
|
|
|
|
@ Now the main action. We combine the four cases shown in figure
|
|
\figref{ct_conf} in a compact form. To start the event handling
|
|
correct we expect that the embedding of the target vertices of the
|
|
edges in the adjacency lists are counterclockwise order-preserving. We
|
|
expect that the adjacency lists can be split in a first part only
|
|
consisting of edges |e| where |point(source(e))| is smaller than
|
|
|point(target(e))| (lexicographic ordering of points) and into a part
|
|
where the opposite holds. Both parts can be empty. The edges |eb_low|
|
|
and |eb_high| store the extreme edges of the ending bundle. Both are
|
|
set during the iteration through the adjacency list of |event| if the
|
|
ending bundle is non-empty, or to an edge that has to be constructed
|
|
and that links |event| to the already constructed triangulation
|
|
$G[\leq|SL|]$. Both |eb_low| and |eb_high| are manipulated by the
|
|
|triangulate_up/down| operations and are finally part of the chain $C$
|
|
of visible edges. Therefore |eb_low| is the visibility edge for the
|
|
edge below |event| referenced via |sit_pred|.
|
|
<<ct event handling>>=
|
|
void process_event()
|
|
{
|
|
TRACEN("\nPROCESS_EVENT " << p_sweep);
|
|
Halfedge_handle e, ep, eb_low, eb_high, e_end;
|
|
if ( !is_isolated(event) ) {
|
|
e = last_out_edge(event);
|
|
ep = first_out_edge(event);
|
|
}
|
|
ss_iterator sit_pred, sit;
|
|
/* PRECONDITION:
|
|
only ingoing => e is lowest in ingoing bundle
|
|
only outgoing => e is highest in outgoing bundle
|
|
ingoing and outgoing => e is lowest in ingoing bundle */
|
|
eb_high = e_end = ep;
|
|
eb_low = e;
|
|
<<determine a handle sit_pred into SL>>
|
|
<<delete ending bundle, insert starting bundle>>
|
|
triangulate_up(eb_high);
|
|
triangulate_down(eb_low);
|
|
sit_pred->second = eb_low;
|
|
}
|
|
|
|
@ Given |e| an edge in the adjacency list of |v| |SLItem[e]| is an
|
|
iterator pointing into |SL| (and not past the end |== SL.end()|) iff
|
|
|e| is an edge in the bundle of edges ending at |v| (with respect to
|
|
the sweep). In the latter case |--SLItem[e]| is an iterator pointing
|
|
to the edge below |event|. If |SLItem[e] == SL.end()| then we have no
|
|
entry point into |SL| and have to query |SL| by a call to
|
|
|upper_bound|. The edge |e_search| is a loop edge with the property
|
|
|source(e_search) == target(e_search)|. We use it for the geometric
|
|
search in |SL|. The search is only executed when the |event| is not
|
|
connected to $G[\leq|SL|]$.
|
|
<<determine a handle sit_pred into SL>>=
|
|
TRACEN("determining handle in SL");
|
|
if ( e != Halfedge_handle() ) {
|
|
point(target(e_search)) = p_sweep; // degenerate loop edge
|
|
sit_pred = SLItem[e];
|
|
if ( sit_pred != SL.end()) sit = --sit_pred;
|
|
else sit = sit_pred = --SL.upper_bound(e_search);
|
|
} else { // event is isolated vertex
|
|
point(target(e_search)) = p_sweep; // degenerate loop edge
|
|
sit_pred = --SL.upper_bound(e_search);
|
|
}
|
|
|
|
@ We iterate the adjacency list \emph{clockwise} starting at |e|, thus
|
|
we first encounter the ending bundle (if existing), then the starting
|
|
bundle (if existing).
|
|
<<delete ending bundle, insert starting bundle>>=
|
|
bool ending_edges(0), starting_edges(0);
|
|
while ( e != Halfedge_handle() ) { // walk adjacency list clockwise
|
|
if ( SLItem[e] != SL.end() )
|
|
<<handling ending edges>>
|
|
else
|
|
<<handling starting edges>>
|
|
if (e == e_end) break;
|
|
e = cyclic_adj_pred(e);
|
|
}
|
|
if (!ending_edges)
|
|
<<create link to constrained triangulation>>
|
|
|
|
|
|
|
|
@ When |e| is directed backwards, then |target(twin(e))==event|. For
|
|
each wedge between two ending edges we triangulate the whole face. We
|
|
also delete all ending edges from |SL| and mark the edges as
|
|
such.
|
|
<<handling ending edges>>=
|
|
{
|
|
TRACEN("ending " << seg(e));
|
|
if (ending_edges) triangulate_between(e,cyclic_adj_succ(e));
|
|
ending_edges = true;
|
|
SL.erase(SLItem[e]);
|
|
link_bi_edge_to(e,SL.end());
|
|
// not in SL anymore
|
|
}
|
|
|
|
@ For starting edges we insert them into |SL| and keep track of
|
|
the last edge |eb_high| of the ending bundle. For the newly inserted
|
|
edge their source is the visible vertex.
|
|
<<handling starting edges>>=
|
|
{
|
|
TRACEN("starting "<<seg(e));
|
|
sit = SL.insert(sit,ss_pair(e,e));
|
|
link_bi_edge_to(e,sit);
|
|
if ( !starting_edges ) eb_high = cyclic_adj_succ(e);
|
|
starting_edges = true;
|
|
}
|
|
|
|
@ The last chunk of this section codes the case where |event| is not
|
|
connected to $G[\leq|SL|]$. We first determine the visible vertex and
|
|
a candidate edge for visibility search along a chain of edges bounding
|
|
the face which contains |event|. Note that we set |eb_low| and
|
|
|eb_high| to the newly created edge such that we can triangulate ``away''
|
|
from them afterwards.
|
|
<<create link to constrained triangulation>>=
|
|
{
|
|
Halfedge_handle e_vis = sit_pred->second;
|
|
Halfedge_handle e_vis_n = cyclic_adj_succ(e_vis);
|
|
eb_low = eb_high = new_bi_edge(event,e_vis_n);
|
|
TRACEN(" producing link "<<seg(eb_low)<<"\n before "<<seg(e_vis_n));
|
|
}
|
|
|
|
@ Take the time to check that in the combined code the promised
|
|
invariants are kept.
|
|
|
|
\subsection{Initialization}
|
|
|
|
After initialization we want to have all our invariants valid.
|
|
Remember that $G$ left of the sweepline has to be a valid constrained
|
|
triangulation, all edges intersecting the sweepline are in |SL|
|
|
and we have a hashed shortcut to their item. Finally for each area
|
|
in between two edges which are neighbors in the sweepline we want to
|
|
know an edge visible from any point of the area. Now what should
|
|
happen to achieve this. We consider the vertex with minimal
|
|
coordinates as the initial contrained triangulation. We have to insert
|
|
all edges in its adjacency list into the sweepline and set the marks
|
|
and visibility properties. And to avoid special cases we insert two
|
|
sentinel edges encompassing the whole scenery in a symbolic
|
|
wedge. Note that the outgoing bundle can be empty in which case we
|
|
just start with the wedge.
|
|
|
|
Now our basic invariants hold: the explicit triangulation is just the
|
|
vertex |event|, all outgoing edges are part of |SL|, and
|
|
$G[\leq|SL|]$ is connected.
|
|
<<ct initialization>>=
|
|
void link_bi_edge_to(Halfedge_handle e, ss_iterator sit) {
|
|
SLItem[e] = SLItem[twin(e)] = sit;
|
|
}
|
|
|
|
void initialize_structures()
|
|
{
|
|
TRACEN("initialize_structures ");
|
|
|
|
for ( event=vertices_begin(); event != vertices_end(); ++event )
|
|
event_Q.insert(event); // sorted order of vertices
|
|
|
|
event_it = event_Q.begin();
|
|
if ( event_Q.empty() ) return;
|
|
event = *event_it;
|
|
p_sweep = point(event);
|
|
<<insert all edges starting at event>>
|
|
<<create sentinels for visibility search>>
|
|
|
|
// we move to the second vertex:
|
|
procede_to_next_event();
|
|
event_exists(); // sets p_sweep for check invariants
|
|
TRACEN("EOF initialization");
|
|
}
|
|
|
|
@ We insert all edges in the adjacency list of |event| into
|
|
|SL|. All are marked to be in |SL| by a call to
|
|
|link_bi_edge_to|. Thereby we obtain also a hashed shortcut into
|
|
|SL|. In the wedge between two edges $e$ and $e_s$ which are
|
|
neighbors in |SL| each edge $e$ is also the entry point for a
|
|
visibility search in the wedge between $e$ and $e_s$.
|
|
<<insert all edges starting at event>>=
|
|
if ( !is_isolated(event) ) {
|
|
Halfedge_around_vertex_circulator
|
|
e(first_out_edge(event)), eend(e);
|
|
CGAL_For_all(e,eend) {
|
|
TRACEN("init with "<<PE(e));
|
|
ss_iterator sit = SL.insert(ss_pair(e,e)).first;
|
|
link_bi_edge_to(e,sit);
|
|
}
|
|
}
|
|
|
|
|
|
@ We create two sentinel edges and insert them into |SL|. They
|
|
are sentinels by identity and not by geometry. The identity is
|
|
checked in the order predicate of |SL| and thereby all
|
|
insertions are done between them in the geometric order defined by
|
|
|lt_edges_in_sweepline|. Additionally as the two sentinel edges
|
|
|e_low| and |e_high| are marked to be in the sweepline for the whole
|
|
time of the sweep we never run out of the wedge during our visibility
|
|
searches. |e_low| and |e_high| start at |event| and extend to a
|
|
symbolic vertex |v_tmp|. We use one vertex for both edges. Also we
|
|
insert a loop edge |e_search| at |v_tmp| which we use for lookup
|
|
within |SL| in our event handling procedure. All edges adjacent
|
|
to |v_tmp| are removed in the postprocessing step. Note that we don't
|
|
treat the artificial edges by the new-edge data accessor.
|
|
<<create sentinels for visibility search>>=
|
|
Vertex_handle v_tmp = new_vertex(); point(v_tmp) = Point();
|
|
e_high = Base::new_halfedge_pair(event,v_tmp);
|
|
e_low = Base::new_halfedge_pair(event,v_tmp);
|
|
// this are two symbolic edges just accessed as sentinels
|
|
// they carry no geometric information
|
|
e_search = Base::new_halfedge_pair(v_tmp,v_tmp);
|
|
// this is just a loop used for searches in SL
|
|
|
|
ss_iterator sit_high = SL.insert(ss_pair(e_high,e_high)).first;
|
|
ss_iterator sit_low = SL.insert(ss_pair(e_low,e_low)).first;
|
|
// inserting sentinels into SL
|
|
link_bi_edge_to(e_high, sit_high);
|
|
link_bi_edge_to(e_low , sit_low);
|
|
// we mark them being in the sweepline, which they will never leave
|
|
|
|
@ Now for the iteration control. We iterate over all vertices, in the
|
|
order given by the coordinates assigned to the vertices. We set
|
|
|event| in the initialization and at the end of each event handling
|
|
phase. We stop when |event_Q| is empty.
|
|
<<ct event handling>>=
|
|
bool event_exists()
|
|
{ if ( event_it != event_Q.end() ) {
|
|
// event is set at end of loop and in init
|
|
event = *event_it;
|
|
p_sweep = point(event);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void procede_to_next_event()
|
|
{ ++event_it; }
|
|
|
|
@ At the end we remove the frame from our structure. This is only the
|
|
pair of edges spanning the initial wedge.
|
|
<<ct cleaning up>>=
|
|
void complete_structures()
|
|
{
|
|
if (e_low != Halfedge_handle()) {
|
|
delete_vertex(target(e_search));
|
|
} // removing sentinels and e_search
|
|
}
|
|
|
|
|
|
@ During the development we check if the adjacency lists of all
|
|
vertices have the correct adjacency list embedding. The final check
|
|
can do the test if the result of our sweep is a correct triangulation.
|
|
As we don't delete edges or vertices from the output and don't add any
|
|
vertex\footnote{apart from the temporary one which we delete at the
|
|
end}, all constraining edges and all vertices are in triangulation. If
|
|
we check the output structure to be a triangulation according to
|
|
\cite{checking-cgta99} we have a checking module for our constrained
|
|
triangulation sweep.
|
|
<<ct checking>>=
|
|
void check_ccw_local_embedding() const
|
|
{ PM_checker<PMDEC,GEOM> C(*this,K);
|
|
C.check_order_preserving_embedding(event);
|
|
}
|
|
|
|
void check_invariants()
|
|
{
|
|
#ifdef CGAL_CHECK_EXPENSIVE
|
|
if ( event_it == event_Q.end() ) return;
|
|
check_ccw_local_embedding();
|
|
#endif
|
|
}
|
|
|
|
void check_final()
|
|
{
|
|
#ifdef CGAL_CHECK_EXPENSIVE
|
|
PM_checker<PMDEC,GEOM> C(*this,K); C.check_is_triangulation();
|
|
#endif
|
|
}
|
|
|
|
@ \displayeps{NewEdge}{The data accessor concept |NewEdge| for
|
|
the treatment of newly created edges.}{5cm}
|
|
|
|
The following operations interface the plane map decorator. Note
|
|
also that the edge data accessor allows to treat the newly created
|
|
edges of the triangulation.
|
|
<<helping operations>>=
|
|
Halfedge_handle new_bi_edge(Vertex_handle v1, Vertex_handle v2)
|
|
{ // appended at v1 and v2 adj list
|
|
Halfedge_handle e = Base::new_halfedge_pair(v1,v2);
|
|
Treat_new_edge(e);
|
|
return e;
|
|
}
|
|
|
|
Halfedge_handle new_bi_edge(Halfedge_handle e_bf, Halfedge_handle e_af)
|
|
{ // ccw before e_bf and after e_af
|
|
Halfedge_handle e = Base::new_halfedge_pair(e_bf,e_af,Halfedge_base(),
|
|
Base::BEFORE, Base::AFTER);
|
|
Treat_new_edge(e);
|
|
return e;
|
|
}
|
|
|
|
Halfedge_handle new_bi_edge(Vertex_handle v, Halfedge_handle e_bf)
|
|
{ // appended at v's adj list and before e_bf
|
|
Halfedge_handle e = Base::new_halfedge_pair(v,e_bf,Halfedge_base(),
|
|
Base::BEFORE);
|
|
Treat_new_edge(e);
|
|
return e;
|
|
}
|
|
|
|
Segment seg(Halfedge_handle e) const
|
|
{ return K.construct_segment(point(source(e)),point(target(e))); }
|
|
|
|
Direction dir(Halfedge_handle e) const
|
|
{ return K.construct_direction(point(source(e)),point(target(e))); }
|
|
|
|
bool is_forward(Halfedge_handle e) const
|
|
{ return K.compare_xy(point(source(e)),point(target(e))) < 0; }
|
|
|
|
|
|
|
|
@ \subsection{Correctness and Running Time}
|
|
|
|
At the end only the sentinels are in |SL|. $G[\leq|SL|]$
|
|
already consists of the constrained triangulation of the input
|
|
structure. The two parts of the chain $C$ between the source of the
|
|
sentinels just consist of the upper and lower convex hull chain
|
|
between the lexicographic smallest and the lexicographic largest
|
|
vertex. The convexity follows from the slope property. During the
|
|
sweep we once encountered any vertex and any edge and integrated
|
|
it into the constrained triangulation according to our invariants.
|
|
Thus completeness is trivial.
|
|
|
|
The size of the constrained triangulation of a set of segments is of
|
|
the same order as the unconstrained triangulation of the segment end
|
|
points. The sweep procedure takes time for the production of the
|
|
output which is known to be linear in size. The only additional cost
|
|
at each event is the insertion of the segments starting at an event
|
|
point where we use a tree based dictionary to store these. This costs
|
|
logarithmic time per segment to insert. We end up with the standard
|
|
$O(n \log n)$ time bound where $n = \vert S \vert$. The space is
|
|
dominated by the size of the produced output $O(n)$.
|
|
@ \begin{ignoreindiss}
|
|
<<some predefinitions>>=
|
|
#ifndef LEDA_ERROR_H
|
|
static void error_handler(int n, const char* s)
|
|
{ std::cerr << s << std::endl;
|
|
exit(n);
|
|
}
|
|
#endif
|
|
|
|
struct Do_nothing {
|
|
Do_nothing() {}
|
|
template <typename ARG>
|
|
void operator()(ARG&) const {}
|
|
};
|
|
|
|
@ \subsection{Visualization via the generic sweep observer}
|
|
<<Constrained_triang_anim.h>>=
|
|
<<CGAL Header1>>
|
|
// file : include/CGAL/Nef_2/Constrained_triang_anim.h
|
|
<<CGAL Header2>>
|
|
#ifndef CGAL_PM_CONSTR_TRIANG_ANIM_H
|
|
#define CGAL_PM_CONSTR_TRIANG_ANIM_H
|
|
|
|
#include <CGAL/Nef_2/PM_visualizor.h>
|
|
|
|
CGAL_BEGIN_NAMESPACE
|
|
|
|
template <class GT>
|
|
class Constrained_triang_anim {
|
|
|
|
CGAL::Window_stream _W;
|
|
public:
|
|
typedef CGAL::Window_stream VDEVICE;
|
|
typedef typename GT::GEOMETRY GEOM;
|
|
typedef typename GT::Base PMDEC;
|
|
typedef typename PMDEC::Point Point;
|
|
|
|
Constrained_triang_anim() : _W(400,400)
|
|
{ _W.set_show_coordinates(true); _W.init(-120,120,-120,5); _W.display(); }
|
|
VDEVICE& device() { return _W; }
|
|
|
|
void post_init_animation(GT& gpst)
|
|
{
|
|
PM_visualizor<PMDEC,GEOM> V(_W,gpst);
|
|
V.point(V.target(gpst.e_search)) = Point(-120,0);
|
|
// to draw we have to embed the virtual search vertex
|
|
V.draw_skeleton(CGAL::BLUE);
|
|
_W.read_mouse();
|
|
}
|
|
|
|
void pre_event_animation(GT& gpst)
|
|
{ }
|
|
|
|
void post_event_animation(GT& gpst)
|
|
{ PM_visualizor<PMDEC,GEOM> V(_W,gpst);
|
|
V.draw_ending_bundle(gpst.event,CGAL::GREEN);
|
|
_W.read_mouse();
|
|
}
|
|
|
|
void post_completion_animation(GT& gpst)
|
|
{ _W.clear();
|
|
PM_visualizor<PMDEC,GEOM> V(_W,gpst);
|
|
V.draw_skeleton(CGAL::BLACK);
|
|
_W.read_mouse(); }
|
|
|
|
};
|
|
|
|
CGAL_END_NAMESPACE
|
|
#endif // CGAL_PM_CONSTR_TRIANG_ANIM_H
|
|
|
|
@ \section{A Test of the plane map triangulation}
|
|
|
|
We produce a simple homogeneous kernel, a plane map and use a
|
|
segment overlay sweep to create an input structure for the constrained
|
|
triangulation algorithm.
|
|
<<Constrained_triang-test.C>>=
|
|
#include <CGAL/basic.h>
|
|
#include <CGAL/leda_integer.h>
|
|
#include <CGAL/Homogeneous.h>
|
|
#include <CGAL/Point_2.h>
|
|
#include "Affine_geometry.h"
|
|
#undef CGAL_CFG_NO_TMPL_IN_TMPL_PARAM
|
|
#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/Constrained_triang_traits.h>
|
|
#include <CGAL/Nef_2/Constrained_triang_anim.h>
|
|
#include <CGAL/Nef_2/Segment_overlay_traits.h>
|
|
#include <CGAL/test_macros.h>
|
|
|
|
// KERNEL:
|
|
typedef CGAL::Homogeneous<leda_integer> Hom_kernel;
|
|
typedef CGAL::Affine_geometry<Hom_kernel> Aff_kernel;
|
|
typedef Aff_kernel::Segment_2 Segment;
|
|
|
|
// HALFEDGE DATA STRUCTURE:
|
|
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 PM_dec::Halfedge_handle Halfedge_handle;
|
|
typedef PM_dec::Vertex_handle Vertex_handle;
|
|
typedef PM_dec::Halfedge_const_handle Halfedge_const_handle;
|
|
|
|
// SEGMENT OVERLAY:
|
|
template <typename PMDEC, typename I>
|
|
class PM_dec_output : public PMDEC {
|
|
public:
|
|
typedef PMDEC Base;
|
|
typedef typename Base::Plane_map Plane_map;
|
|
typedef typename Base::Point Point;
|
|
typedef typename Base::Vertex_handle Vertex_handle;
|
|
typedef typename Base::Halfedge_handle Halfedge_handle;
|
|
typedef I ITERATOR;
|
|
|
|
PM_dec_output(HDS& H) : Base(H) {}
|
|
PM_dec_output(const PM_dec_output& P) : Base(P) {}
|
|
|
|
Vertex_handle new_vertex(const Point& p) const
|
|
{ Vertex_handle v = Base::new_vertex();
|
|
v->point() = p; return v; }
|
|
|
|
void link_as_target_and_append(Vertex_handle v, Halfedge_handle e) const
|
|
{ Base::link_as_target_and_append(v,e); }
|
|
|
|
Halfedge_handle new_halfedge_pair_at_source(Vertex_handle v) const
|
|
{ return Base::new_halfedge_pair_at_source(v,Base::BEFORE); }
|
|
|
|
void supporting_segment(Halfedge_handle e, ITERATOR it) const {}
|
|
void trivial_segment(Vertex_handle v, ITERATOR it) const {}
|
|
void halfedge_below(Vertex_handle v, Halfedge_handle e) const {}
|
|
void starting_segment(Vertex_handle v, ITERATOR it) const {}
|
|
void passing_segment(Vertex_handle v, ITERATOR it) const {}
|
|
void ending_segment(Vertex_handle v, ITERATOR it) const {}
|
|
|
|
}; // PM_dec_output
|
|
|
|
typedef std::list<Segment>::const_iterator Seg_iterator;
|
|
typedef CGAL::Segment_overlay_traits< Seg_iterator,
|
|
PM_dec_output<PM_dec,Seg_iterator>, Aff_kernel> PM_seg_overlay;
|
|
typedef CGAL::generic_sweep<PM_seg_overlay> PM_seg_overlay_sweep;
|
|
|
|
// CONSTRAINED TRIANGULATIONS:
|
|
typedef CGAL::Constrained_triang_traits<PM_dec,Aff_kernel> CTT;
|
|
typedef CGAL::generic_sweep<CTT> Constrained_triang_sweep;
|
|
typedef CGAL::Constrained_triang_anim<CTT> CTA;
|
|
typedef CGAL::sweep_observer<Constrained_triang_sweep,CTA> CTS_observer;
|
|
|
|
// MAIN PROGRAM:
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
// SETDTHREAD(19);
|
|
CGAL::set_pretty_mode ( cerr );
|
|
HDS H;
|
|
Aff_kernel AK;
|
|
CTS_observer Obs;
|
|
Obs.device().message("Insert segments to triangulate.");
|
|
std::list<Segment> L;
|
|
Segment s;
|
|
if ( argc == 2 ) {
|
|
std::ifstream log(argv[1]);
|
|
while ( log >> s ) { L.push_back(s); Obs.device() << s; }
|
|
}
|
|
while ( Obs.device() >> s ) L.push_back(s);
|
|
std::string fname(argv[0]);
|
|
fname += ".log";
|
|
std::ofstream log(fname.c_str());
|
|
for (Seg_iterator sit = L.begin(); sit != L.end(); ++sit)
|
|
log << *sit << " ";
|
|
log.close();
|
|
PM_seg_overlay::OUTPUT D(H);
|
|
PM_seg_overlay_sweep OV(PM_seg_overlay::INPUT(L.begin(),L.end()),D,AK);
|
|
CTT::INPUT I;
|
|
Constrained_triang_sweep CT(I,H,AK);
|
|
Obs.attach(CT);
|
|
OV.sweep();
|
|
CT.sweep();
|
|
return 0;
|
|
}
|
|
|
|
@ \begin{ignore}
|
|
<<CGAL Header1>>=
|
|
// ============================================================================
|
|
//
|
|
// 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$
|
|
//
|
|
<<CGAL Header2>>=
|
|
// package : Nef_2
|
|
// chapter : Nef Polyhedra
|
|
//
|
|
// source : nef_2d/Constrained_triang.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: Constrained triangulation of a plane map
|
|
// ============================================================================
|
|
@ \end{ignore}
|
|
\end{ignoreindiss}
|
|
|
|
%KILLSTART REP
|
|
\newpage
|
|
\bibliographystyle{alpha}
|
|
\bibliography{comp_geo,general,diss}
|
|
\newpage
|
|
\section{Appendix}
|
|
\input manpages/AffineGeometryTraits_2.man
|
|
\input manpages/GenericSweepTraits.man
|
|
\input manpages/PMConstDecorator.man
|
|
\input manpages/PMDecorator.man
|
|
\input manpages/PM_checker.man
|
|
\end{document}
|
|
%KILLEND REP
|