]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / geometry / algorithms / detail / overlay / get_turn_info_la.hpp
CommitLineData
7c673cae
FG
1// Boost.Geometry (aka GGL, Generic Geometry Library)
2
3// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
b32b8144 4// Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
7c673cae 5
92f5a8d4
TL
6// This file was modified by Oracle on 2013, 2014, 2015, 2017, 2018.
7// Modifications copyright (c) 2013-2018 Oracle and/or its affiliates.
7c673cae
FG
8
9// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
10
11// Use, modification and distribution is subject to the Boost Software License,
12// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
13// http://www.boost.org/LICENSE_1_0.txt)
14
15#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
16#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
17
b32b8144
FG
18#include <boost/throw_exception.hpp>
19
7c673cae
FG
20#include <boost/geometry/core/assert.hpp>
21
22#include <boost/geometry/util/condition.hpp>
23
24#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
25#include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
26
27// TEMP, for spikes detector
28//#include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp>
29
30namespace boost { namespace geometry {
31
32#ifndef DOXYGEN_NO_DETAIL
33namespace detail { namespace overlay {
34
35template<typename AssignPolicy>
36struct get_turn_info_linear_areal
37{
38 // Currently only Linear spikes are handled
39 // Areal spikes are ignored
40 static const bool handle_spikes = true;
41
42 template
43 <
92f5a8d4
TL
44 typename UniqueSubRange1,
45 typename UniqueSubRange2,
7c673cae 46 typename TurnInfo,
92f5a8d4 47 typename UmbrellaStrategy,
7c673cae
FG
48 typename RobustPolicy,
49 typename OutputIterator
50 >
51 static inline OutputIterator apply(
92f5a8d4
TL
52 UniqueSubRange1 const& range_p,
53 UniqueSubRange2 const& range_q,
7c673cae 54 TurnInfo const& tp_model,
92f5a8d4 55 UmbrellaStrategy const& umbrella_strategy,
7c673cae
FG
56 RobustPolicy const& robust_policy,
57 OutputIterator out)
58 {
b32b8144
FG
59 typedef intersection_info
60 <
92f5a8d4 61 UniqueSubRange1, UniqueSubRange2,
b32b8144 62 typename TurnInfo::point_type,
92f5a8d4 63 UmbrellaStrategy,
b32b8144
FG
64 RobustPolicy
65 > inters_info;
7c673cae 66
92f5a8d4 67 inters_info inters(range_p, range_q, umbrella_strategy, robust_policy);
7c673cae
FG
68
69 char const method = inters.d_info().how;
70
71 // Copy, to copy possibly extended fields
72 TurnInfo tp = tp_model;
73
74 // Select method and apply
75 switch(method)
76 {
77 case 'a' : // collinear, "at"
78 case 'f' : // collinear, "from"
79 case 's' : // starts from the middle
92f5a8d4
TL
80 get_turn_info_for_endpoint<true, true>(range_p, range_q,
81 tp_model, inters, method_none, out,
82 umbrella_strategy.get_point_in_point_strategy());
7c673cae
FG
83 break;
84
85 case 'd' : // disjoint: never do anything
86 break;
87
88 case 'm' :
89 {
92f5a8d4
TL
90 if ( get_turn_info_for_endpoint<false, true>(range_p, range_q,
91 tp_model, inters, method_touch_interior, out,
92 umbrella_strategy.get_point_in_point_strategy()) )
7c673cae
FG
93 {
94 // do nothing
95 }
96 else
97 {
92f5a8d4 98 typedef touch_interior<TurnInfo> handler;
7c673cae
FG
99
100 // If Q (1) arrives (1)
101 if ( inters.d_info().arrival[1] == 1 )
102 {
92f5a8d4
TL
103 handler::template apply<0>(range_p, range_q, tp,
104 inters.i_info(), inters.d_info(),
105 inters.sides(), umbrella_strategy);
7c673cae
FG
106 }
107 else
108 {
109 // Swap p/q
92f5a8d4 110 handler::template apply<1>(range_q, range_p,
7c673cae 111 tp, inters.i_info(), inters.d_info(),
92f5a8d4 112 inters.get_swapped_sides(), umbrella_strategy);
7c673cae
FG
113 }
114
115 if ( tp.operations[1].operation == operation_blocked )
116 {
117 tp.operations[0].is_collinear = true;
118 }
119
120 replace_method_and_operations_tm(tp.method,
121 tp.operations[0].operation,
122 tp.operations[1].operation);
123
124 // this function assumes that 'u' must be set for a spike
125 calculate_spike_operation(tp.operations[0].operation,
92f5a8d4
TL
126 inters,
127 umbrella_strategy.get_point_in_point_strategy());
7c673cae 128
7c673cae
FG
129 *out++ = tp;
130 }
131 }
132 break;
133 case 'i' :
134 {
92f5a8d4 135 crosses<TurnInfo>::apply(tp, inters.i_info(), inters.d_info());
7c673cae
FG
136
137 replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
138
7c673cae
FG
139 *out++ = tp;
140 }
141 break;
142 case 't' :
143 {
144 // Both touch (both arrive there)
92f5a8d4
TL
145 if ( get_turn_info_for_endpoint<false, true>(range_p, range_q,
146 tp_model, inters, method_touch, out,
147 umbrella_strategy.get_point_in_point_strategy()) )
7c673cae
FG
148 {
149 // do nothing
150 }
151 else
152 {
92f5a8d4
TL
153 touch<TurnInfo>::apply(range_p, range_q, tp,
154 inters.i_info(), inters.d_info(), inters.sides(),
155 umbrella_strategy);
7c673cae
FG
156
157 if ( tp.operations[1].operation == operation_blocked )
158 {
159 tp.operations[0].is_collinear = true;
160 }
161
162 // workarounds for touch<> not taking spikes into account starts here
163 // those was discovered empirically
164 // touch<> is not symmetrical!
165 // P spikes and Q spikes may produce various operations!
166 // Only P spikes are valid for L/A
167 // TODO: this is not optimal solution - think about rewriting touch<>
168
169 if ( tp.operations[0].operation == operation_blocked )
170 {
171 // a spike on P on the same line with Q1
172 if ( inters.is_spike_p() )
173 {
174 if ( inters.sides().qk_wrt_p1() == 0 )
175 {
176 tp.operations[0].is_collinear = true;
177 }
178 else
179 {
180 tp.operations[0].operation = operation_union;
181 }
182 }
183 }
184 else if ( tp.operations[0].operation == operation_continue
185 && tp.operations[1].operation == operation_continue )
186 {
187 // P spike on the same line with Q2 (opposite)
188 if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
189 && inters.is_spike_p() )
190 {
191 tp.operations[0].operation = operation_union;
192 tp.operations[1].operation = operation_union;
193 }
194 }
195 else if ( tp.operations[0].operation == operation_none
196 && tp.operations[1].operation == operation_none )
197 {
198 // spike not handled by touch<>
199 if ( inters.is_spike_p() )
200 {
201 tp.operations[0].operation = operation_intersection;
202 tp.operations[1].operation = operation_union;
203
204 if ( inters.sides().pk_wrt_q2() == 0 )
205 {
206 tp.operations[0].operation = operation_continue; // will be converted to i
207 tp.operations[0].is_collinear = true;
208 }
209 }
210 }
211
212 // workarounds for touch<> not taking spikes into account ends here
213
214 replace_method_and_operations_tm(tp.method,
215 tp.operations[0].operation,
216 tp.operations[1].operation);
217
218 bool ignore_spike
219 = calculate_spike_operation(tp.operations[0].operation,
92f5a8d4
TL
220 inters,
221 umbrella_strategy.get_point_in_point_strategy());
7c673cae
FG
222
223 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
224 || ignore_spike
225 || ! append_opposite_spikes<append_touches>( // for 'i' or 'c' i???
92f5a8d4 226 tp, inters, out) )
7c673cae
FG
227 {
228 *out++ = tp;
229 }
230 }
231 }
232 break;
233 case 'e':
234 {
92f5a8d4
TL
235 if ( get_turn_info_for_endpoint<true, true>(range_p, range_q,
236 tp_model, inters, method_equal, out,
237 umbrella_strategy.get_point_in_point_strategy()) )
7c673cae
FG
238 {
239 // do nothing
240 }
241 else
242 {
243 tp.operations[0].is_collinear = true;
244
245 if ( ! inters.d_info().opposite )
246 {
247 // Both equal
248 // or collinear-and-ending at intersection point
92f5a8d4
TL
249 equal<TurnInfo>::apply(range_p, range_q, tp,
250 inters.i_info(), inters.d_info(), inters.sides(),
251 umbrella_strategy);
7c673cae
FG
252
253 turn_transformer_ec<false> transformer(method_touch);
254 transformer(tp);
92f5a8d4 255
7c673cae
FG
256 // conditionally handle spikes
257 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
92f5a8d4 258 || ! append_collinear_spikes(tp, inters,
7c673cae
FG
259 method_touch, append_equal, out) )
260 {
261 *out++ = tp; // no spikes
262 }
263 }
264 else
265 {
266 equal_opposite
267 <
268 TurnInfo,
269 AssignPolicy
92f5a8d4 270 >::apply(range_p, range_q,
7c673cae
FG
271 tp, out, inters);
272 }
273 }
274 }
275 break;
276 case 'c' :
277 {
278 // Collinear
279 if ( get_turn_info_for_endpoint<true, true>(
92f5a8d4
TL
280 range_p, range_q,
281 tp_model, inters, method_collinear, out,
282 umbrella_strategy.get_point_in_point_strategy()) )
7c673cae
FG
283 {
284 // do nothing
285 }
286 else
287 {
288 tp.operations[0].is_collinear = true;
289
290 if ( ! inters.d_info().opposite )
291 {
292 method_type method_replace = method_touch_interior;
293 append_version_c version = append_collinear;
294
295 if ( inters.d_info().arrival[0] == 0 )
296 {
297 // Collinear, but similar thus handled as equal
92f5a8d4
TL
298 equal<TurnInfo>::apply(range_p, range_q, tp,
299 inters.i_info(), inters.d_info(), inters.sides(),
300 umbrella_strategy);
7c673cae
FG
301
302 method_replace = method_touch;
303 version = append_equal;
304 }
305 else
306 {
92f5a8d4
TL
307 collinear<TurnInfo>::apply(range_p, range_q, tp,
308 inters.i_info(), inters.d_info(), inters.sides());
7c673cae
FG
309
310 //method_replace = method_touch_interior;
311 //version = append_collinear;
312 }
313
314 turn_transformer_ec<false> transformer(method_replace);
315 transformer(tp);
316
7c673cae
FG
317 // conditionally handle spikes
318 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
92f5a8d4 319 || ! append_collinear_spikes(tp, inters,
7c673cae
FG
320 method_replace, version, out) )
321 {
322 // no spikes
323 *out++ = tp;
324 }
325 }
326 else
327 {
328 // Is this always 'm' ?
329 turn_transformer_ec<false> transformer(method_touch_interior);
330
331 // conditionally handle spikes
332 if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
333 {
334 append_opposite_spikes<append_collinear_opposite>(
92f5a8d4 335 tp, inters, out);
7c673cae
FG
336 }
337
338 // TODO: ignore for spikes?
339 // E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
340 // the same with is_q_valid
341
342 collinear_opposite
343 <
344 TurnInfo,
345 AssignPolicy
92f5a8d4 346 >::apply(range_p, range_q,
7c673cae 347 tp, out, inters,
92f5a8d4 348 inters.sides(), transformer);
7c673cae
FG
349 }
350 }
351 }
352 break;
353 case '0' :
354 {
355 // degenerate points
356 if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
357 {
358 only_convert::apply(tp, inters.i_info());
359
92f5a8d4
TL
360 if ( range_p.is_first_segment()
361 && equals::equals_point_point(range_p.at(0), tp.point,
362 umbrella_strategy.get_point_in_point_strategy()) )
7c673cae
FG
363 {
364 tp.operations[0].position = position_front;
365 }
92f5a8d4
TL
366 else if ( range_p.is_last_segment()
367 && equals::equals_point_point(range_p.at(1), tp.point,
368 umbrella_strategy.get_point_in_point_strategy()) )
7c673cae
FG
369 {
370 tp.operations[0].position = position_back;
371 }
372 // tp.operations[1].position = position_middle;
373
7c673cae
FG
374 *out++ = tp;
375 }
376 }
377 break;
378 default :
379 {
380#if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
381 std::cout << "TURN: Unknown method: " << method << std::endl;
382#endif
383#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
b32b8144 384 BOOST_THROW_EXCEPTION(turn_info_exception(method));
7c673cae
FG
385#endif
386 }
387 break;
388 }
389
390 return out;
391 }
392
393 template <typename Operation,
92f5a8d4
TL
394 typename IntersectionInfo,
395 typename EqPPStrategy>
7c673cae
FG
396 static inline bool calculate_spike_operation(Operation & op,
397 IntersectionInfo const& inters,
92f5a8d4 398 EqPPStrategy const& strategy)
7c673cae
FG
399 {
400 bool is_p_spike = ( op == operation_union || op == operation_intersection )
7c673cae
FG
401 && inters.is_spike_p();
402
403 if ( is_p_spike )
404 {
405 int const pk_q1 = inters.sides().pk_wrt_q1();
406
407 bool going_in = pk_q1 < 0; // Pk on the right
408 bool going_out = pk_q1 > 0; // Pk on the left
409
410 int const qk_q1 = inters.sides().qk_wrt_q1();
411
412 // special cases
413 if ( qk_q1 < 0 ) // Q turning R
414 {
415 // spike on the edge point
416 // if it's already known that the spike is going out this musn't be checked
417 if ( ! going_out
92f5a8d4 418 && detail::equals::equals_point_point(inters.rpj(), inters.rqj(), strategy) )
7c673cae
FG
419 {
420 int const pk_q2 = inters.sides().pk_wrt_q2();
421 going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both
422 going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them
423 }
424 }
425 else if ( qk_q1 > 0 ) // Q turning L
426 {
427 // spike on the edge point
428 // if it's already known that the spike is going in this musn't be checked
429 if ( ! going_in
92f5a8d4 430 && detail::equals::equals_point_point(inters.rpj(), inters.rqj(), strategy) )
7c673cae
FG
431 {
432 int const pk_q2 = inters.sides().pk_wrt_q2();
433 going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them
434 going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both
435 }
436 }
437
438 if ( going_in )
439 {
440 op = operation_intersection;
441 return true;
442 }
443 else if ( going_out )
444 {
445 op = operation_union;
446 return true;
447 }
448 }
449
450 return false;
451 }
452
453 enum append_version_c { append_equal, append_collinear };
454
455 template <typename TurnInfo,
456 typename IntersectionInfo,
457 typename OutIt>
458 static inline bool append_collinear_spikes(TurnInfo & tp,
459 IntersectionInfo const& inters,
7c673cae
FG
460 method_type method, append_version_c version,
461 OutIt out)
462 {
463 // method == touch || touch_interior
464 // both position == middle
465
466 bool is_p_spike = ( version == append_equal ?
467 ( tp.operations[0].operation == operation_union
468 || tp.operations[0].operation == operation_intersection ) :
469 tp.operations[0].operation == operation_continue )
7c673cae
FG
470 && inters.is_spike_p();
471
472 // TODO: throw an exception for spike in Areal?
473 /*bool is_q_spike = tp.operations[1].operation == operation_continue
474 && inters.is_spike_q();
475
476 // both are collinear spikes on the same IP, we can just follow both
477 if ( is_p_spike && is_q_spike )
478 {
479 return false;
480 }
481 // spike on Linear - it's turning back on the boundary of Areal
482 else*/
483 if ( is_p_spike )
484 {
485 tp.method = method;
486 tp.operations[0].operation = operation_blocked;
487 tp.operations[1].operation = operation_union;
488 *out++ = tp;
489 tp.operations[0].operation = operation_continue; // boundary
490 //tp.operations[1].operation = operation_union;
491 *out++ = tp;
492
493 return true;
494 }
495 // spike on Areal - Linear is going outside
496 /*else if ( is_q_spike )
497 {
498 tp.method = method;
499 tp.operations[0].operation = operation_union;
500 tp.operations[1].operation = operation_continue;
501 *out++ = tp;
502 *out++ = tp;
503
504 return true;
505 }*/
506
507 return false;
508 }
509
510 enum append_version_o { append_touches, append_collinear_opposite };
511
512 template <append_version_o Version,
513 typename TurnInfo,
514 typename IntersectionInfo,
515 typename OutIt>
516 static inline bool append_opposite_spikes(TurnInfo & tp,
517 IntersectionInfo const& inters,
7c673cae
FG
518 OutIt out)
519 {
520 static const bool is_version_touches = (Version == append_touches);
521
522 bool is_p_spike = ( is_version_touches ?
523 ( tp.operations[0].operation == operation_continue
524 || tp.operations[0].operation == operation_intersection ) : // i ???
525 true )
7c673cae
FG
526 && inters.is_spike_p();
527
528 // TODO: throw an exception for spike in Areal?
529 /*bool is_q_spike = ( ( Version == append_touches
530 && tp.operations[1].operation == operation_continue )
531 || ( Version == append_collinear_opposite
532 && tp.operations[1].operation == operation_none ) )
533 && inters.is_spike_q();
534
535 if ( is_p_spike && is_q_spike )
536 {
537 // u/u or nothing?
538 return false;
539 }
540 else*/
541 if ( is_p_spike )
542 {
543 if ( BOOST_GEOMETRY_CONDITION(is_version_touches)
544 || inters.d_info().arrival[0] == 1 )
545 {
546 if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
547 {
548 tp.operations[0].is_collinear = true;
549 //tp.operations[1].is_collinear = false;
550 tp.method = method_touch;
551 }
552 else
553 {
554 tp.operations[0].is_collinear = true;
555 //tp.operations[1].is_collinear = false;
556
557 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
558 base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1);
7c673cae
FG
559 }
560
561 tp.operations[0].operation = operation_blocked;
562 tp.operations[1].operation = operation_continue; // boundary
563 *out++ = tp;
564 tp.operations[0].operation = operation_continue; // boundary
565 //tp.operations[1].operation = operation_continue; // boundary
566 *out++ = tp;
567
568 return true;
569 }
570 }
571 /*else if ( is_q_spike )
572 {
573 tp.operations[0].is_collinear = true;
574 tp.method = is_version_touches ? method_touch : method_touch_interior;
575 tp.operations[0].operation = operation_continue;
576 tp.operations[1].operation = operation_continue; // boundary
577 *out++ = tp;
578 *out++ = tp;
579
580 return true;
581 }*/
582
583 return false;
584 }
585
586 static inline void replace_method_and_operations_tm(method_type & method,
587 operation_type & op0,
588 operation_type & op1)
589 {
590 if ( op0 == operation_blocked && op1 == operation_blocked )
591 {
592 // NOTE: probably only if methods are WRT IPs, not segments!
593 method = (method == method_touch ? method_equal : method_collinear);
594 }
595
596 // Assuming G1 is always Linear
597 if ( op0 == operation_blocked )
598 {
599 op0 = operation_continue;
600 }
601
602 if ( op1 == operation_blocked )
603 {
604 op1 = operation_continue;
605 }
606 else if ( op1 == operation_intersection )
607 {
608 op1 = operation_union;
609 }
610
611 // spikes in 'm'
612 if ( method == method_error )
613 {
614 method = method_touch_interior;
615 op0 = operation_union;
616 op1 = operation_union;
617 }
618 }
619
620 template <bool IsFront>
621 class turn_transformer_ec
622 {
623 public:
624 explicit turn_transformer_ec(method_type method_t_or_m)
625 : m_method(method_t_or_m)
626 {}
627
628 template <typename Turn>
629 void operator()(Turn & turn) const
630 {
631 operation_type & op0 = turn.operations[0].operation;
632 operation_type & op1 = turn.operations[1].operation;
633
634 // NOTE: probably only if methods are WRT IPs, not segments!
635 if ( BOOST_GEOMETRY_CONDITION(IsFront)
636 || op0 == operation_intersection || op0 == operation_union
637 || op1 == operation_intersection || op1 == operation_union )
638 {
639 turn.method = m_method;
640 }
641
642 turn.operations[0].is_collinear = op0 != operation_blocked;
643
644 // Assuming G1 is always Linear
645 if ( op0 == operation_blocked )
646 {
647 op0 = operation_continue;
648 }
649
650 if ( op1 == operation_blocked )
651 {
652 op1 = operation_continue;
653 }
654 else if ( op1 == operation_intersection )
655 {
656 op1 = operation_union;
657 }
658 }
659
660 private:
661 method_type m_method;
662 };
663
664 static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1)
665 {
666 // assuming Linear is always the first one
667 op1 = operation_union;
668 }
669
670 // NOTE: Spikes may NOT be handled for Linear endpoints because it's not
671 // possible to define a spike on an endpoint. Areal geometries must
672 // NOT have spikes at all. One thing that could be done is to throw
673 // an exception when spike is detected in Areal geometry.
674
675 template <bool EnableFirst,
676 bool EnableLast,
92f5a8d4
TL
677 typename UniqueSubRange1,
678 typename UniqueSubRange2,
7c673cae
FG
679 typename TurnInfo,
680 typename IntersectionInfo,
92f5a8d4
TL
681 typename OutputIterator,
682 typename EqPPStrategy>
7c673cae 683 static inline bool get_turn_info_for_endpoint(
92f5a8d4
TL
684 UniqueSubRange1 const& range_p,
685 UniqueSubRange2 const& range_q,
7c673cae
FG
686 TurnInfo const& tp_model,
687 IntersectionInfo const& inters,
688 method_type /*method*/,
92f5a8d4
TL
689 OutputIterator out,
690 EqPPStrategy const& strategy)
7c673cae
FG
691 {
692 namespace ov = overlay;
92f5a8d4 693 typedef ov::get_turn_info_for_endpoint<EnableFirst, EnableLast> get_info_e;
7c673cae
FG
694
695 const std::size_t ip_count = inters.i_info().count;
696 // no intersection points
92f5a8d4
TL
697 if (ip_count == 0)
698 {
7c673cae 699 return false;
92f5a8d4 700 }
7c673cae 701
92f5a8d4
TL
702 if (! range_p.is_first_segment() && ! range_p.is_last_segment())
703 {
704 // P sub-range has no end-points
7c673cae 705 return false;
92f5a8d4 706 }
7c673cae 707
92f5a8d4
TL
708 typename IntersectionInfo::side_strategy_type const& sides
709 = inters.get_side_strategy();
7c673cae 710
92f5a8d4
TL
711 linear_intersections intersections(range_p.at(0),
712 range_q.at(0),
713 inters.result(),
714 range_p.is_last_segment(),
715 range_q.is_last_segment(),
716 strategy);
7c673cae
FG
717 linear_intersections::ip_info const& ip0 = intersections.template get<0>();
718 linear_intersections::ip_info const& ip1 = intersections.template get<1>();
719
720 const bool opposite = inters.d_info().opposite;
721
722 // ANALYSE AND ASSIGN FIRST
723
724 // IP on the first point of Linear Geometry
725 bool was_first_point_handled = false;
726 if ( BOOST_GEOMETRY_CONDITION(EnableFirst)
92f5a8d4 727 && range_p.is_first_segment() && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication
7c673cae
FG
728 {
729 TurnInfo tp = tp_model;
730 tp.operations[0].position = position_front;
731 tp.operations[1].position = position_middle;
732
733 if ( opposite ) // opposite -> collinear
734 {
735 tp.operations[0].operation = operation_continue;
736 tp.operations[1].operation = operation_union;
737 tp.method = ip0.is_qj ? method_touch : method_touch_interior;
738 }
739 else
740 {
92f5a8d4
TL
741 // pi is the intersection point at qj or in the middle of q1
742 // so consider segments
743 // 1. pi at qj: qi-qj-pj and qi-qj-qk
744 // x: qi-qj, y: qj-qk, qz: qk
745 // 2. pi in the middle of q1: qi-pi-pj and qi-pi-qj
746 // x: qi-pi, y: pi-qj, qz: qj
747 // qi-pi, side the same as WRT q1
748 // pi-qj, side the same as WRT q1
749 // qj WRT q1 is 0
750 method_type replaced_method = method_none;
751 int side_pj_y = 0, side_pj_x = 0, side_qz_x = 0;
752 // 1. ip0 or pi at qj
7c673cae
FG
753 if ( ip0.is_qj )
754 {
7c673cae 755 replaced_method = method_touch;
92f5a8d4
TL
756 side_pj_y = sides.apply(range_q.at(1), range_q.at(2), range_p.at(1)); // pj wrt q2
757 side_pj_x = sides.apply(range_q.at(0), range_q.at(1), range_p.at(1)); // pj wrt q1
758 side_qz_x = sides.apply(range_q.at(0), range_q.at(1), range_q.at(2)); // qk wrt q1
7c673cae 759 }
92f5a8d4 760 // 2. ip0 or pi in the middle of q1
7c673cae
FG
761 else
762 {
92f5a8d4
TL
763 replaced_method = method_touch_interior;
764 side_pj_y = sides.apply(range_q.at(0), range_q.at(1), range_p.at(1)); // pj wrt q1
765 side_pj_x = side_pj_y; // pj wrt q1
766 side_qz_x = 0; // qj wrt q1
7c673cae
FG
767 }
768
92f5a8d4
TL
769 std::pair<operation_type, operation_type> operations
770 = get_info_e::operations_of_equal(side_pj_y, side_pj_x, side_qz_x);
771
772 tp.operations[0].operation = operations.first;
773 tp.operations[1].operation = operations.second;
774
7c673cae
FG
775 turn_transformer_ec<true> transformer(replaced_method);
776 transformer(tp);
777 }
778
779 // equals<> or collinear<> will assign the second point,
780 // we'd like to assign the first one
781 base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0);
782
783 // NOTE: is_collinear is not set for the first endpoint of L
784 // for which there is no preceding segment
785 // here is_p_first_ip == true
786 tp.operations[0].is_collinear = false;
787
7c673cae
FG
788 *out++ = tp;
789
790 was_first_point_handled = true;
791 }
792
793 // ANALYSE AND ASSIGN LAST
794
795 // IP on the last point of Linear Geometry
796 if ( BOOST_GEOMETRY_CONDITION(EnableLast)
92f5a8d4 797 && range_p.is_last_segment()
7c673cae
FG
798 && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication
799 {
800 TurnInfo tp = tp_model;
801
802 if ( inters.i_info().count > 1 )
803 {
804 //BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
805 tp.operations[0].is_collinear = true;
806 tp.operations[1].operation = opposite ? operation_continue : operation_union;
807 }
808 else //if ( result.template get<0>().count == 1 )
809 {
92f5a8d4
TL
810 // pj is the intersection point at qj or in the middle of q1
811 // so consider segments
812 // 1. pj at qj: qi-qj-pi and qi-qj-qk
813 // x: qi-qj, y: qj-qk, qz: qk
814 // 2. pj in the middle of q1: qi-pj-pi and qi-pj-qj
815 // x: qi-pj, y: pj-qj, qz: qj
816 // qi-pj, the side is the same as WRT q1
817 // pj-qj, the side is the same as WRT q1
818 // side of qj WRT q1 is 0
819 int side_pi_y = 0, side_pi_x = 0, side_qz_x = 0;
820 // 1. ip0 or pj at qj
821 if ( ip0.is_qj )
822 {
823 side_pi_y = sides.apply(range_q.at(1), range_q.at(2), range_p.at(0)); // pi wrt q2
824 side_pi_x = sides.apply(range_q.at(0), range_q.at(1), range_p.at(0)); // pi wrt q1
825 side_qz_x = sides.apply(range_q.at(0), range_q.at(1), range_q.at(2)); // qk wrt q1
826 }
827 // 2. ip0 or pj in the middle of q1
828 else
829 {
830 side_pi_y = sides.apply(range_q.at(0), range_q.at(1), range_p.at(0)); // pi wrt q1
831 side_pi_x = side_pi_y; // pi wrt q1
832 side_qz_x = 0; // qj wrt q1
833 }
834
835 std::pair<operation_type, operation_type> operations
836 = get_info_e::operations_of_equal(side_pi_y, side_pi_x, side_qz_x);
7c673cae
FG
837
838 tp.operations[0].operation = operations.first;
839 tp.operations[1].operation = operations.second;
840
841 turn_transformer_ec<false> transformer(method_none);
842 transformer(tp);
843
844 tp.operations[0].is_collinear = tp.both(operation_continue);
845 }
846
847 tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior;
848 tp.operations[0].operation = operation_blocked;
849 tp.operations[0].position = position_back;
850 tp.operations[1].position = position_middle;
851
852 // equals<> or collinear<> will assign the second point,
853 // we'd like to assign the first one
854 unsigned int ip_index = ip_count > 1 ? 1 : 0;
855 base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index);
856
7c673cae
FG
857 *out++ = tp;
858
859 // don't ignore the first IP if the segment is opposite
860 return !( opposite && ip_count > 1 ) || was_first_point_handled;
861 }
862
863 // don't ignore anything for now
864 return false;
865 }
92f5a8d4
TL
866
867 template <typename Point1, typename Point2, typename IntersectionStrategy>
868 static inline bool equals_point_point(Point1 const& point1, Point2 const& point2,
869 IntersectionStrategy const& strategy)
870 {
871 return detail::equals::equals_point_point(point1, point2,
872 strategy.get_point_in_point_strategy());
873 }
7c673cae
FG
874};
875
876}} // namespace detail::overlay
877#endif // DOXYGEN_NO_DETAIL
878
879}} // namespace boost::geometry
880
881#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP