8000 Optimize shared axes (to prevent calling set_xlim/set_ylim more than · matplotlib/matplotlib@e67ef97 · GitHub
[go: up one dir, main page]

Skip to content

Commit e67ef97

Browse files
committed
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. svn path=/branches/transforms/; revision=3856
1 parent 4cd25ce commit e67ef97

File tree

12 files changed

+190
-236
lines changed

12 files changed

+190
-236
lines changed

lib/matplotlib/axes.py

Lines changed: 30 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ class Axes(martist.Artist):
435435
1 : 'log',
436436
}
437437

438+
_shared_x_axes = cbook.Grouper()
439+
_shared_y_axes = cbook.Grouper()
440+
438441
def __str__(self):
439442
return "Axes(%g,%g;%gx%g)" % tuple(self._position.bounds)
440443
def __init__(self, fig, rect,
@@ -491,6 +494,10 @@ def __init__(self, fig, rect,
491494
# must be set before set_figure
492495
self._sharex = sharex
493496
self._sharey = sharey
497+
if sharex is not None:
498+
self._shared_x_axes.join(self, sharex)
499+
if sharey is not None:
500+
self._shared_y_axes.join(self, sharey)
494501
# Flag: True if some other Axes instance is sharing our x or y axis
495502
self._masterx = False
496503
self._mastery = False
@@ -502,7 +509,6 @@ def __init__(self, fig, rect,
502509
# this call may differ for non-sep axes, eg polar
503510
self._init_axis()
504511

505-
506512
if axisbg is None: axisbg = rcParams['axes.facecolor']
507513
self._axisbg = axisbg
508514
self._frameon = frameon
@@ -616,55 +622,28 @@ def set_figure(self, fig):
616622
#these will be updated later as data is added
617623
self._set_lim_and_transforms()
618624

619-
def _shared_xlim_callback(self, ax):
620-
xmin, xmax = ax.get_xlim()
621-
self.set_xlim(xmin, xmax, emit=False)
622-
self.figure.canvas.draw_idle()
623-
624-
def _shared_ylim_callback(self, ax):
625-
ymin, ymax = ax.get_ylim()
626-
self.set_ylim(ymin, ymax, emit=False)
627-
self.figure.canvas.draw_idle()
628-
629625
def _set_lim_and_transforms(self):
630626
"""
631627
set the dataLim and viewLim BBox attributes and the
632628
transData and transAxes Transformation attributes
633629
"""
634-
Bbox = mtransforms.Bbox
635-
self.viewLim = Bbox.unit()
636-
637-
if self._sharex is not None:
638-
# MGDTODO: This may be doing at least one too many updates
639-
# than necessary
640-
self._sharex.callbacks.connect(
641-
'xlim_changed', self._shared_xlim_callback)
642-
self.viewLim.intervalx = self._sharex.viewLim.intervalx
643-
if self._sharey is not None:
644-
self._sharey.callbacks.connect(
645-
'ylim_changed', self._shared_ylim_callback)
646-
self.viewLim.intervaly = self._sharex.viewLim.intervaly
647-
648-
self.dataLim = Bbox.unit()
630+
self.viewLim = mtransforms.Bbox.unit()
631+
self.dataLim = mtransforms.Bbox.unit()
649632

650633
self.transAxes = mtransforms.BboxTransform(
651-
Bbox.unit(), self.bbox)
652-
634+
mtransforms.Bbox.unit(), self.bbox)
653635
self.transData = mtransforms.BboxTransform(
654636
self.viewLim, self.bbox)
655637

656638

657639
def get_position(self, original=False):
658640
'Return the axes rectangle left, bottom, width, height'
659-
# MGDTODO: This changed from returning a list to returning a Bbox
660-
# If you get any errors with the result of this function, please
661-
# update the calling code
662641
if original:
663-
return copy.copy(self._originalPosition)
642+
return self._originalPosition
664643
else:
665-
return copy.copy(self._position)
666-
# return [val.get() for val in self._position]
644+
return self._position
667645

646+
668647
def set_position(self, pos, which='both'):
669648
"""
670649
Set the axes position with pos = [left, bottom, width, height]
@@ -699,8 +678,7 @@ def cla(self):
699678
self.xaxis.cla()
700679
self.yaxis.cla()
701680

702-
# MGDTODO
703-
# self.dataLim.ignore(1)
681+
self.ignore_existing_data_limits = True
704682
self.callbacks = cbook.CallbackRegistry(('xlim_changed', 'ylim_changed'))
705683

706684
if self._sharex is not None:
@@ -886,7 +864,7 @@ def apply_aspect(self, data_ratio = None):
886864
return
887865

888866

889-
l,b,w,h = self.get_position(original=True)
867+
l,b,w,h = self.get_position(original=True).bounds
890868
box_aspect = fig_aspect * (h/w)
891869
data_ratio = box_aspect / A
892870

@@ -1152,7 +1130,7 @@ def update_datalim(self, xys):
11521130
# Otherwise, it will compute the bounds of it's current data
11531131
# and the data in xydata
11541132
xys = npy.asarray(xys)
1155-
self.dataLim.update_numerix_xy(xys, -1)
1133+
self.update_datalim_numerix(xys[:, 0], xys[:, 1])
11561134

11571135

11581136
def update_datalim_numerix(self, x, y):
@@ -1161,10 +1139,9 @@ def update_datalim_numerix(self, x, y):
11611139
# limits and set the bound to be the bounds of the xydata.
11621140
# Otherwise, it will compute the bounds of it's current data
11631141
# and the data in xydata
1164-
#print type(x), type(y)
1165-
# MGDTODO
11661142
## self.dataLim.update_numerix(x, y, -1)
1167-
self.dataLim.update_from_data(x, y)
1143+
self.dataLim.update_from_data(x, y, self.ignore_existing_data_limits)
1144+
self.ignore_existing_data_limits = False
11681145

11691146
def _get_verts_in_data_coords(self, trans, xys):
11701147
if trans == self.transData:
@@ -1264,9 +1241,7 @@ def draw(self, renderer=None, inframe=False):
12641241
if not self.get_visible(): return
12651242
renderer.open_group('axes')
12661243
self.apply_aspect()
1267-
# MGDTODO -- this is where we can finalize all of the transforms
1268-
# self.transData.freeze() # eval the lazy objects
1269-
# self.transAxes.freeze()
1244+
12701245
if self.axison and self._frameon: self.axesPatch.draw(renderer)
12711246
artists = []
12721247

@@ -1314,17 +1289,13 @@ def draw(self, renderer=None, inframe=False):
13141289
if self.axison and self._frameon:
13151290
artists.append(self.axesFrame)
13161291

1317-
# keep track of i to guarantee stable sort for python 2.2
1318-
dsu = [ (a.zorder, i, a) for i, a in enumerate(artists)
1319-
if not a.get_animated()]
1292+
dsu = [ (a.zorder, a) for a in artists
1293+
if not a.get_animated() ]
13201294
dsu.sort()
13211295

1322-
for zorder, i, a in dsu:
1296+
for zorder, a in dsu:
13231297
a.draw(renderer)
13241298

1325-
# MGDTODO
1326-
# self.transData.thaw() # release the lazy objects
1327-
# self.transAxes.thaw() # release the lazy objects
13281299
renderer.close_group('axes')
13291300
self._cachedRenderer = renderer
13301301

@@ -1509,7 +1480,6 @@ def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs):
15091480
15101481
ACCEPTS: len(2) sequence of floats
15111482
"""
1512-
15131483
if xmax is None and iterable(xmin):
15141484
xmin,xmax = xmin
15151485

@@ -1534,11 +1504,10 @@ def set_xlim(self, xmin=None, xmax=None, emit=True, **kwargs):
15341504
self.viewLim.intervalx = (xmin, xmax)
15351505
if emit:
15361506
self.callbacks.process('xlim_changed', self)
1537-
# MGDTODO: It would be nice to do this is in the above callback list,
1538-
# but it's difficult to tell how to initialize this at the
1539-
# right time
1540-
if self._sharex:
1541-
self._sharex.set_xlim(*self.viewLim.intervalx)
1507+
# Call all of the other x-axes that are shared with this one
1508+
for other in self._shared_x_axes.get_siblings(self):
1509+
if other is not self:
1510+
other.set_xlim(self.viewLim.xmin, self.viewLim.xmax, emit=False)
15421511

15431512
return xmin, xmax
15441513

@@ -1665,11 +1634,10 @@ def set_ylim(self, ymin=None, ymax=None, emit=True, **kwargs):
16651634
self.viewLim.intervaly = (ymin, ymax)
16661635
if emit:
16671636
self.callbacks.process('ylim_changed', self)
1668-
# MGDTODO: It would be nice to do this is in the above callback list,
1669-
# but it's difficult to tell how to initialize this at the
1670-
# right time
1671-
if self._sharey:
1672-
self._sharey.set_ylim(*self.viewLim.intervaly)
1637+
# Call all of the other y-axes that are shared with this one
1638+
for other in self._shared_y_axes.get_siblings(self):
1639+
if other is not self:
1640+
other.set_ylim(self.viewLim.ymin, self.viewLim.ymax, emit=False)
16731641

16741642
return ymin, ymax
16751643

lib/matplotlib/axis.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,8 +1033,7 @@ def _update_label_position(self, bboxes, bboxes2):
10331033
bbox = Bbox.union(bboxes)
10341034
bottom = bbox.ymin
10351035

1036-
self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi/72.0))
1037-
# self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi.get()/72.0)) MGDTODO
1036+
self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi / 72.0))
10381037

