====== 高级 DXF 主题 ====== 本章介绍 DXF 二次开发中涉及的进阶主题,包括句柄参照、扩展数据、子类标记、对象坐标系和任意轴算法等。 ===== 句柄与参照 ===== ==== 句柄(Handle)概念 ==== 句柄(组码 **5**)是 DXF 文件中每个对象/图元的唯一标识符,格式为最多 16 个十六进制数字的字符串。句柄在整个 DXF 文件中唯一,由 AutoCAD 自动分配。 句柄的关键特性: * 每次新建文件或保存文件时重新生成 * 在 INSERT 和 XREF 操作期间**会**进行转换 * 用于建立对象之间的引用关系 ==== 句柄参照类型 ==== DXF 使用不同的组码范围来表示不同类型的句柄参照: ^ 组码范围 ^ 参照类型 ^ 说明 ^ | 320-329 | 任意句柄 | "按原样"获取,在 INSERT/XREF 操作期间不进行转换 | | 330-339 | 软指针 | 指向其他对象,不表示所有权关系 | | 340-349 | 硬指针 | 指向其他对象的强引用 | | 350-359 | 软所有者 | 所有者可以删除被所有者,但不强制执行 | | 360-369 | 硬所有者 | 所有者可以删除被所有者,强制执行 | | 390-399 | PlotStyle 句柄 | 用于打印样式对象 | ==== 指针与所有权的区别 ==== * **指针参照**(Pointer):对象 A 引用对象 B,但 A 并不拥有 B。例如,图元引用一个 LAYER,但图元不拥有该图层。 * **所有者参照**(Owner):对象 A 拥有对象 B,当 A 被删除时,B 也会被删除。例如,LAYOUT 词典拥有多个 LAYOUT 对象。 ==== 组码 1005 的特殊用途 ==== 组码 1005 在扩展数据中用于存储句柄值,其特殊之处在于不进行 INSERT/XREF 转换。在扩展数据中使用句柄时,需使用 1005 组码而非 330-369 组码。 ===== 子类标记 ===== ==== 子类标记的概念 ==== 组码 **100** 用于存储**子类数据标记**(Subclass Marker),该标记指示从哪个 ObjectARX 派生类定义了后续的数据。子类标记是理解 DXF 对象继承关系的关键。 子类标记示例: * **AcDbEntity** — 所有图元的基本类 * **AcDbCircle** — 圆的具体类 * **AcDbLine** — 直线的具体类 * **AcDbPolyline** — 多段线的具体类 * **AcDbBlockReference** — 块参照 ==== 继承链与子类标记 ==== 一个对象的完整继承链通过多个子类标记体现。以直线为例: 0 ; 图元开始 LINE 5 42 100 AcDbEntity ; 通用图元数据(图层、颜色、线型等) 100 AcDbLine ; 直线特定数据(起点、终点坐标) 10 0.0 20 0.0 11 100.0 21 100.0 从 ObjectARX 派生的每个具体类**必须**拥有子类标记。 ===== 扩展数据(XDATA) ===== 扩展数据(Extended Data,简称 XDATA)允许应用程序在 DXF 对象或图元中存储自定义数据。所有扩展数据都以组码 **-3** 为标记。 ==== XDATA 结构 ==== -3 ; 扩展数据开始标记(固定) 1001 APP_NAME ; 注册的应用程序名 1000 ... ; 扩展数据内容 ==== XDATA 支持的组码 ==== ^ 组码 ^ 说明 ^ | 1000 | ASCII 字符串(最多 255 字节) | | 1001 | 注册的应用程序名(最多 31 字节) | | 1002 | 控制字符串("{" 开始, "}" 结束) | | 1003 | 图层名 | | 1004 | 二进制数据块(最多 127 字节) | | 1005 | 图元句柄 | | 1010 | 三维点 | | 1011 | 三维世界空间位置 | | 1012 | 三维世界空间位移 | | 1013 | 三维空间方向 | | 1040 | 双精度浮点值 | | 1041 | 距离值 | | 1042 | 缩放比例 | | 1070 | 16 位有符号整数 | | 1071 | 32 位有符号长整数 | ==== XDATA 组织方式 ==== 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 ===== 对象坐标系(OCS) ===== ==== OCS 简介 ==== 对象坐标系(Object Coordinate System,OCS)是通过**拉伸方向**(组码 210/220/230)和**任意轴算法**建立的坐标系。在 OCS 中定义点的图元,其点坐标使用该图元特有的坐标系。 使用 OCS 的图元类型:LINE、CIRCLE、ARC、TEXT 等。 不使用 OCS 的图元类型:LWPOLYLINE(使用标高+OCS)、3DFACE(使用世界坐标系)、POLYLINE(使用世界坐标系)。 ==== OCS 与 WCS 的转换 ==== 拉伸方向(组码 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 (叉积,归一化) ==== 算法实现(Python) ==== 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 应用程序的必要条件。