8000 Enable variadic plot command when c++11 is available · sirus20x6/matplotlib-cpp@029a613 · GitHub
[go: up one dir, main page]

Skip to content

Commit 029a613

Browse files
author
Benno Evers
committed
Enable variadic plot command when c++11 is available
1 parent f8422b6 commit 029a613

File tree

5 files changed

+182
-15
lines changed

5 files changed

+182
-15
lines changed

README.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ Complete minimal example:
1111
#include "matplotlibcpp.h"
1212
namespace plt = matplotlibcpp;
1313
int main() {
14-
std::vector<double> v {1,2,3,4};
15-
plt::plot(v);
14+
plt::plot({1,2,3,4});
1615
plt::show();
1716
}
1817

@@ -53,8 +52,41 @@ A more comprehensive example:
5352
plt::show();
5453
}
5554

55+
// g++ basic.cpp -lpython2.7
56+
5657
Result: ![Basic example](./examples/basic.png)
5758

59+
matplotlib-cpp doesn't require C++11, but will enable some additional syntactic sugar when available:
60+
61+
#include <cmath>
62+
#include "matplotlibcpp.h"
63+
64+
using namespace std;
65+
namespace plt = matplotlibcpp;
66+
67+
int main()
68+
{
69+
// Prepare data.
70+
int n = 5000; // number of data points
71+
vector<double> x(n),y(n);
72+
for(int i=0; i<n; ++i) {
73+
double t = 2*M_PI*i/n;
74+
x.at(i) = 16*sin(t)*sin(t)*sin(t);
75+
y.at(i) = 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t);
76+
}
77+
78+
// plot() takes an arbitrary number of (x,y,format)-triples.
79+
// x must be iterable (that is, anything providing begin(x) and end(x)),
80+
// y must either be callable (providing operator() const) or iterable.
81+
plt::plot(x, y, "r-", x, [](double d) { return 12.5+abs(sin(d)); }, "k-");
82+
83+
84+
// show plots
85+
plt::show();
86+
}
87+
88+
Result: ![Modern example](./examples/modern.png)
89+
5890
Installation
5991
------------
6092
matplotlib-cpp works by wrapping the popular python plotting library matplotlib. (matplotlib.org)
@@ -63,9 +95,9 @@ On Ubuntu:
6395

6496
sudo aptitude install python-matplotlib python2.7-dev
6597

66-
The C++-part of the library consists of the single header file matplotlibcpp.h which can be placed
98+
The C++-part of the library consists of the single header file `matplotlibcpp.h` which can be placed
6799
anywhere.
68-
Since a python interpreter is opened internally, it is necessary to link against libpython2.7 in order to use
100+
Since a python interpreter is opened internally, it is necessary to link against `libpython2.7` in order to use
69101
matplotlib-cpp.
70102
(There should be no problems using python3 instead of python2.7, if desired)
71103

@@ -75,5 +107,5 @@ Todo/Issues/Wishlist
75107
* It would be nice to have a more object-oriented design with a Plot class which would allow
76108
multiple independent plots per program.
77109

78-
* Right now, only a small subset of matplotlibs functionality is exposed. Stuff like xlabel()/ylabel() etc. should
110+
* Right now, only a small subset of matplotlibs functionality is exposed. Stuff like save()/xlabel()/ylabel() etc. should
79111
be easy to add.

examples/minimal.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace plt = matplotlibcpp;
44

55
int main() {
6-
std::vector<double> v {1,2,3,4};
7-
plt::plot(v);
6+
plt::plot({1,2,3,4});
87
plt::show();
98
}

examples/modern.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
#include "../matplotlibcpp.h"
22

3+
#include <cmath>
4+
5+
using namespace std;
36
namespace plt = matplotlibcpp;
47

58
int main()
69
{
710
// plot(y) - the x-coordinates are implicitly set to [0,1,...,n)
8-
plt::plot({1,2,3,1,3.5,2.5});
9-
// plot(x,y,format) - plot as solid red line with circular markers
10-
plt::plot({5,4,3,2,-1}, {-1, 4, 2, 7, 1}, "ro-");
11+
//plt::plot({1,2,3,4});
12+
13+
// Prepare data for parametric plot.
14+
int n = 5000; // number of data points
15+
vector<double> x(n),y(n);
16+
for(int i=0; i<n; ++i) {
17+
double t = 2*M_PI*i/n;
18+
x.at(i) = 16*sin(t)*sin(t)*sin(t);
19+
y.at(i) = 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t);
20+
}
21+
22+
// plot() takes an arbitrary number of (x,y,format)-triples.
23+
// x must be iterable (that is, anything providing begin(x) and end(x)),
24+
// y must either be callable (providing operator() const) or iterable.
25+
plt::plot(x, y, "r-", x, [](double d) { return 12.5+abs(sin(d)); }, "k-");
26+
1127

1228
// show plots
1329
plt::show();

examples/modern.png

29.7 KB
Loading

matplotlibcpp.h

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
#include <map>
55
#include <numeric>
66
#include <stdexcept>
7+
#include <iostream>
8+
9+
#if __cplusplus > 199711L
10+
#include <functional>
11+
#endif
712

813
#include <python2.7/Python.h>
914

