From 37e4026e8f7c6f16e3da67c08875fb6c9d7360cf Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 7 Sep 2016 22:04:24 -0700 Subject: [PATCH] A bit more into chapter 9 --- animation/animation.py | 2 +- animation/simple_animations.py | 6 +- constants.py | 2 +- eola/chapter9.py | 515 +++++++++++++++++++++++++++++++-- eola/two_d_space.py | 4 +- scene/scene.py | 5 + 6 files changed, 511 insertions(+), 23 deletions(-) diff --git a/animation/animation.py b/animation/animation.py index 10eae528..9ca1a6ee 100644 --- a/animation/animation.py +++ b/animation/animation.py @@ -20,7 +20,7 @@ class Animation(object): "remover" : False, #Options are lagged_start, smoothed_lagged_start, #one_at_a_time, all_at_once - "submobject_mode" : "lagged_start", + "submobject_mode" : "all_at_once", "lag_factor" : 2, } def __init__(self, mobject, **kwargs): diff --git a/animation/simple_animations.py b/animation/simple_animations.py index 0740d368..d15ec97d 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -102,6 +102,9 @@ class MoveAlongPath(Animation): ) class Homotopy(Animation): + CONFIG = { + "run_time" : 3 + } def __init__(self, homotopy, mobject, **kwargs): """ Homotopy a function from (x, y, z, t) to (x', y', z') @@ -114,10 +117,11 @@ class Homotopy(Animation): def update_submobject(self, submob, start, alpha): submob.points = start.points + submob.apply_function(self.function_at_time_t(alpha)) def update_mobject(self, alpha): Animation.update_mobject(self, alpha) - submob.apply_function(self.function_at_time_t(alpha)) + class PhaseFlow(Animation): diff --git a/constants.py b/constants.py index 9205eae3..e8cc13c3 100644 --- a/constants.py +++ b/constants.py @@ -30,7 +30,7 @@ SPACE_WIDTH = SPACE_HEIGHT * DEFAULT_WIDTH / DEFAULT_HEIGHT SMALL_BUFF = 0.1 -MED_BUFF = 0.5 +MED_BUFF = 0.25 LARGE_BUFF = 1 DEFAULT_MOBJECT_TO_EDGE_BUFFER = MED_BUFF diff --git a/eola/chapter9.py b/eola/chapter9.py index 3b9fccc0..089f2479 100644 --- a/eola/chapter9.py +++ b/eola/chapter9.py @@ -19,9 +19,37 @@ from mobject.vectorized_mobject import * from eola.matrix import * from eola.two_d_space import * +from eola.chapter1 import plane_wave_homotopy V_COLOR = YELLOW +class Jennifer(PiCreature): + CONFIG = { + "color" : PINK, + "start_corner" : DOWN+LEFT, + } + +class You(PiCreature): + CONFIG = { + "color" : BLUE_E, + "start_corner" : DOWN+RIGHT, + "flip_at_start" : True, + } + +def get_small_bubble(pi_creature): + pi_center_x = pi_creature.get_center()[0] + kwargs = { + "height" : 4, + "bubble_center_adjustment_factor" : 1./6, + } + bubble = ThoughtBubble(**kwargs) + bubble.stretch_to_fit_width(3) + bubble.rotate(np.pi/4) + if pi_center_x < 0: + bubble.flip() + bubble.next_to(pi_creature, UP, buff = MED_BUFF) + bubble.to_edge(pi_center_x*RIGHT, buff = SMALL_BUFF) + return bubble class OpeningQuote(Scene): def construct(self): @@ -55,31 +83,48 @@ class LinearCombinationScene(LinearTransformationScene): "secondary_line_ratio" : 1 }, } + def setup(self): + LinearTransformationScene.setup(self) + self.i_hat.label = self.get_vector_label( + self.i_hat, "\\hat{\\imath}", "right" + ) + self.j_hat.label = self.get_vector_label( + self.j_hat, "\\hat{\\jmath}", "left" + ) + def show_linear_combination(self, numerical_coords, basis_vectors, - coord_mobs = None, + coord_mobs, show_sum_vect = False, sum_vect_color = V_COLOR, ): - for basis, scalar in zip(basis_vectors, numerical_coords): + for basis in basis_vectors: if not hasattr(basis, "label"): basis.label = VectorizedPoint() - direction = np.round(basis.get_end().rotate(np.pi/2)) + direction = np.round(rotate_vector( + basis.get_end(), np.pi/2 + )) basis.label.next_to(basis.get_center(), direction) basis.save_state() basis.label.save_state() - basis.target = basis.copy().scale(scalar) - basis.label.target = basis.label.copy() - basis.label.target.shift( - basis.target.get_center() - basis.get_center() - ) if coord_mobs is None: coord_mobs = map(TexMobject, map(str, numerical_coords)) VGroup(*coord_mobs).set_fill(opacity = 0) for coord, basis in zip(coord_mobs, basis_vectors): coord.next_to(basis.label, LEFT) - for coord, basis in zip(coord_mobs, basis_vectors): + for coord, basis, scalar in zip(coord_mobs, basis_vectors, numerical_coords): + basis.target = basis.copy().scale(scalar) + basis.label.target = basis.label.copy() coord.target = coord.copy() + new_label = VGroup(coord.target, basis.label.target) + new_label.arrange_submobjects(aligned_edge = DOWN) + new_label.move_to( + basis.label, + aligned_edge = basis.get_center()-basis.label.get_center() + ) + new_label.shift( + basis.target.get_center() - basis.get_center() + ) coord.target.next_to(basis.label.target, LEFT) coord.target.set_fill(basis.get_color(), opacity = 1) self.play(*map(MoveToTarget, [ @@ -104,8 +149,6 @@ class LinearCombinationScene(LinearTransformationScene): [FadeOut(sum_vect) for x in [1] if show_sum_vect], )) - - class RemindOfCoordinates(LinearCombinationScene): CONFIG = { "vector_coords" : [3, 2] @@ -118,7 +161,7 @@ class RemindOfCoordinates(LinearCombinationScene): self.show_standard_coord_meaning(*coords.get_entries().copy()) self.show_abstract_scalar_idea(*coords.get_entries().copy()) self.scale_basis_vectors(*coords.get_entries().copy()) - + self.list_implicit_assumptions(*coords.get_entries()) def show_standard_coord_meaning(self, x_coord, y_coord): @@ -169,12 +212,6 @@ class RemindOfCoordinates(LinearCombinationScene): self.dither() def scale_basis_vectors(self, x_coord, y_coord): - self.i_hat.label = self.get_vector_label( - self.i_hat, "\\hat{\\imath}", "right" - ) - self.j_hat.label = self.get_vector_label( - self.j_hat, "\\hat{\\jmath}", "left" - ) self.play(*map(Write, [self.i_hat.label, self.j_hat.label])) self.show_linear_combination( self.vector_coords, @@ -182,6 +219,448 @@ class RemindOfCoordinates(LinearCombinationScene): coord_mobs = [x_coord, y_coord] ) + def list_implicit_assumptions(self, x_coord, y_coord): + everything = VGroup(*self.get_mobjects()) + title = TextMobject("Implicit assumptions") + h_line = Line(title.get_left(), title.get_right()) + h_line.highlight(YELLOW) + h_line.next_to(title, DOWN) + title.add(h_line) + + ass1 = TextMobject("-First coordinate") + ass1 = VGroup(ass1, self.i_hat.copy()) + ass1.arrange_submobjects(buff = MED_BUFF) + + ass2 = TextMobject("-Second coordinate") + ass2 = VGroup(ass2, self.j_hat.copy()) + ass2.arrange_submobjects(buff = MED_BUFF) + + ass3 = TextMobject("-Unit of distance") + + group = VGroup(title, ass1, ass2, ass3) + group.arrange_submobjects(DOWN, aligned_edge = LEFT, buff = MED_BUFF) + group.to_corner(UP+LEFT) + # VGroup(*group[1:]).shift(0.5*DOWN) + for words in group: + words.add_to_back(BackgroundRectangle(words)) + + self.play(Write(title)) + self.dither() + self.play( + Write(ass1), + ApplyFunction( + lambda m : m.rotate(np.pi/6).highlight(X_COLOR), + x_coord, + rate_func = wiggle + ) + ) + self.dither() + self.play( + Write(ass2), + ApplyFunction( + lambda m : m.rotate(np.pi/6).highlight(Y_COLOR), + y_coord, + rate_func = wiggle + ) + ) + self.dither() + self.play(Write(ass3)) + self.dither(2) + keepers = VGroup(*[ + self.i_hat, self.j_hat, + self.i_hat.label, self.j_hat.label + ]) + self.play( + FadeOut(everything), + Animation(keepers.copy()), + Animation(group) + ) + self.dither() + +class NameCoordinateSystem(Scene): + def construct(self): + vector = Vector([3, 2]) + coords = Matrix([3, 2]) + arrow = TexMobject("\\Rightarrow") + vector.next_to(arrow, RIGHT, buff = 0) + coords.next_to(arrow, LEFT, buff = 2*MED_BUFF) + group = VGroup(coords, arrow, vector) + group.shift(2*UP) + coordinate_system = TextMobject("``Coordinate system''") + coordinate_system.next_to(arrow, UP, buff = LARGE_BUFF) + + i_hat, j_hat = Vector([1, 0]), Vector([0, 1]) + i_hat.highlight(X_COLOR) + j_hat.highlight(Y_COLOR) + i_label = TexMobject("\\hat{\\imath}") + i_label.highlight(X_COLOR) + i_label.next_to(i_hat, DOWN) + j_label = TexMobject("\\hat{\\jmath}") + j_label.highlight(Y_COLOR) + j_label.next_to(j_hat, LEFT) + basis_group = VGroup(i_hat, j_hat, i_label, j_label) + basis_group.shift(DOWN) + basis_words = TextMobject("``Basis vectors''") + basis_words.shift(basis_group.get_bottom()[1]*UP+MED_BUFF*DOWN) + + self.play(Write(coords)) + self.play(Write(arrow), ShowCreation(vector)) + self.dither() + self.play(Write(coordinate_system)) + self.dither(2) + self.play(Write(basis_group)) + self.play(Write(basis_words)) + self.dither() + +class JenniferScene(LinearCombinationScene): + CONFIG = { + "b1_coords" : [2, 1], + "b2_coords" : [-1, 1], + "foreground_plane_kwargs" : { + "x_radius" : SPACE_WIDTH, + "y_radius" : SPACE_WIDTH, + }, + } + def setup(self): + LinearCombinationScene.setup(self) + self.remove(self.plane, self.i_hat, self.j_hat) + self.jenny = Jennifer() + self.you = You() + self.b1 = Vector(self.b1_coords, color = X_COLOR) + self.b2 = Vector(self.b2_coords, color = Y_COLOR) + for i, vect in enumerate([self.b1, self.b2]): + vect.label = self.get_vector_label( + vect, "\\vec{\\textbf{b}}_%d"%(i+1), + direction = "right", + color = vect.get_color() + ) + transform = self.get_matrix_transformation(self.cob_matrix().T) + self.jenny_plane = self.plane.copy() + self.jenny_plane.apply_function(transform) + + def cob_matrix(self): + return np.array([self.b1_coords, self.b2_coords]).T + + def inv_cob_matrix(self): + return np.linalg.inv(self.cob_matrix()) + +class IntroduceJennifer(JenniferScene): + CONFIG = { + "v_coords" : [3, 2] + } + def construct(self): + for plane in self.plane, self.jenny_plane: + plane.fade() + self.introduce_jenny() + self.add_basis_vectors() + self.show_v_from_both_perspectives() + self.how_we_label_her_basis() + + def introduce_jenny(self): + jenny = self.jenny + name = TextMobject("Jennifer") + name.next_to(jenny, UP) + name.shift_onto_screen() + + self.add(jenny) + self.play( + jenny.change_mode, "wave_1", + jenny.look, OUT, + Write(name) + ) + self.play( + jenny.change_mode, "happy", + jenny.look, UP+RIGHT, + FadeOut(name) + ) + self.dither() + + def add_basis_vectors(self): + words = TextMobject("Alternate basis vectors") + words.shift(2.5*UP) + self.play(Write(words, run_time = 2)) + for vect in self.b1, self.b2: + self.play( + ShowCreation(vect), + Write(vect.label) + ) + self.dither() + self.play(FadeOut(words)) + self.basis_vectors = VGroup( + self.b1, self.b2, self.b1.label, self.b2.label + ) + + def show_v_from_both_perspectives(self): + v = Vector(self.v_coords) + jenny = self.jenny + you = self.you + + you.coords = Matrix([3, 2]) + jenny.coords = Matrix(["(5/3)", "(1/3)"]) + for pi in you, jenny: + pi.bubble = get_small_bubble(pi) + pi.bubble.set_fill(BLACK, opacity = 0.7) + pi.bubble.add_content(pi.coords) + jenny.coords.scale_in_place(0.7) + + new_coords = [-1, 2] + new_coords_mob = Matrix(new_coords) + new_coords_mob.scale_to_fit_height(jenny.coords.get_height()) + new_coords_mob.move_to(jenny.coords) + + for coords in you.coords, jenny.coords, new_coords_mob: + for entry in coords.get_entries(): + entry.add_background_rectangle() + + self.play(ShowCreation(v)) + self.dither() + self.play(*it.chain( + map(FadeIn, [ + self.plane, self.i_hat, self.j_hat, + self.i_hat.label, self.j_hat.label, + you + ]), + map(Animation, [jenny, v]), + map(FadeOut, self.basis_vectors), + )) + self.play( + ShowCreation(you.bubble), + Write(you.coords) + ) + self.play(you.change_mode, "speaking") + self.show_linear_combination( + self.v_coords, + basis_vectors = [self.i_hat, self.j_hat], + coord_mobs = you.coords.get_entries().copy(), + ) + self.play(*it.chain( + map(FadeOut, [ + self.plane, self.i_hat, self.j_hat, + self.i_hat.label, self.j_hat.label, + you.bubble, you.coords + ]), + map(FadeIn, [self.jenny_plane, self.basis_vectors]), + map(Animation, [v, you, jenny]), + )) + self.play( + ShowCreation(jenny.bubble), + Write(jenny.coords), + jenny.change_mode, "speaking", + ) + self.play(you.change_mode, "erm") + self.show_linear_combination( + np.dot(self.inv_cob_matrix(), self.v_coords), + basis_vectors = [self.b1, self.b2], + coord_mobs = jenny.coords.get_entries().copy(), + ) + self.play( + FadeOut(v), + jenny.change_mode, "plain" + ) + self.play( + Transform(jenny.coords, new_coords_mob), + Blink(jenny), + ) + self.hacked_show_linear_combination( + new_coords, + basis_vectors = [self.b1, self.b2], + coord_mobs = jenny.coords.get_entries().copy(), + show_sum_vect = True, + ) + + def hacked_show_linear_combination( + self, numerical_coords, + basis_vectors, + coord_mobs = None, + show_sum_vect = False, + sum_vect_color = V_COLOR, + ): + for coord, basis, scalar in zip(coord_mobs, basis_vectors, numerical_coords): + basis.save_state() + basis.label.save_state() + basis.target = basis.copy().scale(scalar) + basis.label.target = basis.label.copy() + coord.target = coord.copy() + new_label = VGroup(coord.target, basis.label.target) + new_label.arrange_submobjects(aligned_edge = DOWN) + new_label.move_to( + basis.label, + aligned_edge = basis.get_center()-basis.label.get_center() + ) + new_label.shift( + basis.target.get_center() - basis.get_center() + ) + coord.target.next_to(basis.label.target, LEFT) + coord.target.set_fill(basis.get_color(), opacity = 1) + self.play(*map(MoveToTarget, [ + coord, basis, basis.label + ])) + self.dither() + self.play(*[ + ApplyMethod(m.shift, basis_vectors[0].get_end()) + for m in self.get_mobjects_from_last_animation() + ]) + if show_sum_vect: + sum_vect = Vector( + basis_vectors[1].get_end(), + color = sum_vect_color + ) + self.play(ShowCreation(sum_vect)) + self.dither(2) + + + b1, b2 = basis_vectors + self.jenny_plane.save_state() + self.jenny.bubble.save_state() + + self.jenny.coords.target = self.jenny.coords.copy() + self.you.bubble.add_content(self.jenny.coords.target) + + x, y = numerical_coords + b1.target = self.i_hat.copy().scale(x) + b2.target = self.j_hat.copy().scale(y) + b2.target.shift(b1.target.get_end()) + new_label1 = VGroup(coord_mobs[0], b1.label) + new_label2 = VGroup(coord_mobs[1], b2.label) + new_label1.target = new_label1.copy().next_to(b1.target, DOWN) + new_label2.target = new_label2.copy().next_to(b2.target, LEFT) + i_sym = TexMobject("\\hat{\\imath}").add_background_rectangle() + j_sym = TexMobject("\\hat{\\jmath}").add_background_rectangle() + i_sym.highlight(X_COLOR).move_to(new_label1.target[1], aligned_edge = LEFT) + j_sym.highlight(Y_COLOR).move_to(new_label2.target[1], aligned_edge = LEFT) + Transform(new_label1.target[1], i_sym).update(1) + Transform(new_label2.target[1], j_sym).update(1) + sum_vect.target = Vector(numerical_coords) + self.play( + Transform(self.jenny_plane, self.plane), + Transform(self.jenny.bubble, self.you.bubble), + self.you.change_mode, "speaking", + self.jenny.change_mode, "erm", + *map(MoveToTarget, [ + self.jenny.coords, + b1, b2, new_label1, new_label2, sum_vect + ]) + ) + self.play(Blink(self.you)) + self.dither() + + self.play(*it.chain( + map(FadeOut, [ + self.jenny.bubble, self.jenny.coords, + coord_mobs, sum_vect + ]), + [ + ApplyMethod(pi.change_mode, "plain") + for pi in self.jenny, self.you + ], + [mob.restore for mob in b1, b2, b1.label, b2.label] + )) + self.jenny.bubble.restore() + + def how_we_label_her_basis(self): + you, jenny = self.you, self.jenny + b1_coords = Matrix(self.b1_coords) + b2_coords = Matrix(self.b2_coords) + for coords in b1_coords, b2_coords: + coords.add_to_back(BackgroundRectangle(coords)) + coords.scale(0.7) + coords.add_to_back(BackgroundRectangle(coords)) + you.bubble.add_content(coords) + coords.mover = coords.copy() + + self.play(jenny.change_mode, "erm") + self.play( + ShowCreation(you.bubble), + Write(b1_coords), + you.change_mode, "speaking" + ) + self.play( + b1_coords.mover.next_to, self.b1.get_end(), RIGHT, + b1_coords.mover.highlight, X_COLOR + ) + self.play(Blink(you)) + self.dither() + self.play(Transform(b1_coords, b2_coords)) + self.play( + b2_coords.mover.next_to, self.b2.get_end(), LEFT, + b2_coords.mover.highlight, Y_COLOR + ) + self.play(Blink(jenny)) + for coords, array in (b1_coords, [1, 0]), (b2_coords, [0, 1]): + mover = coords.mover + array_mob = Matrix(array) + array_mob.highlight(mover.get_color()) + array_mob.scale_to_fit_height(mover.get_height()) + array_mob.move_to(mover) + array_mob.add_to_back(BackgroundRectangle(array_mob)) + mover.target = array_mob + self.play( + self.jenny_plane.restore, + FadeOut(self.you.bubble), + FadeOut(b1_coords), + self.jenny.change_mode, "speaking", + self.you.change_mode, "confused", + *map(Animation, [ + self.basis_vectors, + b1_coords.mover, + b2_coords.mover, + ]) + ) + self.play(MoveToTarget(b1_coords.mover)) + self.play(MoveToTarget(b2_coords.mover)) + self.play(Blink(self.jenny)) + +class SpeakingDifferentLanguages(JenniferScene): + def construct(self): + jenny, you = self.jenny, self.you + vector = Vector([3, 2]) + vector.center().shift(DOWN) + you.coords = Matrix([3, 2]) + jenny.coords = Matrix(["5/3", "1/3"]) + for pi in jenny, you: + pi.bubble = pi.get_bubble("speech", width = 4) + pi.bubble.add_content(pi.coords) + self.play( + ShowCreation(vector), + you.look_at, vector, + jenny.look_at, vector, + ) + for pi in you, jenny: + self.play( + pi.change_mode, "speaking", + ShowCreation(pi.bubble), + Write(pi.coords) + ) + self.play(Blink(pi)) + self.dither() + +class ShowGrid(LinearTransformationScene): + CONFIG = { + "include_background_plane" : False, + } + def construct(self): + self.remove(self.i_hat, self.j_hat) + self.dither() + self.plane.prepare_for_nonlinear_transform() + self.play(Homotopy(plane_wave_homotopy, self.plane)) + self.play(self.plane.center) + for vect in self.i_hat, self.j_hat: + self.play(ShowCreation(vect)) + self.dither() + +class GridIsAConstruct(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + \\centering + The grid is + just a construct + """) + self.change_student_modes(*["pondering"]*3) + self.random_blink(2) + + + + diff --git a/eola/two_d_space.py b/eola/two_d_space.py index 856ca105..ca45b4c7 100644 --- a/eola/two_d_space.py +++ b/eola/two_d_space.py @@ -262,9 +262,9 @@ class LinearTransformationScene(VectorScene): "t_matrix" : [[3, 0], [1, 2]], } def setup(self): - if hasattr(self, "has_setup"): + if hasattr(self, "has_already_setup"): return - self.has_setup = True + self.has_already_setup = True ##^This is to not break all the old Scenes self.background_mobjects = [] self.foreground_mobjects = [] diff --git a/scene/scene.py b/scene/scene.py index 4612bdac..f222363a 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -215,6 +215,11 @@ class Scene(object): state["curr_method"] = arg elif state["curr_method"] is not None: state["method_args"].append(arg) + elif isinstance(arg, Mobject): + raise Exception(""" + I think you may have invoked a method + you meant to pass in as a Scene.play argument + """) else: raise Exception("Invalid play arguments") compile_method(state)