渲染文本和公式

有两种不同的方式可以在视频中渲染文本

  1. 使用Pango (text_mobject)

  2. 使用 LaTeX (tex_mobject)

如果你想渲染简单的文本,你应该使用文本MarkupText,或者它的一个衍生版本,如段落。 更多信息请参见没有LaTeX的文本

当你需要数学排版时,应使用LaTeX。有关更多信息,请参见 带有LaTeX的文本

无LaTeX文本

The simplest way to add text to your animations is to use the 文本 class. It uses the Pango library to render text. With Pango, you can also render non-English alphabets like 你好 or こんにちは or 안녕하세요 or مرحبا بالعالم.

这是一个简单的Hello World动画。

示例:HelloWorld

../_images/HelloWorld-1.png
from manim import *

class HelloWorld(Scene):
    def construct(self):
        text = Text("Hello world", font_size=144)
        self.add(text)
class HelloWorld(Scene):
    def construct(self):
        text = Text("Hello world", font_size=144)
        self.add(text)

参考文献: 文本

你也可以使用MarkupText,它允许使用PangoMarkup (详情请参阅MarkupText的文档)来渲染文本。 例如:

示例:单行颜色

../_images/SingleLineColor-1.png
from manim import *

class SingleLineColor(Scene):
    def construct(self):
        text = MarkupText(
            f'all in red <span fgcolor="{YELLOW}">except this</span>', color=RED
        )
        self.add(text)
class SingleLineColor(Scene):
    def construct(self):
        text = MarkupText(
            f'all in red except this', color=RED
        )
        self.add(text)

参考文献: MarkupText

使用Text

本节解释了文本的属性以及如何在您的动画中使用它。

使用字体

你可以使用font来设置不同的字体。

注意

使用的字体必须安装在您的系统中,并且Pango应该知道它。您可以使用manimpango.list_fonts()获取字体列表。

>>> import manimpango
>>> manimpango.list_fonts()
[...]

示例:FontsExample

../_images/FontsExample-1.png
from manim import *

class FontsExample(Scene):
    def construct(self):
        ft = Text("Noto Sans", font="Noto Sans")
        self.add(ft)
class FontsExample(Scene):
    def construct(self):
        ft = Text("Noto Sans", font="Noto Sans")
        self.add(ft)

设置斜体和粗细

倾斜是文本的样式,它可以是NORMAL(默认值), ITALICOBLIQUE。通常,对于许多字体,ITALICOBLIQUE看起来相似,但ITALIC使用罗马风格,而 OBLIQUE使用斜体风格

Weight 指定字体的粗细。你可以在 manimpango.Weight 中看到一系列粗细值。

示例:SlantsExample

../_images/SlantsExample-1.png
from manim import *

class SlantsExample(Scene):
    def construct(self):
        a = Text("Italic", slant=ITALIC)
        self.add(a)
class SlantsExample(Scene):
    def construct(self):
        a = Text("Italic", slant=ITALIC)
        self.add(a)

示例:DifferentWeight

../_images/DifferentWeight-1.png
from manim import *

class DifferentWeight(Scene):
    def construct(self):
        import manimpango

        g = VGroup()
        weight_list = dict(
            sorted(
                {
                    weight: manimpango.Weight(weight).value
                    for weight in manimpango.Weight
                }.items(),
                key=lambda x: x[1],
            )
        )
        for weight in weight_list:
            g += Text(weight.name, weight=weight.name, font="Open Sans")
        self.add(g.arrange(DOWN).scale(0.5))
class DifferentWeight(Scene):
    def construct(self):
        import manimpango

        g = VGroup()
        weight_list = dict(
            sorted(
                {
                    weight: manimpango.Weight(weight).value
                    for weight in manimpango.Weight
                }.items(),
                key=lambda x: x[1],
            )
        )
        for weight in weight_list:
            g += Text(weight.name, weight=weight.name, font="Open Sans")
        self.add(g.arrange(DOWN).scale(0.5))

使用颜色

你可以使用color来设置文本的颜色:

示例:SimpleColor

../_images/SimpleColor-1.png
from manim import *

class SimpleColor(Scene):
    def construct(self):
        col = Text("RED COLOR", color=RED)
        self.add(col)
