1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2013, 2014, 2015.
6 // Modifications copyright (c) 2013-2015 Oracle and/or its affiliates.
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
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)
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP
17 #include <boost/geometry/core/assert.hpp>
19 #include <boost/geometry/util/condition.hpp>
21 #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
22 #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp>
24 // TEMP, for spikes detector
25 //#include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp>
27 namespace boost { namespace geometry {
29 #ifndef DOXYGEN_NO_DETAIL
30 namespace detail { namespace overlay {
32 template<typename AssignPolicy>
33 struct get_turn_info_linear_areal
35 // Currently only Linear spikes are handled
36 // Areal spikes are ignored
37 static const bool handle_spikes = true;
44 typename RobustPolicy,
45 typename OutputIterator
47 static inline OutputIterator apply(
48 Point1 const& pi, Point1 const& pj, Point1 const& pk,
49 Point2 const& qi, Point2 const& qj, Point2 const& qk,
50 bool is_p_first, bool is_p_last,
51 bool is_q_first, bool is_q_last,
52 TurnInfo const& tp_model,
53 RobustPolicy const& robust_policy,
56 typedef intersection_info<Point1, Point2, typename TurnInfo::point_type, RobustPolicy>
59 inters_info inters(pi, pj, pk, qi, qj, qk, robust_policy);
61 char const method = inters.d_info().how;
63 // Copy, to copy possibly extended fields
64 TurnInfo tp = tp_model;
66 // Select method and apply
69 case 'a' : // collinear, "at"
70 case 'f' : // collinear, "from"
71 case 's' : // starts from the middle
72 get_turn_info_for_endpoint<true, true>(
73 pi, pj, pk, qi, qj, qk,
74 is_p_first, is_p_last, is_q_first, is_q_last,
75 tp_model, inters, method_none, out);
78 case 'd' : // disjoint: never do anything
83 if ( get_turn_info_for_endpoint<false, true>(
84 pi, pj, pk, qi, qj, qk,
85 is_p_first, is_p_last, is_q_first, is_q_last,
86 tp_model, inters, method_touch_interior, out) )
92 typedef touch_interior
97 // If Q (1) arrives (1)
98 if ( inters.d_info().arrival[1] == 1 )
100 policy::template apply<0>(pi, pj, pk, qi, qj, qk,
101 tp, inters.i_info(), inters.d_info(),
109 typename inters_info::cs_tag,
110 typename inters_info::robust_point2_type,
111 typename inters_info::robust_point1_type
112 > swapped_side_calc(inters.rqi(), inters.rqj(), inters.rqk(),
113 inters.rpi(), inters.rpj(), inters.rpk());
114 policy::template apply<1>(qi, qj, qk, pi, pj, pk,
115 tp, inters.i_info(), inters.d_info(),
119 if ( tp.operations[1].operation == operation_blocked )
121 tp.operations[0].is_collinear = true;
124 replace_method_and_operations_tm(tp.method,
125 tp.operations[0].operation,
126 tp.operations[1].operation);
128 // this function assumes that 'u' must be set for a spike
129 calculate_spike_operation(tp.operations[0].operation,
132 AssignPolicy::apply(tp, pi, qi, inters);
140 crosses<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
141 tp, inters.i_info(), inters.d_info());
143 replace_operations_i(tp.operations[0].operation, tp.operations[1].operation);
145 AssignPolicy::apply(tp, pi, qi, inters);
151 // Both touch (both arrive there)
152 if ( get_turn_info_for_endpoint<false, true>(
153 pi, pj, pk, qi, qj, qk,
154 is_p_first, is_p_last, is_q_first, is_q_last,
155 tp_model, inters, method_touch, out) )
161 touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
162 tp, inters.i_info(), inters.d_info(), inters.sides());
164 if ( tp.operations[1].operation == operation_blocked )
166 tp.operations[0].is_collinear = true;
169 // workarounds for touch<> not taking spikes into account starts here
170 // those was discovered empirically
171 // touch<> is not symmetrical!
172 // P spikes and Q spikes may produce various operations!
173 // Only P spikes are valid for L/A
174 // TODO: this is not optimal solution - think about rewriting touch<>
176 if ( tp.operations[0].operation == operation_blocked )
178 // a spike on P on the same line with Q1
179 if ( inters.is_spike_p() )
181 if ( inters.sides().qk_wrt_p1() == 0 )
183 tp.operations[0].is_collinear = true;
187 tp.operations[0].operation = operation_union;
191 else if ( tp.operations[0].operation == operation_continue
192 && tp.operations[1].operation == operation_continue )
194 // P spike on the same line with Q2 (opposite)
195 if ( inters.sides().pk_wrt_q1() == -inters.sides().qk_wrt_q1()
196 && inters.is_spike_p() )
198 tp.operations[0].operation = operation_union;
199 tp.operations[1].operation = operation_union;
202 else if ( tp.operations[0].operation == operation_none
203 && tp.operations[1].operation == operation_none )
205 // spike not handled by touch<>
206 if ( inters.is_spike_p() )
208 tp.operations[0].operation = operation_intersection;
209 tp.operations[1].operation = operation_union;
211 if ( inters.sides().pk_wrt_q2() == 0 )
213 tp.operations[0].operation = operation_continue; // will be converted to i
214 tp.operations[0].is_collinear = true;
219 // workarounds for touch<> not taking spikes into account ends here
221 replace_method_and_operations_tm(tp.method,
222 tp.operations[0].operation,
223 tp.operations[1].operation);
226 = calculate_spike_operation(tp.operations[0].operation,
229 // TODO: move this into the append_xxx and call for each turn?
230 AssignPolicy::apply(tp, pi, qi, inters);
232 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
234 || ! append_opposite_spikes<append_touches>( // for 'i' or 'c' i???
235 tp, inters, is_p_last, is_q_last, out) )
244 if ( get_turn_info_for_endpoint<true, true>(
245 pi, pj, pk, qi, qj, qk,
246 is_p_first, is_p_last, is_q_first, is_q_last,
247 tp_model, inters, method_equal, out) )
253 tp.operations[0].is_collinear = true;
255 if ( ! inters.d_info().opposite )
258 // or collinear-and-ending at intersection point
259 equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
260 tp, inters.i_info(), inters.d_info(), inters.sides());
262 turn_transformer_ec<false> transformer(method_touch);
265 // TODO: move this into the append_xxx and call for each turn?
266 AssignPolicy::apply(tp, pi, qi, inters);
268 // conditionally handle spikes
269 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
270 || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last,
271 method_touch, append_equal, out) )
273 *out++ = tp; // no spikes
291 if ( get_turn_info_for_endpoint<true, true>(
292 pi, pj, pk, qi, qj, qk,
293 is_p_first, is_p_last, is_q_first, is_q_last,
294 tp_model, inters, method_collinear, out) )
300 tp.operations[0].is_collinear = true;
302 if ( ! inters.d_info().opposite )
304 method_type method_replace = method_touch_interior;
305 append_version_c version = append_collinear;
307 if ( inters.d_info().arrival[0] == 0 )
309 // Collinear, but similar thus handled as equal
310 equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
311 tp, inters.i_info(), inters.d_info(), inters.sides());
313 method_replace = method_touch;
314 version = append_equal;
318 collinear<TurnInfo>::apply(pi, pj, pk, qi, qj, qk,
319 tp, inters.i_info(), inters.d_info(), inters.sides());
321 //method_replace = method_touch_interior;
322 //version = append_collinear;
325 turn_transformer_ec<false> transformer(method_replace);
328 // TODO: move this into the append_xxx and call for each turn?
329 AssignPolicy::apply(tp, pi, qi, inters);
331 // conditionally handle spikes
332 if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes)
333 || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last,
334 method_replace, version, out) )
342 // Is this always 'm' ?
343 turn_transformer_ec<false> transformer(method_touch_interior);
345 // conditionally handle spikes
346 if ( BOOST_GEOMETRY_CONDITION(handle_spikes) )
348 append_opposite_spikes<append_collinear_opposite>(
349 tp, inters, is_p_last, is_q_last, out);
352 // TODO: ignore for spikes?
353 // E.g. pass is_p_valid = !is_p_last && !is_pj_spike,
354 // the same with is_q_valid
360 >::apply(pi, pj, pk, qi, qj, qk,
362 inters.sides(), transformer,
363 !is_p_last, true); // qk is always valid
371 if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) )
373 only_convert::apply(tp, inters.i_info());
376 && equals::equals_point_point(pi, tp.point) )
378 tp.operations[0].position = position_front;
381 && equals::equals_point_point(pj, tp.point) )
383 tp.operations[0].position = position_back;
385 // tp.operations[1].position = position_middle;
387 AssignPolicy::apply(tp, pi, qi, inters);
394 #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS)
395 std::cout << "TURN: Unknown method: " << method << std::endl;
397 #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW)
398 throw turn_info_exception(method);
407 template <typename Operation,
408 typename IntersectionInfo>
409 static inline bool calculate_spike_operation(Operation & op,
410 IntersectionInfo const& inters,
413 bool is_p_spike = ( op == operation_union || op == operation_intersection )
415 && inters.is_spike_p();
419 int const pk_q1 = inters.sides().pk_wrt_q1();
421 bool going_in = pk_q1 < 0; // Pk on the right
422 bool going_out = pk_q1 > 0; // Pk on the left
424 int const qk_q1 = inters.sides().qk_wrt_q1();
427 if ( qk_q1 < 0 ) // Q turning R
429 // spike on the edge point
430 // if it's already known that the spike is going out this musn't be checked
432 && equals::equals_point_point(inters.rpj(), inters.rqj()) )
434 int const pk_q2 = inters.sides().pk_wrt_q2();
435 going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both
436 going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them
439 else if ( qk_q1 > 0 ) // Q turning L
441 // spike on the edge point
442 // if it's already known that the spike is going in this musn't be checked
444 && equals::equals_point_point(inters.rpj(), inters.rqj()) )
446 int const pk_q2 = inters.sides().pk_wrt_q2();
447 going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them
448 going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both
454 op = operation_intersection;
457 else if ( going_out )
459 op = operation_union;
467 enum append_version_c { append_equal, append_collinear };
469 template <typename TurnInfo,
470 typename IntersectionInfo,
472 static inline bool append_collinear_spikes(TurnInfo & tp,
473 IntersectionInfo const& inters,
474 bool is_p_last, bool /*is_q_last*/,
475 method_type method, append_version_c version,
478 // method == touch || touch_interior
479 // both position == middle
481 bool is_p_spike = ( version == append_equal ?
482 ( tp.operations[0].operation == operation_union
483 || tp.operations[0].operation == operation_intersection ) :
484 tp.operations[0].operation == operation_continue )
486 && inters.is_spike_p();
488 // TODO: throw an exception for spike in Areal?
489 /*bool is_q_spike = tp.operations[1].operation == operation_continue
490 && inters.is_spike_q();
492 // both are collinear spikes on the same IP, we can just follow both
493 if ( is_p_spike && is_q_spike )
497 // spike on Linear - it's turning back on the boundary of Areal
502 tp.operations[0].operation = operation_blocked;
503 tp.operations[1].operation = operation_union;
505 tp.operations[0].operation = operation_continue; // boundary
506 //tp.operations[1].operation = operation_union;
511 // spike on Areal - Linear is going outside
512 /*else if ( is_q_spike )
515 tp.operations[0].operation = operation_union;
516 tp.operations[1].operation = operation_continue;
526 enum append_version_o { append_touches, append_collinear_opposite };
528 template <append_version_o Version,
530 typename IntersectionInfo,
532 static inline bool append_opposite_spikes(TurnInfo & tp,
533 IntersectionInfo const& inters,
534 bool is_p_last, bool /*is_q_last*/,
537 static const bool is_version_touches = (Version == append_touches);
539 bool is_p_spike = ( is_version_touches ?
540 ( tp.operations[0].operation == operation_continue
541 || tp.operations[0].operation == operation_intersection ) : // i ???
544 && inters.is_spike_p();
546 // TODO: throw an exception for spike in Areal?
547 /*bool is_q_spike = ( ( Version == append_touches
548 && tp.operations[1].operation == operation_continue )
549 || ( Version == append_collinear_opposite
550 && tp.operations[1].operation == operation_none ) )
551 && inters.is_spike_q();
553 if ( is_p_spike && is_q_spike )
561 if ( BOOST_GEOMETRY_CONDITION(is_version_touches)
562 || inters.d_info().arrival[0] == 1 )
564 if ( BOOST_GEOMETRY_CONDITION(is_version_touches) )
566 tp.operations[0].is_collinear = true;
567 //tp.operations[1].is_collinear = false;
568 tp.method = method_touch;
572 tp.operations[0].is_collinear = true;
573 //tp.operations[1].is_collinear = false;
575 BOOST_GEOMETRY_ASSERT(inters.i_info().count > 1);
576 base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1);
578 AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters);
581 tp.operations[0].operation = operation_blocked;
582 tp.operations[1].operation = operation_continue; // boundary
584 tp.operations[0].operation = operation_continue; // boundary
585 //tp.operations[1].operation = operation_continue; // boundary
591 /*else if ( is_q_spike )
593 tp.operations[0].is_collinear = true;
594 tp.method = is_version_touches ? method_touch : method_touch_interior;
595 tp.operations[0].operation = operation_continue;
596 tp.operations[1].operation = operation_continue; // boundary
606 static inline void replace_method_and_operations_tm(method_type & method,
607 operation_type & op0,
608 operation_type & op1)
610 if ( op0 == operation_blocked && op1 == operation_blocked )
612 // NOTE: probably only if methods are WRT IPs, not segments!
613 method = (method == method_touch ? method_equal : method_collinear);
616 // Assuming G1 is always Linear
617 if ( op0 == operation_blocked )
619 op0 = operation_continue;
622 if ( op1 == operation_blocked )
624 op1 = operation_continue;
626 else if ( op1 == operation_intersection )
628 op1 = operation_union;
632 if ( method == method_error )
634 method = method_touch_interior;
635 op0 = operation_union;
636 op1 = operation_union;
640 template <bool IsFront>
641 class turn_transformer_ec
644 explicit turn_transformer_ec(method_type method_t_or_m)
645 : m_method(method_t_or_m)
648 template <typename Turn>
649 void operator()(Turn & turn) const
651 operation_type & op0 = turn.operations[0].operation;
652 operation_type & op1 = turn.operations[1].operation;
654 // NOTE: probably only if methods are WRT IPs, not segments!
655 if ( BOOST_GEOMETRY_CONDITION(IsFront)
656 || op0 == operation_intersection || op0 == operation_union
657 || op1 == operation_intersection || op1 == operation_union )
659 turn.method = m_method;
662 turn.operations[0].is_collinear = op0 != operation_blocked;
664 // Assuming G1 is always Linear
665 if ( op0 == operation_blocked )
667 op0 = operation_continue;
670 if ( op1 == operation_blocked )
672 op1 = operation_continue;
674 else if ( op1 == operation_intersection )
676 op1 = operation_union;
681 method_type m_method;
684 static inline void replace_operations_i(operation_type & /*op0*/, operation_type & op1)
686 // assuming Linear is always the first one
687 op1 = operation_union;
690 // NOTE: Spikes may NOT be handled for Linear endpoints because it's not
691 // possible to define a spike on an endpoint. Areal geometries must
692 // NOT have spikes at all. One thing that could be done is to throw
693 // an exception when spike is detected in Areal geometry.
695 template <bool EnableFirst,
700 typename IntersectionInfo,
701 typename OutputIterator>
702 static inline bool get_turn_info_for_endpoint(
703 Point1 const& pi, Point1 const& /*pj*/, Point1 const& /*pk*/,
704 Point2 const& qi, Point2 const& /*qj*/, Point2 const& /*qk*/,
705 bool is_p_first, bool is_p_last,
706 bool /*is_q_first*/, bool is_q_last,
707 TurnInfo const& tp_model,
708 IntersectionInfo const& inters,
709 method_type /*method*/,
712 namespace ov = overlay;
713 typedef ov::get_turn_info_for_endpoint<AssignPolicy, EnableFirst, EnableLast> get_info_e;
715 const std::size_t ip_count = inters.i_info().count;
716 // no intersection points
720 if ( !is_p_first && !is_p_last )
723 // TODO: is_q_last could probably be replaced by false and removed from parameters
725 linear_intersections intersections(pi, qi, inters.result(), is_p_last, is_q_last);
726 linear_intersections::ip_info const& ip0 = intersections.template get<0>();
727 linear_intersections::ip_info const& ip1 = intersections.template get<1>();
729 const bool opposite = inters.d_info().opposite;
731 // ANALYSE AND ASSIGN FIRST
733 // IP on the first point of Linear Geometry
734 bool was_first_point_handled = false;
735 if ( BOOST_GEOMETRY_CONDITION(EnableFirst)
736 && is_p_first && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication
738 TurnInfo tp = tp_model;
739 tp.operations[0].position = position_front;
740 tp.operations[1].position = position_middle;
742 if ( opposite ) // opposite -> collinear
744 tp.operations[0].operation = operation_continue;
745 tp.operations[1].operation = operation_union;
746 tp.method = ip0.is_qj ? method_touch : method_touch_interior;
750 method_type replaced_method = method_touch_interior;
756 typename IntersectionInfo::cs_tag,
757 typename IntersectionInfo::robust_point1_type,
758 typename IntersectionInfo::robust_point2_type,
759 typename IntersectionInfo::robust_point2_type
760 > side_calc(inters.rqi(), inters.rpi(), inters.rpj(),
761 inters.rqi(), inters.rqj(), inters.rqk());
763 std::pair<operation_type, operation_type>
764 operations = get_info_e::operations_of_equal(side_calc);
766 tp.operations[0].operation = operations.first;
767 tp.operations[1].operation = operations.second;
769 replaced_method = method_touch;
775 typename IntersectionInfo::cs_tag,
776 typename IntersectionInfo::robust_point1_type,
777 typename IntersectionInfo::robust_point2_type,
778 typename IntersectionInfo::robust_point2_type,
779 typename IntersectionInfo::robust_point1_type,
780 typename IntersectionInfo::robust_point1_type,
781 typename IntersectionInfo::robust_point2_type,
782 typename IntersectionInfo::robust_point1_type,
783 typename IntersectionInfo::robust_point2_type
784 > side_calc(inters.rqi(), inters.rpi(), inters.rpj(),
785 inters.rqi(), inters.rpi(), inters.rqj());
787 std::pair<operation_type, operation_type>
788 operations = get_info_e::operations_of_equal(side_calc);
790 tp.operations[0].operation = operations.first;
791 tp.operations[1].operation = operations.second;
794 turn_transformer_ec<true> transformer(replaced_method);
798 // equals<> or collinear<> will assign the second point,
799 // we'd like to assign the first one
800 base_turn_handler::assign_point(tp, tp.method, inters.i_info(), 0);
802 // NOTE: is_collinear is not set for the first endpoint of L
803 // for which there is no preceding segment
804 // here is_p_first_ip == true
805 tp.operations[0].is_collinear = false;
807 AssignPolicy::apply(tp, pi, qi, inters);
810 was_first_point_handled = true;
813 // ANALYSE AND ASSIGN LAST
815 // IP on the last point of Linear Geometry
816 if ( BOOST_GEOMETRY_CONDITION(EnableLast)
818 && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication
820 TurnInfo tp = tp_model;
822 if ( inters.i_info().count > 1 )
824 //BOOST_GEOMETRY_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 );
825 tp.operations[0].is_collinear = true;
826 tp.operations[1].operation = opposite ? operation_continue : operation_union;
828 else //if ( result.template get<0>().count == 1 )
832 typename IntersectionInfo::cs_tag,
833 typename IntersectionInfo::robust_point1_type,
834 typename IntersectionInfo::robust_point2_type,
835 typename IntersectionInfo::robust_point2_type
836 > side_calc(inters.rqi(), inters.rpj(), inters.rpi(),
837 inters.rqi(), inters.rqj(), inters.rqk());
839 std::pair<operation_type, operation_type>
840 operations = get_info_e::operations_of_equal(side_calc);
842 tp.operations[0].operation = operations.first;
843 tp.operations[1].operation = operations.second;
845 turn_transformer_ec<false> transformer(method_none);
848 tp.operations[0].is_collinear = tp.both(operation_continue);
851 tp.method = ( ip_count > 1 ? ip1.is_qj : ip0.is_qj ) ? method_touch : method_touch_interior;
852 tp.operations[0].operation = operation_blocked;
853 tp.operations[0].position = position_back;
854 tp.operations[1].position = position_middle;
856 // equals<> or collinear<> will assign the second point,
857 // we'd like to assign the first one
858 unsigned int ip_index = ip_count > 1 ? 1 : 0;
859 base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index);
861 AssignPolicy::apply(tp, pi, qi, inters);
864 // don't ignore the first IP if the segment is opposite
865 return !( opposite && ip_count > 1 ) || was_first_point_handled;
868 // don't ignore anything for now
873 }} // namespace detail::overlay
874 #endif // DOXYGEN_NO_DETAIL
876 }} // namespace boost::geometry
878 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP