Files
karim ca859c4aa4 Browser-BIM (cad): semantisches Modell, abgeleitete 2D/3D-Sichten, Zeichenwerkzeuge
Standalone-Browser-Port von DOSSIER. Enthaelt das semantische Modell mit
Plan-/3D-Ableitung, Zeichen- und Editierwerkzeuge, Rhino-artiges Befehlssystem,
dockbares Panel-System, Resource-Manager, DXF/.lin/.pat-Import, i18n (de/en)
sowie Projektdokumentation und Probe-Harness.
2026-06-30 20:52:27 +02:00

294 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design — Ressourcen & Grafik
> Teil der Standalone-Architektur — siehe [../../ARCHITECTURE.md](../../ARCHITECTURE.md).
> Bauteile: [elements.md](elements.md). Output/Pläne: [plans-output.md](plans-output.md).
Die **Stil-Schicht**: verwaltete Ressourcen-Bibliotheken (Vectorworks-Stil),
regelbasierte Overrides, Symbol-/Text-Bibliotheken und Detailgrad-Steuerung. Sie
wird **beim Rendern angewandt, nie in die Geometrie eingebacken** (ROADMAP §2b).
Dieses Dokument übersetzt DOSSIERs `styles.py`/`gestaltung.py`, `mass_style.py`,
`overrides.py`, `library.py`, `text_create.py`. Bezeichner englisch, Prosa
deutsch.
---
## 1. Resource Manager — verwaltete Bibliotheken
Drei Bibliotheken im `Project.resources`-Block; **alles verweist per id**, zentral
änderbar (ROADMAP §2d). Verweis-Kette: 2D-Objekte/Ebenen → LineStyle/Hatch;
Hatch → LineStyle; Component → Hatch (+3D-Material).
```ts
interface Resources {
lineStyles: LineStyle[];
hatches: Hatch[];
components: Component[];
}
interface LineStyle { // Line Manager
id; name;
weight: number; // Strichstärke in mm (≙ Rhino PlotWeight)
color: string; // hex
dash: number[]; // Strichmuster in mm ([] = durchgezogen)
}
interface Hatch { // Hatch Manager
id; name;
pattern: PatternId; // "solid" | "diagonal" | "insulation" | "concrete" | ...
scale: number; // Grundmaßstab des Musters
angle: number; // Grad
lineStyleId: string; // Linien der Schraffur → Line Manager
}
interface Component { // Component Manager (= DOSSIER-Material, erweitert)
id; name;
hatchId: string; // Schnitt-Schraffur → Hatch Manager
color3d: string; // 3D-Diffusfarbe
texture3d?: TextureRef; // optionale PBR-Textur (Phase 3)
pbr?: { roughness; metalness; opacity; ior }; // Material-Bibliothek (ROADMAP §11)
joinPriority: number; // Verschneidungs-Rang (DOSSIER _MATERIAL_PRIO als Daten)
}
```
**Migration vom heutigen Stand:** Der Spike hat `Material { color, planFill, hatch }`
und `Layer { materialId, thickness, priority }`. Ziel: `Material → Component`
(`color→color3d`, `planFill→` Fill aus `hatch.pattern==solid`+Farbe, `hatch`-Enum
`hatchId`), `Layer.priority → Component.joinPriority` (Priorität wandert vom
Layer zum Component, damit man sie nur einmal pflegt — siehe elements.md §1.3).
### 1.1 Manager-UI
`managers/ComponentManager.tsx`, `HatchManager.tsx`, `LineManager.tsx` — je eine
Liste mit CRUD + Vorschau (Three-Sphere für Component-3D, SVG-Swatch für
Hatch/Line). **Seeds** beim ersten Projekt (DOSSIER-Defaults):
- LineStyles: 0.13 / 0.18 / 0.25 / 0.35 / 0.50 mm (aus `DEFAULT_LAYER_SCHEMA`-lw).
- Hatches: `solid`, `diagonal`, `concrete`, `insulation` (Dämmung).
- Components: Stahlbeton (prio 800), Beton (800), Mauerwerk (600), Ziegel (550),
Holzständer (400), Dämmung (200), Putz (100) — exakt DOSSIER `_MATERIAL_PRIO`
(elements.md §1.3). Plus Glas (transparent), Holz-Türblatt (DOSSIER
`_OEFF_PIECE_DEFS`).
### 1.2 Render-Anwendung
- **3D:** Component → `MeshStandardMaterial` (`color3d`/`pbr`), pro `componentId`
gecacht (`viewport/scene.ts`).
- **Plan/Schnitt:** geschnittene Schicht → Polygon mit `fill` (Component-Farbe) +
`<pattern>` aus `hatchId`. Pattern als SVG `<pattern>` mit `patternTransform`
für Massstab (plans-output.md §3.2). Der Hatch nutzt seinen `lineStyleId` für
die Musterlinien.
---
## 2. Mehrschichtige Aufbauten ↔ Ressourcen
`WallType.layers[].componentId` / `SlabType.layers[].componentId` verweisen auf
Components. 3D und Plan lesen dieselben Schichten (elements.md §1). Die
**Prioritäts-Verschneidung** (Risiko #1) liest `Component.joinPriority`:
höhere Priorität läuft am Stoß durch (Backbone), niedrigere stößt seitlich an —
Algorithmus in elements.md §1.3 (Port DOSSIER `_t_junction_layer_overrides`).
---
## 3. Stile & Element-Override (Selektions-Attribute)
DOSSIERs GESTALTUNG-Panel (`styles.py`) setzt Farbe/Lineweight/Linetype/Hatch auf
die *Selektion*. Browser-Äquivalent — zwei Ebenen, in Render-Reihenfolge:
```
ByLayer (Ebenen-Default) → Element-Style (styleId) → Override-Regeln → gerendert
```
```ts
// Effektiver Stil eines Elements (resolve beim Rendern, nie persistiert)
interface EffectiveStyle { color; lineStyleId; hatchId?; }
function resolveStyle(project, el, doc): EffectiveStyle {
// 1) Default aus LayerCategory(categoryCode)
// 2) überschrieben durch el.styleId (Element-Override, optional)
// 3) überschrieben durch passende Override-Regeln (§4)
}
```
- **Wall-/Opening-Stil-Kataloge** (Presets, ROADMAP §11): benannte Sätze von
Default-Werten (Wandtyp + Farbe + lw; Öffnung mit Rahmen/Sims/…). 1-Klick-
Anwendung, globaler Stilwechsel. Speicherung: pro Projekt + cross-Projekt
(LocalStorage), Seed wie DOSSIER `_OEFF_DEFAULT_STYLES` (elements.md §2.1).
- **LoD-bewusste Stil-UI** (DOSSIER ROADMAP §11): das Stil-Panel zeigt nur
passende Controls je Geometrietyp (keine Füll-Optionen bei einer 3D-/Linien-
Auswahl). `panels/StylePanel.tsx` schaltet Felder nach `selection`-Typ.
- **Pipette:** Stil/Typ von einem Element auf ein anderes übernehmen (DOSSIER
`cmd/pipette`).
---
## 4. Regelbasierte Overrides (Engine)
Port `overrides.py` (ArchiCAD Graphical Overrides / Vectorworks
Datenvisualisierung). Im Browser **viel einfacher**, weil Overrides reine
**Render-Transformationen** sind — kein UserString-Backup/Restore nötig (DOSSIER
musste Originalwerte sichern, weil es echte Rhino-Objekte mutierte; wir mutieren
nichts).
```ts
interface OverrideConfig { enabled: boolean; rules: OverrideRule[]; activePresetId?: string; }
interface OverrideRule {
id; name; enabled: boolean;
conditions: Condition[]; conditionsLogic: "and" | "or";
actions: { color?: string; lineWeight?: number; lineStyleId?: string;
hatchId?: string; hatchScale?: number };
}
interface Condition {
type: "category" | "userField" | "name" | "elementType"; // ≙ layer_name/user_string/object_name
operator: "equals"|"notEquals"|"contains"|"startsWith"|"endsWith";
value: string;
key?: string; // nur für userField (z.B. "sia")
}
```
**Auswertung** (Port `_compose_overrides`):
```ts
function composeOverrides(el, project, cfg): Partial<Actions> {
// additive: Actions aller matchenden, aktiven Regeln kombinieren;
// bei Konflikt für dieselbe Property gewinnt die Regel WEITER OBEN (kleinerer Index).
}
```
- **Anwendung:** `resolveStyle` (§3) ruft `composeOverrides` — Override liegt
über Element-Style. Reines Read beim Rendern → kein `apply_all`/`restore_all`,
kein Backup, **keine reversibilität nötig**. Toggle `enabled` rendert neu.
- **Live:** Da abgeleitet, schlägt jede Modell-/Regel-Änderung sofort durch (kein
`install_listeners`/`AddRhinoObject`-Hook wie DOSSIER).
- **Presets & Templates** (cross-Projekt): Preset = Satz Regeln, Template =
einzelne Regel; LocalStorage statt `~/Library/.../override_presets.json`
(`save_preset`/`load_preset`/`list_rule_templates``resources/overridePresets.ts`).
- **SIA-416-Preset** (elements.md §7): vier Regeln `userField sia == HNF|NNF|VF|FF`
→ Farbe + Solid-Hatch, Port `_build_sia_preset_rules`. Aktivieren = Preset
`activePresetId` setzen.
```ts
// resources/overrides.ts
function composeOverrides(el, project, cfg): Partial<OverrideAction>
function setActivePreset(project, presetId): Project // immutabel
const PRESETS_NS = "cad.presets.overrides"; // LocalStorage
```
---
## 5. Symbol-Bibliothek
Port `library.py` + `cmd/symbol`. Wiederverwendbare 2D-Symbole (Möbel, Sanitär,
Bäume, Nordpfeil, Pflanzen) für den Plan.
```ts
interface Symbol {
id; name; category: string; // "furniture" | "sanitary" | "vegetation" | "annotation"
svgPath: string; // Pfad-/Gruppen-Markup im Symbol-Koordinatensystem (m)
defaultScale: number;
}
interface SymbolInstance extends ElementBase { // type:"draw2d", subtype:"symbol"
symbolId: string; at: Vec2; scale: number; angle: number;
}
```
- **Speicherung:** mitgelieferte Symbole als statische Assets (SVG); Nutzer-
Symbole im Projekt + cross-Projekt (LocalStorage). DOSSIER nutzt Block-
Definitionen; bei uns SVG-Definition + Instanz-Transform (`<use>`-artig).
- **Picker:** `panels/SymbolPicker.tsx` (≙ DOSSIER `SymbolPicker.jsx`) — Grid mit
Vorschau, Drag in den Plan; Instanz auf Ebene `60 Plangrafik` (bzw. `22 Möbel`).
- **Render:** `Primitive{ kind:"symbol", ... }` → SVG `<g transform>` mit dem
Symbol-Markup (plans-output.md §9).
---
## 6. Text & Rich-Text-Annotationen
Port `text_create.py` + `text_editor.py`. Formatierte Beschriftungen auf Canvas.
```ts
interface TextElement extends ElementBase { // type:"draw2d", subtype:"text"
at: Vec2; runs: RichRun[];
heightMm: number; // Paper-mm (rendern × Massstab) ODER Modell-m
heightMode: "paper" | "model"; // DOSSIER raum_txt_modus fix|masstab
font: string; align: "left"|"mid"|"right"; angle: number;
mask?: boolean; // Hintergrund-Maskierung (verdeckt Linien darunter)
frame?: boolean; // Rahmen um den Text
}
interface RichRun {
text: string;
bold?; italic?; super?; sub?; // Hoch-/Tiefstellung (Maß-Indizes)
}
interface TextStyle { id; name; font; heightMm; bold; italic; } // Text-Presets
```
- **Render:** `<text>` mit `<tspan>` pro Run; `super/sub` via `baseline-shift` +
kleinerer `font-size`; `mask` via weißem `<rect>` darunter; `frame` via `<rect>`.
- **Editor:** Inline-Rich-Text-Editor (contentEditable oder leichter Custom-Editor)
`RichRun[]`. Fonts aus einer kuratierten Web-Font-Liste + System-Fonts
(DOSSIER `_list_system_fonts` mit Preferred-Liste DM Mono/Krungthep/…); im
Browser via `document.fonts` / `queryLocalFonts()` (wo verfügbar) + gebündelte
Web-Fonts.
- **Massstabsbezug:** `heightMode:"paper"` → Texthöhe in mm, gerendert × Massstab
(plans-output.md §3) — Beschriftung bleibt bei jedem Massstab lesbar.
---
## 7. Detailgrad (Level of Detail)
Querschnittsthema (ROADMAP §2b „Modelldarstellungen"). Drei Stufen, Dokument-/
Snapshot-weiter Override mit Per-Element-Ausnahme — DOSSIER
`darstellung`/`aktive_darstellung`.
```ts
type DetailLevel = "coarse" | "medium" | "fine"; // einfach | standard | detail
// Auflösung (Port _resolve_oeff_darstellung):
function resolveDetail(el, doc): DetailLevel {
const v = el.detailLevel ?? "auto";
return v === "auto" ? doc.detailLevel : v; // doc-Level hat IMMER konkreten Wert
}
```
- **Dokument-Ebene:** `Project.detailLevel` (Default `coarse`/einfach = 1:100,
DOSSIER `_DARSTELLUNG_DEFAULT_GLOBAL`). In der TopBar global umschaltbar; ein
ViewSnapshot kann ihn pro Ansicht überschreiben (plans-output.md §5).
- **Wirkung:** jedes Bauteil-`generatePlan`/`build3d` liest den aufgelösten LoD und
zeichnet entsprechend (elements.md: Tür coarse=Lücke, fine=Glas/Schwenkbogen/
Sims). Kritisch für Mixed-Scale-Pläne (1:50 Detail neben 1:200 Übersicht).
- **Schnitt-Schraffur** koppelt an LoD: grob ggf. nur Umriss, fein voll schraffiert.
---
## 8. Section-Style (3D-Schnittflächen)
Port DOSSIER `_apply_section_style` (`layer_builder.py`). Wo die Schnittebene ein
Bauteil durchschneidet: Schnittfläche bekommt die **Component-Schraffur**, die
Schnittkante einen dicken Rand, optional eine Silhouette.
```ts
interface SectionStyle { // pro Component (oder Ebene) ableitbar
hatchId?: string; hatchScale; hatchAngle;
boundaryShow: boolean; boundaryLineStyleId; boundaryWidthScale;
fillBackground: boolean;
}
```
- **3D-Viewport:** Three.js hat keinen nativen „Schnittflächen-Cap". Cap-Geometrie
selbst erzeugen: Schnittpolygon der Cut-Plane mit den Breps → Fläche mit
Hatch-Material (oder Stencil-Cap-Technik). Phase 4.
- **2D-Schnitt (SVG):** `generateSection` (plans-output.md §4) liefert `cutFaces`
→ mit `SectionStyle.hatchId` füllen. Das ist der Hauptweg; der 3D-Cap ist Bonus.
---
## 9. Was Browser hier einfacher macht (vs. DOSSIER)
| DOSSIER-Aufwand | entfällt im Browser, weil … |
|---|---|
| UserString-Backup/Restore bei Overrides (`_backup_original`/`_restore_original`) | Overrides sind reine Render-Reads — nichts wird mutiert |
| Hatch-Curve-Link über Sticky (`gestaltung_curve_hatch`, Pending-TTL) | Schraffur ist eine Eigenschaft des Polygons, kein separates Objekt |
| Plotweight-Welt↔Bildschirm-Rescaling (`write/read_plotweight`) | Strichstärke ist in mm im SVG-Paper-Space (plans-output.md §3.2) |
| `install_listeners` für Live-Override-Reapply | reaktiver Store re-rendert automatisch |
| SectionStyle-API-Reflection über Rhino-Versionen | wir definieren das Rendering selbst (SVG/Three) |
| Cross-doc Presets als Dateien im User-Home | LocalStorage + Export/Import |
---
## 10. Umsetzungs-Reihenfolge (verweist auf ROADMAP-Phasen)
1. **Phase 1:** Component/Hatch/Line-Manager (§1) + `resolveStyle` (§3) +
LoD-Grundgerüst (§7) + LoD-bewusste Stil-UI.
2. **Phase 2:** Stil-Kataloge (Wände/Öffnungen), Material-Seeds mit `joinPriority`.
3. **Phase 3:** Overrides-Engine + SIA-Preset (§4), Symbol-Bibliothek (§5),
Rich-Text (§6), PBR-Material-Bibliothek; massstabsabhängige Hatch-/Linetype-
Skalierung (plans-output.md §3.2).
4. **Phase 4:** Section-Style 3D-Cap (§8).