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
|
z1 = geom.PointAtEnd.Z
|
||||||
if abs(z0) < 1e-6 and abs(z1) < 1e-6:
|
if abs(z0) < 1e-6 and abs(z1) < 1e-6:
|
||||||
return False
|
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
|
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))
|
print("[ELEMENTE] wand z-drag triggered: z0={:.3f} z1={:.3f} delta={:.3f}".format(z0, z1, delta))
|
||||||
doc = Rhino.RhinoDoc.ActiveDoc
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
@@ -10291,31 +10313,26 @@ def _on_object_replaced_body(sender, e):
|
|||||||
if meta.get("type") == "wand_axis":
|
if meta.get("type") == "wand_axis":
|
||||||
old_geom = e.OldRhinoObject.Geometry if e.OldRhinoObject else None
|
old_geom = e.OldRhinoObject.Geometry if e.OldRhinoObject else None
|
||||||
new_geom = e.NewRhinoObject.Geometry if e.NewRhinoObject else None
|
new_geom = e.NewRhinoObject.Geometry if e.NewRhinoObject else None
|
||||||
# No-Op-Check: GripsOn/CommitChanges feuert Replace ohne Geometrie-
|
# No-Op-Check: GripsOn/CommitChanges feuert Replace ohne sinnvolle
|
||||||
# Aenderung. Triggert sonst eine ganze Regen-Kette die in eigenem
|
# Geometrie-Aenderung. Toleranz auf 1mm (Architektur-Praezision).
|
||||||
# Undo-Record laeuft (Cmd+Z muesste mehrfach gedrueckt werden).
|
|
||||||
# Endpunkt-Vergleich reicht fuer LineCurves; Polylines/Splines
|
|
||||||
# checken zusaetzlich Mid-Punkt + Laenge.
|
|
||||||
unchanged = False
|
unchanged = False
|
||||||
try:
|
try:
|
||||||
if (isinstance(old_geom, rg.Curve)
|
if (isinstance(old_geom, rg.Curve)
|
||||||
and isinstance(new_geom, rg.Curve)):
|
and isinstance(new_geom, rg.Curve)):
|
||||||
p_o_s, p_o_e = old_geom.PointAtStart, old_geom.PointAtEnd
|
p_o_s, p_o_e = old_geom.PointAtStart, old_geom.PointAtEnd
|
||||||
p_n_s, p_n_e = new_geom.PointAtStart, new_geom.PointAtEnd
|
p_n_s, p_n_e = new_geom.PointAtStart, new_geom.PointAtEnd
|
||||||
tol = 1e-6
|
tol_xy = 0.001 # 1 mm
|
||||||
if (p_o_s.DistanceTo(p_n_s) < tol
|
if (p_o_s.DistanceTo(p_n_s) < tol_xy
|
||||||
and p_o_e.DistanceTo(p_n_e) < tol):
|
and p_o_e.DistanceTo(p_n_e) < tol_xy):
|
||||||
# Endpunkte gleich — Polyline/Spline-Mitten checken
|
|
||||||
try:
|
try:
|
||||||
l_o = old_geom.GetLength()
|
l_o = old_geom.GetLength()
|
||||||
l_n = new_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
|
unchanged = True
|
||||||
except Exception:
|
except Exception:
|
||||||
unchanged = True
|
unchanged = True
|
||||||
except Exception: unchanged = False
|
except Exception: unchanged = False
|
||||||
if unchanged:
|
if unchanged:
|
||||||
# Nur Grips-Toggle / Attribut-Aenderung — kein Regen noetig.
|
|
||||||
return
|
return
|
||||||
# Joint-Cache invalidieren — Wand hat sich geaendert
|
# Joint-Cache invalidieren — Wand hat sich geaendert
|
||||||
_invalidate_joints_cache(meta.get("geschoss"))
|
_invalidate_joints_cache(meta.get("geschoss"))
|
||||||
@@ -11221,6 +11238,10 @@ def _on_idle_selection(sender, e):
|
|||||||
_USER_TRANSFORM_CMDS = frozenset((
|
_USER_TRANSFORM_CMDS = frozenset((
|
||||||
"Move", "Rotate", "Rotate3D", "Mirror", "Scale", "Scale1D", "Scale2D",
|
"Move", "Rotate", "Rotate3D", "Mirror", "Scale", "Scale1D", "Scale2D",
|
||||||
"Drag", "Gumball", "Orient", "Orient3Pt", "RemapCPlane", "Transform",
|
"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
|
# Bulk-Operations: User selektiert N Objekte + ausfuehrt die Operation
|
||||||
@@ -11371,6 +11392,11 @@ def _on_command_begin(sender, e):
|
|||||||
except Exception: name = ""
|
except Exception: name = ""
|
||||||
doc = Rhino.RhinoDoc.ActiveDoc
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
if doc is None: return
|
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 —
|
# Undo/Redo: nur Flag setzen, KEIN Snapshot, KEIN Redraw-Suppress —
|
||||||
# Rhinos Undo verwaltet RedrawEnabled selbst. Event-Handler ignorieren
|
# Rhinos Undo verwaltet RedrawEnabled selbst. Event-Handler ignorieren
|
||||||
# waehrend dieser Phase alle Add/Delete/Replace-Events → kein Regen-
|
# waehrend dieser Phase alle Add/Delete/Replace-Events → kein Regen-
|
||||||
|
|||||||
Reference in New Issue
Block a user