013 - Fraktale & L-Systems

Fraktale sind selbstähnliche Strukturen – Muster, die sich auf jeder Vergrößerungsstufe wiederholen. Sie sind ein Kernthema generativer Kunst, weil sie aus einfachen Regeln komplexe, organisch wirkende Formen erzeugen.

Warum Fraktale?

Die Natur ist voller Fraktale: Bäume, Farne, Küstenlinien, Blutgefäße, Schneeflocken. Wenn du "natürlich" aussehende generative Kunst machen willst, kommst du an Fraktalen nicht vorbei.

Das Faszinierende: Wenige Zeilen Code erzeugen unendliche Komplexität. Ein L-System mit 3 Regeln kann einen realistischen Baum zeichnen. Die Mandelbrot-Menge entsteht aus z = z² + c.

Drei Ansätze

Ansatz Prinzip Beispiele
L-Systems Grammatik-basierte Ersetzungsregeln Bäume, Pflanzen, Koch-Kurve
Escape-Time Iterative Berechnung pro Pixel Mandelbrot, Julia
IFS Zufällige Auswahl aus Transformationen Barnsley Fern, Sierpiński

L-Systems (Lindenmayer-Systeme)

Das Konzept

Ein L-System ist eine Grammatik:

  • Axiom: Startstring (z.B. "F")
  • Regeln: Ersetzungen pro Iteration (z.B. F → F+F--F+F)
  • Interpretation: Turtle Graphics führt den String aus

Beispiel Koch-Kurve:

Axiom:  F
Regel:  F → F+F--F+F
Nach 1: F+F--F+F
Nach 2: F+F--F+F+F+F--F+F--F+F--F+F+F+F--F+F

Die Turtle interpretiert:

  • F = vorwärts zeichnen
  • + = rechts drehen (60°)
  • - = links drehen (60°)

TurtleAgent

import { TurtleAgent } from '@carstennichte/cc-toolbox';

// Manuell konfigurieren
const turtle = new TurtleAgent({
  axiom: 'F',
  rules: [{ symbol: 'F', replacement: 'F+F--F+F' }],
  iterations: 4,
  angleIncrement: 60,
  stepSize: 4,
  startPosition: new Vector(100, 400),
  startAngle: 0
});

// L-System ausführen
const paths = turtle.runLSystem();

// Zeichnen
for (const path of paths) {
  ctx.drawPath(path.getPoints());
}

Fertige Presets

// Koch-Kurve
const koch = TurtleAgent.koch(iterations, stepSize);

// Sierpiński-Dreieck
const sierpinski = TurtleAgent.sierpinski(iterations, stepSize);

// Drachenkurve
const dragon = TurtleAgent.dragonCurve(iterations, stepSize);

// Fraktaler Baum
const tree = TurtleAgent.tree(iterations, stepSize);

// Hilbert-Kurve (Space-Filling)
const hilbert = TurtleAgent.hilbert(iterations, stepSize);

// Gosper-Kurve (Flowsnake)
const gosper = TurtleAgent.gosper(iterations, stepSize);

// Lévy C-Kurve
const levy = TurtleAgent.levyCurve(iterations, stepSize);

// Penrose Tiling (P3)
const penrose = TurtleAgent.penrose(iterations, stepSize);

Turtle-Befehle

Befehl Symbol Beschreibung
forward(n) F, G n Schritte vorwärts, zeichnen
backward(n) n Schritte rückwärts
right(°) + Rechts drehen
left(°) - Links drehen
push() [ State auf Stack
pop() ] State vom Stack
penUp() Stift heben
penDownCmd() Stift senken

Stochastische L-Systems

Für organischere Ergebnisse – Regeln mit Wahrscheinlichkeit:

const stochasticTree = new TurtleAgent({
  axiom: 'X',
  rules: [
    { symbol: 'X', replacement: 'F[+X][-X]FX', probability: 0.5 },
    { symbol: 'X', replacement: 'F[+X]FX', probability: 0.3 },
    { symbol: 'X', replacement: 'F[-X]FX', probability: 0.2 },
    { symbol: 'F', replacement: 'FF' }
  ],
  iterations: 5,
  angleIncrement: 25,
  seed: 12345  // Reproduzierbar
});

Escape-Time Fraktale

Das Konzept

Für jeden Pixel:

  1. Starte mit komplexer Zahl c (aus Pixel-Position)
  2. Iteriere z = z² + c
  3. Zähle Iterationen bis |z| > 2 (Escape)
  4. Farbe basiert auf Iterations-Anzahl

FractalRenderer

import { FractalRenderer } from '@carstennichte/cc-toolbox';

const fractal = new FractalRenderer({
  type: 'mandelbrot',  // oder 'julia'
  width: 800,
  height: 600,
  centerX: -0.5,
  centerY: 0,
  zoom: 1,
  maxIterations: 100,
  colorScheme: 'rainbow'  // oder 'grayscale', 'fire', custom
});

