Box mini-project#
[1]:
from manim import *
config.media_embed = True; config.media_width = "100%"
_RV = "-v WARNING -qm --progress_bar None --disable_caching Example"
_RI = "-v WARNING -s --progress_bar None --disable_caching Example"
Manim Community v0.15.1
Target#
Let’s create the following animation, we will create the Box object and define two animations OpenBox
and CloseBox
, which will only work for this particular VMobject
.

Box definition#
[2]:
ANGLE_OPEN_BOX = PI * 2.3 / 2
class Box(VMobject):
def __init__(self,
width=6,
height=3,
cover_buff=None,
fill_opacity=0.5,
fill_color="#cdab7e",
stroke_color="#D2B48C",
open=False,
**kwargs):
super().__init__(fill_opacity=fill_opacity,
fill_color=fill_color,
stroke_color=stroke_color,
**kwargs)
if cover_buff is None:
cover_buff = width * 0.8 / 2
else:
assert (cover_buff < width/2), "cover_buff >= width/2"
main_rect = Square(width,color=RED)
# 0.25 is Up right corner
# 1 is Up left corner, remember vmob.point_from_proportion(alpha)
self.base_box = main_rect.get_subcurve(0.25,1).match_style(self)
self.base_box.stretch_to_fit_height(height)
self._open = open
self.new_stroke = self.base_box.get_stroke_width() * 4
self.left_cover = self.get_cover(RIGHT,cover_buff)
self.right_cover = self.get_cover(LEFT,cover_buff)
covers = VGroup(self.left_cover,self.right_cover).match_color(self)
self.add(self.base_box, *covers)
def get_cover(self, side, cover_buff):
pivot_index = 0 if side[0] == 1 else -1
pivot = self.base_box.get_all_points()[pivot_index]
cover = Line(pivot, pivot + side * cover_buff, stroke_width=self.new_stroke)
if self._open:
cover.rotate(ANGLE_OPEN_BOX*side[0],about_point=pivot)
return cover
Animations#
[ ]:
class OpenBox(Animation):
def __init__(self, box, open=True, run_time=3, **kwargs):
# Assert open/close
_open = "open" if open else "close"
assert (box._open != open), f"box is already {_open}"
# Define sign
self._sign = 1 if box._open else -1
super().__init__(box, run_time=run_time, **kwargs)
def interpolate_mobject(self, alpha):
self.mobject.become(self.starting_mobject)
self.rotate_cover(self.mobject.right_cover, alpha)
self.rotate_cover(self.mobject.left_cover, alpha, -1)
def rotate_cover(self, cover, alpha, sign=1):
cover.rotate(
sign * self._sign * self.rate_func(alpha) * ANGLE_OPEN_BOX,
about_point=cover.get_start(),
about_edge=cover.get_start(),
)
def clean_up_from_scene(self, scene):
# change open to close or close to open at the end
self.mobject._open = not self.mobject._open
super().clean_up_from_scene(scene)
When creating animations or objects that are going to be reused, it is a good idea that we do unnecessary redefinitions.
[4]:
class CloseBox(OpenBox):
def __init__(self, box, open=False, **kwargs):
super().__init__(box, open, **kwargs)
[5]:
class Example(Scene):
def construct(self):
b = Box()
self.play(DrawBorderThenFill(b))
self.play(OpenBox(b))
self.play(CloseBox(b))
self.play(OpenBox(b))
self.play(CloseBox(b))
self.wait()
%manim $_RV
Assert test#
If the box is open from the beginning then it should give us an error:
[6]:
class Example(Scene):
def construct(self):
b = Box(open=True)
self.add(b)
self.play(OpenBox(b))
self.wait()
%manim $_RV
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Input In [6], in <cell line: 8>()
5 self.play(OpenBox(b))
6 self.wait()
----> 8 get_ipython().run_line_magic('manim', '$_RV')
File ~/GitHubProjects/mce-inter/docs/venv/lib/python3.9/site-packages/IPython/core/interactiveshell.py:2294, in InteractiveShell.run_line_magic(self, magic_name, line, _stack_depth)
2292 kwargs['local_ns'] = self.get_local_scope(stack_depth)
2293 with self.builtin_trap:
-> 2294 result = fn(*args, **kwargs)
2295 return result
File ~/GitHubProjects/mce-inter/docs/venv/lib/python3.9/site-packages/manim/utils/ipython_magic.py:148, in ManimMagic.manim(self, line, cell, local_ns)
146 SceneClass = local_ns[config["scene_names"][0]]
147 scene = SceneClass(renderer=renderer)
--> 148 scene.render()
149 finally:
150 # Shader cache becomes invalid as the context is destroyed
151 shader_program_cache.clear()
File ~/GitHubProjects/mce-inter/docs/venv/lib/python3.9/site-packages/manim/scene/scene.py:222, in Scene.render(self, preview)
220 self.setup()
221 try:
--> 222 self.construct()
223 except EndSceneEarlyException:
224 pass
Input In [6], in Example.construct(self)
3 b = Box(open=True)
4 self.add(b)
----> 5 self.play(OpenBox(b))
6 self.wait()
Input In [3], in OpenBox.__init__(self, box, open, run_time, **kwargs)
2 def __init__(self, box, open=True, run_time=3, **kwargs):
3 # Assert open/close
4 _open = "open" if open else "close"
----> 5 assert (box._open != open), f"box is already {_open}"
6 # Define sign
7 self._sign = 1 if box._open else -1
AssertionError: box is already open
It works, using assert
is a good idea when we need our animations to have certain structures.