Skip to content

Conversation

@eulertour
Copy link
Member

@eulertour eulertour commented Mar 31, 2021

Changelog / Overview

Added OpenGL-compatible text and SVG mobjects

Motivation

Add text and svg mobjects that work with the OpenGL renderer

Explanation for Changes

Created opengl_text_mobject.py and opengl_svg_path.py that are mostly copied from the Cairo versions. opengl_svg_path.py and opengl_svg_mobject.py have just enough information to allow reuse of our existing SVG / Text logic.

Testing Status

None, but I'm willing to add some if I can figure out how to generate test data for OpenGL scenes.

Further Comments

Regarding the amount of duplicated code that this and the earlier OpenGL changes introduced, I don't know if it'll ever make sense to combine our Cairo and OpenGL logic. If so, it will have to be done very carefully.

Checklist

  • I have read the Contributing Guidelines
  • I have written a descriptive PR title (see top of PR template for examples)
  • I have written a changelog entry for the PR or deem it unnecessary
  • My new functions/classes either have a docstring or are private
  • My new functions/classes have tests added and (optional) examples in the docs
  • My new documentation builds, looks correctly formatted, and adds no additional build warnings

Reviewer Checklist

  • The PR title is descriptive enough
  • The PR is labeled correctly
  • The changelog entry is completed if necessary
  • Newly added functions/classes either have a docstring or are private
  • Newly added functions/classes have tests added and (optional) examples in the docs
  • Newly added documentation builds, looks correctly formatted, and adds no additional build warnings

@eulertour eulertour mentioned this pull request Mar 31, 2021
12 tasks
Copy link
Member

@naveen521kk naveen521kk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks neat thanks. Tried it locally and it works. While I wonder something isn't working out

class WriteStuff(Scene):
    def construct(self):
        example_text = OpenGLTex("This is a some text", tex_to_color_map={"text": YELLOW})
        example_tex = OpenGLMathTex(
            "\\sum_{k=1}^\\infty {1 \\over k^2} = {\\pi^2 \\over 6}",
        )
        group = OpenGLVGroup(example_text, example_tex)
        group.arrange(DOWN)
        group.width = config["frame_width"] - 2 * LARGE_BUFF

        self.play(Write(example_text))
        self.play(Write(example_tex))
        self.wait()

same as in basic.py. This creates a video like

WriteStuff.mp4

while when using Cairo renderer from basic.py it gives

WriteStuff.mp4

Is this difference indented?

@markromanmiller markromanmiller mentioned this pull request Apr 5, 2021
12 tasks
@eulertour
Copy link
Member Author

The extra argument to Mobject.rotate was causing the characters to be displaced but I don't know why. Graphical unit testing is also difficult since we can't reproduce the output of the fonts on the GHA servers (this is consistent with cairo rendering).

@kilacoda-old
Copy link
Contributor

WriteStuff works for me as well. However, when I tried to create a custom mobject subclassed from OpenGLMathTex (basically a ChemObject renamed to OpenGLChemObject), for one input string, I get a long traceback which ends up in SVGMobject.handle_transforms:

Traceback
manim render .\opengl_test.py -pqm --renderer=opengl
Manim Community v0.5.0
1: ChanimTest
2: WriteStuff

