manim icon indicating copy to clipboard operation
manim copied to clipboard

Unexpected behaviour when splitting `MathTex` strings

Open AlfaBetaBeta opened this issue 4 years ago • 3 comments

Description of bug / unexpected behavior

When using double braces to split a MathTex string, the index to subset it and retrieve the relevant substring becomes inconsistent.

Expected behavior

I assumed that a single MathTex string t with a single pair of double braces inside it would be subdivided in 3 submobjects (as is the case), and that retrieving the substring inside the double braces could be done via t[1] but the indices do not seem to follow the order of the submobjects list.

How to reproduce the issue

class MathTexUnexpectedBehaviour(Scene):
    def construct(self):
        t = MathTex("\\int{{_a}}^b dx = b - a")
        self.add(t)

        # print(t.submobjects)
        # [SingleStringMathTex('\\int'), SingleStringMathTex('_a'), SingleStringMathTex('^b dx = b - a')]

        # print(t[1])
        # SingleStringMathTex('_a')

        t[1].set_color(RED)
        # colours '^b', not '_a'

        # Alternatively
        # t.submobjects[1].set_color(RED)
        # colours '^b', not '_a'

Additional media files

MathTexUnexpectedBehaviour_ManimCE_v0 9 0

Logs

Terminal output
PASTE HERE OR PROVIDE LINK TO https://pastebin.com/ OR SIMILAR

System specifications

System Details
  • OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)):
  • RAM:
  • Python version (python/py/python3 --version):
  • Installed modules (provide output from pip list):
PASTE HERE
LaTeX details
  • LaTeX distribution (e.g. TeX Live 2020): TeX Live 2021
  • Installed LaTeX packages:
FFMPEG

Output of ffmpeg -version:

PASTE HERE

Additional comments

I am also a bit confused by the fact that the substring that gets coloured in red is just ^b instead of ^b dx = b - a, which is what actually correspond to one of t.submobjects. If t did not have any double braces at all, then t[0][1] would indeed correspond to ^b, so I'm wondering if t[1] in the example above pointing to the same substring is somehow related.

AlfaBetaBeta avatar Aug 30 '21 23:08 AlfaBetaBeta

Hello, and sorry for the long wait.

There is, indeed, a bug, but it's different from what you think. Consider this scene:

class MathTexUnexpectedBehaviour(Scene):
    def construct(self):
        t = MathTex("\\int{{_a}}^b dx = b - a")
        self.add(t)

        self.wait(0.5)
        self.play(t[0].animate.move_to(4*LEFT).set_color(RED))
        self.play(t[2].animate.move_to(4*RIGHT).set_color(BLUE))
        self.play(t[1].animate.move_to(ORIGIN).set_color(GREEN))

https://github.com/user-attachments/assets/70e9d356-806f-469d-9ab7-2c70b1251388

So, the first submobject is correctly the integral symbol, but the second one corresponds to the upper bound b and the third one is the rest. It's as if the string you passed was "\\int{{^b}}_a dx = b - a" instead of "\\int{{_a}}^b dx = b - a".

Why? Consider this other scene:

class SSMTSubmobjects(ThreeDScene):
    def construct(self):
        self.camera.background_color = GRAY_E

        t = SingleStringMathTex(r"\int_a^b dx = b - a").scale(3)
        self.add(t)
        self.add(index_labels(t, label_height=0.5).set_color(RED))

SSMTSubmobjects_ManimCE_v0 18 1

Notice how I use SingleStringMathTex here, which corresponds to a submobject of MathTex. SingleStringMathTex's submobjects are the individual symbols.

When I call index_labels(t), it makes visual indices for the submobjects of t. Notice how the integral symbol is properly indexed as 0, but the upper bound is 1, the lower bound is 2, and so on.

The issue here is that the Tex mobjects first generate a TeX file, followed by an SVG file which describes how to draw the generated symbols. It seems like the process of creating the SVG file decided that the upper bound comes before the lower bound. Then, Manim grabs this SVG file and renders its contents. MathTex splits the substrings and creates a different SingleStringMathTex for each one of them, and the way it decides which characters belong to which submobject is via the string lengths. MathTex only sees that the 2nd substring is "_a" and it corresponds to a single character, so it fetches one character from the remaining ones, without realizing that the next one corresponds to "^b", not "_a". It has worked very well in general, but now I see that there can be exceptions to that depending on the order in which submobjects are rendered.

Honestly, I don't know if there is an easy fix for this which doesn't involve rewriting huge chunks of code, because I don't see any straightforward way for Manim to know that the next symbol in the sequence of submobjects maybe doesn't correspond to the intended character in the original string. However, maybe someone else well versed in these Manim internals might find a solution.

In the meantime, you can work around this issue by first specifying the upper bound and then the lower bound, like this: MathTex("\\int^b{{_a}} dx = b - a").

chopan050 avatar Dec 01 '24 21:12 chopan050

Thanks for the answer @chopan050, in the end that's what I did stick to back then (integral, then upper bound, then lower bound).

I see from what you said and from the related issue that it might not be realistically fixable (not without too much overhead at least). It's probably fine for most cases, perhaps a note in the documentation would help steering people in the right direction from the start?

AlfaBetaBeta avatar Dec 02 '24 20:12 AlfaBetaBeta

perhaps a note in the documentation would help steering people in the right direction from the start?

I agree. If anyone wants to make a PR for this, they're welcome to do so!

chopan050 avatar Dec 06 '24 03:12 chopan050

Duplicate with some more information at #3548

behackl avatar Aug 03 '25 12:08 behackl