前几个月在图像编辑器中完成了导入dxf的功能,趁现在有点时间来总结一下这一功能的实现思路和难点。
dxf文件的内容如下:
后缀名为dxf
dxf的文件内容是文本,很类似json,yaml 结构,里面定义了通过一个一个的属性和属性值。
有图层,圆,线,多线段,嵌套对象,弧度,字体。 更多的对象可以查看官方文档
这个pdf可以查看到 https://images.autodesk.com/adsk/files/autocad_2012_pdf_dxf-reference_enu.pdf
中文版 在线 https://help.autodesk.com/view/ACD/2024/CHS/?guid=GUID-FCEF5726-53AE-4C43-B4EA-C84EB8686A66
解析dxf 使用是这个前端库 https://github.com/gdsestimating/dxf-parser
另外还有个使用threejs来预览dxf文件的库。https://github.com/gdsestimating/three-dxf 使用它可以帮助我们画出的dxf图形是否准确。
输出内容示例
{
"header": {
"$ACADVER": "AC1027",
"$ACADMAINTVER": 55,
"$DWGCODEPAGE": "ANSI_1252",
"$REQUIREDVERSIONS": 0,
"$INSBASE": {
"x": 0,
"y": 0,
"z": 0
},
"$EXTMIN": {
"x": 146.8403717847312,
"y": 141.8294137695975,
"z": -5.934454319554902
},
"$EXTMAX": {
"x": 762.6679804989191,
"y": 545.821967323924,
"z": 6.065545680445099
}
},
"tables": {
"lineType": {
"handle": "5",
"ownerHandle": "0",
"lineTypes": {
"Continuous": {
"name": "Continuous",
"description": "Solid line",
"patternLength": 0
},
"HIDDEN2": {
"name": "HIDDEN2",
"description": "Hidden (.5x) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _",
"pattern": [
0.125,
-0.0625
],
"patternLength": 0.1875
},
"CENTER": {
"name": "CENTER",
"description": "____ _ ____ _ ____ _ ____ _ ____ _ ____ _ ____",
"pattern": [
1.25,
-0.25,
0.25,
-0.25
],
"patternLength": 2
}
}
},
"layer": {
"handle": "2",
"ownerHandle": "0",
"layers": {
"FG-Dim": {
"name": "FG-Dim",
"visible": true,
"color": 16711680
},
"FG-Dtl-Hatch": {
"name": "FG-Dtl-Hatch",
"visible": true,
"color": 8421504
},
"FG-Dtl-Hidden": {
"name": "FG-Dtl-Hidden",
"visible": false,
"color": 16711680
},
"Vport": {
"name": "Vport",
"visible": false,
"color": 65535
},
"FG-Logo": {
"name": "FG-Logo",
"visible": true,
"color": 16711680
}
}
}
},
"blocks": {
"*U34": {
"handle": "2269",
"ownerHandle": "2268",
"layer": "0",
"name": "*U34",
"type": 3,
"position": {
"x": 0,
"y": 0,
"z": 0
},
"name2": "*U34",
"xrefPath": "",
"entities": [
{
"type": "LINE",
"vertices": [
{
"x": 0.1875,
"y": 0,
"z": 0
},
{
"x": -0.1875,
"y": 0,
"z": 0
}
],
"handle": "226C",
"ownerHandle": "2268",
"layer": "0"
},
{
"type": "LINE",
"vertices": [
{
"x": 0,
"y": 0.1875,
"z": 0
},
{
"x": 0,
"y": 0.6811772682290212,
"z": 0
}
],
"handle": "226F",
"ownerHandle": "2268",
"layer": "0"
}
]
}
},
"entities": [
{
"type": "LINE",
"vertices": [
{
"x": 516.4647237971784,
"y": 330.7069626367825,
"z": 0
},
{
"x": 516.1834738922776,
"y": 330.8693422367825,
"z": 0
}
],
"handle": "1805",
"ownerHandle": "1F",
"layer": "FG-Dtl-Fastener"
},
{
"type": "LWPOLYLINE",
"vertices": [
{
"x": 520.4823233451135,
"y": 335.7037165715653,
"bulge": 0.495500738389451
},
{
"x": 520.4823233451135,
"y": 335.385455702,
"bulge": -0.7180232138070655
},
{
"x": 520.4641507323038,
"y": 335.3315861367825
},
{
"x": 520.09745368689,
"y": 335.3315861367825,
"bulge": 0.2818610536572053
},
{
"x": 519.9994740971791,
"y": 335.3915861367824
},
{
"x": 519.9334740971784,
"y": 335.3915861367824
},
{
"x": 519.9334740971784,
"y": 335.6975861367825
},
{
"x": 519.9994740971791,
"y": 335.6975861367825,
"bulge": 0.2818610536572062
},
{
"x": 520.09745368689,
"y": 335.7575861367824
},
{
"x": 520.4641507323034,
"y": 335.7575861367824,
"bulge": -0.7180232138063462
}
],
"handle": "13E1",
"ownerHandle": "1F",
"layer": "SHAPE",
"shape": true
},
{
"type": "CIRCLE",
"handle": "17EA",
"ownerHandle": "1F",
"layer": "FG-Dtl-Fastener",
"center": {
"x": 516.1834740971784,
"y": 329.0445861367825,
"z": 0
},
"radius": 0.1875
},
{
"type": "ARC",
"handle": "17F6",
"ownerHandle": "1F",
"layer": "FG-Dtl-Fastener",
"center": {
"x": 516.1834740971784,
"y": 330.4508361367829,
"z": 0
},
"radius": 0.28125,
"startAngle": 0,
"angleLength": 3.141592653589793,
"endAngle": 3.141592653589793
},
]
}
我们主要是用的是 entities 这个数组内的数据, 每一个元素都应该被当作一个独立的元素,导入到画布中也是单独的元素,可以独立操作。 dxf中也有图层的概念。
最终导入的dxf
带有图层的 dxf解析使用不同的颜色,来表示不同的图层。有些图层是要雕刻,有些图层是要切割。加工更加方便。
在解析dxf时,由于dxf的坐标原点是在左下角,而一般的画布 canvas的原点是在左上角,这里就需要做一个反转。要做反转就需要计算dxf整体的边框尺寸,使用边框尺寸进行反转。这部分在解析字体时非常麻烦,我看有些激光雕刻机软件直接就没有解析字体。另外还有弧度,嵌套元素,都是比较麻烦的。 在渲染dxf的内容时 需要先计算出整体的边框,然后在计算每个元素坐标时,使用边框和元素的原始数据进行反转。最终实现坐标系的转换。这是比较普通的一种做法,还有一种做法是,先按照dxf的原始数据渲染元素,然后获取dxf所有元素的边框, 最后将dxf的所有元素 反转一遍。利用图形化canvas库的能力进行反转,而不是逐个地将原始坐标反转。
看一下调试的数据
针对每个dxf的实体都需要写一个独立的解析方法。
switch (entity.type) {
case "LINE":
addItem = drawLine(entity, offsetX, offsetY);
break;
case "CIRCLE":
addItem = drawCircle(entity, offsetX, offsetY);
break;
case "ARC":
addItem = drawArc(entity, offsetX, offsetY);
break;
case "POLYLINE":
case "LWPOLYLINE":
addItem = drawPolyline(entity, offsetX, offsetY);
break;
case "SPLINE":
addItem = drawSpline(entity, offsetX, offsetY);
break;
case "INSERT":
drawInsert(entity, block, offsetX, offsetY, selectLayerConfig);
break;
case "MTEXT":
addItem = (await drawMText(entity, offsetX, offsetY)) as paper.Item; //1234 假设提供了一些偏移值
setLaserData(addItem, selectLayerConfig.color);
break;
}
做这个dxf解析 我一个人也做了两个星期。不少细节都是边查资料边做。
最后再补充一下,市面上还有一个库 叫dxf, https://www.npmjs.com/package/dxf 可以直接将dxf的内容转换成svg,然后画布再导入svg,整个流程非常简单,就导入dxf的问题,转换成了导入svg的问题。 但是这个有一个缺点,就是无法直接获取图层信息。 我不清楚dxf转svg时,能不能携带图层的信息,但svg本身是没有图层这个概念的,而dxf有。
这两天有人使用toocaastudio时,发现矩形的圆角 没办法显示出来。看来后面还需要再优化一下。😑心累