Short introduction to Manim: Difference between revisions
| (22 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
== Introduction == | == Introduction == | ||
Manim or manimgl. | |||
Virtual engine | |||
<syntaxhighlight lang="bash"> | |||
python -m venv . | |||
source bin/activate | |||
</syntaxhighlight> | |||
For faster rendering use smaller resolution <code>resolution=(15,15)</code> and smaller fps rate | |||
<syntaxhighlight lang="bash"> | |||
manim -pql --fps 10 ma_animatedplanewave.py PlaneWave3D | |||
</syntaxhighlight > | |||
Some short manim scrips to generate easy visualizations. | Some short manim scrips to generate easy visualizations. | ||
<syntaxhighlight lang="bash"> | |||
manim shorts.py MyScene -r 1080,1920 | |||
</syntaxhighlight > | |||
<pre> | <pre> | ||
| Line 26: | Line 42: | ||
self.add( bg_image ) | self.add( bg_image ) | ||
</pre> | </pre> | ||
Add the background plane | |||
<pre>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 ) | |||
</pre> | |||
=== Install Manim on Arch Linux === | |||
Pip is not working on a virtual environment. However use <code>uv</code> to install it. That works (on Sep 2024): | |||
<code> | |||
uv pip install manim | |||
</code> | |||
It is now installed on virtual environment, but the global system is not affected (not working outside virtual environment). | |||
== BG plane == | |||
<syntaxhighlight lang="python"> | |||
number_plane = NumberPlane( | |||
background_line_style={ | |||
"stroke_color": TEAL, | |||
"stroke_width": 1, | |||
"stroke_opacity": 0.2 | |||
} | |||
) | |||
self.add(number_plane) | |||
</syntaxhighlight > | |||
== Bezier curve == | == Bezier curve == | ||
| Line 32: | Line 83: | ||
[[File:Bez2.svg|thumb]] | [[File:Bez2.svg|thumb]] | ||
https://github.com/markkuleino/manim/blob/main/bezier. | 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 == | |||
<gallery> | |||
CarnotCycle.gif|Carnot cycle. Remove bg. | |||
Ottocycle.gif|Otto cycle. Remove bg. | |||
DieselCycle.gif|Diesel cycle. Remove bg. | |||
</gallery> | |||
*[[File:Carnot.zip]] | |||
*[[File:otto.zip]] | |||
*[[File:diesel.zip]] | |||
== 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 == | == Lines == | ||
https://github.com/markkuleino/manim/blob/main/FLL_CC2_m13b.py | https://github.com/markkuleino/manim/blob/main/FLL_CC2_m13b.py | ||
== Camera == | |||
Animate camera movement while animating sine wave (plane wave). | |||
<syntaxhighlight lang="python"> | |||
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, | |||
) | |||
</syntaxhighlight > | |||
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 == | |||
<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> | |||
Latest revision as of 14:56, 13 July 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 shorts.py MyScene -r 1080,1920
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,
)