#pragma once #include #include #include #include #include #include #include // requires c++11 support #if __cplusplus > 199711L || _MSC_VER > 1800 #include #endif #include #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 #endif namespace matplotlibcpp { namespace detail { static std::string s_backend; struct _interpreter { PyObject *s_python_function_show; 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_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_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: _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_array(); // initialize numpy C-API #endif PyObject* matplotlibname = PyString_FromString("matplotlib"); PyObject* pyplotname = PyString_FromString("matplotlib.pyplot"); PyObject* pylabname = PyString_FromString("pylab"); if (!pyplotname || !pylabname || !matplotlibname) { 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("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!"); } 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_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_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_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_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_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_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(); } }; } // must be called before the first regular call to matplotlib to have any effect void backend(const std::string& name) { detail::s_backend = name; } 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 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 std::vector& v) { detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work NPY_TYPES type = select_npy_type::type; if (type == NPY_NOTYPE) { std::vector 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; } #else // fallback if we don't have numpy: copy every element of the given vector 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))); } return list; } #endif // WITHOUT_NUMPY template bool plot(const std::vector &x, const std::vector &y, const std::map& 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::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 bool stem(const std::vector &x, const std::vector &y, const std::map& 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::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& x, const std::vector& y1, const std::vector& y2, const std::map& 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::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& 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& 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 bool plot(const std::vector& x, const std::vector& 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 bool stem(const std::vector& x, const std::vector& 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 bool semilogx(const std::vector& x, const std::vector& 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 bool semilogy(const std::vector& x, const std::vector& 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 bool loglog(const std::vector& x, const std::vector& 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 bool errorbar(const std::vector &x, const std::vector &y, const std::vector &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 bool named_plot(const std::string& name, const std::vector& 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 bool named_plot(const std::string& name, const std::vector& x, const std::vector& 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 bool named_semilogx(const std::string& name, const std::vector& x, const std::vector& 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 bool named_semilogy(const std::string& name, const std::vector& x, const std::vector& 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 bool named_loglog(const std::string& name, const std::vector& x, const std::vector& 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 bool plot(const std::vector& y, const std::string& format = "") { std::vector x(y.size()); for(size_t i=0; i 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() { 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 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 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."); // if PyDeCRFF, the function doesn't work on Mac OS } 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."); // if PyDeCRFF, the function doesn't work on Mac OS } 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."); // if PyDeCRFF, the function doesn't work on Mac OS } 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."); // if PyDeCRFF, the function doesn't work on Mac OS } inline void grid(bool flag) { PyObject* pyflag = flag ? Py_True : Py_False; 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."); // if PyDeCRFF, the function doesn't work on Mac OS } 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); } if (!res) throw std::runtime_error("Call to show() 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); 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 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); } // 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 using is_function = typename std::is_function>>::type; template struct is_callable_impl; template struct is_callable_impl { typedef is_function type; }; // a non-object is callable iff it is a function template struct is_callable_impl { struct Fallback { void operator()(); }; struct Derived : T, Fallback { }; template struct Check; template static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match template static std::false_type test( Check* ); 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; }; 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; } }; template<> struct plot_impl { template bool operator()(const Iterable& ticks, const Callable& f, const std::string& format) { //std::cout << "Callable impl called" << std::endl; 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); } }; } // recursion stop for the above template bool plot() { return true; } 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...); } /* * This group of plot() functions is needed to support initializer lists, i.e. calling * plot( {1,2,3,4} ) */ bool plot(const std::vector& x, const std::vector& y, const std::string& format = "") { return plot(x,y,format); } bool plot(const std::vector& y, const std::string& format = "") { return plot(y,format); } bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { return plot(x,y,keywords); } bool stem(const std::vector& x, const std::vector& y, const std::string& format = "") { return stem(x, y, format); } bool stem(const std::vector& y, const std::string& format = "") { return stem(y, format); } bool stem(const std::vector& x, const std::vector& y, const std::map& keywords) { return stem(x, y, keywords); } bool named_plot(const std::string& name, const std::vector& x, const std::vector& y, const std::string& format = "") { return named_plot(name,x,y,format); } #endif }