Browse Source

python: expose scenetools.filter_fields(), filter_{only,except}_fields().

next
Vladimír Vondruš 3 years ago
parent
commit
4be2d4d9c0
  1. 4
      doc/python/magnum.scenetools.rst
  2. 30
      src/python/magnum/scenetools.cpp
  3. 138
      src/python/magnum/test/test_scenetools.py

4
doc/python/magnum.scenetools.rst

@ -23,6 +23,10 @@
DEALINGS IN THE SOFTWARE.
..
.. py:function:: magnum.scenetools.filter_fields
:raise AssertionError: If size of :p:`fields_to_keep` is different than
:ref:`trade.SceneData.field_count`
.. py:function:: magnum.scenetools.absolute_field_transformations2d
:raise KeyError: If :p:`field` does not exist in :p:`scene`
:raise IndexError: If :p:`field_id` negative or not less than

30
src/python/magnum/scenetools.cpp

@ -25,13 +25,18 @@
#include <pybind11/pybind11.h>
#include <pybind11/stl.h> /* for std::vector */
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/ArrayViewStl.h>
#include <Corrade/Containers/BitArrayView.h>
#include <Corrade/Containers/Optional.h>
#include <Magnum/Math/Matrix3.h>
#include <Magnum/Math/Matrix4.h>
#include <Magnum/SceneTools/Filter.h>
#include <Magnum/SceneTools/Hierarchy.h>
#include <Magnum/Trade/SceneData.h>
#include "Corrade/PythonBindings.h"
#include "Magnum/Trade/PythonBindings.h"
#include "magnum/bootstrap.h"
namespace magnum {
@ -46,6 +51,29 @@ void scenetools(py::module_& m) {
#endif
m
.def("filter_fields", [](const Trade::SceneData& scene, const Containers::BitArrayView fieldsToKeep) {
if(fieldsToKeep.size() != scene.fieldCount()) {
PyErr_Format(PyExc_AssertionError, "expected %u bits but got %zu", scene.fieldCount(), fieldsToKeep.size());
throw py::error_already_set{};
}
/* If the scene already has an owner, use that instead to avoid
long reference chains */
py::object sceneOwner = pyObjectHolderFor<Trade::PyDataHolder>(scene).owner;
return Trade::pyDataHolder(SceneTools::filterFields(scene, fieldsToKeep), sceneOwner.is_none() ? py::cast(scene) : std::move(sceneOwner));
}, "Filter a scene to contain only the selected subset of fields", py::arg("scene"), py::arg("fields_to_keep"))
.def("filter_only_fields", [](const Trade::SceneData& scene, const std::vector<Trade::SceneField> fields) {
/* If the scene already has an owner, use that instead to avoid
long reference chains */
py::object sceneOwner = pyObjectHolderFor<Trade::PyDataHolder>(scene).owner;
return Trade::pyDataHolder(SceneTools::filterOnlyFields(scene, fields), sceneOwner.is_none() ? py::cast(scene) : std::move(sceneOwner));
}, "Filter a scene to contain only the selected subset of named fields", py::arg("scene"), py::arg("fields"))
.def("filter_except_fields", [](const Trade::SceneData& scene, const std::vector<Trade::SceneField> fields) {
/* If the scene already has an owner, use that instead to avoid
long reference chains */
py::object sceneOwner = pyObjectHolderFor<Trade::PyDataHolder>(scene).owner;
return Trade::pyDataHolder(SceneTools::filterExceptFields(scene, fields), sceneOwner.is_none() ? py::cast(scene) : std::move(sceneOwner));
}, "Filter a scene to contain everything the selected subset of named fields", py::arg("scene"), py::arg("fields"))
.def("absolute_field_transformations2d", [](const Trade::SceneData& scene, Trade::SceneField field, const Matrix3& globalTransformation) {
const Containers::Optional<UnsignedInt> fieldId = scene.findFieldId(field);
if(!fieldId) {

138
src/python/magnum/test/test_scenetools.py

@ -23,6 +23,7 @@
# DEALINGS IN THE SOFTWARE.
#
import sys
import unittest
from corrade import containers
@ -30,6 +31,143 @@ from magnum import *
from magnum import scenetools, trade
import magnum
class Filter(unittest.TestCase):
def test_fields(self):
# Static builds with non-static plugins cause assertions with non-owned
# array deleters used by PrimitiveImporter, skip in that case
if magnum.BUILD_STATIC:
self.skipTest("dynamic PrimitiveImporter doesn't work with a static build")
importer = trade.ImporterManager().load_and_instantiate('PrimitiveImporter')
importer.open_data(containers.ArrayView())
scene = importer.scene(0)
scene_refcount = sys.getrefcount(scene)
self.assertEqual(scene.field_count, 3)
self.assertTrue(scene.has_field(trade.SceneField.TRANSLATION))
self.assertTrue(scene.has_field(trade.SceneField.MESH))
fields_to_keep = containers.BitArray.value_init(scene.field_count)
fields_to_keep[scene.field_id(trade.SceneField.TRANSLATION)] = True
fields_to_keep[scene.field_id(trade.SceneField.MESH)] = True
filtered = scenetools.filter_fields(scene, fields_to_keep)
filtered_refcount = sys.getrefcount(filtered)
self.assertEqual(filtered.field_count, 2)
self.assertTrue(filtered.has_field(trade.SceneField.TRANSLATION))
self.assertTrue(filtered.has_field(trade.SceneField.MESH))
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
self.assertIs(filtered.owner, scene)
# Subsequent filtering will still reference the original scene, not the
# intermediates
filtered2 = scenetools.filter_fields(filtered, containers.BitArray.direct_init(filtered.field_count, True))
self.assertEqual(filtered2.field_count, 2)
self.assertTrue(filtered2.has_field(trade.SceneField.TRANSLATION))
self.assertTrue(filtered2.has_field(trade.SceneField.MESH))
self.assertEqual(sys.getrefcount(filtered), filtered_refcount)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 2)
self.assertIs(filtered2.owner, scene)
del filtered
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del filtered2
self.assertEqual(sys.getrefcount(scene), scene_refcount)
def test_fields_invalid_size(self):
# Static builds with non-static plugins cause assertions with non-owned
# array deleters used by PrimitiveImporter, skip in that case
if magnum.BUILD_STATIC:
self.skipTest("dynamic PrimitiveImporter doesn't work with a static build")
importer = trade.ImporterManager().load_and_instantiate('PrimitiveImporter')
importer.open_data(containers.ArrayView())
scene = importer.scene(0)
with self.assertRaisesRegex(AssertionError, "expected 3 bits but got 4"):
scenetools.filter_fields(scene, containers.BitArray.value_init(4))
def test_only_fields(self):
# Static builds with non-static plugins cause assertions with non-owned
# array deleters used by PrimitiveImporter, skip in that case
if magnum.BUILD_STATIC:
self.skipTest("dynamic PrimitiveImporter doesn't work with a static build")
importer = trade.ImporterManager().load_and_instantiate('PrimitiveImporter')
importer.open_data(containers.ArrayView())
scene = importer.scene(0)
scene_refcount = sys.getrefcount(scene)
self.assertEqual(scene.field_count, 3)
# Fields that are not present in the mesh are deliberately ignored
filtered = scenetools.filter_only_fields(scene, [trade.SceneField.LIGHT, trade.SceneField.MESH, trade.SceneField.TRANSLATION])
filtered_refcount = sys.getrefcount(filtered)
self.assertEqual(filtered.field_count, 2)
self.assertTrue(filtered.has_field(trade.SceneField.TRANSLATION))
self.assertTrue(filtered.has_field(trade.SceneField.MESH))
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
self.assertIs(filtered.owner, scene)
# Subsequent filtering will still reference the original scene, not the
# intermediates
filtered2 = scenetools.filter_only_fields(filtered, [trade.SceneField.MESH, trade.SceneField.TRANSLATION])
self.assertEqual(filtered2.field_count, 2)
self.assertTrue(filtered2.has_field(trade.SceneField.TRANSLATION))
self.assertTrue(filtered2.has_field(trade.SceneField.MESH))
self.assertEqual(sys.getrefcount(filtered), filtered_refcount)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 2)
self.assertIs(filtered2.owner, scene)
del filtered
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del filtered2
self.assertEqual(sys.getrefcount(scene), scene_refcount)
def test_except_fields(self):
# Static builds with non-static plugins cause assertions with non-owned
# array deleters used by PrimitiveImporter, skip in that case
if magnum.BUILD_STATIC:
self.skipTest("dynamic PrimitiveImporter doesn't work with a static build")
importer = trade.ImporterManager().load_and_instantiate('PrimitiveImporter')
importer.open_data(containers.ArrayView())
scene = importer.scene(0)
scene_refcount = sys.getrefcount(scene)
self.assertEqual(scene.field_count, 3)
# Fields that are not present in the mesh are deliberately ignored
filtered = scenetools.filter_except_fields(scene, [trade.SceneField.SKIN, trade.SceneField.PARENT])
filtered_refcount = sys.getrefcount(filtered)
self.assertEqual(filtered.field_count, 2)
self.assertTrue(filtered.has_field(trade.SceneField.TRANSLATION))
self.assertTrue(filtered.has_field(trade.SceneField.MESH))
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
self.assertIs(filtered.owner, scene)
# Subsequent filtering will still reference the original scene, not the
# intermediates
filtered2 = scenetools.filter_except_fields(filtered, [trade.SceneField.PARENT])
self.assertEqual(filtered2.field_count, 2)
self.assertTrue(filtered2.has_field(trade.SceneField.TRANSLATION))
self.assertTrue(filtered2.has_field(trade.SceneField.MESH))
self.assertEqual(sys.getrefcount(filtered), filtered_refcount)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 2)
self.assertIs(filtered2.owner, scene)
del filtered
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del filtered2
self.assertEqual(sys.getrefcount(scene), scene_refcount)
class Hierarchy(unittest.TestCase):
def test_absolute_field_transformations2d(self):
# Static builds with non-static plugins cause assertions with non-owned

Loading…
Cancel
Save