from __future__ import annotations from pathlib import Path import sys PROJECT_ROOT = Path(__file__).resolve().parent.parent if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from step_editor.model import StepModel from step_editor.window_state import WindowStateMixin, _feature_dimension_keys MODEL_PATH = PROJECT_ROOT / "assets" / "models" / "geom_extract.step" FACE_ID = 594 BASE_FACE_KEYS = ( "area", "local_face_width", "local_face_height", "face_center_position", "face_target_normal_position", ) class _Probe(WindowStateMixin): pass def _feature_rows(model: StepModel, face_id: int) -> tuple[str, ...]: info = model.quick_face_info(face_id) probe = object.__new__(_Probe) probe.model = model probe.operation_in_progress = False probe.scan_in_progress = False probe.load_in_progress = False probe.selected_face_id = face_id probe.selected_edge_id = None probe.selected_kind = "feature" probe.selected_part_id = int(info["part_id"]) probe.selected_solid_id = int(info["solid_id"]) probe.feature_detection_level = "current-only" probe.manual_bottom_face_id = None probe.manual_slot_pair_face_id = None context = probe._feature_context_info(face_id) specs, _used = probe._editable_property_specs(context) rows = probe._feature_context_property_specs(specs, context) return tuple(str(row.get("key")) for row in rows if row.get("parameter_role") == "dimension") def _assert_contains(keys: tuple[str, ...], expected: tuple[str, ...], label: str) -> None: missing = [key for key in expected if key not in keys] if missing: raise AssertionError(f"{label} missing {missing}, got {keys}") def main() -> int: model = StepModel.load(MODEL_PATH) if FACE_ID >= len(model.faces): raise AssertionError(f"test model has no Face {FACE_ID}") quick_info = model.quick_face_info(FACE_ID) if quick_info.get("surface") != "plane": raise AssertionError(f"Face {FACE_ID} should be planar in the baseline model: {quick_info}") before_cached_rows = _feature_rows(model, FACE_ID) _assert_contains(before_cached_rows, BASE_FACE_KEYS, "Face 594 before full feature cache") full_info = model.feature_info(FACE_ID) if full_info.get("shell_region_status") == "candidate": dimension_keys = _feature_dimension_keys(full_info) _assert_contains(dimension_keys, BASE_FACE_KEYS, "shell candidate feature dimensions") if "shell_thickness_estimate" not in dimension_keys: raise AssertionError(f"shell candidate should keep shell thickness as an extra dimension: {dimension_keys}") after_cached_rows = _feature_rows(model, FACE_ID) _assert_contains(after_cached_rows, BASE_FACE_KEYS, "Face 594 after full feature cache") if before_cached_rows != after_cached_rows: raise AssertionError( "Face 594 current-only feature rows changed after full recognition cache: " f"before={before_cached_rows}, after={after_cached_rows}" ) print("Face feature parameter consistency ok") return 0 if __name__ == "__main__": raise SystemExit(main())