• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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