diff src/math.c @ 1840:0959bd773ffd math_module

added support for swizzling. added methods "enable_swizzle()" and "disable_swizzle()"; default: disabled added test for swizzling
author lorenz
date Sun, 26 Jul 2009 14:06:42 +0000
parents e4fd799c194b
children a7dc31e50c46
line wrap: on
line diff
--- a/src/math.c	Sun Jul 19 15:05:09 2009 +0000
+++ b/src/math.c	Sun Jul 26 14:06:42 2009 +0000
@@ -26,6 +26,9 @@
 #include <math.h>
 
 #define STRING_BUF_SIZE (100)
+#define SWIZZLE_ERR_NO_ERR         0
+#define SWIZZLE_ERR_DOUBLE_IDX     1
+#define SWIZZLE_ERR_EXTRACTION_ERR 2
 
 static PyTypeObject PyVector2_Type;
 #define PyVector2_Check(x) ((x)->ob_type == &PyVector2_Type)
@@ -55,8 +58,12 @@
     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_XDECREF(item);
+    Py_DECREF(item);
     if (!fltobj) {
         PyErr_SetString(PyExc_TypeError, "a float is required");
         return -1;
@@ -623,6 +630,7 @@
 static int
 vector_setx (PyVector *self, PyObject *value, void *closure)
 {
+    /* NOTE: this is never called when swizzling is enabled */
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError, "Cannot delete the x attribute");
         return -1;
@@ -641,6 +649,7 @@
 static int
 vector_sety (PyVector *self, PyObject *value, void *closure)
 {
+    /* NOTE: this is never called when swizzling is enabled */
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError, "Cannot delete the y attribute");
         return -1;
@@ -1108,7 +1117,36 @@
 }
 
 
+static int 
+vector_setAttr_swizzle(PyVector *self, PyObject *attr_name, PyObject *val);
+static PyObject *
+vector_getAttr_swizzle(PyVector *self, PyObject *attr_name);
+
+static PyObject *
+vector_enable_swizzle(PyVector *self)
+{
+    self->ob_type->tp_getattro = (getattrofunc)vector_getAttr_swizzle;
+    self->ob_type->tp_setattro = (setattrofunc)vector_setAttr_swizzle;
+    Py_RETURN_NONE;
+}
+
+static PyObject *
+vector_disable_swizzle(PyVector *self)
+{
+    self->ob_type->tp_getattro = PyObject_GenericGetAttr;
+    self->ob_type->tp_setattro = PyObject_GenericSetAttr;
+    Py_RETURN_NONE;
+}
+
+
+
 static PyMethodDef vector2_methods[] = {
+    {"enable_swizzle", (PyCFunction)vector_enable_swizzle, METH_NOARGS,
+     "enables swizzling."
+    },
+    {"disable_swizzle", (PyCFunction)vector_disable_swizzle, METH_NOARGS,
+     "disables swizzling."
+    },
     {"length", (PyCFunction)vector_length, METH_NOARGS,
      "returns the length/magnitude of the vector."
     },
@@ -1167,8 +1205,7 @@
     
     bufferIdx = 1;
     PyOS_snprintf(buffer[0], STRING_BUF_SIZE, "<Vector%d(", self->dim);
-    for (i = 0; i < self->dim - 1; ++i)
-    {
+    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++;
@@ -1199,8 +1236,7 @@
     
     bufferIdx = 1;
     PyOS_snprintf(buffer[0], STRING_BUF_SIZE, "[", self->dim);
-    for (i = 0; i < self->dim - 1; ++i)
-    {
+    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++;
@@ -1221,6 +1257,97 @@
 }
 */
 
+static PyObject*
+vector_getAttr_swizzle(PyVector *self, PyObject *attr_name)
+{
+    PyObject *res = PyObject_GenericGetAttr(self, attr_name);
+    /* if normal lookup failed try to swizzle */
+    if (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)
+{
+    // first try swizzle
+    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;
+
+    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 normal 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 NOT happen */
+        PyErr_SetString(PyExc_RuntimeError, "Unhandled error in swizzle code");
+        return -1;
+    }
+} 
+
 
 static PyGetSetDef vector2_getsets[] = {
     { "x", (getter)vector_getx, (setter)vector_setx, NULL, NULL },
@@ -1255,13 +1382,13 @@
     0,                         /* tp_hash */
     0,                         /* tp_call */
     (reprfunc)vector_str,      /* tp_str */
-    PyObject_GenericGetAttr,   /* tp_getattro */
-    0,                         /* tp_setattro */
+    (getattrofunc)PyObject_GenericGetAttr, /* tp_getattro */
+    (setattrofunc)PyObject_GenericSetAttr, /* 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 | Py_TPFLAGS_HAVE_INPLACEOPS, /* tp_flags */
+    Py_TPFLAGS_CHECKTYPES, /* tp_flags */
     /* Documentation string */
     DOC_PYGAMEVECTOR2,         /* tp_doc */