diff --git a/rhino/elemente.py b/rhino/elemente.py index 3e10b94..7786aec 100644 --- a/rhino/elemente.py +++ b/rhino/elemente.py @@ -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-