From 2350fbf6a9b5e2d4af9a6b836aff6ed5aae178b1 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 29 Apr 2021 09:35:47 -0700 Subject: [PATCH] Beginning quick_eigen project --- _2021/quick_eigen.py | 909 +++++++++++++++++++++++++++++++++++++++++++ custom_config.yml | 4 +- 2 files changed, 911 insertions(+), 2 deletions(-) create mode 100644 _2021/quick_eigen.py diff --git a/_2021/quick_eigen.py b/_2021/quick_eigen.py new file mode 100644 index 0000000..1a75369 --- /dev/null +++ b/_2021/quick_eigen.py @@ -0,0 +1,909 @@ +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() diff --git a/custom_config.yml b/custom_config.yml index f9921ae..259a15b 100644 --- a/custom_config.yml +++ b/custom_config.yml @@ -17,5 +17,5 @@ style: background_color: "#000000" camera_qualities: default_quality: "ultra_high" -window_position: UR -window_monitor: 1 \ No newline at end of file +window_position: UL +window_monitor: 0 \ No newline at end of file