class SimpleColor(Scene):
    def construct(self):
        col = Text("RED COLOR", color=RED)
        self.add(col)

你可以使用像t2c这样的工具来为特定字符着色。 如果你的文本包含连字,这可能会出现问题, 如迭代文本中所述。

t2c 接受两种类型的字典,

  • 键可以包含像 [2:-1][4:8] 这样的索引, 这与 切片 在 Python 中的工作方式类似。值应该是来自 Color 的文本颜色。

  • 键包含应单独着色的单词或字符,值应为来自Color的颜色:

示例:Textt2c示例

../_images/Textt2cExample-1.png
from manim import *

class Textt2cExample(Scene):
    def construct(self):
        t2cindices = Text('Hello', t2c={'[1:-1]': BLUE}).move_to(LEFT)
        t2cwords = Text('World',t2c={'rl':RED}).next_to(t2cindices, RIGHT)
        self.add(t2cindices, t2cwords)
class Textt2cExample(Scene):
    def construct(self):
        t2cindices = Text('Hello', t2c={'[1:-1]': BLUE}).move_to(LEFT)
        t2cwords = Text('World',t2c={'rl':RED}).next_to(t2cindices, RIGHT)
        self.add(t2cindices, t2cwords)

如果你想避免使用颜色时出现问题(由于连字),考虑使用 MarkupText

使用渐变

你可以使用gradient来添加渐变。该值必须是任意长度的可迭代对象:

示例:GradientExample

../_images/GradientExample-1.png
from manim import *

class GradientExample(Scene):
    def construct(self):
        t = Text("Hello", gradient=(RED, BLUE, GREEN), font_size=96)
        self.add(t)
class GradientExample(Scene):
    def construct(self):
        t = Text("Hello", gradient=(RED, BLUE, GREEN), font_size=96)
        self.add(t)

你也可以使用 t2g 来为文本的特定字符创建渐变。它与 颜色接口 的语法类似:

示例:t2gExample

../_images/t2gExample-1.png
from manim import *

class t2gExample(Scene):
    def construct(self):
        t2gindices = Text(
            'Hello',
            t2g={
                '[1:-1]': (RED,GREEN),
            },
        ).move_to(LEFT)
        t2gwords = Text(
            'World',
            t2g={
                'World':(RED,BLUE),
            },
        ).next_to(t2gindices, RIGHT)
        self.add(t2gindices, t2gwords)
class t2gExample(Scene):
    def construct(self):
        t2gindices = Text(
            'Hello',
            t2g={
                '[1:-1]': (RED,GREEN),
            },
        ).move_to(LEFT)
        t2gwords = Text(
            'World',
            t2g={
                'World':(RED,BLUE),
            },
        ).next_to(t2gindices, RIGHT)
        self.add(t2gindices, t2gwords)

设置行间距

你可以使用line_spacing来设置行间距:

示例:行间距

../_images/LineSpacing-1.png
from manim import *

class LineSpacing(Scene):
    def construct(self):
        a = Text("Hello\nWorld", line_spacing=1)
        b = Text("Hello\nWorld", line_spacing=4)
        self.add(Group(a,b).arrange(LEFT, buff=5))
class LineSpacing(Scene):
    def construct(self):
        a = Text("Hello\nWorld", line_spacing=1)
        b = Text("Hello\nWorld", line_spacing=4)
        self.add(Group(a,b).arrange(LEFT, buff=5))

禁用连字

通过禁用连字,您将获得字符和子对象之间的一对一映射。这解决了文本着色的问题。

警告

请注意,使用此方法处理严重依赖连字(阿拉伯文本)的文本可能会产生意外结果。

你可以通过将disable_ligatures传递给Text来禁用连字。例如:

示例:DisableLigature

../_images/DisableLigature-1.png
from manim import *

class DisableLigature(Scene):
    def construct(self):
        li = Text("fl ligature",font_size=96)
        nli = Text("fl ligature", disable_ligatures=True, font_size=96)
        self.add(Group(li, nli).arrange(DOWN, buff=.8))
