Skip to content

Commit 07a73fa

Browse files
oKermorgantBenno Evers
authored and
Benno Evers
committed
Add dynamic plot class
1 parent 8babc98 commit 07a73fa

File tree

3 files changed

+220
-3
lines changed

3 files changed

+220
-3
lines changed

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
examples: minimal basic modern animation nonblock xkcd quiver bar surface fill_inbetween fill
1+
examples: minimal basic modern animation nonblock xkcd quiver bar surface fill_inbetween fill update
22

33
minimal: examples/minimal.cpp matplotlibcpp.h
44
cd examples && g++ -DWITHOUT_NUMPY minimal.cpp -I/usr/include/python2.7 -lpython2.7 -o minimal -std=c++11
@@ -32,6 +32,9 @@ fill_inbetween: examples/fill_inbetween.cpp matplotlibcpp.h
3232

3333
fill: examples/fill.cpp matplotlibcpp.h
3434
cd examples && g++ fill.cpp -I/usr/include/python2.7 -lpython2.7 -o fill -std=c++11
35+
36+
update: examples/update.cpp matplotlibcpp.h
37+
cd examples && g++ update.cpp -I/usr/include/python2.7 -lpython2.7 -o update -std=c++11
3538

3639
clean:
37-
rm -f examples/{minimal,basic,modern,animation,nonblock,xkcd,quiver,bar,surface,fill_inbetween,fill}
40+
rm -f examples/{minimal,basic,modern,animation,nonblock,xkcd,quiver,bar,surface,fill_inbetween,fill,update}

examples/update.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#define _USE_MATH_DEFINES
2+
#include <cmath>
3+
#include "../matplotlibcpp.h"
4+
#include <chrono>
5+
6+
namespace plt = matplotlibcpp;
7+
8+
void update_window(const double x, const double y, const double t,
9+
std::vector<double> &xt, std::vector<double> &yt)
10+
{
11+
const double target_length = 300;
12+
const double half_win = (target_length/(2.*sqrt(1.+t*t)));
13+
14+
xt[0] = x - half_win;
15+
xt[1] = x + half_win;
16+
yt[0] = y - half_win*t;
17+
yt[1] = y + half_win*t;
18+
}
19+
20+
21+
int main()
22+
{
23+
24+
bool use_dynamic_plot = false;
25+
bool timeit = true;
26+
27+
size_t n = 1000;
28+
std::vector<double> x, y;
29+
30+
const double w = 0.05;
31+
const double a = n/2;
32+
33+
for(size_t i=0; i<n; i++) {
34+
x.push_back(i);
35+
y.push_back(a*sin(w*i));
36+
}
37+
38+
std::vector<double> xt(2), yt(2);
39+
40+
plt::title("Sample figure");
41+
42+
std::chrono::time_point<std::chrono::system_clock> start, end;
43+
start = std::chrono::system_clock::now();
44+
45+
if(use_dynamic_plot)
46+
{
47+
plt::xlim(x.front(), x.back());
48+
plt::ylim(-a,a);
49+
plt::axis("equal");
50+
51+
// plot sin once and for all
52+
plt::named_plot("sin", x, y);
53+
54+
// prepare plotting the tangent
55+
plt::Plot plot("tangent");
56+
57+
plt::legend();
58+
59+
for(size_t i=0; i<n; i++) {
60+
61+
if (i % 10 == 0) {
62+
{
63+
update_window(x[i], y[i], a*w*cos(w*x[i]), xt, yt);
64+
// just update data for this plot
65+
plot.update(xt, yt);
66+
}
67+
68+
// Display plot continuously
69+
if(!timeit)
70+
plt::pause(0.1);
71+
}
72+
}
73+
}
74+
75+
else
76+
{
77+
for(size_t i=0; i<n; i++) {
78+
79+
if (i % 10 == 0) {
80+
{
81+
plt::clf();
82+
83+
plt::named_plot("sin", x, y);
84+
85+
update_window(x[i], y[i], a*w*cos(w*i), xt, yt);
86+
plt::named_plot("tangent", xt, yt);
87+
88+
// we have to control axis size
89+
plt::xlim(x.front(), x.back());
90+
plt::ylim(-a,a);
91+
plt::axis("equal");
92+
plt::legend();
93+
94+
}
95+
96+
// Display plot continuously
97+
if(!timeit)
98+
plt::pause(0.1);
99+
}
100+
}
101+
}
102+
103+
end = std::chrono::system_clock::now();
104+
double elapsed_seconds = std::chrono::duration_cast<std::chrono::microseconds>
105+
(end-start).count();
106+
if(use_dynamic_plot)
107+
std::cout << "dynamic";
108+
else
109+
std::cout << "static";
110+
111+
std::cout << " : " << elapsed_seconds/1000 << " ms\n";
112+
}

