feat: 完善 Edge 一级编辑与 CAD 建模语义
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
SCRIPTS_ROOT = PROJECT_ROOT / "scripts"
|
||||
if str(SCRIPTS_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPTS_ROOT))
|
||||
|
||||
from step_editor.model import StepModel
|
||||
from verify_edge_coordinate_edit import _tuple3
|
||||
from verify_edge_length_resize import _line_edge_ids_near_length
|
||||
from verify_ellipse_edge_resize import _ellipse_edge_ids, _write_ellipse_face_model
|
||||
from verify_hole_resize import _write_through_hole_model
|
||||
|
||||
|
||||
DEFAULT_CUBE = PROJECT_ROOT / "assets" / "models" / "cube_10mm.step"
|
||||
|
||||
|
||||
def _assert_blocked(plan: dict[str, object], label: str) -> None:
|
||||
if str(plan.get("status") or "") != "blocked" or str(plan.get("risk") or "") != "blocked":
|
||||
raise AssertionError(f"{label} should be blocked, got status={plan.get('status')}, plan={plan}")
|
||||
message = str(plan.get("message") or plan.get("blockers") or "")
|
||||
if not message.strip():
|
||||
raise AssertionError(f"{label} should explain why it is blocked: {plan}")
|
||||
|
||||
|
||||
def _cube_edge(model: StepModel) -> int:
|
||||
edge_ids = _line_edge_ids_near_length(model, 10.0, 1e-5)
|
||||
if not edge_ids:
|
||||
raise AssertionError("cube has no 10mm line Edge")
|
||||
return edge_ids[0]
|
||||
|
||||
|
||||
def _verify_straight_edge_guards() -> None:
|
||||
model = StepModel.load(DEFAULT_CUBE)
|
||||
edge_id = _cube_edge(model)
|
||||
info = model.edge_info(edge_id)
|
||||
current_length = float(info.get("length") or 0.0)
|
||||
start = _tuple3(info.get("start_point"), "edge start")
|
||||
end = _tuple3(info.get("end_point"), "edge end")
|
||||
center = _tuple3(info.get("length_center"), "edge center")
|
||||
|
||||
_assert_blocked(model.general_edge_length_plan(edge_id, current_length), "same Edge length")
|
||||
_assert_blocked(model.general_edge_length_plan(edge_id, 0.0), "zero Edge length")
|
||||
_assert_blocked(model.general_edge_length_plan(edge_id, -1.0), "negative Edge length")
|
||||
_assert_blocked(model.edge_endpoint_move_plan(edge_id, "start", start), "same start point")
|
||||
_assert_blocked(model.edge_endpoint_move_plan(edge_id, "end", end), "same end point")
|
||||
_assert_blocked(model.edge_endpoint_move_plan(edge_id, "start", end), "collapsed endpoint")
|
||||
_assert_blocked(model.edge_center_move_plan(edge_id, center), "same Edge center")
|
||||
_assert_blocked(model.edge_fillet_plan(edge_id, 0.0), "zero Edge fillet radius")
|
||||
_assert_blocked(model.edge_fillet_plan(edge_id, current_length * 0.45), "oversized Edge fillet radius")
|
||||
_assert_blocked(model.edge_chamfer_plan(edge_id, 0.0), "zero Edge chamfer distance")
|
||||
_assert_blocked(model.edge_chamfer_plan(edge_id, current_length * 0.45), "oversized Edge chamfer distance")
|
||||
_assert_blocked(model.edge_asymmetric_chamfer_plan(edge_id, 0.0, 0.5), "zero asymmetric chamfer distance")
|
||||
_assert_blocked(
|
||||
model.edge_asymmetric_chamfer_plan(edge_id, current_length * 0.45, 0.5),
|
||||
"oversized asymmetric chamfer distance",
|
||||
)
|
||||
_assert_blocked(model.edge_distance_angle_chamfer_plan(edge_id, 0.0, 45.0), "zero distance-angle chamfer")
|
||||
_assert_blocked(model.edge_distance_angle_chamfer_plan(edge_id, 0.5, 0.0), "zero distance-angle angle")
|
||||
_assert_blocked(model.edge_distance_angle_chamfer_plan(edge_id, 0.5, 90.0), "too large distance-angle angle")
|
||||
print("straight Edge target guards ok")
|
||||
|
||||
|
||||
def _verify_ellipse_guards() -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="geom_param_edge_guard_ellipse_") as temp_dir:
|
||||
model_path = Path(temp_dir) / "ellipse.step"
|
||||
_write_ellipse_face_model(model_path)
|
||||
model = StepModel.load(model_path)
|
||||
edge_ids = _ellipse_edge_ids(model)
|
||||
if not edge_ids:
|
||||
raise AssertionError("ellipse model has no ellipse Edge")
|
||||
edge_id = edge_ids[0]
|
||||
info = model.edge_info(edge_id)
|
||||
major = float(info.get("major_radius") or 0.0)
|
||||
minor = float(info.get("minor_radius") or 0.0)
|
||||
|
||||
_assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, major, axis_kind="major"), "same ellipse major radius")
|
||||
_assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, minor, axis_kind="minor"), "same ellipse minor radius")
|
||||
_assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, 0.0, axis_kind="major"), "zero ellipse major radius")
|
||||
_assert_blocked(model.ellipse_edge_axis_radius_plan(edge_id, -1.0, axis_kind="minor"), "negative ellipse minor radius")
|
||||
print("ellipse Edge target guards ok")
|
||||
|
||||
|
||||
def _verify_circular_axis_guard() -> None:
|
||||
with tempfile.TemporaryDirectory(prefix="geom_param_edge_guard_circle_") as temp_dir:
|
||||
model_path = Path(temp_dir) / "hole.step"
|
||||
_write_through_hole_model(model_path)
|
||||
model = StepModel.load(model_path)
|
||||
for edge_id in range(len(model.edges)):
|
||||
info = model.edge_info(edge_id)
|
||||
if info.get("curve") != "circle":
|
||||
continue
|
||||
center = info.get("center")
|
||||
if center is None:
|
||||
continue
|
||||
_assert_blocked(model.circular_edge_axis_move_plan(edge_id, center), "same circular Edge center")
|
||||
print("circular Edge axis target guard ok")
|
||||
return
|
||||
raise AssertionError("hole model has no circular Edge with center")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
_verify_straight_edge_guards()
|
||||
_verify_ellipse_guards()
|
||||
_verify_circular_axis_guard()
|
||||
print("Edge target guard suite passed.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user