004 - Shapes
Das Shape System bietet eine modulare, erweiterbare Architektur für geometrische Formen mit Physics-Unterstützung.
Das Problem
Geometrische Formen sind das Grundvokabular generativer Kunst: Kreise, Rechtecke, Polygone, Sterne. Aber:
- Jede Form hat eigene Parameter – Kreis hat Radius, Rechteck hat Breite/Höhe, Stern hat Zacken
- Manche Formen können deformiert werden – Soft Shapes mit Jelly-Effekt
- Physik passt nicht zu allen – Ein Kreis kann abprallen, aber eine Linie?
Die Lösung
Das Shape System nutzt ein Registry-Pattern mit Decorator-basierter Auto-Registration. Jedes Shape registriert sich selbst mit seinen Metadaten – Name, Kategorie, Physics-Fähigkeiten. Die ShapeRegistry ist dann die zentrale Factory: "Gib mir ein Shape namens 'Circle'" → fertig.
Warum Decorator? Weil die Information direkt am Code steht, wo sie hingehört. Kein zentrales Config-File, das aus dem Sync gerät. Neue Shape-Klasse schreiben, @registerShape drauf, fertig.
Warum Physics-Capabilities? Nicht jedes Shape kann alles. Ein harter Kreis kann als Rigid Body abprallen. Ein Soft Circle kann sich verformen. Eine Linie hat keine sinnvolle Physik. Das System weiß das und verhindert unsinnige Kombinationen.
Architektur
┌──────────────────────────────────────────────────────────────────────┐
│ Registration │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ @registerShape Decorator │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Registry │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ ShapeRegistry │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Base │
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ ShapeBase │───▶│ IShape Interface │ │
│ └───────────────────────┘ └───────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
▲
┌─────────────────────────┼─────────────────────────┐
│ │ │
┌─────────┴──────────┐ ┌──────────┴─────────┐ ┌──────────┴─────────┐
│ Basic Shapes │ │ Soft Shapes │ │ Custom │
├────────────────────┤ ├────────────────────┤ ├────────────────────┤
│ Circle, Rect │ │ SoftCircle │ │ Character │
│ Ellipse, Polygon │ │ SoftRect │ │ CustomSVG │
│ Star, Line │ │ Blob │ │ │
│ Rhombus, Trapez │ │ │ │ │
│ Triangle │ │ │ │ │
└────────────────────┘ └────────────────────┘ └────────────────────┘Komponenten
| Klasse | Pfad | Funktion |
|---|---|---|
ShapeRegistry |
shapes/ShapeRegistry.ts |
Factory & Registration |
ShapeBase |
shapes/ShapeBase.ts |
Basis-Klasse |
@registerShape |
shapes/ShapeRegistry.ts |
Auto-Registration Decorator |
ShapeRegistry
Zentrale Factory für alle Shapes.
API:
| Methode | Beschreibung |
|---|---|
create(name, props) |
Shape erstellen |
has(name) |
Shape existiert? |
getNames() |
Alle Shape-Namen |
getMetadata(name) |
Shape-Metadaten |
getByCategory(cat) |
Nach Kategorie filtern |
getByPhysicsCapability(cap) |
Nach Physics filtern |
Kategorien:
| Kategorie | Shapes |
|---|---|
basic |
Circle, Rect, Ellipse, Line, Polygon, Star, Rhombus, Trapez, Triangle |
soft |
SoftCircle, SoftRect, Blob |
custom |
Character, CustomSVG |
Basic Shapes
Geometrische Grundformen mit Rigid-Body Physics.
Circle
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
radius |
number |
50 | Radius |
Rect
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
width |
number |
100 | Breite |
height |
number |
100 | Höhe |
cornerRadius |
number |
0 | Eckenradius |
Ellipse
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
radiusX |
number |
50 | Horizontaler Radius |
radiusY |
number |
30 | Vertikaler Radius |
Polygon
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
sides |
number |
6 | Anzahl Seiten |
radius |
number |
50 | Außenradius |
Star
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
points |
number |
5 | Anzahl Zacken |
outerRadius |
number |
50 | Außenradius |
innerRadius |
number |
25 | Innenradius |
Line
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
x1, y1 |
number |
0 | Startpunkt |
x2, y2 |
number |
100 | Endpunkt |
Rhombus, Trapez, Triangle
Spezialisierte Polygone mit entsprechenden Eigenschaften.
Soft Shapes
Deformierbare Formen mit interner Verlet-Simulation.
Zwei Ansätze für Soft Bodies:
| Ansatz | Beschreibung | Anwendung |
|---|---|---|
| SoftCircle, SoftRect, Blob | Spezialisierte Soft-Shape Klassen | Wenn du ein dediziertes weiches Shape brauchst |
| ShapeStateManager | Macht JEDES Shape soft | Flexibel - toggle zwischen starr und weich |
SoftCircle
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
radius |
number |
50 | Basis-Radius |
vertices |
number |
24 | Anzahl Vertices |
stiffness |
number |
0.5 | Steifigkeit (0-1) |
damping |
number |
0.9 | Dämpfung |
SoftRect
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
width |
number |
100 | Basis-Breite |
height |
number |
100 | Basis-Höhe |
segments |
number |
4 | Segmente pro Seite |
stiffness |
number |
0.5 | Steifigkeit |
Blob
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
radius |
number |
50 | Basis-Radius |
irregularity |
number |
0.3 | Unregelmäßigkeit |
vertices |
number |
12 | Anzahl Vertices |
stiffness |
number |
0.3 | Steifigkeit |
ShapeStateManager - Universal Soft Bodies
Das Motto: "Meine Welt, meine Regeln!" - JEDES Shape kann soft werden.
Mit dem ShapeStateManager kannst du normale Shapes (Rect, Circle, Star, Polygon...) in Soft Bodies verwandeln.
Grundlegende Verwendung
import { ShapeStateManager } from '@carstennichte/cc-toolbox';
// 1. StateManager erstellen
const stateManager = new ShapeStateManager();
// 2. Shape-Geometrie initialisieren
stateManager.initializeFromGeometry({
type: 'rect',
center: new Vector(200, 200),
width: 100,
height: 80,
});
// 3. Preset anwenden
stateManager.applyPreset('jelly');
// 4. In der Animations-Schleife
function animate(deltaTime: number) {
stateManager.update(deltaTime);
const vertices = stateManager.getOutlineVertices();
renderer.drawPolygon(vertices, brush);
}Verfügbare Presets
| Preset | Effekt | Beschreibung |
|---|---|---|
default |
Starr | Keine Soft Body Physik |
soft |
Sanft weich | Ausgewogene Werte |
cloth |
Stoff | Pin top, folgt Gravitation |
balloon |
Aufgeblasen | Mit Druck-Simulation |
jelly |
Wackelpudding | Sehr elastisch, springt zurück |
slime |
Schleim | Sehr weich, träge |
rubber |
Gummi | Schnell federnd |
droplet |
Wassertropfen | Oberflächenspannung |
organic |
Organisch | Natürlich wirkend |
stiff |
Fast starr | Minimale Verformung |
Interaktion
// Kräfte anwenden
stateManager.applyForce(new Vector(0, 0.5)); // Gravitation
stateManager.applyExplosion(mousePos, 5, 200); // Explosion
stateManager.repelFrom(mousePos, 0.5, 100); // Abstoßung
// Vertex ziehen (Drag & Drop)
stateManager.dragVertex(targetVertex, mousePos);
// Zurücksetzen
stateManager.resetPositions();Pin-Modi
Fixiere bestimmte Punkte:
| Modus | Beschreibung |
|---|---|
none |
Alles frei beweglich |
top |
Obere Kante fixiert |
bottom |
Untere Kante fixiert |
left / right |
Seite fixiert |
corners |
Alle Ecken fixiert |
center |
Mittelpunkt fixiert |
Kollisionserkennung
Soft Bodies können miteinander kollidieren:
// Mehrere Shapes erstellen
const shapes: ShapeStateManager[] = [];
for (let i = 0; i < 5; i++) {
const sm = new ShapeStateManager();
sm.initializeFromGeometry({ type: 'circle', center: ..., radius: 50 });
sm.applyPreset('jelly');
shapes.push(sm);
}
// Kollision aktivieren - jeder kennt alle anderen
const allDecorators = shapes.map(s => s.getDecorator());
for (const shape of shapes) {
shape.setCollisionBodies(allDecorators);
}
// Update - Kollisionen werden automatisch geprüft
function animate(dt: number) {
for (const shape of shapes) {
shape.update(dt);
}
}Kollisions-Algorithmus:
- Broad-Phase (AABB) - Schneller Bounding-Box Check
- Narrow-Phase (SAT) - Präzise Polygon-Überlappung
- Response - Shapes werden separiert + elastischer Bounce
Custom Shapes
Character
Rendert Einzelzeichen in verschiedenen Fonts.
| Property | Typ | Default | Beschreibung |
|---|---|---|---|
char |
string |
'A' | Das Zeichen |
fontSize |
number |
48 | Schriftgröße |
fontFamily |
string |
'Arial' | Schriftart |
CustomSVG
Rendert SVG-Pfade.
| Property | Typ | Beschreibung |
|---|---|---|
svgPath |
string |
SVG Path-String |
viewBox |
{x,y,w,h} |
Viewport |
Physics Capabilities
Jedes Shape deklariert seine Physics-Fähigkeiten.
graph LR
subgraph "Capability Flags"
RB[supportsRigidBody]
SB[hasSoftBody]
CP[supportsCenterParticle]
end
subgraph "Presets"
NO[NO_PHYSICS]
RIGID[RIGID_BODY_PHYSICS]
SOFT[SOFT_BODY_PHYSICS]
endPresets:
| Preset | RigidBody | SoftBody | CenterParticle |
|---|---|---|---|
NO_PHYSICS |
❌ | ❌ | ❌ |
RIGID_BODY_PHYSICS |
✅ | ❌ | ✅ |
SOFT_BODY_PHYSICS |
❌ | ✅ | ✅ |
Shape-Zuordnung:
| Shape | Physics |
|---|---|
| Circle, Rect, Polygon, Star, etc. | RIGID_BODY_PHYSICS |
| SoftCircle, SoftRect, Blob | SOFT_BODY_PHYSICS |
| Line, Character, CustomSVG | NO_PHYSICS |
ShapePhysicsAdapter
Verbindet Shapes mit PhysicsWorld.
Funktionen:
| Methode | Beschreibung |
|---|---|
attachToWorld(world) |
Shape an PhysicsWorld binden |
setPosition(vec) |
Position setzen |
applyForce(vec) |
Kraft anwenden |
applyImpulse(vec) |
Impuls anwenden |
setLocked(bool) |
Fixieren/Freigeben |
Verhalten:
- Rigid Body Shapes: Ein Particle repräsentiert das Shape
- Soft Body Shapes: Optional ein Center-Particle für globale Kräfte
- No Physics Shapes: Adapter nicht anwendbar
Eigene Shapes erstellen
import { ShapeBase, registerShape, RIGID_BODY_PHYSICS } from '@carstennichte/cc-toolbox';
@registerShape({
name: 'hexagon',
category: 'basic',
description: 'Regular hexagon',
physics: RIGID_BODY_PHYSICS
})
export class Hexagon extends ShapeBase {
getPath(): Path2D {
// ... Pfad generieren
}
getBounds(): { x, y, width, height } {
// ... Bounds berechnen
}
}