%------------------------------------------------------------------------------ %KILLSTART %LDEL TRACE.*?\)\; \documentclass[a4paper]{article} \usepackage{amsmath,MyLweb,version} \input{defs} \excludeversion{ignorecodeparts} \begin{document} \title{Sphere Map Overlay} \author{Michael Seel} \maketitle \tableofcontents \newpage \section{The manual page} \input{SM_overlayer.man} \newpage \section{Implementation details} %KILLEND In this module we present the implementation of the overlay of sphere segments and sphere maps. We first give a formal introduction to the notions and difficulties concerning hemisphere geometry, overlay and support. We show how we divide the problem into two hemispherical problems along a equator of the sphere. We solve these hemispherical problems similar to the planar case and finally unify the solutions per hemisphere to a complete spherical solution in a stitching process along the equator. Following the above hemispherical ideas we present the overlay calculation of a set of spherical segments. We use a generic sweep module to produce the 1-skeleton of the output sphere map. In a different section we show how to add face objects to the 1-skeleton to complete the output structure. The second operation concerns the overlay of two sphere maps. We use the same generic sweep module with slightly more elaborate adaptation to obtain again the 1-skeleton. The face production phase will be the same as before. In case of the second overlay operation our sweep adds a transfer of information assigned to the objects of the two input sphere maps to corresponding objects in the output structure. This allows us to use the module for binary set operations on sphere map structures. Such set operations use a selection phase on the transfered information items. The selection phase is descibed below in an additional section. The last section in this document concerns structural simplification of the output sphere map. We will see that there can be substructures in the output sphere map that can be simplified without loosing any information when the sphere map is interpreted as a point set. Especially the partitioning process per hemisphere introduces additional objects that can be removed. We use template member methods to introduce flexibility. All situation dependend functionality is wrapped in so called data accessors. \subsection{Notions and definitions} We first have to clarify what kind of spherical geometry we use in the surface of our unit sphere $S_2$. In our kernel we introduce geometric objects that are part of the spherical surface $S_2$ and operations on them. We define types |Sphere_point|, |Sphere_circle|, |Sphere_segment|, |Sphere_direction|, and |Sphere_triangle|. |Sphere_point|s are points on $S_2$, |Sphere_circle|s are oriented great circles of $S_2$, |Sphere_segment|s are oriented parts of |Sphere_circles| bounded by a pair of |Sphere_point|s, and |Sphere_direction|s are directions that are part of great circles (a direction is usually defined to be a vector without length, that floats around in its underlying space and can be used to specify a movement at any point of the underlying space; in our case we use directions only at points that are part of the great circle that underlies also the direction.) Note that we have to consider special geometric properties of the objects. For example two points that are part of a great circle define two |Sphere_segment|s, and two arbitrary |Sphere_segment|s can intersect in two points. The |Sphere_segment| interface account for all the operations that are a result of the above ambiguity. For the overlay procedures we restrict ourselves to hemispherical sets that avoid the ambiguities. If we restrict our geometric objects to a so-called \emph{perfect hemisphere} of $S_2$\footnote{A perfect hemisphere of $S_2$ is an open half-sphere plus an open half-circle in the boundary of the open half-sphere plus one endpoint of the half-circle.} then the restricted objects behave like in classical geometry, e.g., two points define exactly one segment, two segments intersect in at most one interior point (non-degenerately), or three non-cocircular sphere points can be qualified as being positively or negatively oriented. As our objects are only parts of great circles the underlying kernel operations can be realized robustly \cite{schwerdt01}. \begin{figure}[thbp] \begin{center} \epsfig{file=inputs/inputsegments.ps,width=5cm}\quad \epsfig{file=inputs/outputsegmap.ps,width=5cm} \end{center} \caption{\small The overlay of a set of segments. On the left side the input segments have blue endpoints, on the right side the calculated sphere map has yellow vertices.} \label{fig:overlay} \end{figure} \newcommand{\SM}{\ensuremath{M = (V,E,L,F)}} \newcommand{\SMi}{\ensuremath{M_i = (V_i,E_i,L_i,F_i)}} \newcommand{\Mi}{\ensuremath{M_i}} \newcommand{\ST}{\ensuremath{S_2}} If we consider our overlay process as a transformation of input objects to output objects then we can define the support relationsship as follows. \begin{deff}[support] Consider an algorithm $T$ that transforms a set of input objects $A$ to a set of output objects $B$ where each $a \in A$ and $b \in B$ represents a subset of $S_2$. We say that \emph{$a$ supports $b$} if $b$ is a subset of $a$ with respect to the represented point sets. \end{deff} We will anchor this notion in the following. \textbf{Overlay of a set of spherical segments}\quad For a segment $s = (p,q)$, |p = source(s)|, |q = target(s)| and $p$, $q$ are called the endpoints of $s$. Let's consider $s$ as a disjoint union of its endpoints and its relative interior |relint(s)|. A set of segments $S$ partitions the sphere into cells of different dimensions. For each point $r \in \ST$ it can happen that (i) $r$ is equal to some endpoint $p$ of some segment $s$, (ii) $r$ is part of the relative interior of some segment $s$, or (iii) $r$ is not part of any segment at all. Note that (i) and (ii) do not exclude each other. Now consider the geometric structure built by all segments. The \emph{overlay} of all segments is the subdivision of all points in $\ST$ with respect to the three criteria (i) to (iii) above including their topological neighborhood and the knowledge how parts of the segments in $S$ support the cells of the subdivision. We store the overlay of $S$ in a sphere map \SM{} in the standard way. For each point $r$ in (i) there's a vertex $v$ in $V$ where the endpoint of the segment supports the vertex. If $r$ is additionally in the relative interior of some other segment according to (ii) then that segment also supports $v$. For each point in (ii) that is the unique intersection point of the relative interior of two segments (that don't overlap) there's a vertex in $V$ and the relative interior of the two segments support that vertex. Between two vertices in $V$ there is a uedge $e$ in $E$ if there's a segment $s$ that supports the straight line embedding of $e$ according to (ii) and there's no further vertex in the relative interior of $e$. The latter can happen for several segments that overlap. Any point of (iii) belongs to one of the maximal connected sets of $\ST - S$, forming the faces of $M$ and is thus not supported by any segment at all. Note that sphere circle as input objecst introduce objects that can be considered as segments without ends. The notion of support can be easily extended. Spherical maps have therefore objects called loops that that are embed via such circles. \textbf{Overlay of two sphere maps}\quad Let $M_i = (V_i,E_i,L_i,F_i), i=0,1$ be two sphere map structures. The overlay of two sphere maps $M_0$, $M_1$ is the sphere map $M$ representing the subdivision of the sphere obtained by interpreting the skeleton objects of \Mi{} according to their embedding as trivial and non-trivial segments or great circles, constructing the overlay of these objects and adding the faces. To make this structure really helpfull we store the support relationship. \begin{figure}[thbp] \begin{center} \epsfig{file=inputs/nef1.ps,width=4cm}\ \epsfig{file=inputs/nef2.ps,width=4cm}\ \epsfig{file=inputs/nef3.ps,width=4cm} \end{center} \caption{\small The overlay of two spherical maps. On the left side the input polyhedra, on the right side the calculated symmetric difference.} \label{fig:sm_overlay} \end{figure} In general each point in the sphere is supported by that object of a sphere map whose corresponding point set contains it. The support relationsship between \Mi{} and $M$ comes in two steps. Each skeleton object of \Mi{} relates to the endpoint or relative interior of a segment (or circle) that supports a skeleton object in $M$. Reversely each object of $M$ (vertex, edge, loop, or face) is supported by a unique supporting object in each of the two structures \Mi{} $(i=0,1)$. We show that this relationship is well-defined. \begin{lemma} Any object of $M$ has exactly one supporting object in each of the $M_i$. \end{lemma} \begin{proof} Obviously each point of the sphere is supported by an object of $M_i$. Therefore we only have to argue why no two objects of $M_i$ can support an object of $M$. For vertices this is trivial. For a uedge $e$ in $M$ there can be only one uedge or one face of $M_i$ that supports it. Assume the embedding of $e$ covers points from more than one object of $M_i$. Then $e$ either contains a vertex or crosses an edge in its interior. But then the corresponding subdivision would have prevented the creation of $e$ in $M$ in the first place. The same holds for a loop $l$ of $M$. For a face $f$ of $M$ there can be only one face $f'$ of $M_i$ that supports it. Assume otherwise, that $f$ contains points from different objects of $M_i$. As $f$ is an open connected point set, $f$ has to cover points of at least one boundary object from the 1-skeleton of $M_i$. But this object is part of the 1-skeleton of $M$ and can therefore never be part of $f$. \end{proof} In our implementation we determine the support relationship in two phases. Any vertex $v$ in $V$ can be supported by a vertex $v_i$, a uedge $e_i$, a uloop $l_i$, or a face $f_i$ of \Mi. If $v$ is supported by $v_i$, $e_i$, or $l_i$ we obtain this information in the sweep process. Assume $v$ is supported by a face $f_i$ (then $v$ is supported by a vertex $v_{1-i}$). During the sweep process the determination of a support of $f_i$ is hard, as the face objects are not in reach. We determine $f_i$ in a post processing phase by a simple iteration over all vertices. Any edge $e$ in $E$ can be supported by a uedge $e_i$, a uloop $l_i$, or a face $f_i$ of \Mi. A possible support by $e_i$/$l_i$ is handled during the sweep process. In case $e$ is part of a face $f_i$ (again $e$ is then supported by an edge $e_{1-i}$ or $l_{1-i}$) we determine $f_i$ also in the postprocessing phase. For a face $f$ in $F$ assume that each edge $e$ (or loop $l$) in $E$ knows the faces $f_i$ supporting points in a small neighborhood on its left side. Then $f$ can determine its two supporting faces $f_i$ via any edge in its boundary cycle. We will enrich the edges of $E$ by such support information. \subsection{The class design} We start with the design of the class object. Our generic overlay class can be adapted via two interface concepts. We interface the underlying sphere map via a sphere map decorator |Decorator_|. We inherit from |Decorator_| to obtain its interface methods. <>= /*{\Manpage {SM_overlayer}{Decorator_}{Overlay in the sphere}{O}}*/ template class SM_overlayer : public Decorator_ { public: <> protected: <> public: <> <> <> }; // SM_overlayer @ \begin{ignorecodeparts} <>= /*{\Mdefinition An instance |\Mvar| of data type |\Mname| is a decorator object offering sphere map overlay calculation. Overlay is either calculated from two sphere maps or from a set of halfspaces. The result is stored in a sphere map |M| that carries the geometry and the topology of the overlay. The template parameter provides the underlying topological interface to sphere maps. The template parameter |Decorator_| has to be a model conforming to our map decorator concept |SM_decorator|. The concept also describes the interface how the topological information stored in |M| can be extracted or extended. The overlay of a set of sphere segments $S$ is stored in a sphere map $M = (V,E,L,F)$. Vertices are either the endpoints of segments (trivial segments are allowed) or the result of the internal intersection of two segments. Between two vertices there's an edge if there's a segment that supports the spherical embedding of $e$ and if there's no vertex in the relative interior of the embedding of $e$. The faces refer to the maximal connected open point sets of the spherical subdivision implied by the embedding of the vertices and edges. Faces are bounded by possibly several face cycles\footnote{For the definition of sphere maps and their concepts see the manual page of |SM_decorator|.} including isolated vertices. The overlay process in the method |create_from_segments| creates the objects and the topology of the result. The method starts from zero- and one-dimensional geometric objects in $S$ and produces a spherical structure where each point of the sphere can be assigned to an object (vertex, edge, loop, or face) of |M|. The overlay of two sphere maps $M_i = (V_i, E_i, L_i, F_i)$ has the additional aspect that we already start from two spherical subdivisions. We use the index $i=0,1$ defining the reference to $M_i$, unindexed variables refer to the resulting sphere map $M$. The $1$-skeleta of the two maps subdivide the edges, loops, and faces of the complementary structure into smaller units. This means vertices, edges, and loops of $M_i$ can split edges and loops of $M_{1-i}$ and face cycles of $M_i$ subdivide faces of $M_{1-i}$. The 1-skeleton $G$ of $M$ is defined by the overlay of the embedding of the 1-skeleta of $M_0$ and $M_1$ (Take a trivial segment for each vertex and a segment for each edge, and a circle for a loop, and use the overlay definition of a set of segments and loops above). The faces of $M$ refer to the maximal connected open point sets of the spherical subdivision implied by the embedding of $G$. Each object from the output tuple $(V,E,F)$ has a \emph{supporting} object $u_i$ in each of the two input structures. Imagine the two maps to be transparent balls, where one contains the other. Then each point of the sphere is covered by an object from each of the input structures. This support relationship from the input structures to the output structure defines an information flow. Each supporting object $u_i$ of $u$ $(i=0,1)$ carries an associated information $|mark|(u_i)$. After the subdivision operation this information is attributed to the output object $u$ by $|mark|(u,i)$.}*/ <>= typedef Decorator_ Base; typedef typename Base::Sphere_map Sphere_map; typedef SM_overlayer Self; CGAL_USING(Vertex_handle); CGAL_USING(Halfedge_handle); CGAL_USING(Halfloop_handle); CGAL_USING(Face_handle); CGAL_USING(Vertex_iterator); CGAL_USING(Halfedge_iterator); CGAL_USING(Face_iterator); CGAL_USING(Object_handle); CGAL_USING(Halfedge_around_vertex_circulator); CGAL_USING(Halfedge_around_face_circulator); typedef std::pair Halfedge_pair; /*{\Mtypes 3}*/ typedef Base SM_decorator; /*{\Mtypemember the sphere map decorator.}*/ typedef typename Base::Kernel Kernel; /*{\Mtypemember the geometry kernel.}*/ typedef typename Kernel::Sphere_point Sphere_point; /*{\Mtypemember the point type of the sphere geometry.}*/ typedef typename Kernel::Sphere_segment Sphere_segment; /*{\Mtypemember the segment type of the sphere geometry.}*/ typedef typename Kernel::Sphere_circle Sphere_circle; /*{\Mtypemember the circle type of the sphere geometry.}*/ typedef typename SM_decorator::Mark Mark; /*{\Mtypemember the mark of sphere map objects.}*/ /*{\Mgeneralization SM_decorator}*/ <>= /*{\Mcreation 6}*/ SM_overlayer(SM_decorator D, const Kernel& G = Kernel()) : Base(D), K(G) {} /*{\Mcreate |\Mvar| is a decorator object manipulating the map decorated by |D|.}*/ /*{\Moperations 1.1 1}*/ template void subdivide_segments(Iterator start, Iterator end) const; template void partition_to_halfsphere(Iterator start, Iterator end, Seg_list& L, CGAL::Unique_hash_map& M, int pos) const; template void merge_halfsphere_maps(Vertex_handle v1, Vertex_handle v2, const Mark_accessor& D) const; template void merge_nodes(Halfedge_handle e1, Halfedge_handle e2, const Mark_accessor& D) const; template void create_face_objects(Halfedge_iterator e_start, Halfedge_iterator e_end, Vertex_iterator v_start, Vertex_iterator v_end, const Below_accessor& D, const Halfsphere_geometry& SG) const; template void complete_face_support(Vertex_iterator v_start, Vertex_iterator v_end, const Below_accessor& D, int pos) const; void dump(std::ostream& os = std::cerr) const { SM_io_parser::dump(*this,os); } <>= SM_decorator PI[2]; const Kernel& K; <>= template void create_from_segments( Forward_iterator start, Forward_iterator end) const; /*{\Mop produces the sphere map which is the overlay of the segments from the iterator range |[start,end)|. \precond |Forward_iterator| has value type |Sphere_segment|.}*/ template void create_from_circles( Forward_iterator start, Forward_iterator end) const; /*{\Mop produces the sphere map which is the overlay of the circles from the iterator range |[start,end)|. \precond |Forward_iterator| has value type |Sphere_circle|.}*/ void create(const Sphere_circle& c) const; /*{\Mop produces the sphere map which consists of one loop and the two halfspheres incident to it.}*/ void subdivide(const Sphere_map& M0, const Sphere_map& M1); /*{\Mop constructs the overlay of the sphere maps |M0| and |M1| in |M|, where all objects (vertices, halfedges, faces) of |M| are \emph{enriched} by the marks of the supporting objects of the two input structures: e.g. let |v| be a vertex supported by a node |v0| in |M0| and by a face |f1| in |M1| and |D0|, |D1| be decorators of type |SM_decorator| on |M0|,|M1|. Then |\Mvar.mark(v,0) = D0.mark(v0)| and |\Mvar.mark(v,1) = D1.mark(f1)|.}*/ template void select(const Selection& SP) const; /*{\Mop sets the marks of all objects according to the selection predicate |SP|. |Selection| has to be a function object type with a function operator\\ [[Mark operator()(Mark m0, Mark m1) const]]\\ For each object |u| of |M| enriched by the marks of the supporting objects according to the previous procedure |subdivide|, after this operation |\Mvar.mark(u) = SP ( \Mvar.mark(u,0),\Mvar.mark(u,1) )|. The additional marks are invalidated afterwards. }*/ void simplify() const; /*{\Mop simplifies the structure of |M| according to the marks of its objects. An edge |e| separating two faces |f1| and |f2| and equal marks |mark(e) == mark(f1) == mark(f2)| is removed and the faces are unified. An isolated vertex |v| in a face |f| with |mark(v)==mark(f)| is removed. A vertex |v| with outdegree two, two collinear out-edges |e1|,|e2| and equal marks |mark(v) == mark(e1) == mark(e2)| is removed and the edges are unified.}*/ @ \end{ignorecodeparts} @ \subsection{Overlay calculation of a list of segments} We want to calculate the sphere map $M$ representing the overlay of a set of segments. For the overlay action we use the generic segment sweep module as presented in the technical report \cite{TR:nefimplementation}. In that report we have presented a generic traits model |Segment_overlay_traits| for the generic sweep framework. To instantiate this model we have to provide the three components (input, output, geometry). In this instance the input is an iterator pair, the geometry is defined per perfect hemisphere and defined in the spherical geometry kernel. Only for the output type we have to work a little more. We define a class |SMO_from_segs| below triggering the correct update operations on the output sphere map during the sweep. |SMO_from_segs| is a template class on global scope as several compilers don't like class scope type dependency. The first part of |SMO_from_segs| takes care of the sphere map extension by new vertices and edges. The second part forwards the treatment of new objects like vertices and edges by means of a data accessor. At this point you can take the module\\ $|generic_sweep< Segment_overlay_traits... >>|$\\ used below as a black box producing the $1$-skeleton of $M$ with the properties described in section \ref{creating face objects}. The specification of |Segment_overlay_traits| guarantees the properties of $M$ as the following decorator model fits the requirements defined there. <>= template struct SMO_from_segs { typedef Decorator_ SM_decorator; typedef typename Decorator_::Vertex_handle Vertex_handle; typedef typename Decorator_::Halfedge_handle Halfedge_handle; typedef typename Decorator_::Sphere_point Point; typedef typename Decorator_::Sphere_segment Segment; typedef CGAL::Unique_hash_map Iterator_map; SM_decorator G; const Iterator_map& M; SMO_from_segs(Decorator_ Gi, const Iterator_map& Mi) : G(Gi),M(Mi) {} <> <> <> }; // SMO_from_segs @ The creation of new objects is forwarded to the |SM_decorator| object |G|. The methods of |G| are members as described in the |SM_decorator| concept. The following methods are called during the sweep at its event points. The first three methods trigger object creation in $M$. We associate a |Halfedge_handle| to each vertex |v| via its generic storage slot $|GenPtr& info(v)|$. We use a scheme in analogy to LEDA \cite[chapter 13]{ledabook}, where information (in form of a built-in or class type) is stored directly in the pointer if it has size not larger than the size of a standard word. If it does not fit, the pointer is used to reference a newly allocated information object on the heap. The scheme is bundled in a class called |geninfo|. For more information see that manual page in the appendix. <>= Vertex_handle new_vertex(const Point& p) { Vertex_handle v = G.new_vertex(p); geninfo::create(G.info(v)); return v; } void link_as_target_and_append(Vertex_handle v, Halfedge_handle e) { G.link_as_target_and_append(v,e); } Halfedge_handle new_halfedge_pair_at_source(Vertex_handle v) { Halfedge_handle e = G.new_edge_pair_at_source(v,SM_decorator::BEFORE); return e; } @ The marks of new objects are depending on the value of |M|. <>= void supporting_segment(Halfedge_handle e, I it) const { if ( M[it] ) G.mark(e) = true; } void trivial_segment(Vertex_handle v, I it) const { if ( M[it] ) G.mark(v) = true; } void starting_segment(Vertex_handle v, I it) const { if ( M[it] ) G.mark(v) = true; } void passing_segment(Vertex_handle v, I it) const { if ( M[it] ) G.mark(v) = true; } void ending_segment(Vertex_handle v, I it) const { if ( M[it] ) G.mark(v) = true; } void halfedge_below(Vertex_handle v, Halfedge_handle e) const { geninfo::access(G.info(v)) = e; } @ |SMO_from_segs| fits also the concept for the face creation data access defined in section \ref{creating face objects}. <>= Halfedge_handle halfedge_below(Vertex_handle v) const { return geninfo::access(G.info(v)); } @ We finally add a clean-up operation discarding the temporary storage. <>= void assert_equal_marks(Vertex_handle v1, Vertex_handle v2) const { CGAL_assertion(G.mark(v1)==G.mark(v2)); } void discard_info(Vertex_handle v) const { geninfo::clear(G.info(v)); } void assert_equal_marks(Halfedge_handle e1, Halfedge_handle e2) const { CGAL_assertion(G.mark(e1)==G.mark(e2)); } void transfer_marks(Halfedge_handle e) const { G.unify_tmp_marks(e); } void discard_info(Halfedge_handle e) const {} void clear_temporary_vertex_info() const { Vertex_handle v; for(v = G.vertices_begin(); v != G.vertices_end(); ++v) geninfo::clear(G.info(v)); } @ Now the overlay creation is trivial. We partion the segments per perfect hemisphere in a first phase. We then create the overlay per perfec hemisphere. Finally we stich the two hemispherical maps together at a equator to obtain one complete sphere map. <>= template template void SM_overlayer:: create_from_segments(Forward_iterator start, Forward_iterator end) const { TRACEN("creating from segment iterator range"); <> <> <> O.clear_temporary_vertex_info(); } @ The partitioning is delegated to the subroutine |partition_to_halfsphere|. The global list $L$ stores all segments, the lists |L_pos| and |L_neg| store geometric object that are contained in the corresponding hemisphere. The hash map |From_input| marks input objects as we will introduce additional geometric objects to bound the hemispheres. The last parameter of |partition_to_halfsphere| selects to which of the complementary hemispheres the input object have to be restricted. <>= Seg_list L(start,end); Unique_hash_map From_input(false); Seg_iterator it; CGAL_forall_iterators(it,L) From_input[it]=true; Seg_list L_pos,L_neg; partition_to_halfsphere(L.begin(), L.end(), L_pos, From_input, +1); partition_to_halfsphere(L.begin(), L.end(), L_neg, From_input, -1); @ For the sweep we just create an output decorator object |O| working on the sphere map maintained by |SM_overlayer| and plug it into the segment sweep overlay framework |Segment_overlay_traits|. The properties of the output sphere map are defined by that algorithmic module. The used geometry is different per hemisphere but predefined in the geometric kernel |K| as referenced by |SM_overlayer|. Note that the information |halfedge_below| information collected during the sweep is associated to the vertices of the output map. Note also that the class |SMO_from_segs| in this chunk fits two concepts simultaneously. The corresponding object |Out| triggers the output of the sweep and provides the halfedge-below information for the face creation in |create_face_objects()|. The two hemispherical sweeps create two hemispherical maps that have a common equator spanned by one face cycle of at least four edges. The objects that are created per sweep are continuosly maintained in the global vertex and edge lists of the sphere map. We store the vertex |v| and edge |e| where the two object lists can be partitioned per hemisphere. <>= typedef SMO_from_segs SM_output; typedef typename Kernel::Positive_halfsphere_geometry PH_geometry; typedef CGAL::Segment_overlay_traits< Seg_iterator, SM_output, PH_geometry> PHS_traits; typedef CGAL::generic_sweep Positive_halfsphere_sweep; typedef typename Kernel::Negative_halfsphere_geometry NH_geometry; typedef CGAL::Segment_overlay_traits< Seg_iterator, SM_output, NH_geometry> NHS_traits; typedef CGAL::generic_sweep Negative_halfsphere_sweep; Vertex_iterator v; Halfedge_iterator e; SM_output O(*this,From_input); typedef typename PHS_traits::INPUT Input_range; Positive_halfsphere_sweep SP( Input_range(L_pos.begin(),L_pos.end()),O, K.get_positive_halfsphere_geometry()); SP.sweep(); //TRACEN("POS SWEEP\n"<<(dump(std::cerr),"")); v=--vertices_end(); e=--halfedges_end(); Negative_halfsphere_sweep SM( Input_range(L_neg.begin(),L_neg.end()),O, K.get_negative_halfsphere_geometry()); SM.sweep(); //TRACEN("NEG SWEEP\n"<<(dump(std::cerr),"")); ++v; ++e; // now two CCs of sphere graph are calculated // v = first vertex of CC in negative x-sphere // e = first edge of CC in negative x-sphere @ We now create the face objects per hemisphere. <>= create_face_objects(halfedges_begin(), e, vertices_begin(), v, O, K.get_positive_halfsphere_geometry()); create_face_objects(e, halfedges_end(), v, vertices_end(), O, K.get_negative_halfsphere_geometry()); @ Still missing are the sphere circles that support the edges of the sphere map. Note that due to our restriction to perfect hemispheres the circles can be easily constructed per edge. <>= Halfedge_iterator u; CGAL_forall_edges(u,*this) { Sphere_segment s(point(source(u)),point(target(u))); circle(u) = s.sphere_circle(); circle(twin(u)) = s.sphere_circle().opposite(); } @ \subsection{Overlay calculation of a list of circles} We now modify the above coding scheme to calculate the sphere map $M$ representing the overlay of a set of great circles. We basically only recode the first phase. <>= template template void SM_overlayer:: create_from_circles(Forward_iterator start, Forward_iterator end) const { TRACEN("creating from circle iterator range"); <> <> <> O.clear_temporary_vertex_info(); } @ We just split great circles to halfcircles that are contained in each of the two hemispheres. Note that this realization contains some redundancy as the |partition_to_halfsphere| again analyzes the geometry of each segment. One could find a more efficient approach. <>= Seg_list L; Unique_hash_map From_input(false); for ( ; start != end; ++start ) { std::pair spair = start->split_at_xy_plane(); L.push_back(spair.first); L.push_back(spair.second); } Seg_iterator it; CGAL_forall_iterators(it,L) From_input[it]=true; Seg_list L_pos,L_neg; partition_to_halfsphere(L.begin(), L.end(), L_pos, From_input, +1); partition_to_halfsphere(L.begin(), L.end(), L_neg, From_input, -1); @ \subsection{Create a sphere map from a great circle} We create a map consisting of a single loop and two faces representing two complementary half-spheres. <>= template void SM_overlayer:: create(const Sphere_circle& c) const { Halfloop_handle l1 = new_loop_pair(); Halfloop_handle l2 = twin(l1); circle(l1) = c; circle(l2) = c.opposite(); Face_handle f1 = new_face(); Face_handle f2 = new_face(); link_as_loop(l1,f1); link_as_loop(l2,f2); } @ \subsection{Overlay calculation of two sphere maps} \label{Overlay calculation of two sphere maps} We calculate the overlay $M$ of two sphere maps $M_0$ and $M_1$. Both input structures are two correctly defined sphere maps including topology, geometry, markers. In the following we use the index $i=0,1$ showing a reference to $M_i = (V_i,E_i,L_i,F_i)$, unindexed variables refer to $M$. The $1$-skeleta of the two maps $M_0$ and $M_1$ subdivide the edges, loops, and faces of the complementary structures into smaller units. This means vertices, edges, and loops of $M_i$ can split edges and loops of $M_{1-i}$ and face cycles of $M_i$ subdivide faces of $M_{1-i}$. The 1-skeleton $G$ of $M$ is defined by the overlay of the embedding of the 1-skeleton of $M_0$ and $M_1$ (Take a trivial segment for each vertex and a segment for each edge and a circle for a loop and use the overlay definition of a set of segments above). Additionally we require that $M$ has the correct order in each adjacency list such that it is order-preserving regarding the embedding of the vertices. Finally the faces of $M$ refer to the maximal connected open point sets of the spherical subdivision implied by the embedding of $G$. The construction of the faces $F$ from $G$ is described in section \ref{creating face objects}. Each object $u$ from the output tuple $(V,E,L,F)$ has a \emph{supporting} object $u_i, i=0,1$ in each of the two input structures. Imagine the two maps to be transparent balls, stacked one on top of the other. Then each point of the sphere is covered by an object from each of the input structures. We analyse the support relationship from input to output in order to transfer the attributes from $u_i$ to $u$. According to our specification each object $u_i$ of a sphere map carries an associated information $|mark|(u_i)$. Thus we associate this information to the output object $u$ by $|mark|(u,i)$. This two tuple of information can then be processed by some combining operation to a single value |mark(u)| later on. We fix the following properties for our input structures $M_i$. Both sphere maps $(V_i, E_i, L_i, F_i)$ consist of vertices, edges, loops, and faces whose topology is accessible by our sphere map interface. Additionally each such object $u$ carries an information item |mark(u)|. The sphere maps have an \emph{order-preserving} embedding. Actually we do not use this property of the input sphere maps at this point but it's a general invariant of our sphere map structures that makes some intermediate actions more efficient. The overlay process consist of several phases. We determine the segments that are the embedding of the edges and partition these segments into two hemispheres. We also subdivide any loop into two halfcircle segments. We then proceed per halfsphere: We produce the 1-skeleton by a spherical sweep action. Afterwards we create the face objects. Then we analyse the support relationship and transfer the marks of the input objects to the output objects. Finally we unify the two maps along the equator that is the common boundary of both hemispheres. We use our generic segment overlay framework to calculate the overlay of a set of sphere segments $S$. The set $S$ consists of all segments that are the embedding of edges in $E_i$ and additionally trivial segments representing all isolated vertices in $V_i$ and two half-circle segments per loop. The output structure $M' = (V', E')$ of the sweep phase is just the 1-skeleton of the output sphere map $M$\footnote{There are no loops in the hemispherically subdivided represention of the skeleton $M'$.}. But of course including an order-preserving embedding. The objects of the $1$-skeleton carry additional structural information: \begin{enumerate} \renewcommand{\labelenumi}{I\theenumi.} \item Each vertex $v$ in $V'$ knows a halfedge $e \in E' : e = |halfedge_below(v)|$ which is determined by the property that a vertical ray shoot from |v| along the negative $y$-axis hits |e| first. Degeneracies are broken with an implicit perturbation scheme: during the ray shooting all edges include their source vertex. \label{halfedge below} \item For each object $u \in V' \cup E'$ there's a mapping to the supporting 1-skeleton objects from the input structures. The support information is incomplete with respect to face support. \label{skeleton support} \end{enumerate} The next phase after the sweep has to complete the sphere map $M$. We basically have to create the face objects and construct their incidence structure. The face creation is done as presented in section \ref{creating face objects} and uses only I\ref{halfedge below}. Afterwards the transfer of marks uses the embedding of the vertex list of $M$ and the additional information I\ref{halfedge below} and I\ref{skeleton support} to define $|mark|(u,i)$ for all objects $u$ in $p$. Finally the two hemispherically separate sphere map structure are then unified along an equator. <>= template void SM_overlayer:: subdivide(const Sphere_map& M0, const Sphere_map& M1) { PI[0] = SM_decorator(const_cast(M0)); PI[1] = SM_decorator(const_cast(M1)); <> <> <> <> } @ \subsection*{Temporary information associated to objects} The usage of generic programming modules implies that we have to provide temporary information containers for the accumulated information. First we need a container to bind segments used in the sweep phase to objects of the input structures. Non-trivial segments know an edge or loop, trivial segments know a vertex, and all segments know the index of the input structure which contains the object. <>= struct Seg_info { // to transport information from input to output Object_handle _o; int _from; Seg_info() : _o(), _from(-1) {} Seg_info(Vertex_handle v, int i) { _o=Object_handle(v); _from=i; } Seg_info(Halfedge_handle e, int i) { _o=Object_handle(e); _from=i; } Seg_info(Halfloop_handle l, int i) { _o=Object_handle(l); _from=i; } Seg_info(const Seg_info& si) { _o=si._o; _from=si._from; } Seg_info& operator=(const Seg_info& si) { _o=si._o; _from=si._from; return *this; } LEDA_MEMORY(Seg_info) }; typedef std::list Seg_list; typedef typename Seg_list::iterator Seg_iterator; typedef std::pair Seg_it_pair; typedef std::pair Seg_pair; typedef CGAL::Unique_hash_map Seg_map; @ In the first phase we fill the segment input list with a non-trivial segment underlying each edge and with a trivial segment for each isolated vertex of the two input structures. If the input map has a loop we take the underlying great circle split it into two segments and proceed as in case of edges. Additionally we store hashed links from the iterators to the edges/loops/vertices to store where they come from. <>= Seg_list L; Seg_map From; for (int i=0; i<2; ++i) { Vertex_iterator v; CGAL_forall_vertices(v,PI[i]) { if ( !PI[i].is_isolated(v) ) continue; L.push_back(trivial_segment(PI[i],v)); From[--L.end()] = Seg_info(v,i); } Halfedge_iterator e; CGAL_forall_edges(e,PI[i]) { if ( source(e) == target(e) ) { Seg_pair p = two_segments(PI[i],e); L.push_back(p.first); L.push_back(p.second); From[--L.end()] = From[--(--L.end())] = Seg_info(e,i); } else { L.push_back(segment(PI[i],e)); From[--L.end()] = Seg_info(e,i); } } if ( PI[i].has_loop() ) { Seg_pair p = two_segments(PI[i],PI[i].halfloop()); L.push_back(p.first); L.push_back(p.second); From[--L.end()] = From[--(--L.end())] = Seg_info(PI[i].halfloop(),i); } } @ We split segments into parts that are fully contained in the closed hemispheres above or below the xy-plane. By taking the closed hemispheres we ensure that the topology on the boundary is the same and unifying both halfsphere map structures is easy. Note that such closed hemispheres contradict carry geometric ambiguity for segments that are defined by two opposite points on the equator. <>= Seg_list L_pos,L_neg; partition_to_halfsphere(L.begin(), L.end(), L_pos, From, +1); partition_to_halfsphere(L.begin(), L.end(), L_neg, From, -1); @ Conside the standard Cartesian $xyz$-coordinate system. The points where the $y$-axis pierces $\ST$ are denoted $y^-$ and $y^+$. The great circle that is the intersection of $\ST$ and the $xy$-plane is called \emph{equator}. Our hemispheres are bounded by the xy-coordinate plane ($z=0$). The \emph{positive perfect hemisphere} is defined to be the $z$-positive open hemisphere\footnote{$S_2 \cap \{z>0\}$} unified with the open $x$-negative half-equator and $y^-$. The \emph{negative perfect hemisphere} is the complementary set. We start from a set of spherical segments. We determine the connected subcomponents that are part of the closed half-spaces above and below the xy-plane depending on the |pos| parameter. Segments that are totally part of the equator are handled differently. We split such segments that additionally contain the y-axis in their relative interior at those points. Our perfect hemispheres avoid ambiguities with respect to the definition of spherical segments. A semicircle that has its endpoints on the equator is not fully part of a perfect hemisphere. Therefore we algorithmically consider points on the $x$-positive half-equator as slightly perturbed up. To generally avoid problems with semicircles we split such segments again at their midpoint. This increases the complexety of the sphere map structure by a constant factor but makes support determination easier, as for each sphere segment we can find an endpoint in the interior of the perfect hemisphere. <>= template template void SM_overlayer:: partition_to_halfsphere(Iterator start, Iterator beyond, Seg_list& L, CGAL::Unique_hash_map& M, int pos) const { TRACEN("partition_to_halfsphere "); CGAL_assertion(pos!=0); Sphere_segment s1,s2; Sphere_circle xycircle(0,0,pos); while ( start != beyond ) { int i = start->intersection(xycircle,s1,s2); if (i>1) { L.push_back(s2); M[--L.end()] = M[start]; } if (i>0) { L.push_back(s1); M[--L.end()] = M[start]; } ++start; } // now all segments are split into hemispheres // we still have to: // - split segments containing our special poles y^-, y^+ // - split halfcircles // - add four equator segments Sphere_point S(0,-1,0),N(0,1,0); Sphere_circle yzcircle(1,0,0); typename Seg_list::iterator it, itl; CGAL_forall_iterators(it,L) { TRACEN(" "<<*it); if ( equal_as_sets(it->sphere_circle(),xycircle) ) { TRACEN(" splitting xy seg "<<*it); int n1 = it->intersection(yzcircle,s1,s2); if (n1 > 1 && !s2.is_degenerate()) { M[ L.insert(it,s2) ] = M[it]; } if (n1 > 0 && !s1.is_degenerate()) { M[ L.insert(it,s1) ] = M[it]; } int n2 = it->intersection(yzcircle.opposite(),s1,s2); if (n2 > 1 && !s2.is_degenerate()) { M[ L.insert(it,s2) ] = M[it]; } if (n2 > 0 && !s1.is_degenerate()) { M[ L.insert(it,s1) ] = M[it]; } itl = it; --it; L.erase(itl); M[itl] = T(); // at least one item was appended } } CGAL_forall_iterators(it,L) { if ( it->is_halfcircle() ) { TRACEN(" splitting halfcircle "<<*it); Sphere_segment s1,s2; it->split_halfcircle(s1,s2); *it = s2; M[ L.insert(it,s1) ] = M[it]; } } // append 4 xy-equator segments: Sphere_segment sp(S,N,xycircle); Sphere_segment sm(S,N,xycircle.opposite()); Sphere_segment s[4]; sp.split_halfcircle(s[0],s[1]); sm.split_halfcircle(s[2],s[3]); L.insert(L.end(),s,s+4); } @ For the sweep phase we need to associate some additional information attributes to the objects of the output sphere map. We use a trick via generic pointers |GenPtr| (equals |void*|). Each object |u| of |M| has such a slot accessible via $|GenPtr& info(u)|$. We use the pointer to reference an object storing the temporary information until the postprocessing does not need it anymore. We have to ensure that we do not mess around with the memory. The temporary information is collected during the sweep operation. After the selection operation the temporary information is discarded. To avoid superflous indirection for the temporary information we use a scheme in analogy to LEDA \cite[chapter 13]{ledabook}, where information (in form of a built-in or class type) is stored directly in the pointer if it has size not larger than the size of a standard word. If it does not fit, the pointer is used to reference a newly allocated information object on the heap. This scheme leaves the vertex, halfedge, and face objects minimal in the sense that we only have one additional pointer in each of them. The scheme is bundled in a class called |geninfo|. For more information see that manual page in the appendix. We associate the following class to a vertex $v$. The methods below are added to the interface of |SM_overlayer|. |vertex_info| can store the possible supporting skeleton objects in a generic handle |o_supp|, marks |m| corresponding to the two supporting objects and a halfedge |e_below| of the output structure that is vertically below $v$. <>= struct vertex_info { Mark m[2]; Object_handle o_supp[2]; Halfedge_handle e_below; vertex_info() { o_supp[0]=o_supp[1]=Object_handle(); } LEDA_MEMORY(vertex_info) }; void assoc_info(Vertex_handle v) const { geninfo::create(info(v)); } void discard_info(Vertex_handle v) const { geninfo::clear(info(v)); } vertex_info& ginfo(Vertex_handle v) const { return geninfo::access(info(v)); } Mark& mark(Vertex_handle v, int i) const { return ginfo(v).m[i]; } Object_handle& supp_object(Vertex_handle v, int i) const { return ginfo(v).o_supp[i]; } Halfedge_handle& halfedge_below(Vertex_handle v) const { return ginfo(v).e_below; } @ For each halfedge we store the following class. Again we provide its interface in |SM_overlayer|. The |edge_info| objects store information common to both halfedge twins (information for the uedge) and information only concerning one halfedge. In the first case we store the information only in the halfedge determined by the smaller memory address which makes access unique. We provide storage |o_supp| for the possible two input edges or input loops supporting an output edge, the marks |m| of the two input objects supporting an output edge, and temporary storage |mf| for the marks of the two input faces supporting points in a small neighborhood of the edge. The boolean flag |forw| just caches the geometric property if an edge is forward oriented (the source is lexicographically smaller than the target). <>= struct edge_info { Mark m[2]; Mark mf[2]; Object_handle o_supp[2]; bool forw; edge_info() { m[0]=m[1]=mf[0]=mf[1]=Mark(); o_supp[0]=o_supp[1]=Object_handle(); forw=false; } LEDA_MEMORY(edge_info) }; void assoc_info(Halfedge_handle e) const { geninfo::create(info(e)); geninfo::create(info(twin(e))); } void discard_info(Halfedge_handle e) const { geninfo::clear(info(e)); geninfo::clear(info(twin(e))); } edge_info& ginfo(Halfedge_handle e) const { return geninfo::access(info(e)); } Mark& mark(Halfedge_handle e, int i) const // uedge information we store in the smaller one { if (&*e < &*(twin(e))) return ginfo(e).m[i]; else return ginfo(twin(e)).m[i]; } Object_handle& supp_object(Halfedge_handle e, int i) const // uedge information we store in the smaller one { if (&*e < &*(twin(e))) return ginfo(e).o_supp[i]; else return ginfo(twin(e)).o_supp[i]; } Mark& incident_mark(Halfedge_handle e, int i) const // biedge information we store in the edge { return ginfo(e).mf[i]; } bool& is_forward(Halfedge_handle e) const // biedge information we store in the edge { return ginfo(e).forw; } @ A face just obtains two mark slots |m|. <>= struct face_info { Mark m[2]; face_info() { m[0]=m[1]=Mark(); } LEDA_MEMORY(face_info) }; void assoc_info(Face_handle f) const { geninfo::create(info(f)); } void discard_info(Face_handle f) const { geninfo::clear(info(f)); } face_info& ginfo(Face_handle f) const { return geninfo::access(info(f)); } Mark& mark(Face_handle f, int i) const { return ginfo(f).m[i]; } @ \subsection*{The sweep instantiation} We have to provide the three components (input, output, geometry) necessary to instantiate the traits model |Segment_\-overlay_\-traits| for our generic sweep framework. The input is an iterator pair, the geometry is forwarded from the current class scope. Only for the output type we have to work a little more. We define a class |SMO_from_sm| below which allows us to track the support relationship from input objects (segments handled via iterators) to the output objects (vertices and halfedges) via the call-back methods triggered during the sweep. Please refer to the description of |Segment_\-overlay_\-traits| in \cite{TR:nefimplementation}. The member methods of |SMO_from_sm| fit the output concept requirements of |Segment_\-overlay_\-traits|. The functionality is such that the skeleton is created and the support information is associated to the newly created objects. |SMO_from_sm| is a class template on the global implementation scope as the usage of local class types (within the scope of |SM_overlayer|) is not allowed by some current C++ compilers. Note that we forward a reference to our hash map |From| to the output decorator object. Thus we can update the support linkage from output skeleton objects via iterators to input objects on the fly when the sweep frameworks calls the corresponding methods of |SMO_from_sm|. <>= template struct SMO_from_sm { <> SM_overlayer G; SM_decorator* pGI; CGAL::Unique_hash_map& M; SMO_from_sm(SM_overlayer Gi, SM_decorator* pGIi, CGAL::Unique_hash_map& Mi) : G(Gi), pGI(pGIi), M(Mi) {} <> <> <> <> <> }; // SMO_from_sm @ \begin{ignorecodeparts} <>= typedef Decorator_ SM_overlayer; typedef typename Decorator_::SM_decorator SM_decorator; typedef typename SM_decorator::Vertex_handle Vertex_handle; typedef typename SM_decorator::Halfedge_handle Halfedge_handle; typedef typename SM_decorator::Sphere_point Point; typedef typename SM_decorator::Sphere_segment Segment; @ \end{ignorecodeparts} <>= void assert_equal_marks(Vertex_handle v1, Vertex_handle v2) const { TRACEV(G.mark(v1,0));TRACEV(G.mark(v1,1)); TRACEV(G.mark(v2,0));TRACEV(G.mark(v2,1)); CGAL_assertion(G.mark(v1,0)==G.mark(v2,0)&& G.mark(v1,1)==G.mark(v2,1)); } void discard_info(Vertex_handle v) const { G.discard_info(v); } void assert_equal_marks(Halfedge_handle e1, Halfedge_handle e2) const { CGAL_assertion(G.mark(e1,0)==G.mark(e2,0) && G.mark(e1,1)==G.mark(e2,1)); } void transfer_marks(Halfedge_handle e) const { Halfedge_handle et = G.twin(e); if (&*e < &*et) std::swap(e,et); for(int i=0; i<2; ++i) G.ginfo(e).m[i] = G.ginfo(et).m[i]; } void discard_info(Halfedge_handle e) const { G.discard_info(e); } @ New vertices and halfedges are created in the sphere map via a call to corresponding creation methods of the decorator. Note that we initialize a temporary storage slot in the objects by a call to |assoc_info|. <>= Vertex_handle new_vertex(const Point& p) const { Vertex_handle v = G.new_vertex(p); G.assoc_info(v); return v; } void link_as_target_and_append(Vertex_handle v, Halfedge_handle e) const { G.link_as_target_and_append(v,e); } Halfedge_handle new_halfedge_pair_at_source(Vertex_handle v) const { Halfedge_handle e = G.new_edge_pair_at_source(v,SM_decorator::BEFORE); G.assoc_info(e); return e; } @ The halfedge vertically below a vertex is stored in a slot of the temporarily associated information container of type |vertex_info|. Access is done via the |SM_overlayer| operation |halfedge_below()|. <>= void halfedge_below(Vertex_handle v, Halfedge_handle e) const { G.halfedge_below(v) = e; } @ For a new halfedge |e| we get to know all segments |*it| that support it. We store the information via the decorator. There can be at most two input segments supporting an edge. <>= void supporting_segment(Halfedge_handle e, IT it) const { INFO& si = M[it]; G.is_forward(e) = true; if ( si._from == -1 ) return; // equatorial segment G.supp_object(e,si._from) = si._o; TRACEN(" supporting "<>= void trivial_segment(Vertex_handle v, IT it) const { INFO& si = M[it]; CGAL_assertion( si._o != NULL ); G.supp_object(v,si._from) = si._o; } void starting_segment(Vertex_handle v, IT it) const { INFO& si = M[it]; if ( si._from == -1 ) return; G.supp_object(v,si._from) = si._o; } void ending_segment(Vertex_handle v, IT it) const { INFO& si = M[it]; if ( si._from == -1 ) return; G.supp_object(v,si._from) = si._o; } void passing_segment(Vertex_handle v, IT it) const { INFO& si = M[it]; if ( si._from == -1 ) return; G.supp_object(v,si._from) = si._o; } @ |SMO_from_sm| also provides data access in the face creation phase. Therefore this operation that redirects the access to |SM_overlayer| object |G|. <>= Halfedge_handle halfedge_below(Vertex_handle v) const { return G.halfedge_below(v); } @ Now executing the sweep is a trivial plugging of types into the generic sweep framework, a creation of the sweep object with input, output and geometry references, and a final execution the sweep. We do this per hemisphere. <>= typedef SMO_from_sm SM_output; typedef typename Kernel::Positive_halfsphere_geometry PH_geometry; typedef CGAL::Segment_overlay_traits< Seg_iterator, SM_output, PH_geometry> PHS_traits; typedef CGAL::generic_sweep Positive_halfsphere_sweep; typedef typename Kernel::Negative_halfsphere_geometry NH_geometry; typedef CGAL::Segment_overlay_traits< Seg_iterator, SM_output, NH_geometry> NHS_traits; typedef CGAL::generic_sweep Negative_halfsphere_sweep; Vertex_handle v; Halfedge_handle e; SM_output O(*this,PI,From); typedef typename PHS_traits::INPUT Input_range; Positive_halfsphere_sweep SP( Input_range(L_pos.begin(),L_pos.end()),O, K.get_positive_halfsphere_geometry()); SP.sweep(); v=--vertices_end(); e=--halfedges_end(); Negative_halfsphere_sweep SM( Input_range(L_neg.begin(),L_neg.end()),O, K.get_negative_halfsphere_geometry()); SM.sweep(); ++v; ++e; // now two CCs of sphere graph are calculated // v = first vertex of CC in negative x-sphere // e = first edge of CC in negative x-sphere create_face_objects(halfedges_begin(), e, vertices_begin(), v, O, PH_geometry()); create_face_objects(e, halfedges_end(), v, vertices_end(), O, NH_geometry()); Halfedge_iterator u; CGAL_forall_edges(u,*this) { Sphere_segment s(point(source(u)),point(target(u))); circle(u) = s.sphere_circle(); circle(twin(u)) = s.sphere_circle().opposite(); } @ \subsection{Creating face objects}\label{creating face objects} Input to this section is the 1-skeleton of a sphere map $M' = (V',E')$ whose embedding is \emph{order-preserving}. The objective of this section is to create face objects. The correct output structure $M = (V,E,L,F)$ of this section is a sphere map with the additional property that there are face objects $f$ in $F$ corresponding to maximal connected point sets which are a result of the partitioning of the sphere by the $1$-skeleton $M'$. All faces are defined via their bounding face cycles and each face object has links to them (a representing halfedge for each non-trivial face cycle and all isolated vertices in their interior). To create the face objects we need to know for two face cycles |fc1| and |fc2| if we can connect them by a path of the sphere which does not cross any other face cycle. We adapt an idea from \cite{mmmo:CG}. The path connectivity making disjoint face cycles bounding the same face, can be modelled by a visibility graph of the minimal vertices\footnote{minimal with respect to the lexicographic order of the point coordinates of their embedding} of each face cycle. We create faces and assign face cycles based on this property and transfer $M'$ to $M$ thereby. Let $\cal{F}$ be a set of face cycles of the sphere map skeleton. For each face cycle |c| let |MinimalHalfedge[c]| be the halfedge |e| whose target vertex has lexicographic minimal coordinates. Let |FaceCycle[e]| be the face cycle containing |e|. We examine the implicit following implicit graph $H$. Each face cycle of $M'$ is a node of $H$. Let's link two face cycles $c_1$ and $c_2$ by an edge of $H$ if $|target|(|MinimalHalfedge|[c_1])$ has a vertical view down on an edge of $c_2$ (in $M'$) or vice versa. Note that face cycles consist of halfedges and thus we have to refer to the correct one of the two paired halfedges respecting the embedding when looking at face cycles (our faces are left of the directed halfedges, thus consider to separate paired halfedges by an infinitesimal distance, then the visibility is uniquely defined). Note that we do not explicitly model the visibility graph. Instead the recursive behavior of the operation |determine_face()| used below imitates a DFS walk on the visibility graph. In the following method we have the visibility along a sweep line coded via a data accessor $D$ providing for all vertices $v \in V$ the knowledge about the halfedge below $v$. |D.halfedge_below(v)| either provides the halfedge of $E$ that is hit first by a vertical ray downwards or an uninitialized halfedge if there is none. <>= template template void SM_overlayer:: create_face_objects(Halfedge_iterator e_start, Halfedge_iterator e_end, Vertex_iterator v_start, Vertex_iterator v_end, const Below_accessor& D, const Halfsphere_geometry& SG) const { TRACEN("create_face_objects()"); CGAL::Unique_hash_map FaceCycle(-1); std::vector MinimalHalfedge; <> <> <> } @ We iterate all halfedges and assign a number for each face cycle. After that iteration for a halfedge |e| the number of its face cycle is |FaceCycle[e]| and for a face cycle |c| we know |MinimalHalfedge[c]|. We first mark the outer face cycle with number 0. There we don't want to create a face object. <>= Halfedge_around_face_circulator hfc(last_out_edge(v_start)),hend(hfc); TRACEN("equator cycle "<= 0 ) continue; // already assigned Halfedge_around_face_circulator hfc(e),hend(hfc); Halfedge_handle e_min = e; TRACEN(" face cycle numbering "<>= for (int j=1; j 0 ) { // left_turn => outer face cycle Face_handle f = new_face(); link_as_face_cycle(e,f); TRACEN(" creating new face object "<<&*f<<" bd "<<&*e); } } @ Now the only halfedges not linked are those on hole face cycles and the equator face cycle. We use a recursive scheme to find the bounding cycle providing the face object and finally iterate over all isolated vertices to link them accordingly to their containing face object. Note that in that final iteration all halfedges already have face links. Thus that ensures termination. The recursive operation $|determine_face|(e,\ldots)$ returns the face containing the hole cycle of |e|. See the specification in the next section. As a postcondition of this chunk we have all edges and isolated vertices linked to face objects, and all face objects know their bounding face cycles. <>= for (Halfedge_iterator e = e_start; e != e_end; ++e) { if ( face(e) != Face_handle() ) continue; if ( FaceCycle[e] == 0 ) continue; TRACEN("linking hole "<>= template Face_handle determine_face(Halfedge_handle e, const std::vector& MinimalHalfedge, const CGAL::Unique_hash_map& FaceCycle, const Below_accessor& D) const { TRACEN("determine_face "<>= complete_face_support(vertices_begin(), v, O, +1); complete_face_support(v, vertices_end(), O, -1); <>= template template void SM_overlayer:: complete_face_support(Vertex_iterator v_start, Vertex_iterator v_end, const Below_accessor& D, int pos) const { TRACEN("complete_face_support"); for (Vertex_iterator v = v_start; v != v_end; ++v) { <> <> <> TRACEN(" mark of "<> } @ The transfer of face support marks is based on the following fact. We treat the face support per hemisphere. To allow this method we maintain two dedicated points close to the minimal vertex of each iteration (one point for each hemispherical iteration). \displaylps{support}{ The face support iteration unrolled. We examine a position $p$ within the structure of the sphere map $M$ with respect to the support by a face in the sphere map $M_i$. We have two vertices $v_1$ and $v_2$ from $V$ with their forward-oriented edge bundle. The face that supports $p$ with respect to $M_i$ can be determined by following the dotted path until the outer unbounded face of $M_i$ is reached.} \begin{fact} Let $p$ be a point of a hemisphere not part of the 1-skeleton of $M_i$, $q$ be a point just right and below of $y^-$ outside the hemiphere, and $\rho$ be any curve from $p$ to $q$ not containing any vertex of $M_i$. Let $e$ be the first edge that $\rho$ intersects when walking from $p$ to $q$ (if any). If $\rho$ does not intersect the 1-skeleton, then $p$ and $q$ are part of the same face of $M_i$. Otherwise $p$ is part of the face incident to $e$. \end{fact} The above fact is a consequence of the connectedness property of the faces of $M_i$. We now consider point $p$ as part of $M$. For $p$ we consider a path as depicted in figure \figref{support}. We walk along a vertical ray down. If we cross a bundle of edges incident to a vertex $v$ the path turn just below the lowest edge and follows the lowest edges in parallel until it is just below $v$, and then iterates this construction until it ends in a point $q$ outside the actual hemisphere of $M$. Each edge $e$ that is crossed by $\rho$ is either supported by an edge from $M_i$ or $M_{1-i}$. In the former case the first such edge determines the face $f_i$ supporting $p$. If there's no such edge then $p$ is supported by the point $q$ of $M_i$. We want to determine face support for many vertices and edges, thus we don't want to pay such a walk for each query point $p$. Instead we associate for each edge $e$ face support knowledge in the two slots |mark(e,i)| and |incident_mark(e,i)|. The idea is that these slots store the knowledge obtained from a reversal walk from $q$ to $p$. Whenever our path $\rho$ crosses an edge $e$ in $M$ that is supported by an edge $e_i$ in $M_i$ then we associate the mark knowledge plus the mark of the supporting faces from $M_i$ (of a small neighborhood left and right of $e$) with $e$. If the information is already constructed for all edges below a query point $p$, we can obtain that information in constant time. We now come to the coding. We want to complete the support marks for a vertex |v| and the edges |e| of the adjacency list of |v| that are forward oriented. Consider to follow $\rho$ reversely with a pen, then |m_below[2]| always stores the marks of the faces of $M_i$ that support the position of the pen. In the beginning |m_below[i]| stores the mark of the face $f_i$ just slightly below and right of $y^-$. Note that we obtain both marks for $i=0,1$ either on initialization from the input sphere maps or from the halfedge below |v|. If |e_below| exists then it was already treated as a forward oriented edge of a vertex already handled in the vertex iteration. Note that the iteration over all vertices |v| has the invariant that either |v| has no halfedge below, or if it has a halfedge |e_below| then |e_below| has all marks correctly assigned (|mark(e_below,i)| and |incident_mark(e_below,i)| are set for both $i=0,1$). Note that the only vertices that don't have edges below are our poles $y^-$, $y^+$ and all vertices on the x-positive equator $xy^+$. We have to treat these vertices separately. For our first vertex $y^-$ we use the mark information from the local graph |mark_of_halfsphere(int i)|. Note that all others have at most one forward edge along the equator (besides $y^+$) and at least one ingoing edge. For those vertices we use the support path from ``above'' via the ingoing edge. That edge has the support information |incident_mark()| set. <>= TRACEN("VERTEX = "< 0 ? (point(v).hx() >= 0) : (point(v).hx()<=0)) ); for (int i=0; i<2; ++i) m_buffer[i] = incident_mark(previous(first_out_edge(v)),i); } TRACEN(" faces right and below "<>= for (int i=0; i<2; ++i) { Object_handle o = supp_object(v,i); if ( o == NULL ) { mark(v,i) = m_buffer[i]; continue; } Vertex_handle vs; Halfedge_handle es; Halfloop_handle ls; if ( assign(vs,o) ) { mark(v,i) = PI[i].mark(vs); continue; } if ( assign(es,supp_object(v,i)) ) { if ( point(source(es)) == point(v) ) { mark(v,i) = PI[i].mark(source(es)); continue; } if ( point(target(es)) == point(v) ) { mark(v,i) = PI[i].mark(target(es)); continue; } mark(v,i) = PI[i].mark(es); continue; } if ( assign(ls,o) ) { mark(v,i) = PI[i].mark(ls); continue; } CGAL_assertion_msg(0,"wrong handle"); } TRACEN(" vertex marks "<>= if ( is_isolated(v) ) continue; Halfedge_around_vertex_circulator e(first_out_edge(v)), hend(e); CGAL_For_all(e,hend) { if ( !is_forward(e) ) break; TRACEN(" forward edge "<>= Face_iterator f; for (f = faces_begin(); f != faces_end(); ++f) { assoc_info(f); Object_handle boundary_object = face_cycles_begin(f); CGAL_assertion(boundary_object != NULL); Halfedge_handle e; if ( !assign(e,boundary_object) ) CGAL_assertion_msg(0,"Outer face cycle should be first."); for (int i=0; i<2; ++i) mark(f,i) = incident_mark(e,i); } @ \subsection{Unifying the halfspheres} Now we have two embedded hemispherical maps that overlap at the equator of $S_2$. We have to stitch the two halfsphere maps along the equator without loosing any information. <>= merge_halfsphere_maps(vertices_begin(),v,O); check_integrity_and_topological_planarity(); @ Let $e_1$ be an edge in the outer boundary of the positive hemispherical map, let $e_2$ be a corresponding edge of the negative hemispherical map. Both edges are embedded into the equator. Let $v_1 = |source|(e_1)$ and $v_2 = |target|(e_2)$. Both vertices have the same embedding on the sphere surface and have to be identified while keeping the adjacency list order-preserving. Note that $A(v_1)$ and $A(v_2)$ are ccw-order-preserving. For both edges the complementary hemisphere face is on their left side. The properties of our map output regarding the maps in the two halfspheres tell us that $|twin|(|prev|(e_1))$ is the first edge and $e_1$ is the last edge in $A(v_1)$. In the other halfsphere $|twin|(e_2)$ is the first edge and $|next|(e_2)$ is the last edge of $A(v_2)$. After the identification $A(v_1)$ contains all edges, $v_2$ is isolated and can be deleted. <>= template template void SM_overlayer:: merge_nodes(Halfedge_handle e1, Halfedge_handle e2, const Mark_accessor& D) const { Vertex_handle v1 = source(e1), v2 = target(e2); TRACEN("merge_nodes "<>= template template void SM_overlayer:: merge_halfsphere_maps(Vertex_handle v1, Vertex_handle v2, const Mark_accessor& D) const { TRACEN("merging halfspheres "< L_equator; Halfedge_around_face_circulator ep(last_out_edge(v1)), en(twin(first_out_edge(v2))); do { L_equator.push_back(Halfedge_pair(ep,en)); merge_nodes(ep,en,D); ++ep; --en; } while ( source(ep) != v1 ); typename std::list::iterator it; CGAL_forall_iterators(it,L_equator) { Halfedge_handle e1 = it->first, e2 = it->second; Halfedge_handle e1t = twin(e1), e2t = twin(e2); TRACEV(PH(e1));TRACEV(PH(e2)); Halfedge_handle e2tp = previous(e2t); Halfedge_handle e2tn = next(e2t); link_as_prev_next_pair(e2tp,e1); link_as_prev_next_pair(e1,e2tn); Face_handle f = face(e2t); if ( is_boundary_object(e2t) ) { undo_boundary_object(e2t,f); store_boundary_object(e1,f); } set_face(e1,f); if ( e2 == first_out_edge(source(e2)) ) set_first_out_edge(source(e2),e1t); D.discard_info(e2); delete_edge_pair_only(e2); } } @ \subsection{Selecting marks} For the selection we just iterate over all objects read the marks refering to the two input structures, apply our selection operation and store the mark back into the object. At this place we discard the additional information which was accumulated during the subdivision. The flexibility of the operation is achieved by a template function object type |Selection|. An object |SP| of type |Selection| must provide a binary function operator returning a new mark object. The runtime of the selection phase is obviously linear in the size of the sphere map |P|. We know the marks of two dedicated points on $S_2$. <>= template template void SM_overlayer:: select(const Selection& SP) const { Vertex_iterator v; for(v = vertices_begin(); v != vertices_end(); ++v) { mark(v) = SP(mark(v,0),mark(v,1)); discard_info(v); } Halfedge_iterator e; for(e = halfedges_begin(); e != halfedges_end(); ++e) { if ( info(e) == 0 ) continue; // twin mark(e) = SP(mark(e,0),mark(e,1)); discard_info(e); } Face_iterator f; for(f = faces_begin(); f != faces_end(); ++f) { mark(f) = SP(mark(f,0),mark(f,1)); discard_info(f); } mark_of_halfsphere(-1) = SP(PI[0].mark_of_halfsphere(-1), PI[1].mark_of_halfsphere(-1)); mark_of_halfsphere(+1) = SP(PI[0].mark_of_halfsphere(+1), PI[1].mark_of_halfsphere(+1)); } @ \subsection{Simplification of attributed sphere maps} \displaylps{simplification}{The possible configurations for simplification.} In this section we examine the task to simplify a given sphere map to reach a minimimal representation (minimimal number of objects of the sphere map structure, while the underlying attributed point set stays the same). There are three situations where one can imagine to simplify the structure (see figure \figref{simplification}): \begin{enumerate} \item a vertex which is incident to exactly two edges both supported by the same line where all three objects have the same mark can be unified into one edge without changing the stored point set. (figure \figref{simplification},A) \item a uedge $e$ which has the same mark as the two faces $f_1$ and $f_2$ incident to it does not contribute any structural information and thus can be removed (figure \figref{simplification},B). \item a vertex $v$ where all the edges of its adjacency list and also all incident faces have the same mark as the vertex also carry no structural information (figure \figref{simplification},C,D). \end{enumerate} Note that if we first remove edges of the second case then the vertices of case three have no incident edges at all and thus can be easily identified as isolated vertices whose surounding face has the same mark. The first case does only play a role if one of the faces incident to the edges carries a diffent mark than the edges. We can thus easily formulate the simplification routine. However there are some problems with the update operations of the sphere map structure. How can we maintain the face objects and incidence links to halfedges and vertices if we are unifying faces by deleting edges? The trivial way does not work within our time bound. We cannot afford to maintain the face objects in a correct status in each step of the simplification, as this would mean to repeatedly iterate total face cycles. (Note that we cannot use a similar scheme as the one based on the |halfedge_below| information of section \ref{creating face objects} due to the fact that such edges might be deleted in the simplification process. Thereby the face creation as described in section \ref{creating face objects} is not possible without a new sweep. We take a different approach. Instead of the geometrically defined below links as a criterium for linking face cycles to face objects, we use a unification history stored in a partition data structure. All face cycles reference face objects. When we have to union two different faces due to the deletion of a halfedge separating them we store that fact by a union operation in a partition data structure. The finally assigned face to all face cycles is the face corresponding to the representative partition item. <>= template void SM_overlayer::simplify() const { TRACEN("simplifying"); typedef typename CGAL::Partition::item partition_item; CGAL::Unique_hash_map Pitem; CGAL::Partition FP; <> <> <> <> <> } @ We assign one partition item to each face object and make the item accessible to the face via a hash map. During the assignment of face cycles to face objects we will only use links from skeleton objects like vertices and edges to faces. We therefore can discard all face cycle entries of the face objects. <>= Face_iterator f; for (f = faces_begin(); f != faces_end(); ++f) { Pitem[f] = FP.make_block(f); clear_face_cycle_entries(f); } @ In this chunk we take care of the simplification critereon 2. We only iterate halfedge pairs (uedges). When the marks of the incident faces agree with the mark of the face, we union the items of the faces if they are different. Special treatment is required for incident vertices if they become isolated when the uedge is deleted. <>= Halfedge_iterator e, en; for(e = halfedges_begin(); e != halfedges_end(); e = en) { en = e; ++en; if ( en==twin(e) ) ++en; if ( mark(e) == mark(face(e)) && mark(e) == mark(face(twin(e))) ) { TRACEN("deleting "<>= CGAL::Unique_hash_map linked(false); for (e = halfedges_begin(); e != halfedges_end(); ++e) { if ( linked[e] ) continue; Halfedge_around_face_circulator hfc(e),hend(hfc); Face_handle f = FP.inf(FP.find(Pitem[face(e)])); CGAL_For_all(hfc,hend) { set_face(hfc,f); linked[hfc]=true; } store_boundary_object(e,f); } if ( has_loop() ) { Halfloop_handle l = halfloop(); Face_handle f = FP.inf(FP.find(Pitem[face(l)])); link_as_loop(l,f); f = FP.inf(FP.find(Pitem[face(twin(l))])); link_as_loop(twin(l),f); } @ After the previous simplification we still have to take care of the vertex related simplifications 1 and 3. In case a vertex has out-degree two, the two incident edges are embedded collinearly, and all three objects have the same mark we remove the vertex by joining the two uedges into one. In case that the vertex is isolated we either remove it if the mark agrees with the incident face, otherwise we anchor the vertex in the face by adding it to the isolated vertex list. Note that the face link of each isolated vertex was either set already in the face creation phase, or in the chunk $\langle$\textit{simplify via non-separating halfedges}$\rangle$ when the last incident edge was deleted. <>= Vertex_iterator v,vn; for(v = vertices_begin(); v != vertices_end(); v=vn) { vn=v; ++vn; if ( is_isolated(v) ) { if ( mark(v) == mark(face(v)) ) { TRACEN("removing isolated vertex"<>= Face_iterator fn; for (f = fn = faces_begin(); f != faces_end(); f=fn) { ++fn; partition_item pit = Pitem[f]; if ( FP.find(pit) != pit ) delete_face_only(f); } @ \begin{ignorecodeparts} The following operations just wrap some basic primitives which make our code more readable. <>= Sphere_segment segment(SM_decorator N, Halfedge_handle e) const { return K.construct_segment( N.point(N.source(e)),N.point(N.target(e)),N.circle(e)); } Sphere_segment trivial_segment(SM_decorator N, Vertex_handle v) const { Sphere_point p = N.point(v); return K.construct_segment(p,p); } Seg_pair two_segments(SM_decorator N, Halfedge_handle e) const // we know that source(e)==target(e) { return N.circle(e).split_at(N.point(N.source(e))); } Seg_pair two_segments(SM_decorator N, Halfloop_handle l) const { return N.circle(l).split_at_xy_plane(); } Mark& mark(Vertex_handle h) const { return Base::mark(h); } Mark& mark(Halfedge_handle h) const { return Base::mark(h); } Mark& mark(Halfloop_handle h) const { return Base::mark(h); } Mark& mark(Face_handle h) const { return Base::mark(h); } @ \end{ignorecodeparts} @ \subsection{Runtime results} We can now summarize the calculated overlay properties \begin{lemma} Assume that $M_0$, $M_1$ each are sphere maps whose embedding is order-preserving and the adjacency lists have a forward prefix then $|subdivide|(P_0, P_1)$ constructs in $M = (V,E,L,F)$ the overlay sphere map of $M_0$ and $M_1$ and each object $u \in V \cup E \cup F$ carries the mark information |mark(u,i)| from the corresponding supporting object of the input sphere map. The runtime of the overlay is dominated by the sweep of the skeleton objects of $M_0$ and $M_1$. \end{lemma} The following analysis of the partition data structure is due to Tarjan \cite{tarjan83}. \begin{fact} A sequence of $m$ union and find operations on a partition data structure using union by rank and path compression can be done in time $O(m \alpha(m,n))$, where $\alpha$ is the very slowly growing inverse of a suitably defined Ackermann function. \end{fact} We can therefore summarize the runtime of the simplification action. \begin{lemma} Assume that |P| is a sphere map with the properties cited in the introduction of this section. Then the method |simplify()| runs in time $O(n\ \alpha(kn,n))$ where $n$ is the size of $M$, $kn$ is a bound for the number of face unifications and find operations, and $\alpha$ is the function mentioned above. \end{lemma} \begin{proof} The number of edges and faces of $M$ is linear in $n$. The number of union operations is bounded by the number of faces, and the number of find operations is bounded by three times\footnote{look for the |find()| and |same_block()| operations above. The latter uses two find operations.} the number of edges plus the number of faces. \end{proof} The time bound shows that the cost of the simplification phase is subsumed in the $O(n \log n)$ cost of the construction of the subdivision. Note that after the simplification the sphere map output has again the input properties of the overlay calculation operation from section \ref{Overlay calculation of two sphere maps}. \begin{ignorecodeparts} @ \subsection{Subdividing a set of sphere segments} Subdivision is done in phases. First we partition all segments into the pieces corresponding to the two halfspheres above and below the xy-plane. Then we sweep both halfspheres separate by a symmetric algorithmic module. We ensure that the boundary of the resulting two halfsphere maps carry the same topology. We unify the maps embedded into both halfspheres at the boundary and create one map embedded into the sphere. <>= template template void SM_overlayer:: subdivide_segments(Iterator start, Iterator end) const { typedef SMO_decorator SM_output; typedef typename Kernel::Positive_halfsphere_geometry PH_geometry; typedef CGAL::Segment_overlay_traits< Iterator, SM_output, PH_geometry> PHS_traits; typedef CGAL::generic_sweep Positive_halfsphere_sweep; typedef typename Kernel::Negative_halfsphere_geometry NH_geometry; typedef CGAL::Segment_overlay_traits< Iterator, SM_output, NH_geometry> NHS_traits; typedef CGAL::generic_sweep Negative_halfsphere_sweep; std::list Lp,Lm; partition_xy( start, end, Lp , +1); partition_xy( start, end, Lm , -1); // both lists initialized with four quarter segments // supporting the xy-equator thereby separating the // two halfspheres // all other segments in the range are split into their // connected components with respect to the xy-plane. Vertex_handle v1,v2; SM_output O(*this); typedef typename PHS_traits::INPUT Input_range; Positive_halfsphere_sweep SP(Input_range(Lp.begin(),Lp.end()),O); SP.sweep(); //TRACEN("POS SWEEP\n"<<(dump(std::cerr),"")); v1= vertices_begin(); v2=--vertices_end(); Negative_halfsphere_sweep SM(Input_range(Lm.begin(),Lm.end()),O); SM.sweep(); //TRACEN("NEG SWEEP\n"<<(dump(std::cerr),"")); ++v2; // now two CCs of sphere graph calculated // v1 = first node of CC in positive xy-sphere // v2 = first node of CC in negative xy-sphere merge_halfsphere_maps(v1,v2,O); check_integrity_and_topological_planarity(false); } <>= template class SMO_decorator { public: typedef Decorator_ Graph; typedef typename Decorator_::Vertex_handle Vertex_handle; typedef typename Decorator_::Halfedge_handle Halfedge_handle; typedef typename Decorator_::Sphere_point Point_2; typedef typename Decorator_::Sphere_segment Segment_2; Decorator_ G; SMO_decorator(Graph Gi) : G(Gi) {} Vertex_handle new_vertex(const Point_2& p) { return G.new_vertex(p); } void link_as_target_and_append(Vertex_handle v, Halfedge_handle e) { G.link_as_target_and_append(v,e); } Halfedge_handle new_halfedge_pair_at_source(Vertex_handle v) { return G.new_edge_pair_at_source(v,Graph::BEFORE); } void supporting_segment(Halfedge_handle e, ITERATOR it) {} void halfedge_below(Vertex_handle v, Halfedge_handle e) {} void trivial_segment(Vertex_handle v, ITERATOR it) {} void starting_segment(Vertex_handle v, ITERATOR it) {} void passing_segment(Vertex_handle v, ITERATOR it) {} void ending_segment(Vertex_handle v, ITERATOR it) {} }; // SMO_decorator @ \begin{ignorecodeparts} <>= <> #ifndef CGAL_SM_OVERLAYER_H #define CGAL_SM_OVERLAYER_H #include #include #include #include #include #include #include #undef _DEBUG #define _DEBUG 131 #include #define CGAL_USING(t) typedef typename Base::t t #ifndef CGAL_USE_LEDA #define LEDA_MEMORY(t) #endif CGAL_BEGIN_NAMESPACE <> <> <> CGAL_END_NAMESPACE #undef CGAL_USING #endif //CGAL_SM_OVERLAYER_H <>= #include #include #include #include #include typedef CGAL::Sphere_geometry SKernel; typedef CGAL::Sphere_map Sphere_map; typedef Sphere_map::Vertex_handle Vertex_handle; typedef Sphere_map::Halfedge_handle Halfedge_handle; typedef Sphere_map::Halfloop_handle Halfloop_handle; typedef Sphere_map::Face_handle Face_handle; typedef CGAL::SM_decorator SM_decorator; typedef CGAL::SM_overlayer SM_overlayer; int main(int argc, char **argv) { CGAL::set_pretty_mode ( std::cerr ); SETDTHREAD(101); // Sphere_geometry 11 // Sphere_geometry_OGL 13 // Segment_overlay 23 // leda_sphere_map 31 Point_3 p(0,0,0); Sphere_map M; Vertex_handle v = M.new_vertex(p); SM_overlayer D(M); return 0; } <>= #include #include #include #include #if CGAL_LEDA_VERSION < 500 #include #else #include #endif #include #include #include #include "SM_overlayer.h" typedef CGAL::Sphere_geometry SKernel; typedef CGAL::Sphere_map Sphere_map; typedef Sphere_map::Vertex_handle Vertex_handle; typedef Sphere_map::Halfedge_handle Halfedge_handle; typedef Sphere_map::Face_handle Face_handle; typedef CGAL::SM_decorator SM_decorator; typedef CGAL::SM_overlayer SM_overlayer; typedef CGAL::Creator_uniform_3 Creator; typedef CGAL::Random_points_in_cube_3 Point_source; typedef SKernel::Sphere_point SPoint; typedef SKernel::Sphere_segment SSegment; struct OR { bool operator()(bool b1, bool b2) const { return b1||b2; } }; int main(int argc, char **argv) { CGAL::set_pretty_mode ( std::cerr ); SETDTHREAD(911); // Sphere_geometry 11 // Sphere_geometry_OGL 13 // Segment_overlay 23 // SM_overlayer 53 Point_3 p(0,0,0); Sphere_map E1,E2,E3; SM_decorator D1(E1),D2(E2),D3(E3); SM_overlayer O1(D1),O2(D2),O3(D3); int n; leda_string input_file; leda_param_handler H(argc,argv,".sg",false); H.add_parameter("number_of_lines:-n:int:10"); H.add_parameter("file_of_segments:-i:string:"); leda_param_handler::init_all(); H.get_parameter("-n",n); H.get_parameter("-i",input_file); CGAL_assertion_msg(n>0,"-n value must be postive."); std::list L; if ( input_file == "" ) { Point_source S(5); Point_3 p1,p2,ph; Point_3 o(0,0,0); while ( n-- > 0 ) { do { ph = *S++; } while ( ph == o ); Plane_3 h(o,(ph-CGAL::ORIGIN).direction()); do { p1 = *S++; } while ( p1 == o || h.projection(p1) == o ); do { p2 = *S++; } while ( p2 == o || h.projection(p2) == o ); SPoint p3(h.projection(p1)),p4(h.projection(p2)); int which = CGAL::default_random.get_int(0,3); if ( p3 == p4 ) which = 3; if ( p3 == p4.opposite() ) which = 2; switch ( which ) { case 0: // short L.push_back( SSegment(p3,p4,true) ); break; case 1: // long L.push_back( SSegment(p3,p4,false) ); break; case 2: // halfcircle L.push_back( SSegment(p3,p3.opposite(),h) ); break; case 3: // trivial L.push_back( SSegment(p3,p3,h) ); break; } } } else { std::ifstream input(input_file); CGAL_assertion_msg(input,"no input log."); SSegment s; while ( input >> s ) L.push_back(s); } std::ofstream output("smo-demo.log"); std::list::iterator it; CGAL_forall_iterators(it,L) output << *it; output << std::endl; output.close(); std::list L1,L2; int b=0; CGAL_forall_iterators(it,L) { if ( b == 0 ) L1.push_back(*it); else L2.push_back(*it); b = 1-b; } O1.create_from_segments(L1.begin(),L1.end()); O1.simplify(); // O1.dump(std::cerr); O2.create_from_segments(L2.begin(),L2.end()); O2.simplify(); // O2.dump(std::cerr); O3.subdivide(E1,E2); O3.select(OR()); O3.simplify(); // O3.dump(std::cerr); return 0; } <>= // ============================================================================ // // 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_S2/SM_overlayer.h // package : Nef_S2 // chapter : Nef Polyhedra // // source : nef_s2/SM_overlayer.lw // revision : $Id$ // revision_date : $Date$ // // author(s) : Michael Seel // maintainer : Michael Seel // coordinator : Michael Seel // // implementation: Overlay module for sphere maps // ============================================================================ @ \end{ignorecodeparts} %KILLSTART \bibliographystyle{alpha} \bibliography{diss,general,geo_mod,comp_geo} \newpage \section{Appendix} %\input{SM_decorator.man} %\input{geninfo.man} \end{document} %KILLEND