From f12aabf78891eed63c2cbd0bc5d5259d6fd0781d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 8 Mar 2023 21:31:15 +0100 Subject: [PATCH] python: expose trade.Abstract{Importer,Converter}.features and .flags. Right now the importer/converter APIs are not checked against the features so using them wrong will assert and make Python die. But there are at least the enums exposed now so it's possible to prevent the assert. --- doc/python/pages/changelog.rst | 2 + src/python/magnum/test/test_trade.py | 21 ++++++ src/python/magnum/trade.cpp | 106 ++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/doc/python/pages/changelog.rst b/doc/python/pages/changelog.rst index a1a691a..92e2968 100644 --- a/doc/python/pages/changelog.rst +++ b/doc/python/pages/changelog.rst @@ -111,6 +111,8 @@ Changelog :ref:`platform.glfw.Application.dpi_scaling` - Exposed :ref:`platform.glfw.Application.swap_interval` and :ref:`platform.glfw.Application.main_loop_iteration` +- Exposed :ref:`trade.AbstractImporter.features` and + :ref:`trade.AbstractImporter.flags` and corresponding enums - Exposed a basic interface of :ref:`trade.AbstractImageConverter` and :ref:`trade.AbstractSceneConverter` - Exposed the whole interface of :ref:`trade.MeshData` including typed access diff --git a/src/python/magnum/test/test_trade.py b/src/python/magnum/test/test_trade.py index 57edafa..0285cfd 100644 --- a/src/python/magnum/test/test_trade.py +++ b/src/python/magnum/test/test_trade.py @@ -1018,6 +1018,11 @@ class Importer(unittest.TestCase): importer = manager.load_and_instantiate('StbImageImporter') self.assertEqual(importer.plugin, 'StbImageImporter') + self.assertEqual(importer.features, trade.ImporterFeatures.OPEN_DATA) + self.assertEqual(importer.flags, trade.ImporterFlags.NONE) + + importer.flags = trade.ImporterFlags.VERBOSE + self.assertEqual(importer.flags, trade.ImporterFlags.VERBOSE) def test_set_plugin_directory(self): manager = trade.ImporterManager() @@ -1421,6 +1426,14 @@ class Importer(unittest.TestCase): importer.image2d('A broken image') class ImageConverter(unittest.TestCase): + def test(self): + converter = trade.ImageConverterManager().load_and_instantiate('StbImageConverter') + self.assertEqual(converter.features, trade.ImageConverterFeatures.CONVERT2D_TO_FILE|trade.ImageConverterFeatures.CONVERT2D_TO_DATA) + self.assertEqual(converter.flags, trade.ImageConverterFlags.NONE) + + converter.flags = trade.ImageConverterFlags.VERBOSE + self.assertEqual(converter.flags, trade.ImageConverterFlags.VERBOSE) + def test_image2d(self): importer = trade.ImporterManager().load_and_instantiate('StbImageImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'rgb.png')) @@ -1444,6 +1457,14 @@ class ImageConverter(unittest.TestCase): converter.convert_to_file(image, os.path.join(tmp, "image.hdr")) class SceneConverter(unittest.TestCase): + def test(self): + converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter') + self.assertEqual(converter.features, trade.SceneConverterFeatures.CONVERT_MESH_TO_FILE|trade.SceneConverterFeatures.CONVERT_MESH_TO_DATA) + self.assertEqual(converter.flags, trade.SceneConverterFlags.NONE) + + converter.flags = trade.SceneConverterFlags.VERBOSE + self.assertEqual(converter.flags, trade.SceneConverterFlags.VERBOSE) + def test_mesh(self): importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) diff --git a/src/python/magnum/trade.cpp b/src/python/magnum/trade.cpp index c99d7dc..1cfebc5 100644 --- a/src/python/magnum/trade.cpp +++ b/src/python/magnum/trade.cpp @@ -1425,10 +1425,31 @@ void trade(py::module_& m) { void*. Leaving the name as AbstractImporter (instead of Importer) to avoid needless name differences and because in the future there *might* be pure Python importers (not now tho). */ + py::enum_ importerFeatures{m, "ImporterFeatures", "Features supported by an image converter"}; + importerFeatures + .value("OPEN_DATA", Trade::ImporterFeature::OpenData) + .value("OPEN_STATE", Trade::ImporterFeature::OpenState) + .value("FILE_CALLBACK", Trade::ImporterFeature::FileCallback) + .value("NONE", Trade::ImporterFeature{}); + corrade::enumOperators(importerFeatures); + + py::enum_ importerFlags{m, "ImporterFlags", "Importer flags"}; + importerFlags + .value("VERBOSE", Trade::ImporterFlag::Verbose) + .value("NONE", Trade::ImporterFlag{}); + corrade::enumOperators(importerFlags); + py::class_, PluginManager::AbstractPlugin> abstractImporter{m, "AbstractImporter", "Interface for importer plugins"}; corrade::plugin(abstractImporter); abstractImporter - /** @todo features */ + .def_property_readonly("features", [](Trade::AbstractImporter& self) { + return Trade::ImporterFeature(Containers::enumCastUnderlyingType(self.features())); + }, "Features supported by this importer") + .def_property("flags", [](Trade::AbstractImporter& self) { + return Trade::ImporterFlag(Containers::enumCastUnderlyingType(self.flags())); + }, [](Trade::AbstractImporter& self, Trade::ImporterFlag flags) { + self.setFlags(flags); + }, "Importer flags") .def_property_readonly("is_opened", &Trade::AbstractImporter::isOpened, "Whether any file is opened") .def("open_data", [](Trade::AbstractImporter& self, Containers::ArrayView data) { /** @todo log redirection -- but we'd need assertions to not be @@ -1525,9 +1546,46 @@ void trade(py::module_& m) { corrade::manager(importerManager); /* Image converter */ + py::enum_ imageConverterFeatures{m, "ImageConverterFeatures", "Features supported by an image converter"}; + imageConverterFeatures + .value("CONVERT1D", Trade::ImageConverterFeature::Convert1D) + .value("CONVERT2D", Trade::ImageConverterFeature::Convert2D) + .value("CONVERT3D", Trade::ImageConverterFeature::Convert3D) + .value("CONVERT_COMPRESSED1D", Trade::ImageConverterFeature::ConvertCompressed1D) + .value("CONVERT_COMPRESSED2D", Trade::ImageConverterFeature::ConvertCompressed2D) + .value("CONVERT_COMPRESSED3D", Trade::ImageConverterFeature::ConvertCompressed3D) + .value("CONVERT1D_TO_FILE", Trade::ImageConverterFeature::Convert1DToFile) + .value("CONVERT2D_TO_FILE", Trade::ImageConverterFeature::Convert2DToFile) + .value("CONVERT3D_TO_FILE", Trade::ImageConverterFeature::Convert3DToFile) + .value("CONVERT_COMPRESSED1D_TO_FILE", Trade::ImageConverterFeature::ConvertCompressed1DToFile) + .value("CONVERT_COMPRESSED2D_TO_FILE", Trade::ImageConverterFeature::ConvertCompressed2DToFile) + .value("CONVERT_COMPRESSED3D_TO_FILE", Trade::ImageConverterFeature::ConvertCompressed3DToFile) + .value("CONVERT1D_TO_DATA", Trade::ImageConverterFeature::Convert1DToData) + .value("CONVERT2D_TO_DATA", Trade::ImageConverterFeature::Convert2DToData) + .value("CONVERT3D_TO_DATA", Trade::ImageConverterFeature::Convert3DToData) + .value("CONVERT_COMPRESSED1D_TO_DATA", Trade::ImageConverterFeature::ConvertCompressed1DToData) + .value("CONVERT_COMPRESSED2D_TO_DATA", Trade::ImageConverterFeature::ConvertCompressed2DToData) + .value("CONVERT_COMPRESSED3D_TO_DATA", Trade::ImageConverterFeature::ConvertCompressed3DToData) + .value("LEVELS", Trade::ImageConverterFeature::Levels) + .value("NONE", Trade::ImageConverterFeature{}); + corrade::enumOperators(imageConverterFeatures); + + py::enum_ imageConverterFlags{m, "ImageConverterFlags", "Image converter flags"}; + imageConverterFlags + .value("VERBOSE", Trade::ImageConverterFlag::Verbose) + .value("NONE", Trade::ImageConverterFlag{}); + corrade::enumOperators(imageConverterFlags); + py::class_, PluginManager::AbstractPlugin> abstractImageConverter{m, "AbstractImageConverter", "Interface for image converter plugins"}; abstractImageConverter - /** @todo features */ + .def_property_readonly("features", [](Trade::AbstractImageConverter& self) { + return Trade::ImageConverterFeature(Containers::enumCastUnderlyingType(self.features())); + }, "Features supported by this converter") + .def_property("flags", [](Trade::AbstractImageConverter& self) { + return Trade::ImageConverterFlag(Containers::enumCastUnderlyingType(self.flags())); + }, [](Trade::AbstractImageConverter& self, Trade::ImageConverterFlag flags) { + self.setFlags(flags); + }, "Converter flags") .def("convert_to_file", checkImageConverterResult, "Convert a 1D image to a file", py::arg("image"), py::arg("filename")) .def("convert_to_file", checkImageConverterResult, "Convert a 2D image to a file", py::arg("image"), py::arg("filename")) .def("convert_to_file", checkImageConverterResult, "Convert a 3D image to a file", py::arg("image"), py::arg("filename")); @@ -1537,9 +1595,51 @@ void trade(py::module_& m) { corrade::manager(imageConverterManager); /* Scene converter */ + py::enum_ sceneConverterFeatures{m, "SceneConverterFeatures", "Features supported by a scene converter"}; + sceneConverterFeatures + .value("CONVERT_MESH", Trade::SceneConverterFeature::ConvertMesh) + .value("CONVERT_MESH_IN_PLACE", Trade::SceneConverterFeature::ConvertMeshInPlace) + .value("CONVERT_MESH_TO_FILE", Trade::SceneConverterFeature::ConvertMeshToFile) + .value("CONVERT_MESH_TO_DATA", Trade::SceneConverterFeature::ConvertMeshToData) + .value("CONVERT_MULTIPLE", Trade::SceneConverterFeature::ConvertMultiple) + .value("CONVERT_MULTIPLE_TO_FILE", Trade::SceneConverterFeature::ConvertMultipleToFile) + .value("CONVERT_MULTIPLE_TO_DATA", Trade::SceneConverterFeature::ConvertMultipleToData) + .value("ADD_SCENES", Trade::SceneConverterFeature::AddScenes) + .value("ADD_ANIMATIONS", Trade::SceneConverterFeature::AddAnimations) + .value("ADD_LIGHTS", Trade::SceneConverterFeature::AddLights) + .value("ADD_CAMERAS", Trade::SceneConverterFeature::AddCameras) + .value("ADD_SKINS2D", Trade::SceneConverterFeature::AddSkins2D) + .value("ADD_SKINS3D", Trade::SceneConverterFeature::AddSkins3D) + .value("ADD_MESHES", Trade::SceneConverterFeature::AddMeshes) + .value("ADD_MATERIALS", Trade::SceneConverterFeature::AddMaterials) + .value("ADD_TEXTURES", Trade::SceneConverterFeature::AddTextures) + .value("ADD_IMAGES1D", Trade::SceneConverterFeature::AddImages1D) + .value("ADD_IMAGES2D", Trade::SceneConverterFeature::AddImages2D) + .value("ADD_IMAGES3D", Trade::SceneConverterFeature::AddImages3D) + .value("ADD_COMPRESSED_IMAGES1D", Trade::SceneConverterFeature::AddCompressedImages1D) + .value("ADD_COMPRESSED_IMAGES2D", Trade::SceneConverterFeature::AddCompressedImages2D) + .value("ADD_COMPRESSED_IMAGES3D", Trade::SceneConverterFeature::AddCompressedImages3D) + .value("MESH_LEVELS", Trade::SceneConverterFeature::MeshLevels) + .value("IMAGE_LEVELS", Trade::SceneConverterFeature::ImageLevels) + .value("NONE", Trade::SceneConverterFeature{}); + corrade::enumOperators(sceneConverterFeatures); + + py::enum_ sceneConverterFlags{m, "SceneConverterFlags", "Scene converter flags"}; + sceneConverterFlags + .value("VERBOSE", Trade::SceneConverterFlag::Verbose) + .value("NONE", Trade::SceneConverterFlag{}); + corrade::enumOperators(sceneConverterFlags); + py::class_, PluginManager::AbstractPlugin> abstractSceneConverter{m, "AbstractSceneConverter", "Interface for scene converter plugins"}; abstractSceneConverter - /** @todo features */ + .def_property_readonly("features", [](Trade::AbstractSceneConverter& self) { + return Trade::SceneConverterFeature(Containers::enumCastUnderlyingType(self.features())); + }, "Features supported by this converter") + .def_property("flags", [](Trade::AbstractSceneConverter& self) { + return Trade::SceneConverterFlag(Containers::enumCastUnderlyingType(self.flags())); + }, [](Trade::AbstractSceneConverter& self, Trade::SceneConverterFlag flags) { + self.setFlags(flags); + }, "Converter flags") /** @todo drop std::string in favor of our own string caster */ .def("convert_to_file", [](Trade::AbstractSceneConverter& self, const Trade::MeshData& mesh, const std::string& filename) { /** @todo log redirection -- but we'd need assertions to not be