From c3822abe3061e19e6d85641085f73021f20e1146 Mon Sep 17 00:00:00 2001 From: Michael Seel Date: Mon, 16 Jul 2001 12:47:27 +0000 Subject: [PATCH] simple fixes --- Packages/Nef_2/dont_submit | 1 + .../CGAL/Nef_2/Constrained_triang_traits.h | 4 +- Packages/Nef_2/include/CGAL/Nef_2/HDS_items.h | 4 +- .../CGAL/Nef_2/HalfedgeDS_default_MSC.h | 17 +- .../Nef_2/HalfedgeDS_using_in_place_list.h | 34 +- .../Nef_2/include/CGAL/Nef_2/Object_index.h | 4 +- .../Nef_2/include/CGAL/Nef_2/PM_checker.h | 3 +- .../include/CGAL/Nef_2/PM_const_decorator.h | 15 +- .../Nef_2/include/CGAL/Nef_2/PM_decorator.h | 18 +- .../Nef_2/include/CGAL/Nef_2/PM_overlayer.h | 16 +- .../include/CGAL/Nef_2/PM_point_locator.h | 4 +- .../Nef_2/include/CGAL/Nef_polyhedron_2.h | 21 +- Packages/Nef_2/noweb/Svens_point_location.lw | 2528 +++++++++++++++++ .../Nef_2/test/Nef_2/Nef_polyhedron_2-test.C | 2 - 14 files changed, 2601 insertions(+), 70 deletions(-) create mode 100644 Packages/Nef_2/noweb/Svens_point_location.lw diff --git a/Packages/Nef_2/dont_submit b/Packages/Nef_2/dont_submit index 1333ed77b7e..743eb210b5d 100644 --- a/Packages/Nef_2/dont_submit +++ b/Packages/Nef_2/dont_submit @@ -1 +1,2 @@ TODO +noweb diff --git a/Packages/Nef_2/include/CGAL/Nef_2/Constrained_triang_traits.h b/Packages/Nef_2/include/CGAL/Nef_2/Constrained_triang_traits.h index cc41ca45a1b..f8cb7727d12 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/Constrained_triang_traits.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/Constrained_triang_traits.h @@ -29,7 +29,7 @@ #define CGAL_PM_CONSTR_TRIANG_TRAITS_H #include -#include +#include #include #include #include @@ -143,7 +143,7 @@ public: Vertex_handle event; Point p_sweep; Sweep_status_structure SL; - CGAL::Hash_map SLItem; + CGAL::Unique_hash_map SLItem; const NEWEDGE& Treat_new_edge; Halfedge_handle e_low,e_high; // framing edges ! Halfedge_handle e_search; diff --git a/Packages/Nef_2/include/CGAL/Nef_2/HDS_items.h b/Packages/Nef_2/include/CGAL/Nef_2/HDS_items.h index 6222b14b9bd..abe77c22d4b 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/HDS_items.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/HDS_items.h @@ -125,7 +125,7 @@ struct HDS_items { void set_face(Face_handle f) { _f=f; } /*{\Mop makes |f| the incident face of |\Mvar|.}*/ - Point& point() { return _p; } + Point& point() { return _p; } /*{\Mop returns the embedding point of |\Mvar|.}*/ const Point& point() const { return _p; } @@ -133,7 +133,7 @@ struct HDS_items { /*{\Mop returns the mark of |\Mvar|.}*/ const Mark& mark() const { return _m; } - GenPtr& info() { return _i; } + GenPtr& info() { return _i; } /*{\Mop returns a generic information slot of |\Mvar|.}*/ const GenPtr& info() const { return _i; } diff --git a/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_default_MSC.h b/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_default_MSC.h index 805184658a4..561264acf53 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_default_MSC.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_default_MSC.h @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include CGAL_BEGIN_NAMESPACE @@ -248,7 +248,7 @@ public: : Base(), nb_border_halfedges(0), nb_border_edges(0) {} // not used here. - ~HalfedgeDS_ipl_MSC() { erase_all(); } + ~HalfedgeDS_ipl_MSC() { clear(); } HalfedgeDS_ipl_MSC(const Self& hds) : vertices( hds.vertices), @@ -261,7 +261,7 @@ public: Self& operator=( const Self& hds) { if ( this != &hds) { - erase_all(); + clear(); vertices = hds.vertices; halfedges = hds.halfedges; faces = hds.faces; @@ -421,7 +421,7 @@ public: faces_erase(first++); } - void erase_all() { + void clear() { vertices.destroy(); edges_erase( halfedges.begin(), halfedges.end()); faces.destroy(); @@ -483,12 +483,9 @@ HalfedgeDS_ipl_MSC:: pointer_update( const HalfedgeDS_ipl_MSC& hds) { // Update own pointers assuming that they lived previously // in a halfedge data structure `hds' with lists. - typedef CGAL::Hash_map V_map; - typedef CGAL::Hash_map H_map; - typedef CGAL::Hash_map F_map; - V_map v_map; - H_map h_map; - F_map f_map; + CGAL::Unique_hash_map v_map; + CGAL::Unique_hash_map h_map; + CGAL::Unique_hash_map f_map; h_map[Halfedge_const_iterator()] = Halfedge_iterator(); v_map[Vertex_const_iterator()] = Vertex_iterator(); f_map[Face_const_iterator()] = Face_iterator(); diff --git a/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_using_in_place_list.h b/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_using_in_place_list.h index 7cc43919ecf..024d950a363 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_using_in_place_list.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/HalfedgeDS_using_in_place_list.h @@ -38,14 +38,16 @@ CGAL_BEGIN_NAMESPACE template < class Vertex_base> class HalfedgeDS_in_place_list_vertex : public Vertex_base, public CGAL::In_place_list_base< - HalfedgeDS_in_place_list_vertex< Vertex_base> > { + HalfedgeDS_in_place_list_vertex > { public: - typedef HalfedgeDS_in_place_list_vertex< Vertex_base> Self; + typedef HalfedgeDS_in_place_list_vertex Self; + typedef CGAL::In_place_list_base Base2; + typedef typename Vertex_base::Vertex_handle Vertex_handle; typedef typename Vertex_base::Vertex_const_handle Vertex_const_handle; - HalfedgeDS_in_place_list_vertex() {} // down cast - HalfedgeDS_in_place_list_vertex( const Vertex_base& v) - : Vertex_base(v) {} + HalfedgeDS_in_place_list_vertex() : Vertex_base(), Base2() {} + HalfedgeDS_in_place_list_vertex( const Vertex_base& v) : + Vertex_base(v) {} Self& operator=( const Self& v) { // This self written assignment avoids that assigning vertices will // overwrite the list linking of the target vertex. @@ -57,15 +59,16 @@ public: template < class Halfedge_base> class HalfedgeDS_in_place_list_halfedge : public Halfedge_base, public CGAL::In_place_list_base< - HalfedgeDS_in_place_list_halfedge< Halfedge_base> > { + HalfedgeDS_in_place_list_halfedge > { public: typedef HalfedgeDS_in_place_list_halfedge< Halfedge_base> Self; - typedef typename Halfedge_base::Halfedge_handle Halfedge_handle; + typedef CGAL::In_place_list_base Base2; + typedef typename Halfedge_base::Halfedge_handle Halfedge_handle; typedef typename Halfedge_base::Halfedge_const_handle Halfedge_const_handle; - HalfedgeDS_in_place_list_halfedge() {} // down cast - HalfedgeDS_in_place_list_halfedge( const Halfedge_base& h) - : Halfedge_base(h) {} + HalfedgeDS_in_place_list_halfedge() : Halfedge_base(), Base2() {} + HalfedgeDS_in_place_list_halfedge( const Halfedge_base& h) : + Halfedge_base(h) {} Self& operator=( const Self& h) { // This self written assignment avoids that assigning halfedges will // overwrite the list linking of the target halfedge. @@ -77,12 +80,13 @@ public: template < class Face_base> class HalfedgeDS_in_place_list_face : public Face_base, public CGAL::In_place_list_base< - HalfedgeDS_in_place_list_face< Face_base> > { + HalfedgeDS_in_place_list_face > { public: - typedef HalfedgeDS_in_place_list_face< Face_base> Self; + typedef HalfedgeDS_in_place_list_face Self; + typedef CGAL::In_place_list_base Base2; typedef typename Face_base::Face_handle Face_handle; typedef typename Face_base::Face_const_handle Face_const_handle; - HalfedgeDS_in_place_list_face() {} // down cast + HalfedgeDS_in_place_list_face() : Face_base(), Base2() {} HalfedgeDS_in_place_list_face( const Face_base& f) : Face_base(f) {} Self& operator=( const Self& f) { // This self written assignment avoids that assigning faces will @@ -125,8 +129,8 @@ public: typedef typename Halfedge_list::const_iterator Halfedge_const_iterator; typedef typename Face_wrapper::Face Face_base; - typedef HalfedgeDS_in_place_list_face< Face_base> Face; - typedef CGAL::In_place_list Face_list; + typedef HalfedgeDS_in_place_list_face< Face_base> Face; + typedef CGAL::In_place_list Face_list; typedef typename Face_list::iterator Face_handle; typedef typename Face_list::const_iterator Face_const_handle; typedef typename Face_list::iterator Face_iterator; diff --git a/Packages/Nef_2/include/CGAL/Nef_2/Object_index.h b/Packages/Nef_2/include/CGAL/Nef_2/Object_index.h index f7046aed08a..d6b3ad02d32 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/Object_index.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/Object_index.h @@ -30,7 +30,7 @@ #define OBJECT_INDEX_H #include -#include +#include #include #include @@ -39,7 +39,7 @@ CGAL_BEGIN_NAMESPACE template class Object_index { char _prefix; - CGAL::Hash_map _index; + CGAL::Unique_hash_map _index; public: Object_index() : _prefix('\0'), _index(-1) {} Object_index(I first, I beyond, char c=' ') : _prefix(c), _index(-1) diff --git a/Packages/Nef_2/include/CGAL/Nef_2/PM_checker.h b/Packages/Nef_2/include/CGAL/Nef_2/PM_checker.h index 2dec078f6db..0852718d5b8 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/PM_checker.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/PM_checker.h @@ -29,6 +29,7 @@ #define CGAL_PM_CHECKER_H #include +#include #include #define USING(t) typedef typename Base::t t @@ -246,7 +247,7 @@ check_is_triangulation() const check_order_preserving_embedding(); eb = check_boundary_is_clockwise_weakly_polygon(); - CGAL::Hash_map< Halfedge_const_iterator, bool> on_boundary(false); + CGAL::Unique_hash_map< Halfedge_const_iterator, bool> on_boundary(false); Halfedge_around_face_const_circulator hit(eb), hend(hit); std::ostrstream error_status; CGAL::set_pretty_mode ( error_status ); diff --git a/Packages/Nef_2/include/CGAL/Nef_2/PM_const_decorator.h b/Packages/Nef_2/include/CGAL/Nef_2/PM_const_decorator.h index 67673e8592a..c867f3b7173 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/PM_const_decorator.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/PM_const_decorator.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include @@ -580,7 +580,7 @@ PM_const_decorator:: number_of_face_cycles() const { unsigned int fc_num=0; - CGAL::Hash_map visited; + CGAL::Unique_hash_map visited; // init with bool() == false Halfedge_const_iterator eit = phds->halfedges_begin(); Halfedge_const_iterator eend = phds->halfedges_end(); @@ -600,7 +600,7 @@ number_of_connected_components() const typedef Vertex_const_iterator vc_handle; typedef Halfedge_around_vertex_const_circulator hvc_circulator; int comp_num=0; - CGAL::Hash_map< vc_handle, bool> handled(false); + CGAL::Unique_hash_map< vc_handle, bool> handled(false); vc_handle vit = vertices_begin(), vend = vertices_end(); for ( ; vit != vend; ++vit) { if (handled[vit]) continue; @@ -643,9 +643,8 @@ void print_as_leda_graph(std::ostream& os, const PMCDEC& D, typedef typename PMCDEC::Halfedge_const_iterator Halfedge_const_iterator; int vn(1), en(1); - CGAL::Hash_map v_num; - CGAL::Hash_map e_num; - CGAL::Hash_map ec_num; + CGAL::Unique_hash_map v_num; + CGAL::Unique_hash_map e_num; os << "LEDA.GRAPH\n" << "point\n" << "int\n"; os << D.number_of_vertices() << std::endl; Vertex_const_iterator vit; @@ -655,7 +654,7 @@ void print_as_leda_graph(std::ostream& os, const PMCDEC& D, typename PMCDEC::Halfedge_around_vertex_const_circulator ecirc(D.first_out_edge(vit)),ecend(ecirc); int l=0; - CGAL_For_all(ecirc,ecend) ec_num[ecirc]=l++; + CGAL_For_all(ecirc,ecend) e_num[ecirc]=l++; } os << 2* D.number_of_edges() << std::endl; Halfedge_const_iterator eit; @@ -666,7 +665,7 @@ void print_as_leda_graph(std::ostream& os, const PMCDEC& D, os << v_num[D.source(eit)] << " " << v_num[D.target(eit)] << " " << e_num[D.twin(eit)] << " "; - os << "|{" << ec_num[eit] << "}|\n"; + os << "|{" << e_num[eit] << "}|\n"; } os << std::flush; } diff --git a/Packages/Nef_2/include/CGAL/Nef_2/PM_decorator.h b/Packages/Nef_2/include/CGAL/Nef_2/PM_decorator.h index 6ed561f9d54..402f3cd608b 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/PM_decorator.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/PM_decorator.h @@ -29,6 +29,8 @@ #define CGAL_PM_DECORATOR_H #include #include +#include +#include #define USING(t) typedef typename Base::t t #define HDSUSING(t) typedef typename HDS::t t @@ -179,7 +181,7 @@ Plane_map& plane_map() const void clear() const /*{\Mop reinitializes |P| to the empty map.}*/ -{ phds->erase_all(); } +{ phds->clear(); } Vertex_handle source(Halfedge_handle e) const /*{\Mop returns the source of |e|.}*/ @@ -715,8 +717,8 @@ void clone_skeleton(const HDS& H, const LINKDA& L) const PM_const_decorator DC(H); CGAL_assertion((DC.check_integrity_and_topological_planarity(),1)); - CGAL::Hash_map Vnew; - CGAL::Hash_map Hnew; + CGAL::Unique_hash_map Vnew; + CGAL::Unique_hash_map Hnew; /* First clone all objects and store correspondance in the two maps.*/ Vertex_const_iterator vit, vend = H.vertices_end(); @@ -818,9 +820,9 @@ void PM_decorator::clone(const HDS& H) const PM_const_decorator DC(H); CGAL_assertion((DC.check_integrity_and_topological_planarity(),1)); - CGAL::Hash_map Vnew; - CGAL::Hash_map Hnew; - CGAL::Hash_map Fnew; + CGAL::Unique_hash_map Vnew; + CGAL::Unique_hash_map Hnew; + CGAL::Unique_hash_map Fnew; /* First clone all objects and store correspondance in three maps.*/ Vertex_const_iterator vit, vend = H.vertices_end(); @@ -887,8 +889,8 @@ clone_skeleton(const HDS& H, const LINKDA& L) const PM_const_decorator DC(H); CGAL_assertion((DC.check_integrity_and_topological_planarity(),1)); - CGAL::Hash_map Vnew; - CGAL::Hash_map Hnew; + CGAL::Unique_hash_map Vnew; + CGAL::Unique_hash_map Hnew; /* First clone all objects and store correspondance in the two maps.*/ Vertex_const_iterator vit, vend = H.vertices_end(); diff --git a/Packages/Nef_2/include/CGAL/Nef_2/PM_overlayer.h b/Packages/Nef_2/include/CGAL/Nef_2/PM_overlayer.h index 4dc66b1d54b..84d078cc588 100644 --- a/Packages/Nef_2/include/CGAL/Nef_2/PM_overlayer.h +++ b/Packages/Nef_2/include/CGAL/Nef_2/PM_overlayer.h @@ -30,7 +30,7 @@ #define CGAL_PM_OVERLAYER_H #include -#include +#include #include #include #include @@ -113,11 +113,11 @@ struct PMO_from_pm { const Decorator& G; const Const_decorator* pGI[2]; - CGAL::Hash_map& M; + CGAL::Unique_hash_map& M; PMO_from_pm(const Decorator& Gi, const Const_decorator* pG0, const Const_decorator* pG1, - CGAL::Hash_map& Mi) : G(Gi),M(Mi) + CGAL::Unique_hash_map& Mi) : G(Gi),M(Mi) { pGI[0]=pG0; pGI[1]=pG1; } Vertex_handle new_vertex(const Point& p) const @@ -371,7 +371,7 @@ and |\Mvar.mark(v,1) = D1.mark(f1)|.}*/ Const_decorator PI[2]; PI[0] = Const_decorator(P0); PI[1] = Const_decorator(P1); Seg_list Segments; int i; - CGAL::Hash_map From; + CGAL::Unique_hash_map From; for (i=0; i<2; ++i) { Vertex_const_iterator v; for(v = PI[i].vertices_begin(); v != PI[i].vertices_end(); ++v) @@ -503,7 +503,7 @@ and the edges are unified.}*/ { TRACEN("simplifying"); typedef typename CGAL::Partition::item partition_item; - CGAL::Hash_map Pitem; + CGAL::Unique_hash_map Pitem; CGAL::Partition FP; Face_iterator f, fend = faces_end(); @@ -532,7 +532,7 @@ and the edges are unified.}*/ } } - CGAL::Hash_map linked(false); + CGAL::Unique_hash_map linked(false); for (e = halfedges_begin(); e != eend; ++e) { if ( linked[e] ) continue; Halfedge_around_face_circulator hfc(e),hend(hfc); @@ -693,7 +693,7 @@ template void create_face_objects(const Below_info& D) const { TRACEN("create_face_objects()"); - CGAL::Hash_map FaceCycle(-1); + CGAL::Unique_hash_map FaceCycle(-1); std::vector MinimalHalfedge; int i=0; Halfedge_iterator e, eend = halfedges_end(); @@ -747,7 +747,7 @@ void create_face_objects(const Below_info& D) const template Face_handle determine_face(Halfedge_handle e, const std::vector& MinimalHalfedge, - const CGAL::Hash_map& FaceCycle, + const CGAL::Unique_hash_map& FaceCycle, const Below_info& D) const { TRACEN("determine_face "< -#include +#include #include #include #undef _DEBUG @@ -221,7 +221,7 @@ public: Halfedge_const_handle e_res; Segment ss = s; // we shorten the segment iteratively Direction dso = K.construct_direction(K.target(s),p), d_res; - CGAL::Hash_map visited(false); + CGAL::Unique_hash_map visited(false); for(vit = vertices_begin(); vit != vertices_end(); ++vit) { Point p_res, vp = point(vit); if ( K.contains(ss,vp) ) { diff --git a/Packages/Nef_2/include/CGAL/Nef_polyhedron_2.h b/Packages/Nef_2/include/CGAL/Nef_polyhedron_2.h index 71539d896aa..16d2773be33 100644 --- a/Packages/Nef_2/include/CGAL/Nef_polyhedron_2.h +++ b/Packages/Nef_2/include/CGAL/Nef_polyhedron_2.h @@ -37,7 +37,7 @@ #include #ifndef CGAL_SIMPLE_HDS #include -#include +#include #else #include #endif @@ -65,19 +65,19 @@ template std::istream& operator>>(std::istream&, Nef_polyhedron_2&); template class Nef_polyhedron_2_rep : public Ref_counted -{ +{ typedef Nef_polyhedron_2_rep Self; friend class Nef_polyhedron_2; #ifndef CGAL_SIMPLE_HDS struct HDS_traits { typedef typename T::Point_2 Point; typedef bool Mark; }; - typedef CGAL::HalfedgeDS_default Plane_map; - typedef CGAL::PM_const_decorator Const_decorator; - typedef CGAL::PM_decorator Decorator; - typedef CGAL::PM_naive_point_locator Slocator; - typedef CGAL::PM_point_locator Locator; - typedef CGAL::PM_overlayer Overlayer; + typedef CGAL_HALFEDGEDS_DEFAULT Plane_map; + typedef CGAL::PM_const_decorator Const_decorator; + typedef CGAL::PM_decorator Decorator; + typedef CGAL::PM_naive_point_locator Slocator; + typedef CGAL::PM_point_locator Locator; + typedef CGAL::PM_overlayer Overlayer; #else struct HDS_traits { @@ -100,8 +100,9 @@ class Nef_polyhedron_2_rep : public Ref_counted void clear_locator() { if ( pl_ ) delete pl_; pl_=0; } public: - Nef_polyhedron_2_rep() : pm_(), pl_(0) {} - ~Nef_polyhedron_2_rep() { pm_.erase_all(); clear_locator(); } + Nef_polyhedron_2_rep() : Ref_counted(), pm_(), pl_(0) {} + Nef_polyhedron_2_rep(const Self& R) : pm_(), pl_(0) {} + ~Nef_polyhedron_2_rep() { pm_.clear(); clear_locator(); } }; /*{\Moptions print_title=yes }*/ diff --git a/Packages/Nef_2/noweb/Svens_point_location.lw b/Packages/Nef_2/noweb/Svens_point_location.lw new file mode 100644 index 00000000000..d5bb623fa8b --- /dev/null +++ b/Packages/Nef_2/noweb/Svens_point_location.lw @@ -0,0 +1,2528 @@ +% ---------------------------------------------------------------------------- +% sweep.lw: Punkt-Lokalisierung in der Ebene +% $Revision$ +% ---------------------------------------------------------------------------- + +% 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. + +@ % === Überblick ü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| verarbeiten, wobei +|Point| einer der LEDA Punkttypen |point| oder |rat_point| sein darf. +Aber auch Voronoi-Diagramme, die durch Objekte vom Typ |GRAPH| +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. +<>= + 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} +<>= + 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. +<>= + 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. +<>= + 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 ================================================== +<>= +/*{\Manpage {GenericLocation}{Node, Edge} +{Return Type for Planar Point Location}{L}}*/ + +template +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.}*/ + + <> + +private: + Type type; + GenPtr value; +}; + +<> +@ \rem{ +<>= +/*{\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& L) { assign(L); } + GenericLocation& operator=( + const GenericLocation& 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::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::const_access(value); + } + /*{\Mconversion converts |\Mvar| into an edge.\\ + \precond |\Mvar| represents an edge.}*/ + + GenericLocation& operator=(Node n) + { clear(); init(n); return *this; } + /*{\Mbinop makes |\Mvar| represent the node |n|.}*/ + + GenericLocation& 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::create(value); + geninfo::access(value) = n; + } + void init(Edge e) + { type = EDGE; + geninfo::create(value); + geninfo::access(value) = e; + } + + void clear() + { + switch(type) { + case NODE: geninfo::clear(value); break; + case EDGE: geninfo::clear(value); break; + case NIL: break; + } + } + + void assign(const GenericLocation& L) + { + type = L.type; + switch(type) { + case NODE: + geninfo::access(value) = geninfo::const_access(L.value); + break; + case EDGE: + geninfo::access(value) = geninfo::const_access(L.value); + break; + case NIL: break; + } + } + +public: + typedef GenericLocation self; + +<>= +/*{\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 +inline +bool +operator==(const GenericLocation& L1, + const GenericLocation& L2) +{ + if (L1.get_type() != L2.get_type()) return false; + switch (L1.get_type()) { + case GenericLocation::NIL: return true; + case GenericLocation::NODE: return Node(L1) == Node(L2); + case GenericLocation::EDGE: return Edge(L1) == Edge(L2); + } +} + +template +inline +bool +operator!=(const GenericLocation& L1, + const GenericLocation& L2) +{ return ! (L1==L2); } + +template +std::ostream& operator<<(std::ostream& o, + const GenericLocation& L) +{ + switch (L.get_type()) { + case GenericLocation::NIL: return o<<"nil"; + case GenericLocation::NODE: return o<<"node("<<&*Node(L)<<')'; + case GenericLocation::EDGE: return o<<"edge("<<&*Edge(L)<<')'; + } + return o; +} + +template +std::istream& operator>>(std::istream& i, GenericLocation&) +{ 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| 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}). +<>= + class QueryResult; + QueryResult PostProcess(const GenericLocation& Result, + const GenericLocation& 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.%" +<>= + 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{ +% === Übersicht über das PLocTraits Interface ================================ +<>= +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& Result, + const GenericLocation& 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. +%" +<>= +template +class GenericXStructure { +public: + typedef std::vector Array_Coordinates; + typedef std::vector 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 "<= 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. +<>= +/*{\Manpage {PointLocator} {PLocTraits} {Planar Point Location} {PL}}*/ + +template +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: + <> + +/*{\Mtypes}*/ + // define additional types + typedef GenericLocation 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 Sweepline; + typedef GenericXStructure XStructure; + typedef typename Sweepline::item SL_item; + typedef typename XStructure::Sweepline_iterator Sweepline_iterator; + <> + +private: + PLocTraits traits; + XStructure X_Structure; + +}; + +@ \rem{ +<>= +// 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: +<>= +/*{\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. %" +<>= + +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|.}*/ + +<>= +template +void +PointLocator::init(const Graph& G) +{ + traits.sweep_begin(G); + <> + <> + 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. % +<>= +PredLessThanX LtX = traits.getLessThanX(); +typedef std::map, 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: %" +<>= +dic_iterator stop; +for(stop = stops.begin(); stop != stops.end(); ++stop) { + std::list& NodesOnSL = stop->second; + traits.sweep_moveto(traits.getXCoord(G, *NodesOnSL.begin())); + <> + 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. +<>= + std::list EmergingEdges, VerticalEdges; + + // explore the nodes on SL + typename std::list::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$. +<>= + // compute SL_at_X + + typename std::list::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. +<>= + // 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. %" +<>= + +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. +<>= +template +PointLocator::QueryResult +PointLocator:: +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 "<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 +PointLocator::QueryResult +PointLocator::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{ +<>= +/*{\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|) or + explicitly by a |node_array|. 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|: standard traits for implicitly specified node + positions\\ + |Graph| can be |GRAPH| (standard graph) or + |PLANAR_MAP| (map). + \item |PLocTraits_NodeArray|: std. traits for explicitly + specified node positions\\ + |Graph| can be |graph| (standard graph) or |planar_map| (map). + \item |PLocTraits_Map| and |PLocTraits_Map_NodeArray|:\\ + The parameter |Graph| can be |GRAPH| and |graph| respectively. + These traits classes assume that the given graphs are maps. + \item |PLocTraits< GRAPH >|: 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 >| + can be used for graphs of type |ugraph| for example. +}*/ +/*{\Mexample +First we show an example where the node positions are given implicitly +as node attributes: +\begin{verbatim} + typedef PointLocator< PLocTraits< GRAPH > > PLocator1; + typedef PLocator1::Location Location; + + UGRAPH 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 PLocTraits2; + typedef PointLocator PLocator2; + + planar_map pm; + node_array 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|.\\ +Hier ist eine "Ubersicht "uber die Traits-Klassen, die dem Anwender zur +Verf"ugung gestellt werden: +\begin{enumerate} +\item |PLocTraits< GRAPH >| und + |PLocTraits_NodeArray| +\item |PLocTraits< PLANAR_MAP >| und + |PLocTraits_NodeArray| +\item |PLocTraits_Map< GRAPH >| und + |PLocTraits_Map_NodeArray| +\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| z.B. nicht nur mit dem Typ |graph|, +sondern auch mit dem Typ |ugraph| verwendet werden. + +@ \rem{ +<>= +// exported traits classes +template class PLocTraits; +template class PLocTraits_NodeArray; +template class PLocTraits_Map; +template 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. +<>= +template +class LEDA_PointTraits; + +template<> +class LEDA_PointTraits { +public: + typedef double Coord; + typedef point Point; + typedef segment Segment; + typedef vector Vector; + typedef circle Circle; +}; + +template<> +class LEDA_PointTraits { +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{ +<>= +typedef typename PointTraits::Coord Coord; +typedef typename PointTraits::Point Point; +typedef typename PointTraits::Segment Segment; +@ }% of rem +<>= +template +class LEDA_CompareSegments { +public: + <> + + 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.%" +<>= +template +int +LEDA_CompareSegments::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); + + <> +} + +@ 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} +<>= +template +inline +int +LEDA_CompareSegments::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}%" +<>= +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} +<>= +int cmpY = compare(a.ycoord(), c.ycoord()); +if (cmpY != 0) return cmpY; + +// cmpX == cmpY == 0 => a == c +return orientation(s2, b); + +@ \rem{ +<>= +/* + 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 +inline +SN& get_object(const LedaNodeMemberAccessor&,const SN&, const node& it) { +return ((GIT_dummygraph*)(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| 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.%" +<>= +template +class LEDA_PLocTraits { +public: + typedef GraphType Graph; + typedef typename NodeDataAccessor::value_type Point; + + <> + + virtual ~LEDA_PLocTraits() {} + +protected: + NodeDataAccessor positions; + const Point& getPosition(node n) const + { return access(const_cast(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. %" +<>= +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: +<>= +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| 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. +<>= +typedef typename LEDA_PointTraits::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 { +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. +<>= +typedef LEDA_CompareSegments< LEDA_PointTraits > 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.%" +<>= +typedef GenericLocation 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: +<>= +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.)%" +<>= +// Standard Graphs + +template +class PLocTraits< GRAPH > : + public LEDA_PLocTraits,node_attribute_da,AdjIt_e> +{}; + +template +class PLocTraits_NodeArray : + public LEDA_PLocTraits,AdjIt_e> +{ +public: + PLocTraits_NodeArray() {} + PLocTraits_NodeArray(node_array& na) { init(na); } + void init(node_array& 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. +<>= +// Planar Maps + +template +class LEDA_PLocTraits_Map : + public LEDA_PLocTraits { +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.%" +<>= +template +class PLocTraits< PLANAR_MAP > : + public LEDA_PLocTraits_Map< PLANAR_MAP,node_attribute_da > +{}; + +template +class PLocTraits_NodeArray : + public LEDA_PLocTraits_Map< planar_map,node_array_da > +{ +public: + PLocTraits_NodeArray() {} + PLocTraits_NodeArray(node_array& na) { init(na); } + void init(node_array& 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| bzw. +|graph| zu betreiben, ohne da"s er seine Objekte zuvor in den Typ +|PLANAR_MAP| 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.%" +<>= +template +class PLocTraits_Map< GRAPH > : + public LEDA_PLocTraits_Map< GRAPH, node_attribute_da > +{ +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 PLocTraits_Map_NodeArray : + public LEDA_PLocTraits_Map< graph, node_array_da > +{ +public: + PLocTraits_Map_NodeArray() {} + PLocTraits_Map_NodeArray(node_array& na) { init(na); } + void init(node_array& 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| 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. +<>= +template +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; } + + <> + +private: + int properties; + GenPtr val; +}; + +@ Die Operationen zum Manipulieren und Abfragen von |properties| sind einfache +Bitoperationen und werden hier nicht aufgef"uhrt.%" + +\rem{ +<>= +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) + +<>= +template +inline +ostream& operator<<(ostream& o, const VoronoiCurve&) { return o; } + +template +inline +istream& operator>>(istream& i, const VoronoiCurve&) { 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. +<>= +template +class PredCompareVoronoiCurves { +public: + typedef VoronoiCurve Curve; + typedef typename LEDA_PointTraits::Coord Coord; + typedef typename LEDA_PointTraits::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| virtuell von |graph| abgeleitet ist, l"a"st sich der +Zeiger, den die Funktion |graph_of| liefert, nicht in einen Zeiger auf +|GRAPH| casten. +<>= +template +inline +const Point& +PredCompareVoronoiCurves::get_site(edge e) +{ + return ((GIT_dummygraph*) 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.%" +<>= +template +inline +const Point +PredCompareVoronoiCurves::get_position(node n) +{ +#ifdef DEBUG + { + Circle C = ((GIT_dummygraph*) graph_of(n))->get_entry(n); + assert(!C.is_degenerate); + } +#endif + return (((GIT_dummygraph*) 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. +<>= +template +inline +int +PredCompareVoronoiCurves::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} +<>= +template +inline +int +PredCompareVoronoiCurves::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} +<>= +template +inline +int +PredCompareVoronoiCurves::Cmp_Pnt_Curve + (const Point& p, const VoronoiCurve& 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.%" +<>= +template +inline +int +PredCompareVoronoiCurves::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)) { + <> + } + if (target(e1) == target(e2)) { + <> + } + + <> +} + +@ 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}$. +%" +<>= +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^-$.%" +<>= +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}%" +<>= +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} +<>= +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} +<>= +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_n$ $\Rightarrow$ Liefere einen Wert aus $]x_n;x_{+\infty}[$. + \end{itemize} +\end{itemize} +%]" +<>= +template +class LEDA_PLocTraits_VD { +public: + virtual ~LEDA_PLocTraits_VD() {} + + <> + +protected: + bool at_infty(node n) const + { return outdeg(n) == 1; } + +protected: + Coord neg_infty_x, pos_infty_x; +}; + +@ \rem{ +<>= +// import types from PointTraits +typedef typename LEDA_PointTraits::Coord Coord; +typedef typename LEDA_PointTraits::Point Point; +typedef typename LEDA_PointTraits::Segment Segment; +typedef typename LEDA_PointTraits::Vector Vector; +typedef typename LEDA_PointTraits::Circle Circle; + +typedef GRAPH 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 { +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. +<>= +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. +%" +<>= +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{ +<>= +typedef VoronoiCurve Curve; +typedef PredCompareVoronoiCurves 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. +<>= +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{ +<>= + typedef GenericLocation 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. +<>= +class PLocTraits< GRAPH > : + public LEDA_PLocTraits_VD +{ +public: + typedef LEDA_PLocTraits_VD 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. %] +<>= +class PLocTraits< GRAPH > : + public LEDA_PLocTraits_VD +{ +public: + typedef LEDA_PLocTraits_VD 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:%" +<>= +<> +#ifndef GEN_POINT_LOCATION_H +#define GEN_POINT_LOCATION_H + +#include +#include +#include +#include +#include +#include +#include + +#undef _DEBUG +#define _DEBUG 17 +#include + +// #define CHECKING_OFF + +// for dictionary +template +inline std::ostream& operator<<(std::ostream& o, const std::list& n) +{ return o; } + +<> +<> +<> + +#endif // GEN_POINT_LOCATION_H + +<>= +// ============================================================================ +// +// 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 : $Revision$ +// revision_date : $Date$ +// +// author(s) : Michael Seel +// maintainer : Michael Seel +// coordinator : Michael Seel +// +// implementation: Polynomials in one variable +// ============================================================================ + + +@ +\end{document} diff --git a/Packages/Nef_2/test/Nef_2/Nef_polyhedron_2-test.C b/Packages/Nef_2/test/Nef_2/Nef_polyhedron_2-test.C index 2b36e8e2062..889034fd382 100644 --- a/Packages/Nef_2/test/Nef_2/Nef_polyhedron_2-test.C +++ b/Packages/Nef_2/test/Nef_2/Nef_polyhedron_2-test.C @@ -285,8 +285,6 @@ int main() CGAL_IO_TEST(N1,N2); - - Nef_polyhedron::EK.print_statistics(); } CGAL_TEST_END;