From 414370b8d42324878485e569fc332b814131887f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 18 Dec 2011 07:27:11 +0000 Subject: [PATCH] Support for arbitrary sized vectors - (was limited by 2-4 previously) patch http://codereview.appspot.com/5482043 from Andrew Hale * Text from the submission * This patch adds the ability to use arbitrary sized vectors from mathutils. Currently vectors are only of size 2, 3 or 4 since they are generally restricted to geometric applications. However, we can use arbitrary sized vectors for efficient calculations and data manipulation. --- source/blender/blenlib/BLI_math_vector.h | 1 + source/blender/blenlib/intern/math_vector.c | 9 + source/blender/python/mathutils/mathutils.c | 114 +++- source/blender/python/mathutils/mathutils.h | 1 + .../python/mathutils/mathutils_Vector.c | 516 ++++++++++++++++-- .../python/mathutils/mathutils_Vector.h | 3 +- 6 files changed, 563 insertions(+), 81 deletions(-) diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index d8e880a9dec..e9e44ed7b2c 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -198,6 +198,7 @@ double dot_vn_vn(const float *array_src_a, const float *array_src_b, const int s float normalize_vn_vn(float *array_tar, const float *array_src, const int size); float normalize_vn(float *array_tar, const int size); void range_vn_i(int *array_tar, const int size, const int start); +void range_vn_fl(float *array_tar, const int size, const float start, const float step); void negate_vn(float *array_tar, const int size); void negate_vn_vn(float *array_tar, const float *array_src, const int size); void mul_vn_fl(float *array_tar, const int size, const float f); diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index a9ea90ef555..590a48e8085 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -416,6 +416,15 @@ void range_vn_i(int *array_tar, const int size, const int start) while(i--) { *(array_pt--) = j--; } } +void range_vn_fl(float *array_tar, const int size, const float start, const float step) +{ + float *array_pt= array_tar + (size-1); + int i= size; + while(i--) { + *(array_pt--) = start + step * (float)(i); + } +} + void negate_vn(float *array_tar, const int size) { float *array_pt= array_tar + (size-1); diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 121c5e26e73..6a7f54d6f81 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -40,36 +40,13 @@ PyDoc_STRVAR(M_Mathutils_doc, "This module provides access to matrices, eulers, quaternions and vectors." ); static int mathutils_array_parse_fast(float *array, - int array_min, int array_max, - PyObject *value, const char *error_prefix) + int size, + PyObject *value_fast, + const char *error_prefix) { - PyObject *value_fast= NULL; PyObject *item; - int i, size; - - /* non list/tuple cases */ - if (!(value_fast=PySequence_Fast(value, error_prefix))) { - /* PySequence_Fast sets the error */ - return -1; - } - - size= PySequence_Fast_GET_SIZE(value_fast); - - if (size > array_max || size < array_min) { - if (array_max == array_min) { - PyErr_Format(PyExc_ValueError, - "%.200s: sequence size is %d, expected %d", - error_prefix, size, array_max); - } - else { - PyErr_Format(PyExc_ValueError, - "%.200s: sequence size is %d, expected [%d - %d]", - error_prefix, size, array_min, array_max); - } - Py_DECREF(value_fast); - return -1; - } + int i; i= size; do { @@ -93,9 +70,10 @@ static int mathutils_array_parse_fast(float *array, /* 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) { -#if 1 /* approx 6x speedup for mathutils types */ int size; +#if 1 /* approx 6x speedup for mathutils types */ + if ( (size= VectorObject_Check(value) ? ((VectorObject *)value)->size : 0) || (size= EulerObject_Check(value) ? 3 : 0) || (size= QuaternionObject_Check(value) ? 4 : 0) || @@ -125,7 +103,85 @@ int mathutils_array_parse(float *array, int array_min, int array_max, PyObject * else #endif { - return mathutils_array_parse_fast(array, array_min, array_max, value, error_prefix); + PyObject *value_fast= NULL; + + /* non list/tuple cases */ + if (!(value_fast=PySequence_Fast(value, error_prefix))) { + /* PySequence_Fast sets the error */ + return -1; + } + + size= PySequence_Fast_GET_SIZE(value_fast); + + if (size > array_max || size < array_min) { + if (array_max == array_min) { + PyErr_Format(PyExc_ValueError, + "%.200s: sequence size is %d, expected %d", + error_prefix, size, array_max); + } + else { + PyErr_Format(PyExc_ValueError, + "%.200s: sequence size is %d, expected [%d - %d]", + error_prefix, size, array_min, array_max); + } + Py_DECREF(value_fast); + return -1; + } + + return mathutils_array_parse_fast(array, size, value_fast, error_prefix); + } +} + +int mathutils_array_parse_alloc(float **array, int array_min, PyObject *value, const char *error_prefix) +{ + int size; + +#if 1 /* approx 6x speedup for mathutils types */ + + if ( (size= VectorObject_Check(value) ? ((VectorObject *)value)->size : 0) || + (size= EulerObject_Check(value) ? 3 : 0) || + (size= QuaternionObject_Check(value) ? 4 : 0) || + (size= ColorObject_Check(value) ? 3 : 0)) + { + if (BaseMath_ReadCallback((BaseMathObject *)value) == -1) { + return -1; + } + + if (size < array_min) { + PyErr_Format(PyExc_ValueError, + "%.200s: sequence size is %d, expected > %d", + error_prefix, size, array_min); + return -1; + } + + *array= PyMem_Malloc(size * sizeof(float)); + memcpy(*array, ((BaseMathObject *)value)->data, size * sizeof(float)); + return size; + } + else +#endif + { + PyObject *value_fast= NULL; + //*array= NULL; + + /* non list/tuple cases */ + if (!(value_fast=PySequence_Fast(value, error_prefix))) { + /* PySequence_Fast sets the error */ + return -1; + } + + size= PySequence_Fast_GET_SIZE(value_fast); + + if (size < array_min) { + PyErr_Format(PyExc_ValueError, + "%.200s: sequence size is %d, expected > %d", + error_prefix, size, array_min); + return -1; + } + + *array= PyMem_Malloc(size * sizeof(float)); + + return mathutils_array_parse_fast(*array, size, value_fast, error_prefix); } } diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h index a8170b8145c..cd3548b9b82 100644 --- a/source/blender/python/mathutils/mathutils.h +++ b/source/blender/python/mathutils/mathutils.h @@ -115,6 +115,7 @@ int _BaseMathObject_WriteIndexCallback(BaseMathObject *self, int index); /* 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_any_to_rotmat(float rmat[3][3], PyObject *value, const char *error_prefix); int column_vector_multiplication(float rvec[4], VectorObject *vec, MatrixObject *mat); diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 9d1a22adb12..a812311dad4 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -54,15 +54,29 @@ static int row_vector_multiplication(float rvec[MAX_DIMENSIONS], VectorObject *v */ static PyObject *Vector_new(PyTypeObject *type, PyObject *args, PyObject *UNUSED(kwds)) { - float vec[4]= {0.0f, 0.0f, 0.0f, 0.0f}; + float *vec= NULL; int size= 3; /* default to a 3D vector */ switch(PyTuple_GET_SIZE(args)) { case 0: + vec= PyMem_Malloc(size * sizeof(float)); + + if (vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector(): " + "problem allocating pointer space"); + return NULL; + } + + fill_vn_fl(vec, size, 0.0f); break; case 1: - if ((size=mathutils_array_parse(vec, 2, 4, PyTuple_GET_ITEM(args, 0), "mathutils.Vector()")) == -1) + if ((size=mathutils_array_parse_alloc(&vec, 2, PyTuple_GET_ITEM(args, 0), "mathutils.Vector()")) == -1) { + if (vec) { + PyMem_Free(vec); + } return NULL; + } break; default: PyErr_SetString(PyExc_TypeError, @@ -87,6 +101,215 @@ static PyObject *vec__apply_to_copy(PyNoArgsFunction vec_func, VectorObject *sel } } +/*-----------------------CLASS-METHODS----------------------------*/ +PyDoc_STRVAR(C_Vector_Fill_doc, +".. classmethod:: Fill(size, fill=0.0)\n" +"\n" +" Create a vector of length size with all values set to fill.\n" +"\n" +" :arg size: The length of the vector to be created.\n" +" :type size: int\n" +" :arg fill: The value used to fill the vector.\n" +" :type fill: float\n" +); +static PyObject *C_Vector_Fill(PyObject *cls, PyObject *args) +{ + float *vec; + int size; + float fill= 0.0f; + + if (!PyArg_ParseTuple(args, "i|f:Vector.Fill", &size, &fill)) { + return NULL; + } + + if (size < 2) { + PyErr_SetString(PyExc_RuntimeError, + "Vector(): invalid size"); + return NULL; + } + + vec= PyMem_Malloc(size * sizeof(float)); + + if (vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector.Fill(): " + "problem allocating pointer space"); + return NULL; + } + + fill_vn_fl(vec, size, fill); + + return Vector_CreatePyObject_alloc(vec, size, (PyTypeObject *)cls); +} + +PyDoc_STRVAR(C_Vector_Range_doc, +".. classmethod:: Range(start=0, stop, step=1)\n" +"\n" +" Create a filled with a range of values.\n" +"\n" +" :arg start: The start of the range used to fill the vector.\n" +" :type start: int\n" +" :arg stop: The end of the range used to fill the vector.\n" +" :type stop: int\n" +" :arg step: The step between successive values in the vector.\n" +" :type step: int\n" +); +static PyObject *C_Vector_Range(PyObject *cls, PyObject *args) +{ + float *vec; + int stop, size; + int start= 0; + int step= 1; + + if (!PyArg_ParseTuple(args, "i|ii:Vector.Range", &start, &stop, &step)) { + return NULL; + } + + switch(PyTuple_GET_SIZE(args)){ + case 1: + size = start; + start= 0; + break; + case 2: + if (start >= stop) { + PyErr_SetString(PyExc_RuntimeError, + "Start value is larger" + "than the stop value"); + return NULL; + } + + size= stop - start; + break; + default: + if (start >= stop) { + PyErr_SetString(PyExc_RuntimeError, + "Start value is larger" + "than the stop value"); + return NULL; + } + size= (stop - start)/step; + if (size%step) + size++; + break; + } + + vec= PyMem_Malloc(size * sizeof(float)); + + if (vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector.Range(): " + "problem allocating pointer space"); + return NULL; + } + + range_vn_fl(vec, size, (float)start, (float)step); + + return Vector_CreatePyObject_alloc(vec, size, (PyTypeObject *)cls); +} + +PyDoc_STRVAR(C_Vector_Linspace_doc, +".. classmethod:: Linspace(start, stop, size)\n" +"\n" +" Create a vector of the specified size which is filled with linearly spaced values between start and stop values.\n" +"\n" +" :arg start: The start of the range used to fill the vector.\n" +" :type start: int\n" +" :arg stop: The end of the range used to fill the vector.\n" +" :type stop: int\n" +" :arg size: The size of the vector to be created.\n" +" :type size: int\n" +); +static PyObject *C_Vector_Linspace(PyObject *cls, PyObject *args) +{ + float *vec; + int size; + float start, end, step; + + if (!PyArg_ParseTuple(args, "ffi:Vector.Linspace", &start, &end, &size)) { + return NULL; + } + + if (size < 2) { + PyErr_SetString(PyExc_RuntimeError, + "Vector.Linspace(): invalid size"); + return NULL; + } + + step= (end - start)/(float)(size-1); + + vec= PyMem_Malloc(size * sizeof(float)); + + if (vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector.Linspace(): " + "problem allocating pointer space"); + return NULL; + } + + range_vn_fl(vec, size, start, step); + + return Vector_CreatePyObject_alloc(vec, size, (PyTypeObject *)cls); +} + +PyDoc_STRVAR(C_Vector_Repeat_doc, +".. classmethod:: Repeat(vector, size)\n" +"\n" +" Create a vector by repeating the values in vector until the required size is reached.\n" +"\n" +" :arg tuple: The vector to draw values from.\n" +" :type tuple: :class:`mathutils.Vector`\n" +" :arg size: The size of the vector to be created.\n" +" :type size: int\n" +); +static PyObject *C_Vector_Repeat(PyObject *cls, PyObject *args) +{ + float *vec; + float *iter_vec= NULL; + int i, size, value_size; + PyObject *value; + + if (!PyArg_ParseTuple(args, "Oi:Vector.Repeat", &value, &size)) { + return NULL; + } + + if (size < 2) { + PyErr_SetString(PyExc_RuntimeError, + "Vector.Repeat(): invalid size"); + return NULL; + } + + if ((value_size=mathutils_array_parse_alloc(&iter_vec, 2, value, "Vector.Repeat(vector, size), invalid 'vector' arg")) == -1) { + PyMem_Free(iter_vec); + return NULL; + } + + if (iter_vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector.Repeat(): " + "problem allocating pointer space"); + return NULL; + } + + vec= PyMem_Malloc(size * sizeof(float)); + + if (vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector.Repeat(): " + "problem allocating pointer space"); + return NULL; + } + + i= 0; + while (i < size) { + vec[i]= iter_vec[i % value_size]; + i++; + } + + PyMem_Free(iter_vec); + + return Vector_CreatePyObject_alloc(vec, size, (PyTypeObject *)cls); +} + /*-----------------------------METHODS---------------------------- */ PyDoc_STRVAR(Vector_zero_doc, ".. method:: zero()\n" @@ -137,6 +360,101 @@ static PyObject *Vector_normalized(VectorObject *self) return vec__apply_to_copy((PyNoArgsFunction)Vector_normalize, self); } +PyDoc_STRVAR(Vector_resize_doc, +".. method:: resize(size=3)\n" +"\n" +" Resize the vector to have size number of elements.\n" +"\n" +" :return: an instance of itself\n" +" :rtype: :class:`Vector`\n" +); +static PyObject *Vector_resize(VectorObject *self, PyObject *value) +{ + int size; + + if (self->wrapped==Py_WRAP) { + PyErr_SetString(PyExc_TypeError, + "Vector.resize(): " + "cannot resize wrapped data - only python vectors"); + return NULL; + } + if (self->cb_user) { + PyErr_SetString(PyExc_TypeError, + "Vector.resize(): " + "cannot resize a vector that has an owner"); + return NULL; + } + + if ((size = PyLong_AsLong(value)) == -1) { + PyErr_SetString(PyExc_TypeError, + "Vector.resize(size): " + "expected size argument to be an integer"); + return NULL; + } + + if (size < 2) { + PyErr_SetString(PyExc_RuntimeError, + "Vector.resize(): invalid size"); + return NULL; + } + + self->vec = PyMem_Realloc(self->vec, (size * sizeof(float))); + if (self->vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector.resize(): " + "problem allocating pointer space"); + return NULL; + } + + /* If the vector has increased in length, set all new elements to 0.0f */ + if (size > self->size) { + fill_vn_fl(self->vec + self->size, size - self->size, 0.0f); + } + + self->size = size; + Py_RETURN_NONE; +} + +PyDoc_STRVAR(Vector_resized_doc, +".. method:: resized(size=3)\n" +"\n" +" Return a resized copy of the vector with size number of elements.\n" +"\n" +" :return: a new vector\n" +" :rtype: :class:`Vector`\n" +); +static PyObject *Vector_resized(VectorObject *self, PyObject *value) +{ + int size; + float *vec; + + /*if (!PyArg_ParseTuple(args, "i:resize", &size)) + return NULL;*/ + if ((size = PyLong_AsLong(value)) == -1){ + return NULL; + } + + if (size < 2) { + PyErr_SetString(PyExc_RuntimeError, + "Vector.resized(): invalid size"); + return NULL; + } + + vec= PyMem_Malloc(size * sizeof(float)); + + if (vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "Vector.resized(): " + "problem allocating pointer space"); + return NULL; + } + + fill_vn_fl(vec, size, 0.0f); + memcpy(vec, self->vec, self->size * sizeof(float)); + + return Vector_CreatePyObject_alloc(vec, size, NULL); +} + PyDoc_STRVAR(Vector_resize_2d_doc, ".. method:: resize_2d()\n" "\n" @@ -505,6 +823,12 @@ static PyObject *Vector_reflect(VectorObject *self, PyObject *value) if ((value_size= mathutils_array_parse(tvec, 2, 4, value, "Vector.reflect(other), invalid 'other' arg")) == -1) return NULL; + if (self->size < 2 || self->size > 4) { + PyErr_SetString(PyExc_ValueError, + "Vector must be 2D, 3D or 4D"); + return NULL; + } + mirror[0] = tvec[0]; mirror[1] = tvec[1]; if (value_size > 2) mirror[2] = tvec[2]; @@ -544,6 +868,12 @@ static PyObject *Vector_cross(VectorObject *self, PyObject *value) if (mathutils_array_parse(tvec, self->size, self->size, value, "Vector.cross(other), invalid 'other' arg") == -1) return NULL; + if (self->size != 3) { + PyErr_SetString(PyExc_ValueError, + "Vector must be 3D"); + return NULL; + } + ret= (VectorObject *)Vector_CreatePyObject(NULL, 3, Py_NEW, Py_TYPE(self)); cross_v3_v3v3(ret->vec, self->vec, tvec); return (PyObject *)ret; @@ -561,15 +891,20 @@ PyDoc_STRVAR(Vector_dot_doc, ); static PyObject *Vector_dot(VectorObject *self, PyObject *value) { - float tvec[MAX_DIMENSIONS]; + float *tvec; if (BaseMath_ReadCallback(self) == -1) return NULL; - if (mathutils_array_parse(tvec, self->size, self->size, value, "Vector.dot(other), invalid 'other' arg") == -1) - return NULL; + if (mathutils_array_parse_alloc(&tvec, self->size, value, "Vector.dot(other), invalid 'other' arg") == -1) { + goto cleanup; + } return PyFloat_FromDouble(dot_vn_vn(self->vec, tvec, self->size)); + +cleanup: + PyMem_Free(tvec); + return NULL; } PyDoc_STRVAR(Vector_angle_doc, @@ -607,6 +942,12 @@ static PyObject *Vector_angle(VectorObject *self, PyObject *args) if (mathutils_array_parse(tvec, self->size, self->size, value, "Vector.angle(other), invalid 'other' arg") == -1) return NULL; + if (self->size > 4) { + PyErr_SetString(PyExc_ValueError, + "Vector must be 2D, 3D or 4D"); + return NULL; + } + for (x = 0; x < size; x++) { dot_self += (double)self->vec[x] * (double)self->vec[x]; dot_other += (double)tvec[x] * (double)tvec[x]; @@ -647,7 +988,7 @@ static PyObject *Vector_rotation_difference(VectorObject *self, PyObject *value) { float quat[4], vec_a[3], vec_b[3]; - if (self->size < 3) { + if (self->size < 3 || self->size > 4) { PyErr_SetString(PyExc_ValueError, "vec.difference(value): " "expects both vectors to be size 3 or 4"); @@ -692,6 +1033,12 @@ static PyObject *Vector_project(VectorObject *self, PyObject *value) if (mathutils_array_parse(tvec, size, size, value, "Vector.project(other), invalid 'other' arg") == -1) return NULL; + if (self->size > 4) { + PyErr_SetString(PyExc_ValueError, + "Vector must be 2D, 3D or 4D"); + return NULL; + } + if (BaseMath_ReadCallback(self) == -1) return NULL; @@ -725,24 +1072,41 @@ static PyObject *Vector_lerp(VectorObject *self, PyObject *args) const int size= self->size; PyObject *value= NULL; float fac, ifac; - float tvec[MAX_DIMENSIONS], vec[MAX_DIMENSIONS]; + float *tvec, *vec; int x; if (!PyArg_ParseTuple(args, "Of:lerp", &value, &fac)) return NULL; - if (mathutils_array_parse(tvec, size, size, value, "Vector.lerp(other), invalid 'other' arg") == -1) - return NULL; + if (mathutils_array_parse_alloc(&tvec, size, value, "Vector.lerp(other), invalid 'other' arg") == -1) { + goto cleanup; + } - if (BaseMath_ReadCallback(self) == -1) + if (BaseMath_ReadCallback(self) == -1) { + goto cleanup; + } + + 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]); } - return Vector_CreatePyObject(vec, size, Py_NEW, Py_TYPE(self)); + + PyMem_Free(tvec); + + return Vector_CreatePyObject_alloc(vec, size, Py_TYPE(self)); + +cleanup: + PyMem_Free(tvec); + return NULL; } PyDoc_STRVAR(Vector_rotate_doc, @@ -763,7 +1127,7 @@ static PyObject *Vector_rotate(VectorObject *self, PyObject *value) if (mathutils_any_to_rotmat(other_rmat, value, "Vector.rotate(value)") == -1) return NULL; - if (self->size < 3) { + if (self->size < 3 || self->size > 4) { PyErr_SetString(PyExc_ValueError, "Vector must be 3D or 4D"); return NULL; @@ -903,8 +1267,8 @@ static PyObject *Vector_slice(VectorObject *self, int begin, int end) /* sequence slice (set): vector[a:b] = value */ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *seq) { - int y, size = 0; - float vec[MAX_DIMENSIONS]; + int size = 0; + float *vec= NULL; if (BaseMath_ReadCallback(self) == -1) return -1; @@ -914,18 +1278,30 @@ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *se begin = MIN2(begin, end); size = (end - begin); - if (mathutils_array_parse(vec, size, size, seq, "vector[begin:end] = [...]") == -1) + if (mathutils_array_parse_alloc(&vec, size, seq, "vector[begin:end] = [...]") == -1) { + goto cleanup; + } + + if (vec == NULL) { + PyErr_SetString(PyExc_MemoryError, + "vec[:] = seq: " + "problem allocating pointer space"); return -1; + } /*parsed well - now set in vector*/ - for (y = 0; y < size; y++) { - self->vec[begin + y] = vec[y]; - } + memcpy(self->vec + begin, vec, size * sizeof(float)); if (BaseMath_WriteCallback(self) == -1) return -1; + PyMem_Free(vec); + return 0; + +cleanup: + PyMem_Free(vec); + return -1; } /* Numeric Protocols */ @@ -933,7 +1309,7 @@ static int Vector_ass_slice(VectorObject *self, int begin, int end, PyObject *se static PyObject *Vector_add(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; - float vec[MAX_DIMENSIONS]; + float *vec= NULL; if (!VectorObject_Check(v1) || !VectorObject_Check(v2)) { PyErr_Format(PyExc_AttributeError, @@ -956,9 +1332,18 @@ static PyObject *Vector_add(PyObject *v1, PyObject *v2) return NULL; } + vec= PyMem_Malloc(vec1->size * sizeof(float)); + + if (vec == NULL) { /*allocation failure*/ + PyErr_SetString(PyExc_MemoryError, + "Vector(): " + "problem allocating pointer space"); + return NULL; + } + add_vn_vnvn(vec, vec1->vec, vec2->vec, vec1->size); - return Vector_CreatePyObject(vec, vec1->size, Py_NEW, Py_TYPE(v1)); + return Vector_CreatePyObject_alloc(vec, vec1->size, Py_TYPE(v1)); } /* addition in-place: obj += obj */ @@ -997,7 +1382,7 @@ static PyObject *Vector_iadd(PyObject *v1, PyObject *v2) static PyObject *Vector_sub(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; - float vec[MAX_DIMENSIONS]; + float *vec; if (!VectorObject_Check(v1) || !VectorObject_Check(v2)) { PyErr_Format(PyExc_AttributeError, @@ -1019,9 +1404,18 @@ static PyObject *Vector_sub(PyObject *v1, PyObject *v2) return NULL; } + vec= PyMem_Malloc(vec1->size * sizeof(float)); + + if (vec == NULL) { /*allocation failure*/ + PyErr_SetString(PyExc_MemoryError, + "Vector(): " + "problem allocating pointer space"); + return NULL; + } + sub_vn_vnvn(vec, vec1->vec, vec2->vec, vec1->size); - return Vector_CreatePyObject(vec, vec1->size, Py_NEW, Py_TYPE(v1)); + return Vector_CreatePyObject_alloc(vec, vec1->size, Py_TYPE(v1)); } /* subtraction in-place: obj -= obj */ @@ -1104,9 +1498,18 @@ int column_vector_multiplication(float rvec[MAX_DIMENSIONS], VectorObject* vec, static PyObject *vector_mul_float(VectorObject *vec, const float scalar) { - float tvec[MAX_DIMENSIONS]; + float *tvec= NULL; + tvec= PyMem_Malloc(vec->size * sizeof(float)); + + if (tvec == NULL) { /*allocation failure*/ + PyErr_SetString(PyExc_MemoryError, + "vec * float: " + "problem allocating pointer space"); + return NULL; + } + mul_vn_vn_fl(tvec, vec->vec, vec->size, scalar); - return Vector_CreatePyObject(tvec, vec->size, Py_NEW, Py_TYPE(vec)); + return Vector_CreatePyObject_alloc(tvec, vec->size, Py_TYPE(vec)); } static PyObject *Vector_mul(PyObject *v1, PyObject *v2) @@ -1277,8 +1680,7 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) /* divid: obj / obj */ static PyObject *Vector_div(PyObject *v1, PyObject *v2) { - int i; - float vec[4], scalar; + float *vec= NULL, scalar; VectorObject *vec1 = NULL; if (!VectorObject_Check(v1)) { /* not a vector */ @@ -1287,7 +1689,7 @@ static PyObject *Vector_div(PyObject *v1, PyObject *v2) "Vector must be divided by a float"); return NULL; } - vec1 = (VectorObject*)v1; /* vector */ + vec1 = (VectorObject *)v1; /* vector */ if (BaseMath_ReadCallback(vec1) == -1) return NULL; @@ -1306,16 +1708,23 @@ static PyObject *Vector_div(PyObject *v1, PyObject *v2) return NULL; } - for (i = 0; i < vec1->size; i++) { - vec[i] = vec1->vec[i] / scalar; + vec= PyMem_Malloc(vec1->size * sizeof(float)); + + if (vec == NULL) { /*allocation failure*/ + PyErr_SetString(PyExc_MemoryError, + "vec / value: " + "problem allocating pointer space"); + return NULL; } - return Vector_CreatePyObject(vec, vec1->size, Py_NEW, Py_TYPE(v1)); + + mul_vn_vn_fl(vec, vec1->vec, vec1->size, 1.0f/scalar); + + return Vector_CreatePyObject_alloc(vec, vec1->size, Py_TYPE(v1)); } /* divide in-place: obj /= obj */ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) { - int i; float scalar; VectorObject *vec1 = (VectorObject*)v1; @@ -1335,9 +1744,8 @@ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) "divide by zero error"); return NULL; } - for (i = 0; i < vec1->size; i++) { - vec1->vec[i] /= scalar; - } + + mul_vn_fl(vec1->vec, vec1->size, 1.0f/scalar); (void)BaseMath_WriteCallback(vec1); @@ -1349,29 +1757,24 @@ static PyObject *Vector_idiv(PyObject *v1, PyObject *v2) returns the negative of this object*/ static PyObject *Vector_neg(VectorObject *self) { - float tvec[MAX_DIMENSIONS]; + float *tvec; if (BaseMath_ReadCallback(self) == -1) return NULL; + tvec= PyMem_Malloc(self->size * sizeof(float)); negate_vn_vn(tvec, self->vec, self->size); - return Vector_CreatePyObject(tvec, self->size, Py_NEW, Py_TYPE(self)); + return Vector_CreatePyObject_alloc(tvec, self->size, Py_TYPE(self)); } /*------------------------vec_magnitude_nosqrt (internal) - for comparing only */ static double vec_magnitude_nosqrt(float *data, int size) { - double dot = 0.0f; - int i; - - for (i=0; isize; i++) { - dot += (double)(self->vec[i] * self->vec[i]); - } - return PyFloat_FromDouble(sqrt(dot)); + return PyFloat_FromDouble(sqrt(dot_vn_vn(self->vec, self->vec, self->size))); } static int Vector_setLength(VectorObject *self, PyObject *value) @@ -2242,6 +2639,12 @@ static PyObject *Vector_negate(VectorObject *self) } static struct PyMethodDef Vector_methods[] = { + /* Class Methods */ + {"Fill", (PyCFunction) C_Vector_Fill, METH_VARARGS | METH_CLASS, C_Vector_Fill_doc}, + {"Range", (PyCFunction) C_Vector_Range, METH_VARARGS | METH_CLASS, C_Vector_Range_doc}, + {"Linspace", (PyCFunction) C_Vector_Linspace, METH_VARARGS | METH_CLASS, C_Vector_Linspace_doc}, + {"Repeat", (PyCFunction) C_Vector_Repeat, METH_VARARGS | METH_CLASS, C_Vector_Repeat_doc}, + /* in place only */ {"zero", (PyCFunction) Vector_zero, METH_NOARGS, Vector_zero_doc}, {"negate", (PyCFunction) Vector_negate, METH_NOARGS, Vector_negate_doc}, @@ -2250,6 +2653,8 @@ static struct PyMethodDef Vector_methods[] = { {"normalize", (PyCFunction) Vector_normalize, METH_NOARGS, Vector_normalize_doc}, {"normalized", (PyCFunction) Vector_normalized, METH_NOARGS, Vector_normalized_doc}, + {"resize", (PyCFunction) Vector_resize, METH_O, Vector_resize_doc}, + {"resized", (PyCFunction) Vector_resized, METH_O, Vector_resized_doc}, {"to_2d", (PyCFunction) Vector_to_2d, METH_NOARGS, Vector_to_2d_doc}, {"resize_2d", (PyCFunction) Vector_resize_2d, METH_NOARGS, Vector_resize_2d_doc}, {"to_3d", (PyCFunction) Vector_to_3d, METH_NOARGS, Vector_to_3d_doc}, @@ -2375,7 +2780,7 @@ PyObject *Vector_CreatePyObject(float *vec, const int size, const int type, PyTy { VectorObject *self; - if (size > 4 || size < 2) { + if (size < 2) { PyErr_SetString(PyExc_RuntimeError, "Vector(): invalid size"); return NULL; @@ -2429,3 +2834,12 @@ PyObject *Vector_CreatePyObject_cb(PyObject *cb_user, int size, int cb_type, int return (PyObject *)self; } + +PyObject *Vector_CreatePyObject_alloc(float *vec, const int size, PyTypeObject *base_type) +{ + VectorObject *vect_ob; + vect_ob= (VectorObject *)Vector_CreatePyObject(vec, size, Py_WRAP, base_type); + vect_ob->wrapped= Py_NEW; + + return (PyObject *)vect_ob; +} diff --git a/source/blender/python/mathutils/mathutils_Vector.h b/source/blender/python/mathutils/mathutils_Vector.h index 0f7fa174d18..4e10e795602 100644 --- a/source/blender/python/mathutils/mathutils_Vector.h +++ b/source/blender/python/mathutils/mathutils_Vector.h @@ -41,11 +41,12 @@ extern PyTypeObject vector_Type; typedef struct { BASE_MATH_MEMBERS(vec); - unsigned char size; /* vec size 2,3 or 4 */ + int size; /* vec size 2,3 or 4 */ } VectorObject; /*prototypes*/ PyObject *Vector_CreatePyObject(float *vec, const int size, const int type, PyTypeObject *base_type); PyObject *Vector_CreatePyObject_cb(PyObject *user, int size, int callback_type, int subtype); +PyObject *Vector_CreatePyObject_alloc(float *vec, const int size, PyTypeObject *base_type); #endif /* MATHUTILS_VECTOR_H */