Screenshot instructions:
Windows
Mac
Red Hat Linux
Ubuntu
Click URL instructions:
Rightclick on ad, choose "Copy Link", then paste here →
(This may not be possible with some types of ads)
From: Jason Grout <jasonsage@cr...>  20080822 10:05:13

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 scaleindependent 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 (20080819): initial version """ ############################################################################ # Copyright (C) 2008 Jason Grout <jasonsage@...> # 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(endystarty, endxstartx) 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 
From: Alan G Isaac <aisaac@am...>  20080822 15:21:47

Jason Grout wrote: > 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? For this problem, what you want is to fill the arrowhead without stroking it. Cheers, Alan Isaac 
From: Jason Grout <jasonsage@cr...>  20080822 15:51:11

Alan G Isaac wrote: > Jason Grout wrote: >> 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? > > For this problem, what you want is to fill the arrowhead > without stroking it. > Brilliant! Thanks for the suggestion. The other problem is a more serious problem for me: how do I shorten the line so that it goes between the boundaries of the circle instead of the centers, especially when the circles are constructed in a scatter plot. If I knew how big the circles were in plot coordinates, it wouldn't be a problem. But the circle size isn't specified in plot coordinates, but in scaleindependent coordinates, I believe. Is there a way I could somehow compute the intersections of the paths? Maybe while I draw the line, I could also construct a circle of the right size at the endpoint, ask for the intersection, and shorten my line to go there? Thanks again! Jason 
From: Alan G Isaac <aisaac@am...>  20080822 16:16:02

Jason Grout wrote: > The other problem is a more serious problem for me: how do > I shorten the line so that it goes between the boundaries > of the circle instead of the centers, especially when the > circles are constructed in a scatter plot. Some years back I briefly tried to think about arrows and I found it trickier than expected. Note that some famous software clearly does arrows wrong. (E.g., gnuplot, at least last I checked.) Example: you have decided that you want to draw to the edge of a point, but a) is that right and b) can it be reasonably implemented? a) One might well argue in many applications that the arrow tip should go to the center of the circle. b) I'm not sure. But surely somebody out there will offer some great clues. Perhaps along the line of graphviz: http://www.graphviz.org/Gallery/directed/fsm.html Really this is not an answer to your questions ... Cheers, Alan Isaac 
From: Jason Grout <jasonsage@cr...>  20080822 21:41:01

Alan G Isaac wrote: > Jason Grout wrote: >> The other problem is a more serious problem for me: how do >> I shorten the line so that it goes between the boundaries >> of the circle instead of the centers, especially when the >> circles are constructed in a scatter plot. > > Some years back I briefly tried to think about arrows and > I found it trickier than expected. Note that some famous > software clearly does arrows wrong. (E.g., gnuplot, at > least last I checked.) > > Example: you have decided that you want to draw to the edge > of a point, but a) is that right and b) can it be reasonably > implemented? > > a) One might well argue in many applications that the arrow > tip should go to the center of the circle. I'm sure there are lots of applications where this is true. My specific problem domain is drawing graphs, exactly as graphviz. Often there is a label inside the circle. Drawing to the edge of the circle is the standard way of doing it, so a) is yes, it is the right thing to do. Things like flowcharts are another example of wanting to do this. > > b) I'm not sure. Thanks; at least this is validating the amount of time I already spent thinking about this and trying to get it to work. > > But surely somebody out there will offer some great clues. > Perhaps along the line of graphviz: > http://www.graphviz.org/Gallery/directed/fsm.html > > Really this is not an answer to your questions ... Thanks for your comments. I think I'll post up another post asking some more specific questions, along the lines of what I think might work if I can figure out what matplotlib is thinking. Jason 
From: Jason Grout <jasonsage@cr...>  20080823 04:11:15

JaeJoon Lee wrote: > Hi Jason, > > I did made a similar class sometime ago and I'm attaching it just in > case. I guess it is very similar to yours but I rely on > matplolib.patches.FancyArrow class to draw the arrow head. > > The circle drawn by scatter() command should be a circle with size s > (the third argument of the scatter command) in points . It seems that > it is implemented as a unit circle centered at (0,0) with a transform > corresponding to the size s (and offset). So you may try something > like below to calculate the size of the circle in data coord. > > ax = gca() > p = scatter([0],[0], 500.) > tr = p.get_transforms()[0] + ax.transData.inverted() > x1, y1 = tr.transform_point([0,0]) > x2, y2 = tr.transform_point([1,0]) > r = abs(x2  x1) > > p is a collection object and p.get_transforms() is a list of transforms. > Note that a circle in the canvas coordinate(?) can be an ellipse in > data coordinates. So, I guess you'd better do things in the canvas > coordinates. > > For shortening your path, if you're only concerned with a straight > line, it should be straight forward. But I guess it would a bit tricky > to do this for general bezier curves (as in the example that Alan > linked). I think (but I may be wrong) there is no universal algorithm > to find the "all" intersecting points of two bezier curves. There may > be one for between a bezier curve and a circle. And in this case where > one point is inside the circle and the other is outside, one simple > way I can think of is to recursively bisect the bezier curve (similar > to the bisect root finding). JaeJoon, Thank you very much. I am just finishing implementing a working version of what I wanted in my ArrowLine class; it now shortens itself by a certain number of points (assuming a line, just using a scale transformation). However, I use paths for drawing the arrowhead where you use patches. I think I like the flexibility your approach offers. Do you mind if I include your code in the GPLlicensed Sage, and extend it to do this shortening thing that I need? I still haven't decided which is ultimately better for what I need (my class or your class), but if you're willing to license your class in a compatible way, that provides a choice. Thanks, Jason 
From: JaeJoon Lee <lee.joon@gm...>  20080824 14:54:56

Sure. you may include it if you want. JJ On Sat, Aug 23, 2008 at 12:10 AM, Jason Grout <jasonsage@...> wrote: > JaeJoon Lee wrote: >> Hi Jason, >> >> I did made a similar class sometime ago and I'm attaching it just in >> case. I guess it is very similar to yours but I rely on >> matplolib.patches.FancyArrow class to draw the arrow head. >> >> The circle drawn by scatter() command should be a circle with size s >> (the third argument of the scatter command) in points . It seems that >> it is implemented as a unit circle centered at (0,0) with a transform >> corresponding to the size s (and offset). So you may try something >> like below to calculate the size of the circle in data coord. >> >> ax = gca() >> p = scatter([0],[0], 500.) >> tr = p.get_transforms()[0] + ax.transData.inverted() >> x1, y1 = tr.transform_point([0,0]) >> x2, y2 = tr.transform_point([1,0]) >> r = abs(x2  x1) >> >> p is a collection object and p.get_transforms() is a list of transforms. >> Note that a circle in the canvas coordinate(?) can be an ellipse in >> data coordinates. So, I guess you'd better do things in the canvas >> coordinates. >> >> For shortening your path, if you're only concerned with a straight >> line, it should be straight forward. But I guess it would a bit tricky >> to do this for general bezier curves (as in the example that Alan >> linked). I think (but I may be wrong) there is no universal algorithm >> to find the "all" intersecting points of two bezier curves. There may >> be one for between a bezier curve and a circle. And in this case where >> one point is inside the circle and the other is outside, one simple >> way I can think of is to recursively bisect the bezier curve (similar >> to the bisect root finding). > > JaeJoon, > > Thank you very much. I am just finishing implementing a working version > of what I wanted in my ArrowLine class; it now shortens itself by a > certain number of points (assuming a line, just using a scale > transformation). However, I use paths for drawing the arrowhead where > you use patches. I think I like the flexibility your approach offers. > Do you mind if I include your code in the GPLlicensed Sage, and extend > it to do this shortening thing that I need? I still haven't decided > which is ultimately better for what I need (my class or your class), but > if you're willing to license your class in a compatible way, that > provides a choice. > > Thanks, > > Jason > > >  > 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://moblincontest.org/redirect.php?banner_id=100&url=/ > _______________________________________________ > Matplotlibusers mailing list > Matplotlibusers@... > https://lists.sourceforge.net/lists/listinfo/matplotlibusers > 
From: JaeJoon Lee <lee.joon@gm...>  20080822 21:43:06
Attachments:
arrow.py

Hi Jason, I did made a similar class sometime ago and I'm attaching it just in case. I guess it is very similar to yours but I rely on matplolib.patches.FancyArrow class to draw the arrow head. The circle drawn by scatter() command should be a circle with size s (the third argument of the scatter command) in points . It seems that it is implemented as a unit circle centered at (0,0) with a transform corresponding to the size s (and offset). So you may try something like below to calculate the size of the circle in data coord. ax = gca() p = scatter([0],[0], 500.) tr = p.get_transforms()[0] + ax.transData.inverted() x1, y1 = tr.transform_point([0,0]) x2, y2 = tr.transform_point([1,0]) r = abs(x2  x1) p is a collection object and p.get_transforms() is a list of transforms. Note that a circle in the canvas coordinate(?) can be an ellipse in data coordinates. So, I guess you'd better do things in the canvas coordinates. For shortening your path, if you're only concerned with a straight line, it should be straight forward. But I guess it would a bit tricky to do this for general bezier curves (as in the example that Alan linked). I think (but I may be wrong) there is no universal algorithm to find the "all" intersecting points of two bezier curves. There may be one for between a bezier curve and a circle. And in this case where one point is inside the circle and the other is outside, one simple way I can think of is to recursively bisect the bezier curve (similar to the bisect root finding). Regards, JJ On Fri, Aug 22, 2008 at 12:15 PM, Alan G Isaac <aisaac@...> wrote: > Jason Grout wrote: >> The other problem is a more serious problem for me: how do >> I shorten the line so that it goes between the boundaries >> of the circle instead of the centers, especially when the >> circles are constructed in a scatter plot. > > Some years back I briefly tried to think about arrows and > I found it trickier than expected. Note that some famous > software clearly does arrows wrong. (E.g., gnuplot, at > least last I checked.) > > Example: you have decided that you want to draw to the edge > of a point, but a) is that right and b) can it be reasonably > implemented? > > a) One might well argue in many applications that the arrow > tip should go to the center of the circle. > > b) I'm not sure. > > But surely somebody out there will offer some great clues. > Perhaps along the line of graphviz: > http://www.graphviz.org/Gallery/directed/fsm.html > > Really this is not an answer to your questions ... > > Cheers, > Alan Isaac > > > >  > 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://moblincontest.org/redirect.php?banner_id=100&url=/ > _______________________________________________ > Matplotlibusers mailing list > Matplotlibusers@... > https://lists.sourceforge.net/lists/listinfo/matplotlibusers > 
Sign up for the SourceForge newsletter:
No, thanks