98 lines
4.9 KiB
Python
98 lines
4.9 KiB
Python
"""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, topology_delta = session.adapter.cylinder_with_topology_delta(radius, height, axis)
|
|
# 3. 与当前主体做布尔并后登记为新主体,并返回该特征的结果对象。
|
|
_register_added_solid(session, node, solid, topology_delta=topology_delta)
|
|
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)
|