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