Wand-Grips + Schnitt-Grips + Referenz-Sublayer pro Bauteil + Print-Auto-Hide

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>
This commit is contained in:
2026-05-23 20:58:06 +02:00
parent 059cbf8d4d
commit 736325fba1
7 changed files with 1149 additions and 109 deletions
+91 -14
View File
@@ -645,16 +645,77 @@ def _layer_path_volume(doc, geschoss_name):
return _layer_path_axis(doc, geschoss_name)
def _ensure_referenz_child_in_doc(doc, parent_code, parent_keywords,
parent_default_name):
"""Stellt sicher dass unter einer Bauteil-Parent-Ebene (z.B. WAENDE,
Code 20) ein 'Referenz'-Sub-Sub-Layer existiert. Code ist
parent_code + 'r' (z.B. '20r'). Liefert (parent_sub_name, child_sub_name)
fuer Pfad-Konstruktion.
Idempotent: legt nur an wenn nicht vorhanden, triggert dann
build_layers + broadcast_state."""
parent_sub = _find_ebene_sublayer_name(
doc, parent_keywords, parent_code, parent_default_name,
default_color="#0a0a0a", default_lw=0.50)
# parent_sub = "20_Wände" oder vom User customisiert — Code aus Prefix
parent_real_code = parent_sub.split("_", 1)[0] if "_" in parent_sub else parent_code
ref_code = parent_real_code + "r"
ref_child_sub = "{}_Referenz".format(ref_code)
raw = doc.Strings.GetValue("dossier_ebenen")
try: ebenen = json.loads(raw) if raw else []
except Exception: ebenen = []
if not isinstance(ebenen, list): return parent_sub, ref_child_sub
# Parent-Eintrag im Tree finden (rekursiv falls nested)
def _find_parent(lst):
for e in lst:
if not isinstance(e, dict): continue
if e.get("code") == parent_real_code: return e
kids = e.get("children")
if isinstance(kids, list):
r = _find_parent(kids)
if r is not None: return r
return None
parent_e = _find_parent(ebenen)
if parent_e is None: return parent_sub, ref_child_sub
if not isinstance(parent_e.get("children"), list):
parent_e["children"] = []
have = any(isinstance(c, dict) and c.get("code") == ref_code
for c in parent_e["children"])
if not have:
parent_e["children"].append({
"code": ref_code, "name": "Referenz",
"color": "#a0a0a0", "lw": 0.13,
"visible": True, "locked": False,
})
try:
doc.Strings.SetString("dossier_ebenen",
json.dumps(ebenen, ensure_ascii=False))
import layer_builder
z_raw = doc.Strings.GetValue("dossier_zeichnungsebenen")
zlist = json.loads(z_raw) if z_raw else []
if zlist: layer_builder.build_layers(doc, zlist, ebenen)
import rhinopanel
rhinopanel._broadcast_state(doc)
except Exception as ex:
print("[ELEMENTE] _ensure_referenz_child:", ex)
return parent_sub, ref_child_sub
def _layer_path_referenz(doc, geschoss_name):
"""Sublayer 'Referenzlinien' (Code 19) — eigene Ebene fuer wand_axis +
oeffnung_point Source-Objekte. Getrennt vom Wand-Volumen-Layer (20)
damit der User die Konstruktions-Referenzen ein-/ausblenden kann ohne
die Volumen-Sichtbarkeit zu verlieren. Wird automatisch im Ebenen-
Panel sichtbar (auto-add via _find_ebene_sublayer_name)."""
sub = _find_ebene_sublayer_name(doc, ["referenz", "referenzlinie"],
"19", "Referenzlinien",
default_color="#a0a0a0", default_lw=0.13)
return "{}::{}".format(geschoss_name, sub)
"""Sub-Sub-Layer '<parent_code>r_Referenz' unter der WAENDE-Ebene fuer
wand_axis + oeffnung_point Source-Objekte. Bauteil-konsistent: die
Referenz-Linie gehoert konzeptuell zur Wand, also nested unter WAENDE
statt als eigene top-level Ebene wie frueher (alt: 19_Referenzlinien).
Toggle in der Oberleiste findet alle 'Referenz'-Sub-Sub-Layer ueber
alle Bauteile keyword-basiert falls Decken/Dach/Tragwerk spaeter
auch Referenz-Sublayer kriegen, wirkt der Toggle automatisch."""
parent_sub, child_sub = _ensure_referenz_child_in_doc(
doc, "20", ["wand", "wände", "waende"], "Wände")
return "{}::{}::{}".format(geschoss_name, parent_sub, child_sub)
def _layer_path_schnittlinie(doc, geschoss_name):
@@ -10367,11 +10428,11 @@ def _migrate_referenz_layer_once(doc):
Geschoss aufgerufen, was via _find_ebene_sublayer_name den Auto-Add
+ broadcast_state ausloest."""
if doc is None: return
# Sticky-Version bumped: vorherige Versionen liefen ohne proaktive
# Ebenen-Registrierung — wenn die alten Keys gesetzt sind, wuerde die
# neue Logik nie greifen. v3 = aktuelle Implementierung.
try: key = "_dossier_referenz_migration_v3_" + str(doc.RuntimeSerialNumber)
except Exception: key = "_dossier_referenz_migration_v3_default"
# v4: Referenzlinien wandern von top-level '19_Referenzlinien' auf
# Sub-Sub-Layer '<parent_code>r_Referenz' unter den jeweiligen
# Bauteil-Parent (aktuell nur WAENDE). Sticky-Bump zwingt Re-Run.
try: key = "_dossier_referenz_migration_v4_" + str(doc.RuntimeSerialNumber)
except Exception: key = "_dossier_referenz_migration_v4_default"
if sc.sticky.get(key): return
sc.sticky[key] = True
n_moved = 0
@@ -11553,6 +11614,22 @@ def _install_listeners(bridge):
schnitte.install_double_click_handler()
except Exception as ex:
print("[ELEMENTE] schnitt dblclick install:", ex)
# Wand-Endpoint-Grip-Overlay (Display-Conduit + Mouse-Handler) —
# dicke klickbare Marker an den Achs-Endpunkten, auch wenn die
# Referenzlinien-Layer ausgeblendet ist.
try:
import wand_grips
wand_grips.install_handlers()
except Exception as ex:
print("[ELEMENTE] wand_grips install:", ex)
# Schnittsymbol-Endpoint-Grips — analoges Overlay an den P1/P2 der
# Schnittlinie. Ziehen updated linePts + regeneriert das Symbol +
# re-aktiviert den Schnitt wenn aktiv.
try:
import schnitt_grips
schnitt_grips.install_handlers()
except Exception as ex:
print("[ELEMENTE] schnitt_grips install:", ex)
def _bridge_factory():