@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())