blenderpython/suw_core/part_creator.py

793 lines
32 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
SUW Core - Part Creator Module
拆分自: suw_impl.py (Line 1302-1600)
用途: Blender部件创建、板材管理、UV处理
版本: 1.0.0
作者: SUWood Team
"""
from . import material_manager as mm_module
from .material_manager import material_manager
from .memory_manager import memory_manager, dependency_manager, safe_blender_operation
from .data_manager import data_manager, get_data_manager
import time
import logging
from typing import Dict, Any, Optional, List, Tuple
# 设置日志
logger = logging.getLogger(__name__)
# 检查Blender可用性
try:
import bpy
BLENDER_AVAILABLE = True
except ImportError:
BLENDER_AVAILABLE = False
# ==================== 部件创建器类 ====================
class PartCreator:
"""部件创建器 - 负责所有部件相关操作"""
def __init__(self):
"""
初始化部件创建器 - 完全独立不依赖suw_impl
"""
# 使用全局数据管理器
self.data_manager = get_data_manager()
# 【修复】初始化时间戳避免AttributeError
self._last_board_creation_time = 0
# 创建统计
self.creation_stats = {
"parts_created": 0,
"boards_created": 0,
"creation_errors": 0
}
logger.info("PartCreator 初始化完成")
def get_parts(self, data: Dict[str, Any]) -> Dict[str, Any]:
"""获取零件信息 - 保持原始方法名和参数"""
return self.data_manager.get_parts(data)
def c04(self, data: Dict[str, Any]):
"""c04 - 添加部件 - 修复版本参考suw_impl.py的实现"""
try:
if not BLENDER_AVAILABLE:
logger.warning("Blender 不可用,跳过零件创建")
return
uid = data.get("uid")
root = data.get("cp")
if not uid or not root:
logger.error("缺少必要参数: uid或cp")
return
logger.info(f" 开始创建部件: uid={uid}, cp={root}")
# 【修复1】获取parts数据结构
parts = self.get_parts(data)
# 【修复2】检查是否已存在
if root in parts:
existing_part = parts[root]
if existing_part and self._is_object_valid(existing_part):
logger.info(f"✅ 部件 {root} 已存在,跳过创建")
return existing_part
else:
logger.warning(f"清理无效的部件引用: {root}")
del parts[root]
# 【修复3】创建部件容器 - 修改命名格式为Part_{uid}_{cp}
part_name = f"Part_{uid}_{root}"
part = bpy.data.objects.new(part_name, None)
bpy.context.scene.collection.objects.link(part)
logger.info(f"✅ 创建Part对象: {part_name}")
# 【修复4】设置部件基本属性
part["sw_uid"] = uid
part["sw_cp"] = root
part["sw_typ"] = "part"
part["sw_zid"] = data.get("zid")
part["sw_pid"] = data.get("pid")
# 【新增】设置layer属性 - 参考Ruby版本的逻辑
layer = data.get("layer", 0)
part["sw_layer"] = layer
if layer == 1:
logger.info(f"✅ 部件 {part_name} 标记为门板图层 (layer=1)")
elif layer == 2:
logger.info(f"✅ 部件 {part_name} 标记为抽屉图层 (layer=2)")
else:
logger.info(f"✅ 部件 {part_name} 标记为普通图层 (layer=0)")
# 【新增】设置门板属性 - 参考Ruby版本的逻辑
door_type = data.get("dor", 0)
part["sw_door"] = door_type
if door_type in [10, 15]:
part["sw_door_width"] = data.get("dow", 0)
part["sw_door_pos"] = data.get("dop", "F")
logger.info(
f"✅ 部件 {part_name} 设置门板属性: door_type={door_type}, width={data.get('dow', 0)}, pos={data.get('dop', 'F')}")
# 【新增】设置抽屉属性 - 参考Ruby版本的逻辑
drawer_type = data.get("drw", 0)
part["sw_drawer"] = drawer_type
if drawer_type in [73, 74]: # DR_LP/DR_RP
part["sw_dr_depth"] = data.get("drd", 0)
logger.info(
f"📦 部件 {part_name} 设置抽屉属性: drawer_type={drawer_type}, depth={data.get('drd', 0)}")
elif drawer_type == 70: # DR_DP
drv = data.get("drv")
if drv:
# 这里需要解析向量,暂时存储原始值
part["sw_drawer_dir"] = drv
logger.info(f"📦 部件 {part_name} 设置抽屉方向: {drv}")
# 【新增】设置Part对象的父对象为Zone对象
zone_name = f"Zone_{uid}"
zone_obj = bpy.data.objects.get(zone_name)
if zone_obj:
part.parent = zone_obj
logger.info(f"✅ 设置Part对象 {part_name} 的父对象为Zone: {zone_name}")
else:
logger.warning(f"⚠️ 未找到Zone对象: {zone_name}Part对象将没有父对象")
# 【修复5】存储部件到数据结构 - 使用data_manager.add_part()方法
self.data_manager.add_part(uid, root, part)
logger.info(
f"✅ 使用data_manager.add_part()存储部件数据: uid={uid}, cp={root}")
# 【修复6】处理finals数据
finals = data.get("finals", [])
logger.info(f" 处理 {len(finals)} 个板材数据")
created_boards = 0
for i, final_data in enumerate(finals):
try:
board = self.create_board_with_material_and_uv(
part, final_data)
if board:
created_boards += 1
logger.info(
f"✅ 板材 {i+1}/{len(finals)} 创建成功: {board.name}")
# 【修复7】移除频繁的依赖图更新避免评估过程中的错误
# if i % 5 == 0:
# bpy.context.view_layer.update()
else:
logger.warning(f"⚠️ 板材 {i+1}/{len(finals)} 创建失败")
except Exception as e:
logger.error(f"❌ 创建板材 {i+1}/{len(finals)} 失败: {e}")
# 【修复8】单个板材失败时的恢复 - 移除依赖图更新
try:
import gc
gc.collect()
# bpy.context.view_layer.update() # 移除这行
except:
pass
logger.info(f"📊 板材创建统计: {created_boards}/{len(finals)} 成功")
# 【修复9】最终清理 - 移除依赖图更新
try:
# bpy.context.view_layer.update() # 移除这行
import gc
gc.collect()
except Exception as cleanup_error:
logger.warning(f"最终清理失败: {cleanup_error}")
# 【修复10】验证创建结果
if part.name in bpy.data.objects:
logger.info(f"🎉 部件创建完全成功: {part_name}")
return part
else:
logger.error(f"❌ 部件创建失败: {part_name} 不在bpy.data.objects中")
return None
except Exception as e:
logger.error(f"❌ c04命令执行失败: {e}")
import traceback
logger.error(traceback.format_exc())
return None
def create_board_with_material_and_uv(self, part, data):
"""创建板材并关联材质和启用UV - 完全修复版本避免bpy.ops"""
try:
# 获取正反面数据
obv = data.get("obv")
rev = data.get("rev")
if not obv or not rev:
logger.warning("缺少正反面数据,创建默认板材")
return self.create_default_board_with_material(part, data)
# 解析顶点计算精确尺寸
obv_vertices = self._parse_surface_vertices(obv)
rev_vertices = self._parse_surface_vertices(rev)
if len(obv_vertices) >= 3 and len(rev_vertices) >= 3:
# 计算板材的精确边界
all_vertices = obv_vertices + rev_vertices
min_x = min(v[0] for v in all_vertices)
max_x = max(v[0] for v in all_vertices)
min_y = min(v[1] for v in all_vertices)
max_y = max(v[1] for v in all_vertices)
min_z = min(v[2] for v in all_vertices)
max_z = max(v[2] for v in all_vertices)
# 计算中心点和精确尺寸
center_x = (min_x + max_x) / 2
center_y = (min_y + max_y) / 2
center_z = (min_z + max_z) / 2
size_x = max(max_x - min_x, 0.001) # 确保最小尺寸
size_y = max(max_y - min_y, 0.001)
size_z = max(max_z - min_z, 0.001)
logger.info(
f" 计算板材尺寸: {size_x:.3f}x{size_y:.3f}x{size_z:.3f}m, 中心: ({center_x:.3f},{center_y:.3f},{center_z:.3f})")
# 【修复1】完全避免bpy.ops直接创建网格对象
board = None
try:
# 创建网格数据
mesh_data = bpy.data.meshes.new("Board_Mesh")
# 创建立方体的顶点和面
vertices = [
(-0.5, -0.5, -0.5), # 0
(0.5, -0.5, -0.5), # 1
(0.5, 0.5, -0.5), # 2
(-0.5, 0.5, -0.5), # 3
(-0.5, -0.5, 0.5), # 4
(0.5, -0.5, 0.5), # 5
(0.5, 0.5, 0.5), # 6
(-0.5, 0.5, 0.5) # 7
]
faces = [
(0, 1, 2, 3), # 底面
(4, 7, 6, 5), # 顶面
(0, 4, 5, 1), # 前面
(2, 6, 7, 3), # 后面
(1, 5, 6, 2), # 右面
(0, 3, 7, 4) # 左面
]
# 创建网格
mesh_data.from_pydata(vertices, [], faces)
mesh_data.update()
# 创建对象
board = bpy.data.objects.new("Board", mesh_data)
# 设置位置
board.location = (center_x, center_y, center_z)
# 添加到场景
bpy.context.scene.collection.objects.link(board)
logger.info("✅ 使用直接创建方式成功创建板材对象")
except Exception as create_error:
logger.error(f"直接创建板材对象失败: {create_error}")
return None
if not board:
logger.error("无法创建板材对象")
return None
# 【修复2】缩放到精确尺寸
board.scale = (size_x, size_y, size_z)
# 【调试】添加缩放验证日志
logger.info(f"🔧 板材缩放信息:")
logger.info(
f" 计算尺寸: {size_x:.6f} x {size_y:.6f} x {size_z:.6f}")
logger.info(f" 应用缩放: {board.scale}")
logger.info(
f" 中心位置: ({center_x:.6f}, {center_y:.6f}, {center_z:.6f})")
logger.info(f" 板材名称: {board.name}")
# 【修复3】设置属性和父子关系
board.parent = part
board.name = f"Board_{part.name}"
board["sw_face_type"] = "board"
board["sw_uid"] = part.get("sw_uid")
board["sw_cp"] = part.get("sw_cp")
board["sw_typ"] = "board"
logger.info(f"✅ 板材属性设置完成: {board.name}, 父对象: {part.name}")
# 【修复4】关联材质 - 使用修复后的材质管理器
color = data.get("ckey", "mat_default")
if color:
try:
# 导入材质管理器
from suw_core.material_manager import MaterialManager
material_manager = MaterialManager()
material = material_manager.get_texture(color)
if material and board.data:
# 清空现有材质
board.data.materials.clear()
# 添加新材质
board.data.materials.append(material)
logger.info(f"✅ 材质 {color} 已关联到板材 {board.name}")
else:
logger.warning(f"材质 {color} 未找到或板材数据无效")
except Exception as e:
logger.error(f"关联材质失败: {e}")
# 【修复5】启用UV - 移除依赖图更新
self.enable_uv_for_board(board)
return board
else:
logger.warning("顶点数据不足,创建默认板材")
return self.create_default_board_with_material(part, data)
except Exception as e:
logger.error(f"创建板材失败: {e}")
return self.create_default_board_with_material(part, data)
def enable_uv_for_board(self, board):
"""为板件启用UV - 保持原始方法名和参数"""
try:
if not board or not board.data:
logger.warning("无效的板件对象无法启用UV")
return
# 确保网格数据存在
mesh = board.data
if not mesh:
logger.warning("板件没有网格数据")
return
# 创建UV贴图层如果不存在
if not mesh.uv_layers:
uv_layer = mesh.uv_layers.new(name="UVMap")
else:
uv_layer = mesh.uv_layers[0]
# 确保UV层是活动的
mesh.uv_layers.active = uv_layer
# 更新网格数据 - 移除可能导致依赖图更新的操作
# mesh.calc_loop_triangles() # 移除这行
# 为立方体创建基本UV坐标
if len(mesh.polygons) == 6: # 标准立方体
# 为每个面分配UV坐标
for poly_idx, poly in enumerate(mesh.polygons):
# 标准UV坐标 (0,0) (1,0) (1,1) (0,1)
uv_coords = [(0.0, 0.0), (1.0, 0.0),
(1.0, 1.0), (0.0, 1.0)]
for loop_idx, loop_index in enumerate(poly.loop_indices):
if loop_idx < len(uv_coords):
uv_layer.data[loop_index].uv = uv_coords[loop_idx]
else:
# 为非标准网格设置简单UV
for loop in mesh.loops:
uv_layer.data[loop.index].uv = (0.5, 0.5)
# 更新网格 - 移除可能导致依赖图更新的操作
# mesh.update() # 移除这行
except Exception as e:
logger.error(f"启用UV失败: {e}")
def create_default_board_with_material(self, part, data):
"""创建默认板材 - 修复版本,使用更安全的对象创建方式"""
try:
# 【修复1】使用更安全的对象创建方式避免bpy.ops上下文问题
board = None
# 方法1尝试使用bpy.ops如果上下文可用
try:
if hasattr(bpy.context, 'active_object'):
bpy.ops.mesh.primitive_cube_add(
size=1,
location=(0, 0, 0)
)
board = bpy.context.active_object
logger.info("✅ 使用bpy.ops成功创建立方体")
else:
raise Exception("bpy.context.active_object不可用")
except Exception as ops_error:
logger.warning(f"使用bpy.ops创建对象失败: {ops_error}")
# 方法2回退方案 - 直接创建网格对象
try:
# 创建网格数据
mesh_data = bpy.data.meshes.new("Board_Mesh")
# 创建立方体的顶点和面
vertices = [
(-0.5, -0.5, -0.5), # 0
(0.5, -0.5, -0.5), # 1
(0.5, 0.5, -0.5), # 2
(-0.5, 0.5, -0.5), # 3
(-0.5, -0.5, 0.5), # 4
(0.5, -0.5, 0.5), # 5
(0.5, 0.5, 0.5), # 6
(-0.5, 0.5, 0.5) # 7
]
faces = [
(0, 1, 2, 3), # 底面
(4, 7, 6, 5), # 顶面
(0, 4, 5, 1), # 前面
(2, 6, 7, 3), # 后面
(1, 5, 6, 2), # 右面
(0, 3, 7, 4) # 左面
]
# 创建网格
mesh_data.from_pydata(vertices, [], faces)
mesh_data.update()
# 创建对象
board = bpy.data.objects.new("Board_Default", mesh_data)
# 添加到场景
bpy.context.collection.objects.link(board)
logger.info("✅ 使用直接创建方式成功创建立方体")
except Exception as direct_error:
logger.error(f"直接创建对象也失败: {direct_error}")
return None
if not board:
logger.error("无法创建板材对象")
return None
# 【修复2】设置属性和父子关系
try:
board.parent = part
board.name = f"Board_{part.name}_default"
board["sw_face_type"] = "board"
# 从part获取uid和cp信息
uid = part.get("sw_uid")
cp = part.get("sw_cp")
board["sw_uid"] = uid
board["sw_cp"] = cp
board["sw_typ"] = "board"
logger.info(f"✅ 默认板材属性设置完成: {board.name}, 父对象: {part.name}")
except Exception as attr_error:
logger.error(f"设置板材属性失败: {attr_error}")
# 【修复3】关联默认材质 - 使用更安全的材质处理
try:
color = data.get("ckey", "mat_default")
# 使用更安全的材质管理器初始化方式
if not mm_module.material_manager:
mm_module.material_manager = mm_module.MaterialManager()
# 额外安全检查
if mm_module.material_manager and hasattr(mm_module.material_manager, 'get_texture'):
material = mm_module.material_manager.get_texture(color)
else:
logger.error("材质管理器未正确初始化")
material = None
if material and board.data:
board.data.materials.clear()
board.data.materials.append(material)
logger.info(f"✅ 材质 {color} 已关联到板材 {board.name}")
else:
logger.warning(f"材质 {color} 未找到或板材数据无效")
except Exception as material_error:
logger.error(f"❌ 默认材质处理失败: {material_error}")
# 【修复4】启用UV
try:
self.enable_uv_for_board(board)
except Exception as uv_error:
logger.error(f"启用UV失败: {uv_error}")
return board
except Exception as e:
logger.error(f"创建默认板材失败: {e}")
return None
def parse_surface_vertices(self, surface):
"""解析表面顶点 - 保持原始方法名和参数"""
try:
vertices = []
if not surface:
return vertices
segs = surface.get("segs", [])
for seg in segs:
if len(seg) >= 2:
coord_str = seg[0].strip('()')
try:
# 解析坐标字符串
coords = coord_str.split(',')
if len(coords) >= 3:
x = float(coords[0]) * 0.001 # 转换为米
y = float(coords[1]) * 0.001
z = float(coords[2]) * 0.001
vertices.append((x, y, z))
except ValueError as e:
logger.warning(f"解析顶点坐标失败: {coord_str}, 错误: {e}")
continue
logger.debug(f"解析得到 {len(vertices)} 个顶点")
return vertices
except Exception as e:
logger.error(f"解析表面顶点失败: {e}")
return []
def _is_object_valid(self, obj) -> bool:
"""检查对象是否有效 - 保持原始方法名和参数"""
try:
if not obj:
return False
if not BLENDER_AVAILABLE:
return True # 在非Blender环境中假设有效
# 检查对象是否仍在Blender数据中
return obj.name in bpy.data.objects
except Exception:
return False
def clear_part_children(self, part):
"""清理部件子对象 - 保持原始方法名和参数"""
try:
if not part or not BLENDER_AVAILABLE:
return
# 清理所有子对象
children_to_remove = []
for child in part.children:
children_to_remove.append(child)
for child in children_to_remove:
if child.name in bpy.data.objects:
bpy.data.objects.remove(child, do_unlink=True)
logger.info(f"清理部件 {part.name}{len(children_to_remove)} 个子对象")
except Exception as e:
logger.error(f"清理部件子对象失败: {e}")
def get_creation_stats(self) -> Dict[str, Any]:
"""获取创建统计信息"""
return self.creation_stats.copy()
def get_part_creator_stats(self) -> Dict[str, Any]:
"""获取部件创建器统计信息"""
try:
# 从data_manager获取parts数据
parts_data = {}
if hasattr(self.data_manager, 'parts'):
parts_data = self.data_manager.parts
stats = {
"manager_type": "PartCreator",
"parts_by_uid": {uid: len(parts) for uid, parts in parts_data.items()},
"total_parts": sum(len(parts) for parts in parts_data.values()),
"creation_stats": self.creation_stats.copy(),
"data_manager_attached": self.data_manager is not None,
"blender_available": BLENDER_AVAILABLE
}
return stats
except Exception as e:
logger.error(f"获取部件创建器统计失败: {e}")
return {"error": str(e)}
def reset_creation_stats(self):
"""重置创建统计信息"""
self.creation_stats = {
"parts_created": 0,
"boards_created": 0,
"creation_errors": 0
}
logger.info("创建统计信息已重置")
def _parse_surface_vertices(self, surface):
"""解析表面顶点坐标"""
try:
vertices = []
segs = surface.get("segs", [])
for seg in segs:
if len(seg) >= 2:
coord_str = seg[0].strip('()')
try:
x, y, z = map(float, coord_str.split(','))
# 转换为米Blender使用米作为单位
vertices.append((x * 0.001, y * 0.001, z * 0.001))
except ValueError:
continue
return vertices
except Exception as e:
logger.error(f"解析表面顶点失败: {e}")
return []
def _calculate_board_dimensions(self, final_data: dict) -> Tuple[Optional['mathutils.Vector'], Optional['mathutils.Vector']]:
"""
[V2] 计算板材的精确尺寸和中心点。
"""
try:
obv_vertices = self._parse_surface_vertices(final_data.get("obv"))
rev_vertices = self._parse_surface_vertices(final_data.get("rev"))
if not obv_vertices or not rev_vertices:
logger.warning("无法解析顶点数据,使用默认尺寸")
return (None, None)
all_vertices = obv_vertices + rev_vertices
min_x = min(v[0] for v in all_vertices)
max_x = max(v[0] for v in all_vertices)
min_y = min(v[1] for v in all_vertices)
max_y = max(v[1] for v in all_vertices)
min_z = min(v[2] for v in all_vertices)
max_z = max(v[2] for v in all_vertices)
center_x = (min_x + max_x) / 2
center_y = (min_y + max_y) / 2
center_z = (min_z + max_z) / 2
size_x = max(max_x - min_x, 0.001)
size_y = max(max_y - min_y, 0.001)
size_z = max(max_z - min_z, 0.001)
logger.info(
f" 计算板材尺寸: {size_x:.3f}x{size_y:.3f}x{size_z:.3f}m, 中心: ({center_x:.3f},{center_y:.3f},{center_z:.3f})")
return (mathutils.Vector((center_x, center_y, center_z)), mathutils.Vector((size_x, size_y, size_z)))
except Exception as e:
logger.error(f"❌ 计算板材尺寸失败: {e}")
return None, None
def _create_board_direct(self, parent_obj: 'bpy.types.Object', final_data: dict, center: 'mathutils.Vector', dimensions: 'mathutils.Vector') -> Optional['bpy.types.Object']:
"""
[V2] 通过直接操作bpy.data来安全地创建板材对象避免bpy.ops的上下文错误。
"""
try:
# 1. 创建网格和对象数据
mesh_name = f"Board_Mesh_{parent_obj.name}"
board_name = f"Board_{parent_obj.name}"
mesh = bpy.data.meshes.new(mesh_name)
board_obj = bpy.data.objects.new(board_name, mesh)
# 2. 将新对象链接到与父对象相同的集合中
if parent_obj.users_collection:
parent_obj.users_collection[0].objects.link(board_obj)
else:
# 如果父对象不在任何集合中,则回退到场景主集合
bpy.context.scene.collection.objects.link(board_obj)
# 3. 直接根据最终尺寸创建顶点。这可以确保对象的缩放比例始终为(1,1,1)
dx, dy, dz = dimensions.x / 2, dimensions.y / 2, dimensions.z / 2
verts = [
(dx, dy, dz), (-dx, dy, dz), (-dx, -dy, dz), (dx, -dy, dz),
(dx, dy, -dz), (-dx, dy, -dz), (-dx, -dy, -dz), (dx, -dy, -dz),
]
faces = [
(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
(1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0)
]
mesh.from_pydata(verts, [], faces)
mesh.update()
# 4. 设置最终的位置和父子关系
board_obj.location = center
board_obj.parent = parent_obj
return board_obj
except Exception as e:
logger.error(f"❌ 使用直接数据创建板材时失败: {e}")
# 清理创建失败时可能产生的孤立数据
if 'board_obj' in locals() and board_obj and board_obj.name in bpy.data.objects:
bpy.data.objects.remove(board_obj, do_unlink=True)
if 'mesh' in locals() and mesh and mesh.name in bpy.data.meshes:
bpy.data.meshes.remove(mesh)
return None
def _add_board_part(self, part_obj: 'bpy.types.Object', final_data: dict) -> Optional['bpy.types.Object']:
"""
[V2] 将板材对象添加到部件对象的集合中。
"""
try:
# 1. 计算板材的精确尺寸和中心点
center, dimensions = self._calculate_board_dimensions(final_data)
if not center or not dimensions:
logger.warning(f"无法计算板材尺寸,跳过添加板材: {final_data.get('name')}")
return None
# 2. 使用直接数据创建板材对象
board_obj = self._create_board_direct(
part_obj, final_data, center, dimensions)
if not board_obj:
logger.warning(
f"使用直接数据创建板材失败,跳过添加板材: {final_data.get('name')}")
return None
# 3. 设置板材属性
board_obj["sw_face_type"] = "board"
board_obj["sw_uid"] = part_obj.get("sw_uid")
board_obj["sw_cp"] = part_obj.get("sw_cp")
board_obj["sw_typ"] = "board"
# 4. 关联材质
color = final_data.get("ckey", "mat_default")
if color:
try:
# 导入材质管理器
from suw_core.material_manager import MaterialManager
material_manager = MaterialManager()
material = material_manager.get_texture(color)
if material and board_obj.data:
board_obj.data.materials.clear()
board_obj.data.materials.append(material)
logger.info(f"✅ 材质 {color} 已关联到板材 {board_obj.name}")
else:
logger.warning(f"材质 {color} 未找到或板材数据无效")
except Exception as e:
logger.error(f"关联材质失败: {e}")
# 5. 启用UV
self.enable_uv_for_board(board_obj)
return board_obj
except Exception as e:
logger.error(f"❌ 添加板材失败: {e}")
return None
# ==================== 模块实例 ====================
# 全局实例
part_creator = None
def init_part_creator():
"""初始化部件创建器 - 不再需要suw_impl参数"""
global part_creator
part_creator = PartCreator()
return part_creator
def get_part_creator():
"""获取全局部件创建器实例"""
global part_creator
if part_creator is None:
part_creator = init_part_creator()
return part_creator
# 确保PartCreator全局实例正确初始化
if part_creator is None:
part_creator = init_part_creator()