我们使用Mstn查看Dgn文件时,通过打开视图属性中的“ACS Triad”开关可以在三维视图中显示出ACS坐标系,如下图所示:
当我们放大或者缩小视图范围时,可以看到这个ACS坐标系的大小是不会发生变化的。那么我们如何通过开发实现同样的效果呢?Mstn SDK中创建或者绘制图形元素的接口函数所需的几何参数基本上Uor单位(C# COM接口函数所需的几何参数都是主单位),我们通过常规方法绘制出来的图形元素大小是固定的,随着视图的放大或者缩小,元素大小也会跟着发生变化。我们需要找到一种方法,在视图放大或缩小时,系统能给我们一个机会去改变元素的大小,这样我们可以以一种特殊的单位例如屏幕像素去绘制图形的话,就可以实现跟ACS坐标系同样的效果。总结起来就是需要解决两个问题,一是如何监听视图更新的事件,二是如何以像素为单位绘制图形。我们先看第一个问题,Mstn SDK中有一个名“IViewManager”的类,在这个类型下边有各种各样的成员函数可以让我们注册不同的回调函数来监听视图的各种事件,其定义如下所示:
其中AddViewTransientHandler和DropViewTransientHandler可以让我们添加或者移除一个处理器,通过处理器我们可以在视图中绘制一个临时元素。当视图发生各种事件时,会触发我们的处理器,我们在处理器的事件回调函数中去重绘图形即可。处理器的类型“IViewTransients”,其定义如下所示:
我们需要重写_DrawTransients虚函数,在重写的函数中去绘制我们的图形。函数的第一个参数是事件发生的视图上下文环境类型实例,通过这个实例我们可以判断出视图发生了什么事件或者说是什么原因需要重新绘制。第二个参数可以判断出是在视图更新之前还是之后发生的回调。
接下来是第二个问题如何以像素为单位绘制图形,在ViewContext类型下有一个名为GetPixelSizeAtPoint的成员函数,其原型如下所示:
//! Calculate the size of a "pixel" at a given point in the current local coordinate system. This method can be used to //! approximate how large geometry in local coordinates will appear in DgnCoordSystem::View units. //! @param[in] origin The point at which the pixel size is calculated. This point is only relevant in camera views, where local coordinates //! closer to the eye are larger than those further from the eye. May be NULL, in which case the center of the view is used. //! @return the length, in the current coordinate system units, of a unit bvector in the x direction in DgnCoordSystem::View, starting at \c origin. DGNPLATFORM_EXPORT double GetPixelSizeAtPoint (DPoint3dCP origin) const;
这个函数可以返回在设计坐标系下指定坐标点处一个屏幕像素对应多少个Uor单位,我们在程序中在调用绘制图形的接口函数时,传递的几何参数乘上这个函数的返回值以后,效果就类似于以屏幕像素为单位去绘制图形了。因为像素大小是固定的,不会随着视图的放大或缩小而改变,所以绘制出来的图形从用户角度看起来大小是不变的。如此一来就实现了我们想要的效果,具体实现代码如下所示:
struct MyViewTransients : IViewTransients { void _DrawTransients(ViewContextR context, bool isPreUpdate) override { if (isPreUpdate) return; DPoint3d ptZero = { 0 }; double uorPerPixel = context.GetPixelSizeAtPoint(&ptZero); DPoint3d ptA = { 0 }; DPoint3d ptB = { 0,0,uorPerPixel * 30 }; double rA = 10 * uorPerPixel, rB = 20 * uorPerPixel; IViewDrawR output = context.GetIViewDraw(); output.SetSymbology(0x0000ff00, 0x00ff0000, 4, 0); // opaque green (color is TBGR packed int), weight 4... switch (context.GetDrawPurpose()) { case DrawPurpose::UpdateDynamic: case DrawPurpose::FitView: // Can support FitView for transient case DrawPurpose::Update: output.DrawCone(ptA, ptB, rA, rB, true); break; } } }; MyViewTransients s_ViewTransient; void turnOnViewTransient() { IViewManagerR viewMgr = IViewManager::GetManager(); viewMgr.AddViewTransientHandler(&s_ViewTransient); }void turnOffViewTransient() { IViewManagerR viewMgr = IViewManager::GetManager(); viewMgr.DropViewTransientHandler(&s_ViewTransient); }
通过调用turnOnViewTransient函数可以启动绘制临时元素,turnOnViewTransient函数可以关闭绘制临时元素。我们是在设计坐标系下的(0,0,0)点处绘制了一个Cone元素,如下图所示:
上图左边的是一个普通的圆柱元素,我们将视图放大,可以看到左边的普通元素会变大,但是右边我们程序绘制的临时元素大小保持不变如下所示: