From 8a417e6fa9127fe9f88db22dd1f4e6eeb2c25432 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 7 Jun 2014 12:38:02 +0200 Subject: [PATCH] [buffer] Classify turn-points using rescaled rings This makes side_on_convex_range and intersection_side redundant It fixes all but one (p20) remaining cases for polygons --- .../buffer/multi_polygon_buffer.cpp | 2 - .../test/algorithms/buffer/polygon_buffer.cpp | 8 - .../algorithms/buffer/buffer_policies.hpp | 22 +- .../buffer/buffered_piece_collection.hpp | 280 ++++++++++-------- .../buffered_piece_collection_with_mapper.hpp | 5 +- 5 files changed, 170 insertions(+), 147 deletions(-) diff --git a/extensions/test/algorithms/buffer/multi_polygon_buffer.cpp b/extensions/test/algorithms/buffer/multi_polygon_buffer.cpp index e2287d8f8..554dfdc7e 100644 --- a/extensions/test/algorithms/buffer/multi_polygon_buffer.cpp +++ b/extensions/test/algorithms/buffer/multi_polygon_buffer.cpp @@ -267,9 +267,7 @@ void test_all() test_one("wrapped_05", wrapped, 105.000, 0.5); test_one("wrapped_10", wrapped, 142.281, 1.0); test_one("wrapped_10", wrapped, 144.000, 1.0); -#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("wrapped_15", wrapped, 167.066, 1.5); -#endif test_one("wrapped_15", wrapped, 169.000, 1.5); // TODO: there is still an undetected hole inside rt_a diff --git a/extensions/test/algorithms/buffer/polygon_buffer.cpp b/extensions/test/algorithms/buffer/polygon_buffer.cpp index a5ea1a0ea..4244da953 100644 --- a/extensions/test/algorithms/buffer/polygon_buffer.cpp +++ b/extensions/test/algorithms/buffer/polygon_buffer.cpp @@ -126,9 +126,7 @@ void test_all() test_one("arrow4", arrow, 27.039, 0.4); test_one("arrow5", arrow, 31.500, 0.5); test_one("arrow5", arrow, 29.621, 0.5); -#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("arrow6", arrow, 34.903, 0.6); -#endif test_one("arrow6", arrow, 32.268, 0.6); test_one("tipped_aitch3", tipped_aitch, 55.36, 0.3); @@ -140,13 +138,9 @@ void test_all() test_one("tipped_aitch9", tipped_aitch, 76.6457, 0.9); test_one("tipped_aitch13", tipped_aitch, 90.641, 1.3); -#if ! defined(BOOST_GEOMETRY_RESCALE_TO_ROBUST) test_one("snake4", snake, 64.44, 0.4); -#endif test_one("snake5", snake, 72, 0.5); -#if ! defined(BOOST_GEOMETRY_RESCALE_TO_ROBUST) test_one("snake6", snake, 75.44, 0.6); -#endif test_one("snake16", snake, 114.24, 1.6); test_one("funnelgate2", funnelgate, 120.982, 2); @@ -163,9 +157,7 @@ void test_all() test_one("flower1", flower, 67.614, 0.1); test_one("flower20", flower, 74.894, 0.20); test_one("flower25", flower, 78.226, 0.25); -#if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) test_one("flower30", flower, 81.492494146177947, 0.30); -#endif test_one("flower35", flower, 84.694183819917185, 0.35); test_one("flower40", flower, 87.8306529577, 0.40); test_one("flower45", flower, 90.901901559536029, 0.45); diff --git a/include/boost/geometry/extensions/algorithms/buffer/buffer_policies.hpp b/include/boost/geometry/extensions/algorithms/buffer/buffer_policies.hpp index 5039c514b..d60f787bf 100644 --- a/include/boost/geometry/extensions/algorithms/buffer/buffer_policies.hpp +++ b/include/boost/geometry/extensions/algorithms/buffer/buffer_policies.hpp @@ -110,7 +110,7 @@ struct buffer_turn_operation }; // Version for buffer including type of location, is_opposite, and helper variables -template +template struct buffer_turn_info : public detail::overlay::turn_info < @@ -119,12 +119,13 @@ struct buffer_turn_info buffer_turn_operation > { + RobustPoint robust_point; bool is_opposite; intersection_location_type location; int priority; - int count_within, count_on_helper, count_on_offsetted, count_on_corner; + int count_within; int count_on_occupied; int count_on_multi; #if defined(BOOST_GEOMETRY_COUNT_DOUBLE_UU) @@ -142,9 +143,6 @@ struct buffer_turn_info , location(location_ok) , priority(0) , count_within(0) - , count_on_helper(0) - , count_on_offsetted(0) - , count_on_corner(0) , count_on_occupied(0) , count_on_multi(0) #if defined(BOOST_GEOMETRY_COUNT_DOUBLE_UU) @@ -153,6 +151,20 @@ struct buffer_turn_info {} }; +struct buffer_operation_less +{ + template + inline bool operator()(Turn const& left, Turn const& right) const + { + segment_identifier const& sl = left.seg_id; + segment_identifier const& sr = right.seg_id; + + // Sort them descending + return sl == sr + ? left.fraction < right.fraction + : sl < sr; + } +}; }} // namespace detail::buffer #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp b/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp index 5a4909c4e..83737ff77 100644 --- a/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection.hpp @@ -26,11 +26,9 @@ #include #include -#include #include #include -#include #include #include #include @@ -68,6 +66,8 @@ enum segment_relation_code // Checks if an intersection point is inside a geometry // In some cases a trivial check might be done, e.g. using symmetric distance: // the point must be further than the distance from the geometry + +// NOTE: for negative buffers inside polygons, this check must be skipped TODO template struct check_original { @@ -104,41 +104,21 @@ struct check_original } }; - template struct buffered_piece_collection { typedef typename geometry::point_type::type point_type; typedef typename geometry::coordinate_type::type coordinate_type; - - struct piece - { - piece_type type; - int index; - - // These both form a complete clockwise ring for each piece (with one dupped point) - - // 1: half, part of offsetted_rings - segment_identifier first_seg_id; - int last_segment_index; // no segment-identifier - it is always the same - - // 2: half, not part (will be indexed in one vector too) - std::vector helper_segments; // 3 points for segment, 2 points for join - 0 points for flat-end - }; - + typedef typename geometry::robust_point_type + < + point_type, + RobustPolicy + >::type robust_point_type; typedef typename strategy::side::services::default_strategy < typename cs_tag::type >::type side_strategy; - typedef std::vector piece_vector; - - piece_vector m_pieces; - buffered_ring_collection > offsetted_rings; // indexed by multi_index - buffered_ring_collection traversed_rings; - segment_identifier current_segment_id; - - std::map, std::set > m_turn_indices_per_segment_pair; typedef typename geometry::rescale_policy_type < @@ -154,6 +134,7 @@ struct buffered_piece_collection typedef buffer_turn_info < point_type, + robust_point_type, segment_ratio_type > buffer_turn_info_type; @@ -169,8 +150,47 @@ struct buffered_piece_collection < turn_assign_for_buffer > turn_policy; + + struct robust_turn + { + int turn_index; + robust_point_type point; + segment_identifier seg_id; + segment_ratio_type fraction; + }; + + struct piece + { + piece_type type; + int index; + + // The next two members form together a complete clockwise ring + // for each piece (with one dupped point) + + // 1: half, part of offsetted_rings + segment_identifier first_seg_id; + int last_segment_index; // no segment-identifier - it is the same as first_seg_id + int offsetted_count; + + // 2: half, not part (future: might be indexed in one vector too) + std::vector helper_segments; // 3 points for segment, 2 points for join - 0 points for flat-end + + // Robust representations + std::vector robust_turns; + geometry::model::ring robust_ring; + }; + + typedef std::vector piece_vector_type; + + piece_vector_type m_pieces; turn_vector_type m_turns; + buffered_ring_collection > offsetted_rings; // indexed by multi_index + buffered_ring_collection traversed_rings; + segment_identifier current_segment_id; + + std::map, std::set > m_turn_indices_per_segment_pair; + // To check clustered locations we keep track of segments being opposite somewhere std::set m_in_opposite_segments; @@ -356,22 +376,6 @@ struct buffered_piece_collection return; } - int flat_ends_involved = 0; - for (int i = 0; i < int(boost::size(turn.operations)); i++) - { - // Don't check any turn against a piece of which is itself the result - if (turn.operations[i].piece_index == pc.index) - { - return; - } - - piece const& piece_from_intersection = m_pieces[turn.operations[i].piece_index]; - if (piece_from_intersection.type == buffered_flat_end) - { - flat_ends_involved++; - } - } - segment_identifier seg_id = pc.first_seg_id; if (seg_id.segment_index < 0) { @@ -380,87 +384,15 @@ struct buffered_piece_collection return; } - segment_identifier on_segment_seg_id; + int const geometry_code = detail::within::point_in_geometry(turn.robust_point, pc.robust_ring); - buffered_ring const& ring = offsetted_rings[seg_id.multi_index]; - - if (pc.type == buffered_circle) + if (geometry_code == 1) { - // The piece is a full (pseudo) circle. There are no helper segments. We only check if it is the turn is inside the generated circle, - // or on the border. - int const side_wrt_circle = side_on_convex_range(turn.point, - boost::begin(ring) + seg_id.segment_index, - boost::begin(ring) + pc.last_segment_index, - seg_id, on_segment_seg_id); - switch (side_wrt_circle) - { - case 0 : turn.count_on_offsetted++; break; - case -1 : turn.count_within++; break; - } - return; - } - - - // Get the segments p/q from which turn.point resulted, to get proper/robust side - buffered_ring const& ring0 = offsetted_rings[turn.operations[0].seg_id.multi_index]; - buffered_ring const& ring1 = offsetted_rings[turn.operations[1].seg_id.multi_index]; - point_type pi = geometry::range::at(ring0, turn.operations[0].seg_id.segment_index); - point_type pj = geometry::range::at(ring0, turn.operations[0].seg_id.segment_index + 1); - point_type qi = geometry::range::at(ring1, turn.operations[1].seg_id.segment_index); - point_type qj = geometry::range::at(ring1, turn.operations[1].seg_id.segment_index + 1); - - - int side_helper = intersection_side_on_convex_range(turn.point, pi, pj, qi, qj, pc.helper_segments, m_robust_policy); - if (side_helper == 1) - { - // Left or outside - return; - } - - int const side_offsetted = intersection_side_on_convex_range(turn.point, pi, pj, qi, qj, - boost::begin(ring) + seg_id.segment_index, - boost::begin(ring) + pc.last_segment_index, - seg_id, on_segment_seg_id, m_robust_policy); - if (side_offsetted == 1) - { - return; - } - - if (side_offsetted == -1 && side_helper == -1) - { - // It is within (assumed that both halves form a closed convex clockwise ring) turn.count_within++; } - if (side_offsetted == 0) - { - turn.count_on_offsetted++; - } - if (side_helper == 0) - { - if (detail::overlay::points_equal_or_close(turn.point, pc.helper_segments.back(), m_robust_policy) - || detail::overlay::points_equal_or_close(turn.point, pc.helper_segments.front(), m_robust_policy)) - { - turn.count_on_corner++; - } - else - { - if (flat_ends_involved == 0) - { - turn.count_on_helper++; -#ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER - std::ostringstream out; - out << "HLP " << pc.index; - turn.debug_string += out.str(); -#endif - } - else - { - turn.count_on_corner++; - } - } - } } + inline void debug_turns_by_indices(std::string const& caption, std::set const& indices) const { #ifdef BOOST_GEOMETRY_DEBUG_BUFFER_OCCUPATION @@ -670,11 +602,6 @@ struct buffered_piece_collection iterator_type prev1 = it1++; iterator_type prev2 = it2++; - typedef typename geometry::robust_point_type - < - point_type, - RobustPolicy - >::type robust_point_type; robust_point_type p1_rob, p2_rob, prev1_rob, prev2_rob, cur1_rob, cur2_rob; geometry::recalculate(p1_rob, select_for_side(prev1, it1, which), m_robust_policy); @@ -892,7 +819,6 @@ struct buffered_piece_collection { return turn.count_within > 0 || turn.count_on_multi > 0 - || turn.count_on_helper > 0 || turn.count_on_occupied > 0 ; } @@ -985,9 +911,103 @@ struct buffered_piece_collection << std::endl; } } - } + inline void rescale_pieces() + { + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + piece& pc = *it; + + pc.offsetted_count = pc.last_segment_index - pc.first_seg_id.segment_index; + BOOST_ASSERT(pc.offsetted_count >= 0); + + pc.robust_ring.reserve(pc.offsetted_count + pc.helper_segments.size()); + + // Add rescaled offsetted segments + { + buffered_ring const& ring = offsetted_rings[pc.first_seg_id.multi_index]; + + typedef typename boost::range_iterator >::type it_type; + for (it_type it = boost::begin(ring) + pc.first_seg_id.segment_index; + it != boost::begin(ring) + pc.last_segment_index; + ++it) + { + robust_point_type point; + geometry::recalculate(point, *it, m_robust_policy); + pc.robust_ring.push_back(point); + } + } + + // Add rescaled helper-segments + { + typedef typename std::vector::const_iterator it_type; + for (it_type it = boost::begin(pc.helper_segments); + it != boost::end(pc.helper_segments); + ++it) + { + robust_point_type point; + geometry::recalculate(point, *it, m_robust_policy); + pc.robust_ring.push_back(point); + } + } + } + + // Add rescaled turn points to corresponding pieces + // (after this, each turn occurs twice) + int index = 0; + for (typename boost::range_iterator::type it = + boost::begin(m_turns); it != boost::end(m_turns); ++it, ++index) + { + geometry::recalculate(it->robust_point, it->point, m_robust_policy); + robust_turn turn; + turn.turn_index = index; + turn.point = it->robust_point; + for (int i = 0; i < 2; i++) + { + turn.seg_id = it->operations[i].seg_id; + turn.fraction = it->operations[i].fraction; + m_pieces[it->operations[i].piece_index].robust_turns.push_back(turn); + } + } + + // All pieces now have closed robust rings. + + // Insert all rescaled turn-points into these rings, to form a + // reliable integer-based ring. All turns can be compared (inside) to this + // rings to see if they are inside. + + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + piece& pc = *it; + int piece_segment_index = pc.first_seg_id.segment_index; + if (! pc.robust_turns.empty()) + { + if (pc.robust_turns.size() > 1u) + { + std::sort(pc.robust_turns.begin(), pc.robust_turns.end(), buffer_operation_less()); + } + for (typename std::vector::const_reverse_iterator + rit = pc.robust_turns.rbegin(); + rit != pc.robust_turns.rend(); + ++rit) + { + int const offsetted_count = pc.last_segment_index - pc.first_seg_id.segment_index; + int const index_in_vector = 1 + rit->seg_id.segment_index - piece_segment_index; + BOOST_ASSERT + ( + index_in_vector > 0 && index_in_vector < offsetted_count + ); + + pc.robust_ring.insert(boost::begin(pc.robust_ring) + index_in_vector, rit->point); + } + } + } + } template inline void get_turns(Geometry const& input_geometry, DistanceStrategy const& distance_strategy) @@ -995,11 +1015,11 @@ struct buffered_piece_collection // Now: quadratic // TODO use partition - for(typename piece_vector::const_iterator it1 = boost::begin(m_pieces); + for(typename piece_vector_type::const_iterator it1 = boost::begin(m_pieces); it1 != boost::end(m_pieces); ++it1) { - for(typename piece_vector::const_iterator it2 = it1 + 1; + for(typename piece_vector_type::const_iterator it2 = it1 + 1; it2 != boost::end(m_pieces); ++it2) { @@ -1010,6 +1030,10 @@ struct buffered_piece_collection } } + rescale_pieces(); + + +#if 0 //discard_uu_turns(); for (typename boost::range_iterator::type it = boost::begin(m_turns); it != boost::end(m_turns); ++it) @@ -1028,7 +1052,7 @@ struct buffered_piece_collection } //split_uu_turns(m_turns); - +#endif fill_segment_map(); get_occupation(); diff --git a/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection_with_mapper.hpp b/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection_with_mapper.hpp index 4ef8c2307..cb28583cf 100644 --- a/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection_with_mapper.hpp +++ b/include/boost/geometry/extensions/algorithms/buffer/buffered_piece_collection_with_mapper.hpp @@ -135,9 +135,6 @@ struct buffered_piece_collection_with_mapper << ":" << operation_char(it->operations[0].operation) << "/" << operation_char(it->operations[1].operation); out << " " << it->count_within - << "-" << it->count_on_helper - << "-" << it->count_on_corner - << "-" << it->count_on_offsetted << "-" << it->count_on_occupied << "-" << it->count_on_multi //<< it->debug_string @@ -163,7 +160,7 @@ struct buffered_piece_collection_with_mapper template inline void map_pieces(Mapper& mapper, bool pieces = true, bool indices = true) { - typedef typename super_type::piece_vector piece_vector; + typedef typename super_type::piece_vector_type piece_vector; for(typename piece_vector::const_iterator it = boost::begin(this->m_pieces); it != boost::end(this->m_pieces); ++it)