diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp index c37f32fd7..43d54fecf 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp @@ -126,6 +126,9 @@ struct buffer_turn_info int count_within; int count_on_offsetted; int count_on_helper; + int count_within_near_offsetted; + + // Obsolete: int count_on_occupied; int count_on_multi; @@ -135,6 +138,7 @@ struct buffer_turn_info , count_within(0) , count_on_offsetted(0) , count_on_helper(0) + , count_within_near_offsetted(0) , count_on_occupied(0) , count_on_multi(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 425f14168..1c082ac98 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -338,11 +338,11 @@ struct buffered_piece_collection for (typename boost::range_iterator::type it = boost::begin(m_turns); it != boost::end(m_turns); ++it) { - if (it->count_on_helper > 0) + if (it->count_within > 0) { it->location = inside_buffer; } - if (it->count_within > 0) + if (it->count_within_near_offsetted > 0) { // Within can have in rare cases a rounding issue. We don't discard this // point, so it can be used to continue started rings in traversal. But 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 9e652bec5..d43a01dcf 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 @@ -83,6 +83,44 @@ class turn_in_piece_visitor return true; } + // TODO: see comment below, this adaption of distance_projected_point can be removed + template + static inline boost::long_long_type distance_from_segment(Point const& subject, Point const& p, Point const& q) + { + typedef Point vector_type; + typedef typename geometry::coordinate_type::type coordinate_type; + + vector_type v = q; + vector_type w = subject; + subtract_point(v, p); + subtract_point(w, p); + + coordinate_type const zero = coordinate_type(); + coordinate_type const c1 = dot_product(w, v); + + if (c1 < zero) + { + // Any value above 2 is fine in this case + return 99999; + } + coordinate_type const c2 = dot_product(v, v); + if (c2 < c1 || c2 <= zero) + { + return 99999; + } + + multiply_value(v, c1); + divide_value(v, c2); + + Point projected = p; + subtract_point(v, projected); + subtract_point(w, projected); + + add_point(projected, v); + + return dot_product(subject, projected); + } + template inline bool on_offsetted(Point const& point, Piece const& piece) const @@ -105,11 +143,37 @@ class turn_in_piece_visitor return true; } } - } return false; } + template + inline boost::long_long_type + comparable_distance_from_offsetted(Point const& point, Piece const& piece) const + { + // TODO: replace this by the code below if that is fixed for boost::long_long_type + // with these contents of values + boost::long_long_type result = 0; + for (int i = 1; i < piece.offsetted_count; i++) + { + Point const& previous = piece.robust_ring[i - 1]; + Point const& current = piece.robust_ring[i]; + boost::long_long_type dist = distance_from_segment(point, previous, current); + if (i == 1 || dist < result) + { + result = dist; + } + } + return result; + + /* + geometry::model::linestring ls; + std::copy(piece.robust_ring.begin(), piece.robust_ring.begin() + piece.offsetted_count, std::back_inserter(ls)); + typename default_comparable_distance_result::type + const result = geometry::comparable_distance(point, ls); + return result; + */ + } public: @@ -184,14 +248,27 @@ public: // Then it is somewhere on the helper-segments // Classify it as inside geometry_code = 1; - mutable_turn.count_on_helper++; + mutable_turn.count_on_helper++; // can still become "near_offsetted" + } + else + { + mutable_turn.count_on_offsetted++; // value is not used anymore } } - switch (geometry_code) + if (geometry_code == 1) { - case 1 : mutable_turn.count_within++; break; - case 0 : mutable_turn.count_on_offsetted++; break; + if (comparable_distance_from_offsetted(turn.robust_point, piece) >= 2) + { + // This is too far from the border, it counts as really inside + mutable_turn.count_within++; + } + else + { + // Other points count as still "on border" because they might be + // travelled through, but not used as starting point + mutable_turn.count_within_near_offsetted++; + } } } }; diff --git a/test/algorithms/buffer/multi_polygon_buffer.cpp b/test/algorithms/buffer/multi_polygon_buffer.cpp index 8bdc8900f..4e1a73cfb 100644 --- a/test/algorithms/buffer/multi_polygon_buffer.cpp +++ b/test/algorithms/buffer/multi_polygon_buffer.cpp @@ -230,6 +230,9 @@ static std::string const rt_u3 static std::string const rt_u4 = "MULTIPOLYGON(((5 0,6 1,6 0,5 0)),((6 1,6 2,7 2,7 1,6 1)),((6 6,7 7,7 6,6 6)),((4 7,4 8,5 8,4 7)),((1 6,1 7,2 7,1 6)),((3 4,3 5,4 5,3 4)),((2 0,2 1,3 0,2 0)),((7 2,7 3,8 3,7 2)),((3 3,4 4,4 3,3 3)),((5 9,5 10,6 10,7 10,6 9,5 8,5 9)),((5 4,5 5,6 4,5 4)),((3 1,4 2,4 1,3 1)),((3 0,3 1,4 0,3 0)),((2 7,2 8,3 8,3 7,2 7)),((9 2,9 3,10 2,9 2)),((6 2,6 3,7 3,6 2)),((8 8,9 9,9 8,8 8)),((2 1,2 2,3 2,3 1,2 1)),((4 3,5 4,5 3,4 3)),((4 6,5 7,5 6,4 6)),((1 8,1 9,2 8,1 7,1 8)),((8 8,9 7,8 7,7 7,7 8,8 8)),((8 1,8 2,9 2,9 1,10 1,9 0,8 0,8 1)),((5 2,4 2,4 3,5 2)),((2 5,1 5,1 6,2 6,3 5,2 5)),((1 3,2 4,2 3,2 2,1 1,0 1,1 2,1 3)),((1 0,0 0,1 1,2 1,1.5 0.5,2 0,1 0)),((2 10,3 10,4 10,4 9,3 9,2 8,2 9,1 9,0 9,1 10,2 10)),((9 5,9 6,10 6,10 5,10 4,9 4,8.5 3.5,9 3,8 3,8 4,7 4,7 5,8 5,9 5)),((7 0,6 0,7 1,8 1,7 0)),((7 9,7 8,6 8,6 9,7 9)))"; +static std::string const rt_u5 + = "MULTIPOLYGON(((4 3,4 4,5 4,5 3,4 3)),((6 5,6 6,7 6,6 5)),((5 4,6 5,6 4,5 4)),((4 0,4 1,5 1,5 0,4 0)),((7 8,8 9,8 8,7 8)),((8 2,8 3,9 3,8 2)),((2 1,2 2,3 1,2 1)),((5 7,5 8,6 8,5 7)),((4 5,4 6,5 5,4 5)),((7 0,8 1,8 0,7 0)),((7 2,8 1,7 1,6 1,7 2)),((3 3,4 3,4 2,3 2,3 3)),((3 9,3 10,4 9,3 9)),((1 2,2 3,2 2,1 2)),((2 4,2 5,3 4,2 3,2 4)),((3 7,4 7,3 6,2 6,3 7)))"; + template void test_all() @@ -344,6 +347,7 @@ void test_all() test_one("rt_u2", rt_u2, join_round, end_flat, 138.8001, 1.0); test_one("rt_u3", rt_u3, join_round, end_flat, 133.4526, 1.0); test_one("rt_u4", rt_u4, join_round, end_flat, 126.9268, 1.0); + test_one("rt_u5", rt_u5, join_round, end_flat, 78.4906, 1.0); } int test_main(int, char* []) diff --git a/test/algorithms/buffer/test_buffer.hpp b/test/algorithms/buffer/test_buffer.hpp index cd6685d95..16b76d144 100644 --- a/test/algorithms/buffer/test_buffer.hpp +++ b/test/algorithms/buffer/test_buffer.hpp @@ -138,10 +138,10 @@ struct svg_visitor out << " " << bg::method_char(it->method) << ":" << bg::operation_char(it->operations[0].operation) << "/" << bg::operation_char(it->operations[1].operation); - out << " " << (!it->selectable_start ? "w" : "") - << (it->count_on_multi > 0 ? "m" : "") - << (it->count_on_occupied > 0 ? "o" : "") + out << " " << (it->count_on_offsetted > 0 ? "b" : "") // b: offsetted border + << (it->count_within_near_offsetted > 0 ? "n" : "") + << (it->count_within > 0 ? "w" : "") << (it->count_on_helper > 0 ? "h" : "") ;