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
+119
View File
@@ -788,6 +788,8 @@ def main() -> int:
"slot_chord_width_estimate": 6.0,
"slot_sagitta_depth_estimate": 3.0,
"slot_arc_length_estimate": 9.42477796076938,
"slot_status": "candidate",
"slot_kind": "partial-cylindrical-groove",
"area": 188.0,
"area_center": (0.0, 0.0, 0.0),
"bbox_center": (0.0, 0.0, 0.0),
@@ -815,6 +817,27 @@ def main() -> int:
("槽宽", "槽深", "轴心"),
"slot/half-hole feature",
)
blocked_slot_specs = _display_specs(
{
**slot_info,
"slot_status": "blocked",
"slot_blockers": "交叉槽/多槽组暂未实现稳定修改。",
"recognition_risk": "blocked",
"recognition_blockers": "交叉槽/多槽组暂未实现稳定修改。",
}
)
_assert_keys_absent(
blocked_slot_specs,
(
"slot_axis_center",
"slot_chord_width_estimate",
"slot_sagitta_depth_estimate",
"slot_arc_length_estimate",
"slot_angular_span_degrees",
"slot_open_angle_degrees",
),
"blocked complex slot display specs",
)
boss_info = {
"surface": "cylinder",
@@ -878,6 +901,94 @@ def main() -> int:
("工程特征", "倒圆角", "重新倒圆"),
"existing fillet feature",
)
fillet_arc_local = _scope_mode(fillet_specs, "existing_fillet_arc_length_estimate", "local")
if fillet_arc_local.get("action") != "resize_existing_fillet":
raise SystemExit(f"existing fillet arc length should rebuild existing fillet: {fillet_arc_local}")
if fillet_arc_local.get("target_transform") != "arc_length_to_radius":
raise SystemExit(f"existing fillet arc length should transform to target radius: {fillet_arc_local}")
chain_fillet_info = dict(fillet_info)
chain_fillet_info.update(
{
"feature_type": "简单等半径圆角链候选",
"feature_existing_fillet_chain_face_ids": (10, 11),
"feature_existing_fillet_chain_adjacent_face_ids": (11,),
"existing_fillet_chain_status": "same-radius-chain-candidate",
"existing_fillet_status": "candidate",
"existing_fillet_risk": "high",
}
)
chain_fillet_specs = _specs(chain_fillet_info)
chain_fillet_keys = {str(spec.get("key", "")) for spec in chain_fillet_specs}
_assert_contains(
chain_fillet_keys,
{"existing_fillet_radius_estimate", "existing_fillet_arc_length_estimate"},
"same-radius existing fillet chain feature",
)
chain_arc_local = _scope_mode(chain_fillet_specs, "existing_fillet_arc_length_estimate", "local")
if chain_arc_local.get("action") != "resize_existing_fillet":
raise SystemExit(f"same-radius fillet chain arc length should rebuild the chain: {chain_arc_local}")
if chain_arc_local.get("target_transform") != "arc_length_to_radius":
raise SystemExit(f"same-radius fillet chain arc length should transform to target radius: {chain_arc_local}")
blocked_fillet_info = dict(fillet_info)
blocked_fillet_info.update(
{
"existing_fillet_status": "blocked",
"existing_fillet_risk": "blocked",
"existing_fillet_blockers": "当前圆角与不同半径的圆角面直接相连,属于变半径圆角链。",
"feature_existing_fillet_chain_face_ids": (10, 11),
"feature_existing_fillet_mixed_radius_chain_face_ids": (11,),
}
)
blocked_fillet_keys = _spec_keys(blocked_fillet_info)
if "existing_fillet_radius_estimate" in blocked_fillet_keys or "existing_fillet_arc_length_estimate" in blocked_fillet_keys:
raise SystemExit(f"blocked fillet chain should not expose editable fillet dimensions: {blocked_fillet_keys}")
complex_same_radius_fillet_info = dict(fillet_info)
complex_same_radius_fillet_info.update(
{
"existing_fillet_status": "blocked",
"existing_fillet_risk": "blocked",
"existing_fillet_blockers": "当前圆角链包含至少 5 个同半径圆角面;复杂长链暂未实现稳定重建。",
"existing_fillet_chain_status": "complex-same-radius-chain-candidate",
"feature_existing_fillet_chain_face_ids": (10, 11, 12, 13, 14),
"feature_existing_fillet_same_radius_chain_face_ids": (11, 12, 13, 14),
}
)
complex_same_radius_fillet_keys = _spec_keys(complex_same_radius_fillet_info)
if (
"existing_fillet_radius_estimate" in complex_same_radius_fillet_keys
or "existing_fillet_arc_length_estimate" in complex_same_radius_fillet_keys
):
raise SystemExit(
"complex same-radius fillet chain should not expose editable fillet dimensions: "
f"{complex_same_radius_fillet_keys}"
)
chamfer_info = {
"kind": "feature",
"surface": "plane",
"feature_guess": "chamfer candidate",
"feature_type": "已有倒角平面候选",
"existing_chamfer_status": "candidate",
"existing_chamfer_distance_estimate": 1.5,
"existing_chamfer_cross_edge_length_estimate": 2.121320343559643,
"feature_existing_chamfer_support_face_ids": (1, 2),
"feature_existing_chamfer_long_edge_ids": (10, 11),
"local_face_width": 10.0,
"local_face_height": 2.121320343559643,
"area_center": (0.75, 0.75, 5.0),
"face_center_position": (0.75, 0.75, 5.0),
}
chamfer_specs = _specs(chamfer_info)
chamfer_keys = {str(spec.get("key", "")) for spec in chamfer_specs}
if "existing_chamfer_distance_estimate" not in chamfer_keys:
raise SystemExit(f"existing chamfer should expose distance: {chamfer_keys}")
for leaked_key in ("local_face_width", "local_face_height", "face_center_position", "face_target_normal_position"):
if leaked_key in chamfer_keys:
raise SystemExit(f"existing chamfer should not expose generic Face edit {leaked_key}: {chamfer_keys}")
_assert_label(chamfer_specs, "existing_chamfer_distance_estimate", "倒角距离")
chamfer_distance_spec = _spec(chamfer_specs, "existing_chamfer_distance_estimate")
if chamfer_distance_spec.get("action") != "resize_existing_chamfer":
raise SystemExit(f"existing chamfer distance should call resize_existing_chamfer: {chamfer_distance_spec}")
analytic_cases = (
(
@@ -1002,6 +1113,14 @@ def main() -> int:
{"length", "edge_first_level_topology"},
"line Edge",
)
keep_relation_mode = _scope_mode(edge_specs, "length", "keep-first-level-planar-relations")
if str(keep_relation_mode.get("label") or "") != "保持关系":
raise SystemExit(f"line Edge should expose a compact keep-relations scope: {keep_relation_mode}")
if not bool(keep_relation_mode.get("enabled")):
raise SystemExit(f"line Edge keep-relations scope should be enabled: {keep_relation_mode}")
keep_relation_tip = f"{keep_relation_mode.get('enabled_tip', '')} {keep_relation_mode.get('range_hint', '')}"
if "一级平面" not in keep_relation_tip or "平行/垂直" not in keep_relation_tip:
raise SystemExit(f"line Edge keep-relations scope should explain the planar relation constraint: {keep_relation_tip}")
_assert_contains(
{str(spec.get("key", "")) for spec in _edge_display_specs({"curve": "line", "length": 10.0})},
{"length", "edge_length_anchor_mode"},