2016-08-02 12:26:15 -07:00
|
|
|
from mobject.tex_mobject import TexMobject
|
|
|
|
from mobject import Mobject
|
|
|
|
from mobject.image_mobject import ImageMobject
|
|
|
|
from mobject.vectorized_mobject import VMobject
|
|
|
|
|
|
|
|
from animation.animation import Animation
|
|
|
|
from animation.transform import *
|
|
|
|
from animation.simple_animations import *
|
|
|
|
from topics.geometry import *
|
|
|
|
from topics.characters import *
|
|
|
|
from topics.functions import *
|
|
|
|
from topics.number_line import *
|
|
|
|
from topics.numerals import *
|
|
|
|
from scene import Scene
|
|
|
|
from camera import Camera
|
|
|
|
from mobject.svg_mobject import *
|
|
|
|
from mobject.tex_mobject import *
|
|
|
|
from mobject.vectorized_mobject import *
|
|
|
|
|
|
|
|
from eola.matrix import *
|
|
|
|
from eola.two_d_space import *
|
|
|
|
from eola.chapter3 import MatrixVectorMultiplicationAbstract
|
|
|
|
|
|
|
|
class Blob(Circle):
|
|
|
|
CONFIG = {
|
|
|
|
"stroke_color" : TEAL,
|
|
|
|
"fill_color" : BLUE_E,
|
|
|
|
"fill_opacity" : 1,
|
|
|
|
"random_seed" : 1,
|
|
|
|
"random_nudge_size" : 0.5,
|
|
|
|
"height" : 2,
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
Circle.__init__(self, **kwargs)
|
|
|
|
random.seed(self.random_seed)
|
|
|
|
self.apply_complex_function(
|
|
|
|
lambda z : z*(1+self.random_nudge_size*(random.random()-0.5))
|
|
|
|
)
|
|
|
|
self.scale_to_fit_height(self.height).center()
|
|
|
|
|
|
|
|
def probably_contains(self, point):
|
|
|
|
border_points = np.array(self.get_anchors_and_handles()[0])
|
|
|
|
distances = map(lambda p : np.linalg.norm(p-point), border_points)
|
|
|
|
min3 = border_points[np.argsort(distances)[:3]]
|
|
|
|
center_direction = self.get_center() - point
|
|
|
|
in_center_direction = map(
|
|
|
|
lambda p : np.dot(p-point, center_direction) > 0,
|
|
|
|
min3
|
|
|
|
)
|
|
|
|
return sum(in_center_direction) <= 2
|
|
|
|
|
|
|
|
|
|
|
|
class OpeningQuote(Scene):
|
|
|
|
def construct(self):
|
|
|
|
words = TextMobject([
|
|
|
|
"``The purpose of computation is \\\\",
|
|
|
|
"insight",
|
|
|
|
", not ",
|
|
|
|
"numbers.",
|
|
|
|
"''",
|
|
|
|
], separate_list_arg_with_spaces = False)
|
|
|
|
# words.scale_to_fit_width(2*SPACE_WIDTH - 2)
|
|
|
|
words.to_edge(UP)
|
|
|
|
words.split()[1].highlight(BLUE)
|
|
|
|
words.split()[3].highlight(GREEN)
|
|
|
|
author = TextMobject("-Richard Hamming")
|
|
|
|
author.highlight(YELLOW)
|
|
|
|
author.next_to(words, DOWN, buff = 0.5)
|
|
|
|
|
|
|
|
self.play(FadeIn(words))
|
|
|
|
self.dither(2)
|
|
|
|
self.play(Write(author, run_time = 3))
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class MovingForward(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
student = self.get_students()[1]
|
|
|
|
bubble = student.get_bubble(direction = RIGHT, width = 5)
|
|
|
|
bubble.rotate(-np.pi/12)
|
|
|
|
bubble.next_to(student, UP, aligned_edge = RIGHT)
|
|
|
|
bubble.shift(0.5*LEFT)
|
|
|
|
bubble.make_green_screen()
|
|
|
|
|
|
|
|
self.teacher_says("""
|
|
|
|
Y'all know about linear
|
|
|
|
transformations, right?
|
|
|
|
""", width = 7)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(bubble),
|
|
|
|
student.change_mode, "pondering"
|
|
|
|
)
|
|
|
|
self.dither(2)
|
|
|
|
|
|
|
|
class StretchingTransformation(LinearTransformationScene):
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
self.add_title("Generally stretches space")
|
|
|
|
self.apply_transposed_matrix([[3, 1], [-1, 2]])
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class SquishingTransformation(LinearTransformationScene):
|
|
|
|
CONFIG = {
|
|
|
|
"foreground_plane_kwargs" : {
|
|
|
|
"x_radius" : 3*SPACE_WIDTH,
|
|
|
|
"y_radius" : 3*SPACE_WIDTH,
|
|
|
|
"secondary_line_ratio" : 0
|
|
|
|
},
|
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
self.add_title("Generally squishes space")
|
|
|
|
self.apply_transposed_matrix([[1./2, -0.5], [1, 1./3]])
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class AskAboutStretching(LinearTransformationScene):
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
words = TextMobject("""
|
|
|
|
Exactly how much are
|
|
|
|
things being stretched?
|
|
|
|
""")
|
|
|
|
words.add_background_rectangle()
|
|
|
|
words.to_corner(UP+RIGHT)
|
|
|
|
words.highlight(YELLOW)
|
|
|
|
self.apply_transposed_matrix(
|
|
|
|
[[2, 1], [-1, 3]],
|
|
|
|
added_anims = [Write(words)]
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class AskAboutStretchingSpecifically(LinearTransformationScene):
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
self.add_title(["How much are", "areas", "scaled?"])
|
|
|
|
hma, areas, scaled = self.title.split()[1].split()
|
|
|
|
areas.highlight(YELLOW)
|
|
|
|
blob = Blob().shift(UP+RIGHT)
|
|
|
|
|
|
|
|
label = TextMobject("Area")
|
|
|
|
label.highlight(YELLOW)
|
|
|
|
label = VMobject(VectorizedPoint(label.get_left()), label)
|
|
|
|
label.move_to(blob)
|
|
|
|
target_label = TexMobject(["c \\cdot", "\\text{Area}"])
|
|
|
|
target_label.split()[1].highlight(YELLOW)
|
|
|
|
|
|
|
|
self.add_transformable_mobject(blob)
|
|
|
|
self.add_moving_mobject(label, target_label)
|
|
|
|
self.dither()
|
|
|
|
self.apply_transposed_matrix([[2, -1], [1, 1]])
|
|
|
|
arrow = Arrow(scaled, label.target.split()[0])
|
|
|
|
self.play(ShowCreation(arrow))
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class BeautyNowUsesLater(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
self.teacher_says("Beauty now, uses later")
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class DiagonalExample(LinearTransformationScene):
|
|
|
|
CONFIG = {
|
|
|
|
"show_square" : False,
|
|
|
|
"show_coordinates" : True,
|
|
|
|
"transposed_matrix" : [[3, 0], [0, 2]]
|
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
matrix = Matrix(np.array(self.transposed_matrix).transpose())
|
|
|
|
matrix.highlight_columns(X_COLOR, Y_COLOR)
|
|
|
|
matrix.next_to(ORIGIN, LEFT).to_edge(UP)
|
|
|
|
matrix_background = BackgroundRectangle(matrix)
|
|
|
|
self.play(ShowCreation(matrix_background), Write(matrix))
|
|
|
|
if self.show_square:
|
|
|
|
self.add_unit_square(animate = True)
|
|
|
|
self.add_foreground_mobject(matrix_background, matrix)
|
|
|
|
self.dither()
|
|
|
|
self.apply_transposed_matrix([self.transposed_matrix[0], [0, 1]])
|
|
|
|
self.apply_transposed_matrix([[1, 0], self.transposed_matrix[1]])
|
|
|
|
self.dither()
|
|
|
|
if self.show_square:
|
|
|
|
|
|
|
|
|
|
|
|
bottom_brace = Brace(self.i_hat, DOWN)
|
|
|
|
right_brace = Brace(self.square, RIGHT)
|
|
|
|
width = TexMobject(str(self.transposed_matrix[0][0]))
|
|
|
|
height = TexMobject(str(self.transposed_matrix[1][1]))
|
|
|
|
width.next_to(bottom_brace, DOWN)
|
|
|
|
height.next_to(right_brace, RIGHT)
|
|
|
|
for mob in bottom_brace, width, right_brace, height:
|
|
|
|
mob.add_background_rectangle()
|
|
|
|
self.play(Write(mob, run_time = 0.5))
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
width_target, height_target = width.copy(), height.copy()
|
|
|
|
det = np.linalg.det(self.transposed_matrix)
|
|
|
|
times, eq_det = map(TexMobject, ["\\times", "=%d"%det])
|
|
|
|
words = TextMobject("New area $=$")
|
|
|
|
equation = VMobject(
|
|
|
|
words, width_target, times, height_target, eq_det
|
|
|
|
)
|
|
|
|
equation.arrange_submobjects(RIGHT, buff = 0.2)
|
|
|
|
equation.next_to(self.square, UP, aligned_edge = LEFT)
|
|
|
|
equation.shift(0.5*RIGHT)
|
|
|
|
background_rect = BackgroundRectangle(equation)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(background_rect),
|
|
|
|
Transform(width.copy(), width_target),
|
|
|
|
Transform(height.copy(), height_target),
|
|
|
|
*map(Write, [words, times, eq_det])
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class DiagonalExampleWithSquare(DiagonalExample):
|
|
|
|
CONFIG = {
|
|
|
|
"show_square" : True
|
|
|
|
}
|
|
|
|
|
|
|
|
class ShearExample(DiagonalExample):
|
|
|
|
CONFIG = {
|
|
|
|
"show_square" : False,
|
|
|
|
"show_coordinates" : True,
|
|
|
|
"transposed_matrix" : [[1, 0], [1, 1]]
|
|
|
|
}
|
|
|
|
|
|
|
|
class ShearExampleWithSquare(DiagonalExample):
|
|
|
|
CONFIG = {
|
|
|
|
"show_square" : True,
|
|
|
|
"show_coordinates" : True,
|
|
|
|
"show_coordinates" : False,
|
|
|
|
"transposed_matrix" : [[1, 0], [1, 1]]
|
|
|
|
}
|
|
|
|
|
|
|
|
class ThisSquareTellsEverything(LinearTransformationScene):
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
self.add_unit_square()
|
|
|
|
words = TextMobject("""
|
|
|
|
This square gives you
|
|
|
|
everything you need.
|
|
|
|
""")
|
|
|
|
words.to_corner(UP+RIGHT)
|
|
|
|
words.highlight(YELLOW)
|
|
|
|
words.add_background_rectangle()
|
|
|
|
arrow = Arrow(
|
|
|
|
words.get_bottom(), self.square.get_right(),
|
|
|
|
color = WHITE
|
|
|
|
)
|
|
|
|
|
|
|
|
self.play(Write(words, run_time = 2))
|
|
|
|
self.play(ShowCreation(arrow))
|
|
|
|
self.add_foreground_mobject(words, arrow)
|
|
|
|
self.dither()
|
|
|
|
self.apply_transposed_matrix([[1.5, -0.5], [1, 1.5]])
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class WhatHappensToOneSquareHappensToAll(LinearTransformationScene):
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
self.add_unit_square()
|
|
|
|
pairs = [
|
|
|
|
(2*RIGHT+UP, 1),
|
|
|
|
(3*LEFT, 2),
|
|
|
|
(2*LEFT+DOWN, 0.5),
|
|
|
|
(3.5*RIGHT+2.5*UP, 1.5),
|
|
|
|
(RIGHT+2*DOWN, 0.25),
|
|
|
|
(3*LEFT+3*DOWN, 1),
|
|
|
|
]
|
|
|
|
squares = VMobject()
|
|
|
|
for position, side_length in pairs:
|
|
|
|
square = self.square.copy()
|
|
|
|
square.scale(side_length)
|
|
|
|
square.shift(position)
|
|
|
|
squares.add(square)
|
|
|
|
self.play(FadeIn(
|
|
|
|
squares, submobject_mode = "lagged_start",
|
|
|
|
run_time = 3
|
|
|
|
))
|
|
|
|
self.add_transformable_mobject(squares)
|
|
|
|
self.apply_transposed_matrix([[1, -1], [0.5, 1]])
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class BreakBlobIntoGridSquares(LinearTransformationScene):
|
|
|
|
CONFIG = {
|
|
|
|
"square_size" : 0.5,
|
|
|
|
"blob_height" : 3,
|
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
|
|
|
blob = Blob(
|
|
|
|
height = self.blob_height,
|
|
|
|
random_seed = 5,
|
|
|
|
random_nudge_size = 0.2,
|
|
|
|
)
|
|
|
|
blob.next_to(ORIGIN, UP+RIGHT)
|
|
|
|
self.add_transformable_mobject(blob)
|
|
|
|
arange = np.arange(
|
|
|
|
0, self.blob_height + self.square_size,
|
|
|
|
self.square_size
|
|
|
|
)
|
|
|
|
square = Square(side_length = self.square_size)
|
|
|
|
square.set_stroke(YELLOW, width = 2)
|
|
|
|
square.set_fill(YELLOW, opacity = 0.3)
|
|
|
|
squares = VMobject()
|
|
|
|
for x, y in it.product(*[arange]*2):
|
|
|
|
point = x*RIGHT + y*UP
|
|
|
|
if blob.probably_contains(point):
|
|
|
|
squares.add(square.copy().shift(point))
|
|
|
|
self.play(ShowCreation(
|
|
|
|
squares, submobject_mode = "lagged_start",
|
|
|
|
run_time = 2,
|
|
|
|
))
|
|
|
|
self.add_transformable_mobject(squares)
|
|
|
|
self.dither()
|
|
|
|
self.apply_transposed_matrix([[1, -1], [0.5, 1]])
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class BreakBlobIntoGridSquaresGranular(BreakBlobIntoGridSquares):
|
|
|
|
CONFIG = {
|
|
|
|
"square_size" : 0.25
|
|
|
|
}
|
|
|
|
|
|
|
|
class BreakBlobIntoGridSquaresVeryGranular(BreakBlobIntoGridSquares):
|
|
|
|
CONFIG = {
|
|
|
|
"square_size" : 0.1
|
|
|
|
}
|
|
|
|
|
|
|
|
class NameDeterminant(LinearTransformationScene):
|
|
|
|
CONFIG = {
|
2016-08-02 15:50:32 -07:00
|
|
|
"t_matrix" : [[3, 0], [2, 2]]
|
2016-08-02 12:26:15 -07:00
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
self.setup()
|
2016-08-02 15:50:32 -07:00
|
|
|
self.plane.fade(0.3)
|
|
|
|
self.add_unit_square(color = YELLOW_E, opacity = 0.5)
|
|
|
|
self.add_title(
|
|
|
|
["The", "``determinant''", "of a transformation"],
|
|
|
|
scale_factor = 1
|
|
|
|
)
|
2016-08-02 12:26:15 -07:00
|
|
|
self.title.split()[1].split()[1].highlight(YELLOW)
|
|
|
|
|
2016-08-02 15:50:32 -07:00
|
|
|
matrix_background, matrix, det_text = self.get_matrix()
|
|
|
|
self.add_foreground_mobject(matrix_background, matrix)
|
|
|
|
|
|
|
|
A = TexMobject("A")
|
|
|
|
area_label = VMobject(A.copy(), A.copy(), A)
|
|
|
|
area_label.move_to(self.square)
|
2016-08-02 12:26:15 -07:00
|
|
|
det = np.linalg.det(self.t_matrix)
|
2016-08-02 15:50:32 -07:00
|
|
|
if np.round(det) == det:
|
|
|
|
det = int(det)
|
|
|
|
area_label_target = VMobject(
|
|
|
|
TexMobject(str(det)), TexMobject("\\cdot"), A.copy()
|
|
|
|
)
|
|
|
|
if det < 1 and det > 0:
|
|
|
|
area_label_target.scale(det)
|
|
|
|
area_label_target.arrange_submobjects(RIGHT, buff = 0.1)
|
|
|
|
self.add_moving_mobject(area_label, area_label_target)
|
|
|
|
|
|
|
|
self.dither()
|
|
|
|
self.apply_transposed_matrix(self.t_matrix)
|
|
|
|
self.dither()
|
|
|
|
det_mob_copy = area_label.split()[0].copy()
|
|
|
|
new_det_mob = det_mob_copy.copy().scale_to_fit_height(
|
|
|
|
det_text.split()[0].get_height()
|
|
|
|
)
|
|
|
|
new_det_mob.next_to(det_text, RIGHT, buff = 0.2)
|
|
|
|
new_det_mob.add_background_rectangle()
|
|
|
|
det_mob_copy.add_background_rectangle(opacity = 0)
|
|
|
|
self.play(Write(det_text))
|
|
|
|
self.play(Transform(det_mob_copy, new_det_mob))
|
|
|
|
self.dither()
|
2016-08-02 12:26:15 -07:00
|
|
|
|
|
|
|
|
2016-08-02 15:50:32 -07:00
|
|
|
def get_matrix(self):
|
|
|
|
matrix = Matrix(np.array(self.t_matrix).transpose())
|
|
|
|
matrix.highlight_columns(X_COLOR, Y_COLOR)
|
|
|
|
matrix.next_to(self.title, DOWN, buff = 0.5)
|
|
|
|
matrix.shift(2*LEFT)
|
|
|
|
matrix_background = BackgroundRectangle(matrix)
|
2016-08-02 12:26:15 -07:00
|
|
|
|
2016-08-02 15:50:32 -07:00
|
|
|
braces = TexMobject("()")
|
|
|
|
braces.scale(2)
|
|
|
|
braces.stretch_to_fit_height(matrix.get_height())
|
|
|
|
l_brace, r_brace = braces.split()
|
|
|
|
l_brace.next_to(matrix, LEFT, buff = 0.1)
|
|
|
|
r_brace.next_to(matrix, RIGHT, buff = 0.1)
|
|
|
|
det = TextMobject("det").next_to(l_brace, LEFT, buff = 0.1)
|
|
|
|
det.add_background_rectangle()
|
|
|
|
eq = TexMobject("=").next_to(r_brace, RIGHT, buff = 0.1)
|
2016-08-02 12:26:15 -07:00
|
|
|
|
2016-08-02 15:50:32 -07:00
|
|
|
det_text = VMobject(det, l_brace, r_brace, eq)
|
|
|
|
return matrix_background, matrix, det_text
|
2016-08-02 12:26:15 -07:00
|
|
|
|
2016-08-02 15:50:32 -07:00
|
|
|
class DeterminantIsOneHalf(NameDeterminant):
|
|
|
|
CONFIG = {
|
|
|
|
"t_matrix" : [[0.5, -0.5], [0.5, 0.5]],
|
|
|
|
"foreground_plane_kwargs" : {
|
|
|
|
"x_radius" : 2*SPACE_WIDTH,
|
|
|
|
"y_radius" : 2*SPACE_WIDTH,
|
|
|
|
"secondary_line_ratio" : 0
|
|
|
|
},
|
|
|
|
}
|
2016-08-02 12:26:15 -07:00
|
|
|
|
2016-08-02 15:50:32 -07:00
|
|
|
class DeterminantIsZero(NameDeterminant):
|
|
|
|
CONFIG = {
|
|
|
|
"t_matrix" : [[2, 1], [2, 1]],
|
|
|
|
}
|
2016-08-02 12:26:15 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|