From 0b4bfe70ac0706be4fe2c8640255d37e481fe707 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 29 Sep 2023 13:06:44 +0200 Subject: [PATCH] [experimental] update buffer and test with partition version with lambdas --- .../detail/buffer/buffer_policies.hpp | 73 -------- .../buffer/buffered_piece_collection.hpp | 132 +++++++++++--- .../buffer/turn_in_original_visitor.hpp | 59 ------ .../algorithms/detail/partition_lambda.hpp | 148 +++++++++++++++ test/robustness/within/Jamfile | 17 ++ test/robustness/within/partition_within.cpp | 168 ++++++------------ 6 files changed, 330 insertions(+), 267 deletions(-) create mode 100644 include/boost/geometry/algorithms/detail/partition_lambda.hpp create mode 100644 test/robustness/within/Jamfile diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp index e75532c3f3..bb386a2562 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp @@ -191,61 +191,6 @@ struct buffer_less } }; -template -struct piece_get_box -{ - explicit piece_get_box(Strategy const& strategy) - : m_strategy(strategy) - {} - - template - inline void apply(Box& total, Piece const& piece) const - { - assert_coordinate_type_equal(total, piece.m_piece_border.m_envelope); - - if (piece.m_piece_border.m_has_envelope) - { - geometry::expand(total, piece.m_piece_border.m_envelope, - m_strategy); - } - } - - Strategy const& m_strategy; -}; - -template -struct piece_overlaps_box -{ - explicit piece_overlaps_box(Strategy const& strategy) - : m_strategy(strategy) - {} - - template - inline bool apply(Box const& box, Piece const& piece) const - { - assert_coordinate_type_equal(box, piece.m_piece_border.m_envelope); - - if (piece.type == strategy::buffer::buffered_flat_end - || piece.type == strategy::buffer::buffered_concave) - { - // Turns cannot be inside a flat end (though they can be on border) - // Neither we need to check if they are inside concave helper pieces - - // Skip all pieces not used as soon as possible - return false; - } - if (! piece.m_piece_border.m_has_envelope) - { - return false; - } - - return ! geometry::detail::disjoint::disjoint_box_box(box, piece.m_piece_border.m_envelope, - m_strategy); - } - - Strategy const& m_strategy; -}; - template struct turn_get_box { @@ -263,24 +208,6 @@ struct turn_get_box Strategy const& m_strategy; }; -template -struct turn_overlaps_box -{ - explicit turn_overlaps_box(Strategy const& strategy) - : m_strategy(strategy) - {} - - template - inline bool apply(Box const& box, Turn const& turn) const - { - assert_coordinate_type_equal(turn.point, box); - return ! geometry::detail::disjoint::disjoint_point_box(turn.point, box, - m_strategy); - } - - Strategy const& m_strategy; -}; - struct enriched_map_buffer_include_policy { template diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index 66943b303c..65faac16de 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -54,7 +54,7 @@ #include #include #include -#include +#include #include #include @@ -358,22 +358,59 @@ struct buffered_piece_collection // Check if a turn is inside any of the originals inline void check_turn_in_original() { + auto const& strategy = m_strategy; + + struct specific_options : experimental::partition_options + { + struct include_turn_policy + { + static inline bool apply(buffer_turn_info_type const& turn) + { + return turn.is_turn_traversable && ! turn.within_original; + } + }; + + using include_policy1 = include_turn_policy; + }; + turn_in_original_visitor < turn_vector_type, Strategy > visitor(m_turns, m_strategy); - geometry::partition + // Partition over the turns and original rings, visiting + // all turns located in an original and changing the turn's + // "count_in_original" and "within_original" values + experimental::partition < - box_type, - include_turn_policy, - detail::partition::include_all_policy - >::apply(m_turns, original_rings, visitor, - turn_get_box(m_strategy), - turn_in_original_overlaps_box(m_strategy), - original_get_box(m_strategy), - original_overlaps_box(m_strategy)); + box_type + >(m_turns, original_rings, + [&strategy](auto& box, auto const& turn) + { + geometry::expand(box, turn.point, strategy); + }, + [&strategy](auto& box, auto const& turn) + { + return ! geometry::detail::disjoint::disjoint_point_box(turn.point, + box, strategy); + }, + [&strategy](auto& box, auto const& original) + { + geometry::expand(box, original.m_box, strategy); + }, + [&strategy](auto& box, auto const& original) + { + return ! detail::disjoint::disjoint_box_box(box, + original.m_box, strategy); + }, + [&visitor](auto const& turn, auto const& original) + { + return visitor.apply(turn, original); + }, + [](auto const&, int) {}, + specific_options() + ); bool const deflate = m_distance_strategy.negative(); @@ -447,6 +484,8 @@ struct buffered_piece_collection { update_piece_administration(); + auto const& strategy = m_strategy; + { // Calculate the turns piece_turn_visitor @@ -461,32 +500,75 @@ struct buffered_piece_collection detail::sectionalize::enlarge_sections(monotonic_sections, m_strategy); - geometry::partition - < - robust_box_type - >::apply(monotonic_sections, visitor, - detail::section::get_section_box(m_strategy), - detail::section::overlaps_section_box(m_strategy)); + experimental::partition(monotonic_sections, + [&strategy](auto& box, auto const& section) + { + geometry::expand(box, section.bounding_box, strategy); + }, + [&strategy](auto const& box, auto const& section) + { + return ! detail::disjoint::disjoint_box_box(box, + section.bounding_box, strategy); + }, + [&visitor](auto const& section1, auto const& section2) + { + return visitor.apply(section1, section2); + } + ); } + update_turn_administration(); { - // Check if turns are inside pieces turn_in_piece_visitor < typename geometry::cs_tag::type, turn_vector_type, piece_vector_type, DistanceStrategy, Strategy > visitor(m_turns, m_pieces, m_distance_strategy, m_strategy); - geometry::partition - < - box_type - >::apply(m_turns, m_pieces, visitor, - turn_get_box(m_strategy), - turn_overlaps_box(m_strategy), - piece_get_box(m_strategy), - piece_overlaps_box(m_strategy)); + // Partition over the turns and pieces, checking if turns are inside pieces. + experimental::partition(m_turns, m_pieces, + [&strategy](auto& box, auto const& turn) + { + geometry::expand(box, turn.point, strategy); + }, + [&strategy](auto& box, auto const& turn) + { + return ! geometry::detail::disjoint::disjoint_point_box(turn.point, + box, strategy); + }, + [&strategy](auto& box, auto const& piece) + { + if (piece.m_piece_border.m_has_envelope) + { + geometry::expand(box, piece.m_piece_border.m_envelope, strategy); + } + }, + [&strategy](auto& box, auto const& piece) + { + if (piece.type == strategy::buffer::buffered_flat_end + || piece.type == strategy::buffer::buffered_concave) + { + // Turns cannot be inside a flat end (though they can be on border) + // Neither we need to check if they are inside concave helper pieces + + // Skip all pieces not used as soon as possible + return false; + } + if (! piece.m_piece_border.m_has_envelope) + { + return false; + } + + return ! geometry::detail::disjoint::disjoint_box_box(box, + piece.m_piece_border.m_envelope, strategy); + }, + [&visitor](auto const& turn, auto const& piece) + { + return visitor.apply(turn, piece); + } + ); } } diff --git a/include/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp b/include/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp index 12fffc4c4a..2c264f93d9 100644 --- a/include/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp @@ -34,42 +34,6 @@ namespace boost { namespace geometry namespace detail { namespace buffer { - -template -struct original_get_box -{ - explicit original_get_box(Strategy const& strategy) - : m_strategy(strategy) - {} - - template - inline void apply(Box& total, Original const& original) const - { - assert_coordinate_type_equal(total, original.m_box); - geometry::expand(total, original.m_box, m_strategy); - } - - Strategy const& m_strategy; -}; - -template -struct original_overlaps_box -{ - explicit original_overlaps_box(Strategy const& strategy) - : m_strategy(strategy) - {} - - template - inline bool apply(Box const& box, Original const& original) const - { - assert_coordinate_type_equal(box, original.m_box); - return ! detail::disjoint::disjoint_box_box(box, original.m_box, - m_strategy); - } - - Strategy const& m_strategy; -}; - struct include_turn_policy { template @@ -79,29 +43,6 @@ struct include_turn_policy } }; -template -struct turn_in_original_overlaps_box -{ - explicit turn_in_original_overlaps_box(Strategy const& strategy) - : m_strategy(strategy) - {} - - template - inline bool apply(Box const& box, Turn const& turn) const - { - if (! turn.is_turn_traversable || turn.within_original) - { - // Skip all points already processed - return false; - } - - return ! geometry::detail::disjoint::disjoint_point_box( - turn.point, box, m_strategy); - } - - Strategy const& m_strategy; -}; - //! Check if specified is in range of specified iterators //! Return value of strategy (true if we can bail out) template diff --git a/include/boost/geometry/algorithms/detail/partition_lambda.hpp b/include/boost/geometry/algorithms/detail/partition_lambda.hpp new file mode 100644 index 0000000000..84eb9719f8 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/partition_lambda.hpp @@ -0,0 +1,148 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2023 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_LAMBDA_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_LAMBDA_HPP + +#include +#include + +#include + +namespace boost { namespace geometry { namespace experimental +{ + +template +struct adapt_partition_visitor +{ + F m_f; + + explicit adapt_partition_visitor(F&& f) + : m_f(std::move(f)) + {} + + template + decltype(auto) apply(Ts&& ...is) const + { + return m_f(std::forward(is)...); + } +}; + +struct partition_options +{ + // Include policy for the (first) range + using include_policy1 = detail::partition::include_all_policy; + + // Include policy for the second range + using include_policy2 = detail::partition::include_all_policy; + + // Defines the end of the iteration (as soon as range 1 or range 2 has + // these number of elements, or less, it will switch to iterative mode) + std::size_t min_elements = 16; +}; + +template +< + typename BoxType, + typename ForwardRange1, + typename ForwardRange2, + typename Options = partition_options +> +bool partition(ForwardRange1 const& forward_range1, + ForwardRange2 const& forward_range2, + const std::function + < + void(BoxType&, + typename boost::range_value::type const&) + >& expand_policy1, + const std::function + < + bool(BoxType const&, + typename boost::range_value::type const&) + >& overlaps_policy1, + const std::function + < + void(BoxType&, + typename boost::range_value::type const&) + >& expand_policy2, + const std::function + < + bool(BoxType const&, + typename boost::range_value::type const&) + >& overlaps_policy2, + const std::function + < + bool(typename boost::range_value::type const&, + typename boost::range_value::type const&) + >& visitor, + const std::function + < + void(BoxType const&, int level) + >& box_visitor = [](auto const&, int) {}, + const Options& options = {}) +{ + adapt_partition_visitor av(visitor); + adapt_partition_visitor aev1(expand_policy1); + adapt_partition_visitor aev2(expand_policy2); + adapt_partition_visitor aov1(overlaps_policy1); + adapt_partition_visitor aov2(overlaps_policy2); + adapt_partition_visitor apbv(box_visitor); + + return geometry::partition + < + BoxType, + typename Options::include_policy1, + typename Options::include_policy2 + >::apply(forward_range1, forward_range2, av, + aev1, aov1, + aev2, aov2, options.min_elements, apbv); +} + +template +< + typename BoxType, + typename ForwardRange, + typename Options = partition_options +> +bool partition(ForwardRange const& forward_range, + const std::function + < + void(BoxType&, + typename boost::range_value::type const&) + >& expand_policy, + const std::function + < + bool(BoxType const&, + typename boost::range_value::type const&) + >& overlaps_policy, + const std::function + < + bool(typename boost::range_value::type const&, + typename boost::range_value::type const&) + >& visitor, + const std::function + < + void(BoxType const&, int level) + >& box_visitor = [](auto const&, int) {}, + const Options& options = {}) +{ + adapt_partition_visitor av(visitor); + adapt_partition_visitor aev(expand_policy); + adapt_partition_visitor aov(overlaps_policy); + adapt_partition_visitor apbv(box_visitor); + + return geometry::partition + < + BoxType, + typename Options::include_policy1 + >::apply(forward_range, av, + aev, aov, options.min_elements, apbv); +} + +}}} // namespace boost::geometry::experimental + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_LAMBDA_HPP diff --git a/test/robustness/within/Jamfile b/test/robustness/within/Jamfile new file mode 100644 index 0000000000..aa967f4a94 --- /dev/null +++ b/test/robustness/within/Jamfile @@ -0,0 +1,17 @@ +# Boost.Geometry (aka GGL, Generic Geometry Library) +# Robustness Test - partition and within +# +# Copyright (c) 2023 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) + + +project partition_within + : requirements + . + static + ; + +exe partition_within : partition_within.cpp ; diff --git a/test/robustness/within/partition_within.cpp b/test/robustness/within/partition_within.cpp index f42842fa83..5614c5e29e 100644 --- a/test/robustness/within/partition_within.cpp +++ b/test/robustness/within/partition_within.cpp @@ -13,6 +13,8 @@ #include #include +#include + #if defined(TEST_WITH_SVG) # include #endif @@ -43,112 +45,41 @@ struct ring_item BOOST_GEOMETRY_REGISTER_POINT_2D(point_item, double, cs::cartesian, x, y) - -struct expand_for_point -{ - template - static inline void apply(Box& total, InputItem const& item) - { - bg::expand(total, item); - } -}; - -struct overlaps_point +#if defined(TEST_WITH_SVG) +template +void create_svg(std::size_t size, Points const& points, Rings const& rings, std::size_t index, + Box const& box, int level, + Boxes& boxes) { - template - static inline bool apply(Box const& box, InputItem const& item) - { - return ! bg::disjoint(item, box); - } -}; + std::ostringstream filename; + filename << "partition_demo_" << std::setfill('0') << std::setw(3) << index << "_" << level << ".svg"; + std::ofstream svg(filename.str()); + bg::svg_mapper mapper(svg, 800, 800); -struct expand_for_ring -{ - template - static inline void apply(Box& total, InputItem const& item) { - bg::expand(total, item.box); + point_item p; + p.x = -1; p.y = -1; mapper.add(p); + p.x = size + 1; p.y = size + 1; mapper.add(p); } -}; -struct overlaps_ring -{ - template - static inline bool apply(Box const& box, InputItem const& item) + for (auto const& item : rings) { - typename bg::strategy::disjoint::services::default_strategy - < - Box, Box - >::type strategy; - - return ! bg::detail::disjoint::disjoint_box_box(box, item.box, strategy); + mapper.map(item.ring, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:0.1"); } -}; - -struct point_in_ring_visitor -{ - std::size_t count = 0; - - template - inline bool apply(Point const& point, BoxItem const& ring_item) + for (auto const& point : points) { - if (bg::within(point, ring_item.ring)) - { - count++; - } - return true; + mapper.map(point, "fill:rgb(0,0,255);stroke:rgb(0,0,100);stroke-width:0.1", 3); } -}; -#if defined(TEST_WITH_SVG) -template -struct svg_visitor -{ - std::vector boxes; - Points const& m_points; - Rings const& m_rings; - std::size_t m_size = 0; - std::size_t m_index = 0; - - svg_visitor(std::size_t size, Points const& points, Rings const& rings) - : m_points(points) - , m_rings(rings) - , m_size(size) - {} - - inline void apply(Box const& box, int level) + for (auto const& b : boxes) { - std::ostringstream filename; - filename << "partition_demo_" << std::setfill('0') << std::setw(3) << m_index++ << "_" << level << ".svg"; - std::ofstream svg(filename.str()); - - bg::svg_mapper mapper(svg, 800, 800); - - { - point_item p; - p.x = -1; p.y = -1; mapper.add(p); - p.x = m_size + 1; p.y = m_size + 1; mapper.add(p); - } - - for (auto const& item : m_rings) - { - mapper.map(item.ring, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:0.1"); - } - for (auto const& point : m_points) - { - mapper.map(point, "fill:rgb(0,0,255);stroke:rgb(0,0,100);stroke-width:0.1", 3); - } - - for (auto const& b : boxes) - { - mapper.map(b, "fill:none;stroke-width:2;stroke:rgb(64,64,64);"); - } - mapper.map(box, "fill:none;stroke-width:4;stroke:rgb(255, 0, 0);"); - - boxes.push_back(box); + mapper.map(b, "fill:none;stroke-width:2;stroke:rgb(64,64,64);"); } -}; + mapper.map(box, "fill:none;stroke-width:4;stroke:rgb(255, 0, 0);"); + + boxes.push_back(box); +} #endif @@ -161,7 +92,7 @@ void fill_points(Collection& collection, std::size_t size, std::size_t count) std::uniform_real_distribution uniform_dist(0, size - 1); int const mean_x = uniform_dist(rde); int const mean_y = uniform_dist(rde); - + // Generate a normal distribution around these means std::seed_seq seed2{rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()}; std::mt19937 e2(seed2); @@ -302,7 +233,7 @@ void call_within(std::size_t size, std::size_t count) auto report = [&points, &rings](const char* title, auto const& start, std::size_t within_count) { auto const finish = std::chrono::steady_clock::now(); - double const elapsed + double const elapsed = std::chrono::duration_cast(finish - start).count(); std::cout << title << " time: " << std::setprecision(6) << elapsed / 1000000.0 << " ms" << std::endl; @@ -311,27 +242,44 @@ void call_within(std::size_t size, std::size_t count) return elapsed; }; - point_in_ring_visitor count_visitor; - { #if defined(TEST_WITH_SVG) - - using partition_box_visitor_type = svg_visitor, std::vector>>; - partition_box_visitor_type partition_box_visitor(size, points, rings); + std::size_t index = 0; + std::vector boxes; + auto box_visitor = [&](auto const& box, int level) + { + create_svg(size, points, rings, index++, box, level, boxes); + }; #else - using partition_box_visitor_type = bg::detail::partition::visit_no_policy; - partition_box_visitor_type partition_box_visitor; + auto box_visitor = [&](auto const& , int ) {}; #endif + std::size_t partition_count = 0; + { auto const start = std::chrono::steady_clock::now(); - bg::partition + typename bg::strategy::disjoint::services::default_strategy < - box_type, - bg::detail::partition::include_all_policy, - bg::detail::partition::include_all_policy - >::apply(points, rings, count_visitor, expand_for_point(), overlaps_point(), - expand_for_ring(), overlaps_ring(), 16, partition_box_visitor); - report("Partition", start, count_visitor.count); + box_type, box_type + >::type strategy; + + bg::experimental::partition(points, + rings, + [](auto& box, auto const& point) { bg::expand(box, point); }, + [](auto const& box, auto const& point) { return ! bg::disjoint(point, box); }, + [](auto& box, auto const& item) { bg::expand(box, item.box); }, + [&strategy](auto const& box, auto const& item) { return ! bg::disjoint(box, item.box, strategy); }, + [&partition_count](auto const& p, auto const& ri) + { + if (bg::within(p, ri.ring)) + { + partition_count++; + } + return true; + }, + box_visitor + ); + + report("Partition", start, partition_count); } { @@ -350,7 +298,7 @@ void call_within(std::size_t size, std::size_t count) } report("Quadratic loop", start, count); - if (count != count_visitor.count) + if (count != partition_count) { std::cerr << "ERROR: counts are not equal" << std::endl; }