blob: 677007403803bebae9efaab369452cf390ffe408 [file] [log] [blame]
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001#pragma once
2
Austin Schuhab802d52020-07-03 18:11:11 -07003// Python headers must be included before any system headers, since
4// they define _POSIX_C_SOURCE
5#include <Python.h>
6
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08007#include <vector>
8#include <map>
Austin Schuhab802d52020-07-03 18:11:11 -07009#include <array>
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080010#include <numeric>
11#include <algorithm>
12#include <stdexcept>
13#include <iostream>
Austin Schuhab802d52020-07-03 18:11:11 -070014#include <cstdint> // <cstdint> requires c++11 support
15#include <functional>
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080016
17#ifndef WITHOUT_NUMPY
18# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
19# include <numpy/arrayobject.h>
Austin Schuhab802d52020-07-03 18:11:11 -070020
21# ifdef WITH_OPENCV
22# include <opencv2/opencv.hpp>
23# endif // WITH_OPENCV
24
25/*
26 * A bunch of constants were removed in OpenCV 4 in favour of enum classes, so
27 * define the ones we need here.
28 */
29# if CV_MAJOR_VERSION > 3
30# define CV_BGR2RGB cv::COLOR_BGR2RGB
31# define CV_BGRA2RGBA cv::COLOR_BGRA2RGBA
32# endif
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080033#endif // WITHOUT_NUMPY
34
35#if PY_MAJOR_VERSION >= 3
36# define PyString_FromString PyUnicode_FromString
Austin Schuhab802d52020-07-03 18:11:11 -070037# define PyInt_FromLong PyLong_FromLong
38# define PyString_FromString PyUnicode_FromString
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080039#endif
40
41
42namespace matplotlibcpp {
43namespace detail {
44
45static std::string s_backend;
46
47struct _interpreter {
Austin Schuhab802d52020-07-03 18:11:11 -070048 PyObject* s_python_function_arrow;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080049 PyObject *s_python_function_show;
50 PyObject *s_python_function_close;
51 PyObject *s_python_function_draw;
52 PyObject *s_python_function_pause;
53 PyObject *s_python_function_save;
54 PyObject *s_python_function_figure;
Austin Schuhab802d52020-07-03 18:11:11 -070055 PyObject *s_python_function_fignum_exists;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080056 PyObject *s_python_function_plot;
Austin Schuhab802d52020-07-03 18:11:11 -070057 PyObject *s_python_function_quiver;
58 PyObject* s_python_function_contour;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080059 PyObject *s_python_function_semilogx;
60 PyObject *s_python_function_semilogy;
61 PyObject *s_python_function_loglog;
Austin Schuhab802d52020-07-03 18:11:11 -070062 PyObject *s_python_function_fill;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080063 PyObject *s_python_function_fill_between;
64 PyObject *s_python_function_hist;
Austin Schuhab802d52020-07-03 18:11:11 -070065 PyObject *s_python_function_imshow;
66 PyObject *s_python_function_scatter;
67 PyObject *s_python_function_boxplot;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080068 PyObject *s_python_function_subplot;
Austin Schuhab802d52020-07-03 18:11:11 -070069 PyObject *s_python_function_subplot2grid;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080070 PyObject *s_python_function_legend;
71 PyObject *s_python_function_xlim;
72 PyObject *s_python_function_ion;
Austin Schuhab802d52020-07-03 18:11:11 -070073 PyObject *s_python_function_ginput;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080074 PyObject *s_python_function_ylim;
75 PyObject *s_python_function_title;
76 PyObject *s_python_function_axis;
Austin Schuhab802d52020-07-03 18:11:11 -070077 PyObject *s_python_function_axvline;
78 PyObject *s_python_function_axvspan;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080079 PyObject *s_python_function_xlabel;
80 PyObject *s_python_function_ylabel;
Austin Schuhab802d52020-07-03 18:11:11 -070081 PyObject *s_python_function_gca;
82 PyObject *s_python_function_xticks;
83 PyObject *s_python_function_yticks;
84 PyObject* s_python_function_margins;
85 PyObject *s_python_function_tick_params;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080086 PyObject *s_python_function_grid;
Austin Schuhab802d52020-07-03 18:11:11 -070087 PyObject* s_python_function_cla;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080088 PyObject *s_python_function_clf;
89 PyObject *s_python_function_errorbar;
90 PyObject *s_python_function_annotate;
91 PyObject *s_python_function_tight_layout;
Austin Schuhab802d52020-07-03 18:11:11 -070092 PyObject *s_python_colormap;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080093 PyObject *s_python_empty_tuple;
94 PyObject *s_python_function_stem;
95 PyObject *s_python_function_xkcd;
Austin Schuhab802d52020-07-03 18:11:11 -070096 PyObject *s_python_function_text;
97 PyObject *s_python_function_suptitle;
98 PyObject *s_python_function_bar;
99 PyObject *s_python_function_colorbar;
100 PyObject *s_python_function_subplots_adjust;
101
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800102
103 /* For now, _interpreter is implemented as a singleton since its currently not possible to have
104 multiple independent embedded python interpreters without patching the python source code
105 or starting a separate process for each.
106 http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program
107 */
108
109 static _interpreter& get() {
110 static _interpreter ctx;
111 return ctx;
112 }
113
Austin Schuhab802d52020-07-03 18:11:11 -0700114 PyObject* safe_import(PyObject* module, std::string fname) {
115 PyObject* fn = PyObject_GetAttrString(module, fname.c_str());
116
117 if (!fn)
118 throw std::runtime_error(std::string("Couldn't find required function: ") + fname);
119
120 if (!PyFunction_Check(fn))
121 throw std::runtime_error(fname + std::string(" is unexpectedly not a PyFunction."));
122
123 return fn;
124 }
125
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800126private:
127
128#ifndef WITHOUT_NUMPY
129# if PY_MAJOR_VERSION >= 3
130
131 void *import_numpy() {
132 import_array(); // initialize C-API
133 return NULL;
134 }
135
136# else
137
138 void import_numpy() {
139 import_array(); // initialize C-API
140 }
141
142# endif
143#endif
144
145 _interpreter() {
146
147 // optional but recommended
148#if PY_MAJOR_VERSION >= 3
149 wchar_t name[] = L"plotting";
150#else
151 char name[] = "plotting";
152#endif
153 Py_SetProgramName(name);
154 Py_Initialize();
155
156#ifndef WITHOUT_NUMPY
157 import_numpy(); // initialize numpy C-API
158#endif
159
160 PyObject* matplotlibname = PyString_FromString("matplotlib");
161 PyObject* pyplotname = PyString_FromString("matplotlib.pyplot");
Austin Schuhab802d52020-07-03 18:11:11 -0700162 PyObject* cmname = PyString_FromString("matplotlib.cm");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800163 PyObject* pylabname = PyString_FromString("pylab");
Austin Schuhab802d52020-07-03 18:11:11 -0700164 if (!pyplotname || !pylabname || !matplotlibname || !cmname) {
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800165 throw std::runtime_error("couldnt create string");
166 }
167
168 PyObject* matplotlib = PyImport_Import(matplotlibname);
169 Py_DECREF(matplotlibname);
Austin Schuhab802d52020-07-03 18:11:11 -0700170 if (!matplotlib) {
171 PyErr_Print();
172 throw std::runtime_error("Error loading module matplotlib!");
173 }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800174
175 // matplotlib.use() must be called *before* pylab, matplotlib.pyplot,
176 // or matplotlib.backends is imported for the first time
177 if (!s_backend.empty()) {
178 PyObject_CallMethod(matplotlib, const_cast<char*>("use"), const_cast<char*>("s"), s_backend.c_str());
179 }
180
181 PyObject* pymod = PyImport_Import(pyplotname);
182 Py_DECREF(pyplotname);
183 if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); }
184
Austin Schuhab802d52020-07-03 18:11:11 -0700185 s_python_colormap = PyImport_Import(cmname);
186 Py_DECREF(cmname);
187 if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800188
189 PyObject* pylabmod = PyImport_Import(pylabname);
190 Py_DECREF(pylabname);
191 if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); }
192
Austin Schuhab802d52020-07-03 18:11:11 -0700193 s_python_function_arrow = safe_import(pymod, "arrow");
194 s_python_function_show = safe_import(pymod, "show");
195 s_python_function_close = safe_import(pymod, "close");
196 s_python_function_draw = safe_import(pymod, "draw");
197 s_python_function_pause = safe_import(pymod, "pause");
198 s_python_function_figure = safe_import(pymod, "figure");
199 s_python_function_fignum_exists = safe_import(pymod, "fignum_exists");
200 s_python_function_plot = safe_import(pymod, "plot");
201 s_python_function_quiver = safe_import(pymod, "quiver");
202 s_python_function_contour = safe_import(pymod, "contour");
203 s_python_function_semilogx = safe_import(pymod, "semilogx");
204 s_python_function_semilogy = safe_import(pymod, "semilogy");
205 s_python_function_loglog = safe_import(pymod, "loglog");
206 s_python_function_fill = safe_import(pymod, "fill");
207 s_python_function_fill_between = safe_import(pymod, "fill_between");
208 s_python_function_hist = safe_import(pymod,"hist");
209 s_python_function_scatter = safe_import(pymod,"scatter");
210 s_python_function_boxplot = safe_import(pymod,"boxplot");
211 s_python_function_subplot = safe_import(pymod, "subplot");
212 s_python_function_subplot2grid = safe_import(pymod, "subplot2grid");
213 s_python_function_legend = safe_import(pymod, "legend");
214 s_python_function_ylim = safe_import(pymod, "ylim");
215 s_python_function_title = safe_import(pymod, "title");
216 s_python_function_axis = safe_import(pymod, "axis");
217 s_python_function_axvline = safe_import(pymod, "axvline");
218 s_python_function_axvspan = safe_import(pymod, "axvspan");
219 s_python_function_xlabel = safe_import(pymod, "xlabel");
220 s_python_function_ylabel = safe_import(pymod, "ylabel");
221 s_python_function_gca = safe_import(pymod, "gca");
222 s_python_function_xticks = safe_import(pymod, "xticks");
223 s_python_function_yticks = safe_import(pymod, "yticks");
224 s_python_function_margins = safe_import(pymod, "margins");
225 s_python_function_tick_params = safe_import(pymod, "tick_params");
226 s_python_function_grid = safe_import(pymod, "grid");
227 s_python_function_xlim = safe_import(pymod, "xlim");
228 s_python_function_ion = safe_import(pymod, "ion");
229 s_python_function_ginput = safe_import(pymod, "ginput");
230 s_python_function_save = safe_import(pylabmod, "savefig");
231 s_python_function_annotate = safe_import(pymod,"annotate");
232 s_python_function_cla = safe_import(pymod, "cla");
233 s_python_function_clf = safe_import(pymod, "clf");
234 s_python_function_errorbar = safe_import(pymod, "errorbar");
235 s_python_function_tight_layout = safe_import(pymod, "tight_layout");
236 s_python_function_stem = safe_import(pymod, "stem");
237 s_python_function_xkcd = safe_import(pymod, "xkcd");
238 s_python_function_text = safe_import(pymod, "text");
239 s_python_function_suptitle = safe_import(pymod, "suptitle");
240 s_python_function_bar = safe_import(pymod,"bar");
241 s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar");
242 s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust");
243#ifndef WITHOUT_NUMPY
244 s_python_function_imshow = safe_import(pymod, "imshow");
245#endif
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800246 s_python_empty_tuple = PyTuple_New(0);
247 }
248
249 ~_interpreter() {
250 Py_Finalize();
251 }
252};
253
254} // end namespace detail
255
Austin Schuhab802d52020-07-03 18:11:11 -0700256/// Select the backend
257///
258/// **NOTE:** This must be called before the first plot command to have
259/// any effect.
260///
261/// Mainly useful to select the non-interactive 'Agg' backend when running
262/// matplotlibcpp in headless mode, for example on a machine with no display.
263///
264/// See also: https://matplotlib.org/2.0.2/api/matplotlib_configuration_api.html#matplotlib.use
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800265inline void backend(const std::string& name)
266{
267 detail::s_backend = name;
268}
269
270inline bool annotate(std::string annotation, double x, double y)
271{
Austin Schuhab802d52020-07-03 18:11:11 -0700272 detail::_interpreter::get();
273
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800274 PyObject * xy = PyTuple_New(2);
275 PyObject * str = PyString_FromString(annotation.c_str());
276
277 PyTuple_SetItem(xy,0,PyFloat_FromDouble(x));
278 PyTuple_SetItem(xy,1,PyFloat_FromDouble(y));
279
280 PyObject* kwargs = PyDict_New();
281 PyDict_SetItemString(kwargs, "xy", xy);
282
283 PyObject* args = PyTuple_New(1);
284 PyTuple_SetItem(args, 0, str);
285
286 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs);
287
288 Py_DECREF(args);
289 Py_DECREF(kwargs);
290
291 if(res) Py_DECREF(res);
292
293 return res;
294}
295
Austin Schuhab802d52020-07-03 18:11:11 -0700296namespace detail {
297
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800298#ifndef WITHOUT_NUMPY
299// Type selector for numpy array conversion
300template <typename T> struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default
301template <> struct select_npy_type<double> { const static NPY_TYPES type = NPY_DOUBLE; };
302template <> struct select_npy_type<float> { const static NPY_TYPES type = NPY_FLOAT; };
303template <> struct select_npy_type<bool> { const static NPY_TYPES type = NPY_BOOL; };
304template <> struct select_npy_type<int8_t> { const static NPY_TYPES type = NPY_INT8; };
305template <> struct select_npy_type<int16_t> { const static NPY_TYPES type = NPY_SHORT; };
306template <> struct select_npy_type<int32_t> { const static NPY_TYPES type = NPY_INT; };
307template <> struct select_npy_type<int64_t> { const static NPY_TYPES type = NPY_INT64; };
308template <> struct select_npy_type<uint8_t> { const static NPY_TYPES type = NPY_UINT8; };
309template <> struct select_npy_type<uint16_t> { const static NPY_TYPES type = NPY_USHORT; };
310template <> struct select_npy_type<uint32_t> { const static NPY_TYPES type = NPY_ULONG; };
311template <> struct select_npy_type<uint64_t> { const static NPY_TYPES type = NPY_UINT64; };
312
Austin Schuhab802d52020-07-03 18:11:11 -0700313// Sanity checks; comment them out or change the numpy type below if you're compiling on
314// a platform where they don't apply
315static_assert(sizeof(long long) == 8);
316template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
317static_assert(sizeof(unsigned long long) == 8);
318template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };
319// TODO: add int, long, etc.
320
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800321template<typename Numeric>
322PyObject* get_array(const std::vector<Numeric>& v)
323{
Austin Schuhab802d52020-07-03 18:11:11 -0700324 npy_intp vsize = v.size();
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800325 NPY_TYPES type = select_npy_type<Numeric>::type;
Austin Schuhab802d52020-07-03 18:11:11 -0700326 if (type == NPY_NOTYPE) {
327 size_t memsize = v.size()*sizeof(double);
328 double* dp = static_cast<double*>(::malloc(memsize));
329 for (size_t i=0; i<v.size(); ++i)
330 dp[i] = v[i];
331 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, dp);
332 PyArray_UpdateFlags(reinterpret_cast<PyArrayObject*>(varray), NPY_ARRAY_OWNDATA);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800333 return varray;
334 }
Austin Schuhab802d52020-07-03 18:11:11 -0700335
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800336 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data()));
337 return varray;
338}
339
Austin Schuhab802d52020-07-03 18:11:11 -0700340
341template<typename Numeric>
342PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
343{
344 if (v.size() < 1) throw std::runtime_error("get_2d_array v too small");
345
346 npy_intp vsize[2] = {static_cast<npy_intp>(v.size()),
347 static_cast<npy_intp>(v[0].size())};
348
349 PyArrayObject *varray =
350 (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE);
351
352 double *vd_begin = static_cast<double *>(PyArray_DATA(varray));
353
354 for (const ::std::vector<Numeric> &v_row : v) {
355 if (v_row.size() != static_cast<size_t>(vsize[1]))
356 throw std::runtime_error("Missmatched array size");
357 std::copy(v_row.begin(), v_row.end(), vd_begin);
358 vd_begin += vsize[1];
359 }
360
361 return reinterpret_cast<PyObject *>(varray);
362}
363
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800364#else // fallback if we don't have numpy: copy every element of the given vector
365
366template<typename Numeric>
367PyObject* get_array(const std::vector<Numeric>& v)
368{
369 PyObject* list = PyList_New(v.size());
370 for(size_t i = 0; i < v.size(); ++i) {
371 PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i)));
372 }
373 return list;
374}
375
376#endif // WITHOUT_NUMPY
377
Austin Schuhab802d52020-07-03 18:11:11 -0700378// sometimes, for labels and such, we need string arrays
379inline PyObject * get_array(const std::vector<std::string>& strings)
380{
381 PyObject* list = PyList_New(strings.size());
382 for (std::size_t i = 0; i < strings.size(); ++i) {
383 PyList_SetItem(list, i, PyString_FromString(strings[i].c_str()));
384 }
385 return list;
386}
387
388// not all matplotlib need 2d arrays, some prefer lists of lists
389template<typename Numeric>
390PyObject* get_listlist(const std::vector<std::vector<Numeric>>& ll)
391{
392 PyObject* listlist = PyList_New(ll.size());
393 for (std::size_t i = 0; i < ll.size(); ++i) {
394 PyList_SetItem(listlist, i, get_array(ll[i]));
395 }
396 return listlist;
397}
398
399} // namespace detail
400
401/// Plot a line through the given x and y data points..
402///
403/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800404template<typename Numeric>
405bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
406{
407 assert(x.size() == y.size());
408
Austin Schuhab802d52020-07-03 18:11:11 -0700409 detail::_interpreter::get();
410
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800411 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700412 PyObject* xarray = detail::get_array(x);
413 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800414
415 // construct positional args
416 PyObject* args = PyTuple_New(2);
417 PyTuple_SetItem(args, 0, xarray);
418 PyTuple_SetItem(args, 1, yarray);
419
420 // construct keyword args
421 PyObject* kwargs = PyDict_New();
422 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
423 {
424 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
425 }
426
427 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs);
428
429 Py_DECREF(args);
430 Py_DECREF(kwargs);
431 if(res) Py_DECREF(res);
432
433 return res;
434}
435
Austin Schuhab802d52020-07-03 18:11:11 -0700436// TODO - it should be possible to make this work by implementing
437// a non-numpy alternative for `detail::get_2darray()`.
438#ifndef WITHOUT_NUMPY
439template <typename Numeric>
440void plot_surface(const std::vector<::std::vector<Numeric>> &x,
441 const std::vector<::std::vector<Numeric>> &y,
442 const std::vector<::std::vector<Numeric>> &z,
443 const std::map<std::string, std::string> &keywords =
444 std::map<std::string, std::string>())
445{
446 detail::_interpreter::get();
447
448 // We lazily load the modules here the first time this function is called
449 // because I'm not sure that we can assume "matplotlib installed" implies
450 // "mpl_toolkits installed" on all platforms, and we don't want to require
451 // it for people who don't need 3d plots.
452 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
453 if (!mpl_toolkitsmod) {
454 detail::_interpreter::get();
455
456 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
457 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
458 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
459
460 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
461 Py_DECREF(mpl_toolkits);
462 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
463
464 axis3dmod = PyImport_Import(axis3d);
465 Py_DECREF(axis3d);
466 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
467 }
468
469 assert(x.size() == y.size());
470 assert(y.size() == z.size());
471
472 // using numpy arrays
473 PyObject *xarray = detail::get_2darray(x);
474 PyObject *yarray = detail::get_2darray(y);
475 PyObject *zarray = detail::get_2darray(z);
476
477 // construct positional args
478 PyObject *args = PyTuple_New(3);
479 PyTuple_SetItem(args, 0, xarray);
480 PyTuple_SetItem(args, 1, yarray);
481 PyTuple_SetItem(args, 2, zarray);
482
483 // Build up the kw args.
484 PyObject *kwargs = PyDict_New();
485 PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1));
486 PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1));
487
488 PyObject *python_colormap_coolwarm = PyObject_GetAttrString(
489 detail::_interpreter::get().s_python_colormap, "coolwarm");
490
491 PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm);
492
493 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
494 it != keywords.end(); ++it) {
495 PyDict_SetItemString(kwargs, it->first.c_str(),
496 PyString_FromString(it->second.c_str()));
497 }
498
499
500 PyObject *fig =
501 PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
502 detail::_interpreter::get().s_python_empty_tuple);
503 if (!fig) throw std::runtime_error("Call to figure() failed.");
504
505 PyObject *gca_kwargs = PyDict_New();
506 PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
507
508 PyObject *gca = PyObject_GetAttrString(fig, "gca");
509 if (!gca) throw std::runtime_error("No gca");
510 Py_INCREF(gca);
511 PyObject *axis = PyObject_Call(
512 gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
513
514 if (!axis) throw std::runtime_error("No axis");
515 Py_INCREF(axis);
516
517 Py_DECREF(gca);
518 Py_DECREF(gca_kwargs);
519
520 PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface");
521 if (!plot_surface) throw std::runtime_error("No surface");
522 Py_INCREF(plot_surface);
523 PyObject *res = PyObject_Call(plot_surface, args, kwargs);
524 if (!res) throw std::runtime_error("failed surface");
525 Py_DECREF(plot_surface);
526
527 Py_DECREF(axis);
528 Py_DECREF(args);
529 Py_DECREF(kwargs);
530 if (res) Py_DECREF(res);
531}
532#endif // WITHOUT_NUMPY
533
534template <typename Numeric>
535void plot3(const std::vector<Numeric> &x,
536 const std::vector<Numeric> &y,
537 const std::vector<Numeric> &z,
538 const std::map<std::string, std::string> &keywords =
539 std::map<std::string, std::string>())
540{
541 detail::_interpreter::get();
542
543 // Same as with plot_surface: We lazily load the modules here the first time
544 // this function is called because I'm not sure that we can assume "matplotlib
545 // installed" implies "mpl_toolkits installed" on all platforms, and we don't
546 // want to require it for people who don't need 3d plots.
547 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
548 if (!mpl_toolkitsmod) {
549 detail::_interpreter::get();
550
551 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
552 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
553 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
554
555 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
556 Py_DECREF(mpl_toolkits);
557 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
558
559 axis3dmod = PyImport_Import(axis3d);
560 Py_DECREF(axis3d);
561 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
562 }
563
564 assert(x.size() == y.size());
565 assert(y.size() == z.size());
566
567 PyObject *xarray = detail::get_array(x);
568 PyObject *yarray = detail::get_array(y);
569 PyObject *zarray = detail::get_array(z);
570
571 // construct positional args
572 PyObject *args = PyTuple_New(3);
573 PyTuple_SetItem(args, 0, xarray);
574 PyTuple_SetItem(args, 1, yarray);
575 PyTuple_SetItem(args, 2, zarray);
576
577 // Build up the kw args.
578 PyObject *kwargs = PyDict_New();
579
580 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
581 it != keywords.end(); ++it) {
582 PyDict_SetItemString(kwargs, it->first.c_str(),
583 PyString_FromString(it->second.c_str()));
584 }
585
586 PyObject *fig =
587 PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
588 detail::_interpreter::get().s_python_empty_tuple);
589 if (!fig) throw std::runtime_error("Call to figure() failed.");
590
591 PyObject *gca_kwargs = PyDict_New();
592 PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
593
594 PyObject *gca = PyObject_GetAttrString(fig, "gca");
595 if (!gca) throw std::runtime_error("No gca");
596 Py_INCREF(gca);
597 PyObject *axis = PyObject_Call(
598 gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
599
600 if (!axis) throw std::runtime_error("No axis");
601 Py_INCREF(axis);
602
603 Py_DECREF(gca);
604 Py_DECREF(gca_kwargs);
605
606 PyObject *plot3 = PyObject_GetAttrString(axis, "plot");
607 if (!plot3) throw std::runtime_error("No 3D line plot");
608 Py_INCREF(plot3);
609 PyObject *res = PyObject_Call(plot3, args, kwargs);
610 if (!res) throw std::runtime_error("Failed 3D line plot");
611 Py_DECREF(plot3);
612
613 Py_DECREF(axis);
614 Py_DECREF(args);
615 Py_DECREF(kwargs);
616 if (res) Py_DECREF(res);
617}
618
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800619template<typename Numeric>
620bool stem(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
621{
622 assert(x.size() == y.size());
623
Austin Schuhab802d52020-07-03 18:11:11 -0700624 detail::_interpreter::get();
625
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800626 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700627 PyObject* xarray = detail::get_array(x);
628 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800629
630 // construct positional args
631 PyObject* args = PyTuple_New(2);
632 PyTuple_SetItem(args, 0, xarray);
633 PyTuple_SetItem(args, 1, yarray);
634
635 // construct keyword args
636 PyObject* kwargs = PyDict_New();
637 for (std::map<std::string, std::string>::const_iterator it =
638 keywords.begin(); it != keywords.end(); ++it) {
639 PyDict_SetItemString(kwargs, it->first.c_str(),
640 PyString_FromString(it->second.c_str()));
641 }
642
643 PyObject* res = PyObject_Call(
644 detail::_interpreter::get().s_python_function_stem, args, kwargs);
645
646 Py_DECREF(args);
647 Py_DECREF(kwargs);
648 if (res)
649 Py_DECREF(res);
650
651 return res;
652}
653
654template< typename Numeric >
Austin Schuhab802d52020-07-03 18:11:11 -0700655bool fill(const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::map<std::string, std::string>& keywords)
656{
657 assert(x.size() == y.size());
658
659 detail::_interpreter::get();
660
661 // using numpy arrays
662 PyObject* xarray = detail::get_array(x);
663 PyObject* yarray = detail::get_array(y);
664
665 // construct positional args
666 PyObject* args = PyTuple_New(2);
667 PyTuple_SetItem(args, 0, xarray);
668 PyTuple_SetItem(args, 1, yarray);
669
670 // construct keyword args
671 PyObject* kwargs = PyDict_New();
672 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
673 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
674 }
675
676 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill, args, kwargs);
677
678 Py_DECREF(args);
679 Py_DECREF(kwargs);
680
681 if (res) Py_DECREF(res);
682
683 return res;
684}
685
686template< typename Numeric >
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800687bool fill_between(const std::vector<Numeric>& x, const std::vector<Numeric>& y1, const std::vector<Numeric>& y2, const std::map<std::string, std::string>& keywords)
688{
689 assert(x.size() == y1.size());
690 assert(x.size() == y2.size());
691
Austin Schuhab802d52020-07-03 18:11:11 -0700692 detail::_interpreter::get();
693
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800694 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700695 PyObject* xarray = detail::get_array(x);
696 PyObject* y1array = detail::get_array(y1);
697 PyObject* y2array = detail::get_array(y2);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800698
699 // construct positional args
700 PyObject* args = PyTuple_New(3);
701 PyTuple_SetItem(args, 0, xarray);
702 PyTuple_SetItem(args, 1, y1array);
703 PyTuple_SetItem(args, 2, y2array);
704
705 // construct keyword args
706 PyObject* kwargs = PyDict_New();
Austin Schuhab802d52020-07-03 18:11:11 -0700707 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it) {
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800708 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
709 }
710
711 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs);
712
713 Py_DECREF(args);
714 Py_DECREF(kwargs);
715 if(res) Py_DECREF(res);
716
717 return res;
718}
719
Austin Schuhab802d52020-07-03 18:11:11 -0700720template <typename Numeric>
721bool arrow(Numeric x, Numeric y, Numeric end_x, Numeric end_y, const std::string& fc = "r",
722 const std::string ec = "k", Numeric head_length = 0.25, Numeric head_width = 0.1625) {
723 PyObject* obj_x = PyFloat_FromDouble(x);
724 PyObject* obj_y = PyFloat_FromDouble(y);
725 PyObject* obj_end_x = PyFloat_FromDouble(end_x);
726 PyObject* obj_end_y = PyFloat_FromDouble(end_y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800727
Austin Schuhab802d52020-07-03 18:11:11 -0700728 PyObject* kwargs = PyDict_New();
729 PyDict_SetItemString(kwargs, "fc", PyString_FromString(fc.c_str()));
730 PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str()));
731 PyDict_SetItemString(kwargs, "head_width", PyFloat_FromDouble(head_width));
732 PyDict_SetItemString(kwargs, "head_length", PyFloat_FromDouble(head_length));
733
734 PyObject* plot_args = PyTuple_New(4);
735 PyTuple_SetItem(plot_args, 0, obj_x);
736 PyTuple_SetItem(plot_args, 1, obj_y);
737 PyTuple_SetItem(plot_args, 2, obj_end_x);
738 PyTuple_SetItem(plot_args, 3, obj_end_y);
739
740 PyObject* res =
741 PyObject_Call(detail::_interpreter::get().s_python_function_arrow, plot_args, kwargs);
742
743 Py_DECREF(plot_args);
744 Py_DECREF(kwargs);
745 if (res)
746 Py_DECREF(res);
747
748 return res;
749}
750
751template< typename Numeric>
752bool hist(const std::vector<Numeric>& y, long bins=10,std::string color="b",
753 double alpha=1.0, bool cumulative=false)
754{
755 detail::_interpreter::get();
756
757 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800758
759 PyObject* kwargs = PyDict_New();
760 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
761 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
762 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
Austin Schuhab802d52020-07-03 18:11:11 -0700763 PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800764
765 PyObject* plot_args = PyTuple_New(1);
766
767 PyTuple_SetItem(plot_args, 0, yarray);
768
769
770 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
771
772
773 Py_DECREF(plot_args);
774 Py_DECREF(kwargs);
775 if(res) Py_DECREF(res);
776
777 return res;
778}
779
Austin Schuhab802d52020-07-03 18:11:11 -0700780#ifndef WITHOUT_NUMPY
781namespace detail {
782
783inline void imshow(void *ptr, const NPY_TYPES type, const int rows, const int columns, const int colors, const std::map<std::string, std::string> &keywords, PyObject** out)
784{
785 assert(type == NPY_UINT8 || type == NPY_FLOAT);
786 assert(colors == 1 || colors == 3 || colors == 4);
787
788 detail::_interpreter::get();
789
790 // construct args
791 npy_intp dims[3] = { rows, columns, colors };
792 PyObject *args = PyTuple_New(1);
793 PyTuple_SetItem(args, 0, PyArray_SimpleNewFromData(colors == 1 ? 2 : 3, dims, type, ptr));
794
795 // construct keyword args
796 PyObject* kwargs = PyDict_New();
797 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
798 {
799 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
800 }
801
802 PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_imshow, args, kwargs);
803 Py_DECREF(args);
804 Py_DECREF(kwargs);
805 if (!res)
806 throw std::runtime_error("Call to imshow() failed");
807 if (out)
808 *out = res;
809 else
810 Py_DECREF(res);
811}
812
813} // namespace detail
814
815inline void imshow(const unsigned char *ptr, const int rows, const int columns, const int colors, const std::map<std::string, std::string> &keywords = {}, PyObject** out = nullptr)
816{
817 detail::imshow((void *) ptr, NPY_UINT8, rows, columns, colors, keywords, out);
818}
819
820inline void imshow(const float *ptr, const int rows, const int columns, const int colors, const std::map<std::string, std::string> &keywords = {}, PyObject** out = nullptr)
821{
822 detail::imshow((void *) ptr, NPY_FLOAT, rows, columns, colors, keywords, out);
823}
824
825#ifdef WITH_OPENCV
826void imshow(const cv::Mat &image, const std::map<std::string, std::string> &keywords = {})
827{
828 // Convert underlying type of matrix, if needed
829 cv::Mat image2;
830 NPY_TYPES npy_type = NPY_UINT8;
831 switch (image.type() & CV_MAT_DEPTH_MASK) {
832 case CV_8U:
833 image2 = image;
834 break;
835 case CV_32F:
836 image2 = image;
837 npy_type = NPY_FLOAT;
838 break;
839 default:
840 image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels()));
841 }
842
843 // If color image, convert from BGR to RGB
844 switch (image2.channels()) {
845 case 3:
846 cv::cvtColor(image2, image2, CV_BGR2RGB);
847 break;
848 case 4:
849 cv::cvtColor(image2, image2, CV_BGRA2RGBA);
850 }
851
852 detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords);
853}
854#endif // WITH_OPENCV
855#endif // WITHOUT_NUMPY
856
857template<typename NumericX, typename NumericY>
858bool scatter(const std::vector<NumericX>& x,
859 const std::vector<NumericY>& y,
860 const double s=1.0, // The marker size in points**2
861 const std::map<std::string, std::string> & keywords = {})
862{
863 detail::_interpreter::get();
864
865 assert(x.size() == y.size());
866
867 PyObject* xarray = detail::get_array(x);
868 PyObject* yarray = detail::get_array(y);
869
870 PyObject* kwargs = PyDict_New();
871 PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s));
872 for (const auto& it : keywords)
873 {
874 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
875 }
876
877 PyObject* plot_args = PyTuple_New(2);
878 PyTuple_SetItem(plot_args, 0, xarray);
879 PyTuple_SetItem(plot_args, 1, yarray);
880
881 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs);
882
883 Py_DECREF(plot_args);
884 Py_DECREF(kwargs);
885 if(res) Py_DECREF(res);
886
887 return res;
888}
889
890template<typename Numeric>
891bool boxplot(const std::vector<std::vector<Numeric>>& data,
892 const std::vector<std::string>& labels = {},
893 const std::map<std::string, std::string> & keywords = {})
894{
895 detail::_interpreter::get();
896
897 PyObject* listlist = detail::get_listlist(data);
898 PyObject* args = PyTuple_New(1);
899 PyTuple_SetItem(args, 0, listlist);
900
901 PyObject* kwargs = PyDict_New();
902
903 // kwargs needs the labels, if there are (the correct number of) labels
904 if (!labels.empty() && labels.size() == data.size()) {
905 PyDict_SetItemString(kwargs, "labels", detail::get_array(labels));
906 }
907
908 // take care of the remaining keywords
909 for (const auto& it : keywords)
910 {
911 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
912 }
913
914 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
915
916 Py_DECREF(args);
917 Py_DECREF(kwargs);
918
919 if(res) Py_DECREF(res);
920
921 return res;
922}
923
924template<typename Numeric>
925bool boxplot(const std::vector<Numeric>& data,
926 const std::map<std::string, std::string> & keywords = {})
927{
928 detail::_interpreter::get();
929
930 PyObject* vector = detail::get_array(data);
931 PyObject* args = PyTuple_New(1);
932 PyTuple_SetItem(args, 0, vector);
933
934 PyObject* kwargs = PyDict_New();
935 for (const auto& it : keywords)
936 {
937 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
938 }
939
940 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
941
942 Py_DECREF(args);
943 Py_DECREF(kwargs);
944
945 if(res) Py_DECREF(res);
946
947 return res;
948}
949
950template <typename Numeric>
951bool bar(const std::vector<Numeric> & x,
952 const std::vector<Numeric> & y,
953 std::string ec = "black",
954 std::string ls = "-",
955 double lw = 1.0,
956 const std::map<std::string, std::string> & keywords = {})
957{
958 detail::_interpreter::get();
959
960 PyObject * xarray = detail::get_array(x);
961 PyObject * yarray = detail::get_array(y);
962
963 PyObject * kwargs = PyDict_New();
964
965 PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str()));
966 PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str()));
967 PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw));
968
969 for (std::map<std::string, std::string>::const_iterator it =
970 keywords.begin();
971 it != keywords.end();
972 ++it) {
973 PyDict_SetItemString(
974 kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
975 }
976
977 PyObject * plot_args = PyTuple_New(2);
978 PyTuple_SetItem(plot_args, 0, xarray);
979 PyTuple_SetItem(plot_args, 1, yarray);
980
981 PyObject * res = PyObject_Call(
982 detail::_interpreter::get().s_python_function_bar, plot_args, kwargs);
983
984 Py_DECREF(plot_args);
985 Py_DECREF(kwargs);
986 if (res) Py_DECREF(res);
987
988 return res;
989}
990
991template <typename Numeric>
992bool bar(const std::vector<Numeric> & y,
993 std::string ec = "black",
994 std::string ls = "-",
995 double lw = 1.0,
996 const std::map<std::string, std::string> & keywords = {})
997{
998 using T = typename std::remove_reference<decltype(y)>::type::value_type;
999
1000 detail::_interpreter::get();
1001
1002 std::vector<T> x;
1003 for (std::size_t i = 0; i < y.size(); i++) { x.push_back(i); }
1004
1005 return bar(x, y, ec, ls, lw, keywords);
1006}
1007
1008inline bool subplots_adjust(const std::map<std::string, double>& keywords = {})
1009{
1010 detail::_interpreter::get();
1011
1012 PyObject* kwargs = PyDict_New();
1013 for (std::map<std::string, double>::const_iterator it =
1014 keywords.begin(); it != keywords.end(); ++it) {
1015 PyDict_SetItemString(kwargs, it->first.c_str(),
1016 PyFloat_FromDouble(it->second));
1017 }
1018
1019
1020 PyObject* plot_args = PyTuple_New(0);
1021
1022 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs);
1023
1024 Py_DECREF(plot_args);
1025 Py_DECREF(kwargs);
1026 if(res) Py_DECREF(res);
1027
1028 return res;
1029}
1030
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001031template< typename Numeric>
1032bool named_hist(std::string label,const std::vector<Numeric>& y, long bins=10, std::string color="b", double alpha=1.0)
1033{
Austin Schuhab802d52020-07-03 18:11:11 -07001034 detail::_interpreter::get();
1035
1036 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001037
1038 PyObject* kwargs = PyDict_New();
1039 PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str()));
1040 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
1041 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
1042 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
1043
1044
1045 PyObject* plot_args = PyTuple_New(1);
1046 PyTuple_SetItem(plot_args, 0, yarray);
1047
1048 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
1049
1050 Py_DECREF(plot_args);
1051 Py_DECREF(kwargs);
1052 if(res) Py_DECREF(res);
1053
1054 return res;
1055}
1056
1057template<typename NumericX, typename NumericY>
1058bool plot(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1059{
1060 assert(x.size() == y.size());
1061
Austin Schuhab802d52020-07-03 18:11:11 -07001062 detail::_interpreter::get();
1063
1064 PyObject* xarray = detail::get_array(x);
1065 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001066
1067 PyObject* pystring = PyString_FromString(s.c_str());
1068
1069 PyObject* plot_args = PyTuple_New(3);
1070 PyTuple_SetItem(plot_args, 0, xarray);
1071 PyTuple_SetItem(plot_args, 1, yarray);
1072 PyTuple_SetItem(plot_args, 2, pystring);
1073
1074 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
1075
1076 Py_DECREF(plot_args);
1077 if(res) Py_DECREF(res);
1078
1079 return res;
1080}
1081
Austin Schuhab802d52020-07-03 18:11:11 -07001082template <typename NumericX, typename NumericY, typename NumericZ>
1083bool contour(const std::vector<NumericX>& x, const std::vector<NumericY>& y,
1084 const std::vector<NumericZ>& z,
1085 const std::map<std::string, std::string>& keywords = {}) {
1086 assert(x.size() == y.size() && x.size() == z.size());
1087
1088 PyObject* xarray = get_array(x);
1089 PyObject* yarray = get_array(y);
1090 PyObject* zarray = get_array(z);
1091
1092 PyObject* plot_args = PyTuple_New(3);
1093 PyTuple_SetItem(plot_args, 0, xarray);
1094 PyTuple_SetItem(plot_args, 1, yarray);
1095 PyTuple_SetItem(plot_args, 2, zarray);
1096
1097 // construct keyword args
1098 PyObject* kwargs = PyDict_New();
1099 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
1100 it != keywords.end(); ++it) {
1101 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1102 }
1103
1104 PyObject* res =
1105 PyObject_Call(detail::_interpreter::get().s_python_function_contour, plot_args, kwargs);
1106
1107 Py_DECREF(kwargs);
1108 Py_DECREF(plot_args);
1109 if (res)
1110 Py_DECREF(res);
1111
1112 return res;
1113}
1114
1115template<typename NumericX, typename NumericY, typename NumericU, typename NumericW>
1116bool quiver(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::vector<NumericU>& u, const std::vector<NumericW>& w, const std::map<std::string, std::string>& keywords = {})
1117{
1118 assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size());
1119
1120 detail::_interpreter::get();
1121
1122 PyObject* xarray = detail::get_array(x);
1123 PyObject* yarray = detail::get_array(y);
1124 PyObject* uarray = detail::get_array(u);
1125 PyObject* warray = detail::get_array(w);
1126
1127 PyObject* plot_args = PyTuple_New(4);
1128 PyTuple_SetItem(plot_args, 0, xarray);
1129 PyTuple_SetItem(plot_args, 1, yarray);
1130 PyTuple_SetItem(plot_args, 2, uarray);
1131 PyTuple_SetItem(plot_args, 3, warray);
1132
1133 // construct keyword args
1134 PyObject* kwargs = PyDict_New();
1135 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1136 {
1137 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1138 }
1139
1140 PyObject* res = PyObject_Call(
1141 detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs);
1142
1143 Py_DECREF(kwargs);
1144 Py_DECREF(plot_args);
1145 if (res)
1146 Py_DECREF(res);
1147
1148 return res;
1149}
1150
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001151template<typename NumericX, typename NumericY>
1152bool stem(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1153{
1154 assert(x.size() == y.size());
1155
Austin Schuhab802d52020-07-03 18:11:11 -07001156 detail::_interpreter::get();
1157
1158 PyObject* xarray = detail::get_array(x);
1159 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001160
1161 PyObject* pystring = PyString_FromString(s.c_str());
1162
1163 PyObject* plot_args = PyTuple_New(3);
1164 PyTuple_SetItem(plot_args, 0, xarray);
1165 PyTuple_SetItem(plot_args, 1, yarray);
1166 PyTuple_SetItem(plot_args, 2, pystring);
1167
1168 PyObject* res = PyObject_CallObject(
1169 detail::_interpreter::get().s_python_function_stem, plot_args);
1170
1171 Py_DECREF(plot_args);
1172 if (res)
1173 Py_DECREF(res);
1174
1175 return res;
1176}
1177
1178template<typename NumericX, typename NumericY>
1179bool semilogx(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1180{
1181 assert(x.size() == y.size());
1182
Austin Schuhab802d52020-07-03 18:11:11 -07001183 detail::_interpreter::get();
1184
1185 PyObject* xarray = detail::get_array(x);
1186 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001187
1188 PyObject* pystring = PyString_FromString(s.c_str());
1189
1190 PyObject* plot_args = PyTuple_New(3);
1191 PyTuple_SetItem(plot_args, 0, xarray);
1192 PyTuple_SetItem(plot_args, 1, yarray);
1193 PyTuple_SetItem(plot_args, 2, pystring);
1194
1195 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args);
1196
1197 Py_DECREF(plot_args);
1198 if(res) Py_DECREF(res);
1199
1200 return res;
1201}
1202
1203template<typename NumericX, typename NumericY>
1204bool semilogy(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1205{
1206 assert(x.size() == y.size());
1207
Austin Schuhab802d52020-07-03 18:11:11 -07001208 detail::_interpreter::get();
1209
1210 PyObject* xarray = detail::get_array(x);
1211 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001212
1213 PyObject* pystring = PyString_FromString(s.c_str());
1214
1215 PyObject* plot_args = PyTuple_New(3);
1216 PyTuple_SetItem(plot_args, 0, xarray);
1217 PyTuple_SetItem(plot_args, 1, yarray);
1218 PyTuple_SetItem(plot_args, 2, pystring);
1219
1220 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogy, plot_args);
1221
1222 Py_DECREF(plot_args);
1223 if(res) Py_DECREF(res);
1224
1225 return res;
1226}
1227
1228template<typename NumericX, typename NumericY>
1229bool loglog(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1230{
1231 assert(x.size() == y.size());
1232
Austin Schuhab802d52020-07-03 18:11:11 -07001233 detail::_interpreter::get();
1234
1235 PyObject* xarray = detail::get_array(x);
1236 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001237
1238 PyObject* pystring = PyString_FromString(s.c_str());
1239
1240 PyObject* plot_args = PyTuple_New(3);
1241 PyTuple_SetItem(plot_args, 0, xarray);
1242 PyTuple_SetItem(plot_args, 1, yarray);
1243 PyTuple_SetItem(plot_args, 2, pystring);
1244
1245 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_loglog, plot_args);
1246
1247 Py_DECREF(plot_args);
1248 if(res) Py_DECREF(res);
1249
1250 return res;
1251}
1252
1253template<typename NumericX, typename NumericY>
Austin Schuhab802d52020-07-03 18:11:11 -07001254bool errorbar(const std::vector<NumericX> &x, const std::vector<NumericY> &y, const std::vector<NumericX> &yerr, const std::map<std::string, std::string> &keywords = {})
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001255{
1256 assert(x.size() == y.size());
1257
Austin Schuhab802d52020-07-03 18:11:11 -07001258 detail::_interpreter::get();
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001259
Austin Schuhab802d52020-07-03 18:11:11 -07001260 PyObject* xarray = detail::get_array(x);
1261 PyObject* yarray = detail::get_array(y);
1262 PyObject* yerrarray = detail::get_array(yerr);
1263
1264 // construct keyword args
1265 PyObject* kwargs = PyDict_New();
1266 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1267 {
1268 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1269 }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001270
1271 PyDict_SetItemString(kwargs, "yerr", yerrarray);
1272
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001273 PyObject *plot_args = PyTuple_New(2);
1274 PyTuple_SetItem(plot_args, 0, xarray);
1275 PyTuple_SetItem(plot_args, 1, yarray);
1276
1277 PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_errorbar, plot_args, kwargs);
1278
1279 Py_DECREF(kwargs);
1280 Py_DECREF(plot_args);
1281
1282 if (res)
1283 Py_DECREF(res);
1284 else
1285 throw std::runtime_error("Call to errorbar() failed.");
1286
1287 return res;
1288}
1289
1290template<typename Numeric>
1291bool named_plot(const std::string& name, const std::vector<Numeric>& y, const std::string& format = "")
1292{
Austin Schuhab802d52020-07-03 18:11:11 -07001293 detail::_interpreter::get();
1294
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001295 PyObject* kwargs = PyDict_New();
1296 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1297
Austin Schuhab802d52020-07-03 18:11:11 -07001298 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001299
1300 PyObject* pystring = PyString_FromString(format.c_str());
1301
1302 PyObject* plot_args = PyTuple_New(2);
1303
1304 PyTuple_SetItem(plot_args, 0, yarray);
1305 PyTuple_SetItem(plot_args, 1, pystring);
1306
1307 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
1308
1309 Py_DECREF(kwargs);
1310 Py_DECREF(plot_args);
1311 if (res) Py_DECREF(res);
1312
1313 return res;
1314}
1315
1316template<typename Numeric>
1317bool named_plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1318{
Austin Schuhab802d52020-07-03 18:11:11 -07001319 detail::_interpreter::get();
1320
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001321 PyObject* kwargs = PyDict_New();
1322 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1323
Austin Schuhab802d52020-07-03 18:11:11 -07001324 PyObject* xarray = detail::get_array(x);
1325 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001326
1327 PyObject* pystring = PyString_FromString(format.c_str());
1328
1329 PyObject* plot_args = PyTuple_New(3);
1330 PyTuple_SetItem(plot_args, 0, xarray);
1331 PyTuple_SetItem(plot_args, 1, yarray);
1332 PyTuple_SetItem(plot_args, 2, pystring);
1333
1334 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
1335
1336 Py_DECREF(kwargs);
1337 Py_DECREF(plot_args);
1338 if (res) Py_DECREF(res);
1339
1340 return res;
1341}
1342
1343template<typename Numeric>
1344bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1345{
Austin Schuhab802d52020-07-03 18:11:11 -07001346 detail::_interpreter::get();
1347
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001348 PyObject* kwargs = PyDict_New();
1349 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1350
Austin Schuhab802d52020-07-03 18:11:11 -07001351 PyObject* xarray = detail::get_array(x);
1352 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001353
1354 PyObject* pystring = PyString_FromString(format.c_str());
1355
1356 PyObject* plot_args = PyTuple_New(3);
1357 PyTuple_SetItem(plot_args, 0, xarray);
1358 PyTuple_SetItem(plot_args, 1, yarray);
1359 PyTuple_SetItem(plot_args, 2, pystring);
1360
1361 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs);
1362
1363 Py_DECREF(kwargs);
1364 Py_DECREF(plot_args);
1365 if (res) Py_DECREF(res);
1366
1367 return res;
1368}
1369
1370template<typename Numeric>
1371bool named_semilogy(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1372{
Austin Schuhab802d52020-07-03 18:11:11 -07001373 detail::_interpreter::get();
1374
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001375 PyObject* kwargs = PyDict_New();
1376 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1377
Austin Schuhab802d52020-07-03 18:11:11 -07001378 PyObject* xarray = detail::get_array(x);
1379 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001380
1381 PyObject* pystring = PyString_FromString(format.c_str());
1382
1383 PyObject* plot_args = PyTuple_New(3);
1384 PyTuple_SetItem(plot_args, 0, xarray);
1385 PyTuple_SetItem(plot_args, 1, yarray);
1386 PyTuple_SetItem(plot_args, 2, pystring);
1387
1388 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs);
1389
1390 Py_DECREF(kwargs);
1391 Py_DECREF(plot_args);
1392 if (res) Py_DECREF(res);
1393
1394 return res;
1395}
1396
1397template<typename Numeric>
1398bool named_loglog(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1399{
Austin Schuhab802d52020-07-03 18:11:11 -07001400 detail::_interpreter::get();
1401
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001402 PyObject* kwargs = PyDict_New();
1403 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1404
Austin Schuhab802d52020-07-03 18:11:11 -07001405 PyObject* xarray = detail::get_array(x);
1406 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001407
1408 PyObject* pystring = PyString_FromString(format.c_str());
1409
1410 PyObject* plot_args = PyTuple_New(3);
1411 PyTuple_SetItem(plot_args, 0, xarray);
1412 PyTuple_SetItem(plot_args, 1, yarray);
1413 PyTuple_SetItem(plot_args, 2, pystring);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001414 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs);
1415
1416 Py_DECREF(kwargs);
1417 Py_DECREF(plot_args);
1418 if (res) Py_DECREF(res);
1419
1420 return res;
1421}
1422
1423template<typename Numeric>
1424bool plot(const std::vector<Numeric>& y, const std::string& format = "")
1425{
1426 std::vector<Numeric> x(y.size());
1427 for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
1428 return plot(x,y,format);
1429}
1430
1431template<typename Numeric>
Austin Schuhab802d52020-07-03 18:11:11 -07001432bool plot(const std::vector<Numeric>& y, const std::map<std::string, std::string>& keywords)
1433{
1434 std::vector<Numeric> x(y.size());
1435 for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
1436 return plot(x,y,keywords);
1437}
1438
1439template<typename Numeric>
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001440bool stem(const std::vector<Numeric>& y, const std::string& format = "")
1441{
1442 std::vector<Numeric> x(y.size());
1443 for (size_t i = 0; i < x.size(); ++i) x.at(i) = i;
1444 return stem(x, y, format);
1445}
1446
Austin Schuhab802d52020-07-03 18:11:11 -07001447template<typename Numeric>
1448void text(Numeric x, Numeric y, const std::string& s = "")
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001449{
Austin Schuhab802d52020-07-03 18:11:11 -07001450 detail::_interpreter::get();
1451
1452 PyObject* args = PyTuple_New(3);
1453 PyTuple_SetItem(args, 0, PyFloat_FromDouble(x));
1454 PyTuple_SetItem(args, 1, PyFloat_FromDouble(y));
1455 PyTuple_SetItem(args, 2, PyString_FromString(s.c_str()));
1456
1457 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_text, args);
1458 if(!res) throw std::runtime_error("Call to text() failed.");
1459
1460 Py_DECREF(args);
1461 Py_DECREF(res);
1462}
1463
1464inline void colorbar(PyObject* mappable = NULL, const std::map<std::string, float>& keywords = {})
1465{
1466 if (mappable == NULL)
1467 throw std::runtime_error("Must call colorbar with PyObject* returned from an image, contour, surface, etc.");
1468
1469 detail::_interpreter::get();
1470
1471 PyObject* args = PyTuple_New(1);
1472 PyTuple_SetItem(args, 0, mappable);
1473
1474 PyObject* kwargs = PyDict_New();
1475 for(std::map<std::string, float>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1476 {
1477 PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(it->second));
1478 }
1479
1480 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_colorbar, args, kwargs);
1481 if(!res) throw std::runtime_error("Call to colorbar() failed.");
1482
1483 Py_DECREF(args);
1484 Py_DECREF(kwargs);
1485 Py_DECREF(res);
1486}
1487
1488
1489inline long figure(long number = -1)
1490{
1491 detail::_interpreter::get();
1492
1493 PyObject *res;
1494 if (number == -1)
1495 res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple);
1496 else {
1497 assert(number > 0);
1498
1499 // Make sure interpreter is initialised
1500 detail::_interpreter::get();
1501
1502 PyObject *args = PyTuple_New(1);
1503 PyTuple_SetItem(args, 0, PyLong_FromLong(number));
1504 res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, args);
1505 Py_DECREF(args);
1506 }
1507
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001508 if(!res) throw std::runtime_error("Call to figure() failed.");
1509
Austin Schuhab802d52020-07-03 18:11:11 -07001510 PyObject* num = PyObject_GetAttrString(res, "number");
1511 if (!num) throw std::runtime_error("Could not get number attribute of figure object");
1512 const long figureNumber = PyLong_AsLong(num);
1513
1514 Py_DECREF(num);
1515 Py_DECREF(res);
1516
1517 return figureNumber;
1518}
1519
1520inline bool fignum_exists(long number)
1521{
1522 detail::_interpreter::get();
1523
1524 PyObject *args = PyTuple_New(1);
1525 PyTuple_SetItem(args, 0, PyLong_FromLong(number));
1526 PyObject *res = PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, args);
1527 if(!res) throw std::runtime_error("Call to fignum_exists() failed.");
1528
1529 bool ret = PyObject_IsTrue(res);
1530 Py_DECREF(res);
1531 Py_DECREF(args);
1532
1533 return ret;
1534}
1535
1536inline void figure_size(size_t w, size_t h)
1537{
1538 detail::_interpreter::get();
1539
1540 const size_t dpi = 100;
1541 PyObject* size = PyTuple_New(2);
1542 PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi));
1543 PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi));
1544
1545 PyObject* kwargs = PyDict_New();
1546 PyDict_SetItemString(kwargs, "figsize", size);
1547 PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi));
1548
1549 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_figure,
1550 detail::_interpreter::get().s_python_empty_tuple, kwargs);
1551
1552 Py_DECREF(kwargs);
1553
1554 if(!res) throw std::runtime_error("Call to figure_size() failed.");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001555 Py_DECREF(res);
1556}
1557
1558inline void legend()
1559{
Austin Schuhab802d52020-07-03 18:11:11 -07001560 detail::_interpreter::get();
1561
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001562 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple);
1563 if(!res) throw std::runtime_error("Call to legend() failed.");
1564
1565 Py_DECREF(res);
1566}
1567
Austin Schuhab802d52020-07-03 18:11:11 -07001568inline void legend(const std::map<std::string, std::string>& keywords)
1569{
1570 detail::_interpreter::get();
1571
1572 // construct keyword args
1573 PyObject* kwargs = PyDict_New();
1574 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1575 {
1576 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1577 }
1578
1579 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple, kwargs);
1580 if(!res) throw std::runtime_error("Call to legend() failed.");
1581
1582 Py_DECREF(kwargs);
1583 Py_DECREF(res);
1584}
1585
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001586template<typename Numeric>
1587void ylim(Numeric left, Numeric right)
1588{
Austin Schuhab802d52020-07-03 18:11:11 -07001589 detail::_interpreter::get();
1590
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001591 PyObject* list = PyList_New(2);
1592 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
1593 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
1594
1595 PyObject* args = PyTuple_New(1);
1596 PyTuple_SetItem(args, 0, list);
1597
1598 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
1599 if(!res) throw std::runtime_error("Call to ylim() failed.");
1600
1601 Py_DECREF(args);
1602 Py_DECREF(res);
1603}
1604
1605template<typename Numeric>
1606void xlim(Numeric left, Numeric right)
1607{
Austin Schuhab802d52020-07-03 18:11:11 -07001608 detail::_interpreter::get();
1609
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001610 PyObject* list = PyList_New(2);
1611 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
1612 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
1613
1614 PyObject* args = PyTuple_New(1);
1615 PyTuple_SetItem(args, 0, list);
1616
1617 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
1618 if(!res) throw std::runtime_error("Call to xlim() failed.");
1619
1620 Py_DECREF(args);
1621 Py_DECREF(res);
1622}
1623
1624
1625inline double* xlim()
1626{
Austin Schuhab802d52020-07-03 18:11:11 -07001627 detail::_interpreter::get();
1628
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001629 PyObject* args = PyTuple_New(0);
1630 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
1631 PyObject* left = PyTuple_GetItem(res,0);
1632 PyObject* right = PyTuple_GetItem(res,1);
1633
1634 double* arr = new double[2];
1635 arr[0] = PyFloat_AsDouble(left);
1636 arr[1] = PyFloat_AsDouble(right);
1637
1638 if(!res) throw std::runtime_error("Call to xlim() failed.");
1639
1640 Py_DECREF(res);
1641 return arr;
1642}
1643
1644
1645inline double* ylim()
1646{
Austin Schuhab802d52020-07-03 18:11:11 -07001647 detail::_interpreter::get();
1648
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001649 PyObject* args = PyTuple_New(0);
1650 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
1651 PyObject* left = PyTuple_GetItem(res,0);
1652 PyObject* right = PyTuple_GetItem(res,1);
1653
1654 double* arr = new double[2];
1655 arr[0] = PyFloat_AsDouble(left);
1656 arr[1] = PyFloat_AsDouble(right);
1657
1658 if(!res) throw std::runtime_error("Call to ylim() failed.");
1659
1660 Py_DECREF(res);
1661 return arr;
1662}
1663
Austin Schuhab802d52020-07-03 18:11:11 -07001664template<typename Numeric>
1665inline void xticks(const std::vector<Numeric> &ticks, const std::vector<std::string> &labels = {}, const std::map<std::string, std::string>& keywords = {})
1666{
1667 assert(labels.size() == 0 || ticks.size() == labels.size());
1668
1669 detail::_interpreter::get();
1670
1671 // using numpy array
1672 PyObject* ticksarray = detail::get_array(ticks);
1673
1674 PyObject* args;
1675 if(labels.size() == 0) {
1676 // construct positional args
1677 args = PyTuple_New(1);
1678 PyTuple_SetItem(args, 0, ticksarray);
1679 } else {
1680 // make tuple of tick labels
1681 PyObject* labelstuple = PyTuple_New(labels.size());
1682 for (size_t i = 0; i < labels.size(); i++)
1683 PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str()));
1684
1685 // construct positional args
1686 args = PyTuple_New(2);
1687 PyTuple_SetItem(args, 0, ticksarray);
1688 PyTuple_SetItem(args, 1, labelstuple);
1689 }
1690
1691 // construct keyword args
1692 PyObject* kwargs = PyDict_New();
1693 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1694 {
1695 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1696 }
1697
1698 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xticks, args, kwargs);
1699
1700 Py_DECREF(args);
1701 Py_DECREF(kwargs);
1702 if(!res) throw std::runtime_error("Call to xticks() failed");
1703
1704 Py_DECREF(res);
1705}
1706
1707template<typename Numeric>
1708inline void xticks(const std::vector<Numeric> &ticks, const std::map<std::string, std::string>& keywords)
1709{
1710 xticks(ticks, {}, keywords);
1711}
1712
1713template<typename Numeric>
1714inline void yticks(const std::vector<Numeric> &ticks, const std::vector<std::string> &labels = {}, const std::map<std::string, std::string>& keywords = {})
1715{
1716 assert(labels.size() == 0 || ticks.size() == labels.size());
1717
1718 detail::_interpreter::get();
1719
1720 // using numpy array
1721 PyObject* ticksarray = detail::get_array(ticks);
1722
1723 PyObject* args;
1724 if(labels.size() == 0) {
1725 // construct positional args
1726 args = PyTuple_New(1);
1727 PyTuple_SetItem(args, 0, ticksarray);
1728 } else {
1729 // make tuple of tick labels
1730 PyObject* labelstuple = PyTuple_New(labels.size());
1731 for (size_t i = 0; i < labels.size(); i++)
1732 PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str()));
1733
1734 // construct positional args
1735 args = PyTuple_New(2);
1736 PyTuple_SetItem(args, 0, ticksarray);
1737 PyTuple_SetItem(args, 1, labelstuple);
1738 }
1739
1740 // construct keyword args
1741 PyObject* kwargs = PyDict_New();
1742 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1743 {
1744 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1745 }
1746
1747 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_yticks, args, kwargs);
1748
1749 Py_DECREF(args);
1750 Py_DECREF(kwargs);
1751 if(!res) throw std::runtime_error("Call to yticks() failed");
1752
1753 Py_DECREF(res);
1754}
1755
1756template<typename Numeric>
1757inline void yticks(const std::vector<Numeric> &ticks, const std::map<std::string, std::string>& keywords)
1758{
1759 yticks(ticks, {}, keywords);
1760}
1761
1762template <typename Numeric> inline void margins(Numeric margin)
1763{
1764 // construct positional args
1765 PyObject* args = PyTuple_New(1);
1766 PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin));
1767
1768 PyObject* res =
1769 PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args);
1770 if (!res)
1771 throw std::runtime_error("Call to margins() failed.");
1772
1773 Py_DECREF(args);
1774 Py_DECREF(res);
1775}
1776
1777template <typename Numeric> inline void margins(Numeric margin_x, Numeric margin_y)
1778{
1779 // construct positional args
1780 PyObject* args = PyTuple_New(2);
1781 PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin_x));
1782 PyTuple_SetItem(args, 1, PyFloat_FromDouble(margin_y));
1783
1784 PyObject* res =
1785 PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args);
1786 if (!res)
1787 throw std::runtime_error("Call to margins() failed.");
1788
1789 Py_DECREF(args);
1790 Py_DECREF(res);
1791}
1792
1793
1794inline void tick_params(const std::map<std::string, std::string>& keywords, const std::string axis = "both")
1795{
1796 detail::_interpreter::get();
1797
1798 // construct positional args
1799 PyObject* args;
1800 args = PyTuple_New(1);
1801 PyTuple_SetItem(args, 0, PyString_FromString(axis.c_str()));
1802
1803 // construct keyword args
1804 PyObject* kwargs = PyDict_New();
1805 for (std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1806 {
1807 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1808 }
1809
1810
1811 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_tick_params, args, kwargs);
1812
1813 Py_DECREF(args);
1814 Py_DECREF(kwargs);
1815 if (!res) throw std::runtime_error("Call to tick_params() failed");
1816
1817 Py_DECREF(res);
1818}
1819
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001820inline void subplot(long nrows, long ncols, long plot_number)
1821{
Austin Schuhab802d52020-07-03 18:11:11 -07001822 detail::_interpreter::get();
1823
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001824 // construct positional args
1825 PyObject* args = PyTuple_New(3);
1826 PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows));
1827 PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols));
1828 PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number));
1829
1830 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args);
1831 if(!res) throw std::runtime_error("Call to subplot() failed.");
1832
1833 Py_DECREF(args);
1834 Py_DECREF(res);
1835}
1836
Austin Schuhab802d52020-07-03 18:11:11 -07001837inline void subplot2grid(long nrows, long ncols, long rowid=0, long colid=0, long rowspan=1, long colspan=1)
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001838{
Austin Schuhab802d52020-07-03 18:11:11 -07001839 detail::_interpreter::get();
1840
1841 PyObject* shape = PyTuple_New(2);
1842 PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows));
1843 PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols));
1844
1845 PyObject* loc = PyTuple_New(2);
1846 PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid));
1847 PyTuple_SetItem(loc, 1, PyLong_FromLong(colid));
1848
1849 PyObject* args = PyTuple_New(4);
1850 PyTuple_SetItem(args, 0, shape);
1851 PyTuple_SetItem(args, 1, loc);
1852 PyTuple_SetItem(args, 2, PyLong_FromLong(rowspan));
1853 PyTuple_SetItem(args, 3, PyLong_FromLong(colspan));
1854
1855 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot2grid, args);
1856 if(!res) throw std::runtime_error("Call to subplot2grid() failed.");
1857
1858 Py_DECREF(shape);
1859 Py_DECREF(loc);
1860 Py_DECREF(args);
1861 Py_DECREF(res);
1862}
1863
1864inline void title(const std::string &titlestr, const std::map<std::string, std::string> &keywords = {})
1865{
1866 detail::_interpreter::get();
1867
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001868 PyObject* pytitlestr = PyString_FromString(titlestr.c_str());
1869 PyObject* args = PyTuple_New(1);
1870 PyTuple_SetItem(args, 0, pytitlestr);
1871
Austin Schuhab802d52020-07-03 18:11:11 -07001872 PyObject* kwargs = PyDict_New();
1873 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
1874 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1875 }
1876
1877 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_title, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001878 if(!res) throw std::runtime_error("Call to title() failed.");
1879
1880 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07001881 Py_DECREF(kwargs);
1882 Py_DECREF(res);
1883}
1884
1885inline void suptitle(const std::string &suptitlestr, const std::map<std::string, std::string> &keywords = {})
1886{
1887 detail::_interpreter::get();
1888
1889 PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str());
1890 PyObject* args = PyTuple_New(1);
1891 PyTuple_SetItem(args, 0, pysuptitlestr);
1892
1893 PyObject* kwargs = PyDict_New();
1894 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
1895 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1896 }
1897
1898 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_suptitle, args, kwargs);
1899 if(!res) throw std::runtime_error("Call to suptitle() failed.");
1900
1901 Py_DECREF(args);
1902 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001903 Py_DECREF(res);
1904}
1905
1906inline void axis(const std::string &axisstr)
1907{
Austin Schuhab802d52020-07-03 18:11:11 -07001908 detail::_interpreter::get();
1909
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001910 PyObject* str = PyString_FromString(axisstr.c_str());
1911 PyObject* args = PyTuple_New(1);
1912 PyTuple_SetItem(args, 0, str);
1913
1914 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args);
1915 if(!res) throw std::runtime_error("Call to title() failed.");
1916
1917 Py_DECREF(args);
1918 Py_DECREF(res);
1919}
1920
Austin Schuhab802d52020-07-03 18:11:11 -07001921inline void axvline(double x, double ymin = 0., double ymax = 1., const std::map<std::string, std::string>& keywords = std::map<std::string, std::string>())
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001922{
Austin Schuhab802d52020-07-03 18:11:11 -07001923 detail::_interpreter::get();
1924
1925 // construct positional args
1926 PyObject* args = PyTuple_New(3);
1927 PyTuple_SetItem(args, 0, PyFloat_FromDouble(x));
1928 PyTuple_SetItem(args, 1, PyFloat_FromDouble(ymin));
1929 PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymax));
1930
1931 // construct keyword args
1932 PyObject* kwargs = PyDict_New();
1933 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1934 {
1935 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1936 }
1937
1938 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvline, args, kwargs);
1939
1940 Py_DECREF(args);
1941 Py_DECREF(kwargs);
1942
1943 if(res) Py_DECREF(res);
1944}
1945
1946inline void axvspan(double xmin, double xmax, double ymin = 0., double ymax = 1., const std::map<std::string, std::string>& keywords = std::map<std::string, std::string>())
1947{
1948 // construct positional args
1949 PyObject* args = PyTuple_New(4);
1950 PyTuple_SetItem(args, 0, PyFloat_FromDouble(xmin));
1951 PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmax));
1952 PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymin));
1953 PyTuple_SetItem(args, 3, PyFloat_FromDouble(ymax));
1954
1955 // construct keyword args
1956 PyObject* kwargs = PyDict_New();
1957 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1958 {
1959 if (it->first == "linewidth" || it->first == "alpha")
1960 PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(std::stod(it->second)));
1961 else
1962 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1963 }
1964
1965 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvspan, args, kwargs);
1966 Py_DECREF(args);
1967 Py_DECREF(kwargs);
1968
1969 if(res) Py_DECREF(res);
1970}
1971
1972inline void xlabel(const std::string &str, const std::map<std::string, std::string> &keywords = {})
1973{
1974 detail::_interpreter::get();
1975
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001976 PyObject* pystr = PyString_FromString(str.c_str());
1977 PyObject* args = PyTuple_New(1);
1978 PyTuple_SetItem(args, 0, pystr);
1979
Austin Schuhab802d52020-07-03 18:11:11 -07001980 PyObject* kwargs = PyDict_New();
1981 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
1982 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1983 }
1984
1985 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xlabel, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001986 if(!res) throw std::runtime_error("Call to xlabel() failed.");
1987
1988 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07001989 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001990 Py_DECREF(res);
1991}
1992
Austin Schuhab802d52020-07-03 18:11:11 -07001993inline void ylabel(const std::string &str, const std::map<std::string, std::string>& keywords = {})
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001994{
Austin Schuhab802d52020-07-03 18:11:11 -07001995 detail::_interpreter::get();
1996
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001997 PyObject* pystr = PyString_FromString(str.c_str());
1998 PyObject* args = PyTuple_New(1);
1999 PyTuple_SetItem(args, 0, pystr);
2000
Austin Schuhab802d52020-07-03 18:11:11 -07002001 PyObject* kwargs = PyDict_New();
2002 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2003 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2004 }
2005
2006 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_ylabel, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002007 if(!res) throw std::runtime_error("Call to ylabel() failed.");
2008
2009 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07002010 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002011 Py_DECREF(res);
2012}
2013
Austin Schuhab802d52020-07-03 18:11:11 -07002014inline void set_zlabel(const std::string &str, const std::map<std::string, std::string>& keywords = {})
2015{
2016 detail::_interpreter::get();
2017
2018 // Same as with plot_surface: We lazily load the modules here the first time
2019 // this function is called because I'm not sure that we can assume "matplotlib
2020 // installed" implies "mpl_toolkits installed" on all platforms, and we don't
2021 // want to require it for people who don't need 3d plots.
2022 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
2023 if (!mpl_toolkitsmod) {
2024 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
2025 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
2026 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
2027
2028 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
2029 Py_DECREF(mpl_toolkits);
2030 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
2031
2032 axis3dmod = PyImport_Import(axis3d);
2033 Py_DECREF(axis3d);
2034 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
2035 }
2036
2037 PyObject* pystr = PyString_FromString(str.c_str());
2038 PyObject* args = PyTuple_New(1);
2039 PyTuple_SetItem(args, 0, pystr);
2040
2041 PyObject* kwargs = PyDict_New();
2042 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2043 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2044 }
2045
2046 PyObject *ax =
2047 PyObject_CallObject(detail::_interpreter::get().s_python_function_gca,
2048 detail::_interpreter::get().s_python_empty_tuple);
2049 if (!ax) throw std::runtime_error("Call to gca() failed.");
2050 Py_INCREF(ax);
2051
2052 PyObject *zlabel = PyObject_GetAttrString(ax, "set_zlabel");
2053 if (!zlabel) throw std::runtime_error("Attribute set_zlabel not found.");
2054 Py_INCREF(zlabel);
2055
2056 PyObject *res = PyObject_Call(zlabel, args, kwargs);
2057 if (!res) throw std::runtime_error("Call to set_zlabel() failed.");
2058 Py_DECREF(zlabel);
2059
2060 Py_DECREF(ax);
2061 Py_DECREF(args);
2062 Py_DECREF(kwargs);
2063 if (res) Py_DECREF(res);
2064}
2065
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002066inline void grid(bool flag)
2067{
Austin Schuhab802d52020-07-03 18:11:11 -07002068 detail::_interpreter::get();
2069
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002070 PyObject* pyflag = flag ? Py_True : Py_False;
2071 Py_INCREF(pyflag);
2072
2073 PyObject* args = PyTuple_New(1);
2074 PyTuple_SetItem(args, 0, pyflag);
2075
2076 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args);
2077 if(!res) throw std::runtime_error("Call to grid() failed.");
2078
2079 Py_DECREF(args);
2080 Py_DECREF(res);
2081}
2082
2083inline void show(const bool block = true)
2084{
Austin Schuhab802d52020-07-03 18:11:11 -07002085 detail::_interpreter::get();
2086
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002087 PyObject* res;
2088 if(block)
2089 {
2090 res = PyObject_CallObject(
2091 detail::_interpreter::get().s_python_function_show,
2092 detail::_interpreter::get().s_python_empty_tuple);
2093 }
2094 else
2095 {
2096 PyObject *kwargs = PyDict_New();
2097 PyDict_SetItemString(kwargs, "block", Py_False);
2098 res = PyObject_Call( detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple, kwargs);
Austin Schuhab802d52020-07-03 18:11:11 -07002099 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002100 }
2101
2102
2103 if (!res) throw std::runtime_error("Call to show() failed.");
2104
2105 Py_DECREF(res);
2106}
2107
2108inline void close()
2109{
Austin Schuhab802d52020-07-03 18:11:11 -07002110 detail::_interpreter::get();
2111
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002112 PyObject* res = PyObject_CallObject(
2113 detail::_interpreter::get().s_python_function_close,
2114 detail::_interpreter::get().s_python_empty_tuple);
2115
2116 if (!res) throw std::runtime_error("Call to close() failed.");
2117
2118 Py_DECREF(res);
2119}
2120
2121inline void xkcd() {
Austin Schuhab802d52020-07-03 18:11:11 -07002122 detail::_interpreter::get();
2123
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002124 PyObject* res;
2125 PyObject *kwargs = PyDict_New();
2126
2127 res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd,
2128 detail::_interpreter::get().s_python_empty_tuple, kwargs);
2129
2130 Py_DECREF(kwargs);
2131
2132 if (!res)
2133 throw std::runtime_error("Call to show() failed.");
2134
2135 Py_DECREF(res);
2136}
2137
2138inline void draw()
2139{
Austin Schuhab802d52020-07-03 18:11:11 -07002140 detail::_interpreter::get();
2141
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002142 PyObject* res = PyObject_CallObject(
2143 detail::_interpreter::get().s_python_function_draw,
2144 detail::_interpreter::get().s_python_empty_tuple);
2145
2146 if (!res) throw std::runtime_error("Call to draw() failed.");
2147
2148 Py_DECREF(res);
2149}
2150
2151template<typename Numeric>
2152inline void pause(Numeric interval)
2153{
Austin Schuhab802d52020-07-03 18:11:11 -07002154 detail::_interpreter::get();
2155
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002156 PyObject* args = PyTuple_New(1);
2157 PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval));
2158
2159 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args);
2160 if(!res) throw std::runtime_error("Call to pause() failed.");
2161
2162 Py_DECREF(args);
2163 Py_DECREF(res);
2164}
2165
2166inline void save(const std::string& filename)
2167{
Austin Schuhab802d52020-07-03 18:11:11 -07002168 detail::_interpreter::get();
2169
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002170 PyObject* pyfilename = PyString_FromString(filename.c_str());
2171
2172 PyObject* args = PyTuple_New(1);
2173 PyTuple_SetItem(args, 0, pyfilename);
2174
2175 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args);
2176 if (!res) throw std::runtime_error("Call to save() failed.");
2177
2178 Py_DECREF(args);
2179 Py_DECREF(res);
2180}
2181
2182inline void clf() {
Austin Schuhab802d52020-07-03 18:11:11 -07002183 detail::_interpreter::get();
2184
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002185 PyObject *res = PyObject_CallObject(
2186 detail::_interpreter::get().s_python_function_clf,
2187 detail::_interpreter::get().s_python_empty_tuple);
2188
2189 if (!res) throw std::runtime_error("Call to clf() failed.");
2190
2191 Py_DECREF(res);
2192}
2193
Austin Schuhab802d52020-07-03 18:11:11 -07002194inline void cla() {
2195 detail::_interpreter::get();
2196
2197 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_cla,
2198 detail::_interpreter::get().s_python_empty_tuple);
2199
2200 if (!res)
2201 throw std::runtime_error("Call to cla() failed.");
2202
2203 Py_DECREF(res);
2204}
2205
2206inline void ion() {
2207 detail::_interpreter::get();
2208
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002209 PyObject *res = PyObject_CallObject(
2210 detail::_interpreter::get().s_python_function_ion,
2211 detail::_interpreter::get().s_python_empty_tuple);
2212
2213 if (!res) throw std::runtime_error("Call to ion() failed.");
2214
2215 Py_DECREF(res);
2216}
2217
Austin Schuhab802d52020-07-03 18:11:11 -07002218inline std::vector<std::array<double, 2>> ginput(const int numClicks = 1, const std::map<std::string, std::string>& keywords = {})
2219{
2220 detail::_interpreter::get();
2221
2222 PyObject *args = PyTuple_New(1);
2223 PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks));
2224
2225 // construct keyword args
2226 PyObject* kwargs = PyDict_New();
2227 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
2228 {
2229 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2230 }
2231
2232 PyObject* res = PyObject_Call(
2233 detail::_interpreter::get().s_python_function_ginput, args, kwargs);
2234
2235 Py_DECREF(kwargs);
2236 Py_DECREF(args);
2237 if (!res) throw std::runtime_error("Call to ginput() failed.");
2238
2239 const size_t len = PyList_Size(res);
2240 std::vector<std::array<double, 2>> out;
2241 out.reserve(len);
2242 for (size_t i = 0; i < len; i++) {
2243 PyObject *current = PyList_GetItem(res, i);
2244 std::array<double, 2> position;
2245 position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0));
2246 position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1));
2247 out.push_back(position);
2248 }
2249 Py_DECREF(res);
2250
2251 return out;
2252}
2253
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002254// Actually, is there any reason not to call this automatically for every plot?
2255inline void tight_layout() {
Austin Schuhab802d52020-07-03 18:11:11 -07002256 detail::_interpreter::get();
2257
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002258 PyObject *res = PyObject_CallObject(
2259 detail::_interpreter::get().s_python_function_tight_layout,
2260 detail::_interpreter::get().s_python_empty_tuple);
2261
2262 if (!res) throw std::runtime_error("Call to tight_layout() failed.");
2263
2264 Py_DECREF(res);
2265}
2266
Austin Schuhab802d52020-07-03 18:11:11 -07002267// Support for variadic plot() and initializer lists:
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002268
2269namespace detail {
2270
2271template<typename T>
2272using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
2273
2274template<bool obj, typename T>
2275struct is_callable_impl;
2276
2277template<typename T>
2278struct is_callable_impl<false, T>
2279{
2280 typedef is_function<T> type;
2281}; // a non-object is callable iff it is a function
2282
2283template<typename T>
2284struct is_callable_impl<true, T>
2285{
2286 struct Fallback { void operator()(); };
2287 struct Derived : T, Fallback { };
2288
2289 template<typename U, U> struct Check;
2290
2291 template<typename U>
2292 static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match
2293
2294 template<typename U>
2295 static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
2296
2297public:
2298 typedef decltype(test<Derived>(nullptr)) type;
2299 typedef decltype(&Fallback::operator()) dtype;
2300 static constexpr bool value = type::value;
2301}; // an object is callable iff it defines operator()
2302
2303template<typename T>
2304struct is_callable
2305{
2306 // dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
2307 typedef typename is_callable_impl<std::is_class<T>::value, T>::type type;
2308};
2309
2310template<typename IsYDataCallable>
2311struct plot_impl { };
2312
2313template<>
2314struct plot_impl<std::false_type>
2315{
2316 template<typename IterableX, typename IterableY>
2317 bool operator()(const IterableX& x, const IterableY& y, const std::string& format)
2318 {
2319 // 2-phase lookup for distance, begin, end
2320 using std::distance;
2321 using std::begin;
2322 using std::end;
2323
2324 auto xs = distance(begin(x), end(x));
2325 auto ys = distance(begin(y), end(y));
2326 assert(xs == ys && "x and y data must have the same number of elements!");
2327
2328 PyObject* xlist = PyList_New(xs);
2329 PyObject* ylist = PyList_New(ys);
2330 PyObject* pystring = PyString_FromString(format.c_str());
2331
2332 auto itx = begin(x), ity = begin(y);
2333 for(size_t i = 0; i < xs; ++i) {
2334 PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++));
2335 PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++));
2336 }
2337
2338 PyObject* plot_args = PyTuple_New(3);
2339 PyTuple_SetItem(plot_args, 0, xlist);
2340 PyTuple_SetItem(plot_args, 1, ylist);
2341 PyTuple_SetItem(plot_args, 2, pystring);
2342
2343 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
2344
2345 Py_DECREF(plot_args);
2346 if(res) Py_DECREF(res);
2347
2348 return res;
2349 }
2350};
2351
2352template<>
2353struct plot_impl<std::true_type>
2354{
2355 template<typename Iterable, typename Callable>
2356 bool operator()(const Iterable& ticks, const Callable& f, const std::string& format)
2357 {
2358 if(begin(ticks) == end(ticks)) return true;
2359
2360 // We could use additional meta-programming to deduce the correct element type of y,
2361 // but all values have to be convertible to double anyways
2362 std::vector<double> y;
2363 for(auto x : ticks) y.push_back(f(x));
2364 return plot_impl<std::false_type>()(ticks,y,format);
2365 }
2366};
2367
2368} // end namespace detail
2369
2370// recursion stop for the above
2371template<typename... Args>
2372bool plot() { return true; }
2373
2374template<typename A, typename B, typename... Args>
2375bool plot(const A& a, const B& b, const std::string& format, Args... args)
2376{
2377 return detail::plot_impl<typename detail::is_callable<B>::type>()(a,b,format) && plot(args...);
2378}
2379
2380/*
2381 * This group of plot() functions is needed to support initializer lists, i.e. calling
2382 * plot( {1,2,3,4} )
2383 */
2384inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::string& format = "") {
2385 return plot<double,double>(x,y,format);
2386}
2387
2388inline bool plot(const std::vector<double>& y, const std::string& format = "") {
2389 return plot<double>(y,format);
2390}
2391
2392inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::map<std::string, std::string>& keywords) {
2393 return plot<double>(x,y,keywords);
2394}
2395
Austin Schuhab802d52020-07-03 18:11:11 -07002396/*
2397 * This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting
2398 */
2399class Plot
2400{
2401public:
2402 // default initialization with plot label, some data and format
2403 template<typename Numeric>
2404 Plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") {
2405 detail::_interpreter::get();
2406
2407 assert(x.size() == y.size());
2408
2409 PyObject* kwargs = PyDict_New();
2410 if(name != "")
2411 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
2412
2413 PyObject* xarray = detail::get_array(x);
2414 PyObject* yarray = detail::get_array(y);
2415
2416 PyObject* pystring = PyString_FromString(format.c_str());
2417
2418 PyObject* plot_args = PyTuple_New(3);
2419 PyTuple_SetItem(plot_args, 0, xarray);
2420 PyTuple_SetItem(plot_args, 1, yarray);
2421 PyTuple_SetItem(plot_args, 2, pystring);
2422
2423 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
2424
2425 Py_DECREF(kwargs);
2426 Py_DECREF(plot_args);
2427
2428 if(res)
2429 {
2430 line= PyList_GetItem(res, 0);
2431
2432 if(line)
2433 set_data_fct = PyObject_GetAttrString(line,"set_data");
2434 else
2435 Py_DECREF(line);
2436 Py_DECREF(res);
2437 }
2438 }
2439
2440 // shorter initialization with name or format only
2441 // basically calls line, = plot([], [])
2442 Plot(const std::string& name = "", const std::string& format = "")
2443 : Plot(name, std::vector<double>(), std::vector<double>(), format) {}
2444
2445 template<typename Numeric>
2446 bool update(const std::vector<Numeric>& x, const std::vector<Numeric>& y) {
2447 assert(x.size() == y.size());
2448 if(set_data_fct)
2449 {
2450 PyObject* xarray = detail::get_array(x);
2451 PyObject* yarray = detail::get_array(y);
2452
2453 PyObject* plot_args = PyTuple_New(2);
2454 PyTuple_SetItem(plot_args, 0, xarray);
2455 PyTuple_SetItem(plot_args, 1, yarray);
2456
2457 PyObject* res = PyObject_CallObject(set_data_fct, plot_args);
2458 if (res) Py_DECREF(res);
2459 return res;
2460 }
2461 return false;
2462 }
2463
2464 // clears the plot but keep it available
2465 bool clear() {
2466 return update(std::vector<double>(), std::vector<double>());
2467 }
2468
2469 // definitely remove this line
2470 void remove() {
2471 if(line)
2472 {
2473 auto remove_fct = PyObject_GetAttrString(line,"remove");
2474 PyObject* args = PyTuple_New(0);
2475 PyObject* res = PyObject_CallObject(remove_fct, args);
2476 if (res) Py_DECREF(res);
2477 }
2478 decref();
2479 }
2480
2481 ~Plot() {
2482 decref();
2483 }
2484private:
2485
2486 void decref() {
2487 if(line)
2488 Py_DECREF(line);
2489 if(set_data_fct)
2490 Py_DECREF(set_data_fct);
2491 }
2492
2493
2494 PyObject* line = nullptr;
2495 PyObject* set_data_fct = nullptr;
2496};
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002497
2498} // end namespace matplotlibcpp