Commit Graph

162 Commits

Author SHA1 Message Date
karim e406e8d9b2 L-Aussen-Polygon: Z-Konsistenz fix + try/except defensive
_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.
2026-05-28 02:40:09 +02:00
karim bb64e4d41e Lock: Targets clearen bei Disable + Wendel-Sweep clamp auf 2π 2026-05-28 02:19:22 +02:00
karim 6060c74b17 Treppe L+Wendel: Lauflinie, Aussen-Polygon, Pfeil-voll, Lock + Cmd+Z
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
2026-05-28 02:16:57 +02:00
karim 970281e10a Treppen UX-Polish: Start-Z, Trittmass-Lock, Pfeil-Stile, Grips
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)
2026-05-28 02:09:38 +02:00
karim bcf7d557b1 Treppen 2D-Plansymbol + Pure-Transform fuer hidden Layer
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
2026-05-28 00:41:05 +02:00
karim d9589e99f5 Cleanup: gitignore Rhino-Testdateien, Layout-Skip-Versuch zurueck (WebView-Bug) 2026-05-27 20:56:22 +02:00
karim f8d1cfe3fe Splash: borderless+transparent+launcher-dedup, idle-dispatch hide 2026-05-27 20:09:09 +02:00
karim 264327432d Splash + Layout-Skip-Revert
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
2026-05-27 19:36:09 +02:00
karim 6a13ede6b7 Startup-Splash (petrol-gruen) waehrend Plugin-Init
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.
2026-05-27 19:31:00 +02:00
karim edaf83229b Startup-Performance: WindowLayout-Apply skippen bei Quick-Restart
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.
2026-05-27 19:24:02 +02:00
karim 6fee7bd143 Startup-Diagnose: Imports + Display-Modes + Window-Layout messen
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.
2026-05-27 19:08:02 +02:00
karim 61923e1b2b SIA-Bilanz CSV-Export aus Elemente-Uebersicht
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
2026-05-27 01:13:08 +02:00
karim e2d66a5d64 Personen-Belegung pro Raum + Stempel-Aggregation
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)
2026-05-27 01:08:58 +02:00
karim 1c3b0f3919 Ebenen: Plangrafik 60 → 80, RAEUME/GF/AGF auf 60/61/62
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).
2026-05-27 01:00:33 +02:00
karim 2a838aee93 Stempelstile-Tab in ProjectSettings (Manager analog Raumstile)
Beide Stempel-Familien (Raum-Stempel + Bilanz-Stempel) sind jetzt
konsistent verwaltbar in den Project-Settings.

Backend:
- elemente.py: DUPLICATE_STEMPEL_STIL + REORDER_STEMPEL_STILE handlers
- rhinopanel.py ProjectSettings-Bridge:
  - stempel_stile in params (initial)
  - 4 neue dispatch-Handler (SAVE/DELETE/DUPLICATE/REORDER_STEMPEL_STIL)
  - direkter Dispatch zu elemente.load/save_stempel_stile (kein Roundtrip)
  - STEMPEL_STILE_UPDATED-Message nach jeder Op

Frontend:
- rhinoBridge.js: reorderStempelStile + duplicateStempelStil exports
- ProjectSettingsDialog:
  - Neuer Tab "Stempelstile" parallel zu "Raumstile"
  - Drag-Reorder via HTML5 native, Inline-Rename, Duplicate, Delete
  - Mini-Preview: Header + Anzahl aktiver Show-Flags
  - Name wird mit Font/Bold/Italic des Stils gerendert als Vorschau
2026-05-27 00:51:57 +02:00
karim f457db93e7 Raum-Properties: SIA-Dropdown-Labels kompakter (GF Geschossflaeche statt GF — Geschossflaeche (gross …)) 2026-05-27 00:46:21 +02:00
karim 975071c995 Stempel: Stile + Layout-Customisation (Header, Show-Toggles)
Bilanz-Stempel hat jetzt:
- Custom Header (Default "Nutzflächen", frei editierbar)
- Per-Stempel Show-Toggles: scope, hnf, nnf, nf, vf, ff, ngf, gf, agf,
  count (Anzahl Raeume), separators (Trennlinien)
