diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index bf8240ae8..7207d0dc5 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -38,52 +39,12 @@ namespace detail { namespace buffer { -template -< - typename RingOutput, - typename Tag -> +template struct buffer_range { typedef typename point_type::type output_point_type; typedef typename coordinate_type::type coordinate_type; - template - < - typename Point, - typename DistanceStrategy - > - static inline void generate_side( - Point const& input_p1, Point const& input_p2, - strategy::buffer::buffer_side_selector side, - DistanceStrategy const& distance, - output_point_type& side_p1, output_point_type& side_p2) - { - // Generate a block along (left or right of) the segment - - // Simulate a vector d (dx,dy) - coordinate_type dx = get<0>(input_p2) - get<0>(input_p1); - coordinate_type dy = get<1>(input_p2) - get<1>(input_p1); - - // For normalization [0,1] (=dot product d.d, sqrt) - // TODO promoted_type - coordinate_type const length = geometry::math::sqrt(dx * dx + dy * dy); - - // Because coordinates are not equal, length should not be zero - BOOST_ASSERT((! geometry::math::equals(length, 0))); - - // Generate the normalized perpendicular p, to the left (ccw) - coordinate_type const px = -dy / length; - coordinate_type const py = dx / length; - - coordinate_type const d = distance.apply(input_p1, input_p2, side); - - set<0>(side_p1, get<0>(input_p1) + px * d); - set<1>(side_p1, get<1>(input_p1) + py * d); - set<0>(side_p2, get<0>(input_p2) + px * d); - set<1>(side_p2, get<1>(input_p2) + py * d); - } - template < typename Collection, @@ -197,7 +158,10 @@ struct buffer_range JoinStrategy const& join_strategy, EndStrategy const& end_strategy, RobustPolicy const& robust_policy, - bool /*close*/ = false) + output_point_type& first_p1, + output_point_type& first_p2, + output_point_type& last_p1, + output_point_type& last_p2) { typedef typename std::iterator_traits < @@ -211,16 +175,14 @@ struct buffer_range >::type robust_point_type; robust_point_type previous_robust_input; - output_point_type previous_p1, previous_p2; - output_point_type first_p1, first_p2; point_type second_point, penultimate_point, ultimate_point; // last two points from begin/end /* - * prev.p1 prev.p2 these are the "previous perpendicular points" + * last.p1 last.p2 these are the "previous (last) perpendicular points" * -------------- * | | * *------------*____ <- *prev - * pup | | p1 "current perpendiculat point 1" + * pup | | p1 "current perpendicular point 1" * | | * | | this forms a "side", a side is a piece * | | @@ -238,6 +200,9 @@ struct buffer_range geometry::recalculate(previous_robust_input, *begin, robust_policy); + std::vector generated_side; + generated_side.reserve(2); + for (Iterator prev = it++; it != end; ++it) { robust_point_type robust_input; @@ -246,69 +211,39 @@ struct buffer_range // unlikely (though possible by rescaling) if (! detail::equals::equals_point_point(previous_robust_input, robust_input)) { - output_point_type p1, p2; - generate_side(*prev, *it, side, distance_strategy, p1, p2); + generated_side.clear(); + strategy::buffer::buffer_side::apply(*prev, + *it, side, distance_strategy, generated_side); if (! first) { add_join(collection, phase, penultimate_point, - *prev, previous_p1, previous_p2, - *it, p1, p2, + *prev, last_p1, last_p2, + *it, generated_side.front(), generated_side.back(), side, distance_strategy, join_strategy, end_strategy, robust_policy); } + collection.add_piece(strategy::buffer::buffered_segment, - *prev, *it, p1, p2, first); + *prev, *it, generated_side, first); penultimate_point = *prev; ultimate_point = *it; - previous_p1 = p1; - previous_p2 = p2; + last_p1 = generated_side.front(); + last_p2 = generated_side.back(); prev = it; if (first) { first = false; second_point = *it; - first_p1 = p1; - first_p2 = p2; + first_p1 = generated_side.front(); + first_p2 = generated_side.back(); } } previous_robust_input = robust_input; } - - // Might be replaced by specialization - if(boost::is_same::value) - { - // Generate closing corner - add_join(collection, phase, - penultimate_point, - *(end - 1), previous_p1, previous_p2, - *(begin + 1), first_p1, first_p2, - side, - distance_strategy, join_strategy, end_strategy, - robust_policy); - - // Buffer is closed automatically by last closing corner (NOT FOR OPEN POLYGONS - TODO) - } - else if (boost::is_same::value) - { - - // Generate perpendicular points to the reverse side, - // these points are necessary for all end-cap strategies - // TODO fix this (approach) for one-side buffer (1.5 - -1.0) - output_point_type rp1, rp2; - generate_side(ultimate_point, penultimate_point, - side == strategy::buffer::buffer_side_left - ? strategy::buffer::buffer_side_right - : strategy::buffer::buffer_side_left, - distance_strategy, rp2, rp1); - - std::vector range_out; - end_strategy.apply(penultimate_point, previous_p2, ultimate_point, rp2, side, distance_strategy, range_out); - collection.add_endcap(end_strategy, range_out, ultimate_point); - } } }; @@ -485,17 +420,47 @@ template typename RingOutput > struct buffer_inserter - : public detail::buffer::buffer_range - < - RingOutput, - ring_tag - > { - typedef detail::buffer::buffer_range - < - RingOutput, - ring_tag - > base; + typedef typename point_type::type output_point_type; + + template + < + typename Collection, + typename Iterator, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void iterate(Collection& collection, + Iterator begin, Iterator end, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + output_point_type first_p1, first_p2, last_p1, last_p2; + + typedef detail::buffer::buffer_range buffer_range; + + buffer_range::iterate(collection, 0, begin, end, + side, + distance_strategy, join_strategy, end_strategy, robust_policy, + first_p1, first_p2, last_p1, last_p2); + + // Generate closing join + buffer_range::add_join(collection, 0, + *(end - 2), + *(end - 1), last_p1, last_p2, + *(begin + 1), first_p1, first_p2, + side, + distance_strategy, join_strategy, end_strategy, + robust_policy); + + // Buffer is closed automatically by last closing corner + // (OPEN IT FOR OPEN POLYGONS - TODO) + } template < @@ -531,16 +496,17 @@ struct buffer_inserter // Walk backwards (rings will be reversed afterwards) // It might be that this will be changed later. // TODO: decide this. - base::iterate(collection, 0, boost::rbegin(simplified), boost::rend(simplified), + iterate(collection, boost::rbegin(simplified), boost::rend(simplified), strategy::buffer::buffer_side_right, distance, join_strategy, end_strategy, robust_policy); } else { - base::iterate(collection, 0, boost::begin(simplified), boost::end(simplified), + iterate(collection, boost::begin(simplified), boost::end(simplified), strategy::buffer::buffer_side_left, distance, join_strategy, end_strategy, robust_policy); } + } } }; @@ -552,17 +518,64 @@ template typename Polygon > struct buffer_inserter - : public detail::buffer::buffer_range - < - typename ring_type::type, - linestring_tag - > { - typedef detail::buffer::buffer_range - < - typename ring_type::type, - linestring_tag - > base; + typedef typename ring_type::type output_ring_type; + typedef typename point_type::type output_point_type; + typedef typename point_type::type input_point_type; + + template + static inline output_point_type first_perpendicular_point( + input_point_type const& p1, input_point_type const& p2, + DistanceStrategy const& distance_strategy) + { + std::vector generated_side; + strategy::buffer::buffer_side::apply(p1, p2, + strategy::buffer::buffer_side_right, + distance_strategy, generated_side); + return generated_side.front(); + } + + template + < + typename Collection, + typename Iterator, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void iterate(Collection& collection, int phase, + Iterator begin, Iterator end, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy, + output_point_type& first_p1) + { + input_point_type const& ultimate_point = *(end - 1); + input_point_type const& penultimate_point = *(end - 2); + + // For the end-cap, we need to have the last perpendicular point on the + // other side of the linestring. If it is the second pass (right), + // we have it already from the first phase (left). + // But for the first pass, we have to generate it + output_point_type reverse_p1 + = side == strategy::buffer::buffer_side_right + ? first_p1 + : first_perpendicular_point(ultimate_point, penultimate_point, distance_strategy); + + output_point_type first_p2, last_p1, last_p2; + + detail::buffer::buffer_range::iterate(collection, + phase, begin, end, side, + distance_strategy, join_strategy, end_strategy, robust_policy, + first_p1, first_p2, last_p1, last_p2); + + std::vector range_out; + end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, side, distance_strategy, range_out); + collection.add_endcap(end_strategy, range_out, ultimate_point); + } template < @@ -584,13 +597,16 @@ struct buffer_inserter geometry::simplify(linestring, simplified, distance.simplify_distance()); collection.start_new_ring(); - base::iterate(collection, 0, boost::begin(simplified), boost::end(simplified), + output_point_type first_p1; + iterate(collection, 0, boost::begin(simplified), boost::end(simplified), strategy::buffer::buffer_side_left, - distance, join_strategy, end_strategy, robust_policy); + distance, join_strategy, end_strategy, robust_policy, + first_p1); - base::iterate(collection, 1, boost::rbegin(simplified), boost::rend(simplified), + iterate(collection, 1, boost::rbegin(simplified), boost::rend(simplified), strategy::buffer::buffer_side_right, - distance, join_strategy, end_strategy, robust_policy, true); + distance, join_strategy, end_strategy, robust_policy, + first_p1); } } 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 dfdd15636..bdad2acf8 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -589,8 +589,9 @@ struct buffered_piece_collection return m_pieces.back(); } + template inline void add_piece(strategy::buffer::piece_type type, point_type const& p1, point_type const& p2, - point_type const& b1, point_type const& b2, bool first) + Range const& range, bool first) { piece& pc = add_piece(type, ! first); @@ -599,14 +600,14 @@ struct buffered_piece_collection // But for now we need it to calculate intersections if (first) { - add_point(b1); + add_point(range.front()); } - pc.last_segment_index = add_point(b2); + pc.last_segment_index = add_point(range.back()); - pc.helper_segments.push_back(b2); + pc.helper_segments.push_back(range.back()); pc.helper_segments.push_back(p2); pc.helper_segments.push_back(p1); - pc.helper_segments.push_back(b1); + pc.helper_segments.push_back(range.front()); } inline void add_piece(strategy::buffer::piece_type type, point_type const& p, diff --git a/include/boost/geometry/strategies/cartesian/buffer_side.hpp b/include/boost/geometry/strategies/cartesian/buffer_side.hpp new file mode 100644 index 000000000..abae27e6f --- /dev/null +++ b/include/boost/geometry/strategies/cartesian/buffer_side.hpp @@ -0,0 +1,87 @@ +// 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_STRATEGIES_CARTESIAN_BUFFER_SIDE_HPP +#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_SIDE_HPP + +#include +#include + +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace buffer +{ + + +struct buffer_side +{ + + template + < + typename Point, + typename OutputRange, + typename DistanceStrategy + > + static inline void apply( + Point const& input_p1, Point const& input_p2, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance, + OutputRange& output_range) + { + typedef typename coordinate_type::type coordinate_type; + // Generate a block along (left or right of) the segment + + // Simulate a vector d (dx,dy) + coordinate_type dx = get<0>(input_p2) - get<0>(input_p1); + coordinate_type dy = get<1>(input_p2) - get<1>(input_p1); + + // For normalization [0,1] (=dot product d.d, sqrt) + // TODO promoted_type + coordinate_type const length = geometry::math::sqrt(dx * dx + dy * dy); + + // Because coordinates are not equal, length should not be zero + BOOST_ASSERT((! geometry::math::equals(length, 0))); + + // Generate the normalized perpendicular p, to the left (ccw) + coordinate_type const px = -dy / length; + coordinate_type const py = dx / length; + + coordinate_type const d = distance.apply(input_p1, input_p2, side); + + output_range.resize(2); + + set<0>(output_range.front(), get<0>(input_p1) + px * d); + set<1>(output_range.front(), get<1>(input_p1) + py * d); + set<0>(output_range.back(), get<0>(input_p2) + px * d); + set<1>(output_range.back(), get<1>(input_p2) + py * d); + } +}; + + +}} // namespace strategy::buffer + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_SIDE_HPP