引线标注

最近用 C# 对 ORD 进行二次开发,做一个自动出图工具。其中必不可少的就是标注,被各种标注折腾得死去活来,特别是引线标注,坑特别多,特记录下来,供和大家学习交流,若有不正确之处,还请斧正。

简介

在 C# 中,引线标注采用的是 NoteCellHeaderElement 这个类。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 其中,arrowHeadPoint, arrowTailPnt, annotation, dgnModel 和 _dimensionCreateData 是传入的参数
TextBlock textBlock = new TextBlock(_dimensionCreateData.GetTextStyle(), DgnModel);
textBlock.AppendText(annotation);

NoteCellHeaderElement header = new NoteCellHeaderElement(out Element leaderElement, textBlock, this._dimensionCreateData.GetDimensionStyle(), DgnModel, new DPoint3d[] { arrowHeadPoint, arrowTailPnt });

// 此步骤是为了设置引线文字的对齐方式为 0 (auto),它的主要作用是当第一个点在第二个点左侧时,文字在左方,否则文字是右方
DimensionElement dim = leaderElement as DimensionElement;
DimensionStyle ds = dim.GetDimensionStyle();
ds.SetIntegerProp(0, DimStyleProp.MLNote_HorAttachment_INTEGER);
dim.ApplyDimensionStyle(ds, true);

// 必须用这种方法添加引线标注到 model 中
header.AddToModel(out dim,dgnModel);

下面详细分析为了得出上面的几行代码,需要处理的技术问题。

_dimensionCreateData 怎么来的?

_dimensionCreateDataDimensionCreateData 的实例,查看 DimensionCreateData 定义,我们可以看到,它是一个抽象类,不能实例化它,所以就想着,看在 ORD 的程序集里面找一个实现它的类,可以查找无果,最后,从优先社区中得知,需要自己子类化一下它,代码如下(代码是项目中的一部分,没有简化,不能开箱即用,仅供参考)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
using Bentley.DgnPlatformNET;
using Bentley.DgnPlatformNET.Elements;
using Bentley.GeometryNET;
using Bentley.MstnPlatformNET;
using swOpenRoadsSDK;
using SwTools.SimpleCAD.Enum.Extension;
using System.Collections.Generic;

namespace SwTools.RebarTools.SDK.Dimension
{
class SwDimesionCreateData : DimensionCreateData
{
private static Dictionary<SwDimensionStyle, DimensionStyle> _dimensionStyleDic = new Dictionary<SwDimensionStyle, DimensionStyle>();
private static Dictionary<SwTextStyle, DgnTextStyle> _textStyleDic = new Dictionary<SwTextStyle, DgnTextStyle>();
private static Dictionary<SwLevel, LevelId> _levelDic = new Dictionary<SwLevel, LevelId>();

private readonly Symbology _symbology;
private readonly DirectionFormatter _directionFormatter;

public DimensionStyle DimensionStyle { get; }
public DgnTextStyle DgnTextStyle { get; }
public LevelId LevelId { get; }

public SwDimesionCreateData(DimensionStyle dimStyle, DgnTextStyle textStyle, Symbology symb, LevelId levelId, DirectionFormatter formatter)
{
DimensionStyle = dimStyle;
DgnTextStyle = textStyle;
LevelId = levelId;

_symbology = symb;
_directionFormatter = formatter;
}

public SwDimesionCreateData(SwDimensionStyle swDimensionStyle,SwTextStyle swTextStyle,Symbology symb, SwLevel swLevel,DirectionFormatter formatter)
{
if(_dimensionStyleDic.TryGetValue(swDimensionStyle,out DimensionStyle value1))
{
DimensionStyle = value1;
}
else
{
DimensionStyle = DimensionHelper.GetDimensionStyle(swDimensionStyle.ToString());
_dimensionStyleDic.Add(swDimensionStyle, DimensionStyle);
}

if(_textStyleDic.TryGetValue(swTextStyle,out DgnTextStyle value2))
{
DgnTextStyle = value2;
}
else
{
DgnTextStyle = DrawingHelper.GetTextStyle(swTextStyle.ToStringValue());
_textStyleDic.Add(swTextStyle, DgnTextStyle);
}

if(_levelDic.TryGetValue(swLevel,out LevelId value3))
{
LevelId = value3;
}
else
{
LevelHandle levelByName = Session.Instance.GetActiveDgnFile().GetLevelCache().GetLevelByName(swLevel.ToString());
LevelId = levelByName.LevelId;
_levelDic.Add(swLevel, LevelId);
}

_symbology = symb;
_directionFormatter = formatter;
}

public override DimensionStyle GetDimensionStyle()
{
return DimensionStyle;
}
public override DgnTextStyle GetTextStyle()
{
return DgnTextStyle;
}
public override Symbology GetSymbology()
{
return _symbology;
}
public override LevelId GetLevelId()
{
return LevelId;
}
public override int GetViewNumber()
{
return 0;
}
public override DMatrix3d GetDimensionRotation()
{
return DMatrix3d.Identity;
}
public override DMatrix3d GetViewRotation()
{
return DMatrix3d.Identity;
}
public override DirectionFormatter GetDirectionFormatter()
{
return _directionFormatter;
}


#region 静态方法
public static SwDimesionCreateData GetSwBridgeAnnotationCreateData()
{
return new SwDimesionCreateData(SwDimensionStyle.SW_Bridge, SwTextStyle.CB_25, new Symbology(), SwLevel.sw_尺寸, null);
}

public static DgnTextStyle GetDgnTextStyle(SwTextStyle swTextStyle)
{
if (_textStyleDic.TryGetValue(swTextStyle, out DgnTextStyle style))
{
return style;
}
else
{
DgnTextStyle styleNew = DrawingHelper.GetTextStyle(swTextStyle.ToStringValue());
_textStyleDic.Add(swTextStyle, styleNew);
return styleNew;
}
}
#endregion
}
}

new NoteCellHeaderElement对象时,参数DPoint3d[] 中的点代表什么?

它的第 1 个点代表引线箭头的坐标,后面的点代表引线导线的关键点。一般传入两个点就可以了。

怎么实现引线和文字关联?

开始 new NoteCellHeaderElement 对象的时候,看到它 out 了一个 element, 便直接将它和 header 添加到了 model。这时查看模型元素后,发现它们俩是分开的,拽文字引线不跟着动,很难受。后面在众多相似的方法中,在 NoteCellHeaderElement 中找到一个 AddToModel的重载,它是这样的:

1
public BentleyStatus AddToModel(out Element leaderElement, DgnModel dgnCache);

从上面的定义里面,我们知道,它会 out 一个元素出来,但是,这个其实就是个坑,一个大坑。这个参数真正的意思是要将从上文得到的 leaderElement 传入进去,它里面会与 header 进行关联,并添加到 model 中。

所以,真正的用法应该是开篇示例代码那样。

怎么让文字水平吸附为自动?

如上图,如果不对参数进行设置,默认生成的引线标注是 1 和 2 的样式,如果要得到 3 ,需要设置它的对齐方式。就是下面的代码:

1
2
3
4
DimensionElement dim = leaderElement as DimensionElement;
DimensionStyle ds = dim.GetDimensionStyle();
ds.SetIntegerProp(0, DimStyleProp.MLNote_HorAttachment_INTEGER);
dim.ApplyDimensionStyle(ds, true);

怎么知道要设置哪个属性值呢?

上面是直接贴出了代码,可能就有人会问,为什么就知道是设置DimStyleProp.MLNote_HorAttachment_INTEGER 这个属性呢?下面介绍一下怎么去解决这种类似的问题。这种问题,我称之为 “操作代码化” 问题,就是说用户可以使用软件手动操作生成出来,但是现在需要用程序来实现。

这种问题的突破口就是要回归操作

  • 在应用程序中,手动放置需要生成的元素,然后通过 key-in analyze element 这个命令去查看元素的详细信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 部分信息如下
Element ID: 160542
File Name: C:\WorkSets\codeTesting\dgn\柱测试.dgn
Model Name: 桩_图纸
Last Modified: 2020/08/27 00:52:41
Type Specific Details:
Dimension Type :
IsAnnotation : Yes
Proxy Cell : <None>
Height : 0.00000
Point #0 : 0.50705, 0.26187
Point #1 : 0.50178, 0.26847 A
Rotation : | 1.0000000 0.0000000 0.0000000 |
|-0.0000000 1.0000000 0.0000000 |
| 0.0000000 0.0000000 1.0000000 |
......
  • 画一个对照元素,也通过命令查看元素的详细信息
  • 将两个详细信息进行对比,差异的部分就是导致两个元素表征显示不一样的原因,然后在程序里面去找到相应的设置。

那么现在又有一个问题了,程序里面的获得元素,都是 Element 这个父类黑箱子,而要设置,就要进行拆箱,我们不知道究竟拆成什么类型?

这个问题,可以在运行时,通过 .GetType() 来获取类型,就知道应该转成什么类型了。