From f4053866258e48dd98136142471ecabf6c138160 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 May 2011 22:59:29 -0600 Subject: Add matricies to Python module. --- libdimension-python/Makefile.am | 3 +- libdimension-python/Matrix.c | 342 ++++++++++++++++++++++++++++++++++ libdimension-python/Matrix.h | 37 ++++ libdimension-python/Vector.c | 26 ++- libdimension-python/Vector.h | 1 + libdimension-python/dimension.c | 13 +- libdimension-python/tests/geometry.py | 13 ++ 7 files changed, 419 insertions(+), 16 deletions(-) create mode 100644 libdimension-python/Matrix.c create mode 100644 libdimension-python/Matrix.h diff --git a/libdimension-python/Makefile.am b/libdimension-python/Makefile.am index 89bfd1d..23af579 100644 --- a/libdimension-python/Makefile.am +++ b/libdimension-python/Makefile.am @@ -26,8 +26,9 @@ AM_CFLAGS = $(Python_CFLAGS) AM_LDFLAGS = $(Python_LDFLAGS) pyexec_LTLIBRARIES = dimension.la -dimension_la_SOURCES = Vector.c \ +dimension_la_SOURCES = Matrix.c \ Scene.c \ + Vector.c \ dimension.c dimension_la_LDFLAGS = -avoid-version -module dimension_la_LIBADD = $(top_builddir)/libdimension/libdimension.la diff --git a/libdimension-python/Matrix.c b/libdimension-python/Matrix.c new file mode 100644 index 0000000..a414622 --- /dev/null +++ b/libdimension-python/Matrix.c @@ -0,0 +1,342 @@ +/************************************************************************* + * Copyright (C) 2009-2011 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +#include "Vector.h" +#include "Matrix.h" + +static int +dmnsn_py_Matrix_init(dmnsn_py_Matrix *self, PyObject *args, PyObject *kwds) +{ + if (kwds) { + PyErr_SetString(PyExc_TypeError, "Keyword arguments not supported"); + return -1; + } + + if (!PyArg_ParseTuple(args, "dddddddddddd", + &self->m.n[0][0], &self->m.n[0][1], &self->m.n[0][2], + &self->m.n[0][3], + &self->m.n[1][0], &self->m.n[1][1], &self->m.n[1][2], + &self->m.n[1][3], + &self->m.n[2][0], &self->m.n[2][1], &self->m.n[2][2], + &self->m.n[2][3])) + return -1; + + return 0; +} + +PyObject * +dmnsn_py_Matrix_scale(PyObject *self, PyObject *args, PyObject *kwds) +{ + dmnsn_vector scale; + if (dmnsn_py_Vector_args(&scale, args, kwds)) { + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_scale_matrix(scale); + } + return (PyObject *)ret; + } else { + PyErr_Clear(); + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Keywords only make sense for component syntax"); + return NULL; + } + + dmnsn_py_Vector *scale; + if (!PyArg_ParseTuple(args, "O!", &dmnsn_py_VectorType, &scale)) + return NULL; + + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_scale_matrix(scale->v); + } + return (PyObject *)ret; + } +} + +PyObject * +dmnsn_py_Matrix_translate(PyObject *self, PyObject *args, PyObject *kwds) +{ + dmnsn_vector translate; + if (dmnsn_py_Vector_args(&translate, args, kwds)) { + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_translation_matrix(translate); + } + return (PyObject *)ret; + } else { + PyErr_Clear(); + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Keywords only make sense for component syntax"); + return NULL; + } + + dmnsn_py_Vector *translate; + if (!PyArg_ParseTuple(args, "O!", &dmnsn_py_VectorType, &translate)) + return NULL; + + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_translation_matrix(translate->v); + } + return (PyObject *)ret; + } +} + +PyObject * +dmnsn_py_Matrix_rotate(PyObject *self, PyObject *args, PyObject *kwds) +{ + dmnsn_vector rotate; + if (dmnsn_py_Vector_args(&rotate, args, kwds)) { + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_rotation_matrix(dmnsn_vector_mul(dmnsn_radians(1.0), + rotate)); + } + return (PyObject *)ret; + } else { + PyErr_Clear(); + if (kwds) { + PyErr_SetString(PyExc_TypeError, + "Keywords only make sense for component syntax"); + return NULL; + } + + dmnsn_py_Vector *rotate; + if (!PyArg_ParseTuple(args, "O!", &dmnsn_py_VectorType, &rotate)) + return NULL; + + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_rotation_matrix(dmnsn_vector_mul(dmnsn_radians(1.0), + rotate->v)); + } + return (PyObject *)ret; + } +} + +static PyObject * +dmnsn_py_Matrix_repr(dmnsn_py_Matrix *self) +{ + PyObject *floats[3][4] = { { NULL } }; + bool initialized = true; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 4; ++j) { + floats[i][j] = PyFloat_FromDouble(self->m.n[i][j]); + if (!floats[i][j]) { + initialized = false; + break; + } + } + if (!initialized) + break; + } + + if (!initialized) { + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 4; ++j) { + Py_XDECREF(floats[i][j]); + } + } + return NULL; + } + + PyObject *repr = PyUnicode_FromFormat("dimension.Matrix(%R, %R, %R, %R," + "%R, %R, %R, %R," + "%R, %R, %R, %R)", + floats[0][0], floats[0][1], + floats[0][2], floats[0][3], + floats[1][0], floats[1][1], + floats[1][2], floats[1][3], + floats[2][0], floats[2][1], + floats[2][2], floats[2][3]); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 4; ++j) { + Py_DECREF(floats[i][j]); + } + } + return repr; +} + +static PyObject * +dmnsn_py_Matrix_str(dmnsn_py_Matrix *self) +{ + PyObject *floats[3][4] = { { NULL } }; + bool initialized = true; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 4; ++j) { + floats[i][j] = PyFloat_FromDouble(self->m.n[i][j]); + if (!floats[i][j]) { + initialized = false; + break; + } + } + if (!initialized) + break; + } + + if (!initialized) { + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 4; ++j) { + Py_XDECREF(floats[i][j]); + } + } + return NULL; + } + + PyObject *str = PyUnicode_FromFormat("\n[%S\t%S\t%S\t%S]\n" + "[%S\t%S\t%S\t%S]\n" + "[%S\t%S\t%S\t%S]\n" + "[0.0\t0.0\t0.0\t1.0]", + floats[0][0], floats[0][1], + floats[0][2], floats[0][3], + floats[1][0], floats[1][1], + floats[1][2], floats[1][3], + floats[2][0], floats[2][1], + floats[2][2], floats[2][3]); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 4; ++j) { + Py_DECREF(floats[i][j]); + } + } + return str; +} + +static PyObject * +dmnsn_py_Matrix_richcompare(PyObject *lhs, PyObject *rhs, int op) +{ + if (!PyObject_TypeCheck(lhs, &dmnsn_py_MatrixType) + || !PyObject_TypeCheck(rhs, &dmnsn_py_MatrixType)) + { + PyErr_SetString(PyExc_TypeError, + "Matricies can only be compared with Matricies"); + return NULL; + } + + dmnsn_py_Matrix *mlhs = (dmnsn_py_Matrix *)lhs; + dmnsn_py_Matrix *mrhs = (dmnsn_py_Matrix *)rhs; + + double sqdiff = 0.0; + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 4; ++j) { + double diff = mlhs->m.n[i][j] - mrhs->m.n[i][j]; + sqdiff += diff*diff; + } + } + + bool equal = sqrt(sqdiff) < 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_Matrix_mul(PyObject *lhs, PyObject *rhs) +{ + if (!PyObject_TypeCheck(lhs, &dmnsn_py_MatrixType)) { + PyErr_SetString(PyExc_TypeError, + "Left-hand side of matrix multiplication must be a matrix"); + return NULL; + } + + dmnsn_py_Matrix *mlhs = (dmnsn_py_Matrix *)lhs; + + if (PyObject_TypeCheck(rhs, &dmnsn_py_MatrixType)) { + dmnsn_py_Matrix *mrhs = (dmnsn_py_Matrix *)rhs; + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_matrix_mul(mlhs->m, mrhs->m); + } + return (PyObject *)ret; + } else if (PyObject_TypeCheck(rhs, &dmnsn_py_VectorType)) { + dmnsn_py_Vector *vrhs = (dmnsn_py_Vector *)rhs; + dmnsn_py_Vector *ret = PyObject_New(dmnsn_py_Vector, &dmnsn_py_VectorType); + if (ret) { + ret->v = dmnsn_transform_vector(mlhs->m, vrhs->v); + } + return (PyObject *)ret; + } else { + PyErr_SetString(PyExc_TypeError, + "Right-hand side of matrix multiplication must be a matrix" + " or vector"); + return NULL; + } +} + +static PyNumberMethods dmnsn_py_Matrix_as_number = { + .nb_multiply = dmnsn_py_Matrix_mul, +}; + +static PyObject * +dmnsn_py_Matrix_inverse(dmnsn_py_Matrix *self) +{ + dmnsn_py_Matrix *ret = PyObject_New(dmnsn_py_Matrix, &dmnsn_py_MatrixType); + if (ret) { + ret->m = dmnsn_matrix_inverse(self->m); + } + return (PyObject *)ret; +} + +static PyMethodDef dmnsn_py_Matrix_methods[] = { + { "inverse", (PyCFunction)dmnsn_py_Matrix_inverse, METH_NOARGS, + "Return the inverse of the matrix" }, + { NULL } +}; + +static PyGetSetDef dmnsn_py_Matrix_getsetters[] = { + { NULL } +}; + +PyTypeObject dmnsn_py_MatrixType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "dimension.Matrix", + .tp_basicsize = sizeof(dmnsn_py_Matrix), + .tp_repr = (reprfunc)dmnsn_py_Matrix_repr, + .tp_str = (reprfunc)dmnsn_py_Matrix_str, + .tp_as_number = &dmnsn_py_Matrix_as_number, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Dimension matrix", + .tp_richcompare = dmnsn_py_Matrix_richcompare, + .tp_methods = dmnsn_py_Matrix_methods, + .tp_getset = dmnsn_py_Matrix_getsetters, + .tp_init = (initproc)dmnsn_py_Matrix_init, +}; + +bool +dmnsn_py_init_MatrixType(void) +{ + dmnsn_py_MatrixType.tp_new = PyType_GenericNew; + Py_INCREF(&dmnsn_py_MatrixType); + return PyType_Ready(&dmnsn_py_MatrixType) >= 0; +} diff --git a/libdimension-python/Matrix.h b/libdimension-python/Matrix.h new file mode 100644 index 0000000..7f7a085 --- /dev/null +++ b/libdimension-python/Matrix.h @@ -0,0 +1,37 @@ +/************************************************************************* + * Copyright (C) 2009-2011 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +#include "dimension.h" + +typedef struct dmnsn_py_Matrix { + PyObject_HEAD + dmnsn_matrix m; +} dmnsn_py_Matrix; + +extern PyTypeObject dmnsn_py_MatrixType; + +bool dmnsn_py_init_MatrixType(void); + +/* Global methods */ +PyObject *dmnsn_py_Matrix_scale(PyObject *self, PyObject *args, PyObject *kwds); +PyObject *dmnsn_py_Matrix_translate(PyObject *self, PyObject *args, + PyObject *kwds); +PyObject *dmnsn_py_Matrix_rotate(PyObject *self, PyObject *args, + PyObject *kwds); diff --git a/libdimension-python/Vector.c b/libdimension-python/Vector.c index d4ed533..8cd7f05 100644 --- a/libdimension-python/Vector.c +++ b/libdimension-python/Vector.c @@ -19,18 +19,19 @@ *************************************************************************/ #include "Vector.h" -#include "dimension.h" -static int -dmnsn_py_Vector_init(dmnsn_py_Vector *self, PyObject *args, PyObject *kwds) +bool +dmnsn_py_Vector_args(dmnsn_vector *v, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "x", "y", "z", NULL }; + return PyArg_ParseTupleAndKeywords(args, kwds, "ddd", kwlist, + &v->x, &v->y, &v->z); +} - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ddd", kwlist, - &self->v.x, &self->v.y, &self->v.z)) - return -1; - - return 0; +static int +dmnsn_py_Vector_init(dmnsn_py_Vector *self, PyObject *args, PyObject *kwds) +{ + return dmnsn_py_Vector_args(&self->v, args, kwds) ? 0 : -1; } static PyObject * @@ -79,12 +80,9 @@ dmnsn_py_Vector_str(dmnsn_py_Vector *self) static PyObject * dmnsn_py_Vector_richcompare(PyObject *lhs, PyObject *rhs, int op) { - if (!PyObject_TypeCheck(lhs, &dmnsn_py_VectorType)) { - PyErr_SetString(PyExc_TypeError, - "Vectors can only be compared with Vectors"); - return NULL; - } - if (!PyObject_TypeCheck(lhs, &dmnsn_py_VectorType)) { + if (!PyObject_TypeCheck(lhs, &dmnsn_py_VectorType) + || !PyObject_TypeCheck(rhs, &dmnsn_py_VectorType)) + { PyErr_SetString(PyExc_TypeError, "Vectors can only be compared with Vectors"); return NULL; diff --git a/libdimension-python/Vector.h b/libdimension-python/Vector.h index 0ec288d..7a8d769 100644 --- a/libdimension-python/Vector.h +++ b/libdimension-python/Vector.h @@ -27,6 +27,7 @@ typedef struct dmnsn_py_Vector { extern PyTypeObject dmnsn_py_VectorType; +bool dmnsn_py_Vector_args(dmnsn_vector *v, PyObject *args, PyObject *kwds); bool dmnsn_py_init_VectorType(void); /* Global methods */ diff --git a/libdimension-python/dimension.c b/libdimension-python/dimension.c index 91d32dc..2b08f6c 100644 --- a/libdimension-python/dimension.c +++ b/libdimension-python/dimension.c @@ -20,6 +20,7 @@ #include "dimension.h" #include "Vector.h" +#include "Matrix.h" #include "Scene.h" static PyObject * @@ -43,6 +44,13 @@ static PyMethodDef DimensionMethods[] = { { "dot", dmnsn_py_Vector_dot, METH_VARARGS, "Dot product." }, { "proj", dmnsn_py_Vector_proj, METH_VARARGS, "Vector projection." }, + { "scale", (PyCFunction)dmnsn_py_Matrix_scale, + METH_VARARGS | METH_KEYWORDS, "Scaling." }, + { "translate", (PyCFunction)dmnsn_py_Matrix_translate, + METH_VARARGS | METH_KEYWORDS, "Translation." }, + { "rotate", (PyCFunction)dmnsn_py_Matrix_rotate, + METH_VARARGS | METH_KEYWORDS, "Rotation." }, + { NULL, NULL, 0, NULL } }; @@ -58,6 +66,7 @@ PyMODINIT_FUNC PyInit_dimension(void) { if (!dmnsn_py_init_VectorType() + || !dmnsn_py_init_MatrixType() || !dmnsn_py_init_SceneType()) return NULL; @@ -89,7 +98,9 @@ PyInit_dimension(void) PyModule_AddObject(module, "Y", (PyObject *)y); PyModule_AddObject(module, "Z", (PyObject *)z); - PyModule_AddObject(module, "Scene", (PyObject *)&dmnsn_py_SceneType); + PyModule_AddObject(module, "Matrix", (PyObject *)&dmnsn_py_MatrixType); + + PyModule_AddObject(module, "Scene", (PyObject *)&dmnsn_py_SceneType); return module; } diff --git a/libdimension-python/tests/geometry.py b/libdimension-python/tests/geometry.py index b398a1b..8f76ef7 100755 --- a/libdimension-python/tests/geometry.py +++ b/libdimension-python/tests/geometry.py @@ -50,3 +50,16 @@ assert dot(v, v) == v.norm()**2, dot(v, v) assert v, bool(v) assert not Zero, not Zero assert proj(v, X) == 2*X, proj(v, X) + +assert scale(1, 2, 3) == Matrix(1, 0, 0, 0, + 0, 2, 0, 0, + 0, 0, 3, 0), \ + scale(1, 2, 3) +assert translate(x = 1, y = 2, z = 3) == Matrix(1, 0, 0, 1, + 0, 1, 0, 2, + 0, 0, 1, 3), \ + translate(x = 1, y = 2, z = 3) +assert rotate(90*Y) == Matrix( 0, 0, 1, 0, + 0, 1, 0, 0, + -1, 0, 0, 0), \ + rotate(90*Y) -- cgit v1.2.3