|
From: <md...@us...> - 2007-09-13 18:00:12
|
Revision: 3848
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3848&view=rev
Author: mdboom
Date: 2007-09-13 11:00:10 -0700 (Thu, 13 Sep 2007)
Log Message:
-----------
New milestone -- resizing figure window works. shared_axis_demo.py
works. (Uses callbacks to track changes between axes's).
Modified Paths:
--------------
branches/transforms/lib/matplotlib/affine.py
branches/transforms/lib/matplotlib/artist.py
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/axis.py
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/figure.py
branches/transforms/lib/matplotlib/pyplot.py
branches/transforms/lib/matplotlib/text.py
Modified: branches/transforms/lib/matplotlib/affine.py
===================================================================
--- branches/transforms/lib/matplotlib/affine.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/affine.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -20,284 +20,461 @@
class TransformNode(object):
def __init__(self):
- self._parents = Set()
-
+ self._parents = Set()
+
def invalidate(self):
- if not self._do_invalidation():
- for parent in self._parents:
- parent.invalidate()
+ if not self._do_invalidation():
+ for parent in self._parents:
+ parent.invalidate()
def _do_invalidation(self):
- return False
-
- def add_children(self, children):
- for child in children:
- child._parents.add(self)
+ return False
-class Bbox(TransformNode):
+ def set_children(self, children):
+ for child in children:
+ getattr(self, child)._parents.add(self)
+ self._children = children
+
+# def replace_child(self, index, child):
+# children = self._children
+# getattr(self, children[index])._parents.remove(self)
+# setattr(self, children[index], child)
+# # We have to reset children in case two or more
+# # of the children are the same
+# for child in children:
+# getattr(self, child)._parents.add(self)
+# self.invalidate()
+
+class BboxBase(TransformNode):
+ '''
+ This is the read-only part of a bounding-box
+ '''
+
+ def __init__(self):
+ TransformNode.__init__(self)
+
+ def __array__(self):
+ return self.get_points()
+
+ # MGDTODO: Probably a more efficient ways to do this...
+ def _get_xmin(self):
+ return self.get_points()[0, 0]
+ xmin = property(_get_xmin)
+
+ def _get_ymin(self):
+ return self.get_points()[0, 1]
+ ymin = property(_get_ymin)
+
+ def _get_xmax(self):
+ return self.get_points()[1, 0]
+ xmax = property(_get_xmax)
+
+ def _get_ymax(self):
+ return self.get_points()[1, 1]
+ ymax = property(_get_ymax)
+
+ def _get_min(self):
+ return self.get_points()[0]
+ min = property(_get_min)
+
+ def _get_max(self):
+ return self.get_points()[1]
+ max = property(_get_max)
+
+ def _get_intervalx(self):
+ return self.get_points()[:, 0]
+ intervalx = property(_get_intervalx)
+
+ def _get_intervaly(self):
+ return self.get_points()[:, 1]
+ intervaly = property(_get_intervaly)
+
+ def _get_width(self):
+ return self.xmax - self.xmin
+ width = property(_get_width)
+
+ def _get_height(self):
+ return self.ymax - self.ymin
+ height = property(_get_height)
+
+ def _get_bounds(self):
+ return (self.xmin, self.ymin,
+ self.xmax - self.xmin, self.ymax - self.ymin)
+ bounds = property(_get_bounds)
+
+ def get_points(self):
+ return NotImplementedError()
+
+ # MGDTODO: Optimize
+ def containsx(self, x):
+ return x >= self.xmin and x <= self.xmax
+
+ def containsy(self, y):
+ return y >= self.ymin and y <= self.ymax
+
+ def contains(self, x, y):
+ return self.containsx(x) and self.containsy(y)
+
+ def overlapsx(self, other):
+ return self.containsx(other.xmin) \
+ or self.containsx(other.xmax)
+
+ def overlapsy(self, other):
+ return self.containsy(other.ymin) \
+ or self.containsx(other.ymax)
+
+ def overlaps(self, other):
+ return self.overlapsx(other) \
+ and self.overlapsy(other)
+
+ def fully_containsx(self, x):
+ return x > self.xmin and x < self.xmax
+
+ def fully_containsy(self, y):
+ return y > self.ymin and y < self.ymax
+
+ def fully_contains(self, x, y):
+ return self.fully_containsx(x) \
+ and self.fully_containsy(y)
+
+ def fully_overlapsx(self, other):
+ return self.fully_containsx(other.xmin) \
+ or self.fully_containsx(other.xmax)
+
+ def fully_overlapsy(self, other):
+ return self.fully_containsy(other.ymin) \
+ or self.fully_containsx(other.ymax)
+
+ def fully_overlaps(self, other):
+ return self.fully_overlapsx(other) and \
+ self.fully_overlapsy(other)
+
+
+class Bbox(BboxBase):
def __init__(self, points):
- TransformNode.__init__(self)
- self._points = npy.asarray(points, npy.float_)
- self.track = False
+ BboxBase.__init__(self)
+ self._points = npy.asarray(points, npy.float_)
#@staticmethod
def unit():
- return Bbox.from_lbrt(0., 0., 1., 1.)
+ return Bbox.from_lbrt(0., 0., 1., 1.)
unit = staticmethod(unit)
#@staticmethod
def from_lbwh(left, bottom, width, height):
- return Bbox.from_lbrt(left, bottom, left + width, bottom + height)
+ return Bbox.from_lbrt(left, bottom, left + width, bottom + height)
from_lbwh = staticmethod(from_lbwh)
#@staticmethod
def from_lbrt(*args):
- points = npy.array(args, dtype=npy.float_).reshape(2, 2)
- return Bbox(points)
+ points = npy.array(args, dtype=npy.float_).reshape(2, 2)
+ return Bbox(points)
from_lbrt = staticmethod(from_lbrt)
def __copy__(self):
- return Bbox(self._points.copy())
+ return Bbox(self._points.copy())
def __deepcopy__(self, memo):
- return Bbox(self._points.copy())
+ return Bbox(self._points.copy())
def __cmp__(self, other):
- # MGDTODO: Totally suboptimal
- if isinstance(other, Bbox) and (self._points == other._points).all():
- return 0
- return -1
+ # MGDTODO: Totally suboptimal
+ if isinstance(other, Bbox) and (self._points == other._points).all():
+ return 0
+ return -1
def __repr__(self):
- return 'Bbox(%s)' % repr(self._points)
+ return 'Bbox(%s)' % repr(self._points)
__str__ = __repr__
- def __array__(self):
- return self._points
-
# JDH: the update method will update the box limits from the
# existing limits and the new data; it appears here you are just
# using the new data. We use an "ignore" flag to specify whether
# you want to include the existing data or not in the update
def update_from_data(self, x, y, ignore=True):
- self._points = npy.array([[x.min(), y.min()], [x.max(), y.max()]], npy.float_)
- self.invalidate()
+ self._points = npy.array(
+ [[x.min(), y.min()], [x.max(), y.max()]],
+ npy.float_)
+ self.invalidate()
# MGDTODO: Probably a more efficient ways to do this...
- def _get_xmin(self):
- return self._points[0, 0]
def _set_xmin(self, val):
- self._points[0, 0] = val
- self.invalidate()
- xmin = property(_get_xmin, _set_xmin)
+ self._points[0, 0] = val
+ self.invalidate()
+ xmin = property(BboxBase._get_xmin, _set_xmin)
- def _get_ymin(self):
- return self._points[0, 1]
def _set_ymin(self, val):
- self._points[0, 1] = val
- self.invalidate()
- ymin = property(_get_ymin, _set_ymin)
+ self._points[0, 1] = val
+ self.invalidate()
+ ymin = property(BboxBase._get_ymin, _set_ymin)
- def _get_xmax(self):
- return self._points[1, 0]
def _set_xmax(self, val):
- self._points[1, 0] = val
- self.invalidate()
- xmax = property(_get_xmax, _set_xmax)
+ self._points[1, 0] = val
+ self.invalidate()
+ xmax = property(BboxBase._get_xmax, _set_xmax)
- def _get_ymax(self):
- return self._points[1, 1]
def _set_ymax(self, val):
- self._points[1, 1] = val
- self.invalidate()
- ymax = property(_get_ymax, _set_ymax)
+ self._points[1, 1] = val
+ self.invalidate()
+ ymax = property(BboxBase._get_ymax, _set_ymax)
- def _get_min(self):
- return self._points[0]
def _set_min(self, val):
- self._points[0] = val
- self.invalidate()
- min = property(_get_min, _set_min)
+ self._points[0] = val
+ self.invalidate()
+ min = property(BboxBase._get_min, _set_min)
- def _get_max(self):
- return self._points[1]
def _set_max(self, val):
- self._points[1] = val
- self.invalidate()
- max = property(_get_max, _set_max)
+ self._points[1] = val
+ self.invalidate()
+ max = property(BboxBase._get_max, _set_max)
- def _get_intervalx(self):
- return self._points[:,0]
def _set_intervalx(self, interval):
- self._points[:,0] = interval
- self.invalidate()
- intervalx = property(_get_intervalx, _set_intervalx)
+ self._points[:, 0] = interval
+ self.invalidate()
+ intervalx = property(BboxBase._get_intervalx, _set_intervalx)
- def _get_intervaly(self):
- return self._points[:,1]
def _set_intervaly(self, interval):
- self._points[:,1] = interval
- self.invalidate()
- intervaly = property(_get_intervaly, _set_intervaly)
+ self._points[:, 1] = interval
+ self.invalidate()
+ intervaly = property(BboxBase._get_intervaly, _set_intervaly)
- def _get_width(self):
- return self.xmax - self.xmin
- width = property(_get_width)
+ def _set_bounds(self, bounds):
+ l,b,w,h = bounds
+ self._points = npy.array([[l, b], [l+w, b+h]], npy.float_)
+ self.invalidate()
+ bounds = property(BboxBase._get_bounds, _set_bounds)
- def _get_height(self):
- return self.ymax - self.ymin
- height = property(_get_height)
+ def get_points(self):
+ return self._points
- def _get_bounds(self):
- return (self.xmin, self.ymin,
- self.xmax - self.xmin, self.ymax - self.ymin)
- def _set_bounds(self, bounds):
- l,b,w,h = bounds
- self._points = npy.array([[l, b], [l+w, b+h]], npy.float_)
- self.invalidate()
- bounds = property(_get_bounds, _set_bounds)
-
+ def set_points(self, points):
+ self._points = points
+ self.invalidate()
+
+ def set(self, other):
+ self._points = other.get_points()
+ self.invalidate()
+
def transformed(self, transform):
- return Bbox(transform(self._points))
+ return Bbox(transform(self._points))
def inverse_transformed(self, transform):
- return Bbox(transform.inverted()(self._points))
+ return Bbox(transform.inverted()(self._points))
def expanded(self, sw, sh):
- width = self.width
- height = self.height
- deltaw = (sw * width - width) / 2.0
- deltah = (sh * height - height) / 2.0
- a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
- return Bbox(self._points + a)
+ width = self.width
+ height = self.height
+ deltaw = (sw * width - width) / 2.0
+ deltah = (sh * height - height) / 2.0
+ a = npy.array([[-deltaw, -deltah], [deltaw, deltah]])
+ return Bbox(self._points + a)
- def contains(self, x, y):
- return (x >= self.xmin and x <= self.xmax and
- y >= self.ymin and y <= self.ymax)
-
#@staticmethod
def union(bboxes):
- """
- Return the Bbox that bounds all bboxes
- """
- assert(len(bboxes))
+ """
+ Return the Bbox that bounds all bboxes
+ """
+ assert(len(bboxes))
- if len(bboxes) == 1:
- return bboxes[0]
+ if len(bboxes) == 1:
+ return bboxes[0]
- bbox = bboxes[0]
- xmin = bbox.xmin
- ymin = bbox.ymin
- xmax = bbox.xmax
- ymax = bbox.ymax
+ bbox = bboxes[0]
+ xmin = bbox.xmin
+ ymin = bbox.ymin
+ xmax = bbox.xmax
+ ymax = bbox.ymax
- 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)
+ 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)
- return Bbox.from_lbrt(xmin, ymin, xmax, ymax)
+ return Bbox.from_lbrt(xmin, ymin, xmax, ymax)
union = staticmethod(union)
+
+class TransformedBbox(BboxBase):
+ def __init__(self, bbox, transform):
+ assert isinstance(bbox, Bbox)
+ assert isinstance(transform, Transform)
+
+ BboxBase.__init__(self)
+ self.bbox = bbox
+ self.transform = 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):
+ self._points = None
+
+ def get_points(self):
+ if self._points is None:
+ self._points = self.transform(self.bbox.get_points())
+ return self._points
+
+# MGDTODO: This code probably works, but I don't think it's a good idea
+# (from a code clarity perspective)
+# class BlendedBbox(BboxBase):
+# def __init__(self, bbox_x, bbox_y):
+# assert isinstance(bbox_x, BboxBase)
+# assert isinstance(bbox_y, BboxBase)
+
+# BboxBase.__init__(self)
+# self._x = bbox_x
+# self._y = bbox_y
+# self.set_children(['_x', '_y'])
+# self._points = None
+
+# def __repr__(self):
+# return "TransformedBbox(%s, %s)" % (self.bbox, self.transform)
+# __str__ = __repr__
+
+# def _do_invalidation(self):
+# self._points = None
+
+# def get_points(self):
+# if self._points is None:
+# # MGDTODO: Optimize
+# if self._x == self._y:
+# self._points = self._x.get_points()
+# else:
+# x_points = self._x.get_points()
+# y_points = self._y.get_points()
+# self._points = npy.array(
+# [[x_points[0,0], y_points[0,1]],
+# [x_points[1,0], y_points[1,1]]],
+# npy.float_)
+# return self._points
+
+# def _set_intervalx(self, pair):
+# # MGDTODO: Optimize
+# bbox = Bbox([[pair[0], 0.0], [pair[1], 0.0]])
+# self.replace_child(0, bbox)
+# intervalx = property(BboxBase._get_intervalx, _set_intervalx)
+
+# def _set_intervaly(self, pair):
+# # MGDTODO: Optimize
+# bbox = Bbox([[0.0, pair[0]], [0.0, pair[1]]])
+# self.replace_child(1, bbox)
+# intervaly = property(BboxBase._get_intervaly, _set_intervaly)
+
class Transform(TransformNode):
def __init__(self):
- TransformNode.__init__(self)
+ TransformNode.__init__(self)
def __call__(self, points):
- raise NotImplementedError()
+ raise NotImplementedError()
def __add__(self, other):
- if isinstance(other, Transform):
- return composite_transform_factory(self, other)
- raise TypeError("Can not add Transform to object of type '%s'" % type(other))
+ if isinstance(other, Transform):
+ return composite_transform_factory(self, other)
+ raise TypeError(
+ "Can not add Transform to object of type '%s'" % type(other))
def __radd__(self, other):
- if isinstance(other, Transform):
- return composite_transform_factory(other, self)
- raise TypeError("Can not add Transform to object of type '%s'" % type(other))
+ if isinstance(other, Transform):
+ return composite_transform_factory(other, self)
+ raise TypeError(
+ "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.__call__(npy.asarray([point]))[0]
def has_inverse(self):
- raise NotImplementedError()
+ raise NotImplementedError()
def inverted(self):
- raise NotImplementedError()
+ raise NotImplementedError()
def is_separable(self):
- return False
+ return False
def is_affine(self):
- return False
+ return False
class Affine2DBase(Transform):
input_dims = 2
output_dims = 2
def __init__(self):
- Transform.__init__(self)
- self._inverted = None
+ Transform.__init__(self)
+ self._inverted = None
def _do_invalidation(self):
- result = self._inverted is None
- self._inverted = None
- return result
+ result = self._inverted is None
+ self._inverted = None
+ return result
#@staticmethod
def _concat(a, b):
return npy.dot(b, a)
_concat = staticmethod(_concat)
+
+ #@staticmethod
+ def concat(a, b):
+ return Affine2D(Affine2D._concat(a.get_matrix(), b.get_matrix()))
+ concat = staticmethod(concat)
def to_values(self):
- mtx = self.get_matrix()
- return tuple(mtx[:2].swapaxes(0, 1).flatten())
+ mtx = self.get_matrix()
+ return tuple(mtx[:2].swapaxes(0, 1).flatten())
#@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
+ affine = npy.zeros((3, 3), npy.float_)
+ affine[0, ] = a, c, e
+ affine[1, ] = b, d, f
+ affine[2, 2] = 1
+ return affine
matrix_from_values = staticmethod(matrix_from_values)
def get_matrix(self):
- raise NotImplementedError()
+ raise NotImplementedError()
def __call__(self, points):
"""
Applies the transformation to an array of 2D points and
- returns the result.
+ returns the result.
- points must be a numpy array of shape (N, 2), where N is the
- number of points.
- """
- # 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.
- mtx = self.get_matrix()
- points = npy.asarray(points, npy.float_)
- points = points.transpose()
- points = npy.dot(mtx[0:2, 0:2], points)
- points = points + mtx[0:2, 2:]
- return points.transpose()
+ points must be a numpy array of shape (N, 2), where N is the
+ number of points.
+ """
+ # 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 = npy.asarray(points, npy.float_)
+ points = points.transpose()
+ points = npy.dot(mtx[0:2, 0:2], points)
+ points = points + mtx[0:2, 2:]
+ return points.transpose()
def inverted(self):
- if self._inverted is None:
- mtx = self.get_matrix()
- self._inverted = Affine2D(inv(mtx))
- return self._inverted
+ 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
+ mtx = self.get_matrix()
+ return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
def is_affine(self):
- return True
+ return True
-
+
class Affine2D(Affine2DBase):
input_dims = 2
output_dims = 2
@@ -310,29 +487,29 @@
b d f
0 0 1
"""
- Affine2DBase.__init__(self)
- if matrix is None:
- matrix = npy.identity(3)
- else:
- assert matrix.shape == (3, 3)
- self._mtx = matrix
- self._inverted = None
+ Affine2DBase.__init__(self)
+ if matrix is None:
+ matrix = npy.identity(3)
+ else:
+ assert matrix.shape == (3, 3)
+ self._mtx = matrix
+ self._inverted = None
def __repr__(self):
- return "Affine2D(%s)" % repr(self._mtx)
+ return "Affine2D(%s)" % repr(self._mtx)
__str__ = __repr__
def __cmp__(self, other):
- if (isinstance(other, Affine2D) and
- (self.get_matrix() == other.get_matrix()).all()):
- return 0
- return -1
+ if (isinstance(other, Affine2D) and
+ (self.get_matrix() == other.get_matrix()).all()):
+ return 0
+ return -1
def __copy__(self):
- return Affine2D(self._mtx.copy())
+ return Affine2D(self._mtx.copy())
def __deepcopy__(self, memo):
- return Affine2D(self._mtx.copy())
+ return Affine2D(self._mtx.copy())
#@staticmethod
def from_values(a, b, c, d, e, f):
@@ -340,209 +517,224 @@
from_values = staticmethod(from_values)
def get_matrix(self):
- return self._mtx
+ 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 concat(a, b):
- return Affine2D(Affine2D._concat(a._mtx, b._mtx))
- concat = staticmethod(concat)
-
- #@staticmethod
def identity():
return Affine2D(npy.identity(3))
identity = staticmethod(identity)
+ def clear(self):
+ self._mtx = npy.identity(3)
+ self.invalidate()
+ return self
+
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)
- self.invalidate()
- return self
+ self.invalidate()
+ return self
def rotate_deg(self, degrees):
return self.rotate(degrees*npy.pi/180.)
+ def rotate_around(self, x, y, theta):
+ return self.translate(-x, -y).rotate(theta).translate(x, y)
+
+ def rotate_deg_around(self, x, y, degrees):
+ 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)
- self.invalidate()
- return self
+ 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.)
+ 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)
- self.invalidate()
- return self
+ self.invalidate()
+ return self
def inverted(self):
- if self._inverted is None:
- mtx = self.get_matrix()
- self._inverted = Affine2D(inv(mtx))
- return self._inverted
+ 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
+ mtx = self.get_matrix()
+ return mtx[0, 1] == 0.0 and mtx[1, 0] == 0.0
def is_affine(self):
- return True
+ return True
-class BlendedAffine2D(Affine2DBase):
+class BlendedTransform(Transform):
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_separable()
+ assert y_transform.is_separable()
- Affine2DBase.__init__(self)
- self.add_children([x_transform, y_transform])
- self._x = x_transform
- self._y = y_transform
- self._mtx = None
+ 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:
+ 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)
+
+# def set_y_transform(self, y_transform):
+# self.replace_child(1, y_transform)
+
+class BlendedAffine2D(Affine2DBase, BlendedTransform):
+ 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()
+ BlendedTransform.__init__(self, x_transform, y_transform)
+
+ Affine2DBase.__init__(self)
+ self._mtx = None
+
def __repr__(self):
- return "BlendedAffine2D(%s,%s)" % (self._x, self._y)
+ return "BlendedAffine2D(%s,%s)" % (self._x, self._y)
__str__ = __repr__
-
+
def _do_invalidation(self):
- if self._mtx is not None:
- self._mtx = None
- Affine2DBase._do_invalidation(self)
- return False
- return True
+ if self._mtx is not None:
+ self._mtx = None
+ Affine2DBase._do_invalidation(self)
+ return False
+ return True
- def _make__mtx(self):
- if self._mtx is None:
- x_mtx = self._x.get_matrix()
- y_mtx = self._y.get_matrix()
- # This works because we already know the transforms are
- # 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]))
-
def is_separable(self):
- return True
+ return True
def get_matrix(self):
- self._make__mtx()
- return self._mtx
+ if self._mtx is None:
+ if self._x == self._y:
+ self._mtx = self._x.get_matrix()
+ else:
+ x_mtx = self._x.get_matrix()
+ y_mtx = self._y.get_matrix()
+ # This works because we already know the transforms are
+ # 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]))
+ return self._mtx
+
+class CompositeTransform(Transform):
+ def __init__(self, a, b):
+ assert a.output_dims == b.input_dims
+ self.input_dims = a.input_dims
+ self.output_dims = b.output_dims
+
+ Transform.__init__(self)
+ self._a = a
+ self._b = b
+ self.set_children(['_a', '_b'])
+
+ def __call__(self, points):
+ return self._b(self._a(points))
-class BlendedTransform(Transform):
- def __init__(self, x_transform, y_transform):
- assert x_transform.is_separable()
- assert y_transform.is_separable()
-
- Transform.__init__(self)
- self.add_children([x_transform, y_transform])
- self._x = x_transform
- self._y = y_transform
-
- def __call__(self, 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]))
-
class CompositeAffine2D(Affine2DBase):
def __init__(self, a, b):
- assert a.is_affine()
- assert b.is_affine()
+ assert a.is_affine()
+ assert b.is_affine()
- Affine2DBase.__init__(self)
- self.add_children([a, b])
- self._a = a
- self._b = b
- self._mtx = None
+ Affine2DBase.__init__(self)
+ self._a = a
+ self._b = b
+ self.set_children(['_a', '_b'])
+ self._mtx = None
def __repr__(self):
- return "CompositeAffine2D(%s, %s)" % (self._a, self._b)
+ return "CompositeAffine2D(%s, %s)" % (self._a, self._b)
__str__ = __repr__
def _do_invalidation(self):
- self._mtx = None
- Affine2DBase._do_invalidation(self)
+ self._mtx = None
+ Affine2DBase._do_invalidation(self)
- def _make__mtx(self):
- if self._mtx is None:
- self._mtx = self._concat(
- self._a.get_matrix(),
- self._b.get_matrix())
-
def get_matrix(self):
- self._make__mtx()
- return self._mtx
-
-class CompositeTransform(Transform):
- def __init__(self, a, b):
- assert a.output_dims == b.input_dims
- self.input_dims = a.input_dims
- self.output_dims = b.output_dims
-
- Transform.__init__(self)
- self.add_children([a, b])
- self._a = a
- self._b = b
+ if self._mtx is None:
+ self._mtx = self._concat(
+ self._a.get_matrix(),
+ self._b.get_matrix())
+ return self._mtx
- def __call__(self, points):
- return self._b(self._a(points))
-
class BboxTransform(Affine2DBase):
def __init__(self, boxin, boxout):
- assert isinstance(boxin, Bbox)
- assert isinstance(boxout, Bbox)
+ assert isinstance(boxin, BboxBase)
+ assert isinstance(boxout, BboxBase)
- Affine2DBase.__init__(self)
- self.add_children([boxin, boxout])
- self._boxin = boxin
- self._boxout = boxout
- self._mtx = None
- self._inverted = None
+ Affine2DBase.__init__(self)
+ self._boxin = boxin
+ self._boxout = boxout
+ self.set_children(['_boxin', '_boxout'])
+ self._mtx = None
+ self._inverted = None
def __repr__(self):
- return "BboxTransform(%s, %s)" % (self._boxin, self._boxout)
+ return "BboxTransform(%s, %s)" % (self._boxin, self._boxout)
__str__ = __repr__
-
+
def _do_invalidation(self):
- if self._mtx is not None:
- self._mtx = None
- Affine2DBase._do_invalidation(self)
- return False
- return True
+ if self._mtx is not None:
+ self._mtx = None
+ Affine2DBase._do_invalidation(self)
+ return False
+ return True
- def _make__mtx(self):
- if self._mtx is None:
- boxin = self._boxin
- boxout = self._boxout
- x_scale = boxout.width / boxin.width
- y_scale = boxout.height / boxin.height
+ def is_separable(self):
+ return True
- # MGDTODO: Optimize
- affine = Affine2D() \
- .translate(-boxin.xmin, -boxin.ymin) \
- .scale(x_scale, y_scale) \
- .translate(boxout.xmin, boxout.ymin)
+ 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
- self._mtx = affine._mtx
-
- def is_separable(self):
- return True
+ # MGDTODO: Optimize
+ affine = Affine2D() \
+ .translate(-boxin.xmin, -boxin.ymin) \
+ .scale(x_scale, y_scale) \
+ .translate(boxout.xmin, boxout.ymin)
- def get_matrix(self):
- self._make__mtx()
- return self._mtx
-
+ self._mtx = affine._mtx
+ return self._mtx
+
def blend_xy_sep_transform(x_transform, y_transform):
if x_transform.is_affine() and y_transform.is_affine():
- return BlendedAffine2D(x_transform, y_transform)
+ return BlendedAffine2D(x_transform, y_transform)
return BlendedTransform(x_transform, y_transform)
def composite_transform_factory(a, b):
if a.is_affine() and b.is_affine():
- return CompositeAffine2D(a, b)
+ return CompositeAffine2D(a, b)
return CompositeTransform(a, b)
# MGDTODO: There's probably a better place for this
@@ -562,7 +754,7 @@
vmin, vmax = vmax, vmin
swapped = True
if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny:
- if vmin==0.0:
+ if vmin == 0.0:
vmin = -expander
vmax = expander
else:
@@ -635,8 +827,8 @@
assert scale.to_values() == (10, 0, 0, 20, 0, 0)
rotation = Affine2D().rotate_deg(30)
print rotation.to_values() == (0.86602540378443871, 0.49999999999999994,
- -0.49999999999999994, 0.86602540378443871,
- 0.0, 0.0)
+ -0.49999999999999994, 0.86602540378443871,
+ 0.0, 0.0)
points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_)
translated_points = translation(points)
Modified: branches/transforms/lib/matplotlib/artist.py
===================================================================
--- branches/transforms/lib/matplotlib/artist.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/artist.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -21,7 +21,7 @@
# http://groups.google.com/groups?hl=en&lr=&threadm=mailman.5090.1098044946.5135.python-list%40python.org&rnum=1&prev=/groups%3Fq%3D__doc__%2Bauthor%253Ajdhunter%2540ace.bsd.uchicago.edu%26hl%3Den%26btnG%3DGoogle%2BSearch
-class Artist:
+class Artist(object):
"""
Abstract base class for someone who renders into a FigureCanvas
"""
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -436,8 +436,7 @@
}
def __str__(self):
- return "Axes(%g,%g;%gx%g)"%(self._position[0].get(),self._position[1].get(),
- self._position[2].get(),self._position[3].get())
+ return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
def __init__(self, fig, rect,
axisbg = None, # defaults to rc axes.facecolor
frameon = True,
@@ -590,13 +589,13 @@
def follow_foreign_ylim(ax):
ymin, ymax = axforeign.get_ylim()
- # do not emit here or we'll get a ping png effect
+ # do not emit here or we'll get a ping pong effect
self.set_ylim(ymin, ymax, emit=False)
self.figure.canvas.draw_idle()
def follow_self_ylim(ax):
ymin, ymax = self.get_ylim()
- # do not emit here or we'll get a ping png effect
+ # do not emit here or we'll get a ping pong effect
axforeign.set_ylim(ymin, ymax, emit=False)
axforeign.figure.canvas.draw_idle()
@@ -613,65 +612,66 @@
"""
martist.Artist.set_figure(self, fig)
- l, b, w, h = self._position.bounds
- xmin = fig.bbox.xmin
- xmax = fig.bbox.xmax
- ymin = fig.bbox.ymin
- ymax = fig.bbox.ymax
- figw = xmax-xmin
- figh = ymax-ymin
- self.left = l*figw
- self.bottom = b*figh
- self.right = (l+w)*figw
- self.top = (b+h)*figh
-
- self.bbox = maffine.Bbox.from_lbrt(
- self.left, self.bottom,
- self.right, self.top,
- )
+ self.bbox = maffine.TransformedBbox(self._position, fig.transFigure)
#these will be updated later as data is added
self._set_lim_and_transforms()
+ def _shared_xlim_callback(self, ax):
+ xmin, xmax = ax.get_xlim()
+ self.set_xlim(xmin, xmax, emit=False)
+ self.figure.canvas.draw_idle()
+
+ def _shared_ylim_callback(self, ax):
+ ymin, ymax = ax.get_ylim()
+ self.set_ylim(ymin, ymax, emit=False)
+ self.figure.canvas.draw_idle()
+
def _set_lim_and_transforms(self):
"""
set the dataLim and viewLim BBox attributes and the
transData and transAxes Transformation attributes
"""
- Bbox = maffine.Bbox
+ Bbox = maffine.Bbox
+ self.viewLim = Bbox.unit()
+
if self._sharex is not None:
- left = self._sharex.viewLim.xmin()
- right = self._sharex.viewLim.xmax()
- else:
- left = 0.0
- right = 1.0
+ # MGDTODO: This may be doing at least one too many updates
+ # than necessary
+ self._sharex.callbacks.connect(
+ 'xlim_changed', self._shared_xlim_callback)
+ self.viewLim.intervalx = self._sharex.viewLim.intervalx
if self._sharey is not None:
- bottom = self._sharey.viewLim.ymin()
- top = self._sharey.viewLim.ymax()
- else:
- bottom = 0.0
- top = 1.0
+ self._sharey.callbacks.connect(
+ 'ylim_changed', self._shared_ylim_callback)
+ self.viewLim.intervaly = self._sharex.viewLim.intervaly
- self.viewLim = Bbox.from_lbrt(left, bottom, right, top)
self.dataLim = Bbox.unit()
- self.transData = maffine.BboxTransform(
- self.viewLim, self.bbox)
self.transAxes = maffine.BboxTransform(
Bbox.unit(), self.bbox)
- # MGDTODO
-# if self._sharex:
-# self.transData.set_funcx(self._sharex.transData.get_funcx())
-
-# if self._sharey:
-# self.transData.set_funcy(self._sharey.transData.get_funcy())
-
+ localTransData = maffine.BboxTransform(
+ self.viewLim, self.bbox)
+ if self._sharex:
+ transDataX = self._sharex.transData
+ else:
+ transDataX = localTransData
+ if self._sharey:
+ transDataY = self._sharey.transData
+ else:
+ transDataY = localTransData
+ self.transData = localTransData # maffine.blend_xy_sep_transform(transDataX, transDataY)
+
+
def get_position(self, original=False):
'Return the axes rectangle left, bottom, width, height'
+ # MGDTODO: This changed from returning a list to returning a Bbox
+ # If you get any errors with the result of this function, please
+ # update the calling code
if original:
- return self._originalPosition.bounds
+ return copy.copy(self._originalPosition)
else:
- return self._position.bounds
+ return copy.copy(self._position)
# return [val.get() for val in self._position]
def set_position(self, pos, which='both'):
@@ -690,14 +690,9 @@
ACCEPTS: len(4) sequence of floats
"""
if which in ('both', 'active'):
- # MGDTODO
-# # Change values within self._position--don't replace it.
-# for num,val in zip(pos, self._position):
-# val.set(num)
- self._position.bounds = pos.bounds
- # MGDTODO: side-effects
+ self._position.set(pos)
if which in ('both', 'original'):
- self._originalPosition.bounds = pos.bounds
+ self._originalPosition.set(pos)
def _set_artist_props(self, a):
@@ -1546,7 +1541,14 @@
xmin, xmax = maffine.nonsingular(xmin, xmax, increasing=False)
self.viewLim.intervalx = (xmin, xmax)
-
+ if emit:
+ self.callbacks.process('xlim_changed', self)
+ # MGDTODO: It would be nice to do this is in the above callback list,
+ # but it's difficult to tell how to initialize this at the
+ # right time
+ if self._sharex:
+ self._sharex.set_xlim(*self.viewLim.intervalx)
+
return xmin, xmax
def get_xscale(self):
@@ -1650,7 +1652,6 @@
ACCEPTS: len(2) sequence of floats
"""
-
if ymax is None and iterable(ymin):
ymin,ymax = ymin
@@ -1671,8 +1672,14 @@
ymin, ymax = maffine.nonsingular(ymin, ymax, increasing=False)
self.viewLim.intervaly = (ymin, ymax)
- if emit: self.callbacks.process('ylim_changed', self)
-
+ if emit:
+ self.callbacks.process('ylim_changed', self)
+ # MGDTODO: It would be nice to do this is in the above callback list,
+ # but it's difficult to tell how to initialize this at the
+ # right time
+ if self._sharey:
+ self._sharey.set_ylim(*self.viewLim.intervaly)
+
return ymin, ymax
def get_yscale(self):
Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/axis.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -1028,9 +1028,8 @@
x,y = self.label.get_position()
if self.label_position == 'bottom':
if not len(bboxes):
- bottom = self.axes.bbox.ymin()
+ bottom = self.axes.bbox.ymin
else:
-
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
@@ -1041,7 +1040,6 @@
if not len(bboxes2):
top = self.axes.bbox.ymax
else:
-
bbox = bbox_union(bboxes2)
top = bbox.ymax
@@ -1054,7 +1052,7 @@
"""
x,y = self.offsetText.get_position()
if not len(bboxes):
- bottom = self.axes.bbox.ymin()
+ bottom = self.axes.bbox.ymin
else:
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -752,7 +752,8 @@
else: # Just found one hit
self.inaxes = axes_list[0]
- try: xdata, ydata = self.inaxes.transData.inverted()([[x, y]])[0]
+ try:
+ xdata, ydata = self.inaxes.transData.inverted().transform_point((x, y))
except ValueError:
self.xdata = None
self.ydata = None
@@ -1584,8 +1585,8 @@
lims.append( (xmin, xmax, ymin, ymax) )
# Store both the original and modified positions
pos.append( (
- tuple( a.get_position(True) ),
- tuple( a.get_position() ) ) )
+ a.get_position(True),
+ a.get_position() ) )
self._views.push(lims)
self._positions.push(pos)
self.set_history_buttons()
Modified: branches/transforms/lib/matplotlib/figure.py
===================================================================
--- branches/transforms/lib/matplotlib/figure.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/figure.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -18,7 +18,7 @@
from text import Text, _process_text_args
from legend import Legend
-from affine import Bbox, BboxTransform
+from affine import Affine2D, Bbox, BboxTransform, TransformedBbox
from ticker import FormatStrFormatter
from cm import ScalarMappable
from contour import ContourSet
@@ -128,17 +128,15 @@
if facecolor is None: facecolor = rcParams['figure.facecolor']
if edgecolor is None: edgecolor = rcParams['figure.edgecolor']
+ self._dpi_scale_trans = Affine2D()
self.dpi = dpi
- figwidth = figsize[0] * dpi
- figheight = figsize[1] * dpi
- self.bbox = Bbox.from_lbwh(0, 0, figwidth, figheight)
+ self.bbox_inches = Bbox.from_lbwh(0, 0, *figsize)
+ self.bbox = TransformedBbox(self.bbox_inches, self._dpi_scale_trans)
self.frameon = frameon
- self.transFigure = BboxTransform( Bbox.unit(), self.bbox)
+ self.transFigure = BboxTransform(Bbox.unit(), self.bbox)
-
-
self.figurePatch = Rectangle(
xy=(0,0), width=1, height=1,
facecolor=facecolor, edgecolor=edgecolor,
@@ -160,6 +158,15 @@
self._cachedRenderer = None
+ def _get_dpi(self):
+ return self._dpi
+ def _set_dpi(self, dpi):
+ print "setting dpi"
+ self._dpi = dpi
+ self._dpi_scale_trans.clear().scale(dpi, dpi)
+ print self._dpi_scale_trans
+ dpi = property(_get_dpi, _set_dpi)
+
def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'):
"""
A common use case is a number of subplots with shared xaxes
@@ -325,7 +332,7 @@
w,h = args
dpival = self.dpi
- self.bbox.max = w * dpival, h * dpival
+ self.bbox_inches.max = w, h
# self.figwidth.set(w) MGDTODO
# self.figheight.set(h)
@@ -339,7 +346,7 @@
manager.resize(int(canvasw), int(canvash))
def get_size_inches(self):
- return self.bbox.max
+ return self.bbox_inches.max
# return self.figwidth.get(), self.figheight.get() MGDTODO
def get_edgecolor(self):
@@ -352,12 +359,12 @@
def get_figwidth(self):
'Return the figwidth as a float'
- return self.bbox.xmax
+ return self.bbox_inches.xmax
# return self.figwidth.get() MGDTODO
def get_figheight(self):
'Return the figheight as a float'
- return self.bbox.ymax
+ return self.bbox_inches.ymax
def get_dpi(self):
'Return the dpi as a float'
@@ -400,7 +407,7 @@
ACCEPTS: float
"""
# self.figwidth.set(val) MGDTODO
- self.bbox.xmax = val
+ self.bbox_inches.xmax = val
def set_figheight(self, val):
"""
@@ -409,7 +416,7 @@
ACCEPTS: float
"""
# MGDTODO (set())
- self.bbox.ymax = val
+ self.bbox_inches.ymax = val
def set_frameon(self, b):
"""
Modified: branches/transforms/lib/matplotlib/pyplot.py
===================================================================
--- branches/transforms/lib/matplotlib/pyplot.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/pyplot.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -481,7 +481,7 @@
byebye = []
for other in fig.axes:
if other==a: continue
- if bbox.overlaps(other.bbox, ignoreend=True):
+ if bbox.fully_overlaps(other.bbox):
byebye.append(other)
for ax in byebye: delaxes(ax)
Modified: branches/transforms/lib/matplotlib/text.py
===================================================================
--- branches/transforms/lib/matplotlib/text.py 2007-09-13 12:50:05 UTC (rev 3847)
+++ branches/transforms/lib/matplotlib/text.py 2007-09-13 18:00:10 UTC (rev 3848)
@@ -15,7 +15,7 @@
from cbook import enumerate, is_string_like, maxdict, is_numlike
from font_manager import FontProperties
from patches import bbox_artist, YAArrow
-from affine import Bbox
+from affine import Affine2D, Bbox
from lines import Line2D
import matplotlib.nxutils as nxutils
@@ -213,30 +213,32 @@
M = self.get_rotation_matrix(xmin, ymin)
# the corners of the unrotated bounding box
- cornersHoriz = ( (xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin) )
- offsetLayout = []
+ cornersHoriz = npy.array(
+ [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)],
+ npy.float_)
+ offsetLayout = npy.zeros((len(lines), 2))
# now offset the individual text lines within the box
if len(lines)>1: # do the multiline aligment
malign = self._get_multialignment()
- for line, thisx, thisy, w, h in horizLayout:
+ for i, (line, thisx, thisy, w, h) in enumerate(horizLayout):
if malign=='center': offsetx = width/2.0-w/2.0
elif malign=='right': offsetx = width-w
else: offsetx = 0
thisx += offsetx
- offsetLayout.append( (thisx, thisy ))
+ offsetLayout[i] = (thisx, thisy)
else: # no additional layout needed
- offsetLayout = [ (thisx, thisy) for line, thisx, thisy, w, h in horizLayout]
+ offsetLayout[0] = horizLayout[0][1:3]
# now rotate the bbox
- cornersRotated = [npy.dot(M,npy.array([[thisx],[thisy],[1]])) for thisx, thisy in cornersHoriz]
+ cornersRotated = M(cornersHoriz)
- txs = [float(v[0][0]) for v in cornersRotated]
- tys = [float(v[1][0]) for v in cornersRotated]
+ txs = cornersRotated[:, 0]
+ tys = cornersRotated[:, 1]
# compute the bounds of the rotated box
- xmin, xmax = min(txs), max(txs)
- ymin, ymax = min(tys), max(tys)
+ xmin, xmax = txs.min(), txs.max()
+ ymin, ymax = tys.min(), tys.max()
width = xmax - xmin
height = ymax - ymin
@@ -264,17 +266,18 @@
bbox = Bbox.from_lbwh(xmin, ymin, width, height)
+
# now rotate the positions around the first x,y position
- xys = [npy.dot(M,npy.array([[thisx],[thisy],[1]])) for thisx, thisy in offsetLayout]
+ xys = M(offsetLayout)
+ tx = xys[:, 0]
+ ty = xys[:, 1]
+ tx += offsetx
+ ty += offsety
-
- tx = [float(v[0][0])+offsetx for v in xys]
- ty = [float(v[1][0])+offsety for v in xys]
-
# now inverse transform back to data coords
inverse_transform = self.get_transform().inverted()
- xys = inverse_transform(zip(tx, ty))
+ xys = inverse_transform(xys)
xs, ys = zip(*xys)
@@ -327,7 +330,7 @@
return
for line, wh, x, y in info:
- x, y = trans([[x, y]])[0]
+ x, y = trans.transform_point((x, y))
if renderer.flipy():
canvasw, canvash = renderer.get_canvas_width_height()
@@ -435,29 +438,9 @@
bbox, info = self._get_layout(self._renderer)
return bbox
-
-
def get_rotation_matrix(self, x0, y0):
+ return Affine2D().rotate_deg_around(x0, y0, self.get_rotation())
- theta = npy.pi/180.0*self.get_rotation()
- # translate x0,y0 to origin
- Torigin = npy.array([ [1, 0, -x0],
- [0, 1, -y0],
- [0, 0, 1 ]])
-
- # rotate by theta
- R = npy.array([ [npy.cos(theta), -npy.sin(theta), 0],
- [npy.sin(theta), npy.cos(theta), 0],
- [0, 0, 1]])
-
- # translate origin back to x0,y0
- Tback = npy.array([ [1, 0, x0],
- [0, 1, y0],
- [0, 0, 1 ]])
-
-
- return npy.dot(npy.dot(Tback,R), Torigin)
-
def set_backgroundcolor(self, color):
"""
Set the background color of the text by updating the bbox (see set_bbox for more info)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|