@@ -26,7 +31,8 @@ namespace matplotlibcpp {
2631

2732
private:
2833
_pyplot_global() {
29-
Py_SetProgramName("plotting"); /* optional but recommended */
34+
char name[] = "plotting"; // silence compiler warning abount const strings
35+
Py_SetProgramName(name); // optional but recommended
3036
Py_Initialize();
3137

3238
PyObject* pyname = PyString_FromString("matplotlib.pyplot");
@@ -109,13 +115,16 @@ namespace matplotlibcpp {
109115
}
110116

111117

112-
template<typename Numeric>
113-
bool plot(const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") {
118+
template<typename NumericX, typename NumericY>
119+
bool plot(const std::vector<NumericX>& x, const std::vector<NumericY>& y, const std::string& s = "")
120+
{
114121
assert(x.size() == y.size());
115122

123+
//std::string format(s);
124+
116125
PyObject* xlist = PyList_New(x.size());
117126
PyObject* ylist = PyList_New(y.size());
118-
PyObject* pystring = PyString_FromString(format.c_str());
127+
PyObject* pystring = PyString_FromString(s.c_str());
119128

120129
for(size_t i = 0; i < x.size(); ++i) {
121130
PyList_SetItem(xlist, i, PyFloat_FromDouble(x.at(i)));
@@ -181,7 +190,7 @@ namespace matplotlibcpp {
181190
* plot( {1,2,3,4} )
182191
*/
183192
bool plot(const std::vector<double>& x, const std::vector<double>& y, const std::string& format = "") {
184-
return plot<double>(x,y,format);
193+
return plot<double,double>(x,y,format);
185194
}
186195

187196
bool plot(const std::vector<double>& y, const std::string& format = "") {
@@ -196,6 +205,117 @@ namespace matplotlibcpp {
196205
return named_plot<double>(name,x,y,format);
197206
}
198207

208+
#if __cplusplus > 199711L
209+
210+
template<typename T>
211+
using is_function = typename std::is_function<std::remove_pointer<std::remove_reference<T>>>::type;
212+
213+
template<bool obj, typename T>
214+
struct is_callable_impl;
215+
216+
template<typename T>
217+
struct is_callable_impl<false, T>
218+
{
219+
typedef is_function<T> type;
220+
}; // a non-object is callable iff it is a function
221+
222+
template<typename T>
223+
struct is_callable_impl<true, T>
224+
{
225+
struct Fallback { void operator()(); };
226+
struct Derived : T, Fallback { };
227+
228+
template<typename U, U> struct Check;
229+
230+
template<typename U>
231+
static std::true_type test( ... ); // use a variadic function to make use (1) it accepts everything and (2) its always the worst match
232+
233+
template<typename U>
234+
static std::false_type test( Check<void(Fallback::*)(), &U::operator()>* );
235+
236+
public:
237+
typedef decltype(test<Derived>(nullptr)) type;
238+
typedef decltype(&Fallback::operator()) dtype;
239+
static constexpr bool value = type::value;
240+
}; // an object is callable iff it defines operator()
241+
242+
template<typename T>
243+
struct is_callable
244+
{
245+
// dispatch to is_callable_impl<true, T> or is_callable_impl<false, T> depending on whether T is of class type or not
246+
typedef typename is_callable_impl<std::is_class<T>::value, T>::type type; // todo: restore remove_reference
247+
};
248+
249+
template<typename IsYDataCallable>
250+
struct plot_impl { };
251+
252+
template<>
253+
struct plot_impl<std::false_type>
254+
{
255+
template<typename IterableX, typename IterableY>
256+
bool operator()(const IterableX& x, const IterableY& y, const std::string& format)
257+
{
258+
// It's annoying that we have to repeat the code of plot() above
259+
auto xs = std::distance(std::begin(x), std::end(x));
260+
auto ys = std::distance(std::begin(y), std::end(y));
261+
assert(xs == ys && "x and y data must have the same number of elements!");
262+
263+
PyObject* xlist = PyList_New(xs);
264+
PyObject* ylist = PyList_New(ys);
265+
PyObject* pystring = PyString_FromString(format.c_str());
266+
267+
auto itx = std::begin(x), ity = std::begin(y);
268+
for(size_t i = 0; i < xs; ++i) {
269+
PyList_SetItem(xlist, i, PyFloat_FromDouble(*itx++));
270+
PyList_SetItem(ylist, i, PyFloat_FromDouble(*ity++));
271+
}
272+
273+
PyObject* plot_args = PyTuple_New(3);
274+
PyTuple_SetItem(plot_args, 0, xlist);
275+
PyTuple_SetItem(plot_args, 1, ylist);
276+
PyTuple_SetItem(plot_args, 2, pystring);
277+
278+
PyObject* res = PyObject_CallObject(detail::_pyplot_global::get().s_python_function_plot, plot_args);
279+
280+
Py_DECREF(xlist);
281+
Py_DECREF(ylist);
282+
Py_DECREF(plot_args);
283+
if(res) Py_DECREF(res);
284+
285+
return res;
286+
}
287+
};
288+
289+
template<>
290+
struct plot_impl<std::true_type>
291+
{
292+
template<typename Iterable, typename Callable>
293+
bool operator()(const Iterable& ticks, const Callable& f, const std::string& format)
294+
{
295+
//std::cout << "Callable impl called" << std::endl;
296+
297+
if(begin(ticks) == end(ticks)) return true;
298+
299+
// We could use additional meta-programming to deduce the correct element type of y,
300+
// but all values have to be convertible to double anyways
301+
std::vector<double> y;
302+
for(auto x : ticks) y.push_back(f(x));
303+
return plot_impl<std::false_type>()(ticks,y,format);
304+
}
305+
};
306+
307+
// recursion stop for the above
308+
template<typename... Args>
309+
bool plot() { return true; }
310+
311+
template<typename A, typename B, typename... Args>
312+
bool plot(const A& a, const B& b, const std::string& format, Args... args)
313+
{
314+
return plot_impl<typename is_callable<B>::type>()(a,b,format) && plot(args...);
315+
}
316+
317+
#endif
318+
199319
inline void legend() {
200320
PyObject* res = PyObject_CallObject(detail::_pyplot_global::get().s_python_function_legend, detail::_pyplot_global::get().s_python_empty_tuple);
201321
if(!res) throw std::runtime_error("Call to legend() failed.");

0 commit comments

Comments
 (0)
0