From 007fe23731571651c7e4a2f61c4af31de5ce5f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 Apr 2023 22:14:37 +0200 Subject: [PATCH] python: support bit field access in SceneData. --- src/python/magnum/test/scene.gltf | 8 +++- src/python/magnum/test/test_trade.py | 69 ++++++++++++++++++++++++++-- src/python/magnum/trade.cpp | 33 ++++++++----- 3 files changed, 92 insertions(+), 18 deletions(-) diff --git a/src/python/magnum/test/scene.gltf b/src/python/magnum/test/scene.gltf index 28a83eb..08fee82 100644 --- a/src/python/magnum/test/scene.gltf +++ b/src/python/magnum/test/scene.gltf @@ -40,14 +40,18 @@ "camera": 1, "extras": { "aNumber": 5, - "aString": "hello!" + "aString": "hello!", + "yes": true }, "children": [3] }, { "camera": 0, "translation": [4, 5, 6], - "children": [0] + "children": [0], + "extras": { + "yes": false + } }, { "name": "A broken node", diff --git a/src/python/magnum/test/test_trade.py b/src/python/magnum/test/test_trade.py index 3aa4794..29b2b87 100644 --- a/src/python/magnum/test/test_trade.py +++ b/src/python/magnum/test/test_trade.py @@ -29,7 +29,7 @@ import sys import tempfile import unittest -from corrade import pluginmanager +from corrade import containers, pluginmanager from magnum import * from magnum import primitives, trade import magnum @@ -723,7 +723,7 @@ class SceneData(unittest.TestCase): scene = importer.scene(0) self.assertEqual(scene.mapping_type, trade.SceneMappingType.UNSIGNED_INT) self.assertEqual(scene.mapping_bound, 4) - self.assertEqual(scene.field_count, 7) + self.assertEqual(scene.field_count, 8) # TODO add some array extras once supported to have this different from # the mapping bound self.assertEqual(scene.field_size_bound, 4) @@ -821,8 +821,12 @@ class SceneData(unittest.TestCase): scene = importer.scene(0) scene_refcount = sys.getrefcount(scene) translation_id = scene.field_id(trade.SceneField.TRANSLATION) + scene_field_yes = importer.scene_field_for_name('yes') + self.assertIsNotNone(scene_field_yes) + yes_id = scene.field_id(scene_field_yes) translations = scene.field(translation_id) + self.assertIsInstance(translations, containers.StridedArrayView1D) self.assertEqual(translations.size, (3, )) self.assertEqual(translations.stride, (12, )) self.assertEqual(translations.format, '3f') @@ -838,6 +842,7 @@ class SceneData(unittest.TestCase): self.assertEqual(sys.getrefcount(scene), scene_refcount) cameras = scene.field(trade.SceneField.CAMERA) + self.assertIsInstance(cameras, containers.StridedArrayView1D) self.assertEqual(cameras.size, (2, )) self.assertEqual(cameras.stride, (4, )) self.assertEqual(cameras.format, 'I') @@ -848,7 +853,26 @@ class SceneData(unittest.TestCase): del cameras self.assertEqual(sys.getrefcount(scene), scene_refcount) + yeses1 = scene.field(scene_field_yes) + yeses2 = scene.field(yes_id) + self.assertIsInstance(yeses1, containers.StridedBitArrayView1D) + self.assertIsInstance(yeses2, containers.StridedBitArrayView1D) + self.assertEqual(yeses1.size, (2, )) + self.assertEqual(yeses2.size, (2, )) + self.assertEqual(yeses1.stride, (1, )) + self.assertEqual(yeses2.stride, (1, )) + self.assertEqual(list(yeses1), [True, False]) + self.assertEqual(list(yeses2), [True, False]) + self.assertIs(yeses1.owner, scene) + self.assertIs(yeses2.owner, scene) + self.assertEqual(sys.getrefcount(scene), scene_refcount + 2) + + del yeses1 + del yeses2 + self.assertEqual(sys.getrefcount(scene), scene_refcount) + mutable_translations = scene.mutable_field(translation_id) + self.assertIsInstance(mutable_translations, containers.MutableStridedArrayView1D) self.assertEqual(mutable_translations.size, (3, )) self.assertEqual(mutable_translations.stride, (12, )) self.assertEqual(mutable_translations.format, '3f') @@ -864,6 +888,7 @@ class SceneData(unittest.TestCase): self.assertEqual(sys.getrefcount(scene), scene_refcount) mutable_cameras = scene.mutable_field(trade.SceneField.CAMERA) + self.assertIsInstance(mutable_cameras, containers.MutableStridedArrayView1D) self.assertEqual(mutable_cameras.size, (2, )) self.assertEqual(mutable_cameras.stride, (4, )) self.assertEqual(mutable_cameras.format, 'I') @@ -874,6 +899,24 @@ class SceneData(unittest.TestCase): del mutable_cameras self.assertEqual(sys.getrefcount(scene), scene_refcount) + mutable_yeses1 = scene.mutable_field(scene_field_yes) + mutable_yeses2 = scene.mutable_field(yes_id) + self.assertIsInstance(mutable_yeses1, containers.MutableStridedBitArrayView1D) + self.assertIsInstance(mutable_yeses2, containers.MutableStridedBitArrayView1D) + self.assertEqual(mutable_yeses1.size, (2, )) + self.assertEqual(mutable_yeses2.size, (2, )) + self.assertEqual(mutable_yeses1.stride, (1, )) + self.assertEqual(mutable_yeses2.stride, (1, )) + self.assertEqual(list(mutable_yeses1), [True, False]) + self.assertEqual(list(mutable_yeses2), [True, False]) + self.assertIs(mutable_yeses1.owner, scene) + self.assertIs(mutable_yeses2.owner, scene) + self.assertEqual(sys.getrefcount(scene), scene_refcount + 2) + + del mutable_yeses1 + del mutable_yeses2 + self.assertEqual(sys.getrefcount(scene), scene_refcount) + def test_mutable_mapping_access(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) @@ -922,6 +965,24 @@ class SceneData(unittest.TestCase): mutable_cameras[1] = 13378 self.assertEqual(cameras[1], 13378) + scene_field_yes = importer.scene_field_for_name('yes') + self.assertIsNotNone(scene_field_yes) + yes_id = scene.field_id(scene_field_yes) + + yeses1 = scene.field(scene_field_yes) + yeses2 = scene.field(yes_id) + mutable_yeses1 = scene.mutable_field(scene_field_yes) + mutable_yeses2 = scene.mutable_field(yes_id) + self.assertEqual(yeses1[0], True) + self.assertEqual(yeses2[1], False) + self.assertEqual(mutable_yeses1[0], True) + self.assertEqual(mutable_yeses2[1], False) + + mutable_yeses1[0] = False + mutable_yeses2[1] = True + self.assertEqual(yeses1[0], False) + self.assertEqual(yeses2[1], True) + def test_pointer_field_access(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) @@ -1375,7 +1436,7 @@ class Importer(unittest.TestCase): self.assertEqual(importer.scene_field_for_name('aString'), trade.SceneField.CUSTOM(1)) scene = importer.scene(0) - self.assertEqual(scene.field_count, 7) + self.assertEqual(scene.field_count, 8) self.assertTrue(scene.has_field(importer.scene_field_for_name('aString'))) def test_scene_by_name(self): @@ -1383,7 +1444,7 @@ class Importer(unittest.TestCase): importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) scene = importer.scene("A scene") - self.assertEqual(scene.field_count, 7) + self.assertEqual(scene.field_count, 8) def test_scene_by_name_not_found(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') diff --git a/src/python/magnum/trade.cpp b/src/python/magnum/trade.cpp index e64c995..77a3e51 100644 --- a/src/python/magnum/trade.cpp +++ b/src/python/magnum/trade.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include /** @todo drop once we have our string casters */ #include #include @@ -1407,26 +1408,30 @@ void trade(py::module_& m) { PyErr_SetNone(PyExc_KeyError); throw py::error_already_set{}; } - /** @todo handle arrays (return a 2D view, and especially annotate - the return type properly in the docs) */ + /** @todo handle arrays (return a 2D (bit) view) */ if(self.fieldArraySize(*found) != 0) { PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry"); throw py::error_already_set{}; } - return sceneFieldView(self, *found, self.field(*found)); + /** @todo annotate the return type properly in the docs */ + if(self.fieldType(*found) == Trade::SceneFieldType::Bit) + return pyCastButNotShitty(Containers::pyArrayViewHolder(self.fieldBits(*found), py::cast(self))); + return pyCastButNotShitty(sceneFieldView(self, *found, self.field(*found))); }, "Data for given named field", py::arg("name")) .def("field", [](Trade::SceneData& self, UnsignedInt id) { if(id >= self.fieldCount()) { PyErr_SetNone(PyExc_IndexError); throw py::error_already_set{}; } - /** @todo handle arrays (return a 2D view, and especially annotate - the return type properly in the docs) */ + /** @todo handle arrays (return a 2D (bit) view) */ if(self.fieldArraySize(id) != 0) { PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry"); throw py::error_already_set{}; } - return sceneFieldView(self, id, self.field(id)); + /** @todo annotate the return type properly in the docs */ + if(self.fieldType(id) == Trade::SceneFieldType::Bit) + return pyCastButNotShitty(Containers::pyArrayViewHolder(self.fieldBits(id), py::cast(self))); + return pyCastButNotShitty(sceneFieldView(self, id, self.field(id))); }, "Data for given field", py::arg("name")) .def("mutable_field", [](Trade::SceneData& self, Trade::SceneField name) { const Containers::Optional found = self.findFieldId(name); @@ -1438,13 +1443,15 @@ void trade(py::module_& m) { PyErr_SetString(PyExc_AttributeError, "scene data is not mutable"); throw py::error_already_set{}; } - /** @todo handle arrays (return a 2D view, and especially annotate - the return type properly in the docs) */ + /** @todo handle arrays (return a 2D (bit) view) */ if(self.fieldArraySize(*found) != 0) { PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry"); throw py::error_already_set{}; } - return sceneFieldView(self, *found, self.mutableField(*found)); + /** @todo annotate the return type properly in the docs */ + if(self.fieldType(*found) == Trade::SceneFieldType::Bit) + return pyCastButNotShitty(Containers::pyArrayViewHolder(self.mutableFieldBits(*found), py::cast(self))); + return pyCastButNotShitty(sceneFieldView(self, *found, self.mutableField(*found))); }, "Mutable data for given named field", py::arg("name")) .def("mutable_field", [](Trade::SceneData& self, UnsignedInt id) { if(id >= self.fieldCount()) { @@ -1455,13 +1462,15 @@ void trade(py::module_& m) { PyErr_SetString(PyExc_AttributeError, "scene data is not mutable"); throw py::error_already_set{}; } - /** @todo handle arrays (return a 2D view, and especially annotate - the return type properly in the docs) */ + /** @todo handle arrays (return a 2D (bit) view) */ if(self.fieldArraySize(id) != 0) { PyErr_SetString(PyExc_NotImplementedError, "array fields not implemented yet, sorry"); throw py::error_already_set{}; } - return sceneFieldView(self, id, self.mutableField(id)); + /** @todo annotate the return type properly in the docs */ + if(self.fieldType(id) == Trade::SceneFieldType::Bit) + return pyCastButNotShitty(Containers::pyArrayViewHolder(self.mutableFieldBits(id), py::cast(self))); + return pyCastButNotShitty(sceneFieldView(self, id, self.mutableField(id))); }, "Mutable data for given field", py::arg("name")) .def_property_readonly("owner", [](Trade::SceneData& self) {