feat: 完善 STEP 一级参数化编辑识别与关系式建模

This commit is contained in:
2026-08-14 18:42:39 +08:00
parent 70b59c1de6
commit a633b5a338
26 changed files with 4676 additions and 77 deletions
+92 -4
View File
@@ -61,10 +61,78 @@ from OCC.Extend.TopologyUtils import TopologyExplorer, discretize_edge
from .constants import CURVE_TYPES, SNAPSHOT_FACE_LOGICAL_IDS_KEY, SURFACE_TYPES
from .geometry_utils import * # noqa: F403
from .recognition_priority import feature_recognition_sort_key
from .recognition_priority import external_relation_score_bonus, feature_recognition_sort_key
class FeatureMixin:
def _external_candidate_relation_fields(self, candidate: dict[str, object]) -> dict[str, object]:
face_ids = (
_int_values(candidate.get("feature_highlight_face_ids"))
or _int_values(candidate.get("feature_face_ids"))
or _int_values(candidate.get("face_region_ids"))
or _int_values(candidate.get("face_id"))
)
if not face_ids and str(candidate.get("target_kind") or "") == "face":
face_ids = _int_values(candidate.get("target_id"))
relation_summary_by_type: dict[str, int] = {}
relation_face_count = 0
summary_getter = getattr(self, "_asitus_face_summary_fields", None)
if not callable(summary_getter):
return {}
for face_id in sorted(set(face_ids)):
try:
fields = summary_getter(face_id)
except Exception:
continue
if not isinstance(fields, dict):
continue
relation_count = int(fields.get("asitus_geometric_relation_count") or 0)
if relation_count <= 0:
continue
relation_face_count += 1
for relation_type in fields.get("asitus_geometric_relation_types", ()) or ():
relation_key = str(relation_type or "").strip()
if relation_key:
relation_summary_by_type[relation_key] = relation_summary_by_type.get(relation_key, 0) + 1
if not relation_summary_by_type:
return {}
relation_summary = ", ".join(
f"{key}:{value}" for key, value in sorted(relation_summary_by_type.items())
)
result = {
"external_recognition_relation_source": "analysis-situs",
"external_recognition_relation_summary": relation_summary,
"external_recognition_relation_types": tuple(sorted(relation_summary_by_type)),
"external_recognition_relation_count": sum(relation_summary_by_type.values()),
"external_recognition_relation_face_count": relation_face_count,
}
hint_getter = getattr(self, "_asitus_cylindrical_feature_hint_fields", None)
if callable(hint_getter) and len(set(face_ids)) == 1:
try:
hint_face_id = int(face_ids[0])
hint_context = {**candidate, **result}
cached_feature_getter = getattr(self, "cached_feature_info", None)
cached_feature = cached_feature_getter(hint_face_id) if callable(cached_feature_getter) else None
if isinstance(cached_feature, dict):
hint_context = {**cached_feature, **hint_context}
result.update(hint_getter(hint_face_id, hint_context))
except Exception:
pass
result["recognition_external_relation_score_bonus"] = external_relation_score_bonus(result)
return result
def _with_external_candidate_relation_support(self, candidate: dict[str, object]) -> dict[str, object]:
result = dict(candidate)
fields = self._external_candidate_relation_fields(result)
if not fields:
return result
result.update(fields)
bonus = int(result.get("recognition_external_relation_score_bonus") or 0)
confidence = str(result.get("confidence") or "")
if bonus >= 8 and confidence in {"", "pending", "unchecked", "none", "low"}:
result["confidence"] = "medium"
return result
def _first_level_fact_plan_fields(self, face_id: int, scope: str) -> dict[str, object]:
try:
return self.face_first_level_facts(face_id, scope=scope)
@@ -1005,6 +1073,7 @@ class FeatureMixin:
)
ellipse_edge_minor_radius_count += 1
candidates = [self._with_external_candidate_relation_support(candidate) for candidate in candidates]
for candidate in candidates:
candidate["recognition_user_priority"] = feature_recognition_sort_key(candidate)[0]
candidates.sort(key=feature_recognition_sort_key)
@@ -1094,12 +1163,21 @@ class FeatureMixin:
"boss_resize_note",
"recognition_risk",
"recognition_blockers",
"analysis_situs_feature_hint_status",
"analysis_situs_feature_hint_preferred",
"analysis_situs_feature_hint_label",
"analysis_situs_feature_hint_score",
"analysis_situs_feature_hint_summary",
"analysis_situs_feature_hint_related_face_ids",
"analysis_situs_slot_hint_score",
"analysis_situs_boss_hint_score",
"analysis_situs_fillet_hint_score",
):
if feature.get(key) not in {None, ""}:
candidate[key] = feature.get(key)
except Exception:
pass
candidates.append(candidate)
candidates.append(self._with_external_candidate_relation_support(candidate))
if len(candidates) >= limit:
break
result = [dict(item) for item in candidates]
@@ -1248,7 +1326,13 @@ class FeatureMixin:
blockers.append("Target cylinder axis center must be three numeric coordinates.")
current_diameter = _float_or_none(info.get("diameter"))
angular_span = _float_or_none(info.get("angular_span"))
selected_angular_span = _float_or_none(info.get("angular_span"))
angular_span = (
_float_or_none(feature.get("same_domain_angular_span"))
or _float_or_none(info.get("same_domain_angular_span"))
or _float_or_none(feature.get("angular_span"))
or selected_angular_span
)
feature_guess = str(info.get("feature_guess", ""))
confidence = str(info.get("confidence", "low"))
surf = BRepAdaptor_Surface(self.faces[face_id])
@@ -1357,13 +1441,17 @@ class FeatureMixin:
"axis_move_radial_distance": radial_distance,
"axis": axis_direction,
"angular_span": angular_span,
"selected_angular_span": selected_angular_span,
"same_domain_angular_span": feature.get("same_domain_angular_span") or info.get("same_domain_angular_span"),
"is_full_cylinder": feature.get("is_full_cylinder", info.get("is_full_cylinder")),
"same_domain_face_ids": axis_range.get("same_domain_face_ids", ()),
"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", ""),
"supports_isolation": True,
"resize_strategy": "fill-old-cylinder-and-cut-moved-cylinder",
"edit_strategy_label": "填旧孔并切新孔",
"edit_semantics": "先填补当前完整圆柱孔,再按同直径在目标轴心切出新孔;这会改变孔的位置,不会整体平移零件。",
"edit_semantics": "先填补当前完整圆柱孔,再按同直径在目标位置切出新孔;这会改变孔的位置,不会整体平移零件。",
}
def cylindrical_slot_resize_plan(