From d6c09d22f751a62098af54e5e53da9215e8f8b47 Mon Sep 17 00:00:00 2001 From: karim Date: Tue, 19 May 2026 00:54:51 +0200 Subject: [PATCH] =?UTF-8?q?Hatch=E2=86=94Curve-Sync:=20User-Transform-Skip?= =?UTF-8?q?=20war=20zu=20aggressiv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symptom: Beim Verschieben einer Polylinie ging die gekoppelte Hatch nicht mehr mit; umgekehrt zog die Hatch die Curve auch nicht mehr mit. Die bidirektionale Hatch-Curve-Kopplung war kaputt. Ursache: ich hatte vor einiger Zeit in gestaltungs on_replace, on_delete und on_add jeweils einen `_dossier_user_transform_active`- Skip eingebaut um waehrend elemente-Moves die Listener stumm zu halten (Performance). Damit wurden aber auch die Hatch-Coupling- Updates fuer normale Polylinien geblockt — die laufen ja gerade genau dann, wenn der User einen `_Move` macht. Fix: Skip-Logik selektiver machen: - on_replace: Skip ENTFERNT. Stattdessen ein frueher Bail-out wenn das Objekt weder _FILL_KEY noch _FILL_OWNER_KEY hat → Wand-Sub- Volumen werden immer noch nicht angepackt, aber Hatch-gekoppelte Polylinien laufen durch (auch waehrend _Move). - on_delete: Skip ENTFERNT. Der vorhandene Bail-out auf "kein Hatch- UserString" filtert dossier-Sub-Volumen weiterhin raus. Hatch- gekoppelte Curves machen Cascade-Delete + Pending-Save fuer die Recovery in on_add. - on_add: zwei Phasen — Drag-Recovery (Phase 1, IMMER) und Auto-Fill (Phase 2, nur ausserhalb User-Transform). So funktioniert die Delete+Add-Recovery von Rhinos Move waehrend Auto-Fill nicht versehentlich neue Hatches fuer Wand-Volumen anlegt. Co-Authored-By: Claude Opus 4.7 --- rhino/gestaltung.py | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/rhino/gestaltung.py b/rhino/gestaltung.py index 718afb3..eaf322b 100644 --- a/rhino/gestaltung.py +++ b/rhino/gestaltung.py @@ -1387,16 +1387,24 @@ 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 a = new_obj.Attributes + # Frueher Bail-out wenn das Objekt KEIN Hatch-Coupling hat — irrelevant. + # Erst NACH dieser Pruefung das user-transform-Skip, sonst wuerde die + # Hatch-Sync beim Verschieben einer normalen Polylinie nicht laufen + # (war der Fall nach dem Split: Hatch ging bei _Move nicht mehr mit). + hatch_id_str_quick = a.GetUserString(_FILL_KEY) + owner_id_str_quick = a.GetUserString(_FILL_OWNER_KEY) + if not hatch_id_str_quick and not owner_id_str_quick: + return + # Dossier-eigene Sources (wand_axis etc.) haben weder _FILL_KEY noch + # _FILL_OWNER_KEY — die werden hier oben rausgekickt. User-Transform- + # Skip war eigentlich nur fuer Dossier-Elemente gedacht; reine Hatch- + # gekoppelte Polylinien brauchen die Sync auch waehrend _Move. # Reverse-Direction: Hatch verschoben/rotiert/skaliert → Curve mitnehmen. # Wir nehmen die Outer-Boundary direkt aus der (bereits transformed) # Hatch — funktioniert fuer Move, Rotate, Scale, beliebige Transforms. @@ -1470,10 +1478,6 @@ 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 @@ -1486,7 +1490,10 @@ def _install_selection_listener(bridge): return # Schneller Bail-out: ohne Hatch-UserString interessiert uns das - # Event nicht. Vermeidet Print-Spam fuer Wand-Sub-Volumen etc. + # Event nicht. Vermeidet Print-Spam fuer Wand-Sub-Volumen etc. UND + # filtert den user-transform-Pfad: nur Hatch-gekoppelte Objekte + # brauchen die Sync (= Cascade-Delete + pending-Save fuer Recovery). + # Wand-Volumen werden nicht beruehrt. try: hatch_id_str = attrs.GetUserString(_FILL_KEY) except Exception: @@ -1551,10 +1558,6 @@ 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 @@ -1565,6 +1568,9 @@ def _install_selection_listener(bridge): doc = Rhino.RhinoDoc.ActiveDoc # 1) Drag-Recovery: Hatch-Metadaten wurden gerade in on_delete gespeichert? + # MUSS auch waehrend User-Transform laufen (= das ist gerade DER Fall: + # Rhino's Move feuert Delete+Add, on_delete hat pending gespeichert, + # jetzt muss on_add die Hatch wiederherstellen). pending = _take_pending_hatch(obj.Id) if pending is not None: try: @@ -1580,7 +1586,10 @@ def _install_selection_listener(bridge): except Exception: pass return - # 2) Auto-Fill aus Ebenen-Definition + # 2) Auto-Fill aus Ebenen-Definition — nur ausserhalb User-Transform. + # Waehrend Move/Rotate werden Sub-Volumen erzeugt die kein Auto-Fill + # brauchen, und elemente uebernimmt die Coupling. + if sc.sticky.get("_dossier_user_transform_active"): return try: ok = _apply_ebene_fill(doc, obj) except Exception as ex: