// 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_BUFFER_INSERTER_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(BOOST_GEOMETRY_BUFFER_SIMPLIFY_WITH_AX) #include #endif namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace buffer { template inline void simplify_input(Range const& range, DistanceStrategy const& distance, Range& simplified) { // 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 gets rid of duplicate points #if ! defined(BOOST_GEOMETRY_BUFFER_SIMPLIFY_WITH_AX) geometry::simplify(range, simplified, distance.simplify_distance()); #else typedef typename boost::range_value::type point_type; typedef strategy::distance::detail::projected_point_ax<> ax_type; typedef typename strategy::distance::services::return_type < strategy::distance::detail::projected_point_ax<>, point_type, point_type >::type return_type; typedef strategy::distance::detail::projected_point_ax_less < return_type > comparator_type; typedef strategy::simplify::detail::douglas_peucker < point_type, strategy::distance::detail::projected_point_ax<>, comparator_type > dp_ax; return_type max_distance(distance.simplify_distance() * 2.0, distance.simplify_distance()); comparator_type comparator(max_distance); dp_ax strategy(comparator); geometry::simplify(range, simplified, max_distance, strategy); #endif if (boost::size(simplified) == 2 && geometry::equals(geometry::range::front(simplified), geometry::range::back(simplified))) { traits::resize::apply(simplified, 1); } } template struct buffer_range { typedef typename point_type::type output_point_type; typedef typename coordinate_type::type coordinate_type; template < typename Collection, typename Point, typename DistanceStrategy, typename JoinStrategy, typename EndStrategy, typename RobustPolicy > static inline void add_join(Collection& collection, 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 : { // 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 (future) - avoid a separate vector, add the piece directly std::vector range_out; if (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 SideStrategy, typename JoinStrategy, typename EndStrategy, typename RobustPolicy > static inline bool iterate(Collection& collection, Iterator begin, Iterator end, strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, RobustPolicy const& robust_policy, 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 < Iterator >::value_type point_type; typedef typename robust_point_type < point_type, RobustPolicy >::type robust_point_type; robust_point_type previous_robust_input; point_type second_point, penultimate_point, ultimate_point; // last two points from begin/end /* * last.p1 last.p2 these are the "previous (last) perpendicular points" * -------------- * | | * *------------*____ <- *prev * pup | | p1 "current perpendicular point 1" * | | * | | this forms a "side", a side is a piece * | | * *____| p2 * * ^ * *it * * pup: penultimate_point */ bool result = false; bool first = true; Iterator it = begin; 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; geometry::recalculate(robust_input, *it, robust_policy); // Check on equality - however, if input is simplified, this is // unlikely (though possible by rescaling or for degenerated pointlike polygons) if (! detail::equals::equals_point_point(previous_robust_input, robust_input)) { generated_side.clear(); side_strategy.apply(*prev, *it, side, distance_strategy, generated_side); if (generated_side.empty()) { break; } result = true; if (! first) { add_join(collection, penultimate_point, *prev, last_p1, last_p2, *it, generated_side.front(), generated_side.back(), side, distance_strategy, join_strategy, end_strategy, robust_policy); } collection.add_side_piece(*prev, *it, generated_side, first); penultimate_point = *prev; ultimate_point = *it; last_p1 = generated_side.front(); last_p2 = generated_side.back(); prev = it; if (first) { first = false; second_point = *it; first_p1 = generated_side.front(); first_p2 = generated_side.back(); } } previous_robust_input = robust_input; } return result; } }; template < typename Multi, typename PolygonOutput, typename Policy > struct buffer_multi { template < typename Collection, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > static inline void apply(Multi const& multi, Collection& collection, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_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_strategy, side_strategy, join_strategy, end_strategy, point_strategy, robust_policy); } } }; struct visit_pieces_default_policy { template static inline void apply(Collection const&, int) {} }; template < typename OutputPointType, typename Point, typename Collection, typename DistanceStrategy, typename PointStrategy > inline void buffer_point(Point const& point, Collection& collection, DistanceStrategy const& distance_strategy, PointStrategy const& point_strategy) { collection.start_new_ring(); std::vector range_out; point_strategy.apply(point, distance_strategy, range_out); collection.add_piece(strategy::buffer::buffered_point, range_out, false); collection.finish_ring(); } }} // 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 { template < typename Collection, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > static inline void apply(Point const& point, Collection& collection, DistanceStrategy const& distance_strategy, SideStrategy const& , JoinStrategy const& , EndStrategy const& , PointStrategy const& point_strategy, RobustPolicy const& ) { detail::buffer::buffer_point < typename point_type::type >(point, collection, distance_strategy, point_strategy); } }; template < typename RingInput, typename RingOutput > struct buffer_inserter { typedef typename point_type::type output_point_type; template < typename Collection, typename Iterator, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename RobustPolicy > static inline bool iterate(Collection& collection, Iterator begin, Iterator end, strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_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; bool result = buffer_range::iterate(collection, begin, end, side, distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, first_p1, first_p2, last_p1, last_p2); // Generate closing join if (result) { buffer_range::add_join(collection, *(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 return result; } template < typename Collection, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > static inline void apply(RingInput const& ring, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy) { RingInput simplified; detail::buffer::simplify_input(ring, distance, simplified); bool has_output = false; std::size_t n = boost::size(simplified); std::size_t const min_points = core_detail::closure::minimum_ring_size < geometry::closure::value >::value; if (n >= min_points) { detail::normalized_view view(simplified); if (distance.negative()) { // Walk backwards (rings will be reversed afterwards) // It might be that this will be changed later. // TODO: decide this. has_output = iterate(collection, boost::rbegin(view), boost::rend(view), strategy::buffer::buffer_side_right, distance, side_strategy, join_strategy, end_strategy, robust_policy); } else { has_output = iterate(collection, boost::begin(view), boost::end(view), strategy::buffer::buffer_side_left, distance, side_strategy, join_strategy, end_strategy, robust_policy); } } if (! has_output && n >= 1) { // Use point_strategy to buffer degenerated ring detail::buffer::buffer_point ( geometry::range::front(simplified), collection, distance, point_strategy ); } } }; template < typename Linestring, typename Polygon > struct buffer_inserter { 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 < typename Collection, typename Iterator, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename RobustPolicy > static inline bool iterate(Collection& collection, Iterator begin, Iterator end, strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_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; if (side == strategy::buffer::buffer_side_right) { reverse_p1 = first_p1; } else { std::vector generated_side; side_strategy.apply(ultimate_point, penultimate_point, strategy::buffer::buffer_side_right, distance_strategy, generated_side); if (generated_side.empty()) { return false; } reverse_p1 = generated_side.front(); } output_point_type first_p2, last_p1, last_p2; detail::buffer::buffer_range::iterate(collection, begin, end, side, distance_strategy, side_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); return true; } template < typename Collection, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > static inline void apply(Linestring const& linestring, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy) { Linestring simplified; detail::buffer::simplify_input(linestring, distance, simplified); bool has_output = false; std::size_t n = boost::size(simplified); if (n > 1) { collection.start_new_ring(); output_point_type first_p1; has_output = iterate(collection, boost::begin(simplified), boost::end(simplified), strategy::buffer::buffer_side_left, distance, side_strategy, join_strategy, end_strategy, robust_policy, first_p1); if (has_output) { iterate(collection, boost::rbegin(simplified), boost::rend(simplified), strategy::buffer::buffer_side_right, distance, side_strategy, join_strategy, end_strategy, robust_policy, first_p1); } collection.finish_ring(); } if (! has_output && n >= 1) { // Use point_strategy to buffer degenerated linestring detail::buffer::buffer_point ( geometry::range::front(simplified), collection, distance, point_strategy ); } } }; 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 SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > static inline void iterate(Iterator begin, Iterator end, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy, bool is_interior) { for (Iterator it = begin; it != end; ++it) { collection.start_new_ring(); policy::apply(*it, collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, robust_policy); collection.finish_ring(is_interior); } } template < typename InteriorRings, typename Collection, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > static inline void apply_interior_rings(InteriorRings const& interior_rings, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy) { iterate(boost::begin(interior_rings), boost::end(interior_rings), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, robust_policy, true); } public: template < typename Collection, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > static inline void apply(PolygonInput const& polygon, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy) { { collection.start_new_ring(); policy::apply(exterior_ring(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, robust_policy); collection.finish_ring(); } apply_interior_rings(interior_rings(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_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 #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace buffer { template < typename GeometryOutput, typename GeometryInput, typename OutputIterator, typename DistanceStrategy, typename SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy, typename VisitPiecesPolicy > inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_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; bool const areal = boost::is_same < typename tag_cast::type, areal_tag>::type, areal_tag >::type::value; dispatch::buffer_inserter < typename tag_cast < typename tag::type, multi_tag >::type, GeometryInput, GeometryOutput >::apply(geometry_input, collection, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, robust_policy); collection.get_turns(); if (areal) { collection.check_remaining_points(distance_strategy.factor()); } // 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.block_turns(); collection.enrich(); collection.traverse(); // Reverse all offsetted rings / traversed rings if: // - they were generated on the negative side (deflate) of polygons // - the output is counter clockwise // and avoid reversing twice bool reverse = distance_strategy.negative() && areal; if (geometry::point_order::value == counterclockwise) { reverse = ! reverse; } if (reverse) { 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 SideStrategy, typename JoinStrategy, typename EndStrategy, typename PointStrategy, typename RobustPolicy > inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator out, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy) { detail::buffer::visit_pieces_default_policy visitor; buffer_inserter(geometry_input, out, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, robust_policy, visitor); } #endif // DOXYGEN_NO_DETAIL }} // namespace detail::buffer }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_BUFFER_INSERTER_HPP