diff --git a/matplotlibcpp.h b/matplotlibcpp.h index ea2e4fb2..b017a027 100644 --- a/matplotlibcpp.h +++ b/matplotlibcpp.h @@ -1,440 +1,697 @@ #pragma once -// Python headers must be included before any system headers, since -// they define _POSIX_C_SOURCE -#include - -#include -#include -#include -#include #include -#include -#include +#include #include // requires c++11 support #include +#include +#include +#include +#include +#include +#include -#ifndef WITHOUT_NUMPY -# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -# include - -# ifdef WITH_OPENCV -# include -# endif // WITH_OPENCV +#include -/* - * A bunch of constants were removed in OpenCV 4 in favour of enum classes, so - * define the ones we need here. - */ -# if CV_MAJOR_VERSION > 3 -# define CV_BGR2RGB cv::COLOR_BGR2RGB -# define CV_BGRA2RGBA cv::COLOR_BGRA2RGBA -# endif +#ifndef WITHOUT_NUMPY +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include #endif // WITHOUT_NUMPY #if PY_MAJOR_VERSION >= 3 -# define PyString_FromString PyUnicode_FromString -# define PyInt_FromLong PyLong_FromLong -# define PyString_FromString PyUnicode_FromString +#define PyString_FromString PyUnicode_FromString +#define PyInt_FromLong PyLong_FromLong +#define PyString_FromString PyUnicode_FromString #endif - namespace matplotlibcpp { namespace detail { static std::string s_backend; struct _interpreter { - PyObject *s_python_function_show; - PyObject *s_python_function_close; - PyObject *s_python_function_draw; - PyObject *s_python_function_pause; - PyObject *s_python_function_save; - PyObject *s_python_function_figure; - PyObject *s_python_function_fignum_exists; - PyObject *s_python_function_plot; - PyObject *s_python_function_quiver; - PyObject *s_python_function_semilogx; - PyObject *s_python_function_semilogy; - PyObject *s_python_function_loglog; - PyObject *s_python_function_fill; - PyObject *s_python_function_fill_between; - PyObject *s_python_function_hist; - PyObject *s_python_function_imshow; - PyObject *s_python_function_scatter; - PyObject *s_python_function_boxplot; - PyObject *s_python_function_subplot; - PyObject *s_python_function_subplot2grid; - PyObject *s_python_function_legend; - PyObject *s_python_function_xlim; - PyObject *s_python_function_ion; - PyObject *s_python_function_ginput; - PyObject *s_python_function_ylim; - PyObject *s_python_function_title; - PyObject *s_python_function_axis; - PyObject *s_python_function_axvline; - PyObject *s_python_function_xlabel; - PyObject *s_python_function_ylabel; - PyObject *s_python_function_gca; - PyObject *s_python_function_xticks; - PyObject *s_python_function_yticks; - PyObject *s_python_function_tick_params; - PyObject *s_python_function_grid; - PyObject *s_python_function_clf; - PyObject *s_python_function_errorbar; - PyObject *s_python_function_annotate; - PyObject *s_python_function_tight_layout; - PyObject *s_python_colormap; - PyObject *s_python_empty_tuple; - PyObject *s_python_function_stem; - PyObject *s_python_function_xkcd; - PyObject *s_python_function_text; - PyObject *s_python_function_suptitle; - PyObject *s_python_function_bar; - PyObject *s_python_function_colorbar; - PyObject *s_python_function_subplots_adjust; - - - /* For now, _interpreter is implemented as a singleton since its currently not possible to have - multiple independent embedded python interpreters without patching the python source code - or starting a separate process for each. - http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program - */ - - static _interpreter& get() { - static _interpreter ctx; - return ctx; - } - - PyObject* safe_import(PyObject* module, std::string fname) { - PyObject* fn = PyObject_GetAttrString(module, fname.c_str()); - - if (!fn) - throw std::runtime_error(std::string("Couldn't find required function: ") + fname); - - if (!PyFunction_Check(fn)) - throw std::runtime_error(fname + std::string(" is unexpectedly not a PyFunction.")); - - return fn; - } + PyObject *s_python_function_show; + PyObject *s_python_function_close; + PyObject *s_python_function_draw; + PyObject *s_python_function_pause; + PyObject *s_python_function_save; + PyObject *s_python_function_figure; + PyObject *s_python_function_fignum_exists; + PyObject *s_python_function_plot; + PyObject *s_python_function_quiver; + PyObject *s_python_function_contour; + PyObject *s_python_function_colormap; + PyObject *s_python_function_axhline; + PyObject *s_python_function_axvline; + PyObject *s_python_function_semilogx; + PyObject *s_python_function_semilogy; + PyObject *s_python_function_loglog; + PyObject *s_python_function_fill; + PyObject *s_python_function_fill_between; + PyObject *s_python_function_hist; + PyObject *s_python_function_scatter; + PyObject *s_python_function_spy; + PyObject *s_python_function_subplot; + PyObject *s_python_function_legend; + PyObject *s_python_function_xlim; + PyObject *s_python_function_ion; + PyObject *s_python_function_ginput; + PyObject *s_python_function_ylim; + PyObject *s_python_function_title; + PyObject *s_python_function_axis; + PyObject *s_python_function_xlabel; + PyObject *s_python_function_ylabel; + PyObject *s_python_function_xticks; + PyObject *s_python_function_yticks; + PyObject *s_python_function_xscale; + PyObject *s_python_function_yscale; + PyObject *s_python_function_grid; + PyObject *s_python_function_clf; + PyObject *s_python_function_errorbar; + PyObject *s_python_function_annotate; + PyObject *s_python_function_tight_layout; + PyObject *s_python_colormap; + PyObject *s_python_empty_tuple; + PyObject *s_python_function_stem; + PyObject *s_python_function_xkcd; + PyObject *s_python_function_text; + PyObject *s_python_function_suptitle; + PyObject *s_python_function_bar; + PyObject *s_python_function_subplots_adjust; + PyObject *s_python_function_imshow; + PyObject *s_python_function_colorbar; + + /* For now, _interpreter is implemented as a singleton since its currently not + possible to have multiple independent embedded python interpreters without + patching the python source code or starting a separate process for each. + http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program + */ + + static _interpreter &get() { + static _interpreter ctx; + return ctx; + } private: - #ifndef WITHOUT_NUMPY -# if PY_MAJOR_VERSION >= 3 +#if PY_MAJOR_VERSION >= 3 - void *import_numpy() { - import_array(); // initialize C-API - return NULL; - } + void *import_numpy() { + import_array(); // initialize C-API + return NULL; + } -# else +#else - void import_numpy() { - import_array(); // initialize C-API - } + void import_numpy() { + import_array(); // initialize C-API + } -# endif +#endif #endif - _interpreter() { + _interpreter() { - // optional but recommended + // optional but recommended #if PY_MAJOR_VERSION >= 3 - wchar_t name[] = L"plotting"; + wchar_t name[] = L"plotting"; #else - char name[] = "plotting"; + char name[] = "plotting"; #endif - Py_SetProgramName(name); - Py_Initialize(); + Py_SetProgramName(name); + Py_Initialize(); #ifndef WITHOUT_NUMPY - import_numpy(); // initialize numpy C-API + import_numpy(); // initialize numpy C-API #endif - PyObject* matplotlibname = PyString_FromString("matplotlib"); - PyObject* pyplotname = PyString_FromString("matplotlib.pyplot"); - PyObject* cmname = PyString_FromString("matplotlib.cm"); - PyObject* pylabname = PyString_FromString("pylab"); - if (!pyplotname || !pylabname || !matplotlibname || !cmname) { - throw std::runtime_error("couldnt create string"); - } - - PyObject* matplotlib = PyImport_Import(matplotlibname); - Py_DECREF(matplotlibname); - if (!matplotlib) { - PyErr_Print(); - throw std::runtime_error("Error loading module matplotlib!"); - } - - // matplotlib.use() must be called *before* pylab, matplotlib.pyplot, - // or matplotlib.backends is imported for the first time - if (!s_backend.empty()) { - PyObject_CallMethod(matplotlib, const_cast("use"), const_cast("s"), s_backend.c_str()); - } - - PyObject* pymod = PyImport_Import(pyplotname); - Py_DECREF(pyplotname); - if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); } - - s_python_colormap = PyImport_Import(cmname); - Py_DECREF(cmname); - if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); } - - PyObject* pylabmod = PyImport_Import(pylabname); - Py_DECREF(pylabname); - if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); } - - s_python_function_show = safe_import(pymod, "show"); - s_python_function_close = safe_import(pymod, "close"); - s_python_function_draw = safe_import(pymod, "draw"); - s_python_function_pause = safe_import(pymod, "pause"); - s_python_function_figure = safe_import(pymod, "figure"); - s_python_function_fignum_exists = safe_import(pymod, "fignum_exists"); - s_python_function_plot = safe_import(pymod, "plot"); - s_python_function_quiver = safe_import(pymod, "quiver"); - s_python_function_semilogx = safe_import(pymod, "semilogx"); - s_python_function_semilogy = safe_import(pymod, "semilogy"); - s_python_function_loglog = safe_import(pymod, "loglog"); - s_python_function_fill = safe_import(pymod, "fill"); - s_python_function_fill_between = safe_import(pymod, "fill_between"); - s_python_function_hist = safe_import(pymod,"hist"); - s_python_function_scatter = safe_import(pymod,"scatter"); - s_python_function_boxplot = safe_import(pymod,"boxplot"); - s_python_function_subplot = safe_import(pymod, "subplot"); - s_python_function_subplot2grid = safe_import(pymod, "subplot2grid"); - s_python_function_legend = safe_import(pymod, "legend"); - s_python_function_ylim = safe_import(pymod, "ylim"); - s_python_function_title = safe_import(pymod, "title"); - s_python_function_axis = safe_import(pymod, "axis"); - s_python_function_axvline = safe_import(pymod, "axvline"); - s_python_function_xlabel = safe_import(pymod, "xlabel"); - s_python_function_ylabel = safe_import(pymod, "ylabel"); - s_python_function_gca = safe_import(pymod, "gca"); - s_python_function_xticks = safe_import(pymod, "xticks"); - s_python_function_yticks = safe_import(pymod, "yticks"); - s_python_function_tick_params = safe_import(pymod, "tick_params"); - s_python_function_grid = safe_import(pymod, "grid"); - s_python_function_xlim = safe_import(pymod, "xlim"); - s_python_function_ion = safe_import(pymod, "ion"); - s_python_function_ginput = safe_import(pymod, "ginput"); - s_python_function_save = safe_import(pylabmod, "savefig"); - s_python_function_annotate = safe_import(pymod,"annotate"); - s_python_function_clf = safe_import(pymod, "clf"); - s_python_function_errorbar = safe_import(pymod, "errorbar"); - s_python_function_tight_layout = safe_import(pymod, "tight_layout"); - s_python_function_stem = safe_import(pymod, "stem"); - s_python_function_xkcd = safe_import(pymod, "xkcd"); - s_python_function_text = safe_import(pymod, "text"); - s_python_function_suptitle = safe_import(pymod, "suptitle"); - s_python_function_bar = safe_import(pymod,"bar"); - s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); - s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust"); -#ifndef WITHOUT_NUMPY - s_python_function_imshow = safe_import(pymod, "imshow"); -#endif - s_python_empty_tuple = PyTuple_New(0); + PyObject *matplotlibname = PyString_FromString("matplotlib"); + PyObject *pyplotname = PyString_FromString("matplotlib.pyplot"); + PyObject *cmname = PyString_FromString("matplotlib.cm"); + PyObject *pylabname = PyString_FromString("pylab"); + if (!pyplotname || !pylabname || !matplotlibname || !cmname) { + throw std::runtime_error("couldnt create string"); + } + + PyObject *matplotlib = PyImport_Import(matplotlibname); + Py_DECREF(matplotlibname); + if (!matplotlib) { + PyErr_Print(); + throw std::runtime_error("Error loading module matplotlib!"); + } + + // matplotlib.use() must be called *before* pylab, matplotlib.pyplot, + // or matplotlib.backends is imported for the first time + if (!s_backend.empty()) { + PyObject_CallMethod(matplotlib, const_cast("use"), + const_cast("s"), s_backend.c_str()); + } + + PyObject *pymod = PyImport_Import(pyplotname); + Py_DECREF(pyplotname); + if (!pymod) { + throw std::runtime_error("Error loading module matplotlib.pyplot!"); + } + + s_python_colormap = PyImport_Import(cmname); + Py_DECREF(cmname); + if (!s_python_colormap) { + throw std::runtime_error("Error loading module matplotlib.cm!"); + } + + PyObject *pylabmod = PyImport_Import(pylabname); + Py_DECREF(pylabname); + if (!pylabmod) { + throw std::runtime_error("Error loading module pylab!"); + } + + s_python_function_show = PyObject_GetAttrString(pymod, "show"); + s_python_function_close = PyObject_GetAttrString(pymod, "close"); + s_python_function_draw = PyObject_GetAttrString(pymod, "draw"); + s_python_function_pause = PyObject_GetAttrString(pymod, "pause"); + s_python_function_figure = PyObject_GetAttrString(pymod, "figure"); + s_python_function_fignum_exists = + PyObject_GetAttrString(pymod, "fignum_exists"); + s_python_function_plot = PyObject_GetAttrString(pymod, "plot"); + s_python_function_quiver = PyObject_GetAttrString(pymod, "quiver"); + s_python_function_contour = PyObject_GetAttrString(pymod, "contour"); + s_python_function_axhline = PyObject_GetAttrString(pymod, "axhline"); + s_python_function_axvline = PyObject_GetAttrString(pymod, "axvline"); + s_python_function_semilogx = PyObject_GetAttrString(pymod, "semilogx"); + s_python_function_semilogy = PyObject_GetAttrString(pymod, "semilogy"); + s_python_function_loglog = PyObject_GetAttrString(pymod, "loglog"); + s_python_function_fill = PyObject_GetAttrString(pymod, "fill"); + s_python_function_fill_between = + PyObject_GetAttrString(pymod, "fill_between"); + s_python_function_hist = PyObject_GetAttrString(pymod, "hist"); + s_python_function_scatter = PyObject_GetAttrString(pymod, "scatter"); + s_python_function_spy = PyObject_GetAttrString(pymod, "spy"); + s_python_function_subplot = PyObject_GetAttrString(pymod, "subplot"); + s_python_function_legend = PyObject_GetAttrString(pymod, "legend"); + s_python_function_ylim = PyObject_GetAttrString(pymod, "ylim"); + s_python_function_title = PyObject_GetAttrString(pymod, "title"); + s_python_function_axis = PyObject_GetAttrString(pymod, "axis"); + s_python_function_xlabel = PyObject_GetAttrString(pymod, "xlabel"); + s_python_function_ylabel = PyObject_GetAttrString(pymod, "ylabel"); + s_python_function_xticks = PyObject_GetAttrString(pymod, "xticks"); + s_python_function_yticks = PyObject_GetAttrString(pymod, "yticks"); + s_python_function_xscale = PyObject_GetAttrString(pymod, "xscale"); + s_python_function_yscale = PyObject_GetAttrString(pymod, "yscale"); + s_python_function_grid = PyObject_GetAttrString(pymod, "grid"); + s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim"); + s_python_function_ion = PyObject_GetAttrString(pymod, "ion"); + s_python_function_ginput = PyObject_GetAttrString(pymod, "ginput"); + s_python_function_save = PyObject_GetAttrString(pylabmod, "savefig"); + s_python_function_annotate = PyObject_GetAttrString(pymod, "annotate"); + s_python_function_clf = PyObject_GetAttrString(pymod, "clf"); + s_python_function_errorbar = PyObject_GetAttrString(pymod, "errorbar"); + s_python_function_tight_layout = + PyObject_GetAttrString(pymod, "tight_layout"); + s_python_function_stem = PyObject_GetAttrString(pymod, "stem"); + s_python_function_xkcd = PyObject_GetAttrString(pymod, "xkcd"); + s_python_function_text = PyObject_GetAttrString(pymod, "text"); + s_python_function_suptitle = PyObject_GetAttrString(pymod, "suptitle"); + s_python_function_bar = PyObject_GetAttrString(pymod, "bar"); + s_python_function_subplots_adjust = + PyObject_GetAttrString(pymod, "subplots_adjust"); + s_python_function_imshow = PyObject_GetAttrString(pymod, "imshow"); + s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); + + if (!s_python_function_show || !s_python_function_close || + !s_python_function_draw || !s_python_function_pause || + !s_python_function_figure || !s_python_function_fignum_exists || + !s_python_function_plot || !s_python_function_quiver || + !s_python_function_contour || !s_python_function_colorbar || + !s_python_function_semilogx || !s_python_function_semilogy || + !s_python_function_loglog || !s_python_function_fill || + !s_python_function_fill_between || !s_python_function_subplot || + !s_python_function_legend || !s_python_function_ylim || + !s_python_function_title || !s_python_function_axis || + !s_python_function_xlabel || !s_python_function_ylabel || + !s_python_function_xticks || !s_python_function_yticks || + !s_python_function_xscale || !s_python_function_yscale || + !s_python_function_grid || !s_python_function_xlim || + !s_python_function_ion || !s_python_function_ginput || + !s_python_function_save || !s_python_function_clf || + !s_python_function_annotate || !s_python_function_errorbar || + !s_python_function_errorbar || !s_python_function_tight_layout || + !s_python_function_stem || !s_python_function_xkcd || + !s_python_function_text || !s_python_function_suptitle || + !s_python_function_bar || !s_python_function_subplots_adjust || + !s_python_function_spy || !s_python_function_imshow) { + throw std::runtime_error("Couldn't find required function!"); } - ~_interpreter() { - Py_Finalize(); + if (!PyFunction_Check(s_python_function_show) || + !PyFunction_Check(s_python_function_close) || + !PyFunction_Check(s_python_function_draw) || + !PyFunction_Check(s_python_function_pause) || + !PyFunction_Check(s_python_function_figure) || + !PyFunction_Check(s_python_function_fignum_exists) || + !PyFunction_Check(s_python_function_plot) || + !PyFunction_Check(s_python_function_quiver) || + !PyFunction_Check(s_python_function_contour) || + !PyFunction_Check(s_python_function_semilogx) || + !PyFunction_Check(s_python_function_semilogy) || + !PyFunction_Check(s_python_function_loglog) || + !PyFunction_Check(s_python_function_fill) || + !PyFunction_Check(s_python_function_fill_between) || + !PyFunction_Check(s_python_function_spy) || + !PyFunction_Check(s_python_function_subplot) || + !PyFunction_Check(s_python_function_legend) || + !PyFunction_Check(s_python_function_annotate) || + !PyFunction_Check(s_python_function_ylim) || + !PyFunction_Check(s_python_function_title) || + !PyFunction_Check(s_python_function_axis) || + !PyFunction_Check(s_python_function_xlabel) || + !PyFunction_Check(s_python_function_ylabel) || + !PyFunction_Check(s_python_function_xticks) || + !PyFunction_Check(s_python_function_yticks) || + !PyFunction_Check(s_python_function_xscale) || + !PyFunction_Check(s_python_function_yscale) || + !PyFunction_Check(s_python_function_grid) || + !PyFunction_Check(s_python_function_xlim) || + !PyFunction_Check(s_python_function_ion) || + !PyFunction_Check(s_python_function_ginput) || + !PyFunction_Check(s_python_function_save) || + !PyFunction_Check(s_python_function_clf) || + !PyFunction_Check(s_python_function_tight_layout) || + !PyFunction_Check(s_python_function_errorbar) || + !PyFunction_Check(s_python_function_stem) || + !PyFunction_Check(s_python_function_xkcd) || + !PyFunction_Check(s_python_function_text) || + !PyFunction_Check(s_python_function_suptitle) || + !PyFunction_Check(s_python_function_bar) || + !PyFunction_Check(s_python_function_subplots_adjust) || + !PyFunction_Check(s_python_function_imshow) || + !PyFunction_Check(s_python_function_colorbar) + ) { + throw std::runtime_error( + "Python object is unexpectedly not a PyFunction."); } + + s_python_empty_tuple = PyTuple_New(0); + } + + ~_interpreter() { Py_Finalize(); } }; } // end namespace detail -/// Select the backend -/// -/// **NOTE:** This must be called before the first plot command to have -/// any effect. -/// -/// Mainly useful to select the non-interactive 'Agg' backend when running -/// matplotlibcpp in headless mode, for example on a machine with no display. -/// -/// See also: https://matplotlib.org/2.0.2/api/matplotlib_configuration_api.html#matplotlib.use -inline void backend(const std::string& name) -{ - detail::s_backend = name; -} - -inline bool annotate(std::string annotation, double x, double y) -{ - detail::_interpreter::get(); +// must be called before the first regular call to matplotlib to have any effect +inline void backend(const std::string &name) { detail::s_backend = name; } - PyObject * xy = PyTuple_New(2); - PyObject * str = PyString_FromString(annotation.c_str()); +inline bool annotate(std::string annotation, double x, double y) { + detail::_interpreter::get(); - PyTuple_SetItem(xy,0,PyFloat_FromDouble(x)); - PyTuple_SetItem(xy,1,PyFloat_FromDouble(y)); + PyObject *xy = PyTuple_New(2); + PyObject *str = PyString_FromString(annotation.c_str()); - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "xy", xy); + PyTuple_SetItem(xy, 0, PyFloat_FromDouble(x)); + PyTuple_SetItem(xy, 1, PyFloat_FromDouble(y)); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, str); + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "xy", xy); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, str); - Py_DECREF(args); - Py_DECREF(kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_annotate, args, kwargs); - if(res) Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(kwargs); - return res; -} + if (res) + Py_DECREF(res); -namespace detail { + return res; +} #ifndef WITHOUT_NUMPY // Type selector for numpy array conversion -template struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default -template <> struct select_npy_type { const static NPY_TYPES type = NPY_DOUBLE; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_FLOAT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_BOOL; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT8; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_SHORT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT8; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_USHORT; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_ULONG; }; -template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; - -// Sanity checks; comment them out or change the numpy type below if you're compiling on -// a platform where they don't apply -static_assert(sizeof(long long) == 8); -template <> struct select_npy_type { const static NPY_TYPES type = NPY_INT64; }; -static_assert(sizeof(unsigned long long) == 8); -template <> struct select_npy_type { const static NPY_TYPES type = NPY_UINT64; }; -// TODO: add int, long, etc. - -template -PyObject* get_array(const std::vector& v) -{ +template struct select_npy_type { + const static NPY_TYPES type = NPY_NOTYPE; +}; // Default +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_DOUBLE; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_FLOAT; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_BOOL; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_INT8; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_SHORT; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_INT; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_INT64; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_UINT8; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_USHORT; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_ULONG; +}; +template <> struct select_npy_type { + const static NPY_TYPES type = NPY_UINT64; +}; + +template PyObject *get_array(const Vector &v) { + detail::_interpreter::get(); // interpreter needs to be initialized for the + // numpy commands to work + // both Eigen::Matrix<..> and std::vector<..> have the member value_type + NPY_TYPES type = select_npy_type::type; + if (type == NPY_NOTYPE) { + std::vector vd(v.size()); npy_intp vsize = v.size(); - NPY_TYPES type = select_npy_type::type; - if (type == NPY_NOTYPE) { - size_t memsize = v.size()*sizeof(double); - double* dp = static_cast(::malloc(memsize)); - for (size_t i=0; i(varray), NPY_ARRAY_OWNDATA); - return varray; - } - - PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data())); + // Eigen Vectors do not support begin/end() in the currently stable version + // this can be changed once Eigen 3.4. is released + // data() returns a pointer to the storage of the first element. If the + // vector is modified afterwards, it may be rendedered invalid. + // Note, that this is not an issue since get_array() is called by a + // plot command using the instantaneous state of the vector. + // The pointer is not reused. If the vector is plotted anew, data() is + // called again and again get's the current, valid storage location. + std::copy(v.data(), v.data() + v.size(), vd.begin()); + PyObject *varray = + PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void *)(vd.data())); return varray; -} + } + npy_intp vsize = v.size(); + PyObject *varray = + PyArray_SimpleNewFromData(1, &vsize, type, (void *)(v.data())); + return varray; +} -template -PyObject* get_2darray(const std::vector<::std::vector>& v) -{ - if (v.size() < 1) throw std::runtime_error("get_2d_array v too small"); +// specialized get_2darray function for nested std::vectors +template +PyObject *get_2darray(const std::vector<::std::vector> &v) { + detail::_interpreter::get(); // interpreter needs to be initialized for the + // numpy commands to work + if (v.size() < 1) + throw std::runtime_error("get_2darray v too small"); - npy_intp vsize[2] = {static_cast(v.size()), - static_cast(v[0].size())}; + npy_intp vsize[2] = {static_cast(v.size()), + static_cast(v[0].size())}; - PyArrayObject *varray = - (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE); + PyArrayObject *varray = + (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE); - double *vd_begin = static_cast(PyArray_DATA(varray)); + double *vd_begin = static_cast(PyArray_DATA(varray)); - for (const ::std::vector &v_row : v) { - if (v_row.size() != static_cast(vsize[1])) - throw std::runtime_error("Missmatched array size"); - std::copy(v_row.begin(), v_row.end(), vd_begin); - vd_begin += vsize[1]; - } + for (const ::std::vector &v_row : v) { + if (v_row.size() != static_cast(vsize[1])) + throw std::runtime_error("mismatched array size"); + std::copy(v_row.begin(), v_row.end(), vd_begin); + vd_begin += vsize[1]; + } - return reinterpret_cast(varray); + return reinterpret_cast(varray); } -#else // fallback if we don't have numpy: copy every element of the given vector +// suitable for more general matrices (especially Eigen matrices) +template PyObject *get_2darray(const Matrix &A) { + detail::_interpreter::get(); // interpreter needs to be initialized for the + // numpy commands to work + if (A.size() < 1) + throw std::runtime_error("get_2darray A too small"); -template -PyObject* get_array(const std::vector& v) -{ - PyObject* list = PyList_New(v.size()); - for(size_t i = 0; i < v.size(); ++i) { - PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i))); + npy_intp vsize[2] = {static_cast(A.rows()), + static_cast(A.cols())}; + + PyArrayObject *varray = + (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE); + + double *vd_begin = static_cast(PyArray_DATA(varray)); + + for (std::size_t i = 0; i < A.rows(); ++i) { + for (std::size_t j = 0; j < A.cols(); ++j) { + *(vd_begin + i * A.cols() + j) = A(i, j); } - return list; + } + + return reinterpret_cast(varray); } -#endif // WITHOUT_NUMPY +#else // fallback if we don't have numpy: copy every element of the given vector -// sometimes, for labels and such, we need string arrays -inline PyObject * get_array(const std::vector& strings) -{ - PyObject* list = PyList_New(strings.size()); - for (std::size_t i = 0; i < strings.size(); ++i) { - PyList_SetItem(list, i, PyString_FromString(strings[i].c_str())); +template PyObject *get_array(const Vector &v) { + detail::_interpreter::get(); + PyObject *list = PyList_New(v.size()); + for (size_t i = 0; i < v.size(); ++i) { + PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i))); } return list; } -// not all matplotlib need 2d arrays, some prefer lists of lists -template -PyObject* get_listlist(const std::vector>& ll) -{ - PyObject* listlist = PyList_New(ll.size()); - for (std::size_t i = 0; i < ll.size(); ++i) { - PyList_SetItem(listlist, i, get_array(ll[i])); +#endif // WITHOUT_NUMPY + +namespace detail { +// @brief Since most of the plot commands require the exact same usage apart +// from the call to the correct Python function, we encapsulate this +// @param pyfunc The matplotlib function to be called with the given arguments +// @param x The x vector +// @param y The y vector +// @param s The formatting string for colour, marker and linestyle +// @param keywords Additional keywords, such as label +// @return true if plot was successful, false otherwise +template +bool plot_base(PyObject *const pyfunc, const VectorX &x, const VectorY &y, + const std::string &s = "", + const std::map &keywords = {}) { + assert(x.size() == y.size()); + + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); + + PyObject *pystring = PyString_FromString(s.c_str()); + + PyObject *plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); + + PyObject *kwargs = PyDict_New(); + for (auto const &item : keywords) { + PyDict_SetItemString(kwargs, item.first.c_str(), + PyString_FromString(item.second.c_str())); } - return listlist; + + PyObject *res = PyObject_Call(pyfunc, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); + + return res; } } // namespace detail -/// Plot a line through the given x and y data points.. -/// -/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html -template -bool plot(const std::vector &x, const std::vector &y, const std::map& keywords) -{ - assert(x.size() == y.size()); +// @brief standard plot function supporting the args (x, y, s, keywords) +// @param x The x vector +// @param y The y vector +// @param s The formatting string +// @param keywords Additional keywords +// @return true, if successful, false otherwise +template +bool plot(const VectorX &x, const VectorY &y, const std::string &s = "", + const std::map &keywords = {}) { + return detail::plot_base(detail::_interpreter::get().s_python_function_plot, + x, y, s, keywords); +} - detail::_interpreter::get(); +// @brief standard plot function without formatting string, needed if +// keywords are given but formatting string is not +template +bool plot(const VectorX &x, const VectorY &y, + const std::map &keywords) { + return plot(x, y, "", keywords); +} - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); +// @brief standard plot function if x data is not specified +template > +bool plot(const VectorY &y, const std::string &format = "", + const std::map &keywords = {}) { + // Note: cannot be an unsigned type for some reason, yields an overflow + // problem.. + std::vector x(y.size()); + for (int i = 0; i < x.size(); ++i) + x.at(i) = i; - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } + return plot(x, y, format); +} - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs); +// @brief standard plot function if x data is not specified and the formatting +// string is missing +template > +bool plot(const VectorY &y, + const std::map &keywords) { + std::vector x(y.size()); + for (int i = 0; i < x.size(); ++i) + x.at(i) = i; - Py_DECREF(args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); + return plot(x, y, "", keywords); +} - return res; +// @brief loglog plot function, see `plot` for more detail +template +bool loglog(const VectorX &x, const VectorY &y, const std::string &s = "", + const std::map &keywords = {}) { + return detail::plot_base(detail::_interpreter::get().s_python_function_loglog, + x, y, s, keywords); } -// TODO - it should be possible to make this work by implementing -// a non-numpy alternative for `detail::get_2darray()`. -#ifndef WITHOUT_NUMPY -template -void plot_surface(const std::vector<::std::vector> &x, - const std::vector<::std::vector> &y, - const std::vector<::std::vector> &z, - const std::map &keywords = - std::map()) -{ - detail::_interpreter::get(); +template +bool loglog(const VectorX &x, const VectorY &y, + const std::map &keywords) { + return loglog(x, y, "", keywords); +} + +template > +bool loglog(const VectorY &y, const std::string &s = "", + const std::map &keywords = {}) { + std::vector x(y.size()); + for (std::size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + + return loglog(x, y, s, keywords); +} +template > +bool loglog(const VectorY &y, + const std::map &keywords) { + std::vector x(y.size()); + for (std::size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + + return loglog(x, y, "", keywords); +} + +// @brief semilogx plot function, see `plot` for more detail +template +bool semilogx(const VectorX &x, const VectorY &y, const std::string &s = "", + const std::map &keywords = {}) { + return detail::plot_base( + detail::_interpreter::get().s_python_function_semilogx, x, y, s, + keywords); +} + +template +bool semilogx(const VectorX &x, const VectorY &y, + const std::map &keywords) { + return semilogx(x, y, "", keywords); +} + +template > +bool semilogx(const VectorY &y, const std::string &s = "", + const std::map &keywords = {}) { + std::vector x(y.size()); + for (std::size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + + return semilogx(x, y, s, keywords); +} + +template > +bool semilogx(const VectorY &y, + const std::map &keywords) { + std::vector x(y.size()); + for (std::size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + + return semilogx(x, y, "", keywords); +} + +// @brief semilogy plot function, see `plot` for more detail +template +bool semilogy(const VectorX &x, const VectorY &y, const std::string &s = "", + const std::map &keywords = {}) { + return detail::plot_base( + detail::_interpreter::get().s_python_function_semilogy, x, y, s, + keywords); +} + +template +bool semilogy(const VectorX &x, const VectorY &y, + const std::map &keywords) { + return semilogy(x, y, "", keywords); +} + +template > +bool semilogy(const VectorY &y, const std::string &s = "", + const std::map &keywords = {}) { + std::vector x(y.size()); + for (std::size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + + return semilogy(x, y, s, keywords); +} + +template > +bool semilogy(const VectorY &y, + const std::map &keywords) { + std::vector x(y.size()); + for (std::size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + + return semilogy(x, y, "", keywords); +} + +template +void imshow(const Matrix& X, const std::map &keywords = {}) { + PyObject *Xarray = get_2darray(X); + + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + + PyObject *plot_args = PyTuple_New(1); + PyTuple_SetItem(plot_args, 0, Xarray); + + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_imshow, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); +} + +// @brief Add the colorbar +void colorbar() { + PyObject *res = + PyObject_CallObject(detail::_interpreter::get().s_python_function_colorbar, + detail::_interpreter::get().s_python_empty_tuple); + if (!res) + throw std::runtime_error("Call to colorbar() failed."); +} + +// @brief plot_surface for datapoints (x_ij, y_ij, z_ij) with i,j = 0..n +// @param x The x values of the datapoints in a matrix +// @param y The y values of the datapoints in a matrix +// @param z The function value of the datapoints in a matrix +// @param keywords Additional keywords +template +void plot_surface(const Matrix &x, const Matrix &y, const Matrix &z, + const std::map &keywords = + std::map()) { // We lazily load the modules here the first time this function is called // because I'm not sure that we can assume "matplotlib installed" implies // "mpl_toolkits installed" on all platforms, and we don't want to require @@ -443,26 +700,32 @@ void plot_surface(const std::vector<::std::vector> &x, if (!mpl_toolkitsmod) { detail::_interpreter::get(); - PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); - PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); - if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } + PyObject *mpl_toolkits = PyString_FromString("mpl_toolkits"); + PyObject *axis3d = PyString_FromString("mpl_toolkits.mplot3d"); + if (!mpl_toolkits || !axis3d) { + throw std::runtime_error("couldnt create string"); + } mpl_toolkitsmod = PyImport_Import(mpl_toolkits); Py_DECREF(mpl_toolkits); - if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } + if (!mpl_toolkitsmod) { + throw std::runtime_error("Error loading module mpl_toolkits!"); + } axis3dmod = PyImport_Import(axis3d); Py_DECREF(axis3d); - if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } + if (!axis3dmod) { + throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); + } } assert(x.size() == y.size()); assert(y.size() == z.size()); // using numpy arrays - PyObject *xarray = detail::get_2darray(x); - PyObject *yarray = detail::get_2darray(y); - PyObject *zarray = detail::get_2darray(z); + PyObject *xarray = get_2darray(x); + PyObject *yarray = get_2darray(y); + PyObject *zarray = get_2darray(z); // construct positional args PyObject *args = PyTuple_New(3); @@ -486,77 +749,59 @@ void plot_surface(const std::vector<::std::vector> &x, PyString_FromString(it->second.c_str())); } - PyObject *fig = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple); - if (!fig) throw std::runtime_error("Call to figure() failed."); + if (!fig) + throw std::runtime_error("Call to figure() failed."); PyObject *gca_kwargs = PyDict_New(); PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); PyObject *gca = PyObject_GetAttrString(fig, "gca"); - if (!gca) throw std::runtime_error("No gca"); + if (!gca) + throw std::runtime_error("No gca"); Py_INCREF(gca); PyObject *axis = PyObject_Call( gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); - if (!axis) throw std::runtime_error("No axis"); + if (!axis) + throw std::runtime_error("No axis"); Py_INCREF(axis); Py_DECREF(gca); Py_DECREF(gca_kwargs); PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface"); - if (!plot_surface) throw std::runtime_error("No surface"); + if (!plot_surface) + throw std::runtime_error("No surface"); Py_INCREF(plot_surface); PyObject *res = PyObject_Call(plot_surface, args, kwargs); - if (!res) throw std::runtime_error("failed surface"); + if (!res) + throw std::runtime_error("failed surface"); Py_DECREF(plot_surface); Py_DECREF(axis); Py_DECREF(args); Py_DECREF(kwargs); - if (res) Py_DECREF(res); + if (res) + Py_DECREF(res); } -#endif // WITHOUT_NUMPY -template -void plot3(const std::vector &x, - const std::vector &y, - const std::vector &z, - const std::map &keywords = - std::map()) -{ +// @brief plot_surface for datapoints (x_ij, y_ij, z_ij) with i,j = 0..n +// @param x The x values of the datapoints in a matrix +// @param y The y values of the datapoints in a matrix +// @param z The function value of the datapoints in a matrix +// @param keywords Additional keywords +template +void contour(const Matrix &x, const Matrix &y, const Matrix &z, + const std::map &keywords = {}) { detail::_interpreter::get(); - // Same as with plot_surface: We lazily load the modules here the first time - // this function is called because I'm not sure that we can assume "matplotlib - // installed" implies "mpl_toolkits installed" on all platforms, and we don't - // want to require it for people who don't need 3d plots. - static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; - if (!mpl_toolkitsmod) { - detail::_interpreter::get(); - - PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); - PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); - if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } - - mpl_toolkitsmod = PyImport_Import(mpl_toolkits); - Py_DECREF(mpl_toolkits); - if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } - - axis3dmod = PyImport_Import(axis3d); - Py_DECREF(axis3d); - if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } - } - - assert(x.size() == y.size()); - assert(y.size() == z.size()); - - PyObject *xarray = detail::get_array(x); - PyObject *yarray = detail::get_array(y); - PyObject *zarray = detail::get_array(z); + // using numpy arrays + PyObject *xarray = get_2darray(x); + PyObject *yarray = get_2darray(y); + PyObject *zarray = get_2darray(z); // construct positional args PyObject *args = PyTuple_New(3); @@ -567,1770 +812,1228 @@ void plot3(const std::vector &x, // Build up the kw args. PyObject *kwargs = PyDict_New(); + PyObject *python_colormap_coolwarm = PyObject_GetAttrString( + detail::_interpreter::get().s_python_colormap, "coolwarm"); + + PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm); + for (std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) { PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); } - PyObject *fig = - PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple); - if (!fig) throw std::runtime_error("Call to figure() failed."); - - PyObject *gca_kwargs = PyDict_New(); - PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d")); - - PyObject *gca = PyObject_GetAttrString(fig, "gca"); - if (!gca) throw std::runtime_error("No gca"); - Py_INCREF(gca); - PyObject *axis = PyObject_Call( - gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs); - - if (!axis) throw std::runtime_error("No axis"); - Py_INCREF(axis); - - Py_DECREF(gca); - Py_DECREF(gca_kwargs); - - PyObject *plot3 = PyObject_GetAttrString(axis, "plot"); - if (!plot3) throw std::runtime_error("No 3D line plot"); - Py_INCREF(plot3); - PyObject *res = PyObject_Call(plot3, args, kwargs); - if (!res) throw std::runtime_error("Failed 3D line plot"); - Py_DECREF(plot3); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_contour, args, kwargs); + if (!res) + throw std::runtime_error("failed surface"); - Py_DECREF(axis); Py_DECREF(args); Py_DECREF(kwargs); - if (res) Py_DECREF(res); + if (res) + Py_DECREF(res); } -template -bool stem(const std::vector &x, const std::vector &y, const std::map& keywords) -{ - assert(x.size() == y.size()); +template +bool stem(const std::vector &x, const std::vector &y, + const std::map &keywords) { + assert(x.size() == y.size()); - detail::_interpreter::get(); + // using numpy arrays + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + // construct positional args + PyObject *args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for (std::map::const_iterator it = - keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyString_FromString(it->second.c_str())); - } + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } - PyObject* res = PyObject_Call( - detail::_interpreter::get().s_python_function_stem, args, kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_stem, args, kwargs); - Py_DECREF(args); - Py_DECREF(kwargs); - if (res) - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); - return res; + return res; } -template< typename Numeric > -bool fill(const std::vector& x, const std::vector& y, const std::map& keywords) -{ - assert(x.size() == y.size()); +template +bool fill(const std::vector &x, const std::vector &y, + const std::map &keywords) { + assert(x.size() == y.size()); - detail::_interpreter::get(); + // using numpy arrays + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + // construct positional args + PyObject *args = PyTuple_New(2); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, yarray); - // construct positional args - PyObject* args = PyTuple_New(2); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, yarray); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill, args, kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_fill, args, kwargs); - Py_DECREF(args); - Py_DECREF(kwargs); - - if (res) Py_DECREF(res); - - return res; -} - -template< typename Numeric > -bool fill_between(const std::vector& x, const std::vector& y1, const std::vector& y2, const std::map& keywords) -{ - assert(x.size() == y1.size()); - assert(x.size() == y2.size()); - - detail::_interpreter::get(); - - // using numpy arrays - PyObject* xarray = detail::get_array(x); - PyObject* y1array = detail::get_array(y1); - PyObject* y2array = detail::get_array(y2); - - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, xarray); - PyTuple_SetItem(args, 1, y1array); - PyTuple_SetItem(args, 2, y2array); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs); + Py_DECREF(args); + Py_DECREF(kwargs); - Py_DECREF(args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); + if (res) + Py_DECREF(res); - return res; + return res; } -template< typename Numeric> -bool hist(const std::vector& y, long bins=10,std::string color="b", - double alpha=1.0, bool cumulative=false) -{ - detail::_interpreter::get(); - - PyObject* yarray = detail::get_array(y); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); - PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); - PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); - PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False); - - PyObject* plot_args = PyTuple_New(1); +template +bool fill_between(const std::vector &x, const std::vector &y1, + const std::vector &y2, + const std::map &keywords) { + assert(x.size() == y1.size()); + assert(x.size() == y2.size()); - PyTuple_SetItem(plot_args, 0, yarray); + // using numpy arrays + PyObject *xarray = get_array(x); + PyObject *y1array = get_array(y1); + PyObject *y2array = get_array(y2); + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, xarray); + PyTuple_SetItem(args, 1, y1array); + PyTuple_SetItem(args, 2, y2array); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_fill_between, args, kwargs); - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); - return res; + return res; } -#ifndef WITHOUT_NUMPY -namespace detail { +template +bool hist(const VectorY &y, long bins = 10, std::string color = "b", + double alpha = 1.0, bool cumulative = false) { -inline void imshow(void *ptr, const NPY_TYPES type, const int rows, const int columns, const int colors, const std::map &keywords, PyObject** out) -{ - assert(type == NPY_UINT8 || type == NPY_FLOAT); - assert(colors == 1 || colors == 3 || colors == 4); + PyObject *yarray = get_array(y); - detail::_interpreter::get(); - - // construct args - npy_intp dims[3] = { rows, columns, colors }; - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyArray_SimpleNewFromData(colors == 1 ? 2 : 3, dims, type, ptr)); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); + PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); + PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); + PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False); - PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_imshow, args, kwargs); - Py_DECREF(args); - Py_DECREF(kwargs); - if (!res) - throw std::runtime_error("Call to imshow() failed"); - if (out) - *out = res; - else - Py_DECREF(res); -} + PyObject *plot_args = PyTuple_New(1); -} // namespace detail + PyTuple_SetItem(plot_args, 0, yarray); -inline void imshow(const unsigned char *ptr, const int rows, const int columns, const int colors, const std::map &keywords = {}, PyObject** out = nullptr) -{ - detail::imshow((void *) ptr, NPY_UINT8, rows, columns, colors, keywords, out); -} - -inline void imshow(const float *ptr, const int rows, const int columns, const int colors, const std::map &keywords = {}, PyObject** out = nullptr) -{ - detail::imshow((void *) ptr, NPY_FLOAT, rows, columns, colors, keywords, out); -} - -#ifdef WITH_OPENCV -void imshow(const cv::Mat &image, const std::map &keywords = {}) -{ - // Convert underlying type of matrix, if needed - cv::Mat image2; - NPY_TYPES npy_type = NPY_UINT8; - switch (image.type() & CV_MAT_DEPTH_MASK) { - case CV_8U: - image2 = image; - break; - case CV_32F: - image2 = image; - npy_type = NPY_FLOAT; - break; - default: - image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels())); - } + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); - // If color image, convert from BGR to RGB - switch (image2.channels()) { - case 3: - cv::cvtColor(image2, image2, CV_BGR2RGB); - break; - case 4: - cv::cvtColor(image2, image2, CV_BGRA2RGBA); - } + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); - detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords); + return res; } -#endif // WITH_OPENCV -#endif // WITHOUT_NUMPY - -template -bool scatter(const std::vector& x, - const std::vector& y, - const double s=1.0, // The marker size in points**2 - const std::map & keywords = {}) -{ - detail::_interpreter::get(); - assert(x.size() == y.size()); +// @brief Scatter plot +// @param x x-coordinates of the 2d points +// @param y y-coordinates of the 2d points +// @param s the marker size in points**2 +// @param keywords Additional keywords +template +bool scatter(const VectorX &x, const VectorY &y, const double s = 1.0, + const std::map keywords = {}) { + assert(x.size() == y.size()); - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s)); - for (const auto& it : keywords) - { - PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); - } + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "s", PyFloat_FromDouble(s)); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - PyObject* plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs); - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); - return res; + return res; } -template -bool boxplot(const std::vector>& data, - const std::vector& labels = {}, - const std::map & keywords = {}) -{ - detail::_interpreter::get(); - - PyObject* listlist = detail::get_listlist(data); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, listlist); - - PyObject* kwargs = PyDict_New(); - - // kwargs needs the labels, if there are (the correct number of) labels - if (!labels.empty() && labels.size() == data.size()) { - PyDict_SetItemString(kwargs, "labels", detail::get_array(labels)); - } - - // take care of the remaining keywords - for (const auto& it : keywords) - { - PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs); - - Py_DECREF(args); - Py_DECREF(kwargs); - - if(res) Py_DECREF(res); - - return res; +template +bool scatter(const VectorX &x, const VectorY &y, + const std::map &keywords) { + return scatter(x, y, 1.0, keywords); } -template -bool boxplot(const std::vector& data, - const std::map & keywords = {}) -{ - detail::_interpreter::get(); - - PyObject* vector = detail::get_array(data); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, vector); +// @brief Spy plot +// @param A the matrix +// @param precision Plot all elements above `|precision|` +// @param keywords Additional keywords +template +bool spy(const Matrix &A, + const std::map &keywords = {}) { + PyObject *Aarray = get_2darray(A); - PyObject* kwargs = PyDict_New(); - for (const auto& it : keywords) - { - PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str())); - } + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs); + PyObject *plot_args = PyTuple_New(1); + PyTuple_SetItem(plot_args, 0, Aarray); - Py_DECREF(args); - Py_DECREF(kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_spy, plot_args, kwargs); - if(res) Py_DECREF(res); + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); - return res; + return res; } template -bool bar(const std::vector & x, - const std::vector & y, - std::string ec = "black", - std::string ls = "-", - double lw = 1.0, - const std::map & keywords = {}) -{ - detail::_interpreter::get(); +bool bar(const std::vector &y, std::string ec = "black", + std::string ls = "-", double lw = 1.0, + const std::map &keywords = {}) { + PyObject *yarray = get_array(y); - PyObject * xarray = detail::get_array(x); - PyObject * yarray = detail::get_array(y); + std::vector x; + for (int i = 0; i < y.size(); i++) + x.push_back(i); - PyObject * kwargs = PyDict_New(); + PyObject *xarray = get_array(x); + + PyObject *kwargs = PyDict_New(); PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str())); PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str())); PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw)); - for (std::map::const_iterator it = - keywords.begin(); - it != keywords.end(); - ++it) { - PyDict_SetItemString( - kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject * plot_args = PyTuple_New(2); + PyObject *plot_args = PyTuple_New(2); PyTuple_SetItem(plot_args, 0, xarray); PyTuple_SetItem(plot_args, 1, yarray); - PyObject * res = PyObject_Call( - detail::_interpreter::get().s_python_function_bar, plot_args, kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_bar, plot_args, kwargs); Py_DECREF(plot_args); Py_DECREF(kwargs); - if (res) Py_DECREF(res); + if (res) + Py_DECREF(res); return res; } -template -bool bar(const std::vector & y, - std::string ec = "black", - std::string ls = "-", - double lw = 1.0, - const std::map & keywords = {}) -{ - using T = typename std::remove_reference::type::value_type; - - detail::_interpreter::get(); - - std::vector x; - for (std::size_t i = 0; i < y.size(); i++) { x.push_back(i); } - - return bar(x, y, ec, ls, lw, keywords); -} - -inline bool subplots_adjust(const std::map& keywords = {}) -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - for (std::map::const_iterator it = - keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), - PyFloat_FromDouble(it->second)); - } - - - PyObject* plot_args = PyTuple_New(0); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; -} - -template< typename Numeric> -bool named_hist(std::string label,const std::vector& y, long bins=10, std::string color="b", double alpha=1.0) -{ - detail::_interpreter::get(); - - PyObject* yarray = detail::get_array(y); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str())); - PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); - PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); - PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); - - - PyObject* plot_args = PyTuple_New(1); - PyTuple_SetItem(plot_args, 0, yarray); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); - - Py_DECREF(plot_args); - Py_DECREF(kwargs); - if(res) Py_DECREF(res); - - return res; -} - -template -bool plot(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool quiver(const std::vector& x, const std::vector& y, const std::vector& u, const std::vector& w, const std::map& keywords = {}) -{ - assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - PyObject* uarray = detail::get_array(u); - PyObject* warray = detail::get_array(w); - - PyObject* plot_args = PyTuple_New(4); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, uarray); - PyTuple_SetItem(plot_args, 3, warray); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call( - detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) - Py_DECREF(res); - - return res; -} - -template -bool stem(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_stem, plot_args); - - Py_DECREF(plot_args); - if (res) - Py_DECREF(res); - - return res; -} - -template -bool semilogx(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool semilogy(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogy, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool loglog(const std::vector& x, const std::vector& y, const std::string& s = "") -{ - assert(x.size() == y.size()); - - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - - PyObject* pystring = PyString_FromString(s.c_str()); - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_loglog, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; -} - -template -bool errorbar(const std::vector &x, const std::vector &y, const std::vector &yerr, const std::map &keywords = {}) -{ - assert(x.size() == y.size()); +inline bool +subplots_adjust(const std::map &keywords = {}) { - detail::_interpreter::get(); - - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - PyObject* yerrarray = detail::get_array(yerr); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } - - PyDict_SetItemString(kwargs, "yerr", yerrarray); - - PyObject *plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - - PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_errorbar, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - - if (res) - Py_DECREF(res); - else - throw std::runtime_error("Call to errorbar() failed."); - - return res; -} - -template -bool named_plot(const std::string& name, const std::vector& y, const std::string& format = "") -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - - PyObject* yarray = detail::get_array(y); - - PyObject* pystring = PyString_FromString(format.c_str()); - - PyObject* plot_args = PyTuple_New(2); + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyFloat_FromDouble(it->second)); + } - PyTuple_SetItem(plot_args, 0, yarray); - PyTuple_SetItem(plot_args, 1, pystring); + PyObject *plot_args = PyTuple_New(0); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, + kwargs); - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); - return res; + return res; } -template -bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - detail::_interpreter::get(); +template +bool named_hist(std::string label, const std::vector &y, + long bins = 10, std::string color = "b", double alpha = 1.0) { + PyObject *yarray = get_array(y); - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str())); + PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins)); + PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str())); + PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha)); - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + PyObject *plot_args = PyTuple_New(1); + PyTuple_SetItem(plot_args, 0, yarray); - PyObject* pystring = PyString_FromString(format.c_str()); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_hist, plot_args, kwargs); - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); - - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if (res) + Py_DECREF(res); - return res; + return res; } -template -bool named_semilogx(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - detail::_interpreter::get(); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); +template +bool quiver(const std::vector &x, const std::vector &y, + const std::vector &u, const std::vector &w, + const std::map &keywords = {}) { + assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size()); - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); + PyObject *uarray = get_array(u); + PyObject *warray = get_array(w); - PyObject* pystring = PyString_FromString(format.c_str()); + PyObject *plot_args = PyTuple_New(4); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, uarray); + PyTuple_SetItem(plot_args, 3, warray); - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs); - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); + Py_DECREF(kwargs); + Py_DECREF(plot_args); + if (res) + Py_DECREF(res); - return res; + return res; } -template -bool named_semilogy(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - detail::_interpreter::get(); +template +void axhline(const NumericY y, + const std::map keywords = {}) { + detail::_interpreter::get(); - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + PyObject *kwargs = PyDict_New(); - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + // add location + PyDict_SetItemString(kwargs, "y", PyFloat_FromDouble(y)); - PyObject* pystring = PyString_FromString(format.c_str()); + // add other keywords + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); + PyObject *res = + PyObject_Call(detail::_interpreter::get().s_python_function_axhline, + detail::_interpreter::get().s_python_empty_tuple, kwargs); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs); + Py_DECREF(kwargs); - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); + if (!res) + throw std::runtime_error("Call to axhline() failed."); - return res; + Py_DECREF(res); } -template -bool named_loglog(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") -{ - detail::_interpreter::get(); +template +void axvline(const NumericX x, + const std::map keywords = {}) { + detail::_interpreter::get(); - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + PyObject *kwargs = PyDict_New(); - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + // add location + PyDict_SetItemString(kwargs, "x", PyFloat_FromDouble(x)); - PyObject* pystring = PyString_FromString(format.c_str()); + // add other keywords + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs); + PyObject *res = + PyObject_Call(detail::_interpreter::get().s_python_function_axvline, + detail::_interpreter::get().s_python_empty_tuple, kwargs); - Py_DECREF(kwargs); - Py_DECREF(plot_args); - if (res) Py_DECREF(res); + Py_DECREF(kwargs); - return res; -} + if (!res) + throw std::runtime_error("Call to axvline() failed."); -template -bool plot(const std::vector& y, const std::string& format = "") -{ - std::vector x(y.size()); - for(size_t i=0; i -bool plot(const std::vector& y, const std::map& keywords) -{ - std::vector x(y.size()); - for(size_t i=0; i +bool stem(const std::vector &x, const std::vector &y, + const std::string &s = "") { + assert(x.size() == y.size()); -template -bool stem(const std::vector& y, const std::string& format = "") -{ - std::vector x(y.size()); - for (size_t i = 0; i < x.size(); ++i) x.at(i) = i; - return stem(x, y, format); -} + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); -template -void text(Numeric x, Numeric y, const std::string& s = "") -{ - detail::_interpreter::get(); + PyObject *pystring = PyString_FromString(s.c_str()); - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(y)); - PyTuple_SetItem(args, 2, PyString_FromString(s.c_str())); + PyObject *plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_text, args); - if(!res) throw std::runtime_error("Call to text() failed."); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_stem, plot_args); - Py_DECREF(args); + Py_DECREF(plot_args); + if (res) Py_DECREF(res); -} - -inline void colorbar(PyObject* mappable = NULL, const std::map& keywords = {}) -{ - if (mappable == NULL) - throw std::runtime_error("Must call colorbar with PyObject* returned from an image, contour, surface, etc."); - detail::_interpreter::get(); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, mappable); - - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(it->second)); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_colorbar, args, kwargs); - if(!res) throw std::runtime_error("Call to colorbar() failed."); - - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); + return res; } +template +bool errorbar(const VectorX &x, const VectorY &y, const VectorY &yerr, + const std::map &keywords = {}) { + assert(x.size() == y.size()); -inline long figure(long number = -1) -{ - detail::_interpreter::get(); + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); + PyObject *yerrarray = get_array(yerr); - PyObject *res; - if (number == -1) - res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple); - else { - assert(number > 0); + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } - // Make sure interpreter is initialised - detail::_interpreter::get(); + PyDict_SetItemString(kwargs, "yerr", yerrarray); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyLong_FromLong(number)); - res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, args); - Py_DECREF(args); - } + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); - if(!res) throw std::runtime_error("Call to figure() failed."); + PyObject *res = + PyObject_Call(detail::_interpreter::get().s_python_function_errorbar, + plot_args, kwargs); - PyObject* num = PyObject_GetAttrString(res, "number"); - if (!num) throw std::runtime_error("Could not get number attribute of figure object"); - const long figureNumber = PyLong_AsLong(num); + Py_DECREF(kwargs); + Py_DECREF(plot_args); - Py_DECREF(num); + if (res) Py_DECREF(res); + else + throw std::runtime_error("Call to errorbar() failed."); - return figureNumber; + return res; } -inline bool fignum_exists(long number) -{ - detail::_interpreter::get(); - - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyLong_FromLong(number)); - PyObject *res = PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, args); - if(!res) throw std::runtime_error("Call to fignum_exists() failed."); - - bool ret = PyObject_IsTrue(res); - Py_DECREF(res); - Py_DECREF(args); - - return ret; +template +bool stem(const std::vector &y, const std::string &format = "") { + std::vector x(y.size()); + for (size_t i = 0; i < x.size(); ++i) + x.at(i) = i; + return stem(x, y, format); } -inline void figure_size(size_t w, size_t h) -{ - detail::_interpreter::get(); - - const size_t dpi = 100; - PyObject* size = PyTuple_New(2); - PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi)); - PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi)); - - PyObject* kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "figsize", size); - PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi)); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_figure, - detail::_interpreter::get().s_python_empty_tuple, kwargs); - - Py_DECREF(kwargs); - - if(!res) throw std::runtime_error("Call to figure_size() failed."); - Py_DECREF(res); -} +template +void text(Numeric x, Numeric y, const std::string &s = "") { + detail::_interpreter::get(); -inline void legend() -{ - detail::_interpreter::get(); + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(y)); + PyTuple_SetItem(args, 2, PyString_FromString(s.c_str())); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple); - if(!res) throw std::runtime_error("Call to legend() failed."); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_text, args); + if (!res) + throw std::runtime_error("Call to text() failed."); - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(res); } -template -void ylim(Numeric left, Numeric right) -{ - detail::_interpreter::get(); - - PyObject* list = PyList_New(2); - PyList_SetItem(list, 0, PyFloat_FromDouble(left)); - PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, list); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); - if(!res) throw std::runtime_error("Call to ylim() failed."); - - Py_DECREF(args); - Py_DECREF(res); -} +inline long figure(long number = -1) { + PyObject *res; + if (number == -1) + res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple); + else { + assert(number > 0); -template -void xlim(Numeric left, Numeric right) -{ + // Make sure interpreter is initialised detail::_interpreter::get(); - PyObject* list = PyList_New(2); - PyList_SetItem(list, 0, PyFloat_FromDouble(left)); - PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, list); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); - if(!res) throw std::runtime_error("Call to xlim() failed."); - + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(number)); + res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_figure, args); Py_DECREF(args); - Py_DECREF(res); -} - - -inline double* xlim() -{ - detail::_interpreter::get(); - - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); - - double* arr = new double[2]; - arr[0] = PyFloat_AsDouble(left); - arr[1] = PyFloat_AsDouble(right); - - if(!res) throw std::runtime_error("Call to xlim() failed."); - - Py_DECREF(res); - return arr; -} - - -inline double* ylim() -{ - detail::_interpreter::get(); - - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); - - double* arr = new double[2]; - arr[0] = PyFloat_AsDouble(left); - arr[1] = PyFloat_AsDouble(right); - - if(!res) throw std::runtime_error("Call to ylim() failed."); - - Py_DECREF(res); - return arr; -} - -template -inline void xticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) -{ - assert(labels.size() == 0 || ticks.size() == labels.size()); - - detail::_interpreter::get(); - - // using numpy array - PyObject* ticksarray = detail::get_array(ticks); - - PyObject* args; - if(labels.size() == 0) { - // construct positional args - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, ticksarray); - } else { - // make tuple of tick labels - PyObject* labelstuple = PyTuple_New(labels.size()); - for (size_t i = 0; i < labels.size(); i++) - PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); - - // construct positional args - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, ticksarray); - PyTuple_SetItem(args, 1, labelstuple); - } + } - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } + if (!res) + throw std::runtime_error("Call to figure() failed."); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xticks, args, kwargs); + PyObject *num = PyObject_GetAttrString(res, "number"); + if (!num) + throw std::runtime_error("Could not get number attribute of figure object"); + const long figureNumber = PyLong_AsLong(num); - Py_DECREF(args); - Py_DECREF(kwargs); - if(!res) throw std::runtime_error("Call to xticks() failed"); - - Py_DECREF(res); -} + Py_DECREF(num); + Py_DECREF(res); -template -inline void xticks(const std::vector &ticks, const std::map& keywords) -{ - xticks(ticks, {}, keywords); + return figureNumber; } -template -inline void yticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) -{ - assert(labels.size() == 0 || ticks.size() == labels.size()); - - detail::_interpreter::get(); - - // using numpy array - PyObject* ticksarray = detail::get_array(ticks); - - PyObject* args; - if(labels.size() == 0) { - // construct positional args - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, ticksarray); - } else { - // make tuple of tick labels - PyObject* labelstuple = PyTuple_New(labels.size()); - for (size_t i = 0; i < labels.size(); i++) - PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); - - // construct positional args - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, ticksarray); - PyTuple_SetItem(args, 1, labelstuple); - } - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_yticks, args, kwargs); +inline bool fignum_exists(long number) { + // Make sure interpreter is initialised + detail::_interpreter::get(); - Py_DECREF(args); - Py_DECREF(kwargs); - if(!res) throw std::runtime_error("Call to yticks() failed"); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(number)); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_fignum_exists, args); + if (!res) + throw std::runtime_error("Call to fignum_exists() failed."); - Py_DECREF(res); -} + bool ret = PyObject_IsTrue(res); + Py_DECREF(res); + Py_DECREF(args); -template -inline void yticks(const std::vector &ticks, const std::map& keywords) -{ - yticks(ticks, {}, keywords); + return ret; } -inline void tick_params(const std::map& keywords, const std::string axis = "both") -{ +inline void figure_size(size_t w, size_t h) { + // Make sure interpreter is initialised detail::_interpreter::get(); - // construct positional args - PyObject* args; - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(axis.c_str())); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for (std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } + const size_t dpi = 100; + PyObject *size = PyTuple_New(2); + PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi)); + PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi)); + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "figsize", size); + PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi)); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_tick_params, args, kwargs); + PyObject *res = + PyObject_Call(detail::_interpreter::get().s_python_function_figure, + detail::_interpreter::get().s_python_empty_tuple, kwargs); - Py_DECREF(args); Py_DECREF(kwargs); - if (!res) throw std::runtime_error("Call to tick_params() failed"); + if (!res) + throw std::runtime_error("Call to figure_size() failed."); Py_DECREF(res); } -inline void subplot(long nrows, long ncols, long plot_number) -{ - detail::_interpreter::get(); - - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); +template > +inline void legend(const std::string &loc = "best", + const Vector &bbox_to_anchor = Vector(), + const std::map &keywords = {}) { + detail::_interpreter::get(); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args); - if(!res) throw std::runtime_error("Call to subplot() failed."); + PyObject *kwargs = PyDict_New(); - Py_DECREF(args); - Py_DECREF(res); -} + // add location + if (loc != "") + PyDict_SetItemString(kwargs, "loc", PyString_FromString(loc.c_str())); -inline void subplot2grid(long nrows, long ncols, long rowid=0, long colid=0, long rowspan=1, long colspan=1) -{ - detail::_interpreter::get(); + // add bbox to anchor + if (bbox_to_anchor.size() == 2 || bbox_to_anchor.size() == 4) { + PyObject *bbox = get_array(bbox_to_anchor); + PyDict_SetItemString(kwargs, "bbox_to_anchor", bbox); + } - PyObject* shape = PyTuple_New(2); - PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows)); - PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols)); + // add other keywords + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } - PyObject* loc = PyTuple_New(2); - PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid)); - PyTuple_SetItem(loc, 1, PyLong_FromLong(colid)); + PyObject *res = + PyObject_Call(detail::_interpreter::get().s_python_function_legend, + detail::_interpreter::get().s_python_empty_tuple, kwargs); - PyObject* args = PyTuple_New(4); - PyTuple_SetItem(args, 0, shape); - PyTuple_SetItem(args, 1, loc); - PyTuple_SetItem(args, 2, PyLong_FromLong(rowspan)); - PyTuple_SetItem(args, 3, PyLong_FromLong(colspan)); + Py_DECREF(kwargs); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot2grid, args); - if(!res) throw std::runtime_error("Call to subplot2grid() failed."); + if (!res) + throw std::runtime_error("Call to legend() failed."); - Py_DECREF(shape); - Py_DECREF(loc); - Py_DECREF(args); - Py_DECREF(res); + Py_DECREF(res); } -inline void title(const std::string &titlestr, const std::map &keywords = {}) -{ - detail::_interpreter::get(); +template +inline void legend(const Vector &bbox_to_anchor, + const std::map &keywords = {}) { + legend("", bbox_to_anchor, keywords); +} - PyObject* pytitlestr = PyString_FromString(titlestr.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pytitlestr); +inline void legend(const std::string &loc, + const std::map &keywords = {}) { + legend(loc, std::vector(), keywords); +} - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } +// to support C-style strings we also need const char[], std::string only +// does not capture calls of style legend("lower left") +inline void legend(const char loc[], + const std::map &keywords = {}) { + legend(loc, std::vector(), keywords); +} - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_title, args, kwargs); - if(!res) throw std::runtime_error("Call to title() failed."); +/* +inline void legend(const std::string& loc, + const std::map& keywords = {}) { + legend(loc, std::vector(), keywords); +} - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); +inline void legend(const std::map& keywords) { + legend("", std::vector(), keywords); } +*/ -inline void suptitle(const std::string &suptitlestr, const std::map &keywords = {}) -{ - detail::_interpreter::get(); - - PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pysuptitlestr); - - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } +template void ylim(const Numeric bottom, const Numeric top) { + detail::_interpreter::get(); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_suptitle, args, kwargs); - if(!res) throw std::runtime_error("Call to suptitle() failed."); + PyObject *list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(bottom)); + PyList_SetItem(list, 1, PyFloat_FromDouble(top)); - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); + + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_ylim, args); + if (!res) + throw std::runtime_error("Call to ylim() failed."); + + Py_DECREF(args); + Py_DECREF(res); } -inline void axis(const std::string &axisstr) -{ - detail::_interpreter::get(); +template void xlim(const Numeric left, const Numeric right) { + detail::_interpreter::get(); - PyObject* str = PyString_FromString(axisstr.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, str); + PyObject *list = PyList_New(2); + PyList_SetItem(list, 0, PyFloat_FromDouble(left)); + PyList_SetItem(list, 1, PyFloat_FromDouble(right)); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args); - if(!res) throw std::runtime_error("Call to title() failed."); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, list); - Py_DECREF(args); - Py_DECREF(res); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_xlim, args); + if (!res) + throw std::runtime_error("Call to xlim() failed."); + + Py_DECREF(args); + Py_DECREF(res); } -inline void axvline(double x, double ymin = 0., double ymax = 1., const std::map& keywords = std::map()) -{ - detail::_interpreter::get(); +inline double *xlim() { + detail::_interpreter::get(); - // construct positional args - PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(x)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(ymin)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymax)); - - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str())); - } + PyObject *args = PyTuple_New(0); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_xlim, args); + PyObject *left = PyTuple_GetItem(res, 0); + PyObject *right = PyTuple_GetItem(res, 1); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvline, args, kwargs); + double *arr = new double[2]; + arr[0] = PyFloat_AsDouble(left); + arr[1] = PyFloat_AsDouble(right); - Py_DECREF(args); - Py_DECREF(kwargs); + if (!res) + throw std::runtime_error("Call to xlim() failed."); - if(res) Py_DECREF(res); + Py_DECREF(res); + return arr; } -inline void xlabel(const std::string &str, const std::map &keywords = {}) -{ - detail::_interpreter::get(); +inline double *ylim() { + detail::_interpreter::get(); - PyObject* pystr = PyString_FromString(str.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pystr); + PyObject *args = PyTuple_New(0); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_ylim, args); + PyObject *bottom = PyTuple_GetItem(res, 0); + PyObject *top = PyTuple_GetItem(res, 1); - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } + double *arr = new double[2]; + arr[0] = PyFloat_AsDouble(bottom); + arr[1] = PyFloat_AsDouble(top); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xlabel, args, kwargs); - if(!res) throw std::runtime_error("Call to xlabel() failed."); + if (!res) + throw std::runtime_error("Call to ylim() failed."); - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); + Py_DECREF(res); + return arr; } -inline void ylabel(const std::string &str, const std::map& keywords = {}) -{ - detail::_interpreter::get(); +template +inline void xticks(const std::vector &ticks, + const std::vector &labels = {}, + const std::map &keywords = {}) { + assert(labels.size() == 0 || ticks.size() == labels.size()); - PyObject* pystr = PyString_FromString(str.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pystr); + // using numpy array + PyObject *ticksarray = get_array(ticks); - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } + PyObject *args; + if (labels.size() == 0) { + // construct positional args + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, ticksarray); + } else { + // make tuple of tick labels + PyObject *labelstuple = PyTuple_New(labels.size()); + for (size_t i = 0; i < labels.size(); i++) + PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_ylabel, args, kwargs); - if(!res) throw std::runtime_error("Call to ylabel() failed."); + // construct positional args + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, ticksarray); + PyTuple_SetItem(args, 1, labelstuple); + } - Py_DECREF(args); - Py_DECREF(kwargs); - Py_DECREF(res); -} + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } -inline void set_zlabel(const std::string &str, const std::map& keywords = {}) -{ - detail::_interpreter::get(); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_xticks, args, kwargs); - // Same as with plot_surface: We lazily load the modules here the first time - // this function is called because I'm not sure that we can assume "matplotlib - // installed" implies "mpl_toolkits installed" on all platforms, and we don't - // want to require it for people who don't need 3d plots. - static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr; - if (!mpl_toolkitsmod) { - PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits"); - PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d"); - if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); } + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) + throw std::runtime_error("Call to xticks() failed"); + + Py_DECREF(res); +} - mpl_toolkitsmod = PyImport_Import(mpl_toolkits); - Py_DECREF(mpl_toolkits); - if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); } +template +inline void xticks(const std::vector &ticks, + const std::map &keywords) { + xticks(ticks, {}, keywords); +} - axis3dmod = PyImport_Import(axis3d); - Py_DECREF(axis3d); - if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); } - } +template +inline void yticks(const std::vector &ticks, + const std::vector &labels = {}, + const std::map &keywords = {}) { + assert(labels.size() == 0 || ticks.size() == labels.size()); - PyObject* pystr = PyString_FromString(str.c_str()); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pystr); + // using numpy array + PyObject *ticksarray = get_array(ticks); - PyObject* kwargs = PyDict_New(); - for (auto it = keywords.begin(); it != keywords.end(); ++it) { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } + PyObject *args; + if (labels.size() == 0) { + // construct positional args + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, ticksarray); + } else { + // make tuple of tick labels + PyObject *labelstuple = PyTuple_New(labels.size()); + for (size_t i = 0; i < labels.size(); i++) + PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str())); - PyObject *ax = - PyObject_CallObject(detail::_interpreter::get().s_python_function_gca, - detail::_interpreter::get().s_python_empty_tuple); - if (!ax) throw std::runtime_error("Call to gca() failed."); - Py_INCREF(ax); + // construct positional args + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, ticksarray); + PyTuple_SetItem(args, 1, labelstuple); + } + + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyString_FromString(it->second.c_str())); + } - PyObject *zlabel = PyObject_GetAttrString(ax, "set_zlabel"); - if (!zlabel) throw std::runtime_error("Attribute set_zlabel not found."); - Py_INCREF(zlabel); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_yticks, args, kwargs); - PyObject *res = PyObject_Call(zlabel, args, kwargs); - if (!res) throw std::runtime_error("Call to set_zlabel() failed."); - Py_DECREF(zlabel); + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) + throw std::runtime_error("Call to yticks() failed"); - Py_DECREF(ax); - Py_DECREF(args); - Py_DECREF(kwargs); - if (res) Py_DECREF(res); + Py_DECREF(res); } -inline void grid(bool flag) -{ - detail::_interpreter::get(); +template +inline void yticks(const std::vector &ticks, + const std::map &keywords) { + yticks(ticks, {}, keywords); +} - PyObject* pyflag = flag ? Py_True : Py_False; - Py_INCREF(pyflag); +inline void subplot(long nrows, long ncols, long plot_number) { + detail::_interpreter::get(); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pyflag); + // construct positional args + PyObject *args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args); - if(!res) throw std::runtime_error("Call to grid() failed."); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_subplot, args); + if (!res) + throw std::runtime_error("Call to subplot() failed."); - Py_DECREF(args); - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(res); } -inline void show(const bool block = true) -{ - detail::_interpreter::get(); +inline void title(const std::string &titlestr, + const std::map &keywords = {}) { + detail::_interpreter::get(); - PyObject* res; - if(block) - { - res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_show, - detail::_interpreter::get().s_python_empty_tuple); - } - else - { - PyObject *kwargs = PyDict_New(); - PyDict_SetItemString(kwargs, "block", Py_False); - res = PyObject_Call( detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple, kwargs); - Py_DECREF(kwargs); - } + PyObject *pytitlestr = PyString_FromString(titlestr.c_str()); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pytitlestr); + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - if (!res) throw std::runtime_error("Call to show() failed."); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_title, args, kwargs); + if (!res) + throw std::runtime_error("Call to title() failed."); - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); } -inline void close() -{ - detail::_interpreter::get(); +inline void suptitle(const std::string &suptitlestr, + const std::map &keywords = {}) { + detail::_interpreter::get(); - PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_close, - detail::_interpreter::get().s_python_empty_tuple); + PyObject *pysuptitlestr = PyString_FromString(suptitlestr.c_str()); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pysuptitlestr); + + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - if (!res) throw std::runtime_error("Call to close() failed."); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_suptitle, args, kwargs); + if (!res) + throw std::runtime_error("Call to suptitle() failed."); - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); } -inline void xkcd() { - detail::_interpreter::get(); +inline void axis(const std::string &option) { + PyObject *str = PyString_FromString(option.c_str()); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, str); - PyObject* res; - PyObject *kwargs = PyDict_New(); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_axis, args); + if (!res) + throw std::runtime_error("Call to axis() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} - res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd, - detail::_interpreter::get().s_python_empty_tuple, kwargs); +inline void xlabel(const std::string &str, + const std::map &keywords = {}) { + PyObject *pystr = PyString_FromString(str.c_str()); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); - Py_DECREF(kwargs); + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - if (!res) - throw std::runtime_error("Call to show() failed."); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_xlabel, args, kwargs); + if (!res) + throw std::runtime_error("Call to xlabel() failed."); - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); } -inline void draw() -{ - detail::_interpreter::get(); +inline void ylabel(const std::string &str, + const std::map &keywords = {}) { + PyObject *pystr = PyString_FromString(str.c_str()); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); - PyObject* res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_draw, - detail::_interpreter::get().s_python_empty_tuple); + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - if (!res) throw std::runtime_error("Call to draw() failed."); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_ylabel, args, kwargs); + if (!res) + throw std::runtime_error("Call to ylabel() failed."); - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); } -template -inline void pause(Numeric interval) -{ - detail::_interpreter::get(); +inline void grid(bool flag = true) { + PyObject *pyflag = flag ? Py_True : Py_False; + Py_INCREF(pyflag); - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval)); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyflag); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args); - if(!res) throw std::runtime_error("Call to pause() failed."); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_grid, args); + if (!res) + throw std::runtime_error("Call to grid() failed."); - Py_DECREF(args); - Py_DECREF(res); + Py_DECREF(args); + Py_DECREF(res); } -inline void save(const std::string& filename) -{ - detail::_interpreter::get(); - - PyObject* pyfilename = PyString_FromString(filename.c_str()); - - PyObject* args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pyfilename); +inline void show(const bool block = true) { + PyObject *res; + if (block) { + res = + PyObject_CallObject(detail::_interpreter::get().s_python_function_show, + detail::_interpreter::get().s_python_empty_tuple); + } else { + PyObject *kwargs = PyDict_New(); + PyDict_SetItemString(kwargs, "block", Py_False); + res = + PyObject_Call(detail::_interpreter::get().s_python_function_show, + detail::_interpreter::get().s_python_empty_tuple, kwargs); + Py_DECREF(kwargs); + } - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args); - if (!res) throw std::runtime_error("Call to save() failed."); + if (!res) + throw std::runtime_error("Call to show() failed."); - Py_DECREF(args); - Py_DECREF(res); + Py_DECREF(res); } -inline void clf() { - detail::_interpreter::get(); - - PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_clf, - detail::_interpreter::get().s_python_empty_tuple); +inline void close() { + PyObject *res = + PyObject_CallObject(detail::_interpreter::get().s_python_function_close, + detail::_interpreter::get().s_python_empty_tuple); - if (!res) throw std::runtime_error("Call to clf() failed."); + if (!res) + throw std::runtime_error("Call to close() failed."); - Py_DECREF(res); + Py_DECREF(res); } -inline void ion() { - detail::_interpreter::get(); +inline void xkcd() { + PyObject *res; + PyObject *kwargs = PyDict_New(); - PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_ion, - detail::_interpreter::get().s_python_empty_tuple); + res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd, + detail::_interpreter::get().s_python_empty_tuple, kwargs); - if (!res) throw std::runtime_error("Call to ion() failed."); + Py_DECREF(kwargs); - Py_DECREF(res); + if (!res) + throw std::runtime_error("Call to xkcd() failed."); + + Py_DECREF(res); } -inline std::vector> ginput(const int numClicks = 1, const std::map& keywords = {}) -{ - detail::_interpreter::get(); +inline void draw() { + PyObject *res = + PyObject_CallObject(detail::_interpreter::get().s_python_function_draw, + detail::_interpreter::get().s_python_empty_tuple); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks)); + if (!res) + throw std::runtime_error("Call to draw() failed."); - // construct keyword args - PyObject* kwargs = PyDict_New(); - for(std::map::const_iterator it = keywords.begin(); it != keywords.end(); ++it) - { - PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str())); - } + Py_DECREF(res); +} - PyObject* res = PyObject_Call( - detail::_interpreter::get().s_python_function_ginput, args, kwargs); +template inline void pause(Numeric interval) { + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval)); - Py_DECREF(kwargs); - Py_DECREF(args); - if (!res) throw std::runtime_error("Call to ginput() failed."); - - const size_t len = PyList_Size(res); - std::vector> out; - out.reserve(len); - for (size_t i = 0; i < len; i++) { - PyObject *current = PyList_GetItem(res, i); - std::array position; - position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0)); - position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1)); - out.push_back(position); - } - Py_DECREF(res); + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_pause, args); + if (!res) + throw std::runtime_error("Call to pause() failed."); - return out; + Py_DECREF(args); + Py_DECREF(res); } -// Actually, is there any reason not to call this automatically for every plot? -inline void tight_layout() { - detail::_interpreter::get(); +inline void savefig(const std::string &filename, + const std::map &keywords = {}) { + PyObject *pyfilename = PyString_FromString(filename.c_str()); - PyObject *res = PyObject_CallObject( - detail::_interpreter::get().s_python_function_tight_layout, - detail::_interpreter::get().s_python_empty_tuple); + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pyfilename); + + PyObject *kwargs = PyDict_New(); + for (auto it = keywords.begin(); it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } - if (!res) throw std::runtime_error("Call to tight_layout() failed."); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_save, args, kwargs); + if (!res) + throw std::runtime_error("Call to save() failed."); - Py_DECREF(res); + Py_DECREF(kwargs); + Py_DECREF(args); + Py_DECREF(res); } -// Support for variadic plot() and initializer lists: - -namespace detail { +inline void save(const std::string &filename) { + std::cerr << "matplotlibcpp::save is deprecated, use savefig instead\n"; + matplotlibcpp::savefig(filename); +} -template -using is_function = typename std::is_function>>::type; +inline void clf() { + PyObject *res = + PyObject_CallObject(detail::_interpreter::get().s_python_function_clf, + detail::_interpreter::get().s_python_empty_tuple); -template -struct is_callable_impl; + if (!res) + throw std::runtime_error("Call to clf() failed."); -template -struct is_callable_impl -{ - typedef is_function type; -}; // a non-object is callable iff it is a function + Py_DECREF(res); +} -template -struct is_callable_impl -{ - struct Fallback { void operator()(); }; - struct Derived : T, Fallback { }; +inline void ion() { + PyObject *res = + PyObject_CallObject(detail::_interpreter::get().s_python_function_ion, + detail::_interpreter::get().s_python_empty_tuple); - template struct Check; + if (!res) + throw std::runtime_error("Call to ion() failed."); - template - static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match + Py_DECREF(res); +} - template - static std::false_type test( Check* ); +inline std::vector> +ginput(const int numClicks = 1, + const std::map &keywords = {}) { + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks)); -public: - typedef decltype(test(nullptr)) type; - typedef decltype(&Fallback::operator()) dtype; - static constexpr bool value = type::value; -}; // an object is callable iff it defines operator() - -template -struct is_callable -{ - // dispatch to is_callable_impl or is_callable_impl depending on whether T is of class type or not - typedef typename is_callable_impl::value, T>::type type; -}; + // construct keyword args + PyObject *kwargs = PyDict_New(); + for (std::map::const_iterator it = keywords.begin(); + it != keywords.end(); ++it) { + PyDict_SetItemString(kwargs, it->first.c_str(), + PyUnicode_FromString(it->second.c_str())); + } -template -struct plot_impl { }; - -template<> -struct plot_impl -{ - template - bool operator()(const IterableX& x, const IterableY& y, const std::string& format) - { - // 2-phase lookup for distance, begin, end - using std::distance; - using std::begin; - using std::end; - - auto xs = distance(begin(x), end(x)); - auto ys = distance(begin(y), end(y)); - assert(xs == ys && "x and y data must have the same number of elements!"); - - PyObject* xlist = PyList_New(xs); - PyObject* ylist = PyList_New(ys); - PyObject* pystring = PyString_FromString(format.c_str()); - - auto itx = begin(x), ity = begin(y); - for(size_t i = 0; i < xs; ++i) { - PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++)); - PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++)); - } - - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xlist); - PyTuple_SetItem(plot_args, 1, ylist); - PyTuple_SetItem(plot_args, 2, pystring); - - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args); - - Py_DECREF(plot_args); - if(res) Py_DECREF(res); - - return res; - } -}; + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_ginput, args, kwargs); -template<> -struct plot_impl -{ - template - bool operator()(const Iterable& ticks, const Callable& f, const std::string& format) - { - if(begin(ticks) == end(ticks)) return true; - - // We could use additional meta-programming to deduce the correct element type of y, - // but all values have to be convertible to double anyways - std::vector y; - for(auto x : ticks) y.push_back(f(x)); - return plot_impl()(ticks,y,format); - } -}; + Py_DECREF(kwargs); + Py_DECREF(args); + if (!res) + throw std::runtime_error("Call to ginput() failed."); + + const size_t len = PyList_Size(res); + std::vector> out; + out.reserve(len); + for (size_t i = 0; i < len; i++) { + PyObject *current = PyList_GetItem(res, i); + std::array position; + position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0)); + position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1)); + out.push_back(position); + } + Py_DECREF(res); -} // end namespace detail + return out; +} -// recursion stop for the above -template -bool plot() { return true; } +inline void tight_layout() { + PyObject *res = PyObject_CallObject( + detail::_interpreter::get().s_python_function_tight_layout, + detail::_interpreter::get().s_python_empty_tuple); -template -bool plot(const A& a, const B& b, const std::string& format, Args... args) -{ - return detail::plot_impl::type>()(a,b,format) && plot(args...); -} + if (!res) + throw std::runtime_error("Call to tight_layout() failed."); -/* - * This group of plot() functions is needed to support initializer lists, i.e. calling - * plot( {1,2,3,4} ) - */ -inline bool plot(const std::vector& x, const std::vector& y, const std::string& format = "") { - return plot(x,y,format); + Py_DECREF(res); } -inline bool plot(const std::vector& y, const std::string& format = "") { - return plot(y,format); -} +// recursion stop for the below +template bool plot() { return true; } -inline bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { - return plot(x,y,keywords); +// enable plotting of multiple triples (x, y, format) +template +bool plot(const A &a, const B &b, const std::string &format, Args... args) { + return plot(a, b, format) && plot(args...); } -/* - * This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting - */ -class Plot -{ +// This class allows dynamic plots, ie changing the plotted data without +// clearing and re-plotting +class Plot { public: - // default initialization with plot label, some data and format - template - Plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { - detail::_interpreter::get(); + // default initialization with plot label, some data and format + template + Plot(const std::string &name, const std::vector &x, + const std::vector &y, const std::string &format = "") { - assert(x.size() == y.size()); + assert(x.size() == y.size()); - PyObject* kwargs = PyDict_New(); - if(name != "") - PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); + PyObject *kwargs = PyDict_New(); + if (name != "") + PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str())); - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); - PyObject* pystring = PyString_FromString(format.c_str()); + PyObject *pystring = PyString_FromString(format.c_str()); - PyObject* plot_args = PyTuple_New(3); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - PyTuple_SetItem(plot_args, 2, pystring); + PyObject *plot_args = PyTuple_New(3); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); + PyTuple_SetItem(plot_args, 2, pystring); - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); + PyObject *res = PyObject_Call( + detail::_interpreter::get().s_python_function_plot, plot_args, kwargs); - Py_DECREF(kwargs); - Py_DECREF(plot_args); + Py_DECREF(kwargs); + Py_DECREF(plot_args); - if(res) - { - line= PyList_GetItem(res, 0); + if (res) { + line = PyList_GetItem(res, 0); - if(line) - set_data_fct = PyObject_GetAttrString(line,"set_data"); - else - Py_DECREF(line); - Py_DECREF(res); - } + if (line) + set_data_fct = PyObject_GetAttrString(line, "set_data"); + else + Py_DECREF(line); + Py_DECREF(res); } + } - // shorter initialization with name or format only - // basically calls line, = plot([], []) - Plot(const std::string& name = "", const std::string& format = "") - : Plot(name, std::vector(), std::vector(), format) {} - - template - bool update(const std::vector& x, const std::vector& y) { - assert(x.size() == y.size()); - if(set_data_fct) - { - PyObject* xarray = detail::get_array(x); - PyObject* yarray = detail::get_array(y); - - PyObject* plot_args = PyTuple_New(2); - PyTuple_SetItem(plot_args, 0, xarray); - PyTuple_SetItem(plot_args, 1, yarray); - - PyObject* res = PyObject_CallObject(set_data_fct, plot_args); - if (res) Py_DECREF(res); - return res; - } - return false; - } + // shorter initialization with name or format only + // basically calls line, = plot([], []) + Plot(const std::string &name = "", const std::string &format = "") + : Plot(name, std::vector(), std::vector(), format) {} - // clears the plot but keep it available - bool clear() { - return update(std::vector(), std::vector()); - } + template + bool update(const std::vector &x, const std::vector &y) { + assert(x.size() == y.size()); + if (set_data_fct) { + PyObject *xarray = get_array(x); + PyObject *yarray = get_array(y); - // definitely remove this line - void remove() { - if(line) - { - auto remove_fct = PyObject_GetAttrString(line,"remove"); - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(remove_fct, args); - if (res) Py_DECREF(res); - } - decref(); - } + PyObject *plot_args = PyTuple_New(2); + PyTuple_SetItem(plot_args, 0, xarray); + PyTuple_SetItem(plot_args, 1, yarray); - ~Plot() { - decref(); + PyObject *res = PyObject_CallObject(set_data_fct, plot_args); + if (res) + Py_DECREF(res); + return res; } -private: + return false; + } - void decref() { - if(line) - Py_DECREF(line); - if(set_data_fct) - Py_DECREF(set_data_fct); + // clears the plot but keep it available + bool clear() { return update(std::vector(), std::vector()); } + + // definitely remove this line + void remove() { + if (line) { + auto remove_fct = PyObject_GetAttrString(line, "remove"); + PyObject *args = PyTuple_New(0); + PyObject *res = PyObject_CallObject(remove_fct, args); + if (res) + Py_DECREF(res); } + decref(); + } + + ~Plot() { decref(); } +private: + void decref() { + if (line) + Py_DECREF(line); + if (set_data_fct) + Py_DECREF(set_data_fct); + } - PyObject* line = nullptr; - PyObject* set_data_fct = nullptr; + PyObject *line = nullptr; + PyObject *set_data_fct = nullptr; }; } // end namespace matplotlibcpp