diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index 127e4c3fb..67396123c 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -383,6 +383,7 @@ inline void buffer_point(Point const& point, Collection& collection, std::vector range_out; point_strategy.apply(point, distance_strategy, range_out); collection.add_piece(strategy::buffer::buffered_point, range_out, false); + collection.set_piece_center(point); collection.finish_ring(); } 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 d71abbf73..dbea92a37 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -127,6 +128,11 @@ struct buffered_piece_collection typedef geometry::model::ring robust_ring_type; typedef geometry::model::box robust_box_type; + typedef typename default_comparable_distance_result + < + robust_point_type + >::type robust_comparable_radius_type; + typedef typename strategy::side::services::default_strategy < typename cs_tag::type @@ -199,7 +205,6 @@ struct buffered_piece_collection // Monotonic sections of pieces around points std::vector sections; - // Robust representations // 3: complete ring robust_ring_type robust_ring; @@ -208,6 +213,27 @@ struct buffered_piece_collection robust_box_type robust_offsetted_envelope; std::vector robust_turns; // Used only in insert_rescaled_piece_turns - we might use a map instead + + robust_point_type robust_center; + robust_comparable_radius_type robust_min_comparable_radius; + robust_comparable_radius_type robust_max_comparable_radius; + + piece() + : type(strategy::buffer::piece_type_unknown) + , index(-1) + , left_index(-1) + , right_index(-1) + , last_segment_index(-1) + , offsetted_count(-1) + , is_convex(false) + , robust_min_comparable_radius(0) + , robust_max_comparable_radius(0) + { + is_monotonic_increasing[0] = false; + is_monotonic_increasing[1] = false; + is_monotonic_decreasing[0] = false; + is_monotonic_decreasing[1] = false; + } }; struct robust_original @@ -629,7 +655,6 @@ struct buffered_piece_collection current = next; ++next; } - } void determine_properties() @@ -666,7 +691,31 @@ struct buffered_piece_collection geometry::sectionalize(pc.robust_ring, detail::no_rescale_policy(), pc.sections); - // TODO (next phase) determine min/max radius + // Determine min/max radius + typedef geometry::model::referring_segment + robust_segment_type; + + typename robust_ring_type::const_iterator current = pc.robust_ring.begin(); + typename robust_ring_type::const_iterator next = current + 1; + + for (int i = 1; i < pc.offsetted_count; i++) + { + robust_segment_type s(*current, *next); + robust_comparable_radius_type const d + = geometry::comparable_distance(pc.robust_center, s); + + if (i == 1 || d < pc.robust_min_comparable_radius) + { + pc.robust_min_comparable_radius = d; + } + if (i == 1 || d > pc.robust_max_comparable_radius) + { + pc.robust_max_comparable_radius = d; + } + + current = next; + ++next; + } } inline void prepare_buffered_point_pieces() @@ -780,6 +829,13 @@ struct buffered_piece_collection } } + inline void set_piece_center(point_type const& center) + { + BOOST_ASSERT(! m_pieces.empty()); + geometry::recalculate(m_pieces.back().robust_center, center, + m_robust_policy); + } + inline void finish_ring(bool is_interior = false, bool has_interiors = false) { if (m_first_piece_index == -1) 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 abd2cbfb8..4e386ca96 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 @@ -214,6 +214,8 @@ public : boost::ignore_unused(strategy); #endif + BOOST_ASSERT(! piece.sections.empty()); + coordinate_type const point_y = geometry::get<1>(turn.robust_point); for (std::size_t s = 0; s < piece.sections.size(); s++) @@ -704,12 +706,37 @@ public: } // TODO: mutable_piece to make some on-demand preparations in analyse + Turn& mutable_turn = m_turns[turn.turn_index]; + + if (piece.type == geometry::strategy::buffer::buffered_point) + { + // Optimization for buffer around points: if distance from center + // is not between min/max radius, the result is clear + typedef typename default_comparable_distance_result + < + typename Turn::robust_point_type + >::type distance_type; + + distance_type const cd + = geometry::comparable_distance(piece.robust_center, + turn.robust_point); + + if (cd < piece.robust_min_comparable_radius) + { + mutable_turn.count_within++; + return; + } + if (cd > piece.robust_max_comparable_radius) + { + return; + } + } + analyse_result analyse_code = piece.type == geometry::strategy::buffer::buffered_point ? analyse_turn_wrt_point_piece::apply(turn, piece) : analyse_turn_wrt_piece::apply(turn, piece); - Turn& mutable_turn = m_turns[turn.turn_index]; switch(analyse_code) { case analyse_disjoint : diff --git a/include/boost/geometry/strategies/buffer.hpp b/include/boost/geometry/strategies/buffer.hpp index 7dbe03b4a..be3daefec 100644 --- a/include/boost/geometry/strategies/buffer.hpp +++ b/include/boost/geometry/strategies/buffer.hpp @@ -66,7 +66,8 @@ enum piece_type buffered_round_end, buffered_flat_end, buffered_point, - buffered_concave // always on the inside + buffered_concave, // always on the inside + piece_type_unknown }; diff --git a/test/algorithms/buffer/multi_point_buffer.cpp b/test/algorithms/buffer/multi_point_buffer.cpp index 2bfcb65b1..0e7e686c1 100644 --- a/test/algorithms/buffer/multi_point_buffer.cpp +++ b/test/algorithms/buffer/multi_point_buffer.cpp @@ -102,6 +102,9 @@ void test_many_points_per_circle() double const tolerance = 1.0; + // Area should be somewhat larger (~>) than pi*distance^2 + // 6051788: area ~> 115058122875258 + // Strategies with many points, which are (very) slow in debug mode test_with_custom_strategies( "mysql_report_2015_02_25_1_8000", @@ -131,12 +134,30 @@ void test_many_points_per_circle() 115058672871849.219, tolerance); #endif + // 5666962: area ~> 100890546298964 test_with_custom_strategies( "mysql_report_2015_02_25_2", mysql_report_2015_02_25_2, join, end_flat, distance_strategy(5666962), side_strategy, point_circle(46641), 100891031341757.344, tolerance); + // Multipoint b with large distances/many points + // Area ~> pi * 10x + test_with_custom_strategies( + "multipoint_b_50k", + multipoint_b, join, end_flat, + distance_strategy(1000000), side_strategy, point_circle(50000), + 3141871558215.26465, tolerance); + +#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_SLOW_TESTS) + // Tests optimization min/max radius + // Takes about 55 seconds in release mode + test_with_custom_strategies( + "multipoint_b_500k", + multipoint_b, join, end_flat, + distance_strategy(10000000), side_strategy, point_circle(500000), + 314162054419515.562, tolerance); +#endif } int test_main(int, char* [])