manim学习笔记

3Blue1Brown,以专业数学素养和高质量的可视化数学教学闻名的YouTube频道,其中的微积分系列线性代数系列更是外行能欣赏其形式美,内行能欣赏其内容美的佳作,帮助了许多人建立数学直觉(math intuition)层面上的更深理解

笔者最近便学习了其可视化制作的动画引擎——manim的使用,本着_学了无聊就要写笔记_ 的原则,在此稍作记录

Hello World

​ 众所周知,不管是啥语言都要从最基本的最经典的”Hello World”写起

​ 先上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from manimlib.imports import *

class Helloworld(Scene):
def construct(self):
example_text = TextMobject(
"Hello World",
tex_to_color_map={"Hello": RED, "World": BLUE}
)
example_tex = TexMobject(
"\\delta S = \\delta \\int_{t_1}^{t_2} L dt = 0",
)
group = VGroup(example_text, example_tex)
group.arrange(DOWN)
group.set_width(FRAME_WIDTH - 2 * LARGE_BUFF)

self.play(Write(example_text))
self.play(Write(example_tex))
self.wait()

​ 前面的manimlib.imports是manim的自带库,基本上所有需要用到的函数都在这个库引用到的文件中有应用

HelloworldScene的子类,如果没有特殊需要的基本都这样定义,等于设定一个幕布的效果?

construct()类似于C++中的main()函数,程序的主框架都放在里面

TextMobject()TexMobject()实际上区别就是前一个支持渲染带空格的文本,后一个就是普通的$\LaTeX$(这个符号要用\LaTeX区分大小写才能渲染),需要注意的是由于python本身语法的问题,原来$\LaTeX $中的\XXX现在要改为\\\XXX才能渲染

VGroup的作用好像是将多个对象有所区分的组成一个动画组,便于对他们的参数进行调整,其中的arrange应该是显示的方向,set_width是宽度设定

FRAME_WIDTH指的是屏幕宽度,LARGE_BUFF是边距,这些直接调用后参数手动微调就好

self.play()Animation中的一个函数,是生成动画效果的必备函数,在其中可以同时放置多个动画的相关函数,意为这些操作会同时进行,若是像上文代码中那样分开排列,则按照先后顺序显示

Write()是绘制文本的动画函数,相似的还有ShowCreation()这个绘制几何图形的动画函数,注意这类函数一定要放在self.play()中才有效

self.wait()根据字面意思就知道,是在动画末尾插入几帧以起到停顿的效果

​ 最后效果如下

Circle

这是根据一个能在上下移动时显示与屏幕水平中线相对位置的正方形改编的

为了好玩乱改一通结果折腾了一个上午。。

话不多说先上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from manimlib.imports import *

class DrawCircle(Scene):
def construct(self):
decimal = DecimalNumber(
0,
show_ellipsis=True,
num_decimal_places=3,
include_sign=True,
)

circle1 = Circle()
circle2 = Circle(color = RED).to_edge(UP)
circle3 = Circle(color = BLUE).to_edge(DOWN)
circle4 = Circle()

decimal.add_updater(lambda d: d.next_to(circle1, RIGHT))
decimal.add_updater(lambda d: d.set_value(circle1.get_center()[1]))

self.play(ShowCreation(circle1))
self.play(ShowCreation(decimal))
self.play(
circle1.to_edge, UP,
rate_func = rush_from,
run_time = 1.25,
)
self.play(FadeOut(circle1), FadeIn(circle2))
decimal.add_updater(lambda d: d.next_to(circle2, RIGHT))
decimal.add_updater(lambda d: d.set_value(circle2.get_center()[1]))
self.play(
circle2.to_edge, DOWN,
rate_fuc = rush_from,
run_time = 2.5
)
self.play(FadeOut(circle2), FadeIn(circle3))
decimal.add_updater(lambda d: d.next_to(circle3, RIGHT))
decimal.add_updater(lambda d: d.set_value(circle3.get_center()[1]))
self.play(
circle3.center,
rate_fuc = rush_from,
run_time = 1.25,
)
self.play(Transform(circle3, circle4))
self.wait()

decimal的意思是十进制数,第一个参数是初始值,show_ellipsis控制是否输出数字后的省略号,num_decimal_places是指显示到小数点后第几位(默认是两位),include_sign控制时候输出数字前的正号(若为False则默认不输出正号而默认输出负号)

Circle()manilib\mobject\geometry.py 中的圆的构造函数,color控制颜色,radius控制半径,heightwidth控制长和宽 ,to_edge()可以用于控制生成圆的初始位置,也可以控制圆的运动位置,UP/DOWN/LEFT/RIGHT控制上下左右(本质是方向不同的单位长度向量,可以乘以常数scale,也可以和其他向量加减)

add_updater虽然不知道是具体的意思,但笔者猜应该是对decimal的位置和数值进行更新,next_to()是一个平移函数,输入初始位置(一般用对象调用的方式给出)和方向后返回平移后具体位置,set_value()就是给出一个给定位置的相对位置(代码中的[1]代表纵坐标)

self.play()中控制对象运动时to_edge, UP给出对象的最终位置,rate_func给出运动方式和速率,具体参见manimlib\utils\rate_functions.py

FadeIn()FadeOut()分别是淡入和淡出,组合同时使用能起到切换对象的效果,在这里是用于解决Transform()会出现的切换后再运动时原对象会被复制一份并在原地不动的问题

center()顾名思义就是中间的意思,而ORIGIN(0,0,0)向量

最后效果见下