# 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).
