提交关于N5功能,“involute_gear渐开线齿廓与齿条专用生成”的改动

This commit is contained in:
2026-09-08 13:47:24 +08:00
parent f48ba19dfa
commit 673ae62e70
14 changed files with 774 additions and 13 deletions
+53
View File
@@ -46,6 +46,9 @@ class ProfileCadRuntime:
"selected_edges_exist": self._preflight_selected_edges_exist,
"source_features_exist": self._preflight_source_features_exist,
"mirror_plane_exists": self._preflight_mirror_plane_exists,
"loft_profiles_exist": self._preflight_loft_profiles_exist,
"loft_profiles_closed": self._preflight_loft_profiles_closed,
"loft_profiles_single_region": self._preflight_loft_profiles_single_region,
}
schema_path = Path(str(self.engine.__file__)).with_name("profile_schema.json")
try:
@@ -990,6 +993,56 @@ class ProfileCadRuntime:
if len(supplied) != 1 or not isinstance(selectors.get(supplied[0]), dict) or selectors[supplied[0]].get("kind") != "plane":
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: mirror plane is absent or stale")
def _loft_profile_sketches(self, fragment: dict[str, Any], base: dict[str, Any] | None) -> list[dict[str, Any]]:
"""Resolve ``profile_sketch_ids`` against the base document's sketches."""
ids = [str(value) for value in fragment.get("feature", {}).get("params", {}).get("profile_sketch_ids") or ()]
sketches = (base or {}).get("geometry", {}).get("sketches") if isinstance((base or {}).get("geometry"), dict) else None
by_id = {
str(sketch.get("id") or ""): sketch
for sketch in (sketches or ())
if isinstance(sketch, dict)
}
resolved: list[dict[str, Any]] = []
for sketch_id in ids:
sketch = by_id.get(sketch_id)
if not isinstance(sketch, dict):
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: loft profile sketch is not part of current head")
resolved.append(sketch)
if not resolved:
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: loft has no profile sketches")
return resolved
def _preflight_loft_profiles_exist(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], base: dict[str, Any] | None, _require_through: bool) -> None:
self._loft_profile_sketches(fragment, base)
def _preflight_loft_profiles_closed(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], base: dict[str, Any] | None, _require_through: bool) -> None:
# circle/polygon 草图类型闭合性由 schema 保证;analytic_contours 需要
# 每条参与轮廓显式 closed。
for sketch in self._loft_profile_sketches(fragment, base):
profile = sketch.get("profile") if isinstance(sketch.get("profile"), dict) else None
if profile is None:
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: loft profile sketch has no profile")
if profile.get("type") == "analytic_contours":
contours = profile.get("contours")
if not isinstance(contours, list) or not contours:
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: loft profile has no contours")
for contour in contours:
if not isinstance(contour, dict) or not contour.get("closed"):
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: loft profile contour is not closed")
def _preflight_loft_profiles_single_region(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], base: dict[str, Any] | None, _require_through: bool) -> None:
# 每个放样截面必须是单连通区域:analytic_contours 只允许恰好一条
# outer 闭合轮廓,不得携带 inner 环或 open 段。
for sketch in self._loft_profile_sketches(fragment, base):
profile = sketch.get("profile") if isinstance(sketch.get("profile"), dict) else None
if profile is None:
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: loft profile sketch has no profile")
if profile.get("type") == "analytic_contours":
contours = [contour for contour in (profile.get("contours") or []) if isinstance(contour, dict)]
outer = [contour for contour in contours if contour.get("role") == "outer" and contour.get("closed")]
if len(outer) != 1 or len(outer) != len(contours):
raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: loft profile must be a single closed region")
@staticmethod
def _polygon_self_intersects(vertices: list[Any]) -> bool:
points = [(float(point[0]), float(point[1])) for point in vertices if isinstance(point, list) and len(point) == 2]