feat: 完善一级关系编辑 UI 与视图体验
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
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())
|
||||
Reference in New Issue
Block a user