Changeset View
Changeset View
Standalone View
Standalone View
source/blender/python/mathutils/mathutils.c
| Show First 20 Lines • Show All 352 Lines • ▼ Show 20 Lines | int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix) | ||||
| else { | else { | ||||
| PyErr_Format(PyExc_TypeError, | PyErr_Format(PyExc_TypeError, | ||||
| "%.200s: expected a Euler, Quaternion or Matrix type, " | "%.200s: expected a Euler, Quaternion or Matrix type, " | ||||
| "found %.200s", error_prefix, Py_TYPE(value)->tp_name); | "found %.200s", error_prefix, Py_TYPE(value)->tp_name); | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| } | } | ||||
| /* Returns a new mathutils object based on a prototype PyObject. | |||||
| * The data pointer should have been allocated by Py_New prior | |||||
| * to calling this.*/ | |||||
| static PyObject *Mathutils_CreatePyObject_alloc(float *data, PyObject *prototype) | |||||
| { | |||||
| PyObject *self; | |||||
| if (VectorObject_Check(prototype)) { | |||||
| VectorObject *vec = BASE_MATH_NEW(VectorObject, vector_Type, Py_TYPE(prototype)); | |||||
| if (vec) { | |||||
| vec->size = ((VectorObject *)prototype)->size; | |||||
| } | |||||
| self = (PyObject *)vec; | |||||
| } | |||||
| else if (MatrixObject_Check(prototype)) { | |||||
| MatrixObject *mat = BASE_MATH_NEW(MatrixObject, matrix_Type, Py_TYPE(prototype)); | |||||
| if (mat) { | |||||
| mat->num_col = ((MatrixObject *)prototype)->num_col; | |||||
| mat->num_row = ((MatrixObject *)prototype)->num_row; | |||||
| } | |||||
| self = (PyObject *)mat; | |||||
| } | |||||
| else if (ColorObject_Check(prototype)) { | |||||
| self = (PyObject *)BASE_MATH_NEW(ColorObject, color_Type, Py_TYPE(prototype)); | |||||
| } | |||||
| else { | |||||
| PyMem_Free(data); | |||||
| return NULL; | |||||
| } | |||||
| if (self) { | |||||
| BaseMathObject *bmo = (BaseMathObject *)self; | |||||
| bmo->data = data; | |||||
| /* init callbacks as NULL */ | |||||
| bmo->cb_user = NULL; | |||||
| bmo->cb_type = bmo->cb_subtype = 0; | |||||
| bmo->flag = BASE_MATH_FLAG_DEFAULT; | |||||
| } | |||||
| else { | |||||
| PyMem_Free(data); | |||||
| } | |||||
| return self; | |||||
| } | |||||
| /* Returns the length of the underlying data for a | |||||
| * mathutils type.*/ | |||||
| static int mathutils_array_size(PyObject *value) | |||||
| { | |||||
| if (!BaseMathObject_CheckExact(value)) { | |||||
| return -1; | |||||
| } | |||||
| else if (VectorObject_Check(value)) { | |||||
| const VectorObject *vec = (VectorObject *)value; | |||||
| return vec->size; | |||||
| } | |||||
| else if (MatrixObject_Check(value)) { | |||||
| const MatrixObject *mat = (MatrixObject *)value; | |||||
| return (mat->num_col * mat->num_row); | |||||
| } | |||||
| else if (ColorObject_Check(value)) { | |||||
| /* macro for color size not available */ | |||||
| return 3; | |||||
| } | |||||
| else { | |||||
| /* all other mathutils types not supported */ | |||||
| return -1; | |||||
| } | |||||
| } | |||||
| /* Check if two PyObjects are the same mathutils types */ | |||||
| static bool mathutils_check_same_type(PyObject *val1, PyObject *val2) | |||||
| { | |||||
| if (VectorObject_Check(val1) && VectorObject_Check(val2)) { | |||||
| return true; | |||||
| } | |||||
| else if (MatrixObject_Check(val1) && MatrixObject_Check(val2)) { | |||||
| return true; | |||||
| } | |||||
| else if (ColorObject_Check(val1) && ColorObject_Check(val2)) { | |||||
| return true; | |||||
| } | |||||
| else { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| static bool mathutils_check_valid_types(PyObject *val1, PyObject *val2, int *r_size1, int *r_size2, | |||||
| bool *r_isfloat1, bool *r_isfloat2, | |||||
| float *r_float1, float *r_float2) | |||||
| { | |||||
| /* check val1 */ | |||||
| if ((*r_size1 = mathutils_array_size(val1)) == -1) { | |||||
| /* not a mathutils type */ | |||||
| if (!((*r_float1 = PyFloat_AsDouble(val1)) == -1.0f && PyErr_Occurred())) { | |||||
| *r_isfloat1 = true; | |||||
| *r_size1 = 0; | |||||
| } | |||||
| else { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* mathutils type */ | |||||
| *r_isfloat1 = false; | |||||
| } | |||||
| /* check val2 */ | |||||
| if ((*r_size2 = mathutils_array_size(val2)) == -1) { | |||||
| /* not a mathutils type */ | |||||
| if (!((*r_float2 = PyFloat_AsDouble(val2)) == -1.0f && PyErr_Occurred())) { | |||||
| *r_isfloat2 = true; | |||||
| *r_size2 = 0; | |||||
| } | |||||
| else { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| else { | |||||
| /* mathutils type */ | |||||
| *r_isfloat2 = false; | |||||
| } | |||||
| if (!(*r_isfloat1) && !(*r_isfloat2)) { | |||||
| if (!mathutils_check_same_type(val1, val2)) { | |||||
| return false; | |||||
| } | |||||
| } | |||||
| return ((*r_size1 > -1) && (*r_size2 > -1)); | |||||
| } | |||||
| static int mathutils_read_callback_check(PyObject *value, bool do_read_callback, bool do_read_for_write_callback) | |||||
| { | |||||
| if (!BaseMathObject_CheckExact(value)) { | |||||
| return -1; | |||||
| } | |||||
| BaseMathObject *bmo = (BaseMathObject *)value; | |||||
| int read_result = 0; | |||||
| int readforwrite_result = 0; | |||||
| if (do_read_callback) { | |||||
| read_result = BaseMath_ReadCallback(bmo); | |||||
| } | |||||
| if (do_read_for_write_callback) { | |||||
| readforwrite_result = BaseMath_ReadCallback_ForWrite(bmo); | |||||
| } | |||||
| return (read_result == -1 || readforwrite_result == -1) ? -1 : 1; | |||||
| } | |||||
| PyObject *mathtutils_generic_elem_op(PyObject *val1, PyObject *val2, Mathutils_Elem_Ops op) | |||||
| { | |||||
| bool is_float1, is_float2, is_scalar = false; | |||||
| int size1, size2, r_size; | |||||
| float float1, float2, scalar; | |||||
| float *array1 = NULL, *array2 = NULL, *array_base = NULL; | |||||
| float *r_array = NULL; | |||||
| PyObject *prototype_pyval = NULL; | |||||
| if (!mathutils_check_valid_types(val1, val2, &size1, &size2, &is_float1, &is_float2, &float1, &float2)) { | |||||
| PyErr_Format(PyExc_TypeError, | |||||
| "Element-wise operation: " | |||||
| "not supported between '%.200s' and '%.200s' types", | |||||
| Py_TYPE(val1)->tp_name, Py_TYPE(val2)->tp_name); | |||||
| return NULL; | |||||
| } | |||||
| /* determine the size of the return array */ | |||||
| r_size = MAX2(size1, size2); | |||||
| if ((!is_float1 && !is_float2) && (size1 != size2)) { | |||||
| PyErr_SetString(PyExc_ValueError, | |||||
| "Element-wise operation: " | |||||
| "values must have the same dimensions for this operation"); | |||||
| return NULL; | |||||
| } | |||||
| if (!is_float1) { | |||||
| if (mathutils_read_callback_check(val1, true, false) == -1) { | |||||
| return NULL; | |||||
| } | |||||
| array1 = ((BaseMathObject *)val1)->data; | |||||
| prototype_pyval = val1; | |||||
| } | |||||
| if (!is_float2) { | |||||
| if (mathutils_read_callback_check(val2, true, false) == -1) { | |||||
| return NULL; | |||||
| } | |||||
| array2 = ((BaseMathObject *)val2)->data; | |||||
| prototype_pyval = val2; | |||||
| } | |||||
| if (is_float1) { | |||||
| is_scalar = true; | |||||
| scalar = float1; | |||||
| array_base = array2; | |||||
| } | |||||
| else if (is_float2) { | |||||
| is_scalar = true; | |||||
| scalar = float2; | |||||
| array_base = array1; | |||||
| } | |||||
| r_array = PyMem_New(float, r_size); | |||||
| if (UNLIKELY(!r_array)) { | |||||
| PyErr_SetString(PyExc_MemoryError, | |||||
| "Mathutils: " | |||||
| "problem allocating memory"); | |||||
| return NULL; | |||||
| } | |||||
| if (is_scalar) { | |||||
| void (*func_ptr_array_float)(float*, const float*, const int, const float) = NULL; | |||||
| float *tar = NULL, *src = NULL; | |||||
| int i; | |||||
| switch (op) { | |||||
| case ADD: | |||||
| copy_vn_fl(r_array, r_size, scalar); | |||||
| add_vn_vn(r_array, array_base, r_size); | |||||
| break; | |||||
| case SUB: | |||||
| if (is_float1) { | |||||
| copy_vn_fl(r_array, r_size, scalar); | |||||
| sub_vn_vn(r_array, array_base, r_size); | |||||
| } | |||||
| else if (is_float2){ | |||||
| memcpy(r_array, array_base, r_size * sizeof(float)); | |||||
| add_vn_fl(r_array, r_size, -1.0f * scalar); | |||||
| } | |||||
| else { | |||||
| BLI_assert(false); | |||||
| } | |||||
| break; | |||||
| case MUL: | |||||
| func_ptr_array_float = &mul_vn_vn_fl; | |||||
| break; | |||||
| case DIV: | |||||
| if (is_float1) { | |||||
| /* float / array */ | |||||
| tar = r_array + (r_size - 1); | |||||
| src = array_base + (r_size - 1); | |||||
| i = r_size; | |||||
| while (i--) { | |||||
| if (*src == 0.0f) { | |||||
| PyMem_Free(r_array); | |||||
| PyErr_SetString(PyExc_ZeroDivisionError, | |||||
| "Element-wise division: " | |||||
| "divide by zero error"); | |||||
| return NULL; | |||||
| } | |||||
| *(tar--) = scalar / *(src--); | |||||
| } | |||||
| } | |||||
| else if (is_float2) { | |||||
| /* array / float */ | |||||
| if (scalar == 0.0f) { | |||||
| PyErr_SetString(PyExc_ZeroDivisionError, | |||||
| "Element-wise division: " | |||||
| "divide by zero error"); | |||||
| return NULL; | |||||
| } | |||||
| scalar = 1.0f / scalar; | |||||
| func_ptr_array_float = &mul_vn_vn_fl; | |||||
| } | |||||
| else { | |||||
| BLI_assert(false); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| return NULL; | |||||
| break; | |||||
| } | |||||
| if (func_ptr_array_float) { | |||||
| (*func_ptr_array_float)(r_array, array_base, r_size, scalar); | |||||
| } | |||||
| } | |||||
| else { | |||||
| void (*func_ptr_array_array)(float*, const float*, const float*, const int) = NULL; | |||||
| float *tar = NULL, *src_a = NULL, *src_b = NULL; | |||||
| int i; | |||||
| switch (op) { | |||||
| case ADD: | |||||
| func_ptr_array_array = &add_vn_vnvn; | |||||
| break; | |||||
| case SUB: | |||||
| func_ptr_array_array = &sub_vn_vnvn; | |||||
| break; | |||||
| case MUL: | |||||
| #ifdef USE_MATHUTILS_ELEM_MUL | |||||
| func_ptr_array_array = &mul_vn_vnvn; | |||||
| #else | |||||
| PyErr_Format(PyExc_TypeError, | |||||
| "Element-wise multiplication not yet supported. " | |||||
| "Use @ for matrix multiplication."); | |||||
| return NULL; | |||||
| #endif | |||||
| break; | |||||
| case DIV: | |||||
| tar = r_array + (r_size - 1); | |||||
| src_a = array1 + (r_size - 1); | |||||
| src_b = array2 + (r_size - 1); | |||||
| i = r_size; | |||||
| while (i--) { | |||||
| if (*src_b == 0.0f) { | |||||
| PyMem_Free(r_array); | |||||
| PyErr_SetString(PyExc_ZeroDivisionError, | |||||
| "Element-wise division: " | |||||
| "divide by zero error"); | |||||
| return NULL; | |||||
| } | |||||
| *(tar--) = *(src_a--) / *(src_b--); | |||||
| } | |||||
| break; | |||||
| default: | |||||
| return NULL; | |||||
| break; | |||||
| } | |||||
| if (func_ptr_array_array) { | |||||
| (*func_ptr_array_array)(r_array, array1, array2, r_size); | |||||
| } | |||||
| } | |||||
| return Mathutils_CreatePyObject_alloc(r_array, prototype_pyval); | |||||
| } | |||||
| PyObject *mathtutils_generic_elem_op_inplace(PyObject *val1, PyObject *val2, Mathutils_Elem_Ops op) | |||||
| { | |||||
| bool is_float1, is_float2, is_scalar = false; | |||||
| int size1, size2, r_size; | |||||
| float float1, float2, scalar; | |||||
| float *array1 = NULL, *array2 = NULL; | |||||
| float *r_array = NULL; | |||||
| if (!mathutils_check_valid_types(val1, val2, &size1, &size2, &is_float1, &is_float2, &float1, &float2)) { | |||||
| PyErr_Format(PyExc_TypeError, | |||||
| "Element-wise operation: " | |||||
| "not supported between '%.200s' and '%.200s' types", | |||||
| Py_TYPE(val1)->tp_name, Py_TYPE(val2)->tp_name); | |||||
| return NULL; | |||||
| } | |||||
| if ((!is_float1 && !is_float2) && (size1 != size2)) { | |||||
| PyErr_SetString(PyExc_ValueError, | |||||
| "Element-wise operation: " | |||||
| "values must have the same dimensions for this operation"); | |||||
| return NULL; | |||||
| } | |||||
| if (!is_float1) { | |||||
| if (mathutils_read_callback_check(val1, true, true) == -1) { | |||||
| return NULL; | |||||
| } | |||||
| array1 = ((BaseMathObject *)val1)->data; | |||||
| } | |||||
| else { | |||||
| /* don't support inplace ops without mathutils type on left */ | |||||
| PyErr_Format(PyExc_TypeError, | |||||
| "Element-wise operation: " | |||||
| "not supported between '%.200s' and '%.200s' types", | |||||
| Py_TYPE(val1)->tp_name, Py_TYPE(val2)->tp_name); | |||||
| return NULL; | |||||
| } | |||||
| if (!is_float2) { | |||||
| if (mathutils_read_callback_check(val2, true, false) == -1) { | |||||
| return NULL; | |||||
| } | |||||
| array2 = ((BaseMathObject *)val2)->data; | |||||
| } | |||||
| if (is_float2) { | |||||
| is_scalar = true; | |||||
| scalar = float2; | |||||
| } | |||||
| r_size = MAX2(size1, size2); | |||||
| r_array = array1; | |||||
| if (is_scalar) { | |||||
| void (*func_ptr_array_float)(float*, const int, const float) = NULL; | |||||
| switch (op) { | |||||
| case ADD: | |||||
| func_ptr_array_float = &add_vn_fl; | |||||
| break; | |||||
| case SUB: | |||||
| scalar *= -1.0f; | |||||
| func_ptr_array_float = &add_vn_fl; | |||||
| break; | |||||
| case MUL: | |||||
| func_ptr_array_float = &mul_vn_fl; | |||||
| break; | |||||
| case DIV: | |||||
| if (scalar == 0.0f) { | |||||
| PyErr_SetString(PyExc_ZeroDivisionError, | |||||
| "Element-wise division: " | |||||
| "divide by zero error"); | |||||
| return NULL; | |||||
| } | |||||
| scalar = 1.0f / scalar; | |||||
| func_ptr_array_float = &mul_vn_fl; | |||||
| break; | |||||
| default: | |||||
| return NULL; | |||||
| break; | |||||
| } | |||||
| if (func_ptr_array_float) { | |||||
| (*func_ptr_array_float)(r_array, r_size, scalar); | |||||
| } | |||||
| } | |||||
| else { | |||||
| void (*func_ptr_array_array)(float*, const float*, const int) = NULL; | |||||
| float *tar = NULL, *src_a = NULL, *src_b = NULL; | |||||
| int i; | |||||
| switch (op) { | |||||
| case ADD: | |||||
| func_ptr_array_array = &add_vn_vn; | |||||
| break; | |||||
| case SUB: | |||||
| func_ptr_array_array = &sub_vn_vn; | |||||
| break; | |||||
| case MUL: | |||||
| #ifdef USE_MATHUTILS_ELEM_MUL | |||||
| func_ptr_array_array = &mul_vn_vn; | |||||
| #else | |||||
| PyErr_Format(PyExc_TypeError, | |||||
| "Inplace element-wise multiplication not yet supported. " | |||||
| "Use @= for inplace matrix multiplication."); | |||||
| return NULL; | |||||
| #endif | |||||
| break; | |||||
| case DIV: | |||||
| r_array = PyMem_New(float, r_size); | |||||
| if (UNLIKELY(!r_array)) { | |||||
| PyErr_SetString(PyExc_MemoryError, | |||||
| "Mathutils: " | |||||
| "problem allocating memory"); | |||||
| return NULL; | |||||
| } | |||||
| tar = r_array + (r_size - 1); | |||||
| src_a = array1 + (r_size - 1); | |||||
| src_b = array2 + (r_size - 1); | |||||
| i = r_size; | |||||
| while (i--) { | |||||
| if (*src_b == 0.0f) { | |||||
| PyMem_Free(r_array); | |||||
| PyErr_SetString(PyExc_ZeroDivisionError, | |||||
| "Element-wise division: " | |||||
| "divide by zero error"); | |||||
| return NULL; | |||||
| } | |||||
| *(tar--) = *(src_a--) / *(src_b--); | |||||
| } | |||||
| memcpy(array1, r_array, r_size * sizeof(float)); | |||||
| PyMem_Free(r_array); | |||||
| break; | |||||
| default: | |||||
| return NULL; | |||||
| break; | |||||
| } | |||||
| if (func_ptr_array_array) { | |||||
| (*func_ptr_array_array)(r_array, array2, r_size); | |||||
| } | |||||
| } | |||||
| (void)BaseMath_WriteCallback((BaseMathObject *)val1); | |||||
| Py_INCREF(val1); | |||||
| return val1; | |||||
| } | |||||
| /* ----------------------------------MATRIX FUNCTIONS-------------------- */ | /* ----------------------------------MATRIX FUNCTIONS-------------------- */ | ||||
| /* Utility functions */ | /* Utility functions */ | ||||
| /* LomontRRDCompare4, Ever Faster Float Comparisons by Randy Dillon */ | /* LomontRRDCompare4, Ever Faster Float Comparisons by Randy Dillon */ | ||||
| /* XXX We may want to use 'safer' BLI's compare_ff_relative ultimately? | /* XXX We may want to use 'safer' BLI's compare_ff_relative ultimately? | ||||
| ▲ Show 20 Lines • Show All 311 Lines • Show Last 20 Lines | |||||