3b1b-manim/manimlib/animation/composition.py
2019-02-08 14:49:38 -08:00

157 lines
5 KiB
Python

import numpy as np
from manimlib.animation.animation import Animation
from manimlib.mobject.mobject import Group
from manimlib.utils.bezier import interpolate
from manimlib.utils.config_ops import digest_config
from manimlib.utils.rate_functions import linear
from manimlib.utils.rate_functions import squish_rate_func
class AnimationGroup(Animation):
CONFIG = {
# If None, this defaults to the sum of all
# internal animations
"run_time": None,
"rate_functions": linear,
# If 0, all animations are played at once.
# If 1, all are played successively.
# If >0 and <1, they start at lagged times
# from one and other.
"lag_ratio": 0,
}
def __init__(self, *animations, **kwargs):
self.animations = animations
self.group = Group(*[anim.mobject for anim in animations])
Animation.__init__(self, self.group, **kwargs)
self.init_run_time()
def get_all_mobjects(self):
return self.group
def init_run_time(self):
# A list of triplets (anim, start_time, end_time)
self.anims_with_timings = []
curr_time = 0
for anim in self.animations:
start_time = curr_time
end_time = start_time + anim.get_run_time()
self.anims_with_timings.append(
(anim, start_time, end_time)
)
# Start time of next animation is based on
# the lag_ratio
curr_time = interpolate(
start_time, end_time, self.lag_ratio
)
if self.run_time is None:
# Default to max end_time
self.run_time = self.get_default_run_time()
def get_default_run_time(self):
return np.max([
awt[2] for awt in self.anims_with_timings
])
def begin(self):
for anim in self.animations:
anim.begin()
def finish(self):
for anim in self.animations:
anim.finish()
def clean_up_from_scene(self, scene):
for anim in self.animations:
anim.clean_up_from_scene(scene)
def interpolate(self, alpha):
time = alpha * self.get_run_time()
for anim, start_time, end_time in self.anims_with_timings:
anim_time = end_time - start_time
if anim_time == 0:
sub_alpha = 0
else:
sub_alpha = np.clip(
(time - start_time) / anim_time,
0, 1
)
anim.interpolate(sub_alpha)
class Succession(AnimationGroup):
CONFIG = {
"lag_ratio": 1,
}
# Variants on mapping an animation over submobjects
# This class is depricated. One should use OldLaggedStart
# instead, which has different syntax, but more generality
class OldLaggedStart(Animation):
CONFIG = {
"run_time": 2,
"lag_ratio": 0.5,
}
def __init__(self, AnimationClass, mobject, arg_creator=None, **kwargs):
for key in ["rate_func", "run_time"]:
if key in AnimationClass.CONFIG:
setattr(self, key, AnimationClass.CONFIG[key])
digest_config(self, kwargs)
for key in "rate_func", "run_time", "lag_ratio":
if key in kwargs:
kwargs.pop(key)
if arg_creator is None:
def arg_creator(mobject):
return (mobject,)
self.subanimations = [
AnimationClass(
*arg_creator(submob),
run_time=self.run_time,
rate_func=squish_rate_func(
self.rate_func, beta, beta + self.lag_ratio
),
**kwargs
)
for submob, beta in zip(
mobject,
np.linspace(0, 1 - self.lag_ratio, len(mobject))
)
]
Animation.__init__(self, mobject, **kwargs)
def update(self, alpha):
for anim in self.subanimations:
anim.update(alpha)
return self
def clean_up_from_scene(self, *args, **kwargs):
for anim in self.subanimations:
anim.clean_up_from_scene(*args, **kwargs)
class ApplyToCenters(Animation):
def __init__(self, AnimationClass, mobjects, **kwargs):
full_kwargs = AnimationClass.CONFIG
full_kwargs.update(kwargs)
full_kwargs["mobject"] = Group(*[
mob.get_point_mobject()
for mob in mobjects
])
self.centers_container = AnimationClass(**full_kwargs)
full_kwargs.pop("mobject")
Animation.__init__(self, Group(*mobjects), **full_kwargs)
self.name = str(self) + AnimationClass.__name__
def interpolate_mobject(self, alpha):
self.centers_container.interpolate_mobject(alpha)
center_mobs = self.centers_container.mobject.split()
mobjects = self.mobject.split()
for center_mob, mobject in zip(center_mobs, mobjects):
mobject.shift(
center_mob.get_center() - mobject.get_center()
)