From 4879ea9f21b7630940c1062d90dcca6fff91fb02 Mon Sep 17 00:00:00 2001 From: Licini Date: Fri, 4 Jul 2025 11:42:56 +0200 Subject: [PATCH] setters for worldtransformation and frame --- CHANGELOG.md | 2 ++ src/compas/scene/sceneobject.py | 14 +++++++++++- tests/compas/scene/test_scene.py | 37 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8344a074f2b0..c77876eda36c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added + * Implemented `to_points` method in `compas.datastructures.Mesh`, which before raised a `NotImplementedError`. * Implemented `compute_aabb` method in `compas.datastructures.Datastructure`, which before raised a `NotImplementedError`. Made use of the `compas.geometry.bbox.bounding_box` function. * Implemented `compute_obb` method in `compas.datastructures.Datastructure`, which before raised a `NotImplementedError`. Made use of the `compas.geometry.bbox_numpy.oriented_bounding_box_numpy` function. @@ -17,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added test function `test_to_points` in `test_graph.py`. * Added test function `test_to_points` in `test_volmesh.py`. * Added test functions `test_to_points`, `test_compute_aabb`, and `test_compute_obb` in `test_mesh.py`. +* Added setters for `SceneObject.worldtransformation` and `SceneObject.frame`, which automatically handles the parent transformations. ### Changed diff --git a/src/compas/scene/sceneobject.py b/src/compas/scene/sceneobject.py index 371e00c8feb3..f227b43e5ac5 100644 --- a/src/compas/scene/sceneobject.py +++ b/src/compas/scene/sceneobject.py @@ -95,7 +95,6 @@ def __init__( color=None, # type: compas.colors.Color | None opacity=1.0, # type: float show=True, # type: bool - frame=None, # type: compas.geometry.Frame | None transformation=None, # type: compas.geometry.Transformation | None context=None, # type: str | None **kwargs # type: dict @@ -157,6 +156,11 @@ def frame(self): # type: () -> compas.geometry.Frame | None return Frame.from_transformation(self.worldtransformation) + @frame.setter + def frame(self, frame): + # type: (compas.geometry.Frame) -> None + self.worldtransformation = Transformation.from_frame(frame) + @property def transformation(self): # type: () -> compas.geometry.Transformation | None @@ -183,6 +187,14 @@ def worldtransformation(self): return worldtransformation + @worldtransformation.setter + def worldtransformation(self, worldtransformation): + # type: (compas.geometry.Transformation) -> None + if isinstance(self.parent, SceneObject): + self.transformation = self.parent.worldtransformation.inverse() * worldtransformation + else: + self.transformation = worldtransformation + @property def contrastcolor(self): # type: () -> compas.colors.Color | None diff --git a/tests/compas/scene/test_scene.py b/tests/compas/scene/test_scene.py index 1d01d217c8b4..fe09c341e078 100644 --- a/tests/compas/scene/test_scene.py +++ b/tests/compas/scene/test_scene.py @@ -11,6 +11,7 @@ from compas.geometry import Box from compas.geometry import Frame from compas.geometry import Translation + from compas.geometry import Transformation from compas.scene import Group @pytest.fixture(autouse=True) @@ -104,6 +105,42 @@ def test_sceneobject_transform(): 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_sceneobject_frame_and_worldtransformation_setters(): + """Test that frame and worldtransformation setters work correctly.""" + scene = Scene() + + # Test on root object + root_obj = scene.add(Box()) + test_frame = Frame([10.0, 20.0, 30.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) + root_obj.frame = test_frame + assert root_obj.frame == test_frame + assert root_obj.worldtransformation == Transformation.from_frame(test_frame) + + # Test worldtransformation setter on root object + test_transform = Translation.from_vector([5.0, 10.0, 15.0]) + root_obj.worldtransformation = test_transform + assert root_obj.worldtransformation == test_transform + assert root_obj.transformation == test_transform + + # Test with parent-child relationship + parent_obj = scene.add(Box()) + child_obj = scene.add(Box(), parent=parent_obj) + parent_obj.transformation = Translation.from_vector([10.0, 0.0, 0.0]) + + # Test frame setter with parent + child_frame = Frame([30.0, 20.0, 10.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) + child_obj.frame = child_frame + assert child_obj.frame == child_frame + expected_local = parent_obj.worldtransformation.inverse() * Transformation.from_frame(child_frame) + assert child_obj.transformation == expected_local + + # Test worldtransformation setter with parent + child_world_transform = Translation.from_vector([50.0, 30.0, 20.0]) + child_obj.worldtransformation = child_world_transform + assert child_obj.worldtransformation == child_world_transform + expected_local = parent_obj.worldtransformation.inverse() * child_world_transform + assert child_obj.transformation == expected_local + def test_scene_clear(): scene = Scene() sceneobj1 = scene.add(Box())