Revision: 3842
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3842&view=rev
Author:   mdboom
Date:     2007-09-12 12:47:56 -0700 (Wed, 12 Sep 2007)

Log Message:
-----------
More progress.  Zooming and panning working thanks to John's patch.

Modified Paths:
--------------
    branches/transforms/lib/matplotlib/affine.py
    branches/transforms/lib/matplotlib/artist.py
    branches/transforms/lib/matplotlib/axes.py
    branches/transforms/lib/matplotlib/backend_bases.py
    branches/transforms/lib/matplotlib/backends/backend_agg.py
    branches/transforms/lib/matplotlib/backends/backend_tkagg.py

Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py        2007-09-12 18:22:24 UTC 
(rev 3841)
+++ branches/transforms/lib/matplotlib/affine.py        2007-09-12 19:47:56 UTC 
(rev 3842)
@@ -8,9 +8,16 @@
 from numpy.linalg import inv
 from sets import Set
 
+# MGDTODO: The name of this module is bad, since it deals with
+# non-affine transformations as well.  It should probably just be
+# "transforms", but we already had one of those... ;)
+
 # MGDTODO: This creates a ton of cyclical references.  We may want to
 # consider using weak references
 
+# MGDTODO: deep copying is probably incorrect wrt the parent/child
+# relationships
+
 class TransformNode(object):
     def __init__(self):
        self._parents = Set()
@@ -48,29 +55,31 @@
        points = N.array(args, dtype=N.float_).reshape(2, 2)
        return Bbox(points)
     from_lbrt = staticmethod(from_lbrt)
+    
+    def __copy__(self):
+       return Bbox(self._points.copy())
 
+    def __deepcopy__(self, memo):
+       return Bbox(self._points.copy())
+    
     def __cmp__(self, other):
        # MGDTODO: Totally suboptimal
-       if isinstance(other, Bbox):
-           if (self._points == other._points).all():
-               return 0
+       if isinstance(other, Bbox) and (self._points == other._points).all():
+           return 0
        return -1
-    
+
+    def __repr__(self):
+       return 'Bbox(%s)' % repr(self._points)
+    __str__ = __repr__
+
     # JDH: the update method will update the box limits from the
     # existing limits and the new data; it appears here you are just
     # using the new data.  We use an "ignore" flag to specify whether
     # you want to include the existing data or not in the update
-    def update_from_data(self, x, y):
+    def update_from_data(self, x, y, ignore=True):
        self._points = N.array([[x.min(), y.min()], [x.max(), y.max()]], 
N.float_)
        self.invalidate()
-    
-    def copy(self):
-       return Bbox(self._points.copy())
 
-    def __repr__(self):
-       return 'Bbox(%s)' % repr(self._points)
-    __str__ = __repr__
-
     # MGDTODO: Probably a more efficient ways to do this...
     def _get_xmin(self):
        return self._points[0, 0]
@@ -136,19 +145,24 @@
        return self.ymax - self.ymin
     height = property(_get_height)
 
+    def _get_bounds(self):
+       return (self.xmin, self.ymin,
+               self.xmax - self.xmin, self.ymax - self.ymin)
+    def _set_bounds(self, bounds):
+       l,b,w,h = bounds
+       self._points = N.array([[l, b], [l+w, b+h]], N.float_)
+       self.invalidate()
+    bounds = property(_get_bounds, _set_bounds)
+       
     def transformed(self, transform):
        return Bbox(transform(self._points))
 
     def inverse_transformed(self, transform):
        return Bbox(transform.inverted()(self._points))
     
-    def get_bounds(self):
-       return (self.xmin, self.ymin,
-               self.xmax - self.xmin, self.ymax - self.ymin)
-
     def expanded(self, sw, sh):
-       width = self.width()
-       height = self.height()
+       width = self.width
+       height = self.height
        deltaw = (sw * width - width) / 2.0
        deltah = (sh * height - height) / 2.0
        a = N.array([[-deltaw, -deltah], [deltaw, deltah]])
@@ -199,6 +213,9 @@
        if isinstance(other, Transform):
            return composite_transform_factory(other, self)
        raise TypeError("Can not add Transform to object of type '%s'" % 
type(other))
+
+    def transform_point(self, point):
+       return self.__call__([point])[0]
     
     def has_inverse(self):
        raise NotImplementedError()
@@ -211,49 +228,27 @@
 
     def is_affine(self):
        return False
-       
-class Affine2D(Transform):
+
+# MGDTODO: Separate out Affine2DBase / Affine2DConcrete so BlendedAffine and 
CompositeAffine don't have translate/scale/rotate members
+
+class Affine2DBase(Transform):
     input_dims = 2
     output_dims = 2
-    
-    def __init__(self, matrix = None):
-        """
-        Initialize an Affine transform from a 3x3 numpy float array.
 
-        a c e
-        b d f
-        0 0 1
-        """
+    def __init__(self):
        Transform.__init__(self)
-       if matrix is None:
-           matrix = N.identity(3)
-       else:
-           assert matrix.shape == (3, 3)
-       self._mtx = matrix
        self._inverted = None
 
-    def __repr__(self):
-       return "Affine2D(%s)" % repr(self._mtx)
-    __str__ = __repr__
-
-    def __cmp__(self, other):
-       # MGDTODO: We need to decide if we want deferred transforms
-       # to be equal to this one
-       if isinstance(other, Affine2D):
-           if (self.get_matrix() == other.get_matrix()).all():
-               return 0
-       return -1
-    
     def _do_invalidation(self):
        result = self._inverted is None
        self._inverted = None
        return result
+
+    [EMAIL PROTECTED]
+    def _concat(a, b):
+        return N.dot(b, a)
+    _concat = staticmethod(_concat)
     
-    [EMAIL PROTECTED]
-    def from_values(a, b, c, d, e, f):
-        return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f))
-    from_values = staticmethod(from_values)
-
     def to_values(self):
        mtx = self.get_matrix()
        return tuple(mtx[:2].swapaxes(0, 1).flatten())
@@ -268,7 +263,7 @@
     matrix_from_values = staticmethod(matrix_from_values)
 
     def get_matrix(self):
-       return self._mtx
+       raise NotImplementedError()
     
     def __call__(self, points):
         """
@@ -278,26 +273,74 @@
        points must be a numpy array of shape (N, 2), where N is the
        number of points.
        """
-       # MGDTODO: This involves a copy.  We may need to do something like
-       # 
http://neuroimaging.scipy.org/svn/ni/ni/trunk/neuroimaging/core/reference/mapping.py
-       # to separate the matrix out into the translation and scale components
-       # and apply each separately (which is still sub-optimal)
-
-       # This is easier for now, however, since we can just keep a
-       # regular affine matrix around
-       # MGDTODO: Trap cases where this isn't an array and fix there
+       # MGDTODO: The major speed trap here is just converting to
+       # the points to an array in the first place.  If we can use
+       # more arrays upstream, that should help here.
        mtx = self.get_matrix()
        points = N.asarray(points, N.float_)
-       new_points = points.swapaxes(0, 1)
-       new_points = N.vstack((new_points, N.ones((1, points.shape[0]))))
-       result = N.dot(mtx, new_points)[:2]
-       return result.swapaxes(0, 1)
+       points = points.transpose()
+       points = N.dot(mtx[0:2, 0:2], points)
+       points = points + mtx[0:2, 2:]
+       return points.transpose()
     
+    def inverted(self):
+       if self._inverted is None:
+           mtx = self.get_matrix()
+           self._inverted = Affine2D(inv(mtx))
+       return self._inverted
+    
+    def is_separable(self):
+       mtx = self.get_matrix()
+       return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
+
+    def is_affine(self):
+       return True
+
+       
+class Affine2D(Affine2DBase):
+    input_dims = 2
+    output_dims = 2
+    
+    def __init__(self, matrix = None):
+        """
+        Initialize an Affine transform from a 3x3 numpy float array.
+
+        a c e
+        b d f
+        0 0 1
+        """
+       Affine2DBase.__init__(self)
+       if matrix is None:
+           matrix = N.identity(3)
+       else:
+           assert matrix.shape == (3, 3)
+       self._mtx = matrix
+       self._inverted = None
+
+    def __repr__(self):
+       return "Affine2D(%s)" % repr(self._mtx)
+    __str__ = __repr__
+
+    def __cmp__(self, other):
+       if (isinstance(other, Affine2D) and
+           (self.get_matrix() == other.get_matrix()).all()):
+           return 0
+       return -1
+    
+    def __copy__(self):
+       return Affine2D(self._mtx.copy())
+    
+    def __deepcopy__(self, memo):
+       return Affine2D(self._mtx.copy())
+    
     [EMAIL PROTECTED]
-    def _concat(a, b):
-        return N.dot(b, a)
-    _concat = staticmethod(_concat)
+    def from_values(a, b, c, d, e, f):
+        return Affine2D(Affine2D.matrix_from_values(a, b, c, d, e, f))
+    from_values = staticmethod(from_values)
 
+    def get_matrix(self):
+       return self._mtx
+    
     [EMAIL PROTECTED]
     def concat(a, b):
        return Affine2D(Affine2D._concat(a._mtx, b._mtx))
@@ -346,19 +389,18 @@
     def is_affine(self):
        return True
     
-class BlendedAffine2D(Affine2D):
+class BlendedAffine2D(Affine2DBase):
     def __init__(self, x_transform, y_transform):
        assert x_transform.is_affine()
        assert y_transform.is_affine()
        assert x_transform.is_separable()
        assert y_transform.is_separable()
 
-       Transform.__init__(self)
+       Affine2DBase.__init__(self)
        self.add_children([x_transform, y_transform])
        self._x = x_transform
        self._y = y_transform
        self._mtx = None
-       self._inverted = None
 
     def __repr__(self):
        return "BlendedAffine2D(%s,%s)" % (self._x, self._y)
@@ -367,7 +409,7 @@
     def _do_invalidation(self):
        if self._mtx is not None:
            self._mtx = None
-           Affine2D._do_invalidation(self)
+           Affine2DBase._do_invalidation(self)
            return False
        return True
 
@@ -376,8 +418,9 @@
            x_mtx = self._x.get_matrix()
            y_mtx = self._y.get_matrix()
            # This works because we already know the transforms are
-           # separable
-           self._mtx = N.vstack([x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]])
+           # separable, though normally one would want to set b and
+           # c to zero.
+           self._mtx = N.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0]))
        
     def is_separable(self):
        return True
@@ -397,20 +440,22 @@
        self._y = y_transform
 
     def __call__(self, points):
-       # MGDTODO: Implement me
-       pass
+       x_points = self._x(points)
+       y_points = self._y(points)
+       # This works because we already know the transforms are
+       # separable
+       return N.hstack((x_points[:, 0:1], y_points[:, 1:2]))
 
-class CompositeAffine2D(Affine2D):
+class CompositeAffine2D(Affine2DBase):
     def __init__(self, a, b):
        assert a.is_affine()
        assert b.is_affine()
 
-       Transform.__init__(self)
+       Affine2DBase.__init__(self)
        self.add_children([a, b])
        self._a = a
        self._b = b
        self._mtx = None
-       self._inverted = None
 
     def __repr__(self):
        return "CompositeAffine2D(%s, %s)" % (self._a, self._b)
@@ -418,7 +463,7 @@
 
     def _do_invalidation(self):
        self._mtx = None
-       Affine2D._do_invalidation(self)
+       Affine2DBase._do_invalidation(self)
     
     def _make__mtx(self):
        if self._mtx is None:
@@ -433,22 +478,23 @@
 class CompositeTransform(Transform):
     def __init__(self, a, b):
        assert a.output_dims == b.input_dims
-
+       self.input_dims = a.input_dims
+       self.output_dims = b.output_dims
+       
        Transform.__init__(self)
        self.add_children([a, b])
        self._a = a
        self._b = b
 
     def __call__(self, points):
-       # MGDTODO: Optimize here by concatenating affines if possible
        return self._b(self._a(points))
 
