Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

* Changed `SceneObject.frame` to read-only result of `Frame.from_transformation(SceneObject.worldtransformation)`, representing the local coordinate system of the scene object in world coordinates.
* Changed `SceneObject.worldtransformation` to the multiplication of all transformations from the scene object to the root of the scene tree, there will no longer be an additional transformation in relation to the object's frame.
* Fixed call to `astar_shortest_path` in `Graph.shortest_path`.

### Removed
Expand Down
36 changes: 13 additions & 23 deletions src/compas/scene/sceneobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from compas.colors import Color
from compas.data import Data
from compas.datastructures import TreeNode
from compas.geometry import Frame
from compas.geometry import Transformation

from .context import clear
Expand All @@ -36,10 +37,8 @@ class SceneObject(TreeNode):
The opacity of the object.
show : bool, optional
Flag for showing or hiding the object. Default is ``True``.
frame : :class:`compas.geometry.Frame`, optional
The local frame of the scene object, in relation to its parent frame.
transformation : :class:`compas.geometry.Transformation`, optional
The local transformation of the scene object in relation to its frame.
The local transformation of the scene object in relation to its parent object.
context : str, optional
The context in which the scene object is created.
**kwargs : dict
Expand All @@ -55,12 +54,13 @@ class SceneObject(TreeNode):
The node in the scene tree which represents the scene object.
guids : list[object]
The GUIDs of the items drawn in the visualization context.
frame : :class:`compas.geometry.Frame`
The local frame of the scene object, in relation to its parent frame.
transformation : :class:`compas.geometry.Transformation`
The local transformation of the scene object in relation to its frame.
The local transformation of the scene object in relation to its parent object.
worldtransformation : :class:`compas.geometry.Transformation`
The transformation of the scene object in world coordinates.
The global transformation of the scene object in world coordinates, computed by multiplying all transformations from the scene object to the root of the scene tree.
(NOTE: Changed from 2.11.0, there will no longer be the option of additional transformation in relation to the object's frame)
frame : :class:`compas.geometry.Frame`
The frame of the local coordinate system of the scene object, derived from the `worldtransformation`.
color : :class:`compas.colors.Color`
The color of the object.
contrastcolor : :class:`compas.colors.Color`, readon-only
Expand Down Expand Up @@ -113,7 +113,6 @@ def __init__(
self._item = item
self._guids = []
self._node = None
self._frame = frame
self._transformation = transformation
self._contrastcolor = None
self.color = color or self.color
Expand Down Expand Up @@ -156,12 +155,7 @@ def guids(self):
@property
def frame(self):
# type: () -> compas.geometry.Frame | None
return self._frame

@frame.setter
def frame(self, frame):
# type: (compas.geometry.Frame) -> None
self._frame = frame
return Frame.from_transformation(self.worldtransformation)

@property
def transformation(self):
Expand All @@ -176,21 +170,17 @@ def transformation(self, transformation):
@property
def worldtransformation(self):
# type: () -> compas.geometry.Transformation
frame_stack = [self.frame] if self.frame else []
transformations = [self.transformation] if self.transformation else []
Comment thread
tomvanmele marked this conversation as resolved.
parent = self.parent
while parent and not parent.is_root:
if parent.frame:
frame_stack.append(parent.frame)
if parent.transformation:
transformations.append(parent.transformation)
parent = parent.parent
matrices = [Transformation.from_frame(f) for f in frame_stack]
if matrices:
worldtransformation = reduce(mul, matrices[::-1])
if transformations:
worldtransformation = reduce(mul, transformations[::-1])
else:
worldtransformation = Transformation()

if self.transformation:
worldtransformation *= self.transformation

return worldtransformation

@property
Expand Down
21 changes: 12 additions & 9 deletions tests/compas/scene/test_scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,22 +83,25 @@ def test_sceneobject_auto_context_discovery_no_context(mocker):
def test_sceneobject_transform():
scene = Scene()
sceneobj1 = scene.add(Box())
sceneobj1.frame = Frame([1.0, 0.0, 0.0], xaxis=[1.0, 0.0, 0.0], yaxis=[0.0, 1.0, 0.0])
sceneobj1.transformation = Translation.from_vector([10.0, 0.0, 0.0])
assert sceneobj1.worldtransformation == sceneobj1.frame.to_transformation() * sceneobj1.transformation
assert sceneobj1.worldtransformation == sceneobj1.transformation
assert sceneobj1.worldtransformation == Translation.from_vector([10.0, 0.0, 0.0])
assert sceneobj1.frame == Frame([10.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
assert sceneobj1.frame.to_transformation() == Translation.from_vector([10.0, 0.0, 0.0])

sceneobj2 = scene.add(Box(), parent=sceneobj1)
sceneobj2.frame = Frame([1.0, 1.0, 0.0], xaxis=[1.0, 0.0, 0.0], yaxis=[0.0, 1.0, 0.0])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by simply removing the frame, you're not reproducing the same result as before...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, but if you see in next few lines, the assert condition was updated according to the new computing scheme, or perhaps I misunderstood something here?

sceneobj2.transformation = Translation.from_vector([10.0, 10.0, 0.0])
assert sceneobj2.worldtransformation == sceneobj1.frame.to_transformation() * sceneobj2.frame.to_transformation() * sceneobj2.transformation
assert sceneobj2.worldtransformation == sceneobj1.transformation * sceneobj2.transformation
assert sceneobj2.worldtransformation == Translation.from_vector([20.0, 10.0, 0.0])
assert sceneobj2.frame == Frame([20.0, 10.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
assert sceneobj2.frame.to_transformation() == Translation.from_vector([20.0, 10.0, 0.0])

sceneobj3 = scene.add(Box(), parent=sceneobj2)
sceneobj3.frame = Frame([1.0, 1.0, 1.0], xaxis=[0.0, 1.0, 0.0], yaxis=[1.0, 0.0, 0.0])
sceneobj3.transformation = Translation.from_vector([10.0, 10.0, 10.0])
assert (
sceneobj3.worldtransformation
== sceneobj1.frame.to_transformation() * sceneobj2.frame.to_transformation() * sceneobj3.frame.to_transformation() * sceneobj3.transformation
)
assert sceneobj3.worldtransformation == sceneobj1.transformation * sceneobj2.transformation * sceneobj3.transformation
assert sceneobj3.worldtransformation == Translation.from_vector([30.0, 20.0, 10.0])
assert sceneobj3.frame == Frame([30.0, 20.0, 10.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
assert sceneobj3.frame.to_transformation() == Translation.from_vector([30.0, 20.0, 10.0])

def test_scene_clear():
scene = Scene()
Expand Down
Loading