here’s a python script that mostly mimics the _Move command except it also has options for X and Y axis lock:
do you know how to set up a macro for use with -RunPythonScript? if not, I’ll help you out with that bit.
# -*- coding: utf-8 -*-
import Rhino
import Rhino.Geometry as rg
import Rhino.Input as ri
import Rhino.Input.Custom as ric
import scriptcontext as sc
import System
def constrain_point(base_point, target_point, mode):
# mode: 0=Free, 1=X, 2=Y, 3=Z
if mode == 0:
return target_point
dx = target_point.X - base_point.X
dy = target_point.Y - base_point.Y
dz = target_point.Z - base_point.Z
if mode == 1: # X only
return rg.Point3d(base_point.X + dx, base_point.Y, base_point.Z)
if mode == 2: # Y only
return rg.Point3d(base_point.X, base_point.Y + dy, base_point.Z)
if mode == 3: # Z only
return rg.Point3d(base_point.X, base_point.Y, base_point.Z + dz)
return target_point
def axis_line_from_base(base_pt, mode, length=100000.0):
if mode == 1: # X
return rg.Line(
base_pt + rg.Vector3d(-length, 0, 0), base_pt + rg.Vector3d(+length, 0, 0)
)
if mode == 2: # Y
return rg.Line(
base_pt + rg.Vector3d(0, -length, 0), base_pt + rg.Vector3d(0, +length, 0)
)
if mode == 3: # Z
return rg.Line(
base_pt + rg.Vector3d(0, 0, -length), base_pt + rg.Vector3d(0, 0, +length)
)
return None
def draw_geom_wire(display, geom, color, thickness=2):
"""
Draws common Rhino geometry as wireframe in the viewport.
(Mac-friendly).
"""
if isinstance(geom, rg.Curve):
display.DrawCurve(geom, color, thickness)
return
if isinstance(geom, rg.Brep):
display.DrawBrepWires(geom, color, thickness)
return
if isinstance(geom, rg.Mesh):
display.DrawMeshWires(geom, color)
return
if isinstance(geom, rg.Extrusion):
b = geom.ToBrep(True)
if b:
display.DrawBrepWires(b, color, thickness)
return
# fallback: bounding box
try:
bbox = geom.GetBoundingBox(True)
display.DrawBox(bbox, color, 1)
except:
pass
class MovePreviewPointGetter(Rhino.Input.Custom.GetPoint):
def __init__(self, base_pt, geoms, axis_mode_ref):
super(MovePreviewPointGetter, self).__init__()
self.base_pt = base_pt
self.geoms = geoms
self.axis_mode_ref = axis_mode_ref # dict holding {"value": int}
def OnDynamicDraw(self, e):
try:
raw_pt = e.CurrentPoint
mode = self.axis_mode_ref["value"]
constrained = constrain_point(self.base_pt, raw_pt, mode)
# Light gray rubber-band line (base -> constrained)
e.Display.DrawLine(
self.base_pt, constrained, System.Drawing.Color.LightGray, 1
)
# magenta marker at constrained point
e.Display.DrawPoint(
constrained, Rhino.Display.PointStyle.X, 8, System.Drawing.Color.Magenta
)
# blue wire preview geometry
move_vec = constrained - self.base_pt
xform = rg.Transform.Translation(move_vec)
for g in self.geoms:
g2 = g.Duplicate()
g2.Transform(xform)
draw_geom_wire(e.Display, g2, System.Drawing.Color.Blue, 2)
except:
# never let preview crash the command
pass
def move_with_axis_lock_and_preview():
# 1) Select objects
go = ric.GetObject()
go.SetCommandPrompt("Select objects to move")
go.GroupSelect = True
go.SubObjectSelect = False
go.EnablePreSelect(True, True)
res = go.GetMultiple(1, 0)
if res != ri.GetResult.Object:
return
objrefs = [go.Object(i) for i in range(go.ObjectCount)]
if not objrefs:
return
# Duplicate geometry for preview
preview_geoms = []
for objref in objrefs:
rhobj = objref.Object()
if rhobj and rhobj.Geometry:
preview_geoms.append(rhobj.Geometry.Duplicate())
if not preview_geoms:
print("No previewable geometry found.")
return
# 2) Base point
gp_base = ric.GetPoint()
gp_base.SetCommandPrompt("Point to move from")
r = gp_base.Get()
if r != ri.GetResult.Point:
return
base_pt = gp_base.Point()
# axis mode state
axis_mode = {"value": 0} # 0 free, 1 X, 2 Y, 3 Z
# 3) Second point with preview + options
gp = MovePreviewPointGetter(base_pt, preview_geoms, axis_mode)
gp.SetCommandPrompt("Point to move to")
opt_free = gp.AddOption("Free")
opt_x = gp.AddOption("X")
opt_y = gp.AddOption("Y")
opt_z = gp.AddOption("Vertical")
while True:
res = gp.Get()
if res == ri.GetResult.Option:
idx = gp.OptionIndex()
if idx == opt_free:
axis_mode["value"] = 0
elif idx == opt_x:
axis_mode["value"] = 1
elif idx == opt_y:
axis_mode["value"] = 2
elif idx == opt_z:
axis_mode["value"] = 3
continue
if res == ri.GetResult.Point:
raw_target = gp.Point()
target = constrain_point(base_pt, raw_target, axis_mode["value"])
break
return # cancel / escape
# 4) Apply move transform
move_vec = target - base_pt
xform = rg.Transform.Translation(move_vec)
moved_any = False
for objref in objrefs:
rhobj = objref.Object()
if not rhobj:
continue
if sc.doc.Objects.Transform(rhobj, xform, True):
moved_any = True
if moved_any:
sc.doc.Views.Redraw()
if __name__ == "__main__":
move_with_axis_lock_and_preview()