composition.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. from __future__ import annotations
  2. import numpy as np
  3. from manimlib.animation.animation import Animation
  4. from manimlib.animation.animation import prepare_animation
  5. from manimlib.mobject.mobject import _AnimationBuilder
  6. from manimlib.mobject.mobject import Group
  7. from manimlib.mobject.types.vectorized_mobject import VGroup
  8. from manimlib.mobject.types.vectorized_mobject import VMobject
  9. from manimlib.utils.bezier import integer_interpolate
  10. from manimlib.utils.bezier import interpolate
  11. from manimlib.utils.iterables import remove_list_redundancies
  12. from manimlib.utils.simple_functions import clip
  13. from typing import TYPE_CHECKING, Union, Iterable
  14. AnimationType = Union[Animation, _AnimationBuilder]
  15. if TYPE_CHECKING:
  16. from typing import Callable, Optional
  17. from manimlib.mobject.mobject import Mobject
  18. from manimlib.scene.scene import Scene
  19. DEFAULT_LAGGED_START_LAG_RATIO = 0.05
  20. class AnimationGroup(Animation):
  21. def __init__(
  22. self,
  23. *args: AnimationType | Iterable[AnimationType],
  24. run_time: float = -1, # If negative, default to sum of inputed animation runtimes
  25. lag_ratio: float = 0.0,
  26. group: Optional[Mobject] = None,
  27. group_type: Optional[type] = None,
  28. **kwargs
  29. ):
  30. animations = args[0] if isinstance(args[0], Iterable) else args
  31. self.animations = [prepare_animation(anim) for anim in animations]
  32. self.build_animations_with_timings(lag_ratio)
  33. self.max_end_time = max((awt[2] for awt in self.anims_with_timings), default=0)
  34. self.run_time = self.max_end_time if run_time < 0 else run_time
  35. self.lag_ratio = lag_ratio
  36. mobs = remove_list_redundancies([a.mobject for a in self.animations])
  37. if group is not None:
  38. self.group = group
  39. if group_type is not None:
  40. self.group = group_type(*mobs)
  41. elif all(isinstance(anim.mobject, VMobject) for anim in animations):
  42. self.group = VGroup(*mobs)
  43. else:
  44. self.group = Group(*mobs)
  45. super().__init__(
  46. self.group,
  47. run_time=self.run_time,
  48. lag_ratio=lag_ratio,
  49. **kwargs
  50. )
  51. def get_all_mobjects(self) -> Mobject:
  52. return self.group
  53. def begin(self) -> None:
  54. self.group.set_animating_status(True)
  55. for anim in self.animations:
  56. anim.begin()
  57. # self.init_run_time()
  58. def finish(self) -> None:
  59. self.group.set_animating_status(False)
  60. for anim in self.animations:
  61. anim.finish()
  62. def clean_up_from_scene(self, scene: Scene) -> None:
  63. for anim in self.animations:
  64. anim.clean_up_from_scene(scene)
  65. def update_mobjects(self, dt: float) -> None:
  66. for anim in self.animations:
  67. anim.update_mobjects(dt)
  68. def calculate_max_end_time(self) -> None:
  69. self.max_end_time = max(
  70. (awt[2] for awt in self.anims_with_timings),
  71. default=0,
  72. )
  73. if self.run_time < 0:
  74. self.run_time = self.max_end_time
  75. def build_animations_with_timings(self, lag_ratio: float) -> None:
  76. """
  77. Creates a list of triplets of the form
  78. (anim, start_time, end_time)
  79. """
  80. self.anims_with_timings = []
  81. curr_time = 0
  82. for anim in self.animations:
  83. start_time = curr_time
  84. end_time = start_time + anim.get_run_time()
  85. self.anims_with_timings.append(
  86. (anim, start_time, end_time)
  87. )
  88. # Start time of next animation is based on the lag_ratio
  89. curr_time = interpolate(
  90. start_time, end_time, lag_ratio
  91. )
  92. def interpolate(self, alpha: float) -> None:
  93. # Note, if the run_time of AnimationGroup has been
  94. # set to something other than its default, these
  95. # times might not correspond to actual times,
  96. # e.g. of the surrounding scene. Instead they'd
  97. # be a rescaled version. But that's okay!
  98. time = alpha * self.max_end_time
  99. for anim, start_time, end_time in self.anims_with_timings:
  100. anim_time = end_time - start_time
  101. if anim_time == 0:
  102. sub_alpha = 0
  103. else:
  104. sub_alpha = clip((time - start_time) / anim_time, 0, 1)
  105. anim.interpolate(sub_alpha)
  106. class Succession(AnimationGroup):
  107. def __init__(
  108. self,
  109. *animations: Animation,
  110. lag_ratio: float = 1.0,
  111. **kwargs
  112. ):
  113. super().__init__(*animations, lag_ratio=lag_ratio, **kwargs)
  114. def begin(self) -> None:
  115. assert len(self.animations) > 0
  116. self.active_animation = self.animations[0]
  117. self.active_animation.begin()
  118. def finish(self) -> None:
  119. self.active_animation.finish()
  120. def update_mobjects(self, dt: float) -> None:
  121. self.active_animation.update_mobjects(dt)
  122. def interpolate(self, alpha: float) -> None:
  123. index, subalpha = integer_interpolate(
  124. 0, len(self.animations), alpha
  125. )
  126. animation = self.animations[index]
  127. if animation is not self.active_animation:
  128. self.active_animation.finish()
  129. animation.begin()
  130. self.active_animation = animation
  131. animation.interpolate(subalpha)
  132. class LaggedStart(AnimationGroup):
  133. def __init__(
  134. self,
  135. *animations,
  136. lag_ratio: float = DEFAULT_LAGGED_START_LAG_RATIO,
  137. **kwargs
  138. ):
  139. super().__init__(*animations, lag_ratio=lag_ratio, **kwargs)
  140. class LaggedStartMap(LaggedStart):
  141. def __init__(
  142. self,
  143. anim_func: Callable[[Mobject], Animation],
  144. group: Mobject,
  145. run_time: float = 2.0,
  146. lag_ratio: float = DEFAULT_LAGGED_START_LAG_RATIO,
  147. **kwargs
  148. ):
  149. anim_kwargs = dict(kwargs)
  150. anim_kwargs.pop("lag_ratio", None)
  151. super().__init__(
  152. *(anim_func(submob, **anim_kwargs) for submob in group),
  153. run_time=run_time,
  154. lag_ratio=lag_ratio,
  155. group=group
  156. )