本章介绍 DXF 二次开发中涉及的进阶主题,包括句柄参照、扩展数据、子类标记、对象坐标系和任意轴算法等。
句柄(组码 5)是 DXF 文件中每个对象/图元的唯一标识符,格式为最多 16 个十六进制数字的字符串。句柄在整个 DXF 文件中唯一,由 AutoCAD 自动分配。
句柄的关键特性:
DXF 使用不同的组码范围来表示不同类型的句柄参照:
| 组码范围 | 参照类型 | 说明 |
|---|---|---|
| 320-329 | 任意句柄 | “按原样”获取,在 INSERT/XREF 操作期间不进行转换 |
| 330-339 | 软指针 | 指向其他对象,不表示所有权关系 |
| 340-349 | 硬指针 | 指向其他对象的强引用 |
| 350-359 | 软所有者 | 所有者可以删除被所有者,但不强制执行 |
| 360-369 | 硬所有者 | 所有者可以删除被所有者,强制执行 |
| 390-399 | PlotStyle 句柄 | 用于打印样式对象 |
组码 1005 在扩展数据中用于存储句柄值,其特殊之处在于不进行 INSERT/XREF 转换。在扩展数据中使用句柄时,需使用 1005 组码而非 330-369 组码。
组码 100 用于存储子类数据标记(Subclass Marker),该标记指示从哪个 ObjectARX 派生类定义了后续的数据。子类标记是理解 DXF 对象继承关系的关键。
子类标记示例:
一个对象的完整继承链通过多个子类标记体现。以直线为例:
0 ; 图元开始 LINE 5 42 100 AcDbEntity ; 通用图元数据(图层、颜色、线型等) 100 AcDbLine ; 直线特定数据(起点、终点坐标) 10 0.0 20 0.0 11 100.0 21 100.0
从 ObjectARX 派生的每个具体类必须拥有子类标记。
扩展数据(Extended Data,简称 XDATA)允许应用程序在 DXF 对象或图元中存储自定义数据。所有扩展数据都以组码 -3 为标记。
-3 ; 扩展数据开始标记(固定) 1001 APP_NAME ; 注册的应用程序名 1000 ... ; 扩展数据内容
| 组码 | 说明 |
|---|---|
| 1000 | ASCII 字符串(最多 255 字节) |
| 1001 | 注册的应用程序名(最多 31 字节) |
| 1002 | 控制字符串(“{” 开始, “}” 结束) |
| 1003 | 图层名 |
| 1004 | 二进制数据块(最多 127 字节) |
| 1005 | 图元句柄 |
| 1010 | 三维点 |
| 1011 | 三维世界空间位置 |
| 1012 | 三维世界空间位移 |
| 1013 | 三维空间方向 |
| 1040 | 双精度浮点值 |
| 1041 | 距离值 |
| 1042 | 缩放比例 |
| 1070 | 16 位有符号整数 |
| 1071 | 32 位有符号长整数 |
XDATA 使用 1002 组码来组织数据层次:
-3
1001
MY_APP ; 应用程序名
1002
{ ; 开始一个列表
1000
Item 1
1000
Item 2
1002
{ ; 嵌套列表
1040
1.5
1040
2.5
1002
} ; 结束嵌套
1002
} ; 结束列表
使用自定义 XDATA 前,应用程序名必须在 APPID 符号表中注册。例如:
0 APPID 2 MY_APP 70 0
对象坐标系(Object Coordinate System,OCS)是通过拉伸方向(组码 210/220/230)和任意轴算法建立的坐标系。在 OCS 中定义点的图元,其点坐标使用该图元特有的坐标系。
使用 OCS 的图元类型:LINE、CIRCLE、ARC、TEXT 等。
不使用 OCS 的图元类型:LWPOLYLINE(使用标高+OCS)、3DFACE(使用世界坐标系)、POLYLINE(使用世界坐标系)。
拉伸方向(组码 210/220/230)默认为 (0, 0, 1),表示图元位于世界坐标系的 XY 平面。当拉伸方向为单位矢量 (0, 0, 1) 时,OCS 与 WCS 一致。
如果拉伸方向不是 (0, 0, 1),则需要使用任意轴算法将 OCS 坐标转换为 WCS 坐标。
任意轴算法(Arbitrary Axis Algorithm,AAA)用于计算 OCS 的 X 轴和 Y 轴方向。该算法是处理非平面图元的关键。
假设拉伸方向为 N(单位矢量):
1. 如果 |N| 非常接近 (0, 0, 1)(容差 1/64): X_axis = (1, 0, 0) Y_axis = (0, 1, 0) 结束 2. 如果 |N| 非常接近 (0, 0, -1)(容差 1/64): X_axis = (-1, 0, 0) Y_axis = (0, 1, 0) 结束 3. 否则: W = (0, 0, 1) (世界坐标 Z 轴) X_axis = W × N (叉积,归一化) Y_axis = N × X_axis (叉积,归一化)
import math def arbitrary_axis_algorithm(normal): """计算任意轴算法,返回 OCS 的 X 轴和 Y 轴方向""" x, y, z = normal # 检查是否接近 (0,0,1) 或 (0,0,-1) if abs(x) < 1/64 and abs(y) < 1/64 and abs(z) > 0: if z > 0: return (1, 0, 0), (0, 1, 0) else: return (-1, 0, 0), (0, 1, 0) # 计算 X 轴 = W × N wx, wy, wz = 0, 0, 1 x_axis = ( wy * z - wz * y, # Y*Z - Z*Y wz * x - wx * z, # Z*X - X*Z wx * y - wy * x # X*Y - Y*X ) # 归一化 X 轴 length = math.sqrt(x_axis[0]**2 + x_axis[1]**2 + x_axis[2]**2) if length > 0: x_axis = (x_axis[0]/length, x_axis[1]/length, x_axis[2]/length) # 计算 Y 轴 = N × X_axis y_axis = ( y * x_axis[2] - z * x_axis[1], z * x_axis[0] - x * x_axis[2], x * x_axis[1] - y * x_axis[0] ) # 归一化 Y 轴 length = math.sqrt(y_axis[0]**2 + y_axis[1]**2 + y_axis[2]**2) if length > 0: y_axis = (y_axis[0]/length, y_axis[1]/length, y_axis[2]/length) return x_axis, y_axis def ocs_to_wcs(point_ocs, normal): """将 OCS 坐标转换为 WCS 坐标""" x_axis, y_axis = arbitrary_axis_algorithm(normal) # OCS point = (u, v, w) # WCS point = origin + u*X_axis + v*Y_axis + w*normal u, v, w = point_ocs wx = u * x_axis[0] + v * y_axis[0] + w * normal[0] wy = u * x_axis[1] + v * y_axis[1] + w * normal[1] wz = u * x_axis[2] + v * y_axis[2] + w * normal[2] return (wx, wy, wz)
扩展词典(Extension Dictionary)允许在对象上附加自定义词典。使用组码 102 后跟 {ACAD_REACTORS 或 {ACAD_XDICTIONARY 来标识。
102
{ACAD_XDICTIONARY ; 扩展词典开始
360
FD ; 硬所有者句柄(指向词典对象)
102
} ; 扩展词典结束
永久反应器(Persistent Reactor)使得对象之间可以建立事件响应关系。当被监控的对象发生变化时,反应器对象会自动收到通知。
102
{ACAD_REACTORS ; 反应器链开始
330
F0 ; 软指针句柄(指向所有者)
102
} ; 反应器链结束
本章介绍了 DXF 开发中的高级话题。句柄管理确保了对象间的正确引用关系;子类标记使得 DXF 能够表示复杂的类继承结构;扩展数据为应用程序提供了自定义存储能力;对象坐标系和任意轴算法则是处理三维空间中非平面图元的基础。掌握这些高级概念是开发专业 DXF 应用程序的必要条件。