"""CDSL runtime adapter driven exclusively by profile operation contracts.""" from __future__ import annotations from copy import deepcopy from hashlib import sha256 import json import math from pathlib import Path from typing import Any from app.cad_agent.domain.operation_contract import ( SEMANTIC_PREFLIGHT_NAMES, OperationContractError, canonical_hash, validate_fragment, validate_operation_contract, ) from app.cad_agent.domain.verifier_registry import default_registry from app.services.engine_service import load_engine, topology_snapshot, validate_cdsl from app.services.review_renderer import ReviewRenderError, render_checkpoint from app.settings import Settings from vendor.cdsl_preview_runtime import step_to_glb class RuntimeAdapterError(RuntimeError): pass class ProfileCadRuntime: """Adapter that owns engine imports; application code sees only its port.""" def __init__(self, settings: Settings) -> None: self.settings = settings self.engine = load_engine(settings) self._semantic_preflight_handlers = { "sketch_workplane": self._preflight_sketch_workplane, "profile_non_self_intersecting": self._preflight_profile_non_self_intersecting, "host_face_exists": self._preflight_host_face_exists, "hole_positions_on_host_plane": self._preflight_hole_positions_on_host_plane, "cut_exit_distance": self._preflight_cut_exit_distance, "requires_active_solid": self._preflight_requires_active_solid, "revolve_axis_on_sketch": self._preflight_revolve_axis_on_sketch, "reference_plane_nonzero_normal": self._preflight_reference_plane_nonzero_normal, "reference_axis_nonzero_direction": self._preflight_reference_axis_nonzero_direction, "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: self._profile = json.loads(schema_path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as error: raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: profile schema is unavailable") from error self._contracts = self._profile.get("operation_contracts") if not isinstance(self._contracts, dict): raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: profile has no v3 operation contracts") declared = {str(item) for item in self._contracts} registered = {str(item) for item in getattr(self.engine, "SUPPORTED_ATOMIC_IDS", ())} if declared != registered: raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: runtime and profile operation registries disagree") verifier_kinds = set(default_registry().claim_kinds) for contract in self._contracts.values(): try: validate_operation_contract(contract) except OperationContractError as error: raise RuntimeAdapterError(f"RUNTIME_CONTRACT_INVALID: {error}") from error unknown_preflights = set(contract["semantic_preflight"]) - set(self._semantic_preflight_handlers) unknown_verifiers = set(contract["candidate_verifiers"]) - verifier_kinds if unknown_preflights or unknown_verifiers: raise RuntimeAdapterError( "RUNTIME_CONTRACT_INVALID: operation contract references an unavailable " f"{'preflight' if unknown_preflights else 'candidate verifier'}" ) if set(self._semantic_preflight_handlers) != SEMANTIC_PREFLIGHT_NAMES: raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: runtime preflight registry is incomplete") def supported_atomic_ids(self) -> tuple[str, ...]: return tuple(sorted(self._contracts)) def operation_contract(self, atomic_id: str) -> dict[str, Any]: contract = self._contracts.get(atomic_id) if not isinstance(contract, dict): raise RuntimeAdapterError(f"RUNTIME_CONTRACT_INVALID: unknown operation {atomic_id}") result = deepcopy(contract) result["contract_hash"] = canonical_hash(contract) result["registry_revision"] = str(self._profile.get("schema_version") or "") return result def selector_tokens(self, topology: dict[str, Any] | None) -> dict[str, dict[str, Any]]: if not isinstance(topology, dict): return {} snapshot_id = str(topology.get("snapshot_id") or "") if not snapshot_id: return {} result: dict[str, dict[str, Any]] = {} for record in topology.get("records") or (): if not isinstance(record, dict) or not record.get("executable"): continue record_id = str(record.get("record_id") or "") kind = str(record.get("kind") or "") if not record_id or kind not in {"face", "edge", "plane", "axis", "body", "vertex"}: continue token = "sel_" + sha256(f"{snapshot_id}|{record_id}".encode("utf-8")).hexdigest()[:16] geometry = deepcopy(record.get("geometry") or {}) owners = record.get("owner_feature_ids") or [record.get("feature_id") or ""] result[token] = { "token": token, "kind": kind, "snapshot_id": snapshot_id, "selector": {"kind": kind, "stable_id": record_id, "owner_feature_id": str(owners[0] or ""), "geometry": geometry, "source": "runtime_snapshot", "snapshot_id": snapshot_id, "confidence": 1.0}, "geometry": geometry, } return result def reference_tokens(self, cdsl: dict[str, Any] | None) -> dict[str, str]: """Return opaque, head-scoped feature references for pattern contracts.""" result: dict[str, str] = {} document_hash = canonical_hash(cdsl) if isinstance(cdsl, dict) else "root" for feature in (cdsl or {}).get("features") or (): if not isinstance(feature, dict) or not isinstance(feature.get("id"), str) or not feature["id"]: continue feature_id = feature["id"] result["ref_" + sha256(f"{document_hash}|{feature_id}".encode("utf-8")).hexdigest()[:16]] = feature_id return result def materialize_fragment(self, base_cdsl: dict[str, Any] | None, fragment: dict[str, Any], contract: dict[str, Any], selector_tokens: dict[str, dict[str, Any]], reference_tokens: dict[str, str], *, require_through: bool = False, depends_on_feature_ids: tuple[str, ...] | list[str] = ()) -> tuple[dict[str, Any], dict[str, Any]]: # ActionCommandHandler validates the exposed schema first, but this # adapter is also used during crash recovery. Keep the runtime boundary # self-contained so a corrupted/replayed staged payload cannot produce # a candidate merely by bypassing that handler-level validation. try: validate_operation_contract(contract) except OperationContractError as error: raise RuntimeAdapterError(f"RUNTIME_CONTRACT_INVALID: {error}") from error current = self.operation_contract(str(contract.get("atomic_id") or "")) if ( contract.get("contract_hash") != current["contract_hash"] or contract.get("registry_revision") != current["registry_revision"] ): raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: operation contract is not the current verified registry entry") selector_shape = str((contract.get("fragment_shape") or {}).get("selector_tokens") or "forbidden") selector_kind = str((contract.get("selector_policy") or {}).get("token_kind") or "") allowed_selectors = [ token for token, value in selector_tokens.items() if selector_shape == "required" and isinstance(value, dict) and value.get("kind") == selector_kind ] errors = validate_fragment( contract, fragment, selector_tokens=allowed_selectors, reference_tokens=list(reference_tokens), root_xy_datum=not bool((base_cdsl or {}).get("features")), ) if errors: raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: fragment no longer matches the active operation schema: " + errors[0]["message"] ) materialized = deepcopy(fragment) reference = contract["reference_policy"] if reference["mode"] == "snapshot_bound": slot = str(reference["slot"]).removeprefix("params.") supplied = materialized.get("feature", {}).get("params", {}).get(slot, []) if not isinstance(supplied, list) or not all(token in reference_tokens for token in supplied): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: reference token is absent or stale") materialized["feature"]["params"][slot] = [reference_tokens[token] for token in supplied] through_normalizations = self._normalize_required_through_cut_depth( materialized, contract, selector_tokens, require_through=require_through, ) cut_support_normal = self._semantic_preflight( materialized, contract, selector_tokens, base_cdsl, require_through=require_through, ) direction_normalizations = self._normalize_extrude_cut_direction( materialized, contract, cut_support_normal, ) document = deepcopy(base_cdsl) if isinstance(base_cdsl, dict) else { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "agent_preflight", "geometry": {"sketches": []}, "features": [], } geometry = document.setdefault("geometry", {}) sketches = geometry.setdefault("sketches", []) if isinstance(geometry, dict) else None features = document.setdefault("features", []) if not isinstance(sketches, list) or not isinstance(features, list): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: base CDSL collections are invalid") existing_feature_ids = { str(item.get("id") or "") for item in features if isinstance(item, dict) and str(item.get("id") or "") } direct_dependencies = tuple(str(value) for value in depends_on_feature_ids) if len(direct_dependencies) != len(set(direct_dependencies)) or any(value not in existing_feature_ids for value in direct_dependencies): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: plan dependency feature is absent from the active checkpoint") index = len(features) + 1 feature = materialized["feature"] output = {"id": f"feature_{index:03d}", "atomic_id": contract["atomic_id"], "params": deepcopy(feature["params"]), "depends_on": list(direct_dependencies)} if contract["fragment_shape"]["sketch"] == "required": sketch_id = f"sketch_{len(sketches) + 1:03d}" sketch = materialized["sketch"] sketches.append({"id": sketch_id, "workplane": deepcopy(sketch["workplane"]), "profile": deepcopy(sketch["profile"])}) output["sketch_id"] = sketch_id selected = [selector_tokens[token]["selector"] for token in feature.get("selector_tokens", [])] slot = contract["selector_policy"]["slot"] if slot == "params.host_face": # A topology selector is authoritative while accepting the # action, but a later pattern replays this feature after its own # cut may have split the selected B-rep face. Lower the accepted # planar selector to a concrete, world-aligned host frame and # transform the author-supplied world coordinates into that # frame. The token remains in the fragment audit for provenance; # the materialized CDSL is stable under replay. output["params"]["host_face"], output["params"]["positions"] = self._materialized_hole_host_frame( selector_tokens[str(feature["selector_tokens"][0])], output["params"].get("positions"), ) elif slot == "params.mirror_plane": output["params"]["mirror_plane"] = selected[0] elif slot == "feature.selectors": output["selectors"] = selected features.append(output) try: self._validate_finite_tree(document) self._validate_materialized_runtime_types(output) validate_cdsl(document, self.engine) except Exception as error: message = str(error) # A JSON-schema failure after server materialization is a registry # drift: the author could not have supplied the missing server # field. Engine capability analysis, however, can reject a # schema-valid author sketch (for example an unresolvable analytic # contour). That is a recoverable action precondition failure, # not a deployment defect. if "CDSL engine runtime preflight failed:" in message: raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: materialized fragment is not executable by the engine: " + message ) from error raise RuntimeAdapterError( "RUNTIME_CONTRACT_INVALID: materialized fragment violates the engine CDSL schema: " + message ) from error return document, {"schema_version": "cad.v3.2.fragment-audit.v1", "atomic_id": contract["atomic_id"], "fragment_hash": canonical_hash(fragment), "contract_hash": contract["contract_hash"], "assigned_feature_ids": [output["id"]], "depends_on_feature_ids": list(direct_dependencies), "assigned_sketch_ids": [output["sketch_id"]] if output.get("sketch_id") else [], "selector_snapshot_id": next(iter(selector_tokens.values()), {}).get("snapshot_id", ""), "selector_tokens": list(feature.get("selector_tokens", [])), "reference_snapshot_id": canonical_hash(reference_tokens), "reference_tokens": list(fragment.get("feature", {}).get("params", {}).get(str(reference.get("slot") or "").removeprefix("params."), [])) if reference["mode"] == "snapshot_bound" else [], "server_normalizations": [*through_normalizations, *direction_normalizations]} def build_checkpoint(self, cdsl: dict[str, Any], output_dir: str, task_id: str, revision_id: str) -> dict[str, Any]: """Build the exact geometry checkpoint required by every DAG node. Rendering is deliberately absent here: a renderer outage must never make a geometrically valid atomic feature fail its local acceptance. """ root = Path(output_dir) root.mkdir(parents=True, exist_ok=True) cdsl_copy = deepcopy(cdsl) cdsl_copy["part_id"] = task_id step_path = root / "model.step" self._write_json(root / "model.cdsl.json", cdsl_copy) try: self._validate_finite_tree(cdsl_copy) validate_cdsl(cdsl_copy, self.engine) engine_result = self.engine.run_cdsl_only(cdsl_copy, step_path) if str(engine_result.get("engine") or "") != "cdsl_only": raise RuntimeAdapterError("engine did not execute CDSL-only rebuild") health = self._health(engine_result, step_path) topology = topology_snapshot(engine_result, task_id=task_id, revision_id=revision_id) self._write_json(root / "model.topology.json", topology) report = {"engine_result": engine_result, "preview": {}, "health": health, "render_manifest": {}} self._write_json(root / "rebuild-report.json", report) return {"health": health, "topology": topology, "report": report, "render_manifest": {}, "paths": {"cdsl": "model.cdsl.json", "step": "model.step", "topology": "model.topology.json", "report": "rebuild-report.json"}} except OSError: raise except RuntimeAdapterError: raise except Exception as error: raise RuntimeAdapterError(f"RUNTIME_EXECUTION_FAILURE: {error}") from error def create_preview(self, output_dir: str) -> dict[str, Any]: """Create an optional GLB preview for a completed checkpoint.""" root = Path(output_dir) step_path = root / "model.step" glb_path = root / "model.glb" preview = step_to_glb(step_path, glb_path) report_path = root / "rebuild-report.json" report = json.loads(report_path.read_text(encoding="utf-8")) if report_path.is_file() else {} report["preview"] = preview self._write_json(report_path, report) return {"preview": preview, "path": "model.glb"} def render_review_bundle(self, output_dir: str) -> dict[str, Any]: root = Path(output_dir) manifest = render_checkpoint(self.settings, step_path=root / "model.step", output_dir=root / "renders") report_path = root / "rebuild-report.json" report = json.loads(report_path.read_text(encoding="utf-8")) if report_path.is_file() else {} report["render_manifest"] = manifest self._write_json(report_path, report) return manifest def rebuild(self, cdsl: dict[str, Any], output_dir: str, task_id: str, revision_id: str) -> dict[str, Any]: # Legacy v3.1 compatibility path. New DAG nodes use build_checkpoint. built = self.build_checkpoint(cdsl, output_dir, task_id, revision_id) root = Path(output_dir) try: preview = self.create_preview(output_dir) manifest = self.render_review_bundle(output_dir) report = json.loads((root / "rebuild-report.json").read_text(encoding="utf-8")) return {**built, "report": report, "preview": preview["preview"], "render_manifest": manifest, "paths": {**built["paths"], "glb": "model.glb", "render_manifest": "renders/render-manifest.json"}} except OSError: # Artifact writes are a recoverable infrastructure outage. Let the # application handler park the same candidate stage for replay. raise except ReviewRenderError as error: raise RuntimeAdapterError(f"RENDER_SERVICE_UNAVAILABLE: {error}") from error except Exception as error: raise RuntimeAdapterError(f"RUNTIME_EXECUTION_FAILURE: {error}") from error def rebuild_best_effort( self, cdsl: dict[str, Any], output_dir: str, task_id: str, revision_id: str, ) -> tuple[dict[str, Any], list[dict[str, Any]]]: """Build independent feature prefixes without discarding prior geometry. A full CDSL document can contain several features even though the action protocol normally appends one at a time. Engine validation is all-or-nothing, so replay features in source order and retain each executable feature. A failed feature is reported as a diagnostic; it does not erase already executable geometry or prevent later, independent features from being tried. """ features = cdsl.get("features") if isinstance(cdsl.get("features"), list) else [] accepted: list[dict[str, Any]] = [] accepted_ids: set[str] = set() failures: list[dict[str, Any]] = [] latest: dict[str, Any] | None = None for index, feature in enumerate(features): if not isinstance(feature, dict): failures.append({"feature_index": index, "feature_id": "", "message": "Feature must be an object."}) continue feature_id = str(feature.get("id") or f"feature_{index + 1:03d}") dependencies = [str(value) for value in feature.get("depends_on") or () if isinstance(value, str)] unavailable = [value for value in dependencies if value not in accepted_ids] if unavailable: failures.append({ "feature_index": index, "feature_id": feature_id, "message": "Feature was skipped because an earlier dependency did not execute.", "dependencies": unavailable, }) continue candidate = self._feature_subset(cdsl, [*accepted, feature]) try: latest = self.rebuild(candidate, output_dir, task_id, revision_id) except Exception as error: failures.append({"feature_index": index, "feature_id": feature_id, "message": str(error)[:1000]}) continue accepted.append(deepcopy(feature)) accepted_ids.add(feature_id) if not accepted: if failures: raise RuntimeAdapterError(str(failures[0].get("message") or "RUNTIME_EXECUTION_FAILURE: no feature could be rebuilt")) raise RuntimeAdapterError("RUNTIME_EXECUTION_FAILURE: CDSL document has no executable features") # A failed later attempt may have left partial files in the stage. # Rebuild the retained feature set once so all published artifacts are # guaranteed to describe the same successful checkpoint. latest = self.rebuild(self._feature_subset(cdsl, accepted), output_dir, task_id, revision_id) return {**latest, "executed_feature_ids": sorted(accepted_ids)}, failures @staticmethod def _feature_subset(cdsl: dict[str, Any], features: list[dict[str, Any]]) -> dict[str, Any]: document = deepcopy(cdsl) document["features"] = deepcopy(features) geometry = document.get("geometry") if isinstance(document.get("geometry"), dict) else {} sketches = geometry.get("sketches") if isinstance(geometry.get("sketches"), list) else [] sketch_ids = {str(feature.get("sketch_id") or "") for feature in features} document["geometry"] = { **geometry, "sketches": [deepcopy(sketch) for sketch in sketches if isinstance(sketch, dict) and str(sketch.get("id") or "") in sketch_ids], } return document def _semantic_preflight(self, fragment: dict[str, Any], contract: dict[str, Any], selector_tokens: dict[str, dict[str, Any]], base_cdsl: dict[str, Any] | None, *, require_through: bool) -> list[float] | None: if fragment.get("feature", {}).get("atomic_id") != contract.get("atomic_id"): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: atomic_id does not match active contract") policy = contract["selector_policy"] supplied = fragment.get("feature", {}).get("selector_tokens", []) if contract["fragment_shape"]["selector_tokens"] == "required": if not all(token in selector_tokens and selector_tokens[token]["kind"] == policy["token_kind"] for token in supplied): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selector token is absent, stale, or has the wrong kind") for name in contract["semantic_preflight"]: self._semantic_preflight_handlers[name](fragment, selector_tokens, base_cdsl, require_through) if contract.get("atomic_id") == "extrude_cut_blind": return self._preflight_extrude_cut_contacts_material(fragment, selector_tokens) return None def _normalize_extrude_cut_direction( self, fragment: dict[str, Any], contract: dict[str, Any], support_normal: list[float] | None, ) -> list[dict[str, Any]]: """Aim a surface-attached cut into the measured material half-space. A sketch extrusion has no host selector. Once preflight proves that its profile lies on an oriented boundary face, the only executable blind cut direction is the material side of that face. This is a coordinate normalization, not a planning decision, and is recorded in the audit. """ if contract.get("atomic_id") != "extrude_cut_blind" or not self._valid_vector3(support_normal, require_nonzero=True): return [] sketch = fragment.get("sketch") if isinstance(fragment, dict) else None workplane = sketch.get("workplane") if isinstance(sketch, dict) else None normal = workplane.get("normal") if isinstance(workplane, dict) else None params = fragment.get("feature", {}).get("params") if isinstance(fragment.get("feature"), dict) else None if not self._valid_vector3(normal, require_nonzero=True) or not isinstance(params, dict): return [] unit_normal = [float(component) / self._norm(normal) for component in normal] unit_support = [float(component) / self._norm(support_normal) for component in support_normal] alignment = self._dot(unit_normal, unit_support) if abs(abs(alignment) - 1.0) > 1e-6: return [] materialized_reverse = alignment > 0 submitted_reverse = bool(params.get("reverse", False)) params["reverse"] = materialized_reverse return [{ "path": "feature.params.reverse", "submitted": submitted_reverse, "materialized": materialized_reverse, "reason": "surface-attached cut must travel into the measured material half-space", "support_normal": unit_support, }] def _normalize_required_through_cut_depth( self, fragment: dict[str, Any], contract: dict[str, Any], selector_tokens: dict[str, dict[str, Any]], *, require_through: bool, ) -> list[dict[str, Any]]: """Add the minimum deterministic exit allowance for a through cut. The author owns nominal feature geometry. For a through requirement, however, the runtime owns the executable end condition: this engine needs a strictly greater cut distance than the measured host span. Recording the adjustment makes the operational allowance visible without treating it as a user-specified blind-cut depth. """ if not require_through or contract.get("atomic_id") != "extrude_cut_blind": return [] params = fragment.get("feature", {}).get("params", {}) sketch = fragment.get("sketch") if isinstance(fragment.get("sketch"), dict) else {} workplane = sketch.get("workplane") if isinstance(sketch, dict) else {} normal = workplane.get("normal") if isinstance(workplane, dict) else None thickness = self._span_from_bbox(self._active_body_bbox(selector_tokens), normal) distance = params.get("distance_mm") if isinstance(params, dict) else None if not isinstance(distance, (int, float)) or thickness is None: return [] required_distance = float(thickness) + 0.01 if float(distance) > float(thickness) + 1e-6: return [] params["distance_mm"] = required_distance return [{ "path": "/feature/params/distance_mm", "submitted_mm": float(distance), "materialized_mm": required_distance, "reason": "required through-cut exit allowance", }] def _preflight_sketch_workplane(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: sketch = fragment.get("sketch") workplane = sketch.get("workplane") if isinstance(sketch, dict) else None if not isinstance(workplane, dict): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: sketch workplane is unavailable") normal, x_dir = workplane.get("normal"), workplane.get("x_dir") if not isinstance(normal, list) or not isinstance(x_dir, list) or self._norm(normal) <= 1e-9 or self._norm(x_dir) <= 1e-9 or self._norm(self._cross(normal, x_dir)) <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: sketch workplane vectors are degenerate") def _preflight_profile_non_self_intersecting(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: sketch = fragment.get("sketch") profile = sketch.get("profile") if isinstance(sketch, dict) else None if not isinstance(profile, dict): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: sketch profile is unavailable") if profile.get("type") == "polygon": vertices = profile.get("vertices") if not isinstance(vertices, list) or len(vertices) < 3 or self._polygon_self_intersects(vertices): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: polygon profile self-intersects") elif profile.get("type") == "analytic_contours": self._preflight_analytic_contours(profile) def _preflight_extrude_cut_contacts_material(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]]) -> list[float] | None: """Reject an extrude cut whose start profile floats above the solid. Sketch cuts do not carry a host-face selector, so a model can place a correct 2-D profile on the top of an unrelated boss. The engine then rebuilds successfully but leaves the body unchanged. Detect the common no-contact form using the current planar topology and return a useful coordinate diagnosis before allocating a stage or running the kernel. """ sketch = fragment.get("sketch") if isinstance(fragment, dict) else None workplane = sketch.get("workplane") if isinstance(sketch, dict) else None profile = sketch.get("profile") if isinstance(sketch, dict) else None origin = workplane.get("origin_mm") if isinstance(workplane, dict) else None normal = workplane.get("normal") if isinstance(workplane, dict) else None x_dir = workplane.get("x_dir") if isinstance(workplane, dict) else None if not self._valid_vector3(origin) or not self._valid_vector3(normal, require_nonzero=True) or not self._valid_vector3(x_dir, require_nonzero=True) or not isinstance(profile, dict): return None unit_normal = [float(component) / self._norm(normal) for component in normal] x_projection = self._dot(x_dir, unit_normal) raw_x = [float(x_dir[index]) - x_projection * unit_normal[index] for index in range(3)] if self._norm(raw_x) <= 1e-9: return None unit_x = [value / self._norm(raw_x) for value in raw_x] unit_y = self._cross(unit_normal, unit_x) local_points = self._profile_probe_points(profile) if not local_points: return None world_points = [ [ float(origin[index]) + local[0] * unit_x[index] + local[1] * unit_y[index] for index in range(3) ] for local in local_points ] tolerance = 1e-5 matching_faces: list[dict[str, Any]] = [] available_heights: list[float] = [] for value in selectors.values(): geometry = value.get("geometry") if isinstance(value, dict) else None face_normal = geometry.get("normal") if isinstance(geometry, dict) else None center = geometry.get("center_mm") if isinstance(geometry, dict) else None loops = geometry.get("boundary_loops_mm") if isinstance(geometry, dict) else None if ( not isinstance(geometry, dict) or geometry.get("surface_type") != "plane" or not self._valid_vector3(face_normal, require_nonzero=True) or not self._valid_vector3(center) or not isinstance(loops, list) or not loops ): continue unit_face_normal = [float(component) / self._norm(face_normal) for component in face_normal] if abs(abs(self._dot(unit_normal, unit_face_normal)) - 1.0) > 1e-6: continue available_heights.append(self._dot([float(center[index]) for index in range(3)], unit_normal)) if abs(self._dot([float(origin[index]) - float(center[index]) for index in range(3)], unit_normal)) <= tolerance: matching_faces.append(geometry) if not matching_faces: return None for face in matching_faces: if any( self._point_in_planar_face(point, face["boundary_loops_mm"], unit_normal, tolerance) for point in world_points ): face_normal = face.get("normal") return [float(component) for component in face_normal] if self._valid_vector3(face_normal, require_nonzero=True) else None plane_coordinate = self._dot([float(value) for value in origin], unit_normal) heights = ", ".join(f"{value:g}" for value in sorted(set(round(value, 6) for value in available_heights))[:8]) raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: extrude-cut profile does not contact material on its start plane " f"(plane coordinate {plane_coordinate:g}; available parallel planar faces: [{heights}]); " "place the sketch on the material face containing the intended cut profile" ) @staticmethod def _profile_probe_points(profile: dict[str, Any]) -> list[tuple[float, float]]: """Return inexpensive local points sufficient for contact preflight.""" points: list[tuple[float, float]] = [] def append(value: Any) -> None: point = ProfileCadRuntime._point2(value) if point is not None: points.append(point) profile_type = profile.get("type") if profile_type == "circle": append(profile.get("center")) elif profile_type == "rectangle": append(profile.get("center")) elif profile_type == "polygon": vertices = profile.get("vertices") if isinstance(vertices, list): for vertex in vertices: append(vertex) elif profile_type == "analytic_contours": contours = profile.get("contours") if isinstance(contours, list): for contour in contours: segments = contour.get("segments") if isinstance(contour, dict) else None if not isinstance(segments, list): continue contour_points: list[tuple[float, float]] = [] for segment in segments: if not isinstance(segment, dict): continue for key in ("start", "end", "center"): point = ProfileCadRuntime._point2(segment.get(key)) if point is not None: points.append(point) contour_points.append(point) if contour_points: points.append(( sum(point[0] for point in contour_points) / len(contour_points), sum(point[1] for point in contour_points) / len(contour_points), )) return points def _preflight_host_face_exists(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: supplied = fragment.get("feature", {}).get("selector_tokens", []) if len(supplied) != 1 or not isinstance(selectors.get(supplied[0]), dict) or selectors[supplied[0]].get("kind") != "face": raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: host face is absent or stale") def _materialized_hole_host_frame( self, selected: dict[str, Any], positions: Any, ) -> tuple[dict[str, Any], list[dict[str, list[float]]]]: """Lower a verified planar host selector into replay-stable CDSL data. Hole position inputs are world coordinates at the author boundary. The engine's frame form uses local coordinates, so both values must be converted together. Keeping only the selector makes a later pattern replay depend on a face that the source cut has already subdivided, which is neither stable nor geometrically meaningful. """ geometry = selected.get("geometry") if isinstance(selected, dict) else None center = geometry.get("center_mm") if isinstance(geometry, dict) else None normal = geometry.get("normal") if isinstance(geometry, dict) else None if not self._valid_vector3(center) or not self._valid_vector3(normal, require_nonzero=True): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selected host face has no usable plane frame") if not isinstance(positions, list) or not positions: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole positions are unavailable for host-frame materialization") origin = [float(value) for value in center] unit_normal = [float(value) / self._norm(normal) for value in normal] seed = [1.0, 0.0, 0.0] if abs(unit_normal[0]) < 0.9 else [0.0, 1.0, 0.0] x_raw = [seed[index] - self._dot(seed, unit_normal) * unit_normal[index] for index in range(3)] x_length = self._norm(x_raw) if x_length <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selected host face cannot define a stable x direction") x_dir = [value / x_length for value in x_raw] y_dir = self._cross(unit_normal, x_dir) local_positions: list[dict[str, list[float]]] = [] for position in positions: point = position.get("mm") if isinstance(position, dict) else None if not self._valid_vector3(point): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is not a finite 3D point") offset = [float(point[index]) - origin[index] for index in range(3)] local_positions.append({"mm": [ self._dot(offset, x_dir), self._dot(offset, y_dir), self._dot(offset, unit_normal), ]}) return ( {"frame": {"origin_mm": origin, "x_dir": x_dir, "y_dir": y_dir, "normal": unit_normal}}, local_positions, ) def _preflight_hole_positions_on_host_plane(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: positions = fragment.get("feature", {}).get("params", {}).get("positions") if not isinstance(positions, list) or not positions or not all(isinstance(item, dict) and isinstance(item.get("mm"), list) and len(item["mm"]) == 3 for item in positions): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole positions are invalid for the host plane") supplied = fragment.get("feature", {}).get("selector_tokens", []) host = selectors.get(supplied[0]) if len(supplied) == 1 else None geometry = host.get("geometry") if isinstance(host, dict) else None normal = geometry.get("normal") if isinstance(geometry, dict) else None center = geometry.get("center_mm") if isinstance(geometry, dict) else None bbox = geometry.get("bbox_mm") if isinstance(geometry, dict) else None if not isinstance(geometry, dict) or geometry.get("surface_type") != "plane" or not self._valid_vector3(normal, require_nonzero=True) or not self._valid_vector3(center): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole host must be an observable planar face") unit_normal = [float(component) / self._norm(normal) for component in normal] tolerance = 1e-5 if not self._valid_bbox(bbox): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: host face has no usable topology bounds") for position in positions: point = position["mm"] if not self._valid_vector3(point): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is not a finite 3D point") offset = [float(point[index]) - float(center[index]) for index in range(3)] if abs(self._dot(offset, unit_normal)) > tolerance: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is not on the selected host plane") if any(float(point[index]) < float(bbox[index]) - tolerance or float(point[index]) > float(bbox[index + 3]) + tolerance for index in range(3)): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is outside the selected host-face bounds") boundary_loops = geometry.get("boundary_loops_mm") if isinstance(boundary_loops, list) and boundary_loops and not self._point_in_planar_face(point, boundary_loops, unit_normal, tolerance): if not self._counterbore_reuses_existing_pilot(fragment, selectors, point, unit_normal, tolerance): host_z = float(center[2]) if isinstance(center, list) and len(center) == 3 else float("nan") coordinate = ", ".join(f"{float(value):g}" for value in point) raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: hole position " f"[{coordinate}] lies outside the selected host face material boundary (host center z={host_z:g}); " "select a planar host face that contains every requested hole center" ) def _counterbore_reuses_existing_pilot( self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], point: list[float], host_normal: list[float], tolerance: float, ) -> bool: """Allow a counterbore to start from an existing coaxial pilot bore. A top face becomes annular after a through bore, so the pilot centre is deliberately outside its material boundary. Counterboring that pilot is nevertheless a standard valid operation. The exception is deliberately narrow: it only applies to a matching inner cylindrical bore whose axis is normal to the selected host plane and whose open end contains the requested start point. """ feature = fragment.get("feature") if isinstance(fragment, dict) else None params = feature.get("params") if isinstance(feature, dict) and isinstance(feature.get("params"), dict) else {} pilot_diameter = params.get("diameter_mm") counterbore_diameter = params.get("counterbore_diameter_mm") if ( not isinstance(feature, dict) or feature.get("atomic_id") != "hole_counterbore" or not isinstance(pilot_diameter, (int, float)) or not isinstance(counterbore_diameter, (int, float)) or float(pilot_diameter) <= 0 or float(counterbore_diameter) <= float(pilot_diameter) ): return False diameter_tolerance = max(tolerance, abs(float(pilot_diameter)) * 1e-6) for value in selectors.values(): geometry = value.get("geometry") if isinstance(value, dict) else None if ( not isinstance(geometry, dict) or geometry.get("surface_type") != "cylinder" or geometry.get("cylinder_role") != "inner" or not bool(geometry.get("through")) ): continue radius = geometry.get("radius_mm") axis_origin = geometry.get("axis_origin_mm") axis_direction = geometry.get("axis_direction") bbox = geometry.get("bbox_mm") if ( not isinstance(radius, (int, float)) or abs(2 * float(radius) - float(pilot_diameter)) > diameter_tolerance or not self._valid_vector3(axis_origin) or not self._valid_vector3(axis_direction, require_nonzero=True) or not self._valid_bbox(bbox) ): continue unit_axis = [float(component) / self._norm(axis_direction) for component in axis_direction] if abs(abs(self._dot(unit_axis, host_normal)) - 1.0) > 1e-6: continue offset = [float(point[index]) - float(axis_origin[index]) for index in range(3)] axial = self._dot(offset, unit_axis) radial = [offset[index] - axial * unit_axis[index] for index in range(3)] if self._norm(radial) > tolerance: continue if any(float(point[index]) < float(bbox[index]) - tolerance or float(point[index]) > float(bbox[index + 3]) + tolerance for index in range(3)): continue return True return False def _preflight_analytic_contours(self, profile: dict[str, Any]) -> None: contours = profile.get("contours") if not isinstance(contours, list) or not contours: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: analytic profile requires at least one contour") tolerance = 1e-7 for contour_index, contour in enumerate(contours): segments = contour.get("segments") if isinstance(contour, dict) else None if not isinstance(segments, list) or not segments: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} is empty") if any(segment.get("type") == "circle" for segment in segments if isinstance(segment, dict)): if len(segments) != 1 or segments[0].get("type") != "circle": raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} cannot mix a full circle with other segments") continue endpoints: list[tuple[tuple[float, float], tuple[float, float]]] = [] for segment_index, segment in enumerate(segments): if not isinstance(segment, dict) or segment.get("type") not in {"line", "arc"}: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} has an unsupported segment") start = self._point2(segment.get("start")) end = self._point2(segment.get("end")) if start is None or end is None: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} segment {segment_index} has non-finite endpoints") if self._distance2(start, end) <= tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} segment {segment_index} is degenerate") if segment.get("type") == "arc": center = self._point2(segment.get("center")) radius = segment.get("radius_mm") if center is None or not isinstance(radius, (int, float)) or isinstance(radius, bool) or not math.isfinite(float(radius)) or float(radius) <= 0: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} arc {segment_index} has an invalid circle") arc_tolerance = max(tolerance, float(radius) * 1e-7) if abs(self._distance2(start, center) - float(radius)) > arc_tolerance or abs(self._distance2(end, center) - float(radius)) > arc_tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} arc {segment_index} endpoints are not on the declared circle") endpoints.append((start, end)) for segment_index in range(1, len(endpoints)): if self._distance2(endpoints[segment_index - 1][1], endpoints[segment_index][0]) > tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} is discontinuous before segment {segment_index}") if contour.get("closed") is True and self._distance2(endpoints[-1][1], endpoints[0][0]) > tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} is not closed") line_segments = [segment for segment, raw in zip(endpoints, segments) if raw.get("type") == "line"] for first, (a, b) in enumerate(line_segments): for second, (c, d) in enumerate(line_segments): if second <= first + 1 or (first == 0 and second == len(line_segments) - 1 and contour.get("closed") is True): continue if self._segments_intersect(a, b, c, d): raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} has an obvious self-intersection") @classmethod def _validate_materialized_runtime_types(cls, feature: dict[str, Any]) -> None: atomic_id = str(feature.get("atomic_id") or "") if not atomic_id.startswith("hole_"): return params = feature.get("params") if isinstance(feature.get("params"), dict) else {} host = params.get("host_face") if isinstance(params.get("host_face"), dict) else None frame = host.get("frame") if isinstance(host, dict) and isinstance(host.get("frame"), dict) else None if not isinstance(frame, dict): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: materialized hole host_face.frame is unavailable") for field in ("origin_mm", "x_dir", "y_dir", "normal"): if not cls._valid_vector3(frame.get(field), require_nonzero=field != "origin_mm"): raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: materialized hole host_face.frame.{field} is invalid") x_dir, y_dir, normal = frame["x_dir"], frame["y_dir"], frame["normal"] if abs(cls._dot(x_dir, y_dir)) > 1e-6 or abs(cls._dot(x_dir, normal)) > 1e-6 or abs(cls._dot(y_dir, normal)) > 1e-6: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: materialized hole host frame is not orthogonal") positions = params.get("positions") if not isinstance(positions, list) or not positions: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: materialized hole positions are unavailable") for index, position in enumerate(positions): point = position.get("mm") if isinstance(position, dict) else None if not cls._valid_vector3(point) or abs(float(point[2])) > 1e-5: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: materialized hole position {index} is invalid in the host frame") @staticmethod def _validate_finite_tree(value: Any, path: str = "$") -> None: if value is None: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: {path} must not be null") if isinstance(value, float) and not math.isfinite(value): raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: {path} must be finite") if isinstance(value, dict): for key, child in value.items(): ProfileCadRuntime._validate_finite_tree(child, f"{path}.{key}") elif isinstance(value, list): for index, child in enumerate(value): ProfileCadRuntime._validate_finite_tree(child, f"{path}[{index}]") @staticmethod def _point2(value: Any) -> tuple[float, float] | None: if not isinstance(value, list) or len(value) != 2: return None if not all(isinstance(item, (int, float)) and not isinstance(item, bool) and math.isfinite(float(item)) for item in value): return None return float(value[0]), float(value[1]) @staticmethod def _distance2(left: tuple[float, float], right: tuple[float, float]) -> float: return math.hypot(left[0] - right[0], left[1] - right[1]) @classmethod def _point_in_planar_face(cls, point: list[float], loops: list[Any], normal: list[float], tolerance: float) -> bool: drop_axis = max(range(3), key=lambda index: abs(float(normal[index]))) def project(value: Any) -> tuple[float, float] | None: if not cls._valid_vector3(value): return None coordinates = [float(value[index]) for index in range(3) if index != drop_axis] return coordinates[0], coordinates[1] projected_point = project(point) if projected_point is None: return False polygons: list[list[tuple[float, float]]] = [] for loop in loops: polygon = [project(vertex) for vertex in loop] if isinstance(loop, list) else [] if len(polygon) >= 3 and all(vertex is not None for vertex in polygon): polygons.append([vertex for vertex in polygon if vertex is not None]) if not polygons: return False def area(polygon: list[tuple[float, float]]) -> float: return abs(sum( polygon[index][0] * polygon[(index + 1) % len(polygon)][1] - polygon[(index + 1) % len(polygon)][0] * polygon[index][1] for index in range(len(polygon)) )) / 2 def contains(polygon: list[tuple[float, float]]) -> bool: x, y = projected_point inside = False for index, first in enumerate(polygon): second = polygon[(index + 1) % len(polygon)] if cls._distance_to_segment_2d(projected_point, first, second) <= tolerance: return True if (first[1] > y) != (second[1] > y): crossing_x = (second[0] - first[0]) * (y - first[1]) / (second[1] - first[1]) + first[0] if x < crossing_x: inside = not inside return inside outer = max(polygons, key=area) return contains(outer) and not any(contains(inner) for inner in polygons if inner is not outer) @staticmethod def _distance_to_segment_2d(point: tuple[float, float], start: tuple[float, float], end: tuple[float, float]) -> float: dx, dy = end[0] - start[0], end[1] - start[1] length_squared = dx * dx + dy * dy if length_squared <= 1e-18: return math.hypot(point[0] - start[0], point[1] - start[1]) fraction = max(0.0, min(1.0, ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy) / length_squared)) return math.hypot(point[0] - (start[0] + fraction * dx), point[1] - (start[1] + fraction * dy)) def _preflight_cut_exit_distance(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, require_through: bool) -> None: if not require_through: return params = fragment.get("feature", {}).get("params", {}) depth = params.get("depth_mm", params.get("distance_mm")) supplied = fragment.get("feature", {}).get("selector_tokens", []) if not isinstance(depth, (int, float)): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: cut depth is unavailable") if supplied: host = selectors[supplied[0]]["geometry"] bbox = host.get("bbox_mm") if isinstance(host, dict) else None normal = host.get("normal") or host.get("plane_normal") if isinstance(host, dict) else None thickness = self._span_from_bbox(bbox, normal) else: bbox = self._active_body_bbox(selectors) normal = (fragment.get("sketch") or {}).get("workplane", {}).get("normal") thickness = self._span_from_bbox(bbox, normal) if thickness is None: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: cannot prove through-cut exit distance from the active topology") if float(depth) <= thickness + 1e-6: raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: " f"cut depth {float(depth):g} mm must exceed measured host-body thickness {thickness:g} mm" ) def _preflight_requires_active_solid(self, _fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], base: dict[str, Any] | None, _require_through: bool) -> None: # A committed active CDSL document exists only after a candidate with # a solid has been accepted. Reference and subtraction operations # therefore cannot be the first feature of a part. if not isinstance(base, dict) or not base.get("features"): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: operation requires an active solid checkpoint") def _preflight_revolve_axis_on_sketch(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: axis = fragment.get("feature", {}).get("params", {}).get("axis") direction = axis.get("direction") if isinstance(axis, dict) else None origin = axis.get("origin_mm") if isinstance(axis, dict) else None workplane = fragment.get("sketch", {}).get("workplane") if isinstance(fragment.get("sketch"), dict) else None plane_origin = workplane.get("origin_mm") if isinstance(workplane, dict) else None plane_normal = workplane.get("normal") if isinstance(workplane, dict) else None if not self._valid_vector3(direction, require_nonzero=True) or not self._valid_vector3(origin) or not self._valid_vector3(plane_origin) or not self._valid_vector3(plane_normal, require_nonzero=True): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: revolve axis or sketch plane is invalid") unit_direction = [float(component) / self._norm(direction) for component in direction] unit_normal = [float(component) / self._norm(plane_normal) for component in plane_normal] if abs(self._dot(unit_direction, unit_normal)) > 1e-6: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: revolve axis is not parallel to the sketch plane") axis_offset = [float(origin[index]) - float(plane_origin[index]) for index in range(3)] if abs(self._dot(axis_offset, unit_normal)) > 1e-5: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: revolve axis is not on the sketch plane") def _preflight_reference_plane_nonzero_normal(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: plane = fragment.get("feature", {}).get("params", {}).get("plane") normal = plane.get("normal") if isinstance(plane, dict) else None if not isinstance(normal, list) or self._norm(normal) <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: reference plane normal is degenerate") def _preflight_reference_axis_nonzero_direction(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: axis = fragment.get("feature", {}).get("params", {}).get("axis") direction = axis.get("direction") if isinstance(axis, dict) else None if not isinstance(direction, list) or self._norm(direction) <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: reference axis direction is degenerate") def _preflight_selected_edges_exist(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: supplied = fragment.get("feature", {}).get("selector_tokens", []) if not supplied or not all(isinstance(selectors.get(token), dict) and selectors[token].get("kind") == "edge" for token in supplied): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selected edge is absent or stale") def _preflight_source_features_exist(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], base: dict[str, Any] | None, _require_through: bool) -> None: params = fragment.get("feature", {}).get("params", {}) known = {str(feature.get("id") or "") for feature in ((base or {}).get("features") or ()) if isinstance(feature, dict)} if not set(params.get("source_feature_ids") or ()).issubset(known): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: source feature is not part of current head") def _preflight_mirror_plane_exists(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: supplied = fragment.get("feature", {}).get("selector_tokens", []) 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] if len(points) != len(vertices): return True segments = list(zip(points, [*points[1:], points[0]])) for first, (a, b) in enumerate(segments): for second, (c, d) in enumerate(segments): if second <= first + 1 or (first == 0 and second == len(segments) - 1): continue if ProfileCadRuntime._segments_intersect(a, b, c, d): return True return False @staticmethod def _segments_intersect(a: tuple[float, float], b: tuple[float, float], c: tuple[float, float], d: tuple[float, float]) -> bool: def orientation(p: tuple[float, float], q: tuple[float, float], r: tuple[float, float]) -> float: return (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]) left = orientation(a, b, c) right = orientation(a, b, d) low = orientation(c, d, a) high = orientation(c, d, b) return (left > 0) != (right > 0) and (low > 0) != (high > 0) @staticmethod def _active_body_bbox(selector_tokens: dict[str, dict[str, Any]]) -> list[float] | None: for token in selector_tokens.values(): if token.get("kind") != "body": continue geometry = token.get("geometry") bbox = geometry.get("bbox_mm") if isinstance(geometry, dict) else None if isinstance(bbox, list) and len(bbox) == 6 and all(isinstance(value, (int, float)) for value in bbox): return [float(value) for value in bbox] return None @staticmethod def _span_from_bbox(bbox: Any, normal: Any) -> float | None: if not isinstance(bbox, list) or len(bbox) != 6 or not isinstance(normal, list) or len(normal) != 3: return None if not all(isinstance(value, (int, float)) for value in [*bbox, *normal]): return None length = math.sqrt(sum(float(value) ** 2 for value in normal)) if length <= 1e-9: return None return sum( abs(float(normal[index]) / length) * abs(float(bbox[index + 3]) - float(bbox[index])) for index in range(3) ) @staticmethod def _norm(vector: list[float]) -> float: return math.sqrt(sum(float(item) ** 2 for item in vector)) @staticmethod def _dot(left: list[float], right: list[float]) -> float: return sum(float(left[index]) * float(right[index]) for index in range(3)) @staticmethod def _valid_vector3(value: Any, *, require_nonzero: bool = False) -> bool: valid = ( isinstance(value, list) and len(value) == 3 and all(isinstance(item, (int, float)) and not isinstance(item, bool) and math.isfinite(float(item)) for item in value) ) return valid and (not require_nonzero or ProfileCadRuntime._norm(value) > 1e-9) @staticmethod def _valid_bbox(value: Any) -> bool: return ( isinstance(value, list) and len(value) == 6 and all(isinstance(item, (int, float)) and not isinstance(item, bool) and math.isfinite(float(item)) for item in value) and all(float(value[index]) <= float(value[index + 3]) for index in range(3)) ) @staticmethod def _cross(a: list[float], b: list[float]) -> list[float]: return [float(a[1]) * float(b[2]) - float(a[2]) * float(b[1]), float(a[2]) * float(b[0]) - float(a[0]) * float(b[2]), float(a[0]) * float(b[1]) - float(a[1]) * float(b[0])] @staticmethod def _write_json(path: Path, payload: dict[str, Any]) -> None: path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") @staticmethod def _health(engine_result: dict[str, Any], step_path: Path, glb_path: Path | None = None) -> dict[str, Any]: bbox = engine_result.get("bbox_mm") if isinstance(engine_result.get("bbox_mm"), dict) else {} minimum, maximum = bbox.get("min"), bbox.get("max") if not isinstance(minimum, list) or not isinstance(maximum, list) or len(minimum) != 3 or len(maximum) != 3 or not step_path.is_file() or (glb_path is not None and not glb_path.is_file()): raise RuntimeAdapterError("rebuild did not produce complete deterministic artifacts") return {"bbox_mm": {"min": [float(item) for item in minimum], "max": [float(item) for item in maximum], "dimensions": [float(maximum[index]) - float(minimum[index]) for index in range(3)]}, "volume_mm3": float(engine_result["volume_mm3"]), "solid_count": int(engine_result["solid_count"]), "feature_count": len(engine_result.get("feature_results") or [])}