line.hpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. //
  2. // Copyright 2020 Olzhas Zhumabek <[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_LINE_HPP
  9. #define BOOST_GIL_EXTENSION_RASTERIZATION_LINE_HPP
  10. #include <boost/gil/extension/rasterization/apply_rasterizer.hpp>
  11. #include <boost/gil/point.hpp>
  12. #include <cmath>
  13. #include <cstddef>
  14. #include <iterator>
  15. #include <vector>
  16. namespace boost { namespace gil {
  17. struct line_rasterizer_t{};
  18. /// \defgroup Rasterization
  19. /// \brief A set of functions to rasterize shapes
  20. ///
  21. /// Due to images being discrete, most shapes require specialized algorithms to
  22. /// handle rasterization efficiently and solve problem of connectivity and being
  23. /// close to the original shape.
  24. /// \defgroup LineRasterization
  25. /// \ingroup Rasterization
  26. /// \brief A set of rasterizers for lines
  27. ///
  28. /// The main problem with line rasterization is to do it efficiently, e.g. less
  29. /// floating point operations. There are multiple algorithms that on paper
  30. /// should reach the same result, but due to quirks of IEEE-754 they don't.
  31. /// Please select one and stick to it if possible. At the moment only Bresenham
  32. /// rasterizer is implemented.
  33. /// \ingroup LineRasterization
  34. /// \brief Rasterize a line according to Bresenham algorithm
  35. ///
  36. /// Do note that if either width or height is 1, slope is set to zero.
  37. /// reference:
  38. /// https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#:~:text=Bresenham's%20line%20algorithm%20is%20a,straight%20line%20between%20two%20points.
  39. struct bresenham_line_rasterizer
  40. {
  41. using type = line_rasterizer_t;
  42. bresenham_line_rasterizer(point_t start, point_t end)
  43. : start_point(start), end_point(end)
  44. {}
  45. std::ptrdiff_t point_count() const noexcept
  46. {
  47. const auto abs_width = std::abs(end_point.x - start_point.x) + 1;
  48. const auto abs_height = std::abs(end_point.y - start_point.y) + 1;
  49. return abs_width > abs_height ? abs_width : abs_height;
  50. }
  51. template <typename OutputIterator>
  52. void operator()(OutputIterator d_first) const
  53. {
  54. // mutable stack copies
  55. point_t start = start_point;
  56. point_t end = end_point;
  57. if (start == end)
  58. {
  59. // put the point and immediately exit, as later on division by zero will
  60. // occur
  61. *d_first = start;
  62. return;
  63. }
  64. auto width = std::abs(end.x - start.x) + 1;
  65. auto height = std::abs(end.y - start.y) + 1;
  66. bool const needs_flip = width < height;
  67. if (needs_flip)
  68. {
  69. // transpose the coordinate system if uncomfortable angle detected
  70. std::swap(width, height);
  71. std::swap(start.x, start.y);
  72. std::swap(end.x, end.y);
  73. }
  74. std::ptrdiff_t const x_increment = end.x >= start.x ? 1 : -1;
  75. std::ptrdiff_t const y_increment = end.y >= start.y ? 1 : -1;
  76. double const slope =
  77. height == 1 ? 0 : static_cast<double>(height) / static_cast<double>(width);
  78. std::ptrdiff_t y = start.y;
  79. double error_term = 0;
  80. for (std::ptrdiff_t x = start.x; x != end.x; x += x_increment)
  81. {
  82. // transpose coordinate system back to proper form if needed
  83. *d_first++ = needs_flip ? point_t{y, x} : point_t{x, y};
  84. error_term += slope;
  85. if (error_term >= 0.5)
  86. {
  87. --error_term;
  88. y += y_increment;
  89. }
  90. }
  91. *d_first++ = needs_flip ? point_t{end.y, end.x} : end;
  92. }
  93. point_t start_point;
  94. point_t end_point;
  95. };
  96. namespace detail {
  97. template <typename View, typename Rasterizer, typename Pixel>
  98. struct apply_rasterizer_op<View, Rasterizer, Pixel, line_rasterizer_t>
  99. {
  100. void operator()(
  101. View const& view, Rasterizer const& rasterizer, Pixel const& pixel)
  102. {
  103. std::vector<point_t> trajectory(rasterizer.point_count());
  104. rasterizer(std::begin(trajectory));
  105. for (auto const& point : trajectory)
  106. {
  107. view(point) = pixel;
  108. }
  109. }
  110. };
  111. } //namespace detail
  112. }} // namespace boost::gil
  113. #endif