Implementing Interactive tools using DgnTools Class


In the previous chapter, we learn to place various graphics by typing commands, but the positions of these graphics are fixed. So, follow-up question is can you make the graph follow the cursor after entering the command, and place the graph at that point after positioning? The answer is undoubtedly yes. 

Before the introduction of the object-oriented MicroStation C ++ API, there were mdlState_xxx class functions in the MDL C API that could interact with MicroStation. In the new C ++ API, a powerful DgnTool class is provided to achieve similar functionality. The old and new APIs are compared as follows: 

differ

The new C++  APIs are provided in the form of classes. The three important classes in the table above are derived from the DgnTool base class, and other important classes and interfaces are involved in the derived tree. As shown below: 

flow-chart

These classes are rich in member functions; some set parameters, such as _AllowSelection, some start an action, such as _BeginDynamics, and some handle user events, such as _OnDataButton. Due to space limitations, we do not list all member functions in these classes here, please refer to the MicroStationAPI.chm document for their details.  

To implement an interactive tool, we need to define a class derived from DgnViewToolDgnPrimitiveTool, or DgnElementSetTool. Then create an instance of this class in the command processing function and start the tool by calling the InstallTool() function.

Let's transform the HelloWorld CreateBsplineSurface command step by step into an interactive tool. 

#include <Mstn\MdlApi\MdlApi.h> 
#include <DgnPlatform\DgnPlatformApi.h> 
#include <DgnView\DgnElementSetTool.h> 
#include "HelloWorldCmd.h" 


USING_NAMESPACE_BENTLEY_DGNPLATFORM 
USING_NAMESPACE_BENTLEY_MSTNPLATFORM 
USING_NAMESPACE_BENTLEY_MSTNPLATFORM_ELEMENT 


double g_1mu; 


struct PlaceBsSurfaceTool : DgnPrimitiveTool 
{ 
PlaceBsSurfaceTool(int toolId, int promptId) : DgnPrimitiveTool(toolId, promptId) {} 


bool CreateBsSurface(EditElementHandleR eeh, DPoint3dCP pBasePt) 
{ 

MSBsplineSurface bsSurface; 
MSBsplineCurve   bsCurves[4]; 
DPoint3d         center[4]; 
RotMatrix        rMatrix[4]; 
double           radius = g_1mu / 2; 

center[0] = center[1] = center[2] = center[3] = *pBasePt; 
center[0].x += radius; 
center[1].x += g_1mu;     center[1].y += radius; 
center[2].x += radius;    center[2].y += g_1mu; 
center[3].y += radius; 


DVec3d xVec = DVec3d::From(1, 0, 0), negativeXVec = DVec3d::From(-1, 0, 0); 
DVec3d yVec = DVec3d::From(0, 1, 0), negativeYVec = DVec3d::From(0, -1, 0); 
DVec3d zVec = DVec3d::From(0, 0, 1); 

rMatrix[0].InitFrom2Vectors(xVec, zVec);  //Front View 
rMatrix[1].InitFrom2Vectors(yVec, zVec);  //Right View 
rMatrix[2].InitFrom2Vectors(negativeXVec, zVec);  //Back View 
rMatrix[3].InitFrom2Vectors(negativeYVec, zVec);  //Left View 

for (int i = 0; i<4; i++) 
{ 
bsCurves[i].InitEllipticArc(center[i], radius, radius, 0, PI, &rMatrix[i]); 
} 

if (SUCCESS != mdlBspline_coonsPatch(&bsSurface, bsCurves)) 
{ 
for (int i = 0; i<4; i++) 
    mdlBspline_freeCurve(&bsCurves[i]); 
return false; 
} 
DraftingElementSchema::ToElement(eeh, bsSurface, nullptr, *ACTIVEMODEL); 
mdlBspline_freeSurface(&bsSurface); 
for (int i = 0; i<4; i++) 
mdlBspline_freeCurve(&bsCurves[i]);

return true; 
} 

 

virtual void _OnPostInstall() override 
{ 
_BeginDynamics(); 
AccuSnap::GetInstance().EnableSnap(true); 
__super::_OnPostInstall(); 
} 


virtual void _OnRestartTool() override 
{ 
PlaceBsSurfaceTool *pTool = new PlaceBsSurfaceTool(GetToolId(), GetToolPrompt()); 
pTool->InstallTool(); 
} 

 

virtual void _OnDynamicFrame(DgnButtonEventCR ev) override 
{ 
EditElementHandle eeh; 
if (!CreateBsSurface(eeh, ev.GetPoint())) return; 

RedrawElems redrawElems; 
redrawElems.SetDynamicsViews(IViewManager::GetActiveViewSet(), ev.GetViewport()); 
redrawElems.SetDrawMode(DRAW_MODE_TempDraw); 
redrawElems.SetDrawPurpose(DrawPurpose::Dynamics); 
redrawElems.DoRedraw(eeh); 
} 

 
virtual bool _OnDataButton(DgnButtonEventCR ev) override 
{ 
EditElementHandle  eeh; 
if (CreateBsSurface(eeh, ev.GetPoint())) 
eeh.AddToModel(); 
_OnReinitialize(); 
return true; 
} 

 

virtual bool _OnResetButton(DgnButtonEventCR ev) override 
{ 
_EndDynamics(); 
_ExitTool(); 
return true; 
} 

}; 

Below we explain the meaning of the above code point by point: 

