Short introduction to Manim: Difference between revisions
(→Camera) |
|||
| Line 153: | Line 153: | ||
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
from manim import * | |||
from manim import smooth | |||
import numpy as np | |||
class PlaneWave3D(ThreeDScene): | |||
def construct(self): | |||
axes = ThreeDAxes( x_range=[-5, 5, 1], y_range=[-5, 5, 1], z_range=[-2, 2, 1],) | |||
self.set_camera_orientation( | |||
phi=65 * DEGREES, | |||
theta=-45 * DEGREES, | |||
zoom=2, | |||
) | |||
phase = ValueTracker(0) | |||
x_pos = ValueTracker(20) # start at x=10 (right) | |||
surface = always_redraw( | |||
lambda: Surface( | |||
lambda u, v: np.array([ u, v, 1*np.sin(u - phase.get_value()) ]), | |||
u_range=[-50, 50], | |||
v_range=[-5, 15], | |||
resolution=(50, 50), | |||
fill_opacity=1.0, | |||
#checkerboard_colors=[ BLUE_D, BLUE_E, ], | |||
).set_fill_by_value( | |||
axes=axes, | |||
colors=[ (BLUE_E, -1), (BLUE, 0), (YELLOW, 1), ] | |||
) | |||
) | |||
gap = 0.02 | |||
antenna = always_redraw( | |||
lambda: VGroup( | |||
Line( | |||
start=[x_pos.get_value(), 0, -1], # bottom of screen | |||
end=[x_pos.get_value(), 0, -gap], # top of screen | |||
color=YELLOW, | |||
stroke_width=8 | |||
), | |||
Line( | |||
start=[x_pos.get_value(), 0, gap], # bottom of screen | |||
end=[x_pos.get_value(), 0, 1], # top of screen | |||
color=YELLOW, | |||
stroke_width=8 | |||
), | |||
)) | |||
amplitude = 0.5 | |||
antenna_sine = always_redraw( | |||
lambda: VGroup( | |||
VMobject().set_points_smoothly([ | |||
np.array([ | |||
x_pos.get_value() + amplitude*np.sin( np.pi* | |||
(z-gap)/(2-gap)*np.cos(phase.get_value())), | |||
0, # fixed y | |||
z | |||
]) | |||
for z in np.linspace(gap, 1, 50) | |||
]), | |||
VMobject().set_points_smoothly([ | |||
np.array([ | |||
x_pos.get_value() - amplitude*np.sin( np.pi + np.pi* | |||
(z-gap)/(2-gap)*np.cos(phase.get_value())), | |||
0, # fixed y | |||
z | |||
]) | |||
for z in np.linspace(-gap, -1, 50) | |||
]), | |||
)) | |||
#self.play(Create(axes)) | |||
self.play(Create(surface)) | |||
#self.begin_ambient_camera_rotation( rate=0.15, about='theta') | |||
#self.begin_ambient_camera_rotation( rate=0.15, about='phi') | |||
#self.begin_ambient_camera_rotation( rate=0.15, about='gamma') | |||
#self.play( | |||
# phase.animate.set_value(8 * np.pi), run_time=10, rate_func=linear, | |||
#) | |||
#self.stop_ambient_camera_rotation( about='theta') | |||
#self.stop_ambient_camera_rotation( about='pi') | |||
#self.stop_ambient_camera_rotation( about='gamma') | |||
#self.wait() | |||
phi, theta, focal_distance, gamma, zoom = self.camera.get_value_trackers() | |||
#2. Slow down | |||
current = phase.get_value() | |||
self.play( | |||
zoom.animate.set_value(0.5), | |||
theta.animate.set_value(-10*DEGREES), | |||
phase.animate.set_value(current + 20), | |||
run_time=4, | |||
#rate_func=lambda t: 1 - (1-t)**2, | |||
rate_func = linear | |||
) | |||
#2. Ramp the | |||
cruise_speed = 2.0 # phase units per second | |||
phase.add_updater( | |||
lambda m, dt: m.increment_value(cruise_speed * dt) | |||
) | |||
self.wait(10) | |||
phase.clear_updaters() | |||
#3 Add the antenna | |||
#Add the sinewave | |||
self.add(antenna) | |||
current = phase.get_value() | |||
self.play( | |||
phase.animate.set_value(current+20), | |||
theta.animate.set_value(-90*DEGREES), | |||
phi.animate.set_value(90*DEGREES), | |||
x_pos.animate.set_value(0), # move to x=-10 (left) | |||
run_time=10, | |||
rate_func=linear, | |||
) | |||
self.add(antenna_sine) | |||
self.play( | |||
zoom.animate.set_value(3), | |||
phase.animate.set_value(current+40), | |||
run_time=10, | |||
rate_func=linear, | |||
) | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Revision as of 20:09, 9 June 2026
Introduction
Manim or manimgl.
Virtual engine
python -m venv .
source bin/activate
For faster rendering use smaller resolution resolution=(15,15) and smaller fps rate
manim -pql --fps 10 ma_animatedplanewave.py PlaneWave3D
Some short manim scrips to generate easy visualizations.
manim -pql bez2.py manim -pqh bez2.py BezierSplineExample --format=png f: open the directory/ folder i: generate gif file p: play qm, qh, qk: quality (low, high, 4k) qh: high quality s: save last frame t: transparent background
config.background_color = WHITE
Add a background image
bg_image = ImageMobject("bg_P5.png")
self.add( bg_image )
Add the background plane
plane = NumberPlane( background_line_style={
"stroke_color": BLACK,
"stroke_width": 4,
"stroke_opacity": 1
},
x_range=[-10, 10, 1],
y_range=[-10, 10, 1],
)
self.add( plane )
Install Manim on Arch Linux
Pip is not working on a virtual environment. However use uv to install it. That works (on Sep 2024):
uv pip install manim
It is now installed on virtual environment, but the global system is not affected (not working outside virtual environment).
BG plane
number_plane = NumberPlane(
background_line_style={
"stroke_color": TEAL,
"stroke_width": 1,
"stroke_opacity": 0.2
}
)
self.add(number_plane)
Bezier curve
Animated bezier curve with two points:

