Snapshot: Transform-Hierarchie + Brüstung-Konvention + Undo-Record
Funktionierender Stand der Move/Rotate-Pipeline mit Eltern-Kind-Cascade und sauberer Brüstung-Semantik: - Pure-Translate hierarchisch: nur Sources mit echtem Delta + ihre Kinder (Öffnungen → Wand) folgen mit. Wand folgt NICHT der Öffnung. - Orphan-Detection: Öffnung ohne mitbewegter Eltern-Wand → Regen-Fallback (sonst bleibt Cutout am alten Ort im Wand-Brep). - Brüstung = relativ zur Wand-UK (Archicad/Revit-Konvention). Bei Wand- Z-Drag wird UK_OVER angepasst, Brüstung bleibt; Öffnungs-Punkt wandert via Snapshot+Delta mit. Keine Doppel-Addition mehr. - Opening-Punkt wird beim Erzeugen direkt auf UK+brüstung platziert (sonst Brüstung-Drop beim ersten Move). - Undo-Record umschliesst Rhinos Move + unseren Regen in einem Cmd+Z- Schritt → keine doppelten Elemente nach Undo. - RedrawEnabled-Suppression event-getriggert (erst beim ersten Replace- Event nach User-Klick) → Rubber-Band + Drag-Vorschau bleiben sichtbar. - _Undo/_Redo: Event-Handler komplett aussetzen → kein Regen-Storm. - Gestaltung-Listener während User-Transform + Regen stumm, danach einmaliger Selection-Refresh. Enthält Debug-Logs in _apply_wand_z_drag_constraint + Wand-Regen für offenen Bug: bei gemeinsamer Z-Verschiebung (Wand+Fenster+Tür) landen Öffnungen manchmal über der Wand — UK_OVER scheint nicht durchzukommen. Logs sollen das eingrenzen. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+38
-19
@@ -1368,6 +1368,13 @@ def _install_selection_listener(bridge):
|
||||
return
|
||||
|
||||
def refresh(*args):
|
||||
# Waehrend Move/Rotate/Mirror/Scale schweigen — Rhino oszilliert die
|
||||
# Selection pro transformiertem Object mehrfach (deselect→delete→add→
|
||||
# reselect). Bei 7 Objekten sind das ~100 IPC-Sends in den WebView,
|
||||
# was sich als „Regen" anfuehlt. elemente._on_command_end refresht
|
||||
# nach dem Command einmalig.
|
||||
if sc.sticky.get("_dossier_user_transform_active"): return
|
||||
if sc.sticky.get("_dossier_undo_active"): return
|
||||
b = sc.sticky.get("gestaltung_bridge")
|
||||
if b is not None:
|
||||
try: b._send_selection()
|
||||
@@ -1380,6 +1387,12 @@ def _install_selection_listener(bridge):
|
||||
- Hatch hat _FILL_OWNER_KEY (= curve_id) → Curve um den gleichen
|
||||
Vektor mit-translaten (User hat Hatch alleine verschoben).
|
||||
"""
|
||||
# Waehrend User-Transform-Command: elemente uebernimmt die Geometrie-
|
||||
# Synchronisation. Hatch-Re-Create laeuft hier sowieso ins Leere weil
|
||||
# Rhino bei Move Delete+Add statt Replace feuert.
|
||||
if sc.sticky.get("_dossier_user_transform_active"): return
|
||||
if sc.sticky.get("_dossier_undo_active"): return
|
||||
if sc.sticky.get("_elemente_regen_busy"): return
|
||||
new_obj = args.NewRhinoObject
|
||||
if new_obj is None or new_obj.Id in _processing:
|
||||
return
|
||||
@@ -1457,11 +1470,13 @@ def _install_selection_listener(bridge):
|
||||
"""Wenn eine Curve geloescht wird, ihre gekoppelte Hatch mitloeschen.
|
||||
Wenn umgekehrt eine Hatch direkt geloescht wird, den Verweis auf der
|
||||
Curve aufraeumen damit beim naechsten Toggle keine Geister-Referenz steht."""
|
||||
# Waehrend User-Transform-Command: Rhino feuert Delete+Add fuer
|
||||
# transformierte Objekte. Curve→Hatch-Cascade hier wuerde die Hatch
|
||||
# killen obwohl sie gleich wieder benoetigt wird.
|
||||
if sc.sticky.get("_dossier_user_transform_active"): return
|
||||
if sc.sticky.get("_dossier_undo_active"): return
|
||||
if sc.sticky.get("_elemente_regen_busy"): return
|
||||
obj = args.TheObject
|
||||
try:
|
||||
print("[GESTALTUNG] on_delete fired id={}".format(obj.Id if obj else None))
|
||||
except Exception:
|
||||
pass
|
||||
if obj is None or obj.Id in _processing:
|
||||
return
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
@@ -1470,16 +1485,24 @@ def _install_selection_listener(bridge):
|
||||
except Exception:
|
||||
return
|
||||
|
||||
# Pfad A: geloeschte Curve hatte eine Hatch -> Hatch mitloeschen
|
||||
# Schneller Bail-out: ohne Hatch-UserString interessiert uns das
|
||||
# Event nicht. Vermeidet Print-Spam fuer Wand-Sub-Volumen etc.
|
||||
try:
|
||||
hatch_id_str = attrs.GetUserString(_FILL_KEY)
|
||||
except Exception:
|
||||
hatch_id_str = None
|
||||
# Fallback: Mapping in sc.sticky (UserStrings koennen nach Delete leer sein)
|
||||
if not hatch_id_str:
|
||||
try:
|
||||
owner_id_str = attrs.GetUserString(_FILL_OWNER_KEY)
|
||||
except Exception:
|
||||
owner_id_str = None
|
||||
if not hatch_id_str and not owner_id_str:
|
||||
# UserStrings koennen nach Delete leer sein → Sticky-Fallback.
|
||||
hatch_id_str = _lookup_hatch_for_curve(obj.Id)
|
||||
if hatch_id_str:
|
||||
print("[GESTALTUNG] on_delete: hatch via sticky map gefunden")
|
||||
if not hatch_id_str:
|
||||
return
|
||||
print("[GESTALTUNG] on_delete: hatch via sticky map gefunden")
|
||||
|
||||
# Pfad A: geloeschte Curve hatte eine Hatch -> Hatch mitloeschen
|
||||
if hatch_id_str:
|
||||
try:
|
||||
hatch_id = System.Guid(hatch_id_str)
|
||||
@@ -1504,10 +1527,6 @@ def _install_selection_listener(bridge):
|
||||
return # Curve-Fall fertig
|
||||
|
||||
# Pfad B: geloeschte Hatch hatte einen Owner-Verweis -> Curve aufraeumen
|
||||
try:
|
||||
owner_id_str = attrs.GetUserString(_FILL_OWNER_KEY)
|
||||
except Exception:
|
||||
owner_id_str = None
|
||||
if owner_id_str:
|
||||
try:
|
||||
owner_id = System.Guid(owner_id_str)
|
||||
@@ -1532,16 +1551,17 @@ def _install_selection_listener(bridge):
|
||||
- Wenn das Objekt eben gerade als Teil eines Drag/Move geloescht wurde,
|
||||
stellen wir die Hatch mit den gemerkten Metadaten wieder her.
|
||||
- Sonst pruefen wir ob die Ebene ein Auto-Fill konfiguriert hat."""
|
||||
# Waehrend User-Transform-Command: elemente uebernimmt Geometrie-Sync.
|
||||
# Auto-Fill hier wuerde unnoetige Hatches erzeugen weil das Objekt
|
||||
# bereits eine geerbte Fill-UserString hat (vom Delete+Add im Move).
|
||||
if sc.sticky.get("_dossier_user_transform_active"): return
|
||||
if sc.sticky.get("_dossier_undo_active"): return
|
||||
if sc.sticky.get("_elemente_regen_busy"): return
|
||||
obj = args.TheObject
|
||||
if obj is None:
|
||||
return
|
||||
try:
|
||||
geom_kind = type(obj.Geometry).__name__
|
||||
except Exception:
|
||||
geom_kind = "?"
|
||||
if obj.Id in _processing:
|
||||
return
|
||||
print("[GESTALTUNG] on_add: id={} type={}".format(obj.Id, geom_kind))
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
|
||||
# 1) Drag-Recovery: Hatch-Metadaten wurden gerade in on_delete gespeichert?
|
||||
@@ -1566,7 +1586,6 @@ def _install_selection_listener(bridge):
|
||||
except Exception as ex:
|
||||
print("[GESTALTUNG] on_add Exception:", ex)
|
||||
return
|
||||
print("[GESTALTUNG] on_add ok={}".format(ok))
|
||||
if ok:
|
||||
b = sc.sticky.get("gestaltung_bridge")
|
||||
if b is not None:
|
||||
|
||||
Reference in New Issue
Block a user