feat: 完善参数化编辑语义和槽孔中心距

This commit is contained in:
2026-07-30 17:54:01 +08:00
parent c937e13d43
commit 27e4f7236c
22 changed files with 8769 additions and 668 deletions
+135
View File
@@ -97,6 +97,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
self.edge_part_ids: list[int] = []
self.edge_solid_ids: list[int] = []
self.solids: list[tuple[int, TopoDS_Shape]] = []
self._quick_face_info_cache: dict[int, dict[str, object]] = {}
self._face_info_cache: dict[int, dict[str, object]] = {}
self._feature_info_cache: dict[int, dict[str, object]] = {}
self._edge_info_cache: dict[int, dict[str, object]] = {}
@@ -176,6 +177,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
self.edge_part_ids.clear()
self.edge_solid_ids.clear()
self.solids.clear()
self._quick_face_info_cache.clear()
self._face_info_cache.clear()
self._feature_info_cache.clear()
self._edge_info_cache.clear()
@@ -269,10 +271,128 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
logical_ids = snapshot.get(SNAPSHOT_FACE_LOGICAL_IDS_KEY)
if isinstance(logical_ids, (list, tuple)) and len(logical_ids) == len(self.faces):
self.face_logical_ids = [int(item) for item in logical_ids]
self._quick_face_info_cache.clear()
self._face_info_cache.clear()
self._feature_info_cache.clear()
self._same_domain_face_ids_cache.clear()
def quick_face_info(self, face_id: int) -> dict[str, object]:
if face_id < 0 or face_id >= len(self.faces):
raise ValueError(f"Unknown face id {face_id}")
full_info = self._face_info_cache.get(face_id)
if full_info is not None:
return dict(full_info)
cached = self._quick_face_info_cache.get(face_id)
if cached is not None:
return dict(cached)
face = self.faces[face_id]
props = GProp_GProps()
brepgprop.SurfaceProperties(face, props)
surf = BRepAdaptor_Surface(face)
surface_type = surf.GetType()
surface_label = SURFACE_TYPES.get(surface_type, f"type {surface_type}")
info: dict[str, object] = {
"kind": "face",
"face_id": face_id,
"topological_face_id": face_id,
"logical_face_id": self.face_logical_id(face_id),
"part_id": self.face_part_ids[face_id],
"solid_id": self.face_solid_ids[face_id],
"orientation": _orientation_name(face.Orientation()),
"surface": surface_label,
"area": props.Mass(),
"area_center": _point_tuple(props.CentreOfMass()),
"u_range": (surf.FirstUParameter(), surf.LastUParameter()),
"v_range": (surf.FirstVParameter(), surf.LastVParameter()),
"boundary_edges": len(self._face_boundary_edge_ids(face_id)),
"selection_info_mode": "quick",
"selection_info_note": "快速选择信息;同域面、端盖、底面等深层识别会在执行编辑计划或手动扫描时再计算。",
}
info.update(_shape_bounds_info(face))
if surface_type == GeomAbs_Plane:
plane = surf.Plane()
direction = plane.Axis().Direction()
push_pull_direction = self._plane_push_pull_direction(face_id, surf)
info["plane_origin"] = _point_tuple(plane.Location())
info["normal"] = _dir_tuple(direction)
info["oriented_normal"] = _oriented_dir_tuple(direction, face)
info["push_pull_outward_direction"] = push_pull_direction["outward_direction"]
info["push_pull_inward_direction"] = push_pull_direction["inward_direction"]
info["push_pull_plus_side"] = push_pull_direction["plus_side_state"]
info["push_pull_minus_side"] = push_pull_direction["minus_side_state"]
info["push_pull_confidence"] = push_pull_direction["confidence"]
info["push_pull_note"] = push_pull_direction["note"]
info["push_pull_status"] = "candidate"
info["feature_type"] = "可推拉平面候选"
info["feature_source_face_id"] = face_id
info["feature_highlight_face_ids"] = (face_id,)
info["feature_edit_actions"] = "推拉平面"
info.update(
self._local_face_plane_size_info(
face_id,
face=face,
surf=surf,
center=_tuple_or_none(info.get("area_center")),
)
)
elif surface_type == GeomAbs_Cylinder:
cyl = surf.Cylinder()
axis = cyl.Axis()
radius = cyl.Radius()
u_span = abs(surf.LastUParameter() - surf.FirstUParameter())
swept_area = max(radius * max(u_span, 1e-9), 1e-9)
classification = self._classify_cylindrical_face(face_id, surf, detailed=False)
info["radius"] = radius
info["diameter"] = radius * 2.0
info["axis_point"] = _point_tuple(axis.Location())
info["axis"] = _dir_tuple(axis.Direction())
info["angular_span"] = u_span
info["is_full_cylinder"] = u_span >= math.tau * 0.98
info["height_estimate"] = props.Mass() / swept_area
info["feature_guess"] = classification["feature_guess"]
info["confidence"] = classification["confidence"]
info["material_toward_axis"] = classification["toward_axis"]
info["material_away_axis"] = classification["away_axis"]
info["material_vote_summary"] = classification["vote_summary"]
info["material_sample_count"] = classification["sample_count"]
info["note"] = classification["note"]
info["feature_source_face_id"] = face_id
info["feature_highlight_face_ids"] = (face_id,)
if u_span < math.tau * 0.92:
info["slot_kind"] = "partial-cylindrical-groove"
info["slot_angular_span"] = u_span
info["slot_open_angle"] = max(math.tau - u_span, 0.0)
info["slot_chord_width_estimate"] = 2.0 * radius * math.sin(min(u_span, math.tau) * 0.5)
info["slot_sagitta_depth_estimate"] = radius * (1.0 - math.cos(min(u_span, math.tau) * 0.5))
info["slot_arc_length_estimate"] = radius * u_span
elif surface_type == GeomAbs_Cone:
cone = surf.Cone()
info["axis_point"] = _point_tuple(cone.Location())
info["axis"] = _dir_tuple(cone.Axis().Direction())
info["reference_radius"] = cone.RefRadius()
info["semi_angle"] = cone.SemiAngle()
info["feature_reference_radius"] = cone.RefRadius()
info["feature_reference_diameter"] = cone.RefRadius() * 2.0
elif surface_type == GeomAbs_Sphere:
sphere = surf.Sphere()
info["center"] = _point_tuple(sphere.Location())
info["radius"] = sphere.Radius()
info["diameter"] = sphere.Radius() * 2.0
info["feature_sphere_radius"] = sphere.Radius()
info["feature_sphere_diameter"] = sphere.Radius() * 2.0
elif surface_type == GeomAbs_Torus:
torus = surf.Torus()
info["center"] = _point_tuple(torus.Location())
info["axis"] = _dir_tuple(torus.Axis().Direction())
info["major_radius"] = torus.MajorRadius()
info["minor_radius"] = torus.MinorRadius()
info["feature_torus_major_radius"] = torus.MajorRadius()
info["feature_torus_minor_radius"] = torus.MinorRadius()
self._quick_face_info_cache[face_id] = dict(info)
return dict(info)
def face_info(self, face_id: int) -> dict[str, object]:
if face_id in self._face_info_cache:
return dict(self._face_info_cache[face_id])
@@ -313,6 +433,14 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
info["push_pull_minus_side"] = push_pull_direction["minus_side_state"]
info["push_pull_confidence"] = push_pull_direction["confidence"]
info["push_pull_note"] = push_pull_direction["note"]
info.update(
self._local_face_plane_size_info(
face_id,
face=face,
surf=surf,
center=_tuple_or_none(info.get("area_center")),
)
)
elif surface_type == GeomAbs_Cylinder:
cyl = surf.Cylinder()
axis = cyl.Axis()
@@ -389,6 +517,10 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
except Exception:
return None
def cached_feature_info(self, face_id: int) -> dict[str, object] | None:
cached = self._feature_info_cache.get(face_id)
return dict(cached) if cached is not None else None
def feature_info(self, face_id: int) -> dict[str, object]:
if face_id < 0 or face_id >= len(self.faces):
raise ValueError(f"Unknown face id {face_id}")
@@ -1030,6 +1162,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
return
for face_id in valid_face_ids:
self.face_logical_ids[face_id] = int(logical_id)
self._quick_face_info_cache.pop(face_id, None)
self._face_info_cache.pop(face_id, None)
self._feature_info_cache.pop(face_id, None)
@@ -1492,6 +1625,8 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
ellipse = curve.Ellipse()
info["center"] = _point_tuple(ellipse.Location())
info["axis"] = _dir_tuple(ellipse.Axis().Direction())
info["major_axis"] = _dir_tuple(ellipse.XAxis().Direction())
info["minor_axis"] = _dir_tuple(ellipse.YAxis().Direction())
info["major_radius"] = ellipse.MajorRadius()
info["minor_radius"] = ellipse.MinorRadius()
adjacent_face_ids = self._edge_adjacent_face_ids(edge_id)