Browse Source

python: expose trade.SceneFieldData.

Like with MeshAttributeData, to be eventually used for populating scenes
from scratch.
next
Vladimír Vondruš 2 years ago
parent
commit
dcbfb28c05
  1. 157
      doc/python/magnum.trade.rst
  2. 7
      doc/python/pages/changelog.rst
  3. 20
      src/Magnum/Trade/PythonBindings.h
  4. 487
      src/python/magnum/test/test_trade.py
  5. 145
      src/python/magnum/trade.cpp

157
doc/python/magnum.trade.rst

@ -702,6 +702,163 @@
>>> attribute.custom_value >>> attribute.custom_value
17 17
.. py:class:: magnum.trade.SceneFieldData
Associates a pair of typed data views with a name, type and other scene
field properties. The mapping data view is always one-dimensional. The
field data view can be either one-dimensional, for example a NumPy array:
..
Just to verify the snippet below actually works (don't want the arrows
shown in the docs, want to have it nicely wrapped)
..
>>> mapping_data = array.array('I', [0, 2, 7])
>>> field_data = np.array([(-0.5, 0.0), (+0.5, 0.0), ( 0.0, 0.5)], dtype='2f')
>>> translations = trade.SceneFieldData(trade.SceneField.TRANSLATION, trade.SceneMappingType.UNSIGNED_INT, mapping_data, trade.SceneFieldType.VECTOR2, field_data)
.. code:: py
mapping_data = array.array('I', [0, 2, 7])
field_data = np.array([(-0.5, 0.0),
(+0.5, 0.0),
( 0.0, 0.5)], dtype='2f')
translations = trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_INT, mapping_data,
trade.SceneFieldType.VECTOR2, field_data)
Or it can be two-dimensional, for example by expanding a flat array into a
list of two-component vectors:
..
Again to verify the snippet below actually works
..
>>> field_data = array.array('f', [-0.5, 0.0, +0.5, 0.0, 0.0, 0.5])
>>> translations = trade.SceneFieldData(trade.SceneField.TRANSLATION, trade.SceneMappingType.UNSIGNED_INT, mapping_data, trade.SceneFieldType.VECTOR2, containers.StridedArrayView1D(field_data).expanded(0, (3, 2)))
.. code:: py
field_data = array.array('f', [-0.5, 0.0,
+0.5, 0.0,
0.0, 0.5])
translations = trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_INT, mapping_data,
trade.SceneFieldType.VECTOR2,
containers.StridedArrayView1D(field_data).expanded(0, (3, 2)))
`Memory ownership and reference counting`_
==========================================
On initialization, the instance inherits the
:ref:`containers.StridedArrayView1D.owner <corrade.containers.StridedArrayView1D.owner>`
objects of both views, storing it in the :ref:`mapping_owner` and
:ref:`field_owner` fields, meaning that calling :py:`del` on the original
data will *not* invalidate the instance.
`Data access`_
==============
Similarly to :ref:`SceneData`, the class makes use of Python's dynamic
nature and provides direct access to attribute data in their concrete type
via :ref:`mapping_data` and :ref:`field_data`. However, the
:ref:`SceneFieldData` is considered a low level API and thus a
:ref:`containers.StridedArrayView2D <corrade.containers.StridedArrayView2D>`
/ :ref:`containers.StridedBitArrayView2D <corrade.containers.StridedBitArrayView2D>`
is returned always for field data, even for non-array attributes. The
returned views inherit the :ref:`mapping_owner` or :ref:`field_owner` and
element access coverts to a type corresponding to a particular
:ref:`SceneMappingType` or :ref:`SceneFieldType`. For example, extracting
the data from the :py:`translations` field created above:
.. code:: pycon
>>> mapping = translations.mapping_data
>>> field = translations.field_data
>>> mapping.owner is mapping_data
True
>>> field.owner is field_data
True
>>> mapping[2]
7
>>> field[2][0]
Vector(0, 0.5)
.. py:function:: magnum.trade.SceneFieldData.__init__(self, name: magnum.trade.SceneField, mapping_type: magnum.trade.SceneMappingType, mapping_data: corrade.containers.StridedArrayView1D, field_type: magnum.trade.SceneFieldType, field_data: corrade.containers.StridedArrayView1D, field_array_size: int, flags: magnum.trade.SceneFieldFlags)
:raise AssertionError: If :p:`mapping_data` and :p:`field_data` don't have
the same size
:raise AssertionError: If :p:`field_type` is not valid for :p:`name`
:raise AssertionError: If :p:`field_type` is a string type or
:ref:`SceneFieldType.BIT`
:raise AssertionError: If :p:`mapping_data` stride doesn't fit into 16 bits
:raise AssertionError: If :p:`mapping_data` format size is smaller than
size of :p:`mapping_type`
:raise AssertionError: If :p:`field_data` stride doesn't fit into 16 bits
:raise AssertionError: If :p:`field_data` format size is smaller than size
of :p:`field_type` at given :p:`field_array_size`
:raise AssertionError: If :p:`field_array_size` is non-zero and :p:`name`
can't be an array field
:raise AssertionError: If :p:`flags` contain
:ref:`SceneFieldFlags.OFFSET_ONLY`,
:ref:`SceneFieldFlags.NULL_TERMINATED_STRING` or values disallowed for
a particular :p:`name`
.. py:function:: magnum.trade.SceneFieldData.__init__(self, name: magnum.trade.SceneField, mapping_type: magnum.trade.SceneMappingType, mapping_data: corrade.containers.StridedArrayView1D, field_type: magnum.trade.SceneFieldType, field_data: corrade.containers.StridedArrayView2D, field_array_size: int, flags: magnum.trade.SceneFieldFlags)
:raise AssertionError: If :p:`mapping_data` and first dimension of
:p:`field_data` don't have the same size
:raise AssertionError: If :p:`field_type` is not valid for :p:`name`
:raise AssertionError: If :p:`field_type` is a string type or
:ref:`SceneFieldType.BIT`
:raise AssertionError: If :p:`mapping_data` stride doesn't fit into 16 bits
:raise AssertionError: If :p:`mapping_data` format size is smaller than
size of :p:`mapping_type`
:raise AssertionError: If :p:`field_data` first dimension stride doesn't
fit into 16 bits
:raise AssertionError: If :p:`field_data` second dimension isn't contiguous
:raise AssertionError: If :p:`field_data` format size times second
dimension size is smaller than size of :p:`field_type` at given :p:`field_array_size`
:raise AssertionError: If :p:`field_array_size` is non-zero and :p:`name`
can't be an array field
:raise AssertionError: If :p:`flags` contain
:ref:`SceneFieldFlags.OFFSET_ONLY`,
:ref:`SceneFieldFlags.NULL_TERMINATED_STRING` or values disallowed for
a particular :p:`name`
.. py:function:: magnum.trade.SceneFieldData.__init__(self, name: magnum.trade.SceneField, mapping_type: magnum.trade.SceneMappingType, mapping_data: corrade.containers.StridedArrayView1D, field_data: corrade.containers.StridedBitArrayView1D, flags: magnum.trade.SceneFieldFlags)
:raise AssertionError: If :p:`mapping_data` and :p:`field_data` don't have
the same size
:raise AssertionError: If :ref:`SceneFieldType.BIT` is not valid for
:p:`name`
:raise AssertionError: If :p:`mapping_data` stride doesn't fit into 16 bits
:raise AssertionError: If :p:`mapping_data` format size is smaller than
size of :p:`mapping_type`
:raise AssertionError: If :p:`field_data` stride doesn't fit into 16 bits
:raise AssertionError: If :p:`flags` contain
:ref:`SceneFieldFlags.OFFSET_ONLY`,
:ref:`SceneFieldFlags.NULL_TERMINATED_STRING` or values disallowed for
a particular :p:`name`
.. py:function:: magnum.trade.SceneFieldData.__init__(self, name: magnum.trade.SceneField, mapping_type: magnum.trade.SceneMappingType, mapping_data: corrade.containers.StridedArrayView1D, field_data: corrade.containers.StridedBitArrayView2D, flags: magnum.trade.SceneFieldFlags)
:raise AssertionError: If :p:`mapping_data` and first dimension of
:p:`field_data` don't have the same size
:raise AssertionError: If :ref:`SceneFieldType.BIT` is not valid for
:p:`name`
:raise AssertionError: If :p:`mapping_data` stride doesn't fit into 16 bits
:raise AssertionError: If :p:`mapping_data` format size is smaller than
size of :p:`mapping_type`
:raise AssertionError: If :p:`field_data` first dimension stride doesn't
fit into 16 bits
:raise AssertionError: If :p:`field_data` second dimension isn't contiguous
:raise AssertionError: If :p:`flags` contain
:ref:`SceneFieldFlags.OFFSET_ONLY`,
:ref:`SceneFieldFlags.NULL_TERMINATED_STRING` or values disallowed for
a particular :p:`name`
.. py:property:: magnum.trade.SceneFieldData.field_data
:raise NotImplementedError: If :ref:`field_type` is a half-float or string
type
A :ref:`containers.StridedArrayView2D <corrade.containers.StridedArrayView2D>`
or :ref:`containers.StridedBitArrayView2D <corrade.containers.StridedBitArrayView2D>`
is returned always, non-array attributes have the second dimension size
:py:`1`.
.. py:class:: magnum.trade.SceneData .. py:class:: magnum.trade.SceneData
:TODO: remove this line once m.css stops ignoring first caption on a page :TODO: remove this line once m.css stops ignoring first caption on a page

