From ba23bc5510fd57a29caa327333221276241f44c7 Mon Sep 17 00:00:00 2001 From: sahilmakhijani <49190295+sahilmakhijani@users.noreply.github.com> Date: Sun, 1 Aug 2021 13:59:21 +0530 Subject: [PATCH] Custom Animation Docs (#1739) * Add custom animation section to docs building blocks page * Add CountingScene Example for Custom Animation Docs * Fix "upto" typo * Scale DecimalNumber * Small tweaks to Custom Animation example * Add updater to decimal number to remain always in center in CountingScene * Rename mob to number in CountingScene * Add reference classes, methods and functions to Custom Animation Example * Make the quality medium for Custom Animation Example * Add the explaination for Custom Animation Example * Remove rate functions reference * Minor tweaks to Custom Animation Example * Incorporate review changes * Add more explaination to the logic behind calculating value * Incorporate review changes * Fix references * Remove quality medium * Add docstring to interpolate_mobject method of Animation class * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review Co-authored-by: Benjamin Hackl Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- docs/source/tutorials/building_blocks.rst | 71 +++++++++++++++++++++++ manim/animation/animation.py | 22 +++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/docs/source/tutorials/building_blocks.rst b/docs/source/tutorials/building_blocks.rst index 92cf89b5ab..bc76f6394e 100644 --- a/docs/source/tutorials/building_blocks.rst +++ b/docs/source/tutorials/building_blocks.rst @@ -294,6 +294,77 @@ Use the :code:`run_time` argument to control the duration. self.play(ApplyMethod(square.shift, UP), run_time=3) self.wait(1) +Creating a custom animation +=========================== + +Even though Manim has many built-in animations, you will find times when you need to smoothly animate from one state of a :class:`~.Mobject` to another. +If you find yourself in that situation, then you can define your own custom animation. +You start by extending the :class:`~.Animation` class and overriding its :meth:`~.Animation.interpolate_mobject`. +The :meth:`~.Animation.interpolate_mobject` method receives alpha as a parameter that starts at 0 and changes throughout the animation. +So, you just have to manipulate self.mobject inside Animation according to the alpha value in its interpolate_mobject method. +Then you get all the benefits of :class:`~.Animation` such as playing it for different run times or using different rate functions. + +Let's say you start with a number and want to create a :class:`~.Transform` animation that transforms it to a target number. +You can do it using :class:`~.FadeTransform`, which will fade out the starting number and fade in the target number. +But when we think about transforming a number from one to another, an intuitive way of doing it is by incrementing or decrementing it smoothly. +Manim has a feature that allows you to customize this behavior by defining your own custom animation. + +You can start by creating your own ``Count`` class that extends :class:`~.Animation`. +The class can have a constructor with three arguments, a :class:`~.DecimalNumber` Mobject, start, and end. +The constructor will pass the :class:`~.DecimalNumber` Mobject to the super constructor (in this case, the :class:`~.Animation` constructor) and will set start and end. + +The only thing that you need to do is to define how you want it to look at every step of the animation. +Manim provides you with the alpha value in the :meth:`~.Animation.interpolate_mobject` method based on frame rate of video, rate function, and run time of animation played. +The alpha parameter holds a value between 0 and 1 representing the step of the currently playing animation. +For example, 0 means the beginning of the animation, 0.5 means halfway through the animation, and 1 means the end of the animation. + +In the case of the ``Count`` animation, you just have to figure out a way to determine the number to display at the given alpha value and then set that value in the :meth:`~.Animation.interpolate_mobject` method of the ``Count`` animation. +Suppose you are starting at 50 and incrementing until the :class:`~.DecimalNumber` reaches 100 at the end of the animation. + +* If alpha is 0, you want the value to be 50. +* If alpha is 0.5, you want the value to be 75. +* If alpha is 1, you want the value to be 100. + +Generally, you start with the starting number and add only some part of the value to be increment according to the alpha value. +So, the logic of calculating the number to display at each step will be - 50 + alpha * (100 - 50). +Once you set the calculated value for the :class:`~.DecimalNumber`, you are done. + +Once you have defined your ``Count`` animation, you can play it in your :class:`~.Scene` for any duration you want for any :class:`~.DecimalNumber` with any rate function. + +.. manim:: CountingScene + :ref_classes: Animation DecimalNumber + :ref_methods: Animation.interpolate_mobject Scene.play + + class Count(Animation): + def __init__(self, number: DecimalNumber, start: float, end: float, **kwargs) -> None: + # Pass number as the mobject of the animation + super().__init__(number, **kwargs) + # Set start and end + self.start = start + self.end = end + + def interpolate_mobject(self, alpha: float) -> None: + # Set value of DecimalNumber according to alpha + value = self.start + (alpha * (self.end - self.start)) + self.mobject.set_value(value) + + + class CountingScene(Scene): + def construct(self): + # Create Decimal Number and add it to scene + number = DecimalNumber().set_color(WHITE).scale(5) + # Add an updater to keep the DecimalNumber centered as its value changes + number.add_updater(lambda number: number.move_to(ORIGIN)) + + self.add(number) + + self.wait() + + # Play the Count Animation to count from 0 to 100 in 4 seconds + self.play(Count(number, 0, 100), run_time=4, rate_func=linear) + + self.wait() + Using coordinates of a mobject ============================== diff --git a/manim/animation/animation.py b/manim/animation/animation.py index cfa016e960..0e0c3ee670 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -1,6 +1,13 @@ """Animate mobjects.""" +from .. import logger +from ..mobject import mobject, opengl_mobject +from ..mobject.mobject import Mobject +from ..mobject.opengl_mobject import OpenGLMobject +from ..utils.deprecation import deprecated +from ..utils.rate_functions import smooth + __all__ = ["Animation", "Wait", "override_animation"] @@ -20,12 +27,6 @@ if TYPE_CHECKING: from manim.scene.scene import Scene -from .. import logger -from ..mobject import mobject, opengl_mobject -from ..mobject.mobject import Mobject -from ..mobject.opengl_mobject import OpenGLMobject -from ..utils.deprecation import deprecated -from ..utils.rate_functions import smooth DEFAULT_ANIMATION_RUN_TIME: float = 1.0 DEFAULT_ANIMATION_LAG_RATIO: float = 0.0 @@ -278,6 +279,15 @@ def interpolate(self, alpha: float) -> None: self.interpolate_mobject(alpha) def interpolate_mobject(self, alpha: float) -> None: + """Interpolates the mobject of the :class:`Animation` based on alpha value. + + Parameters + ---------- + alpha + A float between 0 and 1 expressing the ratio to which the animation + is completed. For example, alpha-values of 0, 0.5, and 1 correspond + to the animation being completed 0%, 50%, and 100%, respectively. + """ families = list(self.get_all_families_zipped()) for i, mobs in enumerate(families): sub_alpha = self.get_sub_alpha(alpha, i, len(families))