Revision: 3884
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3884&view=rev
Author:   mdboom
Date:     2007-09-24 09:53:38 -0700 (Mon, 24 Sep 2007)

Log Message:
-----------
More progress.  (Kind of a broken mess at the moment.)

Modified Paths:
--------------
    branches/transforms/lib/matplotlib/axes.py
    branches/transforms/lib/matplotlib/axis.py
    branches/transforms/lib/matplotlib/ticker.py
    branches/transforms/lib/matplotlib/transforms.py

Added Paths:
-----------
    branches/transforms/lib/matplotlib/scale.py

Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py  2007-09-24 15:11:58 UTC (rev 
3883)
+++ branches/transforms/lib/matplotlib/axes.py  2007-09-24 16:53:38 UTC (rev 
3884)
@@ -25,6 +25,7 @@
 from matplotlib import patches as mpatches
 from matplotlib import pbox as mpbox
 from matplotlib import quiver as mquiver
+from matplotlib import scale as mscale
 from matplotlib import table as mtable
 from matplotlib import text as mtext
 from matplotlib import ticker as mticker
@@ -449,7 +450,6 @@
                  **kwargs
                  ):
         """
-
         Build an Axes instance in Figure with
         rect=[left, bottom, width,height in Figure coords
 
@@ -467,8 +467,8 @@
         navigate: True|False
         navigate_mode: the navigation toolbar button status: 'PAN', 'ZOOM', or 
None
         position: [left, bottom, width,height in Figure coords
-        sharex : an Axes instance to share the x-axis with
-        sharey : an Axes instance to share the y-axis with
+        sharex: an Axes instance to share the x-axis with
+        sharey: an Axes instance to share the y-axis with
         title: the title string
         visible: a boolean - whether the axes is visible
         xlabel: the xlabel
@@ -491,7 +491,7 @@
         self.set_adjustable('box')
         self.set_anchor('C')
 
-        # must be set before set_figure
+        # MGDTODO: Check that the axes being shared are scalable
         self._sharex = sharex
         self._sharey = sharey
        if sharex is not None:
@@ -508,7 +508,7 @@
 
         # this call may differ for non-sep axes, eg polar
         self._init_axis()
-
+        
         if axisbg is None: axisbg = rcParams['axes.facecolor']
         self._axisbg = axisbg
         self._frameon = frameon
@@ -545,8 +545,8 @@
         "move this out of __init__ because non-separable axes don't use it"
         self.xaxis = maxis.XAxis(self)
         self.yaxis = maxis.YAxis(self)
+        self._update_transAxisXY()
 
-
     def sharex_foreign(self, axforeign):
         """
         You can share your x-axis view limits with another Axes in the
@@ -627,26 +627,17 @@
         set the dataLim and viewLim BBox attributes and the
         transData and transAxes Transformation attributes
         """
-       self.viewLim = mtransforms.Bbox.unit()
        self.dataLim = mtransforms.Bbox.unit()
-       
+        self.viewLim = mtransforms.Bbox.unit()
         self.transAxes = mtransforms.BboxTransform(
             mtransforms.Bbox.unit(), self.bbox)
-        # self.set_transform(self.transAxes)
-#         self.transData = mtransforms.BboxTransform(
-#             self.viewLim, self.bbox)
-        self.preDataTransform = mtransforms.BboxTransform(
-            self.viewLim, mtransforms.Bbox.unit())
-#        self.dataTransform = mtransforms.TestPolarTransform()
-#         self.dataTransform = mtransforms.blended_transform_factory(
-#             mtransforms.TestLogTransform(),
-#             mtransforms.Affine2D())
-        self.dataTransform = mtransforms.Affine2D()
-        self.transData = self.preDataTransform + self.dataTransform + 
mtransforms.BboxTransform(
-            mtransforms.Bbox.unit(), self.bbox)
-        self.transData.make_graphviz(open("trans.dot", "w"))
+        self.transAxisXY = mtransforms.TransformWrapper()
+        self.transData = self.transAxisXY + self.transAxes
+
+    def _update_transAxisXY(self):
+        self.transAxisXY.set(mtransforms.blended_transform_factory(
+                self.xaxis.get_transform(), self.yaxis.get_transform()))
         
-           
     def get_position(self, original=False):
         'Return the axes rectangle left, bottom, width, height'
         if original:
@@ -1525,11 +1516,9 @@
 
     def get_xscale(self):
         'return the xaxis scale string: log or linear'
-       # MGDTODO
-        # return self.scaled[self.transData.get_funcx().get_type()]
-       return 'log'
+       return self.xaxis.get_scale()
 
-    def set_xscale(self, value, basex = 10, subsx=None):
+    def set_xscale(self, value, **kwargs):
         """
         SET_XSCALE(value, basex=10, subsx=None)
 
