2018-03-31 15:11:35 -07:00
|
|
|
import numpy as np
|
|
|
|
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.constants import *
|
|
|
|
from manimlib.animation.animation import Animation
|
|
|
|
from manimlib.animation.movement import Homotopy
|
|
|
|
from manimlib.animation.composition import AnimationGroup
|
|
|
|
from manimlib.animation.composition import Succession
|
|
|
|
from manimlib.animation.creation import ShowCreation
|
|
|
|
from manimlib.animation.creation import ShowPartial
|
2019-02-09 09:08:57 -08:00
|
|
|
from manimlib.animation.fading import FadeOut
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.animation.transform import Transform
|
2019-02-09 12:07:33 -08:00
|
|
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.mobject.geometry import Circle
|
|
|
|
from manimlib.mobject.geometry import Dot
|
|
|
|
from manimlib.mobject.shape_matchers import SurroundingRectangle
|
2019-01-05 11:08:08 -08:00
|
|
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
|
|
|
from manimlib.mobject.geometry import Line
|
2018-12-24 12:37:51 -08:00
|
|
|
from manimlib.utils.bezier import interpolate
|
|
|
|
from manimlib.utils.config_ops import digest_config
|
|
|
|
from manimlib.utils.rate_functions import there_and_back
|
|
|
|
from manimlib.utils.rate_functions import wiggle
|
2018-03-31 15:11:35 -07:00
|
|
|
|
|
|
|
|
|
|
|
class FocusOn(Transform):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"opacity": 0.2,
|
|
|
|
"color": GREY,
|
|
|
|
"run_time": 2,
|
|
|
|
"remover": True,
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2019-02-09 12:07:33 -08:00
|
|
|
def __init__(self, focus_point, **kwargs):
|
|
|
|
self.focus_point = focus_point
|
|
|
|
# Initialize with blank mobject, while create_target
|
|
|
|
# and create_starting_mobject handle the meat
|
|
|
|
super().__init__(VMobject(), **kwargs)
|
|
|
|
|
|
|
|
def create_target(self):
|
|
|
|
little_dot = Dot(radius=0)
|
|
|
|
little_dot.set_fill(self.color, opacity=self.opacity)
|
|
|
|
little_dot.add_updater(
|
|
|
|
lambda d: d.move_to(self.focus_point)
|
|
|
|
)
|
|
|
|
return little_dot
|
|
|
|
|
|
|
|
def create_starting_mobject(self):
|
|
|
|
return Dot(
|
2018-04-06 13:58:59 -07:00
|
|
|
radius=FRAME_X_RADIUS + FRAME_Y_RADIUS,
|
|
|
|
stroke_width=0,
|
|
|
|
fill_color=self.color,
|
|
|
|
fill_opacity=0,
|
2018-03-31 15:11:35 -07:00
|
|
|
)
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class Indicate(Transform):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"rate_func": there_and_back,
|
|
|
|
"scale_factor": 1.2,
|
|
|
|
"color": YELLOW,
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2019-02-09 12:07:33 -08:00
|
|
|
def create_target(self):
|
|
|
|
target = self.mobject.copy()
|
2018-03-31 15:11:35 -07:00
|
|
|
target.scale_in_place(self.scale_factor)
|
|
|
|
target.set_color(self.color)
|
2019-02-09 12:07:33 -08:00
|
|
|
return target
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2019-01-05 11:08:08 -08:00
|
|
|
class Flash(AnimationGroup):
|
|
|
|
CONFIG = {
|
|
|
|
"line_length": 0.2,
|
|
|
|
"num_lines": 12,
|
|
|
|
"flash_radius": 0.3,
|
|
|
|
"line_stroke_width": 3,
|
2019-02-09 12:07:33 -08:00
|
|
|
"run_time": 1,
|
2019-01-05 11:08:08 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, point, color=YELLOW, **kwargs):
|
2019-02-09 12:07:33 -08:00
|
|
|
self.point = point
|
|
|
|
self.color = color
|
2019-01-05 11:08:08 -08:00
|
|
|
digest_config(self, kwargs)
|
2019-02-09 12:07:33 -08:00
|
|
|
self.lines = self.create_lines()
|
|
|
|
animations = self.create_line_anims()
|
|
|
|
super().__init__(
|
|
|
|
*animations,
|
|
|
|
group=self.lines,
|
|
|
|
**kwargs,
|
|
|
|
)
|
|
|
|
|
|
|
|
def create_lines(self):
|
2019-01-05 11:08:08 -08:00
|
|
|
lines = VGroup()
|
|
|
|
for angle in np.arange(0, TAU, TAU / self.num_lines):
|
|
|
|
line = Line(ORIGIN, self.line_length * RIGHT)
|
|
|
|
line.shift((self.flash_radius - self.line_length) * RIGHT)
|
|
|
|
line.rotate(angle, about_point=ORIGIN)
|
|
|
|
lines.add(line)
|
2020-03-07 20:53:48 -08:00
|
|
|
lines.set_stroke(
|
|
|
|
color=self.color,
|
|
|
|
width=self.line_stroke_width
|
|
|
|
)
|
2019-02-09 12:07:33 -08:00
|
|
|
lines.add_updater(lambda l: l.move_to(self.point))
|
|
|
|
return lines
|
2019-01-05 11:08:08 -08:00
|
|
|
|
2019-02-09 12:07:33 -08:00
|
|
|
def create_line_anims(self):
|
|
|
|
return [
|
|
|
|
ShowCreationThenDestruction(line)
|
|
|
|
for line in self.lines
|
|
|
|
]
|
2019-01-05 11:08:08 -08:00
|
|
|
|
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class CircleIndicate(Indicate):
|
|
|
|
CONFIG = {
|
2019-02-09 12:07:33 -08:00
|
|
|
"rate_func": there_and_back,
|
|
|
|
"remover": True,
|
|
|
|
"circle_config": {
|
|
|
|
"color": YELLOW,
|
|
|
|
},
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
def __init__(self, mobject, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
2019-02-09 12:07:33 -08:00
|
|
|
circle = self.get_circle(mobject)
|
|
|
|
super().__init__(circle, **kwargs)
|
|
|
|
|
|
|
|
def get_circle(self, mobject):
|
|
|
|
circle = Circle(**self.circle_config)
|
|
|
|
circle.add_updater(lambda c: c.surround(mobject))
|
|
|
|
return circle
|
|
|
|
|
|
|
|
def interpolate_mobject(self, alpha):
|
|
|
|
super().interpolate_mobject(alpha)
|
|
|
|
self.mobject.set_stroke(opacity=alpha)
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class ShowPassingFlash(ShowPartial):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"time_width": 0.1,
|
|
|
|
"remover": True,
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
def get_bounds(self, alpha):
|
2019-01-29 23:52:01 -08:00
|
|
|
tw = self.time_width
|
|
|
|
upper = interpolate(0, 1 + tw, alpha)
|
|
|
|
lower = upper - tw
|
|
|
|
upper = min(upper, 1)
|
|
|
|
lower = max(lower, 0)
|
2018-03-31 15:11:35 -07:00
|
|
|
return (lower, upper)
|
|
|
|
|
2019-02-09 12:07:33 -08:00
|
|
|
def finish(self):
|
|
|
|
super().finish()
|
|
|
|
for submob, start in self.get_all_families_zipped():
|
|
|
|
submob.pointwise_become_partial(start, 0, 1)
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class ShowCreationThenDestruction(ShowPassingFlash):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"time_width": 2.0,
|
|
|
|
"run_time": 1,
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2019-01-16 11:07:36 -08:00
|
|
|
class ShowCreationThenFadeOut(Succession):
|
|
|
|
CONFIG = {
|
|
|
|
"remover": True,
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, mobject, **kwargs):
|
2019-02-09 12:07:33 -08:00
|
|
|
super().__init__(
|
|
|
|
ShowCreation(mobject),
|
|
|
|
FadeOut(mobject),
|
2019-01-16 11:07:36 -08:00
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-05-07 13:33:06 -07:00
|
|
|
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
|
2019-02-09 12:07:33 -08:00
|
|
|
"rect_animation": Animation
|
2018-05-07 13:33:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, mobject, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
2018-08-29 00:08:25 -07:00
|
|
|
if "surrounding_rectangle_config" in kwargs:
|
|
|
|
kwargs.pop("surrounding_rectangle_config")
|
2019-02-09 12:07:33 -08:00
|
|
|
self.mobject_to_surround = mobject
|
|
|
|
|
|
|
|
rect = self.get_rect()
|
|
|
|
rect.add_updater(lambda r: r.move_to(mobject))
|
|
|
|
|
|
|
|
super().__init__(
|
|
|
|
self.rect_animation(rect, **kwargs),
|
|
|
|
)
|
|
|
|
|
|
|
|
def get_rect(self):
|
|
|
|
return SurroundingRectangle(
|
|
|
|
self.mobject_to_surround,
|
|
|
|
**self.surrounding_rectangle_config
|
|
|
|
)
|
2018-05-07 13:33:06 -07:00
|
|
|
|
|
|
|
|
|
|
|
class ShowPassingFlashAround(AnimationOnSurroundingRectangle):
|
|
|
|
CONFIG = {
|
2019-02-09 12:07:33 -08:00
|
|
|
"rect_animation": ShowPassingFlash
|
2018-05-07 13:33:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ShowCreationThenDestructionAround(AnimationOnSurroundingRectangle):
|
|
|
|
CONFIG = {
|
2019-02-09 12:07:33 -08:00
|
|
|
"rect_animation": ShowCreationThenDestruction
|
2018-05-07 13:33:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-15 12:20:43 -08:00
|
|
|
class ShowCreationThenFadeAround(AnimationOnSurroundingRectangle):
|
2018-05-07 13:33:06 -07:00
|
|
|
CONFIG = {
|
2019-02-09 12:07:33 -08:00
|
|
|
"rect_animation": ShowCreationThenFadeOut
|
2018-05-07 13:33:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class ApplyWave(Homotopy):
|
|
|
|
CONFIG = {
|
2018-05-07 13:33:06 -07:00
|
|
|
"direction": UP,
|
2018-04-06 13:58:59 -07:00
|
|
|
"amplitude": 0.2,
|
|
|
|
"run_time": 1,
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
def __init__(self, mobject, **kwargs):
|
|
|
|
digest_config(self, kwargs, locals())
|
|
|
|
left_x = mobject.get_left()[0]
|
|
|
|
right_x = mobject.get_right()[0]
|
2018-04-06 13:58:59 -07:00
|
|
|
vect = self.amplitude * self.direction
|
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
def homotopy(x, y, z, t):
|
2018-04-06 13:58:59 -07:00
|
|
|
alpha = (x - left_x) / (right_x - left_x)
|
2018-05-07 13:33:06 -07:00
|
|
|
power = np.exp(2.0 * (alpha - 0.5))
|
2018-03-31 15:11:35 -07:00
|
|
|
nudge = there_and_back(t**power)
|
2018-04-06 13:58:59 -07:00
|
|
|
return np.array([x, y, z]) + nudge * vect
|
2019-02-09 15:01:41 -08:00
|
|
|
|
|
|
|
super().__init__(homotopy, mobject, **kwargs)
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class WiggleOutThenIn(Animation):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"scale_value": 1.1,
|
|
|
|
"rotation_angle": 0.01 * TAU,
|
|
|
|
"n_wiggles": 6,
|
|
|
|
"run_time": 2,
|
|
|
|
"scale_about_point": None,
|
|
|
|
"rotate_about_point": None,
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2019-02-09 15:01:41 -08:00
|
|
|
def get_scale_about_point(self):
|
2018-03-31 15:11:35 -07:00
|
|
|
if self.scale_about_point is None:
|
2019-02-09 15:01:41 -08:00
|
|
|
return self.mobject.get_center()
|
|
|
|
|
|
|
|
def get_rotate_about_point(self):
|
2018-03-31 15:11:35 -07:00
|
|
|
if self.rotate_about_point is None:
|
2019-02-09 15:01:41 -08:00
|
|
|
return self.mobject.get_center()
|
2018-03-31 15:11:35 -07:00
|
|
|
|
2019-02-08 12:00:51 -08:00
|
|
|
def interpolate_submobject(self, submobject, starting_sumobject, alpha):
|
2018-04-06 13:58:59 -07:00
|
|
|
submobject.points[:, :] = starting_sumobject.points
|
2018-03-31 15:11:35 -07:00
|
|
|
submobject.scale(
|
|
|
|
interpolate(1, self.scale_value, there_and_back(alpha)),
|
2019-02-09 15:01:41 -08:00
|
|
|
about_point=self.get_scale_about_point()
|
2018-03-31 15:11:35 -07:00
|
|
|
)
|
|
|
|
submobject.rotate(
|
2018-04-06 13:58:59 -07:00
|
|
|
wiggle(alpha, self.n_wiggles) * self.rotation_angle,
|
2019-02-09 15:01:41 -08:00
|
|
|
about_point=self.get_rotate_about_point()
|
2018-03-31 15:11:35 -07:00
|
|
|
)
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2018-03-31 15:11:35 -07:00
|
|
|
class TurnInsideOut(Transform):
|
|
|
|
CONFIG = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"path_arc": TAU / 4,
|
2018-03-31 15:11:35 -07:00
|
|
|
}
|
2018-04-06 13:58:59 -07:00
|
|
|
|
2019-02-09 15:01:41 -08:00
|
|
|
def create_target(self):
|
|
|
|
return self.mobject.copy().reverse_points()
|