5ffb106f36
Phase 3 of the decoupling refactor (behavior-preserving): - registry.py: atomic_executor decorator, ALL_ATOMIC_IDS with fail-fast registration validation, execute_node dispatcher - executors/: one module per family (extrude, revolve, surfaces, loft_sweep, bodies, context, primitives, parametric, holes, dressup, patterns) + shared helpers in executors/common - executors/__init__: explicit aggregation + completeness check (registry must cover every declared atomic id at import time) - runtime.py: slimmed to entry points (analyze_cdsl/rebuild_cdsl) plus full historical re-exports incl. test-referenced privates Adding an atomic operation now touches only one executor module and its schema contract; the shared registry never changes. Verified against baseline: zero new failures.
119 lines
6.2 KiB
Python
119 lines
6.2 KiB
Python
"""Parametric feature executors (thread / bend / gear / rack).
|
|
|
|
Each executor parses its runtime-neutral spec from ``runtime_types`` specs,
|
|
asks the geometry adapter to build and place the local-frame solid, then
|
|
fuses it into the active body (or subtracts, for thread_cut).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from ..registry import atomic_executor
|
|
from ..specs import BendSpec, GearSpec, RackSpec, ThreadSpec
|
|
from ..topology import FeaturePlanNode, FeatureResult, RuntimeDiagnostic
|
|
from .common import _register_added_solid
|
|
|
|
if TYPE_CHECKING: # pragma: no cover - import for type checkers only
|
|
from ..session import ExecutionSession
|
|
|
|
|
|
def _execute_thread(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult:
|
|
# 螺纹特征(thread_add)执行入口:按规格生成参数化螺纹段并并入当前主体。
|
|
# 1. 解析并校验尺寸/牙距/轴,非法输入抛出带具体原因的 ValueError。
|
|
spec = ThreadSpec.from_feature(node.atomic_id, node.params)
|
|
# 2. 由适配器门面生成沿 spec.axis 放置的外螺纹实心段。
|
|
solid = session.adapter.thread_solid(spec)
|
|
# 3. 与当前主体做布尔并(fuse)后登记为新主体,并返回该特征的结果对象。
|
|
_register_added_solid(session, node, solid)
|
|
return session.result(node)
|
|
|
|
|
|
def _execute_thread_cut(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult:
|
|
# 内螺纹(thread_cut)执行入口:ThreadSpec.from_feature 对 thread_cut 恒置
|
|
# internal=True,生成牙顶外放 INTERNAL_CUT_OVERLAP_MM 的切削刀具,沿
|
|
# spec.axis 放置后从当前主体布尔差出全深螺旋牙槽(宿主通常已预打光孔,
|
|
# 刀具 core 落在孔腔中,仅外放的牙槽层切入孔壁)。
|
|
# 1. 解析并校验尺寸/牙距/轴,非法输入抛出带具体原因的 ValueError。
|
|
spec = ThreadSpec.from_feature(node.atomic_id, node.params)
|
|
# 2. 由适配器门面生成沿 spec.axis 放置的内螺纹切削刀具实心段。
|
|
tool = session.adapter.thread_solid(spec)
|
|
# 3. 从当前主体布尔差(cut)后登记为新主体,并返回该特征的结果对象。
|
|
session.register_body(node.feature_id, session.adapter.cut(session.body, tool), replay_node=node)
|
|
return session.result(node)
|
|
|
|
|
|
@atomic_executor("thread_add", "thread_cut")
|
|
def _thread_executor(node: FeaturePlanNode, session: "ExecutionSession", sketch: dict[str, Any] | None) -> FeatureResult:
|
|
# 螺纹特征(thread_add/thread_cut)不需要草图平面,丢弃该参数后执行。
|
|
# thread_cut 走布尔差分支:从已有主体切出内螺纹槽,而非并入外螺纹段。
|
|
del sketch
|
|
if node.atomic_id == "thread_cut":
|
|
return _execute_thread_cut(node, session)
|
|
return _execute_thread(node, session)
|
|
|
|
|
|
def _execute_bend(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult:
|
|
# 折弯特征(bend_add)执行入口:按规格生成等厚折弯板并并入当前主体。
|
|
# 1. 解析并校验板厚/宽度/折痕链与放置平面,非法输入抛出带具体原因的 ValueError。
|
|
spec = BendSpec.from_feature(node.params)
|
|
# 2. 由适配器门面生成沿 spec.frame 放置的折弯实心段。
|
|
solid = session.adapter.bend_solid(spec)
|
|
# 3. 与当前主体做布尔并(fuse)后登记为新主体,并返回该特征的结果对象。
|
|
_register_added_solid(session, node, solid)
|
|
return session.result(node)
|
|
|
|
|
|
@atomic_executor("bend_add")
|
|
def _bend_executor(node: FeaturePlanNode, session: "ExecutionSession", sketch: dict[str, Any] | None) -> FeatureResult:
|
|
# 折弯特征(bend_add)不需要草图平面,丢弃该参数后执行。
|
|
del sketch
|
|
return _execute_bend(node, session)
|
|
|
|
|
|
def _execute_gear(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult:
|
|
# 齿轮特征(gear_add)执行入口:按规格生成渐开线齿轮并入当前主体。
|
|
# 1. 解析并校验模数/齿数/齿宽/螺旋角与放置轴,非法输入抛出带具体原因的 ValueError。
|
|
spec = GearSpec.from_feature(node.params)
|
|
# 2. 由适配器门面生成沿 spec.axis 放置的齿轮实体(直齿/斜齿/人字齿)。
|
|
solid = session.adapter.gear_solid(spec)
|
|
# 3. 与当前主体做布尔并(fuse)后登记为新主体,并返回该特征的结果对象。
|
|
_register_added_solid(session, node, solid)
|
|
# 4. 小齿数根切风险:不阻断执行,附加 info 级诊断供完成报告如实披露。
|
|
diagnostics: list[RuntimeDiagnostic] = []
|
|
if spec.teeth_count < 17:
|
|
diagnostics.append(RuntimeDiagnostic(
|
|
code="undercut_risk",
|
|
message=(
|
|
f"Gear with {spec.teeth_count} teeth and a 20 degree pressure angle is undercut-prone; "
|
|
"standard involute geometry is generated without profile shift"
|
|
),
|
|
feature_id=node.feature_id,
|
|
))
|
|
return session.result(node, diagnostics=diagnostics)
|
|
|
|
|
|
@atomic_executor("gear_add")
|
|
def _gear_executor(node: FeaturePlanNode, session: "ExecutionSession", sketch: dict[str, Any] | None) -> FeatureResult:
|
|
# 齿轮特征(gear_add)不需要草图平面,丢弃该参数后执行。
|
|
del sketch
|
|
return _execute_gear(node, session)
|
|
|
|
|
|
def _execute_rack(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult:
|
|
# 齿条特征(rack_add)执行入口:按规格生成直线齿条并入当前主体。
|
|
# 1. 解析并校验模数/齿数/厚度/压力角与放置轴,非法输入抛出带具体原因的 ValueError。
|
|
spec = RackSpec.from_feature(node.params)
|
|
# 2. 由适配器门面生成沿 spec.axis 放置的齿条实体(齿沿轴方向伸出)。
|
|
solid = session.adapter.rack_solid(spec)
|
|
# 3. 与当前主体做布尔并(fuse)后登记为新主体,并返回该特征的结果对象。
|
|
_register_added_solid(session, node, solid)
|
|
return session.result(node)
|
|
|
|
|
|
@atomic_executor("rack_add")
|
|
def _rack_executor(node: FeaturePlanNode, session: "ExecutionSession", sketch: dict[str, Any] | None) -> FeatureResult:
|
|
# 齿条特征(rack_add)不需要草图平面,丢弃该参数后执行。
|
|
del sketch
|
|
return _execute_rack(node, session)
|