diff options
author | Joseph Gilbert <ascotan@gmail.com> | 2005-07-14 07:34:56 +0400 |
---|---|---|
committer | Joseph Gilbert <ascotan@gmail.com> | 2005-07-14 07:34:56 +0400 |
commit | b89035906daa352ac8eae4c157c3dd6f61b7ad62 (patch) | |
tree | d3e3312fb8e282c327b6cb4f7b1f780ce299afa2 /source/blender/python/api2_2x/quat.c | |
parent | 1bfd0eae148af20ed97992d44a66f5a6ec34571c (diff) |
Mathutils update
- also included is some fixes for preprocessor inclues and some clean up of the previous commit
-rewrite and bugfixes
----------------------------------
Here's my changelog:
-fixed Rand() so that it doesn't seed everytime and should generate better random numbers
- changed a few error return types to something more appropriate
- clean up of uninitialized variables & removal of unneccessary objects
- NMesh returns wrapped vectors now
- World returns wrapped matrices now
- Object.getEuler() and Object.getBoundingBox() return Wrapped data when data is present
- Object.getMatrix() returns wrapped data if it's worldspace, 'localspace' returns a new matrix
- Vector, Euler, Mat, Quat, call all now internally wrap object without destroying internal datablocks
- Removed memory allocation (unneeded) from all methods
- Vector's resize methods are only applicable to new vectors not wrapped data.
- Matrix(), Quat(), Euler(), Vector() now accepts ANY sequence list, including tuples, list, or a self object to copy - matrices accept multiple sequences
- Fixed Slerp() so that it now works correctly values are clamped between 0 and 1
- Euler.rotate does internal rotation now
- Slice assignment now works better for all types
- Vector * Vector and Quat * Quat are defined and return the DOT product
- Mat * Vec and Vec * Mat are defined now
- Moved #includes to .c file from headers. Also fixed prototypes in mathutils
- Added new helper functions for incref'ing to genutils
- Major cleanup of header files includes - include Mathutils.h for access to math types
- matrix.toQuat() and .toEuler() now fixed take appropriate matrix sizes
- Matrix() with no parameters now returns an identity matrix by default not a zero matrix
- printf() now prints with 6 digits instead of 4
- printf() now prints output with object descriptor
- Matrices now support [x][y] assignment (e.g. matrix[x][y] = 5.4)
- Matrix[index] = value now expectes a sequence not an integer. This will now set a ROW of the matrix through a sequence. index cannot go above the row size of the matrix.
- slice operations on matrices work with sequences now (rows of the matrix) example: mymatrix[0:2] returns a list of 2 wrapped vectors with access to the matrix data.
- slice assignment will no longer modify the data if the assignment operation fails
- fixed error in matrix * scalar multiplication
- euler.toMatrix(), toQuat() no longer causes "creep" from repeated use
- Wrapped data will generate wrapped objects when toEuler(), toQuat(), toMatrix() is used
- Quats can be created with angle/axis, axis/angle
- 4x4 matrices can be multiplied by 3D vectors (by popular demand :))
- vec *quat / quat * vec is now defined
- vec.magnitude alias for vec.length
- all self, internal methods return a pointer to self now so you can do print vector.internalmethod() or vector.internalmethod().nextmethod() (no more print matrix.inverse() returning 'none')
- these methods have been deprecated (still functioning but suggested to use the corrected functionality):
* CopyVec() - replaced by Vector() functionality
* CopyMat() - replaced by Matrix() functionality
* CopyQuat() - replace by Quaternion() functionality
* CopyEuler() - replaced by Euler() functionality
* RotateEuler() - replaced by Euler.rotate() funtionality
* MatMultVec() - replaced by matrix * vector
* VecMultMat() - replaced by vector * matrix
- New struct containers references to python object data or internally allocated blender data for wrapping
* Explaination here: math structs now function as a 'simple wrapper' or a 'py_object' - data that is created on the fly will now be a 'py_object' with its memory managed by python
* otherwise if the data is returned by blender's G.main then the math object is a 'simple wrapper' and data can be accessed directly from the struct just like other python objects.
Diffstat (limited to 'source/blender/python/api2_2x/quat.c')
-rw-r--r-- | source/blender/python/api2_2x/quat.c | 875 |
1 files changed, 450 insertions, 425 deletions
diff --git a/source/blender/python/api2_2x/quat.c b/source/blender/python/api2_2x/quat.c index 35ef90aca2d..78944b9f7d5 100644 --- a/source/blender/python/api2_2x/quat.c +++ b/source/blender/python/api2_2x/quat.c @@ -29,545 +29,570 @@ * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ -#include "quat.h" - -//doc strings -char Quaternion_Identity_doc[] = - "() - set the quaternion to it's identity (1, vector)"; -char Quaternion_Negate_doc[] = - "() - set all values in the quaternion to their negative"; +#include "BLI_arithb.h" +#include "BKE_utildefines.h" +#include "Mathutils.h" +#include "gen_utils.h" +#include "BLI_blenlib.h" + +//-------------------------DOC STRINGS --------------------------- +char Quaternion_Identity_doc[] = "() - set the quaternion to it's identity (1, vector)"; +char Quaternion_Negate_doc[] = "() - set all values in the quaternion to their negative"; char Quaternion_Conjugate_doc[] = "() - set the quaternion to it's conjugate"; char Quaternion_Inverse_doc[] = "() - set the quaternion to it's inverse"; -char Quaternion_Normalize_doc[] = - "() - normalize the vector portion of the quaternion"; -char Quaternion_ToEuler_doc[] = - "() - return a euler rotation representing the quaternion"; -char Quaternion_ToMatrix_doc[] = - "() - return a rotation matrix representing the quaternion"; - -//methods table +char Quaternion_Normalize_doc[] = "() - normalize the vector portion of the quaternion"; +char Quaternion_ToEuler_doc[] = "() - return a euler rotation representing the quaternion"; +char Quaternion_ToMatrix_doc[] = "() - return a rotation matrix representing the quaternion"; +//-----------------------METHOD DEFINITIONS ---------------------- struct PyMethodDef Quaternion_methods[] = { - {"identity", ( PyCFunction ) Quaternion_Identity, METH_NOARGS, - Quaternion_Identity_doc}, - {"negate", ( PyCFunction ) Quaternion_Negate, METH_NOARGS, - Quaternion_Negate_doc}, - {"conjugate", ( PyCFunction ) Quaternion_Conjugate, METH_NOARGS, - Quaternion_Conjugate_doc}, - {"inverse", ( PyCFunction ) Quaternion_Inverse, METH_NOARGS, - Quaternion_Inverse_doc}, - {"normalize", ( PyCFunction ) Quaternion_Normalize, METH_NOARGS, - Quaternion_Normalize_doc}, - {"toEuler", ( PyCFunction ) Quaternion_ToEuler, METH_NOARGS, - Quaternion_ToEuler_doc}, - {"toMatrix", ( PyCFunction ) Quaternion_ToMatrix, METH_NOARGS, - Quaternion_ToMatrix_doc}, + {"identity", (PyCFunction) Quaternion_Identity, METH_NOARGS, Quaternion_Identity_doc}, + {"negate", (PyCFunction) Quaternion_Negate, METH_NOARGS, Quaternion_Negate_doc}, + {"conjugate", (PyCFunction) Quaternion_Conjugate, METH_NOARGS, Quaternion_Conjugate_doc}, + {"inverse", (PyCFunction) Quaternion_Inverse, METH_NOARGS, Quaternion_Inverse_doc}, + {"normalize", (PyCFunction) Quaternion_Normalize, METH_NOARGS, Quaternion_Normalize_doc}, + {"toEuler", (PyCFunction) Quaternion_ToEuler, METH_NOARGS, Quaternion_ToEuler_doc}, + {"toMatrix", (PyCFunction) Quaternion_ToMatrix, METH_NOARGS, Quaternion_ToMatrix_doc}, {NULL, NULL, 0, NULL} }; - -/* ****** prototypes ********** */ -PyObject *Quaternion_add( PyObject * q1, PyObject * q2 ); -PyObject *Quaternion_sub( PyObject * q1, PyObject * q2 ); -PyObject *Quaternion_mul( PyObject * q1, PyObject * q2 ); -int Quaternion_coerce( PyObject ** q1, PyObject ** q2 ); - - -/*****************************/ -// Quaternion Python Object -/*****************************/ - -PyObject *Quaternion_ToEuler( QuaternionObject * self ) +//-----------------------------METHODS------------------------------ +//----------------------------Quaternion.toEuler()------------------ +//return the quat as a euler +PyObject *Quaternion_ToEuler(QuaternionObject * self) { - float *eul; + float eul[3]; int x; - eul = PyMem_Malloc( 3 * sizeof( float ) ); - QuatToEul( self->quat, eul ); - - for( x = 0; x < 3; x++ ) { - eul[x] *= ( float ) ( 180 / Py_PI ); + QuatToEul(self->quat, eul); + for(x = 0; x < 3; x++) { + eul[x] *= (180 / (float)Py_PI); } - return ( PyObject * ) newEulerObject( eul ); + if(self->data.blend_data) + return newEulerObject(eul, Py_WRAP); + else + return newEulerObject(eul, Py_NEW); } - -PyObject *Quaternion_ToMatrix( QuaternionObject * self ) +//----------------------------Quaternion.toMatrix()------------------ +//return the quat as a matrix +PyObject *Quaternion_ToMatrix(QuaternionObject * self) { - float *mat; - - mat = PyMem_Malloc( 3 * 3 * sizeof( float ) ); - QuatToMat3( self->quat, ( float ( * )[3] ) mat ); + float mat[9] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + QuatToMat3(self->quat, (float (*)[3]) mat); - return ( PyObject * ) newMatrixObject( mat, 3, 3 ); + if(self->data.blend_data) + return newMatrixObject(mat, 3, 3, Py_WRAP); + else + return newMatrixObject(mat, 3, 3, Py_NEW); } - +//----------------------------Quaternion.normalize()---------------- //normalize the axis of rotation of [theta,vector] -PyObject *Quaternion_Normalize( QuaternionObject * self ) +PyObject *Quaternion_Normalize(QuaternionObject * self) { - NormalQuat( self->quat ); - return EXPP_incr_ret( Py_None ); + NormalQuat(self->quat); + return (PyObject*)self; } - -PyObject *Quaternion_Inverse( QuaternionObject * self ) +//----------------------------Quaternion.inverse()------------------ +//invert the quat +PyObject *Quaternion_Inverse(QuaternionObject * self) { - float mag = 0.0f; + double mag = 0.0f; int x; - for( x = 1; x < 4; x++ ) { + for(x = 1; x < 4; x++) { self->quat[x] = -self->quat[x]; } - for( x = 0; x < 4; x++ ) { - mag += ( self->quat[x] * self->quat[x] ); + for(x = 0; x < 4; x++) { + mag += (self->quat[x] * self->quat[x]); } - mag = ( float ) sqrt( mag ); - for( x = 0; x < 4; x++ ) { - self->quat[x] /= ( mag * mag ); + mag = sqrt(mag); + for(x = 0; x < 4; x++) { + self->quat[x] /= (mag * mag); } - return EXPP_incr_ret( Py_None ); + return (PyObject*)self; } - -PyObject *Quaternion_Identity( QuaternionObject * self ) +//----------------------------Quaternion.identity()----------------- +//generate the identity quaternion +PyObject *Quaternion_Identity(QuaternionObject * self) { self->quat[0] = 1.0; self->quat[1] = 0.0; self->quat[2] = 0.0; self->quat[3] = 0.0; - return EXPP_incr_ret( Py_None ); + return (PyObject*)self; } - -PyObject *Quaternion_Negate( QuaternionObject * self ) +//----------------------------Quaternion.negate()------------------- +//negate the quat +PyObject *Quaternion_Negate(QuaternionObject * self) { int x; - - for( x = 0; x < 4; x++ ) { + for(x = 0; x < 4; x++) { self->quat[x] = -self->quat[x]; } - return EXPP_incr_ret( Py_None ); + return (PyObject*)self; } - -PyObject *Quaternion_Conjugate( QuaternionObject * self ) +//----------------------------Quaternion.conjugate()---------------- +//negate the vector part +PyObject *Quaternion_Conjugate(QuaternionObject * self) { int x; - - for( x = 1; x < 4; x++ ) { + for(x = 1; x < 4; x++) { self->quat[x] = -self->quat[x]; } - return EXPP_incr_ret( Py_None ); + return (PyObject*)self; } - -static void Quaternion_dealloc( QuaternionObject * self ) +//----------------------------dealloc()(internal) ------------------ +//free the py_object +static void Quaternion_dealloc(QuaternionObject * self) { - PyMem_Free( self->quat ); - PyObject_DEL( self ); + //only free py_data + if(self->data.py_data){ + PyMem_Free(self->data.py_data); + } + PyObject_DEL(self); } - -static PyObject *Quaternion_getattr( QuaternionObject * self, char *name ) +//----------------------------getattr()(internal) ------------------ +//object.attribute access (get) +static PyObject *Quaternion_getattr(QuaternionObject * self, char *name) { - double mag = 0.0f; - float *vec = NULL; int x; - PyObject *retval; - - if( ELEM4( name[0], 'w', 'x', 'y', 'z' ) && name[1] == 0 ) { - return PyFloat_FromDouble( self->quat[name[0] - 'w'] ); + double mag = 0.0f; + float vec[3]; + + if(STREQ(name,"w")){ + return PyFloat_FromDouble(self->quat[0]); + }else if(STREQ(name, "x")){ + return PyFloat_FromDouble(self->quat[1]); + }else if(STREQ(name, "y")){ + return PyFloat_FromDouble(self->quat[2]); + }else if(STREQ(name, "z")){ + return PyFloat_FromDouble(self->quat[3]); } - if( strcmp( name, "magnitude" ) == 0 ) { - for( x = 0; x < 4; x++ ) { + if(STREQ(name, "magnitude")) { + for(x = 0; x < 4; x++) { mag += self->quat[x] * self->quat[x]; } - mag = ( float ) sqrt( mag ); - return PyFloat_FromDouble( mag ); + mag = sqrt(mag); + return PyFloat_FromDouble(mag); } - if( strcmp( name, "angle" ) == 0 ) { - + if(STREQ(name, "angle")) { mag = self->quat[0]; - mag = 2 * ( acos( mag ) ); - mag *= ( 180 / Py_PI ); - return PyFloat_FromDouble( mag ); + mag = 2 * (acos(mag)); + mag *= (180 / Py_PI); + return PyFloat_FromDouble(mag); } - if( strcmp( name, "axis" ) == 0 ) { - - mag = ( double ) ( self->quat[0] * ( Py_PI / 180 ) ); - mag = 2 * ( acos( mag ) ); - mag = sin( mag / 2 ); - vec = PyMem_Malloc( 3 * sizeof( float ) ); - for( x = 0; x < 3; x++ ) { - vec[x] = ( self->quat[x + 1] / ( ( float ) ( mag ) ) ); + if(STREQ(name, "axis")) { + mag = self->quat[0] * (Py_PI / 180); + mag = 2 * (acos(mag)); + mag = sin(mag / 2); + for(x = 0; x < 3; x++) { + vec[x] = (self->quat[x + 1] / mag); } - Normalise( vec ); - retval = ( PyObject * ) newVectorObject( vec, 3 ); - PyMem_Free( vec ); - return retval; + Normalise(vec); + return (PyObject *) newVectorObject(vec, 3, Py_NEW); } - return Py_FindMethod( Quaternion_methods, ( PyObject * ) self, name ); -} -static int Quaternion_setattr( QuaternionObject * self, char *name, - PyObject * v ) + return Py_FindMethod(Quaternion_methods, (PyObject *) self, name); +} +//----------------------------setattr()(internal) ------------------ +//object.attribute access (set) +static int Quaternion_setattr(QuaternionObject * self, char *name, PyObject * q) { - float val; - - if( !PyFloat_Check( v ) && !PyInt_Check( v ) ) { - return EXPP_ReturnIntError( PyExc_TypeError, - "int or float expected\n" ); - } else { - if( !PyArg_Parse( v, "f", &val ) ) - return EXPP_ReturnIntError( PyExc_TypeError, - "unable to parse float argument\n" ); + PyObject *f = NULL; + + f = PyNumber_Float(q); + if(f == NULL) { // parsed item not a number + return EXPP_ReturnIntError(PyExc_TypeError, + "quaternion.attribute = x: argument not a number\n"); + } + + if(STREQ(name,"w")){ + self->quat[0] = PyFloat_AS_DOUBLE(f); + }else if(STREQ(name, "x")){ + self->quat[1] = PyFloat_AS_DOUBLE(f); + }else if(STREQ(name, "y")){ + self->quat[2] = PyFloat_AS_DOUBLE(f); + }else if(STREQ(name, "z")){ + self->quat[3] = PyFloat_AS_DOUBLE(f); + }else{ + Py_DECREF(f); + return EXPP_ReturnIntError(PyExc_AttributeError, + "quaternion.attribute = x: unknown attribute\n"); } - if( ELEM4( name[0], 'w', 'x', 'y', 'z' ) && name[1] == 0 ) { - self->quat[name[0] - 'w'] = val; - } else - return -1; + Py_DECREF(f); return 0; } - -/* Quaternions Sequence methods */ -static PyObject *Quaternion_item( QuaternionObject * self, int i ) +//----------------------------print object (internal)-------------- +//print the object to screen +static PyObject *Quaternion_repr(QuaternionObject * self) { - if( i < 0 || i >= 4 ) - return EXPP_ReturnPyObjError( PyExc_IndexError, - "array index out of range\n" ); + int i; + char buffer[48], str[1024]; + + BLI_strncpy(str,"[",1024); + for(i = 0; i < 4; i++){ + if(i < (3)){ + sprintf(buffer, "%.6f, ", self->quat[i]); + strcat(str,buffer); + }else{ + sprintf(buffer, "%.6f", self->quat[i]); + strcat(str,buffer); + } + } + strcat(str, "](quaternion)"); - return Py_BuildValue( "f", self->quat[i] ); + return EXPP_incr_ret(PyString_FromString(str)); } - -static PyObject *Quaternion_slice( QuaternionObject * self, int begin, - int end ) +//---------------------SEQUENCE PROTOCOLS------------------------ +//----------------------------len(object)------------------------ +//sequence length +static int Quaternion_len(QuaternionObject * self) { - PyObject *list; - int count; - - if( begin < 0 ) - begin = 0; - if( end > 4 ) - end = 4; - if( begin > end ) - begin = end; + return 4; +} +//----------------------------object[]--------------------------- +//sequence accessor (get) +static PyObject *Quaternion_item(QuaternionObject * self, int i) +{ + if(i < 0 || i >= 4) + return EXPP_ReturnPyObjError(PyExc_IndexError, + "quaternion[attribute]: array index out of range\n"); - list = PyList_New( end - begin ); + return Py_BuildValue("f", self->quat[i]); - for( count = begin; count < end; count++ ) { - PyList_SetItem( list, count - begin, - PyFloat_FromDouble( self->quat[count] ) ); - } - return list; } - -static int Quaternion_ass_item( QuaternionObject * self, int i, PyObject * ob ) +//----------------------------object[]------------------------- +//sequence accessor (set) +static int Quaternion_ass_item(QuaternionObject * self, int i, PyObject * ob) { - if( i < 0 || i >= 4 ) - return EXPP_ReturnIntError( PyExc_IndexError, - "array assignment index out of range\n" ); - if( !PyNumber_Check( ob ) ) - return EXPP_ReturnIntError( PyExc_IndexError, - "Quaternion member must be a number\n" ); - - if( !PyFloat_Check( ob ) && !PyInt_Check( ob ) ) { - return EXPP_ReturnIntError( PyExc_TypeError, - "int or float expected\n" ); - } else { - self->quat[i] = ( float ) PyFloat_AsDouble( ob ); + PyObject *f = NULL; + + f = PyNumber_Float(ob); + if(f == NULL) { // parsed item not a number + return EXPP_ReturnIntError(PyExc_TypeError, + "quaternion[attribute] = x: argument not a number\n"); } - return 0; -} -static int Quaternion_ass_slice( QuaternionObject * self, int begin, int end, - PyObject * seq ) -{ - int count, z; - - if( begin < 0 ) - begin = 0; - if( end > 4 ) - end = 4; - if( begin > end ) - begin = end; - - if( !PySequence_Check( seq ) ) - return EXPP_ReturnIntError( PyExc_TypeError, - "illegal argument type for built-in operation\n" ); - if( PySequence_Length( seq ) != ( end - begin ) ) - return EXPP_ReturnIntError( PyExc_TypeError, - "size mismatch in slice assignment\n" ); - - z = 0; - for( count = begin; count < end; count++ ) { - PyObject *ob = PySequence_GetItem( seq, z ); - z++; - - if( !PyFloat_Check( ob ) && !PyInt_Check( ob ) ) { - Py_DECREF( ob ); - return -1; - } else { - if( !PyArg_Parse( ob, "f", &self->quat[count] ) ) { - Py_DECREF( ob ); - return -1; - } - } + if(i < 0 || i >= 4){ + Py_DECREF(f); + return EXPP_ReturnIntError(PyExc_IndexError, + "quaternion[attribute] = x: array assignment index out of range\n"); } + self->quat[i] = PyFloat_AS_DOUBLE(f); + Py_DECREF(f); return 0; } - -static PyObject *Quaternion_repr( QuaternionObject * self ) +//----------------------------object[z:y]------------------------ +//sequence slice (get) +static PyObject *Quaternion_slice(QuaternionObject * self, int begin, int end) { - int i, maxindex = 4 - 1; - char ftoa[24]; - PyObject *str1, *str2; - - str1 = PyString_FromString( "[" ); - - for( i = 0; i < maxindex; i++ ) { - sprintf( ftoa, "%.4f, ", self->quat[i] ); - str2 = PyString_FromString( ftoa ); - if( !str1 || !str2 ) - goto error; - PyString_ConcatAndDel( &str1, str2 ); - } + PyObject *list = NULL; + int count; - sprintf( ftoa, "%.4f]", self->quat[maxindex] ); - str2 = PyString_FromString( ftoa ); - if( !str1 || !str2 ) - goto error; - PyString_ConcatAndDel( &str1, str2 ); + CLAMP(begin, 0, 4); + CLAMP(end, 0, 4); + begin = MIN2(begin,end); - if( str1 ) - return str1; + list = PyList_New(end - begin); + for(count = begin; count < end; count++) { + PyList_SetItem(list, count - begin, + PyFloat_FromDouble(self->quat[count])); + } - error: - Py_XDECREF( str1 ); - Py_XDECREF( str2 ); - return EXPP_ReturnPyObjError( PyExc_MemoryError, - "couldn't create PyString!\n" ); + return list; } - - -PyObject *Quaternion_add( PyObject * q1, PyObject * q2 ) +//----------------------------object[z:y]------------------------ +//sequence slice (set) +static int Quaternion_ass_slice(QuaternionObject * self, int begin, int end, + PyObject * seq) { - float *quat = NULL; - PyObject *retval; - int x; + int i, y, size = 0; + float quat[4]; - if( ( !QuaternionObject_Check( q1 ) ) - || ( !QuaternionObject_Check( q2 ) ) ) - return EXPP_ReturnPyObjError( PyExc_TypeError, - "unsupported type for this operation\n" ); - if( ( ( QuaternionObject * ) q1 )->flag > 0 - || ( ( QuaternionObject * ) q2 )->flag > 0 ) - return EXPP_ReturnPyObjError( PyExc_ArithmeticError, - "cannot add a scalar and a quat\n" ); - - quat = PyMem_Malloc( 4 * sizeof( float ) ); - for( x = 0; x < 4; x++ ) { - quat[x] = - ( ( ( QuaternionObject * ) q1 )->quat[x] ) + - ( ( ( QuaternionObject * ) q2 )->quat[x] ); + CLAMP(begin, 0, 4); + CLAMP(end, 0, 4); + begin = MIN2(begin,end); + + size = PySequence_Length(seq); + if(size != (end - begin)){ + return EXPP_ReturnIntError(PyExc_TypeError, + "quaternion[begin:end] = []: size mismatch in slice assignment\n"); } - retval = ( PyObject * ) newQuaternionObject( quat ); - PyMem_Free( quat ); - return retval; -} + for (i = 0; i < size; i++) { + PyObject *q, *f; -PyObject *Quaternion_sub( PyObject * q1, PyObject * q2 ) + q = PySequence_GetItem(seq, i); + if (q == NULL) { // Failed to read sequence + return EXPP_ReturnIntError(PyExc_RuntimeError, + "quaternion[begin:end] = []: unable to read sequence\n"); + } + f = PyNumber_Float(q); + if(f == NULL) { // parsed item not a number + Py_DECREF(q); + return EXPP_ReturnIntError(PyExc_TypeError, + "quaternion[begin:end] = []: sequence argument not a number\n"); + } + quat[i] = PyFloat_AS_DOUBLE(f); + EXPP_decr2(f,q); + } + //parsed well - now set in vector + for(y = 0; y < size; y++){ + self->quat[begin + y] = quat[y]; + } + return 0; +} +//------------------------NUMERIC PROTOCOLS---------------------- +//------------------------obj + obj------------------------------ +//addition +static PyObject *Quaternion_add(PyObject * q1, PyObject * q2) { - float *quat = NULL; - PyObject *retval; int x; + float quat[4]; + QuaternionObject *quat1 = NULL, *quat2 = NULL; + + EXPP_incr2(q1, q2); + quat1 = (QuaternionObject*)q1; + quat2 = (QuaternionObject*)q2; - if( ( !QuaternionObject_Check( q1 ) ) - || ( !QuaternionObject_Check( q2 ) ) ) - return EXPP_ReturnPyObjError( PyExc_TypeError, - "unsupported type for this operation\n" ); - if( ( ( QuaternionObject * ) q1 )->flag > 0 - || ( ( QuaternionObject * ) q2 )->flag > 0 ) - return EXPP_ReturnPyObjError( PyExc_ArithmeticError, - "cannot subtract a scalar and a quat\n" ); - - quat = PyMem_Malloc( 4 * sizeof( float ) ); - for( x = 0; x < 4; x++ ) { - quat[x] = - ( ( ( QuaternionObject * ) q1 )->quat[x] ) - - ( ( ( QuaternionObject * ) q2 )->quat[x] ); + if(quat1->coerced_object || quat2->coerced_object){ + return EXPP_ReturnPyObjError(PyExc_AttributeError, + "Quaternion addition: arguments not valid for this operation....\n"); + } + for(x = 0; x < 4; x++) { + quat[x] = quat1->quat[x] + quat2->quat[x]; } - retval = ( PyObject * ) newQuaternionObject( quat ); - PyMem_Free( quat ); - return retval; + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return (PyObject *) newQuaternionObject(quat, Py_NEW); } - -PyObject *Quaternion_mul( PyObject * q1, PyObject * q2 ) +//------------------------obj - obj------------------------------ +//subtraction +static PyObject *Quaternion_sub(PyObject * q1, PyObject * q2) { - float *quat = NULL; - PyObject *retval; int x; + float quat[4]; + QuaternionObject *quat1 = NULL, *quat2 = NULL; + + EXPP_incr2(q1, q2); + quat1 = (QuaternionObject*)q1; + quat2 = (QuaternionObject*)q2; - if( ( !QuaternionObject_Check( q1 ) ) - || ( !QuaternionObject_Check( q2 ) ) ) - return EXPP_ReturnPyObjError( PyExc_TypeError, - "unsupported type for this operation\n" ); - if( ( ( QuaternionObject * ) q1 )->flag == 0 - && ( ( QuaternionObject * ) q2 )->flag == 0 ) - return EXPP_ReturnPyObjError( PyExc_ArithmeticError, - "please use the dot or cross product to multiply quaternions\n" ); - - quat = PyMem_Malloc( 4 * sizeof( float ) ); - //scalar mult by quat - for( x = 0; x < 4; x++ ) { - quat[x] = - ( ( QuaternionObject * ) q1 )->quat[x] * - ( ( QuaternionObject * ) q2 )->quat[x]; + if(quat1->coerced_object || quat2->coerced_object){ + return EXPP_ReturnPyObjError(PyExc_AttributeError, + "Quaternion addition: arguments not valid for this operation....\n"); + } + for(x = 0; x < 4; x++) { + quat[x] = quat1->quat[x] - quat2->quat[x]; } - retval = ( PyObject * ) newQuaternionObject( quat ); - PyMem_Free( quat ); - return retval; + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return (PyObject *) newQuaternionObject(quat, Py_NEW); } - -//coercion of unknown types to type QuaternionObject for numeric protocols -int Quaternion_coerce( PyObject ** q1, PyObject ** q2 ) +//------------------------obj * obj------------------------------ +//mulplication +static PyObject *Quaternion_mul(PyObject * q1, PyObject * q2) { - long *tempI = NULL; - double *tempF = NULL; - float *quat = NULL; int x; - - if( QuaternionObject_Check( *q1 ) ) { - if( QuaternionObject_Check( *q2 ) ) { //two Quaternions - Py_INCREF( *q1 ); - Py_INCREF( *q2 ); - return 0; - } else { - if( PyNumber_Check( *q2 ) ) { - if( PyInt_Check( *q2 ) ) { //cast scalar to Quaternion - tempI = PyMem_Malloc( 1 * - sizeof( long ) ); - *tempI = PyInt_AsLong( *q2 ); - quat = PyMem_Malloc( 4 * - sizeof( float ) ); - for( x = 0; x < 4; x++ ) { - quat[x] = ( float ) *tempI; - } - PyMem_Free( tempI ); - *q2 = newQuaternionObject( quat ); - PyMem_Free( quat ); - ( ( QuaternionObject * ) * q2 )->flag = 1; //int coercion - Py_INCREF( *q1 ); /* fixme: is this needed? */ - return 0; - } else if( PyFloat_Check( *q2 ) ) { //cast scalar to Quaternion - tempF = PyMem_Malloc( 1 * - sizeof - ( double ) ); - *tempF = PyFloat_AsDouble( *q2 ); - quat = PyMem_Malloc( 4 * - sizeof( float ) ); - for( x = 0; x < 4; x++ ) { - quat[x] = ( float ) *tempF; - } - PyMem_Free( tempF ); - *q2 = newQuaternionObject( quat ); - PyMem_Free( quat ); - ( ( QuaternionObject * ) * q2 )->flag = 2; //float coercion - Py_INCREF( *q1 ); /* fixme: is this needed? */ - return 0; + float quat[4], scalar, newVec[3]; + double dot = 0.0f; + QuaternionObject *quat1 = NULL, *quat2 = NULL; + PyObject *f = NULL; + VectorObject *vec = NULL; + + EXPP_incr2(q1, q2); + quat1 = (QuaternionObject*)q1; + quat2 = (QuaternionObject*)q2; + + if(quat1->coerced_object){ + if (PyFloat_Check(quat1->coerced_object) || + PyInt_Check(quat1->coerced_object)){ // FLOAT/INT * QUAT + f = PyNumber_Float(quat1->coerced_object); + if(f == NULL) { // parsed item not a number + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return EXPP_ReturnPyObjError(PyExc_TypeError, + "Quaternion multiplication: arguments not acceptable for this operation\n"); + } + scalar = PyFloat_AS_DOUBLE(f); + for(x = 0; x < 4; x++) { + quat[x] = quat2->quat[x] * scalar; + } + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return (PyObject *) newQuaternionObject(quat, Py_NEW); + } + }else{ + if(quat2->coerced_object){ + if (PyFloat_Check(quat2->coerced_object) || + PyInt_Check(quat2->coerced_object)){ // QUAT * FLOAT/INT + f = PyNumber_Float(quat2->coerced_object); + if(f == NULL) { // parsed item not a number + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return EXPP_ReturnPyObjError(PyExc_TypeError, + "Quaternion multiplication: arguments not acceptable for this operation\n"); + } + scalar = PyFloat_AS_DOUBLE(f); + for(x = 0; x < 4; x++) { + quat[x] = quat1->quat[x] * scalar; + } + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return (PyObject *) newQuaternionObject(quat, Py_NEW); + }else if(VectorObject_Check(quat2->coerced_object)){ //QUAT * VEC + vec = (VectorObject*)EXPP_incr_ret(quat2->coerced_object); + if(vec->size != 3){ + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return EXPP_ReturnPyObjError(PyExc_TypeError, + "Quaternion multiplication: only 3D vector rotations currently supported\n"); } + newVec[0] = quat1->quat[0]*quat1->quat[0]*vec->vec[0] + + 2*quat1->quat[2]*quat1->quat[0]*vec->vec[2] - + 2*quat1->quat[3]*quat1->quat[0]*vec->vec[1] + + quat1->quat[1]*quat1->quat[1]*vec->vec[0] + + 2*quat1->quat[2]*quat1->quat[1]*vec->vec[1] + + 2*quat1->quat[3]*quat1->quat[1]*vec->vec[2] - + quat1->quat[3]*quat1->quat[3]*vec->vec[0] - + quat1->quat[2]*quat1->quat[2]*vec->vec[0]; + newVec[1] = 2*quat1->quat[1]*quat1->quat[2]*vec->vec[0] + + quat1->quat[2]*quat1->quat[2]*vec->vec[1] + + 2*quat1->quat[3]*quat1->quat[2]*vec->vec[2] + + 2*quat1->quat[0]*quat1->quat[3]*vec->vec[0] - + quat1->quat[3]*quat1->quat[3]*vec->vec[1] + + quat1->quat[0]*quat1->quat[0]*vec->vec[1] - + 2*quat1->quat[1]*quat1->quat[0]*vec->vec[2] - + quat1->quat[1]*quat1->quat[1]*vec->vec[1]; + newVec[2] = 2*quat1->quat[1]*quat1->quat[3]*vec->vec[0] + + 2*quat1->quat[2]*quat1->quat[3]*vec->vec[1] + + quat1->quat[3]*quat1->quat[3]*vec->vec[2] - + 2*quat1->quat[0]*quat1->quat[2]*vec->vec[0] - + quat1->quat[2]*quat1->quat[2]*vec->vec[2] + + 2*quat1->quat[0]*quat1->quat[1]*vec->vec[1] - + quat1->quat[1]*quat1->quat[1]*vec->vec[2] + + quat1->quat[0]*quat1->quat[0]*vec->vec[2]; + EXPP_decr3((PyObject*)quat1, (PyObject*)quat2, (PyObject*)vec); + return newVectorObject(newVec,3,Py_NEW); } - //unknown type or numeric cast failure - printf( "attempting quaternion operation with unsupported type...\n" ); - Py_INCREF( *q1 ); /* fixme: is this needed? */ - return 0; //operation will type check + }else{ //QUAT * QUAT (dot product) + for(x = 0; x < 4; x++) { + dot += quat1->quat[x] * quat1->quat[x]; + } + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return PyFloat_FromDouble(dot); } - } else { - printf( "numeric protocol failure...\n" ); - return -1; //this should not occur - fail } - return -1; -} + EXPP_decr2((PyObject*)quat1, (PyObject*)quat2); + return EXPP_ReturnPyObjError(PyExc_TypeError, + "Quaternion multiplication: arguments not acceptable for this operation\n"); +} +//------------------------coerce(obj, obj)----------------------- +//coercion of unknown types to type QuaternionObject for numeric protocols +/*Coercion() is called whenever a math operation has 2 operands that + it doesn't understand how to evaluate. 2+Matrix for example. We want to + evaluate some of these operations like: (vector * 2), however, for math + to proceed, the unknown operand must be cast to a type that python math will + understand. (e.g. in the case above case, 2 must be cast to a vector and + then call vector.multiply(vector, scalar_cast_as_vector)*/ +static int Quaternion_coerce(PyObject ** q1, PyObject ** q2) +{ + PyObject *coerced = NULL; + + if(!QuaternionObject_Check(*q2)) { + if(VectorObject_Check(*q2) || PyFloat_Check(*q2) || PyInt_Check(*q2)) { + coerced = EXPP_incr_ret(*q2); + *q2 = newQuaternionObject(NULL,Py_NEW); + ((QuaternionObject*)*q2)->coerced_object = coerced; + }else{ + return EXPP_ReturnIntError(PyExc_TypeError, + "quaternion.coerce(): unknown operand - can't coerce for numeric protocols\n"); + } + } + EXPP_incr2(*q1, *q2); + return 0; +} +//-----------------PROTCOL DECLARATIONS-------------------------- static PySequenceMethods Quaternion_SeqMethods = { - ( inquiry ) 0, /* sq_length */ - ( binaryfunc ) 0, /* sq_concat */ - ( intargfunc ) 0, /* sq_repeat */ - ( intargfunc ) Quaternion_item, /* sq_item */ - ( intintargfunc ) Quaternion_slice, /* sq_slice */ - ( intobjargproc ) Quaternion_ass_item, /* sq_ass_item */ - ( intintobjargproc ) Quaternion_ass_slice, /* sq_ass_slice */ + (inquiry) Quaternion_len, /* sq_length */ + (binaryfunc) 0, /* sq_concat */ + (intargfunc) 0, /* sq_repeat */ + (intargfunc) Quaternion_item, /* sq_item */ + (intintargfunc) Quaternion_slice, /* sq_slice */ + (intobjargproc) Quaternion_ass_item, /* sq_ass_item */ + (intintobjargproc) Quaternion_ass_slice, /* sq_ass_slice */ }; - static PyNumberMethods Quaternion_NumMethods = { - ( binaryfunc ) Quaternion_add, /* __add__ */ - ( binaryfunc ) Quaternion_sub, /* __sub__ */ - ( binaryfunc ) Quaternion_mul, /* __mul__ */ - ( binaryfunc ) 0, /* __div__ */ - ( binaryfunc ) 0, /* __mod__ */ - ( binaryfunc ) 0, /* __divmod__ */ - ( ternaryfunc ) 0, /* __pow__ */ - ( unaryfunc ) 0, /* __neg__ */ - ( unaryfunc ) 0, /* __pos__ */ - ( unaryfunc ) 0, /* __abs__ */ - ( inquiry ) 0, /* __nonzero__ */ - ( unaryfunc ) 0, /* __invert__ */ - ( binaryfunc ) 0, /* __lshift__ */ - ( binaryfunc ) 0, /* __rshift__ */ - ( binaryfunc ) 0, /* __and__ */ - ( binaryfunc ) 0, /* __xor__ */ - ( binaryfunc ) 0, /* __or__ */ - ( coercion ) Quaternion_coerce, /* __coerce__ */ - ( unaryfunc ) 0, /* __int__ */ - ( unaryfunc ) 0, /* __long__ */ - ( unaryfunc ) 0, /* __float__ */ - ( unaryfunc ) 0, /* __oct__ */ - ( unaryfunc ) 0, /* __hex__ */ + (binaryfunc) Quaternion_add, /* __add__ */ + (binaryfunc) Quaternion_sub, /* __sub__ */ + (binaryfunc) Quaternion_mul, /* __mul__ */ + (binaryfunc) 0, /* __div__ */ + (binaryfunc) 0, /* __mod__ */ + (binaryfunc) 0, /* __divmod__ */ + (ternaryfunc) 0, /* __pow__ */ + (unaryfunc) 0, /* __neg__ */ + (unaryfunc) 0, /* __pos__ */ + (unaryfunc) 0, /* __abs__ */ + (inquiry) 0, /* __nonzero__ */ + (unaryfunc) 0, /* __invert__ */ + (binaryfunc) 0, /* __lshift__ */ + (binaryfunc) 0, /* __rshift__ */ + (binaryfunc) 0, /* __and__ */ + (binaryfunc) 0, /* __xor__ */ + (binaryfunc) 0, /* __or__ */ + (coercion) Quaternion_coerce, /* __coerce__ */ + (unaryfunc) 0, /* __int__ */ + (unaryfunc) 0, /* __long__ */ + (unaryfunc) 0, /* __float__ */ + (unaryfunc) 0, /* __oct__ */ + (unaryfunc) 0, /* __hex__ */ }; - +//------------------PY_OBECT DEFINITION-------------------------- PyTypeObject quaternion_Type = { - PyObject_HEAD_INIT( NULL ) - 0, /*ob_size */ - "quaternion", /*tp_name */ - sizeof( QuaternionObject ), /*tp_basicsize */ - 0, /*tp_itemsize */ - ( destructor ) Quaternion_dealloc, /*tp_dealloc */ - ( printfunc ) 0, /*tp_print */ - ( getattrfunc ) Quaternion_getattr, /*tp_getattr */ - ( setattrfunc ) Quaternion_setattr, /*tp_setattr */ - 0, /*tp_compare */ - ( reprfunc ) Quaternion_repr, /*tp_repr */ - &Quaternion_NumMethods, /*tp_as_number */ - &Quaternion_SeqMethods, /*tp_as_sequence */ + PyObject_HEAD_INIT(NULL) + 0, /*ob_size */ + "quaternion", /*tp_name */ + sizeof(QuaternionObject), /*tp_basicsize */ + 0, /*tp_itemsize */ + (destructor) Quaternion_dealloc, /*tp_dealloc */ + (printfunc) 0, /*tp_print */ + (getattrfunc) Quaternion_getattr, /*tp_getattr */ + (setattrfunc) Quaternion_setattr, /*tp_setattr */ + 0, /*tp_compare */ + (reprfunc) Quaternion_repr, /*tp_repr */ + &Quaternion_NumMethods, /*tp_as_number */ + &Quaternion_SeqMethods, /*tp_as_sequence */ }; - -/** Creates a new quaternion object. - * - * Memory for a new quaternion is allocated. The quaternion copies the given - * list of parameters or initializes to the identity, if a <code>NULL</code> - * pointer is given as parameter. The memory will be freed in the dealloc - * routine. - * - * @param quat Pointer to a list of floats for the quanternion parameters w, x, y, z. - * @return Quaternion Python object. - * @see Quaternion_Identity - */ -PyObject *newQuaternionObject( float *quat ) +//------------------------newQuaternionObject (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 *newQuaternionObject(float *quat, int type) { QuaternionObject *self; int x; quaternion_Type.ob_type = &PyType_Type; - - self = PyObject_NEW( QuaternionObject, &quaternion_Type ); - - self->quat = PyMem_Malloc( 4 * sizeof( float ) ); - - if( !quat ) { - Quaternion_Identity(self); - } else { - for( x = 0; x < 4; x++ ) { - self->quat[x] = quat[x]; + self = PyObject_NEW(QuaternionObject, &quaternion_Type); + self->data.blend_data = NULL; + self->data.py_data = NULL; + self->coerced_object = NULL; + + if(type == Py_WRAP){ + self->data.blend_data = quat; + self->quat = self->data.blend_data; + }else if (type == Py_NEW){ + self->data.py_data = PyMem_Malloc(4 * sizeof(float)); + self->quat = self->data.py_data; + if(!quat) { //new empty + Quaternion_Identity(self); + }else{ + for(x = 0; x < 4; x++){ + self->quat[x] = quat[x]; + } } + }else{ //bad type + return NULL; } - self->flag = 0; - - return ( PyObject * ) self; + return (PyObject *) EXPP_incr_ret((PyObject *)self); } |