@@ -1547,27 +1536,9 @@
 
         ACCEPTS: ['log' | 'linear' ]
         """
-
-        #if subsx is None: subsx = range(2, basex)
-        assert(value.lower() in ('log', 'linear', ))
-        if value == 'log':
-           # MGDTODO
-#             self.xaxis.set_major_locator(mticker.LogLocator(basex))
-#             
self.xaxis.set_major_formatter(mticker.LogFormatterMathtext(basex))
-#             self.xaxis.set_minor_locator(mticker.LogLocator(basex,subsx))
-#             self.transData.get_funcx().set_type(mtrans.LOG10)
-#             minx, maxx = self.get_xlim()
-#             if min(minx, maxx)<=0:
-#                 self.autoscale_view()
-           pass
-        elif value == 'linear':
-            self.xaxis.set_major_locator(mticker.AutoLocator())
-            self.xaxis.set_major_formatter(mticker.ScalarFormatter())
-            self.xaxis.set_minor_locator(mticker.NullLocator())
-            self.xaxis.set_minor_formatter(mticker.NullFormatter())
-            # self.transData.get_funcx().set_type( mtrans.IDENTITY )
-           self.transData.get_funcx().set_type( 0 ) # MGDTODO
-
+        self.xaxis.set_scale(value, **kwargs)
+        self._update_transAxisXY()
+        
     def get_xticks(self):
         'Return the x ticks as a list of locations'
         return self.xaxis.get_ticklocs()
@@ -1655,9 +1626,8 @@
 
     def get_yscale(self):
         'return the yaxis scale string: log or linear'
-        # return self.scaled[self.transData.get_funcy().get_type()]
-       return 'linear'
-
+        return self.yaxis.get_scale()
+        
     def set_yscale(self, value, basey=10, subsy=None):
         """
         SET_YSCALE(value, basey=10, subsy=None)
