Commits

marcus  committed 3cf808f

More math module portage (from trunk).

  • Participants
  • Parent commits f8aa195
  • Branches pgreloaded

Comments (0)

Files changed (4)

File doc/src/mathbase.xml

   <desc>
     TODO
   </desc>
+  <func name="vector_from_polar">
+    <call>vector_from_polar (p1, p2) -> Vector2</call>
+    <desc></desc>
+  </func>
+
+
   <class name="Vector">
     <constructor>Vector () -> Vector</constructor>
     <desc>Creates a new Vector.
       This will only set a maximum of :attr:`dimension` values.
       </desc>
     </attr>
+    <attr name="normalized">
+      <desc>Gets whether the :class:`Vector` is normalized.</desc>
+    </attr>
     <method name="normalize">
       <call>normalize () -> Vector</call>
       <desc></desc>
       <call>slerp () -> Vector</call>
       <desc></desc>
     </method>
+    <method name="lerp">
+      <call>lerp () -> Vector</call>
+      <desc></desc>
+    </method>
+    <method name="dot">
+      <call>dot () -> float</call>
+      <desc></desc>
+    </method>
+    <method name="scale_to">
+      <call>scale_to (v) -> Vector</call>
+      <desc></desc>
+    </method>
+    <method name="reflect">
+      <call>reflect () -> Vector</call>
+      <desc></desc>
+    </method>
+    <method name="reflect_ip">
+      <call>reflect_ip () -> None</call>
+      <desc></desc>
+    </method>
+    <method name="distance">
+      <call>distance () -> None</call>
+      <desc></desc>
+    </method>
+    <method name="distance_squared">
+      <call>distance_squared () -> None</call>
+      <desc></desc>
+    </method>
   </class>
   
   <class name="Vector2">
       <call>rotate_ip () -> Vector2</call>
       <desc></desc>
     </method>
+    <method name="as_polar">
+      <call>as_polar () -> float, float</call>
+      <desc></desc>
+    </method>
+    <method name="angle_to">
+      <call>angle_to (v) -> float</call>
+      <desc></desc>
+    </method>
+    <method name="cross">
+      <call>cross (v) -> Vector</call>
+      <desc></desc>
+    </method>
   </class>
 
   <class name="Vector3">

File src/math/mathmod.c

 #include "pgmath.h"
 #include "mathbase_doc.h"
 
+static PyObject* _frompolar (PyObject* mod, PyObject *args);
+
 static PyMethodDef _math_methods[] = {
+    { "vector_from_polar", _frompolar, METH_VARARGS,
+      DOC_BASE_VECTOR_FROM_POLAR },
     { NULL, NULL, 0, NULL },
 };
 
+static PyObject*
+_frompolar (PyObject* mod, PyObject *args)
+{
+    double r, phi, c1, c2;
+
+    if (!PyArg_ParseTuple (args, "dd:vector_from_polar", &r, &phi))
+        return NULL;
+    c1 = r * cos (phi);
+    c2 = r * sin (phi);
+    return PyVector2_New (c1, c2);
+}
+
+/* Internally used methods */
 double
 _ScalarProduct (const double *coords1, const double *coords2, Py_ssize_t size)
 {

File src/math/vector.c

     void *closure);
 static PyObject* _vector_get_length (PyVector *self, void *closure);
 static PyObject* _vector_get_length_squared (PyVector *self, void *closure);
+static PyObject* _vector_get_normalized (PyVector *self, void *closure);
 static PyObject* _vector_normalize (PyVector *self);
 static PyObject* _vector_normalize_ip (PyVector *self);
 static PyObject* _vector_slerp (PyVector *self, PyObject *args);
+static PyObject* _vector_lerp (PyVector *self, PyObject *args);
+static PyObject* _vector_scaletolength (PyVector *self, PyObject *args);
+static PyObject* _vector_reflect (PyVector *self, PyObject *args);
+static PyObject* _vector_reflect_ip (PyVector *self, PyObject *args);
+static PyObject* _vector_distance (PyVector *self, PyObject *args);
+static PyObject* _vector_distance_squared (PyVector *self, PyObject *args);
 
 /* Generic math operations for vectors. */
 static PyObject* _vector_generic_math (PyObject *o1, PyObject *o2, int op);
       DOC_BASE_VECTOR_NORMALIZE_IP },
     { "slerp", (PyCFunction) _vector_slerp, METH_VARARGS,
       DOC_BASE_VECTOR_SLERP },
