Compare commits
13 commits
4f3c184519
...
e64cbfdcf2
Author | SHA1 | Date | |
---|---|---|---|
Simon Bruder | e64cbfdcf2 | ||
Simon Bruder | 18da3567cc | ||
Simon Bruder | f482d0a951 | ||
Simon Bruder | 1aae98c18b | ||
Simon Bruder | 2290a6314d | ||
Simon Bruder | c5289609e5 | ||
Simon Bruder | 085b8fcdb9 | ||
Simon Bruder | 8f784ccf3e | ||
Simon Bruder | ad02a06db4 | ||
Simon Bruder | d4794e1544 | ||
Simon Bruder | 583f2ecd3e | ||
Simon Bruder | 85e38a358d | ||
Simon Bruder | 2226fb32af |
14
flake.nix
14
flake.nix
|
@ -7,6 +7,10 @@
|
||||||
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
|
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
mkNocheck = drv: drv.overrideAttrs (o: {
|
||||||
|
doCheck = false;
|
||||||
|
checkInputs = [ ];
|
||||||
|
});
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages = rec {
|
packages = rec {
|
||||||
|
@ -16,7 +20,8 @@
|
||||||
|
|
||||||
src = ./u01;
|
src = ./u01;
|
||||||
|
|
||||||
nativeBuildInputs = [ catch2_3 cmake ];
|
nativeBuildInputs = [ cmake ];
|
||||||
|
checkInputs = [ catch2_3 ];
|
||||||
|
|
||||||
doCheck = true;
|
doCheck = true;
|
||||||
})
|
})
|
||||||
|
@ -28,11 +33,16 @@
|
||||||
|
|
||||||
src = ./u02;
|
src = ./u02;
|
||||||
|
|
||||||
nativeBuildInputs = [ catch2_3 cmake freeglut libGL libGLU ];
|
nativeBuildInputs = [ cmake ];
|
||||||
|
buildInputs = [ freeglut libGL libGLU ];
|
||||||
|
checkInputs = [ catch2_3 ];
|
||||||
|
|
||||||
doCheck = true;
|
doCheck = true;
|
||||||
})
|
})
|
||||||
{ };
|
{ };
|
||||||
|
|
||||||
|
u01-nocheck = mkNocheck u01;
|
||||||
|
u02-nocheck = mkNocheck u02;
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
|
|
|
@ -2,12 +2,45 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tool_base.h"
|
#include "tool_base.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Return the points of a regular n-gon
|
||||||
|
// with the centre at (x, y)
|
||||||
|
// that is modified in the sense that it takes rf,
|
||||||
|
// which is used to set the radius for each individual point.
|
||||||
|
// It is rotated by base_angle,
|
||||||
|
// where 0 starts drawing at the top.
|
||||||
|
std::vector<std::pair<float, float>>
|
||||||
|
regular_polygon_mod(int n, std::function<float(int)> rf, int x = 0, int y = 0,
|
||||||
|
float base_angle = 0);
|
||||||
|
|
||||||
|
// Return the points of a star
|
||||||
|
// with n spikes,
|
||||||
|
// the inner radius r1,
|
||||||
|
// the outer radius r2
|
||||||
|
// and the centre (x, y),
|
||||||
|
// rotated by base_angle.
|
||||||
|
std::vector<std::pair<float, float>> star(int n, float r1, float r2, int x = 0,
|
||||||
|
int y = 0, float base_angle = 0);
|
||||||
|
|
||||||
class star_tool : public tool_base {
|
class star_tool : public tool_base {
|
||||||
public:
|
public:
|
||||||
star_tool(canvas_buffer &canvas);
|
// Initialize a new star tool.
|
||||||
|
// It can draw stars with arbitrary number of spikes (however, at least 2).
|
||||||
|
// The preview only supports spikes from 3 to 6 and falls back to 5
|
||||||
|
// if a spike outside of that range is given.
|
||||||
|
star_tool(canvas_buffer &canvas, int spikes = 5);
|
||||||
|
|
||||||
void draw(int x0, int y0, int x1, int y1);
|
void draw(int x0, int y0, int x1, int y1);
|
||||||
|
|
||||||
void set_text(std::stringstream &stream);
|
void set_text(std::stringstream &stream);
|
||||||
|
|
||||||
|
void set_spikes(int spikes);
|
||||||
|
|
||||||
|
void set_radius_factor(int radius_factor);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int spikes;
|
||||||
|
float radius_factor = 1.0 / 3.0;
|
||||||
};
|
};
|
||||||
|
|
75
u02/readme.txt
Normal file
75
u02/readme.txt
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<!-- vim: set ft=markdown: -->
|
||||||
|
<!-- LTeX: language=de-DE -->
|
||||||
|
<!-- SPDX-License-Identifier: LGPL-3.0-or-later -->
|
||||||
|
# Praxisaufgabe 2 Einführung in die Computergrafik
|
||||||
|
|
||||||
|
## Team
|
||||||
|
|
||||||
|
* Simon Bruder, Matrikelnummer: 5075324
|
||||||
|
|
||||||
|
## Bearbeitete Zusatzaufgaben
|
||||||
|
|
||||||
|
* Rechteck-Werkzeug (`rectangle_tool.cpp`)
|
||||||
|
* Kreisrasterisierer (`bresenham_circle_tool.cpp`)
|
||||||
|
* Rasterisierer für Sternform (`star_tool.cpp`)
|
||||||
|
* Sweepline-Algorithmus (`sweep_line_tool.cpp`)
|
||||||
|
|
||||||
|
## Hinweise
|
||||||
|
|
||||||
|
### Projektaufbau
|
||||||
|
|
||||||
|
Die vorgegebene Ordnerstruktur wurde beibehalten.
|
||||||
|
Es wurde jedoch die `CMakeLists.txt`-Datei in das Wurzelverzeichnis verschoben,
|
||||||
|
um einen üblichen Aufbau des Projektes zu erhalten,
|
||||||
|
und es wurden die nicht benötigten Verzeichnisse (`build`, `data`, `dependencies`, `src_solution`) entfernt,
|
||||||
|
um einen Stand zu erhalten, der tauglich für Versionskontrolle ist.
|
||||||
|
|
||||||
|
Das Projekt kann (abweichend von der Ausgangskonfiguration)
|
||||||
|
mit folgenden (für CMake-Projekte übliche) Befehlen gebaut werden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# in `u02`
|
||||||
|
mkdir -p build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
|
Für Tests der Implementation wurde [Catch2](https://github.com/catchorg/Catch2) eingebunden,
|
||||||
|
was jedoch optional ist und bei Nichtvorhandensein lediglich eine Nachricht beim Aufruf von CMake ausgibt,
|
||||||
|
welche aber keinen Fehler darstellt.
|
||||||
|
|
||||||
|
Zu einer Auslagerung von grundlegenden Funktionalitäten,
|
||||||
|
die nicht einem bestimmten Werkzeug zuzuordnen sind,
|
||||||
|
wurde die Hilfsdatei `util.cpp` (und der zugehörige Header `util.h`) angelegt,
|
||||||
|
welche in CMake eingebunden ist.
|
||||||
|
|
||||||
|
### Sternrasterisierer
|
||||||
|
|
||||||
|
Der Sternrasterisierer ermöglicht theoretisch
|
||||||
|
die Rasterisierung von Sternen mit beliebigen Zackenanzahlen
|
||||||
|
(wobei ein hartes Limit von mindestens 2 Zacken besteht,
|
||||||
|
jedoch erst ab 3 eine Art Stern vorliegt).
|
||||||
|
In dem Beispielprogramm kann jedoch aus Komplexitätsgründen nur aus bestimmten Zackenanzahlen gewählt werden,
|
||||||
|
was jedoch per Kontextmenü möglich ist und damit der Aufgabenstellung entspricht.
|
||||||
|
|
||||||
|
Darüber hinaus wurde rudimentär eine Vorschau der Sternform hinzugefügt.
|
||||||
|
Da die vorgegebene Architektur keine Spezialisierungen der Formen eines Tools erlaubt,
|
||||||
|
und C++ (anders als bspw. Rust) keine Tuple/Struct enums zulässt,
|
||||||
|
wurden hier nur die über die Benutzerschnittstelle verfügbaren Zackenanzahlen fest implementiert.
|
||||||
|
|
||||||
|
### Sweepline-Algorithmus
|
||||||
|
|
||||||
|
Der Sweepline-Algorithmus, der im Programm aufrufbar ist,
|
||||||
|
hat für ein Beispiel-Dreieck 3 Eckpunkte fest im Code definiert.
|
||||||
|
Er ist jedoch allgemein ausgelegt und kann mit drei beliebigen Punkten aufgerufen werden,
|
||||||
|
die ein valides Dreieck bilden.
|
||||||
|
|
||||||
|
Dafür wurde `tool_base` so modifiziert,
|
||||||
|
dass es Werkzeugen möglich ist,
|
||||||
|
eine `draw`-Methode mit keinem (für die fest definierten Eckpunkte)
|
||||||
|
oder drei Punkten (für beliebige Dreiecke)
|
||||||
|
anzubieten.
|
||||||
|
Damit das Werkzeug kompatibel mit der Architektur des Hauptprogramms ist,
|
||||||
|
bietet es jedoch zusätzlich die `draw`-Methode mit zwei Punkten an,
|
||||||
|
welche jedoch die Punkte ignoriert und die `draw`-Methode ohne Punkte aufruft.
|
|
@ -1,12 +1,92 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
#include "star_tool.h"
|
#include "star_tool.h"
|
||||||
|
#include "bresenham_line_tool.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
|
||||||
star_tool::star_tool(canvas_buffer &canvas) : tool_base(canvas) {
|
/*
|
||||||
shape = TS_CIRCLE; // TODO: Use star for preview
|
* The implementation is modeled after one in Haskell
|
||||||
|
* I created for EMI in the winter semester 2022/2023
|
||||||
|
* to output SVGs of regular polygons varied in different ways.
|
||||||
|
* It can be found here:
|
||||||
|
* https://git.sbruder.de/simon/emi5/src/branch/master/genstar/Main.hs
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::vector<std::pair<float, float>>
|
||||||
|
regular_polygon_mod(int n, std::function<float(int)> rf, int x, int y,
|
||||||
|
float base_angle) {
|
||||||
|
if (n < 3) {
|
||||||
|
std::cerr << "Polygons must have at least 3 vertices (regular_polygon_mod "
|
||||||
|
"was called with n="
|
||||||
|
<< n << ")." << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::vector<std::pair<float, float>> points = {};
|
||||||
|
for (int step = 0; step < n; step++) {
|
||||||
|
const float angle = base_angle + (step * 2.0 * atan(1) * 4.0) / n;
|
||||||
|
const float r = rf(step);
|
||||||
|
// Changed from Haskell implementation:
|
||||||
|
// It behaves like the unit circle,
|
||||||
|
// in that it starts at (1, 0) instead of (0, 1).
|
||||||
|
points.push_back({round(x + r * cosf(angle)), round(y + r * sinf(angle))});
|
||||||
|
}
|
||||||
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
void star_tool::draw(int x0, int y0, int x1, int y1) {}
|
std::vector<std::pair<float, float>> star(int n, float r1, float r2, int x,
|
||||||
|
int y, float base_angle) {
|
||||||
|
return regular_polygon_mod(
|
||||||
|
n * 2, [r1, r2](int i) { return i % 2 == 0 ? r2 : r1; }, x, y,
|
||||||
|
base_angle);
|
||||||
|
}
|
||||||
|
|
||||||
void star_tool::set_text(std::stringstream &stream) { stream << "Tool: Star"; }
|
star_tool::star_tool(canvas_buffer &canvas, int spikes) : tool_base(canvas) {
|
||||||
|
set_spikes(spikes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void star_tool::draw(int x0, int y0, int x1, int y1) {
|
||||||
|
const int r =
|
||||||
|
round(std::sqrt(std::pow((x1 - x0), 2) + std::pow((y1 - y0), 2)));
|
||||||
|
|
||||||
|
const float angle = atan2(y1 - y0, x1 - x0);
|
||||||
|
|
||||||
|
bresenham_line_tool *blt = new bresenham_line_tool(canvas);
|
||||||
|
|
||||||
|
const std::vector<std::pair<float, float>> points =
|
||||||
|
star(spikes, radius_factor * r, r, x0, y0, angle);
|
||||||
|
|
||||||
|
for (int i = 1; i <= points.size(); i++) {
|
||||||
|
blt->draw(round(points[i - 1].first), round(points[i - 1].second),
|
||||||
|
round(points[i % points.size()].first),
|
||||||
|
round(points[i % points.size()].second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void star_tool::set_text(std::stringstream &stream) {
|
||||||
|
stream << "Tool: Star (" << spikes << " spikes)";
|
||||||
|
}
|
||||||
|
|
||||||
|
void star_tool::set_spikes(int spikes) {
|
||||||
|
this->spikes = spikes;
|
||||||
|
switch (spikes) {
|
||||||
|
case 3:
|
||||||
|
shape = TS_STAR_3;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
shape = TS_STAR_4;
|
||||||
|
break;
|
||||||
|
// 5 is default, handled at the end
|
||||||
|
case 6:
|
||||||
|
shape = TS_STAR_6;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
default:
|
||||||
|
shape = TS_STAR_5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void star_tool::set_radius_factor(int radius_factor) {
|
||||||
|
this->radius_factor = radius_factor;
|
||||||
|
}
|
||||||
|
|
|
@ -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 <catch2/matchers/catch_matchers_floating_point.hpp>
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "bresenham_circle_tool.h"
|
#include "bresenham_circle_tool.h"
|
||||||
#include "bresenham_line_tool.h"
|
#include "bresenham_line_tool.h"
|
||||||
|
@ -13,10 +14,11 @@
|
||||||
#include "non_recursive_fill_tool.h"
|
#include "non_recursive_fill_tool.h"
|
||||||
#include "rectangle_tool.h"
|
#include "rectangle_tool.h"
|
||||||
#include "recursive_fill_tool.h"
|
#include "recursive_fill_tool.h"
|
||||||
|
#include "star_tool.h"
|
||||||
#include "sweep_line_tool.h"
|
#include "sweep_line_tool.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using Catch::Matchers::WithinRel;
|
using Catch::Matchers::WithinAbs;
|
||||||
|
|
||||||
TEST_CASE("Transform Mirror") {
|
TEST_CASE("Transform Mirror") {
|
||||||
// elementary operations
|
// elementary operations
|
||||||
|
@ -348,10 +350,15 @@ TEST_CASE("Bresenham circle (prop: √(x²+y²)-r<ε)") {
|
||||||
|
|
||||||
if ((x0 == min_c || x0 == max_c) && (x1 == min_c || x1 == 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)) {
|
(y0 == min_c || y0 == max_c) && (y1 == min_c || y1 == max_c)) {
|
||||||
SKIP("All coordinates have extreme value, skipping (avoid rounding error)");
|
// catch2’s SKIP macro does not exactly do what I want here,
|
||||||
|
// so this just returns from the function and prints a message.
|
||||||
|
std::cerr
|
||||||
|
<< "All coordinates have extreme value, skipping (avoid rounding error)"
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int r =
|
const float r =
|
||||||
round(std::sqrt(std::pow((x1 - x0), 2) + std::pow((y1 - y0), 2)));
|
round(std::sqrt(std::pow((x1 - x0), 2) + std::pow((y1 - y0), 2)));
|
||||||
|
|
||||||
canvas_buffer *canvas = new canvas_buffer(size, size);
|
canvas_buffer *canvas = new canvas_buffer(size, size);
|
||||||
|
@ -362,7 +369,7 @@ TEST_CASE("Bresenham circle (prop: √(x²+y²)-r<ε)") {
|
||||||
bool pass = true;
|
bool pass = true;
|
||||||
for (int x = 0; x < size; x++) {
|
for (int x = 0; x < size; x++) {
|
||||||
for (int y = 0; y < size; y++) {
|
for (int y = 0; y < size; y++) {
|
||||||
double distance =
|
float distance =
|
||||||
std::abs(std::sqrt(std::pow(x0 - x, 2) + std::pow(y0 - y, 2)) - r);
|
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
|
// Because of rounding errors, an exact test (for all pixels) is not
|
||||||
// feasible.
|
// feasible.
|
||||||
|
@ -431,16 +438,22 @@ TEST_CASE("Barycentric coordinates (prop: Σ = 1)") {
|
||||||
int x2 = GENERATE(take(2, random(-100, 100)));
|
int x2 = GENERATE(take(2, random(-100, 100)));
|
||||||
int y2 = GENERATE(take(2, random(-100, 100)));
|
int y2 = GENERATE(take(2, random(-100, 100)));
|
||||||
|
|
||||||
|
// If all points are on a straight line, the property does not hold.
|
||||||
|
// Checking this is equivalent to checking if the area of the triangle is 0.
|
||||||
|
if ((x0 - x1) * (y0 - y2) - (y0 - y1) * (x0 - x2) == 0) {
|
||||||
|
// see above
|
||||||
|
std::cerr << "Points do not form reasonable triangle, skipping"
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int x = GENERATE(take(5, random(-100, 100)));
|
int x = GENERATE(take(5, random(-100, 100)));
|
||||||
int y = GENERATE(take(5, random(-100, 100)));
|
int y = GENERATE(take(5, random(-100, 100)));
|
||||||
float b1, b2, b3;
|
float b1, b2, b3;
|
||||||
|
|
||||||
std::tie(b1, b2, b3) = barycentric_coordinates(x0, y0, x1, y1, x2, y2, x, y);
|
std::tie(b1, b2, b3) = barycentric_coordinates(x0, y0, x1, y1, x2, y2, x, y);
|
||||||
|
|
||||||
// If all points are on a straight line, the property does not hold
|
REQUIRE_THAT(b1 + b2 + b3, WithinAbs(1.0, 0.01));
|
||||||
if (!(x0 == x1 && x1 == x2) && !(y0 == y1 && y1 == y2)) {
|
|
||||||
REQUIRE_THAT(b1 + b2 + b3, WithinRel(1.0, 0.01));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Sort triangle points") {
|
TEST_CASE("Sort triangle points") {
|
||||||
|
@ -501,7 +514,7 @@ TEST_CASE("Sweep line (prop: Barycentric coordinates)") {
|
||||||
for (int y = 0; y < size; y++) {
|
for (int y = 0; y < size; y++) {
|
||||||
if (point_in_triangle(x0, y0, x1, y1, x2, y2, x, y)) {
|
if (point_in_triangle(x0, y0, x1, y1, x2, y2, x, y)) {
|
||||||
if (!canvas->get_pixel(x, y)) {
|
if (!canvas->get_pixel(x, y)) {
|
||||||
// Barycentric coordinates say, point is not in triangle,
|
// Barycentric coordinates say, point is in triangle,
|
||||||
// but point is not set.
|
// but point is not set.
|
||||||
// This must not happen → fail test.
|
// This must not happen → fail test.
|
||||||
pass = false;
|
pass = false;
|
||||||
|
@ -522,3 +535,70 @@ TEST_CASE("Sweep line (prop: Barycentric coordinates)") {
|
||||||
REQUIRE(deviating < abs(y1 - y0) + abs(y2 - y1) + abs(y0 - y2) +
|
REQUIRE(deviating < abs(y1 - y0) + abs(y2 - y1) + abs(y0 - y2) +
|
||||||
abs(x1 - x0) + abs(x2 - x1) + abs(x0 - x2));
|
abs(x1 - x0) + abs(x2 - x1) + abs(x0 - x2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Star (prop: all points inside circle)") {
|
||||||
|
// See test “Bresenham circle (prop: √(x²+y²)-r<ε)” for what this does.
|
||||||
|
const int size = 100;
|
||||||
|
const int max_c = std::floor(size / (3 - std::sqrt(2)));
|
||||||
|
const int min_c = std::ceil(((4 - std::sqrt(2)) / 7) * size);
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
// see above
|
||||||
|
std::cerr
|
||||||
|
<< "All coordinates have extreme value, skipping (avoid rounding error)"
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float r = std::sqrt(std::pow((x1 - x0), 2) + std::pow((y1 - y0), 2));
|
||||||
|
|
||||||
|
// correction factor for very small stars
|
||||||
|
const float correction = r < 2 ? 1 : 0;
|
||||||
|
|
||||||
|
canvas_buffer *canvas = new canvas_buffer(size, size);
|
||||||
|
star_tool *tool = new star_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 (canvas->get_pixel(x, y) &&
|
||||||
|
(std::sqrt(std::pow(x0 - x, 2) + std::pow(y0 - y, 2)) >
|
||||||
|
r * r + correction)) {
|
||||||
|
pass = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REQUIRE(pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Star (prop: average of all points is centre)") {
|
||||||
|
const int x0 = GENERATE(take(5, random(-100, 100)));
|
||||||
|
const int y0 = GENERATE(take(5, random(-100, 100)));
|
||||||
|
const int n = GENERATE(take(5, random(3, 1000)));
|
||||||
|
const float r2 = GENERATE(take(5, random(0, 100)));
|
||||||
|
const float r_factor = GENERATE(take(5, random(0, 1)));
|
||||||
|
const float r1 = r_factor * r2;
|
||||||
|
const float angle = GENERATE(take(5, random(-16.0 * atan(1), 16 * atan(1))));
|
||||||
|
|
||||||
|
std::vector<std::pair<float, float>> points = star(n, r1, r2, x0, y0, angle);
|
||||||
|
|
||||||
|
float x = 0, y = 0;
|
||||||
|
for (std::pair<float, float> point : points) {
|
||||||
|
x += point.first;
|
||||||
|
y += point.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE_THAT(x / points.size(), WithinAbs(x0, 0.5));
|
||||||
|
REQUIRE_THAT(y / points.size(), WithinAbs(y0, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Haskell implementation of regular_polygon_mod has many more tests,
|
||||||
|
// but they are not implemented here for brevity.
|
||||||
|
|
Reference in a new issue