Skip to content

Instantly share code, notes, and snippets.

@abul4fia
Last active May 6, 2024 20:23
Show Gist options
  • Save abul4fia/47a375c8899dd830ce17df5c48b388bd to your computer and use it in GitHub Desktop.
Save abul4fia/47a375c8899dd830ce17df5c48b388bd to your computer and use it in GitHub Desktop.
Working with sections in manim

Working with sections in manim

Manim provides out of the box the possibility of marking parts of your scene as separate sections, by using self.next_section(). This will cause a separate video to be rendered for each section, and then all of them will be stitched together for the final result. The final result will be the same as if you hadn't used sections at all.

But the use of sections allows you to mark some of them with the parameter skip_animations=True, and in that case, that section will not be rendered. The code of the section will be run anyway (because it may be defining objects or variables needed in following sections), but for every self.play(), only the final frame will be computed (and not rendered). This ensures that, at the end of the section, all objects are at the same positions as if the animations were played, but skipping the actual rendering significantly reduces the time required to run the code.

In addition, the resulting video when skip_animations=True is used will be shorter because the skipped sections will not appear in it.

This allows for a faster workflow because you can skip all sections except the one you are currently working on. That will increase the speed of the rendering, and also will make it easier to see the result because you don't need to watch the whole video again (or fast-forward it to the point of interest).

However, I find the syntax used by Manim for sections a bit clumsy, so in this gist, I propose two new approaches:

  • Make the sections Python context managers. This allows you to write code like the one shown in example1.py.
  • Make the sections independent methods of the Scene. This allows you to write code like the one shown in example2.py.

Both approaches keep the advantages of working with Manim sections but provide a more convenient user interface, especially in terms of navigating the code. Using either of these approaches, your code is visually indented inside each section, which allows for easier navigation. In addition, your editor can fold entire sections, hiding them while you are not working on them.

from manim import *
from section_context import new_section
class Test(Scene):
def construct(self):
with self.new_section("Initialize variables"):
text = Text("Hello world")
text2 = Text("Bye world")
text.shift(UP)
text2.shift(DOWN)
with self.new_section("Transform one text into another"):
self.play(Write(text))
self.wait()
self.play(Transform(text, text2))
self.wait()
with self.new_section("Fade out text"):
self.play(FadeOut(text))
self.wait()
from subscenes_class import SceneWithSubscenes
# Then you inherit from SceneWithSubscenes and instead of writing construct() you write
# separate methods. All those which start with "subscene" will be run, in the
# order they appear in the source. Those that declare `skip=True` do not produce
# animations but are run nevertheless, because they can create objects that
# are required later. Also manim renders the last frame of the skipped ones
# to allow the animation to "continue" after in the next one
class Test(SceneWithSubscenes):
def subscene1(self):
self.c = Circle()
self.play(Create(self.c))
def subscene2(self, skip=True):
self.text = Text("This is a test")
self.text.next_to(self.c, DOWN)
self.play(Write(self.text))
def subscene3(self):
self.play(self.text.animate.next_to(self.c, UP))
from manim import *
from contextlib import contextmanager
from typing import Generator
@contextmanager
def new_section(
self,
name: str = "unnamed",
type: str = DefaultSectionType.NORMAL,
skip_animations: bool = False,
) -> Generator[None, None, None]:
print(f"Entering section: {name}")
self.next_section(name, type, skip_animations)
yield
print(f"Exiting section: {name}")
Scene.new_section = new_section
from manim import *
import inspect
class SceneWithSubscenes(Scene):
def construct(self):
members = inspect.getmembers(type(self), predicate=inspect.isfunction)
members = [ m for m in members if m[0].startswith("subscene") ]
members = sorted(members, key=lambda x: inspect.getsourcelines(x[1])[1])
for name, method in members:
skip = False
s = inspect.signature(method)
if 'skip' in s.parameters:
skip = s.parameters['skip'].default
self.next_section(name, skip_animations=skip)
print("Skipping" if skip else "Running", name)
method(self)
self.wait()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment