Finished cube animation for eoc/chapter3

This commit is contained in:
Grant Sanderson 2017-01-27 13:23:17 -08:00
parent 55a9819ac3
commit b097620389
7 changed files with 151 additions and 38 deletions

View file

@ -321,8 +321,8 @@ class Succession(Animation):
#require leveraging implementation details of #require leveraging implementation details of
#Animations, and knowing about the different #Animations, and knowing about the different
#struction of Transform? #struction of Transform?
if hasattr(curr_anim, "ending_mobject"): if hasattr(curr_anim, "target_mobject"):
curr_anim.mobject.align_data(curr_anim.ending_mobject) curr_anim.mobject.align_data(curr_anim.target_mobject)
curr_anim.starting_mobject = curr_anim.mobject.copy() curr_anim.starting_mobject = curr_anim.mobject.copy()
curr_anim.update(scaled_alpha - index) curr_anim.update(scaled_alpha - index)
self.last_index = index self.last_index = index

View file

@ -15,16 +15,18 @@ class Transform(Animation):
"path_arc" : 0, "path_arc" : 0,
"path_func" : None, "path_func" : None,
"submobject_mode" : "all_at_once", "submobject_mode" : "all_at_once",
"replace_mobject_with_target_in_scene" : False,
} }
def __init__(self, mobject, ending_mobject, **kwargs): def __init__(self, mobject, target_mobject, **kwargs):
#Copy ending_mobject so as to not mess with caller #Copy target_mobject so as to not mess with caller
ending_mobject = ending_mobject.copy() self.original_target_mobject = target_mobject
target_mobject = target_mobject.copy()
digest_config(self, kwargs, locals()) digest_config(self, kwargs, locals())
mobject.align_data(ending_mobject) mobject.align_data(target_mobject)
self.init_path_func() self.init_path_func()
Animation.__init__(self, mobject, **kwargs) Animation.__init__(self, mobject, **kwargs)
self.name += "To" + str(ending_mobject) self.name += "To" + str(target_mobject)
def update_config(self, **kwargs): def update_config(self, **kwargs):
Animation.update_config(self, **kwargs) Animation.update_config(self, **kwargs)
@ -42,7 +44,7 @@ class Transform(Animation):
def get_all_families_zipped(self): def get_all_families_zipped(self):
return zip(*map( return zip(*map(
Mobject.submobject_family, Mobject.submobject_family,
[self.mobject, self.starting_mobject, self.ending_mobject] [self.mobject, self.starting_mobject, self.target_mobject]
)) ))
def update_submobject(self, submob, start, end, alpha): def update_submobject(self, submob, start, end, alpha):
@ -235,13 +237,13 @@ class TransformAnimations(Transform):
if start_anim.starting_mobject.get_num_points() != end_anim.starting_mobject.get_num_points(): if start_anim.starting_mobject.get_num_points() != end_anim.starting_mobject.get_num_points():
start_anim.starting_mobject.align_data(end_anim.starting_mobject) start_anim.starting_mobject.align_data(end_anim.starting_mobject)
for anim in start_anim, end_anim: for anim in start_anim, end_anim:
if hasattr(anim, "ending_mobject"): if hasattr(anim, "target_mobject"):
anim.starting_mobject.align_data(anim.ending_mobject) anim.starting_mobject.align_data(anim.target_mobject)
Transform.__init__(self, start_anim.mobject, end_anim.mobject, **kwargs) Transform.__init__(self, start_anim.mobject, end_anim.mobject, **kwargs)
#Rewire starting and ending mobjects #Rewire starting and ending mobjects
start_anim.mobject = self.starting_mobject start_anim.mobject = self.starting_mobject
end_anim.mobject = self.ending_mobject end_anim.mobject = self.target_mobject
def update(self, alpha): def update(self, alpha):
self.start_anim.update(alpha) self.start_anim.update(alpha)

View file

@ -101,7 +101,7 @@ class Car(SVGMobject):
class MoveCar(ApplyMethod): class MoveCar(ApplyMethod):
def __init__(self, car, target_point, **kwargs): def __init__(self, car, target_point, **kwargs):
ApplyMethod.__init__(self, car.move_to, target_point, **kwargs) ApplyMethod.__init__(self, car.move_to, target_point, **kwargs)
displacement = self.ending_mobject.get_right()-self.starting_mobject.get_right() displacement = self.target_mobject.get_right()-self.starting_mobject.get_right()
distance = np.linalg.norm(displacement) distance = np.linalg.norm(displacement)
tire_radius = car.get_tires()[0].get_width()/2 tire_radius = car.get_tires()[0].get_width()/2
self.total_tire_radians = -distance/tire_radius self.total_tire_radians = -distance/tire_radius

View file

