From c2301e282cb43d2a1e1977a6032b6896c6a549e0 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 10 Sep 2016 15:21:03 -0700 Subject: [PATCH] Finished chapter 9 --- animation/simple_animations.py | 5 +- animation/transform.py | 17 +- eola/chapter10.py | 87 ++++ eola/chapter9.py | 925 ++++++++++++++++++++++++++++++++- mobject/__init__.py | 4 +- mobject/mobject.py | 7 +- topics/characters.py | 6 + 7 files changed, 1037 insertions(+), 14 deletions(-) create mode 100644 eola/chapter10.py diff --git a/animation/simple_animations.py b/animation/simple_animations.py index d15ec97d..885dcb62 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -60,12 +60,15 @@ class Write(ShowCreation): "submobject_mode" : "lagged_start", } def __init__(self, mob_or_text, **kwargs): + digest_config(self, kwargs) if isinstance(mob_or_text, str): mobject = TextMobject(mob_or_text) else: mobject = mob_or_text - if "run_time" not in kwargs: + if not hasattr(self, "run_time"): self.establish_run_time(mobject) + if not hasattr(self, "lag_factor"): + self.lag_factor = self.run_time - 1 ShowCreation.__init__(self, mobject, **kwargs) def establish_run_time(self, mobject): diff --git a/animation/transform.py b/animation/transform.py index 53c4ea57..fa46e0f0 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -8,7 +8,7 @@ from helpers import * from animation import Animation from simple_animations import DelayByOrder -from mobject import Mobject, Point, VMobject +from mobject import Mobject, Point, VMobject, Group class Transform(Animation): CONFIG = { @@ -67,6 +67,21 @@ class MoveToTarget(Transform): raise Exception("MoveToTarget called on mobject without attribute 'target' ") Transform.__init__(self, mobject, mobject.target, **kwargs) +class CyclicReplace(Transform): + CONFIG = { + "path_arc" : np.pi/2 + } + def __init__(self, *mobjects, **kwargs): + start = Group(*mobjects) + target = Group(*[ + m1.copy().move_to(m2) + for m1, m2 in adjascent_pairs(start) + ]) + Transform.__init__(self, start, target, **kwargs) + +class Swap(CyclicReplace): + pass #Renaming, more understandable for two entries + class GrowFromCenter(Transform): def __init__(self, mobject, **kwargs): target = mobject.copy() diff --git a/eola/chapter10.py b/eola/chapter10.py new file mode 100644 index 00000000..4a87fca1 --- /dev/null +++ b/eola/chapter10.py @@ -0,0 +1,87 @@ +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import VMobject + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.number_line import * +from topics.numerals import * +from scene import Scene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * +from mobject.vectorized_mobject import * + +from eola.matrix import * +from eola.two_d_space import * + +class OpeningQuote(Scene): + def construct(self): + words = TextMobject( + "``Last time, I asked: `What does", + "mathematics", + """ mean to you?', and some people answered: `The + manipulation of numbers, the manipulation of structures.' + And if I had asked what""", + "music", + """means to you, would you have answered: `The + manipulation of notes?' '' """, + enforce_new_line_structure = False + ) + words.highlight_by_tex("mathematics", BLUE) + words.highlight_by_tex("music", BLUE) + words.scale_to_fit_width(2*SPACE_WIDTH - 2) + words.to_edge(UP) + author = TextMobject("-Serge Lang") + author.highlight(YELLOW) + author.next_to(words, DOWN, buff = 0.5) + + self.play(Write(words, run_time = 8)) + self.dither(2) + self.play(Write(author, run_time = 3)) + self.dither(2) + +class NewSceneName(Scene): + def construct(self): + pass + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eola/chapter9.py b/eola/chapter9.py index be5c72d0..c15466c3 100644 --- a/eola/chapter9.py +++ b/eola/chapter9.py @@ -49,6 +49,7 @@ def get_small_bubble(pi_creature): bubble.flip() bubble.next_to(pi_creature, UP, buff = MED_BUFF) bubble.to_edge(pi_center_x*RIGHT, buff = SMALL_BUFF) + bubble.set_fill(BLACK, opacity = 0.8) return bubble class OpeningQuote(Scene): @@ -190,6 +191,9 @@ class RemindOfCoordinates(LinearCombinationScene): for mob in to_save: mob.save_state() everything = VGroup(*self.get_mobjects()) + words = TextMobject("Think of coordinates \\\\ as", "scalars") + words.highlight_by_tex("scalars", YELLOW) + words.to_edge(UP) x, y = self.vector_coords scaled_i = self.i_hat.copy().scale(x) @@ -203,6 +207,7 @@ class RemindOfCoordinates(LinearCombinationScene): x_coord.move_to, x_shift + 3*UP, y_coord.scale_in_place, 1.5, y_coord.move_to, y_shift + 3*UP, + Write(words) ) self.play(*map(FadeIn, [self.i_hat, self.j_hat])) self.dither() @@ -210,6 +215,7 @@ class RemindOfCoordinates(LinearCombinationScene): self.play(Transform(self.j_hat, scaled_j)) self.dither() self.play( + FadeOut(words), FadeIn(everything), *[mob.restore for mob in to_save] ) @@ -253,7 +259,7 @@ class RemindOfCoordinates(LinearCombinationScene): self.play( Write(ass1), ApplyFunction( - lambda m : m.rotate(np.pi/6).highlight(X_COLOR), + lambda m : m.rotate_in_place(np.pi/6).highlight(X_COLOR), x_coord, rate_func = wiggle ) @@ -262,7 +268,7 @@ class RemindOfCoordinates(LinearCombinationScene): self.play( Write(ass2), ApplyFunction( - lambda m : m.rotate(np.pi/6).highlight(Y_COLOR), + lambda m : m.rotate_in_place(np.pi/6).highlight(Y_COLOR), y_coord, rate_func = wiggle ) @@ -316,6 +322,16 @@ class NameCoordinateSystem(Scene): self.play(Write(basis_words)) self.dither() +class WhatAboutOtherBasis(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + \\centering What if we used + different basis vectors + """) + self.random_blink() + self.change_student_modes("pondering") + self.random_blink(2) + class JenniferScene(LinearCombinationScene): CONFIG = { "b1_coords" : [2, 1], @@ -623,13 +639,28 @@ class IntroduceJennifer(JenniferScene): class SpeakingDifferentLanguages(JenniferScene): def construct(self): jenny, you = self.jenny, self.you + title = TextMobject("Different languages") + title.to_edge(UP) + vector = Vector([3, 2]) vector.center().shift(DOWN) you.coords = Matrix([3, 2]) + you.text = TextMobject("Looks to be") jenny.coords = Matrix(["5/3", "1/3"]) + jenny.text = TextMobject("Non, c'est") for pi in jenny, you: - pi.bubble = pi.get_bubble("speech", width = 4) + pi.bubble = pi.get_bubble("speech", width = 4.5, height = 3.5) + if pi is you: + pi.bubble.shift(MED_BUFF*RIGHT) + else: + pi.coords.scale(0.8) + pi.bubble.shift(MED_BUFF*LEFT) + pi.coords.next_to(pi.text, buff = MED_BUFF) + pi.coords.add(pi.text) pi.bubble.add_content(pi.coords) + + self.add(you, jenny) + self.play(Write(title)) self.play( ShowCreation(vector), you.look_at, vector, @@ -637,7 +668,7 @@ class SpeakingDifferentLanguages(JenniferScene): ) for pi in you, jenny: self.play( - pi.change_mode, "speaking", + pi.change_mode, "speaking" if pi is you else "sassy", ShowCreation(pi.bubble), Write(pi.coords) ) @@ -669,6 +700,20 @@ class GridIsAConstruct(TeacherStudentsScene): self.change_student_modes(*["pondering"]*3) self.random_blink(2) +class SpaceHasNoGrid(LinearTransformationScene): + CONFIG = { + "include_background_plane" : False + } + def construct(self): + words = TextMobject("Space has no grid") + words.to_edge(UP) + self.play( + Write(words), + FadeOut(self.plane), + *map(Animation, [self.i_hat, self.j_hat]) + ) + self.dither() + class JennysGrid(JenniferScene): def construct(self): self.add(self.jenny) @@ -866,13 +911,13 @@ class TranslateFromJenny(JenniferScene): mob.set_fill(BLACK, opacity = 0) mover_sets = [ + [jenny_x, self.b1_coords_mob], + [plus, jenny_y, self.b2_coords_mob], [self.you.coords, equals], - [self.b1_coords_mob, self.b2_coords_mob], - [jenny_x, plus, jenny_y] ] for mover_set in mover_sets: self.play(*map(MoveToTarget, mover_set)) - self.dither() + self.dither() self.play( MoveToTarget(equals2), Transform(self.b1_coords_mob.copy(), result.target), @@ -909,6 +954,872 @@ class TranslateFromJenny(JenniferScene): ) self.dither() +class WatchChapter3(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + You've all watched + chapter 3, right? + """) + self.random_blink() + self.play( + self.get_students()[0].look, LEFT, + self.get_students()[1].change_mode, "happy", + self.get_students()[2].change_mode, "happy", + ) + self.random_blink(2) + +class TalkThroughChangeOfBasisMatrix(JenniferScene): + def construct(self): + self.add(self.plane, self.jenny, self.you) + self.plane.fade() + self.jenny_plane.fade() + for pi in self.jenny, self.you: + pi.bubble = get_small_bubble(pi) + + matrix = Matrix(np.array([self.b1_coords, self.b2_coords]).T) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.next_to(ORIGIN, RIGHT, buff = MED_BUFF).to_edge(UP) + + b1_coords = Matrix(self.b1_coords) + b1_coords.highlight(X_COLOR) + b1_coords.next_to(self.b1.get_end(), RIGHT) + b2_coords = Matrix(self.b2_coords) + b2_coords.highlight(Y_COLOR) + b2_coords.next_to(self.b2.get_end(), UP) + for coords in b1_coords, b2_coords: + coords.scale_in_place(0.7) + + basis_coords_pair = VGroup( + Matrix([1, 0]).highlight(X_COLOR).scale(0.7), + TexMobject(","), + Matrix([0, 1]).highlight(Y_COLOR).scale(0.7), + ) + basis_coords_pair.arrange_submobjects(aligned_edge = DOWN) + self.you.bubble.add_content(basis_coords_pair) + + t_matrix1 = np.array([self.b1_coords, [0, 1]]) + t_matrix2 = np.dot( + self.cob_matrix(), + np.linalg.inv(t_matrix1.T) + ).T + + for mob in matrix, b1_coords, b2_coords: + mob.rect = BackgroundRectangle(mob) + mob.add_to_back(mob.rect) + + self.play(Write(matrix)) + for vect in self.i_hat, self.j_hat: + self.play( + ShowCreation(vect), + Write(vect.label) + ) + self.play( + self.you.change_mode, "pondering", + ShowCreation(self.you.bubble), + Write(basis_coords_pair) + ) + self.play(Blink(self.you)) + self.dither() + + self.add_foreground_mobject( + self.jenny, self.you, self.you.bubble, + basis_coords_pair, matrix + ) + matrix_copy = matrix.copy() + matrix_copy.rect.set_fill(opacity = 0) + self.apply_transposed_matrix( + t_matrix1, + added_anims = [ + Transform(self.i_hat, self.b1), + Transform(self.i_hat.label, self.b1.label), + Transform(matrix_copy.rect, b1_coords.rect), + Transform( + matrix_copy.get_brackets(), + b1_coords.get_brackets(), + ), + Transform( + VGroup(*matrix_copy.get_mob_matrix()[:,0]), + b1_coords.get_entries() + ), + ] + ) + self.remove(matrix_copy) + self.add_foreground_mobject(b1_coords) + matrix_copy = matrix.copy() + matrix_copy.rect.set_fill(opacity = 0) + self.apply_transposed_matrix( + t_matrix2, + added_anims = [ + Transform(self.j_hat, self.b2), + Transform(self.j_hat.label, self.b2.label), + Transform(matrix_copy.rect, b2_coords.rect), + Transform( + matrix_copy.get_brackets(), + b2_coords.get_brackets(), + ), + Transform( + VGroup(*matrix_copy.get_mob_matrix()[:,1]), + b2_coords.get_entries() + ), + ] + ) + self.remove(matrix_copy) + self.add_foreground_mobject(b2_coords) + basis_coords_pair.target = basis_coords_pair.copy() + self.jenny.bubble.add_content(basis_coords_pair.target) + self.dither() + self.play( + FadeOut(b1_coords), + FadeOut(b2_coords), + self.jenny.change_mode, "speaking", + Transform(self.you.bubble, self.jenny.bubble), + MoveToTarget(basis_coords_pair), + ) + +class ChangeOfBasisExample(JenniferScene): + CONFIG = { + "v_coords" : [-1, 2] + } + def construct(self): + self.add( + self.plane, self.i_hat, self.j_hat, + self.i_hat.label, self.j_hat.label, + ) + self.j_hat.label.next_to(self.j_hat, RIGHT) + v = self.add_vector(self.v_coords) + v_coords = Matrix(self.v_coords) + v_coords.scale(0.8) + v_coords.add_to_back(BackgroundRectangle(v_coords)) + v_coords.to_corner(UP+LEFT) + v_coords.add_background_to_entries() + for pi in self.you, self.jenny: + pi.change_mode("pondering") + pi.bubble = get_small_bubble(pi) + pi.bubble.add_content(v_coords.copy()) + pi.add(pi.bubble, pi.bubble.content) + + start_words = TextMobject("How", "we", "think of") + start_words.add_background_rectangle() + start_group = VGroup(start_words, v_coords) + start_group.arrange_submobjects(buff = MED_BUFF) + start_group.next_to(self.you, LEFT, buff = 0) + start_group.to_edge(UP) + end_words = TextMobject("How", "Jennifer", "thinks of") + end_words.add_background_rectangle() + end_words.move_to(start_words, aligned_edge = RIGHT) + + + self.play( + Write(start_group), + FadeIn(self.you), + ) + self.add_foreground_mobject(start_group, self.you) + + self.show_linear_combination( + numerical_coords = self.v_coords, + basis_vectors = [self.i_hat, self.j_hat], + coord_mobs = v_coords.get_entries().copy(), + ) + self.play(*map(FadeOut, [self.i_hat.label, self.j_hat.label])) + self.apply_transposed_matrix(self.cob_matrix().T) + VGroup(self.i_hat, self.j_hat).fade() + self.add(self.b1, self.b2) + self.play( + Transform(start_words, end_words), + Transform(self.you, self.jenny), + *map(Write, [self.b1.label, self.b2.label]) + ) + self.play(Blink(self.you)) + self.show_linear_combination( + numerical_coords = self.v_coords, + basis_vectors = [self.b1, self.b2], + coord_mobs = v_coords.get_entries().copy(), + ) + +class FeelsBackwards(Scene): + def construct(self): + matrix = Matrix(np.array([ + JenniferScene.CONFIG["b1_coords"], + JenniferScene.CONFIG["b2_coords"], + ]).T) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.shift(UP) + top_arrow = Arrow(matrix.get_left(), matrix.get_right()) + bottom_arrow = top_arrow.copy().rotate(np.pi) + top_arrow.next_to(matrix, UP, buff = LARGE_BUFF) + bottom_arrow.next_to(matrix, DOWN, buff = LARGE_BUFF) + top_arrow.highlight(BLUE) + + jenny_grid = TextMobject("Jennifer's grid").highlight(BLUE) + our_grid = TextMobject("Our grid").highlight(BLUE) + jenny_language = TextMobject("Jennifer's language") + our_language = TextMobject("Our language") + + our_grid.next_to(top_arrow, LEFT) + jenny_grid.next_to(top_arrow, RIGHT) + jenny_language.next_to(bottom_arrow, RIGHT) + our_language.next_to(bottom_arrow, LEFT) + + self.add(matrix) + self.play(Write(our_grid)) + self.play( + ShowCreation(top_arrow), + Write(jenny_grid) + ) + self.dither() + self.play(Write(jenny_language)) + self.play( + ShowCreation(bottom_arrow), + Write(our_language) + ) + self.dither() + + ##Swap things + inverse_word = TextMobject("Inverse") + inverse_word.next_to(matrix, LEFT, buff = MED_BUFF) + inverse_exponent = TexMobject("-1") + inverse_exponent.next_to(matrix.get_corner(UP+RIGHT), RIGHT) + self.play(*map(Write, [inverse_word, inverse_exponent])) + self.play( + Swap(jenny_grid, our_grid), + top_arrow.scale_in_place, 0.8, + top_arrow.shift, 0.8*RIGHT, + top_arrow.highlight, BLUE, + ) + self.play( + Swap(jenny_language, our_language), + bottom_arrow.scale_in_place, 0.8, + bottom_arrow.shift, 0.8*RIGHT + ) + self.dither() + +class AskAboutOtherWayAround(TeacherStudentsScene): + def construct(self): + self.student_says(""" + What about the + other way around? + """) + self.random_blink(3) + +class RecallInverse(JenniferScene): + def construct(self): + numerical_t_matrix = np.array([self.b1_coords, self.b2_coords]) + matrix = Matrix(numerical_t_matrix.T) + matrix.add_to_back(BackgroundRectangle(matrix)) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.to_corner(UP+LEFT, buff = 2*MED_BUFF) + # matrix.shift(MED_BUFF*DOWN) + inverse_exponent = TexMobject("-1") + inverse_exponent.next_to(matrix.get_corner(UP+RIGHT), RIGHT) + inverse_exponent.add_background_rectangle() + brace = Brace(VGroup(matrix, inverse_exponent)) + inverse_word = brace.get_text("Inverse") + inverse_word.add_background_rectangle() + + equals = TexMobject("=") + equals.add_background_rectangle() + inv_matrix = Matrix([ + ["1/3", "1/3"], + ["-1/3", "2/3"] + ]) + inv_matrix.scale_to_fit_height(matrix.get_height()) + inv_matrix.add_to_back(BackgroundRectangle(inv_matrix)) + equals.next_to(matrix, RIGHT, buff = 0.7) + inv_matrix.next_to(equals, RIGHT, buff = MED_BUFF) + + self.add_foreground_mobject(matrix) + self.apply_transposed_matrix(numerical_t_matrix) + self.play( + GrowFromCenter(brace), + Write(inverse_word), + Write(inverse_exponent) + ) + self.add_foreground_mobject(*self.get_mobjects_from_last_animation()) + self.dither() + self.apply_inverse_transpose(numerical_t_matrix) + self.dither() + self.play( + Write(equals), + Transform(matrix.copy(), inv_matrix) + ) + self.remove(*self.get_mobjects_from_last_animation()) + self.add_foreground_mobject(equals, inv_matrix) + self.dither() + for mob in self.plane, self.i_hat, self.j_hat: + self.add(mob.copy().fade(0.7)) + self.apply_transposed_matrix(numerical_t_matrix) + self.play(FadeIn(self.jenny)) + self.play(self.jenny.change_mode, "speaking") + #Little hacky now + inv_matrix.highlight_columns(X_COLOR) + self.play(*[ + ApplyMethod( + mob.scale_in_place, 1.2, + rate_func = there_and_back + ) + for mob in inv_matrix.get_mob_matrix()[:,0] + ]) + self.dither() + inv_matrix.highlight_columns(X_COLOR, Y_COLOR) + self.play(*[ + ApplyMethod( + mob.scale_in_place, 1.2, + rate_func = there_and_back + ) + for mob in inv_matrix.get_mob_matrix()[:,1] + ]) + self.dither() + +class WorkOutInverseComputation(Scene): + def construct(self): + our_vector = Matrix([3, 2]) + her_vector = Matrix(["5/3", "1/3"]) + matrix = Matrix([["1/3", "1/3"], ["-1/3", "2/3"]]) + our_vector.highlight(BLUE_D) + her_vector.highlight(MAROON_B) + equals = TexMobject("=") + equation = VGroup( + matrix, our_vector, equals, her_vector + ) + for mob in equation: + if isinstance(mob, Matrix): + mob.scale_to_fit_height(2) + equation.arrange_submobjects() + + matrix_brace = Brace(matrix, UP) + our_vector_brace = Brace(our_vector) + her_vector_brace = Brace(her_vector, UP) + matrix_text = matrix_brace.get_text(""" + \\centering + Inverse + change of basis + matrix + """) + our_text = our_vector_brace.get_text(""" + \\centering + Written in + our language + """) + our_text.highlight(our_vector.get_color()) + her_text = her_vector_brace.get_text(""" + \\centering + Same vector + in her language + """) + her_text.highlight(her_vector.get_color()) + for text in our_text, her_text: + text.scale_in_place(0.7) + + self.add(our_vector) + self.play( + GrowFromCenter(our_vector_brace), + Write(our_text) + ) + self.dither() + self.play( + FadeIn(matrix), + GrowFromCenter(matrix_brace), + Write(matrix_text) + ) + self.dither() + self.play( + Write(equals), + Write(her_vector) + ) + self.play( + GrowFromCenter(her_vector_brace), + Write(her_text) + ) + self.dither() + +class SoThatsTranslation(TeacherStudentsScene): + def construct(self): + self.teacher_says("So that's translation") + self.random_blink(3) + +class SummarizeTranslationProcess(Scene): + def construct(self): + self.define_matrix() + self.show_translation() + + def define_matrix(self): + matrix = Matrix([[2, -1], [1, 1]]) + matrix.highlight_columns(X_COLOR, Y_COLOR) + A, equals = map(TexMobject, list("A=")) + equation = VGroup(A, equals, matrix) + equation.arrange_submobjects() + equation.to_corner(UP+LEFT) + equation.shift(RIGHT) + words = TextMobject(""" + Jennifer's basis vectors, + written in our coordinates + """) + words.to_edge(LEFT) + mob_matrix = matrix.get_mob_matrix() + arrow1 = Arrow(words, mob_matrix[1, 0], color = X_COLOR) + arrow2 = Arrow(words, mob_matrix[1, 1], color = Y_COLOR) + + self.add(A, equals, matrix) + self.play( + Write(words), + *map(ShowCreation, [arrow1, arrow2]) + ) + self.A_copy = A.copy() + + def show_translation(self): + our_vector = Matrix(["x_o", "y_o"]) + her_vector = Matrix(["x_j", "y_j"]) + for vector, color in (our_vector, BLUE_D), (her_vector, MAROON_B): + # vector.scale_to_fit_height(1.5) + vector.highlight(color) + A = TexMobject("A") + A_inv = TexMobject("A^{-1}") + equals = TexMobject("=") + + equation = VGroup(A, her_vector, equals, our_vector) + equation.arrange_submobjects() + equation.to_edge(RIGHT) + equation.shift(0.5*UP) + A_inv.next_to(our_vector, LEFT) + + her_words = TextMobject("Vector in her coordinates") + her_words.highlight(her_vector.get_color()) + her_words.scale(0.8).to_corner(UP+RIGHT) + her_arrow = Arrow( + her_words, her_vector, + color = her_vector.get_color() + ) + our_words = TextMobject("Same vector in\\\\ our coordinates") + our_words.highlight(our_vector.get_color()) + our_words.scale(0.8).to_edge(RIGHT).shift(2*DOWN) + our_words.shift_onto_screen() + our_arrow = Arrow( + our_words.get_top(), our_vector.get_bottom(), + color = our_vector.get_color() + ) + + self.play( + Write(equation), + Transform(self.A_copy, A) + ) + self.remove(self.A_copy) + self.play( + Write(her_words), + ShowCreation(her_arrow) + ) + self.play( + Write(our_words), + ShowCreation(our_arrow) + ) + self.dither(2) + self.play( + VGroup(her_vector, equals).next_to, A_inv, LEFT, + her_arrow.rotate_in_place, -np.pi/6, + her_arrow.shift, MED_BUFF*LEFT, + Transform(A, A_inv, path_arc = np.pi) + ) + self.dither() + +class VectorsAreNotTheOnlyOnes(TeacherStudentsScene): + def construct(self): + self.teacher_says(""" + \\centering + Vectors aren't the + only thing with coordinates + """) + self.change_student_modes("pondering", "confused", "erm") + self.random_blink(3) + +class Prerequisites(Scene): + def construct(self): + title = TextMobject("Prerequisites") + title.to_edge(UP) + h_line = Line(LEFT, RIGHT).scale(SPACE_WIDTH) + h_line.next_to(title, DOWN) + + self.add(title, h_line) + prereqs = map(TextMobject, [ + "Linear transformations", + "Matrix multiplication", + ]) + for direction, words in zip([LEFT, RIGHT], prereqs): + rect = Rectangle(height = 9, width = 16) + rect.scale_to_fit_height(3.5) + rect.next_to(ORIGIN, direction, buff = MED_BUFF) + rect.highlight(BLUE) + words.next_to(rect, UP, buff = MED_BUFF) + self.play( + Write(words), + ShowCreation(rect) + ) + self.dither() + +class RotationExample(LinearTransformationScene): + CONFIG = { + "t_matrix" : [[0, 1], [-1, 0]] + } + def construct(self): + words = TextMobject("$90^\\circ$ rotation") + words.scale(1.2) + words.add_background_rectangle() + words.to_edge(UP) + + matrix = Matrix(self.t_matrix.T) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.rect = BackgroundRectangle(matrix) + matrix.add_to_back(matrix.rect) + matrix.next_to(words, DOWN) + matrix.shift(2*RIGHT) + + self.play(Write(words)) + self.add_foreground_mobject(words) + self.dither() + self.apply_transposed_matrix(self.t_matrix) + self.dither() + self.play( + self.i_hat.rotate, np.pi/12, + self.j_hat.rotate, -np.pi/12, + rate_func = wiggle, + run_time = 2 + ) + self.dither() + + i_coords, j_coords = coord_arrays = map(Matrix, self.t_matrix) + for coords, vect in zip(coord_arrays, [self.i_hat, self.j_hat]): + coords.scale(0.7) + coords.rect = BackgroundRectangle(coords) + coords.add_to_back(coords.rect) + coords.highlight(vect.get_color()) + direction = UP if vect is self.j_hat else RIGHT + coords.next_to(vect.get_end(), direction, buff = MED_BUFF) + self.play(Write(coords)) + self.dither() + + self.play( + Transform(i_coords.rect, matrix.rect), + Transform(i_coords.get_brackets(), matrix.get_brackets()), + Transform( + i_coords.get_entries(), + VGroup(*matrix.get_mob_matrix()[:, 0]) + ), + ) + self.play( + FadeOut(j_coords.rect), + FadeOut(j_coords.get_brackets()), + Transform( + j_coords.get_entries(), + VGroup(*matrix.get_mob_matrix()[:, 1]) + ), + ) + self.dither() + self.add_words(matrix) + + def add_words(self, matrix): + follow_basis = TextMobject( + "Follow", "our choice", + "\\\\ of basis vectors" + ) + follow_basis.highlight_by_tex("our choice", YELLOW) + follow_basis.add_background_rectangle() + follow_basis.next_to( + matrix, LEFT, + buff = MED_BUFF, + ) + + record = TextMobject( + "Record using \\\\", + "our coordinates" + ) + record.highlight_by_tex("our coordinates", YELLOW) + record.add_background_rectangle() + record.next_to( + matrix, DOWN, + buff = MED_BUFF, + aligned_edge = LEFT + ) + + self.play(Write(follow_basis)) + self.dither() + self.play(Write(record)) + self.dither() + +class JennyWatchesRotation(JenniferScene): + def construct(self): + jenny = self.jenny + self.add(self.jenny_plane.copy().fade()) + self.add(self.jenny_plane) + self.add(jenny) + for vect in self.b1, self.b2: + self.add_vector(vect) + + matrix = Matrix([["?", "?"], ["?", "?"]]) + matrix.get_entries().gradient_highlight(X_COLOR, Y_COLOR) + jenny.bubble = get_small_bubble(jenny) + jenny.bubble.add_content(matrix) + matrix.scale_in_place(0.8) + + self.play( + jenny.change_mode, "sassy", + ShowCreation(jenny.bubble), + Write(matrix) + ) + self.play(*it.chain( + [ + Rotate(mob, np.pi/2, run_time = 3) + for mob in self.jenny_plane, self.b1, self.b2 + ], + map(Animation, [jenny, jenny.bubble, matrix]) + )) + self.play(jenny.change_mode, "pondering") + self.play(Blink(jenny)) + self.dither() + +class AksAboutTranslatingColumns(TeacherStudentsScene): + def construct(self): + matrix = Matrix([[0, -1], [1, 0]]) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.scale(0.7) + words = TextMobject("Translate columns of") + matrix.next_to(words, DOWN) + words.add(matrix) + self.student_says(words, student_index = 0) + self.random_blink(2) + + student = self.get_students()[0] + bubble = get_small_bubble(student) + bubble.set_fill(opacity = 0) + matrix.target = matrix.copy() + bubble.add_content(matrix.target) + self.play( + Transform(student.bubble, bubble), + FadeOut(student.bubble.content), + MoveToTarget(matrix.copy()), + student.change_mode, "pondering", + ) + self.remove(student.bubble) + student.bubble = None + self.add(bubble, matrix.target) + + self.random_blink() + words = TextMobject( + "\\centering Those columns still \\\\ represent ", + "our basis", ", not ", "hers", + arg_separator = "" + ) + words.highlight_by_tex("our basis", BLUE) + words.highlight_by_tex("hers", MAROON_B) + self.teacher_says(words) + self.change_student_modes("erm", "pondering", "pondering") + self.random_blink() + +class HowToTranslateAMatrix(Scene): + def construct(self): + self.add_title() + + arrays = VGroup(*map(Matrix, [ + [["1/3", "-2/3"], ["5/3", "-1/3"]], + [-1, 2], + [[2, -1], [1, 1]], + [[0, -1], [1, 0]], + [[2, -1], [1, 1]], + ])) + result, her_vector, cob_matrix, transform, inv_cob = arrays + neg_1 = TexMobject("-1") + neg_1.next_to(inv_cob.get_corner(UP+RIGHT), RIGHT) + inv_cob.add(neg_1) + arrays.arrange_submobjects(LEFT) + arrays.to_edge(LEFT, buff = LARGE_BUFF/2.) + for array in arrays: + array.brace = Brace(array) + array.top_brace = Brace(VGroup(array, her_vector), UP) + for array in cob_matrix, inv_cob: + submobs = array.split() + submobs.sort(lambda m1, m2: cmp(m1.get_center()[0], m2.get_center()[0])) + array.submobjects = submobs + her_vector.highlight(MAROON_B) + cob_matrix.gradient_highlight(BLUE, MAROON_B) + transform.highlight_columns(X_COLOR, Y_COLOR) + transform.get_brackets().highlight(BLUE) + inv_cob.gradient_highlight(MAROON_B, BLUE) + result.highlight_columns(X_COLOR, Y_COLOR) + result.get_brackets().highlight(MAROON_B) + + final_top_brace = Brace(VGroup(cob_matrix, inv_cob), UP) + + brace_text_pairs = [ + (her_vector.brace, ("Vector in \\\\", "Jennifer's language")), + (her_vector.top_brace, ("",)), + (cob_matrix.brace, ("Change of basis \\\\", "matrix")), + (cob_matrix.top_brace, ("Same vector \\\\", "in", "our", "language")), + (transform.brace, ("Transformation matrix \\\\", "in", "our", "language")), + (transform.top_brace, ("Transformed vector \\\\", "in", "our", "language")), + (inv_cob.brace, ("Inverse \\\\", "change of basis \\\\", "matrix")), + (inv_cob.top_brace, ("Transformed vector \\\\", "in", "her", "language")), + (final_top_brace, ("Transformation matrix \\\\", "in", "her", "language")) + ] + for brace, text_args in brace_text_pairs: + text_args = list(text_args) + text_args[0] = "\\centering " + text_args[0] + text = TextMobject(*text_args) + text.highlight_by_tex("our", BLUE) + text.highlight_by_tex("her", MAROON_B) + brace.put_at_tip(text) + brace.text = text + + brace = her_vector.brace + bottom_words = her_vector.brace.text + top_brace = cob_matrix.top_brace + top_words = cob_matrix.top_brace.text + def introduce(array): + self.play( + Write(array), + Transform(brace, array.brace), + Transform(bottom_words, array.brace.text) + ) + self.dither() + def echo_introduce(array): + self.play( + Transform(top_brace, array.top_brace), + Transform(top_words, array.top_brace.text) + ) + self.dither() + + self.play(Write(her_vector)) + self.play( + GrowFromCenter(brace), + Write(bottom_words) + ) + self.dither() + introduce(cob_matrix) + self.play( + GrowFromCenter(top_brace), + Write(top_words) + ) + self.dither() + introduce(transform) + echo_introduce(transform) + introduce(inv_cob), + echo_introduce(inv_cob) + + #Genearlize to single matrix + v = TexMobject("\\vec{\\textbf{v}}") + v.highlight(her_vector.get_color()) + v.move_to(her_vector, aligned_edge = LEFT) + self.play( + Transform(her_vector, v), + FadeOut(bottom_words), + FadeOut(brace), + ) + self.dither() + self.play( + Transform(top_brace, final_top_brace), + Transform(top_brace.text, final_top_brace.text), + ) + self.dither() + + equals = TexMobject("=") + equals.replace(v) + result.next_to(equals, RIGHT) + self.play( + Transform(her_vector, equals), + Write(result) + ) + self.dither(2) + + everything = VGroup(*self.get_mobjects()) + self.play( + FadeOut(everything), + result.to_corner, UP+LEFT + ) + self.add(result) + self.dither() + + + def add_title(self): + title = TextMobject("How to translate a matrix") + title.to_edge(UP) + h_line = Line(LEFT, RIGHT).scale(SPACE_WIDTH) + h_line.next_to(title, DOWN) + self.add(title) + self.play(ShowCreation(h_line)) + self.dither() + +class JennyWatchesRotationWithMatrixAndVector(JenniferScene): + def construct(self): + self.add(self.jenny_plane.copy().fade(0.8)) + self.add(self.jenny_plane, self.jenny, self.b1, self.b2) + + matrix = Matrix([["1/3", "-2/3"], ["5/3", "-1/3"]]) + matrix.highlight_columns(X_COLOR, Y_COLOR) + matrix.to_corner(UP+LEFT) + + vector_coords = [1, 2] + vector_array = Matrix(vector_coords) + vector_array.highlight(YELLOW) + vector_array.next_to(matrix, RIGHT) + + result = Matrix([-1, 1]) + equals = TexMobject("=") + equals.next_to(vector_array) + result.next_to(equals) + + for array in matrix, vector_array, result: + array.add_to_back(BackgroundRectangle(array)) + + vector = Vector(np.dot(self.cob_matrix(), vector_coords)) + + self.add(matrix) + self.play(Write(vector_array)) + self.play(ShowCreation(vector)) + self.play(Blink(self.jenny)) + self.play(*it.chain( + [ + Rotate(mob, np.pi/2, run_time = 3) + for mob in self.jenny_plane, self.b1, self.b2, vector + ], + map(Animation, [self.jenny, matrix, vector_array]), + )) + self.play( + self.jenny.change_mode, "pondering", + Write(equals), + Write(result) + ) + self.play(Blink(self.jenny)) + self.dither() + +class MathematicalEmpathy(TeacherStudentsScene): + def construct(self): + words = TextMobject( + "\\centering An expression like", + "$A^{-1} M A$", + "\\\\ suggests a mathematical \\\\", + "sort of empathy" + ) + A1, neg, one, M, A2 = words[1] + As = VGroup(A1, neg, one, A2) + VGroup(As, M).highlight(YELLOW) + + self.teacher_says(words) + self.random_blink() + for mob, color in (M, BLUE), (As, MAROON_B): + self.play(mob.highlight, color) + self.play(mob.scale_in_place, 1.2, rate_func = there_and_back) + self.random_blink(2) + +class NextVideo(Scene): + def construct(self): + title = TextMobject(""" + Next video: Eigenvectors and eigenvalues + """) + title.to_edge(UP, buff = MED_BUFF) + rect = Rectangle(width = 16, height = 9, color = BLUE) + rect.scale_to_fit_height(6) + rect.next_to(title, DOWN) + + self.add(title) + self.play(ShowCreation(rect)) + self.dither() + diff --git a/mobject/__init__.py b/mobject/__init__.py index 3ace2364..4001a4d5 100644 --- a/mobject/__init__.py +++ b/mobject/__init__.py @@ -4,6 +4,6 @@ __all__ = [ "tex_mobject", ] -from mobject import Mobject +from mobject import Mobject, Group from point_cloud_mobject import Point, Mobject1D, Mobject2D, PMobject -from vectorized_mobject import VMobject \ No newline at end of file +from vectorized_mobject import VMobject, VGroup \ No newline at end of file diff --git a/mobject/mobject.py b/mobject/mobject.py index ad8a6f94..21248548 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -612,7 +612,7 @@ class Mobject(object): self.interpolate_color(mobject1, mobject2, alpha) def interpolate_color(self, mobject1, mobject2, alpha): - raise Exception("Not implemented") + pass #To implement in subclass def become_partial(self, mobject, a, b): """ @@ -621,11 +621,12 @@ class Mobject(object): Inputs 0 <= a < b <= 1 determine what portion of mobject to become. """ - + pass #To implement in subclasses + #TODO, color? def pointwise_become_partial(self, mobject, a, b): - raise Exception("Not implemented") + pass #To implement in subclass diff --git a/topics/characters.py b/topics/characters.py index 17c52f82..ffab2012 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -416,6 +416,12 @@ class TeacherStudentsScene(Scene): ) def student_says(self, *content, **kwargs): + if "pi_creature_target_mode" not in kwargs: + target_mode = random.choice([ + "raise_right_hand", + "raise_left_hand", + ]) + kwargs["pi_creature_target_mode"] = target_mode student = self.get_students()[kwargs.get("student_index", 1)] return self.introduce_bubble(content, "speech", student, **kwargs)