143 lines
6.3 KiB
Markdown
143 lines
6.3 KiB
Markdown
---
|
||
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.
|