mirror of
https://github.com/boostorg/gil.git
synced 2026-01-24 18:02:17 +00:00
Support construction from step_size, step_count, and a function for angles Implement angle and radious version of Hough line transform and adds a demo with static line that goes over secondary diagonal. Implement incremental line raster Implement naive line raster Implement Bresenham line raster Leave only Bresenham line rasterization Naive and incremental algorithms were removed because they are supposed to produce the same results anyway. The reason for diverging results is inaccuracy of floating point numbers Add circle rendering through trigonometric functions, using arctan(1 / (radius + 1)) as minimal angle step. Trigonometric circle rasterizer does not follow circle equation, but still produces very round shapes. A new testing methodology needs to be devised for this rasterizer. The new version accepts start and points inclusively and tries to use canonic representation during computations. Slope decided to be is (diff_y + 1) / (diff_x + 1).
145 lines
4.6 KiB
C++
145 lines
4.6 KiB
C++
// Boost.GIL (Generic Image Library) - tests
|
|
//
|
|
// Copyright 2020 Olzhas Zhumabek <anonymous.from.applecity@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)
|
|
//
|
|
|
|
#include <algorithm>
|
|
#include <boost/core/lightweight_test.hpp>
|
|
#include <boost/gil/point.hpp>
|
|
#include <boost/gil/rasterization/line.hpp>
|
|
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <iterator>
|
|
#include <random>
|
|
#include <vector>
|
|
|
|
namespace gil = boost::gil;
|
|
|
|
namespace boost
|
|
{
|
|
namespace gil
|
|
{
|
|
std::ostream& operator<<(std::ostream& os, const point_t p)
|
|
{
|
|
os << "{x=" << p.x << ", y=" << p.y << "}";
|
|
return os;
|
|
}
|
|
}} // namespace boost::gil
|
|
|
|
using line_type = std::vector<gil::point_t>;
|
|
|
|
struct endpoints
|
|
{
|
|
gil::point_t start;
|
|
gil::point_t end;
|
|
};
|
|
|
|
endpoints create_endpoints(std::mt19937& twister,
|
|
std::uniform_int_distribution<std::ptrdiff_t>& distr)
|
|
{
|
|
gil::point_t start{distr(twister), distr(twister)};
|
|
gil::point_t end{distr(twister), distr(twister)};
|
|
return {start, end};
|
|
}
|
|
|
|
line_type create_line(endpoints points)
|
|
{
|
|
gil::bresenham_line_rasterizer rasterizer;
|
|
line_type forward_line(rasterizer.point_count(points.start, points.end));
|
|
rasterizer(points.start, points.end, forward_line.begin());
|
|
return forward_line;
|
|
}
|
|
|
|
void test_start_end(const line_type& line_points, endpoints points)
|
|
{
|
|
BOOST_TEST_EQ(line_points.front(), points.start);
|
|
BOOST_TEST_EQ(line_points.back(), points.end);
|
|
}
|
|
|
|
// Look at TODO below
|
|
// void test_two_way_equivalence(const line_type& forward, line_type backward)
|
|
// {
|
|
// std::reverse(backward.begin(), backward.end());
|
|
// BOOST_TEST_ALL_EQ(forward.begin(), forward.end(), backward.begin(), backward.end());
|
|
// }
|
|
|
|
void test_connectivity(line_type const& line_points)
|
|
{
|
|
for (std::size_t i = 1; i < line_points.size(); ++i)
|
|
{
|
|
const auto x_diff = std::abs(line_points[i].x - line_points[i - 1].x);
|
|
const auto y_diff = std::abs(line_points[i].y - line_points[i - 1].y);
|
|
BOOST_TEST_LE(x_diff, 1);
|
|
BOOST_TEST_LE(y_diff, 1);
|
|
}
|
|
}
|
|
|
|
void test_bresenham_rasterizer_follows_equation(line_type line_points)
|
|
{
|
|
auto start = line_points.front();
|
|
auto end = line_points.back();
|
|
|
|
auto width = std::abs(end.x - start.x) + 1;
|
|
auto height = std::abs(end.y - start.y) + 1;
|
|
if (width < height)
|
|
{
|
|
std::swap(width, height);
|
|
std::transform(line_points.begin(), line_points.end(), line_points.begin(),
|
|
[](gil::point_t p)
|
|
{
|
|
return gil::point_t{p.y, p.x};
|
|
});
|
|
// update start and end
|
|
start = line_points.front();
|
|
end = line_points.back();
|
|
}
|
|
const double sign = [start, end]()
|
|
{
|
|
auto const width_sign = end.x < start.x;
|
|
auto const height_sign = end.y < start.y;
|
|
auto const slope_sign = width_sign != height_sign;
|
|
return slope_sign ? -1 : 1;
|
|
}();
|
|
const double slope = static_cast<double>(height) / static_cast<double>(width);
|
|
const double intercept =
|
|
static_cast<double>(start.y) - sign * slope * static_cast<double>(start.x);
|
|
for (const auto& point : line_points)
|
|
{
|
|
double const expected_y = sign * slope * static_cast<double>(point.x) + intercept;
|
|
auto const difference =
|
|
std::abs(point.y - static_cast<std::ptrdiff_t>(std::round(expected_y)));
|
|
BOOST_TEST_LE(difference, static_cast<std::ptrdiff_t>(slope + 1));
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
const std::ptrdiff_t size = 256;
|
|
for (std::size_t seed = 0; seed <= 100; ++seed)
|
|
{
|
|
std::mt19937 twister(seed);
|
|
std::uniform_int_distribution<std::ptrdiff_t> distr(0, size - 1);
|
|
const std::size_t sample_count = 100;
|
|
for (std::size_t sample_index = 0; sample_index < sample_count; ++sample_index)
|
|
{
|
|
auto endpoints = create_endpoints(twister, distr);
|
|
auto forward_line = create_line(endpoints);
|
|
test_start_end(forward_line, endpoints);
|
|
// TODO: figure out if forward/backward equivalence is possible to provide
|
|
// auto backward_line = create_line({endpoints.end, endpoints.start});
|
|
// test_two_way_equivalence(forward_line, backward_line);
|
|
test_connectivity(forward_line);
|
|
// test_connectivity(backward_line);
|
|
test_bresenham_rasterizer_follows_equation(forward_line);
|
|
// test_bresenham_rasterizer_follows_equation(backward_line);
|
|
}
|
|
}
|
|
|
|
return boost::report_errors();
|
|
}
|