e06bee320311b5b2b5b15f5491e267134b3b0cfd
[blender.git] / source / blender / python / generic / quat.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Joseph Gilbert
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include "Mathutils.h"
30
31 #include "BLI_arithb.h"
32 #include "BKE_utildefines.h"
33 #include "BLI_blenlib.h"
34
35
36 //-------------------------DOC STRINGS ---------------------------
37
38 static PyObject *Quaternion_Identity( QuaternionObject * self );
39 static PyObject *Quaternion_Negate( QuaternionObject * self );
40 static PyObject *Quaternion_Conjugate( QuaternionObject * self );
41 static PyObject *Quaternion_Inverse( QuaternionObject * self );
42 static PyObject *Quaternion_Normalize( QuaternionObject * self );
43 static PyObject *Quaternion_ToEuler( QuaternionObject * self, PyObject *args );
44 static PyObject *Quaternion_ToMatrix( QuaternionObject * self );
45 static PyObject *Quaternion_Cross( QuaternionObject * self, QuaternionObject * value );
46 static PyObject *Quaternion_Dot( QuaternionObject * self, QuaternionObject * value );
47 static PyObject *Quaternion_copy( QuaternionObject * self );
48
49 //-----------------------METHOD DEFINITIONS ----------------------
50 static struct PyMethodDef Quaternion_methods[] = {
51         {"identity", (PyCFunction) Quaternion_Identity, METH_NOARGS, NULL},
52         {"negate", (PyCFunction) Quaternion_Negate, METH_NOARGS, NULL},
53         {"conjugate", (PyCFunction) Quaternion_Conjugate, METH_NOARGS, NULL},
54         {"inverse", (PyCFunction) Quaternion_Inverse, METH_NOARGS, NULL},
55         {"normalize", (PyCFunction) Quaternion_Normalize, METH_NOARGS, NULL},
56         {"toEuler", (PyCFunction) Quaternion_ToEuler, METH_VARARGS, NULL},
57         {"toMatrix", (PyCFunction) Quaternion_ToMatrix, METH_NOARGS, NULL},
58         {"cross", (PyCFunction) Quaternion_Cross, METH_O, NULL},
59         {"dot", (PyCFunction) Quaternion_Dot, METH_O, NULL},
60         {"__copy__", (PyCFunction) Quaternion_copy, METH_NOARGS, NULL},
61         {"copy", (PyCFunction) Quaternion_copy, METH_NOARGS, NULL},
62         {NULL, NULL, 0, NULL}
63 };
64
65 //----------------------------------Mathutils.Quaternion() --------------
66 static PyObject *Quaternion_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
67 {
68         PyObject *listObject = NULL, *n, *q;
69         int size, i;
70         float quat[4];
71         double angle = 0.0f;
72
73         size = PyTuple_GET_SIZE(args);
74         if (size == 1 || size == 2) { //seq?
75                 listObject = PyTuple_GET_ITEM(args, 0);
76                 if (PySequence_Check(listObject)) {
77                         size = PySequence_Length(listObject);
78                         if ((size == 4 && PySequence_Length(args) !=1) || 
79                                 (size == 3 && PySequence_Length(args) !=2) || (size >4 || size < 3)) { 
80                                 // invalid args/size
81                                 PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
82                                 return NULL;
83                         }
84                         if(size == 3){ //get angle in axis/angle
85                                 n = PySequence_GetItem(args, 1);
86                                 if(n == NULL) { // parsed item not a number or getItem fail
87                                         PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
88                                         return NULL;
89                                 }
90                                 
91                                 angle = PyFloat_AsDouble(n);
92                                 Py_DECREF(n);
93                                 
94                                 if (angle==-1 && PyErr_Occurred()) {
95                                         PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
96                                         return NULL;
97                                 }
98                         }
99                 }else{
100                         listObject = PyTuple_GET_ITEM(args, 1);
101                         if (size>1 && PySequence_Check(listObject)) {
102                                 size = PySequence_Length(listObject);
103                                 if (size != 3) { 
104                                         // invalid args/size
105                                         PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
106                                         return NULL;
107                                 }
108                                 angle = PyFloat_AsDouble(PyTuple_GET_ITEM(args, 0));
109                                 
110                                 if (angle==-1 && PyErr_Occurred()) {
111                                         PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
112                                         return NULL;
113                                 }
114                         } else { // argument was not a sequence
115                                 PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
116                                 return NULL;
117                         }
118                 }
119         } else if (size == 0) { //returns a new empty quat
120                 return newQuaternionObject(NULL, Py_NEW, NULL);
121         } else {
122                 listObject = args;
123         }
124
125         if (size == 3) { // invalid quat size
126                 if(PySequence_Length(args) != 2){
127                         PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
128                         return NULL;
129                 }
130         }else{
131                 if(size != 4){
132                         PyErr_SetString(PyExc_AttributeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
133                         return NULL;
134                 }
135         }
136
137         for (i=0; i<size; i++) { //parse
138                 q = PySequence_GetItem(listObject, i);
139                 if (q == NULL) { // Failed to read sequence
140                         PyErr_SetString(PyExc_RuntimeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
141                         return NULL;
142                 }
143
144                 quat[i] = PyFloat_AsDouble(q);
145                 Py_DECREF(q);
146
147                 if (quat[i]==-1 && PyErr_Occurred()) {
148                         PyErr_SetString(PyExc_TypeError, "Mathutils.Quaternion(): 4d numeric sequence expected or 3d vector and number\n");
149                         return NULL;
150                 }
151         }
152
153         if(size == 3) //calculate the quat based on axis/angle
154 #ifdef USE_MATHUTILS_DEG
155                 AxisAngleToQuat(quat, quat, angle * (Py_PI / 180));
156 #else
157                 AxisAngleToQuat(quat, quat, angle);
158 #endif
159
160         return newQuaternionObject(quat, Py_NEW, NULL);
161 }
162
163 //-----------------------------METHODS------------------------------
164 //----------------------------Quaternion.toEuler()------------------
165 //return the quat as a euler
166 static PyObject *Quaternion_ToEuler(QuaternionObject * self, PyObject *args)
167 {
168         float eul[3];
169         EulerObject *eul_compat = NULL;
170         
171         if(!PyArg_ParseTuple(args, "|O!:toEuler", &euler_Type, &eul_compat))
172                 return NULL;
173         
174         if(!BaseMath_ReadCallback(self))
175                 return NULL;
176
177         if(eul_compat) {
178                 float mat[3][3];
179                 
180                 if(!BaseMath_ReadCallback(eul_compat))
181                         return NULL;
182                 
183                 QuatToMat3(self->quat, mat);
184
185 #ifdef USE_MATHUTILS_DEG
186                 {
187                         float  eul_compatf[3];
188                         int x;
189
190                         for(x = 0; x < 3; x++) {
191                                 eul_compatf[x] = eul_compat->eul[x] * ((float)Py_PI / 180);
192                         }
193                         Mat3ToCompatibleEul(mat, eul, eul_compatf);
194                 }
195 #else
196                 Mat3ToCompatibleEul(mat, eul, eul_compat->eul);
197 #endif
198         }
199         else {
200                 QuatToEul(self->quat, eul);
201         }
202         
203 #ifdef USE_MATHUTILS_DEG
204         {
205                 int x;
206
207                 for(x = 0; x < 3; x++) {
208                         eul[x] *= (180 / (float)Py_PI);
209                 }
210         }
211 #endif
212         return newEulerObject(eul, Py_NEW, NULL);
213 }
214 //----------------------------Quaternion.toMatrix()------------------
215 //return the quat as a matrix
216 static PyObject *Quaternion_ToMatrix(QuaternionObject * self)
217 {
218         float mat[9]; /* all values are set */
219
220         if(!BaseMath_ReadCallback(self))
221                 return NULL;
222
223         QuatToMat3(self->quat, (float (*)[3]) mat);
224         return newMatrixObject(mat, 3, 3, Py_NEW, NULL);
225 }
226
227 //----------------------------Quaternion.cross(other)------------------
228 //return the cross quat
229 static PyObject *Quaternion_Cross(QuaternionObject * self, QuaternionObject * value)
230 {
231         float quat[4];
232         
233         if (!QuaternionObject_Check(value)) {
234                 PyErr_SetString( PyExc_TypeError, "quat.cross(value): expected a quaternion argument" );
235                 return NULL;
236         }
237         
238         if(!BaseMath_ReadCallback(self) || !BaseMath_ReadCallback(value))
239                 return NULL;
240
241         QuatMul(quat, self->quat, value->quat);
242         return newQuaternionObject(quat, Py_NEW, NULL);
243 }
244
245 //----------------------------Quaternion.dot(other)------------------
246 //return the dot quat
247 static PyObject *Quaternion_Dot(QuaternionObject * self, QuaternionObject * value)
248 {
249         if (!QuaternionObject_Check(value)) {
250                 PyErr_SetString( PyExc_TypeError, "quat.dot(value): expected a quaternion argument" );
251                 return NULL;
252         }
253
254         if(!BaseMath_ReadCallback(self) || !BaseMath_ReadCallback(value))
255                 return NULL;
256
257         return PyFloat_FromDouble(QuatDot(self->quat, value->quat));
258 }
259
260 //----------------------------Quaternion.normalize()----------------
261 //normalize the axis of rotation of [theta,vector]
262 static PyObject *Quaternion_Normalize(QuaternionObject * self)
263 {
264         if(!BaseMath_ReadCallback(self))
265                 return NULL;
266
267         NormalQuat(self->quat);
268
269         BaseMath_WriteCallback(self);
270         Py_INCREF(self);
271         return (PyObject*)self;
272 }
273 //----------------------------Quaternion.inverse()------------------
274 //invert the quat
275 static PyObject *Quaternion_Inverse(QuaternionObject * self)
276 {
277         if(!BaseMath_ReadCallback(self))
278                 return NULL;
279
280         QuatInv(self->quat);
281
282         BaseMath_WriteCallback(self);
283         Py_INCREF(self);
284         return (PyObject*)self;
285 }
286 //----------------------------Quaternion.identity()-----------------
287 //generate the identity quaternion
288 static PyObject *Quaternion_Identity(QuaternionObject * self)
289 {
290         if(!BaseMath_ReadCallback(self))
291                 return NULL;
292
293         QuatOne(self->quat);
294
295         BaseMath_WriteCallback(self);
296         Py_INCREF(self);
297         return (PyObject*)self;
298 }
299 //----------------------------Quaternion.negate()-------------------
300 //negate the quat
301 static PyObject *Quaternion_Negate(QuaternionObject * self)
302 {
303         if(!BaseMath_ReadCallback(self))
304                 return NULL;
305
306         QuatMulf(self->quat, -1.0f);
307
308         BaseMath_WriteCallback(self);
309         Py_INCREF(self);
310         return (PyObject*)self;
311 }
312 //----------------------------Quaternion.conjugate()----------------
313 //negate the vector part
314 static PyObject *Quaternion_Conjugate(QuaternionObject * self)
315 {
316         if(!BaseMath_ReadCallback(self))
317                 return NULL;
318
319         QuatConj(self->quat);
320
321         BaseMath_WriteCallback(self);
322         Py_INCREF(self);
323         return (PyObject*)self;
324 }
325 //----------------------------Quaternion.copy()----------------
326 //return a copy of the quat
327 static PyObject *Quaternion_copy(QuaternionObject * self)
328 {
329         if(!BaseMath_ReadCallback(self))
330                 return NULL;
331
332         return newQuaternionObject(self->quat, Py_NEW, Py_TYPE(self));
333 }
334
335 //----------------------------print object (internal)--------------
336 //print the object to screen
337 static PyObject *Quaternion_repr(QuaternionObject * self)
338 {
339         char str[64];
340
341         if(!BaseMath_ReadCallback(self))
342                 return NULL;
343
344         sprintf(str, "[%.6f, %.6f, %.6f, %.6f](quaternion)", self->quat[0], self->quat[1], self->quat[2], self->quat[3]);
345         return PyUnicode_FromString(str);
346 }
347 //------------------------tp_richcmpr
348 //returns -1 execption, 0 false, 1 true
349 static PyObject* Quaternion_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type)
350 {
351         QuaternionObject *quatA = NULL, *quatB = NULL;
352         int result = 0;
353
354         if(QuaternionObject_Check(objectA)) {
355                 quatA = (QuaternionObject*)objectA;
356                 if(!BaseMath_ReadCallback(quatA))
357                         return NULL;
358         }
359         if(QuaternionObject_Check(objectB)) {
360                 quatB = (QuaternionObject*)objectB;
361                 if(!BaseMath_ReadCallback(quatB))
362                         return NULL;
363         }
364
365         if (!quatA || !quatB){
366                 if (comparison_type == Py_NE){
367                         Py_RETURN_TRUE;
368                 }else{
369                         Py_RETURN_FALSE;
370                 }
371         }
372
373         switch (comparison_type){
374                 case Py_EQ:
375                         result = EXPP_VectorsAreEqual(quatA->quat, quatB->quat, 4, 1);
376                         break;
377                 case Py_NE:
378                         result = EXPP_VectorsAreEqual(quatA->quat, quatB->quat, 4, 1);
379                         if (result == 0){
380                                 result = 1;
381                         }else{
382                                 result = 0;
383                         }
384                         break;
385                 default:
386                         printf("The result of the comparison could not be evaluated");
387                         break;
388         }
389         if (result == 1){
390                 Py_RETURN_TRUE;
391         }else{
392                 Py_RETURN_FALSE;
393         }
394 }
395
396 //---------------------SEQUENCE PROTOCOLS------------------------
397 //----------------------------len(object)------------------------
398 //sequence length
399 static int Quaternion_len(QuaternionObject * self)
400 {
401         return 4;
402 }
403 //----------------------------object[]---------------------------
404 //sequence accessor (get)
405 static PyObject *Quaternion_item(QuaternionObject * self, int i)
406 {
407         if(i<0) i= 4-i;
408
409         if(i < 0 || i >= 4) {
410                 PyErr_SetString(PyExc_IndexError, "quaternion[attribute]: array index out of range\n");
411                 return NULL;
412         }
413
414         if(!BaseMath_ReadIndexCallback(self, i))
415                 return NULL;
416
417         return PyFloat_FromDouble(self->quat[i]);
418
419 }
420 //----------------------------object[]-------------------------
421 //sequence accessor (set)
422 static int Quaternion_ass_item(QuaternionObject * self, int i, PyObject * ob)
423 {
424         float scalar= (float)PyFloat_AsDouble(ob);
425         if(scalar==-1.0f && PyErr_Occurred()) { /* parsed item not a number */
426                 PyErr_SetString(PyExc_TypeError, "quaternion[index] = x: index argument not a number\n");
427                 return -1;
428         }
429
430         if(i<0) i= 4-i;
431
432         if(i < 0 || i >= 4){
433                 PyErr_SetString(PyExc_IndexError, "quaternion[attribute] = x: array assignment index out of range\n");
434                 return -1;
435         }
436         self->quat[i] = scalar;
437
438         if(!BaseMath_WriteIndexCallback(self, i))
439                 return -1;
440
441         return 0;
442 }
443 //----------------------------object[z:y]------------------------
444 //sequence slice (get)
445 static PyObject *Quaternion_slice(QuaternionObject * self, int begin, int end)
446 {
447         PyObject *list = NULL;
448         int count;
449
450         if(!BaseMath_ReadCallback(self))
451                 return NULL;
452
453         CLAMP(begin, 0, 4);
454         if (end<0) end= 5+end;
455         CLAMP(end, 0, 4);
456         begin = MIN2(begin,end);
457
458         list = PyList_New(end - begin);
459         for(count = begin; count < end; count++) {
460                 PyList_SetItem(list, count - begin,
461                                 PyFloat_FromDouble(self->quat[count]));
462         }
463
464         return list;
465 }
466 //----------------------------object[z:y]------------------------
467 //sequence slice (set)
468 static int Quaternion_ass_slice(QuaternionObject * self, int begin, int end, PyObject * seq)
469 {
470         int i, y, size = 0;
471         float quat[4];
472         PyObject *q;
473
474         if(!BaseMath_ReadCallback(self))
475                 return -1;
476
477         CLAMP(begin, 0, 4);
478         if (end<0) end= 5+end;
479         CLAMP(end, 0, 4);
480         begin = MIN2(begin,end);
481
482         size = PySequence_Length(seq);
483         if(size != (end - begin)){
484                 PyErr_SetString(PyExc_TypeError, "quaternion[begin:end] = []: size mismatch in slice assignment\n");
485                 return -1;
486         }
487
488         for (i = 0; i < size; i++) {
489                 q = PySequence_GetItem(seq, i);
490                 if (q == NULL) { // Failed to read sequence
491                         PyErr_SetString(PyExc_RuntimeError, "quaternion[begin:end] = []: unable to read sequence\n");
492                         return -1;
493                 }
494
495                 quat[i]= (float)PyFloat_AsDouble(q);
496                 Py_DECREF(q);
497
498                 if(quat[i]==-1.0f && PyErr_Occurred()) { /* parsed item not a number */
499                         PyErr_SetString(PyExc_TypeError, "quaternion[begin:end] = []: sequence argument not a number\n");
500                         return -1;
501                 }
502         }
503         //parsed well - now set in vector
504         for(y = 0; y < size; y++)
505                 self->quat[begin + y] = quat[y];
506
507         BaseMath_WriteCallback(self);
508         return 0;
509 }
510 //------------------------NUMERIC PROTOCOLS----------------------
511 //------------------------obj + obj------------------------------
512 //addition
513 static PyObject *Quaternion_add(PyObject * q1, PyObject * q2)
514 {
515         float quat[4];
516         QuaternionObject *quat1 = NULL, *quat2 = NULL;
517
518         if(!QuaternionObject_Check(q1) || !QuaternionObject_Check(q2)) {
519                 PyErr_SetString(PyExc_AttributeError, "Quaternion addition: arguments not valid for this operation....\n");
520                 return NULL;
521         }
522         quat1 = (QuaternionObject*)q1;
523         quat2 = (QuaternionObject*)q2;
524         
525         if(!BaseMath_ReadCallback(quat1) || !BaseMath_ReadCallback(quat2))
526                 return NULL;
527
528         QuatAdd(quat, quat1->quat, quat2->quat, 1.0f);
529         return newQuaternionObject(quat, Py_NEW, NULL);
530 }
531 //------------------------obj - obj------------------------------
532 //subtraction
533 static PyObject *Quaternion_sub(PyObject * q1, PyObject * q2)
534 {
535         int x;
536         float quat[4];
537         QuaternionObject *quat1 = NULL, *quat2 = NULL;
538
539         if(!QuaternionObject_Check(q1) || !QuaternionObject_Check(q2)) {
540                 PyErr_SetString(PyExc_AttributeError, "Quaternion addition: arguments not valid for this operation....\n");
541                 return NULL;
542         }
543         
544         quat1 = (QuaternionObject*)q1;
545         quat2 = (QuaternionObject*)q2;
546         
547         if(!BaseMath_ReadCallback(quat1) || !BaseMath_ReadCallback(quat2))
548                 return NULL;
549
550         for(x = 0; x < 4; x++) {
551                 quat[x] = quat1->quat[x] - quat2->quat[x];
552         }
553
554         return newQuaternionObject(quat, Py_NEW, NULL);
555 }
556 //------------------------obj * obj------------------------------
557 //mulplication
558 static PyObject *Quaternion_mul(PyObject * q1, PyObject * q2)
559 {
560         float quat[4], scalar;
561         QuaternionObject *quat1 = NULL, *quat2 = NULL;
562         VectorObject *vec = NULL;
563
564         if(QuaternionObject_Check(q1)) {
565                 quat1 = (QuaternionObject*)q1;
566                 if(!BaseMath_ReadCallback(quat1))
567                         return NULL;
568         }
569         if(QuaternionObject_Check(q2)) {
570                 quat2 = (QuaternionObject*)q2;
571                 if(!BaseMath_ReadCallback(quat2))
572                         return NULL;
573         }
574
575         if(quat1 && quat2) { /* QUAT*QUAT (dot product) */
576                 return PyFloat_FromDouble(QuatDot(quat1->quat, quat2->quat));
577         }
578         
579         /* the only case this can happen (for a supported type is "FLOAT*QUAT" ) */
580         if(!QuaternionObject_Check(q1)) {
581                 scalar= PyFloat_AsDouble(q1);
582                 if ((scalar == -1.0 && PyErr_Occurred())==0) { /* FLOAT*QUAT */
583                         QUATCOPY(quat, quat2->quat);
584                         QuatMulf(quat, scalar);
585                         return newQuaternionObject(quat, Py_NEW, NULL);
586                 }
587                 PyErr_SetString(PyExc_TypeError, "Quaternion multiplication: val * quat, val is not an acceptable type");
588                 return NULL;
589         }
590         else { /* QUAT*SOMETHING */
591                 if(VectorObject_Check(q2)){  /* QUAT*VEC */
592                         vec = (VectorObject*)q2;
593                         if(vec->size != 3){
594                                 PyErr_SetString(PyExc_TypeError, "Quaternion multiplication: only 3D vector rotations currently supported\n");
595                                 return NULL;
596                         }
597                         return quat_rotation((PyObject*)quat1, (PyObject*)vec); /* vector updating done inside the func */
598                 }
599                 
600                 scalar= PyFloat_AsDouble(q2);
601                 if ((scalar == -1.0 && PyErr_Occurred())==0) { /* QUAT*FLOAT */
602                         QUATCOPY(quat, quat1->quat);
603                         QuatMulf(quat, scalar);
604                         return newQuaternionObject(quat, Py_NEW, NULL);
605                 }
606         }
607         
608         PyErr_SetString(PyExc_TypeError, "Quaternion multiplication: arguments not acceptable for this operation\n");
609         return NULL;
610 }
611
612 //-----------------PROTOCOL DECLARATIONS--------------------------
613 static PySequenceMethods Quaternion_SeqMethods = {
614         (inquiry) Quaternion_len,                                       /* sq_length */
615         (binaryfunc) 0,                                                         /* sq_concat */
616         (ssizeargfunc) 0,                                                               /* sq_repeat */
617         (ssizeargfunc) Quaternion_item,                         /* sq_item */
618         (ssizessizeargfunc) Quaternion_slice,                   /* sq_slice */
619         (ssizeobjargproc) Quaternion_ass_item,          /* sq_ass_item */
620         (ssizessizeobjargproc) Quaternion_ass_slice,    /* sq_ass_slice */
621 };
622
623 static PyNumberMethods Quaternion_NumMethods = {
624                 (binaryfunc)    Quaternion_add, /*nb_add*/
625                 (binaryfunc)    Quaternion_sub, /*nb_subtract*/
626                 (binaryfunc)    Quaternion_mul, /*nb_multiply*/
627                 0,                                                      /*nb_remainder*/
628                 0,                                                      /*nb_divmod*/
629                 0,                                                      /*nb_power*/
630                 (unaryfunc)     0,      /*nb_negative*/
631                 (unaryfunc)     0,      /*tp_positive*/
632                 (unaryfunc)     0,      /*tp_absolute*/
633                 (inquiry)       0,      /*tp_bool*/
634                 (unaryfunc)     0,      /*nb_invert*/
635                 0,                              /*nb_lshift*/
636                 (binaryfunc)0,  /*nb_rshift*/
637                 0,                              /*nb_and*/
638                 0,                              /*nb_xor*/
639                 0,                              /*nb_or*/
640                 0,                              /*nb_int*/
641                 0,                              /*nb_reserved*/
642                 0,                              /*nb_float*/
643                 0,                              /* nb_inplace_add */
644                 0,                              /* nb_inplace_subtract */
645                 0,                              /* nb_inplace_multiply */
646                 0,                              /* nb_inplace_remainder */
647                 0,                              /* nb_inplace_power */
648                 0,                              /* nb_inplace_lshift */
649                 0,                              /* nb_inplace_rshift */
650                 0,                              /* nb_inplace_and */
651                 0,                              /* nb_inplace_xor */
652                 0,                              /* nb_inplace_or */
653                 0,                              /* nb_floor_divide */
654                 0,                              /* nb_true_divide */
655                 0,                              /* nb_inplace_floor_divide */
656                 0,                              /* nb_inplace_true_divide */
657                 0,                              /* nb_index */
658 };
659
660 static PyObject *Quaternion_getAxis( QuaternionObject * self, void *type )
661 {
662         return Quaternion_item(self, GET_INT_FROM_POINTER(type));
663 }
664
665 static int Quaternion_setAxis( QuaternionObject * self, PyObject * value, void * type )
666 {
667         return Quaternion_ass_item(self, GET_INT_FROM_POINTER(type), value);
668 }
669
670 static PyObject *Quaternion_getMagnitude( QuaternionObject * self, void *type )
671 {
672         return PyFloat_FromDouble(sqrt(QuatDot(self->quat, self->quat)));
673 }
674
675 static PyObject *Quaternion_getAngle( QuaternionObject * self, void *type )
676 {
677         double ang = self->quat[0];
678         ang = 2 * (saacos(ang));
679 #ifdef USE_MATHUTILS_DEG
680         ang *= (180 / Py_PI);
681 #endif
682         return PyFloat_FromDouble(ang);
683 }
684
685 static PyObject *Quaternion_getAxisVec( QuaternionObject * self, void *type )
686 {
687         int i;
688         float vec[3];
689         double mag = self->quat[0] * (Py_PI / 180);
690         mag = 2 * (saacos(mag));
691         mag = sin(mag / 2);
692         for(i = 0; i < 3; i++)
693                 vec[i] = (float)(self->quat[i + 1] / mag);
694         
695         Normalize(vec);
696         //If the axis of rotation is 0,0,0 set it to 1,0,0 - for zero-degree rotations
697         if( EXPP_FloatsAreEqual(vec[0], 0.0f, 10) &&
698                 EXPP_FloatsAreEqual(vec[1], 0.0f, 10) &&
699                 EXPP_FloatsAreEqual(vec[2], 0.0f, 10) ){
700                 vec[0] = 1.0f;
701         }
702         return (PyObject *) newVectorObject(vec, 3, Py_NEW, NULL);
703 }
704
705
706 /*****************************************************************************/
707 /* Python attributes get/set structure:                                      */
708 /*****************************************************************************/
709 static PyGetSetDef Quaternion_getseters[] = {
710         {"w",
711          (getter)Quaternion_getAxis, (setter)Quaternion_setAxis,
712          "Quaternion W value",
713          (void *)0},
714         {"x",
715          (getter)Quaternion_getAxis, (setter)Quaternion_setAxis,
716          "Quaternion X axis",
717          (void *)1},
718         {"y",
719          (getter)Quaternion_getAxis, (setter)Quaternion_setAxis,
720          "Quaternion Y axis",
721          (void *)2},
722         {"z",
723          (getter)Quaternion_getAxis, (setter)Quaternion_setAxis,
724          "Quaternion Z axis",
725          (void *)3},
726         {"magnitude",
727          (getter)Quaternion_getMagnitude, (setter)NULL,
728          "Size of the quaternion",
729          NULL},
730         {"angle",
731          (getter)Quaternion_getAngle, (setter)NULL,
732          "angle of the quaternion",
733          NULL},
734         {"axis",
735          (getter)Quaternion_getAxisVec, (setter)NULL,
736          "quaternion axis as a vector",
737          NULL},
738         {"wrapped",
739          (getter)BaseMathObject_getWrapped, (setter)NULL,
740          "True when this wraps blenders internal data",
741          NULL},
742         {"__owner__",
743          (getter)BaseMathObject_getOwner, (setter)NULL,
744          "Read only owner for vectors that depend on another object",
745          NULL},
746
747         {NULL,NULL,NULL,NULL,NULL}  /* Sentinel */
748 };
749
750
751 //------------------PY_OBECT DEFINITION--------------------------
752 PyTypeObject quaternion_Type = {
753         PyVarObject_HEAD_INIT(NULL, 0)
754         "quaternion",                                           //tp_name
755         sizeof(QuaternionObject),                       //tp_basicsize
756         0,                                                              //tp_itemsize
757         (destructor)BaseMathObject_dealloc,             //tp_dealloc
758         0,                                                              //tp_print
759         0,                                                              //tp_getattr
760         0,                                                              //tp_setattr
761         0,                                                              //tp_compare
762         (reprfunc) Quaternion_repr,                     //tp_repr
763         &Quaternion_NumMethods,                         //tp_as_number
764         &Quaternion_SeqMethods,                         //tp_as_sequence
765         0,                                                              //tp_as_mapping
766         0,                                                              //tp_hash
767         0,                                                              //tp_call
768         0,                                                              //tp_str
769         0,                                                              //tp_getattro
770         0,                                                              //tp_setattro
771         0,                                                              //tp_as_buffer
772         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, //tp_flags
773         0,                                                              //tp_doc
774         0,                                                              //tp_traverse
775         0,                                                              //tp_clear
776         (richcmpfunc)Quaternion_richcmpr,       //tp_richcompare
777         0,                                                              //tp_weaklistoffset
778         0,                                                              //tp_iter
779         0,                                                              //tp_iternext
780         Quaternion_methods,                             //tp_methods
781         0,                                                              //tp_members
782         Quaternion_getseters,                   //tp_getset
783         0,                                                              //tp_base
784         0,                                                              //tp_dict
785         0,                                                              //tp_descr_get
786         0,                                                              //tp_descr_set
787         0,                                                              //tp_dictoffset
788         0,                                                              //tp_init
789         0,                                                              //tp_alloc
790         Quaternion_new,                                 //tp_new
791         0,                                                              //tp_free
792         0,                                                              //tp_is_gc
793         0,                                                              //tp_bases
794         0,                                                              //tp_mro
795         0,                                                              //tp_cache
796         0,                                                              //tp_subclasses
797         0,                                                              //tp_weaklist
798         0                                                               //tp_del
799 };
800 //------------------------newQuaternionObject (internal)-------------
801 //creates a new quaternion object
802 /*pass Py_WRAP - if vector is a WRAPPER for data allocated by BLENDER
803  (i.e. it was allocated elsewhere by MEM_mallocN())
804   pass Py_NEW - if vector is not a WRAPPER and managed by PYTHON
805  (i.e. it must be created here with PyMEM_malloc())*/
806 PyObject *newQuaternionObject(float *quat, int type, PyTypeObject *base_type)
807 {
808         QuaternionObject *self;
809         
810         if(base_type)   self = (QuaternionObject *)base_type->tp_alloc(base_type, 0);
811         else                    self = PyObject_NEW(QuaternionObject, &quaternion_Type);
812
813         /* init callbacks as NULL */
814         self->cb_user= NULL;
815         self->cb_type= self->cb_subtype= 0;
816
817         if(type == Py_WRAP){
818                 self->quat = quat;
819                 self->wrapped = Py_WRAP;
820         }else if (type == Py_NEW){
821                 self->quat = PyMem_Malloc(4 * sizeof(float));
822                 if(!quat) { //new empty
823                         QuatOne(self->quat);
824                 }else{
825                         QUATCOPY(self->quat, quat);
826                 }
827                 self->wrapped = Py_NEW;
828         }else{ //bad type
829                 return NULL;
830         }
831         return (PyObject *) self;
832 }
833
834 PyObject *newQuaternionObject_cb(PyObject *cb_user, int cb_type, int cb_subtype)
835 {
836         QuaternionObject *self= (QuaternionObject *)newQuaternionObject(NULL, Py_NEW, NULL);
837         if(self) {
838                 Py_INCREF(cb_user);
839                 self->cb_user=                  cb_user;
840                 self->cb_type=                  (unsigned char)cb_type;
841                 self->cb_subtype=               (unsigned char)cb_subtype;
842         }
843
844         return (PyObject *)self;
845 }