- Stempel-Stile (Presets) per Doc — separate Storage von Raumstempel-
  Stilen (dossier_stempel_stile)

Backend (elemente.py):
- UserStrings: dossier_stempel_header + dossier_stempel_show_* (11 Flags)
  + dossier_stempel_stil_id
- _make_stempel_text erweitert: header_text, show_scope, show_separators,
  show_count, visibility-dict
- _format_bilanz_lines: visibility + show_separators + show_count Args
- load_stempel_stile / save_stempel_stile (eigener doc.Strings-Key)
- Bridge-Handler SAVE/DELETE/APPLY_STEMPEL_STIL (analog Raum-Stil-Pattern,
  inkl. Bulk-Apply via Selection + applyToIds-Mechanism wie SaveRaum-Stil)
- _sync_stempel_to_source spiegelt Font/Bold/Italic/TextHeight vom Live-
  TextEntity zur UserString-Storage (Oberleiste-Edits ueberleben Regen)
- _update_wall stempel-Branch um header + show-Flags erweitert

Frontend (StempelProperties):
- Stil-Picker-Dropdown analog Raum (Stil wählen / + speichern / 🗑 löschen)
- Header-Input (Inline-Text)
- 11 Show-Toggles als kompakte Grid (Check-Box-Stil)
- Live-Vorschau respektiert Visibility-Flags + bilanz.count
2026-05-27 00:36:31 +02:00
karim ac7b2f2ee5 GF/AGF-Outlines: eigene Layer + minimaler Stempel/UI
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"
2026-05-27 00:24:57 +02:00
karim 2386366566 Stempel-Element: SIA-Bilanz als platzierbares Viewport-Objekt
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.
2026-05-27 00:10:02 +02:00
karim 7fbda8c289 Stempel-Stil-Bulk-Apply + Manager-Tab in ProjectSettings
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
2026-05-26 23:47:38 +02:00
karim f208e7fc00 Raumstempel-Panel: Hoehe + Ausrichtung weg, kommt aus Oberleiste
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
2026-05-26 23:33:38 +02:00
karim 662ce87e98 Elemente: 'Karte'-Button raus, Map-Icon auf Swisstopo uebertragen 2026-05-26 23:30:25 +02:00
karim 46970fd4f0 Raumstempel: Hoehe-Zeile kompakter — Modus als Dropdown statt Toggle-Pair 2026-05-26 23:28:49 +02:00
karim eff0878f53 Raumstempel-Stile: Active-Stil-Tracking
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).
2026-05-26 23:23:42 +02:00
karim 3c28d2e29c Elemente-Uebersicht: SIA-416 Bilanz pro Geschoss
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
2026-05-26 23:20:00 +02:00
karim f1860ae85d Raumstempel-Stile (Presets) — speichern + anwenden per Doc
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
2026-05-26 23:17:08 +02:00
karim da0fd365f2 Arbeitseinheit als Project-Setting + Doc-Open-Check
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).
2026-05-26 23:11:36 +02:00
karim cd626b0707 Revert "Unit-aware: m → Doc-Units im Regen-Pfad"
This reverts commit dd5ccec881.
2026-05-26 22:06:27 +02:00
karim dd5ccec881 Unit-aware: m → Doc-Units im Regen-Pfad
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
2026-05-26 21:59:33 +02:00
karim 95678d4394 Massstab: Diagnose-Prints raus (Magnify funktioniert wie erwartet) 2026-05-26 21:38:16 +02:00
karim 067cb56584 Massstab: Diagnose-Prints fuer _apply_scale (pre/post Magnify) 2026-05-26 21:24:21 +02:00
karim b10760a704 Masstab: Persist vor Raum-Regen — sonst regent mit alter Skala
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.
2026-05-26 21:10:28 +02:00
karim e56ee2cb8f Raumstempel: Auto-Konvertierung beim Modus-Wechsel + Font-API-Fix
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.
2026-05-26 20:58:28 +02:00
karim 238d7d062b Raumstempel: masstab-Modus — Texthoehe als Paper-mm @ Plan-Massstab
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.
2026-05-26 20:49:26 +02:00
karim 01b6501a0c Raumstempel: Drag-Drop-Layout + persistente Position + Oberleiste-Sync
Datenmodell auf der raum_outline:
- dossier_raum_stamp_dx/dy: Stempel-Offset zum Outline-Centroid
- dossier_raum_layout: JSON-Array of-Rows fuer Multi-Field-pro-Zeile
- dossier_raum_txt_font/bold/italic + raum_show_*: Typografie-Overrides
- Legacy show_*-Flags bleiben Fallback wenn kein Layout gesetzt

