Merge pull request #196 from 3b1b/eola2

Eola2
This commit is contained in:
Grant Sanderson
2018-04-06 13:09:53 -07:00
committed by GitHub
8 changed files with 529 additions and 223 deletions

View File

View File

@ -0,0 +1,286 @@
from big_ol_pile_of_manim_imports import *
class WorkOutNumerically(Scene):
CONFIG = {
"M1_COLOR": TEAL,
"M2_COLOR": PINK,
}
def construct(self):
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.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))

View File

@ -17,6 +17,7 @@ from utils.rate_functions import smooth
# Drawing # Drawing
class ShowPartial(Animation): class ShowPartial(Animation):
def update_submobject(self, submobject, starting_submobject, alpha): def update_submobject(self, submobject, starting_submobject, alpha):
submobject.pointwise_become_partial( submobject.pointwise_become_partial(
@ -26,24 +27,29 @@ class ShowPartial(Animation):
def get_bounds(self, alpha): def get_bounds(self, alpha):
raise Exception("Not Implemented") raise Exception("Not Implemented")
class ShowCreation(ShowPartial): class ShowCreation(ShowPartial):
CONFIG = { CONFIG = {
"submobject_mode": "one_at_a_time", "submobject_mode": "one_at_a_time",
} }
def get_bounds(self, alpha): def get_bounds(self, alpha):
return (0, alpha) return (0, alpha)
class Uncreate(ShowCreation): class Uncreate(ShowCreation):
CONFIG = { CONFIG = {
"rate_func": lambda t: smooth(1 - t), "rate_func": lambda t: smooth(1 - t),
"remover": True "remover": True
} }
class Write(ShowCreation): class Write(ShowCreation):
CONFIG = { CONFIG = {
"rate_func": None, "rate_func": None,
"submobject_mode": "lagged_start", "submobject_mode": "lagged_start",
} }
def __init__(self, mob_or_text, **kwargs): def __init__(self, mob_or_text, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
if isinstance(mob_or_text, str): if isinstance(mob_or_text, str):
@ -67,6 +73,7 @@ class Write(ShowCreation):
else: else:
self.run_time = 2 self.run_time = 2
class DrawBorderThenFill(Animation): class DrawBorderThenFill(Animation):
CONFIG = { CONFIG = {
"run_time": 2, "run_time": 2,
@ -74,6 +81,7 @@ class DrawBorderThenFill(Animation):
"stroke_color": None, "stroke_color": None,
"rate_func": double_smooth, "rate_func": double_smooth,
} }
def __init__(self, vmobject, **kwargs): def __init__(self, vmobject, **kwargs):
if not isinstance(vmobject, VMobject): if not isinstance(vmobject, VMobject):
raise Exception("DrawBorderThenFill only works for VMobjects") raise Exception("DrawBorderThenFill only works for VMobjects")
@ -109,10 +117,12 @@ class DrawBorderThenFill(Animation):
# Fading # Fading
class FadeOut(Transform): class FadeOut(Transform):
CONFIG = { CONFIG = {
"remover": True, "remover": True,
} }
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
target = mobject.copy() target = mobject.copy()
target.fade(1) target.fade(1)
@ -122,6 +132,7 @@ class FadeOut(Transform):
Transform.clean_up(self, surrounding_scene) Transform.clean_up(self, surrounding_scene)
self.update(0) self.update(0)
class FadeIn(Transform): class FadeIn(Transform):
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
target = mobject.copy() target = mobject.copy()
@ -131,10 +142,12 @@ class FadeIn(Transform):
self.starting_mobject.set_stroke(width=0) self.starting_mobject.set_stroke(width=0)
self.starting_mobject.set_fill(opacity=0) self.starting_mobject.set_fill(opacity=0)
class FadeInAndShiftFromDirection(Transform): class FadeInAndShiftFromDirection(Transform):
CONFIG = { CONFIG = {
"direction": DOWN, "direction": DOWN,
} }
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
target = mobject.copy() target = mobject.copy()
@ -142,6 +155,7 @@ class FadeInAndShiftFromDirection(Transform):
mobject.fade(1) mobject.fade(1)
Transform.__init__(self, mobject, target, **kwargs) Transform.__init__(self, mobject, target, **kwargs)
class FadeInFromDown(FadeInAndShiftFromDirection): class FadeInFromDown(FadeInAndShiftFromDirection):
""" """
Essential a more convenient form of FadeInAndShiftFromDirection Essential a more convenient form of FadeInAndShiftFromDirection
@ -151,10 +165,13 @@ class FadeInFromDown(FadeInAndShiftFromDirection):
} }
# Growing # Growing
class GrowFromPoint(Transform): class GrowFromPoint(Transform):
CONFIG = { CONFIG = {
"point_color": None, "point_color": None,
} }
def __init__(self, mobject, point, **kwargs): def __init__(self, mobject, point, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
target = mobject.copy() target = mobject.copy()
@ -165,19 +182,23 @@ class GrowFromPoint(Transform):
mobject.set_color(point_mob.get_color()) mobject.set_color(point_mob.get_color())
Transform.__init__(self, mobject, target, **kwargs) Transform.__init__(self, mobject, target, **kwargs)
class GrowFromCenter(GrowFromPoint): class GrowFromCenter(GrowFromPoint):
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs) GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs)
class GrowArrow(GrowFromPoint): class GrowArrow(GrowFromPoint):
def __init__(self, arrow, **kwargs): def __init__(self, arrow, **kwargs):
GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs) GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs)
class SpinInFromNothing(GrowFromCenter): class SpinInFromNothing(GrowFromCenter):
CONFIG = { CONFIG = {
"path_func": counterclockwise_path() "path_func": counterclockwise_path()
} }
class ShrinkToCenter(Transform): class ShrinkToCenter(Transform):
def __init__(self, mobject, **kwargs): def __init__(self, mobject, **kwargs):
Transform.__init__( Transform.__init__(

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python2 #!/usr/bin/env python2
import sys import sys
# import getopt
import argparse import argparse
import imp import imp
import imp import imp
@ -11,8 +10,9 @@ import os
import subprocess as sp import subprocess as sp
import traceback import traceback
from camera.camera import Camera
from constants import * from constants import *
from camera.camera import Camera
from scene.scene import Scene from scene.scene import Scene
from utils.sounds import play_error_sound from utils.sounds import play_error_sound
from utils.sounds import play_finish_sound from utils.sounds import play_finish_sound

View File

@ -25,6 +25,7 @@ from scene.scene import Scene
from utils.rate_functions import squish_rate_func from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back from utils.rate_functions import there_and_back
class PiCreatureScene(Scene): class PiCreatureScene(Scene):
CONFIG = { CONFIG = {
"total_wait_time": 0, "total_wait_time": 0,
@ -36,6 +37,7 @@ class PiCreatureScene(Scene):
}, },
"default_pi_creature_start_corner": DOWN + LEFT, "default_pi_creature_start_corner": DOWN + LEFT,
} }
def setup(self): def setup(self):
self.pi_creatures = self.create_pi_creatures() self.pi_creatures = self.create_pi_creatures()
self.pi_creature = self.get_primary_pi_creature() self.pi_creature = self.get_primary_pi_creature()
@ -91,6 +93,7 @@ class PiCreatureScene(Scene):
on_screen_mobjects = self.camera.extract_mobject_family_members( on_screen_mobjects = self.camera.extract_mobject_family_members(
self.get_mobjects() self.get_mobjects()
) )
def has_bubble(pi): def has_bubble(pi):
return hasattr(pi, "bubble") and \ return hasattr(pi, "bubble") and \
pi.bubble is not None and \ pi.bubble is not None and \
@ -142,10 +145,12 @@ class PiCreatureScene(Scene):
) )
def say(self, *content, **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): 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): def compile_play_args_to_animation_list(self, *args):
""" """
@ -244,6 +249,7 @@ class PiCreatureScene(Scene):
for pi in pi_creatures for pi in pi_creatures
])) ]))
class TeacherStudentsScene(PiCreatureScene): class TeacherStudentsScene(PiCreatureScene):
CONFIG = { CONFIG = {
"student_colors": [BLUE_D, BLUE_E, BLUE_C], "student_colors": [BLUE_D, BLUE_E, BLUE_C],
@ -251,11 +257,13 @@ class TeacherStudentsScene(PiCreatureScene):
"seconds_to_blink": 2, "seconds_to_blink": 2,
"screen_height": 3, "screen_height": 3,
} }
def setup(self): def setup(self):
PiCreatureScene.setup(self) PiCreatureScene.setup(self)
self.screen = ScreenRectangle(height=self.screen_height) self.screen = ScreenRectangle(height=self.screen_height)
self.screen.to_corner(UP + LEFT) self.screen.to_corner(UP + LEFT)
self.hold_up_spot = self.teacher.get_corner(UP+LEFT) + MED_LARGE_BUFF*UP self.hold_up_spot = self.teacher.get_corner(
UP + LEFT) + MED_LARGE_BUFF * UP
def create_pi_creatures(self): def create_pi_creatures(self):
self.teacher = Mortimer() self.teacher = Mortimer()
@ -340,6 +348,7 @@ class TeacherStudentsScene(PiCreatureScene):
if bubble is None: if bubble is None:
raise Exception("No pi creatures have a thought bubble") raise Exception("No pi creatures have a thought bubble")
vect = -bubble.get_bubble_center() vect = -bubble.get_bubble_center()
def func(point): def func(point):
centered = point + vect centered = point + vect
return radius * centered / np.linalg.norm(centered) return radius * centered / np.linalg.norm(centered)
@ -358,5 +367,3 @@ class TeacherStudentsScene(PiCreatureScene):
ReplacementTransform(mobject_copy, mobject), ReplacementTransform(mobject_copy, mobject),
self.teacher.change, target_mode, self.teacher.change, target_mode,
) )

