diff --git a/basic.png b/basic.png new file mode 100644 index 0000000..8855da8 Binary files /dev/null and b/basic.png differ diff --git a/binding.gyp b/binding.gyp index bd15660..1e1eaba 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,25 +3,26 @@ { "target_name": "matplotlib", "sources": [ "src/matplotlib.cc" ], - "libraries": [ - "-ldl" + "include_dirs": [ + " + +template +struct convert { + static bool valid(Napi::Value value); + static T from(Napi::Value value); +}; + +// String => std::string +template<> +struct convert { + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsString(); + } + + static std::string from(Napi::Value value) { + if (!valid(value)) { + throw std::invalid_argument("expected String, got something else"); + } + + return std::string(value.As()); + } +}; + +// Number => float | double +template +struct convert::value>::type> { + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsNumber(); + } + + static T from(Napi::Value value) { + if (!valid(value)) { + throw std::invalid_argument("floating point conversion expected Number, got something else"); + } + + return value.As(); + } +}; + +// Number => int32_t | uint32_t | int64_t +template +struct convert::value>::type> { + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsNumber(); + } + + static T from(Napi::Value value) { + if (!valid(value)) { + throw std::invalid_argument("integer conversion expected Number, got something else"); + } + + size_t bits = sizeof(T) * CHAR_BIT; + bool is_signed = std::is_signed::value; + + if (bits <= 32) { + if (is_signed) { + return value.As().Int32Value(); + } else { + return value.As().Uint32Value(); + } + } + + return value.As().Int64Value(); + } +}; + +// Boolean => bool +template<> +struct convert { + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsBoolean(); + } + + static bool from(Napi::Value value) { + if (!valid(value)) { + throw std::invalid_argument("expected Boolean, got something else"); + } + + return value.As(); + } +}; + +// Array => std::vector +template +struct convert> { + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsArray(); + } + + static std::vector from(Napi::Value value) { + if (!valid(value)) { + throw std::invalid_argument("expected Array, got something else"); + } + + Napi::Array array = value.As(); + std::vector output; + + for (uint32_t i = 0, count = array.Length(); i < count; ++i) { + output.emplace_back(convert::from(array[i])); + } + + return output; + } +}; + +// Object => std::map +template +struct convert> { + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsObject(); + } + + static std::map from(Napi::Value value) { + if (!valid(value)) { + throw std::invalid_argument("expected Object, got something else"); + } + + Napi::Object object = value.As(); + Napi::Array keys = object.GetPropertyNames(); + std::map output; + + for (uint32_t i = 0, count = keys.Length(); i < count; ++i) { + Napi::Value key = keys[i]; + Napi::Value value = object.Get(key); + + output.emplace(convert::from(key), convert::from(value)); + } + + return output; + } +}; + +template +auto from(Napi::Value value) + -> decltype(convert::from(value)) { + return convert::from(value); +} + +template +auto from(Napi::Value value, T default_value) + -> decltype(convert::from(value)) { + return convert::valid(value) + ? convert::from(value) + : default_value; +} diff --git a/lib/matplotlibcpp.h b/lib/matplotlibcpp.h new file mode 100644 index 0000000..43f38ba --- /dev/null +++ b/lib/matplotlibcpp.h @@ -0,0 +1,2206 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include // requires c++11 support +#include + +#define PY_SSIZE_T_CLEAN +#include + +#ifndef WITHOUT_NUMPY +# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +# include + +# ifdef WITH_OPENCV +# include +# endif // WITH_OPENCV + +/* + * 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 +#endif // WITHOUT_NUMPY + +#if PY_MAJOR_VERSION >= 3 +# 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; + } + +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* 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 = safe_import(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); + } + + ~_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) +{ + 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; +} + +namespace detail { + +#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; +} + +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_2d_array v too small"); + + npy_intp vsize[2] = {static_cast(v.size()), + static_cast(v[0].size())}; + + PyArrayObject *varray = + (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE); + + 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]; + } + + return reinterpret_cast(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 + +// sometimes, for labels and such, we need string arrays +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())); + } + 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])); + } + return listlist; +} + +} // 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()); + + // 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 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; +} + +// 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()) +{ + // 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()); + + // using numpy arrays + PyObject *xarray = detail::get_2darray(x); + PyObject *yarray = detail::get_2darray(y); + PyObject *zarray = detail::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::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); +} +#endif // WITHOUT_NUMPY + +template +void plot3(const std::vector &x, + const std::vector &y, + const std::vector &z, + const std::map &keywords = + std::map()) +{ + // 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); + + // 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(); + + 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); + + Py_DECREF(axis); + Py_DECREF(args); + Py_DECREF(kwargs); + 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()); + + // 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 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(const std::vector& x, const std::vector& y, const std::map& keywords) +{ + assert(x.size() == y.size()); + + // 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 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); + + 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 = 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); + 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, bool cumulative=false) +{ + + 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); + + 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; +} + +#ifndef WITHOUT_NUMPY +namespace detail { + +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); + + detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work + + // 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 *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); +} + +} // namespace detail + +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())); + } + + // 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); + } + + detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords); +} +#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 = {}) +{ + assert(x.size() == y.size()); + + PyObject* xarray = detail::get_array(x); + PyObject* yarray = detail::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* 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); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +template +bool boxplot(const std::vector>& data, + const std::vector& labels = {}, + const std::map & keywords = {}) +{ + 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 boxplot(const std::vector& data, + const std::map & keywords = {}) +{ + PyObject* vector = detail::get_array(data); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, vector); + + PyObject* kwargs = PyDict_New(); + 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 bar(const std::vector & x, + const std::vector & y, + std::string ec = "black", + std::string ls = "-", + double lw = 1.0, + const std::map & keywords = {}) { + PyObject * xarray = detail::get_array(x); + PyObject * yarray = detail::get_array(y); + + 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); + 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); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + 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; + + 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 = {}) +{ + + 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_subplots_adjust, detail::_interpreter::get().s_python_empty_tuple, kwargs); + + 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 = 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + + 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 = "") +{ + 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); + + 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 = detail::get_array(x); + PyObject* yarray = detail::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 = detail::get_array(x); + PyObject* yarray = detail::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 = detail::get_array(x); + PyObject* yarray = detail::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 = detail::get_array(x); + PyObject* yarray = detail::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 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& 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); +} + +template +void text(Numeric x, Numeric y, const std::string& s = "") +{ + 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_text, args); + if(!res) throw std::runtime_error("Call to text() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +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."); + 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); +} + + +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); + + // Make sure interpreter is initialised + detail::_interpreter::get(); + + 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); + } + + if(!res) throw std::runtime_error("Call to figure() failed."); + + 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(num); + Py_DECREF(res); + + return figureNumber; +} + +inline bool fignum_exists(long number) +{ + // Make sure interpreter is initialised + 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; +} + +inline void figure_size(size_t w, size_t h) +{ + // Make sure interpreter is initialised + 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); +} + +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* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, detail::_interpreter::get().s_python_empty_tuple); + 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* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, detail::_interpreter::get().s_python_empty_tuple); + 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()); + + // 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_xticks, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if(!res) throw std::runtime_error("Call to xticks() failed"); + + Py_DECREF(res); +} + +template +inline void xticks(const std::vector &ticks, const std::map& keywords) +{ + xticks(ticks, {}, keywords); +} + +template +inline void yticks(const std::vector &ticks, const std::vector &labels = {}, const std::map& keywords = {}) +{ + assert(labels.size() == 0 || ticks.size() == labels.size()); + + // 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); + + Py_DECREF(args); + Py_DECREF(kwargs); + if(!res) throw std::runtime_error("Call to yticks() failed"); + + Py_DECREF(res); +} + +template +inline void yticks(const std::vector &ticks, const std::map& keywords) +{ + yticks(ticks, {}, keywords); +} + +inline void tick_params(const std::map& keywords, const std::string axis = "both") +{ + // 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())); + } + + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_tick_params, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + if (!res) throw std::runtime_error("Call to tick_params() failed"); + + Py_DECREF(res); +} + +inline void subplot(long nrows, long ncols, long plot_number) +{ + // construct positional args + PyObject* args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyLong_FromLong(nrows)); + PyTuple_SetItem(args, 1, PyLong_FromLong(ncols)); + PyTuple_SetItem(args, 2, PyLong_FromLong(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 subplot2grid(long nrows, long ncols, long rowid=0, long colid=0, long rowspan=1, long colspan=1) +{ + PyObject* shape = PyTuple_New(2); + PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows)); + PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols)); + + PyObject* loc = PyTuple_New(2); + PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid)); + PyTuple_SetItem(loc, 1, PyLong_FromLong(colid)); + + 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)); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot2grid, args); + if(!res) throw std::runtime_error("Call to subplot2grid() failed."); + + Py_DECREF(shape); + Py_DECREF(loc); + Py_DECREF(args); + Py_DECREF(res); +} + +inline void title(const std::string &titlestr, const std::map &keywords = {}) +{ + 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())); + } + + 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(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline void suptitle(const std::string &suptitlestr, const std::map &keywords = {}) +{ + 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())); + } + + 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(args); + Py_DECREF(kwargs); + 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); +} + +void axvline(double x, double ymin = 0., double ymax = 1., const std::map& keywords = std::map()) +{ + // 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* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvline, args, kwargs); + + Py_DECREF(args); + Py_DECREF(kwargs); + + if(res) Py_DECREF(res); +} + +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); + + 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_xlabel, args, kwargs); + if(!res) throw std::runtime_error("Call to xlabel() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +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* 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_ylabel, args, kwargs); + if(!res) throw std::runtime_error("Call to ylabel() failed."); + + Py_DECREF(args); + Py_DECREF(kwargs); + Py_DECREF(res); +} + +inline void set_zlabel(const std::string &str, const std::map& keywords = {}) { + // 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!"); } + } + + PyObject* pystr = PyString_FromString(str.c_str()); + PyObject* args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pystr); + + 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 *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); + + 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(zlabel, args, kwargs); + if (!res) throw std::runtime_error("Call to set_zlabel() failed."); + Py_DECREF(zlabel); + + Py_DECREF(ax); + Py_DECREF(args); + Py_DECREF(kwargs); + if (res) 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 +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); +} + +inline std::vector> ginput(const int numClicks = 1, const std::map& keywords = {}) +{ + PyObject *args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks)); + + // 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_ginput, args, kwargs); + + 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); + + return out; +} + +// 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); +} + +// Support for variadic plot() and initializer lists: + +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) + { + 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); + } +}; + +} // end namespace detail + +// 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} ) + */ +inline bool plot(const std::vector& x, const std::vector& y, const std::string& format = "") { + return plot(x,y,format); +} + +inline bool plot(const std::vector& y, const std::string& format = "") { + return plot(y,format); +} + +inline bool plot(const std::vector& x, const std::vector& y, const std::map& keywords) { + return plot(x,y,keywords); +} + +/* + * 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 = "") { + + assert(x.size() == y.size()); + + 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* 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) + { + line= PyList_GetItem(res, 0); + + 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; + } + + // 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* res = PyObject_CallObject(remove_fct, detail::_interpreter::get().s_python_empty_tuple); + 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; +}; + +} // end namespace matplotlibcpp diff --git a/package-lock.json b/package-lock.json index 00e0b02..19f4632 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,13 @@ { "name": "matplotnode", - "version": "0.4.1", - "lockfileVersion": 1 + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "node-addon-api": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz", + "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA==" + } + } } diff --git a/package.json b/package.json index 3842b16..b2ece5c 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "matplotnode", - "version": "0.4.2", + "version": "1.0.0", "description": "C++ bindings for Node.js exposing a subset of matplotlib's functionality through the CPython API.", - "main": "src/matplotlib.js", + "main": "build/Release/matplotlib.node", "scripts": { "test": "node test.js", "install": "node-gyp configure rebuild" @@ -22,5 +22,8 @@ "bugs": { "url": "https://github.com/mateogianolio/matplotnode/issues" }, - "homepage": "https://github.com/mateogianolio/matplotnode#readme" + "homepage": "https://github.com/mateogianolio/matplotnode#readme", + "dependencies": { + "node-addon-api": "^2.0.0" + } } diff --git a/src/matplotlib.cc b/src/matplotlib.cc index 4434ddf..4ff1060 100644 --- a/src/matplotlib.cc +++ b/src/matplotlib.cc @@ -1,213 +1,410 @@ -#include "matplotlib.h" +#include +#include "matplotlibcpp.h" +#include "convert.h" -namespace plt { - void plot(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); +namespace plt = matplotlibcpp; - PyObject *args, *kwargs, *data, *result; - uint32_t arglen = info.Length(); - uint32_t datalen; +namespace matplotnode { + void backend(const Napi::CallbackInfo& info) { + auto name = from(info[0], ""); - args = PyTuple_New(2); - kwargs = PyDict_New(); + plt::backend(name); + } + + void annotate(const Napi::CallbackInfo& info) { + auto annotation = from(info[0], ""); + auto x = from(info[1], {}); + auto y = from(info[2], {}); + + plt::annotate(annotation, x, y); + } + + void plot(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto keywords = from>(info[2], {}); + + plt::plot(x, y, keywords); + } + + void plot3(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto z = from>(info[2], {}); + auto keywords = from>(info[3], {}); + + plt::plot3(x, y, z, keywords); + } + + void stem(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto keywords = from>(info[2], {}); + + plt::stem(x, y, keywords); + } + + void fill(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto keywords = from>(info[2], {}); + + plt::fill(x, y, keywords); + } + + void fill_between(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y1 = from>(info[1], {}); + auto y2 = from>(info[2], {}); + auto keywords = from>(info[3], {}); + + plt::fill_between(x, y1, y2, keywords); + } + + void hist(const Napi::CallbackInfo& info) { + auto y = from>(info[0], {}); + auto bins = from(info[1], 10); + auto color = from(info[2], "b"); + auto alpha = from(info[3], 1.0); + auto cumulative = from(info[4], false); + + plt::hist(y, bins, color, alpha, cumulative); + } + + void scatter(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto s = from(info[2], 1.0); + auto keywords = from>(info[3], {}); + + plt::scatter(x, y, s, keywords); + } + + void boxplot(const Napi::CallbackInfo& info) { + auto data = from>(info[0], {}); + auto keywords = from>(info[1], {}); + + plt::boxplot(data, keywords); + } + + void bar(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto ec = from(info[2], "black"); + auto ls = from(info[3], "-"); + auto lw = from(info[4], 1.0); + auto keywords = from>(info[5], {}); + + plt::bar(x, y, ec, ls, lw, keywords); + } + + void subplots_adjust(const Napi::CallbackInfo& info) { + auto keywords = from>(info[0], {}); + + plt::subplots_adjust(keywords); + } + + void named_hist(const Napi::CallbackInfo& info) { + auto label = from(info[0], ""); + auto y = from>(info[1], {}); + auto bins = from(info[2], 10); + auto color = from(info[3], "b"); + auto alpha = from(info[4], 1.0); + + plt::named_hist(label, y, bins, color, alpha); + } - for (uint32_t i = 0; i < arglen; i++) { - if (info[i]->IsString()) { - std::string s = std::string(*v8::String::Utf8Value(isolate, info[i].As())); + void quiver(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto u = from>(info[2], {}); + auto w = from>(info[3], {}); + auto keywords = from>(info[4], {}); - unsigned long eq = s.find("="); - if (eq != std::string::npos) { - std::string left = s.substr(0, eq); - std::string right = s.substr(eq + 1, s.size() - 1); + plt::quiver(x, y, u, w, keywords); + } - PyDict_SetItemString(kwargs, left.c_str(), PyString_FromString(right.c_str())); - } - } else if (info[i]->IsArray()) { - v8::Local array = info[i].As(); - datalen = array->Length(); - data = PyList_New(datalen); + void errorbar(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y1 = from>(info[1], {}); + auto yerr = from>(info[2], {}); + auto keywords = from>(info[3], {}); - for (uint32_t j = 0; j < datalen; j++) - PyList_SetItem(data, j, PyFloat_FromDouble(array->Get(isolate->GetCurrentContext(), j).ToLocalChecked().As()->Value())); - PyTuple_SetItem(args, i, data); - } - } + plt::errorbar(x, y1, yerr, keywords); + } - result = PyObject_Call(interpreter::get().plot, args, kwargs); + void named_plot(const Napi::CallbackInfo& info) { + auto name = from(info[0], ""); + auto x = from>(info[1], {}); + auto y = from>(info[2], {}); + auto format = from(info[3], ""); - Py_DECREF(args); - Py_DECREF(kwargs); - Py_XDECREF(result); + plt::named_plot(name, x, y, format); } - void subplot(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void named_semilogx(const Napi::CallbackInfo& info) { + auto name = from(info[0], ""); + auto x = from>(info[1], {}); + auto y = from>(info[2], {}); + auto format = from(info[3], ""); - std::string s = std::string(*v8::String::Utf8Value(isolate, info[0].As())); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(s.c_str())); + plt::named_semilogx(name, x, y, format); + } - PyObject *result = PyObject_CallObject(interpreter::get().subplot, args); + void named_semilogy(const Napi::CallbackInfo& info) { + auto name = from(info[0], ""); + auto x = from>(info[1], {}); + auto y = from>(info[2], {}); + auto format = from(info[3], ""); - Py_DECREF(args); - Py_XDECREF(result); + plt::named_semilogy(name, x, y, format); } - void show(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().show, interpreter::get().empty_tuple); - Py_XDECREF(result); + void named_loglog(const Napi::CallbackInfo& info) { + auto name = from(info[0], ""); + auto x = from>(info[1], {}); + auto y = from>(info[2], {}); + auto format = from(info[3], ""); + + plt::named_loglog(name, x, y, format); } - void legend(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().legend, interpreter::get().empty_tuple); - Py_XDECREF(result); + void text(const Napi::CallbackInfo& info) { + auto x = from(info[0], {}); + auto y = from(info[1], {}); + auto s = from(info[2], ""); + + plt::text(x, y, s); } - void grid(const v8::FunctionCallbackInfo& info) { - PyObject *flag = info[0].As()->Value() ? Py_True : Py_False; - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, flag); + void figure(const Napi::CallbackInfo& info) { + auto number = from(info[0], 0); - PyObject *result = PyObject_CallObject(interpreter::get().grid, args); - - Py_DECREF(args); - Py_XDECREF(result); + plt::figure(number); } - void save(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void figure_size(const Napi::CallbackInfo& info) { + auto w = from(info[0], 0); + auto h = from(info[1], 0); - std::string s = std::string(*v8::String::Utf8Value(isolate, info[0].As())); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(s.c_str())); + plt::figure_size(w, h); + } + + void legend(const Napi::CallbackInfo& info) { + plt::legend(); + } - PyObject *result = PyObject_CallObject(interpreter::get().save, args); + void ylim(const Napi::CallbackInfo& info) { + auto left = from(info[0], 0.0); + auto right = from(info[1], 0.0); - Py_DECREF(args); - Py_XDECREF(result); + plt::ylim(left, right); } - void xlim(const v8::FunctionCallbackInfo& info) { - PyObject *list = PyList_New(2); - PyObject *args = PyTuple_New(1); + void xlim(const Napi::CallbackInfo& info) { + auto left = from(info[0], 0.0); + auto right = from(info[1], 0.0); - PyList_SetItem(list, 0, PyFloat_FromDouble(info[0].As()->Value())); - PyList_SetItem(list, 1, PyFloat_FromDouble(info[1].As()->Value())); - PyTuple_SetItem(args, 0, list); + plt::xlim(left, right); + } - PyObject *result = PyObject_CallObject(interpreter::get().xlim, args); + void xticks(const Napi::CallbackInfo& info) { + auto ticks = from>(info[0], {}); + auto keywords = from>(info[1], {}); - Py_DECREF(args); - Py_XDECREF(result); + plt::xticks(ticks, keywords); } - - void ylim(const v8::FunctionCallbackInfo& info) { - PyObject *list = PyList_New(2); - PyObject *args = PyTuple_New(1); - PyList_SetItem(list, 0, PyFloat_FromDouble(info[0].As()->Value())); - PyList_SetItem(list, 1, PyFloat_FromDouble(info[1].As()->Value())); - PyTuple_SetItem(args, 0, list); + void yticks(const Napi::CallbackInfo& info) { + auto ticks = from>(info[0], {}); + auto keywords = from>(info[1], {}); - PyObject *result = PyObject_CallObject(interpreter::get().ylim, args); - - Py_DECREF(args); - Py_XDECREF(result); + plt::yticks(ticks, keywords); } - void title(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void tick_params(const Napi::CallbackInfo& info) { + auto keywords = from>(info[0], {}); + auto axis = from(info[1], ""); - std::string s = std::string(*v8::String::Utf8Value(isolate, info[0].As())); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(s.c_str())); + plt::tick_params(keywords, axis); + } - PyObject *result = PyObject_CallObject(interpreter::get().title, args); + void subplot(const Napi::CallbackInfo& info) { + auto nrows = from(info[0], 0); + auto ncols = from(info[1], 0); + auto plot_number = from(info[2], 0); - Py_DECREF(args); - Py_XDECREF(result); + plt::subplot(nrows, ncols, plot_number); } - void axis(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void subplot2grid(const Napi::CallbackInfo& info) { + auto nrows = from(info[0], 0); + auto ncols = from(info[1], 0); + auto rowid = from(info[2], 0); + auto colid = from(info[3], 0); + auto rowspan = from(info[4], 0); + auto colspan = from(info[5], 0); - std::string s = std::string(*v8::String::Utf8Value(isolate, info[0].As())); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(s.c_str())); + plt::subplot2grid(nrows, ncols, rowid, colid, rowspan, colspan); + } - PyObject *result = PyObject_CallObject(interpreter::get().axis, args); + void title(const Napi::CallbackInfo& info) { + auto titlestr = from(info[0], ""); + auto keywords = from>(info[1], {}); - Py_DECREF(args); - Py_XDECREF(result); + plt::title(titlestr, keywords); } - void xlabel(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void suptitle(const Napi::CallbackInfo& info) { + auto suptitlestr = from(info[0], ""); + auto keywords = from>(info[1], {}); - std::string s = std::string(*v8::String::Utf8Value(isolate, info[0].As())); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(s.c_str())); + plt::suptitle(suptitlestr, keywords); + } - PyObject *result = PyObject_CallObject(interpreter::get().xlabel, args); + void axis(const Napi::CallbackInfo& info) { + auto axisstr = from(info[0], ""); - Py_DECREF(args); - Py_XDECREF(result); + plt::axis(axisstr); } - void ylabel(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void axvline(const Napi::CallbackInfo& info) { + auto x = from(info[0], 0.0); + auto ymin = from(info[1], 0.0); + auto ymax = from(info[2], 0.0); + auto keywords = from>(info[3], {}); - std::string s = std::string(*v8::String::Utf8Value(isolate, info[0].As())); - PyObject *args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyString_FromString(s.c_str())); + plt::axvline(x, ymin, ymax, keywords); + } - PyObject *result = PyObject_CallObject(interpreter::get().ylabel, args); + void xlabel(const Napi::CallbackInfo& info) { + auto str = from(info[0], ""); + auto keywords = from>(info[1], {}); - Py_DECREF(args); - Py_XDECREF(result); + plt::xlabel(str, keywords); } - void clf(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().clf, interpreter::get().empty_tuple); - Py_XDECREF(result); + void ylabel(const Napi::CallbackInfo& info) { + auto str = from(info[0], ""); + auto keywords = from>(info[1], {}); + + plt::ylabel(str, keywords); } - void cla(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().cla, interpreter::get().empty_tuple); - Py_XDECREF(result); + void set_zlabel(const Napi::CallbackInfo& info) { + auto str = from(info[0], ""); + auto keywords = from>(info[1], {}); + + plt::set_zlabel(str, keywords); } - void close(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().close, interpreter::get().empty_tuple); - Py_XDECREF(result); + void grid(const Napi::CallbackInfo& info) { + auto flag = from(info[0], false); + + plt::grid(flag); + } + + void show(const Napi::CallbackInfo& info) { + auto block = from(info[0], true); + + plt::show(block); } - void xkcd(const v8::FunctionCallbackInfo& info) { - PyObject *kwargs = PyDict_New(); - PyObject *result = PyObject_Call(interpreter::get().xkcd, interpreter::get().empty_tuple, kwargs); + void close(const Napi::CallbackInfo& info) { + plt::close(); + } + + void xkcd(const Napi::CallbackInfo& info) { + plt::xkcd(); + } + + void draw(const Napi::CallbackInfo& info) { + plt::draw(); + } + + void pause(const Napi::CallbackInfo& info) { + auto interval = from(info[0], 0.0); + + plt::pause(interval); + } + + void save(const Napi::CallbackInfo& info) { + auto filename = from(info[0], ""); + + plt::save(filename); + } + + void clf(const Napi::CallbackInfo& info) { + plt::clf(); + } + + void ion(const Napi::CallbackInfo& info) { + plt::ion(); + } + + void ginput(const Napi::CallbackInfo& info) { + auto numClicks = from(info[0], 0); + auto keywords = from>(info[1], {}); + + plt::ginput(numClicks, keywords); + } - Py_DECREF(kwargs); - Py_DECREF(result); + void tight_layout(const Napi::CallbackInfo& info) { + plt::tight_layout(); } } -void init(v8::Local exports) { - NODE_SET_METHOD(exports, "plot", plt::plot); - NODE_SET_METHOD(exports, "subplot", plt::subplot); - NODE_SET_METHOD(exports, "show", plt::show); - NODE_SET_METHOD(exports, "legend", plt::legend); - NODE_SET_METHOD(exports, "grid", plt::grid); - NODE_SET_METHOD(exports, "save", plt::save); - NODE_SET_METHOD(exports, "xlim", plt::xlim); - NODE_SET_METHOD(exports, "ylim", plt::ylim); - NODE_SET_METHOD(exports, "title", plt::title); - NODE_SET_METHOD(exports, "axis", plt::axis); - NODE_SET_METHOD(exports, "xlabel", plt::xlabel); - NODE_SET_METHOD(exports, "ylabel", plt::ylabel); - NODE_SET_METHOD(exports, "clf", plt::clf); - NODE_SET_METHOD(exports, "cla", plt::cla); - NODE_SET_METHOD(exports, "close", plt::close); - NODE_SET_METHOD(exports, "xkcd", plt::xkcd); +Napi::Object init(Napi::Env env, Napi::Object exports) { + exports.Set(Napi::String::New(env, "backend"), Napi::Function::New(env, matplotnode::backend)); + exports.Set(Napi::String::New(env, "annotate"), Napi::Function::New(env, matplotnode::annotate)); + exports.Set(Napi::String::New(env, "plot"), Napi::Function::New(env, matplotnode::plot)); + exports.Set(Napi::String::New(env, "plot3"), Napi::Function::New(env, matplotnode::plot3)); + exports.Set(Napi::String::New(env, "stem"), Napi::Function::New(env, matplotnode::stem)); + exports.Set(Napi::String::New(env, "fill"), Napi::Function::New(env, matplotnode::fill)); + exports.Set(Napi::String::New(env, "fill_between"), Napi::Function::New(env, matplotnode::fill_between)); + exports.Set(Napi::String::New(env, "hist"), Napi::Function::New(env, matplotnode::hist)); + exports.Set(Napi::String::New(env, "scatter"), Napi::Function::New(env, matplotnode::scatter)); + exports.Set(Napi::String::New(env, "boxplot"), Napi::Function::New(env, matplotnode::boxplot)); + exports.Set(Napi::String::New(env, "bar"), Napi::Function::New(env, matplotnode::bar)); + exports.Set(Napi::String::New(env, "subplots_adjust"), Napi::Function::New(env, matplotnode::subplots_adjust)); + exports.Set(Napi::String::New(env, "named_hist"), Napi::Function::New(env, matplotnode::named_hist)); + exports.Set(Napi::String::New(env, "quiver"), Napi::Function::New(env, matplotnode::quiver)); + exports.Set(Napi::String::New(env, "errorbar"), Napi::Function::New(env, matplotnode::errorbar)); + exports.Set(Napi::String::New(env, "named_plot"), Napi::Function::New(env, matplotnode::named_plot)); + exports.Set(Napi::String::New(env, "named_semilogx"), Napi::Function::New(env, matplotnode::named_semilogx)); + exports.Set(Napi::String::New(env, "named_semilogy"), Napi::Function::New(env, matplotnode::named_semilogy)); + exports.Set(Napi::String::New(env, "named_loglog"), Napi::Function::New(env, matplotnode::named_loglog)); + exports.Set(Napi::String::New(env, "text"), Napi::Function::New(env, matplotnode::text)); + exports.Set(Napi::String::New(env, "figure"), Napi::Function::New(env, matplotnode::figure)); + exports.Set(Napi::String::New(env, "figure_size"), Napi::Function::New(env, matplotnode::figure_size)); + exports.Set(Napi::String::New(env, "legend"), Napi::Function::New(env, matplotnode::legend)); + exports.Set(Napi::String::New(env, "ylim"), Napi::Function::New(env, matplotnode::ylim)); + exports.Set(Napi::String::New(env, "xlim"), Napi::Function::New(env, matplotnode::xlim)); + exports.Set(Napi::String::New(env, "xticks"), Napi::Function::New(env, matplotnode::xticks)); + exports.Set(Napi::String::New(env, "yticks"), Napi::Function::New(env, matplotnode::yticks)); + exports.Set(Napi::String::New(env, "tick_params"), Napi::Function::New(env, matplotnode::tick_params)); + exports.Set(Napi::String::New(env, "subplot"), Napi::Function::New(env, matplotnode::subplot)); + exports.Set(Napi::String::New(env, "subplot2grid"), Napi::Function::New(env, matplotnode::subplot2grid)); + exports.Set(Napi::String::New(env, "title"), Napi::Function::New(env, matplotnode::title)); + exports.Set(Napi::String::New(env, "suptitle"), Napi::Function::New(env, matplotnode::suptitle)); + exports.Set(Napi::String::New(env, "axis"), Napi::Function::New(env, matplotnode::axis)); + exports.Set(Napi::String::New(env, "axvline"), Napi::Function::New(env, matplotnode::axvline)); + exports.Set(Napi::String::New(env, "xlabel"), Napi::Function::New(env, matplotnode::xlabel)); + exports.Set(Napi::String::New(env, "ylabel"), Napi::Function::New(env, matplotnode::ylabel)); + exports.Set(Napi::String::New(env, "set_zlabel"), Napi::Function::New(env, matplotnode::set_zlabel)); + exports.Set(Napi::String::New(env, "grid"), Napi::Function::New(env, matplotnode::grid)); + exports.Set(Napi::String::New(env, "show"), Napi::Function::New(env, matplotnode::show)); + exports.Set(Napi::String::New(env, "close"), Napi::Function::New(env, matplotnode::close)); + exports.Set(Napi::String::New(env, "xkcd"), Napi::Function::New(env, matplotnode::xkcd)); + exports.Set(Napi::String::New(env, "draw"), Napi::Function::New(env, matplotnode::draw)); + exports.Set(Napi::String::New(env, "pause"), Napi::Function::New(env, matplotnode::pause)); + exports.Set(Napi::String::New(env, "save"), Napi::Function::New(env, matplotnode::save)); + exports.Set(Napi::String::New(env, "clf"), Napi::Function::New(env, matplotnode::clf)); + exports.Set(Napi::String::New(env, "ion"), Napi::Function::New(env, matplotnode::ion)); + exports.Set(Napi::String::New(env, "ginput"), Napi::Function::New(env, matplotnode::ginput)); + exports.Set(Napi::String::New(env, "tight_layout"), Napi::Function::New(env, matplotnode::tight_layout)); + + return exports; } -NODE_MODULE(matplotlib, init) \ No newline at end of file +NODE_API_MODULE(matplotlib, init) diff --git a/src/matplotlib.h b/src/matplotlib.h deleted file mode 100644 index 748da69..0000000 --- a/src/matplotlib.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef MATPLOTLIB_H -#define MATPLOTLIB_H - -#include -#include -#include - -#ifdef linux - #include -#endif - -#if PY_MAJOR_VERSION >= 3 - #define PyString_FromString PyUnicode_FromString -#endif - -namespace plt { - struct interpreter { - public: - PyObject *plot; - PyObject *subplot; - PyObject *show; - PyObject *legend; - PyObject *grid; - PyObject *save; - PyObject *xlim; - PyObject *ylim; - PyObject *title; - PyObject *axis; - PyObject *xlabel; - PyObject *ylabel; - PyObject *clf; - PyObject *cla; - PyObject *close; - PyObject *xkcd; - - PyObject *empty_tuple; - - static interpreter& get() { - static interpreter context; - return context; - } - private: - interpreter() { -#if PY_MAJOR_VERSION >= 3 - wchar_t name[] = L"matplotnode"; -#else - char name[] = "matplotnode"; -#endif - Py_SetProgramName(name); - -#ifdef linux -#if PY_MAJOR_VERSION >= 3 - dlopen("libpython3.so", RTLD_LAZY | RTLD_GLOBAL); -#else - dlopen("libpython2.7.so", RTLD_LAZY | RTLD_GLOBAL); -#endif -#endif - Py_Initialize(); - - PyObject *matplotlibname = PyString_FromString("matplotlib"); - PyObject* matplotlib = PyImport_Import(matplotlibname); - Py_DECREF(matplotlibname); - if (!matplotlib) { - PyErr_Print(); - fprintf(stderr, "Could not import matplotlib.pyplot.\n"); - return; - } - - PyObject_CallMethod(matplotlib, const_cast("use"), const_cast("s"), "TkAgg"); - - PyObject *pyplotname = PyString_FromString("matplotlib.pyplot"); - PyObject *pyplot = PyImport_Import(pyplotname); - Py_DECREF(pyplotname); - - if (!pyplot) { - PyErr_Print(); - fprintf(stderr, "Could not import matplotlib.pyplot.\n"); - return; - } - - plot = PyObject_GetAttrString(pyplot, "plot"); - subplot = PyObject_GetAttrString(pyplot, "subplot"); - show = PyObject_GetAttrString(pyplot, "show"); - legend = PyObject_GetAttrString(pyplot, "legend"); - grid = PyObject_GetAttrString(pyplot, "grid"); - save = PyObject_GetAttrString(pyplot, "savefig"); - xlim = PyObject_GetAttrString(pyplot, "xlim"); - ylim = PyObject_GetAttrString(pyplot, "ylim"); - title = PyObject_GetAttrString(pyplot, "title"); - axis = PyObject_GetAttrString(pyplot, "axis"); - xlabel = PyObject_GetAttrString(pyplot, "xlabel"); - ylabel = PyObject_GetAttrString(pyplot, "ylabel"); - clf = PyObject_GetAttrString(pyplot, "clf"); - cla = PyObject_GetAttrString(pyplot, "cla"); - close = PyObject_GetAttrString(pyplot, "close"); - xkcd = PyObject_GetAttrString(pyplot, "xkcd"); - - if (!plot - || !subplot - || !show - || !legend - || !grid - || !save - || !xlim - || !ylim - || !title - || !axis - || !xlabel - || !ylabel - || !clf - || !cla - || !close - || !xkcd) { - PyErr_Print(); - fprintf(stderr, "Error loading matplotlib functions.\n"); - return; - } - - if (!PyCallable_Check(plot) - || !PyCallable_Check(subplot) - || !PyCallable_Check(show) - || !PyCallable_Check(legend) - || !PyCallable_Check(grid) - || !PyCallable_Check(save) - || !PyCallable_Check(xlim) - || !PyCallable_Check(ylim) - || !PyCallable_Check(title) - || !PyCallable_Check(axis) - || !PyCallable_Check(xlabel) - || !PyCallable_Check(ylabel) - || !PyCallable_Check(clf) - || !PyCallable_Check(cla) - || !PyCallable_Check(close) - || !PyCallable_Check(xkcd)) { - PyErr_Print(); - fprintf(stderr, "One or more of the matplotlib functions are not callable.\n"); - return; - } - - empty_tuple = PyTuple_New(0); - } - - ~interpreter() { - Py_Finalize(); - } - }; - - void plot(v8::FunctionCallbackInfo& info); - void subplot(v8::FunctionCallbackInfo& info); - void show(v8::FunctionCallbackInfo& info); - void legend(v8::FunctionCallbackInfo& info); - void grid(v8::FunctionCallbackInfo& info); - void save(v8::FunctionCallbackInfo& info); - void xlim(v8::FunctionCallbackInfo& info); - void ylim(v8::FunctionCallbackInfo& info); - void title(v8::FunctionCallbackInfo& info); - void axis(v8::FunctionCallbackInfo& info); - void xlabel(v8::FunctionCallbackInfo& info); - void ylabel(v8::FunctionCallbackInfo& info); - void clf(v8::FunctionCallbackInfo& info); - void cla(v8::FunctionCallbackInfo& info); - void close(v8::FunctionCallbackInfo& info); - void xkcd(v8::FunctionCallbackInfo& info); -} - -#endif \ No newline at end of file diff --git a/src/matplotlib.js b/src/matplotlib.js deleted file mode 100644 index 5c8cb43..0000000 --- a/src/matplotlib.js +++ /dev/null @@ -1,4 +0,0 @@ -(function () { - 'use strict'; - module.exports = require('../build/Release/matplotlib'); -}()); \ No newline at end of file diff --git a/test.js b/test.js deleted file mode 100644 index 43dbb2d..0000000 --- a/test.js +++ /dev/null @@ -1,21 +0,0 @@ -(function () { - 'use strict'; - - const plt = require('./build/Release/matplotlib'); - const x = new Array(100).fill(0).map((x, i) => i / Math.PI); - - plt.xkcd(); - - plt.subplot("211"); - plt.title('trig'); - plt.plot(x, x.map(Math.sin), 'color=r', 'label=sin(x)'); - plt.plot(x, x.map(Math.cos), 'color=g', 'label=cos(x)'); - plt.legend(); - - plt.subplot("212"); - plt.plot(x, x.map(Math.sin).map((t, i) => t * i), 'color=b', 'label=x * sin(x)', 'marker=o', 'linestyle=None'); - plt.legend(); - plt.ylim(-100, 100); - - plt.save("./examples/subplot.png"); -}()); \ No newline at end of file