feat: 完善 Face 参数化编辑和隔离执行
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
|
||||
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
|
||||
from OCC.Core.gp import gp_Ax2, gp_Dir, 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.geometry_utils import _finalize_boolean_result
|
||||
from step_editor.model import StepModel
|
||||
from step_editor.step_io import _write_step
|
||||
|
||||
|
||||
def _write_holed_plate(path: Path) -> None:
|
||||
plate = BRepPrimAPI_MakeBox(30.0, 20.0, 8.0).Shape()
|
||||
axis = gp_Ax2(gp_Pnt(15.0, 10.0, -1.0), gp_Dir(0.0, 0.0, 1.0))
|
||||
cutter = BRepPrimAPI_MakeCylinder(axis, 3.0, 10.0).Shape()
|
||||
cut = BRepAlgoAPI_Cut(plate, cutter)
|
||||
shape = _finalize_boolean_result(cut, "verify holed planar face guard cut")
|
||||
_write_step(shape, path)
|
||||
|
||||
|
||||
def _first_holed_plane_face(model: StepModel) -> int:
|
||||
for face_id in range(len(model.faces)):
|
||||
info = model.face_info(face_id)
|
||||
if info.get("surface") != "plane":
|
||||
continue
|
||||
if bool(info.get("has_inner_boundaries")):
|
||||
return face_id
|
||||
raise SystemExit("no planar Face with an inner boundary was found")
|
||||
|
||||
|
||||
def _assert_blocked(plan: dict[str, object], label: str) -> None:
|
||||
if plan.get("status") != "blocked":
|
||||
raise SystemExit(f"{label} should be blocked for a holed planar Face: {plan}")
|
||||
|
||||
|
||||
def _assert_push_pull_keeps_single_solid(path: Path, distance: float) -> str:
|
||||
model = StepModel.load(path)
|
||||
face_id = _first_holed_plane_face(model)
|
||||
plan = model.push_pull_plan(face_id, distance)
|
||||
if plan.get("status") == "blocked":
|
||||
raise SystemExit(f"holed planar Face push/pull should remain available: {plan}")
|
||||
result = model.push_pull_face(face_id, distance)
|
||||
stats = model.stats()
|
||||
if stats.solids != 1:
|
||||
raise SystemExit(f"holed planar Face push/pull should keep one solid: {stats}")
|
||||
return result
|
||||
|
||||
|
||||
def main() -> int:
|
||||
with tempfile.TemporaryDirectory(prefix="geom_param_face_boundary_") as temp_dir:
|
||||
path = Path(temp_dir) / "holed_plate.step"
|
||||
_write_holed_plate(path)
|
||||
model = StepModel.load(path)
|
||||
face_id = _first_holed_plane_face(model)
|
||||
info = model.face_info(face_id)
|
||||
|
||||
if int(info.get("boundary_wires") or 0) < 2:
|
||||
raise SystemExit(f"Face {face_id} should report at least two boundary wires: {info}")
|
||||
if bool(info.get("local_face_deform_ready", True)):
|
||||
raise SystemExit(f"Face {face_id} should not allow local Face deformation: {info}")
|
||||
solid_raw = info.get("solid_id")
|
||||
solid_id = int(solid_raw) if solid_raw is not None else -1
|
||||
if solid_id < 0 or solid_id not in model._local_face_deform_readiness_cache:
|
||||
raise SystemExit("local Face deformation readiness should be cached after face_info()")
|
||||
other_face_id = next(
|
||||
(item for item, item_solid_id in enumerate(model.face_solid_ids) if item_solid_id == solid_id and item != face_id),
|
||||
None,
|
||||
)
|
||||
if other_face_id is None:
|
||||
raise SystemExit("holed plate should expose another Face on the same Solid")
|
||||
original_vertex_reader = model._local_deform_face_vertex_points
|
||||
|
||||
def fail_on_cache_miss(*_args: object, **_kwargs: object) -> list[tuple[float, float, float]]:
|
||||
raise AssertionError("local Face readiness cache was not reused")
|
||||
|
||||
model._local_deform_face_vertex_points = fail_on_cache_miss # type: ignore[method-assign]
|
||||
try:
|
||||
cached_readiness = model._local_face_deform_readiness(other_face_id)
|
||||
finally:
|
||||
model._local_deform_face_vertex_points = original_vertex_reader # type: ignore[method-assign]
|
||||
if bool(cached_readiness.get("local_face_deform_ready", True)):
|
||||
raise SystemExit(f"cached readiness should keep local deformation disabled: {cached_readiness}")
|
||||
|
||||
center = info.get("area_center") or info.get("bbox_center")
|
||||
if not isinstance(center, tuple) or len(center) != 3:
|
||||
raise SystemExit(f"Face {face_id} does not expose a stable center")
|
||||
target_center = (float(center[0]), float(center[1]), float(center[2]) + 1.0)
|
||||
|
||||
area = float(info.get("area") or 0.0)
|
||||
if area <= 0:
|
||||
raise SystemExit(f"Face {face_id} does not expose a stable area")
|
||||
|
||||
width = float(info.get("local_face_width") or 0.0)
|
||||
if width <= 0:
|
||||
raise SystemExit(f"Face {face_id} does not expose a measured Face width")
|
||||
height = float(info.get("local_face_height") or 0.0)
|
||||
if height <= 0:
|
||||
raise SystemExit(f"Face {face_id} does not expose a measured Face height")
|
||||
|
||||
_assert_blocked(model.face_center_local_move_plan(face_id, target_center), "Face center local move")
|
||||
_assert_blocked(model.face_area_local_resize_plan(face_id, area * 1.1), "Face area local resize")
|
||||
_assert_blocked(model.face_size_local_resize_plan(face_id, width * 1.1, axis="width"), "Face width local resize")
|
||||
_assert_blocked(model.face_size_local_resize_plan(face_id, height * 1.1, axis="height"), "Face height local resize")
|
||||
_assert_blocked(model.face_plane_offset_local_plan(face_id, 1.0), "Face plane offset local move")
|
||||
|
||||
outward_result = _assert_push_pull_keeps_single_solid(path, 1.0)
|
||||
inward_result = _assert_push_pull_keeps_single_solid(path, -1.0)
|
||||
|
||||
print(
|
||||
"holed planar Face local edit guard ok: "
|
||||
f"face_id={face_id}, boundary_wires={info.get('boundary_wires')}, "
|
||||
f"blocker={info.get('local_face_deform_blocker')}, "
|
||||
f"push_pull_outward={outward_result}, push_pull_inward={inward_result}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user