-class BboxTransform(Affine2D):
+class BboxTransform(Affine2DBase):
     def __init__(self, boxin, boxout):
        assert isinstance(boxin, Bbox)
        assert isinstance(boxout, Bbox)
 
-       Transform.__init__(self)
+       Affine2DBase.__init__(self)
        self.add_children([boxin, boxout])
        self._boxin = boxin
        self._boxout = boxout
@@ -462,7 +508,7 @@
     def _do_invalidation(self):
        if self._mtx is not None:
            self._mtx = None
-           Affine2D._do_invalidation(self)
+           Affine2DBase._do_invalidation(self)
            return False
        return True
 
@@ -481,14 +527,6 @@
 
            self._mtx = affine._mtx
        
-    def __call__(self, points):
-       self._make__mtx()
-       return Affine2D.__call__(self, points)
-
-    def inverted(self):
-       self._make__mtx()
-       return Affine2D.inverted(self)
-
     def is_separable(self):
        return True
 
@@ -541,6 +579,9 @@
     return interval[0] < val and interval[1] > val
     
 if __name__ == '__main__':
+    from random import random
+    import timeit
+
     bbox = Bbox.from_lbrt(10., 15., 20., 25.)
     assert bbox.xmin == 10
     assert bbox.ymin == 15
@@ -589,7 +630,9 @@
     scale = Affine2D().scale(10, 20)
     assert scale.to_values() == (10, 0, 0, 20, 0, 0)
     rotation = Affine2D().rotate_deg(30)
-    print rotation.to_values() == (0.86602540378443871, 0.49999999999999994, 
-0.49999999999999994, 0.86602540378443871, 0.0, 0.0)
+    print rotation.to_values() == (0.86602540378443871, 0.49999999999999994,
+                                  -0.49999999999999994, 0.86602540378443871,
+                                  0.0, 0.0)
     
     points = N.array([[1,2],[3,4],[5,6],[7,8]], N.float_)
     translated_points = translation(points)
@@ -600,11 +643,20 @@
     print rotated_points
 
     tpoints1 = rotation(translation(scale(points)))
-    trans_sum = rotation + translation + scale
+    trans_sum = scale + translation + rotation
     tpoints2 = trans_sum(points)
     print tpoints1, tpoints2
     print tpoints1 == tpoints2
     # Need to do some sort of fuzzy comparison here?
     # assert (tpoints1 == tpoints2).all()
