// SPDX-License-Identifier: LGPL-3.0-or-later #include #include #include #include #include #include "bresenham_line_tool.h" #include "canvas_buffer.h" #include "dda_line_tool.h" #include "recursive_fill_tool.h" #include "util.h" TEST_CASE("Transform Mirror") { // elementary operations REQUIRE(transform(TRANSFORM_MIRROR_X, 10, 20) == std::make_pair(10, -20)); REQUIRE(transform(TRANSFORM_MIRROR_Y, 10, 20) == std::make_pair(-10, 20)); // composite operations REQUIRE(transform(TRANSFORM_MIRROR_X | TRANSFORM_MIRROR_Y, 10, 20) == std::make_pair(-10, -20)); } TEST_CASE("Transform Rotate") { REQUIRE(transform(TRANSFORM_ROTATE_CW, 10, 20) == std::make_pair(-20, 10)); // 4th quadrant REQUIRE(transform(TRANSFORM_ROTATE_CW, -20, 10) == std::make_pair(-10, -20)); // 3rd quadrant REQUIRE(transform(TRANSFORM_ROTATE_CW, -10, -20) == std::make_pair(20, -10)); // 2nd quadrant REQUIRE(transform(TRANSFORM_ROTATE_CW, 20, -10) == std::make_pair(10, 20)); // 1st quadrant REQUIRE(transform(TRANSFORM_ROTATE_CCW, 20, -10) == std::make_pair(-10, -20)); // 1nd quadrant REQUIRE(transform(TRANSFORM_ROTATE_CCW, -10, -20) == std::make_pair(-20, 10)); // 2rd quadrant REQUIRE(transform(TRANSFORM_ROTATE_CCW, -20, 10) == std::make_pair(10, 20)); // 3th quadrant REQUIRE(transform(TRANSFORM_ROTATE_CCW, 10, 20) == std::make_pair(20, -10)); // 4st quadrant } TEST_CASE("Transform Rotate + Mirror") { REQUIRE(transform(TRANSFORM_ROTATE_CW | TRANSFORM_MIRROR_X, 10, 20) == std::make_pair(-20, -10)); REQUIRE(transform(TRANSFORM_ROTATE_CW | TRANSFORM_MIRROR_Y, 10, 20) == std::make_pair(20, 10)); REQUIRE( transform(TRANSFORM_ROTATE_CW | TRANSFORM_MIRROR_X | TRANSFORM_MIRROR_Y, 10, 20) == std::make_pair(20, -10)); REQUIRE(transform(TRANSFORM_ROTATE_CCW | TRANSFORM_MIRROR_X, 10, 20) == std::make_pair(20, 10)); REQUIRE(transform(TRANSFORM_ROTATE_CCW | TRANSFORM_MIRROR_Y, 10, 20) == std::make_pair(-20, -10)); REQUIRE( transform(TRANSFORM_ROTATE_CCW | TRANSFORM_MIRROR_X | TRANSFORM_MIRROR_Y, 10, 20) == std::make_pair(-20, 10)); } TEST_CASE("Transform = Inverse Transform ○ Transform") { int x = GENERATE(take(10, random(-100, 100))); int y = GENERATE(take(10, random(-100, 100))); // this iterates over all possible transformations, // even bogus ones (like rotating cw and ccw) for (Transformation transformation = 0; transformation < 0b10000; transformation++) { int xt, yt; std::tie(xt, yt) = transform(transformation, x, y); int xti, yti; std::tie(xti, yti) = transform_inv(transformation, xt, yt); REQUIRE(x == xti); REQUIRE(y == yti); } } TEST_CASE("Transformation to standard case") { REQUIRE(transformation_to_standard_case(5, 20, 20, 10) == 0); REQUIRE(transformation_to_standard_case(5, 5, 20, 15) == TRANSFORM_MIRROR_X); REQUIRE(transformation_to_standard_case(20, 15, 5, 5) == TRANSFORM_MIRROR_Y); REQUIRE(transformation_to_standard_case(20, 10, 5, 20) == (TRANSFORM_MIRROR_X | TRANSFORM_MIRROR_Y)); REQUIRE(transformation_to_standard_case(5, 20, 15, 5) == (TRANSFORM_ROTATE_CW | TRANSFORM_MIRROR_X)); REQUIRE(transformation_to_standard_case(5, 5, 15, 20) == TRANSFORM_ROTATE_CCW); REQUIRE(transformation_to_standard_case(15, 5, 5, 20) == (TRANSFORM_ROTATE_CCW | TRANSFORM_MIRROR_X)); REQUIRE(transformation_to_standard_case(15, 20, 5, 5) == TRANSFORM_ROTATE_CW); } TEST_CASE("Transformation to standard case (prop)") { int x0 = GENERATE(take(10, random(-100, 100))); int y0 = GENERATE(take(10, random(-100, 100))); int x1 = GENERATE(take(10, random(-100, 100))); int y1 = GENERATE(take(10, random(-100, 100))); Transformation transformation = transformation_to_standard_case(x0, y0, x1, y1); transform_mut(transformation, x0, y0); transform_mut(transformation, x1, y1); REQUIRE(x0 <= x1); REQUIRE(y0 >= y1); } TEST_CASE("Bresenham/DDA line tool (prop: for every row/column, only one pixel " "is set)") { const int size = 100; canvas_buffer *canvas = new canvas_buffer(size, size); bresenham_line_tool *tool_bresenham = new bresenham_line_tool(*canvas); dda_line_tool *tool_dda = new dda_line_tool(*canvas); tool_base *tool; int tool_idx = GENERATE(0, 1); switch (tool_idx) { case 0: tool = tool_bresenham; break; case 1: tool = tool_dda; break; } int x0 = GENERATE(take(10, random(0, size - 1))); int y0 = GENERATE(take(10, random(0, size - 1))); int x1 = GENERATE(take(10, random(0, size - 1))); int y1 = GENERATE(take(10, random(0, size - 1))); tool->draw(x0, y0, x1, y1); int x_min = std::min(x0, x1); int x_max = std::max(x0, x1); int y_min = std::min(y0, y1); int y_max = std::max(y0, y1); // Depending on what the direction of the line (rounded to the next 90°) is, // either every row or column has only one pixel set. bool vertical = false; int draw_direction_min; int draw_direction_max; int unique_direction_min; int unique_direction_max; if (abs(y1 - y0) > abs(x1 - x0)) { vertical = true; draw_direction_min = y_min; draw_direction_max = y_max; unique_direction_min = x_min; unique_direction_max = x_max; } else { draw_direction_min = x_min; draw_direction_max = x_max; unique_direction_min = y_min; unique_direction_max = y_max; } bool all_sums_are_one = true; int sum; for (int dd = draw_direction_min; dd <= draw_direction_max; dd++) { sum = 0; for (int ud = unique_direction_min; ud <= unique_direction_max; ud++) { int x, y; if (vertical) { x = ud; y = dd; } else { x = dd; y = ud; } if (canvas->get_pixel(x, y)) sum++; } if (sum != 1) all_sums_are_one = false; } REQUIRE(all_sums_are_one); } TEST_CASE("Recursive fill simple") { canvas_buffer *canvas = new canvas_buffer(100, 100); bresenham_line_tool *tool_line = new bresenham_line_tool(*canvas); recursive_fill_tool *tool = new recursive_fill_tool(*canvas); tool_line->draw(10, 10, 30, 10); tool_line->draw(10, 10, 20, 30); tool_line->draw(20, 30, 30, 10); tool->draw(15, 15); // just some samples, not very thorough REQUIRE(canvas->get_pixel(15, 15)); REQUIRE(canvas->get_pixel(20, 20)); REQUIRE(canvas->get_pixel(25, 20)); REQUIRE_FALSE(canvas->get_pixel(9, 9)); REQUIRE_FALSE(canvas->get_pixel(30, 9)); REQUIRE_FALSE(canvas->get_pixel(19, 30)); }