u02: Implement bresenham circle tool
parent
6b63595986
commit
e942c62ad6
|
@ -5,6 +5,7 @@
|
||||||
#include <catch2/generators/catch_generators_adapters.hpp>
|
#include <catch2/generators/catch_generators_adapters.hpp>
|
||||||
#include <catch2/generators/catch_generators_random.hpp>
|
#include <catch2/generators/catch_generators_random.hpp>
|
||||||
|
|
||||||
|
#include "bresenham_circle_tool.h"
|
||||||
#include "bresenham_line_tool.h"
|
#include "bresenham_line_tool.h"
|
||||||
#include "canvas_buffer.h"
|
#include "canvas_buffer.h"
|
||||||
#include "dda_line_tool.h"
|
#include "dda_line_tool.h"
|
||||||
|
@ -298,3 +299,71 @@ TEST_CASE("Rectangle (prop)") {
|
||||||
}
|
}
|
||||||
REQUIRE(pass);
|
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 New Issue