// Render to ImageData
const imageData = fractal.render();
ctx.putImageData(imageData, 0, 0);

// Zoom
fractal.zoomTo(x, y, factor);

Julia-Sets

const julia = new FractalRenderer({
  type: 'julia',
  juliaC: { real: -0.7, imag: 0.27015 },  // Der "Seed"
  // ... rest
});

// Animierte Julia-Sets: variiere juliaC über Zeit
julia.setJuliaC(Math.sin(time) * 0.5, Math.cos(time) * 0.5);

Bekannte Julia-Parameter

Name Real Imag Beschreibung
Dendrite 0 1 Baumartig
Siegel Disk -0.391 -0.587 Spiralen
Rabbit -0.123 0.745 "Douady Rabbit"
San Marco -0.75 0 Basilika-Form
Dragons -0.8 0.156 Drachenartig

IFS (Iterated Function Systems)

Das Konzept

Ein IFS ist eine Menge von affinen Transformationen. In jedem Schritt:

  1. Wähle zufällig eine Transformation (gewichtet)
  2. Wende sie auf den aktuellen Punkt an
  3. Zeichne den Punkt
  4. Wiederhole millionenfach

Warum funktioniert das? Der "Chaos Game" Algorithmus konvergiert zum Attraktor des IFS – dem Fraktal.

IFSRenderer

import { IFSRenderer } from '@carstennichte/cc-toolbox';

const fern = IFSRenderer.barnsleyFern({
  iterations: 100000,
  pointSize: 1,
  color: '#2d5a27'
});

// Render
fern.render(ctx);

Fertige IFS-Presets

// Barnsley Farn
const fern = IFSRenderer.barnsleyFern();

// Sierpiński-Dreieck
const sierpinski = IFSRenderer.sierpinskiTriangle();

// Sierpiński-Teppich
const carpet = IFSRenderer.sierpinskiCarpet();

// Ahornblatt
const maple = IFSRenderer.mapleLeaf();

// Kristall
const crystal = IFSRenderer.crystal();

Custom IFS

const custom = new IFSRenderer({
  transforms: [
    {
      // Transformation 1: Skalierung + Rotation + Translation
      a: 0.5, b: 0, c: 0, d: 0.5,  // 2x2 Matrix
      e: 0, f: 0,                   // Translation
      probability: 0.33
    },
    {
      a: 0.5, b: 0, c: 0, d: 0.5,
      e: 0.5, f: 0,
      probability: 0.33
    },
    {
      a: 0.5, b: 0, c: 0, d: 0.5,
      e: 0.25, f: 0.5,
      probability: 0.34
    }
  ],
  iterations: 50000
});

IFS-Transformation erklärt

Jede Transformation ist: [x', y'] = [a b; c d] * [x, y] + [e, f]

Parameter Bedeutung
a, d Skalierung (diagonal)
b, c Scherung/Rotation (off-diagonal)
e, f Translation
probability Auswahlwahrscheinlichkeit

Integration mit anderen Systemen

Mit Animation

// Animierte L-System Iteration
const animation = new Animation({
  from: 1,
  to: 6,
  easing: Easing.stepped(6)
});

function draw(props) {
  const iterations = Math.floor(animation.getValue(clock.progress));
  const paths = TurtleAgent.tree(iterations, 5).runLSystem();
  // ...
}

Mit ColorSet

// Farbpalette für Escape-Time
fractal.setColorFunction((iterations, maxIter) => {
  const t = iterations / maxIter;
  return colorSet.getColorAt(t);
});

SVG Export (Plotter!)

// L-Systems sind perfekt für Plotter
const paths = TurtleAgent.hilbert(6, 2).runLSystem();

svgExporter.beginFrame();
for (const path of paths) {
  svgExporter.drawPath(path.getPoints());
}
svgExporter.save('hilbert.svg');

Performance-Tipps

Fraktal-Typ Tipp
L-System Iterations begrenzen (>6 wird sehr groß)
Mandelbrot WebGL für Echtzeit, Canvas für Export
IFS Web Worker für >100k Iterationen

Referenz

TurtleAgent

Methode Beschreibung
forward(n) n Schritte vorwärts
backward(n) n Schritte rückwärts
left(°) / right(°) Drehen
push() / pop() State Stack
runLSystem(iter?) L-System ausführen
getPaths() Alle Pfade als VectorPath[]
reset() Zurücksetzen

FractalRenderer

Methode Beschreibung
render() Gibt ImageData zurück
zoomTo(x, y, factor) Zoom auf Punkt
pan(dx, dy) Verschieben
setJuliaC(r, i) Julia-Parameter setzen
setColorScheme(name) Farbschema wechseln

IFSRenderer

Methode Beschreibung
render(ctx) Auf Canvas zeichnen
getPoints() Alle berechneten Punkte
addTransform(t) Transformation hinzufügen
setIterations(n) Iterationsanzahl