Choose number corresponding to desired scene/arguments.
(Use comma separated list for multiple entries)
Choice(s):  1
Traceback (most recent call last):
╭──────────────────────────────────────────────────────────────────────────────────────╮
│ File "C:\CodeProjects\Python\Manim\manim\manim\cli\render\commands.py", line 115, in │
│render                                                                                │
│    112             try:                                                              │
│    113                 renderer = OpenGLRenderer()                                   │
│    114                 scene = SceneClass(renderer)                                  │
│  ❱ 115                 scene.render()                                                │
│    116             except Exception:                                                 │
│    117                 console.print_exception()                                     │
│    118     elif config.renderer == "webgl":                                          │
│ File "C:\CodeProjects\Python\Manim\manim\manim\scene\scene.py", line 179, in render  │
│     176         """                                                                  │
│     177         self.setup()                                                         │
│     178         try:                                                                 │
│  ❱  179             self.construct()                                                 │
│     180         except EndSceneEarlyException:                                       │
│     181             pass                                                             │
│     182         self.tear_down()                                                     │
│ File "opengl_test.py", line 21, in construct                                         │
│    18                                                                                │
│    19 class ChanimTest(Scene):                                                       │
│    20     def construct(self):                                                       │
│  ❱ 21         c = OpenGLChemObject(Benzene_Diazonium_Chloride)                       │
│    22                                                                                │
│    23         self.play(Write(c))                                                    │
│    24         self.wait()                                                            │
│ File "c:\codeprojects\python\manim\chanim\chanim\chem_objects.py", line 143, in      │
│__init__                                                                              │
│    140             node_style=node_style,                                            │
│    141             bond_style=bond_style,                                            │
│    142         )                                                                     │
│  ❱ 143         super().__init__(                                                     │
│    144             "\\chemfig{%s}" % chem_code,                                      │
│    145             stroke_width=stroke_width,                                        │
│    146             tex_template=self.template,                                       │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\opengl_tex_mobject.py",   │
│line 400, in __init__                                                                 │
│    397         self.tex_environment = tex_environment                                │
│    398         tex_strings = self.break_up_tex_strings(tex_strings)                  │
│    399         self.tex_strings = tex_strings                                        │
│  ❱ 400         OpenGLSingleStringMathTex.__init__(                                   │
│    401             self,                                                             │
│    402             self.arg_separator.join(tex_strings),                             │
│    403             tex_environment=self.tex_environment,                             │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\opengl_tex_mobject.py",   │
│line 237, in __init__                                                                 │
│    234             environment=self.tex_environment,                                 │
│    235             tex_template=self.tex_template,                                   │
│    236         )                                                                     │
│  ❱ 237         OpenGLSVGMobject.__init__(                                            │
│    238             self,                                                             │
│    239             file_name=file_name,                                              │
│    240             should_center=should_center,                                      │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\opengl_svg_mobject.py",   │
│line 31, in __init__                                                                  │
│    28             "should_subdivide_sharp_curves": should_subdivide_sharp_curves,    │
│    29             "should_remove_null_curves": should_remove_null_curves,            │
│    30         }                                                                      │
│  ❱ 31         OpenGLVMobject.__init__(                                               │
│    32             self, stroke_width=stroke_width, fill_opacity=fill_opacity, **kwarg│
│    33         )                                                                      │
│    34         self.move_into_position(width, height)                                 │
│ File                                                                                 │
│"C:\CodeProjects\Python\Manim\manim\manim\mobject\types\opengl_vectorized_mobject.py",│
│line 118, in __init__                                                                 │
│     115                                                                              │
│     116         self.needs_new_triangulation = True                                  │
│     117         self.triangulation = np.zeros(0, dtype="i4")                         │
│  ❱  118         super().__init__(**kwargs)                                           │
│     119         self.refresh_unit_normal()                                           │
│     120                                                                              │
│     121     def get_group_class(self):                                               │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\opengl_mobject.py", line 91,  │
│in __init__                                                                           │
│      88         self.init_uniforms()                                                 │
│      89         self.init_updaters()                                                 │
│      90         # self.init_event_listners()                                         │
│  ❱   91         self.init_points()                                                   │
│      92         self.init_colors()                                                   │
│      93                                                                              │
│      94         self.shader_indices = None                                           │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\opengl_svg_mobject.py",   │
│line 37, in init_points                                                               │
│    34         self.move_into_position(width, height)                                 │
│    35                                                                                │
│    36     def init_points(self):                                                     │
│  ❱ 37         self.generate_points()                                                 │
│    38                                                                                │
│    39     def path_string_to_mobject(self, path_string: str, style: dict):           │
│    40         return OpenGLSVGPathMobject(                                           │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\svg_mobject.py", line 128,│
│in generate_points                                                                    │
│    125         """                                                                   │
│    126         doc = minidom_parse(self.file_path)                                   │
│    127         for svg in doc.getElementsByTagName("svg"):                           │
│  ❱ 128             mobjects = self.get_mobjects_from(svg, {})                        │
│    129             if self.unpack_groups:                                            │
│    130                 self.add(*mobjects)                                           │
│    131             else:                                                             │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\svg_mobject.py", line 174,│
│in get_mobjects_from                                                                  │
│    171             pass  # TODO, handle style                                        │172         elif element.tagName in ["g", "svg", "symbol", "defs"]:               │
│    173             result += it.chain(                                               │
│  ❱ 174                 *[                                                            │
│    175                     self.get_mobjects_from(                                   │
│    176                         child, style, within_defs=within_defs or is_defs      │
│    177                     )                                                         │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\svg_mobject.py", line 175,│
│in <listcomp>                                                                         │
│    172         elif element.tagName in ["g", "svg", "symbol", "defs"]:               │
│    173             result += it.chain(                                               │
│    174                 *[                                                            │
│  ❱ 175                     self.get_mobjects_from(                                   │
│    176                         child, style, within_defs=within_defs or is_defs      │
│    177                     )                                                         │
│    178                     for child in element.childNodes                           │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\svg_mobject.py", line 174,│
│in get_mobjects_from                                                                  │
│    171             pass  # TODO, handle style                                        │172         elif element.tagName in ["g", "svg", "symbol", "defs"]:               │
│    173             result += it.chain(                                               │
│  ❱ 174                 *[                                                            │
│    175                     self.get_mobjects_from(                                   │
│    176                         child, style, within_defs=within_defs or is_defs      │
│    177                     )                                                         │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\svg_mobject.py", line 175,│
│in <listcomp>                                                                         │
│    172         elif element.tagName in ["g", "svg", "symbol", "defs"]:               │
│    173             result += it.chain(                                               │
│    174                 *[                                                            │
│  ❱ 175                     self.get_mobjects_from(                                   │
│    176                         child, style, within_defs=within_defs or is_defs      │
│    177                     )                                                         │
│    178                     for child in element.childNodes                           │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\svg_mobject.py", line 203,│
│in get_mobjects_from                                                                  │
│    200                                                                               │
│    201         result = [m for m in result if m is not None]                         │
│    202         if config["renderer"] == "opengl":                                    │
│  ❱ 203             self.handle_transforms(element, OpenGLVGroup(*result))            │
│    204         else:                                                                 │
│    205             self.handle_transforms(element, VGroup(*result))                  │
│    206         if len(result) > 1 and not self.unpack_groups:                        │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\svg\svg_mobject.py", line 513,│
│in handle_transforms                                                                  │
│    510                 matrix[:, 1] *= -1                                            │
│    511                                                                               │
│    512                 for mob in mobject.family_members_with_points():              │
│  ❱ 513                     mob.points = np.dot(mob.points, matrix)                   │
│    514                 mobject.shift(x * RIGHT + y * UP)                             │
│    515                                                                               │
│    516             elif op_name == "scale":                                          │
│ File "C:\CodeProjects\Python\Manim\manim\manim\mobject\mobject.py", line 526, in     │
│__getattr__                                                                           │
│     523             return types.MethodType(setter, self)                            │
│     524                                                                              │
│     525         # Unhandled attribute, therefore error                               │
│  ❱  526         raise AttributeError(f"{type(self).__name__} object has no attribute │
│     527                                                                              │
│     528     @property                                                                │
│     529     def width(self):                                                         │
╰──────────────────────────────────────────────────────────────────────────────────────╯
AttributeError: OpenGLTexSymbol object has no attribute 'points'
Code used
class ChanimTest(Scene):
    def construct(self):
        Benzene_Diazonium_Chloride = "*6(-=-=(-\\chemabove{N_2}{\quad\scriptstyle+}\\chemabove{Cl}{\quad\scriptstyle-})-=-)"
        c = OpenGLChemObject(Benzene_Diazonium_Chloride)

        self.play(Write(c))
        self.wait()

while for another, the output is pretty jagged:

Code 2
class ChanimTest(Scene):
    def construct(self):
        Benzene = "*6(-=-=-=)"
        c = OpenGLChemObject(Benzene)

        self.play(Write(c))
        self.wait()
Output rendered with `-pqk`
ChanimTest.mp4

I tested it out on some other input strings as well and got the same error. From what it seems, it breaks whenever it encounters an SVG with notmal text apparently. Is there a way around this?

@eulertour
Copy link
Member Author

I added a missing check for the crash. The other problem might be solvable if you upload an SVG to compare against with cairo.

@kilacoda-old
Copy link
Contributor

Great! The fix works.

Here are two videos comparing output from both renderers both rendered with -k, i.e. at 2160p60:
OpenGL:

ChanimTest.mp4

Cairo:

ChanimTest2.mp4

and here's the SVG being used:

benzene_SVG.zip

@eulertour
Copy link
Member Author

I don't get the jagged video, what animation are you using? I generated this with Create.

Test.mp4

@kilacoda-old
Copy link
Contributor

I used Write.

@eulertour
Copy link
Member Author

I get this with Write

Test.mp4

@kilacoda-old
Copy link
Contributor

Weird... I can't get output like that from either Write or Create, even when explicitly using an OpenGLSVGMobject instead of the OpenGLChemObject. I still get the jagged, thin-lined version I sent earlier.

@Darylgolden
Copy link
Member

@Darylgolden
Copy link
Member

@kilacoda Are you able to render the rest of the OpenGL example scenes correctly? Also, it seems that the Cairo renderer renders the SVG with a black stroke color while the OpenGL renderer renders it white, is this intended?

@kilacoda-old
Copy link
Contributor

@kilacoda Are you able to render the rest of the OpenGL example scenes correctly?

I tried them out just now, and apparently InteractiveDevelopment isn't working/not displaying anything in the preview window.

What code are you guys using exactly to render the SVG btw? I can try using that as is.

@Darylgolden
Copy link
Member

from manim import *
from manim.opengl import *

# config["background_color"] = WHITE

class Test(Scene):
    def construct(self):
        self.play(
            Create(OpenGLSVGMobject("benzene.svg"))
        )

For Cairo I uncommented the background color line, since for some reason the colors are opposite on both renderers.

@eulertour
Copy link
Member Author

It's pretty clear that the problem being discussed now isn't related to this PR, so can this be approved and the debugging moved into #1240?

@eulertour eulertour merged commit c71b73b into ManimCommunity:master Apr 11, 2021
@kilacoda-old kilacoda-old added new feature Enhancement specifically adding a new feature (feature request should be used for issues instead) SVG labels Apr 11, 2021
@kilacoda-old
Copy link
Contributor

There weren't any tests for this PR, is @ManimCommunity/tests okay with that?

@kilacoda-old
Copy link
Contributor

Also, this seems to not have been squashed and merged...

@eulertour eulertour deleted the opengl-tex branch April 12, 2021 20:51
@behackl behackl mentioned this pull request May 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Enhancement specifically adding a new feature (feature request should be used for issues instead) SVG

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants