This was fun: I had chatgpt codepilot write the code for me!
All I had to do was tell it what I wanted and iterate…
from manim import *
from manim import config
config.pixel_width = 640
config.pixel_height = 360
config.frame_rate = 30
class RotatingAxesVisualization(ThreeDScene):
def construct(self):
# Set up the fixed red axes
fixed_axes = ThreeDAxes(
x_range=(-2.5, 2.5, 1), y_range=(-2.5, 2.5, 1), z_range=(-1, 1, 0.5)
).set_color(RED).set_opacity(0.5)
# Set up the rotating blue axes with specified ranges
rotating_axes = ThreeDAxes(
x_range=(-2.5, 2.5, 1), y_range=(-2.5, 2.5, 1), z_range=(-1, 1, 0.5)
).set_color(BLUE)
# Camera configuration
self.set_camera_orientation(phi=PI / 4, theta=PI / 6, zoom=1)
# Add labels for the red axes (e_1, e_2, e_3)
#e1_label = MathTex("e_1").next_to(fixed_axes.x_axis.get_end(), RIGHT)
#e2_label = MathTex("e_2").next_to(fixed_axes.y_axis.get_end(), RIGHT)
#e3_label = MathTex("e_3").next_to(fixed_axes.z_axis.get_end(), OUT)
# Add labels for the blue axes (s_1, s_2, s_3)
#s1_label = MathTex("s_1").next_to(rotating_axes.x_axis.get_end(), RIGHT)
#s2_label = MathTex("s_2").next_to(rotating_axes.y_axis.get_end(), RIGHT)
#s3_label = MathTex("s_3").next_to(rotating_axes.z_axis.get_end(), OUT)
# Shade the blue plane (initial xy-plane of the blue axes)
blue_plane = Polygon(
[3.5, 3.5, 0], [-3.5, 3.5, 0], [-3.5, -3.5, 0], [3.5, -3.5, 0],
fill_opacity=0.2, fill_color=BLUE, color=BLUE
)
# Add fixed axes, rotating axes, and blue plane to the scene
self.add(fixed_axes) #, e1_label, e2_label, e3_label)
self.add(rotating_axes, blue_plane )#, s1_label, s2_label, s3_label)
# Define the target direction for the original z-axis
target_direction = np.array([-3, -10, 20])
target_direction = target_direction.astype(float) # Ensure float dtype
target_direction /= np.linalg.norm(target_direction) # Normalize the direction
# Rotation matrix to align the z-axis with the target direction
rotation_axis = np.cross([0, 0, 1], target_direction) # Axis of rotation
rotation_angle = np.arccos(np.dot([0, 0, 1], target_direction)) # Angle of rotation
# Animate the rotation of the blue axes and plane
self.play(
Rotate(rotating_axes, angle=rotation_angle, axis=rotation_axis, about_point=ORIGIN),
Rotate(blue_plane, angle=rotation_angle, axis=rotation_axis, about_point=ORIGIN),
run_time=3
)
# Add the blue vector along the blue x-axis after the rotation
blue_vector = Arrow(start=ORIGIN, end=[2.1, 0, 0], buff=0, color=BLUE)
blue_vector_label = MathTex(r"\mathbf{v}").next_to(blue_vector.get_end(), RIGHT)
# Add blue vector independently to avoid unintended transformations
self.add(blue_vector, blue_vector_label)
# Rotate the blue vector within the blue plane by 60 degrees
self.play(
Rotate(blue_vector, angle=PI / 3, axis=[0, 0, 1], about_point=ORIGIN),
Rotate(blue_vector_label, angle=PI / 3, axis=[0, 0, 1], about_point=ORIGIN),
run_time=3
)
# Pause to display the final scene
self.wait(2)
# Function to render the Manim scene into memory
def render_manim_scene(scene_class):
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
output_path = temp_file.name
config.media_dir = "." # Keep media directory local
config.output_file = output_path # Output directly to the temp file
scene_class().render()
return output_path
# Render the scene
video_path = render_manim_scene(RotatingAxesVisualization)
# Create a Panel app to display the video
video_pane = pn.pane.Video(video_path, loop=False, autoplay=True, width=640, height=360)
# Build the Panel layout
layout = pn.Column(
"# Visualization of a Rotation and Two Sets of Axes",
"## This animation demonstrates a 3D rotation.",
video_pane,
)
# Serve the Panel app
layout.servable()