Enable Isoline Editing in tool plugin
-
Hi, I'v searched the forum but I didn't find any solution.
I'm developing a tool plugin with python for polygon manipulation.
Is it possible to enabledeformedisonline editing in the viewport for the selected object under a HN?thanks
edit [ferdinand]: changed the title the word in the question
-
Hello @mdr74,
thank you for reaching out to us. You can set the display mode of a single or all viewports programmatically, but you cannot do this on a per object basis to my knowledge, since Cinema 4D simply does not support it. Find below an example for how to set the Deformed Editing mode programmatically. If you want to restrict it to cases where the active object is child of a HyperNurbs object, you could do so by retrieving the active object, then its parent and then checking for that parent being of type
c4d.Osds
, e.g.:parent = op.GetUp() if isinstance(parent, c4d.C4DAtom) and parent.CheckType(c4d.Osds): ...
Cheers,
Ferdinand"""Example for modifying a parameter of all viewports. Runs as a script manager script, toggles the Deformed Editing state of all viewports. As discussed in: https://developers.maxon.net/forum/topic/13415 """ import c4d # Viewports are organized as BaseView/BaseDraw instances which are # attached to a document. Just as in Cinema, we have to change the viewport # settings per viewport. So, when we want to change all of them, we have to # obtain all viewports. viewports = [doc.GetBaseDraw(i) for i in range(doc.GetBaseDrawCount())] print (f"All viewports attached to {doc}: {viewports}.") # We can also get specific viewports, e.g., the active one or the one which # is going to be the rendering viewport. activeViewport = doc.GetActiveBaseDraw() renderViewport = doc.GetRenderBaseDraw() print (f"The active viewport is: {activeViewport}") print (f"The rendering viewport is: {renderViewport}") # A viewport is just another BaseList2D. So, setting a parameter, e.g., the # IsoLine Editing mode, is nothing special and just another parameter. # We go over all viewports and toggle their Deformed Editing state. for vp in viewports: vp[c4d.BASEDRAW_DATA_DEFORMEDEDIT] = not vp[c4d.BASEDRAW_DATA_DEFORMEDEDIT] # Since we did modify a node, the viewports, we have to push an update event # to Cinema 4D, in order to let the modifications be reflected in the UI. c4d.EventAdd() # In a ToolData scenario you might want to cache the current Deformed Editing # state before you overwrite it, as otherwise the state will linger after # your tool has been deselected. The user might not like that ;) A good # place to set and cache the Deformed Editing mode would be ToolData.InitTool # and FreeTool would be a good place to set the Deformed Editing mode back to # its original value. You could also attach the setting and caching of the mode # to other methods, e.g. input methods or `Message()`, so that you can catch # the user switching the active object while having your tool active, but I personally # would not bother with this.
-
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 featuresSorry for the confusion.
Do you need me to change the post title? -
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
toc4d.BASEDRAW_DATA_SDSEDIT
. As I said in the script, aBaseDraw
is just anotherBaseList2D
, nothing special about it.Cheers,
Ferdinand -
@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 terminologyI'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
-
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 theToolData
or more specificallyToolDescriptionData
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 viaCallCommand()
?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 aToolData
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 aToolData
plugin and as soon as the user does activate thatToolData
plugin, the viewports of Cinema 4D ignore if they have the Isoline Editing flag enabled or not. They will then always draw geometry as ifBASEDRAW_DATA_SDSEDIT
wasFalse
, 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 -
@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:
and when my tool is active:
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 -
-
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(), )
-
@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?
-
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 ifBASEDRAW_DATA_SDSEDIT
is enabled for theBaseDraw
or not:
With the flag
PLUGINFLAG_TOOL_EDITSTATES
, you will get the following viewport output whenBASEDRAW_DATA_SDSEDIT
is enabled for the viewport:
and the following output when
BASEDRAW_DATA_SDSEDIT
is disabled for the viewport:
Cheers,
Ferdinand -
Thanks Ferdinand.
withinfo=c4d.PLUGINFLAG_TOOL_EDITSTATES
everything works fine.I mark your answer as correct.
is it the right way to set the topic as solved? -