之前刷了MIT Gilbert Strang
老爺子的線性代數公開課,覺得挺牛逼,然后想起以前看了但是沒咋看懂的3Blue1Brown
的線性代數本質,決定再刷一遍,然后直接被里面的動畫圈粉。后來發現3B1B
把這個視頻框架開源了,決定學學。
傳送門: github - 3b1b/manim
介紹&安裝
視頻框架采用Python
提供接口供開發者使用,使用Latex語言編寫數學公式,哈哈哈這簡直就是代碼狗的福音。
學了一點發現這個框架的API設計相當簡潔,采用自頂向下的模式,非常適合數學演示。
關於安裝,這個鏈接演示了多個平台的詳細安裝過程,不過沒有Windows的,Windows看官方倉庫的README吧。
快速開始
在快速開始中通過一個最簡單的例子了解manim引擎的運行模式,了解動畫對象
創建一個文件exm1.py
from manimlib.imports import *
class WriteText(Scene):
def construct(self):
text = TextMobject("This is a regular text")
self.play(Write(text))
self.wait(3)
上面的代碼是一個快速開始的示例,我們先看效果再講代碼
manim exm1.py WriteText -pl
然后等待,就會彈出視頻了,視頻中,一行This is a regular text
一點一點的被從屏幕中間寫出來。
from manimlib.imports import *
這不用解釋,導包
class WriteText(Scene):
manim框架中的一個動畫就是一個類,繼承自Scene
,這個類的類名就是這個動畫的名字
def construct(self):
manim框架的每一個動畫有一個construct
方法,你要編寫的動畫邏輯就在這里
text = TextMobject("This is a regular text")
self.play(Write(text))
self.wait(3)
第一行創建一個文本對象,並編寫其中的文字,然后調用play方法播放動畫,但是光有TextMobject
是不行的,play需要的是一個動畫,所以Write
就是接受一個文本對象並輸出手寫該文本對象的動畫。
第三行,wait等待3秒,至此全部邏輯走完,動畫結束。
追蹤Write
方法的源碼,分析下
# 是一個類,繼承自DrawBorderThenFill
# 可以推斷出父類也是個動畫,代表先繪制出輪廓然后填充的效果
class Write(DrawBorderThenFill):
# 一些動畫配置,這里只定義了數據結構,並沒有對值初始化
CONFIG = {
"run_time": None,
"lag_ratio": None,
"rate_func": linear,
}
# __init__,Python中當對象初始化被調用的魔法方法,可以看作構造方法但還是有點區別
def __init__(self, mobject, **kwargs):
# 從名字推測是從創建對象時傳遞的參數中取出配置信息並且設置的,所以我們應該可以在構造該動畫時傳遞參數
# 並且從方法名中的digest可以推測,傳入的參數會和`CONFIG`里的默認參數融合,也就是用戶傳入了就用用戶的,否則用默認的
# 而CONFIG就相當於給開發者一個該動畫有什么屬性的提示
# 后來我跟蹤了這個方法的代碼,確實是我們推測的情況
digest_config(self, kwargs)
# 看起來是根據傳入的對象長度來設置默認配置,看看這個方法里寫啥了
self.set_default_config_from_length(mobject)
super().__init__(mobject, **kwargs)
# 這就是上面那個默認配置的方法,就是如果用戶沒設置參數,參數還是初始值的話就根據傳入對象的某個方法返回的長度設置
def set_default_config_from_length(self, mobject):
length = len(mobject.family_members_with_points())
if self.run_time is None:
if length < 15:
self.run_time = 1
else:
self.run_time = 2
if self.lag_ratio is None:
self.lag_ratio = min(4.0 / length, 0.2)
上面是Write
動畫的全部源碼,我們從中可以知道,manim中動畫也是一個類,它有一些配置,並且可以在構造時直接傳入,每個動畫可選的配置可以在動畫源碼的CONFIG
屬性中查看,我們設置一下Write
的參數
self.play(Write(text,run_time=10))
動畫頓時長了不少,手寫的速度也慢了,所以這個參數是控制動畫持續時間的。
除了Write
外,還有很多動畫可以選擇,manim源碼的
animation包里定義了很多現成的動畫效果。
介紹幾個常用的:
- FadeIn 淡入
- FadeOut 淡出
- Write 手寫文字
- GrowFromCenter 從中間彈出
下面說說命令行
manim exm1.py WriteText -pl
當執行這個命令時,manim會編譯當前的動畫並彈出預覽,exm1.py
就是腳本名,WriteText
是要播放的動畫,所以一個文件是可以包含多個動畫的,-p
則是預覽,-l
是低畫質,對應的還有-m
中畫質和--high_quality
高畫質(因為-h
被幫助占用所以沒有簡寫)
從Mobject說起
Mobject是manim中定義的對象,你可以看作是一個物體,常見的Mobject有Shape、Text和數學公式等
Shapes
Shape是Mobject子類,代表一個形狀
- Square 方形
- Circle 圓形
- Rectangle 矩形
- Lines 線
- Annulus 環
- Dot 點
等等...mobject/geometry.py中可以查看更多形狀
from manimlib.imports import *
class Shapes(Scene):
def construct(self):
# 創建一個圓形 沒有傳參 使用的都是默認參數
circle = Circle(color=YELLOW)
# 添加到屏幕中
self.add(circle)
self.wait(2)
運行發現是一個黃色的圓環被放在屏幕中間,我們去看看圓的定義
# 父類是Arc(弧形)
class Circle(Arc):
CONFIG = {
"color": RED,
"close_new_points": True,
"anchors_span_full_range": False
}
def __init__(self, **kwargs):
# 調用了弧形的構造器,傳入了0和TAU
# 如果你看Arc類的__init__可以知道傳入的0被設置給起始角度,TAU被設置給結束角度,
# 圓形的結束角度平直覺應該是360,而在manim的contacts.py中也確實是這樣定義的
Arc.__init__(self, 0, TAU, **kwargs)
# ... some method
誒??我們沒看到這里面有關於位置的信息哈,那它咋就被放到屏幕中間了?思考了一下,這種屬性應該放在最底層的類里,通過追蹤代碼,我們在Mobject
類中找到了相應代碼
def reset_points(self):
self.points = np.zeros((0, self.dim))
mainm中所有的物體都繼承自Mobject,而Mobject在初始化時會調用這個方法,可以看到它是把自己放在空間中的原點上了,所以在屏幕中心,由此我們還能知道的信息是mainm使用的坐標系,后面會詳細介紹
我們也可以像上面快速開始示例一樣用動畫來展示形狀
from manimlib.imports import *
class Shapes(Scene):
def construct(self):
circle = Circle(color=YELLOW)
square = Square(color=DARK_BLUE)
# surround方法讓當前圖形包裹另一個形狀
square.surround(circle)
self.add(circle)
self.play(FadeIn(square))
self.wait(2)
Text
在快速開始里我們都見過了,就沒啥好介紹的,我們把官方示例貼出來就好了
from manimlib.imports import *
class makeText(Scene):
def construct(self):
#######Code#######
#Making text
first_line = TextMobject("Manim is fun")
second_line = TextMobject("and useful")
final_line = TextMobject("Hope you like it too!", color=BLUE)
color_final_line = TextMobject("Hope you like it too!")
#Coloring
color_final_line.set_color_by_gradient(BLUE,PURPLE)
#Position text
second_line.next_to(first_line, DOWN)
#Showing text
self.wait(1)
self.play(Write(first_line), Write(second_line))
self.wait(1)
self.play(FadeOut(second_line), ReplacementTransform(first_line, final_line))
self.wait(1)
self.play(Transform(final_line, color_final_line))
self.wait(2)
Math Equations
from manimlib.imports import *
class MathEq(Scene):
def construct(self):
eq = TextMobject(r'$$\begin{bmatrix}1&2&3\\4&5&6\end{bmatrix}$$')
self.add(eq)
self.wait(3)
實際上數學公式用的也是TextMobject
,也就是說TextMobject是支持Latex語言的,只不過需要轉義或者用r'xxx'
的原生字符串形式。