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