Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Detect polygon selection change (revisited)

    SDK Help
    0
    3
    676
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      On 17/11/2017 at 01:09, xxxxxxxx wrote:

      Andreas,
      Well, I didn't look at it that way, but you are right about the hijacking.
      The reason for doing that is that I don't want to re-implement all the available selection tools and all their options.
      Additionally, with my own selection tools implemented, when the user double-click a selection tag, I wouldn't be able to detect the newly created selection.

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 20/11/2017 at 08:33, xxxxxxxx wrote:

        While I understand your reasoning, I unfortunately can't add much more. Currently Cinema 4D is not really prepared to re-use the functionality of for example the selection tools. In the end it will on you to decide between the drawbacks.

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 21/11/2017 at 07:29, xxxxxxxx wrote:

          I understand that hijacking the selection tools, or trying in one way or another to re-use their functionality is probably not meant to be done. But I also don't need to explain why one would try to avoid re-implementation all the possible selection tools and their options.

          If only these tools would be provided as examples in the SDK (hint, hint 😉 ... but since internal implementations are not shared, that's probably not going to happen)

          Anyway, I have been trying another approach, and at first it seemed to work.
          I have searched the forums and this same technique -more or less- was described by Cactus Dan on multiple occasions:
          https://developers.maxon.net/forum/topic/4175/3718_freeing-coupled-objects&PID=15178#15178
          https://developers.maxon.net/forum/topic/5071/4996_catch-the-moment-when-the-user-converts-an-object&PID=20488#20488

          The idea is to detect a change in selection. Undo that change, start a new undo, apply the selection change AND your own change.
          This way both the selection and your required action are in a single user undo step.

          Below is the complete code of a test plugin involving a MessageData and TagData.
          Scenario:
          Create a sphere, make editable, go into polygon mode, assign the MyTagData to the sphere, and select some polygons with the Live Selection tool -> all OK, press undo -> OK, redo -> OK

            
          // Testing  
            
          #include "c4d.h"  
            
          // Dummy IDs - for demonstration purposes only  
          #define TAGDATA_PLUGIN_ID        1999998  
          #define MESSAGEDATA_PLUGIN_ID    1999999  
            
          #define TAG_BC_ID_SELECTION_COUNT    0  
          #define TAG_BC_ID_SELECTION_DATA    1  
            
          //  
          // TagData  
          //  
          class MyTagData : public TagData  
          {  
            INSTANCEOF(MyTagData, TagData)  
            
          public:  
            MyTagData() : TagData(), mIgnoreUndoRedo(FALSE) {}  
            
            static NodeData* Alloc(void) { return NewObjClear(MyTagData); }  
            virtual Bool Message(GeListNode* node, Int32 type, void* data)  
            {  
                if (type == MSG_DOCUMENTINFO)  
                {  
                    DocumentInfoData* did = static_cast<DocumentInfoData*>(data);  
                    if (did && ((did->type == MSG_DOCUMENTINFO_TYPE_UNDO) || (did->type == MSG_DOCUMENTINFO_TYPE_REDO))  
                        && !mIgnoreUndoRedo)  
                    {  
                        // update the selection in the tag container, in order for the MessageData  
                        // to not see a change in selection and avoid calling CheckSelectionChange(),  
                        maxon::BaseArray<Int32> added;  
                        maxon::BaseArray<Int32> removed;  
                        HasSelectionChanged(added, removed, TRUE);  
                    }  
                }  
                return SUPER::Message(node, type, data);  
            }  
            
            // Custom methods  
            
            void CheckSelectionChange()  
            {  
                maxon::BaseArray<Int32> added;  
                maxon::BaseArray<Int32> removed;  
                if (HasSelectionChanged(added, removed, TRUE))  
                {  
                    BaseTag* tag = (BaseTag* )Get();  
                    if (!tag)  
                        return;  
                    BaseDocument* doc = tag->GetDocument();  
                    if (!doc)  
                        return;  
                    BaseObject* object = tag->GetObject();  
                    if (!object)  
                        return;  
            
                    GePrint("Selection change detected - Undo selection, redo selection + action");  
            
                    // selection change detected, undo the selection change  
                    mIgnoreUndoRedo = TRUE;  
                    doc->DoUndo();  
            
                    // we will now create our own undo stack,  
                    // make the selection change and our own change  
                    Bool ret = doc->StartUndo();  
                    ret = doc->AddUndo(UNDOTYPE_CHANGE, object);    // <- crashes when loop selection  
            
                    // step 1. redo the selection change for the new undo stack  
                    BaseSelect* polyS = ToPoly(object)->GetPolygonS();  
                    for (const auto& item : added)  
                        polyS->Select(item);  
                    for (const auto& item : removed)  
                        polyS->Deselect(item);  
            
                    // step 2. perform the actual action  
                    // dummy polygon vertex update  
                    {  
                        PolygonObject* polyObj = ToPoly(object);  
                        const CPolygon* polyR = polyObj->GetPolygonR();  
                        Vector* pointW = polyObj->GetPointW();  
                        Int32 polyCount = polyObj->GetPolygonCount();  
            
                        for (Int32 i = 0; i < polyCount; i++)  
                        {  
                            if (!polyS->IsSelected(i))  
                                continue;  
                            Vector center;  
                            if (polyR[i].IsTriangle())  
                                center = (pointW[polyR[i].a] + pointW[polyR[i].b] + pointW[polyR[i].c]) / 3;  
                            else  
                                center = (pointW[polyR[i].a] + pointW[polyR[i].b] + pointW[polyR[i].c] + pointW[polyR[i].d]) * 0.25;  
            
                            pointW[polyR[i].a] = (pointW[polyR[i].a] + center) * 0.5;  
                            pointW[polyR[i].b] = (pointW[polyR[i].b] + center) * 0.5;  
                            pointW[polyR[i].c] = (pointW[polyR[i].c] + center) * 0.5;  
                            pointW[polyR[i].d] = (pointW[polyR[i].d] + center) * 0.5;  
                        }  
                    }  
            
                    ret = doc->EndUndo();  
                    mIgnoreUndoRedo = FALSE;  
                    GePrint("Selection change - Action completed");  
                }  
            }  
            
          protected:  
            // check if selection has changed, pass back the added and removed items,  
            // and optionally update the selection into the tag container  
            Bool HasSelectionChanged(maxon::BaseArray<Int32>& added, maxon::BaseArray<Int32>& removed, Bool update /*= FALSE*/)  
            {  
                AutoAlloc<BaseSelect> storedSelection;  
                if (!storedSelection)  
                    return FALSE;  
                RetrieveSelection(storedSelection);  
                // compare the stored selection from the tag container with the current selection  
                BaseTag* tag = (BaseTag* )Get();  
                if (!tag)  
                    return FALSE;  
                BaseObject* object = tag->GetObject();  
                if (!object)  
                    return FALSE;  
                BaseSelect* polyS = ToPoly(object)->GetPolygonS();  
                // obtain the added and removed ones  
                Int32 count = ToPoly(object)->GetPolygonCount();  
                for (Int32 idx = 0; idx < count; idx++)  
                {  
                    Bool newSel = polyS->IsSelected(idx);  
                    Bool oldSel = storedSelection->IsSelected(idx);  
                    if (newSel != oldSel)  
                    {  
                        if (oldSel)  
                            removed.Append(idx);  
                        if (newSel)  
                            added.Append(idx);  
                    }  
                }  
                if ((added.GetCount() == 0) && (removed.GetCount() == 0))  
                    return FALSE;  
                // store the new selection when requested  
                if (update)  
                    StoreSelection(polyS);  
                return TRUE;  
            }  
            // store selection into tag basecontainer  
            void StoreSelection(BaseSelect* selection)  
            {  
                if (!selection)  
                    return;  
                BaseTag* tag = (BaseTag* )Get();  
                if (!tag)  
                    return;  
                BaseContainer* data = tag->GetDataInstance();  
                if (!data)  
                    return;  
                // populate the container  
                Int32 seg = 0, a, b, i, selCount = 0;  
                while (selection->GetRange(seg++, LIMIT<Int32>::MAX, &a, &b))  
                    for (i = a; i <= b; ++i)  
                        data->SetInt32(TAG_BC_ID_SELECTION_DATA + selCount++, i);  
                data->SetInt32(TAG_BC_ID_SELECTION_COUNT, selCount);  
            }  
            // retrieve selection from tag basecontainer  
            void RetrieveSelection(BaseSelect* selection)  
            {  
                if (!selection)  
                    return;  
                selection->DeselectAll();  
                BaseTag* tag = (BaseTag* )Get();  
                if (!tag)  
                    return;  
                const BaseContainer* data = tag->GetDataInstance();  
                if (!data)  
                    return;  
                Int32 selCount = data->GetInt32(TAG_BC_ID_SELECTION_COUNT);  
                for (Int32 i = 0; i < selCount; i++)  
                    selection->Select(data->GetInt32(TAG_BC_ID_SELECTION_DATA + i));  
            }  
            
          private:  
            Bool mIgnoreUndoRedo;  
          };  
          Bool RegisterMyTagData()  
          {  
            return RegisterTagPlugin(TAGDATA_PLUGIN_ID, "tagstring", TAG_VISIBLE, MyTagData::Alloc, "Tmytag", AutoBitmap("icon.png"), 0);  
          }  
            
          //  
          // MessageData  
          //  
          class MyMessageData : public MessageData  
          {  
          public:  
            virtual Bool CoreMessage(Int32 id, const BaseContainer &bc)  
            {  
                // we are only interested in EVMSG_CHANGE  
                if (id != EVMSG_CHANGE)  
                    return TRUE;  
                BaseDocument* doc = GetActiveDocument();  
                if (!doc)  
                    return TRUE;  
                BaseObject* object = doc->GetActiveObject();  
                if (!object || !object->IsInstanceOf(Opolygon))  
                    return TRUE;  
            
                BaseTag* tag = object->GetTag(TAGDATA_PLUGIN_ID);  
                if (!tag)  
                    return TRUE;  
            
                MyTagData* mytag = tag->GetNodeData<MyTagData>();  
                if (!mytag)  
                    return TRUE;  
            
                mytag->CheckSelectionChange();  
            
                return TRUE;  
            }  
          };  
          Bool RegisterMyMessageData(void)  
          {  
            return RegisterMessagePlugin(MESSAGEDATA_PLUGIN_ID, String(), 0, NewObj(MyMessageData));  
          }  
            
            
            
          Bool PluginStart(void)  
          {  
            RegisterMyTagData();  
            RegisterMyMessageData();  
            return TRUE;  
          }  
          void PluginEnd(void)   
          {  
          }  
          Bool PluginMessage(Int32 id, void * data)  
          {  
            switch (id) {  
            case C4DPL_INIT_SYS:  
                if (!resource.Init())  
                    return FALSE;  
                return TRUE;  
            case C4DMSG_PRIORITY:  
                return TRUE;  
            case C4DPL_BUILDMENU:  
                break;  
            case C4DPL_ENDACTIVITY:  
                return TRUE;  
            }  
            return FALSE;  
          }  
            
          

          This seemed to work ... at first.
          Well, it does with live selection. But when a loop selection or ring selection is performed a crash happens when performing an AddUndo (after the DoUndo) in the CheckSelectionChange method of the tag.

          So, why does this seem to work for some selection tools, and not for other ones?
          Just trying to figure out, what I am doing wrong (except of abusing the whole SDK, that is)

          1 Reply Last reply Reply Quote 0
          • First post
            Last post