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>
Custom-Grip-Overlays via DisplayConduit + MouseCallback:
- wand_grips.py: dicke klickbare Marker an wand_axis-Endpunkten, auch
wenn die Referenz-Layer ausgeblendet ist. GetPoint mit fixem Anker.
- schnitt_grips.py: 3 Marker pro Schnitt (P1, P2, Mid). Mid translatiert
ganze Linie, P1/P2 verschieben Endpunkt. Hide Clipping-Planes waehrend
GetPoint damit kein Verbots-Cursor durch Locked-Edges erscheint.
skip_view=True bei Re-Activate damit Drag nicht in Section springt.
Referenz-Architektur umgebaut:
- wand_axis + oeffnung_point liegen jetzt unter <Geschoss>::20_Waende::
20r_Referenz statt eigener top-level 19_Referenzlinien-Ebene.
- Migration v4 zieht existierende Sources auf den neuen Pfad.
- Toggle in Oberleiste keyword-driven: findet alle 'Referenz'-Sub-Ebenen
rekursiv, toggelt alle Praefixe gemeinsam. Bauteil-uebergreifend.
Oberleiste-Layout:
- Druck-Ansicht-Button hoch neben Massstab-Dropdown (Reihe 1).
- Referenzlinien-Toggle in Reihe 2 neben Zoom-Pill, symmetrisch zum
Druck-Button. Zoom-Pill auf 3 Buttons reduziert.
- Print-View AN → Referenz-Layer automatisch ausblenden, Snapshot
restored beim Ausschalten.
Fix: clear_schnitt_clipping respektiert Mode=Locked nicht — vor Delete
auf Normal-Mode wechseln + Modify damit's persistiert. Schnitt-Loeschen
raeumt Clipping-Planes jetzt sauber auf.
Fix: Schnitt-Doppelklick-Handler aktiviert nur bei expliziter Schnitt-
Auswahl, ignoriert andere Selektionen.
Fix: _send_state Selection-Detection mit Source-ODER-Volume-Fallback —
Fenster-Properties erscheinen jetzt auch wenn oeffnung_point auf hidden
Referenz-Layer liegt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Schnitt-Feature V1+V2:
- Neues rhino/schnitte.py mit Pick-Workflow, Activation (Clipping-Planes +
Parallel-View), 2D-Plan-Symbol auf 18_Schnittlinien-Sublayer
- Doppelklick auf Symbol aktiviert den Schnitt
- Schnitt-Settings (cutAtLine/Tiefe/Höhen/Blickrichtung) im GeschossSettingsDialog
- View-Snapshot + Restore beim Wechsel Schnitt → Geschoss
- Symbol-Cleanup bei Delete via normalem Ebenen-Menü
Terrain als Volumen:
- swisstopo.volumize_terrain_object: Skirt + Bottom-Cap auf Mesh/Brep
damit Clipping-Planes gefuellte Querschnitte erzeugen
- UI im SwisstopoApp mit Nachbearbeitung-Section + Tiefen-Eingabe
Geschoss-Add mit Dialog:
- + im GeschossManager oeffnet 3-Optionen-Picker (Geschoss/Schnitt/Zeichnung)
- Geschoss-Dialog mit Anker-Dropdown, Position über/unter, Auto-Name,
Höhen-Prefill aus Anker
Fix: _send_state fallback — Element gilt als selektiert wenn Source ODER
Volume in der Selection ist (robust gegen Layer-Visibility wenn Referenz-
linien-Layer im aktuellen Mode versteckt ist)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Oeffnungen-Subtree (Rahmen/Glas/Tuerblatt/Sims/Pane/Schwung/Sturz) als
nested Children unter WAENDE im dossier_ebenen-Tree registriert + per-Kind
Material (Glas mit Transparenz)
- Sturzlinien bei 1:100 Tueren mit Innen/Aussen/Beide/Keine-Dropdown
- Referenzlinien-Layer (19) als eigene Ebene fuer wand_axis + oeffnung_point
- Swisstopo Patch-Terrain (Brep.CreatePatch) ersetzt das falsche Loft
- Pill-Style fuer alle Inputs zentral via index.css
- 2x2 Anordnen-Pill in der Oberleiste (BringToFront/Forward/Backward/SendToBack
via Rhinos DisplayOrder, kein Z-Offset)
- Chevron-Verschiebung in Ebenen-Panel ohne dass Siblings shiften
- Fix: _update_ebene_field walked nur Top-Level, nested Sublayer-Style-
Changes wurden nicht persistiert
- Fix: Sturz-Linetype wurde bei jedem Wand-Regen zurueckgesetzt
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- sweep_sign war vertauscht — closed_pt landete am falschen Ende des
Blattes. hinge=links → +1 (extends in +tan), rechts → -1.
- Schwung-Perp-Position:
- einfach (1:100) → Wand-Innenkante (unchanged)
- standard/detail (1:50/1:20) → Mitte des Rahmens (= wo das Tuer-
blatt-Brep sitzt). Damit liegt der Bogen direkt am sichtbaren
Blatt statt am Wand-Innenrand.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Schwung-Curves-Block VOR dem Volume-Replace-Pfad — sonst greifen
openAngle/hingeSide/swingInvert-Aenderungen nicht (Volume-Anzahl
bleibt gleich → continue → Swing-Skip).
- Hinge-Punkt sitzt nicht mehr auf der Wand-Achse sondern auf der
Wand-Innenkante (half_d * inside-Richtung). Tuerblatt + Bogen
beginnen damit an der Wandflucht statt in der Wandmitte.
- Rotation-Vorzeichen korrigiert (rad = open_angle * sweep_sign *
aus_sign) damit Tuere wirklich ins Innere schwingt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fenster/Tueren:
- 3-stufige SIA-400-Darstellung pro Element: einfach (1:100, flache
Scheibe ohne Tiefe in Wand-Mittelebene), standard (1:50, Rahmen +
Glas + Sims), detail (1:20, Doppelverglasung).
- Aussenseite-Flag mit Auto-Detection aus der Click-Richtung beim
Setzen — Sim sitzt automatisch aussen. Im Panel als Umkehren-Toggle.
- Tueren-Rahmen-Typ Zarge|Block — Blockrahmen ragt seitlich raus.
- Rahmen-Offset (m von Wand-Innenseite) ersetzt das 3-Preset Lage-
Feld. Wirkt auch in der einfachen Darstellung (Pane sitzt auf der
Rahmen-Mittelebene, nicht in Wand-Mitte).
- Sims nur AUSSEN. Innen entfaellt — der Sim ist gleichzeitig der
visuelle Indikator fuer die Aussenseite.
- Oeffnungs-Stile: list/save/delete-API mit 6 Default-Presets
(Fenster Standard/Gross/Bandlage, Tuer Innen/Eingang/Verglast).
Style-ID per UserString am Objekt persistiert. Im Panel BarCombo
mit "Aktuelle als Stil speichern…". Beim Rhino-Command "Stil"-
Option zum Picken vor dem Klick.
Ausschnitt-Darstellung (Phase 3):
- Doc-Level Override dossier_aktive_darstellung gewinnt vor per-
Object-Setting. Wechsel triggert Regen aller Oeffnungen via neuer
regenerate_all_oeffnungen-API.
- Ausschnitt-Capture speichert die Darstellung mit, Restore wendet
sie an und regeneriert.
- Oberleiste-Quick-Switch BarCombo mit 4 Optionen.
- AusschnittSettings-Dialog: Darstellungs-Dropdown.
Gestaltung (SectionStyle Phase 2):
- _set_section_style schreibt per-Object SectionHatchIndex/Scale/
Rotation/Color mit Multi-Fallback (Property-Namen varieren je
Rhino-Build). _selection_summary liest die selben zurueck.
- HatchEditor als shared Component fuer Fill + Section.
- geometryKind ignoriert DOSSIER-Source-Curves damit Wand-Selektion
(Axis + Volume) als 3D klassifiziert wird.
UI-Konsistenz Panels:
- Ebenenkombi zurueck als eigene Section oben im Ebenen-Panel,
Modelldarstellung-Dropdown an die freigewordene Position in der
Oberleiste (Row 1 Col 2 im 2x2-Preset-Block).
- BarCombo erweitert: stretch-Prop (Pill waechst auf Container-
Breite), onSecond/secondIcon/secondTitle fuer 2. Trailing-Button,
gearIcon-Prop. Plus-Slot immer ganz aussen rechts, Settings-Slot
direkt nach dem Caret.
- Ebenen + Zeichnungsebenen visuell kohaerent: identisches Padding
(1px 12px 1px 0), Chevron/Spacer-Slot 12px, Master-Row mit Eye
16x16 + Lock 14x14, gleiche Border + Borderfarbe. Eye-Icons in
beiden Panels untereinander ausgerichtet.
- Properties-Container ohne Border (war zuvor accent-gruen, dann
border — User wollte gar nichts mehr).
- ElementList raus aus dem Elemente-Panel (Uebersicht via Tree-
Window erreichbar). NeuesElement bleibt voll sichtbar bei
Selektion (kein Collapse), Properties oben.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Zwei neue Satellite-Windows (analog Kamera/Text-Editor):
1) Projekt-Übersicht (elemente_uebersicht.py + ElementeUebersichtApp.jsx)
- Tree Geschoss → Kind → Element-Instanzen
- Suche + Kind-Filter-Chips
- Klick = selektieren in Rhino, Shift+Klick = zoomen
- Erreichbar via account_tree-Button im Elemente-Panel-Header
2) Properties-Satellite (elemente_properties.py + ElementePropertiesApp.jsx)
- Eigenes Fenster mit der PropertiesView (gemeinsame Komponente)
- Live-Updates: elemente._send_state forwarded zu satellite-bridge via sticky
- Erreichbar via open_in_new-Icon oben rechts in der Properties-Karte
- Inline-Properties im Panel bleiben — Satellite ist für mehr Platz
Plus ElementeApp-Cleanup:
- ElementList (alle Elemente-Liste) raus — wird jetzt von Projekt-
Übersicht abgedeckt.
- Properties springen bei Selektion nach oben, NeuesElement bleibt
voll sichtbar darunter (kein Scrollen mehr).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lange Iteration mit dem Rhino TextEntity-RTF-Parser (siehe MEMORY:
rhino_textentity_rtf_limits.md). Finale Form:
- RTF-Body: per-Segment {\fN\cfN\b\i\ulnone seg}-Groups, \par
zwischen Groups als Linebreak, { }-Space-Group fuer Leerzeilen
(Rhino collapsed sonst aufeinanderfolgende \par). \fs (Font-Size)
ist NICHT unterstuetzt → eine Size pro TextEntity (global).
- htmlToRuns: emittiert \n VOR Block-Elementen wenn schon Content
davor — fixt nested <div>A<div>B</div></div> die sonst A+B ohne
Trenner als ein Run liefern.
- Round-Trip-Erhaltung: editor.innerHTML 1:1 als UserString
"dossier_text_html" persistiert, beim Reopen direkt gesetzt
(kein runsToHtml-Konvertieren das Zeilen verlieren kann).
- Oberleiste-Editing: in-place modify von obj.Geometry + Commit-
Changes statt Duplicate+Replace (Mac Rhino gibt False zurueck
bei RichText-Klonen). Plus _patch_rtf_b_i_ul: regex-flippt
\b/\b0, \i/\i0, \ul/\ulnone global in der RTF damit Bold/Italic/
Underline OFF in der Oberleiste auch wirklich auf DOSSIER-Texte
greift (per-Segment-Codes wuerden te.Font-Aenderung sonst
uebersteuern).
- Stil-ID am Text persistiert + von read_selection_settings
zurueckgelesen → Stil-Dropdown spiegelt Selektion.
- Editor neu: V-Align (Top/Middle/Bottom), Mask-Type (None/Viewport/
Solid) mit Farb-Picker, Case-Transform (upper/lower/capitalize/
invert), Masstaeblich-Toggle (AnnotationScalingEnabled),
Symbol-Popover, Frame-Optionen.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Neuer Pill-Button in Row 3 neben "Zur Kamera": setzt
TextEntity.AnnotationScalingEnabled. Property-Name wird in mehreren
Varianten probiert (Rhino 8 API hat das je nach Build leicht anders
benannt). Zustand wird zusaetzlich als UserString
"dossier_text_scaled" persistiert, sodass open_for_edit den Toggle
auch dann korrekt restored wenn die API-Property nicht gelesen
werden kann.
Default = an (entspricht aktuellem Verhalten: Text skaliert mit der
Annotation-Scale des Layouts).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Erweitert den Editor um die fehlenden Rhino-Text-Optionen:
- Vertical Alignment (Top/Middle/Bottom) als 3 Pill-Buttons in Row 2.
Backend: text_create._apply_valign mit TextVerticalAlignment-Enum.
- Mask-Type Dropdown (None/Viewport/Solid). Bei Solid erscheint ein
Mask-Color-Picker. MaskMargin disabled wenn Type=none.
Backend: schaltet te.MaskEnabled + te.MaskUsesViewportColor +
te.MaskColor entsprechend.
- Case-Dropdown in Row 2: upper/lower/capitalize/invert. Wirkt nur
auf die Selektion via execCommand insertText.
open_for_edit liest valign + maskType/Color/Margin aus bestehendem
TextEntity zurueck, damit beim Re-Open der Zustand erhalten bleibt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rhinos Parser collapsed mehrere \\par in Folge zu einem einzelnen
Linebreak. Loesung: jeder zusaetzliche \\par bekommt einen
{ }-Space-Group vor sich, damit der leere Paragraph "Inhalt" hat
und als echte Leerzeile gerendert wird.
Why: User-Test "TITEL\\n\\nTEXT" renderte als zwei aufeinander-
folgende Zeilen ohne Leerzeile dazwischen, obwohl die zweite \\n
korrekt in pending_pars gezaehlt wurde.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Zurueck zur Groups-Form ({...} pro Run), aber mit Two-Pass-fonttbl
das Pass 1 bereits korrekt populiert. Zwischen den Groups \\par
fuer Paragraph-Breaks — der einzige Linebreak-Token den Rhinos
TextEntity-Parser tatsaechlich rendert. \\line aus dem letzten
Versuch wurde von Rhino ignoriert.
pending_pars-Counter sammelt Newlines ueber Run-Grenzen hinweg,
sodass auch mehrere aufeinanderfolgende \\n (= Leerzeilen) erhalten
bleiben. Fuehrender \\par wird unterdrueckt (first_emitted-Flag),
damit der Text nicht mit einer Leerzeile beginnt.
Why: Per-Run-Groups isolieren Font-State (Fix fuer "letzte Schrift
dominiert"), waehrend \\par die Mehrzeiligkeit liefert die Rhino
versteht. Two-Pass garantiert dass fonttbl alle benutzten Fonts
enthaelt.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>