Backend:
- _make_raum_stamp_text: Layout-Renderer (Rows zu Lines), Offset wird in
  _regenerate_element_body auf den Centroid addiert
- _sync_raum_stamps_to_source: laeuft am Anfang von _on_command_end,
  spiegelt aktuelle Stempel-Position + Font/Size/Style auf die Source
  zurueck → User-Edits via Move/Oberleiste/Properties ueberleben Regen
- _list_system_fonts: System-Fonts fuer Frontend-Dropdown
- Raumstempel-Bug-Fix: raum_outline jetzt in _on_command_end Regen-Pfad
  per Length-Check getriggert, snapshot um length erweitert. Vertex-Drag
  aktualisiert Flaechen-Wert. Outline-Sources fuegen affected_walls hinzu.
- raum_stamp aus _PAIRED_VOLUME_TYPES entfernt → einzeln greifbar/
  verschiebbar; Klick auf Outline pairt weiter alles drei.

Frontend (ElementeApp.jsx):
- StempelLayoutBuilder: HTML5 drag-and-drop UI, verfuegbare Felder als
  Pills oben (Klick = neue Row, Drag = in bestehende Row), bestehende
  Rows als drop-targets, × zum Entfernen
- Typografie-Block raus aus RaumProperties; Hinweis-Text auf Oberleiste
- PropertiesView nimmt jetzt fonts={state.fonts} (auch Satellite-Window)
2026-05-26 20:42:32 +02:00
karim 02a00a9b4a Display-Modes: 3D-Template + Auto-Assign + Material/Raytracing-Slots
- 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)
2026-05-26 18:26:49 +02:00
karim 13a5e1eb7a AGPL-3.0 Dual-Lizenz + Pill-Stil-UI + Section-Style-Overhaul + Plan-Mode-Template
Lizenz:
- AGPL-3.0 LICENSE-File im Repo-Root (GNU Volltext)
- SPDX-Header + Copyright in allen Source-Files (Python/JSX/JS/Rust)
- license-Feld in package.json + Cargo.toml
- About-App komplett neu: Dual-Lizenz-Block (AGPL + Commercial),
  openbureau-Branding, Version-Pills, made-in-Switzerland-Footer

UI-Restyle (3 Wellen) — alle Dialoge + Satellites + Panel-Sidebars
auf gemeinsamen Pill-Stil aus BarControls (BarToggle/BarButton/BarCombo):
- Welle 1: GeschossDialog/Settings, AusschnittSettings, LayoutDialog
- Welle 2: ConfirmDeleteEbene, Kamera, MasseSettings, Osm, Swisstopo,
  TextEditor, AusschnittLayerDialog, LayerCombinations
- Welle 3: LayoutsApp, MassstabApp, WerkzeugeApp, OverridesApp,
  ZeichnungsebenenApp; Werkzeuge mit ElementeApp-PillGroup-Layout

GeschossDialog Header-Refactor: +Geschoss/+Zeichnung in Toolbar oben,
move-Pfeile-Spalte breiter (kein Overlap mit G-Haken)

Ausschnitte Rows als Pills, kein Outer-Border ums Suchfeld

