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

139 lines
5.4 KiB
Python
Raw Normal View History

from __future__ import annotations
from pathlib import Path
import sys
import tempfile
PROJECT_ROOT = Path(__file__).resolve().parent.parent
SCRIPTS_DIR = Path(__file__).resolve().parent
for path in (PROJECT_ROOT, SCRIPTS_DIR):
if str(path) not in sys.path:
sys.path.insert(0, str(path))
from step_editor.model import StepModel
from verify_boss_resize import ( # noqa: E402
_axis_center,
_first_boss_face,
_first_circle_edge_adjacent_to_face,
_write_boss_model,
)
def _assert(condition: bool, message: str) -> None:
if not condition:
raise AssertionError(message)
def _assert_common_topology(topology: dict[str, object], label: str) -> None:
_assert(topology.get("topology_relation_depth") == 1, f"{label}: topology depth should be 1")
_assert(
topology.get("topology_relation_boundary") == "shared-edge",
f"{label}: topology boundary should be shared-edge",
)
ignored = tuple(topology.get("topology_ignored_relation_depths", ()) or ())
_assert("second-level" in ignored, f"{label}: second-level propagation should be explicitly ignored")
_assert("third-level" in ignored, f"{label}: third-level propagation should be explicitly ignored")
_assert(
int(topology.get("cylindrical_feature_side_face_count", 0) or 0) >= 1,
f"{label}: selected boss side Face is missing",
)
_assert(
int(topology.get("cylindrical_feature_boundary_edge_count", 0) or 0) >= 1,
f"{label}: boss boundary Edges are missing",
)
_assert(
int(topology.get("cylindrical_feature_adjacent_face_count", 0) or 0) >= 1,
f"{label}: direct adjacent Faces are missing",
)
_assert(
int(topology.get("cylindrical_feature_end_face_count", 0) or 0) >= 1,
f"{label}: boss end/base cap Faces are missing",
)
def _assert_plan_topology(plan: dict[str, object], label: str) -> None:
_assert(plan.get("topology_relation_depth") == 1, f"{label}: plan topology depth should be 1")
_assert(
plan.get("topology_relation_status") == "ready",
f"{label}: plan topology should be ready, got {plan.get('topology_relation_status')!r}",
)
_assert(
plan.get("first_level_topology_status") == "ready",
f"{label}: first-level guard should be ready, got {plan.get('first_level_topology_status')!r}",
)
_assert(
int(plan.get("first_level_boundary_edge_count", 0) or 0) >= 1,
f"{label}: plan boundary Edges are missing",
)
_assert(
int(plan.get("first_level_adjacent_face_count", 0) or 0) >= 1,
f"{label}: plan adjacent Faces are missing",
)
_assert(
"second-level" in tuple(plan.get("topology_ignored_relation_depths", ()) or ()),
f"{label}: plan should document ignored deeper topology",
)
_assert(
plan.get("first_level_topology_guard_note"),
f"{label}: first-level topology guard note is missing",
)
def _assert_edge_delegated_topology(plan: dict[str, object]) -> None:
_assert(plan.get("circular_edge_cylinder_mode") == "boss", "circle Edge should delegate to boss axis move")
_assert(plan.get("move_axis_topology_relation_depth") == 1, "delegated boss topology depth should be 1")
_assert(
plan.get("move_axis_topology_relation_status") == "ready",
"delegated boss topology should be ready",
)
_assert(
plan.get("move_axis_first_level_topology_status") == "ready",
"delegated boss first-level guard should be ready",
)
_assert(
int(plan.get("move_axis_first_level_adjacent_face_count", 0) or 0) >= 1,
"delegated boss adjacent Faces are missing",
)
def main() -> int:
with tempfile.TemporaryDirectory(prefix="geom_param_boss_topology_") as temp_dir:
model_path = Path(temp_dir) / "boss.step"
_write_boss_model(model_path)
model = StepModel.load(model_path)
face_id = _first_boss_face(model)
topology = model.cylindrical_feature_first_level_topology(face_id)
_assert_common_topology(topology, "cylindrical boss")
center = _axis_center(model, face_id)
diameter_plan = model.cylindrical_boss_resize_plan(face_id, 8.0)
height_plan = model.cylindrical_boss_height_plan(face_id, 7.0)
axis_plan = model.cylindrical_boss_axis_move_plan(face_id, (center[0] + 2.0, center[1], center[2]))
_assert_plan_topology(diameter_plan, "boss diameter plan")
_assert_plan_topology(height_plan, "boss height plan")
_assert_plan_topology(axis_plan, "boss axis plan")
edge_id = _first_circle_edge_adjacent_to_face(model, face_id)
edge_info = model.edge_info(edge_id)
edge_center = edge_info.get("center")
_assert(isinstance(edge_center, tuple), "boss circular Edge center is missing")
target_edge_center = (float(edge_center[0]) + 2.0, float(edge_center[1]), float(edge_center[2]))
edge_axis_plan = model.circular_edge_axis_move_plan(edge_id, target_edge_center)
_assert_edge_delegated_topology(edge_axis_plan)
result = model.resize_cylindrical_boss(face_id, 8.0)
_assert("verified_face=" in result, "boss diameter edit should report verified target Face")
_assert(
"first_level_topology_matched=True" in result,
f"boss diameter edit should verify first-level topology: {result}",
)
print("boss first-level topology verification passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())