feat: 扩展Face保持关系编辑语义

This commit is contained in:
2026-08-10 17:50:50 +08:00
parent 61f0b76e59
commit 3a851d0967
8 changed files with 382 additions and 41 deletions
+134 -11
View File
@@ -262,6 +262,8 @@ class OperationMixin:
self,
face_id: int,
plan: dict[str, object],
*,
operation_label: str = "拉伸/切除",
) -> dict[str, object]:
relation_status = str(plan.get("first_level_planar_relation_status") or "")
relation_scope = str(plan.get("first_level_planar_relation_scope") or "")
@@ -313,10 +315,13 @@ class OperationMixin:
)
else:
summary = (
"Face保持关系:可按拉伸/切除执行并反查一级平面关系;"
f"Face保持关系:可按{operation_label}执行并反查一级平面关系;"
f"平行 {parallel_count},垂直 {perpendicular_count}"
)
return {
"face_planar_relation_constraint_requested": True,
"face_planar_relation_constraint_label": "保持一级平面关系",
"face_planar_relation_constraint_operation": operation_label,
"face_push_pull_planar_relation_constraint_requested": True,
"face_push_pull_planar_relation_constraint_label": "保持一级平面关系",
"face_push_pull_planar_constraint_status": "blocked" if blockers else "ready",
@@ -339,16 +344,23 @@ class OperationMixin:
),
}
def push_pull_keep_relations_plan(self, face_id: int, distance: float) -> dict[str, object]:
plan = self.push_pull_plan(face_id, distance)
constraint_fields = self._face_push_pull_planar_relation_constraint_fields(face_id, plan)
plan.update(constraint_fields)
plan["resize_strategy"] = "push-pull-planar-face-keep-relations"
plan["edit_strategy_label"] = "保持一级平面关系的拉伸/切除"
plan["edit_semantics"] = (
"沿当前 Face 法向拉伸/切除,同时要求当前 Face 与共享边一级相邻平面的平行/垂直关系"
"在执行前可验证,并在执行后反查。"
def _with_face_planar_relation_constraint(
self,
face_id: int,
plan: dict[str, object],
*,
operation_label: str,
edit_strategy_label: str,
edit_semantics: str,
) -> dict[str, object]:
constraint_fields = self._face_push_pull_planar_relation_constraint_fields(
face_id,
plan,
operation_label=operation_label,
)
plan.update(constraint_fields)
plan["edit_strategy_label"] = edit_strategy_label
plan["edit_semantics"] = edit_semantics
if plan["status"] != "blocked" and constraint_fields["face_push_pull_planar_constraint_status"] == "blocked":
blockers = str(constraint_fields.get("face_push_pull_planar_constraint_blockers") or "")
plan["status"] = "blocked"
@@ -368,6 +380,21 @@ class OperationMixin:
plan["message"] = str(constraint_fields["face_push_pull_planar_constraint_summary"])
return plan
def push_pull_keep_relations_plan(self, face_id: int, distance: float) -> dict[str, object]:
plan = self.push_pull_plan(face_id, distance)
self._with_face_planar_relation_constraint(
face_id,
plan,
operation_label="拉伸/切除",
edit_strategy_label="保持一级平面关系的拉伸/切除",
edit_semantics=(
"沿当前 Face 法向拉伸/切除,同时要求当前 Face 与共享边一级相邻平面的平行/垂直关系"
"在执行前可验证,并在执行后反查。"
),
)
plan["resize_strategy"] = "push-pull-planar-face-keep-relations"
return plan
def _multistep_prismatic_plan_blocker(self, face_id: int, operation_label: str) -> str:
try:
quick_info = self.quick_face_info(face_id)
@@ -4070,6 +4097,23 @@ class OperationMixin:
),
}
def face_center_local_move_keep_relations_plan(
self,
face_id: int,
target_center: tuple[float, float, float],
) -> dict[str, object]:
plan = self.face_center_owning_translation_plan(face_id, target_center)
return self._with_face_planar_relation_constraint(
face_id,
plan,
operation_label="中心移动",
edit_strategy_label="保持一级平面关系的中心移动",
edit_semantics=(
"把目标中心换算成平移量并移动所属特征或 Solid,同时要求当前 Face 与共享边一级相邻平面的"
"平行/垂直关系在执行前可验证,并在执行后反查;不做单面局部扭曲。"
),
)
def existing_chamfer_resize_plan(self, face_id: int, target_distance: float) -> dict[str, object]:
if face_id < 0 or face_id >= len(self.faces):
raise ValueError(f"Unknown face id {face_id}")
@@ -4604,6 +4648,26 @@ class OperationMixin:
),
}
def face_size_local_resize_keep_relations_plan(
self,
face_id: int,
target_size: float,
axis: str = "width",
) -> dict[str, object]:
axis_key = "height" if str(axis).strip().lower() in {"height", "h", "v", "y"} else "width"
axis_label = "面内宽度" if axis_key == "height" else "面内长度"
plan = self.face_size_owning_scale_plan(face_id, target_size, axis_key)
return self._with_face_planar_relation_constraint(
face_id,
plan,
operation_label=f"{axis_label}修改",
edit_strategy_label=f"保持一级平面关系的{axis_label}修改",
edit_semantics=(
f"沿当前 Face 的{axis_label}方向缩放所属特征或 Solid,同时要求当前 Face 与共享边一级相邻平面的"
"平行/垂直关系在执行前可验证,并在执行后反查;不做单面局部扭曲。"
),
)
def _rectangular_prismatic_size_resize_plan(
self,
*,
@@ -8144,6 +8208,35 @@ class OperationMixin:
f"risk={plan['risk']}. {result_check}"
)
def move_face_center_local_keep_relations(
self,
face_id: int,
target_center: tuple[float, float, float],
) -> str:
plan = self.face_center_local_move_keep_relations_plan(face_id, target_center)
if plan["status"] == "blocked":
raise ValueError(str(plan["message"]))
self._remember_face_target_logical_id(plan, face_id)
vector = tuple(plan["translation_vector"])
result, result_check = self._run_checked_face_edit(
plan,
lambda: (
self.translate_solid(int(plan["solid_id"]), vector)
if plan.get("target_kind") == "solid"
else self.translate_part(int(plan["part_id"]), vector)
),
)
return (
"Face center move completed with first-level planar relations: "
f"face {face_id}, "
f"current_center={plan.get('current_face_center')}, "
f"target_center={plan.get('target_face_center')}, "
f"move={plan.get('face_center_move_vector')}, "
f"target={plan.get('target_kind')}, "
f"risk={plan['risk']}. {result_check} {result}"
)
def face_area_local_resize_preview_polydata(
self,
face_id: int,
@@ -8243,6 +8336,33 @@ class OperationMixin:
f"risk={plan['risk']}. {result_check}"
)
def resize_face_size_local_keep_relations(self, face_id: int, target_size: float, axis: str = "width") -> str:
plan = self.face_size_local_resize_keep_relations_plan(face_id, target_size, axis)
if plan["status"] == "blocked":
raise ValueError(str(plan["message"]))
self._remember_face_target_logical_id(plan, face_id)
_action_result, result_check = self._run_checked_face_edit(
plan,
lambda: (
self._apply_local_face_deform(plan)
if plan.get("owning_face_size_rebuild_mode") == "planar-rebuild"
else self._apply_edge_length_affine_transform(plan)
),
)
return (
"Face owning size resize completed with first-level planar relations: "
f"face {face_id}, "
f"axis={plan.get('face_size_axis')}, "
f"current_size={float(plan['current_face_size']):g}, "
f"target_size={float(plan['target_face_size']):g}, "
f"delta={float(plan['face_size_delta']):g}, "
f"scale={float(plan['face_size_scale']):g}, "
f"rebuild_mode={plan.get('owning_face_size_rebuild_mode')}, "
f"target={plan.get('affine_target_kind')}, "
f"risk={plan['risk']}. {result_check}"
)
def face_size_owning_scale_preview_polydata(
self,
face_id: int,
@@ -8318,7 +8438,10 @@ class OperationMixin:
matched_face_id: int,
facts: dict[str, object],
) -> str:
if plan.get("face_push_pull_planar_relation_constraint_requested") is not True:
if (
plan.get("face_planar_relation_constraint_requested") is not True
and plan.get("face_push_pull_planar_relation_constraint_requested") is not True
):
return ""
if str(plan.get("face_push_pull_planar_constraint_status") or "") != "ready":
raise RuntimeError("Face planar relation check was requested but the original constraint plan is not ready.")