feat: 完善 Face 参数化编辑和隔离执行
This commit is contained in:
+156
-2
@@ -9,7 +9,12 @@ from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface
|
||||
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut, BRepAlgoAPI_Defeaturing, BRepAlgoAPI_Fuse
|
||||
from OCC.Core.BRepBndLib import brepbndlib
|
||||
from OCC.Core.BOPAlgo import BOPAlgo_GlueFull
|
||||
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeVertex, BRepBuilderAPI_Transform
|
||||
from OCC.Core.BRepBuilderAPI import (
|
||||
BRepBuilderAPI_MakeFace,
|
||||
BRepBuilderAPI_MakeVertex,
|
||||
BRepBuilderAPI_MakeWire,
|
||||
BRepBuilderAPI_Transform,
|
||||
)
|
||||
from OCC.Core.BRepExtrema import BRepExtrema_DistShapeShape
|
||||
from OCC.Core.BRepCheck import BRepCheck_Analyzer
|
||||
from OCC.Core.BRepClass3d import BRepClass3d_SolidClassifier
|
||||
@@ -58,6 +63,7 @@ from OCC.Core.TopAbs import (
|
||||
TopAbs_OUT,
|
||||
TopAbs_REVERSED,
|
||||
TopAbs_SOLID,
|
||||
TopAbs_WIRE,
|
||||
)
|
||||
from OCC.Core.TopExp import TopExp_Explorer, topexp
|
||||
from OCC.Core.TopLoc import TopLoc_Location
|
||||
@@ -104,6 +110,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
self._face_edge_ids_cache: dict[int, list[int]] = {}
|
||||
self._edge_face_ids_cache: dict[int, list[int]] = {}
|
||||
self._same_domain_face_ids_cache: dict[int, list[int]] = {}
|
||||
self._local_face_deform_readiness_cache: dict[int, dict[str, object]] = {}
|
||||
self._edge_duplicate_key_ids_cache: dict[tuple[object, ...], list[int]] | None = None
|
||||
self._same_domain_internal_edge_ids_cache: set[int] | None = None
|
||||
self._same_domain_duplicate_edge_ids_cache: set[int] | None = None
|
||||
@@ -184,6 +191,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
self._face_edge_ids_cache.clear()
|
||||
self._edge_face_ids_cache.clear()
|
||||
self._same_domain_face_ids_cache.clear()
|
||||
self._local_face_deform_readiness_cache.clear()
|
||||
self._edge_duplicate_key_ids_cache = None
|
||||
self._same_domain_internal_edge_ids_cache = None
|
||||
self._same_domain_duplicate_edge_ids_cache = None
|
||||
@@ -275,6 +283,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
self._face_info_cache.clear()
|
||||
self._feature_info_cache.clear()
|
||||
self._same_domain_face_ids_cache.clear()
|
||||
self._local_face_deform_readiness_cache.clear()
|
||||
|
||||
def quick_face_info(self, face_id: int) -> dict[str, object]:
|
||||
if face_id < 0 or face_id >= len(self.faces):
|
||||
@@ -310,6 +319,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
"selection_info_note": "快速选择信息;同域面、端盖、底面等深层识别会在执行编辑计划或手动扫描时再计算。",
|
||||
}
|
||||
info.update(_shape_bounds_info(face))
|
||||
info.update(self._face_boundary_wire_info(face))
|
||||
if surface_type == GeomAbs_Plane:
|
||||
plane = surf.Plane()
|
||||
direction = plane.Axis().Direction()
|
||||
@@ -328,6 +338,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
info["feature_source_face_id"] = face_id
|
||||
info["feature_highlight_face_ids"] = (face_id,)
|
||||
info["feature_edit_actions"] = "推拉平面"
|
||||
info.update(self._local_face_deform_readiness(face_id))
|
||||
info.update(
|
||||
self._local_face_plane_size_info(
|
||||
face_id,
|
||||
@@ -420,6 +431,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
"boundary_edges": boundary_edges,
|
||||
}
|
||||
info.update(_shape_bounds_info(face))
|
||||
info.update(self._face_boundary_wire_info(face))
|
||||
if surface_type == GeomAbs_Plane:
|
||||
plane = surf.Plane()
|
||||
direction = plane.Axis().Direction()
|
||||
@@ -433,6 +445,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
info["push_pull_minus_side"] = push_pull_direction["minus_side_state"]
|
||||
info["push_pull_confidence"] = push_pull_direction["confidence"]
|
||||
info["push_pull_note"] = push_pull_direction["note"]
|
||||
info.update(self._local_face_deform_readiness(face_id))
|
||||
info.update(
|
||||
self._local_face_plane_size_info(
|
||||
face_id,
|
||||
@@ -1128,6 +1141,112 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
self._face_edge_ids_cache[face_id] = list(edge_ids)
|
||||
return edge_ids
|
||||
|
||||
def _face_boundary_wire_info(self, face: TopoDS_Shape) -> dict[str, object]:
|
||||
try:
|
||||
boundary_wires = len(_explore(face, TopAbs_WIRE))
|
||||
except Exception:
|
||||
boundary_wires = 0
|
||||
inner_boundary_wires = max(boundary_wires - 1, 0)
|
||||
return {
|
||||
"boundary_wires": boundary_wires,
|
||||
"inner_boundary_wires": inner_boundary_wires,
|
||||
"has_inner_boundaries": inner_boundary_wires > 0,
|
||||
}
|
||||
|
||||
def _local_face_deform_readiness(self, face_id: int) -> dict[str, object]:
|
||||
if face_id < 0 or face_id >= len(self.faces):
|
||||
return {
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_blocker": "Face ID 不存在,不能做局部 Face 变形。",
|
||||
}
|
||||
solid_id = self.face_solid_ids[face_id] if face_id < len(self.face_solid_ids) else -1
|
||||
cache_key = solid_id if solid_id >= 0 else -(face_id + 1)
|
||||
cached = self._local_face_deform_readiness_cache.get(cache_key)
|
||||
if cached is not None:
|
||||
return dict(cached)
|
||||
|
||||
def remember(info: dict[str, object]) -> dict[str, object]:
|
||||
self._local_face_deform_readiness_cache[cache_key] = dict(info)
|
||||
return dict(info)
|
||||
|
||||
if solid_id < 0 or solid_id >= len(self.solids):
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": 1,
|
||||
"local_face_deform_blocker": "找不到当前 Face 所属 Solid,不能做“只改当前面”的局部重建。",
|
||||
}
|
||||
)
|
||||
solid_face_ids = [index for index, item in enumerate(self.face_solid_ids) if item == solid_id]
|
||||
if not solid_face_ids:
|
||||
solid_face_ids = [face_id]
|
||||
face_count = len(solid_face_ids)
|
||||
if face_count > 128:
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 的 Face 数量超过 128,当前版本不开放“只改当前面”的局部重建。",
|
||||
}
|
||||
)
|
||||
|
||||
source_shape = self.solids[solid_id][1] if 0 <= solid_id < len(self.solids) else self.faces[face_id]
|
||||
tolerance = max(_shape_diagonal(source_shape) * 1e-7, 1e-6)
|
||||
for item in solid_face_ids:
|
||||
face = self.faces[item]
|
||||
try:
|
||||
surface = BRepAdaptor_Surface(face)
|
||||
except Exception:
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 里有 Face 不能稳定读取曲面类型,不能做局部 Face 变形。",
|
||||
}
|
||||
)
|
||||
if surface.GetType() != GeomAbs_Plane:
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 含有曲面,当前版本只对简单全平面 Solid 开放“只改当前面”。",
|
||||
}
|
||||
)
|
||||
wire_info = self._face_boundary_wire_info(face)
|
||||
if bool(wire_info.get("has_inner_boundaries")):
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 里有带内孔/内边界的 Face,请优先使用孔、槽或推拉等专门修改方式。",
|
||||
}
|
||||
)
|
||||
try:
|
||||
if len(self._local_deform_face_vertex_points(face, tolerance)) < 3:
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 里有 Face 顶点环不能稳定读取,不能做局部 Face 变形。",
|
||||
}
|
||||
)
|
||||
except Exception:
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 里有 Face 顶点环读取失败,不能做局部 Face 变形。",
|
||||
}
|
||||
)
|
||||
|
||||
return remember(
|
||||
{
|
||||
"local_face_deform_ready": True,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "",
|
||||
}
|
||||
)
|
||||
|
||||
def face_boundary_edge_ids(self, face_id: int) -> list[int]:
|
||||
if face_id < 0 or face_id >= len(self.faces):
|
||||
return []
|
||||
@@ -1166,6 +1285,22 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
self._face_info_cache.pop(face_id, None)
|
||||
self._feature_info_cache.pop(face_id, None)
|
||||
|
||||
def assign_logical_face_region_exclusive(self, logical_id: int, face_ids: Iterable[int]) -> None:
|
||||
valid_face_ids = sorted({int(face_id) for face_id in face_ids if 0 <= int(face_id) < len(self.faces)})
|
||||
if not valid_face_ids:
|
||||
return
|
||||
logical_id = int(logical_id)
|
||||
replacement_id = max([logical_id, len(self.faces), *[int(item) for item in self.face_logical_ids]], default=logical_id) + 1
|
||||
for face_id, current_logical_id in enumerate(list(self.face_logical_ids)):
|
||||
if int(current_logical_id) != logical_id or face_id in valid_face_ids:
|
||||
continue
|
||||
self.face_logical_ids[face_id] = replacement_id
|
||||
replacement_id += 1
|
||||
self._quick_face_info_cache.pop(face_id, None)
|
||||
self._face_info_cache.pop(face_id, None)
|
||||
self._feature_info_cache.pop(face_id, None)
|
||||
self.assign_logical_face_region(logical_id, valid_face_ids)
|
||||
|
||||
def nearest_edge_id_to_point(
|
||||
self,
|
||||
edge_ids: Iterable[int],
|
||||
@@ -1487,7 +1622,26 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
return sorted(edge_id for edge_id, count in counts.items() if count == 1)
|
||||
|
||||
def _push_pull_profile_shape(self, face_ids: Iterable[int]) -> TopoDS_Shape:
|
||||
profile_faces = [self.faces[face_id] for face_id in face_ids if 0 <= face_id < len(self.faces)]
|
||||
valid_face_ids = [int(face_id) for face_id in face_ids if 0 <= int(face_id) < len(self.faces)]
|
||||
if not valid_face_ids:
|
||||
raise ValueError("No planar faces were found for push/pull.")
|
||||
if len(valid_face_ids) > 1:
|
||||
boundary_edge_ids = self._region_boundary_edge_ids(valid_face_ids)
|
||||
if boundary_edge_ids:
|
||||
try:
|
||||
wire = BRepBuilderAPI_MakeWire()
|
||||
for edge_id in boundary_edge_ids:
|
||||
wire.Add(topods.Edge(self.edges[edge_id]))
|
||||
if not hasattr(wire, "IsDone") or wire.IsDone():
|
||||
face_builder = BRepBuilderAPI_MakeFace(wire.Wire())
|
||||
if not hasattr(face_builder, "IsDone") or face_builder.IsDone():
|
||||
profile = face_builder.Face()
|
||||
if not profile.IsNull():
|
||||
_ensure_valid_shape(profile)
|
||||
return profile
|
||||
except Exception:
|
||||
pass
|
||||
profile_faces = [self.faces[face_id] for face_id in valid_face_ids]
|
||||
if not profile_faces:
|
||||
raise ValueError("No planar faces were found for push/pull.")
|
||||
return _unify_same_domain_shape(_compound_from_shapes(profile_faces))
|
||||
|
||||
Reference in New Issue
Block a user