Skip to content

Commit 5674a71

Browse files
dastan-ansysjonahrbRobPasMue
authored
Bug/add component shared master (#567)
Co-authored-by: jonahrb <jonahboling@gmail.com> Co-authored-by: Roberto Pastor Muela <37798125+RobPasMue@users.noreply.github.com>
1 parent 741d445 commit 5674a71

File tree

5 files changed

+115
-48
lines changed

5 files changed

+115
-48
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,7 @@ def copy(self, parent: "Component", name: str = None) -> "Body": # noqa: D102
679679
tb = MasterBody(
680680
response.master_id, copy_name, self._grpc_client, is_surface=self.is_surface
681681
)
682-
parent._transformed_part.part.bodies.append(tb)
683-
# TODO: fix when DMS ObjectPath is fixed - previously we return the body with response.id
682+
parent._master_component.part.bodies.append(tb)
684683
body_id = f"{parent.id}/{tb.id}" if parent.parent_component else tb.id
685684
return Body(body_id, response.name, parent, tb)
686685

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

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ class Component:
8787
If a component already exists on the server, you can pass in its ID to create it on the
8888
client-side data model. If this is argument is present, a new Component will not be created
8989
on the server.
90-
transformed_part : MasterComponent, optional
90+
master_component : MasterComponent, optional
9191
This argument should be present when creating a nested instance component. It will use the
92-
given transformed_part instead of creating a new one.
92+
given master_component instead of creating a new one.
9393
read_existing_comp : bool, optional
9494
Indicates whether an existing component on the service should be read
9595
or not. By default, ``False``. This is only valid when connecting
@@ -112,7 +112,7 @@ def __init__(
112112
grpc_client: GrpcClient,
113113
template: Optional["Component"] = None,
114114
preexisting_id: Optional[str] = None,
115-
transformed_part: Optional[MasterComponent] = None,
115+
master_component: Optional[MasterComponent] = None,
116116
read_existing_comp: bool = False,
117117
):
118118
"""Initialize ``Component`` class."""
@@ -146,32 +146,31 @@ def __init__(
146146
self._parent_component = parent_component
147147
self._is_alive = True
148148
self._shared_topology = None
149-
self._transformed_part = transformed_part
149+
self._master_component = master_component
150150

151151
# Populate client data model
152152
if template:
153153
# If this is not a nested instance
154-
if not transformed_part:
154+
if not master_component:
155155
# Create new MasterComponent, but use template's Part
156-
tp = MasterComponent(
156+
master = MasterComponent(
157157
uuid.uuid4(),
158-
f"tp_{name}",
159-
template._transformed_part.part,
160-
template._transformed_part.transform,
158+
f"master_{name}",
159+
template._master_component.part,
160+
template._master_component.transform,
161161
)
162-
tp.part.parts.append(tp)
163-
self._transformed_part = tp
162+
self._master_component = master
164163

165164
# Recurse - Create more children components from template's remaining children
166165
self.__create_children(template)
167-
return
168166

169167
elif not read_existing_comp:
170168
# This is an independent Component - Create new Part and MasterComponent
171169
p = Part(uuid.uuid4(), f"p_{name}", [], [])
172-
tp = MasterComponent(uuid.uuid4(), f"tp_{name}", p)
173-
p.parts.append(tp)
174-
self._transformed_part = tp
170+
master = MasterComponent(uuid.uuid4(), f"master_{name}", p)
171+
self._master_component = master
172+
173+
self._master_component.occurrences.append(self)
175174

176175
@property
177176
def id(self) -> str:
@@ -192,7 +191,7 @@ def components(self) -> List["Component"]:
192191
def bodies(self) -> List[Body]:
193192
"""``Body`` objects inside of the component."""
194193
bodies = []
195-
for body in self._transformed_part.part.bodies:
194+
for body in self._master_component.part.bodies:
196195
id = f"{self.id}/{body.id}" if self.parent_component else body.id
197196
bodies.append(Body(id, body.name, self, body))
198197
return bodies
@@ -242,7 +241,7 @@ def __create_children(self, template: "Component") -> None:
242241
self._grpc_client,
243242
template=template_comp,
244243
preexisting_id=new_id,
245-
transformed_part=template_comp._transformed_part,
244+
master_component=template_comp._master_component,
246245
)
247246
self.components.append(new)
248247

@@ -257,7 +256,7 @@ def get_world_transform(self) -> Matrix44:
257256
"""
258257
if self.parent_component is None:
259258
return IDENTITY_MATRIX44
260-
return self.parent_component.get_world_transform() * self._transformed_part.transform
259+
return self.parent_component.get_world_transform() * self._master_component.transform
261260

262261
@protect_grpc
263262
def modify_placement(
@@ -308,7 +307,7 @@ def modify_placement(
308307
rotation_angle=angle.value.m,
309308
)
310309
)
311-
self._transformed_part.transform = grpc_matrix_to_matrix(response.matrix)
310+
self._master_component.transform = grpc_matrix_to_matrix(response.matrix)
312311

313312
def reset_placement(self):
314313
"""
@@ -336,7 +335,25 @@ def add_component(self, name: str, template: Optional["Component"] = None) -> "C
336335
Component
337336
New component with no children in the design assembly.
338337
"""
339-
self._components.append(Component(name, self, self._grpc_client, template=template))
338+
new_comp = Component(name, self, self._grpc_client, template=template)
339+
master = new_comp._master_component
340+
master_id = new_comp.id.split("/")[-1]
341+
342+
for comp in self._master_component.occurrences:
343+
if comp.id != self.id:
344+
comp.components.append(
345+
Component(
346+
name,
347+
comp,
348+
self._grpc_client,
349+
template,
350+
preexisting_id=f"{comp.id}/{master_id}",
351+
master_component=master,
352+
read_existing_comp=True,
353+
)
354+
)
355+
356+
self.components.append(new_comp)
340357
return self._components[-1]
341358

342359
@protect_grpc
@@ -402,7 +419,7 @@ def extrude_sketch(
402419
self._grpc_client.log.debug(f"Extruding sketch provided on {self.id}. Creating body...")
403420
response = self._bodies_stub.CreateExtrudedBody(request)
404421
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=False)
405-
self._transformed_part.part.bodies.append(tb)
422+
self._master_component.part.bodies.append(tb)
406423
return Body(response.id, response.name, self, tb)
407424

408425
@protect_grpc
@@ -449,7 +466,7 @@ def extrude_face(self, name: str, face: Face, distance: Union[Quantity, Distance
449466
response = self._bodies_stub.CreateExtrudedBodyFromFaceProfile(request)
450467

451468
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=False)
452-
self._transformed_part.part.bodies.append(tb)
469+
self._master_component.part.bodies.append(tb)
453470
return Body(response.id, response.name, self, tb)
454471

455472
@protect_grpc
@@ -486,7 +503,7 @@ def create_surface(self, name: str, sketch: Sketch) -> Body:
486503
response = self._bodies_stub.CreatePlanarBody(request)
487504

488505
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=True)
489-
self._transformed_part.part.bodies.append(tb)
506+
self._master_component.part.bodies.append(tb)
490507
return Body(response.id, response.name, self, tb)
491508

492509
@protect_grpc
@@ -526,7 +543,7 @@ def create_surface_from_face(self, name: str, face: Face) -> Body:
526543
response = self._bodies_stub.CreateBodyFromFace(request)
527544

528545
tb = MasterBody(response.master_id, name, self._grpc_client, is_surface=True)
529-
self._transformed_part.part.bodies.append(tb)
546+
self._master_component.part.bodies.append(tb)
530547
return Body(response.id, response.name, self, tb)
531548

532549
@check_input_types

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -570,25 +570,27 @@ def __read_existing_design(self) -> None:
570570
created_components = {self.id: self}
571571
created_bodies = {}
572572

573-
# Make dummy TP for design since server doesn't have one
574-
self._transformed_part = MasterComponent("1", "tp_design", created_parts[self.id])
573+
# Make dummy master for design since server doesn't have one
574+
self._master_component = MasterComponent("1", "master_design", created_parts[self.id])
575575

576576
# Create MasterComponents
577-
for tp in response.transformed_parts:
578-
part = created_parts.get(tp.part_master.id)
579-
new_tp = MasterComponent(tp.id, tp.name, part, grpc_matrix_to_matrix(tp.placement))
580-
created_tps[tp.id] = new_tp
577+
for master in response.transformed_parts:
578+
part = created_parts.get(master.part_master.id)
579+
new_master = MasterComponent(
580+
master.id, master.name, part, grpc_matrix_to_matrix(master.placement)
581+
)
582+
created_tps[master.id] = new_master
581583

582584
# Create Components
583585
for comp in response.components:
584586
parent = created_components.get(comp.parent_id)
585-
tp = created_tps.get(comp.master_id)
587+
master = created_tps.get(comp.master_id)
586588
c = Component(
587589
comp.name,
588590
parent,
589591
self._grpc_client,
590592
preexisting_id=comp.id,
591-
transformed_part=tp,
593+
master_component=master,
592594
read_existing_comp=True,
593595
)
594596
created_components[comp.id] = c

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

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
"""Provides the ``Part`` class module."""
2-
from beartype.typing import List
2+
from beartype.typing import TYPE_CHECKING, List
33

44
from ansys.geometry.core.designer.body import MasterBody
55
from ansys.geometry.core.math import IDENTITY_MATRIX44, Matrix44
66

7+
if TYPE_CHECKING:
8+
from ansys.geometry.core.designer.component import Component
9+
710

811
class Part:
912
"""
@@ -17,19 +20,19 @@ class Part:
1720
Unique identifier for this part.
1821
name : str
1922
Name of this part.
20-
parts : List[MasterComponent]
23+
components : List[MasterComponent]
2124
List of MasterComponent children that this Part contains.
2225
bodies : List[MasterBody]
2326
List of MasterBody children that this Part contains. These are master bodies.
2427
"""
2528

2629
def __init__(
27-
self, id: str, name: str, parts: List["MasterComponent"], bodies: List[MasterBody]
30+
self, id: str, name: str, components: List["MasterComponent"], bodies: List[MasterBody]
2831
) -> None:
2932
"""Initialize the ``Part`` class."""
3033
self._id: str = id
3134
self._name: str = name
32-
self._parts: List["MasterComponent"] = parts
35+
self._components: List["MasterComponent"] = components
3336
self._bodies: List[MasterBody] = bodies
3437

3538
@property
@@ -43,13 +46,13 @@ def name(self) -> str:
4346
return self._name
4447

4548
@property
46-
def parts(self) -> List["MasterComponent"]:
49+
def components(self) -> List["MasterComponent"]:
4750
"""``MasterComponent`` children that this ``Part`` contains."""
48-
return self._parts
51+
return self._components
4952

50-
@parts.setter
51-
def parts(self, parts: List["MasterComponent"]) -> None:
52-
self._parts = parts
53+
@components.setter
54+
def components(self, components: List["MasterComponent"]) -> None:
55+
self._components = components
5356

5457
@property
5558
def bodies(self) -> List[MasterBody]:
@@ -69,7 +72,7 @@ def __repr__(self) -> str:
6972
return (
7073
f"Part(id={self.id}, "
7174
f"name={self.name}, "
72-
f"parts={[p.name for p in self.parts]}, "
75+
f"parts={[p.name for p in self.components]}, "
7376
f"bodies={[b.name for b in self.bodies]})"
7477
)
7578

@@ -103,7 +106,9 @@ def __init__(
103106
self._id: str = id
104107
self._name: str = name
105108
self._part: Part = part
109+
part.components.append(self)
106110
self._transform: Matrix44 = transform
111+
self._occurrences: List["Component"] = []
107112

108113
@property
109114
def id(self) -> str:
@@ -115,6 +120,11 @@ def name(self) -> str:
115120
"""Name of the transformed part."""
116121
return self._name
117122

123+
@property
124+
def occurrences(self) -> List["Component"]:
125+
"""All occurrences of this component."""
126+
return self._occurrences
127+
118128
@property
119129
def part(self) -> Part:
120130
"""The master part of this transformed part."""

tests/integration/test_design.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,14 +1336,14 @@ def test_component_instances(modeler: Modeler):
13361336
assert len(body_ids) == len(set(body_ids))
13371337

13381338
# Assert all instances have unique MasterComponents
1339-
comp_templates = [wheel2._transformed_part, wheel3._transformed_part, wheel4._transformed_part]
1339+
comp_templates = [wheel2._master_component, wheel3._master_component, wheel4._master_component]
13401340
assert len(comp_templates) == len(set(comp_templates))
13411341

13421342
# Assert all instances have the same Part
13431343
comp_parts = [
1344-
wheel2._transformed_part.part,
1345-
wheel3._transformed_part.part,
1346-
wheel4._transformed_part.part,
1344+
wheel2._master_component.part,
1345+
wheel3._master_component.part,
1346+
wheel4._master_component.part,
13471347
]
13481348
assert len(set(comp_parts)) == 1
13491349

@@ -1612,3 +1612,42 @@ def test_boolean_body_operations(modeler: Modeler, skip_not_on_linux_service):
16121612
assert not copy3.is_alive
16131613
assert body3.is_alive
16141614
assert Accuracy.length_is_equal(copy1.volume.m, 1)
1615+
1616+
1617+
def test_child_component_instances(modeler: Modeler):
1618+
"""Test creation of child ``Component`` instances and check the data model reflects that."""
1619+
1620+
design_name = "ChildComponentInstances_Test"
1621+
design = modeler.create_design(design_name)
1622+
# Create a base component
1623+
base1 = design.add_component("Base1")
1624+
comp1 = base1.add_component("A")
1625+
comp2 = base1.add_component("B")
1626+
1627+
# Create the solid body for the base
1628+
sketch = Sketch().box(Point2D([5, 10]), 10, 20)
1629+
comp2.extrude_sketch("Bottom", sketch, 5)
1630+
1631+
# Create the 2nd base
1632+
base2 = design.add_component("Base2", base1)
1633+
base2.modify_placement(Vector3D([30, 0, 0]))
1634+
1635+
# Create top part (applies to both Base1 and Base2)
1636+
sketch = Sketch(Plane(Point3D([0, 5, 5]))).box(Point2D([5, 2.5]), 10, 5)
1637+
comp1.extrude_sketch("Top", sketch, 5)
1638+
1639+
# create the first child component
1640+
comp1.add_component("Child1")
1641+
comp1.extrude_sketch("Child1_body", Sketch(Plane([5, 7.5, 10])).box(Point2D([0, 0]), 1, 1), 1)
1642+
1643+
assert len(comp1.components) == 1
1644+
assert len(base2.components[0].components) == 1
1645+
assert len(comp1.components) == len(base2.components[0].components)
1646+
1647+
# create the second child component
1648+
comp1.add_component("Child2")
1649+
comp1.extrude_sketch("Child2_body", Sketch(Plane([5, 7.5, 10])).box(Point2D([0, 0]), 1, 1), -1)
1650+
1651+
assert len(comp1.components) == 2
1652+
assert len(base2.components[0].components) == 2
1653+
assert len(comp1.components) == len(base2.components[0].components)

0 commit comments

Comments
 (0)