|
From: <lee...@us...> - 2010-02-26 00:32:20
|
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.
|