-
Notifications
You must be signed in to change notification settings - Fork 113
Scene Data Store #1461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Scene Data Store #1461
Changes from 8 commits
58571a0
eff2b2e
3d5e493
f07bdbf
10e4225
e8b5fab
82fa199
b730a4d
0c866d1
7ef3e4d
55dcfae
3e7a43d
dd4344c
981464d
85c3300
44163d3
ab5425a
38a7d63
ef4def8
01cf1af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,19 @@ | ||
import compas.data # noqa: F401 | ||
import compas.datastructures # noqa: F401 | ||
import compas.geometry # noqa: F401 | ||
from compas.datastructures import Datastructure | ||
from compas.datastructures import Tree | ||
from compas.datastructures import TreeNode | ||
|
||
from .context import after_draw | ||
from .context import before_draw | ||
from .context import clear | ||
from .context import detect_current_context | ||
from .group import Group | ||
from .sceneobject import SceneObject | ||
from .sceneobject import SceneObjectFactory | ||
|
||
|
||
class Scene(Tree): | ||
class Scene(Datastructure): | ||
"""A scene is a container for hierarchical scene objects which are to be visualised in a given context. | ||
|
||
Parameters | ||
|
@@ -43,47 +44,51 @@ class Scene(Tree): | |
@property | ||
def __data__(self): | ||
# type: () -> dict | ||
items = {str(object.item.guid): object.item for object in self.objects if object.item is not None} | ||
return { | ||
"name": self.name, | ||
"root": self.root.__data__, # type: ignore | ||
"items": list(items.values()), | ||
"attributes": self.attributes, | ||
"datastore": self.datastore, | ||
"objectstore": self.objectstore, | ||
"tree": self.tree, | ||
} | ||
|
||
@classmethod | ||
def __from_data__(cls, data): | ||
# type: (dict) -> Scene | ||
scene = cls(data["name"]) | ||
items = {str(item.guid): item for item in data["items"]} | ||
|
||
def add(node, parent, items): | ||
for child_node in node.get("children", []): | ||
settings = child_node["settings"] | ||
if "item" in child_node: | ||
guid = child_node["item"] | ||
sceneobject = parent.add(items[guid], **settings) | ||
else: | ||
sceneobject = parent.add(Group(**settings)) | ||
add(child_node, sceneobject, items) | ||
|
||
add(data["root"], scene, items) | ||
|
||
return scene | ||
Comment on lines
44
to
-71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We directly serialize |
||
|
||
def __init__(self, name="Scene", context=None): | ||
# type: (str, str | None) -> None | ||
super(Scene, self).__init__(name=name) | ||
super(Scene, self).add(TreeNode(name="ROOT")) | ||
def __init__(self, context=None, datastore=None, objectstore=None, tree=None, **kwargs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Users can passing their own customs stores and tree, if they know what they are doing. |
||
# type: (str | None, dict | None, dict | None, Tree | None, **kwargs) -> None | ||
super(Scene, self).__init__(**kwargs) | ||
|
||
self.context = context or detect_current_context() | ||
self.datastore = datastore or {} | ||
self.objectstore = objectstore or {} | ||
self.tree = tree or Tree() | ||
if self.tree.root is None: | ||
self.tree.add(TreeNode(name=self.name)) | ||
|
||
def __repr__(self): | ||
# type: () -> str | ||
|
||
def node_repr(node): | ||
# type: (TreeNode) -> str | ||
if node.is_root: | ||
return node.name | ||
else: | ||
sceneobject = self.objectstore[node.name] | ||
return str(sceneobject) | ||
|
||
return self.tree.get_hierarchy_string(node_repr=node_repr) | ||
|
||
@property | ||
def items(self): | ||
# type: () -> list[compas.data.Data] | ||
return list(self.datastore.values()) | ||
|
||
@property | ||
def objects(self): | ||
# type: () -> list[SceneObject] | ||
return [node for node in self.nodes if not node.is_root] # type: ignore | ||
return list(self.objectstore.values()) | ||
|
||
@property | ||
def context_objects(self): | ||
# type: () -> list | ||
# type: () -> list[SceneObject] | ||
Licini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
guids = [] | ||
for obj in self.objects: | ||
guids += obj.guids | ||
|
@@ -108,19 +113,49 @@ def add(self, item, parent=None, **kwargs): | |
The scene object associated with the item. | ||
""" | ||
|
||
parent = parent or self.root | ||
if "context" in kwargs: | ||
if kwargs["context"] != self.context: | ||
raise Exception("Object context should be the same as scene context: {} != {}".format(kwargs["context"], self.context)) | ||
del kwargs["context"] # otherwist the SceneObject receives "context" twice, which results in an error | ||
|
||
# Create a corresponding new scene object | ||
sceneobject = SceneObjectFactory.create(item=item, context=self.context, scene=self, **kwargs) | ||
Licini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if isinstance(item, SceneObject): | ||
sceneobject = item | ||
# Add the scene object and item to the data store | ||
self.objectstore[str(sceneobject.guid)] = sceneobject | ||
self.datastore[str(item.guid)] = item | ||
|
||
# Add the scene object to the hierarchical tree | ||
if parent is None: | ||
parent_node = self.tree.root | ||
else: | ||
if "context" in kwargs: | ||
if kwargs["context"] != self.context: | ||
raise Exception("Object context should be the same as scene context: {} != {}".format(kwargs["context"], self.context)) | ||
del kwargs["context"] # otherwist the SceneObject receives "context" twice, which results in an error | ||
sceneobject = SceneObject(item=item, context=self.context, **kwargs) # type: ignore | ||
super(Scene, self).add(sceneobject, parent=parent) | ||
if not isinstance(parent, SceneObject): | ||
raise ValueError("Parent is not a SceneObject.", parent) | ||
parent_node = self.tree.get_node_by_name(parent.guid) | ||
Licini marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if parent_node is None: | ||
raise ValueError("Parent is not part of the scene.", parent) | ||
|
||
self.tree.add(TreeNode(name=str(sceneobject.guid)), parent=parent_node) | ||
|
||
return sceneobject | ||
|
||
def remove(self, sceneobject): | ||
"""Remove a scene object along with all its descendants from the scene. | ||
|
||
Parameters | ||
---------- | ||
sceneobject : :class:`compas.scene.SceneObject` | ||
The scene object to remove. | ||
""" | ||
# type: (SceneObject) -> None | ||
guid = str(sceneobject.guid) | ||
self.objectstore.pop(guid, None) | ||
node = self.tree.get_node_by_name(guid) | ||
if node: | ||
for descendant in node.descendants: | ||
self.objectstore.pop(descendant.name, None) | ||
self.tree.remove(node) | ||
|
||
def clear_context(self, guids=None): | ||
# type: (list | None) -> None | ||
"""Clear the visualisation context. | ||
|
@@ -233,7 +268,7 @@ def find_by_name(self, name): | |
return self.get_node_by_name(name=name) | ||
|
||
def find_by_itemtype(self, itemtype): | ||
# type: (...) -> SceneObject | None | ||
# type: (type) -> SceneObject | None | ||
"""Find the first scene object with a data item of the given type. | ||
|
||
Parameters | ||
|
@@ -251,7 +286,7 @@ def find_by_itemtype(self, itemtype): | |
return obj | ||
|
||
def find_all_by_itemtype(self, itemtype): | ||
# type: (...) -> list[SceneObject] | ||
# type: (type) -> list[SceneObject] | ||
"""Find all scene objects with a data item of the given type. | ||
|
||
Parameters | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These customizations are no longer needed anymore, thanks to the more straight forward serialization/deserialization