Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/python/mathutils/mathutils_Quaternion.c')
-rw-r--r--source/blender/python/mathutils/mathutils_Quaternion.c224
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;