Skip to content

Commit cc7aafb

Browse files
committed
use guid refs
1 parent 1368660 commit cc7aafb

File tree

3 files changed

+141
-173
lines changed

3 files changed

+141
-173
lines changed

src/compas_model/models/elementtree.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
from typing import TYPE_CHECKING
12
from typing import Optional
3+
from typing import Union
24

35
from compas.datastructures import Tree
46
from compas.datastructures import TreeNode
57
from compas_model.elements import Element
68

9+
if TYPE_CHECKING:
10+
from compas_model.models import Model
11+
712

813
class ElementNode(TreeNode):
914
"""Class representing nodes containing elements in an element tree.
@@ -25,12 +30,12 @@ class ElementNode(TreeNode):
2530
2631
"""
2732

28-
tree: "ElementTree"
33+
tree: "ElementTree" # type: ignore
2934

3035
@property
3136
def __data__(self) -> dict:
3237
data = super().__data__
33-
data["element"] = None if not self.element else str(self.element.guid)
38+
data["element"] = self._element
3439
return data
3540

3641
@classmethod
@@ -39,16 +44,28 @@ def __from_data__(cls, data: dict) -> "ElementNode":
3944

4045
def __init__(self, element: Optional[Element] = None, **kwargs) -> None:
4146
super().__init__(**kwargs)
42-
43-
if element:
44-
element.treenode = self
47+
self._element = None
4548
self.element = element
4649

50+
@property
51+
def element(self):
52+
if self._element:
53+
return self.tree.model._elements[self._element]
54+
55+
@element.setter
56+
def element(self, element: Optional[Union[Element, str]] = None) -> None:
57+
if isinstance(element, Element):
58+
self._element = str(element.guid)
59+
else:
60+
self._element = element
61+
4762
def __getitem__(self, index: int) -> "ElementNode":
4863
return self.children[index]
4964

5065
def __repr__(self):
5166
if self.parent:
67+
if not self.element:
68+
raise Exception("An element node that is not the root node should have an element.")
5269
return f"{self.element.__class__.__name__}(name={self.element.name})"
5370
else:
5471
return "ROOT"
@@ -75,28 +92,42 @@ class ElementTree(Tree):
7592
7693
"""
7794

78-
@property
79-
def __data__(self) -> dict:
80-
data = super().__data__
81-
return data
95+
model: "Model"
8296

8397
@classmethod
8498
def __from_data__(cls, data: dict) -> "ElementTree":
8599
raise Exception("Serialisation outside model context not allowed.")
86100

87-
def __init__(self, name: Optional[str] = None) -> None:
101+
def __init__(self, model: "Model", name: Optional[str] = None) -> None:
88102
super().__init__(name=name)
89-
103+
self.model = model
90104
root = ElementNode(name="root")
91105
self.add(root)
92106

93107
@property
94108
def elements(self) -> list[Element]:
95-
return [node.element for node in self.nodes if isinstance(node, ElementNode) if node.element]
109+
return [node.element for node in self.nodes if isinstance(node, ElementNode) and node.element]
96110

97111
@property
98112
def rootelements(self) -> list[Element]:
99-
return [node.element for node in self.root.children if isinstance(node, ElementNode) if node.element]
113+
if self.root:
114+
return [node.element for node in self.root.children if isinstance(node, ElementNode) and node.element]
115+
return []
116+
117+
def add_element(self, element: Element, parent: Optional[Union[Element, ElementNode]] = None) -> ElementNode:
118+
if parent is None:
119+
parentnode = self.root
120+
elif isinstance(parent, Element):
121+
parentnode = parent.treenode
122+
if parentnode is None:
123+
raise ValueError("The parent element is not part of this model.")
124+
else:
125+
parentnode = parent
126+
127+
treenode = ElementNode(element=element)
128+
element.treenode = treenode
129+
parentnode.add(treenode) # type: ignore
130+
return treenode
100131

101132
def find_element_node(self, element: Element) -> ElementNode:
102133
"""Find the node containing the element.
Lines changed: 24 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
# from typing import Generator
2-
from typing import Optional
1+
from typing import TYPE_CHECKING
32

43
from compas.datastructures import Graph
54
from compas_model.elements import Element # noqa: F401
65

7-
# Ideally, graph (and mesh) are rewritten to use dedicated classes for nodes and edges.
8-
# This will allow more fine-grained control over the (types of) attributes added to nodes and edges.
9-
# It will also provide a much more intuitive API.
6+
if TYPE_CHECKING:
7+
from compas_model.models import Model
108

119

1210
class InteractionGraph(Graph):
@@ -26,57 +24,16 @@ class InteractionGraph(Graph):
2624
2725
"""
2826

29-
@property
30-
def __data__(self) -> dict:
31-
data = super().__data__
32-
33-
for node, attr in data["node"].items():
34-
# this modifies the attributes in place
35-
# as a consequence, after accessing the __data__ property of the graph,
36-
# the graph is broken
37-
# to prevent this, the attribute dict should be copied
38-
attr = attr.copy()
39-
attr["element"] = str(attr["element"].guid)
40-
data["node"][node] = attr
41-
return data
42-
43-
@classmethod
44-
def __from_data__(cls, data: dict, guid_element: dict[str, Element]) -> "InteractionGraph":
45-
graph = super(InteractionGraph, cls).__from_data__(data)
46-
for node, attr in graph.nodes(data=True):
47-
element = guid_element[attr["element"]]
48-
attr["element"] = element # type: ignore
49-
element.graphnode = node
50-
return graph
51-
52-
def copy(self) -> "InteractionGraph":
53-
# A custom implementation of copy is needed to allow passing the element dictionary to __from_data__.
54-
guid_element = {}
55-
for _, node in self.nodes(data=True):
56-
element = node["element"]
57-
guid_element[str(element.guid)] = element
58-
return self.__from_data__(self.__data__, guid_element)
59-
60-
def __init__(
61-
self,
62-
default_node_attributes: Optional[dict] = None,
63-
default_edge_attributes: Optional[dict] = None,
64-
name: Optional[str] = None,
65-
**kwargs,
66-
) -> None:
67-
super().__init__(
68-
default_node_attributes=default_node_attributes,
69-
default_edge_attributes=default_edge_attributes,
70-
name=name,
71-
**kwargs,
72-
)
27+
model: "Model"
7328

29+
def __init__(self, **kwargs) -> None:
30+
super().__init__(**kwargs)
7431
self.update_default_node_attributes(element=None)
75-
self.update_default_edge_attributes(interactions=None)
32+
self.update_default_edge_attributes(modifiers=None, contacts=None)
33+
self.model = None # type: ignore
7634

7735
def __str__(self) -> str:
7836
output = super().__str__()
79-
8037
output += "\n"
8138
output += self._build_interactions_str()
8239
return output
@@ -91,23 +48,18 @@ def _build_interactions_str(self) -> str:
9148
edge = nbr, node
9249

9350
lines.append(
94-
"- {}: {} {} {}".format(
51+
"- {}: {} {}".format(
9552
nbr,
9653
self.edge_attribute(edge, "modifiers"),
9754
self.edge_attribute(edge, "contacts"),
98-
self.edge_attribute(edge, "collisons"),
9955
) # type: ignore
10056
)
10157
return "\n".join(lines) + "\n"
10258

103-
def clear_edges(self):
104-
"""Clear all the edges and connectivity information of the graph."""
105-
for u, v in list(self.edges()):
106-
del self.edge[u][v]
107-
if v in self.adjacency[u]:
108-
del self.adjacency[u][v]
109-
if u in self.adjacency[v]:
110-
del self.adjacency[v][u]
59+
def add_element(self, element: Element) -> int:
60+
node = self.add_node(element=str(element.guid))
61+
element.graphnode = node
62+
return node
11163

11264
def node_element(self, node: int) -> Element:
11365
"""Get the element associated with the node.
@@ -122,4 +74,14 @@ def node_element(self, node: int) -> Element:
12274
:class:`compas_model.elements.Element`
12375
12476
"""
125-
return self.node_attribute(node, "element") # type: ignore
77+
guid: str = self.node_attribute(node, "element") # type: ignore
78+
return self.model._elements[guid]
79+
80+
def clear_edges(self):
81+
"""Clear all the edges and connectivity information of the graph."""
82+
for u, v in list(self.edges()):
83+
del self.edge[u][v]
84+
if v in self.adjacency[u]:
85+
del self.adjacency[u][v]
86+
if u in self.adjacency[v]:
87+
del self.adjacency[v][u]

0 commit comments

Comments
 (0)