diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_touch.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_touch.hpp index b7e0a8ea0..bece40faf 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_touch.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_touch.hpp @@ -125,9 +125,10 @@ private : if (turn.both(operation_union)) { bool traverse = turn_should_be_traversed(turns, turn, map); + bool start = traverse && turn_should_be_startable(turns, turn, index); #ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TOUCH std::cout << " " << index << " " - << std::boolalpha << traverse + << std::boolalpha << traverse << " " << start << std::endl; #endif @@ -137,13 +138,66 @@ private : // separate rings (outer ring / inner ring) turn.switch_source = true; - // It will now be traversed - the traveral information is, - // for union, already assigned + if (start) + { + turn.selectable_start = true; + } } } } } + static inline + bool turn_should_be_startable(const Turns& turns, + const turn_type& uu_turn, + signed_size_type uu_turn_index) + { + return turn_startable(turns, uu_turn, 0, uu_turn_index) + || turn_startable(turns, uu_turn, 1, uu_turn_index); + + } + + static inline + bool turn_startable(const Turns& turns, + const turn_type& uu_turn, + std::size_t op_index, + signed_size_type original_turn_index, + std::size_t iteration = 0) + { + if (iteration >= boost::size(turns)) + { + // Defensive check to avoid infinite recursion + return false; + } + + signed_size_type const index + = uu_turn.operations[op_index].enriched.travels_to_ip_index; + if (index == original_turn_index) + { + // Completely traveled, having u/u only, via this op_index + return true; + } + signed_size_type const turns_size = + static_cast(boost::size(turns)); + + if (index < 0 || index >= turns_size) + { + return false; + } + + const turn_type& new_turn = turns[index]; + + if (new_turn.selectable_start) + { + // Already selectable - no need to select u/u turn too + return false; + } + + // If this u/u turn is traversed normally (without skipping), sources are switched + return turn_startable(turns, new_turn, 1 - op_index, + original_turn_index, iteration + 1); + } + static inline bool turn_should_be_traversed(const Turns& turns, const turn_type& uu_turn, const map_type& map) diff --git a/test/algorithms/overlay/handle_touch.cpp b/test/algorithms/overlay/handle_touch.cpp index 2e37ac7b6..a39b26197 100644 --- a/test/algorithms/overlay/handle_touch.cpp +++ b/test/algorithms/overlay/handle_touch.cpp @@ -67,6 +67,7 @@ struct test_handle_touch static void apply(std::string const& case_id, std::size_t expected_traverse, std::size_t expected_skipped, + std::size_t expected_start, G1 const& g1, G2 const& g2) { @@ -109,6 +110,7 @@ struct test_handle_touch std::size_t uu_traverse = 0; std::size_t uu_skipped = 0; + std::size_t uu_start = 0; BOOST_FOREACH(turn_info const& turn, turns) { if (turn.both(bg::detail::overlay::operation_union)) @@ -121,6 +123,10 @@ struct test_handle_touch { uu_skipped++; } + if (turn.selectable_start) + { + uu_start++; + } } } @@ -136,6 +142,12 @@ struct test_handle_touch << " detected: " << uu_skipped << " type: " << string_from_type ::type>::name()); + BOOST_CHECK_MESSAGE(expected_start == uu_start, + "handle_touch: " << case_id + << " start expected: " << expected_start + << " detected: " << uu_skipped + << " type: " << string_from_type + ::type>::name()); #if defined(TEST_WITH_SVG) { @@ -191,7 +203,11 @@ struct test_handle_touch if (turn.both(bg::detail::overlay::operation_union)) { // Adapt color to give visual feedback in SVG - if (turn.switch_source) + if (turn.switch_source && turn.selectable_start) + { + color = "fill:rgb(0,0,255);"; // blue + } + else if (turn.switch_source) { color = "fill:rgb(0,128,0);"; // green } @@ -272,6 +288,7 @@ struct test_handle_touch inline static void apply(std::string const& case_id, std::size_t expected_traverse, std::size_t expected_skipped, + std::size_t expected_start, std::string const& wkt1, std::string const& wkt2) { if (wkt1.empty() || wkt2.empty()) @@ -289,7 +306,9 @@ struct test_handle_touch bg::correct(g2); detail_test_handle_touch::apply(case_id, - expected_traverse, expected_skipped, + expected_traverse, + expected_skipped, + expected_start, g1, g2); } @@ -306,13 +325,13 @@ void test_geometries() ov::operation_union > test_union; - test_union::apply("case_36", 1, 0, case_36[0], case_36[1]); - test_union::apply("case_80", 1, 0, case_80[0], case_80[1]); - test_union::apply("case_81", 1, 0, case_81[0], case_81[1]); - test_union::apply("case_82", 0, 2, case_82[0], case_82[1]); - test_union::apply("case_83", 2, 0, case_83[0], case_83[1]); - test_union::apply("case_84", 0, 3, case_84[0], case_84[1]); - test_union::apply("case_85", 1, 0, case_85[0], case_85[1]); + test_union::apply("case_36", 1, 0, 0, case_36[0], case_36[1]); + test_union::apply("case_80", 1, 0, 0, case_80[0], case_80[1]); + test_union::apply("case_81", 1, 0, 0, case_81[0], case_81[1]); + test_union::apply("case_82", 0, 2, 0, case_82[0], case_82[1]); + test_union::apply("case_83", 2, 0, 1, case_83[0], case_83[1]); + test_union::apply("case_84", 0, 3, 0, case_84[0], case_84[1]); + test_union::apply("case_85", 1, 0, 0, case_85[0], case_85[1]); } @@ -329,13 +348,13 @@ void test_multi_geometries() test_union::apply ( - "uu_case_1", 0, 1, + "uu_case_1", 0, 1, 0, "MULTIPOLYGON(((4 0,2 2,4 4,6 2,4 0)))", "MULTIPOLYGON(((4 4,2 6,4 8,6 6,4 4)))" ); test_union::apply ( - "uu_case_2", 0, 2, + "uu_case_2", 0, 2, 0, "MULTIPOLYGON(((0 0,0 2,2 4,4 2,6 4,8 2,8 0,0 0)))", "MULTIPOLYGON(((0 8,8 8,8 6,6 4,4 6,2 4,0 6,0 8)))" ); @@ -343,14 +362,14 @@ void test_multi_geometries() // Provided by Menelaos (1) test_union::apply ( - "uu_case_3", 0, 2, + "uu_case_3", 0, 2, 0, "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((15 5,15 10,20 10,20 5,15 5)))", "MULTIPOLYGON(((10 0,15 5,15 0,10 0)),((10 5,10 10,15 10,15 5,10 5)))" ); // Provided by Menelaos (2) test_union::apply ( - "uu_case_4", 1, 0, + "uu_case_4", 1, 0, 0, "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0)),((15 5,15 10,20 10,20 5,15 5)))", "MULTIPOLYGON(((10 0,15 5,20 5,20 0,10 0)),((10 5,10 10,15 10,15 5,10 5)))" ); @@ -358,7 +377,7 @@ void test_multi_geometries() // Mailed by Barend test_union::apply ( - "uu_case_5", 1, 0, + "uu_case_5", 1, 0, 0, "MULTIPOLYGON(((4 0,2 2,4 4,6 2,4 0)),((4 6,6 8,8 6,6 4,4 6)))", "MULTIPOLYGON(((4 4,2 6,4 8,6 6,4 4)),((4 2,7 6,8 3,4 2)))" ); @@ -366,7 +385,7 @@ void test_multi_geometries() // Formerly referred to as a test_union::apply ( - "uu_case_6", 2, 0, + "uu_case_6", 2, 0, 0, "MULTIPOLYGON(((4 8,4 10,6 10,6 8,4 8)),((7 7,7 11,10 11,10 7,7 7)))", "MULTIPOLYGON(((6 6,6 8,8 8,8 6,6 6)),((6 10,6 12,8 12,8 10,6 10)),((9 9,11 9,11 2,3 2,3 9,5 9,5 3,9 3,9 9)))" ); @@ -376,7 +395,7 @@ void test_multi_geometries() // Formerly referred to as b test_union::apply ( - "uu_case_7", 0, 2, + "uu_case_7", 0, 2, 0, "MULTIPOLYGON(((4 8,4 10,6 10,6 8,4 8)),((7 7,7 11,10 11,10 7,7 7)))", "MULTIPOLYGON(((6 6,6 8,8 8,8 6,6 6)),((6 10,6 12,8 12,8 10,6 10)))" ); @@ -386,7 +405,7 @@ void test_multi_geometries() // Formerly referred to as c test_union::apply ( - "uu_case_8", 0, 4, + "uu_case_8", 0, 4, 0, "MULTIPOLYGON(((4 8,4 10,6 10,6 8,4 8)),((8 8,8 10,10 10,10 8,8 8)),((7 11,7 13,13 13,13 5,7 5,7 7,11 7,11 11,7 11)))", "MULTIPOLYGON(((6 6,6 8,8 8,8 6,6 6)),((6 10,6 12,8 12,8 10,6 10)))" ); @@ -397,7 +416,7 @@ void test_multi_geometries() // Formerly referred to as d test_union::apply ( - "uu_case_9", 0, 2, + "uu_case_9", 0, 2, 0, "MULTIPOLYGON(((2 4,2 6,4 6,4 4,2 4)),((6 4,6 6,8 6,8 4,6 4)),((1 0,1 3,9 3,9 0,1 0)))", "MULTIPOLYGON(((0 2,0 4,2 4,2 2,0 2)),((8 2,8 4,10 4,10 2,8 2)),((3 5,3 7,7 7,7 5,3 5)))" ); @@ -407,7 +426,7 @@ void test_multi_geometries() // With a c/c turn test_union::apply ( - "uu_case_10", 1, 0, + "uu_case_10", 1, 0, 0, "MULTIPOLYGON(((6 4,6 9,9 9,9 6,11 6,11 4,6 4)),((10 7,10 10,12 10,12 7,10 7)))", "MULTIPOLYGON(((10 5,10 8,12 8,12 5,10 5)),((6 10,8 12,10 10,8 8,6 10)))" ); @@ -415,7 +434,7 @@ void test_multi_geometries() // With c/c turns in both involved polygons test_union::apply ( - "uu_case_11", 1, 0, + "uu_case_11", 1, 0, 0, "MULTIPOLYGON(((7 4,7 8,9 8,9 6,11 6,11 4,7 4)),((10 7,10 10,12 10,12 7,10 7)))", "MULTIPOLYGON(((10 5,10 8,12 8,12 5,10 5)),((7 7,7 10,10 10,9 9,9 7,7 7)))" ); @@ -424,55 +443,55 @@ void test_multi_geometries() // (This one breaks if continue is not checked in handle_touch) test_union::apply ( - "uu_case_12", 1, 0, + "uu_case_12", 1, 0, 0, "MULTIPOLYGON(((10 8,10 10,12 10,12 8,10 8)),((10 4,10 7,12 7,12 4,10 4)),((7 5,7 8,9 8,9 5,7 5)))", "MULTIPOLYGON(((7 3,7 6,9 6,9 5,11 5,11 3,7 3)),((10 6,10 9,12 9,12 6,10 6)),((7 7,7 10,10 10,9 9,9 7,7 7)))" ); test_union::apply ( - "case_62_multi", 0, 1, case_62_multi[0], case_62_multi[1] + "case_62_multi", 0, 1, 0, case_62_multi[0], case_62_multi[1] ); test_union::apply ( - "case_63_multi", 0, 1, case_63_multi[0], case_63_multi[1] + "case_63_multi", 0, 1, 0, case_63_multi[0], case_63_multi[1] ); test_union::apply ( - "case_65_multi", 0, 2, case_65_multi[0], case_65_multi[1] + "case_65_multi", 0, 2, 0, case_65_multi[0], case_65_multi[1] ); test_union::apply ( - "case_66_multi", 0, 2, case_66_multi[0], case_66_multi[1] + "case_66_multi", 0, 2, 0, case_66_multi[0], case_66_multi[1] ); test_union::apply ( - "case_75_multi", 0, 4, case_75_multi[0], case_75_multi[1] + "case_75_multi", 0, 4, 0, case_75_multi[0], case_75_multi[1] ); test_union::apply ( - "case_76_multi", 0, 5, case_76_multi[0], case_76_multi[1] + "case_76_multi", 0, 5, 0, case_76_multi[0], case_76_multi[1] ); test_union::apply ( - "case_101_multi", 2, 0, case_101_multi[0], case_101_multi[1] + "case_101_multi", 2, 0, 0, case_101_multi[0], case_101_multi[1] ); test_union::apply ( - "case_108_multi", 0, 0, case_108_multi[0], case_108_multi[1] + "case_108_multi", 0, 0, 0, case_108_multi[0], case_108_multi[1] ); // NOTE: this result is still to be checked test_union::apply ( - "case_recursive_boxes_3", 8, 18, + "case_recursive_boxes_3", 8, 18, 0, case_recursive_boxes_3[0], case_recursive_boxes_3[1] ); } -template +template void test_all() { typedef bg::model::point point_type; @@ -481,23 +500,17 @@ void test_all() < bg::model::multi_polygon < - bg::model::polygon + bg::model::polygon > >(); - test_geometries >(); - - // CCW: - // test_geometries - // >, true>(); + test_geometries >(); } int test_main(int, char* []) { - test_all(); + test_all(); return 0; } diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index 39dd983c4..7c7b118c0 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -188,12 +188,10 @@ void test_areal() test_one("82", case_82[0], case_82[1], 2, 0, 9, 175); -#if defined(BOOST_GEOMETRY_OVERLAY_INCLUDE_FAILING_TESTS) - // Requires selecting a u/u turn as a starting point if (and only if) - // there are only u/u turns involved in a ring + // Case where one u/u turn should be selectable as a starting point test_one("83", case_83[0], case_83[1], 1, 2, 13, 172.917); -#endif + test_one("84", case_84[0], case_84[1], 2, 0, 11, 170); test_one("85",