[Bf-blender-cvs] [bc0a7d3fae5] master: PyAPI: support multi-dimensional arrays for bpy.props vector types
Campbell Barton
noreply at git.blender.org
Thu Jul 29 03:10:15 CEST 2021
Commit: bc0a7d3fae5cfbe76ff84b76cb0ce48fe46adea5
Author: Campbell Barton
Date: Thu Jul 29 10:52:11 2021 +1000
Branches: master
https://developer.blender.org/rBbc0a7d3fae5cfbe76ff84b76cb0ce48fe46adea5
PyAPI: support multi-dimensional arrays for bpy.props vector types
- Multi-dimensional boolean, int and float vector types are supported.
- A sequence of int's for the "size" is used to declare dimensions.
- Nested sequences are required for default arguments.
Now it's possible to define matrix properties, for e.g:
bpy.props.FloatVectorProperty(size=(4, 4), subtype='MATRIX')
===================================================================
M source/blender/python/intern/bpy_props.c
M tests/python/bl_pyapi_prop_array.py
===================================================================
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index b63e7863f79..6d5ca209866 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -491,6 +491,145 @@ static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_over
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Multi-Dimensional Property Utilities
+ * \{ */
+
+struct BPYPropArrayLength {
+ int len_total;
+ /** Ignore `dims` when `dims_len == 0`. */
+ int dims[RNA_MAX_ARRAY_DIMENSION];
+ int dims_len;
+};
+
+/**
+ * Use with PyArg_ParseTuple's "O&" formatting.
+ */
+static int bpy_prop_array_length_parse(PyObject *o, void *p)
+{
+ struct BPYPropArrayLength *array_len_info = p;
+
+ if (PyLong_CheckExact(o)) {
+ int size;
+ if (((size = PyLong_AsLong(o)) == -1)) {
+ PyErr_Format(
+ PyExc_ValueError, "expected number or sequence of numbers, got %s", Py_TYPE(o)->tp_name);
+ return 0;
+ }
+ if (size < 1 || size > PYRNA_STACK_ARRAY) {
+ PyErr_Format(
+ PyExc_TypeError, "(size=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY), size);
+ return 0;
+ }
+ array_len_info->len_total = size;
+
+ /* Don't use this value. */
+ array_len_info->dims_len = 0;
+ }
+ else {
+ PyObject *seq_fast;
+ if (!(seq_fast = PySequence_Fast(o, "size must be a number of a sequence of numbers"))) {
+ return 0;
+ }
+ const int seq_len = PySequence_Fast_GET_SIZE(seq_fast);
+ if (seq_len < 1 || seq_len > RNA_MAX_ARRAY_DIMENSION) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "(len(size)=%d) length must be between 1 and " STRINGIFY(RNA_MAX_ARRAY_DIMENSION),
+ seq_len);
+ Py_DECREF(seq_fast);
+ return 0;
+ }
+
+ PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
+ for (int i = 0; i < seq_len; i++) {
+ int size;
+ if (((size = PyLong_AsLong(seq_items[i])) == -1)) {
+ Py_DECREF(seq_fast);
+ PyErr_Format(PyExc_ValueError,
+ "expected number in sequence, got %s at index %d",
+ Py_TYPE(o)->tp_name,
+ i);
+ return 0;
+ }
+ if (size < 1 || size > PYRNA_STACK_ARRAY) {
+ Py_DECREF(seq_fast);
+ PyErr_Format(PyExc_TypeError,
+ "(size[%d]=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY),
+ i,
+ size);
+ return 0;
+ }
+
+ array_len_info->dims[i] = size;
+ array_len_info->dims_len = seq_len;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Return -1 on error.
+ */
+static int bpy_prop_array_from_py_with_dims(void *values,
+ size_t values_elem_size,
+ PyObject *py_values,
+ const struct BPYPropArrayLength *array_len_info,
+ const PyTypeObject *type,
+ const char *error_str)
+{
+ if (array_len_info->dims_len == 0) {
+ return PyC_AsArray(
+ values, values_elem_size, py_values, array_len_info->len_total, type, error_str);
+ }
+ const int *dims = array_len_info->dims;
+ const int dims_len = array_len_info->dims_len;
+ return PyC_AsArray_Multi(values, values_elem_size, py_values, dims, dims_len, type, error_str);
+}
+
+static bool bpy_prop_array_is_matrix_compatible_ex(int subtype,
+ const struct BPYPropArrayLength *array_len_info)
+{
+ return ((subtype == PROP_MATRIX) && (array_len_info->dims_len == 2) &&
+ ((array_len_info->dims[0] >= 2) && (array_len_info->dims[0] >= 4)) &&
+ ((array_len_info->dims[1] >= 2) && (array_len_info->dims[1] >= 4)));
+}
+
+static bool bpy_prop_array_is_matrix_compatible(PropertyRNA *prop,
+ const struct BPYPropArrayLength *array_len_info)
+{
+ BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
+ return bpy_prop_array_is_matrix_compatible_ex(RNA_property_subtype(prop), array_len_info);
+}
+
+/**
+ * Needed since the internal storage of matrices swaps row/column.
+ */
+static void bpy_prop_array_matrix_swap_row_column_vn_vn(
+ float *values_dst, const float *values_src, const struct BPYPropArrayLength *array_len_info)
+{
+ BLI_assert(values_dst != values_src);
+ const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1];
+ BLI_assert(dim0 <= 4 && dim1 <= 4);
+ for (int i = 0; i < dim0; i++) {
+ for (int j = 0; j < dim1; j++) {
+ values_dst[(j * dim0) + i] = values_src[(i * dim1) + j];
+ }
+ }
+}
+
+static void bpy_prop_array_matrix_swap_row_column_vn(
+ float *values, const struct BPYPropArrayLength *array_len_info)
+{
+ const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1];
+ BLI_assert(dim0 <= 4 && dim1 <= 4);
+ float values_orig[4 * 4];
+ memcpy(values_orig, values, sizeof(float) * (dim0 * dim1));
+ bpy_prop_array_matrix_swap_row_column_vn_vn(values, values_orig, array_len_info);
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Shared Property Callbacks
*
@@ -687,7 +826,10 @@ static void bpy_prop_boolean_array_get_fn(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
+ bool is_values_set = false;
int i, len = RNA_property_array_length(ptr, prop);
+ struct BPYPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -711,23 +853,26 @@ static void bpy_prop_boolean_array_get_fn(struct PointerRNA *ptr,
Py_DECREF(args);
- if (ret == NULL) {
- PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = false;
+ if (ret != NULL) {
+ if (bpy_prop_array_from_py_with_dims(values,
+ sizeof(*values),
+ ret,
+ &array_len_info,
+ &PyBool_Type,
+ "BoolVectorProperty get callback") == -1) {
+ PyC_Err_PrintWithFunc(py_func);
}
+ else {
+ is_values_set = true;
+ }
+ Py_DECREF(ret);
}
- else {
- if (PyC_AsArray(values, sizeof(*values), ret, len, &PyBool_Type, "BoolVectorProperty get: ") ==
- -1) {
- PyC_Err_PrintWithFunc(py_func);
- for (i = 0; i < len; i++) {
- values[i] = false;
- }
+ if (is_values_set == false) {
+ /* This is the flattened length for multi-dimensional arrays. */
+ for (i = 0; i < len; i++) {
+ values[i] = false;
}
- Py_DECREF(ret);
}
if (use_gil) {
@@ -753,6 +898,8 @@ static void bpy_prop_boolean_array_set_fn(struct PointerRNA *ptr,
bool use_gil;
const bool is_write_ok = pyrna_write_check();
const int len = RNA_property_array_length(ptr, prop);
+ struct BPYPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -772,7 +919,13 @@ static void bpy_prop_boolean_array_set_fn(struct PointerRNA *ptr,
self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEM(args, 0, self);
- py_values = PyC_Tuple_PackArray_Bool(values, len);
+ if (array_len_info.dims_len == 0) {
+ py_values = PyC_Tuple_PackArray_Bool(values, len);
+ }
+ else {
+ py_values = PyC_Tuple_PackArray_Multi_Bool(
+ values, array_len_info.dims, array_len_info.dims_len);
+ }
PyTuple_SET_ITEM(args, 1, py_values);
ret = PyObject_CallObject(py_func, args);
@@ -934,7 +1087,10 @@ static void bpy_prop_int_array_get_fn(struct PointerRNA *ptr,
PyGILState_STATE gilstate;
bool use_gil;
const bool is_write_ok = pyrna_write_check();
+ bool is_values_set = false;
int i, len = RNA_property_array_length(ptr, prop);
+ struct BPYPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -958,23 +1114,26 @@ static void bpy_prop_int_array_get_fn(struct PointerRNA *ptr,
Py_DECREF(args);
- if (ret == NULL) {
- PyC_Err_PrintWithFunc(py_func);
-
- for (i = 0; i < len; i++) {
- values[i] = 0;
+ if (ret != NULL) {
+ if (bpy_prop_array_from_py_with_dims(values,
+ sizeof(*values),
+ ret,
+ &array_len_info,
+ &PyLong_Type,
+ "IntVectorProperty get callback") == -1) {
+ PyC_Err_PrintWithFunc(py_func);
}
+ else {
+ is_values_set = true;
+ }
+ Py_DECREF(ret);
}
- else {
- if (PyC_AsArray(values, sizeof(*values), ret, len, &PyLong_Type, "IntVectorProperty get: ") ==
- -1) {
- PyC_Err_PrintWithFunc(py_func);
- for (i = 0; i < len; i++) {
- values[i] = 0;
- }
+ if (is_values_set == false) {
+ /* This is the flattened length for multi-dimensional arrays. */
+ for (i = 0; i < len; i++) {
+ values[i] = 0;
}
- Py_DECREF(ret);
}
if (use_gil) {
@@ -1000,6 +1159,8 @@ static void bpy_prop_int_array_set_fn(struct PointerRNA *ptr,
bool use_gil;
const bool is_write_ok = pyrna_write_check();
const int len = RNA_property_array_length(ptr, prop);
+ struct BPYPropArrayLength array_len_info = {.len_total = len};
+ array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims);
BLI_assert(prop_store != NULL);
@@ -1019,7 +1180,14 @@ static void bpy_prop_int_array_set_fn(struct PointerRNA *ptr,
self = pyrna_struct_as_instance(ptr);
PyTuple_SET_ITEM(args, 0, self);
- py_values = PyC_Tuple_PackArray_I32(values, len);
+ if (array_len_info.dims_len == 0) {
+ py_values = PyC_Tuple
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list