見出し画像

【Manim】Texモブジェクトで日本語を出力する方法

Manim で日本語のテキストを表示しようと思うと、Textクラスが使えますが、Texクラスは通常の設定ではできません。ですが、Texクラスでも日本語を出力する方法を見つけたので、紹介しようと思います。

結論

先に結論から書きますと、次のいずれかのコードを from manim import * の下に書けばOKです。

Tex.set_default(tex_template=TexTemplate(
    tex_compiler = "lualatex", 
    # tex_compiler = "luatex" でも可
    output_format = ".pdf", 
    preamble = r"""
        \usepackage{amsmath}
        \usepackage{amssymb}
        \usepackage{luatexja}
        \usepackage[haranoaji]{luatexja-preset}
    """
))
Tex.set_default(tex_template=TexTemplate(
    tex_compiler = "xelatex", 
    output_format = ".pdf", 
    # output_format = ".xdv" でも可
    preamble = r"""
        \usepackage{amsmath}
        \usepackage{amssymb}
        \usepackage{zxjatype}
        \setjamainfont{HaranoAjiMincho}
    """
))

実際にやってみるとこんな感じ。

from manim import *
config.background_color = WHITE
Mobject.set_default(color = BLACK)

Tex.set_default(tex_template=TexTemplate(
    tex_compiler = "lualatex", 
    # tex_compiler = "luatex" でも可
    output_format = ".pdf", 
    preamble = r"""
        \usepackage{amsmath}
        \usepackage{amssymb}
        \usepackage{luatexja}
        \usepackage[haranoaji]{luatexja-preset}
    """
))

class JaTex(Scene):
    def construct(self):  
        text = Tex("$e^{i\\theta} = \\cos{\\theta} + i\\sin{\\theta}$ をオイラーの公式という.") 
        self.add(text)

もし、変更を全体でなく各クラスごとに適用させたい場合は、そのクラスの中だけにこのコードを書けば良いです。

また、configを使って次のようにしても良いのですが、そうしない理由は後述の問題点で話します。

from manim import *

config.tex_template = TexTemplate(
    tex_compiler = "xelatex", 
    output_format = ".pdf", 
    preamble = r"""
        \usepackage{amsmath}
        \usepackage{amssymb}
        \usepackage{luatexja}
        \usepackage[haranoaji]{luatexja-preset}
    """
)


フォント

上で示したコードの \usepackage[haranoaji]{luatexja-preset}(\setjamainfont{HaranoAjiMincho}) の部分で、使用する日本語のフォントを設定します。今回の例では、pLatexにデフォルトで設定されている原ノ味フォントを使用しています。フォントの変更方法についてはTexの領域になるので、ここではやりません。気になる方は調べてみてください。

問題点

この方法には一つ問題点があります。それは、Texモブジェクトで分数を表示すると、分子と分母の間の線が表示されないことです。これは lualatex, xelatex のどちらを使用しても起こります。

class Fraction1(Scene):
    def construct(self):    
        template1 = TexTemplate(tex_compiler="lualatex", output_format=".pdf")
        template1.add_to_preamble("\\usepackage{luatexja} \n \\usepackage[haranoaji]{luatexja-preset}")
        template2 = TexTemplate(tex_compiler="xelatex", output_format=".pdf")
        template2.add_to_preamble("\\usepackage{zxjatype} \n \\setjamainfont{HaranoAjiMincho}")
        
        eq1 = MathTex(r"e^x = \sum_{n=0}^\infty \frac{x^n}{n!}")
        eq2 = MathTex(r"e^x = \sum_{n=0}^\infty \frac{x^n}{n!}", tex_template=template1)
        eq3 = MathTex(r"e^x = \sum_{n=0}^\infty \frac{x^n}{n!}", tex_template=template2)
        
        eq_group = Group(eq1, eq2, eq3).arrange(buff=2)
        
        text1 = Tex("latex の場合", tex_template=template1).next_to(eq1, DOWN)
        text2 = Tex("lualatex の場合", tex_template=template1).next_to(eq2, DOWN)
        text3 = Tex("xelatex の場合", tex_template=template1).next_to(eq3, DOWN)
        
        self.add(eq_group, text1, text2, text3)

このため、最初に述べたように config での設定はお勧めしません。なぜなら、config で設定すると上の Fraction1 の場合のように MathTex にこの変更が適用され、上のような問題が発生するからです。しかし、Tex.set_default を使用すれば、Mathtex だけは通常の方法で出力するので、この問題を回避することができます。
数式は MathTex、テキストは Tex と完全に使い分けることができれば良いですが、次の例に示すように、一つのTexモブジェクトによって日本語のテキストと分数を同時に表示するような場合は、この方法ではこれを回避することができません。

class FractionWithText(Scene):
    def construct(self):  
        Tex.set_default(tex_template=TexTemplate(
            tex_compiler = "lualatex", 
            output_format = ".pdf", 
            preamble = r"""
                \usepackage{amsmath}
                \usepackage{amssymb}
                \usepackage{luatexja}
                \usepackage[haranoaji]{luatexja-preset}
            """
        ))
        
        eq = MathTex(r"e^x = \sum_{n=0}^\infty \frac{x^n}{n!}").to_edge(LEFT).shift(UP)
        text_eq = Tex("(Tex にのみ適用しているので MathTex には影響がない)", font_size=36).next_to(eq, RIGHT, LARGE_BUFF)
        text = Tex("$e^x = \\sum_{n=0}^\\infty \\frac{x^n}{n!}$ は $e^x$ の $x=0$ におけるテイラー展開である.").shift(DOWN)
        
        self.add(eq, text_eq, text)

一応解決方法は存在して、それは文字を太くすることです。

class Fraction2(Scene):
    def construct(self):  
        Tex.set_default(tex_template=TexTemplate(
            tex_compiler = "lualatex", 
            output_format = ".pdf", 
            preamble = r"""
                \usepackage{amsmath}
                \usepackage{amssymb}
                \usepackage{luatexja}
                \usepackage[haranoaji]{luatexja-preset}
            """
        ))
        
        text = Tex("$e^x = \\sum_{n=0}^\\infty \\frac{x^n}{n!}$ は $e^x$ の $x=0$ におけるテイラー展開である.", stroke_width=2)
        # text = Tex("$e^x = \\sum_{n=0}^\\infty \\frac{x^n}{n!}$ は $e^x$ の $x=0$ におけるテイラー展開である.").set_stroke(width=2) でも同じ
        self.add(text)


理由はわかりませんが、どうやらこの設定をすると分子と分母の間の線が通常よりも細く表示されてしまうようです。これでは文字が太くなってしまうという欠点がありますが、現状で私が把握している解決方法はこれしかありません。誰か良い解決方法を見つけたら教えてください。

--追記--


別の解決方法を一つ見つけました。
下のコードの text のように、分数を含む数式の部分が独立するよう文章を分割します。そして eq のように、分数を含む数式を新たに MathTex モブジェクトとして作り、move_to メソッドによって、text[0](text における "$e^x = \sum_{n=0}^\infty \frac{x^n}{n!}$" の部分)に移動させます。最後に、text[0] は表示せず、代わりに eq を表示させれば、日本語の文章と合わせて分数も表示させることができます。

Tex.set_default(tex_template=TexTemplate(
    tex_compiler = "lualatex", 
    output_format = ".pdf", 
    preamble = r"""
        \usepackage{amsmath}
        \usepackage{amssymb}
        \usepackage{luatexja}
        \usepackage[haranoaji]{luatexja-preset}
    """
))
    
class Test(Scene):
        def construct(self):
            text = Tex(r"$e^x = \sum_{n=0}^\infty \frac{x^n}{n!}$", "は $e^x$ の $x=0$ におけるテイラー展開である.")
            eq = MathTex(r"\textstyle e^x = \sum_{n=0}^\infty \frac{x^n}{n!}").move_to(text[0])
            
            self.add(text[1], eq)

解説

ここからは私の理解できた範囲で深堀りして解説します。

TexTemplate 

このクラスでは Manim で出力する Tex のテンプレートを設定することができます。少しだけ、このクラスのパラメータについて解説します。

  • tex_compiler

使用するTexのコンパイラを指定することができます。ドキュメントには指定できるコンパイラの例として、latex, pdflatex, lualatex が挙げられていますが、xelatex にも対応しています。
これらの内、日本語に対応しているのは lualatex, xelatex の二つです。

  • output_format

コンパイラの出力するフォーマットを指定します。
ドキュメントには指定できるフォーマットの例として、.dvi, .pdf が挙げられています。lualatex を使用する場合には .pdf を、xelatex を使用する場合には .pdf または .xdv を指定します。

  • documentclass

テンプレートの文書クラスを指定します。この辺は Tex の領域ですね。
デフォルトでは \documentclass[preview]{standalone} となっているのですが、この standalone を別のものに変えてしまうと、Texモブジェクトが表示されなくなってしまいます。

class Template(Scene):
    def construct(self): 
        template = TexTemplate(
            documentclass="\documentclass[preview]{article}"
        )
        text1 = Tex("text1").shift(LEFT)
        text2 = Tex("text2", tex_template=template).shift(RIGHT)
        self.add(text1, text2)
  • preamble

テンプレートのプリアンブルを指定します。これも Tex の領域ですね。
lualatex や xelatex で日本語を出力するためにはここで \usepackage{luatexja} や \usepackage{zxjatype} などを追加する必要があります。

tex_file_writing

TexTemplate で設定された情報は次の tex_file_writing で読み込まれます。

この中の tex_compilation_command という関数のソースコードを見ると、
tex_compiler の値として、latex, pdflatex, luatex, lualatex, xelatex をとることが分かります。

tex_file_writing は、TexTemplate が uplatex に対応してないものだろうかと思い、tex_compiler = "uplatex" と脳死で放り込んだ時にターミナルで表示されたエラーログからたどり着きました。

Manimに関する情報はまだあまり多くないので、エラーログから関連するソースにとんで直接見ることも、場合によっては必要だと思います。

この記事が気に入ったらサポートをしてみませんか?