From dd2b42d2925db834f3ec9f5f8dd7e406a54128f5 Mon Sep 17 00:00:00 2001 From: Mateo Gianolio Date: Fri, 3 Apr 2020 10:46:14 +0200 Subject: [PATCH 1/3] Use matplotlib-cpp directly, add type conversion template, fixes and improvements --- binding.gyp | 26 +- examples/animation.js | 32 + examples/bar.js | 14 + examples/basic.js | 41 + examples/basic.png | Bin 0 -> 38229 bytes examples/fill.js | 31 + examples/fill_between.js | 26 + examples/imshow.js | 23 + examples/show.png | Bin 98241 -> 0 bytes examples/subplot.png | Bin 87305 -> 0 bytes lib/convert.h | 118 ++ lib/matplotlibcpp.h | 2211 ++++++++++++++++++++++++++++++++++++++ package-lock.json | 2 +- package.json | 2 +- src/matplotlib.cc | 533 ++++++--- src/matplotlib.h | 166 --- test.js | 21 - 17 files changed, 2918 insertions(+), 328 deletions(-) create mode 100644 examples/animation.js create mode 100644 examples/bar.js create mode 100644 examples/basic.js create mode 100644 examples/basic.png create mode 100644 examples/fill.js create mode 100644 examples/fill_between.js create mode 100644 examples/imshow.js delete mode 100644 examples/show.png delete mode 100644 examples/subplot.png create mode 100644 lib/convert.h create mode 100644 lib/matplotlibcpp.h delete mode 100644 src/matplotlib.h delete mode 100644 test.js diff --git a/binding.gyp b/binding.gyp index bd15660..e7aabf3 100644 --- a/binding.gyp +++ b/binding.gyp @@ -3,25 +3,31 @@ { "target_name": "matplotlib", "sources": [ "src/matplotlib.cc" ], + "include_dirs": [ + "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 IBp*HhUxi5z_W%F@ literal 0 HcmV?d00001 diff --git a/examples/fill.js b/examples/fill.js new file mode 100644 index 0000000..b09e87d --- /dev/null +++ b/examples/fill.js @@ -0,0 +1,31 @@ +const plt = require('../build/Release/matplotlib'); + +// Prepare data. +const theta = []; +for (let d = 0; d < 8 * Math.PI; d += 0.1) { + theta.push(d); +} + +const a = 1; +const b = 0.2; + +for (let dt = 0; dt < 2 * Math.PI; dt += Math.PI / 2.0) { + const x1 = []; + const y1 = []; + const x2 = []; + const y2 = []; + + for (th of theta) { + x1.push(a * Math.cos(th + dt) * Math.exp(b * th)); + y1.push(a * Math.sin(th + dt) * Math.exp(b * th)); + + x2.push(a * Math.cos(th + dt + Math.PI / 4.0) * Math.exp(b * th)); + y2.push(a * Math.sin(th + dt + Math.PI / 4.0) * Math.exp(b * th)); + } + + plt.fill(x1.concat(x2.reverse()), y1.concat(y2.reverse()), {}); +} + +plt.show(); + +plt.pause(); diff --git a/examples/fill_between.js b/examples/fill_between.js new file mode 100644 index 0000000..58981f0 --- /dev/null +++ b/examples/fill_between.js @@ -0,0 +1,26 @@ +const plt = require('../build/Release/matplotlib'); + +// Prepare data. +const n = 5000; +const x = Array(5000); +const y = Array(5000); +const z = Array(5000); + +for (let i = 0; i < n; ++i) { + x[i] = i * i; + y[i] = Math.sin(2 * Math.PI * i / 360.0); + z[i] = Math.log(i); +} + +// Prepare keywords to pass to PolyCollection. See +// https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.fill_between.html +const keywords = { + // alpha: '0.4', + color: 'grey', + hatch: '-', +}; + +plt.fill_between(x, y, z, keywords); +plt.show(); + +plt.pause(); diff --git a/examples/imshow.js b/examples/imshow.js new file mode 100644 index 0000000..b0ca55a --- /dev/null +++ b/examples/imshow.js @@ -0,0 +1,23 @@ +const plt = require('../build/Release/matplotlib'); + +// Prepare data. +const ncols = 500; +const nrows = 300; +const z = new Float32Array(ncols * nrows); + +for (let j = 0; j < nrows; ++j) { + for (let i = 0; i < ncols; ++i) { + z[ncols * j + i] = Math.sin(Math.hypot(i - ncols / 2, j - nrows / 2)); + } +} + +const colors = 1; +const zptr = z.buffer; + +plt.title('My matrix'); +plt.imshow(zptr, 11, nrows, ncols, colors); + +// Show plots +plt.save('./imshow.png'); + +console.log('Result saved to "imshow.png"'); diff --git a/examples/show.png b/examples/show.png deleted file mode 100644 index 2a1dbea7f9554a0f46b660b09c3a730fb848653f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98241 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%0AHH`= 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 Jv6^-8{{x2jsq_E< diff --git a/lib/convert.h b/lib/convert.h new file mode 100644 index 0000000..319be53 --- /dev/null +++ b/lib/convert.h @@ -0,0 +1,118 @@ +#pragma once + +#include + +template +struct convert; + +// Number => float | double +template +struct convert::value>::type> { + static T from(v8::Isolate* isolate, v8::Local value, T default_value = 0.0) { + if (value.IsEmpty() || !value->IsNumber()) { + return default_value; + } + + return value.As()->Value(); + } +}; + +// Number => int32_t | uint32_t | int64_t +template +struct convert::value>::type> { + enum { + bits = sizeof(T) * CHAR_BIT, + is_signed = std::is_signed::value + }; + + static T from(v8::Isolate* isolate, v8::Local value, T default_value = 0) { + if (value.IsEmpty() || !value->IsNumber()) { + return default_value; + } + + if (bits <= 32) { + if (is_signed) { + return value.As()->Value(); + } else { + return value.As()->Value(); + } + } + + return value.As()->Value(); + } +}; + +// Boolean => bool +template<> +struct convert { + static bool from(v8::Isolate* isolate, v8::Local value, bool default_value = false) { + if (value.IsEmpty() || !value->IsBoolean()) { + return default_value; + } + + return value.As()->Value(); + } +}; + +// String => std::string +template<> +struct convert { + static std::string from(v8::Isolate* isolate, v8::Local value, std::string default_value = "") { + if (value.IsEmpty() || !value->IsString()) { + return default_value; + } + + v8::String::Utf8Value str(isolate, value.As()); + + return std::string(*str, str.length()); + } +}; + +// Array => std::vector +template +struct convert> { + static std::vector from(v8::Isolate* isolate, v8::Local value, std::vector default_value = std::vector()) { + if (value.IsEmpty() || !value->IsArray()) { + return default_value; + } + + v8::HandleScope scope(isolate); + v8::Local context = isolate->GetCurrentContext(); + v8::Local array = value.As(); + + std::vector output; + + output.reserve(array->Length()); + for (uint32_t i = 0, count = array->Length(); i < count; ++i) { + output.emplace_back(convert::from(isolate, array->Get(context, i).ToLocalChecked())); + } + + return output; + } +}; + +// Object => std::map +template +struct convert> { + static std::map from(v8::Isolate* isolate, v8::Local value, std::map default_value = std::map()) { + if (value.IsEmpty() || !value->IsObject()) { + return default_value; + } + + v8::HandleScope scope(isolate); + v8::Local context = isolate->GetCurrentContext(); + v8::Local object = value.As(); + v8::Local keys = object->GetPropertyNames(context).ToLocalChecked(); + + std::map output; + + for (uint32_t i = 0, count = keys->Length(); i < count; ++i) { + v8::Local key = keys->Get(context, i).ToLocalChecked(); + v8::Local value = object->Get(context, key).ToLocalChecked(); + + output.emplace(convert::from(isolate, key), convert::from(isolate, value)); + } + + return output; + } +}; \ No newline at end of file diff --git a/lib/matplotlibcpp.h b/lib/matplotlibcpp.h new file mode 100644 index 0000000..928ca05 --- /dev/null +++ b/lib/matplotlibcpp.h @@ -0,0 +1,2211 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include // requires c++11 support +#include + +#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 = PyObject_GetAttrString(pymod, "colorbar"); + s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust"); +#ifndef WITHOUT_NUMPY + s_python_function_imshow = safe_import(pymod, "imshow"); +#endif + s_python_empty_tuple = PyTuple_New(0); + } + + ~_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* plot_args = PyTuple_New(0); + + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs); + + Py_DECREF(plot_args); + Py_DECREF(kwargs); + if(res) Py_DECREF(res); + + return res; +} + +template< typename Numeric> +bool named_hist(std::string label,const std::vector& y, long bins=10, std::string color="b", double alpha=1.0) +{ + 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* args = PyTuple_New(0); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); + PyObject* left = PyTuple_GetItem(res,0); + PyObject* right = PyTuple_GetItem(res,1); + + double* arr = new double[2]; + arr[0] = PyFloat_AsDouble(left); + arr[1] = PyFloat_AsDouble(right); + + if(!res) throw std::runtime_error("Call to xlim() failed."); + + Py_DECREF(res); + return arr; +} + + +inline double* ylim() +{ + PyObject* args = PyTuple_New(0); + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); + PyObject* left = PyTuple_GetItem(res,0); + PyObject* right = PyTuple_GetItem(res,1); + + double* arr = new double[2]; + arr[0] = PyFloat_AsDouble(left); + arr[1] = PyFloat_AsDouble(right); + + if(!res) throw std::runtime_error("Call to ylim() failed."); + + Py_DECREF(res); + return arr; +} + +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, PyFloat_FromDouble(nrows)); + PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); + PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); + + PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args); + if(!res) throw std::runtime_error("Call to subplot() failed."); + + Py_DECREF(args); + Py_DECREF(res); +} + +inline void 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* args = PyTuple_New(0); + PyObject* res = PyObject_CallObject(remove_fct, args); + if (res) Py_DECREF(res); + } + decref(); + } + + ~Plot() { + decref(); + } +private: + + void decref() { + if(line) + Py_DECREF(line); + if(set_data_fct) + Py_DECREF(set_data_fct); + } + + + PyObject* line = nullptr; + PyObject* set_data_fct = nullptr; +}; + +} // end namespace matplotlibcpp diff --git a/package-lock.json b/package-lock.json index 00e0b02..306d18a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { "name": "matplotnode", - "version": "0.4.1", + "version": "1.0.0", "lockfileVersion": 1 } diff --git a/package.json b/package.json index 3842b16..545d486 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "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", "scripts": { diff --git a/src/matplotlib.cc b/src/matplotlib.cc index 4434ddf..51ed658 100644 --- a/src/matplotlib.cc +++ b/src/matplotlib.cc @@ -1,213 +1,488 @@ -#include "matplotlib.h" +#include +#include "matplotlibcpp.h" +#include "convert.h" + +namespace matplotnode { + void backend(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + auto name = convert::from(isolate, info[0]); + + matplotlibcpp::backend(name); + } + + void annotate(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + auto annotation = convert::from(isolate, info[0]); + auto x = convert::from(isolate, info[1]); + auto y = convert::from(isolate, info[2]); + + matplotlibcpp::annotate(annotation, x, y); + } -namespace plt { void plot(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - PyObject *args, *kwargs, *data, *result; - uint32_t arglen = info.Length(); - uint32_t datalen; + auto x = convert>::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto keywords = convert>::from(isolate, info[2]); - args = PyTuple_New(2); - kwargs = PyDict_New(); + matplotlibcpp::plot(x, y, keywords); + } - 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 plot3(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - 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); + auto x = convert>::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto z = convert>::from(isolate, info[2]); + auto keywords = convert>::from(isolate, info[3]); - 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); + matplotlibcpp::plot3(x, y, z, keywords); + } - 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); - } - } + void stem(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - result = PyObject_Call(interpreter::get().plot, args, kwargs); + auto x = convert>::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto keywords = convert>::from(isolate, info[2]); - Py_DECREF(args); - Py_DECREF(kwargs); - Py_XDECREF(result); + matplotlibcpp::stem(x, y, keywords); } - void subplot(const v8::FunctionCallbackInfo& info) { + void fill(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - 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())); + auto x = convert>::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto keywords = convert>::from(isolate, info[2]); + + matplotlibcpp::fill(x, y, keywords); + } + + void fill_between(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - PyObject *result = PyObject_CallObject(interpreter::get().subplot, args); + auto x = convert>::from(isolate, info[0]); + auto y1 = convert>::from(isolate, info[1]); + auto y2 = convert>::from(isolate, info[2]); + auto keywords = convert>::from(isolate, info[3]); - Py_DECREF(args); - Py_XDECREF(result); + matplotlibcpp::fill_between(x, y1, y2, keywords); } - void show(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().show, interpreter::get().empty_tuple); - Py_XDECREF(result); + void hist(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto y = convert>::from(isolate, info[0]); + auto bins = convert::from(isolate, info[1], 10); + auto color = convert::from(isolate, info[2], "b"); + auto alpha = convert::from(isolate, info[3], 1.0); + auto cumulative = convert::from(isolate, info[4], false); + + matplotlibcpp::hist(y, bins, color, alpha, cumulative); } - void legend(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().legend, interpreter::get().empty_tuple); - Py_XDECREF(result); + void scatter(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto x = convert>::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto s = convert::from(isolate, info[2]); + auto keywords = convert>::from(isolate, info[3]); + + matplotlibcpp::scatter(x, y, s, keywords); } - 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 boxplot(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto data = convert>::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); - PyObject *result = PyObject_CallObject(interpreter::get().grid, args); - - Py_DECREF(args); - Py_XDECREF(result); + matplotlibcpp::boxplot(data, keywords); } - void save(const v8::FunctionCallbackInfo& info) { + void bar(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto x = convert>::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto ec = convert::from(isolate, info[2], "black"); + auto ls = convert::from(isolate, info[3], "-"); + auto lw = convert::from(isolate, info[4]); + auto keywords = convert>::from(isolate, info[5]); + + matplotlibcpp::bar(x, y, ec, ls, lw, keywords); + } + + void subplots_adjust(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - 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())); + auto keywords = convert>::from(isolate, info[0]); + + matplotlibcpp::subplots_adjust(keywords); + } + + void named_hist(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - PyObject *result = PyObject_CallObject(interpreter::get().save, args); + auto label = convert::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto bins = convert::from(isolate, info[2], 10); + auto color = convert::from(isolate, info[3], "b"); + auto alpha = convert::from(isolate, info[4], 1.0); - Py_DECREF(args); - Py_XDECREF(result); + matplotlibcpp::named_hist(label, y, bins, color, alpha); } - void xlim(const v8::FunctionCallbackInfo& info) { - PyObject *list = PyList_New(2); - PyObject *args = PyTuple_New(1); + void quiver(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - 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); + auto x = convert>::from(isolate, info[0]); + auto y = convert>::from(isolate, info[1]); + auto u = convert>::from(isolate, info[2]); + auto w = convert>::from(isolate, info[3]); + auto keywords = convert>::from(isolate, info[4]); - PyObject *result = PyObject_CallObject(interpreter::get().xlim, args); + matplotlibcpp::quiver(x, y, u, w, keywords); + } - Py_DECREF(args); - Py_XDECREF(result); + void errorbar(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto x = convert>::from(isolate, info[0]); + auto y1 = convert>::from(isolate, info[1]); + auto yerr = convert>::from(isolate, info[2]); + auto keywords = convert>::from(isolate, info[3]); + + matplotlibcpp::errorbar(x, y1, yerr, keywords); + } + + void named_plot(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto name = convert::from(isolate, info[0]); + auto x = convert>::from(isolate, info[1]); + auto y = convert>::from(isolate, info[2]); + auto format = convert::from(isolate, info[3]); + + matplotlibcpp::named_plot(name, x, y, format); + } + + void named_semilogx(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto name = convert::from(isolate, info[0]); + auto x = convert>::from(isolate, info[1]); + auto y = convert>::from(isolate, info[2]); + auto format = convert::from(isolate, info[3]); + + matplotlibcpp::named_semilogx(name, x, y, format); + } + + void named_semilogy(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto name = convert::from(isolate, info[0]); + auto x = convert>::from(isolate, info[1]); + auto y = convert>::from(isolate, info[2]); + auto format = convert::from(isolate, info[3]); + + matplotlibcpp::named_semilogy(name, x, y, format); + } + + void named_loglog(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto name = convert::from(isolate, info[0]); + auto x = convert>::from(isolate, info[1]); + auto y = convert>::from(isolate, info[2]); + auto format = convert::from(isolate, info[3]); + + matplotlibcpp::named_loglog(name, x, y, format); + } + + void text(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + + auto x = convert::from(isolate, info[0]); + auto y = convert::from(isolate, info[1]); + auto s = convert::from(isolate, info[2]); + + matplotlibcpp::text(x, y, s); + } + + void figure(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto number = convert::from(isolate, info[0]); + + matplotlibcpp::figure(number); + } + + void figure_size(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto w = convert::from(isolate, info[0]); + auto h = convert::from(isolate, info[1]); + + matplotlibcpp::figure_size(w, h); + } + + void legend(const v8::FunctionCallbackInfo& info) { + matplotlibcpp::legend(); } - + void ylim(const v8::FunctionCallbackInfo& info) { - PyObject *list = PyList_New(2); - PyObject *args = PyTuple_New(1); + v8::Isolate* isolate = info.GetIsolate(); + + auto left = convert::from(isolate, info[0]); + auto right = convert::from(isolate, info[1]); + + matplotlibcpp::ylim(left, right); + } + + void xlim(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto left = convert::from(isolate, info[0]); + auto right = convert::from(isolate, info[1]); + + matplotlibcpp::xlim(left, right); + } + + void xticks(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto ticks = convert>::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); + + matplotlibcpp::xticks(ticks, keywords); + } + + void yticks(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto ticks = convert>::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); + + matplotlibcpp::yticks(ticks, keywords); + } + + void tick_params(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto keywords = convert>::from(isolate, info[0]); + auto axis = convert::from(isolate, info[1]); + + matplotlibcpp::tick_params(keywords, axis); + } + + void subplot(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto nrows = convert::from(isolate, info[0]); + auto ncols = convert::from(isolate, info[1]); + auto plot_number = convert::from(isolate, info[2]); - 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); + matplotlibcpp::subplot(nrows, ncols, plot_number); + } + + void subplot2grid(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - PyObject *result = PyObject_CallObject(interpreter::get().ylim, args); - - Py_DECREF(args); - Py_XDECREF(result); + auto nrows = convert::from(isolate, info[0]); + auto ncols = convert::from(isolate, info[1]); + auto rowid = convert::from(isolate, info[2]); + auto colid = convert::from(isolate, info[3]); + auto rowspan = convert::from(isolate, info[4]); + auto colspan = convert::from(isolate, info[5]); + + matplotlibcpp::subplot2grid(nrows, ncols, rowid, colid, rowspan, colspan); } void title(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - 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())); + auto titlestr = convert::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); + + matplotlibcpp::title(titlestr, keywords); + } + + void suptitle(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - PyObject *result = PyObject_CallObject(interpreter::get().title, args); + auto suptitlestr = convert::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); - Py_DECREF(args); - Py_XDECREF(result); + matplotlibcpp::suptitle(suptitlestr, keywords); } void axis(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - 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())); + auto axisstr = convert::from(isolate, info[0]); + + matplotlibcpp::axis(axisstr); + } + + void axvline(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - PyObject *result = PyObject_CallObject(interpreter::get().axis, args); + auto x = convert::from(isolate, info[0]); + auto ymin = convert::from(isolate, info[1]); + auto ymax = convert::from(isolate, info[2]); + auto keywords = convert>::from(isolate, info[3]); - Py_DECREF(args); - Py_XDECREF(result); + matplotlibcpp::axvline(x, ymin, ymax, keywords); } void xlabel(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - 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())); - - PyObject *result = PyObject_CallObject(interpreter::get().xlabel, args); + auto str = convert::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); - Py_DECREF(args); - Py_XDECREF(result); + matplotlibcpp::xlabel(str, keywords); } void ylabel(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - 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())); + auto str = convert::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); + + matplotlibcpp::xlabel(str, keywords); + } + + void set_zlabel(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); - PyObject *result = PyObject_CallObject(interpreter::get().ylabel, args); + auto str = convert::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); - Py_DECREF(args); - Py_XDECREF(result); + matplotlibcpp::set_zlabel(str, keywords); } - void clf(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().clf, interpreter::get().empty_tuple); - Py_XDECREF(result); + void grid(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto flag = convert::from(isolate, info[0]); + + matplotlibcpp::grid(flag); } - void cla(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().cla, interpreter::get().empty_tuple); - Py_XDECREF(result); + void show(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto block = convert::from(isolate, info[0]); + + matplotlibcpp::show(block); } void close(const v8::FunctionCallbackInfo& info) { - PyObject *result = PyObject_CallObject(interpreter::get().close, interpreter::get().empty_tuple); - Py_XDECREF(result); + matplotlibcpp::close(); } void xkcd(const v8::FunctionCallbackInfo& info) { - PyObject *kwargs = PyDict_New(); - PyObject *result = PyObject_Call(interpreter::get().xkcd, interpreter::get().empty_tuple, kwargs); + matplotlibcpp::xkcd(); + } + + void draw(const v8::FunctionCallbackInfo& info) { + matplotlibcpp::draw(); + } + + void pause(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto interval = convert::from(isolate, info[0]); + + matplotlibcpp::pause(interval); + } + + void save(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto filename = convert::from(isolate, info[0]); + + matplotlibcpp::save(filename); + } + + void clf(const v8::FunctionCallbackInfo& info) { + matplotlibcpp::clf(); + } + + void ion(const v8::FunctionCallbackInfo& info) { + matplotlibcpp::ion(); + } + + void ginput(const v8::FunctionCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + auto numClicks = convert::from(isolate, info[0]); + auto keywords = convert>::from(isolate, info[1]); + + matplotlibcpp::ginput(numClicks, keywords); + } - Py_DECREF(kwargs); - Py_DECREF(result); + void tight_layout(const v8::FunctionCallbackInfo& info) { + matplotlibcpp::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); + NODE_SET_METHOD(exports, "backend", matplotnode::backend); + NODE_SET_METHOD(exports, "annotate", matplotnode::annotate); + NODE_SET_METHOD(exports, "plot", matplotnode::plot); + NODE_SET_METHOD(exports, "plot3", matplotnode::plot3); + NODE_SET_METHOD(exports, "stem", matplotnode::stem); + NODE_SET_METHOD(exports, "fill", matplotnode::fill); + NODE_SET_METHOD(exports, "fill_between", matplotnode::fill_between); + NODE_SET_METHOD(exports, "hist", matplotnode::hist); + NODE_SET_METHOD(exports, "scatter", matplotnode::scatter); + NODE_SET_METHOD(exports, "boxplot", matplotnode::boxplot); + NODE_SET_METHOD(exports, "bar", matplotnode::bar); + NODE_SET_METHOD(exports, "subplots_adjust", matplotnode::subplots_adjust); + NODE_SET_METHOD(exports, "named_hist", matplotnode::named_hist); + NODE_SET_METHOD(exports, "quiver", matplotnode::quiver); + NODE_SET_METHOD(exports, "errorbar", matplotnode::errorbar); + NODE_SET_METHOD(exports, "named_plot", matplotnode::named_plot); + NODE_SET_METHOD(exports, "named_semilogx", matplotnode::named_semilogx); + NODE_SET_METHOD(exports, "named_semilogy", matplotnode::named_semilogy); + NODE_SET_METHOD(exports, "named_loglog", matplotnode::named_loglog); + NODE_SET_METHOD(exports, "text", matplotnode::text); + NODE_SET_METHOD(exports, "figure", matplotnode::figure); + NODE_SET_METHOD(exports, "figure_size", matplotnode::figure_size); + NODE_SET_METHOD(exports, "legend", matplotnode::legend); + NODE_SET_METHOD(exports, "ylim", matplotnode::ylim); + NODE_SET_METHOD(exports, "xlim", matplotnode::xlim); + NODE_SET_METHOD(exports, "xticks", matplotnode::xticks); + NODE_SET_METHOD(exports, "yticks", matplotnode::yticks); + NODE_SET_METHOD(exports, "tick_params", matplotnode::tick_params); + NODE_SET_METHOD(exports, "subplot", matplotnode::subplot); + NODE_SET_METHOD(exports, "subplot2grid", matplotnode::subplot2grid); + NODE_SET_METHOD(exports, "title", matplotnode::title); + NODE_SET_METHOD(exports, "suptitle", matplotnode::suptitle); + NODE_SET_METHOD(exports, "axis", matplotnode::axis); + NODE_SET_METHOD(exports, "axvline", matplotnode::axvline); + NODE_SET_METHOD(exports, "xlabel", matplotnode::xlabel); + NODE_SET_METHOD(exports, "ylabel", matplotnode::ylabel); + NODE_SET_METHOD(exports, "set_zlabel", matplotnode::set_zlabel); + NODE_SET_METHOD(exports, "grid", matplotnode::grid); + NODE_SET_METHOD(exports, "show", matplotnode::show); + NODE_SET_METHOD(exports, "close", matplotnode::close); + NODE_SET_METHOD(exports, "xkcd", matplotnode::xkcd); + NODE_SET_METHOD(exports, "draw", matplotnode::draw); + NODE_SET_METHOD(exports, "pause", matplotnode::pause); + NODE_SET_METHOD(exports, "save", matplotnode::save); + NODE_SET_METHOD(exports, "clf", matplotnode::clf); + NODE_SET_METHOD(exports, "ion", matplotnode::ion); + NODE_SET_METHOD(exports, "ginput", matplotnode::ginput); + NODE_SET_METHOD(exports, "tight_layout", matplotnode::tight_layout); } NODE_MODULE(matplotlib, init) \ No newline at end of file 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/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 From debe2d48f664d18ea8dae9ffa7cbb36d3f2867c6 Mon Sep 17 00:00:00 2001 From: Mateo Gianolio Date: Fri, 3 Apr 2020 17:27:52 +0200 Subject: [PATCH 2/3] Add all examples --- basic.png | Bin 0 -> 38229 bytes examples/animation.js | 5 +- examples/fill_between.js | 2 + examples/imshow.js | 23 ---- examples/lines3d.js | 31 ++++++ examples/minimal.js | 5 + examples/quiver.js | 21 ++++ examples/subplot.js | 36 +++++++ examples/subplot2grid.js | 60 +++++++++++ examples/xkcd.js | 16 +++ lib/convert.h | 21 ++-- lib/matplotlibcpp.h | 6 +- src/matplotlib.cc | 226 +++++++++++++++++++-------------------- 13 files changed, 303 insertions(+), 149 deletions(-) create mode 100644 basic.png delete mode 100644 examples/imshow.js create mode 100644 examples/lines3d.js create mode 100644 examples/minimal.js create mode 100644 examples/quiver.js create mode 100644 examples/subplot.js create mode 100644 examples/subplot2grid.js create mode 100644 examples/xkcd.js diff --git a/basic.png b/basic.png new file mode 100644 index 0000000000000000000000000000000000000000..8855da8bd24248cd1d6d5f581d8928cb89152059 GIT binary patch literal 38229 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 IBp*HhUxi5z_W%F@ literal 0 HcmV?d00001 diff --git a/examples/animation.js b/examples/animation.js index 39281c4..13a3a5b 100644 --- a/examples/animation.js +++ b/examples/animation.js @@ -1,11 +1,11 @@ -const plt = require('./build/Release/matplotlib'); +const plt = require('../build/Release/matplotlib'); const n = 1000; const x = []; const y = []; const z = []; -for(let i = 0; i < n; i++) { +for (let i = 0; i < n; i++) { x.push(i * i); y.push(Math.sin(2 * Math.PI * i / 360.0)); z.push(Math.log(i)); @@ -19,7 +19,6 @@ for(let i = 0; i < n; i++) { plt.named_plot('log(x)', x, z); // Set x-axis to interval [0,1000000] - plt.ylim(0, 10); plt.xlim(0, n*n); // Add graph title diff --git a/examples/fill_between.js b/examples/fill_between.js index 58981f0..6136623 100644 --- a/examples/fill_between.js +++ b/examples/fill_between.js @@ -12,6 +12,8 @@ for (let i = 0; i < n; ++i) { z[i] = Math.log(i); } +// TODO: find out why it crashes if uncommenting alpha: '0.4'? + // Prepare keywords to pass to PolyCollection. See // https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.fill_between.html const keywords = { diff --git a/examples/imshow.js b/examples/imshow.js deleted file mode 100644 index b0ca55a..0000000 --- a/examples/imshow.js +++ /dev/null @@ -1,23 +0,0 @@ -const plt = require('../build/Release/matplotlib'); - -// Prepare data. -const ncols = 500; -const nrows = 300; -const z = new Float32Array(ncols * nrows); - -for (let j = 0; j < nrows; ++j) { - for (let i = 0; i < ncols; ++i) { - z[ncols * j + i] = Math.sin(Math.hypot(i - ncols / 2, j - nrows / 2)); - } -} - -const colors = 1; -const zptr = z.buffer; - -plt.title('My matrix'); -plt.imshow(zptr, 11, nrows, ncols, colors); - -// Show plots -plt.save('./imshow.png'); - -console.log('Result saved to "imshow.png"'); diff --git a/examples/lines3d.js b/examples/lines3d.js new file mode 100644 index 0000000..fef64e0 --- /dev/null +++ b/examples/lines3d.js @@ -0,0 +1,31 @@ +const plt = require('../build/Release/matplotlib'); + +const x = []; +const y = []; +const z = []; + +let z_inc = 4.0 / 99.0; +let theta_inc = (8.0 * Math.PI) / 99.0; + +for (let i = 0; i < 100; i++) { + const theta = -4.0 * Math.PI + theta_inc * i; + const r = z[i] * z[i] + 1; + z.push(-2.0 + z_inc * i); + x.push(r * Math.sin(theta)); + y.push(r * Math.cos(theta)); +} + +const keywords = { + label: 'parametric curve', +}; + +// TODO: does not look correct... memory problem? conversion problem? + +plt.plot3(x, y, z, keywords); +plt.xlabel('x label'); +plt.ylabel('y label'); +plt.set_zlabel('z label'); // set_zlabel rather than just zlabel, in accordance with the Axes3D method +plt.legend(); +plt.show(); + +plt.pause(); diff --git a/examples/minimal.js b/examples/minimal.js new file mode 100644 index 0000000..076d0c7 --- /dev/null +++ b/examples/minimal.js @@ -0,0 +1,5 @@ +const plt = require('../build/Release/matplotlib'); + +plt.plot([0, 1, 2, 3], [1, 3, 2, 4]); +plt.show(); +plt.pause(); diff --git a/examples/quiver.js b/examples/quiver.js new file mode 100644 index 0000000..bf85ee2 --- /dev/null +++ b/examples/quiver.js @@ -0,0 +1,21 @@ +const plt = require('../build/Release/matplotlib'); + +// u and v are respectively the x and y components of the arrows we're plotting +const x = []; +const y = []; +const u = []; +const v = []; + +for (let i = -5; i <= 5; i++) { + for (let j = -5; j <= 5; j++) { + x.push(i); + u.push(-i); + y.push(j); + v.push(-j); + } +} + +plt.quiver(x, y, u, v); +plt.show(); + +plt.pause(); diff --git a/examples/subplot.js b/examples/subplot.js new file mode 100644 index 0000000..12e3737 --- /dev/null +++ b/examples/subplot.js @@ -0,0 +1,36 @@ +const plt = require('../build/Release/matplotlib'); + +// Prepare data +const n = 500; +const x = Array(n); +const y = Array(n) +const z = Array(n) + +for(let i = 0; i < n; ++i) { + x[i] = i; + y[i] = Math.sin(2 * Math.PI * i / 360.0); + z[i] = 100.0 / i; +} + +// TODO: fix segfault + +// Set the 'super title' +plt.suptitle('My plot'); +plt.subplot(1, 2, 1); +plt.plot(x, y, { + color: 'red', + linestyle: '-', +}); +plt.subplot(1, 2, 2); +plt.plot(x, z, { + color: 'black', + linestyle: '-', +}); +// Add some text to the plot +plt.text(100, 90, 'Hello!'); + + +// Show plots +plt.show(); + +plt.pause(); diff --git a/examples/subplot2grid.js b/examples/subplot2grid.js new file mode 100644 index 0000000..8d5c40b --- /dev/null +++ b/examples/subplot2grid.js @@ -0,0 +1,60 @@ +const plt = require('../build/Release/matplotlib'); + +// Prepare data +const n = 500; +const x = Array(500); +const u = Array(500); +const v = Array(500); +const w = Array(500); + +for (let i = 0; i < n; ++i) { + x[i] = i; + u[i] = Math.sin(2 * Math.PI * i / 500.0); + v[i] = 100.0 / i; + w[i] = Math.sin(2 * Math.PI * i / 1000.0); +} + +// TODO: fix segfault + +// Set the "super title" +plt.suptitle("My plot"); + +const nrows = 3; +const ncols = 3; +let row = 2; +let col = 2; + +plt.subplot2grid(nrows, ncols, row, col); +plt.plot(x, w, { + color: 'green', + linestyle: '-', +}); + +let spanr = 1; +let spanc = 2; +col = 0; + +plt.subplot2grid(nrows, ncols, row, col, spanr, spanc); +plt.plot(x, v, { + color: 'red', + linestyle: '-', +}); + +spanr = 2; +spanc = 3; +row = 0; +col = 0; + +plt.subplot2grid(nrows, ncols, row, col, spanr, spanc); +plt.plot(x, u, { + color: 'blue', + linestyle: '-', +}); + +// Add some text to the plot +plt.text(100., -0.5, "Hello!"); + +// Show plots +plt.show(); + +plt.pause(); diff --git a/examples/xkcd.js b/examples/xkcd.js new file mode 100644 index 0000000..eda806c --- /dev/null +++ b/examples/xkcd.js @@ -0,0 +1,16 @@ +const plt = require('../build/Release/matplotlib'); + +const t = Array(1000); +const x = Array(t.length); + +for (let i = 0; i < t.length; i++) { + t[i] = i / 100.0; + x[i] = Math.sin(2.0 * Math.PI * 1.0 * t[i]); +} + +plt.xkcd(); +plt.plot(t, x); +plt.title("AN ORDINARY SIN WAVE"); +plt.show(); + +plt.pause(); diff --git a/lib/convert.h b/lib/convert.h index 319be53..95e9ab1 100644 --- a/lib/convert.h +++ b/lib/convert.h @@ -30,6 +30,9 @@ struct convert::value>::type> { return default_value; } + printf("bits: %d\n", bits); + printf("is_signed: %d\n", is_signed); + if (bits <= 32) { if (is_signed) { return value.As()->Value(); @@ -62,17 +65,15 @@ struct convert { return default_value; } - v8::String::Utf8Value str(isolate, value.As()); - - return std::string(*str, str.length()); + return std::string(*v8::String::Utf8Value(isolate, value.As())); } }; // Array => std::vector template struct convert> { - static std::vector from(v8::Isolate* isolate, v8::Local value, std::vector default_value = std::vector()) { - if (value.IsEmpty() || !value->IsArray()) { + static std::vector from(v8::Isolate* isolate, v8::Local value, std::vector default_value = {}) { + if (value.IsEmpty() || (!value->IsArray() && !value->IsTypedArray())) { return default_value; } @@ -94,7 +95,7 @@ struct convert> { // Object => std::map template struct convert> { - static std::map from(v8::Isolate* isolate, v8::Local value, std::map default_value = std::map()) { + static std::map from(v8::Isolate* isolate, v8::Local value, std::map default_value = {}) { if (value.IsEmpty() || !value->IsObject()) { return default_value; } @@ -115,4 +116,10 @@ struct convert> { return output; } -}; \ No newline at end of file +}; + +template +auto from(v8::Isolate* isolate, v8::Local value) + -> decltype(convert::from(isolate, value)) { + return convert::from(isolate, value); +} diff --git a/lib/matplotlibcpp.h b/lib/matplotlibcpp.h index 928ca05..8d70766 100644 --- a/lib/matplotlibcpp.h +++ b/lib/matplotlibcpp.h @@ -1612,9 +1612,9 @@ inline void subplot(long nrows, long ncols, long plot_number) { // construct positional args PyObject* args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows)); - PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols)); - PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number)); + 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."); diff --git a/src/matplotlib.cc b/src/matplotlib.cc index 51ed658..4b02e13 100644 --- a/src/matplotlib.cc +++ b/src/matplotlib.cc @@ -6,7 +6,7 @@ namespace matplotnode { void backend(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); - auto name = convert::from(isolate, info[0]); + auto name = from(isolate, info[0]); matplotlibcpp::backend(name); } @@ -14,9 +14,9 @@ namespace matplotnode { void annotate(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); - auto annotation = convert::from(isolate, info[0]); - auto x = convert::from(isolate, info[1]); - auto y = convert::from(isolate, info[2]); + auto annotation = from(isolate, info[0]); + auto x = from(isolate, info[1]); + auto y = from(isolate, info[2]); matplotlibcpp::annotate(annotation, x, y); } @@ -24,9 +24,9 @@ namespace matplotnode { void plot(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto keywords = convert>::from(isolate, info[2]); + auto x = from>(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto keywords = from>(isolate, info[2]); matplotlibcpp::plot(x, y, keywords); } @@ -34,10 +34,10 @@ namespace matplotnode { void plot3(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto z = convert>::from(isolate, info[2]); - auto keywords = convert>::from(isolate, info[3]); + auto x = from>(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto z = from>(isolate, info[2]); + auto keywords = from>(isolate, info[3]); matplotlibcpp::plot3(x, y, z, keywords); } @@ -45,9 +45,9 @@ namespace matplotnode { void stem(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto keywords = convert>::from(isolate, info[2]); + auto x = from>(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto keywords = from>(isolate, info[2]); matplotlibcpp::stem(x, y, keywords); } @@ -55,9 +55,9 @@ namespace matplotnode { void fill(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto keywords = convert>::from(isolate, info[2]); + auto x = from>(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto keywords = from>(isolate, info[2]); matplotlibcpp::fill(x, y, keywords); } @@ -65,10 +65,10 @@ namespace matplotnode { void fill_between(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y1 = convert>::from(isolate, info[1]); - auto y2 = convert>::from(isolate, info[2]); - auto keywords = convert>::from(isolate, info[3]); + auto x = from>(isolate, info[0]); + auto y1 = from>(isolate, info[1]); + auto y2 = from>(isolate, info[2]); + auto keywords = from>(isolate, info[3]); matplotlibcpp::fill_between(x, y1, y2, keywords); } @@ -76,11 +76,11 @@ namespace matplotnode { void hist(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto y = convert>::from(isolate, info[0]); - auto bins = convert::from(isolate, info[1], 10); - auto color = convert::from(isolate, info[2], "b"); - auto alpha = convert::from(isolate, info[3], 1.0); - auto cumulative = convert::from(isolate, info[4], false); + auto y = from>(isolate, info[0]); + auto bins = from(isolate, info[1]); + auto color = from(isolate, info[2]); + auto alpha = from(isolate, info[3]); + auto cumulative = from(isolate, info[4]); matplotlibcpp::hist(y, bins, color, alpha, cumulative); } @@ -88,10 +88,10 @@ namespace matplotnode { void scatter(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto s = convert::from(isolate, info[2]); - auto keywords = convert>::from(isolate, info[3]); + auto x = from>(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto s = from(isolate, info[2]); + auto keywords = from>(isolate, info[3]); matplotlibcpp::scatter(x, y, s, keywords); } @@ -99,8 +99,8 @@ namespace matplotnode { void boxplot(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto data = convert>::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto data = from>(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::boxplot(data, keywords); } @@ -108,12 +108,12 @@ namespace matplotnode { void bar(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto ec = convert::from(isolate, info[2], "black"); - auto ls = convert::from(isolate, info[3], "-"); - auto lw = convert::from(isolate, info[4]); - auto keywords = convert>::from(isolate, info[5]); + auto x = from>(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto ec = from(isolate, info[2]); + auto ls = from(isolate, info[3]); + auto lw = from(isolate, info[4]); + auto keywords = from>(isolate, info[5]); matplotlibcpp::bar(x, y, ec, ls, lw, keywords); } @@ -129,11 +129,11 @@ namespace matplotnode { void named_hist(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto label = convert::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto bins = convert::from(isolate, info[2], 10); - auto color = convert::from(isolate, info[3], "b"); - auto alpha = convert::from(isolate, info[4], 1.0); + auto label = from(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto bins = from(isolate, info[2]); + auto color = from(isolate, info[3]); + auto alpha = from(isolate, info[4]); matplotlibcpp::named_hist(label, y, bins, color, alpha); } @@ -141,11 +141,11 @@ namespace matplotnode { void quiver(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y = convert>::from(isolate, info[1]); - auto u = convert>::from(isolate, info[2]); - auto w = convert>::from(isolate, info[3]); - auto keywords = convert>::from(isolate, info[4]); + auto x = from>(isolate, info[0]); + auto y = from>(isolate, info[1]); + auto u = from>(isolate, info[2]); + auto w = from>(isolate, info[3]); + auto keywords = from>(isolate, info[4]); matplotlibcpp::quiver(x, y, u, w, keywords); } @@ -153,10 +153,10 @@ namespace matplotnode { void errorbar(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert>::from(isolate, info[0]); - auto y1 = convert>::from(isolate, info[1]); - auto yerr = convert>::from(isolate, info[2]); - auto keywords = convert>::from(isolate, info[3]); + auto x = from>(isolate, info[0]); + auto y1 = from>(isolate, info[1]); + auto yerr = from>(isolate, info[2]); + auto keywords = from>(isolate, info[3]); matplotlibcpp::errorbar(x, y1, yerr, keywords); } @@ -164,10 +164,10 @@ namespace matplotnode { void named_plot(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto name = convert::from(isolate, info[0]); - auto x = convert>::from(isolate, info[1]); - auto y = convert>::from(isolate, info[2]); - auto format = convert::from(isolate, info[3]); + auto name = from(isolate, info[0]); + auto x = from>(isolate, info[1]); + auto y = from>(isolate, info[2]); + auto format = from(isolate, info[3]); matplotlibcpp::named_plot(name, x, y, format); } @@ -175,10 +175,10 @@ namespace matplotnode { void named_semilogx(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto name = convert::from(isolate, info[0]); - auto x = convert>::from(isolate, info[1]); - auto y = convert>::from(isolate, info[2]); - auto format = convert::from(isolate, info[3]); + auto name = from(isolate, info[0]); + auto x = from>(isolate, info[1]); + auto y = from>(isolate, info[2]); + auto format = from(isolate, info[3]); matplotlibcpp::named_semilogx(name, x, y, format); } @@ -186,10 +186,10 @@ namespace matplotnode { void named_semilogy(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto name = convert::from(isolate, info[0]); - auto x = convert>::from(isolate, info[1]); - auto y = convert>::from(isolate, info[2]); - auto format = convert::from(isolate, info[3]); + auto name = from(isolate, info[0]); + auto x = from>(isolate, info[1]); + auto y = from>(isolate, info[2]); + auto format = from(isolate, info[3]); matplotlibcpp::named_semilogy(name, x, y, format); } @@ -197,10 +197,10 @@ namespace matplotnode { void named_loglog(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto name = convert::from(isolate, info[0]); - auto x = convert>::from(isolate, info[1]); - auto y = convert>::from(isolate, info[2]); - auto format = convert::from(isolate, info[3]); + auto name = from(isolate, info[0]); + auto x = from>(isolate, info[1]); + auto y = from>(isolate, info[2]); + auto format = from(isolate, info[3]); matplotlibcpp::named_loglog(name, x, y, format); } @@ -208,9 +208,9 @@ namespace matplotnode { void text(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); - auto x = convert::from(isolate, info[0]); - auto y = convert::from(isolate, info[1]); - auto s = convert::from(isolate, info[2]); + auto x = from(isolate, info[0]); + auto y = from(isolate, info[1]); + auto s = from(isolate, info[2]); matplotlibcpp::text(x, y, s); } @@ -218,7 +218,7 @@ namespace matplotnode { void figure(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto number = convert::from(isolate, info[0]); + auto number = from(isolate, info[0]); matplotlibcpp::figure(number); } @@ -239,8 +239,8 @@ namespace matplotnode { void ylim(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto left = convert::from(isolate, info[0]); - auto right = convert::from(isolate, info[1]); + auto left = from(isolate, info[0]); + auto right = from(isolate, info[1]); matplotlibcpp::ylim(left, right); } @@ -248,8 +248,8 @@ namespace matplotnode { void xlim(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto left = convert::from(isolate, info[0]); - auto right = convert::from(isolate, info[1]); + auto left = from(isolate, info[0]); + auto right = from(isolate, info[1]); matplotlibcpp::xlim(left, right); } @@ -257,8 +257,8 @@ namespace matplotnode { void xticks(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto ticks = convert>::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto ticks = from>(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::xticks(ticks, keywords); } @@ -266,8 +266,8 @@ namespace matplotnode { void yticks(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto ticks = convert>::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto ticks = from>(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::yticks(ticks, keywords); } @@ -275,8 +275,8 @@ namespace matplotnode { void tick_params(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto keywords = convert>::from(isolate, info[0]); - auto axis = convert::from(isolate, info[1]); + auto keywords = from>(isolate, info[0]); + auto axis = from(isolate, info[1]); matplotlibcpp::tick_params(keywords, axis); } @@ -284,9 +284,9 @@ namespace matplotnode { void subplot(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto nrows = convert::from(isolate, info[0]); - auto ncols = convert::from(isolate, info[1]); - auto plot_number = convert::from(isolate, info[2]); + auto nrows = from(isolate, info[0]); + auto ncols = from(isolate, info[1]); + auto plot_number = from(isolate, info[2]); matplotlibcpp::subplot(nrows, ncols, plot_number); } @@ -294,12 +294,12 @@ namespace matplotnode { void subplot2grid(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto nrows = convert::from(isolate, info[0]); - auto ncols = convert::from(isolate, info[1]); - auto rowid = convert::from(isolate, info[2]); - auto colid = convert::from(isolate, info[3]); - auto rowspan = convert::from(isolate, info[4]); - auto colspan = convert::from(isolate, info[5]); + auto nrows = from(isolate, info[0]); + auto ncols = from(isolate, info[1]); + auto rowid = from(isolate, info[2]); + auto colid = from(isolate, info[3]); + auto rowspan = from(isolate, info[4]); + auto colspan = from(isolate, info[5]); matplotlibcpp::subplot2grid(nrows, ncols, rowid, colid, rowspan, colspan); } @@ -307,8 +307,8 @@ namespace matplotnode { void title(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto titlestr = convert::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto titlestr = from(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::title(titlestr, keywords); } @@ -316,8 +316,8 @@ namespace matplotnode { void suptitle(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto suptitlestr = convert::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto suptitlestr = from(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::suptitle(suptitlestr, keywords); } @@ -325,7 +325,7 @@ namespace matplotnode { void axis(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto axisstr = convert::from(isolate, info[0]); + auto axisstr = from(isolate, info[0]); matplotlibcpp::axis(axisstr); } @@ -333,10 +333,10 @@ namespace matplotnode { void axvline(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto x = convert::from(isolate, info[0]); - auto ymin = convert::from(isolate, info[1]); - auto ymax = convert::from(isolate, info[2]); - auto keywords = convert>::from(isolate, info[3]); + auto x = from(isolate, info[0]); + auto ymin = from(isolate, info[1]); + auto ymax = from(isolate, info[2]); + auto keywords = from>(isolate, info[3]); matplotlibcpp::axvline(x, ymin, ymax, keywords); } @@ -344,8 +344,8 @@ namespace matplotnode { void xlabel(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto str = convert::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto str = from(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::xlabel(str, keywords); } @@ -353,17 +353,17 @@ namespace matplotnode { void ylabel(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto str = convert::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto str = from(isolate, info[0]); + auto keywords = from>(isolate, info[1]); - matplotlibcpp::xlabel(str, keywords); + matplotlibcpp::ylabel(str, keywords); } void set_zlabel(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto str = convert::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto str = from(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::set_zlabel(str, keywords); } @@ -371,7 +371,7 @@ namespace matplotnode { void grid(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto flag = convert::from(isolate, info[0]); + auto flag = from(isolate, info[0]); matplotlibcpp::grid(flag); } @@ -379,7 +379,7 @@ namespace matplotnode { void show(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto block = convert::from(isolate, info[0]); + auto block = from(isolate, info[0]); matplotlibcpp::show(block); } @@ -399,7 +399,7 @@ namespace matplotnode { void pause(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto interval = convert::from(isolate, info[0]); + auto interval = from(isolate, info[0]); matplotlibcpp::pause(interval); } @@ -407,7 +407,7 @@ namespace matplotnode { void save(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto filename = convert::from(isolate, info[0]); + auto filename = from(isolate, info[0]); matplotlibcpp::save(filename); } @@ -423,8 +423,8 @@ namespace matplotnode { void ginput(const v8::FunctionCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); - auto numClicks = convert::from(isolate, info[0]); - auto keywords = convert>::from(isolate, info[1]); + auto numClicks = from(isolate, info[0]); + auto keywords = from>(isolate, info[1]); matplotlibcpp::ginput(numClicks, keywords); } From 9fc5481becb3b530a1017da03f0a179b6c410fcd Mon Sep 17 00:00:00 2001 From: Mateo Gianolio Date: Sun, 5 Apr 2020 17:00:13 +0200 Subject: [PATCH 3/3] Use NAPI --- binding.gyp | 7 +- examples/bar.js | 2 - examples/fill.js | 2 - examples/fill_between.js | 2 - examples/lines3d.js | 16 +- examples/minimal.js | 2 +- examples/quiver.js | 2 - examples/subplot.js | 16 +- lib/convert.h | 139 +++++---- lib/matplotlibcpp.h | 27 +- package-lock.json | 10 +- package.json | 7 +- src/matplotlib.cc | 610 +++++++++++++++++---------------------- src/matplotlib.js | 4 - 14 files changed, 385 insertions(+), 461 deletions(-) delete mode 100644 src/matplotlib.js diff --git a/binding.gyp b/binding.gyp index e7aabf3..1e1eaba 100644 --- a/binding.gyp +++ b/binding.gyp @@ -5,11 +5,9 @@ "sources": [ "src/matplotlib.cc" ], "include_dirs": [ " +#include template -struct convert; +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 T from(v8::Isolate* isolate, v8::Local value, T default_value = 0.0) { - if (value.IsEmpty() || !value->IsNumber()) { - return default_value; + 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()->Value(); + return value.As(); } }; // Number => int32_t | uint32_t | int64_t template struct convert::value>::type> { - enum { - bits = sizeof(T) * CHAR_BIT, - is_signed = std::is_signed::value - }; - - static T from(v8::Isolate* isolate, v8::Local value, T default_value = 0) { - if (value.IsEmpty() || !value->IsNumber()) { - return default_value; + 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"); } - printf("bits: %d\n", bits); - printf("is_signed: %d\n", is_signed); + size_t bits = sizeof(T) * CHAR_BIT; + bool is_signed = std::is_signed::value; if (bits <= 32) { if (is_signed) { - return value.As()->Value(); + return value.As().Int32Value(); } else { - return value.As()->Value(); + return value.As().Uint32Value(); } } - return value.As()->Value(); + return value.As().Int64Value(); } }; // Boolean => bool template<> struct convert { - static bool from(v8::Isolate* isolate, v8::Local value, bool default_value = false) { - if (value.IsEmpty() || !value->IsBoolean()) { - return default_value; - } - - return value.As()->Value(); + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsBoolean(); } -}; -// String => std::string -template<> -struct convert { - static std::string from(v8::Isolate* isolate, v8::Local value, std::string default_value = "") { - if (value.IsEmpty() || !value->IsString()) { - return default_value; + static bool from(Napi::Value value) { + if (!valid(value)) { + throw std::invalid_argument("expected Boolean, got something else"); } - return std::string(*v8::String::Utf8Value(isolate, value.As())); + return value.As(); } }; // Array => std::vector template struct convert> { - static std::vector from(v8::Isolate* isolate, v8::Local value, std::vector default_value = {}) { - if (value.IsEmpty() || (!value->IsArray() && !value->IsTypedArray())) { - return default_value; - } + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsArray(); + } - v8::HandleScope scope(isolate); - v8::Local context = isolate->GetCurrentContext(); - v8::Local array = value.As(); + 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; - output.reserve(array->Length()); - for (uint32_t i = 0, count = array->Length(); i < count; ++i) { - output.emplace_back(convert::from(isolate, array->Get(context, i).ToLocalChecked())); + for (uint32_t i = 0, count = array.Length(); i < count; ++i) { + output.emplace_back(convert::from(array[i])); } return output; @@ -95,23 +109,24 @@ struct convert> { // Object => std::map template struct convert> { - static std::map from(v8::Isolate* isolate, v8::Local value, std::map default_value = {}) { - if (value.IsEmpty() || !value->IsObject()) { - return default_value; - } + static bool valid(Napi::Value value) { + return !value.IsEmpty() && value.IsObject(); + } - v8::HandleScope scope(isolate); - v8::Local context = isolate->GetCurrentContext(); - v8::Local object = value.As(); - v8::Local keys = object->GetPropertyNames(context).ToLocalChecked(); + 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) { - v8::Local key = keys->Get(context, i).ToLocalChecked(); - v8::Local value = object->Get(context, key).ToLocalChecked(); + 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(isolate, key), convert::from(isolate, value)); + output.emplace(convert::from(key), convert::from(value)); } return output; @@ -119,7 +134,15 @@ struct convert> { }; template -auto from(v8::Isolate* isolate, v8::Local value) - -> decltype(convert::from(isolate, value)) { - return convert::from(isolate, value); +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 index 8d70766..43f38ba 100644 --- a/lib/matplotlibcpp.h +++ b/lib/matplotlibcpp.h @@ -10,6 +10,7 @@ #include // requires c++11 support #include +#define PY_SSIZE_T_CLEAN #include #ifndef WITHOUT_NUMPY @@ -226,7 +227,7 @@ struct _interpreter { s_python_function_text = safe_import(pymod, "text"); s_python_function_suptitle = safe_import(pymod, "suptitle"); s_python_function_bar = safe_import(pymod,"bar"); - s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar"); + s_python_function_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"); @@ -937,12 +938,8 @@ inline bool subplots_adjust(const std::map& keywords = {}) PyFloat_FromDouble(it->second)); } + PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, detail::_interpreter::get().s_python_empty_tuple, kwargs); - PyObject* plot_args = PyTuple_New(0); - - PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs); - - Py_DECREF(plot_args); Py_DECREF(kwargs); if(res) Py_DECREF(res); @@ -1457,10 +1454,9 @@ void xlim(Numeric left, Numeric right) inline double* xlim() { - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args); - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); + 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); @@ -1475,10 +1471,9 @@ inline double* xlim() inline double* ylim() { - PyObject* args = PyTuple_New(0); - PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args); - PyObject* left = PyTuple_GetItem(res,0); - PyObject* right = PyTuple_GetItem(res,1); + 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); @@ -1671,6 +1666,7 @@ inline void suptitle(const std::string &suptitlestr, const std::map +#include #include "matplotlibcpp.h" #include "convert.h" -namespace matplotnode { - void backend(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); +namespace plt = matplotlibcpp; - auto name = from(isolate, info[0]); +namespace matplotnode { + void backend(const Napi::CallbackInfo& info) { + auto name = from(info[0], ""); - matplotlibcpp::backend(name); + plt::backend(name); } - void annotate(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - auto annotation = from(isolate, info[0]); - auto x = from(isolate, info[1]); - auto y = from(isolate, info[2]); + void annotate(const Napi::CallbackInfo& info) { + auto annotation = from(info[0], ""); + auto x = from(info[1], {}); + auto y = from(info[2], {}); - matplotlibcpp::annotate(annotation, x, y); + plt::annotate(annotation, x, y); } - void plot(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void plot(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto keywords = from>(info[2], {}); - auto x = from>(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto keywords = from>(isolate, info[2]); - - matplotlibcpp::plot(x, y, keywords); + plt::plot(x, y, keywords); } - void plot3(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto x = from>(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto z = from>(isolate, info[2]); - auto keywords = from>(isolate, info[3]); + 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], {}); - matplotlibcpp::plot3(x, y, z, keywords); + plt::plot3(x, y, z, keywords); } - void stem(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto x = from>(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto keywords = from>(isolate, info[2]); + void stem(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto keywords = from>(info[2], {}); - matplotlibcpp::stem(x, y, keywords); + plt::stem(x, y, keywords); } - void fill(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void fill(const Napi::CallbackInfo& info) { + auto x = from>(info[0], {}); + auto y = from>(info[1], {}); + auto keywords = from>(info[2], {}); - auto x = from>(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto keywords = from>(isolate, info[2]); - - matplotlibcpp::fill(x, y, keywords); + plt::fill(x, y, keywords); } - void fill_between(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto x = from>(isolate, info[0]); - auto y1 = from>(isolate, info[1]); - auto y2 = from>(isolate, info[2]); - auto keywords = from>(isolate, info[3]); + 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], {}); - matplotlibcpp::fill_between(x, y1, y2, keywords); + plt::fill_between(x, y1, y2, keywords); } - void hist(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto y = from>(isolate, info[0]); - auto bins = from(isolate, info[1]); - auto color = from(isolate, info[2]); - auto alpha = from(isolate, info[3]); - auto cumulative = from(isolate, info[4]); + 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); - matplotlibcpp::hist(y, bins, color, alpha, cumulative); + plt::hist(y, bins, color, alpha, cumulative); } - void scatter(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + 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], {}); - auto x = from>(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto s = from(isolate, info[2]); - auto keywords = from>(isolate, info[3]); - - matplotlibcpp::scatter(x, y, s, keywords); + plt::scatter(x, y, s, keywords); } - void boxplot(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto data = from>(isolate, info[0]); - auto keywords = from>(isolate, info[1]); + void boxplot(const Napi::CallbackInfo& info) { + auto data = from>(info[0], {}); + auto keywords = from>(info[1], {}); - matplotlibcpp::boxplot(data, keywords); + plt::boxplot(data, keywords); } - void bar(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + 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], {}); - auto x = from>(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto ec = from(isolate, info[2]); - auto ls = from(isolate, info[3]); - auto lw = from(isolate, info[4]); - auto keywords = from>(isolate, info[5]); - - matplotlibcpp::bar(x, y, ec, ls, lw, keywords); + plt::bar(x, y, ec, ls, lw, keywords); } - void subplots_adjust(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto keywords = convert>::from(isolate, info[0]); + void subplots_adjust(const Napi::CallbackInfo& info) { + auto keywords = from>(info[0], {}); - matplotlibcpp::subplots_adjust(keywords); + plt::subplots_adjust(keywords); } - void named_hist(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto label = from(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto bins = from(isolate, info[2]); - auto color = from(isolate, info[3]); - auto alpha = from(isolate, info[4]); + 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); - matplotlibcpp::named_hist(label, y, bins, color, alpha); + plt::named_hist(label, y, bins, color, alpha); } - void quiver(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + 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], {}); - auto x = from>(isolate, info[0]); - auto y = from>(isolate, info[1]); - auto u = from>(isolate, info[2]); - auto w = from>(isolate, info[3]); - auto keywords = from>(isolate, info[4]); - - matplotlibcpp::quiver(x, y, u, w, keywords); + plt::quiver(x, y, u, w, keywords); } - void errorbar(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto x = from>(isolate, info[0]); - auto y1 = from>(isolate, info[1]); - auto yerr = from>(isolate, info[2]); - auto keywords = from>(isolate, info[3]); + 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], {}); - matplotlibcpp::errorbar(x, y1, yerr, keywords); + plt::errorbar(x, y1, yerr, keywords); } - void named_plot(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + 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], ""); - auto name = from(isolate, info[0]); - auto x = from>(isolate, info[1]); - auto y = from>(isolate, info[2]); - auto format = from(isolate, info[3]); - - matplotlibcpp::named_plot(name, x, y, format); + plt::named_plot(name, x, y, format); } - void named_semilogx(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto name = from(isolate, info[0]); - auto x = from>(isolate, info[1]); - auto y = from>(isolate, info[2]); - auto format = from(isolate, info[3]); + 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], ""); - matplotlibcpp::named_semilogx(name, x, y, format); + plt::named_semilogx(name, x, y, format); } - void named_semilogy(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto name = from(isolate, info[0]); - auto x = from>(isolate, info[1]); - auto y = from>(isolate, info[2]); - auto format = from(isolate, info[3]); + 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], ""); - matplotlibcpp::named_semilogy(name, x, y, format); + plt::named_semilogy(name, x, y, format); } - void named_loglog(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + 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], ""); - auto name = from(isolate, info[0]); - auto x = from>(isolate, info[1]); - auto y = from>(isolate, info[2]); - auto format = from(isolate, info[3]); - - matplotlibcpp::named_loglog(name, x, y, format); + plt::named_loglog(name, x, y, format); } - void text(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - - auto x = from(isolate, info[0]); - auto y = from(isolate, info[1]); - auto s = from(isolate, info[2]); + void text(const Napi::CallbackInfo& info) { + auto x = from(info[0], {}); + auto y = from(info[1], {}); + auto s = from(info[2], ""); - matplotlibcpp::text(x, y, s); + plt::text(x, y, s); } - void figure(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void figure(const Napi::CallbackInfo& info) { + auto number = from(info[0], 0); - auto number = from(isolate, info[0]); - - matplotlibcpp::figure(number); + plt::figure(number); } - void figure_size(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto w = convert::from(isolate, info[0]); - auto h = convert::from(isolate, info[1]); + void figure_size(const Napi::CallbackInfo& info) { + auto w = from(info[0], 0); + auto h = from(info[1], 0); - matplotlibcpp::figure_size(w, h); + plt::figure_size(w, h); } - void legend(const v8::FunctionCallbackInfo& info) { - matplotlibcpp::legend(); + void legend(const Napi::CallbackInfo& info) { + plt::legend(); } - void ylim(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto left = from(isolate, info[0]); - auto right = from(isolate, info[1]); + void ylim(const Napi::CallbackInfo& info) { + auto left = from(info[0], 0.0); + auto right = from(info[1], 0.0); - matplotlibcpp::ylim(left, right); + plt::ylim(left, right); } - void xlim(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void xlim(const Napi::CallbackInfo& info) { + auto left = from(info[0], 0.0); + auto right = from(info[1], 0.0); - auto left = from(isolate, info[0]); - auto right = from(isolate, info[1]); - - matplotlibcpp::xlim(left, right); + plt::xlim(left, right); } - void xticks(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto ticks = from>(isolate, info[0]); - auto keywords = from>(isolate, info[1]); + void xticks(const Napi::CallbackInfo& info) { + auto ticks = from>(info[0], {}); + auto keywords = from>(info[1], {}); - matplotlibcpp::xticks(ticks, keywords); + plt::xticks(ticks, keywords); } - void yticks(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto ticks = from>(isolate, info[0]); - auto keywords = from>(isolate, info[1]); + void yticks(const Napi::CallbackInfo& info) { + auto ticks = from>(info[0], {}); + auto keywords = from>(info[1], {}); - matplotlibcpp::yticks(ticks, keywords); + plt::yticks(ticks, keywords); } - void tick_params(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], ""); - auto keywords = from>(isolate, info[0]); - auto axis = from(isolate, info[1]); - - matplotlibcpp::tick_params(keywords, axis); + plt::tick_params(keywords, axis); } - void subplot(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto nrows = from(isolate, info[0]); - auto ncols = from(isolate, info[1]); - auto plot_number = from(isolate, info[2]); + 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); - matplotlibcpp::subplot(nrows, ncols, plot_number); + plt::subplot(nrows, ncols, plot_number); } - void subplot2grid(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto nrows = from(isolate, info[0]); - auto ncols = from(isolate, info[1]); - auto rowid = from(isolate, info[2]); - auto colid = from(isolate, info[3]); - auto rowspan = from(isolate, info[4]); - auto colspan = from(isolate, info[5]); + 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); - matplotlibcpp::subplot2grid(nrows, ncols, rowid, colid, rowspan, colspan); + plt::subplot2grid(nrows, ncols, rowid, colid, rowspan, colspan); } - void title(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void title(const Napi::CallbackInfo& info) { + auto titlestr = from(info[0], ""); + auto keywords = from>(info[1], {}); - auto titlestr = from(isolate, info[0]); - auto keywords = from>(isolate, info[1]); - - matplotlibcpp::title(titlestr, keywords); + plt::title(titlestr, keywords); } - void suptitle(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto suptitlestr = from(isolate, info[0]); - auto keywords = from>(isolate, info[1]); + void suptitle(const Napi::CallbackInfo& info) { + auto suptitlestr = from(info[0], ""); + auto keywords = from>(info[1], {}); - matplotlibcpp::suptitle(suptitlestr, keywords); + plt::suptitle(suptitlestr, keywords); } - void axis(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void axis(const Napi::CallbackInfo& info) { + auto axisstr = from(info[0], ""); - auto axisstr = from(isolate, info[0]); - - matplotlibcpp::axis(axisstr); + plt::axis(axisstr); } - void axvline(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto x = from(isolate, info[0]); - auto ymin = from(isolate, info[1]); - auto ymax = from(isolate, info[2]); - auto keywords = from>(isolate, info[3]); + 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], {}); - matplotlibcpp::axvline(x, ymin, ymax, keywords); + plt::axvline(x, ymin, ymax, keywords); } - void xlabel(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto str = from(isolate, info[0]); - auto keywords = from>(isolate, info[1]); + void xlabel(const Napi::CallbackInfo& info) { + auto str = from(info[0], ""); + auto keywords = from>(info[1], {}); - matplotlibcpp::xlabel(str, keywords); + plt::xlabel(str, keywords); } - void ylabel(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void ylabel(const Napi::CallbackInfo& info) { + auto str = from(info[0], ""); + auto keywords = from>(info[1], {}); - auto str = from(isolate, info[0]); - auto keywords = from>(isolate, info[1]); - - matplotlibcpp::ylabel(str, keywords); + plt::ylabel(str, keywords); } - void set_zlabel(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto str = from(isolate, info[0]); - auto keywords = from>(isolate, info[1]); + void set_zlabel(const Napi::CallbackInfo& info) { + auto str = from(info[0], ""); + auto keywords = from>(info[1], {}); - matplotlibcpp::set_zlabel(str, keywords); + plt::set_zlabel(str, keywords); } - void grid(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void grid(const Napi::CallbackInfo& info) { + auto flag = from(info[0], false); - auto flag = from(isolate, info[0]); - - matplotlibcpp::grid(flag); + plt::grid(flag); } - void show(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto block = from(isolate, info[0]); + void show(const Napi::CallbackInfo& info) { + auto block = from(info[0], true); - matplotlibcpp::show(block); + plt::show(block); } - void close(const v8::FunctionCallbackInfo& info) { - matplotlibcpp::close(); + void close(const Napi::CallbackInfo& info) { + plt::close(); } - void xkcd(const v8::FunctionCallbackInfo& info) { - matplotlibcpp::xkcd(); + void xkcd(const Napi::CallbackInfo& info) { + plt::xkcd(); } - void draw(const v8::FunctionCallbackInfo& info) { - matplotlibcpp::draw(); + void draw(const Napi::CallbackInfo& info) { + plt::draw(); } - void pause(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto interval = from(isolate, info[0]); + void pause(const Napi::CallbackInfo& info) { + auto interval = from(info[0], 0.0); - matplotlibcpp::pause(interval); + plt::pause(interval); } - void save(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); + void save(const Napi::CallbackInfo& info) { + auto filename = from(info[0], ""); - auto filename = from(isolate, info[0]); - - matplotlibcpp::save(filename); + plt::save(filename); } - void clf(const v8::FunctionCallbackInfo& info) { - matplotlibcpp::clf(); + void clf(const Napi::CallbackInfo& info) { + plt::clf(); } - void ion(const v8::FunctionCallbackInfo& info) { - matplotlibcpp::ion(); + void ion(const Napi::CallbackInfo& info) { + plt::ion(); } - void ginput(const v8::FunctionCallbackInfo& info) { - v8::Isolate* isolate = info.GetIsolate(); - - auto numClicks = from(isolate, info[0]); - auto keywords = from>(isolate, info[1]); + void ginput(const Napi::CallbackInfo& info) { + auto numClicks = from(info[0], 0); + auto keywords = from>(info[1], {}); - matplotlibcpp::ginput(numClicks, keywords); + plt::ginput(numClicks, keywords); } - void tight_layout(const v8::FunctionCallbackInfo& info) { - matplotlibcpp::tight_layout(); + void tight_layout(const Napi::CallbackInfo& info) { + plt::tight_layout(); } } -void init(v8::Local exports) { - NODE_SET_METHOD(exports, "backend", matplotnode::backend); - NODE_SET_METHOD(exports, "annotate", matplotnode::annotate); - NODE_SET_METHOD(exports, "plot", matplotnode::plot); - NODE_SET_METHOD(exports, "plot3", matplotnode::plot3); - NODE_SET_METHOD(exports, "stem", matplotnode::stem); - NODE_SET_METHOD(exports, "fill", matplotnode::fill); - NODE_SET_METHOD(exports, "fill_between", matplotnode::fill_between); - NODE_SET_METHOD(exports, "hist", matplotnode::hist); - NODE_SET_METHOD(exports, "scatter", matplotnode::scatter); - NODE_SET_METHOD(exports, "boxplot", matplotnode::boxplot); - NODE_SET_METHOD(exports, "bar", matplotnode::bar); - NODE_SET_METHOD(exports, "subplots_adjust", matplotnode::subplots_adjust); - NODE_SET_METHOD(exports, "named_hist", matplotnode::named_hist); - NODE_SET_METHOD(exports, "quiver", matplotnode::quiver); - NODE_SET_METHOD(exports, "errorbar", matplotnode::errorbar); - NODE_SET_METHOD(exports, "named_plot", matplotnode::named_plot); - NODE_SET_METHOD(exports, "named_semilogx", matplotnode::named_semilogx); - NODE_SET_METHOD(exports, "named_semilogy", matplotnode::named_semilogy); - NODE_SET_METHOD(exports, "named_loglog", matplotnode::named_loglog); - NODE_SET_METHOD(exports, "text", matplotnode::text); - NODE_SET_METHOD(exports, "figure", matplotnode::figure); - NODE_SET_METHOD(exports, "figure_size", matplotnode::figure_size); - NODE_SET_METHOD(exports, "legend", matplotnode::legend); - NODE_SET_METHOD(exports, "ylim", matplotnode::ylim); - NODE_SET_METHOD(exports, "xlim", matplotnode::xlim); - NODE_SET_METHOD(exports, "xticks", matplotnode::xticks); - NODE_SET_METHOD(exports, "yticks", matplotnode::yticks); - NODE_SET_METHOD(exports, "tick_params", matplotnode::tick_params); - NODE_SET_METHOD(exports, "subplot", matplotnode::subplot); - NODE_SET_METHOD(exports, "subplot2grid", matplotnode::subplot2grid); - NODE_SET_METHOD(exports, "title", matplotnode::title); - NODE_SET_METHOD(exports, "suptitle", matplotnode::suptitle); - NODE_SET_METHOD(exports, "axis", matplotnode::axis); - NODE_SET_METHOD(exports, "axvline", matplotnode::axvline); - NODE_SET_METHOD(exports, "xlabel", matplotnode::xlabel); - NODE_SET_METHOD(exports, "ylabel", matplotnode::ylabel); - NODE_SET_METHOD(exports, "set_zlabel", matplotnode::set_zlabel); - NODE_SET_METHOD(exports, "grid", matplotnode::grid); - NODE_SET_METHOD(exports, "show", matplotnode::show); - NODE_SET_METHOD(exports, "close", matplotnode::close); - NODE_SET_METHOD(exports, "xkcd", matplotnode::xkcd); - NODE_SET_METHOD(exports, "draw", matplotnode::draw); - NODE_SET_METHOD(exports, "pause", matplotnode::pause); - NODE_SET_METHOD(exports, "save", matplotnode::save); - NODE_SET_METHOD(exports, "clf", matplotnode::clf); - NODE_SET_METHOD(exports, "ion", matplotnode::ion); - NODE_SET_METHOD(exports, "ginput", matplotnode::ginput); - NODE_SET_METHOD(exports, "tight_layout", matplotnode::tight_layout); +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.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