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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy