draw line into 3d Space with two mouse clicks



  • Hi everyone,

    I searched and found several snipets (e.g. liquid painter example) but my main problem I have is unsolved.
    I need two mouse inputs and no drag input so ...

    Goal:

    1. First MouseClick Corrdinates stored A.
    2. Second MouseClick Coordinates stored B.
    3. Result: Line inbetween thes two clicks converted to 3D Space (don't know if my code is corect yet) in viewport to draw a sticky line.
    4. Bonus Kill line on ESC key presed.

    I got the first click but then my tool sets the first Coordinates also for the second.
    How do I tell to wait for second Mouseclick?
    My drawn line is not sticky when I move my 3d view it disapears.

    Thank yo for your time.
    mogh

        def drawvector (self, p1, p2, bd):     
            pencolor=c4d.Vector(255,255,0)
            bd.SetPen(pencolor)
            doc = c4d.documents.GetActiveDocument()
            camera = bd.GetSceneCamera(doc)
            if camera is None: camera = bd.GetEditorCamera()
            mCam = camera.GetMg()
            
            #bd.SetMatrix_Matrix(camera, mCam, 4) #worked in view as 2D X Y Cords
            bd.SetMatrix_Matrix(None, c4d.Matrix(), 4)
            bd.DrawLine(p1, p2, c4d.NOCLIP_Z)
            c4d.DrawViews( c4d.DA_ONLY_ACTIVE_VIEW | c4d.DA_NO_THREAD | c4d.DA_NO_ANIMATION )
            print "Vector: from P1 to P2: ", p1, p2
            return c4d.TOOLDRAW_HANDLES|c4d.TOOLDRAW_AXIS
     
        def MouseInput(self, doc, data, bd, win, msg):
            bd = doc.GetActiveBaseDraw() 
            pointarray = []
            #pointarray.append(bd.SW(c4d.Vector(100,100, 100)))
            mx = msg[c4d.BFM_INPUT_X]
            my = msg[c4d.BFM_INPUT_Y]
            mz = msg[c4d.BFM_INPUT_Z]
    
            dx = 0.0
            dy = 0.0
    
            if len(pointarray) < 2:
                if c4d.gui.GetInputState(c4d.BFM_INPUT_MOUSE ,c4d.BFM_INPUT_MOUSELEFT, msg):
                    if msg[c4d.BFM_INPUT_VALUE] == 1: #BFM_INPUT_VALUE
                        print "Left Mouse clicked at cord x,y,z: ", mx, my, mz                    
                        print "3d converted: ", bd.SW(c4d.Vector(mx, my, mz))
    
                        pointarray.append(bd.SW(c4d.Vector(mx, my, mz)))
            
            #print "pointarray: ", pointarray
            
            if len(pointarray) == 2:
                self.drawvector(pointarray[0], pointarray[1], bd)
            return True
    


  • hi,

    we are here to help the best we can, beginner or not :)

    i've created an example that will do what you want so you have a base to start.
    I've picked the liquid example (that's why it's nice ^^) and modified it.
    I choose to add the point when you left click. But i could also create a dragloop (like in the liquid paint tool) to "wait" until the mouse is released.

    Fill free to ask questions about it.

    About your question GetNearestPoint, can you open a new thread ? (we like one question per thread so it's easier to search for answer)

    import c4d
    import os
    
    # Be sure to use a unique ID obtained from www.plugincafe.com
    PLUGIN_ID = 1000001
    
    
    class SettingsDialog(c4d.gui.SubDialog):
        """
        Dialog to display option in the ToolData, in this case the Sphere size.
        """
        parameters = {}
    
        def __init__(self, arg):
            # Checks if the argument passed is a dictionary
            if not isinstance(arg, dict):
                raise TypeError("arg is not a dict.")
    
            self.parameters = arg
    
        def CreateLayout(self):
            """
            This Method is called automatically when Cinema 4D Create the Layout (display) of the GeDialog.
            """
            value = self.parameters["line_color"]
            if self.GroupBegin(id=1000, flags=c4d.BFH_SCALEFIT, cols=2, rows=1):
                self.GroupBorderSpace(10, 10, 10, 10)
    
                # Creates a Static text and a number input
                self.AddColorChooser(id=1001, flags=c4d.BFV_TOP, initw=120,  layoutflags=c4d.DR_COLORFIELD_NO_BRIGHTNESS)
                
    
                # Defines the default values
                self.SetColorField(id=1001, color=value, brightness = 1.0, maxbrightness = 1.0, flags = 0)
            self.GroupEnd()
            return True
    
        def Command(self, messageId, msg):
            """
              This Method is called automatically when the user clicks on a gadget and/or changes its value this function will be called.
              It is also called when a string menu item is selected.
             :param messageId: The ID of the gadget that triggered the event.
             :type messageId: int
             :param bc: The original message container
             :type bc: c4d.BaseContainer
             :return: False if there was an error, otherwise True.
             """
            # When the user change the Gadget with the ID 1002 (the input number field)
    
            if messageId == 1001:
                # Stores the value in the parameter variable
                self.parameters["line_color"] = self.GetColorField(1001)["color"]
                # Update the view to redraw the lines
                c4d.DrawViews(c4d.DA_ONLY_ACTIVE_VIEW | c4d.DA_NO_THREAD | c4d.DA_NO_ANIMATION)
            
            return True
    
    
    class DrawLine(c4d.plugins.ToolData):
        """Inherit from ToolData to create your own tool"""
        def __init__(self):
            self.lines = []
            self.mouseCoordinate = c4d.Vector(-1)
            self.data = {"line_color":c4d.Vector(0, 0, 1)}
    
        def GetState(self, doc):
            """
            Called by Cinema 4D to know if the tool can be used currently
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :return: True if the tool can be used, otherwise False.
            """
            return c4d.CMD_ENABLED
    
        def MouseInput(self, doc, data, bd, win, msg):
            """
            Called by Cinema 4D, when the user click on the viewport and the tool is active.
            Mainly the place to do moue interaction from the viewport.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data:  The tool settings container.
            :type data: c4d.BaseContainer
            :param bd:  The BaseDraw object of the active editor view.
            :type bd: c4d.BaseDraw
            :param win: The EditorWindow object for the active editor view.
            :type win: c4d.gui.EditorWindow
            :param msg: The original message container.
            :type msg: c4d.BaseContainer
            :return: False if a problem occurred during this function.
            """
            # Retrieves which clicks is currently clicked
            device = 0
            if msg[c4d.BFM_INPUT_CHANNEL ]== c4d.BFM_INPUT_MOUSELEFT:
                device = c4d.KEY_MLEFT
            else:
                return True
       
            
            # Retrieves the X/Y screen position of the mouse.
            mx = msg[c4d.BFM_INPUT_X]
            my = msg[c4d.BFM_INPUT_Y]
    
            # adds the position to the array
            myPoint = c4d.Vector (mx , my, 0)
            self.lines.append(myPoint)
    
            
            # Pushes an update event to Cinema 4D
            c4d.EventAdd()
            return True
    
        def Draw(self, doc, data, bd, bh, bt, flags):
            """
            Called by Cinema 4d when the display is updated to display some visual element of your tool in the 3D view.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data: The tool settings container.
            :type data: c4d.BaseContainer.
            :param bd: The editor's view.
            :type bd: c4d.BaseDraw
            :param bh: The BaseDrawHelp editor's view.
            :type bh: c4d.plugins.BaseDrawHelp
            :param bt: The calling thread.
            :type bt: c4d.threading.BaseThread
            :param flags: The current drawing pass.
            :type flags: TOOLDRAWFLAGS
            :return: The result of the drawing (most likely c4d.DRAWRESULT_OK)
            """
    
            # Resets the drawing matrix to the world space matrix.
            bd.SetMatrix_Matrix(None, c4d.Matrix())
            
            # set the color for already drawn lines
            color = self.data["line_color"]
            bd.SetPen(color)
            if not flags:
                # draw lines for each group of two points
                for pa, pb in zip(self.lines[::2], self.lines[1::2]):
                    bd.DrawLine2D(pa, pb)
    
            # if the len for lines is odd, we need to draw a line to the mouse cursor's position
            if len(self.lines) % 2 == 1 and self.mouseCoordinate != c4d.Vector(-1):
                lastPoint = self.lines[-1]
                # the last line is green
                bd.SetPen(c4d.Vector(0, 1, 0))
                bd.DrawLine2D(lastPoint, self.mouseCoordinate)
    
            return c4d.TOOLDRAW_NONE
    
        def GetCursorInfo(self, doc, data, bd, x, y, bc):
            """
            Called by Cinema 4D when the cursor is over editor window to get the state of the mouse pointer.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data: The tool settings container.
            :type data: c4d.BaseContainer
            :param bd: The editor's view.
            :type bd: c4d.BaseDraw
            :param x: The x coordinate of the mouse cursor relative to the top-left of the currently active editor view.
            :type x: float
            :param y:The y coordinate of the mouse cursor relative to the top-left of the currently active editor view.
            :type y: float
            :param bc: The container to store the result in.
            :type bc: c4d.BaseContainer
            :return:
            """
            # If the cursor has left a user area, set the mouseCoordinate to -1 and return True
            if bc.GetId() == c4d.BFM_CURSORINFO_REMOVE:
                self.mouseCoordinate = c4d.Vector(-1)
                return True
            # if the mouse is coming back to the viewport, maybe we need to draw the line from last point, we ask for a redraw
            c4d.DrawViews()
            # we set the mouse coordinate
            self.mouseCoordinate = c4d.Vector(x, y, 0)
            
            # Sets the cursor.
            bc.SetInt32(c4d.RESULT_CURSOR, c4d.MOUSE_POINT_HAND)
            return True
    
        def AllocSubDialog(self, bc):
            """
            Called by Cinema 4D To allocate the Tool Dialog Option.
            :param bc: Currently not used.
            :type bc: c4d.BaseContainer
            :return: The allocated sub dialog.
            """
    
            return SettingsDialog(getattr(self, "data", {"line_color": c4d.Vector(1, 0, 0)}))
    
    
    if __name__ == "__main__":
        # Registers the tool plugin
        c4d.plugins.RegisterToolPlugin(id=PLUGIN_ID,
                                       str="draw line",
                                       info=0, icon=None,
                                       help="drawing line",
                                       dat=DrawLine())
    
    

    cheers,
    Manuel



  • Hi,
    You should store point inside your mouseInput and draw them using the Draw function, you should have a look at this thread

    For your next threads, please help us keeping things organised and clean. I know it's not your priority but it really simplify our work here.

    Let me know if it's still not working.

    Cheers,
    Manuel



  • @m_magalhaes said in draw line into 3d Space with two mouse clicks:

    mouseInput

    thanks for pointing me to the forum rules.

    anyway the other thread doesn't help me that much hence it is also just a copy of liquid painter ... with a line ...

    I need "click" + Click + n-click = draw line - not drag.

    And the line painting is a problem (perhaps you are telling me to put the draw into the mouse function to make it sticky I can't tell but i will try)

    but more so my Mouseinput ...

    hope fully somebody has the nerv to help a beginner.
    thanks mogh

    Edit:
    I did some more forum searching and even though this mouse input thing is kinda interesting - the thing i probably need a good example of
    ViewportSelect.GetNearestPoint
    hence my final goal is to draw a line between 2 3d points of a object.



  • hi,

    we are here to help the best we can, beginner or not :)

    i've created an example that will do what you want so you have a base to start.
    I've picked the liquid example (that's why it's nice ^^) and modified it.
    I choose to add the point when you left click. But i could also create a dragloop (like in the liquid paint tool) to "wait" until the mouse is released.

    Fill free to ask questions about it.

    About your question GetNearestPoint, can you open a new thread ? (we like one question per thread so it's easier to search for answer)

    import c4d
    import os
    
    # Be sure to use a unique ID obtained from www.plugincafe.com
    PLUGIN_ID = 1000001
    
    
    class SettingsDialog(c4d.gui.SubDialog):
        """
        Dialog to display option in the ToolData, in this case the Sphere size.
        """
        parameters = {}
    
        def __init__(self, arg):
            # Checks if the argument passed is a dictionary
            if not isinstance(arg, dict):
                raise TypeError("arg is not a dict.")
    
            self.parameters = arg
    
        def CreateLayout(self):
            """
            This Method is called automatically when Cinema 4D Create the Layout (display) of the GeDialog.
            """
            value = self.parameters["line_color"]
            if self.GroupBegin(id=1000, flags=c4d.BFH_SCALEFIT, cols=2, rows=1):
                self.GroupBorderSpace(10, 10, 10, 10)
    
                # Creates a Static text and a number input
                self.AddColorChooser(id=1001, flags=c4d.BFV_TOP, initw=120,  layoutflags=c4d.DR_COLORFIELD_NO_BRIGHTNESS)
                
    
                # Defines the default values
                self.SetColorField(id=1001, color=value, brightness = 1.0, maxbrightness = 1.0, flags = 0)
            self.GroupEnd()
            return True
    
        def Command(self, messageId, msg):
            """
              This Method is called automatically when the user clicks on a gadget and/or changes its value this function will be called.
              It is also called when a string menu item is selected.
             :param messageId: The ID of the gadget that triggered the event.
             :type messageId: int
             :param bc: The original message container
             :type bc: c4d.BaseContainer
             :return: False if there was an error, otherwise True.
             """
            # When the user change the Gadget with the ID 1002 (the input number field)
    
            if messageId == 1001:
                # Stores the value in the parameter variable
                self.parameters["line_color"] = self.GetColorField(1001)["color"]
                # Update the view to redraw the lines
                c4d.DrawViews(c4d.DA_ONLY_ACTIVE_VIEW | c4d.DA_NO_THREAD | c4d.DA_NO_ANIMATION)
            
            return True
    
    
    class DrawLine(c4d.plugins.ToolData):
        """Inherit from ToolData to create your own tool"""
        def __init__(self):
            self.lines = []
            self.mouseCoordinate = c4d.Vector(-1)
            self.data = {"line_color":c4d.Vector(0, 0, 1)}
    
        def GetState(self, doc):
            """
            Called by Cinema 4D to know if the tool can be used currently
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :return: True if the tool can be used, otherwise False.
            """
            return c4d.CMD_ENABLED
    
        def MouseInput(self, doc, data, bd, win, msg):
            """
            Called by Cinema 4D, when the user click on the viewport and the tool is active.
            Mainly the place to do moue interaction from the viewport.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data:  The tool settings container.
            :type data: c4d.BaseContainer
            :param bd:  The BaseDraw object of the active editor view.
            :type bd: c4d.BaseDraw
            :param win: The EditorWindow object for the active editor view.
            :type win: c4d.gui.EditorWindow
            :param msg: The original message container.
            :type msg: c4d.BaseContainer
            :return: False if a problem occurred during this function.
            """
            # Retrieves which clicks is currently clicked
            device = 0
            if msg[c4d.BFM_INPUT_CHANNEL ]== c4d.BFM_INPUT_MOUSELEFT:
                device = c4d.KEY_MLEFT
            else:
                return True
       
            
            # Retrieves the X/Y screen position of the mouse.
            mx = msg[c4d.BFM_INPUT_X]
            my = msg[c4d.BFM_INPUT_Y]
    
            # adds the position to the array
            myPoint = c4d.Vector (mx , my, 0)
            self.lines.append(myPoint)
    
            
            # Pushes an update event to Cinema 4D
            c4d.EventAdd()
            return True
    
        def Draw(self, doc, data, bd, bh, bt, flags):
            """
            Called by Cinema 4d when the display is updated to display some visual element of your tool in the 3D view.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data: The tool settings container.
            :type data: c4d.BaseContainer.
            :param bd: The editor's view.
            :type bd: c4d.BaseDraw
            :param bh: The BaseDrawHelp editor's view.
            :type bh: c4d.plugins.BaseDrawHelp
            :param bt: The calling thread.
            :type bt: c4d.threading.BaseThread
            :param flags: The current drawing pass.
            :type flags: TOOLDRAWFLAGS
            :return: The result of the drawing (most likely c4d.DRAWRESULT_OK)
            """
    
            # Resets the drawing matrix to the world space matrix.
            bd.SetMatrix_Matrix(None, c4d.Matrix())
            
            # set the color for already drawn lines
            color = self.data["line_color"]
            bd.SetPen(color)
            if not flags:
                # draw lines for each group of two points
                for pa, pb in zip(self.lines[::2], self.lines[1::2]):
                    bd.DrawLine2D(pa, pb)
    
            # if the len for lines is odd, we need to draw a line to the mouse cursor's position
            if len(self.lines) % 2 == 1 and self.mouseCoordinate != c4d.Vector(-1):
                lastPoint = self.lines[-1]
                # the last line is green
                bd.SetPen(c4d.Vector(0, 1, 0))
                bd.DrawLine2D(lastPoint, self.mouseCoordinate)
    
            return c4d.TOOLDRAW_NONE
    
        def GetCursorInfo(self, doc, data, bd, x, y, bc):
            """
            Called by Cinema 4D when the cursor is over editor window to get the state of the mouse pointer.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data: The tool settings container.
            :type data: c4d.BaseContainer
            :param bd: The editor's view.
            :type bd: c4d.BaseDraw
            :param x: The x coordinate of the mouse cursor relative to the top-left of the currently active editor view.
            :type x: float
            :param y:The y coordinate of the mouse cursor relative to the top-left of the currently active editor view.
            :type y: float
            :param bc: The container to store the result in.
            :type bc: c4d.BaseContainer
            :return:
            """
            # If the cursor has left a user area, set the mouseCoordinate to -1 and return True
            if bc.GetId() == c4d.BFM_CURSORINFO_REMOVE:
                self.mouseCoordinate = c4d.Vector(-1)
                return True
            # if the mouse is coming back to the viewport, maybe we need to draw the line from last point, we ask for a redraw
            c4d.DrawViews()
            # we set the mouse coordinate
            self.mouseCoordinate = c4d.Vector(x, y, 0)
            
            # Sets the cursor.
            bc.SetInt32(c4d.RESULT_CURSOR, c4d.MOUSE_POINT_HAND)
            return True
    
        def AllocSubDialog(self, bc):
            """
            Called by Cinema 4D To allocate the Tool Dialog Option.
            :param bc: Currently not used.
            :type bc: c4d.BaseContainer
            :return: The allocated sub dialog.
            """
    
            return SettingsDialog(getattr(self, "data", {"line_color": c4d.Vector(1, 0, 0)}))
    
    
    if __name__ == "__main__":
        # Registers the tool plugin
        c4d.plugins.RegisterToolPlugin(id=PLUGIN_ID,
                                       str="draw line",
                                       info=0, icon=None,
                                       help="drawing line",
                                       dat=DrawLine())
    
    

    cheers,
    Manuel



  • @m_magalhaes said in draw line into 3d Space with two mouse clicks:

    1000001

    Thank you for your detailed example, thats what we beginners need. I am really happy.
    anyway down the rabbit hole :-)

    I modivied i a little bit (gui) and also implemented the ESC key -> resetting the self.lines = []

    is that propper ?

    Anyway I'll mark this as solved hence its everything I asked (besides the 3d part but I guess this will be a seperate discussion with GetNearestPoint)

    import c4d
    import os
    # Be sure to use a unique ID obtained from www.plugincafe.com
    PLUGIN_ID = 1000001 #Dummy ID
    
    class SettingsDialog(c4d.gui.SubDialog):
        """
        Dialog to display option in the ToolData, in this case the Sphere size.
        """
        parameters = {}
    
        def __init__(self, arg):
            # Checks if the argument passed is a dictionary
            if not isinstance(arg, dict):
                raise TypeError("arg is not a dict.")
    
            self.parameters = arg
    
        def CreateLayout(self):
            """
            This Method is called automatically when Cinema 4D Create the Layout (display) of the GeDialog.
            """
            value = self.parameters["line_color"]
            if self.GroupBegin(id=1000, flags=c4d.BFH_SCALEFIT, cols=1, rows=2):
                self.GroupBorderSpace(10, 10, 10, 10)
    
                # Creates a Static text and a number input
                self.element = self.AddStaticText( id=2003, flags=c4d.BFH_SCALEFIT, initw=1, name="Line Color: ", borderstyle=c4d.BORDER_NONE )
                self.AddColorChooser(id=1001, flags=c4d.BFV_TOP|c4d.BFH_SCALEFIT, initw=1,  layoutflags=c4d.DR_COLORFIELD_NO_BRIGHTNESS)
                #self.AddColorField(id=1001, flags=c4d.BFV_TOP|c4d.BFH_FIT, initw=120)  #popup color field
    
                # Defines the default values
                self.SetColorField(id=1001, color=value, brightness = 1.0, maxbrightness = 1.0, flags = 0)
            self.GroupEnd()
            return True
    
        def Command(self, messageId, msg):
            """
              This Method is called automatically when the user clicks on a gadget and/or changes its value this function will be called.
              It is also called when a string menu item is selected.
             :param messageId: The ID of the gadget that triggered the event.
             :type messageId: int
             :param bc: The original message container
             :type bc: c4d.BaseContainer
             :return: False if there was an error, otherwise True.
             """
            # When the user change the Gadget with the ID 1002 (the input number field)
    
            if messageId == 1001:
                # Stores the value in the parameter variable
                self.parameters["line_color"] = self.GetColorField(1001)["color"]
                # Update the view to redraw the lines
                c4d.DrawViews(c4d.DA_ONLY_ACTIVE_VIEW | c4d.DA_NO_THREAD | c4d.DA_NO_ANIMATION)
            
            return True
    
    class DrawLine(c4d.plugins.ToolData):
        """Inherit from ToolData to create your own tool"""
        def __init__(self):
            self.lines = []
            self.mouseCoordinate = c4d.Vector(-1)
            self.data = {"line_color":c4d.Vector(1, 1, 0)}
    
        def GetState(self, doc):
            """
            Called by Cinema 4D to know if the tool can be used currently
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :return: True if the tool can be used, otherwise False.
            """
            return c4d.CMD_ENABLED
    
        def Debug(self, msg):
            c4d.CallCommand(13957)  # Konsole loeschen
            
            #print "Konsole geloescht."
            return True
    
        def KeyboardInput(self, doc, data, bd, win, msg):
            key = msg.GetLong( c4d.BFM_INPUT_CHANNEL )
            cstr = msg.GetString( c4d.BFM_INPUT_ASC )
            if key == c4d.KEY_ESC:
                # do what you want
                print "ESC Key Pressed."
                self.lines = []
                print "Lines Flushed."
                self.Debug(msg)
                # return True to signal that the key is processed            
                return True
            return False
    
        def MouseInput(self, doc, data, bd, win, msg):
            """
            Called by Cinema 4D, when the user click on the viewport and the tool is active.
            Mainly the place to do moue interaction from the viewport.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data:  The tool settings container.
            :type data: c4d.BaseContainer
            :param bd:  The BaseDraw object of the active editor view.
            :type bd: c4d.BaseDraw
            :param win: The EditorWindow object for the active editor view.
            :type win: c4d.gui.EditorWindow
            :param msg: The original message container.
            :type msg: c4d.BaseContainer
            :return: False if a problem occurred during this function.
            """
            # Retrieves which clicks is currently clicked
            device = 0
            if msg[c4d.BFM_INPUT_CHANNEL ]== c4d.BFM_INPUT_MOUSELEFT:
                device = c4d.KEY_MLEFT
            else:
                return True
    
            # Retrieves the X/Y screen position of the mouse.
            mx = msg[c4d.BFM_INPUT_X]
            my = msg[c4d.BFM_INPUT_Y]
    
            # adds the position to the array
            myPoint = c4d.Vector (mx , my, 0)
            my3dpoint = bd.SW(myPoint)
            self.lines.append(myPoint)
    
            # Pushes an update event to Cinema 4D
            c4d.EventAdd()
            return True
    
        def Draw(self, doc, data, bd, bh, bt, flags):
            """
            Called by Cinema 4d when the display is updated to display some visual element of your tool in the 3D view.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data: The tool settings container.
            :type data: c4d.BaseContainer.
            :param bd: The editor's view.
            :type bd: c4d.BaseDraw
            :param bh: The BaseDrawHelp editor's view.
            :type bh: c4d.plugins.BaseDrawHelp
            :param bt: The calling thread.
            :type bt: c4d.threading.BaseThread
            :param flags: The current drawing pass.
            :type flags: TOOLDRAWFLAGS
            :return: The result of the drawing (most likely c4d.DRAWRESULT_OK)
            """
    
            # Resets the drawing matrix to the world space matrix.
            bd.SetMatrix_Matrix(None, c4d.Matrix())
            bd.SetMatrix_Camera()
            
            # set the color for already drawn lines
            color = self.data["line_color"]
            bd.SetPen(color)
            if not flags:
                # draw lines for each group of two points
                for pa, pb in zip(self.lines[::2], self.lines[1::2]):
                    bd.DrawLine2D(pa, pb)
    
            # if the len for lines is odd, we need to draw a line to the mouse cursor's position
            if len(self.lines) % 2 == 1 and self.mouseCoordinate != c4d.Vector(-1):
                lastPoint = self.lines[-1]
                # the last line is green
                bd.SetPen(c4d.Vector(0, 1, 0))
                bd.DrawLine2D(lastPoint, self.mouseCoordinate)
    
            return c4d.TOOLDRAW_NONE
    
        def GetCursorInfo(self, doc, data, bd, x, y, bc):
            """
            Called by Cinema 4D when the cursor is over editor window to get the state of the mouse pointer.
            :param doc: The current active document.
            :type doc: c4d.documents.BaseDocument
            :param data: The tool settings container.
            :type data: c4d.BaseContainer
            :param bd: The editor's view.
            :type bd: c4d.BaseDraw
            :param x: The x coordinate of the mouse cursor relative to the top-left of the currently active editor view.
            :type x: float
            :param y:The y coordinate of the mouse cursor relative to the top-left of the currently active editor view.
            :type y: float
            :param bc: The container to store the result in.
            :type bc: c4d.BaseContainer
            :return:
            """
            # If the cursor has left a user area, set the mouseCoordinate to -1 and return True
            if bc.GetId() == c4d.BFM_CURSORINFO_REMOVE:
                self.mouseCoordinate = c4d.Vector(-1)
                return True
            # if the mouse is coming back to the viewport, maybe we need to draw the line from last point, we ask for a redraw
            c4d.DrawViews()
            # we set the mouse coordinate
            self.mouseCoordinate = c4d.Vector(x, y, 0)
            
            # Sets the cursor.
            if len(self.lines) < 2:
                bc.SetInt32(c4d.RESULT_CURSOR, c4d.MOUSE_CROSS)
            else:
                bc.SetInt32(c4d.RESULT_CURSOR, c4d.MOUSE_POINT_HAND)
            return True
    
        def AllocSubDialog(self, bc):
            """
            Called by Cinema 4D To allocate the Tool Dialog Option.
            :param bc: Currently not used.
            :type bc: c4d.BaseContainer
            :return: The allocated sub dialog.
            """
    
            return SettingsDialog(getattr(self, "data", {"line_color": c4d.Vector(1, 0, 0)}))
    
    if __name__ == "__main__":
        # Registers the tool plugin
        c4d.plugins.RegisterToolPlugin(id=PLUGIN_ID,
                                       str="Draw line",
                                       info=0, icon=None,
                                       help="drawing line",
                                       dat=DrawLine())
    


  • hi,

    it looks correct. Of course be sure to retrieve a unique id here, from the top bar. (the icon plugin ID)

    Feel free to ask questions.

    Cheers,
    Manuel


Log in to reply