文档目录结构简洁化

This commit is contained in:
2026-09-01 18:05:18 +08:00
parent bee439cf34
commit 5e531ffe1e
47 changed files with 1334 additions and 3380 deletions
+24 -20
View File
@@ -12,6 +12,7 @@
"""
from typing import Dict, List, Any, Tuple, Optional
import warnings
import numpy as np
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
@@ -55,13 +56,11 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
self.foam_material = foam_material
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
self.cavity_match_rate = 95.0
self.side_action_designer = SideActionDesigner()
def set_foam_material(self, material: str):
@@ -89,7 +88,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
def generate_mold_cavities(self, product_shape: TopoDS_Shape) -> Dict[str, Any]:
"""
从产品的3D模型生成型腔和型芯
[已废弃] 单方案分模入口。生产路径请使用 MultiSchemeMoldPlanner.generate_plan。
完整流程:
1. 分析产品几何
@@ -100,10 +99,15 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
6. 分离型腔和型芯
7. 生成模具块
"""
warnings.warn(
"generate_mold_cavities 已废弃,请改用 MultiSchemeMoldPlanner.generate_plan 生成多方案分模结果",
DeprecationWarning,
stacklevel=2,
)
logger.info(f"开始生成铝泡沫模具型腔 (材料: {self.foam_material})...")
try:
analysis = self._analyze_product_geometry(product_shape)
analysis = self.analyze_product_geometry(product_shape)
parting_result = self._detect_parting_surfaces(product_shape, analysis)
primary_parting_surface = parting_result["primary_surface"]
@@ -113,20 +117,20 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
side_action_result = self.side_action_designer.analyze_and_design(
shape=product_shape,
parting_direction=primary_parting_direction,
mold_size=self._calculate_mold_size(analysis),
mold_size=self.calculate_mold_size(analysis),
parting_surface=primary_parting_surface,
)
undercut_regions = self._build_undercut_regions(
undercut_regions = self.build_undercut_regions(
side_action_result.get("undercut_analysis", {})
)
scaled_shape = self._apply_shrinkage_compensation(product_shape)
scaled_shape = self.apply_shrinkage_compensation(product_shape)
drafted_shape = self._apply_draft_angles(scaled_shape, primary_parting_surface)
drafted_shape = self.apply_draft_angles(scaled_shape, primary_parting_surface)
cavity, core = self._split_cavity_core(drafted_shape, primary_parting_surface)
cavity, core = self.split_cavity_core(drafted_shape, primary_parting_surface)
mold_block = self._generate_mold_block(cavity, analysis)
mold_block = self.generate_mold_block(cavity, analysis)
smoothed_parting_line = self._smooth_parting_line(primary_parting_line)
@@ -187,7 +191,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
},
"parting_surface": parting_geometry,
"manufacturing_info": {
"estimated_mold_size": self._calculate_mold_size(analysis),
"estimated_mold_size": self.calculate_mold_size(analysis),
"estimated_clamping_force": self._calculate_clamping_force(analysis),
"clamping_force_formula": "投影面积(cm²) × 0.3 (泡沫材料系数)",
"recommended_material": material_info.get("description", "Aluminum Foam Mold"),
@@ -251,9 +255,9 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
# ==================== 核心算法实现 ====================
def _analyze_product_geometry(self, shape: TopoDS_Shape) -> Dict[str, Any]:
def analyze_product_geometry(self, shape: TopoDS_Shape) -> Dict[str, Any]:
"""分析产品几何属性(扩展基类版本,增加法向量统计)"""
result = super()._analyze_product_geometry(shape)
result = super().analyze_product_geometry(shape)
result["normal_statistics"] = self._analyze_parting_direction(shape)
return result
@@ -266,7 +270,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
face = topods.Face(explorer.Current())
explorer.Next()
try:
normal = self._get_face_normal(face)
normal = self.get_face_normal(face)
if normal is None:
continue
props = GProp_GProps()
@@ -287,9 +291,9 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
for axis, value in stats.items()
}
def _split_cavity_core(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face) -> Tuple[TopoDS_Shape, TopoDS_Shape]:
def split_cavity_core(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face) -> Tuple[TopoDS_Shape, TopoDS_Shape]:
"""分离型腔和型芯(铝泡沫使用更大余量)"""
return super()._split_cavity_core(shape, parting_surface, margin=25)
return super().split_cavity_core(shape, parting_surface, margin=25)
def _detect_parting_surfaces(self, shape: TopoDS_Shape, analysis: Dict) -> Dict[str, Any]:
"""
@@ -317,7 +321,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
logger.info(f"泡沫模具 Z 轴分型面: Z={parting_z:.2f} mm (包围盒中心)")
parting_line = self.optimize_parting_line(
self._calculate_parting_line(shape, parting_surface)
self.calculate_parting_line(shape, parting_surface)
)
additional_surfaces = []
@@ -352,7 +356,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
"parting_position_z": parting_z,
}
def _build_undercut_regions(self, undercut_analysis: Dict[str, Any]) -> List[Dict[str, Any]]:
def build_undercut_regions(self, undercut_analysis: Dict[str, Any]) -> List[Dict[str, Any]]:
"""将侧向机构分析结果转换为兼容旧结构的倒扣区域列表。"""
undercut_faces = undercut_analysis.get("undercut_faces", [])
regions = []
@@ -429,7 +433,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
except Exception:
return 50.0
def _generate_mold_block(self, cavity: TopoDS_Shape, analysis: Dict) -> TopoDS_Shape:
def generate_mold_block(self, cavity: TopoDS_Shape, analysis: Dict) -> TopoDS_Shape:
"""生成完整的模具块(包含A/B板结构)"""
try:
bbox = analysis["bounding_box"]
@@ -464,7 +468,7 @@ class AluminumFoamMoldGenerator(BaseMoldGenerator):
# ==================== 辅助方法 ====================
def _calculate_mold_size(self, analysis: Dict) -> Dict[str, float]:
def calculate_mold_size(self, analysis: Dict) -> Dict[str, float]:
"""估算模具尺寸"""
dims = analysis["bounding_box"]["dimensions"]
margin = 30
+59 -14
View File
@@ -25,15 +25,60 @@ logger = get_logger(__name__)
class BaseMoldGenerator:
"""模具生成器基类 - 提供共用方法"""
"""模具生成器基类 - 提供共用方法
公共接口契约(MultiSchemeMoldPlanner 及上层服务依赖,子类覆写时必须保持签名稳定):
- analyze_product_geometry(shape) -> 几何分析结果 dict
- calculate_parting_line(shape, parting_surface) -> 分型线点列
- calculate_mold_size(analysis) -> 模具尺寸估算 dict
- build_undercut_regions(undercut_analysis) -> 倒扣区域列表
- apply_shrinkage_compensation(shape) / apply_draft_angles(shape, parting_surface) -> 形状
- split_cavity_core(shape, parting_surface) -> (cavity, core)
- create_mold_block(analysis, margin) -> 模具包围盒形状
- split_mold_block_by_plane(mold_block, parting_plane) -> (a_plate, b_plate)
- get_parting_plane(parting_surface, shape) -> gp_Pln
- get_face_normal(face) -> gp_Dir
- apply_process_params(material, process_params) -> None
- generate_mold_block(cavity, analysis):仅泡沫类生成器实现
- set_material / generate_detailed_cavity_json / generate_cavity_key_info / side_action_designer
generate_mold_cavities 为已废弃的单方案入口,生产路径走 MultiSchemeMoldPlanner.generate_plan。
"""
def __init__(self, shrinkage_rate: float = 0.005, draft_angle: float = 2.0,
material_density: float = 1.05):
self.shrinkage_rate = shrinkage_rate
self.draft_angle = draft_angle
self.material_density = material_density
self.parting_line_tolerance = 0.1
self.cavity_match_rate = 95.0
def _apply_shrinkage_compensation(self, shape: TopoDS_Shape) -> TopoDS_Shape:
def apply_process_params(
self,
material: Dict[str, Any],
process_params: Optional[Dict[str, Any]] = None,
) -> None:
"""按用户工艺参数覆盖生成器参数,未指定的项取材料默认值或当前值。
material 为 MaterialService.get_material() 返回的材料属性 dict;
process_params 支持 draft_angle / shrinkage_rate(百分数) / parting_precision / cavity_match。
"""
params = process_params or {}
draft_angle = float(params.get("draft_angle", getattr(self, "draft_angle", 2.0)))
shrinkage_rate = float(params.get("shrinkage_rate", material.get("shrinkage", 0.005) * 100.0)) / 100.0
parting_precision = float(params.get("parting_precision", getattr(self, "parting_line_tolerance", 0.1)))
cavity_match = float(params.get("cavity_match", getattr(self, "cavity_match_rate", 95.0)))
self.draft_angle = draft_angle
self.shrinkage_rate = shrinkage_rate
self.parting_line_tolerance = parting_precision
self.cavity_match_rate = cavity_match
def generate_mold_block(self, cavity: TopoDS_Shape, analysis: Dict) -> TopoDS_Shape:
"""生成完整模具块(A/B板结构)。仅泡沫类生成器实现,基类不提供默认。"""
raise NotImplementedError("generate_mold_block 仅由泡沫模具生成器实现")
def apply_shrinkage_compensation(self, shape: TopoDS_Shape) -> TopoDS_Shape:
scale_factor = 1.0 + self.shrinkage_rate
trsf = gp_Trsf()
trsf.SetScale(gp_Pnt(0, 0, 0), scale_factor)
@@ -45,7 +90,7 @@ class BaseMoldGenerator:
logger.warning(f"收缩率补偿失败: {e}")
return shape
def _apply_draft_angles(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face) -> TopoDS_Shape:
def apply_draft_angles(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face) -> TopoDS_Shape:
try:
draft_direction = self._get_draft_direction(parting_surface)
if draft_direction is None:
@@ -78,7 +123,7 @@ class BaseMoldGenerator:
return gp_Dir(0, 0, 1)
@staticmethod
def _create_mold_block(analysis: Dict, margin: float = 30.0) -> TopoDS_Shape:
def create_mold_block(analysis: Dict, margin: float = 30.0) -> TopoDS_Shape:
"""基于产品边界框创建模具包围盒(用于切分 A/B 半模等可视化用途)。"""
bbox = analysis.get("bounding_box", {})
dims = bbox.get("dimensions", [100, 100, 100])
@@ -95,7 +140,7 @@ class BaseMoldGenerator:
while explorer.More():
face = topods.Face(explorer.Current())
normal = self._get_face_normal(face)
normal = self.get_face_normal(face)
if normal is not None:
dot = abs(normal.Dot(draft_direction))
@@ -107,7 +152,7 @@ class BaseMoldGenerator:
return draftable
def _get_face_normal(self, face: TopoDS_Face) -> Optional[gp_Dir]:
def get_face_normal(self, face: TopoDS_Face) -> Optional[gp_Dir]:
try:
surface = BRepAdaptor_Surface(face)
u = (surface.FirstUParameter() + surface.LastUParameter()) / 2
@@ -133,7 +178,7 @@ class BaseMoldGenerator:
for face in faces:
try:
normal = self._get_face_normal(face)
normal = self.get_face_normal(face)
if normal is None:
continue
@@ -168,7 +213,7 @@ class BaseMoldGenerator:
for face in faces:
try:
draft = BRepOffsetAPI_DraftAngle(current_shape)
normal = self._get_face_normal(face)
normal = self.get_face_normal(face)
if normal is None:
continue
@@ -194,7 +239,7 @@ class BaseMoldGenerator:
return current_shape
def _analyze_product_geometry(self, shape: TopoDS_Shape) -> Dict[str, Any]:
def analyze_product_geometry(self, shape: TopoDS_Shape) -> Dict[str, Any]:
try:
props = GProp_GProps()
brepgprop.VolumeProperties(shape, props)
@@ -228,7 +273,7 @@ class BaseMoldGenerator:
logger.error(f"产品几何分析失败: {e}")
raise
def _split_cavity_core(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face, margin: int = 20) -> Tuple[TopoDS_Shape, TopoDS_Shape]:
def split_cavity_core(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face, margin: int = 20) -> Tuple[TopoDS_Shape, TopoDS_Shape]:
"""
分离型腔和型芯 — 完全嵌入 + 突出贴合方式。
@@ -254,7 +299,7 @@ class BaseMoldGenerator:
gp_Pnt(mold_xmax, mold_ymax, mold_zmax)
).Shape()
parting_plane = self._get_parting_plane(parting_surface, shape)
parting_plane = self.get_parting_plane(parting_surface, shape)
if parting_plane is None:
center_z = (zmin + zmax) / 2
parting_plane = gp_Pln(gp_Pnt(0, 0, center_z), gp_Dir(0, 0, 1))
@@ -371,7 +416,7 @@ class BaseMoldGenerator:
logger.info("型芯 Compound 兜底构建 (底座+产品)")
return compound
def _get_parting_plane(self, parting_surface: TopoDS_Face, shape: TopoDS_Shape) -> Optional[gp_Pln]:
def get_parting_plane(self, parting_surface: TopoDS_Face, shape: TopoDS_Shape) -> Optional[gp_Pln]:
"""从分型面提取平面方程"""
try:
surface = BRepAdaptor_Surface(parting_surface)
@@ -388,7 +433,7 @@ class BaseMoldGenerator:
logger.warning(f"分型面平面提取失败: {e}")
return None
def _split_mold_block_by_plane(self, mold_block: TopoDS_Shape,
def split_mold_block_by_plane(self, mold_block: TopoDS_Shape,
parting_plane: gp_Pln) -> Tuple[TopoDS_Shape, TopoDS_Shape]:
"""
用分型面将模具块切分为A板(上模)和B板(下模)
@@ -751,7 +796,7 @@ class BaseMoldGenerator:
return total_length
def _calculate_parting_line(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face) -> List[List[float]]:
def calculate_parting_line(self, shape: TopoDS_Shape, parting_surface: TopoDS_Face) -> List[List[float]]:
try:
section = BRepAlgoAPI_Section(shape, parting_surface)
section.Build()
+17 -12
View File
@@ -1,4 +1,5 @@
from typing import Dict, List, Any, Tuple, Optional
import warnings
import numpy as np
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace
from OCC.Core.gp import gp_Pln, gp_Dir, gp_Pnt
@@ -25,7 +26,6 @@ class MoldCavityGenerator(BaseMoldGenerator):
material_density: float = 1.05):
super().__init__(shrinkage_rate, draft_angle, material_density)
self.parting_line_tolerance = 0.1
self.max_draft_angle = 5.0
self.side_action_designer = SideActionDesigner()
@@ -39,7 +39,7 @@ class MoldCavityGenerator(BaseMoldGenerator):
def generate_mold_cavities(self, product_shape: TopoDS_Shape) -> Dict[str, Any]:
"""
从产品的3D模型生成型腔和型芯
[已废弃] 单方案分模入口。生产路径请使用 MultiSchemeMoldPlanner.generate_plan。
Returns:
{
@@ -49,10 +49,15 @@ class MoldCavityGenerator(BaseMoldGenerator):
"parting_line": parting_line
}
"""
warnings.warn(
"generate_mold_cavities 已废弃,请改用 MultiSchemeMoldPlanner.generate_plan 生成多方案分模结果",
DeprecationWarning,
stacklevel=2,
)
logger.info("开始生成模具型腔...")
try:
analysis = self._analyze_product_geometry(product_shape)
analysis = self.analyze_product_geometry(product_shape)
parting_result = self._detect_primary_parting(product_shape, analysis)
parting_surface = parting_result["surface"]
@@ -62,18 +67,18 @@ class MoldCavityGenerator(BaseMoldGenerator):
side_action_result = self.side_action_designer.analyze_and_design(
shape=product_shape,
parting_direction=parting_direction,
mold_size=self._calculate_mold_size(analysis),
mold_size=self.calculate_mold_size(analysis),
parting_surface=parting_surface,
)
undercut_regions = self._build_undercut_regions(
undercut_regions = self.build_undercut_regions(
side_action_result.get("undercut_analysis", {})
)
scaled_shape = self._apply_shrinkage_compensation(product_shape)
scaled_shape = self.apply_shrinkage_compensation(product_shape)
drafted_shape = self._apply_draft_angles(scaled_shape, parting_surface)
drafted_shape = self.apply_draft_angles(scaled_shape, parting_surface)
cavity, core = self._split_cavity_core(drafted_shape, parting_surface)
cavity, core = self.split_cavity_core(drafted_shape, parting_surface)
logger.info("模具型腔生成完成")
@@ -137,7 +142,7 @@ class MoldCavityGenerator(BaseMoldGenerator):
"side_action_summary": cavity_data.get("side_actions", {}).get("summary", {}),
},
"manufacturing_info": {
"estimated_mold_size": self._calculate_mold_size(analysis),
"estimated_mold_size": self.calculate_mold_size(analysis),
"estimated_clamping_force": self._calculate_clamping_force(analysis),
"recommended_material": self._get_recommended_material()
}
@@ -223,7 +228,7 @@ class MoldCavityGenerator(BaseMoldGenerator):
parting_plane, -span, span, -span, span
).Face()
parting_surface = self.extend_parting_surface(parting_surface, shape, extension=30.0)
parting_line = self._calculate_parting_line(shape, parting_surface)
parting_line = self.calculate_parting_line(shape, parting_surface)
return {
"surface": parting_surface,
"line": parting_line,
@@ -243,7 +248,7 @@ class MoldCavityGenerator(BaseMoldGenerator):
"confidence": 0.6,
}
def _build_undercut_regions(self, undercut_analysis: Dict[str, Any]) -> List[Dict[str, Any]]:
def build_undercut_regions(self, undercut_analysis: Dict[str, Any]) -> List[Dict[str, Any]]:
"""将侧向机构分析结果转换为兼容旧结构的倒扣区域列表。"""
undercut_faces = undercut_analysis.get("undercut_faces", [])
regions = []
@@ -360,7 +365,7 @@ class MoldCavityGenerator(BaseMoldGenerator):
"bounds": metadata["bounds"],
}
def _calculate_mold_size(self, analysis: Dict) -> Dict[str, float]:
def calculate_mold_size(self, analysis: Dict) -> Dict[str, float]:
"""估算模具尺寸"""
product_bbox = analysis["bounding_box"]["dimensions"]
margin = 30
+13 -26
View File
@@ -33,9 +33,9 @@ class MultiSchemeMoldPlanner:
) -> Dict[str, Any]:
generator = mold_generator_registry.get_by_type("aluminum_foam" if is_foam_material else "injection")
generator.set_material(material["name"])
self._apply_process_params(generator, material, process_params)
generator.apply_process_params(material, process_params)
analysis = generator._analyze_product_geometry(shape)
analysis = generator.analyze_product_geometry(shape)
analysis["axis_normal_stats"] = self._collect_axis_normal_stats(generator, shape)
candidates = self.candidate_generator.generate_candidates(
analysis=analysis,
@@ -100,24 +100,24 @@ class MultiSchemeMoldPlanner:
candidate.get("opening_span_mm"),
)
parting_line = generator.optimize_parting_line(
generator._calculate_parting_line(shape, parting_surface)
generator.calculate_parting_line(shape, parting_surface)
)
parting_direction = candidate["direction"]
side_action_result = generator.side_action_designer.analyze_and_design(
shape=shape,
parting_direction=parting_direction,
mold_size=generator._calculate_mold_size(analysis),
mold_size=generator.calculate_mold_size(analysis),
parting_surface=parting_surface,
)
undercut_regions = generator._build_undercut_regions(
undercut_regions = generator.build_undercut_regions(
side_action_result.get("undercut_analysis", {})
)
mold_structure = self._determine_mold_structure(analysis, undercut_regions)
scaled_shape = generator._apply_shrinkage_compensation(shape)
drafted_shape = generator._apply_draft_angles(scaled_shape, parting_surface)
cavity, core = generator._split_cavity_core(drafted_shape, parting_surface)
scaled_shape = generator.apply_shrinkage_compensation(shape)
drafted_shape = generator.apply_draft_angles(scaled_shape, parting_surface)
cavity, core = generator.split_cavity_core(drafted_shape, parting_surface)
cavity_result = {
"cavity": cavity,
@@ -130,7 +130,7 @@ class MultiSchemeMoldPlanner:
}
if is_foam_material:
cavity_result["mold_block"] = generator._generate_mold_block(cavity, analysis)
cavity_result["mold_block"] = generator.generate_mold_block(cavity, analysis)
cavity_result["parting_surfaces"] = {
"primary_surface": parting_surface,
"primary_line": parting_line,
@@ -158,10 +158,10 @@ class MultiSchemeMoldPlanner:
# 生成可视化辅助形状:A/B 半模(分型面切分)、产品本体
a_plate, b_plate, product_for_export = None, None, scaled_shape
try:
mold_block_shape = generator._create_mold_block(analysis, margin=30.0)
a_plate, b_plate = generator._split_mold_block_by_plane(
mold_block_shape = generator.create_mold_block(analysis, margin=30.0)
a_plate, b_plate = generator.split_mold_block_by_plane(
mold_block_shape,
generator._get_parting_plane(parting_surface, shape),
generator.get_parting_plane(parting_surface, shape),
)
except Exception as exc:
logger.debug(f"A/B 半模生成失败(不影响主流程): {exc}")
@@ -199,19 +199,6 @@ class MultiSchemeMoldPlanner:
},
}
@staticmethod
def _apply_process_params(generator: Any, material: Dict[str, Any], process_params: Optional[Dict[str, Any]]):
params = process_params or {}
draft_angle = float(params.get("draft_angle", getattr(generator, "draft_angle", 2.0)))
shrinkage_rate = float(params.get("shrinkage_rate", material.get("shrinkage", 0.005) * 100.0)) / 100.0
parting_precision = float(params.get("parting_precision", getattr(generator, "parting_line_tolerance", 0.1)))
cavity_match = float(params.get("cavity_match", getattr(generator, "cavity_match_rate", 95.0)))
generator.draft_angle = draft_angle
generator.shrinkage_rate = shrinkage_rate
generator.parting_line_tolerance = parting_precision
generator.cavity_match_rate = cavity_match
def _build_parting_surface(
self,
generator: Any,
@@ -290,7 +277,7 @@ class MultiSchemeMoldPlanner:
explorer.Next()
try:
normal = generator._get_face_normal(face)
normal = generator.get_face_normal(face)
if normal is None:
continue