From 802d36cc156425a858337c08729bbde5bcdf842f Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Thu, 1 Apr 2021 23:41:05 +0200 Subject: [PATCH 01/21] Type hints in animations and rate_functions --- manim/animation/animation.py | 61 ++++++++++++++++++++++------------- manim/utils/rate_functions.py | 2 +- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index 4c57ce3917..ce0258d92e 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -5,7 +5,7 @@ import typing -from typing import Union +from typing import Union, Optional, Tuple, Iterator from copy import deepcopy import numpy as np @@ -27,7 +27,7 @@ class Animation: def __init__( self, - mobject: Mobject, + mobject: Optional[Mobject], # If lag_ratio is 0, the animation is applied to all submobjects # at the same time # If 1, it is applied to each successively. @@ -35,20 +35,22 @@ def __init__( # with lagged start times lag_ratio: float = DEFAULT_ANIMATION_LAG_RATIO, run_time: float = DEFAULT_ANIMATION_RUN_TIME, - rate_func: typing.Callable[[float, float], np.ndarray] = smooth, + rate_func: typing.Callable[ + [Union[np.ndarray, float]], Union[np.ndarray, float] + ] = smooth, name: str = None, remover: bool = False, # remove a mobject from the screen? suspend_mobject_updating: bool = True, **kwargs, ) -> None: self._typecheck_input(mobject) - self.run_time = run_time + self.run_time: float = run_time self.rate_func = rate_func self.name = name self.remover = remover self.suspend_mobject_updating = suspend_mobject_updating self.lag_ratio = lag_ratio - self.starting_mobject = None + self.starting_mobject: Optional[Mobject] = None self.mobject = mobject if kwargs: logger.debug("Animation received extra kwargs: %s", kwargs) @@ -61,7 +63,7 @@ def __init__( ) ) - def _typecheck_input(self, mobject: Mobject) -> None: + def _typecheck_input(self, mobject: Optional[Mobject]) -> None: if mobject is None: logger.debug("creating dummy animation") elif not isinstance(mobject, Mobject) and not isinstance( @@ -83,7 +85,7 @@ def begin(self) -> None: # especially any mobject copying, should live in # this method self.starting_mobject = self.create_starting_mobject() - if self.suspend_mobject_updating: + if self.suspend_mobject_updating and self.mobject is not None: # All calls to self.mobject's internal updaters # during the animation, either from this Animation # or from the surrounding scene, should do nothing. @@ -95,26 +97,30 @@ def begin(self) -> None: def finish(self) -> None: self.interpolate(1) - if self.suspend_mobject_updating: + if self.suspend_mobject_updating and self.mobject is not None: self.mobject.resume_updating() def clean_up_from_scene(self, scene: "Scene") -> None: if self.is_remover(): scene.remove(self.mobject) - def create_starting_mobject(self) -> Mobject: + def create_starting_mobject(self) -> Optional[Mobject]: # Keep track of where the mobject starts - return self.mobject.copy() + return self.mobject.copy() if self.mobject is not None else None - def get_all_mobjects(self) -> typing.Tuple[Mobject, typing.Union[Mobject, None]]: + def get_all_mobjects(self) -> Tuple[Optional[Mobject], Optional[Mobject]]: """ Ordering must match the ordering of arguments to interpolate_submobject """ return self.mobject, self.starting_mobject - def get_all_families_zipped(self) -> typing.Iterator[typing.Tuple]: + def get_all_families_zipped(self) -> Iterator[Tuple]: return zip( - *[mob.family_members_with_points() for mob in self.get_all_mobjects()] + *[ + mob.family_members_with_points() + for mob in self.get_all_mobjects() + if mob is not None + ] ) def update_mobjects(self, dt: float) -> None: @@ -138,11 +144,12 @@ def copy(self) -> "Animation": return deepcopy(self) # Methods for interpolation, the mean of an Animation - def interpolate(self, alpha: float) -> None: - alpha = np.clip(alpha, 0, 1) + def interpolate(self, alpha: Union[np.ndarray, float]) -> None: + # alpha = np.clip(alpha, 0, 1) + alpha = min(max(alpha, 0), 1) self.interpolate_mobject(self.rate_func(alpha)) - def update(self, alpha: float) -> None: + def update(self, alpha: Union[np.ndarray, float]) -> None: """ This method shouldn't exist, but it's here to keep many old scenes from breaking @@ -153,19 +160,24 @@ def update(self, alpha: float) -> None: ) self.interpolate(alpha) - def interpolate_mobject(self, alpha: float) -> None: + def interpolate_mobject(self, alpha: Union[np.ndarray, float]) -> None: families = list(self.get_all_families_zipped()) for i, mobs in enumerate(families): sub_alpha = self.get_sub_alpha(alpha, i, len(families)) self.interpolate_submobject(*mobs, sub_alpha) def interpolate_submobject( - self, submobject: Mobject, starting_submobject: Mobject, alpha: float + self, + submobject: Mobject, + starting_submobject: Mobject, + alpha: Union[np.ndarray, float], ) -> None: # Typically implemented by subclass pass - def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int): + def get_sub_alpha( + self, alpha: Union[np.ndarray, float], index: int, num_submobjects: int + ): # TODO, make this more understandable, and/or combine # its functionality with AnimationGroup's method # build_animations_with_timings @@ -184,12 +196,17 @@ def get_run_time(self) -> float: return self.run_time def set_rate_func( - self, rate_func: typing.Callable[[float, float], np.ndarray] + self, + rate_func: typing.Callable[ + [Union[np.ndarray, float]], Union[np.ndarray, float] + ], ) -> "Animation": self.rate_func = rate_func return self - def get_rate_func(self) -> typing.Callable[[float, float], np.ndarray]: + def get_rate_func( + self, + ) -> typing.Callable[[Union[np.ndarray, float]], Union[np.ndarray, float]]: return self.rate_func def set_name(self, name: str) -> "Animation": @@ -266,5 +283,5 @@ def clean_up_from_scene(self, scene: "Scene") -> None: def update_mobjects(self, dt: float) -> None: pass - def interpolate(self, alpha: float) -> None: + def interpolate(self, alpha: Union[np.ndarray, float]) -> None: pass diff --git a/manim/utils/rate_functions.py b/manim/utils/rate_functions.py index 272d6a4528..13886b7492 100644 --- a/manim/utils/rate_functions.py +++ b/manim/utils/rate_functions.py @@ -78,7 +78,7 @@ def linear(t: typing.Union[np.ndarray, float]) -> typing.Union[np.ndarray, float return t -def smooth(t: float, inflection: float = 10.0) -> np.ndarray: +def smooth(t: typing.Union[np.ndarray, float], inflection: float = 10.0) -> np.ndarray: error = sigmoid(-inflection / 2) return np.clip( (sigmoid(inflection * (t - 0.5)) - error) / (1 - 2 * error), From 689eb8d5ba37c435ae40d68b1e571370c9a8c988 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sat, 3 Apr 2021 18:24:22 +0200 Subject: [PATCH 02/21] Type Signatures on most rate functions --- manim/animation/animation.py | 2 +- manim/utils/rate_functions.py | 94 ++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 46 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index ce0258d92e..2be5c352e2 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -284,4 +284,4 @@ def update_mobjects(self, dt: float) -> None: pass def interpolate(self, alpha: Union[np.ndarray, float]) -> None: - pass + pass \ No newline at end of file diff --git a/manim/utils/rate_functions.py b/manim/utils/rate_functions.py index 13886b7492..2443e1e961 100644 --- a/manim/utils/rate_functions.py +++ b/manim/utils/rate_functions.py @@ -73,12 +73,14 @@ def construct(self): from ..utils.bezier import bezier from ..utils.simple_functions import sigmoid +NumpyOrFloat = typing.Union[np.ndarray, float] -def linear(t: typing.Union[np.ndarray, float]) -> typing.Union[np.ndarray, float]: + +def linear(t: NumpyOrFloat) -> NumpyOrFloat: return t -def smooth(t: typing.Union[np.ndarray, float], inflection: float = 10.0) -> np.ndarray: +def smooth(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: error = sigmoid(-inflection / 2) return np.clip( (sigmoid(inflection * (t - 0.5)) - error) / (1 - 2 * error), @@ -87,31 +89,33 @@ def smooth(t: typing.Union[np.ndarray, float], inflection: float = 10.0) -> np.n ) -def rush_into(t: float, inflection: float = 10.0) -> np.ndarray: +def rush_into(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: return 2 * smooth(t / 2.0, inflection) -def rush_from(t: float, inflection: float = 10.0) -> np.ndarray: +def rush_from(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: return 2 * smooth(t / 2.0 + 0.5, inflection) - 1 -def slow_into(t: np.ndarray) -> np.ndarray: +def slow_into(t: NumpyOrFloat) -> NumpyOrFloat: return np.sqrt(1 - (1 - t) * (1 - t)) -def double_smooth(t: float) -> np.ndarray: +def double_smooth(t: NumpyOrFloat) -> NumpyOrFloat: if t < 0.5: return 0.5 * smooth(2 * t) else: return 0.5 * (1 + smooth(2 * t - 1)) -def there_and_back(t: float, inflection: float = 10.0) -> np.ndarray: +def there_and_back(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: new_t = 2 * t if t < 0.5 else 2 * (1 - t) return smooth(new_t, inflection) -def there_and_back_with_pause(t: float, pause_ratio: float = 1.0 / 3) -> np.ndarray: +def there_and_back_with_pause( + t: NumpyOrFloat, pause_ratio: float = 1.0 / 3 +) -> NumpyOrFloat: a = 1.0 / pause_ratio if t < 0.5 - pause_ratio / 2: return smooth(a * t) @@ -126,24 +130,24 @@ def running_start(t: float, pull_factor: float = -0.5) -> typing.Iterable: def not_quite_there( - func: typing.Callable[[float, typing.Optional[float]], np.ndarray] = smooth, + func: typing.Callable[[NumpyOrFloat], NumpyOrFloat] = smooth, proportion: float = 0.7, -) -> typing.Callable[[float], np.ndarray]: +) -> typing.Callable[[NumpyOrFloat], NumpyOrFloat]: def result(t): return proportion * func(t) return result -def wiggle(t: float, wiggles: float = 2) -> np.ndarray: +def wiggle(t: NumpyOrFloat, wiggles: float = 2) -> NumpyOrFloat: return there_and_back(t) * np.sin(wiggles * np.pi * t) def squish_rate_func( - func: typing.Callable[[float], typing.Any], + func: typing.Callable[[NumpyOrFloat], NumpyOrFloat], a: float = 0.4, b: float = 0.6, -) -> typing.Callable[[float], typing.Any]: # what is func return type? +) -> typing.Callable[[NumpyOrFloat], NumpyOrFloat]: # what is func return type? def result(t): if a == b: return a @@ -164,85 +168,85 @@ def result(t): # "lingering", different from squish_rate_func's default params -def lingering(t: float) -> float: +def lingering(t: NumpyOrFloat) -> NumpyOrFloat: return squish_rate_func(lambda t: t, 0, 0.8)(t) -def exponential_decay(t: np.ndarray, half_life: float = 0.1) -> np.ndarray: +def exponential_decay(t: NumpyOrFloat, half_life: float = 0.1) -> NumpyOrFloat: # The half-life should be rather small to minimize # the cut-off error at the end return 1 - np.exp(-t / half_life) -def ease_in_sine(t: np.ndarray) -> float: +def ease_in_sine(t: NumpyOrFloat) -> NumpyOrFloat: return 1 - np.cos((t * np.pi) / 2) -def ease_out_sine(t: np.ndarray) -> float: +def ease_out_sine(t: NumpyOrFloat) -> NumpyOrFloat: return np.sin((t * np.pi) / 2) -def ease_in_out_sine(t: np.ndarray) -> float: +def ease_in_out_sine(t: NumpyOrFloat) -> NumpyOrFloat: return -(np.cos(np.pi * t) - 1) / 2 -def ease_in_quad(t: float) -> float: +def ease_in_quad(t: NumpyOrFloat) -> NumpyOrFloat: return t * t -def ease_out_quad(t: float) -> float: +def ease_out_quad(t: NumpyOrFloat) -> NumpyOrFloat: return 1 - (1 - t) * (1 - t) -def ease_in_out_quad(t: float) -> float: +def ease_in_out_quad(t: NumpyOrFloat) -> NumpyOrFloat: return 2 * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 2) / 2 -def ease_in_cubic(t: float) -> float: +def ease_in_cubic(t: NumpyOrFloat) -> NumpyOrFloat: return t * t * t -def ease_out_cubic(t: float) -> float: +def ease_out_cubic(t: NumpyOrFloat) -> NumpyOrFloat: return 1 - pow(1 - t, 3) -def ease_in_out_cubic(t: float) -> float: +def ease_in_out_cubic(t: NumpyOrFloat) -> NumpyOrFloat: return 4 * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 3) / 2 -def ease_in_quart(t: float) -> float: +def ease_in_quart(t: NumpyOrFloat) -> NumpyOrFloat: return t * t * t * t -def ease_out_quart(t: float) -> float: +def ease_out_quart(t: NumpyOrFloat) -> NumpyOrFloat: return 1 - pow(1 - t, 4) -def ease_in_out_quart(t: float) -> float: +def ease_in_out_quart(t: NumpyOrFloat) -> NumpyOrFloat: return 8 * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 4) / 2 -def ease_in_quint(t: float) -> float: +def ease_in_quint(t: NumpyOrFloat) -> NumpyOrFloat: return t * t * t * t * t -def ease_out_quint(t: float) -> float: +def ease_out_quint(t: NumpyOrFloat) -> NumpyOrFloat: return 1 - pow(1 - t, 5) -def ease_in_out_quint(t: float) -> float: +def ease_in_out_quint(t: NumpyOrFloat) -> NumpyOrFloat: return 16 * t * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 5) / 2 -def ease_in_expo(t: float) -> float: +def ease_in_expo(t: NumpyOrFloat) -> NumpyOrFloat: return 0 if t == 0 else pow(2, 10 * t - 10) -def ease_out_expo(t: float) -> float: +def ease_out_expo(t: NumpyOrFloat) -> NumpyOrFloat: return 1 if t == 1 else 1 - pow(2, -10 * t) -def ease_in_out_expo(t: float) -> float: +def ease_in_out_expo(t: NumpyOrFloat) -> NumpyOrFloat: if t == 0: return 0 elif t == 1: @@ -253,15 +257,15 @@ def ease_in_out_expo(t: float) -> float: return 2 - pow(2, -20 * t + 10) / 2 -def ease_in_circ(t: float) -> float: +def ease_in_circ(t: NumpyOrFloat) -> NumpyOrFloat: return 1 - sqrt(1 - pow(t, 2)) -def ease_out_circ(t: float) -> float: +def ease_out_circ(t: NumpyOrFloat) -> NumpyOrFloat: return sqrt(1 - pow(t - 1, 2)) -def ease_in_out_circ(t: float) -> float: +def ease_in_out_circ(t: NumpyOrFloat) -> NumpyOrFloat: return ( (1 - sqrt(1 - pow(2 * t, 2))) / 2 if t < 0.5 @@ -269,19 +273,19 @@ def ease_in_out_circ(t: float) -> float: ) -def ease_in_back(t: float) -> float: +def ease_in_back(t: NumpyOrFloat) -> NumpyOrFloat: c1 = 1.70158 c3 = c1 + 1 return c3 * t * t * t - c1 * t * t -def ease_out_back(t: float) -> float: +def ease_out_back(t: NumpyOrFloat) -> NumpyOrFloat: c1 = 1.70158 c3 = c1 + 1 return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2) -def ease_in_out_back(t: float) -> float: +def ease_in_out_back(t: NumpyOrFloat) -> NumpyOrFloat: c1 = 1.70158 c2 = c1 * 1.525 return ( @@ -291,7 +295,7 @@ def ease_in_out_back(t: float) -> float: ) -def ease_in_elastic(t: float) -> float: +def ease_in_elastic(t: NumpyOrFloat) -> NumpyOrFloat: c4 = (2 * np.pi) / 3 if t == 0: return 0 @@ -301,7 +305,7 @@ def ease_in_elastic(t: float) -> float: return -pow(2, 10 * t - 10) * np.sin((t * 10 - 10.75) * c4) -def ease_out_elastic(t: float) -> float: +def ease_out_elastic(t: NumpyOrFloat) -> NumpyOrFloat: c4 = (2 * np.pi) / 3 if t == 0: return 0 @@ -311,7 +315,7 @@ def ease_out_elastic(t: float) -> float: return pow(2, -10 * t) * np.sin((t * 10 - 0.75) * c4) + 1 -def ease_in_out_elastic(t: float) -> float: +def ease_in_out_elastic(t: NumpyOrFloat) -> NumpyOrFloat: c5 = (2 * np.pi) / 4.5 if t == 0: return 0 @@ -323,11 +327,11 @@ def ease_in_out_elastic(t: float) -> float: return (pow(2, -20 * t + 10) * np.sin((20 * t - 11.125) * c5)) / 2 + 1 -def ease_in_bounce(t: float) -> float: +def ease_in_bounce(t: NumpyOrFloat) -> NumpyOrFloat: return 1 - ease_out_bounce(1 - t) -def ease_out_bounce(t: float) -> float: +def ease_out_bounce(t: NumpyOrFloat) -> NumpyOrFloat: n1 = 7.5625 d1 = 2.75 @@ -341,7 +345,7 @@ def ease_out_bounce(t: float) -> float: return n1 * (t - 2.625 / d1) * t + 0.984375 -def ease_in_out_bounce(t: float) -> float: +def ease_in_out_bounce(t: NumpyOrFloat) -> NumpyOrFloat: c1 = 1.70158 c2 = c1 * 1.525 return ( From b9bf896bfbfe9e28db5d444c4b6af6e92fdc2a64 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sat, 3 Apr 2021 18:36:33 +0200 Subject: [PATCH 03/21] A rate_function should just be float -> float --- .../reference/manim_directive.rst | 2 +- manim/animation/animation.py | 24 ++-- manim/utils/rate_functions.py | 103 +++++++++--------- 3 files changed, 60 insertions(+), 69 deletions(-) diff --git a/docs/source/contributing/reference/manim_directive.rst b/docs/source/contributing/reference/manim_directive.rst index 2b02693757..1f40a739a6 100644 --- a/docs/source/contributing/reference/manim_directive.rst +++ b/docs/source/contributing/reference/manim_directive.rst @@ -1,4 +1,4 @@ -manim\_directive +manim\_directive ================ .. currentmodule:: manim_directive diff --git a/manim/animation/animation.py b/manim/animation/animation.py index 2be5c352e2..5078f06487 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -35,9 +35,7 @@ def __init__( # with lagged start times lag_ratio: float = DEFAULT_ANIMATION_LAG_RATIO, run_time: float = DEFAULT_ANIMATION_RUN_TIME, - rate_func: typing.Callable[ - [Union[np.ndarray, float]], Union[np.ndarray, float] - ] = smooth, + rate_func: typing.Callable[[float], float] = smooth, name: str = None, remover: bool = False, # remove a mobject from the screen? suspend_mobject_updating: bool = True, @@ -144,12 +142,12 @@ def copy(self) -> "Animation": return deepcopy(self) # Methods for interpolation, the mean of an Animation - def interpolate(self, alpha: Union[np.ndarray, float]) -> None: + def interpolate(self, alpha: float) -> None: # alpha = np.clip(alpha, 0, 1) alpha = min(max(alpha, 0), 1) self.interpolate_mobject(self.rate_func(alpha)) - def update(self, alpha: Union[np.ndarray, float]) -> None: + def update(self, alpha: float) -> None: """ This method shouldn't exist, but it's here to keep many old scenes from breaking @@ -160,7 +158,7 @@ def update(self, alpha: Union[np.ndarray, float]) -> None: ) self.interpolate(alpha) - def interpolate_mobject(self, alpha: Union[np.ndarray, float]) -> None: + def interpolate_mobject(self, alpha: float) -> None: families = list(self.get_all_families_zipped()) for i, mobs in enumerate(families): sub_alpha = self.get_sub_alpha(alpha, i, len(families)) @@ -170,14 +168,12 @@ def interpolate_submobject( self, submobject: Mobject, starting_submobject: Mobject, - alpha: Union[np.ndarray, float], + alpha: float, ) -> None: # Typically implemented by subclass pass - def get_sub_alpha( - self, alpha: Union[np.ndarray, float], index: int, num_submobjects: int - ): + def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int): # TODO, make this more understandable, and/or combine # its functionality with AnimationGroup's method # build_animations_with_timings @@ -197,16 +193,14 @@ def get_run_time(self) -> float: def set_rate_func( self, - rate_func: typing.Callable[ - [Union[np.ndarray, float]], Union[np.ndarray, float] - ], + rate_func: typing.Callable[[float], float], ) -> "Animation": self.rate_func = rate_func return self def get_rate_func( self, - ) -> typing.Callable[[Union[np.ndarray, float]], Union[np.ndarray, float]]: + ) -> typing.Callable[[float], float]: return self.rate_func def set_name(self, name: str) -> "Animation": @@ -283,5 +277,5 @@ def clean_up_from_scene(self, scene: "Scene") -> None: def update_mobjects(self, dt: float) -> None: pass - def interpolate(self, alpha: Union[np.ndarray, float]) -> None: + def interpolate(self, alpha: float) -> None: pass \ No newline at end of file diff --git a/manim/utils/rate_functions.py b/manim/utils/rate_functions.py index 2443e1e961..f48eeb88d6 100644 --- a/manim/utils/rate_functions.py +++ b/manim/utils/rate_functions.py @@ -73,49 +73,44 @@ def construct(self): from ..utils.bezier import bezier from ..utils.simple_functions import sigmoid -NumpyOrFloat = typing.Union[np.ndarray, float] - -def linear(t: NumpyOrFloat) -> NumpyOrFloat: +def linear(t: float) -> float: return t -def smooth(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: +def smooth(t: float, inflection: float = 10.0) -> float: error = sigmoid(-inflection / 2) - return np.clip( - (sigmoid(inflection * (t - 0.5)) - error) / (1 - 2 * error), - 0, + return min( + max((sigmoid(inflection * (t - 0.5)) - error) / (1 - 2 * error), 0), 1, ) -def rush_into(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: +def rush_into(t: float, inflection: float = 10.0) -> float: return 2 * smooth(t / 2.0, inflection) -def rush_from(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: +def rush_from(t: float, inflection: float = 10.0) -> float: return 2 * smooth(t / 2.0 + 0.5, inflection) - 1 -def slow_into(t: NumpyOrFloat) -> NumpyOrFloat: +def slow_into(t: float) -> float: return np.sqrt(1 - (1 - t) * (1 - t)) -def double_smooth(t: NumpyOrFloat) -> NumpyOrFloat: +def double_smooth(t: float) -> float: if t < 0.5: return 0.5 * smooth(2 * t) else: return 0.5 * (1 + smooth(2 * t - 1)) -def there_and_back(t: NumpyOrFloat, inflection: float = 10.0) -> NumpyOrFloat: +def there_and_back(t: float, inflection: float = 10.0) -> float: new_t = 2 * t if t < 0.5 else 2 * (1 - t) return smooth(new_t, inflection) -def there_and_back_with_pause( - t: NumpyOrFloat, pause_ratio: float = 1.0 / 3 -) -> NumpyOrFloat: +def there_and_back_with_pause(t: float, pause_ratio: float = 1.0 / 3) -> float: a = 1.0 / pause_ratio if t < 0.5 - pause_ratio / 2: return smooth(a * t) @@ -125,29 +120,31 @@ def there_and_back_with_pause( return smooth(a - a * t) -def running_start(t: float, pull_factor: float = -0.5) -> typing.Iterable: +def running_start( + t: float, pull_factor: float = -0.5 +) -> typing.Iterable: # what is func return type? return bezier([0, 0, pull_factor, pull_factor, 1, 1, 1])(t) def not_quite_there( - func: typing.Callable[[NumpyOrFloat], NumpyOrFloat] = smooth, + func: typing.Callable[[float], float] = smooth, proportion: float = 0.7, -) -> typing.Callable[[NumpyOrFloat], NumpyOrFloat]: +) -> typing.Callable[[float], float]: def result(t): return proportion * func(t) return result -def wiggle(t: NumpyOrFloat, wiggles: float = 2) -> NumpyOrFloat: +def wiggle(t: float, wiggles: float = 2) -> float: return there_and_back(t) * np.sin(wiggles * np.pi * t) def squish_rate_func( - func: typing.Callable[[NumpyOrFloat], NumpyOrFloat], + func: typing.Callable[[float], float], a: float = 0.4, b: float = 0.6, -) -> typing.Callable[[NumpyOrFloat], NumpyOrFloat]: # what is func return type? +) -> typing.Callable[[float], float]: def result(t): if a == b: return a @@ -168,85 +165,85 @@ def result(t): # "lingering", different from squish_rate_func's default params -def lingering(t: NumpyOrFloat) -> NumpyOrFloat: +def lingering(t: float) -> float: return squish_rate_func(lambda t: t, 0, 0.8)(t) -def exponential_decay(t: NumpyOrFloat, half_life: float = 0.1) -> NumpyOrFloat: +def exponential_decay(t: float, half_life: float = 0.1) -> float: # The half-life should be rather small to minimize # the cut-off error at the end return 1 - np.exp(-t / half_life) -def ease_in_sine(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_sine(t: float) -> float: return 1 - np.cos((t * np.pi) / 2) -def ease_out_sine(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_sine(t: float) -> float: return np.sin((t * np.pi) / 2) -def ease_in_out_sine(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_sine(t: float) -> float: return -(np.cos(np.pi * t) - 1) / 2 -def ease_in_quad(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_quad(t: float) -> float: return t * t -def ease_out_quad(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_quad(t: float) -> float: return 1 - (1 - t) * (1 - t) -def ease_in_out_quad(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_quad(t: float) -> float: return 2 * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 2) / 2 -def ease_in_cubic(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_cubic(t: float) -> float: return t * t * t -def ease_out_cubic(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_cubic(t: float) -> float: return 1 - pow(1 - t, 3) -def ease_in_out_cubic(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_cubic(t: float) -> float: return 4 * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 3) / 2 -def ease_in_quart(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_quart(t: float) -> float: return t * t * t * t -def ease_out_quart(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_quart(t: float) -> float: return 1 - pow(1 - t, 4) -def ease_in_out_quart(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_quart(t: float) -> float: return 8 * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 4) / 2 -def ease_in_quint(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_quint(t: float) -> float: return t * t * t * t * t -def ease_out_quint(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_quint(t: float) -> float: return 1 - pow(1 - t, 5) -def ease_in_out_quint(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_quint(t: float) -> float: return 16 * t * t * t * t * t if t < 0.5 else 1 - pow(-2 * t + 2, 5) / 2 -def ease_in_expo(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_expo(t: float) -> float: return 0 if t == 0 else pow(2, 10 * t - 10) -def ease_out_expo(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_expo(t: float) -> float: return 1 if t == 1 else 1 - pow(2, -10 * t) -def ease_in_out_expo(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_expo(t: float) -> float: if t == 0: return 0 elif t == 1: @@ -257,15 +254,15 @@ def ease_in_out_expo(t: NumpyOrFloat) -> NumpyOrFloat: return 2 - pow(2, -20 * t + 10) / 2 -def ease_in_circ(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_circ(t: float) -> float: return 1 - sqrt(1 - pow(t, 2)) -def ease_out_circ(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_circ(t: float) -> float: return sqrt(1 - pow(t - 1, 2)) -def ease_in_out_circ(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_circ(t: float) -> float: return ( (1 - sqrt(1 - pow(2 * t, 2))) / 2 if t < 0.5 @@ -273,19 +270,19 @@ def ease_in_out_circ(t: NumpyOrFloat) -> NumpyOrFloat: ) -def ease_in_back(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_back(t: float) -> float: c1 = 1.70158 c3 = c1 + 1 return c3 * t * t * t - c1 * t * t -def ease_out_back(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_back(t: float) -> float: c1 = 1.70158 c3 = c1 + 1 return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2) -def ease_in_out_back(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_back(t: float) -> float: c1 = 1.70158 c2 = c1 * 1.525 return ( @@ -295,7 +292,7 @@ def ease_in_out_back(t: NumpyOrFloat) -> NumpyOrFloat: ) -def ease_in_elastic(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_elastic(t: float) -> float: c4 = (2 * np.pi) / 3 if t == 0: return 0 @@ -305,7 +302,7 @@ def ease_in_elastic(t: NumpyOrFloat) -> NumpyOrFloat: return -pow(2, 10 * t - 10) * np.sin((t * 10 - 10.75) * c4) -def ease_out_elastic(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_elastic(t: float) -> float: c4 = (2 * np.pi) / 3 if t == 0: return 0 @@ -315,7 +312,7 @@ def ease_out_elastic(t: NumpyOrFloat) -> NumpyOrFloat: return pow(2, -10 * t) * np.sin((t * 10 - 0.75) * c4) + 1 -def ease_in_out_elastic(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_elastic(t: float) -> float: c5 = (2 * np.pi) / 4.5 if t == 0: return 0 @@ -327,11 +324,11 @@ def ease_in_out_elastic(t: NumpyOrFloat) -> NumpyOrFloat: return (pow(2, -20 * t + 10) * np.sin((20 * t - 11.125) * c5)) / 2 + 1 -def ease_in_bounce(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_bounce(t: float) -> float: return 1 - ease_out_bounce(1 - t) -def ease_out_bounce(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_out_bounce(t: float) -> float: n1 = 7.5625 d1 = 2.75 @@ -345,7 +342,7 @@ def ease_out_bounce(t: NumpyOrFloat) -> NumpyOrFloat: return n1 * (t - 2.625 / d1) * t + 0.984375 -def ease_in_out_bounce(t: NumpyOrFloat) -> NumpyOrFloat: +def ease_in_out_bounce(t: float) -> float: c1 = 1.70158 c2 = c1 * 1.525 return ( From 68a32f51727a7117e028039c0ad0a1d28f649b65 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sun, 4 Apr 2021 16:22:55 +0200 Subject: [PATCH 04/21] black? --- manim/animation/animation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index 313455bc3c..225983f53f 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -4,13 +4,13 @@ __all__ = ["Animation", "Wait"] -import typing from copy import deepcopy -from typing import Union, Optional, Tuple, Iterator +from typing import Union, Optional, Tuple, Iterator, TYPE_CHECKING +from collections.abc import Callable import numpy as np -if typing.TYPE_CHECKING: +if TYPE_CHECKING: from manim.scene.scene import Scene from .. import logger @@ -34,7 +34,7 @@ def __init__( # with lagged start times lag_ratio: float = DEFAULT_ANIMATION_LAG_RATIO, run_time: float = DEFAULT_ANIMATION_RUN_TIME, - rate_func: typing.Callable[[float], float] = smooth, + rate_func: Callable[[float], float] = smooth, name: str = None, remover: bool = False, # remove a mobject from the screen? suspend_mobject_updating: bool = True, @@ -192,14 +192,14 @@ def get_run_time(self) -> float: def set_rate_func( self, - rate_func: typing.Callable[[float], float], + rate_func: Callable[[float], float], ) -> "Animation": self.rate_func = rate_func return self def get_rate_func( self, - ) -> typing.Callable[[float], float]: + ) -> Callable[[float], float]: return self.rate_func def set_name(self, name: str) -> "Animation": @@ -277,4 +277,4 @@ def update_mobjects(self, dt: float) -> None: pass def interpolate(self, alpha: float) -> None: - pass \ No newline at end of file + pass From 2bedb011a9b56a77fc527164609e83044cc4bfa2 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sun, 4 Apr 2021 16:34:02 +0200 Subject: [PATCH 05/21] `self.mobject` is of type `Union[mobject, None]` --- manim/animation/animation.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index 225983f53f..cd9c273355 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -26,7 +26,7 @@ class Animation: def __init__( self, - mobject: Optional[Mobject], + mobject: Union[Mobject, None], # If lag_ratio is 0, the animation is applied to all submobjects # at the same time # If 1, it is applied to each successively. @@ -42,13 +42,13 @@ def __init__( ) -> None: self._typecheck_input(mobject) self.run_time: float = run_time - self.rate_func = rate_func - self.name = name - self.remover = remover - self.suspend_mobject_updating = suspend_mobject_updating - self.lag_ratio = lag_ratio + self.rate_func: Callable[[float], float] = rate_func + self.name: str = name + self.remover: bool = remover + self.suspend_mobject_updating: bool = suspend_mobject_updating + self.lag_ratio: float = lag_ratio self.starting_mobject: Optional[Mobject] = None - self.mobject = mobject + self.mobject: Union[Mobject, None] = mobject if kwargs: logger.debug("Animation received extra kwargs: %s", kwargs) @@ -60,7 +60,7 @@ def __init__( ) ) - def _typecheck_input(self, mobject: Optional[Mobject]) -> None: + def _typecheck_input(self, mobject: Union[Mobject, None]) -> None: if mobject is None: logger.debug("creating dummy animation") elif not isinstance(mobject, Mobject) and not isinstance( @@ -258,10 +258,10 @@ class Wait(Animation): def __init__( self, duration: float = 1, stop_condition=None, **kwargs ): # what is stop_condition? - self.duration = duration - self.mobject = None + self.duration: float = duration + self.mobject: Union[Mobject, None] = None self.stop_condition = stop_condition - self.is_static_wait = False + self.is_static_wait: bool = False super().__init__(None, **kwargs) def begin(self) -> None: From b6f1a1be2897261ab08d92f437f89ba630da344c Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sun, 4 Apr 2021 17:23:15 +0200 Subject: [PATCH 06/21] Typing Corrections in composition.py --- manim/animation/animation.py | 2 +- manim/animation/composition.py | 46 +++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index cd9c273355..b372e3df1b 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -43,7 +43,7 @@ def __init__( self._typecheck_input(mobject) self.run_time: float = run_time self.rate_func: Callable[[float], float] = rate_func - self.name: str = name + self.name: Optional[str] = name self.remover: bool = remover self.suspend_mobject_updating: bool = suspend_mobject_updating self.lag_ratio: float = lag_ratio diff --git a/manim/animation/composition.py b/manim/animation/composition.py index bfa357a1b9..19cab1556c 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -1,7 +1,9 @@ """Tools for displaying multiple animations at once.""" -import typing + import numpy as np +from typing import Union, TYPE_CHECKING, Optional +from collections.abc import Callable from ..animation.animation import Animation, prepare_animation from ..mobject.mobject import Group, Mobject @@ -10,7 +12,7 @@ from ..utils.iterables import remove_list_redundancies from ..utils.rate_functions import linear -if typing.TYPE_CHECKING: +if TYPE_CHECKING: from ..mobject.types.vectorized_mobject import VGroup __all__ = ["AnimationGroup", "Succession", "LaggedStart", "LaggedStartMap"] @@ -23,9 +25,9 @@ class AnimationGroup(Animation): def __init__( self, *animations: Animation, - group: typing.Union[Group, "VGroup"] = None, - run_time: float = None, - rate_func: typing.Callable[[float], float] = linear, + group: Union[Group, "VGroup", None] = None, + run_time: Optional[float] = None, + rate_func: Callable[[float], float] = linear, lag_ratio: float = 0, **kwargs ) -> None: @@ -34,7 +36,11 @@ def __init__( if self.group is None: self.group = Group( *remove_list_redundancies( - [anim.mobject for anim in self.animations if not anim.is_dummy()] + [ + anim.mobject + for anim in self.animations + if anim.mobject is not None + ] ) ) super().__init__(self.group, rate_func=rate_func, lag_ratio=lag_ratio, **kwargs) @@ -79,14 +85,13 @@ def build_animations_with_timings(self) -> None: (anim, start_time, end_time) """ self.anims_with_timings = [] - curr_time = 0 + curr_time: float = 0 for anim in self.animations: - start_time = curr_time - end_time = start_time + anim.get_run_time() + start_time: float = curr_time + end_time: float = 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) + # Start time of next animation is based on the lag_ratio + curr_time = (1 - self.lag_ratio) * start_time + self.lag_ratio * end_time def interpolate(self, alpha: float) -> None: # Note, if the run_time of AnimationGroup has been @@ -124,9 +129,9 @@ def update_mobjects(self, dt: float) -> None: def update_active_animation(self, index: int) -> None: self.active_index = index if index >= len(self.animations): - self.active_animation = None - self.active_start_time = None - self.active_end_time = None + self.active_animation: Optional[Animation] = None + self.active_start_time: Optional[float] = None + self.active_end_time: Optional[float] = None else: self.active_animation = self.animations[index] self.active_animation.begin() @@ -134,14 +139,15 @@ def update_active_animation(self, index: int) -> None: self.active_end_time = self.anims_with_timings[index][2] def next_animation(self) -> None: - self.active_animation.finish() + if self.active_animation is not None: + self.active_animation.finish() self.update_active_animation(self.active_index + 1) def interpolate(self, alpha: float) -> None: - current_time = interpolate(0, self.run_time, alpha) + current_time = alpha * self.run_time while self.active_end_time is not None and current_time >= self.active_end_time: self.next_animation() - if self.active_animation: + if self.active_animation is not None and self.active_start_time is not None: elapsed = current_time - self.active_start_time active_run_time = self.active_animation.get_run_time() subalpha = elapsed / active_run_time if active_run_time != 0.0 else 1.0 @@ -161,9 +167,9 @@ def __init__( class LaggedStartMap(LaggedStart): def __init__( self, - AnimationClass: Animation, + AnimationClass: Callable[..., Animation], mobject: Mobject, - arg_creator: typing.Callable[[Mobject], str] = None, + arg_creator: Callable[[Mobject], str] = None, run_time: float = 2, **kwargs ) -> None: From 9e8f2e380273ed57789ba0dd92b83ab3e5393eae Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sun, 4 Apr 2021 20:15:58 +0200 Subject: [PATCH 07/21] chasing down more type errors for transformations --- manim/animation/animation.py | 10 ++-- manim/animation/transform.py | 83 ++++++++++++++------------- manim/mobject/mobject.py | 5 +- manim/mobject/mobject_update_utils.py | 1 - 4 files changed, 51 insertions(+), 48 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index b372e3df1b..b53fe6726b 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -5,7 +5,7 @@ from copy import deepcopy -from typing import Union, Optional, Tuple, Iterator, TYPE_CHECKING +from typing import Union, Optional, Tuple, Iterator, Iterable, TYPE_CHECKING from collections.abc import Callable import numpy as np @@ -111,7 +111,7 @@ def get_all_mobjects(self) -> Tuple[Optional[Mobject], Optional[Mobject]]: """ return self.mobject, self.starting_mobject - def get_all_families_zipped(self) -> Iterator[Tuple]: + def get_all_families_zipped(self) -> Iterable[Tuple]: return zip( *[ mob.family_members_with_points() @@ -167,8 +167,9 @@ def interpolate_submobject( self, submobject: Mobject, starting_submobject: Mobject, + # target_copy: Mobject, alpha: float, - ) -> None: + ) -> "Animation": # Typically implemented by subclass pass @@ -209,9 +210,6 @@ def set_name(self, name: str) -> "Animation": def is_remover(self) -> bool: return self.remover - def is_dummy(self) -> bool: - return self.mobject is None - def prepare_animation( anim: Union["Animation", "mobject._AnimationBuilder"] diff --git a/manim/animation/transform.py b/manim/animation/transform.py index 4f6e11e522..182ee42bb1 100644 --- a/manim/animation/transform.py +++ b/manim/animation/transform.py @@ -26,9 +26,11 @@ import inspect import types -import typing import numpy as np +from typing import TYPE_CHECKING, Optional, Union, Dict, List, Any, Iterable, TypeVar +from collections.abc import Callable + from ..animation.animation import Animation from ..constants import DEFAULT_POINTWISE_FUNCTION_RUN_TIME, DEGREES, OUT @@ -37,7 +39,7 @@ from ..utils.paths import path_along_arc, straight_path from ..utils.rate_functions import smooth, squish_rate_func -if typing.TYPE_CHECKING: +if TYPE_CHECKING: from ..scene.scene import Scene @@ -45,18 +47,20 @@ class Transform(Animation): def __init__( self, mobject: Mobject, - target_mobject: typing.Optional[Mobject] = None, - path_func: typing.Optional[typing.Callable] = None, + target_mobject: Optional[Mobject] = None, + path_func: Optional[Callable] = None, path_arc: float = 0, path_arc_axis: np.ndarray = OUT, replace_mobject_with_target_in_scene: bool = False, **kwargs, ) -> None: - self.path_arc = path_arc - self.path_func = path_func - self.path_arc_axis = path_arc_axis - self.replace_mobject_with_target_in_scene = replace_mobject_with_target_in_scene - self.target_mobject = target_mobject + self.path_arc: float = path_arc + self.path_func: Optional[Callable] = path_func + self.path_arc_axis: np.ndarray = path_arc_axis + self.replace_mobject_with_target_in_scene: bool = ( + replace_mobject_with_target_in_scene + ) + self.target_mobject: Optional[Mobject] = target_mobject super().__init__(mobject, **kwargs) self._init_path_func() @@ -77,13 +81,15 @@ def begin(self) -> None: # preserved. self.target_mobject = self.create_target() self.check_target_mobject_validity() - self.target_copy = self.target_mobject.copy() - # Note, this potentially changes the structure - # of both mobject and target_mobject - self.mobject.align_data(self.target_copy) + if self.target_mobject is not None: + self.target_copy = self.target_mobject.copy() + # Note, this potentially changes the structure + # of both mobject and target_mobject + if self.mobject is not None: + self.mobject.align_data(self.target_copy) super().begin() - def create_target(self) -> typing.Union[Mobject, None]: + def create_target(self) -> Union[Mobject, None]: # Has no meaningful effect here, but may be useful # in subclasses return self.target_mobject @@ -100,31 +106,26 @@ def clean_up_from_scene(self, scene: "Scene") -> None: scene.remove(self.mobject) scene.add(self.target_mobject) - def update_config(self, **kwargs: typing.Dict[str, typing.Any]) -> None: - Animation.update_config(self, **kwargs) - if "path_arc" in kwargs: - self.path_func = path_along_arc( - kwargs["path_arc"], kwargs.get("path_arc_axis", OUT) - ) - - def get_all_mobjects(self) -> typing.List[Mobject]: + def get_all_mobjects(self) -> List[Mobject]: return [ + mob + for mob in [ + self.mobject, + self.starting_mobject, + self.target_mobject, + self.target_copy, + ] + if mob is not None + ] + + def get_all_families_zipped(self) -> Iterable[tuple]: # more precise typing? + mobs = [ self.mobject, self.starting_mobject, - self.target_mobject, self.target_copy, ] - - def get_all_families_zipped(self) -> typing.Iterable[tuple]: # more precise typing? return zip( - *[ - mob.family_members_with_points() - for mob in [ - self.mobject, - self.starting_mobject, - self.target_copy, - ] - ] + *[mob.family_members_with_points() for mob in mobs if mob is not None] ) def interpolate_submobject( @@ -133,7 +134,7 @@ def interpolate_submobject( starting_submobject: Mobject, target_copy: Mobject, alpha: float, - ) -> "Transform": # doesn't match the parent class? + ) -> "Transform": submobject.interpolate(starting_submobject, target_copy, alpha, self.path_func) return self @@ -200,7 +201,7 @@ def __init__(self, mobject, methods): class ApplyMethod(Transform): def __init__( self, method: types.MethodType, *args, **kwargs - ) -> None: # method typing? for args? + ) -> None: # method typing (we want to specify Mobject method)? for args? """ Method is a method of Mobject, ``args`` are arguments for that method. Key word arguments should be passed in @@ -302,7 +303,8 @@ def __init__(self, function: types.MethodType, mobject: Mobject, **kwargs) -> No self.function = function super().__init__(mobject, **kwargs) - def create_target(self) -> typing.Any: + def create_target(self) -> Any: + assert isinstance(self.mobject, Mobject) target = self.function(self.mobject.copy()) if not isinstance(target, Mobject): raise TypeError( @@ -371,7 +373,7 @@ def __init__( self, start_anim: Animation, end_anim: Animation, - rate_func: typing.Callable = squish_rate_func(smooth), + rate_func: Callable = squish_rate_func(smooth), **kwargs, ) -> None: self.start_anim = start_anim @@ -382,14 +384,15 @@ def __init__( self.run_time = max(start_anim.run_time, end_anim.run_time) for anim in start_anim, end_anim: anim.set_run_time(self.run_time) - if ( - start_anim.starting_mobject.get_num_points() + start_anim.starting_mobject is not None + and end_anim.starting_mobject is not None + and start_anim.starting_mobject.get_num_points() != end_anim.starting_mobject.get_num_points() ): start_anim.starting_mobject.align_data(end_anim.starting_mobject) for anim in start_anim, end_anim: - if hasattr(anim, "target_mobject"): + if isinstance(anim, Transform) and anim.starting_mobject is not None: anim.starting_mobject.align_data(anim.target_mobject) super().__init__( diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index e93970311d..bcf40e3a3a 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -1994,7 +1994,7 @@ def shuffle_submobjects(self, *args, **kwargs): return self.shuffle(*args, **kwargs) # Alignment - def align_data(self, mobject): + def align_data(self, mobject: "Mobject"): self.null_point_align(mobject) self.align_submobjects(mobject) self.align_points(mobject) @@ -2185,6 +2185,9 @@ def __init__(self, *mobjects, **kwargs): Mobject.__init__(self, **kwargs) self.add(*mobjects) + def copy(self) -> "Group": + pass + class _AnimationBuilder: def __init__(self, mobject): diff --git a/manim/mobject/mobject_update_utils.py b/manim/mobject/mobject_update_utils.py index 66c9ad5b03..90658b53aa 100644 --- a/manim/mobject/mobject_update_utils.py +++ b/manim/mobject/mobject_update_utils.py @@ -83,7 +83,6 @@ def turn_animation_into_updater(animation, cycle=False, **kwargs): the updater will be popped upon completion """ mobject = animation.mobject - animation.update_config(**kwargs) animation.suspend_mobject_updating = False animation.begin() animation.total_time = 0 From 2ada98e4bd41600edd7d995fd7a9304d2040552c Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sun, 4 Apr 2021 20:21:44 +0200 Subject: [PATCH 08/21] mobject.copy needs better typing --- manim/mobject/mobject.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index bcf40e3a3a..9cd0e74d7d 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -2184,10 +2184,7 @@ class Group(Mobject): def __init__(self, *mobjects, **kwargs): Mobject.__init__(self, **kwargs) self.add(*mobjects) - - def copy(self) -> "Group": - pass - + class _AnimationBuilder: def __init__(self, mobject): From a18f6958f30836b70a85deaf3ff4eef40a3ad322 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Sun, 4 Apr 2021 21:40:41 +0200 Subject: [PATCH 09/21] mobject.copy preserves derived type --- manim/mobject/mobject.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 9cd0e74d7d..769f993185 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -42,6 +42,7 @@ # TODO: Explain array_attrs Updater = Union[Callable[["Mobject"], None], Callable[["Mobject", float], None]] +T = TypeVar("T", bound="Mobject") class Mobject(Container): @@ -606,7 +607,7 @@ def save_image(self, name=None): Path(config.get_dir("video_dir")).joinpath((name or str(self)) + ".png") ) - def copy(self) -> "Mobject": + def copy(self: T) -> T: """Create and return an identical copy of the Mobject including all submobjects. Returns @@ -2184,7 +2185,7 @@ class Group(Mobject): def __init__(self, *mobjects, **kwargs): Mobject.__init__(self, **kwargs) self.add(*mobjects) - + class _AnimationBuilder: def __init__(self, mobject): From 9699d8b794c00a0a04aced00c34bbcf9ce08320f Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Tue, 6 Apr 2021 19:36:09 +0200 Subject: [PATCH 10/21] Every Animation has a mobject, even Wait --- manim/animation/animation.py | 34 ++++++++++++++-------------------- manim/animation/composition.py | 21 ++++----------------- manim/mobject/mobject.py | 2 +- manim/scene/scene.py | 2 +- 4 files changed, 20 insertions(+), 39 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index b53fe6726b..3ed2ea859a 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -47,8 +47,8 @@ def __init__( self.remover: bool = remover self.suspend_mobject_updating: bool = suspend_mobject_updating self.lag_ratio: float = lag_ratio - self.starting_mobject: Optional[Mobject] = None - self.mobject: Union[Mobject, None] = mobject + self.starting_mobject: Mobject = Mobject() + self.mobject: Mobject = mobject if mobject is not None else Mobject() if kwargs: logger.debug("Animation received extra kwargs: %s", kwargs) @@ -62,7 +62,7 @@ def __init__( def _typecheck_input(self, mobject: Union[Mobject, None]) -> None: if mobject is None: - logger.debug("creating dummy animation") + logger.debug("Animation with empty mobject") elif not isinstance(mobject, Mobject) and not isinstance( mobject, OpenGLMobject ): @@ -82,7 +82,7 @@ def begin(self) -> None: # especially any mobject copying, should live in # this method self.starting_mobject = self.create_starting_mobject() - if self.suspend_mobject_updating and self.mobject is not None: + if self.suspend_mobject_updating: # All calls to self.mobject's internal updaters # during the animation, either from this Animation # or from the surrounding scene, should do nothing. @@ -101,11 +101,11 @@ def clean_up_from_scene(self, scene: "Scene") -> None: if self.is_remover(): scene.remove(self.mobject) - def create_starting_mobject(self) -> Optional[Mobject]: + def create_starting_mobject(self) -> Mobject: # Keep track of where the mobject starts - return self.mobject.copy() if self.mobject is not None else None + return self.mobject.copy() - def get_all_mobjects(self) -> Tuple[Optional[Mobject], Optional[Mobject]]: + def get_all_mobjects(self) -> Tuple[Mobject, Mobject]: """ Ordering must match the ordering of arguments to interpolate_submobject """ @@ -113,11 +113,7 @@ def get_all_mobjects(self) -> Tuple[Optional[Mobject], Optional[Mobject]]: def get_all_families_zipped(self) -> Iterable[Tuple]: return zip( - *[ - mob.family_members_with_points() - for mob in self.get_all_mobjects() - if mob is not None - ] + *[mob.family_members_with_points() for mob in self.get_all_mobjects()] ) def update_mobjects(self, dt: float) -> None: @@ -142,7 +138,6 @@ def copy(self) -> "Animation": # Methods for interpolation, the mean of an Animation def interpolate(self, alpha: float) -> None: - # alpha = np.clip(alpha, 0, 1) alpha = min(max(alpha, 0), 1) self.interpolate_mobject(self.rate_func(alpha)) @@ -167,13 +162,13 @@ def interpolate_submobject( self, submobject: Mobject, starting_submobject: Mobject, - # target_copy: Mobject, + # target_copy: Mobject, #Todo: fix - signature of interpolate_submobject differes in Transform(). alpha: float, ) -> "Animation": # Typically implemented by subclass pass - def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int): + def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int) -> float: # TODO, make this more understandable, and/or combine # its functionality with AnimationGroup's method # build_animations_with_timings @@ -181,7 +176,7 @@ def get_sub_alpha(self, alpha: float, index: int, num_submobjects: int): full_length = (num_submobjects - 1) * lag_ratio + 1 value = alpha * full_length lower = index * lag_ratio - return np.clip((value - lower), 0, 1) + return min(max((value - lower), 0), 1) # Getters and setters def set_run_time(self, run_time: float) -> "Animation": @@ -254,13 +249,12 @@ def prepare_animation( class Wait(Animation): def __init__( - self, duration: float = 1, stop_condition=None, **kwargs + self, run_time: float = 1, stop_condition=None, **kwargs ): # what is stop_condition? - self.duration: float = duration - self.mobject: Union[Mobject, None] = None + self.duration: float = run_time self.stop_condition = stop_condition self.is_static_wait: bool = False - super().__init__(None, **kwargs) + super().__init__(None, run_time=run_time, **kwargs) def begin(self) -> None: pass diff --git a/manim/animation/composition.py b/manim/animation/composition.py index 19cab1556c..05e1834719 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -35,26 +35,14 @@ def __init__( self.group = group if self.group is None: self.group = Group( - *remove_list_redundancies( - [ - anim.mobject - for anim in self.animations - if anim.mobject is not None - ] - ) + *remove_list_redundancies([anim.mobject for anim in self.animations]) ) super().__init__(self.group, rate_func=rate_func, lag_ratio=lag_ratio, **kwargs) - self.run_time = run_time - self.init_run_time() + self.run_time: float = self.init_run_time(run_time) def get_all_mobjects(self) -> Group: return self.group - def get_run_time(self) -> float: - if super().get_run_time() is None: - self.init_run_time() - return super().get_run_time() - def begin(self) -> None: for anim in self.animations: anim.begin() @@ -71,13 +59,13 @@ def update_mobjects(self, dt: float) -> None: for anim in self.animations: anim.update_mobjects(dt) - def init_run_time(self) -> None: + def init_run_time(self, run_time) -> float: self.build_animations_with_timings() if self.anims_with_timings: self.max_end_time = np.max([awt[2] for awt in self.anims_with_timings]) else: self.max_end_time = 0 - self.run_time = self.max_end_time if self.run_time is None else self.run_time + return self.max_end_time if run_time is None else run_time def build_animations_with_timings(self) -> None: """ @@ -115,7 +103,6 @@ def __init__(self, *animations: Animation, lag_ratio: float = 1, **kwargs) -> No def begin(self) -> None: assert len(self.animations) > 0 - self.init_run_time() self.update_active_animation(0) def finish(self) -> None: diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 769f993185..3b95304187 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -13,7 +13,7 @@ import warnings from functools import reduce from pathlib import Path -from typing import Callable, List, Optional, Union +from typing import Callable, List, Optional, Union, TypeVar import numpy as np from colour import Color diff --git a/manim/scene/scene.py b/manim/scene/scene.py index 30e9769646..d26e89c14b 100644 --- a/manim/scene/scene.py +++ b/manim/scene/scene.py @@ -803,7 +803,7 @@ def play(self, *args, **kwargs): self.renderer.play(self, *args, **kwargs) def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None): - self.play(Wait(duration=duration, stop_condition=stop_condition)) + self.play(Wait(run_time=duration, stop_condition=stop_condition)) def wait_until(self, stop_condition, max_time=60): """ From 451d6034434f3f55d9e7f6b70252485924c4a0ef Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Tue, 6 Apr 2021 20:25:42 +0200 Subject: [PATCH 11/21] Typing errors in creation.py --- manim/animation/creation.py | 112 +++++++++++++++++------------------ manim/animation/transform.py | 41 +++++-------- 2 files changed, 68 insertions(+), 85 deletions(-) diff --git a/manim/animation/creation.py b/manim/animation/creation.py index 3066640ce1..ff1d5e6287 100644 --- a/manim/animation/creation.py +++ b/manim/animation/creation.py @@ -72,14 +72,15 @@ def construct(self): import itertools as it -import typing import numpy as np from colour import Color from .. import logger -if typing.TYPE_CHECKING: +from typing import Union, Optional, Tuple, Callable, Dict, List, Iterable, TYPE_CHECKING + +if TYPE_CHECKING: from manim.mobject.svg.text_mobject import Text from ..animation.animation import Animation @@ -105,7 +106,7 @@ class ShowPartial(Animation): """ - def __init__(self, mobject: typing.Union[VMobject, OpenGLVMobject], **kwargs): + def __init__(self, mobject: Union[Optional[Mobject], OpenGLVMobject], **kwargs): if not isinstance(mobject, (VMobject, OpenGLVMobject)): raise TypeError("This Animation only works on vectorized mobjects") super().__init__(mobject, **kwargs) @@ -150,13 +151,13 @@ def construct(self): def __init__( self, - mobject: typing.Union[VMobject, OpenGLVMobject], + mobject: Union[VMobject, OpenGLVMobject], lag_ratio: float = 1.0, **kwargs, ) -> None: super().__init__(mobject, lag_ratio=lag_ratio, **kwargs) - def _get_bounds(self, alpha: float) -> typing.Tuple[int, float]: + def _get_bounds(self, alpha: float) -> Tuple[int, float]: return (0, alpha) @@ -169,7 +170,7 @@ def __init__(self, mobject: VMobject, lag_ratio: float = 1.0, **kwargs) -> None: ) super().__init__(mobject, lag_ratio=lag_ratio, **kwargs) - def _get_bounds(self, alpha: float) -> typing.Tuple[int, float]: + def _get_bounds(self, alpha: float) -> Tuple[int, float]: return (0, alpha) @@ -192,10 +193,8 @@ def construct(self): def __init__( self, - mobject: typing.Union[VMobject, OpenGLVMobject], - rate_func: typing.Callable[[float, float], np.ndarray] = lambda t: smooth( - 1 - t - ), + mobject: Union[VMobject, OpenGLVMobject], + rate_func: Callable[[float], float] = lambda t: smooth(1 - t), remover: bool = True, **kwargs, ) -> None: @@ -216,13 +215,13 @@ def construct(self): def __init__( self, - vmobject: typing.Union[VMobject, OpenGLVMobject], + vmobject: Union[VMobject, OpenGLVMobject], run_time: float = 2, - rate_func: typing.Callable[[float], np.ndarray] = double_smooth, + rate_func: Callable[[float], np.ndarray] = double_smooth, stroke_width: float = 2, stroke_color: str = None, - draw_border_animation_config: typing.Dict = {}, # what does this dict accept? - fill_animation_config: typing.Dict = {}, + draw_border_animation_config: Dict = {}, # what does this dict accept? + fill_animation_config: Dict = {}, **kwargs, ) -> None: self._typecheck_input(vmobject) @@ -231,18 +230,12 @@ def __init__( self.stroke_color = stroke_color self.draw_border_animation_config = draw_border_animation_config self.fill_animation_config = fill_animation_config - self.outline = None + self.outline = self.get_outline() - def _typecheck_input( - self, vmobject: typing.Union[VMobject, OpenGLVMobject] - ) -> None: + def _typecheck_input(self, vmobject: Union[VMobject, OpenGLVMobject]) -> None: if not isinstance(vmobject, (VMobject, OpenGLVMobject)): raise TypeError("DrawBorderThenFill only works for vectorized Mobjects") - def begin(self) -> None: - self.outline = self.get_outline() - super().begin() - def get_outline(self) -> Mobject: outline = self.mobject.copy() outline.set_fill(opacity=0) @@ -250,21 +243,21 @@ def get_outline(self) -> Mobject: sm.set_stroke(color=self.get_stroke_color(sm), width=self.stroke_width) return outline - def get_stroke_color( - self, vmobject: typing.Union[VMobject, OpenGLVMobject] - ) -> Color: + def get_stroke_color(self, vmobject: Union[VMobject, OpenGLVMobject]) -> Color: if self.stroke_color: return self.stroke_color elif vmobject.get_stroke_width() > 0: return vmobject.get_stroke_color() return vmobject.get_color() - def get_all_mobjects(self) -> typing.List[typing.Union[Mobject, None]]: + def get_all_mobjects(self) -> List[Union[Mobject, None]]: return [*super().get_all_mobjects(), self.outline] def interpolate_submobject( self, submobject: Mobject, starting_submobject: Mobject, outline, alpha: float ) -> None: # Fixme: not matching the parent class? What is outline doing here? + index: int + subalpha: int index, subalpha = integer_interpolate(0, 2, alpha) if index == 0: submobject.pointwise_become_partial(outline, 0, subalpha) @@ -287,34 +280,38 @@ def construct(self): def __init__( self, - vmobject: typing.Union[VMobject, OpenGLVMobject], - run_time: float = None, - lag_ratio: float = None, - rate_func: typing.Callable[[float], np.ndarray] = linear, + vmobject: Union[VMobject, OpenGLVMobject], + rate_func: Callable[[float], float] = linear, **kwargs, ) -> None: - self.run_time = run_time - self.lag_ratio = lag_ratio - self._set_default_config_from_length(vmobject) + run_time: Optional[float] = kwargs.pop("run_time", None) + lag_ratio: Optional[float] = kwargs.pop("lag_ratio", None) + run_time, lag_ratio = self._set_default_config_from_length( + vmobject, run_time, lag_ratio + ) super().__init__( vmobject, - run_time=self.run_time, - lag_ratio=self.lag_ratio, rate_func=rate_func, + run_time=run_time, + lag_ratio=lag_ratio, **kwargs, ) def _set_default_config_from_length( - self, vmobject: typing.Union[VMobject, OpenGLVMobject] - ) -> None: + self, + vmobject: Union[VMobject, OpenGLVMobject], + run_time: Optional[float], + lag_ratio: Optional[float], + ) -> Tuple[float, float]: length = len(vmobject.family_members_with_points()) - if self.run_time is None: + if run_time is None: if length < 15: - self.run_time = 1 + run_time = 1 else: - self.run_time = 2 - if self.lag_ratio is None: - self.lag_ratio = min(4.0 / length, 0.2) + run_time = 2 + if lag_ratio is None: + lag_ratio = min(4.0 / length, 0.2) + return run_time, lag_ratio class Unwrite(Write): @@ -349,18 +346,18 @@ def construct(self): def __init__( self, vmobject: VMobject, - run_time: float = None, - lag_ratio: float = None, - rate_func: typing.Callable[[float], np.ndarray] = linear, + rate_func: Callable[[float], np.ndarray] = linear, reverse: bool = False, **kwargs, ) -> None: self.vmobject = vmobject - self.run_time = run_time - self.lag_ratio = lag_ratio self.reverse = reverse - self._set_default_config_from_length(vmobject) + run_time: Optional[float] = kwargs.pop("run_time", None) + lag_ratio: Optional[float] = kwargs.pop("lag_ratio", None) + run_time, lag_ratio = self._set_default_config_from_length( + vmobject, run_time, lag_ratio + ) super().__init__( vmobject, run_time=run_time, @@ -403,7 +400,7 @@ def __init__( self, group: Mobject, suspend_mobject_updating: bool = False, - int_func: typing.Callable[[np.ndarray], np.ndarray] = np.floor, + int_func: Callable[[np.ndarray], np.ndarray] = np.floor, **kwargs, ) -> None: self.all_submobs = list(group.submobjects) @@ -442,24 +439,23 @@ def __init__( self, text: "Text", suspend_mobject_updating: bool = False, - int_func: typing.Callable[[np.ndarray], np.ndarray] = np.ceil, - rate_func: typing.Callable[[float], float] = linear, + int_func: Callable[[np.ndarray], np.ndarray] = np.ceil, + rate_func: Callable[[float], float] = linear, time_per_char: float = 0.1, - run_time: typing.Optional[float] = None, + run_time: Optional[float] = None, **kwargs, ) -> None: # time_per_char must be above 0.06, or the animation won't finish self.time_per_char = time_per_char - self.run_time = run_time - if self.run_time is None: - self.run_time = np.max((0.06, self.time_per_char)) * len(text) + if run_time is None: + run_time = np.max((0.06, self.time_per_char)) * len(text) super().__init__( text, suspend_mobject_updating=suspend_mobject_updating, int_func=int_func, rate_func=rate_func, - run_time=self.run_time, + run_time=run_time, **kwargs, ) @@ -469,8 +465,8 @@ class ShowSubmobjectsOneByOne(ShowIncreasingSubsets): def __init__( self, - group: typing.Iterable[Mobject], - int_func: typing.Callable[[np.ndarray], np.ndarray] = np.ceil, + group: Iterable[Mobject], + int_func: Callable[[np.ndarray], np.ndarray] = np.ceil, **kwargs, ) -> None: new_group = Group(*group) diff --git a/manim/animation/transform.py b/manim/animation/transform.py index 182ee42bb1..1e4518aed0 100644 --- a/manim/animation/transform.py +++ b/manim/animation/transform.py @@ -46,7 +46,7 @@ class Transform(Animation): def __init__( self, - mobject: Mobject, + mobject: Optional[Mobject], target_mobject: Optional[Mobject] = None, path_func: Optional[Callable] = None, path_arc: float = 0, @@ -60,7 +60,9 @@ def __init__( self.replace_mobject_with_target_in_scene: bool = ( replace_mobject_with_target_in_scene ) - self.target_mobject: Optional[Mobject] = target_mobject + self.target_mobject: Mobject = ( + target_mobject if target_mobject is not None else Mobject() + ) super().__init__(mobject, **kwargs) self._init_path_func() @@ -80,26 +82,17 @@ def begin(self) -> None: # call so that the actual target_mobject stays # preserved. self.target_mobject = self.create_target() - self.check_target_mobject_validity() - if self.target_mobject is not None: - self.target_copy = self.target_mobject.copy() - # Note, this potentially changes the structure - # of both mobject and target_mobject - if self.mobject is not None: - self.mobject.align_data(self.target_copy) + self.target_copy = self.target_mobject.copy() + # Note, this potentially changes the structure + # of both mobject and target_mobject + self.mobject.align_data(self.target_copy) super().begin() - def create_target(self) -> Union[Mobject, None]: + def create_target(self) -> Mobject: # Has no meaningful effect here, but may be useful # in subclasses return self.target_mobject - def check_target_mobject_validity(self) -> None: - if self.target_mobject is None: - raise NotImplementedError( - f"{self.__class__.__name__}.create_target not properly implemented" - ) - def clean_up_from_scene(self, scene: "Scene") -> None: super().clean_up_from_scene(scene) if self.replace_mobject_with_target_in_scene: @@ -108,14 +101,10 @@ def clean_up_from_scene(self, scene: "Scene") -> None: def get_all_mobjects(self) -> List[Mobject]: return [ - mob - for mob in [ - self.mobject, - self.starting_mobject, - self.target_mobject, - self.target_copy, - ] - if mob is not None + self.mobject, + self.starting_mobject, + self.target_mobject, + self.target_copy, ] def get_all_families_zipped(self) -> Iterable[tuple]: # more precise typing? @@ -124,9 +113,7 @@ def get_all_families_zipped(self) -> Iterable[tuple]: # more precise typing? self.starting_mobject, self.target_copy, ] - return zip( - *[mob.family_members_with_points() for mob in mobs if mob is not None] - ) + return zip(*[mob.family_members_with_points() for mob in mobs]) def interpolate_submobject( self, From eddf9428ce4310f3dc91398b9723273d845628ab Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Tue, 6 Apr 2021 20:27:50 +0200 Subject: [PATCH 12/21] Typing errors in creation.py --- manim/animation/creation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manim/animation/creation.py b/manim/animation/creation.py index ff1d5e6287..8b7342e78e 100644 --- a/manim/animation/creation.py +++ b/manim/animation/creation.py @@ -106,7 +106,7 @@ class ShowPartial(Animation): """ - def __init__(self, mobject: Union[Optional[Mobject], OpenGLVMobject], **kwargs): + def __init__(self, mobject: Union[Mobject, OpenGLVMobject, None], **kwargs): if not isinstance(mobject, (VMobject, OpenGLVMobject)): raise TypeError("This Animation only works on vectorized mobjects") super().__init__(mobject, **kwargs) @@ -217,7 +217,7 @@ def __init__( self, vmobject: Union[VMobject, OpenGLVMobject], run_time: float = 2, - rate_func: Callable[[float], np.ndarray] = double_smooth, + rate_func: Callable[[float], float] = double_smooth, stroke_width: float = 2, stroke_color: str = None, draw_border_animation_config: Dict = {}, # what does this dict accept? From 7a16d443f3414855063f9e2c5532ed36387f3dad Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Tue, 6 Apr 2021 20:47:18 +0200 Subject: [PATCH 13/21] quick fix Wait to work with GL test --- manim/animation/animation.py | 2 ++ manim/animation/transform.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index 3ed2ea859a..84935badf9 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -255,6 +255,8 @@ def __init__( self.stop_condition = stop_condition self.is_static_wait: bool = False super().__init__(None, run_time=run_time, **kwargs) + # quick fix to work in opengl setting: + self.mobject.shader_wrapper_list = [] def begin(self) -> None: pass diff --git a/manim/animation/transform.py b/manim/animation/transform.py index 1e4518aed0..a06c87a236 100644 --- a/manim/animation/transform.py +++ b/manim/animation/transform.py @@ -187,7 +187,7 @@ def __init__(self, mobject, methods): class ApplyMethod(Transform): def __init__( - self, method: types.MethodType, *args, **kwargs + self, method: Callable, *args, **kwargs ) -> None: # method typing (we want to specify Mobject method)? for args? """ Method is a method of Mobject, ``args`` are arguments for @@ -202,7 +202,7 @@ def __init__( self.method_args = args super().__init__(method.__self__, **kwargs) - def check_validity_of_input(self, method: types.MethodType) -> None: + def check_validity_of_input(self, method: Callable) -> None: if not inspect.ismethod(method): raise ValueError( "Whoops, looks like you accidentally invoked " From 98e183edd16ad347fbf5defd89758c4dda12cd29 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Tue, 6 Apr 2021 21:49:12 +0200 Subject: [PATCH 14/21] isort --- manim/animation/animation.py | 4 ++-- manim/animation/composition.py | 5 +++-- manim/animation/creation.py | 3 +-- manim/animation/transform.py | 5 ++--- manim/mobject/mobject.py | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index 84935badf9..1250b21fa0 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -4,9 +4,9 @@ __all__ = ["Animation", "Wait"] -from copy import deepcopy -from typing import Union, Optional, Tuple, Iterator, Iterable, TYPE_CHECKING from collections.abc import Callable +from copy import deepcopy +from typing import TYPE_CHECKING, Iterable, Iterator, Optional, Tuple, Union import numpy as np diff --git a/manim/animation/composition.py b/manim/animation/composition.py index 05e1834719..9179fd715f 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -1,9 +1,10 @@ """Tools for displaying multiple animations at once.""" -import numpy as np -from typing import Union, TYPE_CHECKING, Optional from collections.abc import Callable +from typing import TYPE_CHECKING, Optional, Union + +import numpy as np from ..animation.animation import Animation, prepare_animation from ..mobject.mobject import Group, Mobject diff --git a/manim/animation/creation.py b/manim/animation/creation.py index 8b7342e78e..636bd0e386 100644 --- a/manim/animation/creation.py +++ b/manim/animation/creation.py @@ -72,14 +72,13 @@ def construct(self): import itertools as it +from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Optional, Tuple, Union import numpy as np from colour import Color from .. import logger -from typing import Union, Optional, Tuple, Callable, Dict, List, Iterable, TYPE_CHECKING - if TYPE_CHECKING: from manim.mobject.svg.text_mobject import Text diff --git a/manim/animation/transform.py b/manim/animation/transform.py index a06c87a236..dfcebada28 100644 --- a/manim/animation/transform.py +++ b/manim/animation/transform.py @@ -26,11 +26,10 @@ import inspect import types - -import numpy as np -from typing import TYPE_CHECKING, Optional, Union, Dict, List, Any, Iterable, TypeVar from collections.abc import Callable +from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, TypeVar, Union +import numpy as np from ..animation.animation import Animation from ..constants import DEFAULT_POINTWISE_FUNCTION_RUN_TIME, DEGREES, OUT diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 3b95304187..4243fbc152 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -13,7 +13,7 @@ import warnings from functools import reduce from pathlib import Path -from typing import Callable, List, Optional, Union, TypeVar +from typing import Callable, List, Optional, TypeVar, Union import numpy as np from colour import Color From 72c3033f9fea0ffd233084d8c9b7f4902cbf8341 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Wed, 7 Apr 2021 14:53:52 +0200 Subject: [PATCH 15/21] import Callable from Typying to make test for python <3.9 work again --- manim/animation/animation.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/manim/animation/animation.py b/manim/animation/animation.py index 1250b21fa0..001f8ec259 100644 --- a/manim/animation/animation.py +++ b/manim/animation/animation.py @@ -4,11 +4,8 @@ __all__ = ["Animation", "Wait"] -from collections.abc import Callable from copy import deepcopy -from typing import TYPE_CHECKING, Iterable, Iterator, Optional, Tuple, Union - -import numpy as np +from typing import TYPE_CHECKING, Callable, Iterable, Optional, Tuple, Union if TYPE_CHECKING: from manim.scene.scene import Scene From a20c81d525177d65c9da2e07b1d329863a19d2a3 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Wed, 7 Apr 2021 15:00:46 +0200 Subject: [PATCH 16/21] import Callable from Typying to make test for python <3.9 work again --- manim/animation/composition.py | 3 +-- manim/animation/transform.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/manim/animation/composition.py b/manim/animation/composition.py index 9179fd715f..759946e646 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -1,8 +1,7 @@ """Tools for displaying multiple animations at once.""" -from collections.abc import Callable -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING, Callable, Optional, Union import numpy as np diff --git a/manim/animation/transform.py b/manim/animation/transform.py index dfcebada28..d2be5b9207 100644 --- a/manim/animation/transform.py +++ b/manim/animation/transform.py @@ -26,8 +26,17 @@ import inspect import types -from collections.abc import Callable -from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, TypeVar, Union +from typing import ( + TYPE_CHECKING, + Callable, + Any, + Dict, + Iterable, + List, + Optional, + TypeVar, + Union, +) import numpy as np From 6f8ac0d708a4708cfe42b5a6793df3940a9276be Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 7 Apr 2021 13:01:25 +0000 Subject: [PATCH 17/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- manim/animation/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/animation/transform.py b/manim/animation/transform.py index d2be5b9207..1fd472b9aa 100644 --- a/manim/animation/transform.py +++ b/manim/animation/transform.py @@ -28,8 +28,8 @@ import types from typing import ( TYPE_CHECKING, - Callable, Any, + Callable, Dict, Iterable, List, From 7f6ce4807663886ad4455d64b9d37287c00292b5 Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Wed, 7 Apr 2021 15:07:42 +0200 Subject: [PATCH 18/21] to make flake8 happy --- manim/animation/composition.py | 1 - 1 file changed, 1 deletion(-) diff --git a/manim/animation/composition.py b/manim/animation/composition.py index 759946e646..7cfa6b897d 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -8,7 +8,6 @@ from ..animation.animation import Animation, prepare_animation from ..mobject.mobject import Group, Mobject from ..scene.scene import Scene -from ..utils.bezier import interpolate from ..utils.iterables import remove_list_redundancies from ..utils.rate_functions import linear From ac52947e675dac6bdbd349945bf6b10b44c9a53e Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Wed, 7 Apr 2021 15:46:22 +0200 Subject: [PATCH 19/21] rate functions take float to float --- manim/animation/creation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/animation/creation.py b/manim/animation/creation.py index 636bd0e386..363fdb6a8a 100644 --- a/manim/animation/creation.py +++ b/manim/animation/creation.py @@ -345,7 +345,7 @@ def construct(self): def __init__( self, vmobject: VMobject, - rate_func: Callable[[float], np.ndarray] = linear, + rate_func: Callable[[float], float] = linear, reverse: bool = False, **kwargs, ) -> None: From 7cdfee6406f31e1d55fed7b76fb52373223fa90b Mon Sep 17 00:00:00 2001 From: Aron Fischer Date: Wed, 7 Apr 2021 16:08:58 +0200 Subject: [PATCH 20/21] re-added begin to DrawBorderTheenFill --- manim/animation/creation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/manim/animation/creation.py b/manim/animation/creation.py index 363fdb6a8a..a0b2415543 100644 --- a/manim/animation/creation.py +++ b/manim/animation/creation.py @@ -235,6 +235,10 @@ def _typecheck_input(self, vmobject: Union[VMobject, OpenGLVMobject]) -> None: if not isinstance(vmobject, (VMobject, OpenGLVMobject)): raise TypeError("DrawBorderThenFill only works for vectorized Mobjects") + def begin(self) -> None: + self.outline = self.get_outline() + super().begin() + def get_outline(self) -> Mobject: outline = self.mobject.copy() outline.set_fill(opacity=0) From f1d28ee5c51fb7b4fb4ef7c6185c3987af69c0c8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 Apr 2021 21:53:29 +0000 Subject: [PATCH 21/21] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- manim/animation/composition.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manim/animation/composition.py b/manim/animation/composition.py index c419d094b7..6e207da8ca 100644 --- a/manim/animation/composition.py +++ b/manim/animation/composition.py @@ -36,7 +36,9 @@ def __init__( self.animations = [prepare_animation(anim) for anim in animations] self.group = group if self.group is None: - mobjects = remove_list_redundancies([anim.mobject for anim in self.animations]) + mobjects = remove_list_redundancies( + [anim.mobject for anim in self.animations] + ) if config["renderer"] == "opengl": self.group = OpenGLGroup(*mobjects) else: