Initial commit: DOSSIER Hugo website

This commit is contained in:
2026-05-26 11:23:18 +02:00
commit 53c0532f60
417 changed files with 32891 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
---
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.