From 56fc166435dac176160c80023fc2004eca2d06c2 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 22 Nov 2014 19:15:17 +0100 Subject: [PATCH] [buffer] extend piece analyses, check helper-segments for on-offsetted, on-original, disjointness. This removes the workaround w.r.t. has_concave --- .../detail/buffer/buffer_inserter.hpp | 6 + .../detail/buffer/buffer_policies.hpp | 2 + .../buffer/buffered_piece_collection.hpp | 10 +- .../detail/buffer/turn_in_piece_visitor.hpp | 180 +++++++++++++++--- 4 files changed, 168 insertions(+), 30 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index c734293f6..2fcac5cfa 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -869,6 +869,11 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator typename tag_cast::type, areal_tag>::type, areal_tag >::type::value; + bool const linear = boost::is_same + < + typename tag_cast::type, linear_tag>::type, + linear_tag + >::type::value; dispatch::buffer_inserter < @@ -885,6 +890,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator robust_policy); collection.get_turns(); + collection.classify_turns(linear); if (areal) { collection.check_remaining_points(distance_strategy); diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp index 67845861d..eec16e6d2 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp @@ -124,6 +124,7 @@ struct buffer_turn_info intersection_location_type location; int count_within; + int count_on_original_boundary; int count_on_offsetted; int count_on_helper; int count_within_near_offsetted; @@ -138,6 +139,7 @@ struct buffer_turn_info : turn_index(-1) , location(location_ok) , count_within(0) + , count_on_original_boundary(0) , count_on_offsetted(0) , count_on_helper(0) , count_within_near_offsetted(0) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index 7e7069fee..9a2f6b18f 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -379,7 +379,7 @@ struct buffered_piece_collection } } - inline void classify_turns() + inline void classify_turns(bool linear) { for (typename boost::range_iterator::type it = boost::begin(m_turns); it != boost::end(m_turns); ++it) @@ -388,6 +388,10 @@ struct buffered_piece_collection { it->location = inside_buffer; } + if (it->count_on_original_boundary > 0 && ! linear) + { + it->location = inside_buffer; + } if (it->count_within_near_offsetted > 0) { // Within can have in rare cases a rounding issue. We don't discard this @@ -599,10 +603,6 @@ struct buffered_piece_collection >::apply(m_turns, m_pieces, visitor); } - - classify_turns(); - - //get_occupation(); } inline void start_new_ring() diff --git a/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp b/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp index 166dd6d7a..bf340d0f5 100644 --- a/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp @@ -59,6 +59,9 @@ enum analyse_result { analyse_unknown, analyse_continue, + analyse_disjoint, + analyse_within, + analyse_on_original_boundary, analyse_on_offsetted, analyse_near_offsetted }; @@ -116,13 +119,136 @@ class analyse_turn_wrt_piece return analyse_continue; } + template + static inline analyse_result check_helper_segment(Point const& s1, + Point const& s2, Point const& point, + analyse_result return_if_collinear, + Point const& offsetted) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + switch(side_strategy::apply(s1, s2, point)) + { + case 1 : + return analyse_disjoint; // left of segment + case 0 : + { + // If is collinear, either on segment or before/after + typedef geometry::model::box box_type; + + box_type box; + bg::assign_inverse(box); + bg::expand(box, s1); + bg::expand(box, s2); + + if (geometry::covered_by(point, box)) + { + // It is on the segment + if (return_if_collinear == analyse_within + && bg::comparable_distance(point, offsetted) <= 1) + { + // It is close to the offsetted-boundary, take + // any rounding-issues into account + return analyse_near_offsetted; + } + + return return_if_collinear; + } + + // It is not on the segment + return analyse_continue; + } + break; + } + + // right of segment + return analyse_continue; + } + + template + static inline analyse_result check_helper_segments(Point const& point, Piece const& piece) + { + geometry::equal_to comparator; + + Point points[4]; + + int helper_count = piece.robust_ring.size() - piece.offsetted_count; + if (helper_count == 4) + { + for (int i = 0; i < 4; i++) + { + points[i] = piece.robust_ring[piece.offsetted_count + i]; + } + } + else if (helper_count == 3) + { + // Triangular piece, assign points but assign second twice + for (int i = 0; i < 4; i++) + { + int index = i < 2 ? i : i - 1; + points[i] = piece.robust_ring[piece.offsetted_count + index]; + } + } + else + { + // Some pieces (e.g. around points) do not have helper segments. + // Others should have 3 (join) or 4 (side) + return analyse_continue; + } + + // First check point-equality + if (comparator(point, points[0]) || comparator(point, points[3])) + { + return analyse_on_offsetted; + } + if (comparator(point, points[1]) || comparator(point, points[2])) + { + return analyse_on_original_boundary; + } + + // Right side + analyse_result result + = check_helper_segment(points[0], points[1], point, + analyse_within, points[0]); + if (result != analyse_continue) + { + return result; + } + + // Left side + result = check_helper_segment(points[2], points[3], point, + analyse_within, points[3]); + if (result != analyse_continue) + { + return result; + } + + if (! comparator(points[1], points[2])) + { + // Side at original + result = check_helper_segment(points[1], points[2], point, + analyse_on_original_boundary, point); + if (result != analyse_continue) + { + return result; + } + } + + return analyse_continue; + } + public : template static inline analyse_result apply(Point const& point, Piece const& piece) { - // TODO: we will check first helper-segments here, if it is left of - // any helper segment we're done (assuming joins stay within the area - // of the helper-segments, even if they have concavities there + analyse_result code = check_helper_segments(point, piece); + if (code != analyse_continue) + { + return code; + } geometry::equal_to comparator; @@ -135,14 +261,15 @@ public : // (on which any side or side-value would return 0) if (! comparator(previous, current)) { - analyse_result code = check_segment(previous, current, point); + code = check_segment(previous, current, point); if (code != analyse_continue) { return code; } } } - return analyse_unknown; // Not collinear or very close + + return analyse_unknown; } }; @@ -186,10 +313,6 @@ public: return; } - // TEMPORARY solution to enable one-sided buffer. - // TODO: this will replaced by analyse_piece - bool has_concave = false; - bool neighbour = false; for (int i = 0; i < 2; i++) { @@ -201,11 +324,6 @@ public: Piece const& pc = m_pieces[turn.operations[i].piece_index]; - if (pc.type == strategy::buffer::buffered_concave) - { - has_concave = true; - } - if (pc.left_index == piece.index || pc.right_index == piece.index) { @@ -225,6 +343,28 @@ public: } } + // TODO: mutable_piece to make some on-demand preparations in analyse + analyse_result analyse_code + = analyse_turn_wrt_piece::apply(turn.robust_point, piece); + + Turn& mutable_turn = m_turns[turn.turn_index]; + switch(analyse_code) + { + case analyse_disjoint : + return; + case analyse_on_offsetted : + mutable_turn.count_on_offsetted++; // value is not used anymore + return; + case analyse_on_original_boundary : + mutable_turn.count_on_original_boundary++; + return; + case analyse_within : + mutable_turn.count_within++; + return; + default : + break; + } + // TODO: this point_in_geometry is a performance-bottleneck here and // will be replaced completely by extending analyse_piece functionality int geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring); @@ -241,12 +381,6 @@ public: return; } - // TODO: mutable_piece to make some on-demand preparations in analyse - analyse_result analyse_code - = analyse_turn_wrt_piece::apply(turn.robust_point, piece); - - Turn& mutable_turn = m_turns[turn.turn_index]; - switch(analyse_code) { case analyse_on_offsetted : @@ -259,11 +393,7 @@ public: } break; default : - if (geometry_code == 1 || (geometry_code == 0 && ! has_concave)) - { - mutable_turn.count_within++; - } - + mutable_turn.count_within++; break; } }