diff options
author | shumkovnd <shumkovnd@yandex-team.com> | 2023-11-10 14:39:34 +0300 |
---|---|---|
committer | shumkovnd <shumkovnd@yandex-team.com> | 2023-11-10 16:42:24 +0300 |
commit | 77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch) | |
tree | c51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/matplotlib/py2/src/py_adaptors.h | |
parent | dd6d20cadb65582270ac23f4b3b14ae189704b9d (diff) | |
download | ydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz |
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/matplotlib/py2/src/py_adaptors.h')
-rw-r--r-- | contrib/python/matplotlib/py2/src/py_adaptors.h | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/contrib/python/matplotlib/py2/src/py_adaptors.h b/contrib/python/matplotlib/py2/src/py_adaptors.h new file mode 100644 index 0000000000..8eaa7ad6c7 --- /dev/null +++ b/contrib/python/matplotlib/py2/src/py_adaptors.h @@ -0,0 +1,251 @@ +/* -*- mode: c++; c-basic-offset: 4 -*- */ + +#ifndef __PY_ADAPTORS_H__ +#define __PY_ADAPTORS_H__ + +/*************************************************************************** + * 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 |