/* -*- mode: c++; c-basic-offset: 4 -*- */ #ifndef __PY_ADAPTORS_H__ #define __PY_ADAPTORS_H__ #define PY_SSIZE_T_CLEAN /*************************************************************************** * This module contains a number of C++ classes that adapt Python data * structures to C++ and Agg-friendly interfaces. */ #include <Python.h> #include "numpy/arrayobject.h" #include "py_exceptions.h" extern "C" { int convert_path(PyObject *obj, void *pathp); } namespace py { /************************************************************ * py::PathIterator acts as a bridge between Numpy and Agg. Given a * pair of Numpy arrays, vertices and codes, it iterates over * those vertices and codes, using the standard Agg vertex source * interface: * * unsigned vertex(double* x, double* y) */ class PathIterator { /* We hold references to the Python objects, not just the underlying data arrays, so that Python reference counting can work. */ PyArrayObject *m_vertices; PyArrayObject *m_codes; unsigned m_iterator; unsigned m_total_vertices; /* This class doesn't actually do any simplification, but we store the value here, since it is obtained from the Python object. */ bool m_should_simplify; double m_simplify_threshold; public: inline PathIterator() : m_vertices(NULL), m_codes(NULL), m_iterator(0), m_total_vertices(0), m_should_simplify(false), m_simplify_threshold(1.0 / 9.0) { } inline PathIterator(PyObject *vertices, PyObject *codes, bool should_simplify, double simplify_threshold) : m_vertices(NULL), m_codes(NULL), m_iterator(0) { if (!set(vertices, codes, should_simplify, simplify_threshold)) throw py::exception(); } inline PathIterator(PyObject *vertices, PyObject *codes) : m_vertices(NULL), m_codes(NULL), m_iterator(0) { if (!set(vertices, codes)) throw py::exception(); } inline PathIterator(const PathIterator &other) { Py_XINCREF(other.m_vertices); m_vertices = other.m_vertices; Py_XINCREF(other.m_codes); m_codes = other.m_codes; m_iterator = 0; m_total_vertices = other.m_total_vertices; m_should_simplify = other.m_should_simplify; m_simplify_threshold = other.m_simplify_threshold; } ~PathIterator() { Py_XDECREF(m_vertices); Py_XDECREF(m_codes); } inline int set(PyObject *vertices, PyObject *codes, bool should_simplify, double simplify_threshold) { m_should_simplify = should_simplify; m_simplify_threshold = simplify_threshold; Py_XDECREF(m_vertices); m_vertices = (PyArrayObject *)PyArray_FromObject(vertices, NPY_DOUBLE, 2, 2); if (!m_vertices || PyArray_DIM(m_vertices, 1) != 2) { PyErr_SetString(PyExc_ValueError, "Invalid vertices array"); return 0; } Py_XDECREF(m_codes); m_codes = NULL; if (codes != NULL && codes != Py_None) { m_codes = (PyArrayObject *)PyArray_FromObject(codes, NPY_UINT8, 1, 1); if (!m_codes || PyArray_DIM(m_codes, 0) != PyArray_DIM(m_vertices, 0)) { PyErr_SetString(PyExc_ValueError, "Invalid codes array"); return 0; } } m_total_vertices = (unsigned)PyArray_DIM(m_vertices, 0); m_iterator = 0; return 1; } inline int set(PyObject *vertices, PyObject *codes) { return set(vertices, codes, false, 0.0); } inline unsigned vertex(double *x, double *y) { if (m_iterator >= m_total_vertices) { *x = 0.0; *y = 0.0; return agg::path_cmd_stop; } const size_t idx = m_iterator++; char *pair = (char *)PyArray_GETPTR2(m_vertices, idx, 0); *x = *(double *)pair; *y = *(double *)(pair + PyArray_STRIDE(m_vertices, 1)); if (m_codes != NULL) { return (unsigned)(*(char *)PyArray_GETPTR1(m_codes, idx)); } else { return idx == 0 ? agg::path_cmd_move_to : agg::path_cmd_line_to; } } inline void rewind(unsigned path_id) { m_iterator = path_id; } inline unsigned total_vertices() const { return m_total_vertices; } inline bool should_simplify() const { return m_should_simplify; } inline double simplify_threshold() const { return m_simplify_threshold; } inline bool has_curves() const { return m_codes != NULL; } inline void *get_id() { return (void *)m_vertices; } }; class PathGenerator { PyObject *m_paths; Py_ssize_t m_npaths; public: typedef PathIterator path_iterator; PathGenerator(PyObject *obj) : m_paths(NULL), m_npaths(0) { if (!set(obj)) { throw py::exception(); } } ~PathGenerator() { Py_XDECREF(m_paths); } int set(PyObject *obj) { if (!PySequence_Check(obj)) { return 0; } m_paths = obj; Py_INCREF(m_paths); m_npaths = PySequence_Size(m_paths); return 1; } Py_ssize_t num_paths() const { return m_npaths; } Py_ssize_t size() const { return m_npaths; } path_iterator operator()(size_t i) { path_iterator path; PyObject *item; item = PySequence_GetItem(m_paths, i % m_npaths); if (item == NULL) { throw py::exception(); } if (!convert_path(item, &path)) { throw py::exception(); } Py_DECREF(item); return path; } }; } #endif