Section-Style komplett neu (gestaltung.py + GestaltungApp.jsx):
- ObjectSectionAttributesSource.FromObject (richtiger Enum-Name fuer Mac)
- HatchPatternPrintColor + BoundaryPrintColor mit-setzen (Display = Print)
- BoundaryColor nur bei explizitem User-Override, sonst Rhino-Default
- background_color_hex Parameter (BackgroundFillMode=SolidColor)
- Readback aus GetCustomSectionStyle statt direkt aus Attributes
- UI: Schnittkante > Section Style > Solid-Fill mit proper SectionHead
- 'Boundary' (3D Pen) -> 'Background' weil sich's wie Section-Hintergrund verhaelt

Plan-Mode 'Dossier Plan' via Template:
- rhino/templates/dossier_plan.ini wird direkt geladen
- Fallback auf Technical-Clone + ini-Patch wenn Template fehlt
- Auto-Cleanup von Orphan-Modes vor Import (Name- oder Guid-Match)
- ClipSectionUsage=1 + TechnicalMask=15 als bekannte Soll-Werte
- Bei Template-Pfad keine ini-Patches (1:1 wie User exportiert)
- Sanity-Print listet alle registrierten Modes nach Anlegen

Bridge-Unification: 4 Settings-Apps (Ebenen/Project/Geschoss*Dialog)
benutzen jetzt chunkende send() statt eigene bridgeSend ohne Chunk-
Logik -> grosse Payloads (Hatch-Refs etc.) kommen nicht mehr truncated
bei Python an (loeste 'JSON-Fehler char 990'-Regression in Ebenen-
Settings)

Library-Imports robust: 'import library' jetzt Top-Level in elemente.py
+ rhinopanel.py (statt Lazy in Methoden) -> 'No module named library'-
Crashes weg auch wenn sys.path zwischendurch resettet wird

Tools fuer Display-Mode-Maintenance:
- _clean_display_modes.py (loescht alle Custom-Modes, Built-ins bleiben)
- _inspect_plan_mode.py / _inspect_obj_section.py / _inspect_obj_boundary.py
  (Diagnose-Skripte fuer SectionStyle-Property-Reverse-Engineering)
- _reset_rhino_settings.sh (Backup + Nuke der Rhino-Settings als
  letzte Bastion gegen korrupte Display-Modes)
2026-05-26 17:09:18 +02:00
karim e1b63aa4e6 Library-Thumbnails: Auto-Capture + Base64-Preview in der UI
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>
2026-05-25 19:07:33 +02:00
karim 827bd8d4d7 Library: Format-Konvertierung beim Import (.dwg/.obj/.fbx/.dae/.stl/...)
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>
2026-05-25 18:45:58 +02:00
karim 68b9d14453 Auswahl als Library-Item speichern (Step 3)
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>
2026-05-25 04:05:48 +02:00
karim c993935b17 Symbole-Tab in Project-Settings (Library-Item-Management)
Project-Settings hat jetzt 5 Tabs. Neuer 'Symbole'-Tab managt die
Dossier-Library: List/Detail wie Materialien, mit 2D + 3D Slot pro Item.

Backend (library.py):
- save_manifest, update_item, delete_item, add_item — full CRUD aufs
  library.json
- copy_to_assets: kopiert User-Dateien in library/assets/ mit
  Konflikt-Resolution (auto-suffix)

Backend (rhinopanel.py / ProjectSettingsBridge):
- _send_library: aktuelle Items + libraryRoot an Frontend
- _add_library_file: File-Picker (.3dm direkt; .dwg/.obj/etc. zeigt
  Hinweis fuer kuenftige Konvertierung), kopiert + appended ans Item
  (variant 2d/3d) oder erstellt neues Item
- _update_library_item: patch by id
- _delete_library_item: entfernt Eintrag aus Manifest
- LIBRARY_ITEMS + LIBRARY_ERROR Messages ans Frontend

