mirror of
https://github.com/3b1b/videos.git
synced 2025-08-05 16:48:47 +00:00
910 lines
29 KiB
Python
910 lines
29 KiB
Python
![]() |
from manim_imports_ext import *
|
||
|
|
||
|
# Colors
|
||
|
|
||
|
COL1_COLOR = MAROON_B
|
||
|
COL2_COLOR = MAROON_C
|
||
|
LAMBDA1_COLOR = TEAL_A
|
||
|
LAMBDA2_COLOR = TEAL_D
|
||
|
MEAN_COLOR = BLUE_B
|
||
|
PROD_COLOR = BLUE_D
|
||
|
|
||
|
|
||
|
# Scenes
|
||
|
|
||
|
class Assumptions(TeacherStudentsScene):
|
||
|
def construct(self):
|
||
|
self.play(
|
||
|
PiCreatureSays(self.teacher, TexText("I'm assuming you know\\\\ what eigenvalues are.")),
|
||
|
self.get_student_changes(
|
||
|
"erm", "happy", "tease",
|
||
|
look_at_arg=ORIGIN,
|
||
|
)
|
||
|
)
|
||
|
self.play(self.students[0].animate.change("guilty").look(LEFT))
|
||
|
self.wait()
|
||
|
|
||
|
eigen_expression = Tex("""
|
||
|
\\text{det}\\left( \\left[ \\begin{array}{cc}
|
||
|
3 - \\lambda & 1 \\\\
|
||
|
4 & 1 - \\lambda
|
||
|
\\end{array} \\right] \\right)
|
||
|
""")
|
||
|
eigen_expression.move_to(self.hold_up_spot, DOWN)
|
||
|
eigen_expression.to_edge(RIGHT, buff=2)
|
||
|
VGroup(eigen_expression[0][7], eigen_expression[0][12]).set_color(TEAL)
|
||
|
cross = Cross(eigen_expression)
|
||
|
cross.set_stroke(RED, width=(1, 5, 5, 1))
|
||
|
|
||
|
self.play(
|
||
|
RemovePiCreatureBubble(self.teacher, target_mode="raise_right_hand"),
|
||
|
FadeIn(eigen_expression, UP),
|
||
|
self.students[1].animate.change("hesitant"),
|
||
|
self.students[2].animate.change("sassy"),
|
||
|
)
|
||
|
self.wait()
|
||
|
self.play(
|
||
|
ShowCreation(cross),
|
||
|
self.teacher.animate.change("tease", cross),
|
||
|
self.students[1].animate.change("pondering", cross),
|
||
|
self.students[2].animate.change("confused", cross),
|
||
|
)
|
||
|
self.wait(3)
|
||
|
|
||
|
self.embed()
|
||
|
|
||
|
|
||
|
class PreviousVideoWrapper(Scene):
|
||
|
def construct(self):
|
||
|
self.add(FullScreenRectangle())
|
||
|
|
||
|
title = Text("Video introducing eigenvalues", font_size=72)
|
||
|
screen = ScreenRectangle(height=6)
|
||
|
screen.set_fill(BLACK, 1)
|
||
|
screen.set_stroke(BLUE, 1)
|
||
|
title.match_width(screen)
|
||
|
title.to_edge(UP)
|
||
|
screen.next_to(title, DOWN)
|
||
|
|
||
|
self.add(screen)
|
||
|
self.play(FadeIn(title, UP))
|
||
|
self.wait(2)
|
||
|
self.play(
|
||
|
FadeOut(title, UP),
|
||
|
screen.animate.set_height(7).center(),
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
|
||
|
class SneakierEigenVector(ExternallyAnimatedScene):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class TypicalComputation(Scene):
|
||
|
def construct(self):
|
||
|
# Task
|
||
|
words, mat = task = VGroup(
|
||
|
Text("Find the eigenvalues of ", t2c={"eigenvalues": TEAL}),
|
||
|
IntegerMatrix([[3, 1], [4, 1]]).set_height(1),
|
||
|
)
|
||
|
task.arrange(RIGHT, buff=MED_LARGE_BUFF)
|
||
|
task.to_edge(UP)
|
||
|
self.add(task)
|
||
|
|
||
|
# Top line of the computation
|
||
|
det_expression = Tex("""
|
||
|
\\text{det}\\left( \\left[ \\begin{array}{cc}
|
||
|
3 - \\lambda & 1 \\\\
|
||
|
4 & 1 - \\lambda
|
||
|
\\end{array} \\right] \\right)
|
||
|
""")[0]
|
||
|
lambdas = VGroup(det_expression[7], det_expression[12])
|
||
|
lambdas.set_color(TEAL)
|
||
|
t0, t1, t2, t3 = terms = VGroup(
|
||
|
det_expression[5:8],
|
||
|
det_expression[8:9],
|
||
|
det_expression[9:10],
|
||
|
det_expression[10:13],
|
||
|
).copy()
|
||
|
p_height = terms[0].get_height() * 1.5
|
||
|
for term in terms:
|
||
|
parens = Tex("(", ")")
|
||
|
parens.set_height(p_height)
|
||
|
parens[0].next_to(term, LEFT, 0.5 * SMALL_BUFF)
|
||
|
parens[1].next_to(term, RIGHT, 0.5 * SMALL_BUFF)
|
||
|
term.parens = parens
|
||
|
term.parens.set_opacity(0)
|
||
|
term.add(term.parens)
|
||
|
|
||
|
eq = Tex("=")
|
||
|
eq.next_to(det_expression, RIGHT)
|
||
|
rhs = VGroup(
|
||
|
t0.copy(), t3.copy(), Tex("-"), t1.copy(), t2.copy()
|
||
|
)
|
||
|
rhs.arrange(RIGHT)
|
||
|
rhs.next_to(eq, RIGHT)
|
||
|
rhs.set_opacity(1)
|
||
|
VGroup(det_expression, terms, eq, rhs).next_to(task, DOWN, LARGE_BUFF)
|
||
|
|
||
|
movers = VGroup(det_expression[5], det_expression[10])
|
||
|
movers.save_state()
|
||
|
movers[0].move_to(det_expression[6])
|
||
|
movers[1].move_to(det_expression[11])
|
||
|
|
||
|
self.play(
|
||
|
TransformFromCopy(mat.get_brackets(), VGroup(*(det_expression[i] for i in [4, 13]))),
|
||
|
TransformFromCopy(mat.get_entries(), VGroup(*(det_expression[i] for i in [5, 8, 9, 10]))),
|
||
|
FadeTransform(
|
||
|
mat.get_brackets().copy().set_opacity(0),
|
||
|
VGroup(*(det_expression[i] for i in [0, 1, 2, 3, 14]))
|
||
|
),
|
||
|
run_time=1
|
||
|
)
|
||
|
self.play(
|
||
|
Write(VGroup(*(det_expression[i] for i in [6, 7, 11, 12]))),
|
||
|
Restore(movers),
|
||
|
)
|
||
|
self.wait()
|
||
|
self.add(det_expression)
|
||
|
|
||
|
self.play(
|
||
|
FadeIn(eq),
|
||
|
TransformFromCopy(VGroup(t0, t3), rhs[:2]),
|
||
|
)
|
||
|
self.play(
|
||
|
FadeIn(rhs[2]),
|
||
|
TransformFromCopy(VGroup(t1, t2), rhs[3:])
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Line 2
|
||
|
eq2 = eq.copy()
|
||
|
eq2.shift(DOWN)
|
||
|
self.add(eq2)
|
||
|
rhs2 = Tex("\\left( 3 - 4\\lambda + \\lambda^2 \\right) - 4")[0]
|
||
|
rhs2.next_to(eq2, RIGHT)
|
||
|
VGroup(rhs2[4], rhs2[6]).set_color(TEAL)
|
||
|
|
||
|
top_terms = VGroup(
|
||
|
VGroup(rhs[0][0], rhs[0][2]),
|
||
|
VGroup(rhs[1][0], rhs[1][2]),
|
||
|
)
|
||
|
alt_mid = Tex("-3\\lambda", tex_to_color_map={"\\lambda": TEAL})
|
||
|
alt_mid.move_to(rhs2[2:5], DL)
|
||
|
bottom_terms = VGroup(rhs2[1], alt_mid, rhs2[2:5], rhs2[5:8])
|
||
|
for pair, bt in zip(it.product(*top_terms), bottom_terms):
|
||
|
rects = VGroup(*(SurroundingRectangle(t, buff=SMALL_BUFF) for t in pair))
|
||
|
self.add(rects)
|
||
|
self.add(bt)
|
||
|
self.wait(0.5)
|
||
|
if bt is alt_mid:
|
||
|
self.remove(bt)
|
||
|
self.remove(rects)
|
||
|
self.play(
|
||
|
FadeIn(VGroup(rhs2[0], rhs2[8])),
|
||
|
FadeTransform(rhs[2:].copy(), rhs2[9:])
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Line 3
|
||
|
eq3 = eq2.copy().shift(DOWN)
|
||
|
rhs3 = Tex("\\lambda^2 - 4 \\lambda - 1")[0]
|
||
|
rhs3.next_to(eq3, RIGHT)
|
||
|
VGroup(rhs3[0], rhs3[4]).set_color(TEAL)
|
||
|
|
||
|
kw = {"path_arc": 45 * DEGREES}
|
||
|
self.play(LaggedStart(
|
||
|
TransformFromCopy(eq2, eq3, **kw),
|
||
|
Transform(rhs2[1].copy(), rhs3[6].copy(), remover=True, **kw),
|
||
|
TransformFromCopy(rhs2[9:11], rhs3[5:], **kw),
|
||
|
TransformFromCopy(rhs2[2:5], rhs3[2:5], **kw),
|
||
|
TransformFromCopy(rhs2[6:8], rhs3[0:2], **kw),
|
||
|
run_time=1.5, lag_ratio=0.02,
|
||
|
))
|
||
|
self.wait()
|
||
|
|
||
|
# Characteristic polynomial
|
||
|
brace = Brace(rhs3, DOWN)
|
||
|
char_poly = VGroup(
|
||
|
Text("Characteristic polynomial of", font_size=30, fill_color=BLUE),
|
||
|
mat.copy()
|
||
|
)
|
||
|
char_poly.arrange(RIGHT)
|
||
|
char_poly.next_to(brace, DOWN)
|
||
|
char_poly.shift_onto_screen()
|
||
|
|
||
|
self.play(
|
||
|
GrowFromCenter(brace),
|
||
|
FadeIn(char_poly, DOWN),
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Roots
|
||
|
equals_zero = Tex("= 0")
|
||
|
equals_zero.next_to(rhs3, RIGHT)
|
||
|
root_words = Tex(
|
||
|
"\\lambda_1, \\lambda_2 \\,=\\, \\text{roots}",
|
||
|
tex_to_color_map={
|
||
|
"\\lambda_1": TEAL_C,
|
||
|
"\\lambda_2": TEAL_B,
|
||
|
"=": WHITE,
|
||
|
}
|
||
|
)
|
||
|
new_rhs3 = VGroup(rhs3, equals_zero)
|
||
|
root_words.next_to(brace, DOWN)
|
||
|
root_words.match_x(new_rhs3)
|
||
|
self.play(
|
||
|
FadeIn(root_words, DOWN),
|
||
|
FadeOut(char_poly, DOWN),
|
||
|
brace.animate.become(Brace(new_rhs3)),
|
||
|
Write(equals_zero),
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Quadratic formula
|
||
|
formula = Tex("\\frac{4 \\pm \\sqrt{4^2 - 4(1)(-1)}}{2}")
|
||
|
formula2 = Tex("=\\frac{4 \\pm \\sqrt{20}}{2}")
|
||
|
formula3 = Tex("= 2 \\pm \\sqrt{5}")
|
||
|
formula.move_to(root_words[-1], LEFT)
|
||
|
formula.shift(0.5 * DL)
|
||
|
|
||
|
self.play(
|
||
|
TransformMatchingShapes(rhs3.copy(), formula),
|
||
|
FadeOut(root_words[-1]),
|
||
|
root_words[:-1].animate.shift(0.5 * DL),
|
||
|
)
|
||
|
self.wait()
|
||
|
solution = VGroup(root_words[:-1], formula)
|
||
|
self.play(
|
||
|
solution.animate.shift(formula2.get_width() * LEFT),
|
||
|
)
|
||
|
formula2.next_to(formula, RIGHT)
|
||
|
self.play(FadeIn(formula2))
|
||
|
self.wait()
|
||
|
solution.add(formula2)
|
||
|
self.play(
|
||
|
solution.animate.shift(formula3.get_width() * LEFT),
|
||
|
)
|
||
|
formula3.next_to(formula2, RIGHT)
|
||
|
self.play(FadeIn(formula3))
|
||
|
self.wait()
|
||
|
|
||
|
# Straight line
|
||
|
full_rect = FullScreenFadeRectangle()
|
||
|
arrow = Arrow(mat, formula3, thickness=0.05)
|
||
|
arrow.set_fill(YELLOW)
|
||
|
|
||
|
self.add(full_rect, task, formula3)
|
||
|
self.play(FadeIn(full_rect))
|
||
|
self.play(GrowArrow(arrow))
|
||
|
self.wait()
|
||
|
|
||
|
|
||
|
class TweakDiagonalValue(ExternallyAnimatedScene):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class DetEquationLineOfReasoning(ExternallyAnimatedScene):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class OutlineThreeFacts(Scene):
|
||
|
def construct(self):
|
||
|
# Matrix to lambdas
|
||
|
mat = Matrix([["a", "b"], ["c", "d"]], v_buff=0.8, h_buff=0.8)
|
||
|
mat.set_column_colors(COL1_COLOR, COL2_COLOR)
|
||
|
|
||
|
lambdas = Tex("\\lambda_1", "\\,,\\,", "\\lambda_2")
|
||
|
lambdas[0].set_color(LAMBDA1_COLOR)
|
||
|
lambdas[2].set_color(LAMBDA2_COLOR)
|
||
|
arrow = Vector(1.5 * RIGHT)
|
||
|
group = VGroup(mat, arrow, lambdas)
|
||
|
group.arrange(RIGHT)
|
||
|
arrow_label = Text("Quick?", font_size=24)
|
||
|
arrow_label.next_to(arrow, UP, buff=0)
|
||
|
|
||
|
self.add(mat)
|
||
|
self.play(
|
||
|
GrowArrow(arrow),
|
||
|
Write(arrow_label, run_time=1),
|
||
|
LaggedStart(*(
|
||
|
AnimationGroup(*(
|
||
|
Transform(entry, lambdas[i])
|
||
|
for entry in mat.get_entries().deepcopy()
|
||
|
))
|
||
|
for i in [0, 2]
|
||
|
), lag_ratio=0.3),
|
||
|
FadeIn(lambdas[1]),
|
||
|
)
|
||
|
self.clear()
|
||
|
self.add(group, arrow_label)
|
||
|
self.wait()
|
||
|
|
||
|
# Three steps
|
||
|
indices = VGroup(*(Text(str(i) + ")", font_size=48) for i in range(1, 4)))
|
||
|
indices.set_color(GREY_B)
|
||
|
indices.arrange(DOWN, aligned_edge=LEFT, buff=2)
|
||
|
indices.to_edge(LEFT)
|
||
|
|
||
|
group.generate_target()
|
||
|
group.target[1].rotate(-90 * DEGREES)
|
||
|
group.target[1].scale(0.5)
|
||
|
group.target.arrange(DOWN)
|
||
|
group.target.to_corner(DR)
|
||
|
|
||
|
self.play(
|
||
|
LaggedStartMap(FadeIn, indices, shift=0.25 * UP, lag_ratio=0.3),
|
||
|
FadeOut(arrow_label),
|
||
|
MoveToTarget(group),
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Trace
|
||
|
tr_mat = mat.deepcopy()
|
||
|
tr = Tex("\\text{tr}", "\\Big(", "\\Big)", font_size=60)
|
||
|
tr[1:].match_height(tr_mat, stretch=True)
|
||
|
tr.set_submobjects([*tr[:-1], tr_mat, tr[-1]])
|
||
|
tr.arrange(RIGHT, buff=SMALL_BUFF)
|
||
|
tr.next_to(indices[0], RIGHT, MED_LARGE_BUFF)
|
||
|
|
||
|
tr_rects = VGroup(
|
||
|
SurroundingRectangle(tr_mat.get_entries()[0]),
|
||
|
SurroundingRectangle(tr_mat.get_entries()[3]),
|
||
|
)
|
||
|
tr_rects.set_color(BLUE_C)
|
||
|
moving_tr_rects = tr_rects.copy()
|
||
|
moving_tr_rects.generate_target()
|
||
|
|
||
|
tex_kw = {
|
||
|
"tex_to_color_map": {
|
||
|
"a": COL1_COLOR,
|
||
|
"b": COL2_COLOR,
|
||
|
"c": COL1_COLOR,
|
||
|
"d": COL2_COLOR,
|
||
|
"=": WHITE,
|
||
|
"\\lambda_1": LAMBDA1_COLOR,
|
||
|
"\\lambda_2": LAMBDA2_COLOR,
|
||
|
}
|
||
|
}
|
||
|
tr_rhs = Tex("= a + d = \\lambda_1 + \\lambda_2", **tex_kw)
|
||
|
tr_rhs.next_to(tr, RIGHT)
|
||
|
|
||
|
for term, rect in zip(tr_rhs[1:4:2], moving_tr_rects.target):
|
||
|
rect.move_to(term)
|
||
|
|
||
|
self.play(
|
||
|
TransformFromCopy(mat, tr_mat),
|
||
|
Write(VGroup(*tr[:2], tr[-1])),
|
||
|
)
|
||
|
self.play(LaggedStart(*map(ShowCreation, tr_rects)))
|
||
|
tr.add(tr_rects)
|
||
|
self.play(
|
||
|
MoveToTarget(moving_tr_rects),
|
||
|
TransformFromCopy(tr_mat.get_entries()[0], tr_rhs.get_part_by_tex("a")),
|
||
|
TransformFromCopy(tr_mat.get_entries()[3], tr_rhs.get_part_by_tex("d")),
|
||
|
FadeIn(tr_rhs[0:4:2]),
|
||
|
)
|
||
|
self.play(FadeOut(moving_tr_rects))
|
||
|
self.wait()
|
||
|
self.play(
|
||
|
TransformMatchingShapes(lambdas.copy(), tr_rhs[4:]),
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Mean of eigenvalues
|
||
|
half = Tex("1 \\over 2")
|
||
|
half.move_to(tr, LEFT)
|
||
|
tr.generate_target()
|
||
|
tr.target.next_to(half, RIGHT, SMALL_BUFF)
|
||
|
new_tr_rhs = Tex("= {a + d \\over 2} = {\\lambda_1 + \\lambda_2 \\over 2}", **tex_kw)
|
||
|
new_tr_rhs.next_to(tr.target, RIGHT)
|
||
|
|
||
|
self.play(
|
||
|
GrowFromCenter(half),
|
||
|
MoveToTarget(tr),
|
||
|
TransformMatchingShapes(tr_rhs, new_tr_rhs),
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Determinant
|
||
|
det_mat = mat.deepcopy()
|
||
|
det = Tex("\\text{det}", "\\Big(", "\\Big)", font_size=60)
|
||
|
det[1:].match_height(det_mat, stretch=True)
|
||
|
det.set_submobjects([*det[:-1], det_mat, det[-1]])
|
||
|
det.arrange(RIGHT, buff=SMALL_BUFF)
|
||
|
det.next_to(indices[1], RIGHT, MED_LARGE_BUFF)
|
||
|
|
||
|
det_rhs = Tex("= ad - bc = \\lambda_1 \\lambda_2", **tex_kw)
|
||
|
det_rhs.next_to(det, RIGHT)
|
||
|
self.play(
|
||
|
TransformFromCopy(mat, det_mat),
|
||
|
Write(VGroup(*det[:2], det[-1])),
|
||
|
)
|
||
|
|
||
|
path = VMobject()
|
||
|
path.set_points_smoothly([
|
||
|
det_mat.get_corner(UL),
|
||
|
*[
|
||
|
det_mat.get_entries()[i].get_center()
|
||
|
for i in [0, 3, 1, 2]
|
||
|
],
|
||
|
det_mat.get_corner(DL),
|
||
|
])
|
||
|
path.set_stroke(BLUE, 3)
|
||
|
|
||
|
self.add(path)
|
||
|
self.play(
|
||
|
VShowPassingFlash(path, time_width=1, run_time=3, rate_function=linear),
|
||
|
LaggedStart(
|
||
|
Animation(Mobject(), remover=True),
|
||
|
FadeIn(det_rhs[:3]),
|
||
|
FadeIn(det_rhs[3:6]),
|
||
|
lag_ratio=0.7,
|
||
|
)
|
||
|
)
|
||
|
self.wait()
|
||
|
self.play(
|
||
|
TransformMatchingShapes(lambdas.copy(), det_rhs[6:])
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Mean and product
|
||
|
eq_m = TexText("=", " $m$", "\\quad (mean)")
|
||
|
eq_m[1].set_color(MEAN_COLOR)
|
||
|
eq_m.next_to(new_tr_rhs, RIGHT)
|
||
|
eq_p = TexText("=", " $p$", "\\quad (product)")
|
||
|
eq_p[1].set_color(PROD_COLOR)
|
||
|
eq_p.next_to(det_rhs, RIGHT)
|
||
|
|
||
|
form_lhs = lambdas.copy()
|
||
|
form_rhs = Tex("= {m} \\pm \\sqrt{\\,{m}^2 - {p}}", tex_to_color_map={"{m}": MEAN_COLOR, "{p}": PROD_COLOR})
|
||
|
form_lhs.next_to(indices[2], RIGHT)
|
||
|
form_rhs.next_to(form_lhs, RIGHT)
|
||
|
|
||
|
third_point_placeholder = Text("(We'll get to this...)", font_size=30)
|
||
|
third_point_placeholder.set_fill(GREY_C)
|
||
|
third_point_placeholder.next_to(indices[2], RIGHT, MED_LARGE_BUFF)
|
||
|
|
||
|
form = VGroup(indices[2], form_lhs, form_rhs)
|
||
|
rect = SurroundingRectangle(VGroup(form), buff=MED_SMALL_BUFF)
|
||
|
|
||
|
randy = Randolph(height=2)
|
||
|
randy.next_to(rect, RIGHT)
|
||
|
randy.to_edge(DOWN)
|
||
|
|
||
|
self.play(
|
||
|
FadeIn(third_point_placeholder),
|
||
|
VFadeIn(randy),
|
||
|
randy.animate.change("erm", third_point_placeholder)
|
||
|
)
|
||
|
self.play(Blink(randy))
|
||
|
self.wait()
|
||
|
self.play(
|
||
|
randy.animate.change("thinking", eq_m),
|
||
|
Write(eq_m)
|
||
|
)
|
||
|
self.play(
|
||
|
randy.animate.look_at(eq_p),
|
||
|
Write(eq_p),
|
||
|
mat.animate.set_height(1, about_edge=DOWN)
|
||
|
)
|
||
|
self.play(Blink(randy))
|
||
|
self.wait()
|
||
|
|
||
|
# Example matrix
|
||
|
ex_mat = IntegerMatrix([[8, 4], [2, 6]])
|
||
|
ex_mat.set_height(1.25)
|
||
|
ex_mat.set_column_colors(COL1_COLOR, COL2_COLOR)
|
||
|
ex_mat.next_to(randy, RIGHT, aligned_edge=UP)
|
||
|
|
||
|
kw = {"tex_to_color_map": {"m": MEAN_COLOR, "p": PROD_COLOR, "=": WHITE, "-": WHITE}}
|
||
|
m_eq = Tex("m = 7", **kw)
|
||
|
p_eq1 = Tex("p = 48 - 8", **kw)
|
||
|
p_eq2 = Tex("p = 40", **kw)
|
||
|
|
||
|
for mob in (m_eq, p_eq1, p_eq2):
|
||
|
mob.next_to(ex_mat, RIGHT, buff=MED_LARGE_BUFF)
|
||
|
m_eq.shift(0.5 * UP)
|
||
|
VGroup(p_eq1, p_eq2).shift(0.5 * DOWN)
|
||
|
|
||
|
diag_rects = VGroup(
|
||
|
SurroundingRectangle(ex_mat.get_entries()[0]),
|
||
|
SurroundingRectangle(ex_mat.get_entries()[3]),
|
||
|
)
|
||
|
off_diag_rects = VGroup(
|
||
|
SurroundingRectangle(ex_mat.get_entries()[1]),
|
||
|
SurroundingRectangle(ex_mat.get_entries()[2]),
|
||
|
)
|
||
|
diag_rects.set_color(PROD_COLOR)
|
||
|
off_diag_rects.set_color(PROD_COLOR)
|
||
|
mean_rect = SurroundingRectangle(m_eq[2])
|
||
|
mean_rect.set_color(MEAN_COLOR)
|
||
|
|
||
|
tr_rect = SurroundingRectangle(VGroup(indices[0], tr, eq_m)).set_stroke(MEAN_COLOR)
|
||
|
det_rect = SurroundingRectangle(VGroup(indices[1], det, eq_p)).set_stroke(PROD_COLOR)
|
||
|
|
||
|
self.play(
|
||
|
randy.animate.change("pondering", ex_mat),
|
||
|
FadeIn(ex_mat, RIGHT),
|
||
|
FadeOut(group, RIGHT),
|
||
|
)
|
||
|
self.play(Blink(randy))
|
||
|
self.wait()
|
||
|
|
||
|
self.play(FadeIn(tr_rect))
|
||
|
self.play(
|
||
|
Write(m_eq[:2]),
|
||
|
LaggedStartMap(ShowCreation, diag_rects, lag_ratio=0.5, run_time=1),
|
||
|
randy.animate.look_at(m_eq),
|
||
|
)
|
||
|
self.wait()
|
||
|
self.remove(diag_rects)
|
||
|
self.play(
|
||
|
TransformFromCopy(diag_rects, mean_rect),
|
||
|
FadeTransform(ex_mat.get_entries()[0].copy(), m_eq[2]),
|
||
|
FadeTransform(ex_mat.get_entries()[3].copy(), m_eq[2]),
|
||
|
)
|
||
|
self.play(Blink(randy))
|
||
|
self.wait()
|
||
|
|
||
|
self.play(FadeOut(tr_rect), FadeIn(det_rect))
|
||
|
self.play(
|
||
|
Write(p_eq1[:2]),
|
||
|
randy.animate.change("hesitant", p_eq1),
|
||
|
FadeOut(mean_rect),
|
||
|
)
|
||
|
|
||
|
path.replace(ex_mat.get_entries(), dim_to_match=1)
|
||
|
self.play(VShowPassingFlash(path, time_width=1, run_time=2))
|
||
|
self.wait()
|
||
|
self.play(
|
||
|
FadeIn(diag_rects),
|
||
|
FadeIn(p_eq1[2]),
|
||
|
)
|
||
|
self.play(
|
||
|
FadeOut(diag_rects),
|
||
|
FadeIn(off_diag_rects),
|
||
|
FadeIn(p_eq1[3:]),
|
||
|
)
|
||
|
self.play(
|
||
|
FadeOut(off_diag_rects),
|
||
|
)
|
||
|
self.play(
|
||
|
randy.animate.change("tease", p_eq2),
|
||
|
FadeOut(p_eq1),
|
||
|
FadeIn(p_eq2),
|
||
|
)
|
||
|
self.play(FadeOut(det_rect))
|
||
|
self.wait()
|
||
|
|
||
|
# Let other stuff happen up top
|
||
|
full_rect = FullScreenFadeRectangle()
|
||
|
full_rect.set_fill(BLACK, 1)
|
||
|
|
||
|
ex = VGroup(ex_mat, m_eq, p_eq2)
|
||
|
|
||
|
self.add(full_rect, randy, ex)
|
||
|
self.play(
|
||
|
FadeIn(full_rect),
|
||
|
randy.animate.change("pondering", ORIGIN)
|
||
|
)
|
||
|
for x in range(10):
|
||
|
if random.random() < 0.5:
|
||
|
self.play(Blink(randy))
|
||
|
else:
|
||
|
self.wait()
|
||
|
|
||
|
# Show final formula
|
||
|
ex_rect = SurroundingRectangle(ex, buff=0.35)
|
||
|
ex_rect.set_stroke(GREY_A)
|
||
|
ex_rect.set_fill(interpolate_color(GREY_E, BLACK, 0.25))
|
||
|
ex_rect.set_opacity(0)
|
||
|
ex_group = VGroup(ex_rect, ex)
|
||
|
ex_group.generate_target()
|
||
|
ex_group.target.set_height(1.5)
|
||
|
ex_group.target.to_corner(DR)
|
||
|
ex_group.target[0].set_opacity(1)
|
||
|
|
||
|
self.play(
|
||
|
FadeOut(full_rect),
|
||
|
FadeOut(randy),
|
||
|
MoveToTarget(ex_group)
|
||
|
)
|
||
|
|
||
|
self.play(
|
||
|
FadeIn(form_lhs, 0.25 * UP),
|
||
|
FadeOut(third_point_placeholder, 0.25 * UP)
|
||
|
)
|
||
|
self.play(TransformMatchingShapes(
|
||
|
VGroup(m_eq[0], p_eq2[0]).copy(),
|
||
|
form_rhs,
|
||
|
))
|
||
|
self.play(ShowCreation(rect))
|
||
|
self.wait()
|
||
|
|
||
|
|
||
|
class ShowSquishingAndStretching(Scene):
|
||
|
def construct(self):
|
||
|
plane = NumberPlane()
|
||
|
|
||
|
self.add(plane)
|
||
|
|
||
|
self.embed()
|
||
|
|
||
|
|
||
|
class MeanProductExample(Scene):
|
||
|
def construct(self):
|
||
|
# Number line and midpoint
|
||
|
number_line = NumberLine((0, 14))
|
||
|
number_line.add_numbers()
|
||
|
number_line.set_width(FRAME_WIDTH - 1)
|
||
|
number_line.to_edge(UP, buff=1.5)
|
||
|
nl = number_line
|
||
|
|
||
|
m = 7
|
||
|
m_dot = Dot(nl.n2p(m))
|
||
|
m_dot.set_color(MEAN_COLOR)
|
||
|
m_label = Tex("m", color=MEAN_COLOR)
|
||
|
m_label.next_to(m_dot, UP, buff=MED_SMALL_BUFF)
|
||
|
label7 = Tex("7", color=MEAN_COLOR)
|
||
|
|
||
|
# Distance tracking
|
||
|
d_tracker = ValueTracker(4)
|
||
|
|
||
|
def get_l1_point():
|
||
|
return nl.n2p(m - d_tracker.get_value())
|
||
|
|
||
|
def get_l2_point():
|
||
|
return nl.n2p(m + d_tracker.get_value())
|
||
|
|
||
|
l1_dot, l2_dot = (Dot(color=TEAL) for x in range(2))
|
||
|
l1_label = Tex("\\lambda_1", color=LAMBDA1_COLOR)
|
||
|
l2_label = Tex("\\lambda_2", color=LAMBDA2_COLOR)
|
||
|
l1_arrow, l2_arrow = (Arrow(color=WHITE) for x in range(2))
|
||
|
|
||
|
l1_dot.add_updater(lambda m: m.move_to(get_l1_point()))
|
||
|
l2_dot.add_updater(lambda m: m.move_to(get_l2_point()))
|
||
|
always(l1_label.next_to, l1_dot, UP, buff=MED_SMALL_BUFF)
|
||
|
always(l2_label.next_to, l2_dot, UP, buff=MED_SMALL_BUFF)
|
||
|
m_label.match_y(l1_label)
|
||
|
l1_arrow.add_updater(lambda m: m.set_points_by_ends(
|
||
|
m_label.get_left() + SMALL_BUFF * LEFT, l1_label.get_right() + SMALL_BUFF * RIGHT,
|
||
|
))
|
||
|
l2_arrow.add_updater(lambda m: m.set_points_by_ends(
|
||
|
m_label.get_right() + SMALL_BUFF * RIGHT, l2_label.get_left() + SMALL_BUFF * LEFT,
|
||
|
))
|
||
|
|
||
|
minus_d = Tex("-d")
|
||
|
plus_d = Tex("+d")
|
||
|
always(minus_d.next_to, l1_arrow, UP, SMALL_BUFF)
|
||
|
always(plus_d.next_to, l2_arrow, UP, SMALL_BUFF)
|
||
|
plus_qm = Tex("+??")
|
||
|
minus_qm = Tex("-??")
|
||
|
always(plus_qm.move_to, plus_d)
|
||
|
always(minus_qm.move_to, minus_d)
|
||
|
|
||
|
VGroup(plus_d, minus_d).set_opacity(0)
|
||
|
|
||
|
label7.move_to(m_label)
|
||
|
self.add(number_line)
|
||
|
self.add(m_dot)
|
||
|
self.add(label7)
|
||
|
self.add(l1_dot)
|
||
|
self.add(l2_dot)
|
||
|
self.add(l1_label)
|
||
|
self.add(l2_label)
|
||
|
self.add(l1_arrow)
|
||
|
self.add(l2_arrow)
|
||
|
self.add(minus_d)
|
||
|
self.add(plus_d)
|
||
|
self.add(minus_qm)
|
||
|
self.add(plus_qm)
|
||
|
|
||
|
d_tracker.add_updater(lambda m: m.set_value(4 - 2.5 * np.sin(0.25 * self.time)))
|
||
|
self.add(d_tracker)
|
||
|
self.wait(10)
|
||
|
self.play(
|
||
|
UpdateFromAlphaFunc(
|
||
|
Mobject(),
|
||
|
lambda m, a: VGroup(plus_d, minus_d).set_opacity(a),
|
||
|
remover=True
|
||
|
),
|
||
|
UpdateFromAlphaFunc(
|
||
|
Mobject(),
|
||
|
lambda m, a: VGroup(plus_qm, minus_qm).set_opacity(1 - a),
|
||
|
remover=True
|
||
|
),
|
||
|
)
|
||
|
self.remove(plus_qm, minus_qm)
|
||
|
self.wait(5)
|
||
|
|
||
|
# Write the product
|
||
|
kw = {"tex_to_color_map": {"\\,7": MEAN_COLOR, "d": GREY_A, "=": WHITE, "-": WHITE}}
|
||
|
texs = VGroup(*(Tex(tex, **kw) for tex in [
|
||
|
"(\\,7 + d\\,)(\\,7 - d\\,)",
|
||
|
"\\,7^2 - d^2 = ",
|
||
|
"40 =",
|
||
|
"d^2 = \\,7^2 - 40",
|
||
|
"d^2 = 9",
|
||
|
"d = 3",
|
||
|
]))
|
||
|
texs[:3].arrange(LEFT)
|
||
|
texs[1].align_to(texs[2], DOWN)
|
||
|
texs[:3].next_to(nl, DOWN, MED_LARGE_BUFF).to_edge(LEFT, buff=LARGE_BUFF)
|
||
|
for t1, t2 in zip(texs[2:], texs[3:]):
|
||
|
t2.next_to(t1, DOWN, MED_LARGE_BUFF)
|
||
|
t2.shift((t1.get_part_by_tex("=").get_x() - t2.get_part_by_tex("=").get_x()) * RIGHT)
|
||
|
|
||
|
texs[0].save_state()
|
||
|
texs[0].move_to(texs[1], UL)
|
||
|
|
||
|
self.play(Write(VGroup(texs[2], texs[0])))
|
||
|
self.wait(3)
|
||
|
self.play(Restore(texs[0]))
|
||
|
self.play(TransformMatchingShapes(texs[0].copy(), texs[1], path_arc=45 * DEGREES))
|
||
|
self.wait(3)
|
||
|
|
||
|
self.play(LaggedStart(
|
||
|
TransformFromCopy(texs[2][0], texs[3][6], path_arc=-45 * DEGREES),
|
||
|
TransformFromCopy(texs[2][1], texs[3][2]),
|
||
|
TransformFromCopy(texs[1][:3], texs[3][3:6]),
|
||
|
TransformFromCopy(texs[1][3:5], texs[3][0:2], path_arc=45 * DEGREES),
|
||
|
lag_ratio=0.1
|
||
|
))
|
||
|
self.wait(2)
|
||
|
self.play(FadeIn(texs[4], DOWN))
|
||
|
self.wait()
|
||
|
self.play(FadeIn(texs[5], DOWN))
|
||
|
|
||
|
# Show final d geometrically
|
||
|
d_tracker.clear_updaters()
|
||
|
self.play(d_tracker.animate.set_value(3), rate_func=rush_into)
|
||
|
minus_d.clear_updaters()
|
||
|
plus_d.clear_updaters()
|
||
|
|
||
|
plus_3 = Tex("+3").move_to(plus_d)
|
||
|
minus_3 = Tex("-3").move_to(minus_d)
|
||
|
self.play(
|
||
|
LaggedStart(
|
||
|
FadeOut(minus_d, 0.25 * UP),
|
||
|
FadeOut(plus_d, 0.25 * UP),
|
||
|
lag_ratio=0.25
|
||
|
),
|
||
|
LaggedStart(
|
||
|
FadeIn(minus_3, 0.25 * UP),
|
||
|
FadeIn(plus_3, 0.25 * UP),
|
||
|
lag_ratio=0.25
|
||
|
)
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
# Highlight solutions
|
||
|
rects = VGroup(
|
||
|
SurroundingRectangle(VGroup(l1_label, nl.numbers[4])),
|
||
|
SurroundingRectangle(VGroup(l2_label, nl.numbers[10])),
|
||
|
)
|
||
|
self.play(LaggedStartMap(ShowCreation, rects), lag_ratio=0.3)
|
||
|
self.wait(2)
|
||
|
self.play(LaggedStartMap(FadeOut, rects), lag_ratio=0.3)
|
||
|
|
||
|
# Replace with general variables
|
||
|
ms = VGroup(m_label.copy())
|
||
|
sevens = VGroup(label7)
|
||
|
ps = VGroup()
|
||
|
fourties = VGroup()
|
||
|
for tex in texs[:-2]:
|
||
|
for fourty in tex.get_parts_by_tex("40"):
|
||
|
fourties.add(fourty)
|
||
|
ps.add(Tex("p").set_color(PROD_COLOR).move_to(fourty).shift(0.1 * DOWN))
|
||
|
for seven in tex.get_parts_by_tex("7"):
|
||
|
sevens.add(seven)
|
||
|
m = Tex("m").set_color(MEAN_COLOR)
|
||
|
m.scale(0.95)
|
||
|
m.move_to(seven, DR)
|
||
|
m.shift(0.04 * RIGHT)
|
||
|
ms.add(m)
|
||
|
ps[0].shift(0.05 * RIGHT)
|
||
|
ps[1].shift(0.1 * LEFT)
|
||
|
|
||
|
for g1, g2 in ((sevens, ms), (fourties, ps)):
|
||
|
self.play(
|
||
|
LaggedStartMap(FadeOut, g1, shift=0.25 * UP, lag_ratio=0.3),
|
||
|
LaggedStartMap(FadeIn, g2, shift=0.25 * UP, lag_ratio=0.3),
|
||
|
texs[-2:].animate.set_opacity(0)
|
||
|
)
|
||
|
self.remove(texs[-2:])
|
||
|
self.wait()
|
||
|
self.play(FlashUnder(texs[3]))
|
||
|
self.wait()
|
||
|
|
||
|
plus_form, minus_form = [
|
||
|
Tex(
|
||
|
c + "\\sqrt{\\,m^2 - p}",
|
||
|
tex_to_color_map={"m": MEAN_COLOR, "p": PROD_COLOR},
|
||
|
font_size=24,
|
||
|
).move_to(d, DOWN)
|
||
|
for c, d in [("+", plus_d), ("-", minus_d)]
|
||
|
]
|
||
|
pre_group = VGroup(*texs[3][4:6], ms[-1], ps[-1])
|
||
|
self.play(
|
||
|
TransformMatchingShapes(pre_group.copy(), minus_form),
|
||
|
TransformMatchingShapes(pre_group.copy(), plus_form),
|
||
|
FadeOut(VGroup(minus_3, plus_3), shift=0.25 * UP),
|
||
|
)
|
||
|
self.wait()
|
||
|
|
||
|
|
||
|
class JingleAnimation(Scene):
|
||
|
def construct(self):
|
||
|
form = Tex("m \\pm \\sqrt{m^2 - p}")[0]
|
||
|
form.set_height(2)
|
||
|
m, old_pm, sqrt, root, m2, squared, minus, p = form
|
||
|
VGroup(m, m2).set_color(MEAN_COLOR)
|
||
|
VGroup(p).set_color(PROD_COLOR)
|
||
|
m.refresh_bounding_box() # Why?
|
||
|
p.refresh_bounding_box() # Why?
|
||
|
|
||
|
pm = VGroup(Tex("+"), Tex("-"))
|
||
|
pm.arrange(DOWN, buff=0)
|
||
|
pm.replace(old_pm)
|
||
|
pm.save_state()
|
||
|
pm.scale(1.5)
|
||
|
pm.arrange(DOWN, buff=2)
|
||
|
|
||
|
mean = Text("mean", color=WHITE).next_to(m, DOWN)
|
||
|
product = Text("product", color=WHITE).next_to(p, DOWN)
|
||
|
plus_word = Text("plus", color=YELLOW).next_to(pm, UP)
|
||
|
minus_word = Text("minus", color=YELLOW).next_to(pm, DOWN)
|
||
|
|
||
|
def snap(t):
|
||
|
return t**5
|
||
|
|
||
|
self.play(FadeIn(m, scale=0.5, rate_func=snap))
|
||
|
self.add(mean)
|
||
|
self.wait(0.4)
|
||
|
self.add(pm[0], plus_word)
|
||
|
self.wait(0.2)
|
||
|
self.add(pm[1], minus_word)
|
||
|
self.wait(0.2)
|
||
|
self.play(
|
||
|
Restore(pm, rate_func=smooth, run_time=0.5),
|
||
|
FadeOut(VGroup(plus_word, minus_word, mean), run_time=0.5),
|
||
|
)
|
||
|
|
||
|
# new_sqrt = VMobject()
|
||
|
# new_sqrt.set_points(
|
||
|
# sqrt.get_points()[::-3],
|
||
|
# root.get_points()[::-3],
|
||
|
# )
|
||
|
|
||
|
sqrt_outline = sqrt.copy()
|
||
|
sqrt_outline.set_stroke(YELLOW, 5)
|
||
|
sqrt_outline.set_fill(opacity=0)
|
||
|
sqrt_outline.insert_n_curves(100)
|
||
|
root.save_state()
|
||
|
root.stretch(0, 0, about_edge=LEFT)
|
||
|
self.play(
|
||
|
FadeIn(sqrt, rate_func=squish_rate_func(smooth, 0.25, 0.75)),
|
||
|
VShowPassingFlash(sqrt_outline, time_width=2),
|
||
|
Restore(root, rate_func=squish_rate_func(snap, 0.25, 1)),
|
||
|
run_time=1,
|
||
|
)
|
||
|
self.wait(0.5)
|
||
|
|
||
|
self.play(
|
||
|
TransformFromCopy(m, m2, path_arc=-120 * DEGREES, rate_func=smooth, run_time=0.5),
|
||
|
)
|
||
|
# squared.save_state()
|
||
|
# squared.rotate(90 * DEGREES).scale(0.5).set_opacity(0)
|
||
|
# self.play(Restore(squared, run_time=0.25))
|
||
|
self.play(FadeTransform(m2.copy(), squared, run_time=0.25))
|
||
|
minus.save_state()
|
||
|
minus.stretch(0, 0, about_edge=LEFT)
|
||
|
self.play(Restore(minus), run_time=0.7)
|
||
|
self.wait(0.25)
|
||
|
self.play(FadeIn(p, scale=0.8, rate_func=snap, run_time=0.2))
|
||
|
self.add(product)
|
||
|
self.play(Flash(p, flash_radius=0.8 * p.get_height(), run_time=0.5))
|
||
|
self.remove(product)
|
||
|
self.wait()
|