diff --git a/doc/geometry.qbk b/doc/geometry.qbk index 4cd56e81a..442604534 100644 --- a/doc/geometry.qbk +++ b/doc/geometry.qbk @@ -1,9 +1,9 @@ [/============================================================================ Boost.Geometry (aka GGL, Generic Geometry Library) - Copyright (c) 2009-2012 Barend Gehrels, Amsterdam, the Netherlands. - Copyright (c) 2009-2012 Mateusz Loskot, London, UK. - Copyright (c) 2009-2012 Bruno Lalande, Paris, France. + Copyright (c) 2009-2015 Barend Gehrels, Amsterdam, the Netherlands. + Copyright (c) 2009-2015 Mateusz Loskot, London, UK. + Copyright (c) 2009-2015 Bruno Lalande, Paris, France. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -13,7 +13,7 @@ [library Geometry [quickbook 1.5] [authors [Gehrels, Barend], [Lalande, Bruno], [Loskot, Mateusz], [Wulkiewicz, Adam], [Karavelas, Menelaos]] - [copyright 2009-2014 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] + [copyright 2009-2015 Barend Gehrels, Bruno Lalande, Mateusz Loskot, Adam Wulkiewicz, Oracle and/or its affiliates] [purpose Documentation of Boost.Geometry library] [license Distributed under the Boost Software License, Version 1.0. diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 724d125eb..6b732cefd 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -61,6 +61,7 @@ * Bug in centroid(), non-deterministic result if calculated for e.g. a Polygon with zero-area. * Bug in buffers for joins with a limited number of points * Bug in buffers for joins with large buffer distances +* Bug in buffers for round ends with an odd number of points * Bug in buffers for flat ends with large buffer distances * Bug in buffers for interior rings with large negative buffer distances * Bug in closing iterator not working properly when the input range is empty 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/get_piece_turns.hpp b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 31ffdec53..6a3daa282 100644 --- a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace boost { namespace geometry @@ -88,6 +89,38 @@ class piece_turn_visitor return result; } + template + inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond, + int& index, int dir, Box const& other_bounding_box) + { + for(; it_begin != it_beyond + && it_begin + 1 != it_beyond + && detail::section::preceding(dir, *(it_begin + 1), + other_bounding_box, m_robust_policy); + ++it_begin, index++) + {} + } + + template + inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond, + int dir, Box const& other_bounding_box) + { + while (it_beyond != it_begin + && it_beyond - 1 != it_begin + && it_beyond - 2 != it_begin) + { + if (detail::section::exceeding(dir, *(it_beyond - 2), + other_bounding_box, m_robust_policy)) + { + --it_beyond; + } + else + { + return; + } + } + } + template inline void calculate_turns(Piece const& piece1, Piece const& piece2, Section const& section1, Section const& section2) @@ -113,17 +146,38 @@ class piece_turn_visitor // get geometry and iterators over these sections ring_type const& ring1 = m_rings[piece1.first_seg_id.multi_index]; - iterator const it1_first = boost::begin(ring1) + sec1_first_index; - iterator const it1_beyond = boost::begin(ring1) + sec1_last_index + 1; + iterator it1_first = boost::begin(ring1) + sec1_first_index; + iterator it1_beyond = boost::begin(ring1) + sec1_last_index + 1; ring_type const& ring2 = m_rings[piece2.first_seg_id.multi_index]; - iterator const it2_first = boost::begin(ring2) + sec2_first_index; - iterator const it2_beyond = boost::begin(ring2) + sec2_last_index + 1; + iterator it2_first = boost::begin(ring2) + sec2_first_index; + iterator it2_beyond = boost::begin(ring2) + sec2_last_index + 1; + + // Set begin/end of monotonic ranges, in both x/y directions + int index1 = sec1_first_index; + move_begin_iterator<0>(it1_first, it1_beyond, index1, + section1.directions[0], section2.bounding_box); + move_end_iterator<0>(it1_first, it1_beyond, + section1.directions[0], section2.bounding_box); + move_begin_iterator<1>(it1_first, it1_beyond, index1, + section1.directions[1], section2.bounding_box); + move_end_iterator<1>(it1_first, it1_beyond, + section1.directions[1], section2.bounding_box); + + int index2 = sec2_first_index; + move_begin_iterator<0>(it2_first, it2_beyond, index2, + section2.directions[0], section1.bounding_box); + move_end_iterator<0>(it2_first, it2_beyond, + section2.directions[0], section1.bounding_box); + move_begin_iterator<1>(it2_first, it2_beyond, index2, + section2.directions[1], section1.bounding_box); + move_end_iterator<1>(it2_first, it2_beyond, + section2.directions[1], section1.bounding_box); turn_type the_model; the_model.operations[0].piece_index = piece1.index; the_model.operations[0].seg_id = piece1.first_seg_id; - the_model.operations[0].seg_id.segment_index = sec1_first_index; // override + the_model.operations[0].seg_id.segment_index = index1; // override iterator it1 = it1_first; for (iterator prev1 = it1++; @@ -132,7 +186,7 @@ class piece_turn_visitor { the_model.operations[1].piece_index = piece2.index; the_model.operations[1].seg_id = piece2.first_seg_id; - the_model.operations[1].seg_id.segment_index = sec2_first_index; // override + the_model.operations[1].seg_id.segment_index = index2; // override iterator next1 = next_point(ring1, it1); diff --git a/include/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp b/include/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp deleted file mode 100644 index 2b1c33d29..000000000 --- a/include/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// Copyright (c) 2012-2014 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_BUFFER_TURN_IN_INPUT_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP - -#include -#include - - -namespace boost { namespace geometry -{ - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace buffer -{ - -// Checks if an turn/intersection point is inside (or covered by) the input geometry - -template -struct turn_in_input -{ -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& point, Geometry const& geometry) - { - return geometry::covered_by(point, geometry) ? 1 : -1; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& point, Geometry const& geometry) - { - return geometry::covered_by(point, geometry) ? 1 : -1; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input -{ - template - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - - -}} // namespace detail::buffer -#endif // DOXYGEN_NO_DETAIL - - - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP 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/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 88104d25a..a5d8f3f02 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -63,6 +63,7 @@ #include #include +#include #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION # include @@ -228,7 +229,7 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); + it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator nd_next1( @@ -246,7 +247,7 @@ public : next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); + it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -317,25 +318,6 @@ private : typedef typename model::referring_segment segment1_type; typedef typename model::referring_segment segment2_type; - - template - static inline bool preceding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) - { - typename robust_point_type::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) < get(box)) - || (dir == -1 && get(robust_point) > get(box)); - } - - template - static inline bool exceeding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) - { - typename robust_point_type::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) > get(box)) - || (dir == -1 && get(robust_point) < get(box)); - } - template static inline void advance_to_non_duplicate_next(Iterator& next, RangeIterator const& it, Section const& section, RobustPolicy const& robust_policy) @@ -386,7 +368,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && preceding<0>(dir, *it, other_bounding_box, robust_policy); + for(; it != end && detail::section::preceding<0>(dir, *it, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding diff --git a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp new file mode 100644 index 000000000..ba1cf931b --- /dev/null +++ b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp @@ -0,0 +1,66 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015 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_SECTIONS_FUNCTIONS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_FUNCTIONS_HPP + + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace section +{ + +template +< + std::size_t Dimension, + typename Point, + typename RobustBox, + typename RobustPolicy +> +static inline bool preceding(int dir, Point const& point, + RobustBox const& robust_box, + RobustPolicy const& robust_policy) +{ + typename geometry::robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) < get(robust_box)) + || (dir == -1 && get(robust_point) > get(robust_box)); +} + +template +< + std::size_t Dimension, + typename Point, + typename RobustBox, + typename RobustPolicy +> +static inline bool exceeding(int dir, Point const& point, + RobustBox const& robust_box, + RobustPolicy const& robust_policy) +{ + typename geometry::robust_point_type::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get(robust_point) > get(robust_box)) + || (dir == -1 && get(robust_point) < get(robust_box)); +} + + +}} // namespace detail::section +#endif + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_FUNCTIONS_HPP diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp index 3d1d8e1d1..a744ea0a3 100644 --- a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -74,6 +74,7 @@ template struct section { typedef Box box_type; + static std::size_t const dimension_count = DimensionCount; int directions[DimensionCount]; ring_identifier ring_id; @@ -286,13 +287,14 @@ struct sectionalize_part std::size_t max_count) { boost::ignore_unused_variable_warning(robust_policy); + + typedef typename boost::range_value::type section_type; BOOST_STATIC_ASSERT ( - (static_cast(Sections::value) + (static_cast(section_type::dimension_count) == static_cast(boost::mpl::size::value)) ); - typedef typename boost::range_value::type section_type; typedef typename geometry::robust_point_type < Point, @@ -765,17 +767,13 @@ inline void sectionalize(Geometry const& geometry, { concept::check(); - BOOST_STATIC_ASSERT - ( - (static_cast(Sections::value) - == static_cast(boost::mpl::size::value)) - ); + typedef typename boost::range_value::type section_type; // Compiletime check for point type of section boxes // and point type related to robust policy typedef typename geometry::coordinate_type < - typename Sections::box_type + typename section_type::box_type >::type ctype1; typedef typename geometry::coordinate_type < diff --git a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp index 454c0d5d0..e7486f0e4 100644 --- a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp @@ -36,6 +36,7 @@ #include #include +#include #include namespace boost { namespace geometry { @@ -197,11 +198,11 @@ struct point_in_geometry return -1; // exterior // if the linestring doesn't have a boundary - if ( detail::equals::equals_point_point(*boost::begin(linestring), *(--boost::end(linestring))) ) + if (detail::equals::equals_point_point(range::front(linestring), range::back(linestring))) return 1; // interior // else if the point is equal to the one of the terminal points - else if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) - || detail::equals::equals_point_point(point, *(--boost::end(linestring))) ) + else if (detail::equals::equals_point_point(point, range::front(linestring)) + || detail::equals::equals_point_point(point, range::back(linestring))) return 0; // boundary else return 1; // interior @@ -210,7 +211,7 @@ struct point_in_geometry // throw an exception here? /*else if ( count == 1 ) { - if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) ) + if ( detail::equals::equals_point_point(point, range::front(linestring)) ) return 1; }*/ @@ -336,8 +337,8 @@ struct point_in_geometry if ( boost::size(*it) < 2 ) continue; - point_type const& front = *boost::begin(*it); - point_type const& back = *(--boost::end(*it)); + point_type const& front = range::front(*it); + point_type const& back = range::back(*it); // is closed_ring - no boundary if ( detail::equals::equals_point_point(front, back) ) diff --git a/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp index 74780d616..a233f1c4b 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_end_round.hpp @@ -95,8 +95,9 @@ public : //! \brief Constructs the strategy //! \param points_per_circle points which would be used for a full circle + //! (if points_per_circle is smaller than 4, it is internally set to 4) explicit inline end_round(std::size_t points_per_circle = 90) - : m_points_per_circle(points_per_circle) + : m_points_per_circle((points_per_circle < 4u) ? 4u : points_per_circle) {} #ifndef DOXYGEN_SHOULD_SKIP_THIS @@ -106,7 +107,7 @@ public : inline void apply(Point const& penultimate_point, Point const& perp_left_point, Point const& ultimate_point, - Point const& , + Point const& perp_right_point, buffer_side_selector side, DistanceStrategy const& distance, RangeOut& range_out) const @@ -142,6 +143,13 @@ public : set<1>(shifted_point, get<1>(ultimate_point) + dist_half_diff * sin(alpha)); generate_points(shifted_point, alpha, (dist_left + dist_right) / two, range_out); } + + if (m_points_per_circle % 2 == 1) + { + // For a half circle, if the number of points is not even, + // we should insert the end point too, to generate a full cap + range_out.push_back(perp_right_point); + } } template diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp index 8fcf3b996..99ec80527 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_miter.hpp @@ -35,6 +35,8 @@ namespace strategy { namespace buffer their length. The miter is not changed to a bevel form (as done in some other software), it is just adapted to the specified miter_limit but keeps its miter form. + If the buffer distance is 5.0, and the miter limit is 2.0, generated points + will be located at a distance of at most 10.0 (2*5) units. This strategy is only applicable for Cartesian coordinate systems. \qbk{ diff --git a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp index f88b2904b..838e101c9 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_join_round.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2015 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 @@ -9,6 +9,8 @@ #ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_JOIN_ROUND_HPP #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_JOIN_ROUND_HPP +#include + #include #include #include @@ -83,15 +85,13 @@ private : { angle2 -= two_pi; } + PromotedType const dangle = angle1 - angle2; // Divide the angle into an integer amount of steps to make it // visually correct also for a low number of points / circle - int const n = static_cast - ( - m_points_per_circle * (angle1 - angle2) / two_pi - ); + int const n = static_cast(m_points_per_circle * dangle / two_pi); - PromotedType const diff = (angle1 - angle2) / static_cast(n); + PromotedType const diff = dangle / static_cast(n); PromotedType a = angle1 - diff; for (int i = 0; i < n - 1; i++, a -= diff) { diff --git a/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp b/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp index f64a82d8f..86ebc43c9 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_point_circle.hpp @@ -1,5 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. + +// Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2015. +// Modifications copyright (c) 2015, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + // 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) @@ -45,9 +52,10 @@ class point_circle { public : //! \brief Constructs the strategy - //! \param count number of points for the created circle + //! \param count number of points for the created circle (if count + //! is smaller than 3, count is internally set to 3) explicit point_circle(std::size_t count = 90) - : m_count(count) + : m_count((count < 3u) ? 3u : count) {} #ifndef DOXYGEN_SHOULD_SKIP_THIS diff --git a/include/boost/geometry/util/range.hpp b/include/boost/geometry/util/range.hpp index 177fca92c..89d981619 100644 --- a/include/boost/geometry/util/range.hpp +++ b/include/boost/geometry/util/range.hpp @@ -138,7 +138,7 @@ back(BidirectionalRange const& rng) { BOOST_RANGE_CONCEPT_ASSERT(( boost::BidirectionalRangeConcept )); BOOST_ASSERT(!boost::empty(rng)); - return *(--boost::end(rng)); + return *(boost::rbegin(rng)); } /*! @@ -151,7 +151,7 @@ back(BidirectionalRange & rng) { BOOST_RANGE_CONCEPT_ASSERT(( boost::BidirectionalRangeConcept )); BOOST_ASSERT(!boost::empty(rng)); - return *(--boost::end(rng)); + return *(boost::rbegin(rng)); } diff --git a/test/algorithms/buffer/linestring_buffer.cpp b/test/algorithms/buffer/linestring_buffer.cpp index 01b066415..63f6d8567 100644 --- a/test/algorithms/buffer/linestring_buffer.cpp +++ b/test/algorithms/buffer/linestring_buffer.cpp @@ -59,6 +59,10 @@ static std::string const crossing = "LINESTRING(0 0,10 10,10 0,0 10)"; // Simplified cases from multi_linestring tesets: static std::string const mikado1 = "LINESTRING(11.406143344709896325639419956133 0.75426621160409546007485914742574,12 1,11.403846153846153299582510953769 0.75)"; +static std::string const mysql_report_2015_03_02a = "LINESTRING(0 0,0 5,5 5,5 0,0 0)"; // closed +static std::string const mysql_report_2015_03_02b = "LINESTRING(0 1,0 5,5 5,5 0,1 0)"; // not closed, 1 difference +static std::string const mysql_report_2015_03_02c = "LINESTRING(0 2,0 5,5 5,5 0,2 0)"; // not closed, 2 difference + template void test_all() @@ -166,6 +170,40 @@ void test_all() test_one("degenerate4", degenerate4, join_round, end_round, 36.7410, 3.0); test_one("degenerate4", degenerate4, join_round, end_flat, 8.4853, 3.0); + { + // These tests do test behaviour in end_round strategy: + // -> it should generate closed pieces, also for an odd number of points. + // It also tests behaviour in join_round strategy: + // -> it should generate e.g. 4 points for a full circle, + // so a quarter circle does not get points in between + using bg::strategy::buffer::join_round; + using bg::strategy::buffer::end_round; + + double const d10 = 1.0; + + test_one("mysql_report_2015_03_02a_3", mysql_report_2015_03_02a, join_round(3), end_round(3), 38.000, d10); + test_one("mysql_report_2015_03_02a_4", mysql_report_2015_03_02a, join_round(4), end_round(4), 38.000, d10); + test_one("mysql_report_2015_03_02a_5", mysql_report_2015_03_02a, join_round(5), end_round(5), 38.169, d10); + test_one("mysql_report_2015_03_02a_6", mysql_report_2015_03_02a, join_round(6), end_round(6), 38.196, d10); + test_one("mysql_report_2015_03_02a_7", mysql_report_2015_03_02a, join_round(7), end_round(7), 38.230, d10); + test_one("mysql_report_2015_03_02b_3", mysql_report_2015_03_02b, join_round(3), end_round(3), 36.500, d10); + test_one("mysql_report_2015_03_02b_4", mysql_report_2015_03_02b, join_round(4), end_round(4), 36.500, d10); + test_one("mysql_report_2015_03_02b_5", mysql_report_2015_03_02b, join_round(5), end_round(5), 36.724, d10); + test_one("mysql_report_2015_03_02b_6", mysql_report_2015_03_02b, join_round(6), end_round(6), 36.781, d10); + test_one("mysql_report_2015_03_02b_7", mysql_report_2015_03_02b, join_round(7), end_round(7), 36.884, d10); + test_one("mysql_report_2015_03_02c_3", mysql_report_2015_03_02c, join_round(2), end_round(3), 32.500, d10); + test_one("mysql_report_2015_03_02c_4", mysql_report_2015_03_02c, join_round(4), end_round(4), 32.500, d10); + test_one("mysql_report_2015_03_02c_5", mysql_report_2015_03_02c, join_round(5), end_round(5), 32.990, d10); + test_one("mysql_report_2015_03_02c_6", mysql_report_2015_03_02c, join_round(6), end_round(6), 33.098, d10); + test_one("mysql_report_2015_03_02c_7", mysql_report_2015_03_02c, join_round(7), end_round(7), 33.279, d10); + + // Testing the asymmetric end caps with odd number of points + double const d15 = 1.5; + test_one("mysql_report_2015_03_02c_asym1", mysql_report_2015_03_02c, join_round(7), end_round(7), 39.093, d10, d15); + test_one("mysql_report_2015_03_02c_asym2", mysql_report_2015_03_02c, join_round(7), end_round(7), 44.718, d15, d10); + } + + { double tolerance = 1.0e-10; diff --git a/test/algorithms/buffer/multi_point_buffer.cpp b/test/algorithms/buffer/multi_point_buffer.cpp index e57a6af66..5c4cbd6a7 100644 --- a/test/algorithms/buffer/multi_point_buffer.cpp +++ b/test/algorithms/buffer/multi_point_buffer.cpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2012-2015 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 @@ -73,44 +73,74 @@ void test_all() distance_strategy(0.54), side_strategy, point_strategy, 99); #endif - } + test_with_custom_strategies("mysql_report_2015_02_25_1_800", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, + bg::strategy::buffer::point_circle(800), 115057490003226.125, 1.0); +} + +template +void test_many_points_per_circle() +{ // Tests for large distances / many points in circles. // Before Boost 1.58, this would (seem to) hang. It is solved by using monotonic sections in get_turns for buffer // This is more time consuming, only calculate this for counter clockwise - if (! BOOST_GEOMETRY_CONDITION(Clockwise)) - { - // Reported by MySQL 2015-02-25 - // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 6051788, ST_BUFFER_STRATEGY('point_circle', 83585))); - // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 5666962, ST_BUFFER_STRATEGY('point_circle', 46641))) ; + // Reported by MySQL 2015-02-25 + // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 6051788, ST_BUFFER_STRATEGY('point_circle', 83585))); + // SELECT ST_ASTEXT(ST_BUFFER(ST_GEOMFROMTEXT(''), 5666962, ST_BUFFER_STRATEGY('point_circle', 46641))) ; - using bg::strategy::buffer::point_circle; - test_with_custom_strategies("mysql_report_2015_02_25_1_800", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(800), 115057490003226.125, 1.0); + typedef bg::model::polygon polygon; + typedef bg::model::multi_point

multi_point_type; - test_with_custom_strategies("mysql_report_2015_02_25_1_8000", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(8000), 115058661065242.812, 1.0); + bg::strategy::buffer::join_miter join_miter; + bg::strategy::buffer::end_flat end_flat; + typedef bg::strategy::buffer::distance_symmetric + < + typename bg::coordinate_type

::type + > distance_strategy; + bg::strategy::buffer::side_straight side_strategy; - test_with_custom_strategies("mysql_report_2015_02_25_1", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(83585), 115058672785611.219, 1.0); + using bg::strategy::buffer::point_circle; + + double const tolerance = 1.0; + + // Strategies with many points, which are (very) slow in debug mode + test_with_custom_strategies( + "mysql_report_2015_02_25_1_8000", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(8000), + 115058661065242.812, tolerance); + + test_with_custom_strategies( + "mysql_report_2015_02_25_1", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(83585), + 115058672785611.219, tolerance); + + // 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); #if defined(BOOST_GEOMETRY_BUFFER_INCLUDE_FAILING_TESTS) - // Try to specify even more points per circle - // Turns are calculated in 23 seconds - // But the follow-up still takes too long (there are 63409 turns), this might be improved too - test_with_custom_strategies("mysql_report_2015_02_25_1", - mysql_report_2015_02_25_1, join_miter, end_flat, - distance_strategy(6051788), side_strategy, point_circle(800000), 115058672799999.999, 1.0); // area to be determined + // Takes too long, TODO improve turn_in_piece_visitor + test_with_custom_strategies( + "mysql_report_2015_02_25_1", + mysql_report_2015_02_25_1, join_miter, end_flat, + distance_strategy(6051788), side_strategy, point_circle(800000), + 115058672799999.999, tolerance); // area to be determined #endif - test_with_custom_strategies("mysql_report_2015_02_25_2", - mysql_report_2015_02_25_2, join_miter, end_flat, - distance_strategy(5666962), side_strategy, point_circle(46641), 100891031341757.344, 1.0); - } + test_with_custom_strategies( + "mysql_report_2015_02_25_2", + mysql_report_2015_02_25_2, join_miter, end_flat, + distance_strategy(5666962), side_strategy, point_circle(46641), + 100891031341757.344, tolerance); + } int test_main(int, char* []) @@ -118,5 +148,11 @@ int test_main(int, char* []) test_all >(); test_all >(); +#if defined(BOOST_GEOMETRY_COMPILER_MODE_RELEASE) && ! defined(BOOST_GEOMETRY_COMPILER_MODE_DEBUG) + test_many_points_per_circle >(); +#else + std::cout << "Skipping some tests in debug or unknown mode" << std::endl; +#endif + return 0; } diff --git a/test/algorithms/buffer/polygon_buffer.cpp b/test/algorithms/buffer/polygon_buffer.cpp index a683e88f6..d0eeedb09 100644 --- a/test/algorithms/buffer/polygon_buffer.cpp +++ b/test/algorithms/buffer/polygon_buffer.cpp @@ -532,18 +532,65 @@ void test_all() { - bg::strategy::buffer::join_round join_round12(12); - buffer_custom_side_strategy side_strategy; + using bg::strategy::buffer::join_round; + using bg::strategy::buffer::join_miter; + bg::strategy::buffer::side_straight side_strategy; bg::strategy::buffer::point_circle point_strategy; - bg::strategy::buffer::distance_symmetric + typedef bg::strategy::buffer::distance_symmetric < typename bg::coordinate_type

::type - > distance_strategy(1.0); + > distance; - test_with_custom_strategies("sharp_triangle", + test_with_custom_strategies("sharp_triangle_j12", sharp_triangle, - join_round12, end_flat, distance_strategy, side_strategy, point_strategy, - 31.0721); + join_round(12), end_flat, distance(1.0), side_strategy, point_strategy, + 29.0980); + // Test very various number of points (min is 3) + test_with_custom_strategies("sharp_triangle_j2", + sharp_triangle, + join_round(2), end_flat, distance(1.0), side_strategy, point_strategy, + 27.2399); + test_with_custom_strategies("sharp_triangle_j5", + sharp_triangle, + join_round(5), end_flat, distance(1.0), side_strategy, point_strategy, + 28.1091); + + test_with_custom_strategies("sharp_triangle_j36", + sharp_triangle, + join_round(36), end_flat, distance(1.0), side_strategy, point_strategy, + 29.2482); + test_with_custom_strategies("sharp_triangle_j360", + sharp_triangle, + join_round(360), end_flat, distance(1.0), side_strategy, point_strategy, + 29.2659); + + // Test with various miter limits + test_with_custom_strategies("sharp_triangle_m2", + sharp_triangle, + join_miter(2), end_flat, distance(4.0), side_strategy, point_strategy, + 148.500); + test_with_custom_strategies("sharp_triangle_m3", + sharp_triangle, + join_miter(3), end_flat, distance(4.0), side_strategy, point_strategy, + 164.376); + test_with_custom_strategies("sharp_triangle_m4", + sharp_triangle, + join_miter(4), end_flat, distance(4.0), side_strategy, point_strategy, + 180.2529); + test_with_custom_strategies("sharp_triangle_m5", + sharp_triangle, + join_miter(5), end_flat, distance(4.0), side_strategy, point_strategy, + 196.1293); + test_with_custom_strategies("sharp_triangle_m25", + sharp_triangle, + join_miter(25), end_flat, distance(4.0), side_strategy, point_strategy, + 244.7471); + + buffer_custom_side_strategy custom_side_strategy; + test_with_custom_strategies("sharp_triangle_custom_side", + sharp_triangle, + join_round(49), end_flat, distance(1.0), custom_side_strategy, point_strategy, + 31.1087); } } diff --git a/test/algorithms/buffer/test_buffer.hpp b/test/algorithms/buffer/test_buffer.hpp index b268eb40d..dc0177d15 100644 --- a/test/algorithms/buffer/test_buffer.hpp +++ b/test/algorithms/buffer/test_buffer.hpp @@ -688,7 +688,10 @@ void test_one(std::string const& caseid, std::string const& wkt, check_self_intersections, expected_area, tolerance, NULL); +#if !defined(BOOST_GEOMETRY_COMPILER_MODE_DEBUG) && defined(BOOST_GEOMETRY_COMPILER_MODE_RELEASE) + // Also test symmetric distance strategy if right-distance is not specified + // (only in release mode) if (bg::math::equals(distance_right, -999)) { bg::strategy::buffer::distance_symmetric @@ -704,6 +707,7 @@ void test_one(std::string const& caseid, std::string const& wkt, tolerance, NULL); } +#endif } // Version (currently for the Aimes test) counting self-ip's instead of checking diff --git a/test/geometry_test_common.hpp b/test/geometry_test_common.hpp index 1399e8d6c..ca9959ad7 100644 --- a/test/geometry_test_common.hpp +++ b/test/geometry_test_common.hpp @@ -1,8 +1,8 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. -// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -15,6 +15,27 @@ #ifndef GEOMETRY_TEST_GEOMETRY_TEST_COMMON_HPP #define GEOMETRY_TEST_GEOMETRY_TEST_COMMON_HPP +// Determine debug/release mode +// (it would be convenient if Boost.Config or Boost.Test would define this) +// Note that they might be combined (e.g. for optimize+no inline) +#if defined (__clang__) || defined(__gcc__) +#if defined(__OPTIMIZE__) + #define BOOST_GEOMETRY_COMPILER_MODE_RELEASE +#endif +#if defined(__NO_INLINE__) +#define BOOST_GEOMETRY_COMPILER_MODE_DEBUG +#endif +#endif + +#if defined(_MSC_VER) +#if defined(_DEBUG) +#define BOOST_GEOMETRY_COMPILER_MODE_DEBUG +#else +#define BOOST_GEOMETRY_COMPILER_MODE_RELEASE +#endif +#endif + + #if defined(_MSC_VER) // We deliberately mix float/double's so turn off warnings #pragma warning( disable : 4244 )