Welcome to Manim's documentation!

_images/logo.png

About

Manim 是Grant Sanderson (3Blue1Brown) 开发的Python库,旨在制作数学动画. Sanderson最早开发该库用于自己在可汗学院教授微积分等数学课程时使用,现在Manim仍是一个在发展中的项目!如果你看过3Blue1Brown频道的数学视频,一定领略到了Manim的强大,虽然还有很多不完善的地方,但瑕不掩瑜!

本中文文档是本人业余所为,所以不能保证更新的进度.如果你想要提前了解更多,可以参考 Theorem of Beethoven 编写的 英文文档 (但也不完善).另外推荐YouTube频道上的Theorem of Beethoven频道,其专门制作Manim教程,比较系统;Bilibili上有搬运教程,但由于版权问题,就不在这里给出链接.本文档有很多地方也都是参考Theorem of Beethoven的视频资料和文档.

Theorem of Beethoven的 Github 上有很多有趣的projects,可供大家参考!

关于Manim,Bilibili和YouTube上都有很多有趣的资源可供参考与学习,但都比较杂,没有形成社区生态.故本文档参考资料比较杂烩,会在相关之处一一列明,如果侵权了您的权益,请与我联系!

Happy maniming!

Windows

本节主要参考 Manim Github 的README.

Manim的安装有两种方式,第一种是安装发布的manimlib,第二种是克隆Github上的repo,在本地直接与repo中的manimlib的文件挂钩进行编译.

个人推荐第二种安装方式,因为发布的manimlib比较老了,有些功能没有更新;其次方便自己二次开发,并与自己fork的repo同步;最后是安装比较简单.这里只介绍第二种安装方式,基于Window系统,其他平台本人不熟悉,故暂不予以详细介绍,请参考Github上的README.

警告

如果你选择直接安装manimlib的话,需要把电脑的编码改成UTF-8(自行百度一下),否则可能会出现一片红的报错,但是这会对一些软件造成影响!

安装Python

官网 直接下载安装即可.个人建议安装 Anaconda ,方便一些!

重要

安装Python的时候要把添加到环境变量的选项勾选上.如果安装Anaconda,也要添加到环境变量(一般默认添加).

安装相关的依赖

小技巧

使用pip进行安装相关的包时,可以更换为国内的镜像源(自行百度一下)!

安装FFmpeg

FFmpeg是多媒体处理库,manim的视频、图片、音乐插入与生成都依赖与它.安装请参考 文章.

重要

也要记得把FFmpeg添加到环境变量中.

安装Cairo

这个包是manim的基本图形包,直接通过pip安装会出问题,建议直接手动安装.在 这里 下载对应你的python和操作系统位数版本的Cairo.然后在 cmd 中输入

pip install <Cairo的路径>(比如 C:/path/to/wheel/pycairo‑1.18.0‑cp37‑cp37m‑win32.whl)

重要

如果你安装的是Anaconda,需要先启动Anaconda的环境,在命令行中输入 conda activate <环境名称> 如果省略 环境名称 则默认启动 root 环境.后续的命令行操作也都如此.

安装LaTex

如果你没有使用LaTex的话,可以在网上查找相关的资料快速上手.Manim中的文字和数学公式都是通过Latex编译,然后生成SVG图片插入的.推荐下载 MiKTeX ,包管理比较方便.

重要

同样要把相关的路径添加到环境变量,但一般默认会添加.如果编译出现问题再检查一下.

安装Sox

Sox是音频处理包.可以直接通过 pip install sox 安装.

克隆repo并安装其他依赖

克隆repo

在电脑上选个好地方,打开 Git Bash ,输入

git clone https://github.com/3b1b/manim.git

如果你有二次开发的需求,可以先fork再clone.如果你不了解Git的使用,可以参考廖雪峰的 教程.

安装其他Python依赖

打开 cmd 命令行,进入到manim repo的本地主目录中,输入

pip install -r requirements.txt

requirement.txt 文件内容如下

argparse==1.4.0
colour==0.1.5
numpy==1.16.4
Pillow==5.2.0
progressbar==2.5
scipy==1.3.0
tqdm==4.24.0
opencv-python==3.4.2.17
pycairo==1.17.1; sys_platform == 'linux'
pycairo>=1.18.1; sys_platform == 'win32'
pydub==0.23.0
pyreadline==2.1; sys_platform == 'win32'

如果你本地已经安装了其中的一些包,但版本高于上面所列的版本号,如果不想降低版本的话,可以把上面的 == 都改为 >= ,但本人不晓得会不会出现什么问题.你也可以用Anaconda创建一个新的虚拟环境后再安装.

测试

cmd 中,进入manim的repo本地主目录,输入:

python manim.py example_scenes.py SquareToCircle -pl

