On Mon, Apr 20, 2009 at 4:34 PM, Elan Pavlov <e...@mit.edu> wrote:
> Hi,
>
> I'm trying to use matplotlib for animating data as it is received from an
> online source (online in the algorithmic sense not internet:). I'd like
> the graph plot to be updated with high frequency since the data changes
> rapidly. I've used the BufferRegion with copy_from_bbox / restore_region
> and it speeds up the plotting considerably but alas it's still not good
> enough (with a large number of graphs and a large number of data points in
> each graph).
>

Well, if you need your plot updated with very high frequency, MPL may
not be your tool of choice.
Anyhow, my first recommendation is not to update the plot frequently.
I mean, do you have to update the plot for every changes? Can you
update it every 10th change, for example?

> What I'd like to do is to utilize the fact that the animation is updated
> in a predictable fashion (i.e., scrolling off the screen to the left as
> new data arrives) in order to speed up the animation. The idea would be to
> copy the right 99% of the graph (or some other fraction) via some kind of
> function similar to copy_from_bbox, move it 1% to the left and then plot
> the new 1% of the data. The problem is that as far as I can tell the
> copy_from_bbox/restore_region does not actually allow changing the area in
> which it is restored. I've mucked around in the source files a bit but to
> no avail.
>
> My question is then:
> 1. Is there some other way to copy everything enclosed in a Bbox? or
> 2. Is there some way to modify the region in which data reappears when
> using restore_region?
> 3. Perhaps I'm missing something?
>
> I'm sure someone else has done this since it seems pretty natural and
> useful for a variety of applications.

I'm attaching a patch that might do what you want to do.
It implements a "restore_bbox2" method which restores a subset of the
saved background at a specified position.
A small example is also attached (it is based on the gtk backend).

I, personally, am not sure if this kind of feature is useful. If you
shift part of your figure, you have to be careful not to mess up with
the coordinate system. And also be careful about what to be shifted
and what to be not (e.g., ticks).

If others find this useful, I'll commit this to the trunk with some
api improvement.

Regards,

-JJ


>
> Elan
> ----
> "If stupidity got us into this mess, why can't it get us out?"
>        - Will Rogers
>
> ------------------------------------------------------------------------------
> Stay on top of everything new and different, both inside and
> around Java (TM) technology - register by April 22, and save
> $200 on the JavaOne (SM) conference, June 2-5, 2009, San Francisco.
> 300 plus technical and hands-on sessions. Register today.
> Use priority code J9JMT32. http://p.sf.net/sfu/p
> _______________________________________________
> Matplotlib-users mailing list
> Matplotlib-users@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/matplotlib-users
>
Index: src/_backend_agg.cpp
===================================================================
--- src/_backend_agg.cpp	(revision 7061)
+++ src/_backend_agg.cpp	(working copy)
@@ -104,6 +104,18 @@
     return Py::Object();
 }
 
+Py::Object BufferRegion::get_bounds(const Py::Tuple &args) {
+    args.verify_length(0);
+
+    Py::Tuple bounds(4);
+    bounds[0] = Py::Int(rect.x1);
+    bounds[1] = Py::Int(rect.y1);
+    bounds[2] = Py::Int(rect.x2);
+    bounds[3] = Py::Int(rect.y2);
+
+    return bounds;
+}
+
 Py::Object BufferRegion::to_string_argb(const Py::Tuple &args) {
   // owned=true to prevent memory leak
   Py_ssize_t length;
@@ -426,6 +438,54 @@
   return Py::Object();
 }
 
