From: Lawrence D'Oliveiro on
I find the matrix methods in Pycairo to be an annoying hodge-podge of
ones that overwrite the Matrix object in-place (init_rotate, invert)
versus ones that concatenate additional transformations (rotate, scale,
translate) versus ones that return new matrices without modifying
the originals (multiply).

Myself, I prefer methods that always return new matrices. This allows
for a more functional style of programming, e.g. given

m = cairo.Matrix()

then

m2 = m.translation(-10, -10) * m.rotation(math.pi / 4) * m.translation(10, 10)

concisely expresses rotation by 90° about the centre (10, 10).

Herewith a patch to add such methods to the cairo.Matrix class. Note
that the names (inverse, rotation, scaling, translation) are nouns,
to reflect the fact that they don't perform the actions, but they
return Matrix objects that do.

---
src/matrix.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/src/matrix.c b/src/matrix.c
index eefeab9..d1709b9 100644
--- a/src/matrix.c
+++ b/src/matrix.c
@@ -206,6 +206,76 @@ matrix_translate (PycairoMatrix *o, PyObject *args) {
}

static PyObject *
+matrix_translation(PycairoMatrix *unused, PyObject *args)
+ {
+ PyObject * result = NULL;
+ double tx, ty;
+ cairo_matrix_t result_matrix;
+ do /*once*/
+ {
+ if (!PyArg_ParseTuple(args, "dd:Matrix.translation", &tx, &ty))
+ break;
+ cairo_matrix_init_identity(&result_matrix);
+ cairo_matrix_translate(&result_matrix, tx, ty);
+ result = PycairoMatrix_FromMatrix(&result_matrix);
+ }
+ while (0);
+ return result;
+ } /*matrix_translation*/
+
+static PyObject *
+matrix_scaling(PycairoMatrix *unused, PyObject *args)
+ {
+ PyObject * result = NULL;
+ double sx, sy;
+ cairo_matrix_t result_matrix;
+ do /*once*/
+ {
+ if (!PyArg_ParseTuple(args, "dd:Matrix.scaling", &sx, &sy))
+ break;
+ cairo_matrix_init_identity(&result_matrix);
+ cairo_matrix_scale(&result_matrix, sx, sy);
+ result = PycairoMatrix_FromMatrix(&result_matrix);
+ }
+ while (0);
+ return result;
+ } /*matrix_scaling*/
+
+static PyObject *
+matrix_rotation(PycairoMatrix *unused, PyObject *args)
+ {
+ PyObject * result = NULL;
+ double radians;
+ cairo_matrix_t result_matrix;
+ do /*once*/
+ {
+ if (!PyArg_ParseTuple(args, "d:Matrix.rotation", &radians))
+ break;
+ cairo_matrix_init_identity(&result_matrix);
+ cairo_matrix_rotate(&result_matrix, radians);
+ result = PycairoMatrix_FromMatrix(&result_matrix);
+ }
+ while (0);
+ return result;
+ } /*matrix_rotation*/
+
+static PyObject *
+matrix_inverse(PycairoMatrix *self, PyObject *args_unused)
+ {
+ PyObject * result = NULL;
+ cairo_matrix_t result_matrix;
+ do /*once*/
+ {
+ result_matrix = self->matrix;
+ if (Pycairo_Check_Status(cairo_matrix_invert(&result_matrix)))
+ break;
+ result = PycairoMatrix_FromMatrix(&result_matrix);
+ }
+ while (0);
+ return result;
+ } /*matrix_inverse*/
+
+static PyObject *
matrix_item (PycairoMatrix *o, Py_ssize_t i) {
switch (i) {
case 0:
@@ -297,6 +367,11 @@ static PyMethodDef matrix_methods[] = {
{"transform_distance",(PyCFunction)matrix_transform_distance, METH_VARARGS },
{"transform_point", (PyCFunction)matrix_transform_point, METH_VARARGS },
{"translate", (PyCFunction)matrix_translate, METH_VARARGS },
+ /* functional methods: */
+ {"translation", (PyCFunction)matrix_translation, METH_VARARGS | METH_STATIC },
+ {"scaling", (PyCFunction)matrix_scaling, METH_VARARGS | METH_STATIC },
+ {"rotation", (PyCFunction)matrix_rotation, METH_VARARGS | METH_STATIC },
+ {"inverse", (PyCFunction)matrix_inverse, METH_VARARGS },
{NULL, NULL, 0, NULL},
};

--
1.7.0