Move and Rotate

I am trying to write a simple move and rotate utility command to speed up some of the tasks we do at work very often.
Here is what I am trying to get it to do.
Step1 - Prompted to select objects, then ask for offset distance.
Step2 - Copy object in -Y direction with a predefined offset distance in between the 2 objects
Step3 - Rotate object from Step 2 Down
Step4 - Copy object from Step3 in +X direction with a predefined offset distance
Step5 - Rotate object from Step4 on Y axis
Step6 - Repeat all over again until out of selected objects

I’ve made 2 functions
1 for moving in either X or Y direction
1 for rotating on either X or Y axis

Both functions and both parts of the function work independently on multiple objects at once.
I cannot figure out how to string along the while loop (or some other loop method for that matter) correctly. So it runs all the functions on one object then moves to the next object.

Attaching some print screens to illustrate the final result

Here is what I got for code…

import rhinoscriptsyntax as rs

__commandname__ = "viMoveAndRotate"

# RunCommand is the called when the user enters the command name in Rhino.
# The command name is defined by the filname minus "_cmd.py"
def RunCommand( is_interactive ):
	print "Running", __commandname__

	ids = rs.GetObjects(message = "Select objects to move and rotate", group = True, preselect = True)
	offsetDistance = rs.GetReal("Offset Distance", 10, 1.0)
	if not ids: return

	def vi_moveObject(_id, mDir, mDist):
		if _id:
			bb = rs.BoundingBox(_id)
			if mDir == 'X':
				sizeX = bb[1].DistanceTo(bb[0])
				sizeZ = bb[4].DistanceTo(bb[0])
				translateX = ((sizeX/2) + offsetDistance + (sizeZ/2))
				trDir = [translateX, 0, 0]
			if mDir == 'Y':
				sizeY = bb[3].DistanceTo(bb[0])
				translateY = -(sizeY + offsetDistance)
				trDir = [0, translateY, 0]
			rs.CopyObject(_id, trDir)

	def vi_rotateObject(_id, rDir):
		if _id:
			centroid = rs.SurfaceVolumeCentroid(_id)[0] #need a better center finding method
			# Rotation on X axis
			if rDir == 'Down':
				angle = 90
				axis = [1,0,0]
			# Rotation on Y axis
			if rDir == 'Right':
				angle = -90
				axis = [0,1,0]
			rs.RotateObjects(_id, centroid, angle, axis, copy=False) #True is for test only remove later

	print 'Selection Length', len(ids)
	
	i = 0
	while i < len(ids):
		
		vi_moveObject(ids[i], 'Y', offsetDistance)
		vi_rotateObject(ids[i], 'Down')
		vi_moveObject(ids[i], 'X', offsetDistance)
		vi_rotateObject(ids[i], 'Right')
		
		print i
		i+=1


	# you can optionally return a value from this function
	# to signify command result. Return values that make
	# sense are
	#   0 == success
	#   1 == cancel
	# If this function does not return a value, success is assumed
	return 0

if __name__ == "__main__":
	RunCommand(True)

I am sure it’s evident, but I am super new to python or anything that has to do with classes
I am trying to do my best and use functions instead of writing something sloppy. I am sure what I have is sloppy either way, but from all the reading about def and classes I am just trying to wrap my head around it all.

The goal is to be able to feed 100+ objects at a time…

In advance, thank you for your help!

Not sure if this the right way to do it still. but here is how I got it to work. In case someone else needs something like this.

import rhinoscriptsyntax as rs

__commandname__ = "viMoveAndRotate"

# RunCommand is the called when the user enters the command name in Rhino.
# The command name is defined by the filname minus "_cmd.py"
def RunCommand( is_interactive ):
	print "Running", __commandname__

	ids = rs.GetObjects(message = "Select objects to move and rotate", group = True, preselect = True)
	offsetDistance = rs.GetReal("Offset Distance", 10, 1.0)
	if not ids: return

	
	def vi_moveObject(_id, mDir, mDist):
		if _id:
			bb = rs.BoundingBox(_id)
			if mDir == 'X':
				sizeX = bb[1].DistanceTo(bb[0])
				sizeZ = bb[4].DistanceTo(bb[0])
				translateX = ((sizeX/2) + offsetDistance + (sizeZ/2))
				trDir = [translateX, 0, 0]
			if mDir == 'Y':
				sizeY = bb[3].DistanceTo(bb[0])
				translateY = -(sizeY + offsetDistance)
				trDir = [0, translateY, 0]

			newObj = rs.CopyObject(_id, trDir)
			return newObj

	def vi_rotateObject(_id, rDir):
		if _id:
			centroid = rs.SurfaceVolumeCentroid(_id)[0] #need a better center finding method
			# Rotation on X axis
			if rDir == 'Down':
				angle = 90
				axis = [1,0,0]
			# Rotation on Y axis
			if rDir == 'Right':
				angle = -90
				axis = [0,1,0]
			rs.RotateObjects(_id, centroid, angle, axis, copy=False) #True is for test only remove later


	print 'Selection Length', len(ids)
	
	i = 0
	while i < len(ids):
		
		newObj = vi_moveObject(ids[i], 'Y', offsetDistance)
		vi_rotateObject(newObj, 'Down')
		newObj = vi_moveObject(newObj, 'X', offsetDistance)
		vi_rotateObject(newObj, 'Right')
		
		print i
		i+=1

	return 0

if __name__ == "__main__":
	RunCommand(True)

For the loop, one could do:

for id in ids:
    newObj = vi_moveObject(id, 'Y', offsetDistance)
    ...

instead of using a while and counter. If you need an ‘i’ then there is also the enumerate function:

for i, id in enumerate(ids):
    newObj = vi_moveObject(id, 'Y', offsetDistance)
    print(i)
    ...

Thank you, Ill give this a try!