diff --git a/doc/api/next_api_changes/2019-07-09-AL.rst b/doc/api/next_api_changes/2019-07-09-AL.rst new file mode 100644 index 000000000000..b9b97a6ca9db --- /dev/null +++ b/doc/api/next_api_changes/2019-07-09-AL.rst @@ -0,0 +1,8 @@ +API changes +``````````` + +``Axes.get_data_ratio`` now takes the axes scale into account (linear, log, +logit, etc.) before computing the y-to-x ratio. This change allows fixed +aspects to be applied to any combination of x and y scales. + +``Axes.get_data_ratio_log`` is deprecated. diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 16d8d7ae9a5d..294e05548592 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -1396,20 +1396,21 @@ def set_anchor(self, anchor, share=False): def get_data_ratio(self): """ - Return the aspect ratio of the raw data. + Return the aspect ratio of the scaled data. Notes ----- This method is intended to be overridden by new projection types. """ - xmin, xmax = self.get_xbound() - ymin, ymax = self.get_ybound() - - xsize = max(abs(xmax - xmin), 1e-30) - ysize = max(abs(ymax - ymin), 1e-30) - + trf_xmin, trf_xmax = map( + self.xaxis.get_transform().transform, self.get_xbound()) + trf_ymin, trf_ymax = map( + self.yaxis.get_transform().transform, self.get_ybound()) + xsize = max(abs(trf_xmax - trf_xmin), 1e-30) + ysize = max(abs(trf_ymax - trf_ymin), 1e-30) return ysize / xsize + @cbook.deprecated("3.2") def get_data_ratio_log(self): """ Return the aspect ratio of the raw data in log scale. @@ -1455,81 +1456,53 @@ def apply_aspect(self, position=None): aspect = self.get_aspect() - if self.name != 'polar': - xscale, yscale = self.get_xscale(), self.get_yscale() - if xscale == "linear" and yscale == "linear": - aspect_scale_mode = "linear" - elif xscale == "log" and yscale == "log": - aspect_scale_mode = "log" - elif ((xscale == "linear" and yscale == "log") or - (xscale == "log" and yscale == "linear")): - if aspect != "auto": - cbook._warn_external( - 'aspect is not supported for Axes with xscale=%s, ' - 'yscale=%s' % (xscale, yscale)) - aspect = "auto" - else: # some custom projections have their own scales. - pass - else: - aspect_scale_mode = "linear" - if aspect == 'auto': self._set_position(position, which='active') return if aspect == 'equal': - A = 1 - else: - A = aspect + aspect = 1 + + fig_width, fig_height = self.get_figure().get_size_inches() + fig_aspect = fig_height / fig_width - figW, figH = self.get_figure().get_size_inches() - fig_aspect = figH / figW if self._adjustable == 'box': if self in self._twinned_axes: - raise RuntimeError("Adjustable 'box' is not allowed in a" - " twinned Axes. Use 'datalim' instead.") - if aspect_scale_mode == "log": - box_aspect = A * self.get_data_ratio_log() - else: - box_aspect = A * self.get_data_ratio() + raise RuntimeError("Adjustable 'box' is not allowed in a " + "twinned Axes; use 'datalim' instead") + box_aspect = aspect * self.get_data_ratio() pb = position.frozen() pb1 = pb.shrunk_to_aspect(box_aspect, pb, fig_aspect) self._set_position(pb1.anchored(self.get_anchor(), pb), 'active') return - # reset active to original in case it had been changed - # by prior use of 'box' - self._set_position(position, which='active') - - xmin, xmax = self.get_xbound() - ymin, ymax = self.get_ybound() + # self._adjustable == 'datalim' - if aspect_scale_mode == "log": - xmin, xmax = math.log10(xmin), math.log10(xmax) - ymin, ymax = math.log10(ymin), math.log10(ymax) + # reset active to original in case it had been changed by prior use + # of 'box' + self._set_position(position, which='active') + x_trf = self.xaxis.get_transform() + y_trf = self.yaxis.get_transform() + xmin, xmax = map(x_trf.transform, self.get_xbound()) + ymin, ymax = map(y_trf.transform, self.get_ybound()) xsize = max(abs(xmax - xmin), 1e-30) ysize = max(abs(ymax - ymin), 1e-30) l, b, w, h = position.bounds box_aspect = fig_aspect * (h / w) - data_ratio = box_aspect / A + data_ratio = box_aspect / aspect - y_expander = (data_ratio * xsize / ysize - 1.0) + y_expander = data_ratio * xsize / ysize - 1 # If y_expander > 0, the dy/dx viewLim ratio needs to increase if abs(y_expander) < 0.005: return - if aspect_scale_mode == "log": - dL = self.dataLim - dL_width = math.log10(dL.x1) - math.log10(dL.x0) - dL_height = math.log10(dL.y1) - math.log10(dL.y0) - xr = 1.05 * dL_width - yr = 1.05 * dL_height - else: - dL = self.dataLim - xr = 1.05 * dL.width - yr = 1.05 * dL.height + dL = self.dataLim + x0, x1 = map(x_trf.inverted().transform, dL.intervalx) + y0, y1 = map(y_trf.inverted().transform, dL.intervaly) + xr = 1.05 * (x1 - x0) + yr = 1.05 * (y1 - y0) xmarg = xsize - xr ymarg = ysize - yr @@ -1537,8 +1510,7 @@ def apply_aspect(self, position=None): Xsize = ysize / data_ratio Xmarg = Xsize - xr Ymarg = Ysize - yr - # Setting these targets to, e.g., 0.05*xr does not seem to - # help. + # Setting these targets to, e.g., 0.05*xr does not seem to help. xm = 0 ym = 0 @@ -1546,8 +1518,8 @@ def apply_aspect(self, position=None): shared_y = self in self._shared_y_axes # Not sure whether we need this check: if shared_x and shared_y: - raise RuntimeError("adjustable='datalim' is not allowed when both" - " axes are shared.") + raise RuntimeError("adjustable='datalim' is not allowed when both " + "axes are shared") # If y is shared, then we are only allowed to change x, etc. if shared_y: @@ -1564,18 +1536,12 @@ def apply_aspect(self, position=None): yc = 0.5 * (ymin + ymax) y0 = yc - Ysize / 2.0 y1 = yc + Ysize / 2.0 - if aspect_scale_mode == "log": - self.set_ybound((10. ** y0, 10. ** y1)) - else: - self.set_ybound((y0, y1)) + self.set_ybound(*map(y_trf.inverted().transform, (y0, y1))) else: xc = 0.5 * (xmin + xmax) x0 = xc - Xsize / 2.0 x1 = xc + Xsize / 2.0 - if aspect_scale_mode == "log": - self.set_xbound((10. ** x0, 10. ** x1)) - else: - self.set_xbound((x0, x1)) + self.set_xbound(*map(x_trf.inverted().transform, (x0, x1))) def axis(self, *args, emit=True, **kwargs): """ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index f1adb4e4d692..4fd7a4d12a08 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -6495,3 +6495,29 @@ def test_set_ticks_inverted(): ax.invert_xaxis() ax.set_xticks([.3, .7]) assert ax.get_xlim() == (1, 0) + + +def test_aspect_nonlinear_adjustable_box(): + fig = plt.figure(figsize=(10, 10)) # Square. + + ax = fig.add_subplot() + ax.plot([.4, .6], [.4, .6]) # Set minpos to keep logit happy. + ax.set(xscale="log", xlim=(1, 10), + yscale="logit", ylim=(1/11, 1/1001), + aspect=1, adjustable="box") + ax.margins(0) + pos = fig.transFigure.transform_bbox(ax.get_position()) + assert pos.height / pos.width == pytest.approx(2) + + +def test_aspect_nonlinear_adjustable_datalim(): + fig = plt.figure(figsize=(10, 10)) # Square. + + ax = fig.add_axes([.1, .1, .8, .8]) # Square. + ax.plot([.4, .6], [.4, .6]) # Set minpos to keep logit happy. + ax.set(xscale="log", xlim=(1, 10), + yscale="logit", ylim=(1/11, 1/1001), + aspect=1, adjustable="datalim") + ax.margins(0) + ax.apply_aspect() + assert ax.get_xlim() == pytest.approx(np.array([1/10, 10]) * np.sqrt(10)) diff --git a/lib/matplotlib/tests/test_image.py b/lib/matplotlib/tests/test_image.py index f001b0d2db3f..18d513b2c962 100644 --- a/lib/matplotlib/tests/test_image.py +++ b/lib/matplotlib/tests/test_image.py @@ -686,15 +686,14 @@ def test_load_from_url(): @image_comparison(['log_scale_image'], remove_text=True) -# The recwarn fixture captures a warning in image_comparison. -def test_log_scale_image(recwarn): +def test_log_scale_image(): Z = np.zeros((10, 10)) Z[::2] = 1 fig, ax = plt.subplots() - ax.imshow(Z, extent=[1, 100, 1, 100], cmap='viridis', - vmax=1, vmin=-1) - ax.set_yscale('log') + ax.imshow(Z, extent=[1, 100, 1, 100], cmap='viridis', vmax=1, vmin=-1, + aspect='auto') + ax.set(yscale='log') @image_comparison(['rotate_image'], remove_text=True) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy