如果你是一个数学老师或者你是一个数学类博主,在进行教学或者科普知识的时候,是否考虑过用动画的方法来展现数学特别是涉及到几何图形的知识呢?但是又苦于动画的制作太麻烦而放弃,比如画一个圆,画一条直线,用传统的动画制作方法都会耗费大量的时间和精力。基于此,而往往陷入纠结两难的境地?
今天给你介绍一款专注于数学动画制作的软件,完全开源,只需要你懂一点python编程即可,它就是Manim,传统上,技术概念的动画制作相当乏味,因为很难让动画足够精确,以准确传达概念。而Manim 依靠 Python 的简洁性,以编程方式生成动画,从而方便地指定每个动画的运行方式。
下面就给大家简单介绍下这款软件的用法,这款软件目前在windows下没有安装包,需要自行构建python环境并安装
Manim安装
python -m venv venv #创建虚拟环境
venv/Scripts/activate #激活虚拟环境
python -m pip install manimgl #安装manim
强烈建议源码安装manim
git clone https://github.com/3b1b/manim.git
cd manim
pip install -e .
manimgl example_scenes.py OpeningManimExample
Manim 示例
• 首先创建一个project目录,然后创建一个名为scene.py的文件,当然这个python文件的名称大家可以自行定义
• 运行命令,-o的意思是将结果保存到文件夹,具体位置在config.yml中配置
manimgl project\scene.py CoordinateSystemExample --config_file project\config.yml -o
• config.yml配置文件如下,配置output目录即可保存到想要保存的位置:
directories:
# Set this to true if you want the path to video files
# to match the directory structure of the path to the
# sourcecode generating that video
mirror_module_path:False
# Where should manim output video and image files?
output:""
# If you want to use images, manim will look to these folders to find them
raster_images:""
vector_images:""
# If you want to use sounds, manim will look here to find it.
sounds:""
# Manim often generates tex_files or other kinds of serialized data
# to keep from having to generate the same thing too many times. By
# default, these will be stored at tempfile.gettempdir(), e.g. this might
# return whatever is at to the TMPDIR environment variable. If you want to
# specify them elsewhere,
temporary_storage:"D:\\AI\\Manim\\tmp_result"
universal_import_line:"from manimlib import *"
style:
tex_template:"
font: "Consolas"
text_alignment: "LEFT"
background_color: "#333333"
# Set the position of preview window, you can use directions, e.g. UL/DR/OL/OO/...
# also, you can also specify the position(pixel) of the upper left corner of
# the window on the monitor, e.g. "960,540"
window_position: UR
window_monitor:0
full_screen:False
file_writer_config:
# If break_into_partial_movies is set to True, then many small
# files will be written corresponding to each Scene.play and
# Scene.wait call, and these files will then be combined
# to form the full scene. Sometimes video-editing is made
# easier when working with the broken up scene, which
# effectively has cuts at all the places you might want.
break_into_partial_movies:False
video_codec:"libx264"
pixel_format:"yuv420p"
saturation:1.0
gamma:1.0
camera_resolutions:
low:"854x480"
med:"1280x720"
high:"1920x1080"
4k:"3840x2160"
default_resolution:"high"
fps:30
embed_exception_mode:"Verbose"
embed_error_sound:False
• 然后在scene.py添加如下代码,即可制作由正方形到圆形的变形过渡动画
class SquareToCircle(Scene):
def construct(self):
circle =Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
square =Square()
self.play(ShowCreation(square))
self.wait()
self.play(ReplacementTransform(square, circle))
self.wait()
结果如下:
• 可能有人会说光有图形太单调了,是否可以创建文字动画,当然,我们在python文件中添加如下代码:
class TextExample(Scene):
def construct(self):
# To run this scene properly, you should have "Consolas" font in your computer
# for full usage, you can see https://github.com/3b1b/manim/pull/680
text =Text("Here is a text", font="Consolas", font_size=90)
difference =Text(
"""
The most important difference between Text and TexText is that\n
you can change the font more easily, but can't use the LaTeX grammar
""",
font="Arial", font_size=24,
# t2c is a dict that you can choose color for different text
t2c={"Text": BLUE,"TexText": BLUE,"LaTeX": ORANGE}
)
VGroup(text, difference).arrange(DOWN, buff=1)
self.play(Write(text))
self.play(FadeIn(difference, UP))
self.wait(3)
fonts =Text(
"And you can also set the font according to different words",
font="Arial",
t2f={"font":"Consolas","words":"Consolas"},
t2c={"font": BLUE,"words": GREEN}
)
fonts.set_width(FRAME_WIDTH -1)
slant =Text(
"And the same as slant and weight",
font="Consolas",
t2s={"slant": ITALIC},
t2w={"weight": BOLD},
t2c={"slant": ORANGE,"weight": RED}
)
VGroup(fonts, slant).arrange(DOWN, buff=0.8)
self.play(FadeOut(text),FadeOut(difference, shift=DOWN))
self.play(Write(fonts))
self.wait()
self.play(Write(slant))
self.wait()
结果如下:
• 如果要显示公式相关的内容,建议安装miktex,安装地址 https://miktex.org/download,下面是一个坐标系曲线动画
class GraphExample(Scene):
def construct(self):
axes =Axes((-3,10),(-1,8))
axes.add_coordinate_labels()
self.play(Write(axes, lag_ratio=0.01, run_time=1))
# Axes.get_graph will return the graph of a function
sin_graph = axes.get_graph(
lambda x:2* math.sin(x),
color=BLUE,
)
# By default, it draws it so as to somewhat smoothly interpolate
# between sampled points (x, f(x)). If the graph is meant to have
# a corner, though, you can set use_smoothing to False
relu_graph = axes.get_graph(
lambda x: max(x,0),
use_smoothing=False,
color=YELLOW,
)
# For discontinuous functions, you can specify the point of
# discontinuity so that it does not try to draw over the gap.
step_graph = axes.get_graph(
lambda x:2.0if x >3else1.0,
discontinuities=[3],
color=GREEN,
)
# Axes.get_graph_label takes in either a string or a mobject.
# If it's a string, it treats it as a LaTeX expression. By default
# it places the label next to the graph near the right side, and
# has it match the color of the graph
sin_label = axes.get_graph_label(sin_graph,"\\sin(x)")
relu_label = axes.get_graph_label(relu_graph,Text("ReLU"))
step_label = axes.get_graph_label(step_graph,Text("Step"), x=4)
self.play(
ShowCreation(sin_graph),
FadeIn(sin_label, RIGHT),
)
self.wait(2)
self.play(
ReplacementTransform(sin_graph, relu_graph),
FadeTransform(sin_label, relu_label),
)
self.wait()
self.play(
ReplacementTransform(relu_graph, step_graph),
FadeTransform(relu_label, step_label),
)
self.wait()
parabola = axes.get_graph(lambda x:0.25* x**2)
parabola.set_stroke(BLUE)
self.play(
FadeOut(step_graph),
FadeOut(step_label),
ShowCreation(parabola)
)
self.wait()
# You can use axes.input_to_graph_point, abbreviated
# to axes.i2gp, to find a particular point on a graph
dot =Dot(color=RED)
dot.move_to(axes.i2gp(2, parabola))
self.play(FadeIn(dot, scale=0.5))
# A value tracker lets us animate a parameter, usually
# with the intent of having other mobjects update based
# on the parameter
x_tracker =ValueTracker(2)
f_always(
dot.move_to,
lambda: axes.i2gp(x_tracker.get_value(), parabola)
)
self.play(x_tracker.animate.set_value(4), run_time=3)
self.play(x_tracker.animate.set_value(-2), run_time=3)
self.wait()
结果如下:
• 坐标系统动画
class CoordinateSystemExample(Scene):
def construct(self):
axes =Axes(
# x-axis ranges from -1 to 10, with a default step size of 1
x_range=(-1,10),
# y-axis ranges from -2 to 2 with a step size of 0.5
y_range=(-2,2,0.5),
# The axes will be stretched so as to match the specified
# height and width
height=6,
width=10,
# Axes is made of two NumberLine mobjects. You can specify
# their configuration with axis_config
axis_config={
"stroke_color": GREY_A,
"stroke_width":2,
},
# Alternatively, you can specify configuration for just one
# of them, like this.
y_axis_config={
"include_tip":False,
}
)
# Keyword arguments of add_coordinate_labels can be used to
# configure the DecimalNumber mobjects which it creates and
# adds to the axes
axes.add_coordinate_labels(
font_size=20,
num_decimal_places=1,
)
self.add(axes)
# Axes descends from the CoordinateSystem class, meaning
# you can call call axes.coords_to_point, abbreviated to
# axes.c2p, to associate a set of coordinates with a point,
# like so:
dot =Dot(color=RED)
dot.move_to(axes.c2p(0,0))
self.play(FadeIn(dot, scale=0.5))
self.play(dot.animate.move_to(axes.c2p(3,2)))
self.wait()
self.play(dot.animate.move_to(axes.c2p(5,0.5)))
self.wait()
# Similarly, you can call axes.point_to_coords, or axes.p2c
# print(axes.p2c(dot.get_center()))
# We can draw lines from the axes to better mark the coordinates
# of a given point.
# Here, the always_redraw command means that on each new frame
# the lines will be redrawn
h_line = always_redraw(lambda: axes.get_h_line(dot.get_left()))
v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom()))
self.play(
ShowCreation(h_line),
ShowCreation(v_line),
)
self.play(dot.animate.move_to(axes.c2p(3,-2)))
self.wait()
self.play(dot.animate.move_to(axes.c2p(1,1)))
self.wait()
# If we tie the dot to a particular set of coordinates, notice
# that as we move the axes around it respects the coordinate
# system defined by them.
f_always(dot.move_to,lambda: axes.c2p(1,1))
self.play(
axes.animate.scale(0.75).to_corner(UL),
run_time=2,
)
self.wait()
self.play(FadeOut(VGroup(axes, dot, h_line, v_line)))
# Other coordinate systems you can play around with include
# ThreeDAxes, NumberPlane, and ComplexPlane.
结果如下:
能实现的功能还很多,有兴趣的可以自行探索,官网:
Quick Start - manim documentation (3b1b.github.io)
1578

被折叠的 条评论
为什么被折叠?



