Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Long text has random line breaks or is cut off when using manim.Text #4043

Open
Malacath-92 opened this issue Dec 5, 2024 · 5 comments
Open

Comments

@Malacath-92
Copy link

Malacath-92 commented Dec 5, 2024

Description of bug / unexpected behavior

When rendering a very long string with manim.Text I can observe random line breaks not present in the original string.

Expected behavior

I expect no line breaks

How to reproduce the issue

Simply try to render a long text with

Code for reproducing the problem
from manim import *

long_string = r"{!how()'&where()&what()%how()from()<~ $what()mul(600,238);what()*mul(982,137)%~from())+~mul(550,306)@mul(331,66)>:&~ ?>%{%>why(){]mul(138,385)?~>mul(453,763)?;%mul(162,449)/[mul(629,968)&+@>:@select(){/*when()mul(868,688)?mul(160,791)>+@when()>^}'what()from()mul(473,136)?$[;:who(){mul(825,302)where()&!why()/>from()select()what()when()mul(325,635))when()^from()mul(299,8)when()how()]:mul(332,547)?!}%#when()*from():mul(532,358)#[?how()@from()mul(961,542)select()mul(115,952)-[from()()?[)mul(647,503)mul(474,649)'[mul(239,709)from()select()?^$^select()what(967,742)(where()mul(143,91)why()^,^{mul(465,739)+select()~who()/mul(244,259)mul(45,460){how()$from(),%<)who();?)where()+(select()$how()mul(752,67)mul(344,833));)&,mul(601,124)*select()*< where()]mul(375,322)when(),]when(106,699)'$mul(477,697)+]{ how() <mul(596,223)how()who(66,775){why()$who()mul(860,132)when()#};;[(mul(600,137)when()#<:mul(216,806){--mul(772,585]who()mul(373,681)what()~select()how()-mul(349,138)-},how()when()$do()?:,&/?mul(560,179) from())/who();]>%mul(626,452)mul(482,56)@{where(455,533)):$<[mul(10where(601,497)mul(426,581)who()}*]%select(907,807)>&why()mul(227,368)%mul(812,31)from()$-$:mul(694,781)!mul(737,400)>mul(692,914)^%},</^$mul(450,519)where()mul(343,778)mul(862,640)[*}>>+&mul(292,865)[[]why(){why()&!how()how()"
highlights = {
    "[45:57]": ManimColor("#83C167"),
    "[65:77]": ManimColor("#83C167"),
    "[88:100]": ManimColor("#83C167"),
    "[101:112]": ManimColor("#83C167"),
    "[130:142]": ManimColor("#83C167"),
    "[145:157]": ManimColor("#83C167"),
    "[160:172]": ManimColor("#83C167"),
    "[174:186]": ManimColor("#83C167"),
    "[209:221]": ManimColor("#83C167"),
    "[222:234]": ManimColor("#83C167"),
    "[259:271]": ManimColor("#83C167"),
    "[282:294]": ManimColor("#83C167"),
    "[336:348]": ManimColor("#83C167"),
    "[362:372]": ManimColor("#83C167"),
    "[385:397]": ManimColor("#83C167"),
    "[416:428]": ManimColor("#83C167"),
    "[443:455]": ManimColor("#83C167"),
    "[463:475]": ManimColor("#83C167"),
    "[488:500]": ManimColor("#83C167"),
    "[500:512]": ManimColor("#83C167"),
    "[514:526]": ManimColor("#83C167"),
    "[573:584]": ManimColor("#83C167"),
    "[593:605]": ManimColor("#83C167"),
    "[621:633]": ManimColor("#83C167"),
    "[633:644]": ManimColor("#83C167"),
    "[692:703]": ManimColor("#83C167"),
    "[703:715]": ManimColor("#83C167"),
    "[720:732]": ManimColor("#83C167"),
    "[752:764]": ManimColor("#83C167"),
    "[787:799]": ManimColor("#83C167"),
    "[810:822]": ManimColor("#83C167"),
    "[850:862]": ManimColor("#83C167"),
    "[874:886]": ManimColor("#83C167"),
    "[895:907]": ManimColor("#83C167"),
    "[927:939]": ManimColor("#83C167"),
    "[960:972]": ManimColor("#83C167"),
    "[997:1009]": ManimColor("#83C167"),
    "[1027:1039]": ManimColor("#83C167"),
    "[1039:1050]": ManimColor("#83C167"),
    "[1091:1103]": ManimColor("#83C167"),
    "[1134:1146]": ManimColor("#83C167"),
    "[1147:1158]": ManimColor("#83C167"),
    "[1168:1180]": ManimColor("#83C167"),
    "[1181:1193]": ManimColor("#83C167"),
    "[1194:1206]": ManimColor("#83C167"),
    "[1214:1226]": ManimColor("#83C167"),
    "[1233:1245]": ManimColor("#83C167"),
    "[1245:1257]": ManimColor("#83C167"),
    "[1264:1276]": ManimColor("#83C167"),
}


class TextScene(Scene):
    def __init__(self):
        super().__init__()

        self.text = Text(long_string)
        self.text.scale(0.1)
        self.text.align_on_border(LEFT)

        self.text_with_highlights = Text(long_string, t2c=highlights)
        self.text_with_highlights.scale(0.1)
        self.text_with_highlights.align_to(self.text, DOWN)

    def construct(self):
        self.add(self.text, self.text_with_highlights)
        self.wait()


config.verbosity = "DEBUG"
TextScene().render()

Additional media files

image

Logs

Terminal output

[12/05/24 09:50:42] DEBUG Animation with empty mobject animation.py:175 DEBUG Hashing ... hashing.py:352
[12/05/24 09:50:47] DEBUG Hashing done in 4.736309 s. hashing.py:364
DEBUG Hash generated : 3977891868_123554531_3897131564 hashing.py:367
DEBUG List of the first few animation hashes of the scene: ['3977891868_123554531_3897131564'] cairo_renderer.py:97
INFO Animation 0 : Partial movie file written in scene_file_writer.py:527
'media\videos\1080p60\partial_movie_files\TextScene\3977891868_123554531_3897131564.mp4'
INFO Combining to Movie file. scene_file_writer.py:617
DEBUG Partial movie files to combine (1 files): scene_file_writer.py:561
['media\videos\1080p60\partial_movie_files\TextScene\3977891868_123554531_3897131564.mp4']
INFO scene_file_writer.py:737
File ready at 'media\videos\1080p60\TextScene.mp4'

                INFO     Rendered TextScene                                                                                                                                                           scene.py:247
                         Played 1 animations

System specifications

System Details
  • Windows 11
  • RAM: 16GB
  • Python 3.11.5
  • Installed modules:
pip list --local
Package           Version
----------------- -----------
click             8.1.7
cloup             3.0.5
colorama          0.4.6
decorator         5.1.1
glcontext         3.0.0
isosurfaces       0.1.2
manim             0.18.1
ManimPango        0.6.0
mapbox_earcut     1.0.2
markdown-it-py    3.0.0
mdurl             0.1.2
moderngl          5.12.0
moderngl-window   3.0.2
networkx          3.4.2
numpy             2.1.3
pillow            11.0.0
pip               24.3.1
pycairo           1.27.0
pydub             0.25.1
pyglet            2.0.18
PyGLM             2.7.3
Pygments          2.18.0
rich              13.9.4
scipy             1.14.1
screeninfo        0.8.1
setuptools        75.6.0
skia-pathops      0.8.0.post2
srt               3.5.3
svgelements       1.9.6
tqdm              4.67.1
typing_extensions 4.12.2
watchdog          6.0.0
wheel             0.45.1
@uwezi
Copy link
Contributor

uwezi commented Dec 5, 2024

I guess most users would be confused if there were no linebreaks inserted in such a long text, but I don't know how Pango (the external library rendering Text() objects) actually works.

However, using Tex() instead of Text() uses LaTeX for rendering your text and gives you much better control.

from manim import *

