ca859c4aa4
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.
294 lines
13 KiB
Markdown
294 lines
13 KiB
Markdown
# 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).
|