@@ -1676,29 +1646,9 @@
 
         ACCEPTS: ['log' | 'linear']
         """
-
-        #if subsy is None: subsy = range(2, basey)
-        assert(value.lower() in ('log', 'linear', ))
-
-        if value == 'log':
-           # MGDTODO
-#             self.yaxis.set_major_locator(mticker.LogLocator(basey))
-#             
self.yaxis.set_major_formatter(mticker.LogFormatterMathtext(basey))
-#             self.yaxis.set_minor_locator(mticker.LogLocator(basey,subsy))
-#             self.transData.get_funcy().set_type(mtrans.LOG10)
-#             miny, maxy = self.get_ylim()
-#             if min(miny, maxy)<=0:
-#                 self.autoscale_view()
-           pass
-           
-        elif value == 'linear':
-            self.yaxis.set_major_locator(mticker.AutoLocator())
-            self.yaxis.set_major_formatter(mticker.ScalarFormatter())
-            self.yaxis.set_minor_locator(mticker.NullLocator())
-            self.yaxis.set_minor_formatter(mticker.NullFormatter())
-            # self.transData.get_funcy().set_type( mtrans.IDENTITY ) MGDTODO
-            self.transData.get_funcy().set_type( 0 )
-           
+        self.yaxis.set_scale(value, basey, subsy)
+        self._update_transAxisXY()
+        
     def get_yticks(self):
         'Return the y ticks as a list of locations'
         return self.yaxis.get_ticklocs()

Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py  2007-09-24 15:11:58 UTC (rev 
3883)
+++ branches/transforms/lib/matplotlib/axis.py  2007-09-24 16:53:38 UTC (rev 
3884)
@@ -12,14 +12,15 @@
 from lines import Line2D, TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN
 from matplotlib import rcParams
 from patches import bbox_artist
-from ticker import NullFormatter, FixedFormatter, ScalarFormatter, LogFormatter
+from ticker import NullFormatter, FixedFormatter, ScalarFormatter, 
LogFormatter, LogFormatterMathtext
 from ticker import NullLocator, FixedLocator, LinearLocator, LogLocator, 
AutoLocator
 
 from font_manager import FontProperties
 from text import Text, TextWithDash, _process_text_args
 from transforms import Affine2D, Bbox, blended_transform_factory, 
interval_contains, \
-    interval_contains_open
+    interval_contains_open, IntervalTransform
 from patches import bbox_artist
+from scale import LinearScale, LogScale
 
 import matplotlib.units as units
 #import pdb
@@ -479,7 +480,7 @@
     """
     LABELPAD = 5
     OFFSETTEXTPAD = 3
-
+    
     def __str__(self):
         return str(self.__class__).split('.')[-1] \
             + "(%d,%d)"%self.axes.transAxes.xy_tup((0,0))
@@ -507,9 +508,38 @@
         self.majorTicks = []
         self.minorTicks = []
         self.pickradius = pickradius
-
+        self._transform = LinearScale(self.axes.viewLim, 
self.axis).get_transform()
+        self._scale = 'linear'
+        
         self.cla()
 
+    def get_transform(self):
+        return self._transform
+
+    def get_scale(self):
+        return self._scale
+    
+    def set_scale(self, value, base=10, subs=None):
+        # MGDTODO: Move these settings (ticker etc.) into the scale class 
itself
+        value = value.lower()
+        assert value.lower() in ('log', 'linear')
+        if value == 'linear':
+            self.set_major_locator(AutoLocator())
+            self.set_major_formatter(ScalarFormatter())
+            self.set_minor_locator(NullLocator())
+            self.set_minor_formatter(NullFormatter())
+            self._transform = LinearScale(self.axes.viewLim, 
self.axis).get_transform()
+        elif value == 'log':
+            self.set_major_locator(LogLocator(base))
+            self.set_major_formatter(LogFormatterMathtext(base))
+            self.set_minor_locator(LogLocator(base,subs))
+            # MGDTODO: Pass base along
+            self._transform = LogScale(self.axes.viewLim, 
self.axis).get_transform()
+            miny, maxy = getattr(self.axes.viewLim, 'interval' + self.axis)
+            if min(miny, maxy)<=0:
+                self.axes.autoscale_view()
+        self._scale = value
+                
     def get_children(self):
         children = [self.label]
         children.extend(self.majorTicks)
@@ -595,7 +625,7 @@
 
         for tick, loc, label in zip(minorTicks, minorLocs, minorLabels):
             if tick is None: continue
-            if not interval.contains(loc): continue
+            if not interval_contains(interval, loc): continue
             #if seen.has_key(loc): continue
             tick.update_position(loc)
             tick.set_label1(label)
@@ -952,7 +982,8 @@
 
 class XAxis(Axis):
     __name__ = 'xaxis'
-
+    axis = 'x'
+            
     def contains(self,mouseevent):
         """Test whether the mouse event occured in the x axis.
         """
@@ -1134,7 +1165,8 @@
 
 class YAxis(Axis):
     __name__ = 'yaxis'
