From 64ee58ed46d371553bb315a0f693fc9ea3737099 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 8 Mar 2015 18:08:43 +0100 Subject: [PATCH] [buffer] Fix calculation of the number of points. For an angle of 125 or more, no points in between were generated, which was erroneous. Thanks to Menelaos for spotting and Adam for the calculation This changes some of the unit test results, because in some cases a point in between is generated now, creating a larger polygon --- doc/release_notes.qbk | 3 +- .../cartesian/buffer_join_round.hpp | 13 ++- test/algorithms/buffer/linestring_buffer.cpp | 22 ++--- .../buffer/multi_linestring_buffer.cpp | 18 ++-- .../buffer/multi_polygon_buffer.cpp | 2 +- test/algorithms/buffer/polygon_buffer.cpp | 88 ++++++++++++------- 6 files changed, 89 insertions(+), 57 deletions(-) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 6b732cefd..224cff1a8 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -40,7 +40,7 @@ * [@https://svn.boost.org/trac/boost/ticket/10467 10467] Template parameter name coliding with B0 macro name defined in termios.h * [@https://svn.boost.org/trac/boost/ticket/10666 10666] MSVC compiler warning C4127: conditional expression is constant * [@https://svn.boost.org/trac/boost/ticket/10747 10747] Error in rescaling causing errors in areal/areal set operations -* [@https://svn.boost.org/trac/boost/ticket/10770 10770] Buffer fails for large distances, or rough round joints, where concavities where not intersected properly +* [@https://svn.boost.org/trac/boost/ticket/10770 10770] Buffer fails for large distances, or rough round joins, where concavities where not intersected properly * [@https://svn.boost.org/trac/boost/ticket/10861 10861] Rtree failing to compile for Value being a pair or a tuple containing pointer to Geometry and the default equal_to<> used * [@https://svn.boost.org/trac/boost/ticket/10863 10863] Template parameter name coliding with B0 macro name defined in termios.h (duplicate of 10467) * [@https://svn.boost.org/trac/boost/ticket/10887 10887] Invalid result of within() and relate() for Linear/MultiPolygon @@ -60,6 +60,7 @@ * Bug in Cartesian segment-segment intersection strategy when one segment degenerates to a point and is collinear to the other non-degenerate segment * Bug in centroid(), non-deterministic result if calculated for e.g. a Polygon with zero-area. * Bug in buffers for joins with a limited number of points +* Bug in buffers for round joins with limited number of points around sharp corners * Bug in buffers for joins with large buffer distances * Bug in buffers for round ends with an odd number of points * Bug in buffers for flat ends with large buffer distances diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp index 2472c5385..0a37983fa 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp @@ -89,11 +89,20 @@ private : // Divide the angle into an integer amount of steps to make it // visually correct also for a low number of points / circle - int const n - = static_cast(m_points_per_circle * angle_diff / two_pi); + + // If a full circle is divided into 3 parts (e.g. angle is 125), + // the one point in between must still be generated + // The calculation below: + // - generates 1 point in between for an angle of 125 based on 3 points + // - generates 0 points in between for an angle of 90 based on 4 points + + int const n = 1 + std::max(static_cast( + ceil(-1.0 + m_points_per_circle * angle_diff / two_pi)), 0); PromotedType const diff = angle_diff / static_cast(n); PromotedType a = angle1 - diff; + + // Walk to n - 1 to avoid generating the last point for (int i = 0; i < n - 1; i++, a -= diff) { Point p; diff --git a/test/algorithms/buffer/linestring_buffer.cpp b/test/algorithms/buffer/linestring_buffer.cpp index 63f6d8567..337ea2824 100644 --- a/test/algorithms/buffer/linestring_buffer.cpp +++ b/test/algorithms/buffer/linestring_buffer.cpp @@ -183,24 +183,24 @@ void test_all() test_one("mysql_report_2015_03_02a_3", mysql_report_2015_03_02a, join_round(3), end_round(3), 38.000, d10); test_one("mysql_report_2015_03_02a_4", mysql_report_2015_03_02a, join_round(4), end_round(4), 38.000, d10); - test_one("mysql_report_2015_03_02a_5", mysql_report_2015_03_02a, join_round(5), end_round(5), 38.169, d10); - test_one("mysql_report_2015_03_02a_6", mysql_report_2015_03_02a, join_round(6), end_round(6), 38.196, d10); - test_one("mysql_report_2015_03_02a_7", mysql_report_2015_03_02a, join_round(7), end_round(7), 38.230, d10); + test_one("mysql_report_2015_03_02a_5", mysql_report_2015_03_02a, join_round(5), end_round(5), 38.790, d10); + test_one("mysql_report_2015_03_02a_6", mysql_report_2015_03_02a, join_round(6), end_round(6), 38.817, d10); + test_one("mysql_report_2015_03_02a_7", mysql_report_2015_03_02a, join_round(7), end_round(7), 38.851, d10); test_one("mysql_report_2015_03_02b_3", mysql_report_2015_03_02b, join_round(3), end_round(3), 36.500, d10); test_one("mysql_report_2015_03_02b_4", mysql_report_2015_03_02b, join_round(4), end_round(4), 36.500, d10); - test_one("mysql_report_2015_03_02b_5", mysql_report_2015_03_02b, join_round(5), end_round(5), 36.724, d10); - test_one("mysql_report_2015_03_02b_6", mysql_report_2015_03_02b, join_round(6), end_round(6), 36.781, d10); - test_one("mysql_report_2015_03_02b_7", mysql_report_2015_03_02b, join_round(7), end_round(7), 36.884, d10); + test_one("mysql_report_2015_03_02b_5", mysql_report_2015_03_02b, join_round(5), end_round(5), 37.346, d10); + test_one("mysql_report_2015_03_02b_6", mysql_report_2015_03_02b, join_round(6), end_round(6), 37.402, d10); + test_one("mysql_report_2015_03_02b_7", mysql_report_2015_03_02b, join_round(7), end_round(7), 37.506, d10); test_one("mysql_report_2015_03_02c_3", mysql_report_2015_03_02c, join_round(2), end_round(3), 32.500, d10); test_one("mysql_report_2015_03_02c_4", mysql_report_2015_03_02c, join_round(4), end_round(4), 32.500, d10); - test_one("mysql_report_2015_03_02c_5", mysql_report_2015_03_02c, join_round(5), end_round(5), 32.990, d10); - test_one("mysql_report_2015_03_02c_6", mysql_report_2015_03_02c, join_round(6), end_round(6), 33.098, d10); - test_one("mysql_report_2015_03_02c_7", mysql_report_2015_03_02c, join_round(7), end_round(7), 33.279, d10); + test_one("mysql_report_2015_03_02c_5", mysql_report_2015_03_02c, join_round(5), end_round(5), 33.611, d10); + test_one("mysql_report_2015_03_02c_6", mysql_report_2015_03_02c, join_round(6), end_round(6), 33.719, d10); + test_one("mysql_report_2015_03_02c_7", mysql_report_2015_03_02c, join_round(7), end_round(7), 33.901, d10); // Testing the asymmetric end caps with odd number of points double const d15 = 1.5; - test_one("mysql_report_2015_03_02c_asym1", mysql_report_2015_03_02c, join_round(7), end_round(7), 39.093, d10, d15); - test_one("mysql_report_2015_03_02c_asym2", mysql_report_2015_03_02c, join_round(7), end_round(7), 44.718, d15, d10); + test_one("mysql_report_2015_03_02c_asym1", mysql_report_2015_03_02c, join_round(7), end_round(7), 39.714, d10, d15); + test_one("mysql_report_2015_03_02c_asym2", mysql_report_2015_03_02c, join_round(7), end_round(7), 46.116, d15, d10); } diff --git a/test/algorithms/buffer/multi_linestring_buffer.cpp b/test/algorithms/buffer/multi_linestring_buffer.cpp index b4ed68b63..24aca1a67 100644 --- a/test/algorithms/buffer/multi_linestring_buffer.cpp +++ b/test/algorithms/buffer/multi_linestring_buffer.cpp @@ -59,7 +59,7 @@ void test_all() test_one("two_bends", two_bends, join_round, end_flat, 64.6217, 1.5, 1.5); test_one("bend_near_start1", bend_near_start1, join_round, end_flat, 202.5910, 9.0, 9.0); - test_one("bend_near_start2", bend_near_start2, join_round, end_flat, 231.4882, 9.0, 9.0); + test_one("bend_near_start2", bend_near_start2, join_round, end_flat, 231.4988, 9.0, 9.0); // TODO this should be fixed test_one("turn_inside", turn_inside, join_round, end_flat, 99, 1.5, 1.5); test_one("two_bends_asym", two_bends, join_round, end_flat, 52.3793, 1.5, 0.75); @@ -85,20 +85,20 @@ void test_all() // (The expected area for large distances is about R*R*PI where R is distance) // Note that for large distances the flat ends (not tested here) still give weird effects test_one("mikado1_large", mikado1, join_round32, end_round32, 5455052109.518, 41751.0); - test_one("mikado1_small", mikado1, join_round32, end_round32, 1057.12, 10.0); - test_one("mikado1_small", mikado1, join_round32, end_flat, 874.337, 10.0); + test_one("mikado1_small", mikado1, join_round32, end_round32, 1057.37, 10.0); + test_one("mikado1_small", mikado1, join_round32, end_flat, 874.590, 10.0); test_one("mikado2_large", mikado2, join_round32, end_round32, 19878812278.387, 79610.0); - test_one("mikado2_small", mikado2, join_round32, end_round32, 1082.344, 10.0); - test_one("mikado2_small", mikado2, join_round32, end_flat, 711.552, 10.0); + test_one("mikado2_small", mikado2, join_round32, end_round32, 1082.470, 10.0); + test_one("mikado2_small", mikado2, join_round32, end_flat, 711.678, 10.0); test_one("mikado3_large", mikado3, join_round32, end_round32, 29151950703.779, 96375.0); - test_one("mikado3_small", mikado3, join_round32, end_round32, 2532.945, 10.0); - test_one("mikado3_small", mikado3, join_round32, end_flat, 2135.627, 10.0); + test_one("mikado3_small", mikado3, join_round32, end_round32, 2533.285, 10.0); + test_one("mikado3_small", mikado3, join_round32, end_flat, 2136.236, 10.0); test_one("mikado4_large", mikado4, join_round32, end_round32, 11212832197.267, 59772.0); - test_one("mikado4_small", mikado4, join_round32, end_round32, 2103.113, 10.0); - test_one("mikado4_small", mikado4, join_round32, end_flat, 1930.327, 10.0); + test_one("mikado4_small", mikado4, join_round32, end_round32, 2103.686, 10.0); + test_one("mikado4_small", mikado4, join_round32, end_flat, 1930.785, 10.0); } diff --git a/test/algorithms/buffer/multi_polygon_buffer.cpp b/test/algorithms/buffer/multi_polygon_buffer.cpp index 81da5bcba..4b526a8e6 100644 --- a/test/algorithms/buffer/multi_polygon_buffer.cpp +++ b/test/algorithms/buffer/multi_polygon_buffer.cpp @@ -438,7 +438,7 @@ void test_all() // (the change was irrelevant to this, so they succeeded earlier by luck). // TODO: get_occupation/left_turns in combination with a u/u turn test_one("rt_u7", rt_u7, join_round, end_flat, 35.6233, 1.0); - test_one("rt_u7_rough", rt_u7, join_round_rough, end_flat, 35.0483, 1.0); + test_one("rt_u7_rough", rt_u7, join_round_rough, end_flat, 35.1675, 1.0); } test_one("rt_u8", rt_u8, join_miter, end_flat, 70.9142, 1.0); diff --git a/test/algorithms/buffer/polygon_buffer.cpp b/test/algorithms/buffer/polygon_buffer.cpp index d0eeedb09..2403c61a3 100644 --- a/test/algorithms/buffer/polygon_buffer.cpp +++ b/test/algorithms/buffer/polygon_buffer.cpp @@ -71,6 +71,9 @@ static std::string const triangle static std::string const sharp_triangle = "POLYGON((2 0,3 8,4 0,2 0))"; +static std::string const right_triangle + = "POLYGON((0 1,20 0,0 0,0 1))"; + static std::string const degenerate0 = "POLYGON(())"; @@ -225,17 +228,17 @@ void test_all() test_one("concave_b75", concave_b, join_miter, end_flat, 36318.1642, 75.0); test_one("concave_b100", concave_b, join_miter, end_flat, 63879.5140, 100.0); - test_one("concave_b10", concave_b, join_round, end_flat, 532.2763, 10.0); - test_one("concave_b25", concave_b, join_round, end_flat, 2482.7577, 25.0); - test_one("concave_b50", concave_b, join_round, end_flat, 8872.6784, 50.0); - test_one("concave_b75", concave_b, join_round, end_flat, 19186.8841, 75.0); - test_one("concave_b100", concave_b, join_round, end_flat, 33425.4379, 100.0); + test_one("concave_b10", concave_b, join_round, end_flat, 532.2875, 10.0); + test_one("concave_b25", concave_b, join_round, end_flat, 2482.8329, 25.0); + test_one("concave_b50", concave_b, join_round, end_flat, 8872.9719, 50.0); + test_one("concave_b75", concave_b, join_round, end_flat, 19187.5490, 75.0); + test_one("concave_b100", concave_b, join_round, end_flat, 33426.6139, 100.0); - test_one("concave_b_rough_10", concave_b, join_round_rough, end_flat, 512.8457, 10.0); - test_one("concave_b_rough_25", concave_b, join_round_rough, end_flat, 2364.5658, 25.0); - test_one("concave_b_rough_50", concave_b, join_round_rough, end_flat, 8410.399996, 50.0); - test_one("concave_b_rough_75", concave_b, join_round_rough, end_flat, 18153.9268, 75.0); - test_one("concave_b_rough_100", concave_b, join_round_rough, end_flat, 31595.1463, 100.0); + test_one("concave_b_rough_10", concave_b, join_round_rough, end_flat, 520.312, 10.0); + test_one("concave_b_rough_25", concave_b, join_round_rough, end_flat, 2409.384, 25.0); + test_one("concave_b_rough_50", concave_b, join_round_rough, end_flat, 8586.812, 50.0); + test_one("concave_b_rough_75", concave_b, join_round_rough, end_flat, 18549.018, 75.0); + test_one("concave_b_rough_100", concave_b, join_round_rough, end_flat, 32295.917, 100.0); test_one("spike_simplex15", spike_simplex, join_round, end_round, 50.3633, 1.5); test_one("spike_simplex15", spike_simplex, join_miter, end_flat, 51.5509, 1.5); @@ -244,7 +247,7 @@ void test_all() test_one("spike_simplex30", spike_simplex, join_round, end_round, 100.9199, 3.0); test_one("spike_simplex30", spike_simplex, join_miter, end_flat, 120.9859, 3.0); #endif - test_one("spike_simplex150", spike_simplex, join_round, end_round, 998.9530, 15.0); + test_one("spike_simplex150", spike_simplex, join_round, end_round, 998.9821, 15.0); #if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("spike_simplex150", spike_simplex, join_miter, end_flat, 1532.6543, 15.0); #endif @@ -430,33 +433,33 @@ void test_all() test_one("county1", county1, join_round, end_flat, 0.00114092, -0.01); test_one("county1", county1, join_miter, end_flat, 0.00132859, -0.01); - test_one("parcel1_10", parcel1, join_round, end_flat, 7571.39121246337891, 10.0); + test_one("parcel1_10", parcel1, join_round, end_flat, 7571.405, 10.0); test_one("parcel1_10", parcel1, join_miter, end_flat, 8207.45314788818359, 10.0); - test_one("parcel1_20", parcel1, join_round, end_flat, 11648.0537185668945, 20.0); + test_one("parcel1_20", parcel1, join_round, end_flat, 11648.111, 20.0); test_one("parcel1_20", parcel1, join_miter, end_flat, 14184.0223083496094, 20.0); - test_one("parcel1_30", parcel1, join_round, end_flat, 16350.3611068725586, 30.0); - test_one("parcel1_30", parcel1, join_miter, end_flat, 22417.8007659912109, 30.0); + test_one("parcel1_30", parcel1, join_round, end_flat, 16350.488, 30.0); + test_one("parcel1_30", parcel1, join_miter, end_flat, 22417.799, 30.0); - test_one("parcel2_10", parcel2, join_round, end_flat, 5000.85063171386719, 10.0); + test_one("parcel2_10", parcel2, join_round, end_flat, 5000.867, 10.0); test_one("parcel2_10", parcel2, join_miter, end_flat, 5091.12226867675781, 10.0); - test_one("parcel2_20", parcel2, join_round, end_flat, 9049.60844421386719, 20.0); + test_one("parcel2_20", parcel2, join_round, end_flat, 9049.673, 20.0); test_one("parcel2_20", parcel2, join_miter, end_flat, 9410.69154357910156, 20.0); - test_one("parcel2_30", parcel2, join_round, end_flat, 13726.3790588378906, 30.0); + test_one("parcel2_30", parcel2, join_round, end_flat, 13726.528, 30.0); test_one("parcel2_30", parcel2, join_miter, end_flat, 14535.2319564819336, 30.0); - test_one("parcel3_10", parcel3, join_round, end_flat, 19992.6824035644531, 10.0); + test_one("parcel3_10", parcel3, join_round, end_flat, 19993.007, 10.0); test_one("parcel3_10", parcel3, join_miter, end_flat, 20024.5579376220703, 10.0, 10.0, true, 0.05); // MSVC 14 reports 20024.51456, so we increase the tolerance - test_one("parcel3_20", parcel3, join_round, end_flat, 34505.0746192932129, 20.0); + test_one("parcel3_20", parcel3, join_round, end_flat, 34505.837, 20.0); test_one("parcel3_20", parcel3, join_miter, end_flat, 34633.2606201171875, 20.0); - test_one("parcel3_30", parcel3, join_round, end_flat, 45261.4196014404297, 30.0); + test_one("parcel3_30", parcel3, join_round, end_flat, 45262.452, 30.0); test_one("parcel3_30", parcel3, join_miter, end_flat, 45567.3875694274902, 30.0); - test_one("parcel3_bend_5", parcel3_bend, join_round, end_flat, 155.6188, 5.0); - test_one("parcel3_bend_10", parcel3_bend, join_round, end_flat, 458.4187, 10.0); + test_one("parcel3_bend_5", parcel3_bend, join_round, end_flat, 155.634, 5.0); + test_one("parcel3_bend_10", parcel3_bend, join_round, end_flat, 458.454, 10.0); - // These cases differ a bit based on point order (TODO: find out / describe why) - test_one("parcel3_bend_15", parcel3_bend, join_round, end_flat, Clockwise ? 917.9747 : 917.996, 15.0); - test_one("parcel3_bend_20", parcel3_bend, join_round, end_flat, Clockwise ? 1534.4795 : 1534.508, 20.0); + // These cases differ a bit based on point order, because piece generation is different in one corner. Tolerance is increased + test_one("parcel3_bend_15", parcel3_bend, join_round, end_flat, 918.06, 15.0, 15.0, true, 0.25); + test_one("parcel3_bend_20", parcel3_bend, join_round, end_flat, 1534.64, 20.0, 20.0, true, 0.25); // Parcel - deflated test_one("parcel1_10", parcel1, join_round, end_flat, 1571.9024, -10.0); @@ -464,12 +467,12 @@ void test_all() test_one("parcel1_20", parcel1, join_round, end_flat, 209.3579, -20.0); test_one("parcel1_20", parcel1, join_miter, end_flat, 188.4224, -20.0); - test_one("nl_part1_2", nl_part1, join_round, end_flat, 1848737386.1343, -0.2 * 1000.0); - test_one("nl_part1_5", nl_part1, join_round, end_flat, 1775954035.2947, -0.5 * 1000.0); + test_one("nl_part1_2", nl_part1, join_round, end_flat, 1848737355.633, -0.2 * 1000.0); + test_one("nl_part1_5", nl_part1, join_round, end_flat, 1775953844.678, -0.5 * 1000.0); - test_one("italy_part1_30", italy_part1, join_round, end_flat, 5015307172.18164, 30.0 * 1000.0); - test_one("italy_part1_50", italy_part1, join_round, end_flat, 11362317400.63281, 50.0 * 1000.0); - test_one("italy_part1_60", italy_part1, join_round, end_flat, 15478123348.53125, 60.0 * 1000.0); + test_one("italy_part1_30", italy_part1, join_round, end_flat, 5015638830.339, 30.0 * 1000.0); + test_one("italy_part1_50", italy_part1, join_round, end_flat, 11363180030.278, 50.0 * 1000.0); + test_one("italy_part1_60", italy_part1, join_round, end_flat, 15479097108.720, 60.0 * 1000.0); // Tickets test_one("ticket_10398_1_5", ticket_10398_1, join_miter, end_flat, 494.7192, 0.5, -999, false); @@ -544,7 +547,7 @@ void test_all() test_with_custom_strategies("sharp_triangle_j12", sharp_triangle, join_round(12), end_flat, distance(1.0), side_strategy, point_strategy, - 29.0980); + 29.1604); // Test very various number of points (min is 3) test_with_custom_strategies("sharp_triangle_j2", sharp_triangle, @@ -553,7 +556,7 @@ void test_all() test_with_custom_strategies("sharp_triangle_j5", sharp_triangle, join_round(5), end_flat, distance(1.0), side_strategy, point_strategy, - 28.1091); + 28.8563); test_with_custom_strategies("sharp_triangle_j36", sharp_triangle, @@ -586,6 +589,25 @@ void test_all() join_miter(25), end_flat, distance(4.0), side_strategy, point_strategy, 244.7471); + // Right triangles, testing both points around sharp corner as well as points + // around right corners in join_round strategy + test_with_custom_strategies("right_triangle_j3", + right_triangle, + join_round(3), end_flat, distance(1.0), side_strategy, point_strategy, + 53.0240); + test_with_custom_strategies("right_triangle_j4", + right_triangle, + join_round(4), end_flat, distance(1.0), side_strategy, point_strategy, + 53.2492); + test_with_custom_strategies("right_triangle_j5", + right_triangle, + join_round(5), end_flat, distance(1.0), side_strategy, point_strategy, + 53.7430); + test_with_custom_strategies("right_triangle_j6", + right_triangle, + join_round(6), end_flat, distance(1.0), side_strategy, point_strategy, + 53.7430); + buffer_custom_side_strategy custom_side_strategy; test_with_custom_strategies("sharp_triangle_custom_side", sharp_triangle,