class DisableLigature(Scene):
    def construct(self):
        li = Text("fl ligature",font_size=96)
        nli = Text("fl ligature", disable_ligatures=True, font_size=96)
        self.add(Group(li, nli).arrange(DOWN, buff=.8))

迭代 Text

文本对象的行为类似于VGroups。因此,您可以对文本进行切片和索引。

例如,您可以通过迭代为每个字母设置不同的颜色。

示例:IterateColor

../_images/IterateColor-1.png
from manim import *

class IterateColor(Scene):
    def construct(self):
        text = Text("Colors", font_size=96)
        for letter in text:
            letter.set_color(random_bright_color())
        self.add(text)
class IterateColor(Scene):
    def construct(self):
        text = Text("Colors", font_size=96)
        for letter in text:
            letter.set_color(random_bright_color())
        self.add(text)

警告

请注意,Ligature 可能会导致问题。如果您需要字符与子对象之间的一对一映射,您应该将 disable_ligatures 参数传递给 文本。请参阅 禁用连字

使用 MarkupText

MarkupText 类似于 文本,它们之间的唯一区别是 这个接受并处理 PangoMarkup(类似于 html),而不仅仅是渲染纯文本。

请查阅MarkupText的文档以获取更多关于PangoMarkup的详细信息和进一步参考。

示例:MarkupTest

../_images/MarkupTest-1.png
from manim import *

class MarkupTest(Scene):
    def construct(self):
        text = MarkupText(
            f'<span underline="double" underline_color="green">double green underline</span> in red text<span fgcolor="{YELLOW}"> except this</span>',
            color=RED,
            font_size=34
        )
        self.add(text)
class MarkupTest(Scene):
    def construct(self):
        text = MarkupText(
            f'double green underline in red text except this',
            color=RED,
            font_size=34
        )
        self.add(text)

带有LaTeX的文本

正如你可以使用文本来为你的视频添加文本一样,你可以使用Tex来插入LaTeX。

例如,

示例: HelloLaTeX

../_images/HelloLaTeX-1.png
from manim import *

class HelloLaTeX(Scene):
    def construct(self):
        tex = Tex(r"\LaTeX", font_size=144)
        self.add(tex)
class HelloLaTeX(Scene):
    def construct(self):
        tex = Tex(r"\LaTeX", font_size=144)
        self.add(tex)

注意

请注意,我们使用的是原始字符串(r'...')而不是普通字符串('...')。 这是因为TeX代码使用了许多特殊字符——例如\——这些字符在普通的Python字符串中具有特殊含义。另一种方法是 使用\\来转义反斜杠:Tex('\\LaTeX')

使用 MathTex

传递给MathTex的所有内容默认都在数学模式下。更准确地说, MathTex是在align*环境中处理的。你可以通过将公式用$符号括起来,使用Tex实现类似的效果: $\xrightarrow{x^6y^8}$

示例:MathTeXDemo

../_images/MathTeXDemo-1.png
from manim import *

class MathTeXDemo(Scene):
    def construct(self):
        rtarrow0 = MathTex(r"\xrightarrow{x^6y^8}", font_size=96)
        rtarrow1 = Tex(r"$\xrightarrow{x^6y^8}$", font_size=96)

        self.add(VGroup(rtarrow0, rtarrow1).arrange(DOWN))
class MathTeXDemo(Scene):
    def construct(self):
        rtarrow0 = MathTex(r"\xrightarrow{x^6y^8}", font_size=96)
        rtarrow1 = Tex(r"$\xrightarrow{x^6y^8}$", font_size=96)

        self.add(VGroup(rtarrow0, rtarrow1).arrange(DOWN))

LaTeX 命令和关键字参数

我们可以在AMS数学包中使用任何标准的LaTeX命令。例如mathtt数学文本类型或looparrowright箭头。

示例:AMSLaTeX

../_images/AMSLaTeX-1.png
from manim import *

class AMSLaTeX(Scene):
    def construct(self):
        tex = Tex(r'$\mathtt{H} \looparrowright$ \LaTeX', font_size=144)
        self.add(tex)
class AMSLaTeX(Scene):
    def construct(self):
        tex = Tex(r'$\mathtt{H} \looparrowright$ \LaTeX', font_size=144)
        self.add(tex)

