Parcourir la source

Add dynamic plot class

Olivier Kermorgant il y a 6 ans
Parent
commit
07a73fad22
3 fichiers modifiés avec 220 ajouts et 3 suppressions
  1. 5 2
      Makefile
  2. 112 0
      examples/update.cpp
  3. 103 1
      matplotlibcpp.h

+ 5 - 2
Makefile

@@ -1,4 +1,4 @@
-examples: minimal basic modern animation nonblock xkcd quiver bar surface fill_inbetween fill
+examples: minimal basic modern animation nonblock xkcd quiver bar surface fill_inbetween fill update
 
 minimal: examples/minimal.cpp matplotlibcpp.h
 	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
 
 fill: examples/fill.cpp matplotlibcpp.h
 	cd examples && g++ fill.cpp -I/usr/include/python2.7 -lpython2.7 -o fill -std=c++11
+	
+update: examples/update.cpp matplotlibcpp.h
+	cd examples && g++ update.cpp -I/usr/include/python2.7 -lpython2.7 -o update -std=c++11
 
 clean:
-	rm -f examples/{minimal,basic,modern,animation,nonblock,xkcd,quiver,bar,surface,fill_inbetween,fill}
+	rm -f examples/{minimal,basic,modern,animation,nonblock,xkcd,quiver,bar,surface,fill_inbetween,fill,update}

+ 112 - 0
examples/update.cpp

@@ -0,0 +1,112 @@
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include "../matplotlibcpp.h"
+#include <chrono>
+
+namespace plt = matplotlibcpp;
+
+void update_window(const double x, const double y, const double t,
+                   std::vector<double> &xt, std::vector<double> &yt)
+{
+    const double target_length = 300;
+    const double half_win = (target_length/(2.*sqrt(1.+t*t)));
+
+    xt[0] = x - half_win;
+    xt[1] = x + half_win;
+    yt[0] = y - half_win*t;
+    yt[1] = y + half_win*t;
+}
+
+
+int main()
+{
+
+    bool use_dynamic_plot = false;
+    bool timeit = true;
+
+    size_t n = 1000;
+    std::vector<double> x, y;
+
+    const double w = 0.05;
+    const double a = n/2;
+
+    for(size_t i=0; i<n; i++) {
+        x.push_back(i);
+        y.push_back(a*sin(w*i));
+    }
+
+    std::vector<double> xt(2), yt(2);
+
+    plt::title("Sample figure");
+
+    std::chrono::time_point<std::chrono::system_clock> start, end;
+    start = std::chrono::system_clock::now();
+
+    if(use_dynamic_plot)
+    {
+        plt::xlim(x.front(), x.back());
+        plt::ylim(-a,a);
+        plt::axis("equal");
+
+        // plot sin once and for all
+        plt::named_plot("sin", x, y);
+
+        // prepare plotting the tangent
+        plt::Plot plot("tangent");
+
+        plt::legend();
+
+        for(size_t i=0; i<n; i++) {
+
+            if (i % 10 == 0) {
+                {
+                    update_window(x[i], y[i], a*w*cos(w*x[i]), xt, yt);
+                    // just update data for this plot
+                    plot.update(xt, yt);
+                }
+
+                // Display plot continuously
+                if(!timeit)
+                    plt::pause(0.1);
+            }
+        }
+    }
+
+    else
+    {
+        for(size_t i=0; i<n; i++) {
+
+            if (i % 10 == 0) {
+                {
+                    plt::clf();
+
+                    plt::named_plot("sin", x, y);
+
+                    update_window(x[i], y[i], a*w*cos(w*i), xt, yt);
+                    plt::named_plot("tangent", xt, yt);
+
+                    // we have to control axis size
+                    plt::xlim(x.front(), x.back());
+                    plt::ylim(-a,a);
+                    plt::axis("equal");
+                    plt::legend();
+
+                }
+
+                // Display plot continuously
+                if(!timeit)
+                    plt::pause(0.1);
+            }
+        }
+    }
+
+    end = std::chrono::system_clock::now();
+    double elapsed_seconds = std::chrono::duration_cast<std::chrono::microseconds>
+            (end-start).count();
+    if(use_dynamic_plot)
+        std::cout << "dynamic";
+    else
+        std::cout << "static";
+
+    std::cout << " : " << elapsed_seconds/1000 << " ms\n";
+}

