Anonymous avatar Anonymous committed 80abf33

added the following methods to Vector2
* length
* length_squared
* reflect
* refelct_ip
also included unit tests for the above

Comments (0)

Files changed (2)

 }
 
 
+static PyObject *
+vector_length(PyVector *self)
+{
+    int i;
+    double length = 0;
+    for (i = 0; i < self->dim; ++i)
+        length += self->coords[i] * self->coords[i];
+    return PyFloat_FromDouble(sqrt(length));
+}
+
+static PyObject *
+vector_length_squared(PyVector *self)
+{
+    int i;
+    double length_squared = 0;
+    for (i = 0; i < self->dim; ++i)
+        length_squared += self->coords[i] * self->coords[i];
+    return PyFloat_FromDouble(length_squared);
+}
+
 static void
 vector2_do_rotate(double *dst_coords, const double *src_coords, double angle)
 {
     Py_RETURN_NONE;
 }
 
+static PyObject *
+vector_reflect(PyVector *self, PyObject *normal)
+{
+    int i, dim = self->dim;
+    PyVector *ret;
+    double dot_product;
+    double norm_length;
+    /* allocate enough space for 2, 3 and 4 dim vectors */
+    double norm_coords[4];
+
+    if (!checkPyVectorCompatible(normal, dim)) {
+        PyErr_SetString(PyExc_TypeError, "Expected a vector.");
+        return NULL;
+    }
+
+    /* 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 < self->epsilon) {
+        PyErr_SetString(PyExc_ZeroDivisionError, "Normal must not be of length zero.");
+        return NULL;
+    }
+    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 += self->coords[i] * norm_coords[i];
+    
+    ret = (PyVector *)PyVector_NEW(dim);
+    for (i = 0; i < dim; ++i)
+        ret->coords[i] = self->coords[i] - 2 * norm_coords[i] * dot_product;
+
+    return (PyObject *)ret;
+}
+
+static PyObject *
+vector_reflect_ip(PyVector *self, PyObject *normal)
+{
+    int i, dim = self->dim;
+    double dot_product;
+    double norm_length;
+    /* allocate enough space for 2, 3 and 4 dim vectors */
+    double norm_coords[4];
+
+    if (!checkPyVectorCompatible(normal, dim)) {
+        PyErr_SetString(PyExc_TypeError, "Expected a vector.");
+        return NULL;
+    }
+
+    /* 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 < self->epsilon) {
+        PyErr_SetString(PyExc_ZeroDivisionError, "Normal must not be of length zero.");
+        return NULL;
+    }
+    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 += self->coords[i] * norm_coords[i];
+    
+    for (i = 0; i < dim; ++i)
+        self->coords[i] -= 2 * norm_coords[i] * dot_product;
+
+    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."
     },
      "returns the angle between self and the given vector."
     },
     {"scale_to_length", (PyCFunction)vector_scale_to_length, METH_O,
-     "scalesthe vector to the given length."
+     "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."
     },
     
     {NULL}  /* Sentinel */
 
 
 /********************************
- * PyVector2d type definition
+ * PyVector2 type definition
  ********************************/
 
 static PyTypeObject PyVector2_Type = {
 };
 
 
+
 static PyMethodDef _math_methods[] =
 {
     {NULL, NULL, 0, NULL}

test/math_test.py

     from test.test_utils import test_not_implemented, unittest
 
 from pygame.math import Vector2
-
+from time import clock
 
 class Vector2TypeTest(unittest.TestCase):
     def testConstructionDefault(self):
         self.assertRaises(ZeroDivisionError, lambda : Vector2().scale_to_length(1))
         self.assertEqual(v1.scale_to_length(0), None)
         self.assertEqual(v1, Vector2())
+
+    def test_length(self):
+        self.assertEqual(Vector2(3, 4).length(), 5)
+        self.assertEqual(Vector2(-3, 4).length(), 5)
+        self.assertEqual(Vector2().length(), 0)
+        
+    def test_length_squared(self):
+        self.assertEqual(Vector2(3, 4).length_squared(), 25)
+        self.assertEqual(Vector2(-3, 4).length_squared(), 25)
+        self.assertEqual(Vector2().length_squared(), 0)
+
+    def test_reflect(self):
+        v = Vector2(1, -1)
+        n = Vector2(0, 1)
+        self.assertEqual(v.reflect(n), Vector2(1, 1))
+        self.assertEqual(v.reflect(3*n), v.reflect(n))
+        self.assertEqual(v.reflect(-v), -v)
+        self.assertRaises(ZeroDivisionError, lambda : v.reflect(Vector2()))
+        
+    def test_reflect_ip(self):
+        v1 = Vector2(1, -1)
+        v2 = Vector2(v1)
+        n = Vector2(0, 1)
+        self.assertEqual(v2.reflect_ip(n), None)
+        self.assertEqual(v2, Vector2(1, 1))
+        v2 = Vector2(v1)
+        v2.reflect_ip(3*n)
+        self.assertEqual(v2, v1.reflect(n))
+        v2 = Vector2(v1)
+        v2.reflect_ip(-v1)
+        self.assertEqual(v2, -v1)
+        self.assertRaises(ZeroDivisionError, lambda : v2.reflect_ip(Vector2()))
+
+    def testIterProc(self):
+        v = Vector2(1.2, 3.4)
+        it = v.__iter__()
+        self.assertEqual(it.next(), 1.2)
+        self.assertEqual(it.next(), 3.4)
+        self.assertRaises(StopIteration, lambda : it.next())
+        idx = 0
+        for val in v:
+            self.assertEqual(val, v[idx])
+            idx += 1
         
 if __name__ == '__main__':
     unittest.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.