在Manim方面,Tex类也接受属性来改变输出的外观。这与文本类非常相似。例如,color关键字可以改变TeX mobject的颜色。

示例:LaTeXAttributes

../_images/LaTeXAttributes-1.png
from manim import *

class LaTeXAttributes(Scene):
    def construct(self):
        tex = Tex(r'Hello \LaTeX', color=BLUE, font_size=144)
        self.add(tex)
class LaTeXAttributes(Scene):
    def construct(self):
        tex = Tex(r'Hello \LaTeX', color=BLUE, font_size=144)
        self.add(tex)

额外的 LaTeX 包

一些命令需要将特殊的包加载到TeX模板中。 例如,要使用mathscr脚本,我们需要添加mathrsfs 包。由于这个包默认没有加载到Manim的tex模板中, 我们必须手动添加它。

示例:AddPackageLatex

../_images/AddPackageLatex-1.png
from manim import *

class AddPackageLatex(Scene):
    def construct(self):
        myTemplate = TexTemplate()
        myTemplate.add_to_preamble(r"\usepackage{mathrsfs}")
        tex = Tex(
            r"$\mathscr{H} \rightarrow \mathbb{H}$",
            tex_template=myTemplate,
            font_size=144,
        )
        self.add(tex)
class AddPackageLatex(Scene):
    def construct(self):
        myTemplate = TexTemplate()
        myTemplate.add_to_preamble(r"\usepackage{mathrsfs}")
        tex = Tex(
            r"$\mathscr{H} \rightarrow \mathbb{H}$",
            tex_template=myTemplate,
            font_size=144,
        )
        self.add(tex)

子字符串和部分

TeX mobject 可以接受多个字符串作为参数。之后,您可以通过它们的索引(如 tex[1])或选择 tex 代码的部分来引用各个部分。在这个例子中,我们使用 set_color_by_tex() 来设置 \bigstar 的颜色:

示例:LaTeX子字符串

../_images/LaTeXSubstrings-1.png
from manim import *

class LaTeXSubstrings(Scene):
    def construct(self):
        tex = Tex('Hello', r'$\bigstar$', r'\LaTeX', font_size=144)
        tex.set_color_by_tex('igsta', RED)
        self.add(tex)
class LaTeXSubstrings(Scene):
    def construct(self):
        tex = Tex('Hello', r'$\bigstar$', r'\LaTeX', font_size=144)
        tex.set_color_by_tex('igsta', RED)
        self.add(tex)

请注意,set_color_by_tex() 会为包含 Tex 的整个子字符串着色,而不仅仅是特定的符号或 Tex 表达式。考虑以下示例:

示例:IncorrectLaTeXSubstringColoring

../_images/IncorrectLaTeXSubstringColoring-1.png
from manim import *

class IncorrectLaTeXSubstringColoring(Scene):
    def construct(self):
        equation = MathTex(
            r"e^x = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots"
        )
        equation.set_color_by_tex("x", YELLOW)
        self.add(equation)
class IncorrectLaTeXSubstringColoring(Scene):
    def construct(self):
        equation = MathTex(
            r"e^x = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots"
        )
        equation.set_color_by_tex("x", YELLOW)
        self.add(equation)

如你所见,这会将整个方程着色为黄色,与预期可能相反。要将仅x着色为黄色,我们必须执行以下操作:

示例:CorrectLaTeXSubstringColoring

../_images/CorrectLaTeXSubstringColoring-1.png
from manim import *

class CorrectLaTeXSubstringColoring(Scene):
    def construct(self):
        equation = MathTex(
            r"e^x = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots",
            substrings_to_isolate="x"
        )
        equation.set_color_by_tex("x", YELLOW)
        self.add(equation)
class CorrectLaTeXSubstringColoring(Scene):
    def construct(self):
        equation = MathTex(
            r"e^x = x^0 + x^1 + \frac{1}{2} x^2 + \frac{1}{6} x^3 + \cdots + \frac{1}{n!} x^n + \cdots",
            substrings_to_isolate="x"
        )
        equation.set_color_by_tex("x", YELLOW)
        self.add(equation)

