使用Python把CAD高程图实体化为3D模型
因为设计课程的需要,我需要为场地建立一个直观的3D模型进行形体推敲。但是这次老师给的CAD图纸只有高程点,等高线是缺失的,所以我希望是利用这些高程点的信息进行自动化建模,快速生成场地地形模型。
转换CAD
首先,因为CAD比较凌乱,我们需要整理出一个只包含了高程点的CAD图,并且导出为DXF格式,方便后续的Python程序进行对接。
原CAD:
处理后的CAD:
处理完成后,导出DXF格式的文件,为Python程序化建模做准备。
转化程序
因为blender的Python环境不方便安装别的库,所以处理程序分为两个部分:
- 第一部分使用系统安装的Python环境进行处理,使用
json格式保存转好后的中间结果,也就是每个点的坐标. - 第二部分使用Blender的Python环境,读取已经处理好的地形信息json文件,进行建模。
首先是第一部分的处理dxf文件:
import ezdxf
import json
import re
import os
# --- 配置文件路径 ---
DXF_FILE_PATH = "Drawing2.dxf"
JSON_OUTPUT_PATH = "topography.json"
def dxf_to_json(dxf_path, json_path):
print(f"正在读取 DXF 文件: {dxf_path} ...")
if not os.path.exists(dxf_path):
print(f"错误:找不到文件 '{dxf_path}'。")
return
try:
doc = ezdxf.readfile(dxf_path)
except Exception as e:
print(f"读取 DXF 失败: {e}")
return
msp = doc.modelspace()
points = [] # 用于存储坐标的列表
for entity in msp.query('TEXT MTEXT'):
try:
text_str = entity.dxf.text
# 提取数字(支持负数和小数)
match = re.search(r'[-+]?\d*\.?\d+', text_str)
if match:
z = float(match.group())
x = entity.dxf.insert.x
y = entity.dxf.insert.y
# 将坐标作为一个小列表存入总列表
points.append([x, y, z])
except Exception:
continue
if points:
# 将数据转储为 JSON 格式
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(points, f, indent=2) # indent=2 让 JSON 文件格式化,方便人类阅读
print(f"✅ 成功!共提取了 {len(points)} 个坐标点。")
print(f"数据已保存为 JSON 文件: {json_path}")
else:
print("❌ 提取失败:未找到有效数据。")
# 运行脚本
dxf_to_json(DXF_FILE_PATH, JSON_OUTPUT_PATH)以上代码在系统的Python环境运行,记得安装对应的Python库ezdxf。
然后是在Blender里面运行的Python代码:
import bpy
import json
import mathutils
# --- 配置参数 ---
JSON_FILE_PATH = "/run/media/chocola/3T-DATA02/设计/场地模型/topography.json"
def create_terrain_from_json(filepath):
# 1. 读取 JSON 数据
try:
with open(filepath, 'r', encoding='utf-8') as f:
points = json.load(f)
except Exception as e:
print(f"❌ 读取 JSON 失败: {e}")
return
if not points:
print("❌ JSON 文件中没有数据!")
return
print(f"成功读取 {len(points)} 个坐标点,开始处理...")
# 2. 计算边界框中心,处理坐标过大导致的浮点精度问题
min_x = min(p[0] for p in points)
max_x = max(p[0] for p in points)
min_y = min(p[1] for p in points)
max_y = max(p[1] for p in points)
center_x = (min_x + max_x) / 2.0
center_y = (min_y + max_y) / 2.0
verts_3d = []
verts_2d = [] # Delaunay 算法只需要 2D 平面坐标
for p in points:
# 减去中心点坐标,将整个地形移至原点 (0,0,0) 附近
cx = p[0] - center_x
cy = p[1] - center_y
cz = p[2]
verts_3d.append((cx, cy, cz))
verts_2d.append(mathutils.Vector((cx, cy)))
# 3. 执行 2D 德劳内三角剖分
print("正在计算地形拓扑结构...")
# delaunay_2d_cdt 返回值包含: (vertices, edges, faces, is_valid)
# 我们只需要提取面 (faces) 的索引数据
try:
result = mathutils.geometry.delaunay_2d_cdt(verts_2d, [], [], 0, 0.0001)
faces = result[2]
except Exception as e:
print(f"❌ 三角剖分计算失败: {e}")
return
# 4. 创建 Blender 网格并赋予数据
mesh = bpy.data.meshes.new("Terrain_Mesh")
# from_pydata 极其高效:直接通过顶点列表和面列表生成网格
mesh.from_pydata(verts_3d, [], faces)
# 开启平滑着色
for poly in mesh.polygons:
poly.use_smooth = True
mesh.update()
# 5. 生成物体并放入场景
obj = bpy.data.objects.new("Site_Terrain", mesh)
bpy.context.collection.objects.link(obj)
# 选中生成的物体
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
print(f"✅ 地形生成完毕!")
print(f"提示: 原 CAD 坐标中心点已偏移:X={center_x:.2f}, Y={center_y:.2f}")
# 运行脚本
create_terrain_from_json(JSON_FILE_PATH)最终效果
按顺序执行了两个程序后,你的Blender里面应该就已经出现了对应的地形文件了。当然,是单面的模型,后续可以进行实体化操作等按需调整。
还有一个需要提醒的是,生成的模型是三角面的模型,并不是Blender建模推荐的四边形建模,并且布线并不规整。所以这个模型是能用,但是不方便修改。









































































































































































































