I'm trying to get some "pretty" arrows for graphs and other uses in 
Sage.  One of the problems we've been having with the FancyArrow and 
YAArrow is that the arrow is skewed when the aspect ratio is not 1:1 and 
it is scaled along with the plot.  I've written the attached ArrowLine 
class which basically modifies the marker drawing code to draw an 
arrowhead at the end of a Line2D.  It doesn't suffer either of these 
problems; it works beautifully.

However, in drawing (vertex and line) graphs, we have another problem. 
The vertices of the graph are drawn using scatterplot, and I know the 
corresponding vertex size (in whatever units scatterplot uses).  I'd 
like to draw an arrow between the boundaries of the vertices.  Is there 
a way to shorten a line  that originally goes between the centers of two 
circles so that the line instead goes between the two boundaries of the 
circles?  Note that clipping the line isn't an option since I want to 
keep the arrowhead on the line instead of clipping it off.  I presume 
this shortening will have to be done in the drawing routine since it 
needs to be independent of zooming since the circles are drawn the same 
independent of zooming.

Another related issue is that width of the path used to draw the 
arrowhead makes the arrow tip go beyond the endpoint; is there a way to 
shorten a line by a certain number of points so that we can account for 
that?  Also, in drawing the arrowhead, the line pokes through the 
arrowhead; I'd like to shorten the shaft to the beginning of the arrowhead.

I think all three of these shortening questions are similar; I'd like to 
shorten an arrow in a scale-independent way (i.e., by a certain number 
of points or something).


The code I have for the ArrowLine class is below.  If people are 
interested, I could (eventually, as I have time) incorporate this 
functionality into the Line2D class (i.e., putting arrowheads on the 
ends of lines).


r"""
A matplotlib subclass to draw an arrowhead on a line.

AUTHORS:
     -- Jason Grout (2008-08-19): initial version
"""

############################################################################
#  Copyright (C) 2008 Jason Grout <[EMAIL PROTECTED]>
#  Released under the terms of the modified BSD License
############################################################################

import matplotlib
from matplotlib.path import Path
from matplotlib.lines import Line2D
import math
import matplotlib.cbook

class ArrowLine(Line2D):
     """
     A matplotlib subclass to draw an arrowhead on a line.

     EXAMPLE:
         sage: import pylab
         sage: fig = pylab.figure()
         sage: ax = fig.add_subplot(111, autoscale_on=False)
         sage: t = [-1,2]
         sage: s = [0,-1]
         sage: line = ArrowLine(t, s, color='b', ls='-', lw=2, 
arrow='>', arrowsize=20)
         sage: ax.add_line(line)
         sage: ax.set_xlim(-3,3)
         (-3, 3)
         sage: ax.set_ylim(-3,3)
         (-3, 3)
         sage: pylab.show()

     """

     arrows = {'>' : '_draw_triangle_arrow'}

     def __init__(self, *args, **kwargs):
         """Initialize the line and arrow."""
         self._arrow = kwargs.pop('arrow', None)
         self._arrowsize = kwargs.pop('arrowsize', 2*4)
         self._arrowedgecolor = kwargs.pop('arrowedgecolor', 'b')
         self._arrowfacecolor = kwargs.pop('arrowfacecolor', 'b')
         self._arrowedgewidth = kwargs.pop('arrowedgewidth', 4)
         self._arrowheadwidth = kwargs.pop('arrowheadwidth', 
self._arrowsize)
         self._arrowheadlength = kwargs.pop('arrowheadlength', 
self._arrowsize)
         Line2D.__init__(self, *args, **kwargs)



     def draw(self, renderer):
         """Draw the line and arrowhead using the passed renderer."""
         if self._invalid:
             self.recache()
         renderer.open_group('arrowline2d')
         if not self._visible: return

         Line2D.draw(self, renderer)

         if self._arrow is not None:
             gc = renderer.new_gc()
             self._set_gc_clip(gc)
             gc.set_foreground(self._arrowedgecolor)
             gc.set_linewidth(self._arrowedgewidth)
             gc.set_alpha(self._alpha)
             funcname = self.arrows.get(self._arrow, '_draw_nothing')
             if funcname != '_draw_nothing':
                 tpath, affine = 
self._transformed_path.get_transformed_points_and_affine()
                 arrowFunc = getattr(self, funcname)
                 arrowFunc(renderer, gc, tpath, affine.frozen())

         renderer.close_group('arrowline2d')

     _arrow_path = Path([[0.0, 0.0], [-1.0, 1.0], [-1.0, -1.0], [0.0, 
0.0]], codes=[Path.MOVETO, Path.LINETO,Path.LINETO, Path.CLOSEPOLY])
     def _draw_triangle_arrow(self, renderer, gc, path, path_trans):
         """Draw a triangular arrow."""
         segment = [i[0] for i in path.iter_segments()][-2:]
         startx,starty = path_trans.transform_point(segment[0])
         endx,endy = path_trans.transform_point(segment[1])
         angle = math.atan2(endy-starty, endx-startx)
         halfwidth = 0.5*renderer.points_to_pixels(self._arrowheadwidth)
         length = renderer.points_to_pixels(self._arrowheadlength)
         transform = 
matplotlib.transforms.Affine2D().scale(length,halfwidth).rotate(angle).translate(endx,endy)

         rgbFace = self._get_rgb_arrowface()
         renderer.draw_path(gc, self._arrow_path, transform, rgbFace)

     def _get_rgb_arrowface(self):
         facecolor = self._arrowfacecolor
         if matplotlib.cbook.is_string_like(facecolor) and 
facecolor.lower()=='none':
             rgbFace = None
         else:
             rgbFace = matplotlib.colors.colorConverter.to_rgb(facecolor)
         return rgbFace


-------------------------------------------------------------------------
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-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users

Reply via email to