Files
cdsl-cad/backend/engine/cdsl_engine/executors/parametric.py
T
ganjihong 5ffb106f36 refactor(cdsl_engine): executor registry + per-family executor package
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.
2026-09-09 13:33:06 +08:00

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)