Surface animation

At last, matplotlib [ 3.6.1 ] has proper perspective available.
I’m using Ubuntu 22.04 , with Python 3.10.12, numpy 1.23.4

Here’s an example that also includes animation .


#  projections_4a.py

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation


plt.style.use('dark_background')

# Define a custom function to generate test data for the surface plot
def generate_test_data(t):
    x = np.linspace(-5, 5, 100)
    y = np.linspace(-5, 5, 100)
    X, Y = np.meshgrid(x, y)
    s = np.mod(t,2)
    Z = np.sin(np.sqrt(X**2 + Y**2 )*np.pi*s+0.00001)/(np.sqrt(X**2 + Y**2 )*np.pi*s+0.00001)
    return X, Y, Z

# Create a 3D figure
fig = plt.figure()
axs = fig.add_subplot(111, projection='3d')

# Initialize an empty surface plot
# ae=np.empty([2,2])
X, Y, Z = generate_test_data(0)
surf = axs.plot_surface(X, Y, Z, cmap='viridis')

#quit()


# Set the perspective projection
axs.set_proj_type('persp', focal_length=0.2)  # FOV = 157.4 deg

# Set the background color to black
axs.set_facecolor('black')

# Add red grid lines with spacing
axs.grid(True, color='red', linestyle='solid', linewidth=0.5)

# Set the title
axs.set_title("'persp'\nfocal_length = 0.2", fontsize=10)

# Function to update the plot for each frame
def update_plot(frame):
	global surf
	t = frame * 0.1  # Adjust the time parameter for animation
	X, Y, Z = generate_test_data(t)
	surf.remove()
	surf = axs.plot_surface(X, Y, Z, cmap='viridis')
	return surf,

# Create an animation
ani = animation.FuncAnimation(fig, update_plot, frames=100, interval=100)

plt.show()

An update upon the previous.
In this instance I’ve defined the function at one location within the
code; this is useful when you want to plot an arbitrary function.


#  projections_4b.py

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation


plt.style.use('dark_background')

# Define a custom function to generate test data for the surface plot
def generate_test_data(t):
   x = np.linspace(-5, 5, 100)
   y = np.linspace(-5, 5, 100)
   X, Y = np.meshgrid(x, y)
   s = np.mod(t,2)
   Z = np.sin(np.sqrt(X**2 + Y**2 )*np.pi*s+0.00001)/(np.sqrt(X**2 + Y**2 )*np.pi*s+0.00001)
   return X, Y, Z

# Create a 3D figure
fig = plt.figure()
axs = fig.add_subplot(111, projection='3d')

# Initialize an empty surface plot
ae=np.empty([2,2])
surf = axs.plot_surface(ae, ae, ae, cmap='viridis')

# Set the perspective projection
axs.set_proj_type('persp', focal_length=0.2)  # FOV = 157.4 deg

# Set the background color to black
axs.set_facecolor('black')

# Set the title
axs.set_title("'persp'\nfocal_length = 0.2", fontsize=10)

# Function to update the plot for each frame
def update_plot(frame):
   global surf
   t = frame * 0.1  # Adjust the time parameter for animation
   X, Y, Z = generate_test_data(t)
   surf.remove()
   surf = axs.plot_surface(X, Y, Z, cmap='viridis')
   
   return surf,

# Create an animation
ani = animation.FuncAnimation(fig, update_plot, frames=100, interval=100)

plt.show()

So, this is much the same, see what you think.
A few speed improvements and basic shading, inspired by an example at the matplotlib
website .



#  proj_numb_shade3.py


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
import matplotlib.animation as animation
from matplotlib.colors import LightSource
from matplotlib import cm
from numba import jit

plt.style.use('dark_background')

global x, y, X, Y
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y) # valid outside of jit decoration .

@jit(nopython=True)
def generate_test_data(t):
    s = np.mod(t, 2)
    Z = np.sin(np.sqrt(X**2 + Y**2) * np.pi * s + 0.00001) / (np.sqrt(X**2 + Y**2) * np.pi * s + 0.00001)
    return Z

# Function to apply LightSource shading
def shade_surface(Z):
    ls = LightSource(azdeg=315, altdeg=45)
    rgb = ls.shade(Z, cmap=cm.viridis, blend_mode='hsv')  # Passing colormap object instead of string
    return rgb

# Create a 3D figure
fig = plt.figure()
axs = fig.add_subplot(111, projection='3d')

# Initialize an empty surface plot
ae = np.empty([2, 2])
surf = axs.plot_surface(ae, ae, ae, cmap='viridis')

# Set the perspective projection
axs.set_proj_type('persp', focal_length=0.2)  # FOV = 157.4 deg

# Set the background color to black
axs.set_facecolor('black')

# Set the title
axs.set_title("'persp'\nfocal_length = 0.2", fontsize=10)

# Function to update the plot for each frame
def update_plot(frame):
    global surf
    t = frame * 0.1  # Adjust the time parameter for animation
    Z = generate_test_data(t)
    rgb = shade_surface(Z)
    surf.remove()
    surf = axs.plot_surface(X, Y, Z, facecolors=rgb, shade=False)
    
    return surf,

# Create an animation
ani = animation.FuncAnimation(fig, update_plot, frames=100, interval=100)

plt.show()

quit()