feat: 完善 STEP 一级参数化编辑识别与关系式建模
This commit is contained in:
+92
-4
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user