diff options
Diffstat (limited to 'source/blender/python/mathutils/mathutils_Quaternion.c')
-rw-r--r-- | source/blender/python/mathutils/mathutils_Quaternion.c | 224 |
1 files changed, 164 insertions, 60 deletions
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; |