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