news 2026/10/16 6:01:18

ezdxf 实战:OCS/UCS 坐标系统详解与在三维空间中放置二维实体

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ezdxf 实战:OCS/UCS 坐标系统详解与在三维空间中放置二维实体
  • 图形学

【免费下载链接】ezdxf

Python interface to DXF

项目地址:https://gitcode.com/gh_mirrors/ez/ezdxf
点击查看免费下载

本篇指南以 ezdxf 官方教程《Tutorial for OCS/UCS Usage》(ocs_usage.rst)为主线,系统讲解 WCS、UCS、OCS 三大坐标系统的定义与相互关系,并通过 CIRCLE、LWPOLYLINE、3D POLYLINE、TEXT、ARC、INSERT 六个可运行的实战示例,演示如何借助ezdxf.math.OCS与ezdxf.math.UCS类把平面二维实体准确放置到三维空间中。读完本文,你将掌握挤出向量(extrusion)、标高(elevation)、厚度(thickness)三个关键 DXF 属性的用法,理解 Arbitrary Axis Algorithm(任意轴算法)的底层原理,并能在任意朝向的平面上放置和旋转文本、圆弧与块引用。

坐标系统预备知识

在动手写代码之前,先梳理 ezdxf 涉及的四种坐标系统。完整说明见仓库中的 坐标系统概念文档,OCS 的深入分析见 对象坐标系统文档。

WCS:世界坐标系统

WCS(World Coordinate System)是参照坐标系统,其他所有坐标系统都相对于它定义,且 WCS 本身永不改变。以 WCS 度量的数值在其它坐标系统变化时保持稳定。DXF 文件中的单位可以是任意真实世界单位,AutoCAD 曾为标注与文本实体引入过缩放机制,但这更多属于 AutoCAD 主题而非 DXF 主题,ezdxf 教程并未深入展开。

UCS:用户坐标系统

UCS(User Coordinate System)是用户为了简化绘图任务而定义的工作坐标系统。所有存储于 DXF 文件中的坐标始终是 WCS 或 OCS,永远不会是 UCS——因为没有任何方法能够确定创建 UCS 坐标时激活的是哪个 UCS。因此 ezdxf 提供 UCS 类 负责 UCS 与 WCS 之间的双向换算,但用户必须牢记:只能把 WCS 或 OCS 坐标写入 DXF。

OCS:对象坐标系统

OCS(Object Coordinate System)是相对于对象自身的坐标系统,其核心目标是"在三维空间中放置二维元素"。OCS 由实体的**挤出向量(extrusion vector)**定义:只要挤出向量为(0, 0, 1)(即 WCS 的 z 轴),OCS 就与 WCS 重合,OCS 坐标等于 WCS 坐标——大多数 2D 实体都属于这种情况。

使用 OCS 的实体包括:ARC、CIRCLE、TEXT、LWPOLYLINE、HATCH、SOLID、TRACE、INSERT、IMAGE 等平面实体;而 LINE、POINT、3DFACE、3D POLYLINE、POLYMESH、POLYFACE、VIEWPORT 等实体所有点都用 WCS 表达,不属于 OCS 实体。

ezdxf 只是 DXF 的接口层,不会自动把 OCS 转换为 WCS,这是用户/应用程序的职责。每个图形实体都提供ocs()方法返回其对象坐标系统,例如 dxfgfx.py 中的实现:

def ocs(self) -> OCS: if self.dxf.is_supported("extrusion"): extrusion = self.dxf.get("extrusion", default=(0, 0, 1)) return OCS(extrusion) else: return OCS() # pass-through OCS

对应地,把圆心的 OCS 坐标转换为 WCS 只需两行:

ocs = circle.ocs() wcs_center = ocs.to_wcs(circle.dxf.center)

DCS:显示坐标系统

DCS(Display Coordinate System)是对象显示前被转换到的坐标系统,其原点为 AutoCAD 系统变量 TARGET 存储的点,z 轴为视线方向。ezdxf不以任何方式使用或支持 DCS,此处列出仅为概念完整。

决定二维实体空间形态的三个 DXF 属性

OCS 实体的所有点都位于同一平面内,即使该平面由 OCS 定位在三维空间中也依然如此。有 3 个基本 DXF 属性共同赋予二维实体空间形态:

