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

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3222,6 +3222,10 @@ def draw(self, renderer):

self._update_title_position(renderer)

# Clear axis tick caches for this draw cycle
for _axis in self._axis_map.values():
_axis._clear_ticks_cache()

if not self.axison:
for _axis in self._axis_map.values():
artists.remove(_axis)
Expand Down
86 changes: 62 additions & 24 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,9 @@ def __init__(self, axes, *, pickradius=15, clear=True):
self._major_tick_kw = dict()
self._minor_tick_kw = dict()

self._cached_ticks_to_draw = None
self._cached_ticklabel_bboxes = None

if clear:
self.clear()
else:
Expand Down Expand Up @@ -1302,11 +1305,19 @@ def _tick_group_visible(kw):
kw.get('label2On') is not False or
kw.get('gridOn') is not False)

def _update_ticks(self):
def _clear_ticks_cache(self):
self._cached_ticks_to_draw = None
self._cached_ticklabel_bboxes = None

def _update_ticks(self, *, _use_cache=False):
"""
Update ticks (position and labels) using the current data interval of
the axes. Return the list of ticks that will be drawn.
"""
# Return cached result if available and requested
if _use_cache and self._cached_ticks_to_draw is not None:
return self._cached_ticks_to_draw

# Check if major ticks should be computed.
# Skip if using NullLocator or if all visible components are off.
if (self._tick_group_visible(self._major_tick_kw)
Expand Down Expand Up @@ -1367,16 +1378,28 @@ def _update_ticks(self):
if mtransforms._interval_contains_close(interval_t, loc_t):
ticks_to_draw.append(tick)

# Only cache the result when called from the draw path
if _use_cache:
self._cached_ticks_to_draw = ticks_to_draw
return ticks_to_draw

def _get_ticklabel_bboxes(self, ticks, renderer):
def _get_ticklabel_bboxes(self, ticks, renderer, *, _use_cache=False):
"""Return lists of bboxes for ticks' label1's and label2's."""
return ([tick.label1.get_window_extent(renderer)
for tick in ticks
if tick.label1.get_visible() and tick.label1.get_in_layout()],
[tick.label2.get_window_extent(renderer)
for tick in ticks
if tick.label2.get_visible() and tick.label2.get_in_layout()])
# Return cached result if available and requested
if _use_cache and self._cached_ticklabel_bboxes is not None:
return self._cached_ticklabel_bboxes

result = ([tick.label1.get_window_extent(renderer)
for tick in ticks
if tick.label1.get_visible() and tick.label1.get_in_layout()],
[tick.label2.get_window_extent(renderer)
for tick in ticks
if tick.label2.get_visible() and tick.label2.get_in_layout()])

# Only cache the result when called from the draw path
if _use_cache:
self._cached_ticklabel_bboxes = result
return result

def get_tightbbox(self, renderer=None, *, for_layout_only=False):
"""
Expand All @@ -1392,12 +1415,18 @@ def get_tightbbox(self, renderer=None, *, for_layout_only=False):
return
if renderer is None:
renderer = self.get_figure(root=True)._get_renderer()
ticks_to_draw = self._update_ticks()
# We need to reset the ticks cache here - get_tightbbox() is called
# during layout calculations (e.g., constrained_layout) outside of
# draw(), and must always recalculate to reflect current state.
self._clear_ticks_cache()

self._update_label_position(renderer)
ticks_to_draw = self._update_ticks(_use_cache=True)

self._update_label_position(renderer, _use_cache=True)

# go back to just this axis's tick labels
tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer,
_use_cache=True)

self._update_offset_text_position(tlb1, tlb2)
self.offsetText.set_text(self.major.formatter.get_offset())
Expand All @@ -1423,6 +1452,8 @@ def get_tightbbox(self, renderer=None, *, for_layout_only=False):
bb.y1 = bb.y0 + 1.0
bboxes.append(bb)
bboxes = [b for b in bboxes if b._is_finite()]
self._clear_ticks_cache()

if bboxes:
return mtransforms.Bbox.union(bboxes)
else:
Expand All @@ -1444,21 +1475,25 @@ def draw(self, renderer):
return
renderer.open_group(__name__, gid=self.get_gid())

ticks_to_draw = self._update_ticks()
tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
self._clear_ticks_cache()

ticks_to_draw = self._update_ticks(_use_cache=True)
tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer,
_use_cache=True)

for tick in ticks_to_draw:
tick.draw(renderer)

# Shift label away from axes to avoid overlapping ticklabels.
self._update_label_position(renderer)
self._update_label_position(renderer, _use_cache=True)
self.label.draw(renderer)

self._update_offset_text_position(tlb1, tlb2)
self.offsetText.set_text(self.major.formatter.get_offset())
self.offsetText.draw(renderer)

renderer.close_group(__name__)
self._clear_ticks_cache()
self.stale = False

def get_gridlines(self):
Expand Down Expand Up @@ -1494,15 +1529,15 @@ def get_pickradius(self):

def get_majorticklabels(self):
"""Return this Axis' major tick labels, as a list of `~.text.Text`."""
self._update_ticks()
self._update_ticks(_use_cache=False)
ticks = self.get_major_ticks()
labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()]
labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()]
return labels1 + labels2

def get_minorticklabels(self):
"""Return this Axis' minor tick labels, as a list of `~.text.Text`."""
self._update_ticks()
self._update_ticks(_use_cache=False)
ticks = self.get_minor_ticks()
labels1 = [tick.label1 for tick in ticks if tick.label1.get_visible()]
labels2 = [tick.label2 for tick in ticks if tick.label2.get_visible()]
Expand Down Expand Up @@ -2264,7 +2299,7 @@ def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs):
self.set_ticklabels(labels, minor=minor, **kwargs)
return result

