673 lines
23 KiB
Markdown
673 lines
23 KiB
Markdown
# 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).
|