挤出向量(Extrusion)

挤出向量定义 OCS,它是平面实体基平面(base plane)的法向量。这个基平面始终位于 WCS 原点。但需要特别注意的是:有些实体如 ELLIPSE 虽有挤出向量,却并不建立 OCS——对这类实体而言,挤出向量只定义挤出方向和距离(厚度),其余点与方向均用 WCS 表达。

标高(Elevation)

标高值定义平面实体所有点的 z 轴值,它是 OCS 值,表示实体平面到基平面的距离。

该值仅存在于R11 之前版本的 DXF 输出中,作为独立属性(组码 38)存在;在 DXF R12 及以后版本中,标高作为每个点的 z 轴值提供。但这条简单规则并不适用于所有实体:LWPOLYLINE 和 HATCH 的elevation是一个 3D 点属性,其中 z 值为标高高度,x 值与 y 值恒为 0。

厚度(Thickness)

厚度定义实体的挤出距离(extrusion distance)。AutoCAD 对 TEXT 实体仅在.shx字体下支持厚度,TrueType 字体不支持。

关于本教程的版本说明

原教程标注:ezdxf v0.11 之后提供了基于 UCS 变换的新版教程(ucs_transform.rst),其中 v0.13 引入的实体transform()方法可以自动处理 OCS 与挤出向量,大幅简化操作。而本教程展示的是用底层操作完成变换的"硬核方式",理解它有助于深入掌握坐标系统的本质。

Arbitrary Axis Algorithm:OCS 的 x/y 轴从何而来

对于一个给定的 z 轴(挤出)方向,三维空间中存在无穷多个坐标系统(可通过平移原点、绕 z 轴旋转 x/y 轴得到)。但对同一个 z 轴方向,只有一个 OCS,它具备以下性质(详见 ocs.rst):

  • 原点与 WCS 原点重合;
  • xy 平面内 x/y 轴的方向以一种"任意但一致"的方式计算——AutoCAD 使用任意轴算法完成该计算;
  • 由于任意轴算法的缘故,OCS 只能表示右手坐标系。

任意轴算法的核心是:给定单位长度的 z 轴向量Az,生成对应的 x 轴向量Ax,再由右手定则得出 y 轴向量Ay。用Vec3表达如下:

Az = Vec3(entity.dxf.extrusion).normalize() # 法向(挤出)向量 if (abs(Az.x) < 1/64.) and (abs(Az.y) < 1/64.): Ax = Vec3(0, 1, 0).cross(Az).normalize() else: Ax = Vec3(0, 0, 1).cross(Az).normalize() Ay = Az.cross(Ax).normalize()

WCS→OCS 的坐标变换公式:

def wcs_to_ocs(point): px, py, pz = Vec3(point) # WCS 中的点 x = px * Ax.x + py * Ax.y + pz * Ax.z y = px * Ay.x + py * Ay.y + pz * Ay.z z = px * Az.x + py * Az.y + pz * Az.z return Vec3(x, y, z)

OCS→WCS 的坐标变换公式:

Wx = wcs_to_ocs((1, 0, 0)) Wy = wcs_to_ocs((0, 1, 0)) Wz = wcs_to_ocs((0, 0, 1)) def ocs_to_wcs(point): px, py, pz = Vec3(point) # OCS 中的点 x = px * Wx.x + py * Wx.y + pz * Wx.z y = px * Wy.x + py * Wy.y + pz * Wy.z z = px * Wz.x + py * Wz.y + pz * Wz.z return Vec3(x, y, z)

ezdxf 中的实现:OCS 类

ezdxf 的 OCS 类 直接实现了上述算法,构造时传入挤出向量即可:

class OCS: def __init__(self, extrusion: UVec = Z_AXIS): Az = Vec3(extrusion).normalize() self.transform = not Az.isclose(Z_AXIS) if self.transform: if (abs(Az.x) < _1_OVER_64) and (abs(Az.y) < _1_OVER_64): Ax = Y_AXIS.cross(Az) else: Ax = Z_AXIS.cross(Az) Ax = Ax.normalize() Ay = Az.cross(Ax).normalize() self.matrix = Matrix44.ucs(Ax, Ay, Az)

