diff --git a/.circleci/config.yml b/.circleci/config.yml index 8af7fca8a..7df421c70 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -407,15 +407,15 @@ jobs: root: ~/project paths: - shared-coverage/index_rtree_exceptions - index_varray: + index_detail: <<: *config steps: - *attach_workspace - - run: ./$BOOST_DIR/libs/geometry/.circleci/run_test.sh index_varray index/test//boost-geometry-index-varray + - run: ./$BOOST_DIR/libs/geometry/.circleci/run_test.sh index_detail index/test//boost-geometry-index-detail - persist_to_workspace: root: ~/project paths: - - shared-coverage/index_varray + - shared-coverage/index_detail coverage: <<: *config @@ -494,7 +494,7 @@ requires_4: &requires_4 - index_rtree - index_rtree_b2d - index_rtree_exceptions - - index_varray + - index_detail - cs_undefined only_master_develop: &only_master_develop @@ -630,7 +630,7 @@ workflows: - index_rtree_exceptions: <<: *requires_3 <<: *only_master_develop - - index_varray: + - index_detail: <<: *requires_3 <<: *only_master_develop - cs_undefined: diff --git a/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp b/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp index f51bd2900..6e66d2283 100644 --- a/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp @@ -175,7 +175,7 @@ public: // unit tests of hard cases start to fail (5 in multi_polygon) // But it is acknowlegded that such a threshold depends on the // scale of the input. - if (state.m_min_distance > 1.0e-5 || ! state.m_close_to_offset) + if (state.m_min_distance > 1.0e-4 || ! state.m_close_to_offset) { Turn& mutable_turn = m_turns[turn.turn_index]; mutable_turn.is_turn_traversable = false; diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index 269e9b476..966efc3f8 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -79,7 +79,8 @@ struct direction_code_impl } calc_t const sv = arithmetic::side_value(line, point); - return sv == 0 ? 0 : sv > 0 ? 1 : -1; + static calc_t const zero = 0; + return sv == zero ? 0 : sv > zero ? 1 : -1; } }; diff --git a/include/boost/geometry/algorithms/detail/overlay/approximately_equals.hpp b/include/boost/geometry/algorithms/detail/overlay/approximately_equals.hpp index d28a04ee8..1f41085dc 100644 --- a/include/boost/geometry/algorithms/detail/overlay/approximately_equals.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/approximately_equals.hpp @@ -23,7 +23,7 @@ namespace detail { namespace overlay template inline bool approximately_equals(Point1 const& a, Point2 const& b, - E const& multiplier) + E const& epsilon_multiplier) { using coor_t = typename select_coordinate_type::type; using calc_t = typename geometry::select_most_precise::type; @@ -34,7 +34,7 @@ inline bool approximately_equals(Point1 const& a, Point2 const& b, calc_t const& b1 = geometry::get<1>(b); math::detail::equals_factor_policy policy(a0, b0, a1, b1); - policy.factor *= multiplier; + policy.multiply_epsilon(epsilon_multiplier); return math::detail::equals_by_policy(a0, b0, policy) && math::detail::equals_by_policy(a1, b1, policy); diff --git a/include/boost/geometry/algorithms/detail/overlay/get_clusters.hpp b/include/boost/geometry/algorithms/detail/overlay/get_clusters.hpp index 1e612d657..bb6077ad8 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_clusters.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_clusters.hpp @@ -39,7 +39,8 @@ struct sweep_equal_policy static inline bool equals(P const& p1, P const& p2) { // Points within a kilo epsilon are considered as equal - return approximately_equals(p1, p2, 1000.0); + using coor_t = typename coordinate_type

