feat: 推进SCDM-first后端接入和大模型编辑优化

This commit is contained in:
2026-08-19 10:28:09 +08:00
parent 722256a41f
commit a3eb7e2476
28 changed files with 9666 additions and 168 deletions
+75 -3
View File
@@ -226,6 +226,62 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
info.update(_shape_volume_info(self.shape))
return info
def scdm_local_face_signatures(self) -> list[dict[str, object]]:
"""Return cheap geometry hints used to map SCDM raw objects back to local Face IDs."""
signatures: list[dict[str, object]] = []
for face_id, face in enumerate(self.faces):
try:
surf = BRepAdaptor_Surface(face)
surface_type = surf.GetType()
except Exception:
continue
signature: dict[str, object] = {
"faceId": int(face_id),
"logicalFaceId": self.face_logical_id(face_id),
"surfaceType": SURFACE_TYPES.get(surface_type, f"type {surface_type}"),
}
try:
if surface_type == GeomAbs_Plane:
plane = surf.Plane()
origin = _point_tuple(plane.Location())
normal = _dir_tuple(plane.Axis().Direction())
signature["center"] = origin
signature["axis"] = normal
signature["planeOffset"] = (
origin[0] * normal[0]
+ origin[1] * normal[1]
+ origin[2] * normal[2]
)
elif surface_type == GeomAbs_Cylinder:
cylinder = surf.Cylinder()
axis = cylinder.Axis()
radius = cylinder.Radius()
signature["center"] = _point_tuple(axis.Location())
signature["axis"] = _dir_tuple(axis.Direction())
signature["radius"] = radius
signature["diameter"] = radius * 2.0
elif surface_type == GeomAbs_Cone:
cone = surf.Cone()
signature["center"] = _point_tuple(cone.Location())
signature["axis"] = _dir_tuple(cone.Axis().Direction())
signature["radius"] = cone.RefRadius()
elif surface_type == GeomAbs_Sphere:
sphere = surf.Sphere()
radius = sphere.Radius()
signature["center"] = _point_tuple(sphere.Location())
signature["radius"] = radius
signature["diameter"] = radius * 2.0
elif surface_type == GeomAbs_Torus:
torus = surf.Torus()
signature["center"] = _point_tuple(torus.Location())
signature["axis"] = _dir_tuple(torus.Axis().Direction())
signature["majorRadius"] = torus.MajorRadius()
signature["minorRadius"] = torus.MinorRadius()
except Exception:
pass
signatures.append(signature)
return signatures
def refresh_topology(self) -> None:
self._topology_refresh_generation = int(getattr(self, "_topology_refresh_generation", 0)) + 1
self.shape = _compound_from_shapes([p.shape for p in self.display_parts()])
@@ -746,7 +802,8 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
This intentionally avoids material-side sampling. It only combines
directly connected co-cylindrical fragments and uses face orientation as
a hint, so full edit plans still recompute and guard the real feature
a hint. It must not trigger Analysis Situs or the internal recognition
graph; full edit plans still recompute and guard the real feature
semantics before changing geometry.
"""
radius = _float_or_none(info.get("radius")) or 0.0
@@ -765,7 +822,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
same_domain_note = "快速识别:当前圆柱没有检测到直接相接的同域碎面。"
axis_range: dict[str, object] | None = None
try:
side_face_ids = self.connected_same_domain_face_ids(face_id) or [face_id]
side_face_ids = self._connected_cocylindrical_face_ids(face_id) or [face_id]
spans: list[float] = []
for side_id in side_face_ids:
side_surf = BRepAdaptor_Surface(self.faces[side_id])
@@ -1044,6 +1101,21 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
except Exception:
return None
def face_plane_position_along(self, face_id: int, direction: tuple[float, float, float]) -> tuple[float, tuple[float, float, float]] | None:
if face_id < 0 or face_id >= len(self.faces):
return None
try:
surf = BRepAdaptor_Surface(self.faces[face_id])
if surf.GetType() != GeomAbs_Plane:
return None
plane = surf.Plane()
origin = _point_tuple(plane.Location())
normal = _dir_tuple(plane.Axis().Direction())
value = origin[0] * direction[0] + origin[1] * direction[1] + origin[2] * direction[2]
return float(value), normal
except Exception:
return None
def _recognition_summary_fields(self, info: dict[str, object]) -> dict[str, object]:
surface = str(info.get("surface") or "")
user_priority = feature_recognition_priority(info)
@@ -4663,7 +4735,7 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
return list(self._internal_hole_region_cache.get(face_id, ()))
def _can_use_internal_recognition_graph(self) -> bool:
return bool(self.faces)
return bool(self.faces) and len(self.faces) <= 1000
def _load_internal_hole_regions(self) -> None:
self._internal_hole_regions_attempted = True