210 lines
7.3 KiB
C++
210 lines
7.3 KiB
C++
//
|
|
// Copyright 2021 Prathamesh Tagore <prathameshtagore@gmail.com>
|
|
//
|
|
// Use, modification and distribution are subject to the Boost Software License,
|
|
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
#ifndef BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP
|
|
#define BOOST_GIL_EXTENSION_RASTERIZATION_ELLIPSE_HPP
|
|
|
|
#include <boost/gil/concepts/pixel.hpp>
|
|
#include <boost/gil/extension/rasterization/apply_rasterizer.hpp>
|
|
#include <boost/gil/point.hpp>
|
|
|
|
#include <array>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
namespace boost { namespace gil {
|
|
|
|
struct ellipse_rasterizer_t{};
|
|
|
|
/// \defgroup EllipseRasterization
|
|
/// \ingroup Rasterization
|
|
/// \brief Ellipse rasterization algorithms.
|
|
|
|
/// \ingroup EllipseRasterization
|
|
/// \brief Performs ellipse rasterization using midpoint algorithm. Initially, program considers
|
|
/// origin as center of ellipse and obtains first quadrant trajectory points. After that,
|
|
/// it shifts origin to provided co-ordinates of center and then draws the curve.
|
|
struct midpoint_ellipse_rasterizer
|
|
{
|
|
using type = ellipse_rasterizer_t;
|
|
|
|
/// \brief Creates a midpoint ellipse rasterizer
|
|
/// \param center - Point containing positive integer x co-ordinate and y co-ordinate of the
|
|
/// center respectively.
|
|
/// \param semi_axes - Point containing positive integer lengths of horizontal semi-axis
|
|
/// and vertical semi-axis respectively.
|
|
midpoint_ellipse_rasterizer(point<unsigned int> center_point,
|
|
point<unsigned int> semi_axes_values)
|
|
: center(center_point)
|
|
, semi_axes(semi_axes_values)
|
|
{}
|
|
|
|
/// \brief Returns a vector containing co-ordinates of first quadrant points which lie on
|
|
/// rasterizer trajectory of the ellipse.
|
|
auto obtain_trajectory() const
|
|
-> std::vector<point_t>
|
|
{
|
|
// Citation : J. Van Aken, "An Efficient Ellipse-Drawing Algorithm" in IEEE Computer
|
|
// Graphics and Applications, vol. 4, no. 09, pp. 24-35, 1984.
|
|
// doi: 10.1109/MCG.1984.275994
|
|
// keywords: {null}
|
|
// url: https://doi.ieeecomputersociety.org/10.1109/MCG.1984.275994
|
|
std::vector<point_t> trajectory_points;
|
|
std::ptrdiff_t x = semi_axes[0], y = 0;
|
|
|
|
// Variables declared on following lines are temporary variables used for improving
|
|
// performance since they help in converting all multiplicative operations inside the while
|
|
// loop into additive/subtractive operations.
|
|
long long int const t1 = semi_axes[0] * semi_axes[0];
|
|
long long int const t4 = semi_axes[1] * semi_axes[1];
|
|
long long int t2, t3, t5, t6, t8, t9;
|
|
t2 = 2 * t1, t3 = 2 * t2;
|
|
t5 = 2 * t4, t6 = 2 * t5;
|
|
long long int const t7 = semi_axes[0] * t5;
|
|
t8 = 2 * t7, t9 = 0;
|
|
|
|
// Following variables serve as decision parameters and help in choosing the right point
|
|
// to be included in rasterizer trajectory.
|
|
long long int d1, d2;
|
|
d1 = t2 - t7 + t4 / 2, d2 = t1 / 2 - t8 + t5;
|
|
|
|
while (d2 < 0)
|
|
{
|
|
trajectory_points.push_back({x, y});
|
|
y += 1;
|
|
t9 += t3;
|
|
if (d1 < 0)
|
|
{
|
|
d1 += t9 + t2;
|
|
d2 += t9;
|
|
}
|
|
else
|
|
{
|
|
x -= 1;
|
|
t8 -= t6;
|
|
d1 += t9 + t2 - t8;
|
|
d2 += t5 + t9 - t8;
|
|
}
|
|
}
|
|
while (x >= 0)
|
|
{
|
|
trajectory_points.push_back({x, y});
|
|
x -= 1;
|
|
t8 -= t6;
|
|
if (d2 < 0)
|
|
{
|
|
y += 1;
|
|
t9 += t3;
|
|
d2 += t5 + t9 - t8;
|
|
}
|
|
else
|
|
{
|
|
d2 += t5 - t8;
|
|
}
|
|
}
|
|
return trajectory_points;
|
|
}
|
|
|
|
/// \brief Fills pixels returned by function 'obtain_trajectory' as well as pixels
|
|
/// obtained from their reflection along major axis, minor axis and line passing through
|
|
/// center with slope -1 using colours provided by user.
|
|
/// \param view - Gil view of image on which the elliptical curve is to be drawn.
|
|
/// \param pixel - Pixel value for the elliptical curve to be drawn.
|
|
/// \param trajectory_points - Constant vector specifying pixel co-ordinates of points lying
|
|
/// on rasterizer trajectory.
|
|
/// \tparam View - Type of input image view.
|
|
/// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view
|
|
template<typename View, typename Pixel>
|
|
void draw_curve(View& view, Pixel const& pixel,
|
|
std::vector<point_t> const& trajectory_points) const
|
|
{
|
|
using pixel_t = typename View::value_type;
|
|
if (!pixels_are_compatible<pixel_t, Pixel>())
|
|
{
|
|
throw std::runtime_error("Pixel type of the given image is not compatible to the "
|
|
"type of the provided pixel.");
|
|
}
|
|
|
|
// mutable center copy
|
|
point<unsigned int> center2(center);
|
|
--center2[0], --center2[1]; // For converting center co-ordinate values to zero based indexing.
|
|
for (point_t pnt : trajectory_points)
|
|
{
|
|
std::array<std::ptrdiff_t, 4> co_ords = {center2[0] + pnt[0],
|
|
center2[0] - pnt[0], center2[1] + pnt[1], center2[1] - pnt[1]
|
|
};
|
|
bool validity[4]{};
|
|
if (co_ords[0] < view.width())
|
|
{
|
|
validity[0] = true;
|
|
}
|
|
if (co_ords[1] >= 0 && co_ords[1] < view.width())
|
|
{
|
|
validity[1] = true;
|
|
}
|
|
if (co_ords[2] < view.height())
|
|
{
|
|
validity[2] = true;
|
|
}
|
|
if (co_ords[3] >= 0 && co_ords[3] < view.height())
|
|
{
|
|
validity[3] = true;
|
|
}
|
|
|
|
if (validity[0] && validity[2])
|
|
{
|
|
view(co_ords[0], co_ords[2]) = pixel;
|
|
}
|
|
if (validity[1] && validity[2])
|
|
{
|
|
view(co_ords[1], co_ords[2]) = pixel;
|
|
}
|
|
if (validity[1] && validity[3])
|
|
{
|
|
view(co_ords[1], co_ords[3]) = pixel;
|
|
}
|
|
if (validity[0] && validity[3])
|
|
{
|
|
view(co_ords[0], co_ords[3]) = pixel;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// \brief Calls the function 'obtain_trajectory' and then passes obtained trajectory points
|
|
/// in the function 'draw_curve' for drawing the desired ellipse.
|
|
/// \param view - Gil view of image on which the elliptical curve is to be drawn.
|
|
/// \param pixel - Pixel value for the elliptical curve to be drawn.
|
|
/// \tparam View - Type of input image view.
|
|
/// \tparam Pixel - Type of pixel. Must be compatible to the pixel type of the image view
|
|
template<typename View, typename Pixel>
|
|
void operator()(View& view, Pixel const& pixel) const
|
|
{
|
|
draw_curve(view, pixel, obtain_trajectory());
|
|
}
|
|
|
|
point<unsigned int> center;
|
|
point<unsigned int> semi_axes;
|
|
};
|
|
|
|
namespace detail {
|
|
|
|
template <typename View, typename Rasterizer, typename Pixel>
|
|
struct apply_rasterizer_op<View, Rasterizer, Pixel, ellipse_rasterizer_t>
|
|
{
|
|
void operator()(
|
|
View const& view, Rasterizer const& rasterizer, Pixel const& pixel)
|
|
{
|
|
rasterizer(view, pixel);
|
|
}
|
|
};
|
|
|
|
} //namespace detail
|
|
|
|
}} // namespace boost::gil
|
|
|
|
#endif
|