-
+    axis = 'y'
+            
     def contains(self,mouseevent):
         """Test whether the mouse event occurred in the y axis.
 

Added: branches/transforms/lib/matplotlib/scale.py
===================================================================
--- branches/transforms/lib/matplotlib/scale.py                         (rev 0)
+++ branches/transforms/lib/matplotlib/scale.py 2007-09-24 16:53:38 UTC (rev 
3884)
@@ -0,0 +1,78 @@
+import numpy as npy
+from numpy import ma
+
+from transforms import Affine1D, IntervalTransform, Transform
+
+class ScaleBase(object):
+    pass
+
+class LinearScale(ScaleBase):
+    def __init__(self, viewLim, direction):
+        direction = 'interval' + direction
+        self._transform = IntervalTransform(viewLim, direction)
+
+    def get_transform(self):
+        return self._transform
+        
+class LogScale(ScaleBase):
+    class LogTransform(Transform):
+        input_dims = 1
+        output_dims = 1
+        def __init__(self, viewLim, direction, base):
+            Transform.__init__(self)
+            self._base = base
+            self._viewLim = viewLim
+            self._direction = direction
+
+        def transform(self, a):
+            a, affine = self.transform_without_affine(a)
+            return affine.transform(a)
+            
+        def transform_without_affine(self, a):
+            # MGDTODO: Support different bases
+            base = self._base
+            marray = ma.masked_where(a <= 0.0, a)
+            marray = npy.log10(marray)
+            minimum, maximum = getattr(self._viewLim, self._direction)
+            minimum, maximum = npy.log10([minimum, maximum])
+            print marray
+            print Affine1D.from_values(maximum - minimum, minimum).inverted()
+            print minimum, maximum
+            return marray, Affine1D.from_values(maximum - minimum, 
minimum).inverted()
+            
+        def inverted(self):
+            return LogScale.InvertedLogTransform(self._viewLim, 
self._direction, self._base)
+
+    class InvertedLogTransform(Transform):
+        input_dims = 1
+        output_dims = 1
+        def __init__(self, viewLim, direction, base):
+            Transform.__init__(self)
+            self._base = base
+            self._viewLim = viewLim
+            self._direction = direction
+
+        def transform(self, a):
+            minimum, maximum = getattr(self._viewLim, self._direction)
+            Affine1D.from_values(maximum - minimum, minimum).transform(a)
+            return ma.power(10.0, a)
+
+        def inverted(self):
+            return LogScale.LogTransform(self._viewLim, self._direction, 
self._base)
+            
+    def __init__(self, viewLim, direction, base=10):
+        direction = 'interval' + direction
+        self._transform = self.LogTransform(viewLim, direction, base)
+
+    def get_transform(self):
+        return self._transform
+
+    
+_scale_mapping = {
+    'linear': LinearScale,
+    'log': LogScale
+    }
+def scale_factory(scale, viewLim, direction):
+    if scale is None:
+        scale = 'linear'
+    return _scale_mapping[scale](viewLim, direction)

Modified: branches/transforms/lib/matplotlib/ticker.py
===================================================================
--- branches/transforms/lib/matplotlib/ticker.py        2007-09-24 15:11:58 UTC 
(rev 3883)
+++ branches/transforms/lib/matplotlib/ticker.py        2007-09-24 16:53:38 UTC 
(rev 3884)
@@ -500,8 +500,6 @@
 
     def __call__(self, x, pos=None):
         'Return the format for tick val x at position pos'
-        self.verify_intervals()
-
         b = self._base
         # only label the decades
         fx = math.log(x)/math.log(b)
@@ -890,10 +888,9 @@
 
     def __call__(self):
         'Return the locations of the ticks'
-        self.verify_intervals()
         b=self._base
 
-        vmin, vmax = self.viewInterval.get_bounds()
+        vmin, vmax = self.axis.get_view_interval()
         vmin = math.log(vmin)/math.log(b)
         vmax = math.log(vmax)/math.log(b)
 
@@ -922,16 +919,16 @@
 
     def autoscale(self):
         'Try to choose the view limits intelligently'
-        self.verify_intervals()
-
-        vmin, vmax = self.dataInterval.get_bounds()
+        vmin, vmax = self.axis.get_view_interval()
         if vmax<vmin:
             vmin, vmax = vmax, vmin
 
-        minpos = self.dataInterval.minpos()
+#         minpos = self.dataInterval.minpos()
 
-        if minpos<=0:
-            raise RuntimeError('No positive data to plot')
+#         if minpos<=0:
+#             raise RuntimeError('No positive data to plot')
+
+        minpos = max(vmin, 0.00001) #MGDTODO
         if vmin<=0:
             vmin = minpos
         if not is_decade(vmin,self._base): vmin = decade_down(vmin,self._base)

Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py    2007-09-24 15:11:58 UTC 
(rev 3883)
+++ branches/transforms/lib/matplotlib/transforms.py    2007-09-24 16:53:38 UTC 
(rev 3884)
@@ -9,7 +9,7 @@
 from numpy.linalg import inv
 from sets import Set
 
-DEBUG = True
+DEBUG = False
 
 # MGDTODO: This creates a ton of cyclical references.  We may want to
 # consider using weak references
@@ -61,8 +61,8 @@
 
     def is_bbox(self):
         return isinstance(self, BboxBase)
+
     
-        
 class BboxBase(TransformNode):
     '''
     This is the read-only part of a bounding-box
@@ -378,11 +378,30 @@
     def is_separable(self):
         return False
 
-    
-class Affine2DBase(Transform):
+
+class TransformWrapper(Transform):
     input_dims = 2
     output_dims = 2
+    
+    def set(self, child):
+        self.child = child
+        self.child._parents.add(self)
+        self.invalidate()
 
+    def transform(self, points):
+        return self.child.transform(points)
+
+    def transform_without_affine(points):
+        return self.child.transform_without_affine(points)
+    
+    def inverted(self):
+        return self.child.inverted()
+
+    def is_separable(self):
+        return self.child.is_separable()
+    
+    
+class AffineBase(Transform):
     def __init__(self):
         Transform.__init__(self)
         self._inverted = None
@@ -400,11 +419,173 @@
 
     [EMAIL PROTECTED]
     def concat(a, b):
-        return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix()))
+        return Affine1D(Affine1D._concat(a.get_matrix(), b.get_matrix()))
     concat = staticmethod(concat)
+
+    def get_matrix(self):
+        raise NotImplementedError()
+
+    def transform_without_affine(self, points):
+        # MGDTODO: Should we copy the points here?  I'd like to avoid it,
+        # if possible
+        return points, self
     
+
+class Affine1DBase(AffineBase):
+    input_dims = 1
+    output_dims = 1
+
+    def __init__(self):
+        AffineBase.__init__(self)
+    
+    def __array__(self, *args, **kwargs):
+       return self.get_matrix()
+       
     def to_values(self):
         mtx = self.get_matrix()
+        return tuple(mtx[0])
+    
+    [EMAIL PROTECTED]
+    def matrix_from_values(a, b):
+        affine = npy.zeros((2, 2), npy.float_)
+        affine[0, :] = (a, b)
+        affine[1, 1] = 1
+        return affine
+    matrix_from_values = staticmethod(matrix_from_values)
+
+    def transform(self, values):
+        """
+        Applies the transformation to an array of values and
+        returns the result.
+        """
+        # 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.
+#         if not isinstance(points, npy.ndarray):
+#             import traceback
+#             print '-' * 60
+#             print 'A non-numpy array was passed in for transformation.  
Please '
+#             print 'correct this.'
+#             print "".join(traceback.format_stack())
+#             print points
+        mtx = self.get_matrix()
+        points = ma.asarray(values, npy.float_)
+        return points * mtx[0,0] + mtx[0,1]
+
+    def is_separable(self):
+        return True
+
+    def inverted(self):
+        if self._inverted is None:
+            mtx = self.get_matrix()
+            self._inverted = Affine1D(inv(mtx))
+        return self._inverted
+    
+
+class Affine1D(Affine1DBase):
+    def __init__(self, matrix = None):
+        """
+        Initialize an Affine transform from a 2x2 numpy float array.
+
+        a b
+        0 1
+        """
+        Affine1DBase.__init__(self)
+        if matrix is None:
+            matrix = npy.identity(2)
+        else:
+           matrix = npy.asarray(matrix, npy.float_)
+            assert matrix.shape == (2, 2)
+        self._mtx = matrix
+
+    def __repr__(self):
+        return "Affine1D(%s)" % repr(self._mtx)
+    __str__ = __repr__
+
+    def __cmp__(self, other):
+        if (isinstance(other, Affine1D) and
+            (self.get_matrix() == other.get_matrix()).all()):
+            return 0
+        return -1
+    
+    [EMAIL PROTECTED]
+    def from_values(a, b):
+        return Affine1D(Affine1D.matrix_from_values(a, b))
+    from_values = staticmethod(from_values)
+
+    def get_matrix(self):
+        return self._mtx
+
+    def set_matrix(self, mtx):
+        self._mtx = mtx
+        self.invalidate()
+
+    def set(self, other):
+        self._mtx = other.get_matrix()
+        self.invalidate()
+    
+    [EMAIL PROTECTED]
+    def identity():
+        return Affine1D(npy.identity(2))
+    identity = staticmethod(identity)
+
+    def clear(self):
+        self._mtx = npy.identity(2)
+        self.invalidate()
+        return self
+    
+    def translate(self, t):
+        self._mtx[0, 1] += t
+        self.invalidate()
+        return self
+
+    def scale(self, s):
+        self._mtx[0, 0] *= s
+        self.invalidate()
+        return self
+
+    def is_separable(self):
+        mtx = self.get_matrix()
+        return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
+
+    
+class IntervalTransform(Affine1DBase):
+    def __init__(self, bbox, direction):
+        Affine1DBase.__init__(self)
+        self._bbox = bbox
+        self._direction = direction
+        self.set_children(['_bbox'])
+        self._mtx = None
+        
+    def __repr__(self):
+        return "IntervalTransform(%s)" % (getattr(self._bbox, self._direction))
+    __str__ = __repr__
+
+    def _do_invalidation(self):
+        print "IntervalTransform.invalidation", self._bbox
+        self._mtx = None
+        Affine1DBase._do_invalidation(self)
+
+    def get_matrix(self):
+        if self._mtx is None:
+            min, max = getattr(self._bbox, self._direction)
+            self._mtx = inv(npy.array([[max - min, min],
+                                       [0.0, 1.0]], npy.float_))
+        return self._mtx
+    
+    
+class Affine2DBase(AffineBase):
+    input_dims = 2
+    output_dims = 2
+
+    def __init__(self):
+        AffineBase.__init__(self)
+
+    def __array__(self, *args, **kwargs):
+       return self.get_matrix()
+    
+    def to_values(self):
+        mtx = self.get_matrix()
         return tuple(mtx[:2].swapaxes(0, 1).flatten())
     
     [EMAIL PROTECTED]
@@ -416,9 +597,6 @@
         return affine
     matrix_from_values = staticmethod(matrix_from_values)
 
-    def get_matrix(self):
-        raise NotImplementedError()
-    
     def transform(self, points):
         """
         Applies the transformation to an array of 2D points and
