Browse Source

python: data access in trade.SceneData.

Just regular boring code, all the cool stuff was invented for MeshData
already.
next
Vladimír Vondruš 3 years ago
parent
commit
89fa0d3f93
  1. 34
      doc/python/magnum.trade.rst
  2. 43
      src/Magnum/StridedArrayViewPythonBindings.h
  3. 221
      src/python/magnum/test/test_trade.py
  4. 307
      src/python/magnum/trade.cpp

34
doc/python/magnum.trade.rst

@ -210,6 +210,40 @@
>>> attribute.custom_value >>> attribute.custom_value
17 17
.. py:class:: magnum.trade.SceneData
:TODO: remove this line once m.css stops ignoring first caption on a page
`Index and attribute data access`_
==================================
The class makes use of Python's dynamic nature and provides direct access
to index and attribute data in their concrete types via :ref:`mapping()`
and :ref:`field()`. The returned views point to the underlying scene data,
element access coverts to a type corresponding to a particular
:ref:`SceneFieldType` and for performance-oriented access the view
implements a buffer protocol with a corresponding type annotation:
..
>>> import os
>>> from magnum import trade
>>> importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
>>> importer.open_file('../../src/python/magnum/test/scene.gltf')
.. code:: pycon
>>> scene = importer.scene(0)
>>> list(scene.mapping(trade.SceneField.TRANSLATION))
[1, 3, 0]
>>> list(scene.field(trade.SceneField.TRANSLATION))
[Vector(1, 2, 3), Vector(4, 5, 6), Vector(7, 8, 9)]
>>> np.array(scene.field(trade.SceneField.TRANSLATION), copy=False)[1]
array([4., 5., 6.], dtype=float32)
Depending on the value of :ref:`data_flags` it's also possible to access
the data in a mutable way via :ref:`mutable_mapping()` and
:ref:`mutable_field()`.
.. py:function:: magnum.trade.SceneData.field_name .. py:function:: magnum.trade.SceneData.field_name
:raise IndexError: If :p:`id` is negative or not less than :raise IndexError: If :p:`id` is negative or not less than
:ref:`field_count` :ref:`field_count`

43
src/Magnum/StridedArrayViewPythonBindings.h

@ -61,6 +61,49 @@ _c(Vector4us, "4H")
_c(Vector4s, "4h") _c(Vector4s, "4h")
_c(Vector4ui, "4I") _c(Vector4ui, "4I")
_c(Vector4i, "4i") _c(Vector4i, "4i")
_c(Matrix2x2, "4f")
_c(Matrix2x2d, "4d")
_c(Matrix2x3, "6f")
_c(Matrix2x3d, "6d")
_c(Matrix2x4, "8f")
_c(Matrix2x4d, "8d")
_c(Matrix3x2, "6f")
_c(Matrix3x2d, "6d")
_c(Matrix3x3, "9f")
_c(Matrix3x3d, "9d")
_c(Matrix3x4, "12f")
_c(Matrix3x4d, "12d")
_c(Matrix4x2, "8f")
_c(Matrix4x2d, "8d")
_c(Matrix4x3, "12f")
_c(Matrix4x3d, "12d")
_c(Matrix4x4, "16f")
_c(Matrix4x4d, "16d")
_c(Range1D, "2f")
_c(Range1Dd, "2d")
_c(Range1Di, "2i")
_c(Range2D, "4f")
_c(Range2Dd, "4d")
_c(Range2Di, "4i")
_c(Range3D, "6f")
_c(Range3Dd, "6d")
_c(Range3Di, "6i")
_c(Complex, "2f")
_c(Complexd, "2d")
_c(DualComplex, "4f")
_c(DualComplexd, "4f")
_c(Quaternion, "4f")
_c(Quaterniond, "4d")
_c(DualQuaternion, "8f")
_c(DualQuaterniond, "8d")
_c(Deg, "f")
_c(Degd, "d")
_c(Rad, "f")
_c(Radd, "d")
#undef _c #undef _c
} }

221
src/python/magnum/test/test_trade.py

