init
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Core 模块
|
||||
@@ -0,0 +1,340 @@
|
||||
# core/geometry_analyzer.py
|
||||
from typing import Dict, List, Any
|
||||
|
||||
# import features # 暂时注释掉,避免导入错误
|
||||
import numpy as np
|
||||
from models.schemas import (
|
||||
create_mold_feature,
|
||||
create_design_recommendation,
|
||||
create_analysis_result
|
||||
)
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class GeometryAnalyzer:
|
||||
"""几何分析器 - 简化版"""
|
||||
|
||||
def __init__(self):
|
||||
self.feature_thresholds = {
|
||||
"thin_wall": 2.0,
|
||||
"thick_wall": 8.0,
|
||||
"small_feature": 5.0,
|
||||
"large_feature": 1000.0,
|
||||
"high_complexity": 50,
|
||||
}
|
||||
|
||||
self.product_materials = {
|
||||
"ABS": {"shrinkage": 0.005, "min_wall": 1.2},
|
||||
"PP": {"shrinkage": 0.016, "min_wall": 1.0},
|
||||
"PC": {"shrinkage": 0.007, "min_wall": 1.5},
|
||||
}
|
||||
|
||||
self.mold_materials = {
|
||||
"Aluminum": {"thermal_conductivity": 200, "hardness": "HB80", "cost": "low"},
|
||||
"P20_Steel": {"thermal_conductivity": 30, "hardness": "HRC30", "cost": "medium"},
|
||||
"H13_Steel": {"thermal_conductivity": 25, "hardness": "HRC48", "cost": "high"}
|
||||
}
|
||||
|
||||
def analyze_mold_design(self, geometry_data: Dict[str, Any],
|
||||
product_material: str = "ABS",
|
||||
mold_material: str = "Aluminum"
|
||||
) -> Dict[str, Any]:
|
||||
"""分析模具设计"""
|
||||
logger.info("开始模具设计分析")
|
||||
|
||||
# 检测特征
|
||||
features = self._detect_features(geometry_data)
|
||||
|
||||
# 使用产品材料属性
|
||||
product_props = self.product_materials.get(product_material, {})
|
||||
shrinkage = product_props.get("shrinkage", 0.005)
|
||||
|
||||
# 使用模具材料属性
|
||||
mold_props = self.mold_materials.get(mold_material, {})
|
||||
thermal_cond = mold_props.get("thermal_conductivity", 200)
|
||||
|
||||
# 生成设计建议
|
||||
recommendations = self._generate_recommendations(
|
||||
geometry_data, features, product_material
|
||||
)
|
||||
|
||||
# 计算质量指标
|
||||
quality_metrics = self._calculate_quality_metrics(geometry_data, features)
|
||||
|
||||
# 生成分析摘要
|
||||
analysis_summary = self._generate_analysis_summary(geometry_data, features, recommendations)
|
||||
|
||||
return create_analysis_result(
|
||||
geometry_data=geometry_data,
|
||||
detected_features=features,
|
||||
design_recommendations=recommendations,
|
||||
quality_metrics=quality_metrics,
|
||||
analysis_summary=analysis_summary
|
||||
)
|
||||
|
||||
def _detect_features(self, geometry_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""检测模具特征"""
|
||||
features = []
|
||||
|
||||
# 壁厚分析
|
||||
wall_features = self._detect_wall_features(geometry_data)
|
||||
features.extend(wall_features)
|
||||
|
||||
# 加强筋检测
|
||||
rib_features = self._detect_rib_features(geometry_data)
|
||||
features.extend(rib_features)
|
||||
|
||||
# BOSS柱检测
|
||||
boss_features = self._detect_boss_features(geometry_data)
|
||||
features.extend(boss_features)
|
||||
|
||||
# 拔模角度分析
|
||||
draft_features = self._analyze_draft_angles(geometry_data)
|
||||
features.extend(draft_features)
|
||||
|
||||
logger.info(f"检测到 {len(features)} 个特征")
|
||||
return features
|
||||
|
||||
def _detect_wall_features(self, geometry_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""检测壁厚特征"""
|
||||
features = []
|
||||
volume = geometry_data.get("volume", 0)
|
||||
surface_area = geometry_data.get("surface_area", 0)
|
||||
|
||||
if volume > 0 and surface_area > 0:
|
||||
avg_thickness = (volume / surface_area) * 0.6
|
||||
|
||||
if avg_thickness < self.feature_thresholds["thin_wall"]:
|
||||
features.append(create_mold_feature(
|
||||
feature_type="thin_wall",
|
||||
confidence=0.85,
|
||||
location=geometry_data.get("center_of_mass", [0, 0, 0]),
|
||||
dimensions=[avg_thickness, avg_thickness, avg_thickness],
|
||||
parameters={"average_thickness": avg_thickness},
|
||||
recommendations=[
|
||||
f"平均壁厚 {avg_thickness:.2f}mm 过薄,建议增加到 {self.feature_thresholds['thin_wall']}mm 以上",
|
||||
"考虑增加加强筋以提高结构强度",
|
||||
"检查注塑填充是否充分"
|
||||
]
|
||||
))
|
||||
elif avg_thickness > self.feature_thresholds["thick_wall"]:
|
||||
features.append(create_mold_feature(
|
||||
feature_type="thick_wall",
|
||||
confidence=0.75,
|
||||
location=geometry_data.get("center_of_mass", [0, 0, 0]),
|
||||
dimensions=[avg_thickness, avg_thickness, avg_thickness],
|
||||
parameters={"average_thickness": avg_thickness},
|
||||
recommendations=[
|
||||
f"平均壁厚 {avg_thickness:.2f}mm 过厚,可能产生缩痕",
|
||||
"考虑减薄壁厚或增加加强筋",
|
||||
"优化冷却系统设计"
|
||||
]
|
||||
))
|
||||
elif volume > 0:
|
||||
# 如果没有surface_area,基于边界框估算壁厚
|
||||
bbox = geometry_data.get("bounding_box", {})
|
||||
dimensions = bbox.get("dimensions", [100, 100, 100])
|
||||
bbox_volume = dimensions[0] * dimensions[1] * dimensions[2]
|
||||
if bbox_volume > 0:
|
||||
volume_efficiency = volume / bbox_volume
|
||||
avg_thickness = (dimensions[0] + dimensions[1]) / 2 * volume_efficiency
|
||||
if avg_thickness < self.feature_thresholds["thin_wall"]:
|
||||
features.append(create_mold_feature(
|
||||
feature_type="thin_wall",
|
||||
confidence=0.7,
|
||||
location=bbox.get("center", [50, 50, 50]),
|
||||
dimensions=[avg_thickness, avg_thickness, avg_thickness],
|
||||
parameters={"average_thickness": avg_thickness, "estimation_method": "bbox_based"},
|
||||
recommendations=[
|
||||
f"估算平均壁厚 {avg_thickness:.2f}mm 过薄,建议检查表面积数据",
|
||||
"考虑增加加强筋以提高结构强度"
|
||||
]
|
||||
))
|
||||
|
||||
return features
|
||||
|
||||
def _detect_rib_features(self, geometry_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""检测加强筋特征"""
|
||||
features = []
|
||||
topology = geometry_data.get("topology", {})
|
||||
|
||||
face_count = topology.get("faces", 0)
|
||||
edge_count = topology.get("edges", 0)
|
||||
complexity_ratio = edge_count / max(face_count, 1)
|
||||
|
||||
if complexity_ratio > 3.0:
|
||||
features.append(create_mold_feature(
|
||||
feature_type="rib_structure",
|
||||
confidence=0.7,
|
||||
location=geometry_data.get("center_of_mass", [0, 0, 0]),
|
||||
dimensions=[2.0, 8.0, 2.0],
|
||||
parameters={"complexity_ratio": complexity_ratio},
|
||||
recommendations=[
|
||||
"检测到可能的加强筋结构",
|
||||
"建议加强筋厚度为壁厚的50-80%",
|
||||
"加强筋高度不超过壁厚的3倍",
|
||||
"加强筋根部增加圆角避免应力集中"
|
||||
]
|
||||
))
|
||||
|
||||
return features
|
||||
|
||||
def _detect_boss_features(self, geometry_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""检测BOSS柱特征"""
|
||||
features = []
|
||||
volume = geometry_data.get("volume", 0)
|
||||
bbox = geometry_data.get("bounding_box", {})
|
||||
|
||||
dimensions = bbox.get("dimensions", [100, 100, 100])
|
||||
volume_efficiency = volume / (dimensions[0] * dimensions[1] * dimensions[2])
|
||||
|
||||
if volume_efficiency < 0.3:
|
||||
features.append(create_mold_feature(
|
||||
feature_type="boss_feature",
|
||||
confidence=0.65,
|
||||
location=bbox.get("center", [50, 50, 50]),
|
||||
dimensions=[6.0, 12.0, 6.0],
|
||||
parameters={"volume_efficiency": volume_efficiency},
|
||||
recommendations=[
|
||||
"检测到可能的BOSS柱结构",
|
||||
"建议BOSS柱外径为螺钉直径的2-2.5倍",
|
||||
"BOSS柱高度不超过直径的2倍",
|
||||
"增加拔模角度1-2度",
|
||||
"根部增加圆角R0.5-R1.0"
|
||||
]
|
||||
))
|
||||
|
||||
return features
|
||||
|
||||
def _analyze_draft_angles(self, geometry_data: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
"""分析拔模角度"""
|
||||
features = []
|
||||
|
||||
features.append(create_mold_feature(
|
||||
feature_type="draft_angle",
|
||||
confidence=0.8,
|
||||
location=geometry_data.get("center_of_mass", [0, 0, 0]),
|
||||
dimensions=[1.0, 2.0, 1.0],
|
||||
parameters={"recommended_angle": 2.0},
|
||||
recommendations=[
|
||||
"建议所有垂直面添加1-2度拔模角度",
|
||||
"纹理表面需要3-5度拔模角度",
|
||||
"深腔结构需要更大的拔模角度"
|
||||
]
|
||||
))
|
||||
|
||||
return features
|
||||
|
||||
def _generate_recommendations(self, geometry_data: Dict[str, Any],
|
||||
features: List[Dict[str, Any]],
|
||||
material: str) -> List[Dict[str, Any]]:
|
||||
"""生成设计建议"""
|
||||
recommendations = []
|
||||
|
||||
# 壁厚建议
|
||||
wall_rec = self._get_wall_thickness_recommendation(geometry_data, material)
|
||||
if wall_rec:
|
||||
recommendations.append(wall_rec)
|
||||
|
||||
# 拔模角度建议
|
||||
recommendations.append(create_design_recommendation(
|
||||
rec_type="draft_angle",
|
||||
priority="high",
|
||||
description="添加拔模角度",
|
||||
parameters={"min_angle": 1.0, "preferred_angle": 2.0},
|
||||
reason="确保顺利脱模"
|
||||
))
|
||||
|
||||
# 基于检测到的特征生成建议
|
||||
for feature in features:
|
||||
if feature["feature_type"] == "thin_wall":
|
||||
rec = create_design_recommendation(
|
||||
rec_type="wall_thickness",
|
||||
priority="high",
|
||||
description="增加壁厚",
|
||||
parameters={
|
||||
"current": feature["parameters"]["average_thickness"],
|
||||
"recommended": self.feature_thresholds["thin_wall"]
|
||||
},
|
||||
reason="壁厚不足影响结构强度"
|
||||
)
|
||||
recommendations.append(rec)
|
||||
|
||||
return recommendations
|
||||
|
||||
def _get_wall_thickness_recommendation(self, geometry_data: Dict[str, Any],
|
||||
material: str) -> Dict[str, Any]:
|
||||
"""获取壁厚建议"""
|
||||
volume = geometry_data.get("volume", 0)
|
||||
surface_area = geometry_data.get("surface_area", 0)
|
||||
|
||||
if volume > 0 and surface_area > 0:
|
||||
avg_thickness = (volume / surface_area) * 0.6
|
||||
material_props = self.product_materials.get(material, self.product_materials["ABS"])
|
||||
min_wall = material_props["min_wall"]
|
||||
|
||||
if avg_thickness < min_wall:
|
||||
return create_design_recommendation(
|
||||
rec_type="wall_thickness",
|
||||
priority="high",
|
||||
description=f"增加壁厚至{min_wall}mm以上",
|
||||
parameters={"current": avg_thickness, "recommended": min_wall},
|
||||
reason=f"{material}材料最小壁厚要求"
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def _calculate_quality_metrics(self, geometry_data: Dict[str, Any],
|
||||
features: List[Dict[str, Any]]) -> Dict[str, float]:
|
||||
"""计算质量指标"""
|
||||
metrics = {}
|
||||
|
||||
# 体积利用率
|
||||
bbox = geometry_data.get("bounding_box", {})
|
||||
dimensions = bbox.get("dimensions", [100, 100, 100])
|
||||
volume = geometry_data.get("volume", 0)
|
||||
bbox_volume = dimensions[0] * dimensions[1] * dimensions[2]
|
||||
|
||||
metrics["volume_utilization"] = volume / bbox_volume if bbox_volume > 0 else 0
|
||||
|
||||
# 拓扑复杂度
|
||||
topology = geometry_data.get("topology", {})
|
||||
face_count = topology.get("faces", 0)
|
||||
metrics["topology_complexity"] = face_count / 100.0
|
||||
|
||||
# 壁厚均匀性评分
|
||||
surface_area = geometry_data.get("surface_area", 0)
|
||||
if volume > 0 and surface_area > 0:
|
||||
thickness_ratio = (volume / surface_area) * 0.6
|
||||
ideal_thickness = 3.0
|
||||
metrics["wall_uniformity"] = 1.0 - abs(thickness_ratio - ideal_thickness) / ideal_thickness
|
||||
elif volume > 0 and bbox_volume > 0:
|
||||
# 如果没有surface_area,基于体积利用率估算
|
||||
metrics["wall_uniformity"] = max(0.5, metrics["volume_utilization"])
|
||||
else:
|
||||
metrics["wall_uniformity"] = 0.5
|
||||
|
||||
return metrics
|
||||
|
||||
def _generate_analysis_summary(self, geometry_data: Dict[str, Any],
|
||||
features: List[Dict[str, Any]],
|
||||
recommendations: List[Dict[str, Any]]) -> str:
|
||||
"""生成分析摘要"""
|
||||
volume = geometry_data.get("volume", 0)
|
||||
high_priority_recs = len([r for r in recommendations if r["priority"] == "high"])
|
||||
|
||||
summary_parts = []
|
||||
|
||||
if volume > 0:
|
||||
summary_parts.append(f"模型体积: {volume / 1000:.1f} cm³")
|
||||
|
||||
if features:
|
||||
feature_types = set(f["feature_type"] for f in features)
|
||||
summary_parts.append(f"检测到 {len(feature_types)} 类特征")
|
||||
|
||||
if high_priority_recs > 0:
|
||||
summary_parts.append(f"有 {high_priority_recs} 个高优先级建议")
|
||||
|
||||
return " | ".join(summary_parts) if summary_parts else "分析完成"
|
||||
@@ -0,0 +1,102 @@
|
||||
# src/core/mesh_generator.py
|
||||
import logging
|
||||
import numpy as np
|
||||
from typing import Dict, List, Optional
|
||||
import pyvista as pv
|
||||
import trimesh
|
||||
from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MeshGenerator:
|
||||
"""网格生成器 - 使用PyVista和Trimesh"""
|
||||
|
||||
def __init__(self, quality: str = "medium"):
|
||||
self.quality_settings = {
|
||||
"low": 0.5,
|
||||
"medium": 0.1,
|
||||
"high": 0.01
|
||||
}
|
||||
self.quality = self.quality_settings.get(quality, 0.1)
|
||||
|
||||
def generate_mesh_from_shape(self, shape, num_points: int = 10000) -> Dict:
|
||||
"""从形状生成网格数据"""
|
||||
try:
|
||||
# 方法1: 使用PythonOCC生成网格
|
||||
occ_mesh = self._generate_occ_mesh(shape)
|
||||
|
||||
# 方法2: 转换为PyVista网格
|
||||
pv_mesh = self._convert_to_pyvista(occ_mesh)
|
||||
|
||||
# 方法3: 转换为Trimesh网格
|
||||
tri_mesh = self._convert_to_trimesh(pv_mesh)
|
||||
|
||||
# 生成点云
|
||||
pointcloud = self._generate_pointcloud(tri_mesh, num_points)
|
||||
|
||||
return {
|
||||
"pyvista_mesh": pv_mesh,
|
||||
"trimesh_mesh": tri_mesh,
|
||||
"pointcloud": pointcloud
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"网格生成失败: {e}")
|
||||
raise
|
||||
|
||||
def _generate_occ_mesh(self, shape) -> any:
|
||||
"""使用PythonOCC生成网格"""
|
||||
mesh = BRepMesh_IncrementalMesh(shape, self.quality)
|
||||
mesh.Perform()
|
||||
return mesh
|
||||
|
||||
def _convert_to_pyvista(self, occ_mesh) -> pv.PolyData:
|
||||
"""转换为PyVista网格"""
|
||||
# 这里需要从OCC网格中提取顶点和面数据
|
||||
# 简化实现 - 实际需要遍历OCC网格数据结构
|
||||
try:
|
||||
# 创建示例网格数据
|
||||
cube = pv.Cube()
|
||||
return cube
|
||||
except Exception as e:
|
||||
logger.warning(f"PyVista转换失败,使用备用方法: {e}")
|
||||
return self._create_sample_mesh()
|
||||
|
||||
def _convert_to_trimesh(self, pv_mesh) -> trimesh.Trimesh:
|
||||
"""转换为Trimesh网格"""
|
||||
try:
|
||||
# 从PyVista转换
|
||||
vertices = pv_mesh.points
|
||||
faces = pv_mesh.faces.reshape(-1, 4)[:, 1:4] # 假设三角形网格
|
||||
|
||||
return trimesh.Trimesh(vertices=vertices, faces=faces)
|
||||
except Exception as e:
|
||||
logger.warning(f"Trimesh转换失败: {e}")
|
||||
return self._create_sample_trimesh()
|
||||
|
||||
def _generate_pointcloud(self, mesh: trimesh.Trimesh, num_points: int) -> Dict:
|
||||
"""从网格生成点云"""
|
||||
try:
|
||||
# 均匀采样点云
|
||||
points, face_indices = trimesh.sample.sample_surface(mesh, num_points)
|
||||
|
||||
# 计算法向量
|
||||
normals = mesh.face_normals[face_indices]
|
||||
|
||||
return {
|
||||
"points": points.tolist(),
|
||||
"normals": normals.tolist(),
|
||||
"count": len(points)
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"点云生成失败: {e}")
|
||||
raise
|
||||
|
||||
def _create_sample_mesh(self) -> pv.PolyData:
|
||||
"""创建示例网格(备用)"""
|
||||
return pv.Cube()
|
||||
|
||||
def _create_sample_trimesh(self) -> trimesh.Trimesh:
|
||||
"""创建示例Trimesh(备用)"""
|
||||
return trimesh.creation.box([100, 80, 50])
|
||||
@@ -0,0 +1,523 @@
|
||||
# src/core/mold_generator.py
|
||||
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
|
||||
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_Transform
|
||||
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
|
||||
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 models.schemas import create_mold_cavity_data, create_mold_key_info
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class MoldCavityGenerator:
|
||||
"""模具型腔生成器 - 基于产品模型生成Cavity和Core"""
|
||||
|
||||
def __init__(self, shrinkage_rate: float = 0.005, draft_angle: float = 2.0):
|
||||
"""
|
||||
初始化模具生成器
|
||||
|
||||
Args:
|
||||
shrinkage_rate: 收缩率(默认0.5% for ABS)
|
||||
draft_angle: 拔模角(默认2度)
|
||||
"""
|
||||
self.shrinkage_rate = shrinkage_rate
|
||||
self.draft_angle = draft_angle # 度
|
||||
|
||||
# 分型面检测参数
|
||||
self.parting_line_tolerance = 0.1
|
||||
self.max_draft_angle = 5.0
|
||||
|
||||
def generate_mold_cavities(self, product_shape: Any) -> Dict[str, Any]:
|
||||
"""
|
||||
从产品的3D模型生成型腔和型芯
|
||||
|
||||
Returns:
|
||||
{
|
||||
"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("模具型腔生成完成")
|
||||
|
||||
return {
|
||||
"cavity": cavity,
|
||||
"core": core,
|
||||
"parting_surface": parting_surface,
|
||||
"parting_line": parting_line,
|
||||
"analysis": analysis
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"模具型腔生成失败: {e}")
|
||||
raise
|
||||
|
||||
def generate_detailed_cavity_json(self, cavity_data: Dict) -> Dict[str, Any]:
|
||||
"""
|
||||
生成详细的型腔三维JSON数据
|
||||
|
||||
Returns:
|
||||
包含完整几何信息的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
|
||||
)
|
||||
|
||||
detailed_json = {
|
||||
"metadata": {
|
||||
"version": "2.0",
|
||||
"generated_at": str(np.datetime64('now')),
|
||||
"shrinkage_rate": self.shrinkage_rate,
|
||||
"draft_angle": self.draft_angle,
|
||||
"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方法
|
||||
},
|
||||
"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": self._get_recommended_material()
|
||||
}
|
||||
}
|
||||
|
||||
return detailed_json
|
||||
|
||||
def generate_cavity_key_info(self, cavity_data: Dict) -> Dict[str, Any]:
|
||||
"""
|
||||
生成模具型腔的关键信息
|
||||
|
||||
Returns:
|
||||
关键参数摘要
|
||||
"""
|
||||
analysis = cavity_data["analysis"]
|
||||
|
||||
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["parting_line"]
|
||||
),
|
||||
"cavity_depth": analysis.get("bounding_box", {}).get("dimensions", [0, 0, 0])[2]
|
||||
},
|
||||
"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": "80-120 MPa"
|
||||
},
|
||||
"quality_considerations": {
|
||||
"potential_weld_lines": self._identify_weld_line_risk(analysis),
|
||||
"sink_mark_areas": self._identify_sink_mark_risk(analysis),
|
||||
"warpage_risk": self._assess_warpage_risk(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)
|
||||
|
||||
# 计算边界框
|
||||
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]:
|
||||
"""检测分型面和分型线"""
|
||||
# 简化的分型面检测:基于Z方向的最高点和最低点
|
||||
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 = [
|
||||
[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]
|
||||
]
|
||||
|
||||
return parting_surface, parting_line
|
||||
|
||||
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]:
|
||||
"""分离型腔和型芯"""
|
||||
try:
|
||||
# 使用分型面切割产品
|
||||
# 上半部分为型腔(Cavity)
|
||||
# 下半部分为型芯(Core)
|
||||
|
||||
# 这里需要实现BRepAlgoAPI_Section或类似的切割操作
|
||||
# 简化:返回相同的形状(实际需实现切割逻辑)
|
||||
|
||||
return shape, shape # (cavity, core)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"型腔分离失败: {e}")
|
||||
return shape, shape
|
||||
|
||||
def _extract_shape_geometry(self, shape: Any, shape_type: str) -> Dict[str, Any]:
|
||||
"""提取形状几何数据为JSON格式"""
|
||||
try:
|
||||
# 网格化
|
||||
mesh = BRepMesh_IncrementalMesh(shape, 0.1)
|
||||
mesh.Perform()
|
||||
|
||||
# 提取顶点和面
|
||||
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
|
||||
|
||||
vertices = []
|
||||
faces = []
|
||||
|
||||
explorer = TopExp_Explorer(shape, TopAbs_FACE)
|
||||
vertex_index = 0
|
||||
|
||||
while explorer.More():
|
||||
# 使用 explorer.Current() 直接获取面
|
||||
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()
|
||||
|
||||
vertex_count = len(vertices) // 3
|
||||
face_count = len(faces) // 3
|
||||
|
||||
return {
|
||||
"type": shape_type,
|
||||
"vertices": vertices,
|
||||
"faces": faces,
|
||||
"vertex_count": vertex_count,
|
||||
"face_count": face_count,
|
||||
"triangulation": "BRepMesh三角化"
|
||||
}
|
||||
|
||||
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)}"
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
bounds = {
|
||||
"u_range": [float(u_min), float(u_max)],
|
||||
"v_range": [float(v_min), float(v_max)]
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"分型面边界提取失败,使用默认值: {e}")
|
||||
bounds = {
|
||||
"u_range": [-200, 200],
|
||||
"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],
|
||||
"origin": [0, 0, 0],
|
||||
"bounds": bounds
|
||||
}
|
||||
|
||||
def _calculate_mold_size(self, analysis: Dict) -> Dict[str, float]:
|
||||
"""估算模具尺寸"""
|
||||
product_bbox = analysis["bounding_box"]["dimensions"]
|
||||
|
||||
# 模具通常比产品大20-50mm
|
||||
margin = 30 # mm
|
||||
|
||||
return {
|
||||
"length": product_bbox[0] + 2 * margin,
|
||||
"width": product_bbox[1] + 2 * margin,
|
||||
"height": product_bbox[2] + 2 * margin + 100, # 增加100mm用于模架
|
||||
"margin": margin
|
||||
}
|
||||
|
||||
def _calculate_clamping_force(self, analysis: Dict) -> str:
|
||||
"""估算锁模力"""
|
||||
volume_cm3 = analysis.get("volume", 0) / 1000 # mm³ → cm³
|
||||
|
||||
# 经验公式: 锁模力 ≈ 投影面积 × 压力 × 安全系数
|
||||
# 简化估算
|
||||
if volume_cm3 < 10:
|
||||
return "50-100 吨"
|
||||
elif volume_cm3 < 100:
|
||||
return "150-300 吨"
|
||||
elif volume_cm3 < 500:
|
||||
return "400-600 吨"
|
||||
else:
|
||||
return "800+ 吨"
|
||||
|
||||
def _get_recommended_material(self) -> str:
|
||||
"""推荐模具材料 - 铝模具专用"""
|
||||
return "Aluminum Alloy 7075 (铝合金模具)"
|
||||
|
||||
def _calculate_product_weight(self, analysis: Dict) -> str:
|
||||
"""计算产品重量(泡沫材料,密度约0.1 g/cm³)"""
|
||||
volume_cm3 = analysis.get("volume", 0) / 1000
|
||||
weight_g = volume_cm3 * 0.1 # EPP泡沫密度约0.1 g/cm³
|
||||
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"
|
||||
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:
|
||||
efficiency = volume / bbox_volume
|
||||
avg_thickness = (bbox_dims[0] + bbox_dims[1]) / 2 * efficiency
|
||||
return f"{avg_thickness * 0.7:.2f} - {avg_thickness * 1.3:.2f} mm"
|
||||
|
||||
return "2.0 - 4.0 mm (默认)"
|
||||
|
||||
def _calculate_complexity_score(self, analysis: Dict) -> float:
|
||||
"""计算复杂度评分(0-10)"""
|
||||
# 基于体积、表面积比、边界框等
|
||||
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)
|
||||
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:
|
||||
volume_ratio = volume / bbox_volume
|
||||
complexity = (1.0 - volume_ratio) * 10
|
||||
return round(min(max(complexity, 0), 10), 1)
|
||||
|
||||
return 5.0
|
||||
|
||||
def _estimate_cycle_time(self, analysis: Dict) -> str:
|
||||
"""估算成型周期"""
|
||||
volume_cm3 = analysis.get("volume", 0) / 1000
|
||||
|
||||
if volume_cm3 < 10:
|
||||
return "15-25 秒"
|
||||
elif volume_cm3 < 50:
|
||||
return "25-40 秒"
|
||||
elif volume_cm3 < 200:
|
||||
return "40-60 秒"
|
||||
else:
|
||||
return "60-90 秒"
|
||||
|
||||
def _identify_weld_line_risk(self, analysis: Dict) -> str:
|
||||
"""识别熔接痕风险"""
|
||||
# 基于几何复杂度判断
|
||||
complexity = self._calculate_complexity_score(analysis)
|
||||
|
||||
if complexity > 7:
|
||||
return "高 - 建议优化浇口位置"
|
||||
elif complexity > 4:
|
||||
return "中 - 需仿真验证"
|
||||
else:
|
||||
return "低"
|
||||
|
||||
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 _calculate_parting_line_length(self, parting_line: List) -> float:
|
||||
"""计算分型线长度"""
|
||||
# 简化的长度计算
|
||||
return 250.0 # mm
|
||||
@@ -0,0 +1,292 @@
|
||||
# core/stp_parser.py
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional, List
|
||||
import numpy as np
|
||||
import json
|
||||
from utils.logger import get_logger
|
||||
from OCC.Core.GProp import GProp_GProps
|
||||
from OCC.Core.BRepGProp import brepgprop
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class STPParser:
|
||||
"""STP文件解析器"""
|
||||
|
||||
def __init__(self):
|
||||
# 强制要求PythonOCC必须可用
|
||||
self._verify_occ_availability()
|
||||
|
||||
def _verify_occ_availability(self):
|
||||
"""验证PythonOCC是否可用,不可用则抛出异常"""
|
||||
try:
|
||||
from OCC.Core.STEPControl import STEPControl_Reader
|
||||
from OCC.Core.IFSelect import IFSelect_RetDone
|
||||
logger.info("PythonOCC验证通过")
|
||||
except ImportError as e:
|
||||
logger.error("PythonOCC不可用,服务无法运行")
|
||||
raise RuntimeError("PythonOCC未安装,请安装PythonOCC后再运行服务") from e
|
||||
|
||||
|
||||
|
||||
def load_step_file(self, file_path: Path) -> Any:
|
||||
"""加载STP文件"""
|
||||
try:
|
||||
from OCC.Core.STEPControl import STEPControl_Reader
|
||||
from OCC.Core.IFSelect import IFSelect_RetDone
|
||||
|
||||
logger.info(f"加载STP文件: {file_path}")
|
||||
reader = STEPControl_Reader()
|
||||
status = reader.ReadFile(str(file_path))
|
||||
|
||||
if status == IFSelect_RetDone:
|
||||
reader.TransferRoots()
|
||||
shape = reader.OneShape()
|
||||
logger.info("STP文件加载成功")
|
||||
return shape
|
||||
else:
|
||||
raise ValueError(f"STP文件读取失败,状态码: {status}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"STP解析失败: {e}")
|
||||
raise
|
||||
|
||||
def analyze_geometry(self, shape) -> Dict[str, Any]:
|
||||
"""分析几何属性"""
|
||||
|
||||
try:
|
||||
from OCC.Core.GProp import GProp_GProps
|
||||
from OCC.Core.BRepGProp import brepgprop
|
||||
from OCC.Core.Bnd import Bnd_Box
|
||||
from OCC.Core.BRepBndLib import brepbndlib
|
||||
from OCC.Core.TopExp import TopExp_Explorer
|
||||
from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX
|
||||
|
||||
logger.info("开始几何分析...")
|
||||
|
||||
# 计算边界框
|
||||
bbox = self._compute_bounding_box(shape)
|
||||
|
||||
# 计算体积和表面积
|
||||
volume = self._compute_volume(shape)
|
||||
area = self._compute_surface_area(shape)
|
||||
|
||||
# 分析拓扑
|
||||
topology = self._analyze_topology(shape)
|
||||
|
||||
# 计算质心
|
||||
center_of_mass = self._compute_center_of_mass(shape)
|
||||
|
||||
# 计算惯性属性
|
||||
inertia_properties = self._compute_inertia_properties(shape)
|
||||
|
||||
result = {
|
||||
"bounding_box": bbox,
|
||||
"volume": float(volume),
|
||||
"surface_area": float(area),
|
||||
"topology": topology,
|
||||
"center_of_mass": center_of_mass,
|
||||
"inertia_properties": inertia_properties,
|
||||
"analysis_method": "pythonocc"
|
||||
}
|
||||
|
||||
logger.info("几何分析完成")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"几何分析失败: {e}")
|
||||
raise
|
||||
|
||||
def _compute_bounding_box(self, shape) -> Dict[str, Any]:
|
||||
"""计算边界框"""
|
||||
try:
|
||||
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 {
|
||||
"min": [float(xmin), float(ymin), float(zmin)],
|
||||
"max": [float(xmax), float(ymax), float(zmax)],
|
||||
"dimensions": [
|
||||
float(xmax - xmin),
|
||||
float(ymax - ymin),
|
||||
float(zmax - zmin)
|
||||
],
|
||||
"center": [
|
||||
float((xmin + xmax) / 2),
|
||||
float((ymin + ymax) / 2),
|
||||
float((zmin + zmax) / 2)
|
||||
]
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"边界框计算失败: {e}")
|
||||
return self._default_bounding_box()
|
||||
|
||||
def _compute_volume(self, shape) -> float:
|
||||
"""计算体积"""
|
||||
try:
|
||||
from OCC.Core.GProp import GProp_GProps
|
||||
from OCC.Core.BRepGProp import brepgprop
|
||||
|
||||
props = GProp_GProps()
|
||||
brepgprop.VolumeProperties(shape, props)
|
||||
return props.Mass()
|
||||
except Exception as e:
|
||||
logger.error(f"体积计算失败: {e}")
|
||||
return 1000000.0
|
||||
|
||||
def _compute_surface_area(self, shape) -> float:
|
||||
"""计算表面积"""
|
||||
try:
|
||||
from OCC.Core.GProp import GProp_GProps
|
||||
from OCC.Core.BRepGProp import brepgprop
|
||||
|
||||
props = GProp_GProps()
|
||||
brepgprop.SurfaceProperties(shape, props)
|
||||
area = props.Mass()
|
||||
logger.info(f"表面积计算成功: {area:.2f} mm²")
|
||||
|
||||
# 如果计算结果为0,使用备选估算方法
|
||||
if area <= 0:
|
||||
logger.warning("表面积计算结果为0,使用边界框估算")
|
||||
raise ValueError("Surface area is zero")
|
||||
|
||||
return area
|
||||
except Exception as e:
|
||||
logger.error(f"表面积计算失败: {e}")
|
||||
# 基于边界框估算表面积
|
||||
try:
|
||||
bbox = self._compute_bounding_box(shape)
|
||||
dims = bbox.get("dimensions", [100, 100, 100])
|
||||
# 简化的估算公式:2*(lw + lh + wh)
|
||||
estimated_area = 2 * (dims[0]*dims[1] + dims[0]*dims[2] + dims[1]*dims[2])
|
||||
logger.warning(f"使用边界框估算表面积: {estimated_area:.2f} mm²")
|
||||
return estimated_area
|
||||
except:
|
||||
return 60000.0
|
||||
|
||||
def _compute_center_of_mass(self, shape) -> List[float]:
|
||||
"""计算质心"""
|
||||
try:
|
||||
from OCC.Core.GProp import GProp_GProps
|
||||
from OCC.Core.BRepGProp import brepgprop
|
||||
|
||||
props = GProp_GProps()
|
||||
brepgprop.VolumeProperties(shape, props)
|
||||
center = props.CentreOfMass()
|
||||
return [float(center.X()), float(center.Y()), float(center.Z())]
|
||||
except Exception as e:
|
||||
logger.error(f"质心计算失败: {e}")
|
||||
return [0.0, 0.0, 0.0]
|
||||
|
||||
def _compute_inertia_properties(self, shape) -> Dict[str, Any]:
|
||||
"""计算惯性属性"""
|
||||
try:
|
||||
from OCC.Core.GProp import GProp_GProps
|
||||
from OCC.Core.BRepGProp import brepgprop
|
||||
|
||||
props = GProp_GProps()
|
||||
brepgprop.VolumeProperties(shape, props)
|
||||
|
||||
inertia = props.MatrixOfInertia()
|
||||
return {
|
||||
"mass": float(props.Mass()),
|
||||
"moment_of_inertia": [
|
||||
[float(inertia.Value(1, 1)), float(inertia.Value(1, 2)), float(inertia.Value(1, 3))],
|
||||
[float(inertia.Value(2, 1)), float(inertia.Value(2, 2)), float(inertia.Value(2, 3))],
|
||||
[float(inertia.Value(3, 1)), float(inertia.Value(3, 2)), float(inertia.Value(3, 3))]
|
||||
]
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"惯性属性计算失败: {e}")
|
||||
return {}
|
||||
|
||||
def _analyze_topology(self, shape) -> Dict[str, int]:
|
||||
"""分析拓扑"""
|
||||
try:
|
||||
from OCC.Core.TopExp import TopExp_Explorer
|
||||
from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX
|
||||
|
||||
def count_elements(element_type):
|
||||
explorer = TopExp_Explorer(shape, element_type)
|
||||
count = 0
|
||||
while explorer.More():
|
||||
count += 1
|
||||
explorer.Next()
|
||||
return count
|
||||
|
||||
return {
|
||||
"faces": count_elements(TopAbs_FACE),
|
||||
"edges": count_elements(TopAbs_EDGE),
|
||||
"vertices": count_elements(TopAbs_VERTEX)
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"拓扑分析失败: {e}")
|
||||
raise
|
||||
|
||||
def _create_dummy_shape(self):
|
||||
"""创建虚拟形状"""
|
||||
return "dummy_shape"
|
||||
|
||||
def _simulate_analysis(self) -> Dict[str, Any]:
|
||||
"""模拟分析结果"""
|
||||
logger.info("使用模拟分析数据")
|
||||
return {
|
||||
"bounding_box": self._default_bounding_box(),
|
||||
"volume": 1000000.0,
|
||||
"surface_area": 60000.0,
|
||||
"topology": {"faces": 6, "edges": 12, "vertices": 8},
|
||||
"center_of_mass": [50.0, 50.0, 50.0],
|
||||
"inertia_properties": {},
|
||||
"analysis_method": "simulated"
|
||||
}
|
||||
|
||||
def _default_bounding_box(self) -> Dict[str, Any]:
|
||||
"""默认边界框"""
|
||||
return {
|
||||
"min": [0.0, 0.0, 0.0],
|
||||
"max": [100.0, 100.0, 100.0],
|
||||
"dimensions": [100.0, 100.0, 100.0],
|
||||
"center": [50.0, 50.0, 50.0]
|
||||
}
|
||||
|
||||
def export_to_json(self, geometry_data: Dict[str, Any], output_path: Path) -> str:
|
||||
"""将几何数据导出为JSON文件"""
|
||||
try:
|
||||
# 确保输出目录存在
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 添加元数据
|
||||
json_data = {
|
||||
"metadata": {
|
||||
"export_time": str(np.datetime64('now')),
|
||||
"analysis_method": geometry_data.get("analysis_method", "unknown"),
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"geometry_data": geometry_data
|
||||
}
|
||||
|
||||
# 保存JSON文件
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(json_data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
logger.info(f"几何数据已导出到: {output_path}")
|
||||
return str(output_path)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"JSON导出失败: {e}")
|
||||
raise
|
||||
|
||||
def get_json_data(self, geometry_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""获取JSON格式的几何数据"""
|
||||
return {
|
||||
"metadata": {
|
||||
"export_time": str(np.datetime64('now')),
|
||||
"analysis_method": geometry_data.get("analysis_method", "unknown"),
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"geometry_data": geometry_data
|
||||
}
|
||||
Reference in New Issue
Block a user