7
doc/python/pages/changelog.rst

@ -154,9 +154,10 @@ Changelog
- Exposed the whole interface of :ref:`trade.MaterialData` including typed - Exposed the whole interface of :ref:`trade.MaterialData` including typed
access to attribute data, together with access to attribute data, together with
:ref:`trade.AbstractImporter.material()` and related importer APIs :ref:`trade.AbstractImporter.material()` and related importer APIs
- Exposed the whole interface of :ref:`trade.SceneData` including typed - Exposed the whole interface of :ref:`trade.SceneData` and
access to mapping and field data, together with :ref:`trade.SceneFieldData` including typed access to mapping and field
:ref:`trade.AbstractImporter.scene()` and related importer APIs data, together with :ref:`trade.AbstractImporter.scene()` and related
importer APIs
- Exposed :ref:`Color3.red()` and other convenience constructors (see - Exposed :ref:`Color3.red()` and other convenience constructors (see
:gh:`mosra/magnum-bindings#12`) :gh:`mosra/magnum-bindings#12`)
- Exposed the :ref:`materialtools`, :ref:`scenetools` and :ref:`text` - Exposed the :ref:`materialtools`, :ref:`scenetools` and :ref:`text`

20
src/Magnum/Trade/PythonBindings.h

@ -31,6 +31,7 @@
#include "Magnum/Trade/Data.h" #include "Magnum/Trade/Data.h"
#include "Magnum/Trade/MaterialData.h" /* :( */ #include "Magnum/Trade/MaterialData.h" /* :( */
#include "Magnum/Trade/MeshData.h" /* :( */ #include "Magnum/Trade/MeshData.h" /* :( */
#include "Magnum/Trade/SceneData.h"
namespace Magnum { namespace Trade { namespace Magnum { namespace Trade {
@ -75,8 +76,27 @@ template<class T> PyDataHolder<T> pyDataHolder(T&& data, pybind11::object owner)
return PyDataHolder<T>{new T{std::move(data)}, std::move(owner)}; return PyDataHolder<T>{new T{std::move(data)}, std::move(owner)};
} }
/* Compared to PyDataHolder this stores two owner objects. Has to be a template
even though it's only ever used for a single type because
PYBIND11_DECLARE_HOLDER_TYPE() wants it to be so. */
template<class T> struct PySceneFieldDataHolder: std::unique_ptr<SceneFieldData> {
explicit PySceneFieldDataHolder(SceneFieldData* object): PySceneFieldDataHolder{object, pybind11::none{}, pybind11::none{}} {
/* Data without owners can only be empty */
CORRADE_INTERNAL_ASSERT(!object->mappingData().data() && !object->fieldData().data());
}
explicit PySceneFieldDataHolder(SceneFieldData* object, pybind11::object mappingOwner, pybind11::object fieldOwner): std::unique_ptr<T>{object}, mappingOwner{std::move(mappingOwner)}, fieldOwner{std::move(fieldOwner)} {}
pybind11::object mappingOwner, fieldOwner;
};
inline PySceneFieldDataHolder<SceneFieldData> pySceneFieldDataHolder(SceneFieldData&& data, pybind11::object mappingOwner, pybind11::object fieldOwner) {
return PySceneFieldDataHolder<SceneFieldData>{new SceneFieldData{std::move(data)}, std::move(mappingOwner), std::move(fieldOwner)};
}
}} }}
PYBIND11_DECLARE_HOLDER_TYPE(T, Magnum::Trade::PyDataHolder<T>) PYBIND11_DECLARE_HOLDER_TYPE(T, Magnum::Trade::PyDataHolder<T>)
PYBIND11_DECLARE_HOLDER_TYPE(T, Magnum::Trade::PySceneFieldDataHolder<T>)
#endif #endif

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

