Repository: ManimCommunity/manim

Issue:
Tex parsing issue (introduced in 0.5.0 in break_up_tex_strings)
## Description of bug / unexpected behavior
d = MathTex(r"\frac{dE}{d\mathbf{w}} =")

This produces an incorrect result. See below

![TexFractionWithBoldFaceBug_ManimCE_v0 5 0](https://user-images.githubusercontent.com/127006/113492078-f74fab00-949a-11eb-9c55-bdbb2a2448fa.png)

## Expected behavior

This bug was not present in 0.4.0

## How to reproduce the issue

```py
class TexFractionWithBoldFaceBug(Scene):
    def construct(self):
        d1 = MathTex(r"\frac{dE}{d\mathbf{w}}", "=")
        self.add(d1)
```

## Additional media files


## Logs



## System specifications

<details><summary>System Details</summary>

- OS (with version, e.g Windows 10 v2004 or macOS 10.15 (Catalina)): macOS 11
- RAM:
- Python version (`python/py/python3 --version`):3.7
- Installed modules (provide output from `pip list`):
```
PASTE HERE
```
</details>

<details><summary>LaTeX details</summary>

+ LaTeX distribution (e.g. TeX Live 2020):
+ Installed LaTeX packages:
<!-- output of `tlmgr list --only-installed` for TeX Live or a screenshot of the Packages page for MikTeX -->
</details>

<details><summary>FFMPEG</summary>

Output of `ffmpeg -version`:

```
PASTE HERE
```
</details>

## Additional comments

I have debugged and isolated the portion of the code that has introduced the issue.

break_up_tex_strings has been changed in 0.5.0; essentially borrowed from original manim

```
def break_up_tex_strings(self, tex_strings):
        print("input -> ", tex_strings)
        tex_strings = [str(t) for t in tex_strings]
        # Separate out anything surrounded in double braces
        patterns = ["{{", "}}"]
        # Separate out any strings specified in the isolate
        # or tex_to_color_map lists.
        patterns.extend([
            "({})".format(re.escape(ss)) for ss in it.chain(
                self.substrings_to_isolate, self.tex_to_color_map.keys())
        ])
        pattern = "|".join(patterns)
        pieces = []
        for s in tex_strings:
            pieces.extend(re.split(pattern, s))
        r = list(filter(lambda s: s, pieces))
        print("result -> ", r)
        return r
```

Here is the snapshot of what this new implementation produces with my print statements. Rest is identical to master and 0.5.0.

![break_up_tex_string_log](https://user-images.githubusercontent.com/127006/113492239-a55b5500-949b-11eb-9539-fc3ccdf155f1.png)

As you can see that the returned value is malformed and is also very clear why this would happen. Essentially `patterns = ["{{", "}}"]` would result into this.

Just for testing if you change `patterns = ["{{", "}}"]` to `patterns = ["{{"]`, the bug will go away.

Another workaround is to construct the tex as `MathTex(r"\frac{dE}{d\mathbf{w} }", "=")`.  Note that I have put an extra space between the two closing brackets i.e. "} }" instead of "}}"

I use Latex a lot and based on my experience I envision that this is not the only time when two curly braces will appear. 












Discussion:
Technically, it's a feature, not a bug -- but I agree that the current implementation is unfortunate and definitely needs to be improved to avoid this particular error.

One option would be to parse the input string differently such that the groups are only split if there is both `{{` and `}}` present in the string -- while `}}` occurs rather naturally in TeX strings, `{{` does not. If both are present, that would indicate that the corresponding subexpression actually should be isolated when rendering.

Another way would be to change the delimiters to something else, but I don't really have a good idea.

I would still like to keep this feature around, because it's quite useful in conjunction with the new `TransformMatchingTex` animation.
Thanks, @behackl for the quick follow-up.

For now, I have a workaround by putting a space between 2 curly brackets but for people who are not experienced with Latex, it may become frustrating.

I liked both of your suggestions; here is one based on your different delimiter one -

Maybe make the delimiter configurable (passed to the object) i.e user may decide to use `mm` as delimiters or `3b1b` ;) maybe!

Another thought - If this functionality is only needed for `TransformMatchingTex` then maybe a flag (again in the object) that enables or disables this pattern matching. Essentially user will explicitly enable this pattern matching if he/she intends to use the `TransformMatchingTex` transform. This way he/she is aware of pitfalls if any!





This has affected me as well and really we should not have to do \frac{1}{a+b\sqrt{2} } just to make it work IMHO. I have not put much thought into this issue but if putting a space fixes this maybe manim can replace }} with } } in break_up_tex_strings so that the user doesn't have to.
Currently, `break_up_tex_strings` uses both `{{` as well as `}}` as special delimiters at which the Tex string is broken and substrings are created. This is useful if you want to manipulate equations in order to, for example, transform `a + b = c` into `a = c - b`, because on a Tex string level you want to have the "moving" parts as isolated strings.

