init
This commit is contained in:
+137
-570
@@ -1,48 +1,28 @@
|
||||
# src/core/mold_generator.py
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any, Tuple, Optional, Callable
|
||||
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.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.BRepBuilderAPI import BRepBuilderAPI_MakeFace
|
||||
from OCC.Core.gp import gp_Pln, gp_Dir, gp_Pnt
|
||||
from OCC.Core.TopoDS import TopoDS_Face
|
||||
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
|
||||
from OCC.Core.TopExp import TopExp_Explorer
|
||||
from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE
|
||||
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface, BRepAdaptor_Curve
|
||||
from OCC.Core.TopAbs import TopAbs_FACE
|
||||
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
|
||||
from core.base_mold_generator import BaseMoldGenerator
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class MoldCavityGenerator:
|
||||
class MoldCavityGenerator(BaseMoldGenerator):
|
||||
"""模具型腔生成器 - 基于产品模型生成Cavity和Core"""
|
||||
|
||||
def __init__(self, shrinkage_rate: float = 0.005, draft_angle: float = 2.0,
|
||||
material_density: float = 1.05):
|
||||
"""
|
||||
初始化模具生成器
|
||||
super().__init__(shrinkage_rate, draft_angle, material_density)
|
||||
|
||||
Args:
|
||||
shrinkage_rate: 收缩率(默认0.5% for ABS)
|
||||
draft_angle: 拔模角(默认2度)
|
||||
material_density: 材料密度 g/cm³(默认1.05 for ABS)
|
||||
"""
|
||||
self.shrinkage_rate = shrinkage_rate
|
||||
self.draft_angle = draft_angle # 度
|
||||
self.material_density = material_density # g/cm³
|
||||
|
||||
# 常用塑料材料密度(g/cm³)
|
||||
self.material_densities = {
|
||||
"ABS": 1.05,
|
||||
"PP": 0.90,
|
||||
@@ -54,25 +34,8 @@ class MoldCavityGenerator:
|
||||
"PMMA": 1.18
|
||||
}
|
||||
|
||||
# 分型面检测参数
|
||||
self.parting_line_tolerance = 0.1
|
||||
self.max_draft_angle = 5.0
|
||||
|
||||
# AI 模型接口(预留)
|
||||
self.ai_parting_detector: Optional[Any] = None
|
||||
self.ai_draft_analyzer: Optional[Any] = None
|
||||
|
||||
def set_ai_model(self, parting_detector: Any = None, draft_analyzer: Any = None):
|
||||
"""
|
||||
设置 AI 模型接口(预留)
|
||||
|
||||
Args:
|
||||
parting_detector: 分型面检测 AI 模型
|
||||
draft_analyzer: 拔模分析 AI 模型
|
||||
"""
|
||||
self.ai_parting_detector = parting_detector
|
||||
self.ai_draft_analyzer = draft_analyzer
|
||||
logger.info("AI 模型接口已设置")
|
||||
|
||||
def set_material(self, material: str):
|
||||
"""设置产品材料"""
|
||||
@@ -88,30 +51,25 @@ class MoldCavityGenerator:
|
||||
|
||||
Returns:
|
||||
{
|
||||
"cavity": cavity_shape, # 型腔(产品外部)
|
||||
"core": core_shape, # 型芯(产品内部)
|
||||
"parting_surface": parting_surface, # 分型面
|
||||
"parting_line": parting_line # 分型线
|
||||
"cavity": cavity_shape,
|
||||
"core": core_shape,
|
||||
"parting_surface": parting_surface,
|
||||
"parting_line": parting_line
|
||||
}
|
||||
"""
|
||||
logger.info("开始生成模具型腔...")
|
||||
|
||||
try:
|
||||
# Step 1: 分析产品几何
|
||||
analysis = self._analyze_product_geometry(product_shape)
|
||||
|
||||
# Step 2: 检测分型面和分型线
|
||||
parting_surface, parting_line = self._detect_parting_surface(
|
||||
product_shape, analysis
|
||||
)
|
||||
|
||||
# Step 3: 应用收缩率补偿
|
||||
scaled_shape = self._apply_shrinkage_compensation(product_shape)
|
||||
|
||||
# Step 4: 添加拔模角
|
||||
drafted_shape = self._apply_draft_angles(scaled_shape, parting_surface)
|
||||
|
||||
# Step 5: 分离型腔和型芯
|
||||
cavity, core = self._split_cavity_core(drafted_shape, parting_surface)
|
||||
|
||||
logger.info("模具型腔生成完成")
|
||||
@@ -140,11 +98,9 @@ class MoldCavityGenerator:
|
||||
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
|
||||
)
|
||||
@@ -158,10 +114,10 @@ class MoldCavityGenerator:
|
||||
"unit": "mm"
|
||||
},
|
||||
"product_analysis": {
|
||||
"bounding_box": analysis.get("bounding_box", {}), # 使用get方法
|
||||
"volume": analysis.get("volume", 0), # 使用get方法
|
||||
"surface_area": analysis.get("surface_area", 0), # 使用get方法
|
||||
"center_of_mass": analysis.get("center_of_mass", [0, 0, 0]) # 使用get方法
|
||||
"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,
|
||||
@@ -219,61 +175,24 @@ class MoldCavityGenerator:
|
||||
|
||||
# ==================== 内部方法 ====================
|
||||
|
||||
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)
|
||||
|
||||
# 计算边界框
|
||||
from OCC.Core.Bnd import Bnd_Box
|
||||
from OCC.Core.BRepBndLib import brepbndlib
|
||||
|
||||
bbox = Bnd_Box()
|
||||
brepbndlib.Add(shape, bbox)
|
||||
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
|
||||
|
||||
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]
|
||||
},
|
||||
"inertia_matrix": self._get_inertia_matrix(volume_props)
|
||||
}
|
||||
|
||||
def _detect_parting_surface(self, shape: Any, analysis: Dict) -> Tuple[Any, List]:
|
||||
"""
|
||||
检测分型面和分型线
|
||||
|
||||
|
||||
优先级:
|
||||
1. AI 模型检测(如果已设置)
|
||||
2. 基于法向量分析的几何方法
|
||||
3. 简化方法(基于边界框)
|
||||
"""
|
||||
# 1. 尝试使用 AI 模型
|
||||
if self.ai_parting_detector is not None:
|
||||
try:
|
||||
logger.info("使用 AI 模型检测分型面")
|
||||
ai_result = self.ai_parting_detector.detect(shape, analysis)
|
||||
if ai_result:
|
||||
return self._create_parting_surface_from_ai(ai_result, analysis)
|
||||
return self._create_parting_surface_from_ai(ai_result, analysis, shape)
|
||||
except Exception as e:
|
||||
logger.warning(f"AI 分型面检测失败,回退到几何方法:{e}")
|
||||
|
||||
# 2. 基于法向量分析的几何方法
|
||||
|
||||
try:
|
||||
logger.info("使用法向量分析检测分型面")
|
||||
optimal_direction = self._analyze_face_normals(shape)
|
||||
@@ -281,173 +200,142 @@ class MoldCavityGenerator:
|
||||
shape, analysis, optimal_direction
|
||||
)
|
||||
parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face()
|
||||
|
||||
# 计算真实分型线(产品与分型面的交线)
|
||||
|
||||
parting_line = self._calculate_parting_line(shape, parting_surface)
|
||||
|
||||
|
||||
return parting_surface, parting_line
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"法向量分析失败,使用简化方法:{e}")
|
||||
|
||||
# 3. 简化方法(回退)
|
||||
|
||||
logger.info("使用简化方法检测分型面")
|
||||
return self._simple_parting_surface(analysis)
|
||||
return self._simple_parting_surface(shape, analysis)
|
||||
|
||||
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)
|
||||
|
||||
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Transform
|
||||
scaled_shape = BRepBuilderAPI_Transform(shape, trsf, True).Shape()
|
||||
|
||||
return scaled_shape
|
||||
|
||||
def _apply_draft_angles(self, shape: Any, parting_surface: Any) -> Any:
|
||||
"""添加拔模角(简化实现)"""
|
||||
# 实际实现需要复杂的拔模面处理
|
||||
# 这里返回原始形状(假设已在CAD中处理)
|
||||
logger.warning("拔模角处理为简化实现,建议在设计阶段处理")
|
||||
return shape
|
||||
|
||||
def _split_cavity_core(self, shape: Any, parting_surface: Any) -> Tuple[Any, Any]:
|
||||
"""分离型腔和型芯
|
||||
|
||||
型腔(Cavity): 模具中形成产品外表面的部分,是产品形状的负形
|
||||
型芯(Core): 模具中形成产品内表面的部分,是产品形状的正形
|
||||
def _analyze_face_normals(self, shape: Any) -> gp_Dir:
|
||||
"""
|
||||
try:
|
||||
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
|
||||
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
|
||||
from OCC.Core.TopExp import TopExp_Explorer
|
||||
from OCC.Core.TopAbs import TopAbs_SOLID
|
||||
|
||||
# 获取产品边界框
|
||||
from OCC.Core.Bnd import Bnd_Box
|
||||
from OCC.Core.BRepBndLib import brepbndlib
|
||||
bbox = Bnd_Box()
|
||||
brepbndlib.Add(shape, bbox)
|
||||
xmin, ymin, zmin, xmax, ymax, zmax = bbox.Get()
|
||||
|
||||
# 计算模具块尺寸(比产品大一定余量)
|
||||
margin = 20 # mm
|
||||
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
|
||||
logger.info("型芯 = 产品形状")
|
||||
|
||||
return cavity, core
|
||||
分析产品表面的法向量分布,找出最优分型方向
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"型腔分离失败: {e}")
|
||||
return shape, shape
|
||||
原理:
|
||||
- 统计所有面的法向量
|
||||
- 选择法向量变化最小的方向作为分型方向
|
||||
- 避免倒扣(undercut)区域
|
||||
"""
|
||||
face_normals = []
|
||||
explorer = TopExp_Explorer(shape, TopAbs_FACE)
|
||||
|
||||
def _extract_shape_geometry(self, shape: Any, shape_type: str) -> Dict[str, Any]:
|
||||
"""提取形状几何数据为JSON格式"""
|
||||
try:
|
||||
# 网格化
|
||||
mesh = BRepMesh_IncrementalMesh(shape, 0.1)
|
||||
mesh.Perform()
|
||||
while explorer.More():
|
||||
face = TopoDS_Face(explorer.Current())
|
||||
surface = BRepAdaptor_Surface(face)
|
||||
|
||||
# 提取顶点和面
|
||||
from OCC.Core.TopExp import TopExp_Explorer
|
||||
from OCC.Core.TopAbs import TopAbs_FACE
|
||||
from OCC.Core.BRep import BRep_Tool
|
||||
from OCC.Core.Poly import Poly_Triangulation
|
||||
from OCC.Core.TopLoc import TopLoc_Location
|
||||
try:
|
||||
if surface.GetType() == 0:
|
||||
normal = surface.Plane().Position().Direction()
|
||||
else:
|
||||
bbox = Bnd_Box()
|
||||
brepbndlib_Add(face, bbox)
|
||||
normal = gp_Dir(0, 0, 1)
|
||||
|
||||
vertices = []
|
||||
faces = []
|
||||
face_normals.append(normal)
|
||||
except Exception as e:
|
||||
logger.debug(f"面法向量计算失败:{e}")
|
||||
|
||||
explorer = TopExp_Explorer(shape, TopAbs_FACE)
|
||||
vertex_index = 0
|
||||
explorer.Next()
|
||||
|
||||
while explorer.More():
|
||||
# 使用 explorer.Current() 直接获取面
|
||||
face = explorer.Current()
|
||||
location = TopLoc_Location()
|
||||
triangulation = BRep_Tool.Triangulation(face, location)
|
||||
if not face_normals:
|
||||
return gp_Dir(0, 0, 1)
|
||||
|
||||
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())
|
||||
])
|
||||
avg_x = sum(n.X() for n in face_normals) / len(face_normals)
|
||||
avg_y = sum(n.Y() for n in face_normals) / len(face_normals)
|
||||
avg_z = sum(n.Z() for n in face_normals) / len(face_normals)
|
||||
|
||||
# 提取三角形面
|
||||
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)])
|
||||
length = np.sqrt(avg_x**2 + avg_y**2 + avg_z**2)
|
||||
if length > 0.001:
|
||||
return gp_Dir(avg_x/length, avg_y/length, avg_z/length)
|
||||
else:
|
||||
return gp_Dir(0, 0, 1)
|
||||
|
||||
vertex_index += nb_nodes
|
||||
def _create_optimal_parting_plane(self, shape: Any, analysis: Dict,
|
||||
direction: gp_Dir) -> gp_Pln:
|
||||
"""
|
||||
创建最优分型面
|
||||
|
||||
explorer.Next()
|
||||
Args:
|
||||
shape: 产品形状
|
||||
analysis: 几何分析结果
|
||||
direction: 分型方向(法向量)
|
||||
|
||||
vertex_count = len(vertices) // 3
|
||||
face_count = len(faces) // 3
|
||||
Returns:
|
||||
gp_Pln: 分型面方程
|
||||
"""
|
||||
bbox = analysis["bounding_box"]
|
||||
center = bbox["center"]
|
||||
|
||||
return {
|
||||
"type": shape_type,
|
||||
"vertices": vertices,
|
||||
"faces": faces,
|
||||
"vertex_count": vertex_count,
|
||||
"face_count": face_count,
|
||||
"triangulation": "BRepMesh三角化"
|
||||
}
|
||||
parting_plane = gp_Pln(
|
||||
gp_Pnt(center[0], center[1], center[2]),
|
||||
direction
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{shape_type}几何提取失败: {e}")
|
||||
return {
|
||||
"type": shape_type,
|
||||
"vertices": [],
|
||||
"faces": [],
|
||||
"vertex_count": 0,
|
||||
"face_count": 0,
|
||||
"triangulation": f"提取失败: {str(e)}"
|
||||
}
|
||||
logger.info(f"创建分型面:原点=({center[0]:.2f}, {center[1]:.2f}, {center[2]:.2f}), "
|
||||
f"法向量=({direction.X():.3f}, {direction.Y():.3f}, {direction.Z():.3f})")
|
||||
|
||||
return parting_plane
|
||||
|
||||
def _simple_parting_surface(self, shape: Any, analysis: Dict) -> Tuple[Any, List]:
|
||||
"""简化的分型面检测(回退方案)"""
|
||||
bbox = analysis["bounding_box"]
|
||||
center_z = bbox["center"][2]
|
||||
|
||||
parting_plane = gp_Pln(
|
||||
gp_Pnt(0, 0, center_z),
|
||||
gp_Dir(0, 0, 1)
|
||||
)
|
||||
parting_surface = BRepBuilderAPI_MakeFace(
|
||||
parting_plane,
|
||||
bbox["min"][0] - 10, bbox["max"][0] + 10,
|
||||
bbox["min"][1] - 10, bbox["max"][1] + 10
|
||||
).Face()
|
||||
|
||||
parting_line = self._simple_parting_line(shape)
|
||||
|
||||
return parting_surface, parting_line
|
||||
|
||||
def _create_parting_surface_from_ai(self, ai_result: Dict,
|
||||
analysis: Dict, shape: Any = None) -> Tuple[Any, List]:
|
||||
"""
|
||||
从 AI 模型结果创建分型面(预留接口)
|
||||
|
||||
Args:
|
||||
ai_result: AI 模型输出,应包含:
|
||||
- origin: [x, y, z] 平面原点
|
||||
- normal: [nx, ny, nz] 法向量
|
||||
analysis: 几何分析结果
|
||||
shape: 产品形状(用于计算分型线)
|
||||
|
||||
Returns:
|
||||
(parting_surface, parting_line)
|
||||
"""
|
||||
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])
|
||||
)
|
||||
parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face()
|
||||
|
||||
if "parting_line" in ai_result:
|
||||
parting_line = ai_result["parting_line"]
|
||||
elif shape is not None:
|
||||
parting_line = self._calculate_parting_line(shape, parting_surface)
|
||||
else:
|
||||
parting_line = []
|
||||
|
||||
logger.info(f"从 AI 结果创建分型面:原点={origin}, 法向量={normal}")
|
||||
return parting_surface, parting_line
|
||||
|
||||
def _extract_parting_surface_geometry(self, surface: Any) -> Dict[str, Any]:
|
||||
"""提取分型面几何数据"""
|
||||
# 尝试从surface获取边界信息,失败则使用默认值
|
||||
try:
|
||||
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
|
||||
adaptor = BRepAdaptor_Surface(surface)
|
||||
u_min, u_max = adaptor.FirstUParameter(), adaptor.LastUParameter()
|
||||
v_min, v_max = adaptor.FirstVParameter(), adaptor.LastVParameter()
|
||||
@@ -463,14 +351,6 @@ class MoldCavityGenerator:
|
||||
"v_range": [-200, 200]
|
||||
}
|
||||
|
||||
# 分型面是水平面,法向量为 [0, 0, 1],原点在 Z 轴中心
|
||||
return {
|
||||
"type": "plane",
|
||||
"normal": [0, 0, 1],
|
||||
"origin": [0, 0, 0],
|
||||
"bounds": bounds
|
||||
}
|
||||
|
||||
return {
|
||||
"type": "plane",
|
||||
"normal": [0, 0, 1],
|
||||
@@ -481,23 +361,19 @@ class MoldCavityGenerator:
|
||||
def _calculate_mold_size(self, analysis: Dict) -> Dict[str, float]:
|
||||
"""估算模具尺寸"""
|
||||
product_bbox = analysis["bounding_box"]["dimensions"]
|
||||
|
||||
# 模具通常比产品大20-50mm
|
||||
margin = 30 # mm
|
||||
margin = 30
|
||||
|
||||
return {
|
||||
"length": product_bbox[0] + 2 * margin,
|
||||
"width": product_bbox[1] + 2 * margin,
|
||||
"height": product_bbox[2] + 2 * margin + 100, # 增加100mm用于模架
|
||||
"height": product_bbox[2] + 2 * margin + 100,
|
||||
"margin": margin
|
||||
}
|
||||
|
||||
def _calculate_clamping_force(self, analysis: Dict) -> str:
|
||||
"""估算锁模力"""
|
||||
volume_cm3 = analysis.get("volume", 0) / 1000 # mm³ → cm³
|
||||
volume_cm3 = analysis.get("volume", 0) / 1000
|
||||
|
||||
# 经验公式: 锁模力 ≈ 投影面积 × 压力 × 安全系数
|
||||
# 简化估算
|
||||
if volume_cm3 < 10:
|
||||
return "50-100 吨"
|
||||
elif volume_cm3 < 100:
|
||||
@@ -509,18 +385,8 @@ class MoldCavityGenerator:
|
||||
|
||||
def _get_recommended_material(self) -> str:
|
||||
"""推荐模具材料"""
|
||||
# 根据产品产量推荐模具材料
|
||||
# 小批量 (<5000件): 铝合金
|
||||
# 中批量 (5000-50000件): P20钢
|
||||
# 大批量 (>50000件): H13钢
|
||||
return "Aluminum Alloy 7075 (铝合金模具)"
|
||||
|
||||
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)
|
||||
@@ -530,7 +396,6 @@ class MoldCavityGenerator:
|
||||
avg_thickness = (volume / surface_area) * 0.6
|
||||
return f"{avg_thickness * 0.7:.2f} - {avg_thickness * 1.3:.2f} mm"
|
||||
elif volume > 0:
|
||||
# 如果没有surface_area,基于体积估算
|
||||
bbox_dims = analysis.get("bounding_box", {}).get("dimensions", [1, 1, 1])
|
||||
bbox_volume = bbox_dims[0] * bbox_dims[1] * bbox_dims[2]
|
||||
if bbox_volume > 0:
|
||||
@@ -542,7 +407,6 @@ class MoldCavityGenerator:
|
||||
|
||||
def _calculate_complexity_score(self, analysis: Dict) -> float:
|
||||
"""计算复杂度评分(0-10)"""
|
||||
# 基于体积、表面积比、边界框等
|
||||
volume = analysis.get("volume", 0)
|
||||
surface_area = analysis.get("surface_area", 0)
|
||||
|
||||
@@ -551,7 +415,6 @@ class MoldCavityGenerator:
|
||||
complexity = min(thickness_ratio / 5.0, 10.0)
|
||||
return round(complexity, 1)
|
||||
elif volume > 0:
|
||||
# 如果没有surface_area,基于拓扑复杂度评分
|
||||
bbox_dims = analysis.get("bounding_box", {}).get("dimensions", [100, 100, 100])
|
||||
bbox_volume = bbox_dims[0] * bbox_dims[1] * bbox_dims[2]
|
||||
if bbox_volume > 0:
|
||||
@@ -576,7 +439,6 @@ class MoldCavityGenerator:
|
||||
|
||||
def _identify_weld_line_risk(self, analysis: Dict) -> str:
|
||||
"""识别熔接痕风险"""
|
||||
# 基于几何复杂度判断
|
||||
complexity = self._calculate_complexity_score(analysis)
|
||||
|
||||
if complexity > 7:
|
||||
@@ -588,299 +450,4 @@ class MoldCavityGenerator:
|
||||
|
||||
def _identify_sink_mark_risk(self, analysis: Dict) -> str:
|
||||
"""识别缩痕风险"""
|
||||
thickness = self._estimate_wall_thickness(analysis)
|
||||
# 简化的风险评估
|
||||
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 _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 _analyze_face_normals(self, shape: Any) -> gp_Dir:
|
||||
"""
|
||||
分析产品表面的法向量分布,找出最优分型方向
|
||||
|
||||
原理:
|
||||
- 统计所有面的法向量
|
||||
- 选择法向量变化最小的方向作为分型方向
|
||||
- 避免倒扣(undercut)区域
|
||||
"""
|
||||
from OCC.Core.TopoDS import TopoDS_Compound
|
||||
from OCC.Core.TopTools import TopTools_IndexedMapOfShape
|
||||
|
||||
# 收集所有面的法向量
|
||||
face_normals = []
|
||||
explorer = TopExp_Explorer(shape, TopAbs_FACE)
|
||||
|
||||
while explorer.More():
|
||||
face = TopoDS_Face(explorer.Current())
|
||||
surface = BRepAdaptor_Surface(face)
|
||||
|
||||
# 获取面的法向量(在参数中心点)
|
||||
try:
|
||||
u = (surface.FirstUParameter() + surface.LastUParameter()) / 2
|
||||
v = (surface.FirstVParameter() + surface.LastVParameter()) / 2
|
||||
|
||||
normal = gp_Dir()
|
||||
# 从曲面获取法向量
|
||||
if surface.GetType() == 0: # Plane
|
||||
normal = surface.Plane().Position().Direction()
|
||||
else:
|
||||
# 对于非平面,使用微分几何计算法向量
|
||||
from OCC.Core.GCPnts import GCPnts_AbscissaPoint
|
||||
from OCC.Core.BRepGProp import brepgprop_VolumeProperties
|
||||
|
||||
# 简化:使用面的边界框中心法向量
|
||||
from OCC.Core.Bnd import Bnd_Box
|
||||
from OCC.Core.BRepBndLib import brepbndlib_Add
|
||||
bbox = Bnd_Box()
|
||||
brepbndlib_Add(face, bbox)
|
||||
center = bbox.Center()
|
||||
|
||||
# 估算面法向量(简化)
|
||||
normal = gp_Dir(0, 0, 1) # 默认 Z 方向
|
||||
|
||||
face_normals.append(normal)
|
||||
except Exception as e:
|
||||
logger.debug(f"面法向量计算失败:{e}")
|
||||
|
||||
explorer.Next()
|
||||
|
||||
# 如果没有法向量,返回默认 Z 方向
|
||||
if not face_normals:
|
||||
return gp_Dir(0, 0, 1)
|
||||
|
||||
# 统计法向量分布,选择最优方向
|
||||
# 简化实现:计算平均法向量
|
||||
avg_x = sum(n.X() for n in face_normals) / len(face_normals)
|
||||
avg_y = sum(n.Y() for n in face_normals) / len(face_normals)
|
||||
avg_z = sum(n.Z() for n in face_normals) / len(face_normals)
|
||||
|
||||
# 归一化
|
||||
length = np.sqrt(avg_x**2 + avg_y**2 + avg_z**2)
|
||||
if length > 0.001:
|
||||
return gp_Dir(avg_x/length, avg_y/length, avg_z/length)
|
||||
else:
|
||||
return gp_Dir(0, 0, 1)
|
||||
|
||||
def _create_optimal_parting_plane(self, shape: Any, analysis: Dict,
|
||||
direction: gp_Dir) -> gp_Pln:
|
||||
"""
|
||||
创建最优分型面
|
||||
|
||||
Args:
|
||||
shape: 产品形状
|
||||
analysis: 几何分析结果
|
||||
direction: 分型方向(法向量)
|
||||
|
||||
Returns:
|
||||
gp_Pln: 分型面方程
|
||||
"""
|
||||
bbox = analysis["bounding_box"]
|
||||
|
||||
# 分型面通过产品的质心
|
||||
center = bbox["center"]
|
||||
|
||||
# 创建平面:通过质心,法向量为分型方向
|
||||
parting_plane = gp_Pln(
|
||||
gp_Pnt(center[0], center[1], center[2]),
|
||||
direction
|
||||
)
|
||||
|
||||
logger.info(f"创建分型面:原点=({center[0]:.2f}, {center[1]:.2f}, {center[2]:.2f}), "
|
||||
f"法向量=({direction.X():.3f}, {direction.Y():.3f}, {direction.Z():.3f})")
|
||||
|
||||
return parting_plane
|
||||
|
||||
def _calculate_parting_line(self, shape: Any, parting_surface: Any) -> List[List[float]]:
|
||||
"""
|
||||
计算真实的分型线(产品与分型面的交线)
|
||||
|
||||
使用 BRepAlgoAPI_Section 进行布尔运算求交
|
||||
"""
|
||||
try:
|
||||
# 创建截面运算
|
||||
section = BRepAlgoAPI_Section(shape, parting_surface)
|
||||
section.Build()
|
||||
|
||||
if not section.IsDone():
|
||||
logger.warning("截面运算未完成,使用简化分型线")
|
||||
return self._simple_parting_line(
|
||||
parting_surface,
|
||||
{"bounding_box": {"min": [-50, -50, 0], "max": [50, 50, 100]}}
|
||||
)
|
||||
|
||||
# 提取交线(边)
|
||||
edges = []
|
||||
explorer = TopExp_Explorer(section.Shape(), TopAbs_EDGE)
|
||||
|
||||
while explorer.More():
|
||||
edge = TopoDS_Edge(explorer.Current())
|
||||
|
||||
# 从边提取点
|
||||
curve = BRepAdaptor_Curve(edge)
|
||||
first_param = curve.FirstParameter()
|
||||
last_param = curve.LastParameter()
|
||||
|
||||
# 采样点(至少 10 个点)
|
||||
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()])
|
||||
|
||||
explorer.Next()
|
||||
|
||||
# 如果没有边,使用简化分型线
|
||||
if not edges:
|
||||
logger.warning("未找到交线,使用简化分型线")
|
||||
return self._simple_parting_line(
|
||||
parting_surface,
|
||||
{"bounding_box": {"min": [-50, -50, 0], "max": [50, 50, 100]}}
|
||||
)
|
||||
|
||||
logger.info(f"计算得到 {len(edges)} 个分型线点")
|
||||
return edges
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"分型线计算失败:{e}")
|
||||
return self._simple_parting_line(
|
||||
parting_surface,
|
||||
{"bounding_box": {"min": [-50, -50, 0], "max": [50, 50, 100]}}
|
||||
)
|
||||
|
||||
def _simple_parting_surface(self, analysis: Dict) -> Tuple[Any, List]:
|
||||
"""简化的分型面检测(回退方案)"""
|
||||
bbox = analysis["bounding_box"]
|
||||
center_z = bbox["center"][2]
|
||||
|
||||
# 创建分型面(XY 平面)
|
||||
parting_plane = gp_Pln(
|
||||
gp_Pnt(0, 0, center_z),
|
||||
gp_Dir(0, 0, 1)
|
||||
)
|
||||
parting_surface = BRepBuilderAPI_MakeFace(
|
||||
parting_plane,
|
||||
bbox["min"][0] - 10, bbox["max"][0] + 10,
|
||||
bbox["min"][1] - 10, bbox["max"][1] + 10
|
||||
).Face()
|
||||
|
||||
# 简化分型线
|
||||
parting_line = self._simple_parting_line(parting_surface, analysis)
|
||||
|
||||
return parting_surface, parting_line
|
||||
|
||||
def _simple_parting_line(self, parting_surface: Any, analysis: Dict) -> List[List[float]]:
|
||||
"""简化的分型线(矩形)"""
|
||||
bbox = analysis["bounding_box"]
|
||||
center_z = bbox["center"][2]
|
||||
|
||||
return [
|
||||
[bbox["min"][0], bbox["min"][1], center_z],
|
||||
[bbox["max"][0], bbox["min"][1], center_z],
|
||||
[bbox["max"][0], bbox["max"][1], center_z],
|
||||
[bbox["min"][0], bbox["max"][1], center_z],
|
||||
[bbox["min"][0], bbox["min"][1], center_z]
|
||||
]
|
||||
|
||||
def _create_parting_surface_from_ai(self, ai_result: Dict,
|
||||
analysis: Dict) -> Tuple[Any, List]:
|
||||
"""
|
||||
从 AI 模型结果创建分型面(预留接口)
|
||||
|
||||
Args:
|
||||
ai_result: AI 模型输出,应包含:
|
||||
- origin: [x, y, z] 平面原点
|
||||
- normal: [nx, ny, nz] 法向量
|
||||
analysis: 几何分析结果
|
||||
|
||||
Returns:
|
||||
(parting_surface, parting_line)
|
||||
"""
|
||||
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])
|
||||
)
|
||||
parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face()
|
||||
|
||||
# 分型线可以使用 AI 结果或重新计算
|
||||
if "parting_line" in ai_result:
|
||||
parting_line = ai_result["parting_line"]
|
||||
else:
|
||||
parting_line = self._simple_parting_line(parting_surface, analysis)
|
||||
|
||||
logger.info(f"从 AI 结果创建分型面:原点={origin}, 法向量={normal}")
|
||||
return parting_surface, parting_line
|
||||
|
||||
def _apply_draft_angles(self, shape: Any, parting_surface: Any) -> Any:
|
||||
"""
|
||||
添加拔模角
|
||||
|
||||
使用 OpenCASCADE 的拔模功能
|
||||
"""
|
||||
# 1. 尝试使用 AI 模型
|
||||
if self.ai_draft_analyzer is not None:
|
||||
try:
|
||||
logger.info("使用 AI 模型分析拔模角")
|
||||
ai_result = self.ai_draft_analyzer.analyze(shape, parting_surface, self.draft_angle)
|
||||
if ai_result and "drafted_shape" in ai_result:
|
||||
logger.info("AI 拔模分析成功")
|
||||
return ai_result["drafted_shape"]
|
||||
except Exception as e:
|
||||
logger.warning(f"AI 拔模分析失败,回退到几何方法:{e}")
|
||||
|
||||
# 2. 几何方法(简化实现)
|
||||
try:
|
||||
# 获取分型面的法向量作为拔模方向
|
||||
surface_adaptor = BRepAdaptor_Surface(parting_surface)
|
||||
draft_direction = surface_adaptor.Plane().Position().Direction()
|
||||
|
||||
# 使用 BRepOffsetAPI_ThickSolid 创建拔模
|
||||
# 注意:完整的拔模需要更复杂的实现,这里简化处理
|
||||
logger.info(f"使用几何方法添加拔模角:{self.draft_angle}度,方向=({draft_direction.X():.3f}, {draft_direction.Y():.3f}, {draft_direction.Z():.3f})")
|
||||
|
||||
# 简化:直接返回原始形状(拔模已在 CAD 中处理)
|
||||
# 完整实现需要使用 BRepOffsetAPI_DraftAngle
|
||||
return shape
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"拔模角处理失败:{e}")
|
||||
return shape
|
||||
|
||||
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])
|
||||
segment_length = np.linalg.norm(p2 - p1)
|
||||
total_length += segment_length
|
||||
|
||||
return total_length
|
||||
|
||||
Reference in New Issue
Block a user