@ -1277,6 +1277,493 @@ class MeshData(unittest.TestCase):
with self.assertRaisesRegex(NotImplementedError, "access to VertexFormat.MATRIX2X2 is not implemented yet, sorry"): with self.assertRaisesRegex(NotImplementedError, "access to VertexFormat.MATRIX2X2 is not implemented yet, sorry"):
mesh.mutable_attribute(custom_attribute) mesh.mutable_attribute(custom_attribute)
class SceneFieldData(unittest.TestCase):
def test_init_1d(self):
a = array.array('Q', [3, 7, 166, 2872])
b = array.array('h', [2, -1, 37, -1])
a_refcount = sys.getrefcount(a)
b_refcount = sys.getrefcount(b)
c = trade.SceneFieldData(trade.SceneField.MESH_MATERIAL,
trade.SceneMappingType.UNSIGNED_LONG, a,
trade.SceneFieldType.SHORT, b,
flags=trade.SceneFieldFlags.MULTI_ENTRY|trade.SceneFieldFlags.ORDERED_MAPPING)
c_refcount = sys.getrefcount(c)
self.assertEqual(c.flags, trade.SceneFieldFlags.MULTI_ENTRY|trade.SceneFieldFlags.ORDERED_MAPPING)
self.assertEqual(c.name, trade.SceneField.MESH_MATERIAL)
self.assertEqual(c.size, 4)
self.assertEqual(c.mapping_type, trade.SceneMappingType.UNSIGNED_LONG)
self.assertEqual(c.field_type, trade.SceneFieldType.SHORT)
self.assertEqual(c.field_array_size, 0)
self.assertIs(c.mapping_owner, a)
self.assertIs(c.field_owner, b)
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
mapping_data = c.mapping_data
self.assertEqual(mapping_data.size, (4,))
self.assertEqual(mapping_data.stride, (8,))
self.assertEqual(mapping_data.format, 'Q')
self.assertIs(mapping_data.owner, a)
self.assertEqual(list(mapping_data), [3, 7, 166, 2872])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
field_data = c.field_data
self.assertEqual(field_data.size, (4, 1))
self.assertEqual(field_data.stride, (2, 2))
self.assertEqual(field_data.format, 'h')
self.assertIs(field_data.owner, b)
# Returns a 2D view always, transpose and take the first element to
# "flatten" it.
self.assertEqual(list(field_data.transposed(0, 1)[0]), [2, -1, 37, -1])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 2)
del c
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
del mapping_data
del field_data
self.assertEqual(sys.getrefcount(a), a_refcount)
self.assertEqual(sys.getrefcount(b), b_refcount)
def test_init_2d(self):
a = array.array('I', [0, 1])
b = array.array('f', [1.0, 0.0, 0.0,
0.0, -1.0, 0.0])
a_refcount = sys.getrefcount(a)
b_refcount = sys.getrefcount(b)
c = trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_INT, a,
trade.SceneFieldType.VECTOR3, containers.StridedArrayView1D(b).expanded(0, (2, 3)),
flags=trade.SceneFieldFlags.IMPLICIT_MAPPING)
c_refcount = sys.getrefcount(c)
self.assertEqual(c.flags, trade.SceneFieldFlags.IMPLICIT_MAPPING)
self.assertEqual(c.name, trade.SceneField.TRANSLATION)
self.assertEqual(c.size, 2)
self.assertEqual(c.mapping_type, trade.SceneMappingType.UNSIGNED_INT)
self.assertEqual(c.field_type, trade.SceneFieldType.VECTOR3)
self.assertEqual(c.field_array_size, 0)
self.assertIs(c.mapping_owner, a)
self.assertIs(c.field_owner, b)
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
mapping_data = c.mapping_data
self.assertEqual(mapping_data.size, (2,))
self.assertEqual(mapping_data.stride, (4,))
self.assertEqual(mapping_data.format, 'I')
self.assertIs(mapping_data.owner, a)
self.assertEqual(list(mapping_data), [0, 1])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
field_data = c.field_data
self.assertEqual(field_data.size, (2, 1))
self.assertEqual(field_data.stride, (12, 12))
self.assertEqual(field_data.format, '3f')
self.assertIs(field_data.owner, b)
# Returns a 2D view always, transpose and take the first element to
# "flatten" it.
self.assertEqual(list(field_data.transposed(0, 1)[0]), [(1.0, 0.0, 0.0), (0.0, -1.0, 0.0)])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 2)
del c
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
del mapping_data
del field_data
self.assertEqual(sys.getrefcount(a), a_refcount)
self.assertEqual(sys.getrefcount(b), b_refcount)
def test_init_1d_array(self):
a = array.array('B', [3, 7, 166, 72])
b = array.array('Q', [0x0000ffff66663333, 0x00009999aaaacccc, 0x0000bbbb22227777, 0x00001111eeee8888])
c = trade.SceneFieldData(trade.SceneField.CUSTOM(666),
trade.SceneMappingType.UNSIGNED_BYTE, a,
trade.SceneFieldType.UNSIGNED_SHORT, b,
field_array_size=3)
self.assertEqual(c.flags, trade.SceneFieldFlags.NONE)
self.assertEqual(c.name, trade.SceneField.CUSTOM(666))
self.assertEqual(c.size, 4)
self.assertEqual(c.mapping_type, trade.SceneMappingType.UNSIGNED_BYTE)
self.assertEqual(c.field_type, trade.SceneFieldType.UNSIGNED_SHORT)
self.assertEqual(c.field_array_size, 3)
mapping_data = c.mapping_data
self.assertEqual(mapping_data.size, (4,))
self.assertEqual(mapping_data.stride, (1,))
self.assertEqual(mapping_data.format, 'B')
self.assertEqual(list(mapping_data), [3, 7, 166, 72])
field_data = c.field_data
self.assertEqual(field_data.size, (4, 3))
self.assertEqual(field_data.stride, (8, 2))
self.assertEqual(field_data.format, 'H')
# Getting all first, second and third array elements. Assumes Little
# Endian.
self.assertEqual(list(field_data.transposed(0, 1)[0]), [0x3333, 0xcccc, 0x7777, 0x8888])
self.assertEqual(list(field_data.transposed(0, 1)[1]), [0x6666, 0xaaaa, 0x2222, 0xeeee])
self.assertEqual(list(field_data.transposed(0, 1)[2]), [0xffff, 0x9999, 0xbbbb, 0x1111])
def test_init_2d_array(self):
a = array.array('B', [3, 7, 166, 72])
b = array.array('H', [0x3333, 0x6666, 0xffff, 0xcccc, 0xaaaa, 0x9999, 0x7777, 0x2222, 0xbbbb, 0x8888, 0xeeee, 0x1111])
c = trade.SceneFieldData(trade.SceneField.CUSTOM(666),
trade.SceneMappingType.UNSIGNED_BYTE, a,
trade.SceneFieldType.UNSIGNED_SHORT, containers.StridedArrayView1D(b).expanded(0, (4, 3)),
field_array_size=3)
self.assertEqual(c.flags, trade.SceneFieldFlags.NONE)
self.assertEqual(c.name, trade.SceneField.CUSTOM(666))
self.assertEqual(c.size, 4)
self.assertEqual(c.mapping_type, trade.SceneMappingType.UNSIGNED_BYTE)
self.assertEqual(c.field_type, trade.SceneFieldType.UNSIGNED_SHORT)
self.assertEqual(c.field_array_size, 3)
mapping_data = c.mapping_data
self.assertEqual(mapping_data.size, (4,))
self.assertEqual(mapping_data.stride, (1,))
self.assertEqual(mapping_data.format, 'B')
self.assertEqual(list(mapping_data), [3, 7, 166, 72])
field_data = c.field_data
self.assertEqual(field_data.size, (4, 3))
self.assertEqual(field_data.stride, (6, 2))
self.assertEqual(field_data.format, 'H')
# Getting all first, second and third array elements. Assumes Little
# Endian.
self.assertEqual(list(field_data.transposed(0, 1)[0]), [0x3333, 0xcccc, 0x7777, 0x8888])
self.assertEqual(list(field_data.transposed(0, 1)[1]), [0x6666, 0xaaaa, 0x2222, 0xeeee])
self.assertEqual(list(field_data.transposed(0, 1)[2]), [0xffff, 0x9999, 0xbbbb, 0x1111])
def test_init_bit_1d(self):
a = array.array('H', [3, 7, 166, 2872])
b = array.array('b', [1, 0, 1, 0])
a_refcount = sys.getrefcount(a)
b_refcount = sys.getrefcount(b)
c = trade.SceneFieldData(trade.SceneField.CUSTOM(1337),
trade.SceneMappingType.UNSIGNED_SHORT, a,
containers.StridedArrayView1D(b).slice_bit(0),
flags=trade.SceneFieldFlags.MULTI_ENTRY|trade.SceneFieldFlags.ORDERED_MAPPING)
c_refcount = sys.getrefcount(c)
self.assertEqual(c.flags, trade.SceneFieldFlags.MULTI_ENTRY|trade.SceneFieldFlags.ORDERED_MAPPING)
self.assertEqual(c.name, trade.SceneField.CUSTOM(1337))
self.assertEqual(c.size, 4)
self.assertEqual(c.mapping_type, trade.SceneMappingType.UNSIGNED_SHORT)
self.assertEqual(c.field_type, trade.SceneFieldType.BIT)
self.assertEqual(c.field_array_size, 0)
self.assertIs(c.mapping_owner, a)
self.assertIs(c.field_owner, b)
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
mapping_data = c.mapping_data
self.assertEqual(mapping_data.size, (4,))
self.assertEqual(mapping_data.stride, (2,))
self.assertEqual(mapping_data.format, 'H')
self.assertIs(mapping_data.owner, a)
self.assertEqual(list(mapping_data), [3, 7, 166, 2872])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
field_data = c.field_data
self.assertEqual(field_data.size, (4, 1))
self.assertEqual(field_data.offset, 0)
self.assertEqual(field_data.stride, (8, 1))
self.assertIs(field_data.owner, b)
# Returns a 2D view always, transpose and take the first element to
# "flatten" it.
self.assertEqual(list(field_data.transposed(0, 1)[0]), [True, False, True, False])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 2)
del c
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
del mapping_data
del field_data
self.assertEqual(sys.getrefcount(a), a_refcount)
self.assertEqual(sys.getrefcount(b), b_refcount)
def test_init_bit_2d(self):
a = array.array('H', [3, 7, 166, 2872])
b = containers.BitArray.value_init(4*2)
b[0] = True
b[1] = True
b[4] = True
b[7] = True
a_refcount = sys.getrefcount(a)
b_refcount = sys.getrefcount(b)
c = trade.SceneFieldData(trade.SceneField.CUSTOM(1337),
trade.SceneMappingType.UNSIGNED_SHORT, a,
containers.StridedBitArrayView1D(b).expanded(0, (4, 2)),
flags=trade.SceneFieldFlags.MULTI_ENTRY|trade.SceneFieldFlags.ORDERED_MAPPING)
c_refcount = sys.getrefcount(c)
self.assertEqual(c.flags, trade.SceneFieldFlags.MULTI_ENTRY|trade.SceneFieldFlags.ORDERED_MAPPING)
self.assertEqual(c.name, trade.SceneField.CUSTOM(1337))
self.assertEqual(c.size, 4)
self.assertEqual(c.mapping_type, trade.SceneMappingType.UNSIGNED_SHORT)
self.assertEqual(c.field_type, trade.SceneFieldType.BIT)
self.assertEqual(c.field_array_size, 2)
self.assertIs(c.mapping_owner, a)
self.assertIs(c.field_owner, b)
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
mapping_data = c.mapping_data
self.assertEqual(mapping_data.size, (4,))
self.assertEqual(mapping_data.stride, (2,))
self.assertEqual(mapping_data.format, 'H')
self.assertIs(mapping_data.owner, a)
self.assertEqual(list(mapping_data), [3, 7, 166, 2872])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
field_data = c.field_data
self.assertEqual(field_data.size, (4, 2))
self.assertEqual(field_data.offset, 0)
self.assertEqual(field_data.stride, (2, 1))
self.assertIs(field_data.owner, b)
# Getting all first and second array elements
self.assertEqual(list(field_data.transposed(0, 1)[0]), [True, False, True, False])
self.assertEqual(list(field_data.transposed(0, 1)[1]), [True, False, False, True])
# The data reference the original array, not the SceneFieldData
# instance
self.assertEqual(sys.getrefcount(c), c_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
self.assertEqual(sys.getrefcount(b), b_refcount + 2)
del c
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
self.assertEqual(sys.getrefcount(b), b_refcount + 1)
del mapping_data
del field_data
self.assertEqual(sys.getrefcount(a), a_refcount)
self.assertEqual(sys.getrefcount(b), b_refcount)
def test_init_1d_invalid(self):
data = array.array('Q', [0, 0, 0])
data_byte = array.array('B', [0, 0, 0])
# To check that messages properly handle the case of no format string
data_byte_no_format = containers.ArrayView(data_byte)
self.assertEqual(containers.StridedArrayView1D(data_byte_no_format).format, None)
with self.assertRaisesRegex(AssertionError, "expected SceneField.TRANSLATION mapping and field view to have the same size but got 2 and 3"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data[:2],
trade.SceneFieldType.VECTOR2, data)
with self.assertRaisesRegex(AssertionError, "SceneFieldType.UNSIGNED_SHORT is not a valid type for SceneField.TRANSLATION"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_LONG, data,
trade.SceneFieldType.UNSIGNED_SHORT, data)
with self.assertRaisesRegex(AssertionError, "data type B has 1 bytes but SceneMappingType.UNSIGNED_SHORT expects at least 2"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data_byte,
trade.SceneFieldType.VECTOR2, data)
with self.assertRaisesRegex(AssertionError, "data type B has 1 bytes but SceneMappingType.UNSIGNED_SHORT expects at least 2"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data_byte_no_format,
trade.SceneFieldType.VECTOR2, data)
with self.assertRaisesRegex(AssertionError, "data type Q has 8 bytes but SceneFieldType.VECTOR3 expects at least 12"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data,
trade.SceneFieldType.VECTOR3, data)
with self.assertRaisesRegex(AssertionError, "data type Q has 8 bytes but array of 3 SceneFieldType.FLOAT expects at least 12"):
trade.SceneFieldData(trade.SceneField.CUSTOM(76),
trade.SceneMappingType.UNSIGNED_SHORT, data,
trade.SceneFieldType.FLOAT, data, field_array_size=3)
with self.assertRaisesRegex(AssertionError, "data type B has 1 bytes but SceneFieldType.SHORT expects at least 2"):
trade.SceneFieldData(trade.SceneField.MESH_MATERIAL,
trade.SceneMappingType.UNSIGNED_INT, data,
trade.SceneFieldType.SHORT, data_byte_no_format)
with self.assertRaisesRegex(AssertionError, "expected mapping view stride to fit into 16 bits but got 32768"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, containers.StridedArrayView1D(data)[::4096],
trade.SceneFieldType.VECTOR2, data[:1])
with self.assertRaisesRegex(AssertionError, "expected mapping view stride to fit into 16 bits but got -32769"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_BYTE, containers.StridedArrayView1D(data_byte)[::32769].flipped(0),
trade.SceneFieldType.VECTOR2, data[:1])
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got 32768"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data[:1],
trade.SceneFieldType.VECTOR2, containers.StridedArrayView1D(data)[::4096])
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got -32769"):
trade.SceneFieldData(trade.SceneField.CAMERA,
trade.SceneMappingType.UNSIGNED_SHORT, data[:1],
trade.SceneFieldType.UNSIGNED_BYTE, containers.StridedArrayView1D(data_byte)[::32769].flipped(0))
with self.assertRaisesRegex(AssertionError, "SceneField.MESH can't be an array field"):
trade.SceneFieldData(trade.SceneField.MESH,
trade.SceneMappingType.UNSIGNED_SHORT, data,
trade.SceneFieldType.UNSIGNED_SHORT, data, field_array_size=3)
with self.assertRaisesRegex(AssertionError, "can't pass SceneFieldFlags.MULTI_ENTRY for a SceneField.TRANSLATION view of SceneFieldType.VECTOR2"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_LONG, data,
trade.SceneFieldType.VECTOR2, data, flags=trade.SceneFieldFlags.MULTI_ENTRY)
with self.assertRaisesRegex(AssertionError, "use a string constructor for SceneFieldType.STRING_OFFSET16"):
trade.SceneFieldData(trade.SceneField.CUSTOM(333),
trade.SceneMappingType.UNSIGNED_LONG, data,
trade.SceneFieldType.STRING_OFFSET16, data)
with self.assertRaisesRegex(AssertionError, "use a bit constructor for SceneFieldType.BIT"):
trade.SceneFieldData(trade.SceneField.CUSTOM(333),
trade.SceneMappingType.UNSIGNED_LONG, data,
trade.SceneFieldType.BIT, data)
def test_init_2d_invalid(self):
data_1d = array.array('Q', [0, 0, 0])
data = containers.StridedArrayView1D(array.array('I', [0, 0, 0, 0, 0, 0])).expanded(0, (3, 2))
data_byte = containers.StridedArrayView1D(array.array('B', [0, 0, 0])).expanded(0, (3, 1))
# To check that messages properly handle the case of no format string
data_byte_no_format = containers.StridedArrayView1D(containers.ArrayView(array.array('B', [0, 0, 0]))).expanded(0, (3, 1))
self.assertEqual(data_byte_no_format.format, None)
with self.assertRaisesRegex(AssertionError, "expected SceneField.TRANSLATION mapping and field view to have the same size but got 2 and 3"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data_1d[:2],
trade.SceneFieldType.VECTOR2, data)
with self.assertRaisesRegex(AssertionError, "SceneFieldType.UNSIGNED_SHORT is not a valid type for SceneField.TRANSLATION"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_LONG, data_1d,
trade.SceneFieldType.UNSIGNED_SHORT, data)
# SceneMappingType size checks are shared with the 1D variant, not
# testing here again
with self.assertRaisesRegex(AssertionError, "2-item second dimension of data type I has 8 bytes but SceneFieldType.VECTOR3 expects at least 12"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data_1d,
trade.SceneFieldType.VECTOR3, data)
with self.assertRaisesRegex(AssertionError, "2-item second dimension of data type I has 8 bytes but array of 3 SceneFieldType.FLOAT expects at least 12"):
trade.SceneFieldData(trade.SceneField.CUSTOM(76),
trade.SceneMappingType.UNSIGNED_SHORT, data_1d,
trade.SceneFieldType.FLOAT, data, field_array_size=3)
with self.assertRaisesRegex(AssertionError, "1-item second dimension of data type B has 1 bytes but SceneFieldType.SHORT expects at least 2"):
trade.SceneFieldData(trade.SceneField.MESH_MATERIAL,
trade.SceneMappingType.UNSIGNED_INT, data_1d,
trade.SceneFieldType.SHORT, data_byte_no_format)
with self.assertRaisesRegex(AssertionError, "second field view dimension is not contiguous"):
trade.SceneFieldData(trade.SceneField.MESH,
trade.SceneMappingType.UNSIGNED_SHORT, data_1d,
trade.SceneFieldType.UNSIGNED_SHORT, data[::1,::2])
# SceneMappingType stride checks are shared with the 1D variant, not
# testing here again
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got 32768"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_SHORT, data_1d[:1],
trade.SceneFieldType.VECTOR2, data[::4096])
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got -32769"):
trade.SceneFieldData(trade.SceneField.CAMERA,
trade.SceneMappingType.UNSIGNED_SHORT, data_1d[:1],
trade.SceneFieldType.UNSIGNED_BYTE, data_byte[::32769].flipped(0))
with self.assertRaisesRegex(AssertionError, "SceneField.MESH can't be an array field"):
trade.SceneFieldData(trade.SceneField.MESH,
trade.SceneMappingType.UNSIGNED_SHORT, data_1d,
trade.SceneFieldType.UNSIGNED_SHORT, data, field_array_size=3)
with self.assertRaisesRegex(AssertionError, "can't pass SceneFieldFlags.MULTI_ENTRY for a SceneField.TRANSLATION view of SceneFieldType.VECTOR2"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_LONG, data_1d,
trade.SceneFieldType.VECTOR2, data, flags=trade.SceneFieldFlags.MULTI_ENTRY)
with self.assertRaisesRegex(AssertionError, "use a string constructor for SceneFieldType.STRING_OFFSET16"):
trade.SceneFieldData(trade.SceneField.CUSTOM(333),
trade.SceneMappingType.UNSIGNED_LONG, data_1d,
trade.SceneFieldType.STRING_OFFSET16, data)
with self.assertRaisesRegex(AssertionError, "use a bit constructor for SceneFieldType.BIT"):
trade.SceneFieldData(trade.SceneField.CUSTOM(333),
trade.SceneMappingType.UNSIGNED_LONG, data_1d,
trade.SceneFieldType.BIT, data)
def test_init_bit_1d_invalid(self):
data = array.array('Q', [0, 0, 0])
data_bits = containers.BitArray.value_init(3)
with self.assertRaisesRegex(AssertionError, "expected SceneField.CUSTOM\\(33\\) mapping and field view to have the same size but got 2 and 3"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_SHORT, data[:2],
data_bits)
with self.assertRaisesRegex(AssertionError, "SceneFieldType.BIT is not a valid type for SceneField.TRANSLATION"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_LONG, data,
data_bits)
# SceneMappingType size and stride checks are shared with the non-bit
# variant, not testing here again
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got 32768"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_SHORT, data[:1],
containers.StridedBitArrayView1D(data_bits)[::32768])
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got -32769"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_SHORT, data[:1],
containers.StridedBitArrayView1D(data_bits)[::32769].flipped(0))
with self.assertRaisesRegex(AssertionError, "can't pass SceneFieldFlags.OFFSET_ONLY for a SceneField.CUSTOM\\(33\\) view of SceneFieldType.BIT"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_LONG, data,
data_bits, flags=trade.SceneFieldFlags.OFFSET_ONLY)
def test_init_bit_2d_invalid(self):
data = array.array('Q', [0, 0, 0])
data_bits = containers.StridedBitArrayView1D(containers.BitArray.value_init(3*2)).expanded(0, (3, 2))
data_bits_one_element = containers.StridedBitArrayView1D(containers.BitArray.value_init(3)).expanded(0, (3, 1))
with self.assertRaisesRegex(AssertionError, "expected SceneField.CUSTOM\\(33\\) mapping and field view to have the same size but got 2 and 3"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_SHORT, data[:2],
data_bits)
with self.assertRaisesRegex(AssertionError, "SceneFieldType.BIT is not a valid type for SceneField.TRANSLATION"):
trade.SceneFieldData(trade.SceneField.TRANSLATION,
trade.SceneMappingType.UNSIGNED_LONG, data,
data_bits)
# SceneMappingType size and stride checks are shared with the non-bit
# variant, not testing here again
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got 32768"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_SHORT, data[:1],
data_bits[::16384])
with self.assertRaisesRegex(AssertionError, "expected field view stride to fit into 16 bits but got -32769"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_SHORT, data[:1],
data_bits_one_element[::32769].flipped(0))
with self.assertRaisesRegex(AssertionError, "can't pass SceneFieldFlags.OFFSET_ONLY for a SceneField.CUSTOM\\(33\\) view of SceneFieldType.BIT"):
trade.SceneFieldData(trade.SceneField.CUSTOM(33),
trade.SceneMappingType.UNSIGNED_LONG, data,
data_bits,
flags=trade.SceneFieldFlags.OFFSET_ONLY)
class SceneData(unittest.TestCase): class SceneData(unittest.TestCase):
def test_custom_field(self): def test_custom_field(self):
# Creating a custom attribute # Creating a custom attribute

145
src/python/magnum/trade.cpp

@ -739,6 +739,51 @@ 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()}, std::move(owner)); return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<1, T>{data.template transposed<0, 1>()[0], formatStringGetitemSetitem.first(), itemsize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, std::move(owner));
} }
void sceneFieldDataConstructorChecks(const Trade::SceneField name, const Trade::SceneMappingType mappingType, const Containers::PyStridedArrayView<1, const char>& mappingData, const Trade::SceneFieldType fieldType, const std::size_t fieldDataSize, const std::ptrdiff_t fieldDataStride, const UnsignedShort fieldArraySize, const Trade::SceneFieldFlag flags) {
if(mappingData.size() != fieldDataSize) {
PyErr_Format(PyExc_AssertionError, "expected %S mapping and field view to have the same size but got %zu and %zu", py::cast(name).ptr(), mappingData.size(), fieldDataSize);
throw py::error_already_set{};
}
const UnsignedInt mappingTypeSize = Trade::sceneMappingTypeSize(mappingType);
if(mappingData.itemsize < mappingTypeSize) {
const char* const dataFormat = mappingData.format ? mappingData.format.data() : "B";
PyErr_Format(PyExc_AssertionError, "data type %s has %zu bytes but %S expects at least %u", dataFormat, mappingData.itemsize, py::cast(mappingType).ptr(), mappingTypeSize);
throw py::error_already_set{};
}
if(!Trade::Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType)) {
PyErr_Format(PyExc_AssertionError, "%S is not a valid type for %S", py::cast(fieldType).ptr(), py::cast(name).ptr());
throw py::error_already_set{};
}
if(mappingData.stride() < -32768 || mappingData.stride() > 32767) {
PyErr_Format(PyExc_AssertionError, "expected mapping view stride to fit into 16 bits but got %zi", mappingData.stride());
throw py::error_already_set{};
}
if(fieldDataStride < -32768 || fieldDataStride > 32767) {
PyErr_Format(PyExc_AssertionError, "expected field view stride to fit into 16 bits but got %zi", fieldDataStride);
throw py::error_already_set{};
}
if(fieldArraySize && !Trade::Implementation::isSceneFieldArrayAllowed(name)) {
PyErr_Format(PyExc_AssertionError, "%S can't be an array field", py::cast(name).ptr());
throw py::error_already_set{};
}
if(const Trade::SceneFieldFlags disallowedFlags = flags & (Trade::SceneFieldFlag::OffsetOnly|Trade::SceneFieldFlag::NullTerminatedString|Trade::Implementation::disallowedSceneFieldFlagsFor(name))) {
PyErr_Format(PyExc_AssertionError, "can't pass %S for a %S view of %S", py::cast(Trade::SceneFieldFlag(Containers::enumCastUnderlyingType(disallowedFlags))).ptr(), py::cast(name).ptr(), py::cast(fieldType).ptr());
throw py::error_already_set{};
}
}
void sceneFieldDataConstructorChecks(const Trade::SceneField name, const Trade::SceneMappingType mappingType, const Containers::PyStridedArrayView<1, const char>& mappingData, const Trade::SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const UnsignedShort fieldArraySize, const Trade::SceneFieldFlag flags) {
sceneFieldDataConstructorChecks(name, mappingType, mappingData, fieldType, fieldData.size(), fieldData.stride(), fieldArraySize, flags);
if(Trade::Implementation::isSceneFieldTypeString(fieldType)) {
PyErr_Format(PyExc_AssertionError, "use a string constructor for %S", py::cast(fieldType).ptr());
throw py::error_already_set{};
}
if(fieldType == Trade::SceneFieldType::Bit) {
PyErr_Format(PyExc_AssertionError, "use a bit constructor for %S", py::cast(fieldType).ptr());
throw py::error_already_set{};
}
}
Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> accessorsForSceneMappingType(const Trade::SceneMappingType type) { Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> accessorsForSceneMappingType(const Trade::SceneMappingType type) {
switch(type) { switch(type) {
#define _c(type) \ #define _c(type) \
@ -2538,6 +2583,106 @@ void trade(py::module_& m) {
.value("NONE", Trade::SceneFieldFlag{}); .value("NONE", Trade::SceneFieldFlag{});
corrade::enumOperators(sceneFieldFlag); corrade::enumOperators(sceneFieldFlag);
py::class_<Trade::SceneFieldData, Trade::PySceneFieldDataHolder<Trade::SceneFieldData>>{m, "SceneFieldData", "Scene field data"}
.def(py::init([](Trade::SceneField name, Trade::SceneMappingType mappingType, const Containers::PyStridedArrayView<1, const char>& mappingData, Trade::SceneFieldType fieldType, const Containers::PyStridedArrayView<1, const char>& fieldData, UnsignedShort fieldArraySize, Trade::SceneFieldFlag flags) {
sceneFieldDataConstructorChecks(name, mappingType, mappingData, fieldType, fieldData, fieldArraySize, flags);
const UnsignedInt fieldTypeSize = Trade::sceneFieldTypeSize(fieldType)*(fieldArraySize ? fieldArraySize : 1);
if(fieldData.itemsize < fieldTypeSize) {
const char* const dataFormat = fieldData.format ? fieldData.format.data() : "B";
if(fieldArraySize)
PyErr_Format(PyExc_AssertionError, "data type %s has %zu bytes but array of %u %S expects at least %u", dataFormat, fieldData.itemsize, UnsignedInt(fieldArraySize), py::cast(fieldType).ptr(), fieldTypeSize);
else
PyErr_Format(PyExc_AssertionError, "data type %s has %zu bytes but %S expects at least %u", dataFormat, fieldData.itemsize, py::cast(fieldType).ptr(), fieldTypeSize);
throw py::error_already_set{};
}
return Trade::pySceneFieldDataHolder(Trade::SceneFieldData{name, mappingType, Containers::StridedArrayView1D<const void>{mappingData}, fieldType, Containers::StridedArrayView1D<const void>{fieldData}, fieldArraySize, flags}, pyObjectHolderFor<Containers::PyArrayViewHolder>(mappingData).owner, pyObjectHolderFor<Containers::PyArrayViewHolder>(fieldData).owner);
}), "Construct from a 1D field view", py::arg("name"), py::arg("mapping_type"), py::arg("mapping_data"), py::arg("field_type"), py::arg("field_data"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("field_array_size") = 0, py::arg("flags") = Trade::SceneFieldFlag{})
.def(py::init([](Trade::SceneField name, Trade::SceneMappingType mappingType, const Containers::PyStridedArrayView<1, const char>& mappingData, Trade::SceneFieldType fieldType, const Containers::PyStridedArrayView<2, const char>& fieldData, UnsignedShort fieldArraySize, Trade::SceneFieldFlag flags) {
if(fieldData.itemsize != std::size_t(fieldData.stride()[1])) {
PyErr_SetString(PyExc_AssertionError, "second field view dimension is not contiguous");
throw py::error_already_set{};
}
const Containers::StridedArrayView1D<const void> fieldData1D = fieldData.transposed<0, 1>()[0];
sceneFieldDataConstructorChecks(name, mappingType, mappingData, fieldType, fieldData1D, fieldArraySize, flags);
const std::size_t secondDimensionSize = fieldData.itemsize*fieldData.size()[1];
const UnsignedInt fieldTypeSize = Trade::sceneFieldTypeSize(fieldType)*(fieldArraySize ? fieldArraySize : 1);
if(secondDimensionSize < fieldTypeSize) {
const char* const dataFormat = fieldData.format ? fieldData.format.data() : "B";
if(fieldArraySize)
PyErr_Format(PyExc_AssertionError, "%zu-item second dimension of data type %s has %zu bytes but array of %u %S expects at least %u", fieldData.size()[1], dataFormat, secondDimensionSize, UnsignedInt(fieldArraySize), py::cast(fieldType).ptr(), fieldTypeSize);
else
PyErr_Format(PyExc_AssertionError, "%zu-item second dimension of data type %s has %zu bytes but %S expects at least %u", fieldData.size()[1], dataFormat, secondDimensionSize, py::cast(fieldType).ptr(), fieldTypeSize);
throw py::error_already_set{};
}
return Trade::pySceneFieldDataHolder(Trade::SceneFieldData{name, mappingType, Containers::StridedArrayView1D<const void>{mappingData}, fieldType, fieldData1D, fieldArraySize, flags}, pyObjectHolderFor<Containers::PyArrayViewHolder>(mappingData).owner, pyObjectHolderFor<Containers::PyArrayViewHolder>(fieldData).owner);
}), "Construct from a 2D field view", py::arg("name"), py::arg("mapping_type"), py::arg("mapping_data"), py::arg("field_type"), py::arg("field_data"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("field_array_size") = 0, py::arg("flags") = Trade::SceneFieldFlag{})
.def(py::init([](Trade::SceneField name, Trade::SceneMappingType mappingType, const Containers::PyStridedArrayView<1, const char>& mappingData, const Containers::StridedBitArrayView1D& fieldData, Trade::SceneFieldFlag flags) {
sceneFieldDataConstructorChecks(name, mappingType, mappingData, Trade::SceneFieldType::Bit, fieldData.size(), fieldData.stride(), 0, flags);
return Trade::pySceneFieldDataHolder(Trade::SceneFieldData{name, mappingType, Containers::StridedArrayView1D<const void>{mappingData}, fieldData, flags}, pyObjectHolderFor<Containers::PyArrayViewHolder>(mappingData).owner, pyObjectHolderFor<Containers::PyArrayViewHolder>(fieldData).owner);
}), "Construct a bit field", py::arg("name"), py::arg("mapping_type"), py::arg("mapping_data"), py::arg("field_data"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("flags") = Trade::SceneFieldFlag{})
.def(py::init([](Trade::SceneField name, Trade::SceneMappingType mappingType, const Containers::PyStridedArrayView<1, const char>& mappingData, const Containers::StridedBitArrayView2D& fieldData, Trade::SceneFieldFlag flags) {
if(fieldData.stride()[1] != 1) {
PyErr_SetString(PyExc_AssertionError, "second field view dimension is not contiguous");
throw py::error_already_set{};
}
sceneFieldDataConstructorChecks(name, mappingType, mappingData, Trade::SceneFieldType::Bit, fieldData.size()[0], fieldData.stride()[0], fieldData.size()[1], flags);
return Trade::pySceneFieldDataHolder(Trade::SceneFieldData{name, mappingType, Containers::StridedArrayView1D<const void>{mappingData}, fieldData, flags}, pyObjectHolderFor<Containers::PyArrayViewHolder>(mappingData).owner, pyObjectHolderFor<Containers::PyArrayViewHolder>(fieldData).owner);
}), "Construct an array bit field", py::arg("name"), py::arg("mapping_type"), py::arg("mapping_data"), py::arg("field_data"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("flags") = Trade::SceneFieldFlag{})
.def_property_readonly("flags", [](const Trade::SceneFieldData& self) {
return Trade::SceneFieldFlag(Containers::enumCastUnderlyingType(self.flags()));
}, "Field flags")
.def_property_readonly("name", &Trade::SceneFieldData::name, "Field name")
.def_property_readonly("size", &Trade::SceneFieldData::size, "Number of entries")
.def_property_readonly("mapping_type", &Trade::SceneFieldData::mappingType, "Object mapping type")
.def_property_readonly("mapping_data", [](const Trade::SceneFieldData& self) {
return sceneMappingView(self.mappingType(),
/* The sceneMappingView() helper needs a 2D array with the
second dimension being element bytes, but information about
the type and its size is subsequently discarded so we don't
need any extra logic here. */
Containers::arrayCast<2, const char>(self.mappingData(), 1),
pyObjectHolderFor<Trade::PySceneFieldDataHolder>(self).mappingOwner);
}, "Object mapping data")
.def_property_readonly("field_type", &Trade::SceneFieldData::fieldType, "Field type")
.def_property_readonly("field_array_size", &Trade::SceneFieldData::fieldArraySize, "Field array size")
.def_property_readonly("field_data", [](const Trade::SceneFieldData& self) {
/** @todo annotate the return type properly in the docs */
if(self.fieldType() == Trade::SceneFieldType::Bit)
return pyCastButNotShitty(Containers::pyArrayViewHolder(self.fieldBitData(), pyObjectHolderFor<Trade::PySceneFieldDataHolder>(self).fieldOwner));
/** @todo handle strings, so far they can't even be constructed so
this isn't needed */
const Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> formatStringGetitemSetitem = accessorsForSceneFieldType(self.fieldType());
if(!formatStringGetitemSetitem.first()) {
PyErr_Format(PyExc_NotImplementedError, "access to %S is not implemented yet, sorry", py::cast(self.fieldType()).ptr());
throw py::error_already_set{};
}
const std::size_t fieldTypeSize = Trade::sceneFieldTypeSize(self.fieldType());
return pyCastButNotShitty(Containers::pyArrayViewHolder(Containers::PyStridedArrayView<2, const char>{Containers::arrayCast<2, const char>(self.fieldData(), fieldTypeSize*(self.fieldArraySize() ? self.fieldArraySize() : 1)).every({1, fieldTypeSize}), formatStringGetitemSetitem.first(), fieldTypeSize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, pyObjectHolderFor<Trade::PySceneFieldDataHolder>(self).fieldOwner));
}, "Field data")
.def_property_readonly("mapping_owner", [](const Trade::SceneFieldData& self) {
return pyObjectHolderFor<Trade::PySceneFieldDataHolder>(self).mappingOwner;
}, "Mapping memory owner")
.def_property_readonly("field_owner", [](const Trade::SceneFieldData& self) {
return pyObjectHolderFor<Trade::PySceneFieldDataHolder>(self).fieldOwner;
}, "Field memory owner");
py::class_<Trade::SceneData, Trade::PyDataHolder<Trade::SceneData>>{m, "SceneData", "Scene data"} py::class_<Trade::SceneData, Trade::PyDataHolder<Trade::SceneData>>{m, "SceneData", "Scene data"}
.def_property_readonly("data_flags", [](const Trade::SceneData& self) { .def_property_readonly("data_flags", [](const Trade::SceneData& self) {
return Trade::DataFlag(Containers::enumCastUnderlyingType(self.dataFlags())); return Trade::DataFlag(Containers::enumCastUnderlyingType(self.dataFlags()));

Loading…
Cancel
Save