--- title: Architektur linkTitle: Architektur weight: 2 --- DOSSIER ist als **Plugin-Verbund** aufgebaut: jedes Feature lebt in einem eigenen Modul, alle teilen sich ein gemeinsames Bridge-Pattern für die React-↔-Python-Kommunikation. ## Module-Map | Modul | LOC | Rolle | |----------------------|------:|----------------------------------------------------------------| | `panel_base.py` | 697 | Fundament: BaseBridge, WebView-IO, Panel-Registration, Icons | | `rhinopanel.py` | 798 | EBENEN — Zeichnungsebenen, Layer-Hierarchie, Presets | | `elemente.py` | 7'244 | ELEMENTE — Wände, Decken, Öffnungen, Treppen, Tragwerk, Räume | | `gestaltung.py` | 1'635 | GESTALTUNG — Selektions-Attribute (Farbe, Lineweight, Hatch) | | `oberleiste.py` | 981 | OBERLEISTE — Top-Bar, Display, Massstab, Snaps, Settings | | `massstab.py` | 1'096 | MASSSTAB — Viewport 1:N, Auto-DPI, PlotWeight | | `overrides.py` | 797 | Engine — regelbasierte Overrides (Bedingung → Aktion) | | `overrides_panel.py` | 226 | UI für Overrides-Engine | | `ausschnitte.py` | 708 | AUSSCHNITTE — Viewport-Snapshots (Kamera + Display + Layer) | | `dimensionen.py` | 613 | DIMENSIONEN — Bemassung (Wand-Dicken, Geschoss-Höhen, …) | | `layouts.py` | 749 | LAYOUTS — Plan-Editor, Titelblock, PDF-Export | | `werkzeuge.py` | 58 | WERKZEUGE — Quick-Tools (Batch) | | `layer_builder.py` | 436 | Helper — Ebenen-Hierarchie aufbauen, Sublayer-Sync | | `startup.py` | 136 | Init — liest `dossier.project.json`, lädt Module selektiv | ## Tragende Patterns ### Bridge-Pattern (Pflicht für jedes Panel) ```python class MyBridge(panel_base.BaseBridge): def __init__(self): panel_base.BaseBridge.__init__(self, "mymodule") def _on_ready(self): self.send("STATE_SYNC", {...}) # WebView fertig geladen def handle(self, data): t = data.get("type") if t == "ACTION": self._do_action() def _bridge_factory(): b = MyBridge() _install_listeners(b) # Rhino-Events registrieren return b panel_base.register_and_open( "mymodule", "MY PANEL", PANEL_GUID_STR, _bridge_factory, icon_spec=("foundation", "#5fa896"), # Material-Icon + Petrol min_size=(400, 300), ) ``` ### React ↔ Python Kommunikation - **React → Python**: `document.title = "RHINOMSG::{json}"` — gepollt im Idle-Handler - **Python → React**: `bridge.send(type, payload)` → `webview.ExecuteScript("window.onRhinoMessage(…)")` - **Chunking**: Messages > 200 KB werden in `panel_base.handle_raw` automatisch gesplittet und reassembliert. Subklassen kümmern sich nicht drum. ### Source ↔ Volume Pattern Jedes Smart-Element hat: 1. eine **Source-Geometrie** (Achse / Outline / Punkt) — vom User editierbar 2. ein generiertes **Volume** (Brep) — automatisch regeneriert bei Source-Änderungen Beispiel Wand: Source = Achs-Linie, Volume = Brep mit Dicke × Höhe. ### Sticky-Storage (Cross-Module-State) Konventionen für `sc.sticky`-Keys: - `"{modul}_bridge"` — Bridge-Instanz - `"{modul}_listeners"` — Bool-Flag: Listener bereits registriert? - `"_dossier_*"` — globale States (z.B. `_dossier_joints_cache`) - `"{modul}_*_cache"` — Modul-Cache ### Listener-Hookup (Idempotent) ```python def _install_listeners(bridge): flag = "mymodule_listeners" sc.sticky["mymodule_bridge"] = bridge if sc.sticky.get(flag): return # Schon registriert Rhino.RhinoApp.Idle += _on_idle Rhino.RhinoDoc.ActiveDocumentChanged += _on_view_change sc.sticky[flag] = True ``` ## Datenhaltung - **Geschosse** in `doc.Strings["dossier_ebenen"]` als JSON - **Smart-Elemente** als Rhino-Objekte mit UserStrings — `dossier_element_id`, `dossier_element_type`, … - **Section-Styles** über `Rhino.DocObjects.SectionStyle()` + `layer.SetCustomSectionStyle()` - **Settings**: `~/Library/Application Support/ch.gabrielevarano.Dossier/dossier_settings.json` Eine `.3dm`-Datei bleibt eine Datei — keine externen Datenbanken. ## Layer-Hierarchie ```text 10_GRUNDRISSE └── EG ├── 20_WAENDE ├── 30_DECKEN ├── 31_DAECHER └── 40_TREPPEN └── 1OG (gleiche Sublayer) 20_SCHNITTE 30_ANSICHTEN 00_RASTER · 01_VERMESSUNG · 40_SITUATION · 90_REFERENZEN · 99_KONSTRUKTION ``` ## Cross-Module-Pfade | Sender → Empfänger | Trigger | Effekt | |-----------------------------|-------------------------------|---------------------------------------------------------| | `rhinopanel` → `elemente` | Apply von Ebenen-Struktur | `elemente_bridge._regenerate_all()` regeneriert Wände/Decken | | `elemente` → `rhinopanel` | Wand/Decken-Delete | `ebenen_bridge_ref._send_state()` | | `oberleiste` → `overrides` | Preset-Auswahl in Topbar | `overrides_bridge._send_state()` | | `massstab` ↔ `ausschnitte` | Viewport-/Zoom-Wechsel | Bi-direktional Skala lesen / setzen | | `gestaltung` ↔ `rhinopanel` | Hatch-Pattern auf Selektion | Pattern + Scale + Rotation-Signatur vergleichen | ## Konventionen - **Python-Identifier ohne Umlaute** — `ue/oe/ae` statt `ü/ö/ä` in Code-Bezeichnern, Layer-Codes, UserString-Keys. UI-Strings dürfen Umlaute. - **`LoadHtml`-inline** statt `file://`-URL — Rhinos WKWebView blockiert sonst `