def record_basis_coordinates(self, vect_array, vect): i_label = vector_coordinate_label(self.i_hat) i_label.highlight(X_COLOR) j_label = vector_coordinate_label(self.j_hat) j_label.highlight(Y_COLOR) for mob in i_label, j_label: mob.scale_in_place(0.8) background = BackgroundRectangle(mob) self.play(ShowCreation(background), Write(mob)) self.wait() x, y = vect_array.get_entries().split() pre_formula = VMobject(x, i_label, TexMobject("+"), y, j_label) post_formula = pre_formula.copy() pre_formula.split()[2].fade(1) post_formula.arrange_submobjects(buff=0.1) post_formula.next_to(vect, DOWN) background = BackgroundRectangle(post_formula) everything = self.get_mobjects() everything.remove(vect) self.play(*[ApplyMethod(m.fade) for m in everything] + [ ShowCreation(background, run_time=2, rate_func=squish_rate_func(smooth, 0.5, 1)), Transform(pre_formula.copy(), post_formula, run_time=2), ApplyMethod(vect.set_stroke, width=7) ]) self.wait()
def record_basis_coordinates(self, vect_array, vect): i_label = vector_coordinate_label(self.i_hat) i_label.highlight(X_COLOR) j_label = vector_coordinate_label(self.j_hat) j_label.highlight(Y_COLOR) for mob in i_label, j_label: mob.scale_in_place(0.8) background = BackgroundRectangle(mob) self.play(ShowCreation(background), Write(mob)) self.dither() x, y = vect_array.get_entries().split() pre_formula = VMobject( x, i_label, TexMobject("+"), y, j_label ) post_formula = pre_formula.copy() pre_formula.split()[2].fade(1) post_formula.arrange_submobjects(buff = 0.1) post_formula.next_to(vect, DOWN) background = BackgroundRectangle(post_formula) everything = self.get_mobjects() everything.remove(vect) self.play(*[ ApplyMethod(m.fade) for m in everything ] + [ ShowCreation(background, run_time = 2, rate_func = squish_rate_func(smooth, 0.5, 1)), Transform(pre_formula.copy(), post_formula, run_time = 2), ApplyMethod(vect.set_stroke, width = 7) ]) self.dither()
def construct(self): title = TextMobject("Essence of Linear Algebra") title.highlight(BLUE) title.to_corner(UP+LEFT) h_line = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT) h_line.next_to(title, DOWN) h_line.to_edge(LEFT, buff = 0) chapters = VMobject(*map(TextMobject, [ "Chapter 1: Vectors, what even are they?", "Chapter 2: Linear combinations, span and bases", "Chapter 3: Matrices as linear transformations", "Chapter 4: Matrix multiplication as composition", "Chapter 5: The determinant", "Chapter 6: Inverse matrices, column space and null space", "Chapter 7: Dot products and cross products", "Chapter 8: Change of basis", "Chapter 9: Eigenvectors and eigenvalues", "Chapter 10: Abstract vector spaces", ])) chapters.arrange_submobjects(DOWN) chapters.scale(0.7) chapters.next_to(h_line, DOWN) self.play( Write(title), ShowCreation(h_line) ) for chapter in chapters.split(): chapter.to_edge(LEFT, buff = 1) self.play(FadeIn(chapter)) self.dither(2) entry3 = chapters.split()[2] added_words = TextMobject("(Personally, I'm most excited \\\\ to do this one)") added_words.scale(0.5) added_words.highlight(YELLOW) added_words.next_to(h_line, DOWN) added_words.to_edge(RIGHT) arrow = Arrow(added_words.get_bottom(), entry3) self.play( ApplyMethod(entry3.highlight, YELLOW), ShowCreation(arrow, submobject_mode = "one_at_a_time"), Write(added_words), run_time = 1 ) self.dither() removeable = VMobject(added_words, arrow, h_line, title) self.play(FadeOut(removeable)) self.remove(removeable) self.series_of_videos(chapters)
def construct(self): title = TextMobject("Essence of Linear Algebra") title.highlight(BLUE) title.to_corner(UP + LEFT) h_line = Line(SPACE_WIDTH * LEFT, SPACE_WIDTH * RIGHT) h_line.next_to(title, DOWN) h_line.to_edge(LEFT, buff=0) chapters = VMobject(*map(TextMobject, [ "Chapter 1: Vectors, what even are they?", "Chapter 2: Linear combinations, span and bases", "Chapter 3: Matrices as linear transformations", "Chapter 4: Matrix multiplication as composition", "Chapter 5: The determinant", "Chapter 6: Inverse matrices, column space and null space", "Chapter 7: Dot products and cross products", "Chapter 8: Change of basis", "Chapter 9: Eigenvectors and eigenvalues", "Chapter 10: Abstract vector spaces", ])) chapters.arrange_submobjects(DOWN) chapters.scale(0.7) chapters.next_to(h_line, DOWN) self.play(Write(title), ShowCreation(h_line)) for chapter in chapters.split(): chapter.to_edge(LEFT, buff=1) self.play(FadeIn(chapter)) self.wait(2) entry3 = chapters.split()[2] added_words = TextMobject( "(Personally, I'm most excited \\\\ to do this one)") added_words.scale(0.5) added_words.highlight(YELLOW) added_words.next_to(h_line, DOWN) added_words.to_edge(RIGHT) arrow = Arrow(added_words.get_bottom(), entry3) self.play(ApplyMethod(entry3.highlight, YELLOW), ShowCreation(arrow, submobject_mode="one_at_a_time"), Write(added_words), run_time=1) self.wait() removeable = VMobject(added_words, arrow, h_line, title) self.play(FadeOut(removeable)) self.remove(removeable) self.series_of_videos(chapters)
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()
def construct(self): words = map(TextMobject, [ "Just brushing up", "Has yet to take the course", "Supplementing course concurrently", ]) students = VMobject(*[ Randolph(color = c) for c in BLUE_D, BLUE_C, BLUE_E ]) modes = ["pondering", "speaking_looking_left", "sassy"] students.arrange_submobjects(RIGHT) students.scale(0.8) students.center().to_edge(DOWN) last_word, last_arrow = None, None for word, student, mode in zip(words, students.split(), modes): word.shift(2*UP) arrow = Arrow(word, student) if last_word: word_anim = Transform(last_word, word) arrow_anim = Transform(last_arrow, arrow) else: word_anim = Write(word, run_time = 1) arrow_anim = ShowCreation(arrow, submobject_mode = "one_at_a_time") last_word = word last_arrow = arrow self.play( word_anim, arrow_anim, ApplyMethod(student.change_mode, mode) ) self.play(Blink(student)) self.dither() self.dither()
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.wait() 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.wait()
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()
def construct(self): randy = Randolph().to_corner() bubble = randy.get_bubble(height=3.8) text1 = TextMobject("Think of individual vectors as arrows") text2 = TextMobject("Think of sets of vectors as points") for text in text1, text2: text.to_edge(UP) single_vector = Vector([2, 1]) vector_group = VMobject( *[Vector([x, 1]) for x in np.linspace(-2, 2, 5)]) bubble.position_mobject_inside(single_vector) bubble.position_mobject_inside(vector_group) dots = VMobject(*[Dot(v.get_end()) for v in vector_group.split()]) self.add(randy) self.play(ApplyMethod(randy.change_mode, "pondering"), ShowCreation(bubble)) self.play(FadeIn(text1)) self.play(ShowCreation(single_vector)) self.wait(3) self.play(Transform(text1, text2), Transform(single_vector, vector_group)) self.remove(single_vector) self.add(vector_group) self.wait() self.play(Transform(vector_group, dots)) self.wait()
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 construct(self): vector = Vector([-2, 3]) plane = NumberPlane() axis_labels = plane.get_axis_labels() other_vectors = VMobject(*map(Vector, [[1, 2], [2, -1], [4, 0]])) colors = [GREEN_B, MAROON_B, PINK] for v, color in zip(other_vectors.split(), colors): v.highlight(color) shift_val = 4 * RIGHT + DOWN dot = Dot(radius=0.1) dot.highlight(RED) tail_word = TextMobject("Tail") tail_word.shift(0.5 * DOWN + 2.5 * LEFT) line = Line(tail_word, dot) self.play(ShowCreation(vector, submobject_mode="one_at_a_time")) self.dither(2) self.play(ShowCreation(plane, summobject_mode="lagged_start"), Animation(vector)) self.play(Write(axis_labels, run_time=1)) self.dither() self.play(GrowFromCenter(dot), ShowCreation(line), Write(tail_word, run_time=1)) self.dither() self.play(FadeOut(tail_word), ApplyMethod(VMobject(dot, line).scale, 0.01)) self.remove(tail_word, line, dot) self.dither() self.play( ApplyMethod(vector.shift, shift_val, path_arc=3 * np.pi / 2, run_time=3)) self.play( ApplyMethod(vector.shift, -shift_val, rate_func=rush_into, run_time=0.5)) self.dither(3) self.play( ShowCreation(other_vectors, submobject_mode="one_at_a_time", run_time=3)) self.dither(3) x_axis, y_axis = plane.get_axes().split() x_label = axis_labels.split()[0] x_axis = x_axis.copy() x_label = x_label.copy() everything = VMobject(*self.mobjects) self.play(FadeOut(everything), Animation(x_axis), Animation(x_label))
def animate_product(self, left, right, result): l_matrix = left.get_mob_matrix() r_matrix = right.get_mob_matrix() result_matrix = result.get_mob_matrix() circle = Circle( radius = l_matrix[0][0].get_height(), color = GREEN ) circles = VMobject(*[ entry.get_point_mobject() for entry in l_matrix[0][0], r_matrix[0][0] ]) (m, k), n = l_matrix.shape, r_matrix.shape[1] for mob in result_matrix.flatten(): mob.highlight(BLACK) lagging_anims = [] for a in range(m): for b in range(n): for c in range(k): l_matrix[a][c].highlight(YELLOW) r_matrix[c][b].highlight(YELLOW) for c in range(k): start_parts = VMobject( l_matrix[a][c].copy(), r_matrix[c][b].copy() ) result_entry = result_matrix[a][b].split()[c] new_circles = VMobject(*[ circle.copy().shift(part.get_center()) for part in start_parts.split() ]) self.play(Transform(circles, new_circles)) self.play( Transform( start_parts, result_entry.copy().highlight(YELLOW), path_arc = -np.pi/2, submobject_mode = "all_at_once", ), *lagging_anims ) result_entry.highlight(YELLOW) self.remove(start_parts) lagging_anims = [ ApplyMethod(result_entry.highlight, WHITE) ] for c in range(k): l_matrix[a][c].highlight(WHITE) r_matrix[c][b].highlight(WHITE) self.play(FadeOut(circles), *lagging_anims) self.dither()
def construct(self): v = TexMobject(self.v_str) v.highlight(YELLOW) eq = TexMobject("=") coords = Matrix(["x", "y", "z"]) eq2 = eq.copy() if self.post_transform: L, l_paren, r_paren = map(TexMobject, "L()") parens = VMobject(l_paren, r_paren) parens.scale(2) parens.stretch_to_fit_height( coords.get_height() ) VMobject(L, l_paren, coords, r_paren).arrange_submobjects(buff = 0.1) coords.submobjects = [L, l_paren] + coords.submobjects + [r_paren] lin_comb = VMobject(*map(TexMobject, [ "x", self.i_str, "+", "y", self.j_str, "+", "z", self.k_str, ])) lin_comb.arrange_submobjects( RIGHT, buff = 0.1, aligned_edge = ORIGIN if self.post_transform else DOWN ) lin_comb_parts = np.array(lin_comb.split()) new_x, new_y, new_z = lin_comb_parts[[0, 3, 6]] i, j, k = lin_comb_parts[[1, 4, 7]] plusses = lin_comb_parts[[2, 5]] i.highlight(X_COLOR) j.highlight(Y_COLOR) k.highlight(Z_COLOR) everything = VMobject(v, eq, coords, eq2, lin_comb) everything.arrange_submobjects(buff = 0.2) everything.scale_to_fit_width(2*SPACE_WIDTH - 1) everything.to_edge(DOWN) if not self.post_transform: lin_comb.shift(0.35*UP) self.play(*map(Write, [v, eq, coords])) self.dither() self.play( Transform( coords.get_entries().copy(), VMobject(new_x, new_y, new_z), path_arc = -np.pi, submobject_mode = "lagged_start" ), Write(VMobject(*[eq2, i, j, k] + list(plusses))), run_time = 3 ) self.dither()
def construct(self): title = TextMobject("Questions to ponder") title.highlight(YELLOW).to_edge(UP) self.add(title) questions = VMobject(*map(TextMobject, [ "1. Can you visualize these transformations?", "2. Can you represent them with matrices?", "3. How many rows and columns?", "4. When can you multiply these matrices?", ])) questions.arrange_submobjects(DOWN, buff=1, aligned_edge=LEFT) questions.to_edge(LEFT) for question in questions.split(): self.play(Write(question, run_time=1)) self.wait()
def construct(self): title = TextMobject("Questions to ponder") title.highlight(YELLOW).to_edge(UP) self.add(title) questions = VMobject(*map(TextMobject, [ "1. Can you visualize these transformations?", "2. Can you represent them with matrices?", "3. How many rows and columns?", "4. When can you multiply these matrices?", ])) questions.arrange_submobjects(DOWN, buff = 1, aligned_edge = LEFT) questions.to_edge(LEFT) for question in questions.split(): self.play(Write(question, run_time = 1)) self.dither()
def construct(self): i_to = TexMobject("\\hat{\\imath} \\to").highlight(X_COLOR) j_to = TexMobject("\\hat{\\jmath} \\to").highlight(Y_COLOR) k_to = TexMobject("\\hat{k} \\to").highlight(Z_COLOR) i_array = Matrix(self.col1) j_array = Matrix(self.col2) k_array = Matrix(self.col3) everything = VMobject( i_to, i_array, TexMobject("=").highlight(BLACK), j_to, j_array, TexMobject("=").highlight(BLACK), k_to, k_array, TexMobject("=").highlight(BLACK), ) everything.arrange_submobjects(RIGHT, buff=0.1) everything.scale_to_fit_width(2 * SPACE_WIDTH - 1) everything.to_edge(DOWN) i_array.highlight(X_COLOR) j_array.highlight(Y_COLOR) k_array.highlight(Z_COLOR) arrays = [i_array, j_array, k_array] matrix = Matrix( reduce(lambda a1, a2: np.append(a1, a2, axis=1), [m.copy().get_mob_matrix() for m in arrays])) matrix.to_edge(DOWN) start_entries = reduce(op.add, map(lambda a: a.get_entries().split(), arrays)) target_entries = matrix.get_mob_matrix().transpose().flatten() start_l_bracket = i_array.get_brackets().split()[0] start_r_bracket = k_array.get_brackets().split()[1] start_brackets = VMobject(start_l_bracket, start_r_bracket) target_bracketes = matrix.get_brackets() for mob in everything.split(): self.play(Write(mob, run_time=1)) self.wait() self.play( FadeOut(everything), Transform(VMobject(*start_entries), VMobject(*target_entries)), Transform(start_brackets, target_bracketes)) self.wait()
def animate_product(self, left, right, result): l_matrix = left.get_mob_matrix() r_matrix = right.get_mob_matrix() result_matrix = result.get_mob_matrix() circle = Circle(radius=l_matrix[0][0].get_height(), color=GREEN) circles = VMobject(*[ entry.get_point_mobject() for entry in l_matrix[0][0], r_matrix[0][0] ]) (m, k), n = l_matrix.shape, r_matrix.shape[1] for mob in result_matrix.flatten(): mob.highlight(BLACK) lagging_anims = [] for a in range(m): for b in range(n): for c in range(k): l_matrix[a][c].highlight(YELLOW) r_matrix[c][b].highlight(YELLOW) for c in range(k): start_parts = VMobject(l_matrix[a][c].copy(), r_matrix[c][b].copy()) result_entry = result_matrix[a][b].split()[c] new_circles = VMobject(*[ circle.copy().shift(part.get_center()) for part in start_parts.split() ]) self.play(Transform(circles, new_circles)) self.play( Transform( start_parts, result_entry.copy().highlight(YELLOW), path_arc=-np.pi / 2, submobject_mode="all_at_once", ), *lagging_anims) result_entry.highlight(YELLOW) self.remove(start_parts) lagging_anims = [ ApplyMethod(result_entry.highlight, WHITE) ] for c in range(k): l_matrix[a][c].highlight(WHITE) r_matrix[c][b].highlight(WHITE) self.play(FadeOut(circles), *lagging_anims) self.wait()
def construct(self): i_to = TexMobject("\\hat{\\imath} \\to").highlight(X_COLOR) j_to = TexMobject("\\hat{\\jmath} \\to").highlight(Y_COLOR) k_to = TexMobject("\\hat{k} \\to").highlight(Z_COLOR) i_array = Matrix(self.col1) j_array = Matrix(self.col2) k_array = Matrix(self.col3) everything = VMobject( i_to, i_array, TexMobject("=").highlight(BLACK), j_to, j_array, TexMobject("=").highlight(BLACK), k_to, k_array, TexMobject("=").highlight(BLACK), ) everything.arrange_submobjects(RIGHT, buff = 0.1) everything.scale_to_fit_width(2*SPACE_WIDTH-1) everything.to_edge(DOWN) i_array.highlight(X_COLOR) j_array.highlight(Y_COLOR) k_array.highlight(Z_COLOR) arrays = [i_array, j_array, k_array] matrix = Matrix(reduce( lambda a1, a2 : np.append(a1, a2, axis = 1), [m.copy().get_mob_matrix() for m in arrays] )) matrix.to_edge(DOWN) start_entries = reduce(op.add, map( lambda a : a.get_entries().split(), arrays )) target_entries = matrix.get_mob_matrix().transpose().flatten() start_l_bracket = i_array.get_brackets().split()[0] start_r_bracket = k_array.get_brackets().split()[1] start_brackets = VMobject(start_l_bracket, start_r_bracket) target_bracketes = matrix.get_brackets() for mob in everything.split(): self.play(Write(mob, run_time = 1)) self.dither() self.play( FadeOut(everything), Transform(VMobject(*start_entries), VMobject(*target_entries)), Transform(start_brackets, target_bracketes) ) self.dither()
def construct(self): morty = Mortimer() morty.to_corner(DOWN+RIGHT) morty.look(DOWN+LEFT) new_morty = morty.copy().change_mode("speaking") new_morty.look(DOWN+LEFT) randys = VMobject(*[ Randolph(color = color).scale(0.8) for color in BLUE_D, BLUE_C, BLUE_E ]) randys.arrange_submobjects(RIGHT) randys.to_corner(DOWN+LEFT) randy = randys.split()[1] speech_bubble = morty.get_bubble("speech") words = TextMobject("Think of some vector...") speech_bubble.position_mobject_inside(words) thought_bubble = randy.get_bubble() arrow = Vector([2, 1]).scale(0.7) or_word = TextMobject("or") array = Matrix([2, 1]).scale(0.5) q_mark = TextMobject("?") thought = VMobject(arrow, or_word, array, q_mark) thought.arrange_submobjects(RIGHT, buff = 0.2) thought_bubble.position_mobject_inside(thought) thought_bubble.set_fill(BLACK, opacity = 1) self.add(morty, randys) self.play( ShowCreation(speech_bubble), Transform(morty, new_morty), Write(words) ) self.dither(2) self.play( FadeOut(speech_bubble), FadeOut(words), ApplyMethod(randy.change_mode, "pondering"), ShowCreation(thought_bubble), Write(thought) ) self.dither(2)
def construct(self): icons = [VideoIcon() for x in range(10)] colors = Color(BLUE_A).range_to(BLUE_D, len(icons)) for icon, color in zip(icons, colors): icon.set_fill(color, opacity=1) icons = VMobject(*icons) icons.arrange_submobjects(RIGHT) icons.to_edge(LEFT) icons.shift(UP) icons = icons.split() def rate_func_creator(offset): return lambda a: min(max(2 * (a - offset), 0), 1) self.play(*[ FadeIn(icon, run_time=5, rate_func=rate_func_creator(offset)) for icon, offset in zip(icons, np.linspace(0, 0.5, len(icons))) ]) self.wait()
def construct(self): morty = Mortimer(mode = "speaking") morty.to_corner(DOWN + RIGHT) bubble = morty.get_bubble(SpeechBubble) bubble.write("I'm assuming you \\\\ know linear algebra\\dots") words = bubble.content bubble.clear() randys = VMobject(*[ Randolph(color = c) for c in BLUE_D, BLUE_C, BLUE_E ]) randys.arrange_submobjects(RIGHT) randys.scale(0.8) randys.to_corner(DOWN+LEFT) self.add(randys, morty) self.play(FadeIn(bubble), Write(words), run_time = 3) for randy in np.array(randys.split())[[2,0,1]]: self.play(Blink(randy)) self.dither()
def construct(self): self.setup() self.plane.fade(0.3) self.add_unit_square(color = YELLOW_E, opacity = 0.5) self.add_title( ["The", "``determinant''", "of a transformation"], scale_factor = 1 ) self.title.split()[1].split()[1].highlight(YELLOW) matrix_background, matrix, det_text = self.get_matrix() self.add_foreground_mobject(matrix_background, matrix) A = TexMobject("A") area_label = VMobject(A.copy(), A.copy(), A) area_label.move_to(self.square) det = np.linalg.det(self.t_matrix) if np.round(det) == det: det = int(det) area_label_target = VMobject( TexMobject(str(det)), TexMobject("\\cdot"), A.copy() ) if det < 1 and det > 0: area_label_target.scale(det) area_label_target.arrange_submobjects(RIGHT, buff = 0.1) self.add_moving_mobject(area_label, area_label_target) self.dither() self.apply_transposed_matrix(self.t_matrix) self.dither() det_mob_copy = area_label.split()[0].copy() new_det_mob = det_mob_copy.copy().scale_to_fit_height( det_text.split()[0].get_height() ) new_det_mob.next_to(det_text, RIGHT, buff = 0.2) new_det_mob.add_background_rectangle() det_mob_copy.add_background_rectangle(opacity = 0) self.play(Write(det_text)) self.play(Transform(det_mob_copy, new_det_mob)) self.dither()
def construct(self): icons = [VideoIcon() for x in range(10)] colors = Color(BLUE_A).range_to(BLUE_D, len(icons)) for icon, color in zip(icons, colors): icon.set_fill(color, opacity = 1) icons = VMobject(*icons) icons.arrange_submobjects(RIGHT) icons.to_edge(LEFT) icons.shift(UP) icons = icons.split() def rate_func_creator(offset): return lambda a : min(max(2*(a-offset), 0), 1) self.play(*[ FadeIn( icon, run_time = 5, rate_func = rate_func_creator(offset) ) for icon, offset in zip(icons, np.linspace(0, 0.5, len(icons))) ]) self.dither()
def construct(self): randy = Randolph().to_corner() bubble = randy.get_bubble(height = 3.8) text1 = TextMobject("Think of individual vectors as arrows") text2 = TextMobject("Think of sets of vectors as points") for text in text1, text2: text.to_edge(UP) single_vector = Vector([2, 1]) vector_group = VMobject(*[ Vector([x, 1]) for x in np.linspace(-2, 2, 5) ]) bubble.position_mobject_inside(single_vector) bubble.position_mobject_inside(vector_group) dots = VMobject(*[ Dot(v.get_end()) for v in vector_group.split() ]) self.add(randy) self.play( ApplyMethod(randy.change_mode, "pondering"), ShowCreation(bubble) ) self.play(FadeIn(text1)) self.play(ShowCreation(single_vector)) self.dither(3) self.play( Transform(text1, text2), Transform(single_vector, vector_group) ) self.remove(single_vector) self.add(vector_group) self.dither() self.play(Transform(vector_group, dots)) self.dither()
def construct(self): self.setup() self.plane.fade(0.3) self.add_unit_square(color=YELLOW_E, opacity=0.5) self.add_title(["The", "``determinant''", "of a transformation"], scale_factor=1) self.title.split()[1].split()[1].highlight(YELLOW) matrix_background, matrix, det_text = self.get_matrix() self.add_foreground_mobject(matrix_background, matrix) A = TexMobject("A") area_label = VMobject(A.copy(), A.copy(), A) area_label.move_to(self.square) det = np.linalg.det(self.t_matrix) if np.round(det) == det: det = int(det) area_label_target = VMobject(TexMobject(str(det)), TexMobject("\\cdot"), A.copy()) if det < 1 and det > 0: area_label_target.scale(det) area_label_target.arrange_submobjects(RIGHT, buff=0.1) self.add_moving_mobject(area_label, area_label_target) self.wait() self.apply_transposed_matrix(self.t_matrix) self.wait() det_mob_copy = area_label.split()[0].copy() new_det_mob = det_mob_copy.copy().scale_to_fit_height( det_text.split()[0].get_height()) new_det_mob.next_to(det_text, RIGHT, buff=0.2) new_det_mob.add_background_rectangle() det_mob_copy.add_background_rectangle(opacity=0) self.play(Write(det_text)) self.play(Transform(det_mob_copy, new_det_mob)) self.wait()
class NumberPlane(VMobject): CONFIG = { "color" : BLUE_D, "secondary_color" : BLUE_E, "axes_color" : WHITE, "secondary_stroke_width" : 1, "x_radius": None, "y_radius": None, "x_unit_size" : 1, "y_unit_size" : 1, "center_point" : ORIGIN, "x_line_frequency" : 1, "y_line_frequency" : 1, "secondary_line_ratio" : 1, "written_coordinate_height" : 0.2, "propagate_style_to_family" : False, } def generate_points(self): if self.x_radius is None: center_to_edge = (SPACE_WIDTH + abs(self.center_point[0])) self.x_radius = center_to_edge / self.x_unit_size if self.y_radius is None: center_to_edge = (SPACE_HEIGHT + abs(self.center_point[1])) self.y_radius = center_to_edge / self.y_unit_size self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ ( self.x_radius, self.x_line_frequency, self.y_radius*DOWN, self.y_radius*UP, RIGHT ), ( self.y_radius, self.y_line_frequency, self.x_radius*LEFT, self.x_radius*RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq/float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start+v*unit, end+v*unit) line2 = Line(start-v*unit, end-v*unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.secondary_lines, self.main_lines, self.axes) self.stretch(self.x_unit_size, 0) self.stretch(self.y_unit_size, 1) self.shift(self.center_point) #Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke( self.secondary_color, self.secondary_stroke_width ) return self def get_center_point(self): return self.coords_to_point(0, 0) def coords_to_point(self, x, y): x, y = np.array([x, y]) result = self.axes.get_center() result += x*self.get_x_unit_size()*RIGHT result += y*self.get_y_unit_size()*UP return result def point_to_coords(self, point): new_point = point - self.axes.get_center() x = new_point[0]/self.get_x_unit_size() y = new_point[1]/self.get_y_unit_size() return x, y def get_x_unit_size(self): return self.axes.get_width() / (2.0*self.x_radius) def get_y_unit_size(self): return self.axes.get_height() / (2.0*self.y_radius) def get_coordinate_labels(self, x_vals = None, y_vals = None): coordinate_labels = VGroup() if x_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius)+1) if y_vals == None: y_vals = range(-int(self.y_radius), int(self.y_radius)+1) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: if val == 0: continue num_pair[index] = val point = self.coords_to_point(*num_pair) num = TexMobject(str(val)) num.add_background_rectangle() num.scale_to_fit_height( self.written_coordinate_height ) num.next_to(point, DOWN+LEFT, buff = SMALL_BUFF) coordinate_labels.add(num) self.coordinate_labels = coordinate_labels return coordinate_labels def get_axes(self): return self.axes def get_axis_labels(self, x_label = "x", y_label = "y"): x_axis, y_axis = self.get_axes().split() quads = [ (x_axis, x_label, UP, RIGHT), (y_axis, y_label, RIGHT, UP), ] labels = VGroup() for axis, tex, vect, edge in quads: label = TexMobject(tex) label.add_background_rectangle() label.next_to(axis, vect) label.to_edge(edge) labels.add(label) self.axis_labels = labels return labels def add_coordinates(self, x_vals = None, y_vals = None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0]*RIGHT + coords[1]*UP arrow = Arrow(ORIGIN, coords, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 50): for mob in self.family_members_with_points(): num_anchors = mob.get_num_anchor_points() if num_inserted_anchor_points > num_anchors: mob.insert_n_anchor_points(num_inserted_anchor_points-num_anchors) mob.make_smooth() return self def apply_function(self, function, maintain_smoothness = True): VMobject.apply_function(self, function, maintain_smoothness = maintain_smoothness)
class TeacherStudentsScene(Scene): def setup(self): self.teacher = Mortimer() self.teacher.to_corner(DOWN + RIGHT) self.teacher.look(DOWN+LEFT) self.students = VMobject(*[ Randolph(color = c) for c in BLUE_D, BLUE_C, BLUE_E ]) self.students.arrange_submobjects(RIGHT) self.students.scale(0.8) self.students.to_corner(DOWN+LEFT) for pi_creature in self.get_everyone(): pi_creature.bubble = None self.add(*self.get_everyone()) def get_teacher(self): return self.teacher def get_students(self): return self.students.split() def get_everyone(self): return [self.get_teacher()] + self.get_students() def get_bubble_intro_animation(self, content, bubble_type, pi_creature, **bubble_kwargs): bubble = pi_creature.get_bubble(bubble_type, **bubble_kwargs) bubble.add_content(content) if pi_creature.bubble: content_intro_anims = [ Transform(pi_creature.bubble, bubble), Transform(pi_creature.bubble.content, bubble.content) ] else: content_intro_anims = [ FadeIn(bubble), Write(content), ] pi_creature.bubble = bubble return content_intro_anims def introduce_bubble(self, content, bubble_type, pi_creature, pi_creature_target_mode = None, added_anims = [], **bubble_kwargs): if isinstance(content, str): content = TextMobject(content) content_intro_anims = self.get_bubble_intro_animation( content, bubble_type, pi_creature, **bubble_kwargs ) if not pi_creature_target_mode: if bubble_type is "speech": pi_creature_target_mode = "speaking" else: pi_creature_target_mode = "pondering" for p in self.get_everyone(): if p.bubble and p is not pi_creature: added_anims += [ FadeOut(p.bubble), FadeOut(p.bubble.content) ] p.bubble = None added_anims.append(ApplyMethod(p.change_mode, "plain")) anims = added_anims + content_intro_anims + [ ApplyMethod( pi_creature.change_mode, pi_creature_target_mode, ), ] self.play(*anims) return pi_creature.bubble def teacher_says(self, content = "", **kwargs): return self.introduce_bubble( content, "speech", self.get_teacher(), **kwargs ) def student_says(self, content = "", student_index = 1, **kwargs): student = self.get_students()[student_index] return self.introduce_bubble(content, "speech", student, **kwargs) def teacher_thinks(self, content = "", **kwargs): return self.introduce_bubble( content, "thought", self.get_teacher(), **kwargs ) def student_thinks(self, content = "", student_index = 1, **kwargs): student = self.get_students()[student_index] return self.introduce_bubble(content, "thought", student, **kwargs) def random_blink(self, num_times = 1): for x in range(num_times): pi_creature = random.choice(self.get_everyone()) self.play(Blink(pi_creature)) self.dither()
class NumberPlane(VMobject): CONFIG = { "color": BLUE_D, "secondary_color": BLUE_E, "axes_color": WHITE, "secondary_stroke_width": 1, "x_radius": SPACE_WIDTH, "y_radius": SPACE_HEIGHT, "space_unit_to_x_unit": 1, "space_unit_to_y_unit": 1, "x_line_frequency": 1, "y_line_frequency": 1, "secondary_line_ratio": 1, "written_coordinate_height": 0.2, "written_coordinate_nudge": 0.1 * (DOWN + RIGHT), "num_pair_at_center": (0, 0), "propogate_style_to_family": False, } def generate_points(self): self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ (self.x_radius, self.x_line_frequency, self.y_radius * DOWN, self.y_radius * UP, RIGHT), ( self.y_radius, self.y_line_frequency, self.x_radius * LEFT, self.x_radius * RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq / float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start + v * unit, end + v * unit) line2 = Line(start - v * unit, end - v * unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.secondary_lines, self.main_lines, self.axes) self.stretch(self.space_unit_to_x_unit, 0) self.stretch(self.space_unit_to_y_unit, 1) #Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke(self.secondary_color, self.secondary_stroke_width) return self def get_center_point(self): return self.num_pair_to_point(self.num_pair_at_center) def num_pair_to_point(self, pair): pair = np.array(pair) + self.num_pair_at_center result = self.axes.get_center() result[0] += pair[0] * self.space_unit_to_x_unit result[1] += pair[1] * self.space_unit_to_y_unit return result def point_to_num_pair(self, point): new_point = point - self.get_center() center_x, center_y = self.num_pair_at_center x = center_x + point[0] / self.space_unit_to_x_unit y = center_y + point[1] / self.space_unit_to_y_unit return x, y def get_coordinate_labels(self, x_vals=None, y_vals=None): result = [] if x_vals == None and y_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius)) y_vals = range(-int(self.y_radius), int(self.y_radius)) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: num_pair[index] = val point = self.num_pair_to_point(num_pair) num = TexMobject(str(val)) num.scale_to_fit_height(self.written_coordinate_height) num.shift(point - num.get_corner(UP + LEFT), self.written_coordinate_nudge) result.append(num) return result def get_axes(self): return self.axes def get_axis_labels(self, x_label="x", y_label="y"): x_axis, y_axis = self.get_axes().split() x_label_mob = TexMobject(x_label) y_label_mob = TexMobject(y_label) x_label_mob.next_to(x_axis, DOWN) x_label_mob.to_edge(RIGHT) y_label_mob.next_to(y_axis, RIGHT) y_label_mob.to_edge(UP) return VMobject(x_label_mob, y_label_mob) def add_coordinates(self, x_vals=None, y_vals=None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0] * RIGHT + coords[1] * UP arrow = Arrow(ORIGIN, coords, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points=50): for mob in self.family_members_with_points(): num_anchors = mob.get_num_anchor_points() if num_inserted_anchor_points > num_anchors: mob.insert_n_anchor_points(num_inserted_anchor_points - num_anchors) mob.make_smooth() return self
class TeacherStudentsScene(Scene): def setup(self): self.teacher = Mortimer() self.teacher.to_corner(DOWN + RIGHT) self.teacher.look(DOWN+LEFT) self.students = VMobject(*[ Randolph(color = c) for c in BLUE_D, BLUE_C, BLUE_E ]) self.students.arrange_submobjects(RIGHT) self.students.scale(0.8) self.students.to_corner(DOWN+LEFT) self.students = self.students.split() for pi_creature in self.get_everyone(): pi_creature.bubble = None self.add(*self.get_everyone()) def get_teacher(self): return self.teacher def get_students(self): return self.students def get_everyone(self): return [self.get_teacher()] + self.get_students() def get_bubble_intro_animation(self, content, bubble_type, pi_creature, **bubble_kwargs): bubble = pi_creature.get_bubble(bubble_type, **bubble_kwargs) bubble.add_content(content) if pi_creature.bubble: content_intro_anims = [ Transform(pi_creature.bubble, bubble), Transform(pi_creature.bubble.content, bubble.content) ] else: content_intro_anims = [ FadeIn(bubble), Write(content), ] pi_creature.bubble = bubble return content_intro_anims def introduce_bubble(self, content, bubble_type, pi_creature, pi_creature_target_mode = None, added_anims = [], **bubble_kwargs): if all(map(lambda s : isinstance(s, str), content)): content = TextMobject(*content) elif len(content) == 1 and isinstance(content[0], TexMobject): content = content[0] else: raise Exception("Invalid content type") content_intro_anims = self.get_bubble_intro_animation( content, bubble_type, pi_creature, **bubble_kwargs ) if not pi_creature_target_mode: if bubble_type is "speech": pi_creature_target_mode = "speaking" else: pi_creature_target_mode = "pondering" for p in self.get_everyone(): if p.bubble and p is not pi_creature: added_anims += [ FadeOut(p.bubble), FadeOut(p.bubble.content) ] p.bubble = None added_anims.append(ApplyMethod(p.change_mode, "plain")) anims = added_anims + content_intro_anims + [ ApplyMethod( pi_creature.change_mode, pi_creature_target_mode, ), ] self.play(*anims) return pi_creature.bubble def teacher_says(self, *content, **kwargs): return self.introduce_bubble( content, "speech", self.get_teacher(), **kwargs ) def student_says(self, *content, **kwargs): student = self.get_students()[kwargs.get("student_index", 1)] return self.introduce_bubble(content, "speech", student, **kwargs) def teacher_thinks(self, *content, **kwargs): return self.introduce_bubble( content, "thought", self.get_teacher(), **kwargs ) def student_thinks(self, *content, **kwargs): student = self.get_students()[kwargs.get("student_index", 1)] return self.introduce_bubble(content, "thought", student, **kwargs) def random_blink(self, num_times = 1): for x in range(num_times): pi_creature = random.choice(self.get_everyone()) self.play(Blink(pi_creature)) self.dither() def change_student_modes(self, *modes): self.play(*[ ApplyMethod(pi.change_mode, mode) for pi, mode in zip(self.get_students(), modes) ]) def zoom_in_on_thought_bubble(self, radius = SPACE_HEIGHT+SPACE_WIDTH): bubble = None for pi in self.get_everyone(): if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble): bubble = pi.bubble break if bubble is None: raise Exception("No pi creatures have a thought bubble") vect = -bubble.get_bubble_center() def func(point): centered = point+vect return radius*centered/np.linalg.norm(centered) self.play(*[ ApplyPointwiseFunction(func, mob) for mob in self.get_mobjects() ])
class NumberPlane(VMobject): CONFIG = { "color" : BLUE_D, "secondary_color" : BLUE_E, "axes_color" : WHITE, "secondary_stroke_width" : 1, "x_radius": SPACE_WIDTH, "y_radius": SPACE_HEIGHT, "space_unit_to_x_unit" : 1, "space_unit_to_y_unit" : 1, "x_line_frequency" : 1, "y_line_frequency" : 1, "secondary_line_ratio" : 1, "written_coordinate_height" : 0.2, "written_coordinate_nudge" : 0.1*(DOWN+RIGHT), "num_pair_at_center" : (0, 0), "propogate_style_to_family" : False, "submobject_partial_creation_mode" : "smoothed_lagged_start", } def generate_points(self): self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ ( self.x_radius, self.x_line_frequency, self.y_radius*DOWN, self.y_radius*UP, RIGHT ), ( self.y_radius, self.y_line_frequency, self.x_radius*LEFT, self.x_radius*RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq/float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start+v*unit, end+v*unit) line2 = Line(start-v*unit, end-v*unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.axes, self.main_lines, self.secondary_lines) self.stretch(self.space_unit_to_x_unit, 0) self.stretch(self.space_unit_to_y_unit, 1) #Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke( self.secondary_color, self.secondary_stroke_width ) return self def get_center_point(self): return self.num_pair_to_point(self.num_pair_at_center) def num_pair_to_point(self, pair): pair = np.array(pair) + self.num_pair_at_center result = self.get_center() result[0] += pair[0]*self.space_unit_to_x_unit result[1] += pair[1]*self.space_unit_to_y_unit return result def point_to_num_pair(self, point): new_point = point-self.get_center() center_x, center_y = self.num_pair_at_center x = center_x + point[0]/self.space_unit_to_x_unit y = center_y + point[1]/self.space_unit_to_y_unit return x, y def get_coordinate_labels(self, x_vals = None, y_vals = None): result = [] if x_vals == None and y_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius)) y_vals = range(-int(self.y_radius), int(self.y_radius)) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: num_pair[index] = val point = self.num_pair_to_point(num_pair) num = TexMobject(str(val)) num.scale_to_fit_height( self.written_coordinate_height ) num.shift( point-num.get_corner(UP+LEFT), self.written_coordinate_nudge ) result.append(num) return result def get_axes(self): return self.axes def get_axis_labels(self, x_label = "x", y_label = "y"): x_axis, y_axis = self.get_axes().split() x_label_mob = TexMobject(x_label) y_label_mob = TexMobject(y_label) x_label_mob.next_to(x_axis, DOWN) x_label_mob.to_edge(RIGHT) y_label_mob.next_to(y_axis, RIGHT) y_label_mob.to_edge(UP) return VMobject(x_label_mob, y_label_mob) def add_coordinates(self, x_vals = None, y_vals = None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0]*RIGHT + coords[1]*UP arrow = Arrow(ORIGIN, coords, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 50): for mob in self.family_members_with_points(): mob.insert_n_anchor_points(num_inserted_anchor_points) mob.make_smooth() return self
class NumberPlane(VMobject): CONFIG = { "color" : BLUE_D, "secondary_color" : BLUE_E, "axes_color" : WHITE, "secondary_stroke_width" : 1, "x_radius": None, "y_radius": None, "x_unit_size" : 1, "y_unit_size" : 1, "center_point" : ORIGIN, "x_line_frequency" : 1, "y_line_frequency" : 1, "secondary_line_ratio" : 1, "written_coordinate_height" : 0.2, "propogate_style_to_family" : False, } def generate_points(self): if self.x_radius is None: center_to_edge = (SPACE_WIDTH + abs(self.center_point[0])) self.x_radius = center_to_edge / self.x_unit_size if self.y_radius is None: center_to_edge = (SPACE_HEIGHT + abs(self.center_point[1])) self.y_radius = center_to_edge / self.y_unit_size self.axes = VMobject() self.main_lines = VMobject() self.secondary_lines = VMobject() tuples = [ ( self.x_radius, self.x_line_frequency, self.y_radius*DOWN, self.y_radius*UP, RIGHT ), ( self.y_radius, self.y_line_frequency, self.x_radius*LEFT, self.x_radius*RIGHT, UP, ), ] for radius, freq, start, end, unit in tuples: main_range = np.arange(0, radius, freq) step = freq/float(freq + self.secondary_line_ratio) for v in np.arange(0, radius, step): line1 = Line(start+v*unit, end+v*unit) line2 = Line(start-v*unit, end-v*unit) if v == 0: self.axes.add(line1) elif v in main_range: self.main_lines.add(line1, line2) else: self.secondary_lines.add(line1, line2) self.add(self.secondary_lines, self.main_lines, self.axes) self.stretch(self.x_unit_size, 0) self.stretch(self.y_unit_size, 1) self.shift(self.center_point) #Put x_axis before y_axis y_axis, x_axis = self.axes.split() self.axes = VMobject(x_axis, y_axis) def init_colors(self): VMobject.init_colors(self) self.axes.set_stroke(self.axes_color, self.stroke_width) self.main_lines.set_stroke(self.color, self.stroke_width) self.secondary_lines.set_stroke( self.secondary_color, self.secondary_stroke_width ) return self def get_center_point(self): return self.coords_to_point(0, 0) def coords_to_point(self, x, y): x, y = np.array([x, y]) result = self.axes.get_center() result += x*self.get_x_unit_size()*RIGHT result += y*self.get_y_unit_size()*UP return result def point_to_coords(self, point): new_point = point - self.axes.get_center() x = new_point[0]/self.get_x_unit_size() y = new_point[1]/self.get_y_unit_size() return x, y def get_x_unit_size(self): return self.axes.get_width() / (2.0*self.x_radius) def get_y_unit_size(self): return self.axes.get_height() / (2.0*self.y_radius) def get_coordinate_labels(self, x_vals = None, y_vals = None): result = [] if x_vals == None and y_vals == None: x_vals = range(-int(self.x_radius), int(self.x_radius)) y_vals = range(-int(self.y_radius), int(self.y_radius)) for index, vals in enumerate([x_vals, y_vals]): num_pair = [0, 0] for val in vals: if val == 0: continue num_pair[index] = val point = self.coords_to_point(*num_pair) num = TexMobject(str(val)) num.add_background_rectangle() num.scale_to_fit_height( self.written_coordinate_height ) vect = DOWN if index == 0 else LEFT num.next_to(point, vect, buff = SMALL_BUFF) result.append(num) return result def get_axes(self): return self.axes def get_axis_labels(self, x_label = "x", y_label = "y"): x_axis, y_axis = self.get_axes().split() quads = [ (x_axis, x_label, UP, RIGHT), (y_axis, y_label, RIGHT, UP), ] labels = VGroup() for axis, tex, vect, edge in quads: label = TexMobject(tex) label.add_background_rectangle() label.next_to(axis, vect) label.to_edge(edge) labels.add(label) self.axis_labels = labels return labels def add_coordinates(self, x_vals = None, y_vals = None): self.add(*self.get_coordinate_labels(x_vals, y_vals)) return self def get_vector(self, coords, **kwargs): point = coords[0]*RIGHT + coords[1]*UP arrow = Arrow(ORIGIN, coords, **kwargs) return arrow def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 50): for mob in self.family_members_with_points(): num_anchors = mob.get_num_anchor_points() if num_inserted_anchor_points > num_anchors: mob.insert_n_anchor_points(num_inserted_anchor_points-num_anchors) mob.make_smooth() return self
def construct(self): vector = Vector([-2, 3]) plane = NumberPlane() axis_labels = plane.get_axis_labels() other_vectors = VMobject(*map(Vector, [ [1, 2], [2, -1], [4, 0] ])) colors = [GREEN_B, MAROON_B, PINK] for v, color in zip(other_vectors.split(), colors): v.highlight(color) shift_val = 4*RIGHT+DOWN dot = Dot(radius = 0.1) dot.highlight(RED) tail_word = TextMobject("Tail") tail_word.shift(0.5*DOWN+2.5*LEFT) line = Line(tail_word, dot) self.play(ShowCreation(vector, submobject_mode = "one_at_a_time")) self.dither(2) self.play( ShowCreation(plane, summobject_mode = "lagged_start"), Animation(vector) ) self.play(Write(axis_labels, run_time = 1)) self.dither() self.play( GrowFromCenter(dot), ShowCreation(line), Write(tail_word, run_time = 1) ) self.dither() self.play( FadeOut(tail_word), ApplyMethod(VMobject(dot, line).scale, 0.01) ) self.remove(tail_word, line, dot) self.dither() self.play(ApplyMethod( vector.shift, shift_val, path_arc = 3*np.pi/2, run_time = 3 )) self.play(ApplyMethod( vector.shift, -shift_val, rate_func = rush_into, run_time = 0.5 )) self.dither(3) self.play(ShowCreation( other_vectors, submobject_mode = "one_at_a_time", run_time = 3 )) self.dither(3) x_axis, y_axis = plane.get_axes().split() x_label = axis_labels.split()[0] x_axis = x_axis.copy() x_label = x_label.copy() everything = VMobject(*self.mobjects) self.play( FadeOut(everything), Animation(x_axis), Animation(x_label) )
class PiCreature(SVGMobject): CONFIG = { "color": BLUE_E, "stroke_width": 0, "fill_opacity": 1.0, "initial_scale_factor": 0.01, "corner_scale_factor": 0.75, "flip_at_start": False, "is_looking_direction_purposeful": False, "start_corner": None, } def __init__(self, mode="plain", **kwargs): self.parts_named = False svg_file = os.path.join(PI_CREATURE_DIR, "PiCreatures_%s.svg" % mode) digest_config(self, kwargs, locals()) SVGMobject.__init__(self, svg_file, **kwargs) self.init_colors() if self.flip_at_start: self.flip() if self.start_corner is not None: self.to_corner(self.start_corner) def name_parts(self): self.mouth = self.submobjects[MOUTH_INDEX] self.body = self.submobjects[BODY_INDEX] self.pupils = VMobject(*[ self.submobjects[LEFT_PUPIL_INDEX], self.submobjects[RIGHT_PUPIL_INDEX] ]) self.eyes = VMobject(*[ self.submobjects[LEFT_EYE_INDEX], self.submobjects[RIGHT_EYE_INDEX] ]) self.submobjects = [] self.add(self.body, self.mouth, self.eyes, self.pupils) self.parts_named = True def init_colors(self): self.set_stroke(color=BLACK, width=self.stroke_width) if not self.parts_named: self.name_parts() self.mouth.set_fill(BLACK, opacity=1) self.body.set_fill(self.color, opacity=1) self.pupils.set_fill(BLACK, opacity=1) self.eyes.set_fill(WHITE, opacity=1) return self def highlight(self, color): self.body.set_fill(color) return self def change_mode(self, mode): curr_eye_center = self.eyes.get_center() curr_height = self.get_height() should_be_flipped = self.is_flipped() should_look = hasattr(self, "purposeful_looking_direction") if should_look: looking_direction = self.purposeful_looking_direction self.__init__(mode) self.scale_to_fit_height(curr_height) self.shift(curr_eye_center - self.eyes.get_center()) if should_be_flipped ^ self.is_flipped(): self.flip() if should_look: self.look(looking_direction) return self def look(self, direction): direction = direction / np.linalg.norm(direction) self.purposeful_looking_direction = direction for pupil, eye in zip(self.pupils.split(), self.eyes.split()): pupil_radius = pupil.get_width() / 2. eye_radius = eye.get_width() / 2. pupil.move_to(eye) if direction[1] < 0: pupil.shift(pupil_radius * DOWN / 3) pupil.shift(direction * (eye_radius - pupil_radius)) bottom_diff = eye.get_bottom()[1] - pupil.get_bottom()[1] if bottom_diff > 0: pupil.shift(bottom_diff * UP) return self def look_at(self, point_or_mobject): if isinstance(point_or_mobject, Mobject): point = point_or_mobject.get_center() else: point = point_or_mobject self.look(point - self.eyes.get_center()) return self def get_looking_direction(self): return np.sign( np.round(self.pupils.get_center() - self.eyes.get_center(), decimals=2)) def is_flipped(self): return self.eyes.submobjects[0].get_center()[0] > \ self.eyes.submobjects[1].get_center()[0] def blink(self): eye_bottom_y = self.eyes.get_bottom()[1] for mob in self.eyes, self.pupils: mob.apply_function(lambda p: [p[0], eye_bottom_y, p[2]]) return self def to_corner(self, vect=None): if vect is not None: SVGMobject.to_corner(self, vect) else: self.scale(self.corner_scale_factor) self.to_corner(DOWN + LEFT) return self def get_bubble(self, bubble_type="thought", **kwargs): if bubble_type == "thought": bubble = ThoughtBubble(**kwargs) elif bubble_type == "speech": bubble = SpeechBubble(**kwargs) else: raise Exception("%s is an invalid bubble type" % bubble_type) bubble.pin_to(self) return bubble
class PiCreature(SVGMobject): CONFIG = { "color" : BLUE_E, "stroke_width" : 0, "fill_opacity" : 1.0, "initial_scale_factor" : 0.01, "corner_scale_factor" : 0.75, "flip_at_start" : False, "is_looking_direction_purposeful" : False, } def __init__(self, mode = "plain", **kwargs): self.parts_named = False svg_file = os.path.join( PI_CREATURE_DIR, "PiCreatures_%s.svg"%mode ) digest_config(self, kwargs, locals()) SVGMobject.__init__(self, svg_file, **kwargs) self.init_colors() if self.flip_at_start: self.flip() def name_parts(self): self.mouth = self.submobjects[MOUTH_INDEX] self.body = self.submobjects[BODY_INDEX] self.pupils = VMobject(*[ self.submobjects[LEFT_PUPIL_INDEX], self.submobjects[RIGHT_PUPIL_INDEX] ]) self.eyes = VMobject(*[ self.submobjects[LEFT_EYE_INDEX], self.submobjects[RIGHT_EYE_INDEX] ]) self.submobjects = [] self.add(self.body, self.mouth, self.eyes, self.pupils) self.parts_named = True def init_colors(self): self.set_stroke(color = BLACK, width = self.stroke_width) if not self.parts_named: self.name_parts() self.mouth.set_fill(BLACK, opacity = 1) self.body.set_fill(self.color, opacity = 1) self.pupils.set_fill(BLACK, opacity = 1) self.eyes.set_fill(WHITE, opacity = 1) return self def highlight(self, color): self.body.set_fill(color) return self def move_to(self, destination): self.shift(destination-self.get_bottom()) return self def change_mode(self, mode): curr_center = self.get_center() curr_height = self.get_height() should_be_flipped = self.is_flipped() if self.is_looking_direction_purposeful: looking_direction = self.get_looking_direction() self.__init__(mode) self.scale_to_fit_height(curr_height) self.shift(curr_center) if should_be_flipped ^ self.is_flipped(): self.flip() if self.is_looking_direction_purposeful: self.look(looking_direction) return self def look(self, direction): self.is_looking_direction_purposeful = True x, y = direction[:2] for pupil, eye in zip(self.pupils.split(), self.eyes.split()): pupil.move_to(eye, aligned_edge = direction) #Some hacky nudging is required here if y > 0 and x != 0: # Look up and to a side nudge_size = pupil.get_height()/4. if x > 0: nudge = nudge_size*(DOWN+LEFT) else: nudge = nudge_size*(DOWN+RIGHT) pupil.shift(nudge) elif y < 0: nudge_size = pupil.get_height()/8. pupil.shift(nudge_size*UP) return self def get_looking_direction(self): return np.sign(np.round( self.pupils.get_center() - self.eyes.get_center(), decimals = 2 )) def is_flipped(self): return self.eyes.submobjects[0].get_center()[0] > \ self.eyes.submobjects[1].get_center()[0] def blink(self): eye_bottom_y = self.eyes.get_bottom()[1] for mob in self.eyes, self.pupils: mob.apply_function( lambda p : [p[0], eye_bottom_y, p[2]] ) return self def to_corner(self, vect = None): if vect is not None: SVGMobject.to_corner(self, vect) else: self.scale(self.corner_scale_factor) self.to_corner(DOWN+LEFT) return self def get_bubble(self, bubble_type = "thought", **kwargs): if bubble_type == "thought": bubble = ThoughtBubble(**kwargs) elif bubble_type == "speech": bubble = SpeechBubble(**kwargs) else: raise Exception("%s is an invalid bubble type"%bubble_type) bubble.pin_to(self) return bubble