View File

@ -12,6 +12,7 @@ from constants import *
VECTOR_LABEL_SCALE_FACTOR = 0.8 VECTOR_LABEL_SCALE_FACTOR = 0.8
def matrix_to_tex_string(matrix): def matrix_to_tex_string(matrix):
matrix = np.array(matrix).astype("string") matrix = np.array(matrix).astype("string")
if matrix.ndim == 1: if matrix.ndim == 1:
@ -25,9 +26,11 @@ def matrix_to_tex_string(matrix):
] ]
return prefix + " \\\\ ".join(rows) + suffix return prefix + " \\\\ ".join(rows) + suffix
def matrix_to_mobject(matrix): def matrix_to_mobject(matrix):
return TexMobject(matrix_to_tex_string(matrix)) return TexMobject(matrix_to_tex_string(matrix))
def vector_coordinate_label(vector_mob, integer_labels=True, def vector_coordinate_label(vector_mob, integer_labels=True,
n_dim=2, color=WHITE): n_dim=2, color=WHITE):
vect = np.array(vector_mob.get_end()) vect = np.array(vector_mob.get_end())
@ -49,12 +52,14 @@ def vector_coordinate_label(vector_mob, integer_labels = True,
label.add_to_back(label.rect) label.add_to_back(label.rect)
return label return label
class Matrix(VMobject): class Matrix(VMobject):
CONFIG = { CONFIG = {
"v_buff": 0.5, "v_buff": 0.5,
"h_buff": 1, "h_buff": 1,
"add_background_rectangles": False "add_background_rectangles": False
} }
def __init__(self, matrix, **kwargs): def __init__(self, matrix, **kwargs):
""" """
Matrix can either either include numbres, tex_strings, Matrix can either either include numbres, tex_strings,
@ -123,15 +128,3 @@ class Matrix(VMobject):
def get_brackets(self): def get_brackets(self):
return self.brackets return self.brackets

View File

@ -1,3 +1,4 @@
argparse==1.4.0
colour==0.1.2 colour==0.1.2
numpy==1.11.0 numpy==1.11.0
Pillow==3.4.2 Pillow==3.4.2

View File

@ -36,10 +36,12 @@ X_COLOR = GREEN_C
Y_COLOR = RED_C Y_COLOR = RED_C
Z_COLOR = BLUE_D Z_COLOR = BLUE_D
class VectorScene(Scene): class VectorScene(Scene):
CONFIG = { 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) plane = NumberPlane(**kwargs)
if animate: if animate:
@ -76,17 +78,18 @@ class VectorScene(Scene):
self.play(Write(coords)) self.play(Write(coords))
return coords return coords
def get_basis_vectors(self): def get_basis_vectors(self, i_hat_color=X_COLOR, j_hat_color=Y_COLOR):
return [ return VGroup(*[
Vector( Vector(
vect, color = color, vect,
color=color,
stroke_width=self.basis_vector_stroke_width stroke_width=self.basis_vector_stroke_width
) )
for vect, color in [ for vect, color in [
([1, 0], X_COLOR), ([1, 0], i_hat_color),
([0, 1], Y_COLOR) ([0, 1], j_hat_color)
]
] ]
])
def get_basis_vector_labels(self, **kwargs): def get_basis_vector_labels(self, **kwargs):
i_hat, j_hat = self.get_basis_vectors() i_hat, j_hat = self.get_basis_vectors()
@ -252,6 +255,7 @@ class VectorScene(Scene):
)) ))
self.remove(dots) self.remove(dots)
class LinearTransformationScene(VectorScene): class LinearTransformationScene(VectorScene):
CONFIG = { CONFIG = {
"include_background_plane": True, "include_background_plane": True,
@ -269,16 +273,18 @@ class LinearTransformationScene(VectorScene):
}, },
"show_coordinates": False, "show_coordinates": False,
"show_basis_vectors": True, "show_basis_vectors": True,
"basis_vector_stroke_width": 6,
"i_hat_color": X_COLOR, "i_hat_color": X_COLOR,
"j_hat_color": Y_COLOR, "j_hat_color": Y_COLOR,
"leave_ghost_vectors": False, "leave_ghost_vectors": False,
"t_matrix": [[3, 0], [1, 2]], "t_matrix": [[3, 0], [1, 2]],
} }
def setup(self): def setup(self):
if hasattr(self, "has_already_setup"): if hasattr(self, "has_already_setup"):
return return
self.has_already_setup = True 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.background_mobjects = []
self.foreground_mobjects = [] self.foreground_mobjects = []
self.transformable_mobjects = [] self.transformable_mobjects = []
@ -299,15 +305,12 @@ class LinearTransformationScene(VectorScene):
self.plane = NumberPlane(**self.foreground_plane_kwargs) self.plane = NumberPlane(**self.foreground_plane_kwargs)
self.add_transformable_mobject(self.plane) self.add_transformable_mobject(self.plane)
if self.show_basis_vectors: if self.show_basis_vectors:
self.i_hat, self.j_hat = [ self.basis_vectors = self.get_basis_vectors(
self.add_vector( i_hat_color=self.i_hat_color,
coords, color, animate = False, stroke_width = 6 j_hat_color=self.j_hat_color,
) )
for coords, color in [ self.moving_vectors += list(self.basis_vectors)
((1, 0), self.i_hat_color), self.i_hat, self.j_hat = self.basis_vectors
((0, 1), self.j_hat_color),
]
]
def add_special_mobjects(self, mob_list, *mobs_to_add): def add_special_mobjects(self, mob_list, *mobs_to_add):
for mobject in mobs_to_add: for mobject in mobs_to_add:
@ -378,7 +381,10 @@ class LinearTransformationScene(VectorScene):
self.title = title self.title = title
return self 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) transposed_matrix = np.array(transposed_matrix)
if transposed_matrix.shape == (2, 2): if transposed_matrix.shape == (2, 2):
new_matrix = np.identity(3) new_matrix = np.identity(3)
@ -418,8 +424,11 @@ class LinearTransformationScene(VectorScene):
) )
return self.get_piece_movement(self.transformable_labels) 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): 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: if "path_arc" not in kwargs:
net_rotation = np.mean([ net_rotation = np.mean([
angle_of_vector(func(RIGHT)), angle_of_vector(func(RIGHT)),
@ -451,14 +460,3 @@ class LinearTransformationScene(VectorScene):
for f_mob in self.foreground_mobjects for f_mob in self.foreground_mobjects
] + added_anims ] + added_anims
self.play(*anims, **kwargs) self.play(*anims, **kwargs)