Custom move element tool is not fast as the built-in Microstation "Move extended" when hundreds of elements are manipulated[V8i MDL]

Hello! It´s me again, with a question.

I´m currently working with a Microstation V8i SS4 version, where I needed to add my own functionality to the move tool, so I´ve decided that I will implement myself it with MDL API, as VBA didn´t offer me the things I wanted (and the tool didn´t have that "native microstation tool" feeling). It was also a great opportunity to learn new things.

Functionality is working properly. Everything was great until I have decided to add support of selection set for my tool. Now I was facing a problem with performance, where for example I move around like 500+ elements (of all kinds) and I can see that the FPS is dropped (so the movement is not that smooth). I´ve tried to do the same thing with the "move extended" keyin (I simply launched it from my C++ code) and the performance was much better, smoother, as the number of elements selected didn´t affect it at all.

Now to the implementation (as the code is quite complex I won´t share it all):

Depending on whether user uses selection set or no, I start Modify command
    if (mdlSelect_numSelected() > 0)
    {
        mdlState_startModifyCommand(NULL, acceptFunc, updateFunc, NULL, NULL, 0, 0, TRUE, 2);       
    }
    else
    {
        mdlState_startModifyCommand(NULL, acceptFunc, updateFunc, NULL, NULL, 0, 0, FALSE, 0);
    }
    
Then I set some mdlLocate settings like enabling accusnap etc. (as I think it does not affect
the perfomance, I will skip that)

From the selected elements, I iterate through them, receive ElementRef and create transient elements (the copies that you see moving around with mouse)

    MSElementDescr* descr = nullptr;
    ElementRef descrRef = elementRef_getParent(ref);
    if (descrRef == NULL)
    {
        descrRef = ref;
    }

    mdlElmdscr_getByElemRef(&descr, descrRef, mdlModelRef_getActive(), 0, 0);
    TransDescrP createdTrans = mdlTransient_addElemDescr(NULL, descr, FALSE, 0x00ff, DRAW_MODE_TempDraw, FALSE, FALSE, FALSE);
    _transDescriptors.emplace_back(createdTrans);
    
Then in the dynamic update function, I move them according to the movements of my mouse

for(int i = 0; i < _transDescriptors.size(); i++)
    {
        MSElementDescr* descr;
        mdlTransient_returnElemDescr(&descr, _transDescriptors[i]);

        Transform transformMatrix;
        mdlTMatrix_getIdentity(&transformMatrix);
        mdlTMatrix_setTranslation(&transformMatrix, &mousePoint);
        mdlElmdscr_transform(descr, &transformMatrix);     
        
        _transDescriptors[i] = mdlTransient_replaceElemDescr(_transDescriptors[i], _copyDescriptors[i], FALSE, 0x00ff, DRAW_MODE_TempDraw, FALSE, TRUE, TRUE);
    }

Thats my main approach, but I also tried a different one, but with same results:

Instead of iterating through all elements, I added them to a temporary created cell (using mdlElmdscr_append).

Then i simply just translated this single element, however it didn´t improve the performance, or run smoother.

Do you guys have some ideas what could cause my problems? Is there some workaround to speed up described behavior? Thanks in advance.

Parents Reply
  • Hello Jan.

    I´ve decided for transient elements because I thought its the right idea - the abillity to modify a temporary elements that are not written into the file looked fine for me (I even thought that its optimized more this way).

    Basically you are saying that I should save element descriptors of my selected elements, set this callback for STATE_COMPLEX_DYNAMICS and inside this function clone my element descriptors, translate them and then draw them in tempDraw? 

Children
  • I´ve decided for transient elements because I thought its the right idea

    Bentley staffer provided this tip: A view decorator is good for things that may be changing every frame/cursor motion, an IViewTransient is good for things that don't change often.

     
    Regards, Jon Summers
    LA Solutions

  • Hi Lubo,

    I´ve decided for transient elements because I thought its the right idea

    It's better to check examples delivered with MicroStation SDK (which do not use transient element inside tools), plus to search this forum. Probably all possible usual problems were discussed here already.

    he abillity to modify a temporary elements that are not written into the file looked fine for me

    Jon's quoted the explanation. Transient elements are not tool to implement anything that should be changed with in frame. They are displayed automatically, can be snapped etc., so they are tools to be used in different workflows (e.g. to create an overlay with "phantom" elements).

    Basically you are saying

    Yes. See examples and I assume you will also find some old discussions on this web.

    But, in fact, because we are discussing new code, this approach is wrong. For new code it's recommended to use MicroStation C++ API, in V8i specifically MStnElementSetTool. It's not based on C function callback (which are not very safe), but on classes, where particular class encapsulate complete based functionality, so it's "only" necessary to inherit it and to override functionality, specific for implemented tool.

    In this new API, to display list of elements uses ElementAgenda, so it's extremely simple: To create element agenda and to use RedrawElems class to display the agenda in views.

    This new (C++) way is standard for MicroStation CE, so when migrated, it does not require big effort.

    With regards,

      Jan

  • Basically you are saying that I should save element descriptors of my selected elements

    If moving to C++ then eschew element descriptors and prefer ElementHandles, which are smart pointers to descriptors.  That saves you having to do manual memory management.

    The DgnElementSetTool class has these virtual methods:

    _OnRedrawOperation 

    Returns ERROR to prevent the element from being drawn.

    _OnRedrawComplete 

    Called after drawing all elements (or after aborting draw)

    How we abort a redraw I don't know.

     
    Regards, Jon Summers
    LA Solutions

  • What the MicroStation move tool does to improve performance when drawing lots of elements is to use an existing "cached" representation and just push a transform, which means it avoids copying/transforming the element data in many cases. It does this using a sub-class of MstnElementTool and overriding OnRedrawOperation as follows:

    /*---------------------------------------------------------------------------------**//**
    * Called during complex dynamics. Default implementation calls OnElementModify and
    * assumes it's not valid to display the modified element using an existing cache
    * representation.
    * @param el IN Current element.
    * @param context IN context for the redraw operation.
    * @param canUseCached OUT whether cached representation is still valid for display.
    * @return SUCCESS to not skip the display of the current element.
    * @see IRedrawOperation
    * @bsimethod      
    +---------------+---------------+---------------+---------------+---------------+------*/
    MSCORE_EXPORT virtual StatusInt OnRedrawOperation (EditElemHandleR el, ViewContextP context, bool* canUseCached);
    
    StatusInt TransformTool::OnRedrawOperation (EditElemHandleR elHandle, IViewContextP context, bool* canUseCached)
        {
        if (NULL != canUseCached)
            {
            *canUseCached = true;
            context->PushActiveTransform (&m_transform); // <- Push transform to apply to element...
            return SUCCESS;
            }
    
        return OnElementModify (elHandle);
        }



    Answer Verified By: Lubo B