Threading for modelling tools

On 18/05/2013 at 11:26, xxxxxxxx wrote:

Hi,

I have a pretty diffuse question again. I have a ToolData plugin which modifies the point data of 
a polygon object attached to the active document. The plugin has a GeDialog gui and also allows
modifying the main value by dragging in the editor window. The plugin is working technically 
working fine, but there is still the problem that when I drag a gui slider or live drag in the editor
window, that update method is not executed until i do release the mouse button. I guess the
the reason is that i do all calculations from the main thread.

So are there any resources on how to implement threading for such a task (i have to admit
that i have generally avoided threading until now).

here a visual help, if it was difficult to understand  my description of the problem :

Start:

While dragging (without update) :

Update after releasing the mouse button:

Thanks for reading,
Happy rendering,
Ferdinand

On 19/05/2013 at 07:57, xxxxxxxx wrote:

Is something unclear about my question ? It seems to be rather common problem for me. At
least in python I am not sure if ToolDescriptionData behaves differently in cpp. What I do
really not understand is why there is no update at all while I am dragging, instead of a
sluggish GUI/update behaviour because I do run everything from one thread.

If some code helps for giving me an answer, I will try to show the update cycle on the
example of an editor view mouse drag. The tool is working with a cache of the objects
point and polygon data, so you can drag the sliders back and forth and they will always
relate to the state when the tool has been raised.

    # --- Implements value mouse/editor manipulation and cursor change while drag event. 
    # -----------------------------------------------------------------------------------------------
    def MouseInput(self, doc, data, bd, win, msg) :
        startbc, currentbc, ox, dv = c4d.BaseContainer(), c4d.BaseContainer(), None, None
        # starting mouse state
        isok = gui.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, startbc)
        # start drag
        if isok and startbc[c4d.BFM_INPUT_VALUE]:
            sx = startbc.GetLong(c4d.BFM_INPUT_X)
            # loop while mouse drag
            while gui.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, currentbc) :
                if currentbc.GetLong(c4d.BFM_INPUT_VALUE) == 0: break
                gui.SetMousePointer(c4d.MOUSE_ARROW_H)
                x = currentbc.GetLong(c4d.BFM_INPUT_X)
                # change dialog values when x != ox
                if self.Dialog and x != ox:
                    # reset sx if drag vector changed
                    if dv != None and (dv>0) != (x-ox > 0) : sx = x
                    # check for shift qualifier and adjust dragscale
                    if currentbc[c4d.BFM_INPUT_QUALIFIER] == c4d.QSHIFT: 
                        scale = fhPolyTools.ID_MOUSE_DRAGSMALL
                    else : scale = fhPolyTools.ID_MOUSE_DRAGBIG
                    # edit the dialog value with the mouse data
                    dialogvalue = self.Dialog.GetReal(fhPolyTools.IDC_STRAIGHTEN_STRENGHT_EDT)
                    value  = round(c4d.utils.Clamp(0.001, 1, dialogvalue + ((x - sx) * 0.01) * scale), 4)
                    self.Dialog.SetPercent(id = fhPolyTools.IDC_STRAIGHTEN_STRENGHT_EDT, value = value, 
                                           min = 0.0, max = 100.0, step = 0.01, tristate = False)
                    self.Dialog.Update()
                    # set dragvector and last pos
                    if ox: dv = x - ox
                    ox = x
            # set cursor back
            gui.SetMousePointer(c4d.MOUSE_NORMAL)
        return isok

the orange line calls GeDialog.Update() in my tools dialog, which then simply calls back to the 
tool itself:

    def Update(self) :
        return self.Host.Update(strenght     = self.GetReal(fhPolyTools.IDC_STRAIGHTEN_STRENGHT_EDT), 
                                relax        = self.GetReal(fhPolyTools.IDC_STRAIGHTEN_RELAX_EDT), 
                                distribution = self.GetReal(fhPolyTools.IDC_STRAIGHTEN_UNIFORM_CHK))

the ToolData.Update() method loops through some earlier processed ordered point data to 
apply the transform method ToolData.straightenEdge() which then updates the  point array 
of the object which is then written into the object. undos are currently implemented in 
other methods.

    # --- Update method called by the dialog and mouse input
    # --> Bool
    # -----------------------------------------------------------------------------------------------
    def Update(self, strenght, relax, distribution) :
        doc = documents.GetActiveDocument()
        if self.GetData(doc) :
            # copy cache
            data = self.PointCache[:]
            for pointgroup in self.PointGroups.PointGroups:
                data = self.straightenEdge(data, pointgroup, strenght, True)
                if not data: return False
            # update object
            self.Object.SetAllPoints(data)
            self.Object.Message(c4d.MSG_CHANGE)
            c4d.EventAdd()
            return True
        return False

so which part do i have to put into a thread ?

On 19/05/2013 at 09:44, xxxxxxxx wrote:

Hi littledevil,

you say the mesh doesn't update while dragging a slider, did I get this straight?
From the code you have posted, I only see you are doing the operations in MousInput().
This method is not called while you are dragging a slider in the dialog, you need to
override the Command() method of the dialog in this case.

Best,
-Niklas

On 19/05/2013 at 10:01, xxxxxxxx wrote:

the example posted above is for ToolData.MouseInput(). The example shows the
case where you chanage the 'main' value (the tools strength) by draginng with 
the mouse pointer into a editor view. Just like you can do it for the extrude tool for 
example.

I have of course also overwritten GeDialog.Command() to register changes made
in the GeDialog gui. The update behaviour for that is the same.

# ----------------------------------------------------------------------------------------------- def Command(self, cid, msg) : # Endable/disable apply button if cid == fhPolyTools.IDC_TOOL_REALTIME_CHK: self.Realtime = self.GetBool(fhPolyTools.IDC_TOOL_REALTIME_CHK) self.Enable(fhPolyTools.IDC_TOOL_APPLY_BTN, not self.Realtime) # Apply update if cid == fhPolyTools.IDC_TOOL_APPLY_BTN: self.Update() # Reset values if cid == fhPolyTools.IDC_TOOL_RESET_BTN: self.InitValues() # Invoke update if realtime if self.Realtime: self.Update() return True

On 19/05/2013 at 10:14, xxxxxxxx wrote:

I see. Try DrawViews() instead/in addition to EventAdd(). Threading
wouldn't help at all.

Best,
-Nik

On 19/05/2013 at 10:28, xxxxxxxx wrote:

three things :

1. when dragging in the editor window with the mouse the GeDialog slider is actually
updatetd properly while dragging

2. i also tried calling c4d.StopAllThreads() before updating the object, but it had no effect

3. i already wrote it earlier, what bugs me most, is that the problem is not that everything 
is very slow, as i am doing verything from one thread, but that it runs smoothly, only that
the result of the modification is not shown until i release the left mouse button (both for
the editor and the dialog slider).

so it feels somehow that c4d limits the update of obejcts while some mouse interaction
is going on. but i am not sure how for example the extrude tool is avoiding that problem.
is the object shown by those tools in the editor maybe actually not the object attached 
to the document, but only a reflection of the tools data cache drawn with the tools Draw()
method ?

On 19/05/2013 at 10:31, xxxxxxxx wrote:

Originally posted by xxxxxxxx

I see. Try DrawViews() instead/in addition to EventAdd(). Threading
wouldn't help at all.

Best,
-Nik

that did the trick, god and i saw myself already sunken into some nasty threading geek stuff.

👏

happy rendering,
ferdinand