通过将substrings_to_isolate设置为x,我们自动将 MathTex分割成子字符串,并将x组件 隔离为单独的子字符串。只有这样,才能使用set_color_by_tex() 来实现预期的结果。

请注意,Manim 还支持一种自定义语法,可以轻松地将 TeX 字符串拆分为子字符串:只需用双大括号将您想要隔离的公式部分括起来。在字符串 MathTex(r"{{ a^2 }} + {{ b^2 }} = {{ c^2 }}") 中,渲染的 mobject 将由子字符串 a^2+b^2=c^2 组成。这使得使用 TransformMatchingTex 编写相似文本片段之间的转换变得容易。

使用index_labels处理复杂字符串

有时候你可能在处理一个非常复杂的MathTex mobject,这使得处理其各个组件变得困难。这时调试函数index_labels()就非常有用。

该方法显示了一个mobject的子对象的索引,使您可以轻松找到您想要更改的mobject的组件。

示例:IndexLabelsMathTex

../_images/IndexLabelsMathTex-1.png
from manim import *

class IndexLabelsMathTex(Scene):
    def construct(self):
        text = MathTex(r"\binom{2n}{n+2}", font_size=96)

        # index the first (and only) term of the MathTex mob
        self.add(index_labels(text[0]))

        text[0][1:3].set_color(YELLOW)
        text[0][3:6].set_color(RED)
        self.add(text)
class IndexLabelsMathTex(Scene):
    def construct(self):
        text = MathTex(r"\binom{2n}{n+2}", font_size=96)

        # index the first (and only) term of the MathTex mob
        self.add(index_labels(text[0]))

        text[0][1:3].set_color(YELLOW)
        text[0][3:6].set_color(RED)
        self.add(text)

LaTeX 数学字体 - 模板库

在排版数学公式时更改LaTeX中的字体比常规文本更复杂。它需要更改用于编译TeX的模板。Manim附带了一系列TexFontTemplates供您使用。这些模板都将在数学模式下工作:

示例:LaTeX数学字体

../_images/LaTeXMathFonts-1.png
from manim import *

class LaTeXMathFonts(Scene):
    def construct(self):
        tex = Tex(
            r"$x^2 + y^2 = z^2$",
            tex_template=TexFontTemplates.french_cursive,
            font_size=144,
        )
        self.add(tex)
class LaTeXMathFonts(Scene):
    def construct(self):
        tex = Tex(
            r"$x^2 + y^2 = z^2$",
            tex_template=TexFontTemplates.french_cursive,
            font_size=144,
        )
        self.add(tex)

Manim 还有一个 TexTemplateLibrary,包含了 3Blue1Brown 使用的 TeX 模板。一个例子是用于排版中文的 ctex 模板。要使此功能正常工作,系统中必须安装 ctex LaTeX 包。此外,如果你只是排版文本,你可能根本不需要 Tex,而应该使用 文本 代替。

示例:LaTeXTemplateLibrary

../_images/LaTeXTemplateLibrary-1.png
from manim import *

class LaTeXTemplateLibrary(Scene):
    def construct(self):
        tex = Tex('Hello 你好 \\LaTeX', tex_template=TexTemplateLibrary.ctex, font_size=144)
        self.add(tex)
class LaTeXTemplateLibrary(Scene):
    def construct(self):
        tex = Tex('Hello 你好 \\LaTeX', tex_template=TexTemplateLibrary.ctex, font_size=144)
        self.add(tex)

对齐公式

MathTex mobject 是在 LaTeX 的 align* 环境中排版的。这意味着你可以在排版多行公式时使用 & 对齐字符:

示例:LaTeXAlignEnvironment

../_images/LaTeXAlignEnvironment-1.png
from manim import *

class LaTeXAlignEnvironment(Scene):
    def construct(self):
        tex = MathTex(r'f(x) &= 3 + 2 + 1\\ &= 5 + 1 \\ &= 6', font_size=96)
        self.add(tex)
class LaTeXAlignEnvironment(Scene):
    def construct(self):
        tex = MathTex(r'f(x) &= 3 + 2 + 1\\ &= 5 + 1 \\ &= 6', font_size=96)
        self.add(tex)