Wand-Chain Cmd+Z-Fix: ReplaceObjectProxy als Grip-Drag-Cmd registrieren
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>
This commit is contained in:
+37
-11
@@ -9874,6 +9874,28 @@ def _apply_wand_z_drag_constraint(new_obj, meta):
|
||||
z1 = geom.PointAtEnd.Z
|
||||
if abs(z0) < 1e-6 and abs(z1) < 1e-6:
|
||||
return False
|
||||
# In Top-View kann ein Z != 0 NICHT vom User stammen (er kann in Top gar
|
||||
# nicht Z-draggen). Es ist ein Artefakt — typischerweise CommitChanges
|
||||
# auf GripsOn oder Replace mit veraltetem Display-Z. Wand-Axis flatten
|
||||
# ohne uk/ok zu modifizieren.
|
||||
if _is_active_view_top_like():
|
||||
print("[ELEMENTE] wand z-drag IGNORED (top view, z0={:.3f} z1={:.3f}) "
|
||||
"— axis auf Z=0 flatten".format(z0, z1))
|
||||
try:
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
if doc is None: return False
|
||||
if isinstance(geom, rg.LineCurve):
|
||||
line = geom.Line
|
||||
flat = rg.LineCurve(
|
||||
rg.Point3d(line.From.X, line.From.Y, 0.0),
|
||||
rg.Point3d(line.To.X, line.To.Y, 0.0))
|
||||
else:
|
||||
flat = geom.DuplicateCurve()
|
||||
flat.Translate(rg.Vector3d(0, 0, -(z0 if abs(z0) > abs(z1) else z1)))
|
||||
doc.Objects.Replace(new_obj.Id, flat)
|
||||
except Exception as ex:
|
||||
print("[ELEMENTE] flatten in top-view:", ex)
|
||||
return False
|
||||
delta = z1 if abs(z1) > abs(z0) else z0
|
||||
print("[ELEMENTE] wand z-drag triggered: z0={:.3f} z1={:.3f} delta={:.3f}".format(z0, z1, delta))
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
@@ -10291,31 +10313,26 @@ def _on_object_replaced_body(sender, e):
|
||||
if meta.get("type") == "wand_axis":
|
||||
old_geom = e.OldRhinoObject.Geometry if e.OldRhinoObject else None
|
||||
new_geom = e.NewRhinoObject.Geometry if e.NewRhinoObject else None
|
||||
# No-Op-Check: GripsOn/CommitChanges feuert Replace ohne Geometrie-
|
||||
# Aenderung. Triggert sonst eine ganze Regen-Kette die in eigenem
|
||||
# Undo-Record laeuft (Cmd+Z muesste mehrfach gedrueckt werden).
|
||||
# Endpunkt-Vergleich reicht fuer LineCurves; Polylines/Splines
|
||||
# checken zusaetzlich Mid-Punkt + Laenge.
|
||||
# No-Op-Check: GripsOn/CommitChanges feuert Replace ohne sinnvolle
|
||||
# Geometrie-Aenderung. Toleranz auf 1mm (Architektur-Praezision).
|
||||
unchanged = False
|
||||
try:
|
||||
if (isinstance(old_geom, rg.Curve)
|
||||
and isinstance(new_geom, rg.Curve)):
|
||||
p_o_s, p_o_e = old_geom.PointAtStart, old_geom.PointAtEnd
|
||||
p_n_s, p_n_e = new_geom.PointAtStart, new_geom.PointAtEnd
|
||||
tol = 1e-6
|
||||
if (p_o_s.DistanceTo(p_n_s) < tol
|
||||
and p_o_e.DistanceTo(p_n_e) < tol):
|
||||
# Endpunkte gleich — Polyline/Spline-Mitten checken
|
||||
tol_xy = 0.001 # 1 mm
|
||||
if (p_o_s.DistanceTo(p_n_s) < tol_xy
|
||||
and p_o_e.DistanceTo(p_n_e) < tol_xy):
|
||||
try:
|
||||
l_o = old_geom.GetLength()
|
||||
l_n = new_geom.GetLength()
|
||||
if abs(l_o - l_n) < tol * 100:
|
||||
if abs(l_o - l_n) < tol_xy:
|
||||
unchanged = True
|
||||
except Exception:
|
||||
unchanged = True
|
||||
except Exception: unchanged = False
|
||||
if unchanged:
|
||||
# Nur Grips-Toggle / Attribut-Aenderung — kein Regen noetig.
|
||||
return
|
||||
# Joint-Cache invalidieren — Wand hat sich geaendert
|
||||
_invalidate_joints_cache(meta.get("geschoss"))
|
||||
@@ -11221,6 +11238,10 @@ def _on_idle_selection(sender, e):
|
||||
_USER_TRANSFORM_CMDS = frozenset((
|
||||
"Move", "Rotate", "Rotate3D", "Mirror", "Scale", "Scale1D", "Scale2D",
|
||||
"Drag", "Gumball", "Orient", "Orient3Pt", "RemapCPlane", "Transform",
|
||||
# Mac Rhino feuert "ReplaceObjectProxy" als Command-Name beim Grip-Drag.
|
||||
# Ohne dieses Eintrag kommt unser Regen in eigenen Idle-Undo-Record →
|
||||
# Cmd+Z muss zweimal gedrueckt werden.
|
||||
"ReplaceObjectProxy",
|
||||
))
|
||||
|
||||
# Bulk-Operations: User selektiert N Objekte + ausfuehrt die Operation
|
||||
@@ -11371,6 +11392,11 @@ def _on_command_begin(sender, e):
|
||||
except Exception: name = ""
|
||||
doc = Rhino.RhinoDoc.ActiveDoc
|
||||
if doc is None: return
|
||||
# ReplaceObjectProxy feuert auch fuer unsere eigenen internen Replace-
|
||||
# Calls (chain-volume rebuild etc.). Wenn _REGEN_BUSY aktiv ist, kommt
|
||||
# der Replace von uns — keinen Snapshot/Undo-Record oeffnen.
|
||||
if name == "ReplaceObjectProxy" and sc.sticky.get(_REGEN_BUSY):
|
||||
return
|
||||
# Undo/Redo: nur Flag setzen, KEIN Snapshot, KEIN Redraw-Suppress —
|
||||
# Rhinos Undo verwaltet RedrawEnabled selbst. Event-Handler ignorieren
|
||||
# waehrend dieser Phase alle Add/Delete/Replace-Events → kein Regen-
|
||||
|
||||
Reference in New Issue
Block a user