diff options
Diffstat (limited to 'boost/geometry/strategies/cartesian/buffer_end_round.hpp')
-rw-r--r-- | boost/geometry/strategies/cartesian/buffer_end_round.hpp | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/boost/geometry/strategies/cartesian/buffer_end_round.hpp b/boost/geometry/strategies/cartesian/buffer_end_round.hpp new file mode 100644 index 0000000000..74780d6165 --- /dev/null +++ b/boost/geometry/strategies/cartesian/buffer_end_round.hpp @@ -0,0 +1,166 @@ +// 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_END_ROUND_HPP +#define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_END_ROUND_HPP + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/strategies/tags.hpp> +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/select_most_precise.hpp> + +#include <boost/geometry/strategies/buffer.hpp> + + +#include <boost/geometry/io/wkt/wkt.hpp> + +namespace boost { namespace geometry +{ + + +namespace strategy { namespace buffer +{ + + +/*! +\brief Let the buffer create rounded ends +\ingroup strategies +\details This strategy can be used as EndStrategy for the buffer algorithm. + It creates a rounded end for each linestring-end. It can be applied + for (multi)linestrings. Also it is applicable for spikes in (multi)polygons. + This strategy is only applicable for Cartesian coordinate systems. + +\qbk{ +[heading Example] +[buffer_end_round] +[heading Output] +[$img/strategies/buffer_end_round.png] +[heading See also] +\* [link geometry.reference.algorithms.buffer.buffer_7_with_strategies buffer (with strategies)] +\* [link geometry.reference.strategies.strategy_buffer_end_flat end_flat] +} + */ +class end_round +{ +private : + std::size_t m_points_per_circle; + + template + < + typename Point, + typename PromotedType, + typename DistanceType, + typename RangeOut + > + inline void generate_points(Point const& point, + PromotedType alpha, // by value + DistanceType const& buffer_distance, + RangeOut& range_out) const + { + PromotedType const two = 2.0; + PromotedType const two_pi = two * geometry::math::pi<PromotedType>(); + + std::size_t point_buffer_count = m_points_per_circle; + + PromotedType const diff = two_pi / PromotedType(point_buffer_count); + + // For half circle: + point_buffer_count /= 2; + point_buffer_count++; + + for (std::size_t i = 0; i < point_buffer_count; i++, alpha -= diff) + { + typename boost::range_value<RangeOut>::type p; + set<0>(p, get<0>(point) + buffer_distance * cos(alpha)); + set<1>(p, get<1>(point) + buffer_distance * sin(alpha)); + range_out.push_back(p); + } + } + + template <typename T, typename P1, typename P2> + static inline T calculate_angle(P1 const& from_point, P2 const& to_point) + { + typedef P1 vector_type; + vector_type v = from_point; + geometry::subtract_point(v, to_point); + return atan2(geometry::get<1>(v), geometry::get<0>(v)); + } + +public : + + //! \brief Constructs the strategy + //! \param points_per_circle points which would be used for a full circle + explicit inline end_round(std::size_t points_per_circle = 90) + : m_points_per_circle(points_per_circle) + {} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + //! Fills output_range with a flat end + template <typename Point, typename RangeOut, typename DistanceStrategy> + inline void apply(Point const& penultimate_point, + Point const& perp_left_point, + Point const& ultimate_point, + Point const& , + buffer_side_selector side, + DistanceStrategy const& distance, + RangeOut& range_out) const + { + typedef typename coordinate_type<Point>::type coordinate_type; + + typedef typename geometry::select_most_precise + < + coordinate_type, + double + >::type promoted_type; + + promoted_type const alpha = calculate_angle<promoted_type>(perp_left_point, ultimate_point); + + promoted_type const dist_left = distance.apply(penultimate_point, ultimate_point, buffer_side_left); + promoted_type const dist_right = distance.apply(penultimate_point, ultimate_point, buffer_side_right); + if (geometry::math::equals(dist_left, dist_right)) + { + generate_points(ultimate_point, alpha, dist_left, range_out); + } + else + { + promoted_type const two = 2.0; + promoted_type dist_half_diff = (dist_left - dist_right) / two; + + if (side == buffer_side_right) + { + dist_half_diff = -dist_half_diff; + } + + Point shifted_point; + set<0>(shifted_point, get<0>(ultimate_point) + dist_half_diff * cos(alpha)); + set<1>(shifted_point, get<1>(ultimate_point) + dist_half_diff * sin(alpha)); + generate_points(shifted_point, alpha, (dist_left + dist_right) / two, range_out); + } + } + + template <typename NumericType> + static inline NumericType max_distance(NumericType const& distance) + { + return distance; + } + + //! Returns the piece_type (flat end) + static inline piece_type get_piece_type() + { + return buffered_round_end; + } +#endif // DOXYGEN_SHOULD_SKIP_THIS +}; + + +}} // namespace strategy::buffer + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_END_ROUND_HPP |