feat: 增强一级关系识别与保持关系策略

This commit is contained in:
2026-08-10 14:56:50 +08:00
parent 12250603dd
commit 83c44265ba
24 changed files with 3460 additions and 241 deletions
+155 -20
View File
@@ -1487,6 +1487,7 @@ class WindowActionMixin:
"move_circular_edge_axis_center",
"resize_ellipse_edge_axis_radius",
"resize_existing_fillet",
"resize_existing_chamfer",
"fillet_edge",
"chamfer_edge",
"chamfer_edge_asymmetric",
@@ -4101,6 +4102,92 @@ class WindowActionMixin:
isolation=isolation,
)
def resize_existing_chamfer(self) -> None:
if self.model is None:
return
if self._edit_busy("请等待当前编辑完成后再修改已有倒角。"):
return
if self.selected_face_id is None:
QMessageBox.information(self, "未选择已有倒角", "请先选择一个已有倒角斜面 Face。")
return
try:
target_distance = float(self.edge_chamfer_distance_input.text())
except ValueError:
QMessageBox.critical(self, "距离无效", "请输入数字形式的目标倒角距离。")
return
face_id = self.selected_face_id
plan = self.model.existing_chamfer_resize_plan(face_id, target_distance)
isolation = self._isolation_for_plan(plan, "resize_existing_chamfer", [face_id, target_distance])
if plan["status"] == "blocked":
self._show_blocked_plan_message("修改已有倒角", plan, "已有倒角距离修改已阻止")
return
warnings = str(plan.get("warnings", ""))
warnings_line = f"警告: {warnings}\n\n" if warnings else ""
result = QMessageBox.question(
self,
"确认修改已有倒角距离",
(
f"Face: {plan['face_id']}\n"
f"当前估算距离: {_format_value(plan['current_distance'])}\n"
f"目标距离: {_format_value(plan['target_distance'])}\n"
f"距离变化量: {_format_value(plan['delta_distance'])}\n"
f"距离变化比例: {_format_percent(plan['distance_delta_ratio'])}\n"
f"支撑 Face: {_format_value(plan.get('feature_existing_chamfer_support_face_ids', ''))}\n"
f"长边 Edge: {_format_value(plan.get('feature_existing_chamfer_long_edge_ids', ''))}\n"
f"策略: {plan['resize_strategy']}\n"
f"执行策略: {_format_value(plan.get('edit_strategy_label'))}\n"
f"建模意图: {_format_value(plan.get('edit_semantics'))}\n"
f"风险: {plan['risk']}\n\n"
f"{warnings_line}"
f"{plan['message']}\n\n"
"继续操作会先尝试移除当前倒角斜面,再对恢复出的锐边重新倒角。\n"
"这不是 CAD 历史特征参数编辑,复杂倒角可能失败。\n\n"
"确定继续吗?"
),
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
QMessageBox.StandardButton.No,
)
if result != QMessageBox.StandardButton.Yes:
self.statusBar().showMessage("已取消修改已有倒角距离")
return
def action():
return self.model.resize_existing_chamfer(face_id, target_distance)
self._run_edit_action(
action,
operation_name="修改已有倒角距离",
target=f"Face {face_id}",
parameters={
"part_id": plan.get("part_id"),
"solid_id": plan.get("solid_id"),
"old_distance": plan.get("current_distance"),
"new_distance": target_distance,
"delta_distance": plan.get("delta_distance"),
"distance_delta_ratio": plan.get("distance_delta_ratio"),
"feature_type": plan.get("feature_type"),
"feature_guess": plan.get("feature_guess"),
"confidence": plan.get("confidence"),
"existing_chamfer_status": plan.get("status"),
"existing_chamfer_risk": plan.get("risk"),
"existing_chamfer_message": plan.get("message"),
"existing_chamfer_warnings": plan.get("warnings"),
"existing_chamfer_blockers": plan.get("blockers"),
"feature_existing_chamfer_support_face_ids": plan.get("feature_existing_chamfer_support_face_ids"),
"feature_existing_chamfer_long_edge_ids": plan.get("feature_existing_chamfer_long_edge_ids"),
"resize_strategy": plan.get("resize_strategy"),
"edit_strategy_label": plan.get("edit_strategy_label"),
"edit_semantics": plan.get("edit_semantics"),
"resize_note": plan.get("resize_note"),
"ui_preview": "skipped-for-existing-chamfer",
},
target_kind="face",
target_id=face_id,
isolation=isolation,
)
def fillet_edge(self) -> None:
if self.model is None:
return
@@ -4483,6 +4570,7 @@ class WindowActionMixin:
f"建模意图: {_format_value(plan.get('edge_length_strategy_label', ''))}\n"
f"固定约束: {_format_value(plan.get('edge_length_constraint_summary', ''))}\n"
f"执行影响: {_format_value(plan.get('edge_length_impact_summary', ''))}\n"
f"平面关系约束: {_format_value(plan.get('edge_length_planar_constraint_summary', ''))}\n"
f"策略: {_format_value(plan.get('resize_strategy', ''))}\n"
f"局部形变目标: {_format_value(plan.get('local_edge_deform_target_kind', ''))}\n"
f"局部形变: {_format_value(plan.get('local_edge_deform_anchor', ''))}\n"
@@ -4539,6 +4627,26 @@ class WindowActionMixin:
"edge_length_strategy_label": plan.get("edge_length_strategy_label"),
"edge_length_constraint_summary": plan.get("edge_length_constraint_summary"),
"edge_length_impact_summary": plan.get("edge_length_impact_summary"),
"edge_length_planar_relation_constraint_requested": plan.get(
"edge_length_planar_relation_constraint_requested"
),
"edge_length_planar_relation_constraint_label": plan.get(
"edge_length_planar_relation_constraint_label"
),
"edge_length_planar_constraint_status": plan.get("edge_length_planar_constraint_status"),
"edge_length_planar_constraint_summary": plan.get("edge_length_planar_constraint_summary"),
"edge_length_planar_constraint_side_face_count": plan.get(
"edge_length_planar_constraint_side_face_count"
),
"edge_length_planar_constraint_parallel_count": plan.get(
"edge_length_planar_constraint_parallel_count"
),
"edge_length_planar_constraint_perpendicular_count": plan.get(
"edge_length_planar_constraint_perpendicular_count"
),
"edge_length_planar_constraint_angled_count": plan.get("edge_length_planar_constraint_angled_count"),
"edge_length_planar_constraint_blockers": plan.get("edge_length_planar_constraint_blockers"),
"edge_length_planar_constraint_warnings": plan.get("edge_length_planar_constraint_warnings"),
"edit_strategy_label": plan.get("edit_strategy_label"),
"edit_semantics": plan.get("edit_semantics"),
"local_edge_deform_target_kind": plan.get("local_edge_deform_target_kind"),
@@ -4650,6 +4758,7 @@ class WindowActionMixin:
f"建模意图: {_format_value(plan.get('edge_length_strategy_label', ''))}\n"
f"固定约束: {_format_value(plan.get('edge_length_constraint_summary', ''))}\n"
f"执行影响: {_format_value(plan.get('edge_length_impact_summary', ''))}\n"
f"平面关系约束: {_format_value(plan.get('edge_length_planar_constraint_summary', ''))}\n"
f"策略: {_format_value(strategy)}\n"
f"局部形变目标: {_format_value(plan.get('local_edge_deform_target_kind', ''))}\n"
f"局部形变: {_format_value(plan.get('local_edge_deform_anchor', ''))}\n"
@@ -4707,6 +4816,26 @@ class WindowActionMixin:
"edge_length_strategy_label": plan.get("edge_length_strategy_label"),
"edge_length_constraint_summary": plan.get("edge_length_constraint_summary"),
"edge_length_impact_summary": plan.get("edge_length_impact_summary"),
"edge_length_planar_relation_constraint_requested": plan.get(
"edge_length_planar_relation_constraint_requested"
),
"edge_length_planar_relation_constraint_label": plan.get(
"edge_length_planar_relation_constraint_label"
),
"edge_length_planar_constraint_status": plan.get("edge_length_planar_constraint_status"),
"edge_length_planar_constraint_summary": plan.get("edge_length_planar_constraint_summary"),
"edge_length_planar_constraint_side_face_count": plan.get(
"edge_length_planar_constraint_side_face_count"
),
"edge_length_planar_constraint_parallel_count": plan.get(
"edge_length_planar_constraint_parallel_count"
),
"edge_length_planar_constraint_perpendicular_count": plan.get(
"edge_length_planar_constraint_perpendicular_count"
),
"edge_length_planar_constraint_angled_count": plan.get("edge_length_planar_constraint_angled_count"),
"edge_length_planar_constraint_blockers": plan.get("edge_length_planar_constraint_blockers"),
"edge_length_planar_constraint_warnings": plan.get("edge_length_planar_constraint_warnings"),
"edit_strategy_label": plan.get("edit_strategy_label"),
"edit_semantics": plan.get("edit_semantics"),
"local_edge_deform_target_kind": plan.get("local_edge_deform_target_kind"),
@@ -4894,7 +5023,7 @@ class WindowActionMixin:
float(getattr(self, f"{prefix}_z_input").text()),
)
except (AttributeError, ValueError):
QMessageBox.critical(self, "Edge endpoint coordinate invalid", "Please enter numeric X, Y and Z values.")
QMessageBox.critical(self, "Edge端点坐标无效", "请输入数字形式的 X、Y、Z 目标坐标。")
return None
def move_edge_start_point(self) -> None:
@@ -4992,10 +5121,10 @@ class WindowActionMixin:
def move_edge_center_point(self) -> None:
if self.model is None:
return
if self._edit_busy("Please wait for the current edit to finish before moving an Edge center."):
if self._edit_busy("请等待当前编辑完成后再移动 Edge 中心。"):
return
if self.selected_edge_id is None:
QMessageBox.information(self, "No Edge selected", "Please select an Edge first.")
QMessageBox.information(self, "未选择Edge", "请先选择一条 Edge。")
return
try:
target_center = (
@@ -5004,7 +5133,7 @@ class WindowActionMixin:
float(self.edge_center_z_input.text()),
)
except (AttributeError, ValueError):
QMessageBox.critical(self, "Edge center coordinate invalid", "Please enter numeric X, Y and Z values.")
QMessageBox.critical(self, "Edge中心坐标无效", "请输入数字形式的 X、Y、Z 目标坐标。")
return
edge_id = self.selected_edge_id
@@ -5012,12 +5141,13 @@ class WindowActionMixin:
isolation = self._isolation_for_plan(plan, "move_edge_center", [edge_id, target_center])
lines = [
f"Edge: {edge_id}",
f"Current center: {_format_value(plan.get('current_edge_center'))}",
f"Target center: {_format_value(plan.get('target_edge_center'))}",
f"Move vector: {_format_value(plan.get('moved_edge_center_delta'))}",
f"Edge length: {_format_value(plan.get('current_length'))}",
f"Strategy: {_format_value(plan.get('resize_strategy'))}",
f"Rebuilt faces: {_format_value(plan.get('local_edge_deform_face_count'))}",
f"当前中心: {_format_value(plan.get('current_edge_center'))}",
f"目标中心: {_format_value(plan.get('target_edge_center'))}",
f"移动量: {_format_value(plan.get('moved_edge_center_delta'))}",
f"Edge长度: {_format_value(plan.get('current_length'))}",
f"建模语义: {_format_value(plan.get('edit_strategy_label'))}",
f"影响范围: {_format_value(plan.get('edit_semantics'))}",
f"重建Face数: {_format_value(plan.get('local_edge_deform_face_count'))}",
]
if not self._confirm_quick_edit_plan(
"移动Edge中心",
@@ -5054,6 +5184,8 @@ class WindowActionMixin:
"local_edge_deform_start_move": plan.get("local_edge_deform_start_move"),
"local_edge_deform_end_move": plan.get("local_edge_deform_end_move"),
"resize_strategy": plan.get("resize_strategy"),
"edit_strategy_label": plan.get("edit_strategy_label"),
"edit_semantics": plan.get("edit_semantics"),
"resize_status": plan.get("status"),
"resize_risk": plan.get("risk"),
"resize_message": plan.get("message"),
@@ -5068,10 +5200,10 @@ class WindowActionMixin:
def _move_edge_endpoint(self, endpoint_role: str) -> None:
if self.model is None:
return
if self._edit_busy("Please wait for the current edit to finish before moving an Edge endpoint."):
if self._edit_busy("请等待当前编辑完成后再移动 Edge 端点。"):
return
if self.selected_edge_id is None:
QMessageBox.information(self, "No Edge selected", "Please select an Edge first.")
QMessageBox.information(self, "未选择Edge", "请先选择一条 Edge。")
return
target_point = self._edge_endpoint_target_from_inputs(endpoint_role)
@@ -5084,14 +5216,15 @@ class WindowActionMixin:
title = "移动Edge起点" if endpoint_role == "start" else "移动Edge终点"
lines = [
f"Edge: {edge_id}",
f"Endpoint: {_format_value(plan.get('edge_endpoint_label'))}",
f"Current endpoint: {_format_value(plan.get('current_endpoint_point'))}",
f"Target endpoint: {_format_value(plan.get('target_endpoint_point'))}",
f"Move vector: {_format_value(plan.get('moved_endpoint_delta'))}",
f"Current Edge length: {_format_value(plan.get('current_length'))}",
f"Result Edge length: {_format_value(plan.get('target_length'))}",
f"Strategy: {_format_value(plan.get('resize_strategy'))}",
f"Rebuilt faces: {_format_value(plan.get('local_edge_deform_face_count'))}",
f"端点: {_format_value(plan.get('edge_endpoint_label'))}",
f"当前端点: {_format_value(plan.get('current_endpoint_point'))}",
f"目标端点: {_format_value(plan.get('target_endpoint_point'))}",
f"移动量: {_format_value(plan.get('moved_endpoint_delta'))}",
f"当前Edge长度: {_format_value(plan.get('current_length'))}",
f"结果Edge长度: {_format_value(plan.get('target_length'))}",
f"建模语义: {_format_value(plan.get('edit_strategy_label'))}",
f"影响范围: {_format_value(plan.get('edit_semantics'))}",
f"重建Face数: {_format_value(plan.get('local_edge_deform_face_count'))}",
]
if not self._confirm_quick_edit_plan(
title,
@@ -5133,6 +5266,8 @@ class WindowActionMixin:
"local_edge_deform_start_move": plan.get("local_edge_deform_start_move"),
"local_edge_deform_end_move": plan.get("local_edge_deform_end_move"),
"resize_strategy": plan.get("resize_strategy"),
"edit_strategy_label": plan.get("edit_strategy_label"),
"edit_semantics": plan.get("edit_semantics"),
"resize_status": plan.get("status"),
"resize_risk": plan.get("risk"),
"resize_message": plan.get("message"),