long_string = r"{!how()'&where()&what()%how()from()<~ $what()mul(600,238);what()*mul(982,137)%~from())+~mul(550,306)@mul(331,66)>:&~ ?>%{%>why(){]mul(138,385)?~>mul(453,763)?;%mul(162,449)/[mul(629,968)&+@>:@select(){/*when()mul(868,688)?mul(160,791)>+@when()>^}'what()from()mul(473,136)?$[;:who(){mul(825,302)where()&!why()/>from()select()what()when()mul(325,635))when()^from()mul(299,8)when()how()]:mul(332,547)?!}%#when()*from():mul(532,358)#[?how()@from()mul(961,542)select()mul(115,952)-[from()()?[)mul(647,503)mul(474,649)'[mul(239,709)from()select()?^$^select()what(967,742)(where()mul(143,91)why()^,^{mul(465,739)+select()~who()/mul(244,259)mul(45,460){how()$from(),%<)who();?)where()+(select()$how()mul(752,67)mul(344,833));)&,mul(601,124)*select()*< where()]mul(375,322)when(),]when(106,699)'$mul(477,697)+]{ how() <mul(596,223)how()who(66,775){why()$who()mul(860,132)when()#};;[(mul(600,137)when()#<:mul(216,806){--mul(772,585]who()mul(373,681)what()~select()how()-mul(349,138)-},how()when()$do()?:,&/?mul(560,179) from())/who();]>%mul(626,452)mul(482,56)@{where(455,533)):$<[mul(10where(601,497)mul(426,581)who()}*]%select(907,807)>&why()mul(227,368)%mul(812,31)from()$-$:mul(694,781)!mul(737,400)>mul(692,914)^%},</^$mul(450,519)where()mul(343,778)mul(862,640)[*}>>+&mul(292,865)[[]why(){why()&!how()how()"
highlights = {
    "[45:57]": ManimColor("#83C167"),
    "[65:77]": ManimColor("#83C167"),
    "[88:100]": ManimColor("#83C167"),
    "[101:112]": ManimColor("#83C167"),
    "[130:142]": ManimColor("#83C167"),
    "[145:157]": ManimColor("#83C167"),
    "[160:172]": ManimColor("#83C167"),
    "[174:186]": ManimColor("#83C167"),
    "[209:221]": ManimColor("#83C167"),
    "[222:234]": ManimColor("#83C167"),
    "[259:271]": ManimColor("#83C167"),
    "[282:294]": ManimColor("#83C167"),
    "[336:348]": ManimColor("#83C167"),
    "[362:372]": ManimColor("#83C167"),
    "[385:397]": ManimColor("#83C167"),
    "[416:428]": ManimColor("#83C167"),
    "[443:455]": ManimColor("#83C167"),
    "[463:475]": ManimColor("#83C167"),
    "[488:500]": ManimColor("#83C167"),
    "[500:512]": ManimColor("#83C167"),
    "[514:526]": ManimColor("#83C167"),
    "[573:584]": ManimColor("#83C167"),
    "[593:605]": ManimColor("#83C167"),
    "[621:633]": ManimColor("#83C167"),
    "[633:644]": ManimColor("#83C167"),
    "[692:703]": ManimColor("#83C167"),
    "[703:715]": ManimColor("#83C167"),
    "[720:732]": ManimColor("#83C167"),
    "[752:764]": ManimColor("#83C167"),
    "[787:799]": ManimColor("#83C167"),
    "[810:822]": ManimColor("#83C167"),
    "[850:862]": ManimColor("#83C167"),
    "[874:886]": ManimColor("#83C167"),
    "[895:907]": ManimColor("#83C167"),
    "[927:939]": ManimColor("#83C167"),
    "[960:972]": ManimColor("#83C167"),
    "[997:1009]": ManimColor("#83C167"),
    "[1027:1039]": ManimColor("#83C167"),
    "[1039:1050]": ManimColor("#83C167"),
    "[1091:1103]": ManimColor("#83C167"),
    "[1134:1146]": ManimColor("#83C167"),
    "[1147:1158]": ManimColor("#83C167"),
    "[1168:1180]": ManimColor("#83C167"),
    "[1181:1193]": ManimColor("#83C167"),
    "[1194:1206]": ManimColor("#83C167"),
    "[1214:1226]": ManimColor("#83C167"),
    "[1233:1245]": ManimColor("#83C167"),
    "[1245:1257]": ManimColor("#83C167"),
    "[1264:1276]": ManimColor("#83C167"),
}


