1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
6 // This file was modified by Oracle on 2016, 2018.
7 // Modifications copyright (c) 2016-2018 Oracle and/or its affiliates.
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
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)
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR
18 #include <boost/core/ignore_unused.hpp>
20 #include <boost/range.hpp>
22 #include <boost/geometry/core/assert.hpp>
23 #include <boost/geometry/core/config.hpp>
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>
38 #include <boost/geometry/strategies/cartesian/side_of_intersection.hpp>
41 namespace boost { namespace geometry
45 #ifndef DOXYGEN_NO_DETAIL
46 namespace detail { namespace buffer
51 template <typename Box, typename Piece>
52 static inline void apply(Box& total, Piece const& piece)
54 geometry::expand(total, piece.robust_envelope);
58 template <typename DisjointBoxBoxStrategy>
59 struct piece_ovelaps_box
61 template <typename Box, typename Piece>
62 static inline bool apply(Box const& box, Piece const& piece)
64 if (piece.type == strategy::buffer::buffered_flat_end
65 || piece.type == strategy::buffer::buffered_concave)
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
70 // Skip all pieces not used as soon as possible
74 return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope,
75 DisjointBoxBoxStrategy());
81 template <typename Box, typename Turn>
82 static inline void apply(Box& total, Turn const& turn)
84 geometry::expand(total, turn.robust_point);
88 template <typename DisjointPointBoxStrategy>
89 struct turn_ovelaps_box
91 template <typename Box, typename Turn>
92 static inline bool apply(Box const& box, Turn const& turn)
94 return ! geometry::detail::disjoint::disjoint_point_box(turn.robust_point, box,
95 DisjointPointBoxStrategy());
106 analyse_on_original_boundary,
107 analyse_on_offsetted,
108 analyse_near_offsetted
111 template <typename Point>
112 inline bool in_box(Point const& previous,
113 Point const& current, Point const& point)
115 // Get its box (TODO: this can be prepared-on-demand later)
116 typedef geometry::model::box<Point> box_type;
118 geometry::assign_inverse(box);
119 geometry::expand(box, previous);
120 geometry::expand(box, current);
122 return geometry::covered_by(point, box);
125 template <typename NumericType>
126 inline bool is_one_sided(NumericType const& left, NumericType const& right)
128 static NumericType const zero = 0;
129 return geometry::math::equals(left, zero)
130 || geometry::math::equals(right, zero);
133 template <typename Point, typename DistanceStrategy>
134 inline bool has_zero_distance_at(Point const& point,
135 DistanceStrategy const& distance_strategy)
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));
143 // meta-programming-structure defining if to use side-of-intersection
144 // (only for cartesian / only necessary with rescaling)
145 template <typename Tag>
146 struct use_side_of_intersection {};
148 #if defined(BOOST_GEOMETRY_USE_RESCALING)
149 // With rescaling, let Cartesian use side-of-intersection
151 struct use_side_of_intersection<cartesian_tag> { static bool const value = true; };
154 struct use_side_of_intersection<cartesian_tag> { static bool const value = false; };
158 struct use_side_of_intersection<spherical_tag> { static bool const value = false; };
161 struct use_side_of_intersection<geographic_tag> { static bool const value = false; };
164 template <bool UseSideOfIntersection>
165 struct check_segment {};
167 // Implementation using side-of-intersection
169 struct check_segment<true>
171 template <typename Point, typename Turn, typename SideStrategy>
172 static inline analyse_result apply(Point const& previous,
173 Point const& current, Turn const& turn,
175 SideStrategy const& )
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,
186 return analyse_on_offsetted;
188 if (side == -1 && from_monotonic)
190 return analyse_within;
192 if (side == 1 && from_monotonic)
194 return analyse_disjoint;
196 return analyse_continue;
201 struct check_segment<false>
203 template <typename Point, typename Turn, typename SideStrategy>
204 static inline analyse_result apply(Point const& previous,
205 Point const& current, Turn const& turn,
207 SideStrategy const& side_strategy)
209 int const side = side_strategy.apply(previous, current, turn.robust_point);
213 // Collinear, only on segment if it is covered by its bbox
214 if (in_box(previous, current, turn.robust_point))
216 return analyse_on_offsetted;
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))
226 return analyse_near_offsetted;
228 else if (from_monotonic)
230 return analyse_within;
233 else if (from_monotonic)
236 return analyse_disjoint;
239 // Not monotonic, on left or right side: continue analysing
240 return analyse_continue;
244 template <bool UseSideOfIntersection>
245 class analyse_turn_wrt_point_piece {};
248 class analyse_turn_wrt_point_piece<true>
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& )
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;
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);
264 BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
266 coordinate_type const point_x = geometry::get<0>(turn.robust_point);
268 for (std::size_t s = 0; s < piece.sections.size(); s++)
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)
277 for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
279 point_type const& previous = piece.robust_ring[i - 1];
280 point_type const& current = piece.robust_ring[i];
283 // First check if it is in range - if it is not, the
284 // expensive side_of_intersection does not need to be
286 coordinate_type x1 = geometry::get<0>(previous);
287 coordinate_type x2 = geometry::get<0>(current);
294 if (point_x >= x1 - 1 && point_x <= x2 + 1)
296 segment_type const r(previous, current);
297 int const side = strategy::side::side_of_intersection::apply(p, q, r,
300 // Sections are monotonic in x-dimension
304 return analyse_disjoint;
308 // Collinear - TODO: check if really on segment
309 return analyse_on_offsetted;
316 // It is nowhere outside, and not on segment, so it is within
317 return analyse_within;
323 class analyse_turn_wrt_point_piece<false>
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)
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;
335 typename PointInGeometryStrategy::state_type state;
337 BOOST_GEOMETRY_ASSERT(! piece.sections.empty());
339 coordinate_type const point_x = geometry::get<0>(turn.robust_point);
341 for (std::size_t s = 0; s < piece.sections.size(); s++)
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)
350 for (signed_size_type i = section.begin_index + 1; i <= section.end_index; i++)
352 point_type const& previous = piece.robust_ring[i - 1];
353 point_type const& current = piece.robust_ring[i];
355 analyse_result code = check_segment<false>::apply(previous, current, turn, false, side_strategy);
356 if (code != analyse_continue)
361 // Get the state (to determine it is within), we don't have
362 // to cover the on-segment case (covered above)
363 point_in_geometry_strategy.apply(turn.robust_point, previous, current, state);
368 int const code = point_in_geometry_strategy.result(state);
371 return analyse_within;
375 return analyse_disjoint;
378 // Should normally not occur - on-segment is covered
379 return analyse_unknown;
384 template <bool UseSideOfIntersection>
385 struct check_helper_segment {};
388 struct check_helper_segment<true>
390 template <typename Point, typename Turn, typename SideStrategy>
391 static inline analyse_result apply(Point const& s1,
392 Point const& s2, Turn const& turn,
394 analyse_result result_for_original,
395 Point const& offsetted,
396 SideStrategy const& )
398 boost::ignore_unused(offsetted, is_original);
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,
410 return analyse_disjoint;
414 // If is collinear, either on segment or before/after
415 typedef geometry::model::box<Point> box_type;
418 geometry::assign_inverse(box);
419 geometry::expand(box, s1);
420 geometry::expand(box, s2);
422 if (geometry::covered_by(turn.robust_point, box))
424 return result_for_original;
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;
435 return analyse_continue;
441 struct check_helper_segment<false>
443 template <typename Point, typename Turn, typename SideStrategy>
444 static inline analyse_result apply(Point const& s1,
445 Point const& s2, Turn const& turn,
447 analyse_result result_for_original,
448 Point const& offsetted,
449 SideStrategy const& side_strategy)
451 switch(side_strategy.apply(s1, s2, turn.robust_point))
454 return analyse_disjoint; // left of segment
457 // If is collinear, either on segment or before/after
458 typedef geometry::model::box<Point> box_type;
461 geometry::assign_inverse(box);
462 geometry::expand(box, s1);
463 geometry::expand(box, s2);
465 if (geometry::covered_by(turn.robust_point, box))
467 // It is on the segment
469 && geometry::comparable_distance(turn.robust_point, offsetted) <= 1)
471 // It is within, and close to the offsetted-boundary,
472 // take any rounding-issues into account
473 return analyse_near_offsetted;
476 // Points on helper-segments are considered as within
477 // Points on original boundary are processed differently
478 return result_for_original;
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;
491 return analyse_continue;
495 template <bool UseSideOfIntersection>
496 class analyse_turn_wrt_piece
502 typename DistanceStrategy,
503 typename SideStrategy
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)
510 typedef typename Turn::robust_point_type point_type;
511 geometry::equal_to<point_type> comparator;
513 point_type points[4];
515 signed_size_type helper_count = static_cast<signed_size_type>(piece.robust_ring.size())
516 - piece.offsetted_count;
517 if (helper_count == 4)
519 for (int i = 0; i < 4; i++)
521 points[i] = piece.robust_ring[piece.offsetted_count + i];
524 // 3--offsetted outline--0
528 // 2===>==original===>===1
531 else if (helper_count == 3)
533 // Triangular piece, assign points but assign second twice
534 for (int i = 0; i < 4; i++)
536 int index = i < 2 ? i : i - 1;
537 points[i] = piece.robust_ring[piece.offsetted_count + index];
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;
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);
552 analyse_result const result_for_original
553 = one_sided || piece.is_flat_end || piece.is_flat_start
554 ? analyse_on_original_boundary
557 // First check point-equality
558 point_type const& point = turn.robust_point;
559 if (comparator(point, points[0]) || comparator(point, points[3]))
561 return analyse_on_offsetted;
563 if (comparator(point, points[1]))
565 // On original, with right corner of piece
566 return result_for_original;
568 if (comparator(point, points[2]))
570 // On original, with left corner of piece
571 return result_for_original;
574 // Right side of the piece (never an original)
575 analyse_result result
576 = check_helper_segment<UseSideOfIntersection>::apply(points[0], points[1], turn,
577 false, analyse_within, points[0], side_strategy);
578 if (result != analyse_continue)
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);
586 if (result != analyse_continue)
591 // Side of the piece at side of original geometry
592 // (here flat start/end will result in within)
593 if (! comparator(points[1], points[2]))
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);
599 if (result != analyse_continue)
605 // We are within the \/ or |_| shaped piece, where the top is the
607 if (! geometry::covered_by(point, piece.robust_offsetted_envelope))
609 // Not in offsetted-area. This makes a cheap check possible
610 switch(side_strategy.apply(points[3], points[0], point))
612 case 1 : return analyse_disjoint;
613 case -1 : return analyse_within;
614 case 0 : return analyse_disjoint;
618 return analyse_continue;
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)
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(),
633 && it != piece.robust_ring.begin())
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;
639 return check_segment<UseSideOfIntersection>::apply(*prev, *it, turn, true, side_strategy);
641 return analyse_continue;
649 typename DistanceStrategy,
650 typename SideStrategy
652 static inline analyse_result apply(Turn const& turn, Piece const& piece,
653 DistanceStrategy const& distance_strategy,
654 SideStrategy const& side_strategy)
656 typedef typename Turn::robust_point_type point_type;
657 analyse_result code = check_helper_segments(turn, piece, distance_strategy, side_strategy);
658 if (code != analyse_continue)
663 geometry::equal_to<point_type> comparator;
665 if (piece.offsetted_count > 8)
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])
672 code = check_monotonic(turn, piece, geometry::less<point_type, 0>(), side_strategy);
673 if (code != analyse_continue) return code;
675 else if (piece.is_monotonic_increasing[1])
677 code = check_monotonic(turn, piece, geometry::less<point_type, 1>(), side_strategy);
678 if (code != analyse_continue) return code;
680 else if (piece.is_monotonic_decreasing[0])
682 code = check_monotonic(turn, piece, geometry::greater<point_type, 0>(), side_strategy);
683 if (code != analyse_continue) return code;
685 else if (piece.is_monotonic_decreasing[1])
687 code = check_monotonic(turn, piece, geometry::greater<point_type, 1>(), side_strategy);
688 if (code != analyse_continue) return code;
692 // It is small or not monotonic, walk linearly through offset
693 // TODO: this will be combined with winding strategy
695 for (signed_size_type i = 1; i < piece.offsetted_count; i++)
697 point_type const& previous = piece.robust_ring[i - 1];
698 point_type const& current = piece.robust_ring[i];
700 // The robust ring can contain duplicates
701 // (on which any side or side-value would return 0)
702 if (! comparator(previous, current))
704 code = check_segment<UseSideOfIntersection>::apply(previous, current, turn, false, side_strategy);
705 if (code != analyse_continue)
712 return analyse_unknown;
717 // Helper Structure, of which the apply method returns a side value in {-1, 0, 1}
718 template <bool UseSideOfIntersection>
719 struct turn_in_piece {};
722 struct turn_in_piece<true>
726 template <typename Turn, typename Piece>
727 static inline int in_convex_piece(Turn const& turn, Piece const& piece)
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;
733 segment const p(turn.rob_pi, turn.rob_pj);
734 segment const q(turn.rob_qi, turn.rob_qj);
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);
740 // A robust ring is always closed, and always clockwise
741 for (iterator_type previous = it++; it != end; ++previous, ++it)
743 geometry::equal_to<point_type> comparator;
744 if (comparator(*previous, *it))
746 // Points are the same
750 segment r(*previous, *it);
752 int const side = strategy::side::side_of_intersection::apply(p, q, r,
757 // IP is left of segment, so it is outside
758 return -1; // outside
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))
770 return -1; // outside
779 template <typename Turn, typename Piece, typename Strategy>
780 static inline int apply(Turn const& turn, Piece const& piece,
781 Strategy const& strategy)
785 return in_convex_piece(turn, piece);
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);
799 struct turn_in_piece<false>
803 template <typename Turn, typename Piece, typename Strategy>
804 static inline int apply(Turn const& turn, Piece const& piece,
805 Strategy const& strategy)
807 return detail::within::point_in_geometry(turn.robust_point,
808 piece.robust_ring, strategy);
817 typename DistanceStrategy,
818 typename PointInGeometryStrategy,
819 typename SideStrategy
822 class turn_in_piece_visitor
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;
830 template <typename Operation, typename Piece>
831 inline bool skip(Operation const& op, Piece const& piece) const
833 if (op.piece_index == piece.index)
837 Piece const& pc = m_pieces[op.piece_index];
838 if (pc.left_index == piece.index || pc.right_index == piece.index)
840 if (pc.type == strategy::buffer::buffered_flat_end)
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
846 if (pc.type == strategy::buffer::buffered_concave)
848 // If it is concave, the same applies: the IP will be
849 // located on one of the helper segments
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)
866 , m_distance_strategy(distance_strategy)
867 , m_point_in_geometry_strategy(strategy)
868 , m_side_strategy(side_strategy)
871 template <typename Turn, typename Piece>
872 inline bool apply(Turn const& turn, Piece const& piece, bool first = true)
874 boost::ignore_unused(first);
876 if (turn.count_within > 0)
878 // Already inside - no need to check again
882 if (piece.type == strategy::buffer::buffered_flat_end
883 || piece.type == strategy::buffer::buffered_concave)
885 // Turns cannot be located within flat-end or concave pieces
889 if (! geometry::covered_by(turn.robust_point, piece.robust_envelope))
891 // Easy check: if the turn is not in the envelope, we can safely return
895 if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece))
900 // TODO: mutable_piece to make some on-demand preparations in analyse
901 Turn& mutable_turn = m_turns[turn.turn_index];
903 if (piece.type == geometry::strategy::buffer::buffered_point)
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
909 typename Turn::robust_point_type
910 >::type distance_type;
912 distance_type const cd
913 = geometry::comparable_distance(piece.robust_center,
916 if (cd < piece.robust_min_comparable_radius)
918 mutable_turn.count_within++;
921 if (cd > piece.robust_max_comparable_radius)
927 static const bool use_soi = use_side_of_intersection<CsTag>::value;
928 boost::ignore_unused(use_soi);
930 analyse_result const analyse_code =
931 piece.type == geometry::strategy::buffer::buffered_point
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);
937 case analyse_disjoint :
939 case analyse_on_offsetted :
940 mutable_turn.count_on_offsetted++; // value is not used anymore
942 case analyse_on_original_boundary :
943 mutable_turn.count_on_original_boundary++;
945 case analyse_within :
946 mutable_turn.count_within++;
948 case analyse_near_offsetted :
949 mutable_turn.count_within_near_offsetted++;
955 int const geometry_code = turn_in_piece<use_soi>::apply(turn, piece,
956 m_point_in_geometry_strategy);
958 if (geometry_code == 1)
960 mutable_turn.count_within++;
968 }} // namespace detail::buffer
969 #endif // DOXYGEN_NO_DETAIL
972 }} // namespace boost::geometry
974 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR