Revision: 7259
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7259&view=rev
Author:   leejjoon
Date:     2009-07-14 19:21:47 +0000 (Tue, 14 Jul 2009)

Log Message:
-----------
Fix a few bugs in ConnectionStyle classes. Add ConnectionPatch.

Modified Paths:
--------------
    trunk/matplotlib/CHANGELOG
    trunk/matplotlib/lib/matplotlib/patches.py

Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG  2009-07-12 03:20:53 UTC (rev 7258)
+++ trunk/matplotlib/CHANGELOG  2009-07-14 19:21:47 UTC (rev 7259)
@@ -1,3 +1,6 @@
+2009-07-14 Fix a few bugs in ConnectionStyle algorithms. Add 
+           ConnectionPatch class. -JJL
+
 2009-07-11 Added a fillstyle Line2D property for half filled markers
            -- see examples/pylab_examples/fillstyle_demo.py JDH
 

Modified: trunk/matplotlib/lib/matplotlib/patches.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/patches.py  2009-07-12 03:20:53 UTC (rev 
7258)
+++ trunk/matplotlib/lib/matplotlib/patches.py  2009-07-14 19:21:47 UTC (rev 
7259)
@@ -1729,8 +1729,8 @@
         """
 
         def __init__(self, pad=0.3):
-            self.pad = pad
-            super(BoxStyle.RArrow, self).__init__()
+            #self.pad = pad
+            super(BoxStyle.RArrow, self).__init__(pad)
 
         def transmute(self, x0, y0, width, height, mutation_size):
 
@@ -2466,7 +2466,7 @@
             cosA, sinA = math.cos(self.angleA/180.*math.pi),\
                          math.sin(self.angleA/180.*math.pi),
             cosB, sinB = math.cos(self.angleB/180.*math.pi),\
-                         -math.sin(self.angleB/180.*math.pi),
+                         math.sin(self.angleB/180.*math.pi),
 
             cx, cy = get_intersection(x1, y1, cosA, sinA,
                                       x2, y2, cosB, sinB)
@@ -2478,9 +2478,15 @@
                 vertices.append((cx, cy))
                 codes.append(Path.LINETO)
             else:
-                vertices.extend([(cx - self.rad * cosA, cy - self.rad * sinA),
+                dx1, dy1 = x1-cx, y1-cy
+                d1 = (dx1**2 + dy1**2)**.5
+                f1 = self.rad/d1
+                dx2, dy2 = x2-cx, y2-cy
+                d2 = (dx2**2 + dy2**2)**.5
+                f2 = self.rad/d2
+                vertices.extend([(cx + dx1*f1, cy + dy1*f1),
                                  (cx, cy),
-                                 (cx + self.rad * cosB, cy + self.rad * sinB)])
+                                 (cx + dx2*f2, cy + dy2*f2)])
                 codes.extend([Path.LINETO, Path.CURVE3, Path.CURVE3])
 
             vertices.append((x2, y2))
@@ -2623,7 +2629,8 @@
                 #angle = self.angle % 180.
                 #if angle < 0. or angle > 180.:
                 #    angle
-                theta0 = (self.angle%180.)/180.*math.pi
+                #theta0 = (self.angle%180.)/180.*math.pi
+                theta0 = self.angle/180.*math.pi
                 #theta0 = (((self.angle+90)%180.)  - 90.)/180.*math.pi
                 dtheta = theta1 - theta0
                 dl = dd*math.sin(dtheta)
@@ -3744,3 +3751,302 @@
 
         gc.restore()
         renderer.close_group('patch')
+
+
+class ConnectionPatch(FancyArrowPatch):
+    """
+    A :class:`~matplotlib.patches.ConnectionPatch` class is to make
+    connecting lines between two points (possibly in different axes).
+    """
+    def __str__(self):
+        return "ConnectionPatch((%g,%g),(%g,%g))" % \
+               (self.xy1[0],self.xy1[1],self.xy2[0],self.xy2[1])
+
+    def __init__(self, xyA, xyB, coordsA, coordsB=None,
+                 axesA=None, axesB=None,
+                 arrowstyle="-",
+                 arrow_transmuter=None,
+                 connectionstyle="arc3",
+                 connector=None,
+                 patchA=None,
+                 patchB=None,
+                 shrinkA=0.,
+                 shrinkB=0.,
+                 mutation_scale=10.,
+                 mutation_aspect=None,
+                 clip_on=False,
+                 **kwargs):
+        """
+        Connect point *xyA* in *coordsA* with point *xyB* in *coordsB*
+
+
+        Valid keys are
+
+
+        ===============  ======================================================
+        Key              Description
+        ===============  ======================================================
+        arrowstyle       the arrow style
+        connectionstyle  the connection style
+        relpos           default is (0.5, 0.5)
+        patchA           default is bounding box of the text
+        patchB           default is None
+        shrinkA          default is 2 points
+        shrinkB          default is 2 points
+        mutation_scale   default is text size (in points)
+        mutation_aspect  default is 1.
+        ?                any key for :class:`matplotlib.patches.PathPatch`
+        ===============  ======================================================
+
+
+        *coordsA* and *coordsB* are strings that indicate the
+        coordinates of *xyA* and *xyB*.
+
+        =================   ===================================================
+        Property            Description
+        =================   ===================================================
+        'figure points'     points from the lower left corner of the figure
+        'figure pixels'     pixels from the lower left corner of the figure
+        'figure fraction'   0,0 is lower left of figure and 1,1 is upper, right
+        'axes points'       points from lower left corner of axes
+        'axes pixels'       pixels from lower left corner of axes
+        'axes fraction'     0,1 is lower left of axes and 1,1 is upper right
+        'data'              use the coordinate system of the object being
+                            annotated (default)
+        'offset points'     Specify an offset (in points) from the *xy* value
+
+        'polar'             you can specify *theta*, *r* for the annotation,
+                            even in cartesian plots.  Note that if you
+                            are using a polar axes, you do not need
+                            to specify polar for the coordinate
+                            system since that is the native "data" coordinate
+                            system.
+        =================   ===================================================
+
+        """
+        if coordsB is None:
+            coordsB = coordsA
+        # we'll draw ourself after the artist we annotate by default
+        self.xy1 = xyA
+        self.xy2 = xyB
+        self.coords1 = coordsA
+        self.coords2 = coordsB
+
+        self.axesA = axesA
+        self.axesB = axesB
+
+        FancyArrowPatch.__init__(self,
+                                 posA=(0,0), posB=(1,1),
+                                 arrowstyle=arrowstyle,
+                                 arrow_transmuter=arrow_transmuter,
+                                 connectionstyle=connectionstyle,
+                                 connector=connector,
+                                 patchA=patchA,
+                                 patchB=patchB,
+                                 shrinkA=shrinkA,
+                                 shrinkB=shrinkB,
+                                 mutation_scale=mutation_scale,
+                                 mutation_aspect=mutation_aspect,
+                                 clip_on=clip_on,
+                                 **kwargs)
+
+        # if True, draw annotation only if self.xy is inside the axes
+        self._annotation_clip = None
+
+    __init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
+
+
+    def _get_xy(self, x, y, s, axes=None):
+        """
+        caculate the pixel position of given point
+        """
+
+        if axes is None:
+            axes = self.axes
+
+        if s=='data':
+            trans = axes.transData
+            x = float(self.convert_xunits(x))
+            y = float(self.convert_yunits(y))
+            return trans.transform_point((x, y))
+        elif s=='offset points':
+            # convert the data point
+            dx, dy = self.xy
+
+            # prevent recursion
+            if self.xycoords == 'offset points':
+                return self._get_xy(dx, dy, 'data')
+
+            dx, dy = self._get_xy(dx, dy, self.xycoords)
+
+            # convert the offset
+            dpi = self.figure.get_dpi()
+            x *= dpi/72.
+            y *= dpi/72.
+
+            # add the offset to the data point
+            x += dx
+            y += dy
+
+            return x, y
+        elif s=='polar':
+            theta, r = x, y
+            x = r*np.cos(theta)
+            y = r*np.sin(theta)
+            trans = axes.transData
+            return trans.transform_point((x,y))
+        elif s=='figure points':
+            #points from the lower left corner of the figure
+            dpi = self.figure.dpi
+            l,b,w,h = self.figure.bbox.bounds
+            r = l+w
+            t = b+h
+
+            x *= dpi/72.
+            y *= dpi/72.
+            if x<0:
+                x = r + x
+            if y<0:
+                y = t + y
+            return x,y
+        elif s=='figure pixels':
+            #pixels from the lower left corner of the figure
+            l,b,w,h = self.figure.bbox.bounds
+            r = l+w
+            t = b+h
+            if x<0:
+                x = r + x
+            if y<0:
+                y = t + y
+            return x, y
+        elif s=='figure fraction':
+            #(0,0) is lower left, (1,1) is upper right of figure
+            trans = self.figure.transFigure
+            return trans.transform_point((x,y))
+        elif s=='axes points':
+            #points from the lower left corner of the axes
+            dpi = self.figure.dpi
+            l,b,w,h = axes.bbox.bounds
+            r = l+w
+            t = b+h
+            if x<0:
+                x = r + x*dpi/72.
+            else:
+                x = l + x*dpi/72.
+            if y<0:
+                y = t + y*dpi/72.
+            else:
+                y = b + y*dpi/72.
+            return x, y
+        elif s=='axes pixels':
+            #pixels from the lower left corner of the axes
+
+            l,b,w,h = axes.bbox.bounds
+            r = l+w
+            t = b+h
+            if x<0:
+                x = r + x
+            else:
+                x = l + x
+            if y<0:
+                y = t + y
+            else:
+                y = b + y
+            return x, y
+        elif s=='axes fraction':
+            #(0,0) is lower left, (1,1) is upper right of axes
+            trans = axes.transAxes
+            return trans.transform_point((x, y))
+
+    def set_annotation_clip(self, b):
+        """
+        set *annotation_clip* attribute.
+
+          * True : the annotation will only be drawn when self.xy is inside 
the axes.
+          * False : the annotation will always be drawn regardless of its 
position.
+          * None : the self.xy will be checked only if *xycoords* is "data"
+        """
+        self._annotation_clip = b
+
+    def get_annotation_clip(self):
+        """
+        Return *annotation_clip* attribute.
+        See :meth:`set_annotation_clip` for the meaning of return values.
+        """
+        return self._annotation_clip
+
+
+    def get_path_in_displaycoord(self):
+        """
+        Return the mutated path of the arrow in the display coord
+        """
+
+        x, y = self.xy1
+        posA = self._get_xy(x, y, self.coords1, self.axesA)
+
+        x, y = self.xy2
+        posB = self._get_xy(x, y, self.coords1, self.axesB)
+
+        _path = self.get_connectionstyle()(posA, posB,
+                                           patchA=self.patchA,
+                                           patchB=self.patchB,
+                                           shrinkA=self.shrinkA,
+                                           shrinkB=self.shrinkB
+                                           )
+
+
+
+        _path, fillable = self.get_arrowstyle()(_path,
+                                                self.get_mutation_scale(),
+                                                self.get_linewidth(),
+                                                self.get_mutation_aspect()
+                                                )
+
+        return _path, fillable
+
+
+
+    def _check_xy(self, renderer):
+        """
+        check if the annotation need to
+        be drawn.
+        """
+
+        b = self.get_annotation_clip()
+
+
+        if b or (b is None and self.coords1 == "data"):
+            x, y = self.xy1
+            xy_pixel = self._get_xy(x, y, self.coords1, self.axesA)
+            if not self.axes.contains_point(xy_pixel):
+                return False
+
+        if b or (b is None and self.coords2 == "data"):
+            x, y = self.xy2
+            xy_pixel = self._get_xy(x, y, self.coords2, self.axesB)
+            if self.axesB is None:
+                axes = self.axes
+            else:
+                axes = self.axesB
+            if not axes.contains_point(xy_pixel):
+                return False
+
+        return True
+
+
+    def draw(self, renderer):
+        """
+        Draw.
+        """
+
+        if renderer is not None:
+            self._renderer = renderer
+        if not self.get_visible(): return
+
+        if not self._check_xy(renderer):
+            return
+
+        FancyArrowPatch.draw(self, renderer)
+
+
+


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge  
This is your chance to win up to $100,000 in prizes! For a limited time, 
vendors submitting new applications to BlackBerry App World(TM) will have
the opportunity to enter the BlackBerry Developer Challenge. See full prize  
details at: http://p.sf.net/sfu/Challenge
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to