Rs.XformMultiply and rs.GetBoolean puzzles

Hi Forum,

The rough script below is intended to copy (or move) objects from a location relative to one block insert to a similar location relative to another block insert. It works ok as below but I encountered a few unexpected behaviours in rhinoscriptsyntax,

The first is with rs.GetBoolean - it seems to fail when passed a single item, but is ok with multiples.

The second is with rs.XformMultiply. It gives unexpected results (when blocks are rotated relative to each other), whereas the hand-rolled function MatrixMultiply below works as expected. I’m not sure if I am understanding xforms correctly, or if there is a problem with the method (XformInverse seems fine).

Paul

import rhinoscriptsyntax as rs

def BlockTransform():
    Objects = rs.GetObjects ("Select objects to transform: ")
    #this works:
    items = ("Copy", "Yes", "No"),("Copy", "Yes", "No")
    results = rs.GetBoolean("Copy objects?: ", items, (False, False))
    #this fails:
    #items = ("Copy", "Yes", "No")
    #results = rs.GetBoolean("Copy objects?: ", items, (False))
    if results[0]:
        rs.CopyObjects(Objects)
    Object0 = rs.GetObject("Select first block:", 4096)
    Object1 = rs.GetObject("Select second block:", 4096)
    if rs.IsBlockInstance(Object0):
        Matrix0 = rs.BlockInstanceXform(Object0)
    if rs.IsBlockInstance(Object1):
        Matrix1 = rs.BlockInstanceXform(Object1)
    Matrix0 = rs.XformInverse(Matrix0)
    #this works:
    Matrix0 =  MatrixMultiply(Matrix0, Matrix1)
    #this fails:
    #Matrix0 =  rs.XformMultiply(Matrix0, Matrix1)
    if results[0]:
        rs.TransformObjects(Objects, Matrix0, True)
    else: 
        rs.TransformObjects(Objects, Matrix0)
    if type(Objects) == type([]):
        rs.SelectObjects(Objects)
        
        
def MatrixMultiply(U,V):
    """function to multiply two 4x4 transformation matrices
    index of resultant is sum of row of first, multiplied by column of second
    for whatever reason, this gives the correct result, whereas the inbuilt rhino one does not
    """
    W = rs.XformZero()
    for i in range(4):
        for j in range(4):
            W[i, j] = V[i, 0] * U[0,j] + V[i,1] * U[1,j] + V[i,2] * U[2,j] + V[i,3] * U[3,j]
    return W


if __name__ == "__main__" :
    BlockTransform()


The Help file indicates that you need to make a list (or tuple) of lists (or tuples) for “items”. This is slightly different from the VB Rhinoscript, where you can just throw them all in one array. Not sure if that’s a great thing…

items = (("Copy", "Yes", "No"))
results = rs.GetBoolean("Copy objects?: ", items, (False))
print results[0]

I also have a tendency to reverse the positions for Yes and No, I generally equate Yes with True and No with False in my mind… but that’s just me, it works either way…

As far as the transformation matrix stuff is concerned, I don’t really understand much about matrix algebra (forgotten my 40 year old university math), but if you are just trying to transform objects from one block insertion point to another, you can get the block insertion points for each with rs.BlockInstanceInsertPoint(object_id), and then just use either rs.MoveObject or create a translation xForm with rs.XFormTranslation(ptB-ptA) - as subtracting 3dpoints in Python will give you a vector… Or am I completely off base here…?

–Mitch

Hi Mitch,

The script applies the entire transform - scale, rotate and translate in one go.
I’m wondering if rs.XformMultiply() might be broken.

Paul

OK, that’s what I thought after posting… I don’t know enough to tell you what’s going wrong…

Just for my education here, to get the new transform from one existing transform to the other, you need to divide the “to” matrix (1) by the “from” matrix (0) - i.e. multiplying (1) by the inverse of (0)?

I have to go out for awhile now, but I can try to reconstruct this in vbScript later and see if I get the same result…

–Mitch

