淺談manim-3b1b的數學視頻動畫引擎


之前刷了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'的原生字符串形式。

未完待續...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM