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.
    • 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