简介:针对 Matlab 环境下读取与解析 DXF 格式文件的需求,这份 DXFLib v0.9.1 工具包提供了可直接调用的 M 函数集合,适合本科、硕士阶段开展 CAD 数据交互、图形绘制与教研实验时使用。包内共 43 个文件,其中包含 18 个 DXF 样例,可配合 17 个 M 函数进行点、折线、多边形网格、文本标注、颜色映射等常见实体的读写测试;另有 4 份 PDF 与 4 份 TXT 文档,用于说明函数调用方式和使用注意事项。工具包整体仅约 995KB,结构清晰、无需复杂配置,能在 Matlab 2019a 中快速运行验证。目前已有 1380 人学习浏览,比较适合刚接触 DXF 接口、希望借助现成库完成课程设计或课题验证的师生。通过阅读示例代码和运行结果,可以较快掌握 dxf_open、dxf_point、dxf_polyline、dxf_polymesh 等函数的基本用法,为后续二次开发或格式转换提供基础。
1. DXFLib能读的DXF,是Matlab里最值得先摸清的一类文件
在MATLAB里读DXF,问得最多的不是命令,而是“为什么读出来全是空结构体”。DXF是AutoCAD发布的绘图交换格式,坐标、图层、线型全部以组码和值成对的方式写在ASCII文本里,DXFLib-v0.9.1.zip就是绕着这套文本结构封装的第三方函数包,读完后把图纸拆成Header、Tables、Blocks、Entities等片段,方便按图元类型直接取数。适合需要从CAD图纸里抽坐标算尺寸,或者把轮廓边界转到仿真模型里的工程师。下面按安装、组码、二次解析和文字实体四条线索把读DXF这条流程走通。
2. 在Matlab中安装DXFLib-v0.9.1并跑通首个图元读取
2.1 从zip到可用工具的三步
拿到DXFLib-v0.9.1.zip后,不要双击后在MATLAB里直接读。先解压到一个英文路径的目录,比如D:\cad_tools\DXFLib-v0.9.1。然后让MATLAB认识它,推荐用addpath配合genpath递归加入所有子目录:
addpath(genpath('D:\cad_tools\DXFLib-v0.9.1')); savepath; % 保存到预设路径,下次启动仍在addpath只添加指定目录本身,genpath会把该目录下所有层级的子目录展开,避免库函数分散在多个子文件夹里时漏配。savepath是个人偏好,我一般只在临时环境用;长期项目更推荐把这行写进startup.m或项目启动脚本。如果解压目录出现过中文路径,建议先改名,部分老函数在fopen阶段的路径拼接会对中文支持不好。
验证是否装成功,不要直接跑大文件,先查入口函数:
which DXF_read如果返回路径说明库的读入口已在搜索路径中。部分历史版本把入口命名为dxf_read或ReadDXF,没找到就列一下包里的m文件:
dir('D:\cad_tools\DXFLib-v0.9.1\**\*.m')看到哪个读函数,改调用名即可。这一步能省掉很多“明明装了却报错”的排查时间。
2.2 一个能返回实体的最小读取脚本
拿你自己的图纸就行,比如从AutoCAD里随便画几条直线和圆弧,导出为Drawing1.dxf。最小脚本如下:
clear; clc; addpath(genpath('D:\cad_tools\DXFLib-v0.9.1')); dxfFile = 'D:\cad_files\Drawing1.dxf'; d = DXF_read(dxfFile); if isempty(d) error('DXF解析结果为空,建议另存为AutoCAD 2010以下格式再试'); end disp(fieldnames(d)); % 看顶层段名 disp(fieldnames(d.Entities)); % 看图元段实现了哪些实体类型DXF_read返回的是一整个结构体,不是数组,也不是包含全部顶点的坐标矩阵。它做的是把DXF文本按段拆开:Header记录图形变量,Tables记录图层/线型/文字样式,Blocks记录块定义,Entities记录实际画出来的图元。理解这个分层,后面取数据才不会蒙。
参数说明:dxfFile建议给绝对路径,避免切换工作目录后找不到文件;fieldnames返回的是cell数组,直接disp就能看到有哪些段。读出来的结构里一般至少映射了LINE、CIRCLE、ARC、LWPOLYLINE、TEXT、INSERT这些基础图元,如果你的文件里包含SPLINE或ELLIPSE,旧版本库可能不会单独列出。
拿到d之后,第一步先看Header里的$ACADVER,它表示这个DXF按哪个AutoCAD版本写的:
if isfield(d.Header, 'ACADVER') disp(d.Header.ACADVER); endAC1009对应R12、AC1015对应AutoCAD 2000、AC1021对应2007。DXFLib-v0.9.1在AC1015之前的文件上最稳,新版本导出的DXF结构更复杂,出现未知组码的概率也更高。
2.3 读成功后先比坐标,别急着信结构
第一次解析完成后,很多人直接拿d.Entities里的坐标做计算,结果发现和CAD里差一截。常见原因有两个:单位不一致,或者坐标在Block里没做INSERT变换。
我建议先用一条直线做验证。在CAD里画一条从(0,0)到(100,50)的直线,导出DXF后用下面的方式把直线部分端点打印出来:
linesField = d.Entities.LINE; % 取出LINE数组 if ~isempty(linesField) firstLine = linesField(1); disp(firstLine); % 先看字段清单 end这里X0/Y0/X1/Y1是直线起终点字段的常见叫法,实际字段名在不同变体里可能叫Start/End或其他,拿不准就先disp整条记录再索引。比较坐标时,如果数值整体差一个量级,优先怀疑单位问题;如果完全对不上,则检查是不是用了模型空间与图纸空间混排的文件。
验证通过后再做批量提取。DXFLib可以直接读实体字段,但当图元类型多、字段嵌套深的时候,我更多会回到DXF文本层按组码二次解析。下一章把组码规则讲清楚,就能明白为什么两种做法并行最稳妥。
3. dxf组码与实体映射:读懂DXF文本里的成对结构
3.1 每一行不是坐标,是“组码-值”交替
DXF的ASCII结构没有复杂树形,只有一行组码、一行值循环组成。组码是整数,决定下一行值是什么含义。比如连续的几行文本是:
0 LINE 8 图层1 10 0.0 20 0.0 11 100.0 21 50.0这段含义是:0组码后面是实体类型LINE,8组码后面是图层名“图层1”,10/20是起点坐标,11/21是终点坐标。注意10和20必须成对出现,中间可以插入颜色、线型等其他组码,但坐标读取必须按(10, 20, 30)组合来拼。
理解“组码-值”交替后会发现,DXF读取本质上就是一段带状态机的文本解析。所谓状态,就是当前组码为0时读到的实体类型,它决定后续组码10到底代表圆心X还是起点X,或者插入点X。DXFLib把这种状态转换变成结构体字段,所以d.Entities里才会出现按类型分组的数组。
3.2 常用实体的组码对照与匹配参数
下面把工程里最容易遇到的四类实体整理成一张表,读DXF时直接对表查:
| 实体类型 | 关键组码 | 对应含义 | 补充说明 |
|---|---|---|---|
| LINE | 10/20/30 | 起点坐标 | 30常为0,代表2D直线 |
| LINE | 11/21/31 | 终点坐标 | 与起点码只差首位数字 |
| CIRCLE | 10/20/30 | 圆心坐标 | 实体名之后的10才是圆心 |
| CIRCLE | 40 | 半径 | 值永远为正 |
| ARC | 10/20/30、40 | 圆心、半径 | 半径组码与圆相同 |
| ARC | 50、51 | 起始角、终止角 | 角度单位是度,逆时针为正 |
| LWPOLYLINE | 90 | 顶点数量 | 这个数量决定你读多少组10/20 |
| LWPOLYLINE | 10、20 | 顶点坐标 | 循环出现,每次各读一组值 |
| LWPOLYLINE | 42 | 凸度 | 连接当前顶点到下一点的圆弧参数 |
| LWPOLYLINE | 70 | 闭合标志 | 1为闭合,0为开放 |
| TEXT | 10/20/30、40、1 | 插入点、字高、文字正文 | 组码1的值是字符串,不用转数字 |
第一行LINE里的起点10/20和后面CIRCLE里的圆心10/20,单看组码完全一样,区分它们的方法是当前0组码给出的实体类型。所以用DXFLib或任何现成解析库时,最常见错误是脱离上下文直接抓10、20、30三个组码。
凸度组码值得单独提醒。LWPOLYLINE的42表示当前顶点到下一点之间凸弧的凸度,值为tan(包含角/4)。取一组顶点时如果忽略42,圆弧段会被错误地当直线处理,导致轮廓偏差。DXFLib较新分支会把它放进顶点结构里,旧版本则容易整段丢失,这也是我建议在关键轮廓上做二次校验的原因。
3.3 从Tables段找图层,从Blocks段查块引用
在结构体返回里,d.Tables通常还包含图层表。图层名在8组码里反复出现,DXF并不会为每一条重复存图层属性,只在Tables段定义一次,实体段引用名字。需要按图层筛选图元时,不要直接在实体段里拼图层名,正确做法是先读出Tables.Layer里的图层名列表,再做集合判断,避免图层名带空格或大小写不一致导致误筛。
Blocks段则保存块定义里的局部坐标,图上插入块时使用INSERT实体。INSERT在Entities段只记录块名、插入点、缩放系数和旋转角,真正的图元坐标要进Blocks段找到块名下的实体,再按插入点与缩放矩阵换算。这算DXF里少数躲不开的矩阵计算场景,工程量大的时候建议先在MATLAB里把变换矩阵写成通用函数,别在每个INSERT里重复乘。
3.4 用状态机思路快速手动解析一段dxf
就算DXFLib已经解析好,我也会在排查时临时写一个状态机来验证。下面这段代码可以放脚本里直接抽所有LINE实体:
fid = fopen('check.dxf','r'); lineData = {}; curType = ''; while ~feof(fid) code = str2double(strtrim(fgetl(fid))); val = strtrim(fgetl(fid)); if code == 0 curType = val; if strcmpi(val, 'LINE') lineData{end+1} = struct('start', [], 'end', []); end elseif strcmpi(curType, 'LINE') if code == 10, x0 = str2double(val); end if code == 20, y0 = str2double(val); end if code == 11, x1 = str2double(val); end if code == 21 y1 = str2double(val); lineData{end}.start = [x0 y0]; lineData{end}.end = [x1 y1]; end end end fclose(fid);这个写法故意不用解析库,展示的是组码状态切换的基本思路。fgetl每次读一行,str2double负责把坐标数字转换出来。遇到0组码时说明新实体开始,curType切换;后续组码都按当前curType来解释。手动实现时不要忽略第一个0之前的内容,DXF文件开头几行往往是SECTION标记,一旦漏读,状态机就会把段名当成实体名。
这段代码适合R12到AutoCAD 2013之间最常见的DXF,图元的组码顺序在不同版本里并不完全相同,所以它只能作为默认检查方案。遇到实际文件顺序乱套,先用文本编辑器查看组码段,确认格式后再调整条件。
4. 用DXFLib提取CAD坐标,重建二维轮廓并转成三维边界
4.1 从图元结构里批量取多段线顶点
工程上最常做的动作不是读单条线,而是把一块封闭轮廓导出成坐标序列,交给后续计算。如果图里画的是带圆弧的多段线,直接从DXFLib的Entities字段取顶点最省事。假设返回结构里有LWPOLYLINE数组,典型循环如下:
plies = d.Entities.LWPOLYLINE; allPts = {}; for k = 1:length(plies) pl = plies(k); v = [pl.X(:), pl.Y(:)]; % 把顶点X数组和Y数组拼成N×2 allPts{k} = v; end这里pl.X、pl.Y是对顶点X/Y坐标列的访问,如果字段名不同,先disp(pl)看一眼再改。拼成N×2矩阵是为了后续给plot或polyshape直接使用,避免在循环里一个点一个点地append,那样图元一多性能明显变差。
对于闭合轮廓,判断封闭条件用图元的70组码,在结构体里的字段叫Closed或类似名字。不能只看首尾顶点坐标是否相等,因为DXF里开放多段线首尾坐标也可能相等,而闭合标志位仍然为0。
4.2 含圆弧的多段线重建:凸度转采样点
如果图里带弧线,直接把LWPOLYLINE的相邻顶点连直线画出来,轮廓会有内切误差。正确做法是把每个顶点的凸度值读出来,按包含角均匀采样。换算关系写在注释里,bulge为n时包含角度等于4乘以atan(n):
bulge = pl.VertexBulge(k); % 第k段凸度 theta = atan(bulge) * 4; % 单位:弧度 if abs(theta) < 1e-6 arcPts = [pts(k,:); pts(k+1,:)]; % 凸度接近0,直接当直线 else r = norm(pts(k+1,:) - pts(k,:)) / (2 * sin(theta/2)); center = pts(k,:) + ...; % 由两点和凸度求圆心,注意方向 t = linspace(0, theta, 20)'; arcPts = center + r * [cos(t), sin(t)]; endtheta取4乘以atan(bulge),是圆弧对应圆心角的弧度值。b为0时对应角正好是0,直接转直线;b为1时是半圆弧,theta为π,半径等于两点距离的一半。圆心求解要判断圆弧方向,DXF对旋转方向敏感。简化起见的替代方案是只取弧线的内插点,不严格求圆心,采样数20对一般工程图已经够。
4.3 把平面轮廓拉伸成三维边界,顺便处理单位差异
获取轮廓顶点后的直接用途是转成三维模型或仿真边界。平面图纸不提供高度信息,常规做法是给定一个拉伸厚度,把轮廓复制两层后连侧面。用polyshape加载顶点,再堆叠成三维顶点表:
pgon = polyshape(pts(:,1), pts(:,2)); plot(pgon); axis equal; thick = 30; n = size(pgon.Vertices, 1); verts = [pgon.Vertices, zeros(n,1); ... pgon.Vertices, thick * ones(n,1)]; % 建立侧面四边形面片索引 faces = [(1:n-1)', (2:n)', (n+3:2*n)', (n+2:2*n-1)']; faces = [faces; (1:n-1)', (2:n)', (n+2:2*n-1)', (n+1:2*n-2)']; patch('Faces', faces, 'Vertices', verts, 'FaceColor', 'w');verts第一列第二列是X、Y,第三列是Z,拼接代表底层Z=0,顶层Z=30。patch里Faces每行是一个四边形顶点索引,注意首尾闭合时还要补最后一个侧面,否则轮廓闭合处会出现破面。真正做有限元前处理时,再按这套verts和faces导出为STL或节点单元文件。
单位差异在Allegro或Cadence导出的DXF里尤其常见。从PCB工具导出时默认单位可能是毫米,而图纸内部尺寸用mil,这时通过DXF HEADER段的$INSUNITS组码来判断:
u = d.Header.INSUNITS; % 字段名以实际结构为准 switch u case 1, scale = 25.4; % inch,转mm case 4, scale = 1; % mm case 6, scale = 1000; % m,转mm otherwise, scale = 1; end pts = pts * scale;单位统一成毫米后再进入后续建模。Header段的INSUNITS只是声明值,不保证坐标一定遵守,判断单位最可靠的方式还是拿一条已知长度的实体对比。
5. dxf text实体组码与编码处理,以及DXFLib-v0.9.1的三个兜底
5.1 TEXT实体组码1的读取与中文乱码
DXF的文字图元不像坐标那样是纯数字,TEXT实体的正文放在组码1后面,且与坐标组码10/20交错。组码7后面是样式名,组码40是字高。若DXFLib返回的结构里text字段出现乱码,不代表文件有问题,多半是编码与MATLAB默认编码不一致。
ASCII版DXF头部HEADER段里有$DWGCODEPAGE,常见ANSI_936对应GBK。遇到中文合法但乱码的情况,可用指定编码重开文件:
fid = fopen('text.dxf', 'r', 'n', 'GB2312'); raw = fread(fid, '*char')'; fclose(fid); t = regexp(raw, 'TEXT.*?1\r\n(?<content>[^\r\n]+)', 'names');正则只抓TEXT实体后第一个1组码的内容。fopen第三个参数'n'表示不自动转换换行,第四个参数指定字符编码。MATLAB在R2019a之后对中文编码支持更好,旧版本按本地代码页读,最好提前把DXF另存为带BOM的UTF-8,再用UTF-8方式打开。
5.2 碰到“未知组码”中断时的三个兜底
DXFLib-v0.9.1年代较早,对高版本DXF里新增的组码不一定认识。读取时若出现解析中断,最省事的处理顺序如下。
第一,到AutoCAD里另存一次,选DXF格式为AutoCAD 2010或更低的ASCII DXF。高版本文件里多出来的OBJECTS段和扩展数据,旧库解析时容易撞上未知组码;降版本后组码结构更接近R12时代的写法,兼容性反而好。
第二,检查文件是否包含SPLINE、ELLIPSE、HATCH这类较新的实体类型。这类图元本身没有错,但旧库可能没有映射对象,表现是Entities结果为空却不报错。此时不要埋怨库,比较实际的处理是回到文本层,用第3章的状态机思路自己解析。
第三,在读取代码外做一次防错校验,读完后立即判断实体数量:
if isfield(d, 'Entities') && isempty(fieldnames(d.Entities)) warning('DXF实体段为空,可能只解析出空结构,请检查DXF版本'); end最后再补一个我常用的验证技巧:在CAD里画一条100单位直线,导出后解析,计算MATLAB中的两点距离,如果比例一致,说明解析正确;只差一个常数倍数,就能反过来推断单位转换系数。把这个距离校验脚本固化在读取流程里,每次换图源前先跑一次,比写完代码后随机抓图纸试错要省很多时间。这个方法无论对DXFLib还是自写解析函数都适用,因为所有读取过程最终都会回归到“组码加上下文”两个变量上。
本文还有配套的精品资源,点击获取