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 <[EMAIL PROTECTED]> 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://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
>
import numpy as np
import matplotlib.lines as mlines
import matplotlib.patches as mpatches
from matplotlib.transforms import IdentityTransform

class FancyArrow(mpatches.FancyArrow):

    def __init__(self, *kl, **kw):

        self.head_width=kw["head_width"]
        self.head_length=kw["head_length"]

        kw["length_includes_head"] = True
        mpatches.FancyArrow.__init__(self, *kl, **kw)

        

class ArrowedLine2D(mlines.Line2D):

    def __init__(self, *kl, **kw):
        mlines.Line2D.__init__(self, *kl, **kw)

        self.arrows = {}
        

    def arrow(self, loc="end", 
              head_width=5, head_length=None,
              **kwargs):

        if head_length is None:
            head_length = 2. * head_width

        if loc in ["begin", "end"]:
            # the position will be updated at drawing time.
            self.arrows[loc] = FancyArrow(0., 0., 1., 1.,
                                          head_width=head_width,
                                          head_length=head_length,
                                          length_includes_head=True,
                                          transform=IdentityTransform(),
                                          **kwargs)

        else:
            raise ValueError("unknown location")
        
        return  self.arrows[loc]

    
    def _update_arrows(self, loc=None):

        if loc is None:
            for k in self.arrows.keys():
                self._update_arrows(k)
            return

        xx_yy = self.get_data()
        xx_yy = self.get_transform().transform(np.transpose(xx_yy))
        xx, yy = np.transpose(xx_yy)
        
        if loc == "end":
            ind1, ind2 = -1, -2
        elif loc == "begin":
            ind1, ind2 = 0, 1

        arrow = self.arrows[loc]
        x0, y0 = xx[ind1], yy[ind1],
        dx, dy = xx[ind1]-xx[ind2], yy[ind1]-yy[ind2]

                
        """ adjust arrow parameter so that it only have a head
        """

        dd = (dx**2 + dy**2)**.5
        frac = arrow.head_length/dd

        arr = FancyArrow(x0-dx*frac, y0-dy*frac, dx*frac, dy*frac,
                         head_width=arrow.head_width,
                         head_length=arrow.head_length,
                         transform=IdentityTransform())
            
        arrow.set_xy(arr.get_xy())


    def draw(self, renderer):
        mlines.Line2D.draw(self, renderer)

        self._update_arrows()
        for arr in self.arrows.values():

            arr.set_axes(self.axes)
            self.axes._set_artist_props(arr)
            arr.set_clip_path(self.axes.patch)

            arr.draw(renderer)
        

if __name__ == "__main__":
    import matplotlib.pyplot as plt
    f = plt.figure()
    ax = plt.subplot(1,1,1)
    l = ArrowedLine2D([0.1,.3,0.6], [0.4,0.2,0.4])
    l.arrow("end")
    l.arrow("begin")
    ax.add_line(l)
    
    plt.savefig("test_arrow")
    
-------------------------------------------------------------------------
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