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