diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index 7ce96b91c8..2a6d62ef6c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -107,69 +107,86 @@ inline void enrich_sort(Operations& operations, } +// Assign travel-to-vertex/ip index for each turn. template inline void enrich_assign(Operations& operations, Turns& turns, - bool check_turns) + bool check_consecutive_turns) { - if (operations.empty()) - { - return; - } - - // Assign travel-to-vertex/ip index for each turning point. - // Iterator "next" is circular - - geometry::ever_circling_range_iterator next(operations); - ++next; - - for (auto const& indexed : operations) + for_each_with_index(operations, [&](std::size_t index, auto const& indexed) { auto& turn = turns[indexed.turn_index]; auto& op = turn.operations[indexed.operation_index]; - if (check_turns && indexed.turn_index == next->turn_index) + std::size_t next_index = index + 1 < operations.size() ? index + 1 : 0; + auto advance = [&operations](auto index) + { + std::size_t const result = index + 1; + return result >= operations.size() ? 0 : result; + }; + + auto next_turn = [&operations, &turns, &next_index]() + { + return turns[operations[next_index].turn_index]; + }; + auto next_operation = [&operations, &turns, &next_index]() + { + auto const& next_turn = turns[operations[next_index].turn_index]; + return next_turn.operations[operations[next_index].operation_index]; + }; + + if (check_consecutive_turns + && indexed.turn_index == operations[next_index].turn_index + && op.seg_id == next_operation().seg_id) { - // Normal behaviour: next points at next turn, increase next. - // For dissolve this should not be done, turn_index is often - // the same for two consecutive operations - ++next; + // If the two operations on the same turn are ordered consecutively, + // and they are on the same segment, then the turn where to travel to should + // be considered one further. Therefore next is increased. + // + // It often happens in buffer, in these configurations: + // +---->--+ + // | | + // | +->-*----> + // | | | + // ^ +-<-+ + // If the next index is not corrected, the small rectangle + // will be kept in the output. + + // This is a normal situation and occurs, for example, in every concave bend. + // In general it should always travel from turn to next turn. + // Only in some circumstances traveling to the same turn is necessary, for example + // if there is only one turn in the outer ring. + // + // (For dissolve this is not done, turn_index is often + // the same for two consecutive operations - but the conditions are changed + // and this should be verified again) + next_index = advance(next_index); } // Cluster behaviour: next should point after cluster, unless // their seg_ids are not the same // (For dissolve, this is still to be examined - TODO) while (turn.is_clustered() - && indexed.turn_index != next->turn_index - && turn.cluster_id == turns[next->turn_index].cluster_id - && op.seg_id == turns[next->turn_index].operations[next->operation_index].seg_id) + && turn.cluster_id == next_turn().cluster_id + && op.seg_id == next_operation().seg_id + && indexed.turn_index != operations[next_index].turn_index) { - ++next; + next_index = advance(next_index); } - auto const& next_turn = turns[next->turn_index]; - auto const& next_op = next_turn.operations[next->operation_index]; - op.enriched.travels_to_ip_index - = static_cast(next->turn_index); + = static_cast(operations[next_index].turn_index); op.enriched.travels_to_vertex_index - = next->subject->seg_id.segment_index; + = operations[next_index].subject->seg_id.segment_index; + auto const& next_op = next_operation(); if (op.seg_id.segment_index == next_op.seg_id.segment_index - && op.fraction < next_op.fraction) - { - // Next turn is located further on same segment - // assign next_ip_index - // (this is one not circular therefore fraction is considered) - op.enriched.next_ip_index = static_cast(next->turn_index); - } - - if (! check_turns) + && op.fraction < next_op.fraction) { - ++next; + // Next turn is located further on same segment: assign next_ip_index + op.enriched.next_ip_index = static_cast(operations[next_index].turn_index); } - } + }); - // DEBUG #ifdef BOOST_GEOMETRY_DEBUG_ENRICH for (auto const& indexed_op : operations) { @@ -191,8 +208,6 @@ inline void enrich_assign(Operations& operations, Turns& turns, << std::endl; } #endif - // END DEBUG - } template @@ -490,13 +505,6 @@ inline void enrich_intersection_points(Turns& turns, pair.second, turns, geometry1, geometry2, robust_policy, strategy); -#ifdef BOOST_GEOMETRY_DEBUG_ENRICH - std::cout << "ENRICH-sort Ring " << pair.first << std::endl; - for (auto const& op : pair.second) - { - std::cout << op.turn_index << " " << op.operation_index << std::endl; - } -#endif } if (has_colocations) diff --git a/test/algorithms/buffer/CMakeLists.txt b/test/algorithms/buffer/CMakeLists.txt index 87fc74079a..1fb93e5ae1 100644 --- a/test/algorithms/buffer/CMakeLists.txt +++ b/test/algorithms/buffer/CMakeLists.txt @@ -7,6 +7,7 @@ foreach(item IN ITEMS buffer buffer_gc + buffer_variable_width ) boost_geometry_add_unit_test("algorithms" ${item}) endforeach() diff --git a/test/algorithms/buffer/buffer_linestring.cpp b/test/algorithms/buffer/buffer_linestring.cpp index da0fb9e14a..45bd06cc2f 100644 --- a/test/algorithms/buffer/buffer_linestring.cpp +++ b/test/algorithms/buffer/buffer_linestring.cpp @@ -34,7 +34,9 @@ static std::string const two_bends = "LINESTRING(0 0,4 5,7 4,10 6)"; static std::string const bend_near_start1 = "LINESTRING(0 0,1 0,5 2)"; static std::string const bend_near_start2 = "LINESTRING(0 0,1 0,2 1.5,5 3)"; -static std::string const overlapping = "LINESTRING(0 0,4 5,7 4,10 6, 10 2,2 2)"; +static std::string const overlapping = "LINESTRING(0 0,4 5,7 4,10 6,10 2,2 2)"; +static std::string const overlapping_rev = "LINESTRING(2 2,10 2,10 6,7 4,4 5,0 0)"; + 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 @@ -111,6 +113,8 @@ static std::string const issue_803 = "LINESTRING(2773.6899360413681 -17.49335640 static std::string const issue_988 = "LINESTRING(0.10144 0.034912,0.106079 0.035156,0.109375 0.034302,0.114502 0.035889,0.116333 0.036743,0.117065 0.036499,0.121582 0.035156,0.12439 0.029175,0.123779 0.026978,0.12146 0.025513,0.119507 0.025513,0.118164 0.025513,0.114624 0.025757,0.111694 0.026001,0.108887 0.02832,0.105957 0.028442,0.099854 0.027344,0.095581 0.029419,0.093506 0.031128,0.090576 0.032593,0.085571 0.032959,0.082153 0.035522,0.081421 0.039307,0.082275 0.044067,0.083862 0.047485,0.08606 0.049805,0.087891 0.051025,0.090942 0.05188,0.094727 0.051392,0.100098 0.050049,0.10144 0.05249,0.100952 0.054932,0.098633 0.05835,0.098267 0.062134,0.098755 0.064697,0.098511 0.067383,0.113892 0.068848,0.110718 0.065552,0.109619 0.064331,0.111084 0.063965,0.118042 0.0625,0.115234 0.049805,0.117676 0.049194,0.118774 0.046997,0.119385 0.04541,0.119507 0.043945,0.116089 0.041138,0.116089 0.041016,0.11438 0.040894,0.11145 0.041016,0.109009 0.042114,0.106079 0.04248,0.102417 0.041138,0.102051 0.040039)"; static std::string const issue_1084 = "LINESTRING(269.3 -7.03, 270.23 -3.57, 270.54 0, 270.54 100, 270.23 103.57, 269.3 107.03, 267.79 110.27, 265.73 113.2, 263.2 115.73, 258.89 118.43, 254.28 108.54, 248.52 109, 80 97.21, 72.71 95.87, 67.46 93.82, 62.61 90.92, 58.32 87.27, 54.69 82.95, 51.83 78.08, 49.81 72.81, 48.7 67.28, 48.45 63.38, 48.52 34.87, 49.29 29.26, 50.96 23.87, 54.69 17.05, 58.32 12.73, 62.61 9.08, 65.57 7.18, 70.68 4.81, 76.12 3.31, 80 2.79, 248.52 -9, 254.28 -8.54, 258.89 -18.43, 263.2 -15.73, 265.73 -13.2, 267.79 -10.27, 269.3 -7.03, 270.23 -3.57, 270.54 0, 270.54 100, 270.23 103.57, 269.3 107.03, 267.79 110.27, 265.73 113.2, 264 114.93, 256.24 107.17, 251.86 108.16, 248.58 108.2, 76.24 95.9, 70.93 94.43, 65.94 92.11, 61.4 88.99, 57.44 85.16, 54.18 80.72, 51.69 75.8, 50.06 70.54, 49.25 63.38, 49.32 34.93, 50.06 29.46, 51.69 24.2, 55.36 17.49, 58.9 13.28, 63.1 9.71, 67.83 6.89, 70.93 5.57, 76.24 4.1, 80.06 3.59, 248.58 -8.2, 251.86 -8.16, 256.24 -7.17, 258.89 -18.43)"; +static std::string const issue_1250 = "LINESTRING(3 1, 4 2, 4 4, 2 4, 2 2, 3 2)"; +static std::string const issue_1250_rev = "LINESTRING(3 2, 2 2, 2 4, 4 4, 4 2, 3 1)"; template void test_all() @@ -179,23 +183,38 @@ void test_all() test_one("bend_near_start1", bend_near_start1, join_round, end_flat, 109.2625, 9.0); test_one("bend_near_start2", bend_near_start2, join_round, end_flat, 142.8709, 9.0); - // Next (and all similar cases) which a offsetted-one-sided buffer has to be fixed. TODO + // Next (and all similar cases) which a offsetted-one-sided buffer has to be supported. TODO //test_one("two_bends_neg", two_bends, join_miter, end_flat, 99, +1.5, settings, -1.0); //test_one("two_bends_pos", two_bends, join_miter, end_flat, 99, -1.5, settings, +1.0); - //test_one("two_bends_neg", two_bends, join_round, end_flat,99, +1.5, settings, -1.0); + //test_one("two_bends_neg", two_bends, join_round, end_flat,99, +1.5, settings, -1.0); //test_one("two_bends_pos", two_bends, join_round, end_flat, 99, -1.5, settings, +1.0); - test_one("overlapping150", overlapping, join_round, end_flat, 65.6786, 1.5); - test_one("overlapping150", overlapping, join_miter, end_flat, 68.140, 1.5); + test_one("overlapping_50", overlapping, join_round, end_flat, 24.6326, 0.5); + test_one("overlapping_50", overlapping, join_miter, end_flat, 24.9147, 0.5); + test_one("overlapping_150", overlapping, join_round, end_flat, 65.6786, 1.5); + test_one("overlapping_150", overlapping, join_miter, end_flat, 68.140, 1.5); + + test_one("overlapping_rev_50", overlapping_rev, join_round, end_flat, 24.6326, 0.5); + test_one("overlapping_rev_50", overlapping_rev, join_miter, end_flat, 24.9147, 0.5); + test_one("overlapping_rev_150", overlapping_rev, join_round, end_flat, 65.6786, 1.5); + test_one("overlapping_rev_150", overlapping_rev, join_miter, end_flat, 68.140, 1.5); // Different cases with intersection points on flat and (left/right from line itself) - test_one("overlapping_asym_150_010", overlapping, join_round, end_flat, 48.308, 1.5, settings, 0.25); - test_one("overlapping_asym_150_010", overlapping, join_miter, end_flat, 50.770, 1.5, settings, 0.25); + test_one("overlapping_asym_150_025", overlapping, join_round, end_flat, 48.308, 1.5, settings, 0.25); + test_one("overlapping_asym_150_025", overlapping, join_miter, end_flat, 50.770, 1.5, settings, 0.25); test_one("overlapping_asym_150_075", overlapping, join_round, end_flat, 58.506, 1.5, settings, 0.75); test_one("overlapping_asym_150_075", overlapping, join_miter, end_flat, 60.985, 1.5, settings, 0.75); test_one("overlapping_asym_150_100", overlapping, join_round, end_flat, 62.514, 1.5, settings, 1.0); test_one("overlapping_asym_150_100", overlapping, join_miter, end_flat, 64.984, 1.5, settings, 1.0); + // The reverse cases need to reverse the distance too, for the same result + test_one("overlapping_rev_asym_150_025", overlapping_rev, join_round, end_flat, 48.308, 0.25, settings, 1.5); + test_one("overlapping_rev_asym_150_025", overlapping_rev, join_miter, end_flat, 50.770, 0.25, settings, 1.5); + test_one("overlapping_rev_asym_150_075", overlapping_rev, join_round, end_flat, 58.506, 0.75, settings, 1.5); + test_one("overlapping_rev_asym_150_075", overlapping_rev, join_miter, end_flat, 60.985, 0.75, settings, 1.5); + test_one("overlapping_rev_asym_150_100", overlapping_rev, join_round, end_flat, 62.514, 1.0, settings, 1.5); + test_one("overlapping_rev_asym_150_100", overlapping_rev, join_miter, end_flat, 64.984, 1.0, settings, 1.5); + // Having flat end test_one("for_collinear", for_collinear, join_round, end_flat, 68.561, 2.0); test_one("for_collinear", for_collinear, join_miter, end_flat, 72, 2.0); @@ -326,6 +345,12 @@ void test_all() using bg::strategy::buffer::end_round; test_one("issue_1084", issue_1084, join_round(10), end_round(10), 13200.83, 10.0); } + { + using bg::strategy::buffer::join_miter; + using bg::strategy::buffer::end_flat; + test_one("issue_1250", issue_1250, join_miter(5), end_flat(), 8.39277, 0.5); + test_one("issue_1250_rev", issue_1250_rev, join_miter(5), end_flat(), 8.39277, 0.5); + } test_one("mysql_report_2015_06_11", mysql_report_2015_06_11, join_round32, end_round32,