# Commits

committed 1e46afb

implemented new methods in the math module.

* added "vec1.slerp(vec2, steps)" to Vector2 and Vector3.
This method returns an iterator. This can also scale in radial direction.
* added "as_polar()" and "from_polar((r, phi))" to Vector2.
This converts to and from polar-coordiantes where r is the radial distance and
phi the angle to the positive x-axis.
* added "as_spherical()" and "from_spherical((r, theta, phi))" to Vector3.
This converts to and from spherical-coordiantes where r is the radial distance,
theta the inclination angle and phi the azimutal angle.
* added tests for the above.
* tried to implement the buffer protocol but didn't succeed.
documentation on this could be better. commented out for now.

• Participants
• Parent commits 7d2a16e
• Branches math_module

# File src/math.c

` 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)`
` `
` 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`
`     PyVector *vec;`
` } vector_elementwiseproxy;`
` `
` 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 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_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 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 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);`
` PySequence_AsVectorCoords(PyObject *seq, double *coords, const size_t size)`
` {`
`     int i;`
`-    PyObject *item;`
`-    PyObject *fltobj;`
` `
`     if (PyVector_Check(seq)) {`
`         memcpy(coords, ((PyVector *)seq)->coords, sizeof(double) * size);`
`     }`
` } `
` `
`-`
`+/*`
`+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`
`     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)) {`
`+        PyErr_SetString(PyExc_TypeError, `
`+                        "2-tuple containing r and phi is expected.");`
`+        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."`
`     {"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 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."`
`     },`
`     {"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 */`
` };`
`             axis[i] *= normalizationFactor;`
`     }`
` `
`-    fprintf(stderr, "tja: %f\n", angle); fflush(stderr);`
`     if (angle < epsilon)`
`         memcpy(dst_coords, src_coords, 3 * sizeof(src_coords[0]));`
`     else if (abs(angle - 90) < epsilon) {`
`     return (PyObject*)ret;`
` }`
` `
`+`
` static PyObject *`
` vector3_cross(PyVector *self, PyObject *other)`
` {`
` 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)) {`
`+        PyErr_SetString(PyExc_TypeError, "3-tuple containing r, theta and phi is expected.");`
`+        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[] = {`
`     {"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 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."`
`     },`
`     {"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 PyObject *`
` vectoriter_len(vectoriter *it)`
` {`
`-    Py_ssize_t len;`
`+    Py_ssize_t len = 0;`
`     if (it && it->vec) {`
`         len = it->vec->dim - it->it_index;`
`-        if (len >= 0)`
`-            return PyInt_FromSsize_t(len);`
`     }`
`-    return PyInt_FromLong(0);`
`+    return PyInt_FromSsize_t(len);`
` }`
` `
` static PyMethodDef vectoriter_methods[] = {`
` `
` `
` `
`-`
`-`
`+/********************************************`
`+ * 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);`
`+        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;`
`+        }`
`+        ++(it->it_index);`
`+        memcpy(it->coords, ret->coords, sizeof(it->coords[0]) * it->dim);`
`+        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:Vector2.slerp", &other, &steps_object))`
`+    {`
`+        PyErr_SetString(PyExc_TypeError, "parsing args failed");`
`+        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;`
`+`
`+    it->radial_factor = pow(length2 / length1, 1./it->steps);`
`+    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;`
`+    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 *`
` math_enable_swizzling(PyVector *self)`
` {`
`-    fprintf(stderr, "enableing swizzle C\n"); fflush(stderr);`
`     swizzling_enabled = 1;`
`-/*`
`-    self->ob_type->tp_getattro = (getattrofunc)vector_getAttr_swizzle;`
`-    self->ob_type->tp_setattro = (setattrofunc)vector_setAttr_swizzle;`
`-*/`
`     Py_RETURN_NONE;`
` }`
` `
` math_disable_swizzling(PyVector *self)`
` {`
`     swizzling_enabled = 0;`
`-/*`
`-    self->ob_type->tp_getattro = PyObject_GenericGetAttr;`
`-    self->ob_type->tp_setattro = PyObject_GenericSetAttr;`
`-*/`
`     Py_RETURN_NONE;`
` }`
` `
` `
`     /* initialize the extension types */`
`     if ((PyType_Ready(&PyVector2_Type) < 0) || `
`-        (PyType_Ready(&PyVector3_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;`
`     }`
`     /* 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, "Vector3", (PyObject *)&PyVector3_Type) != 0) ||`
`+        (PyModule_AddObject(module, "VectorElementwiseProxy", (PyObject *)&PyVectorElementwiseProxy_Type) != 0) ||`
`+        (PyModule_AddObject(module, "VectorIterator", (PyObject *)&PyVectorIter_Type) != 0) ||`
`+        (PyModule_AddObject(module, "Vector_SlerpIterator", (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;`

# File test/math_test.py

`         self.assertRaises(ValueError, lambda : pow(Vector2(-1, 0).elementwise(), 1.2))`
`         self.assertRaises(ZeroDivisionError, lambda : self.zeroVec.elementwise() ** -1)`
` `
`+    def test_slerp(self):`
`+        steps = 10`
`+        self.assertRaises(ZeroDivisionError,`
`+                          lambda : self.zeroVec.slerp(self.v1, steps))`
`+        self.assertRaises(ZeroDivisionError,`
`+                          lambda : self.v1.slerp(self.zeroVec, steps))`
`+        self.assertRaises(ZeroDivisionError,`
`+                          lambda : self.zeroVec.slerp(self.zeroVec, steps))`
`+        v1 = Vector2(1, 0)`
`+        v2 = Vector2(0, 1)`
`+        angle_step = v1.angle_to(v2) / steps`
`+        for i, u in enumerate(v1.slerp(v2, steps)):`
`+            self.assertAlmostEqual(u.length(), 1)`
`+            self.assertAlmostEqual(v1.angle_to(u), (i + 1) * angle_step)`
`+        self.assertEqual(u, v2)`
`+        `
`+        v1 = Vector2(100, 0)`
`+        v2 = Vector2(0, 10)`
`+        radial_factor = v2.length() / v1.length()`
`+        for i, u in enumerate(v1.slerp(v2, -steps)):`
`+            self.assertAlmostEqual(u.length(), v1.length() * radial_factor**(float(i+1)/steps))`
`+        self.assertEqual(u, v2)`
` `
`-`
`+    def test_polar(self):`
`+        v = Vector2()`
`+        v.from_polar(self.v1.as_polar())`
`+        self.assertEqual(self.v1, v)`
`+        self.assertEqual(self.e1.as_polar(), (1, 0))`
`+        self.assertEqual(self.e2.as_polar(), (1, math.pi / 2))`
`+        self.assertEqual((2 * self.e2).as_polar(),`
`+                         (2, math.pi / 2))`
`+        self.assertRaises(TypeError, lambda : v.from_polar((None, None)))`
`+        self.assertRaises(TypeError, lambda : v.from_polar("ab"))`
`+        self.assertRaises(TypeError, lambda : v.from_polar((None, 1)))`
`+        self.assertRaises(TypeError, lambda : v.from_polar((1, 2, 3)))`
`+        self.assertRaises(TypeError, lambda : v.from_polar((1,)))`
`+        self.assertRaises(TypeError, lambda : v.from_polar(1, 2))`
`+        v.from_polar((.5, math.pi / 2.))`
`+        self.assertEqual(v, .5 * self.e2)`
` `
` `
` `
`         self.assertEqual(self.v1.cross(self.v1), self.zeroVec)`
` `
`     def test_dot(self):`
`-        self.assertEqual(self.v1.dot(self.v2),`
`-                         self.v1.x * self.v2.x + self.v1.y * self.v2.y + self.v1.z * self.v2.z)`
`-        self.assertEqual(self.v1.dot(self.l2),`
`-                         self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + self.v1.z * self.l2[2])`
`-        self.assertEqual(self.v1.dot(self.t2),`
`+        self.assertAlmostEqual(self.v1.dot(self.v2),`
`+                               self.v1.x * self.v2.x + self.v1.y * self.v2.y + self.v1.z * self.v2.z)`
`+        self.assertAlmostEqual(self.v1.dot(self.l2),`
`+                               self.v1.x * self.l2[0] + self.v1.y * self.l2[1] + self.v1.z * self.l2[2])`
`+        self.assertAlmostEqual(self.v1.dot(self.t2),`
`                          self.v1.x * self.t2[0] + self.v1.y * self.t2[1] + self.v1.z * self.t2[2])`
`-        self.assertEqual(self.v1.dot(self.v2), self.v2.dot(self.v1))`
`-        self.assertEqual(self.v1.dot(self.v2), self.v1 * self.v2)`
`+        self.assertAlmostEqual(self.v1.dot(self.v2), self.v2.dot(self.v1))`
`+        self.assertAlmostEqual(self.v1.dot(self.v2), self.v1 * self.v2)`
` `
`     def test_angle_to(self):`
`         self.assertEqual(Vector3(1, 1, 0).angle_to((-1, 1, 0)), 90)`
`         self.assertEqual(-self.v1.elementwise(), -self.v1)`
`         self.assertEqual(+self.v1.elementwise(), +self.v1)`
`         self.assertEqual(bool(self.v1.elementwise()), bool(self.v1))`
`-        self.assertEqual(bool(Vector2().elementwise()), bool(Vector2()))`
`+        self.assertEqual(bool(Vector3().elementwise()), bool(Vector3()))`
`         self.assertEqual(self.zeroVec.elementwise() ** 0, (1, 1, 1))`
`         self.assertRaises(ValueError, lambda : pow(Vector3(-1, 0, 0).elementwise(), 1.2))`
`         self.assertRaises(ZeroDivisionError, lambda : self.zeroVec.elementwise() ** -1)`
` `
`+    def test_slerp(self):`
`+        steps = 10`
`+        self.assertRaises(ZeroDivisionError,`
`+                          lambda : self.zeroVec.slerp(self.v1, steps))`
`+        self.assertRaises(ZeroDivisionError,`
`+                          lambda : self.v1.slerp(self.zeroVec, steps))`
`+        self.assertRaises(ZeroDivisionError,`
`+                          lambda : self.zeroVec.slerp(self.zeroVec, steps))`
`+        angle_step = self.e1.angle_to(self.e2) / steps`
`+        for i, u in enumerate(self.e1.slerp(self.e2, steps)):`
`+            self.assertAlmostEqual(u.length(), 1)`
`+            self.assertAlmostEqual(self.e1.angle_to(u), (i + 1) * angle_step)`
`+        self.assertEqual(u, self.e2)`
`+        `
`+        v1 = Vector3(100, 0, 0)`
`+        v2 = Vector3(0, 10, 7)`
`+        radial_factor = v2.length() / v1.length()`
`+        for i, u in enumerate(v1.slerp(v2, -steps)):`
`+            self.assertAlmostEqual(u.length(), v1.length() * radial_factor**(float(i+1)/steps))`
`+        self.assertEqual(u, v2)`
`+`
`+    def test_spherical(self):`
`+        v = Vector3()`
`+        v.from_spherical(self.v1.as_spherical())`
`+        self.assertEqual(self.v1, v)`
`+        self.assertEqual(self.e1.as_spherical(), (1, math.pi / 2., 0))`
`+        self.assertEqual(self.e2.as_spherical(), (1, math.pi / 2., math.pi / 2))`
`+        self.assertEqual(self.e3.as_spherical(), (1, 0, 0))`
`+        self.assertEqual((2 * self.e2).as_spherical(),`
`+                         (2, math.pi / 2., math.pi / 2))`
`+        self.assertRaises(TypeError, lambda : v.from_spherical((None, None, None)))`
`+        self.assertRaises(TypeError, lambda : v.from_spherical("abc"))`
`+        self.assertRaises(TypeError, lambda : v.from_spherical((None, 1, 2)))`
`+        self.assertRaises(TypeError, lambda : v.from_spherical((1, 2, 3, 4)))`
`+        self.assertRaises(TypeError, lambda : v.from_spherical((1, 2)))`
`+        self.assertRaises(TypeError, lambda : v.from_spherical(1, 2, 3))`
`+        v.from_spherical((.5, math.pi / 2., math.pi / 2.))`
`+        self.assertEqual(v, .5 * self.e2)`
`+        `
` `
` `
` `