+    { "lerp", (PyCFunction) _vector_lerp, METH_VARARGS, DOC_BASE_VECTOR_LERP },
+    { "scale_to", (PyCFunction) _vector_scaletolength, METH_VARARGS,
+      DOC_BASE_VECTOR_SCALE_TO },
+    { "reflect", (PyCFunction) _vector_reflect, METH_VARARGS,
+      DOC_BASE_VECTOR_REFLECT },
+    { "reflect_ip", (PyCFunction) _vector_reflect_ip, METH_VARARGS,
+      DOC_BASE_VECTOR_REFLECT_IP },
+    { "distance", (PyCFunction) _vector_distance, METH_VARARGS,
+      DOC_BASE_VECTOR_DISTANCE },
+    { "distance_squared", (PyCFunction) _vector_distance_squared, METH_VARARGS,
+      DOC_BASE_VECTOR_DISTANCE_SQUARED },
     { NULL, NULL, 0, NULL },
 };
 
       NULL },
     { "length_squared", (getter) _vector_get_length_squared, NULL,
       DOC_BASE_VECTOR_LENGTH, NULL },
+    { "normalized", (getter) _vector_get_normalized, NULL,
+      DOC_BASE_VECTOR_NORMALIZED, NULL },
     { NULL, NULL, NULL, NULL, NULL }
 };
 
     return PyFloat_FromDouble (length_squared);
 }
 
+/**
+ * vector.normalized
+ */
+static PyObject*
+_vector_get_normalized (PyVector *self, void *closure)
+{
+    double length_squared = _ScalarProduct (self->coords, self->coords, 
+        self->dim);
+    if (fabs (length_squared - 1) < self->epsilon)
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
+
 /* Methods */
 static PyObject*
 _vector_normalize (PyVector *self)
 }
 
 static PyObject*
-_vector_normalize_ip(PyVector *self)
+_vector_normalize_ip (PyVector *self)
 {
     Py_ssize_t i;
     double length;
-    PyVector *ret;
     
     length = sqrt (_ScalarProduct (self->coords, self->coords, self->dim));
     if (length == 0)
     return (PyObject*) ret;
 }
 