::type; + return approximately_equals(p1, p2, coor_t(1000)); } template @@ -47,7 +48,8 @@ struct sweep_equal_policy { // This threshold is an arbitrary value // as long as it is than the used kilo-epsilon - return value > 1.0e-3; + T const limit = T(1) / T(1000); + return value > limit; } }; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index fcd3635fd..427ac0ee1 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -128,7 +128,8 @@ struct base_turn_handler } auto const dm = get_distance_measure(range_p.at(range_index), range_p.at(range_index + 1), range_q.at(point_index)); - return dm.measure == 0 ? 0 : dm.measure > 0 ? 1 : -1; + static decltype(dm.measure) const zero = 0; + return dm.measure == zero ? 0 : dm.measure > zero ? 1 : -1; } template diff --git a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp index 49e676186..9656285ab 100644 --- a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp @@ -316,7 +316,15 @@ public : // then take a point (or more) further back. // The limit of offset avoids theoretical infinite loops. // In practice it currently walks max 1 point back in all cases. - double const tolerance = 1.0e9; + // Use the coordinate type, but if it is too small (e.g. std::int16), use a double + using ct_type = typename geometry::select_most_precise + < + typename geometry::coordinate_type::type, + double + >::type; + + ct_type const tolerance = 1000000000; + int offset = 0; while (approximately_equals(point_from, turn.point, tolerance) && offset > -10) diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp index e98536e3f..8d230573c 100644 --- a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -783,22 +783,24 @@ inline void enlarge_sections(Sections& sections, Strategy const&) // It makes section a tiny bit too large, which might cause (a small number) // of more comparisons - for (typename boost::range_iterator::type it = boost::begin(sections); - it != boost::end(sections); - ++it) + for (auto& section : sections) { #if defined(BOOST_GEOMETRY_USE_RESCALING) detail::sectionalize::expand_by_epsilon < typename Strategy::cs_tag - >::apply(it->bounding_box); + >::apply(section.bounding_box); #else // Expand the box to avoid missing any intersection. The amount is // should be larger than epsilon. About the value itself: the smaller // it is, the higher the risk to miss intersections. The larger it is, // the more comparisons are made. So it should be on the high side. - detail::buffer::buffer_box(it->bounding_box, 0.001, it->bounding_box); + using gt = decltype(section.bounding_box); + using ct = typename geometry::coordinate_type::type; + ct const tolerance = ct(1) / ct(1000); + detail::buffer::buffer_box(section.bounding_box, tolerance, + section.bounding_box); #endif } } diff --git a/include/boost/geometry/policies/robustness/segment_ratio.hpp b/include/boost/geometry/policies/robustness/segment_ratio.hpp index b4797f39c..4a63eb08b 100644 --- a/include/boost/geometry/policies/robustness/segment_ratio.hpp +++ b/include/boost/geometry/policies/robustness/segment_ratio.hpp @@ -119,7 +119,8 @@ struct possibly_collinear template static inline bool apply(Ratio const& ratio, Threshold) { - return ratio.denominator() == 0; + static Type const zero = 0; + return ratio.denominator() == zero; } }; @@ -216,14 +217,14 @@ public : { // Minimal normalization // 1/-4 => -1/4, -1/-4 => 1/4 - if (m_denominator < 0) + if (m_denominator < zero_instance()) { m_numerator = -m_numerator; m_denominator = -m_denominator; } m_approximation = - m_denominator == 0 ? 0 + m_denominator == zero_instance() ? 0 : ( boost::numeric_cast(m_numerator) * scale() / boost::numeric_cast(m_denominator) @@ -235,12 +236,12 @@ public : inline bool on_segment() const { // e.g. 0/4 or 4/4 or 2/4 - return m_numerator >= 0 && m_numerator <= m_denominator; + return m_numerator >= zero_instance() && m_numerator <= m_denominator; } inline bool in_segment() const { // e.g. 1/4 - return m_numerator > 0 && m_numerator < m_denominator; + return m_numerator > zero_instance() && m_numerator < m_denominator; } inline bool on_end() const { @@ -250,7 +251,7 @@ public : inline bool left() const { // e.g. -1/4 - return m_numerator < 0; + return m_numerator < zero_instance(); } inline bool right() const { @@ -336,6 +337,11 @@ private : static floating_point_type const fp_scale{1000000.0}; return fp_scale; } + + static inline Type zero_instance() + { + return 0; + } }; diff --git a/include/boost/geometry/strategy/cartesian/side_non_robust.hpp b/include/boost/geometry/strategy/cartesian/side_non_robust.hpp index 9400f9bd6..25074cc89 100644 --- a/include/boost/geometry/strategy/cartesian/side_non_robust.hpp +++ b/include/boost/geometry/strategy/cartesian/side_non_robust.hpp @@ -87,7 +87,7 @@ public: ); PromotedType const zero = PromotedType(); - return sv == 0 ? 0 : sv > zero ? 1 : -1; + return sv == zero ? 0 : sv > zero ? 1 : -1; } }; diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp index fc9d7a3be..c39341a20 100644 --- a/include/boost/geometry/util/math.hpp +++ b/include/boost/geometry/util/math.hpp @@ -139,6 +139,12 @@ struct equals_factor_policy return factor; } + template + void multiply_epsilon(E const& multiplier) + { + factor *= multiplier; + } + T factor; }; @@ -153,6 +159,8 @@ struct equals_factor_policy { return T(1); } + + void multiply_epsilon(T const& ) {} }; template ; + test_one + < + multi_linestring_type, polygon_ccw + >("mysql_33353637_macos11", + "MULTILINESTRING((0 10,10 0),(10 0,0 0),(0 0,10 10))", + bg::strategy::buffer::join_miter(10), + end_round32, + 1, 0, 35307.0646, + 100.0); + } } diff --git a/test/algorithms/detail/approximately_equals.cpp b/test/algorithms/detail/approximately_equals.cpp index 2d8b1ac3a..39e9ec39a 100644 --- a/test/algorithms/detail/approximately_equals.cpp +++ b/test/algorithms/detail/approximately_equals.cpp @@ -96,5 +96,10 @@ int test_main(int, char* []) test_all>(m, 23); test_all>(m, 23); + // MP is not a floating point type, therefore approximately_equal + // is behaves as exact and returns only true if they are identical. + // This takes 334 steps (then "d" above is 0.0) + test_all>(m, 334); + return 0; } diff --git a/test/algorithms/overlay/get_clusters.cpp b/test/algorithms/overlay/get_clusters.cpp index 75f1bbc7e..f7b8a535d 100644 --- a/test/algorithms/overlay/get_clusters.cpp +++ b/test/algorithms/overlay/get_clusters.cpp @@ -93,7 +93,7 @@ int test_main(int, char* []) using fp = bg::model::point; using dp = bg::model::point; using ep = bg::model::point; - test_get_clusters(1.0e-8f); + test_get_clusters(1.0e-4); test_get_clusters(1.0e-13); test_get_clusters(1.0e-16); return 0; diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 3b1e4c342..701c1f4e9 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -26,6 +26,27 @@ static std::string case_multi_simplex[2] = "MULTIPOLYGON(((3 0,0 3,4 5,3 0)))" }; +// To support exact behavior in integer coordinates (rectangular and diagonal) +static std::string case_multi_rectangular[2] = +{ + "MULTIPOLYGON(((100 100,100 200,200 200,200 100,100 100))," + "((300 100,300 200,400 200,400 100,300 100)," + "(325 125,375 125,375 175,325 175,325 125)))", + "MULTIPOLYGON(((150 50,150 150,350 150,350 50,150 50)))" +}; + +static std::string case_multi_diagonal[2] = +{ + "MULTIPOLYGON(((40 0,0 40,40 80,80 40,40 0),(50 20,60 30,50 40,40 30,50 20)))", + "MULTIPOLYGON(((80 0,40 40,80 80,120 40,80 0),(80 30,90 40,80 50,70 40,80 30)))" +}; + +static std::string case_multi_hard[2] = +{ + "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))", + "MULTIPOLYGON(((2 7,4 7,4 2.99959993362426758,2 3,2 7)))" +}; + // To mix multi/single static std::string case_single_simplex = "POLYGON((3 0,0 3,4 5,3 0))"; diff --git a/test/algorithms/set_operations/union/Jamfile b/test/algorithms/set_operations/union/Jamfile index 8d184ffa9..d9b1c04ee 100644 --- a/test/algorithms/set_operations/union/Jamfile +++ b/test/algorithms/set_operations/union/Jamfile @@ -24,5 +24,6 @@ test-suite boost-geometry-algorithms-union [ run union_multi.cpp : : : BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE : algorithms_union_multi ] [ run union_pl_pl.cpp : : : : algorithms_union_pl_pl ] - [ run union_tupled.cpp : : : : algorithms_union_tupled ] + [ run union_tupled.cpp : : : : algorithms_union_tupled ] + [ run union_other_types.cpp : : : : algorithms_union_other_types ] ; diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index e80c72da3..29f421ebb 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -201,6 +201,10 @@ void test_areal() TEST_UNION(case_140_multi, 2, 1, -1, 64.953); TEST_UNION(case_141_multi, 1, 0, -1, 100.0); + TEST_UNION(case_multi_rectangular, 1, 1, -1, 33125); + TEST_UNION(case_multi_diagonal, 1, 2, -1, 5350); + TEST_UNION(case_multi_hard, 1, 0, -1, 22); + test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], 1, 1, 16, 97.0); @@ -409,7 +413,7 @@ void test_areal() ticket_12118[0], ticket_12118[1], 1, -1, 27, 2221.38713); -#if defined(BOOST_GEOMETRY_TEST_FAILURES) || ! defined(BOOST_GEOMETRY_USE_RESCALING) +#if ! defined(BOOST_GEOMETRY_USE_RESCALING) || defined(BOOST_GEOMETRY_TEST_FAILURES) // No output if rescaling is done test_one("ticket_12125", ticket_12125[0], ticket_12125[1], diff --git a/test/algorithms/set_operations/union/union_other_types.cpp b/test/algorithms/set_operations/union/union_other_types.cpp new file mode 100644 index 000000000..d32f8f62c --- /dev/null +++ b/test/algorithms/set_operations/union/union_other_types.cpp @@ -0,0 +1,154 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2021 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) + +#define BOOST_GEOMETRY_NO_ROBUSTNESS + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +enum class exclude { all, rectangular, diagonal, hard, fp }; + +template +void test_one(std::string const& case_id, + std::string const& wkt1, std::string const& wkt2, + bool debug, + Expected const& expected_area, + Expected const& expected_max = -1) +{ + using coor_t = typename bg::coordinate_type::type; + Geometry g1, g2, clip; + + bg::read_wkt(wkt1, g1); + bg::read_wkt(wkt2, g2); + + bg::correct(g1); + bg::correct(g2); + + bg::union_(g1, g2, clip); + + auto const area = bg::area(clip); + if (debug) + { + std::cout << "AREA: " << std::setprecision(64) << area + << " expected " << expected_area + << " types coordinate " << string_from_type::name() + << " area " << typeid(decltype(area)).name() + << " expected " << typeid(Expected).name() + << " size " << sizeof(coor_t) + << std::endl; + } + + // Check areas, they always have to be specified in integer for this test + // and therefore the checking (including a tolerance) is different + bool const ok = expected_max == -1 + ? bg::math::equals(area, expected_area) + : bg::math::larger_or_equals(area, expected_area) + && bg::math::smaller_or_equals(area, expected_max); + BOOST_CHECK_MESSAGE(ok, + "union: " << case_id + << " area: expected: " << expected_area + << " detected: " << area + << " type: " << (string_from_type::name()) + << " (" << (typeid(coor_t).name()) << ")"); +} + +template +void test_areal(std::set const& exclude = {}, bool debug = false) +{ + using polygon = bg::model::polygon; + using multi_polygon = bg::model::multi_polygon; + + // Intended tests: only 3: + // - simple case having only horizontal/vertical lines ("rectangular") + // - simple case on integer grid but also having diagonals ("diagonal") + // - case going wrong for ("hard") + + if (exclude.count(exclude::rectangular) + + exclude.count(exclude::all) == 0) + { + test_one("case_multi_rectangular", + case_multi_rectangular[0], case_multi_rectangular[1], debug, 33125); + } + if (exclude.count(exclude::diagonal) + + exclude.count(exclude::all) == 0) + { + test_one("case_multi_diagonal", + case_multi_diagonal[0], case_multi_diagonal[1], debug, 5350); + } + if (exclude.count(exclude::hard) + + exclude.count(exclude::fp) + + exclude.count(exclude::all) == 0) + { + test_one("case_multi_hard", + case_multi_hard[0], case_multi_hard[1], debug, 21, 23); + } +} + +int test_main(int, char* []) +{ + namespace bm = boost::multiprecision; + + using bg::model::d2::point_xy; + + // Standard floating point types + test_areal>({exclude::hard}); + test_areal>({}); + test_areal>({}); + + // Standard integer types + test_areal>({exclude::fp}); + test_areal>({exclude::fp}); + test_areal>({exclude::fp}); + + // Boost multi precision (integer) + test_areal>({exclude::fp}); + test_areal>({exclude::fp}); + + // Boost multi precision (floating point) + test_areal>>>(); + test_areal>>>(); + test_areal>>>(); + test_areal>>>(); + + test_areal>>>({}); + + // Boost multi precision (rational) + test_areal>({exclude::fp}); + test_areal>({exclude::fp}); + + // Boost multi precision float128 wrapper, is currently NOT supported + // and it is limited to certain compilers anyway + // test_areal>(); + + // Boost rational (tests compilation) + // (the rectangular case is correct; other input might give wrong results) + // The int16 version throws a exception + test_areal>>({exclude::all}); + test_areal>>({exclude::fp}); + test_areal>>({exclude::fp}); + + return 0; +} diff --git a/test/robustness/overlay/areal_areal/general_intersection_precision.cpp b/test/robustness/overlay/areal_areal/general_intersection_precision.cpp index dc7527ae9..f03137a28 100644 --- a/test/robustness/overlay/areal_areal/general_intersection_precision.cpp +++ b/test/robustness/overlay/areal_areal/general_intersection_precision.cpp @@ -38,6 +38,7 @@ static std::string case_b[2] = "MULTIPOLYGON(((-1 -1,-1 8,8 8,8 -1,-1 -1),(2 7,2 3,4 3,4 7,2 7)))" }; +// Union should deliver 14.0 static std::string case_c[2] = { "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))", @@ -49,7 +50,7 @@ struct test_settings bool verbose{false}; bool do_output{false}; - // Settings currently not modifiable, and still giving quite some errors + // Settings currently not modifiable double start_bound{1.0e-2}; double step_factor{50.0}; // on each side -> 100 steps per factor int max_factor{10000}; @@ -61,7 +62,6 @@ bool test_overlay(std::string const& caseid, double expected_area, test_settings const& settings) { - typedef typename boost::range_value::type geometry_out; typedef bg::detail::overlay::overlay < @@ -96,8 +96,8 @@ bool test_overlay(std::string const& caseid, 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) + auto const detected_area = bg::area(result); + if (std::fabs(detected_area - expected_area) > 0.1) { if (settings.do_output) { diff --git a/test/string_from_type.hpp b/test/string_from_type.hpp index 6d02b8b15..5a8bdaff1 100644 --- a/test/string_from_type.hpp +++ b/test/string_from_type.hpp @@ -24,7 +24,8 @@ #include template -struct string_from_type {}; +struct string_from_type +{ static std::string name() { return "?"; } }; template <> struct string_from_type { static std::string name() { return "v"; } }; @@ -47,7 +48,8 @@ template <> struct string_from_type template <> struct string_from_type { static std::string name() { return "l"; } }; -template <> struct string_from_type +template +struct string_from_type> { static std::string name() { return "m"; } }; // this is what g++ and clang++ use