@ -700,6 +700,188 @@ class SceneData(unittest.TestCase):
# some fields # some fields
self.assertEqual(scene.field_array_size(trade.SceneField.TRANSLATION), 0) self.assertEqual(scene.field_array_size(trade.SceneField.TRANSLATION), 0)
def test_mapping_access(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
scene = importer.scene(0)
scene_refcount = sys.getrefcount(scene)
translation_id = scene.field_id(trade.SceneField.TRANSLATION)
translations = scene.mapping(translation_id)
self.assertEqual(translations.size, (3, ))
self.assertEqual(translations.stride, (4, ))
self.assertEqual(translations.format, 'I')
self.assertEqual(list(translations), [1, 3, 0])
self.assertIs(translations.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del translations
self.assertEqual(sys.getrefcount(scene), scene_refcount)
cameras = scene.mapping(trade.SceneField.CAMERA)
self.assertEqual(cameras.size, (2, ))
self.assertEqual(cameras.stride, (4, ))
self.assertEqual(cameras.format, 'I')
self.assertEqual(list(cameras), [2, 3])
self.assertIs(cameras.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del cameras
self.assertEqual(sys.getrefcount(scene), scene_refcount)
mutable_translations = scene.mutable_mapping(translation_id)
self.assertEqual(mutable_translations.size, (3, ))
self.assertEqual(mutable_translations.stride, (4, ))
self.assertEqual(mutable_translations.format, 'I')
self.assertEqual(list(mutable_translations), [1, 3, 0])
self.assertIs(mutable_translations.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del mutable_translations
self.assertEqual(sys.getrefcount(scene), scene_refcount)
mutable_cameras = scene.mutable_mapping(trade.SceneField.CAMERA)
self.assertEqual(mutable_cameras.size, (2, ))
self.assertEqual(mutable_cameras.stride, (4, ))
self.assertEqual(mutable_cameras.format, 'I')
self.assertEqual(list(mutable_cameras), [2, 3])
self.assertIs(mutable_cameras.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del mutable_cameras
self.assertEqual(sys.getrefcount(scene), scene_refcount)
def test_field_access(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
scene = importer.scene(0)
scene_refcount = sys.getrefcount(scene)
translation_id = scene.field_id(trade.SceneField.TRANSLATION)
translations = scene.field(translation_id)
self.assertEqual(translations.size, (3, ))
self.assertEqual(translations.stride, (12, ))
self.assertEqual(translations.format, '3f')
self.assertEqual(list(translations), [
Vector3(1, 2, 3),
Vector3(4, 5, 6),
Vector3(7, 8, 9)
])
self.assertIs(translations.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del translations
self.assertEqual(sys.getrefcount(scene), scene_refcount)
cameras = scene.field(trade.SceneField.CAMERA)
self.assertEqual(cameras.size, (2, ))
self.assertEqual(cameras.stride, (4, ))
self.assertEqual(cameras.format, 'I')
self.assertEqual(list(cameras), [1, 0])
self.assertIs(cameras.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del cameras
self.assertEqual(sys.getrefcount(scene), scene_refcount)
mutable_translations = scene.mutable_field(translation_id)
self.assertEqual(mutable_translations.size, (3, ))
self.assertEqual(mutable_translations.stride, (12, ))
self.assertEqual(mutable_translations.format, '3f')
self.assertEqual(list(mutable_translations), [
Vector3(1, 2, 3),
Vector3(4, 5, 6),
Vector3(7, 8, 9)
])
self.assertIs(mutable_translations.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del mutable_translations
self.assertEqual(sys.getrefcount(scene), scene_refcount)
mutable_cameras = scene.mutable_field(trade.SceneField.CAMERA)
self.assertEqual(mutable_cameras.size, (2, ))
self.assertEqual(mutable_cameras.stride, (4, ))
self.assertEqual(mutable_cameras.format, 'I')
self.assertEqual(list(mutable_cameras), [1, 0])
self.assertIs(mutable_cameras.owner, scene)
self.assertEqual(sys.getrefcount(scene), scene_refcount + 1)
del mutable_cameras
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'))
scene = importer.scene(0)
self.assertEqual(scene.data_flags, trade.DataFlag.OWNED|trade.DataFlag.MUTABLE)
translation_id = scene.field_id(trade.SceneField.TRANSLATION)
translations = scene.mapping(translation_id)
mutable_translations = scene.mutable_mapping(translation_id)
self.assertEqual(translations[1], 3)
self.assertEqual(mutable_translations[1], 3)
mutable_translations[1] = 776
self.assertEqual(translations[1], 776)
cameras = scene.mapping(trade.SceneField.CAMERA)
mutable_cameras = scene.mutable_mapping(trade.SceneField.CAMERA)
self.assertEqual(cameras[1], 3)
self.assertEqual(mutable_cameras[1], 3)
mutable_cameras[1] = 13378
self.assertEqual(cameras[1], 13378)
def test_mutable_field_access(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
scene = importer.scene(0)
self.assertEqual(scene.data_flags, trade.DataFlag.OWNED|trade.DataFlag.MUTABLE)
translation_id = scene.field_id(trade.SceneField.TRANSLATION)
translations = scene.field(translation_id)
mutable_translations = scene.mutable_field(translation_id)
self.assertEqual(translations[1], Vector3(4, 5, 6))
self.assertEqual(mutable_translations[1], Vector3(4, 5, 6))
mutable_translations[1] *= 0.5
self.assertEqual(translations[1], Vector3(2, 2.5, 3))
cameras = scene.field(trade.SceneField.CAMERA)
mutable_cameras = scene.mutable_field(trade.SceneField.CAMERA)
self.assertEqual(cameras[1], 0)
self.assertEqual(mutable_cameras[1], 0)
mutable_cameras[1] = 13378
self.assertEqual(cameras[1], 13378)
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'))
scene = importer.scene(0)
self.assertEqual(scene.data_flags, trade.DataFlag.OWNED|trade.DataFlag.MUTABLE)
pointer = scene.field(trade.SceneField.IMPORTER_STATE)
mutable_pointer = scene.mutable_field(trade.SceneField.IMPORTER_STATE)
self.assertEqual(pointer.format, 'P')
self.assertEqual(mutable_pointer.format, 'P')
self.assertNotEqual(pointer[1], 0x0)
self.assertEqual(mutable_pointer[1], pointer[1])
mutable_pointer[1] = 0xdeadbeef
self.assertEqual(pointer[1], 0xdeadbeef)
def test_data_access_not_mutable(self):
pass
# TODO implement once there's a way to get immutable SceneData, either
# by "deserializing" a binary blob or via some SceneTools API
def test_field_oob(self): def test_field_oob(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter') importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf')) importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
@ -721,6 +903,14 @@ class SceneData(unittest.TestCase):
scene.has_field_object(scene.field_count, 0) scene.has_field_object(scene.field_count, 0)
with self.assertRaisesRegex(IndexError, "field out of range"): with self.assertRaisesRegex(IndexError, "field out of range"):
scene.field_object_offset(scene.field_count, 0) scene.field_object_offset(scene.field_count, 0)
with self.assertRaises(IndexError):
scene.mapping(scene.field_count)
with self.assertRaises(IndexError):
scene.mutable_mapping(scene.field_count)
with self.assertRaises(IndexError):
scene.field(scene.field_count)
with self.assertRaises(IndexError):
scene.mutable_field(scene.field_count)
# Access by nonexistent field name # Access by nonexistent field name
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
@ -737,6 +927,14 @@ class SceneData(unittest.TestCase):
scene.has_field_object(trade.SceneField.SCALING, 0) scene.has_field_object(trade.SceneField.SCALING, 0)
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
scene.field_object_offset(trade.SceneField.SCALING, 0) scene.field_object_offset(trade.SceneField.SCALING, 0)
with self.assertRaises(KeyError):
scene.mapping(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
scene.mutable_mapping(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
scene.field(trade.SceneField.SCALING)
with self.assertRaises(KeyError):
scene.mutable_field(trade.SceneField.SCALING)
# OOB object ID # OOB object ID
with self.assertRaisesRegex(IndexError, "object out of range"): with self.assertRaisesRegex(IndexError, "object out of range"):
@ -766,6 +964,29 @@ class SceneData(unittest.TestCase):
with self.assertRaisesRegex(IndexError, "offset out of range"): with self.assertRaisesRegex(IndexError, "offset out of range"):
scene.field_object_offset(trade.SceneField.PARENT, 1, scene.field_size(trade.SceneField.PARENT) + 1) scene.field_object_offset(trade.SceneField.PARENT, 1, scene.field_size(trade.SceneField.PARENT) + 1)
def test_field_access_array(self):
pass
# TODO implement once there's some importer that gives back arrays
# (gltf? not sure)
def test_field_access_unsupported_type(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
string_field = importer.scene_field_for_name('aString')
self.assertIsNotNone(string_field)
scene = importer.scene(0)
string_field_id = scene.field_id(string_field)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
scene.field(string_field_id)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
scene.mutable_field(string_field_id)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
scene.field(string_field)
with self.assertRaisesRegex(NotImplementedError, "access to this scene field type is not implemented yet, sorry"):
scene.mutable_field(string_field)
class Importer(unittest.TestCase): class Importer(unittest.TestCase):
def test(self): def test(self):
manager = trade.ImporterManager() manager = trade.ImporterManager()

307
src/python/magnum/trade.cpp

@ -29,8 +29,13 @@
#include <Corrade/Containers/StringStl.h> /** @todo drop once we have our string casters */ #include <Corrade/Containers/StringStl.h> /** @todo drop once we have our string casters */
#include <Corrade/Containers/Triple.h> #include <Corrade/Containers/Triple.h>
#include <Magnum/ImageView.h> #include <Magnum/ImageView.h>
#include <Magnum/Math/Complex.h>
#include <Magnum/Math/DualComplex.h>
#include <Magnum/Math/Matrix4.h> #include <Magnum/Math/Matrix4.h>
#include <Magnum/Math/Packing.h> #include <Magnum/Math/Packing.h>
#include <Magnum/Math/Quaternion.h>
#include <Magnum/Math/DualQuaternion.h>
#include <Magnum/Math/Range.h>
#include <Magnum/Trade/AbstractImporter.h> #include <Magnum/Trade/AbstractImporter.h>
#include <Magnum/Trade/AbstractImageConverter.h> #include <Magnum/Trade/AbstractImageConverter.h>
#include <Magnum/Trade/AbstractSceneConverter.h> #include <Magnum/Trade/AbstractSceneConverter.h>
@ -584,6 +589,201 @@ template<class T> Containers::PyArrayViewHolder<Containers::PyStridedArrayView<1
return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<1, T>{data.template transposed<0, 1>()[0], formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, py::cast(mesh)); return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<1, T>{data.template transposed<0, 1>()[0], formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, py::cast(mesh));
} }
Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> accessorsForSceneMappingType(const Trade::SceneMappingType type) {
switch(type) {
#define _c(type) \
case Trade::SceneMappingType::type: return { \
Containers::Implementation::pythonFormatString<type>(), \
[](const char* item) { \
return py::cast(*reinterpret_cast<const type*>(item)); \
}, \
[](char* item, py::handle object) { \
*reinterpret_cast<type*>(item) = py::cast<type>(object); \
}};
/* LCOV_EXCL_START */
_c(UnsignedByte)
_c(UnsignedShort)
_c(UnsignedInt)
_c(UnsignedLong)
/* LCOV_EXCL_STOP */
#undef _c
}
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> accessorsForSceneFieldType(const Trade::SceneFieldType type) {
switch(type) {
#define _ctf(typeEnum, type, format) \
case Trade::SceneFieldType::typeEnum: return { \
format, \
[](const char* item) { \
return py::cast(*reinterpret_cast<type const*>(item)); \
}, \
[](char* item, py::handle object) { \
*reinterpret_cast<type*>(item) = py::cast<type>(object); \
}};
#define _c(type) _ctf(type, type, Containers::Implementation::pythonFormatString<type>())
/* Types (such as half-floats) that need to be cast before passed
from/to pybind that doesn't understand the type directly */
#define _cc(type, castType) \
case Trade::SceneFieldType::type: return { \
Containers::Implementation::pythonFormatString<type>(), \
[](const char* item) { \
return py::cast(castType(*reinterpret_cast<const type*>(item))); \
}, \
[](char* item, py::handle object) { \
*reinterpret_cast<type*>(item) = type(py::cast<castType>(object)); \
}};
/* LCOV_EXCL_START */
_c(Float)
_c(Double)
_c(UnsignedByte)
_c(Byte)
_c(UnsignedShort)
_c(Short)
_c(UnsignedInt)
_c(Int)
_c(UnsignedLong)
_c(Long)
_c(Vector2)
_c(Vector2d)
_cc(Vector2ub, Vector2ui)
_cc(Vector2b, Vector2i)
_cc(Vector2us, Vector2ui)
_cc(Vector2s, Vector2i)
_c(Vector2ui)
_c(Vector2i)
_c(Vector3)
_c(Vector3d)
_cc(Vector3ub, Vector3ui)
_cc(Vector3b, Vector3i)
_cc(Vector3us, Vector3ui)
_cc(Vector3s, Vector3i)
_c(Vector3ui)
_c(Vector3i)
_c(Vector4)
_c(Vector4d)
_cc(Vector4ub, Vector4ui)
_cc(Vector4b, Vector4i)
_cc(Vector4us, Vector4ui)
_cc(Vector4s, Vector4i)
_c(Vector4ui)
_c(Vector4i)
_c(Matrix2x2)
_c(Matrix2x2d)
_c(Matrix2x3)
_c(Matrix2x3d)
_c(Matrix2x4)
_c(Matrix2x4d)
_c(Matrix3x2)
_c(Matrix3x2d)
_c(Matrix3x3)
_c(Matrix3x3d)
_c(Matrix3x4)
_c(Matrix3x4d)
_c(Matrix4x2)
_c(Matrix4x2d)
_c(Matrix4x3)
_c(Matrix4x3d)
_c(Matrix4x4)
_c(Matrix4x4d)
_c(Range1D)
_c(Range1Dd)
_c(Range1Di)
_c(Range2D)
_c(Range2Dd)
_c(Range2Di)
_c(Range3D)
_c(Range3Dd)
_c(Range3Di)
_c(Complex)
_c(Complexd)
_c(DualComplex)
_c(DualComplexd)
_c(Quaternion)
_c(Quaterniond)
_c(DualQuaternion)
_c(DualQuaterniond)
_c(Deg)
_c(Degd)
_c(Rad)
_c(Radd)
/* I see very little reason for accessing these from Python but
nevertheless, one never knows when it will be useful */
/** @todo passing them through as void* makes them a capsule object in
Python, is that useful for anything? and the P type is useless for
numpy ("'P' is not a valid PEP 3118 buffer format string") */
_ctf(Pointer, std::size_t, "P")
_ctf(MutablePointer, std::size_t, "P")
/* LCOV_EXCL_STOP */
#undef _c
#undef _cc
/** @todo handle these once there's something to test with */
case Trade::SceneFieldType::Half:
case Trade::SceneFieldType::Vector2h:
case Trade::SceneFieldType::Vector3h:
case Trade::SceneFieldType::Vector4h:
case Trade::SceneFieldType::Matrix2x2h:
case Trade::SceneFieldType::Matrix2x3h:
case Trade::SceneFieldType::Matrix2x4h:
case Trade::SceneFieldType::Matrix3x2h:
case Trade::SceneFieldType::Matrix3x3h:
case Trade::SceneFieldType::Matrix3x4h:
case Trade::SceneFieldType::Matrix4x2h:
case Trade::SceneFieldType::Matrix4x3h:
case Trade::SceneFieldType::Matrix4x4h:
case Trade::SceneFieldType::Range1Dh:
case Trade::SceneFieldType::Range2Dh:
case Trade::SceneFieldType::Range3Dh:
case Trade::SceneFieldType::Degh:
case Trade::SceneFieldType::Radh:
/** @todo handle these once StringIterable is exposed */
case Trade::SceneFieldType::StringOffset8:
case Trade::SceneFieldType::StringOffset16:
case Trade::SceneFieldType::StringOffset32:
case Trade::SceneFieldType::StringOffset64:
case Trade::SceneFieldType::StringRange8:
case Trade::SceneFieldType::StringRange16:
case Trade::SceneFieldType::StringRange32:
case Trade::SceneFieldType::StringRange64:
case Trade::SceneFieldType::StringRangeNullTerminated8:
case Trade::SceneFieldType::StringRangeNullTerminated16:
case Trade::SceneFieldType::StringRangeNullTerminated32:
case Trade::SceneFieldType::StringRangeNullTerminated64:
return {};
}
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
template<class T> Containers::PyArrayViewHolder<Containers::PyStridedArrayView<1, T>> sceneMappingView(Trade::SceneData& scene, const Containers::StridedArrayView2D<T>& data) {
const Trade::SceneMappingType type = scene.mappingType();
const std::size_t itemsize = Trade::sceneMappingTypeSize(type);
const Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> formatStringGetitemSetitem = accessorsForSceneMappingType(type);
/* We support all mapping types */
CORRADE_INTERNAL_ASSERT(formatStringGetitemSetitem.first());
return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<1, T>{data.template transposed<0, 1>()[0], formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, py::cast(scene));
}
template<class T> Containers::PyArrayViewHolder<Containers::PyStridedArrayView<1, T>> sceneFieldView(Trade::SceneData& scene, const UnsignedInt id, const Containers::StridedArrayView2D<T>& data) {
const Trade::SceneFieldType type = scene.fieldType(id);
const std::size_t itemsize = Trade::sceneFieldTypeSize(type);
const Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> formatStringGetitemSetitem = accessorsForSceneFieldType(type);
if(!formatStringGetitemSetitem.first()) {
PyErr_SetString(PyExc_NotImplementedError, "access to this scene field type is not implemented yet, sorry");
throw py::error_already_set{};
}
return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<1, T>{data.template transposed<0, 1>()[0], formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, py::cast(scene));
}
} }
void trade(py::module_& m) { void trade(py::module_& m) {
@ -982,6 +1182,11 @@ void trade(py::module_& m) {
corrade::enumOperators(sceneFieldFlag); corrade::enumOperators(sceneFieldFlag);
py::class_<Trade::SceneData>{m, "SceneData", "Scene data"} py::class_<Trade::SceneData>{m, "SceneData", "Scene data"}
.def_property_readonly("data_flags", [](Trade::SceneData& self) {
return Trade::DataFlag(Containers::enumCastUnderlyingType(self.dataFlags()));
}, "Data flags")
/** @todo expose raw data at all? compared to meshes there's no use
case like being able to pass raw memory to the GPU ... yet */
.def_property_readonly("mapping_type", &Trade::SceneData::mappingType, "Type used for object mapping") .def_property_readonly("mapping_type", &Trade::SceneData::mappingType, "Type used for object mapping")
.def_property_readonly("mapping_bound", &Trade::SceneData::mappingBound, "Object mapping bound") .def_property_readonly("mapping_bound", &Trade::SceneData::mappingBound, "Object mapping bound")
.def_property_readonly("field_count", &Trade::SceneData::fieldCount, "Field count") .def_property_readonly("field_count", &Trade::SceneData::fieldCount, "Field count")
@ -1131,7 +1336,107 @@ void trade(py::module_& m) {
throw py::error_already_set{}; throw py::error_already_set{};
} }
return self.hasFieldObject(fieldId, object); return self.hasFieldObject(fieldId, object);
}, "Whether a scene field has given object", py::arg("field_id"), py::arg("object")); }, "Whether a scene field has given object", py::arg("field_id"), py::arg("object"))
.def("mapping", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
return sceneMappingView(self, self.mapping(*found));
}, "Object mapping data for given named field", py::arg("name"))
.def("mapping", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
return sceneMappingView(self, self.mapping(id));
}, "Object mapping data for given field", py::arg("name"))
.def("mutable_mapping", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AttributeError, "scene data is not mutable");
throw py::error_already_set{};
}
return sceneMappingView(self, self.mutableMapping(*found));
}, "Mutable object mapping data for given named field", py::arg("name"))
.def("mutable_mapping", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AttributeError, "scene data is not mutable");
throw py::error_already_set{};
}
return sceneMappingView(self, self.mutableMapping(id));
}, "Mutable object mapping data for given field", py::arg("name"))
.def("field", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
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) */
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));
}, "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) */
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));
}, "Data for given field", py::arg("name"))
.def("mutable_field", [](Trade::SceneData& self, Trade::SceneField name) {
const Containers::Optional<UnsignedInt> found = self.findFieldId(name);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
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) */
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));
}, "Mutable data for given named field", py::arg("name"))
.def("mutable_field", [](Trade::SceneData& self, UnsignedInt id) {
if(id >= self.fieldCount()) {
PyErr_SetNone(PyExc_IndexError);
throw py::error_already_set{};
}
if(!(self.dataFlags() & Trade::DataFlag::Mutable)) {
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) */
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));
}, "Mutable data for given field", py::arg("name"));
/* Importer. Skipping file callbacks and openState as those operate with /* Importer. Skipping file callbacks and openState as those operate with
void*. Leaving the name as AbstractImporter (instead of Importer) to void*. Leaving the name as AbstractImporter (instead of Importer) to

Loading…
Cancel
Save