1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
4 
5 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8 
9 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP
10 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP
11 
12 #include <algorithm>
13 
14 #include <boost/geometry/algorithms/detail/ring_identifier.hpp>
15 #include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
16 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
17 #include <boost/geometry/algorithms/detail/recalculate.hpp>
18 
19 #include <boost/geometry/policies/robustness/robust_point_type.hpp>
20 #include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
21 #include <boost/geometry/policies/robustness/robust_type.hpp>
22 
23 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
24 #include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
25 #endif
26 
27 #include <boost/geometry/geometries/point.hpp>
28 #include <boost/geometry/geometries/segment.hpp>
29 
30 
31 // TODO: the approach below should be completely replaced by the new
32 // get_left_turns, to keep the outgoing vector which has open space one of its
33 // sides.
34 
35 
36 namespace boost { namespace geometry
37 {
38 
39 #ifndef DOXYGEN_NO_DETAIL
40 namespace detail { namespace overlay
41 {
42 
43 
44 template
45 <
46     typename TurnPoints,
47     typename Indexed,
48     typename Geometry1, typename Geometry2,
49     typename RobustPolicy,
50     bool Reverse1, bool Reverse2,
51     typename Strategy
52 >
53 struct sort_in_cluster
54 {
sort_in_clusterboost::geometry::detail::overlay::sort_in_cluster55     inline sort_in_cluster(TurnPoints const& turn_points
56             , Geometry1 const& geometry1
57             , Geometry2 const& geometry2
58             , RobustPolicy const& robust_policy
59             , Strategy const& strategy)
60         : m_turn_points(turn_points)
61         , m_geometry1(geometry1)
62         , m_geometry2(geometry2)
63         , m_rescale_policy(robust_policy)
64         , m_strategy(strategy)
65     {}
66 
67 private :
68 
69     TurnPoints const& m_turn_points;
70     Geometry1 const& m_geometry1;
71     Geometry2 const& m_geometry2;
72     RobustPolicy const& m_rescale_policy;
73     Strategy const& m_strategy;
74 
75     typedef typename Indexed::type turn_operation_type;
76     typedef typename geometry::point_type<Geometry1>::type point_type;
77 
78     typedef typename geometry::robust_point_type
79     <
80         point_type,
81         RobustPolicy
82     >::type robust_point_type;
83 
default_orderboost::geometry::detail::overlay::sort_in_cluster84     inline bool default_order(Indexed const& left, Indexed const& right) const
85     {
86         // We've nothing to sort on. Take the indexes
87         return left.turn_index < right.turn_index;
88     }
89 
90     // Still necessary in some situations,
91     // for example #case_102_multi, #case_107_multi, #case_recursive_boxes_3
get_situation_mapboost::geometry::detail::overlay::sort_in_cluster92     inline void get_situation_map(Indexed const& left, Indexed const& right,
93                               robust_point_type& pi_rob, robust_point_type& pj_rob,
94                               robust_point_type& ri_rob, robust_point_type& rj_rob,
95                               robust_point_type& si_rob, robust_point_type& sj_rob) const
96     {
97         point_type pi, pj, ri, rj, si, sj;
98 
99         geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
100             left.subject->seg_id,
101             pi, pj);
102         geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
103             *left.other_seg_id,
104             ri, rj);
105         geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
106             *right.other_seg_id,
107             si, sj);
108 
109         geometry::recalculate(pi_rob, pi, m_rescale_policy);
110         geometry::recalculate(pj_rob, pj, m_rescale_policy);
111         geometry::recalculate(ri_rob, ri, m_rescale_policy);
112         geometry::recalculate(rj_rob, rj, m_rescale_policy);
113         geometry::recalculate(si_rob, si, m_rescale_policy);
114         geometry::recalculate(sj_rob, sj, m_rescale_policy);
115     }
116 
117 #if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
118     // This method was still called but did no effect whatsoever on the results,
119     // with or without robustness-rescaling.
120     // Probable cause: we rescale in this file ourselves, ignoring passed policy
121     // TODO: check this more.
122     // Besides this, it currently does not compile for yet unknown reasons
123     // (does not find specialization for segment_ratio_type)
124     // It is currently only called from the Unit Test "multi_intersection.cpp"
125 
126     // Determine how p/r and p/s are located.
overlap_infoboost::geometry::detail::overlay::sort_in_cluster127     inline void overlap_info(
128         robust_point_type const& pi, robust_point_type const& pj,
129         robust_point_type const& ri, robust_point_type const& rj,
130         robust_point_type const& si, robust_point_type const& sj,
131         bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) const
132     {
133         // Determine how p/r and p/s are located.
134         // One of them is coming from opposite direction.
135 
136         typedef segment_intersection_points
137                 <
138                     point_type,
139                     typename segment_ratio_type
140                     <
141                         point_type, RobustPolicy
142                     >::type
143                 > intersection_return_type;
144 
145         typedef strategy::intersection::relate_cartesian_segments
146             <
147                 policies::relate::segments_intersection_points
148                     <
149                         intersection_return_type
150                     >
151             > policy;
152 
153         typedef model::referring_segment<robust_point_type const> segment_type;
154         segment_type p(pi, pj);
155         segment_type r(ri, rj);
156         segment_type s(si, sj);
157 
158         // Get the intersection point (or two points)
159         intersection_return_type pr = policy::apply(p, r, m_rescale_policy, pi, pj, ri, rj);
160         intersection_return_type ps = policy::apply(p, s, m_rescale_policy, pi, pj, si, sj);
161         intersection_return_type rs = policy::apply(r, s, m_rescale_policy, ri, rj, si, sj);
162 
163         // Check on overlap
164         pr_overlap = pr.count == 2;
165         ps_overlap = ps.count == 2;
166         rs_overlap = rs.count == 2;
167     }
168 #endif
169 
170 
171 #ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES
debug_considerboost::geometry::detail::overlay::sort_in_cluster172     inline void debug_consider(int order, Indexed const& left,
173             Indexed const& right, std::string const& header,
174             bool skip = true,
175             std::string const& extra = "", bool ret = false
176         ) const
177     {
178         if (skip) return;
179 
180         std::cout << "Case: " << header << " for " << left.turn_index << " / " << right.turn_index << std::endl;
181 
182         robust_point_type pi, pj, ri, rj, si, sj;
183         get_situation_map(left, right, pi, pj, ri, rj, si, sj);
184 
185 #if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
186         bool prc = false, psc = false, rsc = false;
187         overlap_info(pi, pj, ri, rj, si, sj, prc, psc, rsc);
188 #endif
189 
190         int const side_ri_p = m_strategy.apply(pi, pj, ri);
191         int const side_rj_p = m_strategy.apply(pi, pj, rj);
192         int const side_si_p = m_strategy.apply(pi, pj, si);
193         int const side_sj_p = m_strategy.apply(pi, pj, sj);
194         int const side_si_r = m_strategy.apply(ri, rj, si);
195         int const side_sj_r = m_strategy.apply(ri, rj, sj);
196 
197 #ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES_MORE
198         std::cout << " Segment p:" << geometry::wkt(pi) << " .. " << geometry::wkt(pj) << std::endl;
199         std::cout << " Segment r:" << geometry::wkt(ri) << " .. " << geometry::wkt(rj) << std::endl;
200         std::cout << " Segment s:" << geometry::wkt(si) << " .. " << geometry::wkt(sj) << std::endl;
201 
202         std::cout << " r//p: " << side_ri_p << " / " << side_rj_p << std::endl;
203         std::cout << " s//p: " << side_si_p << " / " << side_sj_p << std::endl;
204         std::cout << " s//r: " << side_si_r << " / " << side_sj_r << std::endl;
205 #endif
206 
207         std::cout << header
208                 //<< " order: " << order
209                 << " ops: " << operation_char(left.subject->operation)
210                     << "/" << operation_char(right.subject->operation)
211                 << " ri//p: " << side_ri_p
212                 << " si//p: " << side_si_p
213                 << " si//r: " << side_si_r
214 #if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
215                 << " cnts: " << int(prc) << ","  << int(psc) << "," << int(rsc)
216 #endif
217                 //<< " idx: " << left.turn_index << "/" << right.turn_index
218                 ;
219 
220         if (! extra.empty())
221         {
222             std::cout << " " << extra << " " << (ret ? "true" : "false");
223         }
224         std::cout << std::endl;
225     }
226 #else
debug_considerboost::geometry::detail::overlay::sort_in_cluster227     inline void debug_consider(int, Indexed const& ,
228             Indexed const& , std::string const& ,
229             bool = true,
230             std::string const& = "", bool = false
231         ) const
232     {}
233 #endif
234 
235 
236     // ux/ux
consider_ux_uxboost::geometry::detail::overlay::sort_in_cluster237     inline bool consider_ux_ux(Indexed const& left,
238             Indexed const& right
239             , std::string const& // header
240         ) const
241     {
242         bool ret = left.turn_index < right.turn_index;
243 
244         // In combination of u/x, x/u: take first union, then blocked.
245         // Solves #88, #61, #56, #80
246         if (left.subject->operation == operation_union
247             && right.subject->operation == operation_blocked)
248         {
249             ret = true;
250         }
251         else if (left.subject->operation == operation_blocked
252             && right.subject->operation == operation_union)
253         {
254             ret = false;
255         }
256         else
257         {
258 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
259             std::cout << "ux/ux unhandled" << std::endl;
260 #endif
261         }
262 
263         //debug_consider(0, left, right, header, false, "-> return ", ret);
264 
265         return ret;
266     }
267 
consider_iu_uxboost::geometry::detail::overlay::sort_in_cluster268     inline bool consider_iu_ux(Indexed const& left,
269             Indexed const& right,
270             int order // 1: iu first, -1: ux first
271             , std::string const& // header
272         ) const
273     {
274         bool ret = false;
275 
276         if (left.subject->operation == operation_union
277             && right.subject->operation == operation_union)
278         {
279             ret = order == 1;
280         }
281         else if (left.subject->operation == operation_union
282             && right.subject->operation == operation_blocked)
283         {
284             ret = true;
285         }
286         else if (right.subject->operation == operation_union
287             && left.subject->operation == operation_blocked)
288         {
289             ret = false;
290         }
291         else if (left.subject->operation == operation_union)
292         {
293             ret = true;
294         }
295         else if (right.subject->operation == operation_union)
296         {
297             ret = false;
298         }
299         else
300         {
301 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
302             // this still happens in the traverse.cpp test
303             std::cout << " iu/ux unhandled" << std::endl;
304 #endif
305             ret = order == 1;
306         }
307 
308         //debug_consider(0, left, right, header, false, "-> return", ret);
309         return ret;
310     }
311 
consider_iu_ixboost::geometry::detail::overlay::sort_in_cluster312     inline bool consider_iu_ix(Indexed const& left,
313             Indexed const& right,
314             int order // 1: iu first, -1: ix first
315             , std::string const& // header
316         ) const
317     {
318         //debug_consider(order, left, right, header, false, "iu/ix");
319 
320         return left.subject->operation == operation_intersection
321                 && right.subject->operation == operation_intersection ? order == 1
322             : left.subject->operation == operation_intersection ? false
323             : right.subject->operation == operation_intersection ? true
324             : order == 1;
325     }
326 
consider_ix_ixboost::geometry::detail::overlay::sort_in_cluster327     inline bool consider_ix_ix(Indexed const& left, Indexed const& right
328             , std::string const& // header
329             ) const
330     {
331         // Take first intersection, then blocked.
332         if (left.subject->operation == operation_intersection
333             && right.subject->operation == operation_blocked)
334         {
335             return true;
336         }
337         else if (left.subject->operation == operation_blocked
338             && right.subject->operation == operation_intersection)
339         {
340             return false;
341         }
342 
343         // Default case, should not occur
344 
345 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
346         std::cout << "ix/ix unhandled" << std::endl;
347 #endif
348         //debug_consider(0, left, right, header, false, "-> return", ret);
349 
350         return default_order(left, right);
351     }
352 
353 
consider_iu_iuboost::geometry::detail::overlay::sort_in_cluster354     inline bool consider_iu_iu(Indexed const& left, Indexed const& right,
355                     std::string const& header, bool redo = false) const
356     {
357         //debug_consider(0, left, right, header);
358 
359         // In general, order it like "union, intersection".
360         if (left.subject->operation == operation_intersection
361             && right.subject->operation == operation_union)
362         {
363             //debug_consider(0, left, right, header, false, "i,u", false);
364             return false;
365         }
366         else if (left.subject->operation == operation_union
367             && right.subject->operation == operation_intersection)
368         {
369             //debug_consider(0, left, right, header, false, "u,i", true);
370             return true;
371         }
372 
373         robust_point_type pi, pj, ri, rj, si, sj;
374         get_situation_map(left, right, pi, pj, ri, rj, si, sj);
375 
376         int const side_ri_p = m_strategy.apply(pi, pj, ri);
377         int const side_si_p = m_strategy.apply(pi, pj, si);
378         int const side_si_r = m_strategy.apply(ri, rj, si);
379 
380         // Both located at same side (#58, pie_21_7_21_0_3)
381         if (side_ri_p * side_si_p == 1 && side_si_r != 0)
382         {
383             if (left.subject->operation == operation_union
384                 && right.subject->operation == operation_union)
385             {
386                 int const side_ri_s = m_strategy.apply(si, sj, ri);
387                 if (side_si_r == side_ri_s)
388                 {
389                     return default_order(left, right);
390                 }
391 
392                 // Take the most left one
393                 bool const ret = side_si_r == 1;
394                 //debug_consider(0, left, right, header, false, "same side", ret);
395                 return ret;
396             }
397         }
398 
399 
400         // Coming from opposite sides (#59, #99)
401         if (side_ri_p * side_si_p == -1)
402         {
403             bool ret = false;
404 
405             {
406                 ret = side_ri_p == 1; // #100
407                 debug_consider(0, left, right, header, false, "opp.", ret);
408                 return ret;
409             }
410 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
411             std::cout << " iu/iu coming from opposite unhandled" << std::endl;
412 #endif
413         }
414 
415 #if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
416         // We need EXTRA information here: are p/r/s overlapping?
417         bool pr_ov = false, ps_ov = false, rs_ov = false;
418         overlap_info(pi, pj, ri, rj, si, sj, pr_ov, ps_ov, rs_ov);
419 #else
420         // std::cout << "Boost.Geometry: skipping overlap_info" << std::endl;
421 #endif
422 
423         // One coming from right (#83,#90)
424         // One coming from left (#90, #94, #95)
425         if (side_si_r != 0 && (side_ri_p != 0 || side_si_p != 0))
426         {
427             int const side_ri_s = m_strategy.apply(si, sj, ri);
428             if (side_si_r == side_ri_s)
429             {
430                 return default_order(left, right);
431             }
432 
433             bool ret = false;
434 
435 #if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
436             if (pr_ov || ps_ov)
437             {
438                 int r = side_ri_p != 0 ? side_ri_p : side_si_p;
439                 ret = r * side_si_r == 1;
440             }
441             else
442 #endif
443             {
444                 ret = side_si_r == 1;
445             }
446 
447             debug_consider(0, left, right, header, false, "left or right", ret);
448             return ret;
449         }
450 
451         // All aligned (#92, #96)
452         if (side_ri_p == 0 && side_si_p == 0 && side_si_r == 0)
453         {
454             // One of them is coming from opposite direction.
455 
456             // Take the one NOT overlapping
457             bool ret = false;
458             bool found = false;
459 #if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
460             if (pr_ov && ! ps_ov)
461             {
462                 ret = true;
463                 found = true;
464             }
465             else if (!pr_ov && ps_ov)
466             {
467                 ret = false;
468                 found = true;
469             }
470 #endif
471 
472             debug_consider(0, left, right, header, false, "aligned", ret);
473             if (found)
474             {
475                 return ret;
476             }
477         }
478 
479 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
480         std::cout << " iu/iu unhandled" << std::endl;
481         debug_consider(0, left, right, header, false, "unhandled", left.turn_index < right.turn_index);
482 #endif
483         if (! redo)
484         {
485             // In some cases behaviour is not symmetrical. TODO: fix this properly
486             //   OR: alternatively we might consider calling all these functions one-way anyway
487             return ! consider_iu_iu(right, left, header, true);
488         }
489 
490         return default_order(left, right);
491     }
492 
consider_iiboost::geometry::detail::overlay::sort_in_cluster493     inline bool consider_ii(Indexed const& left, Indexed const& right,
494                     std::string const& header) const
495     {
496         debug_consider(0, left, right, header);
497 
498         robust_point_type pi, pj, ri, rj, si, sj;
499         get_situation_map(left, right, pi, pj, ri, rj, si, sj);
500 
501         int const side_ri_p = m_strategy.apply(pi, pj, ri);
502         int const side_si_p = m_strategy.apply(pi, pj, si);
503 
504         // Two other points are (mostly) lying both right of the considered segment
505         // Take the most left one
506         int const side_si_r = m_strategy.apply(ri, rj, si);
507         if (side_ri_p == -1
508             && side_si_p == -1
509             && side_si_r != 0)
510         {
511             bool const ret = side_si_r != 1;
512             return ret;
513         }
514         return default_order(left, right);
515     }
516 
517 
518 public :
operator ()boost::geometry::detail::overlay::sort_in_cluster519     inline bool operator()(Indexed const& left, Indexed const& right) const
520     {
521         if ((m_turn_points[left.turn_index].discarded || left.discarded)
522             && (m_turn_points[right.turn_index].discarded || right.discarded))
523         {
524             return default_order(left, right);
525         }
526         else if (m_turn_points[left.turn_index].discarded || left.discarded)
527         {
528             // Be careful to sort discarded first, then all others
529             return true;
530         }
531         else if (m_turn_points[right.turn_index].discarded || right.discarded)
532         {
533             // See above so return false here such that right (discarded)
534             // is sorted before left (not discarded)
535             return false;
536         }
537         else if (m_turn_points[left.turn_index].combination(operation_blocked, operation_union)
538                 && m_turn_points[right.turn_index].combination(operation_blocked, operation_union))
539         {
540             // ux/ux
541             return consider_ux_ux(left, right, "ux/ux");
542         }
543         else if (m_turn_points[left.turn_index].both(operation_union)
544             && m_turn_points[right.turn_index].both(operation_union))
545         {
546             // uu/uu, Order is arbitrary
547             // Note: uu/uu is discarded now before so this point will
548             //       not be reached.
549             return default_order(left, right);
550         }
551         else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union)
552                 && m_turn_points[right.turn_index].combination(operation_intersection, operation_union))
553         {
554             return consider_iu_iu(left, right, "iu/iu");
555         }
556         else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked)
557                 && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked))
558         {
559             return consider_ix_ix(left, right, "ix/ix");
560         }
561         else if (m_turn_points[left.turn_index].both(operation_intersection)
562                 && m_turn_points[right.turn_index].both(operation_intersection))
563         {
564             return consider_ii(left, right, "ii/ii");
565         }
566         else  if (m_turn_points[left.turn_index].combination(operation_union, operation_blocked)
567                 && m_turn_points[right.turn_index].combination(operation_intersection, operation_union))
568         {
569             return consider_iu_ux(left, right, -1, "ux/iu");
570         }
571         else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union)
572                 && m_turn_points[right.turn_index].combination(operation_union, operation_blocked))
573         {
574             return consider_iu_ux(left, right, 1, "iu/ux");
575         }
576         else  if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked)
577                 && m_turn_points[right.turn_index].combination(operation_intersection, operation_union))
578         {
579             return consider_iu_ix(left, right, 1, "ix/iu");
580         }
581         else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union)
582                 && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked))
583         {
584             return consider_iu_ix(left, right, -1, "iu/ix");
585         }
586         else if (m_turn_points[left.turn_index].method != method_equal
587             && m_turn_points[right.turn_index].method == method_equal
588             )
589         {
590             // If one of them was EQUAL or CONTINUES, it should always come first
591             return false;
592         }
593         else if (m_turn_points[left.turn_index].method == method_equal
594             && m_turn_points[right.turn_index].method != method_equal
595             )
596         {
597             return true;
598         }
599 
600         // Now we have no clue how to sort.
601 
602 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
603         std::cout << " Consider: " << operation_char(m_turn_points[left.turn_index].operations[0].operation)
604                 << operation_char(m_turn_points[left.turn_index].operations[1].operation)
605                 << "/" << operation_char(m_turn_points[right.turn_index].operations[0].operation)
606                 << operation_char(m_turn_points[right.turn_index].operations[1].operation)
607                 << " " << " Take " << left.turn_index << " < " << right.turn_index
608                 << std::endl;
609 #endif
610 
611         return default_order(left, right);
612     }
613 };
614 
615 
616 
617 template
618 <
619     typename IndexType,
620     typename Iterator,
621     typename TurnPoints,
622     typename Geometry1,
623     typename Geometry2,
624     typename Strategy
625 >
inspect_cluster(Iterator begin_cluster,Iterator end_cluster,TurnPoints & turn_points,operation_type,Geometry1 const &,Geometry2 const &,Strategy const &)626 inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster,
627             TurnPoints& turn_points,
628             operation_type ,
629             Geometry1 const& , Geometry2 const& ,
630             Strategy const& )
631 {
632     int count = 0;
633 
634     // Make an analysis about all occuring cases here.
635     std::map<std::pair<operation_type, operation_type>, int> inspection;
636     for (Iterator it = begin_cluster; it != end_cluster; ++it)
637     {
638         operation_type first = turn_points[it->turn_index].operations[0].operation;
639         operation_type second = turn_points[it->turn_index].operations[1].operation;
640         if (first > second)
641         {
642             std::swap(first, second);
643         }
644         inspection[std::make_pair(first, second)]++;
645         count++;
646     }
647 
648 
649     bool keep_cc = false;
650 
651     // Decide about which is going to be discarded here.
652     if (inspection[std::make_pair(operation_union, operation_union)] == 1
653         && inspection[std::make_pair(operation_continue, operation_continue)] == 1)
654     {
655         // In case of uu/cc, discard the uu, that indicates a tangency and
656         // inclusion would disturb the (e.g.) cc-cc-cc ordering
657         // NOTE: uu is now discarded anyhow.
658         keep_cc = true;
659     }
660     else if (count == 2
661         && inspection[std::make_pair(operation_intersection, operation_intersection)] == 1
662         && inspection[std::make_pair(operation_union, operation_intersection)] == 1)
663     {
664         // In case of ii/iu, discard the iu. The ii should always be visited,
665         // Because (in case of not discarding iu) correctly ordering of ii/iu appears impossible
666         for (Iterator it = begin_cluster; it != end_cluster; ++it)
667         {
668             if (turn_points[it->turn_index].combination(operation_intersection, operation_union))
669             {
670                 it->discarded = true;
671             }
672         }
673     }
674 
675     // Discard any continue turn, unless it is the only thing left
676     //    (necessary to avoid cc-only rings, all being discarded
677     //    e.g. traversal case #75)
678     int nd_count= 0, cc_count = 0;
679     for (Iterator it = begin_cluster; it != end_cluster; ++it)
680     {
681         if (! it->discarded)
682         {
683             nd_count++;
684             if (turn_points[it->turn_index].both(operation_continue))
685             {
686                 cc_count++;
687             }
688         }
689     }
690 
691     if (nd_count == cc_count)
692     {
693         keep_cc = true;
694     }
695 
696     if (! keep_cc)
697     {
698         for (Iterator it = begin_cluster; it != end_cluster; ++it)
699         {
700             if (turn_points[it->turn_index].both(operation_continue))
701             {
702                 it->discarded = true;
703             }
704         }
705     }
706 }
707 
708 
709 template
710 <
711     typename IndexType,
712     bool Reverse1, bool Reverse2,
713     typename Iterator,
714     typename TurnPoints,
715     typename Geometry1,
716     typename Geometry2,
717     typename RobustPolicy,
718     typename Strategy
719 >
handle_cluster(Iterator begin_cluster,Iterator end_cluster,TurnPoints & turn_points,operation_type for_operation,Geometry1 const & geometry1,Geometry2 const & geometry2,RobustPolicy & robust_policy,Strategy const & strategy)720 inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster,
721             TurnPoints& turn_points,
722             operation_type for_operation,
723             Geometry1 const& geometry1, Geometry2 const& geometry2,
724             RobustPolicy& robust_policy,
725             Strategy const& strategy)
726 {
727     // First inspect and (possibly) discard rows
728     inspect_cluster<IndexType>(begin_cluster, end_cluster, turn_points,
729             for_operation, geometry1, geometry2, strategy);
730 
731 
732     // Then sort this range (discarded rows will be ordered first and will be removed in enrich_assign)
733     std::sort(begin_cluster, end_cluster,
734                 sort_in_cluster
735                     <
736                         TurnPoints,
737                         IndexType,
738                         Geometry1, Geometry2,
739                         RobustPolicy,
740                         Reverse1, Reverse2,
741                         Strategy
742                     >(turn_points, geometry1, geometry2, robust_policy, strategy));
743 
744 #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
745     typedef typename IndexType::type operations_type;
746     operations_type const& op = turn_points[begin_cluster->turn_index].operations[begin_cluster->operation_index];
747     std::cout << std::endl << "Clustered points on equal distance " << op.fraction << std::endl;
748 
749     std::cout << "->Indexes ";
750     for (Iterator it = begin_cluster; it != end_cluster; ++it)
751     {
752         std::cout << " " << it->turn_index;
753     }
754     std::cout << std::endl << "->Methods: ";
755     for (Iterator it = begin_cluster; it != end_cluster; ++it)
756     {
757         std::cout << " " << method_char(turn_points[it->turn_index].method);
758     }
759     std::cout << std::endl << "->Operations: ";
760     for (Iterator it = begin_cluster; it != end_cluster; ++it)
761     {
762         std::cout << " " << operation_char(turn_points[it->turn_index].operations[0].operation)
763             << operation_char(turn_points[it->turn_index].operations[1].operation);
764     }
765     std::cout << std::endl << "->Discarded: ";
766     for (Iterator it = begin_cluster; it != end_cluster; ++it)
767     {
768         std::cout << " " << (it->discarded ? "true" : "false");
769     }
770     std::cout << std::endl;
771         //<< "\tOn segments: "    << prev_op.seg_id  << " / "  << prev_op.other_id
772         //<< " and "  << op.seg_id << " / " << op.other_id
773         //<< geometry::distance(turn_points[prev->turn_index].point, turn_points[it->turn_index].point)
774 #endif
775 
776 }
777 
778 
779 }} // namespace detail::overlay
780 #endif //DOXYGEN_NO_DETAIL
781 
782 
783 }} // namespace boost::geometry
784 
785 
786 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP
787