From 3628e85a25fb4c3abe2bc044d21ca25f9e12706f Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 22 Jun 2014 15:12:40 +0200 Subject: [PATCH] [buffer] fix non-intersecting sides around concave points there was no segment in between generated, therefore if the side-segments did not intersect (e.g. donut_diamond in polygon_buffer.cpp unit test), there was no intersection at all. Now a helper piece is generated and it will intersect. --- .../detail/buffer/buffer_inserter.hpp | 15 +++-- .../buffer/buffered_piece_collection.hpp | 12 ++++ include/boost/geometry/strategies/buffer.hpp | 7 +- .../cartesian/buffer_join_miter.hpp | 55 ++++++++-------- .../cartesian/buffer_join_round.hpp | 40 +++++------ .../cartesian/buffer_join_round_by_divide.hpp | 66 +++++++++---------- test/algorithms/buffer/polygon_buffer.cpp | 9 +++ 7 files changed, 118 insertions(+), 86 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index df46db49a..3746cfe60 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -125,15 +125,18 @@ struct buffer_range std::vector range_out; if (! first) { - output_point_type p; + output_point_type ip; segment_type s1(p1, p2); segment_type s2(previous_p1, previous_p2); - if (line_line_intersection::apply(s1, s2, p)) + if (line_line_intersection::apply(s1, s2, ip)) { - join_strategy.apply(p, *prev, previous_p2, p1, + if (!join_strategy.apply(ip, *prev, previous_p2, p1, distance.apply(*prev, *it, side), - range_out); + range_out)) + { + collection.add_piece(strategy::buffer::buffered_concave, *prev, previous_p2, p1); + } } } else @@ -174,6 +177,10 @@ struct buffer_range { collection.add_piece(strategy::buffer::buffered_join, *begin, range_out); } + else + { + collection.add_piece(strategy::buffer::buffered_concave, *begin, previous_p2, first_p1); + } } // Buffer is closed automatically by last closing corner (NOT FOR OPEN POLYGONS - TODO) 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 bba0bc35e..d221cb2ae 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -617,6 +617,18 @@ struct buffered_piece_collection pc.helper_segments.push_back(b1); } + inline void add_piece(strategy::buffer::piece_type type, point_type const& p, + point_type const& b1, point_type const& b2) + { + piece& pc = add_piece(type, false); + add_point(b1); + pc.last_segment_index = add_point(b2); + pc.helper_segments.push_back(b2); + pc.helper_segments.push_back(p); + pc.helper_segments.push_back(b1); + } + + template inline piece& add_piece(strategy::buffer::piece_type type, Range const& range, bool decrease_segment_index_by_one) { diff --git a/include/boost/geometry/strategies/buffer.hpp b/include/boost/geometry/strategies/buffer.hpp index 5de08eab7..485e174ab 100644 --- a/include/boost/geometry/strategies/buffer.hpp +++ b/include/boost/geometry/strategies/buffer.hpp @@ -61,7 +61,12 @@ enum buffer_side_selector { buffer_side_left, buffer_side_right }; */ enum piece_type { - buffered_segment, buffered_join, buffered_round_end, buffered_flat_end, buffered_circle + buffered_segment, + buffered_join, + buffered_round_end, + buffered_flat_end, + buffered_circle, + buffered_concave // always on the inside }; diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp index 73651b0c6..e65d4a27a 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp @@ -41,47 +41,46 @@ struct join_miter {} template - inline void apply(PointIn const& ip, PointIn const& vertex, + inline bool apply(PointIn const& ip, PointIn const& vertex, PointIn const& perp1, PointIn const& perp2, coordinate_type const& buffer_distance, RangeOut& range_out) const { - coordinate_type zero = 0; - int signum = buffer_distance > zero ? 1 + coordinate_type const zero = 0; + int const signum = buffer_distance > zero ? 1 : buffer_distance < zero ? -1 : 0; if (side::apply(perp1, ip, perp2) == signum) { + return false; } - else + + PointIn p = ip; + + // Normalize it and give it X*dist. + coordinate_type dx = get<0>(ip) - get<0>(vertex); + coordinate_type dy = get<1>(ip) - get<1>(vertex); + + coordinate_type length = geometry::math::sqrt(dx * dx + dy * dy); + + // TODO: make max-mitre-limit flexible + const coordinate_type ten = 10.0; + const coordinate_type zero_seven = 1.7; + + const coordinate_type max = ten * geometry::math::abs(buffer_distance); + + if (length > max) { - PointIn p = ip; - - // Normalize it and give it X*dist. - coordinate_type dx = get<0>(ip) - get<0>(vertex); - coordinate_type dy = get<1>(ip) - get<1>(vertex); - - coordinate_type length = geometry::math::sqrt(dx * dx + dy * dy); - - // TODO: make max-mitre-limit flexible - const coordinate_type ten = 10.0; - const coordinate_type zero_seven = 1.7; - - const coordinate_type max = ten * geometry::math::abs(buffer_distance); - - if (length > max) - { - const coordinate_type prop = zero_seven * buffer_distance / length; - set<0>(p, get<0>(vertex) + dx * prop); - set<1>(p, get<1>(vertex) + dy * prop); - } - - range_out.push_back(perp1); - range_out.push_back(p); - range_out.push_back(perp2); + const coordinate_type prop = zero_seven * buffer_distance / length; + set<0>(p, get<0>(vertex) + dx * prop); + set<1>(p, get<1>(vertex) + dy * prop); } + range_out.push_back(perp1); + range_out.push_back(p); + range_out.push_back(perp2); + return true; } }; diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp index f28d741e2..75039100c 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp @@ -108,7 +108,7 @@ public : } template - inline void apply(PointIn const& ip, PointIn const& vertex, + inline bool apply(PointIn const& ip, PointIn const& vertex, PointIn const& perp1, PointIn const& perp2, coordinate_type const& buffer_distance, RangeOut& range_out) const @@ -118,37 +118,37 @@ public : #ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN std::cout << "Corner for equal points " << geometry::wkt(ip) << " " << geometry::wkt(perp1) << std::endl; #endif - return; + return false; } - coordinate_type zero = 0; - int signum = buffer_distance > zero ? 1 + coordinate_type const zero = 0; + int const signum = buffer_distance > zero ? 1 : buffer_distance < zero ? -1 : 0; if (side::apply(perp1, ip, perp2) == signum) { + return false; } - else - { - // Generate 'vectors' - coordinate_type vix = (get<0>(ip) - get<0>(vertex)); - coordinate_type viy = (get<1>(ip) - get<1>(vertex)); - coordinate_type length_i = - geometry::math::sqrt(vix * vix + viy * viy); + // Generate 'vectors' + coordinate_type vix = (get<0>(ip) - get<0>(vertex)); + coordinate_type viy = (get<1>(ip) - get<1>(vertex)); - coordinate_type const bd = geometry::math::abs(buffer_distance); - coordinate_type prop = bd / length_i; + coordinate_type length_i = + geometry::math::sqrt(vix * vix + viy * viy); - PointIn bp; - set<0>(bp, get<0>(vertex) + vix * prop); - set<1>(bp, get<1>(vertex) + viy * prop); + coordinate_type const bd = geometry::math::abs(buffer_distance); + coordinate_type prop = bd / length_i; - range_out.push_back(perp1); - generate_points(vertex, perp1, perp2, bd, range_out); - range_out.push_back(perp2); - } + PointIn bp; + set<0>(bp, get<0>(vertex) + vix * prop); + set<1>(bp, get<1>(vertex) + viy * prop); + + range_out.push_back(perp1); + generate_points(vertex, perp1, perp2, bd, range_out); + range_out.push_back(perp2); + return true; } }; diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round_by_divide.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round_by_divide.hpp index 37292cf2c..542927759 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round_by_divide.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round_by_divide.hpp @@ -97,7 +97,7 @@ public : } template - inline void apply(PointIn const& ip, PointIn const& vertex, + inline bool apply(PointIn const& ip, PointIn const& vertex, PointIn const& perp1, PointIn const& perp2, coordinate_type const& buffer_distance, RangeOut& range_out) const @@ -107,48 +107,48 @@ public : #ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN std::cout << "Corner for equal points " << geometry::wkt(ip) << " " << geometry::wkt(perp1) << std::endl; #endif - return; + return false; } - coordinate_type zero = 0; - int signum = buffer_distance > zero ? 1 + coordinate_type const zero = 0; + int const signum = buffer_distance > zero ? 1 : buffer_distance < zero ? -1 : 0; if (side::apply(perp1, ip, perp2) == signum) { + return false; } - else + + // Generate 'vectors' + coordinate_type vix = (get<0>(ip) - get<0>(vertex)); + coordinate_type viy = (get<1>(ip) - get<1>(vertex)); + + coordinate_type length_i = + geometry::math::sqrt(vix * vix + viy * viy); + + coordinate_type const bd = geometry::math::abs(buffer_distance); + coordinate_type prop = bd / length_i; + + PointIn bp; + set<0>(bp, get<0>(vertex) + vix * prop); + set<1>(bp, get<1>(vertex) + viy * prop); + + range_out.push_back(perp1); + + if (m_max_level > 1) { - // Generate 'vectors' - coordinate_type vix = (get<0>(ip) - get<0>(vertex)); - coordinate_type viy = (get<1>(ip) - get<1>(vertex)); - - coordinate_type length_i = - geometry::math::sqrt(vix * vix + viy * viy); - - coordinate_type const bd = geometry::math::abs(buffer_distance); - coordinate_type prop = bd / length_i; - - PointIn bp; - set<0>(bp, get<0>(vertex) + vix * prop); - set<1>(bp, get<1>(vertex) + viy * prop); - - range_out.push_back(perp1); - - if (m_max_level > 1) - { - mid_points(vertex, perp1, bp, bd, range_out); - range_out.push_back(bp); - mid_points(vertex, bp, perp2, bd, range_out); - } - else if (m_max_level == 1) - { - range_out.push_back(bp); - } - - range_out.push_back(perp2); + mid_points(vertex, perp1, bp, bd, range_out); + range_out.push_back(bp); + mid_points(vertex, bp, perp2, bd, range_out); } + else if (m_max_level == 1) + { + range_out.push_back(bp); + } + + range_out.push_back(perp2); + return true; } }; diff --git a/test/algorithms/buffer/polygon_buffer.cpp b/test/algorithms/buffer/polygon_buffer.cpp index e2f24e29b..7f38f6c3e 100644 --- a/test/algorithms/buffer/polygon_buffer.cpp +++ b/test/algorithms/buffer/polygon_buffer.cpp @@ -26,6 +26,8 @@ static std::string const chained_box static std::string const donut_simplex = "POLYGON ((0 0,1 9,8 1,0 0),(1 1,4 1,1 4,1 1))"; +static std::string const donut_diamond + = "POLYGON((15 0,15 15,30 15,30 0,15 0),(26 11,22 14,19 10,23 07,26 11))"; static std::string const letter_L = "POLYGON ((0 0,0 4,1 4,1 1,3 1,3 0,0 0))"; static std::string const indentation @@ -133,6 +135,11 @@ void test_all() test_one("donut_simplex16", donut_simplex, 93.777, 1.6); test_one("donut_simplex16", donut_simplex, 87.933, 1.6); + test_one("donut_diamond1", donut_diamond, 280.0, 1.0); + test_one("donut_diamond4", donut_diamond, 529.0, 4.0); + test_one("donut_diamond5", donut_diamond, 625.0, 5.0); + test_one("donut_diamond6", donut_diamond, 729.0, 6.0); + test_one("arrow4", arrow, 28.265, 0.4); test_one("arrow4", arrow, 27.039, 0.4); test_one("arrow5", arrow, 31.500, 0.5); @@ -241,8 +248,10 @@ void test_all() test_one(out.str(), bowl, expected_miter[i - 1], double(i) / 2.0); } } +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("county1", county1, 0.00114092, 0.01, 0.01, false); test_one("county1", county1, 0.00132859, 0.01, 0.01, false); +#endif // Negative buffers making polygons smaller test_one("simplex", simplex, 7.04043, -0.5);