Revision: 7102
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7102&view=rev
Author:   leejjoon
Date:     2009-05-14 06:27:58 +0000 (Thu, 14 May 2009)

Log Message:
-----------
An optional offset and bbox support in restore_bbox

Modified Paths:
--------------
    trunk/matplotlib/CHANGELOG
    trunk/matplotlib/lib/matplotlib/backends/backend_agg.py
    trunk/matplotlib/src/_backend_agg.cpp
    trunk/matplotlib/src/_backend_agg.h

Added Paths:
-----------
    trunk/matplotlib/examples/animation/animation_blit_gtk2.py

Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG  2009-05-14 04:00:38 UTC (rev 7101)
+++ trunk/matplotlib/CHANGELOG  2009-05-14 06:27:58 UTC (rev 7102)
@@ -1,4 +1,7 @@
 ======================================================================
+2009-05-13 An optional offset and bbox support in restore_bbox. 
+           Add animation_blit_gtk2.py. -JJL
+
 2009-05-13 psfrag in backend_ps now uses baseline-alignment 
            when preview.sty is used ((default is
            bottom-alignment). Also, a small api imporvement 

Added: trunk/matplotlib/examples/animation/animation_blit_gtk2.py
===================================================================
--- trunk/matplotlib/examples/animation/animation_blit_gtk2.py                  
        (rev 0)
+++ trunk/matplotlib/examples/animation/animation_blit_gtk2.py  2009-05-14 
06:27:58 UTC (rev 7102)
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+
+"""
+This example utlizes restore_region with optional bbox and xy
+arguments.  The plot is continuously shifted to the left. Instead of
+drawing everything again, the plot is saved (copy_from_bbox) and
+restored with offset by the amount of the shift. And only newly
+exposed area is drawn. This technique may reduce drawing time for some cases.
+"""
+
+import time
+
+import gtk, gobject
+
+import matplotlib
+matplotlib.use('GTKAgg')
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+class UpdateLine(object):
+    def get_bg_bbox(self):
+        
+        return self.ax.bbox.padded(-3)
+    
+    def __init__(self, canvas, ax):
+        self.cnt = 0
+        self.canvas = canvas
+        self.ax = ax
+
+        self.prev_time = time.time()
+        self.start_time = self.prev_time
+        self.prev_pixel_offset = 0.
+        
+
+        self.x0 = 0
+        self.phases = np.random.random_sample((20,)) * np.pi * 2
+        self.line, = ax.plot([], [], "-", animated=True, lw=2)
+
+        self.point, = ax.plot([], [], "ro", animated=True, lw=2)
+
+        self.ax.set_ylim(-1.1, 1.1)
+
+        self.background1 = None
+
+        cmap = plt.cm.jet
+        from itertools import cycle
+        self.color_cycle = cycle(cmap(np.arange(cmap.N)))
+
+
+    def save_bg(self):
+        self.background1 = 
self.canvas.copy_from_bbox(self.ax.get_figure().bbox)
+
+        self.background2 = self.canvas.copy_from_bbox(self.get_bg_bbox())
+
+
+    def get_dx_data(self, dx_pixel):
+        tp = self.ax.transData.inverted().transform_point
+        x0, y0 = tp((0, 0))
+        x1, y1 = tp((dx_pixel, 0))
+        return (x1-x0)
+
+
+    def restore_background_shifted(self, dx_pixel):
+        """
+        restore bacground shifted by dx in data coordinate. This only
+        works if the data coordinate system is linear.
+        """
+
+        # restore the clean slate background
+        self.canvas.restore_region(self.background1)
+
+        # restore subregion (x1+dx, y1, x2, y2) of the second bg 
+        # in a offset position (x1-dx, y1)
+        x1, y1, x2, y2 = self.background2.get_extents()
+        self.canvas.restore_region(self.background2,
+                                   bbox=(x1+dx_pixel, y1, x2, y2),
+                                   xy=(x1-dx_pixel, y1))
+
+        return dx_pixel
+
+    def on_draw(self, *args):
+        self.save_bg()
+        return False
+    
+    def update_line(self, *args):
+
+        if self.background1 is None:
+            return True
+        
+        cur_time = time.time()
+        pixel_offset = int((cur_time - self.start_time)*100.)
+        dx_pixel = pixel_offset - self.prev_pixel_offset
+        self.prev_pixel_offset = pixel_offset
+        dx_data = self.get_dx_data(dx_pixel) #cur_time - self.prev_time)
+        
+        x0 = self.x0
+        self.x0 += dx_data
+        self.prev_time = cur_time
+
+        self.ax.set_xlim(self.x0-2, self.x0+0.1)
+
+
+        # restore background which will plot lines from previous plots
+        self.restore_background_shifted(dx_pixel) #x0, self.x0)
+        # This restores lines between [x0-2, x0]
+
+
+
+        self.line.set_color(self.color_cycle.next())
+
+        # now plot line segment within [x0, x0+dx_data], 
+        # Note that we're only plotting a line between [x0, x0+dx_data].
+        xx = np.array([x0, self.x0])
+        self.line.set_xdata(xx)
+
+        # the for loop below could be improved by using collection.
+        [(self.line.set_ydata(np.sin(xx+p)),
+          self.ax.draw_artist(self.line)) \
+         for p in self.phases]
+
+        self.background2 = canvas.copy_from_bbox(self.get_bg_bbox())
+
+        self.point.set_xdata([self.x0])
+
+        [(self.point.set_ydata(np.sin([self.x0+p])),
+          self.ax.draw_artist(self.point)) \
+         for p in self.phases]
+
+
+        self.ax.draw_artist(self.ax.xaxis)
+        self.ax.draw_artist(self.ax.yaxis)
+
+        self.canvas.blit(self.ax.get_figure().bbox)
+
+
+        dt = (time.time()-tstart)
+        if dt>15:
+            # print the timing info and quit
+            print 'FPS:' , self.cnt/dt
+            gtk.main_quit()
+            raise SystemExit
+
+        self.cnt += 1
+        return True
+
+
+plt.rcParams["text.usetex"] = False
+fig = plt.figure()
+
+ax = fig.add_subplot(111)
+ax.xaxis.set_animated(True)
+ax.yaxis.set_animated(True)
+canvas = fig.canvas
+
+fig.subplots_adjust(left=0.2, bottom=0.2)
+canvas.draw()
+
+# for profiling
+tstart = time.time()
+
+ul = UpdateLine(canvas, ax)
+gobject.idle_add(ul.update_line)
+
+canvas.mpl_connect('draw_event', ul.on_draw)
+
+plt.show()

Modified: trunk/matplotlib/lib/matplotlib/backends/backend_agg.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/backends/backend_agg.py     2009-05-14 
04:00:38 UTC (rev 7101)
+++ trunk/matplotlib/lib/matplotlib/backends/backend_agg.py     2009-05-14 
06:27:58 UTC (rev 7102)
@@ -33,7 +33,7 @@
 from matplotlib.ft2font import FT2Font, LOAD_FORCE_AUTOHINT
 from matplotlib.mathtext import MathTextParser
 from matplotlib.path import Path
-from matplotlib.transforms import Bbox
+from matplotlib.transforms import Bbox, BboxBase
 
 from _backend_agg import RendererAgg as _RendererAgg
 from matplotlib import _png
@@ -65,7 +65,6 @@
         self.draw_quad_mesh = self._renderer.draw_quad_mesh
         self.draw_image = self._renderer.draw_image
         self.copy_from_bbox = self._renderer.copy_from_bbox
-        self.restore_region = self._renderer.restore_region
         self.tostring_rgba_minimized = self._renderer.tostring_rgba_minimized
         self.mathtext_parser = MathTextParser('Agg')
 
@@ -239,7 +238,39 @@
         # with the Agg backend
         return True
 
+    def restore_region(self, region, bbox=None, xy=None):
+        """
+        restore the saved region. if bbox (instance of BboxBase, or
+        its extents) is given, only the region specified by the bbox
+        will be restored. *xy* (a tuple of two floasts) optionally
+        specify the new position (of the LLC of the originally region,
+        not the LLC of the bbox) that the region will be restored.
 
+        >>> region = renderer.copy_from_bbox()
+        >>> x1, y1, x2, y2 = region.get_extents()
+        >>> renderer.restore_region(region, bbox=(x1+dx, y1, x2, y2),
+                                    xy=(x1-dx, y1))
+        
+        """
+        if bbox is not None or xy is not None:
+            if bbox is None:
+                x1, y1, x2, y2 = region.get_extents()
+            elif isinstance(bbox, BboxBase):
+                x1, y1, x2, y2 = bbox.extents
+            else:
+                x1, y1, x2, y2 = bbox
+
+            if xy is None:
+                ox, oy = x1, y1
+            else:
+                ox, oy = xy
+
+            self._renderer.restore_region2(region, x1, y1, x2, y2, ox, oy)
+
+        else:
+            self._renderer.restore_region(region)
+
+
 def new_figure_manager(num, *args, **kwargs):
     """
     Create a new figure manager instance
@@ -269,9 +300,9 @@
         renderer = self.get_renderer()
         return renderer.copy_from_bbox(bbox)
 
-    def restore_region(self, region):
+    def restore_region(self, region, bbox=None, xy=None):
         renderer = self.get_renderer()
-        return renderer.restore_region(region)
+        return renderer.restore_region(region, bbox, xy)
 
     def draw(self):
         """
@@ -334,3 +365,4 @@
                        renderer.width, renderer.height,
                        filename_or_obj, self.figure.dpi)
         renderer.dpi = original_dpi
+

Modified: trunk/matplotlib/src/_backend_agg.cpp
===================================================================
--- trunk/matplotlib/src/_backend_agg.cpp       2009-05-14 04:00:38 UTC (rev 
7101)
+++ trunk/matplotlib/src/_backend_agg.cpp       2009-05-14 06:27:58 UTC (rev 
7102)
@@ -104,6 +104,18 @@
     return Py::Object();
 }
 
+Py::Object BufferRegion::get_extents(const Py::Tuple &args) {
+    args.verify_length(0);
+
+    Py::Tuple extents(4);
+    extents[0] = Py::Int(rect.x1);
+    extents[1] = Py::Int(rect.y1);
+    extents[2] = Py::Int(rect.x2);
+    extents[3] = Py::Int(rect.y2);
+
+    return extents;
+}
+
 Py::Object BufferRegion::to_string_argb(const Py::Tuple &args) {
   // owned=true to prevent memory leak
   Py_ssize_t length;
@@ -426,6 +438,49 @@
   return Py::Object();
 }
 
+// Restore the part of the saved region with offsets
+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");
+
+  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, &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 +1772,9 @@
   add_varargs_method("set_y", &BufferRegion::set_y,
                     "set_y(y)");
 
+  add_varargs_method("get_extents", &BufferRegion::get_extents,
+                    "get_extents()");
+
   add_varargs_method("to_string", &BufferRegion::to_string,
                     "to_string()");
   add_varargs_method("to_string_argb", &BufferRegion::to_string_argb,
@@ -1759,6 +1817,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"

Modified: trunk/matplotlib/src/_backend_agg.h
===================================================================
--- trunk/matplotlib/src/_backend_agg.h 2009-05-14 04:00:38 UTC (rev 7101)
+++ trunk/matplotlib/src/_backend_agg.h 2009-05-14 06:27:58 UTC (rev 7102)
@@ -87,6 +87,8 @@
   Py::Object set_x(const Py::Tuple &args);
   Py::Object set_y(const Py::Tuple &args);
 
+  Py::Object get_extents(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();
 


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image 
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to