diff --git a/eola/chapter4.py b/eola/chapter4.py index 29c2b8b1..e9a14e7b 100644 --- a/eola/chapter4.py +++ b/eola/chapter4.py @@ -263,7 +263,8 @@ class IntroduceIdeaOfComposition(RotationThenShear): def construct(self): self.setup() self.show_composition() - self.track_basis_vectors() + matrix = self.track_basis_vectors() + self.show_overall_effect(matrix) def show_composition(self): words = TextMobject([ @@ -281,12 +282,730 @@ class IntroduceIdeaOfComposition(RotationThenShear): self.apply_transposed_matrix([[0, 1], [-1, 0]], run_time = 2) self.apply_transposed_matrix([[1, 0], [1, 1]], run_time = 2) - self.play(Write(words)) + self.play( + ApplyMethod(self.plane.fade), + Write(words), + Animation(self.i_hat), + Animation(self.j_hat), + ) self.dither() def track_basis_vectors(self): - pass - + last_words = self.get_mobjects_from_last_animation()[1] + words = TextMobject([ + "Record where", + "$\\hat{\\imath}$", + "and", + "$\\hat{\\jmath}$", + "land:" + ]) + rw, i_hat, a, j_hat, l = words.split() + i_hat.highlight(X_COLOR) + j_hat.highlight(Y_COLOR) + words.add_background_rectangle() + words.next_to(last_words, DOWN) + + i_coords = vector_coordinate_label(self.i_hat) + j_coords = vector_coordinate_label(self.j_hat) + i_coords.highlight(X_COLOR) + j_coords.highlight(Y_COLOR) + i_background = BackgroundRectangle(i_coords) + j_background = BackgroundRectangle(j_coords) + + matrix = Matrix(np.append( + i_coords.copy().get_mob_matrix(), + j_coords.copy().get_mob_matrix(), + axis = 1 + )) + matrix.next_to(words, RIGHT, aligned_edge = UP) + col1, col2 = [ + VMobject(*matrix.get_mob_matrix()[:,i]) + for i in 0, 1 + ] + matrix_background = BackgroundRectangle(matrix) + + self.play(Write(words)) + self.dither() + self.play(ShowCreation(i_background), Write(i_coords), run_time = 2) + self.dither() + self.play( + Transform(i_background.copy(), matrix_background), + Transform(i_coords.copy().get_brackets(), matrix.get_brackets()), + ApplyMethod(i_coords.copy().get_entries().move_to, col1) + ) + self.dither() + self.play(ShowCreation(j_background), Write(j_coords), run_time = 2) + self.dither() + self.play( + ApplyMethod(j_coords.copy().get_entries().move_to, col2) + ) + self.dither() + matrix = VMobject(matrix_background, matrix) + return matrix + + def show_overall_effect(self, matrix): + everything = self.get_mobjects() + everything = list_difference_update( + everything, matrix.submobject_family() + ) + self.play(*map(FadeOut, everything) + [Animation(matrix)]) + new_matrix = matrix.copy() + new_matrix.center().to_edge(UP) + self.play(Transform(matrix, new_matrix)) + self.dither() + self.remove(matrix) + + self.setup() + everything = self.get_mobjects() + self.play(*map(FadeIn, everything) + [Animation(matrix)]) + func = self.get_matrix_transformation([[1, 1], [-1, 0]]) + bases = VMobject(self.i_hat, self.j_hat) + new_bases = VMobject(*[ + Vector(func(v.get_end()), color = v.get_color()) + for v in bases.split() + ]) + self.play( + ApplyPointwiseFunction(func, self.plane), + Transform(bases, new_bases), + Animation(matrix), + run_time = 3 + ) + self.dither() + +class PumpVectorThroughRotationThenShear(RotationThenShear): + def construct(self): + self.setup() + self.add_vector([2, 3]) + self.apply_transposed_matrix([[0, 1], [-1, 0]], run_time = 2) + self.apply_transposed_matrix([[1, 0], [1, 1]], run_time = 2) + self.dither() + +class ExplainWhyItsMatrixMultiplication(Scene): + def construct(self): + vect = Matrix(["x", "y"]) + vect.get_entries().highlight(YELLOW) + + rot_matrix = Matrix([[0, -1], [1, 0]]) + rot_matrix.highlight(TEAL) + shear_matrix = Matrix([[1, 1], [0, 1]]) + shear_matrix.highlight(PINK) + l_paren, r_paren = map(TexMobject, ["\\Big(", "\\Big)"]) + for p in l_paren, r_paren: + p.scale_to_fit_height(1.4*vect.get_height()) + long_way = VMobject( + shear_matrix, l_paren, rot_matrix, vect, r_paren + ) + long_way.arrange_submobjects(buff = 0.1) + long_way.to_edge(LEFT).shift(UP) + + equals = TexMobject("=").next_to(long_way, RIGHT) + + comp_matrix = Matrix([[1, -1], [1, 0]]) + comp_matrix.highlight_columns(X_COLOR, Y_COLOR) + vect_copy = vect.copy() + short_way = VMobject(comp_matrix, vect_copy) + short_way.arrange_submobjects(buff = 0.1) + short_way.next_to(equals, RIGHT) + + pairs = [ + (rot_matrix, "Rotation"), + (shear_matrix, "Shear"), + (comp_matrix, "Composition"), + ] + for matrix, word in pairs: + brace = Brace(matrix) + text = TextMobject(word).next_to(brace, DOWN) + brace.highlight(matrix.get_color()) + text.highlight(matrix.get_color()) + matrix.add(brace, text) + comp_matrix.split()[-1].submobject_gradient_highlight(TEAL, PINK) + + self.add(vect) + groups = [ + [rot_matrix], + [l_paren, r_paren, shear_matrix], + [equals, comp_matrix, vect_copy], + ] + for group in groups: + self.play(*map(Write, group)) + self.dither() + self.play(*map(FadeOut, [l_paren, r_paren, vect, vect_copy])) + comp_matrix.add(equals) + matrices = VMobject(shear_matrix, rot_matrix, comp_matrix) + self.play(ApplyMethod( + matrices.arrange_submobjects, buff = 0.1, + aligned_edge = UP + )) + self.dither() + + arrow = Arrow(rot_matrix.get_right(), shear_matrix.get_left()) + arrow.shift((rot_matrix.get_top()[1]+0.2)*UP) + words = TextMobject("Read right to left") + words.submobjects.reverse() + words.next_to(arrow, UP) + functions = TexMobject("f(g(x))") + functions.next_to(words, UP) + + self.play(ShowCreation(arrow)) + self.play(Write(words)) + self.dither() + self.play(Write(functions)) + self.dither() + +class MoreComplicatedExampleVisually(LinearTransformationScene): + CONFIG = { + "t_matrix1" : [[1, 1], [-2, 0]], + "t_matrix2" : [[0, 1], [2, 0]], + } + def construct(self): + self.setup() + t_matrix1 = np.array(self.t_matrix1) + t_matrix2 = np.array(self.t_matrix2) + t_m1_inv = np.linalg.inv(t_matrix1.transpose()).transpose() + t_m2_inv = np.linalg.inv(t_matrix2.transpose()).transpose() + + m1_mob, m2_mob, comp_matrix = self.get_matrices() + + self.play(Write(m1_mob)) + self.add_foreground_mobject(m1_mob) + self.dither() + self.apply_transposed_matrix(t_matrix1) + self.dither() + self.play(Write(m1_mob.label)) + self.add_foreground_mobject(m1_mob.label) + self.dither() + self.apply_transposed_matrix(t_m1_inv, run_time = 0) + self.dither() + + self.play(Write(m2_mob)) + self.add_foreground_mobject(m2_mob) + self.dither() + self.apply_transposed_matrix(t_matrix2) + self.dither() + self.play(Write(m2_mob.label)) + self.add_foreground_mobject(m2_mob.label) + self.dither() + self.apply_transposed_matrix(t_m2_inv, run_time = 0) + self.dither() + + for matrix in t_matrix1, t_matrix2: + self.apply_transposed_matrix(matrix, run_time = 1) + self.play(Write(comp_matrix)) + self.add_foreground_mobject(comp_matrix) + self.dither() + self.play(*map(FadeOut, [ + self.background_plane, + self.plane, + self.i_hat, + self.j_hat, + ]) + [ + Animation(m) for m in self.foreground_mobjects + ]) + self.remove(self.i_hat, self.j_hat) + self.dither() + + def get_matrices(self): + m1_mob = Matrix(np.array(self.t_matrix1).transpose()) + m2_mob = Matrix(np.array(self.t_matrix2).transpose()) + comp_matrix = Matrix([["?", "?"], ["?", "?"]]) + m1_mob.highlight(YELLOW) + m2_mob.highlight(PINK) + comp_matrix.get_entries().submobject_gradient_highlight(YELLOW, PINK) + + equals = TexMobject("=") + equals.next_to(comp_matrix, LEFT) + comp_matrix.add(equals) + m1_mob = VMobject(BackgroundRectangle(m1_mob), m1_mob) + m2_mob = VMobject(BackgroundRectangle(m2_mob), m2_mob) + comp_matrix = VMobject(BackgroundRectangle(comp_matrix), comp_matrix) + VMobject( + m2_mob, m1_mob, comp_matrix + ).arrange_submobjects(buff = 0.1).to_corner(UP+LEFT).shift(DOWN) + + for i, mob in enumerate([m1_mob, m2_mob]): + brace = Brace(mob, UP) + text = TexMobject("M_%d"%(i+1)) + text.next_to(brace, UP) + brace.add_background_rectangle() + text.add_background_rectangle() + brace.add(text) + mob.label = brace + return m1_mob, m2_mob, comp_matrix + +class MoreComplicatedExampleNumerically(MoreComplicatedExampleVisually): + def get_result(self): + return np.dot(self.t_matrix1, self.t_matrix2).transpose() + + def construct(self): + m1_mob, m2_mob, comp_matrix = self.get_matrices() + self.add(m1_mob, m2_mob, m1_mob.label, m2_mob.label, comp_matrix) + result = self.get_result() + + col1, col2 = [ + VMobject(*m1_mob.split()[1].get_mob_matrix()[:,i]) + for i in 0, 1 + ] + col1.target_color = X_COLOR + col2.target_color = Y_COLOR + for col in col1, col2: + circle = Circle() + circle.stretch_to_fit_height(m1_mob.get_height()) + circle.stretch_to_fit_width(m1_mob.get_width()/2.5) + circle.highlight(col.target_color) + circle.move_to(col) + col.circle = circle + + triplets = [ + (col1, "i", X_COLOR), + (col2, "j", Y_COLOR), + ] + for i, (col, char, color) in enumerate(triplets): + self.add(col) + start_state = self.get_mobjects() + question = TextMobject( + "Where does $\\hat{\\%smath}$ go?"%char + ) + question.split()[-4].highlight(color) + question.split()[-5].highlight(color) + question.scale(1.2) + question.shift(DOWN) + first = TextMobject("First here") + first.highlight(color) + first.shift(DOWN+LEFT) + first_arrow = Arrow( + first, col.circle.get_bottom(), color = color + ) + second = TextMobject("Then to whatever this is") + second.highlight(color) + second.to_edge(RIGHT).shift(DOWN) + + m2_copy = m2_mob.copy() + m2_target = m2_mob.copy() + m2_target.next_to(m2_mob, DOWN, buff = 1) + col_vect = Matrix(col.copy().split()) + col_vect.highlight(color) + col_vect.next_to(m2_target, RIGHT, buff = 0.1) + second_arrow = Arrow(second, col_vect, color = color) + + new_m2_copy = m2_mob.copy().split()[1] + intermediate = VMobject( + TexMobject("="), + col_vect.copy().get_entries().split()[0], + Matrix(new_m2_copy.get_mob_matrix()[:,0]), + TexMobject("+"), + col_vect.copy().get_entries().split()[1], + Matrix(new_m2_copy.get_mob_matrix()[:,1]), + TexMobject("=") + ) + intermediate.arrange_submobjects(buff = 0.1) + intermediate.next_to(col_vect, RIGHT) + + product = Matrix(result[:,i]) + product.next_to(intermediate, RIGHT) + + comp_col = VMobject(*comp_matrix.split()[1].get_mob_matrix()[:,i]) + + self.play(Write(question, run_time = 1 )) + self.dither() + self.play( + Transform(question, first), + ShowCreation(first_arrow), + ShowCreation(col.circle), + ApplyMethod(col.highlight, col.target_color) + ) + self.dither() + self.play( + Transform(m2_copy, m2_target, run_time = 2), + ApplyMethod(col.copy().move_to, col_vect, run_time = 2), + Write(col_vect.get_brackets()), + Transform(first_arrow, second_arrow), + Transform(question, second), + ) + self.dither() + self.play(*map(FadeOut, [question, first_arrow])) + self.play(Write(intermediate)) + self.dither() + self.play(Write(product)) + self.dither() + product_entries = product.get_entries() + self.play( + ApplyMethod(comp_col.highlight, BLACK), + ApplyMethod(product_entries.move_to, comp_col) + ) + self.dither() + + start_state.append(product_entries) + self.play(*[ + FadeOut(mob) + for mob in self.get_mobjects() + if mob not in start_state + ] + [ + Animation(product_entries) + ]) + self.dither() + +class GeneralMultiplication(MoreComplicatedExampleNumerically): + def get_result(self): + entries = map(TexMobject, [ + "ae+bg", "af+bh", "ce+dg", "cf+dh" + ]) + for mob in entries: + mob.split()[0].highlight(PINK) + mob.split()[3].highlight(PINK) + for mob in entries[0], entries[2]: + mob.split()[1].highlight(X_COLOR) + mob.split()[4].highlight(X_COLOR) + for mob in entries[1], entries[3]: + mob.split()[1].highlight(Y_COLOR) + mob.split()[4].highlight(Y_COLOR) + return np.array(entries).reshape((2, 2)) + + def get_matrices(self): + m1, m2, comp = MoreComplicatedExampleNumerically.get_matrices(self) + self.add(m1, m2, m1.label, m2.label, comp) + m1_entries = m1.split()[1].get_entries() + m2_entries = m2.split()[1].get_entries() + m2_entries_target = VMobject(*[ + TexMobject(char).move_to(entry).highlight(entry.get_color()) + for entry, char in zip(m2_entries.split(), "abcd") + ]) + m1_entries_target = VMobject(*[ + TexMobject(char).move_to(entry).highlight(entry.get_color()) + for entry, char in zip(m1_entries.split(), "efgh") + ]) + + words = TextMobject("This method works genearlly") + self.play(Write(words, run_time = 2)) + self.play(Transform( + m1_entries, m1_entries_target, + submobject_mode = "lagged_start" + )) + self.play(Transform( + m2_entries, m2_entries_target, + submobject_mode = "lagged_start" + )) + self.dither() + + new_comp = Matrix(self.get_result()) + new_comp.next_to(comp.split()[1].submobjects[-1], RIGHT) + new_comp.get_entries().highlight(BLACK) + self.play( + Transform(comp.split()[1].get_brackets(), new_comp.get_brackets()), + *[ + ApplyMethod(q_mark.move_to, entry) + for q_mark, entry in zip( + comp.split()[1].get_entries().split(), + new_comp.get_entries().split() + ) + ] + ) + self.dither() + self.play(FadeOut(words)) + return m1, m2, comp + +class MoreComplicatedExampleWithJustIHat(MoreComplicatedExampleVisually): + CONFIG = { + "show_basis_vectors" : False, + "v_coords" : [1, 0], + "v_color" : X_COLOR, + } + def construct(self): + self.setup() + self.add_vector(self.v_coords, self.v_color) + self.apply_transposed_matrix(self.t_matrix1) + self.dither() + self.apply_transposed_matrix(self.t_matrix2) + self.dither() + +class MoreComplicatedExampleWithJustJHat(MoreComplicatedExampleWithJustIHat): + CONFIG = { + "v_coords" : [0, 1], + "v_color" : Y_COLOR, + } + +class RoteMatrixMultiplication(NumericalMatrixMultiplication): + CONFIG = { + "left_matrix" : [[-3, 1], [2, 5]], + "right_matrix" : [[5, 3], [7, -3]] + } + +class NeverForget(TeacherStudentsScene): + def construct(self): + self.setup() + self.teacher_says("Never forget what \\\\ this represents!") + self.random_blink() + self.student_thinks("", student_index = 0) + def warp(point): + point += 2*DOWN+RIGHT + return 20*point/np.linalg.norm(point) + self.play(ApplyPointwiseFunction( + warp, + VMobject(*self.get_mobjects()) + )) + +class AskAboutCommutativity(Scene): + def construct(self): + l_m1, l_m2, eq, r_m2, r_m1 = TexMobject([ + "M_1", "M_2", "=", "M_2", "M_1" + ]).scale(1.5).split() + VMobject(l_m1, r_m1).highlight(YELLOW) + VMobject(l_m2, r_m2).highlight(PINK) + q_marks = TextMobject("???") + q_marks.highlight(TEAL) + q_marks.next_to(eq, UP) + neq = TexMobject("\\neq") + neq.move_to(eq) + + self.play(*map(Write, [l_m1, l_m2, eq])) + self.play( + Transform(l_m1.copy(), r_m1), + Transform(l_m2.copy(), r_m2), + path_arc = -np.pi, + run_time = 2 + ) + self.play(Write(q_marks)) + self.dither() + self.play(Transform( + VMobject(eq, q_marks), + VMobject(neq), + submobject_mode = "lagged_start" + )) + self.dither() + +class ShowShear(LinearTransformationScene): + CONFIG = { + "title" : "Shear", + "title_color" : PINK, + "t_matrix" : [[1, 0], [1, 1]] + } + def construct(self): + self.setup() + title = TextMobject(self.title) + title.scale(1.5).to_edge(UP) + title.highlight(self.title_color) + title.add_background_rectangle() + self.add_foreground_mobject(title) + + self.dither() + self.apply_transposed_matrix(self.t_matrix) + self.dither() + +class ShowRotation(ShowShear): + CONFIG = { + "title" : "$90^\\circ$ rotation", + "title_color" : YELLOW, + "t_matrix" : [[0, 1], [-1, 0]] + } + +class FirstShearThenRotation(LinearTransformationScene): + CONFIG = { + "title" : "First shear then rotation", + "t_matrix1" : [[1, 0], [1, 1]], + "t_matrix2" : [[0, 1], [-1, 0]], + "foreground_plane_kwargs" : { + "x_radius" : 2*SPACE_WIDTH, + "y_radius" : 2*SPACE_WIDTH, + "secondary_line_ratio" : 0 + }, + } + def construct(self): + self.setup() + title_parts = self.title.split(" ") + title = TextMobject(title_parts) + for i, part in enumerate(title_parts): + if part == "rotation": + title.split()[i].highlight(YELLOW) + elif part == "shear": + title.split()[i].highlight(PINK) + title.scale(1.5) + self.add_title(title) + + self.apply_transposed_matrix(self.t_matrix1) + self.apply_transposed_matrix(self.t_matrix2) + self.i_hat.rotate(-0.01)##Laziness + self.dither() + self.write_vector_coordinates(self.i_hat, color = X_COLOR) + self.dither() + self.write_vector_coordinates(self.j_hat, color = Y_COLOR) + self.dither() + +class RotationThenShear(FirstShearThenRotation): + CONFIG = { + "title" : "First rotation then shear", + "t_matrix1" : [[0, 1], [-1, 0]], + "t_matrix2" : [[1, 0], [1, 1]], + } + +class NoticeTheLackOfComputations(TeacherStudentsScene): + def construct(self): + self.setup() + self.teacher_says(""" + Notice the lack + of computations! + """) + self.random_blink() + + students = self.get_students() + random.shuffle(students) + unit = np.array([-0.5, 0.5]) + self.play(*[ + ApplyMethod( + pi.change_mode, "pondering", + rate_func = squish_rate_func(smooth, *np.clip(unit+0.5*i, 0, 1)) + ) + for i, pi in enumerate(students) + ]) + self.random_blink() + self.dither() + +class AskAssociativityQuestion(Scene): + def construct(self): + morty = Mortimer() + morty.scale(0.8) + morty.to_corner(DOWN+RIGHT) + morty.shift(0.5*LEFT) + title = TextMobject("Associativity:") + title.to_corner(UP+LEFT) + + lhs = TexMobject(list("(AB)C")) + lp, a, b, rp, c = lhs.split() + rhs = VMobject(*[m.copy() for m in a, lp, b, c, rp]) + point = VectorizedPoint() + start = VMobject(*[m.copy() for m in point, a, b, point, c]) + for mob in lhs, rhs, start: + mob.arrange_submobjects(buff = 0.1) + a, lp, b, c, rp = rhs.split() + rhs = VMobject(lp, a, b, rp, c)##Align order to lhs + eq = TexMobject("=") + q_marks = TextMobject("???") + q_marks.submobject_gradient_highlight(TEAL_B, TEAL_D) + q_marks.next_to(eq, UP) + lhs.next_to(eq, LEFT) + rhs.next_to(eq, RIGHT) + start.move_to(lhs) + + + self.add(morty, title) + self.dither() + self.play(Blink(morty)) + self.play(Write(start)) + self.dither() + self.play(Transform(start, lhs)) + self.dither() + self.play( + Transform(lhs, rhs, path_arc = -np.pi), + Write(eq) + ) + self.play(Write(q_marks)) + self.play(Blink(morty)) + self.play(morty.change_mode, "pondering") + + lp, a, b, rp, c = start.split() + self.show_full_matrices(morty, a, b, c, title) + + def show_full_matrices(self, morty, a, b, c, title): + everything = self.get_mobjects() + everything.remove(morty) + everything.remove(title) + everything = VMobject(*everything) + + matrices = map(matrix_to_mobject, [ + np.array(list(m)).reshape((2, 2)) + for m in "abcd", "efgh", "ijkl" + ]) + VMobject(*matrices).arrange_submobjects() + + self.play(everything.to_edge, UP) + for letter, matrix in zip([a, b, c], matrices): + self.play(Transform( + letter.copy(), matrix, + submobject_mode = "lagged_start" + )) + self.remove(*self.get_mobjects_from_last_animation()) + self.add(matrix) + self.dither() + self.move_matrix_parentheses(morty, matrices) + + def move_matrix_parentheses(self, morty, matrices): + m1, m2, m3 = matrices + parens = TexMobject(["(", ")"]) + parens.scale_to_fit_height(1.2*m1.get_height()) + lp, rp = parens.split() + state1 = VMobject( + VectorizedPoint(m1.get_left()), + m1, m2, + VectorizedPoint(m2.get_right()), + m3 + ) + state2 = VMobject(*[ + m.copy() for m in lp, m1, m2, rp, m3 + ]) + state3 = VMobject(*[ + m.copy() for m in m1, lp, m2, m3, rp + ]) + for state in state2, state3: + state.arrange_submobjects(RIGHT, buff = 0.1) + m1, lp, m2, m3, rp = state3.split() + state3 = VMobject(lp, m1, m2, rp, m3) + + self.play(morty.change_mode, "angry") + for state in state2, state3: + self.play(Transform(state1, state)) + self.dither() + self.play(morty.change_mode, "confused") + self.dither() + +class ThreeSuccessiveTransformations(LinearTransformationScene): + CONFIG = { + "t_matrices" : [ + [[2, 1], [1, 2]], + [[np.cos(-np.pi/6), np.sin(-np.pi/6)], [-np.sin(-np.pi/6), np.cos(-np.pi/6)]], + [[1, 0], [1, 1]] + ], + "symbols_str" : "A(BC)", + "include_background_plane" : False, + } + def construct(self): + self.setup() + symbols = TexMobject(list(self.symbols_str)) + symbols.scale(1.5) + symbols.to_edge(UP) + a, b, c = None, None, None + for mob, letter in zip(symbols.split(), self.symbols_str): + if letter == "A": + a = mob + elif letter == "B": + b = mob + elif letter == "C": + c = mob + + symbols.add_background_rectangle() + self.add_foreground_mobject(symbols) + + brace = Brace(c, DOWN) + words = TextMobject("Apply this transformation") + words.add_background_rectangle() + words.next_to(brace, DOWN) + brace.add(words) + + self.play(Write(brace, run_time = 1)) + self.add_foreground_mobject(brace) + + last = VectorizedPoint() + for t_matrix, sym in zip(self.t_matrices, [c, b, a]): + self.play( + brace.next_to, sym, DOWN, + sym.highlight, YELLOW, + last.highlight, WHITE + ) + self.apply_transposed_matrix(t_matrix, run_time = 1) + last = sym + self.dither() + +class ThreeSuccessiveTransformationsAltParens(ThreeSuccessiveTransformations): + CONFIG = { + "symbols_str" : "(AB)C" + } diff --git a/eola/matrix.py b/eola/matrix.py index 32fdb98a..40f7a1cf 100644 --- a/eola/matrix.py +++ b/eola/matrix.py @@ -8,11 +8,11 @@ from animation.transform import ApplyPointwiseFunction, Transform, \ ApplyMethod, FadeOut, ApplyFunction from animation.simple_animations import ShowCreation, Write from topics.number_line import NumberPlane, Axes -from topics.geometry import Vector, Line, Circle, Arrow, Dot +from topics.geometry import Vector, Line, Circle, Arrow, Dot, BackgroundRectangle from helpers import * -VECTOR_LABEL_SCALE_VAL = 1.0 +VECTOR_LABEL_SCALE_VAL = 0.8 def matrix_to_tex_string(matrix): matrix = np.array(matrix).astype("string") @@ -31,7 +31,8 @@ def matrix_to_tex_string(matrix): def matrix_to_mobject(matrix): return TexMobject(matrix_to_tex_string(matrix)) -def vector_coordinate_label(vector_mob, integer_labels = True, n_dim = 2): +def vector_coordinate_label(vector_mob, integer_labels = True, + n_dim = 2, color = WHITE): vect = np.array(vector_mob.get_end()) if integer_labels: vect = np.round(vect).astype(int) @@ -41,11 +42,14 @@ def vector_coordinate_label(vector_mob, integer_labels = True, n_dim = 2): label.scale(VECTOR_LABEL_SCALE_VAL) shift_dir = np.array(vector_mob.get_end()) - if shift_dir[0] > 0: #Pointing right + if shift_dir[0] >= 0: #Pointing right shift_dir -= label.get_left() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*LEFT else: #Pointing left shift_dir -= label.get_right() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*RIGHT label.shift(shift_dir) + label.highlight(color) + background = BackgroundRectangle(label) + label.submobjects = [background] + label.submobjects return label class Matrix(VMobject): @@ -104,9 +108,15 @@ class Matrix(VMobject): self.brackets = VMobject(l_bracket, r_bracket) return self + def highlight_columns(self, *colors): + for i, color in enumerate(colors): + VMobject(*self.mob_matrix[:,i]).highlight(color) + return self + def get_mob_matrix(self): return self.mob_matrix + def get_entries(self): return VMobject(*self.get_mob_matrix().flatten()) @@ -117,7 +127,8 @@ class Matrix(VMobject): class NumericalMatrixMultiplication(Scene): CONFIG = { "left_matrix" : [[1, 2], [3, 4]], - "right_matrix" : [[5, 6], [7, 8]] + "right_matrix" : [[5, 6], [7, 8]], + "use_parens" : True, } def construct(self): left_string_matrix, right_string_matrix = [ @@ -143,8 +154,9 @@ class NumericalMatrixMultiplication(Scene): mob_matrix = np.array([VMobject()]).repeat(m*n).reshape((m, n)) for a in range(m): for b in range(n): + template = "(%s)(%s)" if self.use_parens else "%s%s" parts = [ - prefix + "(%s)(%s)"%(left[a][c], right[c][b]) + prefix + template%(left[a][c], right[c][b]) for c in range(k) for prefix in ["" if c == 0 else "+"] ] diff --git a/eola/two_d_space.py b/eola/two_d_space.py index 646e7a53..03072c60 100644 --- a/eola/two_d_space.py +++ b/eola/two_d_space.py @@ -9,7 +9,7 @@ from animation.transform import ApplyPointwiseFunction, Transform, \ ApplyMethod, FadeOut, ApplyFunction from animation.simple_animations import ShowCreation, Write from topics.number_line import NumberPlane, Axes -from topics.geometry import Vector, Line, Circle, Arrow, Dot +from topics.geometry import Vector, Line, Circle, Arrow, Dot, BackgroundRectangle from helpers import * from eola.matrix import Matrix, VECTOR_LABEL_SCALE_VAL, vector_coordinate_label @@ -55,6 +55,11 @@ class VectorScene(Scene): self.add(vector) return vector + def write_vector_coordinates(self, vector, **kwargs): + coords = vector_coordinate_label(vector, **kwargs) + self.play(Write(coords)) + return coords + def get_basis_vectors(self): return [ Vector( @@ -303,6 +308,11 @@ class LinearTransformationScene(VectorScene): self.moving_vectors.append(vector) return vector + def write_vector_coordinates(self, vector, **kwargs): + coords = VectorScene.write_vector_coordinates(self, vector, **kwargs) + self.add_foreground_mobject(coords) + return coords + def add_transformable_label(self, vector, label, new_label = None, **kwargs): label_mob = self.label_vector(vector, label, **kwargs) if new_label: @@ -316,6 +326,16 @@ class LinearTransformationScene(VectorScene): self.transformable_labels.append(label_mob) return label_mob + def add_title(self, title, scale_val = 1.5, animate = False): + if not isinstance(title, Mobject): + title = TextMobject(title).scale(scale_val) + title.to_edge(UP) + title.add_background_rectangle() + if animate: + self.play(Write(title)) + self.add_foreground_mobject(title) + return self + def get_matrix_transformation(self, transposed_matrix): transposed_matrix = np.array(transposed_matrix) if transposed_matrix.shape == (2, 2): diff --git a/helpers.py b/helpers.py index f7390558..3dcbf07f 100644 --- a/helpers.py +++ b/helpers.py @@ -133,6 +133,9 @@ def list_update(l1, l2): """ return filter(lambda e : e not in l2, l1) + list(l2) +def list_difference_update(l1, l2): + return filter(lambda e : e not in l2, l1) + def all_elements_are_instances(iterable, Class): return all(map(lambda e : isinstance(e, Class), iterable)) diff --git a/mobject/mobject.py b/mobject/mobject.py index 93159a91..9dde2abd 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -179,6 +179,7 @@ class Mobject(object): def flip(self, axis = UP): self.rotate_in_place(np.pi, axis) + return self def scale_in_place(self, scale_factor): self.do_in_place(self.scale, scale_factor)