"""Analytic primitive executors (sphere_add / box_add / cylinder_add).""" from __future__ import annotations from typing import TYPE_CHECKING, Any from ..registry import atomic_executor from ..specs import AxisSpec, PlaneSpec from ..topology import FeaturePlanNode, FeatureResult from .common import _register_added_solid if TYPE_CHECKING: # pragma: no cover - import for type checkers only from ..session import ExecutionSession def _execute_sphere(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult: # 球体特征(sphere_add)执行入口:按球心与半径生成球体并并入当前主体。 # 1. 解析参数:半径 radius_mm 与球心 center_mm。 radius = float(node.params.get("radius_mm") or 0.0) center = node.params.get("center_mm") or [] # 2. 校验:半径必须大于 0,球心必须是三维坐标。 if radius <= 0 or len(center) != 3: raise ValueError("sphere_add requires radius_mm and a three-dimensional center_mm") # 3. 由适配器创建球体实体。 solid = session.adapter.sphere(radius, (float(center[0]), float(center[1]), float(center[2]))) # 4. 球体与当前主体做布尔并(fuse)后登记为新主体,并返回该特征的结果对象。 _register_added_solid(session, node, solid) return session.result(node) def _execute_box(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult: # 长方体特征(box_add)执行入口:以几何中心 center_mm 与三向尺寸生成原生长方体。 # 1. 解析并校验尺寸与中心,非法输入抛出带具体原因的 ValueError。 try: length = float(node.params.get("length_mm") or 0.0) width = float(node.params.get("width_mm") or 0.0) height = float(node.params.get("height_mm") or 0.0) center = node.params.get("center_mm") or [] except (TypeError, ValueError) as error: raise ValueError("box dimensions must be numeric") from error if length <= 0 or width <= 0 or height <= 0 or len(center) != 3: raise ValueError("box_add requires positive length_mm/width_mm/height_mm and a three-dimensional center_mm") # 2. 生成世界轴对齐的 plane frame:plane 原点是长方体的最小角点(中心减去半 # 尺寸),长/宽/高分别沿世界 x/y/z 生长(build123d Solid.make_box 语义)。 corner = ( float(center[0]) - length / 2, float(center[1]) - width / 2, float(center[2]) - height / 2, ) plane = PlaneSpec.from_mapping({"origin_mm": corner, "x_dir": [1, 0, 0], "normal": [0, 0, 1]}) solid = session.adapter.box(length, width, height, plane) # 3. 与当前主体做布尔并后登记为新主体,并返回该特征的结果对象。 _register_added_solid(session, node, solid) return session.result(node) def _execute_cylinder(node: FeaturePlanNode, session: "ExecutionSession") -> FeatureResult: # 圆柱特征(cylinder_add)执行入口:axis 的原点是底面圆心、方向为轴向; # axis 缺省为世界 +Z 过原点(底面圆心落在 (0,0,0))。 # 1. 解析并校验半径与高度,非法输入抛出带具体原因的 ValueError。 try: radius = float(node.params.get("radius_mm") or 0.0) height = float(node.params.get("height_mm") or 0.0) except (TypeError, ValueError) as error: raise ValueError("cylinder dimensions must be numeric") from error if radius <= 0 or height <= 0: raise ValueError("cylinder_add requires positive radius_mm and height_mm") raw_axis = node.params.get("axis") if raw_axis is not None and not ( isinstance(raw_axis, dict) and raw_axis.get("origin_mm") is not None and raw_axis.get("direction") is not None ): raise ValueError("cylinder_add axis must define origin_mm and direction") axis = AxisSpec.from_mapping(raw_axis) if isinstance(raw_axis, dict) else None # 2. 由适配器创建原生圆柱(axis=None 即世界 +Z 过原点)。 solid = session.adapter.cylinder(radius, height, axis) # 3. 与当前主体做布尔并后登记为新主体,并返回该特征的结果对象。 _register_added_solid(session, node, solid) return session.result(node) @atomic_executor("sphere_add") def _sphere_executor(node: FeaturePlanNode, session: "ExecutionSession", sketch: dict[str, Any] | None) -> FeatureResult: del sketch return _execute_sphere(node, session) @atomic_executor("box_add") def _box_executor(node: FeaturePlanNode, session: "ExecutionSession", sketch: dict[str, Any] | None) -> FeatureResult: del sketch return _execute_box(node, session) @atomic_executor("cylinder_add") def _cylinder_executor(node: FeaturePlanNode, session: "ExecutionSession", sketch: dict[str, Any] | None) -> FeatureResult: del sketch return _execute_cylinder(node, session)