11
11
from ..mobject .mobject import Mobject
12
12
from ..mobject .opengl import opengl_mobject
13
13
from ..utils .rate_functions import linear , smooth
14
+ from .scene_buffer import SceneBuffer
15
+ from .protocol import AnimationProtocol
14
16
15
17
__all__ = ["Animation" , "Wait" , "override_animation" ]
16
18
17
19
18
20
from copy import deepcopy
19
- from typing import TYPE_CHECKING , Callable , Iterable , Sequence
21
+ from typing import TYPE_CHECKING , Callable , Iterable , TypeVar , Sequence
20
22
21
23
if TYPE_CHECKING :
22
- from manim . scene . scene import Scene
24
+ from typing_extensions import Self
23
25
24
26
25
27
DEFAULT_ANIMATION_RUN_TIME : float = 1.0
@@ -127,17 +129,17 @@ def __new__(
127
129
128
130
def __init__ (
129
131
self ,
130
- mobject : Mobject | None ,
132
+ mobject : OpenGLMobject | None ,
131
133
lag_ratio : float = DEFAULT_ANIMATION_LAG_RATIO ,
132
134
run_time : float = DEFAULT_ANIMATION_RUN_TIME ,
133
135
rate_func : Callable [[float ], float ] = smooth ,
134
136
reverse_rate_function : bool = False ,
135
- name : str = None ,
136
- remover : bool = False , # remove a mobject from the screen?
137
+ name : str | None = None ,
138
+ remover : bool = False , # remove a mobject from the screen at end of animation
137
139
suspend_mobject_updating : bool = True ,
138
140
introducer : bool = False ,
139
141
* ,
140
- _on_finish : Callable [[], None ] = lambda _ : None ,
142
+ _on_finish : Callable [[SceneBuffer ], object ] = lambda _ : None ,
141
143
** kwargs ,
142
144
) -> None :
143
145
self ._typecheck_input (mobject )
@@ -149,15 +151,16 @@ def __init__(
149
151
self .introducer : bool = introducer
150
152
self .suspend_mobject_updating : bool = suspend_mobject_updating
151
153
self .lag_ratio : float = lag_ratio
152
- self ._on_finish : Callable [[Scene ], None ] = _on_finish
154
+ self ._on_finish = _on_finish
155
+ self .buffer = SceneBuffer ()
153
156
if config ["renderer" ] == RendererType .OPENGL :
154
157
self .starting_mobject : OpenGLMobject = OpenGLMobject ()
155
158
self .mobject : OpenGLMobject = (
156
159
mobject if mobject is not None else OpenGLMobject ()
157
160
)
158
- else :
159
- self .starting_mobject : Mobject = Mobject ()
160
- self .mobject : Mobject = mobject if mobject is not None else Mobject ()
161
+ # else:
162
+ # self.starting_mobject: Mobject = Mobject()
163
+ # self.mobject: Mobject = mobject if mobject is not None else Mobject()
161
164
if kwargs :
162
165
logger .debug ("Animation received extra kwargs: %s" , kwargs )
163
166
@@ -169,7 +172,7 @@ def __init__(
169
172
),
170
173
)
171
174
172
- def _typecheck_input (self , mobject : Mobject | None ) -> None :
175
+ def _typecheck_input (self , mobject : Mobject | OpenGLMobject | None ) -> None :
173
176
if mobject is None :
174
177
logger .debug ("Animation with empty mobject" )
175
178
elif not isinstance (mobject , (Mobject , OpenGLMobject )):
@@ -213,10 +216,10 @@ def begin(self) -> None:
213
216
self .mobject .suspend_updating ()
214
217
self .interpolate (0 )
215
218
219
+ if self .is_introducer ():
220
+ self .buffer .add (self .mobject )
221
+
216
222
def finish (self ) -> None :
217
- # TODO: begin and finish should require a scene as parameter.
218
- # That way Animation.clean_up_from_screen and Scene.add_mobjects_from_animations
219
- # could be removed as they fulfill basically the same purpose.
220
223
"""Finish the animation.
221
224
222
225
This method gets called when the animation is over.
@@ -226,44 +229,20 @@ def finish(self) -> None:
226
229
if self .suspend_mobject_updating and self .mobject is not None :
227
230
self .mobject .resume_updating ()
228
231
229
- def clean_up_from_scene (self , scene : Scene ) -> None :
230
- """Clean up the :class:`~.Scene` after finishing the animation.
231
-
232
- This includes to :meth:`~.Scene.remove` the Animation's
233
- :class:`~.Mobject` if the animation is a remover.
234
-
235
- Parameters
236
- ----------
237
- scene
238
- The scene the animation should be cleaned up from.
239
- """
240
- self ._on_finish (scene )
232
+ self ._on_finish (self .buffer )
241
233
if self .is_remover ():
242
- scene .remove (self .mobject )
243
-
244
- def _setup_scene (self , scene : Scene ) -> None :
245
- """Setup up the :class:`~.Scene` before starting the animation.
246
-
247
- This includes to :meth:`~.Scene.add` the Animation's
248
- :class:`~.Mobject` if the animation is an introducer.
249
-
250
- Parameters
251
- ----------
252
- scene
253
- The scene the animation should be cleaned up from.
254
- """
255
- if scene is None :
256
- return
257
- if (
258
- self .is_introducer ()
259
- and self .mobject not in scene .get_mobject_family_members ()
260
- ):
261
- scene .add (self .mobject )
234
+ self .buffer .remove (self .mobject )
262
235
263
236
def create_starting_mobject (self ) -> Mobject :
264
237
# Keep track of where the mobject starts
265
238
return self .mobject .copy ()
266
239
240
+ def get_all_animations (self ) -> tuple [Animation , ...]:
241
+ """This method is to implement an animation protocol, and
242
+ is more useful in places like :class:`.AnimationGroup`
243
+ """
244
+ return (self ,)
245
+
267
246
def get_all_mobjects (self ) -> Sequence [Mobject ]:
268
247
"""Get all mobjects involved in the animation.
269
248
@@ -305,9 +284,9 @@ def get_all_mobjects_to_update(self) -> list[Mobject]:
305
284
# The surrounding scene typically handles
306
285
# updating of self.mobject. Besides, in
307
286
# most cases its updating is suspended anyway
308
- return list ( filter ( lambda m : m is not self .mobject , self . get_all_mobjects ()))
287
+ return [ m for m in self . get_all_mobjects () if m is not self .mobject ]
309
288
310
- def copy (self ) -> Animation :
289
+ def copy (self ) -> Self :
311
290
"""Create a copy of the animation.
312
291
313
292
Returns
@@ -422,7 +401,7 @@ def get_run_time(self) -> float:
422
401
def set_rate_func (
423
402
self ,
424
403
rate_func : Callable [[float ], float ],
425
- ) -> Animation :
404
+ ) -> Self :
426
405
"""Set the rate function of the animation.
427
406
428
407
Parameters
@@ -451,7 +430,7 @@ def get_rate_func(
451
430
"""
452
431
return self .rate_func
453
432
454
- def set_name (self , name : str ) -> Animation :
433
+ def set_name (self , name : str ) -> Self :
455
434
"""Set the name of the animation.
456
435
457
436
Parameters
@@ -489,7 +468,7 @@ def is_introducer(self) -> bool:
489
468
490
469
491
470
def prepare_animation (
492
- anim : Animation | mobject ._AnimationBuilder ,
471
+ anim : AnimationProtocol | mobject . _AnimationBuilder | opengl_mobject ._AnimationBuilder ,
493
472
) -> Animation :
494
473
r"""Returns either an unchanged animation, or the animation built
495
474
from a passed animation factory.
@@ -517,10 +496,7 @@ def prepare_animation(
517
496
TypeError: Object 42 cannot be converted to an animation
518
497
519
498
"""
520
- if isinstance (anim , mobject ._AnimationBuilder ):
521
- return anim .build ()
522
-
523
- if isinstance (anim , opengl_mobject ._AnimationBuilder ):
499
+ if isinstance (anim , (mobject ._AnimationBuilder , opengl_mobject ._AnimationBuilder )):
524
500
return anim .build ()
525
501
526
502
if isinstance (anim , Animation ):
@@ -576,7 +552,7 @@ def begin(self) -> None:
576
552
def finish (self ) -> None :
577
553
pass
578
554
579
- def clean_up_from_scene (self , scene : Scene ) -> None :
555
+ def clean_up_from_scene (self , scene : SceneBuffer ) -> None :
580
556
pass
581
557
582
558
def update_mobjects (self , dt : float ) -> None :
@@ -625,7 +601,9 @@ def construct(self):
625
601
626
602
"""
627
603
628
- def decorator (func ):
604
+ _F = TypeVar ("_F" , bound = Callable )
605
+
606
+ def decorator (func : _F ) -> _F :
629
607
func ._override_animation = animation_class
630
608
return func
631
609
0 commit comments