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
+46 -19
View File
@@ -149,9 +149,9 @@ class WindowCoreMixin:
self.interactor.AddObserver("StartInteractionEvent", self.on_camera_interaction_start)
self.interactor.AddObserver("EndInteractionEvent", self.on_camera_interaction_end)
if hasattr(self.interactor, "SetDesiredUpdateRate"):
self.interactor.SetDesiredUpdateRate(24.0)
self.interactor.SetDesiredUpdateRate(60.0)
if hasattr(self.interactor, "SetStillUpdateRate"):
self.interactor.SetStillUpdateRate(0.2)
self.interactor.SetStillUpdateRate(1.0)
light = vtk.vtkLight()
light.SetLightTypeToHeadlight()
@@ -252,7 +252,8 @@ class WindowCoreMixin:
"show_internal_edges": show_internal_edges,
}
if build_polydata:
result["model_polydata"] = new_model.build_face_polydata(deflection=deflection)
face_polydata = new_model.build_face_polydata(deflection=deflection)
result["model_polydata"] = _smooth_surface_polydata(face_polydata)
result["edge_polydata"] = new_model.build_edge_polydata(
deflection=deflection,
show_same_domain_internal_edges=show_internal_edges,
@@ -297,8 +298,8 @@ class WindowCoreMixin:
@Slot(object)
def _start_initial_load_worker(self, expected_path: Path) -> None:
# Qt/VTK/OCCT visualization objects are not safe to construct from this
# worker path in the current PySide build. Keep the old entry point as a
# main-thread fallback so stale signal paths cannot crash the process.
# worker path in the current PySide build. Keep this as a main-thread
# fallback so stale signal paths cannot crash the process.
self._run_deferred_initial_load(expected_path)
def _load_step_sync(
@@ -348,7 +349,7 @@ class WindowCoreMixin:
if polydata is None:
return None
copied = vtk.vtkPolyData()
copied.DeepCopy(polydata)
copied.ShallowCopy(polydata)
return copied
def _detach_worker_polydata_result(self, result: dict[str, object]) -> dict[str, object]:
@@ -1027,8 +1028,14 @@ class WindowCoreMixin:
self.renderer.RemoveAllViewProps()
self.renderer.SetBackground(*VIEW_BACKGROUND_COLOR)
self.scene_isolated = False
self.highlight_signature = None
self.model_polydata = _smooth_surface_polydata(model_polydata)
point_normals = None
try:
point_normals = model_polydata.GetPointData().GetNormals()
except Exception:
point_normals = None
self.model_polydata = model_polydata if point_normals is not None else _smooth_surface_polydata(model_polydata)
self.model_face_id_array = self.model_polydata.GetCellData().GetArray("face_id")
self.model_part_id_array = self.model_polydata.GetCellData().GetArray("part_id")
self.model_solid_id_array = self.model_polydata.GetCellData().GetArray("solid_id")
@@ -1569,7 +1576,7 @@ class WindowCoreMixin:
elif action == "resize_shell_thickness":
self._set_selection_mode("Feature")
self.select_feature(int(target_id))
self.statusBar().showMessage(f"已选择可调整薄壁厚度候选Face {target_id}")
self.statusBar().showMessage(f"已选择可调整壳体厚度候选Face {target_id}")
elif action == "suppress_cylinder":
self._set_selection_mode("Feature")
self.select_feature(int(target_id))
@@ -1601,7 +1608,7 @@ class WindowCoreMixin:
else:
self._set_selection_mode("Face")
self.select_face(int(target_id))
self.statusBar().showMessage(f"已选择可拉平面Face {target_id}")
self.statusBar().showMessage(f"已选择可拉伸/切除平面Face {target_id}")
def select_by_id(self, kind: str | None = None) -> None:
if self.model is None:
@@ -1712,18 +1719,23 @@ class WindowCoreMixin:
highlight_face_ids = _int_values(info.get("feature_highlight_face_ids")) or [face_id]
else:
highlight_face_ids = [face_id]
logical_id = int(info.get("logical_face_id", face_id))
selection_fields = self._selection_identity_fields(
face_id,
"特征来源 Face" if feature_mode else "Face",
)
logical_id = int(selection_fields["selection_display_id"])
input_info = dict(info)
input_info.update(selection_fields)
self._sync_cylindrical_edit_inputs(input_info)
self.selected_part_id = int(info["part_id"])
self.selected_solid_id = int(info["solid_id"]) if int(info["solid_id"]) >= 0 else None
self._highlight_faces(face_ids=highlight_face_ids or [face_id])
self._show_pick_marker(pick_position)
self._sync_id_picker("Feature" if feature_mode else "Face", face_id if feature_mode else logical_id)
self._sync_id_picker("Feature" if feature_mode else "Face", logical_id)
self.set_info(self._with_pick_info(input_info, pick_position))
self._update_action_states()
raw_note = f"(拓扑Face {face_id}" if not feature_mode and logical_id != face_id else ""
message = f"已选择Face {logical_id}{raw_note}" if not feature_mode else f"已选择特征来源Face {face_id}"
raw_note = f"当前拓扑 Face {face_id}" if logical_id != face_id else ""
message = f"已选择Face {logical_id}{raw_note}" if not feature_mode else f"已选择特征来源 Face {logical_id}{raw_note}"
self.statusBar().showMessage(self._selection_status(message, pick_position))
def select_feature(self, face_id: int, pick_position: tuple[float, float, float] | None = None) -> None:
@@ -1733,6 +1745,9 @@ class WindowCoreMixin:
return
info = self._feature_context_info(face_id)
info["kind"] = "feature"
selection_fields = self._selection_identity_fields(face_id, "特征来源 Face")
logical_id = int(selection_fields["selection_display_id"])
info.update(selection_fields)
self._reset_selection(clear_highlight=False, clear_info=False)
self.selected_kind = "feature"
self.selected_face_id = face_id
@@ -1743,11 +1758,12 @@ class WindowCoreMixin:
highlight_face_ids = _int_values(info.get("feature_highlight_face_ids"))
self._highlight_faces(face_ids=highlight_face_ids or [face_id])
self._show_pick_marker(pick_position)
self._sync_id_picker("Feature", face_id)
self._sync_id_picker("Feature", logical_id)
self.set_info(self._with_pick_info(info, pick_position))
feature_type = str(info.get("feature_type", "特征候选"))
self._update_action_states()
self.statusBar().showMessage(self._selection_status(f"已选择 {feature_type},来源Face {face_id}", pick_position))
raw_note = f"(当前拓扑 Face {face_id}" if logical_id != face_id else ""
self.statusBar().showMessage(self._selection_status(f"已选择 {feature_type},来源 Face {logical_id}{raw_note}", pick_position))
def _sync_cylindrical_edit_inputs(self, info: dict[str, object]) -> None:
fillet_radius_suggestion: str | None = None
@@ -1927,8 +1943,13 @@ class WindowCoreMixin:
def _highlight_faces(self, face_ids=None, part_ids=None) -> None:
if self.model is None:
return
face_key = _int_tuple_or_none(face_ids)
part_key = _int_tuple_or_none(part_ids)
signature = ("faces", face_key, part_key)
if signature == getattr(self, "highlight_signature", None) and self.highlight_actor is not None:
return
self._clear_highlight()
polydata = self._cached_face_overlay_polydata(face_ids=face_ids, part_ids=part_ids, smooth=True)
polydata = self._cached_face_overlay_polydata(face_ids=face_key, part_ids=part_key, smooth=True)
if polydata is None:
return
mapper = vtk.vtkPolyDataMapper()
@@ -1945,12 +1966,16 @@ class WindowCoreMixin:
actor.GetProperty().SetLineWidth(2)
self._offset_overlay_actor_toward_camera(actor, scale=0.00035)
self.highlight_actor = actor
self.highlight_signature = signature
self.renderer.AddActor(actor)
self.render_window.Render()
def _highlight_edge(self, edge_id: int) -> None:
if self.model is None:
return
signature = ("edge", int(edge_id))
if signature == getattr(self, "highlight_signature", None) and self.edge_highlight_actor is not None:
return
self._clear_highlight()
polydata = self._cached_edge_overlay_polydata(edge_id)
if polydata is None:
@@ -1965,6 +1990,7 @@ class WindowCoreMixin:
actor.GetProperty().SetDiffuse(0.8)
actor.GetProperty().SetLineWidth(5)
self.edge_highlight_actor = actor
self.highlight_signature = signature
self.renderer.AddActor(actor)
self.render_window.Render()
@@ -2090,6 +2116,7 @@ class WindowCoreMixin:
if self.pick_marker_actor is not None:
self.renderer.RemoveActor(self.pick_marker_actor)
self.pick_marker_actor = None
self.highlight_signature = None
def clear_edit_preview(self, render: bool = True) -> None:
if self.edit_preview_timer is not None:
@@ -2138,7 +2165,7 @@ class WindowCoreMixin:
if polydata is None:
raise RuntimeError("当前显示网格里没有可复用的选中 Face 预览数据")
except Exception as exc:
self.statusBar().showMessage(f"拉预览不可用:{exc}")
self.statusBar().showMessage(f"伸/切除预览不可用:{exc}")
return
if distance >= 0:
color = (0.0, 0.86, 0.34)
@@ -2172,9 +2199,9 @@ class WindowCoreMixin:
if polydata is None:
polydata = self._cached_face_overlay_polydata(face_ids=[face_id], smooth=False)
if polydata is None:
raise RuntimeError("当前显示网格里没有可复用的薄壁预览数据")
raise RuntimeError("当前显示网格里没有可复用的壳体预览数据")
except Exception as exc:
self.statusBar().showMessage(f"薄壁厚度调整预览不可用:{exc}")
self.statusBar().showMessage(f"壳体厚度调整预览不可用:{exc}")
return
movement = _triple_or_none(plan.get("shell_desired_movement_vector")) if isinstance(plan, dict) else None
if movement is None and isinstance(plan, dict):