Skip to content

Commit 8f3f987

Browse files
jonahrbRobPasMue
andauthored
Read existing design from server (#496)
Co-authored-by: Roberto Pastor Muela <37798125+RobPasMue@users.noreply.github.com>
1 parent b8b54e7 commit 8f3f987

File tree

11 files changed

+415
-62
lines changed

11 files changed

+415
-62
lines changed

src/ansys/geometry/core/connection/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from ansys.geometry.core.connection.client import GrpcClient
44
from ansys.geometry.core.connection.conversions import (
55
frame_to_grpc_frame,
6+
grpc_frame_to_frame,
67
grpc_matrix_to_matrix,
78
plane_to_grpc_plane,
89
point3d_to_grpc_point,

src/ansys/geometry/core/connection/conversions.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,42 @@ def grpc_matrix_to_matrix(m: GRPCMatrix) -> Matrix44:
370370
8,
371371
)
372372
)
373+
374+
375+
def grpc_frame_to_frame(frame: GRPCFrame) -> Frame:
376+
"""Convert an ``ansys.api.geometry.Frame`` grpc message to a ``Frame`` class.
377+
378+
Parameters
379+
----------
380+
GRPCFrame
381+
Geometry service gRPC frame message. The unit for the frame origin is meters.
382+
383+
Returns
384+
-------
385+
frame : Frame
386+
Resulting converted frame.
387+
"""
388+
return Frame(
389+
Point3D(
390+
[
391+
frame.origin.x,
392+
frame.origin.y,
393+
frame.origin.z,
394+
],
395+
DEFAULT_UNITS.SERVER_LENGTH,
396+
),
397+
UnitVector3D(
398+
[
399+
frame.dir_x.x,
400+
frame.dir_x.y,
401+
frame.dir_x.z,
402+
]
403+
),
404+
UnitVector3D(
405+
[
406+
frame.dir_y.x,
407+
frame.dir_y.y,
408+
frame.dir_y.z,
409+
]
410+
),
411+
)

src/ansys/geometry/core/designer/body.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ def imprint_curves(
616616
"""
617617
imprint_curves is not implemented at the TemplateBody level.
618618
Instead, call this method on a Body.
619-
"""
619+
"""
620620
)
621621

622622
@protect_grpc
@@ -632,7 +632,7 @@ def project_curves(
632632
"""
633633
project_curves is not implemented at the TemplateBody level.
634634
Instead, call this method on a Body.
635-
"""
635+
"""
636636
)
637637

638638
@protect_grpc
@@ -680,7 +680,9 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102
680680
response.master_id, copy_name, self._grpc_client, is_surface=self.is_surface
681681
)
682682
parent._transformed_part.part.bodies.append(tb)
683-
return Body(response.id, response.name, parent, tb)
683+
# TODO: fix when DMS ObjectPath is fixed - previously we return the body with response.id
684+
body_id = f"{parent.id}/{tb.id}" if parent.parent_component else tb.id
685+
return Body(body_id, response.name, parent, tb)
684686

685687
@protect_grpc
686688
def tessellate(

src/ansys/geometry/core/designer/component.py

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ class Component:
9090
transformed_part : TransformedPart, optional
9191
This argument should be present when creating a nested instance component. It will use the
9292
given transformed_part instead of creating a new one.
93+
read_existing_comp : bool, optional
94+
Indicates whether an existing component on the service should be read
95+
or not. By default, ``False``. This is only valid when connecting
96+
to an existing service session. Otherwise, avoid using this optional
97+
argument.
9398
"""
9499

95100
# Types of the class instance private attributes
@@ -108,8 +113,10 @@ def __init__(
108113
template: Optional["Component"] = None,
109114
preexisting_id: Optional[str] = None,
110115
transformed_part: Optional[TransformedPart] = None,
116+
read_existing_comp: bool = False,
111117
):
112118
"""Initialize ``Component`` class."""
119+
# Initialize the client and stubs needed
113120
self._grpc_client = grpc_client
114121
self._component_stub = ComponentsStub(self._grpc_client.channel)
115122
self._bodies_stub = BodiesStub(self._grpc_client.channel)
@@ -124,27 +131,27 @@ def __init__(
124131
new_component = self._component_stub.Create(
125132
CreateRequest(name=name, parent=parent_component.id, template=template_id)
126133
)
127-
self._id = new_component.component.id
134+
# Remove this method call once we know Service sends correct ObjectPath id
135+
self._id = self.__remove_duplicate_ids(new_component.component.id)
128136
self._name = new_component.component.name
129137
else:
130138
self._name = name
131139
self._id = None
132140

141+
# Initialize needed instance variables
133142
self._components = []
134143
self._beams = []
135144
self._coordinate_systems = []
136145
self._design_points = []
137146
self._parent_component = parent_component
138147
self._is_alive = True
139148
self._shared_topology = None
140-
self._transformed_part = None
149+
self._transformed_part = transformed_part
141150

142151
# Populate client data model
143152
if template:
144-
if transformed_part:
145-
# Re-use an existing tp if this is a nested instance
146-
self._transformed_part = transformed_part
147-
else:
153+
# If this is not a nested instance
154+
if not transformed_part:
148155
# Create new TransformedPart, but use template's Part
149156
tp = TransformedPart(
150157
uuid.uuid4(),
@@ -157,8 +164,10 @@ def __init__(
157164

158165
# Recurse - Create more children components from template's remaining children
159166
self.__create_children(template)
160-
else:
161-
# Create new Part and TransformedPart since this is creating a new "master"
167+
return
168+
169+
elif not read_existing_comp:
170+
# This is an independent Component - Create new Part and TransformedPart
162171
p = Part(uuid.uuid4(), f"p_{name}", [], [])
163172
tp = TransformedPart(uuid.uuid4(), f"tp_{name}", p)
164173
p.parts.append(tp)
@@ -184,8 +193,7 @@ def bodies(self) -> List[Body]:
184193
"""``Body`` objects inside of the component."""
185194
bodies = []
186195
for body in self._transformed_part.part.bodies:
187-
id = self.id + body.id if self.parent_component else body.id
188-
id = self.__fix_moniker(id)
196+
id = f"{self.id}/{body.id}" if self.parent_component else body.id
189197
bodies.append(Body(id, body.name, self, body))
190198
return bodies
191199

@@ -226,43 +234,37 @@ def shared_topology(self) -> Union[SharedTopologyType, None]:
226234

227235
def __create_children(self, template: "Component") -> None:
228236
"""Create new Component and Body children in ``self`` from ``template``."""
229-
for t_body in template.bodies:
230-
new_id = self.id + ("~" + t_body.id.split("~")[-1])
231-
new_body = Body(new_id, t_body.name, self, t_body._template)
232-
self.bodies.append(new_body)
233-
234237
for template_comp in template.components:
238+
new_id = self.id + "/" + template_comp.id.split("/")[-1]
235239
new = Component(
236240
template_comp.name,
237241
self,
238242
self._grpc_client,
239243
template=template_comp,
240-
preexisting_id=self.__fix_moniker(self.id + template_comp.id),
244+
preexisting_id=new_id,
241245
transformed_part=template_comp._transformed_part,
242246
)
243247
self.components.append(new)
244248

245-
def __fix_moniker(self, string: str) -> str:
246-
"""Format a chain of monikers so the service can identify the entities."""
247-
x = string.split("~")[1:]
248-
if len(x) > 1:
249-
x[0] = x[0].replace("sE", "~sO_~iI", 1)
250-
for s in x[1:-1]:
251-
index = x.index(s)
252-
s = "~" + s if s[0] != "~" else s
253-
s = s.replace("sE", "oO", 1)
254-
s = s.replace("oE", "oO", 1)
255-
if "iI" not in x[index + 1]:
256-
s = s.replace("oO", "oO_~iI", 1)
257-
s = s.replace("___", "__", 1)
258-
x[index] = s
259-
x[-1] = x[-1].replace("sE", "~oE", 1)
260-
x = "".join(x) + "_"
261-
else:
262-
x = "".join(x)
263-
if x[0] != "~":
264-
x = "~" + x
265-
return x
249+
def __remove_duplicate_ids(self, path: str) -> str:
250+
"""
251+
Remove duplicate entries in the ID path.
252+
253+
Notes
254+
-----
255+
This is a safeguard, as the server is known to have issues sometimes.
256+
257+
Examples
258+
--------
259+
This method converts "0:26/0:44/0:44/0:53" to "0:26/0:44/0:53".
260+
"""
261+
# Split the string into a list -> convert list into a set but maintain order
262+
res = []
263+
[res.append(x) for x in path.split("/") if x not in res]
264+
id = "/".join(res)
265+
if id != path:
266+
print("Removed duplicate!")
267+
return id
266268

267269
def get_world_transform(self) -> Matrix44:
268270
"""
@@ -421,7 +423,9 @@ def extrude_sketch(
421423
response = self._bodies_stub.CreateExtrudedBody(request)
422424
tb = TemplateBody(response.master_id, name, self._grpc_client, is_surface=False)
423425
self._transformed_part.part.bodies.append(tb)
424-
return Body(response.id, response.name, self, tb)
426+
# TODO: fix when DMS ObjectPath is fixed - previously we return the body with response.id
427+
body_id = f"{self.id}/{tb.id}" if self.parent_component else tb.id
428+
return Body(body_id, response.name, self, tb)
425429

426430
@protect_grpc
427431
@check_input_types
@@ -468,7 +472,9 @@ def extrude_face(self, name: str, face: Face, distance: Union[Quantity, Distance
468472

469473
tb = TemplateBody(response.master_id, name, self._grpc_client, is_surface=False)
470474
self._transformed_part.part.bodies.append(tb)
471-
return Body(response.id, response.name, self, tb)
475+
# TODO: fix when DMS ObjectPath is fixed - previously we return the body with response.id
476+
body_id = f"{self.id}/{tb.id}" if self.parent_component else tb.id
477+
return Body(body_id, response.name, self, tb)
472478

473479
@protect_grpc
474480
@check_input_types
@@ -505,7 +511,9 @@ def create_surface(self, name: str, sketch: Sketch) -> Body:
505511

506512
tb = TemplateBody(response.master_id, name, self._grpc_client, is_surface=True)
507513
self._transformed_part.part.bodies.append(tb)
508-
return Body(response.id, response.name, self, tb)
514+
# TODO: fix when DMS ObjectPath is fixed - previously we return the body with response.id
515+
body_id = f"{self.id}/{tb.id}" if self.parent_component else tb.id
516+
return Body(body_id, response.name, self, tb)
509517

510518
@protect_grpc
511519
@check_input_types
@@ -545,7 +553,9 @@ def create_surface_from_face(self, name: str, face: Face) -> Body:
545553

546554
tb = TemplateBody(response.master_id, name, self._grpc_client, is_surface=True)
547555
self._transformed_part.part.bodies.append(tb)
548-
return Body(response.id, response.name, self, tb)
556+
# TODO: fix when DMS ObjectPath is fixed - previously we return the body with response.id
557+
body_id = f"{self.id}/{tb.id}" if self.parent_component else tb.id
558+
return Body(body_id, response.name, self, tb)
549559

550560
@check_input_types
551561
def create_coordinate_system(self, name: str, frame: Frame) -> CoordinateSystem:

src/ansys/geometry/core/designer/coordinate_system.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from ansys.api.geometry.v0.coordinatesystems_pb2 import CreateRequest
44
from ansys.api.geometry.v0.coordinatesystems_pb2_grpc import CoordinateSystemsStub
5-
from beartype.typing import TYPE_CHECKING
5+
from beartype.typing import TYPE_CHECKING, Optional
66

77
from ansys.geometry.core.connection import GrpcClient, frame_to_grpc_frame
88
from ansys.geometry.core.errors import protect_grpc
@@ -33,12 +33,25 @@ class CoordinateSystem:
3333

3434
@protect_grpc
3535
def __init__(
36-
self, name: str, frame: Frame, parent_component: "Component", grpc_client: GrpcClient
36+
self,
37+
name: str,
38+
frame: Frame,
39+
parent_component: "Component",
40+
grpc_client: GrpcClient,
41+
preexisting_id: Optional[str] = None,
3742
):
3843
"""Initialize ``CoordinateSystem`` class."""
3944
self._parent_component = parent_component
4045
self._grpc_client = grpc_client
4146
self._coordinate_systems_stub = CoordinateSystemsStub(grpc_client.channel)
47+
self._is_alive = True
48+
49+
# Create without going to server
50+
if preexisting_id:
51+
self._name = name
52+
self._frame = frame
53+
self._id = preexisting_id
54+
return
4255

4356
self._grpc_client.log.debug("Requesting creation of Coordinate System.")
4457
new_coordinate_system = self._coordinate_systems_stub.Create(
@@ -75,7 +88,6 @@ def __init__(
7588
]
7689
),
7790
)
78-
self._is_alive = True
7991

8092
@property
8193
def id(self) -> str:

0 commit comments

Comments
 (0)