+ 103 - 1
matplotlibcpp.h

@@ -1439,7 +1439,7 @@ inline void show(const bool block = true)
         PyObject *kwargs = PyDict_New();
         PyDict_SetItemString(kwargs, "block", Py_False);
         res = PyObject_Call( detail::_interpreter::get().s_python_function_show, detail::_interpreter::get().s_python_empty_tuple, kwargs);
-	Py_DECREF(kwargs);
+       Py_DECREF(kwargs);
     }
 
 
@@ -1706,4 +1706,106 @@ inline bool plot(const std::vector<double>& x, const std::vector<double>& y, con
     return plot<double>(x,y,keywords);
 }
 
+/*
+ * This class allows dynamic plots, ie changing the plotted data without clearing and re-plotting
+ */
+
+class Plot
+{
+public:
+    // default initialization with plot label, some data and format
+    template<typename Numeric>
+    Plot(const std::string& name, const std::vector<Numeric>& x, const std::vector<Numeric>& y, const std::string& format = "") {
+
+        assert(x.size() == y.size());
+
+        PyObject* kwargs = PyDict_New();
+        if(name != "")
+            PyDict_SetItemString(kwargs, "label", PyString_FromString(name.c_str()));
+
+        PyObject* xarray = get_array(x);
+        PyObject* yarray = get_array(y);
+
+        PyObject* pystring = PyString_FromString(format.c_str());
+
+        PyObject* plot_args = PyTuple_New(3);
+        PyTuple_SetItem(plot_args, 0, xarray);
+        PyTuple_SetItem(plot_args, 1, yarray);
+        PyTuple_SetItem(plot_args, 2, pystring);
+
+        PyObject* res = PyObject_Call(detail::_interpreter::get().s_python_function_plot, plot_args, kwargs);
+
+        Py_DECREF(kwargs);
+        Py_DECREF(plot_args);
+
+        if(res)
+        {
+            line= PyList_GetItem(res, 0);
+
+            if(line)
+                set_data_fct = PyObject_GetAttrString(line,"set_data");
+            else
+                Py_DECREF(line);
+            Py_DECREF(res);
+        }
+    }
+
+    // shorter initialization with name or format only
+    // basically calls line, = plot([], [])
+    Plot(const std::string& name = "", const std::string& format = "")
+        : Plot(name, std::vector<double>(), std::vector<double>(), format) {}
+
+    template<typename Numeric>
+    bool update(const std::vector<Numeric>& x, const std::vector<Numeric>& y) {
+        assert(x.size() == y.size());
+        if(set_data_fct)
+        {
+            PyObject* xarray = get_array(x);
+            PyObject* yarray = get_array(y);
+
+            PyObject* plot_args = PyTuple_New(2);
+            PyTuple_SetItem(plot_args, 0, xarray);
+            PyTuple_SetItem(plot_args, 1, yarray);
+
+            PyObject* res = PyObject_CallObject(set_data_fct, plot_args);
+            if (res) Py_DECREF(res);
+            return res;
+        }
+        return false;
+    }
+
+    // clears the plot but keep it available
+    bool clear() {
+        return update(std::vector<double>(), std::vector<double>());
+    }
+
+    // definitely remove this line
+    void remove() {
+        if(line)
+        {
+            auto remove_fct = PyObject_GetAttrString(line,"remove");
+            PyObject* args = PyTuple_New(0);
+            PyObject* res = PyObject_CallObject(remove_fct, args);
+            if (res) Py_DECREF(res);
+        }
+        decref();
+    }
+
+    ~Plot() {
+        decref();
+    }
+private:
+
+    void decref() {
+        if(line)
+            Py_DECREF(line);
+        if(set_data_fct)
+            Py_DECREF(set_data_fct);
+    }
+
+
+    PyObject* line = nullptr;
+    PyObject* set_data_fct = nullptr;
+};
+
 } // end namespace matplotlibcpp