feat: 完善 Face 一级编辑与隔离计算

This commit is contained in:
2026-08-05 15:08:16 +08:00
parent a76282d7dd
commit ed1faf51d8
28 changed files with 3740 additions and 876 deletions
+136
View File
@@ -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())