dxfedit/03_Python_OpenSource_DXF/find_block_coordinates.py
puzzlesion 31e03fd2d9 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.
2025-09-15 10:09:10 +08:00

149 lines
4.9 KiB
Python

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)