feat: 完善 Face 一级关系编辑和稳定性校验
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user