10391038
else:
10401039
if not len(bboxes2):
@@ -1057,7 +1056,6 @@ def _update_offset_text_position(self, bboxes, bboxes2):
10571056
bbox = Bbox.union(bboxes)
10581057
bottom = bbox.ymin
10591058
self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi/72.0))
1060-
# self.offsetText.set_position((x, bottom-self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO
10611059

10621060
def set_ticks_position(self, position):
10631061
"""
@@ -1225,7 +1223,6 @@ def _update_label_position(self, bboxes, bboxes2):
12251223
left = bbox.xmin
12261224

12271225
self.label.set_position( (left-self.LABELPAD*self.figure.dpi/72.0, y))
1228-
# self.label.set_position( (left-self.LABELPAD*self.figure.dpi.get()/72.0, y)) MGDTODO
12291226

12301227
else:
12311228
if not len(bboxes2):
@@ -1245,7 +1242,6 @@ def _update_offset_text_position(self, bboxes, bboxes2):
12451242
x,y = self.offsetText.get_position()
12461243
top = self.axes.bbox.ymax
12471244
self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi/72.0))
1248-
# self.offsetText.set_position((x, top+self.OFFSETTEXTPAD*self.figure.dpi.get()/72.0)) MGDTODO
12491245

12501246
def set_offset_position(self, position):
12511247
assert position == 'left' or position == 'right'

lib/matplotlib/backend_bases.py

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ class RendererBase:
1818
"""An abstract base class to handle drawing/rendering operations
1919
"""
2020
# This will cache paths across rendering instances
21-
# Each subclass of RenderBase should define this -->
22-
# _paths = weakref.WeakKeyDictionary()
21+
# Each subclass of RenderBase should define this a weak-keyed
22+
# dictionary to hold native paths
23+
# _native_paths = weakref.WeakKeyDictionary()
2324

2425
def __init__(self):
2526
self._texmanager = None
@@ -44,7 +45,7 @@ def _get_cached_native_path(self, path):
4445
self._native_paths[path] = native_path
4546
return native_path
4647

47-
def draw_path(self, gc, path, transform, rgbFace = None):
48+
def draw_path(self, gc, path, transform, rgbFace=None):
4849
"""
4950
Handles the caching of the native path associated with the
5051
given path and calls the underlying backend's _draw_path to
@@ -67,17 +68,31 @@ def convert_to_native_path(self, path):
6768
passed to them in draw_path.
6869
"""
6970
return path
71+
72+
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
73+
native_marker_path = self._get_cached_native_path(marker_path)
74+
self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace)
7075

71-
def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
72-
rotation):
76+
def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
7377
"""
74-
Draw an arc using GraphicsContext instance gcEdge, centered at x,y,
75-
with width and height and angles from 0.0 to 360.0
76-
0 degrees is at 3-o'clock
77-
positive angles are anti-clockwise
78-
draw rotated 'rotation' degrees anti-clockwise about x,y
78+
This method is currently underscore hidden because the
79+
draw_markers method is being used as a sentinel for newstyle
80+
backend drawing
7981
80-
If the color rgbFace is not None, fill the arc with it.
82+
path - a matplotlib.agg.path_storage instance
83+
84+
Draw the marker specified in path with graphics context gc at
85+
each of the locations in arrays x and y. trans is a
86+
matplotlib.transforms.Transformation instance used to
87+
transform x and y to display coords. It consists of an
88+
optional nonlinear component and an affine. You can access
89+
these two components as
90+
91+
if transform.need_nonlinear():
92+
x,y = transform.nonlinear_only_numerix(x, y)
93+
# the a,b,c,d,tx,ty affine which transforms x and y
94+
vec6 = transform.as_vec6_val()
95+
...backend dependent affine...
8196
"""
8297
raise NotImplementedError
8398

@@ -109,30 +124,21 @@ def option_image_nocomposite(self):
109124
"""
110125
return False
111126