+static PyObject*
+_vector_lerp (PyVector *self, PyObject *args)
+{
+    Py_ssize_t i, otherdim;
+    PyObject *other;
+    PyVector *ret;
+    double t;
+    double *othercoords;
+
+    if (!PyArg_ParseTuple (args, "Od:lerp", &other, &t))
+        return NULL;
+
+    if (!IsVectorCompatible (other))
+    {
+        PyErr_SetString (PyExc_TypeError, "other must be a vector compatible");
+        return NULL;
+    }
+
+    othercoords = VectorCoordsFromObj (other, &otherdim);
+    if (!othercoords)
+        return NULL;
+    if (otherdim != self->dim)
+    {
+        /* TODO: is it okay to fail here? */
+        PyErr_SetString (PyExc_TypeError, "other must have the same dimension");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+
+    if (fabs (t) > 1)
+    {
+        PyErr_SetString (PyExc_ValueError, "t must be in range [-1, 1]");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+
+    ret = (PyVector *)PyVector_NewSpecialized (self->dim);
+    if (!ret)
+    {
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+    for (i = 0; i < self->dim; ++i)
+        ret->coords[i] = self->coords[i] * (1 - t) + othercoords[i] * t;
+    return (PyObject *) ret;
+}
+
+static PyObject*
+_vector_dot (PyVector *self, PyObject *args)
+{
+    PyObject *other;
+    Py_ssize_t otherdim;
+    double* othercoords;
+    double retval, t;
+
+    if (!PyArg_ParseTuple (args, "O:dot", &other, &t))
+        return NULL;
+
+    if (!IsVectorCompatible (other))
+    {
+        PyErr_SetString (PyExc_TypeError, "other must be a vector compatible");
+        return NULL;
+    }
+
+    othercoords = VectorCoordsFromObj (other, &otherdim);
+    if (!othercoords)
+        return NULL;
+    if (otherdim != self->dim)
+    {
+        /* TODO: is it okay to fail here? */
+        PyErr_SetString (PyExc_TypeError, "other must have the same dimension");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+
+    retval = _ScalarProduct (self->coords, othercoords, self->dim);
+    PyMem_Free (othercoords);
+    return PyFloat_FromDouble (retval);
+}
+
+static PyObject*
+_vector_scaletolength (PyVector *self, PyObject *args)
+{
+    Py_ssize_t i;
+    double newlength, oldlength = 0;
+    double fraction;
+
+    if (!PyArg_ParseTuple (args, "d:scale_to", &newlength))
+        return NULL;
+
+    for (i = 0; i < self->dim; ++i)
+        oldlength += self->coords[i] * self->coords[i];
+    oldlength = sqrt (oldlength);
+
+    if (oldlength < self->epsilon)
+    {
+        PyErr_SetString (PyExc_ZeroDivisionError,
+            "cannot scale a vector with zero length");
+        return NULL;
+    }
+
+    fraction = newlength / oldlength;
+    for (i = 0; i < self->dim; ++i)
+        self->coords[i] *= fraction;
+    Py_RETURN_NONE;
+}
+
+static double*
+_do_reflect (const double *srccoords, Py_ssize_t dim, double eps, PyObject *n)
+{
+    double *dstcoords = NULL, *ncoords;
+    double dotprod, nlength;
+    Py_ssize_t ndim, i;
+
+    if (!srccoords || !n)
+    {
+        PyErr_SetString (PyExc_ValueError, "arguments must not be NULL");
+        return NULL;
+    }
+
+    if (!IsVectorCompatible (n))
+    {
+        PyErr_SetString (PyExc_TypeError, "normal must be a vector compatible");
+        return NULL;
+    }
+
+    ncoords = VectorCoordsFromObj (n, &ndim);
+    if (!ncoords)
+        return NULL;
+    if (ndim < dim)
+    {
+        PyErr_SetString (PyExc_ValueError,
+            "normal must have same dimension as vector");
+        goto ret;
+    }
+
+    /* Normalize the normal */
+    nlength = 0;
+    for (i = 0; i < ndim; i++)
+        nlength += ncoords[i] * ncoords[i];
+    if (nlength < eps)
+    {
+        PyErr_SetString (PyExc_ZeroDivisionError,
+            "normal must not be a zero-length vector");
+        goto ret;
+    }
+
+    if (nlength != 1.f)
+    {
+        nlength = sqrt (nlength);
+        for (i = 0; i < ndim; i++)
+            ncoords[i] /= nlength;
+    }
+
+    /* Calculate the dot product for the projection. */
+    dotprod = 0;
+    for (i = 0; i < dim; i++)
+        dotprod += srccoords[i] * ncoords[i];
+        
+    dstcoords = PyMem_New (double, dim);
+    if (!dstcoords)
+        goto ret;
+    for (i = 0; i < dim; i++)
+        dstcoords[i] = srccoords[i] - 2 * ncoords[i] * dotprod;
+
+ret:
+    PyMem_Free (ncoords);
+    return dstcoords;
+}
+
+static PyObject*
+_vector_reflect (PyVector *self, PyObject *args)
+{
+    PyObject *normal;
+    PyVector *retval;
+    double *dstcoords;
+
+    if (!PyArg_ParseTuple (args, "O:reflect", &normal))
+        return NULL;
+
+    dstcoords = _do_reflect (self->coords, self->dim, self->epsilon, normal);
+    if (!dstcoords)
+        return NULL;
+    retval = (PyVector*) PyVector_NewSpecialized (self->dim);
+    if (!retval)
+    {
+        PyMem_Free (dstcoords);
+        return NULL;
+    }
+    PyMem_Free (retval->coords);
+    retval->coords = dstcoords;
+    return (PyObject*) retval;
+}
+
+static PyObject*
+_vector_reflect_ip (PyVector *self, PyObject *args)
+{
+    PyObject *normal;
+    double *dstcoords;
+
+    if (!PyArg_ParseTuple (args, "O:reflect", &normal))
+        return NULL;
+
+    dstcoords = _do_reflect (self->coords, self->dim, self->epsilon, normal);
+    if (!dstcoords)
+        return NULL;
+    PyMem_Free (self->coords);
+    self->coords = dstcoords;
+    Py_RETURN_NONE;
+}
+
+static PyObject*
+_vector_distance (PyVector *self, PyObject *args)
+{
+    PyObject *other;
+    Py_ssize_t otherdim, i;
+    double *othercoords, distance, tmp;
+
+    if (!PyArg_ParseTuple (args, "O:reflect", &other))
+        return NULL;
+
+    if (!IsVectorCompatible (other))
+    {
+        PyErr_SetString (PyExc_TypeError, "other must be a vector compatible");
+        return NULL;
+    }
+
+    othercoords = VectorCoordsFromObj (other, &otherdim);
+    if (!othercoords)
+        return NULL;
+    if (otherdim < self->dim)
+    {
+        PyErr_SetString (PyExc_ValueError,
+            "must have same at least the same dimension as vector");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+    distance = 0;
+    for (i = 0; i < self->dim; i++)
+    {
+        tmp = othercoords[i] - self->coords[i];
+        distance += tmp * tmp;
+    }
+    PyMem_Free (othercoords);
+    return PyFloat_FromDouble (sqrt (distance));
+}
+
+static PyObject*
+_vector_distance_squared (PyVector *self, PyObject *args)
+{
+    PyObject *other;
+    Py_ssize_t otherdim, i;
+    double *othercoords, distance, tmp;
+
+    if (!PyArg_ParseTuple (args, "O:reflect", &other))
+        return NULL;
+
+    if (!IsVectorCompatible (other))
+    {
+        PyErr_SetString (PyExc_TypeError, "other must be a vector compatible");
+        return NULL;
+    }
+
+    othercoords = VectorCoordsFromObj (other, &otherdim);
+    if (!othercoords)
+        return NULL;
+    if (otherdim < self->dim)
+    {
+        PyErr_SetString (PyExc_ValueError,
+            "must have same at least the same dimension as vector");
+        PyMem_Free (othercoords);
+        return NULL;
+    }
+    distance = 0;
+    for (i = 0; i < self->dim; i++)
+    {
+        tmp = othercoords[i] - self->coords[i];
+        distance += tmp * tmp;
+    }
+    PyMem_Free (othercoords);
+    return PyFloat_FromDouble (distance);
+}
 
 static PyObject*
 _vector_generic_math (PyObject *o1, PyObject *o2, int op)

File src/math/vector2.c

 
 static PyObject* _vector2_rotate (PyObject *self, PyObject *args);
 static PyObject* _vector2_rotate_ip (PyObject *self, PyObject *args);
+static PyObject* _vector2_cross (PyObject *self, PyObject *args);
+static PyObject* _vector2_angleto (PyObject *self, PyObject *args);
+static PyObject* _vector2_aspolar (PyVector *self);
 
 /**
  * Methods for the PyVector2.
     { "rotate", _vector2_rotate, METH_VARARGS, DOC_BASE_VECTOR2_ROTATE },
     { "rotate_ip", _vector2_rotate_ip, METH_VARARGS,
       DOC_BASE_VECTOR2_ROTATE_IP },
+    { "cross", _vector2_cross, METH_VARARGS, DOC_BASE_VECTOR2_CROSS },
+    { "angle_to", _vector2_angleto, METH_VARARGS, DOC_BASE_VECTOR2_ANGLE_TO },
+    { "as_polar", _vector2_aspolar, METH_VARARGS, DOC_BASE_VECTOR2_AS_POLAR },
     { NULL, NULL, 0, NULL },
 };
 
     Py_RETURN_NONE;
 }
 
+static PyObject*
+_vector2_cross (PyObject *self, PyObject *args)
+{
+    PyObject *other;
+    PyVector *v = (PyVector*) self;
+    double retval, *othercoords;
+    Py_ssize_t otherdim;
+
+    if (!PyArg_ParseTuple (args, "O:cross", &other))
+        return NULL;
+
+    if (!IsVectorCompatible (other))
+    {
+        PyErr_SetString (PyExc_TypeError, "other must be a vector compatible");
+        return NULL;
+    }
+
+    othercoords = VectorCoordsFromObj (other, &otherdim);
+    if (!othercoords)
+        return NULL;
+    retval = v->coords[0] * othercoords[1] - v->coords[1] * othercoords[0];
+    PyMem_Free (othercoords);
+    return PyFloat_FromDouble (retval);
+}
+
+static PyObject*
+_vector2_angleto (PyObject *self, PyObject *args)
+{
+    double angle;
+    PyObject *other;
+    PyVector *v = (PyVector*) self;
+    double retval, *othercoords;
+    Py_ssize_t otherdim;
+
+    if (!PyArg_ParseTuple (args, "O:cross", &other))
+        return NULL;
+
+    if (!IsVectorCompatible (other))
+    {
+        PyErr_SetString (PyExc_TypeError, "other must be a vector compatible");
+        return NULL;
+    }
+
+    othercoords = VectorCoordsFromObj (other, &otherdim);
+    if (!othercoords)
+        return NULL;
+    angle = atan2 (othercoords[1], othercoords[0]) -
+        atan2 (v->coords[1], v->coords[0]);
+    PyMem_Free (othercoords);
+    return PyFloat_FromDouble (RAD2DEG (angle));
+}
+
+
+static PyObject *
+_vector2_aspolar (PyVector *self)
+{
+    double r, phi;
+    r = sqrt(_ScalarProduct (self->coords, self->coords, self->dim));
+    phi = atan2 (self->coords[1], self->coords[0]);
+    return Py_BuildValue ("(dd)", r, phi);
+}
+
 /* C API */
 PyObject*
 PyVector2_New (double x, double y)