Frontend:
- Neuer 'Symbole'-Tab mit List/Detail
- Liste: Name, Type-Icon, '2D'/'3D' Status-Badge
- Detail rechts: Name-Edit (live persist on blur), Type-Toggle
  (Symbol/Objekt), 2D/3D-File-Slots mit Datei-Picker, Tags-Editor
- 'Neues Objekt' Button im Listen-Footer

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:58:41 +02:00
karim de57c320c2 Symbol-Picker als Satellite-Fenster (statt Modal im Elemente-Panel)
UX-Verbesserung: Modal-Overlay im engen Elemente-Panel war unpraktisch.
Symbol-Picker oeffnet sich jetzt als eigenstaendiges Eto.Form-Fenster
(wie Library/Project-Settings).

Frontend:
- SymbolPicker bekommt embedded-Prop (Satellite-Mount vs Modal-Overlay)
- Neuer SymbolPickerApp Satellite-Wrapper (PANEL_PARAMS lesen + Bridge)
- main.jsx: 'symbol_picker' Mode-Routing
- ElementeApp: Symbol-Button ruft nur noch listLibrary() — Backend
  oeffnet das Fenster

Backend:
- _cmd_list_library oeffnet jetzt das Satellite-Window mit eigener
  Bridge (PICK -> CREATE_SYMBOL, CANCEL -> Close)
- PICK schliesst Fenster + triggert interactive GetPoint im Viewport

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:39:03 +02:00
karim 8184f559fc Symbol-Funktion in Elemente-Panel (Phase S1+S2)
Schema (library.py):
- Item-Format erweitert: files2d + files3d (Backwards-compat zu 'files')
- _build_variant_block + _place_instance + Layer-Routing pro Variante
- import_item akzeptiert at_point + layer2d/layer3d
- _ensure_block_definition mit variant-Suffix (dossier_lib_<id>_2d/_3d)

Backend (elemente.py):
- _layer_path_symbole(geschoss_name, variant) → <geschoss>::40_SYMBOLE::
  SYMBOLE_2D bzw. SYMBOLE_3D
- Default-Ebene 40 SYMBOLE via _find_ebene_sublayer_name
- LIST_LIBRARY-Handler: sendet Library-Manifest als LIBRARY_LIST
- CREATE_SYMBOL-Handler: interactive GetPoint im aktiven Viewport,
  laedt Block-Def + platziert Instanz(en) auf den richtigen Ebenen
- Pair-Items (2D+3D) werden an gleichem Punkt beidseitig platziert →
  Top zeigt 2D-Layer, Persp zeigt 3D-Layer wenn User entsprechend
  Sichtbarkeit setzt

Frontend:
- SymbolPicker Modal-Component: Grid mit Symbol/Object-Cards, Search,
  Type-Filter (Alle/Symbole/Objekte), Doppelklick = Pick
- Symbol-Button in ElementeApp (PillGroup "Library") oeffnet Modal +
  triggert listLibrary() fuer aktuelle Items
- createSymbol(id) → Backend → GetPoint → Place

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:31:54 +02:00
karim 8f691e37c4 Projektdaten in Project-Settings + Swisstopo-Adress-Prefill
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>
2026-05-25 03:21:19 +02:00
karim a597b58c93 Linientypen + Schraffuren-Tabs in Project-Settings + Datei-Import
Project-Settings hat jetzt 4 Tabs:
- Voreinstellungen (kompakte InlineNumberField, gruppiert in Sections)
- Materialien (List/Detail, ohne Hatch)
- Linientypen (List/Detail mit SVG-Strich-Vorschau)
- Schraffuren (List/Detail mit echtem HatchLine-Renderer)

Backend (rhinopanel.py):
- _list_linetypes_full liefert Segmente {length, type: Line/Space/Dot}
  (Mac Rhino 8 GetSegment returnt (length, isLine: bool))
- _list_hatch_patterns_full liefert HatchLines mit angle/base/offset/dashes
  (hl.Dashes optional ueber 3 API-Variants)
