Right way to get object out?



  • On 01/04/2017 at 18:07, xxxxxxxx wrote:

    Hi!
    I noticed a strange (but logical though) thing - when I drag'n'drop the object from beneath the parent in object manager it stays where it was. But when I do it via Python (basically, I copy object to memory, delete the original and than paste it under another object or scene root) it now jumps in space.
    It has it's logic, the object retains it's position in relation to parent, but it's somehow compensated on drag'n'drop action, what we have to do manually in Python. Is there a way to make this operation easy? There's no any commands in script log when I do it in interface so I ask you.
    That would be especially good if there's a way to get object out without primitive copy/paste



  • On 02/04/2017 at 08:05, xxxxxxxx wrote:

    Copy the child's global matrix before removing it.
    Then re-apply it to the child after it has been moved in the OM.

    import c4d  
    def main() :  
        
      parent = doc.GetFirstObject()  
      child = parent.GetDown()  
      glMtx = child.GetMg()  
      child.Remove()  
      doc.InsertObject(child, None, parent)  
      child.SetMg(glMtx)  
      child.Message(c4d.MSG_UPDATE)  
        
      c4d.EventAdd()      
      
    if __name__=='__main__':  
      main()
    

    -ScottA



  • On 02/04/2017 at 08:14, xxxxxxxx wrote:

    Why do you remove it before inserting?

    Since without it's working well. And I guess it's why the UNDOTYPE_HIERARCHY_PSR it's done for.

    import c4d
    def main() :
        
        doc.StartUndo()
        parent = doc.GetFirstObject()
        child = parent.GetDown()
        glMtx = child.GetMg()
        doc.AddUndo(c4d.UNDOTYPE_HIERARCHY_PSR, child)
        doc.InsertObject(child, None, parent)
        child.SetMg(glMtx)
        child.Message(c4d.MSG_UPDATE)
        doc.EndUndo()
        c4d.EventAdd()    
      
    if __name__=='__main__':
        main()
    

    EDIT:
    It's just for not doing UNDOTYPE_DELETE and UNDOTYPE_NEW
    http://www.plugincafe.com/\forum/forum_posts.asp?TID=12129&PID=48101#48101



  • On 02/04/2017 at 08:48, xxxxxxxx wrote:

    Oh guys! So many thanks, you saved me form such a hassle! 🙂
    As any nuble definitely must do, I have chosen so awkwardly difficult way to handle it so I doubt I could resolve it. I guess SDK developers are still smarter me :D



  • On 02/04/2017 at 10:15, xxxxxxxx wrote:

    I encountered very strange problem while copying matrix (the same happens when I simply unparent in gui)
    The main purpose of the script I tried to write was an ability to unparent animated object without weird jump, when the animated properties are not (how to say?) "compensated" the way static are. So animated properties are counted from new parent, but with old offsets, and the absolute values are totally different from what it was before.
    Only recently I noted there already is such function built in C4D, but, anyway, I decided to finish the work.
    What's on the video. The child object has animation on one position and one rotation track. When I unparent it two strange things happen: rotation on animated axis changes only by 10 degrees (??) and being free from the parent it starts to move in completely opposite direction! WTF is happening?? I had lost any understanding how to compensate it all to get the object back where it was before unparenting
    I would highly appreciate if you can explain how to deal with that
    https://www.youtube.com/watch?v=c7rIKtNRzo0&feature=youtu.be



  • On 02/04/2017 at 10:53, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Why do you remove it before inserting?
    Since without it's working well.

    Try that in C++ and see what happens. It will be painful. Smile
    Python is allowing you to doing some major memory handling cheating here.
    IMHO. You should still use Remove() in python even if it seems to work without it.

    -ScottA



  • On 03/04/2017 at 11:10, xxxxxxxx wrote:

    Hi,

    in general a any GeListNode can only be part of one single list at a time. So you need to Remove() it before inserting it into a new list (or another position of the same).

    Unfortunately I can't watch your video (seems to be marked private). So I'm not quite sure, what the actual problem is.
    In order to work with animated objects, you will certainly want to take a look at CTrack and CKey classes.



  • On 03/04/2017 at 11:11, xxxxxxxx wrote:

    Ooopsy, I forgot to welcome you in the Plugin Café forums, Intenditore. Welcome! 🙂



  • On 03/04/2017 at 12:44, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Ooopsy, I forgot to welcome you in the Plugin Café forums, Intenditore. Welcome! 🙂

    Thank you! 🙂 Glad to join your community)

    I edited settings, now the video is viewable



  • On 04/04/2017 at 08:18, xxxxxxxx wrote:

    I stumbled upon a great misconception in my head :D
    How do axis work?
    As I imagine, if we have a parent object and it's child, so the child object's PSR might be calculated like this:
    position would equal absolute (world coordinate) parent position + relative (to parent) child position (seems easy);
    scale - parent scale multiplied by child scale (but! What if the child is rotated inside, so the parent is scaled up on x axis, for instance, but in projection to child it will be y axis, for example? Or both Z and Y if it's angled? how to deal with that?);
    and than rotation - firstly I thought it's pretty easy, we simply must take parent H and summ it with child H (and so the same with B and P), but it turned out to be much more, extensively complicated!
    First - do math operations order matter? In what order do we need to compute new coordinates, what axis is primary and needs to be set first? Or there's no difference (I doubt it could be so)
    Second - what if parent and child have their axis misaligned or flipped or even angled? How to get the angles we need to add/substract to get to original values after unparenting?
    And that's not only scripting-related question, I know many (including me) are curious seeing such different values as on the screenshot

    I will be very grateful if you can explain me the concept behind that magic :D. I think it's very important and necessary for many more people not only me



  • On 04/04/2017 at 08:43, xxxxxxxx wrote:

    Basicly op.GetMg(the global matrix) is egal to Parent GlobalMatrix + Current obj local Matrix.
    Then you can do this thing recursivly.
    Moreover keep in mind HPB is a Vector not a Matrix. And that is very important cause Matrice is 4 Vector(3 for rotation) while HBP is an unique Vector whro represente these 3Vectors. So a conversion is needed. And in all conversion you have to keep in mind you can loose some informations. It's why you can have for exemple 0 or 360 in H. Because from the conversion and from a logical point of view it's the same value while in animation it can be a problem.
    Moreover take a look about utils who talk about HPB and axis order ;)
    function. https://developers.maxon.net/docs/Cinema4DPythonSDK/html/modules/c4d.utils/index.html?highlight=hpb#c4d.utils.MatrixToHPB

    A good read about that https://developers.maxon.net/docs/Cinema4DPythonSDK/html/misc/matrixfundamental.htm

    So in your case your master null (An) have probably already a rotation. since in the object panel you are into the object local position while in the coordinate manager you are in world coordinate.
    And that why thoses values are differents.

    Hope it's help you more than confuse you.



  • On 04/04/2017 at 09:06, xxxxxxxx wrote:

    There is a pretty good article on matrices in the COFFEE docs.
    But for what you're trying to do. That information might just confuse you more (although it's good to know that stuff).

    The problem you're facing is that the values for the animation keys were all set using a specific parent. So when you change to a different parent object (in this case the world). Those key values are wrong.
    You really just need to compensate for the difference between the old parent and the new parent. Then use that difference value to update every single animation key in the child object.
    The down side to this is that editing animation keys takes a lot of code. And it can get quite messy. I've been trying to think of a trick to make it simpler. But I haven't been able to think of one yet.

    Here is an example.
    Note that this does not handle scaling or any other types of animation tracks. Those tracks would need to be added as desired.

    #Suppose you have an object with a child object called "Cube.1"  
    #And you want to move the child out from under the parent so it's a child of the world  
    #If the child has no keys on it. You can simply copy the child's global matrix then apply that matrix to it after it's moved in the OM   
    #But if the child has animation keys on it. You'll need to convert the values for each key to respect the world as the parent object  
    #In other words. The animation key values need to be converted with a calculation  
      
    import c4d  
    def main() :      
      
      child = doc.SearchObject("Cube.1") #<---Or whatever your object's name is  
      if child is None: return  
      cMtx = child.GetMg()  
        
      parent = child.GetUp()  
      if parent is None: return  
      pMtx = pMtx = parent.GetMg()  
        
      worldMtx = c4d.Matrix()      
      vec = pMtx.off - worldMtx.off  
        
      #Get all the position key values on the child object  
      #Offset them by the distance the parent object is from the world matrix  
      #Store those values in list arrays  
      posX = []  
      posY = []  
      posZ = []  
      rotX = []  
      rotY = []  
      rotZ = []  
        
      doc.StartUndo()  
      doc.AddUndo(c4d.UNDOTYPE_CHANGE, child)  
        
      tracks = child.GetCTracks()  
        
      ################ Positions ##################   
        
      for track in xrange(len(tracks)) :                 
          if tracks[track].GetName() == "Position . X":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  posX.append(key.GetValue() + vec.x)                   
                    
          if tracks[track].GetName() == "Position . Y":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  posY.append(key.GetValue() + vec.y)   
                                   
          if tracks[track].GetName() == "Position . Z":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  posZ.append(key.GetValue() + vec.z)  
                    
          ################ Rotations ##################          
                    
          if tracks[track].GetName() == "Rotation . X":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  rotX.append(key.GetValue() + vec.x)                   
                    
          if tracks[track].GetName() == "Rotation . Y":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  rotY.append(key.GetValue() + vec.y)   
                                   
          if tracks[track].GetName() == "Rotation . Z":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  rotZ.append(key.GetValue() + vec.z)     
                    
                    
        
      #Change the child's parent to the world by moving it in the OM      
      child.Remove()  
      doc.InsertObject(child,None,parent)  
      child.Message(c4d.MSG_UPDATE)  
            
      #Now we need to change each of the child object's keys  
      #Replacing them with the values stored in the lists  
      for track in xrange(len(tracks)) :                 
          if tracks[track].GetName() == "Position . X":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)                  
                  key.SetValue(curve, posX[k])    
                    
          if tracks[track].GetName() == "Position . Y":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  key.SetValue(curve, posY[k])  
                                   
          if tracks[track].GetName() == "Position . Z":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  key.SetValue(curve, posZ[k])  
                    
                    
          if tracks[track].GetName() == "Rotation . X":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)                  
                  key.SetValue(curve, rotX[k])    
                    
          if tracks[track].GetName() == "Rotation . Y":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  key.SetValue(curve, rotY[k])  
                                   
          if tracks[track].GetName() == "Rotation . Z":   
              curve = tracks[track].GetCurve()  
              for k in xrange(curve.GetKeyCount()) :  
                  key = curve.GetKey(k)   
                  key.SetValue(curve, rotZ[k])                  
                    
      child.Message(c4d.MSG_UPDATE)                  
      doc.EndUndo()  
        
      c4d.DrawViews(c4d.DRAWFLAGS_FORCEFULLREDRAW)  
      c4d.EventAdd()  
      
    if __name__=='__main__':  
      main()
    

    -ScottA



  • On 04/04/2017 at 12:31, xxxxxxxx wrote:

    Oh wow 😵 What an incredibly detailed answers! Huge!
    Basically the concept you describe is exactly the same as the one I had, but something does not work yet.
    gr4ph0s, it could be much easier, but as I have to deal with individual keys it all gets much more complicated. I decided that getting initial values without matrices is more straight-forward but it looks basically the same as in the variant you've described, on principal level.
    I have red the article about Matrix before but thanks anyway, it seems I got it ))
    ScottA, wow! Thank you for that huge piece of work)
    I have written about the same thing, but I call tracks not in loop but strictly by their names because I noticed in Cinema you can add second or even third curve on the same property, so there may be a few curves you will get this way as I got it (still I completely can't understand what it could be used for and how to utilize it)
    Anyway, that's my code
    This is partially working snippet, still unfinished. It works but has a very strange problem. I see the content of staticRotation_parent is not corresponding to the curves_rotation. Both must contain X, Y and Z in that exact order, but in staticRotation_parent the order is strangly different as I discovered (not sure though, maybe the curves_rotation is wrong, but there's much less chances it is).

      
      
    import c4d  
    from c4d import gui  
    import math  
    #Welcome to the world of Python  
      
    obj = doc.GetActiveObject()  
      
    inicialMg = obj.GetMg()  
      
    #GET TRACKS  
      
    tracks_position = [  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))),  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))),  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0)))  
    ]  
    tracks_scale = [  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_SCALE, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))),  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_SCALE, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))),  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_SCALE, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0)))  
    ]  
    tracks_rotation = [  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))),  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))),  
      obj.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0)))  
    ]  
      
    #RETRIVE CURVES  
      
    curves_position = []  
    curves_scale = []  
    curves_rotation = []  
      
    for i in tracks_position:  
      if i:  
          curves_position.append(i.GetCurve())  
      else:  
          curves_position.append(None)  
      
    for i in tracks_scale:  
      if i:  
          curves_scale.append(i.GetCurve())  
      else:  
          curves_scale.append(None)  
      
    for i in tracks_rotation:  
      if i:  
          curves_rotation.append(i.GetCurve())  
      else:  
          curves_rotation.append(None)  
      
    #GET ABS VALS  
      
    parentObj = obj.GetUp()  
      
    if parentObj:  
      staticPosition_parent = parentObj.GetAbsPos()  
      staticScale_parent = parentObj.GetAbsScale()  
      staticRotation_parent = parentObj.GetAbsRot()  
      
      
    nParent = parentObj.GetUp()  
      
    #UNDO  
    doc.StartUndo()  
      
    #MOVE TARGET OBJECT FROM PARENT  
      
    if (nParent) :  
      doc.AddUndo(c4d.UNDOTYPE_DELETE, obj)  
      obj.Remove()  
      doc.AddUndo(c4d.UNDOTYPE_NEW, obj)  
      obj.InsertUnder(nParent)  
    else:  
      doc.AddUndo(c4d.UNDOTYPE_DELETE, obj)  
      obj.Remove()  
      doc.AddUndo(c4d.UNDOTYPE_NEW, obj)  
      doc.InsertObject(obj)  
      
    #UNDO  
    doc.EndUndo()  
    c4d.EventAdd()  
      
    #RESET GLOBAL MATRIX  
      
    obj.SetMg(inicialMg)  
      
    #COMPENSATE KEY VALUES  
      
    q = 0  
    for i in curves_rotation:  #ITERATE CURVES ARRAY  
      if i:    #ITERATE KEYS OF CURVE  
          q2 = 0  
          for l in range(i.GetKeyCount()) :  
              key = i.GetKey(q2)  
              value = key.GetValue() + staticRotation_parent[q]  
              key.SetValue(i, value)  
              q2 += 1  
      q += 1  
      
    # q = 0  
    # for i in curves_position:  #ITERATE CURVES ARRAY  
    #     if i:    #ITERATE KEYS OF CURVE  
    #         q2 = 0  
    #         for l in range(i.GetKeyCount()) :  
    #             key = i.GetKey(q2)  
    #             key.SetValue(i, keysModified_position[q][q2])  
    #             print(key.GetValue())  
    #             q2 += 1  
    #     q += 1  
      
    c4d.EventAdd()  
      
    


  • On 04/04/2017 at 12:57, xxxxxxxx wrote:

    I wish I had a simpler answer for you. But when you target animation tracks the code gets messy looking and complex. And it's easy to write code that works for some case and not others.
    The only way to get it right is by trial and error.

    Lists are very slow in Python. And if you have hundreds or thousands of keys. This code is going to be dog slow.
    I still think there might possibly be a better, cleaner way to change the keys.
    But I couldn't think of anything.

    -ScottA



  • On 05/04/2017 at 10:15, xxxxxxxx wrote:

    Just want to add, that the correct way is to work with transformation matrices instead of adding or multiplying for example rotation vectors.
    Otherwise I hope you guys are progressing nicely, as we are currently lacking the time to review these amounts of code.



  • On 05/04/2017 at 17:48, xxxxxxxx wrote:

    OMG, I just saw your procedural modeling tools on Labs and now you answer to my thread! %)
    I now investigate what's wrong with my code. It can take a long time as I have main work, but I will post updates when I go further so somebody could follow my steps if needed
    By the way, thanks for amazing software! And very friendly forum)



  • On 06/04/2017 at 02:18, xxxxxxxx wrote:

    I guess, it's no surprise that we (MAXON's SDK Team) do some development work as well, after all we also need some practicing with our SDKs. And now that we have MAXON Labs, we also have a way to make our side projects public.

    But in case of the Py-ParametericTools main kudos go to Thanassis/Noseman, who had the idea and needed it for a project of his. And if you take a look at the code, I'm sure you will see, there's not much to it, basically just calling SendModelingCommand().

    One more thing on this topic:
    Before anybody says "the SDK Team is wasting time into MAXON Labs instead of providing us with new examples". Rest assured, we are not. Py-ParametericTools are available in source (not encrypted) directly on MAXON Labs. And Riccardo is working heavily on new examples as well. Sometimes it's just a bit easier to release a binary, than an example source. The later simply needs some more time to be invested into cleanup, commenting and documentation. So, no work is done exclusively for MAXON Labs, quite the opposite, we just use MAXON Labs to make some of our SDK work available to a broader audience. I hope, our community doesn't mind.



  • On 06/04/2017 at 02:29, xxxxxxxx wrote:

    Don't care about what you should do.
    Every one here know the real job you do and it's pretty amazing !

    I also developped mine parametric tool for spline (offset and chamfer) it's very usefull. And for understand caching system.
    I was wondering if we can help the maxon labs by anyway or maybe for the github exemple.

    EDIT: Sorry for this off topic Intenditore.


Log in to reply