Compare commits
6 Commits
e16df7e565
...
4f3c184519
Author | SHA1 | Date |
---|---|---|
Simon Bruder | 4f3c184519 | |
Simon Bruder | 9239c2904a | |
Simon Bruder | 9871b11bfe | |
Simon Bruder | f330960b78 | |
Simon Bruder | 2657f6c2a4 | |
Simon Bruder | 1dcdf09b2a |
|
@ -7,6 +7,10 @@
|
|||
outputs = { self, nixpkgs, flake-utils }: flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
mkNocheck = drv: drv.overrideAttrs (o: {
|
||||
doCheck = false;
|
||||
nativeBuildInputs = pkgs.lib.filter (p: p != pkgs.catch2_3) o.nativeBuildInputs;
|
||||
});
|
||||
in
|
||||
{
|
||||
packages = rec {
|
||||
|
@ -33,6 +37,9 @@
|
|||
doCheck = true;
|
||||
})
|
||||
{ };
|
||||
|
||||
u01-nocheck = mkNocheck u01;
|
||||
u02-nocheck = mkNocheck u02;
|
||||
};
|
||||
|
||||
devShells.default = pkgs.mkShell {
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<!-- 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,
|
||||
was jedoch nur für eine feste Zackenanzahl (hier: 5) möglich ist,
|
||||
ohne die Architektur der Applikation zu ändern.
|
||||
|
||||
### 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.
|
|
@ -5,6 +5,7 @@
|
|||
#include <catch2/generators/catch_generators_adapters.hpp>
|
||||
#include <catch2/generators/catch_generators_random.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#include "bresenham_circle_tool.h"
|
||||
#include "bresenham_line_tool.h"
|
||||
|
@ -17,7 +18,7 @@
|
|||
#include "sweep_line_tool.h"
|
||||
#include "util.h"
|
||||
|
||||
using Catch::Matchers::WithinRel;
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
TEST_CASE("Transform Mirror") {
|
||||
// elementary operations
|
||||
|
@ -349,7 +350,12 @@ TEST_CASE("Bresenham circle (prop: √(x²+y²)-r<ε)") {
|
|||
|
||||
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)");
|
||||
// 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 float r =
|
||||
|
@ -432,16 +438,22 @@ TEST_CASE("Barycentric coordinates (prop: Σ = 1)") {
|
|||
int x2 = 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 y = GENERATE(take(5, random(-100, 100)));
|
||||
float b1, b2, b3;
|
||||
|
||||
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
|
||||
if (!(x0 == x1 && x1 == x2) && !(y0 == y1 && y1 == y2)) {
|
||||
REQUIRE_THAT(b1 + b2 + b3, WithinRel(1.0, 0.01));
|
||||
}
|
||||
REQUIRE_THAT(b1 + b2 + b3, WithinAbs(1.0, 0.01));
|
||||
}
|
||||
|
||||
TEST_CASE("Sort triangle points") {
|
||||
|
@ -502,7 +514,7 @@ TEST_CASE("Sweep line (prop: Barycentric coordinates)") {
|
|||
for (int y = 0; y < size; y++) {
|
||||
if (point_in_triangle(x0, y0, x1, y1, x2, y2, 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.
|
||||
// This must not happen → fail test.
|
||||
pass = false;
|
||||
|
@ -537,7 +549,11 @@ TEST_CASE("Star (prop: all points inside circle)") {
|
|||
|
||||
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)");
|
||||
// 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));
|
||||
|
@ -580,8 +596,8 @@ TEST_CASE("Star (prop: average of all points is centre)") {
|
|||
y += point.second;
|
||||
}
|
||||
|
||||
REQUIRE_THAT(x / points.size(), WithinRel(x0, 0.1));
|
||||
REQUIRE_THAT(y / points.size(), WithinRel(y0, 0.1));
|
||||
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,
|
||||
|
|
Reference in New Issue