From 255f213cd9fc6f458e13bb28fc43e8cb636b2158 Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Sat, 19 Jan 2013 19:08:34 -0500 Subject: [PATCH 01/12] Added a strategy to stop losing ticks on the ends of an axis. The approach that is used is to pad the interval that is being used to "clip" ticks from the axis because they are off the end. The padding is chosen specifically such that the tick cannot be drawn a pixel past the end of the axis. So long as the tick is within this bound, it should be OK to draw it. --- lib/matplotlib/axis.py | 49 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index aa91377c9c5d..5ae6e6b381cd 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -972,11 +972,24 @@ def _update_ticks(self, renderer): tick_tups = [ti for ti in tick_tups if (ti[1] >= ilow) and (ti[1] <= ihigh)] + # so that we don't lose ticks on the end, expand out the interval ever so slightly. The + # "ever so slightly" is defined to be the width of a half of a pixel. We don't want to draw + # a tick that even one pixel outside of the defined axis interval. + if interval[0] <= interval[1]: + interval_expanded = interval + else: + interval_expanded = interval[1],interval[0] + + if hasattr(self,'_get_pixel_distance_along_axis'): + ds1 = self._get_pixel_distance_along_axis(interval_expanded[0],-1)/2.0 + ds2 = self._get_pixel_distance_along_axis(interval_expanded[1],+1)/2.0 + interval_expanded = (interval[0]-ds1,interval[1]+ds2) + ticks_to_draw = [] for tick, loc, label in tick_tups: if tick is None: continue - if not mtransforms.interval_contains(interval, loc): + if not mtransforms.interval_contains(interval_expanded, loc): continue tick.update_position(loc) tick.set_label1(label) @@ -1599,6 +1612,24 @@ def _get_offset_text(self): self.offset_text_position = 'bottom' return offsetText + def _get_pixel_distance_along_axis(self,where,perturb): + # returns the amount, in data coordinates, that a single pixel corresponds to in the + # locality given by "where", which is also given in data coordinates, and is an x coordinate. + # "perturb" is the amount to perturb the pixel. Usually +1 or -1. + + # first figure out the pixel location of the "where" point. We use 1e-10 for the + # y point, so that we remain compatible with log axes. + # + # I THINK this will work too for polar axes, but I'm not 100% sure. + # + trans = self.axes.transData # transformation from data coords to display coords + transinv = trans.inverted() # transformation from display coords to data coords + pix = trans.transform_point((where,1e-10)) + ptp = transinv.transform_point((pix[0]+perturb,pix[1])) # perturb the pixel. + dx = abs(ptp[0]-where) + + return dx + def get_label_position(self): """ Return the label position (top or bottom) @@ -1874,6 +1905,22 @@ def _get_offset_text(self): self.offset_text_position = 'left' return offsetText + def _get_pixel_distance_along_axis(self,where,perturb): + # returns the amount, in data coordinates, that a single pixel corresponds to in the + # locality given by "where", which is also given in data coordinates, and is a y coordinate. + # "perturb" is the amount to perturb the pixel. Usually +1 or -1. + + # first figure out the pixel location of the "where" point. We use 1e-10 for the + # x point, so that we remain compatible with log axes. + # + # I THINK this will work too for polar axes, but I'm not 100% sure. + # + trans = self.axes.transData # transformation from data coords to display coords + transinv = trans.inverted() # transformation from display coords to data coords + pix = trans.transform_point((1e-10,where)) + ptp = transinv.transform_point((pix[0],pix[1]+perturb)) # perturb the pixel. + dy = abs(ptp[1]-where) + def get_label_position(self): """ Return the label position (left or right) From 4c0f3f30f805f53f91f22f26212d366c59d2ae48 Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Sat, 19 Jan 2013 19:14:03 -0500 Subject: [PATCH 02/12] Modifications to MultipleLocator and LogLocator, so that the locators will give locations one past what they really need to. This is necessary, because in situations where round off error matters, we want the locator to offer more that it really thinks it needs to. The clipping of ticks still takes place in the Axis class, so it's perfectly fine to add more locs than necessary. In fact, matplotlib relies on the clipping behavior in the Axis class already to not draw ticks outside of the limits of the axis. --- lib/matplotlib/ticker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 175508880926..6aa1202e4956 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1165,7 +1165,7 @@ def tick_values(self, vmin, vmax): vmin = self._base.ge(vmin) base = self._base.get_base() n = (vmax - vmin + 0.001 * base) // base - locs = vmin + np.arange(n + 1) * base + locs = vmin-base + np.arange(n + 3) * base return self.raise_if_exceeds(locs) def view_limits(self, dmin, dmax): @@ -1450,8 +1450,8 @@ def tick_values(self, vmin, vmax): while numdec / stride + 1 > self.numticks: stride += 1 - decades = np.arange(math.floor(vmin), - math.ceil(vmax) + stride, stride) + decades = np.arange(math.floor(vmin)-1.0, + math.ceil(vmax) + 1.0 + stride, stride) if hasattr(self, '_transform'): ticklocs = self._transform.inverted().transform(decades) if len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0): From 63a40e47578a5013c4e8806b072b35bb5793aa3d Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Sat, 19 Jan 2013 19:24:08 -0500 Subject: [PATCH 03/12] oops, missed a return statement in my copy/pasting. --- lib/matplotlib/axis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 5ae6e6b381cd..73d7d077f22e 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1920,6 +1920,7 @@ def _get_pixel_distance_along_axis(self,where,perturb): pix = trans.transform_point((1e-10,where)) ptp = transinv.transform_point((pix[0],pix[1]+perturb)) # perturb the pixel. dy = abs(ptp[1]-where) + return dy def get_label_position(self): """ From 1f3959d1b2e0102d19eda811749ae742d9bb3c98 Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Fri, 25 Jan 2013 19:03:32 -0500 Subject: [PATCH 04/12] Fixed two of the failing tests for this "Fix lost ticks" branch, and fixed a problem in the calculation of the log tick locations. --- lib/matplotlib/tests/test_ticker.py | 8 ++++---- lib/matplotlib/ticker.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 116f900a3ec7..d0af97687b40 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -26,7 +26,7 @@ def test_LinearLocator(): def test_MultipleLocator(): loc = mticker.MultipleLocator(base=3.147) - test_value = np.array([-6.294, -3.147, 0., 3.147, 6.294, 9.441]) + test_value = np.array([-9.441, -6.294, -3.147, 0., 3.147, 6.294, 9.441, 12.588]) assert_almost_equal(loc.tick_values(-7, 10), test_value) @@ -35,12 +35,12 @@ def test_LogLocator(): assert_raises(ValueError, loc.tick_values, 0, 1000) - test_value = np.array([1.00000000e-03, 1.00000000e-01, 1.00000000e+01, - 1.00000000e+03, 1.00000000e+05, 1.00000000e+07]) + test_value = np.array([1.00000000e-05, 1.00000000e-03, 1.00000000e-01, 1.00000000e+01, + 1.00000000e+03, 1.00000000e+05, 1.00000000e+07, 1.000000000e+09]) assert_almost_equal(loc.tick_values(0.001, 1.1e5), test_value) loc = mticker.LogLocator(base=2) - test_value = np.array([1., 2., 4., 8., 16., 32., 64., 128.]) + test_value = np.array([0.5, 1., 2., 4., 8., 16., 32., 64., 128., 256.]) assert_almost_equal(loc.tick_values(1, 100), test_value) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 6aa1202e4956..68b700b57feb 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1450,8 +1450,8 @@ def tick_values(self, vmin, vmax): while numdec / stride + 1 > self.numticks: stride += 1 - decades = np.arange(math.floor(vmin)-1.0, - math.ceil(vmax) + 1.0 + stride, stride) + decades = np.arange(math.floor(vmin)-stride, + math.ceil(vmax) + 2*stride, stride) if hasattr(self, '_transform'): ticklocs = self._transform.inverted().transform(decades) if len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0): From a0a799820c5e3258f2cfbe7e4dbc08e2e7c71038 Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Sun, 27 Jan 2013 21:02:51 -0500 Subject: [PATCH 05/12] Fix the nose failure for polar plots; don't try to pad the interval for the theta axis of a polar plot. --- lib/matplotlib/axis.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 73d7d077f22e..d8b2acc524cc 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1620,8 +1620,14 @@ def _get_pixel_distance_along_axis(self,where,perturb): # first figure out the pixel location of the "where" point. We use 1e-10 for the # y point, so that we remain compatible with log axes. # - # I THINK this will work too for polar axes, but I'm not 100% sure. + # Note that this routine does not work for a polar axis, because of the 1e-10 below. To + # do things correctly, we need to use rmax instead of 1e-10 for a polar axis. But + # since we do not have that kind of information at this point, we just don't try to + # pad anything for the theta axis of a polar plot. # + if self.axes.name == 'polar': + return 0 + trans = self.axes.transData # transformation from data coords to display coords transinv = trans.inverted() # transformation from display coords to data coords pix = trans.transform_point((where,1e-10)) @@ -1913,8 +1919,6 @@ def _get_pixel_distance_along_axis(self,where,perturb): # first figure out the pixel location of the "where" point. We use 1e-10 for the # x point, so that we remain compatible with log axes. # - # I THINK this will work too for polar axes, but I'm not 100% sure. - # trans = self.axes.transData # transformation from data coords to display coords transinv = trans.inverted() # transformation from display coords to data coords pix = trans.transform_point((1e-10,where)) From 82db1d6bae0eb907b1c8605138f7025a0b77802b Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Sun, 27 Jan 2013 21:08:18 -0500 Subject: [PATCH 06/12] Since the _get_pixel_distance_along_axis routine is supposed to return a distance in data coordinates, it should return 0.0 instead of 0 when appropriate. --- lib/matplotlib/axis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index d8b2acc524cc..72970a365b40 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1626,7 +1626,7 @@ def _get_pixel_distance_along_axis(self,where,perturb): # pad anything for the theta axis of a polar plot. # if self.axes.name == 'polar': - return 0 + return 0.0 trans = self.axes.transData # transformation from data coords to display coords transinv = trans.inverted() # transformation from display coords to data coords From e2a6f92fc7d75167242fc31df2c711532baa523c Mon Sep 17 00:00:00 2001 From: dhyams Date: Mon, 11 Feb 2013 12:50:27 -0500 Subject: [PATCH 07/12] PEP8 fixes/protection try/except PEP8 fixes, and part of a comment changed to a docstring. Also added a try/except around each call to _get_pixel_distance_along_axis, so that if something goes wrong with some off-beat transform, we just don't pad; things revert back to old matplotlib behavior. --- lib/matplotlib/axis.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 72970a365b40..c17df38cca16 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -978,12 +978,18 @@ def _update_ticks(self, renderer): if interval[0] <= interval[1]: interval_expanded = interval else: - interval_expanded = interval[1],interval[0] + interval_expanded = interval[1], interval[0] if hasattr(self,'_get_pixel_distance_along_axis'): - ds1 = self._get_pixel_distance_along_axis(interval_expanded[0],-1)/2.0 - ds2 = self._get_pixel_distance_along_axis(interval_expanded[1],+1)/2.0 - interval_expanded = (interval[0]-ds1,interval[1]+ds2) + try: + ds1 = self._get_pixel_distance_along_axis(interval_expanded[0],-1)/2.0 + except: + ds1 = 0.0 + try: + ds2 = self._get_pixel_distance_along_axis(interval_expanded[1],+1)/2.0 + except: + ds2 = 0.0 + interval_expanded = (interval[0] - ds1, interval[1] + ds2) ticks_to_draw = [] for tick, loc, label in tick_tups: @@ -1613,26 +1619,26 @@ def _get_offset_text(self): return offsetText def _get_pixel_distance_along_axis(self,where,perturb): - # returns the amount, in data coordinates, that a single pixel corresponds to in the - # locality given by "where", which is also given in data coordinates, and is an x coordinate. - # "perturb" is the amount to perturb the pixel. Usually +1 or -1. + """ + Returns the amount, in data coordinates, that a single pixel corresponds to in the + locality given by "where", which is also given in data coordinates, and is an x coordinate. + "perturb" is the amount to perturb the pixel. Usually +1 or -1. + """ - # first figure out the pixel location of the "where" point. We use 1e-10 for the - # y point, so that we remain compatible with log axes. - # # Note that this routine does not work for a polar axis, because of the 1e-10 below. To # do things correctly, we need to use rmax instead of 1e-10 for a polar axis. But # since we do not have that kind of information at this point, we just don't try to # pad anything for the theta axis of a polar plot. - # if self.axes.name == 'polar': return 0.0 + #first figure out the pixel location of the "where" point. We use 1e-10 for the + #y point, so that we remain compatible with log axes. trans = self.axes.transData # transformation from data coords to display coords transinv = trans.inverted() # transformation from display coords to data coords - pix = trans.transform_point((where,1e-10)) - ptp = transinv.transform_point((pix[0]+perturb,pix[1])) # perturb the pixel. - dx = abs(ptp[0]-where) + pix = trans.transform_point((where, 1e-10)) + ptp = transinv.transform_point((pix[0] + perturb, pix[1])) # perturb the pixel. + dx = abs(ptp[0] - where) return dx From c1b988619ca17af01526f1ab3b09ea25953b9ba8 Mon Sep 17 00:00:00 2001 From: dhyams Date: Mon, 11 Feb 2013 12:53:31 -0500 Subject: [PATCH 08/12] PEP8 fixes. --- lib/matplotlib/ticker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 68b700b57feb..d281acb8f7bd 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1165,7 +1165,7 @@ def tick_values(self, vmin, vmax): vmin = self._base.ge(vmin) base = self._base.get_base() n = (vmax - vmin + 0.001 * base) // base - locs = vmin-base + np.arange(n + 3) * base + locs = vmin - base + np.arange(n + 3) * base return self.raise_if_exceeds(locs) def view_limits(self, dmin, dmax): @@ -1450,8 +1450,8 @@ def tick_values(self, vmin, vmax): while numdec / stride + 1 > self.numticks: stride += 1 - decades = np.arange(math.floor(vmin)-stride, - math.ceil(vmax) + 2*stride, stride) + decades = np.arange(math.floor(vmin) - stride, + math.ceil(vmax) + 2 * stride, stride) if hasattr(self, '_transform'): ticklocs = self._transform.inverted().transform(decades) if len(subs) > 1 or (len(subs == 1) and subs[0] != 1.0): From 43929de4915f779a5845fcd1b866422582750dc2 Mon Sep 17 00:00:00 2001 From: dhyams Date: Mon, 11 Feb 2013 13:03:30 -0500 Subject: [PATCH 09/12] more PEP8 fixes. --- lib/matplotlib/axis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index c17df38cca16..8baddcd9371a 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -982,11 +982,11 @@ def _update_ticks(self, renderer): if hasattr(self,'_get_pixel_distance_along_axis'): try: - ds1 = self._get_pixel_distance_along_axis(interval_expanded[0],-1)/2.0 + ds1 = self._get_pixel_distance_along_axis(interval_expanded[0], -1) / 2.0 except: ds1 = 0.0 try: - ds2 = self._get_pixel_distance_along_axis(interval_expanded[1],+1)/2.0 + ds2 = self._get_pixel_distance_along_axis(interval_expanded[1], +1) / 2.0 except: ds2 = 0.0 interval_expanded = (interval[0] - ds1, interval[1] + ds2) From 36ce1841f2eaccc1f206a98603f003e14257a808 Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Mon, 18 Feb 2013 13:31:33 -0500 Subject: [PATCH 10/12] More PEP8 fixes and comments --- lib/matplotlib/axis.py | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 8baddcd9371a..a07de5ca24fe 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -980,7 +980,7 @@ def _update_ticks(self, renderer): else: interval_expanded = interval[1], interval[0] - if hasattr(self,'_get_pixel_distance_along_axis'): + if hasattr(self, '_get_pixel_distance_along_axis'): try: ds1 = self._get_pixel_distance_along_axis(interval_expanded[0], -1) / 2.0 except: @@ -1618,11 +1618,14 @@ def _get_offset_text(self): self.offset_text_position = 'bottom' return offsetText - def _get_pixel_distance_along_axis(self,where,perturb): + def _get_pixel_distance_along_axis(self, where, perturb): """ Returns the amount, in data coordinates, that a single pixel corresponds to in the locality given by "where", which is also given in data coordinates, and is an x coordinate. "perturb" is the amount to perturb the pixel. Usually +1 or -1. + + Implementing this routine for an axis is optional; if present, it will ensure that no + ticks are lost due to round-off at the extreme ends of an axis. """ # Note that this routine does not work for a polar axis, because of the 1e-10 below. To @@ -1632,8 +1635,10 @@ def _get_pixel_distance_along_axis(self,where,perturb): if self.axes.name == 'polar': return 0.0 - #first figure out the pixel location of the "where" point. We use 1e-10 for the - #y point, so that we remain compatible with log axes. + # + # first figure out the pixel location of the "where" point. We use 1e-10 for the + # y point, so that we remain compatible with log axes. + # trans = self.axes.transData # transformation from data coords to display coords transinv = trans.inverted() # transformation from display coords to data coords pix = trans.transform_point((where, 1e-10)) @@ -1917,19 +1922,25 @@ def _get_offset_text(self): self.offset_text_position = 'left' return offsetText - def _get_pixel_distance_along_axis(self,where,perturb): - # returns the amount, in data coordinates, that a single pixel corresponds to in the - # locality given by "where", which is also given in data coordinates, and is a y coordinate. - # "perturb" is the amount to perturb the pixel. Usually +1 or -1. - + def _get_pixel_distance_along_axis(self, where, perturb): + """ + Returns the amount, in data coordinates, that a single pixel corresponds to in the + locality given by "where", which is also given in data coordinates, and is an y coordinate. + "perturb" is the amount to perturb the pixel. Usually +1 or -1. + + Implementing this routine for an axis is optional; if present, it will ensure that no + ticks are lost due to round-off at the extreme ends of an axis. + """ + + # # first figure out the pixel location of the "where" point. We use 1e-10 for the # x point, so that we remain compatible with log axes. # trans = self.axes.transData # transformation from data coords to display coords transinv = trans.inverted() # transformation from display coords to data coords - pix = trans.transform_point((1e-10,where)) - ptp = transinv.transform_point((pix[0],pix[1]+perturb)) # perturb the pixel. - dy = abs(ptp[1]-where) + pix = trans.transform_point((1e-10, where)) + ptp = transinv.transform_point((pix[0], pix[1] + perturb)) # perturb the pixel. + dy = abs(ptp[1] - where) return dy def get_label_position(self): From 7e608d4bbcf95c2f684f284385ec185f0babc3bd Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Mon, 18 Feb 2013 13:33:25 -0500 Subject: [PATCH 11/12] Pixel perturbation is now 0.5, for a more accurate calculation of exactly how much padding to add. --- lib/matplotlib/axis.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index a07de5ca24fe..10719a797ad1 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -982,11 +982,11 @@ def _update_ticks(self, renderer): if hasattr(self, '_get_pixel_distance_along_axis'): try: - ds1 = self._get_pixel_distance_along_axis(interval_expanded[0], -1) / 2.0 + ds1 = self._get_pixel_distance_along_axis(interval_expanded[0], -0.5) except: ds1 = 0.0 try: - ds2 = self._get_pixel_distance_along_axis(interval_expanded[1], +1) / 2.0 + ds2 = self._get_pixel_distance_along_axis(interval_expanded[1], +0.5) except: ds2 = 0.0 interval_expanded = (interval[0] - ds1, interval[1] + ds2) @@ -1622,7 +1622,7 @@ def _get_pixel_distance_along_axis(self, where, perturb): """ Returns the amount, in data coordinates, that a single pixel corresponds to in the locality given by "where", which is also given in data coordinates, and is an x coordinate. - "perturb" is the amount to perturb the pixel. Usually +1 or -1. + "perturb" is the amount to perturb the pixel. Usually +0.5 or -0.5. Implementing this routine for an axis is optional; if present, it will ensure that no ticks are lost due to round-off at the extreme ends of an axis. @@ -1926,7 +1926,7 @@ def _get_pixel_distance_along_axis(self, where, perturb): """ Returns the amount, in data coordinates, that a single pixel corresponds to in the locality given by "where", which is also given in data coordinates, and is an y coordinate. - "perturb" is the amount to perturb the pixel. Usually +1 or -1. + "perturb" is the amount to perturb the pixel. Usually +0.5 or -0.5. Implementing this routine for an axis is optional; if present, it will ensure that no ticks are lost due to round-off at the extreme ends of an axis. From 3cf7ea53cf00b291790a72cda2f6da206509d4bc Mon Sep 17 00:00:00 2001 From: Daniel Hyams Date: Mon, 18 Feb 2013 13:40:22 -0500 Subject: [PATCH 12/12] Added a comment about the try/except block that catches malfunctions in the _get_pixel_distance routines. Also added a warning emit, so that the try/catch at least says something if it is triggered. --- lib/matplotlib/axis.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 10719a797ad1..32b64ff40b6a 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -16,6 +16,7 @@ import matplotlib.transforms as mtransforms import matplotlib.units as munits import numpy as np +import warnings GRIDLINE_INTERPOLATION_STEPS = 180 @@ -981,13 +982,19 @@ def _update_ticks(self, renderer): interval_expanded = interval[1], interval[0] if hasattr(self, '_get_pixel_distance_along_axis'): + # normally, one does not want to catch all exceptions that could possibly happen, but it + # is not clear exactly what exceptions might arise from a user's projection (their rendition + # of the Axis object). So, we catch all, with the idea that one would rather potentially + # lose a tick from one side of the axis or another, rather than see a stack trace. try: ds1 = self._get_pixel_distance_along_axis(interval_expanded[0], -0.5) except: + warnings.warn("Unable to find pixel distance along axis for interval padding; assuming no interval padding needed.") ds1 = 0.0 try: ds2 = self._get_pixel_distance_along_axis(interval_expanded[1], +0.5) except: + warnings.warn("Unable to find pixel distance along axis for interval padding; assuming no interval padding needed.") ds2 = 0.0 interval_expanded = (interval[0] - ds1, interval[1] + ds2)