feat: 完善 Face 一级编辑与隔离计算
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface
|
||||
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace
|
||||
from OCC.Core.GeomAbs import GeomAbs_BSplineSurface
|
||||
from OCC.Core.GeomAPI import GeomAPI_PointsToBSplineSurface
|
||||
from OCC.Core.TColgp import TColgp_Array2OfPnt
|
||||
from OCC.Core.gp import 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.model import StepModel
|
||||
from step_editor.step_io import _write_step
|
||||
from step_editor.window_state import WindowStateMixin
|
||||
|
||||
|
||||
class _PropertySpecProbe(WindowStateMixin):
|
||||
def __init__(self, face_id: int, selected_kind: str = "face") -> None:
|
||||
self.model = object()
|
||||
self.operation_in_progress = False
|
||||
self.scan_in_progress = False
|
||||
self.load_in_progress = False
|
||||
self.selected_face_id = face_id
|
||||
self.selected_edge_id = None
|
||||
self.selected_kind = selected_kind
|
||||
self.selected_part_id = 1
|
||||
self.selected_solid_id = -1
|
||||
self.manual_bottom_face_id = None
|
||||
self.manual_slot_pair_face_id = None
|
||||
|
||||
|
||||
def _write_b_spline_patch(path: Path) -> None:
|
||||
points = TColgp_Array2OfPnt(1, 4, 1, 4)
|
||||
for row in range(1, 5):
|
||||
x = float(row - 1) * 10.0
|
||||
for column in range(1, 5):
|
||||
y = float(column - 1) * 10.0
|
||||
z = 1.2 * ((row - 2.5) ** 2 - (column - 2.5) ** 2)
|
||||
points.SetValue(row, column, gp_Pnt(x, y, z))
|
||||
surface = GeomAPI_PointsToBSplineSurface(points).Surface()
|
||||
face = BRepBuilderAPI_MakeFace(surface, 1e-6).Face()
|
||||
_write_step(face, path)
|
||||
|
||||
|
||||
def _freeform_face_id(model: StepModel) -> int:
|
||||
for face_id, face in enumerate(model.faces):
|
||||
surf = BRepAdaptor_Surface(face)
|
||||
if surf.GetType() == GeomAbs_BSplineSurface:
|
||||
return face_id
|
||||
raise SystemExit("B-spline Face was not found")
|
||||
|
||||
|
||||
def _specs(info: dict[str, object], selected_kind: str = "face") -> list[dict[str, object]]:
|
||||
probe = _PropertySpecProbe(int(info.get("face_id", 0)), selected_kind=selected_kind)
|
||||
specs, _used = probe._editable_property_specs(info)
|
||||
return specs
|
||||
|
||||
|
||||
def _property_rows(info: dict[str, object], selected_kind: str = "face") -> list[dict[str, object]]:
|
||||
probe = _PropertySpecProbe(int(info.get("face_id", 0)), selected_kind=selected_kind)
|
||||
return probe._property_editor_specs(info, info)
|
||||
|
||||
|
||||
def _assert_no_editable_actions(specs: list[dict[str, object]], label: str) -> None:
|
||||
editable = [
|
||||
str(spec.get("key"))
|
||||
for spec in specs
|
||||
if bool(spec.get("editable")) and bool(spec.get("enabled")) and bool(spec.get("action"))
|
||||
]
|
||||
if editable:
|
||||
raise SystemExit(f"{label} should not expose editable actions for a freeform Face: {editable}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
with tempfile.TemporaryDirectory(prefix="geom_param_freeform_guard_") as temp_dir:
|
||||
path = Path(temp_dir) / "freeform_patch.step"
|
||||
_write_b_spline_patch(path)
|
||||
model = StepModel.load(path)
|
||||
face_id = _freeform_face_id(model)
|
||||
|
||||
quick_info = model.quick_face_info(face_id)
|
||||
info = model.face_info(face_id)
|
||||
for label, selected_info in (("quick", quick_info), ("full", info)):
|
||||
if selected_info.get("surface") != "b-spline surface":
|
||||
raise SystemExit(f"{label} info should identify a B-spline surface: {selected_info}")
|
||||
if selected_info.get("freeform_face_status") != "blocked":
|
||||
raise SystemExit(f"{label} info should block freeform Face editing: {selected_info}")
|
||||
if selected_info.get("recognition_risk") != "blocked":
|
||||
raise SystemExit(f"{label} recognition risk should be blocked: {selected_info}")
|
||||
blockers = str(selected_info.get("freeform_face_blockers") or "")
|
||||
if "自由曲面" not in blockers or "历史参数" not in blockers:
|
||||
raise SystemExit(f"{label} blocker should explain the missing stable history parameter: {selected_info}")
|
||||
|
||||
editable_specs = _specs(info)
|
||||
editable_keys = {str(spec.get("key")) for spec in editable_specs}
|
||||
if "freeform_surface_edit_semantics" not in editable_keys:
|
||||
raise SystemExit(f"freeform Face should expose a read-only edit semantics row: {editable_specs}")
|
||||
forbidden = {
|
||||
"area",
|
||||
"local_face_width",
|
||||
"local_face_height",
|
||||
"face_center_position",
|
||||
"face_target_normal_position",
|
||||
"cone_reference_radius",
|
||||
"sphere_radius",
|
||||
"torus_major_radius",
|
||||
}
|
||||
leaked = sorted(forbidden & editable_keys)
|
||||
if leaked:
|
||||
raise SystemExit(f"freeform Face should not expose parametric edit rows: {leaked}")
|
||||
_assert_no_editable_actions(editable_specs, "face mode editable specs")
|
||||
|
||||
feature_rows = _property_rows(info, selected_kind="feature")
|
||||
feature_keys = {str(spec.get("key")) for spec in feature_rows}
|
||||
if "no_editable_feature_dimensions" not in feature_keys:
|
||||
raise SystemExit(f"feature mode should say there are no reliable dimensions: {feature_rows}")
|
||||
if "freeform_surface_edit_semantics" not in feature_keys:
|
||||
raise SystemExit(f"feature mode should keep the freeform limitation explanation: {feature_rows}")
|
||||
_assert_no_editable_actions(feature_rows, "feature mode rows")
|
||||
|
||||
print(
|
||||
"freeform Face guard ok: "
|
||||
f"face_id={face_id}, surface={info.get('surface')}, "
|
||||
f"risk={info.get('recognition_risk')}, specs={sorted(editable_keys)}"
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user