Skip to content

Commit dfc888c

Browse files
authored
Merge pull request #29958 from timhoffm/add_collection_viewlims
ENH: ax.add_collection(..., autolim=True) updates view limits
2 parents b165927 + 5144212 commit dfc888c

File tree

17 files changed

+61
-62
lines changed

17 files changed

+61
-62
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
``Axes.add_collection(..., autolim=True)`` updates view limits
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
``Axes.add_collection(..., autolim=True)`` has so far only updated the data limits.
5+
Users needed to additionally call `.Axes.autoscale_view` to update the view limits.
6+
View limits are now updated as well if ``autolim=True``, using a lazy internal
7+
update mechanism, so that the costs only apply once also if you add multiple
8+
collections.

galleries/examples/lines_bars_and_markers/multicolored_line.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ def colored_line(x, y, c, ax=None, **lc_kwargs):
7272
# Plot the line collection to the axes
7373
ax = ax or plt.gca()
7474
ax.add_collection(lc)
75-
ax.autoscale_view()
7675

7776
return lc
7877

galleries/examples/shapes_and_collections/collections.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
2-
=========================================================
3-
Line, Poly and RegularPoly Collection with autoscaling
4-
=========================================================
2+
=====================================
3+
Line, Poly and RegularPoly Collection
4+
=====================================
55
66
For the first two subplots, we will use spirals. Their size will be set in
77
plot units, not data units. Their positions will be set in data units by using
@@ -38,7 +38,7 @@
3838
# Make some offsets
3939
xyo = rs.randn(npts, 2)
4040

41-
# Make a list of colors cycling through the default series.
41+
# Make a list of colors from the default color cycle.
4242
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
4343

4444
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
@@ -59,14 +59,12 @@
5959
# but it is good enough to generate a plot that you can use
6060
# as a starting point. If you know beforehand the range of
6161
# x and y that you want to show, it is better to set them
62-
# explicitly, leave out the *autolim* keyword argument (or set it to False),
63-
# and omit the 'ax1.autoscale_view()' call below.
62+
# explicitly, set the *autolim* keyword argument to False.
6463

6564
# Make a transform for the line segments such that their size is
6665
# given in points:
6766
col.set_color(colors)
6867

69-
ax1.autoscale_view() # See comment above, after ax1.add_collection.
7068
ax1.set_title('LineCollection using offsets')
7169

7270

@@ -79,7 +77,6 @@
7977
col.set_color(colors)
8078

8179

82-
ax2.autoscale_view()
8380
ax2.set_title('PolyCollection using offsets')
8481

8582
# 7-sided regular polygons
@@ -90,7 +87,6 @@
9087
col.set_transform(trans) # the points to pixels transform
9188
ax3.add_collection(col, autolim=True)
9289
col.set_color(colors)
93-
ax3.autoscale_view()
9490
ax3.set_title('RegularPolyCollection using offsets')
9591

9692

@@ -114,7 +110,6 @@
114110
col = collections.LineCollection(segs, offsets=offs)
115111
ax4.add_collection(col, autolim=True)
116112
col.set_color(colors)
117-
ax4.autoscale_view()
118113
ax4.set_title('Successive data offsets')
119114
ax4.set_xlabel('Zonal velocity component (m/s)')
120115
ax4.set_ylabel('Depth (m)')
@@ -136,6 +131,5 @@
136131
# - `matplotlib.collections.LineCollection`
137132
# - `matplotlib.collections.RegularPolyCollection`
138133
# - `matplotlib.axes.Axes.add_collection`
139-
# - `matplotlib.axes.Axes.autoscale_view`
140134
# - `matplotlib.transforms.Affine2D`
141135
# - `matplotlib.transforms.Affine2D.scale`

galleries/examples/shapes_and_collections/ellipse_collection.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
offset_transform=ax.transData)
3131
ec.set_array((X + Y).ravel())
3232
ax.add_collection(ec)
33-
ax.autoscale_view()
3433
ax.set_xlabel('X')
3534
ax.set_ylabel('y')
3635
cbar = plt.colorbar(ec)
@@ -47,5 +46,4 @@
4746
# - `matplotlib.collections`
4847
# - `matplotlib.collections.EllipseCollection`
4948
# - `matplotlib.axes.Axes.add_collection`
50-
# - `matplotlib.axes.Axes.autoscale_view`
5149
# - `matplotlib.cm.ScalarMappable.set_array`

galleries/users_explain/axes/autoscale.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import matplotlib.pyplot as plt
1919
import numpy as np
2020

21-
import matplotlib as mpl
2221

2322
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
2423
y = np.sinc(x)
@@ -159,22 +158,3 @@
159158
ax.autoscale(enable=None, axis="x", tight=True)
160159

161160
print(ax.margins())
162-
163-
# %%
164-
# Working with collections
165-
# ------------------------
166-
#
167-
# Autoscale works out of the box for all lines, patches, and images added to
168-
# the Axes. One of the artists that it won't work with is a `.Collection`.
169-
# After adding a collection to the Axes, one has to manually trigger the
170-
# `~matplotlib.axes.Axes.autoscale_view()` to recalculate
171-
# axes limits.
172-
173-
fig, ax = plt.subplots()
174-
collection = mpl.collections.StarPolygonCollection(
175-
5, rotation=0, sizes=(250,), # five point star, zero angle, size 250px
176-
offsets=np.column_stack([x, y]), # Set the positions
177-
offset_transform=ax.transData, # Propagate transformations of the Axes
178-
)
179-
ax.add_collection(collection)
180-
ax.autoscale_view()

lib/matplotlib/axes/_axes.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3044,7 +3044,6 @@ def broken_barh(self, xranges, yrange, align="bottom", **kwargs):
30443044

30453045
col = mcoll.PolyCollection(np.array(vertices), **kwargs)
30463046
self.add_collection(col, autolim=True)
3047-
self._request_autoscale_view()
30483047

30493048
return col
30503049

@@ -5353,7 +5352,6 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
53535352
self.set_ymargin(0.05)
53545353

53555354
self.add_collection(collection)
5356-
self._request_autoscale_view()
53575355

53585356
return collection
53595357

@@ -5824,7 +5822,6 @@ def quiver(self, *args, **kwargs):
58245822
args = self._quiver_units(args, kwargs)
58255823
q = mquiver.Quiver(self, *args, **kwargs)
58265824
self.add_collection(q, autolim=True)
5827-
self._request_autoscale_view()
58285825
return q
58295826

58305827
# args can be some combination of X, Y, U, V, C and all should be replaced
@@ -5836,7 +5833,6 @@ def barbs(self, *args, **kwargs):
58365833
args = self._quiver_units(args, kwargs)
58375834
b = mquiver.Barbs(self, *args, **kwargs)
58385835
self.add_collection(b, autolim=True)
5839-
self._request_autoscale_view()
58405836
return b
58415837

58425838
# Uses a custom implementation of data-kwarg handling in
@@ -5996,7 +5992,6 @@ def _fill_between_x_or_y(
59965992
where=where, interpolate=interpolate, step=step, **kwargs)
59975993

59985994
self.add_collection(collection)
5999-
self._request_autoscale_view()
60005995
return collection
60015996

60025997
def _fill_between_process_units(self, ind_dir, dep_dir, ind, dep1, dep2, **kwargs):

lib/matplotlib/axes/_base.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2336,6 +2336,23 @@ def add_child_axes(self, ax):
23362336
def add_collection(self, collection, autolim=True):
23372337
"""
23382338
Add a `.Collection` to the Axes; return the collection.
2339+
2340+
Parameters
2341+
----------
2342+
collection : `.Collection`
2343+
The collection to add.
2344+
autolim : bool
2345+
Whether to update data and view limits.
2346+
2347+
.. versionchanged:: 3.11
2348+
2349+
This now also updates the view limits, making explicit
2350+
calls to `~.Axes.autoscale_view` unnecessary.
2351+
2352+
As an implementation detail, the value "_datalim_only" is
2353+
supported to smooth the internal transition from pre-3.11
2354+
behavior. This is not a public interface and will be removed
2355+
again in the future.
23392356
"""
23402357
_api.check_isinstance(mcoll.Collection, collection=collection)
23412358
if not collection.get_label():
@@ -2371,6 +2388,8 @@ def add_collection(self, collection, autolim=True):
23712388
updatex=x_is_data or ox_is_data,
23722389
updatey=y_is_data or oy_is_data,
23732390
)
2391+
if autolim != "_datalim_only":
2392+
self._request_autoscale_view()
23742393

23752394
self.stale = True
23762395
return collection

lib/matplotlib/axes/_base.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ class _AxesBase(martist.Artist):
234234
def add_artist(self, a: Artist) -> Artist: ...
235235
def add_child_axes(self, ax: _AxesBase) -> _AxesBase: ...
236236
def add_collection(
237-
self, collection: Collection, autolim: bool = ...
237+
self, collection: Collection, autolim: bool | Literal["_datalim_only"] = ...
238238
) -> Collection: ...
239239
def add_image(self, image: AxesImage) -> AxesImage: ...
240240
def add_line(self, line: Line2D) -> Line2D: ...

lib/matplotlib/colorbar.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ def __init__(
373373
colors=[mpl.rcParams['axes.edgecolor']],
374374
linewidths=[0.5 * mpl.rcParams['axes.linewidth']],
375375
clip_on=False)
376-
self.ax.add_collection(self.dividers)
376+
self.ax.add_collection(self.dividers, autolim=False)
377377

378378
self._locator = None
379379
self._minorlocator = None
@@ -807,7 +807,7 @@ def add_lines(self, *args, **kwargs):
807807
xy = self.ax.transAxes.inverted().transform(inches.transform(xy))
808808
col.set_clip_path(mpath.Path(xy, closed=True),
809809
self.ax.transAxes)
810-
self.ax.add_collection(col)
810+
self.ax.add_collection(col, autolim=False)
811811
self.stale = True
812812

813813
def update_ticks(self):

lib/matplotlib/tests/test_backend_ps.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,11 @@ def test_path_collection():
354354
sizes = [0.02, 0.04]
355355
pc = mcollections.PathCollection(paths, sizes, zorder=-1,
356356
facecolors='yellow', offsets=offsets)
357-
ax.add_collection(pc)
357+
# Note: autolim=False is used to keep the view limits as is for now,
358+
# given the updated behavior of autolim=True to also update the view
359+
# limits. It may be reasonable to test the limits handling in the future
360+
# as well. This will require regenerating the reference image.
361+
ax.add_collection(pc, autolim=False)
358362
ax.set_xlim(0, 1)
359363

360364

0 commit comments

Comments
 (0)