From b610dbb37ca67da34e5053808eae6e57d1896454 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 2 Apr 2018 17:49:04 -0700 Subject: [PATCH 1/6] Setup determinant_puzzle.py in eola2 --- active_projects/eola2/__init__.py | 0 active_projects/eola2/determinant_puzzle.py | 12 ++++++++++++ scene/vector_space_scene.py | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 active_projects/eola2/__init__.py create mode 100644 active_projects/eola2/determinant_puzzle.py diff --git a/active_projects/eola2/__init__.py b/active_projects/eola2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/active_projects/eola2/determinant_puzzle.py b/active_projects/eola2/determinant_puzzle.py new file mode 100644 index 00000000..59a3fdf4 --- /dev/null +++ b/active_projects/eola2/determinant_puzzle.py @@ -0,0 +1,12 @@ +from big_ol_pile_of_manim_imports import * + +class WorkOutNumerically(Scene): + def construct(self): + pass + + +class SuccessiveLinearTransformations(LinearTransformationScene): + def construct(self): + self.apply_transposed_matrix([[0, 1], [1, 0]]) + self.apply_transposed_matrix([[1, 1], [0, 1]]) + self.wait() diff --git a/scene/vector_space_scene.py b/scene/vector_space_scene.py index 157561e1..9e418b35 100644 --- a/scene/vector_space_scene.py +++ b/scene/vector_space_scene.py @@ -320,7 +320,7 @@ class LinearTransformationScene(VectorScene): def add_foreground_mobject(self, *mobjects): self.add_special_mobjects(self.foreground_mobjects, *mobjects) - + def add_transformable_mobject(self, *mobjects): self.add_special_mobjects(self.transformable_mobjects, *mobjects) From b82a713d16b1649200f544e522cbda1788f01d5e Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 4 Apr 2018 13:34:31 -0700 Subject: [PATCH 2/6] Tiny change to imports --- extract_scene.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extract_scene.py b/extract_scene.py index 32766b46..a866b4e8 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -1,7 +1,6 @@ #!/usr/bin/env python2 import sys -# import getopt import argparse import imp import imp @@ -11,8 +10,9 @@ import os import subprocess as sp import traceback -from camera.camera import Camera from constants import * + +from camera.camera import Camera from scene.scene import Scene from utils.sounds import play_error_sound from utils.sounds import play_finish_sound From bc229f4f29b2aa8c281fc6ebdcfad259fdc4f893 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 6 Apr 2018 13:06:23 -0700 Subject: [PATCH 3/6] Deriminant puzzle solution animations --- active_projects/eola2/determinant_puzzle.py | 280 +++++++++++++++++++- 1 file changed, 277 insertions(+), 3 deletions(-) diff --git a/active_projects/eola2/determinant_puzzle.py b/active_projects/eola2/determinant_puzzle.py index 59a3fdf4..be24c390 100644 --- a/active_projects/eola2/determinant_puzzle.py +++ b/active_projects/eola2/determinant_puzzle.py @@ -1,12 +1,286 @@ from big_ol_pile_of_manim_imports import * + class WorkOutNumerically(Scene): + CONFIG = { + "M1_COLOR": TEAL, + "M2_COLOR": PINK, + } + def construct(self): - pass + self.add_question() + self.add_example() + self.compute_rhs() + self.compute_lhs() + + def add_question(self): + equation = self.original_equation = TexMobject( + "\\det(", "M_1", "M_2", ")", "=", + "\\det(", "M_1", ")", + "\\det(", "M_2", ")", + ) + equation.set_color_by_tex_to_color_map({ + "M_1": self.M1_COLOR, + "M_2": self.M2_COLOR, + }) + challenge = TextMobject("Explain in one sentence") + challenge.set_color(YELLOW) + group = VGroup(challenge, equation) + group.arrange_submobjects(DOWN) + group.to_edge(UP) + + self.add(equation) + self.play(Write(challenge)) + self.wait() + + def add_example(self): + M1 = self.M1 = Matrix([[2, -1], [1, 1]]) + M1.set_color(self.M1_COLOR) + self.M1_copy = M1.copy() + M2 = self.M2 = Matrix([[-1, 4], [1, 1]]) + M2.set_color(self.M2_COLOR) + self.M2_copy = M2.copy() + eq_parts = TexMobject( + "\\det", "\\big(", "\\big)", "=", + "\\det", "\\big(", "\\big)", + "\\det", "\\big(", "\\big)", + ) + for part in eq_parts.get_parts_by_tex("\\big"): + part.scale(2) + part.stretch(1.5, 1) + i1, i2, i3 = [ + eq_parts.index_of_part(part) + 1 + for part in eq_parts.get_parts_by_tex("\\big(") + ] + equation = self.equation_with_numbers = VGroup(*it.chain( + eq_parts[:i1], [M1, M2], + eq_parts[i1:i2], [self.M1_copy], + eq_parts[i2:i3], [self.M2_copy], + eq_parts[i3:], + )) + equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF) + eq_parts.get_part_by_tex("=").shift(0.2 * SMALL_BUFF * DOWN) + equation.scale_to_fit_width(FRAME_WIDTH - 2 * LARGE_BUFF) + equation.next_to(self.original_equation, DOWN, MED_LARGE_BUFF) + + self.play(LaggedStart(FadeIn, equation)) + + def compute_rhs(self): + M1, M2 = self.M1_copy, self.M2_copy + + line1 = VGroup( + TexMobject( + "\\big(", "2", "\\cdot", "2", "-", + "(-1)", "\\cdot", "1", "\\big)" + ), + TexMobject( + "\\big(", "-1", "\\cdot", "1", "-", + "4", "\\cdot", "1", "\\big)" + ), + ) + line1.arrange_submobjects(RIGHT, buff=SMALL_BUFF) + line1[0].set_color(self.M1_COLOR) + line1[1].set_color(self.M2_COLOR) + indices = [1, 3, 5, 7] + + line2 = TexMobject("(3)", "(-5)") + line2.match_style(line1) + line3 = TexMobject("-15") + arrows = [TexMobject("\\downarrow") for x in range(2)] + lines = VGroup(line1, arrows[0], line2, arrows[1], line3) + lines.arrange_submobjects(DOWN, buff=MED_SMALL_BUFF) + lines.next_to(self.equation_with_numbers, DOWN, buff=MED_LARGE_BUFF) + lines.to_edge(RIGHT) + + for matrix, det in zip([M1, M2], line1): + numbers = VGroup(*[det[i] for i in indices]) + numbers_iter = iter(numbers) + non_numbers = VGroup(*filter( + lambda m: m not in numbers, + det + )) + matrix_numbers = VGroup(*[ + matrix.mob_matrix[i][j].copy() + for i, j in (0, 0), (1, 1), (0, 1), (1, 0) + ]) + self.play( + LaggedStart(FadeIn, non_numbers, run_time=1), + LaggedStart( + ReplacementTransform, + matrix_numbers, + lambda m: (m, numbers_iter.next()), + path_arc=TAU / 6 + ), + ) + self.play(LaggedStart(FadeIn, lines[1:], run_time=3)) + + def compute_lhs(self): + matrix = Matrix([[-3, 7], [0, 5]]) + matrix.set_color(BLUE) + matrix.scale(0.8) + empty_det_tex = TexMobject("\\det", "\\big(", "\\big)") + empty_det_tex[1:].scale(1.5) + empty_det_tex[1:].match_height(matrix, stretch=True) + det_tex = VGroup(empty_det_tex[:2], matrix, *empty_det_tex[2:]) + det_tex.arrange_submobjects(RIGHT, buff=SMALL_BUFF) + + group = VGroup( + det_tex, + TexMobject("\\downarrow"), + TexMobject("(-3)(5) - (7)(0)").scale(0.8), + TexMobject("\\downarrow"), + TexMobject("-15"), + ) + group.arrange_submobjects(DOWN, buff=2 * SMALL_BUFF) + # group.scale_to_fit_height(0.4*FRAME_HEIGHT) + group.next_to(self.equation_with_numbers, DOWN) + group.shift(FRAME_WIDTH * LEFT / 4) + + self.play(FadeIn(empty_det_tex)) + self.play(*[ + ReplacementTransform(M.copy(), matrix) + for M in self.M1, self.M2 + ]) + self.play(LaggedStart(FadeIn, group[1:])) + self.wait() + + +class LetsGoInOneSentence(TeacherStudentsScene): + def construct(self): + self.teacher_says( + "Here we go, \\\\", "one sentence!" + ) + self.change_all_student_modes("hooray") + self.teacher_says( + "Or three...", "", + target_mode="guilty" + ) + self.change_all_student_modes("sassy") + self.wait(4) class SuccessiveLinearTransformations(LinearTransformationScene): + CONFIG = { + "matrix_2": [[3, -1], [0, 1]], + "matrix_1": [[2, 3], [-1. / 3, 2]], + } + def construct(self): - self.apply_transposed_matrix([[0, 1], [1, 0]]) - self.apply_transposed_matrix([[1, 1], [0, 1]]) + self.create_product_and_inverse() + self.scale_area_successively() + self.apply_transformations_successively() + self.scale_area_successively( + "\\det(M_2)", "\\det(M_1)", "\\det(M_1 M_2)", + reset=False + ) + # self.show_det_as_scaling_factor() + + def create_product_and_inverse(self): + self.matrix_product = np.dot(self.matrix_1, self.matrix_2) + self.matrix_product_inverse = np.linalg.inv(self.matrix_product) + + def scale_area_successively(self, tex2="3", tex1="5", tex_prod="15", reset=True): + self.add_unit_square() + t1 = "$%s \\, \\cdot $" % tex1 + t2 = "$%s \\, \\cdot $" % tex2 + t3 = "Area" + areas = VGroup( + TextMobject("", "", t3), + TextMobject("", t2, t3), + TextMobject(t1, t2, t3), + ) + areas.scale(0.8) + areas.move_to(self.square) + area = areas[0] + self.add_moving_mobject(area, areas[1]) + + self.play( + FadeIn(self.square), + Write(area), + Animation(self.basis_vectors) + ) + self.apply_matrix(self.matrix_2) self.wait() + self.add_moving_mobject(area, areas[2]) + self.apply_matrix(self.matrix_1) + self.wait() + + product = VGroup(area[:2]) + brace = Brace(product, DOWN, buff=SMALL_BUFF) + brace_tex = brace.get_tex(tex_prod, buff=SMALL_BUFF) + brace_tex.scale(0.8, about_edge=UP) + + self.play( + GrowFromCenter(brace), + Write(brace_tex) + ) + self.wait() + if reset: + self.play( + FadeOut(VGroup(self.square, area, brace, brace_tex)), + Animation(self.plane), + Animation(self.basis_vectors) + ) + self.transformable_mobjects.remove(self.square) + self.moving_mobjects = [] + self.reset_plane() + self.wait() + + def apply_transformations_successively(self): + M1, M2, all_space = expression = TexMobject( + "M_1", "M_2", "\\text{(All 2d space)}" + ) + expression.set_color_by_tex_to_color_map({ + "M_1": TEAL, + "M_2": PINK, + }) + expression.shift(FRAME_WIDTH * LEFT / 4) + expression.to_edge(UP) + for part in expression: + part.add_background_rectangle() + part.background_rectangle.stretch(1.05, 0) + M1.save_state() + M1.move_to(ORIGIN) + M1.fade(1) + + # Apply one after the other + self.play( + FocusOn(M2, run_time=1), + FadeIn(VGroup(M2, all_space)) + ) + self.add_foreground_mobjects(M2, all_space) + self.apply_matrix(self.matrix_2) + self.wait() + self.play(M1.restore) + self.add_foreground_mobjects(M1) + self.apply_matrix(self.matrix_1) + self.wait() + + # Show full composition + rp, lp = parens = TexMobject("()") + matrices = VGroup(M1, M2) + matrices.generate_target() + parens.match_height(matrices) + lp.move_to(matrices, RIGHT) + matrices.target.next_to(lp, LEFT, SMALL_BUFF) + rp.next_to(matrices.target, LEFT, SMALL_BUFF) + + self.reset_plane() + self.play( + MoveToTarget(matrices), + *map(GrowFromCenter, parens) + ) + self.apply_matrix(self.matrix_product) + self.wait() + self.reset_plane() + + def show_det_as_scaling_factor(self): + pass + + ### + + def reset_plane(self): + plane_and_bases = VGroup(self.plane, self.basis_vectors) + self.play(FadeOut(plane_and_bases)) + self.apply_matrix(self.matrix_product_inverse, run_time=0) + self.play(FadeIn(plane_and_bases)) From 96d261840d5c658c0f830f7535f1cf4b4f0e8133 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 6 Apr 2018 13:07:02 -0700 Subject: [PATCH 4/6] Added apply_matrix_transformation to LinearTransformationScene --- scene/vector_space_scene.py | 218 ++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 110 deletions(-) diff --git a/scene/vector_space_scene.py b/scene/vector_space_scene.py index 9e418b35..cd42b2e5 100644 --- a/scene/vector_space_scene.py +++ b/scene/vector_space_scene.py @@ -36,25 +36,27 @@ X_COLOR = GREEN_C Y_COLOR = RED_C Z_COLOR = BLUE_D + class VectorScene(Scene): CONFIG = { - "basis_vector_stroke_width" : 6 + "basis_vector_stroke_width": 6 } - def add_plane(self, animate = False, **kwargs): + + def add_plane(self, animate=False, **kwargs): plane = NumberPlane(**kwargs) if animate: - self.play(ShowCreation(plane, submobject_mode = "lagged_start")) + self.play(ShowCreation(plane, submobject_mode="lagged_start")) self.add(plane) return plane - def add_axes(self, animate = False, color = WHITE, **kwargs): - axes = Axes(color = color, tick_frequency = 1) + def add_axes(self, animate=False, color=WHITE, **kwargs): + axes = Axes(color=color, tick_frequency=1) if animate: - self.play(ShowCreation(axes, submobject_mode = "one_at_a_time")) + self.play(ShowCreation(axes, submobject_mode="one_at_a_time")) self.add(axes) return axes - def lock_in_faded_grid(self, dimness = 0.7, axes_dimness = 0.5): + def lock_in_faded_grid(self, dimness=0.7, axes_dimness=0.5): plane = self.add_plane() axes = plane.get_axes() plane.fade(dimness) @@ -63,9 +65,9 @@ class VectorScene(Scene): self.add(axes) self.freeze_background() - def add_vector(self, vector, color = YELLOW, animate = True, **kwargs): + def add_vector(self, vector, color=YELLOW, animate=True, **kwargs): if not isinstance(vector, Arrow): - vector = Vector(vector, color = color, **kwargs) + vector = Vector(vector, color=color, **kwargs) if animate: self.play(ShowCreation(vector)) self.add(vector) @@ -76,40 +78,41 @@ class VectorScene(Scene): self.play(Write(coords)) return coords - def get_basis_vectors(self): - return [ + def get_basis_vectors(self, i_hat_color=X_COLOR, j_hat_color=Y_COLOR): + return VGroup(*[ Vector( - vect, color = color, - stroke_width = self.basis_vector_stroke_width + vect, + color=color, + stroke_width=self.basis_vector_stroke_width ) for vect, color in [ - ([1, 0], X_COLOR), - ([0, 1], Y_COLOR) + ([1, 0], i_hat_color), + ([0, 1], j_hat_color) ] - ] + ]) def get_basis_vector_labels(self, **kwargs): i_hat, j_hat = self.get_basis_vectors() return VGroup(*[ self.get_vector_label( - vect, label, color = color, - label_scale_factor = 1, + vect, label, color=color, + label_scale_factor=1, **kwargs ) - for vect, label , color in [ + for vect, label, color in [ (i_hat, "\\hat{\\imath}", X_COLOR), (j_hat, "\\hat{\\jmath}", Y_COLOR), ] ]) - def get_vector_label(self, vector, label, - direction = "left", - rotate = False, - color = None, - label_scale_factor = VECTOR_LABEL_SCALE_FACTOR): + def get_vector_label(self, vector, label, + direction="left", + rotate=False, + color=None, + label_scale_factor=VECTOR_LABEL_SCALE_FACTOR): if not isinstance(label, TexMobject): if len(label) == 1: - label = "\\vec{\\textbf{%s}}"%label + label = "\\vec{\\textbf{%s}}" % label label = TexMobject(label) if color is None: color = vector.get_color() @@ -119,53 +122,53 @@ class VectorScene(Scene): angle = vector.get_angle() if not rotate: - label.rotate(-angle, about_point = ORIGIN) + label.rotate(-angle, about_point=ORIGIN) if direction is "left": - label.shift(-label.get_bottom() + 0.1*UP) + label.shift(-label.get_bottom() + 0.1 * UP) else: - label.shift(-label.get_top() + 0.1*DOWN) - label.rotate(angle, about_point = ORIGIN) - label.shift((vector.get_end() - vector.get_start())/2) + label.shift(-label.get_top() + 0.1 * DOWN) + label.rotate(angle, about_point=ORIGIN) + label.shift((vector.get_end() - vector.get_start()) / 2) return label - def label_vector(self, vector, label, animate = True, **kwargs): + def label_vector(self, vector, label, animate=True, **kwargs): label = self.get_vector_label(vector, label, **kwargs) if animate: - self.play(Write(label, run_time = 1)) + self.play(Write(label, run_time=1)) self.add(label) return label def position_x_coordinate(self, x_coord, x_line, vector): - x_coord.next_to(x_line, -np.sign(vector[1])*UP) + x_coord.next_to(x_line, -np.sign(vector[1]) * UP) x_coord.set_color(X_COLOR) return x_coord def position_y_coordinate(self, y_coord, y_line, vector): - y_coord.next_to(y_line, np.sign(vector[0])*RIGHT) + y_coord.next_to(y_line, np.sign(vector[0]) * RIGHT) y_coord.set_color(Y_COLOR) return y_coord - def coords_to_vector(self, vector, coords_start = 2*RIGHT+2*UP, clean_up = True): + def coords_to_vector(self, vector, coords_start=2 * RIGHT + 2 * UP, clean_up=True): starting_mobjects = list(self.mobjects) array = Matrix(vector) array.shift(coords_start) arrow = Vector(vector) - x_line = Line(ORIGIN, vector[0]*RIGHT) + x_line = Line(ORIGIN, vector[0] * RIGHT) y_line = Line(x_line.get_end(), arrow.get_end()) x_line.set_color(X_COLOR) y_line.set_color(Y_COLOR) x_coord, y_coord = array.get_mob_matrix().flatten() - self.play(Write(array, run_time = 1)) + self.play(Write(array, run_time=1)) self.wait() self.play(ApplyFunction( - lambda x : self.position_x_coordinate(x, x_line, vector), + lambda x: self.position_x_coordinate(x, x_line, vector), x_coord )) self.play(ShowCreation(x_line)) self.play( ApplyFunction( - lambda y : self.position_y_coordinate(y, y_line, vector), + lambda y: self.position_y_coordinate(y, y_line, vector), y_coord ), FadeOut(array.get_brackets()) @@ -178,7 +181,7 @@ class VectorScene(Scene): self.clear() self.add(*starting_mobjects) - def vector_to_coords(self, vector, integer_labels = True, clean_up = True): + def vector_to_coords(self, vector, integer_labels=True, clean_up=True): starting_mobjects = list(self.mobjects) show_creation = False if isinstance(vector, Arrow): @@ -187,8 +190,8 @@ class VectorScene(Scene): else: arrow = Vector(vector) show_creation = True - array = vector_coordinate_label(arrow, integer_labels = integer_labels) - x_line = Line(ORIGIN, vector[0]*RIGHT) + array = vector_coordinate_label(arrow, integer_labels=integer_labels) + x_line = Line(ORIGIN, vector[0] * RIGHT) y_line = Line(x_line.get_end(), arrow.get_end()) x_line.set_color(X_COLOR) y_line.set_color(Y_COLOR) @@ -206,18 +209,18 @@ class VectorScene(Scene): self.play( ShowCreation(x_line), Write(x_coord_start), - run_time = 1 + run_time=1 ) self.play( ShowCreation(y_line), Write(y_coord_start), - run_time = 1 + run_time=1 ) self.wait() self.play( - Transform(x_coord_start, x_coord, submobject_mode = "all_at_once"), - Transform(y_coord_start, y_coord, submobject_mode = "all_at_once"), - Write(brackets, run_time = 1), + Transform(x_coord_start, x_coord, submobject_mode="all_at_once"), + Transform(y_coord_start, y_coord, submobject_mode="all_at_once"), + Write(brackets, run_time=1), ) self.wait() @@ -236,53 +239,56 @@ class VectorScene(Scene): x_max = int(FRAME_X_RADIUS + abs(vector[0])) y_max = int(FRAME_Y_RADIUS + abs(vector[1])) dots = VMobject(*[ - Dot(x*RIGHT + y*UP) + Dot(x * RIGHT + y * UP) for x in range(-x_max, x_max) for y in range(-y_max, y_max) ]) - dots.set_fill(BLACK, opacity = 0) - dots_halfway = dots.copy().shift(vector/2).set_fill(WHITE, 1) + dots.set_fill(BLACK, opacity=0) + dots_halfway = dots.copy().shift(vector / 2).set_fill(WHITE, 1) dots_end = dots.copy().shift(vector) self.play(Transform( - dots, dots_halfway, rate_func = rush_into + dots, dots_halfway, rate_func=rush_into )) self.play(Transform( - dots, dots_end, rate_func = rush_from + dots, dots_end, rate_func=rush_from )) self.remove(dots) + class LinearTransformationScene(VectorScene): CONFIG = { - "include_background_plane" : True, - "include_foreground_plane" : True, - "foreground_plane_kwargs" : { - "x_radius" : FRAME_WIDTH, - "y_radius" : FRAME_HEIGHT, - "secondary_line_ratio" : 0 + "include_background_plane": True, + "include_foreground_plane": True, + "foreground_plane_kwargs": { + "x_radius": FRAME_WIDTH, + "y_radius": FRAME_HEIGHT, + "secondary_line_ratio": 0 }, - "background_plane_kwargs" : { - "color" : GREY, - "secondary_color" : DARK_GREY, - "axes_color" : GREY, - "stroke_width" : 2, + "background_plane_kwargs": { + "color": GREY, + "secondary_color": DARK_GREY, + "axes_color": GREY, + "stroke_width": 2, }, - "show_coordinates" : False, - "show_basis_vectors" : True, - "i_hat_color" : X_COLOR, - "j_hat_color" : Y_COLOR, - "leave_ghost_vectors" : False, - "t_matrix" : [[3, 0], [1, 2]], + "show_coordinates": False, + "show_basis_vectors": True, + "basis_vector_stroke_width": 6, + "i_hat_color": X_COLOR, + "j_hat_color": Y_COLOR, + "leave_ghost_vectors": False, + "t_matrix": [[3, 0], [1, 2]], } + def setup(self): if hasattr(self, "has_already_setup"): return self.has_already_setup = True - ##^This is to not break all the old Scenes + # ^This is to not break all the old Scenes self.background_mobjects = [] - self.foreground_mobjects = [] + self.foreground_mobjects = [] self.transformable_mobjects = [] - self.moving_vectors = [] + self.moving_vectors = [] self.transformable_labels = [] self.moving_mobjects = [] @@ -293,21 +299,18 @@ class LinearTransformationScene(VectorScene): if self.show_coordinates: self.background_plane.add_coordinates() - if self.include_background_plane: + if self.include_background_plane: self.add_background_mobject(self.background_plane) if self.include_foreground_plane: self.plane = NumberPlane(**self.foreground_plane_kwargs) self.add_transformable_mobject(self.plane) if self.show_basis_vectors: - self.i_hat, self.j_hat = [ - self.add_vector( - coords, color, animate = False, stroke_width = 6 - ) - for coords, color in [ - ((1, 0), self.i_hat_color), - ((0, 1), self.j_hat_color), - ] - ] + self.basis_vectors = self.get_basis_vectors( + i_hat_color=self.i_hat_color, + j_hat_color=self.j_hat_color, + ) + self.moving_vectors += list(self.basis_vectors) + self.i_hat, self.j_hat = self.basis_vectors def add_special_mobjects(self, mob_list, *mobs_to_add): for mobject in mobs_to_add: @@ -324,13 +327,13 @@ class LinearTransformationScene(VectorScene): def add_transformable_mobject(self, *mobjects): self.add_special_mobjects(self.transformable_mobjects, *mobjects) - def add_moving_mobject(self, mobject, target_mobject = None): + def add_moving_mobject(self, mobject, target_mobject=None): mobject.target = target_mobject self.add_special_mobjects(self.moving_mobjects, mobject) - def add_unit_square(self, color = YELLOW, opacity = 0.3, animate = False): - square = Square(color = color, side_length = 1) - square.shift(-square.get_corner(DOWN+LEFT)) + def add_unit_square(self, color=YELLOW, opacity=0.3, animate=False): + square = Square(color=color, side_length=1) + square.shift(-square.get_corner(DOWN + LEFT)) if animate: added_anims = map(Animation, self.moving_vectors) self.play(ShowCreation(square), *added_anims) @@ -338,13 +341,13 @@ class LinearTransformationScene(VectorScene): else: square.set_fill(color, opacity) self.add_transformable_mobject(square) - self.bring_to_front(*self.moving_vectors) + self.bring_to_front(*self.moving_vectors) self.square = square return self - def add_vector(self, vector, color = YELLOW, **kwargs): + def add_vector(self, vector, color=YELLOW, **kwargs): vector = VectorScene.add_vector( - self, vector, color = color, **kwargs + self, vector, color=color, **kwargs ) self.moving_vectors.append(vector) return vector @@ -354,12 +357,12 @@ class LinearTransformationScene(VectorScene): self.add_foreground_mobject(coords) return coords - def add_transformable_label(self, vector, label, new_label = None, **kwargs): + def add_transformable_label(self, vector, label, new_label=None, **kwargs): label_mob = self.label_vector(vector, label, **kwargs) if new_label: label_mob.target_text = new_label else: - label_mob.target_text = "L(%s)"%label_mob.get_tex_string() + label_mob.target_text = "L(%s)" % label_mob.get_tex_string() label_mob.vector = vector label_mob.kwargs = kwargs if "animate" in label_mob.kwargs: @@ -367,7 +370,7 @@ class LinearTransformationScene(VectorScene): self.transformable_labels.append(label_mob) return label_mob - def add_title(self, title, scale_factor = 1.5, animate = False): + def add_title(self, title, scale_factor=1.5, animate=False): if not isinstance(title, Mobject): title = TextMobject(title).scale(scale_factor) title.to_edge(UP) @@ -378,7 +381,10 @@ class LinearTransformationScene(VectorScene): self.title = title return self - def get_matrix_transformation(self, transposed_matrix): + def get_matrix_transformation(self, matrix): + return self.get_transposed_matrix_transformation(np.array(matrix).T) + + def get_transposed_matrix_transformation(self, transposed_matrix): transposed_matrix = np.array(transposed_matrix) if transposed_matrix.shape == (2, 2): new_matrix = np.identity(3) @@ -389,11 +395,11 @@ class LinearTransformationScene(VectorScene): return lambda point: np.dot(point, transposed_matrix) def get_piece_movement(self, pieces): - start = VMobject(*pieces) + start = VMobject(*pieces) target = VMobject(*[mob.target for mob in pieces]) if self.leave_ghost_vectors: self.add(start.copy().fade(0.7)) - return Transform(start, target, submobject_mode = "all_at_once") + return Transform(start, target, submobject_mode="all_at_once") def get_moving_mobject_movement(self, func): for m in self.moving_mobjects: @@ -405,7 +411,7 @@ class LinearTransformationScene(VectorScene): def get_vector_movement(self, func): for v in self.moving_vectors: - v.target = Vector(func(v.get_end()), color = v.get_color()) + v.target = Vector(func(v.get_end()), color=v.get_color()) norm = np.linalg.norm(v.target.get_end()) if norm < 0.1: v.target.get_tip().scale_in_place(norm) @@ -418,12 +424,15 @@ class LinearTransformationScene(VectorScene): ) return self.get_piece_movement(self.transformable_labels) + def apply_matrix(self, matrix, **kwargs): + self.apply_transposed_matrix(np.array(matrix).T, **kwargs) + def apply_transposed_matrix(self, transposed_matrix, **kwargs): - func = self.get_matrix_transformation(transposed_matrix) + func = self.get_transposed_matrix_transformation(transposed_matrix) if "path_arc" not in kwargs: net_rotation = np.mean([ angle_of_vector(func(RIGHT)), - angle_of_vector(func(UP))-np.pi/2 + angle_of_vector(func(UP)) - np.pi / 2 ]) kwargs["path_arc"] = net_rotation self.apply_function(func, **kwargs) @@ -436,7 +445,7 @@ class LinearTransformationScene(VectorScene): self.plane.prepare_for_nonlinear_transform() self.apply_function(function, **kwargs) - def apply_function(self, function, added_anims = [], **kwargs): + def apply_function(self, function, added_anims=[], **kwargs): if "run_time" not in kwargs: kwargs["run_time"] = 3 anims = [ @@ -451,14 +460,3 @@ class LinearTransformationScene(VectorScene): for f_mob in self.foreground_mobjects ] + added_anims self.play(*anims, **kwargs) - - - - - - - - - - - From 35b674b480a43a20f83cd181744bafe9623c000c Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 6 Apr 2018 13:08:37 -0700 Subject: [PATCH 5/6] Added argparse requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 8796f6cd..5e284892 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +argparse==1.4.0 colour==0.1.2 numpy==1.11.0 Pillow==3.4.2 From 73433823ced98f076c3ed03296be9d8fb5a755b8 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 6 Apr 2018 13:08:57 -0700 Subject: [PATCH 6/6] Changed several files to conform to PEP8 --- animation/creation.py | 77 +++++++++++------- for_3b1b_videos/pi_creature_scene.py | 117 ++++++++++++++------------- mobject/matrix.py | 47 +++++------ 3 files changed, 131 insertions(+), 110 deletions(-) diff --git a/animation/creation.py b/animation/creation.py index e345f893..a0417b80 100644 --- a/animation/creation.py +++ b/animation/creation.py @@ -15,7 +15,8 @@ from utils.paths import counterclockwise_path from utils.rate_functions import double_smooth from utils.rate_functions import smooth -#Drawing +# Drawing + class ShowPartial(Animation): def update_submobject(self, submobject, starting_submobject, alpha): @@ -26,26 +27,31 @@ class ShowPartial(Animation): def get_bounds(self, alpha): raise Exception("Not Implemented") + class ShowCreation(ShowPartial): CONFIG = { - "submobject_mode" : "one_at_a_time", + "submobject_mode": "one_at_a_time", } + def get_bounds(self, alpha): return (0, alpha) + class Uncreate(ShowCreation): CONFIG = { - "rate_func" : lambda t : smooth(1-t), - "remover" : True + "rate_func": lambda t: smooth(1 - t), + "remover": True } + class Write(ShowCreation): CONFIG = { - "rate_func" : None, - "submobject_mode" : "lagged_start", + "rate_func": None, + "submobject_mode": "lagged_start", } + def __init__(self, mob_or_text, **kwargs): - digest_config(self, kwargs) + digest_config(self, kwargs) if isinstance(mob_or_text, str): mobject = TextMobject(mob_or_text) else: @@ -67,13 +73,15 @@ class Write(ShowCreation): else: self.run_time = 2 + class DrawBorderThenFill(Animation): CONFIG = { - "run_time" : 2, - "stroke_width" : 2, - "stroke_color" : None, - "rate_func" : double_smooth, + "run_time": 2, + "stroke_width": 2, + "stroke_color": None, + "rate_func": double_smooth, } + def __init__(self, vmobject, **kwargs): if not isinstance(vmobject, VMobject): raise Exception("DrawBorderThenFill only works for VMobjects") @@ -82,59 +90,64 @@ class DrawBorderThenFill(Animation): def update_submobject(self, submobject, starting_submobject, alpha): submobject.pointwise_become_partial( - starting_submobject, 0, min(2*alpha, 1) + starting_submobject, 0, min(2 * alpha, 1) ) if alpha < 0.5: if self.stroke_color: - color = self.stroke_color + color = self.stroke_color elif starting_submobject.stroke_width > 0: color = starting_submobject.get_stroke_color() else: color = starting_submobject.get_color() - submobject.set_stroke(color, width = self.stroke_width) - submobject.set_fill(opacity = 0) + submobject.set_stroke(color, width=self.stroke_width) + submobject.set_fill(opacity=0) else: if not self.reached_halfway_point_before: self.reached_halfway_point_before = True submobject.points = np.array(starting_submobject.points) width, opacity = [ - interpolate(start, end, 2*alpha - 1) + interpolate(start, end, 2 * alpha - 1) for start, end in [ (self.stroke_width, starting_submobject.get_stroke_width()), (0, starting_submobject.get_fill_opacity()) ] ] - submobject.set_stroke(width = width) - submobject.set_fill(opacity = opacity) + submobject.set_stroke(width=width) + submobject.set_fill(opacity=opacity) + +# Fading -#Fading class FadeOut(Transform): CONFIG = { - "remover" : True, + "remover": True, } + def __init__(self, mobject, **kwargs): target = mobject.copy() target.fade(1) Transform.__init__(self, mobject, target, **kwargs) - def clean_up(self, surrounding_scene = None): + def clean_up(self, surrounding_scene=None): Transform.clean_up(self, surrounding_scene) self.update(0) + class FadeIn(Transform): def __init__(self, mobject, **kwargs): target = mobject.copy() Transform.__init__(self, mobject, target, **kwargs) self.starting_mobject.fade(1) if isinstance(self.starting_mobject, VMobject): - self.starting_mobject.set_stroke(width = 0) - self.starting_mobject.set_fill(opacity = 0) + self.starting_mobject.set_stroke(width=0) + self.starting_mobject.set_fill(opacity=0) + class FadeInAndShiftFromDirection(Transform): CONFIG = { - "direction" : DOWN, + "direction": DOWN, } + def __init__(self, mobject, **kwargs): digest_config(self, kwargs) target = mobject.copy() @@ -142,19 +155,23 @@ class FadeInAndShiftFromDirection(Transform): mobject.fade(1) Transform.__init__(self, mobject, target, **kwargs) + class FadeInFromDown(FadeInAndShiftFromDirection): """ Essential a more convenient form of FadeInAndShiftFromDirection """ CONFIG = { - "direction" : DOWN, + "direction": DOWN, } -#Growing +# Growing + + class GrowFromPoint(Transform): CONFIG = { - "point_color" : None, + "point_color": None, } + def __init__(self, mobject, point, **kwargs): digest_config(self, kwargs) target = mobject.copy() @@ -165,19 +182,23 @@ class GrowFromPoint(Transform): mobject.set_color(point_mob.get_color()) Transform.__init__(self, mobject, target, **kwargs) + class GrowFromCenter(GrowFromPoint): def __init__(self, mobject, **kwargs): GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs) + class GrowArrow(GrowFromPoint): def __init__(self, arrow, **kwargs): GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs) + class SpinInFromNothing(GrowFromCenter): CONFIG = { - "path_func" : counterclockwise_path() + "path_func": counterclockwise_path() } + class ShrinkToCenter(Transform): def __init__(self, mobject, **kwargs): Transform.__init__( diff --git a/for_3b1b_videos/pi_creature_scene.py b/for_3b1b_videos/pi_creature_scene.py index ba84a55d..220e58a7 100644 --- a/for_3b1b_videos/pi_creature_scene.py +++ b/for_3b1b_videos/pi_creature_scene.py @@ -25,17 +25,19 @@ from scene.scene import Scene from utils.rate_functions import squish_rate_func from utils.rate_functions import there_and_back + class PiCreatureScene(Scene): CONFIG = { - "total_wait_time" : 0, - "seconds_to_blink" : 3, - "pi_creatures_start_on_screen" : True, - "default_pi_creature_kwargs" : { - "color" : GREY_BROWN, - "flip_at_start" : True, + "total_wait_time": 0, + "seconds_to_blink": 3, + "pi_creatures_start_on_screen": True, + "default_pi_creature_kwargs": { + "color": GREY_BROWN, + "flip_at_start": True, }, - "default_pi_creature_start_corner" : DOWN+LEFT, + "default_pi_creature_start_corner": DOWN + LEFT, } + def setup(self): self.pi_creatures = self.create_pi_creatures() self.pi_creature = self.get_primary_pi_creature() @@ -43,7 +45,7 @@ class PiCreatureScene(Scene): self.add(*self.pi_creatures) def create_pi_creatures(self): - """ + """ Likely updated for subclasses """ return VGroup(self.create_pi_creature()) @@ -66,7 +68,7 @@ class PiCreatureScene(Scene): def get_on_screen_pi_creatures(self): mobjects = self.get_mobjects() return VGroup(*filter( - lambda pi : pi in mobjects, + lambda pi: pi in mobjects, self.get_pi_creatures() )) @@ -80,7 +82,7 @@ class PiCreatureScene(Scene): bubble_class = kwargs.pop("bubble_class", SpeechBubble) target_mode = kwargs.pop( - "target_mode", + "target_mode", "thinking" if bubble_class is ThoughtBubble else "speaking" ) bubble_kwargs = kwargs.pop("bubble_kwargs", {}) @@ -91,10 +93,11 @@ class PiCreatureScene(Scene): on_screen_mobjects = self.camera.extract_mobject_family_members( self.get_mobjects() ) + def has_bubble(pi): return hasattr(pi, "bubble") and \ - pi.bubble is not None and \ - pi.bubble in on_screen_mobjects + pi.bubble is not None and \ + pi.bubble in on_screen_mobjects pi_creatures_with_bubbles = filter(has_bubble, self.get_pi_creatures()) if pi_creature in pi_creatures_with_bubbles: @@ -102,7 +105,7 @@ class PiCreatureScene(Scene): old_bubble = pi_creature.bubble bubble = pi_creature.get_bubble( *content, - bubble_class = bubble_class, + bubble_class=bubble_class, **bubble_kwargs ) anims += [ @@ -114,9 +117,9 @@ class PiCreatureScene(Scene): anims.append(PiCreatureBubbleIntroduction( pi_creature, *content, - bubble_class = bubble_class, - bubble_kwargs = bubble_kwargs, - target_mode = target_mode, + bubble_class=bubble_class, + bubble_kwargs=bubble_kwargs, + target_mode=target_mode, **kwargs )) anims += [ @@ -130,26 +133,28 @@ class PiCreatureScene(Scene): def pi_creature_says(self, *args, **kwargs): self.introduce_bubble( *args, - bubble_class = SpeechBubble, + bubble_class=SpeechBubble, **kwargs ) def pi_creature_thinks(self, *args, **kwargs): self.introduce_bubble( *args, - bubble_class = ThoughtBubble, + bubble_class=ThoughtBubble, **kwargs ) def say(self, *content, **kwargs): - self.pi_creature_says(self.get_primary_pi_creature(), *content, **kwargs) + self.pi_creature_says( + self.get_primary_pi_creature(), *content, **kwargs) def think(self, *content, **kwargs): - self.pi_creature_thinks(self.get_primary_pi_creature(), *content, **kwargs) + self.pi_creature_thinks( + self.get_primary_pi_creature(), *content, **kwargs) def compile_play_args_to_animation_list(self, *args): """ - Add animations so that all pi creatures look at the + Add animations so that all pi creatures look at the first mobject being animated with each .play call """ animations = Scene.compile_play_args_to_animation_list(self, *args) @@ -157,13 +162,13 @@ class PiCreatureScene(Scene): return animations non_pi_creature_anims = filter( - lambda anim : anim.mobject not in self.get_pi_creatures(), + lambda anim: anim.mobject not in self.get_pi_creatures(), animations ) if len(non_pi_creature_anims) == 0: return animations first_anim = non_pi_creature_anims[0] - #Look at ending state + # Look at ending state first_anim.update(1) point_of_interest = first_anim.mobject.get_center() first_anim.update(0) @@ -174,7 +179,7 @@ class PiCreatureScene(Scene): if pi_creature in first_anim.mobject.submobject_family(): continue anims_with_pi_creature = filter( - lambda anim : pi_creature in anim.mobject.submobject_family(), + lambda anim: pi_creature in anim.mobject.submobject_family(), animations ) for anim in anims_with_pi_creature: @@ -193,7 +198,7 @@ class PiCreatureScene(Scene): def blink(self): self.play(Blink(random.choice(self.get_on_screen_pi_creatures()))) - def joint_blink(self, pi_creatures = None, shuffle = True, **kwargs): + def joint_blink(self, pi_creatures=None, shuffle=True, **kwargs): if pi_creatures is None: pi_creatures = self.get_on_screen_pi_creatures() creatures_list = list(pi_creatures) @@ -202,25 +207,25 @@ class PiCreatureScene(Scene): def get_rate_func(pi): index = creatures_list.index(pi) - proportion = float(index)/len(creatures_list) - start_time = 0.8*proportion + proportion = float(index) / len(creatures_list) + start_time = 0.8 * proportion return squish_rate_func( there_and_back, start_time, start_time + 0.2 ) self.play(*[ - Blink(pi, rate_func = get_rate_func(pi), **kwargs) + Blink(pi, rate_func=get_rate_func(pi), **kwargs) for pi in creatures_list ]) return self - def wait(self, time = 1, blink = True): + def wait(self, time=1, blink=True): while time >= 1: - time_to_blink = self.total_wait_time%self.seconds_to_blink == 0 + time_to_blink = self.total_wait_time % self.seconds_to_blink == 0 if blink and self.any_pi_creatures_on_screen() and time_to_blink: self.blink() - self.num_plays -= 1 #This shouldn't count as an animation + self.num_plays -= 1 # This shouldn't count as an animation else: self.non_blink_wait() time -= 1 @@ -229,14 +234,14 @@ class PiCreatureScene(Scene): self.non_blink_wait(time) return self - def non_blink_wait(self, time = 1): + def non_blink_wait(self, time=1): Scene.wait(self, time) return self def change_mode(self, mode): self.play(self.get_primary_pi_creature().change_mode, mode) - def look_at(self, thing_to_look_at, pi_creatures = None): + def look_at(self, thing_to_look_at, pi_creatures=None): if pi_creatures is None: pi_creatures = self.get_pi_creatures() self.play(*it.chain(*[ @@ -244,30 +249,33 @@ class PiCreatureScene(Scene): for pi in pi_creatures ])) + class TeacherStudentsScene(PiCreatureScene): CONFIG = { - "student_colors" : [BLUE_D, BLUE_E, BLUE_C], - "student_scale_factor" : 0.8, - "seconds_to_blink" : 2, - "screen_height" : 3, + "student_colors": [BLUE_D, BLUE_E, BLUE_C], + "student_scale_factor": 0.8, + "seconds_to_blink": 2, + "screen_height": 3, } + def setup(self): PiCreatureScene.setup(self) - self.screen = ScreenRectangle(height = self.screen_height) - self.screen.to_corner(UP+LEFT) - self.hold_up_spot = self.teacher.get_corner(UP+LEFT) + MED_LARGE_BUFF*UP + self.screen = ScreenRectangle(height=self.screen_height) + self.screen.to_corner(UP + LEFT) + self.hold_up_spot = self.teacher.get_corner( + UP + LEFT) + MED_LARGE_BUFF * UP def create_pi_creatures(self): self.teacher = Mortimer() self.teacher.to_corner(DOWN + RIGHT) - self.teacher.look(DOWN+LEFT) + self.teacher.look(DOWN + LEFT) self.students = VGroup(*[ - Randolph(color = c) + Randolph(color=c) for c in self.student_colors ]) self.students.arrange_submobjects(RIGHT) self.students.scale(self.student_scale_factor) - self.students.to_corner(DOWN+LEFT) + self.students.to_corner(DOWN + LEFT) self.teacher.look_at(self.students[-1].eyes) for student in self.students: student.look_at(self.teacher.eyes) @@ -288,8 +296,8 @@ class TeacherStudentsScene(PiCreatureScene): def student_says(self, *content, **kwargs): if "target_mode" not in kwargs: target_mode = random.choice([ - "raise_right_hand", - "raise_left_hand", + "raise_right_hand", + "raise_left_hand", ]) kwargs["target_mode"] = target_mode student = self.get_students()[kwargs.get("student_index", 1)] @@ -307,7 +315,7 @@ class TeacherStudentsScene(PiCreatureScene): return self.pi_creature_thinks(student, *content, **kwargs) def change_all_student_modes(self, mode, **kwargs): - self.change_student_modes(*[mode]*len(self.students), **kwargs) + self.change_student_modes(*[mode] * len(self.students), **kwargs) def change_student_modes(self, *modes, **kwargs): added_anims = kwargs.pop("added_anims", []) @@ -326,12 +334,12 @@ class TeacherStudentsScene(PiCreatureScene): pi.look_at(kwargs["look_at_arg"]) submobject_mode = kwargs.get("submobject_mode", "lagged_start") return Transform( - start, target, - submobject_mode = submobject_mode, - run_time = 2 + start, target, + submobject_mode=submobject_mode, + run_time=2 ) - def zoom_in_on_thought_bubble(self, bubble = None, radius = FRAME_Y_RADIUS+FRAME_X_RADIUS): + def zoom_in_on_thought_bubble(self, bubble=None, radius=FRAME_Y_RADIUS + FRAME_X_RADIUS): if bubble is None: for pi in self.get_pi_creatures(): if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble): @@ -340,15 +348,16 @@ class TeacherStudentsScene(PiCreatureScene): 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) + centered = point + vect + return radius * centered / np.linalg.norm(centered) self.play(*[ ApplyPointwiseFunction(func, mob) for mob in self.get_mobjects() ]) - def teacher_holds_up(self, mobject, target_mode = "raise_right_hand", **kwargs): + def teacher_holds_up(self, mobject, target_mode="raise_right_hand", **kwargs): mobject.move_to(self.hold_up_spot, DOWN) mobject.shift_onto_screen() mobject_copy = mobject.copy() @@ -358,5 +367,3 @@ class TeacherStudentsScene(PiCreatureScene): ReplacementTransform(mobject_copy, mobject), self.teacher.change, target_mode, ) - - diff --git a/mobject/matrix.py b/mobject/matrix.py index cf20f6de..e763032a 100644 --- a/mobject/matrix.py +++ b/mobject/matrix.py @@ -12,12 +12,13 @@ from constants import * VECTOR_LABEL_SCALE_FACTOR = 0.8 + def matrix_to_tex_string(matrix): matrix = np.array(matrix).astype("string") if matrix.ndim == 1: matrix = matrix.reshape((matrix.size, 1)) n_rows, n_cols = matrix.shape - prefix = "\\left[ \\begin{array}{%s}"%("c"*n_cols) + prefix = "\\left[ \\begin{array}{%s}" % ("c" * n_cols) suffix = "\\end{array} \\right]" rows = [ " & ".join(row) @@ -25,39 +26,43 @@ def matrix_to_tex_string(matrix): ] return prefix + " \\\\ ".join(rows) + suffix + def matrix_to_mobject(matrix): return TexMobject(matrix_to_tex_string(matrix)) -def vector_coordinate_label(vector_mob, integer_labels = True, - n_dim = 2, color = WHITE): + +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) vect = vect[:n_dim] vect = vect.reshape((n_dim, 1)) - label = Matrix(vect, add_background_rectangles = True) + label = Matrix(vect, add_background_rectangles=True) label.scale(VECTOR_LABEL_SCALE_FACTOR) shift_dir = np.array(vector_mob.get_end()) - 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 + 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.set_color(color) label.rect = BackgroundRectangle(label) label.add_to_back(label.rect) return label + class Matrix(VMobject): CONFIG = { - "v_buff" : 0.5, - "h_buff" : 1, - "add_background_rectangles" : False + "v_buff": 0.5, + "h_buff": 1, + "add_background_rectangles": False } + def __init__(self, matrix, **kwargs): """ - Matrix can either either include numbres, tex_strings, + Matrix can either either include numbres, tex_strings, or mobjects """ VMobject.__init__(self, **kwargs) @@ -89,9 +94,9 @@ class Matrix(VMobject): if i == 0 and j == 0: continue elif i == 0: - mob.next_to(matrix[i][j-1], RIGHT, self.h_buff) + mob.next_to(matrix[i][j - 1], RIGHT, self.h_buff) else: - mob.next_to(matrix[i-1][j], DOWN, self.v_buff) + mob.next_to(matrix[i - 1][j], DOWN, self.v_buff) return self def add_brackets(self): @@ -107,7 +112,7 @@ class Matrix(VMobject): def set_color_columns(self, *colors): for i, color in enumerate(colors): - VGroup(*self.mob_matrix[:,i]).set_color(color) + VGroup(*self.mob_matrix[:, i]).set_color(color) return self def add_background_to_entries(self): @@ -123,15 +128,3 @@ class Matrix(VMobject): def get_brackets(self): return self.brackets - - - - - - - - - - - -