|
From: <md...@us...> - 2007-09-24 16:53:40
|
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 @@
#@staticmethod
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])
+
+ #@staticmethod
+ 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
+
+ #@staticmethod
+ 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()
+
+ #@staticmethod
+ 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())
#@staticmethod
@@ -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.
|