From b0e68b2ddf93bfe58caa3a7a0fdc2922aa10ddaa Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sun, 17 Oct 2021 12:06:01 +0100 Subject: [PATCH] Add ability to scale BBox with just x or y values --- lib/matplotlib/transforms.py | 46 ++++++++++++++++++++++++-- lib/mpl_toolkits/mplot3d/axes3d.py | 15 +++++---- lib/mpl_toolkits/tests/test_mplot3d.py | 11 ++++++ 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 5eb3f380b5f9..d7ac521242a5 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -902,11 +902,51 @@ def update_from_path(self, path, ignore=None, updatex=True, updatey=True): self._points[:, 1] = points[:, 1] self._minpos[1] = minpos[1] + def update_from_data_x(self, x, ignore=None): + """ + Update the x-bounds of the `Bbox` based on the passed in data. After + updating, the bounds will have positive *width*, and *x0* will be the + minimal value. + + Parameters + ---------- + x : ndarray + Array of x-values. + + ignore : bool, optional + - When ``True``, ignore the existing bounds of the `Bbox`. + - When ``False``, include the existing bounds of the `Bbox`. + - When ``None``, use the last value passed to :meth:`ignore`. + """ + x = np.ravel(x) + self.update_from_data_xy(np.column_stack([x, np.ones(x.size)]), + ignore=ignore, updatey=False) + + def update_from_data_y(self, y, ignore=None): + """ + Update the y-bounds of the `Bbox` based on the passed in data. After + updating, the bounds will have positive *height*, and *y0* will be the + minimal value. + + Parameters + ---------- + y : ndarray + Array of y-values. + + ignore : bool, optional + - When ``True``, ignore the existing bounds of the `Bbox`. + - When ``False``, include the existing bounds of the `Bbox`. + - When ``None``, use the last value passed to :meth:`ignore`. + """ + y = np.array(y).ravel() + self.update_from_data_xy(np.column_stack([np.ones(y.size), y]), + ignore=ignore, updatex=False) + def update_from_data_xy(self, xy, ignore=None, updatex=True, updatey=True): """ - Update the bounds of the `Bbox` based on the passed in - data. After updating, the bounds will have positive *width* - and *height*; *x0* and *y0* will be the minimal values. + Update the bounds of the `Bbox` based on the passed in data. After + updating, the bounds will have positive *width* and *height*; + *x0* and *y0* will be the minimal values. Parameters ---------- diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 25d9e9fde457..3e93c85fa888 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -107,6 +107,7 @@ def __init__( self.xy_viewLim = Bbox.unit() self.zz_viewLim = Bbox.unit() self.xy_dataLim = Bbox.unit() + # z-limits are encoded in the x-component of the Bbox, y is un-used self.zz_dataLim = Bbox.unit() # inhibit autoscale_view until the axes are defined @@ -643,14 +644,14 @@ def autoscale(self, enable=True, axis='both', tight=None): def auto_scale_xyz(self, X, Y, Z=None, had_data=None): # This updates the bounding boxes as to keep a record as to what the # minimum sized rectangular volume holds the data. - X = np.reshape(X, -1) - Y = np.reshape(Y, -1) - self.xy_dataLim.update_from_data_xy( - np.column_stack([X, Y]), not had_data) + if np.shape(X) == np.shape(Y): + self.xy_dataLim.update_from_data_xy( + np.column_stack([np.ravel(X), np.ravel(Y)]), not had_data) + else: + self.xy_dataLim.update_from_data_x(X, not had_data) + self.xy_dataLim.update_from_data_y(Y, not had_data) if Z is not None: - Z = np.reshape(Z, -1) - self.zz_dataLim.update_from_data_xy( - np.column_stack([Z, Z]), not had_data) + self.zz_dataLim.update_from_data_x(Z, not had_data) # Let autoscale_view figure out how to use this data. self.autoscale_view() diff --git a/lib/mpl_toolkits/tests/test_mplot3d.py b/lib/mpl_toolkits/tests/test_mplot3d.py index eb662f04c9d7..5fee82f51920 100644 --- a/lib/mpl_toolkits/tests/test_mplot3d.py +++ b/lib/mpl_toolkits/tests/test_mplot3d.py @@ -198,6 +198,17 @@ def test_tricontour(): ax.tricontourf(x, y, z) +def test_contour3d_1d_input(): + # Check that 1D sequences of different length for {x, y} doesn't error + fig = plt.figure() + ax = fig.add_subplot(projection='3d') + nx, ny = 30, 20 + x = np.linspace(-10, 10, nx) + y = np.linspace(-10, 10, ny) + z = np.random.randint(0, 2, [ny, nx]) + ax.contour(x, y, z, [0.5]) + + @mpl3d_image_comparison(['lines3d.png']) def test_lines3d(): fig = plt.figure()