class TextScene(Scene):
    def __init__(self):
        super().__init__()

        self.text = Tex(long_string, tex_environment="verbatim")
        self.text.scale(0.1)
        self.text.align_on_border(LEFT)

        self.text_with_highlights = Text(long_string, t2c=highlights)
        self.text_with_highlights.scale(0.1)
        self.text_with_highlights.align_to(self.text, DOWN)

    def construct(self):
        self.add(self.text, self.text_with_highlights)
        self.play(self.text.animate.scale(20))
        self.wait()

Highlighting will probably needed to be handled slightly different though.

from manim import *

long_string = r"{!how()'&where()&what()%how()from()<~ $what()mul(600,238);what()*mul(982,137)%~from())+~mul(550,306)@mul(331,66)>:&~ ?>%{%>why(){]mul(138,385)?~>mul(453,763)?;%mul(162,449)/[mul(629,968)&+@>:@select(){/*when()mul(868,688)?mul(160,791)>+@when()>^}'what()from()mul(473,136)?$[;:who(){mul(825,302)where()&!why()/>from()select()what()when()mul(325,635))when()^from()mul(299,8)when()how()]:mul(332,547)?!}%#when()*from():mul(532,358)#[?how()@from()mul(961,542)select()mul(115,952)-[from()()?[)mul(647,503)mul(474,649)'[mul(239,709)from()select()?^$^select()what(967,742)(where()mul(143,91)why()^,^{mul(465,739)+select()~who()/mul(244,259)mul(45,460){how()$from(),%<)who();?)where()+(select()$how()mul(752,67)mul(344,833));)&,mul(601,124)*select()*< where()]mul(375,322)when(),]when(106,699)'$mul(477,697)+]{ how() <mul(596,223)how()who(66,775){why()$who()mul(860,132)when()#};;[(mul(600,137)when()#<:mul(216,806){--mul(772,585]who()mul(373,681)what()~select()how()-mul(349,138)-},how()when()$do()?:,&/?mul(560,179) from())/who();]>%mul(626,452)mul(482,56)@{where(455,533)):$<[mul(10where(601,497)mul(426,581)who()}*]%select(907,807)>&why()mul(227,368)%mul(812,31)from()$-$:mul(694,781)!mul(737,400)>mul(692,914)^%},</^$mul(450,519)where()mul(343,778)mul(862,640)[*}>>+&mul(292,865)[[]why(){why()&!how()how()"
highlights = {
    "[45:57]": ManimColor("#83C167"),
    "[65:77]": ManimColor("#83C167"),
    "[88:100]": ManimColor("#83C167"),
    "[101:112]": ManimColor("#83C167"),
    "[130:142]": ManimColor("#83C167"),
    "[145:157]": ManimColor("#83C167"),
    "[160:172]": ManimColor("#83C167"),
    "[174:186]": ManimColor("#83C167"),
    "[209:221]": ManimColor("#83C167"),
    "[222:234]": ManimColor("#83C167"),
    "[259:271]": ManimColor("#83C167"),
    "[282:294]": ManimColor("#83C167"),
    "[336:348]": ManimColor("#83C167"),
    "[362:372]": ManimColor("#83C167"),
    "[385:397]": ManimColor("#83C167"),
    "[416:428]": ManimColor("#83C167"),
    "[443:455]": ManimColor("#83C167"),
    "[463:475]": ManimColor("#83C167"),
    "[488:500]": ManimColor("#83C167"),
    "[500:512]": ManimColor("#83C167"),
    "[514:526]": ManimColor("#83C167"),
    "[573:584]": ManimColor("#83C167"),
    "[593:605]": ManimColor("#83C167"),
    "[621:633]": ManimColor("#83C167"),
    "[633:644]": ManimColor("#83C167"),
    "[692:703]": ManimColor("#83C167"),
    "[703:715]": ManimColor("#83C167"),
    "[720:732]": ManimColor("#83C167"),
    "[752:764]": ManimColor("#83C167"),
    "[787:799]": ManimColor("#83C167"),
    "[810:822]": ManimColor("#83C167"),
    "[850:862]": ManimColor("#83C167"),
    "[874:886]": ManimColor("#83C167"),
    "[895:907]": ManimColor("#83C167"),
    "[927:939]": ManimColor("#83C167"),
    "[960:972]": ManimColor("#83C167"),
    "[997:1009]": ManimColor("#83C167"),
    "[1027:1039]": ManimColor("#83C167"),
    "[1039:1050]": ManimColor("#83C167"),
    "[1091:1103]": ManimColor("#83C167"),
    "[1134:1146]": ManimColor("#83C167"),
    "[1147:1158]": ManimColor("#83C167"),
    "[1168:1180]": ManimColor("#83C167"),
    "[1181:1193]": ManimColor("#83C167"),
    "[1194:1206]": ManimColor("#83C167"),
    "[1214:1226]": ManimColor("#83C167"),
    "[1233:1245]": ManimColor("#83C167"),
    "[1245:1257]": ManimColor("#83C167"),
    "[1264:1276]": ManimColor("#83C167"),
}

import re
class TextScene(Scene):
    def __init__(self):
        super().__init__()

        self.text = Tex(long_string, tex_environment="verbatim")
        self.text.scale(0.1)
        self.text.align_on_border(LEFT)

        self.text_with_highlights = self.text.copy().next_to(self.text,DOWN)
        for highlight in highlights.items():
            temp = re.match(r"\[([0-9\-]{0,}):([0-9\-]{0,})\]", highlight[0])
            if temp:
                start = int(temp.group(1)) if temp.group(1) != "" else 0
                end = int(temp.group(2)) if temp.group(2) != "" else len(self.text)
                start = len(self.text) + start if start < 0 else start
                end = len(self.text) + end if end < 0 else end
                self.text_with_highlights[0][start:end].set_color(highlight[1])

    def construct(self):
        tg = VGroup(self.text,self.text_with_highlights)
        self.add(tg)
        self.play(tg.animate.shift(-tg.get_left()))
        self.play(tg.animate.scale(20,about_point=tg.get_left()))
        self.wait()
        self.play(tg.animate.shift(-tg.get_right()),run_time=3)
        self.wait()
TextScene.mp4

@Malacath-92
Copy link
Author

Malacath-92 commented Dec 5, 2024

Yes, I ended up using Tex for my case, specifically extended Tex to automatically handle the same parameters. Nonetheless, Text seems to be broken and/or uncontrollable so I decided to make the issue anyways.

I guess most users would be confused if there were no linebreaks inserted in such a long text

I'd argue most users would want control over this kinda stuff, e.g. inserting linebreaks where needed or fitting text into a given width, etc.

Also, the text just being cut off when using t2c is clearly a bug I would say, not just behavior one could argue for or against.

A similar issue exists on manimgl if anyone is interested: 3b1b/manim#1833

@behackl
Copy link
Member

behackl commented Dec 5, 2024

The canvas size used by manimpango is the scene width in pixels, but due to some additional weird scaling the dimensions don't translate back cleanly. You can observe that this is the case by rendering the scene in different quality presets (with clearing the media/texts directory inbetween to avoid caching effects), this will change the number of linebreaks.

One potential workaround is to use a smaller font size for generating the Text mobject: Text(long_string, font_size=5) fits on one line on my end. Practically I agree though, the width / height of the canvas used for generating the text should be configureable.

@uwezi
Copy link
Contributor

uwezi commented Dec 5, 2024

One potential workaround is to use a smaller font size for generating the Text mobject

actually that's not a good idea either, because the discrete pixelized rendering of Pango fails to correctly keep the distances between characters and between words at small font sizes.

@behackl
Copy link
Member

behackl commented Dec 5, 2024

Yes, true. The actual workaround, I guess, would be to do something like

with tempconfig({"pixel_width": 9000}):
    t = Text(long_string).to_edge(LEFT)

which indeed produces a one-line string for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🆕 New
Development

No branches or pull requests

3 participants