feat: 完善 STEP/B-Rep 一级关系参数化编辑

This commit is contained in:
2026-08-07 18:08:32 +08:00
parent eef9efcc1e
commit 12250603dd
26 changed files with 4254 additions and 1270 deletions
+65 -31
View File
@@ -61,6 +61,7 @@ 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
class FeatureMixin:
@@ -318,6 +319,9 @@ class FeatureMixin:
feature_guess = str(item["feature_guess"])
if existing_fillet_count < existing_fillet_limit and feature_guess == "round/fillet candidate":
feature = self.feature_info(int(item["face_id"]))
existing_fillet_status = str(feature.get("existing_fillet_status") or "candidate")
if existing_fillet_status == "blocked":
continue
support_face_ids = tuple(feature.get("feature_existing_fillet_support_face_ids", ()))
support_note = (
f"支撑 Face: {support_face_ids}"
@@ -349,7 +353,10 @@ class FeatureMixin:
)
existing_fillet_count += 1
if diameter_count < per_type_limit and feature_guess != "round/fillet candidate":
if diameter_count < per_type_limit and feature_guess not in {
"round/fillet candidate",
"boss/outer-round candidate",
}:
candidates.append(
{
"operation_key": "resize_cylinder",
@@ -735,6 +742,8 @@ class FeatureMixin:
chamfer_edge_count = 0
edge_length_count = 0
circle_edge_radius_count = 0
ellipse_edge_major_radius_count = 0
ellipse_edge_minor_radius_count = 0
edge_type_limit = max(1, per_type_limit // 3)
edge_scan_limit = len(self.edges) if max_scan_edges is None else min(len(self.edges), max(0, int(max_scan_edges)))
for edge_id, edge in enumerate(self.edges[:edge_scan_limit]):
@@ -750,6 +759,7 @@ class FeatureMixin:
curve = BRepAdaptor_Curve(edge)
is_line_edge = curve.GetType() == GeomAbs_Line
is_circle_edge = curve.GetType() == GeomAbs_Circle
is_ellipse_edge = curve.GetType() == GeomAbs_Ellipse
props = GProp_GProps()
brepgprop.LinearProperties(edge, props)
length = props.Mass()
@@ -799,7 +809,7 @@ class FeatureMixin:
}
)
chamfer_edge_count += 1
if edge_length_count < edge_type_limit:
if (is_line_edge or is_circle_edge) and edge_length_count < edge_type_limit:
candidates.append(
{
"operation_key": "resize_edge_length",
@@ -814,9 +824,9 @@ class FeatureMixin:
"current_value": length,
"current_value_label": "length",
"status": "caution",
"risk": "medium" if is_line_edge or is_circle_edge else "high",
"risk": "medium",
"confidence": "pending",
"note": "快速扫描:直线边优先局部形变;圆边优先换算相邻圆柱直径;椭圆/平面曲线会尝试径向缩放;其他边使用后备策略",
"note": "快速扫描:直线边优先局部形变或端面移动;圆边优先换算相邻圆柱直径。",
}
)
edge_length_count += 1
@@ -846,34 +856,58 @@ class FeatureMixin:
}
)
circle_edge_radius_count += 1
if is_ellipse_edge:
ellipse = curve.Ellipse()
if ellipse_edge_major_radius_count < edge_type_limit:
major_radius = float(ellipse.MajorRadius())
if major_radius > 1e-9:
candidates.append(
{
"operation_key": "resize_ellipse_edge_major_radius",
"operation": "修改椭圆Edge主半径",
"target_kind": "edge",
"target_id": edge_id,
"edge_id": edge_id,
"part_id": self.edge_part_ids[edge_id],
"solid_id": solid_id,
"surface": "edge",
"feature_guess": "elliptical edge major radius candidate",
"current_value": major_radius,
"current_value_label": "major_radius",
"status": "caution",
"risk": "medium",
"confidence": "pending",
"note": "快速扫描:点击后会选中椭圆Edge;在参数表里按主轴方向单轴缩放所属对象。",
}
)
ellipse_edge_major_radius_count += 1
if ellipse_edge_minor_radius_count < edge_type_limit:
minor_radius = float(ellipse.MinorRadius())
if minor_radius > 1e-9:
candidates.append(
{
"operation_key": "resize_ellipse_edge_minor_radius",
"operation": "修改椭圆Edge小半径",
"target_kind": "edge",
"target_id": edge_id,
"edge_id": edge_id,
"part_id": self.edge_part_ids[edge_id],
"solid_id": solid_id,
"surface": "edge",
"feature_guess": "elliptical edge minor radius candidate",
"current_value": minor_radius,
"current_value_label": "minor_radius",
"status": "caution",
"risk": "medium",
"confidence": "pending",
"note": "快速扫描:点击后会选中椭圆Edge;在参数表里按小轴方向单轴缩放所属对象。",
}
)
ellipse_edge_minor_radius_count += 1
status_order = {"ready": 0, "caution": 1, "blocked": 2}
risk_order = {"low": 0, "medium": 1, "high": 2, "blocked": 3}
operation_order = {
"resize_cylinder": 0,
"resize_slot_width": 1,
"resize_slot_depth": 2,
"resize_slot_arc_length": 3,
"resize_slot_angular_span": 4,
"resize_boss": 5,
"resize_boss_height": 6,
"suppress_cylinder": 7,
"resize_depth": 8,
"inspect_existing_fillet": 9,
"resize_shell_thickness": 10,
"push_pull_plane": 11,
"fillet_edge": 12,
"chamfer_edge": 13,
"resize_edge_length": 14,
}
candidates.sort(
key=lambda item: (
status_order.get(str(item["status"]), 9),
risk_order.get(str(item["risk"]), 9),
operation_order.get(str(item["operation_key"]), 9),
int(item.get("target_id", item.get("face_id", item.get("edge_id", -1)))),
)
)
for candidate in candidates:
candidate["recognition_user_priority"] = feature_recognition_sort_key(candidate)[0]
candidates.sort(key=feature_recognition_sort_key)
return candidates[:limit]
def cylindrical_feature_candidates(