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_JOIN_ROUND_HPP 15 #define BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_JOIN_ROUND_HPP 16 17 #include <algorithm> 18 19 #include <boost/geometry/core/cs.hpp> 20 #include <boost/geometry/policies/compare.hpp> 21 #include <boost/geometry/strategies/buffer.hpp> 22 #include <boost/geometry/util/math.hpp> 23 #include <boost/geometry/util/select_most_precise.hpp> 24 25 #ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN 26 #include <iostream> 27 #include <boost/geometry/io/wkt/wkt.hpp> 28 #endif 29 30 31 namespace boost { namespace geometry 32 { 33 34 35 namespace strategy { namespace buffer 36 { 37 38 /*! 39 \brief Let the buffer create rounded corners 40 \ingroup strategies 41 \details This strategy can be used as JoinStrategy for the buffer algorithm. 42 It creates a rounded corners around each convex vertex. It can be applied 43 for (multi)linestrings and (multi)polygons. 44 This strategy is only applicable for Cartesian coordinate systems. 45 46 \qbk{ 47 [heading Example] 48 [buffer_join_round] 49 [heading Output] 50 [$img/strategies/buffer_join_round.png] 51 [heading See also] 52 \* [link geometry.reference.algorithms.buffer.buffer_7_with_strategies buffer (with strategies)] 53 \* [link geometry.reference.strategies.strategy_buffer_join_miter join_miter] 54 } 55 */ 56 class join_round 57 { 58 public : 59 60 //! \brief Constructs the strategy 61 //! \param points_per_circle points which would be used for a full circle join_round(std::size_t points_per_circle=90)62 explicit inline join_round(std::size_t points_per_circle = 90) 63 : m_points_per_circle(points_per_circle) 64 {} 65 66 private : 67 template 68 < 69 typename PromotedType, 70 typename Point, 71 typename DistanceType, 72 typename RangeOut 73 > generate_points(Point const & vertex,Point const & perp1,Point const & perp2,DistanceType const & buffer_distance,RangeOut & range_out) const74 inline void generate_points(Point const& vertex, 75 Point const& perp1, Point const& perp2, 76 DistanceType const& buffer_distance, 77 RangeOut& range_out) const 78 { 79 PromotedType const dx1 = get<0>(perp1) - get<0>(vertex); 80 PromotedType const dy1 = get<1>(perp1) - get<1>(vertex); 81 PromotedType const dx2 = get<0>(perp2) - get<0>(vertex); 82 PromotedType const dy2 = get<1>(perp2) - get<1>(vertex); 83 84 PromotedType const two_pi = geometry::math::two_pi<PromotedType>(); 85 86 PromotedType const angle1 = atan2(dy1, dx1); 87 PromotedType angle2 = atan2(dy2, dx2); 88 while (angle2 > angle1) 89 { 90 angle2 -= two_pi; 91 } 92 PromotedType const angle_diff = angle1 - angle2; 93 94 // Divide the angle into an integer amount of steps to make it 95 // visually correct also for a low number of points / circle 96 97 // If a full circle is divided into 3 parts (e.g. angle is 125), 98 // the one point in between must still be generated 99 // The calculation below: 100 // - generates 1 point in between for an angle of 125 based on 3 points 101 // - generates 0 points in between for an angle of 90 based on 4 points 102 103 std::size_t const n = (std::max)(static_cast<std::size_t>( 104 ceil(m_points_per_circle * angle_diff / two_pi)), std::size_t(1)); 105 106 PromotedType const diff = angle_diff / static_cast<PromotedType>(n); 107 PromotedType a = angle1 - diff; 108 109 // Walk to n - 1 to avoid generating the last point 110 for (std::size_t i = 0; i < n - 1; i++, a -= diff) 111 { 112 Point p; 113 set<0>(p, get<0>(vertex) + buffer_distance * cos(a)); 114 set<1>(p, get<1>(vertex) + buffer_distance * sin(a)); 115 range_out.push_back(p); 116 } 117 } 118 119 public : 120 121 122 #ifndef DOXYGEN_SHOULD_SKIP_THIS 123 //! Fills output_range with a rounded shape around a vertex 124 template <typename Point, typename DistanceType, typename RangeOut> apply(Point const & ip,Point const & vertex,Point const & perp1,Point const & perp2,DistanceType const & buffer_distance,RangeOut & range_out) const125 inline bool apply(Point const& ip, Point const& vertex, 126 Point const& perp1, Point const& perp2, 127 DistanceType const& buffer_distance, 128 RangeOut& range_out) const 129 { 130 typedef typename coordinate_type<Point>::type coordinate_type; 131 typedef typename boost::range_value<RangeOut>::type output_point_type; 132 133 typedef typename geometry::select_most_precise 134 < 135 typename geometry::select_most_precise 136 < 137 coordinate_type, 138 typename geometry::coordinate_type<output_point_type>::type 139 >::type, 140 double 141 >::type promoted_type; 142 143 geometry::equal_to<Point> equals; 144 if (equals(perp1, perp2)) 145 { 146 #ifdef BOOST_GEOMETRY_DEBUG_BUFFER_WARN 147 std::cout << "Corner for equal points " << geometry::wkt(ip) << " " << geometry::wkt(perp1) << std::endl; 148 #endif 149 return false; 150 } 151 152 // Generate 'vectors' 153 coordinate_type vix = (get<0>(ip) - get<0>(vertex)); 154 coordinate_type viy = (get<1>(ip) - get<1>(vertex)); 155 156 promoted_type length_i = geometry::math::sqrt(vix * vix + viy * viy); 157 DistanceType const bd = geometry::math::abs(buffer_distance); 158 promoted_type prop = bd / length_i; 159 160 Point bp; 161 set<0>(bp, get<0>(vertex) + vix * prop); 162 set<1>(bp, get<1>(vertex) + viy * prop); 163 164 range_out.push_back(perp1); 165 generate_points<promoted_type>(vertex, perp1, perp2, bd, range_out); 166 range_out.push_back(perp2); 167 return true; 168 } 169 170 template <typename NumericType> max_distance(NumericType const & distance)171 static inline NumericType max_distance(NumericType const& distance) 172 { 173 return distance; 174 } 175 176 #endif // DOXYGEN_SHOULD_SKIP_THIS 177 178 private : 179 std::size_t m_points_per_circle; 180 }; 181 182 183 }} // namespace strategy::buffer 184 185 }} // namespace boost::geometry 186 187 #endif // BOOST_GEOMETRY_STRATEGIES_CARTESIAN_BUFFER_JOIN_ROUND_HPP 188