mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 20:43:56 +08:00
1822 lines
60 KiB
Python
1822 lines
60 KiB
Python
from big_ol_pile_of_manim_imports import *
|
|
from old_projects.eola.chapter1 import plane_wave_homotopy
|
|
|
|
V_COLOR = YELLOW
|
|
|
|
class Jennifer(PiCreature):
|
|
CONFIG = {
|
|
"color" : PINK,
|
|
"start_corner" : DOWN+LEFT,
|
|
}
|
|
|
|
class You(PiCreature):
|
|
CONFIG = {
|
|
"color" : BLUE_E,
|
|
"start_corner" : DOWN+RIGHT,
|
|
"flip_at_start" : True,
|
|
}
|
|
|
|
def get_small_bubble(pi_creature, height = 4, width = 3):
|
|
pi_center_x = pi_creature.get_center()[0]
|
|
kwargs = {
|
|
"height" : 4,
|
|
"bubble_center_adjustment_factor" : 1./6,
|
|
}
|
|
bubble = ThoughtBubble(**kwargs)
|
|
bubble.stretch_to_fit_width(3)##Canonical width
|
|
bubble.rotate(np.pi/4)
|
|
bubble.stretch_to_fit_width(width)
|
|
bubble.stretch_to_fit_height(height)
|
|
if pi_center_x < 0:
|
|
bubble.flip()
|
|
bubble.next_to(pi_creature, UP, buff = MED_SMALL_BUFF)
|
|
bubble.shift_onto_screen()
|
|
bubble.set_fill(BLACK, opacity = 0.8)
|
|
return bubble
|
|
|
|
class OpeningQuote(Scene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
"\\centering ``Mathematics is the art of giving the \\\\",
|
|
"same name ",
|
|
"to ",
|
|
"different things",
|
|
".''",
|
|
arg_separator = " "
|
|
)
|
|
words.set_color_by_tex("same name ", BLUE)
|
|
words.set_color_by_tex("different things", MAROON_B)
|
|
# words.set_width(FRAME_WIDTH - 2)
|
|
words.to_edge(UP)
|
|
author = TextMobject("-Henri Poincar\\'e.")
|
|
author.set_color(YELLOW)
|
|
author.next_to(words, DOWN, buff = 0.5)
|
|
|
|
self.play(FadeIn(words))
|
|
self.wait(2)
|
|
self.play(Write(author, run_time = 3))
|
|
self.wait(2)
|
|
|
|
class LinearCombinationScene(LinearTransformationScene):
|
|
CONFIG = {
|
|
"include_background_plane" : False,
|
|
"foreground_plane_kwargs" : {
|
|
"x_radius" : FRAME_X_RADIUS,
|
|
"y_radius" : FRAME_Y_RADIUS,
|
|
"secondary_line_ratio" : 1
|
|
},
|
|
}
|
|
def setup(self):
|
|
LinearTransformationScene.setup(self)
|
|
self.i_hat.label = self.get_vector_label(
|
|
self.i_hat, "\\hat{\\imath}", "right"
|
|
)
|
|
self.j_hat.label = self.get_vector_label(
|
|
self.j_hat, "\\hat{\\jmath}", "left"
|
|
)
|
|
|
|
def show_linear_combination(self, numerical_coords,
|
|
basis_vectors,
|
|
coord_mobs,
|
|
revert_to_original = True,
|
|
show_sum_vect = False,
|
|
sum_vect_color = V_COLOR,
|
|
):
|
|
for basis in basis_vectors:
|
|
if not hasattr(basis, "label"):
|
|
basis.label = VectorizedPoint()
|
|
direction = np.round(rotate_vector(
|
|
basis.get_end(), np.pi/2
|
|
))
|
|
basis.label.next_to(basis.get_center(), direction)
|
|
basis.save_state()
|
|
basis.label.save_state()
|
|
if coord_mobs is None:
|
|
coord_mobs = list(map(TexMobject, list(map(str, numerical_coords))))
|
|
VGroup(*coord_mobs).set_fill(opacity = 0)
|
|
for coord, basis in zip(coord_mobs, basis_vectors):
|
|
coord.next_to(basis.label, LEFT)
|
|
for coord, basis, scalar in zip(coord_mobs, basis_vectors, numerical_coords):
|
|
basis.target = basis.copy().scale(scalar)
|
|
basis.label.target = basis.label.copy()
|
|
coord.target = coord.copy()
|
|
new_label = VGroup(coord.target, basis.label.target)
|
|
new_label.arrange(aligned_edge = DOWN)
|
|
new_label.move_to(
|
|
basis.label,
|
|
aligned_edge = basis.get_center()-basis.label.get_center()
|
|
)
|
|
new_label.shift(
|
|
basis.target.get_center() - basis.get_center()
|
|
)
|
|
coord.target.next_to(basis.label.target, LEFT)
|
|
coord.target.set_fill(basis.get_color(), opacity = 1)
|
|
self.play(*list(map(MoveToTarget, [
|
|
coord, basis, basis.label
|
|
])))
|
|
self.wait()
|
|
self.play(*[
|
|
ApplyMethod(m.shift, basis_vectors[0].get_end())
|
|
for m in self.get_mobjects_from_last_animation()
|
|
])
|
|
if show_sum_vect:
|
|
sum_vect = Vector(
|
|
basis_vectors[1].get_end(),
|
|
color = sum_vect_color
|
|
)
|
|
self.play(ShowCreation(sum_vect))
|
|
self.wait(2)
|
|
if revert_to_original:
|
|
self.play(*it.chain(
|
|
[basis.restore for basis in basis_vectors],
|
|
[basis.label.restore for basis in basis_vectors],
|
|
[FadeOut(coord) for coord in coord_mobs],
|
|
[FadeOut(sum_vect) for x in [1] if show_sum_vect],
|
|
))
|
|
if show_sum_vect:
|
|
return sum_vect
|
|
|
|
class RemindOfCoordinates(LinearCombinationScene):
|
|
CONFIG = {
|
|
"vector_coords" : [3, 2]
|
|
}
|
|
def construct(self):
|
|
self.remove(self.i_hat, self.j_hat)
|
|
|
|
v = self.add_vector(self.vector_coords, color = V_COLOR)
|
|
coords = self.write_vector_coordinates(v)
|
|
self.show_standard_coord_meaning(*coords.get_entries().copy())
|
|
self.show_abstract_scalar_idea(*coords.get_entries().copy())
|
|
self.scale_basis_vectors(*coords.get_entries().copy())
|
|
self.list_implicit_assumptions(*coords.get_entries())
|
|
|
|
|
|
def show_standard_coord_meaning(self, x_coord, y_coord):
|
|
x, y = self.vector_coords
|
|
x_line = Line(ORIGIN, x*RIGHT, color = GREEN)
|
|
y_line = Line(ORIGIN, y*UP, color = RED)
|
|
y_line.shift(x_line.get_end())
|
|
for line, coord, direction in (x_line, x_coord, DOWN), (y_line, y_coord, LEFT):
|
|
self.play(
|
|
coord.set_color, line.get_color(),
|
|
coord.next_to, line.get_center(), direction,
|
|
ShowCreation(line),
|
|
)
|
|
self.wait()
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [x_coord, y_coord, x_line, y_line])))
|
|
|
|
|
|
def show_abstract_scalar_idea(self, x_coord, y_coord):
|
|
x_shift, y_shift = 4*LEFT, 4*RIGHT
|
|
to_save = x_coord, y_coord, self.i_hat, self.j_hat
|
|
for mob in to_save:
|
|
mob.save_state()
|
|
everything = VGroup(*self.get_mobjects())
|
|
words = TextMobject("Think of coordinates \\\\ as", "scalars")
|
|
words.set_color_by_tex("scalars", YELLOW)
|
|
words.to_edge(UP)
|
|
|
|
x, y = self.vector_coords
|
|
scaled_i = self.i_hat.copy().scale(x)
|
|
scaled_j = self.j_hat.copy().scale(y)
|
|
VGroup(self.i_hat, scaled_i).shift(x_shift)
|
|
VGroup(self.j_hat, scaled_j).shift(y_shift)
|
|
|
|
self.play(
|
|
FadeOut(everything),
|
|
x_coord.scale_in_place, 1.5,
|
|
x_coord.move_to, x_shift + 3*UP,
|
|
y_coord.scale_in_place, 1.5,
|
|
y_coord.move_to, y_shift + 3*UP,
|
|
Write(words)
|
|
)
|
|
self.play(*list(map(FadeIn, [self.i_hat, self.j_hat])))
|
|
self.wait()
|
|
self.play(Transform(self.i_hat, scaled_i))
|
|
self.play(Transform(self.j_hat, scaled_j))
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(words),
|
|
FadeIn(everything),
|
|
*[mob.restore for mob in to_save]
|
|
)
|
|
self.wait()
|
|
|
|
def scale_basis_vectors(self, x_coord, y_coord):
|
|
self.play(*list(map(Write, [self.i_hat.label, self.j_hat.label])))
|
|
self.show_linear_combination(
|
|
self.vector_coords,
|
|
basis_vectors = [self.i_hat, self.j_hat],
|
|
coord_mobs = [x_coord, y_coord]
|
|
)
|
|
|
|
def list_implicit_assumptions(self, x_coord, y_coord):
|
|
everything = VGroup(*self.get_mobjects())
|
|
title = TextMobject("Implicit assumptions")
|
|
h_line = Line(title.get_left(), title.get_right())
|
|
h_line.set_color(YELLOW)
|
|
h_line.next_to(title, DOWN)
|
|
title.add(h_line)
|
|
|
|
ass1 = TextMobject("-First coordinate")
|
|
ass1 = VGroup(ass1, self.i_hat.copy())
|
|
ass1.arrange(buff = MED_SMALL_BUFF)
|
|
|
|
ass2 = TextMobject("-Second coordinate")
|
|
ass2 = VGroup(ass2, self.j_hat.copy())
|
|
ass2.arrange(buff = MED_SMALL_BUFF)
|
|
|
|
ass3 = TextMobject("-Unit of distance")
|
|
|
|
group = VGroup(title, ass1, ass2, ass3)
|
|
group.arrange(DOWN, aligned_edge = LEFT, buff = MED_SMALL_BUFF)
|
|
group.to_corner(UP+LEFT)
|
|
# VGroup(*group[1:]).shift(0.5*DOWN)
|
|
for words in group:
|
|
words.add_to_back(BackgroundRectangle(words))
|
|
|
|
self.play(Write(title))
|
|
self.wait()
|
|
self.play(
|
|
Write(ass1),
|
|
ApplyFunction(
|
|
lambda m : m.rotate_in_place(np.pi/6).set_color(X_COLOR),
|
|
x_coord,
|
|
rate_func = wiggle
|
|
)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Write(ass2),
|
|
ApplyFunction(
|
|
lambda m : m.rotate_in_place(np.pi/6).set_color(Y_COLOR),
|
|
y_coord,
|
|
rate_func = wiggle
|
|
)
|
|
)
|
|
self.wait()
|
|
self.play(Write(ass3))
|
|
self.wait(2)
|
|
keepers = VGroup(*[
|
|
self.i_hat, self.j_hat,
|
|
self.i_hat.label, self.j_hat.label
|
|
])
|
|
self.play(
|
|
FadeOut(everything),
|
|
Animation(keepers.copy()),
|
|
Animation(group)
|
|
)
|
|
self.wait()
|
|
|
|
class NameCoordinateSystem(Scene):
|
|
def construct(self):
|
|
vector = Vector([3, 2])
|
|
coords = Matrix([3, 2])
|
|
arrow = TexMobject("\\Rightarrow")
|
|
vector.next_to(arrow, RIGHT, buff = 0)
|
|
coords.next_to(arrow, LEFT, buff = MED_LARGE_BUFF)
|
|
group = VGroup(coords, arrow, vector)
|
|
group.shift(2*UP)
|
|
coordinate_system = TextMobject("``Coordinate system''")
|
|
coordinate_system.next_to(arrow, UP, buff = LARGE_BUFF)
|
|
|
|
i_hat, j_hat = Vector([1, 0]), Vector([0, 1])
|
|
i_hat.set_color(X_COLOR)
|
|
j_hat.set_color(Y_COLOR)
|
|
i_label = TexMobject("\\hat{\\imath}")
|
|
i_label.set_color(X_COLOR)
|
|
i_label.next_to(i_hat, DOWN)
|
|
j_label = TexMobject("\\hat{\\jmath}")
|
|
j_label.set_color(Y_COLOR)
|
|
j_label.next_to(j_hat, LEFT)
|
|
basis_group = VGroup(i_hat, j_hat, i_label, j_label)
|
|
basis_group.shift(DOWN)
|
|
basis_words = TextMobject("``Basis vectors''")
|
|
basis_words.shift(basis_group.get_bottom()[1]*UP+MED_SMALL_BUFF*DOWN)
|
|
|
|
self.play(Write(coords))
|
|
self.play(Write(arrow), ShowCreation(vector))
|
|
self.wait()
|
|
self.play(Write(coordinate_system))
|
|
self.wait(2)
|
|
self.play(Write(basis_group))
|
|
self.play(Write(basis_words))
|
|
self.wait()
|
|
|
|
class WhatAboutOtherBasis(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says("""
|
|
\\centering What if we used
|
|
different basis vectors
|
|
""")
|
|
self.random_blink()
|
|
self.change_student_modes("pondering")
|
|
self.random_blink(2)
|
|
|
|
class JenniferScene(LinearCombinationScene):
|
|
CONFIG = {
|
|
"b1_coords" : [2, 1],
|
|
"b2_coords" : [-1, 1],
|
|
"foreground_plane_kwargs" : {
|
|
"x_radius" : FRAME_X_RADIUS,
|
|
"y_radius" : FRAME_X_RADIUS,
|
|
},
|
|
}
|
|
def setup(self):
|
|
LinearCombinationScene.setup(self)
|
|
self.remove(self.plane, self.i_hat, self.j_hat)
|
|
self.jenny = Jennifer()
|
|
self.you = You()
|
|
self.b1 = Vector(self.b1_coords, color = X_COLOR)
|
|
self.b2 = Vector(self.b2_coords, color = Y_COLOR)
|
|
for i, vect in enumerate([self.b1, self.b2]):
|
|
vect.label = TexMobject("\\vec{\\textbf{b}}_%d"%(i+1))
|
|
vect.label.scale(0.7)
|
|
vect.label.add_background_rectangle()
|
|
vect.label.set_color(vect.get_color())
|
|
self.b1.label.next_to(
|
|
self.b1.get_end()*0.4, UP+LEFT, SMALL_BUFF/2
|
|
)
|
|
self.b2.label.next_to(
|
|
self.b2.get_end(), DOWN+LEFT, buff = SMALL_BUFF
|
|
)
|
|
self.basis_vectors = VGroup(
|
|
self.b1, self.b2, self.b1.label, self.b2.label
|
|
)
|
|
|
|
transform = self.get_matrix_transformation(self.cob_matrix().T)
|
|
self.jenny_plane = self.plane.copy()
|
|
self.jenny_plane.apply_function(transform)
|
|
|
|
def cob_matrix(self):
|
|
return np.array([self.b1_coords, self.b2_coords]).T
|
|
|
|
def inv_cob_matrix(self):
|
|
return np.linalg.inv(self.cob_matrix())
|
|
|
|
class IntroduceJennifer(JenniferScene):
|
|
CONFIG = {
|
|
"v_coords" : [3, 2]
|
|
}
|
|
def construct(self):
|
|
for plane in self.plane, self.jenny_plane:
|
|
plane.fade()
|
|
self.introduce_jenny()
|
|
self.add_basis_vectors()
|
|
self.show_v_from_both_perspectives()
|
|
self.how_we_label_her_basis()
|
|
|
|
def introduce_jenny(self):
|
|
jenny = self.jenny
|
|
name = TextMobject("Jennifer")
|
|
name.next_to(jenny, UP)
|
|
name.shift_onto_screen()
|
|
|
|
self.add(jenny)
|
|
self.play(
|
|
jenny.change_mode, "wave_1",
|
|
jenny.look, OUT,
|
|
Write(name)
|
|
)
|
|
self.play(
|
|
jenny.change_mode, "happy",
|
|
jenny.look, UP+RIGHT,
|
|
FadeOut(name)
|
|
)
|
|
self.wait()
|
|
|
|
def add_basis_vectors(self):
|
|
words = TextMobject("Alternate basis vectors")
|
|
words.shift(2.5*UP)
|
|
self.play(Write(words, run_time = 2))
|
|
for vect in self.b1, self.b2:
|
|
self.play(
|
|
ShowCreation(vect),
|
|
Write(vect.label)
|
|
)
|
|
self.wait()
|
|
self.play(FadeOut(words))
|
|
|
|
def show_v_from_both_perspectives(self):
|
|
v = Vector(self.v_coords)
|
|
jenny = self.jenny
|
|
you = self.you
|
|
|
|
you.coords = Matrix([3, 2])
|
|
jenny.coords = Matrix(["(5/3)", "(1/3)"])
|
|
for pi in you, jenny:
|
|
pi.bubble = get_small_bubble(pi)
|
|
pi.bubble.set_fill(BLACK, opacity = 0.7)
|
|
pi.bubble.add_content(pi.coords)
|
|
jenny.coords.scale_in_place(0.7)
|
|
|
|
new_coords = [-1, 2]
|
|
new_coords_mob = Matrix(new_coords)
|
|
new_coords_mob.set_height(jenny.coords.get_height())
|
|
new_coords_mob.move_to(jenny.coords)
|
|
|
|
for coords in you.coords, jenny.coords, new_coords_mob:
|
|
for entry in coords.get_entries():
|
|
entry.add_background_rectangle()
|
|
|
|
self.play(ShowCreation(v))
|
|
self.wait()
|
|
self.play(*it.chain(
|
|
list(map(FadeIn, [
|
|
self.plane, self.i_hat, self.j_hat,
|
|
self.i_hat.label, self.j_hat.label,
|
|
you
|
|
])),
|
|
list(map(Animation, [jenny, v])),
|
|
list(map(FadeOut, self.basis_vectors)),
|
|
))
|
|
self.play(
|
|
ShowCreation(you.bubble),
|
|
Write(you.coords)
|
|
)
|
|
self.play(you.change_mode, "speaking")
|
|
self.show_linear_combination(
|
|
self.v_coords,
|
|
basis_vectors = [self.i_hat, self.j_hat],
|
|
coord_mobs = you.coords.get_entries().copy(),
|
|
)
|
|
self.play(*it.chain(
|
|
list(map(FadeOut, [
|
|
self.plane, self.i_hat, self.j_hat,
|
|
self.i_hat.label, self.j_hat.label,
|
|
you.bubble, you.coords
|
|
])),
|
|
list(map(FadeIn, [self.jenny_plane, self.basis_vectors])),
|
|
list(map(Animation, [v, you, jenny])),
|
|
))
|
|
self.play(
|
|
ShowCreation(jenny.bubble),
|
|
Write(jenny.coords),
|
|
jenny.change_mode, "speaking",
|
|
)
|
|
self.play(you.change_mode, "erm")
|
|
self.show_linear_combination(
|
|
np.dot(self.inv_cob_matrix(), self.v_coords),
|
|
basis_vectors = [self.b1, self.b2],
|
|
coord_mobs = jenny.coords.get_entries().copy(),
|
|
)
|
|
self.play(
|
|
FadeOut(v),
|
|
jenny.change_mode, "plain"
|
|
)
|
|
self.play(
|
|
Transform(jenny.coords, new_coords_mob),
|
|
Blink(jenny),
|
|
)
|
|
self.hacked_show_linear_combination(
|
|
new_coords,
|
|
basis_vectors = [self.b1, self.b2],
|
|
coord_mobs = jenny.coords.get_entries().copy(),
|
|
show_sum_vect = True,
|
|
)
|
|
|
|
def hacked_show_linear_combination(
|
|
self, numerical_coords,
|
|
basis_vectors,
|
|
coord_mobs = None,
|
|
show_sum_vect = False,
|
|
sum_vect_color = V_COLOR,
|
|
):
|
|
for coord, basis, scalar in zip(coord_mobs, basis_vectors, numerical_coords):
|
|
basis.save_state()
|
|
basis.label.save_state()
|
|
basis.target = basis.copy().scale(scalar)
|
|
basis.label.target = basis.label.copy()
|
|
coord.target = coord.copy()
|
|
new_label = VGroup(coord.target, basis.label.target)
|
|
new_label.arrange(aligned_edge = DOWN)
|
|
new_label.move_to(
|
|
basis.label,
|
|
aligned_edge = basis.get_center()-basis.label.get_center()
|
|
)
|
|
new_label.shift(
|
|
basis.target.get_center() - basis.get_center()
|
|
)
|
|
coord.target.next_to(basis.label.target, LEFT)
|
|
coord.target.set_fill(basis.get_color(), opacity = 1)
|
|
self.play(*list(map(MoveToTarget, [
|
|
coord, basis, basis.label
|
|
])))
|
|
self.wait()
|
|
self.play(*[
|
|
ApplyMethod(m.shift, basis_vectors[0].get_end())
|
|
for m in self.get_mobjects_from_last_animation()
|
|
])
|
|
if show_sum_vect:
|
|
sum_vect = Vector(
|
|
basis_vectors[1].get_end(),
|
|
color = sum_vect_color
|
|
)
|
|
self.play(ShowCreation(sum_vect))
|
|
self.wait(2)
|
|
|
|
|
|
b1, b2 = basis_vectors
|
|
self.jenny_plane.save_state()
|
|
self.jenny.bubble.save_state()
|
|
|
|
self.jenny.coords.target = self.jenny.coords.copy()
|
|
self.you.bubble.add_content(self.jenny.coords.target)
|
|
|
|
x, y = numerical_coords
|
|
b1.target = self.i_hat.copy().scale(x)
|
|
b2.target = self.j_hat.copy().scale(y)
|
|
b2.target.shift(b1.target.get_end())
|
|
new_label1 = VGroup(coord_mobs[0], b1.label)
|
|
new_label2 = VGroup(coord_mobs[1], b2.label)
|
|
new_label1.target = new_label1.copy().next_to(b1.target, DOWN)
|
|
new_label2.target = new_label2.copy().next_to(b2.target, LEFT)
|
|
i_sym = TexMobject("\\hat{\\imath}").add_background_rectangle()
|
|
j_sym = TexMobject("\\hat{\\jmath}").add_background_rectangle()
|
|
i_sym.set_color(X_COLOR).move_to(new_label1.target[1], aligned_edge = LEFT)
|
|
j_sym.set_color(Y_COLOR).move_to(new_label2.target[1], aligned_edge = LEFT)
|
|
Transform(new_label1.target[1], i_sym).update(1)
|
|
Transform(new_label2.target[1], j_sym).update(1)
|
|
sum_vect.target = Vector(numerical_coords)
|
|
self.play(
|
|
Transform(self.jenny_plane, self.plane),
|
|
Transform(self.jenny.bubble, self.you.bubble),
|
|
self.you.change_mode, "speaking",
|
|
self.jenny.change_mode, "erm",
|
|
*list(map(MoveToTarget, [
|
|
self.jenny.coords,
|
|
b1, b2, new_label1, new_label2, sum_vect
|
|
]))
|
|
)
|
|
self.play(Blink(self.you))
|
|
self.wait()
|
|
|
|
self.play(*it.chain(
|
|
list(map(FadeOut, [
|
|
self.jenny.bubble, self.jenny.coords,
|
|
coord_mobs, sum_vect
|
|
])),
|
|
[
|
|
ApplyMethod(pi.change_mode, "plain")
|
|
for pi in (self.jenny, self.you)
|
|
],
|
|
[mob.restore for mob in (b1, b2, b1.label, b2.label)]
|
|
))
|
|
self.jenny.bubble.restore()
|
|
|
|
def how_we_label_her_basis(self):
|
|
you, jenny = self.you, self.jenny
|
|
b1_coords = Matrix(self.b1_coords)
|
|
b2_coords = Matrix(self.b2_coords)
|
|
for coords in b1_coords, b2_coords:
|
|
coords.add_to_back(BackgroundRectangle(coords))
|
|
coords.scale(0.7)
|
|
coords.add_to_back(BackgroundRectangle(coords))
|
|
you.bubble.add_content(coords)
|
|
coords.mover = coords.copy()
|
|
|
|
self.play(jenny.change_mode, "erm")
|
|
self.play(
|
|
ShowCreation(you.bubble),
|
|
Write(b1_coords),
|
|
you.change_mode, "speaking"
|
|
)
|
|
self.play(
|
|
b1_coords.mover.next_to, self.b1.get_end(), RIGHT,
|
|
b1_coords.mover.set_color, X_COLOR
|
|
)
|
|
self.play(Blink(you))
|
|
self.wait()
|
|
self.play(Transform(b1_coords, b2_coords))
|
|
self.play(
|
|
b2_coords.mover.next_to, self.b2.get_end(), LEFT,
|
|
b2_coords.mover.set_color, Y_COLOR
|
|
)
|
|
self.play(Blink(jenny))
|
|
for coords, array in (b1_coords, [1, 0]), (b2_coords, [0, 1]):
|
|
mover = coords.mover
|
|
array_mob = Matrix(array)
|
|
array_mob.set_color(mover.get_color())
|
|
array_mob.set_height(mover.get_height())
|
|
array_mob.move_to(mover)
|
|
array_mob.add_to_back(BackgroundRectangle(array_mob))
|
|
mover.target = array_mob
|
|
self.play(
|
|
self.jenny_plane.restore,
|
|
FadeOut(self.you.bubble),
|
|
FadeOut(b1_coords),
|
|
self.jenny.change_mode, "speaking",
|
|
self.you.change_mode, "confused",
|
|
*list(map(Animation, [
|
|
self.basis_vectors,
|
|
b1_coords.mover,
|
|
b2_coords.mover,
|
|
]))
|
|
)
|
|
self.play(MoveToTarget(b1_coords.mover))
|
|
self.play(MoveToTarget(b2_coords.mover))
|
|
self.play(Blink(self.jenny))
|
|
|
|
class SpeakingDifferentLanguages(JenniferScene):
|
|
def construct(self):
|
|
jenny, you = self.jenny, self.you
|
|
title = TextMobject("Different languages")
|
|
title.to_edge(UP)
|
|
|
|
vector = Vector([3, 2])
|
|
vector.center().shift(DOWN)
|
|
you.coords = Matrix([3, 2])
|
|
you.text = TextMobject("Looks to be")
|
|
jenny.coords = Matrix(["5/3", "1/3"])
|
|
jenny.text = TextMobject("Non, c'est")
|
|
for pi in jenny, you:
|
|
pi.bubble = pi.get_bubble(SpeechBubble, width = 4.5, height = 3.5)
|
|
if pi is you:
|
|
pi.bubble.shift(MED_SMALL_BUFF*RIGHT)
|
|
else:
|
|
pi.coords.scale(0.8)
|
|
pi.bubble.shift(MED_SMALL_BUFF*LEFT)
|
|
pi.coords.next_to(pi.text, buff = MED_SMALL_BUFF)
|
|
pi.coords.add(pi.text)
|
|
pi.bubble.add_content(pi.coords)
|
|
|
|
self.add(you, jenny)
|
|
self.play(Write(title))
|
|
self.play(
|
|
ShowCreation(vector),
|
|
you.look_at, vector,
|
|
jenny.look_at, vector,
|
|
)
|
|
for pi in you, jenny:
|
|
self.play(
|
|
pi.change_mode, "speaking" if pi is you else "sassy",
|
|
ShowCreation(pi.bubble),
|
|
Write(pi.coords)
|
|
)
|
|
self.play(Blink(pi))
|
|
self.wait()
|
|
|
|
class ShowGrid(LinearTransformationScene):
|
|
CONFIG = {
|
|
"include_background_plane" : False,
|
|
}
|
|
def construct(self):
|
|
self.remove(self.i_hat, self.j_hat)
|
|
self.wait()
|
|
self.plane.prepare_for_nonlinear_transform()
|
|
self.plane.save_state()
|
|
self.play(Homotopy(plane_wave_homotopy, self.plane))
|
|
self.play(self.plane.restore)
|
|
for vect in self.i_hat, self.j_hat:
|
|
self.play(ShowCreation(vect))
|
|
self.wait()
|
|
|
|
class GridIsAConstruct(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says("""
|
|
\\centering
|
|
The grid is
|
|
just a construct
|
|
""")
|
|
self.change_student_modes(*["pondering"]*3)
|
|
self.random_blink(2)
|
|
|
|
class SpaceHasNoGrid(LinearTransformationScene):
|
|
CONFIG = {
|
|
"include_background_plane" : False
|
|
}
|
|
def construct(self):
|
|
words = TextMobject("Space has no grid")
|
|
words.to_edge(UP)
|
|
self.play(
|
|
Write(words),
|
|
FadeOut(self.plane),
|
|
*list(map(Animation, [self.i_hat, self.j_hat]))
|
|
)
|
|
self.wait()
|
|
|
|
class JennysGrid(JenniferScene):
|
|
def construct(self):
|
|
self.add(self.jenny)
|
|
self.jenny.shift(3*RIGHT)
|
|
bubble = self.jenny.get_bubble(SpeechBubble, width = 4)
|
|
bubble.flip()
|
|
bubble.set_fill(BLACK, opacity = 0.8)
|
|
bubble.to_edge(LEFT)
|
|
bubble.write("""
|
|
This grid is also
|
|
just a construct
|
|
""")
|
|
coords = [1.5, -3]
|
|
coords_mob = Matrix(coords)
|
|
coords_mob.add_background_to_entries()
|
|
bubble.position_mobject_inside(coords_mob)
|
|
|
|
for vect in self.b1, self.b2:
|
|
self.play(
|
|
ShowCreation(vect),
|
|
Write(vect.label)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
ShowCreation(
|
|
self.jenny_plane,
|
|
run_time = 3,
|
|
lag_ratio = 0.5
|
|
),
|
|
self.jenny.change_mode, "speaking",
|
|
self.jenny.look_at, ORIGIN,
|
|
ShowCreation(bubble),
|
|
Write(bubble.content),
|
|
Animation(self.basis_vectors)
|
|
)
|
|
self.play(Blink(self.jenny))
|
|
self.play(
|
|
FadeOut(bubble.content),
|
|
FadeIn(coords_mob)
|
|
)
|
|
self.show_linear_combination(
|
|
numerical_coords = coords,
|
|
basis_vectors = [self.b1, self.b2],
|
|
coord_mobs = coords_mob.get_entries().copy(),
|
|
show_sum_vect = True
|
|
)
|
|
|
|
class ShowOriginOfGrid(JenniferScene):
|
|
def construct(self):
|
|
for plane in self.plane, self.jenny_plane:
|
|
plane.fade(0.3)
|
|
self.add(self.jenny_plane)
|
|
self.jenny_plane.save_state()
|
|
|
|
origin_word = TextMobject("Origin")
|
|
origin_word.shift(2*RIGHT+2.5*UP)
|
|
origin_word.add_background_rectangle()
|
|
arrow = Arrow(origin_word, ORIGIN, color = RED)
|
|
origin_dot = Dot(ORIGIN, radius = 0.1, color = RED)
|
|
coords = Matrix([0, 0])
|
|
coords.add_to_back(BackgroundRectangle(coords))
|
|
coords.next_to(ORIGIN, DOWN+LEFT)
|
|
vector = Vector([3, -2], color = PINK)
|
|
|
|
self.play(
|
|
Write(origin_word),
|
|
ShowCreation(arrow)
|
|
)
|
|
self.play(ShowCreation(origin_dot))
|
|
self.wait()
|
|
self.play(
|
|
Transform(self.jenny_plane, self.plane),
|
|
*list(map(Animation, [origin_word, origin_dot, arrow]))
|
|
)
|
|
self.wait()
|
|
self.play(Write(coords))
|
|
self.wait()
|
|
self.play(FadeIn(vector))
|
|
self.wait()
|
|
self.play(Transform(vector, Mobject.scale(vector.copy(), 0)))
|
|
self.wait()
|
|
self.play(
|
|
self.jenny_plane.restore,
|
|
*list(map(Animation, [origin_word, origin_dot, arrow, coords]))
|
|
)
|
|
for vect in self.b1, self.b2:
|
|
self.play(
|
|
ShowCreation(vect),
|
|
Write(vect.label)
|
|
)
|
|
self.wait()
|
|
|
|
class AskAboutTranslation(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says(
|
|
"\\centering How do you translate \\\\ between coordinate systems?",
|
|
target_mode = "raise_right_hand"
|
|
)
|
|
self.random_blink(3)
|
|
|
|
class TranslateFromJenny(JenniferScene):
|
|
CONFIG = {
|
|
"coords" : [-1, 2]
|
|
}
|
|
def construct(self):
|
|
self.add_players()
|
|
self.ask_question()
|
|
self.establish_coordinates()
|
|
self.perform_arithmetic()
|
|
|
|
def add_players(self):
|
|
for plane in self.jenny_plane, self.plane:
|
|
plane.fade()
|
|
self.add(
|
|
self.jenny_plane,
|
|
self.jenny, self.you,
|
|
self.basis_vectors
|
|
)
|
|
self.jenny.coords = Matrix(self.coords)
|
|
self.you.coords = Matrix(["?", "?"])
|
|
self.you.coords.get_entries().set_color_by_gradient(X_COLOR, Y_COLOR)
|
|
for pi in self.jenny, self.you:
|
|
pi.bubble = get_small_bubble(pi)
|
|
pi.bubble.set_fill(BLACK, opacity = 0.8)
|
|
pi.coords.scale(0.8)
|
|
pi.coords.add_background_to_entries()
|
|
pi.bubble.add_content(pi.coords)
|
|
|
|
def ask_question(self):
|
|
self.play(
|
|
self.jenny.change_mode, "pondering",
|
|
ShowCreation(self.jenny.bubble),
|
|
Write(self.jenny.coords)
|
|
)
|
|
coord_mobs = self.jenny.coords.get_entries().copy()
|
|
self.basis_vectors_copy = self.basis_vectors.copy()
|
|
self.basis_vectors_copy.fade(0.3)
|
|
self.add(self.basis_vectors_copy, self.basis_vectors)
|
|
sum_vect = self.show_linear_combination(
|
|
numerical_coords = self.coords,
|
|
basis_vectors = [self.b1, self.b2],
|
|
coord_mobs = coord_mobs,
|
|
revert_to_original = False,
|
|
show_sum_vect = True,
|
|
)
|
|
self.wait()
|
|
everything = self.get_mobjects()
|
|
for submob in self.jenny_plane.get_family():
|
|
everything.remove(submob)
|
|
self.play(
|
|
Transform(self.jenny_plane, self.plane),
|
|
*list(map(Animation, everything))
|
|
)
|
|
self.play(
|
|
self.you.change_mode, "confused",
|
|
ShowCreation(self.you.bubble),
|
|
Write(self.you.coords)
|
|
)
|
|
self.wait()
|
|
|
|
def establish_coordinates(self):
|
|
b1, b2 = self.basis_vectors_copy[:2]
|
|
b1_coords = Matrix(self.b1_coords).set_color(X_COLOR)
|
|
b2_coords = Matrix(self.b2_coords).set_color(Y_COLOR)
|
|
for coords in b1_coords, b2_coords:
|
|
coords.scale(0.7)
|
|
coords.add_to_back(BackgroundRectangle(coords))
|
|
b1_coords.next_to(b1.get_end(), RIGHT)
|
|
b2_coords.next_to(b2.get_end(), UP)
|
|
|
|
for coords in b1_coords, b2_coords:
|
|
self.play(Write(coords))
|
|
self.b1_coords_mob, self.b2_coords_mob = b1_coords, b2_coords
|
|
|
|
def perform_arithmetic(self):
|
|
jenny_x, jenny_y = self.jenny.coords.get_entries().copy()
|
|
equals, plus, equals2 = syms = list(map(TexMobject, list("=+=")))
|
|
result = Matrix([-4, 1])
|
|
result.set_height(self.you.coords.get_height())
|
|
for mob in syms + [self.you.coords, self.jenny.coords, result]:
|
|
mob.add_to_back(BackgroundRectangle(mob))
|
|
movers = [
|
|
self.you.coords, equals,
|
|
jenny_x, self.b1_coords_mob, plus,
|
|
jenny_y, self.b2_coords_mob,
|
|
equals2, result
|
|
]
|
|
for mover in movers:
|
|
mover.target = mover.copy()
|
|
mover_targets = VGroup(*[mover.target for mover in movers])
|
|
mover_targets.arrange()
|
|
mover_targets.to_edge(UP)
|
|
for mob in syms + [result]:
|
|
mob.move_to(mob.target)
|
|
mob.set_fill(BLACK, opacity = 0)
|
|
|
|
mover_sets = [
|
|
[jenny_x, self.b1_coords_mob],
|
|
[plus, jenny_y, self.b2_coords_mob],
|
|
[self.you.coords, equals],
|
|
]
|
|
for mover_set in mover_sets:
|
|
self.play(*list(map(MoveToTarget, mover_set)))
|
|
self.wait()
|
|
self.play(
|
|
MoveToTarget(equals2),
|
|
Transform(self.b1_coords_mob.copy(), result.target),
|
|
Transform(self.b2_coords_mob.copy(), result.target),
|
|
)
|
|
self.remove(*self.get_mobjects_from_last_animation())
|
|
result = result.target
|
|
self.add(equals2, result)
|
|
self.wait()
|
|
|
|
result_copy = result.copy()
|
|
self.you.bubble.add_content(result_copy)
|
|
self.play(
|
|
self.you.change_mode, "hooray",
|
|
Transform(result.copy(), result_copy)
|
|
)
|
|
self.play(Blink(self.you))
|
|
self.wait()
|
|
|
|
matrix = Matrix(np.array([self.b1_coords, self.b2_coords]).T)
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
self.jenny.coords.target = self.jenny.coords.copy()
|
|
self.jenny.coords.target.next_to(equals, LEFT)
|
|
matrix.set_height(self.jenny.coords.get_height())
|
|
matrix.next_to(self.jenny.coords.target, LEFT)
|
|
matrix.add_to_back(BackgroundRectangle(matrix))
|
|
|
|
self.play(
|
|
FadeOut(self.jenny.bubble),
|
|
FadeOut(self.you.coords),
|
|
self.jenny.change_mode, "plain",
|
|
MoveToTarget(self.jenny.coords),
|
|
FadeIn(matrix)
|
|
)
|
|
self.wait()
|
|
|
|
class WatchChapter3(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says("""
|
|
You've all watched
|
|
chapter 3, right?
|
|
""")
|
|
self.random_blink()
|
|
self.play(
|
|
self.get_students()[0].look, LEFT,
|
|
self.get_students()[1].change_mode, "happy",
|
|
self.get_students()[2].change_mode, "happy",
|
|
)
|
|
self.random_blink(2)
|
|
|
|
class TalkThroughChangeOfBasisMatrix(JenniferScene):
|
|
def construct(self):
|
|
self.add(self.plane, self.jenny, self.you)
|
|
self.plane.fade()
|
|
self.jenny_plane.fade()
|
|
for pi in self.jenny, self.you:
|
|
pi.bubble = get_small_bubble(pi)
|
|
|
|
matrix = Matrix(np.array([self.b1_coords, self.b2_coords]).T)
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
matrix.next_to(ORIGIN, RIGHT, buff = MED_SMALL_BUFF).to_edge(UP)
|
|
|
|
b1_coords = Matrix(self.b1_coords)
|
|
b1_coords.set_color(X_COLOR)
|
|
b1_coords.next_to(self.b1.get_end(), RIGHT)
|
|
b2_coords = Matrix(self.b2_coords)
|
|
b2_coords.set_color(Y_COLOR)
|
|
b2_coords.next_to(self.b2.get_end(), UP)
|
|
for coords in b1_coords, b2_coords:
|
|
coords.scale_in_place(0.7)
|
|
|
|
basis_coords_pair = VGroup(
|
|
Matrix([1, 0]).set_color(X_COLOR).scale(0.7),
|
|
TexMobject(","),
|
|
Matrix([0, 1]).set_color(Y_COLOR).scale(0.7),
|
|
)
|
|
basis_coords_pair.arrange(aligned_edge = DOWN)
|
|
self.you.bubble.add_content(basis_coords_pair)
|
|
|
|
t_matrix1 = np.array([self.b1_coords, [0, 1]])
|
|
t_matrix2 = np.dot(
|
|
self.cob_matrix(),
|
|
np.linalg.inv(t_matrix1.T)
|
|
).T
|
|
|
|
for mob in matrix, b1_coords, b2_coords:
|
|
mob.rect = BackgroundRectangle(mob)
|
|
mob.add_to_back(mob.rect)
|
|
|
|
self.play(Write(matrix))
|
|
for vect in self.i_hat, self.j_hat:
|
|
self.play(
|
|
ShowCreation(vect),
|
|
Write(vect.label)
|
|
)
|
|
self.play(
|
|
self.you.change_mode, "pondering",
|
|
ShowCreation(self.you.bubble),
|
|
Write(basis_coords_pair)
|
|
)
|
|
self.play(Blink(self.you))
|
|
self.wait()
|
|
|
|
self.add_foreground_mobject(
|
|
self.jenny, self.you, self.you.bubble,
|
|
basis_coords_pair, matrix
|
|
)
|
|
matrix_copy = matrix.copy()
|
|
matrix_copy.rect.set_fill(opacity = 0)
|
|
self.apply_transposed_matrix(
|
|
t_matrix1,
|
|
added_anims = [
|
|
Transform(self.i_hat, self.b1),
|
|
Transform(self.i_hat.label, self.b1.label),
|
|
Transform(matrix_copy.rect, b1_coords.rect),
|
|
Transform(
|
|
matrix_copy.get_brackets(),
|
|
b1_coords.get_brackets(),
|
|
),
|
|
Transform(
|
|
VGroup(*matrix_copy.get_mob_matrix()[:,0]),
|
|
b1_coords.get_entries()
|
|
),
|
|
]
|
|
)
|
|
self.remove(matrix_copy)
|
|
self.add_foreground_mobject(b1_coords)
|
|
matrix_copy = matrix.copy()
|
|
matrix_copy.rect.set_fill(opacity = 0)
|
|
self.apply_transposed_matrix(
|
|
t_matrix2,
|
|
added_anims = [
|
|
Transform(self.j_hat, self.b2),
|
|
Transform(self.j_hat.label, self.b2.label),
|
|
Transform(matrix_copy.rect, b2_coords.rect),
|
|
Transform(
|
|
matrix_copy.get_brackets(),
|
|
b2_coords.get_brackets(),
|
|
),
|
|
Transform(
|
|
VGroup(*matrix_copy.get_mob_matrix()[:,1]),
|
|
b2_coords.get_entries()
|
|
),
|
|
]
|
|
)
|
|
self.remove(matrix_copy)
|
|
self.add_foreground_mobject(b2_coords)
|
|
basis_coords_pair.target = basis_coords_pair.copy()
|
|
self.jenny.bubble.add_content(basis_coords_pair.target)
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(b1_coords),
|
|
FadeOut(b2_coords),
|
|
self.jenny.change_mode, "speaking",
|
|
Transform(self.you.bubble, self.jenny.bubble),
|
|
MoveToTarget(basis_coords_pair),
|
|
)
|
|
|
|
class ChangeOfBasisExample(JenniferScene):
|
|
CONFIG = {
|
|
"v_coords" : [-1, 2]
|
|
}
|
|
def construct(self):
|
|
self.add(
|
|
self.plane, self.i_hat, self.j_hat,
|
|
self.i_hat.label, self.j_hat.label,
|
|
)
|
|
self.j_hat.label.next_to(self.j_hat, RIGHT)
|
|
v = self.add_vector(self.v_coords)
|
|
v_coords = Matrix(self.v_coords)
|
|
v_coords.scale(0.8)
|
|
v_coords.add_to_back(BackgroundRectangle(v_coords))
|
|
v_coords.to_corner(UP+LEFT)
|
|
v_coords.add_background_to_entries()
|
|
for pi in self.you, self.jenny:
|
|
pi.change_mode("pondering")
|
|
pi.bubble = get_small_bubble(pi)
|
|
pi.bubble.add_content(v_coords.copy())
|
|
pi.add(pi.bubble, pi.bubble.content)
|
|
|
|
start_words = TextMobject("How", "we", "think of")
|
|
start_words.add_background_rectangle()
|
|
start_group = VGroup(start_words, v_coords)
|
|
start_group.arrange(buff = MED_SMALL_BUFF)
|
|
start_group.next_to(self.you, LEFT, buff = 0)
|
|
start_group.to_edge(UP)
|
|
end_words = TextMobject("How", "Jennifer", "thinks of")
|
|
end_words.add_background_rectangle()
|
|
end_words.move_to(start_words, aligned_edge = RIGHT)
|
|
|
|
|
|
self.play(
|
|
Write(start_group),
|
|
FadeIn(self.you),
|
|
)
|
|
self.add_foreground_mobject(start_group, self.you)
|
|
|
|
self.show_linear_combination(
|
|
numerical_coords = self.v_coords,
|
|
basis_vectors = [self.i_hat, self.j_hat],
|
|
coord_mobs = v_coords.get_entries().copy(),
|
|
)
|
|
self.play(*list(map(FadeOut, [self.i_hat.label, self.j_hat.label])))
|
|
self.apply_transposed_matrix(self.cob_matrix().T)
|
|
VGroup(self.i_hat, self.j_hat).fade()
|
|
self.add(self.b1, self.b2)
|
|
self.play(
|
|
Transform(start_words, end_words),
|
|
Transform(self.you, self.jenny),
|
|
*list(map(Write, [self.b1.label, self.b2.label]))
|
|
)
|
|
self.play(Blink(self.you))
|
|
self.show_linear_combination(
|
|
numerical_coords = self.v_coords,
|
|
basis_vectors = [self.b1, self.b2],
|
|
coord_mobs = v_coords.get_entries().copy(),
|
|
)
|
|
|
|
class FeelsBackwards(Scene):
|
|
def construct(self):
|
|
matrix = Matrix(np.array([
|
|
JenniferScene.CONFIG["b1_coords"],
|
|
JenniferScene.CONFIG["b2_coords"],
|
|
]).T)
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
matrix.shift(UP)
|
|
top_arrow = Arrow(matrix.get_left(), matrix.get_right())
|
|
bottom_arrow = top_arrow.copy().rotate(np.pi)
|
|
top_arrow.next_to(matrix, UP, buff = LARGE_BUFF)
|
|
bottom_arrow.next_to(matrix, DOWN, buff = LARGE_BUFF)
|
|
top_arrow.set_color(BLUE)
|
|
|
|
jenny_grid = TextMobject("Jennifer's grid").set_color(BLUE)
|
|
our_grid = TextMobject("Our grid").set_color(BLUE)
|
|
jenny_language = TextMobject("Jennifer's language")
|
|
our_language = TextMobject("Our language")
|
|
|
|
our_grid.next_to(top_arrow, LEFT)
|
|
jenny_grid.next_to(top_arrow, RIGHT)
|
|
jenny_language.next_to(bottom_arrow, RIGHT)
|
|
our_language.next_to(bottom_arrow, LEFT)
|
|
|
|
self.add(matrix)
|
|
self.play(Write(our_grid))
|
|
self.play(
|
|
ShowCreation(top_arrow),
|
|
Write(jenny_grid)
|
|
)
|
|
self.wait()
|
|
self.play(Write(jenny_language))
|
|
self.play(
|
|
ShowCreation(bottom_arrow),
|
|
Write(our_language)
|
|
)
|
|
self.wait()
|
|
|
|
##Swap things
|
|
inverse_word = TextMobject("Inverse")
|
|
inverse_word.next_to(matrix, LEFT, buff = MED_SMALL_BUFF)
|
|
inverse_exponent = TexMobject("-1")
|
|
inverse_exponent.next_to(matrix.get_corner(UP+RIGHT), RIGHT)
|
|
self.play(*list(map(Write, [inverse_word, inverse_exponent])))
|
|
self.play(
|
|
Swap(jenny_grid, our_grid),
|
|
top_arrow.scale_in_place, 0.8,
|
|
top_arrow.shift, 0.8*RIGHT,
|
|
top_arrow.set_color, BLUE,
|
|
)
|
|
self.play(
|
|
Swap(jenny_language, our_language),
|
|
bottom_arrow.scale_in_place, 0.8,
|
|
bottom_arrow.shift, 0.8*RIGHT
|
|
)
|
|
self.wait()
|
|
|
|
class AskAboutOtherWayAround(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.student_says("""
|
|
What about the
|
|
other way around?
|
|
""")
|
|
self.random_blink(3)
|
|
|
|
class RecallInverse(JenniferScene):
|
|
def construct(self):
|
|
numerical_t_matrix = np.array([self.b1_coords, self.b2_coords])
|
|
matrix = Matrix(numerical_t_matrix.T)
|
|
matrix.add_to_back(BackgroundRectangle(matrix))
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
matrix.to_corner(UP+LEFT, buff = MED_LARGE_BUFF)
|
|
# matrix.shift(MED_SMALL_BUFF*DOWN)
|
|
inverse_exponent = TexMobject("-1")
|
|
inverse_exponent.next_to(matrix.get_corner(UP+RIGHT), RIGHT)
|
|
inverse_exponent.add_background_rectangle()
|
|
brace = Brace(VGroup(matrix, inverse_exponent))
|
|
inverse_word = brace.get_text("Inverse")
|
|
inverse_word.add_background_rectangle()
|
|
|
|
equals = TexMobject("=")
|
|
equals.add_background_rectangle()
|
|
inv_matrix = Matrix([
|
|
["1/3", "1/3"],
|
|
["-1/3", "2/3"]
|
|
])
|
|
inv_matrix.set_height(matrix.get_height())
|
|
inv_matrix.add_to_back(BackgroundRectangle(inv_matrix))
|
|
equals.next_to(matrix, RIGHT, buff = 0.7)
|
|
inv_matrix.next_to(equals, RIGHT, buff = MED_SMALL_BUFF)
|
|
|
|
self.add_foreground_mobject(matrix)
|
|
self.apply_transposed_matrix(numerical_t_matrix)
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(inverse_word),
|
|
Write(inverse_exponent)
|
|
)
|
|
self.add_foreground_mobject(*self.get_mobjects_from_last_animation())
|
|
self.wait()
|
|
self.apply_inverse_transpose(numerical_t_matrix)
|
|
self.wait()
|
|
self.play(
|
|
Write(equals),
|
|
Transform(matrix.copy(), inv_matrix)
|
|
)
|
|
self.remove(*self.get_mobjects_from_last_animation())
|
|
self.add_foreground_mobject(equals, inv_matrix)
|
|
self.wait()
|
|
for mob in self.plane, self.i_hat, self.j_hat:
|
|
self.add(mob.copy().fade(0.7))
|
|
self.apply_transposed_matrix(numerical_t_matrix)
|
|
self.play(FadeIn(self.jenny))
|
|
self.play(self.jenny.change_mode, "speaking")
|
|
#Little hacky now
|
|
inv_matrix.set_column_colors(X_COLOR)
|
|
self.play(*[
|
|
ApplyMethod(
|
|
mob.scale_in_place, 1.2,
|
|
rate_func = there_and_back
|
|
)
|
|
for mob in inv_matrix.get_mob_matrix()[:,0]
|
|
])
|
|
self.wait()
|
|
inv_matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
self.play(*[
|
|
ApplyMethod(
|
|
mob.scale_in_place, 1.2,
|
|
rate_func = there_and_back
|
|
)
|
|
for mob in inv_matrix.get_mob_matrix()[:,1]
|
|
])
|
|
self.wait()
|
|
|
|
class WorkOutInverseComputation(Scene):
|
|
def construct(self):
|
|
our_vector = Matrix([3, 2])
|
|
her_vector = Matrix(["5/3", "1/3"])
|
|
matrix = Matrix([["1/3", "1/3"], ["-1/3", "2/3"]])
|
|
our_vector.set_color(BLUE_D)
|
|
her_vector.set_color(MAROON_B)
|
|
equals = TexMobject("=")
|
|
equation = VGroup(
|
|
matrix, our_vector, equals, her_vector
|
|
)
|
|
for mob in equation:
|
|
if isinstance(mob, Matrix):
|
|
mob.set_height(2)
|
|
equation.arrange()
|
|
|
|
matrix_brace = Brace(matrix, UP)
|
|
our_vector_brace = Brace(our_vector)
|
|
her_vector_brace = Brace(her_vector, UP)
|
|
matrix_text = matrix_brace.get_text("""
|
|
\\centering
|
|
Inverse
|
|
change of basis
|
|
matrix
|
|
""")
|
|
our_text = our_vector_brace.get_text("""
|
|
\\centering
|
|
Written in
|
|
our language
|
|
""")
|
|
our_text.set_color(our_vector.get_color())
|
|
her_text = her_vector_brace.get_text("""
|
|
\\centering
|
|
Same vector
|
|
in her language
|
|
""")
|
|
her_text.set_color(her_vector.get_color())
|
|
for text in our_text, her_text:
|
|
text.scale_in_place(0.7)
|
|
|
|
self.add(our_vector)
|
|
self.play(
|
|
GrowFromCenter(our_vector_brace),
|
|
Write(our_text)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(matrix),
|
|
GrowFromCenter(matrix_brace),
|
|
Write(matrix_text)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Write(equals),
|
|
Write(her_vector)
|
|
)
|
|
self.play(
|
|
GrowFromCenter(her_vector_brace),
|
|
Write(her_text)
|
|
)
|
|
self.wait()
|
|
|
|
class SoThatsTranslation(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says("So that's translation")
|
|
self.random_blink(3)
|
|
|
|
class SummarizeTranslationProcess(Scene):
|
|
def construct(self):
|
|
self.define_matrix()
|
|
self.show_translation()
|
|
|
|
def define_matrix(self):
|
|
matrix = Matrix([[2, -1], [1, 1]])
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
A, equals = list(map(TexMobject, list("A=")))
|
|
equation = VGroup(A, equals, matrix)
|
|
equation.arrange()
|
|
equation.to_corner(UP+LEFT)
|
|
equation.shift(RIGHT)
|
|
words = TextMobject("""
|
|
Jennifer's basis vectors,
|
|
written in our coordinates
|
|
""")
|
|
words.to_edge(LEFT)
|
|
mob_matrix = matrix.get_mob_matrix()
|
|
arrow1 = Arrow(words, mob_matrix[1, 0], color = X_COLOR)
|
|
arrow2 = Arrow(words, mob_matrix[1, 1], color = Y_COLOR)
|
|
|
|
self.add(A, equals, matrix)
|
|
self.play(
|
|
Write(words),
|
|
*list(map(ShowCreation, [arrow1, arrow2]))
|
|
)
|
|
self.A_copy = A.copy()
|
|
|
|
def show_translation(self):
|
|
our_vector = Matrix(["x_o", "y_o"])
|
|
her_vector = Matrix(["x_j", "y_j"])
|
|
for vector, color in (our_vector, BLUE_D), (her_vector, MAROON_B):
|
|
# vector.set_height(1.5)
|
|
vector.set_color(color)
|
|
A = TexMobject("A")
|
|
A_inv = TexMobject("A^{-1}")
|
|
equals = TexMobject("=")
|
|
|
|
equation = VGroup(A, her_vector, equals, our_vector)
|
|
equation.arrange()
|
|
equation.to_edge(RIGHT)
|
|
equation.shift(0.5*UP)
|
|
A_inv.next_to(our_vector, LEFT)
|
|
|
|
her_words = TextMobject("Vector in her coordinates")
|
|
her_words.set_color(her_vector.get_color())
|
|
her_words.scale(0.8).to_corner(UP+RIGHT)
|
|
her_arrow = Arrow(
|
|
her_words, her_vector,
|
|
color = her_vector.get_color()
|
|
)
|
|
our_words = TextMobject("Same vector in\\\\ our coordinates")
|
|
our_words.set_color(our_vector.get_color())
|
|
our_words.scale(0.8).to_edge(RIGHT).shift(2*DOWN)
|
|
our_words.shift_onto_screen()
|
|
our_arrow = Arrow(
|
|
our_words.get_top(), our_vector.get_bottom(),
|
|
color = our_vector.get_color()
|
|
)
|
|
|
|
self.play(
|
|
Write(equation),
|
|
Transform(self.A_copy, A)
|
|
)
|
|
self.remove(self.A_copy)
|
|
self.play(
|
|
Write(her_words),
|
|
ShowCreation(her_arrow)
|
|
)
|
|
self.play(
|
|
Write(our_words),
|
|
ShowCreation(our_arrow)
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
VGroup(her_vector, equals).next_to, A_inv, LEFT,
|
|
her_arrow.rotate_in_place, -np.pi/6,
|
|
her_arrow.shift, MED_SMALL_BUFF*LEFT,
|
|
Transform(A, A_inv, path_arc = np.pi)
|
|
)
|
|
self.wait()
|
|
|
|
class VectorsAreNotTheOnlyOnes(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher_says("""
|
|
\\centering
|
|
Vectors aren't the
|
|
only thing with coordinates
|
|
""")
|
|
self.change_student_modes("pondering", "confused", "erm")
|
|
self.random_blink(3)
|
|
|
|
class Prerequisites(Scene):
|
|
def construct(self):
|
|
title = TextMobject("Prerequisites")
|
|
title.to_edge(UP)
|
|
h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
|
|
h_line.next_to(title, DOWN)
|
|
|
|
self.add(title, h_line)
|
|
prereqs = list(map(TextMobject, [
|
|
"Linear transformations",
|
|
"Matrix multiplication",
|
|
]))
|
|
for direction, words in zip([LEFT, RIGHT], prereqs):
|
|
rect = Rectangle(height = 9, width = 16)
|
|
rect.set_height(3.5)
|
|
rect.next_to(ORIGIN, direction, buff = MED_SMALL_BUFF)
|
|
rect.set_color(BLUE)
|
|
words.next_to(rect, UP, buff = MED_SMALL_BUFF)
|
|
self.play(
|
|
Write(words),
|
|
ShowCreation(rect)
|
|
)
|
|
self.wait()
|
|
|
|
class RotationExample(LinearTransformationScene):
|
|
CONFIG = {
|
|
"t_matrix" : [[0, 1], [-1, 0]]
|
|
}
|
|
def construct(self):
|
|
words = TextMobject("$90^\\circ$ rotation")
|
|
words.scale(1.2)
|
|
words.add_background_rectangle()
|
|
words.to_edge(UP)
|
|
|
|
matrix = Matrix(self.t_matrix.T)
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
matrix.rect = BackgroundRectangle(matrix)
|
|
matrix.add_to_back(matrix.rect)
|
|
matrix.next_to(words, DOWN)
|
|
matrix.shift(2*RIGHT)
|
|
|
|
self.play(Write(words))
|
|
self.add_foreground_mobject(words)
|
|
self.wait()
|
|
self.apply_transposed_matrix(self.t_matrix)
|
|
self.wait()
|
|
self.play(
|
|
self.i_hat.rotate, np.pi/12,
|
|
self.j_hat.rotate, -np.pi/12,
|
|
rate_func = wiggle,
|
|
run_time = 2
|
|
)
|
|
self.wait()
|
|
|
|
i_coords, j_coords = coord_arrays = list(map(Matrix, self.t_matrix))
|
|
for coords, vect in zip(coord_arrays, [self.i_hat, self.j_hat]):
|
|
coords.scale(0.7)
|
|
coords.rect = BackgroundRectangle(coords)
|
|
coords.add_to_back(coords.rect)
|
|
coords.set_color(vect.get_color())
|
|
direction = UP if vect is self.j_hat else RIGHT
|
|
coords.next_to(vect.get_end(), direction, buff = MED_SMALL_BUFF)
|
|
self.play(Write(coords))
|
|
self.wait()
|
|
|
|
self.play(
|
|
Transform(i_coords.rect, matrix.rect),
|
|
Transform(i_coords.get_brackets(), matrix.get_brackets()),
|
|
Transform(
|
|
i_coords.get_entries(),
|
|
VGroup(*matrix.get_mob_matrix()[:, 0])
|
|
),
|
|
)
|
|
self.play(
|
|
FadeOut(j_coords.rect),
|
|
FadeOut(j_coords.get_brackets()),
|
|
Transform(
|
|
j_coords.get_entries(),
|
|
VGroup(*matrix.get_mob_matrix()[:, 1])
|
|
),
|
|
)
|
|
self.wait()
|
|
self.add_words(matrix)
|
|
|
|
def add_words(self, matrix):
|
|
follow_basis = TextMobject(
|
|
"Follow", "our choice",
|
|
"\\\\ of basis vectors"
|
|
)
|
|
follow_basis.set_color_by_tex("our choice", YELLOW)
|
|
follow_basis.add_background_rectangle()
|
|
follow_basis.next_to(
|
|
matrix, LEFT,
|
|
buff = MED_SMALL_BUFF,
|
|
)
|
|
|
|
record = TextMobject(
|
|
"Record using \\\\",
|
|
"our coordinates"
|
|
)
|
|
record.set_color_by_tex("our coordinates", YELLOW)
|
|
record.add_background_rectangle()
|
|
record.next_to(
|
|
matrix, DOWN,
|
|
buff = MED_SMALL_BUFF,
|
|
aligned_edge = LEFT
|
|
)
|
|
|
|
self.play(Write(follow_basis))
|
|
self.wait()
|
|
self.play(Write(record))
|
|
self.wait()
|
|
|
|
class JennyWatchesRotation(JenniferScene):
|
|
def construct(self):
|
|
jenny = self.jenny
|
|
self.add(self.jenny_plane.copy().fade())
|
|
self.add(self.jenny_plane)
|
|
self.add(jenny)
|
|
for vect in self.b1, self.b2:
|
|
self.add_vector(vect)
|
|
|
|
matrix = Matrix([["?", "?"], ["?", "?"]])
|
|
matrix.get_entries().set_color_by_gradient(X_COLOR, Y_COLOR)
|
|
jenny.bubble = get_small_bubble(jenny)
|
|
jenny.bubble.add_content(matrix)
|
|
matrix.scale_in_place(0.8)
|
|
|
|
self.play(
|
|
jenny.change_mode, "sassy",
|
|
ShowCreation(jenny.bubble),
|
|
Write(matrix)
|
|
)
|
|
self.play(*it.chain(
|
|
[
|
|
Rotate(mob, np.pi/2, run_time = 3)
|
|
for mob in (self.jenny_plane, self.b1, self.b2)
|
|
],
|
|
list(map(Animation, [jenny, jenny.bubble, matrix]))
|
|
))
|
|
self.play(jenny.change_mode, "pondering")
|
|
self.play(Blink(jenny))
|
|
self.wait()
|
|
|
|
class AksAboutTranslatingColumns(TeacherStudentsScene):
|
|
def construct(self):
|
|
matrix = Matrix([[0, -1], [1, 0]])
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
matrix.scale(0.7)
|
|
words = TextMobject("Translate columns of")
|
|
matrix.next_to(words, DOWN)
|
|
words.add(matrix)
|
|
self.student_says(words, student_index = 0)
|
|
self.random_blink(2)
|
|
|
|
student = self.get_students()[0]
|
|
bubble = get_small_bubble(student)
|
|
bubble.set_fill(opacity = 0)
|
|
matrix.target = matrix.copy()
|
|
bubble.add_content(matrix.target)
|
|
self.play(
|
|
Transform(student.bubble, bubble),
|
|
FadeOut(student.bubble.content),
|
|
MoveToTarget(matrix.copy()),
|
|
student.change_mode, "pondering",
|
|
)
|
|
self.remove(student.bubble)
|
|
student.bubble = None
|
|
self.add(bubble, matrix.target)
|
|
|
|
self.random_blink()
|
|
words = TextMobject(
|
|
"\\centering Those columns still \\\\ represent ",
|
|
"our basis", ", not ", "hers",
|
|
arg_separator = ""
|
|
)
|
|
words.set_color_by_tex("our basis", BLUE)
|
|
words.set_color_by_tex("hers", MAROON_B)
|
|
self.teacher_says(words)
|
|
self.change_student_modes("erm", "pondering", "pondering")
|
|
self.random_blink()
|
|
|
|
class HowToTranslateAMatrix(Scene):
|
|
def construct(self):
|
|
self.add_title()
|
|
|
|
arrays = VGroup(*list(map(Matrix, [
|
|
[["1/3", "-2/3"], ["5/3", "-1/3"]],
|
|
[-1, 2],
|
|
[[2, -1], [1, 1]],
|
|
[[0, -1], [1, 0]],
|
|
[[2, -1], [1, 1]],
|
|
])))
|
|
result, her_vector, cob_matrix, transform, inv_cob = arrays
|
|
neg_1 = TexMobject("-1")
|
|
neg_1.next_to(inv_cob.get_corner(UP+RIGHT), RIGHT)
|
|
inv_cob.add(neg_1)
|
|
arrays.arrange(LEFT)
|
|
arrays.to_edge(LEFT, buff = LARGE_BUFF/2.)
|
|
for array in arrays:
|
|
array.brace = Brace(array)
|
|
array.top_brace = Brace(VGroup(array, her_vector), UP)
|
|
for array in cob_matrix, inv_cob:
|
|
submobs = array.split()
|
|
submobs.sort(key=lambda m: m.get_center()[0])
|
|
array.submobjects = submobs
|
|
her_vector.set_color(MAROON_B)
|
|
cob_matrix.set_color_by_gradient(BLUE, MAROON_B)
|
|
transform.set_column_colors(X_COLOR, Y_COLOR)
|
|
transform.get_brackets().set_color(BLUE)
|
|
inv_cob.set_color_by_gradient(MAROON_B, BLUE)
|
|
result.set_column_colors(X_COLOR, Y_COLOR)
|
|
result.get_brackets().set_color(MAROON_B)
|
|
|
|
final_top_brace = Brace(VGroup(cob_matrix, inv_cob), UP)
|
|
|
|
brace_text_pairs = [
|
|
(her_vector.brace, ("Vector in \\\\", "Jennifer's language")),
|
|
(her_vector.top_brace, ("",)),
|
|
(cob_matrix.brace, ("Change of basis \\\\", "matrix")),
|
|
(cob_matrix.top_brace, ("Same vector \\\\", "in", "our", "language")),
|
|
(transform.brace, ("Transformation matrix \\\\", "in", "our", "language")),
|
|
(transform.top_brace, ("Transformed vector \\\\", "in", "our", "language")),
|
|
(inv_cob.brace, ("Inverse \\\\", "change of basis \\\\", "matrix")),
|
|
(inv_cob.top_brace, ("Transformed vector \\\\", "in", "her", "language")),
|
|
(final_top_brace, ("Transformation matrix \\\\", "in", "her", "language"))
|
|
]
|
|
for brace, text_args in brace_text_pairs:
|
|
text_args = list(text_args)
|
|
text_args[0] = "\\centering " + text_args[0]
|
|
text = TextMobject(*text_args)
|
|
text.set_color_by_tex("our", BLUE)
|
|
text.set_color_by_tex("her", MAROON_B)
|
|
brace.put_at_tip(text)
|
|
brace.text = text
|
|
|
|
brace = her_vector.brace
|
|
bottom_words = her_vector.brace.text
|
|
top_brace = cob_matrix.top_brace
|
|
top_words = cob_matrix.top_brace.text
|
|
def introduce(array):
|
|
self.play(
|
|
Write(array),
|
|
Transform(brace, array.brace),
|
|
Transform(bottom_words, array.brace.text)
|
|
)
|
|
self.wait()
|
|
def echo_introduce(array):
|
|
self.play(
|
|
Transform(top_brace, array.top_brace),
|
|
Transform(top_words, array.top_brace.text)
|
|
)
|
|
self.wait()
|
|
|
|
self.play(Write(her_vector))
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(bottom_words)
|
|
)
|
|
self.wait()
|
|
introduce(cob_matrix)
|
|
self.play(
|
|
GrowFromCenter(top_brace),
|
|
Write(top_words)
|
|
)
|
|
self.wait()
|
|
introduce(transform)
|
|
echo_introduce(transform)
|
|
introduce(inv_cob),
|
|
echo_introduce(inv_cob)
|
|
|
|
#Genearlize to single matrix
|
|
v = TexMobject("\\vec{\\textbf{v}}")
|
|
v.set_color(her_vector.get_color())
|
|
v.move_to(her_vector, aligned_edge = LEFT)
|
|
self.play(
|
|
Transform(her_vector, v),
|
|
FadeOut(bottom_words),
|
|
FadeOut(brace),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Transform(top_brace, final_top_brace),
|
|
Transform(top_brace.text, final_top_brace.text),
|
|
)
|
|
self.wait()
|
|
|
|
equals = TexMobject("=")
|
|
equals.replace(v)
|
|
result.next_to(equals, RIGHT)
|
|
self.play(
|
|
Transform(her_vector, equals),
|
|
Write(result)
|
|
)
|
|
self.wait(2)
|
|
|
|
everything = VGroup(*self.get_mobjects())
|
|
self.play(
|
|
FadeOut(everything),
|
|
result.to_corner, UP+LEFT
|
|
)
|
|
self.add(result)
|
|
self.wait()
|
|
|
|
|
|
def add_title(self):
|
|
title = TextMobject("How to translate a matrix")
|
|
title.to_edge(UP)
|
|
h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
|
|
h_line.next_to(title, DOWN)
|
|
self.add(title)
|
|
self.play(ShowCreation(h_line))
|
|
self.wait()
|
|
|
|
class JennyWatchesRotationWithMatrixAndVector(JenniferScene):
|
|
def construct(self):
|
|
self.add(self.jenny_plane.copy().fade(0.8))
|
|
self.add(self.jenny_plane, self.jenny, self.b1, self.b2)
|
|
|
|
matrix = Matrix([["1/3", "-2/3"], ["5/3", "-1/3"]])
|
|
matrix.set_column_colors(X_COLOR, Y_COLOR)
|
|
matrix.to_corner(UP+LEFT)
|
|
|
|
vector_coords = [1, 2]
|
|
vector_array = Matrix(vector_coords)
|
|
vector_array.set_color(YELLOW)
|
|
vector_array.next_to(matrix, RIGHT)
|
|
|
|
result = Matrix([-1, 1])
|
|
equals = TexMobject("=")
|
|
equals.next_to(vector_array)
|
|
result.next_to(equals)
|
|
|
|
for array in matrix, vector_array, result:
|
|
array.add_to_back(BackgroundRectangle(array))
|
|
|
|
vector = Vector(np.dot(self.cob_matrix(), vector_coords))
|
|
|
|
self.add(matrix)
|
|
self.play(Write(vector_array))
|
|
self.play(ShowCreation(vector))
|
|
self.play(Blink(self.jenny))
|
|
self.play(*it.chain(
|
|
[
|
|
Rotate(mob, np.pi/2, run_time = 3)
|
|
for mob in (self.jenny_plane, self.b1, self.b2, vector)
|
|
],
|
|
list(map(Animation, [self.jenny, matrix, vector_array])),
|
|
))
|
|
self.play(
|
|
self.jenny.change_mode, "pondering",
|
|
Write(equals),
|
|
Write(result)
|
|
)
|
|
self.play(Blink(self.jenny))
|
|
self.wait()
|
|
|
|
class MathematicalEmpathy(TeacherStudentsScene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
"\\centering An expression like",
|
|
"$A^{-1} M A$",
|
|
"\\\\ suggests a mathematical \\\\",
|
|
"sort of empathy"
|
|
)
|
|
A1, neg, one, M, A2 = words[1]
|
|
As = VGroup(A1, neg, one, A2)
|
|
VGroup(As, M).set_color(YELLOW)
|
|
|
|
self.teacher_says(words)
|
|
self.random_blink()
|
|
for mob, color in (M, BLUE), (As, MAROON_B):
|
|
self.play(mob.set_color, color)
|
|
self.play(mob.scale_in_place, 1.2, rate_func = there_and_back)
|
|
self.random_blink(2)
|
|
|
|
class NextVideo(Scene):
|
|
def construct(self):
|
|
title = TextMobject("""
|
|
Next video: Eigenvectors and eigenvalues
|
|
""")
|
|
title.to_edge(UP, buff = MED_SMALL_BUFF)
|
|
rect = Rectangle(width = 16, height = 9, color = BLUE)
|
|
rect.set_height(6)
|
|
rect.next_to(title, DOWN)
|
|
|
|
self.add(title)
|
|
self.play(ShowCreation(rect))
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|