@@ -444,11 +622,6 @@
         points = points + mtx[0:2, 2:]
         return points.transpose()
 
-    def transform_without_affine(self, points):
-        # MGDTODO: Should we copy the points here?  I'd like to avoid it,
-        # if possible
-        return points, self
-    
     def inverted(self):
         if self._inverted is None:
             mtx = self.get_matrix()
@@ -476,7 +649,6 @@
            matrix = npy.asarray(matrix, npy.float_)
             assert matrix.shape == (3, 3)
         self._mtx = matrix
-        self._inverted = None
 
     def __repr__(self):
         return "Affine2D(%s)" % repr(self._mtx)
@@ -545,12 +717,6 @@
         self.invalidate()
         return self
 
-    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
@@ -602,12 +768,10 @@
     __str__ = __repr__
         
     def transform(self, points):
-        # MGDTODO: Optimize the case where one of these is
-        # an affine
         x = self._x
         y = self._y
         if x == y and x.input_dims == 2:
-            return self._x(points)
+            return self._x.transform(points)
 
         if x.input_dims == 2:
             x_points = x.transform(points)[:, 0:1]
@@ -623,13 +787,69 @@
 
         return ma.concatenate((x_points, y_points), 1)
 
+    def transform_without_affine(self, points):
+        x = self._x
+        y = self._y
+        if x == y and x.input_dims == 2:
+            return self._x.transform_without_affine(points)
+
+        if x.input_dims == 2:
+            x_points, x_affine = x.transform_without_affine(points)
+            x_points = x_points[:, 0:1]
+        else:
+            x_points, x_affine = x.transform_without_affine(points[:, 0])
+            x_points = x_points.reshape((len(x_points), 1))
+            
+        if y.input_dims == 2:
+            y_points, y_affine = y.transform_without_affine(points)
+            y_points = y_points[:, 1:]
+        else:
+            y_points, y_affine = y.transform_without_affine(points[:, 1])
+            y_points = y_points.reshape((len(y_points), 1))
+
+        return ma.concatenate((x_points, y_points), 1), 
blended_transform_factory(x_affine, y_affine)
+    
     def inverted(self):
         return BlendedGenericTransform(self._x.inverted(), self._y.inverted())
     
     def is_separable(self):
         return True
