From aa307b5ad24e8e3b49f205400dae96ef16e2a75c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Jun 2014 22:39:29 +0200 Subject: [PATCH] [buffer] add join_selector and use it before generating the join. This moves responsibility of generation to the calling code, removes it from the join strategies. It also enables using the end-strategy for the spikes --- .../detail/buffer/buffer_inserter.hpp | 81 ++++++++++++++----- .../detail/buffer/line_line_intersection.hpp | 32 +------- include/boost/geometry/strategies/buffer.hpp | 13 +++ .../cartesian/buffer_join_miter.hpp | 12 --- .../cartesian/buffer_join_round.hpp | 12 --- .../cartesian/buffer_join_round_by_divide.hpp | 12 --- test/algorithms/buffer/linestring_buffer.cpp | 4 + test/algorithms/buffer/polygon_buffer.cpp | 11 ++- 8 files changed, 92 insertions(+), 85 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index eb76b94f2..167cddde1 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -44,7 +45,6 @@ struct buffer_range typedef typename point_type::type output_point_type; typedef typename coordinate_type::type coordinate_type; - template < typename Point, @@ -90,6 +90,7 @@ struct buffer_range typename RobustPolicy > static inline void add_join(Collection& collection, + strategy::buffer::join_selector join, Point const& previous_input, output_point_type const& prev_perp1, output_point_type const& prev_perp2, @@ -101,26 +102,67 @@ struct buffer_range JoinStrategy const& join_strategy, RobustPolicy const& ) { - - output_point_type intersection_point; - if (line_line_intersection::apply( - perp1, perp2, prev_perp1, prev_perp2, intersection_point)) + switch(join) { - std::vector range_out; - if (join_strategy.apply(intersection_point, - previous_input, prev_perp2, perp1, - distance.apply(previous_input, input, side), - range_out)) - { - collection.add_piece(strategy::buffer::buffered_join, - previous_input, range_out); - } - else - { + case strategy::buffer::join_continue : + // No join, we get two consecutive sides + return; + case strategy::buffer::join_convex : + break; // All code below handles this + case strategy::buffer::join_concave : collection.add_piece(strategy::buffer::buffered_concave, previous_input, prev_perp2, perp1); - } + return; + case strategy::buffer::join_spike : + // TODO use end strategy + // For now: use join strategy + //return; + break; } + + output_point_type intersection_point; + line_line_intersection::apply( + perp1, perp2, prev_perp1, prev_perp2, + intersection_point); + std::vector range_out; + if (join_strategy.apply(intersection_point, + previous_input, prev_perp2, perp1, + distance.apply(previous_input, input, side), + range_out)) + { + collection.add_piece(strategy::buffer::buffered_join, + previous_input, range_out); + } + } + + template + static inline bool parallel_continue(T dx1, T dy1, T dx2, T dy2) + { + return math::sign(dx1) == math::sign(dx2) + && math::sign(dy1) == math::sign(dy2); + } + + static inline strategy::buffer::join_selector get_join_type( + output_point_type const& p0, + output_point_type const& p1, + output_point_type const& p2) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + int const side = side_strategy::apply(p0, p1, p2); + return side == -1 ? strategy::buffer::join_convex + : side == 1 ? strategy::buffer::join_concave + : parallel_continue + ( + get<0>(p2) - get<0>(p1), + get<1>(p2) - get<1>(p1), + get<0>(p1) - get<0>(p0), + get<1>(p1) - get<1>(p0) + ) ? strategy::buffer::join_continue + : strategy::buffer::join_spike; } template @@ -155,7 +197,7 @@ struct buffer_range robust_point_type previous_robust_input; output_point_type previous_p1, previous_p2; output_point_type first_p1, first_p2; - point_type penultimate_point, ultimate_point; // last two points from begin/end + point_type second_point, penultimate_point, ultimate_point; // last two points from begin/end bool first = true; @@ -175,6 +217,7 @@ struct buffer_range if (! first) { add_join(collection, + get_join_type(penultimate_point, *prev, *it), *prev, previous_p1, previous_p2, *it, p1, p2, side, @@ -191,6 +234,7 @@ struct buffer_range if (first) { first = false; + second_point = *it; first_p1 = p1; first_p2 = p2; } @@ -205,6 +249,7 @@ struct buffer_range { // Generate closing corner add_join(collection, + get_join_type(penultimate_point, ultimate_point, second_point), *(end - 1), previous_p1, previous_p2, *begin, first_p1, first_p2, side, diff --git a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp index 68608b33b..2ed787b62 100644 --- a/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp @@ -32,15 +32,6 @@ struct line_line_intersection return a * d - b * c; } - // Returns true if two parallel segments (vectors dx1,dy1 and dx2,dy2) - // move forward (one continues each other - they have the same signs) - template - static inline bool parallel_continue(T dx1, T dy1, T dx2, T dy2) - { - return math::sign(dx1) == math::sign(dx2) - && math::sign(dy1) == math::sign(dy2); - } - template static inline bool apply(Point const& pi, Point const& pj, Point const& qi, Point const& qj, Point& ip) @@ -50,7 +41,7 @@ struct line_line_intersection coordinate_type denominator = det(get<0>(pi) - get<0>(pj), get<1>(pi) - get<1>(pj), get<0>(qi) - get<0>(qj), get<1>(qi) - get<1>(qj)); - // TODO: maybe use something else then denominator (sides?) to determine this. + // TODO: code below will be removed - curve type is now checked before // The limit is arbitrary. If it is small, the IP will be far far away. // For round joins, it will not be used at all. @@ -59,28 +50,9 @@ struct line_line_intersection coordinate_type const limit = 1.0e-9; if (geometry::math::abs(denominator) < limit) { - // If denominator is small or zero, segments are (nearly) parallel - // Either they continue each other - // +---------------+--------------+ - // x1,y1 x2,y2=x3,y3 x4,y4 - // We then return false - - // Or they form a spikey feature - // x1,y1 - // +---------------+ x2,y2 - // +---------------/ x3,y4 - // x4,y4 - // We then calculate the IP from one of the segments up to a certain distance - if (parallel_continue(get<0>(qj) - get<0>(qi), - get<1>(qj) - get<1>(qi), - get<0>(pj) - get<0>(pi), - get<1>(pj) - get<1>(pi))) - { - return false; - } - // Spikey, set something arbitrary and calculate px,py far away denominator = limit; } + // END TODO coordinate_type d1 = det(get<0>(pi), get<1>(pi), get<0>(pj), get<1>(pj)); coordinate_type d2 = det(get<0>(qi), get<1>(qi), get<0>(qj), get<1>(qj)); diff --git a/include/boost/geometry/strategies/buffer.hpp b/include/boost/geometry/strategies/buffer.hpp index 485e174ab..6b27c35bd 100644 --- a/include/boost/geometry/strategies/buffer.hpp +++ b/include/boost/geometry/strategies/buffer.hpp @@ -70,6 +70,19 @@ enum piece_type }; +/*! +\brief Enumerates types of joins +\ingroup enum +*/ +enum join_selector +{ + join_convex, + join_concave, + join_continue, // collinear, next segment touches previous segment + join_spike // collinear, with overlap, next segment goes back +}; + + }} // namespace strategy::buffer diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp index e65d4a27a..c3989b887 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -33,7 +32,6 @@ template > struct join_miter { - typedef typename strategy::side::services::default_strategy::type>::type side; typedef typename coordinate_type::type coordinate_type; // Constructor compatible with other join strategies: @@ -46,16 +44,6 @@ struct join_miter coordinate_type const& buffer_distance, RangeOut& range_out) const { - 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; - } - PointIn p = ip; // Normalize it and give it X*dist. diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp index 75039100c..f81cd2adb 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -42,7 +41,6 @@ public : : m_steps_per_circle(steps_per_circle) {} - typedef typename strategy::side::services::default_strategy::type>::type side; typedef typename coordinate_type::type coordinate_type; typedef typename geometry::select_most_precise @@ -121,16 +119,6 @@ public : return false; } - 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; - } - // Generate 'vectors' coordinate_type vix = (get<0>(ip) - get<0>(vertex)); coordinate_type viy = (get<1>(ip) - get<1>(vertex)); 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 542927759..fd09a0835 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 @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -42,7 +41,6 @@ public : : m_max_level(max_level) {} - typedef typename strategy::side::services::default_strategy::type>::type side; typedef typename coordinate_type::type coordinate_type; typedef typename geometry::select_most_precise @@ -110,16 +108,6 @@ public : return false; } - 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; - } - // Generate 'vectors' coordinate_type vix = (get<0>(ip) - get<0>(vertex)); coordinate_type viy = (get<1>(ip) - get<1>(vertex)); diff --git a/test/algorithms/buffer/linestring_buffer.cpp b/test/algorithms/buffer/linestring_buffer.cpp index a82bdcedd..425d7b0d9 100644 --- a/test/algorithms/buffer/linestring_buffer.cpp +++ b/test/algorithms/buffer/linestring_buffer.cpp @@ -28,6 +28,7 @@ static std::string const one_bend = "LINESTRING(0 0,4 5,7 4)"; static std::string const two_bends = "LINESTRING(0 0,4 5,7 4,10 6)"; static std::string const overlapping = "LINESTRING(0 0,4 5,7 4,10 6, 10 2,2 2)"; static std::string const curve = "LINESTRING(2 7,3 5,5 4,7 5,8 7)"; +static std::string const tripod = "LINESTRING(5 0,5 5,1 8,5 5,9 8)"; // with spike static std::string const for_collinear = "LINESTRING(2 0,0 0,0 4,6 4,6 0,4 0)"; static std::string const for_collinear2 = "LINESTRING(2 1,2 0,0 0,0 4,6 4,6 0,4 0,4 1)"; @@ -116,6 +117,9 @@ void test_all() test_one("curve", curve, 55.3875, 5.0, 3.0); #endif + test_one("tripod", tripod, 89.55, 3.0); + test_one("tripod", tripod, 117.806, 3.0); + test_one("chained2", chained2, 11.3137, 2.5, 1.5); test_one("chained3", chained3, 16.9706, 2.5, 1.5); test_one("chained4", chained4, 22.6274, 2.5, 1.5); diff --git a/test/algorithms/buffer/polygon_buffer.cpp b/test/algorithms/buffer/polygon_buffer.cpp index bc2dd6e93..638288f29 100644 --- a/test/algorithms/buffer/polygon_buffer.cpp +++ b/test/algorithms/buffer/polygon_buffer.cpp @@ -24,6 +24,9 @@ static std::string const spike_simplex static std::string const chained_box = "POLYGON((0 0,0 4,4 4,8 4,12 4,12 0,8 0,4 0,0 0))"; +static std::string const join_types + = "POLYGON ((0 0,0 4,4 4,2 6,0 8,2 6,4 8,8 4,4 0,0 0))"; // first 4 join types are all different: convex, concave, continue, spike + 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 @@ -99,6 +102,8 @@ void test_all() test_one("spike_simplex150", spike_simplex, 998.9530, 15.0); test_one("spike_simplex150", spike_simplex, 1532.6543, 15.0); + test_one("join_types", join_types, 91.7379, 1.5); + test_one("chained_box", chained_box, 83.1403, 1.0); test_one("chained_box", chained_box, 84, 1.0); test_one("L", letter_L, 13.7314, 0.5); @@ -275,12 +280,16 @@ void test_all() #if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("parcel3_10", parcel3, 99, 10.0); -#endif test_one("parcel3_10", parcel3, 20022.4271087646484, 10.0); +#endif test_one("parcel3_20", parcel3, 34504.8032569885254, 20.0, -999, false); +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("parcel3_20", parcel3, 34615.6553726196289, 20.0); +#endif test_one("parcel3_30", parcel3, 45263.0166702270508, 30.0, -999, false); +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("parcel3_30", parcel3, 45506.1910133361816, 30.0); +#endif // Negative buffers making polygons smaller test_one("simplex", simplex, 7.04043, -0.5);