|
From: <md...@us...> - 2007-09-19 19:46:41
|
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
#@classmethod
@@ -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.
|