2. Modify the createABsplineSurface function to the following: 

void createABsplineSurface (WCharCP unparsed) 
{ 
   PlaceBsSurfaceTool *pTool = new PlaceBsSurfaceTool (0, 0); 
   pTool->InstallTool(); 
} 

3. Open the HelloWorld.mke and add two library files BentleyAllocator.lib and DgnView.lib to the macro LINKER_LIBRARIES. The final effect is as follows: 

LINKER_LIBRARIES = $(mdlLibs)bentley.lib \
                    $(mdlLibs)mdlbltin.lib \
                    $(mdlLibs)BentleyGeom.lib \
                    $(mdlLibs)DgnPlatform.lib \
                    $(mdlLibs)BentleyAllocator.lib \
                    $(mdlLibs)DgnView.lib 

4. Save all files in VS, switch to a command prompt, and type bmake to compile.

5. Start MicroStation and enter a 3D model. After loading the HelloWorld application, type HelloWorld CreateBsplineSurface and press Enter to start the command. At this time, you can see a 3D B-spline surface on the screen following the cursor. When the left mouse button accepts, a B-spline surface will be placed at the point you specified.

So far, we have implemented the main functions of the interaction. You may notice that there is no prompt information in the status bar during the work of the tool, and the title bar of the tool settings dialog box only displays Tool Settings instead of a more intuitive command, which shows that our tool is not perfect. Let's continue to transform HelloWorld to work exactly like a tool in MicroStation.

6. Return to Visual Studio, create a new HelloWorld.h header file under Header Files, and enter the following content in it and save it. 

#define STRINGLISTID_Commands         1 
#define STRINGLISTID_Prompts          2 
#define CMDNAME_PlaceBsSurfaceTool    1 
#define PROMPT_PlaceBsSurfaceTool     1

7. Enter the following in HelloWorld.r and include HelloWorld.h in the file header and save: 

//defines the tool name identifier and tool name string
MessageList STRINGLISTID_Commands = 
{ 
    { 
        { CMDNAME_PlaceBsSurfaceTool, "Place B-Spline Surface" },  
    } 
}; 

 
MessageList STRINGLISTID_Prompts = 
{ 
    { 
        { PROMPT_PlaceBsSurfaceTool, "Specify a point" },  
    } 
};

Note: To simplify the project, we directly put these contents into HelloWorld.r. When you read the examples included with the SDK, you will notice that these contents are individually defined in a xxxmsg.r resource file, which is language-dependent and located in a folder called English. This facilitates the localization of the project. For example, when you want to generate the French version of the project, you only need to copy the English folder into a flat french folder, then change the English strings in all files under the french file to French strings, and in the mke file, change langSpec = $ (baseDir)english/ to langSpec = $ (baseDir)french/ and recompile the project.

8. Include HelloWorld.h in the header of the HelloWorldCmd.r file: #include "HelloWorld.h" .Then add the fifth column to the fourth row of the CT_CREATE table as follows: 

{ 4,  CT_NONE, INHERIT,   NONE, "BsplineSurface",  CMDNAME_PlaceBsSurfaceTool}

9. Make three changes to HelloWorld.cpp: 

a. Include HelloWorld.h in the file header.

b. Modify the createABsplineSurface function as follows: 

void createABsplineSurface (char *unparsed)
{
PlaceBsSurfaceTool*pTool= new PlaceBsSurfaceTool(
    CMDNAME_PlaceBsSurfaceTool,PROMPT_PlaceBsSurfaceTool
);
pTool->InstallTool();
}

c. Finally add the following line of code to the MdlMain function: 

mdlState_registerStringIds  ( STRINGLISTID_Commands ,  STRINGLISTID_Prompts ); 

This function registers two information lists: STRINGLISTID_Commands and STRINGLISTID_Prompts in the system, one for the tool name (or command name), and one for the tooltip. In this way, the parameters CMDNAME_PlaceBsSurfaceTool and PROMPT_PlaceBsSurfaceTool in new PlaceBsSurfaceTool will be taken from these two message lists, respectively. Similarly, CMDNAME_PlaceBsSurfaceTool in HelloWorldCmd.r is also taken from STRINGLISTID_Commands. 

10. Unload HelloWorld in MicroStation, save all modified files in Visual Studio, recompile HelloWorld and enter the MicroStation test. You will see the status bar has the tool name and tooltip, and the tool setting dialog title has also been changed to our Tool name instead of the original typed command. 

tool

Through the implementation of the above tools, we can summarize a simple DgnPrimitiveTool tool working diagram as follows:

 flow

In addition to the most commonly used DgnPrimitiveTool, there are more complex DgnElementSetToolDgnRegionElementToolElementGraphicsTool, and LocateSubEntityTool. They are all tool base classes that support element modification: DgnElementSetTool is used to implement a basic modification command. DgnRegionElementTool is used to implement a command to create or modify a closed region. ElementGraphicsTool is used to implement a command to operate a smart entity (SmartSolid). The smart entity establishes a cache to provide operational efficiency. LocateSubEntityTool is also a tool base class for operating smart entities, which allows us to manipulate the sub-entity parts of the entity, such as the faces, edges, and vertices of the entity.Examples of the use of these classes can be found in the examples folder of the SDK. Please refer to specific examples for further study. 

Download the Source Code of this chapter : HelloWorld.zip

Prev:[[Creating a VS project and debugging the application]]Next:[[Adding User Interface to a MDL Application]]