Hi,
Attached are two patch sets for you to review and comment on. I am not
intending for these to go in this release.
One is the beginning of a patch set that lets you choose whether text
rotation angle is with respect to screen or axes coordinates. The idea
is that you might want text that is properly rotated with respect to
some object in a plot (e.g., contours), as opposed to being at some
particular angle with respect to screen coordinates. Along the way I
added a method to transforms.py that transforms angles at a location.
The method included is generic and not at all optimized for simple
linear transforms, but it works fairly well. This transform might be
useful for other functions (e.g., quiver?).
This patch basically works. For example, try the following:
plot(arange(5))
th = text(3,3,'abcd',rotation=45,rotationscreen=False)
th2 = text(2,2,'ABCD',rotation=45)
and then try changing the size of your plot window. 'abcd' doesn't
rotate with respect to the line as the window changes size, but 'ABCD'
does. There are still some imperfections - the text seems to move above
or below the line - I am not sure why this is. Also, I have no idea
whether I should be using unitless (self._x,_y) or unitful coordinates
(self.convert_(x|y)units) in the transformations. Perhaps someone can
enlighten me. It doesn't really matter until one tries non-linear
transformations (aka basemap).
I also haven't tried to integrate this into TextWithDash. I imagine it
can be done, but I wasn't sure it was worth the effort since
TextWithDash is mostly used for axes ticks I believe. I am thinking of
just forcing rotationscreen to True for this class. Comments?
Once these issues are worked out, I would integrate this into contour.py
so that windows can be resized without affecting label rotation.
The second patch is to pyplot.py to create a plotyy function. This is
like a matlab function of the same name that puts two curves with
different y ranges on the same x axis. It basically wraps the
two_scales.py demo functionality with a bit of extra stuff. I had to
use a real hack to change the colors of the y axes. Perhaps someone can
think of a better way or perhaps this sub-function should be moved out
of plotyy so it can be reused. Also, I couldn't find a way to color the
actual y-axis - i.e. the vertical line that is the y-axis. Is there an
easy way to do this?
Cheers,
David
--
**********************************
David M. Kaplan
Charge de Recherche 1
Institut de Recherche pour le Developpement
Centre de Recherche Halieutique Mediterraneenne et Tropicale
av. Jean Monnet
B.P. 171
34203 Sete cedex
France
Phone: +33 (0)4 99 57 32 27
Fax: +33 (0)4 99 57 32 95
http://www.ur097.ird.fr/team/dkaplan/index.html
**********************************
Index: lib/matplotlib/text.py
===================================================================
--- lib/matplotlib/text.py (revision 5867)
+++ lib/matplotlib/text.py (working copy)
@@ -62,6 +62,7 @@
name or fontname string eg, ['Sans' | 'Courier' | 'Helvetica' ...]
position (x,y)
rotation [ angle in degrees 'vertical' | 'horizontal'
+ rotationscreen [ True | False ]
size or fontsize [ size in points | relative size eg 'smaller', 'x-large' ]
style or fontstyle [ 'normal' | 'italic' | 'oblique']
text string
@@ -93,6 +94,7 @@
multialignment=None,
fontproperties=None, # defaults to FontProperties()
rotation=None,
+ rotationscreen=True,
linespacing=None,
**kwargs
):
@@ -118,6 +120,7 @@
self._horizontalalignment = horizontalalignment
self._multialignment = multialignment
self._rotation = rotation
+ self._rotationscreen = rotationscreen
self._fontproperties = fontproperties
self._bbox = None
self._renderer = None
@@ -159,6 +162,11 @@
'return the text angle as float'
return get_rotation(self._rotation) # string_or_number -> number
+ def get_rotationscreen(self):
+ '''return whether rotation is with respect to screen (True) or
+ plot coordinate system'''
+ return self._rotationscreen
+
def update_from(self, other):
'Copy properties from other to self'
Artist.update_from(self, other)
@@ -168,6 +176,7 @@
self._horizontalalignment = other._horizontalalignment
self._fontproperties = other._fontproperties.copy()
self._rotation = other._rotation
+ self._rotationscreen = other._rotationscreen
self._picker = other._picker
self._linespacing = other._linespacing
@@ -207,8 +216,18 @@
height = ymax-ymin
xmax = xmin + width
+ # Get proper rotation angle
+ angle = self.get_rotation()
+ if not self.get_rotationscreen():
+ trans = self.get_transform()
+ posx = float(self.convert_xunits(self._x))
+ posy = float(self.convert_yunits(self._y))
+
+ angle = trans.transform_angles( np.array([[posx,posy]]),
+ np.array([angle]) )[0]
+
# get the rotation matrix
- M = Affine2D().rotate_deg(self.get_rotation())
+ M = Affine2D().rotate_deg(angle)
offsetLayout = np.zeros((len(lines), 2))
offsetLayout[:] = horizLayout[:, 0:2]
@@ -302,6 +321,11 @@
posx = float(self.convert_xunits(self._x))
posy = float(self.convert_yunits(self._y))
+ # If we desire, set rotation angle once
+ if not self.get_rotationscreen():
+ angle = trans.transform_angles( np.array([[posx,posy]]),
+ np.array([angle]) )
+
posx, posy = trans.transform_point((posx, posy))
canvasw, canvash = renderer.get_canvas_width_height()
@@ -398,7 +422,8 @@
return (x, y, self._text, self._color,
self._verticalalignment, self._horizontalalignment,
hash(self._fontproperties), self._rotation,
- self._renderer.dpi, id(self._renderer)
+ self._renderer.dpi, id(self._renderer),
+ self._rotationscreen
)
def get_text(self):
@@ -600,8 +625,17 @@
"""
self._rotation = s
+ def set_rotationscreen(self, s):
+ """
+ Determines whether rotation angle is with respect to screen (True)
+ or axes coordinate system (False).
+ ACCEPTS: [ True | False ]
+ """
+ self._rotationscreen = s
+
+
def set_va(self, align):
'alias for set_verticalalignment'
self.set_verticalalignment(align)
Index: lib/matplotlib/transforms.py
===================================================================
--- lib/matplotlib/transforms.py (revision 5867)
+++ lib/matplotlib/transforms.py (working copy)
@@ -1021,6 +1021,45 @@
"""
raise NotImplementedError()
+ def transform_angles( self, pts, angles, radians=False, pushoff=1e-5 ):
+ """
+ Performs transformation on a set of directions from a set of points.
+
+ For positions, must be a two column numpy array of x,y positions.
+ Angle transform currently only works in 2D.
+
+ For angles, should be a numpy array with the same number of
+ rows as the positions.
+
+ This uses a very generic algorithm that transforms origin and
+ nearby points to determine angle in transformed system.
+ """
+
+ # Must be 2D
+ if self.input_dims <> 2 or self.output_dims <> 2:
+ raise NotImplementedError('Only defined in 2D')
+
+ # Convert to radians
+ if not radians:
+ angles = angles / 180.0 * np.pi
+
+ # Move a short distance away
+ pts2 = pts + pushoff * np.c_[ np.cos(angles), np.sin(angles) ]
+
+ # Transform both sets of points
+ tpts = self.transform( pts )
+ tpts2 = self.transform( pts2 )
+
+ # Calculate transformed angles
+ d = tpts2 - tpts
+ a = np.arctan2( d[:,1], d[:,0] )
+
+ # Convert back to degrees if desired
+ if not radians:
+ a = a * 180.0 / np.pi
+
+ return a
+
def transform_affine(self, values):
"""
Performs only the affine part of this transformation on the
Index: lib/matplotlib/pyplot.py
===================================================================
--- lib/matplotlib/pyplot.py (revision 5867)
+++ lib/matplotlib/pyplot.py (working copy)
@@ -644,8 +644,74 @@
draw_if_interactive()
return ax.twiny()
+def plotyy( x1, y1, x2, y2, color1='b', color2='g', fun=None, **kwargs ):
+ """
+ A work-alike of the Matlab (TM) function of the same name. This
+ places two curves on the same axes using the same x-axis, but
+ different y-axes.
+ Call signature::
+ ax, h1, h2 = plotyy( x1, y2, x2, y2, color1='b', color2='g',
+ fun=None, **kwargs )
+
+ color1 and color2 are the colors to make respective curves and y-axes.
+
+ fun is the function object to use for plotting. Must accept calls
+ of the form fun(x,y,color='color',**kwargs). Typically, something
+ like plot, semilogy, semilogx or loglog. If *None*, defaults to
+ pyplot.plot.
+
+ **kwargs is any list of keyword arguments accepted by fun.
+
+ ax is a 2 element list with the handles for the first and second
+ axes. h1 is the handle to the first curve, h2 to the second
+ curve.
+
+ NOTE that this function won't scale two curves so that y-ticks are
+ in the same location as the Matlab (TM) version does.
+ """
+ if fun == None: fun = plot
+
+ ax1 = gca()
+ ax1.clear()
+
+ # Get axes location
+ rect = ax1.get_position().bounds
+
+ # Add first curve
+ h1 = fun( x1, y1, color=color1, **kwargs )
+
+ # Add second axes on top of first with joined x-axis
+ ax2 = twinx(ax1)
+
+ # Plot second curve initially
+ h2 = fun( x2, y2, color=color2, **kwargs )
+
+ # Set axis properties
+ setp( ax2.get_xticklabels(), visible=False)
+
+ # Change colors appropriately
+ def recolor( obj, col ):
+ try: obj.set_color( col )
+ except: pass
+ try: obj.set_facecolor( col )
+ except: pass
+ try: obj.set_edgecolor( col )
+ except: pass
+ try:
+ ch = obj.get_children()
+ for c in ch:
+ recolor( c, col )
+ except: pass
+
+ recolor( ax1.yaxis, color1 )
+ recolor( ax2.yaxis, color2 )
+
+ draw_if_interactive()
+
+ return ( [ax1,ax2], h1, h2 )
+
def subplots_adjust(*args, **kwargs):
"""
call signature::
@@ -2354,7 +2420,7 @@
except:
hold(b)
raise
-
+
hold(b)
return ret
if Axes.barbs.__doc__ is not None:
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel