|
| 1 | +####################### |
| 2 | +Manim's building blocks |
| 3 | +####################### |
| 4 | + |
| 5 | +This document explains the building blocks of manim and will give you all the |
| 6 | +necessary tools to start producing your own videos. |
| 7 | + |
| 8 | +Essentially, manim puts at your disposal three different concepts that you can |
| 9 | +orchestrate together in order to produce mathematical animations: the |
| 10 | +**mathematical object** (or **mobject** for short) the **animation**, and the |
| 11 | +**scene**. As we will see in the following sections, each of these three |
| 12 | +concepts is implemented in manim as a separate class: the :class:`.Mobject`, |
| 13 | +:class:`.Animation`, and :class:`.Scene` classes. |
| 14 | + |
| 15 | +.. note:: It is recommended that you read the tutorials :doc:`quickstart` and |
| 16 | + :doc:`a_deeper_look` before reading this page. |
| 17 | + |
| 18 | + |
| 19 | +******** |
| 20 | +Mobjects |
| 21 | +******** |
| 22 | + |
| 23 | +Mobjects are the basic building block for all manim animations. Each class |
| 24 | +that derives from :class:`.Mobject` represents an object that can be displayed |
| 25 | +on screen. For example, simple shapes such as :class:`.Circle`, |
| 26 | +:class:`.Arrow`, and :class:`.Rectangle` are all mobjects. More complicated |
| 27 | +constructs such as :class:`.Axes`, :class:`.FunctionGraph`, or |
| 28 | +:class:`.BarChart` are mobjects as well. |
| 29 | + |
| 30 | +If you try to display on screen an instance of :class:`.Mobject`, you will only |
| 31 | +see an empty frame. The reason is that the :class:`.Mobject` class is an |
| 32 | +abstract base class of all other mobjects, i.e. it does not have any |
| 33 | +pre-determined visual shape that can be displayed on screen. It is only the |
| 34 | +skeleton of a thing that *could* be displayed. Therefore, you will rarely need |
| 35 | +to use plain instances of :class:`.Mobject`; instead you will most likely |
| 36 | +create instances of its derived classes. One of these derived classes is |
| 37 | +:class:`.VMobject`. The ``V`` stands for Vectorized Mobject. In essence, a |
| 38 | +vmobject is a mobject that uses vector `vector graphics |
| 39 | +<https://en.wikipedia.org/wiki/Vector_graphics/>`_ to be displayed. Most of |
| 40 | +the time, you will be dealing with vmobjects, though we will continue to use |
| 41 | +the term "mobject" to refer to the class of shapes that can be displayed on |
| 42 | +screen, as it is more general. |
| 43 | + |
| 44 | +.. note:: Any object that can be displayed on screen is a ``mobject``, even if |
| 45 | + it is not necessarily *mathematical* in nature. |
| 46 | + |
| 47 | +.. tip:: To see examples of classes derived from :class:`.Mobject`, see the |
| 48 | + :mod:`.geometry` module. Most of these are in fact derived from |
| 49 | + :class:`.VMobject` as well. |
| 50 | + |
| 51 | + |
| 52 | +Creating and displaying mobjects |
| 53 | +================================ |
| 54 | + |
| 55 | +As explained in :doc:`quickstart`, usually all of the code in a manim |
| 56 | +script is put inside the :meth:`.construct` method of a :class:`.Scene` class. |
| 57 | +To display a mobject on the screen, call the :meth:`~.Scene.add` method of the |
| 58 | +containing :class:`.Scene`. This is the principal way of displaying a mobject |
| 59 | +on the screen when it is not being animated. To remove a mobject from the |
| 60 | +screen, simply call the :meth:`~.Scene.remove` method from the containing |
| 61 | +:class:`.Scene`. |
| 62 | + |
| 63 | +.. manim:: CreatingMobjects |
| 64 | + :display_source: |
| 65 | + :quality: medium |
| 66 | + |
| 67 | + class CreatingMobjects(Scene): |
| 68 | + def construct(self): |
| 69 | + circle = Circle() |
| 70 | + self.add(circle) |
| 71 | + self.wait(1) |
| 72 | + self.remove(circle) |
| 73 | + self.wait(1) |
| 74 | + |
| 75 | + |
| 76 | +Placing mobjects |
| 77 | +================ |
| 78 | + |
| 79 | +Let's define a new :class:`.Scene` called ``Shapes`` and :meth:`~.Scene.add` |
| 80 | +some mobjects to it. This script generates a static picture that displays a |
| 81 | +circle, a square, and a triangle: |
| 82 | + |
| 83 | +.. manim:: Shapes |
| 84 | + :display_source: |
| 85 | + :quality: medium |
| 86 | + |
| 87 | + class Shapes(Scene): |
| 88 | + def construct(self): |
| 89 | + circle = Circle() |
| 90 | + square = Square() |
| 91 | + triangle = Triangle() |
| 92 | + |
| 93 | + circle.shift(LEFT) |
| 94 | + square.shift(UP) |
| 95 | + triangle.shift(RIGHT) |
| 96 | + |
| 97 | + self.add(circle, square, triangle) |
| 98 | + self.wait(1) |
| 99 | + |
| 100 | +By default, mobjects are placed at the center of coordinates, or *origin*, when |
| 101 | +they are first created. They are also given some default colors. Further, the |
| 102 | +``Shapes`` scene places the mobjects by using the :meth:`.shift` method. The |
| 103 | +square is shifted one unit in the ``UP`` direction from the origin, while the |
| 104 | +circle and triangle are shifted one unit ``LEFT`` and ``RIGHT``, respectively. |
| 105 | + |
| 106 | +.. attention:: Unlike other graphics software, manim places the center of |
| 107 | + coordinates at the center of the screen. The positive vertical |
| 108 | + direction is up, and the positive horizontal direction is right. |
| 109 | + See also the constants ``ORIGIN``, ``UP``, ``DOWN``, ``LEFT``, |
| 110 | + ``RIGHT``, and others, defined in the :mod:`.constants` module. |
| 111 | + |
| 112 | +There are many other possible ways to place mobjects on the screen, for example |
| 113 | +:meth:`.move_to`, :meth:`.next_to`, and :meth:`.align_to`. The next scene |
| 114 | +``MobjectPlacement`` uses all three. |
| 115 | + |
| 116 | +.. manim:: MobjectPlacement |
| 117 | + :display_source: |
| 118 | + :quality: medium |
| 119 | + |
| 120 | + class MobjectPlacement(Scene): |
| 121 | + def construct(self): |
| 122 | + circle = Circle() |
| 123 | + square = Square() |
| 124 | + triangle = Triangle() |
| 125 | + |
| 126 | + # place the circle two units left from the origin |
| 127 | + circle.move_to(LEFT * 2) |
| 128 | + # place the square to the left of the circle |
| 129 | + square.next_to(circle, LEFT) |
| 130 | + # align the left border of the triangle to the left border of the circle |
| 131 | + triangle.align_to(circle, LEFT) |
| 132 | + |
| 133 | + self.add(circle, square, triangle) |
| 134 | + self.wait(1) |
| 135 | + |
| 136 | +The :meth:`.move_to` method uses absolute units (measured relative to the |
| 137 | +``ORIGIN``), while :meth:`.next_to` uses relative units (measured from the |
| 138 | +mobject passed as the first argument). :meth:`align_to` uses ``LEFT`` not as |
| 139 | +measuring units but as a way to determine the border to use for alignment. The |
| 140 | +coordinates of the borders of a mobject are determined using an imaginary |
| 141 | +bounding box around it. |
| 142 | + |
| 143 | +.. tip:: Many methods in manim can be chained together. For example the two |
| 144 | + lines |
| 145 | + |
| 146 | + .. code-block:: python |
| 147 | +
|
| 148 | + square = Square() |
| 149 | + square.shift(LEFT) |
| 150 | +
|
| 151 | + can be replaced by |
| 152 | + |
| 153 | + .. code-block:: python |
| 154 | +
|
| 155 | + square = Square().shift(LEFT) |
| 156 | +
|
| 157 | + Technically, this is possible because most methods calls return the modified mobject. |
| 158 | + |
| 159 | + |
| 160 | +Styling mobjects |
| 161 | +================ |
| 162 | + |
| 163 | +The following scene changes the default aesthetics of the mobjects. |
| 164 | + |
| 165 | +.. manim:: MobjectStyling |
| 166 | + :display_source: |
| 167 | + :quality: medium |
| 168 | + |
| 169 | + class MobjectStyling(Scene): |
| 170 | + def construct(self): |
| 171 | + circle = Circle().shift(LEFT) |
| 172 | + square = Square().shift(UP) |
| 173 | + triangle = Triangle().shift(RIGHT) |
| 174 | + |
| 175 | + circle.set_stroke(color=GREEN, width=20) |
| 176 | + square.set_fill(YELLOW, opacity=1.0) |
| 177 | + triangle.set_fill(PINK, opacity=0.5) |
| 178 | + |
| 179 | + self.add(circle, square, triangle) |
| 180 | + self.wait(1) |
| 181 | + |
| 182 | +This scene uses two of the main functions that change the visual style of a |
| 183 | +mobject: :meth:`.set_stroke` and :meth:`.set_fill`. The former changes the |
| 184 | +visual style of the mobject's border while the latter changes the style of the |
| 185 | +interior. By default, most mobjects have a fully transparent interior so you |
| 186 | +must specify the ``opacity`` parameter in order to display the color. An |
| 187 | +opacity of ``1.0`` means fully opaque, while ``0.0`` means fully transparent. |
| 188 | + |
| 189 | +Only instances of :class:`.VMobject` implement :meth:`.set_stroke` and |
| 190 | +:meth:`.set_fill`. Instances of :class:`.Mobject` implement |
| 191 | +:meth:`.~Mobject.set_color` instead. The vast majority of pre-defined classes |
| 192 | +are derived from :class:`.VMobject` so it is usually safe to assume that you |
| 193 | +have access to :meth:`.set_stroke` and :meth:`.set_fill`. |
| 194 | + |
| 195 | + |
| 196 | +Mobject on-screen order |
| 197 | +======================= |
| 198 | + |
| 199 | +The next scene is exactly the same as the ``MobjectStyling`` scene from the |
| 200 | +previous section, except for exactly one line. |
| 201 | + |
| 202 | +.. manim:: MobjectZOrder |
| 203 | + :display_source: |
| 204 | + :quality: medium |
| 205 | + |
| 206 | + class MobjectZOrder(Scene): |
| 207 | + def construct(self): |
| 208 | + circle = Circle().shift(LEFT) |
| 209 | + square = Square().shift(UP) |
| 210 | + triangle = Triangle().shift(RIGHT) |
| 211 | + |
| 212 | + circle.set_stroke(color=GREEN, width=20) |
| 213 | + square.set_fill(YELLOW, opacity=1.0) |
| 214 | + triangle.set_fill(PINK, opacity=0.5) |
| 215 | + |
| 216 | + self.add(triangle, square, circle) |
| 217 | + self.wait(1) |
| 218 | + |
| 219 | +The only difference here (besides the scene name) is the order in which the |
| 220 | +mobjects are added to the scene. In ``MobjectStyling``, we added them as |
| 221 | +``add(circle, square, triangle)``, whereas in ``MobjectZOrder`` we add them as |
| 222 | +``add(triangle, square, circle)``. |
| 223 | + |
| 224 | +As you can see, the order of the arguments of :meth:`~.Scene.add` determines |
| 225 | +the order that the mobjects are displayed on screen, with the left-most |
| 226 | +arguments being put in the back. |
| 227 | + |
| 228 | + |
| 229 | +********** |
| 230 | +Animations |
| 231 | +********** |
| 232 | + |
| 233 | +At the heart of manim is animation. Generally, you can add an animation to |
| 234 | +your scene by calling the :meth:`~.Scene.play` method. |
| 235 | + |
| 236 | +.. manim:: SomeAnimations |
| 237 | + :display_source: |
| 238 | + :quality: medium |
| 239 | + |
| 240 | + class SomeAnimations(Scene): |
| 241 | + def construct(self): |
| 242 | + square = Square() |
| 243 | + self.add(square) |
| 244 | + |
| 245 | + # some animations display mobjects, ... |
| 246 | + self.play(FadeIn(square)) |
| 247 | + |
| 248 | + # ... some move or rotate mobjects around... |
| 249 | + self.play(Rotate(square, PI/4)) |
| 250 | + |
| 251 | + # some animations remove mobjects from the screen |
| 252 | + self.play(FadeOut(square)) |
| 253 | + |
| 254 | + self.wait(1) |
| 255 | + |
| 256 | +Put simply, animations are procedures that interpolate between two mobjects. |
| 257 | +For example, :code:`FadeIn(square)` starts with a fully transparent version of |
| 258 | +:code:`square` and ends with a fully opaque version, interpolating between them |
| 259 | +by gradually increasing the opacity. :class:`.FadeOut` works in the opposite |
| 260 | +way: it interpolates from fully opaque to fully transparent. As another |
| 261 | +example, :class:`.Rotate` starts with the mobject passed to it as argument, and |
| 262 | +ends with the same object but rotated by a certain amount, this time |
| 263 | +interpolating the mobject's angle instead of its opacity. |
| 264 | + |
| 265 | + |
| 266 | +Animating methods |
| 267 | +================= |
| 268 | + |
| 269 | +Any property of a mobject that can be changed can be animated. In fact, any |
| 270 | +method that changes a mobject's property can be used as an animation, through |
| 271 | +the use of :class:`.ApplyMethod`. |
| 272 | + |
| 273 | +.. manim:: ApplyMethodExample |
| 274 | + :display_source: |
| 275 | + :quality: medium |
| 276 | + |
| 277 | + class ApplyMethodExample(Scene): |
| 278 | + def construct(self): |
| 279 | + square = Square().set_fill(RED, opacity=1.0) |
| 280 | + self.add(square) |
| 281 | + |
| 282 | + # animate the change of color |
| 283 | + self.play(ApplyMethod(square.set_fill, WHITE)) |
| 284 | + self.wait(1) |
| 285 | + |
| 286 | + # animate the change of position |
| 287 | + self.play(ApplyMethod(square.shift, UP)) |
| 288 | + self.wait(1) |
| 289 | + |
| 290 | +:meth:`.ApplyMethod` receives one mandatory argument which is the method of the |
| 291 | +mobject to animate (e.g. :code:`square.set_fill` or :code:`square.shift`), and |
| 292 | +any number of optional arguments which are then passed to the method call. For |
| 293 | +example, :code:`ApplyMethod(square.shift, UP)` executes |
| 294 | +:code:`square.shift(UP)`, but animates it instead of applying it immediately. |
| 295 | + |
| 296 | +Animation run time |
| 297 | +================== |
| 298 | + |
| 299 | +By default, any animation passed to :meth:`play` lasts for exactly one second. |
| 300 | +Use the :code:`run_time` argument to control the duration. |
| 301 | + |
| 302 | +.. manim:: RunTime |
| 303 | + :display_source: |
| 304 | + :quality: medium |
| 305 | + |
| 306 | + class RunTime(Scene): |
| 307 | + def construct(self): |
| 308 | + square = Square() |
| 309 | + self.add(square) |
| 310 | + self.play(ApplyMethod(square.shift, UP), run_time=3) |
| 311 | + self.wait(1) |
| 312 | + |
| 313 | + |
| 314 | +****** |
| 315 | +Scenes |
| 316 | +****** |
| 317 | + |
| 318 | +The :class:`.Scene` class is the connective tissue of manim. Every mobject has |
| 319 | +to be :meth:`added <.Scene.add>` to a scene to be displayed, or :meth:`removed |
| 320 | +<.Scene.remove>` from it to cease being displayed. Every animation has to be |
| 321 | +:meth:`played <.Scene.play>` by a scene, and every time interval where no |
| 322 | +animation occurs is determined by a call to :meth:`~.Scene.wait`. All of the |
| 323 | +code of your video must be contained in the :meth:`~.Scene.construct` method of |
| 324 | +a class that derives from :class:`.Scene`. Finally, a single file may contain |
| 325 | +multiple :class:`.Scene` subclasses in the event that multiple scenes are to be |
| 326 | +rendered at the same time. |
0 commit comments