1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2
3 // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
4
5 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
8
9 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
10 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
11
12
13 #include <boost/core/ignore_unused.hpp>
14
15 #include <boost/range.hpp>
16
17 #include <boost/geometry/core/assert.hpp>
18
19 #include <boost/geometry/arithmetic/dot_product.hpp>
20 #include <boost/geometry/algorithms/assign.hpp>
21 #include <boost/geometry/algorithms/comparable_distance.hpp>
22 #include <boost/geometry/algorithms/equals.hpp>
23 #include <boost/geometry/algorithms/expand.hpp>
24 #include <boost/geometry/algorithms/detail/disjoint/point_box.hpp>
25 #include <boost/geometry/algorithms/detail/disjoint/box_box.hpp>
26 #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
27 #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
28 #include <boost/geometry/policies/compare.hpp>
29 #include <boost/geometry/strategies/buffer.hpp>
30 #include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp>
31
32 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
33 #include <boost/geometry/strategies/cartesian/side_of_intersection.hpp>
34 #endif
35
36
37 namespace boost { namespace geometry
38 {
39
40
41 #ifndef DOXYGEN_NO_DETAIL
42 namespace detail { namespace buffer
43 {
44
45 struct piece_get_box
46 {
47 template <typename Box, typename Piece>
applyboost::geometry::detail::buffer::piece_get_box48 static inline void apply(Box& total, Piece const& piece)
49 {
50 geometry::expand(total, piece.robust_envelope);
51 }
52 };
53
54 struct piece_ovelaps_box
55 {
56 template <typename Box, typename Piece>
applyboost::geometry::detail::buffer::piece_ovelaps_box57 static inline bool apply(Box const& box, Piece const& piece)
58 {
59 if (piece.type == strategy::buffer::buffered_flat_end
60 || piece.type == strategy::buffer::buffered_concave)
61 {
62 // Turns cannot be inside a flat end (though they can be on border)
63 // Neither we need to check if they are inside concave helper pieces
64
65 // Skip all pieces not used as soon as possible
66 return false;
67 }
68
69 return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope);
70 }
71 };
72
73 struct turn_get_box
74 {
75 template <typename Box, typename Turn>
applyboost::geometry::detail::buffer::turn_get_box76 static inline void apply(Box& total, Turn const& turn)
77 {
78 geometry::expand(total, turn.robust_point);
79 }
80 };
81
82 struct turn_ovelaps_box
83 {
84 template <typename Box, typename Turn>
applyboost::geometry::detail::buffer::turn_ovelaps_box85 static inline bool apply(Box const& box, Turn const& turn)
86 {
87 return ! geometry::detail::disjoint::disjoint_point_box(turn.robust_point, box);
88 }
89 };
90
91
92 enum analyse_result
93 {
94 analyse_unknown,
95 analyse_continue,
96 analyse_disjoint,
97 analyse_within,
98 analyse_on_original_boundary,
99 analyse_on_offsetted
100 #if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
101 , analyse_near_offsetted
102 #endif
103 };
104
105 template <typename Point>
in_box(Point const & previous,Point const & current,Point const & point)106 inline bool in_box(Point const& previous,
107 Point const& current, Point const& point)
108 {
109 // Get its box (TODO: this can be prepared-on-demand later)
110 typedef geometry::model::box<Point> box_type;
111 box_type box;
112 geometry::assign_inverse(box);
113 geometry::expand(box, previous);
114 geometry::expand(box, current);
115
116 return geometry::covered_by(point, box);
117 }
118
119 template <typename Point, typename Turn>
check_segment(Point const & previous,Point const & current,Turn const & turn,bool from_monotonic)120 inline analyse_result check_segment(Point const& previous,
121 Point const& current, Turn const& turn,
122 bool from_monotonic)
123 {
124
125 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
126 typedef geometry::model::referring_segment<Point const> segment_type;
127 segment_type const p(turn.rob_pi, turn.rob_pj);
128 segment_type const q(turn.rob_qi, turn.rob_qj);
129 segment_type const r(previous, current);
130 int const side = strategy::side::side_of_intersection::apply(p, q, r,
131 turn.robust_point);
132
133 if (side == 0)
134 {
135 return analyse_on_offsetted;
136 }
137 if (side == -1 && from_monotonic)
138 {
139 return analyse_within;
140 }
141 if (side == 1 && from_monotonic)
142 {
143 return analyse_disjoint;
144 }
145 return analyse_continue;
146
147 #else
148
149 typedef typename strategy::side::services::default_strategy
150 <
151 typename cs_tag<Point>::type
152 >::type side_strategy;
153 typedef typename geometry::coordinate_type<Point>::type coordinate_type;
154
155 coordinate_type const twice_area
156 = side_strategy::template side_value
157 <
158 coordinate_type,
159 coordinate_type
160 >(previous, current, turn.robust_point);
161
162 if (twice_area == 0)
163 {
164 // Collinear, only on segment if it is covered by its bbox
165 if (in_box(previous, current, turn.robust_point))
166 {
167 return analyse_on_offsetted;
168 }
169 }
170 else if (twice_area < 0)
171 {
172 // It is in the triangle right-of the segment where the
173 // segment is the hypothenusa. Check if it is close
174 // (within rounding-area)
175 if (twice_area * twice_area < geometry::comparable_distance(previous, current)
176 && in_box(previous, current, turn.robust_point))
177 {
178 return analyse_near_offsetted;
179 }
180 else if (from_monotonic)
181 {
182 return analyse_within;
183 }
184 }
185 else if (twice_area > 0 && from_monotonic)
186 {
187 // Left of segment
188 return analyse_disjoint;
189 }
190
191 // Not monotonic, on left or right side: continue analysing
192 return analyse_continue;
193 #endif
194 }
195
196
197 class analyse_turn_wrt_point_piece
198 {
199 public :
200 template <typename Turn, typename Piece>
apply(Turn const & turn,Piece const & piece)201 static inline analyse_result apply(Turn const& turn, Piece const& piece)
202 {
203 typedef typename Piece::section_type section_type;
204 typedef typename Turn::robust_point_type point_type;
205 typedef typename geometry::coordinate_type<point_type>::type coordinate_type;
206
207 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
208 typedef geometry::model::referring_segment<point_type const> segment_type;
209 segment_type const p(turn.rob_pi, turn.rob_pj);
210 segment_type const q(turn.rob_qi, turn.rob_qj);
211 #else
212 typedef strategy::within::winding<point_type> strategy_type;
213
214 typename strategy_type::state_type state;
215 strategy_type strategy;
216 boost::ignore_unused(strategy);
217 #endif
218
219 BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
220
221 coordinate_type const point_y = geometry::get<1>(turn.robust_point);
222
223 for (std::size_t s = 0; s < piece.sections.size(); s++)
224 {
225 section_type const& section = piece.sections[s];
226 // If point within vertical range of monotonic section:
227 if (! section.duplicate
228 && section.begin_index < section.end_index
229 && point_y >= geometry::get<min_corner, 1>(section.bounding_box) - 1
230 && point_y <= geometry::get<max_corner, 1>(section.bounding_box) + 1)
231 {
232 for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
233 {
234 point_type const& previous = piece.robust_ring[i - 1];
235 point_type const& current = piece.robust_ring[i];
236
237 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
238
239 // First check if it is in range - if it is not, the
240 // expensive side_of_intersection does not need to be
241 // applied
242 coordinate_type y1 = geometry::get<1>(previous);
243 coordinate_type y2 = geometry::get<1>(current);
244
245 if (y1 > y2)
246 {
247 std::swap(y1, y2);
248 }
249
250 if (point_y >= y1 - 1 && point_y <= y2 + 1)
251 {
252 segment_type const r(previous, current);
253 int const side = strategy::side::side_of_intersection::apply(p, q, r,
254 turn.robust_point);
255
256 // Sections are monotonic in y-dimension
257 if (side == 1)
258 {
259 // Left on segment
260 return analyse_disjoint;
261 }
262 else if (side == 0)
263 {
264 // Collinear - TODO: check if really on segment
265 return analyse_on_offsetted;
266 }
267 }
268 #else
269 analyse_result code = check_segment(previous, current, turn, false);
270 if (code != analyse_continue)
271 {
272 return code;
273 }
274
275 // Get the state (to determine it is within), we don't have
276 // to cover the on-segment case (covered above)
277 strategy.apply(turn.robust_point, previous, current, state);
278 #endif
279 }
280 }
281 }
282
283 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
284 // It is nowhere outside, and not on segment, so it is within
285 return analyse_within;
286 #else
287 int const code = strategy.result(state);
288 if (code == 1)
289 {
290 return analyse_within;
291 }
292 else if (code == -1)
293 {
294 return analyse_disjoint;
295 }
296
297 // Should normally not occur - on-segment is covered
298 return analyse_unknown;
299 #endif
300 }
301
302 };
303
304 class analyse_turn_wrt_piece
305 {
306 template <typename Point, typename Turn>
check_helper_segment(Point const & s1,Point const & s2,Turn const & turn,bool is_original,Point const & offsetted)307 static inline analyse_result check_helper_segment(Point const& s1,
308 Point const& s2, Turn const& turn,
309 bool is_original,
310 Point const& offsetted)
311 {
312 boost::ignore_unused(offsetted);
313 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
314 typedef geometry::model::referring_segment<Point const> segment_type;
315 segment_type const p(turn.rob_pi, turn.rob_pj);
316 segment_type const q(turn.rob_qi, turn.rob_qj);
317 segment_type const r(s1, s2);
318 int const side = strategy::side::side_of_intersection::apply(p, q, r,
319 turn.robust_point);
320
321 if (side == 1)
322 {
323 // left of segment
324 return analyse_disjoint;
325 }
326 else if (side == 0)
327 {
328 // If is collinear, either on segment or before/after
329 typedef geometry::model::box<Point> box_type;
330
331 box_type box;
332 geometry::assign_inverse(box);
333 geometry::expand(box, s1);
334 geometry::expand(box, s2);
335
336 if (geometry::covered_by(turn.robust_point, box))
337 {
338 // Points on helper-segments are considered as within
339 // Points on original boundary are processed differently
340 return is_original
341 ? analyse_on_original_boundary
342 : analyse_within;
343 }
344
345 // It is collinear but not on the segment. Because these
346 // segments are convex, it is outside
347 // Unless the offsetted ring is collinear or concave w.r.t.
348 // helper-segment but that scenario is not yet supported
349 return analyse_disjoint;
350 }
351
352 // right of segment
353 return analyse_continue;
354 #else
355 typedef typename strategy::side::services::default_strategy
356 <
357 typename cs_tag<Point>::type
358 >::type side_strategy;
359
360 switch(side_strategy::apply(s1, s2, turn.robust_point))
361 {
362 case 1 :
363 return analyse_disjoint; // left of segment
364 case 0 :
365 {
366 // If is collinear, either on segment or before/after
367 typedef geometry::model::box<Point> box_type;
368
369 box_type box;
370 geometry::assign_inverse(box);
371 geometry::expand(box, s1);
372 geometry::expand(box, s2);
373
374 if (geometry::covered_by(turn.robust_point, box))
375 {
376 // It is on the segment
377 if (! is_original
378 && geometry::comparable_distance(turn.robust_point, offsetted) <= 1)
379 {
380 // It is close to the offsetted-boundary, take
381 // any rounding-issues into account
382 return analyse_near_offsetted;
383 }
384
385 // Points on helper-segments are considered as within
386 // Points on original boundary are processed differently
387 return is_original
388 ? analyse_on_original_boundary
389 : analyse_within;
390 }
391
392 // It is collinear but not on the segment. Because these
393 // segments are convex, it is outside
394 // Unless the offsetted ring is collinear or concave w.r.t.
395 // helper-segment but that scenario is not yet supported
396 return analyse_disjoint;
397 }
398 break;
399 }
400
401 // right of segment
402 return analyse_continue;
403 #endif
404 }
405
406 template <typename Turn, typename Piece>
check_helper_segments(Turn const & turn,Piece const & piece)407 static inline analyse_result check_helper_segments(Turn const& turn, Piece const& piece)
408 {
409 typedef typename Turn::robust_point_type point_type;
410 geometry::equal_to<point_type> comparator;
411
412 point_type points[4];
413
414 signed_size_type helper_count = static_cast<signed_size_type>(piece.robust_ring.size())
415 - piece.offsetted_count;
416 if (helper_count == 4)
417 {
418 for (int i = 0; i < 4; i++)
419 {
420 points[i] = piece.robust_ring[piece.offsetted_count + i];
421 }
422 }
423 else if (helper_count == 3)
424 {
425 // Triangular piece, assign points but assign second twice
426 for (int i = 0; i < 4; i++)
427 {
428 int index = i < 2 ? i : i - 1;
429 points[i] = piece.robust_ring[piece.offsetted_count + index];
430 }
431 }
432 else
433 {
434 // Some pieces (e.g. around points) do not have helper segments.
435 // Others should have 3 (join) or 4 (side)
436 return analyse_continue;
437 }
438
439 // First check point-equality
440 point_type const& point = turn.robust_point;
441 if (comparator(point, points[0]) || comparator(point, points[3]))
442 {
443 return analyse_on_offsetted;
444 }
445 if (comparator(point, points[1]) || comparator(point, points[2]))
446 {
447 return analyse_on_original_boundary;
448 }
449
450 // Right side of the piece
451 analyse_result result
452 = check_helper_segment(points[0], points[1], turn,
453 false, points[0]);
454 if (result != analyse_continue)
455 {
456 return result;
457 }
458
459 // Left side of the piece
460 result = check_helper_segment(points[2], points[3], turn,
461 false, points[3]);
462 if (result != analyse_continue)
463 {
464 return result;
465 }
466
467 if (! comparator(points[1], points[2]))
468 {
469 // Side of the piece at side of original geometry
470 result = check_helper_segment(points[1], points[2], turn,
471 true, point);
472 if (result != analyse_continue)
473 {
474 return result;
475 }
476 }
477
478 // We are within the \/ or |_| shaped piece, where the top is the
479 // offsetted ring.
480 if (! geometry::covered_by(point, piece.robust_offsetted_envelope))
481 {
482 // Not in offsetted-area. This makes a cheap check possible
483 typedef typename strategy::side::services::default_strategy
484 <
485 typename cs_tag<point_type>::type
486 >::type side_strategy;
487
488 switch(side_strategy::apply(points[3], points[0], point))
489 {
490 case 1 : return analyse_disjoint;
491 case -1 : return analyse_within;
492 case 0 : return analyse_disjoint;
493 }
494 }
495
496 return analyse_continue;
497 }
498
499 template <typename Turn, typename Piece, typename Compare>
check_monotonic(Turn const & turn,Piece const & piece,Compare const & compare)500 static inline analyse_result check_monotonic(Turn const& turn, Piece const& piece, Compare const& compare)
501 {
502 typedef typename Piece::piece_robust_ring_type ring_type;
503 typedef typename ring_type::const_iterator it_type;
504 it_type end = piece.robust_ring.begin() + piece.offsetted_count;
505 it_type it = std::lower_bound(piece.robust_ring.begin(),
506 end,
507 turn.robust_point,
508 compare);
509
510 if (it != end
511 && it != piece.robust_ring.begin())
512 {
513 // iterator points to point larger than point
514 // w.r.t. specified direction, and prev points to a point smaller
515 // We now know if it is inside/outside
516 it_type prev = it - 1;
517 return check_segment(*prev, *it, turn, true);
518 }
519 return analyse_continue;
520 }
521
522 public :
523 template <typename Turn, typename Piece>
apply(Turn const & turn,Piece const & piece)524 static inline analyse_result apply(Turn const& turn, Piece const& piece)
525 {
526 typedef typename Turn::robust_point_type point_type;
527 analyse_result code = check_helper_segments(turn, piece);
528 if (code != analyse_continue)
529 {
530 return code;
531 }
532
533 geometry::equal_to<point_type> comparator;
534
535 if (piece.offsetted_count > 8)
536 {
537 // If the offset contains some points and is monotonic, we try
538 // to avoid walking all points linearly.
539 // We try it only once.
540 if (piece.is_monotonic_increasing[0])
541 {
542 code = check_monotonic(turn, piece, geometry::less<point_type, 0>());
543 if (code != analyse_continue) return code;
544 }
545 else if (piece.is_monotonic_increasing[1])
546 {
547 code = check_monotonic(turn, piece, geometry::less<point_type, 1>());
548 if (code != analyse_continue) return code;
549 }
550 else if (piece.is_monotonic_decreasing[0])
551 {
552 code = check_monotonic(turn, piece, geometry::greater<point_type, 0>());
553 if (code != analyse_continue) return code;
554 }
555 else if (piece.is_monotonic_decreasing[1])
556 {
557 code = check_monotonic(turn, piece, geometry::greater<point_type, 1>());
558 if (code != analyse_continue) return code;
559 }
560 }
561
562 // It is small or not monotonic, walk linearly through offset
563 // TODO: this will be combined with winding strategy
564
565 for (signed_size_type i = 1; i < piece.offsetted_count; i++)
566 {
567 point_type const& previous = piece.robust_ring[i - 1];
568 point_type const& current = piece.robust_ring[i];
569
570 // The robust ring can contain duplicates
571 // (on which any side or side-value would return 0)
572 if (! comparator(previous, current))
573 {
574 code = check_segment(previous, current, turn, false);
575 if (code != analyse_continue)
576 {
577 return code;
578 }
579 }
580 }
581
582 return analyse_unknown;
583 }
584
585 };
586
587
588 template <typename Turns, typename Pieces>
589 class turn_in_piece_visitor
590 {
591 Turns& m_turns; // because partition is currently operating on const input only
592 Pieces const& m_pieces; // to check for piece-type
593
594 template <typename Operation, typename Piece>
skip(Operation const & op,Piece const & piece) const595 inline bool skip(Operation const& op, Piece const& piece) const
596 {
597 if (op.piece_index == piece.index)
598 {
599 return true;
600 }
601 Piece const& pc = m_pieces[op.piece_index];
602 if (pc.left_index == piece.index || pc.right_index == piece.index)
603 {
604 if (pc.type == strategy::buffer::buffered_flat_end)
605 {
606 // If it is a flat end, don't compare against its neighbor:
607 // it will always be located on one of the helper segments
608 return true;
609 }
610 if (pc.type == strategy::buffer::buffered_concave)
611 {
612 // If it is concave, the same applies: the IP will be
613 // located on one of the helper segments
614 return true;
615 }
616 }
617
618 return false;
619 }
620
621 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
622 // NOTE: this function returns a side value in {-1, 0, 1}
623 template <typename Turn, typename Piece>
turn_in_convex_piece(Turn const & turn,Piece const & piece)624 static inline int turn_in_convex_piece(Turn const& turn,
625 Piece const& piece)
626 {
627 typedef typename Turn::robust_point_type point_type;
628 typedef typename Piece::piece_robust_ring_type ring_type;
629 typedef geometry::model::referring_segment<point_type const> segment;
630
631 segment const p(turn.rob_pi, turn.rob_pj);
632 segment const q(turn.rob_qi, turn.rob_qj);
633
634 typedef typename boost::range_iterator<ring_type const>::type iterator_type;
635 iterator_type it = boost::begin(piece.robust_ring);
636 iterator_type end = boost::end(piece.robust_ring);
637
638 // A robust ring is always closed, and always clockwise
639 for (iterator_type previous = it++; it != end; ++previous, ++it)
640 {
641 geometry::equal_to<point_type> comparator;
642 if (comparator(*previous, *it))
643 {
644 // Points are the same
645 continue;
646 }
647
648 segment r(*previous, *it);
649
650 int const side = strategy::side::side_of_intersection::apply(p, q, r,
651 turn.robust_point);
652
653 if (side == 1)
654 {
655 // IP is left of segment, so it is outside
656 return -1; // outside
657 }
658 else if (side == 0)
659 {
660 // IP is collinear with segment. TODO: we should analyze this further
661 // For now we use the fallback point
662 if (in_box(*previous, *it, turn.robust_point))
663 {
664 return 0;
665 }
666 else
667 {
668 return -1; // outside
669 }
670 }
671 }
672 return 1; // inside
673 }
674 #endif
675
676
677 public:
678
turn_in_piece_visitor(Turns & turns,Pieces const & pieces)679 inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces)
680 : m_turns(turns)
681 , m_pieces(pieces)
682 {}
683
684 template <typename Turn, typename Piece>
apply(Turn const & turn,Piece const & piece,bool first=true)685 inline void apply(Turn const& turn, Piece const& piece, bool first = true)
686 {
687 boost::ignore_unused_variable_warning(first);
688
689 if (turn.count_within > 0)
690 {
691 // Already inside - no need to check again
692 return;
693 }
694
695 if (piece.type == strategy::buffer::buffered_flat_end
696 || piece.type == strategy::buffer::buffered_concave)
697 {
698 // Turns cannot be located within flat-end or concave pieces
699 return;
700 }
701
702 if (! geometry::covered_by(turn.robust_point, piece.robust_envelope))
703 {
704 // Easy check: if the turn is not in the envelope, we can safely return
705 return;
706 }
707
708 if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece))
709 {
710 return;
711 }
712
713 // TODO: mutable_piece to make some on-demand preparations in analyse
714 Turn& mutable_turn = m_turns[turn.turn_index];
715
716 if (piece.type == geometry::strategy::buffer::buffered_point)
717 {
718 // Optimization for buffer around points: if distance from center
719 // is not between min/max radius, the result is clear
720 typedef typename default_comparable_distance_result
721 <
722 typename Turn::robust_point_type
723 >::type distance_type;
724
725 distance_type const cd
726 = geometry::comparable_distance(piece.robust_center,
727 turn.robust_point);
728
729 if (cd < piece.robust_min_comparable_radius)
730 {
731 mutable_turn.count_within++;
732 return;
733 }
734 if (cd > piece.robust_max_comparable_radius)
735 {
736 return;
737 }
738 }
739
740 analyse_result analyse_code =
741 piece.type == geometry::strategy::buffer::buffered_point
742 ? analyse_turn_wrt_point_piece::apply(turn, piece)
743 : analyse_turn_wrt_piece::apply(turn, piece);
744
745 switch(analyse_code)
746 {
747 case analyse_disjoint :
748 return;
749 case analyse_on_offsetted :
750 mutable_turn.count_on_offsetted++; // value is not used anymore
751 return;
752 case analyse_on_original_boundary :
753 mutable_turn.count_on_original_boundary++;
754 return;
755 case analyse_within :
756 mutable_turn.count_within++;
757 return;
758 #if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
759 case analyse_near_offsetted :
760 mutable_turn.count_within_near_offsetted++;
761 return;
762 #endif
763 default :
764 break;
765 }
766
767 #if defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
768 // We don't know (yet)
769 int geometry_code = 0;
770 if (piece.is_convex)
771 {
772 geometry_code = turn_in_convex_piece(turn, piece);
773 }
774 else
775 {
776
777 // TODO: this point_in_geometry is a performance-bottleneck here and
778 // will be replaced completely by extending analyse_piece functionality
779 geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring);
780 }
781 #else
782 int geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring);
783 #endif
784
785 if (geometry_code == 1)
786 {
787 mutable_turn.count_within++;
788 }
789 }
790 };
791
792
793 }} // namespace detail::buffer
794 #endif // DOXYGEN_NO_DETAIL
795
796
797 }} // namespace boost::geometry
798
799 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
800