From: <lee...@us...> - 2009-05-14 06:28:11
|
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. |