112-
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
113-
native_marker_path = self._get_cached_native_path(marker_path)
114-
self._draw_native_markers(gc, native_marker_path, marker_trans, path, trans, rgbFace)
115-
116-
def _draw_native_markers(self, gc, native_marker_path, marker_trans, path, trans, rgbFace=None):
127+
######################################################################
128+
## OLD API IS BELOW
129+
## These functions no longer need to be implemented in the backends --
130+
## they now perform all of their functions in terms of the new API.
131+
132+
def draw_arc(self, gc, rgbFace, x, y, width, height, angle1, angle2,
133+
rotation):
117134
"""
118-
This method is currently underscore hidden because the
119-
draw_markers method is being used as a sentinel for newstyle
120-
backend drawing
121-
122-
path - a matplotlib.agg.path_storage instance
123-
124-
Draw the marker specified in path with graphics context gc at
125-
each of the locations in arrays x and y. trans is a
126-
matplotlib.transforms.Transformation instance used to
127-
transform x and y to display coords. It consists of an
128-
optional nonlinear component and an affine. You can access
129-
these two components as
135+
Draw an arc using GraphicsContext instance gcEdge, centered at x,y,
136+
with width and height and angles from 0.0 to 360.0
137+
0 degrees is at 3-o'clock
138+
positive angles are anti-clockwise
139+
draw rotated 'rotation' degrees anti-clockwise about x,y
130140
131-
if transform.need_nonlinear():
132-
x,y = transform.nonlinear_only_numerix(x, y)
133-
# the a,b,c,d,tx,ty affine which transforms x and y
134-
vec6 = transform.as_vec6_val()
135-
...backend dependent affine...
141+
If the color rgbFace is not None, fill the arc with it.
136142
"""
137143
raise NotImplementedError
138144

