.. _typst_usage: Typst 的使用 ====================== 简要介绍 --------------------- 如果你是从 Manim 迁移过来的用户,你可能对 LaTeX 有一定的了解。 当我们在 JAnim 中使用 Typst,就像是在 Manim 中使用 LaTeX,可以通过代码的形式创建排版或公式。 我们常用 :class:`~.TypstText` 和 :class:`~.TypstMath` 来在 JAnim 中使用 Typst .. code-block:: python typ1 = TypstText(R'This is a #text(aqua)[sentence] with a math expression $cos^2 x + sin^2 x = 1$') typ2 = TypstMath(R'cos^2 theta + sin^2 theta = 1') .. tip:: 当你使用了 VS Code 插件 ``janim-toolbox``,出现在 ``TypstText`` 中的 **Raw-字符串** (类似 ``R'...'`` 的形式)会作为 Typst 代码高亮颜色,如下图所示 .. image:: /_static/tutorial/TypstHighlight.png 较为遗憾的是,在 :class:`~.TypstMath` 中的 Raw-字符串 暂时没有该功能。 如果你更倾向于使用 JAnim 展示公式,那么你更可能会经常用到 :class:`~.TypstMath`。 Typst 物件的类型 -------------------------- Typst 物件分为三种: - :class:`~.TypstText` 表示 Typst 文字,传入的字符串不会被 ``$ $`` 所包裹 - :class:`~.TypstMath` 表示 Typst 公式,传入的字符串会被包裹在 ``$ $`` 中作为公式进行编译 - :class:`~.TypstDoc` 是所有其它 Typst 物件的基类,它表示一个 Typst 文档 :class:`~.TypstDoc` 与 :class:`~.TypstText` 和 :class:`~.TypstMath` 不同,它会自动与画面的最上方对齐,有一种“从文档的开头开始查看”的感觉 也就是说,:class:`~.TypstText` 和 :class:`~.TypstMath` 的区别仅是是否被包裹在公式环境中,例如 ``TypstMath('x^2')`` 和 ``TypstText('$ x^2 $')`` 在大多数情况下是等效的 Typst 子物件索引 ------------------------ 基础用法 ~~~~~~~~~~~~~~~~~~~~~~ 对于普通的物件,使用 :ref:`subitem_selector` 比较方便;而对于 Typst 物件,有更加方便的索引方式: **字符索引** 比如对于 :class:`~.TypstMath` 对象 .. code-block:: python typ = TypstMath('cos^2 theta + sin^2 theta = 1') 可以使用 ``typ['cos']`` 得到 ``cos`` 对应的部分,这样你就可以使用类似于 ``typ['cos'].set(color=BLUE)`` 的方式进行着色,或进行其它处理。 当出现多个匹配时的处理 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 你应该注意到了这里有两个“θ”(``theta``),当你使用 ``typ['theta']`` 的方式进行索引时,将会取出第一个匹配的 θ,也就是前一个。 因为他们是从 ``0``、 ``1``、... 依次编号的,所以在这个例子中你可以使用 ``typ['theta', 1]`` 得到后一个。 .. note:: 这也意味着 ``t['theta']`` 和 ``t['theta', 0]`` 是等效的 .. janim-example:: TypstColorizeExample :media: _static/videos/TypstColorizeExample.mp4 typ = TypstMath('cos^2 theta + sin^2 theta = 1', scale=3).show() self.forward() self.play(typ['cos'].anim.set(color=BLUE)) self.play(typ['sin'].anim.set(color=BLUE)) self.play(typ['theta', 0].anim.set(color=GOLD)) self.play(typ['theta', 1].anim.set(color=ORANGE)) self.forward() self.play(typ['theta', ...].anim.set(color=GREEN)) self.play(typ['space^2', ...].anim.set(color=RED)) self.forward() 如果想要同时取出多个,则将多个编号写在一个序列中即可,例如 ``typ['theta', (0, 1)]`` 则是取出编号为 ``0`` 和 ``1`` 的匹配项,在这里就是所有匹配到的 θ 符号。 你应该发现了,取出 ``(0, 1)`` 的项,其实就是取出所有项,对于这种情况,JAnim 提供了 ``typ['theta', ...]`` 的方式,使用省略号表示取出所有的匹配项。 一些特殊情况 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 当你想要取出 .. code-block:: python typ = TypstMath('cos^2 theta + sin^2 theta = 1') 中的上标 “2” 时,使用 ``typ['2']`` 无法匹配到它,这是因为上标的 “2” 和普通的 “2” 长得不同。 为了正确匹配,你需要把索引中的 2 也表示为“上标”的形式,例如 ``typ['""^2']`` 或者 ``typ['space^2']``, 这两者都是把 “2” 作为一个空元素( ``""`` 或者 ``space`` ) 的上标,这样就可以正确匹配了。 .. important:: 上面以 :class:`~.TypstMath` 作为字符索引的例子,:class:`~.TypstDoc` 和 :class:`~.TypstText` 也是几乎一致的,但是会有略微区别 我们知道,在这三种对象中,只有 :class:`~.TypstMath` 是在公式环境中的,所以进行它的字符索引时,作为索引的字符串也会在公式环境中解析 这意味着,对于 :class:`~.TypstDoc` 和 :class:`~.TypstText` 而言,作为索引的字符串不在公式环境中 这里给出几段示例作为参考: .. code-block:: python t = TypstMath('cos theta') t['theta'] t = TypstText('$ cos theta $') t['$theta$'] .. code-block:: python t = TypstText('this is a formula: $cos^2 x + sin^2 x = 1$') t['formula'] t['$x$'] 内置包 ----------------- JAnim 提供了内置包可以在 Typst 中使用 ``#import`` 引入 - ``#import "@janim/colors:0.0.0": *`` 提供了 JAnim 中的颜色常量(可参考 :ref:`constants_colors` 条目),以便在 Typst 中使用 .. raw:: html
点击展开 @janim/colors 的具体定义 .. code-block:: typst // Colors #let BLUE_E = rgb("#1C758A") #let BLUE_D = rgb("#29ABCA") #let BLUE_C = rgb("#58C4DD") #let BLUE_B = rgb("#9CDCEB") #let BLUE_A = rgb("#C7E9F1") #let TEAL_E = rgb("#49A88F") #let TEAL_D = rgb("#55C1A7") #let TEAL_C = rgb("#5CD0B3") #let TEAL_B = rgb("#76DDC0") #let TEAL_A = rgb("#ACEAD7") #let GREEN_E = rgb("#699C52") #let GREEN_D = rgb("#77B05D") #let GREEN_C = rgb("#83C167") #let GREEN_B = rgb("#A6CF8C") #let GREEN_A = rgb("#C9E2AE") #let YELLOW_E = rgb("#E8C11C") #let YELLOW_D = rgb("#F4D345") #let YELLOW_C = rgb("#FFFF00") #let YELLOW_B = rgb("#FFEA94") #let YELLOW_A = rgb("#FFF1B6") #let GOLD_E = rgb("#C78D46") #let GOLD_D = rgb("#E1A158") #let GOLD_C = rgb("#F0AC5F") #let GOLD_B = rgb("#F9B775") #let GOLD_A = rgb("#F7C797") #let RED_E = rgb("#CF5044") #let RED_D = rgb("#E65A4C") #let RED_C = rgb("#FC6255") #let RED_B = rgb("#FF8080") #let RED_A = rgb("#F7A1A3") #let MAROON_E = rgb("#94424F") #let MAROON_D = rgb("#A24D61") #let MAROON_C = rgb("#C55F73") #let MAROON_B = rgb("#EC92AB") #let MAROON_A = rgb("#ECABC1") #let PURPLE_E = rgb("#644172") #let PURPLE_D = rgb("#715582") #let PURPLE_C = rgb("#9A72AC") #let PURPLE_B = rgb("#B189C6") #let PURPLE_A = rgb("#CAA3E8") #let GREY_E = rgb("#222222") #let GREY_D = rgb("#444444") #let GREY_C = rgb("#888888") #let GREY_B = rgb("#BBBBBB") #let GREY_A = rgb("#DDDDDD") #let PURE_RED = rgb("#FF0000") #let PURE_GREEN = rgb("#00FF00") #let PURE_BLUE = rgb("#0000FF") #let WHITE = rgb("#FFFFFF") #let BLACK = rgb("#000000") #let GREY_BROWN = rgb("#736357") #let DARK_BROWN = rgb("#8B4513") #let LIGHT_BROWN = rgb("#CD853F") #let PINK = rgb("#D147BD") #let LIGHT_PINK = rgb("#DC75CD") #let GREEN_SCREEN = rgb("#00FF00") #let ORANGE = rgb("#FF862F") // Be compatible with the old names #let GREEN_SCREEN = rgb("#00FF00") // Abbreviated names for the "median" colors #let BLUE = BLUE_C #let TEAL = TEAL_C #let GREEN = GREEN_C #let YELLOW = YELLOW_C #let GOLD = GOLD_C #let RED = RED_C #let MAROON = MAROON_C #let PURPLE = PURPLE_C #let GREY = GREY_C .. raw:: html
.. note:: 如果你需要脱离 JAnim 在外部 ``.typ`` 文件中编写 Typst 代码,希望其也能引入 JAnim 的内置包 你需要将 ``/janim/items/svg`` 完整路径通过 ``--package-path`` 选项传递给 Typst 编译器或 Tinymist 插件的 ``"tinymist.typstExtraArgs"`` 选项 语法高亮 ---------------- 前面提到,如果你使用了 VSCode 插件 ``janim-toolbox``, 会自动给 :class:`TypstDoc` 和 :class:`TypstText` 中出现的 Raw-字符串(形如 ``R'...'``) 进行 Typst 语法高亮。 对于同样需要 Typst 语法高亮,但不在 :class:`TypstDoc` 与 :class:`TypstText` 之中的字符串,你可以使用 ``t_`` 函数来标注需要 Typst 高亮,例如 .. code-block:: python LightTyp = partial(TypstText, color=YELLOW) typ = LightTyp(t_(R'#box(width: 10em)[#lorem(20)]')).show() .. code-block:: python with Config(typst_shared_preamble='#set box(width: 3em, height: 3em)'): group = Group.from_iterable( TypstText(content) for content in t_( R'#box(stroke: red)', R'#box(fill: red)', R'#box(stroke: red, outset: 2pt)[ab]', R'#box(fill: aqua)[A]', ) ) group.points.arrange() group.show() 嵌入 JAnim 物件 ---------------------- Typst 物件支持传入 ``vars`` 参数嵌入 JAnim 物件: .. janim-example:: TypstVars :media: _static/tutorial/TypstVars.mp4 :hide_name: :ref: :class:`~.TypstText` :class:`~.Video` typ1 = TypstText( R'This is a sentence with an inserted #star JAnim item', vars={ 'star': Star(outer_radius=0.5, color=YELLOW, fill_alpha=0.5) }, vars_size_unit='em' ) typ2 = TypstText( R''' #box(fill: luma(40%), inset: 8pt)[ This is a grid containing JAnim items #grid( columns: 2, fill: luma(20%), gutter: 4pt, inset: 8pt, [$f(x)$\ math content], gif, star, [QwQ\ text content] ) ] ''', vars={ 'gif': Video('Ayana.gif', loop=True).start(), 'star': Star(), }, ) Group(typ1, typ2).show().points.arrange(DOWN) self.forward(4) .. hint:: 未传入 ``vars_size_unit`` 时,嵌入的 JAnim 物件会保留在 JAnim 中原有的大小,若传入了 ``vars_size_unit`` 则将其大小乘上对应的单位。 例如对于一个在 JAnim 中高度为 1 的物件,如果直接插入 Typst 文字中会显得很大,此时设置 ``vars_size_unit='em'`` 使其插入高度变为 ``1em``,则基本与文字高度匹配。 ``vars`` 是一个字典,它的键会作为 Typst 代码中可以使用的变量名,值会作为变量名对应的 JAnim 物件,并且支持进一步嵌套列表和字典: .. code-block:: python TypstText( ..., vars=dict( shapes=[ Star(), Square(), Circle() ], mapping=dict( txt=Text('This is a JAnim sentence'), vid=Video('example.mp4').start() ) ) ) .. note:: 在 Typst 中嵌入 JAnim 物件,从原理上来讲是创建了一个对应大小的占位 ``box``,然后在 Typst 物件创建后,将其替换为 JAnim 物件,从而做到嵌入的目的。 特殊类型的 Typst 物件 ----------------------------- - :class:`~.TypstMatrix` 使用 Typst 进行矩阵布局 参考文档 ------------- - JAnim 类定义:请参考 :ref:`typst` 页面 - Typst 中文社区: `typst-doc-cn.github.io/guide `_ - Typst 官方文档: `typst.app/docs `_