1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
4 
5 // This file was modified by Oracle on 2013, 2014, 2017.
6 // Modifications copyright (c) 2013-2017 Oracle and/or its affiliates.
7 
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
9 
10 // Use, modification and distribution is subject to the Boost Software License,
11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
12 // http://www.boost.org/LICENSE_1_0.txt)
13 
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
16 
17 #include <boost/geometry/core/assert.hpp>
18 #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
19 #include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
20 
21 namespace boost { namespace geometry {
22 
23 #ifndef DOXYGEN_NO_DETAIL
24 namespace detail { namespace overlay {
25 
26 // SEGMENT_INTERSECTION RESULT
27 
28 //                   C  H0  H1  A0  A1   O              IP1 IP2
29 
30 // D0 and D1 == 0
31 
32 // |-------->        2   0   0   0   0   F              i/i x/x
33 // |-------->
34 //
35 // |-------->        2   0   0   0   0   T              i/x x/i
36 // <--------|
37 //
38 // |----->           1   0   0   0   0   T              x/x
39 //       <-----|
40 //
41 
42 // |--------->       2   0   0   0   1   T              i/x x/i
43 //      <----|
44 //
45 // |--------->       2   0   0   0   0   F              i/i x/x
46 //      |---->
47 //
48 // |--------->       2   0   0  -1   1   F              i/i u/x
49 // |---->
50 //
51 // |--------->       2   0   0  -1   0   T              i/x u/i
52 // <----|
53 
54 // |------->         2   0   0   1  -1   F   and        i/i x/u
55 //     |------->     2   0   0  -1   1   F   symetric   i/i u/x
56 // |------->
57 //
58 //     |------->     2   0   0  -1  -1   T              i/u u/i
59 // <-------|
60 //
61 // |------->         2   0   0   1   1   T              i/x x/i
62 //     <-------|
63 //
64 // |-------->        2   0   0  -1   1   F              i/i u/x
65 //   |---->
66 //
67 // |-------->        2   0   0  -1   1   T              i/x u/i
68 //   <----|
69 
70 //       |----->     1  -1  -1  -1  -1   T              u/u
71 // <-----|
72 //
73 //       |----->     1  -1   0  -1   0   F   and        u/x
74 // |----->           1   0  -1   0  -1   F   symetric   x/u
75 //       |----->
76 
77 // D0 or D1 != 0
78 
79 //          ^
80 //          |
81 //          +        1  -1   1  -1   1   F   and        u/x  (P is vertical)
82 // |-------->        1   1  -1   1  -1   F   symetric   x/u  (P is horizontal)
83 // ^
84 // |
85 // +
86 //
87 //          +
88 //          |
89 //          v
90 // |-------->        1   1   1   1   1   F              x/x  (P is vertical)
91 //
92 // ^
93 // |
94 // +
95 // |-------->        1  -1  -1  -1  -1   F              u/u  (P is vertical)
96 //
97 //      ^
98 //      |
99 //      +
100 // |-------->        1   0  -1   0  -1   F              u/u  (P is vertical)
101 //
102 //      +
103 //      |
104 //      v
105 // |-------->        1   0   1   0   1   F              u/x  (P is vertical)
106 //
107 
108 class linear_intersections
109 {
110 public:
111     template <typename Point1, typename Point2, typename IntersectionResult>
linear_intersections(Point1 const & pi,Point2 const & qi,IntersectionResult const & result,bool is_p_last,bool is_q_last)112     linear_intersections(Point1 const& pi,
113                          Point2 const& qi,
114                          IntersectionResult const& result,
115                          bool is_p_last, bool is_q_last)
116     {
117         int arrival_a = result.template get<1>().arrival[0];
118         int arrival_b = result.template get<1>().arrival[1];
119         bool same_dirs = result.template get<1>().dir_a == 0
120                       && result.template get<1>().dir_b == 0;
121 
122         if ( same_dirs )
123         {
124             if ( result.template get<0>().count == 2 )
125             {
126                 if ( ! result.template get<1>().opposite )
127                 {
128                     ips[0].p_operation = operation_intersection;
129                     ips[0].q_operation = operation_intersection;
130                     ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
131                     ips[1].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
132 
133                     ips[0].is_pi
134                         = equals::equals_point_point(
135                             pi, result.template get<0>().intersections[0]);
136                     ips[0].is_qi
137                         = equals::equals_point_point(
138                             qi, result.template get<0>().intersections[0]);
139                     ips[1].is_pj = arrival_a != -1;
140                     ips[1].is_qj = arrival_b != -1;
141                 }
142                 else
143                 {
144                     ips[0].p_operation = operation_intersection;
145                     ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
146                     ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
147                     ips[1].q_operation = operation_intersection;
148 
149                     ips[0].is_pi = arrival_b != 1;
150                     ips[0].is_qj = arrival_b != -1;
151                     ips[1].is_pj = arrival_a != -1;
152                     ips[1].is_qi = arrival_a != 1;
153                 }
154             }
155             else
156             {
157                 BOOST_GEOMETRY_ASSERT(result.template get<0>().count == 1);
158                 ips[0].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
159                 ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
160 
161                 ips[0].is_pi = arrival_a == -1;
162                 ips[0].is_qi = arrival_b == -1;
163                 ips[0].is_pj = arrival_a == 0;
164                 ips[0].is_qj = arrival_b == 0;
165             }
166         }
167         else
168         {
169             ips[0].p_operation = union_or_blocked_different_dirs(arrival_a, is_p_last);
170             ips[0].q_operation = union_or_blocked_different_dirs(arrival_b, is_q_last);
171 
172             ips[0].is_pi = arrival_a == -1;
173             ips[0].is_qi = arrival_b == -1;
174             ips[0].is_pj = arrival_a == 1;
175             ips[0].is_qj = arrival_b == 1;
176         }
177     }
178 
179     struct ip_info
180     {
ip_infoboost::geometry::detail::overlay::linear_intersections::ip_info181         inline ip_info()
182             : p_operation(operation_none), q_operation(operation_none)
183             , is_pi(false), is_pj(false), is_qi(false), is_qj(false)
184         {}
185 
186         operation_type p_operation, q_operation;
187         bool is_pi, is_pj, is_qi, is_qj;
188     };
189 
190     template <std::size_t I>
get() const191     ip_info const& get() const
192     {
193         BOOST_STATIC_ASSERT(I < 2);
194         return ips[I];
195     }
196 
197 private:
198 
199     // only if collinear (same_dirs)
union_or_blocked_same_dirs(int arrival,bool is_last)200     static inline operation_type union_or_blocked_same_dirs(int arrival, bool is_last)
201     {
202         if ( arrival == 1 )
203             return operation_blocked;
204         else if ( arrival == -1 )
205             return operation_union;
206         else
207             return is_last ? operation_blocked : operation_union;
208             //return operation_blocked;
209     }
210 
211     // only if not collinear (!same_dirs)
union_or_blocked_different_dirs(int arrival,bool is_last)212     static inline operation_type union_or_blocked_different_dirs(int arrival, bool is_last)
213     {
214         if ( arrival == 1 )
215             //return operation_blocked;
216             return is_last ? operation_blocked : operation_union;
217         else
218             return operation_union;
219     }
220 
221     ip_info ips[2];
222 };
223 
224 template <typename AssignPolicy, bool EnableFirst, bool EnableLast>
225 struct get_turn_info_for_endpoint
226 {
227     BOOST_STATIC_ASSERT(EnableFirst || EnableLast);
228 
229     template<typename Point1,
230              typename Point2,
231              typename TurnInfo,
232              typename IntersectionInfo,
233              typename OutputIterator
234     >
applyboost::geometry::detail::overlay::get_turn_info_for_endpoint235     static inline bool apply(Point1 const& pi, Point1 const& pj, Point1 const& pk,
236                              Point2 const& qi, Point2 const& qj, Point2 const& qk,
237                              bool is_p_first, bool is_p_last,
238                              bool is_q_first, bool is_q_last,
239                              TurnInfo const& tp_model,
240                              IntersectionInfo const& inters,
241                              method_type /*method*/,
242                              OutputIterator out)
243     {
244         std::size_t ip_count = inters.i_info().count;
245         // no intersection points
246         if ( ip_count == 0 )
247             return false;
248 
249         if ( !is_p_first && !is_p_last && !is_q_first && !is_q_last )
250             return false;
251 
252         linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last);
253 
254         bool append0_last
255             = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk,
256                                             is_p_first, is_p_last, is_q_first, is_q_last,
257                                             intersections.template get<0>(),
258                                             tp_model, inters, 0, out);
259 
260         // NOTE: opposite && ip_count == 1 may be true!
261         bool opposite = inters.d_info().opposite;
262 
263         // don't ignore only for collinear opposite
264         bool result_ignore_ip0 = append0_last && ( ip_count == 1 || !opposite );
265 
266         if ( intersections.template get<1>().p_operation == operation_none )
267             return result_ignore_ip0;
268 
269         bool append1_last
270             = analyse_segment_and_assign_ip(pi, pj, pk, qi, qj, qk,
271                                             is_p_first, is_p_last, is_q_first, is_q_last,
272                                             intersections.template get<1>(),
273                                             tp_model, inters, 1, out);
274 
275         // don't ignore only for collinear opposite
276         bool result_ignore_ip1 = append1_last && !opposite /*&& ip_count == 2*/;
277 
278         return result_ignore_ip0 || result_ignore_ip1;
279     }
280 
281     template <typename Point1,
282               typename Point2,
283               typename TurnInfo,
284               typename IntersectionInfo,
285               typename OutputIterator>
286     static inline
analyse_segment_and_assign_ipboost::geometry::detail::overlay::get_turn_info_for_endpoint287     bool analyse_segment_and_assign_ip(Point1 const& pi, Point1 const& pj, Point1 const& pk,
288                                        Point2 const& qi, Point2 const& qj, Point2 const& qk,
289                                        bool is_p_first, bool is_p_last,
290                                        bool is_q_first, bool is_q_last,
291                                        linear_intersections::ip_info const& ip_info,
292                                        TurnInfo const& tp_model,
293                                        IntersectionInfo const& inters,
294                                        unsigned int ip_index,
295                                        OutputIterator out)
296     {
297 #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
298         // may this give false positives for INTs?
299         typename IntersectionResult::point_type const&
300             inters_pt = result.template get<0>().intersections[ip_index];
301         BOOST_GEOMETRY_ASSERT(ip_info.is_pi == equals::equals_point_point(pi, inters_pt));
302         BOOST_GEOMETRY_ASSERT(ip_info.is_qi == equals::equals_point_point(qi, inters_pt));
303         BOOST_GEOMETRY_ASSERT(ip_info.is_pj == equals::equals_point_point(pj, inters_pt));
304         BOOST_GEOMETRY_ASSERT(ip_info.is_qj == equals::equals_point_point(qj, inters_pt));
305 #endif
306 
307         // TODO - calculate first/last only if needed
308         bool is_p_first_ip = is_p_first && ip_info.is_pi;
309         bool is_p_last_ip = is_p_last && ip_info.is_pj;
310         bool is_q_first_ip = is_q_first && ip_info.is_qi;
311         bool is_q_last_ip = is_q_last && ip_info.is_qj;
312         bool append_first = EnableFirst && (is_p_first_ip || is_q_first_ip);
313         bool append_last = EnableLast && (is_p_last_ip || is_q_last_ip);
314 
315         operation_type p_operation = ip_info.p_operation;
316         operation_type q_operation = ip_info.q_operation;
317 
318         if ( append_first || append_last )
319         {
320             bool handled = handle_internal<0>(pi, pj, pk, qi, qj, qk,
321                                               inters.rpi(), inters.rpj(), inters.rpk(),
322                                               inters.rqi(), inters.rqj(), inters.rqk(),
323                                               is_p_first_ip, is_p_last_ip,
324                                               is_q_first_ip, is_q_last_ip,
325                                               ip_info.is_qi, ip_info.is_qj,
326                                               tp_model, inters, ip_index,
327                                               p_operation, q_operation);
328             if ( !handled )
329             {
330                 handle_internal<1>(qi, qj, qk, pi, pj, pk,
331                                    inters.rqi(), inters.rqj(), inters.rqk(),
332                                    inters.rpi(), inters.rpj(), inters.rpk(),
333                                    is_q_first_ip, is_q_last_ip,
334                                    is_p_first_ip, is_p_last_ip,
335                                    ip_info.is_pi, ip_info.is_pj,
336                                    tp_model, inters, ip_index,
337                                    q_operation, p_operation);
338             }
339 
340             if ( p_operation != operation_none )
341             {
342                 method_type method = endpoint_ip_method(ip_info.is_pi, ip_info.is_pj,
343                                                         ip_info.is_qi, ip_info.is_qj);
344                 turn_position p_pos = ip_position(is_p_first_ip, is_p_last_ip);
345                 turn_position q_pos = ip_position(is_q_first_ip, is_q_last_ip);
346 
347                 // handle spikes
348 
349                 // P is spike and should be handled
350                 if ( !is_p_last
351                   && ip_info.is_pj // this check is redundant (also in is_spike_p) but faster
352                   && inters.i_info().count == 2
353                   && inters.is_spike_p() )
354                 {
355                     assign(pi, qi, inters.result(), ip_index, method, operation_blocked, q_operation,
356                            p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
357                     assign(pi, qi, inters.result(), ip_index, method, operation_intersection, q_operation,
358                            p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
359                 }
360                 // Q is spike and should be handled
361                 else if ( !is_q_last
362                        && ip_info.is_qj // this check is redundant (also in is_spike_q) but faster
363                        && inters.i_info().count == 2
364                        && inters.is_spike_q() )
365                 {
366                     assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_blocked,
367                            p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out);
368                     assign(pi, qi, inters.result(), ip_index, method, p_operation, operation_intersection,
369                            p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, true, tp_model, out);
370                 }
371                 // no spikes
372                 else
373                 {
374                     assign(pi, qi, inters.result(), ip_index, method, p_operation, q_operation,
375                            p_pos, q_pos, is_p_first_ip, is_q_first_ip, false, false, tp_model, out);
376                 }
377             }
378         }
379 
380         return append_last;
381     }
382 
383     // TODO: IT'S ALSO PROBABLE THAT ALL THIS FUNCTION COULD BE INTEGRATED WITH handle_segment
384     //       however now it's lazily calculated and then it would be always calculated
385 
386     template<std::size_t G1Index,
387              typename Point1,
388              typename Point2,
389              typename RobustPoint1,
390              typename RobustPoint2,
391              typename TurnInfo,
392              typename IntersectionInfo
393     >
handle_internalboost::geometry::detail::overlay::get_turn_info_for_endpoint394     static inline bool handle_internal(Point1 const& /*i1*/, Point1 const& /*j1*/, Point1 const& /*k1*/,
395                                        Point2 const& i2, Point2 const& j2, Point2 const& /*k2*/,
396                                        RobustPoint1 const& ri1, RobustPoint1 const& rj1, RobustPoint1 const& /*rk1*/,
397                                        RobustPoint2 const& ri2, RobustPoint2 const& rj2, RobustPoint2 const& rk2,
398                                        bool first1, bool last1, bool first2, bool last2,
399                                        bool ip_i2, bool ip_j2, TurnInfo const& tp_model,
400                                        IntersectionInfo const& inters, unsigned int ip_index,
401                                        operation_type & op1, operation_type & op2)
402     {
403         typedef typename cs_tag<typename TurnInfo::point_type>::type cs_tag;
404 
405         boost::ignore_unused_variable_warning(i2);
406         boost::ignore_unused_variable_warning(j2);
407         boost::ignore_unused_variable_warning(ip_index);
408         boost::ignore_unused_variable_warning(tp_model);
409 
410         if ( !first2 && !last2 )
411         {
412             if ( first1 )
413             {
414 #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
415                 // may this give false positives for INTs?
416                 typename IntersectionResult::point_type const&
417                     inters_pt = inters.i_info().intersections[ip_index];
418                 BOOST_GEOMETRY_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
419                 BOOST_GEOMETRY_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
420 #endif
421                 if ( ip_i2 )
422                 {
423                     // don't output this IP - for the first point of other geometry segment
424                     op1 = operation_none;
425                     op2 = operation_none;
426                     return true;
427                 }
428                 else if ( ip_j2 )
429                 {
430                     side_calculator<cs_tag,
431                                     RobustPoint1, RobustPoint2,
432                                     typename IntersectionInfo::side_strategy_type,
433                                     RobustPoint2>
434                         side_calc(ri2, ri1, rj1, ri2, rj2, rk2, inters.get_side_strategy());
435 
436                     std::pair<operation_type, operation_type>
437                         operations = operations_of_equal(side_calc);
438 
439 // TODO: must the above be calculated?
440 // wouldn't it be enough to check if segments are collinear?
441 
442                     if ( operations_both(operations, operation_continue) )
443                     {
444                         if ( op1 != operation_union
445                           || op2 != operation_union
446                           || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
447                         {
448                             // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
449                             bool opposite = inters.d_info().opposite;
450 
451                             op1 = operation_intersection;
452                             op2 = opposite ? operation_union : operation_intersection;
453                         }
454                     }
455                     else
456                     {
457                         BOOST_GEOMETRY_ASSERT(operations_combination(operations, operation_intersection, operation_union));
458                         //op1 = operation_union;
459                         //op2 = operation_union;
460                     }
461 
462                     return true;
463                 }
464                 // else do nothing - shouldn't be handled this way
465             }
466             else if ( last1 )
467             {
468 #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
469                 // may this give false positives for INTs?
470                 typename IntersectionResult::point_type const&
471                     inters_pt = inters.i_info().intersections[ip_index];
472                 BOOST_GEOMETRY_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
473                 BOOST_GEOMETRY_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
474 #endif
475                 if ( ip_i2 )
476                 {
477                     // don't output this IP - for the first point of other geometry segment
478                     op1 = operation_none;
479                     op2 = operation_none;
480                     return true;
481                 }
482                 else if ( ip_j2 )
483                 {
484                     side_calculator<cs_tag, RobustPoint1, RobustPoint2,
485                                     typename IntersectionInfo::side_strategy_type,
486                                     RobustPoint2>
487                         side_calc(ri2, rj1, ri1, ri2, rj2, rk2, inters.get_side_strategy());
488 
489                     std::pair<operation_type, operation_type>
490                         operations = operations_of_equal(side_calc);
491 
492 // TODO: must the above be calculated?
493 // wouldn't it be enough to check if segments are collinear?
494 
495                     if ( operations_both(operations, operation_continue) )
496                     {
497                         if ( op1 != operation_blocked
498                           || op2 != operation_union
499                           || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
500                         {
501                             // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
502                             bool second_going_out = inters.i_info().count > 1;
503 
504                             op1 = operation_blocked;
505                             op2 = second_going_out ? operation_union : operation_intersection;
506                         }
507                     }
508                     else
509                     {
510                         BOOST_GEOMETRY_ASSERT(operations_combination(operations, operation_intersection, operation_union));
511                         //op1 = operation_blocked;
512                         //op2 = operation_union;
513                     }
514 
515                     return true;
516                 }
517                 // else do nothing - shouldn't be handled this way
518             }
519             // else do nothing - shouldn't be handled this way
520         }
521 
522         return false;
523     }
524 
endpoint_ip_methodboost::geometry::detail::overlay::get_turn_info_for_endpoint525     static inline method_type endpoint_ip_method(bool ip_pi, bool ip_pj, bool ip_qi, bool ip_qj)
526     {
527         if ( (ip_pi || ip_pj) && (ip_qi || ip_qj) )
528             return method_touch;
529         else
530             return method_touch_interior;
531     }
532 
ip_positionboost::geometry::detail::overlay::get_turn_info_for_endpoint533     static inline turn_position ip_position(bool is_ip_first_i, bool is_ip_last_j)
534     {
535         return is_ip_first_i ? position_front :
536                ( is_ip_last_j ? position_back : position_middle );
537     }
538 
539     template <typename Point1,
540               typename Point2,
541               typename IntersectionResult,
542               typename TurnInfo,
543               typename OutputIterator>
assignboost::geometry::detail::overlay::get_turn_info_for_endpoint544     static inline void assign(Point1 const& pi, Point2 const& qi,
545                               IntersectionResult const& result,
546                               unsigned int ip_index,
547                               method_type method,
548                               operation_type op0, operation_type op1,
549                               turn_position pos0, turn_position pos1,
550                               bool is_p_first_ip, bool is_q_first_ip,
551                               bool is_p_spike, bool is_q_spike,
552                               TurnInfo const& tp_model,
553                               OutputIterator out)
554     {
555         TurnInfo tp = tp_model;
556 
557         //geometry::convert(ip, tp.point);
558         //tp.method = method;
559         base_turn_handler::assign_point(tp, method, result.template get<0>(), ip_index);
560 
561         tp.operations[0].operation = op0;
562         tp.operations[1].operation = op1;
563         tp.operations[0].position = pos0;
564         tp.operations[1].position = pos1;
565 
566         if ( result.template get<0>().count > 1 )
567         {
568             // NOTE: is_collinear is NOT set for the first endpoint
569             // for which there is no preceding segment
570 
571             //BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
572             if ( ! is_p_first_ip )
573             {
574                 tp.operations[0].is_collinear = op0 != operation_intersection
575                                              || is_p_spike;
576             }
577 
578             if ( ! is_q_first_ip )
579             {
580                 tp.operations[1].is_collinear = op1 != operation_intersection
581                                              || is_q_spike;
582             }
583         }
584         else //if ( result.template get<0>().count == 1 )
585         {
586             if ( op0 == operation_blocked && op1 == operation_intersection )
587             {
588                 tp.operations[0].is_collinear = true;
589             }
590             else if ( op0 == operation_intersection && op1 == operation_blocked )
591             {
592                 tp.operations[1].is_collinear = true;
593             }
594         }
595 
596         // TODO: this should get an intersection_info, which is unavailable here
597         // Because the assign_null policy accepts any structure, we pass the result instead for now
598         AssignPolicy::apply(tp, pi, qi, result);
599         *out++ = tp;
600     }
601 
602     template <typename SidePolicy>
operations_of_equalboost::geometry::detail::overlay::get_turn_info_for_endpoint603     static inline std::pair<operation_type, operation_type> operations_of_equal(SidePolicy const& side)
604     {
605         int const side_pk_q2 = side.pk_wrt_q2();
606         int const side_pk_p = side.pk_wrt_p1();
607         int const side_qk_p = side.qk_wrt_p1();
608 
609         // If pk is collinear with qj-qk, they continue collinearly.
610         // This can be on either side of p1 (== q1), or collinear
611         // The second condition checks if they do not continue
612         // oppositely
613         if ( side_pk_q2 == 0 && side_pk_p == side_qk_p )
614         {
615             return std::make_pair(operation_continue, operation_continue);
616         }
617 
618         // If they turn to same side (not opposite sides)
619         if ( ! base_turn_handler::opposite(side_pk_p, side_qk_p) )
620         {
621             // If pk is left of q2 or collinear: p: union, q: intersection
622             if ( side_pk_q2 != -1 )
623             {
624                 return std::make_pair(operation_union, operation_intersection);
625             }
626             else
627             {
628                return std::make_pair(operation_intersection, operation_union);
629             }
630         }
631         else
632         {
633             // They turn opposite sides. If p turns left (or collinear),
634            // p: union, q: intersection
635             if ( side_pk_p != -1 )
636             {
637                 return std::make_pair(operation_union, operation_intersection);
638             }
639            else
640             {
641                 return std::make_pair(operation_intersection, operation_union);
642             }
643         }
644    }
645 
operations_bothboost::geometry::detail::overlay::get_turn_info_for_endpoint646     static inline bool operations_both(
647                             std::pair<operation_type, operation_type> const& operations,
648                             operation_type const op)
649     {
650         return operations.first == op && operations.second == op;
651     }
652 
operations_combinationboost::geometry::detail::overlay::get_turn_info_for_endpoint653     static inline bool operations_combination(
654                             std::pair<operation_type, operation_type> const& operations,
655                             operation_type const op1, operation_type const op2)
656     {
657         return ( operations.first == op1 && operations.second == op2 )
658             || ( operations.first == op2 && operations.second == op1 );
659     }
660 };
661 
662 }} // namespace detail::overlay
663 #endif // DOXYGEN_NO_DETAIL
664 
665 }} // namespace boost::geometry
666 
667 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP
668