def _get_tick_boxes_siblings(self, renderer):
def _get_tick_boxes_siblings(self, renderer, *, _use_cache=False):
"""
Get the bounding boxes for this `.axis` and its siblings
as set by `.Figure.align_xlabels` or `.Figure.align_ylabels`.
Expand All @@ -2281,13 +2316,14 @@ def _get_tick_boxes_siblings(self, renderer):
# If we want to align labels from other Axes:
for ax in grouper.get_siblings(self.axes):
axis = ax._axis_map[name]
ticks_to_draw = axis._update_ticks()
tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer)
ticks_to_draw = axis._update_ticks(_use_cache=_use_cache)
tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer,
_use_cache=_use_cache)
bboxes.extend(tlb)
bboxes2.extend(tlb2)
return bboxes, bboxes2

def _update_label_position(self, renderer):
def _update_label_position(self, renderer, *, _use_cache=False):
"""
Update the label position based on the bounding box enclosing
all the ticklabels and axis spine.
Expand Down Expand Up @@ -2484,7 +2520,7 @@ def set_label_position(self, position):
self.label_position = position
self.stale = True

def _update_label_position(self, renderer):
def _update_label_position(self, renderer, *, _use_cache=False):
"""
Update the label position based on the bounding box enclosing
all the ticklabels and axis spine
Expand All @@ -2494,7 +2530,8 @@ def _update_label_position(self, renderer):

# get bounding boxes for this axis and any siblings
# that have been set by `fig.align_xlabels()`
bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer,
_use_cache=_use_cache)
x, y = self.label.get_position()

if self.label_position == 'bottom':
Expand Down Expand Up @@ -2711,7 +2748,7 @@ def set_label_position(self, position):
self.label_position = position
self.stale = True

def _update_label_position(self, renderer):
def _update_label_position(self, renderer, *, _use_cache=False):
"""
Update the label position based on the bounding box enclosing
all the ticklabels and axis spine
Expand All @@ -2721,7 +2758,8 @@ def _update_label_position(self, renderer):

# get bounding boxes for this axis and any siblings
# that have been set by `fig.align_ylabels()`
bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer,
_use_cache=_use_cache)
x, y = self.label.get_position()

if self.label_position == 'left':
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/spines.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def get_window_extent(self, renderer=None):
if self.axis is None or not self.axis.get_visible():
return bb
bboxes = [bb]
drawn_ticks = self.axis._update_ticks()
drawn_ticks = self.axis._update_ticks(_use_cache=True)

major_tick = next(iter({*drawn_ticks} & {*self.axis.majorTicks}), None)
minor_tick = next(iter({*drawn_ticks} & {*self.axis.minorTicks}), None)
Expand Down
2 changes: 2 additions & 0 deletions lib/mpl_toolkits/mplot3d/axes3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,8 @@ def draw(self, renderer):
artist.do_3d_projection()

if self._axis3don:
for axis in self._axis_map.values():
axis._clear_ticks_cache()
# Draw panes first
for axis in self._axis_map.values():
axis.draw_pane(renderer)
Expand Down
15 changes: 12 additions & 3 deletions lib/mpl_toolkits/mplot3d/axis3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ def _axmask(self):

def _draw_ticks(self, renderer, edgep1, centers, deltas, highs,
deltas_per_point, pos):
ticks = self._update_ticks()
ticks = self._update_ticks(_use_cache=True)
info = self._axinfo
index = info["i"]
juggled = info["juggled"]
Expand Down Expand Up @@ -569,6 +569,7 @@ def draw(self, renderer):
self.label._transform = self.axes.transData
self.offsetText._transform = self.axes.transData
renderer.open_group("axis3d", gid=self.get_gid())
self._clear_ticks_cache()

# Get general axis information:
mins, maxs, tc, highs = self._get_coord_info()
Expand Down Expand Up @@ -627,6 +628,7 @@ def draw(self, renderer):
self._draw_labels(renderer, edgep1, edgep2, labeldeltas, centers, dx, dy)

renderer.close_group('axis3d')
self._clear_ticks_cache()
self.stale = False

@artist.allow_rasterization
Expand All @@ -636,7 +638,7 @@ def draw_grid(self, renderer):

renderer.open_group("grid3d", gid=self.get_gid())

ticks = self._update_ticks()
ticks = self._update_ticks(_use_cache=True)
if len(ticks):
# Get general axis information:
info = self._axinfo
Expand Down Expand Up @@ -674,6 +676,12 @@ def get_tightbbox(self, renderer=None, *, for_layout_only=False):
# docstring inherited
if not self.get_visible():
return

# We need to reset the ticks cache here - get_tightbbox() is called
# during layout calculations (e.g., constrained_layout) outside of
# draw(), and must always recalculate to reflect current state.
self._clear_ticks_cache()

# We have to directly access the internal data structures
# (and hope they are up to date) because at draw time we
# shift the ticks and their labels around in (x, y) space
Expand Down Expand Up @@ -705,7 +713,7 @@ def get_tightbbox(self, renderer=None, *, for_layout_only=False):

ticks = ticks_to_draw

bb_1, bb_2 = self._get_ticklabel_bboxes(ticks, renderer)
bb_1, bb_2 = self._get_ticklabel_bboxes(ticks, renderer, _use_cache=True)
other = []

if self.offsetText.get_visible() and self.offsetText.get_text():
Expand All @@ -716,6 +724,7 @@ def get_tightbbox(self, renderer=None, *, for_layout_only=False):
self.label.get_text()):
other.append(self.label.get_window_extent(renderer))

self._clear_ticks_cache()
return mtransforms.Bbox.union([*bb_1, *bb_2, *other])

d_interval = _api.deprecated(
Expand Down
Loading