It works!!!!!!
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -32,3 +32,7 @@
|
|||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
|
# Linux executables (keine Endung)
|
||||||
|
*
|
||||||
|
!*/
|
||||||
|
!*.*
|
||||||
|
|||||||
43
.vscode/tasks.json
vendored
Normal file
43
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "cppbuild",
|
||||||
|
"label": "C/C++: g++ build active file",
|
||||||
|
"command": "/usr/bin/g++",
|
||||||
|
"args": [
|
||||||
|
"-fdiagnostics-color=always",
|
||||||
|
"-g",
|
||||||
|
"-std=c++26",
|
||||||
|
"-pedantic-errors",
|
||||||
|
"-Wall",
|
||||||
|
"-Weffc++",
|
||||||
|
"-Wextra",
|
||||||
|
"-Wconversion",
|
||||||
|
"-Wsign-conversion",
|
||||||
|
"-Werror",
|
||||||
|
"${fileDirname}/**.cpp",
|
||||||
|
"-o",
|
||||||
|
"${fileDirname}/${fileBasenameNoExtension}",
|
||||||
|
"-lraylib",
|
||||||
|
"-lGL",
|
||||||
|
"-lm",
|
||||||
|
"-lpthread",
|
||||||
|
"-ldl",
|
||||||
|
"-lrt",
|
||||||
|
"-lX11"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${fileDirname}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"detail": "Task generated by Debugger."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
11
include/constants.hpp
Normal file
11
include/constants.hpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Constants {
|
||||||
|
constexpr int maxIterations{2000};
|
||||||
|
constexpr int windowWidth{600};
|
||||||
|
constexpr int windowHeight{600};
|
||||||
|
constexpr double posRealRange{3.0};
|
||||||
|
constexpr double negRealRange{-3.0};
|
||||||
|
constexpr double posImagRange{3.0};
|
||||||
|
constexpr double negImagRange{-3.0};
|
||||||
|
} // Namespace Constants
|
||||||
3
include/mandelbrot.hpp
Normal file
3
include/mandelbrot.hpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
int calculateMandelbrot(double cReal, double cImag);
|
||||||
57
main.cpp
57
main.cpp
@@ -0,0 +1,57 @@
|
|||||||
|
#include "include/constants.hpp"
|
||||||
|
#include "include/mandelbrot.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <raylib.h>
|
||||||
|
|
||||||
|
Color LerpColor(Color a, Color b, float t) {
|
||||||
|
return {(unsigned char)(a.r + (b.r - a.r) * t),
|
||||||
|
(unsigned char)(a.g + (b.g - a.g) * t),
|
||||||
|
(unsigned char)(a.b + (b.b - a.b) * t), 255};
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawMandelbrot() {
|
||||||
|
using namespace Constants;
|
||||||
|
|
||||||
|
double xNorm{};
|
||||||
|
double yNorm{};
|
||||||
|
double cReal{};
|
||||||
|
double cImag{};
|
||||||
|
|
||||||
|
for (int x{}; x <= Constants::windowWidth; ++x) {
|
||||||
|
for (int y{}; y <= Constants::windowHeight; ++y) {
|
||||||
|
xNorm = x / static_cast<double>(Constants::windowWidth);
|
||||||
|
yNorm = y / static_cast<double>(Constants::windowHeight);
|
||||||
|
|
||||||
|
cReal = negRealRange + xNorm * (posRealRange - negRealRange);
|
||||||
|
cImag = negImagRange + yNorm * (posImagRange - negImagRange);
|
||||||
|
|
||||||
|
int iterationsMandelbrot{calculateMandelbrot(cReal, cImag)};
|
||||||
|
|
||||||
|
if (iterationsMandelbrot - 1 == maxIterations) {
|
||||||
|
DrawPixel(x, y, BLACK);
|
||||||
|
} else {
|
||||||
|
float t = static_cast<float>(iterationsMandelbrot) / maxIterations;
|
||||||
|
Color color = LerpColor(BLUE, RED, t);
|
||||||
|
DrawPixel(x, y, color);
|
||||||
|
}
|
||||||
|
// std::cout << "Iterations: " << iterationsMandelbrot << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
InitWindow(Constants::windowWidth, Constants::windowHeight,
|
||||||
|
"Mandelbrot Plot");
|
||||||
|
SetTargetFPS(60);
|
||||||
|
|
||||||
|
while (!WindowShouldClose()) { // Runs until ESC or window close
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
drawMandelbrot();
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseWindow();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
28
mandelbrot.cpp
Normal file
28
mandelbrot.cpp
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include "include/mandelbrot.hpp"
|
||||||
|
#include "include/constants.hpp"
|
||||||
|
|
||||||
|
int calculateMandelbrot(double cReal, double cImag) {
|
||||||
|
double zImag{0};
|
||||||
|
double zReal{0};
|
||||||
|
double zImagNeu{};
|
||||||
|
double zRealNeu{};
|
||||||
|
double zImagSquared{};
|
||||||
|
double zRealSquared{};
|
||||||
|
int iterations{};
|
||||||
|
|
||||||
|
for (; iterations <= Constants::maxIterations; ++iterations) {
|
||||||
|
zRealSquared = (zReal * zReal);
|
||||||
|
zImagSquared = (zImag * zImag);
|
||||||
|
|
||||||
|
zRealNeu = zRealSquared - zImagSquared + cReal;
|
||||||
|
zImagNeu = 2 * zReal * zImag + cImag;
|
||||||
|
|
||||||
|
zReal = zRealNeu;
|
||||||
|
zImag = zImagNeu;
|
||||||
|
|
||||||
|
if (zRealSquared + zImagSquared > 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iterations;
|
||||||
|
}
|
||||||
684
md/erklärung.md
Normal file
684
md/erklärung.md
Normal file
@@ -0,0 +1,684 @@
|
|||||||
|
# Die Mandelbrot-Menge: Von Null auf Visualisierung
|
||||||
|
|
||||||
|
## 1. Was sind komplexe Zahlen?
|
||||||
|
|
||||||
|
### 1.1 Die imaginäre Einheit
|
||||||
|
|
||||||
|
Normale Zahlen (reelle Zahlen) kennen wir alle: 1, 2, 3.5, -7, π usw. Diese liegen alle auf einem eindimensionalen Zahlenstrahl.
|
||||||
|
|
||||||
|
Komplexe Zahlen erweitern dieses Konzept in die zweite Dimension. Der Trick: Wir erfinden eine neue Zahl namens **i** (die imaginäre Einheit) mit der Eigenschaft:
|
||||||
|
|
||||||
|
```
|
||||||
|
i² = -1
|
||||||
|
```
|
||||||
|
|
||||||
|
Das ist zunächst verwirrend, weil keine normale Zahl diese Eigenschaft hat (sowohl 2² = 4 als auch (-2)² = 4). Aber mathematisch können wir mit **i** genauso rechnen wie mit normalen Zahlen.
|
||||||
|
|
||||||
|
### 1.2 Aufbau komplexer Zahlen
|
||||||
|
|
||||||
|
Eine komplexe Zahl besteht aus zwei Teilen:
|
||||||
|
|
||||||
|
```
|
||||||
|
z = a + bi
|
||||||
|
```
|
||||||
|
|
||||||
|
- **a** = Realteil (eine normale Zahl)
|
||||||
|
- **b** = Imaginärteil (auch eine normale Zahl)
|
||||||
|
- **i** = die imaginäre Einheit
|
||||||
|
|
||||||
|
**Beispiele:**
|
||||||
|
- `3 + 4i` → Realteil: 3, Imaginärteil: 4
|
||||||
|
- `2 - 5i` → Realteil: 2, Imaginärteil: -5
|
||||||
|
- `7 + 0i = 7` → eine rein reelle Zahl
|
||||||
|
- `0 + 3i = 3i` → eine rein imaginäre Zahl
|
||||||
|
|
||||||
|
### 1.3 Visualisierung: Die komplexe Ebene
|
||||||
|
|
||||||
|
Statt einem Zahlenstrahl verwenden wir eine Ebene:
|
||||||
|
- **x-Achse (horizontal)**: Realteil
|
||||||
|
- **y-Achse (vertikal)**: Imaginärteil
|
||||||
|
|
||||||
|
Die Zahl `3 + 4i` liegt also am Punkt (3, 4) in dieser Ebene.
|
||||||
|
|
||||||
|
```
|
||||||
|
Imaginärteil (y)
|
||||||
|
↑
|
||||||
|
5 |
|
||||||
|
4 | • (3+4i)
|
||||||
|
3 |
|
||||||
|
2 |
|
||||||
|
1 |
|
||||||
|
0 |-------|-------|→ Realteil (x)
|
||||||
|
-1 | 2 4
|
||||||
|
-2 |
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.4 Rechnen mit komplexen Zahlen
|
||||||
|
|
||||||
|
#### Addition
|
||||||
|
Einfach: Addiere Real- und Imaginärteile getrennt.
|
||||||
|
|
||||||
|
```
|
||||||
|
(a + bi) + (c + di) = (a + c) + (b + d)i
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
(3 + 4i) + (1 + 2i) = (3+1) + (4+2)i = 4 + 6i
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multiplikation
|
||||||
|
Hier wird's interessanter. Wir multiplizieren wie bei Klammern, aber beachten dass **i² = -1**:
|
||||||
|
|
||||||
|
```
|
||||||
|
(a + bi) × (c + di) = ac + adi + bci + bdi²
|
||||||
|
= ac + adi + bci + bd(-1)
|
||||||
|
= (ac - bd) + (ad + bc)i
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
(3 + 4i) × (1 + 2i) = 3×1 + 3×2i + 4i×1 + 4i×2i
|
||||||
|
= 3 + 6i + 4i + 8i²
|
||||||
|
= 3 + 10i + 8×(-1)
|
||||||
|
= 3 + 10i - 8
|
||||||
|
= -5 + 10i
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Spezialfall: Quadrieren
|
||||||
|
Besonders wichtig für die Mandelbrot-Menge ist das Quadrieren:
|
||||||
|
|
||||||
|
```
|
||||||
|
(a + bi)² = (a + bi) × (a + bi)
|
||||||
|
= a² + abi + abi + (bi)²
|
||||||
|
= a² + 2abi + b²i²
|
||||||
|
= a² + 2abi - b²
|
||||||
|
= (a² - b²) + 2abi
|
||||||
|
```
|
||||||
|
|
||||||
|
**Merke dir diese Formel:**
|
||||||
|
```
|
||||||
|
(a + bi)² = (a² - b²) + (2ab)i
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
(3 + 4i)² = (3² - 4²) + (2×3×4)i
|
||||||
|
= (9 - 16) + 24i
|
||||||
|
= -7 + 24i
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.5 Betrag (Abstand vom Ursprung)
|
||||||
|
|
||||||
|
Der Betrag einer komplexen Zahl ist ihr Abstand vom Ursprung (0, 0). Mit dem Satz des Pythagoras:
|
||||||
|
|
||||||
|
```
|
||||||
|
|a + bi| = √(a² + b²)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```
|
||||||
|
|3 + 4i| = √(3² + 4²) = √(9 + 16) = √25 = 5
|
||||||
|
```
|
||||||
|
|
||||||
|
In der Ebene visualisiert ist das die Länge des Pfeils vom Ursprung zum Punkt (3, 4).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Die Mandelbrot-Iteration
|
||||||
|
|
||||||
|
### 2.1 Die Grundidee
|
||||||
|
|
||||||
|
Die Mandelbrot-Menge ist eine Menge von komplexen Zahlen **c**, die eine bestimmte Eigenschaft haben.
|
||||||
|
|
||||||
|
Für jede komplexe Zahl **c** führen wir folgende Iteration durch:
|
||||||
|
|
||||||
|
```
|
||||||
|
z₀ = 0
|
||||||
|
z₁ = z₀² + c = 0² + c = c
|
||||||
|
z₂ = z₁² + c
|
||||||
|
z₃ = z₂² + c
|
||||||
|
z₄ = z₃² + c
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Die Frage:** Bleibt die Folge z₀, z₁, z₂, z₃, ... beschränkt (in der Nähe des Ursprungs), oder explodiert sie gegen unendlich?
|
||||||
|
|
||||||
|
**Definition:** Die Zahl **c** gehört zur Mandelbrot-Menge, wenn die Folge beschränkt bleibt (nicht gegen unendlich geht).
|
||||||
|
|
||||||
|
### 2.2 Beispiel 1: c = 0
|
||||||
|
|
||||||
|
```
|
||||||
|
z₀ = 0
|
||||||
|
z₁ = 0² + 0 = 0
|
||||||
|
z₂ = 0² + 0 = 0
|
||||||
|
z₃ = 0² + 0 = 0
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Folge bleibt bei 0. **c = 0 gehört zur Mandelbrot-Menge.**
|
||||||
|
|
||||||
|
### 2.3 Beispiel 2: c = 1
|
||||||
|
|
||||||
|
```
|
||||||
|
z₀ = 0
|
||||||
|
z₁ = 0² + 1 = 1
|
||||||
|
z₂ = 1² + 1 = 2
|
||||||
|
z₃ = 2² + 1 = 5
|
||||||
|
z₄ = 5² + 1 = 26
|
||||||
|
z₅ = 26² + 1 = 677
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Folge explodiert! **c = 1 gehört NICHT zur Mandelbrot-Menge.**
|
||||||
|
|
||||||
|
### 2.4 Beispiel 3: c = -1
|
||||||
|
|
||||||
|
```
|
||||||
|
z₀ = 0
|
||||||
|
z₁ = 0² + (-1) = -1
|
||||||
|
z₂ = (-1)² + (-1) = 1 - 1 = 0
|
||||||
|
z₃ = 0² + (-1) = -1
|
||||||
|
z₄ = (-1)² + (-1) = 0
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Folge springt zwischen -1 und 0 hin und her. Sie bleibt beschränkt. **c = -1 gehört zur Mandelbrot-Menge.**
|
||||||
|
|
||||||
|
### 2.5 Beispiel 4: c = i (eine komplexe Zahl!)
|
||||||
|
|
||||||
|
```
|
||||||
|
z₀ = 0
|
||||||
|
z₁ = 0² + i = i
|
||||||
|
z₂ = i² + i = -1 + i
|
||||||
|
z₃ = (-1 + i)² + i
|
||||||
|
```
|
||||||
|
|
||||||
|
Berechnen wir z₃ mit unserer Quadrierformel (a=-1, b=1):
|
||||||
|
```
|
||||||
|
(-1 + i)² = ((-1)² - 1²) + (2×(-1)×1)i
|
||||||
|
= (1 - 1) + (-2)i
|
||||||
|
= -2i
|
||||||
|
|
||||||
|
z₃ = -2i + i = -i
|
||||||
|
```
|
||||||
|
|
||||||
|
Weiter:
|
||||||
|
```
|
||||||
|
z₄ = (-i)² + i = (0 + (-1)i)² + i
|
||||||
|
= (0² - (-1)²) + (2×0×(-1))i + i
|
||||||
|
= -1 + 0i + i
|
||||||
|
= -1 + i
|
||||||
|
```
|
||||||
|
|
||||||
|
Moment! z₄ = z₂. Die Folge wiederholt sich jetzt:
|
||||||
|
```
|
||||||
|
z₂ = -1 + i
|
||||||
|
z₃ = -i
|
||||||
|
z₄ = -1 + i
|
||||||
|
z₅ = -i
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Folge bleibt beschränkt (pendelt zwischen zwei Werten). **c = i gehört zur Mandelbrot-Menge.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Der Algorithmus zur Visualisierung
|
||||||
|
|
||||||
|
### 3.1 Warum nicht unendlich lange testen?
|
||||||
|
|
||||||
|
Wir können nicht unendlich viele Iterationen durchführen. Daher verwenden wir zwei Abbruchkriterien:
|
||||||
|
|
||||||
|
**Kriterium 1: Divergenz erkannt**
|
||||||
|
Mathematisch bewiesen: Wenn der Betrag von z jemals größer als 2 wird (|z| > 2), dann wird die Folge definitiv gegen unendlich gehen.
|
||||||
|
|
||||||
|
Da |z| = √(real² + imag²) ist, können wir stattdessen prüfen:
|
||||||
|
```
|
||||||
|
real² + imag² > 4
|
||||||
|
```
|
||||||
|
(Das spart die Wurzelberechnung, weil 2² = 4)
|
||||||
|
|
||||||
|
**Kriterium 2: Maximale Iterationen**
|
||||||
|
Wir setzen eine Obergrenze, z.B. 100 oder 1000 Iterationen. Wenn wir diese erreichen ohne Divergenz, nehmen wir an, dass c zur Menge gehört.
|
||||||
|
|
||||||
|
### 3.2 Der Pseudocode
|
||||||
|
|
||||||
|
Für jede komplexe Zahl c = (c_real, c_imag):
|
||||||
|
|
||||||
|
```
|
||||||
|
z_real = 0
|
||||||
|
z_imag = 0
|
||||||
|
iteration = 0
|
||||||
|
max_iterations = 1000
|
||||||
|
|
||||||
|
solange (iteration < max_iterations):
|
||||||
|
// Berechne z² + c
|
||||||
|
z_real_neu = z_real² - z_imag² + c_real
|
||||||
|
z_imag_neu = 2 × z_real × z_imag + c_imag
|
||||||
|
|
||||||
|
z_real = z_real_neu
|
||||||
|
z_imag = z_imag_neu
|
||||||
|
|
||||||
|
// Divergenz-Check
|
||||||
|
wenn (z_real² + z_imag² > 4):
|
||||||
|
break // Divergenz erkannt!
|
||||||
|
|
||||||
|
iteration = iteration + 1
|
||||||
|
|
||||||
|
// Ergebnis: iteration
|
||||||
|
// - Wenn iteration = max_iterations: c ist in der Menge (schwarz färben)
|
||||||
|
// - Sonst: c ist außerhalb (Farbe basierend auf iteration)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 Wichtige Details zur Implementierung
|
||||||
|
|
||||||
|
**1. Reihenfolge der Berechnung**
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// FALSCH:
|
||||||
|
z_real = z_real * z_real - z_imag * z_imag + c_real;
|
||||||
|
z_imag = 2 * z_real * z_imag + c_imag; // Nutzt bereits das neue z_real!
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// RICHTIG:
|
||||||
|
double z_real_neu = z_real * z_real - z_imag * z_imag + c_real;
|
||||||
|
double z_imag_neu = 2 * z_real * z_imag + c_imag;
|
||||||
|
z_real = z_real_neu;
|
||||||
|
z_imag = z_imag_neu;
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Optimierung der Divergenz-Prüfung**
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Berechne die Quadrate nur einmal:
|
||||||
|
double z_real_squared = z_real * z_real;
|
||||||
|
double z_imag_squared = z_imag * z_imag;
|
||||||
|
|
||||||
|
double z_real_neu = z_real_squared - z_imag_squared + c_real;
|
||||||
|
double z_imag_neu = 2 * z_real * z_imag + c_imag;
|
||||||
|
|
||||||
|
// Divergenz-Check
|
||||||
|
if (z_real_squared + z_imag_squared > 4.0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Von Pixeln zur komplexen Ebene
|
||||||
|
|
||||||
|
### 4.1 Das Problem
|
||||||
|
|
||||||
|
Dein Bildschirm hat Pixel mit Koordinaten:
|
||||||
|
- x: 0 bis fenster_breite (z.B. 0 bis 800)
|
||||||
|
- y: 0 bis fenster_hoehe (z.B. 0 bis 600)
|
||||||
|
|
||||||
|
Die interessante Region der Mandelbrot-Menge liegt bei:
|
||||||
|
- Real-Achse: ungefähr -2.5 bis +1.0
|
||||||
|
- Imaginär-Achse: ungefähr -1.0 bis +1.0
|
||||||
|
|
||||||
|
Wir müssen jeden Pixel auf einen Punkt in der komplexen Ebene abbilden.
|
||||||
|
|
||||||
|
### 4.2 Die Mapping-Formel
|
||||||
|
|
||||||
|
#### Schritt 1: Normalisiere Pixelkoordinaten auf [0, 1]
|
||||||
|
|
||||||
|
```
|
||||||
|
x_normiert = x / fenster_breite // Wert zwischen 0 und 1
|
||||||
|
y_normiert = y / fenster_hoehe // Wert zwischen 0 und 1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Schritt 2: Skaliere auf den gewünschten Bereich
|
||||||
|
|
||||||
|
```
|
||||||
|
c_real = min_real + x_normiert × (max_real - min_real)
|
||||||
|
c_imag = min_imag + y_normiert × (max_imag - min_imag)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Beispiel mit konkreten Zahlen
|
||||||
|
|
||||||
|
Angenommen:
|
||||||
|
- Fenster: 800×600 Pixel
|
||||||
|
- Bereich: real [-2.5, 1.0], imaginär [-1.0, 1.0]
|
||||||
|
|
||||||
|
Für Pixel (400, 300) (die Fenstermitte):
|
||||||
|
|
||||||
|
```
|
||||||
|
x_normiert = 400 / 800 = 0.5
|
||||||
|
y_normiert = 300 / 600 = 0.5
|
||||||
|
|
||||||
|
c_real = -2.5 + 0.5 × (1.0 - (-2.5))
|
||||||
|
= -2.5 + 0.5 × 3.5
|
||||||
|
= -2.5 + 1.75
|
||||||
|
= -0.75
|
||||||
|
|
||||||
|
c_imag = -1.0 + 0.5 × (1.0 - (-1.0))
|
||||||
|
= -1.0 + 0.5 × 2.0
|
||||||
|
= -1.0 + 1.0
|
||||||
|
= 0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Fenstermitte entspricht also der komplexen Zahl c = -0.75 + 0i.
|
||||||
|
|
||||||
|
### 4.3 Koordinatensysteme beachten
|
||||||
|
|
||||||
|
**Problem:** In den meisten Grafiksystemen ist y=0 oben und y wächst nach unten. In der Mathematik wächst die imaginäre Achse nach oben.
|
||||||
|
|
||||||
|
**Lösung 1:** Y-Koordinate umdrehen
|
||||||
|
```
|
||||||
|
y_normiert = 1.0 - (y / fenster_hoehe)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lösung 2:** min_imag und max_imag vertauschen
|
||||||
|
```
|
||||||
|
c_imag = max_imag - y_normiert × (max_imag - min_imag)
|
||||||
|
```
|
||||||
|
|
||||||
|
Beide führen zum gleichen Ergebnis: Die Mandelbrot-Menge wird richtig herum angezeigt.
|
||||||
|
|
||||||
|
### 4.4 Seitenverhältnis (Aspect Ratio)
|
||||||
|
|
||||||
|
Wenn dein Fenster nicht quadratisch ist, musst du aufpassen, dass Kreise nicht zu Ellipsen werden.
|
||||||
|
|
||||||
|
**Option 1:** Passe den Bereich an das Fensterverhältnis an
|
||||||
|
|
||||||
|
```
|
||||||
|
aspect_ratio = fenster_breite / fenster_hoehe
|
||||||
|
|
||||||
|
real_span = max_real - min_real
|
||||||
|
imag_span = max_imag - min_imag
|
||||||
|
|
||||||
|
// Zentriere und passe an
|
||||||
|
center_real = (min_real + max_real) / 2
|
||||||
|
center_imag = (min_imag + max_imag) / 2
|
||||||
|
|
||||||
|
if (aspect_ratio > 1.0) {
|
||||||
|
// Fenster ist breiter als hoch → erweitere Real-Achse
|
||||||
|
half_span = imag_span * aspect_ratio / 2
|
||||||
|
min_real = center_real - half_span
|
||||||
|
max_real = center_real + half_span
|
||||||
|
} else {
|
||||||
|
// Fenster ist höher als breit → erweitere Imaginär-Achse
|
||||||
|
half_span = real_span / aspect_ratio / 2
|
||||||
|
min_imag = center_imag - half_span
|
||||||
|
max_imag = center_imag + half_span
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2:** Rendere in ein quadratisches Bild und zeige es zentriert im Fenster
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Färbung und Visualisierung
|
||||||
|
|
||||||
|
### 5.1 Basis-Färbung
|
||||||
|
|
||||||
|
Das Einfachste:
|
||||||
|
- **In der Menge** (max_iterations erreicht): Schwarz
|
||||||
|
- **Außerhalb**: Färbung basierend auf der Iterationszahl
|
||||||
|
|
||||||
|
```
|
||||||
|
if (iteration == max_iterations) {
|
||||||
|
farbe = SCHWARZ
|
||||||
|
} else {
|
||||||
|
// iteration geht von 0 bis max_iterations-1
|
||||||
|
helligkeitswert = iteration / max_iterations
|
||||||
|
farbe = helligkeitswert // z.B. 0 = schwarz, 1 = weiß
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 Farb-Paletten
|
||||||
|
|
||||||
|
Statt Graustufen kannst du Farbverläufe verwenden:
|
||||||
|
|
||||||
|
**Linear mapping:**
|
||||||
|
```
|
||||||
|
farbindex = (iteration × 256) / max_iterations
|
||||||
|
farbe = farbpalette[farbindex]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beliebte Paletten:**
|
||||||
|
- Regenbogen (HSV mit variierendem Hue)
|
||||||
|
- Blau-Violett-Verlauf
|
||||||
|
- Feuer (Schwarz → Rot → Orange → Gelb → Weiß)
|
||||||
|
|
||||||
|
### 5.3 Smooth Coloring (kontinuierliche Färbung)
|
||||||
|
|
||||||
|
Problem: Mit der einfachen Methode sieht man harte "Bänder" zwischen den Iterationsstufen.
|
||||||
|
|
||||||
|
Lösung: Nutze den finalen Betragswert für eine feinere Färbung:
|
||||||
|
|
||||||
|
```
|
||||||
|
wenn divergiert:
|
||||||
|
// Wie stark über der Grenze sind wir?
|
||||||
|
betrag_quadrat = z_real² + z_imag²
|
||||||
|
|
||||||
|
// Smooth iteration count
|
||||||
|
smooth_iteration = iteration + 1 - log(log(betrag_quadrat) / log(2)) / log(2)
|
||||||
|
|
||||||
|
// Nutze smooth_iteration für Färbung
|
||||||
|
```
|
||||||
|
|
||||||
|
Das ergibt sanfte Farbverläufe ohne Bänder. (Dies ist mathematisch etwas fortgeschritten, aber das Ergebnis sieht viel besser aus.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Zoom und Navigation
|
||||||
|
|
||||||
|
### 6.1 Zoom implementieren
|
||||||
|
|
||||||
|
Beim Zoomen verkleinerst du den sichtbaren Bereich der komplexen Ebene.
|
||||||
|
|
||||||
|
**Zoom um einen Faktor:**
|
||||||
|
```
|
||||||
|
zoom_factor = 2.0 // 2x näher heran
|
||||||
|
|
||||||
|
center_real = (min_real + max_real) / 2
|
||||||
|
center_imag = (min_imag + max_imag) / 2
|
||||||
|
|
||||||
|
span_real = (max_real - min_real) / zoom_factor
|
||||||
|
span_imag = (max_imag - min_imag) / zoom_factor
|
||||||
|
|
||||||
|
min_real = center_real - span_real / 2
|
||||||
|
max_real = center_real + span_real / 2
|
||||||
|
min_imag = center_imag - span_imag / 2
|
||||||
|
max_imag = center_imag + span_imag / 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Zoom auf Mausposition
|
||||||
|
|
||||||
|
Wenn der User auf Pixel (mouse_x, mouse_y) klickt:
|
||||||
|
|
||||||
|
```
|
||||||
|
// Berechne die komplexe Zahl an der Mausposition
|
||||||
|
mouse_real = min_real + (mouse_x / fenster_breite) × (max_real - min_real)
|
||||||
|
mouse_imag = min_imag + (mouse_y / fenster_hoehe) × (max_imag - min_imag)
|
||||||
|
|
||||||
|
// Zentriere auf diese Position und zoome
|
||||||
|
span_real = (max_real - min_real) / zoom_factor
|
||||||
|
span_imag = (max_imag - min_imag) / zoom_factor
|
||||||
|
|
||||||
|
min_real = mouse_real - span_real / 2
|
||||||
|
max_real = mouse_real + span_real / 2
|
||||||
|
min_imag = mouse_imag - span_imag / 2
|
||||||
|
max_imag = mouse_imag + span_imag / 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Interaktive Navigation
|
||||||
|
|
||||||
|
**Pan (Verschieben):**
|
||||||
|
```
|
||||||
|
verschiebung_real = delta_x / fenster_breite × (max_real - min_real)
|
||||||
|
verschiebung_imag = delta_y / fenster_hoehe × (max_imag - min_imag)
|
||||||
|
|
||||||
|
min_real -= verschiebung_real
|
||||||
|
max_real -= verschiebung_real
|
||||||
|
min_imag -= verschiebung_imag
|
||||||
|
max_imag -= verschiebung_imag
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Performance-Optimierungen
|
||||||
|
|
||||||
|
### 7.1 Frühe Abbrüche
|
||||||
|
|
||||||
|
**Periodizitätsprüfung:** Speichere z in regelmäßigen Abständen. Wenn z sich wiederholt, bist du in einem Zyklus → in der Menge.
|
||||||
|
|
||||||
|
**Hauptkardioiden-Test:** Der große "Körper" der Mandelbrot-Menge (eine Kardioidform) kann analytisch getestet werden:
|
||||||
|
|
||||||
|
```
|
||||||
|
// Teste ob c in der Hauptkardioide liegt
|
||||||
|
q = (c_real - 0.25)² + c_imag²
|
||||||
|
if (q × (q + (c_real - 0.25)) < 0.25 × c_imag²) {
|
||||||
|
// In der Hauptkardioide → direkt als "in der Menge" werten
|
||||||
|
return max_iterations
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teste ob c im Periode-2-Bulb liegt
|
||||||
|
if ((c_real + 1)² + c_imag² < 0.0625) {
|
||||||
|
// Im Bulb → in der Menge
|
||||||
|
return max_iterations
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 Symmetrie nutzen
|
||||||
|
|
||||||
|
Die Mandelbrot-Menge ist symmetrisch zur reellen Achse:
|
||||||
|
- Wenn (a + bi) in der Menge ist, dann auch (a - bi)
|
||||||
|
|
||||||
|
Du kannst nur die obere Hälfte berechnen und spiegeln.
|
||||||
|
|
||||||
|
### 7.3 Multi-Threading
|
||||||
|
|
||||||
|
Jeder Pixel kann unabhängig berechnet werden → perfekt für Parallelisierung!
|
||||||
|
|
||||||
|
Teile das Bild in Streifen oder Kacheln auf und berechne diese in verschiedenen Threads.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Interessante Koordinaten zum Erkunden
|
||||||
|
|
||||||
|
### Klassische Ansicht (Gesamtüberblick)
|
||||||
|
```
|
||||||
|
Real: [-2.5, 1.0]
|
||||||
|
Imag: [-1.25, 1.25]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Seepferdchen-Tal
|
||||||
|
```
|
||||||
|
Real: [-0.75, -0.735]
|
||||||
|
Imag: [0.1, 0.115]
|
||||||
|
max_iterations: 500+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Spiralen
|
||||||
|
```
|
||||||
|
Real: [-0.7, -0.65]
|
||||||
|
Imag: [0.4, 0.45]
|
||||||
|
max_iterations: 1000+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mini-Mandelbrot
|
||||||
|
```
|
||||||
|
Real: [-0.16, -0.14]
|
||||||
|
Imag: [1.025, 1.045]
|
||||||
|
max_iterations: 2000+
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tipp:** Je tiefer du zoomst, desto höher sollte max_iterations sein, um Details zu sehen!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Häufige Fehler und deren Lösung
|
||||||
|
|
||||||
|
### 9.1 "Ich sehe nur Schwarz oder nur Weiß"
|
||||||
|
|
||||||
|
**Ursachen:**
|
||||||
|
- max_iterations zu niedrig oder zu hoch
|
||||||
|
- Koordinatenbereich außerhalb der interessanten Region
|
||||||
|
- Mapping-Formel falsch
|
||||||
|
|
||||||
|
**Lösung:** Beginne mit der klassischen Ansicht und max_iterations = 100.
|
||||||
|
|
||||||
|
### 9.2 "Das Bild ist verzerrt"
|
||||||
|
|
||||||
|
**Ursache:** Seitenverhältnis nicht korrekt berücksichtigt.
|
||||||
|
|
||||||
|
**Lösung:** Siehe Abschnitt 4.4 zur Aspect Ratio.
|
||||||
|
|
||||||
|
### 9.3 "Die Iteration zählt falsch"
|
||||||
|
|
||||||
|
**Ursache:** Du verwendest das neue z_real bei der Berechnung von z_imag_neu.
|
||||||
|
|
||||||
|
**Lösung:** Speichere beide neue Werte in temporären Variablen.
|
||||||
|
|
||||||
|
### 9.4 "Beim Zoomen wird das Bild unscharf"
|
||||||
|
|
||||||
|
**Ursache:** Bei tiefen Zoom-Stufen reicht die Präzision von `double` nicht mehr aus.
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
- Nutze höhere Präzision (z.B. `long double`)
|
||||||
|
- Für extreme Zooms: arbitrary-precision Bibliotheken (GMP, MPFR)
|
||||||
|
|
||||||
|
### 9.5 "Die Berechnung ist sehr langsam"
|
||||||
|
|
||||||
|
**Lösungen:**
|
||||||
|
- Reduziere max_iterations für Vorschau-Rendering
|
||||||
|
- Implementiere Multi-Threading
|
||||||
|
- Nutze Periodizitätsprüfung und analytische Tests
|
||||||
|
- Rendere in niedrigerer Auflösung und skaliere hoch
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Zusammenfassung: Die komplette Visualisierungspipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
FÜR JEDEN PIXEL (x, y) IM FENSTER:
|
||||||
|
|
||||||
|
1. MAPPING: Konvertiere Pixel zu komplexer Zahl
|
||||||
|
c_real = min_real + (x / breite) × (max_real - min_real)
|
||||||
|
c_imag = min_imag + (y / höhe) × (max_imag - min_imag)
|
||||||
|
|
||||||
|
2. ITERATION: Teste ob c in der Menge liegt
|
||||||
|
z_real = 0
|
||||||
|
z_imag = 0
|
||||||
|
iteration = 0
|
||||||
|
|
||||||
|
SOLANGE iteration < max_iterations:
|
||||||
|
// Berechne z² + c
|
||||||
|
temp_real = z_real² - z_imag² + c_real
|
||||||
|
temp_imag = 2 × z_real × z_imag + c_imag
|
||||||
|
z_real = temp_real
|
||||||
|
z_imag = temp_imag
|
||||||
|
|
||||||
|
// Divergenz?
|
||||||
|
WENN z_real² + z_imag² > 4:
|
||||||
|
BREAK
|
||||||
|
|
||||||
|
iteration++
|
||||||
|
|
||||||
|
3. FÄRBUNG: Basierend auf Iterationszahl
|
||||||
|
WENN iteration == max_iterations:
|
||||||
|
farbe = SCHWARZ (in der Menge)
|
||||||
|
SONST:
|
||||||
|
farbe = berechne_farbe(iteration)
|
||||||
|
|
||||||
|
4. ZEICHNE: Setze Pixel (x, y) auf farbe
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Viel Erfolg!
|
||||||
|
|
||||||
|
Die Mandelbrot-Menge ist ein faszinierendes mathematisches Objekt. Beim Hineinzoomen entdeckst du immer neue Strukturen – Spiralen, Seepferdchen-Täler, Mini-Kopien der gesamten Menge.
|
||||||
|
|
||||||
|
Ein paar abschließende Tipps:
|
||||||
|
- Beginne mit niedrigen max_iterations (50-100) für schnelle Tests
|
||||||
|
- Implementiere Zoom und Pan für interaktive Exploration
|
||||||
|
- Experimentiere mit verschiedenen Farbpaletten
|
||||||
|
- Speichere interessante Koordinaten, die du findest
|
||||||
|
|
||||||
|
Die Selbstähnlichkeit der Mandelbrot-Menge ist unendlich – du kannst beliebig tief zoomen und findest immer neue Details (bis zur Grenze der Gleitkomma-Präzision).
|
||||||
Reference in New Issue
Block a user