From: <md...@us...> - 2007-10-01 11:45:06
|
Revision: 3905 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3905&view=rev Author: mdboom Date: 2007-10-01 04:44:54 -0700 (Mon, 01 Oct 2007) Log Message: ----------- Move ticking/formatting defaults to scale.py. Speed improvements in transforms.py Modified Paths: -------------- branches/transforms/lib/matplotlib/axes.py branches/transforms/lib/matplotlib/axis.py branches/transforms/lib/matplotlib/backend_bases.py branches/transforms/lib/matplotlib/backends/backend_agg.py branches/transforms/lib/matplotlib/cbook.py branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/scale.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/axes.py =================================================================== --- branches/transforms/lib/matplotlib/axes.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/axes.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -23,6 +23,7 @@ from matplotlib import mlab from matplotlib import cm from matplotlib import patches as mpatches +from matplotlib import path as mpath from matplotlib import pbox as mpbox from matplotlib import quiver as mquiver from matplotlib import scale as mscale @@ -1461,7 +1462,7 @@ self.axesPatch.set_facecolor(color) ### data limits, ticks, tick labels, and formatting - + def get_xlim(self): 'Get the x axis range [xmin, xmax]' return self.viewLim.intervalx @@ -1503,11 +1504,6 @@ if xmin is None: xmin = old_xmin if xmax is None: xmax = old_xmax - # MGDTODO -# if (self.transData.get_funcx().get_type()==mtrans.LOG10 -# and min(xmin, xmax)<=0): -# raise ValueError('Cannot set nonpositive limits with log transform') - xmin, xmax = mtransforms.nonsingular(xmin, xmax, increasing=False) self.viewLim.intervalx = (xmin, xmax) @@ -1516,7 +1512,7 @@ # Call all of the other x-axes that are shared with this one for other in self._shared_x_axes.get_siblings(self): if other is not self: - other.set_xlim(self.viewLim.xmin, self.viewLim.xmax, emit=False) + other.set_xlim(self.viewLim.intervalx, emit=False) return xmin, xmax @@ -1634,7 +1630,7 @@ 'return the yaxis scale string: log or linear' return self.yaxis.get_scale() - def set_yscale(self, value, basey=10, subsy=None): + def set_yscale(self, value, **kwargs): """ SET_YSCALE(value, basey=10, subsy=None) @@ -1652,7 +1648,7 @@ ACCEPTS: ['log' | 'linear'] """ - self.yaxis.set_scale(value, basey, subsy) + self.yaxis.set_scale(value, **kwargs) self._update_transScale() def get_yticks(self): @@ -1788,6 +1784,32 @@ """ self._navigate_mode = b + def drag_pan(self, button, x, y, startx, starty, start_lim, start_trans): + if button == 1: + inverse = start_trans.inverted() + dx = startx - x + dy = starty - y + result = self.bbox.frozen().translated(dx, dy).transformed(inverse) + elif button == 3: + try: + inverse = start_trans.inverted() + dx = (startx - x) / float(self.bbox.width) + dy = (starty - y) / float(self.bbox.height) + xmin, ymin, xmax, ymax = start_lim.lbrt + + alpha = npy.power(10.0, (dx, dy)) + start = inverse.transform_point((startx, starty)) + lim_points = start_lim.get_points() + result = start + alpha * (lim_points - start) + result = mtransforms.Bbox(result) + except OverflowError: + warnings.warn('Overflow while panning') + return + + # MGDTODO: Could we do this with a single set_lim? + self.set_xlim(*result.intervalx) + self.set_ylim(*result.intervaly) + def get_cursor_props(self): """return the cursor props as a linewidth, color tuple where linewidth is a float and color is an RGBA tuple""" @@ -5658,8 +5680,253 @@ self, fig, [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs) +###################################################################### +# New Polar Axes + +class PolarAxes(Axes): + class PolarTransform(mtransforms.Transform): + input_dims = 2 + output_dims = 2 + is_separable = False + def transform(self, tr): + xy = npy.zeros(tr.shape, npy.float_) + t = tr[:, 0:1] + r = tr[:, 1:2] + x = xy[:, 0:1] + y = xy[:, 1:2] + x += r * npy.cos(t) + y += r * npy.sin(t) + return xy + transform_non_affine = transform + def interpolate(self, a, steps): + steps = npy.floor(steps) + new_length = ((len(a) - 1) * steps) + 1 + new_shape = list(a.shape) + new_shape[0] = new_length + result = npy.zeros(new_shape, a.dtype) + + result[0] = a[0] + a0 = a[0:-1] + a1 = a[1: ] + delta = ((a1 - a0) / steps) + + for i in range(1, int(steps)+1): + result[i::steps] = delta * i + a0 + + return result + +# def transform_path(self, path): +# twopi = 2.0 * npy.pi +# halfpi = 0.5 * npy.pi + +# vertices = path.vertices +# t0 = vertices[0:-1, 0] +# t1 = vertices[1: , 0] +# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1)) +# maxtd = td.max() +# interpolate = npy.ceil(maxtd / halfpi) +# if interpolate > 1.0: +# vertices = self.interpolate(vertices, interpolate) + +# vertices = self.transform(vertices) + +# result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_) +# codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type) +# result[0] = vertices[0] +# codes[0] = mpath.Path.MOVETO + +# kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0) +# kappa = 0.5 + +# p0 = vertices[0:-1] +# p1 = vertices[1: ] + +# x0 = p0[:, 0:1] +# y0 = p0[:, 1: ] +# b0 = ((y0 - x0) - y0) / ((x0 + y0) - x0) +# a0 = y0 - b0*x0 + +# x1 = p1[:, 0:1] +# y1 = p1[:, 1: ] +# b1 = ((y1 - x1) - y1) / ((x1 + y1) - x1) +# a1 = y1 - b1*x1 + +# x = -(a0-a1) / (b0-b1) +# y = a0 + b0*x + +# xk = (x - x0) * kappa + x0 +# yk = (y - y0) * kappa + y0 + +# result[1::3, 0:1] = xk +# result[1::3, 1: ] = yk + +# xk = (x - x1) * kappa + x1 +# yk = (y - y1) * kappa + y1 + +# result[2::3, 0:1] = xk +# result[2::3, 1: ] = yk + +# result[3::3] = p1 + +# print vertices[-2:] +# print result[-2:] + +# return mpath.Path(result, codes) + +# twopi = 2.0 * npy.pi +# halfpi = 0.5 * npy.pi + +# vertices = path.vertices +# t0 = vertices[0:-1, 0] +# t1 = vertices[1: , 0] +# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1)) +# maxtd = td.max() +# interpolate = npy.ceil(maxtd / halfpi) + +# print "interpolate", interpolate +# if interpolate > 1.0: +# vertices = self.interpolate(vertices, interpolate) + +# result = npy.zeros((len(vertices) * 3 - 2, 2), npy.float_) +# codes = mpath.Path.CURVE4 * npy.ones((len(vertices) * 3 - 2, ), mpath.Path.code_type) +# result[0] = vertices[0] +# codes[0] = mpath.Path.MOVETO + +# kappa = 4.0 * ((npy.sqrt(2.0) - 1.0) / 3.0) +# tkappa = npy.arctan(kappa) +# hyp_kappa = npy.sqrt(kappa*kappa + 1.0) + +# t0 = vertices[0:-1, 0] +# t1 = vertices[1: , 0] +# r0 = vertices[0:-1, 1] +# r1 = vertices[1: , 1] + +# td = npy.where(t1 > t0, t1 - t0, twopi - (t0 - t1)) +# td_scaled = td / (npy.pi * 0.5) +# rd = r1 - r0 +# r0kappa = r0 * kappa * td_scaled +# r1kappa = r1 * kappa * td_scaled +# ravg_kappa = ((r1 + r0) / 2.0) * kappa * td_scaled + +# result[1::3, 0] = t0 + (tkappa * td_scaled) +# result[1::3, 1] = r0*hyp_kappa +# # result[1::3, 1] = r0 / npy.cos(tkappa * td_scaled) # npy.sqrt(r0*r0 + ravg_kappa*ravg_kappa) + +# result[2::3, 0] = t1 - (tkappa * td_scaled) +# result[2::3, 1] = r1*hyp_kappa +# # result[2::3, 1] = r1 / npy.cos(tkappa * td_scaled) # npy.sqrt(r1*r1 + ravg_kappa*ravg_kappa) + +# result[3::3, 0] = t1 +# result[3::3, 1] = r1 + +# print vertices[:6], result[:6], t0[:6], t1[:6], td[:6], td_scaled[:6], tkappa +# result = self.transform(result) +# return mpath.Path(result, codes) +# transform_path_non_affine = transform_path + + def inverted(self): + return PolarAxes.InvertedPolarTransform() + + class PolarAffine(mtransforms.Affine2DBase): + def __init__(self, limits): + mtransforms.Affine2DBase.__init__(self) + self._limits = limits + self.set_children(limits) + self._mtx = None + + def get_matrix(self): + if self._invalid: + xmin, ymin, xmax, ymax = self._limits.lbrt + affine = mtransforms.Affine2D().rotate(xmin).scale(0.5 / ymax).translate(0.5, 0.5) + self._mtx = affine.get_matrix() + self._inverted = None + self._invalid = 0 + return self._mtx + + class InvertedPolarTransform(mtransforms.Transform): + input_dims = 2 + output_dims = 2 + is_separable = False + + def transform(self, xy): + x = xy[:, 0:1] + y = xy[:, 1:] + r = npy.sqrt(x*x + y*y) + theta = npy.arccos(x / r) + theta = npy.where(y < 0, 2 * npy.pi - theta, theta) + return npy.concatenate((theta, r), 1) + + def inverted(self): + return PolarAxes.PolarTransform() + + def _set_lim_and_transforms(self): + """ + set the dataLim and viewLim BBox attributes and the + transData and transAxes Transformation attributes + """ + self.dataLim = mtransforms.Bbox.unit() + self.viewLim = mtransforms.Bbox.unit() + self.transAxes = mtransforms.BboxTransform( + mtransforms.Bbox.unit(), self.bbox) + + # Transforms the x and y axis separately by a scale factor + # It is assumed that this part will have non-linear components + self.transScale = mtransforms.TransformWrapper(mtransforms.IdentityTransform()) + + # A (possibly non-linear) projection on the (already scaled) data + self.transProjection = self.PolarTransform() + + # An affine transformation on the data, generally to limit the + # range of the axes + self.transProjectionAffine = self.PolarAffine(self.viewLim) + + self.transData = self.transScale + self.transProjection + \ + self.transProjectionAffine + self.transAxes + + def drag_pan(self, button, x, y, startx, starty, start_lim, start_trans): + if button == 1: + inverse = start_trans.inverted() + startt, startr = inverse.transform_point((startx, starty)) + t, r = inverse.transform_point((x, y)) + + scale = r / startr + self.set_ylim(start_lim.ymin, start_lim.ymax / scale) + + dt0 = t - startt + dt1 = startt - t + if abs(dt1) < abs(dt0): + dt = abs(dt1) * sign(dt0) * -1.0 + else: + dt = dt0 * -1.0 + self.set_xlim(start_lim.xmin - dt, start_lim.xmin - dt + npy.pi*2.0) + + def set_rmax(self, rmax): + self.viewLim.maxy = rmax + +class PolarSubplot(SubplotBase, PolarAxes): + """ + Create a polar subplot with + + PolarSubplot(numRows, numCols, plotNum) + + where plotNum=1 is the first plot number and increasing plotNums + fill rows first. max(plotNum)==numRows*numCols + + You can leave out the commas if numRows<=numCols<=plotNum<10, as + in + + Subplot(211) # 2 rows, 1 column, first (upper) plot + """ + def __str__(self): + return "PolarSubplot(%gx%g)"%(self.figW,self.figH) + def __init__(self, fig, *args, **kwargs): + SubplotBase.__init__(self, fig, *args) + PolarAxes.__init__( + self, fig, + [self.figLeft, self.figBottom, self.figW, self.figH], **kwargs) + martist.kwdocd['Axes'] = martist.kwdocd['Subplot'] = martist.kwdoc(Axes) """ # this is some discarded code I was using to find the minimum positive Modified: branches/transforms/lib/matplotlib/axis.py =================================================================== --- branches/transforms/lib/matplotlib/axis.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/axis.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -20,7 +20,7 @@ from transforms import Affine2D, Bbox, blended_transform_factory, interval_contains, \ interval_contains_open, IntervalTransform from patches import bbox_artist -from scale import LinearScale, LogScale +from scale import scale_factory import matplotlib.units as units #import pdb @@ -514,41 +514,19 @@ self.majorTicks = [] self.minorTicks = [] self.pickradius = pickradius - self._scale = LinearScale() self.cla() - + self.set_scale('linear') + def get_transform(self): return self._scale.get_transform() - + def get_scale(self): return self._scale.name - def set_scale(self, value, basex=10, subsx=None, basey=10, subsy=None): - if self.axis_name == 'x': - base = basex - subs = subsx - else: - base = basey - subs = subsy - # 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._scale = LinearScale() - 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._scale = LogScale() - miny, maxy = getattr(self.axes.viewLim, 'interval' + self.axis_name) - if min(miny, maxy)<=0: - self.axes.autoscale_view() + def set_scale(self, value, **kwargs): + self._scale = scale_factory(value, self, **kwargs) + self._scale.set_default_locators_and_formatters(self) def get_children(self): children = [self.label] Modified: branches/transforms/lib/matplotlib/backend_bases.py =================================================================== --- branches/transforms/lib/matplotlib/backend_bases.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/backend_bases.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -1483,7 +1483,7 @@ self._lastCursor = cursors.SELECT_REGION if self._xypress: x, y = event.x, event.y - lastx, lasty, a, ind, lim, trans= self._xypress[0] + lastx, lasty, a, ind, lim, trans = self._xypress[0] self.draw_rubberband(event, x, y, lastx, lasty) elif (self._active=='PAN' and self._lastCursor != cursors.MOVE): @@ -1558,10 +1558,7 @@ self._xypress=[] for i, a in enumerate(self.canvas.figure.get_axes()): if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate(): - xmin, xmax = a.get_xlim() - ymin, ymax = a.get_ylim() - lim = xmin, xmax, ymin, ymax - self._xypress.append((x, y, a, i, lim, copy.deepcopy(a.transData))) + self._xypress.append((x, y, a, i, a.viewLim.frozen(), a.transData.frozen())) self.canvas.mpl_disconnect(self._idDrag) self._idDrag=self.canvas.mpl_connect('motion_notify_event', self.drag_pan) @@ -1585,10 +1582,7 @@ self._xypress=[] for i, a in enumerate(self.canvas.figure.get_axes()): if x is not None and y is not None and a.in_axes(x, y) and a.get_navigate(): - xmin, xmax = a.get_xlim() - ymin, ymax = a.get_ylim() - lim = xmin, xmax, ymin, ymax - self._xypress.append(( x, y, a, i, lim, copy.deepcopy(a.transData) )) + self._xypress.append(( x, y, a, i, a.viewLim.frozen(), a.transData.frozen())) self.press(event) @@ -1648,38 +1642,12 @@ dx=dx/abs(dx)*abs(dy) return (dx,dy) - for cur_xypress in self._xypress: - lastx, lasty, a, ind, lim, trans = cur_xypress - xmin, xmax, ymin, ymax = lim + for lastx, lasty, a, ind, old_lim, old_trans in self._xypress: #safer to use the recorded button at the press than current button: #multiple button can get pressed during motion... - if self._button_pressed==1: - inverse = trans.inverted() - dx, dy = event.x - lastx, event.y - lasty - dx, dy = format_deltas(event, dx, dy) - delta = npy.array([[dx, dy], [dx, dy]], npy.float_) - bbox = transforms.Bbox(a.bbox.get_points() - delta) - result = bbox.transformed(inverse) - elif self._button_pressed==3: - try: - inverse = trans.inverted() - dx=(lastx-event.x)/float(a.bbox.width) - dy=(lasty-event.y)/float(a.bbox.height) - alphax = pow(10.0, dx) - alphay = pow(10.0, dy) - # MGDTODO: Make better use of numpy - lastx, lasty = inverse.transform_point((lastx, lasty)) - xmin = (lastx + alphax * (xmin - lastx)) - xmax = (lastx + alphax * (xmax - lastx)) - ymin = (lasty + alphay * (ymin - lasty)) - ymax = (lasty + alphay * (ymax - lasty)) - result = transforms.Bbox.from_lbrt(xmin, ymin, xmax, ymax) - except OverflowError: - warnings.warn('Overflow while panning') - return - a.set_xlim(*result.intervalx) - a.set_ylim(*result.intervaly) - + dx, dy = event.x - lastx, event.y - lasty + dx, dy = format_deltas(event, dx, dy) + a.drag_pan(self._button_pressed, lastx + dx, lasty + dy, lastx, lasty, old_lim, old_trans) self.dynamic_update() def release_zoom(self, event): Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py =================================================================== --- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -130,16 +130,16 @@ if __debug__: verbose.report('RendererAgg.__init__ done', 'debug-annoying') - # MGDTODO: This is a hack for now to allow for arbitrary transformations + # MGDTODO: Just adding helpful asserts. This can be removed in the future def draw_path(self, gc, path, trans, rgbFace=None): - assert trans.is_affine() - self._renderer.draw_path(gc, path, trans, rgbFace) + assert trans.is_affine + self._renderer.draw_path(gc, path, trans.frozen(), rgbFace) - # MGDTODO: This is a hack for now to allow for arbitrary transformations + # MGDTODO: Just adding helpful asserts. This can be removed in the future def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - assert marker_trans.is_affine() - assert trans.is_affine() - self._renderer.draw_markers(gc, marker_path, marker_trans, path, trans, rgbFace) + assert marker_trans.is_affine + assert trans.is_affine + self._renderer.draw_markers(gc, marker_path, marker_trans.frozen(), path, trans.frozen(), rgbFace) def draw_mathtext(self, gc, x, y, s, prop, angle): """ Modified: branches/transforms/lib/matplotlib/cbook.py =================================================================== --- branches/transforms/lib/matplotlib/cbook.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/cbook.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -7,10 +7,11 @@ import time, datetime import numpy as npy -try: set +try: + set = set except NameError: from sets import Set as set - + major, minor1, minor2, s, tmp = sys.version_info Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/lines.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -496,7 +496,7 @@ funcname = self._lineStyles.get(self._linestyle, '_draw_nothing') lineFunc = getattr(self, funcname) - lineFunc(renderer, gc, *self._transformed_path.get_path_and_affine()) + lineFunc(renderer, gc, *self._transformed_path.get_transformed_path_and_affine()) # MGDTODO: Deal with markers if self._marker is not None: @@ -507,7 +507,7 @@ gc.set_alpha(self._alpha) funcname = self._markers.get(self._marker, '_draw_nothing') markerFunc = getattr(self, funcname) - markerFunc(renderer, gc, *self._transformed_path.get_path_and_affine()) + markerFunc(renderer, gc, *self._transformed_path.get_transformed_path_and_affine()) #renderer.close_group('line2d') Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/path.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -1,6 +1,8 @@ import numpy as npy from numpy import ma as ma +KAPPA = 4.0 * (npy.sqrt(2) - 1) / 3.0 + class Path(object): # Path codes STOP = 0 # 1 vertex @@ -122,7 +124,7 @@ def unit_circle(cls): # MGDTODO: Optimize? if cls._unit_circle is None: - offset = 4.0 * (npy.sqrt(2) - 1) / 3.0 + offset = KAPPA vertices = npy.array( [[-1.0, 0.0], Modified: branches/transforms/lib/matplotlib/scale.py =================================================================== --- branches/transforms/lib/matplotlib/scale.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/scale.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -2,25 +2,38 @@ from numpy import ma from numpy.linalg import inv +from ticker import NullFormatter, FixedFormatter, ScalarFormatter, \ + LogFormatter, LogFormatterMathtext +from ticker import NullLocator, FixedLocator, LinearLocator, LogLocator, AutoLocator from transforms import Affine1DBase, IntervalTransform, Transform, \ composite_transform_factory, IdentityTransform class ScaleBase(object): - pass + def set_default_locators_and_formatters(self, axis): + raise NotImplementedError + +class LinearScale(ScaleBase): + name = 'linear' + + def __init__(self, axis, **kwargs): + pass -class LinearScale(ScaleBase): + def set_default_locators_and_formatters(self, axis): + axis.set_major_locator(AutoLocator()) + axis.set_major_formatter(ScalarFormatter()) + axis.set_minor_locator(NullLocator()) + axis.set_minor_formatter(NullFormatter()) + def get_transform(self): return IdentityTransform() class LogScale(ScaleBase): + name = 'log' + class Log10Transform(Transform): input_dims = 1 output_dims = 1 - def __init__(self): - Transform.__init__(self) - - def is_separable(self): - return True + is_separable = True def transform(self, a): return ma.log10(ma.masked_where(a <= 0.0, a * 10.0)) @@ -31,11 +44,7 @@ class InvertedLog10Transform(Transform): input_dims = 1 output_dims = 1 - def __init__(self): - Transform.__init__(self) - - def is_separable(self): - return True + is_separable = True def transform(self, a): return ma.power(10.0, a) / 10.0 @@ -46,11 +55,7 @@ class Log2Transform(Transform): input_dims = 1 output_dims = 1 - def __init__(self): - Transform.__init__(self) - - def is_separable(self): - return True + is_separable = True def transform(self, a): return ma.log2(ma.masked_where(a <= 0.0, a * 2.0)) @@ -61,11 +66,7 @@ class InvertedLog2Transform(Transform): input_dims = 1 output_dims = 1 - def __init__(self): - Transform.__init__(self) - - def is_separable(self): - return True + is_separable = True def transform(self, a): return ma.power(2.0, a) / 2.0 @@ -76,12 +77,8 @@ class NaturalLogTransform(Transform): input_dims = 1 output_dims = 1 - def __init__(self): - Transform.__init__(self) - - def is_separable(self): - return True - + is_separable = True + def transform(self, a): return ma.log(ma.masked_where(a <= 0.0, a * npy.e)) @@ -91,12 +88,8 @@ class InvertedNaturalLogTransform(Transform): input_dims = 1 output_dims = 1 - def __init__(self): - Transform.__init__(self) - - def is_separable(self): - return True - + is_separable = True + def transform(self, a): return ma.power(npy.e, a) / npy.e @@ -106,12 +99,11 @@ class LogTransform(Transform): input_dims = 1 output_dims = 1 + is_separable = True + def __init__(self, base): Transform.__init__(self) self._base = base - - def is_separable(self): - return True def transform(self, a): return ma.log(ma.masked_where(a <= 0.0, a * self._base)) / npy.log(self._base) @@ -122,21 +114,27 @@ class InvertedLogTransform(Transform): input_dims = 1 output_dims = 1 + is_separable = True + def __init__(self, base): Transform.__init__(self) self._base = base - def is_separable(self): - return True - def transform(self, a): return ma.power(self._base, a) / self._base def inverted(self): return LogScale.LogTransform(self._base) + - - def __init__(self, base=10): + def __init__(self, axis, **kwargs): + if axis.axis_name == 'x': + base = kwargs.pop('basex') + subs = kwargs.pop('subsx') + else: + base = kwargs.pop('basey') + subs = kwargs.pop('subsy') + if base == 10.0: self._transform = self.Log10Transform() elif base == 2.0: @@ -145,16 +143,30 @@ self._transform = self.NaturalLogTransform() else: self._transform = self.LogTransform(base) + + self._base = base + self._subs = subs + + def set_default_locators_and_formatters(self, axis): + axis.set_major_locator(LogLocator(self._base)) + axis.set_major_formatter(LogFormatterMathtext(self._base)) + axis.set_minor_locator(LogLocator(self._base, self._subs)) + axis.set_minor_formatter(NullFormatter()) def get_transform(self): return self._transform _scale_mapping = { - 'linear': LinearScale, - 'log': LogScale + 'linear' : LinearScale, + 'log' : LogScale } -def scale_factory(scale, viewLim, direction): +def scale_factory(scale, axis, **kwargs): + scale = scale.lower() if scale is None: scale = 'linear' - return _scale_mapping[scale](viewLim, direction) + + if not _scale_mapping.has_key(scale): + raise ValueError("Unknown scale type '%s'" % scale) + + return _scale_mapping[scale](axis, **kwargs) Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-10-01 07:06:43 UTC (rev 3904) +++ branches/transforms/lib/matplotlib/transforms.py 2007-10-01 11:44:54 UTC (rev 3905) @@ -7,49 +7,57 @@ import numpy as npy from numpy import ma as ma from numpy.linalg import inv -from sets import Set -from weakref import WeakKeyDictionary +from copy import deepcopy +from math import sqrt +from weakref import ref, WeakKeyDictionary + +import cbook from path import Path DEBUG = False -# 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): _gid = 0 + + is_affine = False + is_bbox = False def __init__(self): - # MGDTODO: I'd like to use a WeakKeyDictionary here, but it makes - # these instances uncopyable. As it stands, _parents grows - # unboundedly... Not a good idea. - self._parents = Set() - self._children = Set() + self._parents = WeakKeyDictionary() self._id = TransformNode._gid + TransformNode._gid += 1 + self._invalid = 1 + self._children = [] - def invalidate(self, affine_only=None): - if affine_only is None: - affine_only = self.is_affine() or self.is_bbox() - if not self._do_invalidation(affine_only): - self._id = TransformNode._gid - TransformNode._gid += 1 - for parent in self._parents: - parent.invalidate(affine_only) - - def _do_invalidation(self, affine_only): - return False + def invalidate(self): + if self._invalid: + return - def set_children(self, children): + value = (self.is_affine or self.is_bbox) and 2 or 1 + stack = [self] + + while len(stack): + root = stack.pop() + if root._invalid == 0: + root._id = TransformNode._gid + TransformNode._gid += 1 + root._invalid = value + stack.extend(root._parents.keys()) + + def set_children(self, *children): for child in children: - getattr(self, child)._parents.add(self) + child._parents[self] = None self._children = children + def frozen(self): + return self + def make_graphviz(self, fobj): - seen = Set() + seen = cbook.set() def recurse(root): if root in seen: @@ -57,30 +65,22 @@ seen.add(root) fobj.write('%s [label="%s"];\n' % (hash(root), root.__class__.__name__)) - if root.is_affine(): + if root.is_affine: fobj.write('%s [style=filled, color=".7 .7 .9"];\n' % hash(root)) - elif root.is_bbox(): + elif root.is_bbox: fobj.write('%s [style=filled, color=".9 .9 .7"];\n' % hash(root)) - for child_name in root._children: - child = getattr(root, child_name) - fobj.write('%s -> %s [label="%s"];\n' % ( + for child in root._children: + fobj.write('%s -> %s;\n' % ( hash(root), - hash(child), - child_name)) + hash(child))) recurse(child) fobj.write("digraph G {\n") recurse(self) fobj.write("}\n") - - def is_affine(self): - return False - def is_bbox(self): - return False - def get_id(self): return self._id @@ -89,13 +89,14 @@ ''' This is the read-only part of a bounding-box ''' + is_bbox = True def __init__(self): TransformNode.__init__(self) - def is_bbox(self): - return True - + def frozen(self): + return Bbox(self.get_points().copy()) + def __array__(self): return self.get_points() @@ -225,7 +226,6 @@ BboxBase.__init__(self) self._points = npy.asarray(points, npy.float_) self._minpos = npy.array([0.0000001, 0.0000001]) - self._invalid = False #@staticmethod def unit(): @@ -247,11 +247,6 @@ return 'Bbox(%s)' % repr(self._points) __str__ = __repr__ - def _do_invalidation(self, affine_only): - result = self._invalid - self._invalid = True - return result - def update_from_data(self, x, y, ignore=True): if ignore: self._points = npy.array( @@ -330,7 +325,7 @@ minposy = property(_get_minposy) def get_points(self): - self._invalid = False + self._invalid = 0 return self._points def set_points(self, points): @@ -349,6 +344,9 @@ a = npy.array([[-deltaw, -deltah], [deltaw, deltah]]) return Bbox(self._points + a) + def translated(self, tx, ty): + return Bbox(self._points + (tx, ty)) + #@staticmethod def union(bboxes): """ @@ -362,24 +360,22 @@ return bboxes[0] bbox = bboxes[0] - xmin = bbox.xmin - ymin = bbox.ymin - xmax = bbox.xmax - ymax = bbox.ymax + xmin0, ymin0, xmax0, ymax0 = bbox.bounds for bbox in bboxes[1:]: - xmin = min(xmin, bbox.xmin) - ymin = min(ymin, bbox.ymin) - xmax = max(xmax, bbox.xmax) - ymax = max(ymax, bbox.ymax) + xmin, ymin, xmax, ymax = bbox.bounds + xmin0 = min(xmin0, xmin) + ymin0 = min(ymin0, ymin) + xmax0 = max(xmax0, xmax) + ymax0 = max(ymax0, ymax) - return Bbox.from_lbrt(xmin, ymin, xmax, ymax) + return Bbox.from_lbrt(xmin0, ymin0, xmax0, ymax0) union = staticmethod(union) class TransformedBbox(BboxBase): def __init__(self, bbox, transform): - assert bbox.is_bbox() + assert bbox.is_bbox assert isinstance(transform, Transform) assert transform.input_dims == 2 assert transform.output_dims == 2 @@ -387,31 +383,26 @@ BboxBase.__init__(self) self._bbox = bbox self._transform = transform - self.set_children(['_bbox', '_transform']) + self.set_children(bbox, transform) self._points = None def __repr__(self): return "TransformedBbox(%s, %s)" % (self._bbox, self._transform) __str__ = __repr__ - def _do_invalidation(self, affine_only): - result = self._points is None - self._points = None - return result - def get_points(self): - if self._points is None: + if self._invalid: self._points = self._transform.transform(self._bbox.get_points()) + self._invalid = 0 return self._points - + class Transform(TransformNode): + is_separable = False + def __init__(self): TransformNode.__init__(self) - def is_separable(self): - return False - def __add__(self, other): if isinstance(other, Transform): return composite_transform_factory(self, other) @@ -428,17 +419,26 @@ raise NotImplementedError def transform_affine(self, points): - raise NotImplementedError + return points def transform_non_affine(self, points): - raise NotImplementedError + return self.transform(points) def get_affine(self): - raise NotImplementedError + return IdentityTransform() def transform_point(self, point): return self.transform(npy.asarray([point]))[0] + def transform_path(self, path): + return Path(self.transform(path.vertices), path.codes) + + def transform_path_affine(self, path): + return path + + def transform_path_non_affine(self, path): + return Path(self.transform_non_affine(path.vertices), path.codes) + def has_inverse(self): raise NotImplementedError() @@ -454,24 +454,35 @@ self.input_dims = child.input_dims self.output_dims = child.output_dims self._child = child - self.set_children(['_child']) + self.set_children(child) def __repr__(self): return "TransformWrapper(%r)" % self._child __str__ = __repr__ - + + def frozen(self): + return self._child.frozen() + def set(self, child): assert child.input_dims == self.input_dims assert child.output_dims == self.output_dims self._child = child - self.set_children(['_child']) + self.set_children(child) + self._invalid = 0 self.invalidate() + self._invalid = 0 - def is_separable(self): - return self._child.is_separable() + def _get_is_separable(self): + return self._child.is_separable + is_separable = property(_get_is_separable) - def is_affine(self): - return self._child.is_affine() + def _get_is_affine(self): + return self._child.is_affine + is_affine = property(_get_is_affine) + + def get_matrix(self): + assert self._child.is_affine + return self._child.get_matrix() def transform(self, points): return self._child.transform(points) @@ -482,6 +493,15 @@ def transform_non_affine(self, points): return self._child.transform_non_affine(points) + def transform_path(self, path): + return self._child.transform_path(path) + + def transform_path_affine(self, path): + return self._child.transform_path_affine(path) + + def transform_path_non_affine(self, path): + return self._child.transform_path_non_affine(path) + def get_affine(self): return self._child.get_affine() @@ -490,13 +510,12 @@ class AffineBase(Transform): + is_affine = True + def __init__(self): Transform.__init__(self) self._inverted = None - def is_affine(self): - return True - def __array__(self, *args, **kwargs): return self.get_matrix() @@ -507,7 +526,7 @@ #@staticmethod def concat(a, b): - return Affine1D(Affine1D._concat(a.get_matrix(), b.get_matrix())) + return Affine1D(npy.dot(b.get_matrix(), a.get_matrix())) concat = staticmethod(concat) def get_matrix(self): @@ -516,6 +535,12 @@ def transform_non_affine(self, points): return points + def transform_path_affine(self, path): + return self.transform_path(path) + + def transform_path_non_affine(self, path): + return path + def get_affine(self): return self @@ -523,12 +548,13 @@ class Affine1DBase(AffineBase): input_dims = 1 output_dims = 1 + is_separable = True def __init__(self): AffineBase.__init__(self) - def is_separable(self): - return True + def frozen(self): + return Affine1D(self.get_matrix().copy()) def __array__(self, *args, **kwargs): return self.get_matrix() @@ -539,10 +565,7 @@ #@staticmethod def matrix_from_values(a, b): - affine = npy.zeros((2, 2), npy.float_) - affine[0, :] = (a, b) - affine[1, 1] = 1 - return affine + return npy.array([[a, b], [0.0, 1.0]], npy.float_) matrix_from_values = staticmethod(matrix_from_values) def transform(self, values): @@ -561,15 +584,16 @@ # print "".join(traceback.format_stack()) # print points mtx = self.get_matrix() - # points = npy.asarray(values, npy.float_) + points = npy.asarray(values, npy.float_) return points * mtx[0,0] + mtx[0,1] transform_affine = transform def inverted(self): - if self._inverted is None: + if self._inverted is None or self._invalid: mtx = self.get_matrix() self._inverted = Affine1D(inv(mtx)) + self._invalid = 0 return self._inverted @@ -605,6 +629,7 @@ from_values = staticmethod(from_values) def get_matrix(self): + self._invalid = 0 return self._mtx def set_matrix(self, mtx): @@ -635,37 +660,29 @@ 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): assert direction in ('x', 'y') - assert bbox.is_bbox() + assert bbox.is_bbox Affine1DBase.__init__(self) self._bbox = bbox self._direction = "interval" + direction - self.set_children(['_bbox']) + self.set_children(bbox) self._mtx = None def __repr__(self): return "IntervalTransform(%s)" % (getattr(self._bbox, self._direction)) __str__ = __repr__ - def _do_invalidation(self, affine_only): - result = self._mtx is None - self._mtx = None - self._inverted = None - return result - def get_matrix(self): - if self._mtx is None: + if self._invalid: min, max = getattr(self._bbox, self._direction) self._mtx = inv(npy.array([[max - min, min], [0.0, 1.0]], npy.float_)) + self._inverted = None + self._invalid = 0 return self._mtx @@ -676,6 +693,9 @@ def __init__(self): AffineBase.__init__(self) + def frozen(self): + return Affine2D(self.get_matrix().copy()) + def is_separable(self): mtx = self.get_matrix() return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 @@ -689,11 +709,7 @@ #@staticmethod def matrix_from_values(a, b, c, d, e, f): - affine = npy.zeros((3, 3), npy.float_) - affine[0, ] = a, c, e - affine[1, ] = b, d, f - affine[2, 2] = 1 - return affine + return npy.array([[a, c, e], [b, d, f], [0.0, 0.0, 1.0]], npy.float_) matrix_from_values = staticmethod(matrix_from_values) def transform(self, points): @@ -715,7 +731,7 @@ # print "".join(traceback.format_stack()) # print points mtx = self.get_matrix() - if ma.isarray(points): + if ma.isMaskedArray(points): points = points.transpose() points = ma.dot(mtx[0:2, 0:2], points) points = points + mtx[0:2, 2:] @@ -729,9 +745,10 @@ transform_affine = transform def inverted(self): - if self._inverted is None: + if self._inverted is None or self._invalid: mtx = self.get_matrix() self._inverted = Affine2D(inv(mtx)) + self._invalid = 0 return self._inverted @@ -768,6 +785,7 @@ from_values = staticmethod(from_values) def get_matrix(self): + self._invalid = 0 return self._mtx def set_matrix(self, mtx): @@ -791,8 +809,8 @@ def rotate(self, theta): a = npy.cos(theta) b = npy.sin(theta) - rotate_mtx = self.matrix_from_values(a, b, -b, a, 0, 0) - self._mtx = self._concat(self._mtx, rotate_mtx) + rotate_mtx = npy.array([[a, -b, 0.0], [b, a, 0.0], [0.0, 0.0, 1.0]], npy.float_) + self._mtx = npy.dot(rotate_mtx, self._mtx) self.invalidate() return self @@ -806,30 +824,33 @@ return self.translate(-x, -y).rotate_deg(degrees).translate(x, y) def translate(self, tx, ty): - translate_mtx = self.matrix_from_values(1., 0., 0., 1., tx, ty) - self._mtx = self._concat(self._mtx, translate_mtx) + translate_mtx = npy.array([[1.0, 0.0, tx], [0.0, 1.0, ty], [0.0, 0.0, 1.0]], npy.float_) + self._mtx = npy.dot(translate_mtx, self._mtx) self.invalidate() return self def scale(self, sx, sy=None): if sy is None: sy = sx - scale_mtx = self.matrix_from_values(sx, 0., 0., sy, 0., 0.) - self._mtx = self._concat(self._mtx, scale_mtx) + scale_mtx = npy.array([[sx, 0.0, 0.0], [0.0, sy, 0.0], [0.0, 0.0, 1.0]], npy.float_) + self._mtx = npy.dot(scale_mtx, self._mtx) self.invalidate() return self - def is_separable(self): + def _get_is_separable(self): mtx = self.get_matrix() return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0 + is_separable = property(_get_is_separable) - class IdentityTransform(Affine2DBase): """ A special class that does the identity transform quickly. """ _mtx = npy.identity(3) + def frozen(self): + return self + def __repr__(self): return "IdentityTransform()" __str__ = __repr__ @@ -847,6 +868,10 @@ return points transform_affine = transform_non_affine = transform + def transform_path(self, path): + return path + transform_path_affine = transform_path_non_affine = transform_path + def get_affine(self): return self inverted = get_affine @@ -855,33 +880,37 @@ class BlendedGenericTransform(Transform): input_dims = 2 output_dims = 2 + is_separable = True def __init__(self, x_transform, y_transform): # Here we ask: "Does it blend?" - assert x_transform.is_separable() - assert y_transform.is_separable() + # MGDTODO: Reinvoke these asserts? + # assert x_transform.is_separable() + # assert y_transform.is_separable() Transform.__init__(self) self._x = x_transform self._y = y_transform - self.set_children(['_x', '_y']) + self.set_children(x_transform, y_transform) - def is_affine(self): - return self._x.is_affine() and self._y.is_affine() - - def is_separable(self): - return True - + def _get_is_affine(self): + return self._x.is_affine and self._y.is_affine + is_affine = property(_get_is_affine) + + def frozen(self): + return blended_transform_factory(self._x.frozen(), self._y.frozen()) + def __repr__(self): return "BlendedGenericTransform(%s,%s)" % (self._x, self._y) __str__ = __repr__ - + def transform(self, points): x = self._x y = self._y - if x == y and x.input_dims == 2: - return self._x.transform(points) + if x is y and x.input_dims == 2: + return x.transform(points) + if x.input_dims == 2: x_points = x.transform(points)[:, 0:1] else: @@ -895,11 +924,12 @@ y_points = y_points.reshape((len(y_points), 1)) return ma.concatenate((x_points, y_points), 1) + transform_non_affine = transform - + def transform_affine(self, points): return points - + def get_affine(self): return IdentityTransform() @@ -908,6 +938,8 @@ class BlendedAffine1D(Affine2DBase, Transform): + is_separable = True + def __init__(self, x_transform, y_transform): assert isinstance(x_transform, Affine1DBase) assert isinstance(y_transform, Affine1DBase) @@ -915,63 +947,50 @@ Transform.__init__(self) self._x = x_transform self._y = y_transform - self.set_children(['_x', '_y']) + self.set_children(x_transform, y_transform) Affine2DBase.__init__(self) self._mtx = None - def is_separable(self): - return True - def __repr__(self): return "BlendedAffine1D(%s,%s)" % (self._x, self._y) __str__ = __repr__ - def _do_invalidation(self, affine_only): - result = self._mtx is None - self._mtx = None - self._inverted = None - def get_matrix(self): - if self._mtx is None: + if self._invalid: 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]]) + self._inverted = None + self._invalid = 0 return self._mtx class BlendedAffine2D(Affine2DBase, Transform): + is_separable = True + 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() + assert x_transform.is_affine + assert y_transform.is_affine + assert x_transform.is_separable + assert y_transform.is_separable Transform.__init__(self) self._x = x_transform self._y = y_transform - self.set_children(['_x', '_y']) + self.set_children(x_transform, y_transform) Affine2DBase.__init__(self) self._mtx = None - def is_separable(self): - return True - def __repr__(self): return "BlendedAffine2D(%s,%s)" % (self._x, self._y) __str__ = __repr__ - def _do_invalidation(self, affine_only): - result = self._mtx is None - self._mtx = None - self._inverted = None - return result - def get_matrix(self): - if self._mtx is None: + if self._invalid: if self._x == self._y: self._mtx = self._x.get_matrix() else: @@ -981,6 +1000,8 @@ # separable, though normally one would want to set b and # c to zero. self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) + self._inverted = None + self._invalid = 0 return self._mtx @@ -1001,18 +1022,31 @@ Transform.__init__(self) self._a = a self._b = b - self.set_children(['_a', '_b']) + self.set_children(a, b) + self._mtx = None - def is_affine(self): - return self._a.is_affine() and self._b.is_affine() + def frozen(self): + return composite_transform_factory(self._a.frozen(), self._b.frozen()) - def is_separable(self): + def _get_is_affine(self): + return self._a.is_affine and self._b.is_affine + is_affine = property(_get_is_affine) + + def _get_is_separable(self): return self._a.is_separable() and self._b.is_separable() + is_separable = property(_get_is_separable) def __repr__(self): return "CompositeGenericTransform(%s, %s)" % (self._a, self._b) __str__ = __repr__ - + + def get_matrix(self): + if self._invalid: + assert self._a.is_affine and self._b.is_affine + self._mtx = npy.dot(self._b.get_matrix(), self._a.get_matrix()) + self._invalid = 0 + return self._mtx + def transform(self, points): return self._b.transform(self._a.transform(points)) @@ -1022,6 +1056,15 @@ def transform_non_affine(self, points): return self._b.transform_non_affine(self._a.transform_non_affine(points)) + def transform_path(self, path): + return self._b.transform_path(self._a.transform_path(path)) + + def transform_path_affine(self, path): + return self._b.transform_path_affine(self._a.transform_path_affine(path)) + + def transform_path_non_affine(self, path): + return self._b.transform_path_non_affine(self._a.transform_path_non_affine(path)) + def get_affine(self): return CompositeAffine2D(self._a.get_affine(), self._b.get_affine()) @@ -1034,113 +1077,46 @@ assert a.output_dims == b.input_dims self.input_dims = a.input_dims self.output_dims = b.output_dims - assert a.is_affine() - assert b.is_affine() + assert a.is_affine + assert b.is_affine Affine2DBase.__init__(self) self._a = a self._b = b - self.set_children(['_a', '_b']) + self.set_children(a, b) self._mtx = None def __repr__(self): return "CompositeAffine2D(%s, %s)" % (self._a, self._b) __str__ = __repr__ - def _do_invalidation(self, affine_only): - result = self._mtx is None - self._mtx = None - self._inverted = None - return result - def get_matrix(self): - if self._mtx is None: - self._mtx = self._concat( - self._a.get_matrix(), - self._b.get_matrix()) + if self._invalid: + self._mtx = npy.dot( + self._b.get_matrix(), + self._a.get_matrix()) + self._inverted = None + self._invalid = 0 return self._mtx def composite_transform_factory(a, b): -# if isinstance(a, BboxTransform) and isinstance(b, BboxTransform): -# return BboxTransform(a._boxin, b._boxout) if isinstance(a, AffineBase) and isinstance(b, AffineBase): return CompositeAffine2D(a, b) return CompositeGenericTransform(a, b) - - -class TestPolarTransform(Transform): - 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 - 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(self._limits) - def is_separable(self): - return False - - -class TestInvertPolarTransform(Transform): - 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) - r = r * (limmax - limmin) + limmin - return ma.concatenate((theta, r), 1) - - def inverted(self): - return TestInvertPolarTransform(self._limits) - - def is_separable(self): - return False - - class BboxTransform(Affine2DBase): + is_separable = True + def __init__(self, boxin, boxout): - assert boxin.is_bbox() - assert boxout.is_bbox() + assert boxin.is_bbox + assert boxout.is_bbox Affine2DBase.__init__(self) self._boxin = boxin self._boxout = boxout - self.set_children(['_boxin', '_boxout']) + self.set_children(boxin, boxout) self._mtx = None self._inverted = None @@ -1148,29 +1124,17 @@ return "BboxTransform(%s, %s)" % (self._boxin, self._boxout) __str__ = __repr__ - def _do_invalidation(self, affine_only): - result = self._mtx is None - self._mtx = None - self._inverted = None - return result - - def is_separable(self): - return True - def get_matrix(self): - if self._mtx is None: - boxin = self._boxin - boxout = self._boxout - x_scale = boxout.width / boxin.width - y_scale = boxout.height / boxin.height - - # MGDTODO: Optimize - affine = Affine2D() \ - .translate(-boxin.xmin, -boxin.ymin) \ - .scale(x_scale, y_scale) \ - .translate(boxout.xmin, boxout.ymin) - - self._mtx = affine._mtx + if self._invalid: + inl, inb, inw, inh = self._boxin.bounds + outl, outb, outw, outh = self._boxout.bounds + x_scale = outw / inw + y_scale = outh / inh + self._mtx = npy.array([[x_scale, 0.0 , (-inl*x_scale+outl)], + [0.0 , y_scale, (-inb*y_scale+outb)], + [0.0 , 0.0 , 1.0 ]], + npy.float_) + self._inverted = None return self._mtx @@ -1181,27 +1145,20 @@ self._path = path self._transform = transform - self.set_children(['_transform']) + self.set_children(transform) self._transformed_path = None - def _do_invalidation(self, affine_only): - if not affine_only: - self._transformed_path = None - return True - - def get_path_and_affine(self): - if self._transformed_path is None: - vertices = self._transform.transform_non_affine(self._path.vertices) - self._transformed_path = Path(vertices, self._path.codes) - + def get_transformed_path_and_affine(self): + if self._invalid == 1 or self._transformed_path is None: + self._transformed_path = self._transform.transform_path_non_affine(self._path) + self._invalid = 0 return self._transformed_path, self._transform.get_affine() - def get_path(self): - if self._transformed_path is None: - vertices = self._tranform.transform_non_affine(self._path.vertices) - self._transformed_path = Path(vertices, self._path.codes) - vertices = self._transform.transform_affine(self._transformed_path.vertices) - return Path(vertices, self._transformed_path.codes) + def get_fully_transformed_path(self): + if self._invalid == 1 or self._transformed_path is None: + self._transformed_path = self._transform.transform_path_non_affine(self._path) + self._invalid = 0 + return self._transform.transform_path_affine(self._transformed_path) def get_affine(self): return self._transform.get_affine() ... [truncated message content] |