From 01151cacf571bd3ff344aff97ef0e1fbb440ff71 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Jul 2014 12:29:34 +0200 Subject: [PATCH 1/5] [buffer] copy inserter to buffer_side --- .../algorithms/detail/buffer/buffer_side.hpp | 802 ++++++++++++++++++ 1 file changed, 802 insertions(+) create mode 100644 include/boost/geometry/algorithms/detail/buffer/buffer_side.hpp diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_side.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_side.hpp new file mode 100644 index 000000000..ee90e9a05 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_side.hpp @@ -0,0 +1,802 @@ +// 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 +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + + +template +< + typename RingOutput, + typename Tag +> +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, + typename Point, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline + void add_join(Collection& collection, + int phase, + Point const& penultimate_input, + Point const& previous_input, + output_point_type const& prev_perp1, + output_point_type const& prev_perp2, + Point const& input, + output_point_type const& perp1, + output_point_type const& perp2, + strategy::buffer::buffer_side_selector side, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& ) + { + output_point_type intersection_point; + + strategy::buffer::join_selector join + = get_join_type(penultimate_input, previous_input, input); + if (join == strategy::buffer::join_convex) + { + // Calculate the intersection-point formed by the two sides. + // It might be that the two sides are not convex, but continue + // or spikey, we then change the join-type + join = line_line_intersection::apply( + perp1, perp2, prev_perp1, prev_perp2, + intersection_point); + + } + switch(join) + { + case strategy::buffer::join_continue : + // No join, we get two consecutive sides + return; + case strategy::buffer::join_concave : + collection.add_piece(strategy::buffer::buffered_concave, + previous_input, prev_perp2, perp1); + return; + case strategy::buffer::join_spike : + //if (phase == 0) avoid duplicate joins at spikes? this still causes other issues + { + // For linestrings, only add spike at one side to avoid + // duplicates + std::vector range_out; + end_strategy.apply(penultimate_input, prev_perp2, previous_input, perp1, side, distance, range_out); + collection.add_endcap(end_strategy, range_out, previous_input); + } + return; + case strategy::buffer::join_convex : + break; // All code below handles this + } + + // The corner is convex, we create a join + // TODO - try to avoid a separate vector, add the piece directly + std::vector range_out; + join_strategy.apply(intersection_point, + previous_input, prev_perp2, perp1, + distance.apply(previous_input, input, side), + range_out); + collection.add_piece(strategy::buffer::buffered_join, + previous_input, range_out); + } + + static inline strategy::buffer::join_selector get_join_type( + output_point_type const& p0, + output_point_type const& p1, + output_point_type const& p2) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + int const side = side_strategy::apply(p0, p1, p2); + return side == -1 ? strategy::buffer::join_convex + : side == 1 ? strategy::buffer::join_concave + : parallel_continue + ( + get<0>(p2) - get<0>(p1), + get<1>(p2) - get<1>(p1), + get<0>(p1) - get<0>(p0), + get<1>(p1) - get<1>(p0) + ) ? strategy::buffer::join_continue + : strategy::buffer::join_spike; + } + + template + < + typename Collection, + typename Iterator, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void iterate(Collection& collection, + int phase, // 0/1 for left/right of rings. For polygons: 0 + 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, + bool /*close*/ = false) + { + typedef typename std::iterator_traits + < + Iterator + >::value_type point_type; + + typedef typename robust_point_type + < + point_type, + RobustPolicy + >::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" + * -------------- + * | | + * *------------*____ <- *prev + * pup | | p1 "current perpendiculat point 1" + * | | + * | | this forms a "side", a side is a piece + * | | + * *____| p2 + * + * ^ + * *it + * + * pup: penultimate_point + */ + + bool first = true; + + Iterator it = begin; + + geometry::recalculate(previous_robust_input, *begin, robust_policy); + + for (Iterator prev = it++; it != end; ++it) + { + robust_point_type robust_input; + geometry::recalculate(robust_input, *it, robust_policy); + // Check on equality - however, if input is simplified, this is highly + // 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); + + if (! first) + { + add_join(collection, phase, + penultimate_point, + *prev, previous_p1, previous_p2, + *it, p1, p2, + side, + distance_strategy, join_strategy, end_strategy, + robust_policy); + } + collection.add_piece(strategy::buffer::buffered_segment, + *prev, *it, p1, p2, first); + + penultimate_point = *prev; + ultimate_point = *it; + previous_p1 = p1; + previous_p2 = p2; + prev = it; + if (first) + { + first = false; + second_point = *it; + first_p1 = p1; + first_p2 = p2; + } + } + 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); + } + } +}; + + + +template +< + typename Point, + typename RingOutput +> +struct buffer_point +{ + typedef typename point_type::type output_point_type; + typedef typename coordinate_type::type coordinate_type; + + typedef typename geometry::select_most_precise + < + typename geometry::select_most_precise + < + typename geometry::coordinate_type::type, + typename geometry::coordinate_type::type + >::type, + double + >::type promoted_type; + + + template + static inline void generate_points(Point const& point, + promoted_type const& buffer_distance, + RangeOut& range_out) + { + + promoted_type two = 2.0; + promoted_type two_pi = two * geometry::math::pi(); + int point_buffer_count = 88; // 88 gives now fixed problem (collinear opposite / robustness. TODO: make this value flexible + + promoted_type diff = two_pi / promoted_type(point_buffer_count); + promoted_type a = 0; + + output_point_type first; + for (int i = 0; i < point_buffer_count; i++, a -= diff) + { + output_point_type p; + set<0>(p, get<0>(point) + buffer_distance * cos(a)); + set<1>(p, get<1>(point) + buffer_distance * sin(a)); + range_out.push_back(p); + if (i == 0) + { + first = p; + } + } + + // Close it: + range_out.push_back(first); + } + + + template + < + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void generate_circle(Point const& point, + Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& , + EndStrategy const& , + RobustPolicy const& ) + { + std::vector range_out; + + generate_points(point, + distance.apply(point, point, strategy::buffer::buffer_side_left), + range_out); + + collection.add_piece(strategy::buffer::buffered_circle, range_out, false); + } +}; + +template +< + typename Multi, + typename PolygonOutput, + typename Policy +> +struct buffer_multi +{ + template + < + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void apply(Multi const& multi, + Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + for (typename boost::range_iterator::type + it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it, collection, distance, join_strategy, end_strategy, robust_policy); + } + } +}; + +struct visit_pieces_default_policy +{ + template + static inline void apply(Collection const&, int) + {} +}; + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename RingInput, + typename RingOutput +> +struct buffer_inserter +{}; + + + +template +< + typename Point, + typename RingOutput +> +struct buffer_inserter + : public detail::buffer::buffer_point +{ + template + < + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void apply(Point const& point, Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + collection.start_new_ring(); + typedef detail::buffer::buffer_point base; + base::generate_circle(point, collection, distance, join_strategy, end_strategy, robust_policy); + } +}; + + +template +< + typename RingInput, + typename RingOutput +> +struct buffer_inserter + : public detail::buffer::buffer_range + < + RingOutput, + ring_tag + > +{ + typedef detail::buffer::buffer_range + < + RingOutput, + ring_tag + > base; + + template + < + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void apply(RingInput const& ring, + Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + if (boost::size(ring) > 3) + { + // We have to simplify the ring before to avoid very small-scaled + // features in the original (convex/concave/convex) being enlarged + // in a very large scale and causing issues (IP's within pieces). + // This might be reconsidered later. Simplifying with a very small + // distance (1%% of the buffer) will never be visible in the result, + // if it is using round joins. For miter joins they are even more + // sensitive to small scale input features, however the result will + // look better. + // It also get rid of duplicate points + RingOutput simplified; + geometry::simplify(ring, simplified, distance.simplify_distance()); + + if (distance.negative()) + { + // 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), + strategy::buffer::buffer_side_right, + distance, join_strategy, end_strategy, robust_policy); + } + else + { + base::iterate(collection, 0, boost::begin(simplified), boost::end(simplified), + strategy::buffer::buffer_side_left, + distance, join_strategy, end_strategy, robust_policy); + } + } + } +}; + + +template +< + typename Linestring, + 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; + + template + < + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void apply(Linestring const& linestring, Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + if (boost::size(linestring) > 1) + { + Linestring simplified; + geometry::simplify(linestring, simplified, distance.simplify_distance()); + + collection.start_new_ring(); + base::iterate(collection, 0, boost::begin(simplified), boost::end(simplified), + strategy::buffer::buffer_side_left, + distance, join_strategy, end_strategy, robust_policy); + + base::iterate(collection, 1, boost::rbegin(simplified), boost::rend(simplified), + strategy::buffer::buffer_side_right, + distance, join_strategy, end_strategy, robust_policy, true); + } + + } +}; + + +template +< + typename PolygonInput, + typename PolygonOutput +> +struct buffer_inserter +{ +private: + typedef typename ring_type::type input_ring_type; + typedef typename ring_type::type output_ring_type; + + typedef buffer_inserter policy; + + + template + < + typename Iterator, + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline + void iterate(Iterator begin, Iterator end, + Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + for (Iterator it = begin; it != end; ++it) + { + collection.start_new_ring(); + policy::apply(*it, collection, distance, join_strategy, end_strategy, robust_policy); + } + } + + template + < + typename InteriorRings, + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline + void apply_interior_rings(InteriorRings const& interior_rings, + Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + iterate(boost::begin(interior_rings), boost::end(interior_rings), + collection, distance, join_strategy, end_strategy, robust_policy); + } + +public: + template + < + typename Collection, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy + > + static inline void apply(PolygonInput const& polygon, + Collection& collection, + DistanceStrategy const& distance, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) + { + { + collection.start_new_ring(); + policy::apply(exterior_ring(polygon), collection, + distance, join_strategy, end_strategy, robust_policy); + } + + apply_interior_rings(interior_rings(polygon), + collection, distance, join_strategy, end_strategy, robust_policy); + } +}; + + +template +< + typename Multi, + typename PolygonOutput +> +struct buffer_inserter + : public detail::buffer::buffer_multi + < + Multi, + PolygonOutput, + dispatch::buffer_inserter + < + typename single_tag_of + < + typename tag::type + >::type, + typename boost::range_value::type, + typename geometry::ring_type::type + > + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +template +< + typename GeometryOutput, + typename GeometryInput, + typename OutputIterator, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy, + typename VisitPiecesPolicy +> +inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, + DistanceStrategy const& distance_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy, + VisitPiecesPolicy& visit_pieces_policy + ) +{ + typedef detail::buffer::buffered_piece_collection + < + typename geometry::ring_type::type, + RobustPolicy + > collection_type; + collection_type collection(robust_policy); + collection_type const& const_collection = collection; + + dispatch::buffer_inserter + < + typename tag_cast + < + typename tag::type, + multi_tag + >::type, + GeometryInput, + GeometryOutput + >::apply(geometry_input, collection, distance_strategy, join_strategy, end_strategy, robust_policy); + + collection.get_turns(geometry_input, distance_strategy); + + // Visit the piece collection. This does nothing (by default), but + // optionally a debugging tool can be attached (e.g. console or svg), + // or the piece collection can be unit-tested + // phase 0: turns (before discarded) + visit_pieces_policy.apply(const_collection, 0); + + collection.discard_rings(); + collection.discard_turns(); + collection.enrich(); + collection.traverse(); + + if (distance_strategy.negative() + && boost::is_same + < + typename tag_cast::type, areal_tag>::type, + areal_tag + >::type::value) + { + collection.reverse(); + } + + collection.template assign(out); + + // Visit collection again + // phase 1: rings (after discarding and traversing) + visit_pieces_policy.apply(const_collection, 1); +} + +template +< + typename GeometryOutput, + typename GeometryInput, + typename OutputIterator, + typename DistanceStrategy, + typename JoinStrategy, + typename EndStrategy, + typename RobustPolicy +> +inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, + DistanceStrategy const& distance_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + RobustPolicy const& robust_policy) +{ + detail::buffer::visit_pieces_default_policy visitor; + buffer_inserter(geometry_input, out, + distance_strategy, join_strategy, end_strategy, + robust_policy, visitor); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_SIDE_HPP From f933be730d6892c5d316da0172c7dc2570220514 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Jul 2014 12:33:04 +0200 Subject: [PATCH 2/5] [buffer] move buffer_side to strategies --- .../detail/buffer => strategies/cartesian}/buffer_side.hpp | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/boost/geometry/{algorithms/detail/buffer => strategies/cartesian}/buffer_side.hpp (100%) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_side.hpp b/include/boost/geometry/strategies/cartesian/buffer_side.hpp similarity index 100% rename from include/boost/geometry/algorithms/detail/buffer/buffer_side.hpp rename to include/boost/geometry/strategies/cartesian/buffer_side.hpp From c94d6d469d2c5a0f29ec3868397263f9c591f7da Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Jul 2014 12:45:57 +0200 Subject: [PATCH 3/5] [buffer] use strategy buffer_side instead of generate_side --- .../detail/buffer/buffer_inserter.hpp | 44 +- .../strategies/cartesian/buffer_side.hpp | 744 +----------------- 2 files changed, 20 insertions(+), 768 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index bf8240ae8..adae2205f 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 @@ -48,42 +49,6 @@ 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, @@ -247,7 +212,9 @@ struct buffer_range 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); + + strategy::buffer::buffer_side::apply(*prev, + *it, side, distance_strategy, p1, p2); if (! first) { @@ -299,7 +266,8 @@ struct buffer_range // 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, + strategy::buffer::buffer_side::apply(ultimate_point, + penultimate_point, side == strategy::buffer::buffer_side_left ? strategy::buffer::buffer_side_right : strategy::buffer::buffer_side_left, diff --git a/include/boost/geometry/strategies/cartesian/buffer_side.hpp b/include/boost/geometry/strategies/cartesian/buffer_side.hpp index ee90e9a05..593fd1ea2 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_side.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_side.hpp @@ -1,4 +1,5 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// 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) @@ -30,32 +31,27 @@ namespace boost { namespace geometry { -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace buffer +namespace strategy { namespace buffer { -template -< - typename RingOutput, - typename Tag -> -struct buffer_range +struct buffer_side { - 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( + < + typename Point, + typename OutputPointType, + typename DistanceStrategy + > + static inline void apply( 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) + // TODO: the output will be a range + OutputPointType& side_p1, OutputPointType& side_p2) { + typedef typename coordinate_type::type coordinate_type; // Generate a block along (left or right of) the segment // Simulate a vector d (dx,dy) @@ -80,722 +76,10 @@ struct buffer_range set<0>(side_p2, get<0>(input_p2) + px * d); set<1>(side_p2, get<1>(input_p2) + py * d); } - - template - < - typename Collection, - typename Point, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline - void add_join(Collection& collection, - int phase, - Point const& penultimate_input, - Point const& previous_input, - output_point_type const& prev_perp1, - output_point_type const& prev_perp2, - Point const& input, - output_point_type const& perp1, - output_point_type const& perp2, - strategy::buffer::buffer_side_selector side, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& ) - { - output_point_type intersection_point; - - strategy::buffer::join_selector join - = get_join_type(penultimate_input, previous_input, input); - if (join == strategy::buffer::join_convex) - { - // Calculate the intersection-point formed by the two sides. - // It might be that the two sides are not convex, but continue - // or spikey, we then change the join-type - join = line_line_intersection::apply( - perp1, perp2, prev_perp1, prev_perp2, - intersection_point); - - } - switch(join) - { - case strategy::buffer::join_continue : - // No join, we get two consecutive sides - return; - case strategy::buffer::join_concave : - collection.add_piece(strategy::buffer::buffered_concave, - previous_input, prev_perp2, perp1); - return; - case strategy::buffer::join_spike : - //if (phase == 0) avoid duplicate joins at spikes? this still causes other issues - { - // For linestrings, only add spike at one side to avoid - // duplicates - std::vector range_out; - end_strategy.apply(penultimate_input, prev_perp2, previous_input, perp1, side, distance, range_out); - collection.add_endcap(end_strategy, range_out, previous_input); - } - return; - case strategy::buffer::join_convex : - break; // All code below handles this - } - - // The corner is convex, we create a join - // TODO - try to avoid a separate vector, add the piece directly - std::vector range_out; - join_strategy.apply(intersection_point, - previous_input, prev_perp2, perp1, - distance.apply(previous_input, input, side), - range_out); - collection.add_piece(strategy::buffer::buffered_join, - previous_input, range_out); - } - - static inline strategy::buffer::join_selector get_join_type( - output_point_type const& p0, - output_point_type const& p1, - output_point_type const& p2) - { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - - int const side = side_strategy::apply(p0, p1, p2); - return side == -1 ? strategy::buffer::join_convex - : side == 1 ? strategy::buffer::join_concave - : parallel_continue - ( - get<0>(p2) - get<0>(p1), - get<1>(p2) - get<1>(p1), - get<0>(p1) - get<0>(p0), - get<1>(p1) - get<1>(p0) - ) ? strategy::buffer::join_continue - : strategy::buffer::join_spike; - } - - template - < - typename Collection, - typename Iterator, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline void iterate(Collection& collection, - int phase, // 0/1 for left/right of rings. For polygons: 0 - 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, - bool /*close*/ = false) - { - typedef typename std::iterator_traits - < - Iterator - >::value_type point_type; - - typedef typename robust_point_type - < - point_type, - RobustPolicy - >::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" - * -------------- - * | | - * *------------*____ <- *prev - * pup | | p1 "current perpendiculat point 1" - * | | - * | | this forms a "side", a side is a piece - * | | - * *____| p2 - * - * ^ - * *it - * - * pup: penultimate_point - */ - - bool first = true; - - Iterator it = begin; - - geometry::recalculate(previous_robust_input, *begin, robust_policy); - - for (Iterator prev = it++; it != end; ++it) - { - robust_point_type robust_input; - geometry::recalculate(robust_input, *it, robust_policy); - // Check on equality - however, if input is simplified, this is highly - // 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); - - if (! first) - { - add_join(collection, phase, - penultimate_point, - *prev, previous_p1, previous_p2, - *it, p1, p2, - side, - distance_strategy, join_strategy, end_strategy, - robust_policy); - } - collection.add_piece(strategy::buffer::buffered_segment, - *prev, *it, p1, p2, first); - - penultimate_point = *prev; - ultimate_point = *it; - previous_p1 = p1; - previous_p2 = p2; - prev = it; - if (first) - { - first = false; - second_point = *it; - first_p1 = p1; - first_p2 = p2; - } - } - 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); - } - } }; - -template -< - typename Point, - typename RingOutput -> -struct buffer_point -{ - typedef typename point_type::type output_point_type; - typedef typename coordinate_type::type coordinate_type; - - typedef typename geometry::select_most_precise - < - typename geometry::select_most_precise - < - typename geometry::coordinate_type::type, - typename geometry::coordinate_type::type - >::type, - double - >::type promoted_type; - - - template - static inline void generate_points(Point const& point, - promoted_type const& buffer_distance, - RangeOut& range_out) - { - - promoted_type two = 2.0; - promoted_type two_pi = two * geometry::math::pi(); - int point_buffer_count = 88; // 88 gives now fixed problem (collinear opposite / robustness. TODO: make this value flexible - - promoted_type diff = two_pi / promoted_type(point_buffer_count); - promoted_type a = 0; - - output_point_type first; - for (int i = 0; i < point_buffer_count; i++, a -= diff) - { - output_point_type p; - set<0>(p, get<0>(point) + buffer_distance * cos(a)); - set<1>(p, get<1>(point) + buffer_distance * sin(a)); - range_out.push_back(p); - if (i == 0) - { - first = p; - } - } - - // Close it: - range_out.push_back(first); - } - - - template - < - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline void generate_circle(Point const& point, - Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& , - EndStrategy const& , - RobustPolicy const& ) - { - std::vector range_out; - - generate_points(point, - distance.apply(point, point, strategy::buffer::buffer_side_left), - range_out); - - collection.add_piece(strategy::buffer::buffered_circle, range_out, false); - } -}; - -template -< - typename Multi, - typename PolygonOutput, - typename Policy -> -struct buffer_multi -{ - template - < - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline void apply(Multi const& multi, - Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) - { - for (typename boost::range_iterator::type - it = boost::begin(multi); - it != boost::end(multi); - ++it) - { - Policy::apply(*it, collection, distance, join_strategy, end_strategy, robust_policy); - } - } -}; - -struct visit_pieces_default_policy -{ - template - static inline void apply(Collection const&, int) - {} -}; - -}} // namespace detail::buffer -#endif // DOXYGEN_NO_DETAIL - - -#ifndef DOXYGEN_NO_DISPATCH -namespace dispatch -{ - -template -< - typename Tag, - typename RingInput, - typename RingOutput -> -struct buffer_inserter -{}; - - - -template -< - typename Point, - typename RingOutput -> -struct buffer_inserter - : public detail::buffer::buffer_point -{ - template - < - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline void apply(Point const& point, Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) - { - collection.start_new_ring(); - typedef detail::buffer::buffer_point base; - base::generate_circle(point, collection, distance, join_strategy, end_strategy, robust_policy); - } -}; - - -template -< - typename RingInput, - typename RingOutput -> -struct buffer_inserter - : public detail::buffer::buffer_range - < - RingOutput, - ring_tag - > -{ - typedef detail::buffer::buffer_range - < - RingOutput, - ring_tag - > base; - - template - < - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline void apply(RingInput const& ring, - Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) - { - if (boost::size(ring) > 3) - { - // We have to simplify the ring before to avoid very small-scaled - // features in the original (convex/concave/convex) being enlarged - // in a very large scale and causing issues (IP's within pieces). - // This might be reconsidered later. Simplifying with a very small - // distance (1%% of the buffer) will never be visible in the result, - // if it is using round joins. For miter joins they are even more - // sensitive to small scale input features, however the result will - // look better. - // It also get rid of duplicate points - RingOutput simplified; - geometry::simplify(ring, simplified, distance.simplify_distance()); - - if (distance.negative()) - { - // 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), - strategy::buffer::buffer_side_right, - distance, join_strategy, end_strategy, robust_policy); - } - else - { - base::iterate(collection, 0, boost::begin(simplified), boost::end(simplified), - strategy::buffer::buffer_side_left, - distance, join_strategy, end_strategy, robust_policy); - } - } - } -}; - - -template -< - typename Linestring, - 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; - - template - < - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline void apply(Linestring const& linestring, Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) - { - if (boost::size(linestring) > 1) - { - Linestring simplified; - geometry::simplify(linestring, simplified, distance.simplify_distance()); - - collection.start_new_ring(); - base::iterate(collection, 0, boost::begin(simplified), boost::end(simplified), - strategy::buffer::buffer_side_left, - distance, join_strategy, end_strategy, robust_policy); - - base::iterate(collection, 1, boost::rbegin(simplified), boost::rend(simplified), - strategy::buffer::buffer_side_right, - distance, join_strategy, end_strategy, robust_policy, true); - } - - } -}; - - -template -< - typename PolygonInput, - typename PolygonOutput -> -struct buffer_inserter -{ -private: - typedef typename ring_type::type input_ring_type; - typedef typename ring_type::type output_ring_type; - - typedef buffer_inserter policy; - - - template - < - typename Iterator, - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline - void iterate(Iterator begin, Iterator end, - Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) - { - for (Iterator it = begin; it != end; ++it) - { - collection.start_new_ring(); - policy::apply(*it, collection, distance, join_strategy, end_strategy, robust_policy); - } - } - - template - < - typename InteriorRings, - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline - void apply_interior_rings(InteriorRings const& interior_rings, - Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) - { - iterate(boost::begin(interior_rings), boost::end(interior_rings), - collection, distance, join_strategy, end_strategy, robust_policy); - } - -public: - template - < - typename Collection, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy - > - static inline void apply(PolygonInput const& polygon, - Collection& collection, - DistanceStrategy const& distance, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) - { - { - collection.start_new_ring(); - policy::apply(exterior_ring(polygon), collection, - distance, join_strategy, end_strategy, robust_policy); - } - - apply_interior_rings(interior_rings(polygon), - collection, distance, join_strategy, end_strategy, robust_policy); - } -}; - - -template -< - typename Multi, - typename PolygonOutput -> -struct buffer_inserter - : public detail::buffer::buffer_multi - < - Multi, - PolygonOutput, - dispatch::buffer_inserter - < - typename single_tag_of - < - typename tag::type - >::type, - typename boost::range_value::type, - typename geometry::ring_type::type - > - > -{}; - - -} // namespace dispatch -#endif // DOXYGEN_NO_DISPATCH - -template -< - typename GeometryOutput, - typename GeometryInput, - typename OutputIterator, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy, - typename VisitPiecesPolicy -> -inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, - DistanceStrategy const& distance_strategy, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy, - VisitPiecesPolicy& visit_pieces_policy - ) -{ - typedef detail::buffer::buffered_piece_collection - < - typename geometry::ring_type::type, - RobustPolicy - > collection_type; - collection_type collection(robust_policy); - collection_type const& const_collection = collection; - - dispatch::buffer_inserter - < - typename tag_cast - < - typename tag::type, - multi_tag - >::type, - GeometryInput, - GeometryOutput - >::apply(geometry_input, collection, distance_strategy, join_strategy, end_strategy, robust_policy); - - collection.get_turns(geometry_input, distance_strategy); - - // Visit the piece collection. This does nothing (by default), but - // optionally a debugging tool can be attached (e.g. console or svg), - // or the piece collection can be unit-tested - // phase 0: turns (before discarded) - visit_pieces_policy.apply(const_collection, 0); - - collection.discard_rings(); - collection.discard_turns(); - collection.enrich(); - collection.traverse(); - - if (distance_strategy.negative() - && boost::is_same - < - typename tag_cast::type, areal_tag>::type, - areal_tag - >::type::value) - { - collection.reverse(); - } - - collection.template assign(out); - - // Visit collection again - // phase 1: rings (after discarding and traversing) - visit_pieces_policy.apply(const_collection, 1); -} - -template -< - typename GeometryOutput, - typename GeometryInput, - typename OutputIterator, - typename DistanceStrategy, - typename JoinStrategy, - typename EndStrategy, - typename RobustPolicy -> -inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, - DistanceStrategy const& distance_strategy, - JoinStrategy const& join_strategy, - EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) -{ - detail::buffer::visit_pieces_default_policy visitor; - buffer_inserter(geometry_input, out, - distance_strategy, join_strategy, end_strategy, - robust_policy, visitor); -} +}} // namespace strategy::buffer }} // namespace boost::geometry From c9880fbdffae3a6b124d3ca19baecc2a922fc48c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Jul 2014 13:04:16 +0200 Subject: [PATCH 4/5] [buffer] use range instead of two points as output for buffer_side strategy --- .../detail/buffer/buffer_inserter.hpp | 27 ++++++++++--------- .../buffer/buffered_piece_collection.hpp | 11 ++++---- .../strategies/cartesian/buffer_side.hpp | 15 ++++++----- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index adae2205f..943a62869 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -203,6 +203,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; @@ -211,35 +214,35 @@ struct buffer_range // unlikely (though possible by rescaling) if (! detail::equals::equals_point_point(previous_robust_input, robust_input)) { - output_point_type p1, p2; - + generated_side.clear(); strategy::buffer::buffer_side::apply(*prev, - *it, side, distance_strategy, p1, p2); + *it, side, distance_strategy, generated_side); if (! first) { add_join(collection, phase, penultimate_point, *prev, previous_p1, previous_p2, - *it, p1, 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; + previous_p1 = generated_side.front(); + previous_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; @@ -265,16 +268,16 @@ struct buffer_range // 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; + generated_side.clear(); strategy::buffer::buffer_side::apply(ultimate_point, penultimate_point, side == strategy::buffer::buffer_side_left ? strategy::buffer::buffer_side_right : strategy::buffer::buffer_side_left, - distance_strategy, rp2, rp1); + distance_strategy, generated_side); std::vector range_out; - end_strategy.apply(penultimate_point, previous_p2, ultimate_point, rp2, side, distance_strategy, range_out); + end_strategy.apply(penultimate_point, previous_p2, ultimate_point, generated_side.front(), side, distance_strategy, range_out); collection.add_endcap(end_strategy, range_out, ultimate_point); } } 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 index 593fd1ea2..abae27e6f 100644 --- a/include/boost/geometry/strategies/cartesian/buffer_side.hpp +++ b/include/boost/geometry/strategies/cartesian/buffer_side.hpp @@ -41,15 +41,14 @@ struct buffer_side template < typename Point, - typename OutputPointType, + 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, - // TODO: the output will be a range - OutputPointType& side_p1, OutputPointType& side_p2) + OutputRange& output_range) { typedef typename coordinate_type::type coordinate_type; // Generate a block along (left or right of) the segment @@ -71,10 +70,12 @@ struct buffer_side 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); + 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); } }; From 5a008767aef63f5d014335b6d2e820ea25fb9b3e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 2 Jul 2014 14:20:02 +0200 Subject: [PATCH 5/5] [buffer] restructure buffer_range such that tag is not necessary and behaviour per geometry-type is done in the dispatch itself. This also avoids generating side on perpendicular side for linestrings once (the second one cannot easily be avoided in the current structure) --- .../detail/buffer/buffer_inserter.hpp | 189 +++++++++++------- 1 file changed, 117 insertions(+), 72 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index 943a62869..7207d0dc5 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -39,11 +39,7 @@ namespace detail { namespace buffer { -template -< - typename RingOutput, - typename Tag -> +template struct buffer_range { typedef typename point_type::type output_point_type; @@ -162,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 < @@ -176,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 * | | @@ -222,7 +219,7 @@ struct buffer_range { add_join(collection, phase, penultimate_point, - *prev, previous_p1, previous_p2, + *prev, last_p1, last_p2, *it, generated_side.front(), generated_side.back(), side, distance_strategy, join_strategy, end_strategy, @@ -234,8 +231,8 @@ struct buffer_range penultimate_point = *prev; ultimate_point = *it; - previous_p1 = generated_side.front(); - previous_p2 = generated_side.back(); + last_p1 = generated_side.front(); + last_p2 = generated_side.back(); prev = it; if (first) { @@ -247,39 +244,6 @@ struct buffer_range } 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) - generated_side.clear(); - strategy::buffer::buffer_side::apply(ultimate_point, - penultimate_point, - side == strategy::buffer::buffer_side_left - ? strategy::buffer::buffer_side_right - : strategy::buffer::buffer_side_left, - distance_strategy, generated_side); - - std::vector range_out; - end_strategy.apply(penultimate_point, previous_p2, ultimate_point, generated_side.front(), side, distance_strategy, range_out); - collection.add_endcap(end_strategy, range_out, ultimate_point); - } } }; @@ -456,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 < @@ -502,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); } + } } }; @@ -523,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 < @@ -555,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); } }