python 代表启动python, manim.py 是编译脚本, example_scenes.py 是包含你的动画代码的文件, SquareToCircle 是你要编译的动画类, -pl 是编译选项,后面会有详细介绍.第一和第二个是必不可少的.

如果最后出现动画了,那恭喜你,你已经基本完成了安装!

你也可以选择Live Sreaming模式,但我觉得挺鸡肋的.

> python -m manim --livestream
Writing to media/videos/scene/scene/1080p30/LiveStreamTemp.mp4

Manim is now running in streaming mode. Stream animations by passing
them to manim.play(), e.g.
>>> c = Circle()
>>> manim.play(ShowCreation(c))

>>>

如果你安装的是manimlib库,应该输入

manim my_project.py MyScene

manim 是直接启动manim, my_project.py 就是包含你的动画代码的文件, MyScene 是你要编译的动画类.可以附加编译选项,这里没有加.

再次不推荐你安装manimlib!

GNU/Linux

请参考 Manim tutorial | Installation on GNU/Linux 进行安装.

# TODO: 完善细节

Mac OS

请参考 Manim tutorial | Installation on Mac 进行安装.

# TODO: 完善细节

FAQ

注意

此节不仅包含了安装时出现的问题,也包含了各种其他问题,若暂时没有遇到或不需要则不必要看.

LaTex问题

如果你尝试编译 example_scenes.py 中的其他动画类,出现报错 Latex error converting to dvi 的话,那就对了.

LaTex是非常折腾的东西,但如果你熟悉了的话也就那样吧.这里报错最大的问题其实是LaTex的一些宏包没有安装好,而很多MiKTeX宏包安装是连接到国外的服务器.如果你科学上网的话,这里应该不成问题,因为Latex会自动安装缺失的宏包.如果没有的话,因为连接超时下载不到宏包无法正确编译而报错.

解决方案一:科学上网!这是最佳方案,因为可以持续更新宏包!

解决方案二:手动安装或者更换镜像源! 这里 有一些解决方案!

Manim编译需要的LaTex宏包你可以在 /manimlib/tex_template.tex/manimlib/ctex_template.tex 中找到;前者是英文模板,后者是中文模板.怎么使manim支持中文,后面会讲到.

你可以打开电脑中的MiKTeX Console:

_images/2020-03-13-14-32-19.png

选择 Switch to MiKTeX Console administrator mode 进入管理员模式,点击左手边的 Package .

_images/2020-03-13-14-35-23.png

在上面的方框中输入宏包名称来检查该宏包是否安装.

使LaTex支持更多的Latex宏包/字体

参考 Manim fast tutorial - How to add more LaTeX packages?

更改 /manimlib/tex_template.tex 或者 /manimlib/ctex_template.tex 模板即可.但如果你不熟悉LaTex请不要随便更改!

以我的LaTex模板为例:

 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
45
46
47
/documentclass[preview]{standalone}

/usepackage[english]{babel}
/usepackage{amsmath}
/usepackage{amssymb}
/usepackage{dsfont}
/usepackage{setspace}
/usepackage{tipa}
/usepackage{relsize}
/usepackage{textcomp}
/usepackage{mathrsfs}
/usepackage{wasysym}
/usepackage{ragged2e}
/usepackage{physics}
/usepackage{listings}
/usepackage{xcolor}
% /usepackage{microtype}

