buffer_end_round.hpp 6.1 KB

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