1 // Boost.Geometry (aka GGL, Generic Geometry Library) 2 3 // Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands. 4 5 // This file was modified by Oracle on 2015. 6 // Modifications copyright (c) 2015, Oracle and/or its affiliates. 7 8 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle 9 10 // Use, modification and distribution is subject to the Boost Software License, 11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at 12 // http://www.boost.org/LICENSE_1_0.txt) 13 14 #ifndef BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_END_ROUND_HPP 15 #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_END_ROUND_HPP 16 17 #include <boost/geometry/core/cs.hpp> 18 #include <boost/geometry/strategies/tags.hpp> 19 #include <boost/geometry/util/math.hpp> 20 #include <boost/geometry/util/select_most_precise.hpp> 21 22 #include <boost/geometry/strategies/buffer.hpp> 23 24 25 #include <boost/geometry/io/wkt/wkt.hpp> 26 27 namespace boost { namespace geometry 28 { 29 30 31 namespace strategy { namespace buffer 32 { 33 34 35 /*! 36 \brief Let the buffer create rounded ends 37 \ingroup strategies 38 \details This strategy can be used as EndStrategy for the buffer algorithm. 39 It creates a rounded end for each linestring-end. It can be applied 40 for (multi)linestrings. Also it is applicable for spikes in (multi)polygons. 41 This strategy is only applicable for Cartesian coordinate systems. 42 43 \qbk{ 44 [heading Example] 45 [buffer_end_round] 46 [heading Output] 47 [$img/strategies/buffer_end_round.png] 48 [heading See also] 49 \* [link geometry.reference.algorithms.buffer.buffer_7_with_strategies buffer (with strategies)] 50 \* [link geometry.reference.strategies.strategy_buffer_end_flat end_flat] 51 } 52 */ 53 class end_round 54 { 55 private : 56 std::size_t m_points_per_circle; 57 58 template 59 < 60 typename Point, 61 typename PromotedType, 62 typename DistanceType, 63 typename RangeOut 64 > generate_points(Point const & point,PromotedType alpha,DistanceType const & buffer_distance,RangeOut & range_out) const65 inline void generate_points(Point const& point, 66 PromotedType alpha, // by value 67 DistanceType const& buffer_distance, 68 RangeOut& range_out) const 69 { 70 PromotedType const two_pi = geometry::math::two_pi<PromotedType>(); 71 72 std::size_t point_buffer_count = m_points_per_circle; 73 74 PromotedType const diff = two_pi / PromotedType(point_buffer_count); 75 76 // For half circle: 77 point_buffer_count /= 2; 78 point_buffer_count++; 79 80 for (std::size_t i = 0; i < point_buffer_count; i++, alpha -= diff) 81 { 82 typename boost::range_value<RangeOut>::type p; 83 set<0>(p, get<0>(point) + buffer_distance * cos(alpha)); 84 set<1>(p, get<1>(point) + buffer_distance * sin(alpha)); 85 range_out.push_back(p); 86 } 87 } 88 89 template <typename T, typename P1, typename P2> calculate_angle(P1 const & from_point,P2 const & to_point)90 static inline T calculate_angle(P1 const& from_point, P2 const& to_point) 91 { 92 typedef P1 vector_type; 93 vector_type v = from_point; 94 geometry::subtract_point(v, to_point); 95 return atan2(geometry::get<1>(v), geometry::get<0>(v)); 96 } 97 98 public : 99 100 //! \brief Constructs the strategy 101 //! \param points_per_circle points which would be used for a full circle 102 //! (if points_per_circle is smaller than 4, it is internally set to 4) end_round(std::size_t points_per_circle=90)103 explicit inline end_round(std::size_t points_per_circle = 90) 104 : m_points_per_circle((points_per_circle < 4u) ? 4u : points_per_circle) 105 {} 106 107 #ifndef DOXYGEN_SHOULD_SKIP_THIS 108 109 //! Fills output_range with a flat end 110 template <typename Point, typename RangeOut, typename DistanceStrategy> apply(Point const & penultimate_point,Point const & perp_left_point,Point const & ultimate_point,Point const & perp_right_point,buffer_side_selector side,DistanceStrategy const & distance,RangeOut & range_out) const111 inline void apply(Point const& penultimate_point, 112 Point const& perp_left_point, 113 Point const& ultimate_point, 114 Point const& perp_right_point, 115 buffer_side_selector side, 116 DistanceStrategy const& distance, 117 RangeOut& range_out) const 118 { 119 typedef typename coordinate_type<Point>::type coordinate_type; 120 121 typedef typename geometry::select_most_precise 122 < 123 coordinate_type, 124 double 125 >::type promoted_type; 126 127 promoted_type const alpha = calculate_angle<promoted_type>(perp_left_point, ultimate_point); 128 129 promoted_type const dist_left = distance.apply(penultimate_point, ultimate_point, buffer_side_left); 130 promoted_type const dist_right = distance.apply(penultimate_point, ultimate_point, buffer_side_right); 131 if (geometry::math::equals(dist_left, dist_right)) 132 { 133 generate_points(ultimate_point, alpha, dist_left, range_out); 134 } 135 else 136 { 137 promoted_type const two = 2.0; 138 promoted_type dist_half_diff = (dist_left - dist_right) / two; 139 140 if (side == buffer_side_right) 141 { 142 dist_half_diff = -dist_half_diff; 143 } 144 145 Point shifted_point; 146 set<0>(shifted_point, get<0>(ultimate_point) + dist_half_diff * cos(alpha)); 147 set<1>(shifted_point, get<1>(ultimate_point) + dist_half_diff * sin(alpha)); 148 generate_points(shifted_point, alpha, (dist_left + dist_right) / two, range_out); 149 } 150 151 if (m_points_per_circle % 2 == 1) 152 { 153 // For a half circle, if the number of points is not even, 154 // we should insert the end point too, to generate a full cap 155 range_out.push_back(perp_right_point); 156 } 157 } 158 159 template <typename NumericType> max_distance(NumericType const & distance)160 static inline NumericType max_distance(NumericType const& distance) 161 { 162 return distance; 163 } 164 165 //! Returns the piece_type (flat end) get_piece_type()166 static inline piece_type get_piece_type() 167 { 168 return buffered_round_end; 169 } 170 #endif // DOXYGEN_SHOULD_SKIP_THIS 171 }; 172 173 174 }} // namespace strategy::buffer 175 176 }} // namespace boost::geometry 177 178 #endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_END_ROUND_HPP 179