DXF 图纸读写与轨迹数据链1. 总体思路本文围绕 DXF 图纸解析面向激光、点胶这类工业加工场景基于 DXFLib 库搭建一套完整的 DXF 读取‑轨迹优化‑配方导出的数据处理链路。整条数据链分成三层层模块职责解析层DXFLib第三方LGPLDXF 文件 ↔ 组码流的底层解析/生成语义层DxfReaderPathItem/StDrawData回调转图元、坐标变换、bulge 弧还原、轨迹优化应用层RecipeView/RecipeDesign/CustomTreeWidget/GroupParam/Viewer图纸导入、配方编辑、JSON 持久化、显示交互方向通道状态读DXF → 轨迹组DL_Dxf::in→DxfReader回调✅写轨迹组 → JSON 配方Recipes/名称.json✅写轨迹组 → DXFDL_Dxf::outwrite*系列⚠️ 库能力已具备暂时用不上2. DXF 读取链路重点2.1 如何解析 DXFDXFLibDXF 文件其本质是“(组码, 值)” 成对的 ASCII 流如10 / x1 / 20 / y1。读取入口DL_Dxf dxf;boolokdxf.in(filePath.toStdString(),this);// this DxfReader(DL_CreationAdapter 子类)库内部流程in()逐行读取组码与值 →processDXFGroup()按当前实体类型缓存数值实体结束时分派到对应回调addLine / addCircle / addArc / addPolyline / addVertex / endEntity / addLayer ...。解析中关注两个细节LWPOLYLINE轻量多段线顶点预先存入库内数组触发 addPolyline () 时通过 addVertex () 回调批量输出全部顶点末尾调用 endEntity ()。因此 DxfReader 对 POLYLINE、LWPOLYLINE 做了归一化处理。属性携带每个回调内通过getAttributes()取当前实体的图层名、颜色ACI 索引、线宽。2.2 DxfReader 回调实现DxfReader : public DL_CreationAdapter重载 以下回调主要覆盖 LINE / CIRCLE / ARC / POLYLINE(LW)这四类常用的图元回调我的处理addLayer图层名 →_mapLayerItems建立图层→图元映射供图层树显隐addLine两点直线记录起/终点 长度addCircle整圆我离散为 0°/90°/180°/270° 四个特征点 周长addArc圆弧角度规范化(0~360)、跨度spanend−start负值 360取起/中/末三点 弧长addPolyline结束上一条未收尾多段线缓存DL_PolylineData含闭合标志flags 0x1addVertex逐顶点追加y取负顶点 bulge ≠ 0 时我调用createBulgeArc生成弧段图元endEntity实体结束 →finalizePolyline()按顶点串 QPainterPath闭合则补回首点累计长度全部图元封装为PathItemQGraphicsPathItem 子类并绑定StDrawData业务数据通过addPathItem()挂载至图层表与全局_listPaths。2.3 坐标变换与几何还原关键约定1.CAD 世界坐标系 Y 轴朝上但 Qt 绘图场景的 Y 轴是向下的。为了让解析数据和界面显示坐标系对齐我在全部回调接口addLine、addCircle、addArc、addVertex统一做处理直接取y −y。坐标翻转之后圆弧方向也会跟着镜像这块我在 addArc 函数内部做了兼容通过span 0 ? 360完成角度修正。2.bulge 圆弧还原对应函数 createBulgeArc。DXF 依靠组码 42 的 bulge 参数来描述多段线顶点之间的圆弧它的定义是bulge tan(θ/4)。 实际计算时 弓高s bulge · L / 2其中 L 代表弦长 半径r (L²/4 s²) / (2|s|)圆心落在弦的中垂线上bulge 的正负号决定圆心往哪一侧偏移。我借助 atan2 算出起点、终点角度再根据符号做展开最后调用 QPainterPath::arcTo 完成圆弧绘制。3.颜色转换封装在 toQColor 函数里ACI 索引 1‑8分别映射红、黄、绿、青、蓝、洋红、白、灰索引大于 256 的情况下则按 0x00RRGGBB 的 24 位格式解析遇到非法输入直接兜底显示红色。4.单位直接沿用 DXF 原始数值统一约定单位为毫米不额外做缩放变换。Z 轴坐标和加工速度默认初始化为 0这两项参数留给后续配方编辑环节再填充。2.4 读取流水线ReadFile()设计态直接进场景 点表与ReadFileData()配方态只返回分组数据共用同一套解析与优化差别只在输出通道。3. 轨迹优化让连续轨迹一次性走完优化目标把 DXF 中零散的线段/弧串成尽量少的连续路径减少空行程与抬刀次数。步骤函数我的规则① 闭合分拣optimizePathAllisClosedPathCircle 恒闭合Polygon/Line 首尾点重合ε0.01即闭合 → 我让每个闭合图元独立成组从待串接集合移除② 未闭合串接optimizePathsUnClosedLay1/Lay2取剩余首元素作链起点循环在剩余线段中找链尾可接的段首点或尾点匹配 ε0.01尾点相接时先reverseItem反向再接入无匹配则当前链成组换新链起点直到清空③ 组间排序sortGroup/sortGroupLay1贪心最近邻以上一组末图元终点为基准选首点距离最近的下一组接上递归完成全部分组排序结果是自定义的readGroup : QVectorQVectorPathItem*组 → 图元 → 特征点并统一回填GroupIdx / ItemIdx。实测效果下图中的外框、对角线、内菱形与小圆被我串接为一个 18 点的连续 Polygon树中 Group1 → Item1实现整图连续轨迹一次性走完配方的树状结构与场景高亮联动选中 Item → 场景对应图元变绿4. 写出链路4.1 JSON 配方序列化结构CustomTreeWidget::PathItemGroupToJson——层级为Group → PathItems → Points[{bEnable:true,iRecipeNum:0,PathItems:[{Type:Polygon,Index:1,Length:165.530,Speed:0.00,Points:[{X(mm):267.7678,Y(mm):-66.7386,Z(mm):0.0000}]}]}]写出路径触发代码路径落盘从图纸导入RecipeView::loadDxfFile→ 优化分组 →PathItemGroupToJsonRecipes/dxf名.json手动保存onSaveJson→getDataFromQTreeWidgetItem树→数据→ToJson当前选中配方同名覆盖Truncate组参数GroupParam::saveLocalDataparam/recipesparam.jsonNum / OpenDelayTime / CloseDelayTime反序列化JsonToPathItemGroup按Type字符串重建图元路径Line/Polygon 直线连接Arc/Circle 用三点定圆——两段中垂线求交得圆心、到点距离得半径再用象限修正的角度重建arcTo路径保证导入-导出往返一致。弧→折线树中编辑 Arc 的点坐标后用RecipeView::transArcToLine(data, threshold0.05)按弦高比阈值把圆弧离散为等误差折线步进Δθ 2·asin(threshold / 2r)转成 Polygon 下发避免执行端处理圆弧插补。4. 核心数据结构enumEnType{enLine,enArc,enCircle,enPolygon};// 图元类型structStDrawData{// 图元业务数据读写通用载体EnType enType;// 类型QPolygonF points;// 特征点集X/Y已做 Y 翻转QListfloatfZPos;// 逐点 ZQString strType;// Line/Arc/Circle/PolygonfloatfLength;// 图元长度弧长/周长/折线累计floatfSpeed;// 加工速度编辑填入};structGroupInfo{// 分组树/JSON 的一组boolbEnable;// 组启用开关勾选框禁用时图元标红intiRecipeNum;// 关联组参数编号开/关胶延迟等QVectorPathItem*items;// 组内图元};三级数据视图保持同步场景图元PathItem↔ 树控件Group-Item-Point↔ JSON 文件三者的互转函数转换函数分组数据 → 树CustomTreeWidget::loadData树 → 分组数据getDataFromQTreeWidgetItem分组数据 → JSONPathItemGroupToJsonJSON → 分组数据重建路径JsonToPathItemGroup