feat: 完善 Face 一级关系编辑和稳定性校验

This commit is contained in:
2026-08-04 18:15:29 +08:00
parent 5799d5d813
commit a76282d7dd
28 changed files with 6872 additions and 270 deletions
+65 -1
View File
@@ -3,6 +3,11 @@ from __future__ import annotations
from pathlib import Path
import sys
from OCC.Core.BRep import BRep_Tool
from OCC.Core.TopAbs import TopAbs_VERTEX
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopoDS import topods
PROJECT_ROOT = Path(__file__).resolve().parent.parent
if str(PROJECT_ROOT) not in sys.path:
@@ -24,13 +29,21 @@ def _center(info: dict[str, object]) -> tuple[float, float, float]:
def _top_plane_face(model: StepModel) -> int:
return _extreme_plane_face(model, highest=True)
def _bottom_plane_face(model: StepModel) -> int:
return _extreme_plane_face(model, highest=False)
def _extreme_plane_face(model: StepModel, *, highest: bool) -> int:
best: tuple[float, int] | None = None
for face_id in range(len(model.faces)):
info = model.face_info(face_id)
if info.get("surface") != "plane":
continue
center = _center(info)
if best is None or center[2] > best[0]:
if best is None or (center[2] > best[0] if highest else center[2] < best[0]):
best = (center[2], face_id)
if best is None:
raise SystemExit("No planar Face found.")
@@ -52,6 +65,33 @@ def _assert_close(actual: float, expected: float, label: str) -> None:
raise SystemExit(f"{label}: expected {expected:g}, got {actual:g}")
def _edge_vertex_points(model: StepModel, edge_id: int) -> list[tuple[float, float, float]]:
points: list[tuple[float, float, float]] = []
explorer = TopExp_Explorer(model.edges[edge_id], TopAbs_VERTEX)
while explorer.More():
vertex = topods.Vertex(explorer.Current())
point = BRep_Tool.Pnt(vertex)
item = (float(point.X()), float(point.Y()), float(point.Z()))
if not any(
abs(item[0] - existing[0]) <= TOLERANCE
and abs(item[1] - existing[1]) <= TOLERANCE
and abs(item[2] - existing[2]) <= TOLERANCE
for existing in points
):
points.append(item)
explorer.Next()
return points
def _assert_points_z(points: object, expected: float, label: str) -> None:
if not isinstance(points, (tuple, list)) or len(points) == 0:
raise SystemExit(f"{label}: expected non-empty 3D points, got {points!r}")
for index, point in enumerate(points):
if not isinstance(point, (tuple, list)) or len(point) != 3:
raise SystemExit(f"{label}: point {index} is not a 3D coordinate: {point!r}")
_assert_close(float(point[2]), expected, f"{label} point {index} Z")
def _assert_first_level_topology(model: StepModel, face_id: int) -> dict[str, object]:
topology = model.face_first_level_topology(face_id)
if topology.get("topology_relation_depth") != 1:
@@ -149,10 +189,34 @@ def main() -> int:
_assert_close(value, 6.0, f"First-level side Face {index} center Z")
_assert_close(z_values[-1], 12.0, "Moved source Face center Z")
moved_face_id = _top_plane_face(model)
moved_topology = _assert_first_level_topology(model, moved_face_id)
_assert_points_z(
moved_topology.get("first_level_boundary_vertex_points"),
12.0,
"Moved Face first-level boundary Vertex",
)
for edge_id in moved_topology.get("first_level_boundary_edge_ids", ()):
_assert_points_z(
_edge_vertex_points(model, int(edge_id)),
12.0,
f"Moved Face first-level boundary Edge {edge_id}",
)
bottom_face_id = _bottom_plane_face(model)
bottom_topology = model.face_first_level_topology(bottom_face_id)
_assert_points_z(
bottom_topology.get("first_level_boundary_vertex_points"),
0.0,
"Second-level bottom Face boundary Vertex",
)
print(f"model={DEFAULT_MODEL}")
print(f"face_id={face_id}")
print(f"topology={topology}")
print(f"z_values_after_local_move={z_values}")
print(f"moved_face_id={moved_face_id}")
print(f"moved_boundary_edges={moved_topology.get('first_level_boundary_edge_ids')}")
print(result.encode("ascii", "backslashreplace").decode("ascii"))
return 0