]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/geometry/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / geometry / include / boost / geometry / algorithms / detail / overlay / get_turn_info_ll.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, 2015.
6 // Modifications copyright (c) 2013-2015 Oracle and/or its affiliates.
7
8 // Use, modification and distribution is subject to the Boost Software License,
9 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
10 // http://www.boost.org/LICENSE_1_0.txt)
11
12 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
13
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP
16
17 #include <boost/geometry/core/assert.hpp>
18
19 #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
20 #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
21
22 #include <boost/geometry/util/condition.hpp>
23
24 namespace boost { namespace geometry {
25
26 #ifndef DOXYGEN_NO_DETAIL
27 namespace detail { namespace overlay {
28
29 template<typename AssignPolicy>
30 struct get_turn_info_linear_linear
31 {
32 static const bool handle_spikes = true;
33
34 template
35 <
36 typename Point1,
37 typename Point2,
38 typename TurnInfo,
39 typename RobustPolicy,
40 typename OutputIterator
41 >
42 static inline OutputIterator apply(
43 Point1 const& pi, Point1 const& pj, Point1 const& pk,
44 Point2 const& qi, Point2 const& qj, Point2 const& qk,
45 bool is_p_first, bool is_p_last,
46 bool is_q_first, bool is_q_last,
47 TurnInfo const& tp_model,
48 RobustPolicy const& robust_policy,
49 OutputIterator out)
50 {
51 typedef intersection_info<Point1, Point2, typename TurnInfo::point_type, RobustPolicy>
52 inters_info;
53
54 inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy);
55
56 char const method = inters.d_info().how;
57
58 // Copy, to copy possibly extended fields
59 TurnInfo tp = tp_model;
60
61 // Select method and apply
62 switch(method)
63 {
64 case 'a' : // collinear, "at"
65 case 'f' : // collinear, "from"
66 case 's' : // starts from the middle
67 get_turn_info_for_endpoint<AssignPolicy, true, true>
68 ::apply(pi, pj, pk, qi, qj, qk,
69 is_p_first, is_p_last, is_q_first, is_q_last,
70 tp_model, inters, method_none, out);
71 break;
72
73 case 'd' : // disjoint: never do anything
74 break;
75
76 case 'm' :
77 {
78 if ( get_turn_info_for_endpoint<AssignPolicy, false, true>
79 ::apply(pi, pj, pk, qi, qj, qk,
80 is_p_first, is_p_last, is_q_first, is_q_last,
81 tp_model, inters, method_touch_interior, out) )
82 {
83 // do nothing
84 }
85 else
86 {
87 typedef touch_interior
88 <
89 TurnInfo
90 > policy;
91
92 // If Q (1) arrives (1)
93 if ( inters.d_info().arrival[1] == 1)
94 {
95 policy::template apply<0>(pi, pj, pk, qi, qj, qk,
96 tp, inters.i_info(), inters.d_info(),
97 inters.sides());
98 }
99 else
100 {
101 // Swap p/q
102 side_calculator
103 <
104 typename inters_info::cs_tag,
105 typename inters_info::robust_point2_type,
106 typename inters_info::robust_point1_type
107 > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(),
108 inters.rpi(), inters.rpj(), inters.rpk());
109
110 policy::template apply<1>(qi, qj, qk, pi, pj, pk,
111 tp, inters.i_info(), inters.d_info(),
112 swapped_side_calc);
113 }
114
115 if ( tp.operations[0].operation == operation_blocked )
116 {
117 tp.operations[1].is_collinear = true;
118 }
119 if ( tp.operations[1].operation == operation_blocked )
120 {
121 tp.operations[0].is_collinear = true;
122 }
123
124 replace_method_and_operations_tm(tp.method,
125 tp.operations[0].operation,
126 tp.operations[1].operation);
127
128 AssignPolicy::apply(tp, pi, qi, inters);
129 *out++ = tp;
130 }
131 }
132 break;
133 case 'i' :
134 {
135 crosses<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
136 tp, inters.i_info(), inters.d_info());
137
138 replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
139
140 AssignPolicy::apply(tp, pi, qi, inters);
141 *out++ = tp;
142 }
143 break;
144 case 't' :
145 {
146 // Both touch (both arrive there)
147 if ( get_turn_info_for_endpoint<AssignPolicy, false, true>
148 ::apply(pi, pj, pk, qi, qj, qk,
149 is_p_first, is_p_last, is_q_first, is_q_last,
150 tp_model, inters, method_touch, out) )
151 {
152 // do nothing
153 }
154 else
155 {
156 touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
157 tp, inters.i_info(), inters.d_info(), inters.sides());
158
159 // workarounds for touch<> not taking spikes into account starts here
160 // those was discovered empirically
161 // touch<> is not symmetrical!
162 // P spikes and Q spikes may produce various operations!
163 // TODO: this is not optimal solution - think about rewriting touch<>
164
165 if ( tp.operations[0].operation == operation_blocked
166 && tp.operations[1].operation == operation_blocked )
167 {
168 // two touching spikes on the same line
169 if ( inters.is_spike_p() && inters.is_spike_q() )
170 {
171 tp.operations[0].operation = operation_union;
172 tp.operations[1].operation = operation_union;
173 }
174 else
175 {
176 tp.operations[0].is_collinear = true;
177 tp.operations[1].is_collinear = true;
178 }
179 }
180 else if ( tp.operations[0].operation == operation_blocked )
181 {
182 // a spike on P on the same line with Q1
183 if ( inters.is_spike_p() )
184 {
185 if ( inters.sides().qk_wrt_p1() == 0 )
186 {
187 tp.operations[0].is_collinear = true;
188 }
189 else
190 {
191 tp.operations[0].operation = operation_union;
192 }
193 }
194 else
195 {
196 tp.operations[1].is_collinear = true;
197 }
198 }
199 else if ( tp.operations[1].operation == operation_blocked )
200 {
201 // a spike on Q on the same line with P1
202 if ( inters.is_spike_q() )
203 {
204 if ( inters.sides().pk_wrt_q1() == 0 )
205 {
206 tp.operations[1].is_collinear = true;
207 }
208 else
209 {
210 tp.operations[1].operation = operation_union;
211 }
212 }
213 else
214 {
215 tp.operations[0].is_collinear = true;
216 }
217 }
218 else if ( tp.operations[0].operation == operation_continue
219 && tp.operations[1].operation == operation_continue )
220 {
221 // P spike on the same line with Q2 (opposite)
222 if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
223 && inters.is_spike_p() )
224 {
225 tp.operations[0].operation = operation_union;
226 tp.operations[1].operation = operation_union;
227 }
228 }
229 else if ( tp.operations[0].operation == operation_none
230 && tp.operations[1].operation == operation_none )
231 {
232 // spike not handled by touch<>
233 bool const is_p = inters.is_spike_p();
234 bool const is_q = inters.is_spike_q();
235
236 if ( is_p || is_q )
237 {
238 tp.operations[0].operation = operation_union;
239 tp.operations[1].operation = operation_union;
240
241 if ( inters.sides().pk_wrt_q2() == 0 )
242 {
243 tp.operations[0].operation = operation_continue; // will be converted to i
244 if ( is_p )
245 {
246 tp.operations[0].is_collinear = true;
247 }
248 }
249
250 if ( inters.sides().qk_wrt_p2() == 0 )
251 {
252 tp.operations[1].operation = operation_continue; // will be converted to i
253 if ( is_q )
254 {
255 tp.operations[1].is_collinear = true;
256 }
257 }
258 }
259 }
260
261 // workarounds for touch<> not taking spikes into account ends here
262
263 replace_method_and_operations_tm(tp.method,
264 tp.operations[0].operation,
265 tp.operations[1].operation);
266
267 // TODO: move this into the append_xxx and call for each turn?
268 AssignPolicy::apply(tp, pi, qi, inters);
269
270 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
271 || ! append_opposite_spikes<append_touches>(tp, inters,
272 is_p_last, is_q_last,
273 out) )
274 {
275 *out++ = tp;
276 }
277 }
278 }
279 break;
280 case 'e':
281 {
282 if ( get_turn_info_for_endpoint<AssignPolicy, true, true>
283 ::apply(pi, pj, pk, qi, qj, qk,
284 is_p_first, is_p_last, is_q_first, is_q_last,
285 tp_model, inters, method_equal, out) )
286 {
287 // do nothing
288 }
289 else
290 {
291 tp.operations[0].is_collinear = true;
292 tp.operations[1].is_collinear = true;
293
294 if ( ! inters.d_info().opposite )
295 {
296 // Both equal
297 // or collinear-and-ending at intersection point
298 equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
299 tp, inters.i_info(), inters.d_info(), inters.sides());
300
301 operation_type spike_op
302 = ( tp.operations[0].operation != operation_continue
303 || tp.operations[1].operation != operation_continue ) ?
304 operation_union :
305 operation_continue;
306
307 // transform turn
308 turn_transformer_ec transformer(method_touch);
309 transformer(tp);
310
311 // TODO: move this into the append_xxx and call for each turn?
312 AssignPolicy::apply(tp, pi, qi, inters);
313
314 // conditionally handle spikes
315 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
316 || ! append_collinear_spikes(tp, inters,
317 is_p_last, is_q_last,
318 method_touch, spike_op,
319 out) )
320 {
321 *out++ = tp; // no spikes
322 }
323 }
324 else
325 {
326 // TODO: ignore for spikes or generate something else than opposite?
327
328 equal_opposite
329 <
330 TurnInfo,
331 AssignPolicy
332 >::apply(pi, qi, tp, out, inters);
333 }
334 }
335 }
336 break;
337 case 'c' :
338 {
339 // Collinear
340 if ( get_turn_info_for_endpoint<AssignPolicy, true, true>
341 ::apply(pi, pj, pk, qi, qj, qk,
342 is_p_first, is_p_last, is_q_first, is_q_last,
343 tp_model, inters, method_collinear, out) )
344 {
345 // do nothing
346 }
347 else
348 {
349 // NOTE: this is for spikes since those are set in the turn_transformer_ec
350 tp.operations[0].is_collinear = true;
351 tp.operations[1].is_collinear = true;
352
353 if ( ! inters.d_info().opposite )
354 {
355 method_type method_replace = method_touch_interior;
356 operation_type spike_op = operation_continue;
357
358 if ( inters.d_info().arrival[0] == 0 )
359 {
360 // Collinear, but similar thus handled as equal
361 equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
362 tp, inters.i_info(), inters.d_info(), inters.sides());
363
364 method_replace = method_touch;
365 if ( tp.operations[0].operation != operation_continue
366 || tp.operations[1].operation != operation_continue )
367 {
368 spike_op = operation_union;
369 }
370 }
371 else
372 {
373 collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
374 tp, inters.i_info(), inters.d_info(), inters.sides());
375
376 //method_replace = method_touch_interior;
377 //spike_op = operation_continue;
378 }
379
380 // transform turn
381 turn_transformer_ec transformer(method_replace);
382 transformer(tp);
383
384 // TODO: move this into the append_xxx and call for each turn?
385 AssignPolicy::apply(tp, pi, qi, inters);
386
387 // conditionally handle spikes
388 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
389 || ! append_collinear_spikes(tp, inters,
390 is_p_last, is_q_last,
391 method_replace, spike_op,
392 out) )
393 {
394 // no spikes
395 *out++ = tp;
396 }
397 }
398 else
399 {
400 // If this always 'm' ?
401 turn_transformer_ec transformer(method_touch_interior);
402
403 // conditionally handle spikes
404 if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
405 {
406 append_opposite_spikes<append_collinear_opposite>(tp, inters,
407 is_p_last, is_q_last,
408 out);
409 }
410
411 // TODO: ignore for spikes?
412 // E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
413 // the same with is_q_valid
414
415 collinear_opposite
416 <
417 TurnInfo,
418 AssignPolicy
419 >::apply(pi, pj, pk, qi, qj, qk,
420 tp, out, inters, inters.sides(),
421 transformer, !is_p_last, !is_q_last);
422 }
423 }
424 }
425 break;
426 case '0' :
427 {
428 // degenerate points
429 if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
430 {
431 only_convert::apply(tp, inters.i_info());
432
433 // if any, only one of those should be true
434 if ( is_p_first
435 && equals::equals_point_point(pi, tp.point) )
436 {
437 tp.operations[0].position = position_front;
438 }
439 else if ( is_p_last
440 && equals::equals_point_point(pj, tp.point) )
441 {
442 tp.operations[0].position = position_back;
443 }
444 else if ( is_q_first
445 && equals::equals_point_point(qi, tp.point) )
446 {
447 tp.operations[1].position = position_front;
448 }
449 else if ( is_q_last
450 && equals::equals_point_point(qj, tp.point) )
451 {
452 tp.operations[1].position = position_back;
453 }
454
455 AssignPolicy::apply(tp, pi, qi, inters);
456 *out++ = tp;
457 }
458 }
459 break;
460 default :
461 {
462 #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
463 std::cout << "TURN: Unknown method: " << method << std::endl;
464 #endif
465 #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
466 throw turn_info_exception(method);
467 #endif
468 }
469 break;
470 }
471
472 return out;
473 }
474
475 template <typename TurnInfo,
476 typename IntersectionInfo,
477 typename OutIt>
478 static inline bool append_collinear_spikes(TurnInfo & tp,
479 IntersectionInfo const& inters_info,
480 bool is_p_last, bool is_q_last,
481 method_type method, operation_type spike_op,
482 OutIt out)
483 {
484 // method == touch || touch_interior
485 // both position == middle
486
487 bool is_p_spike = tp.operations[0].operation == spike_op
488 && ! is_p_last
489 && inters_info.is_spike_p();
490 bool is_q_spike = tp.operations[1].operation == spike_op
491 && ! is_q_last
492 && inters_info.is_spike_q();
493
494 if ( is_p_spike && is_q_spike )
495 {
496 if ( tp.method == method_equal
497 && tp.operations[0].operation == operation_continue
498 && tp.operations[1].operation == operation_continue )
499 {
500 // treat both non-opposite collinear spikes as no-spikes
501 return false;
502 }
503
504 tp.method = method;
505 tp.operations[0].operation = operation_blocked;
506 tp.operations[1].operation = operation_blocked;
507 *out++ = tp;
508 tp.operations[0].operation = operation_intersection;
509 tp.operations[1].operation = operation_intersection;
510 *out++ = tp;
511
512 return true;
513 }
514 else if ( is_p_spike )
515 {
516 tp.method = method;
517 tp.operations[0].operation = operation_blocked;
518 tp.operations[1].operation = operation_union;
519 *out++ = tp;
520 tp.operations[0].operation = operation_intersection;
521 //tp.operations[1].operation = operation_union;
522 *out++ = tp;
523
524 return true;
525 }
526 else if ( is_q_spike )
527 {
528 tp.method = method;
529 tp.operations[0].operation = operation_union;
530 tp.operations[1].operation = operation_blocked;
531 *out++ = tp;
532 //tp.operations[0].operation = operation_union;
533 tp.operations[1].operation = operation_intersection;
534 *out++ = tp;
535
536 return true;
537 }
538
539 return false;
540 }
541
542 enum append_version { append_touches, append_collinear_opposite };
543
544 template <append_version Version,
545 typename TurnInfo,
546 typename IntersectionInfo,
547 typename OutIt>
548 static inline bool append_opposite_spikes(TurnInfo & tp,
549 IntersectionInfo const& inters,
550 bool is_p_last, bool is_q_last,
551 OutIt out)
552 {
553 static const bool is_version_touches = (Version == append_touches);
554
555 bool is_p_spike = ( is_version_touches ?
556 ( tp.operations[0].operation == operation_continue
557 || tp.operations[0].operation == operation_intersection ) :
558 true )
559 && ! is_p_last
560 && inters.is_spike_p();
561 bool is_q_spike = ( is_version_touches ?
562 ( tp.operations[1].operation == operation_continue
563 || tp.operations[1].operation == operation_intersection ) :
564 true )
565 && ! is_q_last
566 && inters.is_spike_q();
567
568 bool res = false;
569
570 if ( is_p_spike
571 && ( BOOST_GEOMETRY_CONDITION(is_version_touches)
572 || inters.d_info().arrival[0] == 1 ) )
573 {
574 if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
575 {
576 tp.operations[0].is_collinear = true;
577 tp.operations[1].is_collinear = false;
578 tp.method = method_touch;
579 }
580 else // Version == append_collinear_opposite
581 {
582 tp.operations[0].is_collinear = true;
583 tp.operations[1].is_collinear = false;
584
585 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
586
587 base_turn_handler::assign_point(tp, method_touch_interior,
588 inters.i_info(), 1);
589
590 AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters);
591 }
592
593 tp.operations[0].operation = operation_blocked;
594 tp.operations[1].operation = operation_intersection;
595 *out++ = tp;
596 tp.operations[0].operation = operation_intersection;
597 //tp.operations[1].operation = operation_intersection;
598 *out++ = tp;
599
600 res = true;
601 }
602
603 if ( is_q_spike
604 && ( BOOST_GEOMETRY_CONDITION(is_version_touches)
605 || inters.d_info().arrival[1] == 1 ) )
606 {
607 if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
608 {
609 tp.operations[0].is_collinear = false;
610 tp.operations[1].is_collinear = true;
611 tp.method = method_touch;
612 }
613 else // Version == append_collinear_opposite
614 {
615 tp.operations[0].is_collinear = false;
616 tp.operations[1].is_collinear = true;
617
618 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 0);
619
620 base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0);
621
622 AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters);
623 }
624
625 tp.operations[0].operation = operation_intersection;
626 tp.operations[1].operation = operation_blocked;
627 *out++ = tp;
628 //tp.operations[0].operation = operation_intersection;
629 tp.operations[1].operation = operation_intersection;
630 *out++ = tp;
631
632 res = true;
633 }
634
635 return res;
636 }
637
638 static inline void replace_method_and_operations_tm(method_type & method,
639 operation_type & op0,
640 operation_type & op1)
641 {
642 if ( op0 == operation_blocked && op1 == operation_blocked )
643 {
644 // NOTE: probably only if methods are WRT IPs, not segments!
645 method = (method == method_touch ? method_equal : method_collinear);
646 op0 = operation_continue;
647 op1 = operation_continue;
648 }
649 else
650 {
651 if ( op0 == operation_continue || op0 == operation_blocked )
652 {
653 op0 = operation_intersection;
654 }
655 else if ( op0 == operation_intersection )
656 {
657 op0 = operation_union;
658 }
659
660 if ( op1 == operation_continue || op1 == operation_blocked )
661 {
662 op1 = operation_intersection;
663 }
664 else if ( op1 == operation_intersection )
665 {
666 op1 = operation_union;
667 }
668
669 // spikes in 'm'
670 if ( method == method_error )
671 {
672 method = method_touch_interior;
673 op0 = operation_union;
674 op1 = operation_union;
675 }
676 }
677 }
678
679 class turn_transformer_ec
680 {
681 public:
682 explicit turn_transformer_ec(method_type method_t_or_m)
683 : m_method(method_t_or_m)
684 {}
685
686 template <typename Turn>
687 void operator()(Turn & turn) const
688 {
689 operation_type & op0 = turn.operations[0].operation;
690 operation_type & op1 = turn.operations[1].operation;
691
692 BOOST_GEOMETRY_ASSERT(op0 != operation_blocked || op1 != operation_blocked );
693
694 if ( op0 == operation_blocked )
695 {
696 op0 = operation_intersection;
697 }
698 else if ( op0 == operation_intersection )
699 {
700 op0 = operation_union;
701 }
702
703 if ( op1 == operation_blocked )
704 {
705 op1 = operation_intersection;
706 }
707 else if ( op1 == operation_intersection )
708 {
709 op1 = operation_union;
710 }
711
712 if ( op0 == operation_intersection || op0 == operation_union
713 || op1 == operation_intersection || op1 == operation_union )
714 {
715 turn.method = m_method;
716 }
717
718 // TODO: is this correct?
719 // it's equivalent to comparing to operation_blocked at the beginning of the function
720 turn.operations[0].is_collinear = op0 != operation_intersection;
721 turn.operations[1].is_collinear = op1 != operation_intersection;
722 }
723
724 private:
725 method_type m_method;
726 };
727
728 static inline void replace_operations_i(operation_type & op0, operation_type & op1)
729 {
730 if ( op0 == operation_intersection )
731 {
732 op0 = operation_union;
733 }
734
735 if ( op1 == operation_intersection )
736 {
737 op1 = operation_union;
738 }
739 }
740 };
741
742 }} // namespace detail::overlay
743 #endif // DOXYGEN_NO_DETAIL
744
745 }} // namespace boost::geometry
746
747 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LL_HPP