The new delimiter-notation ensures that instead of writing the Tex string manually as `MathTex("a", "+", "b", "=", "c")`, you can write it as `MathTex("{{a}} + {{b}} = {{c}}")`, and the string will be broken apart automatically.

I think that a feature like that is, in general, useful -- however, the implementation should be improved. In particular: if there is no `{{` (which isn't as common in LaTeX, at least as far I am concerned) beforehand, then `}}` shouldn't be regarded as a breaking delimiter.
@behackl I agree that if there is no `{{` then `}}` shouldn't be regarded as a breaking delimiter.

Resolving patch:
diff --git a/manim/mobject/svg/tex_mobject.py b/manim/mobject/svg/tex_mobject.py
--- a/manim/mobject/svg/tex_mobject.py
+++ b/manim/mobject/svg/tex_mobject.py
@@ -203,6 +203,7 @@ def construct(self):
 import operator as op
 import re
 from functools import reduce
+from textwrap import dedent
 
 from ... import config, logger
 from ...constants import *
@@ -391,6 +392,15 @@ def construct(self):
         >>> MathTex('a^2 + b^2 = c^2')
         MathTex('a^2 + b^2 = c^2')
 
+    Check that double brace group splitting works correctly::
+
+        >>> t1 = MathTex('{{ a }} + {{ b }} = {{ c }}')
+        >>> len(t1.submobjects)
+        5
+        >>> t2 = MathTex(r"\frac{1}{a+b\sqrt{2}}")
+        >>> len(t2.submobjects)
+        1
+
     """
 
     def __init__(
@@ -411,27 +421,49 @@ def __init__(
         if self.tex_to_color_map is None:
             self.tex_to_color_map = {}
         self.tex_environment = tex_environment
+        self.brace_notation_split_occurred = False
         tex_strings = self.break_up_tex_strings(tex_strings)
         self.tex_strings = tex_strings
-        SingleStringMathTex.__init__(
-            self,
-            self.arg_separator.join(tex_strings),
-            tex_environment=self.tex_environment,
-            tex_template=self.tex_template,
-            **kwargs,
-        )
-        self.break_up_by_substrings()
+        try:
+            SingleStringMathTex.__init__(
+                self,
+                self.arg_separator.join(tex_strings),
+                tex_environment=self.tex_environment,
+                tex_template=self.tex_template,
+                **kwargs,
+            )
+            self.break_up_by_substrings()
+        except ValueError as compilation_error:
+            if self.brace_notation_split_occurred:
+                logger.error(
+                    dedent(
+                        """\
+                        A group of double braces, {{ ... }}, was detected in
+                        your string. Manim splits TeX strings at the double
+                        braces, which might have caused the current
+                        compilation error. If you didn't use the double brace
+                        split intentionally, add spaces between the braces to
+                        avoid the automatic splitting: {{ ... }} --> { { ... } }.
+                        """
+                    )
+                )
+            raise compilation_error
         self.set_color_by_tex_to_color_map(self.tex_to_color_map)
 
         if self.organize_left_to_right:
             self.organize_submobjects_left_to_right()
 
     def break_up_tex_strings(self, tex_strings):
-        tex_strings = [str(t) for t in tex_strings]
         # Separate out anything surrounded in double braces
-        patterns = ["{{", "}}"]
+        pre_split_length = len(tex_strings)
+        tex_strings = [re.split("{{(.*?)}}", str(t)) for t in tex_strings]
+        tex_strings = sum(tex_strings, [])
+        if len(tex_strings) > pre_split_length:
+            self.brace_notation_split_occurred = True
+
         # Separate out any strings specified in the isolate
         # or tex_to_color_map lists.
+        patterns = []
         patterns.extend(
             [
                 "({})".format(re.escape(ss))
@@ -441,10 +473,15 @@ def break_up_tex_strings(self, tex_strings):
             ]
         )
         pattern = "|".join(patterns)
-        pieces = []
-        for s in tex_strings:
-            pieces.extend(re.split(pattern, s))
-        return list(filter(lambda s: s, pieces))
+        if pattern:
+            pieces = []
+            for s in tex_strings:
+                pieces.extend(re.split(pattern, s))
+        else:
+            pieces = tex_strings
+        ret = [p for p in pieces if p]
+        print(ret)
+        return ret
 
     def break_up_by_substrings(self):
         """


Test patch: