Files
DOSSIER-STANDALONE/docs/design/resources-graphics.md
T
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

13 KiB
Raw Blame History

Design — Ressourcen & Grafik

Teil der Standalone-Architektur — siehe ../../ARCHITECTURE.md. Bauteile: elements.md. Output/Pläne: 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).

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

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):

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_templatesresources/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.
// 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.

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.

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.

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.

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