+Py::Object
+RendererAgg::restore_region2(const Py::Tuple& args) {
+  //copy BufferRegion to buffer
+  args.verify_length(7);
+
+
+
+  int x(0),y(0), xx1(0),yy1(0), xx2(0), yy2(0);
+  try {
+    xx1 = Py::Int( args[1] );
+    yy1 = Py::Int( args[2] );
+    xx2 = Py::Int( args[3] );
+    yy2 = Py::Int( args[4] );
+    x = Py::Int( args[5] );
+    y = Py::Int( args[6] );
+  }
+  catch (Py::TypeError) {
+    throw Py::TypeError("Invalid input arguments to draw_text_image");
+  }
+
+
+  BufferRegion* region  = static_cast<BufferRegion*>(args[0].ptr());
+
+  if (region->data==NULL)
+    throw Py::ValueError("Cannot restore_region from NULL data");
+    //return Py::Object();
+
+  //std::cout << "restoring " << region->width << " " << region->height << " " << region->stride << " " << region->rect.x1 << " " << region->rect.y1 << std::endl;
+
+  //agg::rect_i rect((int)l, height - (int)t, (int)r, height - (int)b);
+  //agg::rect_i rect(xx1-region->rect.x1, height - (yy2-region->rect.y1), 
+  //xx2-region->rect.x1, height - (yy1-region->rect.y1));
+  agg::rect_i rect(xx1-region->rect.x1, (yy1-region->rect.y1), 
+		   xx2-region->rect.x1, (yy2-region->rect.y1));
+
+
+  agg::rendering_buffer rbuf;
+  rbuf.attach(region->data,
+	      region->width,
+	      region->height,
+	      region->stride);
+
+  //rendererBase.copy_from(rbuf, 0, region->rect.x1, region->rect.y1);
+  rendererBase.copy_from(rbuf, &rect, x, y);
+
+  return Py::Object();
+}
+
 bool RendererAgg::render_clippath(const Py::Object& clippath, const agg::trans_affine& clippath_trans) {
   typedef agg::conv_transform<PathIterator> transformed_path_t;
   typedef agg::conv_curve<transformed_path_t> curve_t;
@@ -1717,6 +1777,9 @@
   add_varargs_method("set_y", &BufferRegion::set_y,
 		     "set_y(y)");
 
+  add_varargs_method("get_bounds", &BufferRegion::get_bounds,
+		     "get_bounds()");
+
   add_varargs_method("to_string", &BufferRegion::to_string,
 		     "to_string()");
   add_varargs_method("to_string_argb", &BufferRegion::to_string_argb,
@@ -1759,6 +1822,8 @@
  		     "copy_from_bbox(bbox)");
   add_varargs_method("restore_region", &RendererAgg::restore_region,
  		     "restore_region(region)");
+  add_varargs_method("restore_region2", &RendererAgg::restore_region2,
+ 		     "restore_region(region, x1, y1, x2, y2, x3, y3)");
 }
 
 extern "C"
Index: src/_backend_agg.h
===================================================================
--- src/_backend_agg.h	(revision 7061)
+++ src/_backend_agg.h	(working copy)
@@ -87,6 +87,8 @@
   Py::Object set_x(const Py::Tuple &args);
   Py::Object set_y(const Py::Tuple &args);
 
+  Py::Object get_bounds(const Py::Tuple &args);
+
   Py::Object to_string(const Py::Tuple &args);
   Py::Object to_string_argb(const Py::Tuple &args);
   static void init_type(void);
@@ -174,6 +176,7 @@
 
   Py::Object copy_from_bbox(const Py::Tuple & args);
   Py::Object restore_region(const Py::Tuple & args);
+  Py::Object restore_region2(const Py::Tuple & args);
 
   virtual ~RendererAgg();
 
#!/usr/bin/env python

import time

import gtk, gobject

import matplotlib
matplotlib.use('GTKAgg')

import numpy as np
import matplotlib.pyplot as plt


fig = plt.figure()
ax = fig.add_subplot(111)
canvas = fig.canvas

fig.subplots_adjust(left=0.3, bottom=0.3) # check for flipy bugs
#ax.grid() # to ensure proper background restore

# create the initial line
x = np.arange(0,2*np.pi,0.01)
line, = ax.plot(x, np.sin(x), animated=True, lw=2)
canvas.draw()

# for profiling
tstart = time.time()


class UpdateLine(object):
    def __init__(self, canvas, ax):
        self.cnt = 0
        self.canvas = canvas
        self.ax = ax

        # empty background
        self.background1 = canvas.copy_from_bbox(ax.bbox)

        ax.draw_artist(line)
        # background with line. This will be shifted
        self.background2 = canvas.copy_from_bbox(ax.bbox)
        self.bounds = self.background2.get_bounds()

        x1, y1, x2, y2 = self.bounds

        self.dx = x2 - x1


    def update_line(self, *args):
        # restore the clean slate background
        self.canvas.restore_region(self.background1)


        # restore subregion (x1+self.cnt, y1, x2, y2) of the second bg 
        # with a given offset (x1-self.cnt, y1)
        x1, y1, x2, y2 = self.bounds
        self.canvas.restore_region2 = self.canvas.renderer._renderer.restore_region2
        self.canvas.restore_region2(self.background2,
                                    x1+self.cnt, y1, x2, y2, x1-self.cnt, y1)

        # just redraw the axes rectangle
        self.canvas.blit(self.ax.bbox)

        if self.cnt==self.dx:
            # print the timing info and quit
            print 'FPS:' , 1000/(time.time()-tstart)
            gtk.main_quit()
            raise SystemExit

        self.cnt += 1
        return True


def start_anim(event):
    ul = UpdateLine(canvas, ax)
    gobject.idle_add(ul.update_line)
    canvas.mpl_disconnect(start_anim.cid)

start_anim.cid = canvas.mpl_connect('draw_event', start_anim)



plt.show()
------------------------------------------------------------------------------
Stay on top of everything new and different, both inside and 
around Java (TM) technology - register by April 22, and save
$200 on the JavaOne (SM) conference, June 2-5, 2009, San Francisco.
300 plus technical and hands-on sessions. Register today. 
Use priority code J9JMT32. http://p.sf.net/sfu/p
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users

Reply via email to