Add parametric walls design documentation
This commit is contained in:
@@ -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-
|
||||
|
||||
@@ -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).
|
||||
Reference in New Issue
Block a user