_line_intersect_xy lieferte Z=0 was nicht zum Polygon-Z (=OKFF) passte,
PolylineCurve mit gemischten Z konnte fehlschlagen → 2D-Generierung
broken fuer L-Treppen. Fix: _at_z(p, fallback) Helper setzt Z explicit.
Plus try/except um die ganze Polygon-Konstruktion — Fallback auf
2-Rechteck-Variante bei Fehler.
Cmd+Z:
- _update_wall wrapped in BeginUndoRecord/EndUndoRecord — sodass
Property-Patches + Regen-Delete/Add als ein Undo-Schritt rueckgaengig
L-Treppe Aussenlinie:
- _aussen_l_polygon: sauberes 6-Punkt L-Polygon mit korrekt projizierten
Ecken (Outer + Inner via Linien-Schnitt der mid-versetzten Seiten)
- _aussen_l: nutzt Polygon wenn kein Cut, faellt sonst zurueck auf
per-Lauf Rechtecke mit Diagonal-Cut (wie bisher)
L-Treppe Lauflinie:
- ueber BEIDE Laeufe, mid-perp versetzt, Pfeil am Treppen-Ende
- Eck-Mitte als Linien-Schnitt der zwei versetzten Schaft-Linien →
sauberer Übergang auch bei Lage=links/rechts (kein Versatz an der Ecke)
- 'voll'-Pfeil-Style mit wide-Offsets relativ zur Lauflinie
Wendel-Treppe:
- _lauflinie_wendel: 'voll'-Pfeil-Style mit r_inner/r_outer-Spitzen
relativ zu r_mid (Radial-Offsets)
Trittmass-Lock auf alle Treppen-Arten:
- L: Beide Laeufe proportional skalieren (ratio = N*target_A / (L1+L2))
- Wendel: Sweep-Winkel anpassen (new_delta = sign × N*target_A/r_mid)
- Axis-Geometrie wird in-place via Replace ausgetauscht — Source moves
fliessen in regulären Regen-Pfad ein
Properties-Panel:
- Konsistentes 50px/1fr/14px Grid fuer alle Treppen-Rows
- Lage + Unten als Dropdown (lowercase Labels)
- Versatz: Dropdown (Geschoss-OKFF) oder eigenes Z mit Input + x-Button
- Ziel: gleich (Geschoss-Liste oder eigene Hoehe), Geschosse-Filter
excludes das Start-Geschoss
- Start-Dropdown filtert auf okff < Ziel-Z (kein hoeheres Geschoss als
Start waehlbar, beachtet auch eigene-Hoehe-Ziel)
- Stufen: Dropdown 2-40 (statt freie Eingabe), mit Lock nur S-konforme
Werte
- Dropdowns nutzen System-Font (statt mono)
- Ausgrenzung 'Aussenlinie'-Toggle (Aussenlinie immer an)
- Pfeil-Style-Dropdown unter Lauflinie-Checkbox: klassisch / gefuellt
(Solid-Hatch) / breit / voll (Spitzen bis Treppen-Aussenkanten)
Backend Treppe:
- Start-Z-Override via treppe_uk_over (m Offset relativ zu Geschoss-OKFF)
- 2D-Symbol bleibt auf OKFF (egal ob Versatz) — Symbol klebt am Boden
- Lauflinie-Schaft auf visuellen Treppen-Mittelpunkt versetzt
(bei Lage=links/rechts), nicht mehr auf der Referenz-Achse
- Trittmass-Lock: treppe_lock_s + target_S/A. Beim Aktivieren werden
S+A als Ziel gespeichert. Bei H-Change wird N=round(H/target_S)
recomputed + Axis-Laenge auf N*target_A angepasst (gerade Treppen)
- Bruchsymbol-Toggle aus: ganze Treppe ungesplittet zeichnen
(eff_cut_h=0 → kein Lower/Upper-Split)
- Treppen-Endpunkt-Marker (treppe_grips.py) — gruene Punkte an Start/
Ende der Lauflinie, beachtet treppe_art (Wendel: poly[1]/poly[2])
Verdoppelungs-Fix:
- _find_target_volume skipt treppe_2d_symbol explicit (sind 2D-Curves,
kein Volume). Vorher konnte Replace(curve, brep) fehlschlagen → das
echte Treppen-Brep blieb stehen + neues kam dazu → Duplikat
- _find_objects_by_wall_id mit HiddenObjects+LockedObjects-Iterator,
findet auch Objs auf hidden 3D-Layer
- Anti-Dup-Cleanup in _regenerate_element: bei mehreren treppe_volume
mit gleicher element_id → alle ausser dem ersten loeschen
State-Pipeline:
- geschosse-Liste enthaelt jetzt okff+hoehe (fuer Frontend-Constraints)
- Treppe-State neu: ukOver, arrowStyle, lockS, targetS, targetA
- Hidden-Source-Fallback in _send_state findet auch Treppen wenn der
3D-Layer aus ist (sodass Properties-Panel angezeigt wird)
Dimensionen-Panel:
- on_select + on_idle skippen waehrend Partnership-Cascade oder
User-Transform — kein Flicker mehr beim Drag
Andere:
- Wand-Polyline-Vertex-Grips (alle Vertices, nicht nur Enden)
- PopupMenu unterstuetzt _divider + checked-Items
- TREPPEN/RAEUME Layer-Migration auf Capital-Case
- selection-partnership tolerant: hidden Source wird trotzdem in die
Selection genommen (sonst kann Drag nicht durch Pure-Transform)
Frontend:
- 2D-Plansymbol pro Treppe (Tritte/Lauflinie/Aussenlinie/Bruchsymbol)
mit per-Treppe-Toggles in Properties-Panel
- 'Obere Stufen gestrichelt'-Toggle splittet Tritte/Aussenlinie an
Schnittebene; Lauflinie hat zwei Pfeile bei Bruch
- Wand-Polyline-Grips fuer alle Vertices (nicht nur Enden)
- PopupMenu unterstuetzt Divider + Checkbox-Items
Backend:
- Eigener Layer 41_Treppen_2D fuer Plansymbol, Layer-Default schwarz
- Aussenlinie-Polygone folgen der Bruch-Diagonale (kein Versatz mehr)
- Linetype-Fallback laedt Dashed bei Bedarf nach
- Tritten-immer-an (Toggle entfernt), Z auf Geschoss-OKFF
- TREPPEN/RAEUME Layer-Migration auf Capital-Case (Treppen/Raeume)
- Selection-Partnership: treppe_2d_symbol pairs in axis + volume
Pure-Transform fuer Treppen-Move:
- treppe_2d_symbol + treppe_volume in VOLUME_TYPES → cascade-Support
- Phase 1.5 Volume-only-Detection: wenn Source unbewegt aber Volumes
uniform translated → synthetisiere canonical aus Avg-Delta der
bewegten Volumes (unbewegte rausgefiltert sonst Verzerrung)
- Hidden-inclusive ObjectEnumerator in Snapshot + Apply-Loop damit
hidden treppe_axis auf 40_Treppen mit-transformiert wird
- Properties-Fallback im _send_state findet hidden Sources via
expliziter Iteration → Panel zeigt Treppe auch bei 3D-Layer aus
- Dimensionen-Panel skipt on_select/idle waehrend UT_ACTIVE oder
Partnership-Cascade → keine Flicker beim Drag mehr
User-Bug: Layout-Skip-Optimierung war zu aggressiv — Mac Rhino haelt die
Panel-Anordnung zwischen Sessions doch nicht im internen State, also
wurden Panels falsch platziert nach Quick-Restart. Skip-Logik raus, der
~3s _-WindowLayout-Apply laeuft wieder jedes Mal. Das ist OK weil der
Splash diese Wartezeit jetzt optisch abdeckt.
Splash verbessert:
- _try_borderless_mac(): direkter NSWindow-Zugriff via Eto.ControlObject
+ ObjC-Methoden (setStyleMask_, setOpaque_, setHasShadow_,
setBackgroundColor_, setMovableByWindowBackground_) — produziert
echten borderless Mac-Look wie der Launcher-Splash
- Form-BackgroundColor auf transparent damit das gradient des WebView-
HTMLs durchscheint (rounded petrol gradient mit weichem Verlauf)
- WebView selber transparenter Hintergrund
- Closeable/Minimizable/Maximizable/Resizable alle False
- [SPLASH] visible log fuer Debug-Sichtbarkeit
Verdeckt visuell die 3+ Sekunden Wartezeit beim Cold-Start (Panel-
Registration + WindowLayout-Apply). Stilistisch identisch zum
Launcher-Splash: petrol-gruener Verlauf mit "DOSSIER."-Logo, pulsierendem
Dot, animierter Progress-Bar.
Architektur:
- _startup_splash.py: zentrale show() / hide() Helpers
- Borderless Eto.Forms.Form (420x160), Topmost, kein Taskbar-Eintrag
- WebView mit Inline-HTML (gleicher Stil wie launcher/public/splash.html)
- Sticky-Key _dossier_startup_splash haelt die Form-Referenz
- Safety-Timeout 8s falls hide() vergessen wird
- startup.py _load_all: show() ganz am Anfang (bevor Imports laufen)
- oberleiste._on_ready: hide() via 200ms-Timer NACH window-layout-apply
(bzw. nach skip) — Layout-Animation ist auf Panels in Finalposition
kurz sichtbar bevor Splash verschwindet
Effekt: User sieht sofort einen schoenen Branded-Loading-Screen statt
3s grauer Rhino-UI mit halb-geladenen Panels.
Diagnose-Log zeigte: 3046 ms (= 93% der 3.3s Cold-Start-Zeit) gehen
allein in den _-WindowLayout RunScript-Call beim ersten oberleiste-
_on_ready. Rhino dockt dabei alle Panels neu — teuer.
Optimierung: Marker-File-basiertes Skip
- Nach erfolgreichem Apply: ~/Library/Application Support/
ch.gabrielevarano.Dossier/layout_marker.json mit Timestamp + Name
- Bei _on_ready: wenn Marker < 10 min alt UND derselbe Layout-Name →
skip (Rhino "remembers" die Panel-Positionen meistens noch im
internen State, nur unzuverlaessig zwischen LANGEN Pausen)
- Sonst (kalter Tag, langer Reboot, anderer Name): apply normal
Effekt fuer typischen Dev-Workflow (haeufige Quick-Restarts):
- Erster Restart heute: 3s Apply (wie bisher) + Marker
- Naechste Restarts < 10 min spaeter: ~0ms (skip)
- Nach Pause > 10 min: erneuter Apply
Helpers _is_layout_recently_applied + _mark_layout_applied. Marker
wird auch geschrieben wenn User manuell apply (z.B. via Settings-
Dialog oder Launcher pendingApplyLayout) → konsistent.
Falls Layout doch falsch ist nach Skip: Oberleiste-Settings →
"Jetzt anwenden"-Button erzwingt Apply.
Bisher: Wall-time 3.2s vs gemessene Arbeit 0.9s → 2.3s unmeasured.
Jetzt werden auch die bisher blinden Stellen getrackt:
- 9× Modul-Import (rhinopanel, oberleiste, ausschnitte, ...) als
phase="import", label=mod_id
- oberleiste display-mode-Ensure-Loop (4× DisplayModeDescription-Calls)
als phase="display_modes"
- _apply_window_layout RunScript-Sequenz als phase="window_layout"
- text_editor doppelklick-Hook + view-modes assign + unit-check als
post_init/hook Phases
Nach Reload sollte das Summary zeigen wo die 2.3s versickern → daraus
gezielte Optimierungen.
Download-Button (file_download Icon) neben den Expand/Collapse-Buttons
in der Toolbar. Klick → SaveFileDialog (Default 'sia_bilanz.csv') →
schreibt Excel-kompatible CSV.
Backend (elemente_uebersicht._export_bilanz):
- Wide-Format: Spalten = Kategorie + ein Geschoss + Total
- Zeilen: HNF, NNF, NF, VF, FF, NGF, GF, AGF, Räume (count), Personen
- Werte via _elm.compute_sia_bilanz fuer jeden Scope (gleiche Logik
wie Bilanz-Stempel + Uebersicht — single source of truth)
- Format: Semikolon-Separator + UTF-8 BOM + Komma als Dezimaltrenner
(Excel CH/DE-kompatibel, oeffnet ohne Umweg)
- Flaechen mit 2 Nachkommastellen, Raume/Personen als int
Architekten-Workflow: bei Schulen/Buero/Versammlungsstaetten muss die
Personenzahl pro Raum erfasst werden (SIA: m²/Person als Indikator).
Backend:
- UserString dossier_raum_personen (int)
- _attach_meta + _read_meta + state-emit
- _update_wall raum-Branch akzeptiert "personen" im Patch
- compute_sia_bilanz aggregiert personen-Summe ueber Scope
- Stempel: neue Show-Flag stempel_show_personen (default false) +
Bilanz-Renderer-Zeile "N Personen"
- Stempel-Stil-Field showPersonen mit dabei
Frontend (RaumProperties):
- Personen-Input (number, min 0) zwischen Funktion + Layout-Builder
- Nur sichtbar bei normalen Raeumen (nicht GF/AGF)
- Live-Anzeige "m²/Person" Suffix wenn area + personen > 0
→ User sieht sofort ob die Belegung sinnvoll ist (SIA-Vergleich)
Frontend (StempelProperties):
- Neuer Show-Toggle "Personen" (default off)
- Live-Vorschau zeigt Personen-Summe wenn aktiv + > 0
- _OFF_BY_DEFAULT Set generalisiert Default-Handling (showCount, showPersonen)
Doppelbelegung Code 60: alte Konvention "Plangrafik" steht im Weg fuer
die neuen Raum-Layer (RAEUME 60, GF 61, AGF 62). Wenn ein Doc beides
hat, fiel die Auto-Add-Logik in _find_ebene_sublayer_name aus → Raeume
landeten auf Rhino-Layer "60_RAEUME" der aber im Dossier-Ebenen-Panel
nie auftauchte (dort stand 60 = Plangrafik).
Fix in zwei Teilen:
1) Frontend-Default-Schema (App.jsx INITIAL_EBENEN):
- 60: RAEUME (neu, fuer HNF/NNF/VF/FF)
- 61: GF (Geschossflaeche)
- 62: AGF (Aussengeschossflaeche)
- 80: Plangrafik (verschoben von 60)
2) One-shot Migration in elemente._migrate_plangrafik_60_to_80_once():
- Detect: dossier_ebenen hat code=60 + name=Plangrafik
- Action: code 60 → 80, RAEUME/GF/AGF auf 60/61/62 hinzufuegen
- Rhino-Layer rename: alle "60_Plangrafik" Layer → "80_Plangrafik"
- build_layers + broadcast_state → Ebenen-Manager UI aktualisiert
- Sticky-Flag verhindert Re-Run
Plus kleinerer UX-Fix: Skala-Dropdown-Labels gekuerzt
("fix (m)" / "massstaeblich (mm)" statt langer Beschreibungen).
Geschossflaeche (GF) und Aussengeschossflaeche (AGF) sind reine
Flaechen-Ausweisungs-Outlines — keine Raeume mit Name/Nummer/Funktion.
Backend:
- Neue Layer-Helpers _layer_path_raum_gf (61_GF) und _layer_path_raum_agf
(62_AGF) — eigene Sublayer pro Geschoss, eigene Default-Farben
- _layer_path_for_raum_sia(doc, gname, sia): routet sia=gf → GF-Layer,
sia=agf → AGF-Layer, sonst RAEUME-Layer (HNF/NNF/VF/FF/leer)
- _regenerate_element_body raum_outline-Branch nutzt das Routing →
Source-Outline migriert automatisch auf den richtigen Layer
- _update_wall raum-Branch: bei SIA-Wechsel (z.B. HNF → GF) auch
Layer-Migration
- _make_raum_stamp_text: bei sia=gf/agf default-layout
override auf [["sia", "area"]] → Stempel zeigt nur "GF 234.5 m²" /
"AGF 18.0 m²" ohne Nummer/Name/Funktion
Frontend (RaumProperties):
- Conditional isFlaeche = sia in (gf, agf)
- Versteckt bei isFlaeche: Stil-Picker, Nummer-Input, Name-Input,
Funktion-Input, StempelLayoutBuilder
- Bleibt sichtbar: Geschoss, SIA-Tag-Selector, Fuellung, Rundung,
Skala-Modus, Flaeche/Umfang-Footer
- Info-Zeile zeigt bei isFlaeche: "Flaechen-Outline (GF) auf eigenem
Layer · Stempel zeigt nur GF + Flaeche"
Neuer Element-Type "stempel" — TextEntity die automatisch eine SIA-416
Bilanz aggregiert und im Plan platziert wird. Re-rendert sich live wenn
sich Raeume im Scope aendern.
Backend (elemente.py):
- Neue SIA-Tags: GF (Geschossflaeche), AGF (Aussengeschossflaeche)
mit eigenen Labels + Pastell-Farben in _SIA_COLORS_HEX
- "stempel" als SOURCE_TYPE; eigene UserStrings:
- stempel_scope: "total" | "geschoss:<gid>"
- stempel_txt_h, stempel_font, stempel_bold, stempel_italic
- compute_sia_bilanz(doc, scope): aggregiert nach SIA-Tags, liefert
HNF/NNF/VF/FF/GF/AGF + abgeleitet NF/NGF/count + Scope-Label
- _format_bilanz_lines: kompakte Stempel-Textzeilen ("HNF 120.5 m²"),
Trennlinien + nur Kategorien > 0
- _make_stempel_text: TextEntity-Builder mit Header "Nutzflächen · {Scope}"
- _regenerate_element_body "stempel"-Branch: in-place Replace mit
aktualisiertem Text (Position bleibt aus alter Geometrie)
- _regenerate_stempel_for_geschoss: regennt alle Stempel im selben
Geschoss + alle "total"-Stempel
- Auto-Cascade: raum_outline-Regen setzt sticky-marker; nach REGEN_BUSY-
Release wird _regenerate_stempel_for_geschoss aufgerufen
- _cmd_create_stempel: GetPoint im Viewport, layer = aktives Geschoss
Raum-Sublayer, default-Scope = aktives Geschoss
- _update_wall "stempel"-Branch: scope/txtH/font/bold/italic via patch
- elemente_uebersicht: SIA-Bilanz um gf/agf/ngf erweitert; "stempel"
als KIND-Eintrag
Frontend:
- createStempel-Bridge-Export
- "Stempel"-Pill-Button in der "Raeume"-PillGroup
- StempelProperties-Component: Scope-Dropdown (Total + alle Geschosse),
Bilanz-Vorschau mit Hervorhebung der NF (Accent-Farbe)
- KIND_META + RAUM_SIA_KINDS um GF/AGF erweitert
Workflow: Pill "Stempel" klicken → Punkt im Viewport → Stempel erscheint
mit Header "Nutzflächen · EG" + Bilanz. Properties: Scope auf Total
umstellen oder anderes Geschoss waehlen. Neue Raeume taggen mit SIA-
Tag → Stempel aktualisiert sich automatisch.
PHASE 1 — Bulk-Apply auf mehrere selektierte Raeume:
- Backend _cmd_apply_raum_stil: leere ids im Patch → nimmt automatisch
alle aktuell SELEKTIERTEN raum_outlines aus dem Doc (Bulk-Fallback)
- Frontend sendet leere ids fuer die Default-Bulk-Variante. User
selektiert N Raeume im Viewport → klickt Stil im Properties-Picker →
Stil wird auf alle angewendet. Inkludiert immer den Properties-Raum.
PHASE 2 — Stil-Manager als Settings-Tab:
- Neuer Tab "Raumstile" in ProjectSettings-Dialog
- Liste aller Stile mit Drag-Reorder (HTML5 native), Inline-Rename,
Duplicate (content_copy-Icon), Delete (mit Confirm)
- Mini-Preview: zeigt Layout-Struktur (z.B. "nummer·name / funktion / area")
und der Name wird mit Font/Bold/Italic des Stils gerendert als Vorschau
- Backend _cmd_reorder_raum_stile + _cmd_duplicate_raum_stil in elemente.py
- ProjectSettings-Bridge dispatcht direkt zu elemente.load/save_raum_stempel_stile
(kein Roundtrip via Elemente-Bridge noetig)
- STILE_UPDATED-Message nach jeder Op pusht die neue Liste zum Dialog
- params bei _open_project_settings enthalten jetzt raumStempelStile + fonts
Bridge-Exports: reorderRaumStile / duplicateRaumStil in rhinoBridge.js
UX-Konsolidierung: Texthoehe und Ausrichtung wurden bisher doppelt
gesetzt (Panel + Oberleiste-Text-Block). Jetzt nur noch via Oberleiste
wenn der Stempel selektiert ist — Panel zeigt nur noch den Skala-Modus
(fix/masstab) als kompakten Dropdown.
Backend: _sync_raum_stamps_to_source spiegelt jetzt auch
TextHorizontalAlignment vom Stempel zur Source (Left/Center/Right →
links/mid/rechts). Damit kommt die User-Wahl aus der Oberleiste sauber
in die UserStrings — und wird beim naechsten Regen + bei Stil-Speichern
korrekt uebernommen.
Frontend RaumProperties:
- Ausrichtung-Zeile (3 BarToggles) raus
- Hoehe-Zeile reduziert auf Skala-Modus-Dropdown:
"fix · Hoehe = Oberleiste-Wert (m)" | "masstab · skaliert mit Plan-Massstab"
- Local-State txtH + setTxtH entfernt (unused)
- Stempel-Stil-Speichern liest weiter raum.txtH + raum.align (kommen
jetzt vom Sync) → captured korrekt
UserString dossier_raum_stil_id auf der raum_outline. Wird gesetzt:
- bei APPLY_RAUM_STIL → schreibt sid auf alle Ziel-Raeume
- bei SAVE_RAUM_STIL mit applyToIds=[…] → schreibt neue sid auf
uebergebene Raeume (Frontend nutzt das beim "+ Speichern…"-Flow um
den aktuellen Raum mit dem neuen Stil zu verknuepfen)
_read_meta liest stil_id mit → State-Emit feldet "stilId" zum Raum.
Frontend's Stil-Dropdown markiert dadurch automatisch den aktiven Stil
(via activeStilId = raum.stilId).
Manuelle Edits (UPDATE_ELEMENT) touchen stil_id NICHT — User kann
seinen Stil leicht tweaken ohne dass die Verknuepfung verloren geht.
Wenn der Tweak weit weg ist, kann er den Stil aktualisieren via
"+ Aktuelle Settings als Stil speichern" (uebernimmt dann mit der
NEUEN stil_id).
Aggregiert alle raum_outline-Flaechen nach raum_sia-Klassifikation
(hnf/nnf/vf/ff) und zeigt sie als kompakte Mini-Tabelle direkt unter
dem Geschoss-Header in der Project-Browser-Uebersicht.
Backend (_build_overview):
- Neuer Returnschluessel siaBilanz: {geschossId: {hnf, nnf, vf, ff,
ohne, nf, total, count}} in m^2
- NF = HNF + NNF (Nutzflaeche nach SIA 416)
- Raeume ohne SIA-Tag landen in "ohne"
Frontend (ElementeUebersichtApp):
- Direkt unter dem Geschoss-Header eine Inline-Tabelle mit nur den
Klassen die > 0 sind (kein Spam wenn nichts klassifiziert ist)
- NF separat hervorgehoben (Accent-Farbe) als wichtigste Kennzahl
- Read-only, aktualisiert sich mit jedem state-emit (Raum-Aenderung,
SIA-Tag setzen, neue Raeume) automatisch
Damit User wiederkehrende Stempel-Configs (Wettbewerb / Bauantrag /
Mobiliar etc.) nicht jedes Mal neu klicken muss.
Backend (elemente.py):
- Storage in doc.Strings dossier_raum_stempel_stile als JSON-Array
- load_raum_stempel_stile / save_raum_stempel_stile Helpers
- Bridge-Handler:
- SAVE_RAUM_STIL: upsert by id (neu wenn leer)
- DELETE_RAUM_STIL: remove by id
- APPLY_RAUM_STIL: schreibt Stil-Felder auf Raum-IDs + Regen
- _RAUM_STIL_FIELDS umfasst font/bold/italic/txtH/txtModus/align/
rundung/fuellung/showSia/layout (alles was die Optik bestimmt)
- raumStempelStile im STATE-Emit zum Frontend
Frontend:
- saveRaumStil/deleteRaumStil/applyRaumStil in rhinoBridge.js
- RaumProperties: neue "Stil"-Sektion oben mit Dropdown
* gespeicherte Stile + "+ Aktuelle Settings als Stil speichern" +
"🗑 Aktiven Stil loeschen"
* Klick auf Stil → applyRaumStil mit aktueller Raum-ID
- Beide PropertiesView-Aufrufe (inline + satellite) bekommen die Liste
Statt jeden Wert im Code zu konvertieren wird sichergestellt dass das
Doc in der gewuenschten Unit ist:
- defaults.unit ('meters'|'millimeters'|'centimeters') in
dossier_project_settings, Default 'meters'
- ProjectSettings-Dialog "Voreinstellungen" Tab: neue Sektion
"Arbeitseinheit" mit Toggle-Group fuer m/cm/mm
- get_project_unit() + get_project_unit_enum() Helper in rhinopanel
- startup._check_doc_unit() prueft beim Doc-Open ob ModelUnitSystem
matched — bei Mismatch Eto-MessageBox "Doc auf X umstellen?"
- "Yes" ruft _-Units _Model _<Unit> _Yes (Geometrie wird mit-skaliert)
- "No" setzt doc.Strings-Flag dossier_unit_checked → keine erneute Frage
- Check laeuft beim _on_doc_opened-Hook + initial fuer aktives Doc
Vorgehen ist deutlich sauberer als der vor-revert unit-aware Code
(135 Zeilen Konvertierungslogik vs 80 Zeilen Check+Convert).
DOSSIER-UI nimmt immer Meter entgegen — bisher wurden die Werte 1:1 als
Doc-Units verwendet, was bei Doc=Millimeter winzige Geometrie ergab
(0.25m getippt → 0.25mm Wand).
Storage-Konvention bleibt METER (UI-friendly). Konvertierung passiert
beim Geometrie-Bau:
- Neue Helpers _m2u / _u2m via Rhino.RhinoMath.UnitScale
- _regenerate_element_body normalisiert ALLE m-typischen Meta-Felder am
Eingang via _m2u — Geometrie-Code darunter bleibt unveraendert und
arbeitet in Doc-Units (funktioniert in jedem Unit-System)
- Lokaler _gs() Wrapper konvertiert Geschoss-OKFF + Hoehe → Doc-Units
- _resolve_uk_ok / _resolve_decke_z / _resolve_dach_base konvertieren
Geschoss-Heights aus JSON (m) → Doc-Units
- _resolve_raum_text_height_m masstab-Pfad: m-Result → Doc-Units
- _sync_raum_stamps_to_source: Stempel-TextHeight + Position-Delta sind
in Doc-Units, werden vor Storage via _u2m → m konvertiert
Effekt:
- Doc in Metern: kein sichtbarer Unterschied (UnitScale=1, no-op)
- Doc in Millimeter: 0.25 (m) wird zu 250 (mm) Wand → richtig dick
- State-Emit zum Frontend bleibt in m → UI konsistent
regen_masstab_raeume liest get_applied_scale_ratio() um die paper-mm-
Hoehe in Modell-m umzurechnen. Lief bisher VOR _set_applied_scale_for_vp
→ las immer noch den alten Wert. Resultat: nach Skala-Wechsel war die
Stempel-Hoehe um die alte Skala berechnet, naechster Wechsel um die
vorletzte usw.
Reihenfolge korrigiert: erst write_user_scale + set_applied_scale_for_vp,
DANN regen_masstab_raeume.
Bug: Beim Toggle fix↔masstab blieb txt_h derselbe Zahlenwert mit anderer
Einheit. 0.20m wurde zu 0.20mm@1:100 = 0.02m Text (2cm). Stempel
unsichtbar klein.
Fix: Bridge konvertiert txt_h automatisch wenn der Modus wechselt UND
kein expliziter txtH-Wert im Patch ist:
fix → masstab: paper_mm = m * 1000 / scale
masstab → fix: m = paper_mm * scale / 1000
Frontend sendet beim Modus-Toggle nur { txtModus } ohne txtH → Backend-
Konvertierung greift.
Plus: _list_system_fonts probiert jetzt InstalledFonts() + AvailableFontFaces
(InstalledFontsAsString existiert auf Mac Rhino 8 nicht); Ergebnis wird
session-weit gecacht damit der Error nicht jeden state-emit spamt.
Neuer UserString dossier_raum_txt_modus = "fix" | "masstab" (default fix).
- fix: raum_txt_h ist Meter (Modellhoehe, bisheriges Verhalten)
- masstab: raum_txt_h ist Paper-mm. Render-Hoehe (m) =
paper_mm * applied_scale / 1000 — wird zur Render-Zeit aus
massstab.get_applied_scale_ratio() gelesen, Fallback 1:100.
Massstab-Sync:
- massstab._apply_scale ruft nach Skala-Wechsel elemente.regen_masstab_raeume(doc)
→ alle Raeume im masstab-Modus regennen automatisch, Texthoehe folgt der
neuen Skala (z.B. Switch 1:100 → 1:50 halbiert die Modellhoehe).
_sync_raum_stamps_to_source masstab-aware: im masstab-Modus wird die
TextHeight am Stempel NICHT zurueck auf raum_txt_h gespiegelt (sie ist
abgeleitet, nicht die Wahrheit) — sonst waere der naechste Regen sofort
falsch positioniert. Offset + Font/Style werden weiterhin gespiegelt.
UI: Modus-Toggle "fix m" / "masstab mm" + Hoehen-Input + Einheits-Suffix
in RaumProperties zwischen Ausrichtung und Funktion.
- Neues Template rhino/templates/dossier_3d.ini fuer perspektivische Views
- Registry-Loop in oberleiste.py generalisiert (Plan + 3D + Material +
Raytracing) — Material/Raytracing skippen wenn kein Template vorhanden,
um Cycles-Pipeline-Clone-Crash zu vermeiden
- Guid-Replace praezisiert: nur Section-Header-Guid, nie PipelineId
- Plan-spezifische ini-Patches auf target_name=="Dossier Plan" gegated
- Auto-Assign in startup.py: Parallel-Viewports -> Plan, Perspective -> 3D,
einmal-pro-Doc via doc.Strings-Flag (User-Overrides bleiben)
- schnitte.activate_schnitt setzt Dossier Plan explizit (Hatches auch in
Schnittperspektive sichtbar)
- GestaltungApp Section-Block neu strukturiert: PenBlock fuer 3D als 'Fill'
innerhalb der Section, Solid-Fill-Toggle entfernt (war Duplikat)
Beim Hinzufuegen oder Importieren eines Library-Items wird automatisch
ein PNG-Thumbnail vom Item generiert (Top-View, 128x128) und in
library/previews/<id>.png abgelegt. Frontend rendert die Previews als
Base64-Data-URIs (sicher gegen WebKit-file://-Restriktionen).
library.py:
- _previews_dir(): legt previews/-Folder an
- _preview_rel_for(asset_rel): predictable PNG-Pfad pro Item
- _capture_thumbnail_of_objects(): hided andere Objekte temporaer,
switcht auf Top-Parallel, ZoomBoundingBox, CaptureToBitmap → PNG,
restored Viewport + Hidden-State
- read_preview_data_uri(): liest PNG + encoded als data:image/png;base64
- Hook in convert_to_3dm_via_import (vor Cleanup) + save_selection_to_asset
rhinopanel.py:
- _enrich_library_items_with_previews(): haengt previewDataUri an
jedes Item das ein preview-Feld hat
- Initial-Params + _send_library + ElementeBridge._cmd_list_library
liefern angereicherte Items
- _add_library_file + _save_selection_as_library setzen preview-Pfad
im Item wenn Thumbnail-Datei existiert
Frontend:
- SymbolPicker.ItemPreview: rendert <div backgroundImage> mit Base64-URI
wenn vorhanden, sonst Icon-Fallback
- ProjectSettingsDialog Symbole-Tab: List-Row + Detail-Identity zeigen
Thumbnail (32px in Liste, 56px im Detail), Icon nur als Fallback
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User kann jetzt im Symbole-Tab Dateien in vielen Formaten waehlen — Rhino
konvertiert automatisch nach .3dm via _-Import + File3dm.Write.
library.py — convert_to_3dm_via_import():
- Snapshot der existierenden Object-IDs im aktiven Doc
- User-Selection sichern
- _-Import scripted ausfuehren → neue Objekte in Doc
- Diff -> neue Objekte sammeln
- BoundingBox.Min als Origin → in File3dm packen
- f3.Write nach library/assets/<name>.3dm
- Neue Objekte aus Doc wieder loeschen + User-Selection restoren
- sticky 'dossier_library_import_busy' + 'dossier_swisstopo_busy' damit
unsere Listener nichts cascaden waehrend Doc kurzzeitig die Importe haelt
rhinopanel.py — _add_library_file():
- .3dm: copy_to_assets (wie bisher)
- .dwg/.dxf/.obj/.fbx/.dae/.stl/.3ds/.skp/.iges/.step/.ply: konvertieren
- Sonst LIBRARY_ERROR
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
User selektiert in Rhino → klickt im Symbole-Tab 'Aus Auswahl' →
Selection wird in eine .3dm-Datei verpackt + Library-Item entsteht
(oder bestehendes wird aktualisiert).
Backend (library.py):
- save_selection_to_asset(doc, target_name): erstellt File3dm aus der
aktuellen Selection, packt Geometry relativ zu BoundingBox.Min (Block-
Origin am Ursprung), schreibt nach library/assets/
Backend (ProjectSettingsBridge):
- SAVE_SELECTION_AS_LIBRARY-Handler: holt Selection, fragt bei neuen
Items via Rhino-GetString nach Name, schreibt .3dm, fuegt Item zum
Manifest oder updated bestehendes (variant '2d'/'3d')
Frontend (Symbole-Tab):
- List-Footer: 'Aus Datei' + 'Aus Auswahl' Pills (neues Item)
- Pro 2D/3D-Slot im Detail: 'Datei wählen' + 'Aus Auswahl' Pills
(Variante eines bestehenden Items befüllen)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Schema-Erweiterung:
- _PROJECT_SETTINGS_DEFAULTS hat jetzt 'project'-Block mit
name / number / address / bauherr / architekt / notes / projectZeroMum
- _normalize_project_meta stripped Strings + clampt mum als float
- load/save_project_settings handeln das 'project'-feld
- save_project_settings spiegelt projectZeroMum auch in den Legacy-Key
dossier_project_zero_mum (fuer Geschoss-Settings-Dialog)
- load_project_settings liest Legacy-Key als Fallback wenn neuer Wert
noch nicht gesetzt
UI:
- InlineTextField + TextareaField Helpers (Pill-Stil)
- Projektdaten-Section in Voreinstellungen-Tab:
Name, Projekt-Nr., Adresse, Bauherrschaft, Architekt:in,
EG-Nullpunkt m.ü.M (mit Hinweis auf Swisstopo-Nutzung), Notizen
Swisstopo:
- _cmd_open_swisstopo_dialog laedt Projekt-Adresse + sendet projectAddress
im SWISSTOPO_STATE
- SwisstopoApp: vorbelegt searchText mit projectAddress wenn Feld leer
ist (User-Input wird nicht ueberschrieben)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Material ist jetzt rein 3D — Section-Hatch (2D-Schnitt) wird nicht mehr
am Material definiert, sondern am Layer (via Rhino-Layer-Dialog oder
spaeter via Ebenen-Settings + neuer Hatch-Tab im Project-Settings).
Schema-Aenderungen:
- _normalize_material: hatch + scale entfernt
- _MATERIAL_LIBRARY (elemente.py): hatch + scale aus allen Builtin-Mats
- _get_all_materials: ohne hatch
- _send_state materials payload: nur {name, color}
- Library import_material: PBR + Texturen werden weitergegeben
Backend:
- _ensure_material_sublayer: erstellt Sublayer mit Color, RESETTET aber
alten SectionHatchIndex auf -1 (= "kein eigener Hatch") damit
Inheritance/User-Override greift. Vorher wurden alte Material-Hatch-
Werte da haengen geblieben.
Frontend:
- MaterialDetail: Schraffur-Section entfernt
- hatchPatterns-Prop entfernt
Konsequenz: existierende Waende verlieren ihren Section-Hatch beim
naechsten Regen. User definiert Section-Hatches jetzt auf Layer-Ebene.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
open_satellite_window ohne bridge=-Param verwendet eine Inline-Bridge die
nur READY/SAVE/CANCEL kennt — PICK_TEXTURE_FILE-Messages wurden stumm
verworfen.
Fix: _ProjectSettingsBridge als BaseBridge-Subclass mit eigener
PICK_TEXTURE_FILE-Handler-Logik. Sendet TEXTURE_PICKED direkt zurueck an
die selbe WebView.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
_ensure_pbr_material baut ein vollständiges Rhino-Material aus dem
Project-Settings-dict:
- ToPhysicallyBased() + BaseColor/Roughness/Metallic/Opacity/OpacityIor
- Diffuse-/Bump-/Transparency-Texture via SetBitmapTexture etc.
- UV-Repeat = 1/uvScaleM
- Cache per Signature (Color+PBR+Texture-Pfade)
_get_all_materials liefert jetzt full-dicts (nicht mehr nur color/hatch/
scale) damit Wand-Regen Zugriff auf PBR + Texturen hat.
Wand-Regen: wenn voll-dict aus Project-Settings vorliegt → PBR-Material,
sonst Fallback auf legacy _ensure_material(hex).
Auto-Regen on Save:
- PBR-Material-Cache + Legacy-Material-Cache invalidieren
- Alle wand_axis im Doc regenerieren (in EINEM Undo-Record)
- User aendert Material-Properties -> existierende Waende updaten sofort
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mac Rhino feuert beim Endpunkt-Grip-Drag intern den Command
'ReplaceObjectProxy'. Ohne den Eintrag in _USER_TRANSFORM_CMDS oeffnete
_on_command_begin keinen Undo-Record + nahm keinen Snapshot — unser
chain-Pre-Check + Regen liefen dann erst auf Idle in einem eigenen
Undo-Record ('Elemente regenerieren (N)'). Cmd+Z brauchte deshalb zwei
Schritte: erst Volume-Restore, dann Axis-Restore.
Fix:
- ReplaceObjectProxy in _USER_TRANSFORM_CMDS
- _REGEN_BUSY-Guard in _on_command_begin damit unsere eigenen internen
Replace-Calls (Chain-Volume-Rebuild) keinen unerwuenschten Snapshot
triggern
Plus: No-Op-Tolerance 1mm (Architektur-Praezision) im Replace-Handler
faengt CommitChanges-Microdrift ab — keine Chain-Break-Cascade beim
Selektieren mehr.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>