From f74180b0842e42e7972cb796dce6873df39b177c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sun, 1 Mar 2015 14:08:16 +0100 Subject: [PATCH] [buffer] Enhance turn-in-piece for pieces around points. They now make use of monotonic sections, and get special treatment because there are no helper segments, basically the whole procedure is different This can make buffers around points 3 times faster (if the buffered ring contains many points) --- .../buffer/buffered_piece_collection.hpp | 34 +++- .../detail/buffer/turn_in_piece_visitor.hpp | 164 ++++++++++++------ test/algorithms/buffer/multi_point_buffer.cpp | 4 +- 3 files changed, 146 insertions(+), 56 deletions(-) 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 ea045a267..a501e3f19 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -168,6 +168,9 @@ struct buffered_piece_collection struct piece { + typedef robust_ring_type piece_robust_ring_type; + typedef geometry::section section_type; + strategy::buffer::piece_type type; int index; @@ -191,6 +194,10 @@ struct buffered_piece_collection bool is_monotonic_increasing[2]; // 0=x, 1=y bool is_monotonic_decreasing[2]; // 0=x, 1=y + // Monotonic sections of pieces around points + std::vector sections; + + // Robust representations // 3: complete ring robust_ring_type robust_ring; @@ -199,8 +206,6 @@ 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 - - typedef robust_ring_type piece_robust_ring_type; }; struct robust_original @@ -647,6 +652,29 @@ struct buffered_piece_collection } } + inline void prepare_buffered_point_piece(piece& pc) + { + // create monotonic sections in y-dimension + typedef boost::mpl::vector_c dimensions; + geometry::sectionalize(pc.robust_ring, + detail::no_rescale_policy(), pc.sections); + + // TODO (next phase) determine min/max radius + } + + inline void prepare_buffered_point_pieces() + { + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + if (it->type == geometry::strategy::buffer::buffered_point) + { + prepare_buffered_point_piece(*it); + } + } + } + inline void get_turns() { for(typename boost::range_iterator::type it @@ -681,6 +709,8 @@ struct buffered_piece_collection determine_properties(); + prepare_buffered_point_pieces(); + { // Check if it is inside any of the pieces turn_in_piece_visitor 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 7eaca10e4..a7731549d 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 @@ -90,74 +90,134 @@ enum analyse_result analyse_near_offsetted }; -class analyse_turn_wrt_piece +template +inline bool in_box(Point const& previous, + Point const& current, Point const& point) { - template - static inline bool in_box(Point const& previous, - Point const& current, Point const& point) - { - // Get its box (TODO: this can be prepared-on-demand later) - typedef geometry::model::box box_type; - box_type box; - geometry::assign_inverse(box); - geometry::expand(box, previous); - geometry::expand(box, current); + // Get its box (TODO: this can be prepared-on-demand later) + typedef geometry::model::box box_type; + box_type box; + geometry::assign_inverse(box); + geometry::expand(box, previous); + geometry::expand(box, current); - return geometry::covered_by(point, box); + return geometry::covered_by(point, box); +} + +template +inline analyse_result check_segment(Point const& previous, + Point const& current, Turn const& turn, + bool from_monotonic) +{ + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + typedef typename geometry::coordinate_type::type coordinate_type; + + coordinate_type const twice_area + = side_strategy::template side_value + < + coordinate_type, + coordinate_type + >(previous, current, turn.robust_point); + + if (twice_area == 0) + { + // Collinear, only on segment if it is covered by its bbox + if (in_box(previous, current, turn.robust_point)) + { + return analyse_on_offsetted; + } + } + else if (twice_area < 0) + { + // It is in the triangle right-of the segment where the + // segment is the hypothenusa. Check if it is close + // (within rounding-area) + if (twice_area * twice_area < geometry::comparable_distance(previous, current) + && in_box(previous, current, turn.robust_point)) + { + return analyse_near_offsetted; + } + else if (from_monotonic) + { + return analyse_within; + } + } + else if (twice_area > 0 && from_monotonic) + { + // Left of segment + return analyse_disjoint; } - template - static inline analyse_result check_segment(Point const& previous, - Point const& current, Turn const& turn, - bool from_monotonic) + // Not monotonic, on left or right side: continue analysing + return analyse_continue; +} + + +class analyse_turn_wrt_point_piece +{ +public : + template + static inline analyse_result apply(Turn const& turn, Piece const& piece) { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - typedef typename geometry::coordinate_type::type coordinate_type; + typedef typename Piece::section_type section_type; + typedef typename Turn::robust_point_type point_type; + typedef typename geometry::coordinate_type::type coordinate_type; - coordinate_type const twice_area - = side_strategy::template side_value - < - coordinate_type, - coordinate_type - >(previous, current, turn.robust_point); + coordinate_type const point_y = geometry::get<1>(turn.robust_point); - if (twice_area == 0) + typedef strategy::within::winding strategy_type; + + typename strategy_type::state_type state; + strategy_type strategy; + + for (std::size_t s = 0; s < piece.sections.size(); s++) { - // Collinear, only on segment if it is covered by its bbox - if (in_box(previous, current, turn.robust_point)) + section_type const& section = piece.sections[s]; + // If point within vertical range of monotonic section: + if (! section.duplicate + && section.begin_index < section.end_index + && point_y >= geometry::get(section.bounding_box) - 1 + && point_y <= geometry::get(section.bounding_box) + 1) { - return analyse_on_offsetted; + for (int i = section.begin_index + 1; i <= section.end_index; i++) + { + point_type const& previous = piece.robust_ring[i - 1]; + point_type const& current = piece.robust_ring[i]; + + analyse_result code = check_segment(previous, current, turn, false); + if (code != analyse_continue) + { + return code; + } + + // Get the state (to determine it is within), we don't have + // to cover the on-segment case (covered above) + strategy.apply(turn.robust_point, previous, current, state); + } } } - else if (twice_area < 0) + + int const code = strategy.result(state); + if (code == 1) { - // It is in the triangle right-of the segment where the - // segment is the hypothenusa. Check if it is close - // (within rounding-area) - if (twice_area * twice_area < geometry::comparable_distance(previous, current) - && in_box(previous, current, turn.robust_point)) - { - return analyse_near_offsetted; - } - else if (from_monotonic) - { - return analyse_within; - } + return analyse_within; } - else if (twice_area > 0 && from_monotonic) + else if (code == -1) { - // Left of segment return analyse_disjoint; } - // Not monotonic, on left or right side: continue analysing - return analyse_continue; + // Should normally not occur - on-segment is covered + return analyse_unknown; } +}; +class analyse_turn_wrt_piece +{ template static inline analyse_result check_helper_segment(Point const& s1, Point const& s2, Turn const& turn, @@ -389,7 +449,7 @@ public : } } - return analyse_unknown; + return analyse_unknown; } }; @@ -466,8 +526,10 @@ public: } // TODO: mutable_piece to make some on-demand preparations in analyse - analyse_result analyse_code - = analyse_turn_wrt_piece::apply(turn, piece); + 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) diff --git a/test/algorithms/buffer/multi_point_buffer.cpp b/test/algorithms/buffer/multi_point_buffer.cpp index ff1b185f3..5c4cbd6a7 100644 --- a/test/algorithms/buffer/multi_point_buffer.cpp +++ b/test/algorithms/buffer/multi_point_buffer.cpp @@ -119,14 +119,12 @@ void test_many_points_per_circle() distance_strategy(6051788), side_strategy, point_circle(83585), 115058672785611.219, tolerance); -#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_SLOW_TESTS) - // Takes about 60 seconds in release mode + // Takes about 20 seconds in release mode test_with_custom_strategies( "mysql_report_2015_02_25_1_250k", mysql_report_2015_02_25_1, join_miter, end_flat, distance_strategy(6051788), side_strategy, point_circle(250000), 115058672880671.531, tolerance); -#endif #if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) // Takes too long, TODO improve turn_in_piece_visitor