I am using plot_surface with a 2D colormap array to the Poly3DCollection.set(color=...)
method. I made an interactive plot with the slider but since I’m getting mixed results with ugly commands, I’d like to ask if there is a clearer way of doing this.
Here’s my attempt at drawing an interactive 3D disco ball:
"""
===============================
3D surface coloring with slider
===============================
This example offers a customizable disco ball with a simple interactive coloring.
The plotted color function is:
.. math:: D(x, y, z) = sin(betacdot f) cdot cos((alpha-varphi)cdot f)
Two sliders are included to play around with the offset and the frequency.
"""
from matplotlib import pyplot as plt
from matplotlib.colors import Normalize
from matplotlib.widgets import Slider
import numpy as np
cmap = plt.cm.seismic
# Initiate the plot values
alpha, beta = np.meshgrid(np.linspace(0, 2*np.pi, num=50, endpoint=True),
np.linspace(0, np.pi, num=50, endpoint=True))
X = np.sin(beta)*np.cos(alpha)
Y = np.sin(beta)*np.sin(alpha)
Z = np.cos(beta)
# The parametrized color to be plotted
def colorfunc(freq=5, offset=0, alpha=alpha, beta=beta):
return np.sin(beta*freq)*np.cos((alpha-offset)*freq)
# Define normalization for colors
norm = Normalize(-1, 1)
# Initiate the plot
fig = plt.figure()
ax = fig.add_subplot(projection='3d', box_aspect=(1, 1, 1))
surf = ax.plot_surface(X, Y, Z,
rstride=1,
cstride=1,
facecolors=cmap(norm(colorfunc())),
cmap=cmap)
# Adjust the main plot to make room for the sliders
fig.subplots_adjust(left=0.25, bottom=0.25)
# Make a horizontal slider to control the offset
axs1 = fig.add_axes([0.25, 0.1, 0.65, 0.03])
offslider = Slider(
ax=axs1,
label='X offset',
valmin=-X.max(),
valmax=X.max(),
valinit=X[0, 0],
)
# Make a vertically oriented slider to control the frequency
axs2 = fig.add_axes([0.1, 0.25, 0.0225, 0.63])
freqslider = Slider(
ax=axs2,
label="Frequency",
valmin=2,
valmax=10,
valinit=5,
orientation="vertical"
)
# The function to be called anytime a slider's value changes
def update(val):
C = colorfunc(freqslider.val, offslider.val)
# Use the `set` method to update the surface properties
# https://matplotlib.org/devdocs/api/_as_gen/mpl_toolkits.mplot3d.art3d.Path3DCollection.html#mpl_toolkits.mplot3d.art3d.Path3DCollection
surf.set_color(cmap(norm(C[:-1, :-1]).flatten())) # THIS IS THE UGLY COMMAND
fig.canvas.draw_idle()
# Connect each slider to the `update`function
offslider.on_changed(update)
freqslider.on_changed(update)
plt.show()
Why is it that the argument passed to plot_surface(color=...)
has to be different from the argument to surf.set_color(...)
?
(I’m using matplotlib==3.9.0
)