1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
# SPDX-License-Identifier: Apache-2.0
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_prop_array.py -- --verbose
import bpy
from bpy.props import (
BoolVectorProperty,
FloatVectorProperty,
IntVectorProperty,
)
import unittest
import numpy as np
id_inst = bpy.context.scene
id_type = bpy.types.Scene
# -----------------------------------------------------------------------------
# Utility Functions
def seq_items_xform(data, xform_fn):
"""
Recursively expand items using `xform_fn`.
"""
if hasattr(data, "__len__"):
return tuple(seq_items_xform(v, xform_fn) for v in data)
return xform_fn(data)
def seq_items_as_tuple(data):
"""
Return nested sequences as a nested tuple.
Useful when comparing different kinds of nested sequences.
"""
return seq_items_xform(data, lambda v: v)
def seq_items_as_dims(data):
"""
Nested length calculation, extracting the length from each sequence.
Where a 4x4 matrix returns ``(4, 4)`` for example.
"""
return ((len(data),) + seq_items_as_dims(data[0])) if hasattr(data, "__len__") else ()
# -----------------------------------------------------------------------------
# Tests
class TestPropArray(unittest.TestCase):
def setUp(self):
id_type.test_array_f = FloatVectorProperty(size=10)
id_type.test_array_i = IntVectorProperty(size=10)
scene = bpy.context.scene
self.array_f = scene.test_array_f
self.array_i = scene.test_array_i
def tearDown(self):
del id_type.test_array_f
del id_type.test_array_i
def test_foreach_getset_i(self):
with self.assertRaises(TypeError):
self.array_i.foreach_set(range(5))
self.array_i.foreach_set(range(5, 15))
with self.assertRaises(TypeError):
self.array_i.foreach_set(np.arange(5, dtype=np.int32))
with self.assertRaises(TypeError):
self.array_i.foreach_set(np.arange(10, dtype=np.int64))
with self.assertRaises(TypeError):
self.array_i.foreach_get(np.arange(10, dtype=np.float32))
a = np.arange(10, dtype=np.int32)
self.array_i.foreach_set(a)
with self.assertRaises(TypeError):
self.array_i.foreach_set(a[:5])
for v1, v2 in zip(a, self.array_i[:]):
self.assertEqual(v1, v2)
b = np.empty(10, dtype=np.int32)
self.array_i.foreach_get(b)
for v1, v2 in zip(a, b):
self.assertEqual(v1, v2)
b = [None] * 10
self.array_f.foreach_get(b)
for v1, v2 in zip(a, b):
self.assertEqual(v1, v2)
def test_foreach_getset_f(self):
with self.assertRaises(TypeError):
self.array_i.foreach_set(range(5))
self.array_f.foreach_set(range(5, 15))
with self.assertRaises(TypeError):
self.array_f.foreach_set(np.arange(5, dtype=np.float32))
with self.assertRaises(TypeError):
self.array_f.foreach_set(np.arange(10, dtype=np.int32))
with self.assertRaises(TypeError):
self.array_f.foreach_get(np.arange(10, dtype=np.float64))
a = np.arange(10, dtype=np.float32)
self.array_f.foreach_set(a)
for v1, v2 in zip(a, self.array_f[:]):
self.assertEqual(v1, v2)
b = np.empty(10, dtype=np.float32)
self.array_f.foreach_get(b)
for v1, v2 in zip(a, b):
self.assertEqual(v1, v2)
b = [None] * 10
self.array_f.foreach_get(b)
for v1, v2 in zip(a, b):
self.assertEqual(v1, v2)
class TestPropArrayMultiDimensional(unittest.TestCase):
def setUp(self):
self._initial_dir = set(dir(id_type))
def tearDown(self):
for member in (set(dir(id_type)) - self._initial_dir):
delattr(id_type, member)
def test_defaults(self):
# The data is in int format, converted into float & bool to avoid duplication.
default_data = (
# 1D.
(1,),
(1, 2),
(1, 2, 3),
(1, 2, 3, 4),
# 2D.
((1,),),
((1,), (11,)),
((1, 2), (11, 22)),
((1, 2, 3), (11, 22, 33)),
((1, 2, 3, 4), (11, 22, 33, 44)),
# 3D.
(((1,),),),
((1,), (11,), (111,)),
((1, 2), (11, 22), (111, 222),),
((1, 2, 3), (11, 22, 33), (111, 222, 333)),
((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444)),
)
for data in default_data:
for (vector_prop_fn, xform_fn) in (
(BoolVectorProperty, lambda v: bool(v % 2)),
(FloatVectorProperty, lambda v: float(v)),
(IntVectorProperty, lambda v: v),
):
data_native = seq_items_xform(data, xform_fn)
size = seq_items_as_dims(data)
id_type.temp = vector_prop_fn(size=size, default=data_native)
data_as_tuple = seq_items_as_tuple(id_inst.temp)
self.assertEqual(data_as_tuple, data_native)
del id_type.temp
def test_matrix(self):
data = ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444), (1111, 2222, 3333, 4444),)
data_native = seq_items_xform(data, lambda v: float(v))
id_type.temp = FloatVectorProperty(size=(4, 4), subtype='MATRIX', default=data_native)
data_as_tuple = seq_items_as_tuple(id_inst.temp)
self.assertEqual(data_as_tuple, data_native)
del id_type.temp
def test_matrix_with_callbacks(self):
# """
# Internally matrices have rows/columns swapped,
# This test ensures this is being done properly.
# """
data = ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444), (1111, 2222, 3333, 4444),)
data_native = seq_items_xform(data, lambda v: float(v))
local_data = {"array": data}
def get_fn(id_arg):
return local_data["array"]
def set_fn(id_arg, value):
local_data["array"] = value
id_type.temp = FloatVectorProperty(size=(4, 4), subtype='MATRIX', get=get_fn, set=set_fn)
id_inst.temp = data_native
data_as_tuple = seq_items_as_tuple(id_inst.temp)
self.assertEqual(data_as_tuple, data_native)
del id_type.temp
if __name__ == '__main__':
import sys
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
unittest.main()
|