mirror of https://github.com/CGAL/cgal
2533 lines
103 KiB
Plaintext
2533 lines
103 KiB
Plaintext
% ----------------------------------------------------------------------------
|
||
% sweep.lw: Punkt-Lokalisierung in der Ebene
|
||
% $Id$
|
||
% ----------------------------------------------------------------------------
|
||
|
||
% Seitenumbruch bei PointLocator member functions !!!
|
||
% style: Typen, Klassen und member-Funktionen kursiv
|
||
|
||
% ToDo: s. auch ~/diplom/todo
|
||
% - GenericXStructure:
|
||
% wahrscheinlich ist es deutlich effizienter, ein C++-Array zu verwenden
|
||
% und binary_locate selbst zu implementieren, denn dann kann bei der
|
||
% Compare-Fkt. inlining verwendet werden
|
||
|
||
\documentclass[a4paper]{article}
|
||
\usepackage{amsmath,amsthm,amssymb,Lweb,version}
|
||
\usepackage[german]{babel}
|
||
\begin{document}
|
||
\newcommand{\chapter}{\section}
|
||
\newcommand{\card}[1]{\Labs{#1}}
|
||
\newcommand{\Set}[2]{ \{ #1 \quad : \quad #2 \} }
|
||
\newcommand{\RR}{ R }
|
||
\newcommand{\degree}{ ^\circ }
|
||
\newcommand{\sign}{ \mathrm{sign} }
|
||
\newcommand{\EGplpl}{ \texttt{eg++} }
|
||
\newcommand{\rem}[1]{}
|
||
\newcommand{\SET}[1]{ \{ #1 \} }
|
||
\newcommand{\Seg}[2]{ \ensuremath{\overline{#1#2}} }
|
||
\newcommand{\abs}[1]{ \Labs{#1} }
|
||
\theoremstyle{thmstyle}\newtheorem{lemma}{Lemma}[section]
|
||
\theoremstyle{thmstyle}\newtheorem{beweis}{Beweis}[section]
|
||
\theoremstyle{thmstyle}\newtheorem{bemerkung}{Bemerkung}[section]
|
||
\newcommand{\Figger}[3]{ NO FIGURES INCLUDED \\ }
|
||
\newcommand{\figref}[1]{ NOREF }
|
||
\newcommand{\epsfig}[1]{ NO FIGURES INCLUDED \\ }
|
||
\newcommand{\figlabel}[1]{}
|
||
|
||
\chapter{Punkt-Lokalisierung in der Ebene} \label{PLoc}
|
||
|
||
In diesem Kapitel wird eine Anwendung f"ur die Datenstrukturen vorgestellt,
|
||
die in den letzten Kapiteln entwickelt wurden.
|
||
Es handelt sich um die Lokalisierung eines Punktes in der Ebene.
|
||
Das Problem l"a"st sich folgenderma"sen beschreiben:
|
||
Gegeben sind ein Graph |G=(V,E)| und zwei Abbildungen |P| und |K|.
|
||
Jedem Knoten |v| von |G| ist ein Punkt |P(v)| in der Ebene zugeordnet.
|
||
Mit jeder Kante $e = \{v,w\}$ bzw. $e=(v,w)$ ist eine Kurve |K(e)| assoziiert,
|
||
die zwischen |P(v)| und |P(w)| verl"auft. Sie ist stetig und beidseitig
|
||
offen, sowie entweder x-monoton (jede Parallele zur Y-Achse schneidet
|
||
die Kurve in h"ochstens einem Punkt) oder ein vertikales Geradensegment.
|
||
Die Abbildungen |P| und |K| stellen eine planare Einbettung von |G| dar,
|
||
d.h. alle Kurven sind disjunkt, |P| ist injektiv und keiner der Punkte in
|
||
|P(V)| liegt auf einer Kurve.
|
||
(Informal bedeutet dies, da"s sich der Graph gem"a"s |P| und |K| ohne
|
||
"Uberschneidungen zeichnen l"a"st.)
|
||
Es mu"s sich jedoch nicht um eine ordnungserhaltende Einbettung handeln, d.h.
|
||
die Reihenfolge der Kanten in der Adjazenzliste eines Knotens ist unerheblich
|
||
(vgl. \cite{LEDA_book} Abschnitt 8.4).
|
||
~\\
|
||
Die Aufgabe besteht darin, eine Datenstruktur zu generieren, die Anfragen
|
||
der folgenden Form beantworten kann:
|
||
\begin{quote}
|
||
Bestimme zu einem Punkt |p| das Objekt von |G|, das als erstes von dem
|
||
Strahl geschnitten wird, der in |p| beginnt und vertikal nach oben bzw.
|
||
nach unten gerichtet ist.
|
||
\end{quote}
|
||
Die Antwort auf eine solche Anfrage ist ein Knoten oder eine Kante von |G|
|
||
oder aber Wert |nil|, falls der Strahl |G| nicht schneidet.
|
||
|
||
Punkt-Lokalisierung ist Grundbaustein vieler Verfahren im Bereich
|
||
Computational Geometry und hat zahlreiche Anwendungen bei geographischen
|
||
Informationssystemen und bei der Bewegungsplanung.
|
||
Das Problem war und ist Gegenstand vieler Forschungsarbeiten, und es gibt eine
|
||
Reihe von unterschiedlichen L"osungsans"atzen (vgl. \cite{Preparata_Tour}).
|
||
Die Qualit"at einer L"osung wird durch 3 Faktoren bestimmt:
|
||
die Zeit zum Aufbau der Datenstruktur, ihr Platzverbrauch und die Zeit f"ur
|
||
die Beantwortung einer Lokalisierungsanfrage.
|
||
Da der Graph in der Regel statisch ist und f"ur viele Anfragen benutzt wird,
|
||
sind die beiden letzten Faktoren die wichtigsten.
|
||
Sei $k:=\card{V}+\card{E}$ die Anzahl der Objekte des Graphen |G|. Wenn man
|
||
zur Lokalisierung eines Punktes Lagevergleiche der Form Punkt-Punkt und
|
||
Punkt-Kurve verwendet, so ergibt sich als untere Schranke f"ur die
|
||
Lokalisierungszeit $\Omega(\log k)$%
|
||
\footnote{%
|
||
Eine exaktere untere Schranke findet sich in \cite{Adamy_Seidel}.
|
||
}.
|
||
|
||
Der Ansatz, der in dieser Arbeit verfolgt wird, basiert auf einem Vorschlag
|
||
von Sarnak und Tarjan \cite{ST86}, die persistente Suchb"aume zur L"osung des
|
||
Problems einsetzten. Ihre Datenstruktur kann in Zeit $O(k \cdot \log k)$
|
||
aufgebaut werden, ben"otigt $O(k)$ Platz und kann Anfragen in Zeit $O(\log k)$
|
||
beantworten. Damit ist sie also bis auf konstante Faktoren optimal
|
||
hinsichtlich Platzverbrauch und Lokalisierungszeit.
|
||
|
||
J"ungere Arbeiten \cite{Goodrich_SODA, Adamy_Seidel} besch"aftigten sich mit
|
||
sich mit der Reduzierung des konstanten Faktors bei der Lokalisierungszeit
|
||
unter Beihaltung des linearen Platzverbrauchs; Adamy und Seidel
|
||
\cite{Adamy_Seidel} gelang es eine Datenstruktur zu entwickeln, die mit
|
||
$\log k + o(\log k)$ Vergleichen pro Lokalisierungsanfrage auskommt.
|
||
|
||
@ % === <20>berblick <20>ber die Datenstruktur =====================================
|
||
\section{"Uberblick "uber die Datenstruktur} \label{PLoc_Overview}
|
||
Um die Datenstruktur aufzubauen, wird ein Sweepline-Algorithmus verwendet.
|
||
Dieser l"a"st eine Parallele zur Y-Achse, die sogenannte Sweepline, entlang
|
||
der X-Achse von $-\infty$ bis $\infty$ wandern. Man kann sich zun"achst einmal
|
||
vorstellen, da"s f"ur jede X-Koordinate alle Objekte des Graphen gespeichert
|
||
werden, die die Sweepline an dieser Stelle schneidet, und zwar geordnet nach
|
||
den Y-Koordinaten der Schnittpunkte.
|
||
|
||
\Figger{sweep_idee}
|
||
{Sweep "uber einen Graphen mit 7 Knoten und 4 Kanten}
|
||
{sweep_idee}
|
||
|
||
Wie man leicht sieht, ver"andert sich die Menge der geschnittenen Objekte
|
||
im Laufe eines Sweeps nur selten, n"amlich genau dann, wenn die Sweepline
|
||
einen oder mehrere Knoten des Graphen "uberstreicht.
|
||
Dies motiviert die Definition der Menge |S| der Haltestellen eines Sweeps "uber
|
||
einen Graphen |G|: $S = \Set{x \in \RR}{\exists y \in \RR: (x,y) \in |P(v)| }$.
|
||
Hat ein Sweep die Haltestellen%
|
||
\footnote{ %
|
||
Es gibt Sweepline-Algorithmen (vgl. \cite{LEDA_book} Abschnitt 10.7),
|
||
welche die Menge |P(V)|, also die Punkte selbst, als Haltestellen nehmen und
|
||
diese in lexikographischer Reihenfolge bearbeiten; die Sweepline ist dann
|
||
keine Gerade mehr, sondern eine Stufe mit einem infinitesimal kleinen Absatz.
|
||
Dies hat jedoch den Nachteil, da"s man bei einer Punkt-Lokalisierung nur
|
||
nach unten gerichtete Strahlen verfolgen kann.}
|
||
$x_0 < x_1 < \dots < x_n$, dann mu"s man den
|
||
Zustand der Sweepline an diesen Haltestellen und in den Intervallen %[
|
||
$x_i^+ := ]x_i,x_{i+1}[$ ($i=0, \dots, n-1$) speichern
|
||
(vgl. Abbildung \figref{sweep_idee}). Damit l"a"st sich zu jeder X-Koordinate
|
||
die Sweepline an dieser Stelle rekonstruieren.
|
||
In den Intervallen $]{-\infty},x_0[$ und $]x_n,{+\infty}[ =: x_n^+$
|
||
schneidet die Sweepline kein Objekt von |G|. %]
|
||
|
||
Um die Berechnung der Sweeplines f"ur die Haltestelle $x_i$ und das
|
||
Intervall $x_i^+$ einfacher beschreiben zu k"onnen, werden zwei Begriffe
|
||
definiert.
|
||
Sei |e| eine Kante von |G| mit den inzidenten Knoten |v| und |w|, so da"s
|
||
$P(v) \neq P(w)$.
|
||
Dann \emph{startet} |e| in |v| und \emph{endet} in |w|, falls
|
||
$P(v) \prec_{lex} P(w)$.
|
||
(Hierbei bezeichnet $\prec_{lex}$ die lexikographische Ordnung auf den
|
||
Punkten nach X- und Y-Koordinate.)
|
||
Die Begriffe "`starten"' und "`enden"' beziehen sich also nur auf die Lage
|
||
der inzidenten Knoten einer Kante, nicht aber auf eine eventuelle Richtung
|
||
der Kante im Graphen.
|
||
Die "Anderungen der Sweeplines f"ur die Haltestelle $x_i$ und das Intervall
|
||
$x_i^+$ gegen"uber der Sweepline des vorhergehenden Intervalles kann man
|
||
wie folgt finden:
|
||
\begin{itemize}
|
||
\item Berechne die Menge |NodesOnSL| aller Knoten von |G|, die die Sweepline
|
||
an dieser Haltestelle "uberstreicht.
|
||
Alle diese Knoten geh"oren zur Sweepline f"ur $x_i$, nicht aber zu der f"ur
|
||
$x_i^+$.
|
||
\item F"ur jede Kante |e|, die zu einem Knoten |v| in |NodesOnSL| inzident
|
||
ist, unterscheide 3 F"alle:
|
||
\begin{enumerate}
|
||
\item |e| ist nicht vertikal und endet in |v|, dann ist |e| weder in der
|
||
Sweepline f"ur $x_i$ noch f"ur $x_i^+$ enthalten.
|
||
\item |e| ist nicht vertikal und startet in |v|, dann ist |e| nicht in der
|
||
Sweepline f"ur $x_i$, aber in der f"ur $x_i^+$ enthalten.
|
||
\item |e| ist vertikal, dann ist |e| zwar in der Sweepline f"ur $x_i$, nicht
|
||
aber in der Sweepline f"ur $x_i^+$ enthalten.
|
||
\end{enumerate}
|
||
\end{itemize}
|
||
|
||
Eine Sweepline (oder genauer die Menge der von ihr geschnittenen
|
||
Objekte) kann als ein bin"arer Suchbaum repr"asentiert werden. Da sich
|
||
aufeinanderfolgende Sweeplines nur wenig unterscheiden, bietet es sich
|
||
an, den Suchbaum nicht f"ur jede Haltestelle und jedes Intervall neu
|
||
aufzubauen, sondern nur die "Anderungen zwischen aufeinanderfolgenden
|
||
Suchb"aumen zu speichern. Eine geeignete Datenstruktur hierf"ur ist
|
||
ein persistenter Suchbaum. Bei dem Sweep kann man die X-Achse als
|
||
Zeitachse in der Welt des persistenten Suchbaumes ansehen, wobei die
|
||
Haltestellen und die Intervalle den Versionen des Suchbaumes
|
||
entsprechen.
|
||
|
||
F"ur jede Haltestelle und jedes Intervall mu"s man also im Laufe des
|
||
Sweeps die zugeh"orige Version des persistenten Suchbaumes speichern.
|
||
Wenn man sp"ater eine Lokalisationsanfrage nach einem Punkt $p =
|
||
(x,y)$ beantworten soll, sucht man anhand der X-Koordinate die
|
||
richtige Version des persistenten Suchbaumes und kann so die Sweepline
|
||
an der Stelle $x$ rekonstruieren. Diese enth"alt alle Objekte des
|
||
Graphen, die von der Parallelen zur Y-Achse an dieser Stelle
|
||
geschnitten werden. Weil die Objekte nach Y-Koordinate sortiert sind,
|
||
kann man mittels bin"arer Suche das gesuchte Objekt finden bzw.
|
||
feststellen, da"s keines existiert. Die Aufgabe der Speicherung und
|
||
der Rekonstruktion der Sweeplines wird von der sogenannten
|
||
\emph{X-Struktur} erledigt, sie ist damit das Herzst"uck der
|
||
Datenstruktur zur Punkt-Lokalisierung.%"
|
||
|
||
@ % === Implementierung ======================================================
|
||
\section{Implementierung}
|
||
Bei der Implementierung wurde darauf Wert gelegt, da"s die Datenstruktur
|
||
|PointLocator| mit verschiedenen Eingabetypen benutzt werden kann.
|
||
So kann sie z.B. LEDA Graphen der Form |Graph<Point,...>| verarbeiten, wobei
|
||
|Point| einer der LEDA Punkttypen |point| oder |rat_point| sein darf.
|
||
Aber auch Voronoi-Diagramme, die durch Objekte vom Typ |GRAPH<Circle,Point>|
|
||
repr"asentiert werden, k"onnen verwendet werden.
|
||
Dar"uber hinaus hat der Benutzer die M"oglichkeit, seine eigenen Datentypen
|
||
f"ur Punkte, Graphen, usw. einzusetzen.
|
||
|
||
@ % === Traits f<>r Punkt-Lokalisierung =======================================
|
||
\subsection{Generische Implementierung mit Traits}
|
||
Um diese Anforderungen verwirklichen zu k"onnen, werden Traits-Klassen
|
||
\cite{Mye95} verwendet, wie sie auch in den Bibliotheken STL
|
||
\cite{STL} oder CGAL \cite{CGAL_design} zum Einsatz kommen. Die
|
||
Traits-Klasse teilt dem |PointLocator| die an der Punkt-Lokalisation
|
||
beteiligten Typen wie z.B. Graph, Punkt und Kurve mit und stellt die
|
||
ben"otigten Operationen auf diesen Typen zur Verf"ugung. F"ur einige
|
||
LEDA Graphentypen werden in den Abschnitten
|
||
\ref{PLocTraits_LEDA_Graph} und \ref{PLocTraits_LEDA_VD} konkrete
|
||
Implementierungen von Traits-Klassen besprochen. Will der Anwender
|
||
andere Datentypen benutzen, so mu"s er selbst eine geeignete
|
||
Traits-Klasse implementieren.
|
||
|
||
Die Anforderungen an eine Traits-Klasse f"ur Punkt-Lokalisation werden im
|
||
folgenden behandelt. \label{PLocTraits_interface}
|
||
Neben Anforderungen an das Interface der Klasse werden auch semantische
|
||
Bedingungen f"ur die von ihr zur Verf"ugung gestellten Typen und Operationen
|
||
diskutiert:
|
||
\begin{itemize}
|
||
\item Grundlegende Typen sind Graph, Knoten und Kante, sowie Iteratoren
|
||
zum Durchmustern von Knoten und Kanten.
|
||
<<PLocTraitsInterface>>=
|
||
class Graph;
|
||
class Node;
|
||
class Edge;
|
||
|
||
class NodeIterator;
|
||
NodeIterator Nodes_begin(const Graph& G) const;
|
||
NodeIterator Nodes_end(const Graph& G) const;
|
||
|
||
class IncEdgeIterator;
|
||
IncEdgeIterator IncEdges_begin(const Graph& G, const Node& n) const;
|
||
IncEdgeIterator IncEdges_end(const Graph& G, const Node& n) const;
|
||
|
||
Node toNode(const NodeIterator& n);
|
||
Edge toEdge(const IncEdgeIterator& e);
|
||
@ Die Klassen |NodeIterator| und |IncEdgeIterator| m"ussen den
|
||
Anforderungen an einen STL-ForwardIterator (s. \cite{STL})
|
||
entsprechen.
|
||
Ein |NodeIterator| dient dazu, "uber die Knotenmenge des Graphen zu
|
||
iterieren, wenn der Sweep initialisiert wird.
|
||
Die Funktionen |Nodes_begin(...)| und |Nodes_end(...)| liefern einen
|
||
Iterator, der auf den ersten Knoten des Graphen bzw. hinter den letzten
|
||
zeigt.
|
||
Der |IncEdgeIterator| erlaubt es dem Algorithmus, "uber die zu einem Knoten
|
||
inzidenten Kanten zu iterieren; die Semantik der |begin|- und |end|-
|
||
Funktionen ist analog wie bei |NodeIterator|.%"
|
||
|
||
\item Der Sweep-Algorithmus ist darauf angewiesen, jede Kante |e| bzgl. eines
|
||
ihrer inzidenten Knoten |u| in eine der folgenden 4 Kategorien einzuteilen.
|
||
Hierbei sei |v| der andere zu |e| inzidente Knoten:%
|
||
\label{PLoc_edge_classification}
|
||
\begin{enumerate}
|
||
\item |StartingNonVertical|: $P(u).x < P(v).x$
|
||
\item |EndingNonVertical|: $P(u).x > P(v).x$
|
||
\item |StartingVertical|: $P(u).x = P(v).x \wedge P(u).y < P(v).y$
|
||
\item |EndingVertical|: $P(u).x = P(v).x \wedge P(u).y > P(v).y$
|
||
\end{enumerate}
|
||
<<PLocTraitsInterface>>=
|
||
enum EdgeCategory
|
||
{ StartingNonVertical, StartingVertical, EndingNonVertical, EndingVertical };
|
||
EdgeCategory ClassifyEdge(const Graph& G, const Edge& e, const Node& u);
|
||
@ %
|
||
\item Um Punkt-Lokalisierungen durchzuf"uhren, mu"s der |PointLocator|
|
||
nat"urlich den Punkttyp kennen.
|
||
Wie der "Uberblick in Abschnitt \ref{PLoc_Overview} gezeigt hat, mu"s
|
||
man auch die X-Koordinaten von Punkten (Haltestellen des Sweeps) speichern
|
||
und miteinander vergleichen k"onnen.
|
||
<<PLocTraitsInterface>>=
|
||
class Point;
|
||
class XCoord;
|
||
XCoord getXCoord(const Point& p) const;
|
||
XCoord getXCoord(const Graph& G, const Node& n) const;
|
||
class PredLessThanX;
|
||
PredLessThanX getLessThanX() const;
|
||
@ Der Typ |XCoord| repr"asentiert zwar die X-Koordinaten der Positionen der
|
||
Knoten eines Graphen, dies mu"s aber nicht wirklich eine Zahl sein; bei einem
|
||
Voronoi-Diagramm z.B. k"onnte man |XCoord| durch Kreise repr"asentieren,
|
||
so da"s |PredLessThanX| auf den exakten Koordinaten arbeiten kann, was
|
||
Ungenauigkeiten durch Rundungsfehler vermeidet.
|
||
Die Klasse |PredLessThanX| mu"s einen Funktionsaufruf-Operator zur Verf"ugung
|
||
stellen, der zwei Objekte vom Typ |XCoord| als Eingabe nimmt und das
|
||
Ergebnis des Vergleichs als |bool| zur"uckliefert. Die Semantik ist dieselbe
|
||
wie bei einem |compare|-Object der STL.
|
||
|
||
\item Beim "Uberblick "uber die |PointLocator| Datenstruktur wurde gesagt,
|
||
da"s in einem persistenten Suchbaum, der die Sweeplines repr"asentiert,
|
||
Objekte des gegebenen Graphen sortiert gespeichert werden.
|
||
Die Schl"ussel, die in dem Suchbaum verwendet werden, m"ussen nicht nur
|
||
die Kurven repr"asentieren k"onnen, die mit den Knoten und Kanten des
|
||
Graphen assoziiert sind, sondern auch die Punkte der Lokalisationsanfragen.
|
||
Dies ist der Grund daf"ur, da"s die Objekte des Graphen nicht als Schl"ussel
|
||
verwendet werden, sondern Objekte vom Typ |Curve|. Die Objekte des Graphen
|
||
werden als Informationen im Baum gespeichert.
|
||
Es gibt noch einen weiteren Grund der daf"ur spricht, dem Benutzer die
|
||
Freiheit zu geben, seinen eigenen Kurventyp zu w"ahlen. Es kann eine
|
||
Repr"asentation der Kurven geben, die Vergleiche relativ billig macht,
|
||
deren Berechnung aber relativ teuer ist. Insofern kann es sich lohnen, diese
|
||
Repr"asentation der Kurven zu speichern und sie nicht vor jedem Vergleich
|
||
neu zu berechnen.
|
||
<<PLocTraitsInterface>>=
|
||
class Curve;
|
||
Curve makeCurve(const Point& p) const;
|
||
Curve makeCurve(const Graph& G, const Node& n) const;
|
||
Curve makeCurve(const Graph& G, const Edge& e) const;
|
||
class PredCompareCurves;
|
||
PredCompareCurves getCompareCurves() const;
|
||
@ |PredCompareCurves| mu"s analog wie |PredCompareX| einen
|
||
Funktionsaufruf-Operator bereitstellen, mit dem zwei Kurven |C1| und |C2|
|
||
vom Typ |Curve| verglichen werden k"onnen.
|
||
Die Semantik ist folgende:\\
|
||
$$|PredCompareCurves|(C1, C2) \left\{ \begin{array}{r@{\quad:\quad}l}
|
||
<0 & |C1| \mbox{ unter } |C2| \\
|
||
=0 & |C1| \cap |C2| \neq \emptyset \\
|
||
>0 & |C1| \mbox{ "uber } |C2| \\ %"}
|
||
\end{array} \right.$$
|
||
|
||
Die |PointLocator|-Klasse stellt sicher, da"s |C1| und |C2| bei jedem
|
||
Aufruf von |PredCompareCurves| die folgenden Bedingungen erf"ullen:
|
||
\begin{enumerate}
|
||
\item Die Kurven |C1| und |C2| sind \emph{vergleichbar}, d.h. es existiert
|
||
eine Parallele zur Y-Achse, die beide Kurven schneidet.
|
||
\item Sowohl |C1| als auch |C2| ist durch den Aufruf einer |makeCurve|
|
||
Funktion erzeugt worden.
|
||
\end{enumerate}
|
||
Der Ausdruck $|C1| \cap |C2| \neq \emptyset$ ist eine Abk"urzung f"ur
|
||
folgende Situationen:
|
||
\begin{itemize}
|
||
\item |C1| oder |C2| ist ein Anfragepunkt |p|.
|
||
Dann bedeutet $|C1| \cap |C2| \neq \emptyset$, da"s |p| auf der anderen
|
||
Kurve liegt.
|
||
\item |C1| und |C2| repr"asentieren Objekte des Graphen. In diesem Fall
|
||
hei"st $|C1| \cap |C2| \neq \emptyset$, da"s $|C1| \equiv |C2|$, da der
|
||
Graph planar eingebettet ist.
|
||
\end{itemize}
|
||
|
||
\item Das Ergebnis einer Lokalisierungsanfrage kann ein Knoten oder eine Kante
|
||
sein oder aber der Wert |nil|. Ein Objekt der Klasse |GenericLocation|, die
|
||
mit den Typen der Knoten und der Kanten parametrisiert ist, dient dazu, das
|
||
Resultat einer Anfrage aufzunehmen und es gegebenenfalls in einen Knoten
|
||
oder eine Kante zur"uck zu konvertieren.
|
||
% === Generische Lokation ==================================================
|
||
<<Location>>=
|
||
/*{\Manpage {GenericLocation}{Node, Edge}
|
||
{Return Type for Planar Point Location}{L}}*/
|
||
|
||
template <class Node, class Edge>
|
||
class GenericLocation {
|
||
/*{\Mdefinition
|
||
An instance of the data type |\Mtype| is used as return value for planar
|
||
point location. It can store a node or an edge of a graph or the special
|
||
value |nil| which is used to signal that no node or edge could be found.
|
||
}*/
|
||
typedef void* GenPtr;
|
||
public:
|
||
/*{\Mtypes}*/
|
||
enum Type { NIL, NODE, EDGE };
|
||
/*{\Menum This enumeration allows to specify the 3 basic types of the
|
||
values that a |\Mtype| can represent.}*/
|
||
|
||
<<Konvertierungs- und Zuweisungsoperatoren>>
|
||
|
||
private:
|
||
Type type;
|
||
GenPtr value;
|
||
};
|
||
|
||
<<Vergleichsoperatoren>>
|
||
@ \rem{
|
||
<<Konvertierungs- und Zuweisungsoperatoren>>=
|
||
/*{\Mcreation}*/
|
||
GenericLocation() { init(); }
|
||
/*{\Mcreate creates a |\Mtype| and initializes with the value |nil|.}*/
|
||
GenericLocation(Node n) { init(n); }
|
||
/*{\Mcreate creates a |\Mtype| and initializes with the node |n|.}*/
|
||
GenericLocation(Edge e) { init(e); }
|
||
/*{\Mcreate creates a |\Mtype| and initializes with the edge |e|.}*/
|
||
|
||
~GenericLocation() { clear(); }
|
||
|
||
GenericLocation(const GenericLocation<Node, Edge>& L) { assign(L); }
|
||
GenericLocation<Node, Edge>& operator=(
|
||
const GenericLocation<Node, Edge>& L)
|
||
{ clear(); assign(L); return *this; }
|
||
|
||
/*{\Moperations}*/
|
||
operator const Node&() const
|
||
{
|
||
#if !defined(CHECKING_OFF)
|
||
if (type != NODE) error_handler(1, "Location: not convertible to node");
|
||
#endif
|
||
return geninfo<Node>::const_access(value);
|
||
}
|
||
/*{\Mconversion converts |\Mvar| into a node.\\
|
||
\precond |\Mvar| represents a node.}*/
|
||
|
||
operator const Edge&() const
|
||
{
|
||
#if !defined(CHECKING_OFF)
|
||
if (type != EDGE) error_handler(1, "Location: not convertible to edge");
|
||
#endif
|
||
return geninfo<Edge>::const_access(value);
|
||
}
|
||
/*{\Mconversion converts |\Mvar| into an edge.\\
|
||
\precond |\Mvar| represents an edge.}*/
|
||
|
||
GenericLocation<Node, Edge>& operator=(Node n)
|
||
{ clear(); init(n); return *this; }
|
||
/*{\Mbinop makes |\Mvar| represent the node |n|.}*/
|
||
|
||
GenericLocation<Node, Edge>& operator=(Edge e)
|
||
{ clear(); init(e); return *this; }
|
||
/*{\Mbinop makes |\Mvar| represent the edge |e|.}*/
|
||
|
||
Type get_type() const { return type; }
|
||
/*{\Mop returns the type of the value contained in |\Mvar|.}*/
|
||
|
||
bool is_nil() const { return type == NIL; }
|
||
/*{\Mop returns |true| iff |\Mvar| represents the value |nil|.}*/
|
||
|
||
bool is_node() const { return type == NODE; }
|
||
/*{\Mop returns |true| iff |\Mvar| represents a node.}*/
|
||
|
||
bool is_edge() const { return type == EDGE; }
|
||
/*{\Mop returns |true| iff |\Mvar| represents an edge.}*/
|
||
|
||
private:
|
||
void init() { type = NIL; }
|
||
void init(Node n)
|
||
{ type = NODE;
|
||
geninfo<Node>::create(value);
|
||
geninfo<Node>::access(value) = n;
|
||
}
|
||
void init(Edge e)
|
||
{ type = EDGE;
|
||
geninfo<Edge>::create(value);
|
||
geninfo<Edge>::access(value) = e;
|
||
}
|
||
|
||
void clear()
|
||
{
|
||
switch(type) {
|
||
case NODE: geninfo<Node>::clear(value); break;
|
||
case EDGE: geninfo<Edge>::clear(value); break;
|
||
case NIL: break;
|
||
}
|
||
}
|
||
|
||
void assign(const GenericLocation<Node, Edge>& L)
|
||
{
|
||
type = L.type;
|
||
switch(type) {
|
||
case NODE:
|
||
geninfo<Node>::access(value) = geninfo<Node>::const_access(L.value);
|
||
break;
|
||
case EDGE:
|
||
geninfo<Edge>::access(value) = geninfo<Edge>::const_access(L.value);
|
||
break;
|
||
case NIL: break;
|
||
}
|
||
}
|
||
|
||
public:
|
||
typedef GenericLocation<Node, Edge> self;
|
||
|
||
<<Vergleichsoperatoren>>=
|
||
/*{\Mimplementation
|
||
The data type |\Mtype| is implemented as a union of the types |Node| and
|
||
|Edge|. There is only constant time and space overhead.
|
||
}*/
|
||
template <class Node, class Edge>
|
||
inline
|
||
bool
|
||
operator==(const GenericLocation<Node, Edge>& L1,
|
||
const GenericLocation<Node, Edge>& L2)
|
||
{
|
||
if (L1.get_type() != L2.get_type()) return false;
|
||
switch (L1.get_type()) {
|
||
case GenericLocation<Node, Edge>::NIL: return true;
|
||
case GenericLocation<Node, Edge>::NODE: return Node(L1) == Node(L2);
|
||
case GenericLocation<Node, Edge>::EDGE: return Edge(L1) == Edge(L2);
|
||
}
|
||
}
|
||
|
||
template <class Node, class Edge>
|
||
inline
|
||
bool
|
||
operator!=(const GenericLocation<Node, Edge>& L1,
|
||
const GenericLocation<Node, Edge>& L2)
|
||
{ return ! (L1==L2); }
|
||
|
||
template <class Node, class Edge>
|
||
std::ostream& operator<<(std::ostream& o,
|
||
const GenericLocation<Node, Edge>& L)
|
||
{
|
||
switch (L.get_type()) {
|
||
case GenericLocation<Node, Edge>::NIL: return o<<"nil";
|
||
case GenericLocation<Node, Edge>::NODE: return o<<"node("<<&*Node(L)<<')';
|
||
case GenericLocation<Node, Edge>::EDGE: return o<<"edge("<<&*Edge(L)<<')';
|
||
}
|
||
return o;
|
||
}
|
||
|
||
template <class Node, class Edge>
|
||
std::istream& operator>>(std::istream& i, GenericLocation<Node, Edge>&)
|
||
{ return i; }
|
||
@ }% of rem
|
||
(Die Implementierung der Konvertierungs- und Vergleichsoperatoren ist trivial.)
|
||
|
||
Wenn die Punkt-Lokalisierung eine Lokation berechnet hat, liefert sie diese
|
||
nicht sofort zur"uck, sondern wendet die Funktion |PostProcess| aus der
|
||
Traits-Klasse darauf an. Der R"uckgabewert dieser Funktion ist das
|
||
tats"achliche Ergebnis der Punkt-Lokalisierung. Wie man sieht, darf dieses
|
||
Resultat den frei w"ahlbaren Typ |QueryResult| haben.
|
||
Falls man z.B. Facetten des Graphen anstatt Knoten oder Kanten
|
||
zur"uckliefern will, so kann man dies erreichen, indem man |QueryResult| und
|
||
|PostProcess| entsprechend w"ahlt. In der Regel ist |QueryResult| jedoch
|
||
mit |GenericLocation<Node, Edge>| identisch.
|
||
Man kann |PostProcess| auch dazu benutzen, um beim LEDA Datentyp
|
||
|planar_map| daf"ur zu sorgen, da"s immer die gebietsbegrenzende Kante
|
||
geliefert wird, falls das gefundene Objekt eine Kante ist
|
||
(vgl. Abschnitt \ref{PLocTraits_Map}).
|
||
<<PLocTraitsInterface>>=
|
||
class QueryResult;
|
||
QueryResult PostProcess(const GenericLocation<Node, Edge>& Result,
|
||
const GenericLocation<Node, Edge>& Result_plus_epsilon,
|
||
const Point& query) const;
|
||
@ %
|
||
|
||
\item Schlie"slich mu"s die Traits-Klasse noch einige Funktionen zur
|
||
Verf"ugung stellen, die es dem |PointLocator| erlauben, sie "uber den
|
||
Anfang, die aktuelle Haltestelle und das Ende des Sweeps zu informieren.
|
||
Die Funktion |clear| wird dazu benutzt, um nicht mehr von der Traits-Klasse
|
||
ben"otigte Daten freizugeben, wenn die Funktion |clear| von |PointLocator|
|
||
aufgerufen wird.%"
|
||
<<PLocTraitsInterface>>=
|
||
void sweep_begin(const Graph&);
|
||
void sweep_moveto(const XCoord&);
|
||
void sweep_end();
|
||
void clear();
|
||
@ \end{itemize}
|
||
Die Traits-Klasse mu"s gewisse Anforderungen bez"uglich Platz- und
|
||
Zeitkomplexit"at erf"ullen, damit man Aussagen "uber den
|
||
Ressourcenbedarf der Punkt-Lokalisierung machen kann.
|
||
Sei $k$ die Summe der Knotenanzahl und der Kantenanzahl des Graphen $G$.
|
||
Dann darf der Platzbedarf der Traits-Klasse $O(k)$ nicht "uberschreiten.
|
||
Als Zeitschranken ergeben sich $O(k \cdot \log k)$ f"ur |sweep_begin| und
|
||
|sweep_end|, $O(k)$ f"ur |clear|, $O(\log k)$ f"ur |PostProcess| und $O(1)$
|
||
f"ur die restlichen Operationen.
|
||
|
||
\rem{
|
||
% === <20>bersicht <20>ber das PLocTraits Interface ================================
|
||
<<PLocTraitsInterface>>=
|
||
class PLocTraitsInterface {
|
||
public:
|
||
// der Graph-Typ sowie dessen Knoten und Kanten
|
||
class Graph;
|
||
class Node;
|
||
class Edge;
|
||
|
||
// Iteratoren fuer die Knoten und Kanten des Graphen
|
||
class NodeIterator;
|
||
NodeIterator Nodes_begin(const Graph& G) const;
|
||
NodeIterator Nodes_end(const Graph& G) const;
|
||
class IncEdgeIterator;
|
||
IncEdgeIterator IncEdges_begin(const Graph& G, const Node& n) const;
|
||
IncEdgeIterator IncEdges_end(const Graph& G, const Node& n) const;
|
||
|
||
Node toNode(const NodeIterator& n);
|
||
Edge toEdge(const IncEdgeIterator& e);
|
||
|
||
// Klassifizierung der Beziehung zwischen Kante und inzidenten Knoten
|
||
enum EdgeCategory
|
||
{ StartingNonVertical, StartingVertical, EndingNonVertical, EndingVertical };
|
||
EdgeCategory ClassifyEdge(const Graph& G, const Edge& e, const Node& n);
|
||
|
||
// der Punkt-Typ, X-Koordinate der Punkte und Vergleichsobjekt dafuer
|
||
class Point;
|
||
class XCoord;
|
||
XCoord getXCoord(const Point& p) const;
|
||
XCoord getXCoord(const Graph& G, const Node& n) const;
|
||
class PredLessThanX;
|
||
PredLessThanX getLessThanX() const;
|
||
|
||
// der Kurven-Typ und Vergleichsobjekt dafuer
|
||
class Curve;
|
||
Curve makeCurve(const Point& p) const;
|
||
Curve makeCurve(const Graph& G, const Node& n) const;
|
||
Curve makeCurve(const Graph& G, const Edge& e) const;
|
||
class PredCompareCurves;
|
||
PredCompareCurves getCompareCurves() const;
|
||
|
||
// Sonstiges
|
||
class QueryResult;
|
||
QueryResult
|
||
PostProcess(const GenericLocation<Node,Edge>& Result,
|
||
const GenericLocation<Node,Edge>& Result_plus_epsilon,
|
||
const Point& query) const;
|
||
void sweep_begin(const Graph&);
|
||
void sweep_moveto(const XCoord&);
|
||
void sweep_end();
|
||
void clear();
|
||
};
|
||
@ }% of rem
|
||
@ % === Generic X Structure ==================================================
|
||
\subsection{Die X-Struktur}
|
||
Die Klasse |GenericXStructure| verwaltet die Sweeplines, die beim Aufbau der
|
||
Datenstruktur zur Punkt-Lokalisierung berechnet werden. Zu jeder Haltestelle
|
||
$x$ eines Sweeps, speichert sie die Sweepline bei $x$ und in $x^+$.
|
||
Die Datenstruktur besteht im wesentlichen aus 2 Feldern:
|
||
Das Feld |Coordinates| enth"alt die Haltestellen in sortierter Reihenfolge,
|
||
das Feld |SweepLines| enth"alt die Sweeplines.
|
||
Daher kann man zu einer gegebenen X-Koordinate mittels bin"arer Suche
|
||
in logarithmischer Zeit die zugeh"orige Sweepline finden.
|
||
%"
|
||
<<Generische X-Struktur>>=
|
||
template <class XCoord, class PredLessThanX, class Sweepline>
|
||
class GenericXStructure {
|
||
public:
|
||
typedef std::vector<XCoord> Array_Coordinates;
|
||
typedef std::vector<Sweepline> Array_Sweeplines;
|
||
typedef typename Array_Coordinates::const_iterator Coord_iterator;
|
||
typedef typename Array_Sweeplines::const_iterator Sweepline_iterator;
|
||
private:
|
||
int stops;
|
||
PredLessThanX LtX;
|
||
Array_Coordinates Coordinates;
|
||
Array_Sweeplines SweepLines;
|
||
// SweepLines[0] is EmptyLine;
|
||
public:
|
||
GenericXStructure() { clear(); }
|
||
GenericXStructure(int n, const PredLessThanX& cmp) { init(n, cmp); }
|
||
~GenericXStructure() { clear(); }
|
||
|
||
void init(int n, const PredLessThanX& cmp)
|
||
{ TRACEN("XSinit "<<n);
|
||
LtX = cmp;
|
||
Coordinates = Array_Coordinates(n);
|
||
SweepLines = Array_Sweeplines(2*n+1);
|
||
stops = 0;
|
||
}
|
||
|
||
void clear()
|
||
{ Coordinates.clear();
|
||
SweepLines.clear();
|
||
stops = 0;
|
||
}
|
||
|
||
void insertLines(const XCoord& X,
|
||
const Sweepline& atX, const Sweepline& inXplus)
|
||
{ TRACEN("XSinsert "<<X);
|
||
Coordinates[stops] = X;
|
||
SweepLines[2*stops+1] = atX;
|
||
SweepLines[2*stops+2] = inXplus;
|
||
++stops;
|
||
}
|
||
|
||
Sweepline_iterator getLineAt(const XCoord& X) const
|
||
{ TRACEN("XSgetLineAt "<<X);
|
||
Sweepline_iterator sit = SweepLines.begin(); // EmptyLine
|
||
if ( LtX(X,*Coordinates.begin()) ) {
|
||
TRACEN("infinity first");
|
||
return sit; // ]-infinity, x0[
|
||
}
|
||
Coord_iterator stopit = std::lower_bound (
|
||
Coordinates.begin(),Coordinates.end(),X,LtX);
|
||
/* determines stopit maximal such that
|
||
\forall j \in [begin,stopit) : *j < X
|
||
as a consequence now: *stopit >= X */
|
||
bool found_exact = false;
|
||
if ( LtX(X,*stopit) ) --stopit; // X < *stopit
|
||
else found_exact = true; // X >= *stopit
|
||
|
||
TRACEN("stopit "<<*stopit);
|
||
int offset = stopit-Coordinates.begin();
|
||
return found_exact ?
|
||
SweepLines.begin() + (2*offset+1) :
|
||
SweepLines.begin() + (2*offset+2);
|
||
}
|
||
|
||
Sweepline_iterator begin() const { return SweepLines.begin(); }
|
||
Sweepline_iterator end() const { return SweepLines.end();}
|
||
|
||
};
|
||
@ Die leere Sweepline-Instanz (|EmptyLine|) wird aus Effizienzgr"unden
|
||
gespeichert, sie erlaubt es der Funktion |getLineAt| eine Referenz
|
||
zur"uckzugeben, so da"s keine Kopie erforderlich ist.
|
||
|
||
Wie bereits im "Uberblick erw"ahnt ist ein Objekt der Klasse |Sweepline| kein
|
||
ephemerer Suchbaum, sondern die Version eines teilweise persistenten
|
||
Suchbaumes, d.h. ein solches Objekt kann in konstantem Platz repr"asentiert
|
||
werden. Somit ist der Platzbedarf der X-Struktur linear in der Anzahl der
|
||
Haltestellen des Sweeps und die Funktion |insertLines| arbeitet in konstanter
|
||
Zeit.
|
||
|
||
@ % === PointLocator =========================================================
|
||
\subsection{Die PointLocator Klasse}
|
||
Die |PointLocator| Klasse verwaltet die Datenstrukturen zur
|
||
Punkt-Lokalisierung und stellt die ben"otigte Funktionalit"at zur Verf"ugung.
|
||
Die Funktionen sind generisch, ihre tats"achliche Arbeitsweise wird
|
||
weitgehend durch die Traits-Klasse bestimmt, mit der die Klasse |PointLocator|
|
||
parametrisiert ist. Um dieser Traits-Klasse die Verwaltung von eigenen
|
||
Datenstrukturen zu erlauben, speichert ein |PointLocator| immer eine Instanz
|
||
seiner Traits. Die zentrale Datenstruktur ist die oben beschriebene X-Struktur.
|
||
%Aus Effizienzgr"unden enth"alt die Klasse eine mit |nil| initialisierte
|
||
%Instanz der |Location| Klasse, so da"s der Wert |nil| nicht wiederholt
|
||
%dynamisch generiert werden mu"s.
|
||
<<PointLocator>>=
|
||
/*{\Manpage {PointLocator} {PLocTraits} {Planar Point Location} {PL}}*/
|
||
|
||
template <class PLocTraits>
|
||
class PointLocator {
|
||
/*{\Mdefinition
|
||
An instance |\Mvar| of the parameterized data type |\Mtype| can be used
|
||
to perform point location queries in the two-dimensional plane.
|
||
Every non-empty instance |\Mvar| is associated with an embedded planar
|
||
graph |G|, which has to remain unchanged while it is referenced by |PL|.\\
|
||
A location query for a point |p| returns the first object (node or edge)
|
||
of |G| which is intersected by the straight ray starting in |p| and going
|
||
vertically downwards/upwards.
|
||
If the ray does not intersect any node or edge of |G|, then |nil| is
|
||
returned.\\
|
||
The class |\Mtype| is generic, it is parameterized with a traits class
|
||
|PLocTraits| which widely controls its behaviour.
|
||
The traits may even change the return type of a query and its semantics.
|
||
There are predined traits classes for the LEDA graph types, which are
|
||
described below in a seperate section.
|
||
}*/
|
||
public:
|
||
<<Importiere Typen aus PLocTraits>>
|
||
|
||
/*{\Mtypes}*/
|
||
// define additional types
|
||
typedef GenericLocation<Node, Edge> Location;
|
||
/*{\Mtypedef usual return value for the point loaction.}*/
|
||
|
||
enum Direction { downwards, upwards};
|
||
/*{\Menum used to specify the direction for the point location.}*/
|
||
|
||
typedef pp_dictionary<Curve, Location, PredCompareCurves> Sweepline;
|
||
typedef GenericXStructure<XCoord, PredLessThanX, Sweepline> XStructure;
|
||
typedef typename Sweepline::item SL_item;
|
||
typedef typename XStructure::Sweepline_iterator Sweepline_iterator;
|
||
<<Operationen von PointLocator>>
|
||
|
||
private:
|
||
PLocTraits traits;
|
||
XStructure X_Structure;
|
||
|
||
};
|
||
|
||
@ \rem{
|
||
<<Importiere Typen aus PLocTraits>>=
|
||
// copied types from PLocTraits
|
||
typedef typename PLocTraits::Point Point;
|
||
typedef typename PLocTraits::XCoord XCoord;
|
||
typedef typename PLocTraits::PredLessThanX PredLessThanX;
|
||
|
||
typedef typename PLocTraits::Graph Graph;
|
||
typedef typename PLocTraits::Node Node;
|
||
typedef typename PLocTraits::Edge Edge;
|
||
typedef typename PLocTraits::NodeIterator NodeIterator;
|
||
typedef typename PLocTraits::IncEdgeIterator IncEdgeIterator;
|
||
|
||
typedef typename PLocTraits::Curve Curve;
|
||
typedef typename PLocTraits::PredCompareCurves PredCompareCurves;
|
||
|
||
typedef typename PLocTraits::QueryResult QueryResult;
|
||
@ }% end of rem
|
||
@ % === PointLocator member functions ========================================
|
||
\pagebreak[4]
|
||
|
||
{\noindent}%
|
||
Im folgenden werden die Operationen von |PointLocator| beschrieben.
|
||
Die Konstruktoren und die Funktion |clear| sind trivial:
|
||
<<Operationen von PointLocator>>=
|
||
/*{\Mcreation}*/
|
||
PointLocator() { clear(); }
|
||
/*{\Mcreate creates an empty |\Mtype|.}*/
|
||
|
||
PointLocator(const Graph& G, const PLocTraits& PLT = PLocTraits()) :
|
||
traits(PLT) { init(G); }
|
||
/*{\Mcreate creates a |\Mtype| for the graph |G| and the traits |PLT|.}*/
|
||
|
||
/*{\Moperations}*/
|
||
void clear() { X_Structure.clear(); traits.clear(); }
|
||
/*{\Mop makes |\Mvar| empty.}*/
|
||
|
||
@ Als n"achstes wird die Operation |init| behandelt, sie baut die
|
||
Datenstrukturen mit Hilfe eines Sweeps auf. %"
|
||
<<Operationen von PointLocator>>=
|
||
|
||
void init(const Graph& G, const PLocTraits& PLT) { traits = PLT; init(G); }
|
||
/*{\Mop makes |\Mvar| a |\Mtype| for the graph |G| and the traits |PLT|.}*/
|
||
|
||
void init(const Graph& G);
|
||
/*{\Mop makes |\Mvar| a |\Mtype| for the graph |G|.}*/
|
||
|
||
<<PointLocator>>=
|
||
template <class PLocTraits>
|
||
void
|
||
PointLocator<PLocTraits>::init(const Graph& G)
|
||
{
|
||
traits.sweep_begin(G);
|
||
<<Initialisierung des Sweeps>>
|
||
<<Sweep>>
|
||
traits.sweep_end();
|
||
}
|
||
|
||
@ Bevor der Sweep beginnen kann, wird die Datenstruktur |stops|
|
||
initialisiert, die dazu benutzt wird, die einzelnen Haltestellen des
|
||
Sweeps in sortierter Reihenfolge zu durchlaufen. Bei der
|
||
Initialisierung wird mit jeder Haltestelle die Liste aller Knoten
|
||
gespeichert, die an dieser Stelle auf der Sweepline liegen.
|
||
Schlie"slich wird die leere Ausgangssweepline konstruiert und die
|
||
X-Struktur initialisiert. %
|
||
<<Initialisierung des Sweeps>>=
|
||
PredLessThanX LtX = traits.getLessThanX();
|
||
typedef std::map<XCoord, std::list<Node>, PredLessThanX> dictionary;
|
||
typedef typename dictionary::iterator dic_iterator;
|
||
dictionary stops(LtX);
|
||
// Note: X_Structure, Sweepline, and stops copy compare object
|
||
|
||
NodeIterator ni = traits.Nodes_begin(G), beyond = traits.Nodes_end(G);
|
||
for(; ni != beyond; ++ni) {
|
||
XCoord currentX = traits.getXCoord(G, ni);
|
||
stops[currentX].push_front(traits.toNode(ni));
|
||
}
|
||
|
||
Sweepline SL(traits.getCompareCurves());
|
||
X_Structure.init(stops.size(), LtX);
|
||
@ Der Sweep besucht die Haltestellen in sortierter Reihenfolge, berechnet
|
||
f"ur jede Stelle |x| den Zustand der Sweepline in |x| und den Zustand in
|
||
$x^+$ und f"ugt die Zust"ande in die X-Struktur ein: %"
|
||
<<Sweep>>=
|
||
dic_iterator stop;
|
||
for(stop = stops.begin(); stop != stops.end(); ++stop) {
|
||
std::list<Node>& NodesOnSL = stop->second;
|
||
traits.sweep_moveto(traits.getXCoord(G, *NodesOnSL.begin()));
|
||
<<Berechne die Sweeplines bei $x~(\mathit{SLatX})$ und in $x^+~(\mathit{SL})$>>
|
||
X_Structure.insertLines(traits.getXCoord(G, *NodesOnSL.begin()),
|
||
SL_at_X, SL);
|
||
}
|
||
|
||
@ Die Zust"ande der Sweepline bei |x| und in $x^+$ werden gr"o"stenteils
|
||
simultan berechnet. Es werden alle Knoten, die auf der Sweepline liegen, und
|
||
deren inzidente Kanten abgearbeitet. Startende Kanten werden in der jeweiligen
|
||
Liste (|EmergingEdges| bzw. |VerticalEdges|) zur sp"ateren Verarbeitung
|
||
gespeichert; endende nichtvertikale Kanten werden sofort aus der Sweepline
|
||
gel"oscht; endende vertikale Kanten werden ignoriert, da man sie sonst doppelt
|
||
bearbeiten w"urde.
|
||
<<Berechne die Sweeplines bei $x~(\mathit{SLatX})$ und in $x^+~(\mathit{SL})$>>=
|
||
std::list<Edge> EmergingEdges, VerticalEdges;
|
||
|
||
// explore the nodes on SL
|
||
typename std::list<Node>::iterator cur_node;
|
||
for(cur_node = NodesOnSL.begin();
|
||
cur_node != NodesOnSL.end(); ++cur_node) {
|
||
IncEdgeIterator ei = traits.IncEdges_begin(G, *cur_node);
|
||
IncEdgeIterator beyond = traits.IncEdges_end(G, *cur_node);
|
||
TRACEN("NODE: "<<(*cur_node)->point());
|
||
for(; ei != beyond; ++ei) {
|
||
switch (traits.ClassifyEdge(G, traits.toEdge(ei), *cur_node)) {
|
||
case PLocTraits::StartingNonVertical:
|
||
EmergingEdges.push_front(traits.toEdge(ei)); break;
|
||
case PLocTraits::StartingVertical:
|
||
VerticalEdges.push_front(traits.toEdge(ei)); break;
|
||
case PLocTraits::EndingNonVertical:
|
||
SL.del(traits.makeCurve(G, traits.toEdge(ei))); break;
|
||
case PLocTraits::EndingVertical: break;
|
||
}
|
||
}
|
||
}
|
||
|
||
@ Nachdem in obigem Schritt alle Kanten aus der Sweepline entfernt wurden, die
|
||
diese nicht mehr schneiden, enth"alt die Sweepline nun nur noch die Kanten,
|
||
die sie "`"uberqueren"'. Jetzt f"ugt man die Objekte des Graphen |G| ein, die
|
||
ganz auf der Sweepline liegen: die vertikalen Kanten und die Knoten in
|
||
|NodesOnSL|. So erh"alt man die Sweepline bei $x$.
|
||
<<Berechne die Sweeplines bei $x~(\mathit{SLatX})$ und in $x^+~(\mathit{SL})$>>=
|
||
// compute SL_at_X
|
||
|
||
typename std::list<Edge>::iterator cur_edge;
|
||
for(cur_edge=VerticalEdges.begin();
|
||
cur_edge!=VerticalEdges.end(); ++cur_edge)
|
||
SL.insert(traits.makeCurve(G, *cur_edge), Location(*cur_edge));
|
||
for(cur_node=NodesOnSL.begin();
|
||
cur_node!=NodesOnSL.end(); ++cur_node)
|
||
SL.insert(traits.makeCurve(G, *cur_node), Location(*cur_node));
|
||
Sweepline SL_at_X = SL;
|
||
|
||
@ Um die Sweepline in $x^+$ zu berechnen, mu"s man die Objekte, die ganz auf
|
||
der Sweepline liegen, wieder l"oschen.
|
||
(Bei einer voll persistenten Datenstruktur k"onnte man sich diese Arbeit
|
||
sparen.) %"
|
||
Dann mu"s man noch die nichtvertikalen Kanten einf"ugen, deren Startpunkt auf
|
||
der Sweepline liegt.
|
||
<<Berechne die Sweeplines bei $x~(\mathit{SLatX})$ und in $x^+~(\mathit{SL})$>>=
|
||
// compute SL_in_X_plus
|
||
|
||
for(cur_edge=VerticalEdges.begin();
|
||
cur_edge!=VerticalEdges.end(); ++cur_edge)
|
||
SL.del(traits.makeCurve(G, *cur_edge));
|
||
for(cur_node=NodesOnSL.begin();
|
||
cur_node!=NodesOnSL.end(); ++cur_node)
|
||
SL.del(traits.makeCurve(G, *cur_node));
|
||
|
||
for(cur_edge=EmergingEdges.begin();
|
||
cur_edge!=EmergingEdges.end(); ++cur_edge)
|
||
SL.insert(traits.makeCurve(G, *cur_edge), Location(*cur_edge));
|
||
|
||
@ Damit ist die Beschreibung der Initialisierung der Datenstruktur
|
||
abgeschlossen. Man kann nun Schranken f"ur den Platzverbrauch der
|
||
Klasse |PointLocator| und f"ur die Laufzeit der Operation |init|
|
||
ermitteln. Der Sweepline-Algorithmus f"ugt jedes Objekt des Graphen
|
||
$G$, egal ob Knoten oder Kante, genau einmal in eine Sweepline ein und
|
||
l"oscht es wieder. Auf dem persistenten Suchbaum, der die Sweeplines
|
||
speichert, werden also $2 k$ "Anderungsoperationen ausgef"uhrt, wobei
|
||
$k$ die Anzahl der Objekte des Graphen $G$ sei. F"ur jede Operation
|
||
ist der erwartete Platzbedarf konstant und die erwartete Laufzeit
|
||
$O(\log k)$. Die Anzahl $n$ der Haltestellen des Sweeps ist
|
||
h"ochstens so gro"s wie die Anzahl der Knoten des Graphen; daher ist
|
||
sowohl die Zeit zum Einf"ugen der Sweeplines in die X-Struktur als
|
||
auch deren Platzbedarf $O(n) = O(k)$. Somit ergibt sich insgesamt
|
||
eine erwartete Laufzeit von $O(k \cdot \log k)$ f"ur den Sweep und ein
|
||
erwarteter Platzbedarf von $O(k)$.
|
||
\medskip
|
||
|
||
Als n"achstes werden die eigentlichen Lokalisierungsoperationen vorgestellt.
|
||
Die Operation |locate| ruft eine der beiden Operationen |locate_down| und
|
||
|locate_up| auf, je nachdem welche Richtung ihr "ubergeben wurde. %"
|
||
<<Operationen von PointLocator>>=
|
||
|
||
QueryResult locate(const Point& p, const Direction dir) const
|
||
{ return dir == downwards ? locate_down(p) : locate_up(p); }
|
||
/*{\Mop locates the point |p| in the direction |dir|.}*/
|
||
|
||
QueryResult locate_down(const Point& p) const;
|
||
/*{\Mop locates the point |p| vertically downwards.}*/
|
||
|
||
QueryResult locate_up(const Point& p) const;
|
||
/*{\Mop locates the point |p| vertically upwards.}*/
|
||
|
||
Location location(Sweepline_iterator S, SL_item it) const
|
||
{ return (it == nil ? Location() : S->inf(it)); }
|
||
|
||
std::string str(const Sweepline& S) const
|
||
{ std::ostrstream os; os << "Sweepline:\n";
|
||
SL_item it;
|
||
forall_items(it,S) { os << " " << S.key(it) << std::endl; }
|
||
std::string res(os.str());
|
||
os.freeze(0);
|
||
return res;
|
||
}
|
||
|
||
@ Die beiden letztgenannten Operationen sind sehr "ahnlich:
|
||
Zuerst rekonstruiert man den Zustand der Sweepline an der X-Koordinate des
|
||
gegebenen Punktes |p|. Diese Sweepline fragt man nach dem Vorg"anger bzw.
|
||
Nachfolger von |p|. Falls dieser nicht existiert, liefert man einfach die
|
||
leere Lokation |nil| zur"uck, ansonsten liefert man die entsprechende
|
||
Lokation, wobei man der Traits-Klasse noch die M"oglichkeit zur
|
||
Nachbearbeitung gibt.
|
||
<<PointLocator>>=
|
||
template <class PLocTraits>
|
||
PointLocator<PLocTraits>::QueryResult
|
||
PointLocator<PLocTraits>::
|
||
locate_down(const typename PLocTraits::Point& p) const
|
||
{
|
||
Sweepline_iterator line_at_x = X_Structure.getLineAt(traits.getXCoord(p)),
|
||
line_plus = line_at_x;
|
||
TRACEN("locate_down "<<str(*line_at_x));
|
||
Curve p_curve = traits.makeCurve(p);
|
||
PredCompareCurves cmp = traits.getCompareCurves();
|
||
SL_item it = line_at_x->locate_pred(p_curve), it_plus(0);
|
||
if ( it && line_at_x->inf(it).is_node() &&
|
||
cmp(p_curve, line_at_x->key(it))!=0 ) {
|
||
// p hit a feature exactly
|
||
line_plus = line_at_x+1;
|
||
if ( line_plus != X_Structure.end() )
|
||
it_plus = line_plus->locate_pred(p_curve);
|
||
}
|
||
return traits.PostProcess(location(line_at_x,it),
|
||
location(line_plus,it_plus),p);
|
||
}
|
||
|
||
template <class PLocTraits>
|
||
PointLocator<PLocTraits>::QueryResult
|
||
PointLocator<PLocTraits>::locate_up(const typename PLocTraits::Point& p) const
|
||
{
|
||
Sweepline_iterator line_at_x =
|
||
X_Structure.getLineAt(traits.getXCoord(p)), line_plus;
|
||
Curve p_curve = traits.makeCurve(p);
|
||
PredCompareCurves cmp = traits.getCompareCurves();
|
||
SL_item it = line_at_x->locate_succ(p_curve), it_plus(0);
|
||
if ( it && line_at_x->inf(it).is_node() &&
|
||
cmp(p_curve, line_at_x->key(it))!=0 ) {
|
||
// p hit a feature exactly
|
||
line_plus = line_at_x+1;
|
||
if ( line_plus != X_Structure.end() )
|
||
it_plus = line_plus->locate_succ(p_curve);
|
||
}
|
||
return traits.PostProcess(location(line_at_x,it),
|
||
location(line_plus,it_plus), p);
|
||
}
|
||
|
||
@ Die erwartete Laufzeit dieser Operationen ist $O(\log k)$; denn die
|
||
X-Struktur ben"otigt $O(\log n) = O(\log k)$ Zeit zum Auffinden der richtigen
|
||
Sweepline, der Erwartungswert f"ur die Zeit zur Lokalisierung des Punktes
|
||
in der Sweepline ist ebenfalls $O(\log k)$ und die Funktion |PostProcess| mu"s
|
||
diese Laufzeitschranke auch einhalten.
|
||
@ \rem{
|
||
<<PointLocator>>=
|
||
/*{\Mimplementation
|
||
The implementation of the data type |\Mtype| is based on partially
|
||
persistent binary search trees.
|
||
The expected space requirement is $O(k)$ where $k$ is the sum of the number
|
||
of nodes and the number of edges in the graph $G$.
|
||
The expected time needed for construction and the operation |init| is
|
||
$O(k \cdot \log k)$, for the |locate|-operations it is $O(\log k)$. The
|
||
operation |clear| runs in $O(k)$.
|
||
}*/
|
||
/*{\Mtext
|
||
\headerline{\arabic{manctr}. Predefined traits classes}
|
||
\stepcounter{manctr}
|
||
All predefined traits classes have in common that the return type of a query
|
||
is the type |Location|.
|
||
The embedding of the given graph |G| is a straight-line embedding, so that
|
||
it is totally determined by the position of the nodes of |G|.
|
||
Such a position is specified by a |Point| which can be one of the LEDA point
|
||
types |point| or |rat_point|. The positions can be specified implicitly by
|
||
the node attribute of a parameterized graph (e.g. |GRAPH<Point,...>|) or
|
||
explicitly by a |node_array<Point>|. In case of explicit specification a
|
||
|node_array| with the positions of the nodes can be passed to the constructor
|
||
of the traits class.
|
||
Further, the point location processes for maps and for standard graphs differ
|
||
slightly. As a map is a bidirected graph where each edge knows its reversal,
|
||
the traits classes for maps can ensure the following property:
|
||
If the result of a query for point |p| is an edge |e| (not containing |p|),
|
||
then |e| bounds the face of |G| which contains |p|, i.e. |p| lies to the
|
||
left of |e|.\\
|
||
Here comes a list of the predefined traits classes:\\[-5.5ex]
|
||
\begin{itemize}
|
||
\item |PLocTraits<Graph>|: standard traits for implicitly specified node
|
||
positions\\
|
||
|Graph| can be |GRAPH<Point,...>| (standard graph) or
|
||
|PLANAR_MAP<Point,...,...>| (map).
|
||
\item |PLocTraits_NodeArray<Graph,Point>|: std. traits for explicitly
|
||
specified node positions\\
|
||
|Graph| can be |graph| (standard graph) or |planar_map| (map).
|
||
\item |PLocTraits_Map<Graph>| and |PLocTraits_Map_NodeArray<Graph,Point>|:\\
|
||
The parameter |Graph| can be |GRAPH<Point,...>| and |graph| respectively.
|
||
These traits classes assume that the given graphs are maps.
|
||
\item |PLocTraits< GRAPH<Circle,Point> >|: traits class for closest-site
|
||
voronoi diagrams
|
||
\end{itemize}
|
||
Note that a traits class instantiated with |Graph| can also handle graph
|
||
types which are derived from |Graph|. Thus |PLocTraits< graph<Point,T> >|
|
||
can be used for graphs of type |ugraph<Point,T>| for example.
|
||
}*/
|
||
/*{\Mexample
|
||
First we show an example where the node positions are given implicitly
|
||
as node attributes:
|
||
\begin{verbatim}
|
||
typedef PointLocator< PLocTraits< GRAPH<Point, int> > > PLocator1;
|
||
typedef PLocator1::Location Location;
|
||
|
||
UGRAPH<Point, int> G;
|
||
... // construct G
|
||
|
||
PLocator1 PL1(G);
|
||
Point p = ...; // compute p
|
||
Location L1 = PL1.locate_down(p);
|
||
\end{verbatim}
|
||
|
||
The second example shows how a |node_array| can be used to determine the
|
||
node positions:
|
||
\begin{verbatim}
|
||
typedef PLocTraits_NodeArray<planar_map,Point> PLocTraits2;
|
||
typedef PointLocator<PLocTraits2> PLocator2;
|
||
|
||
planar_map pm;
|
||
node_array<Point> na;
|
||
... // construct pm and na
|
||
|
||
PLocator2 PL2(pm, PLocTraits2(na));
|
||
Point q = ...; // compute q
|
||
Location L2 = PL2.locate_up(q);
|
||
\end{verbatim}
|
||
}*/
|
||
@ }% end of rem
|
||
@ % === Traits f<>r LEDA Graphen ==============================================
|
||
\section{Traits f"ur LEDA Graphen} \label{PLocTraits_LEDA_Graph} %"
|
||
In diesem Abschnitt werden einige Traits-Klassen entwickelt, die es erlauben,
|
||
LEDA Graphentypen mit der |PointLocator|-Klasse zu benutzen.
|
||
Bei den Einbettungen der Graphen in die Ebene sind die Kurven, die mit den
|
||
Kanten assoziiert sind, immer Geradensegmente. Deshalb wird die Einbettung
|
||
durch die Angabe der Positionen der Knoten eindeutig festgelegt.
|
||
Diese Positionen werden durch Objekte vom Typ |Point| spezifiziert, wobei
|
||
|Point| einer der LEDA Punkttypen |point| oder |rat_point| sein kann.
|
||
Die "Ubergabe der Positionen erfolgt entweder implizit durch Knotenattribute
|
||
vom Typ |Point| oder explizit durch ein |node_array<Point>|.\\
|
||
Hier ist eine "Ubersicht "uber die Traits-Klassen, die dem Anwender zur
|
||
Verf"ugung gestellt werden:
|
||
\begin{enumerate}
|
||
\item |PLocTraits< GRAPH<Point,...> >| und
|
||
|PLocTraits_NodeArray<graph>|
|
||
\item |PLocTraits< PLANAR_MAP<Point,...,...> >| und
|
||
|PLocTraits_NodeArray<planar_map>|
|
||
\item |PLocTraits_Map< GRAPH<Point,...> >| und
|
||
|PLocTraits_Map_NodeArray<graph>|
|
||
\end{enumerate}
|
||
Eine Traits-Klasse, die mit dem Typ |Graph| parametrisiert ist, kann auch
|
||
f"ur Graphentypen benutzt werden, die von |Graph| abgeleitet sind.
|
||
So kann |PLocTraits_NodeArray<graph>| z.B. nicht nur mit dem Typ |graph|,
|
||
sondern auch mit dem Typ |ugraph| verwendet werden.
|
||
|
||
@ \rem{
|
||
<<LEDA traits>>=
|
||
// exported traits classes
|
||
template <class ParameterizedGraph> class PLocTraits;
|
||
template <class Graph, class Point> class PLocTraits_NodeArray;
|
||
template <class ParameterizedGraph> class PLocTraits_Map;
|
||
template <class Graph, class Point> class PLocTraits_Map_NodeArray;
|
||
@ }% end of rem
|
||
Bei der Punkt-Lokalisierung wird zwischen Standardgraphen (Punkt 1.) und
|
||
Karten (Punkte 2. und 3.) unterschieden.
|
||
Ein gerichteter Graph $G=(V,E)$ ohne Schleifen und parallele Kanten ist eine
|
||
\emph{Karte} (vgl. \cite{LEDA_book} Abschnitt 8.2), falls f"ur jede Kante
|
||
$e=(v,w)$ in |E| auch die Umkehrkante $e^R=(w,v)$ in |E| enthalten ist und
|
||
jede Kante ihre Umkehrkante "`kennt"'. Der Begriff "`kennen"' bedeutet hier,
|
||
da"s jede Kante einen Zeiger hat, der auf die Umkehrkante verweist.
|
||
Man kann sich vorstellen, da"s die Menge $\SET{e,e^R}$ eine ungerichtete Kante
|
||
in der Karte darstellt, zumal die Kurven $K(e)$ und $K(e^R)$ zusammenfallen.
|
||
Bei der Lokalisierung eines Punktes |p| in der Sweepline k"onnen die beiden
|
||
Kanten daher nicht unterschieden werden, und es wird irgendeine der Kanten
|
||
geliefert, falls der von |p| ausgehende Strahl als erstes die Kurve $K(e)$
|
||
schneidet.
|
||
Man m"ochte jedoch sicherstellen, da"s die Antwort auf die Anfrage die Kante
|
||
ist, die das Gebiet begrenzt, in dem |p| liegt
|
||
(vgl. Abbildung \figref{PLoc_in_Map}).
|
||
\Figger{PLoc_in_Map}
|
||
{Lokalisierung des Punktes |p| in der Karte liefert die Kante |e|}
|
||
{PLoc_in_Map}
|
||
Die Traits-Klassen f"ur Karten stellen mit Hilfe der Funktion |PostProcess|
|
||
sicher, da"s |p| nicht negativ bez"uglich der gelieferten Kante orientiert
|
||
ist, d.h. |p| liegt niemals rechts von der orientierten Gerade durch die
|
||
Punkte |P(source(e))| und |P(target(e))|.%"
|
||
|
||
@ % === Hilfsklassen =========================================================
|
||
\subsection{Hilfsklassen}
|
||
|
||
Oftmals ist es bei der Implementierung wichtig, zu einem gegebenen Punkttyp
|
||
die korrespondierenden Typen wie z.B. Koordinaten- oder Segmenttyp
|
||
herausfinden zu k"onnen. Dazu dient die Klasse |LEDA_PointTraits|, die f"ur
|
||
die LEDA Punkttypen |point| und |rat_point| spezialisiert ist.
|
||
<<LEDA traits>>=
|
||
template <class point_type>
|
||
class LEDA_PointTraits;
|
||
|
||
template<>
|
||
class LEDA_PointTraits<point> {
|
||
public:
|
||
typedef double Coord;
|
||
typedef point Point;
|
||
typedef segment Segment;
|
||
typedef vector Vector;
|
||
typedef circle Circle;
|
||
};
|
||
|
||
template<>
|
||
class LEDA_PointTraits<rat_point> {
|
||
public:
|
||
typedef rational Coord;
|
||
typedef rat_point Point;
|
||
typedef rat_segment Segment;
|
||
typedef rat_vector Vector;
|
||
typedef rat_circle Circle;
|
||
};
|
||
|
||
@ % === Compare Funktionsobjekt f<>r LEDA Segmente =============================
|
||
Die Kurven, die mit den Knoten und Kanten eines LEDA Graphen in der planaren
|
||
Einbettung assoziiert sind, werden durch LEDA Segmente modelliert.
|
||
Die Klasse |LEDA_CompareSegments| dient zum Vergleich dieser Segmente;
|
||
sie realisiert den Typ |PredCompareCurves| f"ur die Traits-Klassen.
|
||
Es wird nicht nur der Funktionsaufruf-Operator zur Verf"ugung gestellt, der
|
||
den Vergleich durchf"uhrt, sondern auch statische Funktionen zum Erzeugen von
|
||
Segmenten.
|
||
Diese Funktionen stellen sicher, da"s die erzeugten Segmente die folgenden
|
||
Invarianten erf"ullen, was eine effiziente Implementierung der
|
||
Vergleichsfunktion erlaubt.
|
||
\begin{enumerate}
|
||
\item F"ur jedes Segment $s = \Seg{a}{b}$ gilt: $a \preceq_{lex} b$.
|
||
\item Bei einem trivialen Segment $s = \Seg{a}{a}$ haben die Punkte nicht nur
|
||
die gleichen Koordinaten, sondern die sie repr"asentierenden Objekte sind
|
||
identisch.
|
||
(Wenn man also die Funktion |identical| von LEDA auf die Randpunkte eines
|
||
Segmentes anwendet, kann man ohne Koordinatenvergleich herausfinden, ob das
|
||
Segment trivial ist.)
|
||
\end{enumerate}
|
||
%"
|
||
@ \rem{
|
||
<<Importiere Typen aus PointTraits>>=
|
||
typedef typename PointTraits::Coord Coord;
|
||
typedef typename PointTraits::Point Point;
|
||
typedef typename PointTraits::Segment Segment;
|
||
@ }% of rem
|
||
<<LEDA traits>>=
|
||
template <class PointTraits>
|
||
class LEDA_CompareSegments {
|
||
public:
|
||
<<Importiere Typen aus PointTraits>>
|
||
|
||
int operator() (const Segment& s1, const Segment& s2) const;
|
||
|
||
static Segment CreateSegment(const Point& p, const Point& q)
|
||
{ return (compare(p,q) < 0) ? Segment(p,q) : Segment(q,p); }
|
||
static Segment CreateTrivialSegment(const Point& p)
|
||
{ return Segment(p,p); }
|
||
|
||
private:
|
||
inline int Cmp_Pnt_NonTrivSeg(const Point& p, const Segment& s) const;
|
||
};
|
||
|
||
@ Bei der Punkt-Lokalisierung ist mindestens eines der beiden am Vergleich
|
||
beteiligten Segmente $s_1$ und $s_2$ ein Punkt, also trivial.
|
||
Dieser Fall wird von der Vergleichsfunktion zuerst behandelt.
|
||
Sind beide Segmente in Wirklichkeit Punkte, so mu"s man deren Y-Koordinaten
|
||
vergleichen.
|
||
Ist jedoch eines der Segmente nicht trivial, so wird die Funktion
|
||
|Cmp_Pnt_NonTrivSeg| benutzt, um das Ergebnis des Vergleichs von $s_1$ und
|
||
$s_2$ zu bestimmen.%"
|
||
<<LEDA traits>>=
|
||
template <class PointTraits>
|
||
int
|
||
LEDA_CompareSegments<PointTraits>::operator()
|
||
(const typename PointTraits::Segment& s1,
|
||
const typename PointTraits::Segment& s2) const
|
||
{
|
||
const Point& a = s1.source(); const Point& b = s1.target();
|
||
const Point& c = s2.source(); const Point& d = s2.target();
|
||
|
||
if (identical(a,b)) {
|
||
if (identical(c,d)) return compare(a.ycoord(), c.ycoord());
|
||
return Cmp_Pnt_NonTrivSeg(a, s2);
|
||
}
|
||
if (identical(c,d)) return - Cmp_Pnt_NonTrivSeg(c, s1);
|
||
|
||
<<Vergleiche zwei nicht-triviale Segmente>>
|
||
}
|
||
|
||
@ Die Hilfsfunktion |Cmp_Pnt_NonTrivSeg| liefert die vertikale Lage des
|
||
Punktes |p| bez"uglich des nichttrivialen Segmentes |s|.
|
||
Voraussetzung ist, da"s |p| und |s| vergleichbar sind, d.h. die Parallele zur
|
||
Y-Achse durch |p| schneidet |s|.
|
||
Wenn das Segment nicht vertikal ist, so ist das Ergebnis einfach die
|
||
Orientierung von |p| bzgl. |s| (vgl. \cite{LEDA_Manual} Abschnitt
|
||
"`segment"').
|
||
Ansonsten mu"s man |p| mit dem Start- und dem Endpunkt von |s| hinsichtlich
|
||
der Y-Koordinate vergleichen.%"
|
||
\label{Cmp_Pnt_Edge}
|
||
<<LEDA traits>>=
|
||
template <class PointTraits>
|
||
inline
|
||
int
|
||
LEDA_CompareSegments<PointTraits>::Cmp_Pnt_NonTrivSeg
|
||
(const typename PointTraits::Point& p,
|
||
const typename PointTraits::Segment& s) const
|
||
{
|
||
if (!s.is_vertical()) return orientation(s, p);
|
||
if (p.ycoord() <= s.ycoord1()) return -1;
|
||
if (p.ycoord() >= s.ycoord2()) return +1;
|
||
return 0;
|
||
}
|
||
|
||
@ Sind die zu vergleichenden Segmente beide nicht trivial, was nur w"ahrend
|
||
eines Sweeps auftreten kann, so vergleicht man zun"achst die X-Koordinaten
|
||
der Anfangspunkte. \label{Cmp_Segments}
|
||
Wenn diese verschieden sind, so gen"ugt die Berechnung einer Orientierung
|
||
eines Punktes bzgl. eines Segmentes um die vertikale Lage der Segmente zu
|
||
bestimmen. Dies sieht man wie folgt:
|
||
\begin{quote}
|
||
Sei $s_1 = \Seg{a}{b}$ und $s_2 = \Seg{c}{d}$ sowie o.E. $a_x < c_x$.
|
||
Dann mu"s $c_x \leq b_x$ sein, denn sonst w"aren die Segmente nicht
|
||
vergleichbar.
|
||
Also ist $a_x < b_x$, so da"s $s_1$ nicht vertikal sein kann.
|
||
Somit liegt $s_1$ genau dann unter $s_2$, falls |c| "uber $s_1$ liegt, also
|
||
positiv orientiert ist.
|
||
\end{quote}%"
|
||
<<Vergleiche zwei nicht-triviale Segmente>>=
|
||
int cmpX = compare(a.xcoord(), c.xcoord());
|
||
if (cmpX < 0) return - orientation(s1, c);
|
||
if (cmpX > 0) return orientation(s2, a);
|
||
|
||
@ Stimmen die X-Koordinaten der Anfangspunkte "uberein, so werden auch die
|
||
Y-Koordinaten verglichen. Sind diese verschieden, so ist die vertikale Lage
|
||
der Segmente klar, da ihre Anfangspunkte "ubereinander liegen.
|
||
Ansonsten sind die Anfangspunkte identisch und die Orientierung des Endpunktes
|
||
von $s_1$ bzgl. $s_2$ liefert das Ergebnis des Vergleiches. Dies sieht man
|
||
wie folgt:
|
||
\begin{quote}
|
||
Falls beide Segmente nicht vertikal sind, ist die Behauptung offensichtlich.
|
||
Ist aber eines der Segmente vertikal, so m"ussen aufgrund der Definition
|
||
von Vergleichbarkeit beide Segmente vertikal sein. Beide haben also
|
||
denselben Anfangspunkt, und ihre Endpunkte liegen (wegen Invariante 2.)
|
||
"uber diesem Anfangspunkt; d.h. die Segmente schneiden sich.
|
||
Da das Ergebnis der Orientierungsberechnung in diesem Fall 0 ist, arbeitet
|
||
die Funktion auch dann korrekt.
|
||
\end{quote}
|
||
<<Vergleiche zwei nicht-triviale Segmente>>=
|
||
int cmpY = compare(a.ycoord(), c.ycoord());
|
||
if (cmpY != 0) return cmpY;
|
||
|
||
// cmpX == cmpY == 0 => a == c
|
||
return orientation(s2, b);
|
||
|
||
@ \rem{
|
||
<<LEDA traits>>=
|
||
/*
|
||
The following declarations are necessary to use LEDA Node Data Accessors
|
||
with nodes instead of iterators.
|
||
*/
|
||
|
||
template<>
|
||
inline
|
||
node get_object(LedaNodeAccessor, const node& it) { return it; }
|
||
|
||
template<class SN>
|
||
inline
|
||
SN& get_object(const LedaNodeMemberAccessor&,const SN&, const node& it) {
|
||
return ((GIT_dummygraph<SN>*)(graph_of(it)))->get_entry(it); }
|
||
@ }% end of rem
|
||
|
||
@ % === Traits-Basisklasse ====================================================
|
||
\subsection{Traits-Basisklasse}
|
||
|
||
Die Basisklasse |LEDA_PLocTraits| erf"ullt die Anforderungen an eine
|
||
Traits-Klasse f"ur die Klasse |PointLocator|
|
||
(vgl. Seite \pageref{PLocTraits_interface} ff).
|
||
Sie ist nicht nur mit dem Graphentyp parametrisiert, sondern besitzt noch
|
||
zwei weitere Parameter.
|
||
Der |NodeDataAccessor| dient dazu, den Zugriff auf die Position |P(v)| eines
|
||
Knotens |v| unabh"angig von deren Repr"asentation zu machen, d.h. der Code
|
||
von |getPosition| ist unabh"angig davon, ob die Information in einem
|
||
|node_array| oder beim Knoten selbst gespeichert ist, wie dies z.B. bei einem
|
||
Graph vom Typ |GRAPH<Point,...>| der Fall ist.
|
||
Dieses Konzept wurde von Marco Nissen \cite{Nissen_diplom} f"ur LEDA
|
||
entwickelt; in derselben Arbeit wurden auch die Iteratoren f"ur LEDA Graphen
|
||
implementiert.
|
||
Der letzte Parameter der Traits-Klasse ist der Iterator f"ur die zu einem
|
||
Knoten inzidenten Kanten. Bei einem Standardgraphen wird hier ein Iterator
|
||
benutzt, der alle inzidenten Kanten durchl"auft; bei einer Karte reicht
|
||
dagegen ein Iterator, der nur die ausgehenden Kanten durchmustert, da die
|
||
eingehenden Kanten gerade die Umkehrungen der ausgehenden sind, so da"s die
|
||
assoziierten Kurven "ubereinstimmen.%"
|
||
<<LEDA traits>>=
|
||
template <class GraphType, class NodeDataAccessor, class IncEdgeIt>
|
||
class LEDA_PLocTraits {
|
||
public:
|
||
typedef GraphType Graph;
|
||
typedef typename NodeDataAccessor::value_type Point;
|
||
|
||
<<Operationen und Typen von LEDA_PLocTraits>>
|
||
|
||
virtual ~LEDA_PLocTraits() {}
|
||
|
||
protected:
|
||
NodeDataAccessor positions;
|
||
const Point& getPosition(node n) const
|
||
{ return access(const_cast<NodeDataAccessor&>(positions), n); }
|
||
};
|
||
|
||
@ Die in LEDA vorhandenen Typen f"ur Knoten, Kanten und Iteratoren erf"ullen
|
||
die f"ur die Traits-Klasse geforderten Eigenschaften und k"onnen daher
|
||
"ubernommen werden. %"
|
||
<<Operationen und Typen von LEDA_PLocTraits>>=
|
||
typedef node Node;
|
||
typedef edge Edge;
|
||
|
||
typedef NodeIt_n NodeIterator;
|
||
NodeIterator Nodes_begin(const Graph& G) { return NodeIterator(G); }
|
||
NodeIterator Nodes_end(const Graph& G) { return NodeIterator(G).end(); }
|
||
|
||
typedef IncEdgeIt IncEdgeIterator;
|
||
IncEdgeIterator IncEdges_begin(const Graph& G, const Node& n)
|
||
{ return IncEdgeIterator(G, n); }
|
||
IncEdgeIterator IncEdges_end(const Graph& G, const Node& n)
|
||
{ return IncEdgeIterator(G, n).end(); }
|
||
|
||
@ Die Einteilung einer Kante in die 4 Kategorien von Seite
|
||
\pageref{PLoc_edge_classification} erfolgt durch Anwendung der Definition
|
||
der einzelnen Kategorien:
|
||
<<Operationen und Typen von LEDA_PLocTraits>>=
|
||
enum EdgeCategory
|
||
{ StartingNonVertical, StartingVertical, EndingNonVertical, EndingVertical };
|
||
EdgeCategory ClassifyEdge(const Graph& G, const Edge& e, const Node& u)
|
||
{
|
||
Point p_u = getPosition(u);
|
||
Point p_v = getPosition(G.opposite(u, e));
|
||
|
||
int cmpX = compare(p_u.xcoord(), p_v.xcoord());
|
||
if (cmpX < 0) return StartingNonVertical;
|
||
if (cmpX > 0) return EndingNonVertical;
|
||
|
||
int cmpY = compare(p_u.ycoord(), p_v.ycoord()); assert(cmpY != 0);
|
||
if (cmpY < 0) return StartingVertical;
|
||
return EndingVertical;
|
||
}
|
||
|
||
@ Als X-Koordinaten (Haltestellen) werden die tats"achliche Koordinaten der
|
||
Punkte, also |double| bzw. |rational| benutzt.
|
||
Ein interessantes Implementierungsdetail ist hier, da"s die Klasse
|
||
|PredCompareX| nicht einfach als |leda_cmp_base<XCoord>| definiert, sondern
|
||
davon abgeleitet wird. Das Verhalten w"are in beiden F"allen das gleiche:
|
||
Zur Durchf"uhrung des Vergleiches wird die Funktion |compare| aufgerufen,
|
||
die von LEDA zur Verf"ugung gestellt wird.
|
||
Es bestehen jedoch Unterschiede hinsichtlich der Effizienz.
|
||
Die hier vorgestellte Variante erm"oglicht dem Compiler ein Inlining der
|
||
|compare|-Funktion.
|
||
Bei der anderen Variante (vgl. |param_types.h| von LEDA) w"urde die Funktion
|
||
"uber einen Funktionszeiger aufgerufen, was die meisten Compiler nicht
|
||
weg\-optimieren k"onnen.
|
||
<<Operationen und Typen von LEDA_PLocTraits>>=
|
||
typedef typename LEDA_PointTraits<Point>::Coord XCoord;
|
||
const XCoord getXCoord(const Point& p) const
|
||
{ return p.xcoord(); }
|
||
const XCoord getXCoord(const Graph& G, const Node& n) const
|
||
{ return getPosition(n).xcoord(); }
|
||
|
||
class PredCompareX : public leda_cmp_base<XCoord> {
|
||
public:
|
||
int operator() (const XCoord& x1, const XCoord& x2) const
|
||
{ return compare(x1, x2); }
|
||
};
|
||
PredCompareX getCompareX() const { return PredCompareX(); }
|
||
|
||
@ Die Typen und Operationen, die mit Kurven zusammenh"angen, werden von der
|
||
Klasse |LEDA_CompareSegments| zur Verf"ugung gestellt und hier nur noch
|
||
aufgerufen.
|
||
<<Operationen und Typen von LEDA_PLocTraits>>=
|
||
typedef LEDA_CompareSegments< LEDA_PointTraits<Point> > PredCompareCurves;
|
||
typedef typename PredCompareCurves::Segment Curve;
|
||
|
||
Curve makeCurve(const Point& p) const
|
||
{ return PredCompareCurves::CreateTrivialSegment(p); }
|
||
Curve makeCurve(const Graph& G, const Node& n) const
|
||
{ return makeCurve(getPosition(n)); }
|
||
Curve makeCurve(const Graph& G, const Edge& e) const
|
||
{
|
||
return PredCompareCurves::CreateSegment(getPosition(source(e)),
|
||
getPosition(target(e)));
|
||
}
|
||
|
||
PredCompareCurves getCompareCurves() const
|
||
{ return PredCompareCurves(); }
|
||
|
||
@ Die Standardversion der Funktion |PostProcess| tut einfach gar nichts, d.h.
|
||
sie liefert die ihr "ubergebene |Location| unver"andert zur"uck.%"
|
||
<<Operationen und Typen von LEDA_PLocTraits>>=
|
||
typedef GenericLocation<node, edge> Location;
|
||
typedef Location QueryResult;
|
||
virtual Location PostProcess(const Location& L, const Location& L_plus,
|
||
const Point&) const { return L; }
|
||
|
||
@ Die Funktionen, die dazu dienen, da"s die Traits-Klasse "uber den
|
||
Fortschritt des Sweeps informiert wird, sowie die Funktion |clear| bleiben
|
||
leer:
|
||
<<Operationen und Typen von LEDA_PLocTraits>>=
|
||
virtual void sweep_begin(const Graph&) {}
|
||
virtual void sweep_moveto(const XCoord&) {}
|
||
virtual void sweep_end() {}
|
||
virtual void clear() {}
|
||
|
||
@ % === Traits f<>r Standardgraphen ============================================
|
||
\subsection{Traits f"ur Standardgraphen} %"
|
||
Bei Standardgraphen kann die Funktionalit"at der Basisklasse unver"andert
|
||
"ubernommen werden. Bei der Variante f"ur |node_array|s wird noch ein
|
||
Konstruktor zur Verf"ugung gestellt, dem man das |node_array| direkt
|
||
"ubergeben kann, sowie eine Funktion |init|, die es erlaubt, das |node_array|
|
||
erst sp"ater zu "ubergeben.
|
||
(Da der |PointLocator| die ihm "ubergebene Instanz der Traits-Klasse kopiert,
|
||
mu"s das |node_array| vor der "Ubergabe gesetzt werden.)%"
|
||
<<LEDA traits>>=
|
||
// Standard Graphs
|
||
|
||
template <class Pnt, class etype>
|
||
class PLocTraits< GRAPH<Pnt,etype> > :
|
||
public LEDA_PLocTraits<GRAPH<Pnt,etype>,node_attribute_da<Pnt>,AdjIt_e>
|
||
{};
|
||
|
||
template <class Pnt>
|
||
class PLocTraits_NodeArray<graph, Pnt> :
|
||
public LEDA_PLocTraits<graph,node_array_da<Pnt>,AdjIt_e>
|
||
{
|
||
public:
|
||
PLocTraits_NodeArray() {}
|
||
PLocTraits_NodeArray(node_array<Pnt>& na) { init(na); }
|
||
void init(node_array<Pnt>& na) { positions.init(na); }
|
||
};
|
||
|
||
@ % === Traits f<>r planare Karten =============================================
|
||
\subsection{Traits f"ur planare Karten} \label{PLocTraits_Map} %"
|
||
Als n"achstes wird die Klasse |LEDA_PLoc_Traits_Map| beschrieben, sie ist die
|
||
Basisklasse aller Traits-Klassen f"ur planare Karten.
|
||
In ihr wird die Funktion |PostProcess| f"ur planare Karten angepa"st.
|
||
Falls die Lokation, die der Funktion "ubergeben wird, eine Kante |e|
|
||
repr"asentiert, so mu"s die Funktion sicherstellen, da"s als Endergebnis
|
||
der Lokalisierungsanfrage diejenige der Kanten |e| und $e^R$ geliefert wird,
|
||
die das Gebiet begrenzt, in dem der Anfragepunkt |p| liegt.
|
||
Dazu fragt die Funktion die Orientierung des Punktes |p| bez"uglich der
|
||
orientierten Gerade durch |P(source(e))| und |P(target(e))| ab.
|
||
Liegt |p| nicht rechts von dieser Geraden, so ist das Ergebnis die Kante |e|;
|
||
ansonsten gibt die Funktion die Umkehrkante $e^R$ zur"uck.
|
||
<<LEDA traits>>=
|
||
// Planar Maps
|
||
|
||
template <class GraphType, class NodeDataAccessor>
|
||
class LEDA_PLocTraits_Map :
|
||
public LEDA_PLocTraits<GraphType,NodeDataAccessor,OutAdjIt_e> {
|
||
public:
|
||
virtual Location PostProcess(const Location& L,
|
||
const Location& L_plus, const Point& p) const
|
||
{
|
||
if (!L.is_edge()) return L;
|
||
edge e = L;
|
||
if (orientation(getPosition(source(e)), getPosition(target(e)), p) >= 0)
|
||
return L;
|
||
else
|
||
return Location(graph_of(e)->reversal(e));
|
||
}
|
||
};
|
||
|
||
@ Bei den Traits-Klassen f"ur die Graphentypen |PLANAR_MAP| und
|
||
|planar_map| (mit |node_array|) wird die Funktionalit"at der Basisklasse
|
||
unver"andert "ubernommen.%"
|
||
<<LEDA traits>>=
|
||
template <class Pnt, class etype, class ftype>
|
||
class PLocTraits< PLANAR_MAP<Pnt,etype,ftype> > :
|
||
public LEDA_PLocTraits_Map< PLANAR_MAP<Pnt,etype,ftype>,node_attribute_da<Pnt> >
|
||
{};
|
||
|
||
template <class Pnt>
|
||
class PLocTraits_NodeArray<planar_map, Pnt> :
|
||
public LEDA_PLocTraits_Map< planar_map,node_array_da<Pnt> >
|
||
{
|
||
public:
|
||
PLocTraits_NodeArray() {}
|
||
PLocTraits_NodeArray(node_array<Pnt>& na) { init(na); }
|
||
void init(node_array<Pnt>& na) { positions.init(na); }
|
||
};
|
||
|
||
@ Die Traits-Klassen mit dem Zusatz |Map| in ihrem Namen erlauben es dem
|
||
Anwender, Punkt-Lokalisierung in Karten vom Typ |GRAPH<Pnt,...>| bzw.
|
||
|graph| zu betreiben, ohne da"s er seine Objekte zuvor in den Typ
|
||
|PLANAR_MAP<Pnt,...,...>| bzw. |planar_map| konvertieren mu"s.
|
||
Vor dem Sweep "uberpr"ufen beide Traits-Klassen, da"s es sich bei dem Graphen
|
||
|G| tats"achlich um eine Karte handelt.%"
|
||
<<LEDA traits>>=
|
||
template <class Pnt, class etype>
|
||
class PLocTraits_Map< GRAPH<Pnt,etype> > :
|
||
public LEDA_PLocTraits_Map< GRAPH<Pnt,etype>, node_attribute_da<Pnt> >
|
||
{
|
||
public:
|
||
virtual void sweep_begin(const Graph& G)
|
||
{
|
||
#if !defined(LEDA_CHECKING_OFF)
|
||
if (! Is_Map(G))
|
||
error_handler(1, "PLocTraits_Map: graph is not a map");
|
||
#endif
|
||
}
|
||
};
|
||
|
||
template <class Pnt>
|
||
class PLocTraits_Map_NodeArray<graph, Pnt> :
|
||
public LEDA_PLocTraits_Map< graph, node_array_da<Pnt> >
|
||
{
|
||
public:
|
||
PLocTraits_Map_NodeArray() {}
|
||
PLocTraits_Map_NodeArray(node_array<Pnt>& na) { init(na); }
|
||
void init(node_array<Pnt>& na) { positions.init(na); }
|
||
|
||
virtual void sweep_begin(const Graph& G)
|
||
{
|
||
#if !defined(LEDA_CHECKING_OFF)
|
||
if (! Is_Map(G))
|
||
error_handler(1, "PLocTraits_Map_NodeArray: graph is not a map");
|
||
#endif
|
||
}
|
||
};
|
||
|
||
@ % === Traits f<>r Voronoi-Diagramme =========================================
|
||
\section{Traits f"ur LEDA Voronoi-Diagramme} \label{PLocTraits_LEDA_VD} %"
|
||
|
||
Im folgenden Abschnitt wird eine Traits-Klasse vorgestellt, die es erlaubt,
|
||
die Klasse |PointLocator| auch mit Voronoi-Diagrammen zu verwenden.
|
||
Doch zun"achst werden Voronoi-Diagramme definiert, einige Eigenschaften
|
||
vorgestellt und ihre Repr"asentation in LEDA beschrieben.
|
||
Diese Darstellung folgt Abschnitt 10.5 in \cite{LEDA_book}.
|
||
|
||
Gegeben ist eine Menge $S \subset \RR^2$ von Orten (Sites) in der Ebene.
|
||
F"ur jeden Punkt |p| sei |close(p)| die Menge aller Orte in |S|, die am
|
||
n"achsten bei |p| liegen, d.h.
|
||
$|close(p)| = \Set{s \in S}{\forall t \in S: |dist(s,p)| \leq |dist(t,p)|}$.
|
||
In der Regel enth"alt |close(p)| nur einen einzigen Ort, aber f"ur einige
|
||
Punkte |p| enth"alt |close(p)| zwei oder mehr Orte.
|
||
Diese Punkte bilden das Voronoi-Diagramm:
|
||
$|VD(S)| = \Set{p \in \RR^2}{\abs{|close(p)|} \geq 2}$.
|
||
Abbildung \figref{voro_example} zeigt ein Beispiel-Diagramm.
|
||
Voronoi-Diagramme "ahneln Graphen; die Knoten sind alle Punkte |p| mit
|
||
$|close(p)| \geq 3$, die Kanten sind maximale zusammenh"angende Mengen mit
|
||
$|close(p)| = 2$ und die Facetten sind maximale zusammenh"angende Mengen mit
|
||
$|close(p)| = 1$.\\
|
||
In \cite{LEDA_book} wird gezeigt, da"s jede Facette ein konvexes Gebiet ist,
|
||
das genau einen Ort enth"alt und dessen Rand ein Polygon ist. Die zu einem
|
||
Ort |s| geh"orende Facette wird als Voronoi-Region |VR(s)| bezeichnet.
|
||
Es gilt: $|VR(s)| = \Set{p \in \RR^2}{\forall t \in S \setminus \{s\}:
|
||
|dist(s,p)| < |dist(t,p)|}$.
|
||
Eine Kante, die zwei aneinandergrenzende Regionen |VR(s)| und |VR(t)| trennt,
|
||
liegt immer auf der Mittelsenkrechten von |s| und |t|.
|
||
|
||
\Figger{voro_example}{Voronoi-Diagramm mit 27 Sites}{voro_example}
|
||
|
||
Voronoi-Diagramme werden in LEDA als planare Karten vom Typ
|
||
|GRAPH<Circle,Point>| dargestellt, wobei |Point| einer der Punkttypen |point|
|
||
oder |rat_point| sein kann und |Circle| der zugeh"orige Kreisttyp ist.
|
||
Das Problem hierbei sind die unbeschr"ankten Kanten; hat eine Kante keinen
|
||
Start- bzw. Endknoten, so erh"alt sie einen "`Knoten im Unendlichen"', siehe
|
||
Abbildung \figref{VD_to_Graph}. Ein Knoten im Unendlichen hat einen
|
||
Ausgangsgrad von 1; die anderen Knoten haben einen Ausgangsgrad von mindestens
|
||
3, sie werden "`echte Knoten"' genannt.
|
||
|
||
Die Attribute der Knoten und der Kanten des Graphen geben Aufschlu"s "uber die
|
||
Positionen der Knoten und "uber die Voronoi-Regionen:
|
||
\begin{itemize}
|
||
\item Jede Kante ist mit dem Ort beschriftet, dessen Region links von ihr
|
||
liegt.
|
||
\item Jeder echte Knoten |v| ist mit einem Kreis |Circle(a,b,c)| beschriftet,
|
||
wobei |a|, |b|, |c| drei verschiedene Orte sind, deren Regionen an |v|
|
||
grenzen.
|
||
Der Mittelpunkt des Kreises ist die Position von |v| in der Ebene.
|
||
\item Jeder Knoten |v| im Unendlichen ist inzident zu einer unbeschr"ankten
|
||
Kante, die zwei Voronoi-Regionen |VR(a)| und |VR(b)| trennt. Man kann |v|
|
||
daher als einen "`Endpunkt"' der Mittelsenkrechten von |a| und |b| ansehen.
|
||
W"ahlt man |a| und |b| so, da"s |v| links von dem orientierten
|
||
Geradensegment \Seg{a}{b} liegt, so sind |a| und |b| eindeutig bestimmt.
|
||
Man beschriftet |v| mit dem Kreis |Circle(a,x,b)|, wobei man f"ur |x| einen
|
||
beliebigen Punkt auf der Geraden durch |a| und |b| w"ahlt, so da"s der Kreis
|
||
entartet.
|
||
\end{itemize}
|
||
|
||
\Figger{VD_to_Graph}
|
||
{Umwandlung eines Voronoi-Diagrammes in einen Graph}
|
||
{VD_to_Graph}
|
||
|
||
Die Einbettung des Graphen in die Ebene ist ordnungserhaltend, d.h. wenn man
|
||
die Liste der ausgehenden Kanten eines Knotens |v| als zyklische Liste
|
||
betrachtet, so gilt, da"s jede Kante links von ihrer Vorg"angerkante liegt.
|
||
Oder anders formuliert: die Reihenfolge der ausgehenden Kanten in der
|
||
Adjazenzliste von |v| ist dieselbe wie bei einem Umlauf um |v| im
|
||
Gegenuhrzeigersinn.
|
||
|
||
\subsection{Handhabung der Knoten im Unendlichen}
|
||
\label{Handling_nodes_at_infty}
|
||
Die Datenstrukturen und Algorithmen von |PointLocator| und den bisher
|
||
vorgestellten Traits-Klassen basieren wesentlich auf den Positionen der
|
||
Knoten in der Ebene; vor allem die X-Koordinaten waren als Haltestellen des
|
||
Sweeps wichtig.
|
||
Um die Positionen von Knoten im Unendlichen beschreiben zu k"onnen, erweitert
|
||
man den Wertebereich f"ur die Koordinaten um die Werte ${-\infty}$ und
|
||
${+\infty}$. Sei |v| ein Knoten im Unendlichen mit der Beschriftung
|
||
|Circle(a,?,b)|, dann besitzt |v| genau eine eingehende Kante |e=(u,v)|.
|
||
Dieser Kante ordnet man einen Richtungsvektor $\vec{r}$ zu, der von |u| nach
|
||
|v| zeigt. Da |e| die Voronoi-Regionen |VR(a)| und |VR(b)| trennt, liegt |e|
|
||
auf der Mittelsenkrechten von |a| und |b|. Daher steht $\vec{r}$ senkrecht auf
|
||
dem Vektor $b-a$. Weil |v| links von \Seg{a}{b} liegt, erh"alt man $\vec{r}$,
|
||
indem man den Vektor $b-a$ um $+90\degree$ dreht.
|
||
Also kann man $\vec{r}=(a_y-b_y, b_x-a_x)$ w"ahlen und $|P(v)|=(x(v),y(v))$
|
||
wie folgt definieren:
|
||
$x(v) = \left\{ \begin{array}{c@{\quad,\quad}l}
|
||
{-\infty} & a_y < b_y \\
|
||
\frac{1}{2} (a_x+b_x) & a_y = b_y \\
|
||
{+\infty} & a_y > b_y
|
||
\end{array}\right.$
|
||
und
|
||
$y(v) = \left\{ \begin{array}{c@{\quad,\quad}l}
|
||
{-\infty} & a_x > b_x \\
|
||
\frac{1}{2} (a_y+b_y) & a_x = b_x \\
|
||
{+\infty} & a_x < b_x
|
||
\end{array}\right.$
|
||
~\\
|
||
Wenn eine Komponente von $\vec{r}$ Null ist, so setzt man $x(v)$ bzw. $y(v)$
|
||
also auf die entsprechende Koordinate des Mittelpunktes von |a| und |b|.
|
||
|
||
Durch diese Definition kann es passieren, da"s man verschiedenen Knoten im
|
||
Unendlichen dieselbe Position zuordnet, und es stellt sich die Frage, wie man
|
||
die Objekte in der Sweepline an den Haltestellen ordnen sollte, bei denen
|
||
Knoten im Unendlichen vorkommen.
|
||
%(Unbeschr"ankte Kanten k"onnen genauso wie beschr"ankte Kanten geordnet
|
||
%werden.)
|
||
Betrachtet man die Sweeplines bei ${-\infty}$ und ${+\infty}$, so stellt
|
||
man fest, da"s sie nur Knoten im Unendlichen und keine anderen Objekte
|
||
enthalten und da"s sie nach dem Sweep nie mehr benutzt werden, da alle
|
||
Anfragepunkte endliche Koordinaten haben. Daher gen"ugt es, alle diese Knoten
|
||
als gleich zu betrachten, so da"s die Sweeplines an diesen Stellen nur ein
|
||
Element enthalten.
|
||
Nun kann es aber auch eine reelle Haltestelle |x| geben, bei der die Sweepline
|
||
einen oder zwei Knoten im Unendlichen enth"alt, n"amlich bei vertikalen
|
||
unbeschr"ankten Kanten. Auch diese Knoten k"onnen niemals das Ergebnis einer
|
||
Punkt-Lokalisierung sein, sondern h"ochstens die unbeschr"ankten Kanten, zu
|
||
denen sie inzident sind.
|
||
Somit kommt man mit folgender Ordnung aus: Die Knoten im Unendlichen werden
|
||
identifiziert, und ein Knoten im Unendlichen ist stets kleiner als jeder echte
|
||
Knoten und als jede Kante.%"
|
||
|
||
@ % === VoronoiCurve =========================================================
|
||
\subsection{Hilfsklassen}
|
||
Die Klasse |VoronoiCurve| dient zur Darstellung der Kurven, die bei der
|
||
Bearbeitung von Voronoi-Diagrammen auftreten.
|
||
Sie ist im wesentlichen eine Vereinigung der LEDA Typen |edge| und |Point|.
|
||
Die Kurve, die mit einer Kante assoziiert ist, wird nicht explizit
|
||
abgespeichert, sondern es wird die Kante selbst gespeichert.
|
||
Dar"uber hinaus werden in dem Bitvektor |properties| noch einige Eigenschaften
|
||
der Kante abgelegt, wie z.B. ob die Kante vertikal ist oder ob ihr Start- oder
|
||
ihr Zielknoten im Unendlichen liegt.
|
||
Die Kurve, die mit einem echten Knoten assoziiert ist, wird in Form eines
|
||
Objektes vom Typ |Point| gespeichert, sie hei"st \emph{echter Punkt}.
|
||
F"ur einen Knoten im Unendlichen wird keine Kurve abgespeichert, es wird nur
|
||
ein Flag in |properties| gesetzt, das das Objekt als
|
||
\emph{unbeschr"ankten Punkt} kennzeichnet.
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
class VoronoiCurve {
|
||
public:
|
||
enum { mask_edge = 1, mask_vertical = 2, mask_unbounded = 4,
|
||
mask_src_at_infty = 8, mask_tgt_at_infty = 16 };
|
||
|
||
VoronoiCurve() : properties(mask_unbounded) {}
|
||
VoronoiCurve(const Point& p) : properties(0) { val = leda_copy(p); }
|
||
VoronoiCurve(edge e) : properties(mask_edge) { val = leda_copy(e); }
|
||
|
||
~VoronoiCurve() { if (is_proper_pnt()) LEDA_CLEAR(Point, val); }
|
||
|
||
operator const Point&() const { return LEDA_CONST_ACCESS(Point, val); }
|
||
operator edge() const { return LEDA_CONST_ACCESS(edge, val); }
|
||
|
||
int is_unbounded_pnt() const { return properties == mask_unbounded; }
|
||
int is_proper_pnt() const { return !properties; }
|
||
int is_edge() const { return properties & mask_edge; }
|
||
|
||
<<Operationen zum Manipulieren und Abfragen von properties>>
|
||
|
||
private:
|
||
int properties;
|
||
GenPtr val;
|
||
};
|
||
|
||
@ Die Operationen zum Manipulieren und Abfragen von |properties| sind einfache
|
||
Bitoperationen und werden hier nicht aufgef"uhrt.%"
|
||
|
||
\rem{
|
||
<<Operationen zum Manipulieren und Abfragen von properties>>=
|
||
void mark_vertical() { properties |= mask_vertical; }
|
||
int is_vertical() const { return properties & mask_vertical; }
|
||
|
||
void mark_unbounded() { properties |= mask_unbounded; }
|
||
int is_unbounded() const { return properties & mask_unbounded;}
|
||
|
||
void mark_src_at_infty()
|
||
{ properties |= (mask_unbounded | mask_src_at_infty); }
|
||
int has_src_at_infty() const { return properties & mask_src_at_infty; }
|
||
|
||
void mark_tgt_at_infty()
|
||
{ properties |= (mask_unbounded | mask_tgt_at_infty); }
|
||
int has_tgt_at_infty() const { return properties & mask_tgt_at_infty; }
|
||
|
||
VoronoiCurve(const VoronoiCurve& VC) : properties(VC.properties), val(VC.val)
|
||
{ if (is_proper_pnt()) LEDA_COPY(Point, val); }
|
||
|
||
VoronoiCurve& operator=(const VoronoiCurve& VC)
|
||
{
|
||
properties = VC.properties; val = VC.val;
|
||
if (is_proper_pnt()) LEDA_COPY(Point, val);
|
||
return *this;
|
||
}
|
||
|
||
LEDA_MEMORY(VoronoiCurve)
|
||
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
inline
|
||
ostream& operator<<(ostream& o, const VoronoiCurve<Point>&) { return o; }
|
||
|
||
template <class Point>
|
||
inline
|
||
istream& operator>>(istream& i, const VoronoiCurve<Point>&) { return i; }
|
||
|
||
@ }% end of rem
|
||
|
||
@ % === Vergleichsobjekt f<>r VoronoiCurve ====================================
|
||
\medskip
|
||
|
||
Als n"achstes wird das Vergleichsobjekt f"ur |VoronoiCurve|s beschrieben.
|
||
Abgesehen vom Funktionsaufruf-Operator, der den Vergleich durchf"uhrt,
|
||
enth"alt die Klasse noch einige Hilfsfunktionen.
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
class PredCompareVoronoiCurves {
|
||
public:
|
||
typedef VoronoiCurve<Point> Curve;
|
||
typedef typename LEDA_PointTraits<Point>::Coord Coord;
|
||
typedef typename LEDA_PointTraits<Point>::Circle Circle;
|
||
|
||
int operator() (const Curve& C1, const Curve& C2) const;
|
||
|
||
inline static const Point& get_site(edge e);
|
||
inline static const Point get_position(node n);
|
||
inline static int orientation(edge e, const Point& p);
|
||
|
||
private:
|
||
int Cmp_Pnt_Curve(const Point& p, const Curve& c) const;
|
||
int Cmp_Two_Edges(const Curve& c1, const Curve& c2) const;
|
||
};
|
||
|
||
@ Die Funktion |get_site| liefert den Ort, dessen Voronoi-Region die Kante |e|
|
||
begrenzt.
|
||
Sie benutzt dazu die Klasse |GIT_dummygraph<>| der LEDA Grapheniteratoren
|
||
(s. |graph_iterator.h| in LEDA), um auf die Kanteninformation zuzugreifen.
|
||
Da |GRAPH<Circle, Point>| virtuell von |graph| abgeleitet ist, l"a"st sich der
|
||
Zeiger, den die Funktion |graph_of| liefert, nicht in einen Zeiger auf
|
||
|GRAPH<Circle, Point>| casten.
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
inline
|
||
const Point&
|
||
PredCompareVoronoiCurves<Point>::get_site(edge e)
|
||
{
|
||
return ((GIT_dummygraph<Point>*) graph_of(e))->get_entry(e);
|
||
}
|
||
|
||
@ Analog liefert |get_position| die Position des Knotens |n|.
|
||
Voraussetzung ist hier, da"s der Knoten nicht im Unendlichen liegt.%"
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
inline
|
||
const Point
|
||
PredCompareVoronoiCurves<Point>::get_position(node n)
|
||
{
|
||
#ifdef DEBUG
|
||
{
|
||
Circle C = ((GIT_dummygraph<Circle>*) graph_of(n))->get_entry(n);
|
||
assert(!C.is_degenerate);
|
||
}
|
||
#endif
|
||
return (((GIT_dummygraph<Circle>*) graph_of(n))->get_entry(n)).center();
|
||
}
|
||
|
||
@ Die Funktion |orientation| liefert die Orientierung des Punktes |p|
|
||
bez"uglich der Kante |e|.
|
||
Der Ort |s| sei das Attribut der Kante |e|, und der Ort |t| sei das Attribut
|
||
der Umkehrkante $e^R$, d.h. |e| trennt die Voronoi-Regionen |VR(s)| und
|
||
|VR(t)|. Die Unterst"utzungsgerade von |e| ist die Mittelsenkrechte von
|
||
|s| und |t|, also die Menge aller Punkte, die von |s| und |t| gleich weit
|
||
entfernt sind. Daher l"a"st sich die Orientierung von |p| aus den Abst"anden
|
||
von |p| zu den Orten |s| und |t| ermitteln.
|
||
Da |s| positiv bez"uglich |e| orientiert ist, ergibt sich f"ur die
|
||
Orientierung von |p|:
|
||
$\sign(|dist(t,p)|-|dist(s,p)|) = \sign(|dist(t,p)|^2-|dist(s,p)|^2)$.
|
||
Weil man nur am Vorzeichen des R"uckgabewertes der Funktion interessiert ist,
|
||
gen"ugt es, $|compare|(|dist(t,p)|^2,|dist(s,p)|^2)$ zu berechnen.
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
inline
|
||
int
|
||
PredCompareVoronoiCurves<Point>::orientation(edge e, const Point& p)
|
||
{
|
||
edge rev_e = graph_of(e)->reversal(e);
|
||
return compare( p.sqr_dist(get_site(rev_e)), p.sqr_dist(get_site(e)) );
|
||
}
|
||
|
||
@ Die Vergleichsfunktion f"ur zwei |VoronoiCurve|s |c1| und |c2| behandelt
|
||
zuerst den Fall, da"s |c1| oder |c2| einen echten Punkt repr"asentiert; dann
|
||
wird das Ergebnis des Vergleichs mit der Funktion |Cmp_Pnt_Curve| berechnet.
|
||
Ist keine der beiden Kurven ein echter Punkt, so wird "uberpr"uft, ob es sich
|
||
bei beiden Kurven um Kanten handelt und falls ja die Funktion |Cmp_Two_Edges|
|
||
aufgerufen.\\
|
||
Ansonsten bleiben 3 F"alle:
|
||
\begin{enumerate}
|
||
\item |c1| ist eine Kante, |c2| ist ein unbeschr"ankter Punkt.
|
||
$\Rightarrow$ Ergebnis: $+1$\\
|
||
denn: Kanten liegen per Definition "uber unbeschr"ankten Punkten.
|
||
\item |c1| ist ein unbeschr"ankter Punkt, |c2| ist eine Kante.
|
||
$\Rightarrow$ Ergebnis: $-1$
|
||
\item |c1| und |c2| sind unbeschr"ankte Punkte.
|
||
$\Rightarrow$ Ergebnis: $0$\\
|
||
denn: Unbeschr"ankte Punkte werden identifiziert.
|
||
\end{enumerate}
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
inline
|
||
int
|
||
PredCompareVoronoiCurves<Point>::operator()
|
||
(const Curve& c1, const Curve& c2) const
|
||
{
|
||
if (c1.is_proper_pnt())
|
||
return Cmp_Pnt_Curve(c1, c2);
|
||
if (c2.is_proper_pnt())
|
||
return -Cmp_Pnt_Curve(c2, c1);
|
||
|
||
if (c1.is_edge())
|
||
if (c2.is_edge())
|
||
return Cmp_Two_Edges(c1, c2);
|
||
else
|
||
return +1;
|
||
else
|
||
return c2.is_edge() ? -1 : 0;
|
||
}
|
||
|
||
@ Die folgende Funktion vergleicht einen Punkt |p| mit einer beliebigen
|
||
|VoronoiCurve| |c|, die von der Parallelen zur Y-Achse durch |p| geschnitten
|
||
wird.
|
||
Sie unterscheidet 3 F"alle:
|
||
\begin{enumerate}
|
||
\item |c| ist ein echter Punkt.
|
||
$\Rightarrow$ Vergleiche die Y-Koordinaten.
|
||
\item |c| ist eine Kante.
|
||
$\Rightarrow$ Der Vergleich l"auft wie der Punkt-Kante Vergleich auf
|
||
Seite \pageref{Cmp_Pnt_Edge}.
|
||
\item |c| ist ein unbeschr"ankter Punkt.
|
||
$\Rightarrow$ |p| liegt "uber |c| per Definition.
|
||
\end{enumerate}
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
inline
|
||
int
|
||
PredCompareVoronoiCurves<Point>::Cmp_Pnt_Curve
|
||
(const Point& p, const VoronoiCurve<Point>& c) const
|
||
{
|
||
if (c.is_proper_pnt())
|
||
return compare(p.ycoord(), ((const Point&)c).ycoord());
|
||
|
||
if (c.is_edge()) {
|
||
if (!c.is_vertical()) return orientation((edge)c, p);
|
||
|
||
// c is a vertical edge:
|
||
Coord Y = p.ycoord();
|
||
if ((!c.has_src_at_infty()) && (Y <= get_position(source(c)).ycoord()))
|
||
return -1;
|
||
if ((!c.has_tgt_at_infty()) && (Y >= get_position(target(c)).ycoord()))
|
||
return +1;
|
||
return 0;
|
||
}
|
||
|
||
return +1; // c is an unbounded point => p > c
|
||
}
|
||
|
||
@ Die Funktion |Cmp_Two_Edges| macht folgende Annahmen "uber die ihr
|
||
"ubergebenen Kurven |c1| und |c2|:
|
||
\begin{enumerate}
|
||
\item |c1| und |c2| stellen 2 Kanten |e1| und |e2| dar, die allerdings auch
|
||
unbeschr"ankt sein d"urfen.
|
||
\item |c1| und |c2| sind vergleichbar, d.h. es gibt eine Parallele zur
|
||
Y-Achse, die beide Kurven schneidet.
|
||
\item Es gilt:
|
||
$P(|source|(e_i)) \prec_{lex} P(|target|(e_i)),~ i \in \SET{1,2}$.
|
||
Informal bedeutet dies, da"s die Kurven von "`links nach rechts"' bzw.
|
||
von "`unten nach oben"' verlaufen.
|
||
Diese Annahme stellt unter anderem sicher, da"s $|e2| \neq |e1|^R$.
|
||
\end{enumerate}
|
||
Als erstes wird getestet, ob die beiden Kanten identisch sind.
|
||
Danach wird "uberpr"uft, ob die Kanten dieselbe Quelle oder dasselbe Ziel
|
||
haben; in diesem Fall kann der Lagevergleich sehr effizient ausgef"uhrt
|
||
werden.
|
||
Am Ende wird der Fall behandelt, da"s weder die Start- noch die Endknoten der
|
||
Kanten "ubereinstimmen.%"
|
||
<<LEDA traits>>=
|
||
template <class Point>
|
||
inline
|
||
int
|
||
PredCompareVoronoiCurves<Point>::Cmp_Two_Edges
|
||
(const Curve& c1, const Curve& c2) const
|
||
{
|
||
edge e1 = c1, e2 = c2;
|
||
|
||
if (e1 == e2) return 0;
|
||
|
||
if (source(e1) == source(e2)) {
|
||
<<Vergleiche Kanten mit gleicher Quelle>>
|
||
}
|
||
if (target(e1) == target(e2)) {
|
||
<<Vergleiche Kanten mit gleichem Ziel>>
|
||
}
|
||
|
||
<<Vergleiche Kanten mit unterschiedlichen Quellen und Zielen>>
|
||
}
|
||
|
||
@ Der Code f"ur den Vergleich zweier Kanten mit gleicher Quelle oder gleichem
|
||
Ziel beruht auf folgenden Lemmata:
|
||
|
||
\begin{lemma} \label{VD_1}
|
||
Sei |v| ein echter Knoten in einem Voronoi-Diagramm, und seien $e_1$ und
|
||
$e_2$ zwei ausgehende Kanten, so da"s $e_2$ der zyklische Nachfolger $e_1$ in
|
||
der Adjazenzliste von |v| ist.\\
|
||
Dann gilt: $\angle~{e_1,e_2} < 180 \degree$.\\
|
||
(Hierbei ist $\angle~{e_1,e_2}$ der Winkel, der "uberstrichen wird, wenn man
|
||
$e_1$ im Gegenuhrzeigersinn auf $e_2$ dreht.)
|
||
\end{lemma}
|
||
|
||
\begin{beweis}
|
||
Der Beweis des Lemmas ergibt sich im wesentlichen aus Abbildung
|
||
\figref{VD_Lemma1} (links).
|
||
Dreht man $e_1$ im Gegenuhrzeigersinn auf $e_2$, so "uberstreicht man keine
|
||
von |v| ausgehende Kante, denn die Adjazenzliste von |v| ist gerade so
|
||
geordnet.
|
||
Folglich "uberstreicht man nur einen Ort |s|, der auf dem Kreis liegt, mit dem
|
||
der Knoten |v| beschriftet ist.
|
||
Seien |r| und |t| die Orte, mit denen $e_1^R$ bzw. $e_2$ beschriftet sind.
|
||
Da $e_1$ auf der Mittelsenkrechten von |s| und |r| liegt, halbiert sie den
|
||
Winkel $\angle~{s,|P(v)|,r}$; analog halbiert $e_2$ den Winkel
|
||
$\angle~{t,|P(v)|,s}$.
|
||
Weil ein echter Knoten einen Ausgangsgrad von mindestens 3 hat, gilt $r \neq t$
|
||
und damit $\gamma > 0$.
|
||
Wegen $2\alpha + 2\beta + \gamma = 360 \degree$ folgt
|
||
$\alpha+\beta < 180 \degree$.
|
||
\end{beweis}
|
||
|
||
\Figger{VD_Lemma1}
|
||
{Skizzen zu Beweis und Anwendung von Lemma \ref{VD_1}}
|
||
{VD_Lemma1}
|
||
|
||
Wie wendet man Lemma \ref{VD_1} an?
|
||
Geht man einmal davon aus, da"s von |v| keine vertikalen Kanten ausgehen, so
|
||
kann man die zyklische Liste der ausgehenden Kanten -- wie in Abbildung
|
||
\figref{VD_Lemma1} (rechts) -- in zwei Listen $L_v^+ = (e_1^+, \dots, e_h^+)$
|
||
und $L_v^- = (e_1^-, \dots, e_k^-)$ partitionieren; $L_v^+$ enth"alt die
|
||
Kanten, deren Zielknoten "`rechts"' von |P(v)| liegen, und $L_v^-$ die Kanten
|
||
mit Zielknoten "`links"' von |P(v)|.
|
||
Man sieht, da"s die Kanten in $L_v^+$ und $L_v^-$ untereinander vergleichbar
|
||
sind, zwei Kanten aus verschiedenen Listen jedoch nicht.
|
||
Es gilt: $e_1^+ < \dots < e_h^+$ und $e_1^- > \dots > e_k^-$.
|
||
Aus Lemma \ref{VD_1} folgt, da"s in der Liste aller ausgehenden Kanten $e_1^+$
|
||
nicht der Nachfolger von $e_h^+$ und $e_1^-$ nicht der Nachfolger von $e_k^-$
|
||
sein kann.
|
||
Hat man also zwei benachbarte Kanten aus $L_v^+$ oder $L_v^-$ gegeben, so kann
|
||
man sie vergleichen, ohne arithmetische Operationen auszuf"uhren.
|
||
Wenn der Ausgangsgrad von |v| drei ist, dann enth"alt jede der Listen
|
||
h"ochstens 2 Elemente, so da"s man immer ohne arithmetische Operationen
|
||
auskommt.
|
||
Falls |v| viele ausgehende Kanten hat, so benutzt man folgendes Lemma:
|
||
|
||
\begin{lemma} \label{VD_2}
|
||
Sei |v| ein echter Knoten in einem Voronoi-Diagramm, und seien $e_1$ und $e_2$
|
||
zwei vergleichbare ausgehende Kanten, die mit den Orten |s| bzw. |t|
|
||
beschriftet sind.\\
|
||
Dann gilt f"ur $\theta \in \SET{<,=,>}: e_1 ~ \theta ~ e_2 \Leftrightarrow
|
||
s_y ~ \theta ~ t_y$.
|
||
\end{lemma}
|
||
|
||
\begin{beweis}
|
||
Wenn $e_1 = e_2$ ist, dann ist sicherlich $s = t$.
|
||
Betrachte nun den Fall $e_1 < e_2$, d.h. $e_1$ liegt unter $e_2$.
|
||
Da $e_1$ und $e_2$ vergleichbar sind, existiert eine Stelle $x \in \RR$, so
|
||
da"s die Parallele zur Y-Achse an dieser Stelle die beiden Kanten schneidet.\\
|
||
Unterscheide 3 F"alle (vgl. Abbildung \figref{VD_Lemma2}):
|
||
\begin{enumerate}
|
||
\item $x > P(v)_x$, d.h. $e_1$ und $e_2$ sind in $L_v^+$\\
|
||
Sei |r| der Ort, mit dem $e_2^R$ beschriftet ist.
|
||
Weil $e_1$ unter $e_2$ liegt, ist $s_y \leq r_y$. Da die Kante $e_2$
|
||
"`rechts"' von |v| liegt, gilt: $r_y < t_y$. $\Rightarrow s_y < t_y$
|
||
\item $x < P(v)_x$, d.h. $e_1$ und $e_2$ sind in $L_v^-$\\
|
||
Sei |r| der Ort, mit dem $e_1^R$ beschriftet ist.
|
||
Weil die Kante $e_1$ "`links"' von |v| liegt, gilt: $s_y < r_y$.
|
||
Da $e_1$ unter $e_2$ liegt, ist $r_y \leq t_y$. $\Rightarrow s_y < t_y$
|
||
\item $x = P(v)_x$, d.h. $e_1$ und $e_2$ sind vertikal\\
|
||
Sei |r| der Ort, mit dem $e_2^R$ beschriftet ist.
|
||
Aus Lemma \ref{VD_1} folgt, da"s eine Kante $e_3$ existieren mu"s, die
|
||
zwischen |s| und |r| verl"auft. $\Rightarrow s_y < r_y = t_y$
|
||
\end{enumerate}
|
||
In jedem Fall hat man: $s_y < t_y$.\\
|
||
Damit gilt f"ur $\theta \in \SET{<,=,>}: e_1 ~ \theta ~ e_2 \Rightarrow
|
||
s_y ~ \theta ~ t_y$. Die R"uckrichtung "`$\Leftarrow$"' folgt daraus.
|
||
\end{beweis}
|
||
|
||
\Figger{VD_Lemma2}
|
||
{Skizzen zum Beweis von Lemma \ref{VD_2}}
|
||
{VD_Lemma2}
|
||
|
||
Bei dem Vergleich zweier Kanten $e_1$ und $e_2$ mit gemeinsamer Quelle |v|
|
||
nutzt man aus, da"s $e_1$ und $e_2$ in $L_v^+$ liegen m"ussen, da
|
||
$P(v) \prec_{lex} P(|target|(e_i)), ~ i \in \SET{1,2}$.
|
||
%"
|
||
<<Vergleiche Kanten mit gleicher Quelle>>=
|
||
graph* Gp = graph_of(e1);
|
||
if (e2 == Gp->cyclic_adj_succ(e1)) return -1;
|
||
if (e2 == Gp->cyclic_adj_pred(e1)) return +1;
|
||
return compare(get_site(e1).ycoord(), get_site(e2).ycoord());
|
||
@ Bei dem Vergleich zweier Kanten mit demselben Ziel |v| geht man "ahnlich
|
||
vor. Man mu"s jedoch beachten, da"s nur die Liste der ausgehenden Kanten im
|
||
Gegenuhrzeigersinn geordnet ist, die Liste der eingehenden Kanten jedoch
|
||
nicht. Darum arbeitet man mit den Umkehrkanten der zu vergleichenden Kanten,
|
||
diese liegen in $L_v^-$.%"
|
||
<<Vergleiche Kanten mit gleichem Ziel>>=
|
||
graph* Gp = graph_of(e1);
|
||
edge rev_e1 = Gp->reversal(e1), rev_e2 = Gp->reversal(e2);
|
||
if (rev_e2 == Gp->cyclic_adj_succ(rev_e1)) return +1;
|
||
if (rev_e2 == Gp->cyclic_adj_pred(rev_e1)) return -1;
|
||
return compare(get_site(rev_e1).ycoord(), get_site(rev_e2).ycoord());
|
||
@ Als letztes wird der Fall behandelt, da"s zwei Kanten mit unterschiedlichen
|
||
Start- und Endknoten verglichen werden.
|
||
Sei $a=P(|source|(e_1)), b=P(|target|(e_1), c=P(|source|(e_2)),
|
||
d=P(|target|(e_2)$, wobei die Punkte $a,b,c,d$ auch nicht reelle Koordinaten
|
||
haben k"onnen. Es gilt: $a \prec_{lex} b$ und $c \prec_{lex} d$.\\
|
||
Betrachte zun"achst den Fall $a \in \RR^2$:
|
||
\begin{itemize}
|
||
\item Gilt auch $c \in \RR^2$, so kann man den Vergleich ausf"uhren wie den
|
||
Vergleich zweier nichttrivialer Segmente in Abschnitt \ref{Cmp_Segments}.
|
||
\item Ansonsten gibt es 2 M"oglichkeiten:
|
||
\begin{itemize}
|
||
\item Ist $e_2$ vertikal, so ist $c_y={-\infty}$. Da $e_1$ und $e_2$ sich
|
||
nicht schneiden, mu"s $e_1$ "uber $e_2$ liegen.
|
||
\item Falls $e_2$ nicht vertikal ist, gilt $c_x={-\infty}$. Aus der
|
||
Vergleichbarkeit der Kanten folgt $d_x > a_x$ und daher ist das Ergebnis
|
||
des Vergleichs die Orientierung von |a| bzgl. $e_2$.
|
||
\end{itemize}
|
||
\end{itemize}%"
|
||
<<Vergleiche Kanten mit unterschiedlichen Quellen und Zielen>>=
|
||
if (! c1.has_src_at_infty()) {
|
||
Point a = get_position(source(e1));
|
||
|
||
if (! c2.has_src_at_infty()) {
|
||
Point c = get_position(source(e2));
|
||
// Note: a != c since sources differ
|
||
|
||
int cmpX = compare(a.xcoord(), c.xcoord());
|
||
if (cmpX < 0) return - orientation(e1, c);
|
||
if (cmpX > 0) return orientation(e2, a);
|
||
return compare(a.ycoord(), c.ycoord()); // cmpX == 0 !
|
||
}
|
||
else {
|
||
if (c2.is_vertical()) return +1;
|
||
else return orientation(e2, a);
|
||
}
|
||
}
|
||
|
||
@ Nun wird der Fall $a \notin \RR^2$ behandelt.
|
||
\begin{itemize}
|
||
\item Ist $e_1$ vertikal, so ist $a_y={-\infty}$ und damit liegt $e_1$ unter
|
||
$e_2$.
|
||
\item Gilt $c \in \RR^2$ und ist $e_1$ nicht vertikal, so hat man
|
||
${-\infty}=a_x < c_x < b_x$, und man kann das Ergebnis dadurch bestimmen,
|
||
da"s man die Orientierung von |c| bzgl. $e_1$ berechnet.
|
||
\end{itemize}
|
||
<<Vergleiche Kanten mit unterschiedlichen Quellen und Zielen>>=
|
||
if (c1.is_vertical()) return -1;
|
||
|
||
if (! c2.has_src_at_infty())
|
||
return -orientation(e1, get_position(source(e2)));
|
||
|
||
@ Wenn bis hierhin kein Ergebnis berechnet werden konnte, so gilt:
|
||
$a, c \notin \RR^2$. Es gibt dann noch 2 M"oglichkeiten:
|
||
\begin{itemize}
|
||
\item $b \notin \RR^2$:\\
|
||
Es gibt also eine Kante, deren inzidente Knoten beide im Unendlichen liegen.
|
||
Dann sind die Orte des Voronoi-Diagrammes kolinear:
|
||
\begin{quote}
|
||
Annahme: Dies ist nicht der Fall.\\
|
||
Betrachte die Orte |s| und |t|, mit denen $e_1$ bzw. $e_1^R$ beschriftet
|
||
sind. Dann existiert ein Ort |r|, der nicht auf der Geraden durch |s| und
|
||
|t| liegt. Der Mittelpunkt des Kreises durch |r|, |s| und |t| ist ein
|
||
Knoten des Voronoi-Diagrammes und liegt auf der Mittelsenkrechten von |s|
|
||
und |t|, also auf $e_1$. Dies ist ein Widerspruch!
|
||
\end{quote}
|
||
In diesem Fall sind alle Kanten parallel, und es gibt keine echten Knoten.
|
||
Da $e_1$ nicht vertikal ist, l"a"st sich das Ergebnis des Vergleiches der
|
||
Kanten ermitteln, indem man die Y-Koordinaten der Orte vergleicht, mit denen
|
||
die Kanten beschriftet sind.
|
||
\item $b \in \RR^2$:\\
|
||
Es ergibt sich, da"s dann auch $d \in \RR^2$ gelten mu"s, denn sonst w"aren
|
||
die Orte des Diagrammes kolinear, was $b \notin \RR^2$ bedeuten w"urde.
|
||
Daher kann man den Vergleich analog wie in Abschnitt \ref{Cmp_Segments}
|
||
durchf"uhren.
|
||
\end{itemize}
|
||
<<Vergleiche Kanten mit unterschiedlichen Quellen und Zielen>>=
|
||
if (c1.has_tgt_at_infty())
|
||
return compare(get_site(e1).ycoord(), get_site(e2).ycoord());
|
||
else {
|
||
assert(!c2.has_tgt_at_infty());
|
||
Point b = get_position(target(e1)); Point d = get_position(target(e2));
|
||
|
||
int cmpX = compare(b.xcoord(), d.xcoord());
|
||
if (cmpX > 0) return - orientation(e1, d);
|
||
if (cmpX < 0) return orientation(e2, b);
|
||
return compare(b.ycoord(), d.ycoord());
|
||
}
|
||
|
||
@ % === LEDA VD Traits-Klassen ===============================================
|
||
\subsection{Die Traits-Klassen}
|
||
Von der Klasse |LEDA_PLocTraits_VD| werden die Traits-Klassen f"ur LEDA
|
||
Voronoi-Diagramme abgeleitet.
|
||
Gro"se Teile des Codes stimmen mit der Traits-Klasse f"ur planare Karten
|
||
"uberein. In diesem Abschnitt werden nur die Aspekte besprochen, in denen sich
|
||
|LEDA_PLocTraits_VD| von dieser Klasse unterscheidet.
|
||
In Abschnitt \ref{Handling_nodes_at_infty} wurde der Wertebereich f"ur die
|
||
X-Koordinate um die Werte ${-\infty}$ und ${+\infty}$ erweitert. Jetzt stellt
|
||
sich die Frage, wie diese Werte konkret repr"asentiert werden. Aus
|
||
Effizienzgr"unden wird der Wertebereich nicht tats"achlich erweitert, sondern
|
||
der Koordinatentyp bleibt weiterhin |double| bzw. |rational|; stattdessen
|
||
werden zwei Werte $x_{-\infty}$ und $x_{+\infty}$ aus dem Wertebereich dieser
|
||
Typen als ${-\infty}$ und ${+\infty}$ festgelegt und in den Attributen
|
||
|neg_infty_x| und |pos_infty_x| gespeichert.
|
||
X-Koordinaten treten an zwei Stellen in |PointLocator| auf:
|
||
\begin{itemize}
|
||
\item Sie sind die Haltestellen des Sweeps, und daher sollte man
|
||
$x_{\pm\infty}$ so w"ahlen, da"s $x_{-\infty}<x_0<x_1<\dots<x_n<x_{+\infty}$
|
||
gilt, wobei $\SET{x_0, \dots, x_n}$ die Menge der reellen Haltestellen des
|
||
Sweeps ist.
|
||
\item Wenn der |PointLocator| einen Punkt |p| lokalisieren soll, dann fragt er
|
||
sein Traits-Objekt nach der X-Koordinate diese Punktes, damit seine
|
||
X-Struktur die relevante Sweepline rekonstruieren kann. Daher sollte sich
|
||
die Traits-Klasse folgenderma"sen verhalten:
|
||
\begin{itemize}
|
||
\item $p_x \in [x_0;x_n]$ $\Rightarrow$ Liefere $p_x$ als X-Koordinate.%[
|
||
\item $p_x < x_0$ $\Rightarrow$ Liefere einen Wert aus $]x_{-\infty};x_0[$.
|
||
\item $p_x > x_n$ $\Rightarrow$ Liefere einen Wert aus $]x_n;x_{+\infty}[$.
|
||
\end{itemize}
|
||
\end{itemize}
|
||
%]"
|
||
<<LEDA traits>>=
|
||
template <class POINT>
|
||
class LEDA_PLocTraits_VD {
|
||
public:
|
||
virtual ~LEDA_PLocTraits_VD() {}
|
||
|
||
<<Operationen und Typen von LEDA_PLocTraits_VD>>
|
||
|
||
protected:
|
||
bool at_infty(node n) const
|
||
{ return outdeg(n) == 1; }
|
||
|
||
protected:
|
||
Coord neg_infty_x, pos_infty_x;
|
||
};
|
||
|
||
@ \rem{
|
||
<<Operationen und Typen von LEDA_PLocTraits_VD>>=
|
||
// import types from PointTraits
|
||
typedef typename LEDA_PointTraits<POINT>::Coord Coord;
|
||
typedef typename LEDA_PointTraits<POINT>::Point Point;
|
||
typedef typename LEDA_PointTraits<POINT>::Segment Segment;
|
||
typedef typename LEDA_PointTraits<POINT>::Vector Vector;
|
||
typedef typename LEDA_PointTraits<POINT>::Circle Circle;
|
||
|
||
typedef GRAPH<Circle, Point> Graph;
|
||
typedef node Node;
|
||
typedef edge Edge;
|
||
|
||
typedef NodeIt_n NodeIterator;
|
||
NodeIterator Nodes_begin(const Graph& G) { return NodeIterator(G); }
|
||
NodeIterator Nodes_end(const Graph& G) { return NodeIterator(G).end(); }
|
||
|
||
typedef OutAdjIt_e IncEdgeIterator;
|
||
IncEdgeIterator IncEdges_begin(const Graph& G, const Node& n)
|
||
{ return IncEdgeIterator(G, n); }
|
||
IncEdgeIterator IncEdges_end(const Graph& G, const Node& n)
|
||
{ return IncEdgeIterator(G, n).end(); }
|
||
|
||
typedef Coord XCoord;
|
||
class PredCompareX : public leda_cmp_base<XCoord> {
|
||
public:
|
||
int operator() (const XCoord& x1, const XCoord& x2) const
|
||
{ return compare(x1, x2); }
|
||
};
|
||
PredCompareX getCompareX() const { return PredCompareX(); }
|
||
@ }% end of rem
|
||
@ Die Wahl der Werte $x_{\pm\infty}$ und die Berechnung der X-Koordinate
|
||
eines Anfragepunktes |p| wird f"ur die beiden Punkttypen unterschiedlich
|
||
realisiert; daher wird diese Funktionalit"at nicht in der Basisklasse
|
||
implementiert.
|
||
Die Berechnung der X-Koordinate eines Knotens |v| ist dagegen in beiden
|
||
F"allen gleich. Als erstes wird "uberpr"uft, ob |v| im Unendlichen liegt.
|
||
Ist dies der Fall, so wird die Definition f"ur |x(v)| aus Abschnitt
|
||
\ref{Handling_nodes_at_infty} angewendet, um das Ergebnis zu berechnen.
|
||
Bei einem echten Knoten |v| wird seine tats"achliche X-Koordinate geliefert,
|
||
also die X-Koordinate des Mittelpunktes des Kreises, mit dem er beschriftet
|
||
ist.
|
||
<<Operationen und Typen von LEDA_PLocTraits_VD>>=
|
||
XCoord getXCoord(const Graph& G, const node& v) const
|
||
{
|
||
if (at_infty(v)) {
|
||
edge e = G.first_adj_edge(v); edge rev_e = G.reversal(e);
|
||
const Point& site_e = G[e]; const Point& site_rev_e = G[rev_e];
|
||
int cmpY = compare(site_e.ycoord(), site_rev_e.ycoord());
|
||
if (cmpY > 0) return neg_infty_x;
|
||
if (cmpY < 0) return pos_infty_x;
|
||
return (site_e.xcoord() + site_rev_e.xcoord()) / 2;
|
||
}
|
||
else return G[v].center().xcoord();
|
||
}
|
||
|
||
@ Die Klassifizierung einer Kante |e| bzgl. des Knotens |v| erfolgt in zwei
|
||
Schritten. Da $|ClassifyEdge|(G,e,v)=|ClassifyEdge|(G,e^R,v)$ gilt, kann man
|
||
$E \in \SET{e, e^R}$ so w"ahlen, da"s |v| die Quelle von |E| ist.
|
||
Die eigentliche Klassifizierung erfolgt nun "uber den Richtungsvektor
|
||
$\vec{r}$ der Kante |E| (vgl. Abschnitt \ref{Handling_nodes_at_infty}); dies
|
||
funktioniert sowohl f"ur unbeschr"ankte als auch f"ur beschr"ankte Kanten.
|
||
%"
|
||
<<Operationen und Typen von LEDA_PLocTraits_VD>>=
|
||
enum EdgeCategory
|
||
{ StartingNonVertical, StartingVertical, EndingNonVertical, EndingVertical };
|
||
|
||
EdgeCategory ClassifyEdge(const Graph& G, const Edge& e, const Node& v) const
|
||
{
|
||
edge E, rev_E;
|
||
if (v == source(e)) { E = e; rev_E = G.reversal(E); }
|
||
else { rev_E = e; E = G.reversal(rev_E); }
|
||
const Point& site_E = G[E]; const Point& site_rev_E = G[rev_E];
|
||
int cmpY = compare(site_E.ycoord(), site_rev_E.ycoord());
|
||
if (cmpY > 0) return StartingNonVertical;
|
||
if (cmpY < 0) return EndingNonVertical;
|
||
return (site_E.xcoord() < site_rev_E.xcoord()) ?
|
||
StartingVertical : EndingVertical;
|
||
}
|
||
|
||
@ \rem{
|
||
<<Operationen und Typen von LEDA_PLocTraits_VD>>=
|
||
typedef VoronoiCurve<Point> Curve;
|
||
typedef PredCompareVoronoiCurves<Point> PredCompareCurves;
|
||
PredCompareCurves getCompareCurves() const
|
||
{ return PredCompareCurves(); }
|
||
Curve makeCurve(const Point& p) const
|
||
{ return Curve(p); }
|
||
Curve makeCurve(const Graph& G, const Node& n) const
|
||
{ return at_infty(n) ? Curve() : Curve(G[n].center()); }
|
||
@ }% end of rem
|
||
@ Die Funktion |makeCurve| erzeugt aus einer Kante |e| eine |VoronoiCurve|
|
||
|c|, welche die Kante repr"asentiert.
|
||
Dieses Objekt speichert eine Kante $E \in \SET{e, e^R}$. Sie wird so gew"ahlt,
|
||
da"s $P(|source|(E)) \prec_{lex} P(|target|(E))$ gilt. Dies ist n"otig, damit
|
||
die Vergleichsfunktion f"ur |VoronoiCurve|s korrekt arbeitet. Dar"uber hinaus
|
||
wird in |c| gespeichert, ob die Kante vertikal ist und welche inzidenten
|
||
Knoten im Unendlichen liegen.
|
||
<<Operationen und Typen von LEDA_PLocTraits_VD>>=
|
||
Curve makeCurve(const Graph& G, const Edge& e) const
|
||
{
|
||
edge E = e;
|
||
Curve c;
|
||
switch (ClassifyEdge(G, e, source(e))) {
|
||
case EndingNonVertical: E = G.reversal(E); // fall-through
|
||
case StartingNonVertical: c = Curve(E); break;
|
||
case EndingVertical: E = G.reversal(E); // fall-through
|
||
case StartingVertical: c = Curve(E); c.mark_vertical(); break;
|
||
}
|
||
if (at_infty(source(E))) c.mark_src_at_infty();
|
||
if (at_infty(target(E))) c.mark_tgt_at_infty();
|
||
return c;
|
||
}
|
||
|
||
@ \rem{
|
||
<<Operationen und Typen von LEDA_PLocTraits_VD>>=
|
||
typedef GenericLocation<node, edge> Location;
|
||
typedef Location QueryResult;
|
||
Location PostProcess(const Location& L,
|
||
const Location& L, const Point& P) const
|
||
{
|
||
if (!L.is_edge()) return L;
|
||
edge e = L;
|
||
if (PredCompareCurves::orientation(e, P) >= 0)
|
||
return L;
|
||
else
|
||
return Location(graph_of(e)->reversal(e));
|
||
}
|
||
|
||
virtual void sweep_begin(const Graph&) {}
|
||
virtual void sweep_moveto(const XCoord&) {}
|
||
virtual void sweep_end() {}
|
||
virtual void clear() {}
|
||
@ }% end of rem
|
||
|
||
@ Die Traits-Klasse f"ur Voronoi-Diagramme, deren Orte durch den Typ |point|
|
||
dargestellt werden, ist einfach zu implementieren.
|
||
Weil die X-Koordinaten der Punkte vom Typ |double| sind, liegen die Werte f"ur
|
||
$x_{\pm\infty}$ auf der Hand: $\pm|MAXDOUBLE|$.
|
||
Die Funkion |getXCoord| liefert bei einem Punkt |p| dessen tats"achliche
|
||
X-Koordinate; der Fall $p_x = x_{\pm\infty}$ wird aus Effizienzgr"unden nicht
|
||
abgefangen.
|
||
<<LEDA traits>>=
|
||
class PLocTraits< GRAPH<circle, point> > :
|
||
public LEDA_PLocTraits_VD<point>
|
||
{
|
||
public:
|
||
typedef LEDA_PLocTraits_VD<point> BASE;
|
||
public:
|
||
PLocTraits() { neg_infty_x = -MAXDOUBLE; pos_infty_x = +MAXDOUBLE; }
|
||
virtual ~PLocTraits() {}
|
||
|
||
XCoord getXCoord(const Point& p) const { return p.xcoord(); }
|
||
|
||
XCoord getXCoord(const Graph& G, const node& n) const
|
||
{ return BASE::getXCoord(G, n); }
|
||
};
|
||
|
||
@ Werden die Orte im Voronoi-Diagramm durch den Typ |rat_point| dargestellt,
|
||
so ist die Implementierung der Traits-Klasse etwas aufwendiger.
|
||
Die Koordinaten der Punkte sind hier vom Typ |rational|, so da"s leider keine
|
||
kanonischen Werte f"ur $x_{-\infty}$ und $x_{+\infty}$ existieren. Vielmehr
|
||
m"ussen diese Werte dynamisch gew"ahlt werden, d.h. in Abh"angigkeit von dem
|
||
Graphen $G=(V,E)$ und dessen Einbettung.
|
||
Sei $S_r = \Set{P(v)_x}{v \in V} \cap \RR$ die Menge aller reellen
|
||
Haltestellen des sp"ateren Sweeps. Die Funktion |sweep_begin| setzt
|
||
$x_{-\infty} = \lfloor \min(S_r \cup \SET{0}) \rfloor-2$ und
|
||
$x_{+\infty} = \lceil \max(S_r \cup \SET{0}) \rceil +2$.
|
||
Das Runden sorgt daf"ur, da"s der Platz f"ur die Darstellung von
|
||
$x_{\pm\infty}$ minimiert wird, da der Nenner auf 1 gesetzt wird.
|
||
Da auch Knoten im Unendlichen zu $S_r$ beitragen beitragen k"onnen, n"amlich
|
||
dann, wenn sie zu einer vertikalen Kante inzident sind, kann sich die Funktion
|
||
|sweep_begin| nicht nur auf die echten Knoten beschr"anken.
|
||
|
||
Nach dem Sweep setzt die Funktion |sweep_end| die Attribute |neg_infty_x| und
|
||
|pos_infty_x| auf die Werte $x_{-\infty}+1$ bzw. $x_{+\infty}-1$. Dadurch wird
|
||
gew"ahrleistet, da"s die Funktion |getXCoord| bei einem Anfragepunkt |p| eine
|
||
X-Koordinate im Intervall $]x_{-\infty};x_{+\infty}[$ berechnet. %]
|
||
<<LEDA traits>>=
|
||
class PLocTraits< GRAPH<rat_circle, rat_point> > :
|
||
public LEDA_PLocTraits_VD<rat_point>
|
||
{
|
||
public:
|
||
typedef LEDA_PLocTraits_VD<rat_point> BASE;
|
||
public:
|
||
virtual ~PLocTraits() {}
|
||
|
||
virtual void sweep_begin(const Graph& G)
|
||
{
|
||
if (G.number_of_nodes() > 0) {
|
||
neg_infty_x = pos_infty_x = 0;
|
||
|
||
node n;
|
||
forall_nodes(n, G) {
|
||
Coord X = BASE::getXCoord(G, n); // Note: Nodes at infty_y
|
||
if (X < neg_infty_x) neg_infty_x = X;
|
||
else if (X > pos_infty_x) pos_infty_x = X;
|
||
}
|
||
|
||
neg_infty_x = floor(neg_infty_x)-2; pos_infty_x = ceil(pos_infty_x)+2;
|
||
}
|
||
}
|
||
|
||
virtual void sweep_end() { ++neg_infty_x; --pos_infty_x; }
|
||
|
||
XCoord getXCoord(const Point& p) const
|
||
{
|
||
XCoord X = p.xcoord();
|
||
if (X < neg_infty_x) return neg_infty_x;
|
||
if (X > pos_infty_x) return pos_infty_x;
|
||
return X;
|
||
}
|
||
|
||
XCoord getXCoord(const Graph& G, const node& n) const
|
||
{ return BASE::getXCoord(G, n); }
|
||
};
|
||
|
||
@ \rem{
|
||
@ % === Header Files =========================================================
|
||
\section{Die Header-Dateien}
|
||
Der oben vorgestellte Code ist auf zwei Header-Dateien verteilt.
|
||
Die Datei |gen_point_location.h| enth"alt alle generischen, oder genauer nicht
|
||
LEDA-spezifischen Klassen:%"
|
||
<<gen_point_location.h>>=
|
||
<<CGAL Header>>
|
||
#ifndef GEN_POINT_LOCATION_H
|
||
#define GEN_POINT_LOCATION_H
|
||
|
||
#if CGAL_LEDA_VERSION < 500
|
||
#include <LEDA/pp_dictionary.h>
|
||
#else
|
||
#include <LEDA/core/pp_dictionary.h>
|
||
#endif
|
||
#include <strstream>
|
||
#include <string>
|
||
#include <list>
|
||
#include <vector>
|
||
#include <map>
|
||
#include <CGAL/Nef_2/geninfo.h>
|
||
|
||
#undef _DEBUG
|
||
#define _DEBUG 17
|
||
#include <CGAL/Nef_2/debug.h>
|
||
|
||
// #define CHECKING_OFF
|
||
|
||
// for dictionary
|
||
template <class Node>
|
||
inline std::ostream& operator<<(std::ostream& o, const std::list<Node>& n)
|
||
{ return o; }
|
||
|
||
<<Location>>
|
||
<<Generische X-Struktur>>
|
||
<<PointLocator>>
|
||
|
||
#endif // GEN_POINT_LOCATION_H
|
||
|
||
<<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/gen_point_location.h
|
||
// package : Nef_2
|
||
// chapter : Nef Polyhedra
|
||
//
|
||
// source : nef_2d/Svens_point_location.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: Polynomials in one variable
|
||
// ============================================================================
|
||
|
||
|
||
@
|
||
\end{document}
|