from __future__ import annotations from pathlib import Path import sys import tempfile PROJECT_ROOT = Path(__file__).resolve().parent.parent if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) SCRIPTS_ROOT = PROJECT_ROOT / "scripts" if str(SCRIPTS_ROOT) not in sys.path: sys.path.insert(0, str(SCRIPTS_ROOT)) from step_editor.model import StepModel from verify_edge_coordinate_edit import _tuple3 from verify_edge_length_resize import _line_edge_ids_near_length from verify_ellipse_edge_resize import _ellipse_edge_ids, _write_ellipse_face_model from verify_hole_resize import _write_through_hole_model DEFAULT_CUBE = PROJECT_ROOT / "assets" / "models" / "cube_10mm.step" def _assert_blocked(plan: dict[str, object], label: str) -> None: if str(plan.get("status") or "") != "blocked" or str(plan.get("risk") or "") != "blocked": raise AssertionError(f"{label} should be blocked, got status={plan.get('status')}, plan={plan}") message = str(plan.get("message") or plan.get("blockers") or "") if not message.strip(): raise AssertionError(f"{label} should explain why it is blocked: {plan}") def _cube_edge(model: StepModel) -> int: edge_ids = _line_edge_ids_near_length(model, 10.0, 1e-5) if not edge_ids: raise AssertionError("cube has no 10mm line Edge") return edge_ids[0] def _verify_straight_edge_guards() -> None: model = StepModel.load(DEFAULT_CUBE) edge_id = _cube_edge(model) info = model.edge_info(edge_id) current_length = float(info.get("length") or 0.0) start = _tuple3(info.get("start_point"), "edge start") end = _tuple3(info.get("end_point"), "edge end") center = _tuple3(info.get("length_center"), "edge center") _assert_blocked(model.general_edge_length_plan(edge_id, current_length), "same Edge length") _assert_blocked(model.general_edge_length_plan(edge_id, 0.0), "zero Edge length") _assert_blocked(model.general_edge_length_plan(edge_id, -1.0), "negative Edge length") _assert_blocked(model.edge_endpoint_move_plan(edge_id, "start", start), "same start point") _assert_blocked(model.edge_endpoint_move_plan(edge_id, "end", end), "same end point") _assert_blocked(model.edge_endpoint_move_plan(edge_id, "start", end), "collapsed endpoint") _assert_blocked(model.edge_center_move_plan(edge_id, center), "same Edge center") _assert_blocked(model.edge_fillet_plan(edge_id, 0.0), "zero Edge fillet radius") _assert_blocked(model.edge_fillet_plan(edge_id, current_length * 0.45), "oversized Edge fillet radius") _assert_blocked(model.edge_chamfer_plan(edge_id, 0.0), "zero Edge chamfer distance") _assert_blocked(model.edge_chamfer_plan(edge_id, current_length * 0.45), "oversized Edge chamfer distance") _assert_blocked(model.edge_asymmetric_chamfer_plan(edge_id, 0.0, 0.5), "zero asymmetric chamfer distance") _assert_blocked( model.edge_asymmetric_chamfer_plan(edge_id, current_length * 0.45, 0.5), "oversized asymmetric chamfer distance", ) _assert_blocked(model.edge_distance_angle_chamfer_plan(edge_id, 0.0, 45.0), "zero distance-angle chamfer") _assert_blocked(model.edge_distance_angle_chamfer_plan(edge_id, 0.5, 0.0), "zero distance-angle angle") _assert_blocked(model.edge_distance_angle_chamfer_plan(edge_id, 0.5, 90.0), "too large distance-angle angle") print("straight Edge target guards ok") def _verify_ellipse_guards() -> None: with tempfile.TemporaryDirectory(prefix="geom_param_edge_guard_ellipse_") as temp_dir: model_path = Path(temp_dir) / "ellipse.step" _write_ellipse_face_model(model_path) model = StepModel.load(model_path) edge_ids = _ellipse_edge_ids(model) if not edge_ids: raise AssertionError("ellipse model has no ellipse Edge") edge_id = edge_ids[0] info = model.edge_info(edge_id) major = float(info.get("major_radius") or 0.0) minor = float(info.get("minor_radius") or 0.0) _assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, major, axis_kind="major"), "same ellipse major radius") _assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, minor, axis_kind="minor"), "same ellipse minor radius") _assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, 0.0, axis_kind="major"), "zero ellipse major radius") _assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, -1.0, axis_kind="minor"), "negative ellipse minor radius") print("ellipse Edge target guards ok") def _verify_circular_axis_guard() -> None: with tempfile.TemporaryDirectory(prefix="geom_param_edge_guard_circle_") as temp_dir: model_path = Path(temp_dir) / "hole.step" _write_through_hole_model(model_path) model = StepModel.load(model_path) for edge_id in range(len(model.edges)): info = model.edge_info(edge_id) if info.get("curve") != "circle": continue center = info.get("center") if center is None: continue _assert_blocked(model.circular_edge_axis_move_plan(edge_id, center), "same circular Edge center") print("circular Edge axis target guard ok") return raise AssertionError("hole model has no circular Edge with center") def main() -> int: _verify_straight_edge_guards() _verify_ellipse_guards() _verify_circular_axis_guard() print("Edge target guard suite passed.") return 0 if __name__ == "__main__": raise SystemExit(main())