Files
pythonocc-step-editor/scripts/verify_prismatic_feature.py
T

131 lines
5.4 KiB
Python

from __future__ import annotations
from pathlib import Path
import sys
import tempfile
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.Core.gp import gp_Pnt
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
from step_editor.step_io import _write_step
DEFAULT_MODEL = PROJECT_ROOT / "assets" / "models" / "cube_10mm.step"
def close_to(value: object, expected: float, tolerance: float = 1e-6) -> bool:
try:
return abs(float(value) - expected) <= tolerance
except (TypeError, ValueError):
return False
def find_feature(model: StepModel, feature_type: str) -> dict[str, object]:
matches = [model.feature_info(face_id) for face_id in range(len(model.faces))]
matches = [info for info in matches if info.get("feature_type") == feature_type]
if not matches:
observed = sorted({str(model.feature_info(face_id).get("feature_type")) for face_id in range(len(model.faces))})
raise AssertionError(f"expected {feature_type}, observed {observed}")
return matches[0]
def assert_prismatic_sizes(
info: dict[str, object], length: float, width: float, depth: float
) -> None:
expected = {
"prismatic_length": length,
"prismatic_width": width,
"prismatic_extrusion_estimate": depth,
}
for key, value in expected.items():
if not close_to(info.get(key), value):
raise AssertionError(f"{key}={info.get(key)!r}, expected {value}")
def main() -> int:
model = StepModel.load(DEFAULT_MODEL)
if len(model.faces) != 6:
raise AssertionError(f"expected 6 cube faces, got {len(model.faces)}")
for face_id in range(len(model.faces)):
info = model.feature_info(face_id)
if info.get("prismatic_profile_status") != "candidate":
raise AssertionError(f"Face {face_id} was not recognized as a rectangular profile")
if info.get("prismatic_extrusion_status") != "candidate":
raise AssertionError(f"Face {face_id} was not recognized as a connected extrusion")
for key in ("prismatic_length", "prismatic_width", "prismatic_extrusion_estimate"):
if not close_to(info.get(key), 10.0):
raise AssertionError(f"Face {face_id} {key}={info.get(key)!r}, expected 10")
if len(tuple(info.get("prismatic_connected_side_face_ids", ()))) != 4:
raise AssertionError(f"Face {face_id} does not have four connected side faces")
info = model.feature_info(0)
expected_keys = ("local_face_width", "local_face_height", "shell_thickness_estimate")
if _feature_dimension_keys(info) != expected_keys:
raise AssertionError(f"unexpected cube feature dimensions: {_feature_dimension_keys(info)}")
state = object.__new__(WindowStateMixin)
state.model = model
state.operation_in_progress = False
state.scan_in_progress = False
state.load_in_progress = False
state.selected_face_id = 0
state.selected_edge_id = None
state.selected_kind = "feature"
state.selected_part_id = int(info["part_id"])
state.selected_solid_id = int(info["solid_id"])
specs, _used = state._editable_property_specs(info)
filtered = state._feature_property_specs(specs, info)
dimensions = [spec for spec in filtered if spec.get("parameter_role") == "dimension"]
actual = tuple(
(
str(spec.get("key")),
str(spec.get("label")),
str(spec.get("current_text")),
)
for spec in dimensions
)
expected = (
("local_face_width", "长度", "10"),
("local_face_height", "宽度", "10"),
("shell_thickness_estimate", "高度/深度", "10"),
)
if actual != expected:
raise AssertionError(f"unexpected prismatic UI parameters: {actual}")
with tempfile.TemporaryDirectory(prefix="step-editor-prismatic-") as temp_dir:
temp_path = Path(temp_dir)
base = BRepPrimAPI_MakeBox(30.0, 20.0, 5.0).Shape()
pocket_tool = BRepPrimAPI_MakeBox(gp_Pnt(10.0, 6.0, 2.0), 10.0, 8.0, 4.0).Shape()
pocket_path = temp_path / "rectangular-pocket.step"
_write_step(BRepAlgoAPI_Cut(base, pocket_tool).Shape(), pocket_path)
pocket_info = find_feature(StepModel.load(pocket_path), "矩形口袋候选")
assert_prismatic_sizes(pocket_info, 10.0, 8.0, 3.0)
if pocket_info.get("prismatic_reference_source") != "side-wall-topology":
raise AssertionError(f"unexpected pocket reference: {pocket_info.get('prismatic_reference_source')}")
boss_tool = BRepPrimAPI_MakeBox(gp_Pnt(10.0, 6.0, 5.0), 10.0, 8.0, 3.0).Shape()
boss_path = temp_path / "rectangular-boss.step"
_write_step(BRepAlgoAPI_Fuse(base, boss_tool).Shape(), boss_path)
boss_info = find_feature(StepModel.load(boss_path), "矩形凸台候选")
assert_prismatic_sizes(boss_info, 10.0, 8.0, 3.0)
if boss_info.get("prismatic_reference_source") not in {
"side-wall-topology", "overlapping-plane"
}:
raise AssertionError(f"unexpected boss reference: {boss_info.get('prismatic_reference_source')}")
print("prismatic feature recognition ok")
return 0
if __name__ == "__main__":
raise SystemExit(main())