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"