Implemented dynamic and multidimensional array support in RNA.
Example code: http://www.pasteall.org/7332/c. New API functions: http://www.pasteall.org/7330/c. Maximum number of dimensions is currently limited to 3, but can be increased arbitrarily if needed. What this means for ID property access: * MeshFace.verts - dynamic array, size 3 or 4 depending on MFace.v4 * MeshTextureFace.uv - dynamic, 2-dimensional array, size depends on MFace.v4 * Object.matrix - 2-dimensional array What this means for functions: * more intuitive API possibility, for example: Mesh.add_vertices([(x, y, z), (x, y, z), ...]) Mesh.add_faces([(1, 2, 3), (4, 5, 6), ...]) Python part is not complete yet, e.g. it is possible to: MeshFace.verts = (1, 2, 3) # even if Mesh.verts is (1, 2, 3, 4) and vice-versa MeshTextureFace.uv = [(0.0, 0.0)] * 4 # only if a corresponding MFace is a quad but the following won't work: MeshTextureFace.uv[3] = (0.0, 0.0) # setting uv[3] modifies MTFace.uv[1][0] instead of MTFace.uv[3]
This commit is contained in:
235
source/blender/python/intern/bpy_array.c
Normal file
235
source/blender/python/intern/bpy_array.c
Normal file
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
*
|
||||
*
|
||||
* ***** 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 *****
|
||||
*/
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
#include "bpy_rna.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
typedef void (*ItemConvertFunc)(PyObject *, char *);
|
||||
typedef int (*ItemTypeCheckFunc)(PyObject *);
|
||||
typedef void (*RNA_SetArrayFunc)(PointerRNA *, PropertyRNA *, const char *);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int i;
|
||||
if (dim < totdim) {
|
||||
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);
|
||||
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]);
|
||||
ok= 0;
|
||||
}
|
||||
|
||||
if (!pyrna_validate_array(item, dim + 1, totdim, dim_size, check_item_type, item_type_str, error_str, error_str_size)) {
|
||||
ok= 0;
|
||||
}
|
||||
|
||||
Py_DECREF(item);
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Returns the number of items in a single- or multi-dimensional sequence. */
|
||||
static int pyrna_count_items(PyObject *seq)
|
||||
{
|
||||
int totitem= 0;
|
||||
|
||||
if (PySequence_Check(seq)) {
|
||||
int i;
|
||||
for (i= 0; i < PySequence_Length(seq); i++) {
|
||||
PyObject *item= PySequence_GetItem(seq, i);
|
||||
totitem += pyrna_count_items(item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
totitem= 1;
|
||||
|
||||
return totitem;
|
||||
}
|
||||
|
||||
static int pyrna_apply_array_length(PointerRNA *ptr, PropertyRNA *prop, int totitem, char *error_str, int error_str_size)
|
||||
{
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
unsigned int i;
|
||||
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);
|
||||
}
|
||||
else {
|
||||
convert_item(item, data);
|
||||
data += item_size;
|
||||
}
|
||||
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned short totdim, dim_size[100];
|
||||
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))
|
||||
return 0;
|
||||
|
||||
if (totitem) {
|
||||
if (!param_data || RNA_property_flag(prop) & PROP_DYNAMIC)
|
||||
data= MEM_callocN(item_size * totitem, "pyrna primitive type array");
|
||||
else
|
||||
data= param_data;
|
||||
|
||||
pyrna_py_to_array(py, 1, totdim, data, item_size, convert_item);
|
||||
|
||||
if (param_data) {
|
||||
if (RNA_property_flag(prop) & PROP_DYNAMIC) {
|
||||
/* not freeing allocated mem, RNA_parameter_list_free will do this */
|
||||
*(char**)param_data= data;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* NULL can only pass through in case RNA property arraylength is 0 (impossible?) */
|
||||
rna_set_array(ptr, prop, data);
|
||||
MEM_freeN(data);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pyrna_py_to_float(PyObject *py, char *data)
|
||||
{
|
||||
*(float*)data= (float)PyFloat_AsDouble(py);
|
||||
}
|
||||
|
||||
static void pyrna_py_to_int(PyObject *py, char *data)
|
||||
{
|
||||
*(int*)data= (int)PyLong_AsSsize_t(py);
|
||||
}
|
||||
|
||||
static void pyrna_py_to_boolean(PyObject *py, char *data)
|
||||
{
|
||||
*(int*)data= (int)PyObject_IsTrue(py);
|
||||
}
|
||||
|
||||
static int py_float_check(PyObject *py)
|
||||
{
|
||||
return PyFloat_Check(py);
|
||||
}
|
||||
|
||||
static int py_int_check(PyObject *py)
|
||||
{
|
||||
return PyLong_Check(py);
|
||||
}
|
||||
|
||||
static int py_bool_check(PyObject *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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
int pyrna_py_to_int_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
int pyrna_py_to_boolean_array(PyObject *py, PointerRNA *ptr, PropertyRNA *prop, char *param_data, char *error_str, int error_str_size)
|
||||
{
|
||||
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);
|
||||
}
|
@@ -246,7 +246,7 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
|
||||
{
|
||||
PyObject *ret;
|
||||
int type = RNA_property_type(prop);
|
||||
int len = RNA_property_array_length(prop);
|
||||
int len = RNA_property_array_length(ptr, prop);
|
||||
|
||||
if (len > 0) {
|
||||
/* resolve the array from a new pytype */
|
||||
@@ -469,128 +469,43 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *v
|
||||
{
|
||||
/* XXX hard limits should be checked here */
|
||||
int type = RNA_property_type(prop);
|
||||
int len = RNA_property_array_length(prop);
|
||||
int len = RNA_property_array_length(ptr, prop);
|
||||
|
||||
if (len > 0) {
|
||||
PyObject *item;
|
||||
int py_len = -1;
|
||||
int i;
|
||||
|
||||
char error_str[512];
|
||||
int ok= 1;
|
||||
|
||||
#ifdef USE_MATHUTILS
|
||||
if(MatrixObject_Check(value)) {
|
||||
MatrixObject *mat = (MatrixObject*)value;
|
||||
if(!BaseMath_ReadCallback(mat))
|
||||
return -1;
|
||||
|
||||
py_len = mat->rowSize * mat->colSize;
|
||||
} else /* continue... */
|
||||
#endif
|
||||
if (PySequence_Check(value)) {
|
||||
py_len= (int)PySequence_Length(value);
|
||||
}
|
||||
else {
|
||||
if (!PySequence_Check(value)) {
|
||||
PyErr_Format(PyExc_TypeError, "%.200s RNA array assignment expected a sequence instead of %.200s instance.", error_prefix, Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
/* done getting the length */
|
||||
|
||||
if (py_len != len) {
|
||||
PyErr_Format(PyExc_TypeError, "%.200s python sequence length %d did not match the RNA array length %d.", error_prefix, py_len, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* for arrays we have a limited number of types */
|
||||
switch (type) {
|
||||
case PROP_BOOLEAN:
|
||||
{
|
||||
int *param_arr;
|
||||
if(data) param_arr= (int*)data;
|
||||
else param_arr= MEM_mallocN(sizeof(int) * len, "pyrna bool array");
|
||||
|
||||
|
||||
/* collect the variables before assigning, incase one of them is incorrect */
|
||||
for (i=0; i<len; i++) {
|
||||
item = PySequence_GetItem(value, i);
|
||||
param_arr[i] = PyObject_IsTrue( item );
|
||||
Py_DECREF(item);
|
||||
|
||||
if (param_arr[i] < 0) {
|
||||
if(data==NULL)
|
||||
MEM_freeN(param_arr);
|
||||
PyErr_Format(PyExc_AttributeError, "%.200s one or more of the values in the sequence is not a boolean", error_prefix);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if(data==NULL) {
|
||||
RNA_property_boolean_set_array(ptr, prop, param_arr);
|
||||
MEM_freeN(param_arr);
|
||||
}
|
||||
|
||||
ok= pyrna_py_to_boolean_array(value, ptr, prop, data, error_str, sizeof(error_str));
|
||||
break;
|
||||
}
|
||||
case PROP_INT:
|
||||
{
|
||||
int *param_arr;
|
||||
if(data) param_arr= (int*)data;
|
||||
else param_arr= MEM_mallocN(sizeof(int) * len, "pyrna int array");
|
||||
|
||||
|
||||
/* collect the variables */
|
||||
for (i=0; i<len; i++) {
|
||||
item = PySequence_GetItem(value, i);
|
||||
param_arr[i] = (int)PyLong_AsSsize_t(item); /* deal with any errors later */
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
if(data==NULL)
|
||||
MEM_freeN(param_arr);
|
||||
PyErr_Format(PyExc_AttributeError, "%.200s one or more of the values in the sequence could not be used as an int", error_prefix);
|
||||
return -1;
|
||||
}
|
||||
if(data==NULL) {
|
||||
RNA_property_int_set_array(ptr, prop, param_arr);
|
||||
MEM_freeN(param_arr);
|
||||
}
|
||||
ok= pyrna_py_to_int_array(value, ptr, prop, data, error_str, sizeof(error_str));
|
||||
break;
|
||||
}
|
||||
case PROP_FLOAT:
|
||||
{
|
||||
float *param_arr;
|
||||
if(data) param_arr = (float*)data;
|
||||
else param_arr = MEM_mallocN(sizeof(float) * len, "pyrna float array");
|
||||
|
||||
|
||||
#ifdef USE_MATHUTILS
|
||||
if(MatrixObject_Check(value) && RNA_property_subtype(prop) == PROP_MATRIX) {
|
||||
MatrixObject *mat = (MatrixObject*)value;
|
||||
memcpy(param_arr, mat->contigPtr, sizeof(float) * len);
|
||||
} else /* continue... */
|
||||
#endif
|
||||
{
|
||||
/* collect the variables */
|
||||
for (i=0; i<len; i++) {
|
||||
item = PySequence_GetItem(value, i);
|
||||
param_arr[i] = (float)PyFloat_AsDouble(item); /* deal with any errors later */
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
if(data==NULL)
|
||||
MEM_freeN(param_arr);
|
||||
PyErr_Format(PyExc_AttributeError, "%.200s one or more of the values in the sequence could not be used as a float", error_prefix);
|
||||
return -1;
|
||||
}
|
||||
if(data==NULL) {
|
||||
RNA_property_float_set_array(ptr, prop, param_arr);
|
||||
MEM_freeN(param_arr);
|
||||
}
|
||||
ok= pyrna_py_to_float_array(value, ptr, prop, data, error_str, sizeof(error_str));
|
||||
break;
|
||||
}
|
||||
if (!ok) {
|
||||
PyErr_Format(PyExc_AttributeError, "%.200s %s", error_prefix, error_str);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
/* Normal Property (not an array) */
|
||||
|
||||
/* see if we can coorce into a python type - PropertyType */
|
||||
@@ -862,7 +777,7 @@ 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->prop);
|
||||
len = RNA_property_array_length(&self->ptr, self->prop);
|
||||
|
||||
if (len==0) { /* not an array*/
|
||||
PyErr_SetString(PyExc_AttributeError, "len() only available for collection RNA types");
|
||||
@@ -888,7 +803,7 @@ static PyObject *prop_subscript_collection_int(BPy_PropertyRNA * self, int keynu
|
||||
}
|
||||
static PyObject *prop_subscript_array_int(BPy_PropertyRNA * self, int keynum)
|
||||
{
|
||||
int len= RNA_property_array_length(self->prop);
|
||||
int len= RNA_property_array_length(&self->ptr, self->prop);
|
||||
|
||||
if(keynum < 0) keynum += len;
|
||||
|
||||
@@ -993,7 +908,7 @@ 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->prop);
|
||||
int len= RNA_property_array_length(&self->ptr, self->prop);
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
|
||||
@@ -1020,7 +935,7 @@ 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->prop)) { /* arrays are currently fixed length, zero length means its not an array */
|
||||
} else if (RNA_property_array_length(&self->ptr, self->prop)) { /* arrays are currently fixed length, 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");
|
||||
@@ -1049,7 +964,7 @@ 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->prop);
|
||||
int len= RNA_property_array_length(&self->ptr, self->prop);
|
||||
|
||||
if(keynum < 0) keynum += len;
|
||||
|
||||
@@ -1083,7 +998,7 @@ static int pyrna_prop_ass_subscript( BPy_PropertyRNA * self, PyObject *key, PyOb
|
||||
return prop_subscript_ass_array_int(self, i, value);
|
||||
}
|
||||
else if (PySlice_Check(key)) {
|
||||
int len= RNA_property_array_length(self->prop);
|
||||
int len= RNA_property_array_length(&self->ptr, self->prop);
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
if (PySlice_GetIndicesEx((PySliceObject*)key, len, &start, &stop, &step, &slicelength) < 0)
|
||||
@@ -1496,7 +1411,7 @@ static void foreach_attr_type( BPy_PropertyRNA *self, char *attr,
|
||||
RNA_PROP_BEGIN(&self->ptr, itemptr, self->prop) {
|
||||
prop = RNA_struct_find_property(&itemptr, attr);
|
||||
*raw_type= RNA_property_raw_type(prop);
|
||||
*attr_tot = RNA_property_array_length(prop);
|
||||
*attr_tot = RNA_property_array_length(&itemptr, prop);
|
||||
*attr_signed= (RNA_property_subtype(prop)==PROP_UNSIGNED) ? FALSE:TRUE;
|
||||
break;
|
||||
}
|
||||
@@ -1535,7 +1450,7 @@ static int foreach_parse_args(
|
||||
if (RNA_property_type(self->prop) == PROP_COLLECTION)
|
||||
array_tot = RNA_property_collection_length(&self->ptr, self->prop);
|
||||
else
|
||||
array_tot = RNA_property_array_length(self->prop);
|
||||
array_tot = RNA_property_array_length(&self->ptr, self->prop);
|
||||
|
||||
|
||||
target_tot= array_tot * (*attr_tot);
|
||||
@@ -1728,7 +1643,7 @@ PyObject *pyrna_prop_iter(BPy_PropertyRNA *self)
|
||||
|
||||
if (ret==NULL) {
|
||||
/* collection did not work, try array */
|
||||
int len = RNA_property_array_length(self->prop);
|
||||
int len = RNA_property_array_length(&self->ptr, self->prop);
|
||||
|
||||
if (len) {
|
||||
int i;
|
||||
@@ -1819,7 +1734,7 @@ PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data)
|
||||
{
|
||||
PyObject *ret;
|
||||
int type = RNA_property_type(prop);
|
||||
int len = RNA_property_array_length(prop);
|
||||
int len = RNA_property_array_length(ptr, prop);
|
||||
|
||||
int a;
|
||||
|
||||
|
@@ -91,4 +91,9 @@ PyObject *pyrna_basetype_unregister(PyObject *self, PyObject *args);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user