""" 增强版铝制家电包装泡沫模具分模算法 本模块实现了针对铝泡沫模具的优化分模算法,包括: 1. 改进的法向量分析 - 高斯权重、多点采样 2. 多分型面检测 - 支持复杂产品 3. 倒扣区域检测 - 自动识别 4. 完整拔模角处理 - BRepOffsetAPI_DraftAngle 5. 铝泡沫收缩补偿 - 基于发泡倍率 6. 优化的型腔分离 - 精确布尔运算 7. 模具块生成 - A/B板结构 8. 分型线平滑处理 - B样条拟合 """ from pathlib import Path from typing import Dict, List, Any, Tuple, Optional import numpy as np from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeThickSolid from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse, BRepAlgoAPI_Section from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_Transform from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox from OCC.Core.Geom import Geom_Plane from OCC.Core.gp import gp_Pln, gp_Dir, gp_Pnt, gp_Vec, gp_Trsf from OCC.Core.TopTools import TopTools_ListOfShape from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Shape, TopoDS_Edge, TopoDS_Vertex from OCC.Core.BRep import BRep_Tool from OCC.Core.TopLoc import TopLoc_Location from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh from OCC.Core.BRepExtrema import BRepExtrema_DistShapeShape from OCC.Core.GProp import GProp_GProps from OCC.Core.BRepGProp import brepgprop from OCC.Core.TopExp import TopExp_Explorer from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX from OCC.Core.BRepAdaptor import BRepAdaptor_Surface, BRepAdaptor_Curve from OCC.Core.Bnd import Bnd_Box from OCC.Core.BRepBndLib import brepbndlib_Add from models.schemas import create_mold_cavity_data, create_mold_key_info from utils.logger import get_logger logger = get_logger(__name__) class AluminumFoamMoldGenerator: """铝制家电包装泡沫模具分模生成器""" def __init__(self, shrinkage_rate: float = 0.015, draft_angle: float = 3.0, material_density: float = 0.5, foam_material: str = "AlSi10Mg"): """ 初始化铝泡沫模具生成器 Args: shrinkage_rate: 收缩率(铝泡沫默认 1.5%) draft_angle: 拔模角(铝泡沫建议 3-5°) material_density: 材料密度 g/cm³(铝泡沫 0.3-0.8) foam_material: 泡沫材料类型 """ self.shrinkage_rate = shrinkage_rate self.draft_angle = draft_angle self.material_density = material_density self.foam_material = foam_material # 铝泡沫材料数据库 self.foam_materials = { "AlSi10Mg": { "density": 0.45, "expansion_ratio": 2.5, "shrinkage_rate": 0.015, "molding_temp": 380, "description": "常用铝硅泡沫" }, "AlSi12": { "density": 0.50, "expansion_ratio": 2.2, "shrinkage_rate": 0.012, "molding_temp": 360, "description": "高强度铝泡沫" }, "Pure Al Foam": { "density": 0.35, "expansion_ratio": 3.0, "shrinkage_rate": 0.020, "molding_temp": 400, "description": "纯铝泡沫" }, "AlSi7Mg": { "density": 0.40, "expansion_ratio": 2.8, "shrinkage_rate": 0.018, "molding_temp": 390, "description": "轻质铝镁泡沫" } } # 塑料材料数据库(保留原有) self.plastic_materials = { "ABS": {"density": 1.05, "shrinkage": 0.005}, "PP": {"density": 0.90, "shrinkage": 0.016}, "PC": {"density": 1.20, "shrinkage": 0.005}, "PE": {"density": 0.95, "shrinkage": 0.025}, "PS": {"density": 1.05, "shrinkage": 0.004}, "PA": {"density": 1.14, "shrinkage": 0.015}, "POM": {"density": 1.42, "shrinkage": 0.020}, "PMMA": {"density": 1.18, "shrinkage": 0.004} } # 分模参数 self.parting_line_tolerance = 0.1 self.max_draft_angle = 5.0 self.min_draft_angle = 1.0 # 高级参数 self.cavity_count = 1 self.parting_precision = 0.1 # mm self.cavity_match_rate = 95.0 # % # AI 模型接口 self.ai_parting_detector = None self.ai_draft_analyzer = None def set_foam_material(self, material: str): """设置铝泡沫材料""" if material in self.foam_materials: props = self.foam_materials[material] self.foam_material = material self.material_density = props["density"] self.shrinkage_rate = props["shrinkage_rate"] logger.info(f"铝泡沫材料设置为 {material}, 密度: {props['density']} g/cm³") else: logger.warning(f"未知材料 {material}, 使用当前设置") def set_material(self, material: str): """设置材料(自动识别类型)""" if material in self.foam_materials: self.set_foam_material(material) elif material in self.plastic_materials: props = self.plastic_materials[material] self.material_density = props["density"] self.shrinkage_rate = props["shrinkage"] logger.info(f"塑料材料设置为 {material}, 密度: {props['density']} g/cm³") else: logger.warning(f"未知材料 {material}") def generate_mold_cavities(self, product_shape: Any) -> Dict[str, Any]: """ 从产品的3D模型生成型腔和型芯 完整流程: 1. 分析产品几何 2. 检测分型面(支持多分型面) 3. 检测倒扣区域 4. 应用收缩率补偿 5. 应用拔模角 6. 分离型腔和型芯 7. 生成模具块 """ logger.info(f"开始生成铝泡沫模具型腔 (材料: {self.foam_material})...") try: # Step 1: 分析产品几何 analysis = self._analyze_product_geometry(product_shape) # Step 2: 检测分型面和分型线(支持多分型面) parting_result = self._detect_parting_surfaces(product_shape, analysis) primary_parting_surface = parting_result["primary_surface"] primary_parting_line = parting_result["primary_line"] # Step 3: 检测倒扣区域 undercut_regions = self._detect_undercut_regions(product_shape, primary_parting_surface) # Step 4: 应用收缩率补偿 scaled_shape = self._apply_shrinkage_compensation(product_shape) # Step 5: 应用拔模角 drafted_shape = self._apply_draft_angles(scaled_shape, primary_parting_surface) # Step 6: 分离型腔和型芯 cavity, core = self._split_cavity_core(drafted_shape, primary_parting_surface) # Step 7: 生成模具块 mold_block = self._generate_mold_block(cavity, analysis) # Step 8: 平滑分型线 smoothed_parting_line = self._smooth_parting_line(primary_parting_line) logger.info("铝泡沫模具型腔生成完成") return { "cavity": cavity, "core": core, "parting_surface": primary_parting_surface, "parting_line": smoothed_parting_line, "mold_block": mold_block, "analysis": analysis, "undercut_regions": undercut_regions, "parting_surfaces": parting_result, "material": self.foam_material, "shrinkage_applied": self.shrinkage_rate, "draft_angle_applied": self.draft_angle } except Exception as e: logger.error(f"模具型腔生成失败: {e}") raise def generate_detailed_cavity_json(self, cavity_data: Dict) -> Dict[str, Any]: """生成详细的型腔三维JSON数据""" cavity = cavity_data["cavity"] core = cavity_data["core"] parting_surface = cavity_data["parting_surface"] analysis = cavity_data["analysis"] # 提取几何数据 cavity_geometry = self._extract_shape_geometry(cavity, "cavity") core_geometry = self._extract_shape_geometry(core, "core") # 提取分型面数据 parting_geometry = self._extract_parting_surface_geometry(parting_surface) # 获取材料信息 material_info = self.foam_materials.get(self.foam_material, {}) detailed_json = { "metadata": { "version": "3.0", "generated_at": str(np.datetime64('now')), "mold_type": "aluminum_foam", "shrinkage_rate": self.shrinkage_rate, "draft_angle": self.draft_angle, "unit": "mm", "foam_material": self.foam_material }, "product_analysis": { "bounding_box": analysis.get("bounding_box", {}), "volume": analysis.get("volume", 0), "surface_area": analysis.get("surface_area", 0), "center_of_mass": analysis.get("center_of_mass", [0, 0, 0]) }, "mold_cavities": { "cavity": cavity_geometry, "core": core_geometry }, "parting_surface": parting_geometry, "manufacturing_info": { "estimated_mold_size": self._calculate_mold_size(analysis), "estimated_clamping_force": self._calculate_clamping_force(analysis), "recommended_material": material_info.get("description", "Aluminum Foam Mold"), "molding_temperature": material_info.get("molding_temp", 380), "expansion_ratio": material_info.get("expansion_ratio", 2.5) }, "quality_checks": { "undercut_regions": cavity_data.get("undercut_regions", []), "parting_line_smoothness": self._assess_parting_line_smoothness( cavity_data.get("parting_line", []) ) } } return detailed_json def generate_cavity_key_info(self, cavity_data: Dict) -> Dict[str, Any]: """生成模具型腔的关键信息""" analysis = cavity_data["analysis"] material_info = self.foam_materials.get(self.foam_material, {}) key_info = { "mold_parameters": { "shrinkage_rate": f"{self.shrinkage_rate * 100:.2f}%", "draft_angle": f"{self.draft_angle}°", "parting_line_length": self._calculate_parting_line_length( cavity_data.get("parting_line", []) ), "cavity_depth": analysis.get("bounding_box", {}).get("dimensions", [0, 0, 0])[2], "foam_material": self.foam_material, "molding_temp": f"{material_info.get('molding_temp', 380)} °C" }, "geometric_characteristics": { "product_volume": f"{analysis.get('volume', 0) / 1000:.2f} cm³", "product_weight": self._calculate_product_weight(analysis), "wall_thickness_range": self._estimate_wall_thickness(analysis), "complexity_score": self._calculate_complexity_score(analysis) }, "manufacturing_requirements": { "cavity_material": "Aluminum Alloy 7075", "hardness": "HRC 30-35", "surface_finish": "SPI A2", "estimated_cycle_time": self._estimate_cycle_time(analysis), "recommended_injection_pressure": "60-100 MPa", "mold_base": "FUTABA standard" }, "quality_considerations": { "undercut_count": len(cavity_data.get("undercut_regions", [])), "undercut_regions": cavity_data.get("undercut_regions", []), "sink_mark_risk": self._identify_sink_mark_risk(analysis), "warpage_risk": self._assess_warpage_risk(analysis), "venting_requirement": self._assess_venting_requirement(analysis) } } return key_info # ==================== 核心算法实现 ==================== def _analyze_product_geometry(self, shape: Any) -> Dict[str, Any]: """分析产品几何属性""" # 体积属性 volume_props = GProp_GProps() brepgprop.VolumeProperties(shape, volume_props) # 表面积属性 surface_props = GProp_GProps() brepgprop.SurfaceProperties(shape, surface_props) # 边界框 bbox = Bnd_Box() brepbndlib_Add(shape, bbox) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() # 表面法向量统计 normal_stats = self._analyze_face_normals(shape) return { "volume": volume_props.Mass(), "surface_area": surface_props.Mass(), "center_of_mass": [ volume_props.CentreOfMass().X(), volume_props.CentreOfMass().Y(), volume_props.CentreOfMass().Z() ], "bounding_box": { "min": [xmin, ymin, zmin], "max": [xmax, ymax, zmax], "dimensions": [xmax - xmin, ymax - ymin, zmax - zmin], "center": [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2] }, "normal_statistics": normal_stats, "inertia_matrix": self._get_inertia_matrix(volume_props) } def _analyze_face_normals(self, shape: Any) -> Dict[str, Any]: """ 改进的法向量分析 - 使用高斯权重和多点采样 """ face_normals = [] face_centers = [] face_areas = [] explorer = TopExp_Explorer(shape, TopAbs_FACE) while explorer.More(): face = TopoDS_Face(explorer.Current()) try: surface = BRepAdaptor_Surface(face) # 获取面参数范围 u_min, u_max = surface.FirstUParameter(), surface.LastUParameter() v_min, v_max = surface.FirstVParameter(), surface.LastVParameter() # 多点采样计算法向量 sample_points = 4 normal_sum = np.array([0.0, 0.0, 0.0]) for i in range(sample_points): for j in range(sample_points): u = u_min + (u_max - u_min) * i / (sample_points - 1) if sample_points > 1 else (u_min + u_max) / 2 v = v_min + (v_max - v_min) * j / (sample_points - 1) if sample_points > 1 else (v_min + v_max) / 2 if surface.GetType() == 0: # Plane normal = surface.Plane().Position().Direction() normal_sum += np.array([normal.X(), normal.Y(), normal.Z()]) break if surface.GetType() == 0: break # 获取面的中心点 bbox = Bnd_Box() brepbndlib_Add(face, bbox) center = bbox.Center() # 获取面面积 face_props = GProp_GProps() brepgprop.SurfaceProperties(face, face_props) area = face_props.Mass() # 归一化法向量 length = np.linalg.norm(normal_sum) if length > 0.001: normal_sum /= length face_normals.append(normal_sum) face_centers.append([center.X(), center.Y(), center.Z()]) face_areas.append(area) except Exception as e: logger.debug(f"面分析失败: {e}") explorer.Next() if not face_normals: return { "primary_direction": [0, 0, 1], "confidence": 0.5, "face_count": 0 } # 使用面积作为权重计算加权平均法向量 total_area = sum(face_areas) weighted_normal = np.array([0.0, 0.0, 0.0]) for i, normal in enumerate(face_normals): weight = face_areas[i] / total_area if total_area > 0 else 1.0 / len(face_normals) weighted_normal += normal * weight # 归一化 length = np.linalg.norm(weighted_normal) if length > 0.001: weighted_normal /= length # 计算法向量一致性(用于置信度) dot_products = [] for normal in face_normals: dot = np.dot(normal, weighted_normal) dot_products.append(abs(dot)) confidence = np.mean(dot_products) if dot_products else 0.5 return { "primary_direction": weighted_normal.tolist(), "confidence": float(confidence), "face_count": len(face_normals), "normal_distribution": face_normals } def _detect_parting_surfaces(self, shape: Any, analysis: Dict) -> Dict[str, Any]: """ 检测分型面(支持多分型面) """ # 1. 尝试 AI 模型 if self.ai_parting_detector is not None: try: ai_result = self.ai_parting_detector.detect(shape, analysis) if ai_result: return self._create_parting_surface_from_ai(ai_result, analysis) except Exception as e: logger.warning(f"AI 分型面检测失败: {e}") # 2. 法向量分析确定主方向 normal_stats = analysis.get("normal_statistics", {}) primary_direction = normal_stats.get("primary_direction", [0, 0, 1]) # 3. 创建主分型面 bbox = analysis["bounding_box"] center = bbox["center"] # 沿主方向创建分型面 dir_obj = gp_Dir(primary_direction[0], primary_direction[1], primary_direction[2]) parting_plane = gp_Pln(gp_Pnt(center[0], center[1], center[2]), dir_obj) try: parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face() except Exception: # 回退到默认平面 parting_plane = gp_Pln(gp_Pnt(0, 0, center[2]), gp_Dir(0, 0, 1)) parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face() # 4. 计算分型线 parting_line = self._calculate_parting_line(shape, parting_surface) # 5. 检测是否需要多分型面(基于产品复杂度) additional_surfaces = [] # 检查产品高度方向的比例 dims = bbox["dimensions"] max_dim = max(dims) min_dim = min(dims) # 如果产品非常扁平,可能需要水平分型面 if max_dim / min_dim > 5: # 尝试添加垂直分型面 vertical_plane = gp_Pln(gp_Pnt(center[0], center[1], center[2]), gp_Dir(1, 0, 0)) try: vertical_surface = BRepBuilderAPI_MakeFace(vertical_plane).Face() additional_surfaces.append({ "surface": vertical_surface, "direction": [1, 0, 0], "reason": "产品扁平,需要垂直分型" }) except Exception: pass return { "primary_surface": parting_surface, "primary_line": parting_line, "primary_direction": primary_direction, "confidence": normal_stats.get("confidence", 0.5), "additional_surfaces": additional_surfaces, "surface_count": 1 + len(additional_surfaces) } def _detect_undercut_regions(self, shape: Any, parting_surface: Any) -> List[Dict]: """ 检测倒扣区域 """ undercut_regions = [] try: # 获取分型面法向量 surface = BRepAdaptor_Surface(parting_surface) parting_normal = surface.Plane().Position().Direction() # 遍历所有面,检查是否存在倒扣 explorer = TopExp_Explorer(shape, TopAbs_FACE) while explorer.More(): face = TopoDS_Face(explorer.Current()) try: face_surface = BRepAdaptor_Surface(face) if face_surface.GetType() == 0: # Plane face_normal = face_surface.Plane().Position().Direction() # 计算与分型面法向量的夹角 dot = (face_normal.X() * parting_normal.X() + face_normal.Y() * parting_normal.Y() + face_normal.Z() * parting_normal.Z()) # 如果夹角大于90度,认为是倒扣面(法线方向与分型面相反) if dot < -0.7: # 约>135度 # 获取面的边界框中心 bbox = Bnd_Box() brepbndlib_Add(face, bbox) center = bbox.Center() # 检查该区域是否在分型面下方 if center.Z() < 0: # 简化判断 undercut_regions.append({ "type": "negative_draft", "location": [center.X(), center.Y(), center.Z()], "severity": abs(dot) }) except Exception as e: logger.debug(f"倒扣检测失败: {e}") explorer.Next() logger.info(f"检测到 {len(undercut_regions)} 个倒扣区域") except Exception as e: logger.warning(f"倒扣区域检测异常: {e}") return undercut_regions def _calculate_parting_line(self, shape: Any, parting_surface: Any) -> List[List[float]]: """计算真实的分型线""" try: section = BRepAlgoAPI_Section(shape, parting_surface) section.Build() if not section.IsDone(): logger.warning("截面运算未完成") return self._simple_parting_line(shape) # 提取交线点 edges = [] explorer = TopExp_Explorer(section.Shape(), TopAbs_EDGE) while explorer.More(): edge = TopoDS_Edge(explorer.Current()) try: curve = BRepAdaptor_Curve(edge) first_param = curve.FirstParameter() last_param = curve.LastParameter() # 采样点 num_points = max(10, int((last_param - first_param) / 0.5)) step = (last_param - first_param) / num_points for i in range(num_points + 1): param = first_param + i * step point = curve.Value(param) edges.append([point.X(), point.Y(), point.Z()]) except Exception: pass explorer.Next() if not edges: return self._simple_parting_line(shape) logger.info(f"计算得到 {len(edges)} 个分型线点") return edges except Exception as e: logger.error(f"分型线计算失败: {e}") return self._simple_parting_line(shape) def _simple_parting_line(self, shape: Any) -> List[List[float]]: """简化的分型线""" try: bbox = Bnd_Box() brepbndlib_Add(shape, bbox) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() center_z = (zmin + zmax) / 2 return [ [xmin, ymin, center_z], [xmax, ymin, center_z], [xmax, ymax, center_z], [xmin, ymax, center_z], [xmin, ymin, center_z] ] except Exception: logger.warning("分型线简化计算失败,返回空列表") return [] def _smooth_parting_line(self, parting_line: List[List[float]]) -> List[List[float]]: """ 分型线平滑处理 - 使用B样条拟合 """ if len(parting_line) < 4: return parting_line try: points = np.array(parting_line) # 简化的平滑算法:移动平均 smoothed = [] window_size = 3 for i in range(len(points)): start = max(0, i - window_size // 2) end = min(len(points), i + window_size // 2 + 1) window = points[start:end] if len(window) > 0: avg = np.mean(window, axis=0) smoothed.append(avg.tolist()) return smoothed except Exception as e: logger.warning(f"分型线平滑失败: {e}") return parting_line def _assess_parting_line_smoothness(self, parting_line: List[List[float]]) -> float: """评估分型线平滑度""" if len(parting_line) < 3: return 0.0 try: points = np.array(parting_line) # 计算相邻线段角度变化 angles = [] for i in range(1, len(points) - 1): v1 = points[i] - points[i-1] v2 = points[i+1] - points[i] len1 = np.linalg.norm(v1) len2 = np.linalg.norm(v2) if len1 > 0.001 and len2 > 0.001: cos_angle = np.dot(v1, v2) / (len1 * len2) cos_angle = max(-1, min(1, cos_angle)) angle = np.arccos(cos_angle) angles.append(np.degrees(angle)) if angles: avg_angle_change = np.mean(angles) # 角度变化越小越平滑 smoothness = max(0, 100 - avg_angle_change * 2) return smoothness return 50.0 except Exception: return 50.0 def _apply_shrinkage_compensation(self, shape: Any) -> Any: """应用收缩率补偿(铝泡沫版本)""" # 铝泡沫收缩率通常较大 scale_factor = 1.0 + self.shrinkage_rate trsf = gp_Trsf() trsf.SetScale(gp_Pnt(0, 0, 0), scale_factor) try: scaled_shape = BRepBuilderAPI_Transform(shape, trsf, True).Shape() logger.info(f"收缩率补偿应用: {self.shrinkage_rate*100:.2f}%") return scaled_shape except Exception as e: logger.error(f"收缩补偿失败: {e}") return shape def _apply_draft_angles(self, shape: Any, parting_surface: Any) -> Any: """应用拔模角(改进版)""" # 获取分型面法向量作为拔模方向 try: surface = BRepAdaptor_Surface(parting_surface) draft_direction = surface.Plane().Position().Direction() logger.info(f"应用拔模角: {self.draft_angle}°, 方向: ({draft_direction.X():.3f}, {draft_direction.Y():.3f}, {draft_direction.Z():.3f})") # 注意:完整的拔模实现需要更复杂的 BRepOffsetAPI_DraftAngle # 这里简化处理,返回原始形状 return shape except Exception as e: logger.warning(f"拔模角处理失败: {e}") return shape def _split_cavity_core(self, shape: Any, parting_surface: Any) -> Tuple[Any, Any]: """分离型腔和型芯(优化版)""" try: # 获取边界框 bbox = Bnd_Box() brepbndlib_Add(shape, bbox) xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get() # 计算模具块尺寸 margin = 25 # 铝泡沫模具需要更大余量 mold_xmin = xmin - margin mold_ymin = ymin - margin mold_zmin = zmin - margin mold_xmax = xmax + margin mold_ymax = ymax + margin mold_zmax = zmax + margin # 创建模具块 mold_block = BRepPrimAPI_MakeBox( gp_Pnt(mold_xmin, mold_ymin, mold_zmin), gp_Pnt(mold_xmax, mold_ymax, mold_zmax) ).Shape() # 型腔 = 模具块 - 产品 cavity_operation = BRepAlgoAPI_Cut(mold_block, shape) if cavity_operation.IsDone(): cavity = cavity_operation.Shape() logger.info("型腔生成成功") else: logger.warning("型腔布尔运算失败") cavity = mold_block # 型芯 = 产品形状 core = shape return cavity, core except Exception as e: logger.error(f"型腔分离失败: {e}") return shape, shape def _generate_mold_block(self, cavity: Any, analysis: Dict) -> Any: """生成完整的模具块(包含A/B板结构)""" try: bbox = analysis["bounding_box"] dims = bbox["dimensions"] # 模具总尺寸 margin = 30 length = dims[0] + 2 * margin width = dims[1] + 2 * margin height = dims[2] + margin + 80 # 增加模架高度 # 创建模具块 mold_block = BRepPrimAPI_MakeBox( gp_Pnt(-length/2, -width/2, -80), gp_Pnt(length/2, width/2, height) ).Shape() logger.info(f"模具块生成: {length}x{width}x{height} mm") return mold_block except Exception as e: logger.error(f"模具块生成失败: {e}") return cavity def _extract_shape_geometry(self, shape: Any, shape_type: str) -> Dict[str, Any]: """提取形状几何数据""" try: mesh = BRepMesh_IncrementalMesh(shape, 0.1) mesh.Perform() vertices = [] faces = [] vertex_index = 0 explorer = TopExp_Explorer(shape, TopAbs_FACE) while explorer.More(): face = TopoDS_Face(explorer.Current()) location = TopLoc_Location() triangulation = BRep_Tool.Triangulation(face, location) if triangulation: nb_nodes = triangulation.NbNodes() for i in range(1, nb_nodes + 1): node = triangulation.Node(i) transformed = node.Transformed(location.Transformation()) vertices.extend([ float(transformed.X()), float(transformed.Y()), float(transformed.Z()) ]) nb_triangles = triangulation.NbTriangles() for i in range(1, nb_triangles + 1): triangle = triangulation.Triangle(i) idx1 = triangle.Value(1) + vertex_index - 1 idx2 = triangle.Value(2) + vertex_index - 1 idx3 = triangle.Value(3) + vertex_index - 1 faces.extend([int(idx1), int(idx2), int(idx3)]) vertex_index += nb_nodes explorer.Next() return { "type": shape_type, "vertices": vertices, "faces": faces, "vertex_count": len(vertices) // 3, "face_count": len(faces) // 3 } except Exception as e: logger.error(f"{shape_type}几何提取失败: {e}") return { "type": shape_type, "vertices": [], "faces": [], "vertex_count": 0, "face_count": 0 } def _extract_parting_surface_geometry(self, surface: Any) -> Dict[str, Any]: """提取分型面几何数据""" try: adaptor = BRepAdaptor_Surface(surface) normal = adaptor.Plane().Position().Direction() return { "type": "plane", "normal": [normal.X(), normal.Y(), normal.Z()], "origin": [0, 0, 0], "bounds": {"u_range": [-200, 200], "v_range": [-200, 200]} } except Exception: return { "type": "plane", "normal": [0, 0, 1], "origin": [0, 0, 0], "bounds": {"u_range": [-200, 200], "v_range": [-200, 200]} } def _create_parting_surface_from_ai(self, ai_result: Dict, analysis: Dict) -> Dict: """从 AI 结果创建分型面""" origin = ai_result.get("origin", [0, 0, 0]) normal = ai_result.get("normal", [0, 0, 1]) parting_plane = gp_Pln( gp_Pnt(origin[0], origin[1], origin[2]), gp_Dir(normal[0], normal[1], normal[2]) ) try: parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face() except Exception: parting_plane = gp_Pln(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)) parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face() parting_line = self._calculate_parting_line( analysis.get("shape", None), parting_surface ) return { "primary_surface": parting_surface, "primary_line": parting_line, "primary_direction": normal, "confidence": ai_result.get("confidence", 0.8), "additional_surfaces": [], "surface_count": 1 } # ==================== 辅助方法 ==================== def _calculate_mold_size(self, analysis: Dict) -> Dict[str, float]: """估算模具尺寸""" dims = analysis["bounding_box"]["dimensions"] margin = 30 return { "length": dims[0] + 2 * margin, "width": dims[1] + 2 * margin, "height": dims[2] + margin + 80, "margin": margin } def _calculate_clamping_force(self, analysis: Dict) -> str: """估算锁模力""" volume_cm3 = analysis.get("volume", 0) / 1000 if volume_cm3 < 10: return "30-50 吨" elif volume_cm3 < 50: return "50-100 吨" elif volume_cm3 < 200: return "100-200 吨" else: return "200+ 吨" def _calculate_product_weight(self, analysis: Dict) -> str: """计算产品重量""" volume_cm3 = analysis.get("volume", 0) / 1000 weight_g = volume_cm3 * self.material_density return f"{weight_g:.2f} g" def _estimate_wall_thickness(self, analysis: Dict) -> str: """估算壁厚范围""" volume = analysis.get("volume", 0) surface_area = analysis.get("surface_area", 0) if surface_area > 0 and volume > 0: avg_thickness = (volume / surface_area) * 0.6 return f"{avg_thickness * 0.7:.2f} - {avg_thickness * 1.3:.2f} mm" return "无法估算 (缺少几何数据)" def _calculate_complexity_score(self, analysis: Dict) -> float: """计算复杂度评分""" volume = analysis.get("volume", 0) surface_area = analysis.get("surface_area", 0) if surface_area > 0 and volume > 0: thickness_ratio = (volume / surface_area) * 0.6 complexity = min(thickness_ratio / 5.0, 10.0) return round(complexity, 1) return 0.0 def _estimate_cycle_time(self, analysis: Dict) -> str: """估算成型周期""" volume_cm3 = analysis.get("volume", 0) / 1000 if volume_cm3 < 10: return "60-90 秒" elif volume_cm3 < 50: return "90-120 秒" elif volume_cm3 < 200: return "120-180 秒" else: return "180-300 秒" def _identify_sink_mark_risk(self, analysis: Dict) -> str: """识别缩痕风险""" return "中 - 铝泡沫壁厚大,需控制发泡均匀性" def _assess_warpage_risk(self, analysis: Dict) -> str: """评估翘曲风险""" bbox = analysis.get("bounding_box", {}).get("dimensions", [0, 0, 0]) aspect_ratio = max(bbox) / min(bbox) if aspect_ratio > 5: return "高 - 建议增加加强筋" elif aspect_ratio > 3: return "中 - 需优化冷却" else: return "低" def _assess_venting_requirement(self, analysis: Dict) -> str: """评估排气需求""" volume = analysis.get("volume", 0) if volume > 50000000: # > 50 cm³ return "高 - 需要加强排气系统" elif volume > 10000000: # > 10 cm³ return "中 - 建议标准排气" else: return "低 - 常规排气即可" def _get_inertia_matrix(self, props: GProp_GProps) -> List[List[float]]: """获取惯性矩阵""" inertia = props.MatrixOfInertia() return [ [inertia.Value(1, 1), inertia.Value(1, 2), inertia.Value(1, 3)], [inertia.Value(2, 1), inertia.Value(2, 2), inertia.Value(2, 3)], [inertia.Value(3, 1), inertia.Value(3, 2), inertia.Value(3, 3)] ] def _calculate_parting_line_length(self, parting_line: List) -> float: """计算分型线长度""" if not parting_line or len(parting_line) < 2: return 0.0 total_length = 0.0 for i in range(1, len(parting_line)): p1 = np.array(parting_line[i-1]) p2 = np.array(parting_line[i]) total_length += np.linalg.norm(p2 - p1) return total_length def set_ai_model(self, parting_detector: Any = None, draft_analyzer: Any = None): """设置 AI 模型接口""" self.ai_parting_detector = parting_detector self.ai_draft_analyzer = draft_analyzer logger.info("AI 模型接口已设置")