Skip to content

Commit ea17a24

Browse files
authored
Merge pull request #1443 from compas-dev/scene
Update relation between frame and transformation
2 parents ea17c7c + 558385b commit ea17a24

File tree

3 files changed

+27
-32
lines changed

3 files changed

+27
-32
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
### Changed
1717

18+
* 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.
19+
* 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.
1820
* Fixed call to `astar_shortest_path` in `Graph.shortest_path`.
1921

2022
### Removed

src/compas/scene/sceneobject.py

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from compas.colors import Color
1414
from compas.data import Data
1515
from compas.datastructures import TreeNode
16+
from compas.geometry import Frame
1617
from compas.geometry import Transformation
1718

1819
from .context import clear
@@ -36,10 +37,8 @@ class SceneObject(TreeNode):
3637
The opacity of the object.
3738
show : bool, optional
3839
Flag for showing or hiding the object. Default is ``True``.
39-
frame : :class:`compas.geometry.Frame`, optional
40-
The local frame of the scene object, in relation to its parent frame.
4140
transformation : :class:`compas.geometry.Transformation`, optional
42-
The local transformation of the scene object in relation to its frame.
41+
The local transformation of the scene object in relation to its parent object.
4342
context : str, optional
4443
The context in which the scene object is created.
4544
**kwargs : dict
@@ -55,12 +54,13 @@ class SceneObject(TreeNode):
5554
The node in the scene tree which represents the scene object.
5655
guids : list[object]
5756
The GUIDs of the items drawn in the visualization context.
58-
frame : :class:`compas.geometry.Frame`
59-
The local frame of the scene object, in relation to its parent frame.
6057
transformation : :class:`compas.geometry.Transformation`
61-
The local transformation of the scene object in relation to its frame.
58+
The local transformation of the scene object in relation to its parent object.
6259
worldtransformation : :class:`compas.geometry.Transformation`
63-
The transformation of the scene object in world coordinates.
60+
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.
61+
(NOTE: Changed from 2.11.0, there will no longer be the option of additional transformation in relation to the object's frame)
62+
frame : :class:`compas.geometry.Frame`
63+
The frame of the local coordinate system of the scene object, derived from the `worldtransformation`.
6464
color : :class:`compas.colors.Color`
6565
The color of the object.
6666
contrastcolor : :class:`compas.colors.Color`, readon-only
@@ -113,7 +113,6 @@ def __init__(
113113
self._item = item
114114
self._guids = []
115115
self._node = None
116-
self._frame = frame
117116
self._transformation = transformation
118117
self._contrastcolor = None
119118
self.color = color or self.color
@@ -156,12 +155,7 @@ def guids(self):
156155
@property
157156
def frame(self):
158157
# type: () -> compas.geometry.Frame | None
159-
return self._frame
160-
161-
@frame.setter
162-
def frame(self, frame):
163-
# type: (compas.geometry.Frame) -> None
164-
self._frame = frame
158+
return Frame.from_transformation(self.worldtransformation)
165159

166160
@property
167161
def transformation(self):
@@ -176,21 +170,17 @@ def transformation(self, transformation):
176170
@property
177171
def worldtransformation(self):
178172
# type: () -> compas.geometry.Transformation
179-
frame_stack = [self.frame] if self.frame else []
173+
transformations = [self.transformation] if self.transformation else []
180174
parent = self.parent
181175
while parent and not parent.is_root:
182-
if parent.frame:
183-
frame_stack.append(parent.frame)
176+
if parent.transformation:
177+
transformations.append(parent.transformation)
184178
parent = parent.parent
185-
matrices = [Transformation.from_frame(f) for f in frame_stack]
186-
if matrices:
187-
worldtransformation = reduce(mul, matrices[::-1])
179+
if transformations:
180+
worldtransformation = reduce(mul, transformations[::-1])
188181
else:
189182
worldtransformation = Transformation()
190183

191-
if self.transformation:
192-
worldtransformation *= self.transformation
193-
194184
return worldtransformation
195185

196186
@property

tests/compas/scene/test_scene.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,25 @@ def test_sceneobject_auto_context_discovery_no_context(mocker):
8383
def test_sceneobject_transform():
8484
scene = Scene()
8585
sceneobj1 = scene.add(Box())
86-
sceneobj1.frame = Frame([1.0, 0.0, 0.0], xaxis=[1.0, 0.0, 0.0], yaxis=[0.0, 1.0, 0.0])
8786
sceneobj1.transformation = Translation.from_vector([10.0, 0.0, 0.0])
88-
assert sceneobj1.worldtransformation == sceneobj1.frame.to_transformation() * sceneobj1.transformation
87+
assert sceneobj1.worldtransformation == sceneobj1.transformation
88+
assert sceneobj1.worldtransformation == Translation.from_vector([10.0, 0.0, 0.0])
89+
assert sceneobj1.frame == Frame([10.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
90+
assert sceneobj1.frame.to_transformation() == Translation.from_vector([10.0, 0.0, 0.0])
8991

9092
sceneobj2 = scene.add(Box(), parent=sceneobj1)
91-
sceneobj2.frame = Frame([1.0, 1.0, 0.0], xaxis=[1.0, 0.0, 0.0], yaxis=[0.0, 1.0, 0.0])
9293
sceneobj2.transformation = Translation.from_vector([10.0, 10.0, 0.0])
93-
assert sceneobj2.worldtransformation == sceneobj1.frame.to_transformation() * sceneobj2.frame.to_transformation() * sceneobj2.transformation
94+
assert sceneobj2.worldtransformation == sceneobj1.transformation * sceneobj2.transformation
95+
assert sceneobj2.worldtransformation == Translation.from_vector([20.0, 10.0, 0.0])
96+
assert sceneobj2.frame == Frame([20.0, 10.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
97+
assert sceneobj2.frame.to_transformation() == Translation.from_vector([20.0, 10.0, 0.0])
9498

9599
sceneobj3 = scene.add(Box(), parent=sceneobj2)
96-
sceneobj3.frame = Frame([1.0, 1.0, 1.0], xaxis=[0.0, 1.0, 0.0], yaxis=[1.0, 0.0, 0.0])
97100
sceneobj3.transformation = Translation.from_vector([10.0, 10.0, 10.0])
98-
assert (
99-
sceneobj3.worldtransformation
100-
== sceneobj1.frame.to_transformation() * sceneobj2.frame.to_transformation() * sceneobj3.frame.to_transformation() * sceneobj3.transformation
101-
)
101+
assert sceneobj3.worldtransformation == sceneobj1.transformation * sceneobj2.transformation * sceneobj3.transformation
102+
assert sceneobj3.worldtransformation == Translation.from_vector([30.0, 20.0, 10.0])
103+
assert sceneobj3.frame == Frame([30.0, 20.0, 10.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0])
104+
assert sceneobj3.frame.to_transformation() == Translation.from_vector([30.0, 20.0, 10.0])
102105

103106
def test_scene_clear():
104107
scene = Scene()

0 commit comments

Comments
 (0)