%% 字体设置
/usepackage[T1,OT1]{fontenc}
/usepackage{palatino}  % english defalut font
/usepackage{calligra}  % hand-writing /calligra
/newcommand{/sz}[1]{/fontencoding{OT1}/fontfamily{pplj}/fontseries{m}/selectfont #1}   % non-aligned number
/newcommand{/gt}[1]{/fontencoding{OT1}/fontfamily{pgoth}/fontseries{m}/selectfont #1}  % Gothic font

%% xelatex font
/usepackage{fontspec}
/usepackage{xeCJK}
/setCJKmainfont[BoldFont={Noto Sans S Chinese}]{Noto Serif CJK SC}
/setCJKmonofont{Noto Serif CJK SC}
/setCJKsansfont{Noto Serif CJK SC SemiBold}  % /sf Chinese title font
/setmainfont{Palatino-Roman}    % /sf English hand-writing font
/setsansfont{Zapfino}

%% 绘图工具
/usepackage[siunitx,RPvoltages,european]{circuitikz}
/usepackage{tikz}

% /DisableLigatures{encoding = *, family = * }

/linespread{1}

/begin{document}

YourTextHere

/end{document}

字体设置 :这里可以自定义字体,不详细展开.

绘图工具 :如果要使Tikz或者CircuiTikz的绘图正确显示,需要更改绘图的填充,否则不能在manim编译的动画中正确显示,因为manim会自动滤除颜色.对于一些LaTex自带的符号也是如此!如果你发现LaTex编译没有问题,那很大程度上的问题就是填充透明度等有问题.

有兴趣的可以尝试一下:

警告

需要在英文模式下,也就是 TEX_USE_CTEX=False 的情况下编译!因为中文情况下的处理会出现问题!

class TikzMobject(TextMobject):
    CONFIG = {
        "stroke_width": 1,
        "fill_opacity": 1,
        "stroke_opacity": 1,
    }


class ExampleTikz(Scene):
    def construct(self):
        circuit = TikzMobject(r"""
            /begin{circuitikz}[american voltages]
            /draw
            (0,0) to [short, *-] (6,0)
            to [V, l_=$/mathrm{j}{/omega}_m /underline{/psi}^s_R$] (6,2)
            to [R, l_=$R_R$] (6,4)
            to [short, i_=$/underline{i}^s_R$] (5,4)
            (0,0) to [open,v^>=$/underline{u}^s_s$] (0,4)
            to [short, *- ,i=$/underline{i}^s_s$] (1,4)
            to [R, l=$R_s$] (3,4)
            to [L, l=$L_{/sigma}$] (5,4)
            to [short, i_=$/underline{i}^s_M$] (5,3)
            to [L, l_=$L_M$] (5,0);
            /end{circuitikz}
            """)
        self.play(Write(circuit))
        self.wait()
_images/ExampleTikz.gif

中文支持问题

首先要确保你的LaTex能够正常编译!在 manimlib/constants.py 找到 TEX_USE_CTEX 设置为 True.

如果你的LaTex在英文模式下能够正常编译的话,这里应该问题就不大了.如果出现 xeLatex error converting to dvi 报错,很大程度上还是宏包问题.这里中文使用的是 ctex 宏包,要注意有没有安装.

LaTex实在有很多可以讲的,这一块都能单独写好几本书了,我们放到后面.网上有很多资料和书籍,需要大家多多学习和折腾!

字体问题

如果你不想通过改变LaTex模板来设置字体,你可以参考 xy-23 编写的一个新的 .

但有个问题是输出的图片并非矢量,所以效果会差一点.

编译过慢问题

Manim编译有时候会很慢,如果你的电脑上有GPU的话,那恭喜你!你可用GPU来加速!在你的文件中添加如下代码:

import os

def set_gpus(gpu_index):
    if type(gpu_index) == list:
        gpu_index = ','.join(str(_) for _ in gpu_index)
    if type(gpu_index) ==int:
        gpu_index = str(gpu_index)
    os.environ["CUDA_VISIBLE_DEVICES"] = gpu_index

然后在你的动画类的 def construct(self): 下面写一行代码: set_gpus(0) 或者 set_gpus([0,1]) 如果你是土豪有两块GPU的话!

有了GPU的加持,你会体验到编译速度的飞跃,但对于大规模的计算场景,如多个对象淡出、Update动画等,速度会回归龟速.

编译动态条问题

如果你不喜欢编译时的动态条,可以参考 Manim tutorial | Settings - Leave the progress bars by default 去除.

# TODO:完善细节.

如何改变视频输出地址

个人不建议更改!默认就好,方便管理. 如果要更改,可以参考 Manim tutorial | Settings - Change media directory 设置.

# TODO:完善细节.

如何在终端中设置输出的帧率

参考 Manim tutorial | Settings - Render faster, set FPS via terminal 进行设置.

# TODO:完善细节.

添加声音、SVG和图像

参考 Manim tutorial | 7 - Add sounds, svgs and images 进行设置.

添加声音

# TODO: 完善细节

添加SVG

# TODO: 完善细节

添加图像

# TODO: 完善细节

如何改变背景颜色 1

class ChangeBackgroundColor(Scene):
    CONFIG={
        "camera_config":{"background_color":RED}, # 改变背景色
        "text":TexMobject(r"/frac{d}{dx}/Bigr|_{y=2}").scale(5)
    }
    def construct(self):
        self.add(self.text)
_images/ChangeBackgroundColor.png

如何去除字符的描边 1

class RemoveBackgroundStrokeWidth(ChangeBackgroundColor):
    CONFIG={
        "text":TexMobject(
            r"/frac{d}{dx}/Bigr|_{y=2}",
            background_stroke_width=0, # 设置描边粗细
            ).scale(5)
    }
_images/RemoveBackgroundStrokeWidth.png

gif输出问题

manimlib中的gif输出是没有问题的,但最新的manim repo进行更新后没有办法正确输出gif(至少我是这样的),如果你也遇到相同的情况,只需要把相关的设置改回去就可以了.

你需要安装manimlib这个包,找到 manimlib/config.py 跟克隆下来的manim仓库中的 manimlib/config.py 进行比对,参照 更新 进行更改.

你可以直接在我的Repo上下载这个 文件 进行替换.

1(1,2)

AnimationsWithManim/English/extra/faqs/faqs.md

VS Code配置

此节基于Bilibili Up主 pdcxs 的文章: 配置Manim自动编译环境(仅限VS Code Win) 进行改进.

如果你使用的是Visual Studio Code的话,可以配置自动编译,而不需要每一次手动输入 python manim.py example_scenes.py SquareToCircle -pl 这样一长串的代码.

配置tasks.json

在工作目录下的 .vscode 中(如果没有就创建一个)创建一个 tasks.json 文件(如果已经有了,直接修改即可).

 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "l",
            "type": "shell",
            "command": "python",
            "args": [
                "${workspaceFolder}\\compile_shell.py",
                "${relativeFile}",
                "${lineNumber}",
                "-pl"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": []
        },
        {
            "label": "best",
            "type": "shell",
            "command": "python",
            "args": [
                "${workspaceFolder}\\compile_shell.py",
                "${relativeFile}",
                "${lineNumber}",
                "-p"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": []
        },
        {
            "label": "m",
            "type": "shell",
            "command": "python",
            "args": [
                "${workspaceFolder}\\compile_shell.py",
                "${relativeFile}",
                "${lineNumber}",
                "-pm"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": []
        },
        {
            "label": "s",
            "type": "shell",
            "command": "python",
            "args": [
                "${workspaceFolder}\\compile_shell.py",
                "${relativeFile}",
                "${lineNumber}",
                "-s"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": []
        },
        {
            "label": "i",
            "type": "shell",
            "command": "python",
            "args": [
                "${workspaceFolder}\\compile_shell.py",
                "${relativeFile}",
                "${lineNumber}",
                "-il"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": []
        },
    ]
}

每一个任务都有一个标签(可以自定义修改),与其任务的输出格式是相对应的;如第 5 行的 l 代表的是低分辨率输出,相应的 第 12-pl 代表低分辨率渲染,并用默认播放器打开;其他任务同理.

  • l :低分辨渲染

  • m :中分辨渲染

  • b :最高分辨渲染,输出品质

  • i :输出gif格式

  • s :低分辨率保存最后一帧

可以自定义添加更多的指令.渲染指令请参考渲染设置.

配置compile_shell.py

在主工作目录下,添加 compile_shell.py 文件.这个文件是用于寻找当前要编译的动画类的名称.

 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
import sys
import os
import linecache

file_relative_dir=sys.argv[1]
linenumber=int(sys.argv[2])
setting=sys.argv[3]

while linenumber:
    text=linecache.getline(file_relative_dir,linenumber)
    if 'class' in text:
        escape_index=text.find(' ')
        brackets_index=text.find('(')
        class_name=text[escape_index:brackets_index]
        linecache.clearcache()
        break
    else:
        linenumber-=1

commands=[
    'D:\Anaconda3\python.exe',  # my anaconda dir -> to replace it with yours
    'manim.py',
    file_relative_dir,
    class_name,
    setting
]
os.system(' '.join(commands))

20-26 行是编译的命令,第 21 行是指定的编译器,由于我的电脑同时安装了Anaconda和Python,为了避免冲突,这里可以自行指定编译器;你需要自行替换一下.如果选择使用默认的Python,把第 21 行改为 python 即可.

这里的机制是在你的鼠标所在行向上搜索类的名称,然后放入命令行进行自动编译.原参考的解决方案是使用Poweshell,这对于非Windows平台是不支持的,这里改成Python脚本.理论上来说,只要能装上VS Code,都能实现这样的自动编译.

用法

鼠标放在要编译的manim动画代码中的任意一行,按下默认快捷键是 Ctrl+Shift+B ,会出现窗口:

_images/2020-03-14-09-46-21.png

通过鼠标或者键盘选择相应的任务即可.

编译选项

用法

对于安装manimlib的情况:

manim [-h] [-p] [-w] [-s] [-l] [-m] [--high_quality] [-g] [-i] [-f]
[-t] [-q] [-a] [-o FILE_NAME] [-n START_AT_ANIMATION_NUMBER]
[-r RESOLUTION] [-c COLOR] [--sound] [--leave_progress_bars]
         [--media_dir MEDIA_DIR]
         [--video_dir VIDEO_DIR | --video_output_dir VIDEO_OUTPUT_DIR]
         [--tex_dir TEX_DIR] [--livestream] [--to-twitch]
         [--with-key TWITCH_KEY]
         [file] [scene_names [scene_names ...]]

对于克隆repo直接挂钩编译则只需把上面的 manim 改为 python manim.py 就可以了.

选项详解

文件参数

file

代码路径

scene_names

需要编译的代码类

可选参数

-h, --help

显示帮助信息

-p, --preview

编译完成后自动播放

-w, --write_to_movie

将场景作为媒体文件渲染为电影文件

-s, --save_last_frame

输出最后一帧

-l, --low_quality

低分辨率渲染

-m, --medium_quality

中分辨率渲染

--high_quality

高分辨率渲染

-g, --save_pngs

保存每一帧为png图片

-i, --save_as_gif

保存为gif图片

-f, --show_file_in_finder

渲染完成后,打开文件所在文件夹

-t, --transparent

在透明通道渲染

-q, --quiet

#TODO

-a, --write_all

编译文件中的所有动画类

-o FILE_NAME, --file_name FILE_NAME

重新命名动画类名称

-n START_AT_ANIMATION_NUMBER, --start_at_animation_number START_AT_ANIMATION_NUMBER

不从第一个动画开始渲染,如果输入两个参数,例如"3,6",就只渲染第3到第6个动画

-r RESOLUTION, --resolution RESOLUTION

重设分辨率,输入形式为 高度,宽度

-c COLOR, --color COLOR

设置背景颜色

--sound

编译成/失败提示音

--leave_progress_bars

隐藏终端中的编译条

--media_dir MEDIA_DIR

设置存放媒体文件的目录

--video_dir VIDEO_DIR

设置存放输出视频的目录

--video_output_dir VIDEO_OUTPUT_DIR

设置输出视频的目录

--tex_dir TEX_DIR

设置存放 .tex 文件的目录

--livestream

进入livestream模式

--to-twitch

#TODO

--with-key TWITCH_KEY

#TODO

Hello World

Manimlib架构

.
├── manim.py
├── stage_scenes.py
├── manimlib
│   ├── animation
│   │   ├── animation.py
│   │   ├── composition.py
│   │   ├── creation.py
│   │   ├── fading.py
│   │   ├── growing.py
│   │   ├── indication.py
│   │   ├── movement.py
│   │   ├── numbers.py
│   │   ├── rotation.py
│   │   ├── specialized.py
│   │   ├── transform.py
│   │   └── update.py
│   ├── camera
│   │   ├── camera.py
│   │   ├── mapping_camera.py
│   │   ├── moving_camera.py
│   │   ├── multi_camera.py
│   │   └── three_d_camera.py
│   ├── config.py
│   ├── constants.py
│   ├── container
│   │   └── container.py
│   ├── ctex_template.tex
│   ├── extract_scene.py
│   ├── files
│   │   ├── Bubbles_speech.svg
│   │   ├── Bubbles_thought.svg
│   │   └── PiCreatures_plain.svg
│   ├── for_3b1b_videos
│   │   ├── common_scenes.py
│   │   ├── pi_class.py
│   │   ├── pi_creature_animations.py
│   │   ├── pi_creature.py
│   │   └── pi_creature_scene.py
│   ├── mobject
│   │   ├── changing.py
│   │   ├── coordinate_systems.py
│   │   ├── frame.py
│   │   ├── functions.py
│   │   ├── geometry.py
│   │   ├── matrix.py
│   │   ├── mobject.py
│   │   ├── mobject_update_utils.py
│   │   ├── number_line.py
│   │   ├── numbers.py
│   │   ├── probability.py
│   │   ├── shape_matchers.py
│   │   ├── svg
│   │   │   ├── brace.py
│   │   │   ├── drawings.py
│   │   │   ├── svg_mobject.py
│   │   │   └── tex_mobject.py
│   │   │   └── text_mobject.py
│   │   ├── three_dimensions.py
│   │   ├── three_d_shading_utils.py
│   │   ├── three_d_utils.py
│   │   ├── types
│   │   │   ├── image_mobject.py
│   │   │   ├── point_cloud_mobject.py
│   │   │   └── vectorized_mobject.py
│   │   ├── value_tracker.py
│   │   └── vector_field.py
│   ├── once_useful_constructs
│   │   ├── arithmetic.py
│   │   ├── combinatorics.py
│   │   ├── complex_transformation_scene.py
│   │   ├── counting.py
│   │   ├── fractals.py
│   │   ├── graph_theory.py
│   │   ├── light.py
│   │   ├── matrix_multiplication.py
│   │   ├── NOTE.md
│   │   └── region.py
│   ├── scene
│   │   ├── graph_scene.py
│   │   ├── moving_camera_scene.py
│   │   ├── reconfigurable_scene.py
│   │   ├── sample_space_scene.py
│   │   ├── scene_file_writer.py
│   │   ├── scene_from_video.py
│   │   ├── scene.py
│   │   ├── three_d_scene.py
│   │   ├── vector_space_scene.py
│   │   └── zoomed_scene.py
│   ├── stream_starter.py
│   ├── tex_template.tex
│   └── utils
│       ├── bezier.py
│       ├── color.py
│       ├── config_ops.py
│       ├── file_ops.py
│       ├── images.py
│       ├── iterables.py
│       ├── paths.py
│       ├── rate_functions.py
│       ├── simple_functions.py
│       ├── sounds.py
│       ├── space_ops.py
│       ├── strings.py
│       └── tex_file_writing.py
├── README.md
├── requirements.txt
├── Dockerfile
├── example_scenes.py
└── LICENSE

基于Theorem of Beethoven编写的 Doc 进行修改.

manim.py

编译启动文件.

stage_scenes.py

# TODO

example_scenes.py

示例文件.

manimlib.config.py

设定编译指令,如输出的分辨率、输出格式、帧率、路径、背景颜色等命令.

manimlib.stream_starter

Livestream模式的启动设置.

manimlib.extract_scene.py

场景解析文件,在编译过程中会使用到.

manimlib.imports.py

模块导入文件.如果你自主开发了一些功能,添加在库中,为了方便调用,你可以在这里文件里面添加相应的模块.

manimlib.media_dir.txt

媒体文件路径.正常情况下,媒体文件都会储存在你的终端运行的主目录下的 /media 中,你可以自定义修改.

manimlib.tex_template.tex

默认LaTex模板.

manimlib.ctex_template.tex

使用中文时的LaTex模板.

manimlib.constants.py

基本参数,将在基本参数中介绍.

基本参数

本节主要参考 Constants.

路径

当你第一次运行动画编译的时候,终端会出现这样的提示:

Media will be stored in media/. You can change this behavior by writing a different directory to media_dir.txt.

这是因为动画文件会被存放在在 media 路径中,在你终端运行的主目录下,会出现新的文件

.
├── manim.py
├── stage_scenes.py
├── media                  # <- 新目录
└── manimlib
    └── media_dir.txt      # <- 新文件
    └── scene
        └── media_dir.txt  # <- 新文件

如果你不更改媒体路径的话,后面不会再出现这个提示.可以通过更改 media_dir.txt 来更改媒体保存路径,这会导致 manimlib/constants.py14 行的改变.

 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
45
46
47
48
49
50
51
52
53
MEDIA_DIR = ""
VIDEO_DIR = ""
VIDEO_OUTPUT_DIR = ""
TEX_DIR = ""
TEXT_DIR = ""


def initialize_directories(config):
    global MEDIA_DIR
    global VIDEO_DIR
    global VIDEO_OUTPUT_DIR
    global TEX_DIR
    global TEXT_DIR

    video_path_specified = config["video_dir"] or config["video_output_dir"]

    if not (video_path_specified and config["tex_dir"]):
        if config["media_dir"]:
            MEDIA_DIR = config["media_dir"]
        else:
            MEDIA_DIR = os.path.join(
                os.path.expanduser('~'),
                "Dropbox (3Blue1Brown)/3Blue1Brown Team Folder"
            )
        if not os.path.isdir(MEDIA_DIR):
            MEDIA_DIR = "./media"
        print(
            f"Media will be written to {MEDIA_DIR + os.sep}. You can change "
            "this behavior with the --media_dir flag."
        )
    else:
        if config["media_dir"]:
            print(
                "Ignoring --media_dir, since both --tex_dir and a video "
                "directory were both passed"
            )

    TEX_DIR = config["tex_dir"] or os.path.join(MEDIA_DIR, "Tex")
    TEXT_DIR = os.path.join(MEDIA_DIR, "texts")
    if not video_path_specified:
        VIDEO_DIR = os.path.join(MEDIA_DIR, "videos")
        VIDEO_OUTPUT_DIR = os.path.join(MEDIA_DIR, "videos")
    elif config["video_output_dir"]:
        VIDEO_OUTPUT_DIR = config["video_output_dir"]
    else:
        VIDEO_DIR = config["video_dir"]

    for folder in [VIDEO_DIR, VIDEO_OUTPUT_DIR, TEX_DIR, TEXT_DIR]:
        if folder != "" and not os.path.exists(folder):
            os.makedirs(folder)

更多细节请参考 FAQ 2.7.

Text设置

Text 类的设置.

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
NOT_SETTING_FONT_MSG='''
Warning:
You haven't set font.
If you are not using English, this may cause text rendering problem.
You set font like:
text = Text('your text', font='your font')
or:
class MyText(Text):
    CONFIG = {
        'font': 'My Font'
    }
'''
START_X = 30
START_Y = 20
NORMAL = 'NORMAL'
ITALIC = 'ITALIC'
OBLIQUE = 'OBLIQUE'
BOLD = 'BOLD'

LaTex编译设置

74
75
76
77
78
79
80
81
82
83
84
85
TEX_USE_CTEX = True
TEX_TEXT_TO_REPLACE = "YourTextHere"
TEMPLATE_TEX_FILE = os.path.join(
    os.path.dirname(os.path.realpath(__file__)),
    "tex_template.tex" if not TEX_USE_CTEX else "ctex_template.tex"
)
with open(TEMPLATE_TEX_FILE, "r") as infile:
    TEMPLATE_TEXT_FILE_BODY = infile.read()
    TEMPLATE_TEX_FILE_BODY = TEMPLATE_TEXT_FILE_BODY.replace(
        TEX_TEXT_TO_REPLACE,
        "\\begin{align*}\n" + TEX_TEXT_TO_REPLACE + "\n\\end{align*}",
    )

帮助与报错信息

 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
HELP_MESSAGE = """
   Usage:
   python extract_scene.py <module> [<scene name>]
   -p preview in low quality
   -s show and save picture of last frame
   -w write result to file [this is default if nothing else is stated]
   -o <file_name> write to a different file_name
   -l use low quality
   -m use medium quality
   -a run and save every scene in the script, or all args for the given scene
   -q don't print progress
   -f when writing to a movie file, export the frames in png sequence
   -t use transperency when exporting images
   -n specify the number of the animation to start from
   -r specify a resolution
   -c specify a background color
"""
SCENE_NOT_FOUND_MESSAGE = """
   {} is not in the script
"""
CHOOSE_NUMBER_MESSAGE = """
Choose number corresponding to desired scene/arguments.
(Use comma separated list for multiple entries)
Choice(s): """
INVALID_NUMBER_MESSAGE = "Fine then, if you don't want to give a valid number I'll just quit"

NO_SCENE_MESSAGE = """
   There are no scenes inside that module
"""

分辨率

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# There might be other configuration than pixel shape later...
PRODUCTION_QUALITY_CAMERA_CONFIG = {
    "pixel_height": 1440,
    "pixel_width": 2560,
    "frame_rate": 60,
}

HIGH_QUALITY_CAMERA_CONFIG = {
    "pixel_height": 1080,
    "pixel_width": 1920,
    "frame_rate": 60,
}

MEDIUM_QUALITY_CAMERA_CONFIG = {
    "pixel_height": 720,
    "pixel_width": 1280,
    "frame_rate": 30,
}

LOW_QUALITY_CAMERA_CONFIG = {
    "pixel_height": 480,
    "pixel_width": 854,
    "frame_rate": 15,
}

画面尺寸

142
143
144
145
146
147
148
149
150
151
152
153
154
DEFAULT_PIXEL_HEIGHT = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_height"]
DEFAULT_PIXEL_WIDTH = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_width"]
DEFAULT_FRAME_RATE = 60

DEFAULT_POINT_DENSITY_2D = 25
DEFAULT_POINT_DENSITY_1D = 250

DEFAULT_STROKE_WIDTH = 4

FRAME_HEIGHT = 8.0
FRAME_WIDTH = FRAME_HEIGHT * DEFAULT_PIXEL_WIDTH / DEFAULT_PIXEL_HEIGHT
FRAME_Y_RADIUS = FRAME_HEIGHT / 2
FRAME_X_RADIUS = FRAME_WIDTH / 2

间距

156
157
158
159
160
161
162
SMALL_BUFF = 0.1
MED_SMALL_BUFF = 0.25
MED_LARGE_BUFF = 0.5
LARGE_BUFF = 1

DEFAULT_MOBJECT_TO_EDGE_BUFFER = MED_LARGE_BUFF
DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = MED_SMALL_BUFF

时间

165
166
167
# All in seconds
DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0
DEFAULT_WAIT_TIME = 1.0

向量

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
ORIGIN = np.array((0., 0., 0.))
UP = np.array((0., 1., 0.))
DOWN = np.array((0., -1., 0.))
RIGHT = np.array((1., 0., 0.))
LEFT = np.array((-1., 0., 0.))
IN = np.array((0., 0., -1.))
OUT = np.array((0., 0., 1.))
X_AXIS = np.array((1., 0., 0.))
Y_AXIS = np.array((0., 1., 0.))
Z_AXIS = np.array((0., 0., 1.))

# Useful abbreviations for diagonals
UL = UP + LEFT
UR = UP + RIGHT
DL = DOWN + LEFT
DR = DOWN + RIGHT

TOP = FRAME_Y_RADIUS * UP
BOTTOM = FRAME_Y_RADIUS * DOWN
LEFT_SIDE = FRAME_X_RADIUS * LEFT
RIGHT_SIDE = FRAME_X_RADIUS * RIGHT

角度

192
193
194
PI = np.pi
TAU = 2 * PI
DEGREES = TAU / 360

颜色

颜色盘展示

注解

默认 COLOR = COLOR_C

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# Colors
COLOR_MAP = {
    "DARK_BLUE": "#236B8E",
    "DARK_BROWN": "#8B4513",
    "LIGHT_BROWN": "#CD853F",
    "BLUE_E": "#1C758A",
    "BLUE_D": "#29ABCA",
    "BLUE_C": "#58C4DD",
    "BLUE_B": "#9CDCEB",
    "BLUE_A": "#C7E9F1",
    "TEAL_E": "#49A88F",
    "TEAL_D": "#55C1A7",
    "TEAL_C": "#5CD0B3",
    "TEAL_B": "#76DDC0",
    "TEAL_A": "#ACEAD7",
    "GREEN_E": "#699C52",
    "GREEN_D": "#77B05D",
    "GREEN_C": "#83C167",
    "GREEN_B": "#A6CF8C",
    "GREEN_A": "#C9E2AE",
    "YELLOW_E": "#E8C11C",
    "YELLOW_D": "#F4D345",
    "YELLOW_C": "#FFFF00",
    "YELLOW_B": "#FFEA94",
    "YELLOW_A": "#FFF1B6",
    "GOLD_E": "#C78D46",
    "GOLD_D": "#E1A158",
    "GOLD_C": "#F0AC5F",
    "GOLD_B": "#F9B775",
    "GOLD_A": "#F7C797",
    "RED_E": "#CF5044",
    "RED_D": "#E65A4C",
    "RED_C": "#FC6255",
    "RED_B": "#FF8080",
    "RED_A": "#F7A1A3",
    "MAROON_E": "#94424F",
    "MAROON_D": "#A24D61",
    "MAROON_C": "#C55F73",
    "MAROON_B": "#EC92AB",
    "MAROON_A": "#ECABC1",
    "PURPLE_E": "#644172",
    "PURPLE_D": "#715582",
    "PURPLE_C": "#9A72AC",
    "PURPLE_B": "#B189C6",
    "PURPLE_A": "#CAA3E8",
    "WHITE": "#FFFFFF",
    "BLACK": "#000000",
    "LIGHT_GRAY": "#BBBBBB",
    "LIGHT_GREY": "#BBBBBB",
    "GRAY": "#888888",
    "GREY": "#888888",
    "DARK_GREY": "#444444",
    "DARK_GRAY": "#444444",
    "DARKER_GREY": "#222222",
    "DARKER_GRAY": "#222222",
    "GREY_BROWN": "#736357",
    "PINK": "#D147BD",
    "LIGHT_PINK": "#DC75CD",
    "GREEN_SCREEN": "#00FF00",
    "ORANGE": "#FF862F",
}
PALETTE = list(COLOR_MAP.values())
locals().update(COLOR_MAP)
for name in [s for s in list(COLOR_MAP.keys()) if s.endswith("_C")]:
    locals()[name.replace("_C", "")] = locals()[name]

Mobject

class manimlib.mobject.mobject.Mobject(**kwargs)

数学对象

align_on_border(direction, buff=0.5)

Direction just needs to be a vector pointing towards side or corner in the 2d plane.

align_to(mobject_or_point, direction=array([0.0, 0.0, 0.0]), alignment_vect=array([0.0, 1.0, 0.0]))

Examples: mob1.align_to(mob2, UP) moves mob1 vertically so that its top edge lines ups with mob2's top edge.

mob1.align_to(mob2, alignment_vect = RIGHT) moves mob1 horizontally so that it's center is directly above/below the center of mob2

become(mobject, copy_submobjects=True)

Edit points, colors and submobjects to be idential to another mobject

become_partial(mobject, a, b)

Set points in such a way as to become only part of mobject. Inputs 0 <= a < b <= 1 determine what portion of mobject to become.

digest_mobject_attrs()

Ensures all attributes which are mobjects are included in the submobjects list.

get_coord(dim, direction=array([0.0, 0.0, 0.0]))

Meant to generalize get_x, get_y, get_z

get_critical_point(direction)

Picture a box bounding the mobject. Such a box has 9 'critical points': 4 corners, 4 edge center, the center. This returns one of them.

get_point_mobject(center=None)

The simplest mobject to be transformed to or from self. Should by a point of the appropriate type

interpolate(mobject1, mobject2, alpha, path_func=<function straight_path>)

Turns self into an interpolation between mobject1 and mobject2.

null_point_align(mobject)

If a mobject with points is being aligned to one without, treat both as groups, and push the one with points into its own submobjects list.

repeat(count)

This can make transition animations nicer

scale(scale_factor, **kwargs)

Default behavior is to scale about the center of the mobject. The argument about_edge can be a vector, indicating which side of the mobject to scale about, e.g., mob.scale(about_edge = RIGHT) scales about mob.get_right().

Otherwise, if about_point is given a value, scaling is done with respect to that point.

set_color(color='#FFFF00', family=True)

Condition is function which takes in one arguments, (x, y, z). Here it just recurses to submobjects, but in subclasses this should be further implemented based on the the inner workings of color

class manimlib.mobject.mobject.Group(*mobjects, **kwargs)