上一章和大家一起学习了怎么样利用Alignment的Linear Geometry 查看详细的路线几何属性的报表。本章将带领大家更加深入的去学习Linear Geometry SDK的内容。大家在用ORD的时候,有些功能,比如执行“New Template Drop”命令时,随着鼠标的拖动,沿路线方向会有一垂直路线的虚线段随着鼠标动态的移动。如下图所示:
本章将带领大家实现一个类似的线段。本章实现的线段不光能随着鼠标的移动能有动态移动的效果,并且线段长度会随着鼠标距离路线平面线的距离的变化而变化。当再次点击鼠标时,线段加入到模型中。
一、构建程序框架
- 我们先创建一个名称为“Lesson4”的Addin项目,并按照上一章的方法配置好项目文件及.bat、.mke文件。
- 我们创建一个CommandTable.xml文件,注册“Lesson4 ADDSTATIONLINE”命令。并新建一个Keyin.cs文件,把“Lesson4 ADDSTATIONLINE”命令子节点和AddStationLine静态函数绑定
- 在ORD目录下的..\config\appl\文件夹中,创建并配置Lesson4.cfg文件。
二、创建Alignment
- 使用Geometry-> Horizontal-> Complex Geometry->Complex by PI菜单命令去绘制Alignment,如下图所示。
2. 我们会以此路线作为测试用例。
三、 交互式选取Alignment
- 新建AddStationLineCommand类派生于DgnElementSetTool类。
- 关于DgnElementSetTool的用法我们在上一章进行了讲解,可以参考上一章。
- 在DgnElementSetTool类中重载OnRestartTool、ExitTool、OnPostInstall、OnDataButton、OnElementModify、OnResetButton、OnDynamicFrame等函数。
- 我们需要实现的功能是在鼠标移动到Alignment上以后按下鼠标左键,获取Alignment元素。代码如下,这段代码与上一章类似:
protected override bool OnDataButton(Bentley.DgnPlatformNET.DgnButtonEvent ev)
{
Bentley.DgnPlatformNET.HitPath hitPath = DoLocate(ev, true, 1);
if (hitPath == null)
return false;
Element el = hitPath.GetCursorElement();
if (el == null)
return false;
Alignment al = (el.ParentElement == null) ? Alignment.CreateFromElement(el) : Alignment.CreateFromElement(el.ParentElement);
if (al == null)
return false;
return true;
}
Alignment类定义在Bentley.CifNET.GeometryModel.SDK命令空间中,我们可以知道Alignment是我们ORD SDK中GeometryModel的重要组成部分,本章我们重点应用它的LinearGeometry属性,重点去探讨LinearGeometry的用处。
四、添加Alignment和Line的成员变量
- 我们OnDataButton函数中可以执行选择Alignment对象的操作,但是我们需要在OnDynamicFrame函数中去用这个Alignment去做几何运算,所以我们需要在AddStationLineCommand类中申请一个成员变量:Alignment m_aligment = null;并在OnDataButton函数中把获取的Alignment对象存储在m_aligment,这样的话创建的m_aligment对象就可以在OnDynamicFrame函数中去用了。
- 我们拖动鼠标过后,再次点击鼠标的时候。我们需要把OnDynamicFrame函数中计算所得的Line存储起来,所以我们需要在AddStationLineCommand类中申请一个成员变量:LineElement m_line = null;并在OnDynamicFrame函数中把计算当前鼠标位置所得的直线存储这变量中。
五、实现OnDynamicFrame函数
具体实现代码如下:
protected override void OnDynamicFrame(DgnButtonEvent ev)
{
if(!DynamicsStarted)
{
return;
}
DPoint3d meter_pt = GetMeterPoint(ev.Point);
LinearPoint pt_road = m_aligment.LinearGeometry.ProjectPointOnPerpendicular(meter_pt);
if(pt_road == null)
{
return;
}
double dis = meter_pt.Distance(pt_road.Coordinates);
LinearPoint pr_l = m_aligment.LinearGeometry.GetPointAtDistanceOffset(pt_road.DistanceAlong, -dis);
LinearPoint pr_r = m_aligment.LinearGeometry.GetPointAtDistanceOffset(pt_road.DistanceAlong, dis);
DPoint3d dp_l = GetUORPoint(pr_l.Coordinates);
DPoint3d dp_r = GetUORPoint(pr_r.Coordinates);
DSegment3d seg = new DSegment3d(dp_l, dp_r);
DgnModel model = Bentley.MstnPlatformNET.Session.Instance.GetActiveDgnModel();
LineElement line = new LineElement(model, null, seg);
var redraw = new RedrawElems();
redraw.DrawMode = DgnDrawMode.TempDraw;
redraw.DrawPurpose = DrawPurpose.Dynamics;
redraw.SetDynamicsViewsFromActiveViewSet(ev.Viewport);
redraw.DoRedraw(line);
m_line = line;
}
对代码的解释如下:
- GetMeterPoint函数的作用是:把当前鼠标的坐标从UOR坐标转换为以米为单位的坐标。因为在MicroStation的坐标系单位为UOR,而ORD中的坐标单位我们设置的为米。他们之间的关系是什么呢?我们从命令File->Settings->File->Design File Settings->Working Units可以查看。结果如下图所示:
我们可以看到设置为10000单位每Meter。GetMeterPoint函数的作用就是把MicroStation的点坐标除以10000,换成ORD的点。函数的具体实现过程,可以查看本章实例代码。
2.我们把转换后的点存成局部变量meter_pt,然后通过Alignment的LinearGeometry属性的ProjectPointOnPerpendicular方法获取当前鼠标坐标与路线的垂足点,并把垂足点存储到局部变量pt_road中。
3.通过meter_pt.Distance方法获取鼠标坐标与垂足点之间的距离,并存储到变量dis中。
4. 接着我们通过Alignment的LinearGeometry属性的GetPointAtDistanceOffset方法获取Alignment在pt_road.DistanceAlong(鼠标与路线垂足处的桩号),左右偏移dis(鼠标与其路线垂足的距离)处的点。
5. 在本段代码处的LinearPoint,它是Linear Geometry SDK中标示路线上点的类。不光有普通点结构含有的x,y,z坐标信息,还有所处桩号,切向角等等工程意义的属性。
6. 我们通过获取的左右侧的LinearPoint点,我们通过这两个点去构造LineElement对象,这时的LineElement对象还不能加入到DgnModel,而是在下次OnDataButton运行时加入到DgnModel中去。
7. 在OnDynamicFrame函数中,我们只需要通过RedrawElems类的DoRedraw方法设置LineElement对象line的动态效果。
六、其它设置
- 当我们再次点击左键,动态效果过后,需要在OnDataButton函数的起始部位去判断m_line是否为空,如果不为空则调用m_line.AddToModel();函数把线段加入到当前模型中,并把m_line再次设为空值。
- 我们重载的OnResetButton函数一定调用 EndDynamics与ExitTool函数,这样的话在点击右键以后结束动态拖动和命令。
- 本章程序的效果如下: