Manim 是3b1b開源的一個(gè)特別漂亮的數(shù)學(xué)動(dòng)畫模塊。
我們能夠基于Manim繪制許多解釋性的動(dòng)畫,比如下面這個(gè):

也支持函數(shù)圖像:

甚至是一些3D視圖和矩陣變換,Manim都可以輕易實(shí)現(xiàn):


如果你是一個(gè)數(shù)學(xué)課程的演講者,或者你需要給觀眾演示某些數(shù)學(xué)公式的圖形,那么Manim就是你的不二之選。
Manim 支持 Python 3.7 及以上版本,推薦Python3.8.
1.準(zhǔn)備
開始之前,你要確保Python和pip已經(jīng)成功安裝在電腦上,如果沒有,可以訪問這篇文章:超詳細(xì)Python安裝指南 進(jìn)行安裝。
(可選1) 如果你用Python的目的是數(shù)據(jù)分析,可以直接安裝Anaconda:Python數(shù)據(jù)分析與挖掘好幫手—Anaconda,它內(nèi)置了Python和pip.
(可選2) 此外,推薦大家用VSCode編輯器,它有許多的優(yōu)點(diǎn):Python 編程的最好搭檔—VSCode 詳細(xì)指南。
請(qǐng)選擇以下任一種方式輸入命令安裝依賴:1. Windows 環(huán)境 打開 Cmd (開始-運(yùn)行-CMD)。2. MacOS 環(huán)境 打開 Terminal (command+空格輸入Terminal)。3. 如果你用的是 VSCode編輯器 或 Pycharm,可以直接使用界面下方的Terminal.
conda create --name manim pythnotallow=3.8 # 創(chuàng)建虛擬環(huán)境
conda activate manim # 切換到此虛擬環(huán)境
pip install manimgl # 安裝manim
安裝完畢后在終端輸入 manimgl,會(huì)出現(xiàn)如下的界面,說明安裝完成。

2. Manim 基本使用
首先學(xué)會(huì)畫一個(gè)基本的數(shù)學(xué)圖形,如圓圈:
from manimlib import *
class SquareToCircle(Scene):
def construct(self):
circle = Circle()
circle.set_fill(BLUE, opacity=0.5)
circle.set_stroke(BLUE_E, width=4)
self.add(circle)
編寫完畢后,在終端里敲下這行命令:
manimgl 你的py文件名.py SquareToCircle
就能彈出一個(gè)圖形界面,繪制完成:

你還可以操作彈出的這個(gè)窗口:
- 滾動(dòng)鼠標(biāo)中鍵來上下移動(dòng)畫面
- 按住鍵盤上 z 鍵的同時(shí)滾動(dòng)鼠標(biāo)中鍵來縮放畫面
- 按住鍵盤上 f 鍵的同時(shí)移動(dòng)鼠標(biāo)來平移畫面
- 按住鍵盤上 d 鍵的同時(shí)移動(dòng)鼠標(biāo)來改變?nèi)S視角
- 按下鍵盤上 r 鍵恢復(fù)到最初的視角
最后,你可以通過按 q來關(guān)閉窗口并退出程序。
接下來,我們學(xué)習(xí)如何讓圓形變成方形:
# 公眾號(hào): Python實(shí)用寶典
from manimlib import *
class CircleToSquare(Scene):
def construct(self):
square = Square()
square.set_fill(BLUE, opacity=0.5)
square.set_stroke(BLUE_E, width=4)
circle = Circle()
self.play(ShowCreation(circle))
self.wait()
self.play(ReplacementTransform(circle, square))
self.wait()
ShowCreation: 演示圓圈繪制過程。
ReplacementTransform: 延時(shí)從第一個(gè)參數(shù)的圖形變化到第二個(gè)參數(shù)的圖形的過程。
self.wait(): 等待上個(gè)play操作執(zhí)行完成。
終端運(yùn)行命令:
manimgl 你的py文件名.py CircleToSquare
效果如下:

再來一個(gè)復(fù)雜一點(diǎn)的演示,增加拉伸、旋轉(zhuǎn)和變換:
# 公眾號(hào): Python實(shí)用寶典
from manimlib import *
class CircleToSquare(Scene):
def construct(self):
square = Square()
square.set_fill(BLUE, opacity=0.5)
square.set_stroke(BLUE_E, width=4)
circle = Circle()
self.play(ShowCreation(circle))
self.wait()
self.play(ReplacementTransform(circle, square))
self.wait()
# 在水平方向上拉伸到四倍
self.play(square.animate.stretch(4, dim=0))
self.wait()
# 旋轉(zhuǎn)90°
self.play(Rotate(square, TAU / 4))
self.wait()
# 在向右移動(dòng)2單位同時(shí)縮小為原來的1/4
self.play(square.animate.shift(2 * RIGHT), square.animate.scale(0.25))
self.wait()
# 為了非線性變換,給square增加10段曲線(不會(huì)播放動(dòng)畫)
square.insert_n_curves(10)
# 給square上的所有點(diǎn)施加f(z)=z^2的復(fù)變換
self.play(square.animate.apply_complex_function(lambda z: z**2))
self.wait()
square.animate.stretch: 將圖形拉伸第一個(gè)參數(shù)的倍數(shù),第二個(gè)維度指明方向,dim=0為水平方向,dim=1為垂直方向。
square.animate.shift: 可以調(diào)整圖形位置和大小。
square.animate.apply_complex_function: 增加函數(shù)復(fù)變換。
效果如下:

3. Manim 坐標(biāo)軸與函數(shù)圖像
想要實(shí)現(xiàn)函數(shù)圖像繪制,我們需要先添加坐標(biāo)軸:
# 公眾號(hào): Python實(shí)用寶典
from manimlib import *
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))
運(yùn)行以下命令顯示坐標(biāo)軸:
manimgl 你的py文件名.py GraphExample

坐標(biāo)軸繪制完成后,就可以開始繪制圖像了:
上滑查看更多代碼:
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會(huì)返回傳入方程的圖像
sin_graph = axes.get_graph(
lambda x: 2 * math.sin(x),
color=BLUE,
)
# 默認(rèn)情況下,它在所有采樣點(diǎn)(x, f(x))之間稍微平滑地插值
# 但是,如果圖形有棱角,可以將use_smoothing設(shè)為False
relu_graph = axes.get_graph(
lambda x: max(x, 0),
use_smoothing=False,
color=YELLOW,
)
# 對(duì)于不連續(xù)的函數(shù),你可以指定間斷點(diǎn)來讓它不試圖填補(bǔ)不連續(xù)的位置
step_graph = axes.get_graph(
lambda x: 2.0 if x > 3 else 1.0,
discnotallow=[3],
color=GREEN,
)
# Axes.get_graph_label可以接受字符串或者mobject。如果傳入的是字符串
# 那么將將其當(dāng)作LaTeX表達(dá)式傳入Tex中
# 默認(rèn)下,label將生成在圖像的右側(cè),并且匹配圖像的顏色
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()
# 你可以使用Axes.input_to_graph_point(縮寫Axes.i2gp)來找到圖像上的一個(gè)點(diǎn)
dot = Dot(color=RED)
dot.move_to(axes.i2gp(2, parabola))
self.play(FadeIn(dot, scale=0.5))
# ValueTracker存儲(chǔ)一個(gè)數(shù)值,可以幫助我們制作可變參數(shù)的動(dòng)畫
# 通常使用updater或者f_always讓其它mobject根據(jù)其中的數(shù)值來更新
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()
如果在運(yùn)行的時(shí)候你出現(xiàn)了這樣的錯(cuò)誤:

請(qǐng)下載安裝MiKTex和dvisvgm.
MiKTex: https://miktex.org/download
Dvisvgm: https://dvisvgm.de/Downloads/
還有更多有趣的繪制案例,你可以在Manim官網(wǎng)上學(xué)習(xí):
??https://docs.manim.org.cn/getting_started/example_scenes.html??
