diff --git a/package-lock.json b/package-lock.json
index 5e8f88c..9458617 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,9 +17,9 @@
"@angular/material": "~21.1.0",
"@angular/platform-browser": "~21.1.0",
"@angular/router": "~21.1.0",
+ "@babylonjs/core": "^8.50.5",
"@ngx-translate/core": "~17.0.0",
"@ngx-translate/http-loader": "~17.0.0",
- "babylonjs": "^8.50.5",
"inquirer": "^13.2.2",
"rxjs": "~7.8.2",
"swiper": "~12.1.0",
@@ -1150,6 +1150,12 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@babylonjs/core": {
+ "version": "8.50.5",
+ "resolved": "https://registry.npmjs.org/@babylonjs/core/-/core-8.50.5.tgz",
+ "integrity": "sha512-rrHAYJmsZOuvC6wdNhGNgdn9JlMxBhk30YhBWGPSxYnnUD7/n/U9Pb3a8k0lpTHipbCfjJ+J5r6GdXtPEikhEg==",
+ "license": "Apache-2.0"
+ },
"node_modules/@emnapi/core": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
@@ -6449,13 +6455,6 @@
}
}
},
- "node_modules/babylonjs": {
- "version": "8.50.5",
- "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-8.50.5.tgz",
- "integrity": "sha512-r4CvDBY798k0E8FqOrj2pg5If7MJhELACIpQw8r1TT4TXNWNXxlqrNmKtSzxauWFQgankwVbKhe41YA8k+tQMw==",
- "hasInstallScript": true,
- "license": "Apache-2.0"
- },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
diff --git a/package.json b/package.json
index 50fb22b..9bac666 100644
--- a/package.json
+++ b/package.json
@@ -20,9 +20,9 @@
"@angular/material": "~21.1.0",
"@angular/platform-browser": "~21.1.0",
"@angular/router": "~21.1.0",
+ "@babylonjs/core": "^8.50.5",
"@ngx-translate/core": "~17.0.0",
"@ngx-translate/http-loader": "~17.0.0",
- "babylonjs": "^8.50.5",
"inquirer": "^13.2.2",
"rxjs": "~7.8.2",
"swiper": "~12.1.0",
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index 0ad4a7e..c8960c9 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -12,6 +12,7 @@ export const routes: Routes = [
{ path: RouterConstants.IMPRINT.PATH, component: RouterConstants.IMPRINT.COMPONENT},
{ path: RouterConstants.GOL.PATH, component: RouterConstants.GOL.COMPONENT},
{ path: RouterConstants.LABYRINTH.PATH, component: RouterConstants.LABYRINTH.COMPONENT},
- { path: RouterConstants.FRACTAL.PATH, component: RouterConstants.FRACTAL.COMPONENT}
+ { path: RouterConstants.FRACTAL.PATH, component: RouterConstants.FRACTAL.COMPONENT},
+ { path: RouterConstants.FRACTAL3d.PATH, component: RouterConstants.FRACTAL3d.COMPONENT}
];
diff --git a/src/app/constants/RouterConstants.ts b/src/app/constants/RouterConstants.ts
index b60e8fa..6b9b9d9 100644
--- a/src/app/constants/RouterConstants.ts
+++ b/src/app/constants/RouterConstants.ts
@@ -7,6 +7,7 @@ import {SortingComponent} from '../pages/algorithms/sorting/sorting.component';
import {ConwayGolComponent} from '../pages/algorithms/conway-gol/conway-gol.component';
import {LabyrinthComponent} from '../pages/algorithms/pathfinding/labyrinth/labyrinth.component';
import {FractalComponent} from '../pages/algorithms/fractal/fractal.component';
+import {Fractal3dComponent} from '../pages/algorithms/fractal3d/fractal3d.component';
export class RouterConstants {
@@ -58,6 +59,12 @@ export class RouterConstants {
COMPONENT: FractalComponent
};
+ static readonly FRACTAL3d = {
+ PATH: 'algorithms/fractal3d',
+ LINK: '/algorithms/fractal3d',
+ COMPONENT: Fractal3dComponent
+ };
+
static readonly IMPRINT = {
PATH: 'imprint',
LINK: '/imprint',
diff --git a/src/app/constants/UrlConstants.ts b/src/app/constants/UrlConstants.ts
index c44df1a..640b9d5 100644
--- a/src/app/constants/UrlConstants.ts
+++ b/src/app/constants/UrlConstants.ts
@@ -14,4 +14,7 @@
static readonly JULIA_WIKI = 'https://de.wikipedia.org/wiki/Julia-Menge'
static readonly NEWTON_FRACTAL_WIKI = 'https://de.wikipedia.org/wiki/Newtonfraktal'
static readonly BURNING_SHIP_WIKI = 'https://de.wikipedia.org/wiki/Burning_ship_(Fraktal)'
+ static readonly MANDELBULB_WIKI = 'https://de.wikipedia.org/wiki/Mandelknolle'
+ static readonly MANDELBOX_WIKI = 'https://de.wikipedia.org/wiki/Mandelbox'
+ static readonly JULIA3D_WIKI = 'https://de.wikipedia.org/wiki/Mandelknolle'
}
diff --git a/src/app/pages/algorithms/fractal3d/fractal.shader.ts b/src/app/pages/algorithms/fractal3d/fractal.shader.ts
new file mode 100644
index 0000000..30354bc
--- /dev/null
+++ b/src/app/pages/algorithms/fractal3d/fractal.shader.ts
@@ -0,0 +1,220 @@
+export const MANDELBULB_VERTEX = /* glsl */`
+ precision highp float;
+ attribute vec3 position;
+ attribute vec2 uv;
+ varying vec2 vUV;
+
+ void main(void) {
+ gl_Position = vec4(position, 1.0);
+ vUV = uv;
+ }
+`;
+
+export const MANDELBULB_FRAGMENT = /* glsl */`
+ precision highp float;
+
+ uniform float time;
+ uniform vec2 resolution;
+ uniform vec3 cameraPosition;
+ uniform vec3 targetPosition;
+ uniform float power;
+ uniform int fractalType; // 0 = Bulb, 1 = Box, 2 = Julia
+
+ // --- Palettes ---
+ vec3 palette( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ) {
+ return a + b*cos( 6.28318*(c*t+d) );
+ }
+
+ // Global trap for coloring
+ float minTrap = 1000.0;
+
+ // --- Shape 1: Mandelbulb ---
+ float mapMandelbulb(vec3 pos, out float trap) {
+ vec3 z = pos;
+ float dr = 1.0;
+ float r = 0.0;
+ trap = 1000.0;
+
+ for (int i = 0; i < 8; i++) {
+ r = length(z);
+ if (r > 100.0) break;
+ trap = min(trap, r);
+
+ float theta = acos(z.y / r);
+ float phi = atan(z.z, z.x);
+ dr = pow(r, power - 1.0) * power * dr + 1.0;
+ float zr = pow(r, power);
+ theta = theta * power;
+ phi = phi * power;
+ z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
+ z += pos;
+ }
+ return 0.5 * log(r) * r / dr;
+ }
+
+ // --- Shape 2: Mandelbox ---
+ float mapMandelbox(vec3 pos, out float trap) {
+ vec3 z = pos;
+ float dr = 1.0;
+ float scale = 2.8; // Fixed scale for good look
+ trap = 1000.0;
+
+ for (int i = 0; i < 15; i++) {
+ // Box fold
+ z = clamp(z, -1.0, 1.0) * 2.0 - z;
+
+ // Sphere fold
+ float r2 = dot(z, z);
+ trap = min(trap, r2); // Trap based on sphere fold
+
+ if (r2 < 0.25) {
+ z = z * 4.0;
+ dr = dr * 4.0;
+ } else if (r2 < 1.0) {
+ z = z / r2;
+ dr = dr / r2;
+ }
+
+ z = z * scale + pos;
+ dr = dr * abs(scale) + 1.0;
+ }
+ return (length(z) - abs(scale - 1.0)) / dr;
+ }
+
+ // --- Shape 3: Julia Bulb ---
+ float mapJulia(vec3 pos, out float trap) {
+ vec3 z = pos;
+ float dr = 1.0;
+ float r = 0.0;
+ trap = 1000.0;
+
+ // Constant C for Julia set (animating slightly makes it alive)
+ vec3 c = vec3(0.35, 0.45, -0.1) + vec3(sin(time*0.1)*0.2);
+
+ for (int i = 0; i < 8; i++) {
+ r = length(z);
+ if (r > 100.0) break; // Higher escape radius for Julia
+ trap = min(trap, r);
+
+ float theta = acos(z.y / r);
+ float phi = atan(z.z, z.x);
+ dr = pow(r, power - 1.0) * power * dr + 1.0;
+ float zr = pow(r, power);
+ theta = theta * power;
+ phi = phi * power;
+ z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
+ z += c; // Add C instead of pos
+ }
+ return 0.5 * log(r) * r / dr;
+ }
+
+ // --- Main Map Dispatcher ---
+ float map(vec3 pos) {
+ float d = 0.0;
+ float currentTrap = 0.0;
+
+ if (fractalType == 1) {
+ d = mapMandelbox(pos, currentTrap);
+ } else if (fractalType == 2) {
+ d = mapJulia(pos, currentTrap);
+ } else {
+ d = mapMandelbulb(pos, currentTrap);
+ }
+
+ minTrap = currentTrap; // Update global
+ return d;
+ }
+
+ // --- Raymarching ---
+ bool intersectSphere(vec3 ro, vec3 rd, vec3 c, float r, out float t0, out float t1) {
+ vec3 oc = ro - c;
+ float b = dot(oc, rd);
+ float c2 = dot(oc, oc) - r * r;
+ float h = b*b - c2;
+ if (h < 0.0) return false;
+ h = sqrt(h);
+ t0 = -b - h;
+ t1 = -b + h;
+ return true;
+ }
+
+ float raymarch(vec3 ro, vec3 rd) {
+ // Bounding sphere around fractal center (here: origin)
+ vec3 center = vec3(0.0);
+ float radius = 6.0;
+
+ float tEnter, tExit;
+ if (!intersectSphere(ro, rd, center, radius, tEnter, tExit)) {
+ return -1.0;
+ }
+
+ float t = max(tEnter, 0.0);
+ float tMax = tExit;
+
+ for (int i = 0; i < 128; i++) {
+ vec3 pos = ro + t * rd;
+ float d = map(pos);
+
+ // distance-based epsilon is more stable for zoom-out
+ float eps = max(0.001, 0.0005 * t);
+
+ if (d < eps) return t;
+ t += d * 0.8; // safety factor against overshoot
+ if (t > tMax) break;
+ }
+ return -1.0;
+}
+
+ vec3 getNormal(vec3 p) {
+ float d = map(p);
+ vec2 e = vec2(0.001, 0.0);
+ return normalize(vec3(
+ d - map(p - e.xyy),
+ d - map(p - e.yxy),
+ d - map(p - e.yyx)
+ ));
+ }
+
+ void main(void) {
+ vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
+ vec3 ro = cameraPosition;
+ vec3 ta = targetPosition;
+
+ vec3 fwd = normalize(ta - ro);
+ vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), fwd));
+ vec3 up = normalize(cross(fwd, right));
+ vec3 rd = normalize(fwd + uv.x * right + uv.y * up);
+
+ vec3 color = vec3(0.1);
+
+ float t = raymarch(ro, rd);
+
+ if(t > 0.0) {
+ vec3 pos = ro + t * rd;
+ vec3 nor = getNormal(pos);
+
+ // Different colors for different shapes
+ vec3 colParamsA = vec3(0.5, 0.5, 0.5);
+ vec3 colParamsB = vec3(0.5, 0.5, 0.5);
+ vec3 colParamsC = vec3(1.0, 1.0, 1.0);
+ vec3 colParamsD = vec3(0.80, 0.90, 0.30);
+
+ if (fractalType == 1) { // Box: Sci-Fi Blue/Grey
+ colParamsD = vec3(0.0, 0.1, 0.2);
+ }
+ if (fractalType == 2) { // Julia: Alien Green/Purple
+ colParamsD = vec3(0.8, 0.0, 0.2);
+ }
+
+ vec3 materialColor = palette(minTrap, colParamsA, colParamsB, colParamsC, colParamsD);
+
+ float camLight = max(0.0, dot(nor, -rd));
+ float ambient = 0.4;
+ vec3 lighting = vec3(1.0) * (camLight * 0.7 + ambient);
+ color = materialColor * lighting;
+ }
+
+ color = pow(color, vec3(0.8));
+ gl_FragColor = vec4(color, 1.0);
+ }
+`;
diff --git a/src/app/pages/algorithms/fractal3d/fractal3d.component.html b/src/app/pages/algorithms/fractal3d/fractal3d.component.html
index 7d2e388..07ac16f 100644
--- a/src/app/pages/algorithms/fractal3d/fractal3d.component.html
+++ b/src/app/pages/algorithms/fractal3d/fractal3d.component.html
@@ -1 +1,18 @@
-
fractal3d works!
+
+
+ {{ 'FRACTAL3D.TITLE' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/pages/algorithms/fractal3d/fractal3d.component.scss b/src/app/pages/algorithms/fractal3d/fractal3d.component.scss
index e69de29..daadde6 100644
--- a/src/app/pages/algorithms/fractal3d/fractal3d.component.scss
+++ b/src/app/pages/algorithms/fractal3d/fractal3d.component.scss
@@ -0,0 +1,2 @@
+.canvas-container { width: 100%; height: 1000px; }
+canvas { width: 100%; height: 100%; touch-action: none; border-width: 0; border-color: transparent; border-style: hidden; }
diff --git a/src/app/pages/algorithms/fractal3d/fractal3d.component.ts b/src/app/pages/algorithms/fractal3d/fractal3d.component.ts
index 4d84a37..d0312f8 100644
--- a/src/app/pages/algorithms/fractal3d/fractal3d.component.ts
+++ b/src/app/pages/algorithms/fractal3d/fractal3d.component.ts
@@ -1,11 +1,146 @@
-import { Component } from '@angular/core';
+import {AfterViewInit, Component, ElementRef, inject, NgZone, OnDestroy, ViewChild} from '@angular/core';
+import {ArcRotateCamera, Engine, MeshBuilder, Scene, ShaderMaterial, Vector2, Vector3} from '@babylonjs/core';
+import {MANDELBULB_FRAGMENT, MANDELBULB_VERTEX} from './fractal.shader';
+import {Information} from '../information/information';
+import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
+import {TranslatePipe} from '@ngx-translate/core';
+import {AlgorithmInformation} from '../information/information.models';
+import {UrlConstants} from '../../../constants/UrlConstants';
+import {MatButton} from '@angular/material/button';
@Component({
selector: 'app-fractal3d',
- imports: [],
+ imports: [
+ Information,
+ MatCard,
+ MatCardContent,
+ MatCardHeader,
+ MatCardTitle,
+ TranslatePipe,
+ MatButton
+ ],
templateUrl: './fractal3d.component.html',
styleUrl: './fractal3d.component.scss',
})
-export class Fractal3dComponent {
+export class Fractal3dComponent implements AfterViewInit, OnDestroy {
+ readonly ngZone = inject(NgZone);
+ @ViewChild('renderCanvas') canvasRef!: ElementRef;
+
+ algoInformation: AlgorithmInformation = {
+ title: 'FRACTAL3D.EXPLANATION.TITLE',
+ entries: [
+ {
+ name: 'Mandel-Bulb',
+ description: 'FRACTAL3D.EXPLANATION.MANDELBULB_EXPLANATION',
+ link: UrlConstants.MANDELBULB_WIKI
+ },
+ {
+ name: 'Mandelbox',
+ description: 'FRACTAL3D.EXPLANATION.MANDELBOX_EXPLANATION',
+ link: UrlConstants.MANDELBOX_WIKI
+ },
+ {
+ name: 'Julia-Bulb',
+ description: 'FRACTAL3D.EXPLANATION.JULIA_EXPLANATION',
+ link: UrlConstants.JULIA3D_WIKI
+ }
+ ],
+ disclaimer: 'FRACTAL3D.EXPLANATION.DISCLAIMER',
+ disclaimerBottom: '',
+ disclaimerListEntry: ['FRACTAL3D.EXPLANATION.DISCLAIMER_1', 'FRACTAL3D.EXPLANATION.DISCLAIMER_2', 'FRACTAL3D.EXPLANATION.DISCLAIMER_3', 'FRACTAL3D.EXPLANATION.DISCLAIMER_4']
+ };
+
+ private readonly fractalPower = 8;
+ private engine!: Engine;
+ private scene!: Scene;
+ private shaderMaterial!: ShaderMaterial;
+ private time = 0;
+ private triggerCamUpdate = false;
+ private cameraPosition: number = 3.5;
+ public currentFractalType = 0;
+
+
+ ngAfterViewInit(): void {
+ this.ngZone.runOutsideAngular(() => {
+ this.initBabylon();
+ });
+ }
+
+ private initBabylon(): void {
+ const canvas = this.canvasRef.nativeElement;
+ this.engine = new Engine(canvas, true);
+ this.scene = new Scene(this.engine);
+
+ const camera = new ArcRotateCamera("Camera", 0, Math.PI / 2, 4, Vector3.Zero(), this.scene);
+ camera.wheelPrecision = 100;
+ camera.minZ = 0.1;
+ camera.maxZ = 100;
+ camera.lowerRadiusLimit = 1.5;
+ camera.upperRadiusLimit = 20;
+ camera.attachControl(this.canvasRef.nativeElement, true);
+
+ const plane = MeshBuilder.CreatePlane("plane", { size: 10 }, this.scene);
+ plane.parent = camera;
+ plane.position.z = 1;
+ plane.alwaysSelectAsActiveMesh = true;
+
+ this.shaderMaterial = new ShaderMaterial(
+ "mandelbulbShader",
+ this.scene,
+ {
+ vertexSource: MANDELBULB_VERTEX,
+ fragmentSource: MANDELBULB_FRAGMENT
+ },
+ {
+ attributes: ["position", "uv"],
+ uniforms: ["time", "resolution", "cameraPosition", "targetPosition", "power", "fractalType"]
+ }
+ );
+ this.shaderMaterial.disableDepthWrite = true;
+ this.shaderMaterial.backFaceCulling = false;
+
+ plane.material = this.shaderMaterial;
+
+ this.engine.runRenderLoop(() => {
+ this.time += 0.005;
+
+ if (this.triggerCamUpdate)
+ {
+ this.triggerCamUpdate = false;
+ camera.radius = this.cameraPosition;
+ }
+
+ if (this.shaderMaterial) {
+ this.shaderMaterial.setFloat("time", this.time);
+ this.shaderMaterial.setVector2("resolution", new Vector2(canvas.width, canvas.height));
+ this.shaderMaterial.setVector3("cameraPosition", camera.position);
+ this.shaderMaterial.setVector3("targetPosition", camera.target);
+ this.shaderMaterial.setFloat("power", this.fractalPower);
+ this.shaderMaterial.setInt("fractalType", this.currentFractalType);
+ }
+
+ this.scene.render();
+ });
+
+ window.addEventListener('resize', () => this.engine.resize());
+ }
+
+ onFractalTypeChange(type: number): void {
+ this.currentFractalType = type;
+ if (type === 0 ||type === 2)
+ {
+ this.cameraPosition = 4;
+ }
+ else {
+ this.cameraPosition = 15;
+ }
+ this.triggerCamUpdate = true;
+ }
+
+ ngOnDestroy(): void {
+ if (this.engine) {
+ this.engine.dispose();
+ }
+ }
}
diff --git a/src/app/pages/algorithms/fractal3d/fraktal-shader.model.ts b/src/app/pages/algorithms/fractal3d/fraktal-shader.model.ts
deleted file mode 100644
index 944edb1..0000000
--- a/src/app/pages/algorithms/fractal3d/fraktal-shader.model.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-const mandelbulbFragmentShader = `
-precision highp float;
-
-// Uniforms (Werte von der CPU)
-uniform float time;
-uniform vec2 resolution;
-uniform vec3 cameraPosition;
-uniform vec3 targetPosition;
-uniform float power; // Die "Potenz" des Fraktals (z.B. 8.0)
-
-// Hilfsfunktion: Rotation
-mat2 rot(float a) {
- float s = sin(a), c = cos(a);
- return mat2(c, -s, s, c);
-}
-
-// --- Distance Estimator (Mandelbulb) ---
-float map(vec3 pos) {
- vec3 z = pos;
- float dr = 1.0;
- float r = 0.0;
-
- // Die Fraktal-Iteration
- for (int i = 0; i < 8; i++) {
- r = length(z);
- if (r > 2.0) break;
-
- // Konvertierung in Polarkoordinaten
- float theta = acos(z.y / r);
- float phi = atan(z.z, z.x);
-
- dr = pow(r, power - 1.0) * power * dr + 1.0;
-
- // Skalieren und Rotieren (Power)
- float zr = pow(r, power);
- theta = theta * power;
- phi = phi * power;
-
- // Zurück zu Kartesischen Koordinaten
- z = zr * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
-
- // Den ursprünglichen Punkt addieren (wie beim Mandelbrot z = z^2 + c)
- z += pos;
- }
- return 0.5 * log(r) * r / dr;
-}
-
-// --- Raymarching ---
-float raymarch(vec3 ro, vec3 rd) {
- float t = 0.0;
- for(int i = 0; i < 100; i++) {
- vec3 pos = ro + t * rd;
- float d = map(pos);
- if(d < 0.001) return t; // Getroffen!
- if(t > 10.0) break; // Zu weit weg
- t += d;
- }
- return -1.0; // Nichts getroffen
-}
-
-// --- Normalenberechnung für Licht ---
-vec3 getNormal(vec3 p) {
- float d = map(p);
- vec2 e = vec2(0.001, 0.0);
- return normalize(vec3(
- d - map(p - e.xyy),
- d - map(p - e.yxy),
- d - map(p - e.yyx)
- ));
-}
-
-void main(void) {
- // UV Koordinaten zentrieren (-1 bis 1) und Aspekt korrigieren
- vec2 uv = (gl_FragCoord.xy - 0.5 * resolution.xy) / resolution.y;
-
- // Kamera Setup (LookAt)
- vec3 ro = cameraPosition; // Ray Origin
- vec3 ta = targetPosition; // Target LookAt
-
- vec3 fwd = normalize(ta - ro);
- vec3 right = normalize(cross(vec3(0,1,0), fwd));
- vec3 up = normalize(cross(fwd, right));
-
- vec3 rd = normalize(fwd + uv.x * right + uv.y * up); // Ray Direction
-
- // Rendern
- float t = raymarch(ro, rd);
-
- vec3 color = vec3(0.0); // Hintergrund Schwarz
-
- if(t > 0.0) {
- vec3 pos = ro + t * rd;
- vec3 nor = getNormal(pos);
-
- // Einfaches Licht (Phong)
- vec3 lightDir = normalize(vec3(1.0, 1.0, -1.0));
- float diff = max(dot(nor, lightDir), 0.0);
- float amb = 0.2; // Ambient
-
- // Farbe basierend auf Position (gibt diesen Alien-Look)
- vec3 baseColor = vec3(0.5) + 0.5 * cos(vec3(0.0, 0.4, 0.8) + length(pos) * 2.0);
-
- color = baseColor * (diff + amb);
- }
-
- gl_FragColor = vec4(color, 1.0);
-}
-`;
diff --git a/src/app/pages/algorithms/service/algorithms.service.ts b/src/app/pages/algorithms/service/algorithms.service.ts
index dc51478..bb9e889 100644
--- a/src/app/pages/algorithms/service/algorithms.service.ts
+++ b/src/app/pages/algorithms/service/algorithms.service.ts
@@ -38,6 +38,12 @@ export class AlgorithmsService {
title: 'ALGORITHM.FRACTAL.TITLE',
description: 'ALGORITHM.FRACTAL.DESCRIPTION',
routerLink: RouterConstants.FRACTAL.LINK
+ },
+ {
+ id: 'fractal3d',
+ title: 'ALGORITHM.FRACTAL3D.TITLE',
+ description: 'ALGORITHM.FRACTAL3D.DESCRIPTION',
+ routerLink: RouterConstants.FRACTAL3d.LINK
}
];
diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json
index 19264c7..d013a9b 100644
--- a/src/assets/i18n/de.json
+++ b/src/assets/i18n/de.json
@@ -395,6 +395,24 @@
"DISCLAIMER_4": "Rechenintensität: Da für jeden Pixel hunderte Berechnungen durchgeführt werden, sind Fraktale ein klassischer Benchmark für die Performance von Grafikprozessoren (GPUs)."
}
},
+ "FRACTAL3D": {
+ "TITLE": "3D Fraktale",
+ "ALGORITHM": "Algorithmen",
+ "MANDELBULB": "Mandelbulb",
+ "MANDELBOX": "Mandelbox",
+ "JULIA": "Julia",
+ "EXPLANATION": {
+ "TITLE": "3D Fraktale Welten",
+ "MANDELBULB_EXPLANATION": "gilt als der 'Heilige Gral' der 3D-Fraktale. Da komplexe Zahlen nur zweidimensional sind, nutzt dieses Fraktal sphärische Koordinaten und hohe Potenzen (meist v^8 + c), um die Mandelbrot-Menge in den Raum zu projizieren. Vorteil: Es entsteht eine organische, extrem detaillierte Struktur, die an Pflanzen, Korallen oder außerirdische Landschaften erinnert.",
+ "MANDELBOX_EXPLANATION": "basiert nicht auf glatten Kurven, sondern auf geometrischem 'Falten' und Skalieren (Box-Folding & Sphere-Folding). Der Raum wird wie Papier immer wieder gefaltet und gespiegelt. Vorteil: Erzeugt streng geometrische, mechanisch wirkende Strukturen, die wie endlose futuristische Städte, der Borg-Würfel oder komplexe Sci-Fi-Architektur aussehen.",
+ "JULIA_EXPLANATION": "ist das 3D-Pendant zur 2D-Julia-Menge. Während der Mandelbulb eine 'Karte' aller Fraktale ist, fixiert man hier den Parameter 'c' und variiert den Startpunkt zudem variiert es mit der Zeit. Vorteil: Anders als der massive Mandelbulb sind Julia-Bulbs oft hohle, komplexe Tunnelsysteme oder blasenartige Strukturen, die sich perfekt eignen, um mit der Kamera hindurchzufliegen.",
+ "DISCLAIMER": "Diese Visualisierung nutzt eine Technik namens 'Raymarching' (Sphere Tracing). Das bedeutet:",
+ "DISCLAIMER_1": "Keine Polygone: Es gibt keine Dreiecke oder Gitter. Die Form wird rein mathematisch für jeden Pixel in Echtzeit berechnet.",
+ "DISCLAIMER_2": "Distance Estimation: Der Algorithmus 'tastet' sich mit Lichtstrahlen voran, indem er berechnet, wie weit das nächste Objekt entfernt ist, ohne es sofort zu berühren.",
+ "DISCLAIMER_3": "Unendliche Details: Da die Oberfläche mathematisch definiert ist, verpixelt sie nicht beim Zoom – es erscheinen immer neue Strukturen.",
+ "DISCLAIMER_4": "Licht & Schatten: Um die Tiefe sichtbar zu machen, werden Lichtreflexionen und Schatten (Ambient Occlusion) basierend auf der Krümmung der Formel simuliert."
+ }
+ },
"ALGORITHM": {
"TITLE": "Algorithmen",
"PATHFINDING": {
@@ -417,6 +435,10 @@
"TITLE": "Fraktale",
"DESCRIPTION": "Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)."
},
+ "FRACTAL3D": {
+ "TITLE": "Fraktale 3D",
+ "DESCRIPTION": "3D-Visualisierung von komplexe, geometrische Mustern, die sich selbst in immer kleineren Maßstäben ähneln (Selbstähnlichkeit)."
+ },
"NOTE": "HINWEIS",
"GRID_HEIGHT": "Höhe",
"GRID_WIDTH": "Beite"
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index f7acef4..12df05b 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -395,6 +395,24 @@
}
},
+ "FRACTAL3D": {
+ "TITLE": "3D Fractals",
+ "ALGORITHM": "Algorithms",
+ "MANDELBULB": "Mandelbulb",
+ "MANDELBOX": "Mandelbox",
+ "JULIA": "Julia",
+ "EXPLANATION": {
+ "TITLE": "3D Fractal Worlds",
+ "MANDELBULB_EXPLANATION": "is considered the 'Holy Grail' of 3D fractals. Since complex numbers are only two-dimensional, this fractal uses spherical coordinates and high powers (usually v^8 + c) to project the Mandelbrot set into 3D space. Benefit: Creates an organic, extremely detailed structure reminiscent of plants, coral reefs, or alien landscapes.",
+ "MANDELBOX_EXPLANATION": "is based not on smooth curves, but on geometric 'folding' and scaling (Box-Folding & Sphere-Folding). Space is repeatedly folded and mirrored like origami. Benefit: Produces strictly geometric, mechanical-looking structures that resemble endless futuristic cities, the Borg cube, or complex sci-fi architecture.",
+ "JULIA_EXPLANATION": "is the 3D counterpart to the 2D Julia set. While the Mandelbulb is a 'map' of all fractals, here we fix the parameter 'c' and vary the starting point and it changes over time. Benefit: Unlike the solid Mandelbulb, Julia Bulbs are often hollow, forming complex tunnel systems or bubble-like structures perfect for flying through with the camera.",
+ "DISCLAIMER": "This visualization uses a technique called 'Raymarching' (Sphere Tracing). This means:",
+ "DISCLAIMER_1": "No Polygons: There are no triangles or meshes. The shape is calculated mathematically for every pixel in real-time.",
+ "DISCLAIMER_2": "Distance Estimation: The algorithm 'marches' light rays forward by calculating the safe distance to the nearest object without hitting it immediately.",
+ "DISCLAIMER_3": "Infinite Detail: Since the surface is mathematically defined, it never pixelates when zooming in – new structures always emerge.",
+ "DISCLAIMER_4": "Light & Shadow: To visualize depth, light reflections and shadows (Ambient Occlusion) are simulated based on the curvature of the formula."
+ }
+ },
"ALGORITHM": {
"TITLE": "Algorithms",
"PATHFINDING": {
@@ -417,6 +435,10 @@
"TITLE": "Fractals",
"DESCRIPTION": "Visualisation of complex geometric patterns that resemble each other on increasingly smaller scales (self-similarity)."
},
+ "FRACTAL3D": {
+ "TITLE": "Fractals 3D",
+ "DESCRIPTION": "3D Visualisation of complex geometric patterns that resemble each other on increasingly smaller scales (self-similarity)."
+ },
"NOTE": "Note",
"GRID_HEIGHT": "Height",
"GRID_WIDTH": "Width"