Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Set c4d.InExcludeData() has something didn't expect.

    Cinema 4D SDK
    s26 python windows
    2
    3
    464
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • DunhouD
      Dunhou
      last edited by

      Hi !

      Question :

      I try to insert a plain effector into a cloner InExcludeData , It worked , but sometimes i have to click twice but no error ( seems sometimes it clear the InExcludeData ) .

      How can I fix it ?

      @Windows11 22H1 C4D R2023.0.0

      Many Thanks

      Code

      from typing import Optional
      import c4d
      from math import radians
      
      doc: c4d.documents.BaseDocument  # The active document
      op: Optional[c4d.BaseObject]  # The active object, None if unselected
      
      def InsertNodeIntoInExclude(data : c4d.InExcludeData, node : c4d.BaseObject, index, isActive=True):
          """Inserts a node at a certain index into an InExcludeData."""
          dataCount = data.GetObjectCount()
          doc = node.GetDocument()
      
          if index > dataCount:
              raise IndexError(f"The index {index} is out of bounds.")    
      
          result, i = c4d.InExcludeData(), 0
          while data.ObjectFromIndex(doc, i):
              if i == index:
                  result.InsertObject(node, int(isActive))
              
              item = data.ObjectFromIndex(doc, i)
              flags = data.GetFlags(i)
              result.InsertObject(item, flags)
              i += 1
          return result
      
      # 量化旋转
      def mogragh_quantize_rotation(doc :c4d.documents.BaseDocument, cloner : c4d.BaseObject) -> c4d.BaseObject :
          """Inserts a quantize rotation effector into selected cloner."""
          doc.StartUndo()
      
          # Pass if selection is not a Cloner
          if cloner is None or cloner.GetType() != 1018544:
              raise RuntimeError("Select a cloner.")
          
          effector = c4d.BaseObject(1021337) # Plain    
          doc.InsertObject(effector,pred=cloner)
          doc.AddUndo(c4d.UNDOTYPE_NEWOBJ,effector)
          
      
          # Set plain to cloner InExclude
          inExData = cloner[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST]
          if inExData:
              if inExData.GetObjectCount() == 0:
                  try:
                      emptyData = c4d.InExcludeData()
                      emptyData.InsertObject(effector,1)
                      cloner[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST] = emptyData
                  except:
                      raise RuntimeError("Insert failed , try again .")
              else:
                  try:                
                      modifiedInExData = InsertNodeIntoInExclude(inExData, effector, 0)
                      doc.AddUndo(c4d.UNDOTYPE_CHANGE, cloner)
                      cloner[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST] = modifiedInExData 
                  except:
                      raise RuntimeError("Insert failed , try again .")
          else:
              raise RuntimeError("Failed to get Includedata .")
          
          # FieldObject
          field_object = c4d.modules.mograph.FieldObject(440000281) # ramdom field object
          doc.InsertObject(field_object,parent=effector)
          doc.AddUndo(c4d.UNDOTYPE_NEWOBJ,field_object)
          
          # Set Field List
          fieldList = effector[c4d.FIELDS]    
          layer = c4d.modules.mograph.FieldLayer(c4d.FLfield)
          layer.SetLinkedObject(field_object)
          fieldList.InsertLayer(layer)
          
          # Quantize Layer
          quantizeLayer = c4d.modules.mograph.FieldLayer(c4d.FLquantize)
          if quantizeLayer is None:
              raise MemoryError("Failed to create a Field Layer.")
      
          fieldList.InsertLayer(quantizeLayer)
          doc.AddUndo(c4d.UNDOTYPE_BITS, quantizeLayer)
          quantizeLayer.SetBit(c4d.BIT_ACTIVE)
          
          # Set
          effector.SetName(cloner.GetName() + ' ' + 'Plain')
          effector[c4d.ID_MG_BASEEFFECTOR_POSITION_ACTIVE] = False # Turn off Pos
          effector[c4d.ID_MG_BASEEFFECTOR_SCALE_ACTIVE] = False # Turn off Scale
          effector[c4d.ID_MG_BASEEFFECTOR_ROTATE_ACTIVE] = True # Turn On Pot
          effector[c4d.ID_MG_BASEEFFECTOR_ROTATION,c4d.VECTOR_X] = radians(360) # Set Rot at H(x) to 360 degrees
          
          field_object[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 1 # Set editor unvisible
          field_object.SetName(effector.GetName() + ' : ' + 'Quantize Rot') # Set Field Name to Effetor name + Function
          
          doc.AddUndo(c4d.UNDOTYPE_CHANGE, effector)
          effector[c4d.FIELDS] = fieldList
      
          doc.EndUndo()
          c4d.EventAdd() # Refresh Viewport
      
          return effector
      
      
      if __name__ == '__main__':
          doc = c4d.documents.GetActiveDocument()
          cloner = doc.GetActiveObject()
          mogragh_quantize_rotation(doc,cloner)
      

      https://boghma.com
      https://github.com/DunHouGo

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @Dunhou
        last edited by ferdinand

        Hey @dunhou,

        Thank you for reaching out to us and a special thanks for your postings which are always super clear. I think I already said it before, but it makes my life much easier.

        There are sort of two problems in your code:

        1. You set your inExData in a bit odd place, which can lead to update problems in some special cases, when you then after that start constructing your field list setup related to the effector referenced by the InExcludeData inExData. This should not cause problems in most cases, but for safety I would first construct my little effector/field rig in its entirety, and then reference it in the InExcludeData.
        2. Your InsertNodeIntoInExclude produced a few problems for me when I tried your script, I did not try to debug it, and instead just rewrote the function how I would write it.

        I have provided below my solution and the script does what I would expect it to do. The "click twice" thing you encountered was likely not an update error, in the sense of Cinema 4D not updating, but an index error due to how you fashioned your InsertNodeIntoInExclude function and then effectively breaking early in it on the InExcludeData iteration when there were some dangling links in it, in short, you made never sure that i reached dataCount - 1 and instead stopped when one of the links in the list returned None.

        If this does not solve your problem, I will have to ask for a more precise explanation of what constitutes "sometimes", as for me it does now what I would expect it to do.

        Cheers,
        Ferdinand

        The code:

        import c4d
        import typing
        
        doc: c4d.documents.BaseDocument  # The active document
        op: typing.Optional[c4d.BaseObject]  # The active object, None if unselected
        
        def GetUpdatedInexcludeData(data:c4d.InExcludeData, node:c4d.BaseObject, index:int, 
                                    isActive:bool=True) -> c4d.InExcludeData:
            """Inserts a node at a certain index into an InExcludeData.
            
            Note: I had to rewrite this because there were some problems with how the function
            behaved in the case of no or a singular element in the list.
            """
            # Bail when the user tries to add a node which is not part of a document and get the number
            # of elements in #data.
            doc: c4d.documents.BaseDocument = node.GetDocument()
            if not isinstance(doc, c4d.documents.BaseDocument):
                raise RuntimeError("Cannot add dangling node to InExcludeData")
        
            count: int = data.GetObjectCount()
        
            # Retrieve all objects in it and their flags as a list of (object, flags) pairs. Object access
            # can fail with ObjectFromIndex(), which is why we have to check if it returns not None, to
            # step over these "dangling" slots. This also implicitly assumes that all nodes in #data are
            # part of the same document #node is part of. To be more robust, you should pass in the document
            # of the node the InExcludeData #data was taken from, and then also test if #node is part of the
            # same document.
            rawData: list[tuple[c4d.BaseList2D, int]] = [
                (data.ObjectFromIndex(doc, i), data.GetFlags(i)) for i in range(count)
                if data.ObjectFromIndex(doc, i) != None
            ]
            # Insert the new node at the desired location in the list. Since Booleans are just integers in
            # disguise, int(#isActive) is equal to #isActive alone, but one can add it for verbosity.
            newIndex: int = index if index < (len(rawData) - 1 ) else 0
            rawData.insert(newIndex, (node, isActive))
        
            # Construct the new InExcludeData where #node is at #index with the flag #isActive.
            result: c4d.InExcludeData = c4d.InExcludeData()
            for node, flags in rawData:
                result.InsertObject(node, flags)
        
            return result
        
        # 量化旋转
        def mogragh_quantize_rotation(doc :c4d.documents.BaseDocument, cloner : c4d.BaseObject) -> c4d.BaseObject :
            """Inserts a quantize rotation effector into selected cloner."""
            doc.StartUndo()
        
            # Pass if selection is not a Cloner
            if cloner is None or cloner.GetType() != 1018544:
                raise RuntimeError("Select a cloner.")
            
            effector = c4d.BaseObject(1021337) # Plain    
            doc.InsertObject(effector,pred=cloner)
            doc.AddUndo(c4d.UNDOTYPE_NEWOBJ,effector)
        
            # --- Change: I removed here code and moved it towards the end of your script -------------------
            # --- End of changes ---------------------------------------------------------------------------
            
            # FieldObject
            field_object = c4d.modules.mograph.FieldObject(440000281) # random field object
            doc.InsertObject(field_object,parent=effector)
            doc.AddUndo(c4d.UNDOTYPE_NEWOBJ,field_object)
            
            # Set Field List
            fieldList = effector[c4d.FIELDS]    
            layer = c4d.modules.mograph.FieldLayer(c4d.FLfield)
            layer.SetLinkedObject(field_object)
            fieldList.InsertLayer(layer)
            
            # Quantize Layer
            quantizeLayer = c4d.modules.mograph.FieldLayer(c4d.FLquantize)
            if quantizeLayer is None:
                raise MemoryError("Failed to create a Field Layer.")
        
            fieldList.InsertLayer(quantizeLayer)
            doc.AddUndo(c4d.UNDOTYPE_BITS, quantizeLayer)
            quantizeLayer.SetBit(c4d.BIT_ACTIVE)
            
            # Set
            effector.SetName(cloner.GetName() + ' ' + 'Plain')
            effector[c4d.ID_MG_BASEEFFECTOR_POSITION_ACTIVE] = False # Turn off Pos
            effector[c4d.ID_MG_BASEEFFECTOR_SCALE_ACTIVE] = False # Turn off Scale
            effector[c4d.ID_MG_BASEEFFECTOR_ROTATE_ACTIVE] = True # Turn On Pot
            effector[c4d.ID_MG_BASEEFFECTOR_ROTATION,c4d.VECTOR_X] = c4d.utils.DegToRad(360) # Set Rot at H(x) to 360 degrees
            
            field_object[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 1 # Set editor unvisible
            field_object.SetName(effector.GetName() + ' : ' + 'Quantize Rot') # Set Field Name to Effetor name + Function
            
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, effector)
            effector[c4d.FIELDS] = fieldList
        
            # Set plain to cloner InExclude
            inExData = cloner[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST]
            
            # --- Changes: Your modified and moved code ----------------------------------------------------
            
            # I would add the effector here after you have created all the field object stuff, because it
            # makes a bit more sense to do it once you are done creating the little effector rig, but it 
            # is not technically required in most cases.
        
            # I am not quite sure why you did all the exception handling stuff in your code here, I assume
            # because you wanted to to capture the case that inExData is not what you expect it to be. I
            # would simply type check here if you want to be sure.
            if not isinstance(inExData, c4d.InExcludeData):
                raise RuntimeError("Whoopsie: ID_MG_MOTIONGENERATOR_EFFECTORLIST does not hold InExcludeData.")
            
            # Use your function to construct an InExcludeData where #effector sits at index 0 while copying
            # over the old data.
            cloner[c4d.ID_MG_MOTIONGENERATOR_EFFECTORLIST] = GetUpdatedInexcludeData(inExData, effector, 0)
        
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, cloner)
            # --- End of changes ---------------------------------------------------------------------------
        
            doc.EndUndo()
            c4d.EventAdd() # Refresh Viewport
        
            return effector
        
        
        if __name__ == '__main__':
            doc = c4d.documents.GetActiveDocument()
            cloner = doc.GetActiveObject()
            mogragh_quantize_rotation(doc,cloner)
        

        MAXON SDK Specialist
        developers.maxon.net

        DunhouD 1 Reply Last reply Reply Quote 0
        • DunhouD
          Dunhou @ferdinand
          last edited by

          @ferdinand Thanks for that solution.

          It's happy to hear that , I do try to make win-win clear post as I can to don't waste our time , hope it's a worthy work 😊

          I tested the new GetUpdatedInexcludeData function , it worked well , The "click twice" thing never happen again . It's the None condition I didn't realize before . It does happend when no or single object in the list .

          And for the "odd place" and the "exception ", it is a test to make sure all the things above work well( after that are all the settings ) . I forgot move to the right place . sorry to that

          Thanks for the caring code for c4d.utils.DegToRad(360) , I haven't notice this before and just stupid import radians😢

          https://boghma.com
          https://github.com/DunHouGo

          1 Reply Last reply Reply Quote 0
          • First post
            Last post