SOLVEDMove just the axis of a polygonal object

I have a list with all the coordinates of the vertexes of a polygonal object, all in global coordinates, that I got with:

``````op_mg = child.GetMg()
points = op.GetAllPoints()

for p,pt in enumerate(points) :
points[p]=pt*op_mg
``````

After setting the global matrix of my polygonal object to the matrix that I want its axis to be, now I want to relocate all the points the location where they were previously (globally).
But I want it to be able to happen, no matter how deep inside a hierarchy my polygonal object is.
Meaning... I want to be able to relocate the axis of my polygonal object to a specific global location, even if my object is inside a hierarchy, but I want to keep all the vertexes in their initial global location.
How can I do this?!?
I have been working with matrixes but no matter what I do, the points of the object always end up moving.

It's actually the axis and the points that have to move.

Hello @rui_mac

you don't really need to deal with hierarchy as long as you are using global space.
you have to come back to local space with the new matrix, by multiplying the points position with the inverse matrix.
Be sure to also move the axis of the object as @mp5gosu stated and to ask the object to update itself.

``````        # Retrieves selected object
op = doc.GetActiveObject()
if op is None:
raise TypeError("There's no object selected.")

# Retrieves the matrix of the object
opmg = op.GetMg()

# Retrieves the point in global space
pointsGlob = [p * opmg for p in op.GetAllPoints()]

# Starts the undo process (the initial scene to be restored after an undo action)
doc.StartUndo()

# Notifies a change on the obj

opmg.off = c4d.Vector(0)
# Updates the axis of the object
op.SetMg(opmg)

# Notifies the object, that internal data changed and it needs to be updated
op.Message(c4d.MSG_UPDATE)

# Inverses the matrix to come back to local space
invopmg = ~opmg

# Retrieves points from global to local space
newPoints = [p * invopmg for p in pointsGlob]

op.SetAllPoints(newPoints)

# Ends the undo process (the final scene state)
doc.EndUndo()

``````

Cheers
Manuel

That is what I was trying to do, but it is still not working.

Lets assume I have a list to objects that are the objects that are all children of a Null. The objects can be children or childs of childs, or even deeper, but they all are inside a Null.
I want all the geometry of those children to remain in the same spacial location but I wan all the axis to move to the location of the Null (no matter how deep the objects are in the hierarchy.
My current code is as follows:

``````main_mg = op.GetMg() # the global matrix of the parent Null

# op_list is a list that contains all the childs of the Null.
# Some of them are childs of childs or even deeper.

doc.StartUndo()

for op in op_list:

child_mg = op.GetMg()
pointsG = [p * child_mg for p in op.GetAllPoints()]

op.SetMg(main_mg)
op.Message(c4d.MSG_UPDATE)
inv_mg = ~child_mg
nPoints = [p * inv_mg for p in pointsG]

op.SetAllPoints(nPoints)

doc.EndUndo()

``````

But it is not working!!
All the geometry still moves.

inv_mg = ~child_mg

Be careful about the matrix you are using as reference to go from global to local.
The object new matrix now is the same as the null. If you are using the old one (child_mg), the points will move the same way you moved the axis. But if you are using the new one, the points be changed to global to local and in fact will not move. (and that's the goal)

``````inv_mg = ~main_mg
``````

forget about the hierarchy in that case.
It's just matrix operation local to global and global to local.

Let me know if it's still not clear.

Cheers
Manuel

Thank you so much. It is clearer now.
It works!!

Spoke too soon.
It works, but not for childs of childs of childs...
This is my current code:

``````import c4d

op_list=[]

def Get_Op(op):
global op_list

while (op):
op_list.append(op)
Get_Op(op.GetDown())
op = op.GetNext()
return None

# Main function
def main():
global op_list

selected = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_0)
if len(selected)!=1: return
op = selected[0]

main_mg = op.GetMg()

child = op.GetDown()
op_list = []

if child != None:
Get_Op(child)

if len(op_list)!= 0:

doc.StartUndo()

for op in op_list:

if op.GetType()==c4d.Opolygon:

child_mg = op.GetMg()
pointsGlob = [p * child_mg for p in op.GetAllPoints()]

op.SetMg(main_mg)
op.Message(c4d.MSG_UPDATE)

inv_mg = ~main_mg
newPoints = [p * inv_mg for p in pointsGlob]

op.SetAllPoints(newPoints)
op.Message(c4d.MSG_UPDATE)

doc.EndUndo()

# Execute main()
if __name__=='__main__':
main()
``````

And it doesn't work with the Cap1 and Cap2 objects, in this hierarchy:

Everything remains in the same location, with the axis moved to the location of the outermost parent Null, except the Cap1 and Cap2 objects, as their geometry changes location.

after trying several solutions, i've change your function with this one for non recursive iteration. and using the same idea to local to global and global to local but with the child "locoal" matrix. (the one you get with GetMl)

``````import c4d

def GetNextObject(op):
# Non recursive hierarchy iteration
if op==None:
return None

if op.GetDown():
return op.GetDown()

while not op.GetNext() and op.GetUp():
op = op.GetUp()

return op.GetNext()

def UpdateObject (op, newpos):
# Update the object by moving it to the new position
# and moving back the points to their old position

# Store the old matrix of the object
old_mg = op.GetMg()

# Retrieve the points position in global space
pointsGlob = [p * old_mg for p in op.GetAllPoints()]

# Add an Undo state to the document

# update the object position matrix
op.SetMg(newpos)

# Inverses the matrix to come back to local space
inv_mg = ~newpos

# Retrieves points from global to local space
newPoints = [p * inv_mg for p in pointsGlob]

# Add an Undo state to the document

op.SetAllPoints(newPoints)

# Notifies a change on the obj
op.Message(c4d.MSG_UPDATE)

def UpdateChildsMatrix(op, oldMg, newMg):
# Update the Childrend Matrix after the parent have been moved

op = op.GetDown()
# For All child of the object
while op:
# Store the local matrix to a global space with the old matrix object
newChildMg = oldMg * op.GetMl()

# Calculate the new local matrix of the object.
newChildMg =  ~newMg * newChildMg

# Set the new local matrix of the object
op.SetMl(newChildMg)

# Notifies a change on the obj
op.Message(c4d.MSG_UPDATE)

# Get the next Object if any
op = op.GetNext()

# Main function
def main():
# Get all Selected Object
selected = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE)

if len(selected) != 1:
return

mainOp = selected[0]

op_list = []

# Get the first object if any
nextObj = GetNextObject(mainOp)
while nextObj:
# Add all object found in the array
op_list.append(nextObj)
# Retreive the next Object
nextObj = GetNextObject(nextObj)

# Retreive the global position of the main object
main_mg = mainOp.GetMg()

# if there's no child, just leave
if len(op_list) == 0:
return

# Starts the undo process (the initial scene to be restored after an undo action)
doc.StartUndo()

for op in op_list:
# Check if it's a polygon Object
if op.GetType()==c4d.Opolygon:
#save the matrix to have a chance to update the child
savedMatrix = op.GetMg()

# Move the object to the new location without moving the points
UpdateObject(op,main_mg)

# Because we moved the parent, we should move back the children.
UpdateChildsMatrix(op, savedMatrix, main_mg)

# Ends the undo process (the final scene state)
doc.EndUndo()

# Execute main()
if __name__=='__main__':
main()
``````

Cheers
Manuel

Thank you so much, Manuel.
It was, mainly, that children matrix update that was missing.
Now, it works fine

hi,

when you will be sure that it's solved, don't forget to mark this thread as solved please.

Cheers
Manuel