SOLVED Move object axis problem in python

Hi, guys! i'm a new python guy and i want to move my object axis only, like center axis tool .and now i can move the axis on Y axis like this:
it's work fine
Work Fine.gif
but if i rotate the object , it's have a problem(I don't know why i can't upload my next GIF).

I think this is a matrix problem, but matrix a little bit hard for me ,i have no idea how to fix it.
Hope you guys can help me.☹

This is my code:

import c4d


def main():
    obj = doc.GetActiveObject()
    axis_offset = c4d.Vector(0,100,0)
    
    points = obj.GetAllPoints()
    count = obj.GetPointCount()
    obj_pos = obj.GetAbsPos()
    
    doc.AddUndo(c4d.UNDOTYPE_CHANGE,obj)
    for i in xrange(len(points)):
        points[i] = points[i] + (axis_offset*-1)
        obj.SetAllPoints(points)
        obj.SetAbsPos(obj_pos+axis_offset)
        
    obj.Message(c4d.MSG_UPDATE)
    c4d.EventAdd()
    
if __name__=='__main__':
    main()

Hi Harry, thanks for reaching out us.

With regard to your topic, as suggested by @zipit - thanks a lot dude -, I strongly recommend in having a look at geometry fundamentals as presented in ScratchPixel or in our documentation at Matrix Fundamentals.

Taking in consideration the global object orientation in space you could reconsider your code to use the inverse global matrix directly applied to your offset vector.

    # set the offset vector
    myOff = c4d.Vector(0,-100,0)
    
    # check op is valid and assume it's already a mesh
    if op is None:
        return
    
    # retrieve object global inverse matrix
    invOpMg = ~(op.GetMg())   
    
    #get the mesh points 
    points = op.GetAllPoints()
    
    # browse over them and add the offset vector left multiplied by the inverse global matrix
    for i,p in enumerate(points):
        points[i] = p + invOpMg*myOff
        
    # set points and update
    op.SetAllPoints(points)
    op.Message(c4d.MSG_UPDATE)
    c4d.EventAdd()

Best, Riccardo

Hi,

I am a bit confused regarding what your actual question is, since your code does not contain any matrix operations. I assume you want to know how matrix transforms work, so that you can construct one? You can check the ScratchPixel section on matrices, they have a pretty good explanation on how you can define transforms with matrices (if you don't already know what vectors are you should probably read that chapter too, especially regarding the geometric meaning of the cross product). Note that practically every software has a slightly different interpretation of transformation matrices, so read ScratchPixel more as a general guide.

c4d.utils defines three functions to construct a rotation matrix axis by axis, the example also modifies the offset component of the final rotation matrix so that it also translates points by 100 units along the y axis.

90_DEGREE = math.pi * .5
# construct a rotation matrix for each axis.
mg_x = c4d.utils.MatrixRotX(90_DEGREE)
mg_y = c4d.utils.MatrixRotY(0)
mg_z = c4d.utils.MatrixRotZ(-90_DEGREE)

# multiplying matrices will give you a matrix which combines all transformations
my_final_transform = mg_x * mg_y * mg_z
# set the offset
my_final_transform.off = c4d.Vector(0, 100, 0)

# transform a point in global space
some_point_in_global_space = c4d.Vector(100, 0, 0)
transformed_point = my_final_transform * some_point_in_global_space 

You can also construct matrices manually if you have one or multiple normals (or vectors if you also want to scale stuff), using the cross product:

# note that the x and y components are switched, so we rotate by 90 degree on
# the axis orthogonal to the xy-plane (the z axis).
x_component = c4d.Vector(0, 1, 0)
# the y component is not an unit vector so we scale by 2 on that axis
y_component = c4d.Vector(2, 0, 0)
# define the z axis manually
z_component = c4d.Vector(0, 0, 1)
# or construct it using the x and y component
z_component = x_component.Cross(y_component).GetNormaliezd()
# the offset (the translation component) for the matrix
off_component = c4d.Vector(100, 0, 0)
# build the matrix
my_transform = c4d.Matrix(off_component, x_component, y_component, z_component)

#do stuff with it
transformed = []
for p in points:
    transformed.append(my_transform * p)

Cheers
zipit

@zipit
Thank you very much for your reply. Maybe I did not describe the problem clearly. Actually, the problem I encountered is like this:

For example : if i use rotate tool rotate my object and click my script.
1.the axis can move correctly.
2.but the object moves with it.(this is my problem)

I don't want my objects to move,and I want my object to stay in place and only move the object axis

thank you again.
Cheers
harry

Hi,

well then you have to apply the invers of the translation you have applied to your points also to your object. Something like this:

my_vector = c4d.Vector(0, 100, 0)

for i, p in enumerate(points):
    points[i] = p + my_vector

my_object.SetAllPoints(points)
my_object.SetRelPos(my_object.GetRelPos() - my_vector)

Cheers
zipit

@zipit
yes it works. but if you object is rotated on X axis 90 degrees, and click the script ,something is wrong...

@gheyret said in Move object axis problem in python:

@zipit
yes it works. but if you object is rotated on X axis 90 degrees, and click the script ,something is wrong...

I do not have C4D here, so i cannot run your code, also "something is wrong" is a rather vague description.

However, if you rotate your object you have to respect the matrix of that object when reapplying the invers of your vector (since you are operating in local coordinates for vertices). See my examples on matrices above. There are methods to get and set global and local matrices of objects in c4d.BaseObject.

Cheers
zipit

@zipit
Thanks for everything. You're really been a big help for me.
It works!!!

Cheers
Harry

Hi Harry, thanks for reaching out us.

With regard to your topic, as suggested by @zipit - thanks a lot dude -, I strongly recommend in having a look at geometry fundamentals as presented in ScratchPixel or in our documentation at Matrix Fundamentals.

Taking in consideration the global object orientation in space you could reconsider your code to use the inverse global matrix directly applied to your offset vector.

    # set the offset vector
    myOff = c4d.Vector(0,-100,0)
    
    # check op is valid and assume it's already a mesh
    if op is None:
        return
    
    # retrieve object global inverse matrix
    invOpMg = ~(op.GetMg())   
    
    #get the mesh points 
    points = op.GetAllPoints()
    
    # browse over them and add the offset vector left multiplied by the inverse global matrix
    for i,p in enumerate(points):
        points[i] = p + invOpMg*myOff
        
    # set points and update
    op.SetAllPoints(points)
    op.Message(c4d.MSG_UPDATE)
    c4d.EventAdd()

Best, Riccardo

@r_gigante
Yes! It's really helpful.🍻