@@ -346,8 +352,10 @@ def draw_rectangle(self, gcEdge, rgbFace, x, y, width, height):
346352
347353
If rgbFace is not None, fill the rectangle with it.
348354
"""
349-
raise NotImplementedError
350-
355+
warnings.warn("draw_rectangle called", warnings.PendingDeprecationWarning)
356+
transform = transforms.Affine2D().scale(width, height).translate(x, y)
357+
self.draw_path(gcEdge, Path.unit_rectangle(), transform, rgbFace)
358+
351359
def draw_regpoly_collection(
352360
self, clipbox, offsets, transOffset, verts, sizes,
353361
facecolors, edgecolors, linewidths, antialiaseds):
@@ -1221,8 +1229,6 @@ def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w',
12211229
origfacecolor = self.figure.get_facecolor()
12221230
origedgecolor = self.figure.get_edgecolor()
12231231

1224-
# MGDTODO
1225-
# self.figure.dpi.set(dpi)
12261232
self.figure.dpi = dpi
12271233
self.figure.set_facecolor(facecolor)
12281234
self.figure.set_edgecolor(edgecolor)
@@ -1236,12 +1242,12 @@ def print_figure(self, filename, dpi=None, facecolor='w', edgecolor='w',
12361242
orientation=orientation,
12371243
**kwargs)
12381244
finally:
1239-
# MGDTODO
1240-
# self.figure.dpi.set(origDPI)
12411245
self.figure.dpi = origDPI
12421246
self.figure.set_facecolor(origfacecolor)
12431247
self.figure.set_edgecolor(origedgecolor)
12441248
self.figure.set_canvas(self)
1249+
1250+
self.draw()
12451251

12461252
return result
12471253

@@ -1623,8 +1629,8 @@ def push_current(self):
16231629
lims.append( (xmin, xmax, ymin, ymax) )
16241630
# Store both the original and modified positions
16251631
pos.append( (
1626-
a.get_position(True),
1627-
a.get_position() ) )
1632+
copy.copy(a.get_position(True)),
1633+
copy.copy(a.get_position() )) )
16281634
self._views.push(lims)
16291635
self._positions.push(pos)
16301636
self.set_history_buttons()

0 commit comments

Comments
 (0)
0