matplotlibcpp.h

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,7 @@ inline void show(const bool block = true)
14391439
PyObject *kwargs = PyDict_New();
14401440
PyDict_SetItemString(kwargs, "block", Py_False);
14411441
res = PyObject_Call( detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple, kwargs);
1442-
Py_DECREF(kwargs);
1442+
Py_DECREF(kwargs);
14431443
}
14441444

14451445

@@ -1706,4 +1706,106 @@ inline bool plot(const std::vector<double>& x, const std::vector<double>& y, con
17061706
return plot<double>(x,y,keywords);
17071707
}
17081708

1709+
/*
1710+
* This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting
1711+
*/
1712+
1713+
class Plot
1714+
{
1715+
public:
1716+
// default initialization with plot label, some data and format
1717+
template<typename Numeric>
1718+
Plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") {
1719+
1720+
assert(x.size() == y.size());
1721+
1722+
PyObject* kwargs = PyDict_New();
1723+
if(name != "")
1724+
PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
1725+
1726+
PyObject* xarray = get_array(x);
1727+
PyObject* yarray = get_array(y);
1728+
1729+
PyObject* pystring = PyString_FromString(format.c_str());
1730+
1731+
PyObject* plot_args = PyTuple_New(3);
1732+
PyTuple_SetItem(plot_args, 0, xarray);
1733+
PyTuple_SetItem(plot_args, 1, yarray);
1734+
PyTuple_SetItem(plot_args, 2, pystring);
1735+
1736+
PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
1737+
1738+
Py_DECREF(kwargs);
1739+
Py_DECREF(plot_args);
1740+
1741+
if(res)
1742+
{
1743+
line= PyList_GetItem(res, 0);
1744+
1745+
if(line)
1746+
set_data_fct = PyObject_GetAttrString(line,"set_data");
1747+
else
1748+
Py_DECREF(line);
1749+
Py_DECREF(res);
1750+
}
1751+
}
1752+
1753+
// shorter initialization with name or format only
1754+
// basically calls line, = plot([], [])
1755+
Plot(const std::string& name = "", const std::string& format = "")
1756+
: Plot(name, std::vector<double>(), std::vector<double>(), format) {}
1757+
1758+
template<typename Numeric>
1759+
bool update(const std::vector<Numeric>& x, const std::vector<Numeric>& y) {
1760+
assert(x.size() == y.size());
1761+
if(set_data_fct)
1762+
{
1763+
PyObject* xarray = get_array(x);
1764+
PyObject* yarray = get_array(y);
1765+
1766+
PyObject* plot_args = PyTuple_New(2);
1767+
PyTuple_SetItem(plot_args, 0, xarray);
1768+
PyTuple_SetItem(plot_args, 1, yarray);
1769+
1770+
PyObject* res = PyObject_CallObject(set_data_fct, plot_args);
1771+
if (res) Py_DECREF(res);
1772+
return res;
1773+
}
1774+
return false;
1775+
}
1776+
1777+
// clears the plot but keep it available
1778+
bool clear() {
1779+
return update(std::vector<double>(), std::vector<double>());
1780+
}
1781+
1782+
// definitely remove this line
1783+
void remove() {
1784+
if(line)
1785+
{
1786+
auto remove_fct = PyObject_GetAttrString(line,"remove");
1787+
PyObject* args = PyTuple_New(0);
1788+
PyObject* res = PyObject_CallObject(remove_fct, args);
1789+
if (res) Py_DECREF(res);
1790+
}
1791+
decref();
1792+
}
1793+
1794+
~Plot() {
1795+
decref();
1796+
}
1797+
private:
1798+
1799+
void decref() {
1800+
if(line)
1801+
Py_DECREF(line);
1802+
if(set_data_fct)
1803+
Py_DECREF(set_data_fct);
1804+
}
1805+
1806+
1807+
PyObject* line = nullptr;
1808+
PyObject* set_data_fct = nullptr;
1809+
};
1810+
17091811
} // end namespace matplotlibcpp

0 commit comments

Comments
 (0)