003 - Rendering System

Das Unified Rendering System ermöglicht renderer-agnostischen Code.

Das Problem

Stell dir vor: Du hast ein wunderschönes Artwork programmiert mit Canvas2D. Jetzt willst du:

  1. Mehr Performance – Tausende Partikel ruckeln, WebGL wäre besser
  2. SVG exportieren – Für deinen Pen-Plotter
  3. Verschiedene Ausgaben – Live auf dem Bildschirm UND als Vektor-Datei

Ohne Abstraktion müsstest du deinen Code dreimal schreiben. Mit dem Unified Rendering System schreibst du ihn einmal.

Die Lösung

Das RenderContext ist eine Facade – eine einheitliche Schnittstelle, hinter der verschiedene Renderer stecken. Dein Code ruft ctx.drawCircle() auf, und je nach aktivem Renderer landet das auf Canvas2D, WebGL oder in einer SVG-Datei.

Warum Facade? Du willst dich nicht um Implementierungsdetails kümmern. Ein Kreis ist ein Kreis, egal ob er mit arc() (Canvas2D), Shader-Triangles (WebGL) oder <circle> (SVG) gezeichnet wird.

Architektur

┌─────────────────────────────────────────────────────────────┐
│                      Artwork Code                           │
│  ┌───────────────────────────────────────────────────────┐  │
│  │              Brush/Agent Code                         │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                         Facade                              │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                   RenderContext                       │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
┌─────────────────────────────────────────────────────────────┐
│                        Renderer                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │ Canvas2D-   │  │  WebGL-     │  │   SVG-      │          │
│  │ Renderer    │  │  Renderer   │  │   Renderer  │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘

Komponenten

Klasse Datei Funktion
IRenderer IRenderer.ts Interface für alle Renderer
RenderContext RenderContext.ts Facade, wählt aktiven Renderer
Canvas2DRenderer Canvas2DRenderer.ts Standard Canvas 2D
WebGLRenderer WebGLRenderer.ts GPU-beschleunigt
SVGRenderer SVGRenderer.ts Vektor-Export

RenderContext

Die Haupt-Facade für das Rendering.

Factory-Funktionen:

Funktion Beschreibung
createCanvas2DContext(canvas) Canvas 2D Renderer
createWebGLContext(canvas) WebGL Renderer
createSVGContext(width, height) SVG Renderer
createAutoContext(canvas) Auto-Detect

Renderer wechseln:

const ctx = createAutoContext(canvas);
ctx.setRenderer('webgl');  // oder 'canvas2d', 'svg'

IRenderer Interface

Alle Renderer implementieren dieses Interface.

Primitive-Methoden:

Methode Beschreibung
drawCircle(x, y, r, style) Kreis
drawRect(x, y, w, h, style) Rechteck
drawLine(x1, y1, x2, y2, style) Linie
drawPolygon(points, style) Polygon
drawPath(commands, style) SVG-artiger Pfad
drawEllipse(x, y, rx, ry, style) Ellipse

State-Methoden:

Methode Beschreibung
save() State auf Stack
restore() State von Stack
translate(x, y) Verschieben
rotate(angle) Rotieren (Radians)
scale(sx, sy) Skalieren
setTransform(matrix) Matrix setzen

Utility-Methoden:

Methode Beschreibung
clear(color?) Canvas löschen
resize(w, h) Größe ändern
getImageData() Pixel lesen

PrimitiveStyle

Styling für alle Primitives.

Property Typ Beschreibung
fill RenderColor Füllfarbe
stroke RenderColor Rahmenfarbe
strokeWidth number Rahmenbreite
lineCap 'butt' | 'round' | 'square' Linienenden
lineJoin 'miter' | 'round' | 'bevel' Linienecken
opacity number Transparenz (0-1)

RenderColor

Farben können in verschiedenen Formaten angegeben werden:

type RenderColor = 
  | string              // '#FF0000', 'red', 'rgb(255,0,0)'
  | { r, g, b, a? }     // Objekt
  | [r, g, b, a?]       // Array

Utility-Funktionen:

Funktion Beschreibung
parseColor(input) Zu {r,g,b,a}
colorToHex(color) Zu #RRGGBB
colorToRgba(color) Zu rgba(...)

Canvas2DRenderer

Standard-Renderer für die meisten Anwendungsfälle.

Vorteile:

  • Breiteste Browser-Unterstützung
  • Einfaches Debugging
  • Gute Performance für 2D

Limitierungen:

  • Kein GPU-Beschleunigung für komplexe Effekte
  • Langsamer bei vielen Partikeln

WebGLRenderer

GPU-beschleunigtes Rendering.

Vorteile:

  • Hohe Performance bei vielen Objekten
  • 60fps auch bei Tausenden Partikeln
  • Shader-Effekte möglich

Limitierungen:

  • Komplexere Implementierung
  • Nicht alle Primitives gleich effizient

SVGRenderer

Für Vektor-Export (z.B. Pen-Plotter).

Vorteile:

  • Verlustfreie Skalierung
  • Perfekt für Plotter-Export
  • Layer-Unterstützung

Besonderheiten:

  • Kein Realtime-Rendering
  • Sammelt Primitive für Export
  • Layer per svgLayerName

Export:

const svgCtx = createSVGContext(1000, 1000);
// ... zeichnen ...
const svgString = svgCtx.toSVGString();

Transform2D

Transformations-Matrix.

interface Transform2D {
  a: number;  // scale x
  b: number;  // skew y
  c: number;  // skew x
  d: number;  // scale y
  e: number;  // translate x
  f: number;  // translate y
}

Helper:

Funktion Beschreibung
identityTransform() Einheitsmatrix
createTransform(opts) Aus translate/rotate/scale

Brush → Style Konvertierung

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

const style = brushToStyle(brush);
// → { fill, stroke, strokeWidth, ... }