feat: 推进一级关系参数化编辑与参数导出

This commit is contained in:
2026-08-13 17:50:20 +08:00
parent 19364d81b5
commit 7d814d2939
21 changed files with 2075 additions and 231 deletions
+60 -20
View File
@@ -3269,9 +3269,13 @@ class OperationMixin:
outward_direction = _tuple_normalized(_tuple_or_none(info.get("push_pull_outward_direction")))
current_plane_position = None
target_plane_position = None
current_plane_center = _tuple_or_none(info.get("area_center")) or _tuple_or_none(info.get("bbox_center"))
target_plane_center = None
if plane_origin is not None and outward_direction is not None:
current_plane_position = _tuple_dot(plane_origin, outward_direction)
target_plane_position = current_plane_position + float(distance)
if current_plane_center is not None:
target_plane_center = _tuple_add(current_plane_center, _tuple_scale(outward_direction, float(distance)))
return {
"status": status,
@@ -3294,6 +3298,8 @@ class OperationMixin:
"plane_direction": outward_direction,
"current_plane_position": current_plane_position,
"target_plane_position": target_plane_position,
"current_plane_center": current_plane_center,
"target_plane_center": target_plane_center,
"push_pull_inward_material_depth": inward_material_depth,
"push_pull_inward_cut_ratio": inward_cut_ratio,
"cylindrical_cap_extension_old_height": (
@@ -8746,6 +8752,7 @@ class OperationMixin:
metric = ""
target: float | tuple[float, ...] | None = None
tolerance = 1e-4
center_tolerance: float | None = None
getter: Callable[[int], float | tuple[float, ...] | None] | None = None
target_area = _float_or_none(plan.get("target_area"))
@@ -8794,6 +8801,8 @@ class OperationMixin:
target = target_position
bbox_diagonal = _float_or_none(plan.get("bbox_diagonal")) or _shape_diagonal(self.shape)
tolerance = max(bbox_diagonal * 1e-4, abs(target_position) * 1e-5, 1e-4)
target_plane_center = _tuple_or_none(plan.get("target_plane_center"))
center_tolerance = max(bbox_diagonal * 0.15, tolerance * 20.0, 1e-3) if target_plane_center is not None else None
def plane_position_getter(face_id: int) -> float | None:
if face_id < 0 or face_id >= len(self.faces):
@@ -8890,6 +8899,46 @@ class OperationMixin:
plan_face_id = _int_or_none(plan.get("face_id"))
if plan_face_id is not None:
preferred_ids.append(plan_face_id)
def candidate_center_error(face_id: int) -> float | None:
if metric != "plane_position":
return None
target_center = _tuple_or_none(plan.get("target_plane_center"))
if target_center is None:
return None
try:
info = self.quick_face_info(face_id)
except Exception:
return None
actual_center = _tuple_or_none(info.get("area_center")) or _tuple_or_none(info.get("bbox_center"))
if actual_center is None:
return None
return _vector_length(_tuple_sub(actual_center, target_center))
def candidate_record(face_id: int, actual: object, error: float) -> dict[str, object]:
record: dict[str, object] = {
"face_id": face_id,
"metric": metric,
"actual": actual,
"target": target,
"error": error,
"tolerance": tolerance,
"scope": scope,
}
center_error = candidate_center_error(face_id)
if center_error is not None:
record["center_error"] = center_error
if center_tolerance is not None:
record["center_tolerance"] = center_tolerance
return record
def candidate_sort_key(record: dict[str, object]) -> tuple[int, float, float]:
center_error = _float_or_none(record.get("center_error"))
center_limit = _float_or_none(record.get("center_tolerance"))
center_penalty = 0
if center_error is not None and center_limit is not None and center_error > center_limit:
center_penalty = 1
return (center_penalty, float(record["error"]), center_error if center_error is not None else 0.0)
if preferred_ids:
preferred_best: dict[str, object] | None = None
for face_id in preferred_ids:
@@ -8902,16 +8951,9 @@ class OperationMixin:
if actual is None:
continue
error = _result_value_error(actual, target)
if preferred_best is None or error < float(preferred_best["error"]):
preferred_best = {
"face_id": face_id,
"metric": metric,
"actual": actual,
"target": target,
"error": error,
"tolerance": tolerance,
"scope": scope,
}
record = candidate_record(face_id, actual, error)
if preferred_best is None or candidate_sort_key(record) < candidate_sort_key(preferred_best):
preferred_best = record
if preferred_best is not None and float(preferred_best["error"]) <= tolerance:
return preferred_best
@@ -8924,16 +8966,9 @@ class OperationMixin:
if actual is None:
continue
error = _result_value_error(actual, target)
if best is None or error < float(best["error"]):
best = {
"face_id": face_id,
"metric": metric,
"actual": actual,
"target": target,
"error": error,
"tolerance": tolerance,
"scope": scope,
}
record = candidate_record(face_id, actual, error)
if best is None or candidate_sort_key(record) < candidate_sort_key(best):
best = record
if best is not None:
return best
return {
@@ -9011,6 +9046,11 @@ class OperationMixin:
all_ids = filtered(range(len(self.faces)))
if not all_ids and solid_id is not None and solid_id >= 0:
all_ids = filtered(range(len(self.faces)), require_solid=False)
elif target_kind == "solid" and solid_id is not None and solid_id >= 0 and surface == "plane":
relaxed_ids = filtered(range(len(self.faces)), require_solid=False)
for face_id in relaxed_ids:
if face_id not in all_ids:
all_ids.append(face_id)
combined: list[int] = []
for face_id in [*primary_ids, *all_ids]:
if face_id not in combined: