Skip to content

Commit a080a71

Browse files
committed
ENH: ax.add_collection(..., autolim=True) updates view limits
This makes explicit calls to `autoscale_view()` or `_request_autoscale_view()` unnecessary. 3D Axes have a special `auto_scale_xyz()`, also there's a mixture of `add_collection()` and `add_collection3d()`. This needs separate sorting . I've added a private value `autolim="_datalim_only"` to keep the behavior for 3D Axes unchanged for now. That will be resolved by a follow-up PR. I believe it's getting too complicated if we fold this into the 2D change.
1 parent 011d12f commit a080a71

File tree

16 files changed

+57
-36
lines changed

16 files changed

+57
-36
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/shapes_and_collections/collections.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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)')

galleries/examples/shapes_and_collections/ellipse_collection.py

Lines changed: 0 additions & 1 deletion
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)

galleries/users_explain/axes/autoscale.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,3 @@
177177
offset_transform=ax.transData, # Propagate transformations of the Axes
178178
)
179179
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
@@ -3028,7 +3028,6 @@ def broken_barh(self, xranges, yrange, **kwargs):
30283028

30293029
col = mcoll.PolyCollection(np.array(vertices), **kwargs)
30303030
self.add_collection(col, autolim=True)
3031-
self._request_autoscale_view()
30323031

30333032
return col
30343033

@@ -5337,7 +5336,6 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None,
53375336
self.set_ymargin(0.05)
53385337

53395338
self.add_collection(collection)
5340-
self._request_autoscale_view()
53415339

53425340
return collection
53435341

@@ -5808,7 +5806,6 @@ def quiver(self, *args, **kwargs):
58085806
args = self._quiver_units(args, kwargs)
58095807
q = mquiver.Quiver(self, *args, **kwargs)
58105808
self.add_collection(q, autolim=True)
5811-
self._request_autoscale_view()
58125809
return q
58135810

58145811
# args can be some combination of X, Y, U, V, C and all should be replaced
@@ -5820,7 +5817,6 @@ def barbs(self, *args, **kwargs):
58205817
args = self._quiver_units(args, kwargs)
58215818
b = mquiver.Barbs(self, *args, **kwargs)
58225819
self.add_collection(b, autolim=True)
5823-
self._request_autoscale_view()
58245820
return b
58255821

58265822
# Uses a custom implementation of data-kwarg handling in
@@ -5980,7 +5976,6 @@ def _fill_between_x_or_y(
59805976
where=where, interpolate=interpolate, step=step, **kwargs)
59815977

59825978
self.add_collection(collection)
5983-
self._request_autoscale_view()
59845979
return collection
59855980

59865981
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

lib/matplotlib/tests/test_collections.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,6 @@ def test_EllipseCollection():
408408
ww, hh, aa, units='x', offsets=XY, offset_transform=ax.transData,
409409
facecolors='none')
410410
ax.add_collection(ec)
411-
ax.autoscale_view()
412411

413412

414413
def test_EllipseCollection_setter_getter():
@@ -526,7 +525,6 @@ def test_regularpolycollection_rotate():
526525
4, sizes=(100,), rotation=alpha,
527526
offsets=[xy], offset_transform=ax.transData)
528527
ax.add_collection(col, autolim=True)
529-
ax.autoscale_view()
530528

531529

532530
@image_comparison(['regularpolycollection_scale.png'], remove_text=True)

0 commit comments

Comments
 (0)