3b1b-manim/animation/indication.py

188 lines
5.9 KiB
Python
Raw Normal View History

from __future__ import absolute_import
import numpy as np
from constants import *
from animation.animation import Animation
from animation.movement import Homotopy
from animation.creation import ShowPartial
from animation.transform import Transform
from mobject.mobject import Group
from mobject.mobject import Mobject
from mobject.types.vectorized_mobject import VMobject
from mobject.geometry import Circle
from mobject.geometry import Dot
from utils.config_ops import digest_config
from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back
class FocusOn(Transform):
CONFIG = {
"opacity" : 0.2,
"color" : GREY,
"run_time" : 2,
"remover" : True,
}
def __init__(self, mobject_or_point, **kwargs):
digest_config(self, kwargs)
big_dot = Dot(
radius = FRAME_X_RADIUS+FRAME_Y_RADIUS,
stroke_width = 0,
fill_color = self.color,
fill_opacity = 0,
)
little_dot = Dot(radius = 0)
little_dot.set_fill(self.color, opacity = self.opacity)
little_dot.move_to(mobject_or_point)
Transform.__init__(self, big_dot, little_dot, **kwargs)
class Indicate(Transform):
CONFIG = {
"rate_func" : there_and_back,
"scale_factor" : 1.2,
"color" : YELLOW,
}
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs)
target = mobject.copy()
target.scale_in_place(self.scale_factor)
target.set_color(self.color)
Transform.__init__(self, mobject, target, **kwargs)
class CircleIndicate(Indicate):
CONFIG = {
"rate_func" : squish_rate_func(there_and_back, 0, 0.8),
"remover" : True
}
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs)
circle = Circle(color = self.color, **kwargs)
circle.surround(mobject)
Indicate.__init__(self, circle, **kwargs)
class ShowPassingFlash(ShowPartial):
CONFIG = {
"time_width" : 0.1,
"remover" : True,
}
def get_bounds(self, alpha):
alpha *= (1+self.time_width)
alpha -= self.time_width/2.0
lower = max(0, alpha - self.time_width/2.0)
upper = min(1, alpha + self.time_width/2.0)
return (lower, upper)
def clean_up(self, *args, **kwargs):
ShowPartial.clean_up(self, *args, **kwargs)
for submob, start_submob in self.get_all_families_zipped():
submob.pointwise_become_partial(start_submob, 0, 1)
class ShowCreationThenDestruction(ShowPassingFlash):
CONFIG = {
"time_width" : 2.0,
"run_time" : 1,
}
class ApplyWave(Homotopy):
CONFIG = {
"direction" : DOWN,
"amplitude" : 0.2,
"run_time" : 1,
"apply_function_kwargs" : {
"maintain_smoothness" : False,
},
}
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs, locals())
left_x = mobject.get_left()[0]
right_x = mobject.get_right()[0]
vect = self.amplitude*self.direction
def homotopy(x, y, z, t):
start_point = np.array([x, y, z])
alpha = (x-left_x)/(right_x-left_x)
power = np.exp(2*(alpha-0.5))
nudge = there_and_back(t**power)
return np.array([x, y, z]) + nudge*vect
Homotopy.__init__(self, homotopy, mobject, **kwargs)
class WiggleOutThenIn(Animation):
CONFIG = {
"scale_value" : 1.1,
"rotation_angle" : 0.01*TAU,
"n_wiggles" : 6,
"run_time" : 2,
"scale_about_point" : None,
"rotate_about_point" : None,
}
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs)
if self.scale_about_point is None:
self.scale_about_point = mobject.get_center()
if self.rotate_about_point is None:
self.rotate_about_point = mobject.get_center()
Animation.__init__(self, mobject, **kwargs)
def update_submobject(self, submobject, starting_sumobject, alpha):
submobject.points[:,:] = starting_sumobject.points
submobject.scale(
interpolate(1, self.scale_value, there_and_back(alpha)),
about_point = self.scale_about_point
)
submobject.rotate(
wiggle(alpha, self.n_wiggles)*self.rotation_angle,
about_point = self.rotate_about_point
)
class Vibrate(Animation):
CONFIG = {
"spatial_period" : 6,
"temporal_period" : 1,
"overtones" : 4,
"amplitude" : 0.5,
"radius" : FRAME_X_RADIUS/2,
"run_time" : 3.0,
"rate_func" : None
}
def __init__(self, mobject = None, **kwargs):
if mobject is None:
mobject = Line(3*LEFT, 3*RIGHT)
Animation.__init__(self, mobject, **kwargs)
def wave_function(self, x, t):
return sum([
reduce(op.mul, [
self.amplitude/(k**2), #Amplitude
np.sin(2*np.pi*(k**1.5)*t/self.temporal_period), #Frequency
np.sin(2*np.pi*k*x/self.spatial_period) #Number of waves
])
for k in range(1, self.overtones+1)
])
def update_mobject(self, alpha):
time = alpha*self.run_time
families = map(
Mobject.submobject_family,
[self.mobject, self.starting_mobject]
)
for mob, start in zip(*families):
mob.points = np.apply_along_axis(
lambda (x, y, z) : (x, y + self.wave_function(x, time), z),
1, start.points
)
class TurnInsideOut(Transform):
CONFIG = {
"path_arc" : TAU/4,
}
def __init__(self, mobject, **kwargs):
mobject.sort_points(np.linalg.norm)
mob_copy = mobject.copy()
mob_copy.sort_points(lambda p : -np.linalg.norm(p))
Transform.__init__(self, mobject, mob_copy, **kwargs)