summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2011-05-20 13:18:41 -0600
committerTavian Barnes <tavianator@gmail.com>2011-05-20 13:18:41 -0600
commit387c1b8b1fcf80233d3fc73aa3be766bfea83dc8 (patch)
tree934405241c57a7c75da50ce9ed62170689fb8c0e
parent7c0e81ee456be8eded08da4b41b361e1db5416f1 (diff)
downloaddimension-387c1b8b1fcf80233d3fc73aa3be766bfea83dc8.tar.xz
Add Colors to the Python extension.
-rw-r--r--libdimension-python/Color.c310
-rw-r--r--libdimension-python/Color.h43
-rw-r--r--libdimension-python/Makefile.am4
-rw-r--r--libdimension-python/Scene.c1
-rw-r--r--libdimension-python/Vector.c32
-rw-r--r--libdimension-python/Vector.h6
-rw-r--r--libdimension-python/dimension.c40
-rw-r--r--libdimension-python/tests/Makefile.am1
-rwxr-xr-xlibdimension-python/tests/color.py55
9 files changed, 461 insertions, 31 deletions
diff --git a/libdimension-python/Color.c b/libdimension-python/Color.c
new file mode 100644
index 0000000..ae8de2f
--- /dev/null
+++ b/libdimension-python/Color.c
@@ -0,0 +1,310 @@
+/*************************************************************************
+ * Copyright (C) 2009-2011 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Python Module. *
+ * *
+ * The Dimension Python Module is free software; you can redistribute it *
+ * and/ or modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either version *
+ * 3 of the License, or (at your option) any later version. *
+ * *
+ * The Dimension Python Module 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 Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#include "Color.h"
+
+bool
+dmnsn_py_Color_args(dmnsn_color *c, PyObject *args, PyObject *kwds)
+{
+ c->filter = 0.0;
+ c->trans = 0.0;
+
+ static char *kwlist[] = { "red", "green", "blue", "filter", "trans", NULL };
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "ddd|dd", kwlist,
+ &c->R, &c->G, &c->B, &c->filter, &c->trans)) {
+ return true;
+ } else {
+ if (kwds)
+ return false;
+
+ PyErr_Clear();
+
+ dmnsn_py_Color *col;
+ if (!PyArg_ParseTuple(args, "O!", &dmnsn_py_ColorType, &col))
+ return false;
+
+ *c = col->c;
+ return true;
+ }
+}
+
+static int
+dmnsn_py_Color_init(dmnsn_py_Color *self, PyObject *args, PyObject *kwds)
+{
+ return dmnsn_py_Color_args(&self->c, args, kwds) ? 0 : -1;
+}
+
+static PyObject *
+dmnsn_py_Color_repr(dmnsn_py_Color *self)
+{
+ PyObject *R = PyFloat_FromDouble(self->c.R);
+ PyObject *G = PyFloat_FromDouble(self->c.G);
+ PyObject *B = PyFloat_FromDouble(self->c.B);
+ PyObject *filter = PyFloat_FromDouble(self->c.filter);
+ PyObject *trans = PyFloat_FromDouble(self->c.trans);
+
+ if (!R || !G || !B || !filter || !trans) {
+ Py_XDECREF(trans);
+ Py_XDECREF(filter);
+ Py_XDECREF(B);
+ Py_XDECREF(G);
+ Py_XDECREF(R);
+ return NULL;
+ }
+
+ PyObject *repr = PyUnicode_FromFormat("dimension.Color(%R, %R, %R, %R, %R)",
+ R, G, B, filter, trans);
+ Py_XDECREF(trans);
+ Py_XDECREF(filter);
+ Py_XDECREF(B);
+ Py_XDECREF(G);
+ Py_XDECREF(R);
+ return repr;
+}
+
+static PyObject *
+dmnsn_py_Color_str(dmnsn_py_Color *self)
+{
+ PyObject *R = PyFloat_FromDouble(self->c.R);
+ PyObject *G = PyFloat_FromDouble(self->c.G);
+ PyObject *B = PyFloat_FromDouble(self->c.B);
+ PyObject *filter = PyFloat_FromDouble(self->c.filter);
+ PyObject *trans = PyFloat_FromDouble(self->c.trans);
+
+ if (!R || !G || !B || !filter || !trans) {
+ Py_XDECREF(trans);
+ Py_XDECREF(filter);
+ Py_XDECREF(B);
+ Py_XDECREF(G);
+ Py_XDECREF(R);
+ return NULL;
+ }
+
+ PyObject *str;
+ if (self->c.filter < dmnsn_epsilon && self->c.trans < dmnsn_epsilon) {
+ str = PyUnicode_FromFormat("<red = %S, green = %S, blue = %S>",
+ R, G, B);
+ } else {
+ str = PyUnicode_FromFormat("<red = %S, green = %S, blue = %S,"
+ " filter = %S, trans = %S>",
+ R, G, B, filter, trans);
+ }
+ Py_XDECREF(trans);
+ Py_XDECREF(filter);
+ Py_XDECREF(B);
+ Py_XDECREF(G);
+ Py_XDECREF(R);
+ return str;
+}
+
+static PyObject *
+dmnsn_py_Color_richcompare(PyObject *lhs, PyObject *rhs, int op)
+{
+ if (!PyObject_TypeCheck(lhs, &dmnsn_py_ColorType)
+ || !PyObject_TypeCheck(rhs, &dmnsn_py_ColorType))
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "Colors can only be compared with Colors");
+ return NULL;
+ }
+
+ dmnsn_py_Color *clhs = (dmnsn_py_Color *)lhs;
+ dmnsn_py_Color *crhs = (dmnsn_py_Color *)rhs;
+
+ double rdiff = (clhs->c.R - crhs->c.R)*(clhs->c.R - crhs->c.R);
+ double gdiff = (clhs->c.G - crhs->c.G)*(clhs->c.G - crhs->c.G);
+ double bdiff = (clhs->c.B - crhs->c.B)*(clhs->c.B - crhs->c.B);
+ double fdiff = (clhs->c.filter - crhs->c.filter)
+ * (clhs->c.filter - crhs->c.filter);
+ double tdiff = (clhs->c.trans - crhs->c.trans)
+ * (clhs->c.trans - crhs->c.trans);
+ bool equal = sqrt(rdiff + gdiff + bdiff + fdiff + tdiff) < dmnsn_epsilon;
+
+ PyObject *result;
+ switch (op) {
+ case Py_EQ:
+ result = equal ? Py_True : Py_False;
+ break;
+ case Py_NE:
+ result = !equal ? Py_True : Py_False;
+ break;
+ default:
+ result = Py_NotImplemented;
+ break;
+ }
+
+ Py_INCREF(result);
+ return result;
+}
+
+static PyObject *
+dmnsn_py_Color_add(PyObject *lhs, PyObject *rhs)
+{
+ if (!PyObject_TypeCheck(lhs, &dmnsn_py_ColorType)
+ || !PyObject_TypeCheck(rhs, &dmnsn_py_ColorType))
+ {
+ PyErr_SetString(PyExc_TypeError,
+ "Colors can only be added to Colors");
+ return NULL;
+ }
+
+ dmnsn_py_Color *ret = PyObject_New(dmnsn_py_Color, &dmnsn_py_ColorType);
+ if (ret) {
+ dmnsn_py_Color *clhs = (dmnsn_py_Color *)lhs;
+ dmnsn_py_Color *crhs = (dmnsn_py_Color *)rhs;
+ ret->c = dmnsn_color_add(clhs->c, crhs->c);
+ }
+ return (PyObject *)ret;
+}
+
+static PyObject *
+dmnsn_py_Color_mul(PyObject *lhs, PyObject *rhs)
+{
+ dmnsn_py_Color *col;
+ double dbl;
+
+ if (PyObject_TypeCheck(lhs, &dmnsn_py_ColorType)) {
+ col = (dmnsn_py_Color *)lhs;
+ dbl = PyFloat_AsDouble(rhs);
+ if (PyErr_Occurred())
+ return NULL;
+ } else {
+ col = (dmnsn_py_Color *)rhs;
+ dbl = PyFloat_AsDouble(lhs);
+ if (PyErr_Occurred())
+ return NULL;
+ }
+
+ dmnsn_py_Color *ret = PyObject_New(dmnsn_py_Color, &dmnsn_py_ColorType);
+ if (ret) {
+ ret->c = dmnsn_color_mul(dbl, col->c);
+ }
+ return (PyObject *)ret;
+}
+
+static int
+dmnsn_py_Color_bool(PyObject *obj)
+{
+ dmnsn_py_Color *col = (dmnsn_py_Color *)obj;
+ return !dmnsn_color_is_black(col->c);
+}
+
+static PyNumberMethods dmnsn_py_Color_as_number = {
+ .nb_add = dmnsn_py_Color_add,
+ .nb_multiply = dmnsn_py_Color_mul,
+ .nb_bool = dmnsn_py_Color_bool,
+};
+
+static PyMethodDef dmnsn_py_Color_methods[] = {
+ { NULL }
+};
+
+static PyObject *
+dmnsn_py_Color_get_red(dmnsn_py_Color *self, void *closure)
+{
+ return PyFloat_FromDouble(self->c.R);
+}
+
+static PyObject *
+dmnsn_py_Color_get_green(dmnsn_py_Color *self, void *closure)
+{
+ return PyFloat_FromDouble(self->c.G);
+}
+
+static PyObject *
+dmnsn_py_Color_get_blue(dmnsn_py_Color *self, void *closure)
+{
+ return PyFloat_FromDouble(self->c.B);
+}
+
+static PyObject *
+dmnsn_py_Color_get_filter(dmnsn_py_Color *self, void *closure)
+{
+ return PyFloat_FromDouble(self->c.filter);
+}
+
+static PyObject *
+dmnsn_py_Color_get_trans(dmnsn_py_Color *self, void *closure)
+{
+ return PyFloat_FromDouble(self->c.trans);
+}
+
+static PyGetSetDef dmnsn_py_Color_getsetters[] = {
+ { "red", (getter)dmnsn_py_Color_get_red, NULL,
+ "Red component", NULL },
+ { "green", (getter)dmnsn_py_Color_get_green, NULL,
+ "Green componant", NULL },
+ { "blue", (getter)dmnsn_py_Color_get_blue, NULL,
+ "Blue componant", NULL },
+ { "filter", (getter)dmnsn_py_Color_get_filter, NULL,
+ "Filter component", NULL },
+ { "trans", (getter)dmnsn_py_Color_get_trans, NULL,
+ "Transmittance component", NULL },
+ { NULL }
+};
+
+PyTypeObject dmnsn_py_ColorType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "dimension.Color",
+ .tp_basicsize = sizeof(dmnsn_py_Color),
+ .tp_repr = (reprfunc)dmnsn_py_Color_repr,
+ .tp_str = (reprfunc)dmnsn_py_Color_str,
+ .tp_as_number = &dmnsn_py_Color_as_number,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = "Dimension color",
+ .tp_richcompare = dmnsn_py_Color_richcompare,
+ .tp_methods = dmnsn_py_Color_methods,
+ .tp_getset = dmnsn_py_Color_getsetters,
+ .tp_init = (initproc)dmnsn_py_Color_init,
+};
+
+#define dmnsn_py_Color_global(name) \
+ dmnsn_py_Color name = { \
+ PyObject_HEAD_INIT(&dmnsn_py_ColorType) \
+ };
+
+dmnsn_py_Color_global(dmnsn_py_Black);
+dmnsn_py_Color_global(dmnsn_py_White);
+dmnsn_py_Color_global(dmnsn_py_Clear);
+dmnsn_py_Color_global(dmnsn_py_Red);
+dmnsn_py_Color_global(dmnsn_py_Green);
+dmnsn_py_Color_global(dmnsn_py_Blue);
+dmnsn_py_Color_global(dmnsn_py_Magenta);
+dmnsn_py_Color_global(dmnsn_py_Orange);
+dmnsn_py_Color_global(dmnsn_py_Yellow);
+dmnsn_py_Color_global(dmnsn_py_Cyan);
+
+bool
+dmnsn_py_init_ColorType(void)
+{
+ dmnsn_py_Black.c = dmnsn_black;
+ dmnsn_py_White.c = dmnsn_white;
+ dmnsn_py_Clear.c = dmnsn_clear;
+ dmnsn_py_Red.c = dmnsn_red;
+ dmnsn_py_Green.c = dmnsn_green;
+ dmnsn_py_Blue.c = dmnsn_blue;
+ dmnsn_py_Magenta.c = dmnsn_magenta;
+ dmnsn_py_Orange.c = dmnsn_orange;
+ dmnsn_py_Yellow.c = dmnsn_yellow;
+ dmnsn_py_Cyan.c = dmnsn_cyan;
+
+ dmnsn_py_ColorType.tp_new = PyType_GenericNew;
+ return PyType_Ready(&dmnsn_py_ColorType) >= 0;
+}
diff --git a/libdimension-python/Color.h b/libdimension-python/Color.h
new file mode 100644
index 0000000..1fbf5ed
--- /dev/null
+++ b/libdimension-python/Color.h
@@ -0,0 +1,43 @@
+/*************************************************************************
+ * Copyright (C) 2009-2011 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Python Module. *
+ * *
+ * The Dimension Python Module is free software; you can redistribute it *
+ * and/ or modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either version *
+ * 3 of the License, or (at your option) any later version. *
+ * *
+ * The Dimension Python Module 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 Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#include "dimension-python.h"
+
+typedef struct dmnsn_py_Color {
+ PyObject_HEAD
+ dmnsn_color c;
+} dmnsn_py_Color;
+
+extern PyTypeObject dmnsn_py_ColorType;
+
+bool dmnsn_py_Color_args(dmnsn_color *v, PyObject *args, PyObject *kwds);
+bool dmnsn_py_init_ColorType(void);
+
+/* Color constants */
+extern dmnsn_py_Color dmnsn_py_Black;
+extern dmnsn_py_Color dmnsn_py_White;
+extern dmnsn_py_Color dmnsn_py_Clear;
+extern dmnsn_py_Color dmnsn_py_Red;
+extern dmnsn_py_Color dmnsn_py_Green;
+extern dmnsn_py_Color dmnsn_py_Blue;
+extern dmnsn_py_Color dmnsn_py_Magenta;
+extern dmnsn_py_Color dmnsn_py_Orange;
+extern dmnsn_py_Color dmnsn_py_Yellow;
+extern dmnsn_py_Color dmnsn_py_Cyan;
diff --git a/libdimension-python/Makefile.am b/libdimension-python/Makefile.am
index a9e1b8c..e75ffad 100644
--- a/libdimension-python/Makefile.am
+++ b/libdimension-python/Makefile.am
@@ -26,7 +26,9 @@ AM_CFLAGS = $(Python_CFLAGS)
AM_LDFLAGS = $(Python_LDFLAGS)
pyexec_LTLIBRARIES = dimension.la
-dimension_la_SOURCES = Matrix.c \
+dimension_la_SOURCES = Color.c \
+ Color.h \
+ Matrix.c \
Matrix.h \
Scene.c \
Scene.h \
diff --git a/libdimension-python/Scene.c b/libdimension-python/Scene.c
index bfbab63..6323402 100644
--- a/libdimension-python/Scene.c
+++ b/libdimension-python/Scene.c
@@ -66,6 +66,5 @@ PyTypeObject dmnsn_py_SceneType = {
bool
dmnsn_py_init_SceneType(void)
{
- Py_INCREF(&dmnsn_py_SceneType);
return PyType_Ready(&dmnsn_py_SceneType) >= 0;
}
diff --git a/libdimension-python/Vector.c b/libdimension-python/Vector.c
index 3fdc90c..6a1928f 100644
--- a/libdimension-python/Vector.c
+++ b/libdimension-python/Vector.c
@@ -84,11 +84,11 @@ dmnsn_py_Vector_str(dmnsn_py_Vector *self)
return NULL;
}
- PyObject *repr = PyUnicode_FromFormat("<%S, %S, %S>", x, y, z);
+ PyObject *str = PyUnicode_FromFormat("<%S, %S, %S>", x, y, z);
Py_DECREF(z);
Py_DECREF(y);
Py_DECREF(x);
- return repr;
+ return str;
}
static PyObject *
@@ -315,27 +315,27 @@ static PyMethodDef dmnsn_py_Vector_methods[] = {
};
static PyObject *
-dmnsn_py_Vector_getx(dmnsn_py_Vector *self, void *closure)
+dmnsn_py_Vector_get_x(dmnsn_py_Vector *self, void *closure)
{
return PyFloat_FromDouble(self->v.x);
}
static PyObject *
-dmnsn_py_Vector_gety(dmnsn_py_Vector *self, void *closure)
+dmnsn_py_Vector_get_y(dmnsn_py_Vector *self, void *closure)
{
return PyFloat_FromDouble(self->v.y);
}
static PyObject *
-dmnsn_py_Vector_getz(dmnsn_py_Vector *self, void *closure)
+dmnsn_py_Vector_get_z(dmnsn_py_Vector *self, void *closure)
{
return PyFloat_FromDouble(self->v.z);
}
static PyGetSetDef dmnsn_py_Vector_getsetters[] = {
- { "x", (getter)dmnsn_py_Vector_getx, NULL, "x coordinate", NULL },
- { "y", (getter)dmnsn_py_Vector_gety, NULL, "y coordinate", NULL },
- { "z", (getter)dmnsn_py_Vector_getz, NULL, "z coordinate", NULL },
+ { "x", (getter)dmnsn_py_Vector_get_x, NULL, "x coordinate", NULL },
+ { "y", (getter)dmnsn_py_Vector_get_y, NULL, "y coordinate", NULL },
+ { "z", (getter)dmnsn_py_Vector_get_z, NULL, "z coordinate", NULL },
{ NULL }
};
@@ -354,10 +354,24 @@ PyTypeObject dmnsn_py_VectorType = {
.tp_init = (initproc)dmnsn_py_Vector_init,
};
+#define dmnsn_py_Vector_global(name) \
+ dmnsn_py_Vector name = { \
+ PyObject_HEAD_INIT(&dmnsn_py_VectorType) \
+ };
+
+dmnsn_py_Vector_global(dmnsn_py_Zero);
+dmnsn_py_Vector_global(dmnsn_py_X);
+dmnsn_py_Vector_global(dmnsn_py_Y);
+dmnsn_py_Vector_global(dmnsn_py_Z);
+
bool
dmnsn_py_init_VectorType(void)
{
+ dmnsn_py_Zero.v = dmnsn_zero;
+ dmnsn_py_X.v = dmnsn_x;
+ dmnsn_py_Y.v = dmnsn_y;
+ dmnsn_py_Z.v = dmnsn_z;
+
dmnsn_py_VectorType.tp_new = PyType_GenericNew;
- Py_INCREF(&dmnsn_py_VectorType);
return PyType_Ready(&dmnsn_py_VectorType) >= 0;
}
diff --git a/libdimension-python/Vector.h b/libdimension-python/Vector.h
index d829e3b..d7e4e05 100644
--- a/libdimension-python/Vector.h
+++ b/libdimension-python/Vector.h
@@ -34,3 +34,9 @@ bool dmnsn_py_init_VectorType(void);
PyObject *dmnsn_py_Vector_cross(PyObject *self, PyObject *args);
PyObject *dmnsn_py_Vector_dot(PyObject *self, PyObject *args);
PyObject *dmnsn_py_Vector_proj(PyObject *self, PyObject *args);
+
+/* Vector constants */
+extern dmnsn_py_Vector dmnsn_py_Zero;
+extern dmnsn_py_Vector dmnsn_py_X;
+extern dmnsn_py_Vector dmnsn_py_Y;
+extern dmnsn_py_Vector dmnsn_py_Z;
diff --git a/libdimension-python/dimension.c b/libdimension-python/dimension.c
index a238f31..e57767d 100644
--- a/libdimension-python/dimension.c
+++ b/libdimension-python/dimension.c
@@ -21,6 +21,7 @@
#include "dimension-python.h"
#include "Vector.h"
#include "Matrix.h"
+#include "Color.h"
#include "Scene.h"
static PyObject *
@@ -67,6 +68,7 @@ PyInit_dimension(void)
{
if (!dmnsn_py_init_VectorType()
|| !dmnsn_py_init_MatrixType()
+ || !dmnsn_py_init_ColorType()
|| !dmnsn_py_init_SceneType())
return NULL;
@@ -77,29 +79,27 @@ PyInit_dimension(void)
PyModule_AddObject(module, "Vector", (PyObject *)&dmnsn_py_VectorType);
/* Vector constants */
- dmnsn_py_Vector *zero = PyObject_New(dmnsn_py_Vector, &dmnsn_py_VectorType);
- dmnsn_py_Vector *x = PyObject_New(dmnsn_py_Vector, &dmnsn_py_VectorType);
- dmnsn_py_Vector *y = PyObject_New(dmnsn_py_Vector, &dmnsn_py_VectorType);
- dmnsn_py_Vector *z = PyObject_New(dmnsn_py_Vector, &dmnsn_py_VectorType);
- if (!zero || !x || !y || !z) {
- Py_XDECREF(zero);
- Py_XDECREF(x);
- Py_XDECREF(y);
- Py_XDECREF(z);
- Py_DECREF(module);
- return NULL;
- }
- zero->v = dmnsn_zero;
- x->v = dmnsn_x;
- y->v = dmnsn_y;
- z->v = dmnsn_z;
- PyModule_AddObject(module, "Zero", (PyObject *)zero);
- PyModule_AddObject(module, "X", (PyObject *)x);
- PyModule_AddObject(module, "Y", (PyObject *)y);
- PyModule_AddObject(module, "Z", (PyObject *)z);
+ PyModule_AddObject(module, "Zero", (PyObject *)&dmnsn_py_Zero);
+ PyModule_AddObject(module, "X", (PyObject *)&dmnsn_py_X);
+ PyModule_AddObject(module, "Y", (PyObject *)&dmnsn_py_Y);
+ PyModule_AddObject(module, "Z", (PyObject *)&dmnsn_py_Z);
PyModule_AddObject(module, "Matrix", (PyObject *)&dmnsn_py_MatrixType);
+ PyModule_AddObject(module, "Color", (PyObject *)&dmnsn_py_ColorType);
+
+ /* Color constants */
+ PyModule_AddObject(module, "Black", (PyObject *)&dmnsn_py_Black);
+ PyModule_AddObject(module, "White", (PyObject *)&dmnsn_py_White);
+ PyModule_AddObject(module, "Clear", (PyObject *)&dmnsn_py_Clear);
+ PyModule_AddObject(module, "Red", (PyObject *)&dmnsn_py_Red);
+ PyModule_AddObject(module, "Green", (PyObject *)&dmnsn_py_Green);
+ PyModule_AddObject(module, "Blue", (PyObject *)&dmnsn_py_Blue);
+ PyModule_AddObject(module, "Magenta", (PyObject *)&dmnsn_py_Magenta);
+ PyModule_AddObject(module, "Orange", (PyObject *)&dmnsn_py_Orange);
+ PyModule_AddObject(module, "Yellow", (PyObject *)&dmnsn_py_Yellow);
+ PyModule_AddObject(module, "Cyan", (PyObject *)&dmnsn_py_Cyan);
+
PyModule_AddObject(module, "Scene", (PyObject *)&dmnsn_py_SceneType);
return module;
diff --git a/libdimension-python/tests/Makefile.am b/libdimension-python/tests/Makefile.am
index 17589ba..48e1129 100644
--- a/libdimension-python/tests/Makefile.am
+++ b/libdimension-python/tests/Makefile.am
@@ -18,6 +18,7 @@
###########################################################################
TESTS = geometry.py \
+ color.py \
demo.py
TESTS_ENVIRONMENT = PYTHONPATH=$(top_builddir)/libdimension-python/.libs
diff --git a/libdimension-python/tests/color.py b/libdimension-python/tests/color.py
new file mode 100755
index 0000000..72056d1
--- /dev/null
+++ b/libdimension-python/tests/color.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python3
+
+#########################################################################
+# Copyright (C) 2010-2011 Tavian Barnes <tavianator@tavianator.com> #
+# #
+# This file is part of The Dimension Test Suite. #
+# #
+# The Dimension Test Suite is free software; you can redistribute it #
+# and/or modify it under the terms of the GNU General Public License as #
+# published by the Free Software Foundation; either version 3 of the #
+# License, or (at your option) any later version. #
+# #
+# The Dimension Test Suite 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 #
+# General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+#########################################################################
+
+from dimension import *
+
+# Treat warnings as errors for tests
+dieOnWarnings(True)
+
+c = Color(0, 0.5, 1, filter = 0.25, trans = 0.35)
+assert repr(c) == 'dimension.Color(0.0, 0.5, 1.0, 0.25, 0.35)', repr(c)
+assert str(c) == '<red = 0.0, green = 0.5, blue = 1.0, \
+filter = 0.25, trans = 0.35>', str(c)
+assert c.red == 0, c.red
+assert c.green == 0.5, c.green
+assert c.blue == 1, c.blue
+assert c.filter == 0.25, c.filter
+assert c.trans == 0.35, c.trans
+
+c = Color(1, 0.5, 0)
+assert str(c) == '<red = 1.0, green = 0.5, blue = 0.0>', str(c)
+
+assert Black == Color(0, 0, 0), Black
+assert White == Color(1, 1, 1), White
+assert Clear == Color(0, 0, 0, trans = 1), Clear
+assert Red == Color(1, 0, 0), Red
+assert Green == Color(0, 1, 0), Green
+assert Blue == Color(0, 0, 1), Blue
+assert Magenta == Color(1, 0, 1), Magenta
+assert Orange == Color(1, 0.5, 0), Orange
+assert Yellow == Color(1, 1, 0), Yellow
+assert Cyan == Color(0, 1, 1), Cyan
+
+assert White, bool(White)
+assert not Black, not Black
+
+assert Red + Blue == Magenta, Red + Blue
+assert 0.5*White == Color(0.5, 0.5, 0.5), 0.5*White