Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware 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

    "Axis Center " Script button reuse problem

    General Talk
    r21 python
    2
    3
    642
    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.
    • X
      x_nerve
      last edited by x_nerve

      Hi:
      I am a beginner of Python, my English is not very good, please forgive me.I wrote a script that was broadly similar to the new "Axis Center" feature in R21, but had difficulty writing an interactive interface.The main problem is that the first time you click the "Null" button, you can normally create a Null object, but the second time the error is reported, what is going on? How do buttons run repeatedly ?

      # -*- coding: UTF-8 -*-
      
       import c4d
      from c4d import gui
      
      __TITLE__ = "Axis Center"
      null = []
      
       class MyDialog(gui.GeDialog):
      def CreateLayout(self):
          self.SetTitle(__TITLE__ )
          self.GroupBegin(id=1000, flags=c4d.BFH_FIT, cols=2, title="Axes")
          self.GroupBorder(c4d.BORDER_GROUP_IN)
          self.GroupSpace(5, 5)
          self.GroupBorderSpace(10, 0, 10, 4)
          self.AddEditSlider(id = 1001, flags=c4d.BFH_RIGHT, initw=60, inith=15)
          self.AddEditSlider(id = 1002, flags=c4d.BFH_RIGHT, initw=60, inith=15)
          self.AddEditSlider(id = 1003, flags=c4d.BFH_RIGHT, initw=60, inith=15)
          self.AddCheckbox(id = 1004, flags=c4d.BFH_RIGHT, initw=80, inith=15, name="Null")
          self.GroupEnd()
      
          self.AddButton(1005, flags=c4d.BFH_LEFT, initw=180, inith=10, name="GO")
          return True
      
      def Command(self,id,msg):
          if id == 1004:
              null.append(c4d.BaseObject(5140))
          else:
              pass
      
          if id == 1005:
              main()
          return True
      
      def main():
      present = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
      if not present:
          print "The polygon object was not selected"
          gui.MessageDialog('The polygon object was not selected!')
          return
      
      for obj in present:
          pl  = obj.GetAllPoints()
          pc  = len(pl)
          sel = obj.GetPointS()
          sel = sel.GetAll(pc)
      
      indexe  = [i for i,o in enumerate(pl)]
      position  = [o for i,o in enumerate(pl)]
      il  = [i for i,o in enumerate(pl) if sel[i] == 1]
      spl = [o for i,o in enumerate(pl) if sel[i] == 1]
      
      if len(spl) < 1:
          print "no enough points"
          gui.MessageDialog('no enough points!')
          return False
      
      x_record =  [round(a[0],7) for a in spl if round(a[0],7) != -0.0]
      x_record += [0 for b in spl if round(b[0],7) == -0.0]
      
      y_record =  [round(c[1],7) for c in spl if round(c[1],7) != -0.0]
      y_record += [0 for d in spl if round(d[1],7) == -0.0]
      
      z_record =  [round(e[2],7) for e in spl if round(e[2],7) != -0.0]
      z_record += [0 for f in spl if round(f[2],7) == -0.0]
      
      x_record.sort(reverse=True)
      y_record.sort(reverse=True)
      z_record.sort(reverse=True)
      
      x = (round((x_record[0] - x_record[-1]) * dlg.GetFloat(1001) * 0.01 , 5)) + x_record[-1]
      y = (round((y_record[0] - y_record[-1]) * dlg.GetFloat(1002) * 0.01 , 5)) + y_record[-1]
      z = (round((z_record[0] - z_record[-1]) * dlg.GetFloat(1003) * 0.01 , 5)) + z_record[-1]
      
      m = obj.GetMg()
      mx   = obj.GetMg()
      mx.off = mx.Mul(c4d.Vector(x,y,z))
      
      matrix = []
      for orientation in position:
          mt = c4d.Matrix()
          mt.off = orientation
          m_mt = m.__mul__(mt)
          swop = m_mt.__invert__()
          result = swop.__mul__(mx)
          matrix.append(result.__invert__())
      
      doc.StartUndo()
      doc.AddUndo(c4d.UNDOTYPE_CHANGE,obj)
      
      obj.SetMg(mx)
      if not null:
          pass
      
      else:
          doc.InsertObject(null[0])
          null[0].SetMg(mx)
      
      point_position = [(t.off) for t in matrix]
      point_indexe   = [([indexe[z],point_position[z]]) for z in range(len(indexe))]
      set_point      = [obj.SetPoint(k[0],k[1]) for k in point_indexe]
      
      obj.Message(c4d.MSG_UPDATE)
      c4d.EventAdd()
      doc.EndUndo()
      
      if __name__=='__main__':
      __anchor__ = "\u98de\u821e\u7684\u56e2\u5b50"
      dlg = MyDialog()
      dlg.Open(c4d.DLG_TYPE_ASYNC, 450, 200, 300, 300)
      
      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        Hi,

        welcome to the forum and no worries, most people here do not speak perfect English. But you might want to do two things in the future: Check if the indentations are correct in your code and give us the exact exceptions if you run into them.

        In your code the problem lies in the way how you handle your "create null" checkbox. You will find details in the commented code below (which I have also sort of fixed for you). Only sort of, because there a quite a few other problems with your code if I am not totally misunderstanding your goals, which I have neither mentioned nor rectified. I also took the freedom of giving some unwanted stylistic advice 😉

        With all that nagging I feel that it is necessary to point out, that you did pretty well with your code, especially some of the list comprehensions are quite elegant for a beginner. Keep at it!

        # -*- coding: UTF-8 -*-
        """
        """
        
        import c4d
        from c4d import gui
        
        __TITLE__ = "Axis Center"
        null = []
        
        
        class MyDialog(gui.GeDialog):
            """You should put little bits of explanation what you are doing for
            yourself and others in Pythons doc strings, i.e. the thing we are
            currently typing in.
            """
        
            def CreateLayout(self):
                """
                """
                self.SetTitle(__TITLE__)
                self.GroupBegin(id=1000, flags=c4d.BFH_FIT, cols=2, title="Axes")
                self.GroupBorder(c4d.BORDER_GROUP_IN)
                self.GroupSpace(5, 5)
                self.GroupBorderSpace(10, 0, 10, 4)
                self.AddEditSlider(id=1001, flags=c4d.BFH_RIGHT, initw=60, inith=15)
                self.AddEditSlider(id=1002, flags=c4d.BFH_RIGHT, initw=60, inith=15)
                self.AddEditSlider(id=1003, flags=c4d.BFH_RIGHT, initw=60, inith=15)
                self.AddCheckbox(id=1004, flags=c4d.BFH_RIGHT,
                                 initw=80, inith=15, name="Null")
                self.GroupEnd()
        
                self.AddButton(1005, flags=c4d.BFH_LEFT,
                               initw=180, inith=10, name="GO")
                return True
        
            def Command(self, id, msg):
                """
                """
                # Here lies the major problem with your script, the following lines
                # will only be executed when you have ticked the checkbox. So when
                # you press the 'GO' button twice in a row after ticking the
                # checkbox, only one null will be inserted. This then means that
                # you will try to insert the same object again into the document.
        
                # This also means that your 'null' list will be modified when
                # the user unchecks the checkbox. In the code in 'main' you are also
                # grabbing the wrong null, because you pick the first null (null[0]),
                # but here you do append and do not insert in front.
        
                # Since you do not use the list as a list at all, I simply removed it
                # all together, because there is also the problem of ambiguity which
                # null object is meant to be used for which polygon object.
        
                # Also module scope variables should be all uppercase and not
                # ambiguous, something like 'NULL_OBJECTS' for example.
        
                # if id == 1004:
                #     null.append(c4d.BaseObject(5140))
                # else:
                #     pass
        
                if id == 1005:
                    # Poll the checkbox if it is selected.
                    insert_null = self.GetBool(1004)
                    # And pass is to your 'main' function as an argument.
                    main(insert_null)
                return True
        
        
        def main(insert_null):
            """This function should probably be split into multiple parts, as it does
            too many things at once.
        
            Args:
                insert_null (bool): If to create a null object or not.
            """
            # Its good to be defensive, but you are only going half way here,
            # you should actually check if the object is a PolygonObject. This
            # is more a opinion, but I think you should also use exceptions and
            # not 'print'. You are also using the method to get all objects, but
            # do not really use it in the end.
            nodes = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
            # if not nodes:
            #     print "The polygon object was not selected"
            #     gui.MessageDialog('The polygon object was not selected!')
            #     return
            if not nodes:
                raise ValueError("No objects selected.")
            for node in nodes:
                if not isinstance(node, c4d.PolygonObject):
                    raise TypeError("Selection contains non PolygonObject.")
        
            # As already mentioned, this does not make much sense, you calculate
            # all this stuff for every object and throw it away after that without
            # using it, only the stats calculated for the last object will be used.
        
            # On a side note, the variable naming is not very good here. I know that
            # you see a lot of code like that out in the wild, and especially MAXON
            # themselves are also a big offender in this department, but you should
            # not use abbreviated variable names, as they do make things harder to
            # read.
            for obj in nodes:
                pl = obj.GetAllPoints()
                pc = len(pl)
                sel = obj.GetPointS()
                sel = sel.GetAll(pc)
        
            # This is quite nice and pythonic, especially for a beginner (good job!),
            # but still could be compacted a lot.
        
            # selected_point_index = {i: p for i, p in enumerate(pl) if sel[i]}
        
            indexe = [i for i, o in enumerate(pl)]
            position = [o for i, o in enumerate(pl)]
            il = [i for i, o in enumerate(pl) if sel[i] == 1]
            spl = [o for i, o in enumerate(pl) if sel[i] == 1]
        
            if len(spl) < 1:
                print "no enough points"
                gui.MessageDialog('no enough points!')
                return False
        
            # I think you are trying to compute the bounding box here. First of all
            #  there is the method 'BaseObject.GetRad' which will give you the
            #  bounding box vector for an object.
        
            # While the first line of each 'record' line pair is a bit complicated,
            # but all together fine, the second one does not make any sense
            # whatsoever, especially paired with the sorting you do employ
            # afterwards. I did not touch this code, because I do not get what
            # you were after, but here almost certainly lies a bug.
        
            x_record = [round(a[0], 7) for a in spl if round(a[0], 7) != -0.0]
            x_record += [0 for b in spl if round(b[0], 7) == -0.0]
        
            y_record = [round(c[1], 7) for c in spl if round(c[1], 7) != -0.0]
            y_record += [0 for d in spl if round(d[1], 7) == -0.0]
        
            z_record = [round(e[2], 7) for e in spl if round(e[2], 7) != -0.0]
            z_record += [0 for f in spl if round(f[2], 7) == -0.0]
        
            x_record.sort(reverse=True)
            y_record.sort(reverse=True)
            z_record.sort(reverse=True)
        
            # This part is fine again for the bounding box calculation.
        
            x = (round((x_record[0] - x_record[-1]) *
                       dlg.GetFloat(1001) * 0.01, 5)) + x_record[-1]
            y = (round((y_record[0] - y_record[-1]) *
                       dlg.GetFloat(1002) * 0.01, 5)) + y_record[-1]
            z = (round((z_record[0] - z_record[-1]) *
                       dlg.GetFloat(1003) * 0.01, 5)) + z_record[-1]
        
            m = obj.GetMg()
            mx = obj.GetMg()
            mx.off = mx.Mul(c4d.Vector(x, y, z))
        
            # Also fine, but underscore methods are not intended to be called
            # directly, instead you should use the operator (this is only syntactic
            # sugar)
            matrix = []
            for orientation in position:
                mt = c4d.Matrix()
                mt.off = orientation
                m_mt = m.__mul__(mt)  # m * mt
                swop = m_mt.__invert__()  # ~m_mt
                result = swop.__mul__(mx)
                matrix.append(result.__invert__())
        
            doc.StartUndo()
            doc.AddUndo(c4d.UNDOTYPE_CHANGE, obj)
        
            obj.SetMg(mx)
        
            # Here is the only place where you use your 'null' list again. Since I
            # commented the code out in the dialog, we have to this instead.
        
            # if not null:
            #     pass
            # else:
            #     doc.InsertObject(null[0])
            #     null[0].SetMg(mx)
        
            # Create a null and set its global matrix to your 'mx' matrix if we
            # indicated to do so with the function call.
            if insert_null:
                node = c4d.BaseList2D(c4d.Onull)
                node.SetMg(mx)
                doc.InsertObject(node)
        
            point_position = [(t.off) for t in matrix]
            point_indexe = [([indexe[z], point_position[z]])
                            for z in range(len(indexe))]
            set_point = [obj.SetPoint(k[0], k[1]) for k in point_indexe]
        
            obj.Message(c4d.MSG_UPDATE)
            c4d.EventAdd()
            doc.EndUndo()
        
        
        if __name__ == '__main__':
            __anchor__ = "\u98de\u821e\u7684\u56e2\u5b50"
            dlg = MyDialog()
            dlg.Open(c4d.DLG_TYPE_ASYNC, 450, 200, 300, 300)
        

        Cheers.
        zipit

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • X
          x_nerve
          last edited by

          Thank you very much for your help to solve the previous doubts. I will continue to work hard.

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