源码中_1_OVER_64 = 1.0 / 64.0,与算法标准一致。注意一个性能细节:当挤出向量就是 WCS z 轴时(绝大多数 2D 实体的情况),transform为False,from_wcs/to_wcs直接原样返回点,不做矩阵乘法。

该实现由单元测试验证,见 test_610_ocs.py:例如以实际 DXF 文件中的挤出方向(0.7081979129501316, 0.0754851955385861, 0.7019670229772758)建立 OCS,断言ocs.from_wcs((-9.56460754, 8.44764172, 9.97894327))得到(9.41378764657076, 13.15481838975576, 0.8689258932616031),双向换算精度达 6 位小数。

场景一:在三维空间放置 2D 圆(CIRCLE)

完整示例代码见 src/ocs/circle.py,核心片段如下:

import ezdxf from ezdxf.math import OCS doc = ezdxf.new('R2010') msp = doc.modelspace() # 本例中 OCS 绕 x 轴旋转 45 度 # OCS z 轴: x=0, y=1, z=1 # 此处挤出向量无需归一化 ocs = OCS((0, 1, 1)) msp.add_circle( # 可以把 2D 圆放置在 3D 空间中, # 但必须把 WCS 坐标转换为 OCS 坐标 center=ocs.from_wcs((0, 2, 2)), # center in OCS: (0.0, 0.0, 2.82842712474619) radius=1, dxfattribs={ # 这里的挤出向量应归一化, # 使用 ocs.uz 可以保证这一点 'extrusion': ocs.uz, 'color': 1, }) # 在 WCS 中标记圆心 msp.add_point((0, 2, 2), dxfattribs={'color': 1}) print(f"center in OCS: {ocs.from_wcs((0, 2, 2))}") ocs.render_axis(msp) doc.saveas(OUT_DIR / 'ocs_circle.dxf')

要点拆解:

  • OCS((0, 1, 1))构造一个绕 x 轴旋转 45° 的对象坐标系统,构造时传入的向量无需归一化(Vec3.normalize()会在内部完成);
  • 圆心在 WCS 中位于(0, 2, 2),必须先用ocs.from_wcs()转换为 OCS 坐标(0.0, 0.0, 2.82842712474619)再写入实体,否则圆会放错位置;
  • 挤出向量写入时应归一化,直接使用属性ocs.uz即可拿到归一化后的 z 轴单位向量;
  • ocs.render_axis(msp)把 OCS 的 x/y/z 轴以带颜色的 3D 直线渲染到模型空间(红色 x 轴、绿色 y 轴、蓝色 z 轴,遵循 AutoCAD 轴色标准),便于在 CAD 中核对。

图中蓝色线即为 OCS 的 z 轴(挤出方向),标高为原点到圆心的距离(本例 2.828),且可以看到 OCS 的 x、y 轴与 WCS 的 x、y 轴并不对齐。

场景二:在三维空间放置 LWPolyline

LWPOLYLINE 只接受 2D 点,并拥有独立的elevationDXF 属性,所有点共享同一个 OCS z 值。为了计算简便,本示例使用 UCS 类 先把 2D 五边形放到一个用户坐标系统中,再统一转换到 OCS。完整代码见 src/ocs/lwpolyline.py:

import ezdxf from ezdxf.math import Vec3, UCS from pathlib import Path OUT_DIR = Path('~/Desktop/Outbox').expanduser() doc = ezdxf.new('R2010') msp = doc.modelspace() # 五边形中心应位于 (0, 2, 2),图形绕 x 轴旋转 45 度, # 为此使用一个 z 轴为 (0, 1, 1)、x 轴平行于 WCS x 轴的 UCS。 ucs = UCS( origin=(0, 2, 2), # 五边形中心 ux=(1, 0, 0), # x 轴平行于 WCS x 轴 uz=(0, 1, 1), # z 轴 ) # 在局部(UCS)坐标中计算角点 points = [Vec3.from_deg_angle((360 / 5) * n) for n in range(5)] # 把 UCS 坐标转换为 OCS 坐标 ocs_points = list(ucs.points_to_ocs(points)) # LWPOLYLINE 只接受 2D 点,并有独立的 DXF 属性 elevation。 # 所有点在 OCS 中具有相同的 z 值(标高)! elevation = ocs_points[0].z msp.add_lwpolyline( points=ocs_points, format='xy', # 忽略 z 轴 close=True, dxfattribs={ 'elevation': elevation, 'extrusion': ucs.uz, 'color': 1, }) from ezdxf.math import OCS OCS(ucs.uz).render_axis(msp) ucs.render_axis(msp) doc.saveas(OUT_DIR / 'ocs_lwpolyline.dxf')

