blob: 1c8bdc19a6623e17dc36becd010aae3199987258 [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 Schuhad596222018-01-31 23:34:03 -080092 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() {
Austin Schuhc4a833d2020-07-03 18:14:58 -0700146 // Force PYTHONHOME and PYTHONPATH to our sandboxed python.
Philipp Schrader1f0a3d02022-12-21 20:30:42 -0800147#if defined(FRC971_DEBIAN_BUNDLED_PYTHON)
Austin Schuhc4a833d2020-07-03 18:14:58 -0700148 wchar_t python_home[] = L"../python_repo/usr/";
149 wchar_t python_path[] =
150 L"../matplotlib_repo/3:../python_repo/usr/lib/python35.zip:../"
151 L"python_repo/usr/lib/python3.5:../python_repo/usr/lib/python3.5/"
152 L"plat-x86_64-linux-gnu:../python_repo/usr/lib/python3.5/"
153 L"lib-dynload:../python_repo/usr/lib/python3/dist-packages";
154
155 Py_SetPath(python_path);
156 Py_SetPythonHome(python_home);
Philipp Schrader1f0a3d02022-12-21 20:30:42 -0800157#elif defined(FRC971_UPSTREAM_BUNDLED_PYTHON)
158 wchar_t python_home[] = L"../python3_9_x86_64-unknown-linux-gnu/";
159 Py_SetPythonHome(python_home);
160
161 char python_path[] = "PYTHONPATH="
162 "../pip_deps_matplotlib/site-packages/:"
163 "../pip_deps_numpy/site-packages/:"
164 "../pip_deps_pillow/site-packages/:"
165 "../pip_deps_pycairo/site-packages/:"
166 "../pip_deps_pygobject/site-packages/:"
167 "../pip_deps_python_dateutil/site-packages/:"
168 "../pip_deps_six/site-packages/:"
169 "../";
170 putenv(python_path);
171#else
172#error Need one of the two defined.
173#endif
Austin Schuhc4a833d2020-07-03 18:14:58 -0700174
175 // We fail really poorly if DISPLAY isn't set. We can do better.
176 if (getenv("DISPLAY") == nullptr) {
177 fprintf(stderr, "DISPLAY not set\n");
178 abort();
179 }
180
181 // TODO(austin): Confirm LD_LIBRARY_PATH does the right thing. Can't
182 // hurt.
183 setenv("LD_LIBRARY_PATH",
184 "../python_repo/lib/x86_64-linux-gnu:../python_repo/usr/lib:../"
185 "python_repo/usr/lib/x86_64-linux-gnu", 0);
186 Py_DontWriteBytecodeFlag = 1;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800187
188 // optional but recommended
189#if PY_MAJOR_VERSION >= 3
190 wchar_t name[] = L"plotting";
191#else
192 char name[] = "plotting";
193#endif
194 Py_SetProgramName(name);
195 Py_Initialize();
196
197#ifndef WITHOUT_NUMPY
198 import_numpy(); // initialize numpy C-API
199#endif
200
201 PyObject* matplotlibname = PyString_FromString("matplotlib");
202 PyObject* pyplotname = PyString_FromString("matplotlib.pyplot");
Austin Schuhad596222018-01-31 23:34:03 -0800203 PyObject* cmname = PyString_FromString("matplotlib.cm");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800204 PyObject* pylabname = PyString_FromString("pylab");
Austin Schuhab802d52020-07-03 18:11:11 -0700205 if (!pyplotname || !pylabname || !matplotlibname || !cmname) {
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800206 throw std::runtime_error("couldnt create string");
207 }
208
209 PyObject* matplotlib = PyImport_Import(matplotlibname);
210 Py_DECREF(matplotlibname);
Austin Schuhab802d52020-07-03 18:11:11 -0700211 if (!matplotlib) {
212 PyErr_Print();
213 throw std::runtime_error("Error loading module matplotlib!");
214 }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800215
Philipp Schrader1f0a3d02022-12-21 20:30:42 -0800216#if defined(FRC971_UPSTREAM_BUNDLED_PYTHON)
217 // We don't support tkinter with Python from rules_python. We use the
218 // GTK backend instead.
219 // https://github.com/matplotlib/matplotlib/issues/23074
220 s_backend = "GTK3Agg";
221#endif
222
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800223 // matplotlib.use() must be called *before* pylab, matplotlib.pyplot,
224 // or matplotlib.backends is imported for the first time
225 if (!s_backend.empty()) {
226 PyObject_CallMethod(matplotlib, const_cast<char*>("use"), const_cast<char*>("s"), s_backend.c_str());
227 }
228
229 PyObject* pymod = PyImport_Import(pyplotname);
230 Py_DECREF(pyplotname);
231 if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); }
232
Austin Schuhad596222018-01-31 23:34:03 -0800233 s_python_colormap = PyImport_Import(cmname);
234 Py_DECREF(cmname);
235 if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800236
237 PyObject* pylabmod = PyImport_Import(pylabname);
238 Py_DECREF(pylabname);
239 if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); }
240
Austin Schuhab802d52020-07-03 18:11:11 -0700241 s_python_function_arrow = safe_import(pymod, "arrow");
242 s_python_function_show = safe_import(pymod, "show");
243 s_python_function_close = safe_import(pymod, "close");
244 s_python_function_draw = safe_import(pymod, "draw");
245 s_python_function_pause = safe_import(pymod, "pause");
246 s_python_function_figure = safe_import(pymod, "figure");
247 s_python_function_fignum_exists = safe_import(pymod, "fignum_exists");
248 s_python_function_plot = safe_import(pymod, "plot");
249 s_python_function_quiver = safe_import(pymod, "quiver");
250 s_python_function_contour = safe_import(pymod, "contour");
251 s_python_function_semilogx = safe_import(pymod, "semilogx");
252 s_python_function_semilogy = safe_import(pymod, "semilogy");
253 s_python_function_loglog = safe_import(pymod, "loglog");
254 s_python_function_fill = safe_import(pymod, "fill");
255 s_python_function_fill_between = safe_import(pymod, "fill_between");
256 s_python_function_hist = safe_import(pymod,"hist");
257 s_python_function_scatter = safe_import(pymod,"scatter");
258 s_python_function_boxplot = safe_import(pymod,"boxplot");
259 s_python_function_subplot = safe_import(pymod, "subplot");
260 s_python_function_subplot2grid = safe_import(pymod, "subplot2grid");
261 s_python_function_legend = safe_import(pymod, "legend");
262 s_python_function_ylim = safe_import(pymod, "ylim");
263 s_python_function_title = safe_import(pymod, "title");
264 s_python_function_axis = safe_import(pymod, "axis");
265 s_python_function_axvline = safe_import(pymod, "axvline");
266 s_python_function_axvspan = safe_import(pymod, "axvspan");
267 s_python_function_xlabel = safe_import(pymod, "xlabel");
268 s_python_function_ylabel = safe_import(pymod, "ylabel");
269 s_python_function_gca = safe_import(pymod, "gca");
270 s_python_function_xticks = safe_import(pymod, "xticks");
271 s_python_function_yticks = safe_import(pymod, "yticks");
272 s_python_function_margins = safe_import(pymod, "margins");
273 s_python_function_tick_params = safe_import(pymod, "tick_params");
274 s_python_function_grid = safe_import(pymod, "grid");
275 s_python_function_xlim = safe_import(pymod, "xlim");
276 s_python_function_ion = safe_import(pymod, "ion");
277 s_python_function_ginput = safe_import(pymod, "ginput");
278 s_python_function_save = safe_import(pylabmod, "savefig");
279 s_python_function_annotate = safe_import(pymod,"annotate");
280 s_python_function_cla = safe_import(pymod, "cla");
281 s_python_function_clf = safe_import(pymod, "clf");
282 s_python_function_errorbar = safe_import(pymod, "errorbar");
283 s_python_function_tight_layout = safe_import(pymod, "tight_layout");
284 s_python_function_stem = safe_import(pymod, "stem");
285 s_python_function_xkcd = safe_import(pymod, "xkcd");
286 s_python_function_text = safe_import(pymod, "text");
287 s_python_function_suptitle = safe_import(pymod, "suptitle");
288 s_python_function_bar = safe_import(pymod,"bar");
289 s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar");
290 s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust");
291#ifndef WITHOUT_NUMPY
292 s_python_function_imshow = safe_import(pymod, "imshow");
293#endif
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800294 s_python_empty_tuple = PyTuple_New(0);
295 }
296
297 ~_interpreter() {
298 Py_Finalize();
299 }
300};
301
302} // end namespace detail
303
Austin Schuhab802d52020-07-03 18:11:11 -0700304/// Select the backend
305///
306/// **NOTE:** This must be called before the first plot command to have
307/// any effect.
308///
309/// Mainly useful to select the non-interactive 'Agg' backend when running
310/// matplotlibcpp in headless mode, for example on a machine with no display.
311///
312/// See also: https://matplotlib.org/2.0.2/api/matplotlib_configuration_api.html#matplotlib.use
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800313inline void backend(const std::string& name)
314{
315 detail::s_backend = name;
316}
317
318inline bool annotate(std::string annotation, double x, double y)
319{
Austin Schuhab802d52020-07-03 18:11:11 -0700320 detail::_interpreter::get();
321
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800322 PyObject * xy = PyTuple_New(2);
323 PyObject * str = PyString_FromString(annotation.c_str());
324
325 PyTuple_SetItem(xy,0,PyFloat_FromDouble(x));
326 PyTuple_SetItem(xy,1,PyFloat_FromDouble(y));
327
328 PyObject* kwargs = PyDict_New();
329 PyDict_SetItemString(kwargs, "xy", xy);
330
331 PyObject* args = PyTuple_New(1);
332 PyTuple_SetItem(args, 0, str);
333
334 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs);
335
336 Py_DECREF(args);
337 Py_DECREF(kwargs);
338
339 if(res) Py_DECREF(res);
340
341 return res;
342}
343
Austin Schuhab802d52020-07-03 18:11:11 -0700344namespace detail {
345
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800346#ifndef WITHOUT_NUMPY
347// Type selector for numpy array conversion
348template <typename T> struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default
349template <> struct select_npy_type<double> { const static NPY_TYPES type = NPY_DOUBLE; };
350template <> struct select_npy_type<float> { const static NPY_TYPES type = NPY_FLOAT; };
351template <> struct select_npy_type<bool> { const static NPY_TYPES type = NPY_BOOL; };
352template <> struct select_npy_type<int8_t> { const static NPY_TYPES type = NPY_INT8; };
353template <> struct select_npy_type<int16_t> { const static NPY_TYPES type = NPY_SHORT; };
354template <> struct select_npy_type<int32_t> { const static NPY_TYPES type = NPY_INT; };
355template <> struct select_npy_type<int64_t> { const static NPY_TYPES type = NPY_INT64; };
356template <> struct select_npy_type<uint8_t> { const static NPY_TYPES type = NPY_UINT8; };
357template <> struct select_npy_type<uint16_t> { const static NPY_TYPES type = NPY_USHORT; };
358template <> struct select_npy_type<uint32_t> { const static NPY_TYPES type = NPY_ULONG; };
359template <> struct select_npy_type<uint64_t> { const static NPY_TYPES type = NPY_UINT64; };
360
Austin Schuhab802d52020-07-03 18:11:11 -0700361// Sanity checks; comment them out or change the numpy type below if you're compiling on
362// a platform where they don't apply
363static_assert(sizeof(long long) == 8);
364template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
365static_assert(sizeof(unsigned long long) == 8);
366template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };
367// TODO: add int, long, etc.
368
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800369template<typename Numeric>
370PyObject* get_array(const std::vector<Numeric>& v)
371{
Austin Schuhab802d52020-07-03 18:11:11 -0700372 npy_intp vsize = v.size();
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800373 NPY_TYPES type = select_npy_type<Numeric>::type;
Austin Schuhab802d52020-07-03 18:11:11 -0700374 if (type == NPY_NOTYPE) {
375 size_t memsize = v.size()*sizeof(double);
376 double* dp = static_cast<double*>(::malloc(memsize));
377 for (size_t i=0; i<v.size(); ++i)
378 dp[i] = v[i];
379 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, dp);
380 PyArray_UpdateFlags(reinterpret_cast<PyArrayObject*>(varray), NPY_ARRAY_OWNDATA);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800381 return varray;
382 }
Austin Schuhab802d52020-07-03 18:11:11 -0700383
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800384 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data()));
385 return varray;
386}
387
Austin Schuhab802d52020-07-03 18:11:11 -0700388
Austin Schuhad596222018-01-31 23:34:03 -0800389template<typename Numeric>
390PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
391{
Austin Schuhad596222018-01-31 23:34:03 -0800392 if (v.size() < 1) throw std::runtime_error("get_2d_array v too small");
393
394 npy_intp vsize[2] = {static_cast<npy_intp>(v.size()),
395 static_cast<npy_intp>(v[0].size())};
396
397 PyArrayObject *varray =
398 (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE);
399
400 double *vd_begin = static_cast<double *>(PyArray_DATA(varray));
401
402 for (const ::std::vector<Numeric> &v_row : v) {
403 if (v_row.size() != static_cast<size_t>(vsize[1]))
404 throw std::runtime_error("Missmatched array size");
405 std::copy(v_row.begin(), v_row.end(), vd_begin);
406 vd_begin += vsize[1];
407 }
408
409 return reinterpret_cast<PyObject *>(varray);
410}
411
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800412#else // fallback if we don't have numpy: copy every element of the given vector
413
414template<typename Numeric>
415PyObject* get_array(const std::vector<Numeric>& v)
416{
417 PyObject* list = PyList_New(v.size());
418 for(size_t i = 0; i < v.size(); ++i) {
419 PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i)));
420 }
421 return list;
422}
423
424#endif // WITHOUT_NUMPY
425
Austin Schuhab802d52020-07-03 18:11:11 -0700426// sometimes, for labels and such, we need string arrays
427inline PyObject * get_array(const std::vector<std::string>& strings)
428{
429 PyObject* list = PyList_New(strings.size());
430 for (std::size_t i = 0; i < strings.size(); ++i) {
431 PyList_SetItem(list, i, PyString_FromString(strings[i].c_str()));
432 }
433 return list;
434}
435
436// not all matplotlib need 2d arrays, some prefer lists of lists
437template<typename Numeric>
438PyObject* get_listlist(const std::vector<std::vector<Numeric>>& ll)
439{
440 PyObject* listlist = PyList_New(ll.size());
441 for (std::size_t i = 0; i < ll.size(); ++i) {
442 PyList_SetItem(listlist, i, get_array(ll[i]));
443 }
444 return listlist;
445}
446
447} // namespace detail
448
449/// Plot a line through the given x and y data points..
450///
451/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800452template<typename Numeric>
453bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
454{
455 assert(x.size() == y.size());
456
Austin Schuhab802d52020-07-03 18:11:11 -0700457 detail::_interpreter::get();
458
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800459 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700460 PyObject* xarray = detail::get_array(x);
461 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800462
463 // construct positional args
464 PyObject* args = PyTuple_New(2);
465 PyTuple_SetItem(args, 0, xarray);
466 PyTuple_SetItem(args, 1, yarray);
467
468 // construct keyword args
469 PyObject* kwargs = PyDict_New();
470 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
471 {
472 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
473 }
474
475 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs);
476
477 Py_DECREF(args);
478 Py_DECREF(kwargs);
479 if(res) Py_DECREF(res);
480
481 return res;
482}
483
Austin Schuhab802d52020-07-03 18:11:11 -0700484// TODO - it should be possible to make this work by implementing
485// a non-numpy alternative for `detail::get_2darray()`.
486#ifndef WITHOUT_NUMPY
Austin Schuhad596222018-01-31 23:34:03 -0800487template <typename Numeric>
488void plot_surface(const std::vector<::std::vector<Numeric>> &x,
489 const std::vector<::std::vector<Numeric>> &y,
490 const std::vector<::std::vector<Numeric>> &z,
491 const std::map<std::string, std::string> &keywords =
Austin Schuhab802d52020-07-03 18:11:11 -0700492 std::map<std::string, std::string>())
493{
494 detail::_interpreter::get();
495
496 // We lazily load the modules here the first time this function is called
497 // because I'm not sure that we can assume "matplotlib installed" implies
498 // "mpl_toolkits installed" on all platforms, and we don't want to require
499 // it for people who don't need 3d plots.
500 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
501 if (!mpl_toolkitsmod) {
502 detail::_interpreter::get();
503
504 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
505 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
506 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
507
508 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
509 Py_DECREF(mpl_toolkits);
510 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
511
512 axis3dmod = PyImport_Import(axis3d);
513 Py_DECREF(axis3d);
514 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
515 }
516
Austin Schuhad596222018-01-31 23:34:03 -0800517 assert(x.size() == y.size());
518 assert(y.size() == z.size());
519
520 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700521 PyObject *xarray = detail::get_2darray(x);
522 PyObject *yarray = detail::get_2darray(y);
523 PyObject *zarray = detail::get_2darray(z);
Austin Schuhad596222018-01-31 23:34:03 -0800524
525 // construct positional args
526 PyObject *args = PyTuple_New(3);
527 PyTuple_SetItem(args, 0, xarray);
528 PyTuple_SetItem(args, 1, yarray);
529 PyTuple_SetItem(args, 2, zarray);
530
531 // Build up the kw args.
532 PyObject *kwargs = PyDict_New();
533 PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1));
534 PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1));
535
536 PyObject *python_colormap_coolwarm = PyObject_GetAttrString(
537 detail::_interpreter::get().s_python_colormap, "coolwarm");
538
539 PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm);
540
541 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
542 it != keywords.end(); ++it) {
543 PyDict_SetItemString(kwargs, it->first.c_str(),
544 PyString_FromString(it->second.c_str()));
545 }
546
547
548 PyObject *fig =
549 PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
550 detail::_interpreter::get().s_python_empty_tuple);
551 if (!fig) throw std::runtime_error("Call to figure() failed.");
552
553 PyObject *gca_kwargs = PyDict_New();
554 PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
555
556 PyObject *gca = PyObject_GetAttrString(fig, "gca");
557 if (!gca) throw std::runtime_error("No gca");
558 Py_INCREF(gca);
559 PyObject *axis = PyObject_Call(
560 gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
561
562 if (!axis) throw std::runtime_error("No axis");
563 Py_INCREF(axis);
564
565 Py_DECREF(gca);
566 Py_DECREF(gca_kwargs);
567
568 PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface");
569 if (!plot_surface) throw std::runtime_error("No surface");
570 Py_INCREF(plot_surface);
571 PyObject *res = PyObject_Call(plot_surface, args, kwargs);
572 if (!res) throw std::runtime_error("failed surface");
573 Py_DECREF(plot_surface);
574
575 Py_DECREF(axis);
576 Py_DECREF(args);
577 Py_DECREF(kwargs);
578 if (res) Py_DECREF(res);
579}
Austin Schuhab802d52020-07-03 18:11:11 -0700580#endif // WITHOUT_NUMPY
581
582template <typename Numeric>
583void plot3(const std::vector<Numeric> &x,
584 const std::vector<Numeric> &y,
585 const std::vector<Numeric> &z,
586 const std::map<std::string, std::string> &keywords =
587 std::map<std::string, std::string>())
588{
589 detail::_interpreter::get();
590
591 // Same as with plot_surface: We lazily load the modules here the first time
592 // this function is called because I'm not sure that we can assume "matplotlib
593 // installed" implies "mpl_toolkits installed" on all platforms, and we don't
594 // want to require it for people who don't need 3d plots.
595 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
596 if (!mpl_toolkitsmod) {
597 detail::_interpreter::get();
598
599 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
600 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
601 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
602
603 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
604 Py_DECREF(mpl_toolkits);
605 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
606
607 axis3dmod = PyImport_Import(axis3d);
608 Py_DECREF(axis3d);
609 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
610 }
611
612 assert(x.size() == y.size());
613 assert(y.size() == z.size());
614
615 PyObject *xarray = detail::get_array(x);
616 PyObject *yarray = detail::get_array(y);
617 PyObject *zarray = detail::get_array(z);
618
619 // construct positional args
620 PyObject *args = PyTuple_New(3);
621 PyTuple_SetItem(args, 0, xarray);
622 PyTuple_SetItem(args, 1, yarray);
623 PyTuple_SetItem(args, 2, zarray);
624
625 // Build up the kw args.
626 PyObject *kwargs = PyDict_New();
627
628 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
629 it != keywords.end(); ++it) {
630 PyDict_SetItemString(kwargs, it->first.c_str(),
631 PyString_FromString(it->second.c_str()));
632 }
633
634 PyObject *fig =
635 PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
636 detail::_interpreter::get().s_python_empty_tuple);
637 if (!fig) throw std::runtime_error("Call to figure() failed.");
638
639 PyObject *gca_kwargs = PyDict_New();
640 PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
641
642 PyObject *gca = PyObject_GetAttrString(fig, "gca");
643 if (!gca) throw std::runtime_error("No gca");
644 Py_INCREF(gca);
645 PyObject *axis = PyObject_Call(
646 gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
647
648 if (!axis) throw std::runtime_error("No axis");
649 Py_INCREF(axis);
650
651 Py_DECREF(gca);
652 Py_DECREF(gca_kwargs);
653
654 PyObject *plot3 = PyObject_GetAttrString(axis, "plot");
655 if (!plot3) throw std::runtime_error("No 3D line plot");
656 Py_INCREF(plot3);
657 PyObject *res = PyObject_Call(plot3, args, kwargs);
658 if (!res) throw std::runtime_error("Failed 3D line plot");
659 Py_DECREF(plot3);
660
661 Py_DECREF(axis);
662 Py_DECREF(args);
663 Py_DECREF(kwargs);
664 if (res) Py_DECREF(res);
665}
Austin Schuhad596222018-01-31 23:34:03 -0800666
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800667template<typename Numeric>
668bool stem(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
669{
670 assert(x.size() == y.size());
671
Austin Schuhab802d52020-07-03 18:11:11 -0700672 detail::_interpreter::get();
673
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800674 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700675 PyObject* xarray = detail::get_array(x);
676 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800677
678 // construct positional args
679 PyObject* args = PyTuple_New(2);
680 PyTuple_SetItem(args, 0, xarray);
681 PyTuple_SetItem(args, 1, yarray);
682
683 // construct keyword args
684 PyObject* kwargs = PyDict_New();
685 for (std::map<std::string, std::string>::const_iterator it =
686 keywords.begin(); it != keywords.end(); ++it) {
687 PyDict_SetItemString(kwargs, it->first.c_str(),
688 PyString_FromString(it->second.c_str()));
689 }
690
691 PyObject* res = PyObject_Call(
692 detail::_interpreter::get().s_python_function_stem, args, kwargs);
693
694 Py_DECREF(args);
695 Py_DECREF(kwargs);
696 if (res)
697 Py_DECREF(res);
698
699 return res;
700}
701
702template< typename Numeric >
Austin Schuhab802d52020-07-03 18:11:11 -0700703bool fill(const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::map<std::string, std::string>& keywords)
704{
705 assert(x.size() == y.size());
706
707 detail::_interpreter::get();
708
709 // using numpy arrays
710 PyObject* xarray = detail::get_array(x);
711 PyObject* yarray = detail::get_array(y);
712
713 // construct positional args
714 PyObject* args = PyTuple_New(2);
715 PyTuple_SetItem(args, 0, xarray);
716 PyTuple_SetItem(args, 1, yarray);
717
718 // construct keyword args
719 PyObject* kwargs = PyDict_New();
720 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
721 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
722 }
723
724 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill, args, kwargs);
725
726 Py_DECREF(args);
727 Py_DECREF(kwargs);
728
729 if (res) Py_DECREF(res);
730
731 return res;
732}
733
734template< typename Numeric >
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800735bool 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)
736{
737 assert(x.size() == y1.size());
738 assert(x.size() == y2.size());
739
Austin Schuhab802d52020-07-03 18:11:11 -0700740 detail::_interpreter::get();
741
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800742 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700743 PyObject* xarray = detail::get_array(x);
744 PyObject* y1array = detail::get_array(y1);
745 PyObject* y2array = detail::get_array(y2);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800746
747 // construct positional args
748 PyObject* args = PyTuple_New(3);
749 PyTuple_SetItem(args, 0, xarray);
750 PyTuple_SetItem(args, 1, y1array);
751 PyTuple_SetItem(args, 2, y2array);
752
753 // construct keyword args
754 PyObject* kwargs = PyDict_New();
Austin Schuhab802d52020-07-03 18:11:11 -0700755 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it) {
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800756 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
757 }
758
759 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs);
760
761 Py_DECREF(args);
762 Py_DECREF(kwargs);
763 if(res) Py_DECREF(res);
764
765 return res;
766}
767
Austin Schuhab802d52020-07-03 18:11:11 -0700768template <typename Numeric>
769bool arrow(Numeric x, Numeric y, Numeric end_x, Numeric end_y, const std::string& fc = "r",
770 const std::string ec = "k", Numeric head_length = 0.25, Numeric head_width = 0.1625) {
771 PyObject* obj_x = PyFloat_FromDouble(x);
772 PyObject* obj_y = PyFloat_FromDouble(y);
773 PyObject* obj_end_x = PyFloat_FromDouble(end_x);
774 PyObject* obj_end_y = PyFloat_FromDouble(end_y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800775
Austin Schuhab802d52020-07-03 18:11:11 -0700776 PyObject* kwargs = PyDict_New();
777 PyDict_SetItemString(kwargs, "fc", PyString_FromString(fc.c_str()));
778 PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str()));
779 PyDict_SetItemString(kwargs, "head_width", PyFloat_FromDouble(head_width));
780 PyDict_SetItemString(kwargs, "head_length", PyFloat_FromDouble(head_length));
781
782 PyObject* plot_args = PyTuple_New(4);
783 PyTuple_SetItem(plot_args, 0, obj_x);
784 PyTuple_SetItem(plot_args, 1, obj_y);
785 PyTuple_SetItem(plot_args, 2, obj_end_x);
786 PyTuple_SetItem(plot_args, 3, obj_end_y);
787
788 PyObject* res =
789 PyObject_Call(detail::_interpreter::get().s_python_function_arrow, plot_args, kwargs);
790
791 Py_DECREF(plot_args);
792 Py_DECREF(kwargs);
793 if (res)
794 Py_DECREF(res);
795
796 return res;
797}
798
799template< typename Numeric>
800bool hist(const std::vector<Numeric>& y, long bins=10,std::string color="b",
801 double alpha=1.0, bool cumulative=false)
802{
803 detail::_interpreter::get();
804
805 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800806
807 PyObject* kwargs = PyDict_New();
808 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
809 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
810 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
Austin Schuhab802d52020-07-03 18:11:11 -0700811 PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800812
813 PyObject* plot_args = PyTuple_New(1);
814
815 PyTuple_SetItem(plot_args, 0, yarray);
816
817
818 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
819
820
821 Py_DECREF(plot_args);
822 Py_DECREF(kwargs);
823 if(res) Py_DECREF(res);
824
825 return res;
826}
827
Austin Schuhab802d52020-07-03 18:11:11 -0700828#ifndef WITHOUT_NUMPY
829namespace detail {
830
831inline 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)
832{
833 assert(type == NPY_UINT8 || type == NPY_FLOAT);
834 assert(colors == 1 || colors == 3 || colors == 4);
835
836 detail::_interpreter::get();
837
838 // construct args
839 npy_intp dims[3] = { rows, columns, colors };
840 PyObject *args = PyTuple_New(1);
841 PyTuple_SetItem(args, 0, PyArray_SimpleNewFromData(colors == 1 ? 2 : 3, dims, type, ptr));
842
843 // construct keyword args
844 PyObject* kwargs = PyDict_New();
845 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
846 {
847 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
848 }
849
850 PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_imshow, args, kwargs);
851 Py_DECREF(args);
852 Py_DECREF(kwargs);
853 if (!res)
854 throw std::runtime_error("Call to imshow() failed");
855 if (out)
856 *out = res;
857 else
858 Py_DECREF(res);
859}
860
861} // namespace detail
862
863inline 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)
864{
865 detail::imshow((void *) ptr, NPY_UINT8, rows, columns, colors, keywords, out);
866}
867
868inline 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)
869{
870 detail::imshow((void *) ptr, NPY_FLOAT, rows, columns, colors, keywords, out);
871}
872
873#ifdef WITH_OPENCV
874void imshow(const cv::Mat &image, const std::map<std::string, std::string> &keywords = {})
875{
876 // Convert underlying type of matrix, if needed
877 cv::Mat image2;
878 NPY_TYPES npy_type = NPY_UINT8;
879 switch (image.type() & CV_MAT_DEPTH_MASK) {
880 case CV_8U:
881 image2 = image;
882 break;
883 case CV_32F:
884 image2 = image;
885 npy_type = NPY_FLOAT;
886 break;
887 default:
888 image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels()));
889 }
890
891 // If color image, convert from BGR to RGB
892 switch (image2.channels()) {
893 case 3:
894 cv::cvtColor(image2, image2, CV_BGR2RGB);
895 break;
896 case 4:
897 cv::cvtColor(image2, image2, CV_BGRA2RGBA);
898 }
899
900 detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords);
901}
902#endif // WITH_OPENCV
903#endif // WITHOUT_NUMPY
904
905template<typename NumericX, typename NumericY>
906bool scatter(const std::vector<NumericX>& x,
907 const std::vector<NumericY>& y,
908 const double s=1.0, // The marker size in points**2
909 const std::map<std::string, std::string> & keywords = {})
910{
911 detail::_interpreter::get();
912
913 assert(x.size() == y.size());
914
915 PyObject* xarray = detail::get_array(x);
916 PyObject* yarray = detail::get_array(y);
917
918 PyObject* kwargs = PyDict_New();
919 PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s));
920 for (const auto& it : keywords)
921 {
922 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
923 }
924
925 PyObject* plot_args = PyTuple_New(2);
926 PyTuple_SetItem(plot_args, 0, xarray);
927 PyTuple_SetItem(plot_args, 1, yarray);
928
929 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs);
930
931 Py_DECREF(plot_args);
932 Py_DECREF(kwargs);
933 if(res) Py_DECREF(res);
934
935 return res;
936}
937
938template<typename Numeric>
939bool boxplot(const std::vector<std::vector<Numeric>>& data,
940 const std::vector<std::string>& labels = {},
941 const std::map<std::string, std::string> & keywords = {})
942{
943 detail::_interpreter::get();
944
945 PyObject* listlist = detail::get_listlist(data);
946 PyObject* args = PyTuple_New(1);
947 PyTuple_SetItem(args, 0, listlist);
948
949 PyObject* kwargs = PyDict_New();
950
951 // kwargs needs the labels, if there are (the correct number of) labels
952 if (!labels.empty() && labels.size() == data.size()) {
953 PyDict_SetItemString(kwargs, "labels", detail::get_array(labels));
954 }
955
956 // take care of the remaining keywords
957 for (const auto& it : keywords)
958 {
959 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
960 }
961
962 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
963
964 Py_DECREF(args);
965 Py_DECREF(kwargs);
966
967 if(res) Py_DECREF(res);
968
969 return res;
970}
971
972template<typename Numeric>
973bool boxplot(const std::vector<Numeric>& data,
974 const std::map<std::string, std::string> & keywords = {})
975{
976 detail::_interpreter::get();
977
978 PyObject* vector = detail::get_array(data);
979 PyObject* args = PyTuple_New(1);
980 PyTuple_SetItem(args, 0, vector);
981
982 PyObject* kwargs = PyDict_New();
983 for (const auto& it : keywords)
984 {
985 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
986 }
987
988 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
989
990 Py_DECREF(args);
991 Py_DECREF(kwargs);
992
993 if(res) Py_DECREF(res);
994
995 return res;
996}
997
998template <typename Numeric>
999bool bar(const std::vector<Numeric> & x,
1000 const std::vector<Numeric> & y,
1001 std::string ec = "black",
1002 std::string ls = "-",
1003 double lw = 1.0,
1004 const std::map<std::string, std::string> & keywords = {})
1005{
1006 detail::_interpreter::get();
1007
1008 PyObject * xarray = detail::get_array(x);
1009 PyObject * yarray = detail::get_array(y);
1010
1011 PyObject * kwargs = PyDict_New();
1012
1013 PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str()));
1014 PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str()));
1015 PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw));
1016
1017 for (std::map<std::string, std::string>::const_iterator it =
1018 keywords.begin();
1019 it != keywords.end();
1020 ++it) {
1021 PyDict_SetItemString(
1022 kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1023 }
1024
1025 PyObject * plot_args = PyTuple_New(2);
1026 PyTuple_SetItem(plot_args, 0, xarray);
1027 PyTuple_SetItem(plot_args, 1, yarray);
1028
1029 PyObject * res = PyObject_Call(
1030 detail::_interpreter::get().s_python_function_bar, plot_args, kwargs);
1031
1032 Py_DECREF(plot_args);
1033 Py_DECREF(kwargs);
1034 if (res) Py_DECREF(res);
1035
1036 return res;
1037}
1038
1039template <typename Numeric>
1040bool bar(const std::vector<Numeric> & y,
1041 std::string ec = "black",
1042 std::string ls = "-",
1043 double lw = 1.0,
1044 const std::map<std::string, std::string> & keywords = {})
1045{
1046 using T = typename std::remove_reference<decltype(y)>::type::value_type;
1047
1048 detail::_interpreter::get();
1049
1050 std::vector<T> x;
1051 for (std::size_t i = 0; i < y.size(); i++) { x.push_back(i); }
1052
1053 return bar(x, y, ec, ls, lw, keywords);
1054}
1055
1056inline bool subplots_adjust(const std::map<std::string, double>& keywords = {})
1057{
1058 detail::_interpreter::get();
1059
1060 PyObject* kwargs = PyDict_New();
1061 for (std::map<std::string, double>::const_iterator it =
1062 keywords.begin(); it != keywords.end(); ++it) {
1063 PyDict_SetItemString(kwargs, it->first.c_str(),
1064 PyFloat_FromDouble(it->second));
1065 }
1066
1067
1068 PyObject* plot_args = PyTuple_New(0);
1069
1070 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs);
1071
1072 Py_DECREF(plot_args);
1073 Py_DECREF(kwargs);
1074 if(res) Py_DECREF(res);
1075
1076 return res;
1077}
1078
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001079template< typename Numeric>
1080bool named_hist(std::string label,const std::vector<Numeric>& y, long bins=10, std::string color="b", double alpha=1.0)
1081{
Austin Schuhab802d52020-07-03 18:11:11 -07001082 detail::_interpreter::get();
1083
1084 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001085
1086 PyObject* kwargs = PyDict_New();
1087 PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str()));
1088 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
1089 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
1090 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
1091
1092
1093 PyObject* plot_args = PyTuple_New(1);
1094 PyTuple_SetItem(plot_args, 0, yarray);
1095
1096 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
1097
1098 Py_DECREF(plot_args);
1099 Py_DECREF(kwargs);
1100 if(res) Py_DECREF(res);
1101
1102 return res;
1103}
1104
1105template<typename NumericX, typename NumericY>
1106bool plot(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1107{
1108 assert(x.size() == y.size());
1109
Austin Schuhab802d52020-07-03 18:11:11 -07001110 detail::_interpreter::get();
1111
1112 PyObject* xarray = detail::get_array(x);
1113 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001114
1115 PyObject* pystring = PyString_FromString(s.c_str());
1116
1117 PyObject* plot_args = PyTuple_New(3);
1118 PyTuple_SetItem(plot_args, 0, xarray);
1119 PyTuple_SetItem(plot_args, 1, yarray);
1120 PyTuple_SetItem(plot_args, 2, pystring);
1121
1122 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
1123
1124 Py_DECREF(plot_args);
1125 if(res) Py_DECREF(res);
1126
1127 return res;
1128}
1129
Austin Schuhab802d52020-07-03 18:11:11 -07001130template <typename NumericX, typename NumericY, typename NumericZ>
1131bool contour(const std::vector<NumericX>& x, const std::vector<NumericY>& y,
1132 const std::vector<NumericZ>& z,
1133 const std::map<std::string, std::string>& keywords = {}) {
1134 assert(x.size() == y.size() && x.size() == z.size());
1135
1136 PyObject* xarray = get_array(x);
1137 PyObject* yarray = get_array(y);
1138 PyObject* zarray = get_array(z);
1139
1140 PyObject* plot_args = PyTuple_New(3);
1141 PyTuple_SetItem(plot_args, 0, xarray);
1142 PyTuple_SetItem(plot_args, 1, yarray);
1143 PyTuple_SetItem(plot_args, 2, zarray);
1144
1145 // construct keyword args
1146 PyObject* kwargs = PyDict_New();
1147 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
1148 it != keywords.end(); ++it) {
1149 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1150 }
1151
1152 PyObject* res =
1153 PyObject_Call(detail::_interpreter::get().s_python_function_contour, plot_args, kwargs);
1154
1155 Py_DECREF(kwargs);
1156 Py_DECREF(plot_args);
1157 if (res)
1158 Py_DECREF(res);
1159
1160 return res;
1161}
1162
1163template<typename NumericX, typename NumericY, typename NumericU, typename NumericW>
1164bool 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 = {})
1165{
1166 assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size());
1167
1168 detail::_interpreter::get();
1169
1170 PyObject* xarray = detail::get_array(x);
1171 PyObject* yarray = detail::get_array(y);
1172 PyObject* uarray = detail::get_array(u);
1173 PyObject* warray = detail::get_array(w);
1174
1175 PyObject* plot_args = PyTuple_New(4);
1176 PyTuple_SetItem(plot_args, 0, xarray);
1177 PyTuple_SetItem(plot_args, 1, yarray);
1178 PyTuple_SetItem(plot_args, 2, uarray);
1179 PyTuple_SetItem(plot_args, 3, warray);
1180
1181 // construct keyword args
1182 PyObject* kwargs = PyDict_New();
1183 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1184 {
1185 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1186 }
1187
1188 PyObject* res = PyObject_Call(
1189 detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs);
1190
1191 Py_DECREF(kwargs);
1192 Py_DECREF(plot_args);
1193 if (res)
1194 Py_DECREF(res);
1195
1196 return res;
1197}
1198
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001199template<typename NumericX, typename NumericY>
1200bool stem(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1201{
1202 assert(x.size() == y.size());
1203
Austin Schuhab802d52020-07-03 18:11:11 -07001204 detail::_interpreter::get();
1205
1206 PyObject* xarray = detail::get_array(x);
1207 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001208
1209 PyObject* pystring = PyString_FromString(s.c_str());
1210
1211 PyObject* plot_args = PyTuple_New(3);
1212 PyTuple_SetItem(plot_args, 0, xarray);
1213 PyTuple_SetItem(plot_args, 1, yarray);
1214 PyTuple_SetItem(plot_args, 2, pystring);
1215
1216 PyObject* res = PyObject_CallObject(
1217 detail::_interpreter::get().s_python_function_stem, plot_args);
1218
1219 Py_DECREF(plot_args);
1220 if (res)
1221 Py_DECREF(res);
1222
1223 return res;
1224}
1225
1226template<typename NumericX, typename NumericY>
1227bool semilogx(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1228{
1229 assert(x.size() == y.size());
1230
Austin Schuhab802d52020-07-03 18:11:11 -07001231 detail::_interpreter::get();
1232
1233 PyObject* xarray = detail::get_array(x);
1234 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001235
1236 PyObject* pystring = PyString_FromString(s.c_str());
1237
1238 PyObject* plot_args = PyTuple_New(3);
1239 PyTuple_SetItem(plot_args, 0, xarray);
1240 PyTuple_SetItem(plot_args, 1, yarray);
1241 PyTuple_SetItem(plot_args, 2, pystring);
1242
1243 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args);
1244
1245 Py_DECREF(plot_args);
1246 if(res) Py_DECREF(res);
1247
1248 return res;
1249}
1250
1251template<typename NumericX, typename NumericY>
1252bool semilogy(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1253{
1254 assert(x.size() == y.size());
1255
Austin Schuhab802d52020-07-03 18:11:11 -07001256 detail::_interpreter::get();
1257
1258 PyObject* xarray = detail::get_array(x);
1259 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001260
1261 PyObject* pystring = PyString_FromString(s.c_str());
1262
1263 PyObject* plot_args = PyTuple_New(3);
1264 PyTuple_SetItem(plot_args, 0, xarray);
1265 PyTuple_SetItem(plot_args, 1, yarray);
1266 PyTuple_SetItem(plot_args, 2, pystring);
1267
1268 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogy, plot_args);
1269
1270 Py_DECREF(plot_args);
1271 if(res) Py_DECREF(res);
1272
1273 return res;
1274}
1275
1276template<typename NumericX, typename NumericY>
1277bool loglog(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1278{
1279 assert(x.size() == y.size());
1280
Austin Schuhab802d52020-07-03 18:11:11 -07001281 detail::_interpreter::get();
1282
1283 PyObject* xarray = detail::get_array(x);
1284 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001285
1286 PyObject* pystring = PyString_FromString(s.c_str());
1287
1288 PyObject* plot_args = PyTuple_New(3);
1289 PyTuple_SetItem(plot_args, 0, xarray);
1290 PyTuple_SetItem(plot_args, 1, yarray);
1291 PyTuple_SetItem(plot_args, 2, pystring);
1292
1293 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_loglog, plot_args);
1294
1295 Py_DECREF(plot_args);
1296 if(res) Py_DECREF(res);
1297
1298 return res;
1299}
1300
1301template<typename NumericX, typename NumericY>
Austin Schuhab802d52020-07-03 18:11:11 -07001302bool 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 -08001303{
1304 assert(x.size() == y.size());
1305
Austin Schuhab802d52020-07-03 18:11:11 -07001306 detail::_interpreter::get();
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001307
Austin Schuhab802d52020-07-03 18:11:11 -07001308 PyObject* xarray = detail::get_array(x);
1309 PyObject* yarray = detail::get_array(y);
1310 PyObject* yerrarray = detail::get_array(yerr);
1311
1312 // construct keyword args
1313 PyObject* kwargs = PyDict_New();
1314 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1315 {
1316 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1317 }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001318
1319 PyDict_SetItemString(kwargs, "yerr", yerrarray);
1320
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001321 PyObject *plot_args = PyTuple_New(2);
1322 PyTuple_SetItem(plot_args, 0, xarray);
1323 PyTuple_SetItem(plot_args, 1, yarray);
1324
1325 PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_errorbar, plot_args, kwargs);
1326
1327 Py_DECREF(kwargs);
1328 Py_DECREF(plot_args);
1329
1330 if (res)
1331 Py_DECREF(res);
1332 else
1333 throw std::runtime_error("Call to errorbar() failed.");
1334
1335 return res;
1336}
1337
1338template<typename Numeric>
1339bool named_plot(const std::string& name, const std::vector<Numeric>& y, const std::string& format = "")
1340{
Austin Schuhab802d52020-07-03 18:11:11 -07001341 detail::_interpreter::get();
1342
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001343 PyObject* kwargs = PyDict_New();
1344 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1345
Austin Schuhab802d52020-07-03 18:11:11 -07001346 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001347
1348 PyObject* pystring = PyString_FromString(format.c_str());
1349
1350 PyObject* plot_args = PyTuple_New(2);
1351
1352 PyTuple_SetItem(plot_args, 0, yarray);
1353 PyTuple_SetItem(plot_args, 1, pystring);
1354
1355 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
1356
1357 Py_DECREF(kwargs);
1358 Py_DECREF(plot_args);
1359 if (res) Py_DECREF(res);
1360
1361 return res;
1362}
1363
1364template<typename Numeric>
1365bool named_plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1366{
Austin Schuhab802d52020-07-03 18:11:11 -07001367 detail::_interpreter::get();
1368
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001369 PyObject* kwargs = PyDict_New();
1370 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1371
Austin Schuhab802d52020-07-03 18:11:11 -07001372 PyObject* xarray = detail::get_array(x);
1373 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001374
1375 PyObject* pystring = PyString_FromString(format.c_str());
1376
1377 PyObject* plot_args = PyTuple_New(3);
1378 PyTuple_SetItem(plot_args, 0, xarray);
1379 PyTuple_SetItem(plot_args, 1, yarray);
1380 PyTuple_SetItem(plot_args, 2, pystring);
1381
1382 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
1383
1384 Py_DECREF(kwargs);
1385 Py_DECREF(plot_args);
1386 if (res) Py_DECREF(res);
1387
1388 return res;
1389}
1390
1391template<typename Numeric>
1392bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1393{
Austin Schuhab802d52020-07-03 18:11:11 -07001394 detail::_interpreter::get();
1395
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001396 PyObject* kwargs = PyDict_New();
1397 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1398
Austin Schuhab802d52020-07-03 18:11:11 -07001399 PyObject* xarray = detail::get_array(x);
1400 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001401
1402 PyObject* pystring = PyString_FromString(format.c_str());
1403
1404 PyObject* plot_args = PyTuple_New(3);
1405 PyTuple_SetItem(plot_args, 0, xarray);
1406 PyTuple_SetItem(plot_args, 1, yarray);
1407 PyTuple_SetItem(plot_args, 2, pystring);
1408
1409 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs);
1410
1411 Py_DECREF(kwargs);
1412 Py_DECREF(plot_args);
1413 if (res) Py_DECREF(res);
1414
1415 return res;
1416}
1417
1418template<typename Numeric>
1419bool named_semilogy(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1420{
Austin Schuhab802d52020-07-03 18:11:11 -07001421 detail::_interpreter::get();
1422
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001423 PyObject* kwargs = PyDict_New();
1424 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1425
Austin Schuhab802d52020-07-03 18:11:11 -07001426 PyObject* xarray = detail::get_array(x);
1427 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001428
1429 PyObject* pystring = PyString_FromString(format.c_str());
1430
1431 PyObject* plot_args = PyTuple_New(3);
1432 PyTuple_SetItem(plot_args, 0, xarray);
1433 PyTuple_SetItem(plot_args, 1, yarray);
1434 PyTuple_SetItem(plot_args, 2, pystring);
1435
1436 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs);
1437
1438 Py_DECREF(kwargs);
1439 Py_DECREF(plot_args);
1440 if (res) Py_DECREF(res);
1441
1442 return res;
1443}
1444
1445template<typename Numeric>
1446bool named_loglog(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1447{
Austin Schuhab802d52020-07-03 18:11:11 -07001448 detail::_interpreter::get();
1449
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001450 PyObject* kwargs = PyDict_New();
1451 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1452
Austin Schuhab802d52020-07-03 18:11:11 -07001453 PyObject* xarray = detail::get_array(x);
1454 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001455
1456 PyObject* pystring = PyString_FromString(format.c_str());
1457
1458 PyObject* plot_args = PyTuple_New(3);
1459 PyTuple_SetItem(plot_args, 0, xarray);
1460 PyTuple_SetItem(plot_args, 1, yarray);
1461 PyTuple_SetItem(plot_args, 2, pystring);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001462 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs);
1463
1464 Py_DECREF(kwargs);
1465 Py_DECREF(plot_args);
1466 if (res) Py_DECREF(res);
1467
1468 return res;
1469}
1470
1471template<typename Numeric>
1472bool plot(const std::vector<Numeric>& y, const std::string& format = "")
1473{
1474 std::vector<Numeric> x(y.size());
1475 for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
1476 return plot(x,y,format);
1477}
1478
1479template<typename Numeric>
Austin Schuhab802d52020-07-03 18:11:11 -07001480bool plot(const std::vector<Numeric>& y, const std::map<std::string, std::string>& keywords)
1481{
1482 std::vector<Numeric> x(y.size());
1483 for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
1484 return plot(x,y,keywords);
1485}
1486
1487template<typename Numeric>
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001488bool stem(const std::vector<Numeric>& y, const std::string& format = "")
1489{
1490 std::vector<Numeric> x(y.size());
1491 for (size_t i = 0; i < x.size(); ++i) x.at(i) = i;
1492 return stem(x, y, format);
1493}
1494
Austin Schuhab802d52020-07-03 18:11:11 -07001495template<typename Numeric>
1496void text(Numeric x, Numeric y, const std::string& s = "")
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001497{
Austin Schuhab802d52020-07-03 18:11:11 -07001498 detail::_interpreter::get();
1499
1500 PyObject* args = PyTuple_New(3);
1501 PyTuple_SetItem(args, 0, PyFloat_FromDouble(x));
1502 PyTuple_SetItem(args, 1, PyFloat_FromDouble(y));
1503 PyTuple_SetItem(args, 2, PyString_FromString(s.c_str()));
1504
1505 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_text, args);
1506 if(!res) throw std::runtime_error("Call to text() failed.");
1507
1508 Py_DECREF(args);
1509 Py_DECREF(res);
1510}
1511
1512inline void colorbar(PyObject* mappable = NULL, const std::map<std::string, float>& keywords = {})
1513{
1514 if (mappable == NULL)
1515 throw std::runtime_error("Must call colorbar with PyObject* returned from an image, contour, surface, etc.");
1516
1517 detail::_interpreter::get();
1518
1519 PyObject* args = PyTuple_New(1);
1520 PyTuple_SetItem(args, 0, mappable);
1521
1522 PyObject* kwargs = PyDict_New();
1523 for(std::map<std::string, float>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1524 {
1525 PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(it->second));
1526 }
1527
1528 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_colorbar, args, kwargs);
1529 if(!res) throw std::runtime_error("Call to colorbar() failed.");
1530
1531 Py_DECREF(args);
1532 Py_DECREF(kwargs);
1533 Py_DECREF(res);
1534}
1535
1536
1537inline long figure(long number = -1)
1538{
1539 detail::_interpreter::get();
1540
1541 PyObject *res;
1542 if (number == -1)
1543 res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple);
1544 else {
1545 assert(number > 0);
1546
1547 // Make sure interpreter is initialised
1548 detail::_interpreter::get();
1549
1550 PyObject *args = PyTuple_New(1);
1551 PyTuple_SetItem(args, 0, PyLong_FromLong(number));
1552 res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, args);
1553 Py_DECREF(args);
1554 }
1555
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001556 if(!res) throw std::runtime_error("Call to figure() failed.");
1557
Austin Schuhab802d52020-07-03 18:11:11 -07001558 PyObject* num = PyObject_GetAttrString(res, "number");
1559 if (!num) throw std::runtime_error("Could not get number attribute of figure object");
1560 const long figureNumber = PyLong_AsLong(num);
1561
1562 Py_DECREF(num);
1563 Py_DECREF(res);
1564
1565 return figureNumber;
1566}
1567
1568inline bool fignum_exists(long number)
1569{
1570 detail::_interpreter::get();
1571
1572 PyObject *args = PyTuple_New(1);
1573 PyTuple_SetItem(args, 0, PyLong_FromLong(number));
1574 PyObject *res = PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, args);
1575 if(!res) throw std::runtime_error("Call to fignum_exists() failed.");
1576
1577 bool ret = PyObject_IsTrue(res);
1578 Py_DECREF(res);
1579 Py_DECREF(args);
1580
1581 return ret;
1582}
1583
1584inline void figure_size(size_t w, size_t h)
1585{
1586 detail::_interpreter::get();
1587
1588 const size_t dpi = 100;
1589 PyObject* size = PyTuple_New(2);
1590 PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi));
1591 PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi));
1592
1593 PyObject* kwargs = PyDict_New();
1594 PyDict_SetItemString(kwargs, "figsize", size);
1595 PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi));
1596
1597 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_figure,
1598 detail::_interpreter::get().s_python_empty_tuple, kwargs);
1599
1600 Py_DECREF(kwargs);
1601
1602 if(!res) throw std::runtime_error("Call to figure_size() failed.");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001603 Py_DECREF(res);
1604}
1605
1606inline void legend()
1607{
Austin Schuhab802d52020-07-03 18:11:11 -07001608 detail::_interpreter::get();
1609
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001610 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple);
1611 if(!res) throw std::runtime_error("Call to legend() failed.");
1612
1613 Py_DECREF(res);
1614}
1615
Austin Schuhab802d52020-07-03 18:11:11 -07001616inline void legend(const std::map<std::string, std::string>& keywords)
1617{
1618 detail::_interpreter::get();
1619
1620 // construct keyword args
1621 PyObject* kwargs = PyDict_New();
1622 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1623 {
1624 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1625 }
1626
1627 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple, kwargs);
1628 if(!res) throw std::runtime_error("Call to legend() failed.");
1629
1630 Py_DECREF(kwargs);
1631 Py_DECREF(res);
1632}
1633
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001634template<typename Numeric>
1635void ylim(Numeric left, Numeric right)
1636{
Austin Schuhab802d52020-07-03 18:11:11 -07001637 detail::_interpreter::get();
1638
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001639 PyObject* list = PyList_New(2);
1640 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
1641 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
1642
1643 PyObject* args = PyTuple_New(1);
1644 PyTuple_SetItem(args, 0, list);
1645
1646 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
1647 if(!res) throw std::runtime_error("Call to ylim() failed.");
1648
1649 Py_DECREF(args);
1650 Py_DECREF(res);
1651}
1652
1653template<typename Numeric>
1654void xlim(Numeric left, Numeric right)
1655{
Austin Schuhab802d52020-07-03 18:11:11 -07001656 detail::_interpreter::get();
1657
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001658 PyObject* list = PyList_New(2);
1659 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
1660 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
1661
1662 PyObject* args = PyTuple_New(1);
1663 PyTuple_SetItem(args, 0, list);
1664
1665 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
1666 if(!res) throw std::runtime_error("Call to xlim() failed.");
1667
1668 Py_DECREF(args);
1669 Py_DECREF(res);
1670}
1671
1672
1673inline double* xlim()
1674{
Austin Schuhab802d52020-07-03 18:11:11 -07001675 detail::_interpreter::get();
1676
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001677 PyObject* args = PyTuple_New(0);
1678 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
1679 PyObject* left = PyTuple_GetItem(res,0);
1680 PyObject* right = PyTuple_GetItem(res,1);
1681
1682 double* arr = new double[2];
1683 arr[0] = PyFloat_AsDouble(left);
1684 arr[1] = PyFloat_AsDouble(right);
1685
1686 if(!res) throw std::runtime_error("Call to xlim() failed.");
1687
1688 Py_DECREF(res);
1689 return arr;
1690}
1691
1692
1693inline double* ylim()
1694{
Austin Schuhab802d52020-07-03 18:11:11 -07001695 detail::_interpreter::get();
1696
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001697 PyObject* args = PyTuple_New(0);
1698 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
1699 PyObject* left = PyTuple_GetItem(res,0);
1700 PyObject* right = PyTuple_GetItem(res,1);
1701
1702 double* arr = new double[2];
1703 arr[0] = PyFloat_AsDouble(left);
1704 arr[1] = PyFloat_AsDouble(right);
1705
1706 if(!res) throw std::runtime_error("Call to ylim() failed.");
1707
1708 Py_DECREF(res);
1709 return arr;
1710}
1711
Austin Schuhab802d52020-07-03 18:11:11 -07001712template<typename Numeric>
1713inline void xticks(const std::vector<Numeric> &ticks, const std::vector<std::string> &labels = {}, const std::map<std::string, std::string>& keywords = {})
1714{
1715 assert(labels.size() == 0 || ticks.size() == labels.size());
1716
1717 detail::_interpreter::get();
1718
1719 // using numpy array
1720 PyObject* ticksarray = detail::get_array(ticks);
1721
1722 PyObject* args;
1723 if(labels.size() == 0) {
1724 // construct positional args
1725 args = PyTuple_New(1);
1726 PyTuple_SetItem(args, 0, ticksarray);
1727 } else {
1728 // make tuple of tick labels
1729 PyObject* labelstuple = PyTuple_New(labels.size());
1730 for (size_t i = 0; i < labels.size(); i++)
1731 PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str()));
1732
1733 // construct positional args
1734 args = PyTuple_New(2);
1735 PyTuple_SetItem(args, 0, ticksarray);
1736 PyTuple_SetItem(args, 1, labelstuple);
1737 }
1738
1739 // construct keyword args
1740 PyObject* kwargs = PyDict_New();
1741 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1742 {
1743 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1744 }
1745
1746 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xticks, args, kwargs);
1747
1748 Py_DECREF(args);
1749 Py_DECREF(kwargs);
1750 if(!res) throw std::runtime_error("Call to xticks() failed");
1751
1752 Py_DECREF(res);
1753}
1754
1755template<typename Numeric>
1756inline void xticks(const std::vector<Numeric> &ticks, const std::map<std::string, std::string>& keywords)
1757{
1758 xticks(ticks, {}, keywords);
1759}
1760
1761template<typename Numeric>
1762inline void yticks(const std::vector<Numeric> &ticks, const std::vector<std::string> &labels = {}, const std::map<std::string, std::string>& keywords = {})
1763{
1764 assert(labels.size() == 0 || ticks.size() == labels.size());
1765
1766 detail::_interpreter::get();
1767
1768 // using numpy array
1769 PyObject* ticksarray = detail::get_array(ticks);
1770
1771 PyObject* args;
1772 if(labels.size() == 0) {
1773 // construct positional args
1774 args = PyTuple_New(1);
1775 PyTuple_SetItem(args, 0, ticksarray);
1776 } else {
1777 // make tuple of tick labels
1778 PyObject* labelstuple = PyTuple_New(labels.size());
1779 for (size_t i = 0; i < labels.size(); i++)
1780 PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str()));
1781
1782 // construct positional args
1783 args = PyTuple_New(2);
1784 PyTuple_SetItem(args, 0, ticksarray);
1785 PyTuple_SetItem(args, 1, labelstuple);
1786 }
1787
1788 // construct keyword args
1789 PyObject* kwargs = PyDict_New();
1790 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1791 {
1792 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1793 }
1794
1795 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_yticks, args, kwargs);
1796
1797 Py_DECREF(args);
1798 Py_DECREF(kwargs);
1799 if(!res) throw std::runtime_error("Call to yticks() failed");
1800
1801 Py_DECREF(res);
1802}
1803
1804template<typename Numeric>
1805inline void yticks(const std::vector<Numeric> &ticks, const std::map<std::string, std::string>& keywords)
1806{
1807 yticks(ticks, {}, keywords);
1808}
1809
1810template <typename Numeric> inline void margins(Numeric margin)
1811{
1812 // construct positional args
1813 PyObject* args = PyTuple_New(1);
1814 PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin));
1815
1816 PyObject* res =
1817 PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args);
1818 if (!res)
1819 throw std::runtime_error("Call to margins() failed.");
1820
1821 Py_DECREF(args);
1822 Py_DECREF(res);
1823}
1824
1825template <typename Numeric> inline void margins(Numeric margin_x, Numeric margin_y)
1826{
1827 // construct positional args
1828 PyObject* args = PyTuple_New(2);
1829 PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin_x));
1830 PyTuple_SetItem(args, 1, PyFloat_FromDouble(margin_y));
1831
1832 PyObject* res =
1833 PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args);
1834 if (!res)
1835 throw std::runtime_error("Call to margins() failed.");
1836
1837 Py_DECREF(args);
1838 Py_DECREF(res);
1839}
1840
1841
1842inline void tick_params(const std::map<std::string, std::string>& keywords, const std::string axis = "both")
1843{
1844 detail::_interpreter::get();
1845
1846 // construct positional args
1847 PyObject* args;
1848 args = PyTuple_New(1);
1849 PyTuple_SetItem(args, 0, PyString_FromString(axis.c_str()));
1850
1851 // construct keyword args
1852 PyObject* kwargs = PyDict_New();
1853 for (std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1854 {
1855 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1856 }
1857
1858
1859 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_tick_params, args, kwargs);
1860
1861 Py_DECREF(args);
1862 Py_DECREF(kwargs);
1863 if (!res) throw std::runtime_error("Call to tick_params() failed");
1864
1865 Py_DECREF(res);
1866}
1867
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001868inline void subplot(long nrows, long ncols, long plot_number)
1869{
Austin Schuhab802d52020-07-03 18:11:11 -07001870 detail::_interpreter::get();
1871
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001872 // construct positional args
1873 PyObject* args = PyTuple_New(3);
1874 PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows));
1875 PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols));
1876 PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number));
1877
1878 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args);
1879 if(!res) throw std::runtime_error("Call to subplot() failed.");
1880
1881 Py_DECREF(args);
1882 Py_DECREF(res);
1883}
1884
Austin Schuhab802d52020-07-03 18:11:11 -07001885inline 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 -08001886{
Austin Schuhab802d52020-07-03 18:11:11 -07001887 detail::_interpreter::get();
1888
1889 PyObject* shape = PyTuple_New(2);
1890 PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows));
1891 PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols));
1892
1893 PyObject* loc = PyTuple_New(2);
1894 PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid));
1895 PyTuple_SetItem(loc, 1, PyLong_FromLong(colid));
1896
1897 PyObject* args = PyTuple_New(4);
1898 PyTuple_SetItem(args, 0, shape);
1899 PyTuple_SetItem(args, 1, loc);
1900 PyTuple_SetItem(args, 2, PyLong_FromLong(rowspan));
1901 PyTuple_SetItem(args, 3, PyLong_FromLong(colspan));
1902
1903 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot2grid, args);
1904 if(!res) throw std::runtime_error("Call to subplot2grid() failed.");
1905
1906 Py_DECREF(shape);
1907 Py_DECREF(loc);
1908 Py_DECREF(args);
1909 Py_DECREF(res);
1910}
1911
1912inline void title(const std::string &titlestr, const std::map<std::string, std::string> &keywords = {})
1913{
1914 detail::_interpreter::get();
1915
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001916 PyObject* pytitlestr = PyString_FromString(titlestr.c_str());
1917 PyObject* args = PyTuple_New(1);
1918 PyTuple_SetItem(args, 0, pytitlestr);
1919
Austin Schuhab802d52020-07-03 18:11:11 -07001920 PyObject* kwargs = PyDict_New();
1921 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
1922 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1923 }
1924
1925 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_title, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001926 if(!res) throw std::runtime_error("Call to title() failed.");
1927
1928 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07001929 Py_DECREF(kwargs);
1930 Py_DECREF(res);
1931}
1932
1933inline void suptitle(const std::string &suptitlestr, const std::map<std::string, std::string> &keywords = {})
1934{
1935 detail::_interpreter::get();
1936
1937 PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str());
1938 PyObject* args = PyTuple_New(1);
1939 PyTuple_SetItem(args, 0, pysuptitlestr);
1940
1941 PyObject* kwargs = PyDict_New();
1942 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
1943 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1944 }
1945
1946 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_suptitle, args, kwargs);
1947 if(!res) throw std::runtime_error("Call to suptitle() failed.");
1948
1949 Py_DECREF(args);
1950 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001951 Py_DECREF(res);
1952}
1953
1954inline void axis(const std::string &axisstr)
1955{
Austin Schuhab802d52020-07-03 18:11:11 -07001956 detail::_interpreter::get();
1957
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001958 PyObject* str = PyString_FromString(axisstr.c_str());
1959 PyObject* args = PyTuple_New(1);
1960 PyTuple_SetItem(args, 0, str);
1961
1962 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args);
1963 if(!res) throw std::runtime_error("Call to title() failed.");
1964
1965 Py_DECREF(args);
1966 Py_DECREF(res);
1967}
1968
Austin Schuhab802d52020-07-03 18:11:11 -07001969inline 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 -08001970{
Austin Schuhab802d52020-07-03 18:11:11 -07001971 detail::_interpreter::get();
1972
1973 // construct positional args
1974 PyObject* args = PyTuple_New(3);
1975 PyTuple_SetItem(args, 0, PyFloat_FromDouble(x));
1976 PyTuple_SetItem(args, 1, PyFloat_FromDouble(ymin));
1977 PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymax));
1978
1979 // construct keyword args
1980 PyObject* kwargs = PyDict_New();
1981 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1982 {
1983 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1984 }
1985
1986 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvline, args, kwargs);
1987
1988 Py_DECREF(args);
1989 Py_DECREF(kwargs);
1990
1991 if(res) Py_DECREF(res);
1992}
1993
1994inline 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>())
1995{
1996 // construct positional args
1997 PyObject* args = PyTuple_New(4);
1998 PyTuple_SetItem(args, 0, PyFloat_FromDouble(xmin));
1999 PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmax));
2000 PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymin));
2001 PyTuple_SetItem(args, 3, PyFloat_FromDouble(ymax));
2002
2003 // construct keyword args
2004 PyObject* kwargs = PyDict_New();
2005 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
2006 {
2007 if (it->first == "linewidth" || it->first == "alpha")
2008 PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(std::stod(it->second)));
2009 else
2010 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
2011 }
2012
2013 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvspan, args, kwargs);
2014 Py_DECREF(args);
2015 Py_DECREF(kwargs);
2016
2017 if(res) Py_DECREF(res);
2018}
2019
2020inline void xlabel(const std::string &str, const std::map<std::string, std::string> &keywords = {})
2021{
2022 detail::_interpreter::get();
2023
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002024 PyObject* pystr = PyString_FromString(str.c_str());
2025 PyObject* args = PyTuple_New(1);
2026 PyTuple_SetItem(args, 0, pystr);
2027
Austin Schuhab802d52020-07-03 18:11:11 -07002028 PyObject* kwargs = PyDict_New();
2029 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2030 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2031 }
2032
2033 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xlabel, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002034 if(!res) throw std::runtime_error("Call to xlabel() failed.");
2035
2036 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07002037 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002038 Py_DECREF(res);
2039}
2040
Austin Schuhab802d52020-07-03 18:11:11 -07002041inline void ylabel(const std::string &str, const std::map<std::string, std::string>& keywords = {})
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002042{
Austin Schuhab802d52020-07-03 18:11:11 -07002043 detail::_interpreter::get();
2044
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002045 PyObject* pystr = PyString_FromString(str.c_str());
2046 PyObject* args = PyTuple_New(1);
2047 PyTuple_SetItem(args, 0, pystr);
2048
Austin Schuhab802d52020-07-03 18:11:11 -07002049 PyObject* kwargs = PyDict_New();
2050 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2051 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2052 }
2053
2054 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_ylabel, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002055 if(!res) throw std::runtime_error("Call to ylabel() failed.");
2056
2057 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07002058 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002059 Py_DECREF(res);
2060}
2061
Austin Schuhab802d52020-07-03 18:11:11 -07002062inline void set_zlabel(const std::string &str, const std::map<std::string, std::string>& keywords = {})
2063{
2064 detail::_interpreter::get();
2065
2066 // Same as with plot_surface: We lazily load the modules here the first time
2067 // this function is called because I'm not sure that we can assume "matplotlib
2068 // installed" implies "mpl_toolkits installed" on all platforms, and we don't
2069 // want to require it for people who don't need 3d plots.
2070 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
2071 if (!mpl_toolkitsmod) {
2072 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
2073 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
2074 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
2075
2076 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
2077 Py_DECREF(mpl_toolkits);
2078 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
2079
2080 axis3dmod = PyImport_Import(axis3d);
2081 Py_DECREF(axis3d);
2082 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
2083 }
2084
2085 PyObject* pystr = PyString_FromString(str.c_str());
2086 PyObject* args = PyTuple_New(1);
2087 PyTuple_SetItem(args, 0, pystr);
2088
2089 PyObject* kwargs = PyDict_New();
2090 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2091 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2092 }
2093
2094 PyObject *ax =
2095 PyObject_CallObject(detail::_interpreter::get().s_python_function_gca,
2096 detail::_interpreter::get().s_python_empty_tuple);
2097 if (!ax) throw std::runtime_error("Call to gca() failed.");
2098 Py_INCREF(ax);
2099
2100 PyObject *zlabel = PyObject_GetAttrString(ax, "set_zlabel");
2101 if (!zlabel) throw std::runtime_error("Attribute set_zlabel not found.");
2102 Py_INCREF(zlabel);
2103
2104 PyObject *res = PyObject_Call(zlabel, args, kwargs);
2105 if (!res) throw std::runtime_error("Call to set_zlabel() failed.");
2106 Py_DECREF(zlabel);
2107
2108 Py_DECREF(ax);
2109 Py_DECREF(args);
2110 Py_DECREF(kwargs);
2111 if (res) Py_DECREF(res);
2112}
2113
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002114inline void grid(bool flag)
2115{
Austin Schuhab802d52020-07-03 18:11:11 -07002116 detail::_interpreter::get();
2117
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002118 PyObject* pyflag = flag ? Py_True : Py_False;
2119 Py_INCREF(pyflag);
2120
2121 PyObject* args = PyTuple_New(1);
2122 PyTuple_SetItem(args, 0, pyflag);
2123
2124 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args);
2125 if(!res) throw std::runtime_error("Call to grid() failed.");
2126
2127 Py_DECREF(args);
2128 Py_DECREF(res);
2129}
2130
2131inline void show(const bool block = true)
2132{
Austin Schuhab802d52020-07-03 18:11:11 -07002133 detail::_interpreter::get();
2134
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002135 PyObject* res;
2136 if(block)
2137 {
2138 res = PyObject_CallObject(
2139 detail::_interpreter::get().s_python_function_show,
2140 detail::_interpreter::get().s_python_empty_tuple);
2141 }
2142 else
2143 {
2144 PyObject *kwargs = PyDict_New();
2145 PyDict_SetItemString(kwargs, "block", Py_False);
2146 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 -07002147 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002148 }
2149
2150
2151 if (!res) throw std::runtime_error("Call to show() failed.");
2152
2153 Py_DECREF(res);
2154}
2155
2156inline void close()
2157{
Austin Schuhab802d52020-07-03 18:11:11 -07002158 detail::_interpreter::get();
2159
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002160 PyObject* res = PyObject_CallObject(
2161 detail::_interpreter::get().s_python_function_close,
2162 detail::_interpreter::get().s_python_empty_tuple);
2163
2164 if (!res) throw std::runtime_error("Call to close() failed.");
2165
2166 Py_DECREF(res);
2167}
2168
2169inline void xkcd() {
Austin Schuhab802d52020-07-03 18:11:11 -07002170 detail::_interpreter::get();
2171
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002172 PyObject* res;
2173 PyObject *kwargs = PyDict_New();
2174
2175 res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd,
2176 detail::_interpreter::get().s_python_empty_tuple, kwargs);
2177
2178 Py_DECREF(kwargs);
2179
2180 if (!res)
2181 throw std::runtime_error("Call to show() failed.");
2182
2183 Py_DECREF(res);
2184}
2185
2186inline void draw()
2187{
Austin Schuhab802d52020-07-03 18:11:11 -07002188 detail::_interpreter::get();
2189
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002190 PyObject* res = PyObject_CallObject(
2191 detail::_interpreter::get().s_python_function_draw,
2192 detail::_interpreter::get().s_python_empty_tuple);
2193
2194 if (!res) throw std::runtime_error("Call to draw() failed.");
2195
2196 Py_DECREF(res);
2197}
2198
2199template<typename Numeric>
2200inline void pause(Numeric interval)
2201{
Austin Schuhab802d52020-07-03 18:11:11 -07002202 detail::_interpreter::get();
2203
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002204 PyObject* args = PyTuple_New(1);
2205 PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval));
2206
2207 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args);
2208 if(!res) throw std::runtime_error("Call to pause() failed.");
2209
2210 Py_DECREF(args);
2211 Py_DECREF(res);
2212}
2213
2214inline void save(const std::string& filename)
2215{
Austin Schuhab802d52020-07-03 18:11:11 -07002216 detail::_interpreter::get();
2217
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002218 PyObject* pyfilename = PyString_FromString(filename.c_str());
2219
2220 PyObject* args = PyTuple_New(1);
2221 PyTuple_SetItem(args, 0, pyfilename);
2222
2223 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args);
2224 if (!res) throw std::runtime_error("Call to save() failed.");
2225
2226 Py_DECREF(args);
2227 Py_DECREF(res);
2228}
2229
2230inline void clf() {
Austin Schuhab802d52020-07-03 18:11:11 -07002231 detail::_interpreter::get();
2232
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002233 PyObject *res = PyObject_CallObject(
2234 detail::_interpreter::get().s_python_function_clf,
2235 detail::_interpreter::get().s_python_empty_tuple);
2236
2237 if (!res) throw std::runtime_error("Call to clf() failed.");
2238
2239 Py_DECREF(res);
2240}
2241
Austin Schuhab802d52020-07-03 18:11:11 -07002242inline void cla() {
2243 detail::_interpreter::get();
2244
2245 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_cla,
2246 detail::_interpreter::get().s_python_empty_tuple);
2247
2248 if (!res)
2249 throw std::runtime_error("Call to cla() failed.");
2250
2251 Py_DECREF(res);
2252}
2253
2254inline void ion() {
2255 detail::_interpreter::get();
2256
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002257 PyObject *res = PyObject_CallObject(
2258 detail::_interpreter::get().s_python_function_ion,
2259 detail::_interpreter::get().s_python_empty_tuple);
2260
2261 if (!res) throw std::runtime_error("Call to ion() failed.");
2262
2263 Py_DECREF(res);
2264}
2265
Austin Schuhab802d52020-07-03 18:11:11 -07002266inline std::vector<std::array<double, 2>> ginput(const int numClicks = 1, const std::map<std::string, std::string>& keywords = {})
2267{
2268 detail::_interpreter::get();
2269
2270 PyObject *args = PyTuple_New(1);
2271 PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks));
2272
2273 // construct keyword args
2274 PyObject* kwargs = PyDict_New();
2275 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
2276 {
2277 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2278 }
2279
2280 PyObject* res = PyObject_Call(
2281 detail::_interpreter::get().s_python_function_ginput, args, kwargs);
2282
2283 Py_DECREF(kwargs);
2284 Py_DECREF(args);
2285 if (!res) throw std::runtime_error("Call to ginput() failed.");
2286
2287 const size_t len = PyList_Size(res);
2288 std::vector<std::array<double, 2>> out;
2289 out.reserve(len);
2290 for (size_t i = 0; i < len; i++) {
2291 PyObject *current = PyList_GetItem(res, i);
2292 std::array<double, 2> position;
2293 position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0));
2294 position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1));
2295 out.push_back(position);
2296 }
2297 Py_DECREF(res);
2298
2299 return out;
2300}
2301
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002302// Actually, is there any reason not to call this automatically for every plot?
2303inline void tight_layout() {
Austin Schuhab802d52020-07-03 18:11:11 -07002304 detail::_interpreter::get();
2305
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002306 PyObject *res = PyObject_CallObject(
2307 detail::_interpreter::get().s_python_function_tight_layout,
2308 detail::_interpreter::get().s_python_empty_tuple);
2309
2310 if (!res) throw std::runtime_error("Call to tight_layout() failed.");
2311
2312 Py_DECREF(res);
2313}
2314
Austin Schuhab802d52020-07-03 18:11:11 -07002315// Support for variadic plot() and initializer lists:
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002316
2317namespace detail {
2318
2319template<typename T>
2320using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
2321
2322template<bool obj, typename T>
2323struct is_callable_impl;
2324
2325template<typename T>
2326struct is_callable_impl<false, T>
2327{
2328 typedef is_function<T> type;
2329}; // a non-object is callable iff it is a function
2330
2331template<typename T>
2332struct is_callable_impl<true, T>
2333{
2334 struct Fallback { void operator()(); };
2335 struct Derived : T, Fallback { };
2336
2337 template<typename U, U> struct Check;
2338
2339 template<typename U>
2340 static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match
2341
2342 template<typename U>
2343 static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
2344
2345public:
2346 typedef decltype(test<Derived>(nullptr)) type;
2347 typedef decltype(&Fallback::operator()) dtype;
2348 static constexpr bool value = type::value;
2349}; // an object is callable iff it defines operator()
2350
2351template<typename T>
2352struct is_callable
2353{
2354 // dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
2355 typedef typename is_callable_impl<std::is_class<T>::value, T>::type type;
2356};
2357
2358template<typename IsYDataCallable>
2359struct plot_impl { };
2360
2361template<>
2362struct plot_impl<std::false_type>
2363{
2364 template<typename IterableX, typename IterableY>
2365 bool operator()(const IterableX& x, const IterableY& y, const std::string& format)
2366 {
2367 // 2-phase lookup for distance, begin, end
2368 using std::distance;
2369 using std::begin;
2370 using std::end;
2371
2372 auto xs = distance(begin(x), end(x));
2373 auto ys = distance(begin(y), end(y));
2374 assert(xs == ys && "x and y data must have the same number of elements!");
2375
2376 PyObject* xlist = PyList_New(xs);
2377 PyObject* ylist = PyList_New(ys);
2378 PyObject* pystring = PyString_FromString(format.c_str());
2379
2380 auto itx = begin(x), ity = begin(y);
2381 for(size_t i = 0; i < xs; ++i) {
2382 PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++));
2383 PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++));
2384 }
2385
2386 PyObject* plot_args = PyTuple_New(3);
2387 PyTuple_SetItem(plot_args, 0, xlist);
2388 PyTuple_SetItem(plot_args, 1, ylist);
2389 PyTuple_SetItem(plot_args, 2, pystring);
2390
2391 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
2392
2393 Py_DECREF(plot_args);
2394 if(res) Py_DECREF(res);
2395
2396 return res;
2397 }
2398};
2399
2400template<>
2401struct plot_impl<std::true_type>
2402{
2403 template<typename Iterable, typename Callable>
2404 bool operator()(const Iterable& ticks, const Callable& f, const std::string& format)
2405 {
2406 if(begin(ticks) == end(ticks)) return true;
2407
2408 // We could use additional meta-programming to deduce the correct element type of y,
2409 // but all values have to be convertible to double anyways
2410 std::vector<double> y;
2411 for(auto x : ticks) y.push_back(f(x));
2412 return plot_impl<std::false_type>()(ticks,y,format);
2413 }
2414};
2415
2416} // end namespace detail
2417
2418// recursion stop for the above
2419template<typename... Args>
2420bool plot() { return true; }
2421
2422template<typename A, typename B, typename... Args>
2423bool plot(const A& a, const B& b, const std::string& format, Args... args)
2424{
2425 return detail::plot_impl<typename detail::is_callable<B>::type>()(a,b,format) && plot(args...);
2426}
2427
2428/*
2429 * This group of plot() functions is needed to support initializer lists, i.e. calling
2430 * plot( {1,2,3,4} )
2431 */
2432inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::string& format = "") {
2433 return plot<double,double>(x,y,format);
2434}
2435
2436inline bool plot(const std::vector<double>& y, const std::string& format = "") {
2437 return plot<double>(y,format);
2438}
2439
2440inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::map<std::string, std::string>& keywords) {
2441 return plot<double>(x,y,keywords);
2442}
2443
Austin Schuhab802d52020-07-03 18:11:11 -07002444/*
2445 * This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting
2446 */
2447class Plot
2448{
2449public:
2450 // default initialization with plot label, some data and format
2451 template<typename Numeric>
2452 Plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") {
2453 detail::_interpreter::get();
2454
2455 assert(x.size() == y.size());
2456
2457 PyObject* kwargs = PyDict_New();
2458 if(name != "")
2459 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
2460
2461 PyObject* xarray = detail::get_array(x);
2462 PyObject* yarray = detail::get_array(y);
2463
2464 PyObject* pystring = PyString_FromString(format.c_str());
2465
2466 PyObject* plot_args = PyTuple_New(3);
2467 PyTuple_SetItem(plot_args, 0, xarray);
2468 PyTuple_SetItem(plot_args, 1, yarray);
2469 PyTuple_SetItem(plot_args, 2, pystring);
2470
2471 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
2472
2473 Py_DECREF(kwargs);
2474 Py_DECREF(plot_args);
2475
2476 if(res)
2477 {
2478 line= PyList_GetItem(res, 0);
2479
2480 if(line)
2481 set_data_fct = PyObject_GetAttrString(line,"set_data");
2482 else
2483 Py_DECREF(line);
2484 Py_DECREF(res);
2485 }
2486 }
2487
2488 // shorter initialization with name or format only
2489 // basically calls line, = plot([], [])
2490 Plot(const std::string& name = "", const std::string& format = "")
2491 : Plot(name, std::vector<double>(), std::vector<double>(), format) {}
2492
2493 template<typename Numeric>
2494 bool update(const std::vector<Numeric>& x, const std::vector<Numeric>& y) {
2495 assert(x.size() == y.size());
2496 if(set_data_fct)
2497 {
2498 PyObject* xarray = detail::get_array(x);
2499 PyObject* yarray = detail::get_array(y);
2500
2501 PyObject* plot_args = PyTuple_New(2);
2502 PyTuple_SetItem(plot_args, 0, xarray);
2503 PyTuple_SetItem(plot_args, 1, yarray);
2504
2505 PyObject* res = PyObject_CallObject(set_data_fct, plot_args);
2506 if (res) Py_DECREF(res);
2507 return res;
2508 }
2509 return false;
2510 }
2511
2512 // clears the plot but keep it available
2513 bool clear() {
2514 return update(std::vector<double>(), std::vector<double>());
2515 }
2516
2517 // definitely remove this line
2518 void remove() {
2519 if(line)
2520 {
2521 auto remove_fct = PyObject_GetAttrString(line,"remove");
2522 PyObject* args = PyTuple_New(0);
2523 PyObject* res = PyObject_CallObject(remove_fct, args);
2524 if (res) Py_DECREF(res);
2525 }
2526 decref();
2527 }
2528
2529 ~Plot() {
2530 decref();
2531 }
2532private:
2533
2534 void decref() {
2535 if(line)
2536 Py_DECREF(line);
2537 if(set_data_fct)
2538 Py_DECREF(set_data_fct);
2539 }
2540
2541
2542 PyObject* line = nullptr;
2543 PyObject* set_data_fct = nullptr;
2544};
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002545
2546} // end namespace matplotlibcpp