feat: 完善 Face 一级编辑与隔离计算
This commit is contained in:
+12
-8
@@ -151,6 +151,7 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
|
||||
self.edge_actor = None
|
||||
self.highlight_actor = None
|
||||
self.edge_highlight_actor = None
|
||||
self.highlight_signature: tuple[object, ...] | None = None
|
||||
self.hover_face_actor = None
|
||||
self.hover_edge_actor = None
|
||||
self.hover_signature: tuple[str, int] | None = None
|
||||
@@ -238,6 +239,9 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
|
||||
self.last_id_kind = "Feature"
|
||||
self.feature_detection_level = "current-only"
|
||||
self.property_editor_updating = False
|
||||
self._control_state_cache: dict[int, tuple[bool, str]] = {}
|
||||
self._selected_action_info_cache_key: tuple[object, ...] | None = None
|
||||
self._selected_action_info_cache_value: dict[str, object] | None = None
|
||||
self.property_editor_specs: list[dict[str, object]] = []
|
||||
self.property_table_expanded = False
|
||||
self.property_table_collapsed_rows = 6
|
||||
@@ -835,7 +839,7 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
|
||||
self.show_internal_edges_checkbox = QCheckBox("显示同域内部边")
|
||||
help_tip(
|
||||
self.show_internal_edges_checkbox,
|
||||
"显示同一平面或同一圆柱面内部的拓扑分割边。关闭时会隐藏布尔推拉后常见的视觉接缝线。",
|
||||
"显示同一平面或同一圆柱面内部的拓扑分割边。关闭时会隐藏布尔拉伸/切除后常见的视觉接缝线。",
|
||||
)
|
||||
self.show_internal_edges_checkbox.toggled.connect(self._on_internal_edges_toggled)
|
||||
view_layout.addWidget(self.show_internal_edges_checkbox)
|
||||
@@ -981,9 +985,9 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
|
||||
edit_layout = QGridLayout(edit_box)
|
||||
edit_layout.addWidget(QLabel("移动距离"), 0, 0)
|
||||
self.offset_input = QLineEdit("5.0")
|
||||
help_tip(self.offset_input, "平面推拉距离。正数通常向外加料,负数通常向内切削;单位沿用 STEP 模型单位。")
|
||||
help_tip(self.offset_input, "平面拉伸/切除距离。正数通常向外加料,负数通常向内切削;单位沿用 STEP 模型单位。")
|
||||
edit_layout.addWidget(self.offset_input, 0, 1)
|
||||
self.push_button = QPushButton("推拉平面")
|
||||
self.push_button = QPushButton("拉伸/切除平面")
|
||||
help_tip(self.push_button, "移动当前选中的平面区域:正数加料,负数切削。会先显示半透明预览,再后台执行。")
|
||||
self.push_button.clicked.connect(self.push_pull_face)
|
||||
edit_layout.addWidget(self.push_button, 1, 0, 1, 2)
|
||||
@@ -1081,10 +1085,10 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
|
||||
help_tip(self.face_area_input, "目标面面积。当前选中对象表会使用这个隐藏输入执行所属特征或 Solid 的均匀缩放。")
|
||||
self.face_width_input = QLineEdit("", edit_box)
|
||||
self.face_width_input.setVisible(False)
|
||||
help_tip(self.face_width_input, "目标面宽。当前选中对象表会使用这个隐藏输入修改 Face 平面内第一个方向的尺寸。")
|
||||
help_tip(self.face_width_input, "目标U向尺寸。当前选中对象表会使用这个隐藏输入修改 Face 平面内第一个方向的尺寸。")
|
||||
self.face_height_input = QLineEdit("", edit_box)
|
||||
self.face_height_input.setVisible(False)
|
||||
help_tip(self.face_height_input, "目标面高。当前选中对象表会使用这个隐藏输入修改 Face 平面内第二个方向的尺寸。")
|
||||
help_tip(self.face_height_input, "目标V向尺寸。当前选中对象表会使用这个隐藏输入修改 Face 平面内第二个方向的尺寸。")
|
||||
self.resize_boss_button = QPushButton("调整圆柱凸台直径")
|
||||
help_tip(self.resize_boss_button, "修改完整圆柱凸台直径。变大会加料,变小会重建凸台区域。")
|
||||
self.resize_boss_button.clicked.connect(self.resize_boss)
|
||||
@@ -1214,14 +1218,14 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
|
||||
self.resize_edge_length_button.clicked.connect(self.resize_any_edge_length)
|
||||
edit_layout.addWidget(self.resize_edge_length_button, 19, 0, 1, 2)
|
||||
|
||||
edit_layout.addWidget(QLabel("薄壁厚度"), 20, 0)
|
||||
edit_layout.addWidget(QLabel("壳体厚度"), 20, 0)
|
||||
self.shell_thickness_input = QLineEdit("")
|
||||
help_tip(
|
||||
self.shell_thickness_input,
|
||||
"薄壁/壳体局部区域的目标厚度。选中有相对平面的平面候选后会自动填一个参考值。",
|
||||
"壳体局部区域的目标厚度。选中有相对平面的平面候选后会自动填一个参考值。",
|
||||
)
|
||||
edit_layout.addWidget(self.shell_thickness_input, 20, 1)
|
||||
self.resize_shell_thickness_button = QPushButton("调整薄壁厚度")
|
||||
self.resize_shell_thickness_button = QPushButton("调整壳体厚度")
|
||||
help_tip(
|
||||
self.resize_shell_thickness_button,
|
||||
"移动当前平面区域来达到目标厚度。当前版本基于相对平面估算,执行前会预览并可回滚。",
|
||||
|
||||
@@ -42,6 +42,15 @@ SNAPSHOT_FACE_LOGICAL_IDS_KEY = "__face_logical_ids__"
|
||||
|
||||
FACE_SELECTION_FEATURE_INFO_SURFACES = {"plane", "cylinder", "cone", "sphere", "torus"}
|
||||
|
||||
FREEFORM_FACE_SURFACES = {
|
||||
"bezier surface",
|
||||
"b-spline surface",
|
||||
"surface of revolution",
|
||||
"surface of extrusion",
|
||||
"offset surface",
|
||||
"other surface",
|
||||
}
|
||||
|
||||
CURVE_TYPES = {
|
||||
GeomAbs_Line: "line",
|
||||
GeomAbs_Circle: "circle",
|
||||
|
||||
@@ -536,7 +536,7 @@ class FeatureMixin:
|
||||
"confidence": item["confidence"],
|
||||
"note": (
|
||||
"这是完整圆柱凸台候选;点击后会选中该 face,可在当前选中对象里修改凸台高度。"
|
||||
"执行时会推拉识别到的端盖 Face。"
|
||||
"执行时会拉伸/切除识别到的端盖 Face。"
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -681,11 +681,11 @@ class FeatureMixin:
|
||||
confidence = "pending"
|
||||
risk = "medium"
|
||||
status = "caution"
|
||||
note = "快速扫描:推拉方向会在选中Face或执行编辑前再详细判断。"
|
||||
note = "快速扫描:拉伸/切除方向会在选中Face或执行编辑前再详细判断。"
|
||||
candidates.append(
|
||||
{
|
||||
"operation_key": "push_pull_plane",
|
||||
"operation": "推拉平面",
|
||||
"operation": "拉伸/切除平面",
|
||||
"target_kind": "face",
|
||||
"target_id": face_id,
|
||||
"face_id": face_id,
|
||||
@@ -710,7 +710,7 @@ class FeatureMixin:
|
||||
candidates.append(
|
||||
{
|
||||
"operation_key": "resize_shell_thickness",
|
||||
"operation": "调整薄壁厚度",
|
||||
"operation": "调整壳体厚度",
|
||||
"target_kind": "face",
|
||||
"target_id": face_id,
|
||||
"face_id": face_id,
|
||||
@@ -725,7 +725,7 @@ class FeatureMixin:
|
||||
"confidence": shell_confidence,
|
||||
"note": (
|
||||
"快速扫描:已找到投影重叠的相对平面;点击后会填入参考目标厚度,"
|
||||
"执行时会移动当前平面区域来改变局部薄壁/壳体厚度。"
|
||||
"执行时会移动当前平面区域来改变局部壳体厚度。"
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -2591,7 +2591,7 @@ class FeatureMixin:
|
||||
if not bottom_face_ids:
|
||||
return {
|
||||
"context_status": "blocked",
|
||||
"context_message": "当前版本的孔深调整需要疑似底面;如果自动识别失败,请手动填写底面Face ID。",
|
||||
"context_message": "当前版本的孔深调整需要疑似底面;如果自动识别失败,请手动填写底面 Face ID。",
|
||||
}
|
||||
|
||||
cyl = surf.Cylinder()
|
||||
|
||||
@@ -22,6 +22,8 @@ from .workers import EditWorker, ScanWorker
|
||||
|
||||
class InfoPanelMixin:
|
||||
def set_info(self, info: dict[str, object]) -> None:
|
||||
if hasattr(self, "_invalidate_selected_action_info_cache"):
|
||||
self._invalidate_selected_action_info_cache()
|
||||
self.current_info_values = dict(info)
|
||||
self.current_info_text = _info_to_text(info)
|
||||
self.info_text.setPlainText(self.current_info_text)
|
||||
@@ -44,6 +46,8 @@ class InfoPanelMixin:
|
||||
self._update_action_states()
|
||||
|
||||
def set_plain_info(self, text: str) -> None:
|
||||
if hasattr(self, "_invalidate_selected_action_info_cache"):
|
||||
self._invalidate_selected_action_info_cache()
|
||||
self.current_info_values = {}
|
||||
self.current_info_text = text
|
||||
self.info_tree.clear()
|
||||
@@ -262,6 +266,14 @@ class InfoPanelMixin:
|
||||
if self.selected_kind == "solid" and self.selected_solid_id is not None:
|
||||
return f"solid {self.selected_solid_id}"
|
||||
if self.selected_kind in {"face", "feature"} and self.selected_face_id is not None:
|
||||
for key in ("selection_display_id", "face_region_logical_id", "logical_face_id"):
|
||||
value = self.current_info_values.get(key)
|
||||
if value in {None, ""}:
|
||||
continue
|
||||
try:
|
||||
return f"face {int(value)}"
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return f"face {self.selected_face_id}"
|
||||
if self.selected_kind == "edge" and self.selected_edge_id is not None:
|
||||
return f"edge {self.selected_edge_id}"
|
||||
|
||||
@@ -8,6 +8,19 @@ from pathlib import Path
|
||||
from .model import StepModel
|
||||
|
||||
|
||||
def _optional_int(value: object) -> int | None:
|
||||
if value is None or value == "":
|
||||
return None
|
||||
return int(value)
|
||||
|
||||
|
||||
def _point3(value: object, operation: str) -> tuple[float, float, float]:
|
||||
point = list(value) if isinstance(value, (list, tuple)) else []
|
||||
if len(point) != 3:
|
||||
raise ValueError(f"{operation} requires a 3D target center.")
|
||||
return (float(point[0]), float(point[1]), float(point[2]))
|
||||
|
||||
|
||||
def _execute(model: StepModel, operation: str, args: list[object]) -> str:
|
||||
if operation == "push_pull_face":
|
||||
return model.push_pull_face(int(args[0]), float(args[1]))
|
||||
@@ -47,6 +60,73 @@ def _execute(model: StepModel, operation: str, args: list[object]) -> str:
|
||||
return model.resize_spherical_radius(int(args[0]), float(args[1]))
|
||||
if operation == "resize_torus_radius":
|
||||
return model.resize_toroidal_radius(int(args[0]), float(args[1]), str(args[2]))
|
||||
if operation == "resize_cylindrical_hole":
|
||||
return model.resize_cylindrical_hole(int(args[0]), float(args[1]))
|
||||
if operation == "resize_cylindrical_owning_scale":
|
||||
return model.resize_cylindrical_owning_scale(int(args[0]), float(args[1]))
|
||||
if operation == "move_cylindrical_hole_axis":
|
||||
return model.move_cylindrical_hole_axis(int(args[0]), _point3(args[1], operation))
|
||||
if operation == "suppress_cylindrical_hole":
|
||||
return model.suppress_cylindrical_hole(int(args[0]))
|
||||
if operation == "resize_cylindrical_depth":
|
||||
bottom_face_id = _optional_int(args[2]) if len(args) > 2 else None
|
||||
return model.resize_cylindrical_depth(int(args[0]), float(args[1]), bottom_face_id=bottom_face_id)
|
||||
if operation == "resize_cylindrical_depth_owning_scale":
|
||||
bottom_face_id = _optional_int(args[2]) if len(args) > 2 else None
|
||||
return model.resize_cylindrical_depth_owning_scale(int(args[0]), float(args[1]), bottom_face_id=bottom_face_id)
|
||||
if operation == "move_cylindrical_slot_axis":
|
||||
return model.move_cylindrical_slot_axis(int(args[0]), _point3(args[1], operation))
|
||||
if operation == "resize_cylindrical_slot_width":
|
||||
pair_face_id = _optional_int(args[2]) if len(args) > 2 else None
|
||||
return model.resize_cylindrical_slot_width(int(args[0]), float(args[1]), pair_face_id=pair_face_id)
|
||||
if operation == "resize_cylindrical_slot_depth":
|
||||
pair_face_id = _optional_int(args[2]) if len(args) > 2 else None
|
||||
return model.resize_cylindrical_slot_depth(int(args[0]), float(args[1]), pair_face_id=pair_face_id)
|
||||
if operation == "resize_cylindrical_slot_arc_length":
|
||||
pair_face_id = _optional_int(args[2]) if len(args) > 2 else None
|
||||
return model.resize_cylindrical_slot_arc_length(int(args[0]), float(args[1]), pair_face_id=pair_face_id)
|
||||
if operation == "resize_cylindrical_slot_angular_span":
|
||||
return model.resize_cylindrical_slot_angular_span(int(args[0]), float(args[1]))
|
||||
if operation == "resize_cylindrical_slot_total_length":
|
||||
pair_face_id = _optional_int(args[2]) if len(args) > 2 else None
|
||||
return model.resize_cylindrical_slot_total_length(int(args[0]), float(args[1]), pair_face_id=pair_face_id)
|
||||
if operation == "resize_cylindrical_slot_center_distance":
|
||||
pair_face_id = _optional_int(args[2]) if len(args) > 2 else None
|
||||
return model.resize_cylindrical_slot_center_distance(
|
||||
int(args[0]),
|
||||
float(args[1]),
|
||||
pair_face_id=pair_face_id,
|
||||
)
|
||||
if operation == "resize_general_edge_length":
|
||||
anchor_mode = str(args[2]) if len(args) > 2 else "auto"
|
||||
strategy_mode = str(args[3]) if len(args) > 3 else "auto"
|
||||
return model.resize_general_edge_length(
|
||||
int(args[0]),
|
||||
float(args[1]),
|
||||
anchor_mode=anchor_mode,
|
||||
strategy_mode=strategy_mode,
|
||||
)
|
||||
if operation == "move_edge_endpoint":
|
||||
return model.move_edge_endpoint(int(args[0]), str(args[1]), _point3(args[2], operation))
|
||||
if operation == "move_edge_center":
|
||||
return model.move_edge_center(int(args[0]), _point3(args[1], operation))
|
||||
if operation == "move_circular_edge_axis_center":
|
||||
return model.move_circular_edge_axis_center(int(args[0]), _point3(args[1], operation))
|
||||
if operation == "resize_ellipse_edge_axis_radius":
|
||||
axis_kind = str(args[2]) if len(args) > 2 else "major"
|
||||
return model.resize_ellipse_edge_axis_radius(int(args[0]), float(args[1]), axis_kind=axis_kind)
|
||||
if operation == "resize_existing_fillet":
|
||||
return model.resize_existing_fillet(int(args[0]), float(args[1]))
|
||||
if operation == "fillet_edge":
|
||||
return model.fillet_edge(int(args[0]), float(args[1]))
|
||||
if operation == "chamfer_edge":
|
||||
return model.chamfer_edge(int(args[0]), float(args[1]))
|
||||
if operation == "chamfer_edge_asymmetric":
|
||||
reference_face_id = _optional_int(args[3]) if len(args) > 3 else None
|
||||
return model.chamfer_edge_asymmetric(int(args[0]), float(args[1]), float(args[2]), reference_face_id)
|
||||
if operation == "chamfer_edge_distance_angle":
|
||||
reference_face_id = _optional_int(args[3]) if len(args) > 3 else None
|
||||
return model.chamfer_edge_distance_angle(int(args[0]), float(args[1]), float(args[2]), reference_face_id)
|
||||
raise ValueError(f"Unsupported isolated edit operation: {operation}")
|
||||
|
||||
|
||||
|
||||
+70
-34
@@ -74,7 +74,7 @@ from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool
|
||||
from OCC.Core.gp import gp_Ax1, gp_Ax2, gp_Dir, gp_Pnt, gp_Trsf, gp_Vec
|
||||
from OCC.Extend.TopologyUtils import TopologyExplorer, discretize_edge
|
||||
|
||||
from .constants import CURVE_TYPES, SNAPSHOT_FACE_LOGICAL_IDS_KEY, SURFACE_TYPES
|
||||
from .constants import CURVE_TYPES, FREEFORM_FACE_SURFACES, SNAPSHOT_FACE_LOGICAL_IDS_KEY, SURFACE_TYPES
|
||||
from .export import ExportMixin
|
||||
from .features import FeatureMixin
|
||||
from .operations import OperationMixin
|
||||
@@ -352,19 +352,20 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
info["normal"] = _dir_tuple(direction)
|
||||
info["oriented_normal"] = _oriented_dir_tuple(direction, face)
|
||||
info["push_pull_confidence"] = "unchecked"
|
||||
info["push_pull_note"] = "快速选择阶段不判断材料内外方向;执行推拉时会重新计算。"
|
||||
cap_direction = self._cylindrical_cap_push_pull_direction(face_id, surf)
|
||||
if cap_direction is not None:
|
||||
info.update(cap_direction)
|
||||
info["push_pull_note"] = "快速选择阶段不判断材料内外方向;执行拉伸/切除时会重新计算。"
|
||||
# Keep ordinary selection cheap. Cylindrical-cap direction can
|
||||
# scan adjacent surfaces and cost hundreds of ms on large STEP
|
||||
# files; the full push/pull plan recomputes it when the user
|
||||
# actually edits.
|
||||
info["push_pull_status"] = "candidate"
|
||||
info["feature_type"] = "可推拉平面候选"
|
||||
info["feature_type"] = "可拉伸/切除平面候选"
|
||||
info["feature_source_face_id"] = face_id
|
||||
info["feature_highlight_face_ids"] = (face_id,)
|
||||
info["feature_edit_actions"] = "推拉平面"
|
||||
info["feature_edit_actions"] = "拉伸/切除平面"
|
||||
if bool(info.get("has_inner_boundaries")):
|
||||
info["local_face_deform_ready"] = False
|
||||
info["local_face_deform_face_count"] = 1
|
||||
info["local_face_deform_blocker"] = "当前 Face 有内孔/内边界;请优先使用推拉当前面、孔或槽的专门修改入口。"
|
||||
info["local_face_deform_blocker"] = "当前 Face 有内孔/内边界;请优先使用拉伸/切除、孔或槽的专门修改入口。"
|
||||
else:
|
||||
info["local_face_deform_ready"] = True
|
||||
info["local_face_deform_face_count"] = 1
|
||||
@@ -430,6 +431,8 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
info["minor_radius"] = torus.MinorRadius()
|
||||
info["feature_torus_major_radius"] = torus.MajorRadius()
|
||||
info["feature_torus_minor_radius"] = torus.MinorRadius()
|
||||
elif surface_label in FREEFORM_FACE_SURFACES:
|
||||
info.update(self._freeform_face_limit_fields(surface_label))
|
||||
|
||||
info.update(self._recognition_summary_fields(info))
|
||||
self._quick_face_info_cache[face_id] = dict(info)
|
||||
@@ -528,10 +531,33 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
info["axis"] = _dir_tuple(torus.Axis().Direction())
|
||||
info["major_radius"] = torus.MajorRadius()
|
||||
info["minor_radius"] = torus.MinorRadius()
|
||||
elif str(info.get("surface") or "") in FREEFORM_FACE_SURFACES:
|
||||
info.update(self._freeform_face_limit_fields(str(info.get("surface") or "")))
|
||||
info.update(self._recognition_summary_fields(info))
|
||||
self._face_info_cache[face_id] = dict(info)
|
||||
return dict(info)
|
||||
|
||||
def _freeform_face_limit_fields(self, surface: str) -> dict[str, object]:
|
||||
blocker = (
|
||||
"当前 Face 是自由曲面/非基础解析曲面;STEP 里通常没有可直接修改的历史参数,"
|
||||
"当前一级阶段不开放面积、尺寸、半径或偏移类参数化修改。"
|
||||
"请先保留为只读诊断,后续需要专门的曲面控制点或曲面替换语义。"
|
||||
)
|
||||
return {
|
||||
"feature_type": "自由曲面 Face(暂不支持参数化编辑)",
|
||||
"feature_edit_actions": "只读诊断;不开放参数化修改",
|
||||
"freeform_face_status": "blocked",
|
||||
"freeform_face_risk": "blocked",
|
||||
"freeform_face_blockers": blocker,
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": 1,
|
||||
"local_face_deform_blocker": blocker,
|
||||
"recognition_confidence": "low",
|
||||
"recognition_risk": "blocked",
|
||||
"recognition_blockers": blocker,
|
||||
"note": f"{surface} 当前按自由曲面处理,不会伪装成平面、圆柱、圆锥、球面或环面参数。",
|
||||
}
|
||||
|
||||
def face_surface_kind(self, face_id: int) -> str:
|
||||
if face_id < 0 or face_id >= len(self.faces):
|
||||
return ""
|
||||
@@ -588,12 +614,14 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
confidence = "high"
|
||||
elif surface == "torus" and _float_or_none(info.get("major_radius")) is not None and _float_or_none(info.get("minor_radius")) is not None:
|
||||
confidence = "high"
|
||||
if surface in FREEFORM_FACE_SURFACES:
|
||||
confidence = "low"
|
||||
|
||||
risk_rank = {"low": 0, "medium": 1, "high": 2, "blocked": 3}
|
||||
capability_specs = (
|
||||
("resize_status", "resize_risk", "resize_blockers", "孔/槽/圆柱直径"),
|
||||
("push_pull_status", "push_pull_risk", "push_pull_blockers", "平面推拉"),
|
||||
("shell_status", "shell_risk", "shell_blockers", "薄壁厚度"),
|
||||
("push_pull_status", "push_pull_risk", "push_pull_blockers", "平面拉伸/切除"),
|
||||
("shell_status", "shell_risk", "shell_blockers", "壳体厚度"),
|
||||
("cylinder_resize_status", "cylinder_resize_risk", "cylinder_resize_blockers", "圆柱直径/半径"),
|
||||
("boss_resize_status", "boss_resize_risk", "boss_resize_blockers", "圆柱凸台直径/高度/轴心"),
|
||||
("depth_status", "depth_risk", "depth_blockers", "盲孔/盲槽深度"),
|
||||
@@ -611,8 +639,8 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
has_planar_push_pull_candidate = (
|
||||
surface == "plane"
|
||||
and (
|
||||
"推拉" in feature_type_text
|
||||
or "推拉" in feature_actions_text
|
||||
"拉伸/切除" in feature_type_text
|
||||
or "拉伸/切除" in feature_actions_text
|
||||
or str(info.get("push_pull_status") or "").strip() == "candidate"
|
||||
)
|
||||
)
|
||||
@@ -632,10 +660,13 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
for key in (
|
||||
"risk",
|
||||
"first_level_topology_risk",
|
||||
"freeform_face_risk",
|
||||
):
|
||||
value = str(info.get(key) or "").strip()
|
||||
if risk_rank.get(value, -1) > risk_rank.get(risk, -1):
|
||||
risk = value
|
||||
if surface in FREEFORM_FACE_SURFACES:
|
||||
risk = "blocked"
|
||||
for status_key, risk_key, _blocker_key, _label in capability_specs:
|
||||
value = str(info.get(risk_key) or "").strip()
|
||||
if value == "blocked" and has_available_capability:
|
||||
@@ -696,11 +727,11 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
bottom_count = len(tuple(info.get("feature_bottom_face_ids") or ()))
|
||||
except TypeError:
|
||||
bottom_count = 0
|
||||
add("bottom_faces", f"疑似底面Face={bottom_count}")
|
||||
add("bottom_faces", f"疑似底面 Face={bottom_count}")
|
||||
if info.get("slot_status") == "candidate":
|
||||
add("slot_geometry", "部分圆柱槽/半孔几何")
|
||||
if info.get("shell_region_status") == "candidate":
|
||||
add("shell_opposite_face", "找到相对平面/薄壁候选")
|
||||
add("shell_opposite_face", "找到相对平面/壳体候选")
|
||||
|
||||
ready_actions: list[str] = []
|
||||
limited_actions: list[str] = []
|
||||
@@ -724,13 +755,13 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
add_action(limited_actions, label)
|
||||
|
||||
if has_planar_push_pull_candidate:
|
||||
add_action(ready_actions, "平面推拉")
|
||||
add_action(ready_actions, "平面拉伸/切除")
|
||||
if has_local_face_deform:
|
||||
add_action(ready_actions, "当前面面积/面宽/面高/中心/偏移")
|
||||
add_action(ready_actions, "当前面面积/U向尺寸/V向尺寸/中心/偏移")
|
||||
elif str(info.get("local_face_deform_blocker") or "").strip():
|
||||
add_action(limited_actions, "当前面局部尺寸/中心/偏移")
|
||||
add_action(limited_actions, "局部重建尺寸/中心/偏移")
|
||||
if has_shell_candidate:
|
||||
add_action(ready_actions, "薄壁厚度")
|
||||
add_action(ready_actions, "壳体厚度")
|
||||
if has_slot_candidate:
|
||||
add_action(ready_actions, "槽/半孔宽度/深度/弧长")
|
||||
if surface == "cone" and feature_type_text:
|
||||
@@ -1079,25 +1110,25 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
shell_info = self._planar_shell_region_info(face_id, coplanar_face_ids, info)
|
||||
prismatic_info = self._planar_rectangular_profile_info(face_id, coplanar_face_ids, info, shell_info)
|
||||
if len(coplanar_face_ids) > 1:
|
||||
scope_note = f"已检测到 {len(coplanar_face_ids)} 个共面且相接/重叠的 face,推拉时会作为同一片平面区域处理。"
|
||||
scope_note = f"已检测到 {len(coplanar_face_ids)} 个共面且相接/重叠的 face,拉伸/切除时会作为同一片平面区域处理。"
|
||||
else:
|
||||
scope_note = "当前 face 没有检测到可一起推拉的共面相接/重叠邻居。"
|
||||
edit_actions = "推拉平面"
|
||||
scope_note = "当前 face 没有检测到可一起拉伸/切除的共面相接/重叠邻居。"
|
||||
edit_actions = "拉伸/切除平面"
|
||||
if shell_info.get("shell_region_status") == "candidate":
|
||||
edit_actions += ";调整薄壁/壳体厚度"
|
||||
edit_actions += ";调整壳体厚度"
|
||||
if prismatic_info.get("prismatic_profile_status") == "candidate":
|
||||
edit_actions = "调整规则矩形轮廓长度/宽度"
|
||||
if prismatic_info.get("prismatic_extrusion_status") == "candidate":
|
||||
edit_actions += ";调整棱柱高度/凹槽深度"
|
||||
else:
|
||||
edit_actions += ";沿法向推拉"
|
||||
edit_actions += ";沿法向拉伸/切除"
|
||||
highlight_face_ids = set(coplanar_face_ids)
|
||||
highlight_face_ids.update(_int_values(prismatic_info.get("prismatic_highlight_face_ids")))
|
||||
result = dict(info)
|
||||
result.update(
|
||||
{
|
||||
"kind": "feature",
|
||||
"feature_type": "可推拉平面候选",
|
||||
"feature_type": "可拉伸/切除平面候选",
|
||||
"feature_source_face_id": face_id,
|
||||
"feature_face_ids": tuple(coplanar_face_ids),
|
||||
"feature_highlight_face_ids": tuple(coplanar_face_ids),
|
||||
@@ -1363,7 +1394,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
except Exception:
|
||||
return {
|
||||
"shell_region_status": "not-detected",
|
||||
"shell_region_note": "无法读取当前平面,不能估算薄壁/壳体区域。",
|
||||
"shell_region_note": "无法读取当前平面,不能估算壳体区域。",
|
||||
}
|
||||
if source_surf.GetType() != GeomAbs_Plane:
|
||||
return {}
|
||||
@@ -1382,7 +1413,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
if source_interval is None:
|
||||
return {
|
||||
"shell_region_status": "not-detected",
|
||||
"shell_region_note": "当前平面区域缺少稳定投影范围,不能估算薄壁/壳体厚度。",
|
||||
"shell_region_note": "当前平面区域缺少稳定投影范围,不能估算壳体厚度。",
|
||||
}
|
||||
|
||||
def interval_length(interval: tuple[float, float]) -> float:
|
||||
@@ -1440,7 +1471,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
if best is None:
|
||||
return {
|
||||
"shell_region_status": "not-detected",
|
||||
"shell_region_note": "未找到与当前平面投影重叠的相对平面;暂不能估算局部壳体/薄壁厚度。",
|
||||
"shell_region_note": "未找到与当前平面投影重叠的相对平面;暂不能估算局部壳体厚度。",
|
||||
}
|
||||
|
||||
thickness = float(best["shell_thickness_estimate"])
|
||||
@@ -1454,7 +1485,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
"shell_opposite_face_id": best["shell_opposite_face_id"],
|
||||
"shell_thickness_estimate": thickness,
|
||||
"shell_overlap_ratio_estimate": overlap_ratio,
|
||||
"shell_region_note": "相对平面距离明显大于当前面的局部短边,不按薄壁厚度处理。",
|
||||
"shell_region_note": "相对平面距离明显大于当前面的局部短边,不按壳体厚度处理。",
|
||||
}
|
||||
thin_ratio = thickness / diagonal
|
||||
if overlap_ratio >= 0.55 and thin_ratio <= 0.08:
|
||||
@@ -1471,7 +1502,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
"shell_source_face_ids": tuple(valid_region_ids),
|
||||
**best,
|
||||
"shell_note": (
|
||||
"通过同一 solid 内投影重叠的相对平面估算薄壁/壳体厚度;"
|
||||
"通过同一 solid 内投影重叠的相对平面估算壳体厚度;"
|
||||
"这是 B-Rep 几何近似,不等同于原 CAD 壳命令参数。"
|
||||
),
|
||||
}
|
||||
@@ -1501,18 +1532,23 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
opening_face_ids = end_faces["opening_face_ids"]
|
||||
guess = str(info.get("feature_guess", "cylindrical face"))
|
||||
has_two_axial_caps = bool(end_faces["start_end_face_ids"] and end_faces["end_end_face_ids"])
|
||||
support_face_ids_for_round = sorted(set(adjacent_face_ids) - set(end_face_ids))
|
||||
material_toward = str(info.get("material_toward_axis", "") or "")
|
||||
material_away = str(info.get("material_away_axis", "") or "")
|
||||
complete_cylinder_with_caps = combined_angular_span >= math.tau * 0.92 and has_two_axial_caps
|
||||
if (
|
||||
guess == "round/fillet candidate"
|
||||
and has_two_axial_caps
|
||||
and material_toward == "inside"
|
||||
and "outside" in material_away
|
||||
and (complete_cylinder_with_caps or len(support_face_ids_for_round) < 2)
|
||||
):
|
||||
info = dict(info)
|
||||
info["feature_guess"] = "boss/outer-round candidate"
|
||||
info["confidence"] = "medium"
|
||||
info["note"] = "partial cylinder has material inside its axis and explicit planar caps at both ends"
|
||||
info["note"] = (
|
||||
"cocylindrical region has material inside its axis and explicit planar caps at both ends"
|
||||
)
|
||||
domain_info["feature_guess"] = info["feature_guess"]
|
||||
slot_info = self._cylindrical_slot_info(face_id, adjacent_face_ids, end_face_ids, domain_info)
|
||||
fillet_info = self._cylindrical_existing_fillet_info(face_id, adjacent_face_ids, end_face_ids, domain_info)
|
||||
@@ -2261,7 +2297,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": 1,
|
||||
"local_face_deform_blocker": "找不到当前 Face 所属 Solid,不能做“只改当前面”的局部重建。",
|
||||
"local_face_deform_blocker": "找不到当前 Face 所属 Solid,不能做局部重建。",
|
||||
}
|
||||
)
|
||||
solid_face_ids = [index for index, item in enumerate(self.face_solid_ids) if item == solid_id]
|
||||
@@ -2273,7 +2309,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 的 Face 数量超过 128,当前版本不开放“只改当前面”的局部重建。",
|
||||
"local_face_deform_blocker": "所属 Solid 的 Face 数量超过 128,当前版本不开放局部重建。",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2296,7 +2332,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 含有曲面,当前版本只对简单全平面 Solid 开放“只改当前面”。",
|
||||
"local_face_deform_blocker": "所属 Solid 含有曲面,当前版本只对简单全平面 Solid 开放局部重建。",
|
||||
}
|
||||
)
|
||||
wire_info = self._face_boundary_wire_info(face)
|
||||
@@ -2305,7 +2341,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
|
||||
{
|
||||
"local_face_deform_ready": False,
|
||||
"local_face_deform_face_count": face_count,
|
||||
"local_face_deform_blocker": "所属 Solid 里有带内孔/内边界的 Face,请优先使用孔、槽或推拉等专门修改方式。",
|
||||
"local_face_deform_blocker": "所属 Solid 里有带内孔/内边界的 Face,请优先使用孔、槽或拉伸/切除等专门修改方式。",
|
||||
}
|
||||
)
|
||||
try:
|
||||
|
||||
+238
-167
File diff suppressed because it is too large
Load Diff
+56
-47
@@ -18,6 +18,9 @@ INFO_GROUPS: list[tuple[str, list[str]]] = [
|
||||
"file",
|
||||
"part_id",
|
||||
"solid_id",
|
||||
"selection_title",
|
||||
"selection_display_id",
|
||||
"selection_topological_face_id",
|
||||
"logical_face_id",
|
||||
"face_region_logical_id",
|
||||
"topological_face_id",
|
||||
@@ -392,10 +395,13 @@ INFO_LABELS = {
|
||||
"file": "文件",
|
||||
"part_id": "零件 ID",
|
||||
"solid_id": "Solid ID",
|
||||
"selection_title": "当前选中对象",
|
||||
"selection_display_id": "逻辑选择 ID",
|
||||
"selection_topological_face_id": "当前拓扑 Face ID",
|
||||
"logical_face_id": "逻辑 Face ID",
|
||||
"face_region_logical_id": "面区域逻辑 ID",
|
||||
"topological_face_id": "拓扑 Face ID",
|
||||
"face_id": "Face ID",
|
||||
"face_id": "当前拓扑 Face ID",
|
||||
"face_region_ids": "面区域 Face",
|
||||
"face_region_count": "面区域 Face 数",
|
||||
"edge_id": "Edge ID",
|
||||
@@ -544,24 +550,24 @@ INFO_LABELS = {
|
||||
"center_of_mass": "重心",
|
||||
"surface_center": "表面积中心",
|
||||
"area_center": "面积中心",
|
||||
"local_face_width": "当前面宽",
|
||||
"local_face_height": "当前面高",
|
||||
"local_face_width_direction": "面宽 方向",
|
||||
"local_face_height_direction": "面高 方向",
|
||||
"local_face_size_center": "面宽/面高 中心",
|
||||
"face_size_axis": "面宽/面高 方向",
|
||||
"face_size_label": "面宽/面高 名称",
|
||||
"current_face_width": "当前面宽",
|
||||
"target_face_width": "目标面宽",
|
||||
"current_face_height": "当前面高",
|
||||
"target_face_height": "目标面高",
|
||||
"current_face_size": "当前面宽/面高",
|
||||
"target_face_size": "目标面宽/面高",
|
||||
"face_size_delta": "面宽/面高 变化量",
|
||||
"face_size_delta_ratio": "面宽/面高 变化比例",
|
||||
"face_size_scale": "面宽/面高 缩放比例",
|
||||
"face_size_center": "面宽/面高 缩放中心",
|
||||
"face_size_axis_direction": "面宽/面高 缩放方向",
|
||||
"local_face_width": "U向尺寸",
|
||||
"local_face_height": "V向尺寸",
|
||||
"local_face_width_direction": "U向方向",
|
||||
"local_face_height_direction": "V向方向",
|
||||
"local_face_size_center": "U/V 尺寸中心",
|
||||
"face_size_axis": "U/V 尺寸方向",
|
||||
"face_size_label": "U/V 尺寸名称",
|
||||
"current_face_width": "当前 U向尺寸",
|
||||
"target_face_width": "目标 U向尺寸",
|
||||
"current_face_height": "当前 V向尺寸",
|
||||
"target_face_height": "目标 V向尺寸",
|
||||
"current_face_size": "当前 U/V 尺寸",
|
||||
"target_face_size": "目标 U/V 尺寸",
|
||||
"face_size_delta": "U/V 尺寸变化量",
|
||||
"face_size_delta_ratio": "U/V 尺寸变化比例",
|
||||
"face_size_scale": "U/V 尺寸缩放比例",
|
||||
"face_size_center": "U/V 尺寸缩放中心",
|
||||
"face_size_axis_direction": "U/V 尺寸缩放方向",
|
||||
"owning_face_size_rebuild_mode": "所属对象尺寸重建模式",
|
||||
"length_center": "长度中心",
|
||||
"bbox_min": "包围盒最小点",
|
||||
@@ -574,7 +580,7 @@ INFO_LABELS = {
|
||||
"end_face_id": "端面 Face",
|
||||
"end_face_label": "端面位置",
|
||||
"end_face_plane_distance": "端面匹配距离",
|
||||
"push_pull_distance": "端面推拉距离",
|
||||
"push_pull_distance": "拉伸/切除距离",
|
||||
"normal": "几何法向",
|
||||
"oriented_normal": "拓扑修正法向",
|
||||
"plane_origin": "平面原点",
|
||||
@@ -585,37 +591,37 @@ INFO_LABELS = {
|
||||
"rotation_axis": "旋转轴",
|
||||
"end_face_outward_direction": "端面向外方向",
|
||||
"desired_movement_vector": "目标移动向量",
|
||||
"push_pull_outward_direction": "推拉向外方向",
|
||||
"push_pull_inward_direction": "推拉向内方向",
|
||||
"push_pull_outward_direction": "拉伸向外方向",
|
||||
"push_pull_inward_direction": "切除向内方向",
|
||||
"push_pull_plus_side": "原始法向侧",
|
||||
"push_pull_minus_side": "反向法向侧",
|
||||
"push_pull_confidence": "推拉方向置信度",
|
||||
"push_pull_note": "推拉方向说明",
|
||||
"push_pull_status": "推拉状态",
|
||||
"push_pull_risk": "推拉风险",
|
||||
"push_pull_message": "推拉说明",
|
||||
"push_pull_scope_face_ids": "推拉共面区域 Face",
|
||||
"push_pull_scope_face_count": "推拉共面区域 Face 数",
|
||||
"push_pull_scope_area": "推拉共面区域面积",
|
||||
"push_pull_scope_note": "推拉共面区域说明",
|
||||
"shell_region_kind": "壳体/薄壁候选类型",
|
||||
"shell_region_status": "壳体/薄壁识别状态",
|
||||
"shell_confidence": "壳体/薄壁置信度",
|
||||
"shell_thickness": "薄壁厚度",
|
||||
"shell_source_face_ids": "壳体/薄壁源平面 Face",
|
||||
"push_pull_confidence": "拉伸/切除方向置信度",
|
||||
"push_pull_note": "拉伸/切除方向说明",
|
||||
"push_pull_status": "拉伸/切除状态",
|
||||
"push_pull_risk": "拉伸/切除风险",
|
||||
"push_pull_message": "拉伸/切除说明",
|
||||
"push_pull_scope_face_ids": "拉伸/切除共面区域 Face",
|
||||
"push_pull_scope_face_count": "拉伸/切除共面区域 Face 数",
|
||||
"push_pull_scope_area": "拉伸/切除共面区域面积",
|
||||
"push_pull_scope_note": "拉伸/切除共面区域说明",
|
||||
"shell_region_kind": "壳体候选类型",
|
||||
"shell_region_status": "壳体识别状态",
|
||||
"shell_confidence": "壳体置信度",
|
||||
"shell_thickness": "壳体厚度",
|
||||
"shell_source_face_ids": "壳体源平面 Face",
|
||||
"shell_opposite_face_id": "相对平面 Face",
|
||||
"shell_thickness_estimate": "薄壁厚度估算",
|
||||
"shell_current_thickness": "当前薄壁厚度",
|
||||
"shell_target_thickness": "目标薄壁厚度",
|
||||
"shell_delta_thickness": "薄壁厚度变化量",
|
||||
"shell_delta_ratio": "薄壁厚度变化比例",
|
||||
"shell_signed_thickness": "有符号薄壁厚度",
|
||||
"shell_thickness_estimate": "壳体厚度估算",
|
||||
"shell_current_thickness": "当前壳体厚度",
|
||||
"shell_target_thickness": "目标壳体厚度",
|
||||
"shell_delta_thickness": "壳体厚度变化量",
|
||||
"shell_delta_ratio": "壳体厚度变化比例",
|
||||
"shell_signed_thickness": "有符号壳体厚度",
|
||||
"shell_overlap_ratio_estimate": "相对平面重叠率估算",
|
||||
"shell_opposite_normal_dot": "相对平面法向点积",
|
||||
"shell_desired_movement_vector": "薄壁目标移动向量",
|
||||
"shell_movement_alignment": "薄壁移动方向匹配度",
|
||||
"shell_note": "壳体/薄壁识别说明",
|
||||
"shell_region_note": "壳体/薄壁识别说明",
|
||||
"shell_desired_movement_vector": "壳体目标移动向量",
|
||||
"shell_movement_alignment": "壳体移动方向匹配度",
|
||||
"shell_note": "壳体识别说明",
|
||||
"shell_region_note": "壳体识别说明",
|
||||
"u_range": "U 参数范围",
|
||||
"v_range": "V 参数范围",
|
||||
"first_parameter": "起始参数",
|
||||
@@ -700,6 +706,9 @@ INFO_LABELS = {
|
||||
"existing_fillet_arc_length_estimate": "已有圆角圆弧长度估算",
|
||||
"existing_fillet_note": "已有圆角识别说明",
|
||||
"feature_edit_actions": "当前可用操作",
|
||||
"freeform_face_status": "自由曲面状态",
|
||||
"freeform_face_risk": "自由曲面风险",
|
||||
"freeform_face_blockers": "自由曲面限制",
|
||||
"resize_status": "切削状态",
|
||||
"resize_strategy": "编辑策略",
|
||||
"edit_strategy_label": "编辑策略说明",
|
||||
@@ -940,7 +949,7 @@ def _edit_quality_warnings(before_part_stats, after_part_stats) -> list[str]:
|
||||
warnings.append(
|
||||
"目标零件Solid数发生变化:"
|
||||
f"{before_part_stats.solids} -> {after_part_stats.solids}。"
|
||||
"如果这是一次局部推拉/孔径修改,请重点检查导出后是否仍是一体实体。"
|
||||
"如果这是一次局部拉伸/切除/孔径修改,请重点检查导出后是否仍是一体实体。"
|
||||
)
|
||||
return warnings
|
||||
|
||||
|
||||
+435
-194
File diff suppressed because it is too large
Load Diff
+46
-19
@@ -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):
|
||||
|
||||
+305
-169
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user