Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 136de27

Browse files
committed
Improve tightbbox speed for non-rectilinear axes
This significantly improves the speed of `Axes.get_tightbbox()` for non-rectilinear axes (in particular, PolarAxes and cartopy GeoAxes). By default, artists added to these axes have an undefined `clip_box` and have a `clip_path` equivalent to `TransformedPatchPath(ax.patch)` but whose extent is undefined until the aritst is drawn. Matplotlib currently skips artists in the `Axes.get_tightbbox()` computation only if their `clip_box` is defined and its extents are equivalent to `ax.bbox.extents`, but this fails to skip artists clipped by `TransformedPatchPath(ax.patch)`. This PR adds `ax.patch.get_window_extent()` to the list of bounding boxes used by `Axes.get_tightbbox()`, removes `Artist._get_clipping_extent_bbox()`, adds `Artist._is_axes_clipped()`, and prevents `Axes.get_tightbbox()` from drawing artists clipped by either `ax.bbox` or `ax.patch` (i.e. artists for which `Artist._is_axes_clipped()` returns True).
1 parent 0fc7c4b commit 136de27

File tree

2 files changed

+34
-27
lines changed

2 files changed

+34
-27
lines changed

lib/matplotlib/artist.py

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -320,22 +320,34 @@ def get_window_extent(self, renderer):
320320
"""
321321
return Bbox([[0, 0], [0, 0]])
322322

323-
def _get_clipping_extent_bbox(self):
324-
"""
325-
Return a bbox with the extents of the intersection of the clip_path
326-
and clip_box for this artist, or None if both of these are
327-
None, or ``get_clip_on`` is False.
328-
"""
329-
bbox = None
330-
if self.get_clip_on():
331-
clip_box = self.get_clip_box()
332-
if clip_box is not None:
333-
bbox = clip_box
334-
clip_path = self.get_clip_path()
335-
if clip_path is not None and bbox is not None:
336-
clip_path = clip_path.get_fully_transformed_path()
337-
bbox = Bbox.intersection(bbox, clip_path.get_extents())
338-
return bbox
323+
def _is_axes_clipped(self):
324+
"""
325+
Return a boolean indicating whether the artist is clipped by the
326+
axes bounding box or patch. True if ``get_clip_on`` is True, one of
327+
`clip_box` or `clip_path` is set, ``clip_box.extents`` is equivalent
328+
to ``axes.bbox.extents`` (if set), and ``clip_path._patch`` is
329+
equivalent to ``axes.patch`` (if set).
330+
"""
331+
# Note that ``clip_path.get_fully_transformed_path().get_extents()``
332+
# cannot be directly compared to ``axes.bbox.extents`` because the
333+
# extents may be undefined (i.e. negative to positive infinity)
334+
# before the associated artist is drawn, and this method is meant
335+
# to determine whether ``axes.get_tightbboe()`` may bypass drawing
336+
ax = self.axes
337+
clip_on = self.get_clip_on()
338+
clip_box = self.get_clip_box()
339+
clip_path = self.get_clip_path()
340+
if ax is None or not clip_on or clip_box is None and clip_path is None:
341+
return False
342+
if clip_box is not None:
343+
if not np.all(clip_box.extents == ax.bbox.extents):
344+
return False
345+
if clip_path is not None:
346+
if not isinstance(clip_path, TransformedPatchPath):
347+
return False
348+
if clip_path._patch is not ax.patch:
349+
return False
350+
return True
339351

340352
def get_tightbbox(self, renderer):
341353
"""

lib/matplotlib/axes/_base.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4626,6 +4626,8 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
46264626
self._update_title_position(renderer)
46274627
axbbox = self.get_window_extent(renderer)
46284628
bb.append(axbbox)
4629+
patchbbox = self.patch.get_window_extent(renderer)
4630+
bb.append(patchbbox)
46294631

46304632
for title in [self.title, self._left_title, self._right_title]:
46314633
if title.get_visible():
@@ -4642,18 +4644,11 @@ def get_tightbbox(self, renderer, call_axes_locator=True,
46424644
if bbox_artists is None:
46434645
bbox_artists = self.get_default_bbox_extra_artists()
46444646

4647+
include_types = (_AxesBase, maxis.Axis, mspines.Spine)
46454648
for a in bbox_artists:
4646-
# Extra check here to quickly see if clipping is on and
4647-
# contained in the Axes. If it is, don't get the tightbbox for
4648-
# this artist because this can be expensive:
4649-
clip_extent = a._get_clipping_extent_bbox()
4650-
if clip_extent is not None:
4651-
clip_extent = mtransforms.Bbox.intersection(
4652-
clip_extent, axbbox)
4653-
if np.all(clip_extent.extents == axbbox.extents):
4654-
# clip extent is inside the Axes bbox so don't check
4655-
# this artist
4656-
continue
4649+
# skip artists clipped by ax.bbox or ax.patch
4650+
if a._is_axes_clipped() and not isinstance(a, include_types):
4651+
continue
46574652
bbox = a.get_tightbbox(renderer)
46584653
if (bbox is not None
46594654
and 0 < bbox.width < np.inf

0 commit comments

Comments
 (0)