https://github.com/markkuleino/manim/blob/main/FLL_CC0_bezier.py
See tutorial on Bezier Curves https://pomax.github.io/bezierinfo/ and the interactive tutorial https://www.jasondavies.com/animated-bezier/
Heat engine
-
Carnot cycle. Remove bg.
-
Otto cycle. Remove bg.
-
Diesel cycle. Remove bg.
Paths
https://github.com/Elteoremadebeethoven/AnimationsWithManim/blob/master/English/extra/faqs/paths.md
- Show Points
- Path as corners
- Path smoothly
- Bezier points of a path
- Change path style
Lines
https://github.com/markkuleino/manim/blob/main/FLL_CC2_m13b.py
Camera
Animate camera movement while animating sine wave (plane wave).
surface = always_redraw(
lambda: Surface(
lambda u, v: np.array([ u, v, np.sin(u - phase.get_value()) ]),
u_range=[-50, 50],
v_range=[-5, 5],
resolution=(15, 15),
fill_opacity=0.8,
#checkerboard_colors=[ BLUE_D, BLUE_E, ],
).set_fill_by_value(
axes=axes,
colors=[ (BLUE_E, -1), (BLUE, 0), (YELLOW, 1), ]
)
)
phi, theta, focal_distance, gamma, zoom = self.camera.get_value_trackers()
#1. Zoom away
self.play(
zoom.animate.set_value(0.5),
phase.animate.set_value(8 * np.pi),
run_time=10,
rate_func=linear,
)
The camera parameters
- phi Vertical angle (elevation)
- theta Horizontal angle (azimuth/orbit angle)
- focal_distance Perspective strength
- gamma Roll (rotation around viewing axis)
- zoom Zoom factor
Plane wave
from manim import *
from manim import smooth
import numpy as np
class PlaneWave3D(ThreeDScene):
def construct(self):
axes = ThreeDAxes( x_range=[-5, 5, 1], y_range=[-5, 5, 1], z_range=[-2, 2, 1],)
self.set_camera_orientation(
phi=65 * DEGREES,
theta=-45 * DEGREES,
zoom=2,
)
phase = ValueTracker(0)
x_pos = ValueTracker(20) # start at x=10 (right)
surface = always_redraw(
lambda: Surface(
lambda u, v: np.array([ u, v, 1*np.sin(u - phase.get_value()) ]),
u_range=[-50, 50],
v_range=[-5, 15],
resolution=(50, 50),
fill_opacity=1.0,
#checkerboard_colors=[ BLUE_D, BLUE_E, ],
).set_fill_by_value(
axes=axes,
colors=[ (BLUE_E, -1), (BLUE, 0), (YELLOW, 1), ]
)
)
gap = 0.02
antenna = always_redraw(
lambda: VGroup(
Line(
start=[x_pos.get_value(), 0, -1], # bottom of screen
end=[x_pos.get_value(), 0, -gap], # top of screen
color=YELLOW,
stroke_width=8
),
Line(
start=[x_pos.get_value(), 0, gap], # bottom of screen
end=[x_pos.get_value(), 0, 1], # top of screen
color=YELLOW,
stroke_width=8
),
))
amplitude = 0.5
antenna_sine = always_redraw(
lambda: VGroup(
VMobject().set_points_smoothly([
np.array([
x_pos.get_value() + amplitude*np.sin( np.pi*
(z-gap)/(2-gap)*np.cos(phase.get_value())),
0, # fixed y
z
])
for z in np.linspace(gap, 1, 50)
]),
VMobject().set_points_smoothly([
np.array([
x_pos.get_value() - amplitude*np.sin( np.pi + np.pi*
(z-gap)/(2-gap)*np.cos(phase.get_value())),
0, # fixed y
z
])
for z in np.linspace(-gap, -1, 50)
]),
))
#self.play(Create(axes))
self.play(Create(surface))
#self.begin_ambient_camera_rotation( rate=0.15, about='theta')
#self.begin_ambient_camera_rotation( rate=0.15, about='phi')
#self.begin_ambient_camera_rotation( rate=0.15, about='gamma')
#self.play(
# phase.animate.set_value(8 * np.pi), run_time=10, rate_func=linear,
#)
#self.stop_ambient_camera_rotation( about='theta')
#self.stop_ambient_camera_rotation( about='pi')
#self.stop_ambient_camera_rotation( about='gamma')
#self.wait()
phi, theta, focal_distance, gamma, zoom = self.camera.get_value_trackers()
#2. Slow down
current = phase.get_value()
self.play(
zoom.animate.set_value(0.5),
theta.animate.set_value(-10*DEGREES),
phase.animate.set_value(current + 20),
run_time=4,
#rate_func=lambda t: 1 - (1-t)**2,
rate_func = linear
)
#2. Ramp the
cruise_speed = 2.0 # phase units per second
phase.add_updater(
lambda m, dt: m.increment_value(cruise_speed * dt)
)
self.wait(10)
phase.clear_updaters()
#3 Add the antenna
#Add the sinewave
self.add(antenna)
current = phase.get_value()
self.play(
phase.animate.set_value(current+20),
theta.animate.set_value(-90*DEGREES),
phi.animate.set_value(90*DEGREES),
x_pos.animate.set_value(0), # move to x=-10 (left)
run_time=10,
rate_func=linear,
)
self.add(antenna_sine)
self.play(
zoom.animate.set_value(3),
phase.animate.set_value(current+40),
run_time=10,
rate_func=linear,
)