diff --git a/release/test/rna_array.py b/release/test/rna_array.py new file mode 100644 index 00000000000..9b7c65ff017 --- /dev/null +++ b/release/test/rna_array.py @@ -0,0 +1,279 @@ +import unittest +import random + +test= bpy.data.test + +# farr - 1-dimensional array of float +# fdarr - dynamic 1-dimensional array of float +# fmarr - 3-dimensional ([3][4][5]) array of float +# fdmarr - dynamic 3-dimensional (ditto size) array of float + +# same as above for other types except that the first letter is "i" for int and "b" for bool + +class TestArray(unittest.TestCase): + # test that assignment works by: assign -> test value + # - rvalue = list of float + # - rvalue = list of numbers + # test.object + # bpy.data.test.farr[3], iarr[3], barr[...], fmarr, imarr, bmarr + + def setUp(self): + test.farr= (1.0, 2.0, 3.0) + test.iarr= (7, 8, 9) + test.barr= (False, True, False) + + # test access + # test slice access, negative indices + def test_access(self): + rvals= ([1.0, 2.0, 3.0], [7, 8, 9], [False, True, False]) + for arr, rval in zip((test.farr, test.iarr, test.barr), rvals): + self.assertEqual(prop_to_list(arr), rval) + self.assertEqual(arr[0:3], rval) + self.assertEqual(arr[1:2], rval[1:2]) + self.assertEqual(arr[-1], arr[2]) + self.assertEqual(arr[-2], arr[1]) + self.assertEqual(arr[-3], arr[0]) + + # fail when index out of bounds + def test_access_fail(self): + for arr in (test.farr, test.iarr, test.barr): + self.assertRaises(IndexError, lambda : arr[4]) + + # test assignment of a whole array + def test_assign_array(self): + # should accept int as float + test.farr= (1, 2, 3) + + # fail when: unexpected no. of items, invalid item type + def test_assign_array_fail(self): + def assign_empty_list(arr): + setattr(test, arr, ()) + + for arr in ("farr", "iarr", "barr"): + self.assertRaises(ValueError, assign_empty_list, arr) + + def assign_invalid_float(): + test.farr= (1.0, 2.0, "3.0") + + def assign_invalid_int(): + test.iarr= ("1", 2, 3) + + def assign_invalid_bool(): + test.barr= (True, 0.123, False) + + for func in [assign_invalid_float, assign_invalid_int, assign_invalid_bool]: + self.assertRaises(TypeError, func) + + # shouldn't accept float as int + def assign_float_as_int(): + test.iarr= (1, 2, 3.0) + self.assertRaises(TypeError, assign_float_as_int) + + # non-dynamic arrays cannot change size + def assign_different_size(arr, val): + setattr(test, arr, val) + for arr, val in zip(("iarr", "farr", "barr"), ((1, 2), (1.0, 2.0), (True, False))): + self.assertRaises(ValueError, assign_different_size, arr, val) + + # test assignment of specific items + def test_assign_item(self): + for arr, rand_func in zip((test.farr, test.iarr, test.barr), (rand_float, rand_int, rand_bool)): + for i in range(len(arr)): + val= rand_func() + arr[i]= val + + self.assertEqual(arr[i], val) + + # float prop should accept also int + for i in range(len(test.farr)): + val= rand_int() + test.farr[i]= val + self.assertEqual(test.farr[i], float(val)) + + # + + def test_assign_item_fail(self): + def assign_bad_index(arr): + arr[4] = 1.0 + + def assign_bad_type(arr): + arr[1]= "123" + + for arr in [test.farr, test.iarr, test.barr]: + self.assertRaises(IndexError, assign_bad_index, arr) + + # not testing bool because bool allows not only (True|False) + for arr in [test.farr, test.iarr]: + self.assertRaises(TypeError, assign_bad_type, arr) + + def test_dynamic_assign_array(self): + # test various lengths here + for arr, rand_func in zip(("fdarr", "idarr", "bdarr"), (rand_float, rand_int, rand_bool)): + for length in range(1, 64): + rval= make_random_array(length, rand_func) + setattr(test, arr, rval) + self.assertEqual(prop_to_list(getattr(test, arr)), rval) + + def test_dynamic_assign_array_fail(self): + # could also test too big length here + + def assign_empty_list(arr): + setattr(test, arr, ()) + + for arr in ("fdarr", "idarr", "bdarr"): + self.assertRaises(ValueError, assign_empty_list, arr) + + +class TestMArray(unittest.TestCase): + def setUp(self): + # reset dynamic array sizes + for arr, func in zip(("fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool)): + setattr(test, arr, make_random_3d_array((3, 4, 5), func)) + + # test assignment + def test_assign_array(self): + for arr, func in zip(("fmarr", "imarr", "bmarr"), (rand_float, rand_int, rand_bool)): + # assignment of [3][4][5] + rval= make_random_3d_array((3, 4, 5), func) + setattr(test, arr, rval) + self.assertEqual(prop_to_list(getattr(test, arr)), rval) + + # test assignment of [2][4][5], [1][4][5] should work on dynamic arrays + + def test_assign_array_fail(self): + def assign_empty_array(): + test.fmarr= () + self.assertRaises(ValueError, assign_empty_array) + + def assign_invalid_size(arr, rval): + setattr(test, arr, rval) + + # assignment of 3,4,4 or 3,3,5 should raise ex + for arr, func in zip(("fmarr", "imarr", "bmarr"), (rand_float, rand_int, rand_bool)): + rval= make_random_3d_array((3, 4, 4), func) + self.assertRaises(ValueError, assign_invalid_size, arr, rval) + + rval= make_random_3d_array((3, 3, 5), func) + self.assertRaises(ValueError, assign_invalid_size, arr, rval) + + rval= make_random_3d_array((3, 3, 3), func) + self.assertRaises(ValueError, assign_invalid_size, arr, rval) + + def test_assign_item(self): + # arr[i] = x + for arr, func in zip(("fmarr", "imarr", "bmarr", "fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool) * 2): + rval= make_random_2d_array((4, 5), func) + + for i in range(3): + getattr(test, arr)[i]= rval + self.assertEqual(prop_to_list(getattr(test, arr)[i]), rval) + + # arr[i][j] = x + for arr, func in zip(("fmarr", "imarr", "bmarr", "fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool) * 2): + + arr= getattr(test, arr) + rval= make_random_array(5, func) + + for i in range(3): + for j in range(4): + arr[i][j]= rval + self.assertEqual(prop_to_list(arr[i][j]), rval) + + + def test_assign_item_fail(self): + def assign_wrong_size(arr, i, rval): + getattr(test, arr)[i]= rval + + # assign wrong size at level 2 + for arr, func in zip(("fmarr", "imarr", "bmarr"), (rand_float, rand_int, rand_bool)): + rval1= make_random_2d_array((3, 5), func) + rval2= make_random_2d_array((4, 3), func) + + for i in range(3): + self.assertRaises(ValueError, assign_wrong_size, arr, i, rval1) + self.assertRaises(ValueError, assign_wrong_size, arr, i, rval2) + + def test_dynamic_assign_array(self): + for arr, func in zip(("fdmarr", "idmarr", "bdmarr"), (rand_float, rand_int, rand_bool)): + # assignment of [3][4][5] + rval= make_random_3d_array((3, 4, 5), func) + setattr(test, arr, rval) + self.assertEqual(prop_to_list(getattr(test, arr)), rval) + + # [2][4][5] + rval= make_random_3d_array((2, 4, 5), func) + setattr(test, arr, rval) + self.assertEqual(prop_to_list(getattr(test, arr)), rval) + + # [1][4][5] + rval= make_random_3d_array((1, 4, 5), func) + setattr(test, arr, rval) + self.assertEqual(prop_to_list(getattr(test, arr)), rval) + + + # test access + def test_access(self): + pass + + # test slice access, negative indices + def test_access_fail(self): + pass + +random.seed() + +def rand_int(): + return random.randint(-1000, 1000) + +def rand_float(): + return float(rand_int()) + +def rand_bool(): + return bool(random.randint(0, 1)) + +def make_random_array(len, rand_func): + arr= [] + for i in range(len): + arr.append(rand_func()) + + return arr + +def make_random_2d_array(dimsize, rand_func): + marr= [] + for i in range(dimsize[0]): + marr.append([]) + + for j in range(dimsize[1]): + marr[-1].append(rand_func()) + + return marr + +def make_random_3d_array(dimsize, rand_func): + marr= [] + for i in range(dimsize[0]): + marr.append([]) + + for j in range(dimsize[1]): + marr[-1].append([]) + + for k in range(dimsize[2]): + marr[-1][-1].append(rand_func()) + + return marr + +def prop_to_list(prop): + ret= [] + + for x in prop: + if type(x) not in (bool, int, float): + ret.append(prop_to_list(x)) + else: + ret.append(x) + + return ret + +def suite(): + return unittest.TestSuite([unittest.TestLoader().loadTestsFromTestCase(TestArray), unittest.TestLoader().loadTestsFromTestCase(TestMArray)]) + +if __name__ == "__main__": + unittest.TextTestRunner(verbosity=2).run(suite()) + diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 1c21c1817e6..9cd45a268da 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -32,14 +32,13 @@ #include #endif +#include "BLI_storage.h" /* _LARGEFILE_SOURCE */ + #include #include #include #include - -#include "BLI_storage.h" /* _LARGEFILE_SOURCE */ - #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 98205d17ef3..4107c1346e5 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -586,6 +586,7 @@ PropertyUnit RNA_property_unit(PropertyRNA *prop); int RNA_property_flag(PropertyRNA *prop); int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop); +int RNA_property_multidimensional_array_length(PointerRNA *ptr, PropertyRNA *prop, int dimension); int RNA_property_dynamic_array_set_length(PointerRNA *ptr, PropertyRNA *prop, int length); char RNA_property_array_item_char(PropertyRNA *prop, int index); unsigned short RNA_property_array_dimension(PropertyRNA *prop, unsigned short dim_size[]); diff --git a/source/blender/makesrna/SConscript b/source/blender/makesrna/SConscript index c2790927cef..845abf636e2 100644 --- a/source/blender/makesrna/SConscript +++ b/source/blender/makesrna/SConscript @@ -37,4 +37,7 @@ if env['WITH_BF_LCMS']: if env['WITH_BF_GAMEENGINE']: defs.append('GAMEBLENDER=1') +if env['BF_UNIT_TEST']: + defs.append('UNIT_TEST') + env.BlenderLib ( 'bf_rna', objs, Split(incs), defines=defs, libtype=['core','player'], priority = [165,20] ) diff --git a/source/blender/makesrna/intern/SConscript b/source/blender/makesrna/intern/SConscript index 9234efa2a5d..569f0547731 100644 --- a/source/blender/makesrna/intern/SConscript +++ b/source/blender/makesrna/intern/SConscript @@ -71,6 +71,9 @@ if env['WITH_BF_OPENAL']: if env['WITH_BF_JACK']: defs.append('WITH_JACK') +if env['BF_UNIT_TEST']: + defs.append('UNIT_TEST') + makesrna_tool.Append(CPPDEFINES=defs) makesrna_tool.Append (CPPPATH = Split(incs)) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index bb7b6cbcd37..907eba4018f 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -1991,6 +1991,7 @@ RNAProcessItem PROCESS_ITEMS[]= { {"rna_sequence.c", NULL, RNA_def_sequence}, {"rna_smoke.c", NULL, RNA_def_smoke}, {"rna_space.c", NULL, RNA_def_space}, + {"rna_test.c", NULL, RNA_def_test}, {"rna_text.c", NULL, RNA_def_text}, {"rna_timeline.c", NULL, RNA_def_timeline_marker}, {"rna_sound.c", NULL, RNA_def_sound}, diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index e71dcc2a586..f37fa01480c 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -557,14 +557,24 @@ int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop) int RNA_property_dynamic_array_set_length(PointerRNA *ptr, PropertyRNA *prop, int length) { - if (prop->setlength) - return prop->setlength(ptr, length); + /* length 0 is not allowed */ + if (!length) + return 0; + + if (prop->getlength) { + if (prop->setlength) + return prop->setlength(ptr, length); + else + /* length cannot be changed */ + return 0; + } else prop->arraylength= length; /* function parameters only? */ return 1; } +/* used by BPY to make an array from the python object */ unsigned short RNA_property_array_dimension(PropertyRNA *prop, unsigned short dimsize[]) { if (dimsize && prop->arraydimension > 1) { @@ -573,6 +583,25 @@ unsigned short RNA_property_array_dimension(PropertyRNA *prop, unsigned short di return prop->arraydimension; } +/* Return the size of Nth dimension. */ +int RNA_property_multidimensional_array_length(PointerRNA *ptr, PropertyRNA *prop, int dim) +{ + unsigned short i; + int len; + + if (dim == 0) { + len= RNA_property_array_length(ptr, prop); + + for (i= 0; i < prop->arraydimension - 1; i++) + len /= prop->dimsize[i]; + } + else { + len= prop->dimsize[dim - 1]; + } + + return len; +} + char RNA_property_array_item_char(PropertyRNA *prop, int index) { const char *vectoritem= "XYZW"; diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 69e6698bd3b..e415304ab6c 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -1046,6 +1046,8 @@ void RNA_def_property_multidimensional_array(PropertyRNA *prop, int arraylength, prop->arraydimension= dimension; + /* TODO make sure dimsize values are sane */ + if (dimension > 1) memcpy(prop->dimsize, dimsize, sizeof(dimsize[0]) * (dimension - 1)); } diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 13cc2ae9017..7de80843f27 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -157,6 +157,7 @@ void RNA_def_sensor(struct BlenderRNA *brna); void RNA_def_sequence(struct BlenderRNA *brna); void RNA_def_smoke(struct BlenderRNA *brna); void RNA_def_space(struct BlenderRNA *brna); +void RNA_def_test(struct BlenderRNA *brna); void RNA_def_text(struct BlenderRNA *brna); void RNA_def_texture(struct BlenderRNA *brna); void RNA_def_timeline_marker(struct BlenderRNA *brna); diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index d706fd5ac19..2bd89dbd3bf 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -39,7 +39,12 @@ struct bContext; struct IDProperty; struct GHash; +#ifdef UNIT_TEST +#define RNA_MAX_ARRAY 64 +#else #define RNA_MAX_ARRAY 32 +#endif + #define RNA_MAX_ARRAY_DIMENSION 3 /* Function Callbacks */ @@ -134,11 +139,10 @@ struct PropertyRNA { PropertySubType subtype; /* if an array this is > 0, specifying the length */ unsigned int arraylength; - /* these, if non-NULL, override arraylength */ + /* if non-NULL, overrides arraylength. Must not return 0? */ PropArrayLengthGetFunc getlength; /* if NULL, length cannot be changed by a user */ PropArrayLengthSetFunc setlength; - /* used only for dynamic arrays for now, default 1 */ unsigned short arraydimension; /* dimension sizes for dimensions greater than 1, first dimension size is not specified */ unsigned short dimsize[RNA_MAX_ARRAY_DIMENSION - 1]; diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 82e460ea57d..344135acaff 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -219,6 +219,18 @@ static void rna_Main_wm_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) rna_iterator_listbase_begin(iter, &bmain->wm, NULL); } +#ifdef UNIT_TEST + +static PointerRNA rna_Test_test_get(PointerRNA *ptr) +{ + PointerRNA ret= *ptr; + ret.type= &RNA_Test; + + return ret; +} + +#endif + #else void RNA_def_main(BlenderRNA *brna) @@ -276,6 +288,18 @@ void RNA_def_main(BlenderRNA *brna) } RNA_api_main(srna); + +#ifdef UNIT_TEST + + RNA_define_verify_sdna(0); + + prop= RNA_def_property(srna, "test", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "Test"); + RNA_def_property_pointer_funcs(prop, "rna_Test_test_get", NULL, NULL); + + RNA_define_verify_sdna(1); + +#endif } #endif diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index f7235db49a5..efd0046d827 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1182,7 +1182,7 @@ static void rna_def_mtface(BlenderRNA *brna) RNA_def_property_ui_text(prop, "UV 4", ""); RNA_def_property_update(prop, 0, "rna_Mesh_update_data"); - prop= RNA_def_property(srna, "uv", PROP_FLOAT, PROP_XYZ); + prop= RNA_def_property(srna, "uv", PROP_FLOAT, PROP_NONE); RNA_def_property_multidimensional_array(prop, 4 * 2, 2, uv_dim); RNA_def_property_flag(prop, PROP_DYNAMIC); RNA_def_property_dynamic_array_funcs(prop, "rna_MeshTextureFace_uv_get_length", "rna_MeshTextureFace_uv_set_length"); diff --git a/source/blender/makesrna/intern/rna_test.c b/source/blender/makesrna/intern/rna_test.c new file mode 100644 index 00000000000..bfaf318018a --- /dev/null +++ b/source/blender/makesrna/intern/rna_test.c @@ -0,0 +1,188 @@ +/** + * $Id: rna_test.c $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Arystanbek Dyussenov + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* Defines a structure with properties used for array manipulation tests in BPY. */ + +#include +#include + +#include "RNA_define.h" +#include "RNA_types.h" + +#include "rna_internal.h" + +#define ARRAY_SIZE 3 +#define DYNAMIC_ARRAY_SIZE 64 +#define MARRAY_DIM [3][4][5] +#define MARRAY_TOTDIM 3 +#define MARRAY_DIMSIZE 4, 5 +#define MARRAY_SIZE(type) (sizeof(type MARRAY_DIM) / sizeof(type)) +#define DYNAMIC_MARRAY_DIM [3][4][5] +#define DYNAMIC_MARRAY_SIZE(type) (sizeof(type DYNAMIC_MARRAY_DIM) / sizeof(type)) + +#ifdef RNA_RUNTIME + +#ifdef UNIT_TEST + +#define DEF_VARS(type, prefix) \ + static type prefix ## arr[ARRAY_SIZE]; \ + static type prefix ## darr[DYNAMIC_ARRAY_SIZE]; \ + static int prefix ## darr_len= ARRAY_SIZE; \ + static type prefix ## marr MARRAY_DIM; \ + static type prefix ## dmarr DYNAMIC_MARRAY_DIM; \ + static int prefix ## dmarr_len= sizeof(prefix ## dmarr); + +#define DEF_GET_SET(type, arr) \ + void rna_Test_ ## arr ## _get(PointerRNA *ptr, type *values) \ + { \ + memcpy(values, arr, sizeof(arr)); \ + } \ + \ + void rna_Test_ ## arr ## _set(PointerRNA *ptr, const type *values) \ + { \ + memcpy(arr, values, sizeof(arr)); \ + } + +#define DEF_GET_SET_LEN(arr, max) \ + static int rna_Test_ ## arr ## _get_length(PointerRNA *ptr) \ + { \ + return arr ## _len; \ + } \ + \ + static int rna_Test_ ## arr ## _set_length(PointerRNA *ptr, int length) \ + { \ + if (length > max) \ + return 0; \ + \ + arr ## _len= length; \ + \ + return 1; \ + } \ + +DEF_VARS(float, f) +DEF_VARS(int, i) +DEF_VARS(int, b) + +DEF_GET_SET(float, farr) +DEF_GET_SET(int, iarr) +DEF_GET_SET(int, barr) + +DEF_GET_SET(float, fmarr) +DEF_GET_SET(int, imarr) +DEF_GET_SET(int, bmarr) + +DEF_GET_SET(float, fdarr) +DEF_GET_SET_LEN(fdarr, DYNAMIC_ARRAY_SIZE) +DEF_GET_SET(int, idarr) +DEF_GET_SET_LEN(idarr, DYNAMIC_ARRAY_SIZE) +DEF_GET_SET(int, bdarr) +DEF_GET_SET_LEN(bdarr, DYNAMIC_ARRAY_SIZE) + +DEF_GET_SET(float, fdmarr) +DEF_GET_SET_LEN(fdmarr, DYNAMIC_MARRAY_SIZE(float)) +DEF_GET_SET(int, idmarr) +DEF_GET_SET_LEN(idmarr, DYNAMIC_MARRAY_SIZE(int)) +DEF_GET_SET(int, bdmarr) +DEF_GET_SET_LEN(bdmarr, DYNAMIC_MARRAY_SIZE(int)) + +#endif + +#else + +void RNA_def_test(BlenderRNA *brna) +{ +#ifdef UNIT_TEST + StructRNA *srna; + PropertyRNA *prop; + unsigned short dimsize[]= {MARRAY_DIMSIZE}; + + srna= RNA_def_struct(brna, "Test", NULL); + RNA_def_struct_sdna(srna, "Test"); + + prop= RNA_def_float_array(srna, "farr", ARRAY_SIZE, NULL, 0.0f, 0.0f, "farr", "float array", 0.0f, 0.0f); + RNA_def_property_float_funcs(prop, "rna_Test_farr_get", "rna_Test_farr_set", NULL); + + prop= RNA_def_int_array(srna, "iarr", ARRAY_SIZE, NULL, 0, 0, "iarr", "int array", 0, 0); + RNA_def_property_int_funcs(prop, "rna_Test_iarr_get", "rna_Test_iarr_set", NULL); + + prop= RNA_def_boolean_array(srna, "barr", ARRAY_SIZE, NULL, "barr", "boolean array"); + RNA_def_property_boolean_funcs(prop, "rna_Test_barr_get", "rna_Test_barr_set"); + + /* dynamic arrays */ + + prop= RNA_def_float_array(srna, "fdarr", DYNAMIC_ARRAY_SIZE, NULL, 0.0f, 0.0f, "fdarr", "dynamic float array", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_property_dynamic_array_funcs(prop, "rna_Test_fdarr_get_length", "rna_Test_fdarr_set_length"); + RNA_def_property_float_funcs(prop, "rna_Test_fdarr_get", "rna_Test_fdarr_set", NULL); + + prop= RNA_def_int_array(srna, "idarr", DYNAMIC_ARRAY_SIZE, NULL, 0, 0, "idarr", "int array", 0, 0); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_property_dynamic_array_funcs(prop, "rna_Test_idarr_get_length", "rna_Test_idarr_set_length"); + RNA_def_property_int_funcs(prop, "rna_Test_idarr_get", "rna_Test_idarr_set", NULL); + + prop= RNA_def_boolean_array(srna, "bdarr", DYNAMIC_ARRAY_SIZE, NULL, "bdarr", "boolean array"); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_property_dynamic_array_funcs(prop, "rna_Test_bdarr_get_length", "rna_Test_bdarr_set_length"); + RNA_def_property_boolean_funcs(prop, "rna_Test_bdarr_get", "rna_Test_bdarr_set"); + + /* multidimensional arrays */ + + prop= RNA_def_property(srna, "fmarr", PROP_FLOAT, PROP_NONE); + RNA_def_property_multidimensional_array(prop, MARRAY_SIZE(float), MARRAY_TOTDIM, dimsize); + RNA_def_property_float_funcs(prop, "rna_Test_fmarr_get", "rna_Test_fmarr_set", NULL); + + prop= RNA_def_property(srna, "imarr", PROP_INT, PROP_NONE); + RNA_def_property_multidimensional_array(prop, MARRAY_SIZE(int), MARRAY_TOTDIM, dimsize); + RNA_def_property_int_funcs(prop, "rna_Test_imarr_get", "rna_Test_imarr_set", NULL); + + prop= RNA_def_property(srna, "bmarr", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_multidimensional_array(prop, MARRAY_SIZE(int), MARRAY_TOTDIM, dimsize); + RNA_def_property_boolean_funcs(prop, "rna_Test_bmarr_get", "rna_Test_bmarr_set"); + + /* dynamic multidimensional arrays */ + + prop= RNA_def_property(srna, "fdmarr", PROP_FLOAT, PROP_NONE); + RNA_def_property_multidimensional_array(prop, DYNAMIC_MARRAY_SIZE(float), MARRAY_TOTDIM, dimsize); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_property_dynamic_array_funcs(prop, "rna_Test_fdmarr_get_length", "rna_Test_fdmarr_set_length"); + RNA_def_property_float_funcs(prop, "rna_Test_fdmarr_get", "rna_Test_fdmarr_set", NULL); + + prop= RNA_def_property(srna, "idmarr", PROP_INT, PROP_NONE); + RNA_def_property_multidimensional_array(prop, DYNAMIC_MARRAY_SIZE(int), MARRAY_TOTDIM, dimsize); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_property_dynamic_array_funcs(prop, "rna_Test_idmarr_get_length", "rna_Test_idmarr_set_length"); + RNA_def_property_int_funcs(prop, "rna_Test_idmarr_get", "rna_Test_idmarr_set", NULL); + + prop= RNA_def_property(srna, "bdmarr", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_multidimensional_array(prop, DYNAMIC_MARRAY_SIZE(int), MARRAY_TOTDIM, dimsize); + RNA_def_property_flag(prop, PROP_DYNAMIC); + RNA_def_property_dynamic_array_funcs(prop, "rna_Test_bdmarr_get_length", "rna_Test_bdmarr_set_length"); + RNA_def_property_boolean_funcs(prop, "rna_Test_bdmarr_get", "rna_Test_bdmarr_set"); + +#endif +} + +#endif /* RNA_RUNTIME */ + + diff --git a/source/blender/python/intern/bpy_array.c b/source/blender/python/intern/bpy_array.c index 669cccb7011..9db4b20d011 100644 --- a/source/blender/python/intern/bpy_array.c +++ b/source/blender/python/intern/bpy_array.c @@ -30,33 +30,65 @@ #include "BLI_string.h" +#include "BKE_global.h" + #include "MEM_guardedalloc.h" -typedef void (*ItemConvertFunc)(PyObject *, char *); -typedef int (*ItemTypeCheckFunc)(PyObject *); -typedef void (*RNA_SetArrayFunc)(PointerRNA *, PropertyRNA *, const char *); +#define MAX_ARRAY_DIMENSION 10 -/* Ensures that a python sequence has an expected number of items/sub-items and items are of expected type. */ -static int pyrna_validate_array(PyObject *seq, unsigned short dim, unsigned short totdim, unsigned short dim_size[], - ItemTypeCheckFunc check_item_type, const char *item_type_str, char *error_str, int error_str_size) +/* convenient way to access array dimension size */ +#define DIMSIZE(a) (dimsize[a - 1]) + +typedef void (*ItemConvertFunc)(PyObject *, char *); +typedef int (*ItemTypeCheckFunc)(PyObject *); +typedef void (*RNA_SetArrayFunc)(PointerRNA *, PropertyRNA *, const char *); +typedef void (*RNA_SetIndexFunc)(PointerRNA *, PropertyRNA *, int index, void *); + +/* + arr[3][4][5] + 0 1 2 <- dimension index +*/ + +/* + arr[2] = x + + py_to_array_index(arraydim=0, arrayoffset=0, index=2) + validate_array(lvalue_dim=0) + ... make real index ... +*/ + +/* arr[3]=x, self->arraydim is 0, lvalue_dim is 1 */ +/* Ensures that a python sequence has expected number of items/sub-items and items are of desired type. */ +static int validate_array_type(PyObject *seq, unsigned short dim, unsigned short totdim, unsigned short dimsize[], + ItemTypeCheckFunc check_item_type, const char *item_type_str, const char *error_prefix) { int i; - if (dim < totdim) { + + /* not the last dimension */ + if (dim + 1 < totdim) { + /* check that a sequence contains dimsize[dim] items */ + for (i= 0; i < PySequence_Length(seq); i++) { PyObject *item; int ok= 1; item= PySequence_GetItem(seq, i); if (!PySequence_Check(item)) { - BLI_snprintf(error_str, error_str_size, "expected a %d-dimensional sequence of %s", (int)totdim, item_type_str); + /* BLI_snprintf(error_str, error_str_size, "expected a sequence of %s", item_type_str); */ + PyErr_Format(PyExc_TypeError, "%s expected a sequence of %s", error_prefix, item_type_str); ok= 0; } - else if (PySequence_Length(item) != dim_size[dim - 1]) { - BLI_snprintf(error_str, error_str_size, "dimension %d should contain %d items", (int)dim, (int)dim_size[dim - 1]); + /* arr[3][4][5] + DIMSIZE(1)=4 + DIMSIZE(2)=5 + + dim=0 */ + else if (PySequence_Length(item) != DIMSIZE(dim + 1)) { + /* BLI_snprintf(error_str, error_str_size, "sequences of dimension %d should contain %d items", (int)dim + 1, (int)DIMSIZE(dim + 1)); */ + PyErr_Format(PyExc_ValueError, "%s sequences of dimension %d should contain %d items", error_prefix, (int)dim + 1, (int)DIMSIZE(dim + 1)); ok= 0; } - - if (!pyrna_validate_array(item, dim + 1, totdim, dim_size, check_item_type, item_type_str, error_str, error_str_size)) { + else if (!validate_array_type(item, dim + 1, totdim, dimsize, check_item_type, item_type_str, error_prefix)) { ok= 0; } @@ -67,13 +99,15 @@ static int pyrna_validate_array(PyObject *seq, unsigned short dim, unsigned shor } } else { + /* check that items are of correct type */ for (i= 0; i < PySequence_Length(seq); i++) { PyObject *item= PySequence_GetItem(seq, i); if (!check_item_type(item)) { Py_DECREF(item); - - BLI_snprintf(error_str, error_str_size, "sequence items should be of type %s", item_type_str); + + /* BLI_snprintf(error_str, error_str_size, "sequence items should be of type %s", item_type_str); */ + PyErr_Format(PyExc_TypeError, "sequence items should be of type %s", item_type_str); return 0; } @@ -85,7 +119,7 @@ static int pyrna_validate_array(PyObject *seq, unsigned short dim, unsigned shor } /* Returns the number of items in a single- or multi-dimensional sequence. */ -static int pyrna_count_items(PyObject *seq) +static int count_items(PyObject *seq) { int totitem= 0; @@ -93,7 +127,7 @@ static int pyrna_count_items(PyObject *seq) int i; for (i= 0; i < PySequence_Length(seq); i++) { PyObject *item= PySequence_GetItem(seq, i); - totitem += pyrna_count_items(item); + totitem += count_items(item); Py_DECREF(item); } } @@ -103,40 +137,103 @@ static int pyrna_count_items(PyObject *seq) return totitem; } -static int pyrna_apply_array_length(PointerRNA *ptr, PropertyRNA *prop, int totitem, char *error_str, int error_str_size) +/* Modifies property array length if needed and PROP_DYNAMIC flag is set. */ +static int validate_array_length(PyObject *rvalue, PointerRNA *ptr, PropertyRNA *prop, int lvalue_dim, int *totitem, const char *error_prefix) { - if (RNA_property_flag(prop) & PROP_DYNAMIC) { - /* length can be flexible */ - if (RNA_property_array_length(ptr, prop) != totitem) { - if (!RNA_property_dynamic_array_set_length(ptr, prop, totitem)) { - BLI_snprintf(error_str, error_str_size, "%s.%s: array length cannot be changed to %d", RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), totitem); + unsigned short dimsize[MAX_ARRAY_DIMENSION]; + int tot, totdim, len; + + tot= count_items(rvalue); + totdim= RNA_property_array_dimension(prop, dimsize); + + if ((RNA_property_flag(prop) & PROP_DYNAMIC) && lvalue_dim == 0) { + /* length is flexible */ + if (RNA_property_array_length(ptr, prop) != tot) { + if (!RNA_property_dynamic_array_set_length(ptr, prop, tot)) { + /* BLI_snprintf(error_str, error_str_size, "%s.%s: array length cannot be changed to %d", RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); */ + PyErr_Format(PyExc_ValueError, "%s %s.%s: array length cannot be changed to %d", error_prefix, RNA_struct_identifier(ptr->type), RNA_property_identifier(prop), tot); return 0; } + + len= tot; } } else { /* length is a constraint */ - int len= RNA_property_array_length(ptr, prop); - if (totitem != len) { - BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); + if (!lvalue_dim) { + len= RNA_property_array_length(ptr, prop); + } + /* array item assignment */ + else { + int i; + + len= 1; + + /* arr[3][4][5] + + arr[2] = x + dimsize={4, 5} + DIMSIZE(1) = 4 + DIMSIZE(2) = 5 + lvalue_dim=0, totdim=3 + + arr[2][3] = x + lvalue_dim=1 + + arr[2][3][4] = x + lvalue_dim=2 */ + for (i= lvalue_dim; i < totdim; i++) + len *= DIMSIZE(i); + } + + if (tot != len) { + /* BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); */ + PyErr_Format(PyExc_ValueError, "%s sequence must have %d items total", error_prefix, len); return 0; } } + + *totitem= len; + return 1; } -static char *pyrna_py_to_array(PyObject *seq, unsigned short dim, unsigned short totdim, char *data, unsigned int item_size, ItemConvertFunc convert_item) +static int validate_array(PyObject *rvalue, PointerRNA *ptr, PropertyRNA *prop, int lvalue_dim, ItemTypeCheckFunc check_item_type, const char *item_type_str, int *totitem, const char *error_prefix) +{ + unsigned short dimsize[MAX_ARRAY_DIMENSION]; + int totdim= RNA_property_array_dimension(prop, dimsize); + + /* validate type first because length validation may modify property array length */ + + if (!validate_array_type(rvalue, lvalue_dim, totdim, dimsize, check_item_type, item_type_str, error_prefix)) + return 0; + + return validate_array_length(rvalue, ptr, prop, lvalue_dim, totitem, error_prefix); +} + +static char *copy_values(PyObject *seq, PointerRNA *ptr, PropertyRNA *prop, unsigned short dim, char *data, unsigned int item_size, int *index, ItemConvertFunc convert_item, RNA_SetIndexFunc rna_set_index) { unsigned int i; + int totdim= RNA_property_array_dimension(prop, NULL); + for (i= 0; i < PySequence_Length(seq); i++) { PyObject *item= PySequence_GetItem(seq, i); - if (dim < totdim) { - data= pyrna_py_to_array(item, dim + 1, totdim, data, item_size, convert_item); + if (dim + 1 < totdim) { + data= copy_values(item, ptr, prop, dim + 1, data, item_size, index, convert_item, rna_set_index); } else { - convert_item(item, data); - data += item_size; + if (!data) { + char value[sizeof(int)]; + + convert_item(item, value); + rna_set_index(ptr, prop, *index, value); + *index = *index + 1; + } + else { + convert_item(item, data); + data += item_size; + } } Py_DECREF(item); @@ -145,21 +242,15 @@ static char *pyrna_py_to_array(PyObject *seq, unsigned short dim, unsigned short return data; } -static int pyrna_py_to_array_generic(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size, - ItemTypeCheckFunc check_item_type, const char *item_type_str, int item_size, ItemConvertFunc convert_item, RNA_SetArrayFunc rna_set_array) +static int py_to_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, ItemTypeCheckFunc check_item_type, const char *item_type_str, int item_size, ItemConvertFunc convert_item, RNA_SetArrayFunc rna_set_array, const char *error_prefix) { - unsigned short totdim, dim_size[100]; + unsigned short totdim, dim_size[MAX_ARRAY_DIMENSION]; int totitem; char *data= NULL; totdim= RNA_property_array_dimension(prop, dim_size); - if (!pyrna_validate_array(py, 1, totdim, dim_size, check_item_type, item_type_str, error_str, error_str_size)) - return 0; - - totitem= pyrna_count_items(py); - - if (!pyrna_apply_array_length(ptr, prop, totitem, error_str, error_str_size)) + if (!validate_array(py, ptr, prop, 0, check_item_type, item_type_str, &totitem, error_prefix)) return 0; if (totitem) { @@ -168,7 +259,7 @@ static int pyrna_py_to_array_generic(PyObject *py, PointerRNA *ptr, PropertyRNA else data= param_data; - pyrna_py_to_array(py, 1, totdim, data, item_size, convert_item); + copy_values(py, ptr, prop, 0, data, item_size, NULL, convert_item, NULL); if (param_data) { if (RNA_property_flag(prop) & PROP_DYNAMIC) { @@ -186,50 +277,236 @@ static int pyrna_py_to_array_generic(PyObject *py, PointerRNA *ptr, PropertyRNA return 1; } -static void pyrna_py_to_float(PyObject *py, char *data) +static int py_to_array_index(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, int lvalue_dim, int arrayoffset, int index, ItemTypeCheckFunc check_item_type, const char *item_type_str, ItemConvertFunc convert_item, RNA_SetIndexFunc rna_set_index, const char *error_prefix) +{ + unsigned short totdim, dimsize[MAX_ARRAY_DIMENSION]; + int totitem, i; + + totdim= RNA_property_array_dimension(prop, dimsize); + + /* convert index */ + + /* arr[3][4][5] + + arr[2] = x + lvalue_dim=0, index = 0 + 2 * 4 * 5 + + arr[2][3] = x + lvalue_dim=1, index = 40 + 3 * 5 */ + + lvalue_dim++; + + for (i= lvalue_dim; i < totdim; i++) + index *= DIMSIZE(i); + + index += arrayoffset; + + if (!validate_array(py, ptr, prop, lvalue_dim, check_item_type, item_type_str, &totitem, error_prefix)) + return 0; + + if (totitem) + copy_values(py, ptr, prop, lvalue_dim, NULL, 0, &index, convert_item, rna_set_index); + + return 1; +} + +static void py_to_float(PyObject *py, char *data) { *(float*)data= (float)PyFloat_AsDouble(py); } -static void pyrna_py_to_int(PyObject *py, char *data) +static void py_to_int(PyObject *py, char *data) { *(int*)data= (int)PyLong_AsSsize_t(py); } -static void pyrna_py_to_boolean(PyObject *py, char *data) +static void py_to_bool(PyObject *py, char *data) { *(int*)data= (int)PyObject_IsTrue(py); } static int py_float_check(PyObject *py) { - return PyFloat_Check(py) || (PyIndex_Check(py)); + /* accept both floats and integers */ + return PyFloat_Check(py) || PyLong_Check(py); } static int py_int_check(PyObject *py) { - return PyLong_Check(py) || (PyIndex_Check(py)); + /* accept only integers */ + return PyLong_Check(py); } static int py_bool_check(PyObject *py) { - return PyBool_Check(py) || (PyIndex_Check(py)); + return PyBool_Check(py); } -int pyrna_py_to_float_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size) +static void float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value) { - return pyrna_py_to_array_generic(py, ptr, prop, param_data, error_str, error_str_size, - py_float_check, "float", sizeof(float), pyrna_py_to_float, (RNA_SetArrayFunc)RNA_property_float_set_array); + RNA_property_float_set_index(ptr, prop, index, *(float*)value); } -int pyrna_py_to_int_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size) +static void int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value) { - return pyrna_py_to_array_generic(py, ptr, prop, param_data, error_str, error_str_size, - py_int_check, "int", sizeof(int), pyrna_py_to_int, (RNA_SetArrayFunc)RNA_property_int_set_array); + RNA_property_int_set_index(ptr, prop, index, *(int*)value); } -int pyrna_py_to_boolean_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size) +static void bool_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, void *value) { - return pyrna_py_to_array_generic(py, ptr, prop, param_data, error_str, error_str_size, - py_bool_check, "boolean", sizeof(int), pyrna_py_to_boolean, (RNA_SetArrayFunc)RNA_property_boolean_set_array); + RNA_property_boolean_set_index(ptr, prop, index, *(int*)value); +} + +int pyrna_py_to_array(PointerRNA *ptr, PropertyRNA *prop, char *param_data, PyObject *py, const char *error_prefix) +{ + int ret; + switch (RNA_property_type(prop)) { + case PROP_FLOAT: + ret= py_to_array(py, ptr, prop, param_data, py_float_check, "float", sizeof(float), py_to_float, (RNA_SetArrayFunc)RNA_property_float_set_array, error_prefix); + break; + case PROP_INT: + ret= py_to_array(py, ptr, prop, param_data, py_int_check, "int", sizeof(int), py_to_int, (RNA_SetArrayFunc)RNA_property_int_set_array, error_prefix); + break; + case PROP_BOOLEAN: + ret= py_to_array(py, ptr, prop, param_data, py_bool_check, "boolean", sizeof(int), py_to_bool, (RNA_SetArrayFunc)RNA_property_boolean_set_array, error_prefix); + break; + default: + PyErr_SetString(PyExc_TypeError, "not an array type"); + ret= 0; + } + + return ret; +} + +int pyrna_py_to_array_index(PointerRNA *ptr, PropertyRNA *prop, int arraydim, int arrayoffset, int index, PyObject *py, const char *error_prefix) +{ + int ret; + switch (RNA_property_type(prop)) { + case PROP_FLOAT: + ret= py_to_array_index(py, ptr, prop, arraydim, arrayoffset, index, py_float_check, "float", py_to_float, float_set_index, error_prefix); + break; + case PROP_INT: + ret= py_to_array_index(py, ptr, prop, arraydim, arrayoffset, index, py_int_check, "int", py_to_int, int_set_index, error_prefix); + break; + case PROP_BOOLEAN: + ret= py_to_array_index(py, ptr, prop, arraydim, arrayoffset, index, py_bool_check, "boolean", py_to_bool, bool_set_index, error_prefix); + break; + default: + PyErr_SetString(PyExc_TypeError, "not an array type"); + ret= 0; + } + + return ret; +} + +static PyObject *pyrna_array_item(PointerRNA *ptr, PropertyRNA *prop, int index) +{ + PyObject *item; + + switch (RNA_property_type(prop)) { + case PROP_FLOAT: + item= PyFloat_FromDouble(RNA_property_float_get_index(ptr, prop, index)); + break; + case PROP_BOOLEAN: + item= PyBool_FromLong(RNA_property_boolean_get_index(ptr, prop, index)); + break; + case PROP_INT: + item= PyLong_FromSsize_t(RNA_property_int_get_index(ptr, prop, index)); + break; + default: + PyErr_SetString(PyExc_TypeError, "not an array type"); + item= NULL; + } + + return item; +} + +#if 0 +/* XXX this is not used (and never will?) */ +/* Given an array property, creates an N-dimensional tuple of values. */ +static PyObject *pyrna_py_from_array_internal(PointerRNA *ptr, PropertyRNA *prop, int dim, int *index) +{ + PyObject *tuple; + int i, len; + int totdim= RNA_property_array_dimension(prop, NULL); + + len= RNA_property_multidimensional_array_length(ptr, prop, dim); + + tuple= PyTuple_New(len); + + for (i= 0; i < len; i++) { + PyObject *item; + + if (dim + 1 < totdim) + item= pyrna_py_from_array_internal(ptr, prop, dim + 1, index); + else { + item= pyrna_array_item(ptr, prop, *index); + *index= *index + 1; + } + + if (!item) { + Py_DECREF(tuple); + return NULL; + } + + PyTuple_SetItem(tuple, i, item); + } + + return tuple; +} +#endif + +PyObject *pyrna_py_from_array_index(BPy_PropertyRNA *self, int index) +{ + int totdim, i, len; + unsigned short dimsize[MAX_ARRAY_DIMENSION]; + BPy_PropertyRNA *ret= NULL; + + /* just in case check */ + len= RNA_property_multidimensional_array_length(&self->ptr, self->prop, self->arraydim); + if (index >= len || index < 0) { + /* this shouldn't happen because higher level funcs must check for invalid index */ + if (G.f & G_DEBUG) printf("pyrna_py_from_array_index: invalid index %d for array with length=%d\n", index, len); + + PyErr_SetString(PyExc_IndexError, "out of range"); + return NULL; + } + + totdim= RNA_property_array_dimension(self->prop, dimsize); + + if (self->arraydim + 1 < totdim) { + ret= (BPy_PropertyRNA*)pyrna_prop_CreatePyObject(&self->ptr, self->prop); + ret->arraydim= self->arraydim + 1; + + /* arr[3][4][5] + + x = arr[2] + index = 0 + 2 * 4 * 5 + + x = arr[2][3] + index = offset + 3 * 5 */ + + for (i= self->arraydim + 1; i < totdim; i++) + index *= DIMSIZE(i); + + ret->arrayoffset= self->arrayoffset + index; + } + else { + index = self->arrayoffset + index; + ret= (BPy_PropertyRNA*)pyrna_array_item(&self->ptr, self->prop, index); + } + + return (PyObject*)ret; +} + +PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop) +{ + PyObject *ret; + + ret= pyrna_math_object_from_array(ptr, prop); + + /* is this a maths object? */ + if (ret) return ret; + + return pyrna_prop_CreatePyObject(ptr, prop); } diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 8ba3b5f8732..f2ffd5e0358 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -130,6 +130,70 @@ Mathutils_Callback mathutils_rna_matrix_cb = { (BaseMathSetIndexFunc) NULL }; +PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) +{ + PyObject *ret= NULL; + +#ifdef USE_MATHUTILS + int type, subtype, totdim; + int len; + + len= RNA_property_array_length(ptr, prop); + type= RNA_property_type(prop); + subtype= RNA_property_subtype(prop); + totdim= RNA_property_array_dimension(prop, NULL); + + if (type != PROP_FLOAT) return NULL; + + if (totdim == 1 || (totdim == 2 && subtype == PROP_MATRIX)) { + ret = pyrna_prop_CreatePyObject(ptr, prop); + + switch(RNA_property_subtype(prop)) { + case PROP_TRANSLATION: + case PROP_DIRECTION: + case PROP_VELOCITY: + case PROP_ACCELERATION: + case PROP_XYZ: + if(len>=2 && len <= 4) { + PyObject *vec_cb= newVectorObject_cb(ret, len, mathutils_rna_array_cb_index, FALSE); + Py_DECREF(ret); /* the vector owns now */ + ret= vec_cb; /* return the vector instead */ + } + break; + case PROP_MATRIX: + if(len==16) { + PyObject *mat_cb= newMatrixObject_cb(ret, 4,4, mathutils_rna_matrix_cb_index, FALSE); + Py_DECREF(ret); /* the matrix owns now */ + ret= mat_cb; /* return the matrix instead */ + } + else if (len==9) { + PyObject *mat_cb= newMatrixObject_cb(ret, 3,3, mathutils_rna_matrix_cb_index, FALSE); + Py_DECREF(ret); /* the matrix owns now */ + ret= mat_cb; /* return the matrix instead */ + } + break; + case PROP_EULER: + case PROP_QUATERNION: + if(len==3) { /* euler */ + PyObject *eul_cb= newEulerObject_cb(ret, mathutils_rna_array_cb_index, FALSE); + Py_DECREF(ret); /* the matrix owns now */ + ret= eul_cb; /* return the matrix instead */ + } + else if (len==4) { + PyObject *quat_cb= newQuaternionObject_cb(ret, mathutils_rna_array_cb_index, FALSE); + Py_DECREF(ret); /* the matrix owns now */ + ret= quat_cb; /* return the matrix instead */ + } + break; + default: + break; + } + } +#endif + + return ret; +} + #endif static StructRNA *pyrna_struct_as_srna(PyObject *self); @@ -288,58 +352,7 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop) int len = RNA_property_array_length(ptr, prop); if (len > 0) { - /* resolve the array from a new pytype */ - PyObject *ret = pyrna_prop_CreatePyObject(ptr, prop); - -#ifdef USE_MATHUTILS - - /* return a mathutils vector where possible */ - if(RNA_property_type(prop)==PROP_FLOAT) { - switch(RNA_property_subtype(prop)) { - case PROP_TRANSLATION: - case PROP_DIRECTION: - case PROP_VELOCITY: - case PROP_ACCELERATION: - case PROP_XYZ: - if(len>=2 && len <= 4) { - PyObject *vec_cb= newVectorObject_cb(ret, len, mathutils_rna_array_cb_index, FALSE); - Py_DECREF(ret); /* the vector owns now */ - ret= vec_cb; /* return the vector instead */ - } - break; - case PROP_MATRIX: - if(len==16) { - PyObject *mat_cb= newMatrixObject_cb(ret, 4,4, mathutils_rna_matrix_cb_index, FALSE); - Py_DECREF(ret); /* the matrix owns now */ - ret= mat_cb; /* return the matrix instead */ - } - else if (len==9) { - PyObject *mat_cb= newMatrixObject_cb(ret, 3,3, mathutils_rna_matrix_cb_index, FALSE); - Py_DECREF(ret); /* the matrix owns now */ - ret= mat_cb; /* return the matrix instead */ - } - break; - case PROP_EULER: - case PROP_QUATERNION: - if(len==3) { /* euler */ - PyObject *eul_cb= newEulerObject_cb(ret, mathutils_rna_array_cb_index, FALSE); - Py_DECREF(ret); /* the matrix owns now */ - ret= eul_cb; /* return the matrix instead */ - } - else if (len==4) { - PyObject *quat_cb= newQuaternionObject_cb(ret, mathutils_rna_array_cb_index, FALSE); - Py_DECREF(ret); /* the matrix owns now */ - ret= quat_cb; /* return the matrix instead */ - } - break; - default: - break; - } - } - -#endif - - return ret; + return pyrna_py_from_array(ptr, prop); } /* see if we can coorce into a python type - PropertyType */ @@ -511,7 +524,7 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v int len = RNA_property_array_length(ptr, prop); if (len > 0) { - char error_str[512]; + /* char error_str[512]; */ int ok= 1; #ifdef USE_MATHUTILS @@ -526,21 +539,10 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v return -1; } /* done getting the length */ - - /* for arrays we have a limited number of types */ - switch (type) { - case PROP_BOOLEAN: - ok= pyrna_py_to_boolean_array(value, ptr, prop, data, error_str, sizeof(error_str)); - break; - case PROP_INT: - ok= pyrna_py_to_int_array(value, ptr, prop, data, error_str, sizeof(error_str)); - break; - case PROP_FLOAT: - ok= pyrna_py_to_float_array(value, ptr, prop, data, error_str, sizeof(error_str)); - break; - } + ok= pyrna_py_to_array(ptr, prop, data, value, error_prefix); + if (!ok) { - PyErr_Format(PyExc_AttributeError, "%.200s %s", error_prefix, error_str); + /* PyErr_Format(PyExc_AttributeError, "%.200s %s", error_prefix, error_str); */ return -1; } } @@ -733,82 +735,84 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v return 0; } -static PyObject * pyrna_prop_to_py_index(PointerRNA *ptr, PropertyRNA *prop, int index) +static PyObject * pyrna_prop_to_py_index(BPy_PropertyRNA *self, int index) { - PyObject *ret; - int type = RNA_property_type(prop); - - /* see if we can coorce into a python type - PropertyType */ - switch (type) { - case PROP_BOOLEAN: - ret = PyBool_FromLong( RNA_property_boolean_get_index(ptr, prop, index) ); - break; - case PROP_INT: - ret = PyLong_FromSsize_t( (Py_ssize_t)RNA_property_int_get_index(ptr, prop, index) ); - break; - case PROP_FLOAT: - ret = PyFloat_FromDouble( RNA_property_float_get_index(ptr, prop, index) ); - break; - default: - PyErr_SetString(PyExc_AttributeError, "not an array type"); - ret = NULL; - break; - } - - return ret; + return pyrna_py_from_array_index(self, index); } -static int pyrna_py_to_prop_index(PointerRNA *ptr, PropertyRNA *prop, int index, PyObject *value) +static int pyrna_py_to_prop_index(BPy_PropertyRNA *self, int index, PyObject *value) { int ret = 0; + int totdim; + PointerRNA *ptr= &self->ptr; + PropertyRNA *prop= self->prop; int type = RNA_property_type(prop); - - /* see if we can coorce into a python type - PropertyType */ - switch (type) { - case PROP_BOOLEAN: - { - int param = PyObject_IsTrue( value ); + + totdim= RNA_property_array_dimension(prop, NULL); + + if (totdim > 1) { + /* char error_str[512]; */ + if (!pyrna_py_to_array_index(&self->ptr, self->prop, self->arraydim, self->arrayoffset, index, value, "")) { + /* PyErr_SetString(PyExc_AttributeError, error_str); */ + ret= -1; + } + } + else { + /* see if we can coorce into a python type - PropertyType */ + switch (type) { + case PROP_BOOLEAN: + { + int param = PyObject_IsTrue( value ); - if( param < 0 ) { - PyErr_SetString(PyExc_TypeError, "expected True/False or 0/1"); + if( param < 0 ) { + PyErr_SetString(PyExc_TypeError, "expected True/False or 0/1"); + ret = -1; + } else { + RNA_property_boolean_set_index(ptr, prop, index, param); + } + break; + } + case PROP_INT: + { + int param = PyLong_AsSsize_t(value); + if (PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "expected an int type"); + ret = -1; + } else { + RNA_property_int_set_index(ptr, prop, index, param); + } + break; + } + case PROP_FLOAT: + { + float param = PyFloat_AsDouble(value); + if (PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, "expected a float type"); + ret = -1; + } else { + RNA_property_float_set_index(ptr, prop, index, param); + } + break; + } + default: + PyErr_SetString(PyExc_AttributeError, "not an array type"); ret = -1; - } else { - RNA_property_boolean_set_index(ptr, prop, index, param); + break; } - break; - } - case PROP_INT: - { - int param = PyLong_AsSsize_t(value); - if (PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, "expected an int type"); - ret = -1; - } else { - RNA_property_int_set_index(ptr, prop, index, param); - } - break; - } - case PROP_FLOAT: - { - float param = PyFloat_AsDouble(value); - if (PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, "expected a float type"); - ret = -1; - } else { - RNA_property_float_set_index(ptr, prop, index, param); - } - break; - } - default: - PyErr_SetString(PyExc_AttributeError, "not an array type"); - ret = -1; - break; } return ret; } //---------------sequence------------------------------------------- +static int pyrna_prop_array_length(BPy_PropertyRNA *self) +{ + if (RNA_property_array_dimension(self->prop, NULL) > 1) + return RNA_property_multidimensional_array_length(&self->ptr, self->prop, self->arraydim); + else + return RNA_property_array_length(&self->ptr, self->prop); +} + static Py_ssize_t pyrna_prop_len( BPy_PropertyRNA * self ) { Py_ssize_t len; @@ -816,10 +820,10 @@ static Py_ssize_t pyrna_prop_len( BPy_PropertyRNA * self ) if (RNA_property_type(self->prop) == PROP_COLLECTION) { len = RNA_property_collection_length(&self->ptr, self->prop); } else { - len = RNA_property_array_length(&self->ptr, self->prop); + len = pyrna_prop_array_length(self); if (len==0) { /* not an array*/ - PyErr_SetString(PyExc_AttributeError, "len() only available for collection RNA types"); + PyErr_SetString(PyExc_AttributeError, "len() only available for collection and array RNA types"); return -1; } } @@ -840,14 +844,15 @@ static PyObject *prop_subscript_collection_int(BPy_PropertyRNA * self, int keynu PyErr_Format(PyExc_IndexError, "index %d out of range", keynum); return NULL; } + static PyObject *prop_subscript_array_int(BPy_PropertyRNA * self, int keynum) { - int len= RNA_property_array_length(&self->ptr, self->prop); + int len= pyrna_prop_array_length(self); if(keynum < 0) keynum += len; if(keynum >= 0 && keynum < len) - return pyrna_prop_to_py_index(&self->ptr, self->prop, keynum); + return pyrna_prop_to_py_index(self, keynum); PyErr_Format(PyExc_IndexError, "index %d out of range", keynum); return NULL; @@ -894,7 +899,7 @@ static PyObject *prop_subscript_array_slice(BPy_PropertyRNA * self, int start, i start = MIN2(start,stop); /* values are clamped from PySlice_GetIndicesEx */ for(count = start; count < stop; count++) - PyList_SetItem(list, count - start, pyrna_prop_to_py_index(&self->ptr, self->prop, count)); + PyList_SetItem(list, count - start, pyrna_prop_to_py_index(self, count)); return list; } @@ -947,8 +952,8 @@ static PyObject *prop_subscript_array(BPy_PropertyRNA * self, PyObject *key) return prop_subscript_array_int(self, PyLong_AsSsize_t(key)); } else if (PySlice_Check(key)) { - int len= RNA_property_array_length(&self->ptr, self->prop); Py_ssize_t start, stop, step, slicelength; + int len = pyrna_prop_array_length(self); if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0) return NULL; @@ -974,13 +979,12 @@ static PyObject *pyrna_prop_subscript( BPy_PropertyRNA * self, PyObject *key ) { if (RNA_property_type(self->prop) == PROP_COLLECTION) { return prop_subscript_collection(self, key); - } else if (RNA_property_array_length(&self->ptr, self->prop)) { /* arrays are currently fixed length, zero length means its not an array */ + } else if (RNA_property_array_length(&self->ptr, self->prop)) { /* zero length means its not an array */ return prop_subscript_array(self, key); - } else { - PyErr_SetString(PyExc_TypeError, "rna type is not an array or a collection"); - return NULL; - } + } + PyErr_SetString(PyExc_TypeError, "rna type is not an array or a collection"); + return NULL; } static int prop_subscript_ass_array_slice(BPy_PropertyRNA * self, int begin, int end, PyObject *value) @@ -991,7 +995,7 @@ static int prop_subscript_ass_array_slice(BPy_PropertyRNA * self, int begin, int begin = MIN2(begin,end); for(count = begin; count < end; count++) { - if(pyrna_py_to_prop_index(&self->ptr, self->prop, count - begin, value) == -1) { + if(pyrna_py_to_prop_index(self, count - begin, value) == -1) { /* TODO - this is wrong since some values have been assigned... will need to fix that */ return -1; /* pyrna_struct_CreatePyObject should set the error */ } @@ -1002,13 +1006,12 @@ static int prop_subscript_ass_array_slice(BPy_PropertyRNA * self, int begin, int static int prop_subscript_ass_array_int(BPy_PropertyRNA * self, int keynum, PyObject *value) { - - int len= RNA_property_array_length(&self->ptr, self->prop); + int len= pyrna_prop_array_length(self); if(keynum < 0) keynum += len; if(keynum >= 0 && keynum < len) - return pyrna_py_to_prop_index(&self->ptr, self->prop, keynum, value); + return pyrna_py_to_prop_index(self, keynum, value); PyErr_SetString(PyExc_IndexError, "out of range"); return -1; @@ -1682,7 +1685,7 @@ PyObject *pyrna_prop_iter(BPy_PropertyRNA *self) if (ret==NULL) { /* collection did not work, try array */ - int len = RNA_property_array_length(&self->ptr, self->prop); + int len = pyrna_prop_array_length(self); if (len) { int i; @@ -1690,7 +1693,7 @@ PyObject *pyrna_prop_iter(BPy_PropertyRNA *self) ret = PyList_New(len); for (i=0; i < len; i++) { - PyList_SET_ITEM(ret, i, pyrna_prop_to_py_index(&self->ptr, self->prop, i)); + PyList_SET_ITEM(ret, i, pyrna_prop_to_py_index(self, i)); } } } @@ -1781,6 +1784,8 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data) /* resolve the array from a new pytype */ ret = PyTuple_New(len); + /* kazanbas: TODO make multidim sequences here */ + switch (type) { case PROP_BOOLEAN: for(a=0; aptr = *ptr; pyrna->prop = prop; + + pyrna->arraydim= 0; + pyrna->arrayoffset= 0; return ( PyObject * ) pyrna; } diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index d65849ad8a4..d006b168f45 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -55,6 +55,10 @@ typedef struct { PyObject_HEAD /* required python macro */ PointerRNA ptr; PropertyRNA *prop; + + /* Arystan: this is a hack to allow sub-item r/w access like: face.uv[n][m] */ + int arraydim; /* array dimension, e.g: 0 for face.uv, 2 for face.uv[n][m], etc. */ + int arrayoffset; /* array first item offset, e.g. if face.uv is [4][2], arrayoffset for face.uv[n] is 2n */ } BPy_PropertyRNA; /* cheap trick */ @@ -92,8 +96,11 @@ void pyrna_alloc_types(void); void pyrna_free_types(void); /* primitive type conversion */ -int pyrna_py_to_boolean_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size); -int pyrna_py_to_int_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size); -int pyrna_py_to_float_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size); +int pyrna_py_to_array(PointerRNA *ptr, PropertyRNA *prop, char *param_data, PyObject *py, const char *error_prefix); +int pyrna_py_to_array_index(PointerRNA *ptr, PropertyRNA *prop, int arraydim, int arrayoffset, int index, PyObject *py, const char *error_prefix); + +PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop); +PyObject *pyrna_py_from_array_index(BPy_PropertyRNA *self, int index); +PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop); #endif diff --git a/tools/btools.py b/tools/btools.py index 580a457e3a0..7cadab992b8 100755 --- a/tools/btools.py +++ b/tools/btools.py @@ -91,7 +91,7 @@ def validate_arguments(args, bc): 'BF_BSC', 'BF_CONFIG', 'BF_PRIORITYLIST', 'BF_BUILDINFO','CC', 'CXX', 'BF_QUICKDEBUG', 'BF_LISTDEBUG', 'LCGDIR', 'BF_X264_CONFIG', 'BF_XVIDCORE_CONFIG', - 'BF_DOCDIR'] + 'BF_DOCDIR', 'BF_UNIT_TEST'] okdict = {} @@ -386,7 +386,9 @@ def read_opts(cfg, args): ('BF_CONFIG', 'SCons python config file used to set default options', 'user_config.py'), ('BF_NUMJOBS', 'Number of build processes to spawn', '1'), - ('BF_MSVS', 'Generate MSVS project files and solution', False) + ('BF_MSVS', 'Generate MSVS project files and solution', False), + + (BoolVariable('BF_UNIT_TEST', 'Build with unit test support.', False)) ) # end of opts.AddOptions()