要点拆解:

  • UCS 与 OCS 的关系:本示例中 UCS 的 z 轴与 OCS 的 z 轴(挤出方向)同向,UCS 的 x 轴与 WCS 的 x 轴同向,而三者 y 轴互不对齐;ucs.points_to_ocs()内部先经 WCS 中转,再以OCS(self.uz)完成到 OCS 的换算(见 ucs.py 中的实现);
  • 由于 LWPOLYLINE 只有 2D 点,format='xy'让 ezdxf 丢弃 z 值,标高则单独写入elevation属性;
  • elevation = ocs_points[0].z直接取第一个角点的 z 值即可,因为所有角点在 OCS 中拥有相同标高;
  • 同时渲染 OCS 与 UCS 的轴便于对比:五边形中心的 3 条线是 UCS 轴,原点处的彩色线是 OCS 轴,原点处的白色线是 WCS 轴。

场景三:使用 UCS 放置 3D Polyline

对于真正的三维图形,直接使用 3D POLYLINE 更加简单——它不需要 OCS,所有点都是 WCS 坐标。UCS 在此类三维操作中非常趁手。完整代码见 src/ocs/polyline3d.py:

import math import ezdxf from ezdxf.math import UCS, Matrix44 from pathlib import Path OUT_DIR = Path('~/Desktop/Outbox').expanduser() doc = ezdxf.new('R2010') msp = doc.modelspace() # 使用 UCS 简化 3D 操作,UCS 可以在稍后定义 # 在局部(UCS)坐标中计算角点,无需 Vec3 类 angle = math.radians(360 / 5) corners_ucs = [(math.cos(angle * n), math.sin(angle * n), 0) for n in range(5)] # 做一些变换 tmatrix = Matrix44.chain( # 创建变换矩阵 Matrix44.z_rotate(math.radians(15)), # 1. 绕 z 轴旋转 Matrix44.translate(0, .333, .333), # 2. 平移 ) transformed_corners_ucs = tmatrix.transform_vertices(corners_ucs) # 把 UCS 变换为 WCS ucs = UCS( origin=(0, 2, 2), # 五边形中心 ux=(1, 0, 0), # x 轴平行于 WCS x 轴 uz=(0, 1, 1), # z 轴 ) corners_wcs = list(ucs.points_to_wcs(transformed_corners_ucs)) msp.add_polyline3d( points=corners_wcs, close=True, ) # 从中心到各角点添加连线 center_wcs = ucs.to_wcs((0, .333, .333)) for corner in corners_wcs: msp.add_line(center_wcs, corner, dxfattribs={'color': 1}) ucs.render_axis(msp) doc.saveas(OUT_DIR / 'ucs_polyline3d.dxf')

要点拆解:

  • Matrix44.chain按参数顺序级联多个 4×4 变换矩阵:先绕局部 z 轴旋转 15°,再平移(0, 0.333, 0.333),得到变换后的五边形顶点;
  • ucs.points_to_wcs()一次性把多个 UCS 点变换到 WCS,得到的顶点直接写入add_polyline3d(),无需任何 OCS 换算;
  • 最后ucs.render_axis(msp)渲染 UCS 坐标轴用于可视化验证。

场景四:在三维空间放置 2D 文本(TEXT)

文本在三维空间放置的难点在于旋转角度:TEXT 的旋转总是绕 OCS z 轴逆时针进行,0度是 OCS x 轴正方向,而 OCS x 轴由任意轴算法计算得出。因此把 UCS(或 WCS)中的文本方向换算成 OCS 旋转角度的标准流程是:

  1. 把 UCS 中的文本旋转角度转换为方向向量(或直接以方向向量为起点);
  2. 用UCS.to_ocs()把方向向量变换到 OCS;
  3. 取 OCS 向量在 xy 平面内的角度(angle_deg)作为旋转角。

这一流程已被封装为UCS.to_ocs_angle_deg()与UCS.to_ocs_angle_rad()两个便捷方法(见 ucs.py 中的实现),二者内部都通过ucs_direction_to_ocs_direction()完成方向向量的 UCS→WCS→OCS 两级变换。