+
+    # Here are some timing tests
+    points = [(random(), random()) for i in xrange(10000)]
+    t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, 
points")
+    print "Time to transform 10000 x 10 points as tuples:", t.timeit(10)
+
+    points2 = N.asarray(points)
+    t = timeit.Timer("trans_sum(points2)", "from __main__ import trans_sum, 
points2")
+    print "Time to transform 10000 x 10 points as numpy array:", t.timeit(10)
     
 __all__ = ['Transform', 'Affine2D']

Modified: branches/transforms/lib/matplotlib/artist.py
===================================================================
--- branches/transforms/lib/matplotlib/artist.py        2007-09-12 18:22:24 UTC 
(rev 3841)
+++ branches/transforms/lib/matplotlib/artist.py        2007-09-12 19:47:56 UTC 
(rev 3842)
@@ -337,7 +337,7 @@
     def _set_gc_clip(self, gc):
         'set the clip properly for the gc'
         if self.clipbox is not None:
-            gc.set_clip_rectangle(self.clipbox.get_bounds())
+            gc.set_clip_rectangle(self.clipbox.bounds)
         gc.set_clip_path(self._clippath)
 
     def draw(self, renderer, *args, **kwargs):

Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py  2007-09-12 18:22:24 UTC (rev 
3841)
+++ branches/transforms/lib/matplotlib/axes.py  2007-09-12 19:47:56 UTC (rev 
3842)
@@ -1,5 +1,5 @@
 from __future__ import division, generators
-import math, sys, warnings
+import math, sys, warnings, copy
 
 import numpy as npy
 
@@ -483,7 +483,7 @@
         """
         martist.Artist.__init__(self)
         self._position = maffine.Bbox.from_lbwh(*rect)
-        self._originalPosition = self._position.copy()
+        self._originalPosition = copy.deepcopy(self._position)
         self.set_axes(self)
         self.set_aspect('auto')
         self.set_adjustable('box')
@@ -613,7 +613,7 @@
         """
         martist.Artist.set_figure(self, fig)
 
-        l, b, w, h = self._position.get_bounds()
+        l, b, w, h = self._position.bounds
         xmin = fig.bbox.xmin
         xmax = fig.bbox.xmax
         ymin = fig.bbox.ymin
@@ -669,9 +669,9 @@
     def get_position(self, original=False):
         'Return the axes rectangle left, bottom, width, height'
         if original:
-            return self._originalPosition[:]
+            return self._originalPosition.bounds
         else:
-            return self._position[:]
+            return self._position.bounds
            # return [val.get() for val in self._position]
 
     def set_position(self, pos, which='both'):
@@ -694,10 +694,10 @@
 #             # Change values within self._position--don't replace it.
 #             for num,val in zip(pos, self._position):
 #                 val.set(num)
-           self._position = pos
+           self._position.bounds = pos.bounds
            # MGDTODO: side-effects
         if which in ('both', 'original'):
-            self._originalPosition = pos
+            self._originalPosition.bounds = pos.bounds
            
            
     def _set_artist_props(self, a):
@@ -1547,7 +1547,9 @@
 
     def get_xscale(self):
         'return the xaxis scale string: log or linear'
-        return self.scaled[self.transData.get_funcx().get_type()]
+       # MGDTODO
+        # return self.scaled[self.transData.get_funcx().get_type()]
+       return 'linear'
 
     def set_xscale(self, value, basex = 10, subsx=None):
         """
@@ -1671,7 +1673,8 @@
 
     def get_yscale(self):
         'return the yaxis scale string: log or linear'
-        return self.scaled[self.transData.get_funcy().get_type()]
+        # return self.scaled[self.transData.get_funcy().get_type()]
+       return 'linear'
 
     def set_yscale(self, value, basey=10, subsy=None):
         """

Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-12 18:22:24 UTC 
(rev 3841)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-12 19:47:56 UTC 
(rev 3842)
@@ -4,7 +4,7 @@
 """
 
 from __future__ import division
-import os, sys, warnings
+import os, sys, warnings, copy
 
 import numpy as npy
 import matplotlib.numerix.npyma as ma
@@ -1070,7 +1070,7 @@
     def get_width_height(self):
         """return the figure width and height in points or pixels
         (depending on the backend), truncated to integers"""
-        return int(self.figure.bbox.width()), int(self.figure.bbox.height())
+        return int(self.figure.bbox.width), int(self.figure.bbox.height)
 
     filetypes = {
         'emf': 'Enhanced Metafile',
@@ -1544,7 +1544,7 @@
                 xmin, xmax = a.get_xlim()
                 ymin, ymax = a.get_ylim()
                 lim = xmin, xmax, ymin, ymax
-                self._xypress.append((x, y, a, i, lim,a.transData.deepcopy()))
+                self._xypress.append((x, y, a, i, lim, 
copy.deepcopy(a.transData)))
                 self.canvas.mpl_disconnect(self._idDrag)
                 self._idDrag=self.canvas.mpl_connect('motion_notify_event', 
self.drag_pan)
 
@@ -1571,7 +1571,7 @@
                 xmin, xmax = a.get_xlim()
                 ymin, ymax = a.get_ylim()
                 lim = xmin, xmax, ymin, ymax
-                self._xypress.append(( x, y, a, i, lim, a.transData.deepcopy() 
))
+                self._xypress.append(( x, y, a, i, lim, 
copy.deepcopy(a.transData) ))
 
         self.press(event)
 
@@ -1637,8 +1637,9 @@
             #safer to use the recorded button at the press than current button:
             #multiple button can get pressed during motion...
             if self._button_pressed==1:
-                lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )
-                x, y = trans.inverse_xy_tup( (event.x, event.y) )
+               inverse = trans.inverted()
+                lastx, lasty = inverse.transform_point((lastx, lasty))
+                x, y = inverse.transform_point( (event.x, event.y) )
                 if a.get_xscale()=='log':
                     dx=1-lastx/x
                 else:
@@ -1664,15 +1665,16 @@
                     ymax -= dy
             elif self._button_pressed==3:
                 try:
-                    dx=(lastx-event.x)/float(a.bbox.width())
-                    dy=(lasty-event.y)/float(a.bbox.height())
+                    dx=(lastx-event.x)/float(a.bbox.width)
+                    dy=(lasty-event.y)/float(a.bbox.height)
                     dx,dy=format_deltas(event,dx,dy)
                     if a.get_aspect() != 'auto':
                         dx = 0.5*(dx + dy)
                         dy = dx
                     alphax = pow(10.0,dx)
                     alphay = pow(10.0,dy)#use logscaling, avoid singularities 
and smother scaling...
-                    lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )
+                   inverse = trans.inverted()
+                    lastx, lasty = inverse.transform_point( (lastx, lasty) )
                     if a.get_xscale()=='log':
                         xmin = lastx*(xmin/lastx)**alphax
                         xmax = lastx*(xmax/lastx)**alphax
@@ -1710,8 +1712,9 @@
             xmin, ymin, xmax, ymax = lim
 
             # zoom to rect
-            lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) )
-            x, y = a.transData.inverse_xy_tup( (x, y) )
+           inverse = a.transData.inverted()
+            lastx, lasty = inverse.transform_point( (lastx, lasty) )
+            x, y = inverse.transform_point( (x, y) )
             Xmin,Xmax=a.get_xlim()
             Ymin,Ymax=a.get_ylim()
 

Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py  2007-09-12 
18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py  2007-09-12 
19:47:56 UTC (rev 3842)
@@ -398,7 +398,7 @@
         self.figure.draw(self.renderer)
 
     def get_renderer(self):
-        l,b,w,h = self.figure.bbox.get_bounds()
+        l,b,w,h = self.figure.bbox.bounds
        # MGDTODO
         # key = w, h, self.figure.dpi.get()
         key = w, h, self.figure.dpi

Modified: branches/transforms/lib/matplotlib/backends/backend_tkagg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_tkagg.py        
2007-09-12 18:22:24 UTC (rev 3841)
+++ branches/transforms/lib/matplotlib/backends/backend_tkagg.py        
2007-09-12 19:47:56 UTC (rev 3842)
@@ -147,7 +147,7 @@
     def __init__(self, figure, master=None, resize_callback=None):
         FigureCanvasAgg.__init__(self, figure)
         self._idle = True
-        t1,t2,w,h = self.figure.bbox.get_bounds()
+        t1,t2,w,h = self.figure.bbox.bounds
         w, h = int(w), int(h)
         self._tkcanvas = Tk.Canvas(
             master=master, width=w, height=h, borderwidth=4)
@@ -288,7 +288,7 @@
         self.window.wm_title("Figure %d" % num)
         self.canvas = canvas
         self._num =  num
-        t1,t2,w,h = canvas.figure.bbox.get_bounds()
+        t1,t2,w,h = canvas.figure.bbox.bounds
         w, h = int(w), int(h)
         self.window.minsize(int(w*3/4),int(h*3/4))
         if matplotlib.rcParams['toolbar']=='classic':
@@ -436,7 +436,7 @@
         self.canvas = canvas
         self.window = window
 
-        xmin, xmax = canvas.figure.bbox.intervalx().get_bounds()
+        xmin, xmax = canvas.figure.bbox.intervalx
         height, width = 50, xmax-xmin
         Tk.Frame.__init__(self, master=self.window,
                           width=width, height=height,
@@ -582,7 +582,7 @@
         self.message.set(s)
 
     def draw_rubberband(self, event, x0, y0, x1, y1):
-        height = self.canvas.figure.bbox.height()
+        height = self.canvas.figure.bbox.height
         y0 =  height-y0
         y1 =  height-y1
         try: self.lastrect


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

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to