Files
geMoldInsight/src/core/aluminum_foam_mold.py
T
2026-04-13 09:43:24 +08:00

1015 lines
39 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.
"""
增强版铝制家电包装泡沫模具分模算法
本模块实现了针对铝泡沫模具的优化分模算法,包括:
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 模型接口已设置")