完整代码见 src/ocs/text.py:

import ezdxf from ezdxf.math import UCS, Vec3 from pathlib import Path OUT_DIR = Path('~/Desktop/Outbox').expanduser() doc = ezdxf.new('R2010') msp = doc.modelspace() # 文本厚度仅对 shx 字体有效,对 TrueType 字体无效 doc.styles.new('TXT', dxfattribs={'font': 'romans.shx'}) ucs = UCS(origin=(0, 2, 2), ux=(1, 0, 0), uz=(0, 1, 1)) # 把文本方向计算为 OCS 中的角度: # 先把 UCS 中的文本旋转角度(度数)转换为向量 text_direction = Vec3.from_deg_angle(-45) # 把向量变换到 OCS,并求向量在 xy 平面内的角度 rotation = ucs.to_ocs(text_direction).angle_deg text = msp.add_text( text="TEXT", dxfattribs={ # 文本旋转角度(度数,OCS 内) 'rotation': rotation, 'extrusion': ucs.uz, 'thickness': .333, 'color': 1, 'style': 'TXT', }) # 在 OCS 中设置文本位置 text.set_pos(ucs.to_ocs((0, 0, 0)), align='MIDDLE_CENTER') ucs.render_axis(msp) doc.saveas(OUT_DIR / 'ocs_text.dxf')

提示:从 UCS 计算 OCS 角度时务必注意,TEXT、ARC 这类 2D 实体是平行于 UCS 的 xy 平面放置的,角度换算必须基于该平面内的方向向量进行。

场景五:在三维空间放置 2D 圆弧(ARC)

ARC 与文本面临同样的问题:需要以度数给出 OCS 内的起始角与终止角。本示例同时展示了计算 OCS 角度的快捷方式——直接调用ucs.to_ocs_angle_deg()。完整代码见 src/ocs/arc.py:

import ezdxf from ezdxf.math import UCS, Vec3 from pathlib import Path OUT_DIR = Path('~/Desktop/Outbox').expanduser() doc = ezdxf.new('R2010') msp = doc.modelspace() ucs = UCS(origin=(0, 2, 2), ux=(1, 0, 0), uz=(0, 1, 1)) msp.add_arc( center=ucs.to_ocs((0, 0)), radius=1, start_angle=ucs.to_ocs_angle_deg(45), end_angle=ucs.to_ocs_angle_deg(270), dxfattribs={ 'extrusion': ucs.uz, 'color': 1, }) center = ucs.to_wcs((0, 0)) msp.add_line( start=center, end=ucs.to_wcs(Vec3.from_deg_angle(45)), dxfattribs={'color': 1}, ) msp.add_line( start=center, end=ucs.to_wcs(Vec3.from_deg_angle(270)), dxfattribs={'color': 1}, ) ucs.render_axis(msp) doc.saveas(OUT_DIR / 'ocs_arc.dxf')

与文本示例对比:这里不再手动构造方向向量再取角度,而是把 UCS 内的45°与270°直接交给to_ocs_angle_deg()完成变换,代码更简洁;圆心的处理则是ucs.to_ocs((0, 0))(UCS 点 → OCS 点),验证用的辅助直线端点仍以 WCS 表达(ucs.to_wcs(...))。

场景六:在三维空间放置块引用(INSERT)

尽管块引用(INSERT)内部可以包含 LINE、MESH 等真正的三维实体,但 INSERT 实体本身的放置原理与 TEXT、ARC 相同:用 OCS 坐标定位,绕 OCS z 轴旋转。Insert.dxf.rotation让块引用绕块的 z 轴旋转,而块的 z 轴位于Block.dxf.base_point处。

完整示例见 src/ocs/insert.py,以下逐一拆解其中关键片段。

绕 WCS x 轴旋转块引用

若要把块引用绕 WCS x 轴旋转,需要先把块的 z 轴变换到 WCS x 轴方向——即用 UCS 把块 z 轴绕 y 轴逆时针旋转 90°。示例先定义一个通用的 UCS 旋转辅助函数:

