Aha. I just managed to have the stem drawn. My silly

> mistake; i thought that to instantiate a Line2D i

> needed to pass it (x0, y0) and (x1, y1), but it rather

> expects (x0, x1) and (y0, y1). The arrow looks cool

> now.

Rather than a line and a polygon, it might be more flexible and

attractive to design the arrow simply as a polygon (you could then

have control of the linewidth, facecolor, and edgewidth, something

like

p0

/ \

/ \

/ \

p6--p5 p2--p1

> >

> >

> >

> >

> >

p4--p3

> My remaining problem is the coordinates. It seems that

> matplotlib is positioning the arrow using pixels as

> coordinates, from the bottom left corner of the figure

> window.

> Is my problem a 'transformation' issue?

Yes. If you derive your class from Artist and add it to the axes with

ax.add_artist (or Patch if you use the polygon approach above and add

it with ax.add_artist), the axes will set the default data

transformation for you, iff and only if you haven't already set the

transform. There are three default transforms you can choose from

fig.transFigure # 0,0 is lower left of fig and 1,1 is upper right

ax.transAxes # 0,0 is lower left of axes and 1,1 is upper right

ax.transData # same coordinates as the data in the axes

You have a additional choices with custom transforms. One approach

would be to set the coordinates of the polygon in points such that the

arrow tip is 0,0 and the width and height are both 1. You could then

use a scaling and rotation affine where sx, sy are the x and y scales,

and theta is the angle. If you apply this affine to the arrow, the

width of the arrow would be sx points, the height sy points, and the

angle would be theta and the sucker would still be pointing at 0,0.

One nice feature of transformations is that the let you combine two

coordinate systems by applying a an offset transformation. In this

case you'd want to apply and offset in data coords and then the arrow

would be pointing at some data location x,y but would still have a

width and height specified in points.

This is basically how the ticks work. An x tick is located at an x

location in data coords, a y location in axes coords (eg 0 for bottom

ticks and 1 for top ticks) and a length in points.

Here's an example. I'm not sure this is the best design. It might be

more useful to specify a point for the base and a point for the arrowhead,

and draw the arrow between them. But I am not sure what the best way

to specify the arrow width if you use that design. In any case, this

will serve as an example you can study to get an idea of how the

transforms work, and you can go from there. It would also be nice to

have some intelligent labeling built it, eg at the arrow base

from pylab import *

from matplotlib.patches import Polygon

from matplotlib.transforms import Affine, Value, zero

import math

class Arrow(Polygon):

zorder = 4 # these should generally above the things they mark

def __init__(self, x, y, xytrans, width, height, theta,

tipx=2, tipy=0.2):

"""

Create an arrow pointing at x,y with a base width and total

height in points

theta is the arrow rotation - 0 degrees is point up, 90 is

pointing to the right, 180 is pointing down, 270 is pointing

left.

tipx is the tip width and is expressed as fraction of the base width.

tipy is the tip height expressed as a fraction of the total

height

xytrans is the transformation of the x,y coordinate, eg

ax.transData for data coords and ax.transAxes for axes coords

"""

# p0

# / \

# / \

# / \

# p6--p5 p2--p1

# | |

# | |

# | |

# | |

# | |

# p4--p3

p0 = 0,0

p1 = tipx*0.5, -tipy

p2 = 0.5, -tipy

p3 = 0.5, -1

p4 = -0.5, -1

p5 = -0.5, -tipy

p6 = -tipx*0.5, -tipy

verts = p0, p1, p2, p3, p4, p5, p6

Polygon.__init__(self, verts)

theta = math.pi*theta/180.

a = width*math.cos(theta)

b = -width*math.sin(theta)

c = height*math.sin(theta)

d = height*math.cos(theta)

a,b,c,d = [Value(val) for val in (a,b,c,d)]

trans = Affine(a, b, c, d, zero(), zero())

trans.set_offset((x,y), xytrans)

self.set_transform(trans)

plot([0,1,2], [1,2,3], 'bo', ms=15)

axis([0,3, 0, 4])

ax = gca()

arrow = Arrow(1,2, ax.transData, 10, 100, 135)

set(arrow, fc='g', ec='r', lw=1)

ax.add_patch(arrow)

show()