Files
DOSSIER-WEBSITE/content/docs/architektur.md
T

143 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 `<script type="module">` durch CORS.
- **TextEntity-RTF** — Rhinos eingebauter Parser unterstützt nur `\b \i \ul \strike \fN \tab {}` plus Newline-via-`\par`. **Kein `\fs`** (eine TextEntity hat global eine Schriftgröße).
- **Sticky-Reads** immer mit `is not None`-Check.
## Launcher-Anbindung
Der **Dossier-Launcher** (`launcher/`) ist eine separate Tauri-2-App. IPC zu Rhino läuft **dateibasiert**, nicht über Socket:
- Settings: `~/Library/Application Support/ch.gabrielevarano.Dossier/dossier_settings.json`
- Live-Push: `pendingApplyLayout`-Key, `oberleiste.tick_idle()` pollt und cleart
- System-Tray mit Quick-Open der letzten 5 Projekte
Rhino läuft ohne Launcher, Launcher läuft ohne Rhino.