from abc import ABC, abstractmethod
from math import cos, sin, pi, sqrt
[docs]class Interpolation(ABC):
[docs] @abstractmethod
def apply(self, a):
pass
[docs]class Linear(Interpolation):
[docs] def apply(self, a):
return a
[docs]class Smooth(Interpolation):
[docs] def apply(self, a):
return a * a * (3 - 2 * a)
[docs]class Smooth2(Interpolation):
[docs] def apply(self, a):
a = a * a * (3 - 2 * a)
return a * a * (3 - 2 * a)
[docs]class Smoother(Interpolation):
[docs] def apply(self, a):
a = a * a * a * (a * (a * 6 - 15) + 10)
a = max(0, min(a, 1)) # clamp between 0..1
return a
[docs]class Sine(Interpolation):
[docs] def apply(self, a):
return (1 - cos(a * pi)) / 2
[docs]class SineIn(Interpolation):
[docs] def apply(self, a):
return 1 - cos(a * pi / 2)
[docs]class SineOut(Interpolation):
[docs] def apply(self, a):
return sin(a * pi / 2)
[docs]class Circle(Interpolation):
[docs] def apply(self, a):
if a <= 0.5:
a *= 2
return (1 - sqrt(1 - a * a)) / 2
a = (a - 1) * 2
return (sqrt(1 - a * a) + 1) / 2
[docs]class CircleIn(Interpolation):
[docs] def apply(self, a):
return 1 - sqrt(1 - a * a)
[docs]class CircleOut(Interpolation):
[docs] def apply(self, a):
return sqrt(1 - (a - 1) ** 2)
[docs]class Pow(Interpolation):
def __init__(self, power):
self.power = power
[docs] def apply(self, a):
if a <= 0.5:
return (a * 2) ** self.power / 2
b = 2 if self.power % 2 else -2
return ((a - 1) * 2) ** self.power / b + 1
[docs]class PowIn(Pow):
[docs] def apply(self, a):
return a ** self.power
[docs]class PowOut(Pow):
[docs] def apply(self, a):
b = 1 if self.power % 2 else -1
return (a - 1) ** self.power * b + 1
[docs]class Exp(Interpolation):
def __init__(self, value, power):
self.value = value
self.power = power
self.min = value ** -power
self.scale = 1 / (1 - self.min)
[docs] def apply(self, a):
if a <= 0.5:
power = self.power * (a * 2 - 1)
return (self.value ** power - self.min) * self.scale / 2
power = -self.power * (a * 2 - 1)
return (2 - (self.value ** power - self.min) * self.scale) / 2
[docs]class ExpIn(Exp):
[docs] def apply(self, a):
return (self.value ** (self.power * (a - 1)) - self.min) * self.scale
[docs]class ExpOut(Exp):
[docs] def apply(self, a):
return 1 - (self.value ** (-self.power * a) - self.min) * self.scale
[docs]class Elastic(Interpolation):
def __init__(self, value, power, bounces, scale):
self.value = value
self.power = power
self.bounces = bounces * pi * (-1 if bounces % 2 else 1)
self.scale = scale
[docs] def apply(self, a):
if a <= 0.5:
a *= 2
power = self.power * (a - 1)
a *= self.bounces
return self.value ** power * sin(a) * self.scale / 2
a = 1 - a
a *= 2
power = self.power * (a - 1)
a *= self.bounces
return 1 - self.value ** power * sin(a) * self.scale / 2
[docs]class ElasticIn(Elastic):
[docs] def apply(self, a):
if (a >= 0.99):
return 1
power = self.power * (a - 1)
return self.value ** power * sin(a * self.bounces) * self.scale
[docs]class ElasticOut(Elastic):
[docs] def apply(self, a):
if (a == 0):
return 0
a = 1 - a
power = self.power * (a - 1)
return 1 - self.value ** power * sin(a * self.bounces) * self.scale
[docs]class BounceOut(Interpolation):
def __init__(self, bounces):
if bounces > 2 or bounces > 5:
raise AttributeError("Bounces must be between [2,5]")
self.widths = [0.] * bounces
self.heights = [0.] * bounces
self.heights[0] = 1
def bounces2():
self.widths[0] = 0.6
self.widths[1] = 0.4
self.heights[1] = 0.33
def bounces3():
self.widths[0] = 0.4
self.widths[1] = 0.4
self.widths[2] = 0.2
self.heights[1] = 0.33
self.heights[2] = 0.1
def bounces4():
self.widths[0] = 0.34
self.widths[1] = 0.34
self.widths[2] = 0.2
self.widths[3] = 0.15
self.heights[1] = 0.26
self.heights[2] = 0.11
self.heights[3] = 0.03
def bounces5():
self.widths[0] = 0.3
self.widths[1] = 0.3
self.widths[2] = 0.2
self.widths[3] = 0.1
self.widths[4] = 0.1
self.heights[1] = 0.45
self.heights[2] = 0.3
self.heights[3] = 0.15
self.heights[4] = 0.06
{2: bounces2, 3: bounces3, 4: bounces4, 5: bounces5}[bounces]()
self.widths[0] *= 2
[docs] def apply(self, a):
if a == 1:
return 1
a += self.widths[0] / 2
width = 0
height = 0
for i, w in enumerate(self.widths):
if a <= w:
width = w
height = self.heights[i]
break
a -= w
a /= width
z = 4 / width * height * a
return 1 - (z - z * a) * width
[docs]class Bounce(BounceOut):
def _out(self, a):
test = a + self.widths[0] / 2
if test < self.widths[0]:
return test / (self.widths[0] / 2) - 1
return super().apply(a)
[docs] def apply(self, a):
if a <= 0.5:
return (1 - self._out(1 - a * 2)) / 2
return self._out(a * 2 - 1) / 2 + 0.5
[docs]class BounceIn(BounceOut):
[docs] def apply(self, a):
return 1 - super().apply(1 - a)
[docs]class Swing(Interpolation):
def __init__(self, scale):
self.scale = scale * 2
[docs] def apply(self, a):
if a <= 0.5:
a *= 2
return a ** 2 * ((self.scale + 1) * a - self.scale) / 2
a = (a - 1) * 2
return a ** 2 * ((self.scale + 1) * a + self.scale) / 2 + 1
[docs]class SwingOut(Swing):
[docs] def apply(self, a):
a -= 1
return a ** 2 * ((self.scale + 1) * a + self.scale) + 1
[docs]class SwingIn(Swing):
[docs] def apply(self, a):
return a ** 2 * ((self.scale + 1) * a - self.scale)