3b1b-manim/manimlib/animation/creation.py

297 lines
8.1 KiB
Python
Raw Normal View History

import numpy as np
from manimlib.animation.animation import Animation
from manimlib.animation.transform import Transform
from manimlib.animation.composition import Succession
from manimlib.animation.composition import LaggedStart
from manimlib.constants import *
from manimlib.mobject.types.vectorized_mobject import VMobject
from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.utils.bezier import interpolate
2019-02-09 08:59:02 -08:00
from manimlib.utils.bezier import integer_interpolate
from manimlib.utils.config_ops import digest_config
from manimlib.utils.paths import counterclockwise_path
from manimlib.utils.rate_functions import linear
2019-02-09 08:59:02 -08:00
from manimlib.utils.rate_functions import double_smooth
from manimlib.utils.rate_functions import smooth
# Drawing
class ShowPartial(Animation):
2019-02-08 15:44:58 -08:00
"""
Abstract class for ShowCreation and ShowPassingFlash
"""
def interpolate_submobject(self, submob, start_submob, alpha):
submob.pointwise_become_partial(
start_submob, *self.get_bounds(alpha)
)
def get_bounds(self, alpha):
raise Exception("Not Implemented")
class ShowCreation(ShowPartial):
CONFIG = {
2019-02-08 15:44:58 -08:00
"lag_ratio": 1,
}
def get_bounds(self, alpha):
return (0, alpha)
class Uncreate(ShowCreation):
CONFIG = {
"rate_func": lambda t: smooth(1 - t),
"remover": True
}
2019-02-09 08:59:02 -08:00
class DrawBorderThenFill(Animation):
CONFIG = {
"run_time": 2,
2019-02-09 08:59:02 -08:00
"rate_func": double_smooth,
"stroke_width": 2,
"stroke_color": None,
"draw_border_animation_config": {},
"fill_animation_config": {},
}
def __init__(self, vmobject, **kwargs):
self.check_validity_of_input(vmobject)
2019-02-09 08:59:02 -08:00
Animation.__init__(self, vmobject, **kwargs)
def check_validity_of_input(self, vmobject):
if not isinstance(vmobject, VMobject):
2019-02-09 08:59:02 -08:00
raise Exception(
"DrawBorderThenFill only works for VMobjects"
)
def begin(self):
2019-02-09 08:59:02 -08:00
self.outline = self.get_outline()
super().begin()
2019-02-09 08:59:02 -08:00
def get_outline(self):
outline = self.mobject.copy()
outline.set_fill(opacity=0)
outline.set_stroke(
color=self.get_stroke_color(outline),
width=self.stroke_width
)
2019-02-09 08:59:02 -08:00
return outline
def get_stroke_color(self, vmobject):
if self.stroke_color:
return self.stroke_color
elif vmobject.get_stroke_width() > 0:
return vmobject.get_stroke_color()
return vmobject.get_color()
2019-02-09 08:59:02 -08:00
def get_all_mobjects(self):
return [*super().get_all_mobjects(), self.outline]
2019-02-09 08:59:02 -08:00
def interpolate_submobject(self, submob, start, outline, alpha):
index, subalpha = integer_interpolate(0, 2, alpha)
if index == 0:
submob.pointwise_become_partial(
outline, 0, subalpha
)
submob.match_style(outline)
else:
submob.interpolate(outline, start, subalpha)
2019-02-09 08:59:02 -08:00
class Write(DrawBorderThenFill):
CONFIG = {
# To be figured out in
# set_default_config_from_lengths
"run_time": None,
"lag_ratio": None,
2019-02-09 08:59:02 -08:00
"rate_func": linear,
}
2019-02-09 08:59:02 -08:00
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs)
2019-02-09 08:59:02 -08:00
self.set_default_config_from_length(mobject)
DrawBorderThenFill.__init__(self, mobject, **kwargs)
2019-02-09 08:59:02 -08:00
def set_default_config_from_length(self, mobject):
length = len(mobject.family_members_with_points())
if self.run_time is None:
if length < 15:
self.run_time = 1
else:
self.run_time = 2
if self.lag_ratio is None:
self.lag_ratio = min(4.0 / length, 0.2)
2019-01-17 14:09:15 -08:00
class ShowIncreasingSubsets(Animation):
def __init__(self, group, **kwargs):
self.all_submobs = group.submobjects
Animation.__init__(self, group, **kwargs)
def interpolate_mobject(self, alpha):
2019-01-17 14:09:15 -08:00
n_submobs = len(self.all_submobs)
index = int(alpha * n_submobs)
self.mobject.submobjects = self.all_submobs[:index]
# Fading
class FadeOut(Transform):
CONFIG = {
"remover": True,
}
def __init__(self, mobject, **kwargs):
target = mobject.copy()
target.fade(1)
Transform.__init__(self, mobject, target, **kwargs)
2019-02-08 11:00:04 -08:00
def clean_up_from_scene(self, scene=None):
Transform.clean_up_from_scene(self, scene)
self.interpolate(0)
class FadeIn(Transform):
def __init__(self, mobject, **kwargs):
target = mobject.copy()
Transform.__init__(self, mobject, target, **kwargs)
self.starting_mobject.fade(1)
if isinstance(self.starting_mobject, VMobject):
self.starting_mobject.set_stroke(width=0)
self.starting_mobject.set_fill(opacity=0)
class FadeInAndShiftFromDirection(Transform):
2018-07-14 10:45:29 -07:00
CONFIG = {
"direction": DOWN,
}
def __init__(self, mobject, direction=None, **kwargs):
digest_config(self, kwargs)
target = mobject.copy()
if direction is None:
direction = self.direction
mobject.shift(direction)
mobject.fade(1)
Transform.__init__(self, mobject, target, **kwargs)
class FadeInFrom(FadeInAndShiftFromDirection):
"""
Alternate name for FadeInAndShiftFromDirection
"""
class FadeInFromDown(FadeInAndShiftFromDirection):
"""
Essential a more convenient form of FadeInAndShiftFromDirection
"""
CONFIG = {
"direction": DOWN,
}
2018-05-16 00:04:59 -07:00
2018-07-14 10:45:29 -07:00
class FadeOutAndShift(FadeOut):
CONFIG = {
"direction": DOWN,
}
def __init__(self, mobject, direction=None, **kwargs):
FadeOut.__init__(self, mobject, **kwargs)
if direction is None:
direction = self.direction
2018-07-14 10:45:29 -07:00
self.target_mobject.shift(direction)
class FadeOutAndShiftDown(FadeOutAndShift):
CONFIG = {
"direction": DOWN,
}
2018-08-25 18:30:29 -07:00
class FadeInFromLarge(Transform):
def __init__(self, mobject, scale_factor=2, **kwargs):
target = mobject.copy()
mobject.scale(scale_factor)
mobject.fade(1)
Transform.__init__(self, mobject, target, **kwargs)
2018-05-16 00:04:59 -07:00
class VFadeIn(Animation):
"""
VFadeIn and VFadeOut only work for VMobjects, but they can be applied
to mobjects while they are being animated in some other way (e.g. shifting
then) in a way that does not work with FadeIn and FadeOut
"""
def interpolate_submobject(self, submobject, starting_submobject, alpha):
2018-05-16 00:04:59 -07:00
submobject.set_stroke(
2018-08-11 23:34:58 -07:00
opacity=interpolate(0, starting_submobject.get_stroke_opacity(), alpha)
2018-05-16 00:04:59 -07:00
)
submobject.set_fill(
opacity=interpolate(0, starting_submobject.get_fill_opacity(), alpha)
)
class VFadeOut(VFadeIn):
CONFIG = {
"remover": True
}
def interpolate_submobject(self, submobject, starting_submobject, alpha):
VFadeIn.interpolate_submobject(
2018-05-16 00:04:59 -07:00
self, submobject, starting_submobject, 1 - alpha
)
# Growing
class GrowFromPoint(Transform):
CONFIG = {
"point_color": None,
}
def __init__(self, mobject, point, **kwargs):
digest_config(self, kwargs)
target = mobject.copy()
2019-01-22 15:37:36 -08:00
mobject.scale(0)
mobject.move_to(point)
if self.point_color:
2019-01-22 15:37:36 -08:00
mobject.set_color(self.point_color)
Transform.__init__(self, mobject, target, **kwargs)
class GrowFromCenter(GrowFromPoint):
def __init__(self, mobject, **kwargs):
GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs)
2018-04-12 15:11:45 -07:00
class GrowFromEdge(GrowFromPoint):
def __init__(self, mobject, edge, **kwargs):
GrowFromPoint.__init__(
self, mobject, mobject.get_critical_point(edge), **kwargs
)
class GrowArrow(GrowFromPoint):
def __init__(self, arrow, **kwargs):
GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs)
class SpinInFromNothing(GrowFromCenter):
CONFIG = {
"path_func": counterclockwise_path()
}
class ShrinkToCenter(Transform):
def __init__(self, mobject, **kwargs):
Transform.__init__(
self, mobject, mobject.get_point_mobject(), **kwargs
)