// Boost.Geometry // Robustness Test // Copyright (c) 2019 Barend Gehrels, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include // Basic case. Union should deliver 22.0 static std::string case_a[2] = { "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))", "MULTIPOLYGON(((2 7,4 7,4 3,2 3,2 7)))" }; // Case with an interior ring. Union should deliver 73.0 static std::string case_b[2] = { "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))", "MULTIPOLYGON(((-1 -1,-1 8,8 8,8 -1,-1 -1),(2 7,2 3,4 3,4 7,2 7)))" }; static std::string case_c[2] = { "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))", "MULTIPOLYGON(((1 1,0 1,0 3,1 3,1 1)))" }; template bool test_overlay(std::string const& caseid, Geometry const& g1, Geometry const& g2, double expected_area, bool do_output) { typedef typename boost::range_value::type geometry_out; typedef bg::detail::overlay::overlay < Geometry, Geometry, bg::detail::overlay::do_reverse::value>::value, OverlayType == bg::overlay_difference ? ! bg::detail::overlay::do_reverse::value>::value : bg::detail::overlay::do_reverse::value>::value, bg::detail::overlay::do_reverse::value>::value, geometry_out, OverlayType > overlay; typedef typename bg::strategy::intersection::services::default_strategy < typename bg::cs_tag::type >::type strategy_type; strategy_type strategy; typedef typename bg::rescale_overlay_policy_type < Geometry, Geometry >::type rescale_policy_type; rescale_policy_type robust_policy = bg::get_rescale_policy(g1, g2); Geometry result; bg::detail::overlay::overlay_null_visitor visitor; overlay::apply(g1, g2, robust_policy, std::back_inserter(result), strategy, visitor); const double detected_area = bg::area(result); if (std::fabs(detected_area - expected_area) > 0.01) { if (do_output) { std::cout << "ERROR: " << caseid << std::setprecision(18) << " detected=" << detected_area << " expected=" << expected_area << std::endl << " " << bg::wkt(g1) << std::endl << " " << bg::wkt(g2) << std::endl; } return false; } return true; } template void update(Ring& ring, double x, double y, std::size_t index) { if (index >= ring.size()) { return; } bg::set<0>(ring[index], bg::get<0>(ring[index]) + x); bg::set<1>(ring[index], bg::get<1>(ring[index]) + y); if (index == 0) { ring.back() = ring.front(); } } template std::size_t test_case(std::size_t& error_count, std::size_t case_index, std::size_t i, std::size_t j, std::size_t min_vertex_index, std::size_t max_vertex_index, double x, double y, double expectation, MultiPolygon const& poly1, MultiPolygon const& poly2, bool do_output) { std::size_t n = 0; for (std::size_t k = min_vertex_index; k <= max_vertex_index; k++, ++n) { MultiPolygon poly2_adapted = poly2; switch (case_index) { case 2 : update(bg::interior_rings(poly2_adapted.front()).front(), x, y, k); break; default : update(bg::exterior_ring(poly2_adapted.front()), x, y, k); break; } std::ostringstream out; out << "case_" << i << "_" << j << "_" << k; if (! test_overlay(out.str(), poly1, poly2_adapted, expectation, do_output)) { if (error_count == 0) { // First failure is always reported test_overlay(out.str(), poly1, poly2_adapted, expectation, true); } error_count++; } } return n; } template std::size_t test_all(std::size_t case_index, std::size_t min_vertex_index, std::size_t max_vertex_index, double expectation, bool do_output) { typedef bg::model::point point_type; typedef bg::model::polygon polygon; typedef bg::model::multi_polygon multi_polygon; const std::string& first = case_a[0]; const std::string& second = case_index == 1 ? case_a[1] : case_index == 2 ? case_b[1] : case_index == 3 ? case_c[1] : ""; multi_polygon poly1; bg::read_wkt(first, poly1); multi_polygon poly2; bg::read_wkt(second, poly2); std::size_t error_count = 0; std::size_t n = 0; for (double factor = 1.0; factor > 1.0e-10; factor /= 10.0) { std::size_t i = 0; double const bound = 1.0e-5 * factor; double const step = 1.0e-6 * factor; for (double x = -bound; x <= bound; x += step, ++i) { std::size_t j = 0; for (double y = -bound; y <= bound; y += step, ++j, ++n) { n += test_case(error_count, case_index, i, j, min_vertex_index, max_vertex_index, x, y, expectation, poly1, poly2, do_output); } } } std::cout << case_index << " #cases: " << n << " #errors: " << error_count << std::endl; BOOST_CHECK_EQUAL(error_count, 0u); return error_count; } int test_main(int argc, char** argv) { typedef double coordinate_type; const double factor = argc > 1 ? atof(argv[1]) : 2.0; const bool do_output = argc > 2 && atol(argv[2]) == 1; std::cout << std::endl << " -> TESTING " << string_from_type::name() << " " << factor << std::endl; test_all(1, 0, 3, 22.0, do_output); test_all(2, 0, 3, 73.0, do_output); test_all(3, 1, 2, 2.0, do_output); test_all(3, 1, 2, 14.0, do_output); return 0; }