203 lines
6.7 KiB
C++
203 lines
6.7 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 "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));
|
|
}
|