feat: 完善 Edge 一级编辑与 CAD 建模语义
This commit is contained in:
+332
-39
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
import math
|
||||
from pathlib import Path
|
||||
import time
|
||||
from PySide6.QtCore import Qt, QThread, QTimer, Slot
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
@@ -29,6 +30,8 @@ PROPERTY_TARGET_COLUMN = 3
|
||||
PROPERTY_ACTION_COLUMN = 4
|
||||
|
||||
FEATURE_EDIT_SEMANTICS_KEYS = {
|
||||
"cad_modeling_form",
|
||||
"cad_recommended_operation",
|
||||
"face_first_level_topology",
|
||||
"cylindrical_feature_first_level_topology",
|
||||
"slot_edit_semantics",
|
||||
@@ -338,6 +341,59 @@ class WindowStateMixin:
|
||||
**self._first_level_fact_selection_fields(face_id, scope="cylindrical-feature"),
|
||||
}
|
||||
|
||||
def _edge_first_level_selection_fields(self, edge_id: int) -> dict[str, object]:
|
||||
if self.model is None:
|
||||
return {}
|
||||
try:
|
||||
topology = self.model.edge_first_level_topology(edge_id)
|
||||
facts = self.model.edge_first_level_facts(edge_id)
|
||||
except Exception as exc:
|
||||
return {
|
||||
"topology_relation_depth": 1,
|
||||
"topology_relation_model": "STEP/B-Rep edge first-level",
|
||||
"topology_relation_status": "unavailable",
|
||||
"topology_relation_message": str(exc),
|
||||
"topology_ignored_relation_depths": ("second-level", "third-level", "deeper"),
|
||||
"topology_ignored_relation_note": "Current Edge first-level topology is unavailable.",
|
||||
"first_level_vertex_count": 0,
|
||||
"first_level_adjacent_edge_count": 0,
|
||||
"first_level_adjacent_face_count": 0,
|
||||
"first_level_fact_model": "STEP/B-Rep first-level fact graph",
|
||||
"first_level_fact_status": "unavailable",
|
||||
"first_level_fact_source_model": "edge",
|
||||
"first_level_fact_relation_depth": 1,
|
||||
"first_level_fact_scope": "edge",
|
||||
"first_level_fact_subject_edge_ids": (edge_id,),
|
||||
"first_level_fact_subject_edge_count": 1,
|
||||
"first_level_fact_boundary_edge_count": 1,
|
||||
"first_level_fact_boundary_vertex_count": 0,
|
||||
"first_level_fact_adjacent_edge_count": 0,
|
||||
"first_level_fact_adjacent_face_count": 0,
|
||||
"first_level_fact_ignored_relation_depths": ("second-level", "third-level", "deeper"),
|
||||
"first_level_fact_summary": f"Current Edge first-level fact graph is unavailable: {exc}",
|
||||
}
|
||||
return {
|
||||
"topology_relation_depth": topology.get("topology_relation_depth", 1),
|
||||
"topology_relation_model": topology.get("topology_relation_model"),
|
||||
"topology_relation_scope": topology.get("topology_relation_scope"),
|
||||
"topology_relation_boundary": topology.get("topology_relation_boundary"),
|
||||
"topology_relation_status": "ready",
|
||||
"topology_ignored_relation_depths": topology.get("topology_ignored_relation_depths", ()),
|
||||
"topology_ignored_relation_note": topology.get("topology_ignored_relation_note", ""),
|
||||
"selected_edge_ids": topology.get("selected_edge_ids", (edge_id,)),
|
||||
"selected_edge_count": topology.get("selected_edge_count", 1),
|
||||
"first_level_vertex_points": topology.get("first_level_vertex_points", ()),
|
||||
"first_level_vertex_count": topology.get("first_level_vertex_count", 0),
|
||||
"first_level_adjacent_edge_ids": topology.get("first_level_adjacent_edge_ids", ()),
|
||||
"first_level_adjacent_edge_count": topology.get("first_level_adjacent_edge_count", 0),
|
||||
"first_level_edge_ids": topology.get("first_level_edge_ids", (edge_id,)),
|
||||
"first_level_edge_count": topology.get("first_level_edge_count", 1),
|
||||
"first_level_adjacent_face_ids": topology.get("first_level_adjacent_face_ids", ()),
|
||||
"first_level_adjacent_face_count": topology.get("first_level_adjacent_face_count", 0),
|
||||
"first_level_topology_note": topology.get("first_level_topology_note", ""),
|
||||
**facts,
|
||||
}
|
||||
|
||||
def _feature_info_for_selected_face(self, face_id: int, fallback_info: dict[str, object]) -> dict[str, object]:
|
||||
if self.model is None:
|
||||
return dict(fallback_info)
|
||||
@@ -415,22 +471,28 @@ class WindowStateMixin:
|
||||
if self.model is None:
|
||||
return {}
|
||||
detection_level = self._current_feature_detection_level()
|
||||
if detection_level == "current-only":
|
||||
root_info = self._feature_info_for_selected_face(face_id, self.model.quick_face_info(face_id))
|
||||
else:
|
||||
root_info = self.model.feature_info(face_id)
|
||||
if str(root_info.get("surface", "") or "") == "plane":
|
||||
root_info = self._feature_info_for_selected_face(face_id, self.model.quick_face_info(face_id))
|
||||
if str(root_info.get("surface", "") or "") == "plane" and "topology_relation_depth" not in root_info:
|
||||
root_info.update(self._face_first_level_selection_fields(face_id))
|
||||
elif str(root_info.get("surface", "") or "") == "cylinder" and detection_level != "current-only":
|
||||
root_info.update(self._cylindrical_first_level_selection_fields(face_id))
|
||||
associated: list[dict[str, object]] = []
|
||||
if detection_level in {"associated-only", "secondary"}:
|
||||
try:
|
||||
associated = self.model.associated_feature_infos(face_id)
|
||||
associated = self.model.associated_feature_infos(
|
||||
face_id,
|
||||
max_depth=3,
|
||||
max_scan_faces=72,
|
||||
max_features=10,
|
||||
time_budget_seconds=0.55 if detection_level == "associated-only" else 0.75,
|
||||
lightweight=True,
|
||||
)
|
||||
except Exception:
|
||||
associated = []
|
||||
if detection_level == "secondary":
|
||||
associated = self._secondary_associated_feature_infos(face_id, associated)
|
||||
associated = self._secondary_associated_feature_infos(
|
||||
face_id,
|
||||
associated,
|
||||
time_budget_seconds=0.45,
|
||||
)
|
||||
|
||||
highlight_ids = set(_int_values(root_info.get("feature_highlight_face_ids")) or [face_id])
|
||||
for item in associated:
|
||||
@@ -509,11 +571,19 @@ class WindowStateMixin:
|
||||
self,
|
||||
source_face_id: int,
|
||||
direct_infos: list[dict[str, object]],
|
||||
*,
|
||||
time_budget_seconds: float | None = None,
|
||||
) -> list[dict[str, object]]:
|
||||
if self.model is None:
|
||||
return list(direct_infos)
|
||||
results: list[dict[str, object]] = []
|
||||
seen: set[tuple[str, tuple[int, ...]]] = set()
|
||||
deadline = None
|
||||
if time_budget_seconds is not None and time_budget_seconds > 0:
|
||||
deadline = time.monotonic() + float(time_budget_seconds)
|
||||
|
||||
def budget_expired() -> bool:
|
||||
return deadline is not None and time.monotonic() >= deadline
|
||||
|
||||
def add(info: dict[str, object]) -> None:
|
||||
source_id = _int_or_none(info.get("association_source_face_id"))
|
||||
@@ -529,17 +599,26 @@ class WindowStateMixin:
|
||||
for info in direct_infos:
|
||||
add(info)
|
||||
for info in list(results):
|
||||
if budget_expired():
|
||||
break
|
||||
parent_id = _int_or_none(info.get("association_source_face_id"))
|
||||
if parent_id is None:
|
||||
continue
|
||||
try:
|
||||
nested_budget = None
|
||||
if deadline is not None:
|
||||
nested_budget = max(0.02, min(0.12, deadline - time.monotonic()))
|
||||
for nested in self.model.associated_feature_infos(
|
||||
parent_id,
|
||||
max_depth=2,
|
||||
max_scan_faces=48,
|
||||
max_features=6,
|
||||
max_depth=1,
|
||||
max_scan_faces=16,
|
||||
max_features=4,
|
||||
time_budget_seconds=nested_budget,
|
||||
lightweight=True,
|
||||
):
|
||||
add(nested)
|
||||
if budget_expired():
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
results.sort(
|
||||
@@ -1293,7 +1372,7 @@ class WindowStateMixin:
|
||||
action_item = self._property_table_item(action_text, editable=False)
|
||||
target_item.setToolTip(self._property_target_tooltip(effective_spec, editable=editable))
|
||||
row_items = (label_item, current_item, scope_item, target_item, action_item)
|
||||
self._style_property_row_items(row_items, editable=editable)
|
||||
self._style_property_row_items(row_items, editable=editable, spec=effective_spec)
|
||||
for column, item in enumerate(row_items):
|
||||
item.setToolTip(item.toolTip() or item.text())
|
||||
self.property_table.setItem(row, column, item)
|
||||
@@ -1319,6 +1398,8 @@ class WindowStateMixin:
|
||||
|
||||
def _sort_property_specs_for_display(self, specs: list[dict[str, object]]) -> list[dict[str, object]]:
|
||||
def rank(spec: dict[str, object]) -> int:
|
||||
if bool(spec.get("pin_top")):
|
||||
return -1
|
||||
editable = bool(spec.get("editable"))
|
||||
enabled = bool(spec.get("enabled"))
|
||||
has_action = bool(spec.get("action"))
|
||||
@@ -1340,8 +1421,24 @@ class WindowStateMixin:
|
||||
items: tuple[QTableWidgetItem, QTableWidgetItem, QTableWidgetItem, QTableWidgetItem, QTableWidgetItem],
|
||||
*,
|
||||
editable: bool,
|
||||
spec: dict[str, object] | None = None,
|
||||
) -> None:
|
||||
label_item, current_item, scope_item, target_item, status_item = items
|
||||
if spec is not None and bool(spec.get("pin_top")):
|
||||
for item in items:
|
||||
item.setBackground(QColor("#eef6ff"))
|
||||
label_item.setForeground(QColor("#075985"))
|
||||
current_item.setForeground(QColor("#0f172a"))
|
||||
scope_item.setForeground(QColor("#0369a1"))
|
||||
target_item.setForeground(QColor("#64748b"))
|
||||
status_item.setForeground(QColor("#0369a1"))
|
||||
label_font = label_item.font()
|
||||
label_font.setBold(True)
|
||||
label_item.setFont(label_font)
|
||||
current_font = current_item.font()
|
||||
current_font.setBold(True)
|
||||
current_item.setFont(current_font)
|
||||
return
|
||||
if editable:
|
||||
row_backgrounds = ("#fff7ed", "#fffbeb", "#fff7ed", "#fff7ed", "#fff7ed")
|
||||
for item, color in zip(items, row_backgrounds):
|
||||
@@ -1421,8 +1518,8 @@ class WindowStateMixin:
|
||||
tip_key = "enabled_tip" if bool(mode.get("enabled", True)) else "disabled_tip"
|
||||
tip = str(mode.get(tip_key) or mode.get("enabled_tip") or mode.get("disabled_tip") or "").strip()
|
||||
if tip:
|
||||
return f"影响范围:{label}\n\n{tip}"
|
||||
return f"影响范围:{label}"
|
||||
return f"建模意图:{label}\n\n{tip}"
|
||||
return f"建模意图:{label}"
|
||||
|
||||
def _set_property_target_editor(self, row: int, spec: dict[str, object]) -> None:
|
||||
editor = QLineEdit(str(spec.get("target_text", "")))
|
||||
@@ -1632,10 +1729,15 @@ class WindowStateMixin:
|
||||
dimension["label"] = label_overrides[key]
|
||||
dimensions.append(dimension)
|
||||
|
||||
pinned_explanations = [
|
||||
dict(spec)
|
||||
for spec in specs
|
||||
if str(spec.get("key", "")) in FEATURE_EDIT_SEMANTICS_KEYS and bool(spec.get("pin_top"))
|
||||
]
|
||||
explanations = [
|
||||
dict(spec)
|
||||
for spec in specs
|
||||
if str(spec.get("key", "")) in FEATURE_EDIT_SEMANTICS_KEYS
|
||||
if str(spec.get("key", "")) in FEATURE_EDIT_SEMANTICS_KEYS and not bool(spec.get("pin_top"))
|
||||
]
|
||||
if not dimensions:
|
||||
dimensions.append(
|
||||
@@ -1654,7 +1756,7 @@ class WindowStateMixin:
|
||||
),
|
||||
}
|
||||
)
|
||||
return dimensions + explanations
|
||||
return pinned_explanations + dimensions + explanations
|
||||
|
||||
def _feature_context_property_specs(
|
||||
self,
|
||||
@@ -1675,6 +1777,7 @@ class WindowStateMixin:
|
||||
related_specs, _used = self._editable_property_specs(related_info)
|
||||
feature_label = str(related_info.get("feature_type") or related_info.get("feature_guess") or "关联特征")
|
||||
source_face_id = _int_or_none(related_info.get("association_source_face_id"))
|
||||
feature_label = str(related_info.get("association_label") or feature_label)
|
||||
for spec in self._feature_property_specs(related_specs, related_info):
|
||||
if spec.get("parameter_role") != "dimension":
|
||||
continue
|
||||
@@ -2149,6 +2252,7 @@ class WindowStateMixin:
|
||||
label: str,
|
||||
text: str,
|
||||
tip: str,
|
||||
pin_top: bool = False,
|
||||
) -> None:
|
||||
specs.append(
|
||||
{
|
||||
@@ -2161,9 +2265,196 @@ class WindowStateMixin:
|
||||
"enabled": False,
|
||||
"status_text": "说明",
|
||||
"disabled_tip": tip or text,
|
||||
"pin_top": pin_top,
|
||||
}
|
||||
)
|
||||
|
||||
def cad_modeling_form() -> tuple[str, str]:
|
||||
if has_face:
|
||||
if is_hole_or_groove:
|
||||
if is_slot_or_half_hole:
|
||||
return (
|
||||
"工程特征 / 槽:按槽宽、槽深、弧长、总长或轴心做局部重建。",
|
||||
"这类对象优先按 Creo 式槽特征理解;当前只传播直接相邻的一级关系,复杂二级/三级联动后续再开放。",
|
||||
)
|
||||
return (
|
||||
"工程特征 / 孔:按孔径、轴心、封堵或盲孔/盲槽深度做重切/补料。",
|
||||
"这类对象优先按 Creo 式孔特征理解;通孔、盲孔、锥孔和槽孔会根据识别结果开放不同参数。",
|
||||
)
|
||||
if is_boss:
|
||||
return (
|
||||
"工程特征 / 凸台:按直径、高度或轴心做局部重建。",
|
||||
"凸台修改会尽量按局部补料、切除端盖或重建包络处理;也可以选择缩放所属对象这类整体语义。",
|
||||
)
|
||||
if is_existing_fillet:
|
||||
return (
|
||||
"工程特征 / 倒圆角:移除已有圆角后按目标半径重新倒圆。",
|
||||
"STEP 没有原始倒圆角历史时,只能基于当前圆角面和支撑面推断;复杂 blend 会阻止或回滚。",
|
||||
)
|
||||
if is_shell_candidate:
|
||||
return (
|
||||
"工程特征 / 抽壳:按壳体厚度或相对面距离调整薄壁区域。",
|
||||
"壳体厚度修改本质上是移动当前平面或沿厚度方向缩放所属对象,需要相对面和厚度方向识别稳定。",
|
||||
)
|
||||
if is_plane:
|
||||
return (
|
||||
"柔性建模 / 拉伸切除 / 偏移变换:平面 Face 可按不同意图改变位置、面积或尺寸。",
|
||||
"平面 Face 不是直接暴露 B-Rep 参数;界面会把目标值映射为拉伸/切除、局部重建、移动特征或缩放特征。",
|
||||
)
|
||||
if is_cone:
|
||||
return (
|
||||
"工程特征 / 拔模或锥孔:只在简单圆锥或可识别锥孔上开放半角和参考半径。",
|
||||
"复杂圆锥面可能只是拔模、过渡或导入后的自由裁剪面,当前不会把不可靠参数伪装成可修改特征。",
|
||||
)
|
||||
if is_sphere:
|
||||
return (
|
||||
"解析曲面 / 缩放特征:球面半径通过缩放所属对象调整。",
|
||||
"当前不是恢复 CAD 历史里的球面特征,而是围绕识别到的中心做受限几何缩放。",
|
||||
)
|
||||
if is_torus:
|
||||
return (
|
||||
"解析曲面 / 缩放特征:环面主半径和小半径通过缩放所属对象调整。",
|
||||
"当前不是恢复 CAD 历史里的环面特征,而是围绕识别到的环面中心和轴线做受限几何缩放。",
|
||||
)
|
||||
if is_generic_surface:
|
||||
surface_name = _format_info_value("surface", surface)
|
||||
return (
|
||||
f"自由/复杂曲面:{surface_name} 当前只读,不开放参数化修改。",
|
||||
"自由曲面需要单独的曲面替换、重拟合或控制点编辑语义;当前一级阶段不把底层 UV 参数当成用户尺寸。",
|
||||
)
|
||||
if is_cylinder:
|
||||
return (
|
||||
"解析圆柱 / 拉伸切除:可按直径、高度或轴向缩放处理普通圆柱面。",
|
||||
"如果识别不到孔、槽、凸台或圆角语义,当前只按普通圆柱几何入口开放有限参数。",
|
||||
)
|
||||
return (
|
||||
"导入几何 Face:当前只显示可识别参数。",
|
||||
"STEP 通常没有完整建模历史;只有能映射到稳定 CAD 建模形式的参数才会开放修改。",
|
||||
)
|
||||
if has_edge:
|
||||
if curve == "line":
|
||||
return (
|
||||
"柔性建模 / 移动几何:直线 Edge 可改长度、端点,并作为倒角/倒圆角入口。",
|
||||
"Edge 长度有多种 CAD 语义:只改当前边、移动端面、相邻圆柱联动或缩放所属对象,界面会让用户选择。",
|
||||
)
|
||||
if curve == "circle":
|
||||
return (
|
||||
"工程特征入口 / 圆 Edge:可作为孔槽、圆角或相邻圆柱尺寸的参考。",
|
||||
"圆 Edge 本身通常不是独立历史特征;修改时会优先寻找相邻圆柱、孔槽或圆角语义。",
|
||||
)
|
||||
return (
|
||||
"曲线 Edge:当前只开放能稳定映射的尺寸或倒角/圆角入口。",
|
||||
"复杂曲线 Edge 暂不直接做自由形变,避免把拓扑边参数误当成 CAD 设计尺寸。",
|
||||
)
|
||||
if self.selected_solid_id is not None or self.selected_part_id is not None:
|
||||
return (
|
||||
"整体特征 / 移动几何 / 缩放特征:当前对象按整体变换处理。",
|
||||
"零件或 Solid 层级不代表单个 CAD 历史特征;当前只开放清晰的整体移动、旋转或缩放语义。",
|
||||
)
|
||||
return "", ""
|
||||
|
||||
def cad_recommended_operation() -> tuple[str, str]:
|
||||
if has_face:
|
||||
if is_hole_or_groove:
|
||||
if is_slot_or_half_hole:
|
||||
return (
|
||||
"优先改槽宽、槽深或槽孔总长度;要换位置时改轴心。",
|
||||
"槽类修改先走局部重建;如果用户选择缩放特征,会连同所属对象上的其它尺寸一起变化。",
|
||||
)
|
||||
return (
|
||||
"优先改孔径/半径;盲孔改深度;要换位置时改轴心。",
|
||||
"孔类修改会尽量先补旧孔再按目标重新切孔;螺纹孔、复杂孔组和底面识别不稳的盲孔仍可能受限。",
|
||||
)
|
||||
if is_boss:
|
||||
return (
|
||||
"优先改凸台直径或高度;要换位置时改轴心。",
|
||||
"凸台高度更接近拉伸/切除端盖;凸台直径和轴心更接近移除旧包络后重建局部凸台。",
|
||||
)
|
||||
if is_existing_fillet:
|
||||
return (
|
||||
"优先改圆角半径;复杂圆角链先不要用整体缩放替代。",
|
||||
"已有圆角需要先恢复支撑锐边再重新倒圆,支撑面不明确时失败回滚是正常保护。",
|
||||
)
|
||||
if is_shell_candidate:
|
||||
return (
|
||||
"优先改壳体厚度;普通位置变化再用偏移变换。",
|
||||
"壳体厚度需要相对面识别稳定;如果只想移动整个对象,请选择移动特征或缩放特征语义。",
|
||||
)
|
||||
if is_plane:
|
||||
return (
|
||||
"优先改偏移变换,并选择拉伸/切除;只想让当前面变形时再改面积、U/V尺寸或中心。",
|
||||
"拉伸/切除更接近常见 CAD 加料/切料;局部重建会让相邻面自然跟随变形,不一定保持垂直。",
|
||||
)
|
||||
if is_cone:
|
||||
return (
|
||||
"简单圆锥优先改半角或参考半径;复杂拔模面先保持只读。",
|
||||
"锥面可能来自拔模、沉孔或导入裁剪曲面,识别不稳时不直接开放大参数修改。",
|
||||
)
|
||||
if is_sphere:
|
||||
return (
|
||||
"优先改球面半径,并确认这是缩放所属对象。",
|
||||
"当前没有恢复球面历史特征,只能按识别中心做受限几何缩放。",
|
||||
)
|
||||
if is_torus:
|
||||
return (
|
||||
"优先改环面主半径或小半径,并确认这是缩放所属对象。",
|
||||
"当前没有恢复环面历史特征,只能按识别中心和轴线做受限几何缩放。",
|
||||
)
|
||||
if is_generic_surface:
|
||||
return (
|
||||
"当前建议只查看,不直接改自由曲面参数。",
|
||||
"自由曲面需要曲面替换、重拟合或控制点编辑,不能用面积或包围盒伪装成稳定 CAD 尺寸。",
|
||||
)
|
||||
if is_cylinder:
|
||||
return (
|
||||
"优先改直径或高度;若它其实是孔、槽、凸台或圆角,请先提高特征探测级别。",
|
||||
"普通圆柱入口是兜底几何编辑,不等同于已经识别到完整 CAD 特征。",
|
||||
)
|
||||
return (
|
||||
"先查看识别摘要,再只修改标记为可修改的参数。",
|
||||
"未映射到明确建模形式的 STEP Face 不应强行参数化。",
|
||||
)
|
||||
if has_edge:
|
||||
if curve == "line":
|
||||
return (
|
||||
"优先改长度;想保持端面垂直选移动端面,想只动当前边选只改当前Edge。",
|
||||
"同一条 Edge 改长可能对应多种 CAD 结果,当前必须显式选择建模意图。",
|
||||
)
|
||||
if curve == "circle":
|
||||
return (
|
||||
"优先让圆 Edge 关联到孔、槽、凸台或圆角直径,不直接自由变形这条边。",
|
||||
"圆 Edge 往往是相邻特征的边界,直接改边可能破坏孔槽或圆角语义。",
|
||||
)
|
||||
return (
|
||||
"只修改已明确开放的 Edge 参数;复杂曲线先保持只读。",
|
||||
"复杂曲线需要约束或曲线重拟合,不适合直接用边长驱动。",
|
||||
)
|
||||
if self.selected_solid_id is not None or self.selected_part_id is not None:
|
||||
return (
|
||||
"优先使用整体移动、旋转或缩放;不要把整体对象误当成单个历史特征。",
|
||||
"STEP 里零件/Solid 层级通常缺少装配约束和特征历史,整体操作要明确影响范围。",
|
||||
)
|
||||
return "", ""
|
||||
|
||||
cad_form_text, cad_form_tip = cad_modeling_form()
|
||||
if cad_form_text:
|
||||
add_readonly_spec(
|
||||
key="cad_modeling_form",
|
||||
label="建模形式",
|
||||
text=cad_form_text,
|
||||
tip=cad_form_tip,
|
||||
pin_top=True,
|
||||
)
|
||||
recommended_text, recommended_tip = cad_recommended_operation()
|
||||
if recommended_text:
|
||||
add_readonly_spec(
|
||||
key="cad_recommended_operation",
|
||||
label="推荐操作",
|
||||
text=recommended_text,
|
||||
tip=recommended_tip,
|
||||
pin_top=True,
|
||||
)
|
||||
|
||||
if has_face:
|
||||
topology_depth = _int_or_none(action_info.get("topology_relation_depth"))
|
||||
is_cylindrical_topology = bool(
|
||||
@@ -2225,7 +2516,7 @@ class WindowStateMixin:
|
||||
if is_slot_or_half_hole:
|
||||
add_readonly_spec(
|
||||
key="slot_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text="槽/半孔:局部扇形槽重建;槽孔总长度会尝试配对另一端。",
|
||||
tip=(
|
||||
"槽/半孔宽度、深度和圆弧参数会换算成圆柱半径或圆弧角度,"
|
||||
@@ -2236,7 +2527,7 @@ class WindowStateMixin:
|
||||
else:
|
||||
add_readonly_spec(
|
||||
key="hole_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text="孔:同轴重切孔壁;移动轴心会先填旧孔再切新孔。",
|
||||
tip=(
|
||||
"孔径/半径不是缩放整个模型,而是在当前孔轴线上做受限布尔重切;"
|
||||
@@ -2247,7 +2538,7 @@ class WindowStateMixin:
|
||||
elif is_boss:
|
||||
add_readonly_spec(
|
||||
key="boss_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text="凸台:直径重建包络;高度拉伸/切除端盖;轴心先移除再补新凸台。",
|
||||
tip=(
|
||||
"凸台直径会通过局部布尔补料或移除旧包络后重建目标圆柱;"
|
||||
@@ -2258,7 +2549,7 @@ class WindowStateMixin:
|
||||
elif is_existing_fillet:
|
||||
add_readonly_spec(
|
||||
key="existing_fillet_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text="圆角:移除已有圆角面,再尝试按目标半径重新倒圆。",
|
||||
tip=(
|
||||
"已有圆角修改不是恢复 CAD 历史参数;当前会先 defeature 当前圆角面,"
|
||||
@@ -2268,11 +2559,11 @@ class WindowStateMixin:
|
||||
elif is_cone or is_sphere or is_torus:
|
||||
add_readonly_spec(
|
||||
key="analytic_surface_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text="解析曲面:围绕中心或轴线缩放所属对象,会影响同对象其它尺寸。",
|
||||
tip=(
|
||||
"圆锥、球面、环面这类曲面当前走几何缩放语义,"
|
||||
"不是只替换单个曲面的历史参数;执行前需要确认影响范围。"
|
||||
"不是只替换单个曲面的历史参数;执行前需要确认建模意图。"
|
||||
),
|
||||
)
|
||||
elif is_generic_surface:
|
||||
@@ -2284,7 +2575,7 @@ class WindowStateMixin:
|
||||
)
|
||||
add_readonly_spec(
|
||||
key="freeform_surface_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text=f"{surface_name}:暂不支持参数化编辑。",
|
||||
tip=(
|
||||
f"{blocker} 当前不会把自由曲面的面积、中心、包围盒或底层 UV 参数伪装成可修改尺寸;"
|
||||
@@ -2311,11 +2602,11 @@ class WindowStateMixin:
|
||||
),
|
||||
tip=topology_tip,
|
||||
)
|
||||
face_semantics_text = "Face:先改目标值,再用“影响范围”选择局部重建、拉伸/切除、移动特征或缩放特征。"
|
||||
face_semantics_text = "Face:先改目标值,再用“建模意图”选择局部重建、拉伸/切除、移动特征或缩放特征。"
|
||||
face_semantics_tip = (
|
||||
"面积是 Face 的几何面积;U向尺寸/V向尺寸是这个 Face 自身参数方向上的两个投影尺寸;"
|
||||
"偏移曲面使用 Creo 曲面 Offset 语义,表示沿当前 Face 法向得到的目标位置。"
|
||||
"影响范围决定这次修改是局部重建当前 Face、按拉伸/切除加减材料,"
|
||||
"偏移变换使用 Creo 柔性建模里的 Offset 语义,表示沿当前 Face 法向得到的目标位置。"
|
||||
"建模意图决定这次修改是局部重建当前 Face、按拉伸/切除加减材料,"
|
||||
"还是移动或缩放所属特征/Solid。"
|
||||
)
|
||||
if is_plane and not local_face_deform_ready:
|
||||
@@ -2327,7 +2618,7 @@ class WindowStateMixin:
|
||||
)
|
||||
add_readonly_spec(
|
||||
key="face_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text=face_semantics_text,
|
||||
tip=face_semantics_tip,
|
||||
)
|
||||
@@ -2335,10 +2626,10 @@ class WindowStateMixin:
|
||||
if has_edge:
|
||||
add_readonly_spec(
|
||||
key="edge_edit_semantics",
|
||||
label="编辑方式",
|
||||
label="建模意图",
|
||||
text="Edge:长度修改可选择局部形变、移动端面或缩放所属对象。",
|
||||
tip=(
|
||||
"只变当前Edge会重建局部相邻面;移动端面更像整体尺寸变化;"
|
||||
"只改当前Edge会重建局部相邻面;移动端面更像整体尺寸变化;"
|
||||
"缩放所属会影响同一特征或 Solid 上的其它尺寸。"
|
||||
),
|
||||
)
|
||||
@@ -2571,7 +2862,7 @@ class WindowStateMixin:
|
||||
)
|
||||
add_scoped_spec(
|
||||
key="face_target_normal_position",
|
||||
label="偏移曲面",
|
||||
label="偏移变换",
|
||||
current_raw=current_plane_position if current_plane_position is not None else "",
|
||||
target_text=numeric_text(current_plane_position),
|
||||
scope_default="push_pull",
|
||||
@@ -2581,10 +2872,10 @@ class WindowStateMixin:
|
||||
"action": "push_pull_face",
|
||||
"target_attr": "offset_input",
|
||||
"enabled": current_plane_position is not None,
|
||||
"enabled_tip": "输入偏移曲面的目标位置;程序会沿当前 Face 法向执行拉伸或切除。",
|
||||
"enabled_tip": "输入偏移变换的目标位置;程序会沿当前 Face 法向执行拉伸或切除。",
|
||||
"disabled_tip": "当前平面缺少稳定移动方向或基准点,不能按目标位置拉伸/切除。",
|
||||
"range_hint": (
|
||||
"偏移曲面是沿当前 Face 法向测量的目标位置,不是面积、不是移动距离,也不是 X/Y/Z 坐标;单位同模型。"
|
||||
"偏移变换是沿当前 Face 法向测量的目标位置,不是面积、不是移动距离,也不是 X/Y/Z 坐标;单位同模型。"
|
||||
"程序会把目标位置自动换算成本次拉伸/切除距离。"
|
||||
f"{face_offset_hard_limit}"
|
||||
),
|
||||
@@ -2602,11 +2893,11 @@ class WindowStateMixin:
|
||||
and current_face_center is not None
|
||||
),
|
||||
"enabled_tip": (
|
||||
"输入偏移曲面的目标位置;程序只把当前 Face 沿法向移动到该目标值,"
|
||||
"输入偏移变换的目标位置;程序只把当前 Face 沿法向移动到该目标值,"
|
||||
"并让相邻平面按新顶点重建。"
|
||||
),
|
||||
"disabled_tip": face_local_disabled_tip(
|
||||
"当前平面缺少稳定方向、基准点或中心,不能按偏移曲面局部重建当前 Face。"
|
||||
"当前平面缺少稳定方向、基准点或中心,不能按偏移变换局部重建当前 Face。"
|
||||
),
|
||||
"range_hint": (
|
||||
"局部重建不会自动加料/切削,相邻面可能自然变斜,"
|
||||
@@ -2626,10 +2917,10 @@ class WindowStateMixin:
|
||||
and (self.selected_part_id is not None or self.selected_solid_id is not None)
|
||||
),
|
||||
"enabled_tip": (
|
||||
"输入偏移曲面的目标位置;程序会沿当前 Face 法向平移所属特征或 Solid,"
|
||||
"输入偏移变换的目标位置;程序会沿当前 Face 法向平移所属特征或 Solid,"
|
||||
"当前面形状和所属对象内部尺寸不变。"
|
||||
),
|
||||
"disabled_tip": "当前平面缺少稳定方向、基准点或所属对象,不能按偏移曲面移动所属特征。",
|
||||
"disabled_tip": "当前平面缺少稳定方向、基准点或所属对象,不能按偏移变换移动所属特征。",
|
||||
"range_hint": (
|
||||
"这是移动所属对象,不是拉伸/切除当前 Face;如果想改变形状或厚度,请选择“拉伸/切除”。"
|
||||
f"{translation_hint()} {face_offset_hard_limit}"
|
||||
@@ -2682,7 +2973,7 @@ class WindowStateMixin:
|
||||
"disabled_tip": "当前壳体候选缺少稳定厚度、厚度方向、基准平面或所属对象,不能按厚度缩放所属特征。",
|
||||
"range_hint": (
|
||||
"这是缩放所属对象,不是单独移动当前平面;"
|
||||
f"如果只想移动当前壳体平面区域,请把影响范围设为“拉伸/切除”。 {relative_range_hint(current, 0.2, 0.5, face_linear_hard_limit)}"
|
||||
f"如果只想移动当前壳体平面区域,请把建模意图设为“拉伸/切除”。 {relative_range_hint(current, 0.2, 0.5, face_linear_hard_limit)}"
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -4788,7 +5079,7 @@ class WindowStateMixin:
|
||||
self._update_action_states()
|
||||
self._refresh_property_editor()
|
||||
label = self.edge_length_strategy_combo.currentText() if hasattr(self, "edge_length_strategy_combo") else ""
|
||||
self.statusBar().showMessage(f"已设置修改策略:{label or '自动策略'}。")
|
||||
self.statusBar().showMessage(f"已设置建模意图:{label or '自动选择'}。")
|
||||
|
||||
def set_chamfer_reference_face(self) -> None:
|
||||
self._update_action_states()
|
||||
@@ -5126,6 +5417,7 @@ class WindowStateMixin:
|
||||
return result
|
||||
if self.selected_kind == "edge" and self.selected_edge_id is not None:
|
||||
result = self.model.edge_info(self.selected_edge_id)
|
||||
result.update(self._edge_first_level_selection_fields(self.selected_edge_id))
|
||||
self._selected_action_info_cache_key = cache_key
|
||||
self._selected_action_info_cache_value = dict(result)
|
||||
return result
|
||||
@@ -5200,6 +5492,7 @@ class WindowStateMixin:
|
||||
resolved_edge_id = self._resolve_record_edge_id(record)
|
||||
if resolved_edge_id is not None:
|
||||
info = self.model.edge_info(resolved_edge_id)
|
||||
info.update(self._edge_first_level_selection_fields(resolved_edge_id))
|
||||
self.selected_kind = "edge"
|
||||
self.selected_edge_id = resolved_edge_id
|
||||
self.selected_part_id = int(info["part_id"])
|
||||
|
||||
Reference in New Issue
Block a user