3b1b-manim/animation/indication.py

246 lines
7.1 KiB
Python
Raw Normal View History

2018-08-09 17:56:05 -07:00
import numpy as np
from constants import *
from animation.animation import Animation
from animation.movement import Homotopy
from animation.composition import AnimationGroup
from animation.composition import Succession
from animation.creation import ShowCreation
from animation.creation import ShowPartial
from animation.creation import FadeOut
from animation.transform import Transform
from mobject.mobject import Mobject
from mobject.geometry import Circle
from mobject.geometry import Dot
from mobject.shape_matchers import SurroundingRectangle
from utils.bezier import interpolate
from utils.config_ops import digest_config
from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back
from utils.rate_functions import wiggle
from functools import reduce
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 AnimationOnSurroundingRectangle(AnimationGroup):
CONFIG = {
"surrounding_rectangle_config": {},
# Function which takes in a rectangle, and spits
# out some animation. Could be some animation class,
# could be something more
"rect_to_animation": Animation
}
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs)
rect = SurroundingRectangle(
mobject, **self.surrounding_rectangle_config
)
if "surrounding_rectangle_config" in kwargs:
kwargs.pop("surrounding_rectangle_config")
AnimationGroup.__init__(self, self.rect_to_animation(rect, **kwargs))
class ShowPassingFlashAround(AnimationOnSurroundingRectangle):
CONFIG = {
"rect_to_animation": ShowPassingFlash
}
class ShowCreationThenDestructionAround(AnimationOnSurroundingRectangle):
CONFIG = {
"rect_to_animation": ShowCreationThenDestruction
}
class CircleThenFadeAround(AnimationOnSurroundingRectangle):
CONFIG = {
"rect_to_animation": lambda rect: Succession(
ShowCreation, rect,
FadeOut, rect,
)
}
class ApplyWave(Homotopy):
CONFIG = {
"direction": UP,
"amplitude": 0.2,
"run_time": 1,
}
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):
alpha = (x - left_x) / (right_x - left_x)
# lf = self.lag_factor
power = np.exp(2.0 * (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
# Number of waves
np.sin(2 * np.pi * k * x / self.spatial_period)
])
for k in range(1, self.overtones + 1)
])
def update_mobject(self, alpha):
time = alpha * self.run_time
2018-08-09 17:56:05 -07:00
families = list(map(
Mobject.get_family,
[self.mobject, self.starting_mobject]
2018-08-09 17:56:05 -07:00
))
for mob, start in zip(*families):
mob.points = np.apply_along_axis(
lambda x_y_z: (x_y_z[0], x_y_z[1] + self.wave_function(x_y_z[0], time), x_y_z[2]),
1, start.points
)
class TurnInsideOut(Transform):
CONFIG = {
"path_arc": TAU / 4,
}
def __init__(self, mobject, **kwargs):
mob_copy = mobject.copy()
mob_copy.reverse_points()
Transform.__init__(self, mobject, mob_copy, **kwargs)