diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index c56e4c6b7039..75e8b796e539 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -2198,9 +2198,12 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None, vmin, vmax : float, optional Bounds for the normalization. - shade : bool, default: True - Whether to shade the facecolors. Shading is always disabled when - *cmap* is specified. + shade : bool or "auto", default: "auto" + Whether to shade the facecolors. "auto" will shade only if the facecolor is uniform, + i.e. neither *cmap* nor *facecolors* is given. + + Furthermore, shading is generally not compatible with colormapping and + ``shade=True, cmap=...`` will raise an error. lightsource : `~matplotlib.colors.LightSource`, optional The lightsource to use when *shade* is True. @@ -2251,8 +2254,10 @@ def plot_surface(self, X, Y, Z, *, norm=None, vmin=None, fcolors = kwargs.pop('facecolors', None) cmap = kwargs.get('cmap', None) - shade = kwargs.pop('shade', cmap is None) - if shade is None: + shade = kwargs.pop('shade', "auto") + if shade == "auto": + shade = cmap is None and fcolors is None + elif shade is None: raise ValueError("shade cannot be None.") colset = [] # the sampled facecolor diff --git a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py index e6d11f793b46..fff419b62610 100644 --- a/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py +++ b/lib/mpl_toolkits/mplot3d/tests/test_axes3d.py @@ -703,7 +703,7 @@ def test_surface3d_masked(): z = np.ma.masked_less(matrix, 0) norm = mcolors.Normalize(vmax=z.max(), vmin=z.min()) colors = mpl.colormaps["plasma"](norm(z)) - ax.plot_surface(x, y, z, facecolors=colors) + ax.plot_surface(x, y, z, facecolors=colors, shade=True) ax.view_init(30, -80, 0) diff --git a/lib/mpl_toolkits/mplot3d/tests/test_plot_surface_shade.py b/lib/mpl_toolkits/mplot3d/tests/test_plot_surface_shade.py new file mode 100644 index 000000000000..5da9dee36998 --- /dev/null +++ b/lib/mpl_toolkits/mplot3d/tests/test_plot_surface_shade.py @@ -0,0 +1,132 @@ +""" +Tests for plot_surface shade parameter behavior. +""" +import numpy as np +import matplotlib.pyplot as plt +from matplotlib import cm +from matplotlib.colors import Normalize + + +def test_plot_surface_auto_shade_with_facecolors(): + """Test that plot_surface with facecolors uses shade=False by default.""" + X = np.linspace(0, 1, 10) + Y = np.linspace(0, 1, 10) + X_mesh, Y_mesh = np.meshgrid(X, Y) + Z = np.cos((1-X_mesh) * np.pi) * np.cos((1-Y_mesh) * np.pi) * 1e+14 + 1.4e+15 + Z_colors = np.cos(X_mesh * np.pi) + + norm = Normalize(vmin=np.min(Z_colors), vmax=np.max(Z_colors)) + colors = cm.viridis(norm(Z_colors))[:-1, :-1] + + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # Test that when facecolors is provided, shade defaults to False + surf = ax.plot_surface(X_mesh, Y_mesh, Z, facecolors=colors, edgecolor='none') + + # We can't directly check shade attribute, but we can verify the plot works + # and doesn't crash, which indicates our logic is working + assert surf is not None + plt.close(fig) + + +def test_plot_surface_auto_shade_without_facecolors(): + """Test that plot_surface without facecolors uses shade=True by default.""" + X = np.linspace(0, 1, 10) + Y = np.linspace(0, 1, 10) + X_mesh, Y_mesh = np.meshgrid(X, Y) + Z = np.cos((1-X_mesh) * np.pi) * np.cos((1-Y_mesh) * np.pi) * 1e+14 + 1.4e+15 + + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # Test that when no facecolors or cmap is provided, shade defaults to True + surf = ax.plot_surface(X_mesh, Y_mesh, Z) + + assert surf is not None + plt.close(fig) + + +def test_plot_surface_auto_shade_with_cmap(): + """Test that plot_surface with cmap uses shade=False by default.""" + X = np.linspace(0, 1, 10) + Y = np.linspace(0, 1, 10) + X_mesh, Y_mesh = np.meshgrid(X, Y) + Z = np.cos((1-X_mesh) * np.pi) * np.cos((1-Y_mesh) * np.pi) * 1e+14 + 1.4e+15 + + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # Test that when cmap is provided, shade defaults to False + surf = ax.plot_surface(X_mesh, Y_mesh, Z, cmap=cm.viridis) + + assert surf is not None + plt.close(fig) + + +def test_plot_surface_explicit_shade_with_facecolors(): + """Test that explicit shade parameter overrides auto behavior with facecolors.""" + X = np.linspace(0, 1, 10) + Y = np.linspace(0, 1, 10) + X_mesh, Y_mesh = np.meshgrid(X, Y) + Z = np.cos((1-X_mesh) * np.pi) * np.cos((1-Y_mesh) * np.pi) * 1e+14 + 1.4e+15 + Z_colors = np.cos(X_mesh * np.pi) + + norm = Normalize(vmin=np.min(Z_colors), vmax=np.max(Z_colors)) + colors = cm.viridis(norm(Z_colors))[:-1, :-1] + + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # Test that explicit shade=True works with facecolors + surf = ax.plot_surface(X_mesh, Y_mesh, Z, facecolors=colors, shade=True) + + assert surf is not None + plt.close(fig) + + +def test_plot_surface_explicit_shade_false_without_facecolors(): + """Test that explicit shade=False overrides auto behavior without facecolors.""" + X = np.linspace(0, 1, 10) + Y = np.linspace(0, 1, 10) + X_mesh, Y_mesh = np.meshgrid(X, Y) + Z = np.cos((1-X_mesh) * np.pi) * np.cos((1-Y_mesh) * np.pi) * 1e+14 + 1.4e+15 + + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # Test that explicit shade=False works without facecolors + surf = ax.plot_surface(X_mesh, Y_mesh, Z, shade=False) + + assert surf is not None + plt.close(fig) + + +def test_plot_surface_shade_auto_behavior_comprehensive(): + """Test the auto behavior logic comprehensively.""" + X = np.linspace(0, 1, 5) + Y = np.linspace(0, 1, 5) + X_mesh, Y_mesh = np.meshgrid(X, Y) + Z = np.ones_like(X_mesh) + Z_colors = np.ones_like(X_mesh) + colors = cm.viridis(Z_colors)[:-1, :-1] + + test_cases = [ + # (kwargs, description) + ({}, "no facecolors, no cmap -> shade=True"), + ({'facecolors': colors}, "facecolors provided -> shade=False"), + ({'cmap': cm.viridis}, "cmap provided -> shade=False"), + ({'facecolors': colors, 'cmap': cm.viridis}, "both facecolors and cmap -> shade=False"), + ({'facecolors': colors, 'shade': True}, "explicit shade=True overrides auto"), + ({'facecolors': colors, 'shade': False}, "explicit shade=False overrides auto"), + ({}, "no parameters -> shade=True"), + ] + + for kwargs, description in test_cases: + fig = plt.figure() + ax = fig.add_subplot(111, projection='3d') + + # All these should work without crashing + surf = ax.plot_surface(X_mesh, Y_mesh, Z, **kwargs) + assert surf is not None, f"Failed: {description}" + plt.close(fig)
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: