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.
13 KiB
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), procomponentIdgecacht (viewport/scene.ts). - Plan/Schnitt: geschnittene Schicht → Polygon mit
fill(Component-Farbe) +<pattern>aushatchId. Pattern als SVG<pattern>mitpatternTransformfür Massstab (plans-output.md §3.2). Der Hatch nutzt seinenlineStyleIdfü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.tsxschaltet Felder nachselection-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) ruftcomposeOverrides— Override liegt über Element-Style. Reines Read beim Rendern → keinapply_all/restore_all, kein Backup, keine reversibilität nötig. Toggleenabledrendert 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 = PresetactivePresetIdsetzen.
// 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(≙ DOSSIERSymbolPicker.jsx) — Grid mit Vorschau, Drag in den Plan; Instanz auf Ebene60 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/subviabaseline-shift+ kleinererfont-size;maskvia weißem<rect>darunter;framevia<rect>. - Editor: Inline-Rich-Text-Editor (contentEditable oder leichter Custom-Editor)
→
RichRun[]. Fonts aus einer kuratierten Web-Font-Liste + System-Fonts (DOSSIER_list_system_fontsmit Preferred-Liste DM Mono/Krungthep/…); im Browser viadocument.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(Defaultcoarse/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/build3dliest 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) liefertcutFaces→ mitSectionStyle.hatchIdfü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)
- Phase 1: Component/Hatch/Line-Manager (§1) +
resolveStyle(§3) + LoD-Grundgerüst (§7) + LoD-bewusste Stil-UI. - Phase 2: Stil-Kataloge (Wände/Öffnungen), Material-Seeds mit
joinPriority. - 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).
- Phase 4: Section-Style 3D-Cap (§8).