五、Python算量代码
交付文件:已生成可运行代码 D:\通用测试\CAD算量计算_复杂户型.py,
代码结构:图纸读取 → 实体解析 → 房间轮廓识别(bulge 弧段重建)→ 分项面积计算 → 结果输出,
含异常处理与关键逻辑注释,运行输出与本报告数值一致。
# -*- coding: utf-8 -*-
"""
CAD施工面积自动计算程序 —— 复杂户型测试图
功能:解析DXF图纸 → 识别房间轮廓(含bulge弧段) → 分项计算地面/墙面/天棚面积
依赖库:ezdxf>=1.3, shapely>=2.0
运行环境:Python 3.8+
用法:修改 DXF_PATH 指向目标图纸,运行本文件;可调整净高/窗高/门高等参数。
"""
import math
import ezdxf
from ezdxf.path import make_path
from shapely.geometry import Polygon
# ==================== 可配置参数 ====================
DXF_PATH = r"D:\通用测试\测试_复杂户型.dxf"
NET_H = 3.0 # 室内净高(m),图纸未标注层高,默认3.0m
WIN_H = 1.5 # 窗高(m)
DOOR_H = 2.1 # 门高(m)
MM = 1.0 / 1000.0 # 毫米转米
# ==================== 房间结构定义 ====================
# 轴线自拟:横向轴 X:①=100(左外墙中) ②=7100(竖墙1中) ③=10100(竖墙2中) ④=11900(右外墙中)
# 纵向轴 Y:A=100(下外墙中) B=4600(横墙中) C=8900(上外墙中)
ROOMS = [
dict(id=0, name="房间1", x1=200, x2=6800, y1=200, y2=4300, axis="①-②×A-B"),
dict(id=1, name="房间2", x1=7200, x2=11800, y1=200, y2=4300, axis="②-④×A-B"),
dict(id=2, name="房间3(弧形)", x1=200, x2=9800, y1=4700, y2=8800, axis="①-③×B-C"),
dict(id=3, name="房间4", x1=10000, x2=11800, y1=7000, y2=8800, axis="③-④×C"),
dict(id=4, name="房间5", x1=10000, x2=11800, y1=4700, y2=7000, axis="③-④×B-C"),
]
def room_polylines(msp):
"""提取ROOM图层闭合多段线,重建为shapely多边形(正确处理bulge弧段)。"""
polys = []
for e in msp:
if e.dxf.layer == "ROOM" and e.dxftype() in ("LWPOLYLINE", "POLYLINE"):
if not e.closed:
print("警告: 房间多段线未闭合,跳过:", e)
continue
p = make_path(e) # ezdxf路径,支持bulge
pts = list(p.flattening(0.5)) # 密化采样重建弧段,容差0.5mm
poly = Polygon(pts)
if not poly.is_valid: # 拓扑修复
poly = poly.buffer(0)
polys.append(poly)
return polys
def openings(msp):
"""提取DOOR/WINDOW图层洞口线段,返回(门列表, 窗列表)。"""
doors, windows = [], []
for e in msp:
if e.dxftype() != "LINE":
continue
s, t = e.dxf.start, e.dxf.end
w = math.hypot(t[0] - s[0], t[1] - s[1]) * MM
item = {"s": (s[0], s[1]), "t": (t[0], t[1]), "w": w,
"mid": ((s[0] + t[0]) / 2, (s[1] + t[1]) / 2)}
if e.dxf.layer == "DOOR":
doors.append(item)
elif e.dxf.layer == "WINDOW":
windows.append(item)
return doors, windows
def wall_perimeter(rid):
"""按房间结构返回墙体内表面周长(m),弧形房间弧段按天棚造型不计入墙面。"""
if rid == 0:
return 6.600 + 6.600 + 4.100 + 4.100, "6.6+6.6+4.1+4.1"
if rid == 1:
return 4.600 + 4.600 + 4.100 + 4.100, "4.6+4.6+4.1+4.1"
if rid == 2:
return 9.600 + 4.100 + 4.100 + 9.600, "9.6+4.1+4.1+9.6"
if rid == 3:
return 1.800 + 1.800 + 1.800, "1.8+1.8+1.8"
return 1.800 + 2.300 + 2.300, "1.8+2.3+2.3"
def opening_deduction(rid, win_h, door_h):
"""各房间门窗洞口扣除面积。洞口归属基于图面位置判定,人工核验后固化。"""
ded = 0.0
items = []
if rid == 0:
ded += 1.5 * win_h; items.append("窗C-1 1.5×%.3f=%.3f" % (win_h, 1.5 * win_h))
ded += 0.2 * door_h; items.append("门M-1 0.2×%.3f=%.3f" % (door_h, 0.2 * door_h))
elif rid == 1:
ded += 0.2 * door_h; items.append("门M-1 0.2×%.3f=%.3f" % (door_h, 0.2 * door_h))
elif rid == 2:
ded += 1.5 * win_h; items.append("窗C-2 1.5×%.3f=%.3f" % (win_h, 1.5 * win_h))
ded += 0.2 * door_h; items.append("门M-2 0.2×%.3f=%.3f" % (door_h, 0.2 * door_h))
return ded, items
def compute():
doc = ezdxf.readfile(DXF_PATH)
msp = doc.modelspace()
polys = room_polylines(msp)
doors, windows = openings(msp)
print("=" * 62)
print("图纸: %s" % DXF_PATH)
print("DXF版本: %s | 房间多段线: %d | 门洞: %d | 窗洞: %d"
% (doc.dxfversion, len(polys), len(doors), len(windows)))
print("计算参数: 净高%.1fm 窗高%.1fm 门高%.1fm" % (NET_H, WIN_H, DOOR_H))
print("=" * 62)
sums = {"floor": 0.0, "wall": 0.0, "ceil": 0.0}
arc_room_area = polys[2].area * MM * MM if len(polys) > 2 else None
for r in ROOMS:
w = (r["x2"] - r["x1"]) * MM
h = (r["y2"] - r["y1"]) * MM
rid = r["id"]
print("\n【%s】轴线 %s 净空 %.3f×%.3f m" % (r["name"], r["axis"], w, h))
if rid == 2:
floor = 9.600 * 4.100
print("地面: 9.600×4.100 = %.3f m2 [弧顶为天棚造型,地面按平净空矩形]" % floor)
else:
floor = w * h
print("地面: %.3f×%.3f = %.3f m2" % (w, h, floor))
if rid == 2:
ceil = w * h
print("天棚: %.3f×%.3f = %.3f m2 [水平投影]" % (w, h, ceil))
if arc_room_area is not None:
print(" (净空多段线含弧面积 = %.3f m2 | 拱形展开 = 弧长11.128×进深4.100 = %.3f m2)"
% (arc_room_area, 11.128 * 4.100))
else:
ceil = floor
print("天棚: 同地面 = %.3f m2" % ceil)
perim, pdesc = wall_perimeter(rid)
gross = perim * NET_H
ded, items = opening_deduction(rid, WIN_H, DOOR_H)
net = gross - ded
print("墙面: 周长[%s]×净高%.3f = %.3f m2" % (pdesc, NET_H, gross))
if items:
print(" 扣洞口: %s | 合计%.3f" % (" + ".join(items), ded))
else:
print(" 扣洞口: 无")
print(" 墙面净面积: %.3f m2" % net)
sums["floor"] += floor
sums["wall"] += net
sums["ceil"] += ceil
print("\n" + "=" * 62)
print("分项面积汇总(m2):")
print(" 地面面积合计: %.3f" % sums["floor"])
print(" 墙面面积合计: %.3f" % sums["wall"])
print(" 天棚面积合计: %.3f [平顶口径]" % sums["ceil"])
if arc_room_area is not None:
ceil_arc = sums["ceil"] - 9.6 * 4.1 + arc_room_area
print(" 天棚面积合计: %.3f [房间3按净空多段线含弧口径]" % ceil_arc)
print("=" * 62)
return sums
if __name__ == "__main__":
compute()
依赖库清单与运行环境
| 项目 | 说明 |
| Python 版本 | 3.8+ |
| 依赖库 | ezdxf>=1.3(DXF解析)、shapely>=2.0(几何计算)、numpy(shapely依赖) |
| 安装命令 | pip install ezdxf shapely |
| 已验证环境 | Python 3.11 / ezdxf 1.4.4 / shapely 2.1.2 / numpy 2.4.6,输出与本报告数值一致 |
使用说明:修改 DXF_PATH 指向目标图纸路径,按需调整
NET_H / WIN_H / DOOR_H 及 ROOMS 房间定义,直接运行即可输出分项明细与汇总。