- CRUD: RENAME / DELETE / LOAD_DEFAULTS
- File-Import: IMPORT_LINETYPE_FILE (.lin), IMPORT_HATCH_FILE (.pat)
  via Eto.OpenFileDialog → Linetypes.Load / HatchPatterns.LoadFromFile

Frontend (ProjectSettingsDialog.jsx):
- LinetypePreview: SVG mit tile-fenster (4 Repetitions), Line als <line>,
  Dot als <circle>, currentColor fuer Renderer-Robustheit
- HatchPreview: rendert pro HatchLine alle parallelen Linien mit Angle,
  Offset (Spacing + Stagger), Dashes als stroke-dasharray
- TABLES_UPDATED Message vom Backend re-rendert Listen
- Import-Pills im List-Footer

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 03:14:28 +02:00
karim 8d3b3af882 Material/Ebene-Separation: Hatch raus aus Material (Refactor a)
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>
2026-05-25 02:10:04 +02:00
karim ad56d9e930 Fix Texture-Picker: Custom-Bridge fuer Project-Settings Satellite
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>
2026-05-24 18:14:13 +02:00
karim c63bdd5bc1 Material Phase B Stufe 4: PBR auf Rhino-Material applied + Auto-Regen
_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>
2026-05-24 17:33:38 +02:00
karim 0bf891641f Project-Settings Material-List/Detail + PBR-UI (Phase B Stufe 2+3)
UI:
- Voreinstellungen: InlineNumberField (Label links, schmaler Number-Input
  rechts mit Einheit-Suffix) statt full-width pills
- Materialien-Tab: List/Detail-Layout im ArchiCAD-Stil
  - Links (240px): suchbare Material-Liste mit Color-Swatch + Source-Badge
  - Rechts: Detail-Panel mit collapsible Sections
- Slider-Component (Pill-Track mit accent-fill + Wert rechts)
- TextureSlot-Component (Preview-Tile + Filename + Picker- + Clear-Button)

Schema:
- Material erweitert: roughness, reflection, transparency, iorN, uvScaleM
- Texturen: diffuse / bump / roughness / transparency (je {path, ...})
- _normalize_material clamped 0..1, IoR 1.0..2.5

Backend:
- Neuer Handler PICK_TEXTURE_FILE oeffnet Eto.OpenFileDialog fuer
  .jpg/.png/.tif/.bmp/.tga, antwortet mit TEXTURE_PICKED {slot, path}

Hinweis: PBR-Werte + Texturen landen aktuell nur in doc.Strings —
Anwendung auf Rhinos Render-Material kommt in Stufe 4.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 17:11:23 +02:00
karim f760d1c54b Library Phase A.2 (Symbol/Object-Import) + Oberleiste-Pill-Restyle
Library Phase A.2:
- import_symbol/import_object via File3dm.Read + InstanceDefinitions.Add
- Stabile Block-Namen 'dossier_lib_<libraryId>' fuer Dedupe
- Seed-Manifest erweitert um Nordpfeil (symbol) + Laubbaum (object)
- ItemCard rendert type-spezifische Preview (Color-Swatch fuer material,
  Material-Icon fuer symbol/object)

Oberleiste-Pill-Restyle:
- OberleisteApp: Version unter DOSSIER-Logo, Settings-Icons vertikal
  gestapelt
- ProjectSettingsDialog: Pill-Tabs, BarToggle-Footer, MaterialRow mit
  Hover-Highlight, Header entfernt (Eto.Form hat eigenen)
- LibraryBrowser: BarButton-Reload, Pill-Typ-Filter, MaterialCard mit
  BarToggle-Pill, Header entfernt
- Globaler select-Stil: bg-input statt bg-item (dunkler im Dark-Mode,
  konsistent zu Oberleiste-BarCombo)

Routing:
- OberleisteBridge delegiert OPEN_PROJECT_SETTINGS + OPEN_LIBRARY an
  EbenenBridge (sticky ebenen_bridge_ref) — vorher kamen die Messages an
  der falschen Bridge an und wurden verschluckt

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 16:57:42 +02:00