mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Halfway through chapter 6
This commit is contained in:
parent
f52179c3d9
commit
e5747b3f3b
9 changed files with 1061 additions and 46 deletions
|
@ -441,6 +441,22 @@ class IntroduceLinearTransformations(LinearTransformationScene):
|
||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
class ToThePedants(Scene):
|
||||||
|
def construct(self):
|
||||||
|
words = TextMobject([
|
||||||
|
"To the pedants:\\\\",
|
||||||
|
"""
|
||||||
|
Yeah yeah, I know that's not the formal definition
|
||||||
|
for linear transformations, as seen in textbooks,
|
||||||
|
but I'm building visual intuition here, and what
|
||||||
|
I've said is equivalent to the formal definition
|
||||||
|
(which I'll get to later in the series).
|
||||||
|
"""])
|
||||||
|
words.split()[0].highlight(RED)
|
||||||
|
words.to_edge(UP)
|
||||||
|
self.add(words)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
class SimpleLinearTransformationScene(LinearTransformationScene):
|
class SimpleLinearTransformationScene(LinearTransformationScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"show_basis_vectors" : False,
|
"show_basis_vectors" : False,
|
||||||
|
@ -1138,13 +1154,13 @@ class ColumnsToBasisVectors(LinearTransformationScene):
|
||||||
self.apply_transposed_matrix(
|
self.apply_transposed_matrix(
|
||||||
transform_matrix1.transpose(),
|
transform_matrix1.transpose(),
|
||||||
added_anims = [Transform(i_coords_start, i_coords_end)],
|
added_anims = [Transform(i_coords_start, i_coords_end)],
|
||||||
path_arc = np.pi/2
|
path_arc = np.pi/2,
|
||||||
)
|
)
|
||||||
self.add_foreground_mobject(i_coords_start)
|
self.add_foreground_mobject(i_coords_start)
|
||||||
self.apply_transposed_matrix(
|
self.apply_transposed_matrix(
|
||||||
transform_matrix2.transpose(),
|
transform_matrix2.transpose(),
|
||||||
added_anims = [Transform(j_coords_start, j_coords_end) ],
|
added_anims = [Transform(j_coords_start, j_coords_end) ],
|
||||||
path_arc = np.pi/2
|
path_arc = np.pi/2,
|
||||||
)
|
)
|
||||||
self.add_foreground_mobject(j_coords_start)
|
self.add_foreground_mobject(j_coords_start)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
|
@ -687,7 +687,7 @@ class GeneralMultiplication(MoreComplicatedExampleNumerically):
|
||||||
for entry, char in zip(m1_entries.split(), "efgh")
|
for entry, char in zip(m1_entries.split(), "efgh")
|
||||||
])
|
])
|
||||||
|
|
||||||
words = TextMobject("This method works genearlly")
|
words = TextMobject("This method works generally")
|
||||||
self.play(Write(words, run_time = 2))
|
self.play(Write(words, run_time = 2))
|
||||||
self.play(Transform(
|
self.play(Transform(
|
||||||
m1_entries, m1_entries_target,
|
m1_entries, m1_entries_target,
|
||||||
|
|
|
@ -348,6 +348,11 @@ class BreakBlobIntoGridSquaresGranular(BreakBlobIntoGridSquares):
|
||||||
"square_size" : 0.25
|
"square_size" : 0.25
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class BreakBlobIntoGridSquaresMoreGranular(BreakBlobIntoGridSquares):
|
||||||
|
CONFIG = {
|
||||||
|
"square_size" : 0.15
|
||||||
|
}
|
||||||
|
|
||||||
class BreakBlobIntoGridSquaresVeryGranular(BreakBlobIntoGridSquares):
|
class BreakBlobIntoGridSquaresVeryGranular(BreakBlobIntoGridSquares):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"square_size" : 0.1
|
"square_size" : 0.1
|
||||||
|
@ -409,6 +414,11 @@ class NameDeterminant(LinearTransformationScene):
|
||||||
det_text.remove(det_text.split()[-1])
|
det_text.remove(det_text.split()[-1])
|
||||||
return matrix_background, matrix, det_text
|
return matrix_background, matrix, det_text
|
||||||
|
|
||||||
|
class SecondDeterminantExample(NameDeterminant):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : [[-1, -1], [1, -1]]
|
||||||
|
}
|
||||||
|
|
||||||
class DeterminantIsThree(NameDeterminant):
|
class DeterminantIsThree(NameDeterminant):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"t_matrix" : [[0, -1.5], [2, 1]]
|
"t_matrix" : [[0, -1.5], [2, 1]]
|
||||||
|
@ -429,6 +439,12 @@ class DeterminantIsZero(NameDeterminant):
|
||||||
"t_matrix" : [[4, 2], [2, 1]],
|
"t_matrix" : [[4, 2], [2, 1]],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SecondDeterminantIsZeroExamlpe(NameDeterminant):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : [[0, 0], [0, 0]],
|
||||||
|
"show_basis_vectors" : False
|
||||||
|
}
|
||||||
|
|
||||||
class NextFewVideos(Scene):
|
class NextFewVideos(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
icon = SVGMobject("video_icon")
|
icon = SVGMobject("video_icon")
|
||||||
|
@ -447,6 +463,16 @@ class NextFewVideos(Scene):
|
||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
class UnderstandingBeforeApplication(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
self.teacher_says("""
|
||||||
|
Just the visual
|
||||||
|
understanding for now
|
||||||
|
""")
|
||||||
|
self.random_blink()
|
||||||
|
self.dither()
|
||||||
|
|
||||||
class WhatIveSaidSoFar(TeacherStudentsScene):
|
class WhatIveSaidSoFar(TeacherStudentsScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.setup()
|
self.setup()
|
||||||
|
@ -807,6 +833,10 @@ class RightHandRule(Scene):
|
||||||
i_hat = Vector([-1.75, 0.5])
|
i_hat = Vector([-1.75, 0.5])
|
||||||
j_hat = Vector([-1.4, -0.7])
|
j_hat = Vector([-1.4, -0.7])
|
||||||
k_hat = Vector([0, 1.7])
|
k_hat = Vector([0, 1.7])
|
||||||
|
vects = [i_hat, j_hat, k_hat]
|
||||||
|
if self.flip:
|
||||||
|
VMobject(hand, *vects).flip()
|
||||||
|
|
||||||
i_label, j_label, k_label = [
|
i_label, j_label, k_label = [
|
||||||
TexMobject("\\hat{%s}"%s).scale(1.5)
|
TexMobject("\\hat{%s}"%s).scale(1.5)
|
||||||
for s in "\\imath", "\\jmath", "k"
|
for s in "\\imath", "\\jmath", "k"
|
||||||
|
@ -815,12 +845,9 @@ class RightHandRule(Scene):
|
||||||
j_label.next_to(j_hat.get_end(), DOWN)
|
j_label.next_to(j_hat.get_end(), DOWN)
|
||||||
k_label.next_to(k_hat.get_end(), UP)
|
k_label.next_to(k_hat.get_end(), UP)
|
||||||
|
|
||||||
vects = [i_hat, j_hat, k_hat]
|
|
||||||
labels = [i_label, j_label, k_label]
|
labels = [i_label, j_label, k_label]
|
||||||
colors = [X_COLOR, Y_COLOR, Z_COLOR]
|
colors = [X_COLOR, Y_COLOR, Z_COLOR]
|
||||||
|
|
||||||
if self.flip:
|
|
||||||
VMobject(hand, *vects+labels).flip()
|
|
||||||
|
|
||||||
# self.add(NumberPlane())
|
# self.add(NumberPlane())
|
||||||
self.play(
|
self.play(
|
||||||
|
@ -841,6 +868,16 @@ class LeftHandRule(RightHandRule):
|
||||||
"flip" : True
|
"flip" : True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AskHowToCompute(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
student = self.get_students()[1]
|
||||||
|
self.student_says("How do you \\\\ compute this?")
|
||||||
|
self.play(student.change_mode, "confused")
|
||||||
|
self.random_blink()
|
||||||
|
self.dither()
|
||||||
|
self.random_blink()
|
||||||
|
|
||||||
class TwoDDeterminantFormula(Scene):
|
class TwoDDeterminantFormula(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
eq = TextMobject("=")
|
eq = TextMobject("=")
|
||||||
|
@ -853,7 +890,7 @@ class TwoDDeterminantFormula(Scene):
|
||||||
VMobject(matrix, det_text).next_to(eq, LEFT)
|
VMobject(matrix, det_text).next_to(eq, LEFT)
|
||||||
formula = TexMobject(list("ad-bc"))
|
formula = TexMobject(list("ad-bc"))
|
||||||
formula.next_to(eq, RIGHT)
|
formula.next_to(eq, RIGHT)
|
||||||
formula.shift(0.2*UP)
|
formula.shift(0.1*UP)
|
||||||
|
|
||||||
a, d, minus, b, c = formula.split()
|
a, d, minus, b, c = formula.split()
|
||||||
VMobject(a, c).highlight(X_COLOR)
|
VMobject(a, c).highlight(X_COLOR)
|
||||||
|
@ -878,7 +915,7 @@ class TwoDDeterminantFormula(Scene):
|
||||||
for m in mb, mc, b, c
|
for m in mb, mc, b, c
|
||||||
])
|
])
|
||||||
self.dither()
|
self.dither()
|
||||||
for pair in (mc, c), (mb, b):
|
for pair in (mb, b), (mc, c):
|
||||||
self.play(*[
|
self.play(*[
|
||||||
Transform(m, m.original)
|
Transform(m, m.original)
|
||||||
for m in pair
|
for m in pair
|
||||||
|
@ -957,9 +994,9 @@ class FullFormulaExplanation(LinearTransformationScene):
|
||||||
d = self.j_hat.get_end()[1]*UP
|
d = self.j_hat.get_end()[1]*UP
|
||||||
|
|
||||||
shapes_colors_and_tex = [
|
shapes_colors_and_tex = [
|
||||||
(Polygon(ORIGIN, a, a+c), TEAL, "bd/2"),
|
(Polygon(ORIGIN, a, a+c), MAROON, "ac/2"),
|
||||||
(Polygon(ORIGIN, d+b, d), TEAL, "\\dfrac{bd}{2}"),
|
(Polygon(ORIGIN, d+b, d, d), TEAL, "\\dfrac{bd}{2}"),
|
||||||
(Polygon(a+c, a+b+c, a+b+c+d), MAROON, "\\dfrac{ac}{2}"),
|
(Polygon(a+c, a+b+c, a+b+c, a+b+c+d), TEAL, "\\dfrac{bd}{2}"),
|
||||||
(Polygon(b+d, a+b+c+d, b+c+d), MAROON, "ac/2"),
|
(Polygon(b+d, a+b+c+d, b+c+d), MAROON, "ac/2"),
|
||||||
(Polygon(a, a+b, a+b+c, a+c), PINK, "bc"),
|
(Polygon(a, a+b, a+b+c, a+c), PINK, "bc"),
|
||||||
(Polygon(d, d+b, d+b+c, d+c), PINK, "bc"),
|
(Polygon(d, d+b, d+b+c, d+c), PINK, "bc"),
|
||||||
|
@ -991,8 +1028,8 @@ class FullFormulaExplanation(LinearTransformationScene):
|
||||||
(a, a+b, DOWN, "b"),
|
(a, a+b, DOWN, "b"),
|
||||||
(a+b, a+b+c, RIGHT, "c"),
|
(a+b, a+b+c, RIGHT, "c"),
|
||||||
(a+b+c, a+b+c+d, RIGHT, "d"),
|
(a+b+c, a+b+c+d, RIGHT, "d"),
|
||||||
(a+b+c+d, a+c+d, UP, "a"),
|
(a+b+c+d, b+c+d, UP, "a"),
|
||||||
(a+c+d, d+c, UP, "b"),
|
(b+c+d, d+c, UP, "b"),
|
||||||
(d+c, d, LEFT, "c"),
|
(d+c, d, LEFT, "c"),
|
||||||
(d, ORIGIN, LEFT, "d"),
|
(d, ORIGIN, LEFT, "d"),
|
||||||
]
|
]
|
||||||
|
|
814
eola/chapter6.py
Normal file
814
eola/chapter6.py
Normal file
|
@ -0,0 +1,814 @@
|
||||||
|
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 ka_playgrounds.circuits import Resistor, Source, LongResistor
|
||||||
|
|
||||||
|
class OpeningQuote(Scene):
|
||||||
|
def construct(self):
|
||||||
|
words = TextMobject([
|
||||||
|
"The question you raise, ",
|
||||||
|
"``how can such a formulation lead to computations?''",
|
||||||
|
"doesn't bother me in the least! Throughout my whole life "
|
||||||
|
"as a mathematician, the possibility of making explicit, "
|
||||||
|
"elegant computations has always come out by itself, as a "
|
||||||
|
"byproduct of a ",
|
||||||
|
"thorough conceptual understanding."
|
||||||
|
], 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(["-Grothendieck", "(a hero of mine)"])
|
||||||
|
author.split()[0].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 ListTerms(Scene):
|
||||||
|
def construct(self):
|
||||||
|
title = TextMobject("Under the light of linear transformations")
|
||||||
|
title.highlight(YELLOW)
|
||||||
|
title.to_edge(UP)
|
||||||
|
randy = Randolph().to_corner()
|
||||||
|
words = VMobject(*map(TextMobject, [
|
||||||
|
"Inverse matrices",
|
||||||
|
"Column space",
|
||||||
|
"Rank",
|
||||||
|
"Null space",
|
||||||
|
]))
|
||||||
|
words.arrange_submobjects(DOWN, aligned_edge = LEFT)
|
||||||
|
words.next_to(title, DOWN, aligned_edge = LEFT)
|
||||||
|
words.shift(RIGHT)
|
||||||
|
|
||||||
|
self.add(title, randy)
|
||||||
|
for i, word in enumerate(words.split()):
|
||||||
|
self.play(Write(word), run_time = 1)
|
||||||
|
if i%2 == 0:
|
||||||
|
self.play(Blink(randy))
|
||||||
|
else:
|
||||||
|
self.dither()
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class NoComputations(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
self.student_says(
|
||||||
|
"Will you cover \\\\ computations?",
|
||||||
|
pi_creature_target_mode = "raise_left_hand"
|
||||||
|
)
|
||||||
|
self.random_blink()
|
||||||
|
self.teacher_says(
|
||||||
|
"Well...uh...no",
|
||||||
|
pi_creature_target_mode = "guilty",
|
||||||
|
)
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(student.change_mode, mode)
|
||||||
|
for student, mode in zip(
|
||||||
|
self.get_students(),
|
||||||
|
["dejected", "confused", "angry"]
|
||||||
|
)
|
||||||
|
])
|
||||||
|
self.random_blink()
|
||||||
|
self.dither()
|
||||||
|
new_words = self.teacher.bubble.position_mobject_inside(
|
||||||
|
TextMobject([
|
||||||
|
"Search",
|
||||||
|
"``Gaussian elimination'' \\\\",
|
||||||
|
"and",
|
||||||
|
"``Row echelon form''",
|
||||||
|
])
|
||||||
|
)
|
||||||
|
new_words.split()[1].highlight(YELLOW)
|
||||||
|
new_words.split()[3].highlight(GREEN)
|
||||||
|
self.play(
|
||||||
|
Transform(self.teacher.bubble.content, new_words),
|
||||||
|
self.teacher.change_mode, "speaking"
|
||||||
|
)
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(student.change_mode, "pondering")
|
||||||
|
for student in self.get_students()
|
||||||
|
])
|
||||||
|
self.random_blink()
|
||||||
|
|
||||||
|
class UsefulnessOfMatrices(Scene):
|
||||||
|
def construct(self):
|
||||||
|
title = TextMobject("Usefulness of matrices")
|
||||||
|
title.highlight(YELLOW)
|
||||||
|
title.to_edge(UP)
|
||||||
|
self.add(title)
|
||||||
|
self.dither(3) #Play some 3d linear transform over this
|
||||||
|
|
||||||
|
equations = TexMobject("""
|
||||||
|
6x - 3y + 2z &= 7 \\\\
|
||||||
|
x + 2y + 5z &= 0 \\\\
|
||||||
|
2x - 8y - z &= -2 \\\\
|
||||||
|
""")
|
||||||
|
equations.to_edge(RIGHT, buff = 2)
|
||||||
|
syms = VMobject(*np.array(equations.split())[[1, 4, 7]])
|
||||||
|
new_syms = VMobject(*[
|
||||||
|
m.copy().highlight(c)
|
||||||
|
for m, c in zip(syms.split(), [X_COLOR, Y_COLOR, Z_COLOR])
|
||||||
|
])
|
||||||
|
new_syms.arrange_submobjects(RIGHT, buff = 0.5)
|
||||||
|
new_syms.next_to(equations, LEFT, buff = 3)
|
||||||
|
sym_brace = Brace(new_syms, DOWN)
|
||||||
|
unknowns = sym_brace.get_text("Unknown variables")
|
||||||
|
eq_brace = Brace(equations, DOWN)
|
||||||
|
eq_words = eq_brace.get_text("Equations")
|
||||||
|
|
||||||
|
self.play(Write(equations))
|
||||||
|
self.dither()
|
||||||
|
self.play(Transform(syms.copy(), new_syms, path_arc = np.pi/2))
|
||||||
|
for brace, words in (sym_brace, unknowns), (eq_brace, eq_words):
|
||||||
|
self.play(
|
||||||
|
GrowFromCenter(brace),
|
||||||
|
Write(words)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class CircuitDiagram(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.add(TextMobject("Voltages").to_edge(UP))
|
||||||
|
|
||||||
|
source = Source()
|
||||||
|
p1, p2 = source.get_top(), source.get_bottom()
|
||||||
|
r1 = Resistor(p1, p1+2*RIGHT)
|
||||||
|
r2 = LongResistor(p1+2*RIGHT, p2+2*RIGHT)
|
||||||
|
r3 = Resistor(p1+2*RIGHT, p1+2*2*RIGHT)
|
||||||
|
l1 = Line(p1+2*2*RIGHT, p2+2*2*RIGHT)
|
||||||
|
l2 = Line(p2+2*2*RIGHT, p2)
|
||||||
|
circuit = VMobject(source, r1, r2, r3, l1, l2)
|
||||||
|
circuit.center()
|
||||||
|
v1 = TexMobject("v_1").next_to(r1, UP)
|
||||||
|
v2 = TexMobject("v_2").next_to(r2, RIGHT)
|
||||||
|
v3 = TexMobject("v_3").next_to(r3, UP)
|
||||||
|
unknowns = VMobject(v1, v2, v3)
|
||||||
|
unknowns.highlight(BLUE)
|
||||||
|
|
||||||
|
self.play(ShowCreation(circuit))
|
||||||
|
self.dither()
|
||||||
|
self.play(Write(unknowns))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class StockLine(VMobject):
|
||||||
|
CONFIG = {
|
||||||
|
"num_points" : 15,
|
||||||
|
"step_range" : 2
|
||||||
|
}
|
||||||
|
def generate_points(self):
|
||||||
|
points = [ORIGIN]
|
||||||
|
for x in range(self.num_points):
|
||||||
|
step_size = self.step_range*(random.random() - 0.5)
|
||||||
|
points.append(points[-1] + 0.5*RIGHT + step_size*UP)
|
||||||
|
self.set_anchor_points(points, mode = "corners")
|
||||||
|
|
||||||
|
class StockPrices(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.add(TextMobject("Stock prices").to_edge(UP))
|
||||||
|
|
||||||
|
x_axis = Line(ORIGIN, SPACE_WIDTH*RIGHT)
|
||||||
|
y_axis = Line(ORIGIN, SPACE_HEIGHT*UP)
|
||||||
|
everyone = VMobject(x_axis, y_axis)
|
||||||
|
stock_lines = []
|
||||||
|
for color in TEAL, PINK, YELLOW, RED, BLUE:
|
||||||
|
sl = StockLine(color = color)
|
||||||
|
sl.move_to(y_axis.get_center(), side_to_align = LEFT)
|
||||||
|
everyone.add(sl)
|
||||||
|
stock_lines.append(sl)
|
||||||
|
everyone.center()
|
||||||
|
|
||||||
|
self.add(x_axis, y_axis)
|
||||||
|
self.play(ShowCreation(
|
||||||
|
VMobject(*stock_lines),
|
||||||
|
run_time = 3,
|
||||||
|
submobject_mode = "lagged_start"
|
||||||
|
))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class MachineLearningNetwork(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.add(TextMobject("Machine learning parameters").to_edge(UP))
|
||||||
|
|
||||||
|
layers = []
|
||||||
|
for i, num_nodes in enumerate([3, 4, 4, 1]):
|
||||||
|
layer = VMobject(*[
|
||||||
|
Circle(radius = 0.5, color = YELLOW)
|
||||||
|
for x in range(num_nodes)
|
||||||
|
])
|
||||||
|
for j, mob in enumerate(layer.split()):
|
||||||
|
sym = TexMobject("x_{%d, %d}"%(i, j))
|
||||||
|
sym.move_to(mob)
|
||||||
|
mob.add(sym)
|
||||||
|
layer.arrange_submobjects(DOWN, buff = 0.5)
|
||||||
|
layer.center()
|
||||||
|
layers.append(layer)
|
||||||
|
VMobject(*layers).arrange_submobjects(RIGHT, buff = 1.5)
|
||||||
|
lines = VMobject()
|
||||||
|
for l_layer, r_layer in zip(layers, layers[1:]):
|
||||||
|
for l_node, r_node in it.product(l_layer.split(), r_layer.split()):
|
||||||
|
lines.add(Line(l_node, r_node))
|
||||||
|
lines.submobject_gradient_highlight(BLUE_E, BLUE_A)
|
||||||
|
for mob in VMobject(*layers), lines:
|
||||||
|
self.play(Write(mob), run_time = 2)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class SystemOfEquations(Scene):
|
||||||
|
def construct(self):
|
||||||
|
equations = self.get_equations()
|
||||||
|
self.show_linearity_rules(equations)
|
||||||
|
self.describe_organization(equations)
|
||||||
|
self.factor_into_matrix(equations)
|
||||||
|
|
||||||
|
def get_equations(self):
|
||||||
|
matrix = Matrix([
|
||||||
|
[2, 5, 3],
|
||||||
|
[4, 0, 8],
|
||||||
|
[1, 3, 0]
|
||||||
|
])
|
||||||
|
mob_matrix = matrix.get_mob_matrix()
|
||||||
|
rhs = map(TexMobject, map(str, [-3, 0, 2]))
|
||||||
|
variables = map(TexMobject, list("xyz"))
|
||||||
|
for v, color in zip(variables, [X_COLOR, Y_COLOR, Z_COLOR]):
|
||||||
|
v.highlight(color)
|
||||||
|
equations = VMobject()
|
||||||
|
for row in mob_matrix:
|
||||||
|
equation = VMobject(*it.chain(*zip(
|
||||||
|
row,
|
||||||
|
[v.copy() for v in variables],
|
||||||
|
map(TexMobject, list("++="))
|
||||||
|
)))
|
||||||
|
equation.arrange_submobjects(
|
||||||
|
RIGHT, buff = 0.1,
|
||||||
|
aligned_edge = DOWN
|
||||||
|
)
|
||||||
|
equation.split()[4].shift(0.1*DOWN)
|
||||||
|
equation.split()[-1].next_to(equation.split()[-2], RIGHT)
|
||||||
|
equations.add(equation)
|
||||||
|
equations.arrange_submobjects(DOWN, aligned_edge = RIGHT)
|
||||||
|
for eq, rhs_elem in zip(equations.split(), rhs):
|
||||||
|
rhs_elem.next_to(eq, RIGHT)
|
||||||
|
eq.add(rhs_elem)
|
||||||
|
equations.center()
|
||||||
|
self.play(Write(equations))
|
||||||
|
self.add(equations)
|
||||||
|
return equations
|
||||||
|
|
||||||
|
def show_linearity_rules(self, equations):
|
||||||
|
top_equation = equations.split()[0]
|
||||||
|
other_equations = VMobject(*equations.split()[1:])
|
||||||
|
other_equations.save_state()
|
||||||
|
scaled_vars = VMobject(*[
|
||||||
|
VMobject(*top_equation.split()[3*i:3*i+2])
|
||||||
|
for i in range(3)
|
||||||
|
])
|
||||||
|
scaled_vars.save_state()
|
||||||
|
isolated_scaled_vars = scaled_vars.copy()
|
||||||
|
isolated_scaled_vars.scale(1.5)
|
||||||
|
isolated_scaled_vars.next_to(top_equation, UP)
|
||||||
|
scalars = VMobject(*[m.split()[0] for m in scaled_vars.split()])
|
||||||
|
plusses = np.array(top_equation.split())[[2, 5]]
|
||||||
|
|
||||||
|
self.play(other_equations.fade, 0.7)
|
||||||
|
self.play(Transform(scaled_vars, isolated_scaled_vars))
|
||||||
|
self.play(scalars.highlight, YELLOW, submobject_mode = "lagged_start")
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(m.scale_in_place, 1.2, rate_func = there_and_back)
|
||||||
|
for m in scalars.split()
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
self.remove(scalars)
|
||||||
|
self.play(scaled_vars.restore)
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(p.scale_in_place, 1.5, rate_func = there_and_back)
|
||||||
|
for p in plusses
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
self.show_nonlinearity_examples()
|
||||||
|
self.play(other_equations.restore)
|
||||||
|
|
||||||
|
def show_nonlinearity_examples(self):
|
||||||
|
squared = TexMobject("x^2")
|
||||||
|
squared.split()[0].highlight(X_COLOR)
|
||||||
|
sine = TexMobject("\\sin(x)")
|
||||||
|
sine.split()[-2].highlight(X_COLOR)
|
||||||
|
product = TexMobject("xy")
|
||||||
|
product.split()[0].highlight(X_COLOR)
|
||||||
|
product.split()[1].highlight(Y_COLOR)
|
||||||
|
|
||||||
|
|
||||||
|
words = TextMobject("Not allowed!")
|
||||||
|
words.highlight(RED)
|
||||||
|
words.to_corner(UP+LEFT, buff = 1)
|
||||||
|
arrow = Vector(RIGHT, color = RED)
|
||||||
|
arrow.next_to(words, RIGHT)
|
||||||
|
for mob in squared, sine, product:
|
||||||
|
mob.scale(1.7)
|
||||||
|
mob.next_to(arrow.get_end(), RIGHT, buff = 0.5)
|
||||||
|
circle_slash = Circle(color = RED)
|
||||||
|
line = Line(LEFT, RIGHT, color = RED)
|
||||||
|
line.rotate(np.pi/4)
|
||||||
|
circle_slash.add(line)
|
||||||
|
circle_slash.next_to(arrow, RIGHT)
|
||||||
|
def draw_circle_slash(mob):
|
||||||
|
circle_slash.replace(mob)
|
||||||
|
circle_slash.scale_in_place(1.4)
|
||||||
|
self.play(ShowCreation(circle_slash), run_time = 0.5)
|
||||||
|
self.dither(0.5)
|
||||||
|
self.play(FadeOut(circle_slash), run_time = 0.5)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
Write(squared),
|
||||||
|
Write(words, run_time = 1),
|
||||||
|
ShowCreation(arrow),
|
||||||
|
)
|
||||||
|
draw_circle_slash(squared)
|
||||||
|
for mob in sine, product:
|
||||||
|
self.play(Transform(squared, mob))
|
||||||
|
draw_circle_slash(mob)
|
||||||
|
self.play(*map(FadeOut, [words, arrow, squared]))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
def describe_organization(self, equations):
|
||||||
|
variables = VMobject(*it.chain(*[
|
||||||
|
eq.split()[:-2]
|
||||||
|
for eq in equations.split()
|
||||||
|
]))
|
||||||
|
variables.words = "Throw variables on the left"
|
||||||
|
constants = VMobject(*[
|
||||||
|
eq.split()[-1]
|
||||||
|
for eq in equations.split()
|
||||||
|
])
|
||||||
|
constants.words = "Lingering constants on the right"
|
||||||
|
xs, ys, zs = [
|
||||||
|
VMobject(*[
|
||||||
|
eq.split()[i]
|
||||||
|
for eq in equations.split()
|
||||||
|
])
|
||||||
|
for i in 1, 4, 7
|
||||||
|
]
|
||||||
|
ys.words = "Vertically align variables"
|
||||||
|
colors = [PINK, YELLOW, BLUE_B, BLUE_C, BLUE_D]
|
||||||
|
for mob, color in zip([variables, constants, xs, ys, zs], colors):
|
||||||
|
mob.square = Square(color = color)
|
||||||
|
mob.square.replace(mob, stretch = True)
|
||||||
|
mob.square.scale_in_place(1.1)
|
||||||
|
if hasattr(mob, "words"):
|
||||||
|
mob.words = TextMobject(mob.words)
|
||||||
|
mob.words.highlight(color)
|
||||||
|
mob.words.next_to(mob.square, UP)
|
||||||
|
ys.square.add(xs.square, zs.square)
|
||||||
|
zero_circles = VMobject(*[
|
||||||
|
Circle().replace(mob).scale_in_place(1.3)
|
||||||
|
for mob in [
|
||||||
|
VMobject(*equations.split()[i].split()[j:j+2])
|
||||||
|
for i, j in (1, 3), (2, 6)
|
||||||
|
]
|
||||||
|
])
|
||||||
|
zero_circles.highlight(PINK)
|
||||||
|
zero_circles.words = TextMobject("Add zeros as needed")
|
||||||
|
zero_circles.words.highlight(zero_circles.get_color())
|
||||||
|
zero_circles.words.next_to(equations, UP)
|
||||||
|
|
||||||
|
for mob in variables, constants, ys:
|
||||||
|
self.play(
|
||||||
|
FadeIn(mob.square),
|
||||||
|
FadeIn(mob.words)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(*map(FadeOut, [mob.square, mob.words]))
|
||||||
|
self.play(
|
||||||
|
ShowCreation(zero_circles),
|
||||||
|
Write(zero_circles.words, run_time = 1)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(*map(FadeOut, [zero_circles, zero_circles.words]))
|
||||||
|
self.dither()
|
||||||
|
title = TextMobject("``Linear system of equations''")
|
||||||
|
title.scale(1.5)
|
||||||
|
title.to_edge(UP)
|
||||||
|
self.play(Write(title))
|
||||||
|
self.dither()
|
||||||
|
self.play(FadeOut(title))
|
||||||
|
|
||||||
|
|
||||||
|
def factor_into_matrix(self, equations):
|
||||||
|
coefficients = np.array([
|
||||||
|
np.array(eq.split())[[0, 3, 6]]
|
||||||
|
for eq in equations.split()
|
||||||
|
])
|
||||||
|
variable_arrays = np.array([
|
||||||
|
np.array(eq.split())[[1, 4, 7]]
|
||||||
|
for eq in equations.split()
|
||||||
|
])
|
||||||
|
rhs_entries = np.array([
|
||||||
|
eq.split()[-1]
|
||||||
|
for eq in equations.split()
|
||||||
|
])
|
||||||
|
|
||||||
|
matrix = Matrix(copy.deepcopy(coefficients))
|
||||||
|
x_array = Matrix(copy.deepcopy(variable_arrays[0]))
|
||||||
|
v_array = Matrix(copy.deepcopy(rhs_entries))
|
||||||
|
equals = TexMobject("=")
|
||||||
|
ax_equals_v = VMobject(matrix, x_array, equals, v_array)
|
||||||
|
ax_equals_v.arrange_submobjects(RIGHT)
|
||||||
|
ax_equals_v.to_edge(RIGHT)
|
||||||
|
all_brackets = [
|
||||||
|
mob.get_brackets()
|
||||||
|
for mob in matrix, x_array, v_array
|
||||||
|
]
|
||||||
|
|
||||||
|
self.play(equations.to_edge, LEFT)
|
||||||
|
arrow = Vector(RIGHT, color = YELLOW)
|
||||||
|
arrow.next_to(ax_equals_v, LEFT)
|
||||||
|
self.play(ShowCreation(arrow))
|
||||||
|
self.play(*it.chain(*[
|
||||||
|
[
|
||||||
|
Transform(
|
||||||
|
m1.copy(), m2,
|
||||||
|
run_time = 2,
|
||||||
|
path_arc = -np.pi/2
|
||||||
|
)
|
||||||
|
for m1, m2 in zip(
|
||||||
|
start_array.flatten(),
|
||||||
|
matrix_mobject.get_entries().split()
|
||||||
|
)
|
||||||
|
]
|
||||||
|
for start_array, matrix_mobject in [
|
||||||
|
(coefficients, matrix),
|
||||||
|
(variable_arrays[0], x_array),
|
||||||
|
(variable_arrays[1], x_array),
|
||||||
|
(variable_arrays[2], x_array),
|
||||||
|
(rhs_entries, v_array)
|
||||||
|
]
|
||||||
|
]))
|
||||||
|
self.play(*[
|
||||||
|
Write(mob)
|
||||||
|
for mob in all_brackets + [equals]
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
self.label_matrix_product(matrix, x_array, v_array)
|
||||||
|
|
||||||
|
def label_matrix_product(self, matrix, x_array, v_array):
|
||||||
|
matrix.words = "Coefficients"
|
||||||
|
matrix.symbol = "A"
|
||||||
|
x_array.words = "Variables"
|
||||||
|
x_array.symbol = "\\vec{\\textbf{x}}"
|
||||||
|
v_array.words = "Constants"
|
||||||
|
v_array.symbol = "\\vec{\\textbf{v}}"
|
||||||
|
parts = matrix, x_array, v_array
|
||||||
|
for mob in parts:
|
||||||
|
mob.brace = Brace(mob, UP)
|
||||||
|
mob.words = mob.brace.get_text(mob.words)
|
||||||
|
mob.words.shift_onto_screen()
|
||||||
|
mob.symbol = TexMobject(mob.symbol)
|
||||||
|
mob.brace.put_at_tip(mob.symbol)
|
||||||
|
x_array.words.submobject_gradient_highlight(
|
||||||
|
X_COLOR, Y_COLOR, Z_COLOR
|
||||||
|
)
|
||||||
|
x_array.symbol.highlight(PINK)
|
||||||
|
v_array.symbol.highlight(YELLOW)
|
||||||
|
for mob in parts:
|
||||||
|
self.play(
|
||||||
|
GrowFromCenter(mob.brace),
|
||||||
|
FadeIn(mob.words)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(*map(FadeOut, [mob.brace, mob.words]))
|
||||||
|
self.dither()
|
||||||
|
for mob in parts:
|
||||||
|
self.play(
|
||||||
|
FadeIn(mob.brace),
|
||||||
|
Write(mob.symbol)
|
||||||
|
)
|
||||||
|
compact_equation = VMobject(*[
|
||||||
|
mob.symbol for mob in parts
|
||||||
|
])
|
||||||
|
compact_equation.submobjects.insert(
|
||||||
|
2, TexMobject("=").next_to(x_array, RIGHT)
|
||||||
|
)
|
||||||
|
compact_equation.target = compact_equation.copy()
|
||||||
|
compact_equation.target.arrange_submobjects(buff = 0.1)
|
||||||
|
compact_equation.target.to_edge(UP)
|
||||||
|
|
||||||
|
self.play(Transform(
|
||||||
|
compact_equation.copy(),
|
||||||
|
compact_equation.target
|
||||||
|
))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class LinearSystemTransformationScene(LinearTransformationScene):
|
||||||
|
def setup(self):
|
||||||
|
LinearTransformationScene.setup(self)
|
||||||
|
equation = TexMobject([
|
||||||
|
"A",
|
||||||
|
"\\vec{\\textbf{x}}",
|
||||||
|
"=",
|
||||||
|
"\\vec{\\textbf{v}}",
|
||||||
|
])
|
||||||
|
equation.scale(1.5)
|
||||||
|
equation.next_to(ORIGIN, LEFT).to_edge(UP)
|
||||||
|
equation.add_background_rectangle()
|
||||||
|
self.add_foreground_mobject(equation)
|
||||||
|
self.equation = equation
|
||||||
|
self.A, self.x, eq, self.v = equation.split()[1].split()
|
||||||
|
self.x.highlight(PINK)
|
||||||
|
self.v.highlight(YELLOW)
|
||||||
|
|
||||||
|
class MentionThatItsATransformation(LinearSystemTransformationScene):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : np.array([[2, 1], [2, 3]])
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
brace = Brace(self.A)
|
||||||
|
words = brace.get_text("Transformation")
|
||||||
|
words.add_background_rectangle()
|
||||||
|
self.play(GrowFromCenter(brace), Write(words, run_time = 1))
|
||||||
|
self.add_foreground_mobject(words, brace)
|
||||||
|
self.apply_transposed_matrix(self.t_matrix)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class LookForX(MentionThatItsATransformation):
|
||||||
|
CONFIG = {
|
||||||
|
"show_basis_vectors" : False
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
v = [-4, - 1]
|
||||||
|
x = np.linalg.solve(self.t_matrix.T, v)
|
||||||
|
v = Vector(v, color = YELLOW)
|
||||||
|
x = Vector(x, color = PINK)
|
||||||
|
v_label = self.get_vector_label(v, "v", color = YELLOW)
|
||||||
|
x_label = self.get_vector_label(x, "x", color = PINK)
|
||||||
|
for label in x_label, v_label:
|
||||||
|
label.add_background_rectangle()
|
||||||
|
self.play(
|
||||||
|
ShowCreation(v),
|
||||||
|
Write(v_label)
|
||||||
|
)
|
||||||
|
self.add_foreground_mobject(v_label)
|
||||||
|
x = self.add_vector(x, animate = False)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(x),
|
||||||
|
Write(x_label)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.add(VMobject(x, x_label).copy().fade())
|
||||||
|
self.apply_transposed_matrix(self.t_matrix)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class SystemOfTwoEquationsTwoUnknowns(Scene):
|
||||||
|
def construct(self):
|
||||||
|
system = TexMobject("""
|
||||||
|
2x + 2y &= -4 \\\\
|
||||||
|
1x + 3y &= -1
|
||||||
|
""")
|
||||||
|
system.to_edge(UP)
|
||||||
|
for indices, color in ((1, 9), X_COLOR), ((4, 12), Y_COLOR):
|
||||||
|
for i in indices:
|
||||||
|
system.split()[i].highlight(color)
|
||||||
|
matrix = Matrix([[2, 2], [1, 3]])
|
||||||
|
v = Matrix([-4, -1])
|
||||||
|
x = Matrix(["x", "y"])
|
||||||
|
x.get_entries().submobject_gradient_highlight(X_COLOR, Y_COLOR)
|
||||||
|
matrix_system = VMobject(
|
||||||
|
matrix, x, TexMobject("="), v
|
||||||
|
)
|
||||||
|
matrix_system.arrange_submobjects(RIGHT)
|
||||||
|
matrix_system.next_to(system, DOWN, buff = 1)
|
||||||
|
|
||||||
|
self.add(system)
|
||||||
|
self.play(Write(matrix_system))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class ShowBijectivity(LinearTransformationScene):
|
||||||
|
CONFIG = {
|
||||||
|
"show_basis_vectors" : False,
|
||||||
|
"t_matrix" : np.array([[0, -1], [2, 1]])
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
vectors = VMobject(*[
|
||||||
|
Vector([x, y])
|
||||||
|
for x, y in it.product(*[
|
||||||
|
np.arange(-int(val)+0.5, int(val)+0.5)
|
||||||
|
for val in SPACE_WIDTH, SPACE_HEIGHT
|
||||||
|
])
|
||||||
|
])
|
||||||
|
vectors.submobject_gradient_highlight(BLUE_E, PINK)
|
||||||
|
dots = VMobject(*[
|
||||||
|
Dot(v.get_end(), color = v.get_color())
|
||||||
|
for v in vectors.split()
|
||||||
|
])
|
||||||
|
titles = [
|
||||||
|
TextMobject([
|
||||||
|
"Each vector lands on\\\\",
|
||||||
|
"exactly one vector"
|
||||||
|
]),
|
||||||
|
TextMobject([
|
||||||
|
"Every vector has \\\\",
|
||||||
|
"been landed on"
|
||||||
|
])
|
||||||
|
]
|
||||||
|
for title in titles:
|
||||||
|
title.to_edge(UP)
|
||||||
|
background = BackgroundRectangle(VMobject(*titles))
|
||||||
|
self.add_foreground_mobject(background, titles[0])
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
"submobject_mode" : "lagged_start",
|
||||||
|
"run_time" : 2
|
||||||
|
}
|
||||||
|
anims = map(Animation, self.foreground_mobjects)
|
||||||
|
self.play(ShowCreation(vectors, **kwargs), *anims)
|
||||||
|
self.play(Transform(vectors, dots, **kwargs), *anims)
|
||||||
|
self.dither()
|
||||||
|
self.add_transformable_mobject(vectors)
|
||||||
|
self.apply_transposed_matrix(self.t_matrix)
|
||||||
|
self.dither()
|
||||||
|
self.play(Transform(*titles))
|
||||||
|
self.dither()
|
||||||
|
self.apply_transposed_matrix(
|
||||||
|
np.linalg.inv(self.t_matrix.T).T
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class LabeledExample(LinearSystemTransformationScene):
|
||||||
|
CONFIG = {
|
||||||
|
"title" : "",
|
||||||
|
"t_matrix" : [[0, 0], [0, 0]],
|
||||||
|
}
|
||||||
|
def setup(self):
|
||||||
|
LinearSystemTransformationScene.setup(self)
|
||||||
|
title = TextMobject(self.title)
|
||||||
|
title.scale(1.5)
|
||||||
|
title.next_to(self.equation, DOWN)
|
||||||
|
title.add_background_rectangle()
|
||||||
|
self.add_foreground_mobject(title)
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
self.dither()
|
||||||
|
self.apply_transposed_matrix(self.t_matrix)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class SquishExmapleWithWords(LabeledExample):
|
||||||
|
CONFIG = {
|
||||||
|
"title" : "$A$ squishes things to a lower dimension",
|
||||||
|
"t_matrix" : [[-2, -1], [2, 1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
class FullRankExmapleWithWords(LabeledExample):
|
||||||
|
CONFIG = {
|
||||||
|
"title" : "$A$ keeps things 2D",
|
||||||
|
"t_matrix" : [[3, 0], [2, 1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
class SquishExmapleDet(SquishExmapleWithWords):
|
||||||
|
CONFIG = {
|
||||||
|
"title" : "$\\det(A) = 0$",
|
||||||
|
}
|
||||||
|
|
||||||
|
class FullRankExmapleDet(FullRankExmapleWithWords):
|
||||||
|
CONFIG = {
|
||||||
|
"title" : "$\\det(A) \\ne 0$",
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlayInReverse(FullRankExmapleDet):
|
||||||
|
CONFIG = {
|
||||||
|
"show_basis_vectors" : False
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
FullRankExmapleDet.construct(self)
|
||||||
|
v = self.add_vector([-2, -2], color = YELLOW)
|
||||||
|
v_label = self.label_vector(v, "v", color = YELLOW)
|
||||||
|
self.add(v.copy())
|
||||||
|
self.apply_inverse_transpose(self.t_matrix)
|
||||||
|
self.play(v.highlight, PINK)
|
||||||
|
self.label_vector(v, "x", color = PINK)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class DescribeInverse(LinearTransformationScene):
|
||||||
|
CONFIG = {
|
||||||
|
"show_matrix" : False
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
title = TextMobject("Transformation:")
|
||||||
|
new_title = TextMobject("Inverse transformation:")
|
||||||
|
if self.show_matrix:
|
||||||
|
matrix = Matrix(self.t_matrix.T)
|
||||||
|
inv_matrix = Matrix(np.linalg.inv(self.t_matrix.T).astype('int'))
|
||||||
|
else:
|
||||||
|
matrix, inv_matrix = map(TexMobject, ["A", "A^{-1}"])
|
||||||
|
for m, text in (matrix, title), (inv_matrix, new_title):
|
||||||
|
m.rect = BackgroundRectangle(m)
|
||||||
|
m = VMobject(m.rect, m)
|
||||||
|
text.add_background_rectangle()
|
||||||
|
m.next_to(text, RIGHT)
|
||||||
|
text.add(m)
|
||||||
|
if text.get_width() > 2*SPACE_WIDTH-1:
|
||||||
|
text.scale_to_fit_width(2*SPACE_WIDTH-1)
|
||||||
|
text.center().to_edge(UP)
|
||||||
|
|
||||||
|
self.add_foreground_mobject(title)
|
||||||
|
self.apply_transposed_matrix(self.t_matrix)
|
||||||
|
self.dither()
|
||||||
|
self.play(Transform(title, new_title))
|
||||||
|
self.apply_inverse_transpose(self.t_matrix)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class ClockwiseCounterclockwise(DescribeInverse):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : [[0, 1], [-1, 0]],
|
||||||
|
"show_matrix" : True,
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShearInverseShear(DescribeInverse):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : [[1, 0], [1, 1]],
|
||||||
|
"show_matrix" : True,
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultiplyToIdentity(LinearTransformationScene):
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
lhs = TexMobject("A", "A^{-1}", "=")
|
||||||
|
lhs.scale(1.5)
|
||||||
|
A, A_inv, eq = lhs.split()
|
||||||
|
identity = Matrix([[1, 0], [0, 1]])
|
||||||
|
identity.highlight_columns(X_COLOR, Y_COLOR)
|
||||||
|
identity.next_to(eq, RIGHT)
|
||||||
|
VMobject(lhs, identity).center().to_edge(UP)
|
||||||
|
lhs.add_background_rectangle()
|
||||||
|
identity.add_to_back(BackgroundRectangle(identity))
|
||||||
|
|
||||||
|
col1 = VMobject(*identity.get_mob_matrix[:,0])
|
||||||
|
col2 = VMobject(*identity.get_mob_matrix[:,1])
|
||||||
|
|
||||||
|
A.text = "Transformation"
|
||||||
|
A_inv.text = "Inverse transformation"
|
||||||
|
product = VMobject(A, A_inv)
|
||||||
|
product.text = "Matrix multiplication"
|
||||||
|
identity.text = "The transformation that does nothing"
|
||||||
|
for mob in A, A_inv, product, identity:
|
||||||
|
mob.brace = Brace(mob)
|
||||||
|
mob.text = mob.brace.get_text(mob.text)
|
||||||
|
mob.text.add_background_rectangle()
|
||||||
|
|
||||||
|
self.add(A, A_inv)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
96
eola/thumbnails.py
Normal file
96
eola/thumbnails.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
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 *
|
||||||
|
|
||||||
|
class Chapter0(LinearTransformationScene):
|
||||||
|
CONFIG = {
|
||||||
|
"include_background_plane" : False,
|
||||||
|
"t_matrix" : [[3, 1], [2, -1]]
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.setup()
|
||||||
|
self.plane.fade()
|
||||||
|
for mob in self.get_mobjects():
|
||||||
|
mob.set_stroke(width = 6)
|
||||||
|
self.apply_transposed_matrix(self.t_matrix, run_time = 0)
|
||||||
|
|
||||||
|
class Chapter1(Scene):
|
||||||
|
def construct(self):
|
||||||
|
arrow = Vector(2*UP+RIGHT)
|
||||||
|
vs = TextMobject("vs.")
|
||||||
|
array = Matrix([1, 2])
|
||||||
|
array.highlight(TEAL)
|
||||||
|
everyone = VMobject(arrow, vs, array)
|
||||||
|
everyone.arrange_submobjects(RIGHT, buff = 0.5)
|
||||||
|
everyone.scale_to_fit_height(4)
|
||||||
|
self.add(everyone)
|
||||||
|
|
||||||
|
class Chapter2(LinearTransformationScene):
|
||||||
|
def construct(self):
|
||||||
|
self.lock_in_faded_grid()
|
||||||
|
vectors = VMobject(*[
|
||||||
|
Vector([x, y])
|
||||||
|
for x in np.arange(-int(SPACE_WIDTH)+0.5, int(SPACE_WIDTH)+0.5)
|
||||||
|
for y in np.arange(-int(SPACE_HEIGHT)+0.5, int(SPACE_HEIGHT)+0.5)
|
||||||
|
])
|
||||||
|
vectors.submobject_gradient_highlight(PINK, BLUE_E)
|
||||||
|
words = TextMobject("Span")
|
||||||
|
words.scale(3)
|
||||||
|
words.to_edge(UP)
|
||||||
|
words.add_background_rectangle()
|
||||||
|
self.add(vectors, words)
|
||||||
|
|
||||||
|
|
||||||
|
class Chapter3(Chapter0):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : [[3, 0], [2, -1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
class Chapter4p1(Chapter0):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : [[1, 0], [1, 1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
class Chapter4p2(Chapter0):
|
||||||
|
CONFIG = {
|
||||||
|
"t_matrix" : [[1, 2], [-1, 1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -257,6 +257,7 @@ class LinearTransformationScene(VectorScene):
|
||||||
"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" : np.array([[3, 0], [1, 2]]),
|
||||||
}
|
}
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.background_mobjects = []
|
self.background_mobjects = []
|
||||||
|
@ -266,7 +267,7 @@ class LinearTransformationScene(VectorScene):
|
||||||
self.transformable_labels = []
|
self.transformable_labels = []
|
||||||
self.moving_mobjects = []
|
self.moving_mobjects = []
|
||||||
|
|
||||||
|
self.t_matrix = np.array(self.t_matrix)
|
||||||
self.background_plane = NumberPlane(
|
self.background_plane = NumberPlane(
|
||||||
**self.background_plane_kwargs
|
**self.background_plane_kwargs
|
||||||
)
|
)
|
||||||
|
@ -289,6 +290,7 @@ class LinearTransformationScene(VectorScene):
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
if mobject not in mob_list:
|
if mobject not in mob_list:
|
||||||
|
@ -405,6 +407,10 @@ class LinearTransformationScene(VectorScene):
|
||||||
kwargs["path_arc"] = net_rotation
|
kwargs["path_arc"] = net_rotation
|
||||||
self.apply_function(func, **kwargs)
|
self.apply_function(func, **kwargs)
|
||||||
|
|
||||||
|
def apply_inverse_transpose(self, t_matrix, **kwargs):
|
||||||
|
t_inv = np.linalg.inv(np.array(t_matrix).T).T
|
||||||
|
self.apply_transposed_matrix(t_inv, **kwargs)
|
||||||
|
|
||||||
def apply_nonlinear_transformation(self, function, **kwargs):
|
def apply_nonlinear_transformation(self, function, **kwargs):
|
||||||
self.plane.prepare_for_nonlinear_transform()
|
self.plane.prepare_for_nonlinear_transform()
|
||||||
self.apply_function(function, **kwargs)
|
self.apply_function(function, **kwargs)
|
||||||
|
|
|
@ -51,6 +51,11 @@ class Mobject(object):
|
||||||
self.submobjects = list_update(self.submobjects, mobjects)
|
self.submobjects = list_update(self.submobjects, mobjects)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def add_to_back(self, *mobjects):
|
||||||
|
self.remove(*mobjects)
|
||||||
|
self.submobjects = list(mobjects) + self.submobjects
|
||||||
|
return self
|
||||||
|
|
||||||
def remove(self, *mobjects):
|
def remove(self, *mobjects):
|
||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
if mobject in self.submobjects:
|
if mobject in self.submobjects:
|
||||||
|
@ -298,17 +303,25 @@ class Mobject(object):
|
||||||
def gradient_highlight(self, start_color, end_color):
|
def gradient_highlight(self, start_color, end_color):
|
||||||
raise Exception("Not implemented")
|
raise Exception("Not implemented")
|
||||||
|
|
||||||
def submobject_gradient_highlight(self, start_color, end_color, use_color_range_to = False):
|
def submobject_gradient_highlight(self, *colors):
|
||||||
|
if len(colors) == 0:
|
||||||
|
raise Exception("Need at least one color")
|
||||||
|
elif len(colors) == 1:
|
||||||
|
return self.highlight(*colors)
|
||||||
|
|
||||||
mobs = self.family_members_with_points()
|
mobs = self.family_members_with_points()
|
||||||
if use_color_range_to:
|
rgbs = map(color_to_rgb, colors)
|
||||||
colors = Color(start_color).range_to(end_color, len(mobs))
|
alphas = np.linspace(0, (len(rgbs) - 1), len(mobs))
|
||||||
else:
|
floors = alphas.astype('int')
|
||||||
rgb1, rgb2 = map(color_to_rgb, [start_color, end_color])
|
alphas_mod1 = alphas % 1
|
||||||
colors = [
|
#End edge case
|
||||||
Color(rgb = interpolate(rgb1, rgb2, alpha))
|
alphas_mod1[-1] = 1
|
||||||
for alpha in np.linspace(0, 1, len(mobs))
|
floors[-1] = len(rgbs) - 2
|
||||||
]
|
new_colors = [
|
||||||
for mob, color in zip(mobs, colors):
|
Color(rgb = interpolate(rgbs[i], rgbs[i+1], alpha))
|
||||||
|
for i, alpha in zip(floors, alphas_mod1)
|
||||||
|
]
|
||||||
|
for mob, color in zip(mobs, new_colors):
|
||||||
mob.highlight(color, family = False)
|
mob.highlight(color, family = False)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -337,6 +350,15 @@ class Mobject(object):
|
||||||
return self.color
|
return self.color
|
||||||
##
|
##
|
||||||
|
|
||||||
|
def save_state(self):
|
||||||
|
self.saved_state = self.copy()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def restore(self):
|
||||||
|
if not hasattr(self, "saved_state"):
|
||||||
|
raise Exception("Trying to restore without having saved")
|
||||||
|
self.__dict__.update(self.saved_state.__dict__)
|
||||||
|
return self
|
||||||
|
|
||||||
def apply_complex_function(self, function):
|
def apply_complex_function(self, function):
|
||||||
return self.apply_function(
|
return self.apply_function(
|
||||||
|
|
|
@ -39,26 +39,18 @@ class TexMobject(SVGMobject):
|
||||||
"organize_left_to_right" : False,
|
"organize_left_to_right" : False,
|
||||||
"propogate_style_to_family" : True,
|
"propogate_style_to_family" : True,
|
||||||
}
|
}
|
||||||
def __init__(self, expression, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
digest_config(self, kwargs, locals())
|
digest_config(self, kwargs, locals())
|
||||||
self.is_input_a_list = isinstance(expression, list)
|
##TODO, Eventually remove this
|
||||||
|
if len(args) == 1 and isinstance(args[0], list):
|
||||||
|
args = args[0]
|
||||||
|
##
|
||||||
|
assert(all([isinstance(a, str) for a in args]))
|
||||||
VMobject.__init__(self, **kwargs)
|
VMobject.__init__(self, **kwargs)
|
||||||
self.move_into_position()
|
self.move_into_position()
|
||||||
if self.organize_left_to_right:
|
if self.organize_left_to_right:
|
||||||
self.organize_submobjects_left_to_right()
|
self.organize_submobjects_left_to_right()
|
||||||
|
|
||||||
def handle_input_type(self):
|
|
||||||
if isinstance(self.expression, str):
|
|
||||||
self.is_input_a_list = False
|
|
||||||
elif isinstance(self.expression, collections.Iterable):
|
|
||||||
self.is_input_a_list = True
|
|
||||||
self.expression = list(self.expression)
|
|
||||||
else:
|
|
||||||
raise Exception(
|
|
||||||
"TexMobject was expecting string or list, got " + \
|
|
||||||
str(type(self.expression)) + \
|
|
||||||
" instead."
|
|
||||||
)
|
|
||||||
|
|
||||||
def path_string_to_mobject(self, path_string):
|
def path_string_to_mobject(self, path_string):
|
||||||
#Overwrite superclass default to use
|
#Overwrite superclass default to use
|
||||||
|
@ -72,22 +64,22 @@ class TexMobject(SVGMobject):
|
||||||
self.template_tex_file
|
self.template_tex_file
|
||||||
)
|
)
|
||||||
SVGMobject.generate_points(self)
|
SVGMobject.generate_points(self)
|
||||||
if self.is_input_a_list:
|
if len(self.args) > 1:
|
||||||
self.handle_list_expression(self.expression)
|
self.handle_multiple_args()
|
||||||
|
|
||||||
def get_modified_expression(self):
|
def get_modified_expression(self):
|
||||||
separator = ""
|
separator = ""
|
||||||
if self.is_input_a_list and self.separate_list_arg_with_spaces:
|
if self.separate_list_arg_with_spaces:
|
||||||
separator = " "
|
separator = " "
|
||||||
result = separator.join(self.expression)
|
result = separator.join(self.args)
|
||||||
if self.enforce_new_line_structure:
|
if self.enforce_new_line_structure:
|
||||||
result = result.replace("\n", " \\\\ \n ")
|
result = result.replace("\n", " \\\\ \n ")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def handle_list_expression(self, list_expression):
|
def handle_multiple_args(self):
|
||||||
new_submobjects = []
|
new_submobjects = []
|
||||||
curr_index = 0
|
curr_index = 0
|
||||||
for expr in list_expression:
|
for expr in self.args:
|
||||||
model = TexMobject(expr, **self.CONFIG)
|
model = TexMobject(expr, **self.CONFIG)
|
||||||
new_index = curr_index + len(model.submobjects)
|
new_index = curr_index + len(model.submobjects)
|
||||||
new_submobjects.append(VMobject(
|
new_submobjects.append(VMobject(
|
||||||
|
@ -108,7 +100,7 @@ class TexMobject(SVGMobject):
|
||||||
fill_opacity = opacity
|
fill_opacity = opacity
|
||||||
)
|
)
|
||||||
letters = VMobject(*self.submobjects)
|
letters = VMobject(*self.submobjects)
|
||||||
self.submobjects = [rect, letters]
|
self.add_to_back(rect)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
class TextMobject(TexMobject):
|
class TextMobject(TexMobject):
|
||||||
|
|
|
@ -5,6 +5,7 @@ from mobject.svg_mobject import SVGMobject
|
||||||
from mobject.vectorized_mobject import VMobject
|
from mobject.vectorized_mobject import VMobject
|
||||||
from mobject.tex_mobject import TextMobject
|
from mobject.tex_mobject import TextMobject
|
||||||
|
|
||||||
|
from animation import Animation
|
||||||
from animation.transform import Transform, ApplyMethod, FadeOut, FadeIn
|
from animation.transform import Transform, ApplyMethod, FadeOut, FadeIn
|
||||||
from animation.simple_animations import Write
|
from animation.simple_animations import Write
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
|
@ -107,6 +108,7 @@ class PiCreature(SVGMobject):
|
||||||
pupil.shift(nudge_size*UP)
|
pupil.shift(nudge_size*UP)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
def get_looking_direction(self):
|
def get_looking_direction(self):
|
||||||
return np.sign(np.round(
|
return np.sign(np.round(
|
||||||
self.pupils.get_center() - self.eyes.get_center(),
|
self.pupils.get_center() - self.eyes.get_center(),
|
||||||
|
@ -166,6 +168,35 @@ class Blink(ApplyMethod):
|
||||||
def __init__(self, pi_creature, **kwargs):
|
def __init__(self, pi_creature, **kwargs):
|
||||||
ApplyMethod.__init__(self, pi_creature.blink, **kwargs)
|
ApplyMethod.__init__(self, pi_creature.blink, **kwargs)
|
||||||
|
|
||||||
|
class DoTheWave(Transform):
|
||||||
|
CONFIG = {
|
||||||
|
"run_time" : 2
|
||||||
|
}
|
||||||
|
def __init__(self, pi_creature, **kwargs):
|
||||||
|
start_state = pi_creature.copy()
|
||||||
|
self.target_states = [
|
||||||
|
pi_creature.copy().change_mode("wave_%d"%x)
|
||||||
|
for x in 1, 2, 3
|
||||||
|
] + [
|
||||||
|
pi_creature.copy()
|
||||||
|
]
|
||||||
|
Transform.__init__(self, pi_creature, self.target_states[0], **kwargs)
|
||||||
|
|
||||||
|
def update_mobject(self, alpha):
|
||||||
|
scaled = alpha*len(self.target_states)
|
||||||
|
try:
|
||||||
|
if scaled-1 > 0:
|
||||||
|
self.starting_mobject = self.target_states[int(scaled)-1]
|
||||||
|
self.ending_mobject = self.target_states[int(scaled)]
|
||||||
|
except IndexError:
|
||||||
|
self.ending_mobject = self.target_states[-1]
|
||||||
|
Transform.update_mobject(self, scaled%1)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Bubble(SVGMobject):
|
class Bubble(SVGMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"direction" : LEFT,
|
"direction" : LEFT,
|
||||||
|
@ -275,6 +306,7 @@ class TeacherStudentsScene(Scene):
|
||||||
self.students.arrange_submobjects(RIGHT)
|
self.students.arrange_submobjects(RIGHT)
|
||||||
self.students.scale(0.8)
|
self.students.scale(0.8)
|
||||||
self.students.to_corner(DOWN+LEFT)
|
self.students.to_corner(DOWN+LEFT)
|
||||||
|
self.students = self.students.split()
|
||||||
|
|
||||||
for pi_creature in self.get_everyone():
|
for pi_creature in self.get_everyone():
|
||||||
pi_creature.bubble = None
|
pi_creature.bubble = None
|
||||||
|
@ -284,7 +316,7 @@ class TeacherStudentsScene(Scene):
|
||||||
return self.teacher
|
return self.teacher
|
||||||
|
|
||||||
def get_students(self):
|
def get_students(self):
|
||||||
return self.students.split()
|
return self.students
|
||||||
|
|
||||||
def get_everyone(self):
|
def get_everyone(self):
|
||||||
return [self.get_teacher()] + self.get_students()
|
return [self.get_teacher()] + self.get_students()
|
||||||
|
|
Loading…
Add table
Reference in a new issue