Transforms can move individual points, or entire objects from one ‘space’ to another. They are a 4x4 grid of numbers applied to a vector to do rotation, translation and scaling (and skewing and perspective) all in one operation. Like adding vectors, a series of transforms can be multiplied together to give a new transform that represents the result of a series of moves.

Blocks use them - they are defined at an origin and a transform is applied to move them into their inserted position. The script above (which should work fine, just not using the inbuilt rs.XformMultiply() method) moves objects from a location relative to one block insertion, into the same location relative to another block insertion. So if a person is sitting on chair block, she can be moved to sitting on another chair block, whatever its orientation, with two clicks. First she is moved back to the ‘origin’ by finding the inverse of the transform of the first block. That inverse is then multiplied with the transform of the new block, and applied to the object, effectively moving her back to the origin, then off to the new location in one go.

p

Yep, got that…

OK, that explains the inverse operation which is what I didn’t understand.

Thanks,
–Mitch

OK, I had time to test your script with some minor mods - both in Python and VB - simply cleaning it up and eliminating some extraneous stuff for the test - and it seems to be working… With one interesting discovery - inverting the Matrix0 does not seem necessary… it seems to work by just multiplying Matrix0 and Matrix1 directly… Maybe I did something wrong:

import rhinoscriptsyntax as rs

def BlockTransform():  
    Objects = rs.GetObjects ("Select objects to transform: ")
    if not Objects: return
    items = [["Copy", "Yes", "No"]]
    results = rs.GetBoolean("Copy objects?", items, [False])
    if not results: return

    Object0 = rs.GetObject("Select first block:", 4096)
    if not Object0: return
    Object1 = rs.GetObject("Select second block:", 4096)
    if not Object1: return
    #Object0 and Object1 must be 4096 block instances

    Matrix0 = rs.BlockInstanceXform(Object0)
    Matrix1 = rs.BlockInstanceXform(Object1)
    MatrixMult =  rs.XformMultiply(Matrix0, Matrix1)
    #also tried the following, same result
    #MatrixInv = rs.XformInverse(Matrix0)
    #MatrixMult =  rs.XformMultiply(MatrixInv, Matrix1)
    rs.TransformObjects(Objects, MatrixMult, True)
    
if __name__ == "__main__" :  
    BlockTransform()

I also ran the same in vb and got the same result…

Option Explicit

Call TestObjBlockTransformVB()
Sub TestObjBlockTransformVB()
	Dim Objects,items,results,Object0,Object1,Matrix0,Matrix1,MatrixInv,MatrixMult
	Objects = Rhino.GetObjects("Select objects to transform: ")
	If Not IsArray(Objects) Then Exit Sub
	items = Array("Copy", "Yes", "No")
	results = Rhino.GetBoolean("Copy objects?: ", items, Array(False))
	If Not IsArray(results) Then Exit Sub
	
	'Call Rhino.CopyObjects(Objects)
	Object0 = Rhino.GetObject("Select first block:", 4096)
	If IsNull(Object0) Then Exit Sub
	Object1 = Rhino.GetObject("Select second block:", 4096)
	If IsNull(Object1) Then Exit Sub

	Matrix0 = Rhino.BlockInstanceXform(Object0)
	Matrix1 = Rhino.BlockInstanceXform(Object1)
	MatrixMult = Rhino.XformMultiply(Matrix0, Matrix1)
	Call Rhino.TransformObjects(Objects, MatrixMult, True)	
End Sub

Dunno… ???
–Mitch

OK, now I see something is not working… I had only tried with blocks that had the original insertion point at 0… If the insertion point of block 0 is not at the origin, the script does not place the transformed objects at the right spot… either with Python nor with VB… So I don’t know what is wrong. And you are right, your “home-made” matrix multiply function DOES appear to work correctly. @stevebaer?

This is a bug in GetBoolean. It will be fixed in SR7 (SR6 is pretty much ready to be pushed out). You can also get the working module from github at

https://github.com/mcneel/rhinopython/blob/master/scripts/rhinoscript/userinterface.py  

I think you just need to switch the multiplication order to match your MatrixMultiply function

Matrix0 = rs.XformMultiple(Matrix1, Matrix0)

Ahhh… :smile: