Skip to content

Commit 3d5e493

Browse files
committed
use "data store" for Scene
1 parent eff2b2e commit 3d5e493

2 files changed

Lines changed: 66 additions & 115 deletions

File tree

src/compas/scene/scene.py

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import compas.data # noqa: F401
22
import compas.datastructures # noqa: F401
33
import compas.geometry # noqa: F401
4+
from compas.datastructures import Datastructure
45
from compas.datastructures import Tree
56
from compas.datastructures import TreeNode
67

@@ -13,7 +14,7 @@
1314
from .sceneobject import SceneObjectFactory
1415

1516

16-
class Scene(Tree):
17+
class Scene(Datastructure):
1718
"""A scene is a container for hierarchical scene objects which are to be visualised in a given context.
1819
1920
Parameters
@@ -44,49 +45,42 @@ class Scene(Tree):
4445
@property
4546
def __data__(self):
4647
# type: () -> dict
47-
items = {str(object.item.guid): object.item for object in self.objects if object.item is not None}
4848
return {
4949
"name": self.name,
50-
"root": self.root.__data__, # type: ignore
51-
"items": list(items.values()),
50+
"items": list(self.items.values()),
51+
"objects": list(self.objects.values()),
52+
"tree": self.tree,
5253
}
5354

54-
@classmethod
55-
def __from_data__(cls, data):
56-
# type: (dict) -> Scene
57-
scene = cls(data["name"])
58-
items = {str(item.guid): item for item in data["items"]}
59-
60-
def add(node, parent, items):
61-
for child_node in node.get("children", []):
62-
settings = child_node["settings"]
63-
if "item" in child_node:
64-
guid = child_node["item"]
65-
sceneobject = parent.add(items[guid], **settings)
66-
else:
67-
sceneobject = parent.add(Group(**settings))
68-
add(child_node, sceneobject, items)
69-
70-
add(data["root"], scene, items)
71-
72-
return scene
73-
7455
def __init__(self, name="Scene", context=None):
7556
# type: (str, str | None) -> None
7657
super(Scene, self).__init__(name=name)
77-
super(Scene, self).add(TreeNode(name="ROOT"))
58+
59+
# TODO: deal with context
7860
self.context = context or detect_current_context()
7961

80-
@property
81-
def objects(self):
82-
# type: () -> list[SceneObject]
83-
return [node for node in self.nodes if not node.is_root] # type: ignore
62+
self.tree = Tree()
63+
self.tree.add(TreeNode(name="ROOT"))
64+
self.items = {}
65+
self.objects = {}
66+
67+
def __repr__(self):
68+
# type: () -> str
69+
70+
def node_repr(node):
71+
if node.name == "ROOT":
72+
return self.name
73+
74+
sceneobject = self.objects[node.name]
75+
return str(sceneobject)
76+
77+
return self.tree.get_hierarchy_string(node_repr=node_repr)
8478

8579
@property
8680
def context_objects(self):
8781
# type: () -> list
8882
guids = []
89-
for obj in self.objects:
83+
for obj in self.objects.values():
9084
guids += obj.guids
9185
return guids
9286

@@ -109,20 +103,22 @@ def add(self, item, parent=None, **kwargs):
109103
The scene object associated with the item.
110104
"""
111105

112-
parent = parent or self.root
106+
# Create a corresponding new scene object
107+
sceneobject = SceneObjectFactory.create(item=item, context=self.context, scene=self, **kwargs)
113108

114-
if isinstance(item, SceneObject):
115-
sceneobject = item
109+
# Add the scene object and item to the data store
110+
self.objects[str(sceneobject.guid)] = sceneobject
111+
self.items[str(item.guid)] = item
112+
113+
# Add the scene object to the hierarchical tree
114+
if parent is None:
115+
parent_node = self.tree.root
116116
else:
117-
if "context" in kwargs:
118-
if kwargs["context"] != self.context:
119-
raise Exception("Object context should be the same as scene context: {} != {}".format(kwargs["context"], self.context))
120-
del kwargs["context"] # otherwist the SceneObject receives "context" twice, which results in an error
121-
122-
# Use the factory to create the scene object
123-
sceneobject = SceneObjectFactory.create(item=item, context=self.context, **kwargs)
124-
125-
super(Scene, self).add(sceneobject, parent=parent)
117+
print(parent.guid)
118+
parent_node = self.tree.get_node_by_name(parent.guid)
119+
120+
self.tree.add(TreeNode(name=str(sceneobject.guid)), parent=parent_node)
121+
126122
return sceneobject
127123

128124
def clear_context(self, guids=None):

src/compas/scene/sceneobject.py

Lines changed: 28 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,27 @@
2424

2525
class SceneObjectFactory:
2626
"""Factory class for creating appropriate SceneObject instances based on input item type.
27-
27+
2828
This factory encapsulates the logic for selecting the right SceneObject subclass
2929
for a given data item, making the creation process more explicit and easier to understand.
3030
"""
31-
31+
3232
@staticmethod
33-
def create(item=None, **kwargs):
33+
def create(item=None, scene=None, **kwargs):
3434
"""Create appropriate SceneObject instance based on item type.
35-
35+
3636
Parameters
3737
----------
3838
item : :class:`compas.data.Data`
3939
The data item to create a scene object for.
4040
**kwargs : dict
4141
Additional keyword arguments to pass to the SceneObject constructor.
42-
42+
4343
Returns
4444
-------
4545
:class:`compas.scene.SceneObject`
4646
A SceneObject instance of the appropriate subclass for the given item.
47-
47+
4848
Raises
4949
------
5050
ValueError
@@ -55,13 +55,16 @@ def create(item=None, **kwargs):
5555
if item is None:
5656
raise ValueError("Cannot create a scene object for None. Please ensure you pass an instance of a supported class.")
5757

58+
if scene is None:
59+
raise ValueError("Cannot create a scene object without a scene.")
60+
5861
sceneobject_cls = get_sceneobject_cls(item, **kwargs)
59-
62+
6063
# Create and return an instance of the appropriate scene object class
61-
return sceneobject_cls(item=item, **kwargs)
64+
return sceneobject_cls(item=item, scene=scene, **kwargs)
6265

6366

64-
class SceneObject(TreeNode):
67+
class SceneObject(Data):
6568
"""Base class for all scene objects.
6669
6770
Parameters
@@ -130,9 +133,9 @@ def __init__(
130133
color=None, # type: compas.colors.Color | None
131134
opacity=1.0, # type: float
132135
show=True, # type: bool
133-
frame=None, # type: compas.geometry.Frame | None
134136
transformation=None, # type: compas.geometry.Transformation | None
135137
context=None, # type: str | None
138+
scene=None, # type: compas.scene.Scene | None
136139
**kwargs # type: dict
137140
): # fmt: skip
138141
# type: (...) -> None
@@ -145,7 +148,8 @@ def __init__(
145148
# because it has no access to the tree and/or the scene before it is added
146149
# which means that adding child objects will be added in context "None"
147150
self.context = context
148-
self._item = item
151+
self._scene = scene
152+
self._item = item.guid
149153
self._guids = []
150154
self._node = None
151155
self._transformation = transformation
@@ -158,29 +162,32 @@ def __init__(
158162
def __data__(self):
159163
# type: () -> dict
160164
return {
161-
"item": str(self.item.guid),
162-
"settings": self.settings,
163-
"children": [child.__data__ for child in self.children],
165+
"item": str(self._item),
166+
"name": self.name,
167+
"color": self.color,
168+
"opacity": self.opacity,
169+
"show": self.show,
170+
"transformation": self.transformation,
164171
}
165172

166-
@classmethod
167-
def __from_data__(cls, data):
168-
# type: (dict) -> None
169-
raise TypeError("Serialisation outside Scene not allowed.")
170-
171173
def __repr__(self):
172174
# type: () -> str
173175
return "<{}: {}>".format(self.__class__.__name__, self.name)
174176

175177
@property
176178
def scene(self):
177179
# type: () -> compas.scene.Scene | None
178-
return self.tree
180+
return self._scene
179181

180182
@property
181183
def item(self):
182184
# type: () -> compas.data.Data
183-
return self._item
185+
return self.scene.items[self._item]
186+
187+
@property
188+
def node(self):
189+
# type: () -> compas.datastructures.TreeNode
190+
return self.scene.tree.get_object_node(self.guid)
184191

185192
@property
186193
def guids(self):
@@ -234,58 +241,6 @@ def contrastcolor(self, color):
234241
# type: (compas.colors.Color) -> None
235242
self._contrastcolor = Color.coerce(color)
236243

237-
@property
238-
def settings(self):
239-
# type: () -> dict
240-
settings = {
241-
"name": self.name,
242-
"color": self.color,
243-
"opacity": self.opacity,
244-
"show": self.show,
245-
}
246-
247-
if self.frame:
248-
settings["frame"] = self.frame
249-
if self.transformation:
250-
settings["transformation"] = self.transformation
251-
252-
return settings
253-
254-
def add(self, item, **kwargs):
255-
# type: (compas.data.Data, dict) -> SceneObject
256-
"""Add a child item to the scene object.
257-
258-
Parameters
259-
----------
260-
item : :class:`compas.data.Data`
261-
The item to add.
262-
**kwargs : dict
263-
Additional keyword arguments to create the scene object for the item.
264-
265-
Returns
266-
-------
267-
:class:`compas.scene.SceneObject`
268-
The scene object associated with the added item.
269-
270-
Raises
271-
------
272-
ValueError
273-
If the scene object does not have an associated scene node.
274-
"""
275-
if isinstance(item, SceneObject):
276-
sceneobject = item
277-
else:
278-
if "context" in kwargs:
279-
if kwargs["context"] != self.context:
280-
raise Exception("Child context should be the same as parent context: {} != {}".format(kwargs["context"], self.context))
281-
del kwargs["context"] # otherwist the SceneObject receives "context" twice, which results in an error
282-
283-
# Use the factory to create the scene object
284-
sceneobject = SceneObjectFactory.create(item=item, context=self.context, **kwargs)
285-
286-
super(SceneObject, self).add(sceneobject)
287-
return sceneobject
288-
289244
def draw(self):
290245
"""The main drawing method."""
291246
raise NotImplementedError

0 commit comments

Comments
 (0)