+
+
+class BlendedAffine1D(Affine2DBase, Transform):
+    def __init__(self, x_transform, y_transform):
+        assert isinstance(x_transform, Affine1DBase)
+        assert isinstance(y_transform, Affine1DBase)
+
+        Transform.__init__(self)
+        self._x = x_transform
+        self._y = y_transform
+        self.set_children(['_x', '_y'])
+        
+        Affine2DBase.__init__(self)
+        self._mtx = None
+
+    def __repr__(self):
+        return "BlendedAffine1D(%s,%s)" % (self._x, self._y)
+    __str__ = __repr__
+        
+    def _do_invalidation(self):
+        self._mtx = None
+        Affine2DBase._do_invalidation(self)
+
+    def is_separable(self):
+        return True
+
+    def get_matrix(self):
+        if self._mtx is None:
+            x_mtx = self._x.get_matrix()
+            y_mtx = self._y.get_matrix()
+            self._mtx = npy.array([[x_mtx[0, 0], 0.0, x_mtx[0, 1]],
+                                   [0.0, y_mtx[0, 0], y_mtx[0, 1]],
+                                   [0.0, 0.0, 1.0]])
+        return self._mtx
+
     
-    
 class BlendedAffine2D(Affine2DBase, Transform):
     def __init__(self, x_transform, y_transform):
         assert x_transform.is_affine()
