Mercurial > pygame
view src/math.c @ 1849:301bc61643a9 math_module
merged changes from trunk
| author | lorenz |
|---|---|
| date | Mon, 16 Nov 2009 18:31:40 +0000 |
| parents | bbc21cd6faeb |
| children |
line wrap: on
line source
/* pygame - Python Game Library This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define PYGAMEAPI_MATH_INTERNAL #include "pygame.h" #include "pygamedocs.h" #include "structmember.h" #include "pgcompat.h" #include <float.h> #include <math.h> #define VECTOR_MAX_SIZE (4) #define STRING_BUF_SIZE (100) #define SWIZZLE_ERR_NO_ERR 0 #define SWIZZLE_ERR_DOUBLE_IDX 1 #define SWIZZLE_ERR_EXTRACTION_ERR 2 #define RETURN_ERROR 0 #define RETURN_NO_ERROR 1 #define OP_ADD 1 #define OP_IADD 2 #define OP_SUB 3 #define OP_ISUB 4 #define OP_MUL 5 #define OP_IMUL 6 #define OP_DIV 7 #define OP_IDIV 8 #define OP_FLOOR_DIV 9 #define OP_IFLOOR_DIV 10 #define OP_MOD 11 #define OP_ARG_REVERSE 32 #define OP_ARG_UNKNOWN 64 #define OP_ARG_VECTOR 128 #define OP_ARG_NUMBER 256 static PyTypeObject PyVector2_Type; static PyTypeObject PyVector3_Type; static PyTypeObject PyVectorElementwiseProxy_Type; static PyTypeObject PyVectorIter_Type; static PyTypeObject PyVector_SlerpIter_Type; #define PyVector2_Check(x) ((x)->ob_type == &PyVector2_Type) #define PyVector3_Check(x) ((x)->ob_type == &PyVector3_Type) #define PyVector_Check(x) (PyVector2_Check(x) || PyVector3_Check(x)) #define vector_elementwiseproxy_Check(x) \ ((x)->ob_type == &PyVectorElementwiseProxy_Type) #define DEG2RAD(angle) ((angle) * M_PI / 180.) #define RAD2DEG(angle) ((angle) * 180. / M_PI) typedef struct { PyObject_HEAD double *coords; /* Coordinates */ unsigned int dim; /* Dimension of the vector */ double epsilon; /* Small value for comparisons */ } PyVector; typedef struct { PyObject_HEAD long it_index; PyVector *vec; } vectoriter; typedef struct { PyObject_HEAD long it_index; long steps; long dim; double coords[VECTOR_MAX_SIZE]; double matrix[VECTOR_MAX_SIZE][VECTOR_MAX_SIZE]; double radial_factor; } vector_slerpiter; typedef struct { PyObject_HEAD long it_index; long steps; long dim; double coords[VECTOR_MAX_SIZE]; double step_vec[VECTOR_MAX_SIZE]; } vector_lerpiter; typedef struct { PyObject_HEAD PyVector *vec; } vector_elementwiseproxy; /* further forward declerations */ /* generic helper functions */ static int RealNumber_Check(PyObject *obj); static double PySequence_GetItem_AsDouble(PyObject *seq, Py_ssize_t index); static int PySequence_AsVectorCoords(PyObject *seq, double *coords, const size_t size); static int PyVectorCompatible_Check(PyObject *obj, int dim); static double _scalar_product(const double *coords1, const double *coords2, int size); static void _make_vector2_slerp_matrix(vector_slerpiter *it, const double *vec1_coords, const double *vec2_coords, double angle); static void _make_vector3_slerp_matrix(vector_slerpiter *it, const double *vec1_coords, const double *vec2_coords, double angle); /* generic vector functions */ static PyObject *PyVector_NEW(int dim); static void vector_dealloc(PyVector* self); static PyObject *vector_generic_math(PyObject *o1, PyObject *o2, int op); static PyObject *vector_add(PyObject *o1, PyObject *o2); static PyObject *vector_sub(PyObject *o1, PyObject *o2); static PyObject *vector_mul(PyObject *o1, PyObject *o2); static PyObject *vector_div(PyVector *self, PyObject *other); static PyObject *vector_floor_div(PyVector *self, PyObject *other); static PyObject *vector_inplace_add(PyVector *self, PyObject *other); static PyObject *vector_inplace_sub(PyVector *self, PyObject *other); static PyObject *vector_inplace_mul(PyVector *self, PyObject *other); static PyObject *vector_inplace_div(PyVector *self, PyObject *other); static PyObject *vector_inplace_floor_div(PyVector *self, PyObject *other); static PyObject *vector_neg(PyVector *self); static PyObject *vector_pos(PyVector *self); static int vector_nonzero(PyVector *self); static Py_ssize_t vector_len(PyVector *self); static PyObject *vector_GetItem(PyVector *self, Py_ssize_t index); static int vector_SetItem(PyVector *self, Py_ssize_t index, PyObject *value); static PyObject *vector_GetSlice(PyVector *self, Py_ssize_t ilow, Py_ssize_t ihigh); static int vector_SetSlice(PyVector *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v); static PyObject *vector_getx (PyVector *self, void *closure); static PyObject *vector_gety (PyVector *self, void *closure); static PyObject *vector_getz (PyVector *self, void *closure); static PyObject *vector_getw (PyVector *self, void *closure); static int vector_setx (PyVector *self, PyObject *value, void *closure); static int vector_sety (PyVector *self, PyObject *value, void *closure); static int vector_setz (PyVector *self, PyObject *value, void *closure); static int vector_setw (PyVector *self, PyObject *value, void *closure); static PyObject *vector_richcompare(PyVector *self, PyObject *other, int op); static PyObject *vector_length(PyVector *self); static PyObject *vector_length_squared(PyVector *self); static PyObject *vector_normalize(PyVector *self); static PyObject *vector_normalize_ip(PyVector *self); static PyObject *vector_dot(PyVector *self, PyObject *other); static PyObject *vector_scale_to_length(PyVector *self, PyObject *length); static int _vector_reflect_helper(double *dst_coords, const double *src_coords, PyObject *normal, int dim, double epsilon); static PyObject *vector_reflect(PyVector *self, PyObject *normal); static PyObject *vector_reflect_ip(PyVector *self, PyObject *normal); static PyObject *vector_distance_to(PyVector *self, PyObject *other); static PyObject *vector_distance_squared_to(PyVector *self, PyObject *other); static PyObject *vector_getAttr_swizzle(PyVector *self, PyObject *attr_name); static int vector_setAttr_swizzle(PyVector *self, PyObject *attr_name, PyObject *val); static PyObject *vector_elementwise(PyVector *self); static PyObject *vector_slerp(PyVector *self, PyObject *args); static PyObject *vector_lerp(PyVector *self, PyObject *args); static PyObject *vector_repr(PyVector *self); static PyObject *vector_str(PyVector *self); /* static Py_ssize_t vector_readbuffer(PyVector *self, Py_ssize_t segment, void **ptrptr); static Py_ssize_t vector_writebuffer(PyVector *self, Py_ssize_t segment, void **ptrptr); static Py_ssize_t vector_segcount(PyVector *self, Py_ssize_t *lenp); */ /* vector2 specific functions */ static PyObject *vector2_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static int vector2_init(PyVector *self, PyObject *args, PyObject *kwds); static void _vector2_do_rotate(double *dst_coords, const double *src_coords, double angle, double epsilon); static PyObject *vector2_rotate(PyVector *self, PyObject *args); static PyObject *vector2_rotate_ip(PyVector *self, PyObject *args); static PyObject *vector2_cross(PyVector *self, PyObject *other); static PyObject *vector2_angle_to(PyVector *self, PyObject *other); static PyObject *vector2_as_polar(PyVector *self); static PyObject *vector2_from_polar(PyVector *self, PyObject *args); /* vector3 specific functions */ static PyObject *vector3_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static int vector3_init(PyVector *self, PyObject *args, PyObject *kwds); static void _vector3_do_rotate(double *dst_coords, const double *src_coords, const double *axis_coords, double angle, double epsilon); static PyObject *vector3_rotate(PyVector *self, PyObject *args); static PyObject *vector3_rotate_ip(PyVector *self, PyObject *args); static PyObject *vector3_cross(PyVector *self, PyObject *other); static PyObject *vector3_angle_to(PyVector *self, PyObject *other); static PyObject *vector3_as_spherical(PyVector *self); static PyObject *vector3_from_spherical(PyVector *self, PyObject *args); /* vector iterator functions */ static void vectoriter_dealloc(vectoriter *it); static PyObject *vectoriter_next(vectoriter *it); static PyObject *vectoriter_len(vectoriter *it); static PyObject *vector_iter(PyObject *vec); /* elementwiseproxy */ static void vector_elementwiseproxy_dealloc(vector_elementwiseproxy *it); static PyObject *vector_elementwiseproxy_richcompare(PyObject *o1, PyObject *o2, int op); static PyObject *vector_elementwiseproxy_generic_math(PyObject *o1, PyObject *o2, int op); static PyObject *vector_elementwiseproxy_add(PyObject *o1, PyObject *o2); static PyObject *vector_elementwiseproxy_sub(PyObject *o1, PyObject *o2); static PyObject *vector_elementwiseproxy_mul(PyObject *o1, PyObject *o2); static PyObject *vector_elementwiseproxy_div(PyObject *o1, PyObject *o2); static PyObject *vector_elementwiseproxy_floor_div(PyObject *o1, PyObject *o2); static PyObject *vector_elementwiseproxy_pow(PyObject *baseObj, PyObject *expoObj, PyObject *mod); static PyObject *vector_elementwiseproxy_mod(PyObject *o1, PyObject *o2); static PyObject *vector_elementwiseproxy_abs(vector_elementwiseproxy *self); static PyObject *vector_elementwiseproxy_neg(vector_elementwiseproxy *self); static PyObject *vector_elementwiseproxy_pos(vector_elementwiseproxy *self); static int vector_elementwiseproxy_nonzero(vector_elementwiseproxy *self); static PyObject *vector_elementwise(PyVector *vec); static int swizzling_enabled = 0; /******************************** * Global helper functions ********************************/ static int RealNumber_Check(PyObject *obj) { if (PyNumber_Check(obj) && !PyComplex_Check(obj)) return 1; return 0; } static double PySequence_GetItem_AsDouble(PyObject *seq, Py_ssize_t index) { PyObject *item, *fltobj; double value; item = PySequence_GetItem(seq, index); if (item == NULL) { PyErr_SetString(PyExc_TypeError, "a sequence is expected"); return -1; } fltobj = PyNumber_Float(item); Py_DECREF(item); if (!fltobj) { PyErr_SetString(PyExc_TypeError, "a float is required"); return -1; } value = PyFloat_AsDouble(fltobj); Py_DECREF(fltobj); return value; } static int PySequence_AsVectorCoords(PyObject *seq, double *coords, const size_t size) { int i; if (PyVector_Check(seq)) { memcpy(coords, ((PyVector *)seq)->coords, sizeof(double) * size); return 1; } if (!PySequence_Check(seq) || PySequence_Length(seq) != size) { coords = NULL; return 0; } for (i = 0; i < size; ++i) { coords[i] = PySequence_GetItem_AsDouble(seq, i); if (PyErr_Occurred()) { coords = NULL; return 0; } } return 1; } static int PyVectorCompatible_Check(PyObject *obj, int dim) { int i; PyObject *tmp; switch(dim) { case 2: if (PyVector2_Check(obj)) { return 1; } break; case 3: if (PyVector3_Check(obj)) { return 1; } break; /* case 4: if (PyVector4_Check(obj)) { return 1; } break; */ default: PyErr_SetString(PyExc_SystemError, "Wrong internal call to PyVectorCompatible_Check."); return 0; } if (!PySequence_Check(obj) || (PySequence_Length(obj) != dim)) { return 0; } for (i = 0; i < dim; ++i) { tmp = PySequence_GetItem(obj, i); if (!RealNumber_Check(tmp)) { Py_DECREF(tmp); return 0; } Py_DECREF(tmp); } return 1; } static double _scalar_product(const double *coords1, const double *coords2, int size) { int i; double product = 0; for (i = 0; i < size; ++i) product += coords1[i] * coords2[i]; return product; } static PyMemberDef vector_members[] = { {"epsilon", T_DOUBLE, offsetof(PyVector, epsilon), 0, "small value used in comparisons"}, {NULL} /* Sentinel */ }; static PyObject* PyVector_NEW(int dim) { PyVector *vec; switch (dim) { case 2: vec = PyObject_New(PyVector, &PyVector2_Type); break; case 3: vec = PyObject_New(PyVector, &PyVector3_Type); break; /* case 4: vec = PyObject_New(PyVector, &PyVector4_Type); break; */ default: PyErr_SetString(PyExc_SystemError, "Wrong internal call to PyVector_NEW.\n"); return NULL; } if (vec != NULL) { vec->dim = dim; vec->epsilon = FLT_EPSILON; vec->coords = PyMem_New(double, dim); if (vec->coords == NULL) { Py_DECREF(vec); return PyErr_NoMemory(); } } return (PyObject *)vec; } static void vector_dealloc(PyVector* self) { PyMem_Del(self->coords); self->ob_type->tp_free((PyObject*)self); } /********************************************** * Generic vector PyNumber emulation routines **********************************************/ static PyObject * vector_generic_math(PyObject *o1, PyObject *o2, int op) { int i, dim; double *vec_coords; double tmp; PyObject *other; PyVector *vec, *ret; if (PyVector_Check(o1)) { vec = (PyVector *)o1; other = o2; } else { vec = (PyVector *)o2; other = o1; op |= OP_ARG_REVERSE; } dim = vec->dim; vec_coords = vec->coords; if (PyVectorCompatible_Check(other, dim)) op |= OP_ARG_VECTOR; else if (RealNumber_Check(other)) op |= OP_ARG_NUMBER; else op |= OP_ARG_UNKNOWN; switch (op) { case OP_ADD | OP_ARG_VECTOR: case OP_ADD | OP_ARG_VECTOR | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) { ret->coords[i] = (vec_coords[i] + PySequence_GetItem_AsDouble(other, i)); } return (PyObject*)ret; case OP_IADD | OP_ARG_VECTOR: for (i = 0; i < dim; i++) vec_coords[i] += PySequence_GetItem_AsDouble(other, i); return (PyObject*)vec; case OP_SUB | OP_ARG_VECTOR: ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) { ret->coords[i] = (vec_coords[i] - PySequence_GetItem_AsDouble(other, i)); } return (PyObject*)ret; case OP_SUB | OP_ARG_VECTOR | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) { ret->coords[i] = (PySequence_GetItem_AsDouble(other, i) - vec_coords[i]); } return (PyObject*)ret; case OP_ISUB | OP_ARG_VECTOR: for (i = 0; i < dim; i++) vec_coords[i] -= PySequence_GetItem_AsDouble(other, i); return (PyObject*)vec; case OP_MUL | OP_ARG_VECTOR: case OP_MUL | OP_ARG_VECTOR | OP_ARG_REVERSE: tmp = 0.; for (i = 0; i < dim; i++) tmp += vec_coords[i] * PySequence_GetItem_AsDouble(other, i); return PyFloat_FromDouble(tmp); case OP_MUL | OP_ARG_NUMBER: case OP_MUL | OP_ARG_NUMBER | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(dim); tmp = PyFloat_AsDouble(other); for (i = 0; i < dim; i++) ret->coords[i] = vec_coords[i] * tmp; return (PyObject*)ret; case OP_IMUL | OP_ARG_NUMBER: tmp = PyFloat_AsDouble(other); for (i = 0; i < dim; i++) vec_coords[i] *= tmp; return (PyObject*)vec; case OP_DIV | OP_ARG_NUMBER: tmp = 1. / PyFloat_AsDouble(other); ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) ret->coords[i] = vec_coords[i] * tmp; return (PyObject*)ret; case OP_IDIV | OP_ARG_NUMBER: tmp = 1. / PyFloat_AsDouble(other); for (i = 0; i < dim; i++) vec_coords[i] *= tmp; return (PyObject*)vec; case OP_FLOOR_DIV | OP_ARG_NUMBER: tmp = 1. / PyFloat_AsDouble(other); ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) ret->coords[i] = floor(vec_coords[i] * tmp); return (PyObject*)ret; case OP_IFLOOR_DIV | OP_ARG_NUMBER: tmp = 1. / PyFloat_AsDouble(other); for (i = 0; i < dim; i++) vec->coords[i] = floor(vec_coords[i] * tmp); return (PyObject*)vec; default: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } } static PyObject * vector_add(PyObject *o1, PyObject *o2) { return vector_generic_math(o1, o2, OP_ADD); } static PyObject * vector_inplace_add(PyVector *o1, PyObject *o2) { return vector_generic_math((PyObject*)o1, o2, OP_IADD); } static PyObject * vector_sub(PyObject *o1, PyObject *o2) { return vector_generic_math(o1, o2, OP_SUB); } static PyObject * vector_inplace_sub(PyVector *o1, PyObject *o2) { return vector_generic_math((PyObject*)o1, o2, OP_ISUB); } static PyObject * vector_mul(PyObject *o1, PyObject *o2) { return vector_generic_math(o1, o2, OP_MUL); } static PyObject * vector_inplace_mul(PyVector *o1, PyObject *o2) { return vector_generic_math((PyObject*)o1, o2, OP_IMUL); } static PyObject * vector_div(PyVector *o1, PyObject *o2) { return vector_generic_math((PyObject*)o1, o2, OP_DIV); } static PyObject * vector_inplace_div(PyVector *o1, PyObject *o2) { return vector_generic_math((PyObject*)o1, o2, OP_IDIV); } static PyObject * vector_floor_div(PyVector *o1, PyObject *o2) { return vector_generic_math((PyObject*)o1, o2, OP_FLOOR_DIV); } static PyObject * vector_inplace_floor_div(PyVector *o1, PyObject *o2) { return vector_generic_math((PyObject*)o1, o2, OP_IFLOOR_DIV); } static PyObject * vector_neg(PyVector *self) { int i; PyVector *ret = (PyVector*)PyVector_NEW(self->dim); for (i = 0; i < self->dim; i++) { ret->coords[i] = -self->coords[i]; } return (PyObject*)ret; } static PyObject * vector_pos(PyVector *self) { int i; PyVector *ret = (PyVector*)PyVector_NEW(self->dim); memcpy(ret->coords, self->coords, sizeof(ret->coords[0]) * ret->dim); return (PyObject*)ret; } static int vector_nonzero(PyVector *self) { int i; for (i = 0; i < self->dim; i++) { if (fabs(self->coords[i]) > self->epsilon) { return 1; } } return 0; } static PyNumberMethods vector_as_number = { (binaryfunc)vector_add, /* nb_add; __add__ */ (binaryfunc)vector_sub, /* nb_subtract; __sub__ */ (binaryfunc)vector_mul, /* nb_multiply; __mul__ */ (binaryfunc)vector_div, /* nb_divide; __div__ */ (binaryfunc)0, /* nb_remainder; __mod__ */ (binaryfunc)0, /* nb_divmod; __divmod__ */ (ternaryfunc)0, /* nb_power; __pow__ */ (unaryfunc)vector_neg, /* nb_negative; __neg__ */ (unaryfunc)vector_pos, /* nb_positive; __pos__ */ (unaryfunc)0, /* nb_absolute; __abs__ */ (inquiry)vector_nonzero, /* nb_nonzero; __nonzero__ */ (unaryfunc)0, /* nb_invert; __invert__ */ (binaryfunc)0, /* nb_lshift; __lshift__ */ (binaryfunc)0, /* nb_rshift; __rshift__ */ (binaryfunc)0, /* nb_and; __and__ */ (binaryfunc)0, /* nb_xor; __xor__ */ (binaryfunc)0, /* nb_or; __or__ */ (coercion)0, /* nb_coerce; __coerce__ */ (unaryfunc)0, /* nb_int; __int__ */ (unaryfunc)0, /* nb_long; __long__ */ (unaryfunc)0, /* nb_float; __float__ */ (unaryfunc)0, /* nb_oct; __oct__ */ (unaryfunc)0, /* nb_hex; __hex__ */ /* Added in release 2.0 */ (binaryfunc)vector_inplace_add, /* nb_inplace_add; __iadd__ */ (binaryfunc)vector_inplace_sub, /* nb_inplace_subtract; __isub__ */ (binaryfunc)vector_inplace_mul, /* nb_inplace_multiply; __imul__ */ (binaryfunc)vector_inplace_div, /* nb_inplace_divide; __idiv__ */ (binaryfunc)0, /* nb_inplace_remainder; __imod__ */ (ternaryfunc)0, /* nb_inplace_power; __pow__ */ (binaryfunc)0, /* nb_inplace_lshift; __ilshift__ */ (binaryfunc)0, /* nb_inplace_rshift; __irshift__ */ (binaryfunc)0, /* nb_inplace_and; __iand__ */ (binaryfunc)0, /* nb_inplace_xor; __ixor__ */ (binaryfunc)0, /* nb_inplace_or; __ior__ */ /* Added in release 2.2 */ (binaryfunc)vector_floor_div, /* nb_floor_divide; __floor__ */ (binaryfunc)vector_div, /* nb_true_divide; __truediv__ */ (binaryfunc)vector_inplace_floor_div, /* nb_inplace_floor_divide; __ifloor__ */ (binaryfunc)vector_inplace_div, /* nb_inplace_true_divide; __itruediv__ */ /* Added in release 2.5 */ (unaryfunc)0, /* nb_index; __index__ */ }; /************************************************* * Generic vector PySequence emulation routines *************************************************/ static Py_ssize_t vector_len(PyVector *self) { return (Py_ssize_t)self->dim; } static PyObject * vector_GetItem(PyVector *self, Py_ssize_t index) { if (index < 0 || index >= self->dim) { PyErr_SetString(PyExc_IndexError, "subscript out of range."); return NULL; } return PyFloat_FromDouble(self->coords[index]); } static int vector_SetItem(PyVector *self, Py_ssize_t index, PyObject *value) { if (index < 0 || index >= self->dim) { PyErr_SetString(PyExc_IndexError, "subscript out of range."); return -1; } self->coords[index] = PyFloat_AsDouble(value); return 0; } static PyObject * vector_GetSlice(PyVector *self, Py_ssize_t ilow, Py_ssize_t ihigh) { /* some code was taken from the CPython source listobject.c */ PyListObject *slice; Py_ssize_t i, len; double *src; PyObject **dest; /* make sure boundaries are sane */ if (ilow < 0) ilow = 0; else if (ilow > self->dim) ilow = self->dim; if (ihigh < ilow) ihigh = ilow; else if (ihigh > self->dim) ihigh = self->dim; len = ihigh - ilow; slice = (PyListObject *) PyList_New(len); if (slice == NULL) return NULL; src = self->coords + ilow; dest = slice->ob_item; for (i = 0; i < len; i++) { dest[i] = PyFloat_FromDouble(src[i]); } return (PyObject *)slice; } static int vector_SetSlice(PyVector *self, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v) { Py_ssize_t i, len; if (ilow < 0) ilow = 0; else if (ilow > self->dim) ilow = self->dim; if (ihigh < ilow) ihigh = ilow; else if (ihigh > self->dim) ihigh = self->dim; len = ihigh - ilow; if (len != PySequence_Length(v)) { PyErr_SetString(PyExc_ValueError, "Cannot assign slice of different length."); return -1; } /* TODO: better error checking. for example if v is not a sequence or doesn't numbers. */ for (i = 0; i < len; ++i) { self->coords[i + ilow] = PySequence_GetItem_AsDouble(v, i); } return 0; } static PySequenceMethods vector_as_sequence = { (lenfunc)vector_len, /* sq_length; __len__ */ (binaryfunc)0, /* sq_concat; __add__ */ (ssizeargfunc)0, /* sq_repeat; __mul__ */ (ssizeargfunc)vector_GetItem, /* sq_item; __getitem__ */ (ssizessizeargfunc)vector_GetSlice, /* sq_slice; __getslice__ */ (ssizeobjargproc)vector_SetItem, /* sq_ass_item; __setitem__ */ (ssizessizeobjargproc)vector_SetSlice, /* sq_ass_slice; __setslice__ */ }; static PyObject* vector_getx (PyVector *self, void *closure) { return PyFloat_FromDouble(self->coords[0]); } static int vector_setx (PyVector *self, PyObject *value, void *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the x attribute"); return -1; } self->coords[0] = PyFloat_AsDouble(value); return 0; } static PyObject* vector_gety (PyVector *self, void *closure) { return PyFloat_FromDouble(self->coords[1]); } static int vector_sety (PyVector *self, PyObject *value, void *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the y attribute"); return -1; } self->coords[1] = PyFloat_AsDouble(value); return 0; } static PyObject* vector_getz (PyVector *self, void *closure) { return PyFloat_FromDouble(self->coords[2]); } static int vector_setz (PyVector *self, PyObject *value, void *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the z attribute"); return -1; } self->coords[2] = PyFloat_AsDouble(value); return 0; } static PyObject* vector_getw (PyVector *self, void *closure) { return PyFloat_FromDouble(self->coords[3]); } static int vector_setw (PyVector *self, PyObject *value, void *closure) { if (value == NULL) { PyErr_SetString(PyExc_TypeError, "Cannot delete the w attribute"); return -1; } self->coords[3] = PyFloat_AsDouble(value); return 0; } static PyObject * vector_richcompare(PyVector *self, PyObject *other, int op) { int i; double diff; if (!PyVectorCompatible_Check(other, self->dim)) { if (op == Py_EQ) Py_RETURN_FALSE; else if (op == Py_NE) Py_RETURN_TRUE; else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } } switch(op) { case Py_EQ: for (i = 0; i < self->dim; i++) { diff = self->coords[i] - PySequence_GetItem_AsDouble(other, i); // test diff != diff to catch NaN if ((diff != diff) || (fabs(diff) >= self->epsilon)) { Py_RETURN_FALSE; } } Py_RETURN_TRUE; case Py_NE: for (i = 0; i < self->dim; i++) { diff = self->coords[i] - PySequence_GetItem_AsDouble(other, i); if ((diff != diff) || (fabs(diff) >= self->epsilon)) { Py_RETURN_TRUE; } } Py_RETURN_FALSE; default: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } } static PyObject * vector_length(PyVector *self) { double length_squared = _scalar_product(self->coords, self->coords, self->dim); return PyFloat_FromDouble(sqrt(length_squared)); } static PyObject * vector_length_squared(PyVector *self) { double length_squared = _scalar_product(self->coords, self->coords, self->dim); return PyFloat_FromDouble(length_squared); } static PyObject * vector_normalize(PyVector *self) { int i; double length; PyVector *ret; length = sqrt(_scalar_product(self->coords, self->coords, self->dim)); if (length == 0) { PyErr_SetString(PyExc_ZeroDivisionError, "Can't normalize Vector of length Zero"); return NULL; } ret = (PyVector*)PyVector_NEW(self->dim); for (i = 0; i < self->dim; ++i) ret->coords[i] = self->coords[i] / length; return (PyObject *)ret; } static PyObject * vector_normalize_ip(PyVector *self) { int i; double length; length = sqrt(_scalar_product(self->coords, self->coords, self->dim)); if (length == 0) { PyErr_SetString(PyExc_ZeroDivisionError, "Can't normalize Vector of length Zero"); return NULL; } for (i = 0; i < self->dim; ++i) self->coords[i] /= length; Py_RETURN_NONE; } static PyObject * vector_is_normalized(PyVector *self) { double length_squared = _scalar_product(self->coords, self->coords, self->dim); if (fabs(length_squared - 1) < self->epsilon) Py_RETURN_TRUE; else Py_RETURN_FALSE; } static PyObject * vector_dot(PyVector *self, PyObject *other) { double other_coords[VECTOR_MAX_SIZE]; if (!PySequence_AsVectorCoords(other, other_coords, self->dim)) { PyErr_SetString(PyExc_TypeError, "Cannot perform dot product with this type."); return NULL; } return PyFloat_FromDouble(_scalar_product(self->coords, other_coords, self->dim)); } static PyObject * vector_scale_to_length(PyVector *self, PyObject *length) { int i; double new_length, old_length; double fraction; if (!RealNumber_Check(length)) { PyErr_SetString(PyExc_ValueError, "new length must be a number"); return NULL; } new_length = PyFloat_AsDouble(length); old_length = 0; for (i = 0; i < self->dim; ++i) old_length += self->coords[i] * self->coords[i]; old_length = sqrt(old_length); if (old_length < self->epsilon) { PyErr_SetString(PyExc_ZeroDivisionError, "Cannot scale a vector with zero length"); return NULL; } fraction = new_length / old_length; for (i = 0; i < self->dim; ++i) self->coords[i] *= fraction; Py_RETURN_NONE; } static int _vector_reflect_helper(double *dst_coords, const double *src_coords, PyObject *normal, int dim, double epsilon) { int i; double dot_product; double norm_length; /* allocate enough space for 2, 3 and 4 dim vectors */ double norm_coords[VECTOR_MAX_SIZE]; if (!PyVectorCompatible_Check(normal, dim)) { PyErr_SetString(PyExc_TypeError, "Expected a vector."); return RETURN_ERROR; } /* normalize the normal */ norm_length = 0; for (i = 0; i < dim; ++i) { norm_coords[i] = PySequence_GetItem_AsDouble(normal, i); norm_length += norm_coords[i] * norm_coords[i]; } if (norm_length < epsilon) { PyErr_SetString(PyExc_ZeroDivisionError, "Normal must not be of length zero."); return RETURN_ERROR;; } if (norm_length != 1) { norm_length = sqrt(norm_length); for (i = 0; i < dim; ++i) norm_coords[i] /= norm_length; } /* calculate the dot_product for the projection */ dot_product = 0; for (i = 0; i < dim; ++i) dot_product += src_coords[i] * norm_coords[i]; for (i = 0; i < dim; ++i) dst_coords[i] = src_coords[i] - 2 * norm_coords[i] * dot_product; return RETURN_NO_ERROR; } static PyObject * vector_reflect(PyVector *self, PyObject *normal) { int error_code; PyVector *ret = (PyVector *)PyVector_NEW(self->dim); error_code = _vector_reflect_helper(ret->coords, self->coords, normal, self->dim, self->epsilon); if (error_code == RETURN_ERROR) return NULL; return (PyObject *)ret; } static PyObject * vector_reflect_ip(PyVector *self, PyObject *normal) { int error_code; double tmp_coords[VECTOR_MAX_SIZE]; error_code = _vector_reflect_helper(tmp_coords, self->coords, normal, self->dim, self->epsilon); if (error_code == RETURN_ERROR) return NULL; memcpy(self->coords, tmp_coords, self->dim * sizeof(tmp_coords[0])); Py_RETURN_NONE; } static PyObject * vector_distance_to(PyVector *self, PyObject *other) { int i; double distance_squared; distance_squared = 0; for (i = 0; i < self->dim; ++i) { double tmp = PySequence_GetItem_AsDouble(other, i) - self->coords[i]; distance_squared += tmp * tmp; } return PyFloat_FromDouble(sqrt(distance_squared)); } static PyObject * vector_distance_squared_to(PyVector *self, PyObject *other) { int i; double distance_squared; distance_squared = 0; for (i = 0; i < self->dim; ++i) { double tmp = PySequence_GetItem_AsDouble(other, i) - self->coords[i]; distance_squared += tmp * tmp; } return PyFloat_FromDouble(distance_squared); } static PyObject * vector_repr(PyVector *self) { int i; int bufferIdx; char buffer[2][STRING_BUF_SIZE]; bufferIdx = 1; PyOS_snprintf(buffer[0], STRING_BUF_SIZE, "<Vector%d(", self->dim); for (i = 0; i < self->dim - 1; ++i) { PyOS_snprintf(buffer[bufferIdx % 2], STRING_BUF_SIZE, "%s%g, ", buffer[(bufferIdx + 1) % 2], self->coords[i]); bufferIdx++; } PyOS_snprintf(buffer[bufferIdx % 2], STRING_BUF_SIZE, "%s%g)>", buffer[(bufferIdx + 1) % 2], self->coords[i]); return PyString_FromString(buffer[bufferIdx % 2]); } static PyObject * vector_str(PyVector *self) { int i; int bufferIdx; char buffer[2][STRING_BUF_SIZE]; bufferIdx = 1; PyOS_snprintf(buffer[0], STRING_BUF_SIZE, "[", self->dim); for (i = 0; i < self->dim - 1; ++i) { PyOS_snprintf(buffer[bufferIdx % 2], STRING_BUF_SIZE, "%s%g, ", buffer[(bufferIdx + 1) % 2], self->coords[i]); bufferIdx++; } PyOS_snprintf(buffer[bufferIdx % 2], STRING_BUF_SIZE, "%s%g]", buffer[(bufferIdx + 1) % 2], self->coords[i]); return PyString_FromString(buffer[bufferIdx % 2]); } static PyObject* vector_getAttr_swizzle(PyVector *self, PyObject *attr_name) { PyObject *res = PyObject_GenericGetAttr((PyObject*)self, attr_name); /* if normal lookup failed try to swizzle */ if (swizzling_enabled && PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_AttributeError)) { Py_ssize_t len = PySequence_Length(attr_name); const char *attr = PyString_AsString(attr_name); double *coords = self->coords; Py_ssize_t i; res = (PyObject*)PyTuple_New(len); for (i = 0; i < len; i++) { switch (attr[i]) { case 'x': PyTuple_SetItem(res, i, PyFloat_FromDouble(coords[0])); break; case 'y': PyTuple_SetItem(res, i, PyFloat_FromDouble(coords[1])); break; default: /* swizzling failed! clean up and return NULL * the exception from PyObject_GenericGetAttr is still set */ Py_DECREF(res); return NULL; } } /* swizzling succeeded! clear the error and return result */ PyErr_Clear(); } return res; } static int vector_setAttr_swizzle(PyVector *self, PyObject *attr_name, PyObject *val) { const char *attr = PyString_AsString(attr_name); Py_ssize_t len = PySequence_Length(attr_name); double entry[self->dim]; int entry_was_set[self->dim]; int swizzle_err = SWIZZLE_ERR_NO_ERR; int i; /* if swizzling is disabled always default to generic implementation */ if (!swizzling_enabled) return PyObject_GenericSetAttr((PyObject*)self, attr_name, val); /* if swizzling is enabled first try swizzle */ for (i = 0; i < self->dim; ++i) entry_was_set[i] = 0; for (i = 0; i < len; ++i) { int idx; switch (attr[i]) { case 'x': case 'y': case 'z': idx = attr[i] - 'x'; break; case 'w': idx = 3; break; default: /* swizzle failed. attempt generic attribute setting */ return PyObject_GenericSetAttr((PyObject*)self, attr_name, val); } if (idx >= self->dim) /* swizzle failed. attempt generic attribute setting */ return PyObject_GenericSetAttr((PyObject*)self, attr_name, val); if (entry_was_set[idx]) swizzle_err = SWIZZLE_ERR_DOUBLE_IDX; if (swizzle_err == SWIZZLE_ERR_NO_ERR) { entry_was_set[idx] = 1; entry[idx] = PySequence_GetItem_AsDouble(val, i); if (PyErr_Occurred()) swizzle_err = SWIZZLE_ERR_EXTRACTION_ERR; } } switch (swizzle_err) { case SWIZZLE_ERR_NO_ERR: /* swizzle successful */ for (i = 0; i < self->dim; ++i) if (entry_was_set[i]) self->coords[i] = entry[i]; return 0; case SWIZZLE_ERR_DOUBLE_IDX: PyErr_SetString(PyExc_AttributeError, "Attribute assignment conflicts with swizzling."); return -1; case SWIZZLE_ERR_EXTRACTION_ERR: /* exception was set by PySequence_GetItem_AsDouble */ return -1; default: /* this should NEVER happen and means a bug in the code */ PyErr_SetString(PyExc_RuntimeError, "Unhandled error in swizzle code"); return -1; } } /* static Py_ssize_t vector_readbuffer(PyVector *self, Py_ssize_t segment, void **ptrptr) { if (segment != 0) { PyErr_SetString(PyExc_SystemError, "accessing non-existent vector segment"); return -1; } *ptrptr = self->coords; return self->dim; } static Py_ssize_t vector_writebuffer(PyVector *self, Py_ssize_t segment, void **ptrptr) { if (segment != 0) { PyErr_SetString(PyExc_SystemError, "accessing non-existent vector segment"); return -1; } *ptrptr = self->coords; return self->dim; } static Py_ssize_t vector_segcount(PyVector *self, Py_ssize_t *lenp) { if (lenp) { *lenp = self->dim * sizeof(self->coords[0]); } return 1; } static int vector_getbuffer(PyVector *self, Py_buffer *view, int flags) { int ret; void *ptr; if (view == NULL) { self->ob_exports++; return 0; } ptr = self->coords; ret = PyBuffer_FillInfo(view, (PyObject*)self, ptr, Py_SIZE(self), 0, flags); if (ret >= 0) { obj->ob_exports++; } return ret; } static void vector_releasebuffer(PyVector *self, Py_buffer *view) { self->ob_exports--; } static PyBufferProcs vector_as_buffer = { (readbufferproc)vector_readbuffer, (writebufferproc)vector_writebuffer, (segcountproc)vector_segcount, (charbufferproc)0, (getbufferproc)vector_getbuffer, (releasebufferproc)vector_releasebuffer, }; */ /********************************************************************* * vector2 specific functions *********************************************************************/ static PyObject * vector2_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyVector *vec = (PyVector *)type->tp_alloc(type, 0); if (vec != NULL) { vec->dim = 2; vec->epsilon = FLT_EPSILON; vec->coords = PyMem_New(double, vec->dim); if (vec->coords == NULL) { vec->ob_type->tp_free((PyObject*)vec); return NULL; } } return (PyObject *)vec; } static int vector2_init(PyVector *self, PyObject *args, PyObject *kwds) { PyObject *xOrSequence=NULL, *y=NULL, *z=NULL; static char *kwlist[] = {"x", "y", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OO:Vector2", kwlist, &xOrSequence, &y)) return -1; if (xOrSequence) { if (RealNumber_Check(xOrSequence)) { self->coords[0] = PyFloat_AsDouble(xOrSequence); } else if (PyVectorCompatible_Check(xOrSequence, self->dim)) { self->coords[0] = PySequence_GetItem_AsDouble(xOrSequence, 0); self->coords[1] = PySequence_GetItem_AsDouble(xOrSequence, 1); /* successful initialization from sequence type */ return 0; } else if (PyString_Check(xOrSequence)) { /* This should make "Vector2(Vector2().__repr__())" possible */ char buffer[STRING_BUF_SIZE]; char *endptr; char *str = PyString_AsString(xOrSequence); if (strncmp(str, "<Vector2(", strlen("<Vector2(")) != 0) goto error; str += strlen("<Vector2("); self->coords[0] = PyOS_ascii_strtod(str, &endptr); if (endptr == str) { goto error; } str = endptr + strlen(", "); self->coords[1] = PyOS_ascii_strtod(str, &endptr); if (endptr == str) goto error; /* successful conversion from string */ return 0; } else { goto error; } } else { self->coords[0] = 0.; } if (y) { if (RealNumber_Check(y)) { self->coords[1] = PyFloat_AsDouble(y); } else { goto error; } } else { self->coords[1] = 0.; } /* success initialization */ return 0; error: PyErr_SetString(PyExc_ValueError, "Vector2 must be initialized with 2 real numbers or a sequence of 2 real numbers"); return -1; } static void _vector2_do_rotate(double *dst_coords, const double *src_coords, double angle, double epsilon) { /* make sure angle is in range [0, 360) */ angle = fmod(angle, 360.); if (angle < 0) angle += 360.; /* special-case rotation by 0, 90, 180 and 270 degrees */ if (fmod(angle + epsilon, 90.) < 2 * epsilon) { switch ((int)((angle + epsilon) / 90)) { case 0: /* 0 degrees */ dst_coords[0] = src_coords[0]; dst_coords[1] = src_coords[1]; break; case 1: /* 90 degrees */ dst_coords[0] = -src_coords[1]; dst_coords[1] = src_coords[0]; break; case 2: /* 180 degrees */ dst_coords[0] = -src_coords[0]; dst_coords[1] = -src_coords[1]; break; case 3: /* 270 degrees */ dst_coords[0] = src_coords[1]; dst_coords[1] = -src_coords[0]; break; default: /* this should NEVER happen and means a bug in the code */ PyErr_SetString(PyExc_RuntimeError, "Please report this bug in vector2_do_rotate to the developers"); break; } } else { double sinValue, cosValue; angle = DEG2RAD(angle); sinValue = sin(angle); cosValue = cos(angle); dst_coords[0] = cosValue * src_coords[0] - sinValue * src_coords[1]; dst_coords[1] = sinValue * src_coords[0] + cosValue * src_coords[1]; } } static PyObject * vector2_rotate(PyVector *self, PyObject *args) { double angle; PyVector *ret; if (!PyArg_ParseTuple(args, "d:rotate", &angle)) { return NULL; } ret = (PyVector*)PyVector_NEW(self->dim); _vector2_do_rotate(ret->coords, self->coords, angle, self->epsilon); return (PyObject*)ret; } static PyObject * vector2_rotate_ip(PyVector *self, PyObject *args) { double angle; double tmp[2]; if (!PyArg_ParseTuple(args, "d:rotate_ip", &angle)) { return NULL; } tmp[0] = self->coords[0]; tmp[1] = self->coords[1]; _vector2_do_rotate(self->coords, tmp, angle, self->epsilon); Py_RETURN_NONE; } static PyObject * vector2_cross(PyVector *self, PyObject *other) { if (!PyVectorCompatible_Check(other, self->dim)) { PyErr_SetString(PyExc_TypeError, "cannot calculate cross Product"); return NULL; } if (PyVector_Check(other)) { return PyFloat_FromDouble((self->coords[0] * ((PyVector *)other)->coords[1]) - (self->coords[1] * ((PyVector *)other)->coords[0])); } else { return PyFloat_FromDouble((self->coords[0] * PySequence_GetItem_AsDouble(other, 1)) - (self->coords[1] * PySequence_GetItem_AsDouble(other, 0))); } } static PyObject * vector2_angle_to(PyVector *self, PyObject *other) { double angle; if (!PyVectorCompatible_Check(other, self->dim)) { PyErr_SetString(PyExc_TypeError, "expected an vector."); return NULL; } angle = (atan2(PySequence_GetItem_AsDouble(other, 1), PySequence_GetItem_AsDouble(other, 0)) - atan2(self->coords[1], self->coords[0])); return PyFloat_FromDouble(RAD2DEG(angle)); } static PyObject * vector2_as_polar(PyVector *self) { double r, phi; r = sqrt(_scalar_product(self->coords, self->coords, self->dim)); phi = atan2(self->coords[1], self->coords[0]); return Py_BuildValue("(dd)", r, phi); } static PyObject * vector2_from_polar(PyVector *self, PyObject *args) { PyObject *polar_obj, *r_obj, *phi_obj; double r, phi; if (!PyArg_ParseTuple(args, "O:Vector2.from_polar", &polar_obj)) { return NULL; } if (!PySequence_Check(polar_obj) || PySequence_Length(polar_obj) != 2) { PyErr_SetString(PyExc_TypeError, "2-tuple containing r and phi is expected."); return NULL; } r_obj = PySequence_GetItem(polar_obj, 0); phi_obj = PySequence_GetItem(polar_obj, 1); if (!PyNumber_Check(r_obj) || !PyNumber_Check(phi_obj)) { PyErr_SetString(PyExc_TypeError, "expected numerical values"); Py_DECREF(r_obj); Py_DECREF(phi_obj); return NULL; } r = PyFloat_AsDouble(r_obj); phi = PyFloat_AsDouble(phi_obj); Py_DECREF(r_obj); Py_DECREF(phi_obj); self->coords[0] = r * cos(phi); self->coords[1] = r * sin(phi); Py_RETURN_NONE; } static PyMethodDef vector2_methods[] = { {"length", (PyCFunction)vector_length, METH_NOARGS, "returns the length/magnitude of the vector." }, {"length_squared", (PyCFunction)vector_length_squared, METH_NOARGS, "returns the length/magnitude of the vector." }, {"rotate", (PyCFunction)vector2_rotate, METH_VARARGS, "returns a new vector rotated counterclockwise by the angle given in degrees." }, {"rotate_ip", (PyCFunction)vector2_rotate_ip, METH_VARARGS, "rotates the vector counterclockwise by the angle given in degrees." }, {"slerp", (PyCFunction)vector_slerp, METH_VARARGS, "Interpolates spherically to a given vector in a given number of steps." }, {"lerp", (PyCFunction)vector_lerp, METH_VARARGS, "Interpolates linear to a given vector in a given number of steps." }, {"normalize", (PyCFunction)vector_normalize, METH_NOARGS, "returns a vector that has length == 1 and the same direction as self." }, {"normalize_ip", (PyCFunction)vector_normalize_ip, METH_NOARGS, "Normalizes the vector so that it has length == 1." }, {"is_normalized", (PyCFunction)vector_is_normalized, METH_NOARGS, "returns True if the vector has length == 1. otherwise it returns False." }, {"cross", (PyCFunction)vector2_cross, METH_O, "calculates the cross product." }, {"dot", (PyCFunction)vector_dot, METH_O, "calculates the dot product." }, {"angle_to", (PyCFunction)vector2_angle_to, METH_O, "returns the angle between self and the given vector." }, {"scale_to_length", (PyCFunction)vector_scale_to_length, METH_O, "scales the vector to the given length." }, {"reflect", (PyCFunction)vector_reflect, METH_O, "reflects the vector on the surface characterized by the given normal." }, {"reflect_ip", (PyCFunction)vector_reflect_ip, METH_O, "reflects the vector in-place on the surface characterized by the given normal." }, {"distance_to", (PyCFunction)vector_distance_to, METH_O, "returns the distance to the given vector." }, {"distance_squared_to", (PyCFunction)vector_distance_squared_to, METH_O, "returns the squared distance to the given vector." }, {"elementwise", (PyCFunction)vector_elementwise, METH_NOARGS, "applies the following operation to each element of the vector." }, {"as_polar", (PyCFunction)vector2_as_polar, METH_NOARGS, "returns a 2-tuple (r, phi) where r is the radial distance and phi is the angle to the positive x-axis." }, {"from_polar", (PyCFunction)vector2_from_polar, METH_VARARGS, "sets x and y from a 2-tuple (r, phi) where r is the radial distance and phi is the angle to the positive x-axis." }, {NULL} /* Sentinel */ }; static PyGetSetDef vector2_getsets[] = { { "x", (getter)vector_getx, (setter)vector_setx, NULL, NULL }, { "y", (getter)vector_gety, (setter)vector_sety, NULL, NULL }, { NULL, 0, NULL, NULL, NULL } /* Sentinel */ }; /******************************** * PyVector2 type definition ********************************/ static PyTypeObject PyVector2_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pygame.math.Vector2", /* tp_name */ sizeof(PyVector), /* tp_basicsize */ 0, /* tp_itemsize */ /* Methods to implement standard operations */ (destructor)vector_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)vector_repr, /* tp_repr */ /* Method suites for standard classes */ &vector_as_number, /* tp_as_number */ &vector_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ /* More standard operations (here for binary compatibility) */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc)vector_str, /* tp_str */ (getattrofunc)vector_getAttr_swizzle, /* tp_getattro */ (setattrofunc)vector_setAttr_swizzle, /* tp_setattro */ /* Functions to access object as input/output buffer */ 0, /* tp_as_buffer */ /* Flags to define presence of optional/expanded features */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ /* Documentation string */ DOC_PYGAMEVECTOR2, /* tp_doc */ /* Assigned meaning in release 2.0 */ /* call function for all accessible objects */ 0, /* tp_traverse */ /* delete references to contained objects */ 0, /* tp_clear */ /* Assigned meaning in release 2.1 */ /* rich comparisons */ (richcmpfunc)vector_richcompare, /* tp_richcompare */ /* weak reference enabler */ 0, /* tp_weaklistoffset */ /* Added in release 2.2 */ /* Iterators */ vector_iter, /* tp_iter */ 0, /* tp_iternext */ /* Attribute descriptor and subclassing stuff */ vector2_methods, /* tp_methods */ vector_members, /* tp_members */ vector2_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)vector2_init, /* tp_init */ 0, /* tp_alloc */ (newfunc)vector2_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ }; /************************************************************* * PyVector3 specific functions *************************************************************/ static PyObject * vector3_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyVector *vec = (PyVector *)type->tp_alloc(type, 0); if (vec != NULL) { vec->dim = 3; vec->epsilon = FLT_EPSILON; vec->coords = PyMem_New(double, vec->dim); if (vec->coords == NULL) { vec->ob_type->tp_free((PyObject*)vec); return NULL; } } return (PyObject *)vec; } static int vector3_init(PyVector *self, PyObject *args, PyObject *kwds) { PyObject *xOrSequence=NULL, *y=NULL, *z=NULL; static char *kwlist[] = {"x", "y", "z", NULL}; if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOO:Vector3", kwlist, &xOrSequence, &y, &z)) return -1; if (xOrSequence) { if (RealNumber_Check(xOrSequence)) { self->coords[0] = PyFloat_AsDouble(xOrSequence); } else if (PyVectorCompatible_Check(xOrSequence, self->dim)) { self->coords[0] = PySequence_GetItem_AsDouble(xOrSequence, 0); self->coords[1] = PySequence_GetItem_AsDouble(xOrSequence, 1); self->coords[2] = PySequence_GetItem_AsDouble(xOrSequence, 2); /* successful initialization from sequence type */ return 0; } else if (PyString_Check(xOrSequence)) { /* This should make "Vector3(Vector3().__repr__())" possible */ char buffer[STRING_BUF_SIZE]; char *endptr; char *str = PyString_AsString(xOrSequence); if (strncmp(str, "<Vector3(", strlen("<Vector3(")) != 0) goto error; str += strlen("<Vector3("); self->coords[0] = PyOS_ascii_strtod(str, &endptr); if (endptr == str) { goto error; } str = endptr + strlen(", "); self->coords[1] = PyOS_ascii_strtod(str, &endptr); if (endptr == str) goto error; str = endptr + strlen(", "); self->coords[2] = PyOS_ascii_strtod(str, &endptr); if (endptr == str) goto error; /* successful conversion from string */ return 0; } else { goto error; } } else { self->coords[0] = 0.; } if (y) { if (RealNumber_Check(y)) { self->coords[1] = PyFloat_AsDouble(y); } else { goto error; } } else { self->coords[1] = 0.; } if (z) { if (RealNumber_Check(z)) { self->coords[2] = PyFloat_AsDouble(z); } else { goto error; } } else { self->coords[2] = 0.; } /* success initialization */ return 0; error: PyErr_SetString(PyExc_ValueError, "Vector3 must be initialized with 3 real numbers or a sequence of 3 real numbers"); return -1; } static void _vector3_do_rotate(double *dst_coords, const double *src_coords, const double *axis_coords, double angle, double epsilon) { double sinValue, cosValue, cosComplement; double normalizationFactor; double axisLength2 = 0; double axis[3]; int i; /* make sure angle is in range [0, 360) */ angle = fmod(angle, 360.); if (angle < 0) angle += 360.; for (i = 0; i < 3; ++i) { axisLength2 += axis_coords[i] * axis_coords[i]; axis[i] = axis_coords[i]; } /* normalize the axis */ if (axisLength2 - 1 > epsilon) { normalizationFactor = 1. / sqrt(axisLength2); for (i = 0; i < 3; ++i) axis[i] *= normalizationFactor; } /* special-case rotation by 0, 90, 180 and 270 degrees */ if (fmod(angle + epsilon, 90.) < 2 * epsilon) { switch ((int)((angle + epsilon) / 90)) { case 0: /* 0 degrees */ memcpy(dst_coords, src_coords, 3 * sizeof(src_coords[0])); break; case 1: /* 90 degrees */ dst_coords[0] = (src_coords[0] * (axis[0] * axis[0]) + src_coords[1] * (axis[0] * axis[1] - axis[2]) + src_coords[2] * (axis[0] * axis[2] + axis[1])); dst_coords[1] = (src_coords[0] * (axis[0] * axis[1] + axis[2]) + src_coords[1] * (axis[1] * axis[1]) + src_coords[2] * (axis[1] * axis[2] - axis[0])); dst_coords[2] = (src_coords[0] * (axis[0] * axis[2] - axis[1]) + src_coords[1] * (axis[1] * axis[2] + axis[0]) + src_coords[2] * (axis[2] * axis[2])); break; case 2: /* 180 degrees */ dst_coords[0] = (src_coords[0] * (-1 + axis[0] * axis[0] * 2) + src_coords[1] * (axis[0] * axis[1] * 2) + src_coords[2] * (axis[0] * axis[2] * 2)); dst_coords[1] = (src_coords[0] * (axis[0] * axis[1] * 2) + src_coords[1] * (-1 + axis[1] * axis[1] * 2) + src_coords[2] * (axis[1] * axis[2] * 2)); dst_coords[2] = (src_coords[0] * (axis[0] * axis[2] * 2) + src_coords[1] * (axis[1] * axis[2] * 2) + src_coords[2] * (-1 + axis[2] * axis[2] * 2)); break; case 3: /* 270 degrees */ dst_coords[0] = (src_coords[0] * (axis[0] * axis[0]) + src_coords[1] * (axis[0] * axis[1] + axis[2]) + src_coords[2] * (axis[0] * axis[2] - axis[1])); dst_coords[1] = (src_coords[0] * (axis[0] * axis[1] - axis[2]) + src_coords[1] * (axis[1] * axis[1]) + src_coords[2] * (axis[1] * axis[2] + axis[0])); dst_coords[2] = (src_coords[0] * (axis[0] * axis[2] + axis[1]) + src_coords[1] * (axis[1] * axis[2] - axis[0]) + src_coords[2] * (axis[2] * axis[2])); break; } } else { angle = DEG2RAD(angle); sinValue = sin(angle); cosValue = cos(angle); cosComplement = 1 - cosValue; dst_coords[0] = (src_coords[0] * (cosValue + axis[0] * axis[0] * cosComplement) + src_coords[1] * (axis[0] * axis[1] * cosComplement - axis[2] * sinValue) + src_coords[2] * (axis[0] * axis[2] * cosComplement + axis[1] * sinValue)); dst_coords[1] = (src_coords[0] * (axis[0] * axis[1] * cosComplement + axis[2] * sinValue) + src_coords[1] * (cosValue + axis[1] * axis[1] * cosComplement) + src_coords[2] * (axis[1] * axis[2] * cosComplement - axis[0] * sinValue)); dst_coords[2] = (src_coords[0] * (axis[0] * axis[2] * cosComplement - axis[1] * sinValue) + src_coords[1] * (axis[1] * axis[2] * cosComplement + axis[0] * sinValue) + src_coords[2] * (cosValue + axis[2] * axis[2] * cosComplement)); } } static PyObject * vector3_rotate(PyVector *self, PyObject *args) { PyVector *ret; PyObject *axis; double axis_coords[3]; double angle; if (!PyArg_ParseTuple(args, "dO:rotate", &angle, &axis)) { return NULL; } if (!PyVectorCompatible_Check(axis, self->dim)) { PyErr_SetString(PyExc_TypeError, "axis must be a 3D Vector"); return NULL; } PySequence_AsVectorCoords(axis, axis_coords, 3); ret = (PyVector*)PyVector_NEW(self->dim); _vector3_do_rotate(ret->coords, self->coords, axis_coords, angle, self->epsilon); return (PyObject*)ret; } static PyObject * vector3_rotate_ip(PyVector *self, PyObject *args) { PyObject *axis; double axis_coords[3]; double angle; double tmp[3]; if (!PyArg_ParseTuple(args, "dO:rotate_ip", &angle, &axis)) { return NULL; } if (!PyVectorCompatible_Check(axis, self->dim)) { PyErr_SetString(PyExc_TypeError, "axis must be a 3D Vector"); return NULL; } PySequence_AsVectorCoords(axis, axis_coords, 3); memcpy(tmp, self->coords, 3 * sizeof(self->coords[0])); _vector3_do_rotate(self->coords, tmp, axis_coords, angle, self->epsilon); Py_RETURN_NONE; } static PyObject * vector3_rotate_x(PyVector *self, PyObject *angleObject) { PyVector *ret; double sinValue, cosValue; double angle; if (!RealNumber_Check(angleObject)) { PyErr_SetString(PyExc_TypeError, "angle must be a real number"); return NULL; } angle = DEG2RAD(PyFloat_AsDouble(angleObject)); sinValue = sin(angle); cosValue = cos(angle); ret = (PyVector*)PyVector_NEW(self->dim); ret->coords[0] = self->coords[0]; ret->coords[1] = self->coords[1] * cosValue - self->coords[2] * sinValue; ret->coords[2] = self->coords[1] * sinValue + self->coords[2] * cosValue; return (PyObject*)ret; } static PyObject * vector3_rotate_x_ip(PyVector *self, PyObject *angleObject) { PyVector *ret; double tmp_coords[3]; double sinValue, cosValue; double angle; if (!RealNumber_Check(angleObject)) { PyErr_SetString(PyExc_TypeError, "angle must be a real number"); return NULL; } angle = DEG2RAD(PyFloat_AsDouble(angleObject)); sinValue = sin(angle); cosValue = cos(angle); memcpy(tmp_coords, self->coords, 3 * sizeof(tmp_coords[0])); self->coords[1] = tmp_coords[1] * cosValue - tmp_coords[2] * sinValue; self->coords[2] = tmp_coords[1] * sinValue + tmp_coords[2] * cosValue; return (PyObject*)ret; } static PyObject * vector3_rotate_y(PyVector *self, PyObject *angleObject) { PyVector *ret; double sinValue, cosValue; double angle; if (!RealNumber_Check(angleObject)) { PyErr_SetString(PyExc_TypeError, "angle must be a real number"); return NULL; } angle = DEG2RAD(PyFloat_AsDouble(angleObject)); sinValue = sin(angle); cosValue = cos(angle); ret = (PyVector*)PyVector_NEW(self->dim); ret->coords[0] = self->coords[0] * cosValue + self->coords[2] * sinValue; ret->coords[1] = self->coords[1]; ret->coords[2] = -self->coords[0] * sinValue + self->coords[2] * cosValue; return (PyObject*)ret; } static PyObject * vector3_rotate_y_ip(PyVector *self, PyObject *angleObject) { PyVector *ret; double tmp_coords[3]; double sinValue, cosValue; double angle; if (!RealNumber_Check(angleObject)) { PyErr_SetString(PyExc_TypeError, "angle must be a real number"); return NULL; } angle = DEG2RAD(PyFloat_AsDouble(angleObject)); sinValue = sin(angle); cosValue = cos(angle); memcpy(tmp_coords, self->coords, 3 * sizeof(tmp_coords[0])); self->coords[0] = tmp_coords[0] * cosValue + tmp_coords[2] * sinValue; self->coords[2] = -tmp_coords[0] * sinValue + tmp_coords[2] * cosValue; return (PyObject*)ret; } static PyObject * vector3_rotate_z(PyVector *self, PyObject *angleObject) { PyVector *ret; double sinValue, cosValue; double angle; if (!RealNumber_Check(angleObject)) { PyErr_SetString(PyExc_TypeError, "angle must be a real number"); return NULL; } angle = DEG2RAD(PyFloat_AsDouble(angleObject)); sinValue = sin(angle); cosValue = cos(angle); ret = (PyVector*)PyVector_NEW(self->dim); ret->coords[0] = self->coords[0] * cosValue - self->coords[1] * sinValue; ret->coords[1] = self->coords[0] * sinValue + self->coords[1] * cosValue; ret->coords[2] = self->coords[2]; return (PyObject*)ret; } static PyObject * vector3_rotate_z_ip(PyVector *self, PyObject *angleObject) { PyVector *ret; double tmp_coords[3]; double sinValue, cosValue; double angle; if (!RealNumber_Check(angleObject)) { PyErr_SetString(PyExc_TypeError, "angle must be a real number"); return NULL; } angle = DEG2RAD(PyFloat_AsDouble(angleObject)); sinValue = sin(angle); cosValue = cos(angle); memcpy(tmp_coords, self->coords, 3 * sizeof(tmp_coords[0])); self->coords[0] = tmp_coords[0] * cosValue - tmp_coords[1] * sinValue; self->coords[1] = tmp_coords[0] * sinValue + tmp_coords[1] * cosValue; return (PyObject*)ret; } static PyObject * vector3_cross(PyVector *self, PyObject *other) { PyVector *ret; double *ret_coords; if (!PyVectorCompatible_Check(other, self->dim)) { PyErr_SetString(PyExc_TypeError, "cannot calculate cross Product"); return NULL; } ret = (PyVector*)PyVector_NEW(self->dim); ret_coords = ret->coords; if (PyVector_Check(other)) { double *other_coords = ((PyVector *)other)->coords; ret_coords[0] = (self->coords[1] * other_coords[2]) - (self->coords[2] * other_coords[1]); ret_coords[1] = (self->coords[2] * other_coords[0]) - (self->coords[0] * other_coords[2]); ret_coords[2] = (self->coords[0] * other_coords[1]) - (self->coords[1] * other_coords[0]); } else { double other_coords[3]; PySequence_AsVectorCoords(other, other_coords, 3); ret_coords[0] = (self->coords[1] * other_coords[2]) - (self->coords[2] * other_coords[1]); ret_coords[1] = (self->coords[2] * other_coords[0]) - (self->coords[0] * other_coords[2]); ret_coords[2] = (self->coords[0] * other_coords[1]) - (self->coords[1] * other_coords[0]); } return (PyObject*)ret; } static PyObject * vector3_angle_to(PyVector *self, PyObject *other) { int i; double angle, tmp1, tmp2; double other_coords[VECTOR_MAX_SIZE]; if (!PyVectorCompatible_Check(other, self->dim)) { PyErr_SetString(PyExc_TypeError, "expected an vector."); return NULL; } PySequence_AsVectorCoords(other, other_coords, self->dim); tmp1 = _scalar_product(self->coords, self->coords, self->dim); tmp2 = _scalar_product(other_coords, other_coords, self->dim); angle = acos(_scalar_product(self->coords, other_coords, self->dim) / sqrt(tmp1 * tmp2)); return PyFloat_FromDouble(RAD2DEG(angle)); } static PyObject * vector3_as_spherical(PyVector *self) { double r, theta, phi; r = sqrt(_scalar_product(self->coords, self->coords, self->dim)); theta = acos(self->coords[2] / r); phi = atan2(self->coords[1], self->coords[0]); return Py_BuildValue("(ddd)", r, theta, phi); } static PyObject * vector3_from_spherical(PyVector *self, PyObject *args) { PyObject *spherical_obj, *r_obj, *theta_obj, *phi_obj; double r, theta, phi; theta_obj = NULL; phi_obj = NULL; if (!PyArg_ParseTuple(args, "O:Vector3.from_spherical", &spherical_obj)) { return NULL; } if (!PySequence_Check(spherical_obj) || PySequence_Length(spherical_obj) != 3) { PyErr_SetString(PyExc_TypeError, "3-tuple containing r, theta and phi is expected."); return NULL; } r_obj = PySequence_GetItem(spherical_obj, 0); theta_obj = PySequence_GetItem(spherical_obj, 1); phi_obj = PySequence_GetItem(spherical_obj, 2); if (!PyNumber_Check(r_obj) || !PyNumber_Check(theta_obj) || !PyNumber_Check(phi_obj)) { PyErr_SetString(PyExc_TypeError, "expected numerical values"); Py_DECREF(r_obj); Py_DECREF(theta_obj); Py_DECREF(phi_obj); return NULL; } r = PyFloat_AsDouble(r_obj); theta = PyFloat_AsDouble(theta_obj); phi = PyFloat_AsDouble(phi_obj); Py_DECREF(r_obj); Py_DECREF(theta_obj); Py_DECREF(phi_obj); self->coords[0] = r * sin(theta) * cos(phi); self->coords[1] = r * sin(theta) * sin(phi); self->coords[2] = r * cos(theta); Py_RETURN_NONE; } static PyMethodDef vector3_methods[] = { {"length", (PyCFunction)vector_length, METH_NOARGS, "returns the length/magnitude of the vector." }, {"length_squared", (PyCFunction)vector_length_squared, METH_NOARGS, "returns the length/magnitude of the vector." }, {"rotate", (PyCFunction)vector3_rotate, METH_VARARGS, "returns a new vector rotated counterclockwise by the angle given in degrees." }, {"rotate_ip", (PyCFunction)vector3_rotate_ip, METH_VARARGS, "rotates the vector counterclockwise by the angle given in degrees." }, {"rotate_x", (PyCFunction)vector3_rotate_x, METH_VARARGS, "returns a new vector rotated counterclockwise around the x-axis by the angle given in degrees." }, {"rotate_x_ip", (PyCFunction)vector3_rotate_x_ip, METH_VARARGS, "rotates the vector counterclockwise around the x-axis by the angle given in degrees." }, {"rotate_y", (PyCFunction)vector3_rotate_y, METH_VARARGS, "returns a new vector rotated counterclockwise around the y-axis by the angle given in degrees." }, {"rotate_y_ip", (PyCFunction)vector3_rotate_y_ip, METH_VARARGS, "rotates the vector counterclockwise around the y-axis by the angle given in degrees." }, {"rotate_z", (PyCFunction)vector3_rotate_z, METH_VARARGS, "returns a new vector rotated counterclockwise around the z-axis by the angle given in degrees." }, {"rotate_z_ip", (PyCFunction)vector3_rotate_z_ip, METH_VARARGS, "rotates the vector counterclockwise around the z-axis by the angle given in degrees." }, {"slerp", (PyCFunction)vector_slerp, METH_VARARGS, "Interpolates spherically to a given vector in a given number of steps." }, {"lerp", (PyCFunction)vector_lerp, METH_VARARGS, "Interpolates linear to a given vector in a given number of steps." }, {"normalize", (PyCFunction)vector_normalize, METH_NOARGS, "returns a vector that has length == 1 and the same direction as self." }, {"normalize_ip", (PyCFunction)vector_normalize_ip, METH_NOARGS, "Normalizes the vector so that it has length == 1." }, {"is_normalized", (PyCFunction)vector_is_normalized, METH_NOARGS, "returns True if the vector has length == 1. otherwise it returns False." }, {"cross", (PyCFunction)vector3_cross, METH_O, "calculates the cross product." }, {"dot", (PyCFunction)vector_dot, METH_O, "calculates the dot product." }, {"angle_to", (PyCFunction)vector3_angle_to, METH_O, "returns the angle between self and the given vector." }, {"scale_to_length", (PyCFunction)vector_scale_to_length, METH_O, "scales the vector to the given length." }, {"reflect", (PyCFunction)vector_reflect, METH_O, "reflects the vector on the surface characterized by the given normal." }, {"reflect_ip", (PyCFunction)vector_reflect_ip, METH_O, "reflects the vector in-place on the surface characterized by the given normal." }, {"distance_to", (PyCFunction)vector_distance_to, METH_O, "returns the distance to the given vector." }, {"distance_squared_to", (PyCFunction)vector_distance_squared_to, METH_O, "returns the squared distance to the given vector." }, {"elementwise", (PyCFunction)vector_elementwise, METH_NOARGS, "applies the following operation to each element of the vector." }, {"as_spherical", (PyCFunction)vector3_as_spherical, METH_NOARGS, "returns a tuple (r, theta, phi) where r is the radial distance, theta is the inclination angle and phi is the azimutal angle." }, {"from_spherical", (PyCFunction)vector3_from_spherical, METH_VARARGS, "sets x, y and z from a tuple (r, theta, phi) where r is the radial distance, theta is the inclination angle and phi is the azimutal angle." }, {NULL} /* Sentinel */ }; static PyGetSetDef vector3_getsets[] = { { "x", (getter)vector_getx, (setter)vector_setx, NULL, NULL }, { "y", (getter)vector_gety, (setter)vector_sety, NULL, NULL }, { "z", (getter)vector_getz, (setter)vector_setz, NULL, NULL }, { NULL, 0, NULL, NULL, NULL } /* Sentinel */ }; /******************************** * PyVector3 type definition ********************************/ static PyTypeObject PyVector3_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pygame.math.Vector3", /* tp_name */ sizeof(PyVector), /* tp_basicsize */ 0, /* tp_itemsize */ /* Methods to implement standard operations */ (destructor)vector_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)vector_repr, /* tp_repr */ /* Method suites for standard classes */ &vector_as_number, /* tp_as_number */ &vector_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ /* More standard operations (here for binary compatibility) */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc)vector_str, /* tp_str */ (getattrofunc)vector_getAttr_swizzle, /* tp_getattro */ (setattrofunc)vector_setAttr_swizzle, /* tp_setattro */ /* Functions to access object as input/output buffer */ 0, /* tp_as_buffer */ /* Flags to define presence of optional/expanded features */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ /* Documentation string */ DOC_PYGAMEVECTOR3, /* tp_doc */ /* Assigned meaning in release 2.0 */ /* call function for all accessible objects */ 0, /* tp_traverse */ /* delete references to contained objects */ 0, /* tp_clear */ /* Assigned meaning in release 2.1 */ /* rich comparisons */ (richcmpfunc)vector_richcompare, /* tp_richcompare */ /* weak reference enabler */ 0, /* tp_weaklistoffset */ /* Added in release 2.2 */ /* Iterators */ vector_iter, /* tp_iter */ 0, /* tp_iternext */ /* Attribute descriptor and subclassing stuff */ vector3_methods, /* tp_methods */ vector_members, /* tp_members */ vector3_getsets, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)vector3_init, /* tp_init */ 0, /* tp_alloc */ (newfunc)vector3_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ }; /******************************************** * PyVectorIterator type definition ********************************************/ static void vectoriter_dealloc(vectoriter *it) { Py_XDECREF(it->vec); PyObject_Del(it); } static PyObject * vectoriter_next(vectoriter *it) { assert(it != NULL); if (it->vec == NULL) return NULL; assert(PyVector_Check(it->vec)); if (it->it_index < it->vec->dim) { double item = it->vec->coords[it->it_index]; ++(it->it_index); return PyFloat_FromDouble(item); } Py_DECREF(it->vec); it->vec = NULL; return NULL; } static PyObject * vectoriter_len(vectoriter *it) { Py_ssize_t len = 0; if (it && it->vec) { len = it->vec->dim - it->it_index; } return PyInt_FromSsize_t(len); } static PyMethodDef vectoriter_methods[] = { {"__length_hint__", (PyCFunction)vectoriter_len, METH_NOARGS, }, {NULL, NULL} /* sentinel */ }; static PyTypeObject PyVectorIter_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pygame.math.VectorIterator", /* tp_name */ sizeof(vectoriter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)vectoriter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)vectoriter_next, /* tp_iternext */ vectoriter_methods, /* tp_methods */ 0, /* tp_members */ }; static PyObject * vector_iter(PyObject *vec) { vectoriter *it; if (!PyVector_Check(vec)) { PyErr_BadInternalCall(); return NULL; } it = PyObject_New(vectoriter, &PyVectorIter_Type); if (it == NULL) return NULL; it->it_index = 0; Py_INCREF(vec); it->vec = (PyVector *)vec; return (PyObject *)it; } /******************************************** * PyVector_SlerpIterator type definition ********************************************/ static void vector_slerpiter_dealloc(vector_slerpiter *it) { PyObject_Del(it); } static PyObject * vector_slerpiter_next(vector_slerpiter *it) { int i, j; PyVector *ret; double tmp; assert(it != NULL); if (it->it_index < it->steps) { ret = (PyVector*)PyVector_NEW(it->dim); if (ret != NULL) { for (i = 0; i < it->dim; ++i) { tmp = 0; for (j = 0; j < it->dim; ++j) tmp += it->coords[j] * it->matrix[j][i]; ret->coords[i] = tmp * it->radial_factor; } memcpy(it->coords, ret->coords, sizeof(it->coords[0]) * it->dim); } ++(it->it_index); return (PyObject*)ret; } return NULL; } static PyObject * vector_slerpiter_len(vector_slerpiter *it) { Py_ssize_t len = 0; if (it) { len = it->steps - it->it_index; } return PyInt_FromSsize_t(len); } static PyMethodDef vector_slerpiter_methods[] = { {"__length_hint__", (PyCFunction)vector_slerpiter_len, METH_NOARGS, }, {NULL, NULL} /* sentinel */ }; static PyTypeObject PyVector_SlerpIter_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pygame.math.VectorSlerpIterator", /* tp_name */ sizeof(vector_slerpiter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)vector_slerpiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)vector_slerpiter_next, /* tp_iternext */ vector_slerpiter_methods, /* tp_methods */ 0, /* tp_members */ }; static PyObject * vector_slerp(PyVector *self, PyObject *args) { vector_slerpiter *it; PyObject *other, *steps_object; long int steps; double vec1_coords[VECTOR_MAX_SIZE], vec2_coords[VECTOR_MAX_SIZE]; double angle, length1, length2; if (!PyArg_ParseTuple(args, "OO:Vector.slerp", &other, &steps_object)) { return NULL; } if (!PyInt_Check(steps_object)) { PyErr_SetString(PyExc_TypeError, "Expected Int as argument 2"); return NULL; } if (!PySequence_AsVectorCoords((PyObject*)self, vec1_coords, self->dim)) { PyErr_BadInternalCall(); return NULL; } if (!PySequence_AsVectorCoords(other, vec2_coords, self->dim)) { PyErr_SetString(PyExc_TypeError, "Expected Vector as argument 1"); return NULL; } it = PyObject_New(vector_slerpiter, &PyVector_SlerpIter_Type); if (it == NULL) return NULL; it->it_index = 0; it->dim = self->dim; memcpy(it->coords, vec1_coords, sizeof(vec1_coords[0]) * it->dim); length1 = sqrt(_scalar_product(vec1_coords, vec1_coords, it->dim)); length2 = sqrt(_scalar_product(vec2_coords, vec2_coords, it->dim)); if ((length1 < self->epsilon) || (length2 < self->epsilon)) { PyErr_SetString(PyExc_ZeroDivisionError, "can't use slerp with Zero-Vector"); Py_DECREF(it); return NULL; } angle = acos(_scalar_product(vec1_coords, vec2_coords, self->dim) / (length1 * length2)); it->steps = PyInt_AsLong(steps_object); if (it->steps < 0) { angle -= 2 * M_PI; it->steps = -it->steps; } angle /= it->steps; if (fabs(length1 - length2) > self->epsilon) it->radial_factor = pow(length2 / length1, 1./it->steps); else it->radial_factor = 1; switch (self->dim) { case 2: _make_vector2_slerp_matrix(it, vec1_coords, vec2_coords, angle); break; case 3: _make_vector3_slerp_matrix(it, vec1_coords, vec2_coords, angle); break; default: PyErr_BadInternalCall(); Py_DECREF(it); return NULL; } return (PyObject *)it; } static void _make_vector2_slerp_matrix(vector_slerpiter *it, const double *vec1_coords, const double *vec2_coords, double angle) { double sin_value, cos_value; if (vec1_coords[0] * vec2_coords[1] < vec1_coords[1] * vec2_coords[0]) angle *= -1; cos_value = cos(angle); sin_value = sin(angle); it->matrix[0][0] = cos_value; it->matrix[0][1] = sin_value; it->matrix[1][0] = -sin_value; it->matrix[1][1] = cos_value; } static void _make_vector3_slerp_matrix(vector_slerpiter *it, const double *vec1_coords, const double *vec2_coords, double angle) { int i; double axis[3]; double norm_factor, sin_value, cos_value, cos_complement; /* calculate rotation axis via cross-product */ for (i = 0; i < 3; ++i) axis[i] = ((vec1_coords[(i+1)%3] * vec2_coords[(i+2)%3]) - (vec1_coords[(i+2)%3] * vec2_coords[(i+1)%3])); /* normalize the rotation axis */ norm_factor = 1. / sqrt(_scalar_product(axis, axis, 3)); for (i = 0; i < 3; ++i) axis[i] *= norm_factor; sin_value = sin(angle); cos_value = cos(angle); cos_complement = 1 - cos_value; /* calculate the rotation matrix */ it->matrix[0][0] = cos_value + axis[0] * axis[0] * cos_complement; it->matrix[0][1] = axis[0] * axis[1] * cos_complement + axis[2] * sin_value; it->matrix[0][2] = axis[0] * axis[2] * cos_complement - axis[1] * sin_value; it->matrix[1][0] = axis[0] * axis[1] * cos_complement - axis[2] * sin_value; it->matrix[1][1] = cos_value + axis[1] * axis[1] * cos_complement; it->matrix[1][2] = axis[1] * axis[2] * cos_complement + axis[0] * sin_value; it->matrix[2][0] = axis[0] * axis[2] * cos_complement + axis[1] * sin_value; it->matrix[2][1] = axis[1] * axis[2] * cos_complement - axis[0] * sin_value; it->matrix[2][2] = cos_value + axis[2] * axis[2] * cos_complement; } /* static PyObject * vector2_slerp(PyVector *self, PyObject *args) { PyObject *other; PyVector *ret; double *other_coords; double t; if (!PyArg_ParseTuple("Od:slerp", &other, &t)) { return NULL; } if (!PyVectorCompatible_Check(end_vector, self->dim)) { PyErr_SetString(PyExc_TypeError, "Argument 1 must be a vector."); return NULL; } if (!PySequence_AsVectorCoords(other, other_coords, self->dim)) { PyErr_SetString(PyExc_TypeError, "Argument 1 must be a vector."); return NULL; } if (fabs(t) > 1) { PyErr_SetString(PyExc_ValueError, "Argument 2 must be in range [-1, 1]."); return NULL; } length1 = sqrt(_scalar_product(self->coords, self->coords, self->dim)); length2 = sqrt(_scalar_product(other_coords, other_coords, self->dim)); if ((length1 < self->epsilon) || (length2 < self->epsilon)) { PyErr_SetString(PyExc_ZeroDivisionError, "can't use slerp with Zero-Vector"); return NULL; } angle = acos(_scalar_product(self->coords, other_coords, self->dim) / (length1 * length2)); if (t < 0) { angle -= 2 * M_PI; t = -t; } angle *= t; if (vec1_coords[0] * vec2_coords[1] < vec1_coords[1] * vec2_coords[0]) angle *= -1; if (fabs(length1 - length2) > self->epsilon) it->radial_factor = t * length2 / length1; else it->radial_factor = 1; ret = PyVector_NEW(self->dim); cos_value = cos(angle); sin_value = sin(angle); ret->coords[0] = cos_value * self->coords[0] - sin_value * self->coords[1]; ret->coords[1] = sin_value * self->coords[0] + cos_value * self->coords[1]; n1 = 1. / length1; n2 = 1. / length2; s0 = 1. / sin(angle); s1 = sin(angle * t); s2 = sin(angle * (1-t)); for (i = 0; i < self->dim; ++i) ret->coords[i] = (self->coords[i] * s1 + other_coords[i] * s2) * s0; return ret; } */ /******************************************** * PyVector_LerpIterator type definition ********************************************/ static void vector_lerpiter_dealloc(vector_lerpiter *it) { PyObject_Del(it); } static PyObject * vector_lerpiter_next(vector_lerpiter *it) { int i; PyVector *ret; assert(it != NULL); if (it->it_index < it->steps) { ret = (PyVector*)PyVector_NEW(it->dim); if (ret != NULL) { for (i = 0; i < it->dim; ++i) { it->coords[i] += it->step_vec[i]; ret->coords[i] = it->coords[i]; } } ++(it->it_index); return (PyObject*)ret; } return NULL; } static PyObject * vector_lerpiter_len(vector_lerpiter *it) { Py_ssize_t len = 0; if (it) { len = it->steps - it->it_index; } return PyInt_FromSsize_t(len); } static PyMethodDef vector_lerpiter_methods[] = { {"__length_hint__", (PyCFunction)vector_lerpiter_len, METH_NOARGS, }, {NULL, NULL} /* sentinel */ }; static PyTypeObject PyVector_LerpIter_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pygame.math.VectorLerpIterator", /* tp_name */ sizeof(vector_lerpiter), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)vector_lerpiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ 0, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)vector_lerpiter_next, /* tp_iternext */ vector_lerpiter_methods, /* tp_methods */ 0, /* tp_members */ }; static PyObject * vector_lerp(PyVector *self, PyObject *args) { vector_lerpiter *it; PyObject *other, *steps_object; long int i, steps; double vec1_coords[VECTOR_MAX_SIZE], vec2_coords[VECTOR_MAX_SIZE]; if (!PyArg_ParseTuple(args, "OO:Vector.lerp", &other, &steps_object)) { return NULL; } if (!PyInt_Check(steps_object)) { PyErr_SetString(PyExc_TypeError, "Expected Int as argument 2"); return NULL; } if (!PySequence_AsVectorCoords((PyObject*)self, vec1_coords, self->dim)) { PyErr_BadInternalCall(); return NULL; } if (!PySequence_AsVectorCoords(other, vec2_coords, self->dim)) { PyErr_SetString(PyExc_TypeError, "Expected Vector as argument 1"); return NULL; } it = PyObject_New(vector_lerpiter, &PyVector_LerpIter_Type); if (it == NULL) return NULL; it->it_index = 0; it->dim = self->dim; it->steps = PyInt_AsLong(steps_object); memcpy(it->coords, vec1_coords, sizeof(vec1_coords[0]) * it->dim); for (i = 0; i < it->dim; ++i) it->step_vec[i] = (vec2_coords[i] - vec1_coords[i]) / it->steps; return (PyObject *)it; } /***************************************** * elementwiseproxy *****************************************/ static void vector_elementwiseproxy_dealloc(vector_elementwiseproxy *it) { Py_XDECREF(it->vec); PyObject_Del(it); } static PyObject * vector_elementwiseproxy_richcompare(PyObject *o1, PyObject *o2, int op) { int i, dim; double diff; PyVector *vec; PyObject *other; if (vector_elementwiseproxy_Check(o1)) { vec = ((vector_elementwiseproxy*)o1)->vec; other = o2; } else { vec = ((vector_elementwiseproxy*)o2)->vec; other = o1; /* flip op */ if (op == Py_LT) op = Py_GE; else if (op == Py_LE) op = Py_GT; else if (op == Py_GT) op = Py_LE; else if (op == Py_GE) op = Py_LT; } if (vector_elementwiseproxy_Check(other)) other = (PyObject*)((vector_elementwiseproxy*)o2)->vec; dim = vec->dim; if (PyVectorCompatible_Check(other, dim)) { /* use diff == diff to check for NaN */ /* TODO: how should NaN be handled with LT/LE/GT/GE? */ switch (op) { case Py_EQ: for (i = 0; i < dim; i++) { diff = vec->coords[i] - PySequence_GetItem_AsDouble(other, i); if ((diff != diff) || (fabs(diff) >= vec->epsilon)) { Py_RETURN_FALSE; } } Py_RETURN_TRUE; case Py_NE: for (i = 0; i < dim; i++) { diff = vec->coords[i] - PySequence_GetItem_AsDouble(other, i); if ((diff == diff) && (fabs(diff) < vec->epsilon)) { Py_RETURN_FALSE; } } Py_RETURN_TRUE; case Py_LT: for (i = 0; i < dim; i++) { if (vec->coords[i] >= PySequence_GetItem_AsDouble(other, i)) Py_RETURN_FALSE; } Py_RETURN_TRUE; case Py_LE: for (i = 0; i < dim; i++) { if (vec->coords[i] > PySequence_GetItem_AsDouble(other, i)) Py_RETURN_FALSE; } Py_RETURN_TRUE; case Py_GT: for (i = 0; i < dim; i++) { if (vec->coords[i] <= PySequence_GetItem_AsDouble(other, i)) Py_RETURN_FALSE; } Py_RETURN_TRUE; case Py_GE: for (i = 0; i < dim; i++) { if (vec->coords[i] < PySequence_GetItem_AsDouble(other, i)) Py_RETURN_FALSE; } Py_RETURN_TRUE; } } else if (RealNumber_Check(other)) { double value = PyFloat_AsDouble(other); switch (op) { case Py_EQ: for (i = 0; i < dim; i++) { diff = vec->coords[i] - value; if (diff != diff || fabs(diff) >= vec->epsilon) { Py_RETURN_FALSE; } } Py_RETURN_TRUE; case Py_NE: for (i = 0; i < dim; i++) { diff = vec->coords[i] - value; if (diff == diff && fabs(diff) < vec->epsilon) { Py_RETURN_FALSE; } } Py_RETURN_TRUE; case Py_LT: for (i = 0; i < dim; i++) { if (vec->coords[i] >= value) Py_RETURN_FALSE; } Py_RETURN_TRUE; case Py_LE: for (i = 0; i < dim; i++) { if (vec->coords[i] > value) Py_RETURN_FALSE; } Py_RETURN_TRUE; case Py_GT: for (i = 0; i < dim; i++) { if (vec->coords[i] <= value) Py_RETURN_FALSE; } Py_RETURN_TRUE; case Py_GE: for (i = 0; i < dim; i++) { if (vec->coords[i] < value) Py_RETURN_FALSE; } Py_RETURN_TRUE; } } Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } /******************************************************* * vector_elementwiseproxy PyNumber emulation routines *******************************************************/ static PyObject * vector_elementwiseproxy_generic_math(PyObject *o1, PyObject *o2, int op) { /* TODO: DIV, FLOOR_DIV and MOD should check for ZeroDivision */ int i, dim; double tmp, mod; PyObject *other; PyVector *vec, *ret; if (vector_elementwiseproxy_Check(o1)) { vec = ((vector_elementwiseproxy*)o1)->vec; other = o2; } else { other = o1; vec = ((vector_elementwiseproxy*)o2)->vec; op |= OP_ARG_REVERSE; } dim = vec->dim; if (vector_elementwiseproxy_Check(other)) { other = (PyObject*)((vector_elementwiseproxy*)other)->vec; op |= OP_ARG_VECTOR; } else if (PyVectorCompatible_Check(other, dim)) op |= OP_ARG_VECTOR; else if (RealNumber_Check(other)) op |= OP_ARG_NUMBER; else op |= OP_ARG_UNKNOWN; switch (op) { case OP_ADD | OP_ARG_VECTOR: case OP_ADD | OP_ARG_VECTOR | OP_ARG_REVERSE: return vector_add((PyObject*)vec, other); case OP_ADD | OP_ARG_NUMBER: case OP_ADD | OP_ARG_NUMBER | OP_ARG_REVERSE: tmp = PyFloat_AsDouble(other); ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) ret->coords[i] = vec->coords[i] + tmp; return (PyObject*)ret; case OP_SUB | OP_ARG_VECTOR: return vector_sub((PyObject*)vec, other); case OP_SUB | OP_ARG_VECTOR | OP_ARG_REVERSE: return vector_sub(other, (PyObject*)vec); case OP_SUB | OP_ARG_NUMBER: tmp = PyFloat_AsDouble(other); ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) ret->coords[i] = vec->coords[i] - tmp; return (PyObject*)ret; case OP_SUB | OP_ARG_NUMBER | OP_ARG_REVERSE: tmp = PyFloat_AsDouble(other); ret = (PyVector*)PyVector_NEW(dim); for (i = 0; i < dim; i++) ret->coords[i] = tmp - vec->coords[i]; return (PyObject*)ret; case OP_MUL | OP_ARG_VECTOR: case OP_MUL | OP_ARG_VECTOR | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) { ret->coords[i] = (vec->coords[i] * PySequence_GetItem_AsDouble(other, i)); } return (PyObject*)ret; case OP_MUL | OP_ARG_NUMBER: case OP_MUL | OP_ARG_NUMBER | OP_ARG_REVERSE: return vector_mul((PyObject*)vec, other); case OP_DIV | OP_ARG_VECTOR: ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) { ret->coords[i] = (vec->coords[i] / PySequence_GetItem_AsDouble(other, i)); } return (PyObject*)ret; case OP_DIV | OP_ARG_VECTOR | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) { ret->coords[i] = (PySequence_GetItem_AsDouble(o1, i) / vec->coords[i]); } return (PyObject*)ret; case OP_DIV | OP_ARG_NUMBER: return vector_div(vec, other); case OP_DIV | OP_ARG_NUMBER | OP_ARG_REVERSE: tmp = PyFloat_AsDouble(o1); ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) ret->coords[i] = tmp / vec->coords[i]; return (PyObject*)ret; case OP_FLOOR_DIV | OP_ARG_VECTOR: ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) { ret->coords[i] = floor(vec->coords[i] / PySequence_GetItem_AsDouble(other, i)); } return (PyObject*)ret; case OP_FLOOR_DIV | OP_ARG_VECTOR | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) { ret->coords[i] = floor(PySequence_GetItem_AsDouble(o1, i) / vec->coords[i]); } return (PyObject*)ret; case OP_FLOOR_DIV | OP_ARG_NUMBER: return vector_floor_div(vec, other); case OP_FLOOR_DIV | OP_ARG_NUMBER | OP_ARG_REVERSE: tmp = PyFloat_AsDouble(other); ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) ret->coords[i] = floor(tmp / vec->coords[i]); return (PyObject*)ret; case OP_MOD | OP_ARG_VECTOR: ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) { tmp = PySequence_GetItem_AsDouble(other, i); mod = fmod(vec->coords[i], tmp); /* note: checking mod*value < 0 is incorrect -- underflows to 0 if value < sqrt(smallest nonzero double) */ if (mod && ((tmp < 0) != (mod < 0))) { mod += tmp; } ret->coords[i] = mod; } return (PyObject*)ret; case OP_MOD | OP_ARG_VECTOR | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(vec->dim); for (i = 0; i < vec->dim; i++) { tmp = PySequence_GetItem_AsDouble(other, i); mod = fmod(tmp, vec->coords[i]); /* note: see above */ if (mod && ((vec->coords[i] < 0) != (mod < 0))) { mod += vec->coords[i]; } ret->coords[i] = mod; } return (PyObject*)ret; case OP_MOD | OP_ARG_NUMBER: ret = (PyVector*)PyVector_NEW(vec->dim); tmp = PyFloat_AsDouble(other); for (i = 0; i < vec->dim; i++) { mod = fmod(vec->coords[i], tmp); /* note: see above */ if (mod && ((tmp < 0) != (mod < 0))) { mod += tmp; } ret->coords[i] = mod; } return (PyObject*)ret; case OP_MOD | OP_ARG_NUMBER | OP_ARG_REVERSE: ret = (PyVector*)PyVector_NEW(vec->dim); tmp = PyFloat_AsDouble(other); for (i = 0; i < vec->dim; i++) { mod = fmod(tmp, vec->coords[i]); /* note: see above */ if (mod && ((vec->coords[i] < 0) != (mod < 0))) { mod += vec->coords[i]; } ret->coords[i] = mod; } return (PyObject*)ret; default: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } } static PyObject * vector_elementwiseproxy_add(PyObject *o1, PyObject *o2) { return vector_elementwiseproxy_generic_math(o1, o2, OP_ADD); } static PyObject * vector_elementwiseproxy_sub(PyObject *o1, PyObject *o2) { return vector_elementwiseproxy_generic_math(o1, o2, OP_SUB); } static PyObject * vector_elementwiseproxy_mul(PyObject *o1, PyObject *o2) { return vector_elementwiseproxy_generic_math(o1, o2, OP_MUL); } static PyObject * vector_elementwiseproxy_div(PyObject *o1, PyObject *o2) { return vector_elementwiseproxy_generic_math(o1, o2, OP_DIV); } static PyObject * vector_elementwiseproxy_floor_div(PyObject *o1, PyObject *o2) { return vector_elementwiseproxy_generic_math(o1, o2, OP_FLOOR_DIV); } static PyObject * vector_elementwiseproxy_mod(PyObject *o1, PyObject *o2) { return vector_elementwiseproxy_generic_math(o1, o2, OP_MOD); } static PyObject * vector_elementwiseproxy_pow(PyObject *baseObj, PyObject *expoObj, PyObject *mod) { int i, dim; double tmp; double bases[VECTOR_MAX_SIZE]; double expos[VECTOR_MAX_SIZE]; PyVector *ret, *vec; PyObject *base, *expo, *result; if (mod != Py_None) { PyErr_SetString(PyExc_TypeError, "pow() 3rd argument not " "allowed for vectors"); return NULL; } if (vector_elementwiseproxy_Check(baseObj)) { dim = ((vector_elementwiseproxy*)baseObj)->vec->dim; memcpy(bases, ((vector_elementwiseproxy*)baseObj)->vec->coords, sizeof(double) * dim); if (vector_elementwiseproxy_Check(expoObj)) { memcpy(expos, ((vector_elementwiseproxy*)expoObj)->vec->coords, sizeof(double) * dim); } else if (PyVectorCompatible_Check(expoObj, dim)) { PySequence_AsVectorCoords(expoObj, expos, dim); } else if (RealNumber_Check(expoObj)) { tmp = PyFloat_AsDouble(expoObj); for (i = 0; i < dim; i++) expos[i] = tmp; } else goto NOT_IMPLEMENTED; } else { dim = ((vector_elementwiseproxy*)expoObj)->vec->dim; memcpy(expos, ((vector_elementwiseproxy*)expoObj)->vec->coords, sizeof(double) * dim); if (PyVectorCompatible_Check(baseObj, dim)) { PySequence_AsVectorCoords(baseObj, bases, dim); } else if (RealNumber_Check(baseObj)) { tmp = PyFloat_AsDouble(baseObj); for (i = 0; i < dim; i++) bases[i] = tmp; } else goto NOT_IMPLEMENTED; } ret = (PyVector*)PyVector_NEW(dim); /* there are many special cases so we let python do the work for now */ for (i = 0; i < dim; i++) { base = PyFloat_FromDouble(bases[i]); expo = PyFloat_FromDouble(expos[i]); result = PyNumber_Power(base, expo, Py_None); if (!result) return NULL; ret->coords[i] = PyFloat_AsDouble(result); Py_DECREF(result); Py_DECREF(expo); Py_DECREF(base); } return (PyObject*)ret; NOT_IMPLEMENTED: Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } static PyObject * vector_elementwiseproxy_abs(vector_elementwiseproxy *self) { int i; PyVector *ret = (PyVector*)PyVector_NEW(self->vec->dim); for (i = 0; i < self->vec->dim; i++) { ret->coords[i] = fabs(self->vec->coords[i]); } return (PyObject*)ret; } static PyObject * vector_elementwiseproxy_neg(vector_elementwiseproxy *self) { int i; PyVector *ret = (PyVector*)PyVector_NEW(self->vec->dim); for (i = 0; i < self->vec->dim; i++) { ret->coords[i] = -self->vec->coords[i]; } return (PyObject*)ret; } static PyObject * vector_elementwiseproxy_pos(vector_elementwiseproxy *self) { int i; PyVector *ret = (PyVector*)PyVector_NEW(self->vec->dim); memcpy(ret->coords, self->vec->coords, sizeof(ret->coords[0]) * ret->dim); return (PyObject*)ret; } static int vector_elementwiseproxy_nonzero(vector_elementwiseproxy *self) { int i; for (i = 0; i < self->vec->dim; i++) { if (fabs(self->vec->coords[i]) > self->vec->epsilon) { return 1; } } return 0; } static PyNumberMethods vector_elementwiseproxy_as_number = { (binaryfunc)vector_elementwiseproxy_add, /* nb_add; __add__ */ (binaryfunc)vector_elementwiseproxy_sub, /* nb_subtract; __sub__ */ (binaryfunc)vector_elementwiseproxy_mul, /* nb_multiply; __mul__ */ (binaryfunc)vector_elementwiseproxy_div, /* nb_divide; __div__ */ (binaryfunc)vector_elementwiseproxy_mod, /* nb_remainder; __mod__ */ (binaryfunc)0, /* nb_divmod; __divmod__ */ (ternaryfunc)vector_elementwiseproxy_pow, /* nb_power; __pow__ */ (unaryfunc)vector_elementwiseproxy_neg, /* nb_negative; __neg__ */ (unaryfunc)vector_elementwiseproxy_pos, /* nb_positive; __pos__ */ (unaryfunc)vector_elementwiseproxy_abs, /* nb_absolute; __abs__ */ (inquiry)vector_elementwiseproxy_nonzero, /* nb_nonzero; __nonzero__ */ (unaryfunc)0, /* nb_invert; __invert__ */ (binaryfunc)0, /* nb_lshift; __lshift__ */ (binaryfunc)0, /* nb_rshift; __rshift__ */ (binaryfunc)0, /* nb_and; __and__ */ (binaryfunc)0, /* nb_xor; __xor__ */ (binaryfunc)0, /* nb_or; __or__ */ (coercion)0, /* nb_coerce; __coerce__ */ (unaryfunc)0, /* nb_int; __int__ */ (unaryfunc)0, /* nb_long; __long__ */ (unaryfunc)0, /* nb_float; __float__ */ (unaryfunc)0, /* nb_oct; __oct__ */ (unaryfunc)0, /* nb_hex; __hex__ */ /* Added in release 2.0 */ (binaryfunc)0, /* nb_inplace_add; __iadd__ */ (binaryfunc)0, /* nb_inplace_subtract; __isub__ */ (binaryfunc)0, /* nb_inplace_multiply; __imul__ */ (binaryfunc)0, /* nb_inplace_divide; __idiv__ */ (binaryfunc)0, /* nb_inplace_remainder; __imod__ */ (ternaryfunc)0, /* nb_inplace_power; __pow__ */ (binaryfunc)0, /* nb_inplace_lshift; __ilshift__ */ (binaryfunc)0, /* nb_inplace_rshift; __irshift__ */ (binaryfunc)0, /* nb_inplace_and; __iand__ */ (binaryfunc)0, /* nb_inplace_xor; __ixor__ */ (binaryfunc)0, /* nb_inplace_or; __ior__ */ /* Added in release 2.2 */ (binaryfunc)vector_elementwiseproxy_floor_div, /* nb_floor_divide; __floor__ */ (binaryfunc)vector_elementwiseproxy_div, /* nb_true_divide; __truediv__ */ (binaryfunc)0, /* nb_inplace_floor_divide; __ifloor__ */ (binaryfunc)0, /* nb_inplace_true_divide; __itruediv__ */ /* Added in release 2.5 */ (unaryfunc)0, /* nb_index; __index__ */ }; static PyTypeObject PyVectorElementwiseProxy_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pygame.math.VectorElementwiseProxy", /* tp_name */ sizeof(vector_elementwiseproxy), /* tp_basicsize */ 0, /* tp_itemsize */ /* Methods to implement standard operations */ (destructor)vector_elementwiseproxy_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)0, /* tp_repr */ /* Method suites for standard classes */ &vector_elementwiseproxy_as_number, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ /* More standard operations (here for binary compatibility) */ 0, /* tp_hash */ 0, /* tp_call */ (reprfunc)0, /* tp_str */ (getattrofunc)0, /* tp_getattro */ (setattrofunc)0, /* tp_setattro */ /* Functions to access object as input/output buffer */ 0, /* tp_as_buffer */ /* Flags to define presence of optional/expanded features */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ /* Documentation string */ 0, /* tp_doc */ /* Assigned meaning in release 2.0 */ /* call function for all accessible objects */ 0, /* tp_traverse */ /* delete references to contained objects */ 0, /* tp_clear */ /* Assigned meaning in release 2.1 */ /* rich comparisons */ (richcmpfunc)vector_elementwiseproxy_richcompare, /* tp_richcompare */ /* weak reference enabler */ 0, /* tp_weaklistoffset */ /* Added in release 2.2 */ /* Iterators */ 0, /* tp_iter */ 0, /* tp_iternext */ /* Attribute descriptor and subclassing stuff */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)0, /* tp_init */ 0, /* tp_alloc */ (newfunc)0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ }; static PyObject * vector_elementwise(PyVector *vec) { vector_elementwiseproxy *proxy; if (!PyVector_Check(vec)) { PyErr_BadInternalCall(); return NULL; } proxy = PyObject_New(vector_elementwiseproxy, &PyVectorElementwiseProxy_Type); if (proxy == NULL) return NULL; Py_INCREF(vec); proxy->vec = (PyVector *)vec; return (PyObject *)proxy; } static PyObject * math_enable_swizzling(PyVector *self) { swizzling_enabled = 1; Py_RETURN_NONE; } static PyObject * math_disable_swizzling(PyVector *self) { swizzling_enabled = 0; Py_RETURN_NONE; } static PyMethodDef _math_methods[] = { {"enable_swizzling", (PyCFunction)math_enable_swizzling, METH_NOARGS, "enables swizzling." }, {"disable_swizzling", (PyCFunction)math_disable_swizzling, METH_NOARGS, "disables swizzling." }, {NULL, NULL, 0, NULL} }; /* DOC */ static char _math_doc[] = /* DOC */ "Module for various math related classes and functions\n"; /**************************** * Module init function ****************************/ MODINIT_DEFINE (math) { PyObject *module, *apiobj; static void *c_api[PYGAMEAPI_MATH_NUMSLOTS]; #if PY3 static struct PyModuleDef _module = { PyModuleDef_HEAD_INIT, "math", _math_doc, -1, _math_methods, NULL, NULL, NULL, NULL }; #endif /* initialize the extension types */ if ((PyType_Ready(&PyVector2_Type) < 0) || (PyType_Ready(&PyVector3_Type) < 0) || (PyType_Ready(&PyVectorElementwiseProxy_Type) < 0) || (PyType_Ready(&PyVectorIter_Type) < 0) || (PyType_Ready(&PyVector_SlerpIter_Type) < 0) /*|| (PyType_Ready(&PyVector4_Type) < 0)*/) { MODINIT_ERROR; } /* initialize the module */ #if PY3 module = PyModule_Create (&_module); #else module = Py_InitModule3 (MODPREFIX "math", _math_methods, _math_doc); #endif if (module == NULL) { MODINIT_ERROR; } /* add extension types to module */ Py_INCREF(&PyVector2_Type); Py_INCREF(&PyVector3_Type); Py_INCREF(&PyVector3_Type); Py_INCREF(&PyVectorElementwiseProxy_Type); Py_INCREF(&PyVectorIter_Type); Py_INCREF(&PyVector_SlerpIter_Type); // Py_INCREF(&PyVector4_Type); if ((PyModule_AddObject(module, "Vector2", (PyObject *)&PyVector2_Type) != 0) || (PyModule_AddObject(module, "Vector3", (PyObject *)&PyVector3_Type) != 0) || (PyModule_AddObject(module, "VectorElementwiseProxy", (PyObject *)&PyVectorElementwiseProxy_Type) != 0) || (PyModule_AddObject(module, "VectorIterator", (PyObject *)&PyVectorIter_Type) != 0) || (PyModule_AddObject(module, "VectorSlerpIterator", (PyObject *)&PyVector_SlerpIter_Type) != 0) /*|| (PyModule_AddObject(module, "Vector4", (PyObject *)&PyVector4_Type) != 0)*/) { Py_DECREF(&PyVector2_Type); Py_DECREF(&PyVector3_Type); Py_DECREF(&PyVectorElementwiseProxy_Type); Py_DECREF(&PyVectorIter_Type); Py_DECREF(&PyVector_SlerpIter_Type); // Py_DECREF(&PyVector4_Type); DECREF_MOD (module); MODINIT_ERROR; } /* export the C api */ c_api[0] = &PyVector2_Type; c_api[1] = &PyVector3_Type; // c_api[2] = &PyVector4_Type; // c_api[3] = PyVector_NEW; // c_api[4] = PyVectorCompatible_Check; apiobj = PyCObject_FromVoidPtr(c_api, NULL); if (apiobj == NULL) { DECREF_MOD (module); MODINIT_ERROR; } if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj) != 0) { Py_DECREF (apiobj); DECREF_MOD (module); MODINIT_ERROR; } MODINIT_RETURN (module); }