# 绕任意轴旋转 UCS: def ucs_rotation(ucs: UCS, axis: Vec3, angle: float): # ezdxf v0.11 起可直接使用 UCS.rotate(axis, angle) t = Matrix44.axis_rotate(axis, math.radians(angle)) ux, uy, uz = t.transform_vertices([ucs.ux, ucs.uy, ucs.uz]) return UCS(origin=ucs.origin, ux=ux, uy=uy, uz=uz)

然后创建绕Y_AXIS旋转 90° 的 UCS,把插入位置变换到 OCS,并把 WCS x 轴方向的旋转角度 15° 变换为 OCS 角度:

ucs = ucs_rotation(UCS(), axis=Y_AXIS, angle=90) # 把插入位置变换到 OCS insert = ucs.to_ocs((0, 0, 0)) # 绕 z 轴(= WCS x 轴)的旋转角度 rotation = ucs.to_ocs_angle_deg(15) msp.add_blockref('CSYS', insert, dxfattribs={ 'extrusion': ucs.uz, 'rotation': rotation, })

绕块 x 轴旋转块引用

要绕除块 z 轴之外的任意轴旋转块引用,必须求出旋转后块 z 轴(挤出向量)的方向。下面的示例绕块 x 轴旋转 15°:

# t 是绕 x 轴旋转 15 度的变换矩阵 t = Matrix44.axis_rotate(axis=X_AXIS, angle=math.radians(15)) # 把块 z 轴变换为新的 UCS z 轴(= 挤出向量) uz = Vec3(t.transform(Z_AXIS)) # 在插入点创建新 UCS;因为是绕 x 轴旋转, # ux 仍为 WCS x 轴,uz 是旋转后的 z 轴。 ucs = UCS(origin=(1, 2, 0), ux=X_AXIS, uz=uz) # 把插入位置变换到 OCS,块 base_point=(0, 0, 0) insert = ucs.to_ocs((0, 0, 0)) # 本例无需绕 z 轴旋转 rotation = 0 blockref = msp.add_blockref('CSYS', insert, dxfattribs={ 'extrusion': ucs.uz, 'rotation': rotation, })

平移已建立 OCS 的块引用

先通过blockref.ocs()取回实体既有的 OCS(其挤出向量已存储于实体中),把 OCS 插入点转回 WCS 做平移,再转回 OCS 写回:

translation = Vec3(-3, -1, 1) # 取回既有 OCS ocs = blockref.ocs() # 取得 WCS 中的插入位置 actual_wcs_location = ocs.to_wcs(blockref.dxf.insert) # 平移位置 new_wcs_location = actual_wcs_location + translation # 把 WCS 位置转换回 OCS 位置 blockref.dxf.insert = ocs.from_wcs(new_wcs_location)

仅设置新的插入位置也是同样的流程:把新位置变换进 OCS 即可(不加平移向量)。

绕块 y 轴旋转已建立 OCS 的块引用

这个操作代表"绕任意轴旋转带 OCS 的块引用"的完整套路,共分四步:

第 1 步:把块 y 轴(旋转轴)与块 z 轴(挤出向量)从 OCS 变换到 WCS:

ocs = blockref.ocs() # 把块 y 轴(= 旋转轴)变换为 WCS 向量 rotation_axis = ocs.to_wcs((0, 1, 0)) # 把局部 z 轴(= 挤出向量)变换为 WCS 向量 local_z_axis = ocs.to_wcs((0, 0, 1))

第 2 步:构建绕该旋转轴旋转 -90° 的变换矩阵,变换挤出向量,并构建新 UCS(块引用原点位置不变,无需旋转):

t = Matrix44.axis_rotate(axis=rotation_axis, angle=math.radians(-90)) uz = t.transform(local_z_axis) uy = rotation_axis # 块引用原点保持在同一位置,无需旋转 wcs_insert = ocs.to_wcs(blockref.dxf.insert) # 构建新 UCS,用于把 WCS 位置与角度转换到 OCS ucs = UCS(origin=wcs_insert, uy=uy, uz=uz)

第 3 步:设置新的 OCS 属性——注意即使没有绕局部 z 轴旋转块引用,也必须设置 rotation 属性,因为新块的 x 轴(0 度方向)与 OCS 的 x 轴不同,需要修正:

