%------------------------------------------------------------------------------ %KILLSTART REP %LDEL TRACE.*?\)\; \documentclass[a4paper]{article} \usepackage{MyLweb,version} \excludeversion{ignoreindiss} \excludeversion{ignore} \input{defs} \input{nef_macros} \begin{document} \title{Point location in plane maps} \author{Michael Seel} \date{\today} \maketitle \tableofcontents %KILLEND REP \section{The Manual Page} \input manpages/PM_point_locator.man \section{Implementation} This section describes two point location and ray shooting modules working in plane maps that are decorated according to our plane map decorator model. The first module offers naive point location without any preprocessing of the plane map. The second module implements point location and ray shooting for the case of iterated queries and trades query time for a one time preprocessing phase. In the center of our approach is a constrained triangulation of the obstacle set (the input plane map). For the point location we offer an optimal solution with respect to space and runtime based on a slap subdivision with persistent dictionaries from LEDA. As CGAL demands to minimize cross library dependency we offer a default fall-back solution based on walks in the triangulation. For ray shooting in general obstacle sets the theory recommends to use minimum weight triangulations. However as minimum weight triangulations are difficult to calculate we restrict our effort to optimize the weight of the constrained triangulation only locally. We will present the corresponding theoretical basics in the section where we explain the construction of the triangulation. We start with the naive module and append the triangulation module. \subsection{Point location and ray shooting done naively} In this section we implement the class |PM_naive_point_locator<>| that wraps naive point location functionality. We first sketch the class design consisting of the local types and the interface. Then we present the two main interface operations for point location and ray shooting. A point locator |PM_naive_point_locator<>| is a generalization of a plane map decorator |PM_decorator_| where we inherit the decorator interface. We obtain the geometry used for point location from |Geometry_|. <>= /*{\Moptions print_title=yes }*/ /*{\Msubst PM_decorator_#PMD Geometry_#GEO }*/ /*{\Manpage {PM_naive_point_locator}{PMD,GEO} {Naive point location in plane maps}{PL}}*/ /*{\Mdefinition An instance |\Mvar| of data type |\Mname| encapsulates naive point location queries within a plane map |P|. The two template parameters are specified via concepts. |PM_decorator_| must be a model of the concept |PMDecorator| as described in the appendix. |Geometry_| must be a model of the concept |AffineGeometryTraits_2| as described in the appendix. For a specification of plane maps see also the concept of |PMConstDecorator|.}*/ /*{\Mgeneralization PMD}*/ template class PM_naive_point_locator : public PM_decorator_ { protected: typedef PM_decorator_ Base; typedef PM_naive_point_locator Self; const Geometry_& K; public: <> <> <> }; // PM_naive_point_locator @ We transport types from |PM_decorator_| and |Geometry_| into the local scope. A CGAL |Object| is a general wrapper class for any type. Unwrapping can be done typesave by means of C++ dynamic casts. The |Object_handle| type is just a generalized CGAL |Object| where we add the |NULL| equality test. <>= /*{\Mtypes 5}*/ typedef PM_decorator_ Decorator; /*{\Mtypemember equals |PM_decorator_|.}*/ typedef typename Decorator::Plane_map Plane_map; /*{\Mtypemember the plane map type decorated by |Decorator|.}*/ typedef typename Decorator::Mark Mark; /*{\Mtypemember the attribute of all objects (vertices, edges, faces).}*/ typedef Geometry_ Geometry; /*{\Mtypemember equals |Geometry_|.}*/ typedef typename Geometry_::Point_2 Point; /*{\Mtypemember the point type of the geometry kernel.\\ \require |Geometry::Point_2| equals |Plane_map::Point|.}*/ typedef typename Geometry_::Segment_2 Segment; /*{\Mtypemember the segment type of the geometry kernel.}*/ typedef typename Geometry_::Direction_2 Direction; /*{\Mtext Local types are handles, iterators and circulators of the following kind: |Vertex_const_handle|, |Vertex_const_iterator|, |Halfedge_const_handle|, |Halfedge_const_iterator|, |Face_const_handle|, |Face_const_iterator|.}*/ typedef CGAL::Object_handle Object_handle; /*{\Mtypemember a generic handle to an object of the underlying plane map. The kind of the object |(vertex, halfedge,face)| can be determined and the object assigned by the three functions:\\ |bool assign(Vertex_const_handle& h, Object_handle o)|\\ |bool assign(Halfedge_const_handle& h, Object_handle o)|\\ |bool assign(Face_const_handle& h, Object_handle o)|\\ where each function returns |true| iff the assignment of |o| to |h| was valid.}*/ @ \begin{ignoreindiss} <>= #define CGAL_USING(t) typedef typename PM_decorator_::t t CGAL_USING(Vertex_handle); CGAL_USING(Halfedge_handle); CGAL_USING(Face_handle); CGAL_USING(Vertex_const_handle); CGAL_USING(Halfedge_const_handle); CGAL_USING(Face_const_handle); CGAL_USING(Vertex_iterator); CGAL_USING(Halfedge_iterator); CGAL_USING(Face_iterator); CGAL_USING(Vertex_const_iterator); CGAL_USING(Halfedge_const_iterator); CGAL_USING(Face_const_iterator); CGAL_USING(Halfedge_around_vertex_circulator); CGAL_USING(Halfedge_around_vertex_const_circulator); CGAL_USING(Halfedge_around_face_circulator); CGAL_USING(Halfedge_around_face_const_circulator); #undef CGAL_USING @ \end{ignoreindiss}% The naive interface provides construction, access to the attributes of type |Mark|, point location, and ray shooting. Note that the ray shooting is parameterized by a template predicate that allows adaptation of the termination criterion of the ray-shot. <>= /*{\Mcreation 3}*/ PM_naive_point_locator() : Base() {} /*{\Moptions constref=yes}*/ PM_naive_point_locator(const Plane_map& P, const Geometry& k = Geometry()) : Base(const_cast(P)), K(k) {} /*{\Mcreate constructs a point locator working on |P|.}*/ /*{\Moptions constref=no}*/ /*{\Moperations 2.5 0.5}*/ const Mark& mark(Object_handle h) const /*{\Mop returns the mark associated to the object |h|.}*/ { Vertex_const_handle v; Halfedge_const_handle e; Face_const_handle f; if ( assign(v,h) ) return mark(v); if ( assign(e,h) ) return mark(e); if ( assign(f,h) ) return mark(f); CGAL_assertion_msg(0, "PM_point_locator::mark: Object_handle holds no object."); #if !defined(__BORLANDC__) return mark(v); // never reached #endif } <> <> @ \begin{ignoreindiss} <>= // C++ is really friendly: #define USECMARK(t) const Mark& mark(t h) const { return Base::mark(h); } #define USEMARK(t) Mark& mark(t h) const { return Base::mark(h); } USEMARK(Vertex_handle) USEMARK(Halfedge_handle) USEMARK(Face_handle) USECMARK(Vertex_const_handle) USECMARK(Halfedge_const_handle) USECMARK(Face_const_handle) #undef USEMARK #undef USECMARK @ \end{ignoreindiss} \subsubsection*{Point location} The following point location scheme runs in linear time without any preprocessing. The segment |s| is required to intersect the skeleton of our plane map |P|. We just check if the location |p = source(s)| is part of the embedding of any vertex or uedge of the $1$-skeleton of |P|. <>= Object_handle locate(const Segment& s) const /*{\Mop returns a generic handle |h| to an object (vertex, halfedge, face) of the underlying plane map |P| which contains the point |p = s.source()| in its relative interior. |s.target()| must be a point such that |s| intersects the $1$-skeleton of |P|.}*/ { TRACEN("locate naivly "<> if ( e_res != Halfedge_const_handle() ) return Object_handle((Face_const_handle)(face(e_res))); else return Object_handle((Face_const_handle)(face(v_res))); } @ If |p| is located in the interior of a face |f| we can identify |f| only by any object in its boundary. We do this by a thought ray shoot along |s| through all obstacles of the $1$-skeleton. Note that we are sure to hit at least one object of the $1$-skeleton of the plane map. Let |q = s.target()|. We check the intersection points of the segment |s| with all skeleton objects. As soon as we find an object we prune |s|, store the object that gives possibly access to the face containing |p|, and iterate. An edge |e| that intersects |s| in its relative interior is trivial to handle. In this case we just clip |s| by the intersection point and go on with our search to possibly find a closer edge to |p|. Isolated vertices are easy to treat too. They can be checked directly as they carry a link to their incident face. Non-isolated vertices don't carry the face information directly. There we have to find an incident edge whose incident face intersects |s|. Intuitively when |s| contains a non-isolated vertex |v| we use a symbolic perturbation scheme. Let's assume that our direction is down along the negative $y$-axis. We just shift the whole scene infinitesimally left. Then we only hit edges. But of course the closest edge in $A(v)$\footnote{the adjacency list of $v$.} has to be determined not by proximity but by the direction of edges in $A(v)$. Now we are interested in an edge |e| of $A(v)$ that has a minimal counterclockwise angle to |s|. |e| bounds the wedge containing |p|. See figure \figref{walkseg}. \displayeps{walkseg}{Configurations of |s| and the $1$-skeleton of |P|}{9cm} Assume $d$ to be a direction anchored in $v$. The operation |out_wedge(v,d,collinear)| returns the halfedge |e| bounding a wedge in between two neighbored edges in the adjacency list of |v| that contains |d|. If |d| extends along an edge then |e| is this edge. If |d| extends into the interior of such a wedge then |e| is the first edge hit when |d| is rotated clockwise. As a precondition we ask |v| not to be isolated. <>= Halfedge_const_handle out_wedge(Vertex_const_handle v, const Direction& d, bool& collinear) const /*{\Xop returns a halfedge |e| bounding a wedge in between two neighbored edges in the adjacency list of |v| which contains |d|. If |d| extends along a edge then |e| is this edge. If |d| extends into the interior of such a wedge then |e| is the first edge hit when |d| is rotated clockwise. \precond |v| is not isolated.}*/ { TRACEN("out_wedge "<>= Segment ss = s; // we shorten the segment iteratively Direction dso = K.construct_direction(K.target(s),p), d_res; 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) ) { TRACEN(" location via vertex at "<>= for (eit = halfedges_begin(); eit != halfedges_end(); ++eit) { if ( visited[eit] ) continue; Point se = point(source(eit)), te = point(target(eit)); int o1 = K.orientation(ss,se); int o2 = K.orientation(ss,te); if ( o1 == -o2 && // internal intersection K.orientation(se,te,K.source(ss)) != K.orientation(se,te,K.target(ss)) ) { TRACEN(" location via halfedge "< 0 ? eit : twin(eit)); // o2>0 => te left of s and se right of s => p left of e visited[eit] = visited[twin(eit)] = true; TRACEN(" determined "<>= template Object_handle ray_shoot(const Segment& s, const Object_predicate& M) const /*{\Mop returns an |Object_handle o| which can be converted to a |Vertex_const_handle|, |Halfedge_const_handle|, |Face_const_handle| |h| as described above. The object predicate |M| has to have function operators \\ |bool operator() (const Vertex_/Halfedge_/Face_const_handle&)|.\\ The object returned is intersected by the segment |s| and has minimal distance to |s.source()| and |M(h)| holds on the converted object. The operation returns the null handle |NULL| if the ray shoot along |s| does not hit any object |h| of |P| with |M(h)|.}*/ { TRACEN("naive ray_shoot "<> <> return h; } @ Look at a vertex |v| at position |pv| on segment |s|. If |M(v)| we can shorten |s| and iterate. If not |M(v)| then a ray shoot along |s| can still hit an object at |v| where |M| is fulfilled in form of an outgoing edge or the face in a wedge between two edges. At each vertex we look only for the part of |s| between |pv| and |s.target()|. <>= for (v = vertices_begin(); v != vertices_end(); ++v) { Point pv = point(v); if ( !K.contains(ss,pv) ) continue; TRACEN("candidate "<>= Halfedge_const_iterator e_res; for(e = halfedges_begin(); e != halfedges_end(); ++(++e)) { Segment es = segment(e); int o1 = K.orientation(ss,K.source(es)); int o2 = K.orientation(ss,K.target(es)); if ( o1 == -o2 && o1 != 0 && K.orientation(es, K.source(ss)) == - K.orientation(es, K.target(ss)) ) { // internal intersection TRACEN("candidate "< 0 ? e : twin(e)); // o2 > 0 => te left of s and se right of s => p left of e if ( M(e_res) ) { h = Object_handle(e_res); ss = K.construct_segment(p,p_res); } else if ( M(face(twin(e_res))) ) { h = Object_handle(face(twin(e_res))); ss = K.construct_segment(p,p_res); } } } @ \subsubsection*{Correctness and Runtimes} We summarize the facts in a lemma. \begin{lemma} If the segment |s| intersects the skeleton of |P| then |point_location(s)| returns a generic handle |h| to the object (vertex, halfedge, face) whose embedding in the plane contains the source of |s|. The running time is linear in the size of the plane map |P|. If the segment |s| intersects the skeleton of |P| then |ray_shoot(s,M)| determines a generic handle |h| which is |NULL| if no object of |P| that intersects |s| fulfills |M(h)|\footnote {Note that the predicate |M| is defined on vertex, halfedge, and face handles for performance reasons. However we sloppily write |M(h)| on a generic handle wrapping one of the above.} and which can be converted to a corresponding vertex, halfedge, or face otherwise. In the latter case the object fulfills |M(h)| and additionally has minimal distance to |source(s)|. The running time is linear in the size of the plane map. \end{lemma} \begin{proof} For the point location case note that we iterate over all objects of the skeleton of |P| twice. If |p = source(s)| is contained in a skeleton object then |h| is determined already in $\langle$\textit{NPL locate}$\rangle$ in one of the two iterations. If |p| is contained in the plane in between the $1$-skeleton then the closest skeleton object along |s| defines the face. If the closest object is a vertex we determine this vertex in the vertex iteration of $\langle$\textit{NPL determine closest object intersecting s}$\rangle$. For non-isolated vertices we also iterate over all edges in the adjacency list of the vertices (in |out_wedge()|) to determine a halfedge visible from |p| along |s|. If the closest object is a halfedge intersecting |s| in its iterior the halfedge iteration of $\langle$\textit{NPL determine closest object intersecting s}$\rangle$ does the job. As all geometric tests take constant time this gives us the linear runtime bound. Note that starting from |s| we only shorten |s|. Due to our precondition |s| indeed is shortened at least once. This ensures that we obtain an object which allows us to obtain the face containing |p|. For the ray shooting note that if |p| already is contained in an object marked correctly then the point location already determines this object. If not then a similar iteration over all objects now only considering the predicate |M| does the job. If no object is determined then |h| is the |NULL| handle. The runtime stays linear. \end{proof} \begin{ignoreindiss} <>= Segment segment(Halfedge_const_handle e) const { return K.construct_segment(point(source(e)), point(target(e))); } Direction direction(Halfedge_const_handle e) const { return K.construct_direction(point(source(e)),point(target(e))); } <>= /*{\Mimplementation Naive query operations are realized by checking the intersection points of the $1$-skeleton of the plane map |P| with the query segments $s$. This method takes time linear in the size $n$ of the underlying plane map without any preprocessing.}*/ @ \end{ignoreindiss} \subsection{Point location and ray shooting based on constrained triangulations} Our second point location structure is based on a convex subdivision of the plane map by means of a constrained triangulation\footnote{The constrained triangulation of plane map is a triangulation of the vertices such that all edges are part of the triangulation}. We create the point location structure starting from a plane map. The edges and isolated vertices of the plane map are our obstacle set. We reference the plane map again via a decorator. We present the class layout, and the implementation of the two main interface operations |locate()| and |ray_shoot()|. The construction of the constrained triangulation and the integration of persistent dictionaries as a location structure is shown in the following sections. The main class is derived from the naive point location class to inherit its primitive methods. The class |PM_point_locator| decorates the input plane map |P|. We store the additional triangulation via an additional decorator |CT| in the local scope. Note that we talk about two plane map structures here. The plane map decorated by the |*this| object which we also call the input structure and the plane map storing the constrained triangulation of the input structure. We have to maintain links such that each object in |CT| knows the object of |*this| that supports\footnote{an object $a$ supports $b$ if |embedding(a)| contains |embedding(b)|.} it. We use an optional point location structure based on persistent dictionaries that is configuration dependent. All properties of that optional module are explained in section \ref{point location with persistent dictionaries}. <>= /*{\Moptions print_title=yes }*/ /*{\Msubst PM_decorator_#PMD Geometry_#GEO }*/ /*{\Manpage {PM_point_locator}{PMD,GEO} {Point location in plane maps via LMWT}{PL}}*/ /*{\Mdefinition An instance |\Mvar| of data type |\Mname| encapsulates point location queries within a plane map |P|. The two template parameters are specified via concepts. |PMD| must be a model of the concept |PMDecorator| as described in the appendix. |GEO| must be a model of the concept |AffineGeometryTraits_2| as described in the appendix. For a specification of plane maps see also the concept of |PMConstDecorator|.}*/ /*{\Mgeneralization PMD^#PM_naive_point_locator}*/ template class PM_point_locator : public PM_naive_point_locator { protected: typedef PM_naive_point_locator Base; typedef PM_point_locator Self; Base CT; <> public: <> protected: <> public: <> }; // PM_point_locator @ \begin{ignoreindiss} <>= #define CGAL_USING(t) typedef typename Base::t t CGAL_USING(Decorator); CGAL_USING(Plane_map); CGAL_USING(Mark); CGAL_USING(Geometry); CGAL_USING(Point); CGAL_USING(Segment); CGAL_USING(Direction); CGAL_USING(Object_handle); CGAL_USING(Vertex_handle); CGAL_USING(Halfedge_handle); CGAL_USING(Face_handle); CGAL_USING(Vertex_const_handle); CGAL_USING(Halfedge_const_handle); CGAL_USING(Face_const_handle); CGAL_USING(Vertex_iterator); CGAL_USING(Halfedge_iterator); CGAL_USING(Face_iterator); CGAL_USING(Vertex_const_iterator); CGAL_USING(Halfedge_const_iterator); CGAL_USING(Face_const_iterator); CGAL_USING(Halfedge_around_vertex_circulator); CGAL_USING(Halfedge_around_vertex_const_circulator); CGAL_USING(Halfedge_around_face_circulator); CGAL_USING(Halfedge_around_face_const_circulator); #undef CGAL_USING @ \end{ignoreindiss} Our constrained triangulation |CT| decorates the $1$-skeleton of a plane map consisting just of vertices and edges. We save the face objects there. Each vertex |v| of |CT| is linked to an object of type |VF_pair| which stores the input vertex supporting |v| and potentially an incident face of the input plane map if |v| is isolated (before the triangulation process). Each halfedge |e| of |CT| is linked to an object of type |EF_pair| which stores the input halfedge supporting |e| and the face incident to |e| in the input structure. <>= /*{\Mtypes 2}*/ /*{\Mtext All local types of |PM_naive_point_locator| are inherited.}*/ typedef std::pair VF_pair; typedef std::pair EF_pair; @ We associate the above pair structures to the skeleton objects via the available |GenPtr| slot. We have one additional pointer in each object that we can use for temporary information. We use a special class |geninfo| for the expansion and the access. See the manual page of |geninfo| in the appendix for its usage. <>= Vertex_const_handle input_vertex(Vertex_const_handle v) const { return geninfo::const_access(CT.info(v)).first; } Halfedge_const_handle input_halfedge(Halfedge_const_handle e) const { return geninfo::const_access(CT.info(e)).first; } Face_const_handle input_face(Halfedge_const_handle e) const { return geninfo::const_access(CT.info(e)).second; } @ The public interface provides construction, access to the underlying triangulation, point location, and ray shooting. Note that the ray shooting is parameterized by a template predicate that allows adaptation of the termination criterion of the ray shooting walk. <>= /*{\Mcreation 3}*/ PM_point_locator() { <> } /*{\Moptions constref=yes}*/ PM_point_locator(const Plane_map& P, const Geometry& k = Geometry()); /*{\Mcreate constructs a point locator working on |P|.}*/ ~PM_point_locator(); /*{\Moperations 2.5 0.5}*/ const Decorator& triangulation() const { return CT; } /*{\Mop access to the constrained triangulation structure that is superimposed to |P|.}*/ /*{\Moptions constref=no}*/ <> <> Object_handle walk_in_triangulation(const Point& p) const; @ \subsubsection*{Construction and Destruction} We use a constrained triangulation of the input plane map to do ray shooting walks. We leave the input structure |P| unchanged. Therefore we clone the structure into one which we extend to a constrained triangulation. The cloning can be enriched by actions on the cloned objects via a data accessor. See the description of the operation |clone_skeleton| in the manual page of |PMDecorator| for the concept of this data accessor. Our data accessor |CT_link_to_original| does the linkage of input objects to cloned objects. Additionally it takes care that the cloned objects know the faces incident to their supporting input objects. For the used data association method see the manual page of |geninfo|. <>= struct CT_link_to_original : Decorator { // CT decorator const Decorator& Po; CT_link_to_original(const Decorator& P, const Decorator& Poi) : Decorator(P), Po(Poi) {} void operator()(Vertex_handle vn, Vertex_const_handle vo) const { Face_const_handle f; if ( Po.is_isolated(vo) ) f = Po.face(vo); geninfo::create(info(vn)); geninfo::access(info(vn)) = VF_pair(vo,f); TRACEN("linking to org "<::create(info(hn)); geninfo::access(info(hn)) = EF_pair(ho,Po.face(ho)); TRACEN("linking to org "<>= template PM_point_locator:: PM_point_locator(const Plane_map& P, const Geometry& k) : Base(P,k), CT(*(new Plane_map),k) { TRACEN("PM_point_locator construction"); CT.clone_skeleton(P,CT_link_to_original(CT,*this)); triangulate_CT(); minimize_weight_CT(); <> } @ Destruction mirrors the allocations within |CT| above. We discard the extended information slots in all objects (vertices and halfedges). Then we discard all objects. Finally we free the memory for the |CT| plane map on the heap. Optionally we clean up the memory of the persistent module. <>= template PM_point_locator:: ~PM_point_locator() { TRACEN("clear_static_point_locator"); Vertex_iterator vit, vend = CT.vertices_end(); for (vit = CT.vertices_begin(); vit != vend; ++vit) { geninfo::clear(CT.info(vit)); } Halfedge_iterator eit, eend = CT.halfedges_end(); for (eit = CT.halfedges_begin(); eit != eend; ++eit) { geninfo::clear(CT.info(eit)); } CT.clear(); delete &(CT.plane_map()); <> } @ \subsubsection*{Point location} The following chunk interfaces the basic point location layer depending on the presence of LEDA. |LOCATE_IN_TRIANGULATION| is either |walk_in_triangulation| or a call to the persistent point location structure in $\langle$\textit{TPL persistent module members}$\rangle$ of section \ref{point location with persistent dictionaries}. <>= Object_handle locate(const Point& p) const /*{\Mop returns a generic handle |h| to an object (vertex, halfedge, face) of |P| which contains the point |p| in its relative interior.}*/ { Object_handle h = LOCATE_IN_TRIANGULATION(p); Vertex_const_handle v_triang; if ( assign(v_triang,h) ) { return input_object(v_triang); } Halfedge_const_handle e_triang; if ( assign(e_triang,h) ) { Halfedge_const_handle e = input_halfedge(e_triang); if ( e == Halfedge_const_handle() ) // inserted during triangulation return Object_handle(input_face(e_triang)); int orientation_ = K.orientation(segment(e),p); if ( orientation_ == 0 ) return Object_handle(e); if ( orientation_ < 0 ) return Object_handle(face(twin(e))); if ( orientation_ > 0 ) return Object_handle(face(e)); } assert(0); return h; // compiler warning } @ The following function |walk_in_triangulation(q)| provides a fall-back solution to point location. It returns an |Object_handle f| which is either a vertex |v| with |point(v) == q| or a halfedge |e| where |e| contains |q| in its relative interior, or one of the two triangles incident to |e| (or |twin(e)| respectively) contains |q|. This semantics is equivalent to the function from the generic point location framework programmed by Sven Thiel \cite{thiel-mt} which allows us to obtain a halfedge or vertex that contains |q| or that is vertically below |q|. We walk from the embedding |p = point(v)| of the first vertex $v = |vertices_begin()|$ of our structure to the point |q| that we expect to lie in the interior of the triangulation. We examine the skeleton of the triangulation. We can thereby hit vertices and edges, the latter in two ways. We walk along an edge |e| when |segment(e)| and |s = pq| overlap, or we cross |e| in its relative interior. Our walk is thus an iteration storing a vertex or an edge and a flag how we traverse them. We store the oriented halfedge |e| either in direction of |s| or such that |q| is left of |e|. <>= enum object_kind { VERTEX, EDGE_CROSSING, EDGE_COLLINEAR }; <>= template typename PM_point_locator::Object_handle PM_point_locator::walk_in_triangulation(const Point& q) const { TRACEN("walk in triangulation "<> break; case EDGE_CROSSING: <> break; case EDGE_COLLINEAR: <> break; } #if !defined(__BORLANDC__) return Object_handle(); // never reached warning acceptable #endif } @ We walk along |s| and hit a vertex |v|. If we have reached |q| we are done. Otherwise we walk the adjacency list to find either the wedge between two edges via which we leave the vertex or we find a halfedge collinear to |s|. The adjacency list walk is encapsulated in |out_wedge|. We obtain a halfedge |e_out| out of |v|, either collinear or bounding the wedge right of |s|. If |e_out| is collinear to |s| it is the next object. Otherwise we take the edge |next(e_out)| closing the wedge to a triangle which we certainly hit next on our walk along |s|. <>= { TRACEN("vertex "<>= { TRACEN("crossing edge "< 0) ) // q not left of e return Object_handle(e); Vertex_const_handle v_cand = CT.target(CT.next(e)); int orientation_ = K.orientation(p,q,CT.point(v_cand)); switch( orientation_ ) { case 0: // collinear if ( K.strictly_ordered_along_line(p,q,CT.point(v_cand)) ) return Object_handle(e); v = v_cand; current = VERTEX; break; case +1: // left_turn e = twin(next(e)); current = EDGE_CROSSING; break; case -1: e = twin(previous(e)); current = EDGE_CROSSING; break; } } @ Hitting a collinear edge is easy to treat. If it does not contain |q| its target is the next object. <>= { TRACEN("collinear edge "<>= template Object_handle ray_shoot(const Segment& s, const Object_predicate& M) const /*{\Mop returns an |Object_handle o| which can be converted to a |Vertex_const_handle|, |Halfedge_const_handle|, |Face_const_handle| |h| as described above. The object predicate |M| has to have function operators\\ |bool operator() (const Vertex_/ Halfedge_/Face_const_handle&) const|.\\ The object returned is intersected by the segment |s| and has minimal distance to |s.source()| and |M(h)| holds on the converted object. The operation returns the null handle |NULL| if the ray shoot along |s| does not hit any object |h| of |P| with |M(h)|.}*/ { TRACEN("ray_shoot "<> while (true) switch ( current ) { case VERTEX: <> break; case EDGE_CROSSING: <> break; case EDGE_COLLINEAR: <> break; } // assert(0); return h; // compiler warning } @ If we hit a vertex we are done. <>= if ( assign(v,h) ) { TRACEN("located vertex "<>= if ( assign(e,h) ) { TRACEN("located edge "<> } else { // p not on segment, thus in triangle if ( orientation_ < 0 ) e = CT.twin(e); // now p left of e <> } } @ In this case we have to handle the cases that |s| overlaps |segment(e)| or that we shoot into a face adjacent to |e|. <>= TRACEN("on edge "<0) ) // not left_turn e = CT.twin(e); } @ If the triangle (underlying face) which contains |p| fulfills |M| we return it. Otherwise we have to find the object (vertex or edge) hit by the ray shoot from |p| along |s|. Note that it might be that |s| is totally contained in the triangle in which case we return the |NULL| handle. <>= TRACEN("in face at "< 0 && or1 < 0 && !K.left_turn(p1,p2,q) ) { e = CT.twin(e); current = EDGE_CROSSING; } else if ( or3 > 0 && or2 < 0 && !K.left_turn(p2,p3,q) ) { e = CT.twin(CT.next(e)); current = EDGE_CROSSING; } else if ( or1 > 0 && or3 < 0 && !K.left_turn(p3,p1,q) ) { e = CT.twin(CT.previous(e)); current = EDGE_CROSSING; } else return Object_handle(); @ Now we are on our walk along |s|. If we hit a vertex |v| with |M(v)| we have found the right object. Otherwise it could be that we have reached |q|. If not we walk the adjacency list to find either the wedge between two edges via which we leave the vertex or we find a halfedge collinear to |s|. The adjacency list walk is encapsulated in |out_wedge()|. We obtain a halfedge |e_out| out of |v|, either collinear or bounding the wedge right of |s|. Note that if the wedge represents a face |f| with |M(f)| we are done. Otherwise we take the edge |next(e_out)| closing the wedge to a triangle which we certainly hit next on our walk along |s|. <>= { TRACEN("vertex "<>= { TRACEN("crossing edge "<>= { TRACEN("collinear edge "<>= Object_handle input_object(Vertex_const_handle v) const { return Object_handle(input_vertex(v)); } Object_handle input_object(Halfedge_const_handle e) const { Halfedge_const_handle e_org = input_halfedge(e); if ( e_org != Halfedge_const_handle() ) return Object_handle( e_org ); // now e_org is not existing return Object_handle( input_face(e) ); } /*{\Mimplementation The efficiency of this point location module is mostly based on heuristics. Therefore worst case bounds are not very expressive. The query operations take up to linear time for subsequent query operations though they are better in practise. They trigger a one-time initialization which needs worst case $O(n^2)$ time though runtime tests often show subquadratic results. The necessary space for the query structure is subsumed in the storage space $O(n)$ of the input plane map. The query times are configuration dependent. If LEDA is present then point location is done via the slap method based on persistent dictionaries. Then $T_{pl}(n) = O( \log(n) )$. If CGAL is not configured to use LEDA then point location is done via a segment walk in the underlying convex subdivision of $P$. In this case $T_{pl}(n)$ is the number of triangles crossed by a walk from the boundary of the structure to the query point. The time for the ray shooting operation $T_{rs}(n)$ is the time for the point location $T_{pl}(n)$ plus the time for the walk in the triangulation that is superimposed to the plane map. Let's consider the plane map edges as obstacles and the additional triangulation edges as non-obstacle edges. Let's call the sum of the lengths of all edges of the triangulation its weight. If the calculated triangulation approximates\footnote{The calculation of general minimum-weight-triangulations is conjectured to be NP-complete and locally-minimum-weight-triangulations that we use are considered good approximations.} the minimum weight triangulation of the obstacle set then the stepping quotient\footnote {The number of non-obstacle edges crossed until an obstacle edge is hit.} for a random direction of the ray shot is expected to be $O( \sqrt{n} )$.}*/ @ \subsection{The constrained triangulation} \repdiss{We calculate the constrained triangulation of the cloned skeleton by our constrained triangulation sweep as presented in chapter \ref{constrained triangulations}}{}. The class |Constrained_triang_traits<>| obtains three traits models |PMDEC|, |GEOM|, and |NEWEDGE| on instantiation. The two former parameters are the local types |PM_decorator| and |Geometry|. The latter is the data accessor type implemented below. All edges of the cloned skeleton carry links to the edges of |P|. We mark additional triangulation edges by a default link to |Halfedge_const_handle()| that can be checked after the triangulation process to separate input halfedges (obstacles) from triangulation halfedges. We forward input face links from existing edges of the triangulation to newly created edges. Thereby each newly created edge knows the face that it splits into triangles. This information association and transfer is done by means of |CT_new_edge|. The following function operator |operator()(Halfedge_handle&)| attributes the newly created triangulation edge |e| in |CT| by an additional |EF_pair| in a similar way as the cloning process did it for the input edges. As |e| does not have a supporting halfedge in |P| it obtains the default handle. |e| obtains the mark information from a face that supports it with respect to the input structure. The face usually comes from an adjacent edge of |CT|. If there is none, then we obtain is from the source which was isolated in the input structure before |e| was created. <>= struct CT_new_edge : Decorator { const Decorator& _DP; CT_new_edge(const Decorator& CT, const Decorator& DP) : Decorator(CT), _DP(DP) {} void operator()(Halfedge_handle& e) const { Halfedge_handle e_from = previous(e); Face_const_handle f; if ( is_closed_at_source(e) ) // source(e) was isolated before f = geninfo::access(info(source(e))).second; else f = geninfo::access(info(e_from)).second; mark(e) = _DP.mark(f); geninfo::create(info(e)); geninfo::create(info(twin(e))); geninfo::access(info(e)).first = geninfo::access(info(twin(e))).first = Halfedge_const_handle(); geninfo::access(info(e)).second = geninfo::access(info(twin(e))).second = f; TRACEN("CT_new_edge "<>= void triangulate_CT() const { TRACEN("triangulate_CT"); typedef CGAL::Constrained_triang_traits< Decorator,Geometry,CT_new_edge> NCTT; typedef CGAL::generic_sweep Constrained_triang_sweep; CT_new_edge NE(CT,*this); Constrained_triang_sweep T(NE,CT.plane_map(),K); T.sweep(); } @ \subsection{Minimizing the triangulation weight} For a plane map or a triangulation let its weight be the sum of the length of all edges. After the calculation of the constrained triangulation we minimize locally the weight of the triangulation by a sequence of flipping operations starting from all non-constrained edges. We first motivate the following procedure by some theory and some experiments. \begin{itemize} \item Ray shooting by line walks in planar triangulations are worst-case expensive. Agarwal et al \cite{agarwal95} show that such a walk can have linear costs in the size of the triangulation though the shoot does not hit any obstacle edge. Aronov and Fortune show in \cite{aronovfortune97} that average case stepping cost for random lines can be bounded by $O( \sqrt{n} )$ for plane maps of size $n$ if we use triangulations of minimal weight. Unfortunately such triangulations are hard to construct in general. For general points sets the problem is conjectured to be NP-hard. In simple polygons there's a $O(n^3)$ solution by dynamic programming which is hardly practical. Thus currently only approximation or even heuristics offer practical solutions. \item Note that the delaunay triangulation of a point set of size $n$ in general can be a bad approximation of the minimal weight triangulation \cite{kirkpatrick80} but it has been shown to be a $O(\log n)$ approximation \cite{lingas86} if the points are universally distributed. However we have no such result for the constrained case. \item Heuristic results on the approximation of minimum weight triangulation are presented by Dickerson et al. in \cite{dickerson95}. They present results that propose to use local minimal weight triangulations as an approximations of the general unsolved problem. \item The running time of the following flipping algorithm is worst case quadratic though runtime tests seem to show a subquadratic runtime for random inputs\footnote{see the runtime results for the flipping number in \cite{dickerson95}}. The quadratic bound for the worst case runtime is based on a result of Hurtado et al. \cite{hurtado96} where they show that the graph of triangulations of a simple polygon is connected and has the complexity of $O(n^2)$. Note that the termination is guaranteed due to the fact that for each quadrangle of points we can only choose the minimal diagonal once, thus we never cycle. \end{itemize} <>= void minimize_weight_CT() const { TRACEN("minimize_weight_CT"); if ( number_of_vertices() < 2 ) return; std::list S; /* We maintain a stack |S| of edges containing diagonals which might have to be flipped. */ int flip_count = 0; <> while ( !S.empty() ) { Halfedge_handle e = S.front(); S.pop_front(); Halfedge_handle r = twin(e); Halfedge_const_handle e_org = input_halfedge(e); if ( e_org != Halfedge_const_handle() ) continue; <> <> } TRACEN(" flipped "<>= Halfedge_iterator e; for (e = CT.halfedges_begin(); e != CT.halfedges_end(); ++(++e)) { Halfedge_const_handle e_org = input_halfedge(e); if ( e_org != Halfedge_const_handle() ) continue; S.push_back(e); } @ A non-constrained edge can only be flipped if it is the diagonal of a convex quadrilateral. We can continue if it is not. <>= Halfedge_handle e1 = next(r); Halfedge_handle e3 = next(e); // e1,e3: edges of quadrilateral with diagonal e Point a = point(source(e1)); Point b = point(target(e1)); Point c = point(source(e3)); Point d = point(target(e3)); if (! (K.orientation(b,d,a) > 0 && // left_turn K.orientation(b,d,c) < 0) ) // right_turn continue; @ We check if for our non-constrained edge |e| the complementary diagonal has less weight. If yes we flip |e|. <>= if ( K.first_pair_closer_than_second(b,d,a,c) ) { // flip TRACEN("flipping diagonal of quadilateral"<>= <> // file : include/CGAL/Nef_2/PM_point_locator.h <> #ifndef CGAL_PM_POINT_LOCATOR_H #define CGAL_PM_POINT_LOCATOR_H #include #include #include #include #undef _DEBUG #define _DEBUG 17 #include #include <> CGAL_BEGIN_NAMESPACE <> <> <> <> <> CGAL_END_NAMESPACE #endif // CGAL_PM_POINT_LOCATOR_H @ \end{ignoreindiss} \subsection{Point location with persistent dictionaries} \label{point location with persistent dictionaries} We add a dynamically allocated point location structure of type |PointLocator| working on the constrained triangulation of the class |PM_point_locator|. \begin{ignoreindiss} The used persistent dictionaries are part of LEDA starting version 4.2. <>= #ifdef CGAL_USE_LEDA # if CGAL_LEDA_VERSION < 500 # include # else # include # endif # if __LEDA__ > 410 && __LEDA__ < 441 # define CGAL_USING_PPL # include # endif #endif @ \end{ignoreindiss}% The point location framework is added to the point location class only if the necessary modules are present. The traits class |PM_persistent_PL_traits| is defined below. The point location framework |PointLocator| is described in Sven Thiel's masters thesis \cite{thiel-mt}. <>= #ifdef CGAL_USING_PPL typedef PM_persistent_PL_traits PMPPLT; typedef PointLocator PMPP_locator; PMPP_locator* pPPL; #define LOCATE_IN_TRIANGULATION pPPL->locate_down #else #define LOCATE_IN_TRIANGULATION walk_in_triangulation #endif @ The first parameter of |PMPP_locator| is the graph structure worked on, the second is a traits class object. We forward a reference to the geometric kernel |K| that we already obtained for the |*this| object of type |PM_point_locator|. <>= #ifdef CGAL_USING_PPL pPPL = new PMPP_locator(CT,PMPPLT(K)); #endif <>= #ifdef CGAL_USING_PPL pPPL = 0; #endif <>= #ifdef CGAL_USING_PPL delete pPPL; pPPL=0; #endif @ \begin{ignoreindiss} <>= #ifdef CGAL_USING_PPL static const char* pointlocationversion = "point location via pers dicts"; #else static const char* pointlocationversion = "point location via seg walks"; #endif @ \end{ignoreindiss}% The rest of this section describes a traits model for the persistent point location framework as developed by Sven Thiel \cite{thiel-mt}. We adapt his framework for point location via persistent dictionaries within the constrained triangulation. Thereby we obtain a better runtime bound than the fall-back method |walk_in_triangulation()|. The class |PM_persistent_PL_traits| transports a bunch of types and methods into the point location data structure |PointLocator| of S. Thiel. Note that the class references a plane map and a geometric kernel, that we both forward on construction. \label{persistent point location traits} <>= template struct PM_persistent_PL_traits { <> <> <> <> <> <> <> }; @ \begin{ignoreindiss} <>= <> // file : include/CGAL/Nef_2/PM_persistent_PL.h <> #ifndef CGAL_PM_PERSISTENT_PL_H #define CGAL_PM_PM_PERSISTENT_PL_H #include <> #endif // CGAL_PM_PM_PERSISTENT_PL_H @ \end{ignoreindiss}% The type mapping is trivial. For the persistent point location we need the interface types |Graph|, |Node|, |Edge|. We additionally introduce the geometry. <>= typedef PMPL Graph; typedef typename PMPL::Vertex_const_handle Node; typedef typename PMPL::Halfedge_const_handle Edge; typedef typename PMPL::Face_const_handle Face; typedef typename PMPL::Object_handle Object_handle; typedef typename PMPL::Geometry Geometry; typedef typename PMPL::Point Point; typedef typename PMPL::Segment Segment; const Geometry* pK; @ We store a reference to the geometric kernel in the traits class. <>= PM_persistent_PL_traits() : pK(0) {} PM_persistent_PL_traits(const Geometry& k) : pK(&k) {} virtual ~PM_persistent_PL_traits() {} virtual void sweep_begin(const Graph&) {} virtual void sweep_moveto(const XCoord&) {} virtual void sweep_end() {} virtual void clear() {} <>= typedef typename PMPL::Vertex_const_iterator NodeIterator; NodeIterator Nodes_begin(const Graph& G) const { return G.vertices_begin(); } NodeIterator Nodes_end(const Graph& G) const { return G.vertices_end(); } Node toNode(const NodeIterator& nit) const { return nit; } typedef typename PMPL::Halfedge_around_vertex_const_circulator HAVC; struct IncEdgeIterator { HAVC _start, _curr; bool met; IncEdgeIterator() {} IncEdgeIterator(HAVC c) : _start(c), _curr(c), met(false) {} IncEdgeIterator& operator++() { if (_curr==_start) if (!met) { met=true; ++_curr; } else { _curr=HAVC(); } else ++_curr; return *this; } bool operator==(const IncEdgeIterator& it2) const { return _curr==it2._curr; } bool operator!=(const IncEdgeIterator& it2) const { return !(*this==it2); } }; Edge toEdge(const IncEdgeIterator& eit) const { return eit._curr; } IncEdgeIterator IncEdges_begin(const Graph& G, const Node& n) { return IncEdgeIterator(HAVC(G.first_out_edge(n))); } IncEdgeIterator IncEdges_end(const Graph& G, const Node& n) { return IncEdgeIterator(); } <>= enum EdgeCategory { StartingNonVertical, StartingVertical, EndingNonVertical, EndingVertical }; Node opposite(const Graph& G, const Edge& e, const Node& u) { if ( G.source(e) == u ) return G.target(e); else return G.source(e); } EdgeCategory ClassifyEdge(const Graph& G, const Edge& e, const Node& u) { Point p_u = G.point(u); Point p_v = G.point(opposite(G,e,u)); int cmpX = pK->compare_x(p_u, p_v); if ( cmpX < 0 ) return StartingNonVertical; if ( cmpX > 0 ) return EndingNonVertical; int cmpY = pK->compare_y(p_u, p_v); assert(cmpY != 0); if ( cmpY < 0 ) return StartingVertical; return EndingVertical; } @ The generic persistent point location framework maintains slaps of the graph skeleton ordered by the x-coordinates of the embedding of the nodes. We make the |point| type representing its x-coordinate and use |compare_x| from the kernel to sort them. <>= typedef Point XCoord; const XCoord getXCoord(const Point& p) const { return p; } const XCoord getXCoord(const Graph& G, const Node& n) const { return G.point(n); } class PredLessThanX { const Geometry* pK; public: PredLessThanX() : pK(0) {} PredLessThanX(const Geometry* pKi) : pK(pKi) {} PredLessThanX(const PredLessThanX& P) : pK(P.pK) { TRACEN("copy PredLessThanX"); } int operator() (const XCoord& x1, const XCoord& x2) const { return pK->compare_x(x1,x2) < 0; } }; PredLessThanX getLessThanX() const { return PredLessThanX(pK); } @ In our case curves are segments. We obtain them from the geometric kernel. <>= // Curve connected functionality: typedef Segment Curve; Curve makeCurve(const Point& p) const { return pK->construct_segment(p,p); } Curve makeCurve(const Graph& G, const Node& n) const { return makeCurve(G.point(n)); } Curve makeCurve(const Graph& G, const Edge& e) const { Point ps = G.point(G.source(e)), pt = G.point(G.target(e)); Curve res(G.point(G.source(e)),G.point(G.target(e))); if ( pK->compare_xy(ps,pt) < 0 ) res = pK->construct_segment(ps,pt); else res = pK->construct_segment(pt,ps); return res; } struct PredCompareCurves { const Geometry* pK; PredCompareCurves() : pK(0) {} PredCompareCurves(const Geometry* pKi) : pK(pKi) {} PredCompareCurves(const PredCompareCurves& P) : pK(P.pK) {} int cmppntseg(const Point& p, const Curve& s) const { if ( pK->compare_x(pK->source(s),pK->target(s)) != 0 ) // !vertical return pK->orientation(pK->source(s),pK->target(s), p); if ( pK->compare_y(p,pK->source(s)) <= 0 ) return -1; if ( pK->compare_y(p,pK->target(s)) >= 0 ) return +1; return 0; } int operator()(const Curve& s1, const Curve& s2) const { Point a = pK->source(s1); Point b = pK->target(s1); Point c = pK->source(s2); Point d = pK->target(s2); if ( a==b ) if ( c==d ) return pK->compare_y(a,c); else return cmppntseg(a, s2); if ( c==d ) return -cmppntseg(c, s1); // now both are non-trivial: int cmpX = pK->compare_x(a, c); if ( cmpX < 0 ) return - pK->orientation(a,b,c); if ( cmpX > 0 ) return pK->orientation(c,d,a); int cmpY = pK->compare_y(a, c); if ( cmpY < 0 ) return -1; if ( cmpY > 0 ) return +1; // cmpX == cmpY == 0 => a == c return pK->orientation(c,d,b); } }; PredCompareCurves getCompareCurves() const { return PredCompareCurves(pK); } @ The generic location type |GenericLocation| is defined in the generic point location framework. The framework allows us to introduce a postprocessing phase after the actual location phase. From the location phase we obtain two locations in two slaps, which are neigbored in the x-structure. The second location |L_plus| is non-nil if the first slap has width zero and is defined by a node at an x-coordinate |x|. Then |L_plus| returns an edge |e| just below $x + \epsilon$. This allows us to extract the face that we search from |e|. <>= typedef GenericLocation Location; typedef Object_handle QueryResult; virtual Object_handle PostProcess(const Location& L, const Location& L_plus, const Point& p) const { /* we only get an L_plus (non-nil) if L is ABOVE a vertex in which case we want to extract the face from the edge below (p+epsilon) available via L_plus. */ if (!L_plus.is_nil()) { assert(L_plus.is_edge()); return Object_handle(Edge(L_plus)); } else { if ( L.is_edge() ) { return Object_handle(Edge(L)); } if ( L.is_node() ) { Node v(L); assert( v != Node() ); return Object_handle(v); } return Object_handle(); } } @ \begin{ignoreindiss} \section{A Test Case} <>= #include #include #include #include "Affine_geometry.h" #include #include #include #include #include #include #include #include #include // KERNEL: typedef CGAL::Homogeneous Hom_kernel; typedef CGAL::Affine_geometry Aff_kernel; typedef Aff_kernel::Segment_2 Segment; // HALFEDGE DATA STRUCTURE: struct HDS_traits { typedef Aff_kernel::Point_2 Point; typedef bool Mark; }; typedef CGAL::HalfedgeDS_default Plane_map; typedef CGAL::PM_const_decorator CDecorator; typedef CGAL::PM_decorator Decorator; typedef CGAL::PM_overlayer PM_aff_overlayer; typedef CDecorator::Halfedge_const_handle Halfedge_const_handle; typedef CDecorator::Vertex_const_handle Vertex_const_handle; typedef CDecorator::Face_const_handle Face_const_handle; typedef Decorator::Halfedge_handle Halfedge_handle; typedef Decorator::Vertex_handle Vertex_handle; // INPUT: typedef std::list::const_iterator Iterator; struct Object_DA { const Decorator& D; Object_DA(const Decorator& Di) : D(Di) {} void supporting_segment(Halfedge_handle e, Iterator it) const { D.mark(e) = true; } void trivial_segment(Vertex_handle v, Iterator it) const { D.mark(v) = true; } void starting_segment(Vertex_handle v, Iterator it) const { D.mark(v) = true; } void passing_segment(Vertex_handle v, Iterator it) const { D.mark(v) = true; } void ending_segment(Vertex_handle v, Iterator it) const { D.mark(v) = true; } }; // POINT LOCATION: typedef CGAL::PM_naive_point_locator PM_naive_PL; typedef CGAL::PM_point_locator PM_triang_PL; typedef PM_naive_PL::Object_handle Object_handle; void print(Object_handle h) { Vertex_const_handle v; Halfedge_const_handle e; Face_const_handle f; if (assign(v,h)) cout << " ohandle = vertex " << PV(v) << endl; if (assign(e,h)) cout << " ohandle = halfedge " << PE(e) << endl; if (assign(f,h)) cout << " ohandle = face " << PE(f->halfedge()) << endl; } // struct INSET { bool operator()(bool b) const { return b; } }; struct INSET { const CDecorator& D; INSET(const CDecorator& Di) : D(Di) {} bool operator()(Vertex_const_handle v) const { return D.mark(v); } bool operator()(Halfedge_const_handle e) const { return D.mark(e); } bool operator()(Face_const_handle f) const { return D.mark(f); } }; int main(int argc, char* argv[]) { SETDTHREAD(17); CGAL::set_pretty_mode(cerr); CGAL::Window_stream W; W.init(-50,50,-50,1); W.display(); W.message("Insert segments to create a map."); Plane_map H; PM_aff_overlayer PMOV(H,Aff_kernel()); CGAL::PM_visualizor V(W,PMOV); std::list L; Segment s; if ( argc == 2 ) { std::ifstream log(argv[1]); while ( log >> s ) { L.push_back(s); W << s; } } while ( W >> s ) L.push_back(s); std::string fname(argv[0]); fname += ".log"; std::ofstream log(fname.c_str()); for (Iterator sit = L.begin(); sit != L.end(); ++sit) log << *sit << " "; log.close(); Object_DA ODA(PMOV); PMOV.create(L.begin(),L.end(),ODA); V.draw_skeleton(); W.message("Insert one segment for point location"); PM_naive_PL PL1(H); PM_triang_PL PL2(H); Object_handle h; W >> s; CGAL::PM_io_parser::dump(PMOV); h = PL1.locate(s); print(h); h = PL2.locate(s.source()); print(h); h = PL1.ray_shoot(s,INSET(PL1)); h = PL2.ray_shoot(s,INSET(PL2)); W.read_mouse(); return 0; } @ \begin{ignore} <>= // ============================================================================ // // 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$ // <>= // package : Nef_2 // chapter : Nef Polyhedra // // source : nef_2d/PM_point_locator_2.lw // revision : $Id$ // revision_date : $Date$ // // author(s) : Michael Seel // maintainer : Michael Seel // coordinator : Michael Seel // // implementation: Point location module // ============================================================================ @ \end{ignore} \end{ignoreindiss} %KILLSTART REP \bibliographystyle{alpha} \bibliography{comp_geo,geo_mod,diss} \newpage \section{Appendix} \input manpages/PMConstDecorator.man \input manpages/PMDecorator.man \input manpages/AffineGeometryTraits_2.man \input manpages/geninfo.man \end{document} %KILLEND