@@ -650,9 +870,8 @@
     __str__ = __repr__
         
     def _do_invalidation(self):
-        if self._mtx is not None:
-            self._mtx = None
-            Affine2DBase._do_invalidation(self)
+        self._mtx = None
+        Affine2DBase._do_invalidation(self)
 
     def is_separable(self):
         return True
@@ -672,8 +891,10 @@
 
     
 def blended_transform_factory(x_transform, y_transform):
-    if x_transform.is_affine() and y_transform.is_affine():
+    if isinstance(x_transform, Affine2DBase) and isinstance(y_transform, 
Affine2DBase):
         return BlendedAffine2D(x_transform, y_transform)
+    elif isinstance(x_transform, Affine1DBase) and isinstance(y_transform, 
Affine1DBase):
+        return BlendedAffine1D(x_transform, y_transform)
     return BlendedGenericTransform(x_transform, y_transform)
 
 
@@ -726,7 +947,7 @@
 
     def _do_invalidation(self):
         self._mtx = None
-        return Affine2DBase._do_invalidation(self)
+        Affine2DBase._do_invalidation(self)
     
     def get_matrix(self):
         if self._mtx is None:
@@ -754,8 +975,8 @@
 class TestLogTransform(Transform):
     input_dims = 1
     output_dims = 1
