feat: 完善参数化编辑语义和槽孔中心距
This commit is contained in:
+156
-19
@@ -744,6 +744,11 @@ class FeatureMixin:
|
||||
target_to_height_ratio = new_diameter / height_estimate if height_estimate > 1e-9 else ""
|
||||
cutter_plan = self._bounded_cylinder_cutter_plan(face_id, new_diameter, feature)
|
||||
fill_plan = self._bounded_cylinder_fill_plan(face_id) if resize_mode == "shrink" else {}
|
||||
edit_semantics = (
|
||||
"缩小孔/槽直径:先用同轴圆柱补料封住旧孔壁,再按目标直径同轴重切;保持当前轴线和估算高度。"
|
||||
if resize_mode == "shrink"
|
||||
else "扩大孔/槽直径:沿当前圆柱轴线用有限长度圆柱 cutter 切到目标直径;保持当前轴线和估算高度。"
|
||||
)
|
||||
return {
|
||||
"status": readiness["resize_status"],
|
||||
"risk": readiness["resize_risk"],
|
||||
@@ -773,6 +778,9 @@ class FeatureMixin:
|
||||
"same_domain_range_source": axis_range["range_source"],
|
||||
"material_vote_summary": info.get("material_vote_summary"),
|
||||
"material_sample_count": info.get("material_sample_count"),
|
||||
"resize_strategy": "same-axis-bounded-cylinder-recut",
|
||||
"edit_strategy_label": "同轴圆柱重切",
|
||||
"edit_semantics": edit_semantics,
|
||||
**cutter_plan,
|
||||
**fill_plan,
|
||||
}
|
||||
@@ -918,6 +926,8 @@ class FeatureMixin:
|
||||
"same_domain_v_range": (axis_range.get("v_min"), axis_range.get("v_max")),
|
||||
"same_domain_range_source": axis_range.get("range_source", ""),
|
||||
"resize_strategy": "fill-old-cylinder-and-cut-moved-cylinder",
|
||||
"edit_strategy_label": "填旧孔并切新孔",
|
||||
"edit_semantics": "先填补当前完整圆柱孔,再按同直径在目标轴心切出新孔;这会改变孔的位置,不会整体平移零件。",
|
||||
}
|
||||
|
||||
def cylindrical_slot_resize_plan(
|
||||
@@ -1034,6 +1044,8 @@ class FeatureMixin:
|
||||
"slot_resize_mode": mode_key,
|
||||
"slot_resize_label": mode_labels.get(mode_key, "slot value"),
|
||||
"slot_resize_strategy": "local-sector-fixed-angular-span",
|
||||
"edit_strategy_label": "局部扇形槽重建",
|
||||
"edit_semantics": "保持当前槽/半孔圆弧角度,把目标宽度、深度或圆弧长度换算成圆柱直径,再填旧槽并切出新的局部扇形槽。",
|
||||
"slot_kind": slot_kind,
|
||||
"slot_status": slot_status,
|
||||
"slot_target_value": target_value,
|
||||
@@ -1084,7 +1096,11 @@ class FeatureMixin:
|
||||
resize_plan,
|
||||
pair_face_id=pair_face_id,
|
||||
)
|
||||
edit_strategy_label = "局部扇形槽重建"
|
||||
edit_semantics = "保持当前槽/半孔圆弧角度,把目标值换算成圆柱直径,再填旧槽并切出新的局部扇形槽。"
|
||||
if paired_slot_plan:
|
||||
edit_strategy_label = "配对长圆槽重建"
|
||||
edit_semantics = "识别到长圆槽另一端后,会填充旧长圆槽包络,再按同一槽中心线和目标宽度重切完整长圆槽。"
|
||||
risk = _max_risk(risk, str(paired_slot_plan.get("slot_pair_risk", "medium")))
|
||||
pair_warning = str(paired_slot_plan.get("slot_pair_warning", ""))
|
||||
if pair_warning:
|
||||
@@ -1104,6 +1120,8 @@ class FeatureMixin:
|
||||
**resize_plan,
|
||||
**slot_plan,
|
||||
**paired_slot_plan,
|
||||
"edit_strategy_label": edit_strategy_label,
|
||||
"edit_semantics": edit_semantics,
|
||||
"status": status,
|
||||
"risk": risk,
|
||||
"message": message,
|
||||
@@ -1235,6 +1253,8 @@ class FeatureMixin:
|
||||
"slot_resize_mode": "angular_span",
|
||||
"slot_resize_label": "slot angular span",
|
||||
"slot_resize_strategy": "local-sector-angular-span",
|
||||
"edit_strategy_label": "局部扇形槽角度重建",
|
||||
"edit_semantics": "保持当前圆柱半径和轴线,围绕当前角度中心填旧扇形槽并切出目标圆弧角度的新扇形槽。",
|
||||
"resize_mode": "widen" if (delta_span or 0.0) > 0 else "narrow",
|
||||
"feature_type": feature.get("feature_type"),
|
||||
"feature_guess": info.get("feature_guess"),
|
||||
@@ -1388,11 +1408,51 @@ class FeatureMixin:
|
||||
except Exception as exc:
|
||||
blockers.append(f"Could not build slot axis move tool plan: {exc}")
|
||||
|
||||
resize_strategy = "fill-old-slot-sector-and-cut-moved-sector"
|
||||
edit_strategy_label = "填旧槽并切新槽"
|
||||
edit_semantics = "先填补当前槽/半孔扇形区域,再按同宽度、同角度在目标轴心切出新槽;不整体平移零件。"
|
||||
if pair_plan:
|
||||
risk = _max_risk(risk, "high")
|
||||
warnings.append(
|
||||
"A paired obround slot end was detected; this axis move edits the selected local sector, not the whole CAD slot sketch."
|
||||
)
|
||||
center_1 = _tuple_or_none(pair_plan.get("slot_capsule_start_center_1"))
|
||||
center_2 = _tuple_or_none(pair_plan.get("slot_capsule_start_center_2"))
|
||||
if center_1 is None or center_2 is None:
|
||||
risk = _max_risk(risk, "high")
|
||||
warnings.append(
|
||||
"A paired obround slot end was detected, but the full-slot centerline could not be derived."
|
||||
)
|
||||
else:
|
||||
pair_plan["slot_capsule_target_start_center_1"] = _tuple_add(center_1, movement)
|
||||
pair_plan["slot_capsule_target_start_center_2"] = _tuple_add(center_2, movement)
|
||||
pair_plan["slot_target_center_distance"] = pair_plan.get("slot_pair_axis_distance", "")
|
||||
pair_plan["slot_current_center_distance"] = pair_plan.get("slot_pair_axis_distance", "")
|
||||
pair_plan["slot_pair_current_axis_center_1"] = current_center
|
||||
pair_plan["slot_pair_target_axis_center_1"] = target_center
|
||||
try:
|
||||
pair_face_id = int(pair_plan.get("slot_pair_face_id", -1))
|
||||
pair_surf = BRepAdaptor_Surface(self.faces[pair_face_id])
|
||||
pair_feature = self.feature_info(pair_face_id)
|
||||
pair_axis_range = self._cylindrical_axis_range(
|
||||
pair_face_id,
|
||||
pair_surf,
|
||||
_int_values(pair_feature.get("feature_side_face_ids")),
|
||||
)
|
||||
pair_cyl = pair_surf.Cylinder()
|
||||
pair_mid_parameter = (float(pair_axis_range["v_min"]) + float(pair_axis_range["v_max"])) * 0.5
|
||||
pair_center = _point_tuple(
|
||||
_point_on_axis(pair_cyl.Axis().Location(), pair_cyl.Axis().Direction(), pair_mid_parameter)
|
||||
)
|
||||
pair_plan["slot_pair_current_axis_center_2"] = pair_center
|
||||
pair_plan["slot_pair_target_axis_center_2"] = _tuple_add(pair_center, movement)
|
||||
except Exception:
|
||||
pair_plan["slot_pair_current_axis_center_2"] = ""
|
||||
pair_plan["slot_pair_target_axis_center_2"] = ""
|
||||
if not pair_plan.get("slot_pair_target_axis_center_2"):
|
||||
blockers.append("Could not derive both obround slot end centers for the axis move.")
|
||||
else:
|
||||
resize_strategy = "paired-obround-slot-axis-prism"
|
||||
edit_strategy_label = "整条长圆槽轴心移动"
|
||||
edit_semantics = "识别到长圆槽另一端后,会填充旧长圆槽包络,再按同槽宽、同总长度在目标轴心重切整条长圆槽;不整体平移零件。"
|
||||
risk = _max_risk(risk, str(pair_plan.get("slot_pair_risk", "medium")))
|
||||
warnings.append("Detected paired partial-cylinder slot ends; axis move will relocate the full obround slot.")
|
||||
|
||||
current_radius = current_diameter * 0.5 if current_diameter is not None else None
|
||||
current_width = (
|
||||
@@ -1412,6 +1472,7 @@ class FeatureMixin:
|
||||
return {
|
||||
**cutter_plan,
|
||||
**fill_plan,
|
||||
**pair_plan,
|
||||
"status": status,
|
||||
"risk": "blocked" if blockers else risk,
|
||||
"message": message,
|
||||
@@ -1455,7 +1516,9 @@ class FeatureMixin:
|
||||
"same_domain_face_count": axis_range.get("same_domain_face_count", 0),
|
||||
"same_domain_v_range": (axis_range.get("v_min"), axis_range.get("v_max")),
|
||||
"same_domain_range_source": axis_range.get("range_source", ""),
|
||||
"resize_strategy": "fill-old-slot-sector-and-cut-moved-sector",
|
||||
"resize_strategy": resize_strategy,
|
||||
"edit_strategy_label": edit_strategy_label,
|
||||
"edit_semantics": edit_semantics,
|
||||
}
|
||||
|
||||
def _paired_obround_slot_plan(
|
||||
@@ -1767,6 +1830,8 @@ class FeatureMixin:
|
||||
"slot_resize_mode": "total_length",
|
||||
"slot_resize_label": "slot total length",
|
||||
"slot_resize_strategy": "paired-obround-slot-length-prism",
|
||||
"edit_strategy_label": "长圆槽总长度重建",
|
||||
"edit_semantics": "保持槽宽不变,围绕长圆槽中心线对两端中心距做对称调整,然后填旧槽并重切目标总长度的长圆槽。",
|
||||
"resize_mode": "lengthen" if (delta_length or 0.0) > 0 else "shorten",
|
||||
"slot_current_total_length": current_total_length,
|
||||
"slot_target_total_length": target_total_length,
|
||||
@@ -1778,6 +1843,68 @@ class FeatureMixin:
|
||||
"slot_capsule_target_start_center_2": target_center_2,
|
||||
}
|
||||
|
||||
def cylindrical_slot_center_distance_plan(
|
||||
self,
|
||||
face_id: int,
|
||||
target_center_distance: float,
|
||||
pair_face_id: int | None = None,
|
||||
) -> dict[str, object]:
|
||||
if face_id < 0 or face_id >= len(self.faces):
|
||||
raise ValueError(f"Unknown face id {face_id}")
|
||||
info = self.face_info(face_id)
|
||||
current_diameter = _float_or_none(info.get("diameter"))
|
||||
if current_diameter is None or current_diameter <= 1e-9:
|
||||
return {
|
||||
"status": "blocked",
|
||||
"risk": "blocked",
|
||||
"message": "Selected face is not a measurable cylindrical slot face.",
|
||||
"blockers": "Selected face is not a measurable cylindrical slot face.",
|
||||
"warnings": "",
|
||||
}
|
||||
try:
|
||||
target_center_distance = float(target_center_distance)
|
||||
except (TypeError, ValueError):
|
||||
target_center_distance = 0.0
|
||||
|
||||
target_total_length = target_center_distance + current_diameter
|
||||
plan = self.cylindrical_slot_total_length_plan(
|
||||
face_id,
|
||||
target_total_length,
|
||||
pair_face_id=pair_face_id,
|
||||
)
|
||||
current_center_distance = _float_or_none(plan.get("slot_current_center_distance"))
|
||||
delta = (
|
||||
None
|
||||
if current_center_distance is None
|
||||
else target_center_distance - current_center_distance
|
||||
)
|
||||
delta_ratio = (
|
||||
None
|
||||
if current_center_distance is None or current_center_distance <= 1e-9 or delta is None
|
||||
else abs(delta) / current_center_distance
|
||||
)
|
||||
if target_center_distance <= 0:
|
||||
blockers = str(plan.get("blockers") or "")
|
||||
extra = "Target slot center distance must be greater than 0."
|
||||
plan["blockers"] = "; ".join(item for item in (blockers, extra) if item)
|
||||
plan["message"] = plan["blockers"]
|
||||
plan["status"] = "blocked"
|
||||
plan["risk"] = "blocked"
|
||||
plan.update(
|
||||
{
|
||||
"slot_resize_mode": "center_distance",
|
||||
"slot_resize_label": "slot center distance",
|
||||
"slot_resize_strategy": "paired-obround-slot-center-distance-prism",
|
||||
"edit_strategy_label": "长圆槽中心距重建",
|
||||
"edit_semantics": "保持槽宽不变,围绕长圆槽中心线对两端半圆中心距做对称调整,然后填旧槽并重切目标中心距的长圆槽。",
|
||||
"slot_target_center_distance": target_center_distance,
|
||||
"slot_target_total_length": target_total_length,
|
||||
"slot_center_distance_delta": delta,
|
||||
"slot_center_distance_delta_ratio": delta_ratio,
|
||||
}
|
||||
)
|
||||
return plan
|
||||
|
||||
def cylindrical_boss_resize_plan(self, face_id: int, new_diameter: float) -> dict[str, object]:
|
||||
if face_id < 0 or face_id >= len(self.faces):
|
||||
raise ValueError(f"Unknown face id {face_id}")
|
||||
@@ -1806,6 +1933,11 @@ class FeatureMixin:
|
||||
height_estimate = float(scoped_info.get("height_estimate", 0.0))
|
||||
target_to_height_ratio = new_diameter / height_estimate if height_estimate > 1e-9 else ""
|
||||
tool_plan = self._bounded_boss_resize_tool_plan(face_id, new_diameter)
|
||||
edit_semantics = (
|
||||
"扩大凸台直径:按当前凸台轴线和估算高度生成目标圆柱补料体,并与所属特征做局部 Fuse。"
|
||||
if resize_mode == "enlarge"
|
||||
else "缩小凸台直径:先移除旧凸台包络,再补回目标直径圆柱;这是局部重建,不是缩放整个零件。"
|
||||
)
|
||||
return {
|
||||
"status": readiness["boss_resize_status"],
|
||||
"risk": readiness["boss_resize_risk"],
|
||||
@@ -1834,6 +1966,9 @@ class FeatureMixin:
|
||||
"material_sample_count": info.get("material_sample_count"),
|
||||
"feature_adjacent_face_ids": feature.get("feature_adjacent_face_ids"),
|
||||
"feature_boundary_edge_ids": feature.get("feature_boundary_edge_ids"),
|
||||
"resize_strategy": "bounded-cylindrical-boss-envelope-rebuild",
|
||||
"edit_strategy_label": "凸台包络重建",
|
||||
"edit_semantics": edit_semantics,
|
||||
**tool_plan,
|
||||
}
|
||||
|
||||
@@ -1980,6 +2115,8 @@ class FeatureMixin:
|
||||
"same_domain_v_range": (axis_range.get("v_min"), axis_range.get("v_max")),
|
||||
"same_domain_range_source": axis_range.get("range_source", ""),
|
||||
"resize_strategy": "remove-old-boss-and-fuse-moved-cylinder",
|
||||
"edit_strategy_label": "移除旧凸台并补新凸台",
|
||||
"edit_semantics": "先移除当前凸台包络,再按同直径在目标轴心补出新凸台;不整体平移零件。",
|
||||
}
|
||||
|
||||
def cylindrical_suppress_plan(self, face_id: int) -> dict[str, object]:
|
||||
@@ -2004,6 +2141,8 @@ class FeatureMixin:
|
||||
scoped_info.update(self._cylinder_end_opening_info(face_id, BRepAdaptor_Surface(self.faces[face_id]), axis_range))
|
||||
readiness = _cylinder_suppress_readiness(scoped_info)
|
||||
fill_plan = self._bounded_cylinder_fill_plan(face_id)
|
||||
fill_plan["fill_strategy"] = "bounded-hole-suppress-fill"
|
||||
fill_plan["fill_note"] = "按当前圆柱孔范围生成略带重叠的补料圆柱体,用于封堵完整通孔或盲孔。"
|
||||
return {
|
||||
"status": readiness["suppress_status"],
|
||||
"risk": readiness["suppress_risk"],
|
||||
@@ -2029,6 +2168,9 @@ class FeatureMixin:
|
||||
"feature_bottom_face_ids": feature.get("feature_bottom_face_ids"),
|
||||
"feature_opening_face_ids": feature.get("feature_opening_face_ids"),
|
||||
"feature_bottom_note": feature.get("feature_bottom_note"),
|
||||
"resize_strategy": "fill-cylindrical-hole-volume",
|
||||
"edit_strategy_label": "圆柱补料封堵",
|
||||
"edit_semantics": "按当前孔轴线和估算高度生成补料圆柱体,局部 Fuse 后封堵当前完整圆柱孔。",
|
||||
**fill_plan,
|
||||
}
|
||||
|
||||
@@ -2106,6 +2248,9 @@ class FeatureMixin:
|
||||
"feature_bottom_confidence": feature.get("feature_bottom_confidence"),
|
||||
"feature_bottom_detection": feature.get("feature_bottom_detection"),
|
||||
"feature_bottom_note": feature.get("feature_bottom_note"),
|
||||
"resize_strategy": "bounded-blind-depth-cut-or-fill",
|
||||
"edit_strategy_label": "盲孔/盲槽深度切削或补料",
|
||||
"edit_semantics": "沿识别到的开口到底面方向调整深度:加深时切削,变浅时从新底面到旧底面补料。",
|
||||
}
|
||||
plan.update(context)
|
||||
return plan
|
||||
@@ -2407,23 +2552,14 @@ class FeatureMixin:
|
||||
old_radius = cyl.Radius()
|
||||
new_radius = new_diameter / 2.0
|
||||
axis_range = self._cylindrical_axis_range(face_id, surf)
|
||||
end_info = self._cylinder_end_opening_info(face_id, surf, axis_range)
|
||||
v_min = float(axis_range["v_min"])
|
||||
v_max = float(axis_range["v_max"])
|
||||
span = max(v_max - v_min, 1e-6)
|
||||
delta_radius = abs(new_radius - old_radius)
|
||||
base_radius = max(old_radius, new_radius)
|
||||
resize_mode = _resize_mode(old_radius * 2.0, new_diameter)
|
||||
if resize_mode == "enlarge":
|
||||
axial_margin = 0.0
|
||||
start_parameter = v_min
|
||||
end_parameter = v_max
|
||||
else:
|
||||
axial_margin = min(
|
||||
max(base_radius * 0.001, delta_radius * 0.01, span * 0.001, 0.001),
|
||||
max(span * 0.01, 0.02),
|
||||
)
|
||||
start_parameter = v_min - axial_margin
|
||||
end_parameter = v_max + axial_margin
|
||||
axial_margin = 0.0
|
||||
start_parameter = v_min
|
||||
end_parameter = v_max
|
||||
radial_overlap = min(max(old_radius * 0.001, 0.001), 0.05)
|
||||
height = max(end_parameter - start_parameter, 1e-6)
|
||||
axis = cyl.Axis()
|
||||
@@ -2436,7 +2572,7 @@ class FeatureMixin:
|
||||
"boss_tool_note": (
|
||||
"扩大凸台会在同域圆柱侧壁整体 V 范围内生成目标半径圆柱并 Fuse;"
|
||||
"缩小凸台会先用旧半径包络体移除原凸台范围,再 Fuse 目标半径圆柱重建。"
|
||||
"当前版本会给轴向两端保留少量重叠,让布尔结果更容易和原实体合并。"
|
||||
"补新凸台时使用原凸台轴向范围,避免直径或轴心修改时悄悄改变高度。"
|
||||
),
|
||||
"boss_tool_scope_face_ids": axis_range["same_domain_face_ids"],
|
||||
"boss_tool_scope_face_count": axis_range["same_domain_face_count"],
|
||||
@@ -2455,6 +2591,7 @@ class FeatureMixin:
|
||||
"boss_tool_start_point": _point_tuple(start),
|
||||
"boss_tool_exact_start_point": _point_tuple(exact_start),
|
||||
"boss_tool_exact_height": max(v_max - v_min, 1e-6),
|
||||
**end_info,
|
||||
}
|
||||
|
||||
def _cylinder_end_opening_info(
|
||||
|
||||
Reference in New Issue
Block a user