Skip to content

Commit 438f2e3

Browse files
authored
Merge pull request #58 from BlockResearchGroup/viewer_objects
Adding Group and viewer objects
2 parents e90c17e + 1a18e02 commit 438f2e3

File tree

14 files changed

+233
-57
lines changed

14 files changed

+233
-57
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* Added back `Group` Element.
13+
* Added `ElementObject` and `ModelElement` for `compas_viewer`.
14+
1215
### Changed
1316

17+
* Updated existing `ElementObject` and `ModelElement` for renewed `SceneObject` APIs.
18+
1419
### Removed
1520

1621

docs/examples/elements/group.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from compas.geometry import Polygon
2+
from compas.geometry import Translation
3+
from compas_model.elements import BeamElement
4+
from compas_model.elements import PlateElement
5+
from compas_model.models import Model
6+
from compas_viewer import Viewer
7+
8+
beam1 = BeamElement(0.2, 0.3, 3)
9+
beam2 = BeamElement(0.2, 0.3, 3)
10+
beam3 = BeamElement(0.2, 0.3, 3)
11+
beam4 = BeamElement(0.2, 0.3, 3)
12+
13+
beam1.transformation = Translation.from_vector([3, 3, 0])
14+
beam2.transformation = Translation.from_vector([-3, 3, 0])
15+
beam3.transformation = Translation.from_vector([-3, -3, 0])
16+
beam4.transformation = Translation.from_vector([3, -3, 0])
17+
18+
19+
points: list[list[float]] = [
20+
[-3, -3, 0],
21+
[-3, 3, 0],
22+
[3, 3, 0],
23+
[3, -3, 0],
24+
]
25+
polygon: Polygon = Polygon(points)
26+
plate = PlateElement(polygon=polygon, thickness=0.2)
27+
plate.transformation = Translation.from_vector([0, 0, 3])
28+
29+
model = Model()
30+
group = model.add_group(name="Beams")
31+
model.add_element(beam1, parent=group)
32+
model.add_element(beam2, parent=group)
33+
model.add_element(beam3, parent=group)
34+
model.add_element(beam4, parent=group)
35+
model.add_element(plate)
36+
37+
# Vizualize.
38+
viewer = Viewer()
39+
viewer.scene.add(model)
40+
viewer.show()

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ ruff
88
sphinx_compas2_theme
99
twine
1010
wheel
11+
compas_viewer # For building documentation

src/compas_model/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@
2020
"compas_model.rhino",
2121
"compas_model.rhino.scene",
2222
"compas_model.notebook.scene",
23+
"compas_model.viewer",
2324
]

src/compas_model/elements/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from .column import ColumnFeature
88
from .plate import PlateElement
99
from .plate import PlateFeature
10-
10+
from .group import Group
1111

1212
__all__ = [
1313
"reset_computed",
@@ -19,4 +19,5 @@
1919
"ColumnFeature",
2020
"PlateElement",
2121
"PlateFeature",
22+
"Group",
2223
]

src/compas_model/elements/element.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,21 @@ def material(self) -> Union[Material, None]:
164164
return self._material
165165

166166
@property
167-
def parent(self) -> "ElementNode":
167+
def parentnode(self) -> "ElementNode":
168168
return self.treenode.parent
169169

170+
@property
171+
def parent(self) -> "Element":
172+
return self.parentnode.element
173+
174+
@property
175+
def childnodes(self) -> "list[ElementNode]":
176+
return self.treenode.children
177+
178+
@property
179+
def children(self) -> "list[Element]":
180+
return [child.element for child in self.childnodes]
181+
170182
@property
171183
def features(self) -> list[Feature]:
172184
return self._features
@@ -285,9 +297,8 @@ def compute_modeltransformation(self) -> Transformation:
285297
parent = self.parent
286298

287299
while parent:
288-
if parent.element:
289-
if parent.element.transformation:
290-
stack.append(parent.element.transformation)
300+
if parent.transformation:
301+
stack.append(parent.transformation)
291302
parent = parent.parent
292303

293304
if self.model.transformation:

src/compas_model/elements/group.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .element import Element
2+
3+
4+
class Group(Element):
5+
pass

src/compas_model/models/elementtree.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ def __init__(self, element: Optional[Element] = None, **kwargs) -> None:
4747
def __getitem__(self, index: int) -> "ElementNode":
4848
return self.children[index]
4949

50+
def __repr__(self):
51+
if self.parent:
52+
return f"{self.element.__class__.__name__}(name={self.element.name})"
53+
else:
54+
return "ROOT"
55+
5056

5157
class ElementTree(Tree):
5258
"""Class representing the hierarchy of elements in a model through a tree.
@@ -88,6 +94,10 @@ def __init__(self, name: Optional[str] = None) -> None:
8894
def elements(self) -> list[Element]:
8995
return [node.element for node in self.nodes if isinstance(node, ElementNode) if node.element]
9096

97+
@property
98+
def rootelements(self) -> list[Element]:
99+
return [node.element for node in self.root.children if isinstance(node, ElementNode) if node.element]
100+
91101
def find_element_node(self, element: Element) -> ElementNode:
92102
"""Find the node containing the element.
93103

src/compas_model/models/model.py

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from compas.geometry import Transformation
77
from compas_model.datastructures import KDTree
88
from compas_model.elements import Element
9+
from compas_model.elements import Group
910
from compas_model.interactions import Modifier
1011
from compas_model.materials import Material
1112

@@ -252,26 +253,26 @@ def has_material(self, material: Material) -> bool:
252253
def add_element(
253254
self,
254255
element: Element,
255-
parent: Optional[ElementNode] = None,
256+
parent: Optional[Element] = None,
256257
material: Optional[Material] = None,
257-
) -> ElementNode:
258+
) -> Element:
258259
"""Add an element to the model.
259260
260261
Parameters
261262
----------
262263
element : :class:`Element`
263264
The element to add.
264-
parent : :class:`ElementNode`, optional
265-
The parent group node of the element.
266-
If ``None``, the element will be added directly under the root node.
265+
parent : :class:`Element`, optional
266+
The parent element of the element.
267+
If ``None``, the element will be added directly under the root element.
267268
material : :class:`Material`, optional
268269
A material to assign to the element.
269270
Note that the material should have already been added to the model before it can be assigned.
270271
271272
Returns
272273
-------
273-
:class:`Elementnode`
274-
The tree node containing the element in the hierarchy.
274+
:class:`Element`
275+
The element added to the model.
275276
276277
Raises
277278
------
@@ -289,23 +290,20 @@ def add_element(
289290

290291
element.graphnode = self.graph.add_node(element=element)
291292

292-
if not parent:
293-
parent = self._tree.root
294-
295-
if isinstance(parent, Element):
296-
if parent.treenode is None:
293+
if parent is None:
294+
parent_node = self._tree.root
295+
elif isinstance(parent, Element):
296+
parent_node = parent.treenode
297+
if parent_node is None:
297298
raise ValueError("The parent element is not part of this model.")
298-
299-
parent = parent.treenode
300-
301-
if not isinstance(parent, ElementNode):
302-
raise ValueError("Parent should be an Element or ElementNode of the current model.")
299+
else:
300+
raise ValueError("Parent should be an Element or None")
303301

304302
if material and not self.has_material(material):
305303
raise ValueError("The material is not part of the model: {}".format(material))
306304

307305
element_node = ElementNode(element=element)
308-
parent.add(element_node)
306+
parent_node.add(element_node)
309307

310308
if material:
311309
self.assign_material(material=material, element=element)
@@ -317,28 +315,45 @@ def add_element(
317315
# and perhaps all resets should be collected in a reset decorator
318316
self._bvh = None
319317

320-
return element_node
318+
return element
319+
320+
def add_group(self, name: str = None) -> Group:
321+
"""Add a group to the model.
322+
323+
Parameters
324+
----------
325+
name : str
326+
The name of the group.
327+
328+
Returns
329+
-------
330+
:class:`Group`
331+
The group added to the model.
332+
333+
"""
334+
group = Group(name=name)
335+
return self.add_element(group)
321336

322-
def add_elements(self, elements: list[Element], parent: Optional[ElementNode] = None) -> list[ElementNode]:
337+
def add_elements(self, elements: list[Element], parent: Optional[Element] = None) -> list[Element]:
323338
"""Add multiple elements to the model.
324339
325340
Parameters
326341
----------
327342
elements : list[:class:`Element`]
328343
The model elements.
329-
parent : :class:`GroupNode`, optional
330-
The parent group node of the elements.
331-
If ``None``, the elements will be added directly under the root node.
344+
parent : :class:`Group`, optional
345+
The parent group of the elements.
346+
If ``None``, the elements will be added directly under the root element.
332347
333348
Returns
334349
-------
335-
list[:class:`ElementNode`]
350+
list[:class:`Element`]
336351
337352
"""
338-
nodes = []
353+
added_elements = []
339354
for element in elements:
340-
nodes.append(self.add_element(element, parent=parent))
341-
return nodes
355+
added_elements.append(self.add_element(element, parent=parent))
356+
return added_elements
342357

343358
def add_material(self, material: Material) -> None:
344359
"""Add a material to the model.

src/compas_model/scene/elementobject.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class ElementObject(SceneObject):
5454

5555
def __init__(
5656
self,
57-
element: Element,
5857
vertexcolor: Optional[Color] = Color.black(),
5958
edgecolor: Optional[Color] = Color.black(),
6059
facecolor: Optional[Color] = Color.white(),
@@ -65,9 +64,7 @@ def __init__(
6564
show_faces: Optional[bool] = True,
6665
**kwargs,
6766
) -> None:
68-
super().__init__(item=element, **kwargs)
69-
70-
self._element = element
67+
super().__init__(**kwargs)
7168

7269
self.vertexcolor = vertexcolor
7370
self.edgecolor = edgecolor
@@ -80,14 +77,14 @@ def __init__(
8077
self.show_edges = show_edges
8178
self.show_faces = show_faces
8279

80+
for child in self.element.children:
81+
child_kwargs = kwargs.copy()
82+
child_kwargs["item"] = child
83+
self.add(**child_kwargs)
84+
8385
@property
8486
def element(self) -> Element:
85-
return self._element
86-
87-
@element.setter
88-
def element(self, element: Element) -> None:
89-
self._element = element
90-
self._transformation = None
87+
return self.item
9188

9289
@property
9390
def transformation(self) -> Transformation:

0 commit comments

Comments
 (0)