-    def transform(self, xy):
-        marray = ma.masked_where(xy <= 0.0, xy * 10.0)
+    def transform(self, a):
+        marray = ma.masked_where(a <= 0.0, a * 10.0)
         return (npy.log10(marray) * 0.5) + 0.5
         
     def inverted(self):
@@ -768,8 +989,8 @@
 class TestInvertLogTransform(Transform):
     input_dims = 1
     output_dims = 1
-    def transform(self, xy):
-        return ma.power(10, (xy - 0.5) * 2.0) / 10.0
+    def transform(self, a):
+        return ma.power(10, (a - 0.5) * 2.0) / 10.0
         
     def inverted(self):
         return TestLogTransform()
@@ -782,18 +1003,31 @@
     input_dims = 2
     output_dims = 2
 
+    def __init__(self, limits):
+        assert limits.is_bbox()
+
+        Transform.__init__(self)
+        self._limits = limits
+        self.set_children(['_limits'])
+    
     def transform(self, xy):
         debug = len(xy) > 4
-        x = xy[:, 0:1]
-        y = xy[:, 1:]
-        x, y = ((y * npy.cos(x)) + 1.0) * 0.5, ((y * npy.sin(x)) + 1.0) * 0.5
-        if debug:
-            print npy.min(xy[:, 0:1]), npy.max(xy[:, 0:1]), npy.min(xy[:, 
1:]), npy.max(xy[:, 1:])
-            print x.min(), x.max(), y.min(), y.max()
-        return ma.concatenate((x, y), 1)
+        limmin, limmax = self._limits.intervaly
+        mask = (xy[:, 1:] < limmin) | (xy[:, 1:] > limmax)
+        mask = ma.concatenate((mask, mask), 1)
+        masked_xy = npy.ma.masked_where(mask, xy)
+        x = masked_xy[:, 0:1]
+        y = masked_xy[:, 1:2]
+        if x.shape == () or y.shape == ():
+            return masked_xy
+        y = (y - limmin) / (limmax - limmin)
+        x, y = y * ma.cos(x), y * ma.sin(x)
+        result = ma.concatenate((x, y), 1)
+        result = result * 0.5 + 0.5
+        return result
 
     def inverted(self):
-        return TestInvertPolarTransform()
+        return TestInvertPolarTransform(self._limits)
     
     def is_separable(self):
         return False
@@ -803,16 +1037,26 @@
     input_dims = 2
     output_dims = 2
 
+    def __init__(self, limits):
+        assert limits.is_bbox()
+
+        Transform.__init__(self)
+        self._limits = limits
+        self.set_children(['_limits'])
+    
     def transform(self, xy):
+        limmin, limmax = self._limits.intervaly
+        xy = (xy - 0.5) * 2.0
         x = xy[:, 0:1]
         y = xy[:, 1:]
         r = ma.sqrt(ma.power(x, 2) + ma.power(y, 2))
         theta = ma.arccos(x / r)
         theta = ma.where(y < 0, 2 * npy.pi - theta, theta)
-        return ma.concatenate((theta / (npy.pi * 2), r), 1)
+        r = r * (limmax - limmin) + limmin
+        return ma.concatenate((theta, r), 1)
 
     def inverted(self):
-        return TestInvertPolarTransform()
+        return TestInvertPolarTransform(self._limits)
     
     def is_separable(self):
         return False
@@ -835,9 +1079,8 @@
     __str__ = __repr__
         
     def _do_invalidation(self):
-        if self._mtx is not None:
-            self._mtx = None
-            Affine2DBase._do_invalidation(self)
+        self._mtx = None
+        Affine2DBase._do_invalidation(self)
 
     def is_separable(self):
         return True


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
Matplotlib-checkins@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to