diff options
Diffstat (limited to 'source/blender/python/mathutils')
18 files changed, 1408 insertions, 903 deletions
diff --git a/source/blender/python/mathutils/CMakeLists.txt b/source/blender/python/mathutils/CMakeLists.txt index 133b8d3895c..ef6b090140b 100644 --- a/source/blender/python/mathutils/CMakeLists.txt +++ b/source/blender/python/mathutils/CMakeLists.txt @@ -38,6 +38,7 @@ set(SRC mathutils_Quaternion.c mathutils_Vector.c mathutils_geometry.c + mathutils_interpolate.c mathutils_kdtree.c mathutils_noise.c @@ -48,6 +49,7 @@ set(SRC mathutils_Quaternion.h mathutils_Vector.h mathutils_geometry.h + mathutils_interpolate.h mathutils_kdtree.h mathutils_noise.h ) diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 349f8483fb0..7b51b08451b 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -31,12 +31,25 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "../generic/python_utildefines.h" + #ifndef MATH_STANDALONE # include "BLI_dynstr.h" #endif PyDoc_STRVAR(M_Mathutils_doc, -"This module provides access to matrices, eulers, quaternions and vectors." +"This module provides access to the math classes:\n" +"\n" +"- :class:`Color`,\n" +"- :class:`Euler`,\n" +"- :class:`Matrix`,\n" +"- :class:`Quaternion`,\n" +"- :class:`Vector`,\n" +"\n" +".. note::\n" +"\n" +" Classes, methods and attributes that accept vectors also accept other numeric sequences,\n" +" such as tuples, lists." ); static int mathutils_array_parse_fast(float *array, int size, @@ -66,11 +79,45 @@ static int mathutils_array_parse_fast(float *array, return size; } +/** + * helper function that returns a Python ``__hash__``. + * + * \note consistent with the equivalent tuple of floats (CPython's 'tuplehash') + */ +Py_hash_t mathutils_array_hash(const float *array, size_t array_len) +{ + int i; + Py_uhash_t x; /* Unsigned for defined overflow behavior. */ + Py_hash_t y; + Py_uhash_t mult; + Py_ssize_t len; + + mult = _PyHASH_MULTIPLIER; + len = array_len; + x = 0x345678UL; + i = 0; + while (--len >= 0) { + y = _Py_HashDouble((double)(array[i++])); + if (y == -1) + return -1; + x = (x ^ y) * mult; + /* the cast might truncate len; that doesn't change hash stability */ + mult += (Py_hash_t)(82520UL + len + len); + } + x += 97531UL; + if (x == (Py_uhash_t)-1) + x = -2; + return x; +} + /* helper functionm returns length of the 'value', -1 on error */ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject *value, const char *error_prefix) { + const int flag = array_max; int size; + array_max &= ~MU_ARRAY_FLAGS; + #if 1 /* approx 6x speedup for mathutils types */ if ((size = VectorObject_Check(value) ? ((VectorObject *)value)->size : 0) || @@ -82,6 +129,10 @@ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject * return -1; } + if (flag & MU_ARRAY_SPILL) { + CLAMP_MAX(size, array_max); + } + if (size > array_max || size < array_min) { if (array_max == array_min) { PyErr_Format(PyExc_ValueError, @@ -97,7 +148,6 @@ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject * } memcpy(array, ((BaseMathObject *)value)->data, size * sizeof(float)); - return size; } else #endif @@ -112,6 +162,10 @@ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject * size = PySequence_Fast_GET_SIZE(value_fast); + if (flag & MU_ARRAY_SPILL) { + CLAMP_MAX(size, array_max); + } + if (size > array_max || size < array_min) { if (array_max == array_min) { PyErr_Format(PyExc_ValueError, @@ -127,8 +181,19 @@ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject * return -1; } - return mathutils_array_parse_fast(array, size, value_fast, error_prefix); + size = mathutils_array_parse_fast(array, size, value_fast, error_prefix); } + + if (size != -1) { + if (flag & MU_ARRAY_ZERO) { + int size_left = array_max - size; + if (size_left) { + memset(&array[size], 0, sizeof(float) * size_left); + } + } + } + + return size; } /* on error, -1 is returned and no allocation is made */ @@ -196,6 +261,7 @@ int mathutils_array_parse_alloc(float **array, int array_min, PyObject *value, c int mathutils_array_parse_alloc_v(float **array, int array_dim, PyObject *value, const char *error_prefix) { PyObject *value_fast = NULL; + const int array_dim_flag = array_dim; int i, size; /* non list/tuple cases */ @@ -209,12 +275,14 @@ int mathutils_array_parse_alloc_v(float **array, int array_dim, PyObject *value, if (size != 0) { float *fp; + array_dim &= ~MU_ARRAY_FLAGS; + fp = *array = PyMem_Malloc(size * array_dim * sizeof(float)); for (i = 0; i < size; i++, fp += array_dim) { PyObject *item = PySequence_Fast_GET_ITEM(value, i); - if (mathutils_array_parse(fp, array_dim, array_dim, item, error_prefix) == -1) { + if (mathutils_array_parse(fp, array_dim, array_dim_flag, item, error_prefix) == -1) { PyMem_Free(*array); *array = NULL; size = -1; @@ -415,19 +483,59 @@ int _BaseMathObject_WriteIndexCallback(BaseMathObject *self, int index) return -1; } +void _BaseMathObject_RaiseFrozenExc(const BaseMathObject *self) +{ + PyErr_Format(PyExc_TypeError, + "%s is frozen (immutable)", + Py_TYPE(self)->tp_name); +} + +void _BaseMathObject_RaiseNotFrozenExc(const BaseMathObject *self) +{ + PyErr_Format(PyExc_TypeError, + "%s is not frozen (mutable), call freeze first", + Py_TYPE(self)->tp_name); +} + /* BaseMathObject generic functions for all mathutils types */ char BaseMathObject_owner_doc[] = "The item this is wrapping or None (read-only)."; PyObject *BaseMathObject_owner_get(BaseMathObject *self, void *UNUSED(closure)) { PyObject *ret = self->cb_user ? self->cb_user : Py_None; - Py_INCREF(ret); - return ret; + return Py_INCREF_RET(ret); } char BaseMathObject_is_wrapped_doc[] = "True when this object wraps external data (read-only).\n\n:type: boolean"; PyObject *BaseMathObject_is_wrapped_get(BaseMathObject *self, void *UNUSED(closure)) { - return PyBool_FromLong((self->wrapped == Py_WRAP) ? 1 : 0); + return PyBool_FromLong((self->flag & BASE_MATH_FLAG_IS_WRAP) != 0); +} + +char BaseMathObject_is_frozen_doc[] = "True when this object has been frozen (read-only).\n\n:type: boolean"; +PyObject *BaseMathObject_is_frozen_get(BaseMathObject *self, void *UNUSED(closure)) +{ + return PyBool_FromLong((self->flag & BASE_MATH_FLAG_IS_FROZEN) != 0); +} + +char BaseMathObject_freeze_doc[] = +".. function:: freeze()\n" +"\n" +" Make this object immutable.\n" +"\n" +" After this the object can be hashed, used in dictionaries & sets.\n" +"\n" +" :return: An instance of this object.\n" +; +PyObject *BaseMathObject_freeze(BaseMathObject *self) +{ + if (self->flag & BASE_MATH_FLAG_IS_WRAP) { + PyErr_SetString(PyExc_TypeError, "Cannot freeze wrapped data"); + return NULL; + } + + self->flag |= BASE_MATH_FLAG_IS_FROZEN; + + return Py_INCREF_RET((PyObject *)self);; } int BaseMathObject_traverse(BaseMathObject *self, visitproc visit, void *arg) @@ -445,7 +553,7 @@ int BaseMathObject_clear(BaseMathObject *self) void BaseMathObject_dealloc(BaseMathObject *self) { /* only free non wrapped */ - if (self->wrapped != Py_WRAP) { + if ((self->flag & BASE_MATH_FLAG_IS_WRAP) == 0) { PyMem_Free(self->data); } @@ -477,6 +585,7 @@ static struct PyModuleDef M_Mathutils_module_def = { /* submodules only */ #include "mathutils_geometry.h" +#include "mathutils_interpolate.h" #ifndef MATH_STANDALONE # include "mathutils_kdtree.h" # include "mathutils_noise.h" @@ -518,6 +627,13 @@ PyMODINIT_FUNC PyInit_mathutils(void) PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); Py_INCREF(submodule); + PyModule_AddObject(mod, "interpolate", (submodule = PyInit_mathutils_interpolate())); + /* XXX, python doesnt do imports with this usefully yet + * 'from mathutils.geometry import PolyFill' + * ...fails without this. */ + PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule); + Py_INCREF(submodule); + #ifndef MATH_STANDALONE /* Noise submodule */ PyModule_AddObject(mod, "noise", (submodule = PyInit_mathutils_noise())); diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h index eb25d9bff07..e653b45389b 100644 --- a/source/blender/python/mathutils/mathutils.h +++ b/source/blender/python/mathutils/mathutils.h @@ -29,11 +29,25 @@ /* Can cast different mathutils types to this, use for generic funcs */ +#include "BLI_compiler_attrs.h" + struct DynStr; extern char BaseMathObject_is_wrapped_doc[]; +extern char BaseMathObject_is_frozen_doc[]; extern char BaseMathObject_owner_doc[]; +#define BASE_MATH_NEW(struct_name, root_type, base_type) \ + (struct_name *)((base_type ? (base_type)->tp_alloc(base_type, 0) : _PyObject_GC_New(&(root_type)))); + + +/* BaseMathObject.flag */ +enum { + BASE_MATH_FLAG_IS_WRAP = (1 << 0), + BASE_MATH_FLAG_IS_FROZEN = (1 << 1), +}; +#define BASE_MATH_FLAG_DEFAULT 0 + #define BASE_MATH_MEMBERS(_data) \ PyObject_VAR_HEAD \ float *_data; /* array of data (alias), wrapped status depends on wrapped status */ \ @@ -42,7 +56,7 @@ extern char BaseMathObject_owner_doc[]; unsigned char cb_type; /* which user funcs do we adhere to, RNA, GameObject, etc */ \ unsigned char cb_subtype; /* subtype: location, rotation... \ * to avoid defining many new functions for every attribute of the same type */ \ - unsigned char wrapped /* wrapped data type? */ \ + unsigned char flag /* wrapped data type? */ \ typedef struct { BASE_MATH_MEMBERS(data); @@ -57,6 +71,10 @@ typedef struct { PyObject *BaseMathObject_owner_get(BaseMathObject *self, void *); PyObject *BaseMathObject_is_wrapped_get(BaseMathObject *self, void *); +PyObject *BaseMathObject_is_frozen_get(BaseMathObject *self, void *); + +extern char BaseMathObject_freeze_doc[]; +PyObject *BaseMathObject_freeze(BaseMathObject *self); int BaseMathObject_traverse(BaseMathObject *self, visitproc visit, void *arg); int BaseMathObject_clear(BaseMathObject *self); @@ -67,9 +85,6 @@ PyMODINIT_FUNC PyInit_mathutils(void); int EXPP_FloatsAreEqual(float A, float B, int floatSteps); int EXPP_VectorsAreEqual(const float *vecA, const float *vecB, int size, int floatSteps); -#define Py_NEW 1 -#define Py_WRAP 2 - typedef struct Mathutils_Callback Mathutils_Callback; typedef int (*BaseMathCheckFunc)(BaseMathObject *); /* checks the user is still valid */ @@ -93,6 +108,9 @@ int _BaseMathObject_WriteCallback(BaseMathObject *self); int _BaseMathObject_ReadIndexCallback(BaseMathObject *self, int index); int _BaseMathObject_WriteIndexCallback(BaseMathObject *self, int index); +void _BaseMathObject_RaiseFrozenExc(const BaseMathObject *self); +void _BaseMathObject_RaiseNotFrozenExc(const BaseMathObject *self); + /* since this is called so often avoid where possible */ #define BaseMath_ReadCallback(_self) \ (((_self)->cb_user ? _BaseMathObject_ReadCallback((BaseMathObject *)_self):0)) @@ -103,12 +121,39 @@ int _BaseMathObject_WriteIndexCallback(BaseMathObject *self, int index); #define BaseMath_WriteIndexCallback(_self, _index) \ (((_self)->cb_user ? _BaseMathObject_WriteIndexCallback((BaseMathObject *)_self, _index):0)) +/* support BASE_MATH_FLAG_IS_FROZEN */ +#define BaseMath_ReadCallback_ForWrite(_self) \ + (UNLIKELY((_self)->flag & BASE_MATH_FLAG_IS_FROZEN) ? \ + (_BaseMathObject_RaiseFrozenExc((BaseMathObject *)_self), -1) : (BaseMath_ReadCallback(_self))) + +#define BaseMath_ReadIndexCallback_ForWrite(_self, _index) \ + (UNLIKELY((_self)->flag & BASE_MATH_FLAG_IS_FROZEN) ? \ + (_BaseMathObject_RaiseFrozenExc((BaseMathObject *)_self), -1) : (BaseMath_ReadIndexCallback(_self, _index))) + +#define BaseMath_Prepare_ForWrite(_self) \ + (UNLIKELY((_self)->flag & BASE_MATH_FLAG_IS_FROZEN) ? \ + (_BaseMathObject_RaiseFrozenExc((BaseMathObject *)_self), -1) : 0) + +#define BaseMathObject_Prepare_ForHash(_self) \ + (UNLIKELY(((_self)->flag & BASE_MATH_FLAG_IS_FROZEN) == 0) ? \ + (_BaseMathObject_RaiseNotFrozenExc((BaseMathObject *)_self), -1) : 0) + /* utility func */ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject *value, const char *error_prefix); int mathutils_array_parse_alloc(float **array, int array_min, PyObject *value, const char *error_prefix); int mathutils_array_parse_alloc_v(float **array, int array_dim, PyObject *value, const char *error_prefix); int mathutils_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix); +Py_hash_t mathutils_array_hash(const float *float_array, size_t array_len); + +/* zero remaining unused elements of the array */ +#define MU_ARRAY_ZERO (1 << 30) +/* ignore larger py sequences than requested (just use first elements), + * handy when using 3d vectors as 2d */ +#define MU_ARRAY_SPILL (1 << 31) + +#define MU_ARRAY_FLAGS (MU_ARRAY_ZERO | MU_ARRAY_SPILL) + int column_vector_multiplication(float rvec[4], VectorObject *vec, MatrixObject *mat); #ifndef MATH_STANDALONE diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index aee2b9e9711..add8c2451ff 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -32,6 +32,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "../generic/python_utildefines.h" + #ifndef MATH_STANDALONE # include "BLI_dynstr.h" #endif @@ -64,7 +66,7 @@ static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds) "more than a single arg given"); return NULL; } - return Color_CreatePyObject(col, Py_NEW, type); + return Color_CreatePyObject(col, type); } /* -----------------------------METHODS---------------------------- */ @@ -107,7 +109,7 @@ static PyObject *Color_copy(ColorObject *self) if (BaseMath_ReadCallback(self) == -1) return NULL; - return Color_CreatePyObject(self->col, Py_NEW, Py_TYPE(self)); + return Color_CreatePyObject(self->col, Py_TYPE(self)); } static PyObject *Color_deepcopy(ColorObject *self, PyObject *args) { @@ -187,7 +189,18 @@ static PyObject *Color_richcmpr(PyObject *a, PyObject *b, int op) return NULL; } - return Py_INCREF(res), res; + return Py_INCREF_RET(res); +} + +static Py_hash_t Color_hash(ColorObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) + return -1; + + if (BaseMathObject_Prepare_ForHash(self) == -1) + return -1; + + return mathutils_array_hash(self->col, COLOR_SIZE); } /* ---------------------SEQUENCE PROTOCOLS------------------------ */ @@ -220,8 +233,12 @@ static PyObject *Color_item(ColorObject *self, int i) /* sequence accessor (set) */ static int Color_ass_item(ColorObject *self, int i, PyObject *value) { - float f = PyFloat_AsDouble(value); + float f; + + if (BaseMath_Prepare_ForWrite(self) == -1) + return -1; + f = PyFloat_AsDouble(value); if (f == -1 && PyErr_Occurred()) { /* parsed item not a number */ PyErr_SetString(PyExc_TypeError, "color[item] = x: " @@ -273,7 +290,7 @@ static int Color_ass_slice(ColorObject *self, int begin, int end, PyObject *seq) int i, size; float col[COLOR_SIZE]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; CLAMP(begin, 0, COLOR_SIZE); @@ -411,7 +428,7 @@ static PyObject *Color_add(PyObject *v1, PyObject *v2) add_vn_vnvn(col, color1->col, color2->col, COLOR_SIZE); - return Color_CreatePyObject(col, Py_NEW, Py_TYPE(v1)); + return Color_CreatePyObject(col, Py_TYPE(v1)); } /* addition in-place: obj += obj */ @@ -429,7 +446,7 @@ static PyObject *Color_iadd(PyObject *v1, PyObject *v2) color1 = (ColorObject *)v1; color2 = (ColorObject *)v2; - if (BaseMath_ReadCallback(color1) == -1 || BaseMath_ReadCallback(color2) == -1) + if (BaseMath_ReadCallback_ForWrite(color1) == -1 || BaseMath_ReadCallback(color2) == -1) return NULL; add_vn_vn(color1->col, color2->col, COLOR_SIZE); @@ -460,7 +477,7 @@ static PyObject *Color_sub(PyObject *v1, PyObject *v2) sub_vn_vnvn(col, color1->col, color2->col, COLOR_SIZE); - return Color_CreatePyObject(col, Py_NEW, Py_TYPE(v1)); + return Color_CreatePyObject(col, Py_TYPE(v1)); } /* subtraction in-place: obj -= obj */ @@ -478,7 +495,7 @@ static PyObject *Color_isub(PyObject *v1, PyObject *v2) color1 = (ColorObject *)v1; color2 = (ColorObject *)v2; - if (BaseMath_ReadCallback(color1) == -1 || BaseMath_ReadCallback(color2) == -1) + if (BaseMath_ReadCallback_ForWrite(color1) == -1 || BaseMath_ReadCallback(color2) == -1) return NULL; sub_vn_vn(color1->col, color2->col, COLOR_SIZE); @@ -492,7 +509,7 @@ static PyObject *color_mul_float(ColorObject *color, const float scalar) { float tcol[COLOR_SIZE]; mul_vn_vn_fl(tcol, color->col, COLOR_SIZE, scalar); - return Color_CreatePyObject(tcol, Py_NEW, Py_TYPE(color)); + return Color_CreatePyObject(tcol, Py_TYPE(color)); } @@ -577,7 +594,7 @@ static PyObject *Color_imul(PyObject *v1, PyObject *v2) ColorObject *color = (ColorObject *)v1; float scalar; - if (BaseMath_ReadCallback(color) == -1) + if (BaseMath_ReadCallback_ForWrite(color) == -1) return NULL; /* only support color *= float */ @@ -603,7 +620,7 @@ static PyObject *Color_idiv(PyObject *v1, PyObject *v2) ColorObject *color = (ColorObject *)v1; float scalar; - if (BaseMath_ReadCallback(color) == -1) + if (BaseMath_ReadCallback_ForWrite(color) == -1) return NULL; /* only support color /= float */ @@ -639,7 +656,7 @@ static PyObject *Color_neg(ColorObject *self) return NULL; negate_vn_vn(tcol, self->col, COLOR_SIZE); - return Color_CreatePyObject(tcol, Py_NEW, Py_TYPE(self)); + return Color_CreatePyObject(tcol, Py_TYPE(self)); } @@ -726,7 +743,7 @@ static int Color_channel_hsv_set(ColorObject *self, PyObject *value, void *type) return -1; } - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; rgb_to_hsv_v(self->col, hsv); @@ -753,9 +770,10 @@ static PyObject *Color_hsv_get(ColorObject *self, void *UNUSED(closure)) rgb_to_hsv(self->col[0], self->col[1], self->col[2], &(hsv[0]), &(hsv[1]), &(hsv[2])); ret = PyTuple_New(3); - PyTuple_SET_ITEM(ret, 0, PyFloat_FromDouble(hsv[0])); - PyTuple_SET_ITEM(ret, 1, PyFloat_FromDouble(hsv[1])); - PyTuple_SET_ITEM(ret, 2, PyFloat_FromDouble(hsv[2])); + PyTuple_SET_ITEMS(ret, + PyFloat_FromDouble(hsv[0]), + PyFloat_FromDouble(hsv[1]), + PyFloat_FromDouble(hsv[2])); return ret; } @@ -766,6 +784,9 @@ static int Color_hsv_set(ColorObject *self, PyObject *value, void *UNUSED(closur if (mathutils_array_parse(hsv, 3, 3, value, "mathutils.Color.hsv = value") == -1) return -1; + if (BaseMath_Prepare_ForWrite(self) == -1) + return -1; + CLAMP(hsv[0], 0.0f, 1.0f); CLAMP(hsv[1], 0.0f, 1.0f); CLAMP(hsv[2], 0.0f, 1.0f); @@ -793,6 +814,7 @@ static PyGetSetDef Color_getseters[] = { {(char *)"hsv", (getter)Color_hsv_get, (setter)Color_hsv_set, (char *)Color_hsv_doc, (void *)0}, {(char *)"is_wrapped", (getter)BaseMathObject_is_wrapped_get, (setter)NULL, BaseMathObject_is_wrapped_doc, NULL}, + {(char *)"is_frozen", (getter)BaseMathObject_is_frozen_get, (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, {(char *)"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; @@ -803,12 +825,20 @@ static struct PyMethodDef Color_methods[] = { {"copy", (PyCFunction) Color_copy, METH_NOARGS, Color_copy_doc}, {"__copy__", (PyCFunction) Color_copy, METH_NOARGS, Color_copy_doc}, {"__deepcopy__", (PyCFunction) Color_deepcopy, METH_VARARGS, Color_copy_doc}, + + /* base-math methods */ + {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, {NULL, NULL, 0, NULL} }; /* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(color_doc, -"This object gives access to Colors in Blender." +".. class:: Color(rgb)\n" +"\n" +" This object gives access to Colors in Blender.\n" +"\n" +" :param rgb: (r, g, b) color values\n" +" :type rgb: 3d vector\n" ); PyTypeObject color_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -824,7 +854,7 @@ PyTypeObject color_Type = { &Color_NumMethods, /* tp_as_number */ &Color_SeqMethods, /* tp_as_sequence */ &Color_AsMapping, /* tp_as_mapping */ - NULL, /* tp_hash */ + (hashfunc)Color_hash, /* tp_hash */ NULL, /* tp_call */ #ifndef MATH_STANDALONE (reprfunc) Color_str, /* tp_str */ @@ -862,40 +892,60 @@ PyTypeObject color_Type = { NULL, /* tp_weaklist */ NULL /* tp_del */ }; -/* ------------------------Color_CreatePyObject (internal)------------- */ -/* creates a new color object */ -/* pass Py_WRAP - if vector is a WRAPPER for data allocated by BLENDER - * (i.e. it was allocated elsewhere by MEM_mallocN()) - * pass Py_NEW - if vector is not a WRAPPER and managed by PYTHON - * (i.e. it must be created here with PyMEM_malloc())*/ -PyObject *Color_CreatePyObject(float col[3], int type, PyTypeObject *base_type) + +PyObject *Color_CreatePyObject( + const float col[3], + PyTypeObject *base_type) { ColorObject *self; + float *col_alloc; - self = base_type ? (ColorObject *)base_type->tp_alloc(base_type, 0) : - (ColorObject *)PyObject_GC_New(ColorObject, &color_Type); + col_alloc = PyMem_Malloc(COLOR_SIZE * sizeof(float)); + if (UNLIKELY(col_alloc == NULL)) { + PyErr_SetString(PyExc_MemoryError, + "Color(): " + "problem allocating data"); + return NULL; + } + self = BASE_MATH_NEW(ColorObject, color_Type, base_type); if (self) { + self->col = col_alloc; + /* init callbacks as NULL */ self->cb_user = NULL; self->cb_type = self->cb_subtype = 0; - if (type == Py_WRAP) { - self->col = col; - self->wrapped = Py_WRAP; - } - else if (type == Py_NEW) { - self->col = PyMem_Malloc(COLOR_SIZE * sizeof(float)); - if (col) - copy_v3_v3(self->col, col); - else - zero_v3(self->col); - - self->wrapped = Py_NEW; - } - else { - Py_FatalError("Color(): invalid type!"); - } + /* NEW */ + if (col) + copy_v3_v3(self->col, col); + else + zero_v3(self->col); + + self->flag = BASE_MATH_FLAG_DEFAULT; + } + else { + PyMem_Free(col_alloc); + } + + return (PyObject *)self; +} + +PyObject *Color_CreatePyObject_wrap( + float col[3], + PyTypeObject *base_type) +{ + ColorObject *self; + + self = BASE_MATH_NEW(ColorObject, color_Type, base_type); + if (self) { + /* init callbacks as NULL */ + self->cb_user = NULL; + self->cb_type = self->cb_subtype = 0; + + /* WRAP */ + self->col = col; + self->flag = BASE_MATH_FLAG_DEFAULT | BASE_MATH_FLAG_IS_WRAP; } return (PyObject *)self; @@ -904,7 +954,7 @@ PyObject *Color_CreatePyObject(float col[3], int type, PyTypeObject *base_type) PyObject *Color_CreatePyObject_cb(PyObject *cb_user, unsigned char cb_type, unsigned char cb_subtype) { - ColorObject *self = (ColorObject *)Color_CreatePyObject(NULL, Py_NEW, NULL); + ColorObject *self = (ColorObject *)Color_CreatePyObject(NULL, NULL); if (self) { Py_INCREF(cb_user); self->cb_user = cb_user; diff --git a/source/blender/python/mathutils/mathutils_Color.h b/source/blender/python/mathutils/mathutils_Color.h index 193d30a2b6f..1290f73da62 100644 --- a/source/blender/python/mathutils/mathutils_Color.h +++ b/source/blender/python/mathutils/mathutils_Color.h @@ -42,8 +42,17 @@ typedef struct { * blender (stored in blend_data). This is an either/or struct not both*/ /* prototypes */ -PyObject *Color_CreatePyObject(float col[3], int type, PyTypeObject *base_type); -PyObject *Color_CreatePyObject_cb(PyObject *cb_user, - unsigned char cb_type, unsigned char cb_subtype); +PyObject *Color_CreatePyObject( + const float col[3], + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT; +PyObject *Color_CreatePyObject_wrap( + float col[3], + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +PyObject *Color_CreatePyObject_cb( + PyObject *cb_user, + unsigned char cb_type, unsigned char cb_subtype + ) ATTR_WARN_UNUSED_RESULT; #endif /* __MATHUTILS_COLOR_H__ */ diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c index f6d124938a4..54adc826af7 100644 --- a/source/blender/python/mathutils/mathutils_Euler.c +++ b/source/blender/python/mathutils/mathutils_Euler.c @@ -31,6 +31,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "../generic/python_utildefines.h" #ifndef MATH_STANDALONE # include "BLI_dynstr.h" @@ -70,7 +71,7 @@ static PyObject *Euler_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; break; } - return Euler_CreatePyObject(eul, order, Py_NEW, type); + return Euler_CreatePyObject(eul, order, type); } /* internal use, assume read callback is done */ @@ -150,7 +151,7 @@ static PyObject *Euler_to_quaternion(EulerObject *self) eulO_to_quat(quat, self->eul, self->order); - return Quaternion_CreatePyObject(quat, Py_NEW, NULL); + return Quaternion_CreatePyObject(quat, NULL); } /* return a matrix representation of the euler */ @@ -171,7 +172,7 @@ static PyObject *Euler_to_matrix(EulerObject *self) eulO_to_mat3((float (*)[3])mat, self->eul, self->order); - return Matrix_CreatePyObject(mat, 3, 3, Py_NEW, NULL); + return Matrix_CreatePyObject(mat, 3, 3, NULL); } PyDoc_STRVAR(Euler_zero_doc, @@ -181,6 +182,9 @@ PyDoc_STRVAR(Euler_zero_doc, ); static PyObject *Euler_zero(EulerObject *self) { + if (BaseMath_Prepare_ForWrite(self) == -1) + return NULL; + zero_v3(self->eul); if (BaseMath_WriteCallback(self) == -1) @@ -219,7 +223,7 @@ static PyObject *Euler_rotate_axis(EulerObject *self, PyObject *args) return NULL; } - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; @@ -233,7 +237,7 @@ static PyObject *Euler_rotate_axis(EulerObject *self, PyObject *args) PyDoc_STRVAR(Euler_rotate_doc, ".. method:: rotate(other)\n" "\n" -" Rotates the euler a by another mathutils value.\n" +" Rotates the euler by another mathutils value.\n" "\n" " :arg other: rotation component of mathutils value\n" " :type other: :class:`Euler`, :class:`Quaternion` or :class:`Matrix`\n" @@ -242,7 +246,7 @@ static PyObject *Euler_rotate(EulerObject *self, PyObject *value) { float self_rmat[3][3], other_rmat[3][3], rmat[3][3]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (mathutils_any_to_rotmat(other_rmat, value, "euler.rotate(value)") == -1) @@ -269,7 +273,7 @@ static PyObject *Euler_make_compatible(EulerObject *self, PyObject *value) { float teul[EULER_SIZE]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (mathutils_array_parse(teul, EULER_SIZE, EULER_SIZE, value, @@ -304,7 +308,7 @@ static PyObject *Euler_copy(EulerObject *self) if (BaseMath_ReadCallback(self) == -1) return NULL; - return Euler_CreatePyObject(self->eul, self->order, Py_NEW, Py_TYPE(self)); + return Euler_CreatePyObject(self->eul, self->order, Py_TYPE(self)); } static PyObject *Euler_deepcopy(EulerObject *self, PyObject *args) { @@ -382,7 +386,18 @@ static PyObject *Euler_richcmpr(PyObject *a, PyObject *b, int op) return NULL; } - return Py_INCREF(res), res; + return Py_INCREF_RET(res); +} + +static Py_hash_t Euler_hash(EulerObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) + return -1; + + if (BaseMathObject_Prepare_ForHash(self) == -1) + return -1; + + return mathutils_array_hash(self->eul, EULER_SIZE); } /* ---------------------SEQUENCE PROTOCOLS------------------------ */ @@ -415,8 +430,12 @@ static PyObject *Euler_item(EulerObject *self, int i) /* sequence accessor (set) */ static int Euler_ass_item(EulerObject *self, int i, PyObject *value) { - float f = PyFloat_AsDouble(value); + float f; + + if (BaseMath_Prepare_ForWrite(self) == -1) + return -1; + f = PyFloat_AsDouble(value); if (f == -1 && PyErr_Occurred()) { /* parsed item not a number */ PyErr_SetString(PyExc_TypeError, "euler[attribute] = x: " @@ -469,7 +488,7 @@ static int Euler_ass_slice(EulerObject *self, int begin, int end, PyObject *seq) int i, size; float eul[EULER_SIZE]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; CLAMP(begin, 0, EULER_SIZE); @@ -614,12 +633,18 @@ static PyObject *Euler_order_get(EulerObject *self, void *UNUSED(closure)) static int Euler_order_set(EulerObject *self, PyObject *value, void *UNUSED(closure)) { - const char *order_str = _PyUnicode_AsString(value); - short order = euler_order_from_string(order_str, "euler.order"); + const char *order_str; + short order; - if (order == -1) + if (BaseMath_Prepare_ForWrite(self) == -1) return -1; + if (((order_str = _PyUnicode_AsString(value)) == NULL) || + ((order = euler_order_from_string(order_str, "euler.order")) == -1)) + { + return -1; + } + self->order = order; (void)BaseMath_WriteCallback(self); /* order can be written back */ return 0; @@ -635,6 +660,7 @@ static PyGetSetDef Euler_getseters[] = { {(char *)"order", (getter)Euler_order_get, (setter)Euler_order_set, Euler_order_doc, (void *)NULL}, {(char *)"is_wrapped", (getter)BaseMathObject_is_wrapped_get, (setter)NULL, BaseMathObject_is_wrapped_doc, NULL}, + {(char *)"is_frozen", (getter)BaseMathObject_is_frozen_get, (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, {(char *)"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; @@ -651,12 +677,22 @@ static struct PyMethodDef Euler_methods[] = { {"copy", (PyCFunction) Euler_copy, METH_NOARGS, Euler_copy_doc}, {"__copy__", (PyCFunction) Euler_copy, METH_NOARGS, Euler_copy_doc}, {"__deepcopy__", (PyCFunction) Euler_deepcopy, METH_VARARGS, Euler_copy_doc}, + + /* base-math methods */ + {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, {NULL, NULL, 0, NULL} }; /* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(euler_doc, -"This object gives access to Eulers in Blender." +".. class:: Euler(angles, order='XYZ')\n" +"\n" +" This object gives access to Eulers in Blender.\n" +"\n" +" :param angles: Three angles, in radians.\n" +" :type angles: 3d vector\n" +" :param order: Optional order of the angles, a permutation of ``XYZ``.\n" +" :type order: str\n" ); PyTypeObject euler_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -672,7 +708,7 @@ PyTypeObject euler_Type = { NULL, /* tp_as_number */ &Euler_SeqMethods, /* tp_as_sequence */ &Euler_AsMapping, /* tp_as_mapping */ - NULL, /* tp_hash */ + (hashfunc)Euler_hash, /* tp_hash */ NULL, /* tp_call */ #ifndef MATH_STANDALONE (reprfunc) Euler_str, /* tp_str */ @@ -710,53 +746,74 @@ PyTypeObject euler_Type = { NULL, /* tp_weaklist */ NULL /* tp_del */ }; -/* ------------------------Euler_CreatePyObject (internal)------------- */ -/* creates a new euler object */ -/* pass Py_WRAP - if vector is a WRAPPER for data allocated by BLENDER - * (i.e. it was allocated elsewhere by MEM_mallocN()) - * pass Py_NEW - if vector is not a WRAPPER and managed by PYTHON - * (i.e. it must be created here with PyMEM_malloc())*/ -PyObject *Euler_CreatePyObject(float eul[3], const short order, int type, PyTypeObject *base_type) + + +PyObject *Euler_CreatePyObject( + const float eul[3], const short order, + PyTypeObject *base_type) { EulerObject *self; + float *eul_alloc; - self = base_type ? (EulerObject *)base_type->tp_alloc(base_type, 0) : - (EulerObject *)PyObject_GC_New(EulerObject, &euler_Type); + eul_alloc = PyMem_Malloc(EULER_SIZE * sizeof(float)); + if (UNLIKELY(eul_alloc == NULL)) { + PyErr_SetString(PyExc_MemoryError, + "Euler(): " + "problem allocating data"); + return NULL; + } + self = BASE_MATH_NEW(EulerObject, euler_Type, base_type); if (self) { + self->eul = eul_alloc; + /* init callbacks as NULL */ self->cb_user = NULL; self->cb_type = self->cb_subtype = 0; - if (type == Py_WRAP) { - self->eul = eul; - self->wrapped = Py_WRAP; - } - else if (type == Py_NEW) { - self->eul = PyMem_Malloc(EULER_SIZE * sizeof(float)); - if (eul) { - copy_v3_v3(self->eul, eul); - } - else { - zero_v3(self->eul); - } - - self->wrapped = Py_NEW; + if (eul) { + copy_v3_v3(self->eul, eul); } else { - Py_FatalError("Euler(): invalid type!"); + zero_v3(self->eul); } + self->flag = BASE_MATH_FLAG_DEFAULT; + self->order = order; + } + else { + PyMem_Free(eul_alloc); + } + + return (PyObject *)self; +} + +PyObject *Euler_CreatePyObject_wrap( + float eul[3], const short order, + PyTypeObject *base_type) +{ + EulerObject *self; + + self = BASE_MATH_NEW(EulerObject, euler_Type, base_type); + if (self) { + /* init callbacks as NULL */ + self->cb_user = NULL; + self->cb_type = self->cb_subtype = 0; + + self->eul = eul; + self->flag = BASE_MATH_FLAG_DEFAULT | BASE_MATH_FLAG_IS_WRAP; + self->order = order; } return (PyObject *)self; } -PyObject *Euler_CreatePyObject_cb(PyObject *cb_user, const short order, - unsigned char cb_type, unsigned char cb_subtype) +PyObject *Euler_CreatePyObject_cb( + PyObject *cb_user, const short order, + unsigned char cb_type, unsigned char cb_subtype) { - EulerObject *self = (EulerObject *)Euler_CreatePyObject(NULL, order, Py_NEW, NULL); + EulerObject *self = (EulerObject *)Euler_CreatePyObject(NULL, order, NULL); if (self) { Py_INCREF(cb_user); self->cb_user = cb_user; diff --git a/source/blender/python/mathutils/mathutils_Euler.h b/source/blender/python/mathutils/mathutils_Euler.h index 62fb83ef234..744f39faed1 100644 --- a/source/blender/python/mathutils/mathutils_Euler.h +++ b/source/blender/python/mathutils/mathutils_Euler.h @@ -43,9 +43,18 @@ typedef struct { * blender (stored in blend_data). This is an either/or struct not both */ /* prototypes */ -PyObject *Euler_CreatePyObject(float eul[3], const short order, int type, PyTypeObject *base_type); -PyObject *Euler_CreatePyObject_cb(PyObject *cb_user, const short order, - unsigned char cb_type, unsigned char cb_subtype); +PyObject *Euler_CreatePyObject( + const float eul[3], const short order, + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT; +PyObject *Euler_CreatePyObject_wrap( + float eul[3], const short order, + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +PyObject *Euler_CreatePyObject_cb( + PyObject *cb_user, const short order, + unsigned char cb_type, unsigned char cb_subtype + ) ATTR_WARN_UNUSED_RESULT; short euler_order_from_string(const char *str, const char *error_prefix); diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 282f29b4934..67905f8e340 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -32,6 +32,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "../generic/python_utildefines.h" + #ifndef MATH_STANDALONE # include "BLI_string.h" # include "BLI_dynstr.h" @@ -109,7 +111,7 @@ static int mathutils_matrix_row_set(BaseMathObject *bmo, int row) MatrixObject *self = (MatrixObject *)bmo->cb_user; int col; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; if (!matrix_row_vector_check(self, (VectorObject *)bmo, row)) return -1; @@ -139,7 +141,7 @@ static int mathutils_matrix_row_set_index(BaseMathObject *bmo, int row, int col) { MatrixObject *self = (MatrixObject *)bmo->cb_user; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; if (!matrix_row_vector_check(self, (VectorObject *)bmo, row)) return -1; @@ -198,7 +200,7 @@ static int mathutils_matrix_col_set(BaseMathObject *bmo, int col) int num_row; int row; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; if (!matrix_col_vector_check(self, (VectorObject *)bmo, col)) return -1; @@ -231,7 +233,7 @@ static int mathutils_matrix_col_set_index(BaseMathObject *bmo, int col, int row) { MatrixObject *self = (MatrixObject *)bmo->cb_user; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; if (!matrix_col_vector_check(self, (VectorObject *)bmo, col)) return -1; @@ -284,7 +286,7 @@ static int mathutils_matrix_translation_set(BaseMathObject *bmo, int col) MatrixObject *self = (MatrixObject *)bmo->cb_user; int row; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; for (row = 0; row < 3; row++) { @@ -310,7 +312,7 @@ static int mathutils_matrix_translation_set_index(BaseMathObject *bmo, int col, { MatrixObject *self = (MatrixObject *)bmo->cb_user; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; MATRIX_ITEM(self, row, col) = bmo->data[row]; @@ -344,7 +346,7 @@ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds) switch (PyTuple_GET_SIZE(args)) { case 0: - return Matrix_CreatePyObject(NULL, 4, 4, Py_NEW, type); + return Matrix_CreatePyObject(NULL, 4, 4, type); case 1: { PyObject *arg = PyTuple_GET_ITEM(args, 0); @@ -363,7 +365,7 @@ static PyObject *Matrix_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (num_col >= 2 && num_col <= 4) { /* sane row & col size, new matrix and assign as slice */ - PyObject *matrix = Matrix_CreatePyObject(NULL, num_col, num_row, Py_NEW, type); + PyObject *matrix = Matrix_CreatePyObject(NULL, num_col, num_row, type); if (Matrix_ass_slice((MatrixObject *)matrix, 0, INT_MAX, arg) == 0) { return matrix; } @@ -444,7 +446,7 @@ static PyObject *C_Matrix_Identity(PyObject *cls, PyObject *args) return NULL; } - return Matrix_CreatePyObject(NULL, matSize, matSize, Py_NEW, (PyTypeObject *)cls); + return Matrix_CreatePyObject(NULL, matSize, matSize, (PyTypeObject *)cls); } PyDoc_STRVAR(C_Matrix_Rotation_doc, @@ -535,7 +537,7 @@ static PyObject *C_Matrix_Rotation(PyObject *cls, PyObject *args) matrix_3x3_as_4x4(mat); } /* pass to matrix creation */ - return Matrix_CreatePyObject(mat, matSize, matSize, Py_NEW, (PyTypeObject *)cls); + return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } @@ -558,7 +560,7 @@ static PyObject *C_Matrix_Translation(PyObject *cls, PyObject *value) if (mathutils_array_parse(mat[3], 3, 4, value, "mathutils.Matrix.Translation(vector), invalid vector arg") == -1) return NULL; - return Matrix_CreatePyObject(&mat[0][0], 4, 4, Py_NEW, (PyTypeObject *)cls); + return Matrix_CreatePyObject(&mat[0][0], 4, 4, (PyTypeObject *)cls); } /* ----------------------------------mathutils.Matrix.Scale() ------------- */ /* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ @@ -650,7 +652,7 @@ static PyObject *C_Matrix_Scale(PyObject *cls, PyObject *args) matrix_3x3_as_4x4(mat); } /* pass to matrix creation */ - return Matrix_CreatePyObject(mat, matSize, matSize, Py_NEW, (PyTypeObject *)cls); + return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } /* ----------------------------------mathutils.Matrix.OrthoProjection() --- */ /* mat is a 1D array of floats - row[0][0], row[0][1], row[1][0], etc. */ @@ -771,7 +773,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args) matrix_3x3_as_4x4(mat); } /* pass to matrix creation */ - return Matrix_CreatePyObject(mat, matSize, matSize, Py_NEW, (PyTypeObject *)cls); + return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } PyDoc_STRVAR(C_Matrix_Shear_doc, @@ -874,7 +876,7 @@ static PyObject *C_Matrix_Shear(PyObject *cls, PyObject *args) matrix_3x3_as_4x4(mat); } /* pass to matrix creation */ - return Matrix_CreatePyObject(mat, matSize, matSize, Py_NEW, (PyTypeObject *)cls); + return Matrix_CreatePyObject(mat, matSize, matSize, (PyTypeObject *)cls); } void matrix_as_3x3(float mat[3][3], MatrixObject *self) @@ -893,6 +895,19 @@ static void matrix_copy(MatrixObject *mat_dst, const MatrixObject *mat_src) memcpy(mat_dst->matrix, mat_src->matrix, sizeof(float) * (mat_dst->num_col * mat_dst->num_row)); } +/* transposes memory layout, rol/col's don't have to match */ +static void matrix_transpose_internal(float mat_dst_fl[], const MatrixObject *mat_src) +{ + unsigned short col, row; + unsigned int i = 0; + + for (row = 0; row < mat_src->num_row; row++) { + for (col = 0; col < mat_src->num_col; col++) { + mat_dst_fl[i++] = MATRIX_ITEM(mat_src, row, col); + } + } +} + /* assumes rowsize == colsize is checked and the read callback has run */ static float matrix_determinant_internal(const MatrixObject *self) { @@ -1078,7 +1093,7 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self) mat4_to_quat(quat, (float (*)[4])self->matrix); } - return Quaternion_CreatePyObject(quat, Py_NEW, NULL); + return Quaternion_CreatePyObject(quat, NULL); } /*---------------------------matrix.toEuler() --------------------*/ @@ -1152,7 +1167,7 @@ static PyObject *Matrix_to_euler(MatrixObject *self, PyObject *args) else mat3_to_eulO(eul, order, mat); } - return Euler_CreatePyObject(eul, order, Py_NEW, NULL); + return Euler_CreatePyObject(eul, order, NULL); } PyDoc_STRVAR(Matrix_resize_4x4_doc, @@ -1165,7 +1180,7 @@ static PyObject *Matrix_resize_4x4(MatrixObject *self) float mat[4][4]; int col; - if (self->wrapped == Py_WRAP) { + if (self->flag & BASE_MATH_FLAG_IS_WRAP) { PyErr_SetString(PyExc_ValueError, "Matrix.resize_4x4(): " "cannot resize wrapped data - make a copy and resize that"); @@ -1214,12 +1229,12 @@ static PyObject *Matrix_to_4x4(MatrixObject *self) return NULL; if (self->num_row == 4 && self->num_col == 4) { - return Matrix_CreatePyObject(self->matrix, 4, 4, Py_NEW, Py_TYPE(self)); + return Matrix_CreatePyObject(self->matrix, 4, 4, Py_TYPE(self)); } else if (self->num_row == 3 && self->num_col == 3) { float mat[4][4]; copy_m4_m3(mat, (float (*)[3])self->matrix); - return Matrix_CreatePyObject((float *)mat, 4, 4, Py_NEW, Py_TYPE(self)); + return Matrix_CreatePyObject((float *)mat, 4, 4, Py_TYPE(self)); } /* TODO, 2x2 matrix */ @@ -1252,15 +1267,15 @@ static PyObject *Matrix_to_3x3(MatrixObject *self) matrix_as_3x3(mat, self); - return Matrix_CreatePyObject((float *)mat, 3, 3, Py_NEW, Py_TYPE(self)); + return Matrix_CreatePyObject((float *)mat, 3, 3, Py_TYPE(self)); } PyDoc_STRVAR(Matrix_to_translation_doc, ".. method:: to_translation()\n" "\n" -" Return a the translation part of a 4 row matrix.\n" +" Return the translation part of a 4 row matrix.\n" "\n" -" :return: Return a the translation of a matrix.\n" +" :return: Return the translation of a matrix.\n" " :rtype: :class:`Vector`\n" ); static PyObject *Matrix_to_translation(MatrixObject *self) @@ -1275,15 +1290,15 @@ static PyObject *Matrix_to_translation(MatrixObject *self) return NULL; } - return Vector_CreatePyObject(MATRIX_COL_PTR(self, 3), 3, Py_NEW, NULL); + return Vector_CreatePyObject(MATRIX_COL_PTR(self, 3), 3, NULL); } PyDoc_STRVAR(Matrix_to_scale_doc, ".. method:: to_scale()\n" "\n" -" Return a the scale part of a 3x3 or 4x4 matrix.\n" +" Return the scale part of a 3x3 or 4x4 matrix.\n" "\n" -" :return: Return a the scale of a matrix.\n" +" :return: Return the scale of a matrix.\n" " :rtype: :class:`Vector`\n" "\n" " .. note:: This method does not return negative a scale on any axis because it is not possible to obtain this data from the matrix alone.\n" @@ -1310,7 +1325,7 @@ static PyObject *Matrix_to_scale(MatrixObject *self) /* compatible mat4_to_loc_rot_size */ mat3_to_rot_size(rot, size, mat); - return Vector_CreatePyObject(size, 3, Py_NEW, NULL); + return Vector_CreatePyObject(size, 3, NULL); } /*---------------------------matrix.invert() ---------------------*/ @@ -1383,7 +1398,7 @@ PyDoc_STRVAR(Matrix_invert_doc, ); static PyObject *Matrix_invert(MatrixObject *self, PyObject *args) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (matrix_invert_is_compat(self) == false) { @@ -1423,8 +1438,8 @@ PyDoc_STRVAR(Matrix_inverted_doc, "\n" " Return an inverted copy of the matrix.\n" "\n" -" :arg fallback: return this value when the inverse can't be calculated\n" -" (instead of raising a :exc:`ValueError` exception).\n" +" :arg fallback: return this when the inverse can't be calculated\n" +" (instead of raising a :exc:`ValueError`).\n" " :type fallback: any\n" " :return: the inverted matrix or fallback when given.\n" " :rtype: :class:`Matrix`\n" @@ -1464,7 +1479,7 @@ static PyObject *Matrix_inverted(MatrixObject *self, PyObject *args) static PyObject *Matrix_inverted_noargs(MatrixObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (matrix_invert_is_compat(self) == false) { @@ -1494,7 +1509,7 @@ PyDoc_STRVAR(Matrix_invert_safe_doc, ); static PyObject *Matrix_invert_safe(MatrixObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (matrix_invert_is_compat(self) == false) { @@ -1545,7 +1560,7 @@ PyDoc_STRVAR(Matrix_adjugate_doc, ); static PyObject *Matrix_adjugate(MatrixObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (self->num_col != self->num_row) { @@ -1589,7 +1604,7 @@ static PyObject *Matrix_adjugated(MatrixObject *self) PyDoc_STRVAR(Matrix_rotate_doc, ".. method:: rotate(other)\n" "\n" -" Rotates the matrix a by another mathutils value.\n" +" Rotates the matrix by another mathutils value.\n" "\n" " :arg other: rotation component of mathutils value\n" " :type other: :class:`Euler`, :class:`Quaternion` or :class:`Matrix`\n" @@ -1600,7 +1615,7 @@ static PyObject *Matrix_rotate(MatrixObject *self, PyObject *value) { float self_rmat[3][3], other_rmat[3][3], rmat[3][3]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (mathutils_any_to_rotmat(other_rmat, value, "matrix.rotate(value)") == -1) @@ -1653,10 +1668,10 @@ static PyObject *Matrix_decompose(MatrixObject *self) mat3_to_quat(quat, rot); ret = PyTuple_New(3); - PyTuple_SET_ITEM(ret, 0, Vector_CreatePyObject(loc, 3, Py_NEW, NULL)); - PyTuple_SET_ITEM(ret, 1, Quaternion_CreatePyObject(quat, Py_NEW, NULL)); - PyTuple_SET_ITEM(ret, 2, Vector_CreatePyObject(size, 3, Py_NEW, NULL)); - + PyTuple_SET_ITEMS(ret, + Vector_CreatePyObject(loc, 3, NULL), + Quaternion_CreatePyObject(quat, NULL), + Vector_CreatePyObject(size, 3, NULL)); return ret; } @@ -1706,7 +1721,7 @@ static PyObject *Matrix_lerp(MatrixObject *self, PyObject *args) return NULL; } - return Matrix_CreatePyObject(mat, self->num_col, self->num_row, Py_NEW, Py_TYPE(self)); + return Matrix_CreatePyObject(mat, self->num_col, self->num_row, Py_TYPE(self)); } /*---------------------------matrix.determinant() ----------------*/ @@ -1715,7 +1730,7 @@ PyDoc_STRVAR(Matrix_determinant_doc, "\n" " Return the determinant of a matrix.\n" "\n" -" :return: Return a the determinant of a matrix.\n" +" :return: Return the determinant of a matrix.\n" " :rtype: float\n" "\n" " .. seealso:: <http://en.wikipedia.org/wiki/Determinant>\n" @@ -1744,7 +1759,7 @@ PyDoc_STRVAR(Matrix_transpose_doc, ); static PyObject *Matrix_transpose(MatrixObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (self->num_col != self->num_row) { @@ -1791,7 +1806,7 @@ PyDoc_STRVAR(Matrix_normalize_doc, ); static PyObject *Matrix_normalize(MatrixObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (self->num_col != self->num_row) { @@ -1836,19 +1851,37 @@ PyDoc_STRVAR(Matrix_zero_doc, "\n" " Set all the matrix values to zero.\n" "\n" -" :return: an instance of itself\n" " :rtype: :class:`Matrix`\n" ); static PyObject *Matrix_zero(MatrixObject *self) { + if (BaseMath_Prepare_ForWrite(self) == -1) + return NULL; + fill_vn_fl(self->matrix, self->num_col * self->num_row, 0.0f); if (BaseMath_WriteCallback(self) == -1) return NULL; Py_RETURN_NONE; + } /*---------------------------matrix.identity(() ------------------*/ +static void matrix_identity_internal(MatrixObject *self) +{ + BLI_assert((self->num_col == self->num_row) && (self->num_row <= 4)); + + if (self->num_col == 2) { + unit_m2((float (*)[2])self->matrix); + } + else if (self->num_col == 3) { + unit_m3((float (*)[3])self->matrix); + } + else { + unit_m4((float (*)[4])self->matrix); + } +} + PyDoc_STRVAR(Matrix_identity_doc, ".. method:: identity()\n" "\n" @@ -1861,7 +1894,7 @@ PyDoc_STRVAR(Matrix_identity_doc, ); static PyObject *Matrix_identity(MatrixObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (self->num_col != self->num_row) { @@ -1871,15 +1904,7 @@ static PyObject *Matrix_identity(MatrixObject *self) return NULL; } - if (self->num_col == 2) { - unit_m2((float (*)[2])self->matrix); - } - else if (self->num_col == 3) { - unit_m3((float (*)[3])self->matrix); - } - else { - unit_m4((float (*)[4])self->matrix); - } + matrix_identity_internal(self); if (BaseMath_WriteCallback(self) == -1) return NULL; @@ -1891,7 +1916,7 @@ static PyObject *Matrix_identity(MatrixObject *self) static PyObject *Matrix_copy_notest(MatrixObject *self, const float *matrix) { - return Matrix_CreatePyObject((float *)matrix, self->num_col, self->num_row, Py_NEW, Py_TYPE(self)); + return Matrix_CreatePyObject((float *)matrix, self->num_col, self->num_row, Py_TYPE(self)); } PyDoc_STRVAR(Matrix_copy_doc, @@ -2025,7 +2050,22 @@ static PyObject *Matrix_richcmpr(PyObject *a, PyObject *b, int op) return NULL; } - return Py_INCREF(res), res; + return Py_INCREF_RET(res); +} + +static Py_hash_t Matrix_hash(MatrixObject *self) +{ + float mat[SQUARE(MATRIX_MAX_DIM)]; + + if (BaseMath_ReadCallback(self) == -1) + return -1; + + if (BaseMathObject_Prepare_ForHash(self) == -1) + return -1; + + matrix_transpose_internal(mat, self); + + return mathutils_array_hash(mat, self->num_row * self->num_col); } /*---------------------SEQUENCE PROTOCOLS------------------------ @@ -2040,7 +2080,7 @@ static int Matrix_len(MatrixObject *self) * the wrapped vector gives direct access to the matrix data */ static PyObject *Matrix_item_row(MatrixObject *self, int row) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (row < 0 || row >= self->num_row) { @@ -2054,7 +2094,7 @@ static PyObject *Matrix_item_row(MatrixObject *self, int row) /* same but column access */ static PyObject *Matrix_item_col(MatrixObject *self, int col) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (col < 0 || col >= self->num_col) { @@ -2073,7 +2113,7 @@ static int Matrix_ass_item_row(MatrixObject *self, int row, PyObject *value) { int col; float vec[MATRIX_MAX_DIM]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; if (row >= self->num_row || row < 0) { @@ -2098,7 +2138,7 @@ static int Matrix_ass_item_col(MatrixObject *self, int col, PyObject *value) { int row; float vec[MATRIX_MAX_DIM]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; if (col >= self->num_col || col < 0) { @@ -2150,7 +2190,7 @@ static int Matrix_ass_slice(MatrixObject *self, int begin, int end, PyObject *va { PyObject *value_fast = NULL; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; CLAMP(begin, 0, self->num_row); @@ -2233,7 +2273,7 @@ static PyObject *Matrix_add(PyObject *m1, PyObject *m2) add_vn_vnvn(mat, mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row); - return Matrix_CreatePyObject(mat, mat1->num_col, mat1->num_row, Py_NEW, Py_TYPE(mat1)); + return Matrix_CreatePyObject(mat, mat1->num_col, mat1->num_row, Py_TYPE(mat1)); } /*------------------------obj - obj------------------------------ * subtraction */ @@ -2265,7 +2305,7 @@ static PyObject *Matrix_sub(PyObject *m1, PyObject *m2) sub_vn_vnvn(mat, mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row); - return Matrix_CreatePyObject(mat, mat1->num_col, mat1->num_row, Py_NEW, Py_TYPE(mat1)); + return Matrix_CreatePyObject(mat, mat1->num_col, mat1->num_row, Py_TYPE(mat1)); } /*------------------------obj * obj------------------------------ * multiplication */ @@ -2273,7 +2313,7 @@ static PyObject *matrix_mul_float(MatrixObject *mat, const float scalar) { float tmat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; mul_vn_vn_fl(tmat, mat->matrix, mat->num_col * mat->num_row, scalar); - return Matrix_CreatePyObject(tmat, mat->num_col, mat->num_row, Py_NEW, Py_TYPE(mat)); + return Matrix_CreatePyObject(tmat, mat->num_col, mat->num_row, Py_TYPE(mat)); } static PyObject *Matrix_mul(PyObject *m1, PyObject *m2) @@ -2317,7 +2357,7 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2) } } - return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_NEW, Py_TYPE(mat1)); + return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_TYPE(mat1)); } else if (mat2) { /*FLOAT/INT * MATRIX */ @@ -2343,7 +2383,7 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2) vec_size = mat1->num_row; } - return Vector_CreatePyObject(tvec, vec_size, Py_NEW, Py_TYPE(m2)); + return Vector_CreatePyObject(tvec, vec_size, Py_TYPE(m2)); } /*FLOAT/INT * MATRIX */ else if (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0) { @@ -2516,7 +2556,7 @@ static int Matrix_translation_set(MatrixObject *self, PyObject *value, void *UNU { float tvec[3]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; /*must be 4x4 square matrix*/ @@ -2652,6 +2692,7 @@ static PyGetSetDef Matrix_getseters[] = { {(char *)"is_orthogonal", (getter)Matrix_is_orthogonal_get, (setter)NULL, Matrix_is_orthogonal_doc, NULL}, {(char *)"is_orthogonal_axis_vectors", (getter)Matrix_is_orthogonal_axis_vectors_get, (setter)NULL, Matrix_is_orthogonal_axis_vectors_doc, NULL}, {(char *)"is_wrapped", (getter)BaseMathObject_is_wrapped_get, (setter)NULL, BaseMathObject_is_wrapped_doc, NULL}, + {(char *)"is_frozen", (getter)BaseMathObject_is_frozen_get, (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, {(char *)"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; @@ -2695,6 +2736,9 @@ static struct PyMethodDef Matrix_methods[] = { {"__copy__", (PyCFunction) Matrix_copy, METH_NOARGS, Matrix_copy_doc}, {"__deepcopy__", (PyCFunction) Matrix_deepcopy, METH_VARARGS, Matrix_copy_doc}, + /* base-math methods */ + {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, + /* class methods */ {"Identity", (PyCFunction) C_Matrix_Identity, METH_VARARGS | METH_CLASS, C_Matrix_Identity_doc}, {"Rotation", (PyCFunction) C_Matrix_Rotation, METH_VARARGS | METH_CLASS, C_Matrix_Rotation_doc}, @@ -2707,7 +2751,14 @@ static struct PyMethodDef Matrix_methods[] = { /*------------------PY_OBECT DEFINITION--------------------------*/ PyDoc_STRVAR(matrix_doc, -"This object gives access to Matrices in Blender." +".. class:: Matrix([rows])\n" +"\n" +" This object gives access to Matrices in Blender, supporting square and rectangular\n" +" matrices from 2x2 up to 4x4.\n" +"\n" +" :param rows: Sequence of rows.\n" +" When ommitted, a 4x4 identity matrix is constructed.\n" +" :type rows: 2d number sequence\n" ); PyTypeObject matrix_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -2723,7 +2774,7 @@ PyTypeObject matrix_Type = { &Matrix_NumMethods, /*tp_as_number*/ &Matrix_SeqMethods, /*tp_as_sequence*/ &Matrix_AsMapping, /*tp_as_mapping*/ - NULL, /*tp_hash*/ + (hashfunc)Matrix_hash, /*tp_hash*/ NULL, /*tp_call*/ #ifndef MATH_STANDALONE (reprfunc) Matrix_str, /*tp_str*/ @@ -2762,15 +2813,13 @@ PyTypeObject matrix_Type = { NULL /*tp_del*/ }; -/* pass Py_WRAP - if vector is a WRAPPER for data allocated by BLENDER - * (i.e. it was allocated elsewhere by MEM_mallocN()) - * pass Py_NEW - if vector is not a WRAPPER and managed by PYTHON - * (i.e. it must be created here with PyMEM_malloc()) */ -PyObject *Matrix_CreatePyObject(float *mat, - const unsigned short num_col, const unsigned short num_row, - int type, PyTypeObject *base_type) +PyObject *Matrix_CreatePyObject( + const float *mat, + const unsigned short num_col, const unsigned short num_row, + PyTypeObject *base_type) { MatrixObject *self; + float *mat_alloc; /* matrix objects can be any 2-4row x 2-4col matrix */ if (num_col < 2 || num_col > 4 || num_row < 2 || num_row > 4) { @@ -2780,10 +2829,17 @@ PyObject *Matrix_CreatePyObject(float *mat, return NULL; } - self = base_type ? (MatrixObject *)base_type->tp_alloc(base_type, 0) : - (MatrixObject *)PyObject_GC_New(MatrixObject, &matrix_Type); + mat_alloc = PyMem_Malloc(num_col * num_row * sizeof(float)); + if (UNLIKELY(mat_alloc == NULL)) { + PyErr_SetString(PyExc_MemoryError, + "Matrix(): " + "problem allocating data"); + return NULL; + } + self = BASE_MATH_NEW(MatrixObject, matrix_Type, base_type); if (self) { + self->matrix = mat_alloc; self->num_col = num_col; self->num_row = num_row; @@ -2791,37 +2847,52 @@ PyObject *Matrix_CreatePyObject(float *mat, self->cb_user = NULL; self->cb_type = self->cb_subtype = 0; - if (type == Py_WRAP) { - self->matrix = mat; - self->wrapped = Py_WRAP; + if (mat) { /*if a float array passed*/ + memcpy(self->matrix, mat, num_col * num_row * sizeof(float)); } - else if (type == Py_NEW) { - self->matrix = PyMem_Malloc(num_col * num_row * sizeof(float)); - if (self->matrix == NULL) { /*allocation failure*/ - PyErr_SetString(PyExc_MemoryError, - "Matrix(): " - "problem allocating pointer space"); - return NULL; - } - - if (mat) { /*if a float array passed*/ - memcpy(self->matrix, mat, num_col * num_row * sizeof(float)); - } - else if (num_col == num_row) { - /* or if no arguments are passed return identity matrix for square matrices */ - PyObject *ret_dummy = Matrix_identity(self); - Py_DECREF(ret_dummy); - } - else { - /* otherwise zero everything */ - memset(self->matrix, 0, num_col * num_row * sizeof(float)); - } - self->wrapped = Py_NEW; + else if (num_col == num_row) { + /* or if no arguments are passed return identity matrix for square matrices */ + matrix_identity_internal(self); } else { - Py_FatalError("Matrix(): invalid type!"); - return NULL; + /* otherwise zero everything */ + memset(self->matrix, 0, num_col * num_row * sizeof(float)); } + self->flag = BASE_MATH_FLAG_DEFAULT; + } + else { + PyMem_Free(mat_alloc); + } + + return (PyObject *)self; +} + +PyObject *Matrix_CreatePyObject_wrap( + float *mat, + const unsigned short num_col, const unsigned short num_row, + PyTypeObject *base_type) +{ + MatrixObject *self; + + /* matrix objects can be any 2-4row x 2-4col matrix */ + if (num_col < 2 || num_col > 4 || num_row < 2 || num_row > 4) { + PyErr_SetString(PyExc_RuntimeError, + "Matrix(): " + "row and column sizes must be between 2 and 4"); + return NULL; + } + + self = BASE_MATH_NEW(MatrixObject, matrix_Type, base_type); + if (self) { + self->num_col = num_col; + self->num_row = num_row; + + /* init callbacks as NULL */ + self->cb_user = NULL; + self->cb_type = self->cb_subtype = 0; + + self->matrix = mat; + self->flag = BASE_MATH_FLAG_DEFAULT | BASE_MATH_FLAG_IS_WRAP; } return (PyObject *) self; } @@ -2830,7 +2901,7 @@ PyObject *Matrix_CreatePyObject_cb(PyObject *cb_user, const unsigned short num_col, const unsigned short num_row, unsigned char cb_type, unsigned char cb_subtype) { - MatrixObject *self = (MatrixObject *)Matrix_CreatePyObject(NULL, num_col, num_row, Py_NEW, NULL); + MatrixObject *self = (MatrixObject *)Matrix_CreatePyObject(NULL, num_col, num_row, NULL); if (self) { Py_INCREF(cb_user); self->cb_user = cb_user; diff --git a/source/blender/python/mathutils/mathutils_Matrix.h b/source/blender/python/mathutils/mathutils_Matrix.h index f94af9e540e..9ae5a4bd61d 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.h +++ b/source/blender/python/mathutils/mathutils_Matrix.h @@ -61,12 +61,21 @@ typedef struct { * blender (stored in blend_data). This is an either/or struct not both */ /* prototypes */ -PyObject *Matrix_CreatePyObject(float *mat, - const unsigned short num_col, const unsigned short num_row, - int type, PyTypeObject *base_type); -PyObject *Matrix_CreatePyObject_cb(PyObject *user, - const unsigned short num_col, const unsigned short num_row, - unsigned char cb_type, unsigned char cb_subtype); +PyObject *Matrix_CreatePyObject( + const float *mat, + const unsigned short num_col, const unsigned short num_row, + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT; +PyObject *Matrix_CreatePyObject_wrap( + float *mat, + const unsigned short num_col, const unsigned short num_row, + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +PyObject *Matrix_CreatePyObject_cb( + PyObject *user, + const unsigned short num_col, const unsigned short num_row, + unsigned char cb_type, unsigned char cb_subtype + ) ATTR_WARN_UNUSED_RESULT; extern unsigned char mathutils_matrix_row_cb_index; /* default */ extern unsigned char mathutils_matrix_col_cb_index; diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index ae3476f5802..1752be6e306 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -32,6 +32,8 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "../generic/python_utildefines.h" + #ifndef MATH_STANDALONE # include "BLI_dynstr.h" #endif @@ -121,7 +123,7 @@ static PyObject *Quaternion_to_euler(QuaternionObject *self, PyObject *args) else quat_to_eulO(eul, order, tquat); } - return Euler_CreatePyObject(eul, order, Py_NEW, NULL); + return Euler_CreatePyObject(eul, order, NULL); } PyDoc_STRVAR(Quaternion_to_matrix_doc, @@ -140,7 +142,7 @@ static PyObject *Quaternion_to_matrix(QuaternionObject *self) return NULL; quat_to_mat3((float (*)[3])mat, self->quat); - return Matrix_CreatePyObject(mat, 3, 3, Py_NEW, NULL); + return Matrix_CreatePyObject(mat, 3, 3, NULL); } PyDoc_STRVAR(Quaternion_to_axis_angle_doc, @@ -169,11 +171,36 @@ static PyObject *Quaternion_to_axis_angle(QuaternionObject *self) quat__axis_angle_sanitize(axis, &angle); ret = PyTuple_New(2); - PyTuple_SET_ITEM(ret, 0, Vector_CreatePyObject(axis, 3, Py_NEW, NULL)); - PyTuple_SET_ITEM(ret, 1, PyFloat_FromDouble(angle)); + PyTuple_SET_ITEMS(ret, + Vector_CreatePyObject(axis, 3, NULL), + PyFloat_FromDouble(angle)); return ret; } +PyDoc_STRVAR(Quaternion_to_exponential_map_doc, +".. method:: to_exponential_map()\n" +"\n" +" Return the exponential map representation of the quaternion.\n" +"\n" +" This representation consist of the rotation axis multiplied by the rotation angle." +" Such a representation is useful for interpolation between multiple orientations.\n" +"\n" +" :return: exponential map.\n" +" :rtype: :class:`Vector` of size 3\n" +"\n" +" To convert back to a quaternion, pass it to the :class:`Quaternion` constructor.\n" +); +static PyObject *Quaternion_to_exponential_map(QuaternionObject *self) +{ + float expmap[3]; + + if (BaseMath_ReadCallback(self) == -1) + return NULL; + + quat_to_expmap(expmap, self->quat); + return Vector_CreatePyObject(expmap, 3, NULL); +} + PyDoc_STRVAR(Quaternion_cross_doc, ".. method:: cross(other)\n" "\n" @@ -198,7 +225,7 @@ static PyObject *Quaternion_cross(QuaternionObject *self, PyObject *value) } mul_qt_qtqt(quat, self->quat, tquat); - return Quaternion_CreatePyObject(quat, Py_NEW, Py_TYPE(self)); + return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } PyDoc_STRVAR(Quaternion_dot_doc, @@ -252,7 +279,7 @@ static PyObject *Quaternion_rotation_difference(QuaternionObject *self, PyObject rotation_between_quats_to_quat(quat, self->quat, tquat); - return Quaternion_CreatePyObject(quat, Py_NEW, Py_TYPE(self)); + return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } PyDoc_STRVAR(Quaternion_slerp_doc, @@ -297,13 +324,13 @@ static PyObject *Quaternion_slerp(QuaternionObject *self, PyObject *args) interp_qt_qtqt(quat, self->quat, tquat, fac); - return Quaternion_CreatePyObject(quat, Py_NEW, Py_TYPE(self)); + return Quaternion_CreatePyObject(quat, Py_TYPE(self)); } PyDoc_STRVAR(Quaternion_rotate_doc, ".. method:: rotate(other)\n" "\n" -" Rotates the quaternion a by another mathutils value.\n" +" Rotates the quaternion by another mathutils value.\n" "\n" " :arg other: rotation component of mathutils value\n" " :type other: :class:`Euler`, :class:`Quaternion` or :class:`Matrix`\n" @@ -313,7 +340,7 @@ static PyObject *Quaternion_rotate(QuaternionObject *self, PyObject *value) float self_rmat[3][3], other_rmat[3][3], rmat[3][3]; float tquat[4], length; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (mathutils_any_to_rotmat(other_rmat, value, "Quaternion.rotate(value)") == -1) @@ -331,7 +358,8 @@ static PyObject *Quaternion_rotate(QuaternionObject *self, PyObject *value) } /* ----------------------------Quaternion.normalize()---------------- */ -/* normalize the axis of rotation of [theta, vector] */ +/* Normalize the quaternion. This may change the angle as well as the + * rotation axis, as all of (w, x, y, z) are scaled. */ PyDoc_STRVAR(Quaternion_normalize_doc, ".. function:: normalize()\n" "\n" @@ -339,7 +367,7 @@ PyDoc_STRVAR(Quaternion_normalize_doc, ); static PyObject *Quaternion_normalize(QuaternionObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; normalize_qt(self->quat); @@ -367,7 +395,7 @@ PyDoc_STRVAR(Quaternion_invert_doc, ); static PyObject *Quaternion_invert(QuaternionObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; invert_qt(self->quat); @@ -393,12 +421,11 @@ PyDoc_STRVAR(Quaternion_identity_doc, "\n" " Set the quaternion to an identity quaternion.\n" "\n" -" :return: an instance of itself.\n" " :rtype: :class:`Quaternion`\n" ); static PyObject *Quaternion_identity(QuaternionObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; unit_qt(self->quat); @@ -412,12 +439,11 @@ PyDoc_STRVAR(Quaternion_negate_doc, "\n" " Set the quaternion to its negative.\n" "\n" -" :return: an instance of itself.\n" " :rtype: :class:`Quaternion`\n" ); static PyObject *Quaternion_negate(QuaternionObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; mul_qt_fl(self->quat, -1.0f); @@ -433,7 +459,7 @@ PyDoc_STRVAR(Quaternion_conjugate_doc, ); static PyObject *Quaternion_conjugate(QuaternionObject *self) { - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; conjugate_qt(self->quat); @@ -470,7 +496,7 @@ static PyObject *Quaternion_copy(QuaternionObject *self) if (BaseMath_ReadCallback(self) == -1) return NULL; - return Quaternion_CreatePyObject(self->quat, Py_NEW, Py_TYPE(self)); + return Quaternion_CreatePyObject(self->quat, Py_TYPE(self)); } static PyObject *Quaternion_deepcopy(QuaternionObject *self, PyObject *args) { @@ -546,7 +572,18 @@ static PyObject *Quaternion_richcmpr(PyObject *a, PyObject *b, int op) return NULL; } - return Py_INCREF(res), res; + return Py_INCREF_RET(res); +} + +static Py_hash_t Quaternion_hash(QuaternionObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) + return -1; + + if (BaseMathObject_Prepare_ForHash(self) == -1) + return -1; + + return mathutils_array_hash(self->quat, QUAT_SIZE); } /* ---------------------SEQUENCE PROTOCOLS------------------------ */ @@ -579,8 +616,14 @@ static PyObject *Quaternion_item(QuaternionObject *self, int i) /* sequence accessor (set) */ static int Quaternion_ass_item(QuaternionObject *self, int i, PyObject *ob) { - float scalar = (float)PyFloat_AsDouble(ob); - if (scalar == -1.0f && PyErr_Occurred()) { /* parsed item not a number */ + float f; + + if (BaseMath_Prepare_ForWrite(self) == -1) + return -1; + + f = (float)PyFloat_AsDouble(ob); + + if (f == -1.0f && PyErr_Occurred()) { /* parsed item not a number */ PyErr_SetString(PyExc_TypeError, "quaternion[index] = x: " "assigned value not a number"); @@ -595,7 +638,7 @@ static int Quaternion_ass_item(QuaternionObject *self, int i, PyObject *ob) "array assignment index out of range"); return -1; } - self->quat[i] = scalar; + self->quat[i] = f; if (BaseMath_WriteIndexCallback(self, i) == -1) return -1; @@ -631,7 +674,7 @@ static int Quaternion_ass_slice(QuaternionObject *self, int begin, int end, PyOb int i, size; float quat[QUAT_SIZE]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; CLAMP(begin, 0, QUAT_SIZE); @@ -750,7 +793,7 @@ static PyObject *Quaternion_add(PyObject *q1, PyObject *q2) return NULL; add_qt_qtqt(quat, quat1->quat, quat2->quat, 1.0f); - return Quaternion_CreatePyObject(quat, Py_NEW, Py_TYPE(q1)); + return Quaternion_CreatePyObject(quat, Py_TYPE(q1)); } /* ------------------------obj - obj------------------------------ */ /* subtraction */ @@ -778,7 +821,7 @@ static PyObject *Quaternion_sub(PyObject *q1, PyObject *q2) quat[x] = quat1->quat[x] - quat2->quat[x]; } - return Quaternion_CreatePyObject(quat, Py_NEW, Py_TYPE(q1)); + return Quaternion_CreatePyObject(quat, Py_TYPE(q1)); } static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar) @@ -786,7 +829,7 @@ static PyObject *quat_mul_float(QuaternionObject *quat, const float scalar) float tquat[4]; copy_qt_qt(tquat, quat->quat); mul_qt_fl(tquat, scalar); - return Quaternion_CreatePyObject(tquat, Py_NEW, Py_TYPE(quat)); + return Quaternion_CreatePyObject(tquat, Py_TYPE(quat)); } /*------------------------obj * obj------------------------------ @@ -809,7 +852,7 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2) if (quat1 && quat2) { /* QUAT * QUAT (cross product) */ mul_qt_qtqt(quat, quat1->quat, quat2->quat); - return Quaternion_CreatePyObject(quat, Py_NEW, Py_TYPE(q1)); + return Quaternion_CreatePyObject(quat, Py_TYPE(q1)); } /* the only case this can happen (for a supported type is "FLOAT * QUAT") */ else if (quat2) { /* FLOAT * QUAT */ @@ -837,7 +880,7 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2) copy_v3_v3(tvec, vec2->vec); mul_qt_v3(quat1->quat, tvec); - return Vector_CreatePyObject(tvec, 3, Py_NEW, Py_TYPE(vec2)); + return Vector_CreatePyObject(tvec, 3, Py_TYPE(vec2)); } /* QUAT * FLOAT */ else if ((((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) { @@ -865,7 +908,7 @@ static PyObject *Quaternion_neg(QuaternionObject *self) return NULL; negate_v4_v4(tquat, self->quat); - return Quaternion_CreatePyObject(tquat, Py_NEW, Py_TYPE(self)); + return Quaternion_CreatePyObject(tquat, Py_TYPE(self)); } @@ -978,7 +1021,7 @@ static int Quaternion_angle_set(QuaternionObject *self, PyObject *value, void *U float axis[3], angle_dummy; float angle; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; len = normalize_qt_qt(tquat, self->quat); @@ -1023,7 +1066,7 @@ static PyObject *Quaternion_axis_vector_get(QuaternionObject *self, void *UNUSED quat__axis_angle_sanitize(axis, NULL); - return Vector_CreatePyObject(axis, 3, Py_NEW, NULL); + return Vector_CreatePyObject(axis, 3, NULL); } static int Quaternion_axis_vector_set(QuaternionObject *self, PyObject *value, void *UNUSED(closure)) @@ -1034,7 +1077,7 @@ static int Quaternion_axis_vector_set(QuaternionObject *self, PyObject *value, v float axis[3]; float angle; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; len = normalize_qt_qt(tquat, self->quat); @@ -1075,9 +1118,24 @@ static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kw case 0: break; case 1: - if (mathutils_array_parse(quat, QUAT_SIZE, QUAT_SIZE, seq, "mathutils.Quaternion()") == -1) + { + int size; + + if ((size = mathutils_array_parse(quat, 3, QUAT_SIZE, seq, "mathutils.Quaternion()")) == -1) { return NULL; + } + + if (size == 4) { + /* 4d: Quaternion (common case) */ + } + else { + /* 3d: Interpret as exponential map */ + BLI_assert(size == 3); + expmap_to_quat(quat, quat); + } + break; + } case 2: { float axis[3]; @@ -1089,7 +1147,7 @@ static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kw /* PyArg_ParseTuple assures no more than 2 */ } } - return Quaternion_CreatePyObject(quat, Py_NEW, type); + return Quaternion_CreatePyObject(quat, type); } static PyObject *quat__apply_to_copy(PyNoArgsFunction quat_func, QuaternionObject *self) @@ -1154,6 +1212,7 @@ static struct PyMethodDef Quaternion_methods[] = { {"to_euler", (PyCFunction) Quaternion_to_euler, METH_VARARGS, Quaternion_to_euler_doc}, {"to_matrix", (PyCFunction) Quaternion_to_matrix, METH_NOARGS, Quaternion_to_matrix_doc}, {"to_axis_angle", (PyCFunction) Quaternion_to_axis_angle, METH_NOARGS, Quaternion_to_axis_angle_doc}, + {"to_exponential_map", (PyCFunction) Quaternion_to_exponential_map, METH_NOARGS, Quaternion_to_exponential_map_doc}, /* operation between 2 or more types */ {"cross", (PyCFunction) Quaternion_cross, METH_O, Quaternion_cross_doc}, @@ -1162,6 +1221,9 @@ static struct PyMethodDef Quaternion_methods[] = { {"slerp", (PyCFunction) Quaternion_slerp, METH_VARARGS, Quaternion_slerp_doc}, {"rotate", (PyCFunction) Quaternion_rotate, METH_O, Quaternion_rotate_doc}, + /* base-math methods */ + {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, + {"copy", (PyCFunction) Quaternion_copy, METH_NOARGS, Quaternion_copy_doc}, {"__copy__", (PyCFunction) Quaternion_copy, METH_NOARGS, Quaternion_copy_doc}, {"__deepcopy__", (PyCFunction) Quaternion_deepcopy, METH_VARARGS, Quaternion_copy_doc}, @@ -1180,13 +1242,36 @@ static PyGetSetDef Quaternion_getseters[] = { {(char *)"angle", (getter)Quaternion_angle_get, (setter)Quaternion_angle_set, Quaternion_angle_doc, NULL}, {(char *)"axis", (getter)Quaternion_axis_vector_get, (setter)Quaternion_axis_vector_set, Quaternion_axis_vector_doc, NULL}, {(char *)"is_wrapped", (getter)BaseMathObject_is_wrapped_get, (setter)NULL, BaseMathObject_is_wrapped_doc, NULL}, + {(char *)"is_frozen", (getter)BaseMathObject_is_frozen_get, (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, {(char *)"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; /* ------------------PY_OBECT DEFINITION-------------------------- */ PyDoc_STRVAR(quaternion_doc, -"This object gives access to Quaternions in Blender." +".. class:: Quaternion([seq, [angle]])\n" +"\n" +" This object gives access to Quaternions in Blender.\n" +"\n" +" :param seq: size 3 or 4\n" +" :type seq: :class:`Vector`\n" +" :param angle: rotation angle, in radians\n" +" :type angle: float\n" +"\n" +" The constructor takes arguments in various forms:\n" +"\n" +" (), *no args*\n" +" Create an identity quaternion\n" +" (*wxyz*)\n" +" Create a quaternion from a ``(w, x, y, z)`` vector.\n" +" (*exponential_map*)\n" +" Create a quaternion from a 3d exponential map vector.\n" +"\n" +" .. seealso:: :meth:`to_exponential_map`\n" +" (*axis, angle*)\n" +" Create a quaternion representing a rotation of *angle* radians over *axis*.\n" +"\n" +" .. seealso:: :meth:`to_axis_angle`\n" ); PyTypeObject quaternion_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -1202,7 +1287,7 @@ PyTypeObject quaternion_Type = { &Quaternion_NumMethods, /* tp_as_number */ &Quaternion_SeqMethods, /* tp_as_sequence */ &Quaternion_AsMapping, /* tp_as_mapping */ - NULL, /* tp_hash */ + (hashfunc)Quaternion_hash, /* tp_hash */ NULL, /* tp_call */ #ifndef MATH_STANDALONE (reprfunc) Quaternion_str, /* tp_str */ @@ -1240,41 +1325,60 @@ PyTypeObject quaternion_Type = { NULL, /* tp_weaklist */ NULL, /* tp_del */ }; -/* ------------------------Quaternion_CreatePyObject (internal)------------- */ -/* creates a new quaternion object */ -/*pass Py_WRAP - if vector is a WRAPPER for data allocated by BLENDER - * (i.e. it was allocated elsewhere by MEM_mallocN()) - * pass Py_NEW - if vector is not a WRAPPER and managed by PYTHON - * (i.e. it must be created here with PyMEM_malloc())*/ -PyObject *Quaternion_CreatePyObject(float quat[4], int type, PyTypeObject *base_type) + +PyObject *Quaternion_CreatePyObject( + const float quat[4], + PyTypeObject *base_type) { QuaternionObject *self; + float *quat_alloc; - self = base_type ? (QuaternionObject *)base_type->tp_alloc(base_type, 0) : - (QuaternionObject *)PyObject_GC_New(QuaternionObject, &quaternion_Type); + quat_alloc = PyMem_Malloc(QUAT_SIZE * sizeof(float)); + if (UNLIKELY(quat_alloc == NULL)) { + PyErr_SetString(PyExc_MemoryError, + "Quaternion(): " + "problem allocating data"); + return NULL; + } + self = BASE_MATH_NEW(QuaternionObject, quaternion_Type, base_type); if (self) { + self->quat = quat_alloc; /* init callbacks as NULL */ self->cb_user = NULL; self->cb_type = self->cb_subtype = 0; - if (type == Py_WRAP) { - self->quat = quat; - self->wrapped = Py_WRAP; - } - else if (type == Py_NEW) { - self->quat = PyMem_Malloc(QUAT_SIZE * sizeof(float)); - if (!quat) { /* new empty */ - unit_qt(self->quat); - } - else { - copy_qt_qt(self->quat, quat); - } - self->wrapped = Py_NEW; + /* NEW */ + if (!quat) { /* new empty */ + unit_qt(self->quat); } else { - Py_FatalError("Quaternion(): invalid type!"); + copy_qt_qt(self->quat, quat); } + self->flag = BASE_MATH_FLAG_DEFAULT; + } + else { + PyMem_Free(quat_alloc); + } + + return (PyObject *)self; +} + +PyObject *Quaternion_CreatePyObject_wrap( + float quat[4], + PyTypeObject *base_type) +{ + QuaternionObject *self; + + self = BASE_MATH_NEW(QuaternionObject, quaternion_Type, base_type); + if (self) { + /* init callbacks as NULL */ + self->cb_user = NULL; + self->cb_type = self->cb_subtype = 0; + + /* WRAP */ + self->quat = quat; + self->flag = BASE_MATH_FLAG_DEFAULT | BASE_MATH_FLAG_IS_WRAP; } return (PyObject *) self; } @@ -1282,7 +1386,7 @@ PyObject *Quaternion_CreatePyObject(float quat[4], int type, PyTypeObject *base_ PyObject *Quaternion_CreatePyObject_cb(PyObject *cb_user, unsigned char cb_type, unsigned char cb_subtype) { - QuaternionObject *self = (QuaternionObject *)Quaternion_CreatePyObject(NULL, Py_NEW, NULL); + QuaternionObject *self = (QuaternionObject *)Quaternion_CreatePyObject(NULL, NULL); if (self) { Py_INCREF(cb_user); self->cb_user = cb_user; diff --git a/source/blender/python/mathutils/mathutils_Quaternion.h b/source/blender/python/mathutils/mathutils_Quaternion.h index 36036c6d3fa..66ee3362906 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.h +++ b/source/blender/python/mathutils/mathutils_Quaternion.h @@ -40,8 +40,17 @@ typedef struct { * blender (stored in blend_data). This is an either/or struct not both */ /* prototypes */ -PyObject *Quaternion_CreatePyObject(float quat[4], int type, PyTypeObject *base_type); -PyObject *Quaternion_CreatePyObject_cb(PyObject *cb_user, - unsigned char cb_type, unsigned char cb_subtype); +PyObject *Quaternion_CreatePyObject( + const float quat[4], + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT; +PyObject *Quaternion_CreatePyObject_wrap( + float quat[4], + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +PyObject *Quaternion_CreatePyObject_cb( + PyObject *cb_user, + unsigned char cb_type, unsigned char cb_subtype + ) ATTR_WARN_UNUSED_RESULT; #endif /* __MATHUTILS_QUATERNION_H__ */ diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 15a9860be0a..a33b55c35dc 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -336,6 +336,9 @@ PyDoc_STRVAR(Vector_zero_doc, ); static PyObject *Vector_zero(VectorObject *self) { + if (BaseMath_Prepare_ForWrite(self) == -1) + return NULL; + fill_vn_fl(self->vec, self->size, 0.0f); if (BaseMath_WriteCallback(self) == -1) @@ -357,7 +360,7 @@ PyDoc_STRVAR(Vector_normalize_doc, static PyObject *Vector_normalize(VectorObject *self) { int size = (self->size == 4 ? 3 : self->size); - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; normalize_vn(self->vec, size); @@ -387,7 +390,7 @@ static PyObject *Vector_resize(VectorObject *self, PyObject *value) { int size; - if (self->wrapped == Py_WRAP) { + if (self->flag & BASE_MATH_FLAG_IS_WRAP) { PyErr_SetString(PyExc_TypeError, "Vector.resize(): " "cannot resize wrapped data - only python vectors"); @@ -475,7 +478,7 @@ PyDoc_STRVAR(Vector_resize_2d_doc, ); static PyObject *Vector_resize_2d(VectorObject *self) { - if (self->wrapped == Py_WRAP) { + if (self->flag & BASE_MATH_FLAG_IS_WRAP) { PyErr_SetString(PyExc_TypeError, "Vector.resize_2d(): " "cannot resize wrapped data - only python vectors"); @@ -507,7 +510,7 @@ PyDoc_STRVAR(Vector_resize_3d_doc, ); static PyObject *Vector_resize_3d(VectorObject *self) { - if (self->wrapped == Py_WRAP) { + if (self->flag & BASE_MATH_FLAG_IS_WRAP) { PyErr_SetString(PyExc_TypeError, "Vector.resize_3d(): " "cannot resize wrapped data - only python vectors"); @@ -542,7 +545,7 @@ PyDoc_STRVAR(Vector_resize_4d_doc, ); static PyObject *Vector_resize_4d(VectorObject *self) { - if (self->wrapped == Py_WRAP) { + if (self->flag & BASE_MATH_FLAG_IS_WRAP) { PyErr_SetString(PyExc_TypeError, "Vector.resize_4d(): " "cannot resize wrapped data - only python vectors"); @@ -586,7 +589,7 @@ static PyObject *Vector_to_2d(VectorObject *self) if (BaseMath_ReadCallback(self) == -1) return NULL; - return Vector_CreatePyObject(self->vec, 2, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(self->vec, 2, Py_TYPE(self)); } PyDoc_STRVAR(Vector_to_3d_doc, ".. method:: to_3d()\n" @@ -604,7 +607,7 @@ static PyObject *Vector_to_3d(VectorObject *self) return NULL; memcpy(tvec, self->vec, sizeof(float) * MIN2(self->size, 3)); - return Vector_CreatePyObject(tvec, 3, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(tvec, 3, Py_TYPE(self)); } PyDoc_STRVAR(Vector_to_4d_doc, ".. method:: to_4d()\n" @@ -622,7 +625,7 @@ static PyObject *Vector_to_4d(VectorObject *self) return NULL; memcpy(tvec, self->vec, sizeof(float) * MIN2(self->size, 4)); - return Vector_CreatePyObject(tvec, 4, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(tvec, 4, Py_TYPE(self)); } PyDoc_STRVAR(Vector_to_tuple_doc, @@ -797,7 +800,7 @@ static PyObject *Vector_to_track_quat(VectorObject *self, PyObject *args) vec_to_quat(quat, vec, track, up); - return Quaternion_CreatePyObject(quat, Py_NEW, NULL); + return Quaternion_CreatePyObject(quat, NULL); } PyDoc_STRVAR(Vector_orthogonal_doc, @@ -829,7 +832,7 @@ static PyObject *Vector_orthogonal(VectorObject *self) else ortho_v2_v2(vec, self->vec); - return Vector_CreatePyObject(vec, self->size, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(vec, self->size, Py_TYPE(self)); } @@ -877,7 +880,7 @@ static PyObject *Vector_reflect(VectorObject *self, PyObject *value) normalize_v3(mirror); reflect_v3_v3v3(reflect, vec, mirror); - return Vector_CreatePyObject(reflect, self->size, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(reflect, self->size, Py_TYPE(self)); } PyDoc_STRVAR(Vector_cross_doc, @@ -910,7 +913,7 @@ static PyObject *Vector_cross(VectorObject *self, PyObject *value) return NULL; if (self->size == 3) { - ret = Vector_CreatePyObject(NULL, 3, Py_NEW, Py_TYPE(self)); + ret = Vector_CreatePyObject(NULL, 3, Py_TYPE(self)); cross_v3_v3v3(((VectorObject *)ret)->vec, self->vec, tvec); } else { @@ -954,13 +957,11 @@ PyDoc_STRVAR(Vector_angle_doc, "\n" " :arg other: another vector to compare the angle with\n" " :type other: :class:`Vector`\n" -" :arg fallback: return this value when the angle can't be calculated\n" -" (zero length vector)\n" +" :arg fallback: return this when the angle can't be calculated (zero length vector),\n" +" (instead of raising a :exc:`ValueError`).\n" " :type fallback: any\n" " :return: angle in radians or fallback when given\n" " :rtype: float\n" -"\n" -" .. note:: Zero length vectors raise an :exc:`ValueError`.\n" ); static PyObject *Vector_angle(VectorObject *self, PyObject *args) { @@ -1018,13 +1019,11 @@ PyDoc_STRVAR(Vector_angle_signed_doc, "\n" " :arg other: another vector to compare the angle with\n" " :type other: :class:`Vector`\n" -" :arg fallback: return this value when the angle can't be calculated\n" -" (zero length vector)\n" +" :arg fallback: return this when the angle can't be calculated (zero length vector),\n" +" (instead of raising a :exc:`ValueError`).\n" " :type fallback: any\n" " :return: angle in radians or fallback when given\n" " :rtype: float\n" -"\n" -" .. note:: Zero length vectors raise an :exc:`ValueError`.\n" ); static PyObject *Vector_angle_signed(VectorObject *self, PyObject *args) { @@ -1102,7 +1101,7 @@ static PyObject *Vector_rotation_difference(VectorObject *self, PyObject *value) rotation_between_vecs_to_quat(quat, vec_a, vec_b); - return Quaternion_CreatePyObject(quat, Py_NEW, NULL); + return Quaternion_CreatePyObject(quat, NULL); } PyDoc_STRVAR(Vector_project_doc, @@ -1123,9 +1122,6 @@ static PyObject *Vector_project(VectorObject *self, PyObject *value) double dot = 0.0f, dot2 = 0.0f; int x; - if (BaseMath_ReadCallback(self) == -1) - return NULL; - if (mathutils_array_parse(tvec, size, size, value, "Vector.project(other), invalid 'other' arg") == -1) return NULL; @@ -1148,7 +1144,7 @@ static PyObject *Vector_project(VectorObject *self, PyObject *value) for (x = 0; x < size; x++) { vec[x] = (float)dot * tvec[x]; } - return Vector_CreatePyObject(vec, size, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(vec, size, Py_TYPE(self)); } PyDoc_STRVAR(Vector_lerp_doc, @@ -1167,9 +1163,8 @@ static PyObject *Vector_lerp(VectorObject *self, PyObject *args) { const int size = self->size; PyObject *value = NULL; - float fac, ifac; - float *tvec, *vec; - int x; + float fac; + float *tvec; if (!PyArg_ParseTuple(args, "Of:lerp", &value, &fac)) return NULL; @@ -1182,23 +1177,9 @@ static PyObject *Vector_lerp(VectorObject *self, PyObject *args) return NULL; } - vec = PyMem_Malloc(size * sizeof(float)); - if (vec == NULL) { - PyErr_SetString(PyExc_MemoryError, - "Vector.lerp(): " - "problem allocating pointer space"); - return NULL; - } - - ifac = 1.0f - fac; - - for (x = 0; x < size; x++) { - vec[x] = (ifac * self->vec[x]) + (fac * tvec[x]); - } - - PyMem_Free(tvec); + interp_vn_vn(tvec, self->vec, 1.0f - fac, size); - return Vector_CreatePyObject_alloc(vec, size, Py_TYPE(self)); + return Vector_CreatePyObject_alloc(tvec, size, Py_TYPE(self)); } PyDoc_STRVAR(Vector_slerp_doc, @@ -1210,8 +1191,8 @@ PyDoc_STRVAR(Vector_slerp_doc, " :type other: :class:`Vector`\n" " :arg factor: The interpolation value typically in [0.0, 1.0].\n" " :type factor: float\n" -" :arg fallback: return this value when the vector can't be calculated\n" -" (zero length vector or direct opposites)\n" +" :arg fallback: return this when the vector can't be calculated (zero length vector or direct opposites),\n" +" (instead of raising a :exc:`ValueError`).\n" " :type fallback: any\n" " :return: The interpolated vector.\n" " :rtype: :class:`Vector`\n" @@ -1287,7 +1268,7 @@ static PyObject *Vector_slerp(VectorObject *self, PyObject *args) ret_vec[x] = (w[0] * self_vec[x]) + (w[1] * other_vec[x]); } - return Vector_CreatePyObject(ret_vec, size, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(ret_vec, size, Py_TYPE(self)); } PyDoc_STRVAR(Vector_rotate_doc, @@ -1302,7 +1283,7 @@ static PyObject *Vector_rotate(VectorObject *self, PyObject *value) { float other_rmat[3][3]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return NULL; if (mathutils_any_to_rotmat(other_rmat, value, "Vector.rotate(value)") == -1) @@ -1336,7 +1317,7 @@ static PyObject *Vector_copy(VectorObject *self) if (BaseMath_ReadCallback(self) == -1) return NULL; - return Vector_CreatePyObject(self->vec, self->size, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(self->vec, self->size, Py_TYPE(self)); } static PyObject *Vector_deepcopy(VectorObject *self, PyObject *args) { @@ -1420,6 +1401,10 @@ static PyObject *Vector_item(VectorObject *self, int i) static int vector_ass_item_internal(VectorObject *self, int i, PyObject *value, const bool is_attr) { float scalar; + + if (BaseMath_Prepare_ForWrite(self) == -1) + return -1; + if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) { /* parsed item not a number */ PyErr_SetString(PyExc_TypeError, "vector[index] = x: " @@ -1481,7 +1466,7 @@ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *se int size = 0; float *vec = NULL; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; CLAMP(begin, 0, self->size); @@ -1540,8 +1525,7 @@ static PyObject *Vector_add(PyObject *v1, PyObject *v2) } vec = PyMem_Malloc(vec1->size * sizeof(float)); - - if (vec == NULL) { /*allocation failure*/ + if (vec == NULL) { PyErr_SetString(PyExc_MemoryError, "Vector(): " "problem allocating pointer space"); @@ -1575,7 +1559,7 @@ static PyObject *Vector_iadd(PyObject *v1, PyObject *v2) return NULL; } - if (BaseMath_ReadCallback(vec1) == -1 || BaseMath_ReadCallback(vec2) == -1) + if (BaseMath_ReadCallback_ForWrite(vec1) == -1 || BaseMath_ReadCallback(vec2) == -1) return NULL; add_vn_vn(vec1->vec, vec2->vec, vec1->size); @@ -1612,8 +1596,7 @@ static PyObject *Vector_sub(PyObject *v1, PyObject *v2) } vec = PyMem_Malloc(vec1->size * sizeof(float)); - - if (vec == NULL) { /*allocation failure*/ + if (vec == NULL) { PyErr_SetString(PyExc_MemoryError, "Vector(): " "problem allocating pointer space"); @@ -1647,7 +1630,7 @@ static PyObject *Vector_isub(PyObject *v1, PyObject *v2) return NULL; } - if (BaseMath_ReadCallback(vec1) == -1 || BaseMath_ReadCallback(vec2) == -1) + if (BaseMath_ReadCallback_ForWrite(vec1) == -1 || BaseMath_ReadCallback(vec2) == -1) return NULL; sub_vn_vn(vec1->vec, vec2->vec, vec1->size); @@ -1705,8 +1688,7 @@ int column_vector_multiplication(float r_vec[MAX_DIMENSIONS], VectorObject *vec, static PyObject *vector_mul_float(VectorObject *vec, const float scalar) { float *tvec = PyMem_Malloc(vec->size * sizeof(float)); - - if (tvec == NULL) { /*allocation failure*/ + if (tvec == NULL) { PyErr_SetString(PyExc_MemoryError, "vec * float: " "problem allocating pointer space"); @@ -1765,7 +1747,7 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) vec_size = ((MatrixObject *)v2)->num_col; } - return Vector_CreatePyObject(tvec, vec_size, Py_NEW, Py_TYPE(vec1)); + return Vector_CreatePyObject(tvec, vec_size, Py_TYPE(vec1)); } else if (QuaternionObject_Check(v2)) { /* VEC * QUAT */ @@ -1791,7 +1773,7 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) copy_v3_v3(tvec, vec1->vec); mul_qt_v3(quat2->quat, tvec); - return Vector_CreatePyObject(tvec, 3, Py_NEW, Py_TYPE(vec1)); + return Vector_CreatePyObject(tvec, 3, Py_TYPE(vec1)); #endif /* ------ to be removed ------*/ } @@ -1821,7 +1803,7 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) VectorObject *vec = (VectorObject *)v1; float scalar; - if (BaseMath_ReadCallback(vec) == -1) + if (BaseMath_ReadCallback_ForWrite(vec) == -1) return NULL; /* only support vec*=float and vec*=mat @@ -1923,7 +1905,7 @@ static PyObject *Vector_div(PyObject *v1, PyObject *v2) vec = PyMem_Malloc(vec1->size * sizeof(float)); - if (vec == NULL) { /*allocation failure*/ + if (vec == NULL) { PyErr_SetString(PyExc_MemoryError, "vec / value: " "problem allocating pointer space"); @@ -1941,7 +1923,7 @@ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) float scalar; VectorObject *vec1 = (VectorObject *)v1; - if (BaseMath_ReadCallback(vec1) == -1) + if (BaseMath_ReadCallback_ForWrite(vec1) == -1) return NULL; if ((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) { /* parsed item not a number */ @@ -2065,6 +2047,17 @@ static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int compa } } +static Py_hash_t Vector_hash(VectorObject *self) +{ + if (BaseMath_ReadCallback(self) == -1) + return -1; + + if (BaseMathObject_Prepare_ForHash(self) == -1) + return -1; + + return mathutils_array_hash(self->vec, self->size); +} + /*-----------------PROTCOL DECLARATIONS--------------------------*/ static PySequenceMethods Vector_SeqMethods = { (lenfunc) Vector_len, /* sq_length */ @@ -2228,7 +2221,7 @@ static int Vector_length_set(VectorObject *self, PyObject *value) { double dot = 0.0f, param; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; if ((param = PyFloat_AsDouble(value)) == -1.0 && PyErr_Occurred()) { @@ -2308,7 +2301,7 @@ static PyObject *Vector_swizzle_get(VectorObject *self, void *closure) axis_to++; } - return Vector_CreatePyObject(vec, axis_to, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject(vec, axis_to, Py_TYPE(self)); } /* Set the items of this vector using a swizzle. @@ -2334,7 +2327,7 @@ static int Vector_swizzle_set(VectorObject *self, PyObject *value, void *closure float tvec[MAX_DIMENSIONS]; float vec_assign[MAX_DIMENSIONS]; - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback_ForWrite(self) == -1) return -1; /* Check that the closure can be used with this vector: even 2D vectors have @@ -2415,6 +2408,7 @@ static PyGetSetDef Vector_getseters[] = { {(char *)"length_squared", (getter)Vector_length_squared_get, (setter)NULL, Vector_length_squared_doc, NULL}, {(char *)"magnitude", (getter)Vector_length_get, (setter)Vector_length_set, Vector_length_doc, NULL}, {(char *)"is_wrapped", (getter)BaseMathObject_is_wrapped_get, (setter)NULL, BaseMathObject_is_wrapped_doc, NULL}, + {(char *)"is_frozen", (getter)BaseMathObject_is_frozen_get, (setter)NULL, BaseMathObject_is_frozen_doc, NULL}, {(char *)"owner", (getter)BaseMathObject_owner_get, (setter)NULL, BaseMathObject_owner_doc, NULL}, /* autogenerated swizzle attrs, see python script below */ @@ -2893,6 +2887,9 @@ static struct PyMethodDef Vector_methods[] = { {"slerp", (PyCFunction) Vector_slerp, METH_VARARGS, Vector_slerp_doc}, {"rotate", (PyCFunction) Vector_rotate, METH_O, Vector_rotate_doc}, + /* base-math methods */ + {"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc}, + {"copy", (PyCFunction) Vector_copy, METH_NOARGS, Vector_copy_doc}, {"__copy__", (PyCFunction) Vector_copy, METH_NOARGS, NULL}, {"__deepcopy__", (PyCFunction) Vector_deepcopy, METH_VARARGS, NULL}, @@ -2907,7 +2904,12 @@ static struct PyMethodDef Vector_methods[] = { */ PyDoc_STRVAR(vector_doc, -"This object gives access to Vectors in Blender." +".. class:: Vector(seq)\n" +"\n" +" This object gives access to Vectors in Blender.\n" +"\n" +" :param seq: Components of the vector, must be a sequence of at least two\n" +" :type seq: sequence of numbers\n" ); PyTypeObject vector_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -2933,7 +2935,7 @@ PyTypeObject vector_Type = { /* More standard operations (here for binary compatibility) */ - NULL, /* hashfunc tp_hash; */ + (hashfunc)Vector_hash, /* hashfunc tp_hash; */ NULL, /* ternaryfunc tp_call; */ #ifndef MATH_STANDALONE (reprfunc)Vector_str, /* reprfunc tp_str; */ @@ -2994,15 +2996,12 @@ PyTypeObject vector_Type = { NULL }; -/*------------------------Vector_CreatePyObject (internal)------------- - * creates a new vector object - * pass Py_WRAP - if vector is a WRAPPER for data allocated by BLENDER - * (i.e. it was allocated elsewhere by MEM_mallocN()) - * pass Py_NEW - if vector is not a WRAPPER and managed by PYTHON - * (i.e. it must be created here with PyMEM_malloc())*/ -PyObject *Vector_CreatePyObject(float *vec, const int size, const int type, PyTypeObject *base_type) +PyObject *Vector_CreatePyObject( + const float *vec, const int size, + PyTypeObject *base_type) { VectorObject *self; + float *vec_alloc; if (size < 2) { PyErr_SetString(PyExc_RuntimeError, @@ -3010,44 +3009,72 @@ PyObject *Vector_CreatePyObject(float *vec, const int size, const int type, PyTy return NULL; } - self = base_type ? (VectorObject *)base_type->tp_alloc(base_type, 0) : - (VectorObject *)PyObject_GC_New(VectorObject, &vector_Type); + vec_alloc = PyMem_Malloc(size * sizeof(float)); + if (UNLIKELY(vec_alloc == NULL)) { + PyErr_SetString(PyExc_MemoryError, + "Vector(): " + "problem allocating data"); + return NULL; + } + self = BASE_MATH_NEW(VectorObject, vector_Type, base_type); if (self) { + self->vec = vec_alloc; self->size = size; /* init callbacks as NULL */ self->cb_user = NULL; self->cb_type = self->cb_subtype = 0; - if (type == Py_WRAP) { - self->vec = vec; - self->wrapped = Py_WRAP; + if (vec) { + memcpy(self->vec, vec, size * sizeof(float)); } - else if (type == Py_NEW) { - self->vec = PyMem_Malloc(size * sizeof(float)); - if (vec) { - memcpy(self->vec, vec, size * sizeof(float)); + else { /* new empty */ + fill_vn_fl(self->vec, size, 0.0f); + if (size == 4) { /* do the homogeneous thing */ + self->vec[3] = 1.0f; } - else { /* new empty */ - fill_vn_fl(self->vec, size, 0.0f); - if (size == 4) { /* do the homogeneous thing */ - self->vec[3] = 1.0f; - } - } - self->wrapped = Py_NEW; - } - else { - Py_FatalError("Vector(): invalid type!"); } + self->flag = BASE_MATH_FLAG_DEFAULT; + } + else { + PyMem_Free(vec_alloc); + } + + return (PyObject *)self; +} + +PyObject *Vector_CreatePyObject_wrap( + float *vec, const int size, + PyTypeObject *base_type) +{ + VectorObject *self; + + if (size < 2) { + PyErr_SetString(PyExc_RuntimeError, + "Vector(): invalid size"); + return NULL; + } + + self = BASE_MATH_NEW(VectorObject, vector_Type, base_type); + if (self) { + self->size = size; + + /* init callbacks as NULL */ + self->cb_user = NULL; + self->cb_type = self->cb_subtype = 0; + + self->vec = vec; + self->flag = BASE_MATH_FLAG_DEFAULT | BASE_MATH_FLAG_IS_WRAP; } return (PyObject *) self; } -PyObject *Vector_CreatePyObject_cb(PyObject *cb_user, int size, unsigned char cb_type, unsigned char cb_subtype) +PyObject *Vector_CreatePyObject_cb( + PyObject *cb_user, int size, + unsigned char cb_type, unsigned char cb_subtype) { - float dummy[4] = {0.0, 0.0, 0.0, 0.0}; /* dummy init vector, callbacks will be used on access */ - VectorObject *self = (VectorObject *)Vector_CreatePyObject(dummy, size, Py_NEW, NULL); + VectorObject *self = (VectorObject *)Vector_CreatePyObject(NULL, size, NULL); if (self) { Py_INCREF(cb_user); self->cb_user = cb_user; @@ -3059,11 +3086,15 @@ PyObject *Vector_CreatePyObject_cb(PyObject *cb_user, int size, unsigned char cb return (PyObject *)self; } -PyObject *Vector_CreatePyObject_alloc(const float *vec, const int size, PyTypeObject *base_type) +PyObject *Vector_CreatePyObject_alloc( + float *vec, const int size, + PyTypeObject *base_type) { - VectorObject *vect_ob; - vect_ob = (VectorObject *)Vector_CreatePyObject((float *)vec, size, Py_WRAP, base_type); - vect_ob->wrapped = Py_NEW; + VectorObject *self; + self = (VectorObject *)Vector_CreatePyObject_wrap(vec, size, base_type); + if (self) { + self->flag = BASE_MATH_FLAG_DEFAULT; + } - return (PyObject *)vect_ob; + return (PyObject *)self; } diff --git a/source/blender/python/mathutils/mathutils_Vector.h b/source/blender/python/mathutils/mathutils_Vector.h index 2074270670a..74ca3336f4b 100644 --- a/source/blender/python/mathutils/mathutils_Vector.h +++ b/source/blender/python/mathutils/mathutils_Vector.h @@ -34,13 +34,25 @@ extern PyTypeObject vector_Type; typedef struct { BASE_MATH_MEMBERS(vec); - int size; /* vec size 2,3 or 4 */ + int size; /* vec size 2 or more */ } VectorObject; /*prototypes*/ -PyObject *Vector_CreatePyObject(float *vec, const int size, const int type, PyTypeObject *base_type); -PyObject *Vector_CreatePyObject_cb(PyObject *user, int size, - unsigned char cb_type, unsigned char subtype); -PyObject *Vector_CreatePyObject_alloc(const float *vec, const int size, PyTypeObject *base_type); +PyObject *Vector_CreatePyObject( + const float *vec, const int size, + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT; +PyObject *Vector_CreatePyObject_wrap( + float *vec, const int size, + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +PyObject *Vector_CreatePyObject_cb( + PyObject *user, int size, + unsigned char cb_type, unsigned char subtype + ) ATTR_WARN_UNUSED_RESULT; +PyObject *Vector_CreatePyObject_alloc( + float *vec, const int size, + PyTypeObject *base_type + ) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); #endif /* __MATHUTILS_VECTOR_H__ */ diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 0f0ffe9fec5..bb4080decef 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -42,6 +42,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "../generic/python_utildefines.h" /*-------------------------DOC STRINGS ---------------------------*/ PyDoc_STRVAR(M_Geometry_doc, @@ -72,48 +73,37 @@ PyDoc_STRVAR(M_Geometry_intersect_ray_tri_doc, ); static PyObject *M_Geometry_intersect_ray_tri(PyObject *UNUSED(self), PyObject *args) { - VectorObject *ray, *ray_off, *vec1, *vec2, *vec3; - float dir[3], orig[3], v1[3], v2[3], v3[3], e1[3], e2[3], pvec[3], tvec[3], qvec[3]; + const char *error_prefix = "intersect_ray_tri"; + PyObject *py_ray, *py_ray_off, *py_tri[3]; + float dir[3], orig[3], tri[3][3], e1[3], e2[3], pvec[3], tvec[3], qvec[3]; float det, inv_det, u, v, t; int clip = 1; + int i; - if (!PyArg_ParseTuple(args, - "O!O!O!O!O!|i:intersect_ray_tri", - &vector_Type, &vec1, - &vector_Type, &vec2, - &vector_Type, &vec3, - &vector_Type, &ray, - &vector_Type, &ray_off, &clip)) + if (!PyArg_ParseTuple( + args, "OOOOO|i:intersect_ray_tri", + UNPACK3_EX(&, py_tri, ), &py_ray, &py_ray_off, &clip)) { return NULL; } - if (vec1->size != 3 || vec2->size != 3 || vec3->size != 3 || ray->size != 3 || ray_off->size != 3) { - PyErr_SetString(PyExc_ValueError, - "only 3D vectors for all parameters"); - return NULL; - } - if (BaseMath_ReadCallback(vec1) == -1 || - BaseMath_ReadCallback(vec2) == -1 || - BaseMath_ReadCallback(vec3) == -1 || - BaseMath_ReadCallback(ray) == -1 || - BaseMath_ReadCallback(ray_off) == -1) + if (((mathutils_array_parse(dir, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_ray, error_prefix) != -1) && + (mathutils_array_parse(orig, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_ray_off, error_prefix) != -1)) == 0) { return NULL; } - copy_v3_v3(v1, vec1->vec); - copy_v3_v3(v2, vec2->vec); - copy_v3_v3(v3, vec3->vec); + for (i = 0; i < ARRAY_SIZE(tri); i++) { + if (mathutils_array_parse(tri[i], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_tri[i], error_prefix) == -1) { + return NULL; + } + } - copy_v3_v3(dir, ray->vec); normalize_v3(dir); - copy_v3_v3(orig, ray_off->vec); - /* find vectors for two edges sharing v1 */ - sub_v3_v3v3(e1, v2, v1); - sub_v3_v3v3(e2, v3, v1); + sub_v3_v3v3(e1, tri[1], tri[0]); + sub_v3_v3v3(e2, tri[2], tri[0]); /* begin calculating determinant - also used to calculated U parameter */ cross_v3_v3v3(pvec, dir, e2); @@ -128,7 +118,7 @@ static PyObject *M_Geometry_intersect_ray_tri(PyObject *UNUSED(self), PyObject * inv_det = 1.0f / det; /* calculate distance from v1 to ray origin */ - sub_v3_v3v3(tvec, orig, v1); + sub_v3_v3v3(tvec, orig, tri[0]); /* calculate U parameter and test bounds */ u = dot_v3v3(tvec, pvec) * inv_det; @@ -157,7 +147,7 @@ static PyObject *M_Geometry_intersect_ray_tri(PyObject *UNUSED(self), PyObject * mul_v3_fl(dir, t); add_v3_v3v3(pvec, orig, dir); - return Vector_CreatePyObject(pvec, 3, Py_NEW, NULL); + return Vector_CreatePyObject(pvec, 3, NULL); } /* Line-Line intersection using algorithm from mathworld.wolfram.com */ @@ -179,82 +169,45 @@ PyDoc_STRVAR(M_Geometry_intersect_line_line_doc, ); static PyObject *M_Geometry_intersect_line_line(PyObject *UNUSED(self), PyObject *args) { + const char *error_prefix = "intersect_line_line"; PyObject *tuple; - VectorObject *vec1, *vec2, *vec3, *vec4; - float v1[3], v2[3], v3[3], v4[3], i1[3], i2[3]; - - if (!PyArg_ParseTuple(args, "O!O!O!O!:intersect_line_line", - &vector_Type, &vec1, - &vector_Type, &vec2, - &vector_Type, &vec3, - &vector_Type, &vec4)) + PyObject *py_lines[4]; + float lines[4][3], i1[3], i2[3]; + int len; + int result; + + if (!PyArg_ParseTuple( + args, "OOOO:intersect_line_line", + UNPACK4_EX(&, py_lines, ))) { return NULL; } - if (vec1->size != vec2->size || vec1->size != vec3->size || vec3->size != vec2->size) { - PyErr_SetString(PyExc_ValueError, - "vectors must be of the same size"); - return NULL; - } - - if (BaseMath_ReadCallback(vec1) == -1 || - BaseMath_ReadCallback(vec2) == -1 || - BaseMath_ReadCallback(vec3) == -1 || - BaseMath_ReadCallback(vec4) == -1) + if ((((len = mathutils_array_parse(lines[0], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[0], error_prefix)) != -1) && + (mathutils_array_parse(lines[1], len, len | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[1], error_prefix) != -1) && + (mathutils_array_parse(lines[2], len, len | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[2], error_prefix) != -1) && + (mathutils_array_parse(lines[3], len, len | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_lines[3], error_prefix) != -1)) == 0) { return NULL; } - if (vec1->size == 3 || vec1->size == 2) { - int result; - - if (vec1->size == 3) { - copy_v3_v3(v1, vec1->vec); - copy_v3_v3(v2, vec2->vec); - copy_v3_v3(v3, vec3->vec); - copy_v3_v3(v4, vec4->vec); - } - else { - v1[0] = vec1->vec[0]; - v1[1] = vec1->vec[1]; - v1[2] = 0.0f; - - v2[0] = vec2->vec[0]; - v2[1] = vec2->vec[1]; - v2[2] = 0.0f; - - v3[0] = vec3->vec[0]; - v3[1] = vec3->vec[1]; - v3[2] = 0.0f; - - v4[0] = vec4->vec[0]; - v4[1] = vec4->vec[1]; - v4[2] = 0.0f; - } - - result = isect_line_line_v3(v1, v2, v3, v4, i1, i2); - /* The return-code isnt exposed, - * this way we can check know how close the lines are. */ - if (result == 1) { - closest_to_line_v3(i2, i1, v3, v4); - } + result = isect_line_line_v3(UNPACK4(lines), i1, i2); + /* The return-code isnt exposed, + * this way we can check know how close the lines are. */ + if (result == 1) { + closest_to_line_v3(i2, i1, lines[2], lines[3]); + } - if (result == 0) { - /* colinear */ - Py_RETURN_NONE; - } - else { - tuple = PyTuple_New(2); - PyTuple_SET_ITEM(tuple, 0, Vector_CreatePyObject(i1, vec1->size, Py_NEW, NULL)); - PyTuple_SET_ITEM(tuple, 1, Vector_CreatePyObject(i2, vec1->size, Py_NEW, NULL)); - return tuple; - } + if (result == 0) { + /* colinear */ + Py_RETURN_NONE; } else { - PyErr_SetString(PyExc_ValueError, - "2D/3D vectors only"); - return NULL; + tuple = PyTuple_New(2); + PyTuple_SET_ITEMS(tuple, + Vector_CreatePyObject(i1, len, NULL), + Vector_CreatePyObject(i2, len, NULL)); + return tuple; } } @@ -277,31 +230,30 @@ PyDoc_STRVAR(M_Geometry_intersect_sphere_sphere_2d_doc, ); static PyObject *M_Geometry_intersect_sphere_sphere_2d(PyObject *UNUSED(self), PyObject *args) { + const char *error_prefix = "intersect_sphere_sphere_2d"; PyObject *ret; - VectorObject *vec_a, *vec_b; - const float *v_a, *v_b; + PyObject *py_v_a, *py_v_b; + float v_a[2], v_b[2]; float rad_a, rad_b; float v_ab[2]; float dist; - if (!PyArg_ParseTuple(args, "O!fO!f:intersect_sphere_sphere_2d", - &vector_Type, &vec_a, &rad_a, - &vector_Type, &vec_b, &rad_b)) + if (!PyArg_ParseTuple( + args, "OfOf:intersect_sphere_sphere_2d", + &py_v_a, &rad_a, + &py_v_b, &rad_b)) { return NULL; } - if (BaseMath_ReadCallback(vec_a) == -1 || - BaseMath_ReadCallback(vec_b) == -1) + if (((mathutils_array_parse(v_a, 2, 2, py_v_a, error_prefix) != -1) && + (mathutils_array_parse(v_b, 2, 2, py_v_b, error_prefix) != -1)) == 0) { return NULL; } ret = PyTuple_New(2); - v_a = vec_a->vec; - v_b = vec_b->vec; - sub_v2_v2v2(v_ab, v_b, v_a); dist = len_v2(v_ab); @@ -313,8 +265,9 @@ static PyObject *M_Geometry_intersect_sphere_sphere_2d(PyObject *UNUSED(self), P (dist < FLT_EPSILON)) { /* out of range */ - PyTuple_SET_ITEM(ret, 0, Py_None); Py_INCREF(Py_None); - PyTuple_SET_ITEM(ret, 1, Py_None); Py_INCREF(Py_None); + PyTuple_SET_ITEMS(ret, + Py_INCREF_RET(Py_None), + Py_INCREF_RET(Py_None)); } else { const float dist_delta = ((rad_a * rad_a) - (rad_b * rad_b) + (dist * dist)) / (2.0f * dist); @@ -331,94 +284,51 @@ static PyObject *M_Geometry_intersect_sphere_sphere_2d(PyObject *UNUSED(self), P i2[0] = i_cent[0] - h * v_ab[1] / dist; i2[1] = i_cent[1] + h * v_ab[0] / dist; - PyTuple_SET_ITEM(ret, 0, Vector_CreatePyObject(i1, 2, Py_NEW, NULL)); - PyTuple_SET_ITEM(ret, 1, Vector_CreatePyObject(i2, 2, Py_NEW, NULL)); + PyTuple_SET_ITEMS(ret, + Vector_CreatePyObject(i1, 2, NULL), + Vector_CreatePyObject(i2, 2, NULL)); } return ret; } PyDoc_STRVAR(M_Geometry_normal_doc, -".. function:: normal(v1, v2, v3, v4=None)\n" +".. function:: normal(vectors)\n" "\n" -" Returns the normal of the 3D tri or quad.\n" +" Returns the normal of a 3D polygon.\n" "\n" -" :arg v1: Point1\n" -" :type v1: :class:`mathutils.Vector`\n" -" :arg v2: Point2\n" -" :type v2: :class:`mathutils.Vector`\n" -" :arg v3: Point3\n" -" :type v3: :class:`mathutils.Vector`\n" -" :arg v4: Point4 (optional)\n" -" :type v4: :class:`mathutils.Vector`\n" +" :arg vectors: Vectors to calculate normals with\n" +" :type vectors: sequence of 3 or more 3d vector\n" " :rtype: :class:`mathutils.Vector`\n" ); static PyObject *M_Geometry_normal(PyObject *UNUSED(self), PyObject *args) { - VectorObject *vec1, *vec2, *vec3, *vec4; + float (*coords)[3]; + int coords_len; float n[3]; + PyObject *ret = NULL; - if (PyTuple_GET_SIZE(args) == 3) { - if (!PyArg_ParseTuple(args, "O!O!O!:normal", - &vector_Type, &vec1, - &vector_Type, &vec2, - &vector_Type, &vec3)) - { - return NULL; - } - - if (vec1->size != vec2->size || vec1->size != vec3->size) { - PyErr_SetString(PyExc_ValueError, - "vectors must be of the same size"); - return NULL; - } - if (vec1->size < 3) { - PyErr_SetString(PyExc_ValueError, - "2D vectors unsupported"); - return NULL; - } - - if (BaseMath_ReadCallback(vec1) == -1 || - BaseMath_ReadCallback(vec2) == -1 || - BaseMath_ReadCallback(vec3) == -1) - { - return NULL; - } - - normal_tri_v3(n, vec1->vec, vec2->vec, vec3->vec); + /* use */ + if (PyTuple_GET_SIZE(args) == 1) { + args = PyTuple_GET_ITEM(args, 0); } - else { - if (!PyArg_ParseTuple(args, "O!O!O!O!:normal", - &vector_Type, &vec1, - &vector_Type, &vec2, - &vector_Type, &vec3, - &vector_Type, &vec4)) - { - return NULL; - } - if (vec1->size != vec2->size || vec1->size != vec3->size || vec1->size != vec4->size) { - PyErr_SetString(PyExc_ValueError, - "vectors must be of the same size"); - return NULL; - } - if (vec1->size < 3) { - PyErr_SetString(PyExc_ValueError, - "2D vectors unsupported"); - return NULL; - } - if (BaseMath_ReadCallback(vec1) == -1 || - BaseMath_ReadCallback(vec2) == -1 || - BaseMath_ReadCallback(vec3) == -1 || - BaseMath_ReadCallback(vec4) == -1) - { - return NULL; - } + if ((coords_len = mathutils_array_parse_alloc_v((float **)&coords, 3 | MU_ARRAY_SPILL, args, "normal")) == -1) { + return NULL; + } - normal_quad_v3(n, vec1->vec, vec2->vec, vec3->vec, vec4->vec); + if (coords_len < 3) { + PyErr_SetString(PyExc_ValueError, + "Expected 3 or more vectors"); + goto finally; } - return Vector_CreatePyObject(n, 3, Py_NEW, NULL); + normal_poly_v3(n, (const float (*)[3])coords, coords_len); + ret = Vector_CreatePyObject(n, 3, NULL); + +finally: + PyMem_Free(coords); + return ret; } /* --------------------------------- AREA FUNCTIONS-------------------- */ @@ -438,40 +348,26 @@ PyDoc_STRVAR(M_Geometry_area_tri_doc, ); static PyObject *M_Geometry_area_tri(PyObject *UNUSED(self), PyObject *args) { - VectorObject *vec1, *vec2, *vec3; - - if (!PyArg_ParseTuple(args, "O!O!O!:area_tri", - &vector_Type, &vec1, - &vector_Type, &vec2, - &vector_Type, &vec3)) + const char *error_prefix = "area_tri"; + PyObject *py_tri[3]; + float tri[3][3]; + int len; + + if (!PyArg_ParseTuple( + args, "OOO:area_tri", + UNPACK3_EX(&, py_tri, ))) { return NULL; } - if (vec1->size != vec2->size || vec1->size != vec3->size) { - PyErr_SetString(PyExc_ValueError, - "vectors must be of the same size"); - return NULL; - } - - if (BaseMath_ReadCallback(vec1) == -1 || - BaseMath_ReadCallback(vec2) == -1 || - BaseMath_ReadCallback(vec3) == -1) + if ((((len = mathutils_array_parse(tri[0], 2, 3, py_tri[0], error_prefix)) != -1) && + (mathutils_array_parse(tri[1], len, len, py_tri[1], error_prefix) != -1) && + (mathutils_array_parse(tri[2], len, len, py_tri[2], error_prefix) != -1)) == 0) { return NULL; } - if (vec1->size == 3) { - return PyFloat_FromDouble(area_tri_v3(vec1->vec, vec2->vec, vec3->vec)); - } - else if (vec1->size == 2) { - return PyFloat_FromDouble(area_tri_v2(vec1->vec, vec2->vec, vec3->vec)); - } - else { - PyErr_SetString(PyExc_ValueError, - "only 2D,3D vectors are supported"); - return NULL; - } + return PyFloat_FromDouble((len == 3 ? area_tri_v3 : area_tri_v2)(UNPACK3(tri))); } PyDoc_STRVAR(M_Geometry_volume_tetrahedron_doc, @@ -491,33 +387,25 @@ PyDoc_STRVAR(M_Geometry_volume_tetrahedron_doc, ); static PyObject *M_Geometry_volume_tetrahedron(PyObject *UNUSED(self), PyObject *args) { - VectorObject *vec1, *vec2, *vec3, *vec4; + const char *error_prefix = "volume_tetrahedron"; + PyObject *py_tet[4]; + float tet[4][3]; + int i; - if (!PyArg_ParseTuple(args, "O!O!O!O!:volume_tetrahedron", - &vector_Type, &vec1, - &vector_Type, &vec2, - &vector_Type, &vec3, - &vector_Type, &vec4)) + if (!PyArg_ParseTuple( + args, "OOOO:volume_tetrahedron", + UNPACK4_EX(&, py_tet, ))) { return NULL; } - if (vec1->size < 3 || vec2->size < 3 || vec3->size < 3 || vec4->size < 3) { - PyErr_SetString(PyExc_ValueError, - "geometry.volume_tetrahedron(...): " - " can't use 2D Vectors"); - return NULL; - } - - if (BaseMath_ReadCallback(vec1) == -1 || - BaseMath_ReadCallback(vec2) == -1 || - BaseMath_ReadCallback(vec3) == -1 || - BaseMath_ReadCallback(vec4) == -1) - { - return NULL; + for (i = 0; i < ARRAY_SIZE(tet); i++) { + if (mathutils_array_parse(tet[i], 3, 3 | MU_ARRAY_SPILL, py_tet[i], error_prefix) == -1) { + return NULL; + } } - return PyFloat_FromDouble(volume_tetrahedron_v3(vec1->vec, vec2->vec, vec3->vec, vec4->vec)); + return PyFloat_FromDouble(volume_tetrahedron_v3(UNPACK4(tet))); } PyDoc_STRVAR(M_Geometry_intersect_line_line_2d_doc, @@ -538,27 +426,27 @@ PyDoc_STRVAR(M_Geometry_intersect_line_line_2d_doc, ); static PyObject *M_Geometry_intersect_line_line_2d(PyObject *UNUSED(self), PyObject *args) { - VectorObject *line_a1, *line_a2, *line_b1, *line_b2; + const char *error_prefix = "intersect_line_line_2d"; + PyObject *py_lines[4]; + float lines[4][2]; float vi[2]; - if (!PyArg_ParseTuple(args, "O!O!O!O!:intersect_line_line_2d", - &vector_Type, &line_a1, - &vector_Type, &line_a2, - &vector_Type, &line_b1, - &vector_Type, &line_b2)) + int i; + + if (!PyArg_ParseTuple( + args, "OOOO:intersect_line_line_2d", + UNPACK4_EX(&, py_lines, ))) { return NULL; } - - if (BaseMath_ReadCallback(line_a1) == -1 || - BaseMath_ReadCallback(line_a2) == -1 || - BaseMath_ReadCallback(line_b1) == -1 || - BaseMath_ReadCallback(line_b2) == -1) - { - return NULL; + + for (i = 0; i < ARRAY_SIZE(lines); i++) { + if (mathutils_array_parse(lines[i], 2, 2 | MU_ARRAY_SPILL, py_lines[i], error_prefix) == -1) { + return NULL; + } } - if (isect_seg_seg_v2_point(line_a1->vec, line_a2->vec, line_b1->vec, line_b2->vec, vi) == 1) { - return Vector_CreatePyObject(vi, 2, Py_NEW, NULL); + if (isect_seg_seg_v2_point(UNPACK4(lines), vi) == 1) { + return Vector_CreatePyObject(vi, 2, NULL); } else { Py_RETURN_NONE; @@ -585,38 +473,31 @@ PyDoc_STRVAR(M_Geometry_intersect_line_plane_doc, ); static PyObject *M_Geometry_intersect_line_plane(PyObject *UNUSED(self), PyObject *args) { - VectorObject *line_a, *line_b, *plane_co, *plane_no; + const char *error_prefix = "intersect_line_plane"; + PyObject *py_line_a, *py_line_b, *py_plane_co, *py_plane_no; + float line_a[3], line_b[3], plane_co[3], plane_no[3]; float isect[3]; int no_flip = false; - if (!PyArg_ParseTuple(args, "O!O!O!O!|i:intersect_line_plane", - &vector_Type, &line_a, - &vector_Type, &line_b, - &vector_Type, &plane_co, - &vector_Type, &plane_no, - &no_flip)) + if (!PyArg_ParseTuple( + args, "OOOO|i:intersect_line_plane", + &py_line_a, &py_line_b, &py_plane_co, &py_plane_no, + &no_flip)) { return NULL; } - if (BaseMath_ReadCallback(line_a) == -1 || - BaseMath_ReadCallback(line_b) == -1 || - BaseMath_ReadCallback(plane_co) == -1 || - BaseMath_ReadCallback(plane_no) == -1) + if (((mathutils_array_parse(line_a, 3, 3 | MU_ARRAY_SPILL, py_line_a, error_prefix) != -1) && + (mathutils_array_parse(line_b, 3, 3 | MU_ARRAY_SPILL, py_line_b, error_prefix) != -1) && + (mathutils_array_parse(plane_co, 3, 3 | MU_ARRAY_SPILL, py_plane_co, error_prefix) != -1) && + (mathutils_array_parse(plane_no, 3, 3 | MU_ARRAY_SPILL, py_plane_no, error_prefix) != -1)) == 0) { return NULL; } - if (ELEM(2, line_a->size, line_b->size, plane_co->size, plane_no->size)) { - PyErr_SetString(PyExc_ValueError, - "geometry.intersect_line_plane(...): " - " can't use 2D Vectors"); - return NULL; - } - /* TODO: implements no_flip */ - if (isect_line_plane_v3(isect, line_a->vec, line_b->vec, plane_co->vec, plane_no->vec) == 1) { - return Vector_CreatePyObject(isect, 3, Py_NEW, NULL); + if (isect_line_plane_v3(isect, line_a, line_b, plane_co, plane_no) == 1) { + return Vector_CreatePyObject(isect, 3, NULL); } else { Py_RETURN_NONE; @@ -641,68 +522,59 @@ PyDoc_STRVAR(M_Geometry_intersect_plane_plane_doc, ); static PyObject *M_Geometry_intersect_plane_plane(PyObject *UNUSED(self), PyObject *args) { + const char *error_prefix = "intersect_plane_plane"; PyObject *ret, *ret_co, *ret_no; - VectorObject *plane_a_co, *plane_a_no, *plane_b_co, *plane_b_no; + PyObject *py_plane_a_co, *py_plane_a_no, *py_plane_b_co, *py_plane_b_no; + float plane_a_co[3], plane_a_no[3], plane_b_co[3], plane_b_no[3]; float isect_co[3]; float isect_no[3]; - if (!PyArg_ParseTuple(args, "O!O!O!O!:intersect_plane_plane", - &vector_Type, &plane_a_co, - &vector_Type, &plane_a_no, - &vector_Type, &plane_b_co, - &vector_Type, &plane_b_no)) + if (!PyArg_ParseTuple( + args, "OOOO:intersect_plane_plane", + &py_plane_a_co, &py_plane_a_no, &py_plane_b_co, &py_plane_b_no)) { return NULL; } - if (BaseMath_ReadCallback(plane_a_co) == -1 || - BaseMath_ReadCallback(plane_a_no) == -1 || - BaseMath_ReadCallback(plane_b_co) == -1 || - BaseMath_ReadCallback(plane_b_no) == -1) + if (((mathutils_array_parse(plane_a_co, 3, 3 | MU_ARRAY_SPILL, py_plane_a_co, error_prefix) != -1) && + (mathutils_array_parse(plane_a_no, 3, 3 | MU_ARRAY_SPILL, py_plane_a_no, error_prefix) != -1) && + (mathutils_array_parse(plane_b_co, 3, 3 | MU_ARRAY_SPILL, py_plane_b_co, error_prefix) != -1) && + (mathutils_array_parse(plane_b_no, 3, 3 | MU_ARRAY_SPILL, py_plane_b_no, error_prefix) != -1)) == 0) { return NULL; } - if (ELEM(2, plane_a_co->size, plane_a_no->size, plane_b_co->size, plane_b_no->size)) { - PyErr_SetString(PyExc_ValueError, - "geometry.intersect_plane_plane(...): " - " can't use 2D Vectors"); - return NULL; - } - if (isect_plane_plane_v3(isect_co, isect_no, - plane_a_co->vec, plane_a_no->vec, - plane_b_co->vec, plane_b_no->vec)) + plane_a_co, plane_a_no, + plane_b_co, plane_b_no)) { normalize_v3(isect_no); - ret_co = Vector_CreatePyObject(isect_co, 3, Py_NEW, NULL); - ret_no = Vector_CreatePyObject(isect_no, 3, Py_NEW, NULL); + ret_co = Vector_CreatePyObject(isect_co, 3, NULL); + ret_no = Vector_CreatePyObject(isect_no, 3, NULL); } else { - ret_co = Py_None; - ret_no = Py_None; - - Py_INCREF(ret_co); - Py_INCREF(ret_no); + ret_co = Py_INCREF_RET(Py_None); + ret_no = Py_INCREF_RET(Py_None); } ret = PyTuple_New(2); - PyTuple_SET_ITEM(ret, 0, ret_co); - PyTuple_SET_ITEM(ret, 1, ret_no); + PyTuple_SET_ITEMS(ret, + ret_co, + ret_no); return ret; } PyDoc_STRVAR(M_Geometry_intersect_line_sphere_doc, ".. function:: intersect_line_sphere(line_a, line_b, sphere_co, sphere_radius, clip=True)\n" "\n" -" Takes a lines (as 2 vectors), a sphere as a point and a radius and\n" +" Takes a line (as 2 points) and a sphere (as a point and a radius) and\n" " returns the intersection\n" "\n" -" :arg line_a: First point of the first line\n" +" :arg line_a: First point of the line\n" " :type line_a: :class:`mathutils.Vector`\n" -" :arg line_b: Second point of the first line\n" +" :arg line_b: Second point of the line\n" " :type line_b: :class:`mathutils.Vector`\n" " :arg sphere_co: The center of the sphere\n" " :type sphere_co: :class:`mathutils.Vector`\n" @@ -713,35 +585,28 @@ PyDoc_STRVAR(M_Geometry_intersect_line_sphere_doc, ); static PyObject *M_Geometry_intersect_line_sphere(PyObject *UNUSED(self), PyObject *args) { - VectorObject *line_a, *line_b, *sphere_co; + const char *error_prefix = "intersect_line_sphere"; + PyObject *py_line_a, *py_line_b, *py_sphere_co; + float line_a[3], line_b[3], sphere_co[3]; float sphere_radius; int clip = true; float isect_a[3]; float isect_b[3]; - if (!PyArg_ParseTuple(args, "O!O!O!f|i:intersect_line_sphere", - &vector_Type, &line_a, - &vector_Type, &line_b, - &vector_Type, &sphere_co, - &sphere_radius, &clip)) + if (!PyArg_ParseTuple( + args, "OOOf|i:intersect_line_sphere", + &py_line_a, &py_line_b, &py_sphere_co, &sphere_radius, &clip)) { return NULL; } - if (BaseMath_ReadCallback(line_a) == -1 || - BaseMath_ReadCallback(line_b) == -1 || - BaseMath_ReadCallback(sphere_co) == -1) + if (((mathutils_array_parse(line_a, 3, 3 | MU_ARRAY_SPILL, py_line_a, error_prefix) != -1) && + (mathutils_array_parse(line_b, 3, 3 | MU_ARRAY_SPILL, py_line_b, error_prefix) != -1) && + (mathutils_array_parse(sphere_co, 3, 3 | MU_ARRAY_SPILL, py_sphere_co, error_prefix) != -1)) == 0) { return NULL; } - - if (ELEM(2, line_a->size, line_b->size, sphere_co->size)) { - PyErr_SetString(PyExc_ValueError, - "geometry.intersect_line_sphere(...): " - " can't use 2D Vectors"); - return NULL; - } else { bool use_a = true; bool use_b = true; @@ -749,14 +614,14 @@ static PyObject *M_Geometry_intersect_line_sphere(PyObject *UNUSED(self), PyObje PyObject *ret = PyTuple_New(2); - switch (isect_line_sphere_v3(line_a->vec, line_b->vec, sphere_co->vec, sphere_radius, isect_a, isect_b)) { + switch (isect_line_sphere_v3(line_a, line_b, sphere_co, sphere_radius, isect_a, isect_b)) { case 1: - if (!(!clip || (((lambda = line_point_factor_v3(isect_a, line_a->vec, line_b->vec)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; + if (!(!clip || (((lambda = line_point_factor_v3(isect_a, line_a, line_b)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; use_b = false; break; case 2: - if (!(!clip || (((lambda = line_point_factor_v3(isect_a, line_a->vec, line_b->vec)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; - if (!(!clip || (((lambda = line_point_factor_v3(isect_b, line_a->vec, line_b->vec)) >= 0.0f) && (lambda <= 1.0f)))) use_b = false; + if (!(!clip || (((lambda = line_point_factor_v3(isect_a, line_a, line_b)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; + if (!(!clip || (((lambda = line_point_factor_v3(isect_b, line_a, line_b)) >= 0.0f) && (lambda <= 1.0f)))) use_b = false; break; default: use_a = false; @@ -764,11 +629,9 @@ static PyObject *M_Geometry_intersect_line_sphere(PyObject *UNUSED(self), PyObje break; } - if (use_a) { PyTuple_SET_ITEM(ret, 0, Vector_CreatePyObject(isect_a, 3, Py_NEW, NULL)); } - else { PyTuple_SET_ITEM(ret, 0, Py_None); Py_INCREF(Py_None); } - - if (use_b) { PyTuple_SET_ITEM(ret, 1, Vector_CreatePyObject(isect_b, 3, Py_NEW, NULL)); } - else { PyTuple_SET_ITEM(ret, 1, Py_None); Py_INCREF(Py_None); } + PyTuple_SET_ITEMS(ret, + use_a ? Vector_CreatePyObject(isect_a, 3, NULL) : Py_INCREF_RET(Py_None), + use_b ? Vector_CreatePyObject(isect_b, 3, NULL) : Py_INCREF_RET(Py_None)); return ret; } @@ -778,12 +641,12 @@ static PyObject *M_Geometry_intersect_line_sphere(PyObject *UNUSED(self), PyObje PyDoc_STRVAR(M_Geometry_intersect_line_sphere_2d_doc, ".. function:: intersect_line_sphere_2d(line_a, line_b, sphere_co, sphere_radius, clip=True)\n" "\n" -" Takes a lines (as 2 vectors), a sphere as a point and a radius and\n" +" Takes a line (as 2 points) and a sphere (as a point and a radius) and\n" " returns the intersection\n" "\n" -" :arg line_a: First point of the first line\n" +" :arg line_a: First point of the line\n" " :type line_a: :class:`mathutils.Vector`\n" -" :arg line_b: Second point of the first line\n" +" :arg line_b: Second point of the line\n" " :type line_b: :class:`mathutils.Vector`\n" " :arg sphere_co: The center of the sphere\n" " :type sphere_co: :class:`mathutils.Vector`\n" @@ -794,25 +657,25 @@ PyDoc_STRVAR(M_Geometry_intersect_line_sphere_2d_doc, ); static PyObject *M_Geometry_intersect_line_sphere_2d(PyObject *UNUSED(self), PyObject *args) { - VectorObject *line_a, *line_b, *sphere_co; + const char *error_prefix = "intersect_line_sphere_2d"; + PyObject *py_line_a, *py_line_b, *py_sphere_co; + float line_a[2], line_b[2], sphere_co[2]; float sphere_radius; int clip = true; float isect_a[2]; float isect_b[2]; - if (!PyArg_ParseTuple(args, "O!O!O!f|i:intersect_line_sphere_2d", - &vector_Type, &line_a, - &vector_Type, &line_b, - &vector_Type, &sphere_co, - &sphere_radius, &clip)) + if (!PyArg_ParseTuple( + args, "OOOf|i:intersect_line_sphere_2d", + &py_line_a, &py_line_b, &py_sphere_co, &sphere_radius, &clip)) { return NULL; } - if (BaseMath_ReadCallback(line_a) == -1 || - BaseMath_ReadCallback(line_b) == -1 || - BaseMath_ReadCallback(sphere_co) == -1) + if (((mathutils_array_parse(line_a, 2, 2 | MU_ARRAY_SPILL, py_line_a, error_prefix) != -1) && + (mathutils_array_parse(line_b, 2, 2 | MU_ARRAY_SPILL, py_line_b, error_prefix) != -1) && + (mathutils_array_parse(sphere_co, 2, 2 | MU_ARRAY_SPILL, py_sphere_co, error_prefix) != -1)) == 0) { return NULL; } @@ -823,14 +686,14 @@ static PyObject *M_Geometry_intersect_line_sphere_2d(PyObject *UNUSED(self), PyO PyObject *ret = PyTuple_New(2); - switch (isect_line_sphere_v2(line_a->vec, line_b->vec, sphere_co->vec, sphere_radius, isect_a, isect_b)) { + switch (isect_line_sphere_v2(line_a, line_b, sphere_co, sphere_radius, isect_a, isect_b)) { case 1: - if (!(!clip || (((lambda = line_point_factor_v2(isect_a, line_a->vec, line_b->vec)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; + if (!(!clip || (((lambda = line_point_factor_v2(isect_a, line_a, line_b)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; use_b = false; break; case 2: - if (!(!clip || (((lambda = line_point_factor_v2(isect_a, line_a->vec, line_b->vec)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; - if (!(!clip || (((lambda = line_point_factor_v2(isect_b, line_a->vec, line_b->vec)) >= 0.0f) && (lambda <= 1.0f)))) use_b = false; + if (!(!clip || (((lambda = line_point_factor_v2(isect_a, line_a, line_b)) >= 0.0f) && (lambda <= 1.0f)))) use_a = false; + if (!(!clip || (((lambda = line_point_factor_v2(isect_b, line_a, line_b)) >= 0.0f) && (lambda <= 1.0f)))) use_b = false; break; default: use_a = false; @@ -838,11 +701,9 @@ static PyObject *M_Geometry_intersect_line_sphere_2d(PyObject *UNUSED(self), PyO break; } - if (use_a) { PyTuple_SET_ITEM(ret, 0, Vector_CreatePyObject(isect_a, 2, Py_NEW, NULL)); } - else { PyTuple_SET_ITEM(ret, 0, Py_None); Py_INCREF(Py_None); } - - if (use_b) { PyTuple_SET_ITEM(ret, 1, Vector_CreatePyObject(isect_b, 2, Py_NEW, NULL)); } - else { PyTuple_SET_ITEM(ret, 1, Py_None); Py_INCREF(Py_None); } + PyTuple_SET_ITEMS(ret, + use_a ? Vector_CreatePyObject(isect_a, 2, NULL) : Py_INCREF_RET(Py_None), + use_b ? Vector_CreatePyObject(isect_b, 2, NULL) : Py_INCREF_RET(Py_None)); return ret; } @@ -863,43 +724,35 @@ PyDoc_STRVAR(M_Geometry_intersect_point_line_doc, ); static PyObject *M_Geometry_intersect_point_line(PyObject *UNUSED(self), PyObject *args) { - VectorObject *pt, *line_1, *line_2; - float pt_in[3], pt_out[3], l1[3], l2[3]; + const char *error_prefix = "intersect_point_line"; + PyObject *py_pt, *py_line_a, *py_line_b; + float pt[3], pt_out[3], line_a[3], line_b[3]; float lambda; PyObject *ret; int size = 2; - if (!PyArg_ParseTuple(args, "O!O!O!:intersect_point_line", - &vector_Type, &pt, - &vector_Type, &line_1, - &vector_Type, &line_2)) + if (!PyArg_ParseTuple( + args, "OOO:intersect_point_line", + &py_pt, &py_line_a, &py_line_b)) { return NULL; } - if (BaseMath_ReadCallback(pt) == -1 || - BaseMath_ReadCallback(line_1) == -1 || - BaseMath_ReadCallback(line_2) == -1) + /* accept 2d verts */ + if ((((size = mathutils_array_parse(pt, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_pt, error_prefix)) != -1) && + (mathutils_array_parse(line_a, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_line_a, error_prefix) != -1) && + (mathutils_array_parse(line_b, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_line_b, error_prefix) != -1)) == 0) { return NULL; } - /* accept 2d verts */ - if (pt->size >= 3) { copy_v3_v3(pt_in, pt->vec); size = 3; } - else { copy_v2_v2(pt_in, pt->vec); pt_in[2] = 0.0f; } - - if (line_1->size >= 3) { copy_v3_v3(l1, line_1->vec); size = 3; } - else { copy_v2_v2(l1, line_1->vec); l1[2] = 0.0f; } - - if (line_2->size >= 3) { copy_v3_v3(l2, line_2->vec); size = 3; } - else { copy_v2_v2(l2, line_2->vec); l2[2] = 0.0f; } - /* do the calculation */ - lambda = closest_to_line_v3(pt_out, pt_in, l1, l2); + lambda = closest_to_line_v3(pt_out, pt, line_a, line_b); ret = PyTuple_New(2); - PyTuple_SET_ITEM(ret, 0, Vector_CreatePyObject(pt_out, size, Py_NEW, NULL)); - PyTuple_SET_ITEM(ret, 1, PyFloat_FromDouble(lambda)); + PyTuple_SET_ITEMS(ret, + Vector_CreatePyObject(pt_out, size, NULL), + PyFloat_FromDouble(lambda)); return ret; } @@ -921,38 +774,30 @@ PyDoc_STRVAR(M_Geometry_intersect_point_tri_doc, ); static PyObject *M_Geometry_intersect_point_tri(PyObject *UNUSED(self), PyObject *args) { - VectorObject *pt_vec, *tri_p1, *tri_p2, *tri_p3; + const char *error_prefix = "intersect_point_tri"; + PyObject *py_pt, *py_tri[3]; + float pt[3], tri[3][3]; float vi[3]; + int i; - if (!PyArg_ParseTuple(args, "O!O!O!O!:intersect_point_tri", - &vector_Type, &pt_vec, - &vector_Type, &tri_p1, - &vector_Type, &tri_p2, - &vector_Type, &tri_p3)) + if (!PyArg_ParseTuple( + args, "OOOO:intersect_point_tri", + &py_pt, UNPACK3_EX(&, py_tri, ))) { return NULL; } - if (BaseMath_ReadCallback(pt_vec) == -1 || - BaseMath_ReadCallback(tri_p1) == -1 || - BaseMath_ReadCallback(tri_p2) == -1 || - BaseMath_ReadCallback(tri_p3) == -1) - { + if (mathutils_array_parse(pt, 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_pt, error_prefix) == -1) { return NULL; } - - if (pt_vec->size < 3 || - tri_p1->size < 3 || - tri_p2->size < 3 || - tri_p3->size < 3) - { - PyErr_SetString(PyExc_ValueError, - "One of more of the vector arguments wasn't a 3D vector"); - return NULL; + for (i = 0; i < ARRAY_SIZE(tri); i++) { + if (mathutils_array_parse(tri[i], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_tri[i], error_prefix) == -1) { + return NULL; + } } - if (isect_point_tri_v3(pt_vec->vec, tri_p1->vec, tri_p2->vec, tri_p3->vec, vi)) { - return Vector_CreatePyObject(vi, 3, Py_NEW, NULL); + if (isect_point_tri_v3(pt, UNPACK3(tri), vi)) { + return Vector_CreatePyObject(vi, 3, NULL); } else { Py_RETURN_NONE; @@ -976,26 +821,28 @@ PyDoc_STRVAR(M_Geometry_intersect_point_tri_2d_doc, ); static PyObject *M_Geometry_intersect_point_tri_2d(PyObject *UNUSED(self), PyObject *args) { - VectorObject *pt_vec, *tri_p1, *tri_p2, *tri_p3; - - if (!PyArg_ParseTuple(args, "O!O!O!O!:intersect_point_tri_2d", - &vector_Type, &pt_vec, - &vector_Type, &tri_p1, - &vector_Type, &tri_p2, - &vector_Type, &tri_p3)) + const char *error_prefix = "intersect_point_tri_2d"; + PyObject *py_pt, *py_tri[3]; + float pt[2], tri[3][2]; + int i; + + if (!PyArg_ParseTuple( + args, "OOOO:intersect_point_tri_2d", + &py_pt, UNPACK3_EX(&, py_tri, ))) { return NULL; } - - if (BaseMath_ReadCallback(pt_vec) == -1 || - BaseMath_ReadCallback(tri_p1) == -1 || - BaseMath_ReadCallback(tri_p2) == -1 || - BaseMath_ReadCallback(tri_p3) == -1) - { + + if (mathutils_array_parse(pt, 2, 2 | MU_ARRAY_SPILL, py_pt, error_prefix) == -1) { return NULL; } + for (i = 0; i < ARRAY_SIZE(tri); i++) { + if (mathutils_array_parse(tri[i], 2, 2 | MU_ARRAY_SPILL, py_tri[i], error_prefix) == -1) { + return NULL; + } + } - return PyLong_FromLong(isect_point_tri_v2(pt_vec->vec, tri_p1->vec, tri_p2->vec, tri_p3->vec)); + return PyLong_FromLong(isect_point_tri_v2(pt, UNPACK3(tri))); } PyDoc_STRVAR(M_Geometry_intersect_point_quad_2d_doc, @@ -1019,28 +866,28 @@ PyDoc_STRVAR(M_Geometry_intersect_point_quad_2d_doc, ); static PyObject *M_Geometry_intersect_point_quad_2d(PyObject *UNUSED(self), PyObject *args) { - VectorObject *pt_vec, *quad_p1, *quad_p2, *quad_p3, *quad_p4; + const char *error_prefix = "intersect_point_quad_2d"; + PyObject *py_pt, *py_quad[4]; + float pt[2], quad[4][2]; + int i; - if (!PyArg_ParseTuple(args, "O!O!O!O!O!:intersect_point_quad_2d", - &vector_Type, &pt_vec, - &vector_Type, &quad_p1, - &vector_Type, &quad_p2, - &vector_Type, &quad_p3, - &vector_Type, &quad_p4)) + if (!PyArg_ParseTuple( + args, "OOOOO:intersect_point_quad_2d", + &py_pt, UNPACK4_EX(&, py_quad, ))) { return NULL; } - if (BaseMath_ReadCallback(pt_vec) == -1 || - BaseMath_ReadCallback(quad_p1) == -1 || - BaseMath_ReadCallback(quad_p2) == -1 || - BaseMath_ReadCallback(quad_p3) == -1 || - BaseMath_ReadCallback(quad_p4) == -1) - { + if (mathutils_array_parse(pt, 2, 2 | MU_ARRAY_SPILL, py_pt, error_prefix) == -1) { return NULL; } + for (i = 0; i < ARRAY_SIZE(quad); i++) { + if (mathutils_array_parse(quad[i], 2, 2 | MU_ARRAY_SPILL, py_quad[i], error_prefix) == -1) { + return NULL; + } + } - return PyLong_FromLong(isect_point_quad_v2(pt_vec->vec, quad_p1->vec, quad_p2->vec, quad_p3->vec, quad_p4->vec)); + return PyLong_FromLong(isect_point_quad_v2(pt, UNPACK4(quad))); } PyDoc_STRVAR(M_Geometry_distance_point_to_plane_doc, @@ -1059,35 +906,27 @@ PyDoc_STRVAR(M_Geometry_distance_point_to_plane_doc, ); static PyObject *M_Geometry_distance_point_to_plane(PyObject *UNUSED(self), PyObject *args) { - VectorObject *pt, *plane_co, *plane_no; + const char *error_prefix = "distance_point_to_plane"; + PyObject *py_pt, *py_plane_co, *py_plane_no; + float pt[3], plane_co[3], plane_no[3]; float plane[4]; - if (!PyArg_ParseTuple(args, "O!O!O!:distance_point_to_plane", - &vector_Type, &pt, - &vector_Type, &plane_co, - &vector_Type, &plane_no)) - { - return NULL; - } - - if (pt->size != 3 || - plane_co->size != 3 || - plane_no->size != 3) + if (!PyArg_ParseTuple( + args, "OOO:distance_point_to_plane", + &py_pt, &py_plane_co, &py_plane_no)) { - PyErr_SetString(PyExc_ValueError, - "One of more of the vector arguments wasn't a 3D vector"); return NULL; } - if (BaseMath_ReadCallback(pt) == -1 || - BaseMath_ReadCallback(plane_co) == -1 || - BaseMath_ReadCallback(plane_no) == -1) + if (((mathutils_array_parse(pt, 3, 3 | MU_ARRAY_SPILL, py_pt, error_prefix) != -1) && + (mathutils_array_parse(plane_co, 3, 3 | MU_ARRAY_SPILL, py_plane_co, error_prefix) != -1) && + (mathutils_array_parse(plane_no, 3, 3 | MU_ARRAY_SPILL, py_plane_no, error_prefix) != -1)) == 0) { return NULL; } - plane_from_point_normal_v3(plane, plane_co->vec, plane_no->vec); - return PyFloat_FromDouble(dist_signed_to_plane_v3(pt->vec, plane)); + plane_from_point_normal_v3(plane, plane_co, plane_no); + return PyFloat_FromDouble(dist_signed_to_plane_v3(pt, plane)); } PyDoc_STRVAR(M_Geometry_barycentric_transform_doc, @@ -1114,53 +953,37 @@ PyDoc_STRVAR(M_Geometry_barycentric_transform_doc, ); static PyObject *M_Geometry_barycentric_transform(PyObject *UNUSED(self), PyObject *args) { - VectorObject *vec_pt; - VectorObject *vec_t1_tar, *vec_t2_tar, *vec_t3_tar; - VectorObject *vec_t1_src, *vec_t2_src, *vec_t3_src; - float vec[3]; - - if (!PyArg_ParseTuple(args, "O!O!O!O!O!O!O!::barycentric_transform", - &vector_Type, &vec_pt, - &vector_Type, &vec_t1_src, - &vector_Type, &vec_t2_src, - &vector_Type, &vec_t3_src, - &vector_Type, &vec_t1_tar, - &vector_Type, &vec_t2_tar, - &vector_Type, &vec_t3_tar)) - { - return NULL; - } + const char *error_prefix = "barycentric_transform"; + PyObject *py_pt_src, *py_tri_src[3], *py_tri_dst[3]; + float pt_src[3], pt_dst[3], tri_src[3][3], tri_dst[3][3]; + int i; - if (vec_pt->size != 3 || - vec_t1_src->size != 3 || - vec_t2_src->size != 3 || - vec_t3_src->size != 3 || - vec_t1_tar->size != 3 || - vec_t2_tar->size != 3 || - vec_t3_tar->size != 3) + if (!PyArg_ParseTuple( + args, "OOOOOOO:barycentric_transform", + &py_pt_src, + UNPACK3_EX(&, py_tri_src, ), + UNPACK3_EX(&, py_tri_dst, ))) { - PyErr_SetString(PyExc_ValueError, - "One of more of the vector arguments wasn't a 3D vector"); return NULL; } - if (BaseMath_ReadCallback(vec_pt) == -1 || - BaseMath_ReadCallback(vec_t1_src) == -1 || - BaseMath_ReadCallback(vec_t2_src) == -1 || - BaseMath_ReadCallback(vec_t3_src) == -1 || - BaseMath_ReadCallback(vec_t1_tar) == -1 || - BaseMath_ReadCallback(vec_t2_tar) == -1 || - BaseMath_ReadCallback(vec_t3_tar) == -1) - { + if (mathutils_array_parse(pt_src, 3, 3 | MU_ARRAY_SPILL, py_pt_src, error_prefix) == -1) { return NULL; } + for (i = 0; i < ARRAY_SIZE(tri_src); i++) { + if (((mathutils_array_parse(tri_src[i], 3, 3 | MU_ARRAY_SPILL, py_tri_src[i], error_prefix) != -1) && + (mathutils_array_parse(tri_dst[i], 3, 3 | MU_ARRAY_SPILL, py_tri_dst[i], error_prefix) != -1)) == 0) + { + return NULL; + } + } transform_point_by_tri_v3( - vec, vec_pt->vec, - vec_t1_tar->vec, vec_t2_tar->vec, vec_t3_tar->vec, - vec_t1_src->vec, vec_t2_src->vec, vec_t3_src->vec); + pt_dst, pt_src, + UNPACK3(tri_dst), + UNPACK3(tri_src)); - return Vector_CreatePyObject(vec, 3, Py_NEW, NULL); + return Vector_CreatePyObject(pt_dst, 3, NULL); } PyDoc_STRVAR(M_Geometry_points_in_planes_doc, @@ -1180,8 +1003,9 @@ static PyObject *M_Geometry_points_in_planes(PyObject *UNUSED(self), PyObject *a float (*planes)[4]; unsigned int planes_len; - if (!PyArg_ParseTuple(args, "O:points_in_planes", - &py_planes)) + if (!PyArg_ParseTuple( + args, "O:points_in_planes", + &py_planes)) { return NULL; } @@ -1233,10 +1057,7 @@ static PyObject *M_Geometry_points_in_planes(PyObject *UNUSED(self), PyObject *a if (l == len) { /* ok */ /* python */ - PyObject *item = Vector_CreatePyObject(potentialVertex, 3, Py_NEW, NULL); - PyList_Append(py_verts, item); - Py_DECREF(item); - + PyList_APPEND(py_verts, Vector_CreatePyObject(potentialVertex, 3, NULL)); planes_used[i] = planes_used[j] = planes_used[k] = true; } } @@ -1252,17 +1073,16 @@ static PyObject *M_Geometry_points_in_planes(PyObject *UNUSED(self), PyObject *a /* now make a list of used planes */ for (i = 0; i < len; i++) { if (planes_used[i]) { - PyObject *item = PyLong_FromLong(i); - PyList_Append(py_plane_index, item); - Py_DECREF(item); + PyList_APPEND(py_plane_index, PyLong_FromLong(i)); } } PyMem_Free(planes_used); { PyObject *ret = PyTuple_New(2); - PyTuple_SET_ITEM(ret, 0, py_verts); - PyTuple_SET_ITEM(ret, 1, py_plane_index); + PyTuple_SET_ITEMS(ret, + py_verts, + py_plane_index); return ret; } } @@ -1290,58 +1110,45 @@ PyDoc_STRVAR(M_Geometry_interpolate_bezier_doc, ); static PyObject *M_Geometry_interpolate_bezier(PyObject *UNUSED(self), PyObject *args) { - VectorObject *vec_k1, *vec_h1, *vec_k2, *vec_h2; + const char *error_prefix = "interpolate_bezier"; + PyObject *py_data[4]; + float data[4][4] = {{0.0f}}; int resolu; - int dims; + int dims = 0; int i; float *coord_array, *fp; PyObject *list; - float k1[4] = {0.0, 0.0, 0.0, 0.0}; - float h1[4] = {0.0, 0.0, 0.0, 0.0}; - float k2[4] = {0.0, 0.0, 0.0, 0.0}; - float h2[4] = {0.0, 0.0, 0.0, 0.0}; - - - if (!PyArg_ParseTuple(args, "O!O!O!O!i:interpolate_bezier", - &vector_Type, &vec_k1, - &vector_Type, &vec_h1, - &vector_Type, &vec_h2, - &vector_Type, &vec_k2, &resolu)) + if (!PyArg_ParseTuple( + args, "OOOOi:interpolate_bezier", + UNPACK4_EX(&, py_data, ), &resolu)) { return NULL; } + for (i = 0; i < 4; i++) { + int dims_tmp; + if ((dims_tmp = mathutils_array_parse(data[i], 2, 3 | MU_ARRAY_SPILL | MU_ARRAY_ZERO, py_data[i], error_prefix)) == -1) { + return NULL; + } + dims = max_ii(dims, dims_tmp); + } + if (resolu <= 1) { PyErr_SetString(PyExc_ValueError, "resolution must be 2 or over"); return NULL; } - if (BaseMath_ReadCallback(vec_k1) == -1 || - BaseMath_ReadCallback(vec_h1) == -1 || - BaseMath_ReadCallback(vec_k2) == -1 || - BaseMath_ReadCallback(vec_h2) == -1) - { - return NULL; - } - - dims = max_iiii(vec_k1->size, vec_h1->size, vec_h2->size, vec_k2->size); - - for (i = 0; i < vec_k1->size; i++) k1[i] = vec_k1->vec[i]; - for (i = 0; i < vec_h1->size; i++) h1[i] = vec_h1->vec[i]; - for (i = 0; i < vec_k2->size; i++) k2[i] = vec_k2->vec[i]; - for (i = 0; i < vec_h2->size; i++) h2[i] = vec_h2->vec[i]; - - coord_array = MEM_callocN(dims * (resolu) * sizeof(float), "interpolate_bezier"); + coord_array = MEM_callocN(dims * (resolu) * sizeof(float), error_prefix); for (i = 0; i < dims; i++) { - BKE_curve_forward_diff_bezier(k1[i], h1[i], h2[i], k2[i], coord_array + i, resolu - 1, sizeof(float) * dims); + BKE_curve_forward_diff_bezier(UNPACK4_EX(, data, [i]), coord_array + i, resolu - 1, sizeof(float) * dims); } list = PyList_New(resolu); fp = coord_array; for (i = 0; i < resolu; i++, fp = fp + dims) { - PyList_SET_ITEM(list, i, Vector_CreatePyObject(fp, dims, Py_NEW, NULL)); + PyList_SET_ITEM(list, i, Vector_CreatePyObject(fp, dims, NULL)); } MEM_freeN(coord_array); return list; @@ -1581,8 +1388,9 @@ static PyObject *M_Geometry_box_pack_2d(PyObject *UNUSED(self), PyObject *boxlis } ret = PyTuple_New(2); - PyTuple_SET_ITEM(ret, 0, PyFloat_FromDouble(tot_width)); - PyTuple_SET_ITEM(ret, 1, PyFloat_FromDouble(tot_height)); + PyTuple_SET_ITEMS(ret, + PyFloat_FromDouble(tot_width), + PyFloat_FromDouble(tot_height)); return ret; } diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c new file mode 100644 index 00000000000..4d7841b906a --- /dev/null +++ b/source/blender/python/mathutils/mathutils_interpolate.c @@ -0,0 +1,137 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/mathutils/mathutils_interpolate.c + * \ingroup pymathutils + */ + + +#include <Python.h> + +#include "mathutils.h" +#include "mathutils_interpolate.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#ifndef MATH_STANDALONE /* define when building outside blender */ +# include "MEM_guardedalloc.h" +#endif + +/*-------------------------DOC STRINGS ---------------------------*/ +PyDoc_STRVAR(M_Interpolate_doc, +"The Blender interpolate module" +); + +/* ---------------------------------WEIGHT CALCULATION ----------------------- */ + +#ifndef MATH_STANDALONE + +PyDoc_STRVAR(M_Interpolate_poly_3d_calc_doc, +".. function:: poly_3d_calc(veclist, pt)\n" +"\n" +" Calculate barycentric weights for a point on a polygon.\n" +"\n" +" :arg veclist: list of vectors\n" +" :arg pt: point" +" :rtype: list of per-vector weights\n" +); +static PyObject *M_Interpolate_poly_3d_calc(PyObject *UNUSED(self), PyObject *args) +{ + float fp[3]; + float (*vecs)[3]; + Py_ssize_t len; + + PyObject *point, *veclist, *ret; + int i; + + if (!PyArg_ParseTuple(args, "OO!:interpolation_weights", + &veclist, + &vector_Type, &point)) + { + return NULL; + } + + if (BaseMath_ReadCallback((VectorObject *)point) == -1) + return NULL; + + fp[0] = ((VectorObject *)point)->vec[0]; + fp[1] = ((VectorObject *)point)->vec[1]; + if (((VectorObject *)point)->size > 2) + fp[2] = ((VectorObject *)point)->vec[2]; + else + fp[2] = 0.0f; /* if its a 2d vector then set the z to be zero */ + + len = mathutils_array_parse_alloc_v(((float **)&vecs), 3, veclist, "interpolation_weights"); + if (len == -1) { + return NULL; + } + + if (len) { + float *weights = MEM_mallocN(sizeof(float) * len, "interpolation weights"); + + interp_weights_poly_v3(weights, vecs, len, fp); + + ret = PyList_New(len); + for (i = 0; i < len; i++) { + PyList_SET_ITEM(ret, i, PyFloat_FromDouble(weights[i])); + } + + MEM_freeN(weights); + + PyMem_Free(vecs); + } + else { + ret = PyList_New(0); + } + + return ret; +} + +#endif /* MATH_STANDALONE */ + + +static PyMethodDef M_Interpolate_methods[] = { +#ifndef MATH_STANDALONE + {"poly_3d_calc", (PyCFunction) M_Interpolate_poly_3d_calc, METH_VARARGS, M_Interpolate_poly_3d_calc_doc}, +#endif + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef M_Interpolate_module_def = { + PyModuleDef_HEAD_INIT, + "mathutils.interpolate", /* m_name */ + M_Interpolate_doc, /* m_doc */ + 0, /* m_size */ + M_Interpolate_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; + +/*----------------------------MODULE INIT-------------------------*/ +PyMODINIT_FUNC PyInit_mathutils_interpolate(void) +{ + PyObject *submodule = PyModule_Create(&M_Interpolate_module_def); + return submodule; +} diff --git a/source/blender/python/mathutils/mathutils_interpolate.h b/source/blender/python/mathutils/mathutils_interpolate.h new file mode 100644 index 00000000000..1bbff6f3286 --- /dev/null +++ b/source/blender/python/mathutils/mathutils_interpolate.h @@ -0,0 +1,32 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Lukas Toenne + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __MATHUTILS_INTERPOLATE_H__ +#define __MATHUTILS_INTERPOLATE_H__ + +/** \file blender/python/mathutils/mathutils_interpolate.h + * \ingroup pymathutils + */ + +PyMODINIT_FUNC PyInit_mathutils_interpolate(void); + +#endif /* __MATHUTILS_INTERPOLATE_H__ */ diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c index 519778aea7d..199c2e02da4 100644 --- a/source/blender/python/mathutils/mathutils_kdtree.c +++ b/source/blender/python/mathutils/mathutils_kdtree.c @@ -35,6 +35,7 @@ #include "BLI_kdtree.h" #include "../generic/py_capi_utils.h" +#include "../generic/python_utildefines.h" #include "mathutils.h" #include "mathutils_kdtree.h" /* own include */ @@ -58,9 +59,10 @@ static void kdtree_nearest_to_py_tuple(const KDTreeNearest *nearest, PyObject *p BLI_assert(nearest->index >= 0); BLI_assert(PyTuple_GET_SIZE(py_retval) == 3); - PyTuple_SET_ITEM(py_retval, 0, Vector_CreatePyObject((float *)nearest->co, 3, Py_NEW, NULL)); - PyTuple_SET_ITEM(py_retval, 1, PyLong_FromLong(nearest->index)); - PyTuple_SET_ITEM(py_retval, 2, PyFloat_FromDouble(nearest->dist)); + PyTuple_SET_ITEMS(py_retval, + Vector_CreatePyObject((float *)nearest->co, 3, NULL), + PyLong_FromLong(nearest->index), + PyFloat_FromDouble(nearest->dist)); } static PyObject *kdtree_nearest_to_py(const KDTreeNearest *nearest) @@ -126,7 +128,7 @@ static void PyKDTree__tp_dealloc(PyKDTree *self) } PyDoc_STRVAR(py_kdtree_insert_doc, -".. method:: insert(index, co)\n" +".. method:: insert(co, index)\n" "\n" " Insert a point into the KDTree.\n" "\n" @@ -171,6 +173,10 @@ PyDoc_STRVAR(py_kdtree_balance_doc, ".. method:: balance()\n" "\n" " Balance the tree.\n" +"\n" +".. note::\n" +"\n" +" This builds the entire tree, avoid calling after each insertion.\n" ); static PyObject *py_kdtree_balance(PyKDTree *self) { diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c index 8dfa7904300..f0449c23dcd 100644 --- a/source/blender/python/mathutils/mathutils_noise.c +++ b/source/blender/python/mathutils/mathutils_noise.c @@ -34,8 +34,6 @@ #include <Python.h> -#include "structseq.h" - #include "BLI_math.h" #include "BLI_noise.h" #include "BLI_utildefines.h" @@ -311,7 +309,7 @@ static PyObject *M_Noise_random_unit_vector(PyObject *UNUSED(self), PyObject *ar norm = normalize_vn(vec, size); } - return Vector_CreatePyObject(vec, size, Py_NEW, NULL); + return Vector_CreatePyObject(vec, size, NULL); } /* This is dumb, most people will want a unit vector anyway, since this doesn't have uniform distribution over a sphere*/ #if 0 @@ -340,7 +338,7 @@ static PyObject *M_Noise_random_vector(PyObject *UNUSED(self), PyObject *args) rand_vn(vec, size); - return Vector_CreatePyObject(vec, size, Py_NEW, NULL); + return Vector_CreatePyObject(vec, size, NULL); } #endif @@ -414,7 +412,7 @@ static PyObject *M_Noise_noise_vector(PyObject *UNUSED(self), PyObject *args) noise_vector(vec[0], vec[1], vec[2], nb, r_vec); - return Vector_CreatePyObject(r_vec, 3, Py_NEW, NULL); + return Vector_CreatePyObject(r_vec, 3, NULL); } PyDoc_STRVAR(M_Noise_turbulence_doc, @@ -486,7 +484,7 @@ static PyObject *M_Noise_turbulence_vector(PyObject *UNUSED(self), PyObject *arg return NULL; vTurb(vec[0], vec[1], vec[2], oct, hd, nb, as, fs, r_vec); - return Vector_CreatePyObject(r_vec, 3, Py_NEW, NULL); + return Vector_CreatePyObject(r_vec, 3, NULL); } /* F. Kenton Musgrave's fractal functions */ @@ -738,7 +736,7 @@ static PyObject *M_Noise_voronoi(PyObject *UNUSED(self), PyObject *args) voronoi(vec[0], vec[1], vec[2], da, pa, me, dtype); for (i = 0; i < 4; i++) { - PyList_SET_ITEM(list, i, Vector_CreatePyObject(pa + 3 * i, 3, Py_NEW, NULL)); + PyList_SET_ITEM(list, i, Vector_CreatePyObject(pa + 3 * i, 3, NULL)); } return Py_BuildValue("[[ffff]O]", da[0], da[1], da[2], da[3], list); @@ -790,7 +788,7 @@ static PyObject *M_Noise_cell_vector(PyObject *UNUSED(self), PyObject *args) return NULL; cellNoiseV(vec[0], vec[1], vec[2], r_vec); - return Vector_CreatePyObject(r_vec, 3, Py_NEW, NULL); + return Vector_CreatePyObject(r_vec, 3, NULL); } static PyMethodDef M_Noise_methods[] = { |