Customize 3D Plot Camera Angles in Python Matplotlib
In this tutorial, you’ll learn how to customize 3D plot camera angles using the Python Matplotlib library.
You’ll learn how to control the viewer’s perspective by adjusting azimuth and elevation angles and implementing interactive features that allow users to explore 3D plots in real time.
We’ll cover everything from basic angle adjustments to advanced methods like custom camera paths and smooth transitions.
Use view_init to set azimuth and elevation
In 3D plotting, the azimuth angle determines the horizontal rotation, while the elevation angle sets the vertical tilt.
You can use the view_init()
method to set specific azimuth and elevation angles:
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) fig = plt.figure(figsize=(15, 5)) # Default view ax1 = fig.add_subplot(131, projection='3d') ax1.plot_surface(X, Y, Z, cmap='viridis') ax1.set_title('Default View') # Change azimuth ax2 = fig.add_subplot(132, projection='3d') ax2.plot_surface(X, Y, Z, cmap='viridis') ax2.view_init(elev=20, azim=45) ax2.set_title('Azimuth: 45°') # Change elevation ax3 = fig.add_subplot(133, projection='3d') ax3.plot_surface(X, Y, Z, cmap='viridis') ax3.view_init(elev=60, azim=0) ax3.set_title('Elevation: 60°') plt.tight_layout() plt.show()
Output:
The three subplots show how changing azimuth and elevation affects the view.
The middle plot rotates the view horizontally, while the right plot tilts it vertically.
Create animations with changing camera angles
To create an animation with changing camera angles, you can use a loop to update the view:
import matplotlib.animation as animation def rotate(angle): ax.view_init(elev=10., azim=angle) return ax fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') ax.plot_surface(X, Y, Z, cmap='viridis') anim = animation.FuncAnimation(fig, rotate, frames=np.arange(0, 362, 2), interval=100) plt.close() # Prevents displaying the static plot anim.save('rotation_animation.gif', writer='pillow', fps=30)
Output:
Using FuncAnimation for smooth transitions
For smoother transitions between camera angles, you can use FuncAnimation
with interpolation:
from scipy.interpolate import interp1d def smooth_transition(frame): elev = elev_interp(frame) azim = azim_interp(frame) ax.view_init(elev=elev, azim=azim) return ax fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') ax.plot_surface(X, Y, Z, cmap='viridis') frames = 100 elev_start, elev_end = 0, 90 azim_start, azim_end = 0, 180 elev_interp = interp1d([0, frames], [elev_start, elev_end]) azim_interp = interp1d([0, frames], [azim_start, azim_end]) anim = animation.FuncAnimation(fig, smooth_transition, frames=frames, interval=50) plt.close() anim.save('smooth_transition.gif', writer='pillow', fps=30)
Output:
This animation smoothly transitions from a front view to a top-down view while rotating.
Save Animated plots as GIF or MP4
You can use various writers to save your animated plots in different formats.
Here’s how to save as both GIF and MP4:
import matplotlib.animation as animation def rotate(angle): ax.view_init(elev=10., azim=angle) return ax fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') ax.plot_surface(X, Y, Z, cmap='viridis') anim = animation.FuncAnimation(fig, rotate, frames=np.arange(0, 362, 2), interval=50) # Save as GIF anim.save('rotation_animation.gif', writer='pillow', fps=30) # Save as MP4 Writer = animation.writers['ffmpeg'] writer = Writer(fps=30, metadata=dict(artist='Me'), bitrate=1800) anim.save('rotation_animation.mp4', writer=writer)
Output:
This code creates two files: a GIF and an MP4 of the rotating 3D plot.
The MP4 version requires FFmpeg to be installed on your system.
Custom Camera Path (Using interpolation)
Let’s create a more complex camera path that combines changes in elevation, azimuth, and zoom:
from scipy.interpolate import interp1d def custom_camera_path(frame): elev = elev_interp(frame) azim = azim_interp(frame) dist = dist_interp(frame) ax.view_init(elev=elev, azim=azim) ax.dist = dist return ax fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') surf = ax.plot_surface(X, Y, Z, cmap='viridis') frames = 300 keyframes = [0, 75, 150, 225, 299] elevations = [0, 45, 90, 45, 0] azimuths = [0, 90, 180, 270, 359] distances = [10, 8, 6, 8, 10] # Controls zoom level elev_interp = interp1d(keyframes, elevations, kind='cubic') azim_interp = interp1d(keyframes, azimuths, kind='cubic') dist_interp = interp1d(keyframes, distances, kind='cubic') anim = animation.FuncAnimation(fig, custom_camera_path, frames=frames, interval=50) plt.close() anim.save('complex_camera_path.gif', writer='pillow', fps=30)
Output:
This animation creates a smooth camera movement that not only changes the elevation and azimuth but also zooms in and out.
Save and restore camera settings
To save and restore camera settings across different plotting sessions, you can use Python pickle
module:
import matplotlib.pyplot as plt import pickle import os import numpy as np from mpl_toolkits.mplot3d import Axes3D def save_view(ax, filename='view.pkl'): """Save the current view (camera position and zoom level)""" view = { 'elev': ax.elev, 'azim': ax.azim, 'xlim': ax.get_xlim(), 'ylim': ax.get_ylim(), 'zlim': ax.get_zlim() } with open(filename, 'wb') as f: pickle.dump(view, f) def load_view(ax, filename='view.pkl'): """Load the view (camera position and zoom level)""" if os.path.exists(filename): with open(filename, 'rb') as f: view = pickle.load(f) ax.view_init(elev=view['elev'], azim=view['azim']) ax.set_xlim(view['xlim']) ax.set_ylim(view['ylim']) ax.set_zlim(view['zlim']) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # Load the previous view if it exists load_view(ax) x = np.linspace(-5, 5, 100) y = np.linspace(-5, 5, 100) X, Y = np.meshgrid(x, y) Z = np.sin(np.sqrt(X**2 + Y**2)) ax.plot_surface(X, Y, Z, cmap='viridis') # Connect the save function to the figure's close event fig.canvas.mpl_connect('close_event', lambda event: save_view(ax)) plt.show()
This code shows how to save camera settings to a view.pkl
file and load them later.
Mokhtar is the founder of LikeGeeks.com. He is a seasoned technologist and accomplished author, with expertise in Linux system administration and Python development. Since 2010, Mokhtar has built an impressive career, transitioning from system administration to Python development in 2015. His work spans large corporations to freelance clients around the globe. Alongside his technical work, Mokhtar has authored some insightful books in his field. Known for his innovative solutions, meticulous attention to detail, and high-quality work, Mokhtar continually seeks new challenges within the dynamic field of technology.