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

    Enable Isoline Editing in tool plugin

    Cinema 4D SDK
    2
    13
    2.2k
    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.
    • M
      mdr74
      last edited by

      Thank you.
      But I realise I used the wrong option.
      I was trying to enable the isoline editing while in polygon mode inside my tool.
      I know that some tools, like Loop cut for example, are not using isoline display, but I don't know if it's a choice or is it impossible to enable it.
      While using my tool, in the viewport settings the option is enabled, but I think it also depends on some unknown features

      Sorry for the confusion.
      Do you need me to change the post title?

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

        Hello,

        I did change the title for you. In the script I did post, you only have to change the parameter symbol, i.e., c4d.BASEDRAW_DATA_DEFORMEDEDIT to c4d.BASEDRAW_DATA_SDSEDIT. As I said in the script, a BaseDraw is just another BaseList2D, nothing special about it.

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        M 1 Reply Last reply Reply Quote 0
        • M
          mdr74 @ferdinand
          last edited by

          @ferdinand sorry to bother you again.
          I think I'm not able to exactly describe my request, maybe it's my poor english or bad terminology 😉

          I'm trying to create a tool plugin for expanding the selection tool with custom functionality.
          And when I switch to it, the isoline editing (still enabled in the basedraw) seems disabled in the viewport.

          If I edit a polygon object under HN I'd like to have the same display that I have with the move tool, but I have the same display of the loop cut tool. I don't know if it's possible or where I have to set a specific option (maybe in the draw method ?)

          I added this:

          print(bd[c4d.BASEDRAW_DATA_SDSEDIT])
          print(bd.IsViewOpen(doc))
          

          in the Draw and MouseInput method and the result is always 1 and True

          I'm using ViewportSelect GetNearestPolygon to select the polygon under the mouse.

          I hope it's everything clear...🤞

          thank you

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

            Hello @mdr74,

            @ferdinand sorry to bother you again I think I'm not able to exactly describe my request, maybe it's my poor english or bad terminology 😉

            You are not bothering us, we are here for answering questions and communication can be hard, especially when it must be conducted in a language which is not our first/native language. We understand this, so no need to feel rushed.

            I'm trying to create a tool plugin for expanding the selection tool with custom functionality. And when I switch to it, the isoline editing (still enabled in the basedraw) seems disabled in the viewport.

            Hm, you lost me a bit there. What do you mean by 'expanding the selection tool'? You cannot really extend existing functionalities of Cinema 4D via our API, at least it is not clear to me what you mean by that. When you want to implement a tool in Python, you must implement a ToolData plugin. But you cannot extend the ToolData or more specifically ToolDescriptionData plugins provided by Cinema 4D, e.g. one of the selection tools. Are you writing a Python script for the Script Manger which does 'some stuff' to a scene and then invokes a tool via CallCommand()?

            If I edit a polygon object under HN I'd like to have the same display that I have with the move tool, but I have the same display of the loop cut tool. I don't know if it's possible or where I have to set a specific option (maybe in the draw method ?)

            Hm, again, the context would be helpful, but I assume you are talking about ToolData.Draw here. So, you seem to be in a ToolData context. Invoking a tool should not change the viewport representation of objects and you certainly do not have to draw stuff yourself, except for things like handles and more complex information in special cases. I am also not quite sure what is meant with "edit" here, since the move tool is also an edit operation? Are you talking about the difference between Model mode and the polygon data modes Point, Edge and Polygon?

            I added this:

            print(bd[c4d.BASEDRAW_DATA_SDSEDIT])
            print(bd.IsViewOpen(doc))
            

            in the Draw and MouseInput method and the result is always 1 and True. I'm using ViewportSelect GetNearestPolygon to select the polygon under the mouse. I hope it's everything clear.

            Well, as long you or the user does not change BASEDRAW_DATA_SDSEDIT, it is not surprising that it does not change its value. So, the bottom line seems to be here, that you have a ToolData plugin and as soon as the user does activate that ToolData plugin, the viewports of Cinema 4D ignore if they have the Isoline Editing flag enabled or not. They will then always draw geometry as if BASEDRAW_DATA_SDSEDIT was False, am I understanding this correctly?

            This is certainly not a usual behavior. Could be a bug or a mistake on your side, hard to say without code. To get here any further, you will have to share code with us, as we otherwise cannot confirm a bug or point out a misconception/bug of your code. You can share code privately with us if you are not at liberty of sharing it publicly.

            The code should be a condensed example if possible and be accompanied by a description of when and where something is not working as you would expect it to. Read more about sharing posts and how formulate technical questions in our Forum Guidelines.

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            M 1 Reply Last reply Reply Quote 0
            • M
              mdr74 @ferdinand
              last edited by mdr74

              @ferdinand
              Hi,
              here's the code ( a simplified version but with the same problem 😞 )

              import c4d, os, sys, pprint, itertools, math, random, time
              from dataclasses import dataclass, field
              from typing import List
              from itertools import islice
              from c4d import plugins, gui
              from c4d import utils as u
              from c4d import Vector as vc
              
              # Be sure to use a unique ID obtained from www.plugincafe.com
              TOOL_PLUGIN_ID = 1045691
              GET_KEYS = False
              DEBUG = False
              
              
              class TimeController(object):
                  """This object makes sure to sleep as long as necessary to keep a specific
                  time-rate."""
              
                  def __init__(self, twait):
                      self.twait = twait
                      self.lastCall = 0
              
                  def __call__(self):
                      t = time.process_time()
                      delta = t - self.lastCall
                      if delta < self.twait:
                          time.sleep(self.twait - delta)
                      self.lastCall = time.process_time()
              
              
              class ToolSelector(c4d.plugins.ToolData):
                  def __init__(self):
                      self.obj = None
                      self.base_select = None
                      self.mouseCoordinate = None
                      self.direction = None
                      self.poly_sel_array = []
                      self.pick_data = False
                      self.selector_tag = None
              
                  def InitTool(self, doc, data, bt):
                      self.mouseCoordinate = None
                      self.direction = None
                      self.pick_data = False
                      self.poly_sel_array = []
                      self.selector_tag = None
                      self.obj = None
              
                      return True
              
                  def InitObject(self, doc):
                      if self.obj:
                          self.base_select = self.obj.GetPolygonS()
                          doc.SetActiveObject(self.obj, 0)
                          doc.SetMode(c4d.Mpolygons)
              
                      c4d.EventAdd()
              
                      return True
              
              
                  def FreeTool(self, doc, data):
                      self.obj = None
              
                  def GetState(self, doc):
                      return c4d.CMD_ENABLED
              
                  def Debug(self, msg):
                      c4d.CallCommand(13957)  # Konsole loeschen
              
                      return True
              
                  def KeyboardInput(self, doc, data, bd, win, msg):
                      key = msg.GetInt32(c4d.BFM_INPUT_CHANNEL)
                      cstr = msg.GetString(c4d.BFM_INPUT_ASC)
                      mod = msg.GetInt32(c4d.BFM_INPUT_QUALIFIER)
              
              
                      if key == c4d.KEY_ESC:
                          c4d.CallCommand(300001111)
              
                          return True
              
              
                      elif msg[c4d.BFM_INPUT_ASC] == "\t":
                          self.pick_data = True
                          c4d.DrawViews(
                                  c4d.DRAWFLAGS_ONLY_ACTIVE_VIEW
                                  | c4d.DRAWFLAGS_NO_THREAD
                                  | c4d.DRAWFLAGS_NO_ANIMATION
                              )
                          c4d.EventAdd()
              
                          return True
              
                      return False
              
                  def CallViewportSelect(self, viewport_select, bd, doc):
                      frame = bd.GetFrame()
                      w = frame["cr"] - frame["cl"] + 1
                      h = frame["cb"] - frame["ct"] + 1
              
                      viewport_select.Init(
                          w,
                          h,
                          bd,
                          [self.obj],
                          c4d.Mpolyedgepoint,
                          True,
                          c4d.VIEWPORTSELECTFLAGS_USE_HN | c4d.VIEWPORTSELECTFLAGS_USE_DEFORMERS | c4d.VIEWPORTSELECTFLAGS_FORCE_USE_DEFORMERS,
                      )
              
                  def MouseInput(self, doc, data, bd, win, msg):
                      if not self.obj:
                          self.obj = doc.GetActiveObject() if doc.GetActiveObject() else None
              
                          if self.obj:
                              self.InitObject(doc)
                          else:
                              return True
              
                      bc = c4d.BaseContainer()
                      pick_poly = None
              
                      mouse_x = msg[c4d.BFM_INPUT_X]
                      mouse_y = msg[c4d.BFM_INPUT_Y]
              
                      myPoint = c4d.Vector(mouse_x, mouse_y, 0)
              
                      viewport_select = c4d.utils.ViewportSelect()
                      self.CallViewportSelect(viewport_select, bd, doc)
              
                      win.MouseDragStart(c4d.KEY_MLEFT, mouse_x, mouse_y, c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE)
              
                      if msg[c4d.BFM_INPUT_QUALIFIER] != c4d.QSHIFT:
                          self.base_select.DeselectAll()
                          self.poly_sel_array = []
              
                      controller = TimeController(40 / 1000)
              
                      while True:
                          controller()
              
                          result, m_deltax, m_deltay, channels = win.MouseDrag()
                          delta = c4d.Vector(m_deltax, m_deltay, 0)
                          myPoint = myPoint + delta
              
                          if result == c4d.MOUSEDRAGRESULT_CONTINUE:
                              self.dragging = True
              
                              pick_poly = viewport_select.GetNearestPolygon(self.obj, int(myPoint.x), int(myPoint.y))
              
                              if pick_poly["i"] not in self.poly_sel_array:
                                  self.poly_sel_array.append(pick_poly["i"])
              
                              doc.AddUndo(c4d.UNDOTYPE_CHANGE_SELECTION, self.obj)
                              self.base_select.Select(pick_poly["i"])
              
                              c4d.DrawViews(
                                  c4d.DRAWFLAGS_ONLY_ACTIVE_VIEW
                                  | c4d.DRAWFLAGS_NO_THREAD
                                  | c4d.DRAWFLAGS_NO_ANIMATION
                              )
              
                          elif result in (c4d.MOUSEDRAGRESULT_FINISHED, c4d.MOUSEDRAGRESULT_ESCAPE):
                              self.dragging = False
                              break
              
                      win.MouseDragEnd()
              
                      if pick_poly:
                          if pick_poly["i"] not in self.poly_sel_array:
                              self.poly_sel_array.append(pick_poly["i"])
              
                      c4d.EventAdd()
              
                      return True
              
                  def Draw(self, doc, data, bd, bh, bt, flags):
              
                      return c4d.DRAWRESULT_OK
              
                  def GetCursorInfo(self, doc, data, bd, x, y, bc):
                      if bc.GetId() == c4d.BFM_CURSORINFO_REMOVE:
                          self.mouseCoordinate = c4d.Vector(-1)
                          return True
              
                      c4d.DrawViews()
              
                      self.mouseCoordinate = c4d.Vector(x, y, 0)
              
                      if self.pick_data:
                          mouse_x = x
                          mouse_y = y
              
                          viewport_select = c4d.utils.ViewportSelect()
              
                          pick_objects = viewport_select.PickObject(
                              bd, doc, int(mouse_x), int(mouse_y), rad=0, flags=c4d.VIEWPORT_PICK_FLAGS_0
                          )
              
                          if pick_objects:
                              if pick_objects[0] != self.obj:
                                  self.poly_sel_array = []
              
                              self.obj = pick_objects[0]
              
                              while not isinstance(self.obj, c4d.PolygonObject):
                                  self.obj = ObjectData(self.obj.GetDown())
              
              
                              self.InitObject(doc)
                              self.pick_data = False
              
                          c4d.EventAdd()
                          return True
              
                      return True
              
                  def AllocSubDialog(self, bc):
              
                      return SettingsDialog(None)
              
              
              if __name__ == "__main__":
              
                  plugins.RegisterToolPlugin(
                      id=TOOL_PLUGIN_ID,
                      str="Tool Selector",
                      info=0,
                      icon=None,
                      help="tool selector",
                      dat=ToolSelector(),
                  )
              
              
              

              and two images to show what I'm trying to reach:

              with Mpolygons mode and the move tool active:

              image.jpg

              and when my tool is active:

              image.jpg

              what I meant with "expanding" is creating a tool that can remember the selection order of polygons.
              and use this selection for custom functions.

              thanks again 😊
              please tell me if you need more information

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

                Hello @mdr74,

                thanks, that is okay, will have a look.

                Cheers,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

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

                  Hello @mdr74,

                  I apologize for the longer wait, but due to the length of your code example I pushed dealing with the example to a spot where I had more time. The solution simple though, as you never tell your plugin implementation, i.e., Cinema 4D, that you do want to support editing modes. This is done with the info argument passed on plugin registration. See attached example at the end for details.

                  Cheers,
                  Ferdinand

                  """Example for a ToolData plugin which supports editing modes and the viewport
                  conventions that come with them.
                  
                  As discussed in:
                      https://developers.maxon.net/forum/topic/13415/
                  """
                  
                  import c4d
                  
                  TOOL_PLUGIN_ID = 1045691
                  
                  class ToolSelector(c4d.plugins.ToolData):
                      """Just a dummy tool which does not do anything.
                      """
                      pass
                  
                  if __name__ == "__main__":
                      c4d.plugins.RegisterToolPlugin(
                          id=TOOL_PLUGIN_ID,
                          str="Tool Selector",
                          # You did pass in here 0, i.e., no flags. But in order to support edit
                          # states, e.g., Mpolygon, one has to pass in PLUGINFLAG_TOOL_EDITSTATES
                          # Note that this flag is a bitmask, so one could also pass in for
                          # example:
                          #
                          #   info= c4d.PLUGINFLAG_TOOL_EDITSTATES | c4d.PLUGINFLAG_TOOL_TWEAK
                          #
                          # to support both edit modes and tweak mode. So, one can chain together
                          # as many flags as one wants with the or-operator.
                          info=c4d.PLUGINFLAG_TOOL_EDITSTATES,
                          icon=None,
                          help="tool selector",
                          dat=ToolSelector(),
                      )
                  
                  

                  MAXON SDK Specialist
                  developers.maxon.net

                  M 1 Reply Last reply Reply Quote 1
                  • M
                    mdr74 @ferdinand
                    last edited by

                    @ferdinand
                    😊 Thanks.
                    I will be away in the next days.
                    I will try it as soon as possible and inform you about the result.

                    P.S . when the "info" is set as in your example, the isoline editing checkbox in the viewport configuration is automatically seen by the plugin or I have to configure the basedraw setting in the code?

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

                      Hi @mdr74,

                      it will just carry out what the current state of the BaseDraw instance is. So, when you always want isoline editing to be enabled with your tool, you must implement it yourself like described in my first posting here.

                      Without PLUGINFLAG_TOOL_EDITSTATES you will always get the following viewport output while in one of the edit modes with your plugin active, no matter if BASEDRAW_DATA_SDSEDIT is enabled for the BaseDraw or not:
                      alt text

                      With the flag PLUGINFLAG_TOOL_EDITSTATES, you will get the following viewport output when BASEDRAW_DATA_SDSEDIT is enabled for the viewport:

                      and the following output when BASEDRAW_DATA_SDSEDIT is disabled for the viewport:
                      alt text

                      Cheers,
                      Ferdinand

                      MAXON SDK Specialist
                      developers.maxon.net

                      1 Reply Last reply Reply Quote 0
                      • M
                        mdr74
                        last edited by

                        Thanks Ferdinand.
                        with info=c4d.PLUGINFLAG_TOOL_EDITSTATES everything works fine.

                        I mark your answer as correct.
                        is it the right way to set the topic as solved?

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

                          Hi @mdr74,

                          yes, it is and thanks for doing it!

                          Cheers,
                          Ferdinand

                          MAXON SDK Specialist
                          developers.maxon.net

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