]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / geometry / algorithms / detail / buffer / turn_in_piece_visitor.hpp
CommitLineData
7c673cae
FG
1// Boost.Geometry (aka GGL, Generic Geometry Library)
2
3// Copyright (c) 2012-2014 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 2016, 2018.
7// Modifications copyright (c) 2016-2018 Oracle and/or its affiliates.
7c673cae
FG
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_BUFFER_TURN_IN_PIECE_VISITOR
15#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
16
17
18#include <boost/core/ignore_unused.hpp>
19
20#include <boost/range.hpp>
21
22#include <boost/geometry/core/assert.hpp>
92f5a8d4 23#include <boost/geometry/core/config.hpp>
7c673cae
FG
24
25#include <boost/geometry/arithmetic/dot_product.hpp>
26#include <boost/geometry/algorithms/assign.hpp>
27#include <boost/geometry/algorithms/comparable_distance.hpp>
28#include <boost/geometry/algorithms/equals.hpp>
29#include <boost/geometry/algorithms/expand.hpp>
30#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp>
31#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
32#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
33#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
34#include <boost/geometry/policies/compare.hpp>
35#include <boost/geometry/strategies/buffer.hpp>
36#include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
37
7c673cae 38#include <boost/geometry/strategies/cartesian/side_of_intersection.hpp>
7c673cae
FG
39
40
41namespace boost { namespace geometry
42{
43
44
45#ifndef DOXYGEN_NO_DETAIL
46namespace detail { namespace buffer
47{
48
49struct piece_get_box
50{
51 template <typename Box, typename Piece>
52 static inline void apply(Box& total, Piece const& piece)
53 {
54 geometry::expand(total, piece.robust_envelope);
55 }
56};
57
92f5a8d4 58template <typename DisjointBoxBoxStrategy>
7c673cae
FG
59struct piece_ovelaps_box
60{
61 template <typename Box, typename Piece>
62 static inline bool apply(Box const& box, Piece const& piece)
63 {
64 if (piece.type == strategy::buffer::buffered_flat_end
65 || piece.type == strategy::buffer::buffered_concave)
66 {
67 // Turns cannot be inside a flat end (though they can be on border)
68 // Neither we need to check if they are inside concave helper pieces
69
70 // Skip all pieces not used as soon as possible
71 return false;
72 }
73
92f5a8d4
TL
74 return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope,
75 DisjointBoxBoxStrategy());
7c673cae
FG
76 }
77};
78
79struct turn_get_box
80{
81 template <typename Box, typename Turn>
82 static inline void apply(Box& total, Turn const& turn)
83 {
84 geometry::expand(total, turn.robust_point);
85 }
86};
87
92f5a8d4 88template <typename DisjointPointBoxStrategy>
7c673cae
FG
89struct turn_ovelaps_box
90{
91 template <typename Box, typename Turn>
92 static inline bool apply(Box const& box, Turn const& turn)
93 {
92f5a8d4
TL
94 return ! geometry::detail::disjoint::disjoint_point_box(turn.robust_point, box,
95 DisjointPointBoxStrategy());
7c673cae
FG
96 }
97};
98
99
100enum analyse_result
101{
102 analyse_unknown,
103 analyse_continue,
104 analyse_disjoint,
105 analyse_within,
106 analyse_on_original_boundary,
92f5a8d4
TL
107 analyse_on_offsetted,
108 analyse_near_offsetted
7c673cae
FG
109};
110
111template <typename Point>
112inline bool in_box(Point const& previous,
113 Point const& current, Point const& point)
114{
115 // Get its box (TODO: this can be prepared-on-demand later)
116 typedef geometry::model::box<Point> box_type;
117 box_type box;
118 geometry::assign_inverse(box);
119 geometry::expand(box, previous);
120 geometry::expand(box, current);
121
122 return geometry::covered_by(point, box);
123}
124
92f5a8d4
TL
125template <typename NumericType>
126inline bool is_one_sided(NumericType const& left, NumericType const& right)
7c673cae 127{
92f5a8d4
TL
128 static NumericType const zero = 0;
129 return geometry::math::equals(left, zero)
130 || geometry::math::equals(right, zero);
131}
7c673cae 132
92f5a8d4
TL
133template <typename Point, typename DistanceStrategy>
134inline bool has_zero_distance_at(Point const& point,
135 DistanceStrategy const& distance_strategy)
136{
137 return is_one_sided(distance_strategy.apply(point, point,
138 strategy::buffer::buffer_side_left),
139 distance_strategy.apply(point, point,
140 strategy::buffer::buffer_side_right));
141}
7c673cae 142
92f5a8d4
TL
143// meta-programming-structure defining if to use side-of-intersection
144// (only for cartesian / only necessary with rescaling)
145template <typename Tag>
146struct use_side_of_intersection {};
7c673cae 147
92f5a8d4
TL
148#if defined(BOOST_GEOMETRY_USE_RESCALING)
149// With rescaling, let Cartesian use side-of-intersection
150template <>
151struct use_side_of_intersection<cartesian_tag> { static bool const value = true; };
7c673cae 152#else
92f5a8d4
TL
153template <>
154struct use_side_of_intersection<cartesian_tag> { static bool const value = false; };
155#endif
7c673cae 156
92f5a8d4
TL
157template <>
158struct use_side_of_intersection<spherical_tag> { static bool const value = false; };
7c673cae 159
92f5a8d4
TL
160template <>
161struct use_side_of_intersection<geographic_tag> { static bool const value = false; };
7c673cae 162
92f5a8d4
TL
163
164template <bool UseSideOfIntersection>
165struct check_segment {};
166
167// Implementation using side-of-intersection
168template <>
169struct check_segment<true>
170{
171 template <typename Point, typename Turn, typename SideStrategy>
172 static inline analyse_result apply(Point const& previous,
173 Point const& current, Turn const& turn,
174 bool from_monotonic,
175 SideStrategy const& )
7c673cae 176 {
92f5a8d4
TL
177 typedef geometry::model::referring_segment<Point const> segment_type;
178 segment_type const p(turn.rob_pi, turn.rob_pj);
179 segment_type const q(turn.rob_qi, turn.rob_qj);
180 segment_type const r(previous, current);
181 int const side = strategy::side::side_of_intersection::apply(p, q, r,
182 turn.robust_point);
183
184 if (side == 0)
7c673cae
FG
185 {
186 return analyse_on_offsetted;
187 }
92f5a8d4
TL
188 if (side == -1 && from_monotonic)
189 {
190 return analyse_within;
191 }
192 if (side == 1 && from_monotonic)
193 {
194 return analyse_disjoint;
195 }
196 return analyse_continue;
7c673cae 197 }
92f5a8d4
TL
198};
199
200template <>
201struct check_segment<false>
202{
203 template <typename Point, typename Turn, typename SideStrategy>
204 static inline analyse_result apply(Point const& previous,
205 Point const& current, Turn const& turn,
206 bool from_monotonic,
207 SideStrategy const& side_strategy)
7c673cae 208 {
92f5a8d4
TL
209 int const side = side_strategy.apply(previous, current, turn.robust_point);
210
211 if (side == 0)
7c673cae 212 {
92f5a8d4
TL
213 // Collinear, only on segment if it is covered by its bbox
214 if (in_box(previous, current, turn.robust_point))
215 {
216 return analyse_on_offsetted;
217 }
218 }
219 else if (side == -1)
220 {
221 // It is in the triangle right-of the segment where the
222 // segment is the hypothenusa. Check if it is close
223 // (within rounding-area)
224 if (in_box(previous, current, turn.robust_point))
225 {
226 return analyse_near_offsetted;
227 }
228 else if (from_monotonic)
229 {
230 return analyse_within;
231 }
7c673cae
FG
232 }
233 else if (from_monotonic)
234 {
92f5a8d4
TL
235 // Left of segment
236 return analyse_disjoint;
7c673cae 237 }
7c673cae 238
92f5a8d4
TL
239 // Not monotonic, on left or right side: continue analysing
240 return analyse_continue;
241 }
242};
7c673cae 243
92f5a8d4
TL
244template <bool UseSideOfIntersection>
245class analyse_turn_wrt_point_piece {};
7c673cae 246
92f5a8d4
TL
247template <>
248class analyse_turn_wrt_point_piece<true>
7c673cae
FG
249{
250public :
92f5a8d4
TL
251 template <typename Turn, typename Piece, typename PointInGeometryStrategy, typename SideStrategy>
252 static inline analyse_result apply(Turn const& turn, Piece const& piece,
253 PointInGeometryStrategy const& ,
254 SideStrategy const& )
7c673cae
FG
255 {
256 typedef typename Piece::section_type section_type;
257 typedef typename Turn::robust_point_type point_type;
258 typedef typename geometry::coordinate_type<point_type>::type coordinate_type;
259
7c673cae
FG
260 typedef geometry::model::referring_segment<point_type const> segment_type;
261 segment_type const p(turn.rob_pi, turn.rob_pj);
262 segment_type const q(turn.rob_qi, turn.rob_qj);
7c673cae
FG
263
264 BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
265
266 coordinate_type const point_x = geometry::get<0>(turn.robust_point);
267
268 for (std::size_t s = 0; s < piece.sections.size(); s++)
269 {
270 section_type const& section = piece.sections[s];
271 // If point within horizontal range of monotonic section:
272 if (! section.duplicate
273 && section.begin_index < section.end_index
274 && point_x >= geometry::get<min_corner, 0>(section.bounding_box) - 1
275 && point_x <= geometry::get<max_corner, 0>(section.bounding_box) + 1)
276 {
277 for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
278 {
279 point_type const& previous = piece.robust_ring[i - 1];
280 point_type const& current = piece.robust_ring[i];
281
7c673cae
FG
282
283 // First check if it is in range - if it is not, the
284 // expensive side_of_intersection does not need to be
285 // applied
286 coordinate_type x1 = geometry::get<0>(previous);
287 coordinate_type x2 = geometry::get<0>(current);
288
289 if (x1 > x2)
290 {
291 std::swap(x1, x2);
292 }
293
294 if (point_x >= x1 - 1 && point_x <= x2 + 1)
295 {
296 segment_type const r(previous, current);
297 int const side = strategy::side::side_of_intersection::apply(p, q, r,
298 turn.robust_point);
299
300 // Sections are monotonic in x-dimension
301 if (side == 1)
302 {
303 // Left on segment
304 return analyse_disjoint;
305 }
306 else if (side == 0)
307 {
308 // Collinear - TODO: check if really on segment
309 return analyse_on_offsetted;
310 }
311 }
92f5a8d4
TL
312 }
313 }
314 }
315
316 // It is nowhere outside, and not on segment, so it is within
317 return analyse_within;
318 }
319
320};
321
322template <>
323class analyse_turn_wrt_point_piece<false>
324{
325public :
326 template <typename Turn, typename Piece, typename PointInGeometryStrategy, typename SideStrategy>
327 static inline analyse_result apply(Turn const& turn, Piece const& piece,
328 PointInGeometryStrategy const& point_in_geometry_strategy,
329 SideStrategy const& side_strategy)
330 {
331 typedef typename Piece::section_type section_type;
332 typedef typename Turn::robust_point_type point_type;
333 typedef typename geometry::coordinate_type<point_type>::type coordinate_type;
334
335 typename PointInGeometryStrategy::state_type state;
336
337 BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
338
339 coordinate_type const point_x = geometry::get<0>(turn.robust_point);
340
341 for (std::size_t s = 0; s < piece.sections.size(); s++)
342 {
343 section_type const& section = piece.sections[s];
344 // If point within horizontal range of monotonic section:
345 if (! section.duplicate
346 && section.begin_index < section.end_index
347 && point_x >= geometry::get<min_corner, 0>(section.bounding_box) - 1
348 && point_x <= geometry::get<max_corner, 0>(section.bounding_box) + 1)
349 {
350 for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
351 {
352 point_type const& previous = piece.robust_ring[i - 1];
353 point_type const& current = piece.robust_ring[i];
354
355 analyse_result code = check_segment<false>::apply(previous, current, turn, false, side_strategy);
7c673cae
FG
356 if (code != analyse_continue)
357 {
358 return code;
359 }
360
361 // Get the state (to determine it is within), we don't have
362 // to cover the on-segment case (covered above)
92f5a8d4 363 point_in_geometry_strategy.apply(turn.robust_point, previous, current, state);
7c673cae
FG
364 }
365 }
366 }
367
92f5a8d4 368 int const code = point_in_geometry_strategy.result(state);
7c673cae
FG
369 if (code == 1)
370 {
371 return analyse_within;
372 }
373 else if (code == -1)
374 {
375 return analyse_disjoint;
376 }
377
378 // Should normally not occur - on-segment is covered
379 return analyse_unknown;
7c673cae
FG
380 }
381
382};
383
92f5a8d4
TL
384template <bool UseSideOfIntersection>
385struct check_helper_segment {};
386
387template <>
388struct check_helper_segment<true>
7c673cae 389{
92f5a8d4
TL
390 template <typename Point, typename Turn, typename SideStrategy>
391 static inline analyse_result apply(Point const& s1,
7c673cae
FG
392 Point const& s2, Turn const& turn,
393 bool is_original,
92f5a8d4
TL
394 analyse_result result_for_original,
395 Point const& offsetted,
396 SideStrategy const& )
7c673cae 397 {
92f5a8d4
TL
398 boost::ignore_unused(offsetted, is_original);
399
7c673cae
FG
400 typedef geometry::model::referring_segment<Point const> segment_type;
401 segment_type const p(turn.rob_pi, turn.rob_pj);
402 segment_type const q(turn.rob_qi, turn.rob_qj);
403 segment_type const r(s1, s2);
404 int const side = strategy::side::side_of_intersection::apply(p, q, r,
405 turn.robust_point);
406
407 if (side == 1)
408 {
409 // left of segment
410 return analyse_disjoint;
411 }
412 else if (side == 0)
413 {
414 // If is collinear, either on segment or before/after
415 typedef geometry::model::box<Point> box_type;
416
417 box_type box;
418 geometry::assign_inverse(box);
419 geometry::expand(box, s1);
420 geometry::expand(box, s2);
421
422 if (geometry::covered_by(turn.robust_point, box))
423 {
92f5a8d4 424 return result_for_original;
7c673cae
FG
425 }
426
427 // It is collinear but not on the segment. Because these
428 // segments are convex, it is outside
429 // Unless the offsetted ring is collinear or concave w.r.t.
430 // helper-segment but that scenario is not yet supported
431 return analyse_disjoint;
432 }
433
434 // right of segment
435 return analyse_continue;
92f5a8d4
TL
436 }
437
438};
7c673cae 439
92f5a8d4
TL
440template <>
441struct check_helper_segment<false>
442{
443 template <typename Point, typename Turn, typename SideStrategy>
444 static inline analyse_result apply(Point const& s1,
445 Point const& s2, Turn const& turn,
446 bool is_original,
447 analyse_result result_for_original,
448 Point const& offsetted,
449 SideStrategy const& side_strategy)
450 {
451 switch(side_strategy.apply(s1, s2, turn.robust_point))
7c673cae
FG
452 {
453 case 1 :
454 return analyse_disjoint; // left of segment
455 case 0 :
456 {
457 // If is collinear, either on segment or before/after
458 typedef geometry::model::box<Point> box_type;
459
460 box_type box;
461 geometry::assign_inverse(box);
462 geometry::expand(box, s1);
463 geometry::expand(box, s2);
464
465 if (geometry::covered_by(turn.robust_point, box))
466 {
467 // It is on the segment
468 if (! is_original
469 && geometry::comparable_distance(turn.robust_point, offsetted) <= 1)
470 {
92f5a8d4
TL
471 // It is within, and close to the offsetted-boundary,
472 // take any rounding-issues into account
7c673cae
FG
473 return analyse_near_offsetted;
474 }
475
476 // Points on helper-segments are considered as within
477 // Points on original boundary are processed differently
92f5a8d4 478 return result_for_original;
7c673cae
FG
479 }
480
481 // It is collinear but not on the segment. Because these
482 // segments are convex, it is outside
483 // Unless the offsetted ring is collinear or concave w.r.t.
484 // helper-segment but that scenario is not yet supported
485 return analyse_disjoint;
486 }
487 break;
488 }
489
490 // right of segment
491 return analyse_continue;
7c673cae 492 }
92f5a8d4 493};
7c673cae 494
92f5a8d4
TL
495template <bool UseSideOfIntersection>
496class analyse_turn_wrt_piece
497{
498 template
499 <
500 typename Turn,
501 typename Piece,
502 typename DistanceStrategy,
503 typename SideStrategy
504 >
505 static inline analyse_result
506 check_helper_segments(Turn const& turn, Piece const& piece,
507 DistanceStrategy const& distance_strategy,
508 SideStrategy const& side_strategy)
7c673cae
FG
509 {
510 typedef typename Turn::robust_point_type point_type;
511 geometry::equal_to<point_type> comparator;
512
513 point_type points[4];
514
515 signed_size_type helper_count = static_cast<signed_size_type>(piece.robust_ring.size())
516 - piece.offsetted_count;
517 if (helper_count == 4)
518 {
519 for (int i = 0; i < 4; i++)
520 {
521 points[i] = piece.robust_ring[piece.offsetted_count + i];
522 }
b32b8144
FG
523
524 // 3--offsetted outline--0
525 // | |
526 // left | | right
527 // | |
528 // 2===>==original===>===1
529
7c673cae
FG
530 }
531 else if (helper_count == 3)
532 {
533 // Triangular piece, assign points but assign second twice
534 for (int i = 0; i < 4; i++)
535 {
536 int index = i < 2 ? i : i - 1;
537 points[i] = piece.robust_ring[piece.offsetted_count + index];
538 }
539 }
540 else
541 {
542 // Some pieces (e.g. around points) do not have helper segments.
543 // Others should have 3 (join) or 4 (side)
544 return analyse_continue;
545 }
546
92f5a8d4
TL
547 // If a turn is located on the original, it is considered as within,
548 // unless it is at a flat start or end, or the buffer (at that point)
549 // is one-sided (zero-distance)
550 bool const one_sided = has_zero_distance_at(turn.point, distance_strategy);
551
552 analyse_result const result_for_original
553 = one_sided || piece.is_flat_end || piece.is_flat_start
554 ? analyse_on_original_boundary
555 : analyse_within;
556
7c673cae
FG
557 // First check point-equality
558 point_type const& point = turn.robust_point;
559 if (comparator(point, points[0]) || comparator(point, points[3]))
560 {
561 return analyse_on_offsetted;
562 }
b32b8144 563 if (comparator(point, points[1]))
7c673cae 564 {
92f5a8d4
TL
565 // On original, with right corner of piece
566 return result_for_original;
b32b8144
FG
567 }
568 if (comparator(point, points[2]))
569 {
92f5a8d4
TL
570 // On original, with left corner of piece
571 return result_for_original;
7c673cae
FG
572 }
573
92f5a8d4 574 // Right side of the piece (never an original)
7c673cae 575 analyse_result result
92f5a8d4
TL
576 = check_helper_segment<UseSideOfIntersection>::apply(points[0], points[1], turn,
577 false, analyse_within, points[0], side_strategy);
7c673cae
FG
578 if (result != analyse_continue)
579 {
580 return result;
581 }
582
92f5a8d4
TL
583 // Left side of the piece (never an original)
584 result = check_helper_segment<UseSideOfIntersection>::apply(points[2], points[3], turn,
585 false, analyse_within, points[3], side_strategy);
7c673cae
FG
586 if (result != analyse_continue)
587 {
588 return result;
589 }
590
92f5a8d4
TL
591 // Side of the piece at side of original geometry
592 // (here flat start/end will result in within)
7c673cae
FG
593 if (! comparator(points[1], points[2]))
594 {
92f5a8d4
TL
595 result = check_helper_segment<UseSideOfIntersection>::apply(points[1],
596 points[2], turn, true,
597 one_sided ? analyse_on_original_boundary : analyse_within,
598 point, side_strategy);
7c673cae
FG
599 if (result != analyse_continue)
600 {
601 return result;
602 }
603 }
604
605 // We are within the \/ or |_| shaped piece, where the top is the
606 // offsetted ring.
607 if (! geometry::covered_by(point, piece.robust_offsetted_envelope))
608 {
609 // Not in offsetted-area. This makes a cheap check possible
92f5a8d4 610 switch(side_strategy.apply(points[3], points[0], point))
7c673cae
FG
611 {
612 case 1 : return analyse_disjoint;
613 case -1 : return analyse_within;
614 case 0 : return analyse_disjoint;
615 }
616 }
617
618 return analyse_continue;
619 }
620
92f5a8d4
TL
621 template <typename Turn, typename Piece, typename Compare, typename SideStrategy>
622 static inline analyse_result check_monotonic(Turn const& turn, Piece const& piece, Compare const& compare, SideStrategy const& side_strategy)
7c673cae
FG
623 {
624 typedef typename Piece::piece_robust_ring_type ring_type;
625 typedef typename ring_type::const_iterator it_type;
626 it_type end = piece.robust_ring.begin() + piece.offsetted_count;
627 it_type it = std::lower_bound(piece.robust_ring.begin(),
628 end,
629 turn.robust_point,
630 compare);
631
632 if (it != end
633 && it != piece.robust_ring.begin())
634 {
635 // iterator points to point larger than point
636 // w.r.t. specified direction, and prev points to a point smaller
637 // We now know if it is inside/outside
638 it_type prev = it - 1;
92f5a8d4 639 return check_segment<UseSideOfIntersection>::apply(*prev, *it, turn, true, side_strategy);
7c673cae
FG
640 }
641 return analyse_continue;
642 }
643
644public :
92f5a8d4
TL
645 template
646 <
647 typename Turn,
648 typename Piece,
649 typename DistanceStrategy,
650 typename SideStrategy
651 >
652 static inline analyse_result apply(Turn const& turn, Piece const& piece,
653 DistanceStrategy const& distance_strategy,
654 SideStrategy const& side_strategy)
7c673cae
FG
655 {
656 typedef typename Turn::robust_point_type point_type;
92f5a8d4 657 analyse_result code = check_helper_segments(turn, piece, distance_strategy, side_strategy);
7c673cae
FG
658 if (code != analyse_continue)
659 {
660 return code;
661 }
662
663 geometry::equal_to<point_type> comparator;
664
665 if (piece.offsetted_count > 8)
666 {
667 // If the offset contains some points and is monotonic, we try
668 // to avoid walking all points linearly.
669 // We try it only once.
670 if (piece.is_monotonic_increasing[0])
671 {
92f5a8d4 672 code = check_monotonic(turn, piece, geometry::less<point_type, 0>(), side_strategy);
7c673cae
FG
673 if (code != analyse_continue) return code;
674 }
675 else if (piece.is_monotonic_increasing[1])
676 {
92f5a8d4 677 code = check_monotonic(turn, piece, geometry::less<point_type, 1>(), side_strategy);
7c673cae
FG
678 if (code != analyse_continue) return code;
679 }
680 else if (piece.is_monotonic_decreasing[0])
681 {
92f5a8d4 682 code = check_monotonic(turn, piece, geometry::greater<point_type, 0>(), side_strategy);
7c673cae
FG
683 if (code != analyse_continue) return code;
684 }
685 else if (piece.is_monotonic_decreasing[1])
686 {
92f5a8d4 687 code = check_monotonic(turn, piece, geometry::greater<point_type, 1>(), side_strategy);
7c673cae
FG
688 if (code != analyse_continue) return code;
689 }
690 }
691
692 // It is small or not monotonic, walk linearly through offset
693 // TODO: this will be combined with winding strategy
694
695 for (signed_size_type i = 1; i < piece.offsetted_count; i++)
696 {
697 point_type const& previous = piece.robust_ring[i - 1];
698 point_type const& current = piece.robust_ring[i];
699
700 // The robust ring can contain duplicates
701 // (on which any side or side-value would return 0)
702 if (! comparator(previous, current))
703 {
92f5a8d4 704 code = check_segment<UseSideOfIntersection>::apply(previous, current, turn, false, side_strategy);
7c673cae
FG
705 if (code != analyse_continue)
706 {
707 return code;
708 }
709 }
710 }
711
712 return analyse_unknown;
713 }
714
715};
716
92f5a8d4
TL
717// Helper Structure, of which the apply method returns a side value in {-1, 0, 1}
718template <bool UseSideOfIntersection>
719struct turn_in_piece {};
7c673cae 720
92f5a8d4
TL
721template <>
722struct turn_in_piece<true>
7c673cae 723{
7c673cae 724
92f5a8d4 725private :
7c673cae 726 template <typename Turn, typename Piece>
92f5a8d4 727 static inline int in_convex_piece(Turn const& turn, Piece const& piece)
7c673cae
FG
728 {
729 typedef typename Turn::robust_point_type point_type;
730 typedef typename Piece::piece_robust_ring_type ring_type;
731 typedef geometry::model::referring_segment<point_type const> segment;
732
733 segment const p(turn.rob_pi, turn.rob_pj);
734 segment const q(turn.rob_qi, turn.rob_qj);
735
736 typedef typename boost::range_iterator<ring_type const>::type iterator_type;
737 iterator_type it = boost::begin(piece.robust_ring);
738 iterator_type end = boost::end(piece.robust_ring);
739
740 // A robust ring is always closed, and always clockwise
741 for (iterator_type previous = it++; it != end; ++previous, ++it)
742 {
743 geometry::equal_to<point_type> comparator;
744 if (comparator(*previous, *it))
745 {
746 // Points are the same
747 continue;
748 }
749
750 segment r(*previous, *it);
751
752 int const side = strategy::side::side_of_intersection::apply(p, q, r,
753 turn.robust_point);
754
755 if (side == 1)
756 {
757 // IP is left of segment, so it is outside
758 return -1; // outside
759 }
760 else if (side == 0)
761 {
762 // IP is collinear with segment. TODO: we should analyze this further
763 // For now we use the fallback point
764 if (in_box(*previous, *it, turn.robust_point))
765 {
766 return 0;
767 }
768 else
769 {
770 return -1; // outside
771 }
772 }
773 }
774 return 1; // inside
775 }
92f5a8d4
TL
776
777public :
778
779 template <typename Turn, typename Piece, typename Strategy>
780 static inline int apply(Turn const& turn, Piece const& piece,
781 Strategy const& strategy)
782 {
783 if (piece.is_convex)
784 {
785 return in_convex_piece(turn, piece);
786 }
787 else
788 {
789 // side-of-intersection only supported for convex pieces
790 // Call point_in_geometry, a performance-bottleneck
791 // TODO: might be replaced by extending analysing piece
792 return detail::within::point_in_geometry(turn.robust_point,
793 piece.robust_ring, strategy);
794 }
795 }
796};
797
798template <>
799struct turn_in_piece<false>
800{
801public :
802
803 template <typename Turn, typename Piece, typename Strategy>
804 static inline int apply(Turn const& turn, Piece const& piece,
805 Strategy const& strategy)
806 {
807 return detail::within::point_in_geometry(turn.robust_point,
808 piece.robust_ring, strategy);
809 }
810};
811
812template
813<
814 typename CsTag,
815 typename Turns,
816 typename Pieces,
817 typename DistanceStrategy,
818 typename PointInGeometryStrategy,
819 typename SideStrategy
820
821>
822class turn_in_piece_visitor
823{
824 Turns& m_turns; // because partition is currently operating on const input only
825 Pieces const& m_pieces; // to check for piece-type
826 DistanceStrategy const& m_distance_strategy; // to check if point is on original
827 PointInGeometryStrategy const& m_point_in_geometry_strategy;
828 SideStrategy const& m_side_strategy;
829
830 template <typename Operation, typename Piece>
831 inline bool skip(Operation const& op, Piece const& piece) const
832 {
833 if (op.piece_index == piece.index)
834 {
835 return true;
836 }
837 Piece const& pc = m_pieces[op.piece_index];
838 if (pc.left_index == piece.index || pc.right_index == piece.index)
839 {
840 if (pc.type == strategy::buffer::buffered_flat_end)
841 {
842 // If it is a flat end, don't compare against its neighbor:
843 // it will always be located on one of the helper segments
844 return true;
845 }
846 if (pc.type == strategy::buffer::buffered_concave)
847 {
848 // If it is concave, the same applies: the IP will be
849 // located on one of the helper segments
850 return true;
851 }
852 }
853
854 return false;
855 }
7c673cae
FG
856
857
858public:
859
92f5a8d4
TL
860 inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces,
861 DistanceStrategy const& distance_strategy,
862 PointInGeometryStrategy const& strategy,
863 SideStrategy const& side_strategy)
7c673cae
FG
864 : m_turns(turns)
865 , m_pieces(pieces)
92f5a8d4
TL
866 , m_distance_strategy(distance_strategy)
867 , m_point_in_geometry_strategy(strategy)
868 , m_side_strategy(side_strategy)
7c673cae
FG
869 {}
870
871 template <typename Turn, typename Piece>
b32b8144 872 inline bool apply(Turn const& turn, Piece const& piece, bool first = true)
7c673cae 873 {
92f5a8d4 874 boost::ignore_unused(first);
7c673cae
FG
875
876 if (turn.count_within > 0)
877 {
878 // Already inside - no need to check again
b32b8144 879 return true;
7c673cae
FG
880 }
881
882 if (piece.type == strategy::buffer::buffered_flat_end
883 || piece.type == strategy::buffer::buffered_concave)
884 {
885 // Turns cannot be located within flat-end or concave pieces
b32b8144 886 return true;
7c673cae
FG
887 }
888
889 if (! geometry::covered_by(turn.robust_point, piece.robust_envelope))
890 {
891 // Easy check: if the turn is not in the envelope, we can safely return
b32b8144 892 return true;
7c673cae
FG
893 }
894
895 if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece))
896 {
b32b8144 897 return true;
7c673cae
FG
898 }
899
900 // TODO: mutable_piece to make some on-demand preparations in analyse
901 Turn& mutable_turn = m_turns[turn.turn_index];
902
903 if (piece.type == geometry::strategy::buffer::buffered_point)
904 {
905 // Optimization for buffer around points: if distance from center
906 // is not between min/max radius, the result is clear
907 typedef typename default_comparable_distance_result
908 <
909 typename Turn::robust_point_type
910 >::type distance_type;
911
912 distance_type const cd
913 = geometry::comparable_distance(piece.robust_center,
914 turn.robust_point);
915
916 if (cd < piece.robust_min_comparable_radius)
917 {
918 mutable_turn.count_within++;
b32b8144 919 return true;
7c673cae
FG
920 }
921 if (cd > piece.robust_max_comparable_radius)
922 {
b32b8144 923 return true;
7c673cae
FG
924 }
925 }
926
92f5a8d4
TL
927 static const bool use_soi = use_side_of_intersection<CsTag>::value;
928 boost::ignore_unused(use_soi);
929
930 analyse_result const analyse_code =
7c673cae 931 piece.type == geometry::strategy::buffer::buffered_point
92f5a8d4
TL
932 ? analyse_turn_wrt_point_piece<use_soi>::apply(turn, piece, m_point_in_geometry_strategy, m_side_strategy)
933 : analyse_turn_wrt_piece<use_soi>::apply(turn, piece, m_distance_strategy, m_side_strategy);
7c673cae
FG
934
935 switch(analyse_code)
936 {
937 case analyse_disjoint :
b32b8144 938 return true;
7c673cae
FG
939 case analyse_on_offsetted :
940 mutable_turn.count_on_offsetted++; // value is not used anymore
b32b8144 941 return true;
7c673cae
FG
942 case analyse_on_original_boundary :
943 mutable_turn.count_on_original_boundary++;
b32b8144 944 return true;
7c673cae
FG
945 case analyse_within :
946 mutable_turn.count_within++;
b32b8144 947 return true;
7c673cae
FG
948 case analyse_near_offsetted :
949 mutable_turn.count_within_near_offsetted++;
b32b8144 950 return true;
7c673cae
FG
951 default :
952 break;
953 }
954
92f5a8d4
TL
955 int const geometry_code = turn_in_piece<use_soi>::apply(turn, piece,
956 m_point_in_geometry_strategy);
7c673cae
FG
957
958 if (geometry_code == 1)
959 {
960 mutable_turn.count_within++;
961 }
b32b8144
FG
962
963 return true;
7c673cae
FG
964 }
965};
966
967
968}} // namespace detail::buffer
969#endif // DOXYGEN_NO_DETAIL
970
971
972}} // namespace boost::geometry
973
974#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR