feat: 增强一级关系识别与保持关系策略
This commit is contained in:
@@ -17,9 +17,14 @@ from step_editor.step_io import _write_step
|
||||
from step_editor.ui_helpers import INFO_LABELS
|
||||
|
||||
from verify_hole_resize import _first_hole_face, _write_blind_hole_model, _write_through_hole_model # noqa: E402
|
||||
from verify_slot_resize import _first_slot_face, _write_half_round_slot_model # noqa: E402
|
||||
from verify_slot_resize import _first_slot_face, _write_cross_obround_slot_model, _write_half_round_slot_model # noqa: E402
|
||||
from verify_boss_resize import _first_boss_face, _write_boss_model # noqa: E402
|
||||
from verify_ellipse_edge_resize import _write_ellipse_face_model # noqa: E402
|
||||
from verify_edge_round_chamfer import ( # noqa: E402
|
||||
_first_existing_fillet_face,
|
||||
_write_mixed_radius_filleted_box_model,
|
||||
)
|
||||
from verify_shell_thickness_resize import _first_open_shell_wall_face, _write_open_thin_wall_box_model # noqa: E402
|
||||
|
||||
|
||||
def _assert(condition: bool, message: str) -> None:
|
||||
@@ -288,6 +293,89 @@ def _verify_ellipse_edge_scan_entries(root: Path) -> None:
|
||||
raise AssertionError(f"ellipse Edge scan should not expose generic length editing: {item}")
|
||||
|
||||
|
||||
def _verify_complex_slot_guard(root: Path) -> None:
|
||||
path = root / "cross_obround_slot.step"
|
||||
_write_cross_obround_slot_model(path)
|
||||
model = StepModel.load(path)
|
||||
blocked_slot_faces: list[int] = []
|
||||
for face_id in range(len(model.faces)):
|
||||
info = model.face_info(face_id)
|
||||
if info.get("surface") != "cylinder":
|
||||
continue
|
||||
feature = model.feature_info(face_id)
|
||||
if feature.get("slot_kind") == "partial-cylindrical-groove" and feature.get("slot_status") == "blocked":
|
||||
blocked_slot_faces.append(face_id)
|
||||
blockers = str(feature.get("slot_blockers") or feature.get("recognition_blockers") or "")
|
||||
_assert("交叉槽" in blockers or "多槽组" in blockers, f"complex slot blocker is unclear: {feature}")
|
||||
_assert("复杂槽" in str(feature.get("feature_type") or ""), f"complex slot label is unclear: {feature}")
|
||||
_assert(blocked_slot_faces, "cross obround slot should be recognized as blocked complex slots")
|
||||
|
||||
candidates = model.editable_feature_candidates(limit=80, detailed=False)
|
||||
forbidden_actions = {
|
||||
"resize_cylinder",
|
||||
"resize_slot_width",
|
||||
"resize_slot_depth",
|
||||
"resize_slot_arc_length",
|
||||
"resize_slot_angular_span",
|
||||
}
|
||||
leaked = [
|
||||
(item.get("operation_key"), item.get("target_id"))
|
||||
for item in candidates
|
||||
if int(item.get("target_id", -1)) in blocked_slot_faces and item.get("operation_key") in forbidden_actions
|
||||
]
|
||||
_assert(not leaked, f"complex slot should not leak editable scan entries: {leaked}")
|
||||
|
||||
|
||||
def _verify_mixed_radius_fillet_chain_guard(root: Path) -> None:
|
||||
path = root / "mixed_radius_fillet_chain.step"
|
||||
_write_mixed_radius_filleted_box_model(path, 1.0, 1.8)
|
||||
model = StepModel.load(path)
|
||||
face_id = _first_existing_fillet_face(model, 1.0, 2e-4)
|
||||
feature = model.feature_info(face_id)
|
||||
_assert(feature.get("existing_fillet_status") == "blocked", f"mixed-radius fillet chain should be blocked: {feature}")
|
||||
_assert(
|
||||
feature.get("existing_fillet_chain_status") == "variable-radius-chain-candidate",
|
||||
f"mixed-radius fillet chain should expose variable-radius status: {feature}",
|
||||
)
|
||||
blockers = str(feature.get("recognition_blockers") or feature.get("existing_fillet_blockers") or "")
|
||||
_assert("变半径" in blockers, f"mixed-radius fillet blocker should mention variable radius: {feature}")
|
||||
_assert(feature.get("recognition_decision") == "已阻止", f"mixed-radius fillet should be globally blocked: {feature}")
|
||||
_assert(
|
||||
"已有圆角半径" not in str(feature.get("recognition_ready_actions") or ""),
|
||||
f"mixed-radius fillet chain should not be ready-editable: {feature}",
|
||||
)
|
||||
chain_face_ids = tuple(feature.get("feature_existing_fillet_chain_face_ids") or ())
|
||||
candidates = model.editable_feature_candidates(limit=40, detailed=False)
|
||||
leaked = [
|
||||
(item.get("operation_key"), item.get("target_id"))
|
||||
for item in candidates
|
||||
if item.get("operation_key") == "inspect_existing_fillet" and int(item.get("target_id", -1)) in chain_face_ids
|
||||
]
|
||||
_assert(not leaked, f"mixed-radius fillet chain should not leak editable scan entries: {leaked}")
|
||||
|
||||
|
||||
def _verify_open_shell_context_summary(root: Path) -> None:
|
||||
path = root / "open_thin_wall_box.step"
|
||||
_write_open_thin_wall_box_model(path)
|
||||
model = StepModel.load(path)
|
||||
face_id = _first_open_shell_wall_face(model, 2.0, 2e-4)
|
||||
info = model.feature_info(face_id)
|
||||
_assert(info.get("open_shell_context_status") == "limited", f"open shell context should be limited: {info}")
|
||||
_assert_summary(info, "open shell context", {"surface", "first_level_topology", "open_shell_context"})
|
||||
_assert(
|
||||
"完整抽壳/开口面编辑" in str(info.get("recognition_limited_actions") or ""),
|
||||
f"open shell should list full shell/opening edit as limited: {info}",
|
||||
)
|
||||
_assert(
|
||||
"完整抽壳/开口面" in str(info.get("recognition_limitations") or ""),
|
||||
f"open shell limitation should explain unsupported complete shell edit: {info}",
|
||||
)
|
||||
_assert(
|
||||
"壳体厚度" in str(info.get("recognition_ready_actions") or ""),
|
||||
f"open shell wall should keep local shell thickness ready: {info}",
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
for key in (
|
||||
"recognition_summary",
|
||||
@@ -318,6 +406,9 @@ def main() -> int:
|
||||
_verify_torus_summary(root)
|
||||
_verify_user_priority_scan_order(root)
|
||||
_verify_ellipse_edge_scan_entries(root)
|
||||
_verify_complex_slot_guard(root)
|
||||
_verify_mixed_radius_fillet_chain_guard(root)
|
||||
_verify_open_shell_context_summary(root)
|
||||
|
||||
print("feature recognition summary ok")
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user