1 // Copyright (c) 2006,2007,2008,2009,2010,2011 Tel-Aviv University (Israel).
2 // All rights reserved.
3 //
4 // This file is part of CGAL (www.cgal.org).
5 //
6 // $URL: https://github.com/CGAL/cgal/blob/v5.3/Arrangement_on_surface_2/include/CGAL/Arr_topology_traits/Arr_spherical_topology_traits_2_impl.h $
7 // $Id: Arr_spherical_topology_traits_2_impl.h f55ef7d 2020-10-09T18:36:17+02:00 Mael Rouxel-Labbé
8 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 // Author(s): Efi Fogel         <efif@post.tau.ac.il>
11 //            Ron Wein          <wein@post.tau.ac.il>
12 
13 #ifndef CGAL_ARR_SPHERICAL_TOPOLOGY_TRAITS_2_IMPL_H
14 #define CGAL_ARR_SPHERICAL_TOPOLOGY_TRAITS_2_IMPL_H
15 
16 #include <CGAL/license/Arrangement_on_surface_2.h>
17 
18 /*! \file
19  *
20  * Member-function definitions for the
21  * Arr_spherical_topology_traits_2<GeomTraits> class.
22  */
23 
24 namespace CGAL {
25 
26 /*! \brief constructs default */
27 template <typename GeomTraits, typename Dcel>
28 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
Arr_spherical_topology_traits_2()29 Arr_spherical_topology_traits_2() :
30   m_spherical_face(nullptr),
31   m_north_pole(nullptr),
32   m_south_pole(nullptr),
33   m_own_geom_traits(true)
34 {
35   m_geom_traits = new Gt_adaptor_2;
36   m_boundary_vertices = Vertex_map(Vertex_key_comparer(m_geom_traits));
37 }
38 
39 /*! \brief constructs from a geometry-traits object. */
40 template <typename GeomTraits, typename Dcel>
41 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
Arr_spherical_topology_traits_2(const Geometry_traits_2 * traits)42 Arr_spherical_topology_traits_2(const Geometry_traits_2* traits) :
43   m_spherical_face(nullptr),
44   m_north_pole(nullptr),
45   m_south_pole(nullptr),
46   m_own_geom_traits(false)
47 {
48   m_geom_traits = static_cast<const Gt_adaptor_2*>(traits);
49   m_boundary_vertices = Vertex_map(Vertex_key_comparer(m_geom_traits));
50 }
51 
52 /*! \brief destructs */
53 template <typename GeomTraits, typename Dcel>
54 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
~Arr_spherical_topology_traits_2()55 ~Arr_spherical_topology_traits_2()
56 {
57   // Clear the DCEL.
58   m_dcel.delete_all();
59 
60   if (m_own_geom_traits && (m_geom_traits != nullptr)) {
61     delete m_geom_traits;
62     m_geom_traits = nullptr;
63   }
64 }
65 
66 /*! \brief assigns the contents of another topology-traits class */
67 template <typename GeomTraits, typename Dcel>
68 void Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
assign(const Self & other)69 assign(const Self& other)
70 {
71   // Clear the current DCEL and duplicate the other DCEL.
72   m_dcel.delete_all();
73   m_dcel.assign(other.m_dcel);
74 
75   // Take care of the traits object.
76   if (m_own_geom_traits && m_geom_traits != nullptr) {
77     delete m_geom_traits;
78     m_geom_traits = nullptr;
79   }
80 
81   if (other.m_own_geom_traits) {
82     m_geom_traits = new Gt_adaptor_2;
83     m_own_geom_traits = true;
84   }
85   else {
86     m_geom_traits = other.m_geom_traits;
87     m_own_geom_traits = false;
88   }
89 
90   // Update the rest of the properties.
91   dcel_updated();
92 }
93 
94 /*! \brief initializes an empty DCEL structure. */
95 template <typename GeomTraits_, typename Dcel_>
dcel_updated()96 void Arr_spherical_topology_traits_2<GeomTraits_, Dcel_>::dcel_updated()
97 {
98   typedef Dcel_                                         Dcel;
99 
100   // Go over the DCEL vertices and locate the south and north pole (if any)
101   // and any other vertex on the line of discontinuity.
102 
103   m_north_pole = nullptr;
104   m_south_pole = nullptr;
105   m_boundary_vertices.clear();
106 
107   typename Dcel::Vertex_iterator vit = this->m_dcel.vertices_begin();
108   for (; vit != this->m_dcel.vertices_end(); ++vit) {
109     Arr_parameter_space bx = vit->parameter_space_in_x();
110     Arr_parameter_space by = vit->parameter_space_in_y();
111 
112     if (by == ARR_BOTTOM_BOUNDARY) m_south_pole = &(*vit);
113     else if (by == ARR_TOP_BOUNDARY) m_north_pole = &(*vit);
114     else if (bx != ARR_INTERIOR) {
115       const Point_2& key = vit->point();
116       m_boundary_vertices.insert(Vertex_value(key, &(*vit)));
117     }
118   }
119 
120   // Go over the DCEL faces and locate the spherical face, which is the only
121   // face with no outer CCB.
122 
123   m_spherical_face = nullptr;
124   typename Dcel::Face_iterator fit = this->m_dcel.faces_begin();
125   for (; fit != this->m_dcel.faces_end(); ++fit) {
126     if (fit->number_of_outer_ccbs() == 0) {
127       CGAL_assertion(m_spherical_face == nullptr);
128 
129       m_spherical_face = &(*fit);
130       break;
131     }
132   }
133   CGAL_assertion(m_spherical_face != nullptr);
134 }
135 
136 /*! \brief initializes an empty DCEL structure. */
137 template <typename GeomTraits, typename Dcel>
init_dcel()138 void Arr_spherical_topology_traits_2<GeomTraits, Dcel>::init_dcel()
139 {
140   // std::cout << "init_dcel()" << std::endl;
141   // Clear the current DCEL.
142   m_dcel.delete_all();
143   m_boundary_vertices.clear();
144 
145   // Create the face.
146   m_spherical_face = this->m_dcel.new_face();
147   m_spherical_face->set_unbounded(false);
148   m_spherical_face->set_fictitious(false);
149 
150   m_north_pole = nullptr;
151   m_south_pole = nullptr;
152 }
153 
154 /*! \brief determines whether a point lies in the interior of a given face. */
155 template <typename GeomTraits, typename Dcel>
156 bool Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
is_in_face(const Face * f,const Point_2 & p,const Vertex * v)157 is_in_face(const Face* f, const Point_2& p, const Vertex* v) const
158 {
159   // std::cout << "is_in_face()" << std::endl;
160   CGAL_precondition((v == nullptr) || !v->has_null_point());
161   CGAL_precondition((v == nullptr) ||
162                     m_geom_traits->equal_2_object()(p, v->point()));
163 
164   /* There is always one face that contains everything else. It has no
165    * outer CCB's. When a new face is constructed, we make sure that the
166    * face that contains everything also contains the north pole. (In the
167    * degenerate case, where a vertex coincides with the north pole, the face
168    * that contains everything is incident to the north pole.)
169    * If the face has no outer ccb's, it contains everything:
170    */
171 #if 0
172   std::cout << "p: " << p
173             << ", f->number_of_outer_ccbs(): " << f->number_of_outer_ccbs()
174             << std::endl;
175 #endif
176   if (f->number_of_outer_ccbs() == 0) return true;
177   if (((v != nullptr) && (v->parameter_space_in_y() == ARR_TOP_BOUNDARY)) ||
178       (m_geom_traits->parameter_space_in_y_2_object()(p) == ARR_TOP_BOUNDARY))
179     return false;
180 
181   /*! \todo a temporary test
182    * if (((v != nullptr) && (v->parameter_space_in_y() == ARR_BOTTOM_BOUNDARY)) ||
183    *   (p.is_min_boundary()))
184    * return false;
185    */
186 
187   typename Gt_adaptor_2::Parameter_space_in_x_2 ps_x_op =
188     m_geom_traits->parameter_space_in_x_2_object();
189   typename Gt_adaptor_2::Parameter_space_in_y_2 ps_y_op =
190     m_geom_traits->parameter_space_in_y_2_object();
191   typename Gt_adaptor_2::Compare_x_2 cmp_x_op =
192     m_geom_traits->compare_x_2_object();
193   typename Gt_adaptor_2::Compare_y_at_x_2 cmp_y_at_x_op =
194     m_geom_traits->compare_y_at_x_2_object();
195   typename Gt_adaptor_2::Compare_x_point_curve_end_2 cmp_x_pt_ce =
196     m_geom_traits->compare_x_point_curve_end_2_object();
197 
198   // Process the input point.
199   bool p_is_interior_x = !(m_geom_traits->is_on_y_identification_2_object()(p));
200 
201   /* Maintain a counter of the number of x-monotone curves that intersect an
202    * upward vertical ray emanating from p. Handle degenerate cases as
203    * explained below).
204    */
205   unsigned int num_intersections = 0;
206 
207   /* Traverse all outer CCBs of the face. For each boundary component go over
208    * all its halfedges, and count those which are above p.
209    */
210   typename Face::Outer_ccb_const_iterator oit;
211   for (oit = f->outer_ccbs_begin(); oit != f->outer_ccbs_end(); ++oit) {
212     const Halfedge* first = *oit;
213     const Halfedge* curr = first;
214 
215     /* Compare p to the source vertex of the first halfedge. If p coincides
216      * with this vertex, p is obviously not in the interior of the face.
217      */
218     if (curr->opposite()->vertex() == v) return false;
219 
220     /*! We identify 2 main cases:
221      * 1. The vertical ray intersects the boundary at a halfedge. In this
222      * case the x-possition of p is strictly larger than the x-possition of
223      * the current-curve source, and strictly smaller than x-possition of
224      * the current-curve target, or vise versa.
225      * 2. The vertical ray intersects the boundary at a vertex. In this case:
226      * a. the x-possition of p is strictly smaller than the x-position of the
227      * current-curve source, and equal to the x-position of the current-curve
228      * target, and
229      * b. the x-possition of p is equal to the x-position of the next-curve
230      * source (not counting vertical curves in between), and strictly larger
231      * than the x-possition of the next-curve target, or vise verase (that is,
232      * the "smaller" and "larger" interchanged).
233      */
234 
235     /* Indicates that a change between the x-position of p and the x-position
236      * of the current-curve source, and the x-position of p and the x-position
237      * of the current-curve target is pending. Used to handle case (2) above.
238      */
239     bool change_pending = false;
240 
241     /*! Indicates that the conditions described in (2.b) above are met during
242      * the 1st iteration in the loop, which implies that the last curve must be
243      * checked.
244      */
245     bool last_pending = false;
246 
247     Comparison_result res_pending = EQUAL, res_last = EQUAL,
248       res_source = EQUAL, res_target;
249     Arr_parameter_space ps_x_pending = ARR_INTERIOR, ps_x_last = ARR_INTERIOR,
250       ps_x_source, ps_x_target = ARR_INTERIOR,
251       ps_y_source, ps_y_target;
252 
253     do {
254       /* Compare p to the target vertex of the current halfedge. If the
255        * vertex v is on the boundary of the component, p is not in the interior
256        * the face.
257        */
258       if (curr->vertex() == v) return false;
259 
260       // Ignore vertical curves:
261       bool is_vertical = m_geom_traits->is_vertical_2_object()(curr->curve());
262       if (is_vertical) {
263         /* If this outer ccb chain contains the north pole, and our point
264          * lies horizontaly between the two vertical curves that meet at
265          * the north pole, increase the intersection counter
266          */
267         if (curr->direction() == ARR_LEFT_TO_RIGHT) {
268           Arr_parameter_space ps_y_1 = ps_y_op(curr->curve(), ARR_MAX_END);
269           Arr_parameter_space ps_y_2 = ps_y_op(curr->next()->curve(),
270                                                ARR_MAX_END);
271           if ((ps_y_1 == ARR_TOP_BOUNDARY) && (ps_y_2 == ARR_TOP_BOUNDARY)) {
272             // Compare the x-coordinates:
273             Comparison_result rc1 =
274               cmp_x_pt_ce(p, curr->curve(), ARR_MAX_END);
275             Comparison_result rc2 =
276               cmp_x_pt_ce(p, curr->next()->curve(), ARR_MAX_END);
277             if (rc1 == opposite(rc2)) ++num_intersections;
278           }
279         }
280         curr = curr->next();
281         continue;
282       }
283 
284       /* If the current halfedge belongs to an "antenna". Namely, its
285        * incident face is the same as its twin's, skip it to avoid counting
286        * it twice.
287        */
288       const Face* curr_face = (curr->is_on_inner_ccb()) ?
289         curr->inner_ccb()->face() : curr->outer_ccb()->face();
290       const Halfedge* opp_he = curr->opposite();
291       const Face* opp_curr_face = (opp_he->is_on_inner_ccb()) ?
292         opp_he->inner_ccb()->face() : opp_he->outer_ccb()->face();
293 
294       if (curr_face == opp_curr_face) {
295         curr = curr->next();
296         continue;
297       }
298 
299       Arr_curve_end ind_source, ind_target;
300       if (curr->direction() == ARR_LEFT_TO_RIGHT) {
301         ind_source = ARR_MIN_END;
302         ind_target = ARR_MAX_END;
303       }
304       else {
305         ind_source = ARR_MAX_END;
306         ind_target = ARR_MIN_END;
307       }
308 
309       ps_x_source = ps_x_op(curr->curve(), ind_source);
310       ps_x_target = ps_x_op(curr->curve(), ind_target);
311 
312       ps_y_source = ps_y_op(curr->curve(), ind_source);
313       ps_y_target = ps_y_op(curr->curve(), ind_target);
314 
315       if (!p_is_interior_x) {
316         if (ps_x_source == ps_x_target) {
317           curr = curr->next();
318           continue;
319         }
320 
321         if (ps_x_target != ARR_INTERIOR) {
322           change_pending = true;
323           ps_x_pending = (ps_x_target == ARR_LEFT_BOUNDARY) ?
324             ARR_RIGHT_BOUNDARY : ARR_LEFT_BOUNDARY;
325         }
326         if (ps_x_source != ARR_INTERIOR) {
327           if (change_pending) {
328             change_pending = false;
329             if (ps_x_pending == ps_x_source) {
330               Comparison_result res_y_at_x = cmp_y_at_x_op(p, curr->curve());
331               if (res_y_at_x == EQUAL) return false;
332               if (res_y_at_x == SMALLER) num_intersections++;
333             }
334           } else {
335             // This must be the first curve. Remember to check the last curve
336             ps_x_last = (ps_x_source == ARR_LEFT_BOUNDARY) ?
337               ARR_RIGHT_BOUNDARY : ARR_LEFT_BOUNDARY;
338             last_pending = true;
339           }
340         }
341         curr = curr->next();
342         continue;
343       }
344 
345       res_source = (ps_x_source == ARR_LEFT_BOUNDARY) ? LARGER :
346         (ps_x_source == ARR_RIGHT_BOUNDARY) ? SMALLER :
347         (ps_y_source == ARR_INTERIOR) ?
348         cmp_x_op(p, curr->opposite()->vertex()->point()) :
349         cmp_x_pt_ce(p, curr->curve(), ind_source);
350 
351       res_target = (ps_x_target == ARR_LEFT_BOUNDARY) ? LARGER :
352         (ps_x_target == ARR_RIGHT_BOUNDARY) ? SMALLER :
353         (ps_y_target == ARR_INTERIOR) ?
354         cmp_x_op(p, curr->vertex()->point()) :
355         cmp_x_pt_ce(p, curr->curve(), ind_target);
356 
357       /* If a vertical ray is shot from p upward, the x-monotone curve
358        * associated with curr is hit once.
359        */
360       if (res_source == res_target) {
361         curr = curr->next();
362         continue;
363       }
364 
365       if (res_source != EQUAL) {
366         change_pending = true;
367         res_pending = (res_source == SMALLER) ? LARGER : SMALLER;
368       }
369       if (res_target != EQUAL) {
370         if (change_pending) {
371           change_pending = false;
372           if (res_pending == res_target) {
373             Comparison_result res_y_at_x = cmp_y_at_x_op(p, curr->curve());
374             if (res_y_at_x == EQUAL) return false;
375             if (res_y_at_x == SMALLER) num_intersections++;
376           }
377         } else {
378           // This must be the first curve. Remember to check the last curve
379           res_last = (res_target == SMALLER) ? LARGER : SMALLER;
380           last_pending = true;
381         }
382       }
383 
384       /* Proceed to the next halfedge along the component boundary.
385        * Note that the source vertex of this halfedge is the current target.
386        */
387       curr = curr->next();
388     } while (curr != first);
389 
390     if (last_pending) {
391       if (!p_is_interior_x) {
392         if (ps_x_last == ps_x_target) {
393           Comparison_result res_y_at_x = cmp_y_at_x_op(p, curr->curve());
394           if (res_y_at_x == EQUAL) return false;
395           if (res_y_at_x == SMALLER) num_intersections++;
396         }
397         continue;
398       }
399 
400       if (res_last == res_source) {
401         Comparison_result res_y_at_x = cmp_y_at_x_op(p, curr->curve());
402         if (res_y_at_x == EQUAL) return false;
403         if (res_y_at_x == SMALLER) num_intersections++;
404       }
405     }
406   }
407   /* The query point lies inside the connected components if the face does
408    * not contain the north pole, and the vertical ray intersects the
409    * boundaries an odd number of times. As mentioned above, if the face does
410    * contain the north pole, then it contains everything, (and has no outer
411    * CCB's at all).
412    */
413   return (num_intersections& 0x1);
414 }
415 
416 /*! \brief compares the relative y-position of a point and a halfedge */
417 template <typename GeomTraits, typename Dcel>
418 Comparison_result
419 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
compare_y_at_x(const Point_2 & p,const Halfedge * he)420 compare_y_at_x(const Point_2& p, const Halfedge* he) const
421 {
422   // std::cout << "compare_y_at_x(Point_2&,Halfedge*)" << std::endl;
423   return m_geom_traits->compare_y_at_x_2_object()(p, he->curve());
424 }
425 
426 /*! \brief determines whether a vertex is associated with a curve end */
427 template <typename GeomTraits, typename Dcel>
428 bool Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
are_equal(const Vertex * v,const X_monotone_curve_2 & xc,Arr_curve_end ind,Arr_parameter_space ps_x,Arr_parameter_space ps_y)429 are_equal(const Vertex* v,
430           const X_monotone_curve_2& xc, Arr_curve_end ind,
431           Arr_parameter_space ps_x, Arr_parameter_space ps_y) const
432 {
433 #if 0
434   std::cout << "are_equal"
435             << ", v: " << v->point()
436             << ", xc: " << xc << ", " << ind
437             << std::endl;
438 #endif
439   CGAL_precondition(ps_x == ARR_LEFT_BOUNDARY || ps_x == ARR_RIGHT_BOUNDARY ||
440                     ps_y == ARR_BOTTOM_BOUNDARY || ps_y == ARR_TOP_BOUNDARY);
441 
442   // If the given boundary conditions do not match those of the given
443   // vertex, v cannot represent the curve end.
444   if (ps_y != v->parameter_space_in_y()) return false;
445 
446   if (ps_y != ARR_INTERIOR) return (ps_y == v->parameter_space_in_y());
447 
448   if (((ps_x == ARR_INTERIOR) && (v->parameter_space_in_x() != ARR_INTERIOR)) ||
449       ((ps_x != ARR_INTERIOR) && (v->parameter_space_in_x() == ARR_INTERIOR)))
450     return false;
451 
452   CGAL_assertion(ps_x != ARR_INTERIOR);
453   /* Both vertices have the same x boundary conditions =>
454    * comapare their y-position.
455    */
456   const Point_2& p1 = v->point();
457   const Point_2& p2 = (ind == ARR_MIN_END) ?
458     m_geom_traits->construct_min_vertex_2_object()(xc) :
459     m_geom_traits->construct_max_vertex_2_object()(xc);
460   return (m_geom_traits->compare_y_on_boundary_2_object()(p1, p2) == EQUAL);
461 }
462 
463 /*! \brief receives a notification on the creation of a new boundary vertex */
464 template <typename GeomTraits, typename Dcel>
465 void
466 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
notify_on_boundary_vertex_creation(Vertex * v,const X_monotone_curve_2 & xc,Arr_curve_end ind,Arr_parameter_space CGAL_assertion_code (ps_x),Arr_parameter_space ps_y)467 notify_on_boundary_vertex_creation(Vertex* v,
468                                    const X_monotone_curve_2& xc,
469                                    Arr_curve_end ind,
470                                    Arr_parameter_space
471                                      CGAL_assertion_code(ps_x),
472                                    Arr_parameter_space ps_y)
473 {
474   // std::cout << "notify_on_boundary_vertex_creation()" << std::endl;
475   if (ps_y == ARR_BOTTOM_BOUNDARY) {
476     m_south_pole = v;
477     return;
478   }
479   if (ps_y == ARR_TOP_BOUNDARY) {
480     m_north_pole = v;
481     return;
482   }
483   CGAL_assertion(ps_x != ARR_INTERIOR);
484   const Point_2& key = (ind == ARR_MIN_END) ?
485     m_geom_traits->construct_min_vertex_2_object()(xc) :
486     m_geom_traits->construct_max_vertex_2_object()(xc);
487   m_boundary_vertices.insert(Vertex_value(key, v));
488 }
489 
490 template <typename GeomTraits, typename Dcel>
491 bool Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
let_me_decide_the_outer_ccb(std::pair<CGAL::Sign,CGAL::Sign> signs1,std::pair<CGAL::Sign,CGAL::Sign> signs2,bool & swap_predecessors)492 let_me_decide_the_outer_ccb(std::pair< CGAL::Sign, CGAL::Sign> signs1,
493                             std::pair< CGAL::Sign, CGAL::Sign> signs2,
494                             bool& swap_predecessors) const
495 {
496   // no perimetric in top-bottom for first loop
497   CGAL_precondition(signs1.second == CGAL::ZERO);
498 
499   // no perimetric in top-bottom for second loop
500   CGAL_precondition(signs2.second == CGAL::ZERO);
501 
502   // choose prev1 to define outer ccb of new face if it is a non-perimetric loop,
503   // otherwise choose prev2
504   // TODO what if both are non-zero? does it occur?
505   // TODO EBEB check this!!!!
506   swap_predecessors = (signs2.first != CGAL::POSITIVE);
507 
508   // but only if the at least one of the loops is perimetric, otherwise return
509   // false to let leftmost-vertex decide which becomes part of the new outer ccb
510   return (signs1.first != CGAL::ZERO) || (signs2.first != CGAL::ZERO);
511 }
512 
513 /*! \brief given a curve end with boundary conditions and a face that contains
514  * the interior of the curve, find a place for a boundary vertex that will
515  * represent the curve end along the face boundary */
516 template <typename GeomTraits, typename Dcel>
517 boost::optional
518   <boost::variant
519     <typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Vertex*,
520      typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Halfedge*> >
521 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
place_boundary_vertex(Face *,const X_monotone_curve_2 & xc,Arr_curve_end ind,Arr_parameter_space ps_x,Arr_parameter_space ps_y)522 place_boundary_vertex(Face* /* f */,
523                       const X_monotone_curve_2& xc, Arr_curve_end ind,
524                       Arr_parameter_space
525 #if !defined(CGAL_NO_ASSERTIONS)
526                       ps_x
527 #endif
528                       ,
529                       Arr_parameter_space ps_y)
530 {
531   typedef boost::variant<Vertex*, Halfedge*>    Non_optional_result;
532   typedef boost::optional<Non_optional_result>  Result;
533 
534   // std::cout << "place_boundary_vertex()" << std::endl;
535   if (ps_y == ARR_BOTTOM_BOUNDARY) {
536     if (m_south_pole == nullptr) return boost::none;
537     return Result(Non_optional_result(m_south_pole));
538   }
539 
540   if (ps_y == ARR_TOP_BOUNDARY) {
541     if (m_north_pole == nullptr) return boost::none;
542     return Result(Non_optional_result(m_north_pole));
543   }
544 
545   CGAL_assertion((ps_x == ARR_LEFT_BOUNDARY) || (ps_x == ARR_RIGHT_BOUNDARY));
546 
547   const Point_2& key = (ind == ARR_MIN_END) ?
548     m_geom_traits->construct_min_vertex_2_object()(xc) :
549     m_geom_traits->construct_max_vertex_2_object()(xc);
550   auto it = m_boundary_vertices.find(key);
551   if (it != m_boundary_vertices.end()) {
552     Vertex* v = it->second;
553     return Result(Non_optional_result(v));
554   }
555 
556   // The vertex hasn't been created yet, return a null object:
557   return boost::none;
558 }
559 
560 /*! \brief locate the predecessor halfedge for the given curve around a given
561  * vertex with boundary conditions. */
562 template <typename GeomTraits, typename Dcel>
563 typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Halfedge*
564 Arr_spherical_topology_traits_2<GeomTraits,Dcel>::
locate_around_boundary_vertex(Vertex * v,const X_monotone_curve_2 & xc,Arr_curve_end ind,Arr_parameter_space ps_x,Arr_parameter_space ps_y)565 locate_around_boundary_vertex(Vertex* v,
566                               const X_monotone_curve_2& xc,
567                               Arr_curve_end ind,
568                               Arr_parameter_space
569 #if !defined(CGAL_NO_ASSERTIONS)
570                               ps_x
571 #endif
572                               ,
573                               Arr_parameter_space ps_y) const
574 {
575   // std::cout << "locate_around_boundary_vertex()" << std::endl;
576   if (ps_y == ARR_BOTTOM_BOUNDARY) {
577     CGAL_assertion(v == m_south_pole);
578     return (_locate_around_pole(m_south_pole, xc, ind));
579   }
580 
581   if (ps_y == ARR_TOP_BOUNDARY) {
582     CGAL_assertion(v == m_north_pole);
583     return (_locate_around_pole(m_north_pole, xc, ind));
584   }
585 
586   CGAL_assertion((ps_x == ARR_LEFT_BOUNDARY) || (ps_x == ARR_RIGHT_BOUNDARY));
587 
588   return (_locate_around_vertex_on_discontinuity(v, xc, ind));
589 }
590 
591 /*! \brief locates a DCEL feature that contains a given curve end. */
592 template <typename GeomTraits, typename Dcel>
593 boost::variant
594 <typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Vertex*,
595  typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Halfedge*,
596  typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Face*>
597 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
locate_curve_end(const X_monotone_curve_2 & xc,Arr_curve_end ind,Arr_parameter_space ps_x,Arr_parameter_space ps_y)598 locate_curve_end(const X_monotone_curve_2& xc, Arr_curve_end ind,
599                  Arr_parameter_space
600 #if !defined(CGAL_NO_ASSERTIONS)
601                  ps_x
602 #endif
603                  ,
604                  Arr_parameter_space ps_y)
605 {
606   typedef boost::variant<Vertex*, Halfedge*, Face*>     Result;
607   // Act according to the boundary conditions.
608   if (ps_y == ARR_TOP_BOUNDARY) {
609     // In case the curve end coincides with the north pole, return the vertex
610     // representing the north pole, if one exists. Otherwise, return the face
611     // containing this pole (the spherical face).
612     if (m_north_pole != nullptr) return Result(m_north_pole);
613     return Result(m_spherical_face);
614   }
615 
616   typename Vertex_map::iterator it;
617   Vertex* v = nullptr;
618 
619   if (ps_y == ARR_BOTTOM_BOUNDARY) {
620     // In case the curve end coincides with the south pole, return the vertex
621     // representing the south pole, if one exists. Otherwise, search for the
622     // face containing this pole.
623     if (m_south_pole != nullptr) return Result(m_south_pole);
624     it = m_boundary_vertices.begin();
625   }
626   else {
627     CGAL_assertion((ps_x == ARR_LEFT_BOUNDARY) || (ps_x == ARR_RIGHT_BOUNDARY));
628 
629     // Check if the given curve end is incident to a vertex on the line of
630     // discontinuity. If so, return this vertex. Otherwise, locate the first
631     // vertex above it.
632     const Point_2& key = (ind == ARR_MIN_END) ?
633       m_geom_traits->construct_min_vertex_2_object()(xc) :
634       m_geom_traits->construct_max_vertex_2_object()(xc);
635     it = m_boundary_vertices.find(key);
636     if (it != m_boundary_vertices.end()) {
637       v = it->second;
638       return Result(v);
639     }
640 
641     it = m_boundary_vertices.lower_bound(key);
642   }
643 
644   // At this point, the iterator it points to a vertex on the line of
645   // discontinuity that is strictly above the curve end. If there is none,
646   // we know the curve end is contained in the spherical face. Otherwise,
647   // we return the face that lies below the vertex v.
648   if (it == m_boundary_vertices.end()) return Result(m_spherical_face);
649 
650   v = it->second;
651   return Result(_face_below_vertex_on_discontinuity(v));
652 }
653 
654 /*! \brief determines whether a given boundary vertex is redundant */
655 template <typename GeomTraits, typename Dcel>
656 bool Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
is_redundant(const Vertex * v)657 is_redundant(const Vertex* v) const
658 { return (v->halfedge() == nullptr); }
659 
660 /* \brief erases a given redundant vertex */
661 template <typename GeomTraits, typename Dcel>
662 typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Halfedge*
663 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
erase_redundant_vertex(Vertex * v)664 erase_redundant_vertex(Vertex* v)
665 {
666   const Arr_parameter_space ps_y = v->parameter_space_in_y();
667   if (ps_y == ARR_BOTTOM_BOUNDARY) {
668     m_south_pole = nullptr;
669     return nullptr;
670   }
671   if (ps_y == ARR_TOP_BOUNDARY) {
672     m_north_pole = nullptr;
673     return nullptr;
674   }
675   CGAL_assertion_code(Arr_parameter_space ps_x = v->parameter_space_in_x());
676   CGAL_assertion(ps_x != ARR_INTERIOR);
677   m_boundary_vertices.erase(v->point());
678   return nullptr;
679 }
680 
681 /*! \brief obtains the curve associated with a boundary vertex */
682 template <typename GeomTraits, typename Dcel>
683 const typename
684 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::X_monotone_curve_2&
685 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
_curve(const Vertex * v,Arr_curve_end & ind)686 _curve(const Vertex* v, Arr_curve_end& ind) const
687 {
688   // std::cout << "curve()" << std::endl;
689   const Halfedge* he = v->halfedge();
690   ind = (he->direction() == ARR_LEFT_TO_RIGHT) ? ARR_MAX_END : ARR_MIN_END;
691   return he->curve();
692 }
693 
694 /*! \brief returns the halfedge, the target vertex of which is given, that is
695  * the predecessor of a halfedge, the curve of which is given, that is about
696  * to be inserted into the dcel.
697  */
698 template <typename GeomTraits, typename Dcel>
699 typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Halfedge*
700 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
_locate_around_vertex_on_discontinuity(Vertex * v,const X_monotone_curve_2 & xc,Arr_curve_end ind)701 _locate_around_vertex_on_discontinuity(Vertex* v,
702                                        const X_monotone_curve_2& xc,
703                                        Arr_curve_end ind) const
704 {
705   // If the vertex is isolated, there is no predecssor halfedge.
706   if (v->is_isolated()) return nullptr;
707 
708   // Get the first incident halfedge around v and the next halfedge.
709   Halfedge* first = v->halfedge();
710   Halfedge* curr = first;
711   CGAL_assertion(curr != nullptr);
712   Halfedge* next = curr->next()->opposite();
713 
714   // If is only one halfedge incident to v, return this halfedge as xc's
715   // predecessor:
716   if (curr == next) return curr;
717 
718   // Otherwise, we traverse the halfedges around v until we find the pair
719   // of adjacent halfedges between which we should insert xc.
720   typename Gt_adaptor_2::Is_between_cw_2 is_between_cw =
721     m_geom_traits->is_between_cw_2_object();
722   bool eq_curr, eq_next;
723 
724   while (!is_between_cw(xc, (ind == ARR_MIN_END), curr->curve(),
725                         (curr->direction() == ARR_RIGHT_TO_LEFT), next->curve(),
726                         (next->direction() == ARR_RIGHT_TO_LEFT), v->point(),
727                         eq_curr, eq_next))
728   {
729     // The curve must not be equal to one of the curves already incident to v.
730     CGAL_assertion(!eq_curr && !eq_next);
731 
732     // Move to the next pair of incident halfedges.
733     curr = next;
734     next = curr->next()->opposite();
735 
736     // Make sure we have not completed a full traversal around v without
737     // locating a place for the new curve xc.
738     CGAL_assertion(curr != first);
739   }
740 
741   // Return the halfedge we have located.
742   return curr;
743 }
744 
745 /*! \brief returns the halfedge, the target vertex of which is a given pole,
746  * that is the predecessor of a halfedge, the curve of which is given, that
747  * is about to be inserted into the dcel.
748  */
749 template <typename GeomTraits, typename Dcel>
750 typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Halfedge*
751 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
_locate_around_pole(Vertex * v,const X_monotone_curve_2 & xc,Arr_curve_end ind)752 _locate_around_pole(Vertex* v,
753                     const X_monotone_curve_2& xc, Arr_curve_end ind) const
754 {
755   CGAL_assertion((v == m_south_pole) || (v == m_north_pole));
756 
757   // std::cout << "locate_around_pole() " << ind << std::endl;
758   // If the vertex is isolated, return a null halfedge:
759   if (v->is_isolated()) return nullptr;
760 
761   // Get the first incident halfedge around v and the next halfedge:
762   Halfedge* first = v->halfedge();
763   Halfedge* curr = first;
764   CGAL_assertion(curr != nullptr);
765   Halfedge* next = curr->next()->opposite();
766 
767   // If there is only one halfedge, it is the predecessor, return it:
768   if (curr == next) return curr;
769 
770   // If we compare a curve and its successor around the south (resp. north)
771   // pole, the result LARGER (resp. SMALLER) indicates that the line of
772   // discontinuity is located in between the two curves.
773   const Comparison_result cross_res = (v == m_south_pole) ? LARGER : SMALLER;
774 
775   // Traverse all other halfedges, and compare their x-positions next to the
776   // pole with the query curve xc.
777   typename Gt_adaptor_2::Compare_x_curve_ends_2 cmp_x_curve_ends =
778     m_geom_traits->compare_x_curve_ends_2_object();
779   Arr_curve_end curr_end, next_end;
780   Comparison_result curr_res, next_res;
781   Comparison_result curr_next_res;
782 
783   curr_end =
784     (curr->direction() == ARR_RIGHT_TO_LEFT) ? ARR_MIN_END : ARR_MAX_END;
785   curr_res = cmp_x_curve_ends(xc, ind, curr->curve(), curr_end);
786   do {
787     next_end =
788       (next->direction() == ARR_RIGHT_TO_LEFT) ? ARR_MIN_END : ARR_MAX_END;
789     next_res = cmp_x_curve_ends(xc, ind, next->curve(), next_end);
790     curr_next_res =
791       cmp_x_curve_ends(curr->curve(), curr_end, next->curve(), next_end);
792     if (curr_next_res == cross_res) {
793       // The line of discontinuity must lie between curr and next, so the
794       // comparison result of xc with the two curves should be equal:
795       if (curr_res == next_res) return curr;
796     }
797     else {
798       // The line of discontinuity does not lie between curr and next, so the
799       // comparison results must be different if xc lies in between.
800       if (curr_res != next_res) return curr;
801     }
802 
803     // Move to the next halfedge around the pole.
804     curr = next;
805     curr_end = next_end;
806     curr_res = next_res;
807     next = curr->next()->opposite();
808   } while (curr != first);
809 
810   // We sould never reach here:
811   CGAL_error();
812   return nullptr;
813 }
814 
815 /*! \brief returns the face that lies below the given vertex, which lies
816  * on the line of discontinuity.
817  */
818 template <typename GeomTraits, typename Dcel>
819 typename Arr_spherical_topology_traits_2<GeomTraits, Dcel>::Face*
820 Arr_spherical_topology_traits_2<GeomTraits, Dcel>::
_face_below_vertex_on_discontinuity(Vertex * v)821 _face_below_vertex_on_discontinuity(Vertex* v) const
822 {
823   // If the vertex is isolated, just return the face that contains it.
824   if (v->is_isolated()) return (v->isolated_vertex()->face());
825 
826   // Get the first incident halfedge around v and the next halfedge.
827   Halfedge* first = v->halfedge();
828   Halfedge* curr = first;
829   CGAL_assertion(curr != nullptr);
830   Halfedge* next = curr->next()->opposite();
831 
832   // If there is only one halfedge incident to v, return its incident face.
833   if (curr == next)
834     return ((curr->is_on_inner_ccb()) ?
835             curr->inner_ccb()->face() : curr->outer_ccb()->face());
836 
837   // Otherwise, we traverse the halfedges around v and locate the first
838   // halfedge we encounter if we go from "6 o'clock" clockwise.
839   // First locate the lower left and the top right halfedges around v.
840   typename Gt_adaptor_2::Compare_y_at_x_right_2 cmp_y_at_x_op_right =
841     m_geom_traits->compare_y_at_x_right_2_object();
842   typename Gt_adaptor_2::Compare_y_at_x_left_2  cmp_y_at_x_op_left =
843     m_geom_traits->compare_y_at_x_left_2_object();
844 
845   Halfedge* lowest_left = nullptr;
846   Halfedge* top_right = nullptr;
847 
848   do {
849     // Check whether the current halfedge is defined to the left or to the
850     // right of the given vertex.
851     if (curr->direction() == ARR_LEFT_TO_RIGHT) {
852       // The curve associated with the current halfedge is defined to the left
853       // of v.
854       if (lowest_left == nullptr ||
855           cmp_y_at_x_op_left(curr->curve(), lowest_left->curve(), v->point())
856           == SMALLER)
857       {
858         lowest_left = curr;
859       }
860     }
861     else {
862       // The curve associated with the current halfedge is defined to the right
863       // of v.
864       if (top_right == nullptr ||
865           cmp_y_at_x_op_right(curr->curve(), top_right->curve(), v->point()) ==
866           LARGER)
867       {
868         top_right = curr;
869       }
870     }
871 
872     // Move to the next halfedge around the vertex.
873     curr = curr->next()->opposite();
874   } while (curr != first);
875 
876   // The first halfedge we encounter is the lowest to the left, but if there
877   // is no edge to the left, we first encounter the topmost halfedge to the
878   // right. Note that as the halfedge we located has v as its target, we now
879   // have to return its twin.
880   first =
881     (lowest_left != nullptr) ? lowest_left->opposite() : top_right->opposite();
882   // std::cout << "first: " << first->opposite()->vertex()->point() << " => "
883   //           << first->vertex()->point() << std::endl;
884 
885   // Face* f = (first->is_on_inner_ccb()) ?
886   //   first->inner_ccb()->face() : first->outer_ccb()->face();
887   // std::cout << "outer: " << f->number_of_outer_ccbs() << std::endl;
888   // std::cout << "inner: " << f->number_of_inner_ccbs() << std::endl;
889   // Return the incident face.
890   return ((first->is_on_inner_ccb()) ?
891           first->inner_ccb()->face() : first->outer_ccb()->face());
892 }
893 
894 } //namespace CGAL
895 
896 #endif
897