From b9731a49799fe85bfac51efacec26d12859778da Mon Sep 17 00:00:00 2001 From: Karim Date: Wed, 1 Jul 2026 20:32:07 +0200 Subject: [PATCH] Add parametric walls design documentation --- docs/README.md | 8 + docs/design/parametric-walls.md | 672 ++++++++++++++++++++++++++++++++ 2 files changed, 680 insertions(+) create mode 100644 docs/design/parametric-walls.md diff --git a/docs/README.md b/docs/README.md index 042250a..d2d1b7d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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 ü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) Legt **Daten, Generierung (3D + Plan) und Grip-Editing pro Bauteil** fest. Wichtigste Empfehlung: die **Prioritäts-T-/X-Verschneidung mehrschichtiger Wände** (Backbone- diff --git a/docs/design/parametric-walls.md b/docs/design/parametric-walls.md new file mode 100644 index 0000000..c0b0509 --- /dev/null +++ b/docs/design/parametric-walls.md @@ -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--gx-" // GridRule, X-Achse +"pw--gy-" // GridRule, Y-Achse +"pw--mx-" // ModuleRule, X-Teilung +"pw--ct-" // ConditionalThicknessRule +"pw--rl-" // 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).