|
2 | 2 |
|
3 | 3 | from abc import ABCMeta, abstractmethod |
4 | 4 | from collections import defaultdict |
5 | | -from typing import Annotated, List, Literal, Optional, Union |
| 5 | +from typing import Annotated, Dict, List, Literal, Optional, Union |
6 | 6 |
|
7 | 7 | import pydantic as pd |
8 | 8 |
|
@@ -84,11 +84,31 @@ def get_persistent_entity_registry(self, internal_registry, **kwargs): |
84 | 84 | """ |
85 | 85 |
|
86 | 86 |
|
| 87 | +class BodyComponentInfo(Flow360BaseModel): |
| 88 | + """Data model for body component info.""" |
| 89 | + |
| 90 | + face_ids: list[str] = pd.Field( |
| 91 | + description="A full list of face IDs that appear in the body.", |
| 92 | + ) |
| 93 | + edge_ids: Optional[list[str]] = pd.Field( |
| 94 | + None, |
| 95 | + description="A full list of edge IDs that appear in the body. Optional for surface mesh geometry.", |
| 96 | + ) |
| 97 | + |
| 98 | + |
87 | 99 | class GeometryEntityInfo(EntityInfoModel): |
88 | 100 | """Data model for geometry entityInfo.json""" |
89 | 101 |
|
90 | 102 | type_name: Literal["GeometryEntityInfo"] = pd.Field("GeometryEntityInfo", frozen=True) |
91 | 103 |
|
| 104 | + bodies_face_edge_ids: Optional[Dict[str, BodyComponentInfo]] = pd.Field( |
| 105 | + None, |
| 106 | + description="Mapping from body ID to the face and edge IDs of the body.", |
| 107 | + ) |
| 108 | + # bodies_face_edge_ids: Mostly just used by front end. On python side this |
| 109 | + # is less useful as users do not operate on face/body/edge IDs directly. |
| 110 | + # But at least this can replace `face_ids`, `body_ids`, and `edge_ids` since these contains less info. |
| 111 | + |
92 | 112 | body_ids: list[str] = pd.Field( |
93 | 113 | [], |
94 | 114 | description="A full list of body IDs that appear in the geometry.", |
@@ -155,6 +175,45 @@ class GeometryEntityInfo(EntityInfoModel): |
155 | 175 | description="The default value based on uploaded geometry for geometry_accuracy.", |
156 | 176 | ) |
157 | 177 |
|
| 178 | + @property |
| 179 | + def all_face_ids(self) -> list[str]: |
| 180 | + """ |
| 181 | + Returns a full list of face IDs that appear in the geometry. |
| 182 | + Use `bodies_face_edge_ids` if available, otherwise fall back to use `face_ids`. |
| 183 | + """ |
| 184 | + if self.bodies_face_edge_ids is not None: |
| 185 | + return [ |
| 186 | + face_id |
| 187 | + for body_component_info in self.bodies_face_edge_ids.values() |
| 188 | + for face_id in body_component_info.face_ids |
| 189 | + ] |
| 190 | + return self.face_ids |
| 191 | + |
| 192 | + @property |
| 193 | + def all_edge_ids(self) -> list[str]: |
| 194 | + """ |
| 195 | + Returns a full list of edge IDs that appear in the geometry. |
| 196 | + Use `bodies_face_edge_ids` if available, otherwise fall back to use `edge_ids`. |
| 197 | + """ |
| 198 | + if self.bodies_face_edge_ids is not None: |
| 199 | + return [ |
| 200 | + edge_id |
| 201 | + for body_component_info in self.bodies_face_edge_ids.values() |
| 202 | + # edge_ids can be None for surface-only geometry; treat it as an empty list. |
| 203 | + for edge_id in (body_component_info.edge_ids or []) |
| 204 | + ] |
| 205 | + return self.edge_ids |
| 206 | + |
| 207 | + @property |
| 208 | + def all_body_ids(self) -> list[str]: |
| 209 | + """ |
| 210 | + Returns a full list of body IDs that appear in the geometry. |
| 211 | + Use `bodies_face_edge_ids` if available, otherwise fall back to use `body_ids`. |
| 212 | + """ |
| 213 | + if self.bodies_face_edge_ids is not None: |
| 214 | + return list(self.bodies_face_edge_ids.keys()) |
| 215 | + return self.body_ids |
| 216 | + |
158 | 217 | def group_in_registry( |
159 | 218 | self, |
160 | 219 | entity_type_name: Literal["face", "edge", "body", "snappy_body"], |
@@ -420,7 +479,7 @@ def get_persistent_entity_registry(self, internal_registry, **_) -> EntityRegist |
420 | 479 | "face", face_group_tag, registry=internal_registry |
421 | 480 | ) |
422 | 481 |
|
423 | | - if len(self.edge_ids) > 0: |
| 482 | + if len(self.all_edge_ids) > 0: |
424 | 483 | if self.edge_group_tag is None: |
425 | 484 | edge_group_tag = self._get_default_grouping_tag("edge") |
426 | 485 | log.info(f"Using `{edge_group_tag}` as default grouping for edges.") |
@@ -467,7 +526,7 @@ def create_group_to_sub_component_mapping(group): |
467 | 526 | # This likely means the geometry asset is pre-25.5. |
468 | 527 | raise ValueError( |
469 | 528 | "Geometry cloud resource is too old." |
470 | | - " Please consider re-uploading the geometry with newer solver version." |
| 529 | + " Please consider re-uploading the geometry with newer solver version (>25.5)." |
471 | 530 | ) |
472 | 531 |
|
473 | 532 | face_group_by_body_id_to_face = create_group_to_sub_component_mapping( |
|
0 commit comments