blob: 35aeb543b9d3ac0f4bf808cac7d5bf58b817f317 [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.
147 wchar_t python_home[] = L"../python_repo/usr/";
148 wchar_t python_path[] =
149 L"../matplotlib_repo/3:../python_repo/usr/lib/python35.zip:../"
150 L"python_repo/usr/lib/python3.5:../python_repo/usr/lib/python3.5/"
151 L"plat-x86_64-linux-gnu:../python_repo/usr/lib/python3.5/"
152 L"lib-dynload:../python_repo/usr/lib/python3/dist-packages";
153
154 Py_SetPath(python_path);
155 Py_SetPythonHome(python_home);
156
157 // We fail really poorly if DISPLAY isn't set. We can do better.
158 if (getenv("DISPLAY") == nullptr) {
159 fprintf(stderr, "DISPLAY not set\n");
160 abort();
161 }
162
163 // TODO(austin): Confirm LD_LIBRARY_PATH does the right thing. Can't
164 // hurt.
165 setenv("LD_LIBRARY_PATH",
166 "../python_repo/lib/x86_64-linux-gnu:../python_repo/usr/lib:../"
167 "python_repo/usr/lib/x86_64-linux-gnu", 0);
168 Py_DontWriteBytecodeFlag = 1;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800169
170 // optional but recommended
171#if PY_MAJOR_VERSION >= 3
172 wchar_t name[] = L"plotting";
173#else
174 char name[] = "plotting";
175#endif
176 Py_SetProgramName(name);
177 Py_Initialize();
178
179#ifndef WITHOUT_NUMPY
180 import_numpy(); // initialize numpy C-API
181#endif
182
183 PyObject* matplotlibname = PyString_FromString("matplotlib");
184 PyObject* pyplotname = PyString_FromString("matplotlib.pyplot");
Austin Schuhad596222018-01-31 23:34:03 -0800185 PyObject* cmname = PyString_FromString("matplotlib.cm");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800186 PyObject* pylabname = PyString_FromString("pylab");
Austin Schuhab802d52020-07-03 18:11:11 -0700187 if (!pyplotname || !pylabname || !matplotlibname || !cmname) {
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800188 throw std::runtime_error("couldnt create string");
189 }
190
191 PyObject* matplotlib = PyImport_Import(matplotlibname);
192 Py_DECREF(matplotlibname);
Austin Schuhab802d52020-07-03 18:11:11 -0700193 if (!matplotlib) {
194 PyErr_Print();
195 throw std::runtime_error("Error loading module matplotlib!");
196 }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800197
198 // matplotlib.use() must be called *before* pylab, matplotlib.pyplot,
199 // or matplotlib.backends is imported for the first time
200 if (!s_backend.empty()) {
201 PyObject_CallMethod(matplotlib, const_cast<char*>("use"), const_cast<char*>("s"), s_backend.c_str());
202 }
203
204 PyObject* pymod = PyImport_Import(pyplotname);
205 Py_DECREF(pyplotname);
206 if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); }
207
Austin Schuhad596222018-01-31 23:34:03 -0800208 s_python_colormap = PyImport_Import(cmname);
209 Py_DECREF(cmname);
210 if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800211
212 PyObject* pylabmod = PyImport_Import(pylabname);
213 Py_DECREF(pylabname);
214 if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); }
215
Austin Schuhab802d52020-07-03 18:11:11 -0700216 s_python_function_arrow = safe_import(pymod, "arrow");
217 s_python_function_show = safe_import(pymod, "show");
218 s_python_function_close = safe_import(pymod, "close");
219 s_python_function_draw = safe_import(pymod, "draw");
220 s_python_function_pause = safe_import(pymod, "pause");
221 s_python_function_figure = safe_import(pymod, "figure");
222 s_python_function_fignum_exists = safe_import(pymod, "fignum_exists");
223 s_python_function_plot = safe_import(pymod, "plot");
224 s_python_function_quiver = safe_import(pymod, "quiver");
225 s_python_function_contour = safe_import(pymod, "contour");
226 s_python_function_semilogx = safe_import(pymod, "semilogx");
227 s_python_function_semilogy = safe_import(pymod, "semilogy");
228 s_python_function_loglog = safe_import(pymod, "loglog");
229 s_python_function_fill = safe_import(pymod, "fill");
230 s_python_function_fill_between = safe_import(pymod, "fill_between");
231 s_python_function_hist = safe_import(pymod,"hist");
232 s_python_function_scatter = safe_import(pymod,"scatter");
233 s_python_function_boxplot = safe_import(pymod,"boxplot");
234 s_python_function_subplot = safe_import(pymod, "subplot");
235 s_python_function_subplot2grid = safe_import(pymod, "subplot2grid");
236 s_python_function_legend = safe_import(pymod, "legend");
237 s_python_function_ylim = safe_import(pymod, "ylim");
238 s_python_function_title = safe_import(pymod, "title");
239 s_python_function_axis = safe_import(pymod, "axis");
240 s_python_function_axvline = safe_import(pymod, "axvline");
241 s_python_function_axvspan = safe_import(pymod, "axvspan");
242 s_python_function_xlabel = safe_import(pymod, "xlabel");
243 s_python_function_ylabel = safe_import(pymod, "ylabel");
244 s_python_function_gca = safe_import(pymod, "gca");
245 s_python_function_xticks = safe_import(pymod, "xticks");
246 s_python_function_yticks = safe_import(pymod, "yticks");
247 s_python_function_margins = safe_import(pymod, "margins");
248 s_python_function_tick_params = safe_import(pymod, "tick_params");
249 s_python_function_grid = safe_import(pymod, "grid");
250 s_python_function_xlim = safe_import(pymod, "xlim");
251 s_python_function_ion = safe_import(pymod, "ion");
252 s_python_function_ginput = safe_import(pymod, "ginput");
253 s_python_function_save = safe_import(pylabmod, "savefig");
254 s_python_function_annotate = safe_import(pymod,"annotate");
255 s_python_function_cla = safe_import(pymod, "cla");
256 s_python_function_clf = safe_import(pymod, "clf");
257 s_python_function_errorbar = safe_import(pymod, "errorbar");
258 s_python_function_tight_layout = safe_import(pymod, "tight_layout");
259 s_python_function_stem = safe_import(pymod, "stem");
260 s_python_function_xkcd = safe_import(pymod, "xkcd");
261 s_python_function_text = safe_import(pymod, "text");
262 s_python_function_suptitle = safe_import(pymod, "suptitle");
263 s_python_function_bar = safe_import(pymod,"bar");
264 s_python_function_colorbar = PyObject_GetAttrString(pymod, "colorbar");
265 s_python_function_subplots_adjust = safe_import(pymod,"subplots_adjust");
266#ifndef WITHOUT_NUMPY
267 s_python_function_imshow = safe_import(pymod, "imshow");
268#endif
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800269 s_python_empty_tuple = PyTuple_New(0);
270 }
271
272 ~_interpreter() {
273 Py_Finalize();
274 }
275};
276
277} // end namespace detail
278
Austin Schuhab802d52020-07-03 18:11:11 -0700279/// Select the backend
280///
281/// **NOTE:** This must be called before the first plot command to have
282/// any effect.
283///
284/// Mainly useful to select the non-interactive 'Agg' backend when running
285/// matplotlibcpp in headless mode, for example on a machine with no display.
286///
287/// See also: https://matplotlib.org/2.0.2/api/matplotlib_configuration_api.html#matplotlib.use
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800288inline void backend(const std::string& name)
289{
290 detail::s_backend = name;
291}
292
293inline bool annotate(std::string annotation, double x, double y)
294{
Austin Schuhab802d52020-07-03 18:11:11 -0700295 detail::_interpreter::get();
296
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800297 PyObject * xy = PyTuple_New(2);
298 PyObject * str = PyString_FromString(annotation.c_str());
299
300 PyTuple_SetItem(xy,0,PyFloat_FromDouble(x));
301 PyTuple_SetItem(xy,1,PyFloat_FromDouble(y));
302
303 PyObject* kwargs = PyDict_New();
304 PyDict_SetItemString(kwargs, "xy", xy);
305
306 PyObject* args = PyTuple_New(1);
307 PyTuple_SetItem(args, 0, str);
308
309 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs);
310
311 Py_DECREF(args);
312 Py_DECREF(kwargs);
313
314 if(res) Py_DECREF(res);
315
316 return res;
317}
318
Austin Schuhab802d52020-07-03 18:11:11 -0700319namespace detail {
320
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800321#ifndef WITHOUT_NUMPY
322// Type selector for numpy array conversion
323template <typename T> struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default
324template <> struct select_npy_type<double> { const static NPY_TYPES type = NPY_DOUBLE; };
325template <> struct select_npy_type<float> { const static NPY_TYPES type = NPY_FLOAT; };
326template <> struct select_npy_type<bool> { const static NPY_TYPES type = NPY_BOOL; };
327template <> struct select_npy_type<int8_t> { const static NPY_TYPES type = NPY_INT8; };
328template <> struct select_npy_type<int16_t> { const static NPY_TYPES type = NPY_SHORT; };
329template <> struct select_npy_type<int32_t> { const static NPY_TYPES type = NPY_INT; };
330template <> struct select_npy_type<int64_t> { const static NPY_TYPES type = NPY_INT64; };
331template <> struct select_npy_type<uint8_t> { const static NPY_TYPES type = NPY_UINT8; };
332template <> struct select_npy_type<uint16_t> { const static NPY_TYPES type = NPY_USHORT; };
333template <> struct select_npy_type<uint32_t> { const static NPY_TYPES type = NPY_ULONG; };
334template <> struct select_npy_type<uint64_t> { const static NPY_TYPES type = NPY_UINT64; };
335
Austin Schuhab802d52020-07-03 18:11:11 -0700336// Sanity checks; comment them out or change the numpy type below if you're compiling on
337// a platform where they don't apply
338static_assert(sizeof(long long) == 8);
339template <> struct select_npy_type<long long> { const static NPY_TYPES type = NPY_INT64; };
340static_assert(sizeof(unsigned long long) == 8);
341template <> struct select_npy_type<unsigned long long> { const static NPY_TYPES type = NPY_UINT64; };
342// TODO: add int, long, etc.
343
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800344template<typename Numeric>
345PyObject* get_array(const std::vector<Numeric>& v)
346{
Austin Schuhab802d52020-07-03 18:11:11 -0700347 npy_intp vsize = v.size();
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800348 NPY_TYPES type = select_npy_type<Numeric>::type;
Austin Schuhab802d52020-07-03 18:11:11 -0700349 if (type == NPY_NOTYPE) {
350 size_t memsize = v.size()*sizeof(double);
351 double* dp = static_cast<double*>(::malloc(memsize));
352 for (size_t i=0; i<v.size(); ++i)
353 dp[i] = v[i];
354 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, dp);
355 PyArray_UpdateFlags(reinterpret_cast<PyArrayObject*>(varray), NPY_ARRAY_OWNDATA);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800356 return varray;
357 }
Austin Schuhab802d52020-07-03 18:11:11 -0700358
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800359 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data()));
360 return varray;
361}
362
Austin Schuhab802d52020-07-03 18:11:11 -0700363
Austin Schuhad596222018-01-31 23:34:03 -0800364template<typename Numeric>
365PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
366{
Austin Schuhad596222018-01-31 23:34:03 -0800367 if (v.size() < 1) throw std::runtime_error("get_2d_array v too small");
368
369 npy_intp vsize[2] = {static_cast<npy_intp>(v.size()),
370 static_cast<npy_intp>(v[0].size())};
371
372 PyArrayObject *varray =
373 (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE);
374
375 double *vd_begin = static_cast<double *>(PyArray_DATA(varray));
376
377 for (const ::std::vector<Numeric> &v_row : v) {
378 if (v_row.size() != static_cast<size_t>(vsize[1]))
379 throw std::runtime_error("Missmatched array size");
380 std::copy(v_row.begin(), v_row.end(), vd_begin);
381 vd_begin += vsize[1];
382 }
383
384 return reinterpret_cast<PyObject *>(varray);
385}
386
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800387#else // fallback if we don't have numpy: copy every element of the given vector
388
389template<typename Numeric>
390PyObject* get_array(const std::vector<Numeric>& v)
391{
392 PyObject* list = PyList_New(v.size());
393 for(size_t i = 0; i < v.size(); ++i) {
394 PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i)));
395 }
396 return list;
397}
398
399#endif // WITHOUT_NUMPY
400
Austin Schuhab802d52020-07-03 18:11:11 -0700401// sometimes, for labels and such, we need string arrays
402inline PyObject * get_array(const std::vector<std::string>& strings)
403{
404 PyObject* list = PyList_New(strings.size());
405 for (std::size_t i = 0; i < strings.size(); ++i) {
406 PyList_SetItem(list, i, PyString_FromString(strings[i].c_str()));
407 }
408 return list;
409}
410
411// not all matplotlib need 2d arrays, some prefer lists of lists
412template<typename Numeric>
413PyObject* get_listlist(const std::vector<std::vector<Numeric>>& ll)
414{
415 PyObject* listlist = PyList_New(ll.size());
416 for (std::size_t i = 0; i < ll.size(); ++i) {
417 PyList_SetItem(listlist, i, get_array(ll[i]));
418 }
419 return listlist;
420}
421
422} // namespace detail
423
424/// Plot a line through the given x and y data points..
425///
426/// See: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.plot.html
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800427template<typename Numeric>
428bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
429{
430 assert(x.size() == y.size());
431
Austin Schuhab802d52020-07-03 18:11:11 -0700432 detail::_interpreter::get();
433
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800434 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700435 PyObject* xarray = detail::get_array(x);
436 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800437
438 // construct positional args
439 PyObject* args = PyTuple_New(2);
440 PyTuple_SetItem(args, 0, xarray);
441 PyTuple_SetItem(args, 1, yarray);
442
443 // construct keyword args
444 PyObject* kwargs = PyDict_New();
445 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
446 {
447 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
448 }
449
450 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs);
451
452 Py_DECREF(args);
453 Py_DECREF(kwargs);
454 if(res) Py_DECREF(res);
455
456 return res;
457}
458
Austin Schuhab802d52020-07-03 18:11:11 -0700459// TODO - it should be possible to make this work by implementing
460// a non-numpy alternative for `detail::get_2darray()`.
461#ifndef WITHOUT_NUMPY
Austin Schuhad596222018-01-31 23:34:03 -0800462template <typename Numeric>
463void plot_surface(const std::vector<::std::vector<Numeric>> &x,
464 const std::vector<::std::vector<Numeric>> &y,
465 const std::vector<::std::vector<Numeric>> &z,
466 const std::map<std::string, std::string> &keywords =
Austin Schuhab802d52020-07-03 18:11:11 -0700467 std::map<std::string, std::string>())
468{
469 detail::_interpreter::get();
470
471 // We lazily load the modules here the first time this function is called
472 // because I'm not sure that we can assume "matplotlib installed" implies
473 // "mpl_toolkits installed" on all platforms, and we don't want to require
474 // it for people who don't need 3d plots.
475 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
476 if (!mpl_toolkitsmod) {
477 detail::_interpreter::get();
478
479 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
480 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
481 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
482
483 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
484 Py_DECREF(mpl_toolkits);
485 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
486
487 axis3dmod = PyImport_Import(axis3d);
488 Py_DECREF(axis3d);
489 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
490 }
491
Austin Schuhad596222018-01-31 23:34:03 -0800492 assert(x.size() == y.size());
493 assert(y.size() == z.size());
494
495 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700496 PyObject *xarray = detail::get_2darray(x);
497 PyObject *yarray = detail::get_2darray(y);
498 PyObject *zarray = detail::get_2darray(z);
Austin Schuhad596222018-01-31 23:34:03 -0800499
500 // construct positional args
501 PyObject *args = PyTuple_New(3);
502 PyTuple_SetItem(args, 0, xarray);
503 PyTuple_SetItem(args, 1, yarray);
504 PyTuple_SetItem(args, 2, zarray);
505
506 // Build up the kw args.
507 PyObject *kwargs = PyDict_New();
508 PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1));
509 PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1));
510
511 PyObject *python_colormap_coolwarm = PyObject_GetAttrString(
512 detail::_interpreter::get().s_python_colormap, "coolwarm");
513
514 PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm);
515
516 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
517 it != keywords.end(); ++it) {
518 PyDict_SetItemString(kwargs, it->first.c_str(),
519 PyString_FromString(it->second.c_str()));
520 }
521
522
523 PyObject *fig =
524 PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
525 detail::_interpreter::get().s_python_empty_tuple);
526 if (!fig) throw std::runtime_error("Call to figure() failed.");
527
528 PyObject *gca_kwargs = PyDict_New();
529 PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
530
531 PyObject *gca = PyObject_GetAttrString(fig, "gca");
532 if (!gca) throw std::runtime_error("No gca");
533 Py_INCREF(gca);
534 PyObject *axis = PyObject_Call(
535 gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
536
537 if (!axis) throw std::runtime_error("No axis");
538 Py_INCREF(axis);
539
540 Py_DECREF(gca);
541 Py_DECREF(gca_kwargs);
542
543 PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface");
544 if (!plot_surface) throw std::runtime_error("No surface");
545 Py_INCREF(plot_surface);
546 PyObject *res = PyObject_Call(plot_surface, args, kwargs);
547 if (!res) throw std::runtime_error("failed surface");
548 Py_DECREF(plot_surface);
549
550 Py_DECREF(axis);
551 Py_DECREF(args);
552 Py_DECREF(kwargs);
553 if (res) Py_DECREF(res);
554}
Austin Schuhab802d52020-07-03 18:11:11 -0700555#endif // WITHOUT_NUMPY
556
557template <typename Numeric>
558void plot3(const std::vector<Numeric> &x,
559 const std::vector<Numeric> &y,
560 const std::vector<Numeric> &z,
561 const std::map<std::string, std::string> &keywords =
562 std::map<std::string, std::string>())
563{
564 detail::_interpreter::get();
565
566 // Same as with plot_surface: We lazily load the modules here the first time
567 // this function is called because I'm not sure that we can assume "matplotlib
568 // installed" implies "mpl_toolkits installed" on all platforms, and we don't
569 // want to require it for people who don't need 3d plots.
570 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
571 if (!mpl_toolkitsmod) {
572 detail::_interpreter::get();
573
574 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
575 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
576 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
577
578 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
579 Py_DECREF(mpl_toolkits);
580 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
581
582 axis3dmod = PyImport_Import(axis3d);
583 Py_DECREF(axis3d);
584 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
585 }
586
587 assert(x.size() == y.size());
588 assert(y.size() == z.size());
589
590 PyObject *xarray = detail::get_array(x);
591 PyObject *yarray = detail::get_array(y);
592 PyObject *zarray = detail::get_array(z);
593
594 // construct positional args
595 PyObject *args = PyTuple_New(3);
596 PyTuple_SetItem(args, 0, xarray);
597 PyTuple_SetItem(args, 1, yarray);
598 PyTuple_SetItem(args, 2, zarray);
599
600 // Build up the kw args.
601 PyObject *kwargs = PyDict_New();
602
603 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
604 it != keywords.end(); ++it) {
605 PyDict_SetItemString(kwargs, it->first.c_str(),
606 PyString_FromString(it->second.c_str()));
607 }
608
609 PyObject *fig =
610 PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
611 detail::_interpreter::get().s_python_empty_tuple);
612 if (!fig) throw std::runtime_error("Call to figure() failed.");
613
614 PyObject *gca_kwargs = PyDict_New();
615 PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
616
617 PyObject *gca = PyObject_GetAttrString(fig, "gca");
618 if (!gca) throw std::runtime_error("No gca");
619 Py_INCREF(gca);
620 PyObject *axis = PyObject_Call(
621 gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
622
623 if (!axis) throw std::runtime_error("No axis");
624 Py_INCREF(axis);
625
626 Py_DECREF(gca);
627 Py_DECREF(gca_kwargs);
628
629 PyObject *plot3 = PyObject_GetAttrString(axis, "plot");
630 if (!plot3) throw std::runtime_error("No 3D line plot");
631 Py_INCREF(plot3);
632 PyObject *res = PyObject_Call(plot3, args, kwargs);
633 if (!res) throw std::runtime_error("Failed 3D line plot");
634 Py_DECREF(plot3);
635
636 Py_DECREF(axis);
637 Py_DECREF(args);
638 Py_DECREF(kwargs);
639 if (res) Py_DECREF(res);
640}
Austin Schuhad596222018-01-31 23:34:03 -0800641
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800642template<typename Numeric>
643bool stem(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
644{
645 assert(x.size() == y.size());
646
Austin Schuhab802d52020-07-03 18:11:11 -0700647 detail::_interpreter::get();
648
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800649 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700650 PyObject* xarray = detail::get_array(x);
651 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800652
653 // construct positional args
654 PyObject* args = PyTuple_New(2);
655 PyTuple_SetItem(args, 0, xarray);
656 PyTuple_SetItem(args, 1, yarray);
657
658 // construct keyword args
659 PyObject* kwargs = PyDict_New();
660 for (std::map<std::string, std::string>::const_iterator it =
661 keywords.begin(); it != keywords.end(); ++it) {
662 PyDict_SetItemString(kwargs, it->first.c_str(),
663 PyString_FromString(it->second.c_str()));
664 }
665
666 PyObject* res = PyObject_Call(
667 detail::_interpreter::get().s_python_function_stem, args, kwargs);
668
669 Py_DECREF(args);
670 Py_DECREF(kwargs);
671 if (res)
672 Py_DECREF(res);
673
674 return res;
675}
676
677template< typename Numeric >
Austin Schuhab802d52020-07-03 18:11:11 -0700678bool fill(const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::map<std::string, std::string>& keywords)
679{
680 assert(x.size() == y.size());
681
682 detail::_interpreter::get();
683
684 // using numpy arrays
685 PyObject* xarray = detail::get_array(x);
686 PyObject* yarray = detail::get_array(y);
687
688 // construct positional args
689 PyObject* args = PyTuple_New(2);
690 PyTuple_SetItem(args, 0, xarray);
691 PyTuple_SetItem(args, 1, yarray);
692
693 // construct keyword args
694 PyObject* kwargs = PyDict_New();
695 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
696 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
697 }
698
699 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill, args, kwargs);
700
701 Py_DECREF(args);
702 Py_DECREF(kwargs);
703
704 if (res) Py_DECREF(res);
705
706 return res;
707}
708
709template< typename Numeric >
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800710bool 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)
711{
712 assert(x.size() == y1.size());
713 assert(x.size() == y2.size());
714
Austin Schuhab802d52020-07-03 18:11:11 -0700715 detail::_interpreter::get();
716
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800717 // using numpy arrays
Austin Schuhab802d52020-07-03 18:11:11 -0700718 PyObject* xarray = detail::get_array(x);
719 PyObject* y1array = detail::get_array(y1);
720 PyObject* y2array = detail::get_array(y2);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800721
722 // construct positional args
723 PyObject* args = PyTuple_New(3);
724 PyTuple_SetItem(args, 0, xarray);
725 PyTuple_SetItem(args, 1, y1array);
726 PyTuple_SetItem(args, 2, y2array);
727
728 // construct keyword args
729 PyObject* kwargs = PyDict_New();
Austin Schuhab802d52020-07-03 18:11:11 -0700730 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it) {
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800731 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
732 }
733
734 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs);
735
736 Py_DECREF(args);
737 Py_DECREF(kwargs);
738 if(res) Py_DECREF(res);
739
740 return res;
741}
742
Austin Schuhab802d52020-07-03 18:11:11 -0700743template <typename Numeric>
744bool arrow(Numeric x, Numeric y, Numeric end_x, Numeric end_y, const std::string& fc = "r",
745 const std::string ec = "k", Numeric head_length = 0.25, Numeric head_width = 0.1625) {
746 PyObject* obj_x = PyFloat_FromDouble(x);
747 PyObject* obj_y = PyFloat_FromDouble(y);
748 PyObject* obj_end_x = PyFloat_FromDouble(end_x);
749 PyObject* obj_end_y = PyFloat_FromDouble(end_y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800750
Austin Schuhab802d52020-07-03 18:11:11 -0700751 PyObject* kwargs = PyDict_New();
752 PyDict_SetItemString(kwargs, "fc", PyString_FromString(fc.c_str()));
753 PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str()));
754 PyDict_SetItemString(kwargs, "head_width", PyFloat_FromDouble(head_width));
755 PyDict_SetItemString(kwargs, "head_length", PyFloat_FromDouble(head_length));
756
757 PyObject* plot_args = PyTuple_New(4);
758 PyTuple_SetItem(plot_args, 0, obj_x);
759 PyTuple_SetItem(plot_args, 1, obj_y);
760 PyTuple_SetItem(plot_args, 2, obj_end_x);
761 PyTuple_SetItem(plot_args, 3, obj_end_y);
762
763 PyObject* res =
764 PyObject_Call(detail::_interpreter::get().s_python_function_arrow, plot_args, kwargs);
765
766 Py_DECREF(plot_args);
767 Py_DECREF(kwargs);
768 if (res)
769 Py_DECREF(res);
770
771 return res;
772}
773
774template< typename Numeric>
775bool hist(const std::vector<Numeric>& y, long bins=10,std::string color="b",
776 double alpha=1.0, bool cumulative=false)
777{
778 detail::_interpreter::get();
779
780 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800781
782 PyObject* kwargs = PyDict_New();
783 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
784 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
785 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
Austin Schuhab802d52020-07-03 18:11:11 -0700786 PyDict_SetItemString(kwargs, "cumulative", cumulative ? Py_True : Py_False);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800787
788 PyObject* plot_args = PyTuple_New(1);
789
790 PyTuple_SetItem(plot_args, 0, yarray);
791
792
793 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
794
795
796 Py_DECREF(plot_args);
797 Py_DECREF(kwargs);
798 if(res) Py_DECREF(res);
799
800 return res;
801}
802
Austin Schuhab802d52020-07-03 18:11:11 -0700803#ifndef WITHOUT_NUMPY
804namespace detail {
805
806inline 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)
807{
808 assert(type == NPY_UINT8 || type == NPY_FLOAT);
809 assert(colors == 1 || colors == 3 || colors == 4);
810
811 detail::_interpreter::get();
812
813 // construct args
814 npy_intp dims[3] = { rows, columns, colors };
815 PyObject *args = PyTuple_New(1);
816 PyTuple_SetItem(args, 0, PyArray_SimpleNewFromData(colors == 1 ? 2 : 3, dims, type, ptr));
817
818 // construct keyword args
819 PyObject* kwargs = PyDict_New();
820 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
821 {
822 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
823 }
824
825 PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_imshow, args, kwargs);
826 Py_DECREF(args);
827 Py_DECREF(kwargs);
828 if (!res)
829 throw std::runtime_error("Call to imshow() failed");
830 if (out)
831 *out = res;
832 else
833 Py_DECREF(res);
834}
835
836} // namespace detail
837
838inline 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)
839{
840 detail::imshow((void *) ptr, NPY_UINT8, rows, columns, colors, keywords, out);
841}
842
843inline 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)
844{
845 detail::imshow((void *) ptr, NPY_FLOAT, rows, columns, colors, keywords, out);
846}
847
848#ifdef WITH_OPENCV
849void imshow(const cv::Mat &image, const std::map<std::string, std::string> &keywords = {})
850{
851 // Convert underlying type of matrix, if needed
852 cv::Mat image2;
853 NPY_TYPES npy_type = NPY_UINT8;
854 switch (image.type() & CV_MAT_DEPTH_MASK) {
855 case CV_8U:
856 image2 = image;
857 break;
858 case CV_32F:
859 image2 = image;
860 npy_type = NPY_FLOAT;
861 break;
862 default:
863 image.convertTo(image2, CV_MAKETYPE(CV_8U, image.channels()));
864 }
865
866 // If color image, convert from BGR to RGB
867 switch (image2.channels()) {
868 case 3:
869 cv::cvtColor(image2, image2, CV_BGR2RGB);
870 break;
871 case 4:
872 cv::cvtColor(image2, image2, CV_BGRA2RGBA);
873 }
874
875 detail::imshow(image2.data, npy_type, image2.rows, image2.cols, image2.channels(), keywords);
876}
877#endif // WITH_OPENCV
878#endif // WITHOUT_NUMPY
879
880template<typename NumericX, typename NumericY>
881bool scatter(const std::vector<NumericX>& x,
882 const std::vector<NumericY>& y,
883 const double s=1.0, // The marker size in points**2
884 const std::map<std::string, std::string> & keywords = {})
885{
886 detail::_interpreter::get();
887
888 assert(x.size() == y.size());
889
890 PyObject* xarray = detail::get_array(x);
891 PyObject* yarray = detail::get_array(y);
892
893 PyObject* kwargs = PyDict_New();
894 PyDict_SetItemString(kwargs, "s", PyLong_FromLong(s));
895 for (const auto& it : keywords)
896 {
897 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
898 }
899
900 PyObject* plot_args = PyTuple_New(2);
901 PyTuple_SetItem(plot_args, 0, xarray);
902 PyTuple_SetItem(plot_args, 1, yarray);
903
904 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_scatter, plot_args, kwargs);
905
906 Py_DECREF(plot_args);
907 Py_DECREF(kwargs);
908 if(res) Py_DECREF(res);
909
910 return res;
911}
912
913template<typename Numeric>
914bool boxplot(const std::vector<std::vector<Numeric>>& data,
915 const std::vector<std::string>& labels = {},
916 const std::map<std::string, std::string> & keywords = {})
917{
918 detail::_interpreter::get();
919
920 PyObject* listlist = detail::get_listlist(data);
921 PyObject* args = PyTuple_New(1);
922 PyTuple_SetItem(args, 0, listlist);
923
924 PyObject* kwargs = PyDict_New();
925
926 // kwargs needs the labels, if there are (the correct number of) labels
927 if (!labels.empty() && labels.size() == data.size()) {
928 PyDict_SetItemString(kwargs, "labels", detail::get_array(labels));
929 }
930
931 // take care of the remaining keywords
932 for (const auto& it : keywords)
933 {
934 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
935 }
936
937 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
938
939 Py_DECREF(args);
940 Py_DECREF(kwargs);
941
942 if(res) Py_DECREF(res);
943
944 return res;
945}
946
947template<typename Numeric>
948bool boxplot(const std::vector<Numeric>& data,
949 const std::map<std::string, std::string> & keywords = {})
950{
951 detail::_interpreter::get();
952
953 PyObject* vector = detail::get_array(data);
954 PyObject* args = PyTuple_New(1);
955 PyTuple_SetItem(args, 0, vector);
956
957 PyObject* kwargs = PyDict_New();
958 for (const auto& it : keywords)
959 {
960 PyDict_SetItemString(kwargs, it.first.c_str(), PyString_FromString(it.second.c_str()));
961 }
962
963 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_boxplot, args, kwargs);
964
965 Py_DECREF(args);
966 Py_DECREF(kwargs);
967
968 if(res) Py_DECREF(res);
969
970 return res;
971}
972
973template <typename Numeric>
974bool bar(const std::vector<Numeric> & x,
975 const std::vector<Numeric> & y,
976 std::string ec = "black",
977 std::string ls = "-",
978 double lw = 1.0,
979 const std::map<std::string, std::string> & keywords = {})
980{
981 detail::_interpreter::get();
982
983 PyObject * xarray = detail::get_array(x);
984 PyObject * yarray = detail::get_array(y);
985
986 PyObject * kwargs = PyDict_New();
987
988 PyDict_SetItemString(kwargs, "ec", PyString_FromString(ec.c_str()));
989 PyDict_SetItemString(kwargs, "ls", PyString_FromString(ls.c_str()));
990 PyDict_SetItemString(kwargs, "lw", PyFloat_FromDouble(lw));
991
992 for (std::map<std::string, std::string>::const_iterator it =
993 keywords.begin();
994 it != keywords.end();
995 ++it) {
996 PyDict_SetItemString(
997 kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
998 }
999
1000 PyObject * plot_args = PyTuple_New(2);
1001 PyTuple_SetItem(plot_args, 0, xarray);
1002 PyTuple_SetItem(plot_args, 1, yarray);
1003
1004 PyObject * res = PyObject_Call(
1005 detail::_interpreter::get().s_python_function_bar, plot_args, kwargs);
1006
1007 Py_DECREF(plot_args);
1008 Py_DECREF(kwargs);
1009 if (res) Py_DECREF(res);
1010
1011 return res;
1012}
1013
1014template <typename Numeric>
1015bool bar(const std::vector<Numeric> & y,
1016 std::string ec = "black",
1017 std::string ls = "-",
1018 double lw = 1.0,
1019 const std::map<std::string, std::string> & keywords = {})
1020{
1021 using T = typename std::remove_reference<decltype(y)>::type::value_type;
1022
1023 detail::_interpreter::get();
1024
1025 std::vector<T> x;
1026 for (std::size_t i = 0; i < y.size(); i++) { x.push_back(i); }
1027
1028 return bar(x, y, ec, ls, lw, keywords);
1029}
1030
1031inline bool subplots_adjust(const std::map<std::string, double>& keywords = {})
1032{
1033 detail::_interpreter::get();
1034
1035 PyObject* kwargs = PyDict_New();
1036 for (std::map<std::string, double>::const_iterator it =
1037 keywords.begin(); it != keywords.end(); ++it) {
1038 PyDict_SetItemString(kwargs, it->first.c_str(),
1039 PyFloat_FromDouble(it->second));
1040 }
1041
1042
1043 PyObject* plot_args = PyTuple_New(0);
1044
1045 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_subplots_adjust, plot_args, kwargs);
1046
1047 Py_DECREF(plot_args);
1048 Py_DECREF(kwargs);
1049 if(res) Py_DECREF(res);
1050
1051 return res;
1052}
1053
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001054template< typename Numeric>
1055bool named_hist(std::string label,const std::vector<Numeric>& y, long bins=10, std::string color="b", double alpha=1.0)
1056{
Austin Schuhab802d52020-07-03 18:11:11 -07001057 detail::_interpreter::get();
1058
1059 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001060
1061 PyObject* kwargs = PyDict_New();
1062 PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str()));
1063 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
1064 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
1065 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
1066
1067
1068 PyObject* plot_args = PyTuple_New(1);
1069 PyTuple_SetItem(plot_args, 0, yarray);
1070
1071 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
1072
1073 Py_DECREF(plot_args);
1074 Py_DECREF(kwargs);
1075 if(res) Py_DECREF(res);
1076
1077 return res;
1078}
1079
1080template<typename NumericX, typename NumericY>
1081bool plot(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1082{
1083 assert(x.size() == y.size());
1084
Austin Schuhab802d52020-07-03 18:11:11 -07001085 detail::_interpreter::get();
1086
1087 PyObject* xarray = detail::get_array(x);
1088 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001089
1090 PyObject* pystring = PyString_FromString(s.c_str());
1091
1092 PyObject* plot_args = PyTuple_New(3);
1093 PyTuple_SetItem(plot_args, 0, xarray);
1094 PyTuple_SetItem(plot_args, 1, yarray);
1095 PyTuple_SetItem(plot_args, 2, pystring);
1096
1097 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
1098
1099 Py_DECREF(plot_args);
1100 if(res) Py_DECREF(res);
1101
1102 return res;
1103}
1104
Austin Schuhab802d52020-07-03 18:11:11 -07001105template <typename NumericX, typename NumericY, typename NumericZ>
1106bool contour(const std::vector<NumericX>& x, const std::vector<NumericY>& y,
1107 const std::vector<NumericZ>& z,
1108 const std::map<std::string, std::string>& keywords = {}) {
1109 assert(x.size() == y.size() && x.size() == z.size());
1110
1111 PyObject* xarray = get_array(x);
1112 PyObject* yarray = get_array(y);
1113 PyObject* zarray = get_array(z);
1114
1115 PyObject* plot_args = PyTuple_New(3);
1116 PyTuple_SetItem(plot_args, 0, xarray);
1117 PyTuple_SetItem(plot_args, 1, yarray);
1118 PyTuple_SetItem(plot_args, 2, zarray);
1119
1120 // construct keyword args
1121 PyObject* kwargs = PyDict_New();
1122 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
1123 it != keywords.end(); ++it) {
1124 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1125 }
1126
1127 PyObject* res =
1128 PyObject_Call(detail::_interpreter::get().s_python_function_contour, plot_args, kwargs);
1129
1130 Py_DECREF(kwargs);
1131 Py_DECREF(plot_args);
1132 if (res)
1133 Py_DECREF(res);
1134
1135 return res;
1136}
1137
1138template<typename NumericX, typename NumericY, typename NumericU, typename NumericW>
1139bool 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 = {})
1140{
1141 assert(x.size() == y.size() && x.size() == u.size() && u.size() == w.size());
1142
1143 detail::_interpreter::get();
1144
1145 PyObject* xarray = detail::get_array(x);
1146 PyObject* yarray = detail::get_array(y);
1147 PyObject* uarray = detail::get_array(u);
1148 PyObject* warray = detail::get_array(w);
1149
1150 PyObject* plot_args = PyTuple_New(4);
1151 PyTuple_SetItem(plot_args, 0, xarray);
1152 PyTuple_SetItem(plot_args, 1, yarray);
1153 PyTuple_SetItem(plot_args, 2, uarray);
1154 PyTuple_SetItem(plot_args, 3, warray);
1155
1156 // construct keyword args
1157 PyObject* kwargs = PyDict_New();
1158 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1159 {
1160 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1161 }
1162
1163 PyObject* res = PyObject_Call(
1164 detail::_interpreter::get().s_python_function_quiver, plot_args, kwargs);
1165
1166 Py_DECREF(kwargs);
1167 Py_DECREF(plot_args);
1168 if (res)
1169 Py_DECREF(res);
1170
1171 return res;
1172}
1173
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001174template<typename NumericX, typename NumericY>
1175bool stem(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1176{
1177 assert(x.size() == y.size());
1178
Austin Schuhab802d52020-07-03 18:11:11 -07001179 detail::_interpreter::get();
1180
1181 PyObject* xarray = detail::get_array(x);
1182 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001183
1184 PyObject* pystring = PyString_FromString(s.c_str());
1185
1186 PyObject* plot_args = PyTuple_New(3);
1187 PyTuple_SetItem(plot_args, 0, xarray);
1188 PyTuple_SetItem(plot_args, 1, yarray);
1189 PyTuple_SetItem(plot_args, 2, pystring);
1190
1191 PyObject* res = PyObject_CallObject(
1192 detail::_interpreter::get().s_python_function_stem, plot_args);
1193
1194 Py_DECREF(plot_args);
1195 if (res)
1196 Py_DECREF(res);
1197
1198 return res;
1199}
1200
1201template<typename NumericX, typename NumericY>
1202bool semilogx(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
1203{
1204 assert(x.size() == y.size());
1205
Austin Schuhab802d52020-07-03 18:11:11 -07001206 detail::_interpreter::get();
1207
1208 PyObject* xarray = detail::get_array(x);
1209 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001210
1211 PyObject* pystring = PyString_FromString(s.c_str());
1212
1213 PyObject* plot_args = PyTuple_New(3);
1214 PyTuple_SetItem(plot_args, 0, xarray);
1215 PyTuple_SetItem(plot_args, 1, yarray);
1216 PyTuple_SetItem(plot_args, 2, pystring);
1217
1218 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args);
1219
1220 Py_DECREF(plot_args);
1221 if(res) Py_DECREF(res);
1222
1223 return res;
1224}
1225
1226template<typename NumericX, typename NumericY>
1227bool semilogy(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_semilogy, 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 loglog(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_loglog, 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>
Austin Schuhab802d52020-07-03 18:11:11 -07001277bool 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 -08001278{
1279 assert(x.size() == y.size());
1280
Austin Schuhab802d52020-07-03 18:11:11 -07001281 detail::_interpreter::get();
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001282
Austin Schuhab802d52020-07-03 18:11:11 -07001283 PyObject* xarray = detail::get_array(x);
1284 PyObject* yarray = detail::get_array(y);
1285 PyObject* yerrarray = detail::get_array(yerr);
1286
1287 // construct keyword args
1288 PyObject* kwargs = PyDict_New();
1289 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1290 {
1291 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1292 }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001293
1294 PyDict_SetItemString(kwargs, "yerr", yerrarray);
1295
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001296 PyObject *plot_args = PyTuple_New(2);
1297 PyTuple_SetItem(plot_args, 0, xarray);
1298 PyTuple_SetItem(plot_args, 1, yarray);
1299
1300 PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_errorbar, plot_args, kwargs);
1301
1302 Py_DECREF(kwargs);
1303 Py_DECREF(plot_args);
1304
1305 if (res)
1306 Py_DECREF(res);
1307 else
1308 throw std::runtime_error("Call to errorbar() failed.");
1309
1310 return res;
1311}
1312
1313template<typename Numeric>
1314bool named_plot(const std::string& name, const std::vector<Numeric>& y, const std::string& format = "")
1315{
Austin Schuhab802d52020-07-03 18:11:11 -07001316 detail::_interpreter::get();
1317
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001318 PyObject* kwargs = PyDict_New();
1319 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1320
Austin Schuhab802d52020-07-03 18:11:11 -07001321 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001322
1323 PyObject* pystring = PyString_FromString(format.c_str());
1324
1325 PyObject* plot_args = PyTuple_New(2);
1326
1327 PyTuple_SetItem(plot_args, 0, yarray);
1328 PyTuple_SetItem(plot_args, 1, pystring);
1329
1330 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
1331
1332 Py_DECREF(kwargs);
1333 Py_DECREF(plot_args);
1334 if (res) Py_DECREF(res);
1335
1336 return res;
1337}
1338
1339template<typename Numeric>
1340bool named_plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1341{
Austin Schuhab802d52020-07-03 18:11:11 -07001342 detail::_interpreter::get();
1343
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001344 PyObject* kwargs = PyDict_New();
1345 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1346
Austin Schuhab802d52020-07-03 18:11:11 -07001347 PyObject* xarray = detail::get_array(x);
1348 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001349
1350 PyObject* pystring = PyString_FromString(format.c_str());
1351
1352 PyObject* plot_args = PyTuple_New(3);
1353 PyTuple_SetItem(plot_args, 0, xarray);
1354 PyTuple_SetItem(plot_args, 1, yarray);
1355 PyTuple_SetItem(plot_args, 2, pystring);
1356
1357 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
1358
1359 Py_DECREF(kwargs);
1360 Py_DECREF(plot_args);
1361 if (res) Py_DECREF(res);
1362
1363 return res;
1364}
1365
1366template<typename Numeric>
1367bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1368{
Austin Schuhab802d52020-07-03 18:11:11 -07001369 detail::_interpreter::get();
1370
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001371 PyObject* kwargs = PyDict_New();
1372 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1373
Austin Schuhab802d52020-07-03 18:11:11 -07001374 PyObject* xarray = detail::get_array(x);
1375 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001376
1377 PyObject* pystring = PyString_FromString(format.c_str());
1378
1379 PyObject* plot_args = PyTuple_New(3);
1380 PyTuple_SetItem(plot_args, 0, xarray);
1381 PyTuple_SetItem(plot_args, 1, yarray);
1382 PyTuple_SetItem(plot_args, 2, pystring);
1383
1384 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs);
1385
1386 Py_DECREF(kwargs);
1387 Py_DECREF(plot_args);
1388 if (res) Py_DECREF(res);
1389
1390 return res;
1391}
1392
1393template<typename Numeric>
1394bool named_semilogy(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1395{
Austin Schuhab802d52020-07-03 18:11:11 -07001396 detail::_interpreter::get();
1397
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001398 PyObject* kwargs = PyDict_New();
1399 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1400
Austin Schuhab802d52020-07-03 18:11:11 -07001401 PyObject* xarray = detail::get_array(x);
1402 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001403
1404 PyObject* pystring = PyString_FromString(format.c_str());
1405
1406 PyObject* plot_args = PyTuple_New(3);
1407 PyTuple_SetItem(plot_args, 0, xarray);
1408 PyTuple_SetItem(plot_args, 1, yarray);
1409 PyTuple_SetItem(plot_args, 2, pystring);
1410
1411 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs);
1412
1413 Py_DECREF(kwargs);
1414 Py_DECREF(plot_args);
1415 if (res) Py_DECREF(res);
1416
1417 return res;
1418}
1419
1420template<typename Numeric>
1421bool named_loglog(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
1422{
Austin Schuhab802d52020-07-03 18:11:11 -07001423 detail::_interpreter::get();
1424
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001425 PyObject* kwargs = PyDict_New();
1426 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1427
Austin Schuhab802d52020-07-03 18:11:11 -07001428 PyObject* xarray = detail::get_array(x);
1429 PyObject* yarray = detail::get_array(y);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001430
1431 PyObject* pystring = PyString_FromString(format.c_str());
1432
1433 PyObject* plot_args = PyTuple_New(3);
1434 PyTuple_SetItem(plot_args, 0, xarray);
1435 PyTuple_SetItem(plot_args, 1, yarray);
1436 PyTuple_SetItem(plot_args, 2, pystring);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001437 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs);
1438
1439 Py_DECREF(kwargs);
1440 Py_DECREF(plot_args);
1441 if (res) Py_DECREF(res);
1442
1443 return res;
1444}
1445
1446template<typename Numeric>
1447bool plot(const std::vector<Numeric>& y, const std::string& format = "")
1448{
1449 std::vector<Numeric> x(y.size());
1450 for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
1451 return plot(x,y,format);
1452}
1453
1454template<typename Numeric>
Austin Schuhab802d52020-07-03 18:11:11 -07001455bool plot(const std::vector<Numeric>& y, const std::map<std::string, std::string>& keywords)
1456{
1457 std::vector<Numeric> x(y.size());
1458 for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
1459 return plot(x,y,keywords);
1460}
1461
1462template<typename Numeric>
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001463bool stem(const std::vector<Numeric>& y, const std::string& format = "")
1464{
1465 std::vector<Numeric> x(y.size());
1466 for (size_t i = 0; i < x.size(); ++i) x.at(i) = i;
1467 return stem(x, y, format);
1468}
1469
Austin Schuhab802d52020-07-03 18:11:11 -07001470template<typename Numeric>
1471void text(Numeric x, Numeric y, const std::string& s = "")
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001472{
Austin Schuhab802d52020-07-03 18:11:11 -07001473 detail::_interpreter::get();
1474
1475 PyObject* args = PyTuple_New(3);
1476 PyTuple_SetItem(args, 0, PyFloat_FromDouble(x));
1477 PyTuple_SetItem(args, 1, PyFloat_FromDouble(y));
1478 PyTuple_SetItem(args, 2, PyString_FromString(s.c_str()));
1479
1480 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_text, args);
1481 if(!res) throw std::runtime_error("Call to text() failed.");
1482
1483 Py_DECREF(args);
1484 Py_DECREF(res);
1485}
1486
1487inline void colorbar(PyObject* mappable = NULL, const std::map<std::string, float>& keywords = {})
1488{
1489 if (mappable == NULL)
1490 throw std::runtime_error("Must call colorbar with PyObject* returned from an image, contour, surface, etc.");
1491
1492 detail::_interpreter::get();
1493
1494 PyObject* args = PyTuple_New(1);
1495 PyTuple_SetItem(args, 0, mappable);
1496
1497 PyObject* kwargs = PyDict_New();
1498 for(std::map<std::string, float>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1499 {
1500 PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(it->second));
1501 }
1502
1503 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_colorbar, args, kwargs);
1504 if(!res) throw std::runtime_error("Call to colorbar() failed.");
1505
1506 Py_DECREF(args);
1507 Py_DECREF(kwargs);
1508 Py_DECREF(res);
1509}
1510
1511
1512inline long figure(long number = -1)
1513{
1514 detail::_interpreter::get();
1515
1516 PyObject *res;
1517 if (number == -1)
1518 res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple);
1519 else {
1520 assert(number > 0);
1521
1522 // Make sure interpreter is initialised
1523 detail::_interpreter::get();
1524
1525 PyObject *args = PyTuple_New(1);
1526 PyTuple_SetItem(args, 0, PyLong_FromLong(number));
1527 res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, args);
1528 Py_DECREF(args);
1529 }
1530
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001531 if(!res) throw std::runtime_error("Call to figure() failed.");
1532
Austin Schuhab802d52020-07-03 18:11:11 -07001533 PyObject* num = PyObject_GetAttrString(res, "number");
1534 if (!num) throw std::runtime_error("Could not get number attribute of figure object");
1535 const long figureNumber = PyLong_AsLong(num);
1536
1537 Py_DECREF(num);
1538 Py_DECREF(res);
1539
1540 return figureNumber;
1541}
1542
1543inline bool fignum_exists(long number)
1544{
1545 detail::_interpreter::get();
1546
1547 PyObject *args = PyTuple_New(1);
1548 PyTuple_SetItem(args, 0, PyLong_FromLong(number));
1549 PyObject *res = PyObject_CallObject(detail::_interpreter::get().s_python_function_fignum_exists, args);
1550 if(!res) throw std::runtime_error("Call to fignum_exists() failed.");
1551
1552 bool ret = PyObject_IsTrue(res);
1553 Py_DECREF(res);
1554 Py_DECREF(args);
1555
1556 return ret;
1557}
1558
1559inline void figure_size(size_t w, size_t h)
1560{
1561 detail::_interpreter::get();
1562
1563 const size_t dpi = 100;
1564 PyObject* size = PyTuple_New(2);
1565 PyTuple_SetItem(size, 0, PyFloat_FromDouble((double)w / dpi));
1566 PyTuple_SetItem(size, 1, PyFloat_FromDouble((double)h / dpi));
1567
1568 PyObject* kwargs = PyDict_New();
1569 PyDict_SetItemString(kwargs, "figsize", size);
1570 PyDict_SetItemString(kwargs, "dpi", PyLong_FromSize_t(dpi));
1571
1572 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_figure,
1573 detail::_interpreter::get().s_python_empty_tuple, kwargs);
1574
1575 Py_DECREF(kwargs);
1576
1577 if(!res) throw std::runtime_error("Call to figure_size() failed.");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001578 Py_DECREF(res);
1579}
1580
1581inline void legend()
1582{
Austin Schuhab802d52020-07-03 18:11:11 -07001583 detail::_interpreter::get();
1584
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001585 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple);
1586 if(!res) throw std::runtime_error("Call to legend() failed.");
1587
1588 Py_DECREF(res);
1589}
1590
Austin Schuhab802d52020-07-03 18:11:11 -07001591inline void legend(const std::map<std::string, std::string>& keywords)
1592{
1593 detail::_interpreter::get();
1594
1595 // construct keyword args
1596 PyObject* kwargs = PyDict_New();
1597 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1598 {
1599 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1600 }
1601
1602 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple, kwargs);
1603 if(!res) throw std::runtime_error("Call to legend() failed.");
1604
1605 Py_DECREF(kwargs);
1606 Py_DECREF(res);
1607}
1608
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001609template<typename Numeric>
1610void ylim(Numeric left, Numeric right)
1611{
Austin Schuhab802d52020-07-03 18:11:11 -07001612 detail::_interpreter::get();
1613
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001614 PyObject* list = PyList_New(2);
1615 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
1616 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
1617
1618 PyObject* args = PyTuple_New(1);
1619 PyTuple_SetItem(args, 0, list);
1620
1621 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
1622 if(!res) throw std::runtime_error("Call to ylim() failed.");
1623
1624 Py_DECREF(args);
1625 Py_DECREF(res);
1626}
1627
1628template<typename Numeric>
1629void xlim(Numeric left, Numeric right)
1630{
Austin Schuhab802d52020-07-03 18:11:11 -07001631 detail::_interpreter::get();
1632
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001633 PyObject* list = PyList_New(2);
1634 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
1635 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
1636
1637 PyObject* args = PyTuple_New(1);
1638 PyTuple_SetItem(args, 0, list);
1639
1640 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
1641 if(!res) throw std::runtime_error("Call to xlim() failed.");
1642
1643 Py_DECREF(args);
1644 Py_DECREF(res);
1645}
1646
1647
1648inline double* xlim()
1649{
Austin Schuhab802d52020-07-03 18:11:11 -07001650 detail::_interpreter::get();
1651
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001652 PyObject* args = PyTuple_New(0);
1653 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
1654 PyObject* left = PyTuple_GetItem(res,0);
1655 PyObject* right = PyTuple_GetItem(res,1);
1656
1657 double* arr = new double[2];
1658 arr[0] = PyFloat_AsDouble(left);
1659 arr[1] = PyFloat_AsDouble(right);
1660
1661 if(!res) throw std::runtime_error("Call to xlim() failed.");
1662
1663 Py_DECREF(res);
1664 return arr;
1665}
1666
1667
1668inline double* ylim()
1669{
Austin Schuhab802d52020-07-03 18:11:11 -07001670 detail::_interpreter::get();
1671
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001672 PyObject* args = PyTuple_New(0);
1673 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
1674 PyObject* left = PyTuple_GetItem(res,0);
1675 PyObject* right = PyTuple_GetItem(res,1);
1676
1677 double* arr = new double[2];
1678 arr[0] = PyFloat_AsDouble(left);
1679 arr[1] = PyFloat_AsDouble(right);
1680
1681 if(!res) throw std::runtime_error("Call to ylim() failed.");
1682
1683 Py_DECREF(res);
1684 return arr;
1685}
1686
Austin Schuhab802d52020-07-03 18:11:11 -07001687template<typename Numeric>
1688inline void xticks(const std::vector<Numeric> &ticks, const std::vector<std::string> &labels = {}, const std::map<std::string, std::string>& keywords = {})
1689{
1690 assert(labels.size() == 0 || ticks.size() == labels.size());
1691
1692 detail::_interpreter::get();
1693
1694 // using numpy array
1695 PyObject* ticksarray = detail::get_array(ticks);
1696
1697 PyObject* args;
1698 if(labels.size() == 0) {
1699 // construct positional args
1700 args = PyTuple_New(1);
1701 PyTuple_SetItem(args, 0, ticksarray);
1702 } else {
1703 // make tuple of tick labels
1704 PyObject* labelstuple = PyTuple_New(labels.size());
1705 for (size_t i = 0; i < labels.size(); i++)
1706 PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str()));
1707
1708 // construct positional args
1709 args = PyTuple_New(2);
1710 PyTuple_SetItem(args, 0, ticksarray);
1711 PyTuple_SetItem(args, 1, labelstuple);
1712 }
1713
1714 // construct keyword args
1715 PyObject* kwargs = PyDict_New();
1716 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1717 {
1718 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1719 }
1720
1721 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xticks, args, kwargs);
1722
1723 Py_DECREF(args);
1724 Py_DECREF(kwargs);
1725 if(!res) throw std::runtime_error("Call to xticks() failed");
1726
1727 Py_DECREF(res);
1728}
1729
1730template<typename Numeric>
1731inline void xticks(const std::vector<Numeric> &ticks, const std::map<std::string, std::string>& keywords)
1732{
1733 xticks(ticks, {}, keywords);
1734}
1735
1736template<typename Numeric>
1737inline void yticks(const std::vector<Numeric> &ticks, const std::vector<std::string> &labels = {}, const std::map<std::string, std::string>& keywords = {})
1738{
1739 assert(labels.size() == 0 || ticks.size() == labels.size());
1740
1741 detail::_interpreter::get();
1742
1743 // using numpy array
1744 PyObject* ticksarray = detail::get_array(ticks);
1745
1746 PyObject* args;
1747 if(labels.size() == 0) {
1748 // construct positional args
1749 args = PyTuple_New(1);
1750 PyTuple_SetItem(args, 0, ticksarray);
1751 } else {
1752 // make tuple of tick labels
1753 PyObject* labelstuple = PyTuple_New(labels.size());
1754 for (size_t i = 0; i < labels.size(); i++)
1755 PyTuple_SetItem(labelstuple, i, PyUnicode_FromString(labels[i].c_str()));
1756
1757 // construct positional args
1758 args = PyTuple_New(2);
1759 PyTuple_SetItem(args, 0, ticksarray);
1760 PyTuple_SetItem(args, 1, labelstuple);
1761 }
1762
1763 // construct keyword args
1764 PyObject* kwargs = PyDict_New();
1765 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1766 {
1767 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1768 }
1769
1770 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_yticks, args, kwargs);
1771
1772 Py_DECREF(args);
1773 Py_DECREF(kwargs);
1774 if(!res) throw std::runtime_error("Call to yticks() failed");
1775
1776 Py_DECREF(res);
1777}
1778
1779template<typename Numeric>
1780inline void yticks(const std::vector<Numeric> &ticks, const std::map<std::string, std::string>& keywords)
1781{
1782 yticks(ticks, {}, keywords);
1783}
1784
1785template <typename Numeric> inline void margins(Numeric margin)
1786{
1787 // construct positional args
1788 PyObject* args = PyTuple_New(1);
1789 PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin));
1790
1791 PyObject* res =
1792 PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args);
1793 if (!res)
1794 throw std::runtime_error("Call to margins() failed.");
1795
1796 Py_DECREF(args);
1797 Py_DECREF(res);
1798}
1799
1800template <typename Numeric> inline void margins(Numeric margin_x, Numeric margin_y)
1801{
1802 // construct positional args
1803 PyObject* args = PyTuple_New(2);
1804 PyTuple_SetItem(args, 0, PyFloat_FromDouble(margin_x));
1805 PyTuple_SetItem(args, 1, PyFloat_FromDouble(margin_y));
1806
1807 PyObject* res =
1808 PyObject_CallObject(detail::_interpreter::get().s_python_function_margins, args);
1809 if (!res)
1810 throw std::runtime_error("Call to margins() failed.");
1811
1812 Py_DECREF(args);
1813 Py_DECREF(res);
1814}
1815
1816
1817inline void tick_params(const std::map<std::string, std::string>& keywords, const std::string axis = "both")
1818{
1819 detail::_interpreter::get();
1820
1821 // construct positional args
1822 PyObject* args;
1823 args = PyTuple_New(1);
1824 PyTuple_SetItem(args, 0, PyString_FromString(axis.c_str()));
1825
1826 // construct keyword args
1827 PyObject* kwargs = PyDict_New();
1828 for (std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1829 {
1830 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1831 }
1832
1833
1834 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_tick_params, args, kwargs);
1835
1836 Py_DECREF(args);
1837 Py_DECREF(kwargs);
1838 if (!res) throw std::runtime_error("Call to tick_params() failed");
1839
1840 Py_DECREF(res);
1841}
1842
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001843inline void subplot(long nrows, long ncols, long plot_number)
1844{
Austin Schuhab802d52020-07-03 18:11:11 -07001845 detail::_interpreter::get();
1846
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001847 // construct positional args
1848 PyObject* args = PyTuple_New(3);
1849 PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows));
1850 PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols));
1851 PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number));
1852
1853 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args);
1854 if(!res) throw std::runtime_error("Call to subplot() failed.");
1855
1856 Py_DECREF(args);
1857 Py_DECREF(res);
1858}
1859
Austin Schuhab802d52020-07-03 18:11:11 -07001860inline 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 -08001861{
Austin Schuhab802d52020-07-03 18:11:11 -07001862 detail::_interpreter::get();
1863
1864 PyObject* shape = PyTuple_New(2);
1865 PyTuple_SetItem(shape, 0, PyLong_FromLong(nrows));
1866 PyTuple_SetItem(shape, 1, PyLong_FromLong(ncols));
1867
1868 PyObject* loc = PyTuple_New(2);
1869 PyTuple_SetItem(loc, 0, PyLong_FromLong(rowid));
1870 PyTuple_SetItem(loc, 1, PyLong_FromLong(colid));
1871
1872 PyObject* args = PyTuple_New(4);
1873 PyTuple_SetItem(args, 0, shape);
1874 PyTuple_SetItem(args, 1, loc);
1875 PyTuple_SetItem(args, 2, PyLong_FromLong(rowspan));
1876 PyTuple_SetItem(args, 3, PyLong_FromLong(colspan));
1877
1878 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot2grid, args);
1879 if(!res) throw std::runtime_error("Call to subplot2grid() failed.");
1880
1881 Py_DECREF(shape);
1882 Py_DECREF(loc);
1883 Py_DECREF(args);
1884 Py_DECREF(res);
1885}
1886
1887inline void title(const std::string &titlestr, const std::map<std::string, std::string> &keywords = {})
1888{
1889 detail::_interpreter::get();
1890
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001891 PyObject* pytitlestr = PyString_FromString(titlestr.c_str());
1892 PyObject* args = PyTuple_New(1);
1893 PyTuple_SetItem(args, 0, pytitlestr);
1894
Austin Schuhab802d52020-07-03 18:11:11 -07001895 PyObject* kwargs = PyDict_New();
1896 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
1897 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1898 }
1899
1900 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_title, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001901 if(!res) throw std::runtime_error("Call to title() failed.");
1902
1903 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07001904 Py_DECREF(kwargs);
1905 Py_DECREF(res);
1906}
1907
1908inline void suptitle(const std::string &suptitlestr, const std::map<std::string, std::string> &keywords = {})
1909{
1910 detail::_interpreter::get();
1911
1912 PyObject* pysuptitlestr = PyString_FromString(suptitlestr.c_str());
1913 PyObject* args = PyTuple_New(1);
1914 PyTuple_SetItem(args, 0, pysuptitlestr);
1915
1916 PyObject* kwargs = PyDict_New();
1917 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
1918 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
1919 }
1920
1921 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_suptitle, args, kwargs);
1922 if(!res) throw std::runtime_error("Call to suptitle() failed.");
1923
1924 Py_DECREF(args);
1925 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001926 Py_DECREF(res);
1927}
1928
1929inline void axis(const std::string &axisstr)
1930{
Austin Schuhab802d52020-07-03 18:11:11 -07001931 detail::_interpreter::get();
1932
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001933 PyObject* str = PyString_FromString(axisstr.c_str());
1934 PyObject* args = PyTuple_New(1);
1935 PyTuple_SetItem(args, 0, str);
1936
1937 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args);
1938 if(!res) throw std::runtime_error("Call to title() failed.");
1939
1940 Py_DECREF(args);
1941 Py_DECREF(res);
1942}
1943
Austin Schuhab802d52020-07-03 18:11:11 -07001944inline 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 -08001945{
Austin Schuhab802d52020-07-03 18:11:11 -07001946 detail::_interpreter::get();
1947
1948 // construct positional args
1949 PyObject* args = PyTuple_New(3);
1950 PyTuple_SetItem(args, 0, PyFloat_FromDouble(x));
1951 PyTuple_SetItem(args, 1, PyFloat_FromDouble(ymin));
1952 PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymax));
1953
1954 // construct keyword args
1955 PyObject* kwargs = PyDict_New();
1956 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1957 {
1958 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1959 }
1960
1961 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvline, args, kwargs);
1962
1963 Py_DECREF(args);
1964 Py_DECREF(kwargs);
1965
1966 if(res) Py_DECREF(res);
1967}
1968
1969inline 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>())
1970{
1971 // construct positional args
1972 PyObject* args = PyTuple_New(4);
1973 PyTuple_SetItem(args, 0, PyFloat_FromDouble(xmin));
1974 PyTuple_SetItem(args, 1, PyFloat_FromDouble(xmax));
1975 PyTuple_SetItem(args, 2, PyFloat_FromDouble(ymin));
1976 PyTuple_SetItem(args, 3, PyFloat_FromDouble(ymax));
1977
1978 // construct keyword args
1979 PyObject* kwargs = PyDict_New();
1980 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
1981 {
1982 if (it->first == "linewidth" || it->first == "alpha")
1983 PyDict_SetItemString(kwargs, it->first.c_str(), PyFloat_FromDouble(std::stod(it->second)));
1984 else
1985 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
1986 }
1987
1988 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_axvspan, args, kwargs);
1989 Py_DECREF(args);
1990 Py_DECREF(kwargs);
1991
1992 if(res) Py_DECREF(res);
1993}
1994
1995inline void xlabel(const std::string &str, const std::map<std::string, std::string> &keywords = {})
1996{
1997 detail::_interpreter::get();
1998
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001999 PyObject* pystr = PyString_FromString(str.c_str());
2000 PyObject* args = PyTuple_New(1);
2001 PyTuple_SetItem(args, 0, pystr);
2002
Austin Schuhab802d52020-07-03 18:11:11 -07002003 PyObject* kwargs = PyDict_New();
2004 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2005 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2006 }
2007
2008 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_xlabel, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002009 if(!res) throw std::runtime_error("Call to xlabel() failed.");
2010
2011 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07002012 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002013 Py_DECREF(res);
2014}
2015
Austin Schuhab802d52020-07-03 18:11:11 -07002016inline void ylabel(const std::string &str, const std::map<std::string, std::string>& keywords = {})
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002017{
Austin Schuhab802d52020-07-03 18:11:11 -07002018 detail::_interpreter::get();
2019
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002020 PyObject* pystr = PyString_FromString(str.c_str());
2021 PyObject* args = PyTuple_New(1);
2022 PyTuple_SetItem(args, 0, pystr);
2023
Austin Schuhab802d52020-07-03 18:11:11 -07002024 PyObject* kwargs = PyDict_New();
2025 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2026 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2027 }
2028
2029 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_ylabel, args, kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002030 if(!res) throw std::runtime_error("Call to ylabel() failed.");
2031
2032 Py_DECREF(args);
Austin Schuhab802d52020-07-03 18:11:11 -07002033 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002034 Py_DECREF(res);
2035}
2036
Austin Schuhab802d52020-07-03 18:11:11 -07002037inline void set_zlabel(const std::string &str, const std::map<std::string, std::string>& keywords = {})
2038{
2039 detail::_interpreter::get();
2040
2041 // Same as with plot_surface: We lazily load the modules here the first time
2042 // this function is called because I'm not sure that we can assume "matplotlib
2043 // installed" implies "mpl_toolkits installed" on all platforms, and we don't
2044 // want to require it for people who don't need 3d plots.
2045 static PyObject *mpl_toolkitsmod = nullptr, *axis3dmod = nullptr;
2046 if (!mpl_toolkitsmod) {
2047 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
2048 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
2049 if (!mpl_toolkits || !axis3d) { throw std::runtime_error("couldnt create string"); }
2050
2051 mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
2052 Py_DECREF(mpl_toolkits);
2053 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
2054
2055 axis3dmod = PyImport_Import(axis3d);
2056 Py_DECREF(axis3d);
2057 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
2058 }
2059
2060 PyObject* pystr = PyString_FromString(str.c_str());
2061 PyObject* args = PyTuple_New(1);
2062 PyTuple_SetItem(args, 0, pystr);
2063
2064 PyObject* kwargs = PyDict_New();
2065 for (auto it = keywords.begin(); it != keywords.end(); ++it) {
2066 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2067 }
2068
2069 PyObject *ax =
2070 PyObject_CallObject(detail::_interpreter::get().s_python_function_gca,
2071 detail::_interpreter::get().s_python_empty_tuple);
2072 if (!ax) throw std::runtime_error("Call to gca() failed.");
2073 Py_INCREF(ax);
2074
2075 PyObject *zlabel = PyObject_GetAttrString(ax, "set_zlabel");
2076 if (!zlabel) throw std::runtime_error("Attribute set_zlabel not found.");
2077 Py_INCREF(zlabel);
2078
2079 PyObject *res = PyObject_Call(zlabel, args, kwargs);
2080 if (!res) throw std::runtime_error("Call to set_zlabel() failed.");
2081 Py_DECREF(zlabel);
2082
2083 Py_DECREF(ax);
2084 Py_DECREF(args);
2085 Py_DECREF(kwargs);
2086 if (res) Py_DECREF(res);
2087}
2088
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002089inline void grid(bool flag)
2090{
Austin Schuhab802d52020-07-03 18:11:11 -07002091 detail::_interpreter::get();
2092
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002093 PyObject* pyflag = flag ? Py_True : Py_False;
2094 Py_INCREF(pyflag);
2095
2096 PyObject* args = PyTuple_New(1);
2097 PyTuple_SetItem(args, 0, pyflag);
2098
2099 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args);
2100 if(!res) throw std::runtime_error("Call to grid() failed.");
2101
2102 Py_DECREF(args);
2103 Py_DECREF(res);
2104}
2105
2106inline void show(const bool block = true)
2107{
Austin Schuhab802d52020-07-03 18:11:11 -07002108 detail::_interpreter::get();
2109
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002110 PyObject* res;
2111 if(block)
2112 {
2113 res = PyObject_CallObject(
2114 detail::_interpreter::get().s_python_function_show,
2115 detail::_interpreter::get().s_python_empty_tuple);
2116 }
2117 else
2118 {
2119 PyObject *kwargs = PyDict_New();
2120 PyDict_SetItemString(kwargs, "block", Py_False);
2121 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 -07002122 Py_DECREF(kwargs);
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002123 }
2124
2125
2126 if (!res) throw std::runtime_error("Call to show() failed.");
2127
2128 Py_DECREF(res);
2129}
2130
2131inline void close()
2132{
Austin Schuhab802d52020-07-03 18:11:11 -07002133 detail::_interpreter::get();
2134
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002135 PyObject* res = PyObject_CallObject(
2136 detail::_interpreter::get().s_python_function_close,
2137 detail::_interpreter::get().s_python_empty_tuple);
2138
2139 if (!res) throw std::runtime_error("Call to close() failed.");
2140
2141 Py_DECREF(res);
2142}
2143
2144inline void xkcd() {
Austin Schuhab802d52020-07-03 18:11:11 -07002145 detail::_interpreter::get();
2146
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002147 PyObject* res;
2148 PyObject *kwargs = PyDict_New();
2149
2150 res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd,
2151 detail::_interpreter::get().s_python_empty_tuple, kwargs);
2152
2153 Py_DECREF(kwargs);
2154
2155 if (!res)
2156 throw std::runtime_error("Call to show() failed.");
2157
2158 Py_DECREF(res);
2159}
2160
2161inline void draw()
2162{
Austin Schuhab802d52020-07-03 18:11:11 -07002163 detail::_interpreter::get();
2164
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002165 PyObject* res = PyObject_CallObject(
2166 detail::_interpreter::get().s_python_function_draw,
2167 detail::_interpreter::get().s_python_empty_tuple);
2168
2169 if (!res) throw std::runtime_error("Call to draw() failed.");
2170
2171 Py_DECREF(res);
2172}
2173
2174template<typename Numeric>
2175inline void pause(Numeric interval)
2176{
Austin Schuhab802d52020-07-03 18:11:11 -07002177 detail::_interpreter::get();
2178
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002179 PyObject* args = PyTuple_New(1);
2180 PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval));
2181
2182 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args);
2183 if(!res) throw std::runtime_error("Call to pause() failed.");
2184
2185 Py_DECREF(args);
2186 Py_DECREF(res);
2187}
2188
2189inline void save(const std::string& filename)
2190{
Austin Schuhab802d52020-07-03 18:11:11 -07002191 detail::_interpreter::get();
2192
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002193 PyObject* pyfilename = PyString_FromString(filename.c_str());
2194
2195 PyObject* args = PyTuple_New(1);
2196 PyTuple_SetItem(args, 0, pyfilename);
2197
2198 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args);
2199 if (!res) throw std::runtime_error("Call to save() failed.");
2200
2201 Py_DECREF(args);
2202 Py_DECREF(res);
2203}
2204
2205inline void clf() {
Austin Schuhab802d52020-07-03 18:11:11 -07002206 detail::_interpreter::get();
2207
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002208 PyObject *res = PyObject_CallObject(
2209 detail::_interpreter::get().s_python_function_clf,
2210 detail::_interpreter::get().s_python_empty_tuple);
2211
2212 if (!res) throw std::runtime_error("Call to clf() failed.");
2213
2214 Py_DECREF(res);
2215}
2216
Austin Schuhab802d52020-07-03 18:11:11 -07002217inline void cla() {
2218 detail::_interpreter::get();
2219
2220 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_cla,
2221 detail::_interpreter::get().s_python_empty_tuple);
2222
2223 if (!res)
2224 throw std::runtime_error("Call to cla() failed.");
2225
2226 Py_DECREF(res);
2227}
2228
2229inline void ion() {
2230 detail::_interpreter::get();
2231
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002232 PyObject *res = PyObject_CallObject(
2233 detail::_interpreter::get().s_python_function_ion,
2234 detail::_interpreter::get().s_python_empty_tuple);
2235
2236 if (!res) throw std::runtime_error("Call to ion() failed.");
2237
2238 Py_DECREF(res);
2239}
2240
Austin Schuhab802d52020-07-03 18:11:11 -07002241inline std::vector<std::array<double, 2>> ginput(const int numClicks = 1, const std::map<std::string, std::string>& keywords = {})
2242{
2243 detail::_interpreter::get();
2244
2245 PyObject *args = PyTuple_New(1);
2246 PyTuple_SetItem(args, 0, PyLong_FromLong(numClicks));
2247
2248 // construct keyword args
2249 PyObject* kwargs = PyDict_New();
2250 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
2251 {
2252 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
2253 }
2254
2255 PyObject* res = PyObject_Call(
2256 detail::_interpreter::get().s_python_function_ginput, args, kwargs);
2257
2258 Py_DECREF(kwargs);
2259 Py_DECREF(args);
2260 if (!res) throw std::runtime_error("Call to ginput() failed.");
2261
2262 const size_t len = PyList_Size(res);
2263 std::vector<std::array<double, 2>> out;
2264 out.reserve(len);
2265 for (size_t i = 0; i < len; i++) {
2266 PyObject *current = PyList_GetItem(res, i);
2267 std::array<double, 2> position;
2268 position[0] = PyFloat_AsDouble(PyTuple_GetItem(current, 0));
2269 position[1] = PyFloat_AsDouble(PyTuple_GetItem(current, 1));
2270 out.push_back(position);
2271 }
2272 Py_DECREF(res);
2273
2274 return out;
2275}
2276
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002277// Actually, is there any reason not to call this automatically for every plot?
2278inline void tight_layout() {
Austin Schuhab802d52020-07-03 18:11:11 -07002279 detail::_interpreter::get();
2280
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002281 PyObject *res = PyObject_CallObject(
2282 detail::_interpreter::get().s_python_function_tight_layout,
2283 detail::_interpreter::get().s_python_empty_tuple);
2284
2285 if (!res) throw std::runtime_error("Call to tight_layout() failed.");
2286
2287 Py_DECREF(res);
2288}
2289
Austin Schuhab802d52020-07-03 18:11:11 -07002290// Support for variadic plot() and initializer lists:
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002291
2292namespace detail {
2293
2294template<typename T>
2295using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
2296
2297template<bool obj, typename T>
2298struct is_callable_impl;
2299
2300template<typename T>
2301struct is_callable_impl<false, T>
2302{
2303 typedef is_function<T> type;
2304}; // a non-object is callable iff it is a function
2305
2306template<typename T>
2307struct is_callable_impl<true, T>
2308{
2309 struct Fallback { void operator()(); };
2310 struct Derived : T, Fallback { };
2311
2312 template<typename U, U> struct Check;
2313
2314 template<typename U>
2315 static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match
2316
2317 template<typename U>
2318 static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
2319
2320public:
2321 typedef decltype(test<Derived>(nullptr)) type;
2322 typedef decltype(&Fallback::operator()) dtype;
2323 static constexpr bool value = type::value;
2324}; // an object is callable iff it defines operator()
2325
2326template<typename T>
2327struct is_callable
2328{
2329 // dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
2330 typedef typename is_callable_impl<std::is_class<T>::value, T>::type type;
2331};
2332
2333template<typename IsYDataCallable>
2334struct plot_impl { };
2335
2336template<>
2337struct plot_impl<std::false_type>
2338{
2339 template<typename IterableX, typename IterableY>
2340 bool operator()(const IterableX& x, const IterableY& y, const std::string& format)
2341 {
2342 // 2-phase lookup for distance, begin, end
2343 using std::distance;
2344 using std::begin;
2345 using std::end;
2346
2347 auto xs = distance(begin(x), end(x));
2348 auto ys = distance(begin(y), end(y));
2349 assert(xs == ys && "x and y data must have the same number of elements!");
2350
2351 PyObject* xlist = PyList_New(xs);
2352 PyObject* ylist = PyList_New(ys);
2353 PyObject* pystring = PyString_FromString(format.c_str());
2354
2355 auto itx = begin(x), ity = begin(y);
2356 for(size_t i = 0; i < xs; ++i) {
2357 PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++));
2358 PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++));
2359 }
2360
2361 PyObject* plot_args = PyTuple_New(3);
2362 PyTuple_SetItem(plot_args, 0, xlist);
2363 PyTuple_SetItem(plot_args, 1, ylist);
2364 PyTuple_SetItem(plot_args, 2, pystring);
2365
2366 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
2367
2368 Py_DECREF(plot_args);
2369 if(res) Py_DECREF(res);
2370
2371 return res;
2372 }
2373};
2374
2375template<>
2376struct plot_impl<std::true_type>
2377{
2378 template<typename Iterable, typename Callable>
2379 bool operator()(const Iterable& ticks, const Callable& f, const std::string& format)
2380 {
2381 if(begin(ticks) == end(ticks)) return true;
2382
2383 // We could use additional meta-programming to deduce the correct element type of y,
2384 // but all values have to be convertible to double anyways
2385 std::vector<double> y;
2386 for(auto x : ticks) y.push_back(f(x));
2387 return plot_impl<std::false_type>()(ticks,y,format);
2388 }
2389};
2390
2391} // end namespace detail
2392
2393// recursion stop for the above
2394template<typename... Args>
2395bool plot() { return true; }
2396
2397template<typename A, typename B, typename... Args>
2398bool plot(const A& a, const B& b, const std::string& format, Args... args)
2399{
2400 return detail::plot_impl<typename detail::is_callable<B>::type>()(a,b,format) && plot(args...);
2401}
2402
2403/*
2404 * This group of plot() functions is needed to support initializer lists, i.e. calling
2405 * plot( {1,2,3,4} )
2406 */
2407inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::string& format = "") {
2408 return plot<double,double>(x,y,format);
2409}
2410
2411inline bool plot(const std::vector<double>& y, const std::string& format = "") {
2412 return plot<double>(y,format);
2413}
2414
2415inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::map<std::string, std::string>& keywords) {
2416 return plot<double>(x,y,keywords);
2417}
2418
Austin Schuhab802d52020-07-03 18:11:11 -07002419/*
2420 * This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting
2421 */
2422class Plot
2423{
2424public:
2425 // default initialization with plot label, some data and format
2426 template<typename Numeric>
2427 Plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") {
2428 detail::_interpreter::get();
2429
2430 assert(x.size() == y.size());
2431
2432 PyObject* kwargs = PyDict_New();
2433 if(name != "")
2434 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
2435
2436 PyObject* xarray = detail::get_array(x);
2437 PyObject* yarray = detail::get_array(y);
2438
2439 PyObject* pystring = PyString_FromString(format.c_str());
2440
2441 PyObject* plot_args = PyTuple_New(3);
2442 PyTuple_SetItem(plot_args, 0, xarray);
2443 PyTuple_SetItem(plot_args, 1, yarray);
2444 PyTuple_SetItem(plot_args, 2, pystring);
2445
2446 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
2447
2448 Py_DECREF(kwargs);
2449 Py_DECREF(plot_args);
2450
2451 if(res)
2452 {
2453 line= PyList_GetItem(res, 0);
2454
2455 if(line)
2456 set_data_fct = PyObject_GetAttrString(line,"set_data");
2457 else
2458 Py_DECREF(line);
2459 Py_DECREF(res);
2460 }
2461 }
2462
2463 // shorter initialization with name or format only
2464 // basically calls line, = plot([], [])
2465 Plot(const std::string& name = "", const std::string& format = "")
2466 : Plot(name, std::vector<double>(), std::vector<double>(), format) {}
2467
2468 template<typename Numeric>
2469 bool update(const std::vector<Numeric>& x, const std::vector<Numeric>& y) {
2470 assert(x.size() == y.size());
2471 if(set_data_fct)
2472 {
2473 PyObject* xarray = detail::get_array(x);
2474 PyObject* yarray = detail::get_array(y);
2475
2476 PyObject* plot_args = PyTuple_New(2);
2477 PyTuple_SetItem(plot_args, 0, xarray);
2478 PyTuple_SetItem(plot_args, 1, yarray);
2479
2480 PyObject* res = PyObject_CallObject(set_data_fct, plot_args);
2481 if (res) Py_DECREF(res);
2482 return res;
2483 }
2484 return false;
2485 }
2486
2487 // clears the plot but keep it available
2488 bool clear() {
2489 return update(std::vector<double>(), std::vector<double>());
2490 }
2491
2492 // definitely remove this line
2493 void remove() {
2494 if(line)
2495 {
2496 auto remove_fct = PyObject_GetAttrString(line,"remove");
2497 PyObject* args = PyTuple_New(0);
2498 PyObject* res = PyObject_CallObject(remove_fct, args);
2499 if (res) Py_DECREF(res);
2500 }
2501 decref();
2502 }
2503
2504 ~Plot() {
2505 decref();
2506 }
2507private:
2508
2509 void decref() {
2510 if(line)
2511 Py_DECREF(line);
2512 if(set_data_fct)
2513 Py_DECREF(set_data_fct);
2514 }
2515
2516
2517 PyObject* line = nullptr;
2518 PyObject* set_data_fct = nullptr;
2519};
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08002520
2521} // end namespace matplotlibcpp