DoUndo() issue when using loop selection, ring, ...



  • I would like to point to an older discussion, which didn't provide any answer then.
    https://plugincafe.maxon.net/topic/10457/13898_detect-polygon-selection-change-revisited
    I am now again encountering this problem ...

    I have attached code for a dummy plugin which can be used to reproduce the problem (note that you will need an icon file for this plugin, not included)

    The idea is to perform a polygon selection on an object, and call the plugin.
    The plugin will perform some actions on the polygons. But one would want to have AND the polygon selection AND the action on the polygon into a single undo step.
    This way, the user simply has to undo once. There's more behind the reason for having a single undo step, but let's leave this out of the discussion.

    One way to look at this is to provide an own selection tool. Been there, done that.
    The other solution (in order to re-use the native selection tools) is to detect the change, undo the change, create a new undo and in this one perform the selection change AND the processing of the polygons.
    This is what the implementation below does.

    So, with the plugin available, you an create a cube, make it editable.
    With the live selection tool you select a polygon, then shift-select another polygon.
    The two polygons will be part of 2 separate undo steps.
    If you now activate the plugin it will:

    1. undo the last step (the last selected polygon will become unselected)
    2. create a new undo
    3. re-select the last selected polygon
    4. do our own thing
    5. end the new undo

    This all works as expected with Live Selection tool.
    But if the last undo step -before calling the plugin- was a result of the Loop Selection tool, or the Ring Selection tool, ... then we get an exception when a new doc->AddUndo is performed, right after the doc->DoUndo.
    Is this a bug?
    Any workaround to make it work for any selection tool?

    // ========================
    // Cinema 4D C++ plugin
    //
    // PluginName: Test
    // Dummy "empty" plugin
    // ========================
    
    // Main.cpp
    
    #include "c4d.h"
    
    // Dummy IDs - for demonstration purposes only
    #define MYCOMMAND_PLUGIN_ID	1000000
    
    // ====================================
    // CommandData
    // ====================================
    
    class MyCommand : public CommandData
    {
    	INSTANCEOF(MyCommand, CommandData)
    
    public:
    
    	//virtual Bool Execute(BaseDocument* doc); // R20
    	virtual Bool Execute(BaseDocument* doc, GeDialog* parentManager);
    };
    
    //Bool MyCommand::Execute(BaseDocument* doc) // R20
    Bool MyCommand::Execute(BaseDocument* doc, GeDialog* parentManager)
    {
    	if (!doc)
    		return false;
    
    	BaseObject* obj = doc->GetActiveObject();
    	if (!obj)
    	{
    		ApplicationOutput("No active object!");
    		return false;
    	}
    	ApplicationOutput("Current active object = @", maxon::String(obj->GetName()));
    
    	BaseSelect* polyS = ToPoly(obj)->GetPolygonS();
    	if (!polyS)
    	{
    		ApplicationOutput("No selected polygons!");
    		return false;
    	}
    
    	// undo the last step (assuming it is a polygon selection change)
    
    	// make a copy of the selection,
    	// since the original selection will be undone
    	AutoFree<BaseSelect> polyScopy((BaseSelect*)polyS->GetClone());
    	if (!doc->DoUndo())
    	{
    		ApplicationOutput("Failed to DoUndo()");
    		return false;
    	}
    
    
    	// we now want to apply the previous changes again, but include our own changes into the same undo
    
    	doc->StartUndo();
    	doc->AddUndo(UNDOTYPE::CHANGE_SELECTION, obj);
    
    	// set the edges that weren't set
    	// Note: obviously we also need to unset the ones that had been deselected,
    	// which we will not do in this demonstration plugin
    
    	{
    		Int32 seg = 0, a, b, i;
    		while (polyScopy->GetRange(seg++, LIMIT<Int32>::MAX, &a, &b))
    		{
    			for (i = a; i <= b; ++i)
    			{
    				if (!polyS->IsSelected(i))
    				{
    					polyS->Select(i);
    				}
    			}
    		}
    	}
    
    	// do our own undo steps
    	{
    		// ..
    	}
    
    	doc->EndUndo();
    	EventAdd();
    
    	ApplicationOutput("Undo completed");
    
    	return TRUE;
    }
    
    
    
    Bool RegisterMyCommand(void)
    {
    	return RegisterCommandPlugin(MYCOMMAND_PLUGIN_ID, "Test"_s, 0, AutoBitmap("icon.png"_s), "Test"_s, NewObjClear(MyCommand));
    }
    
    // ====================================
    // Plugin Main 
    // ====================================
    Bool PluginStart(void)
    {
    	ApplicationOutput("Test"_s);
    	RegisterMyCommand();
    
    	return TRUE;
    }
    void PluginEnd(void)
    {
    }
    Bool PluginMessage(Int32 id, void * data)
    {
    	switch (id) {
    	case C4DPL_INIT_SYS:
    		if (!g_resource.Init())
    			return FALSE;
    		return TRUE;
    	case C4DMSG_PRIORITY:
    		return TRUE;
    	case C4DPL_BUILDMENU:
    		break;
    	case C4DPL_ENDACTIVITY:
    		return TRUE;
    	}
    	return FALSE;
    }
    
    

    EDIT:
    I have adjusted the included code to be R21 compliant, as the originally provided implementation came from R20.



  • hello,

    this look awkward. I've opened a bug entry and foward this to the devs. It look like with the c++ API, the object is on a uninitialized state.

    Just this simple code will make your cinema4D hang (it enter an infinite loop).

    if (!doc->DoUndo())
    	{
    		ApplicationOutput("Failed to DoUndo()");
    		return maxon::OK;
    	}
    	 maxon::Int32 polyCnt = op->GetPolygonCount();
    

    The most funny part is that using a python script (that does use the same c++ function) works.
    And that's why I've opened a bug entry.

    I'll come back when I got more information.

    Cheers,
    Manuel



  • @m_magalhaes said in DoUndo() issue when using loop selection, ring, ...:

    I'll come back when I got more information.

    For some reason I had set this topic as "solved". Have changed it to "unsolved" again.



  • Hello,

    The bug is still opened and will be fixed as soon as possible.

    Cheers,
    Manuel



  • Hi Daniel,

    with regard to Solved/Unsolved we usually refer to the state of the thread not to the state of an eventually spotted bug.

    In case a user starts a thread reporting an issue and during the discussion a bug is found, we usually consider, upon the bug report being filed, the discussion as Solved since it has actually reached an end and a reasonable cause, responsible for the initial issue, has been found. The thread is then tagged by the SDK Team with the bug report tag and only when the spotted bug gets released the bug fixed tag is added to the discussion.

    Finally, the solved tag has also been removed - it was used before the introduction of the Ask as a question feature and all the threads previously marked with such tag have been "converted" to Solved/Unsolved questions.

    Hoping it make sense, give best.



  • @r_gigante
    Hi Riccardo,
    Thanks for the explanation.
    I wasn't sure that a "Solved" thread would still get revisited by SDK team members.
    As Manuel mentioned "I'll come back when I got more information", but the thread was set to solved, I wasn't sure the thread would get an update.
    Also, others visiting the forum (now or in future) might be misleaded by the state of the thread, thinking that the bug has been fixed, while it actually isn't.

    But now I understand a "bug fixed" tag is being added. Seems I need to pay more attention to the tags, as I assumed these tags were added by the creator of the thread. As such, I never really paid any attention later on, as I assumed the tags I added were the only ones to be available for that thread, and only the unsolved/solved state would change as a result of a resolution.

    Thanks for clarifying.