Add parametric walls design documentation

This commit is contained in:
2026-07-01 20:32:07 +02:00
parent ce3b594403
commit b9731a4979
2 changed files with 680 additions and 0 deletions
+8
View File
@@ -48,6 +48,14 @@ Undo/Redo** eliminiert die Cache-Stale-Bugs strukturell. Ziel-Repo-Struktur mit
**kleinen Bauteil-Modulen** statt des 7244-LOC-`elemente.py`-Monolithen; Rendering **kleinen Bauteil-Modulen** statt des 7244-LOC-`elemente.py`-Monolithen; Rendering
über einen `THREE.Group`-Baum, der den Ebenen-Baum spiegelt. über einen `THREE.Group`-Baum, der den Ebenen-Baum spiegelt.
### [design/parametric-walls.md](design/parametric-walls.md)
Regelbasierte Wandgenerierung als Alternative zum Direktzeichnen. Vier Regel-Varianten
(`GridRule`, `ModuleRule`, `ConditionalRule`, `PolylineRule`) erzeugen `Wall[]`-Arrays
über einen reinen Auflöser (`resolveParametricWall`). Deckungsbereich: Schweizer 3-m-
Wohnraster, bedingte Außen-/Innenwand-Dicken, 6-m-Jochbauweise. Phase A: Typsystem +
Resolver isoliert, kein UI. Phase B: Command + Formular-Editor. Phase C: Grid-Ressource
und IFC-Export.
### [design/elements.md](design/elements.md) ### [design/elements.md](design/elements.md)
Legt **Daten, Generierung (3D + Plan) und Grip-Editing pro Bauteil** fest. Wichtigste Legt **Daten, Generierung (3D + Plan) und Grip-Editing pro Bauteil** fest. Wichtigste
Empfehlung: die **Prioritäts-T-/X-Verschneidung mehrschichtiger Wände** (Backbone- Empfehlung: die **Prioritäts-T-/X-Verschneidung mehrschichtiger Wände** (Backbone-
+672
View File
@@ -0,0 +1,672 @@
# Parametrische Wände
Status: Implementiert (Phase A — Typ-System und Resolver in `src/model/`, kein UI).
Dieses Dokument spezifiziert die **Parametrischen Wände**: regelbasierte Definitionen,
die beim Auflösen eine Liste von `Wall`-Elementen erzeugen, anstatt sie einzeln vom
Nutzer zeichnen zu lassen.
Bezugsdokumente: [elements.md](elements.md) (Wand-/Türmodell),
[drawing-tools.md](drawing-tools.md) (Werkzeugsystem, Direktzeichnen),
[state-architecture.md](state-architecture.md) (Projekt-Store),
[resources-graphics.md](resources-graphics.md) (WallType/Component-Auflösung).
Implementierungsdateien:
- `src/model/types.ts``ParametricWall`, `ParametricRule` und alle Regel-Varianten.
- `src/model/parametricWalls.ts``resolveParametricWall()`, `applyRule()` und
Hilfsfunktionen.
---
## 0. Überblick
Eine **parametrische Wand** (`ParametricWall`) ist kein festes `Wall`-Element, sondern
ein **Regelwerk**, das beim Auflösen (`resolveParametricWall`) eine Menge von `Wall[]`-
Elementen generiert. Die erzeugten Wände sind gewöhnliche `Wall`-Objekte; sie
unterscheiden sich lediglich in ihrer Herkunft. Das semantische Modell (`Project`)
bleibt die einzige Wahrheit — parametrische Wände sind eine Ressource in der
Ressourcen-Bibliothek, nicht eine separate Laufzeit-Geometrie-Schicht.
```
Project.parametricWalls: ParametricWall[]
│ resolveParametricWall(pw, floorId, context, defaultWallType)
Wall[] ──→ normales Rendering über generatePlan / Viewport3D
```
Erzeugte Wände können entweder **temporär** (zur Laufzeit, als Ergänzung zu
`project.walls` im Rendering-Pfad) oder **eingebacken** (als `Wall[]` fest in
`Project.walls` gespeichert) behandelt werden. Phase A legt nur den Auflöser fest;
die Auswahl liegt bei der aufrufenden Komponente.
---
## 1. Motivation
### 1.1 Schnellere Modellierung von Regelgrundrissen
Schweizer Wohnbauten folgen häufig einem 3-m-Achsraster (SIA-Norm, Modul-/
Skelettbauweise). Zwanzig Wände eines Rasters von Hand zu zeichnen ist fehleranfällig
und verhindert spätere parametrische Änderungen (z. B. Geschossanzahl, Rasterweite,
Wandtyp).
Eine `GridRule` erzeugt dieses Muster aus wenigen Parametern (Achsabstand, Richtung,
Bereich) und lässt sich mit einer einzigen Zahl auf „4-m-Büroraster" umstellen.
### 1.2 Kongruenz mit FreeCAD BIM / IFC
FreeCAD BIM kennt **ParametricObjects**, die ihre Geometrie aus Regeln ableiten (z. B.
`ArchWall` mit `Length`, `Width`, `Height`). Obwohl das Datenformat hier kein IFC ist,
schafft ein ähnliches Abstraktionsniveau eine spätere Brücke: Beim IFC-Export können
parametrische Wände als `IfcWallStandardCase` mit konstanten Attributen exportiert
werden — kein Informationsverlust gegenüber manuell gezeichneten Wänden.
### 1.3 Bedingte Wandtypen ohne manuelle Klassifizierung
Außenwände sind dicker als Innenwände; Trennwände zwischen Einheiten erfordern
Schallschutz. Eine `ConditionalThicknessRule` (`condition: "exterior" → thickType`)
weist den richtigen Wandtyp automatisch aus der geometrischen Lage zu — ohne dass der
Nutzer jeden Wandabschnitt einzeln klassifizieren muss.
---
## 2. Architektur
### 2.1 Typen (`src/model/types.ts`)
```ts
/**
* Eine parametrische Wand-Regel — generiert automatisch Wall[]-Einträge für
* ein gegebenes Geschoss. Lebt in Project.parametricWalls[].
*/
export interface ParametricWall {
id: string;
name: string;
description?: string;
/**
* Geordnete Liste der anzuwendenden Regeln. Spätere Regeln können die
* Ausgabe früherer verfeinern (z. B. Dickenzuweisung nach Raster).
*/
rules: ParametricRule[];
/**
* Rückfall-Wandtyp, falls eine Regel keinen eigenen `wallTypeId` nennt.
*/
defaultWallTypeId: string;
}
/** Diskriminierte Union aller Regel-Varianten. */
export type ParametricRule =
| GridRule
| ModuleRule
| ConditionalThicknessRule
| ReferenceLineRule
| SequenceRule;
```
### 2.2 Einbettung ins Projekt
```ts
export interface Project {
// … bestehende Felder …
/**
* Parametrische Wanddefinitionen (Ressourcen-Bibliothek). Optional, damit
* bestehende Projekte/Tests ohne `parametricWalls` gültig bleiben.
*/
parametricWalls?: ParametricWall[];
}
```
### 2.3 Resolver-Kontext (`src/model/parametricWalls.ts`)
```ts
export interface ParametricContext {
/** Das Ziel-Geschoss. */
floor: DrawingLevel;
/**
* Optionale Rasterachsen (Phase C: verlinkter Grid-Ressource). Fehlen sie,
* berechnet die Engine die Achsen aus GridRule.spacing.
*/
gridAxes?: { x: number[]; y: number[] };
/**
* Optionales Clipping-Polygon (Meter). Fehlt es, reicht das Raster über
* einen Standardbereich (0 … spacing × 10).
*/
boundaryGeometry?: { boundary: Vec2[] };
/**
* Bereits im Projekt vorhandene Wände des Geschosses. Werden von
* refinierenden Regeln (ConditionalThicknessRule, ReferenceLineRule) genutzt.
*/
existingWalls?: Wall[];
}
```
---
## 3. Regel-Varianten
### 3.1 GridRule — Achsraster
Erzeugt parallele Wände auf einem gleichmäßigen Raster. Typischer Einsatz: Schweizer
Wohnbau-Achsraster (3 m), Büro-Konstruktionsraster (6 m), strukturelle Raster mit
fester Stützweite.
```ts
export interface GridRule {
type: "grid";
/**
* Optionaler Verweis auf eine Grid-Ressource (Phase C). Für Phase A wird
* stattdessen `spacing` genutzt.
*/
gridId?: string;
/** Rasterabstand in Metern (Default: 3.0). */
spacing?: number;
/**
* Achsrichtungen: „x" = nur Wände entlang der Y-Achse,
* „y" = nur Wände entlang der X-Achse, „both" = Vollraster.
*/
directions: "x" | "y" | "both";
/** Optionaler Verweis auf Clipping-Polygon. */
boundaryId?: string;
/** Optionale Wandtyp-Übersteuerung; sonst defaultWallTypeId. */
wallTypeId?: string;
/** Lage der Wandachse über die Dicke (Vectorworks-Stil). */
referenceLine?: WallReferenceLine;
/** Optionale Höhenübersteuerung in Metern; sonst Geschosshöhe. */
height?: number;
}
```
**Geometrieausgabe (top-down Grundriss):**
```
directions: "x", spacing: 3.0, Bereich 0…12 m:
y
12 ──────────────────────────
9 ──────────────────────────
6 ──────────────────────────
3 ──────────────────────────
0 ──────────────────────────
└──────────────────────────► x
0 12
```
**Wann verwenden:**
- Tragende Wände auf fester Stützweite (Wohnbau 3 m, Büro 6 m).
- Vollraster (`"both"`) für strukturelle Rastersysteme.
- In Kombination mit `ConditionalThicknessRule` zur automatischen Außen/Innen-Klassifizierung.
**Beispiel: Schweizer 3-m-Wohnraster**
```ts
const pw: ParametricWall = {
id: "pw-eg-raster",
name: "EG Längswände 3m-Raster",
defaultWallTypeId: "wt-innen-15",
rules: [
{
type: "grid",
spacing: 3.0,
directions: "x", // Wände in X-Richtung (y = 0, 3, 6, 9, 12)
wallTypeId: "wt-innen-15",
},
],
};
// Auflösung:
const walls = resolveParametricWall(pw, "floor-eg", {
floor: egFloor,
boundaryGeometry: { boundary: rectBoundary(0, 0, 12, 12) },
}, defaultWallType);
// → 5 Wände bei y = 0, 3, 6, 9, 12, je 12 m lang
```
---
### 3.2 ModuleRule — Bay-/Jochbauweise
Unterteilt eine Referenzspanne in gleiche Module und erzeugt Querwände an jedem
Teilungspunkt. Typisch für Bürogebäude (6-m-Joch) oder Reihenhäuser mit modularer
Erschließung.
```ts
export interface ModuleRule {
type: "module";
/** Modulmaß in Metern (z. B. 6.0, 3.6). */
moduleSize: number;
/** Ausrichtung der Trennwände: „x" = Querwände senkrecht zu X, „y" = zu Y. */
direction: "x" | "y";
/**
* Optionaler Verweis auf eine Referenzwand, die die Spannweite definiert.
* Fehlt er, wird die Geschoss-Ausdehnung (Bounding-Box) genutzt.
*/
referenceWallId?: string;
/** Optionale Wandtyp-Übersteuerung; sonst defaultWallTypeId. */
wallTypeId?: string;
referenceLine?: WallReferenceLine;
height?: number;
}
```
**Wann verwenden:**
- Wenn sich Querwände aus einer Referenzspanne (Fassade, Achswand) ergeben.
- Vorzug vor `GridRule`, wenn nur in eine Richtung unterteilt wird und eine
Referenzwand die Spanne definiert.
**Beispiel: 6-m-Bay-Bürogebäude**
```ts
const pw: ParametricWall = {
id: "pw-buero-joch",
name: "Büro 6m-Joch",
defaultWallTypeId: "wt-beton-20",
rules: [
{
type: "module",
moduleSize: 6.0,
direction: "x", // Querwände senkrecht zur X-Achse
// referenceWallId: "W-sudfassade" → Spanne aus der Südwand ableiten
},
],
};
// resolveParametricWall → Querwände bei x = 6, 12, 18, 24, 30 (bei 36-m-Fassade)
```
---
### 3.3 ConditionalThicknessRule — Bedingte Wandtypen
Weist bereits erzeugten Wänden (aus vorherigen Regeln in der Sequenz) einen anderen
Wandtyp zu — abhängig von einer Bedingung. Gibt modifizierte **Kopien** zurück; die
Eingabe-Wände werden nicht mutiert.
```ts
export interface ConditionalThicknessRule {
type: "conditional-thickness";
/**
* Bedingung für den Treffer:
* • „exterior" — Wand liegt am Außenrand (Bounding-Box des Kontexts).
* • „interior" — Wand liegt im Inneren.
* • „bearing" — tragende Wand (Heuristikum: Wand läuft ±10° zu X/Y-Achse).
* • beliebiger String — benutzerdefiniertes Tag (Phase C: Wall.tags[]).
*/
condition: "exterior" | "interior" | "bearing" | string;
/** Ziel-Wandtyp, der bei Treffer gesetzt wird. */
wallTypeId: string;
}
```
**Wann verwenden:**
- Immer in Kombination mit `GridRule` oder `ModuleRule` (als zweite Regel in
`ParametricWall.rules`): Raster erzeugt, Dicke verfeinert.
- Wenn Außen- und Innenwände denselben geometrischen Ursprung haben, aber
verschiedene Aufbauten benötigen.
**Beispiel: Außen dick, Innen dünn**
```ts
const pw: ParametricWall = {
id: "pw-eg-komplett",
name: "EG Vollraster mit Außenwand-Differenzierung",
defaultWallTypeId: "wt-innen-15",
rules: [
{
type: "grid", spacing: 3.0, directions: "both",
wallTypeId: "wt-innen-15",
},
{
type: "conditional-thickness",
condition: "exterior",
wallTypeId: "wt-aussen-36", // Außenwände erhalten dicken Aufbau
},
],
};
```
---
### 3.4 ReferenceLineRule — Wandachsen-Lage
Setzt `referenceLine` bei passenden Wänden einheitlich (Vectorworks-Stil: Achse
links/rechts/mittig). Gibt modifizierte Kopien zurück.
```ts
export interface ReferenceLineRule {
type: "reference-line";
/** Neue Lage der Wandachse, die einheitlich gesetzt wird. */
referenceLine: WallReferenceLine; // "left" | "center" | "right"
/**
* Filterziel:
* • „all" — alle Wände im aktuellen Satz.
* • „exterior" — nur Außenwände.
* • beliebiger String — benutzerdefiniertes Tag (Phase C).
*/
target: "all" | "exterior" | string;
}
```
**Wann verwenden:**
- Außenwände auf `"left"` setzen (Achse liegt auf der Fassadenfläche).
- Als abschließende Regel in einer `SequenceRule` nach Raster und Dickenzuweisung.
---
### 3.5 SequenceRule — Zusammenfassung von Unterregeln
Fasst mehrere Regeln als atomare Einheit zusammen. Jede Unterregel erhält die Ausgabe
der vorherigen als `existingWalls` — so können spätere Regeln frühere verfeinern.
```ts
export interface SequenceRule {
type: "sequence";
rules: ParametricRule[];
/**
* Wenn true: Abbruch nach der ersten Unterregel, die mindestens eine Wand
* generiert/verändert hat (Short-Circuit-Fallback).
*/
stopOnMatch?: boolean;
}
```
**Wann verwenden:**
- Um eine zusammengehörige Kombination (Raster → Dicke → Referenzlinie) als
Untermodul wiederzuverwenden — z. B. in unterschiedlichen Geschossen mit leicht
abweichenden Parametern.
---
## 4. Resolver-API (`src/model/parametricWalls.ts`)
```ts
/**
* Löst ein ParametricWall-Regelwerk zu einem Wall[]-Array für ein gegebenes
* Geschoss auf.
*
* Ablauf:
* 1. Regelwerk sequenziell ausführen; jede Regel erhält die Ausgabe der
* vorherigen als existingWalls (ermöglicht Verfeinerung).
* 2. Duplikate (gleicher Start-/Endpunkt innerhalb tolerance) entfernen.
* 3. Bereinigte Wall[]-Liste zurückgeben.
*
* Die Ausgabe ist sofort bereit zur Einfügung in project.walls. Es werden
* keine Seiteneffekte erzeugt — kein Store, kein Dispatch, kein React.
*
* @param parametricWall Das Regelwerk.
* @param floorId ID des Ziel-Geschosses.
* @param context Kontext (Geschoss-Objekt, Grid-Achsen, Grenzen, …).
* @param defaultWallType Fallback-Wandtyp, wenn eine Regel keinen nennt.
* @param tolerance Näherungstoleranz für Duplikat-Erkennung (Meter, Default 0.01).
* @returns Wall[]-Array, bereit zur Einfügung.
*/
export function resolveParametricWall(
parametricWall: ParametricWall,
floorId: string,
context: ParametricContext,
defaultWallType: WallType,
tolerance?: number,
): Wall[];
/**
* Dispatcher: delegiert eine Regel an die passende Implementierung.
* Exportiert für Unit-Tests und erweiterbare Regeltypen.
*/
export function applyRule(rule: ParametricRule, ctx: RuleCtx): Wall[];
/**
* Entfernt doppelte Wände: zwei Wände gelten als Duplikat, wenn Start- und
* Endpunkt jeweils innerhalb tolerance übereinstimmen (vorwärts und rückwärts).
*/
export function deduplicateWalls(walls: Wall[], tolerance?: number): Wall[];
```
### 4.1 Höhenauflösung
Die Wandhöhe (`Wall.height`) ergibt sich nach folgender Priorität:
1. `rule.height`, falls an der einzelnen Regel gesetzt.
2. `context.floor.floorHeight` des Zielgeschosses.
3. Fallback: 2.6 m (globaler Default, CONVENTIONS.md).
### 4.2 ID-Schema
```
"pw-<floorId>-gx-<counter>" // GridRule, X-Achse
"pw-<floorId>-gy-<counter>" // GridRule, Y-Achse
"pw-<floorId>-mx-<counter>" // ModuleRule, X-Teilung
"pw-<floorId>-ct-<counter>" // ConditionalThicknessRule
"pw-<floorId>-rl-<counter>" // ReferenceLineRule
```
IDs sind sessionlokal (Zähler startet bei 0 je Modullade). Eingebrannte Wände
erhalten beim Commit neue stabile IDs über `uniqueId("W")` — konsistent mit dem
Wand-Werkzeug (vgl. [drawing-tools.md §8](drawing-tools.md#8-id-vergabe--immutabilität)).
### 4.3 Duplikat-Erkennung
`deduplicateWalls` vergleicht Start-/Endpunkte beider Wände (vorwärts: A→B == A→B,
und rückwärts: A→B == B→A) innerhalb einer Toleranz von 1 cm (0.01 m). Die **erste**
Instanz wird behalten; spätere Duplikate werden verworfen. Dies ist wichtig bei
Vollrastern (`"both"`), bei denen X- und Y-Wände exakt auf einem Rasterpunkt
zusammentreffen könnten.
### 4.4 Verhalten bei ungültigen Eingaben
| Situation | Verhalten |
|-----------|-----------|
| `spacing <= 0` oder `moduleSize <= 0` | `[]` |
| `referenceWallId` nicht in `existingWalls` | Fallback auf Bounding-Box, kein Fehler |
| Unbekannter `condition`-String | `matchesCondition` gibt `false` zurück (kein Treffer) |
| Unbekannter `SequenceRule`-Untertyp | TypeScript exhaustiveness-Guard, `[]` |
| Segment mit `|end - start| < 1e-6` m | Kann durch deduplicateWalls entfernt werden |
---
## 5. Integration ins Projekt
### 5.1 Ressourcen-Speicherung
`ParametricWall`-Einträge leben unter `Project.parametricWalls` (optionales Array).
Sie sind Teil des `.cad.json`-Dokuments und werden mit dem Rest des Projekts gespeichert.
```ts
// sampleProject.ts — Beispieleintrag
export const sampleProject: Project = {
// …
parametricWalls: [
{
id: "pw-eg-raster",
name: "EG Längswände 3m-Raster",
defaultWallTypeId: "wt-innen-15",
rules: [
{ type: "grid", spacing: 3.0, directions: "x" },
{ type: "conditional-thickness", condition: "exterior",
wallTypeId: "wt-aussen-36" },
],
},
],
};
```
### 5.2 Rendering ohne UI (Phase A)
In Phase A werden parametrische Wände **nicht** automatisch gerendert. Der Auflöser
ist eine reine Funktion; Aufrufer müssen ihn explizit einbinden. Mögliche Verwendung
in `generatePlan` oder `Viewport3D`:
```ts
// generatePlan.ts (Ergänzung, Phase A)
const defaultWallType = project.wallTypes[0];
const extraWalls = (project.parametricWalls ?? []).flatMap((pw) =>
resolveParametricWall(pw, activeLevelId, {
floor: activeFloor,
boundaryGeometry: projectBoundary,
}, defaultWallType)
);
const allWalls = [...project.walls, ...extraWalls];
// … allWalls statt project.walls in der Rendering-Pipeline verwenden
```
### 5.3 Keine UI in Phase A
Kein Command, kein Panel, kein Formular. `ParametricWall`-Einträge werden in Phase A
ausschließlich **programmatisch** (Unit-Tests, `sampleProject`, direkte JSON-Bearbeitung
des Projekts) erstellt.
---
## 6. Ausblick: Folge-Phasen
### Phase B — UI und Command-Schnittstelle
- Neues Command (z. B. `PWWALL`) oder Ressourcen-Manager-Tab „Parametrische Wände"
mit Formular-Editor je Regeltyp.
- „Einbrennen" (Flatten): `ParametricWall` → feste `Wall[]` in `Project.walls`
einfügen und den `ParametricWall`-Eintrag entfernen (unidirektional, Undo über Store).
- Auswahl parametrischer Wände im Plan (als Gruppe); Grip-Editing der Raster-Parameter
und Spannweiten.
### Phase C — Grid-Ressource und Schnittpunkt-Clipping
- `GridResource`: ein projektweites, benanntes Koordinatenraster (LV95-Offset,
Rasterweite, Drehung), auf das mehrere `GridRule`-Instanzen via `gridId` verweisen.
- Präzises Clipping: erzeugte Wände werden am Gebäudeumriss getrimmt — exakte
`lineIntersect`-Berechnung statt Bounding-Box-Approximation.
- Benutzerdefinierte Tags (`Wall.tags[]`) für komplexe `ConditionalThicknessRule`-
Bedingungen jenseits von „exterior/interior/bearing".
- IFC-Export: `ParametricWall`-Gruppen → `IfcWallStandardCase` mit parametrischen
Attributen und `IfcRelDefinesByType`.
---
## 7. Vollständige Anwendungsbeispiele
### 7.1 Schweizer Wohnbau: 3-m-Raster EG + 1.OG
Zwei-Geschoss-Wohnhaus, typisches CH-Wohnbauraster. Die Längswände beider Geschosse
entstehen aus zwei `ParametricWall`-Einträgen mit identischen Regeln, unterschieden
nur durch `floorId` beim Auflösen:
```
Top-down (Grundriss):
y=12 ──────────────────────────── (W5)
y=9 ──────────────────────────── (W4)
y=6 ──────────────────────────── (W3)
y=3 ──────────────────────────── (W2)
y=0 ──────────────────────────── (W1)
x=0 x=12
```
```ts
const rasterRegel: ParametricWall = {
id: "pw-laengswand-raster",
name: "Längswände 3m-Raster",
defaultWallTypeId: "wt-innen-15",
rules: [
{ type: "grid", spacing: 3.0, directions: "x" },
// Außenwände (y=0 und y=12) erhalten den dicken Aufbau:
{ type: "conditional-thickness", condition: "exterior",
wallTypeId: "wt-aussen-36" },
// Außenwände: Achse liegt auf der Fassadenfläche:
{ type: "reference-line", referenceLine: "left", target: "exterior" },
],
};
// EG auflösen:
const wallsEG = resolveParametricWall(rasterRegel, "floor-eg",
{ floor: egFloor, boundaryGeometry: { boundary: rect(0,0,12,12) } },
project.wallTypes[0]);
// 1.OG auflösen (gleiche Regel, anderes Geschoss):
const wallsOG = resolveParametricWall(rasterRegel, "floor-og1",
{ floor: ogFloor, boundaryGeometry: { boundary: rect(0,0,12,12) } },
project.wallTypes[0]);
// Änderung spacing: 3.5 → beide Geschosse sofort konsistent.
```
### 7.2 Vollraster mit Außen/Innen-Differenzierung
Gebäudeumriss als Rechteck; die Randwände erhalten automatisch den dicken
Außenwand-Typ, alle anderen den dünnen Innenwand-Typ:
```ts
const vollraster: ParametricWall = {
id: "pw-eg-vollraster",
name: "EG Vollraster mit Differenzierung",
defaultWallTypeId: "wt-innen-15",
rules: [
{ type: "grid", spacing: 3.0, directions: "both" },
{ type: "conditional-thickness", condition: "exterior",
wallTypeId: "wt-aussen-36" },
{ type: "conditional-thickness", condition: "interior",
wallTypeId: "wt-innen-15" },
{ type: "reference-line", referenceLine: "left", target: "exterior" },
],
};
```
```
─────┬─────┬─────┬─────
│ │ │ │ │
─────┼─────┼─────┼─────
│ │ │ │ │
─────┴─────┴─────┴─────
Rand-Segmente: wt-aussen-36 (dicker Aufbau)
Innen-Segmente: wt-innen-15 (dünner Aufbau)
```
### 7.3 Modulbauweise: 6-m-Joch, Bürogebäude
Längliches Bürogebäude, 36 m × 12 m, 6-m-Joch. Querwände entstehen automatisch;
Entwurfsänderung (z. B. auf 7.2-m-Joch) erfordert eine einzige Zahl:
```ts
const joch: ParametricWall = {
id: "pw-buero-joch",
name: "Büro 6m-Joch",
defaultWallTypeId: "wt-beton-20",
rules: [
{
type: "module",
moduleSize: 6.0,
direction: "x", // Querwände senkrecht zur X-Achse
// referenceWallId: "W-sudfassade" → Spanne aus Referenzwand
},
],
};
// resolveParametricWall → Querwände bei x = 6, 12, 18, 24, 30
// (bei Bounding-Box minX=0, maxX=36, Enden selbst ausgespart)
// Änderung auf 7.2-m-Joch: moduleSize: 7.2
// → 4 Trennwände bei x ≈ 7.2, 14.4, 21.6, 28.8 — automatisch neu berechnet.
```
---
## 8. Architektur-Garantien
- **Modell bleibt einzige Wahrheit.** `ParametricWall`-Definitionen sind Daten in
`Project.parametricWalls`; `resolveParametricWall` ist eine **reine Funktion** ohne
Side-Effects. Keine globale Laufzeit-Geometrie-Schicht.
- **Erzeugte Wände sind gewöhnliche `Wall`-Objekte.** Alle nachgelagerten Systeme
(`generatePlan`, `Viewport3D`, `computeJoins`) arbeiten unverändert; sie müssen
nicht zwischen „parametrisch erzeugten" und „direkt gezeichneten" Wänden
unterscheiden.
- **Fehlertoleranz statt Absturz.** Unbekannte Regeltypen liefern `[]`; der TypeScript-
exhaustiveness-Guard fängt fehlende `case`-Zweige zur Compilezeit. Unbekannte
Bedingungsstrings in `ConditionalThicknessRule` geben `false` (kein Treffer) statt
zu werfen.
- **Keine vorzeitige Generalisierung.** Phase A liefert fünf Regel-Varianten und
einen Auflöser. UI, Command-Schnittstelle und Grid-Ressource folgen in Phase B/C.
- **Immutabilität.** Verfeinerungsregeln (`ConditionalThicknessRule`,
`ReferenceLineRule`) geben modifizierte **Kopien** zurück; `existingWalls` werden
nie mutiert — konsistent mit der `setProject`-Konvention (CONVENTIONS.md).