ellipse.hpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. //
  2. // Copyright 2021 Prathamesh Tagore <[email protected]>
  3. //
  4. // Use, modification and distribution are subject to the Boost Software License,
  5. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. #ifndef BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP
  9. #define BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP
  10. #include <boost/gil/concepts/pixel.hpp>
  11. #include <boost/gil/extension/rasterization/apply_rasterizer.hpp>
  12. #include <boost/gil/point.hpp>
  13. #include <array>
  14. #include <stdexcept>
  15. #include <vector>
  16. namespace boost { namespace gil {
  17. struct ellipse_rasterizer_t{};
  18. /// \defgroup EllipseRasterization
  19. /// \ingroup Rasterization
  20. /// \brief Ellipse rasterization algorithms.
  21. /// \ingroup EllipseRasterization
  22. /// \brief Performs ellipse rasterization using midpoint algorithm. Initially, program considers
  23. /// origin as center of ellipse and obtains first quadrant trajectory points. After that,
  24. /// it shifts origin to provided co-ordinates of center and then draws the curve.
  25. struct midpoint_ellipse_rasterizer
  26. {
  27. using type = ellipse_rasterizer_t;
  28. /// \brief Creates a midpoint ellipse rasterizer
  29. /// \param center - Point containing positive integer x co-ordinate and y co-ordinate of the
  30. /// center respectively.
  31. /// \param semi_axes - Point containing positive integer lengths of horizontal semi-axis
  32. /// and vertical semi-axis respectively.
  33. midpoint_ellipse_rasterizer(point<unsigned int> center_point,
  34. point<unsigned int> semi_axes_values)
  35. : center(center_point)
  36. , semi_axes(semi_axes_values)
  37. {}
  38. /// \brief Returns a vector containing co-ordinates of first quadrant points which lie on
  39. /// rasterizer trajectory of the ellipse.
  40. auto obtain_trajectory() const
  41. -> std::vector<point_t>
  42. {
  43. // Citation : J. Van Aken, "An Efficient Ellipse-Drawing Algorithm" in IEEE Computer
  44. // Graphics and Applications, vol. 4, no. 09, pp. 24-35, 1984.
  45. // doi: 10.1109/MCG.1984.275994
  46. // keywords: {null}
  47. // url: https://doi.ieeecomputersociety.org/10.1109/MCG.1984.275994
  48. std::vector<point_t> trajectory_points;
  49. std::ptrdiff_t x = semi_axes[0], y = 0;
  50. // Variables declared on following lines are temporary variables used for improving
  51. // performance since they help in converting all multiplicative operations inside the while
  52. // loop into additive/subtractive operations.
  53. long long int const t1 = semi_axes[0] * semi_axes[0];
  54. long long int const t4 = semi_axes[1] * semi_axes[1];
  55. long long int t2, t3, t5, t6, t8, t9;
  56. t2 = 2 * t1, t3 = 2 * t2;
  57. t5 = 2 * t4, t6 = 2 * t5;
  58. long long int const t7 = semi_axes[0] * t5;
  59. t8 = 2 * t7, t9 = 0;
  60. // Following variables serve as decision parameters and help in choosing the right point
  61. // to be included in rasterizer trajectory.
  62. long long int d1, d2;
  63. d1 = t2 - t7 + t4 / 2, d2 = t1 / 2 - t8 + t5;
  64. while (d2 < 0)
  65. {
  66. trajectory_points.push_back({x, y});
  67. y += 1;
  68. t9 += t3;
  69. if (d1 < 0)
  70. {
  71. d1 += t9 + t2;
  72. d2 += t9;
  73. }
  74. else
  75. {
  76. x -= 1;
  77. t8 -= t6;
  78. d1 += t9 + t2 - t8;
  79. d2 += t5 + t9 - t8;
  80. }
  81. }
  82. while (x >= 0)
  83. {
  84. trajectory_points.push_back({x, y});
  85. x -= 1;
  86. t8 -= t6;
  87. if (d2 < 0)
  88. {
  89. y += 1;
  90. t9 += t3;
  91. d2 += t5 + t9 - t8;
  92. }
  93. else
  94. {
  95. d2 += t5 - t8;
  96. }
  97. }
  98. return trajectory_points;
  99. }
  100. /// \brief Fills pixels returned by function 'obtain_trajectory' as well as pixels
  101. /// obtained from their reflection along major axis, minor axis and line passing through
  102. /// center with slope -1 using colours provided by user.
  103. /// \param view - Gil view of image on which the elliptical curve is to be drawn.
  104. /// \param pixel - Pixel value for the elliptical curve to be drawn.
  105. /// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying
  106. /// on rasterizer trajectory.
  107. /// \tparam View - Type of input image view.
  108. /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view
  109. template<typename View, typename Pixel>
  110. void draw_curve(View& view, Pixel const& pixel,
  111. std::vector<point_t> const& trajectory_points) const
  112. {
  113. using pixel_t = typename View::value_type;
  114. if (!pixels_are_compatible<pixel_t, Pixel>())
  115. {
  116. throw std::runtime_error("Pixel type of the given image is not compatible to the "
  117. "type of the provided pixel.");
  118. }
  119. // mutable center copy
  120. point<unsigned int> center2(center);
  121. --center2[0], --center2[1]; // For converting center co-ordinate values to zero based indexing.
  122. for (point_t pnt : trajectory_points)
  123. {
  124. std::array<std::ptrdiff_t, 4> co_ords = {center2[0] + pnt[0],
  125. center2[0] - pnt[0], center2[1] + pnt[1], center2[1] - pnt[1]
  126. };
  127. bool validity[4]{};
  128. if (co_ords[0] < view.width())
  129. {
  130. validity[0] = true;
  131. }
  132. if (co_ords[1] >= 0 && co_ords[1] < view.width())
  133. {
  134. validity[1] = true;
  135. }
  136. if (co_ords[2] < view.height())
  137. {
  138. validity[2] = true;
  139. }
  140. if (co_ords[3] >= 0 && co_ords[3] < view.height())
  141. {
  142. validity[3] = true;
  143. }
  144. if (validity[0] && validity[2])
  145. {
  146. view(co_ords[0], co_ords[2]) = pixel;
  147. }
  148. if (validity[1] && validity[2])
  149. {
  150. view(co_ords[1], co_ords[2]) = pixel;
  151. }
  152. if (validity[1] && validity[3])
  153. {
  154. view(co_ords[1], co_ords[3]) = pixel;
  155. }
  156. if (validity[0] && validity[3])
  157. {
  158. view(co_ords[0], co_ords[3]) = pixel;
  159. }
  160. }
  161. }
  162. /// \brief Calls the function 'obtain_trajectory' and then passes obtained trajectory points
  163. /// in the function 'draw_curve' for drawing the desired ellipse.
  164. /// \param view - Gil view of image on which the elliptical curve is to be drawn.
  165. /// \param pixel - Pixel value for the elliptical curve to be drawn.
  166. /// \tparam View - Type of input image view.
  167. /// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view
  168. template<typename View, typename Pixel>
  169. void operator()(View& view, Pixel const& pixel) const
  170. {
  171. draw_curve(view, pixel, obtain_trajectory());
  172. }
  173. point<unsigned int> center;
  174. point<unsigned int> semi_axes;
  175. };
  176. namespace detail {
  177. template <typename View, typename Rasterizer, typename Pixel>
  178. struct apply_rasterizer_op<View, Rasterizer, Pixel, ellipse_rasterizer_t>
  179. {
  180. void operator()(
  181. View const& view, Rasterizer const& rasterizer, Pixel const& pixel)
  182. {
  183. rasterizer(view, pixel);
  184. }
  185. };
  186. } //namespace detail
  187. }} // namespace boost::gil
  188. #endif