Manim 教程

用 Manim 作出精美的数学视频!

用 Manim 作出精美的数学视频!

简介与安装

  Manim 是一个用于绘制数学视频的动画引擎。下面是一个介绍视频:

  Windows 用户可以根据 知乎:3Blue1Brown的动画引擎如何配置? 来安装 Manim。Mac 和 Linux 用户请自行查找相关资料。

Hello, world!

  按照惯例,我们以 Hello, world! 作为第一个程序。在 manim-master 解压出的文件夹中新建 HelloWorld.py 文件,输入以下代码:

Hello, world!代码
1
2
3
4
5
6
7
from manimlib.imports import *  # 导入manimlib

class HelloWorld(Scene):  # 从 Scene 创建子类
    def construct(self):  # 开始定义动画
        helloWorld = TextMobject("Hello, world!")
        self.play(Write(helloWorld))
        self.wait()

  我们来一行行看我们的代码:

  1. from manimlib.imports import * 导入 manimlib.imports 模块,必不可少的。
  2. class HelloWorld(Scene): 从 Scene 创建一个子类 HelloWorld. Scene 可以理解是一个空画布,而 HelloWorld 则定义在画布上的动画
  3. def construct(self):…… 向空画布中添加一系列动画。后面的代码我们以后再深入学习。

  写好代码后,我们需要使用 manim 来生成 mp4视频文件。在 Anaconda Prompt 中进入到 manim-master文件夹,运行渲染命令:

python -m manim HelloWorld.py HelloWorld -lp

  各个参数的意义如下:

  1. -m manim 以脚本模式运行 manim 模块
  2. HelloWorld.py 动画文件的地址
  3. HelloWorld 动画文件中要生成动画的类
  4. -lp 以低画质(low)模式渲染,渲染完后预览(preview)。更多参数可以参考 文件的执行

  就会出现以下视频:

Manim 对象与方法

Manim 动画中所有的东西都是类,而所有的类都基于 Mobject (Math object),从 Mobject 出发引申出各种文字、图形等子类。类的方法则定义了各种动画。

文字

初始化

文字对应于 TextMobject(),其初始化方法如下:

# 初始化 TextMobject 对象
class WriteText(Scene): 
    def construct(self):
        #一般文字 
        text1 = TextMobject("Euler’s formula:")

        #LaTeX
        text2 = TextMobject(r"$\rm{e}^{i\pi}+1=0$")

        #LaTeX环境
        text3 = TextMobject(r"""$$
            \begin{cases}
            \rm{e}^{i\theta}=\cos\theta+i\sin\theta\\
            \rm{e}^{-i\theta}=\cos\theta-i\sin\theta
            \end{cases}
            $$
        """)

        text1.move_to(1*UP)
        text3.move_to(1.2*DOWN+1*RIGHT)
        self.add(text1)
        self.play(Write(text2))
        self.play(ShowCreation(text3))
        self.wait(3)

  运行渲染命令后,可以得到这样的视频: WriteText.gif(点击查看)

  在上面的程序中,我们利用了三种不同的字符串来初始化 TextMobject()

  1. 使用一般字符串。效果和普通的文字一样
  2. 使用 raw 字符串+LaTeX,显示出 LaTeX 公式
  3. 使用 raw """ 字符串+LaTeX环境+LaTeX,显示大括号与公式组

  这里唯一要说明的就是 \ 符号。在一般字符串中,\ 作为转义字符,要输入 \ 必须使用 \\;而在 r 字符串中,直接输入 \ 即可。

  此外,这段视频也展示了三种不同的出场方式。以后我们常用的就是这三种。

改变字体属性

# 设置 TextMobject 属性
class SetText(Scene): 
    def construct(self):
        # 改变颜色
        textColor = TextMobject("A","B","C","D","E","F")
        textColor[0].set_color(RED)
        textColor[1].set_color(ORANGE)
        textColor[2].set_color(YELLOW)
        textColor[3].set_color(GREEN)
        textColor[4].set_color(BLUE)
        self.play(Write(textColor))

        #设置字体大小
        textSmaller = TextMobject("Small Text")
        textSmaller.scale(0.8)
        textSmaller.move_to(DOWN)
        textBigger = TextMobject("Bigger Text")
        textBigger.next_to(textSmaller,0.5*DOWN,buff=2.5)
        self.play(Write(textSmaller), Write(textBigger))

        #在动画中改变大小
        self.play(ApplyMethod(textColor.scale,2))

        #改变字体位置
        textDL = TextMobject("DOWN+LEFT")
        textDL.to_edge(DOWN+LEFT)
        textUR = TextMobject("UP+RIGHT")
        textUR.to_edge(UP+RIGHT)
        self.add(textDL, textUR)

        #旋转
        textRotate=TextMobject(r"$\rightarrow$")
        textRotate.shift(UP)
        textRotate.rotate(PI/4)
        self.play(Write(textRotate))
        for i in range(360):
            self.wait(0.1)
            textRotate.rotate(PI/180)
        self.wait(0.5)
        textRotate.flip(LEFT)

        self.wait(0.5)

  我们不再一一解释命令了,对应视频 SetText.gif(点击查看) 就可以知道命令的作用。唯一要说明的,就是颜色是预定义的,如果要自定义颜色,可以在 manimlib-constants.py 里定义。

字体变换

  在数学教学视频中,我们经常通过字体之间的移动、渐变等来表示函数变换。最常用的就是 TransformReplacementTransform。这两者都能用于从一个文字变到另一个文字,但有如下区别:

  • Transform(A, B) 将 A 的文字、样式替换为 B 的文字、样式,但屏幕上显示的依然是 A
  • ReplacementTransform 将 A 的文字、样式替换为 B 的文字、样式,但屏幕上显示的是 B

Transform代码示例
1
2
3
4
5
6
7
8
9
class TransformationText1V2(Scene):
    def construct(self):
        texto1 = TextMobject("First text")
        texto1.to_edge(UP)
        texto2 = TextMobject("Second text")
        self.play(Write(texto1))
        self.wait()
        self.play(Transform(texto1,texto2))
        self.wait()

ReplacementTransform代码示例1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class TransformationText2(Scene):
    def construct(self):
        text1 = TextMobject("Function")
        text2 = TextMobject("Derivative")
        text3 = TextMobject("Integral")
        text4 = TextMobject("Transformation")
        self.play(Write(text1))
        self.wait()
        #Trans text1 -> text2
        self.play(ReplacementTransform(text1,text2))
        self.wait()
        #Trans text2 -> text3
        self.play(ReplacementTransform(text2,text3))
        self.wait()
        #Trans text3 -> text4
        self.play(ReplacementTransform(text3,text4))
        self.wait()

ReplacementTransform代码示例2
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
class CopyTextV1(Scene):
    def construct(self):
        formula = TexMobject(
            "\\frac{d}{dx}", #0
            "(", #1
            "u", #2
            "+", #3
            "v", #4
            ")", #5
            "=", #6
            "\\frac{d}{dx}", #7
            "u", #8
            "+", #9
            "\\frac{d}{dx}", #10
            "v" #11
            )
        formula.scale(2)
        self.play(Write(formula[0:7]))
        self.wait()
        self.play(
            ReplacementTransform(formula[2].copy(),formula[8]),
            ReplacementTransform(formula[4].copy(),formula[11]),
            ReplacementTransform(formula[3].copy(),formula[9])
            )
        self.wait()
        self.play(
            ReplacementTransform(formula[0].copy(),formula[7]),
            ReplacementTransform(formula[0].copy(),formula[10])
            )
        self.wait()

参考资料