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