@ -621,12 +621,21 @@ class NudgeSideLengthOfCube(Scene):
"pose_angle" : np.pi/12, "pose_angle" : np.pi/12,
"pose_axis" : UP+RIGHT, "pose_axis" : UP+RIGHT,
"small_piece_scaling_factor" : 0.7, "small_piece_scaling_factor" : 0.7,
"is_recursing" : False, "allow_recursion" : True,
} }
def construct(self): def construct(self):
self.states = dict()
if self.allow_recursion:
self.alt_scene = self.__class__(
skip_animations = True,
allow_recursion = False,
dx = self.alt_dx,
)
self.add_title() self.add_title()
self.introduce_cube() self.introduce_cube()
self.write_df_equation() self.write_df_equation()
self.write_derivative()
def add_title(self): def add_title(self):
title = TexMobject("f(x) = x^3") title = TexMobject("f(x) = x^3")
@ -671,6 +680,8 @@ class NudgeSideLengthOfCube(Scene):
self.add(dv_pieces) self.add(dv_pieces)
self.play(GrowFromCenter(dx_brace)) self.play(GrowFromCenter(dx_brace))
self.dither() self.dither()
for piece in dv_pieces:
piece.on_cube_state = piece.copy()
self.play(*[ self.play(*[
ApplyMethod( ApplyMethod(
piece.shift, piece.shift,
@ -683,6 +694,7 @@ class NudgeSideLengthOfCube(Scene):
self.dither() self.dither()
self.cube = cube self.cube = cube
self.dx_brace = dx_brace
self.faces, self.bars, self.corner_cube = [ self.faces, self.bars, self.corner_cube = [
VGroup(*[ VGroup(*[
piece piece
@ -713,7 +725,7 @@ class NudgeSideLengthOfCube(Scene):
df_equation.to_edge(UP) df_equation.to_edge(UP)
faces_brace = Brace(faces, DOWN) faces_brace = Brace(faces, DOWN)
faces_brace_text = faces_brace.get_text("$3x^2", "\\, dx$") derivative = faces_brace.get_text("$3x^2", "\\, dx$")
extras_brace = Brace(VGroup(bars, corner_cube), DOWN) extras_brace = Brace(VGroup(bars, corner_cube), DOWN)
ignore_text = extras_brace.get_text( ignore_text = extras_brace.get_text(
"Multiple \\\\ of $dx^2$" "Multiple \\\\ of $dx^2$"
@ -725,19 +737,26 @@ class NudgeSideLengthOfCube(Scene):
self.play(*map(Write, [df, equals])) self.play(*map(Write, [df, equals]))
self.grab_pieces(self.faces, faces) self.grab_pieces(self.faces, faces)
self.dither() self.dither()
# self.shrink_dx() self.shrink_dx("Faces are introduced")
face = self.faces[0] face = self.faces[0]
face.save_state() face.save_state()
self.play(face.shift, SPACE_WIDTH*RIGHT) self.play(face.shift, SPACE_WIDTH*RIGHT)
x_squared_dx.move_to(self.faces[0]) x_squared_dx.next_to(face, LEFT)
self.play(Write(x_squared_dx, run_time = 1)) self.play(Write(x_squared_dx, run_time = 1))
for submob in x_squared_dx:
self.play(submob.highlight, RED, rate_func = there_and_back)
self.play(submob.highlight, RED, rate_func = there_and_back)
self.dither() self.dither()
for submob, sides in zip(x_squared_dx, [face[0], VGroup(*face[1:])]):
self.play(
submob.highlight, RED,
sides.highlight, RED,
rate_func = there_and_back
)
self.dither()
self.play( self.play(
face.restore, face.restore,
Transform(x_squared_dx, faces_brace_text), Transform(
x_squared_dx, derivative,
replace_mobject_with_target_in_scene = True
),
GrowFromCenter(faces_brace) GrowFromCenter(faces_brace)
) )
self.dither() self.dither()
@ -757,6 +776,79 @@ class NudgeSideLengthOfCube(Scene):
]) ])
self.dither() self.dither()
self.df_equation = df_equation
self.derivative = derivative
def write_derivative(self):
df, equals, faces, plus1, bars, plus2, corner_cube = self.df_equation
df = df.copy()
equals = equals.copy()
df_equals = VGroup(df, equals)
derivative = self.derivative.copy()
dx = derivative[1]
extra_stuff = TexMobject("+(\\dots)", "dx^2")
dx_squared = extra_stuff[1]
derivative.generate_target()
derivative.target.shift(2*DOWN)
extra_stuff.next_to(derivative.target)
self.play(
MoveToTarget(derivative),
df_equals.next_to, derivative.target[0], LEFT,
df_equals.shift, 0.07*DOWN
)
self.play(Write(extra_stuff))
self.dither()
frac_line = TexMobject("-")
frac_line.replace(df)
extra_stuff.generate_target()
extra_stuff.target.next_to(derivative[0])
frac_line2 = TexMobject("-")
frac_line2.stretch_to_fit_width(
extra_stuff.target[1].get_width()
)
frac_line2.move_to(extra_stuff.target[1])
extra_stuff.target[1].next_to(frac_line2, UP, buff = SMALL_BUFF)
dx_below_dx_squared = TexMobject("dx")
dx_below_dx_squared.next_to(frac_line2, DOWN, buff = SMALL_BUFF)
self.play(
FadeIn(frac_line),
FadeIn(frac_line2),
df.next_to, frac_line, UP, SMALL_BUFF,
dx.next_to, frac_line, DOWN, SMALL_BUFF,
MoveToTarget(extra_stuff),
Write(dx_below_dx_squared),
path_arc = -np.pi
)
self.dither()
inner_dx = VGroup(*dx_squared[:-1])
self.play(
FadeOut(frac_line2),
FadeOut(dx_below_dx_squared),
dx_squared[-1].highlight, BLACK,
inner_dx.next_to, extra_stuff[0], RIGHT, SMALL_BUFF
)
self.dither()
self.shrink_dx("Derivative is written", restore = False)
self.play(*[
ApplyMethod(mob.fade, 0.7)
for mob in extra_stuff, inner_dx
])
self.dither(2)
anims = []
for mob in list(self.faces)+list(self.bars)+list(self.corner_cube):
vect = mob.get_center()-self.cube.get_center()
anims += [
mob.shift, -(1./3)*vect
]
anims += self.dx_brace.shift, 0.7*DOWN
self.play(*anims)
self.dither()
def grab_pieces(self, start_pieces, end_pices, to_write = None): def grab_pieces(self, start_pieces, end_pices, to_write = None):
for piece in start_pieces: for piece in start_pieces:
piece.generate_target() piece.generate_target()
@ -774,29 +866,38 @@ class NudgeSideLengthOfCube(Scene):
*added_anims *added_anims
) )
def shrink_dx(self): def shrink_dx(self, state_name, restore = True):
self.mobjects_at_start_of_shrink_dx = self.get_mobjects() mobjects = self.get_mobjects()
if self.is_recursing: mobjects_with_points = [
m for m in mobjects
if m.get_num_points() > 0
]
#Alt_scene will reach this point, and save copy of self
#in states dict
self.states[state_name] = [
mob.copy() for mob in mobjects_with_points
]
if not self.allow_recursion:
return return
alt_scene = self.__class__( if restore:
dx = self.alt_dx, movers = self.states[state_name]
skip_animations = True, for mob in movers:
is_recursing = True mob.save_state()
) self.remove(*mobjects)
for mob in self.get_mobjects(): else:
mob.save_state() movers = mobjects_with_points
self.play(*[ self.play(*[
Transform(*pair) Transform(*pair)
for pair in zip( for pair in zip(
self.get_mobjects(), movers,
alt_scene.mobjects_at_start_of_shrink_dx self.alt_scene.states[state_name]
) )
]) ])
self.dither() self.dither()
self.play(*[ if restore:
mob.restore self.play(*[m.restore for m in movers])
for mob in self.get_mobjects() self.remove(*movers)
]) self.mobjects = mobjects
def get_cube(self): def get_cube(self):
cube = self.get_prism(self.x, self.x, self.x) cube = self.get_prism(self.x, self.x, self.x)

View file

@ -16,7 +16,7 @@ from camera import Camera
from tk_scene import TkSceneRoot from tk_scene import TkSceneRoot
from mobject import Mobject, VMobject from mobject import Mobject, VMobject
from animation import Animation from animation import Animation
from animation.transform import MoveToTarget from animation.transform import MoveToTarget, Transform
class Scene(object): class Scene(object):
CONFIG = { CONFIG = {
@ -241,6 +241,7 @@ class Scene(object):
def play(self, *args, **kwargs): def play(self, *args, **kwargs):
if len(args) == 0: if len(args) == 0:
warnings.warn("Called Scene.play with no animations") warnings.warn("Called Scene.play with no animations")
return
if self.skip_animations: if self.skip_animations:
kwargs["run_time"] = 0 kwargs["run_time"] = 0
@ -267,6 +268,10 @@ class Scene(object):
animation.clean_up() animation.clean_up()
if animation.is_remover(): if animation.is_remover():
self.remove(animation.mobject) self.remove(animation.mobject)
if isinstance(animation, Transform) :
if animation.replace_mobject_with_target_in_scene:
self.remove(animation.mobject)
self.add(animation.original_target_mobject)
return self return self
def get_mobjects_from_last_animation(self): def get_mobjects_from_last_animation(self):

View file

@ -317,7 +317,7 @@ class PiCreatureScene(Scene):
last_anim = animations[-1] last_anim = animations[-1]
if last_anim.mobject is self.pi_creature and isinstance(last_anim, Transform): if last_anim.mobject is self.pi_creature and isinstance(last_anim, Transform):
if isinstance(animations[-1], Transform): if isinstance(animations[-1], Transform):
animations[-1].ending_mobject.look_at(point_of_interest) animations[-1].target_mobject.look_at(point_of_interest)
return animations return animations
new_anim = ApplyMethod(self.pi_creature.look_at, point_of_interest) new_anim = ApplyMethod(self.pi_creature.look_at, point_of_interest)
return list(animations) + [new_anim] return list(animations) + [new_anim]

View file

@ -587,6 +587,11 @@ class QuadraticKoch(LindenmayerCurve):
} }
class QuadraticKochIsland(QuadraticKoch):
CONFIG = {
"axiom" : "A+A+A+A"
}
class StellarCurve(LindenmayerCurve): class StellarCurve(LindenmayerCurve):
CONFIG = { CONFIG = {
"start_color" : RED, "start_color" : RED,