u02: Implement bresenham circle tool
This commit is contained in:
parent
6b63595986
commit
e942c62ad6
|
@ -5,6 +5,7 @@
|
|||
#include <catch2/generators/catch_generators_adapters.hpp>
|
||||
#include <catch2/generators/catch_generators_random.hpp>
|
||||
|
||||
#include "bresenham_circle_tool.h"
|
||||
#include "bresenham_line_tool.h"
|
||||
#include "canvas_buffer.h"
|
||||
#include "dda_line_tool.h"
|
||||
|
@ -298,3 +299,71 @@ TEST_CASE("Rectangle (prop)") {
|
|||
}
|
||||
REQUIRE(pass);
|
||||
}
|
||||
|
||||
TEST_CASE("Bresenham circle (prop: √(x²+y²)-r<ε)") {
|
||||
// Let s be the size of the canvas (s,s).
|
||||
// Let m be the smallest coordinate (x and y) for random points
|
||||
// and M be the largest coordinate (x and y) for random points.
|
||||
//
|
||||
// The largest radius that can fit on canvas (for arbitrary centres) is m.
|
||||
// The largest radius that is possible to create is √(2)(M-m).
|
||||
//
|
||||
// ⇒ √(2)(M-m) ≥ m
|
||||
// ⇔ √(2)M-√(2)m ≥ m
|
||||
// ⇔ √(2)M ≥ (1+√(2))m
|
||||
// ⇔ m ≤ (√(2)/(1+√(2)))m
|
||||
// ⇔ m ≤ (2-√(2))M (1)
|
||||
//
|
||||
// Additionally, to have a centered point field,
|
||||
// s-M=m must hold
|
||||
//
|
||||
// s-M = m
|
||||
// ⇒ s-M ≤ (2-√(2))M
|
||||
// ⇔ s ≤ (3-√(2))M
|
||||
// ⇔ M ≥ s/(3-√(2)) (2)
|
||||
//
|
||||
// With this, it now is possible to express M and m in terms of s:
|
||||
//
|
||||
// m ≤ (2-√(2))M (1)
|
||||
// ⇔ m ≤ ((2-√(2))/(3-√(2)))s
|
||||
// ⇔ m ≤ ((4-√(2))/7)s (3)
|
||||
//
|
||||
// When the points are rounded to the nearest integer,
|
||||
// M must be rounded down and m rounded down.
|
||||
const int size = 100; // s
|
||||
const int max_c = std::floor(size / (3 - std::sqrt(2))); // M (2)
|
||||
const int min_c = std::ceil(((4 - std::sqrt(2)) / 7) * size); // m (3)
|
||||
|
||||
const int x0 = GENERATE_COPY(take(10, random(min_c, max_c)));
|
||||
const int y0 = GENERATE_COPY(take(10, random(min_c, max_c)));
|
||||
const int x1 = GENERATE_COPY(take(10, random(min_c, max_c)));
|
||||
const int y1 = GENERATE_COPY(take(10, random(min_c, max_c)));
|
||||
|
||||
if ((x0 == min_c || x0 == max_c) && (x1 == min_c || x1 == max_c) &&
|
||||
(y0 == min_c || y0 == max_c) && (y1 == min_c || y1 == max_c)) {
|
||||
SKIP("All coordinates have extreme value, skipping (avoid rounding error)");
|
||||
}
|
||||
|
||||
const int r =
|
||||
round(std::sqrt(std::pow((x1 - x0), 2) + std::pow((y1 - y0), 2)));
|
||||
|
||||
canvas_buffer *canvas = new canvas_buffer(size, size);
|
||||
bresenham_circle_tool *tool = new bresenham_circle_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++) {
|
||||
double distance =
|
||||
std::abs(std::sqrt(std::pow(x0 - x, 2) + std::pow(y0 - y, 2)) - r);
|
||||
// Because of rounding errors, an exact test (for all pixels) is not
|
||||
// feasible.
|
||||
// Therefore, it is only tested if set pixels have a distance <= 1.
|
||||
if (canvas->get_pixel(x, y) && distance > 1) {
|
||||
pass = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(pass);
|
||||
}
|
||||
|
|
Reference in a new issue