An animation runs from 0, its start, to 1, its end. Its rate function says how far along the
change is at each moment of that time: rate_func(t) for t from 0 to 1. The default,
smooth, starts slowly, speeds up and ends slowly; linear
keeps one speed. Give another to play or to any animation:
self.play(square.animate.shift(m.RIGHT), rate_func=m.rush_into).
A rate function may go past 1 and back, for a bounce, or come back to 0, so that the
animation ends where it started. Choose one to see it move:
@unit_intervaldefsmooth(t:float,inflection:float=10.0)->float:"""Slow, fast, slow: a sigmoid curve, stretched to run from 0 to 1. The default rate function of animations. Args: t: The time, from 0 to 1. inflection: How steep its middle is: higher starts and ends slower and runs faster between. Returns: The progress, from 0 to 1. """error=1.0/(1+np.exp(inflection/2))# the logistic curve's value at t = 0logistic=1.0/(1+np.exp(-inflection*(t-0.5)))returnmin(max((logistic-error)/(1-2*error),0),1)
@unit_intervaldefrush_into(t:float,inflection:float=10.0)->float:"""The first half of [smooth][manimgx.smooth], stretched over the whole time: starts at rest and ends at full speed. Args: t: The time, from 0 to 1. inflection: How steep [smooth][manimgx.smooth] is. Returns: The progress, from 0 to 1. """return2*smooth(t/2.0,inflection)
@unit_intervaldefrush_from(t:float,inflection:float=10.0)->float:"""The second half of [smooth][manimgx.smooth], stretched over the whole time: starts at full speed and comes to rest. Args: t: The time, from 0 to 1. inflection: How steep [smooth][manimgx.smooth] is. Returns: The progress, from 0 to 1. """return2*smooth(t/2.0+0.5,inflection)-1
@unit_intervaldefslow_into(t:float)->float:"""√(1 − (1 − t)²), a quarter circle: starts at once at full speed and slows to a stop."""returnfloat(np.sqrt(1-(1-t)*(1-t)))
@unit_intervaldefdouble_smooth(t:float)->float:"""Two [smooth][manimgx.smooth] moves, each half the way in half the time: comes to rest at the middle."""return0.5*smooth(2*t)ift<0.5else0.5*(1+smooth(2*t-1))
@zerodefthere_and_back(t:float,inflection:float=10.0)->float:"""There and back: [smooth][manimgx.smooth] to the end by the middle, then back to the start. Args: t: The time, from 0 to 1. inflection: How steep [smooth][manimgx.smooth] is. Returns: The progress, 0 at the start and the end and 1 at the middle. """returnsmooth(2*tift<0.5else2*(1-t),inflection)
@zerodefthere_and_back_with_pause(t:float,pause_ratio:float=1.0/3)->float:"""There and back with a pause: [smooth][manimgx.smooth] to the end, a pause there, then back to the start. Args: t: The time, from 0 to 1. pause_ratio: The fraction of the time spent at the end, in the middle. Returns: The progress, 0 at the start and the end and 1 during the pause. """a=2.0/(1.0-pause_ratio)ift<0.5-pause_ratio/2:returnsmooth(a*t)ift<0.5+pause_ratio/2:return1returnsmooth(a-a*t)
@unit_intervaldefrunning_start(t:float,pull_factor:float=-0.5)->float:"""Backs up for a running start, then runs to the end: with the default pull, back to −0.18 at t ≈ 0.29. Args: t: The time, from 0 to 1. pull_factor: How far it pulls back: the third and fourth of its Bézier curve's seven control values (0, 0, pull, pull, 1, 1, 1). Returns: The progress, from 0 to 1, after dipping below 0. """mt=1-treturn(15*t**2*mt**4*pull_factor+20*t**3*mt**3*pull_factor+15*t**4*mt**2+6*t**5*mt+t**6)
@unit_intervaldeflingering(t:float)->float:"""Runs evenly to the end by 0.8 of the time, then lingers there."""returnsquish_rate_func(lambdax:x,0,0.8)(t)
@zerodefwiggle(t:float,wiggles:float=2)->float:"""Swings to either side of the start and comes back: sin(`wiggles`·πt), swelling and fading as [there_and_back][manimgx.there_and_back] does. With the default 2 wiggles, it swings to 0.73 and then to −0.73. Args: t: The time, from 0 to 1. wiggles: How many swings, to one side or the other. Returns: The progress, 0 at the start and the end. """returnthere_and_back(t)*float(np.sin(wiggles*np.pi*t))
@unit_intervaldefexponential_decay(t:float,half_life:float=0.1)->float:"""1 − e^(−t / `half_life`), scaled to end at 1: shoots off, then closes in on the end, ever slower. It is about 1 − 1/e ≈ 0.63 at `half_life`. Args: t: The time, from 0 to 1. half_life: The time constant, as a fraction of the time. Returns: The progress, from 0 to 1. """returnfloat(np.expm1(-t/half_life)/np.expm1(-1/half_life))
@unit_intervaldefsmootherstep(t:float)->float:"""6t⁵ − 15t⁴ + 10t³: starts and ends at rest, and without acceleration there."""return6*t**5-15*t**4+10*t**3
@unit_intervaldefease_in_bounce(t:float)->float:"""[ease_out_bounce][manimgx.ease_out_bounce] turned around: bounces off the start, higher each time (to 0.02, 0.06 and 0.25), then rises to the end."""return1-ease_out_bounce(1-t)
@unit_intervaldefease_in_circ(t:float)->float:"""1 − √(1 − t²), a quarter circle: starts at rest and ends at full speed."""return1-math.sqrt(1-pow(t,2))
@unit_intervaldefease_in_elastic(t:float)->float:"""Swings about the start, more and more widely (down to −0.37), then springs to the end: a sine under the envelope of [ease_in_expo][manimgx.ease_in_expo]."""returnfloat(-(pow(2,10*t)-1)/1023*np.sin((t*10-10.75)*2*np.pi/3))
@unit_intervaldefease_in_out_back(t:float)->float:"""Pulls back to −0.1, then overshoots to 1.1 and settles at the end."""c1=1.70158c2=c1*1.525return(pow(2*t,2)*((c2+1)*2*t-c2)/2ift<0.5else(pow(2*t-2,2)*((c2+1)*(t*2-2)+c2)+2)/2)
@unit_intervaldefease_in_out_bounce(t:float)->float:"""[ease_in_bounce][manimgx.ease_in_bounce] up to the middle, then [ease_out_bounce][manimgx.ease_out_bounce]."""ift<0.5:return(1-ease_out_bounce(1-2*t))/2else:return(1+ease_out_bounce(2*t-1))/2
@unit_intervaldefease_in_out_circ(t:float)->float:"""[ease_in_circ][manimgx.ease_in_circ] up to the middle, then its mirror image."""return((1-math.sqrt(1-pow(2*t,2)))/2ift<0.5else(math.sqrt(1-pow(-2*t+2,2))+1)/2)
@unit_intervaldefease_in_out_cubic(t:float)->float:"""[ease_in_cubic][manimgx.ease_in_cubic] up to the middle, then its mirror image: slow, fast, slow."""return4*t*t*tift<0.5else1-pow(-2*t+2,3)/2
@unit_intervaldefease_in_out_elastic(t:float)->float:"""Swings about the start, more and more widely (down to −0.12), springs across the middle, then swings about the end, less and less widely (up to 1.12)."""u=2*tift<0.5else2-2*t# the half's own time, from its outer endhalf=-(pow(2,10*u)-1)/1023*np.sin((10*u-11.125)*2*np.pi/4.5)returnfloat(half/2ift<0.5else1-half/2)
@unit_intervaldefease_in_out_expo(t:float)->float:"""[ease_in_expo][manimgx.ease_in_expo] up to the middle, then its mirror image."""ift<0.5:return(pow(2,20*t)-1)/2046return1-(pow(2,20-20*t)-1)/2046
@unit_intervaldefease_in_out_quad(t:float)->float:"""[ease_in_quad][manimgx.ease_in_quad] up to the middle, then its mirror image: slow, fast, slow."""return2*t*tift<0.5else1-pow(-2*t+2,2)/2
@unit_intervaldefease_in_out_quart(t:float)->float:"""[ease_in_quart][manimgx.ease_in_quart] up to the middle, then its mirror image: slow, fast, slow."""return8*t*t*t*tift<0.5else1-pow(-2*t+2,4)/2
@unit_intervaldefease_in_out_quint(t:float)->float:"""[ease_in_quint][manimgx.ease_in_quint] up to the middle, then its mirror image: slow, fast, slow."""return16*t*t*t*t*tift<0.5else1-pow(-2*t+2,5)/2
@unit_intervaldefease_out_back(t:float)->float:"""Overshoots the end, to 1.1, then settles back to it."""c1=1.70158c3=c1+1return1+c3*pow(t-1,3)+c1*pow(t-1,2)
@unit_intervaldefease_out_bounce(t:float)->float:"""Falls to the end and bounces off it like a ball: reaches it at t ≈ 0.36, then bounces back to 0.75, 0.94 and 0.98."""n1=7.5625d1=2.75ift<1/d1:returnn1*t*telift<2/d1:returnn1*(t-1.5/d1)*(t-1.5/d1)+0.75elift<2.5/d1:returnn1*(t-2.25/d1)*(t-2.25/d1)+0.9375else:returnn1*(t-2.625/d1)*(t-2.625/d1)+0.984375
@unit_intervaldefease_out_circ(t:float)->float:"""√(1 − (t − 1)²), a quarter circle: starts at full speed and comes to rest."""returnmath.sqrt(1-pow(t-1,2))
@unit_intervaldefease_out_elastic(t:float)->float:"""Springs past the end (to 1.37), then swings about it, less and less widely, like a spring: [ease_in_elastic][manimgx.ease_in_elastic] turned around."""return1-ease_in_elastic(1-t)
@unit_intervaldefease_out_expo(t:float)->float:"""[ease_in_expo][manimgx.ease_in_expo] turned around: shoots off, then creeps to the end."""return1-(pow(2,10-10*t)-1)/1023
defsquish_rate_func(func:RateFunc,a:float=0.4,b:float=0.6)->RateFunc:"""Squeeze a rate function into part of the time: it runs between `a` and `b`, and holds its ends before and after. Args: func: The rate function. a: When it starts, from 0 to 1. b: When it ends, from `a` to 1; if it is `a`, the function steps from its start to its end at `a`. Returns: The squeezed rate function. """defresult(t:float)->float:ift<a:returnfunc(0.0)returnfunc(1.0)ift>=belsefunc((t-a)/(b-a))returnresult
defnot_quite_there(func:RateFunc=smooth,proportion:float=0.7)->RateFunc:"""Scale a rate function so it goes only part of the way. Args: func: The rate function. proportion: How far it goes: its progress is scaled by this. Returns: The scaled rate function. """returnlambdat:proportion*func(t)
defunit_interval[**P](function:Callable[Concatenate[float,P],float],)->Callable[Concatenate[float,P],float]:"""Make a rate function start exactly at 0 and end exactly at 1, and hold its ends outside [0, 1]. Args: function: The rate function, as it runs from 0 to 1. Returns: The rate function: 0 up to 0, 1 from 1 on. """@functools.wraps(function)defwrapper(t:float,/,*args:P.args,**kwargs:P.kwargs)->float:# its ends are exact, whatever rounding its formula does there: an animation landsif0<t<1:returnfunction(t,*args,**kwargs)return0ift<=0else1returnwrapper
defzero[**P](function:Callable[Concatenate[float,P],float],)->Callable[Concatenate[float,P],float]:"""Make a rate function that comes back to 0 start and end exactly at 0, and hold 0 outside [0, 1]. Args: function: The rate function, as it runs from 0 to 1. Returns: The rate function, 0 up to 0 and from 1 on. """@functools.wraps(function)defwrapper(t:float,/,*args:P.args,**kwargs:P.kwargs)->float:returnfunction(t,*args,**kwargs)if0<t<1else0returnwrapper