|
From: <md...@us...> - 2007-09-18 19:29:24
|
Revision: 3856
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3856&view=rev
Author: mdboom
Date: 2007-09-18 12:29:21 -0700 (Tue, 18 Sep 2007)
Log Message:
-----------
Optimize shared axes (to prevent calling set_xlim/set_ylim more than
once per axes per update).
Save figure at correct dpi.
General cleanup and optimizations.
Modified Paths:
--------------
branches/transforms/lib/matplotlib/axes.py
branches/transforms/lib/matplotlib/axis.py
branches/transforms/lib/matplotlib/backend_bases.py
branches/transforms/lib/matplotlib/backends/backend_agg.py
branches/transforms/lib/matplotlib/backends/backend_gtkagg.py
branches/transforms/lib/matplotlib/backends/backend_tkagg.py
branches/transforms/lib/matplotlib/cbook.py
branches/transforms/lib/matplotlib/figure.py
branches/transforms/lib/matplotlib/legend.py
branches/transforms/lib/matplotlib/lines.py
branches/transforms/lib/matplotlib/transforms.py
branches/transforms/src/_backend_agg.cpp
Modified: branches/transforms/lib/matplotlib/axes.py
===================================================================
--- branches/transforms/lib/matplotlib/axes.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/axes.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -435,6 +435,9 @@
1 : 'log',
}
+ _shared_x_axes = cbook.Grouper()
+ _shared_y_axes = cbook.Grouper()
+
def __str__(self):
return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
def __init__(self, fig, rect,
@@ -491,6 +494,10 @@
# must be set before set_figure
self._sharex = sharex
self._sharey = sharey
+ if sharex is not None:
+ self._shared_x_axes.join(self, sharex)
+ if sharey is not None:
+ self._shared_y_axes.join(self, sharey)
# Flag: True if some other Axes instance is sharing our x or y axis
self._masterx = False
self._mastery = False
@@ -502,7 +509,6 @@
# this call may differ for non-sep axes, eg polar
self._init_axis()
-
if axisbg is None: axisbg = rcParams['axes.facecolor']
self._axisbg = axisbg
self._frameon = frameon
@@ -616,55 +622,28 @@
#these will be updated later as data is added
self._set_lim_and_transforms()
- def _shared_xlim_callback(self, ax):
- xmin, xmax = ax.get_xlim()
- self.set_xlim(xmin, xmax, emit=False)
- self.figure.canvas.draw_idle()
-
- def _shared_ylim_callback(self, ax):
- ymin, ymax = ax.get_ylim()
- self.set_ylim(ymin, ymax, emit=False)
- self.figure.canvas.draw_idle()
-
def _set_lim_and_transforms(self):
"""
set the dataLim and viewLim BBox attributes and the
transData and transAxes Transformation attributes
"""
- Bbox = mtransforms.Bbox
- self.viewLim = Bbox.unit()
+ self.viewLim = mtransforms.Bbox.unit()
+ self.dataLim = mtransforms.Bbox.unit()
- if self._sharex is not None:
- # MGDTODO: This may be doing at least one too many updates
- # than necessary
- self._sharex.callbacks.connect(
- 'xlim_changed', self._shared_xlim_callback)
- self.viewLim.intervalx = self._sharex.viewLim.intervalx
- if self._sharey is not None:
- self._sharey.callbacks.connect(
- 'ylim_changed', self._shared_ylim_callback)
- self.viewLim.intervaly = self._sharex.viewLim.intervaly
-
- self.dataLim = Bbox.unit()
-
self.transAxes = mtransforms.BboxTransform(
- Bbox.unit(), self.bbox)
-
+ mtransforms.Bbox.unit(), self.bbox)
self.transData = mtransforms.BboxTransform(
self.viewLim, self.bbox)
def get_position(self, original=False):
'Return the axes rectangle left, bottom, width, height'
- # MGDTODO: This changed from returning a list to returning a Bbox
- # If you get any errors with the result of this function, please
- # update the calling code
if original:
- return copy.copy(self._originalPosition)
+ return self._originalPosition
else:
- return copy.copy(self._position)
- # return [val.get() for val in self._position]
+ return self._position
+
def set_position(self, pos, which='both'):
"""
Set the axes position with pos = [left, bottom, width, height]
@@ -699,8 +678,7 @@
self.xaxis.cla()
self.yaxis.cla()
- # MGDTODO
- # self.dataLim.ignore(1)
+ self.ignore_existing_data_limits = True
self.callbacks = cbook.CallbackRegistry(('xlim_changed', 'ylim_changed'))
if self._sharex is not None:
@@ -886,7 +864,7 @@
return
- l,b,w,h = self.get_position(original=True)
+ l,b,w,h = self.get_position(original=True).bounds
box_aspect = fig_aspect * (h/w)
data_ratio = box_aspect / A
@@ -1152,7 +1130,7 @@
# Otherwise, it will compute the bounds of it's current data
# and the data in xydata
xys = npy.asarray(xys)
- self.dataLim.update_numerix_xy(xys, -1)
+ self.update_datalim_numerix(xys[:, 0], xys[:, 1])
def update_datalim_numerix(self, x, y):
@@ -1161,10 +1139,9 @@
# limits and set the bound to be the bounds of the xydata.
# Otherwise, it will compute the bounds of it's current data
# and the data in xydata
- #print type(x), type(y)
- # MGDTODO
## self.dataLim.update_numerix(x, y, -1)
- self.dataLim.update_from_data(x, y)
+ self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits)
+ self.ignore_existing_data_limits = False
def _get_verts_in_data_coords(self, trans, xys):
if trans == self.transData:
@@ -1264,9 +1241,7 @@
if not self.get_visible(): return
renderer.open_group('axes')
self.apply_aspect()
- # MGDTODO -- this is where we can finalize all of the transforms
- # self.transData.freeze() # eval the lazy objects
- # self.transAxes.freeze()
+
if self.axison and self._frameon: self.axesPatch.draw(renderer)
artists = []
@@ -1314,17 +1289,13 @@
if self.axison and self._frameon:
artists.append(self.axesFrame)
- # keep track of i to guarantee stable sort for python 2.2
- dsu = [ (a.zorder, i, a) for i, a in enumerate(artists)
- if not a.get_animated()]
+ dsu = [ (a.zorder, a) for a in artists
+ if not a.get_animated() ]
dsu.sort()
- for zorder, i, a in dsu:
+ for zorder, a in dsu:
a.draw(renderer)
- # MGDTODO
- # self.transData.thaw() # release the lazy objects
- # self.transAxes.thaw() # release the lazy objects
renderer.close_group('axes')
self._cachedRenderer = renderer
@@ -1509,7 +1480,6 @@
ACCEPTS: len(2) sequence of floats
"""
-
if xmax is None and iterable(xmin):
xmin,xmax = xmin
@@ -1534,11 +1504,10 @@
self.viewLim.intervalx = (xmin, xmax)
if emit:
self.callbacks.process('xlim_changed', self)
- # MGDTODO: It would be nice to do this is in the above callback list,
- # but it's difficult to tell how to initialize this at the
- # right time
- if self._sharex:
- self._sharex.set_xlim(*self.viewLim.intervalx)
+ # Call all of the other x-axes that are shared with this one
+ for other in self._shared_x_axes.get_siblings(self):
+ if other is not self:
+ other.set_xlim(self.viewLim.xmin, self.viewLim.xmax, emit=False)
return xmin, xmax
@@ -1665,11 +1634,10 @@
self.viewLim.intervaly = (ymin, ymax)
if emit:
self.callbacks.process('ylim_changed', self)
- # MGDTODO: It would be nice to do this is in the above callback list,
- # but it's difficult to tell how to initialize this at the
- # right time
- if self._sharey:
- self._sharey.set_ylim(*self.viewLim.intervaly)
+ # Call all of the other y-axes that are shared with this one
+ for other in self._shared_y_axes.get_siblings(self):
+ if other is not self:
+ other.set_ylim(self.viewLim.ymin, self.viewLim.ymax, emit=False)
return ymin, ymax
Modified: branches/transforms/lib/matplotlib/axis.py
===================================================================
--- branches/transforms/lib/matplotlib/axis.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/axis.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -1033,8 +1033,7 @@
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
- self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi/72.0))
-# self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi.get()/72.0)) MGDTODO
+ self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi / 72.0))
else:
if not len(bboxes2):
@@ -1057,7 +1056,6 @@
bbox = Bbox.union(bboxes)
bottom = bbox.ymin
self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi/72.0))
-# self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO
def set_ticks_position(self, position):
"""
@@ -1225,7 +1223,6 @@
left = bbox.xmin
self.label.set_position( (left-self.LABELPAD*self.figure.dpi/72.0, y))
- # self.label.set_position( (left-self.LABELPAD*self.figure.dpi.get()/72.0, y)) MGDTODO
else:
if not len(bboxes2):
@@ -1245,7 +1242,6 @@
x,y = self.offsetText.get_position()
top = self.axes.bbox.ymax
self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi/72.0))
-# self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO
def set_offset_position(self, position):
assert position == 'left' or position == 'right'
Modified: branches/transforms/lib/matplotlib/backend_bases.py
===================================================================
--- branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backend_bases.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -18,8 +18,9 @@
"""An abstract base class to handle drawing/rendering operations
"""
# This will cache paths across rendering instances
- # Each subclass of RenderBase should define this -->
- # _paths = weakref.WeakKeyDictionary()
+ # Each subclass of RenderBase should define this a weak-keyed
+ # dictionary to hold native paths
+ # _native_paths = weakref.WeakKeyDictionary()
def __init__(self):
self._texmanager = None
@@ -44,7 +45,7 @@
self._native_paths[path] = native_path
return native_path
- def draw_path(self, gc, path, transform, rgbFace = None):
+ def draw_path(self, gc, path, transform, rgbFace=None):
"""
Handles the caching of the native path associated with the
given path and calls the underlying backend's _draw_path to
@@ -67,17 +68,31 @@
passed to them in draw_path.
"""
return path
+
+ def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
+ native_marker_path = self._get_cached_native_path(marker_path)
+ self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace)
- def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
- rotation):
+ def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
"""
- Draw an arc using GraphicsContext instance gcEdge, centered at x,y,
- with width and height and angles from 0.0 to 360.0
- 0 degrees is at 3-o'clock
- positive angles are anti-clockwise
- draw rotated 'rotation' degrees anti-clockwise about x,y
+ This method is currently underscore hidden because the
+ draw_markers method is being used as a sentinel for newstyle
+ backend drawing
- If the color rgbFace is not None, fill the arc with it.
+ path - a matplotlib.agg.path_storage instance
+
+ Draw the marker specified in path with graphics context gc at
+ each of the locations in arrays x and y. trans is a
+ matplotlib.transforms.Transformation instance used to
+ transform x and y to display coords. It consists of an
+ optional nonlinear component and an affine. You can access
+ these two components as
+
+ if transform.need_nonlinear():
+ x,y = transform.nonlinear_only_numerix(x, y)
+ # the a,b,c,d,tx,ty affine which transforms x and y
+ vec6 = transform.as_vec6_val()
+ ...backend dependent affine...
"""
raise NotImplementedError
@@ -109,30 +124,21 @@
"""
return False
- def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
- native_marker_path = self._get_cached_native_path(marker_path)
- self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace)
-
- def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
+ ######################################################################
+ ## OLD API IS BELOW
+ ## These functions no longer need to be implemented in the backends --
+ ## they now perform all of their functions in terms of the new API.
+
+ def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
+ rotation):
"""
- This method is currently underscore hidden because the
- draw_markers method is being used as a sentinel for newstyle
- backend drawing
+ Draw an arc using GraphicsContext instance gcEdge, centered at x,y,
+ with width and height and angles from 0.0 to 360.0
+ 0 degrees is at 3-o'clock
+ positive angles are anti-clockwise
+ draw rotated 'rotation' degrees anti-clockwise about x,y
- path - a matplotlib.agg.path_storage instance
-
- Draw the marker specified in path with graphics context gc at
- each of the locations in arrays x and y. trans is a
- matplotlib.transforms.Transformation instance used to
- transform x and y to display coords. It consists of an
- optional nonlinear component and an affine. You can access
- these two components as
-
- if transform.need_nonlinear():
- x,y = transform.nonlinear_only_numerix(x, y)
- # the a,b,c,d,tx,ty affine which transforms x and y
- vec6 = transform.as_vec6_val()
- ...backend dependent affine...
+ If the color rgbFace is not None, fill the arc with it.
"""
raise NotImplementedError
@@ -346,8 +352,10 @@
If rgbFace is not None, fill the rectangle with it.
"""
- raise NotImplementedError
-
+ warnings.warn("draw_rectangle called", warnings.PendingDeprecationWarning)
+ transform = transforms.Affine2D().scale(width, height).translate(x, y)
+ self.draw_path(gcEdge, Path.unit_rectangle(), transform, rgbFace)
+
def draw_regpoly_collection(
self, clipbox, offsets, transOffset, verts, sizes,
facecolors, edgecolors, linewidths, antialiaseds):
@@ -1221,8 +1229,6 @@
origfacecolor = self.figure.get_facecolor()
origedgecolor = self.figure.get_edgecolor()
- # MGDTODO
- # self.figure.dpi.set(dpi)
self.figure.dpi = dpi
self.figure.set_facecolor(facecolor)
self.figure.set_edgecolor(edgecolor)
@@ -1236,12 +1242,12 @@
orientation=orientation,
**kwargs)
finally:
- # MGDTODO
- # self.figure.dpi.set(origDPI)
self.figure.dpi = origDPI
self.figure.set_facecolor(origfacecolor)
self.figure.set_edgecolor(origedgecolor)
self.figure.set_canvas(self)
+
+ self.draw()
return result
@@ -1623,8 +1629,8 @@
lims.append( (xmin, xmax, ymin, ymax) )
# Store both the original and modified positions
pos.append( (
- a.get_position(True),
- a.get_position() ) )
+ copy.copy(a.get_position(True)),
+ copy.copy(a.get_position() )) )
self._views.push(lims)
self._positions.push(pos)
self.set_history_buttons()
Modified: branches/transforms/lib/matplotlib/backends/backend_agg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backends/backend_agg.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -114,9 +114,6 @@
self.height = height
if __debug__: verbose.report('RendererAgg.__init__ width=%s, \
height=%s'%(width, height), 'debug-annoying')
- # MGDTODO
-# self._renderer = _RendererAgg(int(width), int(height), dpi.get(),
-# debug=False)
self._renderer = _RendererAgg(int(width), int(height), dpi,
debug=False)
if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done',
@@ -136,35 +133,6 @@
def _draw_native_path(self, gc, path, transform, rgbFace):
return self._renderer.draw_path(gc, path, transform.get_matrix(), rgbFace)
- def draw_arc(self, gcEdge, rgbFace, x, y, width, height, angle1, angle2, rotation):
- """
- Draw an arc centered at x,y with width and height and angles
- from 0.0 to 360.0
-
- If rgbFace is not None, fill the rectangle with that color. gcEdge
- is a GraphicsContext instance
-
- Currently, I'm only supporting ellipses, ie angle args are
- ignored
- """
- if __debug__: verbose.report('RendererAgg.draw_arc', 'debug-annoying')
- self._renderer.draw_ellipse(
- gcEdge, rgbFace, x, y, width/2, height/2, rotation) # ellipse takes radius
-
-
- def draw_line(self, gc, x1, y1, x2, y2):
- """
- x and y are equal length arrays, draw lines connecting each
- point in x, y
- """
- if __debug__: verbose.report('RendererAgg.draw_line', 'debug-annoying')
- x = npy.array([x1,x2], float)
- y = npy.array([y1,y2], float)
- self._renderer.draw_lines(gc, x, y)
-
- def draw_lines(self, gc, x, y, transform):
- return self._renderer.draw_lines(gc, x, y, transform.to_values())
-
def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
return self._renderer.draw_markers(
gc,
@@ -172,18 +140,6 @@
path.vertices, path.codes, trans.get_matrix(),
rgbFace)
- def draw_polygon(self, *args):
- return self._renderer.draw_polygon(*args)
-
- def draw_point(self, gc, x, y):
- """
- Draw a single point at x,y
- """
- if __debug__: verbose.report('RendererAgg.draw_point', 'debug-annoying')
- rgbFace = gc.get_rgb()
- self._renderer.draw_ellipse(
- gc, rgbFace, x, y, 0.5, 0.5, 0.0)
-
def draw_mathtext(self, gc, x, y, s, prop, angle):
"""
Draw the math text using matplotlib.mathtext
@@ -192,8 +148,6 @@
'debug-annoying')
ox, oy, width, height, descent, font_image, used_characters = \
self.mathtext_parser.parse(s, self.dpi, prop)
-# ox, oy, width, height, descent, font_image, used_characters = \
-# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO
x = int(x) + ox
y = int(y) - oy
@@ -227,7 +181,6 @@
# (in vector space) in the above call to font.set_text.
self._renderer.draw_text_image(font.get_image(), int(x), int(y) + 1, angle, gc)
-
def get_text_width_height_descent(self, s, prop, ismath, rgb=(0,0,0)):
"""
get the width and height in display coords of the string s
@@ -241,7 +194,7 @@
# todo: handle props
size = prop.get_size_in_points()
texmanager = self.get_texmanager()
- Z = texmanager.get_rgba(s, size, self.dpi.get(), rgb)
+ Z = texmanager.get_rgba(s, size, self.dpi, rgb)
m,n,tmp = Z.shape
# TODO: descent of TeX text (I am imitating backend_ps here -JKS)
return n, m, 0
@@ -249,8 +202,6 @@
if ismath:
ox, oy, width, height, descent, fonts, used_characters = \
self.mathtext_parser.parse(s, self.dpi, prop)
-# ox, oy, width, height, descent, fonts, used_characters = \
-# self.mathtext_parser.parse(s, self.dpi.get(), prop) MGDTODO
return width, height, descent
font = self._get_agg_font(prop)
font.set_text(s, 0.0, flags=LOAD_DEFAULT) # the width and height of unrotated string
@@ -265,7 +216,7 @@
# todo, handle props, angle, origins
rgb = gc.get_rgb()
size = prop.get_size_in_points()
- dpi = self.dpi.get()
+ dpi = self.dpi
flip = angle==90
w,h,d = self.get_text_width_height_descent(s, prop, 'TeX', rgb)
@@ -306,7 +257,6 @@
'return the canvas width and height in display coords'
return self.width, self.height
-
def _get_agg_font(self, prop):
"""
Get the font for text instance t, cacheing for efficiency
@@ -325,11 +275,9 @@
font.clear()
size = prop.get_size_in_points()
font.set_size(size, self.dpi)
- # font.set_size(size, self.dpi.get()) MGDTODO
return font
-
def points_to_pixels(self, points):
"""
convert point measures to pixes using dpi and the pixels per
@@ -337,8 +285,6 @@
"""
if __debug__: verbose.report('RendererAgg.points_to_pixels',
'debug-annoying')
- # MGDTODO
- # return points*self.dpi.get()/72.0
return points*self.dpi/72.0
def tostring_rgb(self):
@@ -404,9 +350,7 @@
self.figure.draw(self.renderer)
def get_renderer(self):
- l,b,w,h = self.figure.bbox.bounds
- # MGDTODO
- # key = w, h, self.figure.dpi.get()
+ l, b, w, h = self.figure.bbox.bounds
key = w, h, self.figure.dpi
try: self._lastKey, self.renderer
except AttributeError: need_new_renderer = True
Modified: branches/transforms/lib/matplotlib/backends/backend_gtkagg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backends/backend_gtkagg.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -60,7 +60,6 @@
w,h = widget.window.get_size()
if w==1 or h==1: return # empty fig
- # dpival = self.figure.dpi.get() MGDTODO
# compute desired figure size in inches
dpival = self.figure.dpi
winch = w/dpival
Modified: branches/transforms/lib/matplotlib/backends/backend_tkagg.py
===================================================================
--- branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/backends/backend_tkagg.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -175,7 +175,6 @@
self._resize_callback(event)
# compute desired figure size in inches
- # dpival = self.figure.dpi.get() MGDTODO
dpival = self.figure.dpi
winch = width/dpival
hinch = height/dpival
Modified: branches/transforms/lib/matplotlib/cbook.py
===================================================================
--- branches/transforms/lib/matplotlib/cbook.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/cbook.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -955,6 +955,84 @@
outstream.write("Examining: %r\n" % (obj,))
recurse(obj, obj, { }, [])
+class Grouper(object):
+ """
+ This class provides a lightweight way to group arbitrary objects
+ together into disjoint sets when a full-blown graph data structure
+ would be overkill.
+
+ Objects can be joined using .join(), tested for connectedness
+ using .joined(), and all disjoint sets can be retreived using
+ .get().
+
+ The objects being joined must be hashable.
+
+ For example:
+
+ >>> g = grouper.Grouper()
+ >>> g.join('a', 'b')
+ >>> g.join('b', 'c')
+ >>> g.join('d', 'e')
+ >>> list(g.get())
+ [['a', 'b', 'c'], ['d', 'e']]
+ >>> g.joined('a', 'b')
+ True
+ >>> g.joined('a', 'c')
+ True
+ >>> g.joined('a', 'd')
+ False"""
+ def __init__(self, init=[]):
+ mapping = self._mapping = {}
+ for x in init:
+ mapping[x] = [x]
+
+ def join(self, a, *args):
+ """
+ Join given arguments into the same set.
+ Accepts one or more arguments.
+ """
+ mapping = self._mapping
+ set_a = mapping.setdefault(a, [a])
+
+ for arg in args:
+ set_b = mapping.get(arg)
+ if set_b is None:
+ set_a.append(arg)
+ mapping[arg] = set_a
+ elif set_b is not set_a:
+ if len(set_b) > len(set_a):
+ set_a, set_b = set_b, set_a
+ set_a.extend(set_b)
+ for elem in set_b:
+ mapping[elem] = set_a
+
+ def joined(self, a, b):
+ """
+ Returns True if a and b are members of the same set.
+ """
+ mapping = self._mapping
+ try:
+ return mapping[a] is mapping[b]
+ except KeyError:
+ return False
+
+ def __iter__(self):
+ """
+ Returns an iterator returning each of the disjoint sets as a list.
+ """
+ seen = set()
+ for elem, group in self._mapping.iteritems():
+ if elem not in seen:
+ yield group
+ seen.update(group)
+
+ def get_siblings(self, a):
+ """
+ Returns all of the items joined with the given item, including
+ itself.
+ """
+ return self._mapping.get(a, [a])
+
if __name__=='__main__':
assert( allequal([1,1,1]) )
assert(not allequal([1,1,0]) )
Modified: branches/transforms/lib/matplotlib/figure.py
===================================================================
--- branches/transforms/lib/matplotlib/figure.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/figure.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -161,10 +161,8 @@
def _get_dpi(self):
return self._dpi
def _set_dpi(self, dpi):
- print "setting dpi"
self._dpi = dpi
self._dpi_scale_trans.clear().scale(dpi, dpi)
- print self._dpi_scale_trans
dpi = property(_get_dpi, _set_dpi)
def autofmt_xdate(self, bottom=0.2, rotation=30, ha='right'):
@@ -178,10 +176,7 @@
bottom : the bottom of the subplots for subplots_adjust
rotation: the rotation of the xtick labels
ha : the horizontal alignment of the xticklabels
-
-
"""
-
for ax in self.get_axes():
if not hasattr(ax, 'is_last_row'):
raise RuntimeError('Axes must be subplot instances; found %s'%type(ax))
@@ -333,11 +328,8 @@
dpival = self.dpi
self.bbox_inches.max = w, h
- # self.figwidth.set(w) MGDTODO
- # self.figheight.set(h)
if forward:
- # dpival = self.dpi.get()
dpival = self.dpi
canvasw = w*dpival
canvash = h*dpival
@@ -347,7 +339,6 @@
def get_size_inches(self):
return self.bbox_inches.max
- # return self.figwidth.get(), self.figheight.get() MGDTODO
def get_edgecolor(self):
'Get the edge color of the Figure rectangle'
@@ -360,7 +351,6 @@
def get_figwidth(self):
'Return the figwidth as a float'
return self.bbox_inches.xmax
- # return self.figwidth.get() MGDTODO
def get_figheight(self):
'Return the figheight as a float'
@@ -369,7 +359,6 @@
def get_dpi(self):
'Return the dpi as a float'
return self.dpi
- # return self.dpi.get() MGDTODO
def get_frameon(self):
'get the boolean indicating frameon'
@@ -397,7 +386,6 @@
ACCEPTS: float
"""
- # self.dpi.set(val) MGDTODO
self.dpi = val
def set_figwidth(self, val):
@@ -406,7 +394,6 @@
ACCEPTS: float
"""
- # self.figwidth.set(val) MGDTODO
self.bbox_inches.xmax = val
def set_figheight(self, val):
@@ -415,7 +402,6 @@
ACCEPTS: float
"""
- # MGDTODO (set())
self.bbox_inches.ymax = val
def set_frameon(self, b):
@@ -598,8 +584,6 @@
#print 'figure draw'
if not self.get_visible(): return
renderer.open_group('figure')
- # MGDTODO
- # self.transFigure.freeze() # eval the lazy objects
if self.frameon: self.figurePatch.draw(renderer)
@@ -633,8 +617,6 @@
for legend in self.legends:
legend.draw(renderer)
- # MGDTODO
- # self.transFigure.thaw() # release the lazy objects
renderer.close_group('figure')
self._cachedRenderer = renderer
Modified: branches/transforms/lib/matplotlib/legend.py
===================================================================
--- branches/transforms/lib/matplotlib/legend.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/legend.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -21,7 +21,7 @@
up the legend
"""
from __future__ import division
-import sys, warnings
+import copy, sys, warnings
import numpy as npy
@@ -558,9 +558,7 @@
handle.set_height(h/2)
# Set the data for the legend patch
- # MGDTODO: This copy may no longer be needed now that Bboxes are
- # essentially immutable
- bbox = self._get_handle_text_bbox(renderer).copy()
+ bbox = copy.copy(self._get_handle_text_bbox(renderer))
bbox = bbox.scaled(1 + self.pad, 1 + self.pad)
l,b,w,h = bbox.get_bounds()
Modified: branches/transforms/lib/matplotlib/lines.py
===================================================================
--- branches/transforms/lib/matplotlib/lines.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/lines.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -352,32 +352,18 @@
self._picker = p
def get_window_extent(self, renderer):
- self._newstyle = hasattr(renderer, 'draw_markers')
- if self._newstyle:
- x = self._x
- y = self._y
- else:
- x, y = self._get_plottable()
+ xys = self.get_transform()(self._xys)
- # MGDTODO: Put this in a single Nx2 array, rather than these
- # separate ones
- #### Conversion code
- a = npy.vstack((x, y)).swapaxes(0, 1)
- ####
- x, y = self.get_transform()(a)
- print "get_window_extent", self.get_transform()
-
- #x, y = self.get_transform().seq_x_y(x, y)
+ x = xys[:, 0]
+ y = xys[:, 1]
+ left = x.min()
+ bottom = y.min()
+ width = x.max() - left
+ height = y.max() - bottom
- left = min(x)
- bottom = min(y)
- width = max(x) - left
- height = max(y) - bottom
-
# correct for marker size, if any
if self._marker is not None:
- ms = self._markersize/72.0*self.figure.dpi
- # ms = self._markersize/72.0*self.figure.dpi.get() MGDTODO
+ ms = self._markersize / 72.0 * self.figure.dpi
left -= ms/2
bottom -= ms/2
width += ms
@@ -411,6 +397,7 @@
def recache(self):
#if self.axes is None: print 'recache no axes'
#else: print 'recache units', self.axes.xaxis.units, self.axes.yaxis.units
+ # MGDTODO: Deal with units
x = ma.asarray(self.convert_xunits(self._xorig), float)
y = ma.asarray(self.convert_yunits(self._yorig), float)
@@ -435,15 +422,15 @@
else:
self._segments = None
- self._x = npy.asarray(x, float)
- self._y = npy.asarray(y, float)
- self._path = Path(npy.vstack((self._x, self._y)).transpose(),
- closed=False)
+ self._xy = npy.vstack((npy.asarray(x, npy.float_),
+ npy.asarray(y, npy.float_))).transpose()
+ self._x = self._xy[:, 0]
+ self._y = self._xy[:, 1]
+ self._path = Path(self._xy, closed=False)
self._logcache = None
-
def _is_sorted(self, x):
"return true if x is sorted"
if len(x)<2: return 1
Modified: branches/transforms/lib/matplotlib/transforms.py
===================================================================
--- branches/transforms/lib/matplotlib/transforms.py 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/lib/matplotlib/transforms.py 2007-09-18 19:29:21 UTC (rev 3856)
@@ -19,9 +19,9 @@
self._parents = Set()
def invalidate(self):
- if not self._do_invalidation():
- for parent in self._parents:
- parent.invalidate()
+ self._do_invalidation()
+ for parent in self._parents:
+ parent.invalidate()
def _do_invalidation(self):
return False
@@ -187,14 +187,16 @@
return 'Bbox(%s)' % repr(self._points)
__str__ = __repr__
- # JDH: the update method will update the box limits from the
- # existing limits and the new data; it appears here you are just
- # using the new data. We use an "ignore" flag to specify whether
- # you want to include the existing data or not in the update
def update_from_data(self, x, y, ignore=True):
- self._points = npy.array(
- [[x.min(), y.min()], [x.max(), y.max()]],
- npy.float_)
+ if ignore:
+ self._points = npy.array(
+ [[x.min(), y.min()], [x.max(), y.max()]],
+ npy.float_)
+ else:
+ self._points = npy.array(
+ [[min(x.min(), self.xmin), min(y.min(), self.ymin)],
+ [max(x.max(), self.xmax), max(y.max(), self.ymax)]],
+ npy.float_)
self.invalidate()
# MGDTODO: Probably a more efficient ways to do this...
@@ -409,9 +411,7 @@
return self.get_matrix()
def _do_invalidation(self):
- result = self._inverted is None
self._inverted = None
- return result
#@staticmethod
def _concat(a, b):
@@ -494,6 +494,7 @@
if matrix is None:
matrix = npy.identity(3)
else:
+ matrix = npy.asarray(matrix, npy.float_)
assert matrix.shape == (3, 3)
self._mtx = matrix
self._inverted = None
@@ -629,8 +630,6 @@
if self._mtx is not None:
self._mtx = None
Affine2DBase._do_invalidation(self)
- return False
- return True
def is_separable(self):
return True
@@ -684,7 +683,7 @@
def _do_invalidation(self):
self._mtx = None
- Affine2DBase._do_invalidation(self)
+ return Affine2DBase._do_invalidation(self)
def get_matrix(self):
if self._mtx is None:
@@ -718,8 +717,6 @@
if self._mtx is not None:
self._mtx = None
Affine2DBase._do_invalidation(self)
- return False
- return True
def is_separable(self):
return True
Modified: branches/transforms/src/_backend_agg.cpp
===================================================================
--- branches/transforms/src/_backend_agg.cpp 2007-09-18 16:21:37 UTC (rev 3855)
+++ branches/transforms/src/_backend_agg.cpp 2007-09-18 19:29:21 UTC (rev 3856)
@@ -456,7 +456,7 @@
//return the agg::rect for bbox, flipping y
PyArrayObject *bbox = (PyArrayObject *) PyArray_ContiguousFromObject(o.ptr(), PyArray_DOUBLE, 2, 2);
- if (!bbox || bbox->nd != 2 bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2)
+ if (!bbox || bbox->nd != 2 || bbox->dimensions[0] != 2 || bbox->dimensions[1] != 2)
throw Py::TypeError
("Expected a Bbox object.");
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|