blob: eb7b7ccb844123b7c57bab8c3ea078f4608f915a [file] [log] [blame]
Austin Schuh6c8ec4c2018-01-23 11:18:57 -08001#pragma once
2
3#include <vector>
4#include <map>
5#include <numeric>
6#include <algorithm>
7#include <stdexcept>
8#include <iostream>
9#include <stdint.h> // <cstdint> requires c++11 support
10
11#if __cplusplus > 199711L || _MSC_VER > 1800
12# include <functional>
13#endif
14
15#include <Python.h>
16
17#ifndef WITHOUT_NUMPY
18# define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
19# include <numpy/arrayobject.h>
20#endif // WITHOUT_NUMPY
21
22#if PY_MAJOR_VERSION >= 3
23# define PyString_FromString PyUnicode_FromString
24#endif
25
26
27namespace matplotlibcpp {
28namespace detail {
29
30static std::string s_backend;
31
32struct _interpreter {
33 PyObject *s_python_function_show;
34 PyObject *s_python_function_close;
35 PyObject *s_python_function_draw;
36 PyObject *s_python_function_pause;
37 PyObject *s_python_function_save;
38 PyObject *s_python_function_figure;
39 PyObject *s_python_function_plot;
40 PyObject *s_python_function_semilogx;
41 PyObject *s_python_function_semilogy;
42 PyObject *s_python_function_loglog;
43 PyObject *s_python_function_fill_between;
44 PyObject *s_python_function_hist;
45 PyObject *s_python_function_subplot;
46 PyObject *s_python_function_legend;
47 PyObject *s_python_function_xlim;
48 PyObject *s_python_function_ion;
49 PyObject *s_python_function_ylim;
50 PyObject *s_python_function_title;
51 PyObject *s_python_function_axis;
52 PyObject *s_python_function_xlabel;
53 PyObject *s_python_function_ylabel;
54 PyObject *s_python_function_grid;
55 PyObject *s_python_function_clf;
56 PyObject *s_python_function_errorbar;
57 PyObject *s_python_function_annotate;
58 PyObject *s_python_function_tight_layout;
Austin Schuhad596222018-01-31 23:34:03 -080059 PyObject *s_python_colormap;
Austin Schuh6c8ec4c2018-01-23 11:18:57 -080060 PyObject *s_python_empty_tuple;
61 PyObject *s_python_function_stem;
62 PyObject *s_python_function_xkcd;
63
64 /* For now, _interpreter is implemented as a singleton since its currently not possible to have
65 multiple independent embedded python interpreters without patching the python source code
66 or starting a separate process for each.
67 http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program
68 */
69
70 static _interpreter& get() {
71 static _interpreter ctx;
72 return ctx;
73 }
74
75private:
76
77#ifndef WITHOUT_NUMPY
78# if PY_MAJOR_VERSION >= 3
79
80 void *import_numpy() {
81 import_array(); // initialize C-API
82 return NULL;
83 }
84
85# else
86
87 void import_numpy() {
88 import_array(); // initialize C-API
89 }
90
91# endif
92#endif
93
94 _interpreter() {
95
96 // optional but recommended
97#if PY_MAJOR_VERSION >= 3
98 wchar_t name[] = L"plotting";
99#else
100 char name[] = "plotting";
101#endif
102 Py_SetProgramName(name);
103 Py_Initialize();
104
105#ifndef WITHOUT_NUMPY
106 import_numpy(); // initialize numpy C-API
107#endif
108
109 PyObject* matplotlibname = PyString_FromString("matplotlib");
110 PyObject* pyplotname = PyString_FromString("matplotlib.pyplot");
Austin Schuhad596222018-01-31 23:34:03 -0800111 PyObject* mpl_toolkits = PyString_FromString("mpl_toolkits");
112 PyObject* axis3d = PyString_FromString("mpl_toolkits.mplot3d");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800113 PyObject* pylabname = PyString_FromString("pylab");
Austin Schuhad596222018-01-31 23:34:03 -0800114 PyObject* cmname = PyString_FromString("matplotlib.cm");
115 if (!pyplotname || !pylabname || !matplotlibname || !mpl_toolkits ||
116 !axis3d || !cmname) {
117 throw std::runtime_error("couldnt create string");
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800118 }
119
120 PyObject* matplotlib = PyImport_Import(matplotlibname);
121 Py_DECREF(matplotlibname);
122 if (!matplotlib) { throw std::runtime_error("Error loading module matplotlib!"); }
123
124 // matplotlib.use() must be called *before* pylab, matplotlib.pyplot,
125 // or matplotlib.backends is imported for the first time
126 if (!s_backend.empty()) {
127 PyObject_CallMethod(matplotlib, const_cast<char*>("use"), const_cast<char*>("s"), s_backend.c_str());
128 }
129
130 PyObject* pymod = PyImport_Import(pyplotname);
131 Py_DECREF(pyplotname);
132 if (!pymod) { throw std::runtime_error("Error loading module matplotlib.pyplot!"); }
133
Austin Schuhad596222018-01-31 23:34:03 -0800134 s_python_colormap = PyImport_Import(cmname);
135 Py_DECREF(cmname);
136 if (!s_python_colormap) { throw std::runtime_error("Error loading module matplotlib.cm!"); }
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800137
138 PyObject* pylabmod = PyImport_Import(pylabname);
139 Py_DECREF(pylabname);
140 if (!pylabmod) { throw std::runtime_error("Error loading module pylab!"); }
141
Austin Schuhad596222018-01-31 23:34:03 -0800142 PyObject* mpl_toolkitsmod = PyImport_Import(mpl_toolkits);
143 Py_DECREF(mpl_toolkitsmod);
144 if (!mpl_toolkitsmod) { throw std::runtime_error("Error loading module mpl_toolkits!"); }
145
146 PyObject* axis3dmod = PyImport_Import(axis3d);
147 Py_DECREF(axis3dmod);
148 if (!axis3dmod) { throw std::runtime_error("Error loading module mpl_toolkits.mplot3d!"); }
149
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800150 s_python_function_show = PyObject_GetAttrString(pymod, "show");
151 s_python_function_close = PyObject_GetAttrString(pymod, "close");
152 s_python_function_draw = PyObject_GetAttrString(pymod, "draw");
153 s_python_function_pause = PyObject_GetAttrString(pymod, "pause");
154 s_python_function_figure = PyObject_GetAttrString(pymod, "figure");
155 s_python_function_plot = PyObject_GetAttrString(pymod, "plot");
156 s_python_function_semilogx = PyObject_GetAttrString(pymod, "semilogx");
157 s_python_function_semilogy = PyObject_GetAttrString(pymod, "semilogy");
158 s_python_function_loglog = PyObject_GetAttrString(pymod, "loglog");
159 s_python_function_fill_between = PyObject_GetAttrString(pymod, "fill_between");
160 s_python_function_hist = PyObject_GetAttrString(pymod,"hist");
161 s_python_function_subplot = PyObject_GetAttrString(pymod, "subplot");
162 s_python_function_legend = PyObject_GetAttrString(pymod, "legend");
163 s_python_function_ylim = PyObject_GetAttrString(pymod, "ylim");
164 s_python_function_title = PyObject_GetAttrString(pymod, "title");
165 s_python_function_axis = PyObject_GetAttrString(pymod, "axis");
166 s_python_function_xlabel = PyObject_GetAttrString(pymod, "xlabel");
167 s_python_function_ylabel = PyObject_GetAttrString(pymod, "ylabel");
168 s_python_function_grid = PyObject_GetAttrString(pymod, "grid");
169 s_python_function_xlim = PyObject_GetAttrString(pymod, "xlim");
170 s_python_function_ion = PyObject_GetAttrString(pymod, "ion");
171 s_python_function_save = PyObject_GetAttrString(pylabmod, "savefig");
172 s_python_function_annotate = PyObject_GetAttrString(pymod,"annotate");
173 s_python_function_clf = PyObject_GetAttrString(pymod, "clf");
174 s_python_function_errorbar = PyObject_GetAttrString(pymod, "errorbar");
175 s_python_function_tight_layout = PyObject_GetAttrString(pymod, "tight_layout");
176 s_python_function_stem = PyObject_GetAttrString(pymod, "stem");
177 s_python_function_xkcd = PyObject_GetAttrString(pymod, "xkcd");
178
179 if( !s_python_function_show
180 || !s_python_function_close
181 || !s_python_function_draw
182 || !s_python_function_pause
183 || !s_python_function_figure
184 || !s_python_function_plot
185 || !s_python_function_semilogx
186 || !s_python_function_semilogy
187 || !s_python_function_loglog
188 || !s_python_function_fill_between
189 || !s_python_function_subplot
190 || !s_python_function_legend
191 || !s_python_function_ylim
192 || !s_python_function_title
193 || !s_python_function_axis
194 || !s_python_function_xlabel
195 || !s_python_function_ylabel
196 || !s_python_function_grid
197 || !s_python_function_xlim
198 || !s_python_function_ion
199 || !s_python_function_save
200 || !s_python_function_clf
201 || !s_python_function_annotate
202 || !s_python_function_errorbar
203 || !s_python_function_errorbar
204 || !s_python_function_tight_layout
205 || !s_python_function_stem
206 || !s_python_function_xkcd
207 ) { throw std::runtime_error("Couldn't find required function!"); }
208
209 if ( !PyFunction_Check(s_python_function_show)
210 || !PyFunction_Check(s_python_function_close)
211 || !PyFunction_Check(s_python_function_draw)
212 || !PyFunction_Check(s_python_function_pause)
213 || !PyFunction_Check(s_python_function_figure)
214 || !PyFunction_Check(s_python_function_plot)
215 || !PyFunction_Check(s_python_function_semilogx)
216 || !PyFunction_Check(s_python_function_semilogy)
217 || !PyFunction_Check(s_python_function_loglog)
218 || !PyFunction_Check(s_python_function_fill_between)
219 || !PyFunction_Check(s_python_function_subplot)
220 || !PyFunction_Check(s_python_function_legend)
221 || !PyFunction_Check(s_python_function_annotate)
222 || !PyFunction_Check(s_python_function_ylim)
223 || !PyFunction_Check(s_python_function_title)
224 || !PyFunction_Check(s_python_function_axis)
225 || !PyFunction_Check(s_python_function_xlabel)
226 || !PyFunction_Check(s_python_function_ylabel)
227 || !PyFunction_Check(s_python_function_grid)
228 || !PyFunction_Check(s_python_function_xlim)
229 || !PyFunction_Check(s_python_function_ion)
230 || !PyFunction_Check(s_python_function_save)
231 || !PyFunction_Check(s_python_function_clf)
232 || !PyFunction_Check(s_python_function_tight_layout)
233 || !PyFunction_Check(s_python_function_errorbar)
234 || !PyFunction_Check(s_python_function_stem)
235 || !PyFunction_Check(s_python_function_xkcd)
236 ) { throw std::runtime_error("Python object is unexpectedly not a PyFunction."); }
237
238 s_python_empty_tuple = PyTuple_New(0);
239 }
240
241 ~_interpreter() {
242 Py_Finalize();
243 }
244};
245
246} // end namespace detail
247
248// must be called before the first regular call to matplotlib to have any effect
249inline void backend(const std::string& name)
250{
251 detail::s_backend = name;
252}
253
254inline bool annotate(std::string annotation, double x, double y)
255{
256 PyObject * xy = PyTuple_New(2);
257 PyObject * str = PyString_FromString(annotation.c_str());
258
259 PyTuple_SetItem(xy,0,PyFloat_FromDouble(x));
260 PyTuple_SetItem(xy,1,PyFloat_FromDouble(y));
261
262 PyObject* kwargs = PyDict_New();
263 PyDict_SetItemString(kwargs, "xy", xy);
264
265 PyObject* args = PyTuple_New(1);
266 PyTuple_SetItem(args, 0, str);
267
268 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_annotate, args, kwargs);
269
270 Py_DECREF(args);
271 Py_DECREF(kwargs);
272
273 if(res) Py_DECREF(res);
274
275 return res;
276}
277
278#ifndef WITHOUT_NUMPY
279// Type selector for numpy array conversion
280template <typename T> struct select_npy_type { const static NPY_TYPES type = NPY_NOTYPE; }; //Default
281template <> struct select_npy_type<double> { const static NPY_TYPES type = NPY_DOUBLE; };
282template <> struct select_npy_type<float> { const static NPY_TYPES type = NPY_FLOAT; };
283template <> struct select_npy_type<bool> { const static NPY_TYPES type = NPY_BOOL; };
284template <> struct select_npy_type<int8_t> { const static NPY_TYPES type = NPY_INT8; };
285template <> struct select_npy_type<int16_t> { const static NPY_TYPES type = NPY_SHORT; };
286template <> struct select_npy_type<int32_t> { const static NPY_TYPES type = NPY_INT; };
287template <> struct select_npy_type<int64_t> { const static NPY_TYPES type = NPY_INT64; };
288template <> struct select_npy_type<uint8_t> { const static NPY_TYPES type = NPY_UINT8; };
289template <> struct select_npy_type<uint16_t> { const static NPY_TYPES type = NPY_USHORT; };
290template <> struct select_npy_type<uint32_t> { const static NPY_TYPES type = NPY_ULONG; };
291template <> struct select_npy_type<uint64_t> { const static NPY_TYPES type = NPY_UINT64; };
292
293template<typename Numeric>
294PyObject* get_array(const std::vector<Numeric>& v)
295{
296 detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work
297 NPY_TYPES type = select_npy_type<Numeric>::type;
298 if (type == NPY_NOTYPE)
299 {
300 std::vector<double> vd(v.size());
301 npy_intp vsize = v.size();
302 std::copy(v.begin(),v.end(),vd.begin());
303 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, NPY_DOUBLE, (void*)(vd.data()));
304 return varray;
305 }
306
307 npy_intp vsize = v.size();
308 PyObject* varray = PyArray_SimpleNewFromData(1, &vsize, type, (void*)(v.data()));
309 return varray;
310}
311
Austin Schuhad596222018-01-31 23:34:03 -0800312template<typename Numeric>
313PyObject* get_2darray(const std::vector<::std::vector<Numeric>>& v)
314{
315 detail::_interpreter::get(); //interpreter needs to be initialized for the numpy commands to work
316 if (v.size() < 1) throw std::runtime_error("get_2d_array v too small");
317
318 npy_intp vsize[2] = {static_cast<npy_intp>(v.size()),
319 static_cast<npy_intp>(v[0].size())};
320
321 PyArrayObject *varray =
322 (PyArrayObject *)PyArray_SimpleNew(2, vsize, NPY_DOUBLE);
323
324 double *vd_begin = static_cast<double *>(PyArray_DATA(varray));
325
326 for (const ::std::vector<Numeric> &v_row : v) {
327 if (v_row.size() != static_cast<size_t>(vsize[1]))
328 throw std::runtime_error("Missmatched array size");
329 std::copy(v_row.begin(), v_row.end(), vd_begin);
330 vd_begin += vsize[1];
331 }
332
333 return reinterpret_cast<PyObject *>(varray);
334}
335
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800336#else // fallback if we don't have numpy: copy every element of the given vector
337
338template<typename Numeric>
339PyObject* get_array(const std::vector<Numeric>& v)
340{
341 PyObject* list = PyList_New(v.size());
342 for(size_t i = 0; i < v.size(); ++i) {
343 PyList_SetItem(list, i, PyFloat_FromDouble(v.at(i)));
344 }
345 return list;
346}
347
348#endif // WITHOUT_NUMPY
349
350template<typename Numeric>
351bool plot(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
352{
353 assert(x.size() == y.size());
354
355 // using numpy arrays
356 PyObject* xarray = get_array(x);
357 PyObject* yarray = get_array(y);
358
359 // construct positional args
360 PyObject* args = PyTuple_New(2);
361 PyTuple_SetItem(args, 0, xarray);
362 PyTuple_SetItem(args, 1, yarray);
363
364 // construct keyword args
365 PyObject* kwargs = PyDict_New();
366 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
367 {
368 PyDict_SetItemString(kwargs, it->first.c_str(), PyString_FromString(it->second.c_str()));
369 }
370
371 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, args, kwargs);
372
373 Py_DECREF(args);
374 Py_DECREF(kwargs);
375 if(res) Py_DECREF(res);
376
377 return res;
378}
379
Austin Schuhad596222018-01-31 23:34:03 -0800380template <typename Numeric>
381void plot_surface(const std::vector<::std::vector<Numeric>> &x,
382 const std::vector<::std::vector<Numeric>> &y,
383 const std::vector<::std::vector<Numeric>> &z,
384 const std::map<std::string, std::string> &keywords =
385 std::map<std::string, std::string>()) {
386 assert(x.size() == y.size());
387 assert(y.size() == z.size());
388
389 // using numpy arrays
390 PyObject *xarray = get_2darray(x);
391 PyObject *yarray = get_2darray(y);
392 PyObject *zarray = get_2darray(z);
393
394 // construct positional args
395 PyObject *args = PyTuple_New(3);
396 PyTuple_SetItem(args, 0, xarray);
397 PyTuple_SetItem(args, 1, yarray);
398 PyTuple_SetItem(args, 2, zarray);
399
400 // Build up the kw args.
401 PyObject *kwargs = PyDict_New();
402 PyDict_SetItemString(kwargs, "rstride", PyInt_FromLong(1));
403 PyDict_SetItemString(kwargs, "cstride", PyInt_FromLong(1));
404
405 PyObject *python_colormap_coolwarm = PyObject_GetAttrString(
406 detail::_interpreter::get().s_python_colormap, "coolwarm");
407
408 PyDict_SetItemString(kwargs, "cmap", python_colormap_coolwarm);
409
410 for (std::map<std::string, std::string>::const_iterator it = keywords.begin();
411 it != keywords.end(); ++it) {
412 PyDict_SetItemString(kwargs, it->first.c_str(),
413 PyString_FromString(it->second.c_str()));
414 }
415
416
417 PyObject *fig =
418 PyObject_CallObject(detail::_interpreter::get().s_python_function_figure,
419 detail::_interpreter::get().s_python_empty_tuple);
420 if (!fig) throw std::runtime_error("Call to figure() failed.");
421
422 PyObject *gca_kwargs = PyDict_New();
423 PyDict_SetItemString(gca_kwargs, "projection", PyString_FromString("3d"));
424
425 PyObject *gca = PyObject_GetAttrString(fig, "gca");
426 if (!gca) throw std::runtime_error("No gca");
427 Py_INCREF(gca);
428 PyObject *axis = PyObject_Call(
429 gca, detail::_interpreter::get().s_python_empty_tuple, gca_kwargs);
430
431 if (!axis) throw std::runtime_error("No axis");
432 Py_INCREF(axis);
433
434 Py_DECREF(gca);
435 Py_DECREF(gca_kwargs);
436
437 PyObject *plot_surface = PyObject_GetAttrString(axis, "plot_surface");
438 if (!plot_surface) throw std::runtime_error("No surface");
439 Py_INCREF(plot_surface);
440 PyObject *res = PyObject_Call(plot_surface, args, kwargs);
441 if (!res) throw std::runtime_error("failed surface");
442 Py_DECREF(plot_surface);
443
444 Py_DECREF(axis);
445 Py_DECREF(args);
446 Py_DECREF(kwargs);
447 if (res) Py_DECREF(res);
448}
449
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800450template<typename Numeric>
451bool stem(const std::vector<Numeric> &x, const std::vector<Numeric> &y, const std::map<std::string, std::string>& keywords)
452{
453 assert(x.size() == y.size());
454
455 // using numpy arrays
456 PyObject* xarray = get_array(x);
457 PyObject* yarray = get_array(y);
458
459 // construct positional args
460 PyObject* args = PyTuple_New(2);
461 PyTuple_SetItem(args, 0, xarray);
462 PyTuple_SetItem(args, 1, yarray);
463
464 // construct keyword args
465 PyObject* kwargs = PyDict_New();
466 for (std::map<std::string, std::string>::const_iterator it =
467 keywords.begin(); it != keywords.end(); ++it) {
468 PyDict_SetItemString(kwargs, it->first.c_str(),
469 PyString_FromString(it->second.c_str()));
470 }
471
472 PyObject* res = PyObject_Call(
473 detail::_interpreter::get().s_python_function_stem, args, kwargs);
474
475 Py_DECREF(args);
476 Py_DECREF(kwargs);
477 if (res)
478 Py_DECREF(res);
479
480 return res;
481}
482
483template< typename Numeric >
484bool 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)
485{
486 assert(x.size() == y1.size());
487 assert(x.size() == y2.size());
488
489 // using numpy arrays
490 PyObject* xarray = get_array(x);
491 PyObject* y1array = get_array(y1);
492 PyObject* y2array = get_array(y2);
493
494 // construct positional args
495 PyObject* args = PyTuple_New(3);
496 PyTuple_SetItem(args, 0, xarray);
497 PyTuple_SetItem(args, 1, y1array);
498 PyTuple_SetItem(args, 2, y2array);
499
500 // construct keyword args
501 PyObject* kwargs = PyDict_New();
502 for(std::map<std::string, std::string>::const_iterator it = keywords.begin(); it != keywords.end(); ++it)
503 {
504 PyDict_SetItemString(kwargs, it->first.c_str(), PyUnicode_FromString(it->second.c_str()));
505 }
506
507 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_fill_between, args, kwargs);
508
509 Py_DECREF(args);
510 Py_DECREF(kwargs);
511 if(res) Py_DECREF(res);
512
513 return res;
514}
515
516template< typename Numeric>
517bool hist(const std::vector<Numeric>& y, long bins=10,std::string color="b", double alpha=1.0)
518{
519
520 PyObject* yarray = get_array(y);
521
522 PyObject* kwargs = PyDict_New();
523 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
524 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
525 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
526
527
528 PyObject* plot_args = PyTuple_New(1);
529
530 PyTuple_SetItem(plot_args, 0, yarray);
531
532
533 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
534
535
536 Py_DECREF(plot_args);
537 Py_DECREF(kwargs);
538 if(res) Py_DECREF(res);
539
540 return res;
541}
542
543template< typename Numeric>
544bool named_hist(std::string label,const std::vector<Numeric>& y, long bins=10, std::string color="b", double alpha=1.0)
545{
546 PyObject* yarray = get_array(y);
547
548 PyObject* kwargs = PyDict_New();
549 PyDict_SetItemString(kwargs, "label", PyString_FromString(label.c_str()));
550 PyDict_SetItemString(kwargs, "bins", PyLong_FromLong(bins));
551 PyDict_SetItemString(kwargs, "color", PyString_FromString(color.c_str()));
552 PyDict_SetItemString(kwargs, "alpha", PyFloat_FromDouble(alpha));
553
554
555 PyObject* plot_args = PyTuple_New(1);
556 PyTuple_SetItem(plot_args, 0, yarray);
557
558 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_hist, plot_args, kwargs);
559
560 Py_DECREF(plot_args);
561 Py_DECREF(kwargs);
562 if(res) Py_DECREF(res);
563
564 return res;
565}
566
567template<typename NumericX, typename NumericY>
568bool plot(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
569{
570 assert(x.size() == y.size());
571
572 PyObject* xarray = get_array(x);
573 PyObject* yarray = get_array(y);
574
575 PyObject* pystring = PyString_FromString(s.c_str());
576
577 PyObject* plot_args = PyTuple_New(3);
578 PyTuple_SetItem(plot_args, 0, xarray);
579 PyTuple_SetItem(plot_args, 1, yarray);
580 PyTuple_SetItem(plot_args, 2, pystring);
581
582 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
583
584 Py_DECREF(plot_args);
585 if(res) Py_DECREF(res);
586
587 return res;
588}
589
590template<typename NumericX, typename NumericY>
591bool stem(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
592{
593 assert(x.size() == y.size());
594
595 PyObject* xarray = get_array(x);
596 PyObject* yarray = get_array(y);
597
598 PyObject* pystring = PyString_FromString(s.c_str());
599
600 PyObject* plot_args = PyTuple_New(3);
601 PyTuple_SetItem(plot_args, 0, xarray);
602 PyTuple_SetItem(plot_args, 1, yarray);
603 PyTuple_SetItem(plot_args, 2, pystring);
604
605 PyObject* res = PyObject_CallObject(
606 detail::_interpreter::get().s_python_function_stem, plot_args);
607
608 Py_DECREF(plot_args);
609 if (res)
610 Py_DECREF(res);
611
612 return res;
613}
614
615template<typename NumericX, typename NumericY>
616bool semilogx(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
617{
618 assert(x.size() == y.size());
619
620 PyObject* xarray = get_array(x);
621 PyObject* yarray = get_array(y);
622
623 PyObject* pystring = PyString_FromString(s.c_str());
624
625 PyObject* plot_args = PyTuple_New(3);
626 PyTuple_SetItem(plot_args, 0, xarray);
627 PyTuple_SetItem(plot_args, 1, yarray);
628 PyTuple_SetItem(plot_args, 2, pystring);
629
630 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogx, plot_args);
631
632 Py_DECREF(plot_args);
633 if(res) Py_DECREF(res);
634
635 return res;
636}
637
638template<typename NumericX, typename NumericY>
639bool semilogy(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
640{
641 assert(x.size() == y.size());
642
643 PyObject* xarray = get_array(x);
644 PyObject* yarray = get_array(y);
645
646 PyObject* pystring = PyString_FromString(s.c_str());
647
648 PyObject* plot_args = PyTuple_New(3);
649 PyTuple_SetItem(plot_args, 0, xarray);
650 PyTuple_SetItem(plot_args, 1, yarray);
651 PyTuple_SetItem(plot_args, 2, pystring);
652
653 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_semilogy, plot_args);
654
655 Py_DECREF(plot_args);
656 if(res) Py_DECREF(res);
657
658 return res;
659}
660
661template<typename NumericX, typename NumericY>
662bool loglog(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
663{
664 assert(x.size() == y.size());
665
666 PyObject* xarray = get_array(x);
667 PyObject* yarray = get_array(y);
668
669 PyObject* pystring = PyString_FromString(s.c_str());
670
671 PyObject* plot_args = PyTuple_New(3);
672 PyTuple_SetItem(plot_args, 0, xarray);
673 PyTuple_SetItem(plot_args, 1, yarray);
674 PyTuple_SetItem(plot_args, 2, pystring);
675
676 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_loglog, plot_args);
677
678 Py_DECREF(plot_args);
679 if(res) Py_DECREF(res);
680
681 return res;
682}
683
684template<typename NumericX, typename NumericY>
Austin Schuhf3bac672018-01-23 11:18:57 -0800685bool errorbar(const std::vector<NumericX> &x, const std::vector<NumericY> &y, const std::vector<NumericX> &yerr, const std::string & /*s*/ = "")
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800686{
687 assert(x.size() == y.size());
688
689 PyObject* xarray = get_array(x);
690 PyObject* yarray = get_array(y);
691 PyObject* yerrarray = get_array(yerr);
692
693 PyObject *kwargs = PyDict_New();
694
695 PyDict_SetItemString(kwargs, "yerr", yerrarray);
696
Austin Schuhf3bac672018-01-23 11:18:57 -0800697 //PyObject *pystring = PyString_FromString(s.c_str());
Austin Schuh6c8ec4c2018-01-23 11:18:57 -0800698
699 PyObject *plot_args = PyTuple_New(2);
700 PyTuple_SetItem(plot_args, 0, xarray);
701 PyTuple_SetItem(plot_args, 1, yarray);
702
703 PyObject *res = PyObject_Call(detail::_interpreter::get().s_python_function_errorbar, plot_args, kwargs);
704
705 Py_DECREF(kwargs);
706 Py_DECREF(plot_args);
707
708 if (res)
709 Py_DECREF(res);
710 else
711 throw std::runtime_error("Call to errorbar() failed.");
712
713 return res;
714}
715
716template<typename Numeric>
717bool named_plot(const std::string& name, const std::vector<Numeric>& y, const std::string& format = "")
718{
719 PyObject* kwargs = PyDict_New();
720 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
721
722 PyObject* yarray = get_array(y);
723
724 PyObject* pystring = PyString_FromString(format.c_str());
725
726 PyObject* plot_args = PyTuple_New(2);
727
728 PyTuple_SetItem(plot_args, 0, yarray);
729 PyTuple_SetItem(plot_args, 1, pystring);
730
731 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
732
733 Py_DECREF(kwargs);
734 Py_DECREF(plot_args);
735 if (res) Py_DECREF(res);
736
737 return res;
738}
739
740template<typename Numeric>
741bool named_plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
742{
743 PyObject* kwargs = PyDict_New();
744 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
745
746 PyObject* xarray = get_array(x);
747 PyObject* yarray = get_array(y);
748
749 PyObject* pystring = PyString_FromString(format.c_str());
750
751 PyObject* plot_args = PyTuple_New(3);
752 PyTuple_SetItem(plot_args, 0, xarray);
753 PyTuple_SetItem(plot_args, 1, yarray);
754 PyTuple_SetItem(plot_args, 2, pystring);
755
756 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
757
758 Py_DECREF(kwargs);
759 Py_DECREF(plot_args);
760 if (res) Py_DECREF(res);
761
762 return res;
763}
764
765template<typename Numeric>
766bool named_semilogx(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
767{
768 PyObject* kwargs = PyDict_New();
769 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
770
771 PyObject* xarray = get_array(x);
772 PyObject* yarray = get_array(y);
773
774 PyObject* pystring = PyString_FromString(format.c_str());
775
776 PyObject* plot_args = PyTuple_New(3);
777 PyTuple_SetItem(plot_args, 0, xarray);
778 PyTuple_SetItem(plot_args, 1, yarray);
779 PyTuple_SetItem(plot_args, 2, pystring);
780
781 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogx, plot_args, kwargs);
782
783 Py_DECREF(kwargs);
784 Py_DECREF(plot_args);
785 if (res) Py_DECREF(res);
786
787 return res;
788}
789
790template<typename Numeric>
791bool named_semilogy(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
792{
793 PyObject* kwargs = PyDict_New();
794 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
795
796 PyObject* xarray = get_array(x);
797 PyObject* yarray = get_array(y);
798
799 PyObject* pystring = PyString_FromString(format.c_str());
800
801 PyObject* plot_args = PyTuple_New(3);
802 PyTuple_SetItem(plot_args, 0, xarray);
803 PyTuple_SetItem(plot_args, 1, yarray);
804 PyTuple_SetItem(plot_args, 2, pystring);
805
806 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_semilogy, plot_args, kwargs);
807
808 Py_DECREF(kwargs);
809 Py_DECREF(plot_args);
810 if (res) Py_DECREF(res);
811
812 return res;
813}
814
815template<typename Numeric>
816bool named_loglog(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "")
817{
818 PyObject* kwargs = PyDict_New();
819 PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
820
821 PyObject* xarray = get_array(x);
822 PyObject* yarray = get_array(y);
823
824 PyObject* pystring = PyString_FromString(format.c_str());
825
826 PyObject* plot_args = PyTuple_New(3);
827 PyTuple_SetItem(plot_args, 0, xarray);
828 PyTuple_SetItem(plot_args, 1, yarray);
829 PyTuple_SetItem(plot_args, 2, pystring);
830
831 PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_loglog, plot_args, kwargs);
832
833 Py_DECREF(kwargs);
834 Py_DECREF(plot_args);
835 if (res) Py_DECREF(res);
836
837 return res;
838}
839
840template<typename Numeric>
841bool plot(const std::vector<Numeric>& y, const std::string& format = "")
842{
843 std::vector<Numeric> x(y.size());
844 for(size_t i=0; i<x.size(); ++i) x.at(i) = i;
845 return plot(x,y,format);
846}
847
848template<typename Numeric>
849bool stem(const std::vector<Numeric>& y, const std::string& format = "")
850{
851 std::vector<Numeric> x(y.size());
852 for (size_t i = 0; i < x.size(); ++i) x.at(i) = i;
853 return stem(x, y, format);
854}
855
856inline void figure()
857{
858 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_figure, detail::_interpreter::get().s_python_empty_tuple);
859 if(!res) throw std::runtime_error("Call to figure() failed.");
860
861 Py_DECREF(res);
862}
863
864inline void legend()
865{
866 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_legend, detail::_interpreter::get().s_python_empty_tuple);
867 if(!res) throw std::runtime_error("Call to legend() failed.");
868
869 Py_DECREF(res);
870}
871
872template<typename Numeric>
873void ylim(Numeric left, Numeric right)
874{
875 PyObject* list = PyList_New(2);
876 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
877 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
878
879 PyObject* args = PyTuple_New(1);
880 PyTuple_SetItem(args, 0, list);
881
882 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
883 if(!res) throw std::runtime_error("Call to ylim() failed.");
884
885 Py_DECREF(args);
886 Py_DECREF(res);
887}
888
889template<typename Numeric>
890void xlim(Numeric left, Numeric right)
891{
892 PyObject* list = PyList_New(2);
893 PyList_SetItem(list, 0, PyFloat_FromDouble(left));
894 PyList_SetItem(list, 1, PyFloat_FromDouble(right));
895
896 PyObject* args = PyTuple_New(1);
897 PyTuple_SetItem(args, 0, list);
898
899 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
900 if(!res) throw std::runtime_error("Call to xlim() failed.");
901
902 Py_DECREF(args);
903 Py_DECREF(res);
904}
905
906
907inline double* xlim()
908{
909 PyObject* args = PyTuple_New(0);
910 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlim, args);
911 PyObject* left = PyTuple_GetItem(res,0);
912 PyObject* right = PyTuple_GetItem(res,1);
913
914 double* arr = new double[2];
915 arr[0] = PyFloat_AsDouble(left);
916 arr[1] = PyFloat_AsDouble(right);
917
918 if(!res) throw std::runtime_error("Call to xlim() failed.");
919
920 Py_DECREF(res);
921 return arr;
922}
923
924
925inline double* ylim()
926{
927 PyObject* args = PyTuple_New(0);
928 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylim, args);
929 PyObject* left = PyTuple_GetItem(res,0);
930 PyObject* right = PyTuple_GetItem(res,1);
931
932 double* arr = new double[2];
933 arr[0] = PyFloat_AsDouble(left);
934 arr[1] = PyFloat_AsDouble(right);
935
936 if(!res) throw std::runtime_error("Call to ylim() failed.");
937
938 Py_DECREF(res);
939 return arr;
940}
941
942inline void subplot(long nrows, long ncols, long plot_number)
943{
944 // construct positional args
945 PyObject* args = PyTuple_New(3);
946 PyTuple_SetItem(args, 0, PyFloat_FromDouble(nrows));
947 PyTuple_SetItem(args, 1, PyFloat_FromDouble(ncols));
948 PyTuple_SetItem(args, 2, PyFloat_FromDouble(plot_number));
949
950 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_subplot, args);
951 if(!res) throw std::runtime_error("Call to subplot() failed.");
952
953 Py_DECREF(args);
954 Py_DECREF(res);
955}
956
957inline void title(const std::string &titlestr)
958{
959 PyObject* pytitlestr = PyString_FromString(titlestr.c_str());
960 PyObject* args = PyTuple_New(1);
961 PyTuple_SetItem(args, 0, pytitlestr);
962
963 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_title, args);
964 if(!res) throw std::runtime_error("Call to title() failed.");
965
966 Py_DECREF(args);
967 Py_DECREF(res);
968}
969
970inline void axis(const std::string &axisstr)
971{
972 PyObject* str = PyString_FromString(axisstr.c_str());
973 PyObject* args = PyTuple_New(1);
974 PyTuple_SetItem(args, 0, str);
975
976 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_axis, args);
977 if(!res) throw std::runtime_error("Call to title() failed.");
978
979 Py_DECREF(args);
980 Py_DECREF(res);
981}
982
983inline void xlabel(const std::string &str)
984{
985 PyObject* pystr = PyString_FromString(str.c_str());
986 PyObject* args = PyTuple_New(1);
987 PyTuple_SetItem(args, 0, pystr);
988
989 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_xlabel, args);
990 if(!res) throw std::runtime_error("Call to xlabel() failed.");
991
992 Py_DECREF(args);
993 Py_DECREF(res);
994}
995
996inline void ylabel(const std::string &str)
997{
998 PyObject* pystr = PyString_FromString(str.c_str());
999 PyObject* args = PyTuple_New(1);
1000 PyTuple_SetItem(args, 0, pystr);
1001
1002 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_ylabel, args);
1003 if(!res) throw std::runtime_error("Call to ylabel() failed.");
1004
1005 Py_DECREF(args);
1006 Py_DECREF(res);
1007}
1008
1009inline void grid(bool flag)
1010{
1011 PyObject* pyflag = flag ? Py_True : Py_False;
1012 Py_INCREF(pyflag);
1013
1014 PyObject* args = PyTuple_New(1);
1015 PyTuple_SetItem(args, 0, pyflag);
1016
1017 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_grid, args);
1018 if(!res) throw std::runtime_error("Call to grid() failed.");
1019
1020 Py_DECREF(args);
1021 Py_DECREF(res);
1022}
1023
1024inline void show(const bool block = true)
1025{
1026 PyObject* res;
1027 if(block)
1028 {
1029 res = PyObject_CallObject(
1030 detail::_interpreter::get().s_python_function_show,
1031 detail::_interpreter::get().s_python_empty_tuple);
1032 }
1033 else
1034 {
1035 PyObject *kwargs = PyDict_New();
1036 PyDict_SetItemString(kwargs, "block", Py_False);
1037 res = PyObject_Call( detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple, kwargs);
1038 Py_DECREF(kwargs);
1039 }
1040
1041
1042 if (!res) throw std::runtime_error("Call to show() failed.");
1043
1044 Py_DECREF(res);
1045}
1046
1047inline void close()
1048{
1049 PyObject* res = PyObject_CallObject(
1050 detail::_interpreter::get().s_python_function_close,
1051 detail::_interpreter::get().s_python_empty_tuple);
1052
1053 if (!res) throw std::runtime_error("Call to close() failed.");
1054
1055 Py_DECREF(res);
1056}
1057
1058inline void xkcd() {
1059 PyObject* res;
1060 PyObject *kwargs = PyDict_New();
1061
1062 res = PyObject_Call(detail::_interpreter::get().s_python_function_xkcd,
1063 detail::_interpreter::get().s_python_empty_tuple, kwargs);
1064
1065 Py_DECREF(kwargs);
1066
1067 if (!res)
1068 throw std::runtime_error("Call to show() failed.");
1069
1070 Py_DECREF(res);
1071}
1072
1073inline void draw()
1074{
1075 PyObject* res = PyObject_CallObject(
1076 detail::_interpreter::get().s_python_function_draw,
1077 detail::_interpreter::get().s_python_empty_tuple);
1078
1079 if (!res) throw std::runtime_error("Call to draw() failed.");
1080
1081 Py_DECREF(res);
1082}
1083
1084template<typename Numeric>
1085inline void pause(Numeric interval)
1086{
1087 PyObject* args = PyTuple_New(1);
1088 PyTuple_SetItem(args, 0, PyFloat_FromDouble(interval));
1089
1090 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_pause, args);
1091 if(!res) throw std::runtime_error("Call to pause() failed.");
1092
1093 Py_DECREF(args);
1094 Py_DECREF(res);
1095}
1096
1097inline void save(const std::string& filename)
1098{
1099 PyObject* pyfilename = PyString_FromString(filename.c_str());
1100
1101 PyObject* args = PyTuple_New(1);
1102 PyTuple_SetItem(args, 0, pyfilename);
1103
1104 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_save, args);
1105 if (!res) throw std::runtime_error("Call to save() failed.");
1106
1107 Py_DECREF(args);
1108 Py_DECREF(res);
1109}
1110
1111inline void clf() {
1112 PyObject *res = PyObject_CallObject(
1113 detail::_interpreter::get().s_python_function_clf,
1114 detail::_interpreter::get().s_python_empty_tuple);
1115
1116 if (!res) throw std::runtime_error("Call to clf() failed.");
1117
1118 Py_DECREF(res);
1119}
1120
1121 inline void ion() {
1122 PyObject *res = PyObject_CallObject(
1123 detail::_interpreter::get().s_python_function_ion,
1124 detail::_interpreter::get().s_python_empty_tuple);
1125
1126 if (!res) throw std::runtime_error("Call to ion() failed.");
1127
1128 Py_DECREF(res);
1129}
1130
1131// Actually, is there any reason not to call this automatically for every plot?
1132inline void tight_layout() {
1133 PyObject *res = PyObject_CallObject(
1134 detail::_interpreter::get().s_python_function_tight_layout,
1135 detail::_interpreter::get().s_python_empty_tuple);
1136
1137 if (!res) throw std::runtime_error("Call to tight_layout() failed.");
1138
1139 Py_DECREF(res);
1140}
1141
1142#if __cplusplus > 199711L || _MSC_VER > 1800
1143// C++11-exclusive content starts here (variadic plot() and initializer list support)
1144
1145namespace detail {
1146
1147template<typename T>
1148using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
1149
1150template<bool obj, typename T>
1151struct is_callable_impl;
1152
1153template<typename T>
1154struct is_callable_impl<false, T>
1155{
1156 typedef is_function<T> type;
1157}; // a non-object is callable iff it is a function
1158
1159template<typename T>
1160struct is_callable_impl<true, T>
1161{
1162 struct Fallback { void operator()(); };
1163 struct Derived : T, Fallback { };
1164
1165 template<typename U, U> struct Check;
1166
1167 template<typename U>
1168 static std::true_type test( ... ); // use a variadic function to make sure (1) it accepts everything and (2) its always the worst match
1169
1170 template<typename U>
1171 static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
1172
1173public:
1174 typedef decltype(test<Derived>(nullptr)) type;
1175 typedef decltype(&Fallback::operator()) dtype;
1176 static constexpr bool value = type::value;
1177}; // an object is callable iff it defines operator()
1178
1179template<typename T>
1180struct is_callable
1181{
1182 // dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
1183 typedef typename is_callable_impl<std::is_class<T>::value, T>::type type;
1184};
1185
1186template<typename IsYDataCallable>
1187struct plot_impl { };
1188
1189template<>
1190struct plot_impl<std::false_type>
1191{
1192 template<typename IterableX, typename IterableY>
1193 bool operator()(const IterableX& x, const IterableY& y, const std::string& format)
1194 {
1195 // 2-phase lookup for distance, begin, end
1196 using std::distance;
1197 using std::begin;
1198 using std::end;
1199
1200 auto xs = distance(begin(x), end(x));
1201 auto ys = distance(begin(y), end(y));
1202 assert(xs == ys && "x and y data must have the same number of elements!");
1203
1204 PyObject* xlist = PyList_New(xs);
1205 PyObject* ylist = PyList_New(ys);
1206 PyObject* pystring = PyString_FromString(format.c_str());
1207
1208 auto itx = begin(x), ity = begin(y);
1209 for(size_t i = 0; i < xs; ++i) {
1210 PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++));
1211 PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++));
1212 }
1213
1214 PyObject* plot_args = PyTuple_New(3);
1215 PyTuple_SetItem(plot_args, 0, xlist);
1216 PyTuple_SetItem(plot_args, 1, ylist);
1217 PyTuple_SetItem(plot_args, 2, pystring);
1218
1219 PyObject* res = PyObject_CallObject(detail::_interpreter::get().s_python_function_plot, plot_args);
1220
1221 Py_DECREF(plot_args);
1222 if(res) Py_DECREF(res);
1223
1224 return res;
1225 }
1226};
1227
1228template<>
1229struct plot_impl<std::true_type>
1230{
1231 template<typename Iterable, typename Callable>
1232 bool operator()(const Iterable& ticks, const Callable& f, const std::string& format)
1233 {
1234 if(begin(ticks) == end(ticks)) return true;
1235
1236 // We could use additional meta-programming to deduce the correct element type of y,
1237 // but all values have to be convertible to double anyways
1238 std::vector<double> y;
1239 for(auto x : ticks) y.push_back(f(x));
1240 return plot_impl<std::false_type>()(ticks,y,format);
1241 }
1242};
1243
1244} // end namespace detail
1245
1246// recursion stop for the above
1247template<typename... Args>
1248bool plot() { return true; }
1249
1250template<typename A, typename B, typename... Args>
1251bool plot(const A& a, const B& b, const std::string& format, Args... args)
1252{
1253 return detail::plot_impl<typename detail::is_callable<B>::type>()(a,b,format) && plot(args...);
1254}
1255
1256/*
1257 * This group of plot() functions is needed to support initializer lists, i.e. calling
1258 * plot( {1,2,3,4} )
1259 */
1260inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::string& format = "") {
1261 return plot<double,double>(x,y,format);
1262}
1263
1264inline bool plot(const std::vector<double>& y, const std::string& format = "") {
1265 return plot<double>(y,format);
1266}
1267
1268inline bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::map<std::string, std::string>& keywords) {
1269 return plot<double>(x,y,keywords);
1270}
1271
1272#endif
1273
1274} // end namespace matplotlibcpp