1014 lines
39 KiB
Python
1014 lines
39 KiB
Python
|
|
"""
|
|||
|
|
增强版铝制家电包装泡沫模具分模算法
|
|||
|
|
|
|||
|
|
本模块实现了针对铝泡沫模具的优化分模算法,包括:
|
|||
|
|
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:
|
|||
|
|
# 回退到默认平面
|
|||
|
|
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:
|
|||
|
|
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:
|
|||
|
|
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:
|
|||
|
|
return [[0, 0, 0], [100, 0, 0], [100, 100, 0], [0, 100, 0], [0, 0, 0]]
|
|||
|
|
|
|||
|
|
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:
|
|||
|
|
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:
|
|||
|
|
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:
|
|||
|
|
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 "10.0 - 30.0 mm (铝泡沫典型)"
|
|||
|
|
|
|||
|
|
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 5.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", [1, 1, 1])
|
|||
|
|
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 模型接口已设置")
|