/* * $Id$ * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * * Contributor(s): Willian P. Germano, Joseph Gilbert, Ken Hughes * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #include "vector.h" //doc strings char Vector_Zero_doc[] = "() - set all values in the vector to 0"; char Vector_Normalize_doc[] = "() - normalize the vector"; char Vector_Negate_doc[] = "() - changes vector to it's additive inverse"; char Vector_Resize2D_doc[] = "() - resize a vector to [x,y]"; char Vector_Resize3D_doc[] = "() - resize a vector to [x,y,z]"; char Vector_Resize4D_doc[] = "() - resize a vector to [x,y,z,w]"; //method table struct PyMethodDef Vector_methods[] = { {"zero", ( PyCFunction ) Vector_Zero, METH_NOARGS, Vector_Zero_doc}, {"normalize", ( PyCFunction ) Vector_Normalize, METH_NOARGS, Vector_Normalize_doc}, {"negate", ( PyCFunction ) Vector_Negate, METH_NOARGS, Vector_Negate_doc}, {"resize2D", ( PyCFunction ) Vector_Resize2D, METH_NOARGS, Vector_Resize2D_doc}, {"resize3D", ( PyCFunction ) Vector_Resize3D, METH_NOARGS, Vector_Resize2D_doc}, {"resize4D", ( PyCFunction ) Vector_Resize4D, METH_NOARGS, Vector_Resize2D_doc}, {NULL, NULL, 0, NULL} }; /******prototypes*************/ PyObject *Vector_add( PyObject * v1, PyObject * v2 ); PyObject *Vector_sub( PyObject * v1, PyObject * v2 ); PyObject *Vector_mul( PyObject * v1, PyObject * v2 ); PyObject *Vector_div( PyObject * v1, PyObject * v2 ); int Vector_coerce( PyObject ** v1, PyObject ** v2 ); /*****************************/ // Vector Python Object /*****************************/ //object methods PyObject *Vector_Zero( VectorObject * self ) { int x; for( x = 0; x < self->size; x++ ) { self->vec[x] = 0.0f; } return EXPP_incr_ret( Py_None ); } PyObject *Vector_Normalize( VectorObject * self ) { float norm; int x; norm = 0.0f; for( x = 0; x < self->size; x++ ) { norm += self->vec[x] * self->vec[x]; } norm = ( float ) sqrt( norm ); for( x = 0; x < self->size; x++ ) { self->vec[x] /= norm; } return EXPP_incr_ret( Py_None ); } PyObject *Vector_Negate( VectorObject * self ) { int x; for( x = 0; x < self->size; x++ ) { self->vec[x] = -( self->vec[x] ); } return EXPP_incr_ret( Py_None ); } PyObject *Vector_Resize2D( VectorObject * self ) { float x, y; if( self->size == 4 || self->size == 3 ) { x = self->vec[0]; y = self->vec[1]; PyMem_Free( self->vec ); self->vec = PyMem_Malloc( 2 * sizeof( float ) ); self->vec[0] = x; self->vec[1] = y; self->size = 2; } return EXPP_incr_ret( Py_None ); } PyObject *Vector_Resize3D( VectorObject * self ) { float x, y, z; if( self->size == 2 ) { x = self->vec[0]; y = self->vec[1]; PyMem_Free( self->vec ); self->vec = PyMem_Malloc( 3 * sizeof( float ) ); self->vec[0] = x; self->vec[1] = y; self->vec[2] = 0.0f; self->size = 3; } else if( self->size == 4 ) { x = self->vec[0]; y = self->vec[1]; z = self->vec[2]; PyMem_Free( self->vec ); self->vec = PyMem_Malloc( 3 * sizeof( float ) ); self->vec[0] = x; self->vec[1] = y; self->vec[2] = z; self->size = 3; } return EXPP_incr_ret( Py_None ); } PyObject *Vector_Resize4D( VectorObject * self ) { float x, y, z; if( self->size == 2 ) { x = self->vec[0]; y = self->vec[1]; PyMem_Free( self->vec ); self->vec = PyMem_Malloc( 4 * sizeof( float ) ); self->vec[0] = x; self->vec[1] = y; self->vec[2] = 0.0f; self->vec[3] = 1.0f; self->size = 4; } else if( self->size == 3 ) { x = self->vec[0]; y = self->vec[1]; z = self->vec[2]; PyMem_Free( self->vec ); self->vec = PyMem_Malloc( 4 * sizeof( float ) ); self->vec[0] = x; self->vec[1] = y; self->vec[2] = z; self->vec[3] = 1.0f; self->size = 4; } return EXPP_incr_ret( Py_None ); } static void Vector_dealloc( VectorObject * self ) { /* if we own this memory we must delete it */ if( self->delete_pymem ) PyMem_Free( self->vec ); PyObject_DEL( self ); } static PyObject *Vector_getattr( VectorObject * self, char *name ) { if( self->size == 4 && ELEM4( name[0], 'x', 'y', 'z', 'w' ) && name[1] == 0 ) { if( ( name[0] ) == ( 'w' ) ) { return PyFloat_FromDouble( self->vec[3] ); } else { return PyFloat_FromDouble( self->vec[name[0] - 'x'] ); } } else if( self->size == 3 && ELEM3( name[0], 'x', 'y', 'z' ) && name[1] == 0 ) return PyFloat_FromDouble( self->vec[name[0] - 'x'] ); else if( self->size == 2 && ELEM( name[0], 'x', 'y' ) && name[1] == 0 ) return PyFloat_FromDouble( self->vec[name[0] - 'x'] ); if( ( strcmp( name, "length" ) == 0 ) ) { if( self->size == 4 ) { return PyFloat_FromDouble( sqrt ( self->vec[0] * self->vec[0] + self->vec[1] * self->vec[1] + self->vec[2] * self->vec[2] + self->vec[3] * self->vec[3] ) ); } else if( self->size == 3 ) { return PyFloat_FromDouble( sqrt ( self->vec[0] * self->vec[0] + self->vec[1] * self->vec[1] + self->vec[2] * self->vec[2] ) ); } else if( self->size == 2 ) { return PyFloat_FromDouble( sqrt ( self->vec[0] * self->vec[0] + self->vec[1] * self->vec[1] ) ); } else return EXPP_ReturnPyObjError( PyExc_AttributeError, "can only return the length of a 2D ,3D or 4D vector\n" ); } return Py_FindMethod( Vector_methods, ( PyObject * ) self, name ); } static int Vector_setattr( VectorObject * self, char *name, PyObject * v ) { float val; int valTemp; if( !PyFloat_Check( v ) ) { if( !PyInt_Check( v ) ) { return EXPP_ReturnIntError( PyExc_TypeError, "int or float expected\n" ); } else { if( !PyArg_Parse( v, "i", &valTemp ) ) return EXPP_ReturnIntError( PyExc_TypeError, "unable to parse int argument\n" ); val = ( float ) valTemp; } } else { if( !PyArg_Parse( v, "f", &val ) ) return EXPP_ReturnIntError( PyExc_TypeError, "unable to parse float argument\n" ); } if( self->size == 4 && ELEM4( name[0], 'x', 'y', 'z', 'w' ) && name[1] == 0 ) { if( ( name[0] ) == ( 'w' ) ) { self->vec[3] = val; } else { self->vec[name[0] - 'x'] = val; } } else if( self->size == 3 && ELEM3( name[0], 'x', 'y', 'z' ) && name[1] == 0 ) self->vec[name[0] - 'x'] = val; else if( self->size == 2 && ELEM( name[0], 'x', 'y' ) && name[1] == 0 ) self->vec[name[0] - 'x'] = val; else return -1; return 0; } /* Vectors Sequence methods */ static int Vector_len( VectorObject * self ) { return self->size; } static PyObject *Vector_item( VectorObject * self, int i ) { if( i < 0 || i >= self->size ) return EXPP_ReturnPyObjError( PyExc_IndexError, "array index out of range\n" ); return Py_BuildValue( "f", self->vec[i] ); } static PyObject *Vector_slice( VectorObject * self, int begin, int end ) { PyObject *list; int count; if( begin < 0 ) begin = 0; if( end > self->size ) end = self->size; if( begin > end ) begin = end; list = PyList_New( end - begin ); for( count = begin; count < end; count++ ) { PyList_SetItem( list, count - begin, PyFloat_FromDouble( self->vec[count] ) ); } return list; } static int Vector_ass_item( VectorObject * self, int i, PyObject * ob ) { if( i < 0 || i >= self->size ) return EXPP_ReturnIntError( PyExc_IndexError, "array assignment index out of range\n" ); if( !PyInt_Check( ob ) && !PyFloat_Check( ob ) ) return EXPP_ReturnIntError( PyExc_IndexError, "vector member must be a number\n" ); self->vec[i] = ( float ) PyFloat_AsDouble( ob ); return 0; } static int Vector_ass_slice( VectorObject * self, int begin, int end, PyObject * seq ) { int count, z; if( begin < 0 ) begin = 0; if( end > self->size ) end = self->size; 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( !PyInt_Check( ob ) && !PyFloat_Check( ob ) ) return EXPP_ReturnIntError( PyExc_IndexError, "list member must be a number\n" ); if( !PyArg_Parse( ob, "f", &self->vec[count] ) ) { Py_DECREF( ob ); return -1; } } return 0; } static PyObject *Vector_repr( VectorObject * self ) { int i, maxindex = self->size - 1; char ftoa[24]; PyObject *str1, *str2; str1 = PyString_FromString( "[" ); for( i = 0; i < maxindex; i++ ) { sprintf( ftoa, "%.4f, ", self->vec[i] ); str2 = PyString_FromString( ftoa ); if( !str1 || !str2 ) goto error; PyString_ConcatAndDel( &str1, str2 ); } sprintf( ftoa, "%.4f]", self->vec[maxindex] ); str2 = PyString_FromString( ftoa ); if( !str1 || !str2 ) goto error; PyString_ConcatAndDel( &str1, str2 ); if( str1 ) return str1; error: Py_XDECREF( str1 ); Py_XDECREF( str2 ); return EXPP_ReturnPyObjError( PyExc_MemoryError, "couldn't create PyString!\n" ); } PyObject *Vector_add( PyObject * v1, PyObject * v2 ) { float *vec; int x; PyObject *retval; if( ( !VectorObject_Check( v1 ) ) || ( !VectorObject_Check( v2 ) ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "unsupported type for this operation\n" ); if( ( ( VectorObject * ) v1 )->flag != 0 || ( ( VectorObject * ) v2 )->flag != 0 ) return EXPP_ReturnPyObjError( PyExc_TypeError, "cannot add a scalar to a vector\n" ); if( ( ( VectorObject * ) v1 )->size != ( ( VectorObject * ) v2 )->size ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "vectors must have the same dimensions for this operation\n" ); vec = PyMem_Malloc( ( ( ( VectorObject * ) v1 )->size ) * sizeof( float ) ); for( x = 0; x < ( ( VectorObject * ) v1 )->size; x++ ) { vec[x] = ( ( VectorObject * ) v1 )->vec[x] + ( ( VectorObject * ) v2 )->vec[x]; } retval = ( PyObject * ) newVectorObject( vec, ( ( ( VectorObject * ) v1 )-> size ) ); PyMem_Free( vec ); return retval; } PyObject *Vector_sub( PyObject * v1, PyObject * v2 ) { float *vec; int x; PyObject *retval; if( ( !VectorObject_Check( v1 ) ) || ( !VectorObject_Check( v2 ) ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "unsupported type for this operation\n" ); if( ( ( VectorObject * ) v1 )->flag != 0 || ( ( VectorObject * ) v2 )->flag != 0 ) return EXPP_ReturnPyObjError( PyExc_TypeError, "cannot subtract a scalar from a vector\n" ); if( ( ( VectorObject * ) v1 )->size != ( ( VectorObject * ) v2 )->size ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "vectors must have the same dimensions for this operation\n" ); vec = PyMem_Malloc( ( ( ( VectorObject * ) v1 )->size ) * sizeof( float ) ); for( x = 0; x < ( ( VectorObject * ) v1 )->size; x++ ) { vec[x] = ( ( VectorObject * ) v1 )->vec[x] - ( ( VectorObject * ) v2 )->vec[x]; } retval = ( PyObject * ) newVectorObject( vec, ( ( ( VectorObject * ) v1 )-> size ) ); PyMem_Free( vec ); return retval; } PyObject *Vector_mul( PyObject * v1, PyObject * v2 ) { float *vec; int x; PyObject *retval; if( ( !VectorObject_Check( v1 ) ) || ( !VectorObject_Check( v2 ) ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "unsupported type for this operation\n" ); if( ( ( VectorObject * ) v1 )->flag == 0 && ( ( VectorObject * ) v2 )->flag == 0 ) return EXPP_ReturnPyObjError( PyExc_ArithmeticError, "please use the dot product or the cross product to multiply vectors\n" ); if( ( ( VectorObject * ) v1 )->size != ( ( VectorObject * ) v2 )->size ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "vector dimension error during Vector_mul\n" ); vec = PyMem_Malloc( ( ( ( VectorObject * ) v1 )->size ) * sizeof( float ) ); for( x = 0; x < ( ( VectorObject * ) v1 )->size; x++ ) { vec[x] = ( ( VectorObject * ) v1 )->vec[x] * ( ( VectorObject * ) v2 )->vec[x]; } retval = ( PyObject * ) newVectorObject( vec, ( ( ( VectorObject * ) v1 )-> size ) ); PyMem_Free( vec ); return retval; } PyObject *Vector_div( PyObject * v1, PyObject * v2 ) { float *vec; int x; PyObject *retval; if( ( !VectorObject_Check( v1 ) ) || ( !VectorObject_Check( v2 ) ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, "unsupported type for this operation\n" ); if( ( ( VectorObject * ) v1 )->flag == 0 && ( ( VectorObject * ) v2 )->flag == 0 ) return EXPP_ReturnPyObjError( PyExc_ArithmeticError, "cannot divide two vectors\n" ); if( ( ( VectorObject * ) v1 )->flag != 0 && ( ( VectorObject * ) v2 )->flag == 0 ) return EXPP_ReturnPyObjError( PyExc_TypeError, "cannot divide a scalar by a vector\n" ); if( ( ( VectorObject * ) v1 )->size != ( ( VectorObject * ) v2 )->size ) return EXPP_ReturnPyObjError( PyExc_AttributeError, "vector dimension error during Vector_mul\n" ); vec = PyMem_Malloc( ( ( ( VectorObject * ) v1 )->size ) * sizeof( float ) ); for( x = 0; x < ( ( VectorObject * ) v1 )->size; x++ ) { vec[x] = ( ( VectorObject * ) v1 )->vec[x] / ( ( VectorObject * ) v2 )->vec[x]; } retval = ( PyObject * ) newVectorObject( vec, ( ( ( VectorObject * ) v1 )-> size ) ); PyMem_Free( vec ); return retval; } //coercion of unknown types to type VectorObject for numeric protocols int Vector_coerce( PyObject ** v1, PyObject ** v2 ) { long *tempI; double *tempF; float *vec; int x; if( VectorObject_Check( *v1 ) ) { if( VectorObject_Check( *v2 ) ) { //two vectors Py_INCREF( *v1 ); /* fixme: wahy are we bumping the ref count? */ Py_INCREF( *v2 ); return 0; } else { if( Matrix_CheckPyObject( *v2 ) ) { printf( "vector/matrix numeric protocols unsupported...\n" ); Py_INCREF( *v1 ); return 0; //operation will type check } else if( *v2 == Py_None ) { Py_INCREF(*v1); Py_INCREF(Py_None); return 0; } else if( PyNumber_Check( *v2 ) ) { if( PyInt_Check( *v2 ) ) { //cast scalar to vector tempI = PyMem_Malloc( 1 * sizeof( long ) ); *tempI = PyInt_AsLong( *v2 ); vec = PyMem_Malloc( ( ( ( VectorObject * ) * v1 )->size ) * sizeof( float ) ); for( x = 0; x < ( ( ( VectorObject * ) * v1 )->size ); x++ ) { vec[x] = ( float ) *tempI; } PyMem_Free( tempI ); *v2 = newVectorObject( vec, ( ( ( VectorObject * ) * v1 )->size ) ); ( ( VectorObject * ) * v2 )->flag = 1; //int coercion Py_INCREF( *v1 ); return 0; } else if( PyFloat_Check( *v2 ) ) { //cast scalar to vector tempF = PyMem_Malloc( 1 * sizeof ( double ) ); *tempF = PyFloat_AsDouble( *v2 ); vec = PyMem_Malloc( ( ( ( VectorObject * ) * v1 )->size ) * sizeof( float ) ); for( x = 0; x < ( ( ( VectorObject * ) * v1 )->size ); x++ ) { vec[x] = ( float ) *tempF; } PyMem_Free( tempF ); *v2 = newVectorObject( vec, ( ( ( VectorObject * ) * v1 )->size ) ); ( ( VectorObject * ) * v2 )->flag = 2; //float coercion Py_INCREF( *v1 ); return 0; } } //unknown type or numeric cast failure printf( "attempting vector operation with unsupported type...\n" ); Py_INCREF( *v1 ); Py_INCREF( *v2 ); return 0; //operation will type check } } else { printf( "numeric protocol failure...\n" ); return -1; //this should not occur - fail } return -1; } static PySequenceMethods Vector_SeqMethods = { ( inquiry ) Vector_len, /* sq_length */ ( binaryfunc ) 0, /* sq_concat */ ( intargfunc ) 0, /* sq_repeat */ ( intargfunc ) Vector_item, /* sq_item */ ( intintargfunc ) Vector_slice, /* sq_slice */ ( intobjargproc ) Vector_ass_item, /* sq_ass_item */ ( intintobjargproc ) Vector_ass_slice, /* sq_ass_slice */ }; static PyNumberMethods Vector_NumMethods = { ( binaryfunc ) Vector_add, /* __add__ */ ( binaryfunc ) Vector_sub, /* __sub__ */ ( binaryfunc ) Vector_mul, /* __mul__ */ ( binaryfunc ) Vector_div, /* __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 ) Vector_coerce, /* __coerce__ */ ( unaryfunc ) 0, /* __int__ */ ( unaryfunc ) 0, /* __long__ */ ( unaryfunc ) 0, /* __float__ */ ( unaryfunc ) 0, /* __oct__ */ ( unaryfunc ) 0, /* __hex__ */ }; PyTypeObject vector_Type = { PyObject_HEAD_INIT( NULL ) 0, /*ob_size */ "vector", /*tp_name */ sizeof( VectorObject ), /*tp_basicsize */ 0, /*tp_itemsize */ ( destructor ) Vector_dealloc, /*tp_dealloc */ ( printfunc ) 0, /*tp_print */ ( getattrfunc ) Vector_getattr, /*tp_getattr */ ( setattrfunc ) Vector_setattr, /*tp_setattr */ 0, /*tp_compare */ ( reprfunc ) Vector_repr, /*tp_repr */ &Vector_NumMethods, /*tp_as_number */ &Vector_SeqMethods, /*tp_as_sequence */ }; /* * create a Vector Object( vec, size ) * * Note: Vector now uses copy semantics like STL containers. * Memory for vec member is allocated on python stack. * We own this memory and will free it later. * * size arg is number of floats to alloc. * * if vec arg is NULL * fill our vec with zeros * initialize 4d vectors to zero in homogenous coords. * else * vec param is copied into our local memory and always freed. */ PyObject *newVectorObject( float *vec, int size ) { VectorObject *self; int x; vector_Type.ob_type = &PyType_Type; self = PyObject_NEW( VectorObject, &vector_Type ); self->vec = PyMem_Malloc( size * sizeof( float ) ); self->delete_pymem = 1; /* must free this alloc later */ if( !vec ) { for( x = 0; x < size; x++ ) { self->vec[x] = 0.0f; } if( size == 4 ) /* do the homogenous thing */ self->vec[3] = 1.0f; } else { for( x = 0; x < size; x++ ){ self->vec[x] = vec[x]; } } self->size = size; self->flag = 0; return ( PyObject * ) self; } /* create a Vector that is a proxy for blender data. we do not own this data, we NEVER free it. Note: users must deal with bad pointer issue */ PyObject *newVectorProxy( float *vec, int size) { VectorObject *proxy; proxy = PyObject_NEW( VectorObject, &vector_Type ); proxy->delete_pymem = 0; /* must NOT free this alloc later */ if( !vec || size < 1 ) { return EXPP_ReturnPyObjError( PyExc_AttributeError, "cannot creat zero length vector proxy" ); } proxy->vec = vec; proxy->size = size; proxy->flag = 0; return ( PyObject * ) proxy; }