feat(dxf): enhance block diagnostics and layout listing
This commit introduces several new scripts to the DXF project to improve diagnostics and data extraction capabilities: - find_block_coordinates.py: A new utility to accurately find the coordinates of rectangular blocks, which is essential for automated positioning and plotting. - diagnose_block_geometry.py: A script to analyze and report the geometric properties of blocks, aiding in debugging and validation. - list_layouts.py: A simple tool to list all available layouts within a DXF file. Associated documentation has been added to .specstory to cover these new features.
This commit is contained in:
parent
bcefe9c927
commit
31e03fd2d9
2333
.specstory/history/2025-09-12_09-41Z-获取矩形图块的坐标.md
Normal file
2333
.specstory/history/2025-09-12_09-41Z-获取矩形图块的坐标.md
Normal file
File diff suppressed because it is too large
Load Diff
0
03_Python_Open-Source_DXF/find_block_coordinates.py
Normal file
0
03_Python_Open-Source_DXF/find_block_coordinates.py
Normal file
81
03_Python_OpenSource_DXF/diagnose_block_geometry.py
Normal file
81
03_Python_OpenSource_DXF/diagnose_block_geometry.py
Normal file
@ -0,0 +1,81 @@
|
||||
import ezdxf
|
||||
import sys
|
||||
import os
|
||||
|
||||
def diagnose_block_geometry(dxf_file_path: str, block_name: str):
|
||||
"""
|
||||
Loads a DXF file, finds a specific block definition, and prints a
|
||||
report of the geometric entities it contains.
|
||||
"""
|
||||
if not os.path.exists(dxf_file_path):
|
||||
print(f"错误: 文件不存在 '{dxf_file_path}'")
|
||||
return
|
||||
|
||||
try:
|
||||
doc = ezdxf.readfile(dxf_file_path)
|
||||
except IOError:
|
||||
print(f"错误: 无法打开文件 '{dxf_file_path}'")
|
||||
return
|
||||
except ezdxf.DXFStructureError as e:
|
||||
print(f"错误: 无效或损坏的 DXF 文件. {e}")
|
||||
return
|
||||
|
||||
# Get the block definition
|
||||
block_def = doc.blocks.get(block_name)
|
||||
if not block_def:
|
||||
print(f"错误: 在文件中找不到名为 '{block_name}' 的图块定义。")
|
||||
return
|
||||
|
||||
print(f"--- 图块 '{block_name}' 的几何结构诊断报告 ---")
|
||||
|
||||
entity_count = 0
|
||||
|
||||
if not block_def:
|
||||
print("图块定义为空。")
|
||||
return
|
||||
|
||||
for entity in block_def:
|
||||
entity_count += 1
|
||||
print(f"\n实体 #{entity_count}:")
|
||||
print(f" - 类型: {entity.dxftype()}")
|
||||
|
||||
if entity.dxftype() == 'LWPOLYLINE':
|
||||
print(f" - 是否闭合: {entity.closed}")
|
||||
with entity.points() as points:
|
||||
point_list = list(points)
|
||||
print(f" - 顶点数量: {len(point_list)}")
|
||||
for i, p in enumerate(point_list):
|
||||
print(f" - P{i+1}: ({p[0]:.3f}, {p[1]:.3f})")
|
||||
|
||||
elif entity.dxftype() == 'LINE':
|
||||
print(f" - 起点: ({entity.dxf.start.x:.3f}, {entity.dxf.start.y:.3f})")
|
||||
print(f" - 终点: ({entity.dxf.end.x:.3f}, {entity.dxf.end.y:.3f})")
|
||||
|
||||
elif entity.dxftype() == 'ARC':
|
||||
print(f" - 圆心: ({entity.dxf.center.x:.3f}, {entity.dxf.center.y:.3f})")
|
||||
print(f" - 半径: {entity.dxf.radius:.3f}")
|
||||
print(f" - 起始角: {entity.dxf.start_angle:.3f}")
|
||||
print(f" - 终止角: {entity.dxf.end_angle:.3f}")
|
||||
|
||||
else:
|
||||
print(f" - (未详细解析的实体)")
|
||||
|
||||
if entity_count == 0:
|
||||
print("图块定义中不包含任何几何实体。")
|
||||
|
||||
print(f"\n--- 报告结束: 共找到 {entity_count} 个实体 ---")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
DXF_FILE = r"C:\Users\83500\久翌\CAD编辑同步excel\测试文件区\04_Test_Files\塔器.dxf"
|
||||
BLOCK_TO_DIAGNOSE = "A$C1CC9093B" # The block that previously failed
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
dxf_path = sys.argv[1]
|
||||
block_name = sys.argv[2]
|
||||
diagnose_block_geometry(dxf_path, block_name)
|
||||
else:
|
||||
print(f"未提供参数。将使用默认文件和图块进行诊断:")
|
||||
print(f" - 文件: {DXF_FILE}")
|
||||
print(f" - 图块: {BLOCK_TO_DIAGNOSE}")
|
||||
diagnose_block_geometry(DXF_FILE, BLOCK_TO_DIAGNOSE)
|
||||
148
03_Python_OpenSource_DXF/find_block_coordinates.py
Normal file
148
03_Python_OpenSource_DXF/find_block_coordinates.py
Normal file
@ -0,0 +1,148 @@
|
||||
import ezdxf
|
||||
from ezdxf.math import Vec3
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import math
|
||||
|
||||
def get_bounding_box_from_block(block_def):
|
||||
"""
|
||||
Calculates the bounding box of all geometric entities within a block
|
||||
definition and returns its four corner vertices in local coordinates.
|
||||
This version manually handles LINE entities.
|
||||
"""
|
||||
min_x, min_y = math.inf, math.inf
|
||||
max_x, max_y = -math.inf, -math.inf
|
||||
has_geometry = False
|
||||
|
||||
for entity in block_def:
|
||||
if entity.dxftype() == 'LINE':
|
||||
start, end = entity.dxf.start, entity.dxf.end
|
||||
min_x = min(min_x, start.x, end.x)
|
||||
min_y = min(min_y, start.y, end.y)
|
||||
max_x = max(max_x, start.x, end.x)
|
||||
max_y = max(max_y, start.y, end.y)
|
||||
has_geometry = True
|
||||
elif entity.dxftype() == 'LWPOLYLINE':
|
||||
try:
|
||||
# LWPOLYLINE points are 2D tuples in OCS
|
||||
with entity.points() as points:
|
||||
point_list = list(points)
|
||||
if not point_list:
|
||||
continue
|
||||
|
||||
x_coords = [p[0] for p in point_list]
|
||||
y_coords = [p[1] for p in point_list]
|
||||
|
||||
min_x = min(min_x, min(x_coords))
|
||||
min_y = min(min_y, min(y_coords))
|
||||
max_x = max(max_x, max(x_coords))
|
||||
max_y = max(max_y, max(y_coords))
|
||||
has_geometry = True
|
||||
except (AttributeError, TypeError):
|
||||
continue
|
||||
|
||||
if not has_geometry:
|
||||
return None
|
||||
|
||||
# The four corners of the bounding box
|
||||
return [
|
||||
Vec3(min_x, min_y, 0),
|
||||
Vec3(max_x, min_y, 0),
|
||||
Vec3(max_x, max_y, 0),
|
||||
Vec3(min_x, max_y, 0),
|
||||
]
|
||||
|
||||
def main(dxf_file_path: str):
|
||||
"""
|
||||
Finds specified block references in a layout, extracts their corner
|
||||
coordinates based on bounding box, and prints the results as JSON.
|
||||
"""
|
||||
# --- Configuration ---
|
||||
TARGET_LAYOUT = "布局1"
|
||||
TARGET_BLOCK_NAMES = [
|
||||
"A$C2EB80DB8",
|
||||
"A$C1CC9093B",
|
||||
"A$C6D564680",
|
||||
"新块",
|
||||
"新块1",
|
||||
]
|
||||
|
||||
if not os.path.exists(dxf_file_path):
|
||||
print(f"错误: 文件不存在 '{dxf_file_path}'")
|
||||
return
|
||||
|
||||
try:
|
||||
doc = ezdxf.readfile(dxf_file_path)
|
||||
except IOError:
|
||||
print(f"错误: 无法打开文件 '{dxf_file_path}'")
|
||||
return
|
||||
except ezdxf.DXFStructureError as e:
|
||||
print(f"错误: 无效或损坏的 DXF 文件. {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
layout = doc.layouts.get(TARGET_LAYOUT)
|
||||
except KeyError:
|
||||
print(f"错误: 找不到布局 '{TARGET_LAYOUT}'。")
|
||||
return
|
||||
|
||||
found_blocks = {}
|
||||
|
||||
query_string = 'INSERT'
|
||||
for block_ref in layout.query(query_string):
|
||||
block_name = block_ref.dxf.name
|
||||
|
||||
if block_name not in TARGET_BLOCK_NAMES:
|
||||
continue
|
||||
|
||||
block_def = doc.blocks.get(block_name)
|
||||
if not block_def:
|
||||
print(f"警告: 找不到图块 '{block_name}' 的定义。")
|
||||
continue
|
||||
|
||||
local_vertices = get_bounding_box_from_block(block_def)
|
||||
if not local_vertices:
|
||||
print(f"警告: 在图块 '{block_name}' 中找不到可计算边界的几何体 (LINE, LWPOLYLINE)。")
|
||||
continue
|
||||
|
||||
# Manually apply the transformation
|
||||
transformed_vertices = []
|
||||
for v in local_vertices:
|
||||
# Apply scaling
|
||||
scaled_v = Vec3(v.x * block_ref.dxf.xscale, v.y * block_ref.dxf.yscale, v.z * block_ref.dxf.zscale)
|
||||
# Apply rotation and then translation
|
||||
world_vertex = block_ref.dxf.insert + scaled_v.rotate_deg(block_ref.dxf.rotation)
|
||||
transformed_vertices.append(world_vertex)
|
||||
world_vertices = transformed_vertices
|
||||
|
||||
if block_name not in found_blocks:
|
||||
found_blocks[block_name] = []
|
||||
|
||||
found_blocks[block_name].append(
|
||||
{
|
||||
"插入点": f"({block_ref.dxf.insert.x:.3f}, {block_ref.dxf.insert.y:.3f})",
|
||||
"旋转角度": f"{block_ref.dxf.rotation:.3f}",
|
||||
"比例": f"({block_ref.dxf.xscale:.3f}, {block_ref.dxf.yscale:.3f})",
|
||||
"世界坐标": [f"({v.x:.3f}, {v.y:.3f})" for v in world_vertices],
|
||||
}
|
||||
)
|
||||
|
||||
if not found_blocks:
|
||||
print(f"在布局 '{TARGET_LAYOUT}' 中没有找到任何目标图块。")
|
||||
else:
|
||||
print(
|
||||
f"在布局 '{TARGET_LAYOUT}' 中找到了 {sum(len(v) for v in found_blocks.values())} 个目标图块的实例。"
|
||||
)
|
||||
print("--- 结果 ---")
|
||||
print(json.dumps(found_blocks, indent=4, ensure_ascii=False))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
file_path = sys.argv[1]
|
||||
main(file_path)
|
||||
else:
|
||||
default_path = r"C:\Users\83500\久翌\CAD编辑同步excel\测试文件区\04_Test_Files\塔器.dxf"
|
||||
print(f"未提供文件路径参数。将使用默认路径: {default_path}")
|
||||
main(default_path)
|
||||
34
03_Python_OpenSource_DXF/list_layouts.py
Normal file
34
03_Python_OpenSource_DXF/list_layouts.py
Normal file
@ -0,0 +1,34 @@
|
||||
import ezdxf
|
||||
import sys
|
||||
import os
|
||||
|
||||
def list_layouts(dxf_file_path: str):
|
||||
if not os.path.exists(dxf_file_path):
|
||||
print(f"错误: 文件不存在 '{dxf_file_path}'")
|
||||
return
|
||||
|
||||
try:
|
||||
doc = ezdxf.readfile(dxf_file_path)
|
||||
except IOError:
|
||||
print(f"错误: 无法打开文件 '{dxf_file_path}'")
|
||||
return
|
||||
except ezdxf.DXFStructureError as e:
|
||||
print(f"错误: 无效或损坏的 DXF 文件. {e}")
|
||||
return
|
||||
|
||||
print(f"'{os.path.basename(dxf_file_path)}' 文件中的布局:")
|
||||
# The 'Model' layout is the model space.
|
||||
print(f"- Model (模型空间)")
|
||||
# Iterate over paper space layouts
|
||||
for layout in doc.layouts:
|
||||
if layout.name.lower() != 'model':
|
||||
print(f"- {layout.name} (图纸空间)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
file_path = sys.argv[1]
|
||||
list_layouts(file_path)
|
||||
else:
|
||||
default_path = r"C:\Users\83500\久翌\CAD编辑同步excel\测试文件区\04_Test_Files\塔器.dxf"
|
||||
print(f"未提供文件路径。使用默认路径: {default_path}")
|
||||
list_layouts(default_path)
|
||||
Loading…
Reference in New Issue
Block a user