Revision: 8157
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8157&view=rev
Author:   leejjoon
Date:     2010-02-26 00:28:07 +0000 (Fri, 26 Feb 2010)

Log Message:
-----------
refactor Annotation to support arbitrary transformation.

Modified Paths:
--------------
    trunk/matplotlib/CHANGELOG
    trunk/matplotlib/lib/matplotlib/offsetbox.py
    trunk/matplotlib/lib/matplotlib/text.py

Modified: trunk/matplotlib/CHANGELOG
===================================================================
--- trunk/matplotlib/CHANGELOG  2010-02-25 15:57:15 UTC (rev 8156)
+++ trunk/matplotlib/CHANGELOG  2010-02-26 00:28:07 UTC (rev 8157)
@@ -1,3 +1,8 @@
+2010-02-25 refactor Annotation to support arbitrary Transform as xycoords 
+           or textcoords. Also, if a tuple of two coordinates is provided, 
+           they are interpreted as coordinates for each x and y position. 
+           -JJL
+
 2010-02-24 Added pyplot.fig_subplot(), to create a figure and a group of
           subplots in a single call.  This offers an easier pattern than
           manually making figures and calling add_subplot() multiple times. FP

Modified: trunk/matplotlib/lib/matplotlib/offsetbox.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/offsetbox.py        2010-02-25 15:57:15 UTC 
(rev 8156)
+++ trunk/matplotlib/lib/matplotlib/offsetbox.py        2010-02-26 00:28:07 UTC 
(rev 8157)
@@ -1301,11 +1301,11 @@
         x, y = self.xytext
         if isinstance(self.textcoords, tuple):
             xcoord, ycoord = self.textcoords
-            x1, y1 = self._get_xy(x, y, xcoord)
-            x2, y2 = self._get_xy(x, y, ycoord)
+            x1, y1 = self._get_xy(renderer, x, y, xcoord)
+            x2, y2 = self._get_xy(renderer, x, y, ycoord)
             ox0, oy0 = x1, y2
         else:
-            ox0, oy0 = self._get_xy(x, y, self.textcoords)
+            ox0, oy0 = self._get_xy(renderer, x, y, self.textcoords)
 
         #self.offsetbox.set_bbox_to_anchor((ox0, oy0))
         w, h, xd, yd = self.offsetbox.get_extent(renderer)
@@ -1526,11 +1526,11 @@
         x, y = ann.xytext
         if isinstance(ann.textcoords, tuple):
             xcoord, ycoord = ann.textcoords
-            x1, y1 = ann._get_xy(x, y, xcoord)
-            x2, y2 = ann._get_xy(x, y, ycoord)
+            x1, y1 = ann._get_xy(self.canvas.renderer, x, y, xcoord)
+            x2, y2 = ann._get_xy(self.canvas.renderer, x, y, ycoord)
             ox0, oy0 = x1, y2
         else:
-            ox0, oy0 = ann._get_xy(x, y, ann.textcoords)
+            ox0, oy0 = ann._get_xy(self.canvas.renderer, x, y, ann.textcoords)
 
         self.ox, self.oy = ox0, oy0
         self.annotation.textcoords = "figure pixels"
@@ -1539,7 +1539,7 @@
         ann = self.annotation
         ann.xytext = self.ox + dx, self.oy + dy
         x, y = ann.xytext
-        xy = ann._get_xy(x, y, ann.textcoords)
+        xy = ann._get_xy(self.canvas.renderer, x, y, ann.textcoords)
 
     def finalize_offset(self):
         loc_in_canvas = self.annotation.xytext

Modified: trunk/matplotlib/lib/matplotlib/text.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/text.py     2010-02-25 15:57:15 UTC (rev 
8156)
+++ trunk/matplotlib/lib/matplotlib/text.py     2010-02-26 00:28:07 UTC (rev 
8157)
@@ -16,7 +16,8 @@
 from matplotlib.patches import bbox_artist, YAArrow, FancyBboxPatch, \
      FancyArrowPatch, Rectangle
 import matplotlib.transforms as mtransforms
-from matplotlib.transforms import Affine2D, Bbox
+from matplotlib.transforms import Affine2D, Bbox, Transform ,\
+     BboxBase,  BboxTransformTo
 from matplotlib.lines import Line2D
 
 from matplotlib.artist import allow_rasterization
@@ -306,7 +307,7 @@
                                                         ismath=ismath)
             else:
                 w, h, d = 0, 0, 0
-                
+
             if baseline is None:
                 baseline = h - d
             whs[i] = w, h
@@ -1389,6 +1390,45 @@
 
 docstring.interpd.update(TextWithDash=artist.kwdoc(TextWithDash))
 
+
+class OffsetFrom(object):
+    def __init__(self, artist, ref_coord, unit="points"):
+        self._artist = artist
+        self._ref_coord= ref_coord
+        self.set_unit(unit)
+
+    def set_unit(self, unit):
+        assert unit in ["points", "pixels"]
+        self._unit = unit
+
+    def get_unit(self):
+        return self._unit
+
+    def _get_scale(self, renderer):
+        unit =  self.get_unit()
+        if unit == "pixels":
+            return 1.
+        else:
+            return renderer.points_to_pixels(1.)
+
+    def __call__(self, renderer):
+        if isinstance(self._artist, Artist):
+            bbox = self._artist.get_window_extent(renderer)
+            l, b, w, h = bbox.bounds
+            xf, yf = self._ref_coord
+            x, y = l+w*xf, b+h*yf
+        elif isinstance(self._artist, BboxBase):
+            l, b, w, h = self._artist.bounds
+            xf, yf = self._ref_coord
+            x, y = l+w*xf, b+h*yf
+        elif isinstance(self._artist, Transform):
+            x, y = self._artist.transform_point(self._ref_coord)
+
+        sc = self._get_scale(renderer)
+        tr = Affine2D().scale(sc, sc).translate(x, y)
+
+        return tr
+
 class _AnnotationBase(object):
     def __init__(self,
                  xy, xytext=None,
@@ -1408,102 +1448,169 @@
 
         self._draggable = None
 
+    def _get_xy(self, renderer, x, y, s):
+        if isinstance(s, tuple):
+            s1, s2 = s
+        else:
+            s1, s2 = s, s
 
-    def _get_xy(self, x, y, s):
-        if s=='data':
-            trans = self.axes.transData
+        if s1 == 'data':
             x = float(self.convert_xunits(x))
+        if s2 == 'data':
             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)
+        tr = self._get_xy_transform(renderer, s)
+        x1, y1 = tr.transform_point((x, y))
+        return x1, y1
 
-            # convert the offset
-            dpi = self.figure.get_dpi()
-            x *= dpi/72.
-            y *= dpi/72.
+    def _get_xy_transform(self, renderer, s):
 
-            # add the offset to the data point
-            x += dx
-            y += dy
+        if isinstance(s, tuple):
+            s1, s2 = s
+            from matplotlib.transforms import blended_transform_factory
+            tr1 = self._get_xy_transform(renderer, s1)
+            tr2 = self._get_xy_transform(renderer, s2)
+            tr = blended_transform_factory(tr1, tr2)
+            return tr
 
-            return x, y
+        if callable(s):
+            tr = s(renderer)
+            if isinstance(tr, BboxBase):
+                return BboxTransformTo(tr)
+            elif isinstance(tr, Transform):
+                return tr
+            else:
+                raise RuntimeError("unknown return type ...")
+        if isinstance(s, Artist):
+            bbox = s.get_window_extent(renderer)
+            return BboxTransformTo(bbox)
+        elif isinstance(s, BboxBase):
+            return BboxTransformTo(s)
+        elif isinstance(s, Transform):
+            return s
+        elif not is_string_like(s):
+            raise RuntimeError("unknown coordinate type : %s" % (s,))
+
+        if s=='data':
+            return self.axes.transData
         elif s=='polar':
-            theta, r = x, y
-            x = r*np.cos(theta)
-            y = r*np.sin(theta)
-            trans = self.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
+            from matplotlib.projections import PolarAxes
+            tr = PolarAxes.PolarTransform()
+            trans = tr + self.axes.transData
+            return trans
+            
+        s_ = s.split()
+        if len(s_) != 2:
+            raise ValueError("%s is not a recognized coodinate" % s)
 
-            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 = self.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
+        bbox0, xy0 = None, None
 
-            l,b,w,h = self.axes.bbox.bounds
-            r = l+w
-            t = b+h
-            if x<0:
-                x = r + x
+        bbox_name, unit = s_
+        # if unit is offset-like
+        if bbox_name == "figure":
+            bbox0 = self.figure.bbox
+        elif bbox_name == "axes":
+            bbox0 = self.axes.bbox
+        # elif bbox_name == "bbox":
+        #     if bbox is None:
+        #         raise RuntimeError("bbox is specified as a coordinate but 
never set")
+        #     bbox0 = self._get_bbox(renderer, bbox)
+
+        if bbox0 is not None:
+            xy0 = bbox0.bounds[:2]
+        elif bbox_name == "offset":
+            xy0 = self._get_ref_xy(renderer)
+
+        if xy0 is not None:
+            # reference x, y in display coordinate
+            ref_x, ref_y = xy0
+            from matplotlib.transforms import Affine2D
+            if unit == "points":
+                dpi = self.figure.get_dpi()
+                tr = Affine2D().scale(dpi/72., dpi/72.)
+            elif unit == "pixels":
+                tr = Affine2D()
+            elif unit == "fontsize":
+                fontsize = self.get_size()
+                dpi = self.figure.get_dpi()
+                tr = Affine2D().scale(fontsize*dpi/72., fontsize*dpi/72.)
+            elif unit == "fraction":
+                w, h = bbox0.bounds[2:]
+                tr = Affine2D().scale(w, h)
             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 = self.axes.transAxes
-            return trans.transform_point((x, y))
+                raise ValueError("%s is not a recognized coodinate" % s)
 
+            return tr.translate(ref_x, ref_y)
+
+        else:
+            raise ValueError("%s is not a recognized coodinate" % s)
+
+
+    def _get_ref_xy(self, renderer):
+        """
+        return x, y (in display coordinate) that is to be used for a reference
+        of any offset coordinate
+        """
+
+        if isinstance(self.xycoords, tuple):
+            s1, s2 = self.xycoords
+            if s1.split()[0] == "offset" or s2.split()[0] == "offset":
+                raise ValueError("xycoords should not be an offset coordinate")
+            x, y = self.xy
+            x1, y1 = self._get_xy(renderer, x, y, s1)
+            x2, y2 = self._get_xy(renderer, x, y, s2)
+            return x1, y2
+        elif is_string_like(self.xycoords) and self.xycoords.split()[0] == 
"offset":
+            raise ValueError("xycoords should not be an offset coordinate")
+        else:
+            x, y = self.xy
+            return self._get_xy(renderer, x, y, self.xycoords)
+        #raise RuntimeError("must be defined by the derived class")
+
+
+    # def _get_bbox(self, renderer):
+    #     if hasattr(bbox, "bounds"):
+    #         return bbox
+    #     elif hasattr(bbox, "get_window_extent"):
+    #         bbox = bbox.get_window_extent()
+    #         return bbox
+    #     else:
+    #         raise ValueError("A bbox instance is expected but got %s" % 
str(bbox))
+
+
+
+    def _get_xy_legacy(self, renderer, x, y, s):
+        """
+        only used when s in ['axes points', 'axes pixel', 'figure points', 
'figure pixel'].
+        """
+        s_ = s.split()
+        bbox0, xy0 = None, None
+        bbox_name, unit = s_
+
+        if bbox_name == "figure":
+            bbox0 = self.figure.bbox
+        elif bbox_name == "axes":
+            bbox0 = self.axes.bbox
+
+        if unit == "points":
+            sc = self.figure.get_dpi()/72.
+        elif unit == "pixels":
+            sc = 1
+
+        l,b,r,t = bbox0.extents
+        if x<0:
+            x = r + x*sc
+        else:
+            x = l + x*sc
+        if y<0:
+            y = t + y*sc
+        else:
+            y = b + y*sc
+
+        return x, y
+    
+
     def set_annotation_clip(self, b):
         """
         set *annotation_clip* attribute.
@@ -1524,7 +1631,7 @@
     def _get_position_xy(self, renderer):
         "Return the pixel position of the the annotated point."
         x, y = self.xy
-        return self._get_xy(x, y, self.xycoords)
+        return self._get_xy(renderer, x, y, self.xycoords)
 
     def _check_xy(self, renderer, xy_pixel):
         """
@@ -1533,6 +1640,7 @@
         """
 
         b = self.get_annotation_clip()
+
         if b or (b is None and self.xycoords == "data"):
             # check if self.xy is inside the axes.
             if not self.axes.contains_point(xy_pixel):
@@ -1550,7 +1658,7 @@
           * True : turn draggable on
 
           * False : turn draggable off
-          
+
         If draggable is on, you can drag the annotation on the canvas with
         the mouse.  The DraggableAnnotation helper instance is returned if
         draggable is on.
@@ -1561,7 +1669,7 @@
         # if state is None we'll toggle
         if state is None:
             state = not is_draggable
-            
+
         if state:
             if self._draggable is None:
                 self._draggable = DraggableAnnotation(self, use_blit)
@@ -1706,7 +1814,7 @@
         else:
             self.arrow_patch = None
 
-        
+
     def contains(self,event):
         t,tinfo = Text.contains(self,event)
         if self.arrow is not None:
@@ -1737,7 +1845,8 @@
         "Update the pixel positions of the annotation text and the arrow 
patch."
 
         x, y = self.xytext
-        self._x, self._y = self._get_xy(x, y, self.textcoords)
+        self._x, self._y = self._get_xy(renderer, x, y,
+                                        self.textcoords)
 
 
         x, y = xy_pixel


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

------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
Matplotlib-checkins mailing list
Matplotlib-checkins@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to