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