This repository has been archived on 2024-01-28. You can view files and clone it, but cannot push or open issues/pull-requests.
ecg-prog-filtered/u02/src/tests.cpp

301 lines
9.8 KiB
C++

// SPDX-License-Identifier: LGPL-3.0-or-later
#include <algorithm>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/generators/catch_generators_adapters.hpp>
#include <catch2/generators/catch_generators_random.hpp>
#include "bresenham_line_tool.h"
#include "canvas_buffer.h"
#include "dda_line_tool.h"
#include "non_recursive_fill_tool.h"
#include "rectangle_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") {
const int x = GENERATE(take(10, random(-100, 100)));
const 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)));
const 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;
const int tool_idx = GENERATE(0, 1);
switch (tool_idx) {
case 0:
tool = tool_bresenham;
break;
case 1:
tool = tool_dda;
break;
}
const int x0 = GENERATE(take(10, random(0, size - 1)));
const int y0 = GENERATE(take(10, random(0, size - 1)));
const int x1 = GENERATE(take(10, random(0, size - 1)));
const int y1 = GENERATE(take(10, random(0, size - 1)));
tool->draw(x0, y0, x1, y1);
const int x_min = std::min(x0, x1);
const int x_max = std::max(x0, x1);
const int y_min = std::min(y0, y1);
const 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("Fill (recursive and non recursive) test shape") {
canvas_buffer *canvas = new canvas_buffer(100, 100);
bresenham_line_tool *tool_line = new bresenham_line_tool(*canvas);
recursive_fill_tool *tool_fill_recursive = new recursive_fill_tool(*canvas);
non_recursive_fill_tool *tool_fill_non_recursive =
new non_recursive_fill_tool(*canvas);
tool_base *tool_fill;
const int tool_fill_idx = GENERATE(0, 1);
switch (tool_fill_idx) {
case 0:
tool_fill = tool_fill_recursive;
break;
case 1:
tool_fill = tool_fill_non_recursive;
break;
}
canvas->draw_test_shape();
REQUIRE_FALSE(canvas->get_pixel(50, 49));
REQUIRE_FALSE(canvas->get_pixel(50, 25));
REQUIRE_FALSE(canvas->get_pixel(50, 75));
tool_fill->draw(50, 25);
REQUIRE(canvas->get_pixel(50, 49));
REQUIRE(canvas->get_pixel(50, 25));
REQUIRE(canvas->get_pixel(50, 75));
REQUIRE_FALSE(canvas->get_pixel(75, 40));
REQUIRE_FALSE(canvas->get_pixel(75, 60));
tool_fill->draw(75, 50);
REQUIRE(canvas->get_pixel(75, 40));
REQUIRE(canvas->get_pixel(75, 60));
REQUIRE_FALSE(canvas->get_pixel(0, 0));
REQUIRE_FALSE(canvas->get_pixel(99, 99));
tool_fill->draw(25, 50);
REQUIRE(canvas->get_pixel(0, 0));
REQUIRE(canvas->get_pixel(99, 99));
}
TEST_CASE("Fill recursive == Fill non recursive (prop, 5 random lines)") {
const int size = 100;
canvas_buffer *canvas_recursive = new canvas_buffer(size, size);
canvas_buffer *canvas_non_recursive = new canvas_buffer(size, size);
bresenham_line_tool *tool_line_recursive =
new bresenham_line_tool(*canvas_recursive);
bresenham_line_tool *tool_line_non_recursive =
new bresenham_line_tool(*canvas_non_recursive);
recursive_fill_tool *tool_fill_recursive =
new recursive_fill_tool(*canvas_recursive);
non_recursive_fill_tool *tool_fill_non_recursive =
new non_recursive_fill_tool(*canvas_non_recursive);
for (int i = 0; i < 5; i++) {
const int x0 = GENERATE(take(1, random(0, size - 1)));
const int y0 = GENERATE(take(1, random(0, size - 1)));
const int x1 = GENERATE(take(1, random(0, size - 1)));
const int y1 = GENERATE(take(1, random(0, size - 1)));
tool_line_recursive->draw(x0, y0, x1, y1);
tool_line_non_recursive->draw(x0, y0, x1, y1);
}
const int x = GENERATE(take(3, random(0, size - 1)));
const int y = GENERATE(take(3, random(0, size - 1)));
tool_fill_recursive->draw(x, y);
tool_fill_non_recursive->draw(x, y);
bool equal = true;
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
if (canvas_recursive->get_pixel(x, y) !=
canvas_non_recursive->get_pixel(x, y))
equal = false;
}
}
REQUIRE(equal);
}
TEST_CASE("Rectangle (prop)") {
const int size = 100;
const int x0 = GENERATE(take(5, random(0, size - 1)));
const int y0 = GENERATE(take(5, random(0, size - 1)));
const int x1 = GENERATE(take(5, random(0, size - 1)));
const int y1 = GENERATE(take(5, random(0, size - 1)));
const int x_min = std::min(x0, x1);
const int x_max = std::max(x0, x1);
const int y_min = std::min(y0, y1);
const int y_max = std::max(y0, y1);
canvas_buffer *canvas = new canvas_buffer(size, size);
rectangle_tool *tool = new rectangle_tool(*canvas);
tool->draw(x0, y0, x1, y1);
bool pass = true;
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
if (((x == x0 || x == x1) && (y >= y_min && y <= y_max)) ||
((y == y0 || y == y1) && (x >= x_min && x <= x_max))) {
if (!canvas->get_pixel(x, y))
pass = false;
} else {
if (canvas->get_pixel(x, y))
pass = false;
}
}
}
REQUIRE(pass);
}