Skip to content

Commit a3961bf

Browse files
authored
Merge pull request #946 from compas-dev/assembly-suggestions
Assembly suggestions
2 parents d32150c + 87aa974 commit a3961bf

File tree

4 files changed

+88
-9
lines changed

4 files changed

+88
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
* Added `compas_rhino.DEFAULT_VERSION`.
1313
* Added `clean` option to `compas_rhino.install` to remove existing symlinks if they cannot be imported from the current environment.
14+
* Added basic implementation of `compas.datastructures.Assembly`.
1415

1516
### Changed
1617

src/compas/datastructures/assembly/assembly.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,24 @@ def JSONSCHEMANAME(self):
3232

3333
def __init__(self, name=None, **kwargs):
3434
super(Assembly, self).__init__()
35-
self.attributes = {}
35+
self.attributes = {'name': name or 'Assembly'}
36+
self.attributes.update(kwargs)
3637
self.graph = Graph()
37-
self.parts = {}
38+
self._parts = {}
3839

3940
def __str__(self):
4041
tpl = "<Assembly with {} parts and {} connections>"
4142
return tpl.format(self.graph.number_of_nodes(), self.graph.number_of_edges())
4243

44+
@property
45+
def name(self):
46+
"""str : The name of the assembly."""
47+
return self.attributes.get('name') or self.__class__.__name__
48+
49+
@name.setter
50+
def name(self, value):
51+
self.attributes['name'] = value
52+
4353
@property
4454
def data(self):
4555
"""dict : A data dict representing the assembly data structure for serialization.
@@ -76,9 +86,11 @@ def add_part(self, part, key=None, **kwargs):
7686
The identifier of the part in the current assembly graph.
7787
7888
"""
89+
if part.guid in self._parts:
90+
raise AssemblyError('Part already added to the assembly')
7991
key = self.graph.add_node(key=key, part=part, **kwargs)
8092
part.key = key
81-
self.parts[part.guid] = part
93+
self._parts[part.guid] = part
8294
return key
8395

8496
def add_connection(self, a, b, **kwargs):
@@ -120,9 +132,21 @@ def parts(self):
120132
for node in self.graph.nodes():
121133
yield self.graph.node_attribute(node, 'part')
122134

123-
def connections(self):
124-
"""The connections between the parts."""
125-
return self.graph.edges()
135+
def connections(self, data=False):
136+
"""Iterate over the connections between the parts.
137+
138+
Parameters
139+
----------
140+
data : bool, optional
141+
If ``True``, yield both the identifier and the attributes of each connection.
142+
143+
Yields
144+
------
145+
tuple
146+
The next connection identifier (u, v), if ``data`` is ``False``.
147+
Otherwise, the next connector identifier and its attributes as a ((u, v), attr) tuple.
148+
"""
149+
return self.graph.edges(data)
126150

127151
def find(self, guid):
128152
"""Find a part in the assembly by its GUID.
@@ -139,4 +163,4 @@ def find(self, guid):
139163
The identified part, if any.
140164
141165
"""
142-
return self.parts.get(guid)
166+
return self._parts.get(guid)

src/compas/datastructures/assembly/part.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,11 @@ def DATASCHEMA(self):
7474
def JSONSCHEMANAME(self):
7575
return 'part'
7676

77-
def __init__(self, name, frame=None, shape=None, features=None, **kwargs):
77+
def __init__(self, name=None, frame=None, shape=None, features=None, **kwargs):
7878
super(Part, self).__init__()
7979
self._frame = None
8080
self.attributes = {'name': name or 'Part'}
81+
self.attributes.update(kwargs)
8182
self.key = None
8283
self.frame = frame
8384
self.shape = shape or Shape([], [])
@@ -88,6 +89,15 @@ def __str__(self):
8889
tpl = "<Part with shape {} and features {}>"
8990
return tpl.format(self.shape, self.features)
9091

92+
@property
93+
def name(self):
94+
"""str : The name of the part."""
95+
return self.attributes.get('name') or self.__class__.__name__
96+
97+
@name.setter
98+
def name(self, value):
99+
self.attributes['name'] = value
100+
91101
@property
92102
def data(self):
93103
"""dict : A data dict representing the part attributes, the assembly graph identifier, the local coordinate system,
@@ -124,7 +134,8 @@ def frame(self, frame):
124134

125135
@property
126136
def geometry(self):
127-
# this is a temp solution
137+
# TODO: this is a temp solution
138+
# TODO: add memoization or some other kind of caching
128139
A = Mesh.from_shape(self.shape)
129140
for shape, operation in self.features:
130141
A.quads_to_triangles()
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from compas.datastructures import Assembly, assembly
2+
from compas.datastructures import Part
3+
4+
5+
def test_init():
6+
assembly = Assembly(name='abc')
7+
assert assembly.name == 'abc'
8+
9+
assembly = Assembly(attr1='value', attr2=3.14)
10+
assert assembly.attributes['attr1'] == 'value'
11+
assert assembly.attributes['attr2'] == 3.14
12+
13+
14+
def test_add_parts():
15+
assembly = Assembly()
16+
17+
for _ in range(3):
18+
assembly.add_part(Part())
19+
20+
assert len(list(assembly.parts())) == 3
21+
22+
23+
def test_add_connections():
24+
assembly = Assembly()
25+
parts = [Part() for i in range(3)]
26+
27+
for part in parts:
28+
assembly.add_part(part)
29+
30+
assembly.add_connection(parts[0], parts[1])
31+
assembly.add_connection(parts[1], parts[2])
32+
assembly.add_connection(parts[2], parts[0])
33+
34+
assert list(assembly.connections()) == [(0, 1), (1, 2), (2, 0)]
35+
36+
37+
def test_find():
38+
assembly = Assembly()
39+
part = Part()
40+
assert assembly.find(part.guid) is None
41+
42+
assembly.add_part(part)
43+
assert assembly.find(part.guid) == part

0 commit comments

Comments
 (0)