# 设置新的 OCS blockref.dxf.extrusion = ucs.uz # 设置新的插入位置 blockref.dxf.insert = ucs.to_ocs((0, 0, 0)) # 设置新的旋转:虽然没有绕局部 z 轴旋转块引用, # 但新块 x 轴(0 度)与 OCS x 轴不同,必须修正 blockref.dxf.rotation = ucs.to_ocs_angle_deg(0)

至此,块引用的平移与任意轴旋转都可以在"OCS 坐标定位 + OCS 角度旋转"的框架内完成。这也是原教程作者所言的"数学知识边界"——更高级的 CAD 操作需要结合矩阵论等更深的知识自行探索。

进阶路线与参考资料

  • 新版 UCS 变换教程:本教程展示的是底层"硬核"操作;若使用 ezdxf v0.13+ 的实体transform()接口,OCS 与挤出向量会被自动处理,参见 ucs_transform.rst;
  • 坐标系统概念:coordinates.rst 汇总了 WCS/UCS/OCS/DCS 的定义与"只存储 WCS 或 OCS 坐标"的核心原则;
  • OCS 深入原理:ocs.rst 给出了任意轴算法的完整推导、WCS↔OCS 变换公式以及全部 OCS 实体清单;
  • 源码实现:UCS 与 OCS 类 是坐标换算的底层实现;图形实体的ocs()方法见 dxfgfx.py;
  • 单元测试:test_610_ocs.py 验证 OCS 双向换算精度,test_611_ucs.py 覆盖 UCS 的构造、平移、旋转与角度变换,可作回归参考。
  • 图形学

【免费下载链接】ezdxf

Python interface to DXF

项目地址:https://gitcode.com/gh_mirrors/ez/ezdxf
点击查看免费下载
上一篇:告别框架混战:Ivy多框架环境变量配置终极指南
下一篇:IDM-Activation-Script多架构支持:x86、x64、ARM64兼容性

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 6:00:05

超螺旋滑模观测器抑制PMSM无位置传感器控制抖振的Matlab复现

干电机控制的都知道&#xff0c;无位置传感器控制这块&#xff0c;滑模观测器是绕不开的主流。最近我在复现一个基于高速滑模观测器优化抖振问题的永磁同步电机无位置传感器控制算法&#xff0c;Matlab代码从零搭到仿真收敛&#xff0c;中间踩了不少坑&#xff0c;也把传统一阶…

作者头像 李华
网站建设 2026/10/16 6:00:05

两张静态图变16帧卡通动画:ToonCrafter 图像插值免费上手

两张静态图变16帧卡通动画&#xff1a;ToonCrafter 图像插值免费上手 【免费下载链接】ToonCrafter [SIGGRAPH Asia 2024, Journal Track] ToonCrafter: Generative Cartoon Interpolation 项目地址: https://gitcode.com/GitHub_Trending/to/ToonCrafter ToonCrafter 是…

作者头像 李华
网站建设 2026/10/16 5:57:57

轻量级ERP进销存系统源码解析:小微企业从零搭建管理工具

轻量级ERP进销存系统源码&#xff5c;专为小微企业、个体工商户及个人记账设计做开发这些年&#xff0c;陆陆续续帮朋友和客户折腾过好几套进销存系统。从最初拿Excel表格硬撑&#xff0c;到后来用商用SaaS系统&#xff0c;再到自己动手写轻量级ERP源码&#xff0c;这条路上踩过…

作者头像 李华
网站建设 2026/10/16 5:57:40

C++ STL高阶实战:容器底层、迭代器失效与性能优化

看到标题里写着“高阶篇”&#xff0c;我猜你应该已经能把vector、map、sort用得飞起了。最近我在排查一个线上服务的偶发抖动时&#xff0c;最终定位到的不是业务代码&#xff0c;而是 STL 容器的 reallocation 行为和迭代器失效问题。查到最后我发现&#xff0c;真正能把 STL…

作者头像 李华
网站建设 2026/10/16 5:56:04

微信小程序开发费用全解析:从模板到定制的成本指南

微信小程序开发需要多少钱&#xff1f;这个问题几乎每周都有人向我问起&#xff0c;而且每次问的人处境都不同&#xff1a;有街边餐馆老板&#xff0c;有刚拿到融资的创业者&#xff0c;也有公司里被领导派来“打听打听”的产品经理。我看过几百块的模板&#xff0c;也接触过报…

作者头像 李华