// // Copyright 2020 Olzhas Zhumabek // // 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 "core/point/test_fixture.hpp" #include #include #include #include #include #include #include #include #include namespace gil = boost::gil; using line_type = std::vector; struct endpoints { gil::point_t start; gil::point_t end; }; endpoints create_endpoints(std::mt19937& twister, std::uniform_int_distribution& 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(points.start, points.end); line_type forward_line(rasterizer.point_count()); rasterizer(forward_line.begin()); return forward_line; } void test_start_end(line_type const& 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(height) / static_cast(width); const double intercept = static_cast(start.y) - sign * slope * static_cast(start.x); for (const auto& point : line_points) { double const expected_y = sign * slope * static_cast(point.x) + intercept; auto const difference = std::abs(point.y - static_cast(std::round(expected_y))); BOOST_TEST_LE(difference, static_cast(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 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(); }