blob: eb7b7ccb844123b7c57bab8c3ea078f4608f915a [file] [log] [blame]
#pragma once
#include <vector>
#include <map>
#include <numeric>
#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <stdint.h> // <cstdint> requires c++11 support
#if __cplusplus > 199711L || _MSC_VER > 1800
# include <functional>
#endif
#include <Python.h>
#ifndef WITHOUT_NUMPY
# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
# include <numpy/arrayobject.h>
#endif // WITHOUT_NUMPY
#if PY_MAJOR_VERSION >= 3
# 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_plot;
PyObject *s_python_function_semilogx;
PyObject *s_python_function_semilogy;
PyObject *s_python_function_loglog;
PyObject *s_python_function_fill_between;
PyObject *s_python_function_hist;
PyObject *s_python_function_subplot;
PyObject *s_python_function_legend;
PyObject *s_python_function_xlim;
PyObject *s_python_function_ion;
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_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;
/* 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
void *import_numpy() {
import_array(); // initialize C-API
return NULL;
}
# else
void import_numpy() {
import_array(); // initialize C-API
}
# endif
#endif
_interpreter() {
// optional but recommended
#if PY_MAJOR_VERSION >= 3
wchar_t name[] = L"plotting";
#else
char name[] = "plotting";
#endif
Py_SetProgramName(name);
Py_Initialize();
#ifndef WITHOUT_NUMPY
import_numpy(); // initialize numpy C-API
#endif
PyObject* matplotlibname = PyString_FromString("matplotlib");
PyObject* pyplotname = PyString_FromString("matplotlib.pyplot");
PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
PyObject* pylabname = PyString_FromString("pylab");
PyObject* cmname = PyString_FromString("matplotlib.cm");
if (!pyplotname || !pylabname || !matplotlibname || !mpl_toolkits ||
!axis3d || !cmname) {
throw std::runtime_error("couldnt create string");
}
PyObject* matplotlib = PyImport_Import(matplotlibname);
Py_DECREF(matplotlibname);
if (!matplotlib) { 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<char*>("use"), const_cast<char*>("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!"); }
PyObject* mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
Py_DECREF(mpl_toolkitsmod);
if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
PyObject* axis3dmod = PyImport_Import(axis3d);
Py_DECREF(axis3dmod);
if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
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_plot = PyObject_GetAttrString(pymod, "plot");
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_between = PyObject_GetAttrString(pymod, "fill_between");
s_python_function_hist = PyObject_GetAttrString(pymod,"hist");
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_grid = PyObject_GetAttrString(pymod, "grid");
s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim");
s_python_function_ion = PyObject_GetAttrString(pymod, "ion");
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");
if( !s_python_function_show
|| !s_python_function_close
|| !s_python_function_draw
|| !s_python_function_pause
|| !s_python_function_figure
|| !s_python_function_plot
|| !s_python_function_semilogx
|| !s_python_function_semilogy
|| !s_python_function_loglog
|| !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_grid
|| !s_python_function_xlim
|| !s_python_function_ion
|| !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
) { throw std::runtime_error("Couldn't find required function!"); }
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_plot)
|| !PyFunction_Check(s_python_function_semilogx)
|| !PyFunction_Check(s_python_function_semilogy)
|| !PyFunction_Check(s_python_function_loglog)
|| !PyFunction_Check(s_python_function_fill_between)
|| !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_grid)
|| !PyFunction_Check(s_python_function_xlim)
|| !PyFunction_Check(s_python_function_ion)
|| !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)
) { throw std::runtime_error("Python object is unexpectedly not a PyFunction."); }
s_python_empty_tuple = PyTuple_New(0);
}
~_interpreter() {
Py_Finalize();
}
};
} // end namespace detail
// 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;
}
inline bool annotate(std::string annotation, double x, double y)
{
PyObject * xy = PyTuple_New(2);
PyObject * str = PyString_FromString(annotation.c_str());
PyTuple_SetItem(xy,0,PyFloat_FromDouble(x));
PyTuple_SetItem(xy,1,PyFloat_FromDouble(y));
PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "xy", xy);
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, str);
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs);
Py_DECREF(args);
Py_DECREF(kwargs);
if(res) Py_DECREF(res);
return res;
}
#ifndef WITHOUT_NUMPY
// Type selector for numpy array conversion
template <typename T> struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default
template <> struct select_npy_type<double> { const static NPY_TYPES type = NPY_DOUBLE; };
template <> struct select_npy_type<float> { const static NPY_TYPES type = NPY_FLOAT; };
template <> struct select_npy_type<bool> { const static NPY_TYPES type = NPY_BOOL; };
template <> struct select_npy_type<int8_t> { const static NPY_TYPES type = NPY_INT8; };
template <> struct select_npy_type<int16_t> { const static NPY_TYPES type = NPY_SHORT; };
template <> struct select_npy_type<int32_t> { const static NPY_TYPES type = NPY_INT; };
template <> struct select_npy_type<int64_t> { const static NPY_TYPES type = NPY_INT64; };
template <> struct select_npy_type<uint8_t> { const static NPY_TYPES type = NPY_UINT8; };
template <> struct select_npy_type<uint16_t> { const static NPY_TYPES type = NPY_USHORT; };
template <> struct select_npy_type<uint32_t> { const static NPY_TYPES type = NPY_ULONG; };
template <> struct select_npy_type<uint64_t> { const static NPY_TYPES type = NPY_UINT64; };
template<typename Numeric>
PyObject* get_array(const std::vector<Numeric>& v)
{
detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work
NPY_TYPES type = select_npy_type<Numeric>::type;
if (type == NPY_NOTYPE)
{
std::vector<double> vd(v.size());
npy_intp vsize = v.size();
std::copy(v.begin(),v.end(),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<typename Numeric>
PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
{
detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work
if (v.size() < 1) throw std::runtime_error("get_2d_array v too small");
npy_intp vsize[2] = {static_cast<npy_intp>(v.size()),
static_cast<npy_intp>(v[0].size())};
PyArrayObject *varray =
(PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE);
double *vd_begin = static_cast<double *>(PyArray_DATA(varray));
for (const ::std::vector<Numeric> &v_row : v) {
if (v_row.size() != static_cast<size_t>(vsize[1]))
throw std::runtime_error("Missmatched array size");
std::copy(v_row.begin(), v_row.end(), vd_begin);
vd_begin += vsize[1];
}
return reinterpret_cast<PyObject *>(varray);
}
#else // fallback if we don't have numpy: copy every element of the given vector
template<typename Numeric>
PyObject* get_array(const std::vector<Numeric>& 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)));
}
return list;
}
#endif // WITHOUT_NUMPY
template<typename Numeric>
bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
{
assert(x.size() == y.size());
// using numpy arrays
PyObject* xarray = get_array(x);
PyObject* yarray = get_array(y);
// 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<std::string, std::string>::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_plot, args, kwargs);
Py_DECREF(args);
Py_DECREF(kwargs);
if(res) Py_DECREF(res);
return res;
}
template <typename Numeric>
void plot_surface(const std::vector<::std::vector<Numeric>> &x,
const std::vector<::std::vector<Numeric>> &y,
const std::vector<::std::vector<Numeric>> &z,
const std::map<std::string, std::string> &keywords =
std::map<std::string, std::string>()) {
assert(x.size() == y.size());
assert(y.size() == z.size());
// 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);
PyTuple_SetItem(args, 0, xarray);
PyTuple_SetItem(args, 1, yarray);
PyTuple_SetItem(args, 2, zarray);
// Build up the kw args.
PyObject *kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1));
PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1));
PyObject *python_colormap_coolwarm = PyObject_GetAttrString(
detail::_interpreter::get().s_python_colormap, "coolwarm");
PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm);
for (std::map<std::string, std::string>::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 *plot_surface = PyObject_GetAttrString(axis, "plot_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");
Py_DECREF(plot_surface);
Py_DECREF(axis);
Py_DECREF(args);
Py_DECREF(kwargs);
if (res) Py_DECREF(res);
}
template<typename Numeric>
bool stem(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
{
assert(x.size() == y.size());
// using numpy arrays
PyObject* xarray = get_array(x);
PyObject* yarray = get_array(y);
// 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<std::string, std::string>::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);
Py_DECREF(args);
Py_DECREF(kwargs);
if (res)
Py_DECREF(res);
return res;
}
template< typename Numeric >
bool fill_between(const std::vector<Numeric>& x, const std::vector<Numeric>& y1, const std::vector<Numeric>& y2, const std::map<std::string, std::string>& keywords)
{
assert(x.size() == y1.size());
assert(x.size() == y2.size());
// 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);
// construct keyword args
PyObject* kwargs = PyDict_New();
for(std::map<std::string, std::string>::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);
if(res) Py_DECREF(res);
return res;
}
template< typename Numeric>
bool hist(const std::vector<Numeric>& y, long bins=10,std::string color="b", double alpha=1.0)
{
PyObject* yarray = 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));
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< typename Numeric>
bool named_hist(std::string label,const std::vector<Numeric>& 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(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<typename NumericX, typename NumericY>
bool plot(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
{
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* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
Py_DECREF(plot_args);
if(res) Py_DECREF(res);
return res;
}
template<typename NumericX, typename NumericY>
bool stem(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
{
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* res = PyObject_CallObject(
detail::_interpreter::get().s_python_function_stem, plot_args);
Py_DECREF(plot_args);
if (res)
Py_DECREF(res);
return res;
}
template<typename NumericX, typename NumericY>
bool semilogx(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
{
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* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args);
Py_DECREF(plot_args);
if(res) Py_DECREF(res);
return res;
}
template<typename NumericX, typename NumericY>
bool semilogy(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
{
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* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogy, plot_args);
Py_DECREF(plot_args);
if(res) Py_DECREF(res);
return res;
}
template<typename NumericX, typename NumericY>
bool loglog(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
{
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* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_loglog, plot_args);
Py_DECREF(plot_args);
if(res) Py_DECREF(res);
return res;
}
template<typename NumericX, typename NumericY>
bool errorbar(const std::vector<NumericX> &x, const std::vector<NumericY> &y, const std::vector<NumericX> &yerr, const std::string & /*s*/ = "")
{
assert(x.size() == y.size());
PyObject* xarray = get_array(x);
PyObject* yarray = get_array(y);
PyObject* yerrarray = get_array(yerr);
PyObject *kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "yerr", yerrarray);
//PyObject *pystring = PyString_FromString(s.c_str());
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<typename Numeric>
bool named_plot(const std::string& name, const std::vector<Numeric>& y, const std::string& format = "")
{
PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
PyObject* yarray = get_array(y);
PyObject* pystring = PyString_FromString(format.c_str());
PyObject* plot_args = PyTuple_New(2);
PyTuple_SetItem(plot_args, 0, yarray);
PyTuple_SetItem(plot_args, 1, 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);
return res;
}
template<typename Numeric>
bool named_plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
{
PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
PyObject* xarray = get_array(x);
PyObject* yarray = get_array(y);
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* 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);
return res;
}
template<typename Numeric>
bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
{
PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
PyObject* xarray = get_array(x);
PyObject* yarray = get_array(y);
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* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs);
Py_DECREF(kwargs);
Py_DECREF(plot_args);
if (res) Py_DECREF(res);
return res;
}
template<typename Numeric>
bool named_semilogy(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
{
PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
PyObject* xarray = get_array(x);
PyObject* yarray = get_array(y);
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* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs);
Py_DECREF(kwargs);
Py_DECREF(plot_args);
if (res) Py_DECREF(res);
return res;
}
template<typename Numeric>
bool named_loglog(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
{
PyObject* kwargs = PyDict_New();
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
PyObject* xarray = get_array(x);
PyObject* yarray = get_array(y);
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* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs);
Py_DECREF(kwargs);
Py_DECREF(plot_args);
if (res) Py_DECREF(res);
return res;
}
template<typename Numeric>
bool plot(const std::vector<Numeric>& y, const std::string& format = "")
{
std::vector<Numeric> x(y.size());
for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
return plot(x,y,format);
}
template<typename Numeric>
bool stem(const std::vector<Numeric>& y, const std::string& format = "")
{
std::vector<Numeric> x(y.size());
for (size_t i = 0; i < x.size(); ++i) x.at(i) = i;
return stem(x, y, format);
}
inline void figure()
{
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple);
if(!res) throw std::runtime_error("Call to figure() failed.");
Py_DECREF(res);
}
inline void legend()
{
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.");
Py_DECREF(res);
}
template<typename Numeric>
void ylim(Numeric left, Numeric right)
{
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);
}
template<typename Numeric>
void xlim(Numeric left, Numeric right)
{
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.");
Py_DECREF(args);
Py_DECREF(res);
}
inline double* xlim()
{
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()
{
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;
}
inline void subplot(long nrows, long ncols, long plot_number)
{
// 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_subplot, args);
if(!res) throw std::runtime_error("Call to subplot() failed.");
Py_DECREF(args);
Py_DECREF(res);
}
inline void title(const std::string &titlestr)
{
PyObject* pytitlestr = PyString_FromString(titlestr.c_str());
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, pytitlestr);
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_title, args);
if(!res) throw std::runtime_error("Call to title() failed.");
Py_DECREF(args);
Py_DECREF(res);
}
inline void axis(const std::string &axisstr)
{
PyObject* str = PyString_FromString(axisstr.c_str());
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, str);
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args);
if(!res) throw std::runtime_error("Call to title() failed.");
Py_DECREF(args);
Py_DECREF(res);
}
inline void xlabel(const std::string &str)
{
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_xlabel, args);
if(!res) throw std::runtime_error("Call to xlabel() failed.");
Py_DECREF(args);
Py_DECREF(res);
}
inline void ylabel(const std::string &str)
{
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_ylabel, args);
if(!res) throw std::runtime_error("Call to ylabel() failed.");
Py_DECREF(args);
Py_DECREF(res);
}
inline void grid(bool flag)
{
PyObject* pyflag = flag ? Py_True : Py_False;
Py_INCREF(pyflag);
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, pyflag);
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);
}
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);
}
if (!res) throw std::runtime_error("Call to show() failed.");
Py_DECREF(res);
}
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 close() failed.");
Py_DECREF(res);
}
inline void xkcd() {
PyObject* res;
PyObject *kwargs = PyDict_New();
res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd,
detail::_interpreter::get().s_python_empty_tuple, kwargs);
Py_DECREF(kwargs);
if (!res)
throw std::runtime_error("Call to show() failed.");
Py_DECREF(res);
}
inline void draw()
{
PyObject* res = PyObject_CallObject(
detail::_interpreter::get().s_python_function_draw,
detail::_interpreter::get().s_python_empty_tuple);
if (!res) throw std::runtime_error("Call to draw() failed.");
Py_DECREF(res);
}
template<typename Numeric>
inline void pause(Numeric interval)
{
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval));
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args);
if(!res) throw std::runtime_error("Call to pause() failed.");
Py_DECREF(args);
Py_DECREF(res);
}
inline void save(const std::string& filename)
{
PyObject* pyfilename = PyString_FromString(filename.c_str());
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, pyfilename);
PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args);
if (!res) throw std::runtime_error("Call to save() failed.");
Py_DECREF(args);
Py_DECREF(res);
}
inline void clf() {
PyObject *res = PyObject_CallObject(
detail::_interpreter::get().s_python_function_clf,
detail::_interpreter::get().s_python_empty_tuple);
if (!res) throw std::runtime_error("Call to clf() failed.");
Py_DECREF(res);
}
inline void ion() {
PyObject *res = PyObject_CallObject(
detail::_interpreter::get().s_python_function_ion,
detail::_interpreter::get().s_python_empty_tuple);
if (!res) throw std::runtime_error("Call to ion() failed.");
Py_DECREF(res);
}
// Actually, is there any reason not to call this automatically for every plot?
inline void tight_layout() {
PyObject *res = PyObject_CallObject(
detail::_interpreter::get().s_python_function_tight_layout,
detail::_interpreter::get().s_python_empty_tuple);
if (!res) throw std::runtime_error("Call to tight_layout() failed.");
Py_DECREF(res);
}
#if __cplusplus > 199711L || _MSC_VER > 1800
// C++11-exclusive content starts here (variadic plot() and initializer list support)
namespace detail {
template<typename T>
using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
template<bool obj, typename T>
struct is_callable_impl;
template<typename T>
struct is_callable_impl<false, T>
{
typedef is_function<T> type;
}; // a non-object is callable iff it is a function
template<typename T>
struct is_callable_impl<true, T>
{
struct Fallback { void operator()(); };
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
template<typename U>
static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match
template<typename U>
static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
public:
typedef decltype(test<Derived>(nullptr)) type;
typedef decltype(&Fallback::operator()) dtype;
static constexpr bool value = type::value;
}; // an object is callable iff it defines operator()
template<typename T>
struct is_callable
{
// dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
typedef typename is_callable_impl<std::is_class<T>::value, T>::type type;
};
template<typename IsYDataCallable>
struct plot_impl { };
template<>
struct plot_impl<std::false_type>
{
template<typename IterableX, typename IterableY>
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;
}
};
template<>
struct plot_impl<std::true_type>
{
template<typename Iterable, typename Callable>
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<double> y;
for(auto x : ticks) y.push_back(f(x));
return plot_impl<std::false_type>()(ticks,y,format);
}
};
} // end namespace detail
// recursion stop for the above
template<typename... Args>
bool plot() { return true; }
template<typename A, typename B, typename... Args>
bool plot(const A& a, const B& b, const std::string& format, Args... args)
{
return detail::plot_impl<typename detail::is_callable<B>::type>()(a,b,format) && plot(args...);
}
/*
* 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<double>& x, const std::vector<double>& y, const std::string& format = "") {
return plot<double,double>(x,y,format);
}
inline bool plot(const std::vector<double>& y, const std::string& format = "") {
return plot<double>(y,format);
}
inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::map<std::string, std::string>& keywords) {
return plot<double>(x,y,keywords);
}
#endif
} // end namespace matplotlibcpp