Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / geometry / algorithms / detail / overlay / get_turn_info_for_endpoint.hpp
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, 2018.
6 // Modifications copyright (c) 2013-2018 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/core/ignore_unused.hpp>
18
19 #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
20 #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
21 #include <boost/geometry/core/assert.hpp>
22 #include <boost/geometry/policies/robustness/no_rescale_policy.hpp>
23
24 namespace boost { namespace geometry {
25
26 #ifndef DOXYGEN_NO_DETAIL
27 namespace detail { namespace overlay {
28
29 // SEGMENT_INTERSECTION RESULT
30
31 //                   C  H0  H1  A0  A1   O              IP1 IP2
32
33 // D0 and D1 == 0
34
35 // |-------->        2   0   0   0   0   F              i/i x/x
36 // |-------->
37 //
38 // |-------->        2   0   0   0   0   T              i/x x/i
39 // <--------|
40 //
41 // |----->           1   0   0   0   0   T              x/x
42 //       <-----|
43 //
44
45 // |--------->       2   0   0   0   1   T              i/x x/i
46 //      <----|
47 //
48 // |--------->       2   0   0   0   0   F              i/i x/x
49 //      |---->
50 //
51 // |--------->       2   0   0  -1   1   F              i/i u/x
52 // |---->
53 //
54 // |--------->       2   0   0  -1   0   T              i/x u/i
55 // <----|
56
57 // |------->         2   0   0   1  -1   F   and        i/i x/u
58 //     |------->     2   0   0  -1   1   F   symmetric  i/i u/x
59 // |------->
60 //
61 //     |------->     2   0   0  -1  -1   T              i/u u/i
62 // <-------|
63 //
64 // |------->         2   0   0   1   1   T              i/x x/i
65 //     <-------|
66 //
67 // |-------->        2   0   0  -1   1   F              i/i u/x
68 //   |---->
69 //
70 // |-------->        2   0   0  -1   1   T              i/x u/i
71 //   <----|
72
73 //       |----->     1  -1  -1  -1  -1   T              u/u
74 // <-----|
75 //
76 //       |----->     1  -1   0  -1   0   F   and        u/x
77 // |----->           1   0  -1   0  -1   F   symmetric  x/u
78 //       |----->
79
80 // D0 or D1 != 0
81
82 //          ^
83 //          |
84 //          +        1  -1   1  -1   1   F   and        u/x  (P is vertical)
85 // |-------->        1   1  -1   1  -1   F   symmetric  x/u  (P is horizontal)
86 // ^
87 // |
88 // +
89 //
90 //          +
91 //          |
92 //          v
93 // |-------->        1   1   1   1   1   F              x/x  (P is vertical)
94 //
95 // ^
96 // |
97 // +
98 // |-------->        1  -1  -1  -1  -1   F              u/u  (P is vertical)
99 //
100 //      ^
101 //      |
102 //      +
103 // |-------->        1   0  -1   0  -1   F              u/u  (P is vertical)
104 //
105 //      +
106 //      |
107 //      v
108 // |-------->        1   0   1   0   1   F              u/x  (P is vertical)
109 //
110
111 class linear_intersections
112 {
113 public:
114     template <typename Point1, typename Point2, typename IntersectionResult, typename EqPPStrategy>
115     linear_intersections(Point1 const& pi,
116                          Point2 const& qi,
117                          IntersectionResult const& result,
118                          bool is_p_last, bool is_q_last,
119                          EqPPStrategy const& strategy)
120     {
121         int arrival_a = result.template get<1>().arrival[0];
122         int arrival_b = result.template get<1>().arrival[1];
123         bool same_dirs = result.template get<1>().dir_a == 0
124                       && result.template get<1>().dir_b == 0;
125
126         if ( same_dirs )
127         {
128             if ( result.template get<0>().count == 2 )
129             {
130                 if ( ! result.template get<1>().opposite )
131                 {
132                     ips[0].p_operation = operation_intersection;
133                     ips[0].q_operation = operation_intersection;
134                     ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
135                     ips[1].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
136
137                     ips[0].is_pi
138                         = equals::equals_point_point(
139                             pi, result.template get<0>().intersections[0], strategy);
140                     ips[0].is_qi
141                         = equals::equals_point_point(
142                             qi, result.template get<0>().intersections[0], strategy);
143                     ips[1].is_pj = arrival_a != -1;
144                     ips[1].is_qj = arrival_b != -1;
145                 }
146                 else
147                 {
148                     ips[0].p_operation = operation_intersection;
149                     ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
150                     ips[1].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
151                     ips[1].q_operation = operation_intersection;
152
153                     ips[0].is_pi = arrival_b != 1;
154                     ips[0].is_qj = arrival_b != -1;
155                     ips[1].is_pj = arrival_a != -1;
156                     ips[1].is_qi = arrival_a != 1;
157                 }
158             }
159             else
160             {
161                 BOOST_GEOMETRY_ASSERT(result.template get<0>().count == 1);
162                 ips[0].p_operation = union_or_blocked_same_dirs(arrival_a, is_p_last);
163                 ips[0].q_operation = union_or_blocked_same_dirs(arrival_b, is_q_last);
164
165                 ips[0].is_pi = arrival_a == -1;
166                 ips[0].is_qi = arrival_b == -1;
167                 ips[0].is_pj = arrival_a == 0;
168                 ips[0].is_qj = arrival_b == 0;
169             }
170         }
171         else
172         {
173             ips[0].p_operation = union_or_blocked_different_dirs(arrival_a, is_p_last);
174             ips[0].q_operation = union_or_blocked_different_dirs(arrival_b, is_q_last);
175
176             ips[0].is_pi = arrival_a == -1;
177             ips[0].is_qi = arrival_b == -1;
178             ips[0].is_pj = arrival_a == 1;
179             ips[0].is_qj = arrival_b == 1;
180         }
181     }
182
183     struct ip_info
184     {
185         inline ip_info()
186             : p_operation(operation_none), q_operation(operation_none)
187             , is_pi(false), is_pj(false), is_qi(false), is_qj(false)
188         {}
189
190         operation_type p_operation, q_operation;
191         bool is_pi, is_pj, is_qi, is_qj;
192     };
193
194     template <std::size_t I>
195     ip_info const& get() const
196     {
197         BOOST_STATIC_ASSERT(I < 2);
198         return ips[I];
199     }
200     
201 private:
202
203     // only if collinear (same_dirs)
204     static inline operation_type union_or_blocked_same_dirs(int arrival, bool is_last)
205     {
206         if ( arrival == 1 )
207             return operation_blocked;
208         else if ( arrival == -1 )
209             return operation_union;
210         else
211             return is_last ? operation_blocked : operation_union;
212             //return operation_blocked;
213     }
214
215     // only if not collinear (!same_dirs)
216     static inline operation_type union_or_blocked_different_dirs(int arrival, bool is_last)
217     {
218         if ( arrival == 1 )
219             //return operation_blocked;
220             return is_last ? operation_blocked : operation_union;
221         else
222             return operation_union;
223     }
224
225     ip_info ips[2];
226 };
227
228 template <bool EnableFirst, bool EnableLast>
229 struct get_turn_info_for_endpoint
230 {
231     typedef std::pair<operation_type, operation_type> operations_pair;
232
233     BOOST_STATIC_ASSERT(EnableFirst || EnableLast);
234
235     template<typename UniqueSubRange1,
236              typename UniqueSubRange2,
237              typename TurnInfo,
238              typename IntersectionInfo,
239              typename OutputIterator,
240              typename EqPPStrategy
241     >
242     static inline bool apply(UniqueSubRange1 const& range_p,
243                              UniqueSubRange2 const& range_q,
244                              TurnInfo const& tp_model,
245                              IntersectionInfo const& inters,
246                              method_type /*method*/,
247                              OutputIterator out,
248                              EqPPStrategy const& strategy)
249     {
250         std::size_t ip_count = inters.i_info().count;
251         // no intersection points
252         if (ip_count == 0)
253         {
254             return false;
255         }
256
257         if (! range_p.is_first_segment()
258             && ! range_q.is_first_segment()
259             && ! range_p.is_last_segment()
260             && ! range_q.is_last_segment())
261         {
262             // Not an end-point from segment p or q
263             return false;
264         }
265
266         linear_intersections intersections(range_p.at(0),
267                                            range_q.at(0),
268                                            inters.result(),
269                                            range_p.is_last_segment(),
270                                            range_q.is_last_segment(),
271                                            strategy);
272
273         bool append0_last
274             = analyse_segment_and_assign_ip(range_p, range_q,
275                                             intersections.template get<0>(),
276                                             tp_model, inters, 0, out);
277
278         // NOTE: opposite && ip_count == 1 may be true!
279         bool opposite = inters.d_info().opposite;
280
281         // don't ignore only for collinear opposite
282         bool result_ignore_ip0 = append0_last && ( ip_count == 1 || !opposite );
283
284         if ( intersections.template get<1>().p_operation == operation_none )
285             return result_ignore_ip0;
286         
287         bool append1_last
288             = analyse_segment_and_assign_ip(range_p, range_q,
289                                             intersections.template get<1>(),
290                                             tp_model, inters, 1, out);
291
292         // don't ignore only for collinear opposite
293         bool result_ignore_ip1 = append1_last && !opposite /*&& ip_count == 2*/;
294
295         return result_ignore_ip0 || result_ignore_ip1;
296     }
297
298     template <typename UniqueSubRange1,
299               typename UniqueSubRange2,
300               typename TurnInfo,
301               typename IntersectionInfo,
302               typename OutputIterator>
303     static inline
304     bool analyse_segment_and_assign_ip(UniqueSubRange1 const& range_p,
305                                        UniqueSubRange2 const& range_q,
306                                        linear_intersections::ip_info const& ip_info,
307                                        TurnInfo const& tp_model,
308                                        IntersectionInfo const& inters,
309                                        unsigned int ip_index,
310                                        OutputIterator out)
311     {
312         // TODO - calculate first/last only if needed
313         bool is_p_first_ip = range_p.is_first_segment() && ip_info.is_pi;
314         bool is_p_last_ip = range_p.is_last_segment() && ip_info.is_pj;
315         bool is_q_first_ip = range_q.is_first_segment() && ip_info.is_qi;
316         bool is_q_last_ip = range_q.is_last_segment() && ip_info.is_qj;
317         bool append_first = EnableFirst && (is_p_first_ip || is_q_first_ip);
318         bool append_last = EnableLast && (is_p_last_ip || is_q_last_ip);
319
320         operation_type p_operation = ip_info.p_operation;
321         operation_type q_operation = ip_info.q_operation;
322
323         if ( append_first || append_last )
324         {
325             bool handled = handle_internal<0>(range_p, range_q,
326                                               is_p_first_ip, is_p_last_ip,
327                                               is_q_first_ip, is_q_last_ip,
328                                               ip_info.is_qi, ip_info.is_qj,
329                                               tp_model, inters, ip_index,
330                                               p_operation, q_operation);
331             if ( !handled )
332             {
333                 // Reverse p/q
334                 handle_internal<1>(range_q, range_p,
335                                    is_q_first_ip, is_q_last_ip,
336                                    is_p_first_ip, is_p_last_ip,
337                                    ip_info.is_pi, ip_info.is_pj,
338                                    tp_model, inters, ip_index,
339                                    q_operation, p_operation);
340             }
341
342             if ( p_operation != operation_none )
343             {
344                 method_type method = endpoint_ip_method(ip_info.is_pi, ip_info.is_pj,
345                                                         ip_info.is_qi, ip_info.is_qj);
346                 turn_position p_pos = ip_position(is_p_first_ip, is_p_last_ip);
347                 turn_position q_pos = ip_position(is_q_first_ip, is_q_last_ip);
348
349                 // handle spikes
350
351                 // P is spike and should be handled
352                 if (ip_info.is_pj // this check is redundant (also in is_spike_p) but faster
353                   && inters.i_info().count == 2
354                   && inters.is_spike_p() )
355                 {
356                     assign(inters.result(), ip_index, method, operation_blocked, q_operation,
357                            p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
358                     assign(inters.result(), ip_index, method, operation_intersection, q_operation,
359                            p_pos, q_pos, is_p_first_ip, is_q_first_ip, true, false, tp_model, out);
360                 }
361                 // Q is spike and should be handled
362                 else if (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(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(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(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 UniqueRange1,
388              typename UniqueRange2,
389              typename TurnInfo,
390              typename IntersectionInfo
391     >
392     static inline bool handle_internal(UniqueRange1 const& range1,
393                                        UniqueRange2 const& range2,
394                                        bool first1, bool last1, bool first2, bool last2,
395                                        bool ip_i2, bool ip_j2, TurnInfo const& tp_model,
396                                        IntersectionInfo const& inters, unsigned int ip_index,
397                                        operation_type & op1, operation_type & op2)
398     {
399         boost::ignore_unused(ip_index, tp_model);
400
401         typename IntersectionInfo::side_strategy_type const& sides
402                 = inters.get_side_strategy();
403
404         if ( !first2 && !last2 )
405         {
406             if ( first1 )
407             {
408 #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
409                 // may this give false positives for INTs?
410                 typename IntersectionResult::point_type const&
411                     inters_pt = inters.i_info().intersections[ip_index];
412                 BOOST_GEOMETRY_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
413                 BOOST_GEOMETRY_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
414 #endif
415                 if ( ip_i2 )
416                 {
417                     // don't output this IP - for the first point of other geometry segment
418                     op1 = operation_none;
419                     op2 = operation_none;
420                     return true;
421                 }
422                 else if ( ip_j2 )
423                 {
424                     int const side_pj_q2 = sides.apply(range2.at(1), range2.at(2), range1.at(1));
425                     int const side_pj_q1 = sides.apply(range2.at(0), range2.at(1), range1.at(1));
426                     int const side_qk_q1 = sides.apply(range2.at(0), range2.at(1), range2.at(2));
427
428                     operations_pair operations = operations_of_equal(side_pj_q2, side_pj_q1, side_qk_q1);
429
430 // TODO: must the above be calculated?
431 // wouldn't it be enough to check if segments are collinear?
432
433                     if ( operations_both(operations, operation_continue) )
434                     {
435                         if ( op1 != operation_union 
436                           || op2 != operation_union
437                           || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
438                         {
439                             // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
440                             bool opposite = inters.d_info().opposite;
441
442                             op1 = operation_intersection;
443                             op2 = opposite ? operation_union : operation_intersection;
444                         }
445                     }
446                     else
447                     {
448                         BOOST_GEOMETRY_ASSERT(operations_combination(operations, operation_intersection, operation_union));
449                         //op1 = operation_union;
450                         //op2 = operation_union;
451                     }
452
453                     return true;
454                 }
455                 // else do nothing - shouldn't be handled this way
456             }
457             else if ( last1 )
458             {
459 #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR
460                 // may this give false positives for INTs?
461                 typename IntersectionResult::point_type const&
462                     inters_pt = inters.i_info().intersections[ip_index];
463                 BOOST_GEOMETRY_ASSERT(ip_i2 == equals::equals_point_point(i2, inters_pt));
464                 BOOST_GEOMETRY_ASSERT(ip_j2 == equals::equals_point_point(j2, inters_pt));
465 #endif
466                 if ( ip_i2 )
467                 {
468                     // don't output this IP - for the first point of other geometry segment
469                     op1 = operation_none;
470                     op2 = operation_none;
471                     return true;
472                 }
473                 else if ( ip_j2 )
474                 {
475                     int const side_pi_q2 = sides.apply(range2.at(1), range2.at(2), range1.at(0));
476                     int const side_pi_q1 = sides.apply(range2.at(0), range2.at(1), range1.at(0));
477                     int const side_qk_q1 = sides.apply(range2.at(0), range2.at(1), range2.at(2));
478
479                     operations_pair operations = operations_of_equal(side_pi_q2, side_pi_q1, side_qk_q1);
480
481 // TODO: must the above be calculated?
482 // wouldn't it be enough to check if segments are collinear?
483
484                     if ( operations_both(operations, operation_continue) )
485                     {
486                         if ( op1 != operation_blocked
487                           || op2 != operation_union
488                           || ! ( G1Index == 0 ? inters.is_spike_q() : inters.is_spike_p() ) )
489                         {
490                             // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE!
491                             bool second_going_out = inters.i_info().count > 1;
492
493                             op1 = operation_blocked;
494                             op2 = second_going_out ? operation_union : operation_intersection;
495                         }
496                     }
497                     else
498                     {
499                         BOOST_GEOMETRY_ASSERT(operations_combination(operations, operation_intersection, operation_union));
500                         //op1 = operation_blocked;
501                         //op2 = operation_union;
502                     }
503
504                     return true;
505                 }
506                 // else do nothing - shouldn't be handled this way
507             }
508             // else do nothing - shouldn't be handled this way
509         }
510
511         return false;
512     }
513
514     static inline method_type endpoint_ip_method(bool ip_pi, bool ip_pj, bool ip_qi, bool ip_qj)
515     {
516         if ( (ip_pi || ip_pj) && (ip_qi || ip_qj) )
517             return method_touch;
518         else
519             return method_touch_interior;
520     }
521
522     static inline turn_position ip_position(bool is_ip_first_i, bool is_ip_last_j)
523     {
524         return is_ip_first_i ? position_front :
525                ( is_ip_last_j ? position_back : position_middle );
526     }
527
528     template <typename IntersectionResult,
529               typename TurnInfo,
530               typename OutputIterator>
531     static inline void assign(IntersectionResult const& result,
532                               unsigned int ip_index,
533                               method_type method,
534                               operation_type op0, operation_type op1,
535                               turn_position pos0, turn_position pos1,
536                               bool is_p_first_ip, bool is_q_first_ip,
537                               bool is_p_spike, bool is_q_spike,
538                               TurnInfo const& tp_model,
539                               OutputIterator out)
540     {
541         TurnInfo tp = tp_model;
542         
543         //geometry::convert(ip, tp.point);
544         //tp.method = method;
545         base_turn_handler::assign_point(tp, method, result.template get<0>(), ip_index);
546
547         tp.operations[0].operation = op0;
548         tp.operations[1].operation = op1;
549         tp.operations[0].position = pos0;
550         tp.operations[1].position = pos1;
551
552         if ( result.template get<0>().count > 1 )
553         {
554             // NOTE: is_collinear is NOT set for the first endpoint
555             // for which there is no preceding segment
556
557             //BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
558             if ( ! is_p_first_ip )
559             {
560                 tp.operations[0].is_collinear = op0 != operation_intersection
561                                              || is_p_spike;
562             }
563
564             if ( ! is_q_first_ip )
565             {
566                 tp.operations[1].is_collinear = op1 != operation_intersection
567                                              || is_q_spike;
568             }
569         }
570         else //if ( result.template get<0>().count == 1 )
571         {
572             if ( op0 == operation_blocked && op1 == operation_intersection )
573             {
574                 tp.operations[0].is_collinear = true;
575             }
576             else if ( op0 == operation_intersection && op1 == operation_blocked )
577             {
578                 tp.operations[1].is_collinear = true;
579             }
580         }
581
582         *out++ = tp;
583     }
584
585     static inline operations_pair operations_of_equal(int side_px_q2,
586                                                       int side_px_q1,
587                                                       int side_qk_q1)
588     {
589         // If px (pi or pj) is collinear with qj-qk (q2), they continue collinearly.
590         // This can be on either side of q1, or collinear
591         // The second condition checks if they do not continue
592         // oppositely
593         if (side_px_q2 == 0 && side_px_q1 == side_qk_q1)
594         {
595             return std::make_pair(operation_continue, operation_continue);
596         }
597
598         // If they turn to same side (not opposite sides)
599         if ( ! base_turn_handler::opposite(side_px_q1, side_qk_q1) )
600         {
601             // If px is left of q2 or collinear: p: union, q: intersection
602             if (side_px_q2 != -1 )
603             {
604                 return std::make_pair(operation_union, operation_intersection);
605             }
606             else
607             {
608                return std::make_pair(operation_intersection, operation_union);
609             }
610         }
611         else
612         {
613             // They turn opposite sides. If p turns left (or collinear),
614            // p: union, q: intersection
615             if (side_px_q1 != -1 )
616             {
617                 return std::make_pair(operation_union, operation_intersection);
618             }
619            else
620             {
621                 return std::make_pair(operation_intersection, operation_union);
622             }
623         }
624    }
625
626     static inline bool operations_both(operations_pair const& operations,
627                                        operation_type const op)
628     {
629         return operations.first == op && operations.second == op;
630     }
631
632     static inline bool operations_combination(operations_pair const& operations,
633                                               operation_type const op1,
634                                               operation_type const op2)
635     {
636         return ( operations.first == op1 && operations.second == op2 )
637             || ( operations.first == op2 && operations.second == op1 );
638     }
639 };
640
641 }} // namespace detail::overlay
642 #endif // DOXYGEN_NO_DETAIL
643
644 }} // namespace boost::geometry
645
646 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_FOR_ENDPOINT_HPP