Revision: 3859 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3859&view=rev Author: mdboom Date: 2007-09-19 12:46:34 -0700 (Wed, 19 Sep 2007)
Log Message: ----------- Lots of minor fixes Modified Paths: -------------- branches/transforms/lib/matplotlib/lines.py branches/transforms/lib/matplotlib/patches.py branches/transforms/lib/matplotlib/path.py branches/transforms/lib/matplotlib/pbox.py branches/transforms/lib/matplotlib/text.py branches/transforms/lib/matplotlib/transforms.py Modified: branches/transforms/lib/matplotlib/lines.py =================================================================== --- branches/transforms/lib/matplotlib/lines.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/lines.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -252,12 +252,9 @@ if not is_numlike(self.pickradius): raise ValueError,"pick radius should be a distance" - if self._newstyle: - # transform in backend - x = self._x - y = self._y - else: - x, y = self._get_plottable() + # transform in backend + x = self._x + y = self._y if len(x)==0: return False,{} xt, yt = self.get_transform().numerix_x_y(x, y) @@ -337,7 +334,6 @@ ACCEPTS: (npy.array xdata, npy.array ydata) """ - if len(args)==1: x, y = args[0] else: @@ -347,8 +343,9 @@ self._yorig = y self.recache() + # MGDTODO: Masked data arrays are broken _masked_array_to_path_code_mapping = npy.array( - [Path.LINETO, Path.IGNORE, Path.MOVETO], Path.code_type) + [Path.LINETO, Path.MOVETO, Path.MOVETO], Path.code_type) def recache(self): #if self.axes is None: print 'recache no axes' #else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units @@ -387,18 +384,18 @@ # MGDTODO: If _draw_steps is removed, remove the following line also self._step_path = None - def _is_sorted(self, x): "return true if x is sorted" if len(x)<2: return 1 return npy.alltrue(x[1:]-x[0:-1]>=0) + # MGDTODO: Remove me (seems to be used for old-style interface only) def _get_plottable(self): # If log scale is set, only pos data will be returned x, y = self._x, self._y - # MGDTODO: Deal with the log scale here + # MGDTODO: (log-scaling) # try: logx = self.get_transform().get_funcx().get_type()==LOG10 # except RuntimeError: logx = False # non-separable Modified: branches/transforms/lib/matplotlib/patches.py =================================================================== --- branches/transforms/lib/matplotlib/patches.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/patches.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -319,8 +319,6 @@ return str(self.__class__).split('.')[-1] \ + "(%g,%g;%gx%g)"%(self.xy[0],self.xy[1],self.width,self.height) - # MGDTODO: Perhaps pass in a Bbox here instead, then the updates will - # happen automatically (without needing to call set_x etc. def __init__(self, xy, width, height, **kwargs): """ xy is an x,y tuple lower, left @@ -459,17 +457,14 @@ def __init__(self, xy, **kwargs): """ - xy is a sequence of (x,y) 2 tuples + xy is a numpy array with shape Nx2 Valid kwargs are: %(Patch)s See Patch documentation for additional kwargs """ - # MGDTODO: This should encourage the use of numpy arrays of shape Nx2 Patch.__init__(self, **kwargs) - if not isinstance(xy, list): - xy = list(xy) - self._path = Path(xy, closed=False) + self._path = Path(xy, closed=True) __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd def get_verts(self): Modified: branches/transforms/lib/matplotlib/path.py =================================================================== --- branches/transforms/lib/matplotlib/path.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/path.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -1,15 +1,13 @@ import numpy as npy -DEBUG = True - class Path(object): # Path codes - IGNORE = 0 # 1 vertex + STOP = 0 # 1 vertex MOVETO = 1 # 1 vertex LINETO = 2 # 1 vertex CURVE3 = 3 # 2 vertices CURVE4 = 4 # 3 vertices - CLOSEPOLY = 5 + CLOSEPOLY = 5 # 1 vertex ### # MGDTODO: I'm not sure these are supported by PS/PDF/SVG, # so if they don't, we probably shouldn't @@ -18,38 +16,36 @@ UBSPLINE = 8 #### - NUM_VERTICES = [1, 1, 1, 2, 3, 0] + NUM_VERTICES = [1, 1, 1, 2, 3, 1] code_type = npy.uint8 def __init__(self, vertices, codes=None, closed=True): - self._vertices = npy.asarray(vertices, npy.float_) - assert self._vertices.ndim == 2 - assert self._vertices.shape[1] == 2 - + vertices = npy.asarray(vertices, npy.float_) + assert vertices.ndim == 2 + assert vertices.shape[1] == 2 + if codes is None: if closed: codes = self.LINETO * npy.ones( - self._vertices.shape[0] + 1, self.code_type) + vertices.shape[0] + 1, self.code_type) codes[0] = self.MOVETO - codes[-1] = self.CLOSEPOLY + codes[-1] = self.CLOSEPOLY + vertices = npy.concatenate((vertices, [[0.0, 0.0]])) else: codes = self.LINETO * npy.ones( - self._vertices.shape[0], self.code_type) + vertices.shape[0], self.code_type) codes[0] = self.MOVETO else: codes = npy.asarray(codes, self.code_type) - self._codes = codes - + assert codes.ndim == 1 + assert len(codes) == len(vertices) + + self._codes = codes + self._vertices = vertices + assert self._codes.ndim == 1 - if DEBUG: - i = 0 - NUM_VERTICES = self.NUM_VERTICES - for code in codes: - i += NUM_VERTICES[code] - assert i == len(self.vertices) - def __repr__(self): return "Path(%s, %s)" % (self.vertices, self.codes) @@ -66,11 +62,13 @@ NUM_VERTICES = self.NUM_VERTICES vertices = self.vertices for code in self.codes: - num_vertices = NUM_VERTICES[code] - if num_vertices >= 1: - i += num_vertices - 1 - yield vertices[i] - i += 1 + if code == self.CLOSEPOLY: + i += 1 + else: + num_vertices = NUM_VERTICES[code] + i += num_vertices - 1 + yield vertices[i] + i += 1 _unit_rectangle = None [EMAIL PROTECTED] @@ -118,16 +116,18 @@ [-offset, -1.0], [-1.0, -offset], - [-1.0, 0.0]], - npy.float_) - codes = npy.array( - [cls.MOVETO, - cls.CURVE4, - cls.CURVE4, - cls.CURVE4, - cls.CURVE4, - cls.CLOSEPOLY], - cls.code_type) + [-1.0, 0.0], + + [-1.0, 0.0]], + npy.float_) + + codes = cls.CURVE4 + npy.ones((len(vertices))) + codes[0] = cls.MOVETO + codes[-1] = cls.CLOSEPOLY + cls._unit_circle = Path(vertices, codes) return cls._unit_circle unit_circle = classmethod(unit_circle) + +# MGDTODO: Add a transformed path that would automatically invalidate +# itself when its transform changes Modified: branches/transforms/lib/matplotlib/pbox.py =================================================================== --- branches/transforms/lib/matplotlib/pbox.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/pbox.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -1,5 +1,3 @@ -# MGDTODO: Just included verbatim for now - class PBox(list): ''' A left-bottom-width-height (lbwh) specification of a bounding box, Modified: branches/transforms/lib/matplotlib/text.py =================================================================== --- branches/transforms/lib/matplotlib/text.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/text.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -231,7 +231,7 @@ # now rotate the bbox - cornersRotated = M(cornersHoriz) + cornersRotated = M.transform(cornersHoriz) txs = cornersRotated[:, 0] tys = cornersRotated[:, 1] @@ -269,7 +269,7 @@ # now rotate the positions around the first x,y position - xys = M(offsetLayout) + xys = M.transform(offsetLayout) tx = xys[:, 0] ty = xys[:, 1] tx += offsetx @@ -277,7 +277,7 @@ # now inverse transform back to data coords inverse_transform = self.get_transform().inverted() - xys = inverse_transform(xys) + xys = inverse_transform.transform(xys) xs, ys = zip(*xys) @@ -407,7 +407,7 @@ return (x, y, self._text, self._color, self._verticalalignment, self._horizontalalignment, hash(self._fontproperties), self._rotation, - self.get_transform().to_values(), + self.get_transform(), ) def get_text(self): Modified: branches/transforms/lib/matplotlib/transforms.py =================================================================== --- branches/transforms/lib/matplotlib/transforms.py 2007-09-19 16:18:51 UTC (rev 3858) +++ branches/transforms/lib/matplotlib/transforms.py 2007-09-19 19:46:34 UTC (rev 3859) @@ -32,7 +32,8 @@ for child in children: getattr(self, child)._parents.add(self) self._children = children - + + class BboxBase(TransformNode): ''' This is the read-only part of a bounding-box @@ -293,6 +294,7 @@ return Bbox.from_lbrt(xmin, ymin, xmax, ymax) union = staticmethod(union) + class TransformedBbox(BboxBase): def __init__(self, bbox, transform): assert isinstance(bbox, Bbox) @@ -313,16 +315,20 @@ def get_points(self): if self._points is None: - self._points = self.transform(self.bbox.get_points()) + self._points = self.transform.transform(self.bbox.get_points()) return self._points + class Transform(TransformNode): def __init__(self): TransformNode.__init__(self) - def __call__(self, points): + def transform(self, points): raise NotImplementedError() + def transform_without_affine(self, points): + return self.transform(points), IDENTITY + def __add__(self, other): if isinstance(other, Transform): return composite_transform_factory(self, other) @@ -336,7 +342,7 @@ "Can not add Transform to object of type '%s'" % type(other)) def transform_point(self, point): - return self.__call__(npy.asarray([point]))[0] + return self.transform(npy.asarray([point]))[0] def has_inverse(self): raise NotImplementedError() @@ -350,6 +356,7 @@ def is_affine(self): return False + class Affine2DBase(Transform): input_dims = 2 output_dims = 2 @@ -390,7 +397,7 @@ def get_matrix(self): raise NotImplementedError() - def __call__(self, points): + def transform(self, points): """ Applies the transformation to an array of 2D points and returns the result. @@ -414,6 +421,11 @@ points = npy.dot(mtx[0:2, 0:2], points) 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: @@ -430,9 +442,6 @@ class Affine2D(Affine2DBase): - input_dims = 2 - output_dims = 2 - def __init__(self, matrix = None): """ Initialize an Affine transform from a 3x3 numpy float array. @@ -535,40 +544,82 @@ def is_affine(self): return True + +IDENTITY = Affine2D() class BlendedGenericTransform(Transform): + input_dims = 2 + output_dims = 2 + def __init__(self, x_transform, y_transform): # Here we ask: "Does it blend?" assert x_transform.is_separable() assert y_transform.is_separable() - + assert x_transform.input_dims == x_transform.output_dims == 2 + assert y_transform.input_dims == y_transform.output_dims == 2 + Transform.__init__(self) self._x = x_transform self._y = y_transform self.set_children(['_x', '_y']) - def __call__(self, points): - if self._x == self._y: + 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) - - x_points = self._x(points) - y_points = self._y(points) - # This works because we already know the transforms are - # separable - return npy.hstack((x_points[:, 0:1], y_points[:, 1:2])) -# def set_x_transform(self, x_transform): -# self.replace_child(0, x_transform) + if x.input_dims == 2: + x_points = x.transform(points)[:, 0] + else: + x_points = x.transform(points[:, 0]) -# def set_y_transform(self, y_transform): -# self.replace_child(1, y_transform) + if y.input_dims == 2: + y_points = y.transform(points)[:, 1] + else: + y_points = y.transform(points[:, 1]) + return npy.vstack((x_points, y_points)).transpose() + + def inverted(self): + return BlendedGenericTransform(self._x.inverted(), self._y.inverted()) -class BlendedAffine2D(Affine2DBase, BlendedGenericTransform): + def is_separable(self): + return True + + +class BlendedSeparableTransform(Transform): + input_dims = 2 + output_dims = 2 + def __init__(self, x_transform, y_transform): + # Here we ask: "Does it blend?" + assert x_transform.is_separable() + assert y_transform.is_separable() + assert x_transform.input_dims == x.transform.output_dims == 1 + assert y_transform.input_dims == y.transform.output_dims == 1 + + Transform.__init__(self) + self._x = x_transform + self._y = y_transform + self.set_children(['_x', '_y']) + + def transform(self, points): + x_points = self._x(points[:, 0]) + y_points = self._y(points[:, 1]) + return npy.vstack((x_points[:, 0:1], y_points[:, 1:2])).transpose() + + +class BlendedAffine2D(Affine2DBase, Transform): + def __init__(self, x_transform, y_transform): assert x_transform.is_affine() assert y_transform.is_affine() - BlendedGenericTransform.__init__(self, x_transform, y_transform) + Transform.__init__(self) + self._x = x_transform + self._y = y_transform + self.set_children(['_x', '_y']) Affine2DBase.__init__(self) self._mtx = None @@ -597,12 +648,14 @@ # c to zero. self._mtx = npy.vstack((x_mtx[0], y_mtx[1], [0.0, 0.0, 1.0])) return self._mtx - + + def blended_transform_factory(x_transform, y_transform): if x_transform.is_affine() and y_transform.is_affine(): return BlendedAffine2D(x_transform, y_transform) return BlendedGenericTransform(x_transform, y_transform) + class CompositeGenericTransform(Transform): def __init__(self, a, b): assert a.output_dims == b.input_dims @@ -614,9 +667,17 @@ self._b = b self.set_children(['_a', '_b']) - def __call__(self, points): - return self._b(self._a(points)) + def transform(self, points): + return self._b.transform(self._a.transform(points)) + + def inverted(self): + return CompositeGenericTransform(self._b.inverted(), self._a.inverted()) + def is_separable(self): + return True + return self._a.is_separable() and self._b.is_separable() + + class CompositeAffine2D(Affine2DBase): def __init__(self, a, b): assert a.is_affine() @@ -643,11 +704,32 @@ self._b.get_matrix()) return self._mtx + def composite_transform_factory(a, b): if a.is_affine() and b.is_affine(): return CompositeAffine2D(a, b) return CompositeGenericTransform(a, b) + + +class LogTransform(Transform): + input_dims = 1 + output_dims = 1 + def transform(self, a): + m = npy.ma.masked_where(a < 0, a) + return npy.log10(m) + + +class TestLogTransform(Transform): + input_dims = 2 + output_dims = 2 + def transform(self, xy): + return xy * 2 + + def inverted(self): + return self + + class BboxTransform(Affine2DBase): def __init__(self, boxin, boxout): assert isinstance(boxin, BboxBase) @@ -688,6 +770,7 @@ self._mtx = affine._mtx return self._mtx + def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): ''' Ensure the endpoints of a range are not too close together. 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