diff --git a/from_3b1b/active/bayes/footnote.py b/from_3b1b/active/bayes/footnote.py index 82449f2a4c..73c40fbf73 100644 --- a/from_3b1b/active/bayes/footnote.py +++ b/from_3b1b/active/bayes/footnote.py @@ -20,12 +20,13 @@ def get_bayes_formula(): "A": YELLOW, "B": BLUE, }, - substrings_to_isolate=list("P(|)") + substrings_to_isolate=list("P(|)"), ) # Scenes + class ThisIsAFootnote(TeacherStudentsScene): def construct(self): image = ImageMobject("bayes_thumbnail") @@ -57,31 +58,30 @@ def construct(self): # Show main video self.play( FadeInFromDown(image_group), - self.get_student_changes( - "pondering", "hooray", "tease", - look_at_arg=image - ) + self.get_student_changes("pondering", "hooray", "tease", look_at_arg=image), ) self.play( Write(asterisk), - self.teacher.change, "speaking", - ) - self.play( - self.get_student_changes( - "thinking", "erm", "thinking" - ) + self.teacher.change, + "speaking", ) + self.play(self.get_student_changes("thinking", "erm", "thinking")) self.wait(3) self.play( - self.teacher.change, "raise_right_hand", + self.teacher.change, + "raise_right_hand", FadeInFromDown(formula), - self.get_student_changes(*3 * ["pondering"]) + self.get_student_changes(*3 * ["pondering"]), ) self.wait() # Rearrange parts = VGroup( - pb, pab, eq, pa, pba, + pb, + pab, + eq, + pa, + pba, ) parts.generate_target() parts.target.arrange(RIGHT, buff=SMALL_BUFF) @@ -90,7 +90,8 @@ def construct(self): self.play( MoveToTarget(parts, path_arc=-30 * DEGREES), FadeOut(over), - self.teacher.change, "pondering", + self.teacher.change, + "pondering", ) self.wait() @@ -100,9 +101,7 @@ def construct(self): tex_to_color_map={"A": YELLOW, "B": BLUE}, ) eq2 = TexMobject("=") - full_equation = VGroup( - pb, pab, eq, p_both, eq2, pa, pba - ) + full_equation = VGroup(pb, pab, eq, p_both, eq2, pa, pba) full_equation.generate_target() full_equation.target.arrange(RIGHT, buff=SMALL_BUFF) full_equation.target.set_width(FRAME_WIDTH - 1) @@ -118,11 +117,9 @@ def construct(self): MoveToTarget(full_equation), FadeOutAndShift(image_group, 2 * LEFT), FadeOutAndShift(asterisk, 2 * LEFT), - self.teacher.look_at, 4 * UP, - self.get_student_changes( - "thinking", "erm", "confused", - look_at_arg=4 * UP - ) + self.teacher.look_at, + 4 * UP, + self.get_student_changes("thinking", "erm", "confused", look_at_arg=4 * UP), ) self.wait(2) @@ -149,8 +146,10 @@ def construct(self): self.add(formula) self.play( - formula[:3].set_opacity, 0.2, - formula[-3:].set_opacity, 0.2, + formula[:3].set_opacity, + 0.2, + formula[-3:].set_opacity, + 0.2, ) for i in (0, 1): self.play( @@ -174,9 +173,7 @@ def construct(self): ) d1_line.set_stroke(BLACK, 2) - space_words = TextMobject( - "Space of all\\\\possibilities" - ) + space_words = TextMobject("Space of all\\\\possibilities") space_words.match_width(diagram1.square) space_words.scale(0.9) space_words.move_to(diagram1.square) @@ -188,7 +185,8 @@ def construct(self): FadeOut(venn_diagram[0][1]), FadeOut(venn_diagram[1][1]), FadeOut(arrow), - formula[4:6].set_opacity, 1, + formula[4:6].set_opacity, + 1, ) diagram1.pa_label.update() self.play( @@ -237,7 +235,8 @@ def construct(self): Restore(diagram1.he_brace), VFadeIn(diagram1.he_brace), VFadeIn(diagram1.pba_label), - formula.pba.set_opacity, 1, + formula.pba.set_opacity, + 1, ) self.wait() @@ -255,12 +254,16 @@ def construct(self): rect.stretch(0.001, 0, about_edge=LEFT) self.play( - diagram1_copy.move_to, diagram2, - formula.pb.set_opacity, 1, + diagram1_copy.move_to, + diagram2, + formula.pb.set_opacity, + 1, ) self.play( - diagram1_copy.set_likelihood, self.pb, - diagram1_copy.set_antilikelihood, self.pb, + diagram1_copy.set_likelihood, + self.pb, + diagram1_copy.set_antilikelihood, + self.pb, VFadeOut(diagram1_copy), FadeIn(diagram2), TransformFromCopy(formula.pb, diagram2.pb_label), @@ -269,8 +272,10 @@ def construct(self): ) self.wait() self.play( - formula.pab.set_opacity, 1, - formula.eq1.set_opacity, 1, + formula.pab.set_opacity, + 1, + formula.eq1.set_opacity, + 1, ) self.play( TransformFromCopy(formula.pab, diagram2.pab_label), @@ -287,17 +292,21 @@ def get_formula(self): "B": BLUE, } } - parts = VGroup(*[ - TexMobject(tex, **kw) - for tex in [ - "P(B)", "P(A|B)", "=", - "P(A \\text{ and } B)", - "=", "P(A)", "P(B|A)", + parts = VGroup( + *[ + TexMobject(tex, **kw) + for tex in [ + "P(B)", + "P(A|B)", + "=", + "P(A \\text{ and } B)", + "=", + "P(A)", + "P(B|A)", + ] ] - ]) - attrs = [ - "pb", "pab", "eq1", "p_both", "eq2", "pa", "pba" - ] + ) + attrs = ["pb", "pab", "eq1", "p_both", "eq2", "pa", "pba"] for attr, part in zip(attrs, parts): setattr(parts, attr, part) @@ -329,16 +338,12 @@ def get_venn_diagram(self): for title, circle, vect in zip(titles, circles, [UL, UR]): title.match_color(circle) title.scale(2) - title.next_to( - circle.get_boundary_point(vect), - vect, - buff=SMALL_BUFF - ) + title.next_to(circle.get_boundary_point(vect), vect, buff=SMALL_BUFF) return VGroup(circles, titles) def get_diagram1(self): - likelihood = (self.p_both / self.pa) + likelihood = self.p_both / self.pa antilikelihood = (self.pb - self.p_both) / (1 - self.pa) diagram = BayesDiagram(self.pa, likelihood, antilikelihood) diagram.set_height(self.diagram_height) @@ -383,8 +388,10 @@ def get_diagram2(self): result = VGroup( square.set_opacity(0), - b_rect, nb_rect, - ba_rect, nba_rect, + b_rect, + nb_rect, + ba_rect, + nba_rect, ) result.b_rect = b_rect result.nb_rect = nb_rect @@ -406,8 +413,10 @@ def get_diagram2(self): VGroup( result, - pb_brace, pab_brace, - pb_label, pab_label, + pb_brace, + pab_brace, + pb_label, + pab_label, ).to_edge(LEFT) return result @@ -437,16 +446,14 @@ def construct(self): eq1.generate_target() eq1.target.rotate(PI / 3) - eq1.target.move_to(midpoint( - p_both.get_corner(DL), - new_line.target[0].get_corner(UR) - )) + eq1.target.move_to( + midpoint(p_both.get_corner(DL), new_line.target[0].get_corner(UR)) + ) eq2.generate_target() eq2.target.rotate(-PI / 3) - eq2.target.move_to(midpoint( - p_both.get_corner(DR), - new_line.target[4].get_corner(UL) - )) + eq2.target.move_to( + midpoint(p_both.get_corner(DR), new_line.target[4].get_corner(UL)) + ) self.add(formula) self.play( @@ -459,12 +466,16 @@ def construct(self): over.move_to(VGroup(pa, pba)) self.play( ApplyMethod( - pb.next_to, over, DOWN, + pb.next_to, + over, + DOWN, path_arc=30 * DEGREES, ), - VGroup(pa, pba).next_to, over, UP, + VGroup(pa, pba).next_to, + over, + UP, ShowCreation(over), - FadeOut(VGroup(eq1, eq2)) + FadeOut(VGroup(eq1, eq2)), ) self.wait(2) over.generate_target() @@ -473,13 +484,15 @@ def construct(self): numer.generate_target() numer.target.arrange(RIGHT, buff=SMALL_BUFF) numer.target.next_to(over.target, UP) - self.play(LaggedStart( - MoveToTarget(over, path_arc=-30 * DEGREES), - MoveToTarget(numer, path_arc=-30 * DEGREES), - ApplyMethod(pa.next_to, over.target, DOWN), - ApplyMethod(pba.next_to, eq3, RIGHT), - lag_ratio=0.3, - )) + self.play( + LaggedStart( + MoveToTarget(over, path_arc=-30 * DEGREES), + MoveToTarget(numer, path_arc=-30 * DEGREES), + ApplyMethod(pa.next_to, over.target, DOWN), + ApplyMethod(pba.next_to, eq3, RIGHT), + lag_ratio=0.3, + ) + ) self.wait(2) # Numbers @@ -507,52 +520,51 @@ def construct(self): mag.set_fill(GREY, 1) mag.set_sheen(1, UL) - Bs = VGroup(*[ - mob.get_part_by_tex("B") - for mob in [pb, pab, pba] - ]) - As = VGroup(*[ - mob.get_part_by_tex("A") - for mob in [pab, pa, pba] - ]) - books = VGroup(*[ - LibrarianIcon().replace(B, dim_to_match=0) - for B in Bs - ]) + Bs = VGroup(*[mob.get_part_by_tex("B") for mob in [pb, pab, pba]]) + As = VGroup(*[mob.get_part_by_tex("A") for mob in [pab, pa, pba]]) + books = VGroup(*[LibrarianIcon().replace(B, dim_to_match=0) for B in Bs]) books.set_color(YELLOW) - mags = VGroup(*[ - mag.copy().replace(A) - for A in As - ]) - - self.play(LaggedStart(*[ - ReplacementTransform(A, mag, path_arc=PI) - for A, mag in zip(As, mags) - ])) - self.play(LaggedStart(*[ - ReplacementTransform(B, book, path_arc=PI) - for B, book in zip(Bs, books) - ])) + mags = VGroup(*[mag.copy().replace(A) for A in As]) + + self.play( + LaggedStart( + *[ReplacementTransform(A, mag, path_arc=PI) for A, mag in zip(As, mags)] + ) + ) + self.play( + LaggedStart( + *[ + ReplacementTransform(B, book, path_arc=PI) + for B, book in zip(Bs, books) + ] + ) + ) self.wait() class ClassLooking(TeacherStudentsScene): def construct(self): self.play( - self.teacher.change, "pondering", + self.teacher.change, + "pondering", self.get_student_changes( - "pondering", "confused", "sassy", + "pondering", + "confused", + "sassy", look_at_arg=self.screen, ), ) self.wait(5) self.play( - self.teacher.change, "raise_right_hand", + self.teacher.change, + "raise_right_hand", ) self.play( self.get_student_changes( - "thinking", "pondering", "pondering", + "thinking", + "pondering", + "pondering", look_at_arg=self.hold_up_spot + 2 * UP, ) ) @@ -565,7 +577,8 @@ def construct(self): bayes = group[0].copy() self.play( - self.teacher.change, "raise_right_hand", + self.teacher.change, + "raise_right_hand", self.get_student_changes( *3 * ["confused"], look_at_arg=group, @@ -573,36 +586,45 @@ def construct(self): FadeInFromDown(bayes), ) self.remove(bayes) - self.play( - ShowSubmobjectsOneByOne(group, remover=True), - run_time=5 - ) + self.play(ShowSubmobjectsOneByOne(group, remover=True), run_time=5) self.add(bayes) self.wait(2) bubble = self.students[0].get_bubble() self.add(bubble, bayes) self.play( - bayes.move_to, bubble.get_bubble_center(), + bayes.move_to, + bubble.get_bubble_center(), DrawBorderThenFill(bubble), - self.teacher.change, "happy", + self.teacher.change, + "happy", self.get_student_changes( - "pondering", "erm", "erm", + "pondering", + "erm", + "erm", look_at_arg=bubble, - ) + ), ) self.change_all_student_modes( - "thinking", look_at_arg=bayes, + "thinking", + look_at_arg=bayes, ) self.wait() self.play( FadeOut(bayes), - bubble.set_fill, BLACK, 0.2, - bubble.set_stroke, WHITE, 1, + bubble.set_fill, + BLACK, + 0.2, + bubble.set_stroke, + WHITE, + 1, self.get_student_changes( - "pleading", "guilty", "guilty", + "pleading", + "guilty", + "guilty", ), - self.teacher.change, "hesitant" + self.teacher.change, + "hesitant", ) self.wait(2) @@ -610,11 +632,12 @@ def get_formulas(self): group = VGroup( get_bayes_formula(), TexMobject( - "P(X = k) = {\\lambda^k \\over k!}", "e^{-\\lambda}", + "P(X = k) = {\\lambda^k \\over k!}", + "e^{-\\lambda}", tex_to_color_map={ "k": YELLOW, "\\lambda": GREEN, - } + }, ), TexMobject( "{1 \\over \\sigma\\sqrt{2\\pi}}", @@ -622,29 +645,24 @@ def get_formulas(self): tex_to_color_map={ "\\sigma": GREEN, "\\mu": BLUE, - } + }, ), TexMobject( - "P(X = k) =", "\\left({n \\over k}\\right)", "p^k(1-p)^{n-k}", - tex_to_color_map={ - "\\over": BLACK, - "p": WHITE, - "k": YELLOW, - "n": BLUE, - "k": GREEN - } - ), - TexMobject( - "E[X + Y] = E[x] + E[y]" + "P(X = k) =", + "\\left({n \\over k}\\right)", + "p^k(1-p)^{n-k}", + tex_to_color_map={"\\over": BLACK, "p": WHITE, "n": BLUE, "k": GREEN}, ), + TexMobject("E[X + Y] = E[x] + E[y]"), TexMobject( "\\text{Var}(X + Y) = \\text{Var}(x) + \\text{Var}(y) + 2\\text{Cov}(X, Y)" ), TexMobject( - "H = \\sum_{i} -p_i \\log", "(p_i)", + "H = \\sum_{i} -p_i \\log", + "(p_i)", tex_to_color_map={ "p_i": YELLOW, - } + }, ), TexMobject( "{n \\choose k}", @@ -652,7 +670,7 @@ def get_formulas(self): tex_to_color_map={ "\\alpha": BLUE, "\\beta": YELLOW, - } + }, ), TexMobject( "P(d) = \\log_{10}\\left(1 + {1 \\over d}\\right)", @@ -663,7 +681,7 @@ def get_formulas(self): tex_to_color_map={ "{x}": BLUE, "{y}": RED, - } + }, ), ) @@ -709,8 +727,10 @@ def construct(self): FadeIn(venn[1][i]), ) self.play( - and_part.scale, 0.5, - and_part.move_to, venn, + and_part.scale, + 0.5, + and_part.move_to, + venn, ) self.remove(and_part) venn.add(and_part) @@ -746,7 +766,10 @@ def construct(self): pair = pairs[0].target.copy() prob = TexMobject( - "P(", "OO", ")", "= \\frac{1}{4} \\cdot \\frac{1}{4} = \\frac{1}{16}", + "P(", + "OO", + ")", + "= \\frac{1}{4} \\cdot \\frac{1}{4} = \\frac{1}{16}", ) pair.move_to(prob[1]) prob.submobjects[1] = pair @@ -769,8 +792,12 @@ def construct(self): self.add(both_square, pairs) self.play( LaggedStartMap(MoveToTarget, pairs, path_arc=30 * DEGREES), - both_square.set_stroke, YELLOW, 5, - both_square.set_fill, YELLOW, 0.25, + both_square.set_stroke, + YELLOW, + 5, + both_square.set_fill, + YELLOW, + 0.25, ) self.play(FadeIn(prob[3:])) self.wait() @@ -793,14 +820,17 @@ def construct(self): ht_prob = TexMobject( "P(\\text{TT}) = \\frac{1}{2} \\cdot \\frac{1}{2} = \\frac{1}{4}", - tex_to_color_map={"\\text{TT}": RED} + tex_to_color_map={"\\text{TT}": RED}, ) ht_prob.scale(1.5) ht_prob.next_to(ht_grid, RIGHT, LARGE_BUFF) ht_grid_group = VGroup( - ht_grid, ht_labels, left_ht_labels, - ht_both_square, ht_pairs, + ht_grid, + ht_labels, + left_ht_labels, + ht_both_square, + ht_pairs, ) self.play( @@ -829,7 +859,9 @@ def construct(self): dice_both_square.set_fill(YELLOW, 0.25) dice_prob = TexMobject( - "P(", "OO", ") = \\frac{1}{6} \\cdot \\frac{1}{6} = \\frac{1}{36}", + "P(", + "OO", + ") = \\frac{1}{6} \\cdot \\frac{1}{6} = \\frac{1}{36}", ) pair = dice_pairs[0].copy() pair.scale(1.5) @@ -839,8 +871,11 @@ def construct(self): dice_prob.next_to(dice_grid, RIGHT, LARGE_BUFF) dice_grid_group = VGroup( - dice_grid, dice_labels, left_dice_labels, - dice_both_square, dice_pairs, + dice_grid, + dice_labels, + left_dice_labels, + dice_both_square, + dice_pairs, ) self.play( @@ -869,8 +904,7 @@ def construct(self): person.add_updater(lambda m: m.next_to(m.square, UP)) row_rect = SurroundingRectangle( - VGroup(grid[0], left_people[0]), - buff=SMALL_BUFF + VGroup(grid[0], left_people[0]), buff=SMALL_BUFF ) row_rect.set_stroke(RED, 3) @@ -879,14 +913,21 @@ def construct(self): FadeOut(prob), FadeOut(cross), ) - self.play( - ShowCreation(row_rect) - ) + self.play(ShowCreation(row_rect)) self.wait() self.play( - grid[0][0].stretch, 2, 0, {"about_edge": LEFT}, - grid[0][1:].stretch, 2 / 3, 0, {"about_edge": RIGHT}, - both_square.stretch, 2, 0, {"about_edge": LEFT}, + grid[0][0].stretch, + 2, + 0, + {"about_edge": LEFT}, + grid[0][1:].stretch, + 2 / 3, + 0, + {"about_edge": RIGHT}, + both_square.stretch, + 2, + 0, + {"about_edge": LEFT}, *[ ApplyMethod(grid[i][0].stretch, 2 / 3, 0, {"about_edge": LEFT}) for i in range(1, 4) @@ -928,8 +969,10 @@ def construct(self): self.wait() self.play( TransformFromCopy(formula, real_formula), - grid_group.scale, 0.7, - grid_group.to_corner, DL, + grid_group.scale, + 0.7, + grid_group.to_corner, + DL, ) self.play( FadeIn(real_rect), @@ -978,12 +1021,12 @@ def construct(self): self.wait() def get_grid(self, n, m, height=4): - grid = VGroup(*[ - VGroup( - *[Square() for x in range(m)] - ).arrange(RIGHT, buff=0) - for y in range(n) - ]).arrange(DOWN, buff=0) + grid = VGroup( + *[ + VGroup(*[Square() for x in range(m)]).arrange(RIGHT, buff=0) + for y in range(n) + ] + ).arrange(DOWN, buff=0) grid.set_height(height) grid.set_stroke(WHITE, 2) return grid @@ -1017,7 +1060,7 @@ def construct(self): "D": YELLOW, "+": BLUE, }, - substrings_to_isolate=list("P(|)=") + substrings_to_isolate=list("P(|)="), ) formula.scale(2.5) @@ -1054,8 +1097,10 @@ def construct(self): positive_arrow = Arrow(lhs[4].get_bottom(), positive_words.get_top()) arrow_groups = VGroup( - sick_words, sick_arrow, - positive_words, positive_arrow, + sick_words, + sick_arrow, + positive_words, + positive_arrow, ) sicky.save_state() @@ -1095,7 +1140,9 @@ def get_formula_slice(*indices): likelihood = formula[12:18] run_time = 1 self.play( - lhs_copy.next_to, likelihood, UP, + lhs_copy.next_to, + likelihood, + UP, run_time=run_time, ) self.play( @@ -1103,7 +1150,8 @@ def get_formula_slice(*indices): run_time=run_time, ) self.play( - lhs_copy.move_to, likelihood, + lhs_copy.move_to, + likelihood, run_time=run_time, ) @@ -1119,11 +1167,7 @@ def get_formula_slice(*indices): class EndScreen(Scene): - CONFIG = { - "camera_config": { - "background_color": DARKER_GREY - } - } + CONFIG = {"camera_config": {"background_color": DARKER_GREY}} def construct(self): width = (475 / 1280) * FRAME_WIDTH @@ -1137,8 +1181,8 @@ def construct(self): video_rect.shift(UP) date = TextMobject( - "Solution will be\\\\" - "posted", "1/20/19", + "Solution will be\\\\" "posted", + "1/20/19", ) date[1].set_color(YELLOW) date.set_width(video_rect.get_width() - 2 * MED_SMALL_BUFF) diff --git a/from_3b1b/old/borsuk.py b/from_3b1b/old/borsuk.py index 46d20fc95f..63d0808d16 100644 --- a/from_3b1b/old/borsuk.py +++ b/from_3b1b/old/borsuk.py @@ -1,15 +1,17 @@ from manimlib.imports import * from functools import reduce + class Jewel(VMobject): CONFIG = { - "color" : WHITE, - "fill_opacity" : 0.75, - "stroke_width" : 0, - "height" : 0.5, - "num_equator_points" : 5, - "sun_vect" : OUT+LEFT+UP, + "color": WHITE, + "fill_opacity": 0.75, + "stroke_width": 0, + "height": 0.5, + "num_equator_points": 5, + "sun_vect": OUT + LEFT + UP, } + def generate_points(self): for vect in OUT, IN: compass_vects = list(compass_directions(self.num_equator_points)) @@ -18,20 +20,20 @@ def generate_points(self): for vect_pair in adjacent_pairs(compass_vects): self.add(Polygon(vect, *vect_pair)) self.set_height(self.height) - self.rotate(-np.pi/2-np.pi/24, RIGHT) - self.rotate(-np.pi/12, UP) - self.submobjects.sort( - key=lambda m: -m1.get_center()[2] - ) + self.rotate(-np.pi / 2 - np.pi / 24, RIGHT) + self.rotate(-np.pi / 12, UP) + self.submobjects.sort(key=lambda m: -m1.get_center()[2]) return self + class Necklace(VMobject): CONFIG = { - "width" : FRAME_WIDTH - 1, - "jewel_buff" : MED_SMALL_BUFF, - "chain_color" : GREY, - "default_colors" : [(4, BLUE), (6, WHITE), (4, GREEN)] + "width": FRAME_WIDTH - 1, + "jewel_buff": MED_SMALL_BUFF, + "chain_color": GREY, + "default_colors": [(4, BLUE), (6, WHITE), (4, GREEN)], } + def __init__(self, *colors, **kwargs): digest_config(self, kwargs, locals()) if len(colors) == 0: @@ -39,38 +41,34 @@ def __init__(self, *colors, **kwargs): VMobject.__init__(self, **kwargs) def get_default_colors(self): - result = list(it.chain(*[ - num*[color] - for num, color in self.default_colors - ])) + result = list(it.chain(*[num * [color] for num, color in self.default_colors])) random.shuffle(result) return result def generate_points(self): - jewels = VGroup(*[ - Jewel(color = color) - for color in self.colors - ]) - jewels.arrange(buff = self.jewel_buff) + jewels = VGroup(*[Jewel(color=color) for color in self.colors]) + jewels.arrange(buff=self.jewel_buff) jewels.set_width(self.width) jewels.center() - j_to_j_dist = (jewels[1].get_center()-jewels[0].get_center())[0] + j_to_j_dist = (jewels[1].get_center() - jewels[0].get_center())[0] chain = Line( - jewels[0].get_center() + j_to_j_dist*LEFT/2, - jewels[-1].get_center() + j_to_j_dist*RIGHT/2, - color = self.chain_color, + jewels[0].get_center() + j_to_j_dist * LEFT / 2, + jewels[-1].get_center() + j_to_j_dist * RIGHT / 2, + color=self.chain_color, ) self.add(chain, *jewels) self.chain = chain self.jewels = jewels + ################ + class FromPreviousTopologyVideo(Scene): def construct(self): - rect = Rectangle(height = 9, width = 16) - rect.set_height(FRAME_HEIGHT-2) + rect = Rectangle(height=9, width=16) + rect.set_height(FRAME_HEIGHT - 2) title = TextMobject("From original ``Who cares about topology'' video") title.to_edge(UP) rect.next_to(title, DOWN) @@ -79,32 +77,32 @@ def construct(self): self.play(ShowCreation(rect)) self.wait() + class CheckOutMathologer(PiCreatureScene): CONFIG = { - "logo_height" : 1.5, - "screen_height" : 5, - "channel_name" : "Mathologer", - "logo_file" : "mathologer_logo", - "logo_color" : None, + "logo_height": 1.5, + "screen_height": 5, + "channel_name": "Mathologer", + "logo_file": "mathologer_logo", + "logo_color": None, } + def construct(self): logo = self.get_logo() name = TextMobject(self.channel_name) name.next_to(logo, RIGHT) - rect = Rectangle(height = 9, width = 16) + rect = Rectangle(height=9, width=16) rect.set_height(self.screen_height) rect.next_to(logo, DOWN) rect.to_edge(LEFT) self.play( self.get_logo_intro_animation(logo), - self.pi_creature.change_mode, "hooray", - ) - self.play( - ShowCreation(rect), - Write(name) + self.pi_creature.change_mode, + "hooray", ) + self.play(ShowCreation(rect), Write(name)) self.wait(2) self.change_mode("happy") self.wait(2) @@ -112,7 +110,7 @@ def construct(self): def get_logo(self): logo = ImageMobject(self.logo_file) logo.set_height(self.logo_height) - logo.to_corner(UP+LEFT) + logo.to_corner(UP + LEFT) if self.logo_color is not None: logo.set_color(self.logo_color) logo.stroke_width = 1 @@ -124,15 +122,17 @@ def get_logo_intro_animation(self, logo): logo.set_color(BLACK) return ApplyMethod(logo.restore) + class IntroduceStolenNecklaceProblem(ThreeDScene): CONFIG = { - "jewel_colors" : [BLUE, GREEN, WHITE, RED], - "num_per_jewel" : [8, 10, 4, 6], - "num_shuffles" : 1, - "necklace_center" : UP, - "random_seed" : 2, - "forced_binary_choices" : (0, 1, 0, 1, 0), + "jewel_colors": [BLUE, GREEN, WHITE, RED], + "num_per_jewel": [8, 10, 4, 6], + "num_shuffles": 1, + "necklace_center": UP, + "random_seed": 2, + "forced_binary_choices": (0, 1, 0, 1, 0), } + def construct(self): random.seed(self.random_seed) self.add_thieves() @@ -144,11 +144,8 @@ def construct(self): self.divvy_with_n_cuts() def add_thieves(self): - thieves = VGroup( - Randolph(), - Mortimer() - ) - thieves.arrange(RIGHT, buff = 4*LARGE_BUFF) + thieves = VGroup(Randolph(), Mortimer()) + thieves.arrange(RIGHT, buff=4 * LARGE_BUFF) thieves.to_edge(DOWN) thieves[0].make_eye_contact(thieves[1]) @@ -159,11 +156,7 @@ def write_title(self): title = TextMobject("Stolen necklace problem") title.to_edge(UP) self.play( - Write(title), - *[ - ApplyMethod(pi.look_at, title) - for pi in self.thieves - ] + Write(title), *[ApplyMethod(pi.look_at, title) for pi in self.thieves] ) self.title = title @@ -179,50 +172,41 @@ def introduce_necklace(self): jewel_copy.next_to(num_mob) label = VGroup(num_mob, jewel_copy) enumeration_labels.add(label) - enumeration_labels.arrange(RIGHT, buff = LARGE_BUFF) + enumeration_labels.arrange(RIGHT, buff=LARGE_BUFF) enumeration_labels.to_edge(UP) self.play( - FadeIn( - necklace, - lag_ratio = 0.5, - run_time = 3 - ), - *it.chain(*[ - [pi.change_mode, "conniving", pi.look_at, necklace] - for pi in self.thieves - ]) - ) - self.play(*[ - ApplyMethod( - jewel.rotate_in_place, np.pi/6, UP, - rate_func = there_and_back + FadeIn(necklace, lag_ratio=0.5, run_time=3), + *it.chain( + *[ + [pi.change_mode, "conniving", pi.look_at, necklace] + for pi in self.thieves + ] ) - for jewel in jewels - ]) + ) + self.play( + *[ + ApplyMethod( + jewel.rotate_in_place, np.pi / 6, UP, rate_func=there_and_back + ) + for jewel in jewels + ] + ) self.play(Blink(self.thieves[0])) for jewel_type in jewel_types: - self.play( - jewel_type.shift, 0.2*UP, - rate_func = wiggle - ) + self.play(jewel_type.shift, 0.2 * UP, rate_func=wiggle) self.wait() for x in range(self.num_shuffles): self.shuffle_jewels(jewels) self.play(FadeOut(self.title)) for jewel_type, label in zip(jewel_types, enumeration_labels): - jewel_type.submobjects.sort( - key=lambda m: m1.get - ) + jewel_type.submobjects.sort(key=lambda m: m1.get) jewel_type.save_state() jewel_type.generate_target() jewel_type.target.arrange() jewel_type.target.scale(2) - jewel_type.target.move_to(2*UP) - self.play( - MoveToTarget(jewel_type), - Write(label) - ) + jewel_type.target.move_to(2 * UP) + self.play(MoveToTarget(jewel_type), Write(label)) self.play(jewel_type.restore) self.play(Blink(self.thieves[1])) @@ -241,26 +225,16 @@ def divvy_by_cutting_all(self): for label in enumeration_labels: tex, jewel = label num = int(tex.get_tex_string()) - half_label = VGroup( - TexMobject(str(num/2)), - jewel.copy() - ) + half_label = VGroup(TexMobject(str(num / 2)), jewel.copy()) half_label.arrange() half_labels.add(half_label) half_labels.arrange(DOWN) half_labels.set_height(thief.get_height()) - half_labels.next_to( - thief, vect, - buff = MED_LARGE_BUFF, - aligned_edge = DOWN - ) + half_labels.next_to(thief, vect, buff=MED_LARGE_BUFF, aligned_edge=DOWN) both_half_labels.add(half_labels) for half_labels in both_half_labels: - self.play(ReplacementTransform( - enumeration_labels.copy(), - half_labels - )) + self.play(ReplacementTransform(enumeration_labels.copy(), half_labels)) self.play(*[ApplyMethod(pi.change_mode, "pondering") for pi in thieves]) self.wait() @@ -269,64 +243,68 @@ def divvy_by_cutting_all(self): jewel_type_copy = jewel_type.copy() n_jewels = len(jewel_type) halves = [ - VGroup(*jewel_type_copy[:n_jewels/2]), - VGroup(*jewel_type_copy[n_jewels/2:]), + VGroup(*jewel_type_copy[: n_jewels / 2]), + VGroup(*jewel_type_copy[n_jewels / 2 :]), ] for half, thief, vect in zip(halves, thieves, [RIGHT, LEFT]): half.arrange(DOWN) half.next_to( - thief, vect, - buff = SMALL_BUFF + type_index*half.get_width(), - aligned_edge = DOWN + thief, + vect, + buff=SMALL_BUFF + type_index * half.get_width(), + aligned_edge=DOWN, ) self.play( Transform(jewel_type, jewel_type_copy), + *[ApplyMethod(thief.look_at, jewel_type_copy) for thief in thieves] + ) + self.play( + *it.chain( + *[ + [thief.change_mode, "happy", thief.look_at, necklace] + for thief in thieves + ] + ) + ) + self.wait() + self.play(*[jewel_type.restore for jewel_type in jewel_types]) + self.play( + *it.chain( *[ - ApplyMethod(thief.look_at, jewel_type_copy) + [thief.change_mode, "confused", thief.look_at, necklace] for thief in thieves ] ) - self.play(*it.chain(*[ - [thief.change_mode, "happy", thief.look_at, necklace] - for thief in thieves - ])) - self.wait() - self.play(*[ - jewel_type.restore - for jewel_type in jewel_types - ]) - self.play(*it.chain(*[ - [thief.change_mode, "confused", thief.look_at, necklace] - for thief in thieves - ])) + ) def divvy_with_n_cuts( - self, - with_thieves = True, - highlight_groups = True, - show_matching_after_divvying = True, - ): + self, + with_thieves=True, + highlight_groups=True, + show_matching_after_divvying=True, + ): necklace = self.necklace jewel_types = self.jewel_types jewels = sorted( - necklace.jewels, - lambda m1, m2 : cmp(m1.get_center()[0], m2.get_center()[0]) + necklace.jewels, lambda m1, m2: cmp(m1.get_center()[0], m2.get_center()[0]) ) slice_indices, binary_choices = self.find_slice_indices(jewels, jewel_types) subgroups = [ - VGroup(*jewels[i1:i2]) - for i1, i2 in zip(slice_indices, slice_indices[1:]) + VGroup(*jewels[i1:i2]) for i1, i2 in zip(slice_indices, slice_indices[1:]) ] - buff = (jewels[1].get_left()[0]-jewels[0].get_right()[0])/2 - v_lines = VGroup(*[ - DashedLine(UP, DOWN).next_to(group, RIGHT, buff = buff) - for group in subgroups[:-1] - ]) + buff = (jewels[1].get_left()[0] - jewels[0].get_right()[0]) / 2 + v_lines = VGroup( + *[ + DashedLine(UP, DOWN).next_to(group, RIGHT, buff=buff) + for group in subgroups[:-1] + ] + ) strand_groups = [VGroup(), VGroup()] for group, choice in zip(subgroups, binary_choices): strand = Line( - group[0].get_center(), group[-1].get_center(), - color = necklace.chain.get_color() + group[0].get_center(), + group[-1].get_center(), + color=necklace.chain.get_color(), ) strand.add(*group) strand_groups[choice].add(strand) @@ -335,22 +313,25 @@ def divvy_with_n_cuts( self.play(ShowCreation(v_lines)) self.play( FadeOut(necklace.chain), - *it.chain(*[ - list(map(Animation, group)) - for group in strand_groups - ]) + *it.chain(*[list(map(Animation, group)) for group in strand_groups]) ) for group in strand_groups: group.save_state() self.play( - strand_groups[0].shift, UP/2., - strand_groups[1].shift, DOWN/2., + strand_groups[0].shift, + UP / 2.0, + strand_groups[1].shift, + DOWN / 2.0, ) if with_thieves: - self.play(*it.chain(*[ - [thief.change_mode, "happy", thief.look_at, self.necklace] - for thief in self.thieves - ])) + self.play( + *it.chain( + *[ + [thief.change_mode, "happy", thief.look_at, self.necklace] + for thief in self.thieves + ] + ) + ) self.play(Blink(self.thieves[1])) else: self.wait() @@ -358,11 +339,11 @@ def divvy_with_n_cuts( if highlight_groups: for group in strand_groups: box = Rectangle( - width = group.get_width()+2*SMALL_BUFF, - height = group.get_height()+2*SMALL_BUFF, - stroke_width = 0, - fill_color = YELLOW, - fill_opacity = 0.3, + width=group.get_width() + 2 * SMALL_BUFF, + height=group.get_height() + 2 * SMALL_BUFF, + stroke_width=0, + fill_color=YELLOW, + fill_opacity=0.3, ) box.move_to(group) self.play(FadeIn(box)) @@ -373,20 +354,15 @@ def divvy_with_n_cuts( if show_matching_after_divvying: for jewel_type in jewel_types: self.play( - *[ - ApplyMethod(jewel.scale_in_place, 1.5) - for jewel in jewel_type - ], - rate_func = there_and_back, - run_time = 2 + *[ApplyMethod(jewel.scale_in_place, 1.5) for jewel in jewel_type], + rate_func=there_and_back, + run_time=2 ) self.wait() self.play( FadeOut(v_lines), FadeIn(necklace.chain), - *[ - group.restore for group in strand_groups - ] + *[group.restore for group in strand_groups] ) self.remove(*strand_groups) self.add(necklace) @@ -394,10 +370,13 @@ def divvy_with_n_cuts( ######## def get_necklace(self, **kwargs): - colors = reduce(op.add, [ - num*[color] - for num, color in zip(self.num_per_jewel, self.jewel_colors) - ]) + colors = reduce( + op.add, + [ + num * [color] + for num, color in zip(self.num_per_jewel, self.jewel_colors) + ], + ) self.necklace = Necklace(*colors, **kwargs) self.necklace.shift(self.necklace_center) return self.necklace @@ -408,27 +387,28 @@ def get_jewels_organized_by_type(self, jewels): for color in map(Color, self.jewel_colors) ] - def shuffle_jewels(self, jewels, run_time = 2, path_arc = np.pi/2, **kwargs): + def shuffle_jewels(self, jewels, run_time=2, path_arc=np.pi / 2, **kwargs): shuffled_indices = list(range(len(jewels))) random.shuffle(shuffled_indices) - target_group = VGroup(*[ - jewel.copy().move_to(jewels[shuffled_indices[i]]) - for i, jewel in enumerate(jewels) - ]) - self.play(Transform( - jewels, target_group, - run_time = run_time, - path_arc = path_arc, - **kwargs - )) + target_group = VGroup( + *[ + jewel.copy().move_to(jewels[shuffled_indices[i]]) + for i, jewel in enumerate(jewels) + ] + ) + self.play( + Transform( + jewels, target_group, run_time=run_time, path_arc=path_arc, **kwargs + ) + ) def find_slice_indices(self, jewels, jewel_types): - def jewel_to_type_number(jewel): for i, jewel_type in enumerate(jewel_types): if jewel in jewel_type: return i raise Exception("Not in any jewel_types") + type_numbers = list(map(jewel_to_type_number, jewels)) n_types = len(jewel_types) @@ -437,7 +417,7 @@ def jewel_to_type_number(jewel): if self.forced_binary_choices is not None: all_binary_choices = [self.forced_binary_choices] else: - all_binary_choices = it.product(*[list(range(2))]*(n_types+1)) + all_binary_choices = it.product(*[list(range(2))] * (n_types + 1)) for binary_choices in all_binary_choices: subsets = [ type_numbers[i1:i2] @@ -454,7 +434,6 @@ def jewel_to_type_number(jewel): flat_left_set = np.array(list(it.chain(*left_sets))) flat_right_set = np.array(list(it.chain(*right_sets))) - match_array = [ sum(flat_left_set == n) == sum(flat_right_set == n) for n in range(n_types) @@ -463,40 +442,45 @@ def jewel_to_type_number(jewel): return slice_indices, binary_choices raise Exception("No fair division found") + class ThingToProve(PiCreatureScene): def construct(self): arrow = Arrow(UP, DOWN) top_words = TextMobject("$n$ jewel types") top_words.next_to(arrow, UP) - bottom_words = TextMobject(""" + bottom_words = TextMobject( + """ Fair division possible with $n$ cuts - """) + """ + ) bottom_words.next_to(arrow, DOWN) self.play( - Write(top_words, run_time = 2), - self.pi_creature.change_mode, "raise_right_hand" + Write(top_words, run_time=2), + self.pi_creature.change_mode, + "raise_right_hand", ) self.play(ShowCreation(arrow)) self.play( - Write(bottom_words, run_time = 2), - self.pi_creature.change_mode, "pondering" + Write(bottom_words, run_time=2), self.pi_creature.change_mode, "pondering" ) self.wait(3) + class FiveJewelCase(IntroduceStolenNecklaceProblem): CONFIG = { - "jewel_colors" : [BLUE, GREEN, WHITE, RED, YELLOW], - "num_per_jewel" : [6, 4, 4, 2, 8], - "forced_binary_choices" : (0, 1, 0, 1, 0, 1), + "jewel_colors": [BLUE, GREEN, WHITE, RED, YELLOW], + "num_per_jewel": [6, 4, 4, 2, 8], + "forced_binary_choices": (0, 1, 0, 1, 0, 1), } + def construct(self): random.seed(self.random_seed) self.add(self.get_necklace()) jewels = self.necklace.jewels - self.shuffle_jewels(jewels, run_time = 0) - self.jewel_types = self.get_jewels_organized_by_type(jewels) + self.shuffle_jewels(jewels, run_time=0) + self.jewel_types = self.get_jewels_organized_by_type(jewels) self.add_title() self.add_thieves() for thief in self.thieves: @@ -506,46 +490,51 @@ def construct(self): def add_title(self): n_cuts = len(self.jewel_colors) - title = TextMobject( - "%d jewel types, %d cuts"%(n_cuts, n_cuts) - ) + title = TextMobject("%d jewel types, %d cuts" % (n_cuts, n_cuts)) title.to_edge(UP) self.add(title) + class SixJewelCase(FiveJewelCase): CONFIG = { - "jewel_colors" : [BLUE, GREEN, WHITE, RED, YELLOW, MAROON_B], - "num_per_jewel" : [6, 4, 4, 2, 2, 6], - "forced_binary_choices" : (0, 1, 0, 1, 0, 1, 0), + "jewel_colors": [BLUE, GREEN, WHITE, RED, YELLOW, MAROON_B], + "num_per_jewel": [6, 4, 4, 2, 2, 6], + "forced_binary_choices": (0, 1, 0, 1, 0, 1, 0), } + class DiscussApplicability(TeacherStudentsScene): def construct(self): - self.teacher_says(""" + self.teacher_says( + """ Minimize sharding, allocate resources evenly - """) - self.change_student_modes(*["pondering"]*3) + """ + ) + self.change_student_modes(*["pondering"] * 3) self.wait(2) + class ThreeJewelCase(FiveJewelCase): CONFIG = { - "jewel_colors" : [BLUE, GREEN, WHITE], - "num_per_jewel" : [6, 4, 8], - "forced_binary_choices" : (0, 1, 0, 1), + "jewel_colors": [BLUE, GREEN, WHITE], + "num_per_jewel": [6, 4, 8], + "forced_binary_choices": (0, 1, 0, 1), } + class RepeatedShuffling(IntroduceStolenNecklaceProblem): CONFIG = { - "num_shuffles" : 5, - "random_seed" : 3, - "show_matching_after_divvying" : False, + "num_shuffles": 5, + "random_seed": 3, + "show_matching_after_divvying": False, } + def construct(self): random.seed(self.random_seed) self.add(self.get_necklace()) jewels = self.necklace.jewels - self.jewel_types = self.get_jewels_organized_by_type(jewels) + self.jewel_types = self.get_jewels_organized_by_type(jewels) self.add_thieves() for thief in self.thieves: ApplyMethod(thief.change_mode, "pondering").update(1) @@ -553,99 +542,118 @@ def construct(self): for x in range(self.num_shuffles): self.shuffle_jewels(jewels) - self.divvy_with_n_cuts( - show_matching_after_divvying = False - ) + self.divvy_with_n_cuts(show_matching_after_divvying=False) + class NowForTheTopology(TeacherStudentsScene): def construct(self): self.teacher_says("Now for the \\\\ topology") - self.change_student_modes(*["hooray"]*3) + self.change_student_modes(*["hooray"] * 3) self.wait(3) + class ExternallyAnimatedScene(Scene): def construct(self): raise Exception("Don't actually run this class.") + class SphereOntoPlaneIn3D(ExternallyAnimatedScene): pass + class DiscontinuousSphereOntoPlaneIn3D(ExternallyAnimatedScene): pass + class WriteWords(Scene): CONFIG = { - "words" : "", - "color" : WHITE, + "words": "", + "color": WHITE, } + def construct(self): words = TextMobject(self.words) words.set_color(self.color) - words.set_width(FRAME_WIDTH-1) + words.set_width(FRAME_WIDTH - 1) words.to_edge(DOWN) self.play(Write(words)) self.wait(2) + class WriteNotAllowed(WriteWords): CONFIG = { - "words" : "Not allowed", - "color" : RED, + "words": "Not allowed", + "color": RED, } + class NonAntipodalCollisionIn3D(ExternallyAnimatedScene): pass + class AntipodalCollisionIn3D(ExternallyAnimatedScene): pass + class WriteBorsukUlam(WriteWords): CONFIG = { - "words" : "Borsuk-Ulam Theorem", + "words": "Borsuk-Ulam Theorem", } + class WriteAntipodal(WriteWords): CONFIG = { - "words" : "``Antipodal''", - "color" : MAROON_B, + "words": "``Antipodal''", + "color": MAROON_B, } + class ProjectOntoEquatorIn3D(ExternallyAnimatedScene): pass + class ProjectOntoEquatorWithPolesIn3D(ExternallyAnimatedScene): pass + class ProjectAntipodalNonCollisionIn3D(ExternallyAnimatedScene): pass + class ShearThenProjectnOntoEquatorPolesMissIn3D(ExternallyAnimatedScene): pass + class ShearThenProjectnOntoEquatorAntipodalCollisionIn3D(ExternallyAnimatedScene): pass + class ClassicExample(TeacherStudentsScene): def construct(self): self.teacher_says("The classic example...") - self.change_student_modes(*["happy"]*3) + self.change_student_modes(*["happy"] * 3) self.wait(2) + class AntipodalEarthPoints(ExternallyAnimatedScene): pass + class RotatingEarth(ExternallyAnimatedScene): pass + class TemperaturePressurePlane(GraphScene): CONFIG = { - "x_labeled_nums" : [], - "y_labeled_nums" : [], - "x_axis_label" : "Temperature", - "y_axis_label" : "Pressure", - "graph_origin" : 2.5*DOWN + 2*LEFT, - "corner_square_width" : 4, - "example_point_coords" : (2, 5), + "x_labeled_nums": [], + "y_labeled_nums": [], + "x_axis_label": "Temperature", + "y_axis_label": "Pressure", + "graph_origin": 2.5 * DOWN + 2 * LEFT, + "corner_square_width": 4, + "example_point_coords": (2, 5), } + def construct(self): self.setup_axes() self.draw_arrow() @@ -654,15 +662,14 @@ def construct(self): def draw_arrow(self): square = Square( - side_length = self.corner_square_width, - stroke_color = WHITE, - stroke_width = 0, + side_length=self.corner_square_width, + stroke_color=WHITE, + stroke_width=0, ) - square.to_corner(UP+LEFT, buff = 0) + square.to_corner(UP + LEFT, buff=0) arrow = Arrow( - square.get_right(), - self.coords_to_point(*self.example_point_coords) + square.get_right(), self.coords_to_point(*self.example_point_coords) ) self.play(ShowCreation(arrow)) @@ -671,7 +678,7 @@ def add_example_coordinates(self): dot = Dot(self.coords_to_point(*self.example_point_coords)) dot.set_color(YELLOW) tex = TexMobject("(25^\\circ\\text{C}, 101 \\text{ kPa})") - tex.next_to(dot, UP+RIGHT, buff = SMALL_BUFF) + tex.next_to(dot, UP + RIGHT, buff=SMALL_BUFF) self.play(ShowCreation(dot)) self.play(Write(tex)) @@ -679,25 +686,35 @@ def add_example_coordinates(self): self.play(FadeOut(tex)) def wander_continuously(self): - path = VMobject().set_points_smoothly([ - ORIGIN, 2*UP+RIGHT, 2*DOWN+RIGHT, - 5*RIGHT, 4*RIGHT+UP, 3*RIGHT+2*DOWN, - DOWN+LEFT, 2*RIGHT - ]) + path = VMobject().set_points_smoothly( + [ + ORIGIN, + 2 * UP + RIGHT, + 2 * DOWN + RIGHT, + 5 * RIGHT, + 4 * RIGHT + UP, + 3 * RIGHT + 2 * DOWN, + DOWN + LEFT, + 2 * RIGHT, + ] + ) point = self.coords_to_point(*self.example_point_coords) path.shift(point) path.set_color(GREEN) - self.play(ShowCreation(path, run_time = 10, rate_func=linear)) + self.play(ShowCreation(path, run_time=10, rate_func=linear)) self.wait() + class AlternateSphereSquishing(ExternallyAnimatedScene): pass + class AlternateAntipodalCollision(ExternallyAnimatedScene): pass + class AskWhy(TeacherStudentsScene): def construct(self): self.student_says("But...why?") @@ -705,18 +722,20 @@ def construct(self): self.play(self.get_teacher().change_mode, "happy") self.wait(3) + class PointOutVSauce(CheckOutMathologer): CONFIG = { - "channel_name" : "", - "logo_file" : "Vsauce_logo", - "logo_height" : 1, - "logo_color" : GREY, + "channel_name": "", + "logo_file": "Vsauce_logo", + "logo_height": 1, + "logo_color": GREY, } + def get_logo(self): - logo = SVGMobject(file_name = self.logo_file) + logo = SVGMobject(file_name=self.logo_file) logo.set_height(self.logo_height) - logo.to_corner(UP+LEFT) - logo.set_stroke(width = 0) + logo.to_corner(UP + LEFT) + logo.set_stroke(width=0) logo.set_fill(GREEN) logo.sort() return logo @@ -724,18 +743,20 @@ def get_logo(self): def get_logo_intro_animation(self, logo): return DrawBorderThenFill( logo, - run_time = 2, + run_time=2, ) + class WalkEquatorPostTransform(GraphScene): CONFIG = { - "x_labeled_nums" : [], - "y_labeled_nums" : [], - "graph_origin" : 2.5*DOWN + 2*LEFT, - "curved_arrow_color" : WHITE, - "curved_arrow_radius" : 3, - "num_great_arcs" : 10, + "x_labeled_nums": [], + "y_labeled_nums": [], + "graph_origin": 2.5 * DOWN + 2 * LEFT, + "curved_arrow_color": WHITE, + "curved_arrow_radius": 3, + "num_great_arcs": 10, } + def construct(self): self.setup_axes() self.add_curved_arrow() @@ -748,9 +769,10 @@ def construct(self): def add_curved_arrow(self): arc = Arc( - start_angle = 2*np.pi/3, angle = -np.pi/2, - radius = self.curved_arrow_radius, - color = self.curved_arrow_color + start_angle=2 * np.pi / 3, + angle=-np.pi / 2, + radius=self.curved_arrow_radius, + color=self.curved_arrow_color, ) arc.add_tip() arc.move_to(self.coords_to_point(0, 7)) @@ -764,31 +786,29 @@ def walk_equator(self): dot_movement = self.get_arc_walk_dot_movement(equator, dots) dot_movement.update(0) - self.play(ShowCreation(equator, run_time = 3)) + self.play(ShowCreation(equator, run_time=3)) self.play(FadeIn(dots[0])) - dots[1].set_fill(opacity = 0) + dots[1].set_fill(opacity=0) self.play(dot_movement) self.play(dots[1].set_fill, None, 1) self.play(dot_movement) self.play(dot_movement) proportion = equator.collision_point_proportion - self.play(self.get_arc_walk_dot_movement( - equator, dots, - rate_func = lambda t : 2*proportion*smooth(t) - )) - v_line = DashedLine(FRAME_Y_RADIUS*UP, FRAME_Y_RADIUS*DOWN) - v_line.shift(dots.get_center()[0]*RIGHT) + self.play( + self.get_arc_walk_dot_movement( + equator, dots, rate_func=lambda t: 2 * proportion * smooth(t) + ) + ) + v_line = DashedLine(FRAME_Y_RADIUS * UP, FRAME_Y_RADIUS * DOWN) + v_line.shift(dots.get_center()[0] * RIGHT) self.play(ShowCreation(v_line)) self.wait() self.play(FadeOut(v_line)) dots.save_state() equator.save_state() - self.play( - equator.fade, - dots.fade - ) + self.play(equator.fade, dots.fade) self.first_dots = dots @@ -807,12 +827,13 @@ def walk_tilted_equator(self): self.play(dot_movement) proportion = tilted_eq.collision_point_proportion - self.play(self.get_arc_walk_dot_movement( - tilted_eq, dots, - rate_func = lambda t : 2*proportion*smooth(t) - )) - v_line = DashedLine(FRAME_Y_RADIUS*UP, FRAME_Y_RADIUS*DOWN) - v_line.shift(dots.get_center()[0]*RIGHT) + self.play( + self.get_arc_walk_dot_movement( + tilted_eq, dots, rate_func=lambda t: 2 * proportion * smooth(t) + ) + ) + v_line = DashedLine(FRAME_Y_RADIUS * UP, FRAME_Y_RADIUS * DOWN) + v_line.shift(dots.get_center()[0] * RIGHT) self.play(ShowCreation(v_line)) self.wait() self.play(FadeOut(v_line)) @@ -842,20 +863,18 @@ def draw_transverse_curve(self): target_arcs += [alt_eq, alt_eq.copy()] target_dots += [alt_dots, alt_dots.copy()] - equator_transform = Succession(*[ - Transform(equator, arc, rate_func=linear) - for arc in target_arcs - ]) - dots_transform = Succession(*[ - Transform(dots, target, rate_func=linear) - for target in target_dots - ]) + equator_transform = Succession( + *[Transform(equator, arc, rate_func=linear) for arc in target_arcs] + ) + dots_transform = Succession( + *[Transform(dots, target, rate_func=linear) for target in target_dots] + ) self.play( - ShowCreation(transverse_curve, lag_ratio = 0), + ShowCreation(transverse_curve, lag_ratio=0), equator_transform, dots_transform, - run_time = 10, + run_time=10, rate_func=linear, ) self.wait(2) @@ -870,19 +889,14 @@ def dot_update(dots, alpha): return dots for x in range(2): - self.play( - UpdateFromAlphaFunc(dots, dot_update), - run_time = 4 - ) + self.play(UpdateFromAlphaFunc(dots, dot_update), run_time=4) self.play( UpdateFromAlphaFunc(dots, dot_update), - run_time = 4, - rate_func = lambda t : 0.455*smooth(t) + run_time=4, + rate_func=lambda t: 0.455 * smooth(t), ) self.play( - dots.set_color, YELLOW, - dots.scale_in_place, 1.2, - rate_func = there_and_back + dots.set_color, YELLOW, dots.scale_in_place, 1.2, rate_func=there_and_back ) self.wait() @@ -890,42 +904,42 @@ def dot_update(dots, alpha): def get_arc_walk_dot_movement(self, arc, dots, **kwargs): def dot_update(dots, alpha): - dots[0].move_to(arc.point_from_proportion(0.5*alpha)) - dots[1].move_to(arc.point_from_proportion(0.5+0.5*alpha)) + dots[0].move_to(arc.point_from_proportion(0.5 * alpha)) + dots[1].move_to(arc.point_from_proportion(0.5 + 0.5 * alpha)) return dots + if "run_time" not in kwargs: kwargs["run_time"] = 5 return UpdateFromAlphaFunc(dots, dot_update, **kwargs) def sphere_to_plane(self, point): x, y, z = point - return np.array([ - x - 2*x*z + y + 1, - y+0.5*y*np.cos(z*np.pi), - 0 - ]) - - def sphere_point(self, portion_around_equator, equator_tilt = 0): - theta = portion_around_equator*2*np.pi - point = np.cos(theta)*RIGHT + np.sin(theta)*UP - phi = equator_tilt*np.pi + return np.array([x - 2 * x * z + y + 1, y + 0.5 * y * np.cos(z * np.pi), 0]) + + def sphere_point(self, portion_around_equator, equator_tilt=0): + theta = portion_around_equator * 2 * np.pi + point = np.cos(theta) * RIGHT + np.sin(theta) * UP + phi = equator_tilt * np.pi return rotate_vector(point, phi, RIGHT) def get_great_arc_images(self): - curves = VGroup(*[ - ParametricFunction( - lambda t : self.sphere_point(t, s) - ).apply_function(self.sphere_to_plane) - for s in np.arange(0, 1, 1./self.num_great_arcs) - # for s in [0] - ]) + curves = VGroup( + *[ + ParametricFunction(lambda t: self.sphere_point(t, s)).apply_function( + self.sphere_to_plane + ) + for s in np.arange(0, 1, 1.0 / self.num_great_arcs) + # for s in [0] + ] + ) curves.set_color(YELLOW) curves[0].set_color(RED) for curve in curves: - antipodal_x_diff = lambda x : \ - curve.point_from_proportion(x+0.5)[0]-\ - curve.point_from_proportion(x)[0] - last_x = 0 + antipodal_x_diff = ( + lambda x: curve.point_from_proportion(x + 0.5)[0] + - curve.point_from_proportion(x)[0] + ) + last_x = 0 last_sign = np.sign(antipodal_x_diff(last_x)) for x in np.linspace(0, 0.5, 100): sign = np.sign(antipodal_x_diff(x)) @@ -933,7 +947,7 @@ def get_great_arc_images(self): mean = np.mean([last_x, x]) curve.x_collision_points = [ curve.point_from_proportion(mean), - curve.point_from_proportion(mean+0.5), + curve.point_from_proportion(mean + 0.5), ] curve.collision_point_proportion = mean break @@ -942,39 +956,43 @@ def get_great_arc_images(self): return curves def get_transverse_curve(self, gerat_arc_images): - points = list(it.chain(*[ - [ - curve.x_collision_points[i] - for curve in gerat_arc_images - ] - for i in (0, 1) - ])) - full_curve = VMobject(close_new_points = True) + points = list( + it.chain( + *[ + [curve.x_collision_points[i] for curve in gerat_arc_images] + for i in (0, 1) + ] + ) + ) + full_curve = VMobject(close_new_points=True) full_curve.set_points_smoothly(points + [points[0]]) full_curve.set_color(MAROON_B) - first_half = full_curve.copy().pointwise_become_partial( - full_curve, 0, 0.5 - ) + first_half = full_curve.copy().pointwise_become_partial(full_curve, 0, 0.5) second_half = first_half.copy().rotate_in_place(np.pi, RIGHT) broken_curve = VGroup(first_half, second_half) return broken_curve + class WalkAroundEquatorPreimage(ExternallyAnimatedScene): pass + class WalkTiltedEquatorPreimage(ExternallyAnimatedScene): pass + class FormLoopTransverseToEquator(ExternallyAnimatedScene): pass + class AntipodalWalkAroundTransverseLoop(ExternallyAnimatedScene): pass + class MentionGenerality(TeacherStudentsScene, ThreeDScene): def construct(self): - necklace = Necklace(width = FRAME_X_RADIUS) - necklace.shift(2*UP) + necklace = Necklace(width=FRAME_X_RADIUS) + necklace.shift(2 * UP) necklace.to_edge(RIGHT) arrow = TexMobject("\\Leftrightarrow") arrow.scale(2) @@ -984,43 +1002,41 @@ def construct(self): arrow.add(q_marks) formula = TexMobject("f(\\textbf{x}) = f(-\\textbf{x})") - formula.next_to(self.get_students(), UP, buff = LARGE_BUFF) - formula.to_edge(LEFT, buff = LARGE_BUFF) + formula.next_to(self.get_students(), UP, buff=LARGE_BUFF) + formula.to_edge(LEFT, buff=LARGE_BUFF) self.play( - self.teacher.change_mode, "raise_right_hand", - self.teacher.look_at, arrow + self.teacher.change_mode, "raise_right_hand", self.teacher.look_at, arrow ) self.play( - FadeIn(necklace, run_time = 2, lag_ratio = 0.5), + FadeIn(necklace, run_time=2, lag_ratio=0.5), Write(arrow), - *[ - ApplyMethod(pi.look_at, arrow) - for pi in self.get_pi_creatures() - ] + *[ApplyMethod(pi.look_at, arrow) for pi in self.get_pi_creatures()] ) self.change_student_modes("pondering", "erm", "confused") self.wait() - self.play(*[ - ApplyMethod(pi.look_at, arrow) - for pi in self.get_pi_creatures() - ]) + self.play(*[ApplyMethod(pi.look_at, arrow) for pi in self.get_pi_creatures()]) self.play(Write(formula)) self.wait(3) + class SimpleSphere(ExternallyAnimatedScene): pass + class PointsIn3D(Scene): CONFIG = { # "colors" : [RED, GREEN, BLUE], - "colors" : color_gradient([GREEN, BLUE], 3), + "colors": color_gradient([GREEN, BLUE], 3), } + def construct(self): sphere_def = TextMobject( - "\\doublespacing Sphere in 3D: All", "$(x_1, x_2, x_3)$\\\\", - "such that", "$x_1^2 + x_2^2 + x_3^2 = 1$", - alignment = "", + "\\doublespacing Sphere in 3D: All", + "$(x_1, x_2, x_3)$\\\\", + "such that", + "$x_1^2 + x_2^2 + x_3^2 = 1$", + alignment="", ) sphere_def.next_to(ORIGIN, DOWN) for index, subindex_list in (1, [1, 2, 4, 5, 7, 8]), (3, [0, 2, 4, 6, 8, 10]): @@ -1029,17 +1045,20 @@ def construct(self): sphere_def[index][subindex].set_color(color) point_ex = TextMobject( - "For example, ", - "(", "0.41", ", ", "-0.58", ", ", "0.71", ")", - arg_separator = "" + "For example, ", + "(", + "0.41", + ", ", + "-0.58", + ", ", + "0.71", + ")", + arg_separator="", ) for index, color in zip([2, 4, 6], self.colors): point_ex[index].set_color(color) point_ex.scale(0.8) - point_ex.next_to( - sphere_def[1], UP+RIGHT, - buff = 1.5*LARGE_BUFF - ) + point_ex.next_to(sphere_def[1], UP + RIGHT, buff=1.5 * LARGE_BUFF) point_ex.shift_onto_screen() arrow = Arrow(sphere_def[1].get_top(), point_ex.get_bottom()) @@ -1048,76 +1067,85 @@ def construct(self): self.play(Write(point_ex)) self.wait() self.play( - Animation(sphere_def[1].copy(), remover = True), + Animation(sphere_def[1].copy(), remover=True), Write(sphere_def), ) self.wait() + class AntipodalPairToBeGivenCoordinates(ExternallyAnimatedScene): pass + class WritePointCoordinates(Scene): CONFIG = { - "colors" : color_gradient([GREEN, BLUE], 3), - "corner" : DOWN+RIGHT, + "colors": color_gradient([GREEN, BLUE], 3), + "corner": DOWN + RIGHT, } + def construct(self): coords = self.get_coords() - arrow = Arrow( - -self.corner, self.corner, - stroke_width = 8, - color = MAROON_B - ) - x_component = self.corner[0]*RIGHT - y_component = self.corner[1]*UP + arrow = Arrow(-self.corner, self.corner, stroke_width=8, color=MAROON_B) + x_component = self.corner[0] * RIGHT + y_component = self.corner[1] * UP arrow.next_to( - coords.get_edge_center(y_component), - y_component, - aligned_edge = -x_component, - buff = MED_SMALL_BUFF + coords.get_edge_center(y_component), + y_component, + aligned_edge=-x_component, + buff=MED_SMALL_BUFF, ) group = VGroup(coords, arrow) - group.scale(2) + group.scale(2) group.to_corner(self.corner) - self.play(FadeIn(coords)) self.play(ShowCreation(arrow)) self.wait() def get_coords(self): coords = TexMobject( - "(", "0.41", ", ", "-0.58", ", ", "0.71", ")", - arg_separator = "" + "(", "0.41", ", ", "-0.58", ", ", "0.71", ")", arg_separator="" ) for index, color in zip([1, 3, 5], self.colors): coords[index].set_color(color) return coords + class WriteAntipodalCoordinates(WritePointCoordinates): CONFIG = { - "corner" : UP+LEFT, - "sign_color" : RED, + "corner": UP + LEFT, + "sign_color": RED, } def get_coords(self): coords = TexMobject( - "(", "-", "0.41", ", ", "+", "0.58", ", ", "-", "0.71", ")", - arg_separator = "" + "(", + "-", + "0.41", + ", ", + "+", + "0.58", + ", ", + "-", + "0.71", + ")", + arg_separator="", ) for index, color in zip([2, 5, 8], self.colors): coords[index].set_color(color) - coords[index-1].set_color(self.sign_color) + coords[index - 1].set_color(self.sign_color) return coords + class GeneralizeBorsukUlam(Scene): CONFIG = { - "n_dims" : 3, - "boundary_colors" : [GREEN_B, BLUE], - "output_boundary_color" : [MAROON_B, YELLOW], - "negative_color" : RED, + "n_dims": 3, + "boundary_colors": [GREEN_B, BLUE], + "output_boundary_color": [MAROON_B, YELLOW], + "negative_color": RED, } + def setup(self): self.colors = color_gradient(self.boundary_colors, self.n_dims) @@ -1128,11 +1156,11 @@ def construct(self): output_space = self.get_output_space() equation = self.get_equation() - sphere_set.to_corner(UP+LEFT) + sphere_set.to_corner(UP + LEFT) arrow.next_to(sphere_set, RIGHT) f.next_to(arrow, UP) output_space.next_to(arrow, RIGHT) - equation.next_to(sphere_set, DOWN, buff = LARGE_BUFF) + equation.next_to(sphere_set, DOWN, buff=LARGE_BUFF) equation.to_edge(RIGHT) lhs = VGroup(*equation[:2]) eq = equation[2] @@ -1140,28 +1168,21 @@ def construct(self): self.play(FadeIn(sphere_set)) self.wait() - self.play( - ShowCreation(arrow), - Write(f) - ) + self.play(ShowCreation(arrow), Write(f)) self.play(Write(output_space)) self.wait() self.play(FadeIn(lhs)) - self.play( - ReplacementTransform(lhs.copy(), rhs), - Write(eq) - ) + self.play(ReplacementTransform(lhs.copy(), rhs), Write(eq)) self.wait() def get_condition(self): - squares = list(map(TexMobject, [ - "x_%d^2"%d - for d in range(1, 1+self.n_dims) - ])) + squares = list( + map(TexMobject, ["x_%d^2" % d for d in range(1, 1 + self.n_dims)]) + ) for square, color in zip(squares, self.colors): square[0].set_color(color) square[-1].set_color(color) - plusses = [TexMobject("+") for x in range(self.n_dims-1)] + plusses = [TexMobject("+") for x in range(self.n_dims - 1)] plusses += [TexMobject("=1")] condition = VGroup(*it.chain(*list(zip(squares, plusses)))) condition.arrange(RIGHT) @@ -1169,30 +1190,26 @@ def get_condition(self): return condition def get_tuple(self): - mid_parts = list(it.chain(*[ - ["x_%d"%d, ","] - for d in range(1, self.n_dims) - ])) - tup = TexMobject(*["("] + mid_parts + ["x_%d"%self.n_dims, ")"]) + mid_parts = list(it.chain(*[["x_%d" % d, ","] for d in range(1, self.n_dims)])) + tup = TexMobject(*["("] + mid_parts + ["x_%d" % self.n_dims, ")"]) for index, color in zip(it.count(1, 2), self.colors): tup[index].set_color(color) return tup def get_negative_tuple(self): - mid_parts = list(it.chain(*[ - ["-", "x_%d"%d, ","] - for d in range(1, self.n_dims) - ])) - tup = TexMobject(*["("] + mid_parts + ["-", "x_%d"%self.n_dims, ")"]) + mid_parts = list( + it.chain(*[["-", "x_%d" % d, ","] for d in range(1, self.n_dims)]) + ) + tup = TexMobject(*["("] + mid_parts + ["-", "x_%d" % self.n_dims, ")"]) for index, color in zip(it.count(1, 3), self.colors): tup[index].set_color(self.negative_color) - tup[index+1].set_color(color) + tup[index + 1].set_color(color) return tup def get_output_space(self): - return TextMobject("%dD space"%(self.n_dims-1)) + return TextMobject("%dD space" % (self.n_dims - 1)) # n_dims = self.n_dims-1 # colors = color_gradient(self.output_boundary_color, n_dims) # mid_parts = list(it.chain(*[ @@ -1211,7 +1228,7 @@ def get_equation(self): f1, f2 = [TexMobject("f") for x in range(2)] equals = TexMobject("=") equation = VGroup(f1, tup, equals, f2, neg_tup) - equation.arrange(RIGHT, buff = SMALL_BUFF) + equation.arrange(RIGHT, buff=SMALL_BUFF) return equation @@ -1220,11 +1237,7 @@ def get_sphere_set(self): such_that = TextMobject("such that") such_that.next_to(tup, RIGHT) condition = self.get_condition() - condition.next_to( - tup, DOWN, - buff = MED_LARGE_BUFF, - aligned_edge = LEFT - ) + condition.next_to(tup, DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT) group = VGroup(tup, such_that, condition) l_brace = Brace(group, LEFT) r_brace = Brace(group, RIGHT) @@ -1232,32 +1245,37 @@ def get_sphere_set(self): return group + # class FiveDBorsukUlam(GeneralizeBorsukUlam): # CONFIG = { # "n_dims" : 5, # } + class MentionMakingNecklaceProblemContinuous(TeacherStudentsScene): def construct(self): - self.teacher_says(""" + self.teacher_says( + """ Translate this into a continuous problem. - """) + """ + ) self.change_student_modes("confused", "pondering", "erm") self.wait(3) + class MakeTwoJewelCaseContinuous(IntroduceStolenNecklaceProblem): CONFIG = { - "jewel_colors" : [BLUE, GREEN], - "num_per_jewel" : [8, 10], - "random_seed" : 2, - "forced_binary_choices" : (0, 1, 0), - "show_matching_after_divvying" : True, - "necklace_center" : ORIGIN, - "necklace_width" : FRAME_WIDTH - 3, - "random_seed" : 0, - "num_continuous_division_searches" : 4, + "jewel_colors": [BLUE, GREEN], + "num_per_jewel": [8, 10], + "forced_binary_choices": (0, 1, 0), + "show_matching_after_divvying": True, + "necklace_center": ORIGIN, + "necklace_width": FRAME_WIDTH - 3, + "random_seed": 0, + "num_continuous_division_searches": 4, } + def construct(self): random.seed(self.random_seed) self.introduce_necklace() @@ -1272,16 +1290,11 @@ def construct(self): def introduce_necklace(self): self.get_necklace( - width = self.necklace_width, + width=self.necklace_width, ) - self.play(FadeIn( - self.necklace, - lag_ratio = 0.5 - )) + self.play(FadeIn(self.necklace, lag_ratio=0.5)) self.shuffle_jewels(self.necklace.jewels) - jewel_types = self.get_jewels_organized_by_type( - self.necklace.jewels - ) + jewel_types = self.get_jewels_organized_by_type(self.necklace.jewels) self.wait() self.count_jewel_types(jewel_types) self.wait() @@ -1297,48 +1310,42 @@ def count_jewel_types(self, jewel_types): jewel_copy.next_to(num_mob) label = VGroup(num_mob, jewel_copy) enumeration_labels.add(label) - enumeration_labels.arrange(RIGHT, buff = LARGE_BUFF) + enumeration_labels.arrange(RIGHT, buff=LARGE_BUFF) enumeration_labels.to_edge(UP) for jewel_type, label in zip(jewel_types, enumeration_labels): jewel_type.sort_submobjects() - + jewel_type.save_state() jewel_type.generate_target() jewel_type.target.arrange() - jewel_type.target.move_to(2*UP) - self.play( - MoveToTarget(jewel_type), - Write(label) - ) + jewel_type.target.move_to(2 * UP) + self.play(MoveToTarget(jewel_type), Write(label)) self.play(jewel_type.restore) def divvy_with_n_cuts(self): IntroduceStolenNecklaceProblem.divvy_with_n_cuts( - self, - with_thieves = False, - highlight_groups = False, - show_matching_after_divvying = True, + self, + with_thieves=False, + highlight_groups=False, + show_matching_after_divvying=True, ) def identify_necklace_with_unit_interval(self): interval = UnitInterval( - tick_frequency = 1./sum(self.num_per_jewel), - tick_size = 0.2, - numbers_with_elongated_ticks = [], + tick_frequency=1.0 / sum(self.num_per_jewel), + tick_size=0.2, + numbers_with_elongated_ticks=[], ) interval.stretch_to_fit_width(self.necklace.get_width()) interval.move_to(self.necklace) tick_marks = interval.tick_marks - tick_marks.set_stroke(WHITE, width = 2) + tick_marks.set_stroke(WHITE, width=2) brace = Brace(interval) brace_text = brace.get_text("Length = 1") - self.play( - GrowFromCenter(brace), - Write(brace_text) - ) + self.play(GrowFromCenter(brace), Write(brace_text)) self.wait() self.play( ShowCreation(interval.tick_marks), @@ -1349,15 +1356,13 @@ def identify_necklace_with_unit_interval(self): self.length_brace = VGroup(brace, brace_text) def color_necklace(self): - example_index = len(self.necklace.jewels)/2 + example_index = len(self.necklace.jewels) / 2 jewels = self.necklace.jewels chain = self.necklace.chain self.remove(self.necklace) self.add(chain, jewels) - jewels.submobjects.sort( - key=lambda m: m.get_center()[0] - ) + jewels.submobjects.sort(key=lambda m: m.get_center()[0]) remaining_indices = list(range(len(jewels))) remaining_indices.remove(example_index) @@ -1365,59 +1370,49 @@ def color_necklace(self): remaining_segments = self.color_necklace_by_indices(*remaining_indices) self.remove(chain) segments = VGroup(example_segment[0], *remaining_segments) - segments.submobjects.sort( - key=lambda m: m.get_center()[0] + segments.submobjects.sort(key=lambda m: m.get_center()[0]) + segment_types = VGroup( + *[ + VGroup(*[m for m in segments if m.get_color() == Color(color)]) + for color in self.jewel_colors + ] ) - segment_types = VGroup(*[ - VGroup(*[m for m in segments if m.get_color() == Color(color)]) - for color in self.jewel_colors - ]) for group in segment_types: - length_tex = TexMobject("\\frac{%d}{%d}"%( - len(group), - len(jewels) - )) + length_tex = TexMobject("\\frac{%d}{%d}" % (len(group), len(jewels))) length_tex.next_to(group, UP) length_tex.shift(UP) self.play( - group.shift, UP, - Write(length_tex, run_time = 1), + group.shift, + UP, + Write(length_tex, run_time=1), ) self.wait() - self.play( - group.shift, DOWN, - FadeOut(length_tex) - ) + self.play(group.shift, DOWN, FadeOut(length_tex)) self.play(FadeOut(self.length_brace)) self.segments = segments def color_necklace_by_indices(self, *indices): chain = self.necklace.chain - jewels = VGroup(*[ - self.necklace.jewels[i] - for i in indices - ]) + jewels = VGroup(*[self.necklace.jewels[i] for i in indices]) n_jewels = len(self.necklace.jewels) - segments = VGroup(*[ - Line( - chain.point_from_proportion(index/float(n_jewels)), - chain.point_from_proportion((index+1)/float(n_jewels)), - color = jewel.get_color() - ) - for index, jewel in zip(indices, jewels) - ]) + segments = VGroup( + *[ + Line( + chain.point_from_proportion(index / float(n_jewels)), + chain.point_from_proportion((index + 1) / float(n_jewels)), + color=jewel.get_color(), + ) + for index, jewel in zip(indices, jewels) + ] + ) for jewel in jewels: jewel.save_state() - self.play(jewels.shift, jewels.get_height()*UP) - self.play(ReplacementTransform( - jewels, segments, - lag_ratio = 0.5, - run_time = 2 - )) + self.play(jewels.shift, jewels.get_height() * UP) + self.play(ReplacementTransform(jewels, segments, lag_ratio=0.5, run_time=2)) self.wait() return segments @@ -1426,20 +1421,15 @@ def find_continuous_fair_division(self): n_jewels = len(self.necklace.jewels) slice_indices, ignore = self.find_slice_indices( - self.necklace.jewels, - self.jewel_types + self.necklace.jewels, self.jewel_types ) cut_proportions = [ sorted([random.random(), random.random()]) for x in range(self.num_continuous_division_searches) ] - cut_proportions.append([ - float(i)/n_jewels - for i in slice_indices[1:-1] - ]) + cut_proportions.append([float(i) / n_jewels for i in slice_indices[1:-1]]) cut_points = [ - list(map(chain.point_from_proportion, pair)) - for pair in cut_proportions + list(map(chain.point_from_proportion, pair)) for pair in cut_proportions ] v_lines = VGroup(*[DashedLine(UP, DOWN) for x in range(2)]) @@ -1449,10 +1439,12 @@ def find_continuous_fair_division(self): self.play(ShowCreation(v_lines)) self.wait() for target_points in cut_points[1:]: - self.play(*[ - ApplyMethod(line.move_to, point) - for line, point in zip(v_lines, target_points) - ]) + self.play( + *[ + ApplyMethod(line.move_to, point) + for line, point in zip(v_lines, target_points) + ] + ) self.wait() self.slice_indices = slice_indices @@ -1471,10 +1463,12 @@ def show_continuous_fair_division(self): groups[-1].add(self.tick_marks[-1]) vects = [[UP, DOWN][i] for i in self.forced_binary_choices] - self.play(*[ - ApplyMethod(group.shift, 0.5*vect) - for group, vect in zip(groups, vects) - ]) + self.play( + *[ + ApplyMethod(group.shift, 0.5 * vect) + for group, vect in zip(groups, vects) + ] + ) self.wait() self.groups = groups @@ -1485,26 +1479,28 @@ def set_color_continuous_groups(self): boxes = VGroup() for group in top_group, bottom_group: box = Rectangle( - width = FRAME_WIDTH-2, - height = group.get_height()+SMALL_BUFF, - stroke_width = 0, - fill_color = WHITE, - fill_opacity = 0.25, + width=FRAME_WIDTH - 2, + height=group.get_height() + SMALL_BUFF, + stroke_width=0, + fill_color=WHITE, + fill_opacity=0.25, ) - box.shift(group.get_center()[1]*UP) + box.shift(group.get_center()[1] * UP) boxes.add(box) - weight_description = VGroup(*[ - VGroup( - TexMobject("\\frac{%d}{%d}"%( - len(jewel_type)/2, len(self.segments) - )), - Jewel(color = jewel_type[0].get_color()) - ).arrange() - for jewel_type in self.jewel_types - ]) - weight_description.arrange(buff = LARGE_BUFF) - weight_description.next_to(boxes, UP, aligned_edge = LEFT) + weight_description = VGroup( + *[ + VGroup( + TexMobject( + "\\frac{%d}{%d}" % (len(jewel_type) / 2, len(self.segments)) + ), + Jewel(color=jewel_type[0].get_color()), + ).arrange() + for jewel_type in self.jewel_types + ] + ) + weight_description.arrange(buff=LARGE_BUFF) + weight_description.next_to(boxes, UP, aligned_edge=LEFT) self.play(FadeIn(boxes)) self.play(Write(weight_description)) @@ -1519,21 +1515,21 @@ def mention_equivalence_to_discrete_case(self): morty.to_edge(DOWN) morty.shift(LEFT) self.play(FadeIn(morty)) - self.play(PiCreatureSays( - morty, - """This is equivalent to + self.play( + PiCreatureSays( + morty, + """This is equivalent to the discrete case. """, - bubble_kwargs = { - "height" : 3, - "direction" : LEFT, - } - )) + bubble_kwargs={ + "height": 3, + "direction": LEFT, + }, + ) + ) self.play(Blink(morty)) self.wait() - self.play(*list(map(FadeOut, [ - morty, morty.bubble, morty.bubble.content - ]))) - + self.play(*list(map(FadeOut, [morty, morty.bubble, morty.bubble.content]))) + def shift_divide_off_tick_marks(self): groups = self.groups slice_indices = self.slice_indices @@ -1557,33 +1553,37 @@ def shift_divide_off_tick_marks(self): for mob in restorers: mob.save_state() - emerald_segments = VGroup(*[ - segment - for segment in list(groups[0][0])+list(groups[2][0]) - if segment.get_color() == Color(self.jewel_colors[1]) - if segment is not right_segment - ]) + emerald_segments = VGroup( + *[ + segment + for segment in list(groups[0][0]) + list(groups[2][0]) + if segment.get_color() == Color(self.jewel_colors[1]) + if segment is not right_segment + ] + ) emerald_segments.add( left_segment.parts[0], right_segment.parts[1], ) emerald_segments.sort() - self.play(v_lines.shift, segment_width*RIGHT/2) - self.play(*[ - ApplyMethod(mob.shift, vect) - for mob, vect in [ - (left_segment.parts[0], UP), - (left_tick, UP), - (right_segment.parts[0], DOWN), - (right_tick, DOWN), + self.play(v_lines.shift, segment_width * RIGHT / 2) + self.play( + *[ + ApplyMethod(mob.shift, vect) + for mob, vect in [ + (left_segment.parts[0], UP), + (left_tick, UP), + (right_segment.parts[0], DOWN), + (right_tick, DOWN), + ] ] - ]) + ) self.wait() words = TextMobject("Cut part way through segment") words.to_edge(RIGHT) - words.shift(2*UP) + words.shift(2 * UP) arrow1 = Arrow(words.get_bottom(), left_segment.parts[0].get_right()) arrow2 = Arrow(words.get_bottom(), right_segment.parts[1].get_left()) VGroup(words, arrow1, arrow2).set_color(RED) @@ -1594,11 +1594,10 @@ def shift_divide_off_tick_marks(self): emerald_segments.save_state() emerald_segments.generate_target() emerald_segments.target.arrange() - emerald_segments.target.move_to(2*DOWN) + emerald_segments.target.move_to(2 * DOWN) brace = Brace(emerald_segments.target, DOWN) label = VGroup( - TexMobject("5\\left( 1/18 \\right)"), - Jewel(color = self.jewel_colors[1]) + TexMobject("5\\left( 1/18 \\right)"), Jewel(color=self.jewel_colors[1]) ).arrange() label.next_to(brace, DOWN) self.play(MoveToTarget(emerald_segments)) @@ -1607,20 +1606,19 @@ def shift_divide_off_tick_marks(self): self.wait() broken_pair = VGroup(*emerald_segments[2:4]) broken_pair.save_state() - self.play(broken_pair.shift, 0.5*UP) - vect = broken_pair[1].get_left()-broken_pair[1].get_right() + self.play(broken_pair.shift, 0.5 * UP) + vect = broken_pair[1].get_left() - broken_pair[1].get_right() self.play( - broken_pair[0].shift, -vect/2, - broken_pair[1].shift, vect/2, + broken_pair[0].shift, + -vect / 2, + broken_pair[1].shift, + vect / 2, ) self.wait() self.play(broken_pair.space_out_submobjects) self.play(broken_pair.restore) self.wait() - self.play( - emerald_segments.restore, - *list(map(FadeOut, [brace, label])) - ) + self.play(emerald_segments.restore, *list(map(FadeOut, [brace, label]))) self.wait() self.play(ShowCreation(arrow2)) @@ -1628,30 +1626,35 @@ def shift_divide_off_tick_marks(self): self.play(*list(map(FadeOut, [words, arrow1, arrow2]))) for line in v_lines: - self.play(line.shift, segment_width*LEFT/2) + self.play(line.shift, segment_width * LEFT / 2) self.play(*[mob.restore for mob in restorers]) self.remove(left_segment.parts, right_segment.parts) self.add(left_segment, right_segment) + class ThinkAboutTheChoices(TeacherStudentsScene): def construct(self): - self.teacher_says(""" + self.teacher_says( + """ Think about the choices behind a division... - """) + """ + ) self.change_student_modes( - *["pondering"]*3, - look_at_arg = FRAME_X_RADIUS*RIGHT+FRAME_Y_RADIUS*DOWN + *["pondering"] * 3, + look_at_arg=FRAME_X_RADIUS * RIGHT + FRAME_Y_RADIUS * DOWN ) self.wait(3) + class ChoicesInNecklaceCutting(ReconfigurableScene): CONFIG = { - "num_continuous_division_searches" : 4, - "denoms" : [6, 3, 2], - "necklace_center" : DOWN, - "thief_box_offset" : 1.2, + "num_continuous_division_searches": 4, + "denoms": [6, 3, 2], + "necklace_center": DOWN, + "thief_box_offset": 1.2, } + def construct(self): self.add_necklace() self.choose_places_to_cut() @@ -1661,43 +1664,36 @@ def construct(self): def add_necklace(self): width, colors, num_per_color = [ MakeTwoJewelCaseContinuous.CONFIG[key] - for key in [ - "necklace_width", "jewel_colors", "num_per_jewel" - ] + for key in ["necklace_width", "jewel_colors", "num_per_jewel"] ] - color_list = list(it.chain(*[ - num*[color] - for num, color in zip(num_per_color, colors) - ])) + color_list = list( + it.chain(*[num * [color] for num, color in zip(num_per_color, colors)]) + ) random.shuffle(color_list) interval = UnitInterval( - tick_frequency = 1./sum(num_per_color), - tick_size = 0.2, - numbers_with_elongated_ticks = [], + tick_frequency=1.0 / sum(num_per_color), + tick_size=0.2, + numbers_with_elongated_ticks=[], ) interval.stretch_to_fit_width(width) interval.shift(self.necklace_center) tick_marks = interval.tick_marks - tick_marks.set_stroke(WHITE, width = 2) + tick_marks.set_stroke(WHITE, width=2) segments = VGroup() for l_tick, r_tick, color in zip(tick_marks, tick_marks[1:], color_list): - segment = Line( - l_tick.get_center(), - r_tick.get_center(), - color = color - ) + segment = Line(l_tick.get_center(), r_tick.get_center(), color=color) segments.add(segment) self.necklace = VGroup(segments, tick_marks) self.add(self.necklace) - self.interval = interval + self.interval = interval def choose_places_to_cut(self): v_lines = VGroup(*[DashedLine(UP, DOWN) for x in range(2)]) - final_num_pair = np.cumsum([1./d for d in self.denoms[:2]]) + final_num_pair = np.cumsum([1.0 / d for d in self.denoms[:2]]) num_pairs = [ sorted([random.random(), random.random()]) @@ -1705,68 +1701,69 @@ def choose_places_to_cut(self): ] + [final_num_pair] point_pairs = [ - list(map(self.interval.number_to_point, num_pair)) - for num_pair in num_pairs + list(map(self.interval.number_to_point, num_pair)) for num_pair in num_pairs ] - + for line, point in zip(v_lines, point_pairs[0]): line.move_to(point) self.play(ShowCreation(v_lines)) for point_pair in point_pairs[1:]: self.wait() - self.play(*[ - ApplyMethod(line.move_to, point) - for line, point in zip(v_lines, point_pair) - ]) + self.play( + *[ + ApplyMethod(line.move_to, point) + for line, point in zip(v_lines, point_pair) + ] + ) self.wait() - self.division_points = list(it.chain( - [self.interval.get_left()], - point_pairs[-1], - [self.interval.get_right()] - )) + self.division_points = list( + it.chain( + [self.interval.get_left()], point_pairs[-1], [self.interval.get_right()] + ) + ) self.v_lines = v_lines def show_three_numbers_adding_to_one(self): points = self.division_points braces = [ - Brace(Line(p1+SMALL_BUFF*RIGHT/2, p2+SMALL_BUFF*LEFT/2)) + Brace(Line(p1 + SMALL_BUFF * RIGHT / 2, p2 + SMALL_BUFF * LEFT / 2)) for p1, p2 in zip(points, points[1:]) ] for char, denom, brace in zip("abc", self.denoms, braces): - brace.label = brace.get_text("$%s$"%char) - brace.concrete_label = brace.get_text("$\\frac{1}{%d}$"%denom) - VGroup( - brace.label, - brace.concrete_label - ).set_color(YELLOW) + brace.label = brace.get_text("$%s$" % char) + brace.concrete_label = brace.get_text("$\\frac{1}{%d}$" % denom) + VGroup(brace.label, brace.concrete_label).set_color(YELLOW) - words = TextMobject( - "1) Choose", "$a$, $b$, $c$", "so that", "$a+b+c = 1$" - ) + words = TextMobject("1) Choose", "$a$, $b$, $c$", "so that", "$a+b+c = 1$") words[1].set_color(YELLOW) words[3].set_color(YELLOW) - words.to_corner(UP+LEFT) + words.to_corner(UP + LEFT) - self.play(*it.chain(*[ - [GrowFromCenter(brace), Write(brace.label)] - for brace in braces - ])) + self.play( + *it.chain( + *[[GrowFromCenter(brace), Write(brace.label)] for brace in braces] + ) + ) self.play(Write(words)) self.wait(2) - self.play(*[ - ReplacementTransform(brace.label, brace.concrete_label) - for brace in braces - ]) + self.play( + *[ + ReplacementTransform(brace.label, brace.concrete_label) + for brace in braces + ] + ) self.wait() self.wiggle_v_lines() self.wait() - self.transition_to_alt_config(denoms = [3, 3, 3]) + self.transition_to_alt_config(denoms=[3, 3, 3]) self.wait() - self.play(*list(map(FadeOut, list(braces) + [ - brace.concrete_label for brace in braces - ]))) + self.play( + *list( + map(FadeOut, list(braces) + [brace.concrete_label for brace in braces]) + ) + ) self.choice_one_words = words @@ -1776,35 +1773,33 @@ def make_binary_choice(self): arrow_pairs, curr_arrows = self.get_choice_arrow_pairs(groups) words = TextMobject("2) Make a binary choice for each segment") words.next_to( - self.choice_one_words, DOWN, - buff = MED_LARGE_BUFF, - aligned_edge = LEFT + self.choice_one_words, DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT ) self.play(Write(words)) self.play(*list(map(FadeIn, [boxes, labels]))) - for binary_choices in it.product(*[[0, 1]]*3): - self.play(*[ - ApplyMethod(group.move_to, group.target_points[choice]) - for group, choice in zip(groups, binary_choices) - ] + [ - Transform( - curr_arrow, arrow_pair[choice], - path_arc = np.pi - ) - for curr_arrow, arrow_pair, choice in zip( - curr_arrows, arrow_pairs, binary_choices - ) - ]) + for binary_choices in it.product(*[[0, 1]] * 3): + self.play( + *[ + ApplyMethod(group.move_to, group.target_points[choice]) + for group, choice in zip(groups, binary_choices) + ] + + [ + Transform(curr_arrow, arrow_pair[choice], path_arc=np.pi) + for curr_arrow, arrow_pair, choice in zip( + curr_arrows, arrow_pairs, binary_choices + ) + ] + ) self.wait() ###### - def get_groups(self, indices = None): + def get_groups(self, indices=None): segments, tick_marks = self.necklace if indices is None: n_segments = len(segments) - indices = [0, n_segments/6, n_segments/2, n_segments] + indices = [0, n_segments / 6, n_segments / 2, n_segments] groups = [ VGroup( @@ -1819,44 +1814,36 @@ def get_groups(self, indices = None): for group in groups: group.target_points = [ - group.get_center() + self.thief_box_offset*vect - for vect in (UP, DOWN) + group.get_center() + self.thief_box_offset * vect for vect in (UP, DOWN) ] return groups def get_boxes_and_labels(self): box = Rectangle( - height = self.necklace.get_height()+SMALL_BUFF, - width = self.necklace.get_width()+2*SMALL_BUFF, - stroke_width = 0, - fill_color = WHITE, - fill_opacity = 0.25 + height=self.necklace.get_height() + SMALL_BUFF, + width=self.necklace.get_width() + 2 * SMALL_BUFF, + stroke_width=0, + fill_color=WHITE, + fill_opacity=0.25, ) box.move_to(self.necklace) - boxes = VGroup(*[ - box.copy().shift(self.thief_box_offset*vect) - for vect in (UP, DOWN) - ]) - labels = VGroup(*[ - TextMobject( - "Thief %d"%(i+1) - ).next_to(box, UP, aligned_edge = RIGHT) - for i, box in enumerate(boxes) - ]) + boxes = VGroup( + *[box.copy().shift(self.thief_box_offset * vect) for vect in (UP, DOWN)] + ) + labels = VGroup( + *[ + TextMobject("Thief %d" % (i + 1)).next_to(box, UP, aligned_edge=RIGHT) + for i, box in enumerate(boxes) + ] + ) return boxes, labels def get_choice_arrow_pairs(self, groups): arrow = TexMobject("\\uparrow") - arrow_pairs = [ - [arrow.copy(), arrow.copy().rotate(np.pi)] - for group in groups - ] - pre_arrow_points = [ - VectorizedPoint(group.get_center()) - for group in groups - ] + arrow_pairs = [[arrow.copy(), arrow.copy().rotate(np.pi)] for group in groups] + pre_arrow_points = [VectorizedPoint(group.get_center()) for group in groups] for point, arrow_pair in zip(pre_arrow_points, arrow_pairs): for arrow, color in zip(arrow_pair, [GREEN, RED]): arrow.set_color(color) @@ -1865,31 +1852,35 @@ def get_choice_arrow_pairs(self, groups): def wiggle_v_lines(self): self.play( - *it.chain(*[ - [ - line.rotate_in_place, np.pi/12, vect, - line.set_color, RED + *it.chain( + *[ + [line.rotate_in_place, np.pi / 12, vect, line.set_color, RED] + for line, vect in zip(self.v_lines, [OUT, IN]) ] - for line, vect in zip(self.v_lines, [OUT, IN]) - ]), - rate_func = wiggle + ), + rate_func=wiggle ) + class CompareThisToSphereChoice(TeacherStudentsScene): def construct(self): - self.teacher_says(""" + self.teacher_says( + """ Compare this to choosing a point on the sphere. - """) + """ + ) self.change_student_modes( - *["pondering"]*3, - look_at_arg = FRAME_X_RADIUS*RIGHT+FRAME_Y_RADIUS*DOWN + *["pondering"] * 3, + look_at_arg=FRAME_X_RADIUS * RIGHT + FRAME_Y_RADIUS * DOWN ) self.wait(3) + class SimpleRotatingSphereWithPoint(ExternallyAnimatedScene): pass + class ChoicesForSpherePoint(GeneralizeBorsukUlam): def construct(self): self.add_sphere_set() @@ -1908,7 +1899,7 @@ def get_tuple(self): def get_condition(self): condition = TexMobject("x^2+y^2+z^2 = 1") for i, color in zip([0, 3, 6], self.colors): - VGroup(*condition[i:i+2]).set_color(color) + VGroup(*condition[i : i + 2]).set_color(color) return condition def add_sphere_set(self): @@ -1922,49 +1913,41 @@ def add_sphere_set(self): def initialize_words(self): choice_one_words = TextMobject( - "1) Choose", "$x^2$, $y^2$, $z^2$", - "so that", "$x^2+y^2+z^2 = 1$" + "1) Choose", "$x^2$, $y^2$, $z^2$", "so that", "$x^2+y^2+z^2 = 1$" ) for i in 1, 3: for j, color in zip([0, 3, 6], self.colors): - VGroup(*choice_one_words[i][j:j+2]).set_color(color) - choice_one_words.to_corner(UP+LEFT) + VGroup(*choice_one_words[i][j : j + 2]).set_color(color) + choice_one_words.to_corner(UP + LEFT) - choice_two_words = TextMobject( - "2) Make a binary choice for each one" - ) + choice_two_words = TextMobject("2) Make a binary choice for each one") choice_two_words.next_to( - choice_one_words, DOWN, - buff = MED_LARGE_BUFF, - aligned_edge = LEFT + choice_one_words, DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT ) self.choice_one_words = choice_one_words self.choice_two_words = choice_two_words def show_example_choices(self): - choices = VGroup(*[ - TexMobject(*tex).set_color(color) - for color, tex in zip(self.colors, [ - ("x", "^2 = ", "1/6"), - ("y", "^2 = ", "1/3"), - ("z", "^2 = ", "1/2"), - ]) - ]) - choices.arrange( - DOWN, - buff = LARGE_BUFF, - aligned_edge = LEFT + choices = VGroup( + *[ + TexMobject(*tex).set_color(color) + for color, tex in zip( + self.colors, + [ + ("x", "^2 = ", "1/6"), + ("y", "^2 = ", "1/3"), + ("z", "^2 = ", "1/2"), + ], + ) + ] ) + choices.arrange(DOWN, buff=LARGE_BUFF, aligned_edge=LEFT) choices.set_height(FRAME_Y_RADIUS) choices.to_edge(LEFT) choices.shift(DOWN) - self.play(FadeIn( - choices, - run_time = 2, - lag_ratio = 0.5 - )) + self.play(FadeIn(choices, run_time=2, lag_ratio=0.5)) self.wait() self.choices = choices @@ -1973,40 +1956,45 @@ def show_binary_choices(self): for choice in self.choices: var_tex = choice.expression_parts[0] frac_tex = choice.expression_parts[2] - sqrts = VGroup(*[ - TexMobject( - var_tex + "=" + sign + \ - "\\sqrt{%s}"%frac_tex) - for sign in ["+", "-"] - ]) + sqrts = VGroup( + *[ + TexMobject(var_tex + "=" + sign + "\\sqrt{%s}" % frac_tex) + for sign in ["+", "-"] + ] + ) for sqrt in sqrts: sqrt.scale(0.6) sqrts.arrange(DOWN) - sqrts.next_to(choice, RIGHT, buff = LARGE_BUFF) + sqrts.next_to(choice, RIGHT, buff=LARGE_BUFF) sqrts.set_color(choice.get_color()) - arrows = VGroup(*[ - Arrow( - choice.get_right(), sqrt.get_left(), - color = WHITE, - tip_length = 0.1, - buff = SMALL_BUFF - ) - for sqrt in sqrts - ]) + arrows = VGroup( + *[ + Arrow( + choice.get_right(), + sqrt.get_left(), + color=WHITE, + tip_length=0.1, + buff=SMALL_BUFF, + ) + for sqrt in sqrts + ] + ) self.play(ShowCreation(arrows)) - self.play(FadeIn(sqrts, lag_ratio = 0.5)) + self.play(FadeIn(sqrts, lag_ratio=0.5)) self.play(Write(self.choice_two_words)) self.wait() + class NecklaceDivisionSphereAssociation(ChoicesInNecklaceCutting): CONFIG = { - "xyz_colors" : color_gradient([GREEN_B, BLUE], 3), - "necklace_center" : DOWN, - "thief_box_offset" : 1.6, - "denoms" : [6, 3, 2], + "xyz_colors": color_gradient([GREEN_B, BLUE], 3), + "necklace_center": DOWN, + "thief_box_offset": 1.6, + "denoms": [6, 3, 2], } + def construct(self): self.add_necklace() self.add_sphere_point_label() @@ -2017,24 +2005,17 @@ def construct(self): self.ask_about_antipodal_pairs() def add_sphere_point_label(self): - label = TextMobject( - "$(x, y, z)$", - "such that", - "$x^2 + y^2 + z^2 = 1$" - ) + label = TextMobject("$(x, y, z)$", "such that", "$x^2 + y^2 + z^2 = 1$") for i, j_list in (0, [1, 3, 5]), (2, [0, 3, 6]): for j, color in zip(j_list, self.xyz_colors): label[i][j].set_color(color) - label.to_corner(UP+RIGHT) + label.to_corner(UP + RIGHT) ghost_sphere_point = VectorizedPoint() - ghost_sphere_point.to_corner(UP+LEFT, buff = LARGE_BUFF) - ghost_sphere_point.shift(2*RIGHT) + ghost_sphere_point.to_corner(UP + LEFT, buff=LARGE_BUFF) + ghost_sphere_point.shift(2 * RIGHT) - arrow = Arrow( - label.get_left(), ghost_sphere_point, - color = WHITE - ) + arrow = Arrow(label.get_left(), ghost_sphere_point, color=WHITE) self.add(label, arrow) @@ -2043,28 +2024,25 @@ def add_sphere_point_label(self): def add_braces(self): points = self.division_points braces = [ - Brace( - Line(p1+SMALL_BUFF*RIGHT/2, p2+SMALL_BUFF*LEFT/2), - UP - ) + Brace(Line(p1 + SMALL_BUFF * RIGHT / 2, p2 + SMALL_BUFF * LEFT / 2), UP) for p1, p2 in zip(points, points[1:]) ] - for char, brace, color, denom in zip("xyz", braces, self.xyz_colors, self.denoms): + for char, brace, color, denom in zip( + "xyz", braces, self.xyz_colors, self.denoms + ): brace.label = brace.get_text( - "$%s^2$"%char, "$= 1/%d$"%denom, - buff = SMALL_BUFF + "$%s^2$" % char, "$= 1/%d$" % denom, buff=SMALL_BUFF ) brace.label.set_color(color) if brace.label.get_right()[0] > brace.get_right()[0]: - brace.label.next_to( - brace, UP, buff = SMALL_BUFF, - aligned_edge = RIGHT - ) + brace.label.next_to(brace, UP, buff=SMALL_BUFF, aligned_edge=RIGHT) - self.play(*it.chain( - list(map(GrowFromCenter, braces)), - [Write(brace.label) for brace in braces] - )) + self.play( + *it.chain( + list(map(GrowFromCenter, braces)), + [Write(brace.label) for brace in braces], + ) + ) self.wait() self.braces = braces @@ -2082,9 +2060,7 @@ def show_binary_choice_association(self): for brace, denom, group, final_choice in quads: char = brace.label.args[0][1] choices = [ - TexMobject( - char, "=", sign, "\\sqrt{\\frac{1}{%d}}"%denom - ) + TexMobject(char, "=", sign, "\\sqrt{\\frac{1}{%d}}" % denom) for sign in ("+", "-") ] for choice, color in zip(choices, [GREEN, RED]): @@ -2092,41 +2068,38 @@ def show_binary_choice_association(self): choice[2].set_color(color) choice.scale(0.8) choice.move_to(group) - if choice.get_width() > 0.8*group.get_width(): - choice.next_to(group.get_right(), LEFT, buff = MED_SMALL_BUFF) + if choice.get_width() > 0.8 * group.get_width(): + choice.next_to(group.get_right(), LEFT, buff=MED_SMALL_BUFF) original_choices = [m.copy() for m in choices] self.play( ReplacementTransform( - VGroup(brace.label[0], brace, brace.label[1]), - choices[0] + VGroup(brace.label[0], brace, brace.label[1]), choices[0] ), - group.move_to, group.target_points[0] + group.move_to, + group.target_points[0], ) self.wait() - self.play( - Transform(*choices), - group.move_to, group.target_points[1] - ) + self.play(Transform(*choices), group.move_to, group.target_points[1]) self.wait() if final_choice == 0: self.play( Transform(choices[0], original_choices[0]), - group.move_to, group.target_points[0] + group.move_to, + group.target_points[0], ) self.swapping_anims += [ - Transform(choices[0], original_choices[1-final_choice]), - group.move_to, group.target_points[1-final_choice] + Transform(choices[0], original_choices[1 - final_choice]), + group.move_to, + group.target_points[1 - final_choice], ] def ask_about_antipodal_pairs(self): question = TextMobject("What do antipodal points signify?") question.move_to(self.sphere_point_label, LEFT) question.set_color(MAROON_B) - antipodal_tex = TexMobject( - "(x, y, z) \\rightarrow (-x, -y, -z)" - ) - antipodal_tex.next_to(question, DOWN, aligned_edge = LEFT) + antipodal_tex = TexMobject("(x, y, z) \\rightarrow (-x, -y, -z)") + antipodal_tex.next_to(question, DOWN, aligned_edge=LEFT) self.play(FadeOut(self.sphere_point_label)) self.play(FadeIn(question)) @@ -2138,14 +2111,17 @@ def ask_about_antipodal_pairs(self): self.play(*self.swapping_anims) self.wait() + class SimpleRotatingSphereWithAntipodes(ExternallyAnimatedScene): pass + class TotalLengthOfEachJewelEquals(NecklaceDivisionSphereAssociation, ThreeDScene): CONFIG = { - "random_seed" : 1, - "thief_box_offset" : 1.2, + "random_seed": 1, + "thief_box_offset": 1.2, } + def construct(self): random.seed(self.random_seed) self.add_necklace() @@ -2157,29 +2133,28 @@ def construct(self): def find_fair_division(self): segments, tick_marks = self.necklace segments.sort() - segment_colors = [ - segment.get_color() - for segment in segments - ] + segment_colors = [segment.get_color() for segment in segments] indices = self.get_fair_division_indices(segment_colors) - groups = self.get_groups( - [0] + list(np.array(indices)+1) + [len(segments)] - ) + groups = self.get_groups([0] + list(np.array(indices) + 1) + [len(segments)]) self.add(*groups) binary_choice = [0, 1, 0] v_lines = VGroup(*[DashedLine(UP, DOWN) for x in range(2)]) v_lines.move_to(self.necklace) self.play(ShowCreation(v_lines)) - self.play(*[ - ApplyMethod(line.move_to, segments[index].get_right()) - for line, index in zip(v_lines, indices) - ]) + self.play( + *[ + ApplyMethod(line.move_to, segments[index].get_right()) + for line, index in zip(v_lines, indices) + ] + ) self.wait() - self.play(*[ - ApplyMethod(group.move_to, group.target_points[choice]) - for group, choice in zip(groups, binary_choice) - ]) + self.play( + *[ + ApplyMethod(group.move_to, group.target_points[choice]) + for group, choice in zip(groups, binary_choice) + ] + ) self.wait() self.groups = groups @@ -2188,13 +2163,10 @@ def find_fair_division(self): def get_fair_division_indices(self, colors): colors = np.array(list(colors)) color_types = list(map(Color, set([c.get_hex_l() for c in colors]))) - type_to_count = dict([ - (color, sum(colors == color)) - for color in color_types - ]) - for i1, i2 in it.combinations(list(range(1, len(colors)-1)), 2): + type_to_count = dict([(color, sum(colors == color)) for color in color_types]) + for i1, i2 in it.combinations(list(range(1, len(colors) - 1)), 2): bools = [ - sum(colors[i1:i2] == color) == type_to_count[color]/2 + sum(colors[i1:i2] == color) == type_to_count[color] / 2 for color in color_types ] if np.all(bools): @@ -2203,35 +2175,41 @@ def get_fair_division_indices(self, colors): def demonstrate_fair_division(self): segments, tick_marks = self.necklace - color_types = list(map(Color, set([ - segment.get_color().get_hex_l() - for segment in segments - ]))) - top_segments = VGroup(*it.chain( - self.groups[0][0], - self.groups[2][0], - )) + color_types = list( + map(Color, set([segment.get_color().get_hex_l() for segment in segments])) + ) + top_segments = VGroup( + *it.chain( + self.groups[0][0], + self.groups[2][0], + ) + ) bottom_segments = self.groups[1][0] for color in color_types: monochrome_groups = [ - VGroup(*[segment for segment in segment_group if segment.get_color() == color]) + VGroup( + *[ + segment + for segment in segment_group + if segment.get_color() == color + ] + ) for segment_group in (top_segments, bottom_segments) ] labels = VGroup() for i, group in enumerate(monochrome_groups): group.save_state() group.generate_target() - group.target.arrange(buff = SMALL_BUFF) + group.target.arrange(buff=SMALL_BUFF) brace = Brace(group.target, UP) label = VGroup( - TextMobject("Thief %d"%(i+1)), - Jewel(color = group[0].get_color()) + TextMobject("Thief %d" % (i + 1)), Jewel(color=group[0].get_color()) ) label.arrange() label.next_to(brace, UP) full_group = VGroup(group.target, brace, label) vect = LEFT if i == 0 else RIGHT - full_group.next_to(ORIGIN, vect, buff = MED_LARGE_BUFF) + full_group.next_to(ORIGIN, vect, buff=MED_LARGE_BUFF) full_group.to_edge(UP) labels.add(VGroup(brace, label)) equals = TexMobject("=") @@ -2244,43 +2222,38 @@ def demonstrate_fair_division(self): FadeIn(label), ) self.wait() - self.play( - FadeOut(labels), - *[group.restore for group in monochrome_groups] - ) + self.play(FadeOut(labels), *[group.restore for group in monochrome_groups]) self.wait() def perform_antipodal_swap(self): binary_choices_list = [(1, 0, 1), (0, 1, 0)] for binary_choices in binary_choices_list: - self.play(*[ - ApplyMethod( - group.move_to, - group.target_points[choice] - ) - for group, choice in zip(self.groups, binary_choices) - ]) + self.play( + *[ + ApplyMethod(group.move_to, group.target_points[choice]) + for group, choice in zip(self.groups, binary_choices) + ] + ) self.wait() + class ExclaimBorsukUlam(TeacherStudentsScene): def construct(self): - self.student_says( - "Borsuk-Ulam!", - target_mode = "hooray" + self.student_says("Borsuk-Ulam!", target_mode="hooray") + self.play( + *[ApplyMethod(pi.change_mode, "hooray") for pi in self.get_pi_creatures()] ) - self.play(*[ - ApplyMethod(pi.change_mode, "hooray") - for pi in self.get_pi_creatures() - ]) self.wait(3) + class ShowFunctionDiagram(TotalLengthOfEachJewelEquals, ReconfigurableScene): CONFIG = { - "necklace_center" : ORIGIN, - "camera_class" : ThreeDCamera, - "thief_box_offset" : 0.3, - "make_up_fair_division_indices" : False, + "necklace_center": ORIGIN, + "camera_class": ThreeDCamera, + "thief_box_offset": 0.3, + "make_up_fair_division_indices": False, } + def construct(self): self.add_necklace() self.add_number_pair() @@ -2290,33 +2263,29 @@ def construct(self): def add_necklace(self): random.seed(self.random_seed) ChoicesInNecklaceCutting.add_necklace(self) - self.necklace.set_width(FRAME_X_RADIUS-1) - self.necklace.to_edge(UP, buff = LARGE_BUFF) - self.necklace.to_edge(LEFT, buff = SMALL_BUFF) + self.necklace.set_width(FRAME_X_RADIUS - 1) + self.necklace.to_edge(UP, buff=LARGE_BUFF) + self.necklace.to_edge(LEFT, buff=SMALL_BUFF) self.add(self.necklace) self.find_fair_division() def add_number_pair(self): plane_classes = [ - JewelPairPlane( - skip_animations = True, - thief_number = x - ) - for x in (1, 2) + JewelPairPlane(skip_animations=True, thief_number=x) for x in (1, 2) ] - t1_plane, t2_plane = planes = VGroup(*[ - VGroup(*plane_class.get_top_level_mobjects()) - for plane_class in plane_classes - ]) + t1_plane, t2_plane = planes = VGroup( + *[ + VGroup(*plane_class.get_top_level_mobjects()) + for plane_class in plane_classes + ] + ) planes.set_width(FRAME_X_RADIUS) planes.to_edge(RIGHT) self.example_coords = plane_classes[0].example_coords[0] arrow = Arrow( - self.necklace.get_corner(DOWN+RIGHT), - self.example_coords, - color = YELLOW + self.necklace.get_corner(DOWN + RIGHT), self.example_coords, color=YELLOW ) self.play(ShowCreation(arrow)) @@ -2325,40 +2294,32 @@ def add_number_pair(self): clean_state = VGroup(*self.mobjects).family_members_with_points() self.clear() self.add(*clean_state) - self.transition_to_alt_config( - make_up_fair_division_indices = True - ) + self.transition_to_alt_config(make_up_fair_division_indices=True) self.wait() t1_plane.save_state() - self.play( - Transform(*planes, path_arc = np.pi), - Animation(arrow) - ) + self.play(Transform(*planes, path_arc=np.pi), Animation(arrow)) self.wait(2) - self.play( - ApplyMethod(t1_plane.restore, path_arc = np.pi), - Animation(arrow) - ) + self.play(ApplyMethod(t1_plane.restore, path_arc=np.pi), Animation(arrow)) self.wait() def swap_necklace_allocation(self): for choices in [(1, 0, 1), (0, 1, 0)]: - self.play(*[ - ApplyMethod(group.move_to, group.target_points[i]) - for group, i in zip(self.groups, choices) - ]) + self.play( + *[ + ApplyMethod(group.move_to, group.target_points[i]) + for group, i in zip(self.groups, choices) + ] + ) self.wait() def add_sphere_arrow(self): up_down_arrow = TexMobject("\\updownarrow") up_down_arrow.scale(1.5) up_down_arrow.set_color(YELLOW) - up_down_arrow.next_to(self.necklace, DOWN, buff = LARGE_BUFF) + up_down_arrow.next_to(self.necklace, DOWN, buff=LARGE_BUFF) to_plane_arrow = Arrow( - up_down_arrow.get_bottom() + DOWN+RIGHT, - self.example_coords, - color = YELLOW + up_down_arrow.get_bottom() + DOWN + RIGHT, self.example_coords, color=YELLOW ) self.play(Write(up_down_arrow)) @@ -2372,75 +2333,87 @@ def get_fair_division_indices(self, *args): else: return TotalLengthOfEachJewelEquals.get_fair_division_indices(self, *args) + class JewelPairPlane(GraphScene): CONFIG = { - "camera_class" : ThreeDCamera, - "x_labeled_nums" : [], - "y_labeled_nums" : [], - "thief_number" : 1, - "colors" : [BLUE, GREEN], + "camera_class": ThreeDCamera, + "x_labeled_nums": [], + "y_labeled_nums": [], + "thief_number": 1, + "colors": [BLUE, GREEN], } + def construct(self): self.setup_axes() point = self.coords_to_point(4, 5) - dot = Dot(point, color = WHITE) + dot = Dot(point, color=WHITE) coord_pair = TexMobject( - "\\big(", - "\\text{Thief %d }"%self.thief_number, "X", ",", - "\\text{Thief %d }"%self.thief_number, "X", - "\\big)" + "\\big(", + "\\text{Thief %d }" % self.thief_number, + "X", + ",", + "\\text{Thief %d }" % self.thief_number, + "X", + "\\big)", ) # coord_pair.scale(1.5) to_replace = [coord_pair[i] for i in [2, 5]] for mob, color in zip(to_replace, self.colors): - jewel = Jewel(color = color) + jewel = Jewel(color=color) jewel.replace(mob) coord_pair.remove(mob) coord_pair.add(jewel) - coord_pair.next_to(dot, UP+RIGHT, buff = 0) + coord_pair.next_to(dot, UP + RIGHT, buff=0) self.example_coords = VGroup(dot, coord_pair) self.add(self.example_coords) + class WhatThisMappingActuallyLooksLikeWords(Scene): def construct(self): words = TextMobject("What this mapping actually looks like") - words.set_width(FRAME_WIDTH-1) + words.set_width(FRAME_WIDTH - 1) words.to_edge(DOWN) self.play(Write(words)) self.wait() + class WhatAboutGeneralCase(TeacherStudentsScene): def construct(self): - self.student_says(""" + self.student_says( + """ What about when there's more than 2 jewels? - """) + """ + ) self.change_student_modes("confused", None, "sassy") self.wait() self.play(self.get_teacher().change_mode, "thinking") self.wait() self.teacher_says( """Use Borsuk-Ulam for - higher-dimensional spheres """, - target_mode = "hooray" + higher-dimensional spheres """, + target_mode="hooray", ) - self.change_student_modes(*["confused"]*3) + self.change_student_modes(*["confused"] * 3) self.wait(2) + class Simple3DSpace(ExternallyAnimatedScene): pass + class FourDBorsukUlam(GeneralizeBorsukUlam, PiCreatureScene): CONFIG = { - "n_dims" : 4, - "use_morty" : False, + "n_dims": 4, + "use_morty": False, } + def setup(self): GeneralizeBorsukUlam.setup(self) PiCreatureScene.setup(self) - self.pi_creature.to_corner(DOWN+LEFT, buff = MED_SMALL_BUFF) + self.pi_creature.to_corner(DOWN + LEFT, buff=MED_SMALL_BUFF) def construct(self): sphere_set = self.get_sphere_set() @@ -2449,17 +2422,17 @@ def construct(self): output_space = self.get_output_space() equation = self.get_equation() - sphere_set.to_corner(UP+LEFT) + sphere_set.to_corner(UP + LEFT) arrow.next_to(sphere_set, RIGHT) f.next_to(arrow, UP) output_space.next_to(arrow, RIGHT) - equation.next_to(sphere_set, DOWN, buff = LARGE_BUFF) + equation.next_to(sphere_set, DOWN, buff=LARGE_BUFF) equation.to_edge(RIGHT) lhs = VGroup(*equation[:2]) eq = equation[2] rhs = VGroup(*equation[3:]) - brace = Brace(Line(ORIGIN, 5*RIGHT)) + brace = Brace(Line(ORIGIN, 5 * RIGHT)) brace.to_edge(RIGHT) brace_text = brace.get_text("Triplets of numbers") brace_text.shift_onto_screen() @@ -2467,31 +2440,19 @@ def construct(self): self.play(FadeIn(sphere_set)) self.change_mode("confused") self.wait() - self.play( - ShowCreation(arrow), - Write(f) - ) + self.play(ShowCreation(arrow), Write(f)) self.play(Write(output_space)) self.wait() self.change_mode("maybe") self.wait(2) self.change_mode("pondering") self.wait() - self.play( - GrowFromCenter(brace), - Write(brace_text) - ) + self.play(GrowFromCenter(brace), Write(brace_text)) self.wait() self.play(*list(map(FadeOut, [brace, brace_text]))) self.wait() - self.play( - FadeIn(lhs), - self.pi_creature.change_mode, "raise_right_hand" - ) - self.play( - ReplacementTransform(lhs.copy(), rhs), - Write(eq) - ) + self.play(FadeIn(lhs), self.pi_creature.change_mode, "raise_right_hand") + self.play(ReplacementTransform(lhs.copy(), rhs), Write(eq)) self.wait(2) def get_sphere_set(self): @@ -2501,43 +2462,36 @@ def get_sphere_set(self): sphere_set.add(brace, text) return sphere_set + class CircleToSphereToQMarks(Scene): def construct(self): pi_groups = VGroup() modes = ["happy", "pondering", "pleading"] - shapes = [ - Circle(color = BLUE, radius = 0.5), - VectorizedPoint(), - TexMobject("???") - ] + shapes = [Circle(color=BLUE, radius=0.5), VectorizedPoint(), TexMobject("???")] for d, mode, shape in zip(it.count(2), modes, shapes): - randy = Randolph(mode = mode) + randy = Randolph(mode=mode) randy.scale(0.7) - bubble = randy.get_bubble( - height = 3, width = 4, - direction = LEFT - ) + bubble = randy.get_bubble(height=3, width=4, direction=LEFT) bubble.pin_to(randy) bubble.position_mobject_inside(shape) - title = TextMobject("%dD"%d) + title = TextMobject("%dD" % d) title.next_to(randy, UP) arrow = Arrow(LEFT, RIGHT) - arrow.next_to(randy.get_corner(UP+RIGHT)) - pi_groups.add(VGroup( - randy, bubble, shape, title, arrow - )) + arrow.next_to(randy.get_corner(UP + RIGHT)) + pi_groups.add(VGroup(randy, bubble, shape, title, arrow)) pi_groups[-1].remove(pi_groups[-1][-1]) - pi_groups.arrange(buff = -1) + pi_groups.arrange(buff=-1) for mob in pi_groups: self.play(FadeIn(mob)) self.wait(2) self.play(pi_groups[-1][0].change_mode, "thinking") self.wait(2) + class BorsukPatreonThanks(PatreonThanks): CONFIG = { - "specific_patrons" : [ + "specific_patrons": [ "Ali Yahya", "Meshal Alshammari", "CrypticSwarm ", @@ -2568,33 +2522,33 @@ class BorsukPatreonThanks(PatreonThanks): ] } + class MortyLookingAtRectangle(Scene): def construct(self): morty = Mortimer() - morty.to_corner(DOWN+RIGHT) + morty.to_corner(DOWN + RIGHT) url = TextMobject("www.thegreatcoursesplus.com/3blue1brown") url.scale(0.75) - url.to_corner(UP+LEFT) - rect = Rectangle(height = 9, width = 16) + url.to_corner(UP + LEFT) + rect = Rectangle(height=9, width=16) rect.set_height(5) rect.next_to(url, DOWN) rect.shift_onto_screen() url.save_state() - url.next_to(morty.get_corner(UP+LEFT), UP) + url.next_to(morty.get_corner(UP + LEFT), UP) url.shift_onto_screen() self.add(morty) self.play( - morty.change_mode, "raise_right_hand", - morty.look_at, url, + morty.change_mode, + "raise_right_hand", + morty.look_at, + url, ) self.play(Write(url)) self.play(Blink(morty)) self.wait() - self.play( - url.restore, - morty.change_mode, "happy" - ) + self.play(url.restore, morty.change_mode, "happy") self.play(ShowCreation(rect)) self.wait() self.play(Blink(morty)) @@ -2604,32 +2558,35 @@ def construct(self): self.play(Blink(morty)) self.wait(2) + class RotatingThreeDSphereProjection(Scene): CONFIG = { - "camera_class" : ThreeDCamera, + "camera_class": ThreeDCamera, } + def construct(self): - sphere = VGroup(*[ - Circle(radius = np.sin(t)).shift(np.cos(t)*OUT) - for t in np.linspace(0, np.pi, 20) - ]) - sphere.set_stroke(BLUE, width = 2) + sphere = VGroup( + *[ + Circle(radius=np.sin(t)).shift(np.cos(t) * OUT) + for t in np.linspace(0, np.pi, 20) + ] + ) + sphere.set_stroke(BLUE, width=2) # sphere.set_fill(BLUE, opacity = 0.1) - self.play(Rotating( - sphere, axis = RIGHT+OUT, - run_time = 10 - )) + self.play(Rotating(sphere, axis=RIGHT + OUT, run_time=10)) self.repeat_frames(4) + class FourDSphereProjectTo4D(ExternallyAnimatedScene): pass class Test(Scene): CONFIG = { - "camera_class" : ThreeDCamera, + "camera_class": ThreeDCamera, } + def construct(self): randy = Randolph() necklace = Necklace() @@ -2641,16 +2598,3 @@ def construct(self): necklace.move_to(randy) self.add(randy, necklace) - - - - - - - - - - - - - diff --git a/from_3b1b/old/clacks/question.py b/from_3b1b/old/clacks/question.py index a3b61cb5cc..cbc4f0b3d8 100644 --- a/from_3b1b/old/clacks/question.py +++ b/from_3b1b/old/clacks/question.py @@ -15,7 +15,6 @@ class Block(Square): "stroke_width": 3, "stroke_color": WHITE, "fill_color": None, - "sheen_direction": UL, "sheen_factor": 0.5, "sheen_direction": UL, } @@ -90,7 +89,8 @@ def __init__(self, scene, **kwargs): self.mass_ratio = self.block2.mass / self.block1.mass self.phase_space_point_tracker = self.get_phase_space_point_tracker() self.add( - self.block1, self.block2, + self.block1, + self.block2, self.phase_space_point_tracker, ) self.add_updater(self.__class__.update_positions) @@ -101,8 +101,8 @@ def __init__(self, scene, **kwargs): def get_block(self, distance, **kwargs): block = Block(**kwargs) block.move_to( - self.floor.get_top()[1] * UP + - (self.wall.get_right()[0] + distance) * RIGHT, + self.floor.get_top()[1] * UP + + (self.wall.get_right()[0] + distance) * RIGHT, DL, ) return block @@ -112,17 +112,17 @@ def get_phase_space_point_tracker(self): w2 = block2.get_width() s1 = block1.get_left()[0] - self.wall.get_right()[0] - w2 s2 = block2.get_right()[0] - self.wall.get_right()[0] - w2 - result = VectorizedPoint([ - s1 * np.sqrt(block1.mass), - s2 * np.sqrt(block2.mass), - 0 - ]) - - result.velocity = np.array([ - np.sqrt(block1.mass) * block1.velocity, - np.sqrt(block2.mass) * block2.velocity, - 0 - ]) + result = VectorizedPoint( + [s1 * np.sqrt(block1.mass), s2 * np.sqrt(block2.mass), 0] + ) + + result.velocity = np.array( + [ + np.sqrt(block1.mass) * block1.velocity, + np.sqrt(block2.mass) * block2.velocity, + 0, + ] + ) return result def update_positions(self, dt): @@ -138,10 +138,7 @@ def update_blocks_from_phase_space_point_tracker(self): theta = np.arctan(np.sqrt(self.mass_ratio)) ps_point_angle = angle_of_vector(ps_point) n_clacks = int(ps_point_angle / theta) - reflected_point = rotate_vector( - ps_point, - -2 * np.ceil(n_clacks / 2) * theta - ) + reflected_point = rotate_vector(ps_point, -2 * np.ceil(n_clacks / 2) * theta) reflected_point = np.abs(reflected_point) shadow_wall_x = self.wall.get_right()[0] + block2.get_width() @@ -149,13 +146,11 @@ def update_blocks_from_phase_space_point_tracker(self): s1 = reflected_point[0] / np.sqrt(block1.mass) s2 = reflected_point[1] / np.sqrt(block2.mass) block1.move_to( - (shadow_wall_x + s1) * RIGHT + - floor_y * UP, + (shadow_wall_x + s1) * RIGHT + floor_y * UP, DL, ) block2.move_to( - (shadow_wall_x + s2) * RIGHT + - floor_y * UP, + (shadow_wall_x + s2) * RIGHT + floor_y * UP, DR, ) @@ -174,23 +169,16 @@ def get_clack_data(self): clack_data = [] for k in range(1, int(PI / theta) + 1): - clack_ps_point = np.array([ - y / np.tan(k * theta), - y, - 0 - ]) + clack_ps_point = np.array([y / np.tan(k * theta), y, 0]) time = get_norm(ps_point - clack_ps_point) / get_norm(ps_velocity) reflected_point = rotate_vector( - clack_ps_point, - -2 * np.ceil((k - 1) / 2) * theta + clack_ps_point, -2 * np.ceil((k - 1) / 2) * theta ) block2 = self.block2 s2 = reflected_point[1] / np.sqrt(block2.mass) - location = np.array([ - self.wall.get_right()[0] + s2, - block2.get_center()[1], - 0 - ]) + location = np.array( + [self.wall.get_right()[0] + s2, block2.get_center()[1], 0] + ) if k % 2 == 1: location += block2.get_width() * RIGHT clack_data.append((location, time)) @@ -238,9 +226,7 @@ def update(self, dt): if flash.start_time < time < flash.end_time: if flash.mobject not in self.submobjects: self.add(flash.mobject) - flash.update( - (time - flash.start_time) / flash.run_time - ) + flash.update((time - flash.start_time) / flash.run_time) else: if flash.mobject in self.submobjects: self.remove(flash.mobject) @@ -264,10 +250,12 @@ def __init__(self, height, **kwargs): def get_ticks(self): n_lines = int(self.height / self.tick_spacing) - lines = VGroup(*[ - Line(ORIGIN, self.tick_length * UR).shift(n * self.tick_spacing * UP) - for n in range(n_lines) - ]) + lines = VGroup( + *[ + Line(ORIGIN, self.tick_length * UR).shift(n * self.tick_spacing * UP) + for n in range(n_lines) + ] + ) lines.set_style(**self.tick_style) lines.move_to(self, DR) return lines @@ -324,7 +312,8 @@ def add_counter(self): counter_label = TextMobject(self.counter_label) counter_mob = Integer(self.n_clacks) counter_mob.next_to( - counter_label[-1], RIGHT, + counter_label[-1], + RIGHT, ) counter_mob.align_to(counter_label[-1][-1], DOWN) counter_group = VGroup( @@ -358,11 +347,7 @@ def update_num_clacks(self, n_clacks): def add_clack_sounds(self, clack_data): clack_file = self.collision_sound total_time = self.get_time() - times = [ - time - for location, time in clack_data - if time < total_time - ] + times = [time for location, time in clack_data if time < total_time] last_time = 0 for time in times: d_time = time - last_time @@ -380,6 +365,7 @@ def tear_down(self): if self.include_sound: self.add_clack_sounds(self.clack_data) + # Animated scenes @@ -402,7 +388,9 @@ def construct(self): self.play( Flash(blue.get_right(), run_time=flash_time), ApplyMethod( - blue.to_edge, LEFT, {"buff": 0}, + blue.to_edge, + LEFT, + {"buff": 0}, rate_func=linear, ), ) @@ -413,13 +401,15 @@ def construct(self): self.play( Flash(blue.get_right(), run_time=flash_time), ApplyMethod( - brown.to_edge, RIGHT, {"buff": 0}, + brown.to_edge, + RIGHT, + {"buff": 0}, rate_func=linear, - ) + ), ) self.play( Flash(brown.get_right(), run_time=flash_time), - Restore(brown, rate_func=linear) + Restore(brown, rate_func=linear), ) @@ -448,7 +438,8 @@ def construct(self): self.play( LaggedStartMap( - FadeInFromDown, to_fade, + FadeInFromDown, + to_fade, lag_ratio=0.7, run_time=3, ), @@ -475,10 +466,7 @@ def get_tangent_image(self): line.set_color(WHITE) one = TexMobject("1").scale(0.5) one.next_to(line.point_from_proportion(0.7), UL, 0.5 * SMALL_BUFF) - tan_line = Line( - line.get_end(), - (1.0 / np.cos(theta)) * RIGHT - ) + tan_line = Line(line.get_end(), (1.0 / np.cos(theta)) * RIGHT) tan_line.set_color(RED) tan_text = TexMobject("\\tan(\\theta)") tan_text.rotate(tan_line.get_angle()) @@ -488,10 +476,14 @@ def get_tangent_image(self): tan_text.shift(0.2 * normalize(line.get_vector())) result = VGroup( - axes, circle, - line, one, - arc, theta_label, - tan_line, tan_text, + axes, + circle, + line, + one, + arc, + theta_label, + tan_line, + tan_text, ) result.set_height(4) return result @@ -516,14 +508,20 @@ def construct(self): beam_height = self.beam_height start_point = self.beam_start_x * RIGHT + beam_height * UP - points = [start_point] + [ - np.array([ - (beam_height / np.tan(k * theta)), - beam_height, - 0, - ]) - for k in range(1, int(PI / theta)) - ] + [rotate(start_point, PI, UP)] + points = ( + [start_point] + + [ + np.array( + [ + (beam_height / np.tan(k * theta)), + beam_height, + 0, + ] + ) + for k in range(1, int(PI / theta)) + ] + + [rotate(start_point, PI, UP)] + ) reflected_points = [] for k, point in enumerate(points): reflected_point = rotate_vector(point, -2 * (k // 2) * theta) @@ -561,10 +559,7 @@ def get_beam_anim(self, beam): rate_func=lambda t: smooth(t, 5), time_width=0.05, ), - UpdateFromFunc( - dot, - lambda m: m.move_to(beam.points[-1]) - ), + UpdateFromFunc(dot, lambda m: m.move_to(beam.points[-1])), ) @@ -599,15 +594,10 @@ class TwoBlocksLabel(Scene): def construct(self): label = TextMobject("Two sliding \\\\ blocks") label.to_edge(UP) - arrows = VGroup(*[ - Arrow(label.get_bottom(), point) - for point in [RIGHT, LEFT] - ]) + arrows = VGroup(*[Arrow(label.get_bottom(), point) for point in [RIGHT, LEFT]]) arrows.set_color(RED) self.play( - Write(label), - LaggedStartMap(GrowArrow, arrows, lag_ratio=0.7), - run_time=1 + Write(label), LaggedStartMap(GrowArrow, arrows, lag_ratio=0.7), run_time=1 ) self.wait() @@ -619,10 +609,7 @@ def construct(self): word = TextMobject("Wall") word.rotate(-90 * DEGREES) word.next_to(wall, RIGHT, MED_SMALL_BUFF) - self.play( - Write(word), - ShowPassingFlash(wall) - ) + self.play(Write(word), ShowPassingFlash(wall)) self.wait() @@ -634,9 +621,7 @@ class NoFrictionLabel(Scene): def construct(self): words = TextMobject("Frictionless") words.shift(2 * RIGHT) - words.add_updater( - lambda m, dt: m.shift(dt * LEFT) - ) + words.add_updater(lambda m, dt: m.shift(dt * LEFT)) self.play(VFadeIn(words)) self.wait(2) @@ -647,17 +632,11 @@ class Mass1e1WithElasticLabel(BlocksAndWallExampleMass1e1): def add_flash_animations(self): super().add_flash_animations() flashes = self.clack_flashes - label = TextMobject( - "Purely elastic collisions\\\\" - "(no energy lost)" - ) + label = TextMobject("Purely elastic collisions\\\\" "(no energy lost)") label.set_color(YELLOW) label.move_to(2 * LEFT + 2 * UP) self.add(label) - self.add(*[ - self.get_arrow(label, flashes, flash) - for flash in flashes.flashes - ]) + self.add(*[self.get_arrow(label, flashes, flash) for flash in flashes.flashes]) def get_arrow(self, label, clack_flashes, flash): arrow = Arrow( @@ -689,17 +668,13 @@ def set_opacity(arrow): class AskAboutSoundlessness(TeacherStudentsScene): def construct(self): - self.student_says( - "No sound,\\\\right?" - ) + self.student_says("No sound,\\\\right?") self.play(self.teacher.change, "guilty") self.wait(2) self.teacher_says( "Focus on \\\\ collisions", target_mode="speaking", - added_anims=[ - self.get_student_changes("pondering", "confused", "thinking") - ] + added_anims=[self.get_student_changes("pondering", "confused", "thinking")], ) self.look_at(self.screen) self.wait(3) @@ -731,8 +706,7 @@ def construct(self): morty.set_color(GREY_BROWN) self.pi_creature_says( - "What will\\\\" - "happen?", + "What will\\\\" "happen?", target_mode="maybe", look_at_arg=4 * DR, ) @@ -755,9 +729,9 @@ class BlocksAndWallExampleMassSameSpeedAtEnd(BlocksAndWallExample): CONFIG = { "sliding_blocks_config": { "block1_config": { - "mass": 1 / np.tan(PI / 5)**2, + "mass": 1 / np.tan(PI / 5) ** 2, "velocity": -1, - "label_text": "$\\frac{1}{\\tan(\\pi / 5)^2}$ kg" + "label_text": "$\\frac{1}{\\tan(\\pi / 5)^2}$ kg", } }, "wait_time": 25, @@ -779,11 +753,7 @@ class BlocksAndWallExampleMass1e4(BlocksAndWallExample): class BlocksAndWallExampleMass1e4SlowMo(BlocksAndWallExample): CONFIG = { "sliding_blocks_config": { - "block1_config": { - "mass": 1e4, - "velocity": -0.1, - "distance": 4.1 - }, + "block1_config": {"mass": 1e4, "velocity": -0.1, "distance": 4.1}, }, "wait_time": 50, "collision_sound": "slow_clack.wav", @@ -813,11 +783,7 @@ class BlocksAndWallExampleMass1e6(BlocksAndWallExample): class BlocksAndWallExampleMass1e6SlowMo(BlocksAndWallExample): CONFIG = { "sliding_blocks_config": { - "block1_config": { - "mass": 1e6, - "velocity": -0.1, - "distance": 4.1 - }, + "block1_config": {"mass": 1e6, "velocity": -0.1, "distance": 4.1}, }, "wait_time": 60, "collision_sound": "slow_clack.wav", @@ -855,20 +821,20 @@ def construct(self): nd = self.n_digits pow10 = int(10**nd) rounded_pi = int(pow10 * PI) / pow10 - equation = TexMobject( - ("\\pi = {:." + str(nd) + "f}...").format(rounded_pi) - ) + equation = TexMobject(("\\pi = {:." + str(nd) + "f}...").format(rounded_pi)) equation.set_color(YELLOW) pi_creature = Randolph(color=YELLOW) pi_creature.match_width(equation[0]) pi_creature.scale(1.4) pi_creature.move_to(equation[0], DOWN) self.add(pi_creature, equation[1]) - self.play(ShowIncreasingSubsets( - equation[2:], - rate_func=linear, - run_time=1, - )) + self.play( + ShowIncreasingSubsets( + equation[2:], + rate_func=linear, + run_time=1, + ) + ) self.play(Blink(pi_creature)) self.wait() @@ -891,7 +857,7 @@ def setup_axes(self): axis_config={ "tick_frequency": 100, "numbers_with_elongated_ticks": [], - } + }, ) y_label = TextMobject("Efficiency") @@ -929,11 +895,14 @@ def add_methods(self): method.shift_onto_screen() algorithms.add(VGroup(method, cross)) - self.play(LaggedStartMap( - FadeInFromDown, algorithms, - run_time=4, - lag_ratio=0.4, - )) + self.play( + LaggedStartMap( + FadeInFromDown, + algorithms, + run_time=4, + lag_ratio=0.4, + ) + ) self.wait() self.play(ShowCreationThenFadeAround(algorithms[-1][0])) @@ -966,8 +935,7 @@ def get_monte_carlo(self): def get_basel(self): formula = TexMobject( - "\\frac{\\pi^2}{6} = " - "\\sum_{n=1}^\\infty \\frac{1}{n^2}" + "\\frac{\\pi^2}{6} = " "\\sum_{n=1}^\\infty \\frac{1}{n^2}" ) formula.scale(0.5) return formula @@ -981,19 +949,16 @@ def get_blocks_image(self): wall_x=0, n_wall_ticks=6, sliding_blocks_config={ - "block1_config": { - "mass": 1e2, - "velocity": -0.01, - "distance": 3.5 - }, + "block1_config": {"mass": 1e2, "velocity": -0.01, "distance": 3.5}, "block2_config": { "distance": 1, "velocity": 0, }, - } + }, ) group = VGroup( - scene.wall, scene.floor, + scene.wall, + scene.floor, scene.blocks.block1, scene.blocks.block2, ) @@ -1013,22 +978,21 @@ def construct(self): steps.scale(0.8) for step in steps: - self.play( - FadeInFromDown(step[0]), - self.teacher.change, "raise_right_hand" - ) + self.play(FadeInFromDown(step[0]), self.teacher.change, "raise_right_hand") self.play( Write(step[1], run_time=2), self.get_student_changes( *["pondering"] * 3, look_at_arg=step, - ) + ), ) self.wait() self.change_student_modes( - "sassy", "erm", "confused", + "sassy", + "erm", + "confused", look_at_arg=steps, - added_anims=[self.teacher.change, "happy"] + added_anims=[self.teacher.change, "happy"], ) self.wait(3) @@ -1038,12 +1002,10 @@ def get_steps(self): TextMobject( "Step 2:", "Choose the number of digits, $d$,\\\\" - "of $\\pi$ that you want to compute" + "of $\\pi$ that you want to compute", ), TextMobject( - "Step 3:", - "Set one mass to $100^{d - 1}$,\\\\" - "the other to $1$" + "Step 3:", "Set one mass to $100^{d - 1}$,\\\\" "the other to $1$" ), TextMobject("Step 4:", "Count collisions"), ) @@ -1099,10 +1061,12 @@ def add_digits_of_pi(self): digits.set_width(FRAME_WIDTH - 3) digits.to_edge(UP) - highlighted_digits = VGroup(*[ - d.copy().set_background_stroke(color=BLUE, width=5) - for d in [digits[0], *digits[2:-3]] - ]) + highlighted_digits = VGroup( + *[ + d.copy().set_background_stroke(color=BLUE, width=5) + for d in [digits[0], *digits[2:-3]] + ] + ) counter = Integer(0) counter.scale(1.5) counter.set_color(BLUE) @@ -1117,7 +1081,7 @@ def add_digits_of_pi(self): self.add(digits[k + 1]) self.add(highlighted_digits[k]) counter.increment_value() - brace.become(Brace(highlighted_digits[:k + 1], DOWN)) + brace.become(Brace(highlighted_digits[: k + 1], DOWN)) counter.next_to(brace, DOWN) self.wait(0.1) self.add(digits) @@ -1141,9 +1105,7 @@ def add_digits_of_pi(self): ) self.wait() - self.pi_digits_group = VGroup( - digits, brace, counter, digits_word - ) + self.pi_digits_group = VGroup(digits, brace, counter, digits_word) def show_mass(self): bw_scene = BlocksAndWallExample( @@ -1166,14 +1128,11 @@ def show_mass(self): "distance": 1, "velocity": 0, }, - } + }, ) block1 = bw_scene.blocks.block1 block2 = bw_scene.blocks.block2 - group = VGroup( - bw_scene.wall, bw_scene.floor, - block1, block2 - ) + group = VGroup(bw_scene.wall, bw_scene.floor, block1, block2) group.center() group.to_edge(DOWN) @@ -1183,7 +1142,8 @@ def show_mass(self): brace = Brace(block1.label[:-2], UP, buff=SMALL_BUFF) number_words = TextMobject( - "100", *["billion"] * 4, + "100", + *["billion"] * 4, ) number_words.next_to(brace, UP, buff=SMALL_BUFF) VGroup(brace, number_words).set_color(YELLOW) @@ -1217,9 +1177,7 @@ def show_galactic_black_holes(self): black_hole.set_color(BLACK) black_hole.set_sheen(0.2, UL) black_hole.set_height(1) - black_holes = VGroup(*[ - black_hole.copy() for k in range(10) - ]) + black_holes = VGroup(*[black_hole.copy() for k in range(10)]) black_holes.arrange_in_grid(5, 2) black_holes.to_corner(DR) random.shuffle(black_holes.submobjects) @@ -1239,10 +1197,7 @@ def show_galactic_black_holes(self): self.play( Write(equals), Write(words), - LaggedStartMap( - Restore, black_holes, - run_time=3 - ) + LaggedStartMap(Restore, black_holes, run_time=3), ) self.wait() @@ -1257,16 +1212,10 @@ def show_total_count(self): number.scale(1.5) number.to_edge(UP) - commas = VGroup(*[ - mob - for c, mob in zip(tex_string, number) - if c is "," - ]) - dots = VGroup(*[ - mob - for c, mob in zip(digits.get_tex_string(), digits) - if c is "." - ]) + commas = VGroup(*[mob for c, mob in zip(tex_string, number) if c is ","]) + dots = VGroup( + *[mob for c, mob in zip(digits.get_tex_string(), digits) if c is "."] + ) self.play(FadeOut(to_fade)) self.play( @@ -1274,16 +1223,12 @@ def show_total_count(self): VGroup(*filter(lambda m: m not in dots, digits)), VGroup(*filter(lambda m: m not in commas, number)), ), - ReplacementTransform( - dots, commas, - lag_ratio=0.5, - run_time=2 - ) + ReplacementTransform(dots, commas, lag_ratio=0.5, run_time=2), ) group0 = number[:2].copy() - group1 = number[3:3 + 9 + 2].copy() - group2 = number[-(9 + 2):].copy() + group1 = number[3 : 3 + 9 + 2].copy() + group2 = number[-(9 + 2) :].copy() for group in group0, group1, group2: group.set_background_stroke(color=BLUE, width=5) self.add(group) @@ -1308,9 +1253,7 @@ class BlocksAndWallExampleGalacticMass(BlocksAndWallExample): def setup(self): super().setup() - words = TextMobject( - "Burst of $10^{38}$ clacks per second" - ) + words = TextMobject("Burst of $10^{38}$ clacks per second") words.scale(1.5) words.to_edge(UP) self.add(words) @@ -1325,7 +1268,8 @@ def construct(self): this.next_to(physics) self.add(physics, this) self.play( - this.shift, FRAME_WIDTH * RIGHT, + this.shift, + FRAME_WIDTH * RIGHT, rate_func=rush_into, run_time=3, ) @@ -1336,9 +1280,7 @@ class CompareAlgorithmToPhysics(PiCreatureScene): def construct(self): morty = self.pi_creature right_pic = ImageMobject( - self.get_image_file_path().replace( - str(self), "PiComputingAlgorithmsAxes" - ) + self.get_image_file_path().replace(str(self), "PiComputingAlgorithmsAxes") ) right_rect = SurroundingRectangle(right_pic, buff=0, color=WHITE) right_pic.add(right_rect) @@ -1352,12 +1294,14 @@ def construct(self): self.play( FadeInFromDown(right_pic), - morty.change, "raise_right_hand", + morty.change, + "raise_right_hand", ) self.wait() self.play( FadeInFromDown(left_rect), - morty.change, "raise_left_hand", + morty.change, + "raise_left_hand", ) self.wait() @@ -1369,18 +1313,20 @@ def construct(self): # FadeOutAndShift(left_rect, 5 * LEFT), FadeOut(left_rect), PiCreatureBubbleIntroduction( - morty, "This doesn't seem \\\\ like me...", + morty, + "This doesn't seem \\\\ like me...", bubble_class=ThoughtBubble, bubble_kwargs={"direction": LEFT}, target_mode="pondering", look_at_arg=left_rect, ), LaggedStartMap( - FadeInFrom, digits, + FadeInFrom, + digits, lambda m: (m, LEFT), run_time=5, lag_ratio=0.2, - ) + ), ) self.blink() self.wait() @@ -1396,29 +1342,31 @@ def construct(self): circle = Circle(radius=2, color=YELLOW) circle.next_to(self.teacher, UL) ke_conservation = TexMobject( - "\\frac{1}{2}m_1 v_1^2 + " - "\\frac{1}{2}m_2 v_2^2 = \\text{const.}" + "\\frac{1}{2}m_1 v_1^2 + " "\\frac{1}{2}m_2 v_2^2 = \\text{const.}" ) ke_conservation.move_to(circle) self.student_says("But why?") self.change_student_modes( - "erm", "raise_left_hand", "sassy", - added_anims=[self.teacher.change, "happy"] + "erm", + "raise_left_hand", + "sassy", + added_anims=[self.teacher.change, "happy"], ) self.wait() self.play( ShowCreation(circle), RemovePiCreatureBubble(self.students[1]), - self.teacher.change, "raise_right_hand", - ) - self.change_all_student_modes( - "pondering", look_at_arg=circle + self.teacher.change, + "raise_right_hand", ) + self.change_all_student_modes("pondering", look_at_arg=circle) self.wait(2) self.play( Write(ke_conservation), - circle.stretch, 1.5, 0, + circle.stretch, + 1.5, + 0, ) self.change_all_student_modes("confused") self.look_at(circle) @@ -1462,9 +1410,7 @@ def construct(self): dots.scale(2) dots.move_to(videos) - mid_words = TextMobject( - "Patient\\\\", "problem\\\\", "solving" - ) + mid_words = TextMobject("Patient\\\\", "problem\\\\", "solving") mid_words.next_to(dots, DOWN) randy = Randolph(height=1) randy.next_to(dots, UP, SMALL_BUFF) @@ -1474,11 +1420,7 @@ def construct(self): speech_bubble = SpeechBubble(height=2, width=2) speech_bubble.pin_to(randy) speech_bubble.write("What do \\\\ you think?") - friends = VGroup( - PiCreature(color=BLUE_E), - PiCreature(color=BLUE_C), - Mortimer() - ) + friends = VGroup(PiCreature(color=BLUE_E), PiCreature(color=BLUE_C), Mortimer()) friends.set_height(1) friends.arrange(RIGHT, buff=MED_SMALL_BUFF) friends[:2].next_to(randy, LEFT) @@ -1493,13 +1435,16 @@ def construct(self): self.wait() self.play( LaggedStartMap( - FadeInFrom, mid_words, + FadeInFrom, + mid_words, lambda m: (m, UP), lag_ratio=0.8, ), - randy.change, "pondering", + randy.change, + "pondering", VFadeIn(randy), - videos.space_out_submobjects, 1.3, + videos.space_out_submobjects, + 1.3, ) self.play(ShowCreation(thought_bubble)) self.play(Blink(randy)) @@ -1507,13 +1452,17 @@ def construct(self): Uncreate(thought_bubble), ShowCreation(speech_bubble), Write(speech_bubble.content), - randy.change, "maybe", friends[0].eyes, + randy.change, + "maybe", + friends[0].eyes, LaggedStartMap(FadeInFromDown, friends), - videos.space_out_submobjects, 1.6, + videos.space_out_submobjects, + 1.6, ) self.play( LaggedStartMap( - ApplyMethod, friends, + ApplyMethod, + friends, lambda m: (m.change, "pondering"), run_time=1, lag_ratio=0.7, @@ -1533,17 +1482,19 @@ def construct(self): height=height, ) video_rect.shift(UP) - video_rects = VGroup(*[ - video_rect.copy().set_color(color) - for color in [BLUE_E, BLUE_C, BLUE_D, GREY_BROWN] - ]) + video_rects = VGroup( + *[ + video_rect.copy().set_color(color) + for color in [BLUE_E, BLUE_C, BLUE_D, GREY_BROWN] + ] + ) for rect in video_rects[1::2]: rect.reverse_points() video_rect.set_fill(DARK_GREY, 0.5) video_rect.set_stroke(GREY_BROWN, 0.5) date = TextMobject( - "Solution will be\\\\" - "posted", "1/20/19", + "Solution will be\\\\" "posted", + "1/20/19", ) date[1].set_color(YELLOW) date.set_width(video_rect.get_width() - 2 * MED_SMALL_BUFF) @@ -1612,21 +1563,14 @@ def grow_labels(self): def add_vector(self): blocks = self.blocks arrow = self.arrow = Vector( - 2.5 * LEFT, - color=RED, - rectangular_stem_width=1.5, - tip_length=0.5 + 2.5 * LEFT, color=RED, rectangular_stem_width=1.5, tip_length=0.5 ) arrow.move_to(blocks.block1.get_center(), RIGHT) - arrow.add_to_back( - arrow.copy().set_stroke(GREY, 5) - ) + arrow.add_to_back(arrow.copy().set_stroke(GREY, 5)) self.add(arrow) def add_text(self): - question = self.question = TextMobject( - "How many\\\\collisions?" - ) + question = self.question = TextMobject("How many\\\\collisions?") question.scale(2.5) question.to_edge(UP) question.set_color(YELLOW) diff --git a/from_3b1b/old/eola/chapter5.py b/from_3b1b/old/eola/chapter5.py index 60e5b6078d..e834e45c34 100644 --- a/from_3b1b/old/eola/chapter5.py +++ b/from_3b1b/old/eola/chapter5.py @@ -4,81 +4,89 @@ class Blob(Circle): CONFIG = { - "stroke_color" : TEAL, - "fill_color" : BLUE_E, - "fill_opacity" : 1, - "random_seed" : 1, - "random_nudge_size" : 0.5, - "height" : 2, + "stroke_color": TEAL, + "fill_color": BLUE_E, + "fill_opacity": 1, + "random_seed": 1, + "random_nudge_size": 0.5, + "height": 2, } + def __init__(self, **kwargs): Circle.__init__(self, **kwargs) random.seed(self.random_seed) self.apply_complex_function( - lambda z : z*(1+self.random_nudge_size*(random.random()-0.5)) + lambda z: z * (1 + self.random_nudge_size * (random.random() - 0.5)) ) self.set_height(self.height).center() def probably_contains(self, point): border_points = np.array(self.get_anchors_and_handles()[0]) - distances = [get_norm(p-point) for p in border_points] + distances = [get_norm(p - point) for p in border_points] min3 = border_points[np.argsort(distances)[:3]] center_direction = self.get_center() - point - in_center_direction = [np.dot(p-point, center_direction) > 0 for p in min3] + in_center_direction = [np.dot(p - point, center_direction) > 0 for p in min3] return sum(in_center_direction) <= 2 - + + class RightHand(VMobject): def __init__(self, **kwargs): hand = SVGMobject("RightHandOutline") self.inlines = VMobject(*hand.split()[:-4]) self.outline = VMobject(*hand.split()[-4:]) - self.outline.set_stroke(color = WHITE, width = 5) - self.inlines.set_stroke(color = DARK_GREY, width = 3) + self.outline.set_stroke(color=WHITE, width=5) + self.inlines.set_stroke(color=DARK_GREY, width=3) VMobject.__init__(self, self.outline, self.inlines) self.center().set_height(3) + class OpeningQuote(Scene): def construct(self): - words = TextMobject([ - "``The purpose of computation is \\\\", - "insight", - ", not ", - "numbers.", - "''", - ], arg_separator = "") + words = TextMobject( + [ + "``The purpose of computation is \\\\", + "insight", + ", not ", + "numbers.", + "''", + ], + arg_separator="", + ) # words.set_width(FRAME_WIDTH - 2) words.to_edge(UP) words.split()[1].set_color(BLUE) words.split()[3].set_color(GREEN) author = TextMobject("-Richard Hamming") author.set_color(YELLOW) - author.next_to(words, DOWN, buff = 0.5) + author.next_to(words, DOWN, buff=0.5) self.play(FadeIn(words)) self.wait(2) - self.play(Write(author, run_time = 3)) + self.play(Write(author, run_time=3)) self.wait() + class MovingForward(TeacherStudentsScene): def construct(self): self.setup() student = self.get_students()[1] - bubble = student.get_bubble(direction = RIGHT, width = 5) - bubble.rotate(-np.pi/12) - bubble.next_to(student, UP, aligned_edge = RIGHT) - bubble.shift(0.5*LEFT) + bubble = student.get_bubble(direction=RIGHT, width=5) + bubble.rotate(-np.pi / 12) + bubble.next_to(student, UP, aligned_edge=RIGHT) + bubble.shift(0.5 * LEFT) bubble.make_green_screen() - self.teacher_says(""" + self.teacher_says( + """ Y'all know about linear transformations, right? - """, width = 7) - self.play( - ShowCreation(bubble), - student.change_mode, "pondering" + """, + width=7, ) + self.play(ShowCreation(bubble), student.change_mode, "pondering") self.wait(2) + class StretchingTransformation(LinearTransformationScene): def construct(self): self.setup() @@ -86,43 +94,46 @@ def construct(self): self.apply_transposed_matrix([[3, 1], [-1, 2]]) self.wait() + class SquishingTransformation(LinearTransformationScene): CONFIG = { - "foreground_plane_kwargs" : { - "x_radius" : 3*FRAME_X_RADIUS, - "y_radius" : 3*FRAME_X_RADIUS, - "secondary_line_ratio" : 0 + "foreground_plane_kwargs": { + "x_radius": 3 * FRAME_X_RADIUS, + "y_radius": 3 * FRAME_X_RADIUS, + "secondary_line_ratio": 0, }, } + def construct(self): self.setup() self.add_title("Generally squishes space") - self.apply_transposed_matrix([[1./2, -0.5], [1, 1./3]]) + self.apply_transposed_matrix([[1.0 / 2, -0.5], [1, 1.0 / 3]]) self.wait() + class AskAboutStretching(LinearTransformationScene): def construct(self): self.setup() - words = TextMobject(""" + words = TextMobject( + """ Exactly how much are things being stretched? - """) + """ + ) words.add_background_rectangle() - words.to_corner(UP+RIGHT) + words.to_corner(UP + RIGHT) words.set_color(YELLOW) - self.apply_transposed_matrix( - [[2, 1], [-1, 3]], - added_anims = [Write(words)] - ) + self.apply_transposed_matrix([[2, 1], [-1, 3]], added_anims=[Write(words)]) self.wait() + class AskAboutStretchingSpecifically(LinearTransformationScene): def construct(self): self.setup() self.add_title(["How much are", "areas", "scaled?"]) hma, areas, scaled = self.title.split()[1].split() areas.set_color(YELLOW) - blob = Blob().shift(UP+RIGHT) + blob = Blob().shift(UP + RIGHT) label = TextMobject("Area") label.set_color(YELLOW) @@ -139,18 +150,21 @@ def construct(self): self.play(ShowCreation(arrow)) self.wait() + class BeautyNowUsesLater(TeacherStudentsScene): def construct(self): self.setup() self.teacher_says("Beauty now, uses later") self.wait() + class DiagonalExample(LinearTransformationScene): CONFIG = { - "show_square" : False, - "show_coordinates" : True, - "transposed_matrix" : [[3, 0], [0, 2]] + "show_square": False, + "show_coordinates": True, + "transposed_matrix": [[3, 0], [0, 2]], } + def construct(self): self.setup() matrix = Matrix(np.array(self.transposed_matrix).transpose()) @@ -159,7 +173,7 @@ def construct(self): matrix_background = BackgroundRectangle(matrix) self.play(ShowCreation(matrix_background), Write(matrix)) if self.show_square: - self.add_unit_square(animate = True) + self.add_unit_square(animate=True) self.add_foreground_mobject(matrix_background, matrix) self.wait() self.apply_transposed_matrix([self.transposed_matrix[0], [0, 1]]) @@ -167,7 +181,6 @@ def construct(self): self.wait() if self.show_square: - bottom_brace = Brace(self.i_hat, DOWN) right_brace = Brace(self.square, RIGHT) width = TexMobject(str(self.transposed_matrix[0][0])) @@ -176,83 +189,82 @@ def construct(self): height.next_to(right_brace, RIGHT) for mob in bottom_brace, width, right_brace, height: mob.add_background_rectangle() - self.play(Write(mob, run_time = 0.5)) + self.play(Write(mob, run_time=0.5)) self.wait() width_target, height_target = width.copy(), height.copy() det = np.linalg.det(self.transposed_matrix) - times, eq_det = list(map(TexMobject, ["\\times", "=%d"%det])) + times, eq_det = list(map(TexMobject, ["\\times", "=%d" % det])) words = TextMobject("New area $=$") - equation = VMobject( - words, width_target, times, height_target, eq_det - ) - equation.arrange(RIGHT, buff = 0.2) - equation.next_to(self.square, UP, aligned_edge = LEFT) - equation.shift(0.5*RIGHT) + equation = VMobject(words, width_target, times, height_target, eq_det) + equation.arrange(RIGHT, buff=0.2) + equation.next_to(self.square, UP, aligned_edge=LEFT) + equation.shift(0.5 * RIGHT) background_rect = BackgroundRectangle(equation) self.play( - ShowCreation(background_rect), + ShowCreation(background_rect), Transform(width.copy(), width_target), Transform(height.copy(), height_target), *list(map(Write, [words, times, eq_det])) ) self.wait() + class DiagonalExampleWithSquare(DiagonalExample): - CONFIG = { - "show_square" : True - } + CONFIG = {"show_square": True} + class ShearExample(DiagonalExample): CONFIG = { - "show_square" : False, - "show_coordinates" : True, - "transposed_matrix" : [[1, 0], [1, 1]] + "show_square": False, + "show_coordinates": True, + "transposed_matrix": [[1, 0], [1, 1]], } + class ShearExampleWithSquare(DiagonalExample): CONFIG = { - "show_square" : True, - "show_coordinates" : True, - "show_coordinates" : False, - "transposed_matrix" : [[1, 0], [1, 1]] + "show_square": True, + "show_coordinates": False, + "transposed_matrix": [[1, 0], [1, 1]], } + class ThisSquareTellsEverything(LinearTransformationScene): def construct(self): self.setup() self.add_unit_square() - words = TextMobject(""" + words = TextMobject( + """ This square gives you everything you need. - """) - words.to_corner(UP+RIGHT) + """ + ) + words.to_corner(UP + RIGHT) words.set_color(YELLOW) words.add_background_rectangle() - arrow = Arrow( - words.get_bottom(), self.square.get_right(), - color = WHITE - ) + arrow = Arrow(words.get_bottom(), self.square.get_right(), color=WHITE) - self.play(Write(words, run_time = 2)) + self.play(Write(words, run_time=2)) self.play(ShowCreation(arrow)) self.add_foreground_mobject(words, arrow) self.wait() self.apply_transposed_matrix([[1.5, -0.5], [1, 1.5]]) self.wait() + class WhatHappensToOneSquareHappensToAll(LinearTransformationScene): def construct(self): self.setup() self.add_unit_square() pairs = [ - (2*RIGHT+UP, 1), - (3*LEFT, 2), - (2*LEFT+DOWN, 0.5), - (3.5*RIGHT+2.5*UP, 1.5), - (RIGHT+2*DOWN, 0.25), - (3*LEFT+3*DOWN, 1), + (2 * RIGHT + UP, 1), + (3 * LEFT, 2), + (2 * LEFT + DOWN, 0.5), + (3.5 * RIGHT + 2.5 * UP, 1.5), + (RIGHT + 2 * DOWN, 0.25), + (3 * LEFT + 3 * DOWN, 1), ] squares = VMobject() for position, side_length in pairs: @@ -260,75 +272,70 @@ def construct(self): square.scale(side_length) square.shift(position) squares.add(square) - self.play(FadeIn( - squares, lag_ratio = 0.5, - run_time = 3 - )) + self.play(FadeIn(squares, lag_ratio=0.5, run_time=3)) self.add_transformable_mobject(squares) self.apply_transposed_matrix([[1, -1], [0.5, 1]]) self.wait() + class BreakBlobIntoGridSquares(LinearTransformationScene): CONFIG = { - "square_size" : 0.5, - "blob_height" : 3, + "square_size": 0.5, + "blob_height": 3, } + def construct(self): self.setup() blob = Blob( - height = self.blob_height, - random_seed = 5, - random_nudge_size = 0.2, + height=self.blob_height, + random_seed=5, + random_nudge_size=0.2, ) - blob.next_to(ORIGIN, UP+RIGHT) + blob.next_to(ORIGIN, UP + RIGHT) self.add_transformable_mobject(blob) - arange = np.arange( - 0, self.blob_height + self.square_size, - self.square_size - ) - square = Square(side_length = self.square_size) - square.set_stroke(YELLOW, width = 2) - square.set_fill(YELLOW, opacity = 0.3) + arange = np.arange(0, self.blob_height + self.square_size, self.square_size) + square = Square(side_length=self.square_size) + square.set_stroke(YELLOW, width=2) + square.set_fill(YELLOW, opacity=0.3) squares = VMobject() - for x, y in it.product(*[arange]*2): - point = x*RIGHT + y*UP + for x, y in it.product(*[arange] * 2): + point = x * RIGHT + y * UP if blob.probably_contains(point): squares.add(square.copy().shift(point)) - self.play(ShowCreation( - squares, lag_ratio = 0.5, - run_time = 2, - )) + self.play( + ShowCreation( + squares, + lag_ratio=0.5, + run_time=2, + ) + ) self.add_transformable_mobject(squares) self.wait() self.apply_transposed_matrix([[1, -1], [0.5, 1]]) self.wait() + class BreakBlobIntoGridSquaresGranular(BreakBlobIntoGridSquares): - CONFIG = { - "square_size" : 0.25 - } + CONFIG = {"square_size": 0.25} + class BreakBlobIntoGridSquaresMoreGranular(BreakBlobIntoGridSquares): - CONFIG = { - "square_size" : 0.15 - } + CONFIG = {"square_size": 0.15} + class BreakBlobIntoGridSquaresVeryGranular(BreakBlobIntoGridSquares): - CONFIG = { - "square_size" : 0.1 - } + CONFIG = {"square_size": 0.1} + class NameDeterminant(LinearTransformationScene): - CONFIG = { - "t_matrix" : [[3, 0], [2, 2]] - } + CONFIG = {"t_matrix": [[3, 0], [2, 2]]} + def construct(self): self.setup() self.plane.fade(0.3) - self.add_unit_square(color = YELLOW_E, opacity = 0.5) + self.add_unit_square(color=YELLOW_E, opacity=0.5) self.add_title( - ["The", "``determinant''", "of a transformation"], - scale_factor = 1 + ["The", "``determinant''", "of a transformation"], scale_factor=1 ) self.title.split()[1].split()[1].set_color(YELLOW) @@ -346,183 +353,185 @@ def construct(self): ) if det < 1 and det > 0: area_label_target.scale(det) - area_label_target.arrange(RIGHT, buff = 0.1) + area_label_target.arrange(RIGHT, buff=0.1) self.add_moving_mobject(area_label, area_label_target) - + self.wait() self.apply_transposed_matrix(self.t_matrix) self.wait() det_mob_copy = area_label.split()[0].copy() - new_det_mob = det_mob_copy.copy().set_height( - det_text.split()[0].get_height() - ) - new_det_mob.next_to(det_text, RIGHT, buff = 0.2) + new_det_mob = det_mob_copy.copy().set_height(det_text.split()[0].get_height()) + new_det_mob.next_to(det_text, RIGHT, buff=0.2) new_det_mob.add_background_rectangle() - det_mob_copy.add_background_rectangle(opacity = 0) + det_mob_copy.add_background_rectangle(opacity=0) self.play(Write(det_text)) self.play(Transform(det_mob_copy, new_det_mob)) self.wait() - def get_matrix(self): matrix = Matrix(np.array(self.t_matrix).transpose()) matrix.set_column_colors(X_COLOR, Y_COLOR) - matrix.next_to(self.title, DOWN, buff = 0.5) - matrix.shift(2*LEFT) + matrix.next_to(self.title, DOWN, buff=0.5) + matrix.shift(2 * LEFT) matrix_background = BackgroundRectangle(matrix) det_text = get_det_text(matrix, 0) det_text.remove(det_text.split()[-1]) return matrix_background, matrix, det_text + class SecondDeterminantExample(NameDeterminant): - CONFIG = { - "t_matrix" : [[-1, -1], [1, -1]] - } + CONFIG = {"t_matrix": [[-1, -1], [1, -1]]} + class DeterminantIsThree(NameDeterminant): - CONFIG = { - "t_matrix" : [[0, -1.5], [2, 1]] - } + CONFIG = {"t_matrix": [[0, -1.5], [2, 1]]} + class DeterminantIsOneHalf(NameDeterminant): CONFIG = { - "t_matrix" : [[0.5, -0.5], [0.5, 0.5]], - "foreground_plane_kwargs" : { - "x_radius" : FRAME_WIDTH, - "y_radius" : FRAME_WIDTH, - "secondary_line_ratio" : 0 + "t_matrix": [[0.5, -0.5], [0.5, 0.5]], + "foreground_plane_kwargs": { + "x_radius": FRAME_WIDTH, + "y_radius": FRAME_WIDTH, + "secondary_line_ratio": 0, }, } + class DeterminantIsZero(NameDeterminant): CONFIG = { - "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 - } + CONFIG = {"t_matrix": [[0, 0], [0, 0]], "show_basis_vectors": False} + class NextFewVideos(Scene): def construct(self): icon = SVGMobject("video_icon") icon.center() - icon.set_width(FRAME_WIDTH/12.) - icon.set_stroke(color = WHITE, width = 0) - icon.set_fill(WHITE, opacity = 1) + icon.set_width(FRAME_WIDTH / 12.0) + icon.set_stroke(color=WHITE, width=0) + icon.set_fill(WHITE, opacity=1) icons = VMobject(*[icon.copy() for x in range(10)]) icons.set_submobject_colors_by_gradient(BLUE_A, BLUE_D) icons.arrange(RIGHT) icons.to_edge(LEFT) - self.play( - FadeIn(icons, lag_ratio = 0.5), - run_time = 3 - ) + self.play(FadeIn(icons, lag_ratio=0.5), run_time=3) self.wait() + class UnderstandingBeforeApplication(TeacherStudentsScene): def construct(self): self.setup() - self.teacher_says(""" + self.teacher_says( + """ Just the visual understanding for now - """) + """ + ) self.random_blink() self.wait() + class WhatIveSaidSoFar(TeacherStudentsScene): def construct(self): self.setup() - self.teacher_says(""" + self.teacher_says( + """ What I've said so far is not quite right... - """) + """ + ) self.wait() + class NegativeDeterminant(Scene): def construct(self): numerical_matrix = [[1, 2], [3, 4]] matrix = Matrix(numerical_matrix) matrix.set_column_colors(X_COLOR, Y_COLOR) det_text = get_det_text(matrix, np.linalg.det(numerical_matrix)) - words = TextMobject(""" + words = TextMobject( + """ How can you scale area by a negative number? - """) + """ + ) words.set_color(YELLOW) - words.to_corner(UP+RIGHT) + words.to_corner(UP + RIGHT) det_num = det_text.split()[-1] arrow = Arrow(words.get_bottom(), det_num) self.add(matrix) self.play(Write(det_text)) self.wait() - self.play( - Write(words, run_time = 2), - ShowCreation(arrow) - ) + self.play(Write(words, run_time=2), ShowCreation(arrow)) self.play(det_num.set_color, YELLOW) self.wait() + class FlipSpaceOver(Scene): def construct(self): - plane1 = NumberPlane(y_radius = FRAME_X_RADIUS) + plane1 = NumberPlane(y_radius=FRAME_X_RADIUS) plane2 = NumberPlane( - y_radius = FRAME_X_RADIUS, - color = RED_D, secondary_color = RED_E + y_radius=FRAME_X_RADIUS, color=RED_D, secondary_color=RED_E ) axis = UP for word, plane in ("Front", plane1), ("Back", plane2): text = TextMobject(word) if word == "Back": - text.rotate(np.pi, axis = axis) + text.rotate(np.pi, axis=axis) text.scale(2) text.next_to(ORIGIN, RIGHT).to_edge(UP) text.add_background_rectangle() plane.add(text) - self.play(ShowCreation( - plane1, lag_ratio = 0.5, - run_time = 1 - )) + self.play(ShowCreation(plane1, lag_ratio=0.5, run_time=1)) self.wait() - self.play(Rotate( - plane1, axis = axis, - rate_func = lambda t : smooth(t/2), - run_time = 1.5, - path_arc = np.pi/2, - )) + self.play( + Rotate( + plane1, + axis=axis, + rate_func=lambda t: smooth(t / 2), + run_time=1.5, + path_arc=np.pi / 2, + ) + ) self.remove(plane1) - self.play(Rotate( - plane2, axis = axis, - rate_func = lambda t : smooth((t+1)/2), - run_time = 1.5, - path_arc = np.pi/2, - )) + self.play( + Rotate( + plane2, + axis=axis, + rate_func=lambda t: smooth((t + 1) / 2), + run_time=1.5, + path_arc=np.pi / 2, + ) + ) self.wait() + class RandyThinking(Scene): def construct(self): randy = Randolph().to_corner() bubble = randy.get_bubble() bubble.make_green_screen() - self.play( - randy.change_mode, "pondering", - ShowCreation(bubble) - ) + self.play(randy.change_mode, "pondering", ShowCreation(bubble)) self.wait() self.play(Blink(randy)) self.wait(2) self.play(Blink(randy)) + class NegativeDeterminantTransformation(LinearTransformationScene): CONFIG = { - "t_matrix" : [[1, 1], [2, -1]], + "t_matrix": [[1, 1], [2, -1]], } + def construct(self): self.setup() self.add_title("Feels like flipping space") @@ -530,51 +539,55 @@ def construct(self): self.apply_transposed_matrix(self.t_matrix) self.wait() + class ThinkAboutFlippingPaper(Scene): def construct(self): pass + class NegativeDeterminantTransformation2(NegativeDeterminantTransformation): - CONFIG ={ - "t_matrix" : [[-2, 1], [2, 1]] - } + CONFIG = {"t_matrix": [[-2, 1], [2, 1]]} + class IHatJHatOrientation(NegativeDeterminantTransformation): def construct(self): self.setup() i_label, j_label = self.get_basis_vector_labels() - self.add_transformable_label(self.i_hat, i_label, color = X_COLOR) - self.add_transformable_label(self.j_hat, j_label, color = Y_COLOR) + self.add_transformable_label(self.i_hat, i_label, color=X_COLOR) + self.add_transformable_label(self.j_hat, j_label, color=Y_COLOR) - arc = Arc(start_angle = 0, angle = np.pi/2, color = YELLOW) - arc.shift(0.5*(RIGHT+UP)).scale(1/1.6) + arc = Arc(start_angle=0, angle=np.pi / 2, color=YELLOW) + arc.shift(0.5 * (RIGHT + UP)).scale(1 / 1.6) arc.add_tip() - words1 = TextMobject([ - "$\\hat{\\jmath}$", - "is to the", - "left", - "of", - "$\\hat{\\imath}$", - ]) + words1 = TextMobject( + [ + "$\\hat{\\jmath}$", + "is to the", + "left", + "of", + "$\\hat{\\imath}$", + ] + ) words1.split()[0].set_color(Y_COLOR) words1.split()[2].set_color(YELLOW) words1.split()[-1].set_color(X_COLOR) words1.add_background_rectangle() - words1.next_to(arc, UP+RIGHT) - - words2 = TextMobject([ - "$L(\\hat{\\jmath})$", - "is to the \\\\", - "\\emph{right}", - "of", - "$L(\\hat{\\imath})$", - ]) + words1.next_to(arc, UP + RIGHT) + + words2 = TextMobject( + [ + "$L(\\hat{\\jmath})$", + "is to the \\\\", + "\\emph{right}", + "of", + "$L(\\hat{\\imath})$", + ] + ) words2.split()[0].set_color(Y_COLOR) words2.split()[2].set_color(YELLOW) words2.split()[-1].set_color(X_COLOR) words2.add_background_rectangle() - self.play(ShowCreation(arc)) self.play(Write(words1)) self.wait() @@ -586,15 +599,16 @@ def construct(self): words2.next_to(arc, RIGHT) self.play( ShowCreation(arc), - Write(words2, run_time = 2), + Write(words2, run_time=2), ) self.wait() title = TextMobject("Orientation has been reversed") title.to_edge(UP) title.add_background_rectangle() - self.play(Write(title, run_time = 1)) + self.play(Write(title, run_time=1)) self.wait() + class WriteNegativeDeterminant(NegativeDeterminantTransformation): def construct(self): self.setup() @@ -604,9 +618,7 @@ def construct(self): matrix.to_edge(UP) matrix.set_column_colors(X_COLOR, Y_COLOR) - det_text = get_det_text( - matrix, determinant = np.linalg.det(self.t_matrix) - ) + det_text = get_det_text(matrix, determinant=np.linalg.det(self.t_matrix)) three = VMobject(*det_text.split()[-1].split()[1:]) for mob in det_text.split(): if isinstance(mob, TexMobject): @@ -624,61 +636,64 @@ def construct(self): self.play(three.copy().move_to, self.square) self.wait() + class AltWriteNegativeDeterminant(WriteNegativeDeterminant): - CONFIG = { - "t_matrix" : [[2, -1], [1, -3]] - } + CONFIG = {"t_matrix": [[2, -1], [1, -3]]} + class WhyNegativeScaling(TeacherStudentsScene): def construct(self): self.setup() - self.student_says(""" + self.student_says( + """ Why does negative area relate to orientation-flipping? - """) + """ + ) other_students = np.array(self.get_students())[[0, 2]] - self.play(*[ - ApplyMethod(student.change_mode, "confused") - for student in other_students - ]) + self.play( + *[ + ApplyMethod(student.change_mode, "confused") + for student in other_students + ] + ) self.random_blink() self.wait() self.random_blink() + class SlowlyRotateIHat(LinearTransformationScene): def construct(self): self.setup() self.add_unit_square() self.apply_transposed_matrix( [[-1, 0], [0, 1]], - path_arc = np.pi, - run_time = 30, + path_arc=np.pi, + run_time=30, rate_func=linear, ) + class DeterminantGraphForRotatingIHat(Scene): def construct(self): t_axis = NumberLine( - numbers_with_elongated_ticks = [], - x_min = 0, - x_max = 10, - color = WHITE, + numbers_with_elongated_ticks=[], + x_min=0, + x_max=10, + color=WHITE, ) det_axis = NumberLine( - numbers_with_elongated_ticks = [], - x_min = -2, - x_max = 2, - color = WHITE + numbers_with_elongated_ticks=[], x_min=-2, x_max=2, color=WHITE ) - det_axis.rotate(np.pi/2) - t_axis.next_to(ORIGIN, RIGHT, buff = 0) + det_axis.rotate(np.pi / 2) + t_axis.next_to(ORIGIN, RIGHT, buff=0) det_axis.move_to(t_axis.get_left()) axes = VMobject(det_axis, t_axis) - graph = FunctionGraph(np.cos, x_min = 0, x_max = np.pi) - graph.next_to(det_axis, RIGHT, buff = 0) + graph = FunctionGraph(np.cos, x_min=0, x_max=np.pi) + graph.next_to(det_axis, RIGHT, buff=0) graph.set_color(YELLOW) det_word = TextMobject("Det") - det_word.next_to(det_axis, RIGHT, aligned_edge = UP) + det_word.next_to(det_axis, RIGHT, aligned_edge=UP) time_word = TextMobject("time") time_word.next_to(t_axis, UP) time_word.to_edge(RIGHT) @@ -686,51 +701,52 @@ def construct(self): everything.scale(1.5) self.add(axes, det_word, time_word) - self.play(ShowCreation( - graph, rate_func=linear, run_time = 10 - )) + self.play(ShowCreation(graph, rate_func=linear, run_time=10)) + class WhatAboutThreeDimensions(TeacherStudentsScene): def construct(self): self.setup() - self.student_says(""" + self.student_says( + """ What about 3D transformations? - """) + """ + ) self.random_blink() self.wait() self.random_blink() + class Transforming3DCube(Scene): def construct(self): pass + class NameParallelepiped(Scene): def construct(self): word = TextMobject("``Parallelepiped''") word.scale(2) - pp_part1 = VMobject(*word.split()[:len(word.split())/2]) - pp_part2 = VMobject(*word.split()[len(word.split())/2:]) + pp_part1 = VMobject(*word.split()[: len(word.split()) / 2]) + pp_part2 = VMobject(*word.split()[len(word.split()) / 2 :]) pp_part1.set_submobject_colors_by_gradient(X_COLOR, Y_COLOR) pp_part2.set_submobject_colors_by_gradient(Y_COLOR, Z_COLOR) self.play(Write(word)) self.wait(2) + class DeterminantIsVolumeOfParallelepiped(Scene): def construct(self): matrix = Matrix([[1, 0, 0.5], [0.5, 1, 0], [1, 0, 1]]) - matrix.shift(3*LEFT) + matrix.shift(3 * LEFT) matrix.set_column_colors(X_COLOR, Y_COLOR, Z_COLOR) det_text = get_det_text(matrix) eq = TexMobject("=") eq.next_to(det_text, RIGHT) - words = TextMobject([ - "Volume of this\\\\", - "parallelepiped" - ]) + words = TextMobject(["Volume of this\\\\", "parallelepiped"]) pp = words.split()[1] - pp_part1 = VMobject(*pp.split()[:len(pp.split())/2]) - pp_part2 = VMobject(*pp.split()[len(pp.split())/2:]) + pp_part1 = VMobject(*pp.split()[: len(pp.split()) / 2]) + pp_part2 = VMobject(*pp.split()[len(pp.split()) / 2 :]) pp_part1.set_submobject_colors_by_gradient(X_COLOR, Y_COLOR) pp_part2.set_submobject_colors_by_gradient(Y_COLOR, Z_COLOR) @@ -741,21 +757,25 @@ def construct(self): self.play(Write(det_text), Write(words), Write(eq)) self.wait() + class Degenerate3DTransformation(Scene): def construct(self): pass + class WriteZeroDeterminant(Scene): def construct(self): matrix = Matrix([[1, 0, 1], [0.5, 1, 1.5], [1, 0, 1]]) - matrix.shift(2*LEFT) + matrix.shift(2 * LEFT) matrix.set_column_colors(X_COLOR, Y_COLOR, Z_COLOR) det_text = get_det_text(matrix, 0) brace = Brace(matrix, DOWN) - words = TextMobject(""" + words = TextMobject( + """ Columns must be linearly dependent - """) + """ + ) words.set_color(YELLOW) words.next_to(brace, DOWN) @@ -763,45 +783,47 @@ def construct(self): self.wait() self.play(Write(det_text)) self.wait() - self.play( - GrowFromCenter(brace), - Write(words, run_time = 2) - ) + self.play(GrowFromCenter(brace), Write(words, run_time=2)) self.wait() + class AskAboutNegaive3DDeterminant(TeacherStudentsScene): def construct(self): self.setup() - self.student_says(""" + self.student_says( + """ What would det$(M) < 0$ mean? - """) + """ + ) self.random_blink() self.play(self.teacher.change_mode, "pondering") self.wait() self.random_blink() + class OrientationReversing3DTransformation(Scene): def construct(self): pass + class RightHandRule(Scene): CONFIG = { - "flip" : False, - "labels_tex" : ["\\hat{\\imath}", "\\hat{\\jmath}", "\\hat{k}"], - "colors" : [X_COLOR, Y_COLOR, Z_COLOR], + "flip": False, + "labels_tex": ["\\hat{\\imath}", "\\hat{\\jmath}", "\\hat{k}"], + "colors": [X_COLOR, Y_COLOR, Z_COLOR], } + def construct(self): hand = RightHand() v1 = Vector([-1.75, 0.5]) v2 = Vector([-1.4, -0.7]) v3 = Vector([0, 1.7]) - vects = [v1, v2, v3] + vects = [v1, v2, v3] if self.flip: VMobject(hand, *vects).flip() v1_label, v2_label, v3_label = [ - TexMobject(label_tex).scale(1.5) - for label_tex in self.labels_tex + TexMobject(label_tex).scale(1.5) for label_tex in self.labels_tex ] v1_label.next_to(v1.get_end(), UP) v2_label.next_to(v2.get_end(), DOWN) @@ -811,22 +833,22 @@ def construct(self): # self.add(NumberPlane()) self.play( - ShowCreation(hand.outline, run_time = 2, rate_func=linear), - FadeIn(hand.inlines) + ShowCreation(hand.outline, run_time=2, rate_func=linear), + FadeIn(hand.inlines), ) self.wait() for vect, label, color in zip(vects, labels, self.colors): vect.set_color(color) label.set_color(color) - vect.set_stroke(width = 8) + vect.set_stroke(width=8) self.play(ShowCreation(vect)) self.play(Write(label)) self.wait() + class LeftHandRule(RightHandRule): - CONFIG = { - "flip" : True - } + CONFIG = {"flip": True} + class AskHowToCompute(TeacherStudentsScene): def construct(self): @@ -838,19 +860,20 @@ def construct(self): self.wait() self.random_blink() + class TwoDDeterminantFormula(Scene): def construct(self): eq = TextMobject("=") matrix = Matrix([["a", "b"], ["c", "d"]]) matrix.set_column_colors(X_COLOR, Y_COLOR) ma, mb, mc, md = matrix.get_entries().split() - ma.shift(0.1*DOWN) - mc.shift(0.7*mc.get_height()*DOWN) + ma.shift(0.1 * DOWN) + mc.shift(0.7 * mc.get_height() * DOWN) det_text = get_det_text(matrix) VMobject(matrix, det_text).next_to(eq, LEFT) formula = TexMobject(list("ad-bc")) formula.next_to(eq, RIGHT) - formula.shift(0.1*UP) + formula.shift(0.1 * UP) a, d, minus, b, c = formula.split() VMobject(a, c).set_color(X_COLOR) @@ -861,27 +884,22 @@ def construct(self): mob.zero = TexMobject("\\cdot 0") else: mob.zero = TexMobject("0") - mob.zero.move_to(mob, aligned_edge = DOWN+LEFT) + mob.zero.move_to(mob, aligned_edge=DOWN + LEFT) mob.zero.set_color(mob.get_color()) mob.original = mob.copy() - c.zero.shift(0.1*RIGHT) + c.zero.shift(0.1 * RIGHT) self.add(matrix) - self.play(Write(det_text, run_time = 1)) + self.play(Write(det_text, run_time=1)) self.play(Write(eq), Write(formula)) self.wait() - self.play(*[ - Transform(m, m.zero) - for m in (mb, mc, b, c) - ]) + self.play(*[Transform(m, m.zero) for m in (mb, mc, b, c)]) self.wait() for pair in (mb, b), (mc, c): - self.play(*[ - Transform(m, m.original) - for m in pair - ]) + self.play(*[Transform(m, m.original) for m in pair]) self.wait() + class TwoDDeterminantFormulaIntuition(LinearTransformationScene): def construct(self): self.setup() @@ -908,33 +926,25 @@ def construct(self): self.wait() self.apply_transposed_matrix( - [[1, 0], [float(b)/d, 1]], - added_anims = [ - ApplyMethod(m.shift, b*RIGHT) - for m in (side_brace, height) - ] + [[1, 0], [float(b) / d, 1]], + added_anims=[ApplyMethod(m.shift, b * RIGHT) for m in (side_brace, height)], ) self.wait() self.play(*list(map(FadeOut, [i_brace, side_brace, width, height]))) - matrix1 = np.dot( - [[a, b], [c, d]], - np.linalg.inv([[a, b], [0, d]]) - ) - matrix2 = np.dot( - [[a, b], [-c, d]], - np.linalg.inv([[a, b], [c, d]]) - ) - self.apply_transposed_matrix(matrix1.transpose(), path_arc = 0) + matrix1 = np.dot([[a, b], [c, d]], np.linalg.inv([[a, b], [0, d]])) + matrix2 = np.dot([[a, b], [-c, d]], np.linalg.inv([[a, b], [c, d]])) + self.apply_transposed_matrix(matrix1.transpose(), path_arc=0) self.wait() - self.apply_transposed_matrix(matrix2.transpose(), path_arc = 0) + self.apply_transposed_matrix(matrix2.transpose(), path_arc=0) self.wait() + class FullFormulaExplanation(LinearTransformationScene): def construct(self): self.setup() self.add_unit_square() - self.apply_transposed_matrix([[3, 1], [1, 2]], run_time = 0) - self.add_braces() + self.apply_transposed_matrix([[3, 1], [1, 2]], run_time=0) + self.add_braces() self.add_polygons() self.show_formula() @@ -942,61 +952,59 @@ def get_matrix(self): matrix = Matrix([["a", "b"], ["c", "d"]]) matrix.set_column_colors(X_COLOR, Y_COLOR) ma, mb, mc, md = matrix.get_entries().split() - ma.shift(0.1*DOWN) - mc.shift(0.7*mc.get_height()*DOWN) - matrix.shift(2*DOWN+4*LEFT) + ma.shift(0.1 * DOWN) + mc.shift(0.7 * mc.get_height() * DOWN) + matrix.shift(2 * DOWN + 4 * LEFT) return matrix def add_polygons(self): - a = self.i_hat.get_end()[0]*RIGHT - b = self.j_hat.get_end()[0]*RIGHT - c = self.i_hat.get_end()[1]*UP - d = self.j_hat.get_end()[1]*UP + a = self.i_hat.get_end()[0] * RIGHT + b = self.j_hat.get_end()[0] * RIGHT + c = self.i_hat.get_end()[1] * UP + d = self.j_hat.get_end()[1] * UP shapes_colors_and_tex = [ - (Polygon(ORIGIN, a, a+c), MAROON, "ac/2"), - (Polygon(ORIGIN, d+b, d, d), TEAL, "\\dfrac{bd}{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(a, a+b, a+b+c, a+c), PINK, "bc"), - (Polygon(d, d+b, d+b+c, d+c), PINK, "bc"), + (Polygon(ORIGIN, a, a + c), MAROON, "ac/2"), + (Polygon(ORIGIN, d + b, d, d), TEAL, "\\dfrac{bd}{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(a, a + b, a + b + c, a + c), PINK, "bc"), + (Polygon(d, d + b, d + b + c, d + c), PINK, "bc"), ] everyone = VMobject() for shape, color, tex in shapes_colors_and_tex: - shape.set_stroke(width = 0) - shape.set_fill(color = color, opacity = 0.7) + shape.set_stroke(width=0) + shape.set_fill(color=color, opacity=0.7) tex_mob = TexMobject(tex) tex_mob.scale(0.7) tex_mob.move_to(shape.get_center_of_mass()) everyone.add(shape, tex_mob) - self.play(FadeIn( - everyone, - lag_ratio = 0.5, - run_time = 1 - )) - - + self.play(FadeIn(everyone, lag_ratio=0.5, run_time=1)) def add_braces(self): - a = self.i_hat.get_end()[0]*RIGHT - b = self.j_hat.get_end()[0]*RIGHT - c = self.i_hat.get_end()[1]*UP - d = self.j_hat.get_end()[1]*UP + a = self.i_hat.get_end()[0] * RIGHT + b = self.j_hat.get_end()[0] * RIGHT + c = self.i_hat.get_end()[1] * UP + d = self.j_hat.get_end()[1] * UP quads = [ (ORIGIN, a, DOWN, "a"), - (a, a+b, DOWN, "b"), - (a+b, a+b+c, RIGHT, "c"), - (a+b+c, a+b+c+d, RIGHT, "d"), - (a+b+c+d, b+c+d, UP, "a"), - (b+c+d, d+c, UP, "b"), - (d+c, d, LEFT, "c"), + (a, a + b, DOWN, "b"), + (a + b, a + b + c, RIGHT, "c"), + (a + b + c, a + b + c + d, RIGHT, "d"), + (a + b + c + d, b + c + d, UP, "a"), + (b + c + d, d + c, UP, "b"), + (d + c, d, LEFT, "c"), (d, ORIGIN, LEFT, "d"), ] everyone = VMobject() for p1, p2, direction, char in quads: line = Line(p1, p2) - brace = Brace(line, direction, buff = 0) + brace = Brace(line, direction, buff=0) text = brace.get_text(char) text.add_background_rectangle() if char in ["a", "c"]: @@ -1004,8 +1012,7 @@ def add_braces(self): else: text.set_color(Y_COLOR) everyone.add(brace, text) - self.play(Write(everyone), run_time = 1) - + self.play(Write(everyone), run_time=1) def show_formula(self): matrix = self.get_matrix() @@ -1018,12 +1025,10 @@ def show_formula(self): everyone.set_width(FRAME_WIDTH - 1) everyone.next_to(DOWN, DOWN) background_rect = BackgroundRectangle(everyone) - self.play( - ShowCreation(background_rect), - Write(everyone) - ) + self.play(ShowCreation(background_rect), Write(everyone)) self.wait() + class ThreeDDetFormula(Scene): def construct(self): matrix = Matrix([list("abc"), list("def"), list("ghi")]) @@ -1038,24 +1043,28 @@ def construct(self): for m in matrix, m1, m2, m3: m.add(get_det_text(m)) a, b, c = matrix.get_entries().split()[:3] - parts = it.starmap(VMobject, [ - [matrix], - [TexMobject("="), a.copy(), m1], - [TexMobject("-"), b.copy(), m2], - [TexMobject("+"), c.copy(), m3], - ]) + parts = it.starmap( + VMobject, + [ + [matrix], + [TexMobject("="), a.copy(), m1], + [TexMobject("-"), b.copy(), m2], + [TexMobject("+"), c.copy(), m3], + ], + ) parts = list(parts) for part in parts: - part.arrange(RIGHT, buff = 0.2) + part.arrange(RIGHT, buff=0.2) parts[1].next_to(parts[0], RIGHT) - parts[2].next_to(parts[1], DOWN, aligned_edge = LEFT) - parts[3].next_to(parts[2], DOWN, aligned_edge = LEFT) + parts[2].next_to(parts[1], DOWN, aligned_edge=LEFT) + parts[3].next_to(parts[2], DOWN, aligned_edge=LEFT) everyone = VMobject(*parts) everyone.center().to_edge(UP) for part in parts: self.play(Write(part)) self.wait(2) + class QuizTime(TeacherStudentsScene): def construct(self): self.setup() @@ -1064,25 +1073,15 @@ def construct(self): self.wait() self.random_blink() + class ProductProperty(Scene): def construct(self): - lhs = TexMobject([ - "\\text{det}(", - "M_1", - "M_2", - ")" - ]) + lhs = TexMobject(["\\text{det}(", "M_1", "M_2", ")"]) det, m1, m2, rp = lhs.split() m1.set_color(TEAL) m2.set_color(PINK) - rhs = TexMobject([ - "=\\text{det}(", - "M_1", - ")\\text{det}(", - "M_2", - ")" - ]) + rhs = TexMobject(["=\\text{det}(", "M_1", ")\\text{det}(", "M_2", ")"]) rhs.split()[1].set_color(TEAL) rhs.split()[3].set_color(PINK) @@ -1092,7 +1091,7 @@ def construct(self): title = TextMobject("Explain in one sentence") title.set_color(YELLOW) - title.next_to(formula, UP, buff = 0.5) + title.next_to(formula, UP, buff=0.5) self.play(Write(m1)) self.play(Write(m2)) @@ -1103,29 +1102,20 @@ def construct(self): self.play(Write(title)) self.wait(2) + class NextVideo(Scene): def construct(self): - title = TextMobject(""" + title = TextMobject( + """ Next video: Inverse matrices, column space and null space - """) + """ + ) title.set_width(FRAME_WIDTH - 2) title.to_edge(UP) - rect = Rectangle(width = 16, height = 9, color = BLUE) + rect = Rectangle(width=16, height=9, color=BLUE) rect.set_height(6) rect.next_to(title, DOWN) self.add(title) self.play(ShowCreation(rect)) - self.wait() - - - - - - - - - - - - + self.wait() diff --git a/from_3b1b/old/turbulence.py b/from_3b1b/old/turbulence.py index c3765f44ed..6f1eb9f72a 100644 --- a/from_3b1b/old/turbulence.py +++ b/from_3b1b/old/turbulence.py @@ -44,10 +44,7 @@ def update(mob, dt): class Eddy(VMobject): CONFIG = { - "cd_mob_config": { - "frequency": 0.2, - "max_ratio_shown": 0.3 - }, + "cd_mob_config": {"frequency": 0.2, "max_ratio_shown": 0.3}, "n_spirils": 5, "n_layers": 20, "radius": 1, @@ -58,10 +55,9 @@ def __init__(self, **kwargs): VMobject.__init__(self, **kwargs) lines = self.get_lines() # self.add(lines) - self.add(*[ - CreationDestructionMobject(line, **self.cd_mob_config) - for line in lines - ]) + self.add( + *[CreationDestructionMobject(line, **self.cd_mob_config) for line in lines] + ) self.randomize_times() def randomize_times(self): @@ -72,22 +68,28 @@ def randomize_times(self): def get_lines(self): a = 0.2 - return VGroup(*[ - self.get_line(r=self.radius * (1 - a + 2 * a * random.random())) - for x in range(self.n_layers) - ]) + return VGroup( + *[ + self.get_line(r=self.radius * (1 - a + 2 * a * random.random())) + for x in range(self.n_layers) + ] + ) def get_line(self, r): return ParametricFunction( - lambda t: r * (t + 1)**(-1) * np.array([ - np.cos(TAU * t), - np.sin(TAU * t), - 0, - ]), + lambda t: r + * (t + 1) ** (-1) + * np.array( + [ + np.cos(TAU * t), + np.sin(TAU * t), + 0, + ] + ), t_min=0.1 * random.random(), t_max=self.n_spirils, stroke_width=1, - color=interpolate_color(*self.colors, random.random()) + color=interpolate_color(*self.colors, random.random()), ) @@ -97,11 +99,7 @@ class Chaos(Eddy): "height": 1, "width": 2, "n_midpoints": 4, - "cd_mob_config": { - "use_copy": False, - "frequency": 1, - "max_ratio_shown": 0.8 - } + "cd_mob_config": {"use_copy": False, "frequency": 1, "max_ratio_shown": 0.8}, } def __init__(self, **kwargs): @@ -113,27 +111,22 @@ def __init__(self, **kwargs): self.add(rect) lines = self.get_lines() - self.add(*[ - CreationDestructionMobject(line, **self.cd_mob_config) - for line in lines - ]) + self.add( + *[CreationDestructionMobject(line, **self.cd_mob_config) for line in lines] + ) self.randomize_times() lines.fade(1) self.add(lines) def get_lines(self): - return VGroup(*[ - self.get_line(y) - for y in np.linspace(0, self.height, self.n_lines) - ]) + return VGroup( + *[self.get_line(y) for y in np.linspace(0, self.height, self.n_lines)] + ) def get_line(self, y): frequencies = [0] + list(2 + 2 * np.random.random(self.n_midpoints)) + [0] rect = self.rect - line = Line( - y * UP, y * UP + self.width * RIGHT, - stroke_width=1 - ) + line = Line(y * UP, y * UP + self.width * RIGHT, stroke_width=1) line.insert_n_curves(self.n_midpoints) line.total_time = random.random() delta_h = self.height / (self.n_lines - 1) @@ -144,11 +137,13 @@ def update(line, dt): line.total_time += dt xs = np.linspace(x0, x1, self.n_midpoints + 2) new_anchors = [ - np.array([ - x + 1.0 * delta_h * np.cos(f * line.total_time), - y0 + y + 1.0 * delta_h * np.cos(f * line.total_time), - 0 - ]) + np.array( + [ + x + 1.0 * delta_h * np.cos(f * line.total_time), + y0 + y + 1.0 * delta_h * np.cos(f * line.total_time), + 0, + ] + ) for (x, f) in zip(xs, frequencies) ] line.set_points_smoothly(new_anchors) @@ -181,10 +176,14 @@ def __init__(self, **kwargs): # The gravitational acceleration (m.s-2). g = 9.81 - self.state_vect = np.array([ - self.start_angles[0], 0, - self.start_angles[1], 0, - ]) + self.state_vect = np.array( + [ + self.start_angles[0], + 0, + self.start_angles[1], + 0, + ] + ) self.state_vect += np.random.random(4) * 1e-7 def update(group, dt): @@ -195,15 +194,42 @@ def update(group, dt): c, s = np.cos(theta1 - theta2), np.sin(theta1 - theta2) theta1dot = z1 - z1dot = (m2 * g * np.sin(theta2) * c - m2 * s * (L1 * (z1**2) * c + L2 * z2**2) - - (m1 + m2) * g * np.sin(theta1)) / L1 / (m1 + m2 * s**2) + z1dot = ( + ( + m2 * g * np.sin(theta2) * c + - m2 * s * (L1 * (z1**2) * c + L2 * z2**2) + - (m1 + m2) * g * np.sin(theta1) + ) + / L1 + / (m1 + m2 * s**2) + ) theta2dot = z2 - z2dot = ((m1 + m2) * (L1 * (z1**2) * s - g * np.sin(theta2) + g * np.sin(theta1) * c) + - m2 * L2 * (z2**2) * s * c) / L2 / (m1 + m2 * s**2) + z2dot = ( + ( + (m1 + m2) + * ( + L1 * (z1**2) * s + - g * np.sin(theta2) + + g * np.sin(theta1) * c + ) + + m2 * L2 * (z2**2) * s * c + ) + / L2 + / (m1 + m2 * s**2) + ) - group.state_vect += 0.5 * dt * np.array([ - theta1dot, z1dot, theta2dot, z2dot, - ]) + group.state_vect += ( + 0.5 + * dt + * np.array( + [ + theta1dot, + z1dot, + theta2dot, + z2dot, + ] + ) + ) group.state_vect[1::2] *= 0.9999 p1 = L1 * np.sin(theta1) * RIGHT - L1 * np.cos(theta1) * UP @@ -232,11 +258,7 @@ def __init__(self, **kwargs): class Diffusion(VMobject): - CONFIG = { - "height": 1.5, - "n_dots": 1000, - "colors": [RED, BLUE] - } + CONFIG = {"height": 1.5, "n_dots": 1000, "colors": [RED, BLUE]} def __init__(self, **kwargs): VMobject.__init__(self, **kwargs) @@ -249,8 +271,8 @@ def add_dots(self): dots.center() dots.set_height(self.height) dots.sort(lambda p: p[0]) - dots[:len(dots) // 2].set_color(self.colors[0]) - dots[len(dots) // 2:].set_color(self.colors[1]) + dots[: len(dots) // 2].set_color(self.colors[0]) + dots[len(dots) // 2 :].set_color(self.colors[1]) dots.set_fill(opacity=0.8) self.dots = dots self.add(dots) @@ -296,21 +318,36 @@ def __init__(self, **kwargs): TexMobject.__init__( self, "\\rho", - "\\left(" - "{\\partial", v_tex, "\\over", - "\\partial", "t}", + "\\left(" "{\\partial", + v_tex, + "\\over", + "\\partial", + "t}", "+", - v_tex, "\\cdot", "\\nabla", v_tex, + v_tex, + "\\cdot", + "\\nabla", + v_tex, "\\right)", "=", - "-", "\\nabla", "p{}", "+", - "\\mu", "\\nabla^2", v_tex, "+", + "-", + "\\nabla", + "p{}", + "+", + "\\mu", + "\\nabla^2", + v_tex, + "+", # "\\frac{1}{3}", "\\mu", "\\nabla", # "(", "\\nabla", "\\cdot", v_tex, ")", "+", "\\textbf{F}", "\\qquad\\qquad", - "\\nabla", "\\cdot", v_tex, "=", "0", - **kwargs + "\\nabla", + "\\cdot", + v_tex, + "=", + "0", + **kwargs, ) self.set_width(self.width) @@ -353,13 +390,13 @@ def construct(self): self.add(DoublePendulums()) self.wait(30) + # Scenes class EddyReference(Scene): CONFIG = { "radius": 1, - "label": "Eddy", "label": "", } @@ -367,9 +404,7 @@ def construct(self): eddy = Eddy(radius=self.radius) new_eddy = eddy.get_lines() for line in new_eddy: - line.set_stroke( - width=(3 + 3 * random.random()) - ) + line.set_stroke(width=(3 + 3 * random.random())) label = TextMobject(self.label) label.next_to(new_eddy, UP) @@ -379,14 +414,12 @@ def construct(self): label, rate_func=there_and_back_with_pause, ), - run_time=3 + run_time=3, ) class EddyReferenceWithLabel(EddyReference): - CONFIG = { - "label": "Eddy" - } + CONFIG = {"label": "Eddy"} class EddyLabels(Scene): @@ -397,32 +430,19 @@ def construct(self): TextMobject("Small eddy"), ) for label in labels: - self.play(FadeIn( - label, - rate_func=there_and_back_with_pause, - run_time=3 - )) + self.play(FadeIn(label, rate_func=there_and_back_with_pause, run_time=3)) class LargeEddyReference(EddyReference): - CONFIG = { - "radius": 2, - "label": "" - } + CONFIG = {"radius": 2, "label": ""} class MediumEddyReference(EddyReference): - CONFIG = { - "radius": 0.8, - "label": "Medium eddy" - } + CONFIG = {"radius": 0.8, "label": "Medium eddy"} class SmallEddyReference(EddyReference): - CONFIG = { - "radius": 0.25, - "label": "Small eddy" - } + CONFIG = {"radius": 0.25, "label": "Small eddy"} class SomeTurbulenceEquations(PiCreatureScene): @@ -438,7 +458,7 @@ def construct(self): tex_to_color_map={ "k": GREEN, "-5/3": YELLOW, - } + }, ) distribution.next_to(morty, UL) brace = Brace(distribution, DOWN, buff=SMALL_BUFF) @@ -447,15 +467,23 @@ def construct(self): self.play( Write(navier_stokes), - randy.change, "confused", navier_stokes, - morty.change, "confused", navier_stokes, + randy.change, + "confused", + navier_stokes, + morty.change, + "confused", + navier_stokes, ) self.wait(3) self.play( - morty.change, "raise_right_hand", distribution, - randy.look_at, distribution, + morty.change, + "raise_right_hand", + distribution, + randy.look_at, + distribution, FadeInFromDown(distribution), - navier_stokes.fade, 0.5, + navier_stokes.fade, + 0.5, ) self.play(GrowFromCenter(brace_group)) self.play(randy.change, "pondering", distribution) @@ -463,9 +491,11 @@ def construct(self): dist_group = VGroup(distribution, brace_group) self.play( LaggedStartMap(FadeOut, VGroup(randy, morty, navier_stokes)), - dist_group.scale, 1.5, + dist_group.scale, + 1.5, dist_group.center, - dist_group.to_edge, UP, + dist_group.to_edge, + UP, ) self.wait() @@ -495,21 +525,20 @@ def construct(self): point = 3.8 * LEFT + 0.2 * UP arrow1 = Arrow( - items[0].get_left(), point + 0.8 * UP + 0.3 * RIGHT, + items[0].get_left(), + point + 0.8 * UP + 0.3 * RIGHT, path_arc=90 * DEGREES, ) arrow1.pointwise_become_partial(arrow1, 0, 0.99) arrow2 = Arrow( - items[1].get_left(), point, + items[1].get_left(), + point, ) arrows = VGroup(arrow1, arrow2) for i in 0, 1: - self.play( - FadeInFromDown(items[i]), - ShowCreation(arrows[i]) - ) + self.play(FadeInFromDown(items[i]), ShowCreation(arrows[i])) self.wait() self.play(LaggedStartMap(FadeIn, items[2:])) self.wait() @@ -532,10 +561,7 @@ def construct(self): class LightBouncingOffFogParticle(Scene): def construct(self): - words = TextMobject( - "Light bouncing\\\\", - "off fog particles" - ) + words = TextMobject("Light bouncing\\\\", "off fog particles") arrow = Vector(UP + 0.5 * RIGHT) arrow.next_to(words, UP) arrow.set_color(WHITE) @@ -572,7 +598,7 @@ def construct(self): stroke_color=DARK_GREY, stroke_width=1, ), - Line(ORIGIN, 10 * RIGHT, color=GREEN_SCREEN) + Line(ORIGIN, 10 * RIGHT, color=GREEN_SCREEN), ) laser.arrange(RIGHT, buff=0) laser.rotate(45 * DEGREES) @@ -583,9 +609,9 @@ def construct(self): def update_laser(laser, dt): laser.time += dt laser.rotate( - 0.5 * dt * np.sin(laser.time), - about_point=laser[0].get_center() + 0.5 * dt * np.sin(laser.time), about_point=laser[0].get_center() ) + laser.add_updater(update_laser) self.play(LaggedStartMap(FadeInFromDown, self.pi_creatures, run_time=1)) @@ -595,40 +621,24 @@ def update_laser(laser, dt): self.play( ShowCreation(laser), self.get_student_changes( - "surprised", "hooray", "horrified", - look_at_arg=laser - ) - ) - self.teacher_says( - "Careful with \\\\ the laser!", - target_mode="angry" + "surprised", "hooray", "horrified", look_at_arg=laser + ), ) + self.teacher_says("Careful with \\\\ the laser!", target_mode="angry") self.wait(2.2) morty.save_state() randy2.save_state() - self.play( - morty.blink, randy2.blink, - run_time=0.3 - ) + self.play(morty.blink, randy2.blink, run_time=0.3) self.wait(2) - self.play( - morty.restore, randy2.restore, - run_time=0.3 - ) + self.play(morty.restore, randy2.restore, run_time=0.3) self.wait(2) class SetAsideTurbulence(PiCreatureScene): def construct(self): - self.pi_creature_says( - "Forget vortex rings", - target_mode="speaking" - ) + self.pi_creature_says("Forget vortex rings", target_mode="speaking") self.wait() - self.pi_creature_says( - "look at that\\\\ turbulence!", - target_mode="surprised" - ) + self.pi_creature_says("look at that\\\\ turbulence!", target_mode="surprised") self.wait() def create_pi_creature(self): @@ -639,9 +649,7 @@ def create_pi_creature(self): class WavingRodLabel(Scene): def construct(self): - words = TextMobject( - "(Waving a small flag \\\\ through the air)" - ) + words = TextMobject("(Waving a small flag \\\\ through the air)") self.play(Write(words)) self.wait() @@ -692,30 +700,28 @@ def pi_creatures_ask(self): self.student_says( words, - target_mode='raise_left_hand', - added_anims=[morty.change, 'pondering'] + target_mode="raise_left_hand", + added_anims=[morty.change, "pondering"], ) self.change_student_modes( - "erm", "raise_left_hand", "confused", + "erm", + "raise_left_hand", + "confused", ) self.wait(3) self.play( - morty.change, "raise_right_hand", + morty.change, + "raise_right_hand", FadeOut(randy.bubble), ReplacementTransform(VGroup(words[1], words[3]), question), FadeOut(VGroup(words[0], words[2])), - self.get_student_changes( - *3 * ["pondering"], - look_at_arg=question - ) + self.get_student_changes(*3 * ["pondering"], look_at_arg=question), ) self.play( ShowCreation(h_line), LaggedStartMap( - FadeOutAndShiftDown, self.pi_creatures, - run_time=1, - lag_ratio=0.8 - ) + FadeOutAndShiftDown, self.pi_creatures, run_time=1, lag_ratio=0.8 + ), ) self.wait() @@ -734,10 +740,7 @@ def divide_by_qualitative_quantitative(self): words.next_to(self.h_line, DOWN) words[0].shift(FRAME_WIDTH * LEFT / 4) words[1].shift(FRAME_WIDTH * RIGHT / 4) - self.play( - ShowCreation(v_line), - LaggedStartMap(FadeInFromDown, words) - ) + self.play(ShowCreation(v_line), LaggedStartMap(FadeInFromDown, words)) self.wait() self.words = words @@ -748,10 +751,7 @@ def three_qualitative_descriptors(self): TextMobject("- Chaos"), TextMobject("- Diffusion"), ) - words.arrange( - DOWN, buff=1.25, - aligned_edge=LEFT - ) + words.arrange(DOWN, buff=1.25, aligned_edge=LEFT) words.to_edge(LEFT) words.shift(MED_LARGE_BUFF * DOWN) @@ -801,11 +801,7 @@ def construct(self): time = 0.2 * random.random() total_time += time arc = PI * random.random() - PI / 2 - self.play( - plane.move_to, point, - run_time=time, - path_arc=arc - ) + self.play(plane.move_to, point, run_time=time, path_arc=arc) class PureAirfoilFlowCopy(PureAirfoilFlow): @@ -821,8 +817,7 @@ def construct(self): words.scale(1.5) words.to_edge(UP) subwords = TextMobject( - "`Lamina', in Latin, means \\\\" - "``a thin sheet of material''", + "`Lamina', in Latin, means \\\\" "``a thin sheet of material''", tex_to_color_map={"Lamina": YELLOW}, arg_separator="", ) @@ -840,10 +835,7 @@ class HighCurlFieldBreakingLayers(Scene): } def construct(self): - lines = VGroup(*[ - self.get_line() - for x in range(20) - ]) + lines = VGroup(*[self.get_line() for x in range(20)]) lines.arrange(DOWN, buff=MED_SMALL_BUFF) lines[0::2].set_color(BLUE) lines[1::2].set_color(RED) @@ -853,7 +845,7 @@ def func(p): vect = four_swirls_function(p) norm = get_norm(vect) if norm > 2: - vect *= 4.0 / get_norm(vect)**2 + vect *= 4.0 / get_norm(vect) ** 2 return vect self.add(lines) @@ -869,9 +861,7 @@ def get_line(self): class HighCurlFieldBreakingLayersLines(HighCurlFieldBreakingLayers): - CONFIG = { - "flow_anim": move_points_along_vector_field - } + CONFIG = {"flow_anim": move_points_along_vector_field} def get_line(self): line = Line(LEFT, RIGHT) @@ -884,20 +874,13 @@ class VorticitySynonyms(Scene): def construct(self): words = VGroup( TextMobject("High", "vorticity"), - TexMobject( - "\\text{a.k.a} \\,", - "|\\nabla \\times \\vec{\\textbf{v}}| > 0" - ), + TexMobject("\\text{a.k.a} \\,", "|\\nabla \\times \\vec{\\textbf{v}}| > 0"), TextMobject("a.k.a", "high", "swirly-swirly", "factor"), ) words[0].set_color_by_tex("vorticity", BLUE) words[1].set_color_by_tex("nabla", BLUE) words[2].set_color_by_tex("swirly", BLUE) - words.arrange( - DOWN, - aligned_edge=LEFT, - buff=MED_LARGE_BUFF - ) + words.arrange(DOWN, aligned_edge=LEFT, buff=MED_LARGE_BUFF) for word in words: word.add_background_rectangle() @@ -908,33 +891,36 @@ def construct(self): class VorticityDoesNotImplyTurbulence(TeacherStudentsScene): def construct(self): t_to_v = TextMobject( - "Turbulence", "$\\Rightarrow$", "Vorticity", + "Turbulence", + "$\\Rightarrow$", + "Vorticity", ) v_to_t = TextMobject( - "Vorticity", "$\\Rightarrow$", "Turbulence", + "Vorticity", + "$\\Rightarrow$", + "Turbulence", ) for words in t_to_v, v_to_t: words.move_to(self.hold_up_spot, DR) - words.set_color_by_tex_to_color_map({ - "Vorticity": BLUE, - "Turbulence": GREEN, - }) + words.set_color_by_tex_to_color_map( + { + "Vorticity": BLUE, + "Turbulence": GREEN, + } + ) v_to_t.submobjects.reverse() cross = Cross(v_to_t[1]) morty = self.teacher + self.play(morty.change, "raise_right_hand", FadeInFromDown(t_to_v)) + self.wait() self.play( - morty.change, "raise_right_hand", - FadeInFromDown(t_to_v) + t_to_v.shift, + 2 * UP, ) - self.wait() - self.play(t_to_v.shift, 2 * UP,) self.play( TransformFromCopy(t_to_v, v_to_t, path_arc=PI / 2), - self.get_student_changes( - "erm", "confused", "sassy", - run_time=1 - ), + self.get_student_changes("erm", "confused", "sassy", run_time=1), ShowCreation(cross, run_time=2), ) self.add(cross) @@ -956,8 +942,10 @@ def construct(self): name = TextMobject("Richard Feynman") name.next_to(feynman, DOWN) quote = TextMobject( - "``", "Turbulence", "is the most\\\\" - "important", "unsolved problem\\\\", + "``", + "Turbulence", + "is the most\\\\" "important", + "unsolved problem\\\\", "of classical physics.''", tex_to_color_map={ "Turbulence": BLUE, @@ -969,9 +957,7 @@ def construct(self): Group(feynman, name, quote).center() self.play( - FadeInFrom(feynman, UP), - FadeInFrom(name, DOWN), - Write(quote, run_time=4) + FadeInFrom(feynman, UP), FadeInFrom(name, DOWN), Write(quote, run_time=4) ) self.wait() @@ -991,47 +977,54 @@ def introduce_equations(self): equations.next_to(name, DOWN, MED_LARGE_BUFF) labels = equations.get_labels() parts = equations.get_parts() - newtons_second = TextMobject( - "Newton's 2nd law \\\\ $ma = F$" - ) + newtons_second = TextMobject("Newton's 2nd law \\\\ $ma = F$") newtons_second.next_to(parts, DOWN) variables = TexMobject( - "&\\textbf{v}", "\\text{ is velocity}\\\\", - "&\\rho", "\\text{ is density}\\\\", - "&p{}", "\\text{ is pressure}\\\\", - "&\\mu", "\\text{ is viscosity}\\\\", - tex_to_color_map=NavierStokesEquations.CONFIG["tex_to_color_map"] + "&\\textbf{v}", + "\\text{ is velocity}\\\\", + "&\\rho", + "\\text{ is density}\\\\", + "&p{}", + "\\text{ is pressure}\\\\", + "&\\mu", + "\\text{ is viscosity}\\\\", + tex_to_color_map=NavierStokesEquations.CONFIG["tex_to_color_map"], ) variables.to_corner(DL) self.play(FadeInFromDown(equations)) self.play(Write(name)) - self.play(LaggedStartMap( - FadeInFrom, variables, - lambda m: (m, RIGHT), - )) + self.play( + LaggedStartMap( + FadeInFrom, + variables, + lambda m: (m, RIGHT), + ) + ) self.wait() self.play(Write(newtons_second)) self.wait() self.play( FadeInFromDown(labels[0]), - newtons_second.next_to, variables, RIGHT, LARGE_BUFF + newtons_second.next_to, + variables, + RIGHT, + LARGE_BUFF, ) self.play(ShowCreationThenFadeAround(parts[0])) self.wait() self.play(LaggedStartMap(FadeInFrom, labels[1:])) self.wait(3) - self.play(LaggedStartMap( - FadeOut, VGroup(*it.chain(labels, variables, newtons_second)) - )) + self.play( + LaggedStartMap( + FadeOut, VGroup(*it.chain(labels, variables, newtons_second)) + ) + ) self.equations = equations def ask_about_evolution(self): - words = TextMobject( - "Given a start state...", - "...how does it evolve?" - ) + words = TextMobject("Given a start state...", "...how does it evolve?") words.arrange(RIGHT, buff=2) words.next_to(self.equations, DOWN, LARGE_BUFF) @@ -1043,11 +1036,7 @@ def ask_about_evolution(self): self.play(FadeOut(words)) def ask_about_reasonable(self): - question = TextMobject( - "Do ``reasonable'' \\\\" - "solutions always\\\\" - "exist?" - ) + question = TextMobject("Do ``reasonable'' \\\\" "solutions always\\\\" "exist?") self.play(FadeInFromDown(question)) self.wait() @@ -1063,17 +1052,19 @@ def ask_about_blowup(self): question.get_bottom(), graph.point_from_proportion(0.8), buff=SMALL_BUFF, - path_arc=-60 * DEGREES + path_arc=-60 * DEGREES, ) q_arrow.set_stroke(WHITE, 3) morty = Mortimer() morty.to_corner(DR) - morty.change('confused', graph) + morty.change("confused", graph) self.play( Write(axes, run_time=1), - self.reasonable_question.to_edge, LEFT, - self.reasonable_question.shift, DOWN, + self.reasonable_question.to_edge, + LEFT, + self.reasonable_question.shift, + DOWN, ) self.play( Write(question), @@ -1088,10 +1079,7 @@ def ask_about_blowup(self): self.play(morty.change, "maybe", graph) self.wait(2) to_fade = VGroup(question, q_arrow, axes, graph) - self.play( - LaggedStartMap(FadeOut, to_fade), - morty.change, "pondering" - ) + self.play(LaggedStartMap(FadeOut, to_fade), morty.change, "pondering") self.wait(2) self.play(Blink(morty)) self.wait(2) @@ -1100,9 +1088,7 @@ def ask_about_blowup(self): def show_money(self): # Million dollar problem - problem = TextMobject( - "Navier-Stokes existence \\\\ and smoothness problems" - ) + problem = TextMobject("Navier-Stokes existence \\\\ and smoothness problems") money = TextMobject("\\$1{,}000{,}000") money.set_color(GREEN) money.next_to(problem, DOWN) @@ -1124,15 +1110,19 @@ def show_money(self): self.reasonable_question, problem, ), - pi2.look_at, problem, - pi1.look_at, problem, + pi2.look_at, + problem, + pi1.look_at, + problem, VFadeIn(pi1), ) self.wait() self.play(FadeInFromLarge(money)) self.play( - pi1.change, "hooray", - pi2.change, "hooray", + pi1.change, + "hooray", + pi2.change, + "hooray", ) self.play( ReplacementTransform(pi1.pupils, pi1.money_eyes), @@ -1173,10 +1163,7 @@ def get_axes_and_graph(self): class NewtonsSecond(Scene): def construct(self): square = Square( - stroke_color=WHITE, - fill_color=LIGHT_GREY, - fill_opacity=0.5, - side_length=1 + stroke_color=WHITE, fill_color=LIGHT_GREY, fill_opacity=0.5, side_length=1 ) label = TexMobject("m") label.scale(1.5) @@ -1189,20 +1176,14 @@ def construct(self): ) self.play( - square.shift, 4 * RIGHT + 2 * UP, - rate_func=lambda t: t**2, - run_time=2 + square.shift, 4 * RIGHT + 2 * UP, rate_func=lambda t: t**2, run_time=2 ) self.wait() square.restore() - self.play( - LaggedStartMap(GrowArrow, arrows) - ) + self.play(LaggedStartMap(GrowArrow, arrows)) square.add(arrows) self.play( - square.shift, 4 * RIGHT + 2 * UP, - rate_func=lambda t: t**2, - run_time=2 + square.shift, 4 * RIGHT + 2 * UP, rate_func=lambda t: t**2, run_time=2 ) self.wait() @@ -1212,10 +1193,7 @@ def construct(self): word = TextMobject("Candle") arrow = Vector(DR, color=WHITE) arrow.move_to(word.get_bottom() + SMALL_BUFF * DOWN, UL) - self.play( - FadeInFromDown(word), - GrowArrow(arrow) - ) + self.play(FadeInFromDown(word), GrowArrow(arrow)) self.wait() @@ -1229,10 +1207,9 @@ def construct(self): "Geoffrey Taylor", "Andrey Kolmogorov", ] - images = Group(*[ - ImageMobject(name.replace(" ", "_"), height=3) - for name in names - ]) + images = Group( + *[ImageMobject(name.replace(" ", "_"), height=3) for name in names] + ) images.arrange(RIGHT, buff=MED_SMALL_BUFF) image_groups = Group() for image, name in zip(images, names): @@ -1243,11 +1220,9 @@ def construct(self): image_groups.arrange_in_grid(2, 3) image_groups.set_height(FRAME_HEIGHT - 1) - self.play(LaggedStartMap( - FadeInFromDown, image_groups, - lag_ratio=0.5, - run_time=3 - )) + self.play( + LaggedStartMap(FadeInFromDown, image_groups, lag_ratio=0.5, run_time=3) + ) self.wait() to_fade = image_groups[:-1] to_fade.generate_target() @@ -1256,7 +1231,8 @@ def construct(self): to_fade.target.fade(1) self.play( MoveToTarget(to_fade, remover=True), - image_groups[-1].set_height, 5, + image_groups[-1].set_height, + 5, image_groups[-1].center, ) self.wait() @@ -1285,11 +1261,7 @@ def construct(self): left_items.next_to(lc_title, DOWN, MED_LARGE_BUFF) left_items.to_edge(LEFT) - self.play( - Write(VGroup(*it.chain( - title, h_line, v_line, lc_title, rc_title - ))) - ) + self.play(Write(VGroup(*it.chain(title, h_line, v_line, lc_title, rc_title)))) self.wait() for item in left_items: self.play(FadeInFrom(item)) @@ -1310,30 +1282,28 @@ def construct(self): side_length=2, stroke_color=WHITE, ) - balls = VGroup(*[ - self.get_ball(box) - for x in range(20) - ]) + balls = VGroup(*[self.get_ball(box) for x in range(20)]) self.add(box, balls) self.wait(20) def get_ball(self, box): speed_factor = random.random() - ball = Dot( - radius=0.05, - color=interpolate_color(BLUE, RED, speed_factor) - ) + ball = Dot(radius=0.05, color=interpolate_color(BLUE, RED, speed_factor)) speed = 2 + 3 * speed_factor direction = rotate_vector(RIGHT, TAU * random.random()) ball.velocity = speed * direction x0, y0, z0 = box.get_corner(DL) x1, y1, z1 = box.get_corner(UR) - ball.move_to(np.array([ - interpolate(x0, x1, random.random()), - interpolate(y0, y1, random.random()), - 0 - ])) + ball.move_to( + np.array( + [ + interpolate(x0, x1, random.random()), + interpolate(y0, y1, random.random()), + 0, + ] + ) + ) def update(ball, dt): ball.shift(ball.velocity * dt) @@ -1384,16 +1354,12 @@ def construct(self): if "whirl" in word.get_tex_string(): word.set_color(BLUE) self.play(ShowWord(word)) - self.wait(0.005 * len(word)**1.5) + self.wait(0.005 * len(word) ** 1.5) class SwirlDiameterD(Scene): def construct(self): - kwargs = { - "path_arc": PI, - "buff": SMALL_BUFF, - "color": WHITE - } + kwargs = {"path_arc": PI, "buff": SMALL_BUFF, "color": WHITE} swirl = VGroup( Arrow(RIGHT, LEFT, **kwargs), Arrow(LEFT, RIGHT, **kwargs), @@ -1403,7 +1369,8 @@ def construct(self): swirl.scale(f) h_line = DashedLine( - f * LEFT, f * RIGHT, + f * LEFT, + f * RIGHT, color=YELLOW, ) D_label = TexMobject("D") @@ -1429,7 +1396,7 @@ def construct(self): y_max=9, y_axis_config={ "unit_size": 0.7, - } + }, ) axes.center().shift(1.5 * RIGHT) x_label = TexMobject("\\log(D)") @@ -1440,15 +1407,17 @@ def construct(self): y_label.shift_onto_screen() axes.add(x_label, y_label) - v_lines = VGroup(*[ - DashedLine( - axes.coords_to_point(x, 0), - axes.coords_to_point(x, 9), - color=YELLOW, - stroke_width=1 - ) - for x in [0.5, 5] - ]) + v_lines = VGroup( + *[ + DashedLine( + axes.coords_to_point(x, 0), + axes.coords_to_point(x, 9), + color=YELLOW, + stroke_width=1, + ) + for x in [0.5, 5] + ] + ) inertial_subrange = TextMobject("``Inertial subrange''") inertial_subrange.scale(0.7) inertial_subrange.next_to(v_lines.get_bottom(), UP) @@ -1464,10 +1433,7 @@ def func(x): graph = axes.get_graph(func, x_min=0.3, x_max=7) prop_label = TexMobject("\\text{K.E.} \\propto D^{5/3}") - prop_label.next_to( - graph.point_from_proportion(0.5), UL, - buff=0 - ) + prop_label.next_to(graph.point_from_proportion(0.5), UL, buff=0) self.add(axes) self.play(ShowCreation(graph)) @@ -1485,7 +1451,8 @@ def construct(self): title.set_color(RED) self.add(title) - words = TextMobject(""" + words = TextMobject( + """ This idea of quantifying the energy held at different length scales is typically defined in terms of an ``energy spectrum'' involving the Fourier @@ -1503,7 +1470,9 @@ def construct(self): \\quad\\\\ See the links in the description for more details, if you're curious. - """, alignment="") + """, + alignment="", + ) words.scale(0.75) words.next_to(title, DOWN, LARGE_BUFF) @@ -1520,9 +1489,12 @@ def construct(self): self.play( FadeOut(self.teacher.bubble), FadeOut(words[1]), - self.teacher.change, "raise_right_hand", - words[0].scale, 1.5, - words[0].move_to, self.hold_up_spot + self.teacher.change, + "raise_right_hand", + words[0].scale, + 1.5, + words[0].move_to, + self.hold_up_spot, ) self.change_student_modes("thinking", "pondering", "hooray") self.wait(3) @@ -1533,9 +1505,7 @@ def construct(self): title = TextMobject("Turbulence in 2d") title.to_edge(UP) - attribution = TextMobject( - "Animation by Gabe Weymouth (@gabrielweymouth)" - ) + attribution = TextMobject("Animation by Gabe Weymouth (@gabrielweymouth)") attribution.scale(0.5) attribution.to_edge(DOWN) @@ -1586,37 +1556,35 @@ def construct(self): self.wait(5) self.play(Transform(circles, tall_circles, run_time=3)) self.wait(10) - self.play(Transform( - circles, torus_circles, - run_time=3 - )) + self.play(Transform(circles, torus_circles, run_time=3)) self.wait(10) def get_cylinder_circles(self, radius, radius_var, max_z): - return VGroup(*[ - ParametricFunction( - lambda t: np.array([ - np.cos(TAU * t) * r, - np.sin(TAU * t) * r, - z - ]), - **self.get_circle_kwargs() - ) - for z in sorted(max_z * np.random.random(self.n_circles)) - for r in [radius + radius_var * random.random()] - ]).center() + return VGroup( + *[ + ParametricFunction( + lambda t: np.array([np.cos(TAU * t) * r, np.sin(TAU * t) * r, z]), + **self.get_circle_kwargs(), + ) + for z in sorted(max_z * np.random.random(self.n_circles)) + for r in [radius + radius_var * random.random()] + ] + ).center() def get_torus_circles(self, out_r, in_r, in_r_var): result = VGroup() for u in sorted(np.random.random(self.n_circles)): r = in_r + in_r_var * random.random() circle = ParametricFunction( - lambda t: r * np.array([ - np.cos(TAU * t), - np.sin(TAU * t), - 0, - ]), - **self.get_circle_kwargs() + lambda t: r + * np.array( + [ + np.cos(TAU * t), + np.sin(TAU * t), + 0, + ] + ), + **self.get_circle_kwargs(), ) circle.shift(out_r * RIGHT) circle.rotate( @@ -1633,8 +1601,8 @@ def get_flow_lines(self, circle_group): def update_circle(circle, dt): circle.total_time += dt diameter = get_norm( - circle.template.point_from_proportion(0) - - circle.template.point_from_proportion(0.5) + circle.template.point_from_proportion(0) + - circle.template.point_from_proportion(0.5) ) modulus = np.sqrt(diameter) + 0.1 alpha = (circle.total_time % modulus) / modulus diff --git a/from_3b1b/old/uncertainty.py b/from_3b1b/old/uncertainty.py index f707a3f06a..f2326da15b 100644 --- a/from_3b1b/old/uncertainty.py +++ b/from_3b1b/old/uncertainty.py @@ -5,15 +5,19 @@ from from_3b1b.old.fourier import * import warnings -warnings.warn(""" + +warnings.warn( + """ Warning: This file makes use of ContinualAnimation, which has since been deprecated -""") +""" +) FREQUENCY_COLOR = RED USE_ALMOST_FOURIER_BY_DEFAULT = False + class GaussianDistributionWrapper(Line): """ This is meant to encode a 2d normal distribution as @@ -22,16 +26,18 @@ class GaussianDistributionWrapper(Line): mu of a distribution, and whose radial vector (center to end) is the distribution's standard deviation """ + CONFIG = { - "stroke_width" : 0, - "mu" : ORIGIN, - "sigma" : RIGHT, + "stroke_width": 0, + "mu": ORIGIN, + "sigma": RIGHT, } + def __init__(self, **kwargs): Line.__init__(self, ORIGIN, RIGHT, **kwargs) self.change_parameters(self.mu, self.sigma) - def change_parameters(self, mu = None, sigma = None): + def change_parameters(self, mu=None, sigma=None): curr_mu, curr_sigma = self.get_parameters() mu = mu if mu is not None else curr_mu sigma = sigma if sigma is not None else curr_sigma @@ -39,28 +45,34 @@ def change_parameters(self, mu = None, sigma = None): return self def get_parameters(self): - """ Return mu_x, mu_y, sigma_x, sigma_y""" + """Return mu_x, mu_y, sigma_x, sigma_y""" center, end = self.get_center(), self.get_end() - return center, end-center + return center, end - center - def get_random_points(self, size = 1): + def get_random_points(self, size=1): mu, sigma = self.get_parameters() - return np.array([ - np.array([ - np.random.normal(mu_coord, sigma_coord) - for mu_coord, sigma_coord in zip(mu, sigma) - ]) - for x in range(size) - ]) + return np.array( + [ + np.array( + [ + np.random.normal(mu_coord, sigma_coord) + for mu_coord, sigma_coord in zip(mu, sigma) + ] + ) + for x in range(size) + ] + ) + class ProbabalisticMobjectCloud(ContinualAnimation): CONFIG = { - "fill_opacity" : 0.25, - "n_copies" : 100, - "gaussian_distribution_wrapper_config" : {}, - "time_per_change" : 1./60, - "start_up_time" : 0, + "fill_opacity": 0.25, + "n_copies": 100, + "gaussian_distribution_wrapper_config": {}, + "time_per_change": 1.0 / 60, + "start_up_time": 0, } + def __init__(self, prototype, **kwargs): digest_config(self, kwargs) fill_opacity = self.fill_opacity or prototype.get_fill_opacity() @@ -70,10 +82,12 @@ def __init__(self, prototype, **kwargs): **self.gaussian_distribution_wrapper_config ) self.time_since_last_change = np.inf - group = VGroup(*[ - prototype.copy().set_fill(opacity = fill_opacity) - for x in range(self.n_copies) - ]) + group = VGroup( + *[ + prototype.copy().set_fill(opacity=fill_opacity) + for x in range(self.n_copies) + ] + ) ContinualAnimation.__init__(self, group, **kwargs) self.update_mobject(0) @@ -93,187 +107,200 @@ def update_mobject_by_point(self, mobject, point): mobject.move_to(point) return self + class ProbabalisticDotCloud(ProbabalisticMobjectCloud): CONFIG = { - "color" : BLUE, + "color": BLUE, } + def __init__(self, **kwargs): digest_config(self, kwargs) - dot = Dot(color = self.color) + dot = Dot(color=self.color) ProbabalisticMobjectCloud.__init__(self, dot) + class ProbabalisticVectorCloud(ProbabalisticMobjectCloud): CONFIG = { - "color" : RED, - "n_copies" : 20, - "fill_opacity" : 0.5, - "center_func" : lambda : ORIGIN, + "color": RED, + "n_copies": 20, + "fill_opacity": 0.5, + "center_func": lambda: ORIGIN, } + def __init__(self, **kwargs): digest_config(self, kwargs) vector = Vector( - RIGHT, color = self.color, - max_tip_length_to_length_ratio = 1, + RIGHT, + color=self.color, + max_tip_length_to_length_ratio=1, ) ProbabalisticMobjectCloud.__init__(self, vector) def update_mobject_by_point(self, vector, point): - vector.put_start_and_end_on( - self.center_func(), - point - ) + vector.put_start_and_end_on(self.center_func(), point) + class RadarDish(SVGMobject): CONFIG = { - "file_name" : "radar_dish", - "fill_color" : LIGHT_GREY, - "stroke_color" : WHITE, - "stroke_width" : 1, - "height" : 1, + "file_name": "radar_dish", + "fill_color": LIGHT_GREY, + "stroke_color": WHITE, + "stroke_width": 1, + "height": 1, } + class Plane(SVGMobject): CONFIG = { - "file_name" : "plane", - "color" : LIGHT_GREY, - "height" : 1, + "file_name": "plane", + "color": LIGHT_GREY, + "height": 1, } + def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) - self.rotate(-TAU/4) + self.rotate(-TAU / 4) + class FalconHeavy(SVGMobject): CONFIG = { - "file_name" : "falcon_heavy", - "color" : WHITE, - "logo_color" : BLUE_E, - "height" : 1.5, + "file_name": "falcon_heavy", + "color": WHITE, + "logo_color": BLUE_E, + "height": 1.5, } + def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) self.logo = self[-9:] self.logo.set_color(self.logo_color) + class RadarPulseSingleton(ContinualAnimation): CONFIG = { - "speed" : 3.0, - "direction" : RIGHT, - "start_up_time" : 0, - "fade_in_time" : 0.5, - "color" : WHITE, - "stroke_width" : 3, + "speed": 3.0, + "direction": RIGHT, + "start_up_time": 0, + "fade_in_time": 0.5, + "color": WHITE, + "stroke_width": 3, } + def __init__(self, radar_dish, target, **kwargs): digest_config(self, kwargs) - self.direction = self.direction/get_norm(self.direction) + self.direction = self.direction / get_norm(self.direction) self.radar_dish = radar_dish self.target = target self.reflection_distance = None self.arc = Arc( - start_angle = -30*DEGREES, - angle = 60*DEGREES, + start_angle=-30 * DEGREES, + angle=60 * DEGREES, ) - self.arc.set_height(0.75*radar_dish.get_height()) - self.arc.move_to(radar_dish, UP+RIGHT) + self.arc.set_height(0.75 * radar_dish.get_height()) + self.arc.move_to(radar_dish, UP + RIGHT) self.start_points = np.array(self.arc.points) self.start_center = self.arc.get_center() self.finished = False ContinualAnimation.__init__(self, self.arc, **kwargs) - + def update_mobject(self, dt): arc = self.arc - total_distance = self.speed*self.internal_time + total_distance = self.speed * self.internal_time arc.points = np.array(self.start_points) - arc.shift(total_distance*self.direction) + arc.shift(total_distance * self.direction) if self.internal_time < self.fade_in_time: - alpha = np.clip(self.internal_time/self.fade_in_time, 0, 1) - arc.set_stroke(self.color, alpha*self.stroke_width) + alpha = np.clip(self.internal_time / self.fade_in_time, 0, 1) + arc.set_stroke(self.color, alpha * self.stroke_width) if self.reflection_distance is None: - #Check if reflection is happening + # Check if reflection is happening arc_point = arc.get_edge_center(self.direction) target_point = self.target.get_edge_center(-self.direction) arc_distance = np.dot(arc_point, self.direction) target_distance = np.dot(target_point, self.direction) if arc_distance > target_distance: self.reflection_distance = target_distance - #Don't use elif in case the above code creates reflection_distance + # Don't use elif in case the above code creates reflection_distance if self.reflection_distance is not None: delta_distance = total_distance - self.reflection_distance point_distances = np.dot(self.direction, arc.points.T) diffs = point_distances - self.reflection_distance - shift_vals = np.outer(-2*np.maximum(diffs, 0), self.direction) + shift_vals = np.outer(-2 * np.maximum(diffs, 0), self.direction) arc.points += shift_vals - #Check if done + # Check if done arc_point = arc.get_edge_center(-self.direction) - if np.dot(arc_point, self.direction) < np.dot(self.start_center, self.direction): + if np.dot(arc_point, self.direction) < np.dot( + self.start_center, self.direction + ): self.finished = True self.arc.fade(1) def is_finished(self): return self.finished + class RadarPulse(ContinualAnimation): - CONFIG = { - "n_pulse_singletons" : 8, - "frequency" : 0.05, - "colors" : [BLUE, YELLOW] - } + CONFIG = {"n_pulse_singletons": 8, "frequency": 0.05, "colors": [BLUE, YELLOW]} + def __init__(self, *args, **kwargs): digest_config(self, kwargs) colors = color_gradient(self.colors, self.n_pulse_singletons) self.pulse_singletons = [ - RadarPulseSingleton(*args, color = color, **kwargs) - for color in colors + RadarPulseSingleton(*args, color=color, **kwargs) for color in colors ] pluse_mobjects = VGroup(*[ps.mobject for ps in self.pulse_singletons]) ContinualAnimation.__init__(self, pluse_mobjects, **kwargs) - + def update_mobject(self, dt): for i, ps in enumerate(self.pulse_singletons): - ps.internal_time = self.internal_time - i*self.frequency + ps.internal_time = self.internal_time - i * self.frequency ps.update_mobject(dt) def is_finished(self): return all([ps.is_finished() for ps in self.pulse_singletons]) + class MultipleFlashes(Succession): CONFIG = { - "run_time_per_flash" : 1.0, - "num_flashes" : 3, + "run_time_per_flash": 1.0, + "num_flashes": 3, } + def __init__(self, *args, **kwargs): digest_config(self, kwargs) kwargs["run_time"] = self.run_time_per_flash - Succession.__init__(self, *[ - Flash(*args, **kwargs) - for x in range(self.num_flashes) - ]) + Succession.__init__( + self, *[Flash(*args, **kwargs) for x in range(self.num_flashes)] + ) + class TrafficLight(SVGMobject): CONFIG = { - "file_name" : "traffic_light", - "height" : 0.7, - "post_height" : 2, - "post_width" : 0.05, + "file_name": "traffic_light", + "height": 0.7, + "post_height": 2, + "post_width": 0.05, } + def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) post = Rectangle( - height = self.post_height, - width = self.post_width, - stroke_width = 0, - fill_color = WHITE, - fill_opacity = 1, + height=self.post_height, + width=self.post_width, + stroke_width=0, + fill_color=WHITE, + fill_opacity=1, ) self.move_to(post.get_top(), DOWN) self.add_to_back(post) + ################### + class MentionUncertaintyPrinciple(TeacherStudentsScene): def construct(self): title = TextMobject("Heisenberg Uncertainty Principle") @@ -281,210 +308,216 @@ def construct(self): dot_cloud = ProbabalisticDotCloud() vector_cloud = ProbabalisticVectorCloud( - gaussian_distribution_wrapper_config = {"sigma_x" : 0.2}, - center_func = lambda : dot_cloud.gaussian_distribution_wrapper.get_parameters()[0], + gaussian_distribution_wrapper_config={"sigma_x": 0.2}, + center_func=lambda: dot_cloud.gaussian_distribution_wrapper.get_parameters()[ + 0 + ], ) for cloud in dot_cloud, vector_cloud: - cloud.gaussian_distribution_wrapper.next_to( - title, DOWN, 2*LARGE_BUFF - ) - vector_cloud.gaussian_distribution_wrapper.shift(3*RIGHT) + cloud.gaussian_distribution_wrapper.next_to(title, DOWN, 2 * LARGE_BUFF) + vector_cloud.gaussian_distribution_wrapper.shift(3 * RIGHT) def get_brace_text_group_update(gdw, vect, text, color): brace = Brace(gdw, vect) - text = brace.get_tex("2\\sigma_{\\text{%s}}"%text, buff = SMALL_BUFF) + text = brace.get_tex("2\\sigma_{\\text{%s}}" % text, buff=SMALL_BUFF) group = VGroup(brace, text) + def update_group(group): brace, text = group - brace.match_width(gdw, stretch = True) + brace.match_width(gdw, stretch=True) brace.next_to(gdw, vect) - text.next_to(brace, vect, buff = SMALL_BUFF) + text.next_to(brace, vect, buff=SMALL_BUFF) + group.set_color(color) return Mobject.add_updater(group, update_group) dot_brace_anim = get_brace_text_group_update( - dot_cloud.gaussian_distribution_wrapper, - DOWN, "position", dot_cloud.color + dot_cloud.gaussian_distribution_wrapper, DOWN, "position", dot_cloud.color ) vector_brace_anim = get_brace_text_group_update( vector_cloud.gaussian_distribution_wrapper, - UP, "momentum", vector_cloud.color + UP, + "momentum", + vector_cloud.color, ) self.add(title) self.add(dot_cloud) self.play( Write(title), - self.teacher.change, "raise_right_hand", - self.get_student_changes(*["pondering"]*3) - ) - self.play( - Write(dot_brace_anim.mobject, run_time = 1) + self.teacher.change, + "raise_right_hand", + self.get_student_changes(*["pondering"] * 3), ) + self.play(Write(dot_brace_anim.mobject, run_time=1)) self.add(dot_brace_anim) self.wait() # self.wait(2) self.play( - dot_cloud.gaussian_distribution_wrapper.change_parameters, - {"sigma" : 0.1*RIGHT}, - run_time = 2, + dot_cloud.gaussian_distribution_wrapper.change_parameters, + {"sigma": 0.1 * RIGHT}, + run_time=2, ) self.wait() self.add(vector_cloud) - self.play( - FadeIn(vector_brace_anim.mobject) - ) + self.play(FadeIn(vector_brace_anim.mobject)) self.add(vector_brace_anim) self.play( vector_cloud.gaussian_distribution_wrapper.change_parameters, - {"sigma" : RIGHT}, - self.get_student_changes(*3*["confused"]), - run_time = 3, + {"sigma": RIGHT}, + self.get_student_changes(*3 * ["confused"]), + run_time=3, ) - #Back and forth + # Back and forth for x in range(2): self.play( dot_cloud.gaussian_distribution_wrapper.change_parameters, - {"sigma" : 2*RIGHT}, + {"sigma": 2 * RIGHT}, vector_cloud.gaussian_distribution_wrapper.change_parameters, - {"sigma" : 0.1*RIGHT}, - run_time = 3, + {"sigma": 0.1 * RIGHT}, + run_time=3, ) self.change_student_modes("thinking", "erm", "sassy") self.play( dot_cloud.gaussian_distribution_wrapper.change_parameters, - {"sigma" : 0.1*RIGHT}, + {"sigma": 0.1 * RIGHT}, vector_cloud.gaussian_distribution_wrapper.change_parameters, - {"sigma" : 1*RIGHT}, - run_time = 3, + {"sigma": 1 * RIGHT}, + run_time=3, ) self.wait() + class FourierTradeoff(Scene): CONFIG = { - "show_text" : True, - "complex_to_real_func" : lambda z : z.real, - "widths" : [6, 0.02, 1], + "show_text": True, + "complex_to_real_func": lambda z: z.real, + "widths": [6, 0.02, 1], } + def construct(self): - #Setup axes + # Setup axes time_mean = 4 time_axes = Axes( - x_min = 0, - x_max = 2*time_mean, - x_axis_config = {"unit_size" : 1.5}, - y_min = -2, - y_max = 2, - y_axis_config = {"unit_size" : 0.5} + x_min=0, + x_max=2 * time_mean, + x_axis_config={"unit_size": 1.5}, + y_min=-2, + y_max=2, + y_axis_config={"unit_size": 0.5}, ) time_label = TextMobject("Time") time_label.scale(1.5) time_label.next_to( - time_axes.x_axis.get_right(), UP+LEFT, - buff = MED_SMALL_BUFF, + time_axes.x_axis.get_right(), + UP + LEFT, + buff=MED_SMALL_BUFF, ) time_axes.add(time_label) time_axes.center().to_edge(UP) - time_axes.x_axis.add_numbers(*list(range(1, 2*time_mean))) + time_axes.x_axis.add_numbers(*list(range(1, 2 * time_mean))) frequency_axes = Axes( - x_min = 0, - x_max = 8, - x_axis_config = {"unit_size" : 1.5}, - y_min = -0.025, - y_max = 0.075, - y_axis_config = { - "unit_size" : 30, - "tick_frequency" : 0.025, + x_min=0, + x_max=8, + x_axis_config={"unit_size": 1.5}, + y_min=-0.025, + y_max=0.075, + y_axis_config={ + "unit_size": 30, + "tick_frequency": 0.025, }, - color = TEAL, + color=TEAL, ) frequency_label = TextMobject("Frequency") frequency_label.scale(1.5) frequency_label.next_to( - frequency_axes.x_axis.get_right(), UP+LEFT, - buff = MED_SMALL_BUFF, + frequency_axes.x_axis.get_right(), + UP + LEFT, + buff=MED_SMALL_BUFF, ) frequency_label.set_color(FREQUENCY_COLOR) frequency_axes.add(frequency_label) frequency_axes.move_to(time_axes, LEFT) - frequency_axes.to_edge(DOWN, buff = LARGE_BUFF) + frequency_axes.to_edge(DOWN, buff=LARGE_BUFF) frequency_axes.x_axis.add_numbers() # Graph information - #x-coordinate of this point determines width of wave_packet graph + # x-coordinate of this point determines width of wave_packet graph width_tracker = ExponentialValueTracker(0.5) get_width = width_tracker.get_value def get_wave_packet_function(): - factor = 1./get_width() - return lambda t : (factor**0.25)*np.cos(4*TAU*t)*np.exp(-factor*(t-time_mean)**2) + factor = 1.0 / get_width() + return ( + lambda t: (factor**0.25) + * np.cos(4 * TAU * t) + * np.exp(-factor * (t - time_mean) ** 2) + ) def get_wave_packet(): graph = time_axes.get_graph( get_wave_packet_function(), - num_graph_points = 200, + num_graph_points=200, ) graph.set_color(YELLOW) return graph time_radius = 10 + def get_wave_packet_fourier_transform(): return get_fourier_graph( - frequency_axes, + frequency_axes, get_wave_packet_function(), - t_min = time_mean - time_radius, - t_max = time_mean + time_radius, - n_samples = 2*time_radius*17, - complex_to_real_func = self.complex_to_real_func, - color = FREQUENCY_COLOR, + t_min=time_mean - time_radius, + t_max=time_mean + time_radius, + n_samples=2 * time_radius * 17, + complex_to_real_func=self.complex_to_real_func, + color=FREQUENCY_COLOR, ) wave_packet = get_wave_packet() wave_packet_update = UpdateFromFunc( - wave_packet, - lambda g : Transform(g, get_wave_packet()).update(1) + wave_packet, lambda g: Transform(g, get_wave_packet()).update(1) ) fourier_graph = get_wave_packet_fourier_transform() fourier_graph_update = UpdateFromFunc( - fourier_graph, - lambda g : Transform(g, get_wave_packet_fourier_transform()).update(1) + fourier_graph, + lambda g: Transform(g, get_wave_packet_fourier_transform()).update(1), ) arrow = Arrow( - wave_packet, frequency_axes.coords_to_point( - 4, frequency_axes.y_max/2, + wave_packet, + frequency_axes.coords_to_point( + 4, + frequency_axes.y_max / 2, ), - color = FREQUENCY_COLOR, + color=FREQUENCY_COLOR, ) fourier_words = TextMobject("Fourier Transform") - fourier_words.next_to(arrow, LEFT, buff = MED_LARGE_BUFF) + fourier_words.next_to(arrow, LEFT, buff=MED_LARGE_BUFF) sub_words = TextMobject("(To be explained shortly)") sub_words.set_color(BLUE) sub_words.scale(0.75) sub_words.next_to(fourier_words, DOWN) - #Draw items + # Draw items self.add(time_axes, frequency_axes) - self.play(ShowCreation(wave_packet, rate_func = double_smooth)) - anims = [ReplacementTransform( - wave_packet.copy(), fourier_graph - )] + self.play(ShowCreation(wave_packet, rate_func=double_smooth)) + anims = [ReplacementTransform(wave_packet.copy(), fourier_graph)] if self.show_text: - anims += [ - GrowArrow(arrow), - Write(fourier_words, run_time = 1) - ] + anims += [GrowArrow(arrow), Write(fourier_words, run_time=1)] self.play(*anims) # self.play(FadeOut(arrow)) self.wait() for width in self.widths: self.play( - width_tracker.set_value, width, + width_tracker.set_value, + width, wave_packet_update, fourier_graph_update, - run_time = 3 + run_time=3, ) if sub_words not in self.mobjects and self.show_text: self.play(FadeIn(sub_words)) @@ -492,6 +525,7 @@ def get_wave_packet_fourier_transform(): self.wait() self.wait() + class ShowPlan(PiCreatureScene): def construct(self): self.add_title() @@ -519,9 +553,9 @@ def get_words(self): word = TextMobject("Uncertainty for", topic, tradeoff) word[1:].set_color(color) word[2].scale(0.75) - word[2].next_to(word[1], DOWN, buff = 1.5*SMALL_BUFF) + word[2].next_to(word[1], DOWN, buff=1.5 * SMALL_BUFF) words.add(word) - words.arrange(DOWN, aligned_edge = LEFT, buff = MED_LARGE_BUFF) + words.arrange(DOWN, aligned_edge=LEFT, buff=MED_LARGE_BUFF) words.to_edge(LEFT) return words @@ -529,35 +563,30 @@ def get_words(self): def play_sound_anims(self, word): morty = self.pi_creature wave = FunctionGraph( - lambda x : 0.3*np.sin(15*x)*np.sin(0.5*x), - x_min = 0, x_max = 30, - step_size = 0.001, + lambda x: 0.3 * np.sin(15 * x) * np.sin(0.5 * x), + x_min=0, + x_max=30, + step_size=0.001, ) wave.next_to(word, RIGHT) - rect = BackgroundRectangle(wave, fill_opacity = 1) + rect = BackgroundRectangle(wave, fill_opacity=1) rect.stretch(2, 1) - rect.next_to(wave, LEFT, buff = 0) + rect.next_to(wave, LEFT, buff=0) always_shift(wave, direction=LEFT, rate=5) - wave_fader = UpdateFromAlphaFunc( - wave, - lambda w, a : w.set_stroke(width = 3*a) - ) + wave_fader = UpdateFromAlphaFunc(wave, lambda w, a: w.set_stroke(width=3 * a)) checkmark = self.get_checkmark(word) self.add(wave) self.add_foreground_mobjects(rect, word) - self.play( - Animation(word), - wave_fader, - morty.change, "raise_right_hand", word - ) + self.play(Animation(word), wave_fader, morty.change, "raise_right_hand", word) self.wait(2) - wave_fader.rate_func = lambda a : 1-smooth(a) + wave_fader.rate_func = lambda a: 1 - smooth(a) self.add_foreground_mobjects(checkmark) self.play( Write(checkmark), - morty.change, "happy", - wave_fader, + morty.change, + "happy", + wave_fader, ) self.remove_foreground_mobjects(rect, word) self.add(word) @@ -567,11 +596,11 @@ def play_doppler_anims(self, word): morty = self.pi_creature radar_dish = RadarDish() - radar_dish.next_to(word, DOWN, aligned_edge = LEFT) + radar_dish.next_to(word, DOWN, aligned_edge=LEFT) target = Plane() # target.match_height(radar_dish) - target.next_to(radar_dish, RIGHT, buff = LARGE_BUFF) - always_shift(target, direction = RIGHT, rate = 1.25) + target.next_to(radar_dish, RIGHT, buff=LARGE_BUFF) + always_shift(target, direction=RIGHT, rate=1.25) pulse = RadarPulse(radar_dish, target) @@ -581,26 +610,21 @@ def play_doppler_anims(self, word): self.play( Write(word), DrawBorderThenFill(radar_dish), - UpdateFromAlphaFunc( - target, lambda m, a : m.set_fill(opacity = a) - ), - morty.change, "pondering", - run_time = 1 + UpdateFromAlphaFunc(target, lambda m, a: m.set_fill(opacity=a)), + morty.change, + "pondering", + run_time=1, ) self.add(pulse) - count = it.count() #TODO, this is not a great hack... + count = it.count() # TODO, this is not a great hack... while not pulse.is_finished() and next(count) < 15: - self.play( - morty.look_at, pulse.mobject, - run_time = 0.5 - ) + self.play(morty.look_at, pulse.mobject, run_time=0.5) self.play( Write(checkmark), - UpdateFromAlphaFunc( - target, lambda m, a : m.set_fill(opacity = 1-a) - ), + UpdateFromAlphaFunc(target, lambda m, a: m.set_fill(opacity=1 - a)), FadeOut(radar_dish), - morty.change, "happy" + morty.change, + "happy", ) self.wait() @@ -609,11 +633,10 @@ def play_quantum_anims(self, word): dot_cloud = ProbabalisticDotCloud() gdw = dot_cloud.gaussian_distribution_wrapper gdw.next_to(word, DOWN, MED_LARGE_BUFF) - gdw.rotate(5*DEGREES) + gdw.rotate(5 * DEGREES) gdw.save_state() gdw.scale(0) - checkmark = self.get_checkmark(word) ish = TextMobject("$\\dots$ish") ish.next_to(checkmark, RIGHT, -SMALL_BUFF, DOWN) @@ -622,44 +645,44 @@ def play_quantum_anims(self, word): self.play( Write(word), FadeIn(dot_cloud.mobject), - morty.change, "confused", + morty.change, + "confused", ) - self.play(gdw.restore, run_time = 2) + self.play(gdw.restore, run_time=2) self.play(Write(checkmark)) self.wait() - self.play( - Write(ish), - morty.change, 'maybe' - ) + self.play(Write(ish), morty.change, "maybe") self.wait(6) - ## def get_checkmark(self, word): checkmark = TexMobject("\\checkmark") checkmark.set_color(GREEN) checkmark.scale(1.25) - checkmark.next_to(word[1], UP+RIGHT, buff = 0) + checkmark.next_to(word[1], UP + RIGHT, buff=0) return checkmark + class StartWithIntuition(TeacherStudentsScene): def construct(self): self.teacher_says( "You already \\\\ have this \\\\ intuition", - bubble_kwargs = { - "height" : 3.5, - "width" : 3, + bubble_kwargs={ + "height": 3.5, + "width": 3, }, ) self.change_student_modes("pondering", "erm", "maybe") - self.look_at(VectorizedPoint(4*LEFT + 2*UP)) + self.look_at(VectorizedPoint(4 * LEFT + 2 * UP)) self.wait(5) + class TwoCarsAtRedLight(Scene): CONFIG = { - "text_scale_val" : 0.75, + "text_scale_val": 0.75, } + def construct(self): self.pull_up_behind() self.flash_in_sync_short_time() @@ -668,66 +691,60 @@ def construct(self): self.show_high_confidence() def pull_up_behind(self): - #Setup Traffic light + # Setup Traffic light traffic_light = TrafficLight() - traffic_light.move_to(6*RIGHT + 2.5*DOWN, DOWN) - source_point = VectorizedPoint( - traffic_light[2].get_right() - ) + traffic_light.move_to(6 * RIGHT + 2.5 * DOWN, DOWN) + source_point = VectorizedPoint(traffic_light[2].get_right()) screen = Line(ORIGIN, UP) screen.next_to(source_point, RIGHT, LARGE_BUFF) red_light = Spotlight( - color = RED, - source_point = source_point, - radius = 0.5, - screen = screen, - num_levels = 20, - opacity_function = lambda r : 1/(10*r**2+1) + color=RED, + source_point=source_point, + radius=0.5, + screen=screen, + num_levels=20, + opacity_function=lambda r: 1 / (10 * r**2 + 1), ) red_light.fade(0.5) - red_light.rotate(TAU/2, about_edge = LEFT) + red_light.rotate(TAU / 2, about_edge=LEFT) self.add(red_light, traffic_light) - #Setup cars - car1, car2 = cars = self.cars = VGroup(*[ - Car() for x in range(2) - ]) - cars.arrange(RIGHT, buff = LARGE_BUFF) - cars.next_to( - traffic_light, LEFT, - buff = LARGE_BUFF, aligned_edge = DOWN - ) + # Setup cars + car1, car2 = cars = self.cars = VGroup(*[Car() for x in range(2)]) + cars.arrange(RIGHT, buff=LARGE_BUFF) + cars.next_to(traffic_light, LEFT, buff=LARGE_BUFF, aligned_edge=DOWN) car2.pi_creature.set_color(GREY_BROWN) - car1.start_point = car1.get_corner(DOWN+RIGHT) - car1.shift(FRAME_X_RADIUS*LEFT) + car1.start_point = car1.get_corner(DOWN + RIGHT) + car1.shift(FRAME_X_RADIUS * LEFT) - #Pull up car + # Pull up car self.add(cars) self.play( SwitchOn( - red_light, - rate_func = squish_rate_func(smooth, 0, 0.3), + red_light, + rate_func=squish_rate_func(smooth, 0, 0.3), ), Animation(traffic_light), - self.get_flashes(car2, num_flashes = 3), + self.get_flashes(car2, num_flashes=3), MoveCar( - car1, car1.start_point, - run_time = 3, - rate_func = rush_from, - ) + car1, + car1.start_point, + run_time=3, + rate_func=rush_from, + ), ) def flash_in_sync_short_time(self): car1, car2 = cars = self.cars - #Setup axes + # Setup axes axes = Axes( - x_min = 0, - x_max = 5, - y_min = 0, - y_max = 2, - y_axis_config = { - "tick_frequency" : 0.5, + x_min=0, + x_max=5, + y_min=0, + y_max=2, + y_axis_config={ + "tick_frequency": 0.5, }, ) axes.x_axis.add_numbers(1, 2, 3) @@ -738,65 +755,68 @@ def flash_in_sync_short_time(self): y_title.scale(self.text_scale_val) y_title.next_to(axes.y_axis, UP, SMALL_BUFF) axes.add(time_label, y_title) - axes.to_corner(UP+LEFT, buff = MED_SMALL_BUFF) + axes.to_corner(UP + LEFT, buff=MED_SMALL_BUFF) graph = axes.get_graph( self.get_multispike_function(list(range(1, 4))), - x_min = 0.8, - x_max = 3.8, + x_min=0.8, + x_max=3.8, ) graph.set_color(YELLOW) - #Label short duration - brace = Brace(Line( - axes.input_to_graph_point(1, graph), - axes.input_to_graph_point(3, graph), - ), UP) + # Label short duration + brace = Brace( + Line( + axes.input_to_graph_point(1, graph), + axes.input_to_graph_point(3, graph), + ), + UP, + ) text = TextMobject("Short duration observation") text.scale(self.text_scale_val) text.next_to(brace, UP, SMALL_BUFF) - text.align_to( - axes.coords_to_point(0.25, 0), LEFT - ) - + text.align_to(axes.coords_to_point(0.25, 0), LEFT) self.play( - self.get_flashes(car1, num_flashes = 2), - self.get_flashes(car2, num_flashes = 2), - LaggedStartMap(FadeIn, VGroup( - axes, time_label, y_title, - )) + self.get_flashes(car1, num_flashes=2), + self.get_flashes(car2, num_flashes=2), + LaggedStartMap( + FadeIn, + VGroup( + axes, + time_label, + y_title, + ), + ), ) self.play( - self.get_flashes(car1, num_flashes = 3), - self.get_flashes(car2, num_flashes = 3), - ShowCreation(graph, rate_func=linear, run_time = 3) + self.get_flashes(car1, num_flashes=3), + self.get_flashes(car2, num_flashes=3), + ShowCreation(graph, rate_func=linear, run_time=3), ) self.play( - self.get_flashes(car1, num_flashes = 10), - self.get_flashes(car2, num_flashes = 10, run_time_per_flash = 0.98), + self.get_flashes(car1, num_flashes=10), + self.get_flashes(car2, num_flashes=10, run_time_per_flash=0.98), GrowFromCenter(brace), Write(text), ) self.time_axes = axes self.time_graph = graph - self.time_graph_label = VGroup( - brace, text - ) + self.time_graph_label = VGroup(brace, text) def show_low_confidence(self): car1, car2 = cars = self.cars time_axes = self.time_axes - #Setup axes + # Setup axes frequency_axes = Axes( - x_min = 0, - x_max = 3, - y_min = 0, - y_max = 1.5, - y_axis_config = { - "tick_frequency" : 0.5, - } + x_min=0, + x_max=3, + y_min=0, + y_max=1.5, + y_axis_config={ + "tick_frequency": 0.5, + }, ) frequency_axes.next_to(time_axes, DOWN, LARGE_BUFF) frequency_axes.set_color(LIGHT_GREY) @@ -804,47 +824,35 @@ def show_low_confidence(self): frequency_label.scale(self.text_scale_val) frequency_label.next_to(frequency_axes.x_axis.get_right(), DOWN) frequency_axes.add( - frequency_label, - VectorizedPoint(frequency_axes.y_axis.get_top()) + frequency_label, VectorizedPoint(frequency_axes.y_axis.get_top()) ) frequency_axes.x_axis.add_numbers(1, 2) frequency_graph = frequency_axes.get_graph( - lambda x : np.exp(-4*(x-1)**2), - x_min = 0, - x_max = 2, + lambda x: np.exp(-4 * (x - 1) ** 2), + x_min=0, + x_max=2, ) frequency_graph.set_color(RED) - peak_point = frequency_axes.input_to_graph_point( - 1, frequency_graph - ) + peak_point = frequency_axes.input_to_graph_point(1, frequency_graph) - #Setup label + # Setup label label = TextMobject("Low confidence") label.scale(self.text_scale_val) - label.move_to(peak_point + UP+RIGHT, DOWN) + label.move_to(peak_point + UP + RIGHT, DOWN) label.match_color(frequency_graph) - arrow = Arrow(label.get_bottom(), peak_point, buff = 2*SMALL_BUFF) + arrow = Arrow(label.get_bottom(), peak_point, buff=2 * SMALL_BUFF) arrow.match_color(frequency_graph) self.play( - ReplacementTransform( - self.time_axes.copy(), frequency_axes - ), - ReplacementTransform( - self.time_graph.copy(), frequency_graph - ), - ) - self.play( - Write(label), - GrowArrow(arrow) + ReplacementTransform(self.time_axes.copy(), frequency_axes), + ReplacementTransform(self.time_graph.copy(), frequency_graph), ) + self.play(Write(label), GrowArrow(arrow)) self.wait() self.frequency_axes = frequency_axes self.frequency_graph = frequency_graph - self.frequency_graph_label = VGroup( - label, arrow - ) + self.frequency_graph_label = VGroup(label, arrow) def flash_in_sync_long_time(self): time_graph = self.time_graph @@ -854,17 +862,17 @@ def flash_in_sync_long_time(self): n_spikes = 12 new_time_graph = time_axes.get_graph( - self.get_multispike_function(list(range(1, n_spikes+1))), - x_min = 0.8, - x_max = n_spikes + 0.8, + self.get_multispike_function(list(range(1, n_spikes + 1))), + x_min=0.8, + x_max=n_spikes + 0.8, ) new_time_graph.match_color(time_graph) new_frequency_graph = frequency_axes.get_graph( - lambda x : np.exp(-500*(x-1)**2), - x_min = 0, - x_max = 2, - num_anchors = 500, + lambda x: np.exp(-500 * (x - 1) ** 2), + x_min=0, + x_max=2, + num_anchors=500, ) new_frequency_graph.match_color(self.frequency_graph) @@ -872,48 +880,49 @@ def pin_freq_graph_end_points(freq_graph): freq_graph.points[0] = frequency_axes.coords_to_point(0, 0) freq_graph.points[-1] = frequency_axes.coords_to_point(2, 0) - self.play(LaggedStartMap( - FadeOut, VGroup( - self.time_graph_label, - self.frequency_graph_label, - self.time_graph, + self.play( + LaggedStartMap( + FadeOut, + VGroup( + self.time_graph_label, + self.frequency_graph_label, + self.time_graph, + ), ) - )) + ) self.play( ApplyMethod( - self.time_axes.x_axis.stretch, 2.5, 0, - {"about_edge" : LEFT}, - run_time = 4, - rate_func = squish_rate_func(smooth, 0.3, 0.6), + self.time_axes.x_axis.stretch, + 2.5, + 0, + {"about_edge": LEFT}, + run_time=4, + rate_func=squish_rate_func(smooth, 0.3, 0.6), ), UpdateFromFunc( self.time_axes.x_axis.tip, - lambda m : m.move_to( - self.time_axes.x_axis.get_right(), - LEFT - ) + lambda m: m.move_to(self.time_axes.x_axis.get_right(), LEFT), ), ShowCreation( new_time_graph, - run_time = n_spikes, + run_time=n_spikes, rate_func=linear, ), ApplyMethod( - frequency_graph.stretch, 0.1, 0, - run_time = n_spikes, + frequency_graph.stretch, + 0.1, + 0, + run_time=n_spikes, ), UpdateFromFunc(frequency_graph, pin_freq_graph_end_points), - *[ - self.get_flashes(car, num_flashes = n_spikes) - for car in self.cars - ] + *[self.get_flashes(car, num_flashes=n_spikes) for car in self.cars] ) self.new_time_graph = new_time_graph self.new_frequency_graph = new_frequency_graph def show_high_confidence(self): - #Frequency stuff + # Frequency stuff arrow = self.frequency_graph_label[1] label = TextMobject("High confidence") label.scale(self.text_scale_val) @@ -922,41 +931,36 @@ def show_high_confidence(self): frequency_axes = self.frequency_axes - #Time stuff + # Time stuff new_time_graph = self.new_time_graph - brace = Brace(new_time_graph, UP, buff = SMALL_BUFF) + brace = Brace(new_time_graph, UP, buff=SMALL_BUFF) text = TextMobject("Long duration observation") text.scale(self.text_scale_val) - text.next_to(brace, UP, buff = SMALL_BUFF) + text.next_to(brace, UP, buff=SMALL_BUFF) self.play( - FadeIn(label), - GrowArrow(arrow), - *list(map(self.get_flashes, self.cars)) + FadeIn(label), GrowArrow(arrow), *list(map(self.get_flashes, self.cars)) ) self.play( GrowFromCenter(brace), - Write(text, run_time = 1), + Write(text, run_time=1), *list(map(self.get_flashes, self.cars)) ) - self.play(*[ - self.get_flashes(car, num_flashes = 10) - for car in self.cars - ]) + self.play(*[self.get_flashes(car, num_flashes=10) for car in self.cars]) ### - def get_flashes(self, car, colors = [YELLOW, RED], num_flashes = 1, **kwargs): - return AnimationGroup(*[ - MultipleFlashes(light, color, num_flashes = num_flashes, **kwargs) - for light, color in zip(car.get_lights(), colors) - ]) + def get_flashes(self, car, colors=[YELLOW, RED], num_flashes=1, **kwargs): + return AnimationGroup( + *[ + MultipleFlashes(light, color, num_flashes=num_flashes, **kwargs) + for light, color in zip(car.get_lights(), colors) + ] + ) def get_multispike_function(self, spike_times): - return lambda x : sum([ - 1.25*np.exp(-100*(x-m)**2) - for m in spike_times - ]) + return lambda x: sum([1.25 * np.exp(-100 * (x - m) ** 2) for m in spike_times]) + class VariousMusicalNotes(Scene): def construct(self): @@ -965,23 +969,29 @@ def construct(self): # where the bell curve component of the signal # is exp(-a*(x**2)) graph_width_tracker = ExponentialValueTracker(1) + def get_graph(): a = graph_width_tracker.get_value() return FunctionGraph( - lambda x : np.exp(-a*x**2)*np.sin(freq*x)-0.5, - step_size = 0.001, + lambda x: np.exp(-a * x**2) * np.sin(freq * x) - 0.5, + step_size=0.001, ) + graph = get_graph() + def graph_update(graph): graph.points = get_graph().points + graph_update_anim = UpdateFromFunc(graph, graph_update) + def change_width_anim(width, **kwargs): - a = 2.0/(width**2) + a = 2.0 / (width**2) return AnimationGroup( ApplyMethod(graph_width_tracker.set_value, a), graph_update_anim, **kwargs ) + change_width_anim(FRAME_X_RADIUS).update(1) graph_update_anim.update(0) @@ -994,78 +1004,80 @@ def change_width_anim(width, **kwargs): ] ] - - #Show graphs and phrases + # Show graphs and phrases widths = [FRAME_X_RADIUS, 1, 0.2] for width, phrase in zip(widths, phrases): brace = Brace(Line(LEFT, RIGHT), UP) brace.stretch(width, 0) - brace.next_to(graph.get_center(), UP, buff = 1.2) + brace.next_to(graph.get_center(), UP, buff=1.2) phrase.next_to(brace, UP) if width is widths[0]: self.play(ShowCreation(graph, rate_func=linear)), - self.play( - GrowFromCenter(brace), - Write(phrase, run_time = 1) - ) + self.play(GrowFromCenter(brace), Write(phrase, run_time=1)) else: self.play( change_width_anim(width), ReplacementTransform( VGroup(last_phrase, last_brace), VGroup(phrase, brace), - rate_func = squish_rate_func(smooth, 0.5, 1), + rate_func=squish_rate_func(smooth, 0.5, 1), ), - run_time = 2 + run_time=2, ) self.wait() # self.play(*map(FadeOut, [graph, brace, phrase])) last_phrase = phrase last_brace = brace - #Talk about correlations + # Talk about correlations short_signal_words = TextMobject( - "Short", "signal", "correlates", - "with", "wide range", "of frequencies" + "Short", "signal", "correlates", "with", "wide range", "of frequencies" ) long_signal_words = TextMobject( - "Only", "wide", "signals", "correlate", - "with a", "short range", "of frequencies" + "Only", + "wide", + "signals", + "correlate", + "with a", + "short range", + "of frequencies", ) phrases = VGroup(short_signal_words, long_signal_words) for phrase in phrases: phrase.scale(0.8) - phrase.set_color_by_tex_to_color_map({ - "short" : RED, - "long" : GREEN, - "wide" : GREEN, - }, case_sensitive = False) + phrase.set_color_by_tex_to_color_map( + { + "short": RED, + "long": GREEN, + "wide": GREEN, + }, + case_sensitive=False, + ) phrases.arrange(DOWN) phrases.to_edge(UP) long_graph = FunctionGraph( - lambda x : 0.5*np.sin(freq*x), - x_min = -FRAME_WIDTH, - x_max = FRAME_WIDTH, - n_components = 0.001 + lambda x: 0.5 * np.sin(freq * x), + x_min=-FRAME_WIDTH, + x_max=FRAME_WIDTH, + n_components=0.001, ) long_graph.set_color(BLUE) long_graph.next_to(graph, UP, MED_LARGE_BUFF) self.play( - ShowCreation(long_graph), - *list(map(FadeOut, [last_brace, last_phrase])) + ShowCreation(long_graph), *list(map(FadeOut, [last_brace, last_phrase])) ) + self.play(Write(short_signal_words), change_width_anim(widths[2])) self.play( - Write(short_signal_words), - change_width_anim(widths[2]) - ) - self.play( - long_graph.stretch, 0.35, 0, - long_graph.set_color, GREEN, - run_time = 5, - rate_func = wiggle + long_graph.stretch, + 0.35, + 0, + long_graph.set_color, + GREEN, + run_time=5, + rate_func=wiggle, ) self.wait() self.play( @@ -1073,13 +1085,17 @@ def change_width_anim(width, **kwargs): change_width_anim(widths[0]), ) self.play( - long_graph.stretch, 0.95, 0, - long_graph.set_color, average_color(GREEN, BLUE), - run_time = 4, - rate_func = wiggle + long_graph.stretch, + 0.95, + 0, + long_graph.set_color, + average_color(GREEN, BLUE), + run_time=4, + rate_func=wiggle, ) self.wait() + class CrossOutDefinitenessAndCertainty(TeacherStudentsScene): def construct(self): words = VGroup( @@ -1087,59 +1103,54 @@ def construct(self): TextMobject("Certainty"), ) words.arrange(DOWN) - words.next_to(self.teacher, UP+LEFT) + words.next_to(self.teacher, UP + LEFT) crosses = VGroup(*list(map(Cross, words))) self.add(words) - self.play( - self.teacher.change, "sassy", - ShowCreation(crosses[0]) - ) - self.play( - self.get_student_changes(*3*["erm"]), - ShowCreation(crosses[1]) - ) + self.play(self.teacher.change, "sassy", ShowCreation(crosses[0])) + self.play(self.get_student_changes(*3 * ["erm"]), ShowCreation(crosses[1])) self.wait(2) + class BringInFourierTranform(TeacherStudentsScene): def construct(self): fourier = TextMobject("Fourier") fourier.scale(1.5) - fourier.next_to(self.teacher.get_corner(UP+LEFT), UP, LARGE_BUFF) + fourier.next_to(self.teacher.get_corner(UP + LEFT), UP, LARGE_BUFF) fourier.save_state() fourier.shift(DOWN) fourier.fade(1) - self.play( - self.teacher.change, "raise_right_hand", - fourier.restore - ) + self.play(self.teacher.change, "raise_right_hand", fourier.restore) self.change_student_modes("happy", "erm", "confused") - self.look_at(3*LEFT + 2*UP) + self.look_at(3 * LEFT + 2 * UP) self.wait(3) + class LastVideoWrapper(Scene): def construct(self): title = TextMobject("Visualizing the Fourier Transform") title.to_edge(UP) - screen_rect = ScreenRectangle(height = 6) + screen_rect = ScreenRectangle(height=6) screen_rect.next_to(title, DOWN) self.add(title) self.play(ShowCreation(screen_rect)) self.wait() + class FourierRecapScene(DrawFrequencyPlot): CONFIG = { - "frequency_axes_config" : { - "x_max" : 10.0, - "x_axis_config" : { - "unit_size" : 0.7, - "numbers_to_show" : list(range(1, 10, 1)), - } + "frequency_axes_config": { + "x_max": 10.0, + "x_axis_config": { + "unit_size": 0.7, + "numbers_to_show": list(range(1, 10, 1)), + }, }, - "initial_winding_frequency" : 0.1, + "initial_winding_frequency": 0.1, } + def construct(self): self.setup_axes() self.preview_fourier_plot() @@ -1152,65 +1163,68 @@ def construct(self): def setup_axes(self): self.remove(self.pi_creature) time_axes = self.get_time_axes() - time_axes.to_edge(UP, buff = MED_SMALL_BUFF) - time_axes.scale(0.9, about_edge = UP) + time_axes.to_edge(UP, buff=MED_SMALL_BUFF) + time_axes.scale(0.9, about_edge=UP) frequency_axes = self.get_frequency_axes() circle_plane = self.get_circle_plane() self.add(time_axes) - self.set_variables_as_attrs( - time_axes, frequency_axes, - circle_plane - ) + self.set_variables_as_attrs(time_axes, frequency_axes, circle_plane) def preview_fourier_plot(self): time_graph = self.graph = self.get_time_graph( - width = 2, - num_graph_points = 200, - ) - fourier_graph = self.get_fourier_transform_graph( - time_graph + width=2, + num_graph_points=200, ) + fourier_graph = self.get_fourier_transform_graph(time_graph) fourier_graph.pointwise_become_partial(fourier_graph, 0.1, 1) - #labels + # labels signal_label = TextMobject("Signal") fourier_label = TextMobject("Fourier transform") - signal_label.next_to(time_graph, UP, buff = SMALL_BUFF) + signal_label.next_to(time_graph, UP, buff=SMALL_BUFF) fourier_label.next_to(fourier_graph, UP) fourier_label.match_color(fourier_graph) self.play( - ShowCreation(time_graph, run_time = 2), + ShowCreation(time_graph, run_time=2), Write(signal_label), ) self.wait() self.play( LaggedStartMap(FadeIn, self.frequency_axes), - ReplacementTransform( - time_graph.copy(), - fourier_graph, - run_time = 2 - ), + ReplacementTransform(time_graph.copy(), fourier_graph, run_time=2), ReplacementTransform( signal_label.copy(), fourier_label, - run_time = 2, - rate_func = squish_rate_func(smooth, 0.5, 1) + run_time=2, + rate_func=squish_rate_func(smooth, 0.5, 1), ), ) self.wait() - self.play(LaggedStartMap( - Indicate, self.frequency_axes.x_axis.numbers, - run_time = 4, - rate_func = wiggle, - )) + self.play( + LaggedStartMap( + Indicate, + self.frequency_axes.x_axis.numbers, + run_time=4, + rate_func=wiggle, + ) + ) self.wait() - self.play(*list(map(FadeOut, [ - self.frequency_axes, fourier_graph, - signal_label, fourier_label, - ]))) + self.play( + *list( + map( + FadeOut, + [ + self.frequency_axes, + fourier_graph, + signal_label, + fourier_label, + ], + ) + ) + ) self.time_graph = time_graph self.set_variables_as_attrs(time_graph, fourier_label) @@ -1226,24 +1240,22 @@ def wrap_signal_around_circle(self): self.add_foreground_mobjects(winding_freq_label) self.play( - Write(circle_plane, run_time = 1), + Write(circle_plane, run_time=1), ReplacementTransform( - time_graph.copy(), pol_graph, - path_arc = -TAU/4, - run_time_per_flash = 2, - run_time = 2, + time_graph.copy(), + pol_graph, + path_arc=-TAU / 4, + run_time_per_flash=2, + run_time=2, ), FadeIn(winding_freq_label), ) freq = 0.3 - self.change_frequency(freq, run_time = 2) + self.change_frequency(freq, run_time=2) ghost_pol_graph = pol_graph.copy() self.remove(pol_graph) - self.play(ghost_pol_graph.set_stroke, {"width" : 0.5}) - self.play( - *self.get_vector_animations(time_graph), - run_time = 15 - ) + self.play(ghost_pol_graph.set_stroke, {"width": 0.5}) + self.play(*self.get_vector_animations(time_graph), run_time=15) self.remove(ghost_pol_graph) self.wait() @@ -1251,20 +1263,17 @@ def match_winding_to_beat_frequency(self): self.v_lines_indicating_periods = self.get_v_lines_indicating_periods(0.3) self.add(self.v_lines_indicating_periods) for freq in range(1, 6): - self.change_frequency(freq, run_time = 5) + self.change_frequency(freq, run_time=5) self.play( - *self.get_vector_animations( - self.time_graph, - draw_polarized_graph = False - ), - run_time = 10 + *self.get_vector_animations(self.time_graph, draw_polarized_graph=False), + run_time=10 ) self.wait() def follow_center_of_mass(self): com_dot = self.get_center_of_mass_dot() self.generate_center_of_mass_dot_update_anim() - com_arrow = Arrow(UP+3*RIGHT, ORIGIN) + com_arrow = Arrow(UP + 3 * RIGHT, ORIGIN) com_arrow.shift(com_dot.get_center()) com_arrow.match_color(com_dot) com_words = TextMobject("Center of mass") @@ -1278,25 +1287,25 @@ def follow_center_of_mass(self): self.play( com_dot.restore, - GrowArrow(com_arrow, rate_func = squish_rate_func(smooth, 0.2, 1)), + GrowArrow(com_arrow, rate_func=squish_rate_func(smooth, 0.2, 1)), Write(com_words), ) self.wait() squished_func = squish_rate_func(smooth, 0, 0.2) self.change_frequency( 4, - added_anims = [ - FadeOut(com_arrow, rate_func = squished_func), - FadeOut(com_words, rate_func = squished_func), + added_anims=[ + FadeOut(com_arrow, rate_func=squished_func), + FadeOut(com_words, rate_func=squished_func), ], - run_time = 5 + run_time=5, ) def draw_fourier_plot(self): frequency_axes = self.frequency_axes fourier_label = self.fourier_label - self.change_frequency(0, run_time = 2) + self.change_frequency(0, run_time=2) self.play( FadeIn(frequency_axes), FadeIn(fourier_label), @@ -1306,17 +1315,15 @@ def draw_fourier_plot(self): self.get_fourier_graph_drawing_update_anim(fourier_graph) self.generate_fourier_dot_transform(fourier_graph) - self.change_frequency(5, run_time = 20) + self.change_frequency(5, run_time=20) self.wait() - self.change_frequency(7.5, run_time = 10) + self.change_frequency(7.5, run_time=10) self.fourier_graph_drawing_update_anim = Animation(Mobject()) self.fourier_graph = fourier_graph def set_color_spike(self): - spike_point = self.frequency_axes.input_to_graph_point( - 5, self.fourier_graph - ) - circle = Circle(color = YELLOW, radius = 0.25) + spike_point = self.frequency_axes.input_to_graph_point(5, self.fourier_graph) + circle = Circle(color=YELLOW, radius=0.25) circle.move_to(spike_point) circle.save_state() circle.scale(5) @@ -1327,15 +1334,14 @@ def set_color_spike(self): self.play(FadeOut(circle)) self.wait() for x in range(2): - self.change_frequency(5.2, run_time = 3) - self.change_frequency(4.8, run_time = 3) - self.change_frequency(5, run_time = 1.5) + self.change_frequency(5.2, run_time=3) + self.change_frequency(4.8, run_time=3) + self.change_frequency(5, run_time=1.5) self.wait() - ######### - def get_time_graph(self, frequency = 5, width = 2, **kwargs): + def get_time_graph(self, frequency=5, width=2, **kwargs): # low_x = center-width/2 # high_x = center+width/2 # new_smooth = lambda x : np.clip(smooth((x+0.5)), 0, 1) @@ -1344,13 +1350,15 @@ def get_time_graph(self, frequency = 5, width = 2, **kwargs): # factor = new_smooth(x - low_x) - new_smooth(x-high_x) # return 1 + factor*pure_signal graph = self.time_axes.get_graph( - lambda x : 1+0.9*np.cos(TAU*frequency*x), - x_min = 0, x_max = width, + lambda x: 1 + 0.9 * np.cos(TAU * frequency * x), + x_min=0, + x_max=width, **kwargs ) graph.set_color(YELLOW) return graph + class RealPartOfInsert(Scene): def construct(self): words = TextMobject("(Real part of the)") @@ -1359,6 +1367,7 @@ def construct(self): self.play(Write(words)) self.wait(5) + class CenterOfMassDescription(FourierRecapScene): def construct(self): self.remove(self.pi_creature) @@ -1367,39 +1376,37 @@ def construct(self): circle_plane.generate_target() circle_plane.target.set_height(FRAME_HEIGHT) circle_plane.target.center() - circle_plane.target.axes.set_stroke(width = 2) - circle_plane.targets.set_stroke(width = 2) - circle_plane.target.secondary_lines.set_stroke(width = 1) + circle_plane.target.axes.set_stroke(width=2) + circle_plane.targets.set_stroke(width=2) + circle_plane.target.secondary_lines.set_stroke(width=1) start_coords = (0.5, 0.5) alt_coords = (0.8, 0.8) - com_dot = Dot(color = self.center_of_mass_color) + com_dot = Dot(color=self.center_of_mass_color) com_dot.move_to(circle_plane.coords_to_point(*start_coords)) self.add(circle_plane, com_dot) self.wait() self.play( MoveToTarget(circle_plane), - com_dot.move_to, - circle_plane.target.coords_to_point(*start_coords) + com_dot.move_to, + circle_plane.target.coords_to_point(*start_coords), ) self.wait() - alt_com_dot = com_dot.copy().move_to( - circle_plane.coords_to_point(*alt_coords) - ) + alt_com_dot = com_dot.copy().move_to(circle_plane.coords_to_point(*alt_coords)) for dot in com_dot, alt_com_dot: line = Line(ORIGIN, dot.get_center()) line.match_color(com_dot) angle = line.get_angle() - line.rotate(-angle, about_point = ORIGIN) + line.rotate(-angle, about_point=ORIGIN) brace = Brace(line, UP) words = brace.get_text("Strength of frequency") words.add_background_rectangle() dot.length_label_group = VGroup(line, brace, words) - dot.length_label_group.rotate(angle, about_point = ORIGIN) + dot.length_label_group.rotate(angle, about_point=ORIGIN) line, brace, words = com_dot.length_label_group self.play( @@ -1414,59 +1421,61 @@ def construct(self): alt_com_dot.length_label_group, ), Transform(com_dot, alt_com_dot), - rate_func = there_and_back, - run_time = 4, + rate_func=there_and_back, + run_time=4, ) - #Do rotation + # Do rotation line = com_dot.length_label_group[0] com_dot.length_label_group.remove(line) angle = line.get_angle() arc, alt_arc = [ Arc( - start_angle = 0, - angle = factor*angle, - radius = 0.5, + start_angle=0, + angle=factor * angle, + radius=0.5, ) for factor in (1, 2) ] theta = TexMobject("\\theta") - theta.shift(1.5*arc.point_from_proportion(0.5)) + theta.shift(1.5 * arc.point_from_proportion(0.5)) self.play( FadeOut(com_dot.length_label_group), Animation(line), ShowCreation(arc), - Write(theta) + Write(theta), ) self.play( - Rotate( - VGroup(line, com_dot), - angle, about_point = ORIGIN - ), + Rotate(VGroup(line, com_dot), angle, about_point=ORIGIN), Transform(arc, alt_arc), - theta.move_to, 1.5*alt_arc.point_from_proportion(0.5), - rate_func = there_and_back, - run_time = 4 + theta.move_to, + 1.5 * alt_arc.point_from_proportion(0.5), + rate_func=there_and_back, + run_time=4, ) self.wait() + class AskAboutLongVsShort(TeacherStudentsScene): def construct(self): self.student_says( "What happens if we \\\\ change the length of \\\\ the signal?", - student_index = 2, + student_index=2, ) self.play( - self.teacher.change, "happy", - self.get_student_changes("pondering", "confused", "raise_right_hand") + self.teacher.change, + "happy", + self.get_student_changes("pondering", "confused", "raise_right_hand"), ) self.wait(5) + class LongAndShortSignalsInWindingMachine(FourierRecapScene): CONFIG = { - "num_fourier_graph_points" : 1000, + "num_fourier_graph_points": 1000, } + def construct(self): self.setup_axes() self.extend_for_long_time() @@ -1478,7 +1487,7 @@ def setup_axes(self): FourierRecapScene.setup_axes(self) self.add(self.circle_plane) self.add(self.frequency_axes) - self.time_graph = self.graph = self.get_time_graph(width = 2) + self.time_graph = self.graph = self.get_time_graph(width=2) self.add(self.time_graph) self.force_skipping() @@ -1494,27 +1503,24 @@ def setup_axes(self): def extend_for_long_time(self): short_time_graph = self.time_graph long_time_graph = self.get_time_graph( - width = 10, - num_graph_points = 500, + width=10, + num_graph_points=500, ) - long_time_graph.set_stroke(width = 2) + long_time_graph.set_stroke(width=2) new_freq = 5.1 - long_pol_graph = self.get_polarized_mobject( - long_time_graph, - freq = new_freq - ) + long_pol_graph = self.get_polarized_mobject(long_time_graph, freq=new_freq) fourier_graph = self.fourier_graph self.change_frequency(new_freq) self.play( FadeOut(self.graph), FadeOut(self.graph.polarized_mobject), - FadeOut(fourier_graph) + FadeOut(fourier_graph), ) self.play( ShowCreation(long_time_graph, rate_func=linear), ShowCreation(long_pol_graph, rate_func=linear), - run_time = 5 + run_time=5, ) self.wait() @@ -1522,8 +1528,7 @@ def extend_for_long_time(self): def note_sharp_fourier_peak(self): fourier_graph = self.get_fourier_transform_graph( - self.time_graph, - num_graph_points = self.num_fourier_graph_points + self.time_graph, num_graph_points=self.num_fourier_graph_points ) self.fourier_graph = fourier_graph self.note_fourier_peak(fourier_graph, 5, 5.1) @@ -1531,12 +1536,9 @@ def note_sharp_fourier_peak(self): def very_short_signal(self): time_graph = self.time_graph fourier_graph = self.fourier_graph - short_time_graph = self.get_time_graph(width = 0.6) + short_time_graph = self.get_time_graph(width=0.6) new_freq = 5.1 - short_pol_graph = self.get_polarized_mobject( - short_time_graph, - freq = new_freq - ) + short_pol_graph = self.get_polarized_mobject(short_time_graph, freq=new_freq) self.play( FadeOut(fourier_graph), @@ -1548,17 +1550,15 @@ def very_short_signal(self): ShowCreation(short_time_graph.polarized_mobject), ) self.graph = self.time_graph = short_time_graph - self.change_frequency(6.66, run_time = 5) + self.change_frequency(6.66, run_time=5) def note_wide_fourier_peak(self): fourier_graph = self.get_fourier_transform_graph( - self.graph, - num_graph_points = self.num_fourier_graph_points + self.graph, num_graph_points=self.num_fourier_graph_points ) self.fourier_graph = fourier_graph self.note_fourier_peak(fourier_graph, 5, 6.66) - ### def note_fourier_peak(self, fourier_graph, freq1, freq2): @@ -1568,36 +1568,28 @@ def note_fourier_peak(self, fourier_graph, freq1, freq2): self.generate_center_of_mass_dot_update_anim() self.generate_fourier_dot_transform(fourier_graph) dot = self.fourier_graph_dot - arrow = Arrow(UP, ORIGIN, buff = SMALL_BUFF) - arrow.next_to(dot, UP, buff = SMALL_BUFF) + arrow = Arrow(UP, ORIGIN, buff=SMALL_BUFF) + arrow.next_to(dot, UP, buff=SMALL_BUFF) self.play(ShowCreation(fourier_graph)) - self.change_frequency(freq1, - added_anims = [ + self.change_frequency( + freq1, + added_anims=[ MaintainPositionRelativeTo(arrow, dot), - UpdateFromAlphaFunc( - arrow, - lambda m, a : m.set_fill(opacity = a) - ), + UpdateFromAlphaFunc(arrow, lambda m, a: m.set_fill(opacity=a)), ], - run_time = 3, + run_time=3, ) self.wait() - self.change_frequency(freq2, - added_anims = [ - MaintainPositionRelativeTo(arrow, dot) - ], - run_time = 3 + self.change_frequency( + freq2, added_anims=[MaintainPositionRelativeTo(arrow, dot)], run_time=3 ) self.wait() - self.play(*list(map(FadeOut, [ - dot, arrow, self.center_of_mass_dot - ]))) - #This is not great... + self.play(*list(map(FadeOut, [dot, arrow, self.center_of_mass_dot]))) + # This is not great... for attr in "center_of_mass_dot", "fourier_graph_dot": self.__dict__.pop(attr) - def get_fourier_graph_dots(self, fourier_graph, *freqs): axis_point = self.frequency_axes.coords_to_point(4.5, -0.25) dots = VGroup() @@ -1607,45 +1599,51 @@ def get_fourier_graph_dots(self, fourier_graph, *freqs): dot.scale(0.5) dots.add(dot) vect = point - axis_point - vect *= 1.3/get_norm(vect) - arrow = Arrow(vect, ORIGIN, buff = SMALL_BUFF) + vect *= 1.3 / get_norm(vect) + arrow = Arrow(vect, ORIGIN, buff=SMALL_BUFF) arrow.set_color(YELLOW) arrow.shift(point) dot.arrow = arrow return dots + class FocusRectangleInsert(FourierRecapScene): - CONFIG = { - "target_width" : 0.5 - } + CONFIG = {"target_width": 0.5} + def construct(self): self.setup_axes() self.clear() point = self.frequency_axes.coords_to_point(5, 0.25) - rect = ScreenRectangle(height = 2.1*FRAME_Y_RADIUS) + rect = ScreenRectangle(height=2.1 * FRAME_Y_RADIUS) rect.set_stroke(YELLOW, 2) self.add(rect) self.wait() self.play( - rect.stretch_to_fit_width, self.target_width, - rect.stretch_to_fit_height, 1.5, - rect.move_to, point, - run_time = 2 + rect.stretch_to_fit_width, + self.target_width, + rect.stretch_to_fit_height, + 1.5, + rect.move_to, + point, + run_time=2, ) self.wait(3) + class BroadPeakFocusRectangleInsert(FocusRectangleInsert): CONFIG = { - "target_width" : 1.5, + "target_width": 1.5, } + class CleanerFourierTradeoff(FourierTradeoff): CONFIG = { - "show_text" : False, - "complex_to_real_func" : lambda z : z.real, - "widths" : [0.02, 6, 1], + "show_text": False, + "complex_to_real_func": lambda z: z.real, + "widths": [0.02, 6, 1], } + class MentionDopplerRadar(TeacherStudentsScene): def construct(self): words = TextMobject("Doppler Radar") @@ -1653,7 +1651,7 @@ def construct(self): words.save_state() words.shift(DOWN).fade(1) dish = RadarDish() - dish.next_to(self.students, UP, buff = 2, aligned_edge = LEFT) + dish.next_to(self.students, UP, buff=2, aligned_edge=LEFT) plane = Plane() plane.to_edge(RIGHT) plane.align_to(dish) @@ -1661,34 +1659,31 @@ def construct(self): plane.flip() pulse = RadarPulse(dish, plane) look_at_anims = [ - Mobject.add_updater( - pi, lambda pi : pi.look_at(pulse.mobject) - ) + Mobject.add_updater(pi, lambda pi: pi.look_at(pulse.mobject)) for pi in self.get_pi_creatures() ] self.add(dish, plane, pulse, *look_at_anims) - self.play( - self.teacher.change, "hooray", - words.restore - ) + self.play(self.teacher.change, "hooray", words.restore) self.change_student_modes("pondering", "erm", "sassy") self.wait(2) self.play( - self.teacher.change, "happy", - self.get_student_changes(*["thinking"]*3) + self.teacher.change, "happy", self.get_student_changes(*["thinking"] * 3) ) self.wait() - dish.set_stroke(width = 0) - self.play(UpdateFromAlphaFunc( - VGroup(plane, dish), - lambda m, a : m.set_fill(opacity = 1 - a) - )) + dish.set_stroke(width=0) + self.play( + UpdateFromAlphaFunc( + VGroup(plane, dish), lambda m, a: m.set_fill(opacity=1 - a) + ) + ) + class IntroduceDopplerRadar(Scene): CONFIG = { - "frequency_spread_factor" : 100, + "frequency_spread_factor": 100, } + def construct(self): self.setup_axes() self.measure_distance_with_time() @@ -1697,13 +1692,8 @@ def construct(self): def setup_axes(self): self.dish = RadarDish() - self.dish.to_corner(UP+LEFT) - axes = Axes( - x_min = 0, - x_max = 10, - y_min = -1.5, - y_max = 1.5 - ) + self.dish.to_corner(UP + LEFT) + axes = Axes(x_min=0, x_max=10, y_min=-1.5, y_max=1.5) axes.move_to(DOWN) time_label = TextMobject("Time") time_label.next_to(axes.x_axis.get_right(), UP) @@ -1719,17 +1709,15 @@ def measure_distance_with_time(self): axes = self.axes distance = 5 time_diff = 5 - speed = (2*distance)/time_diff + speed = (2 * distance) / time_diff randy = Randolph().flip() randy.match_height(dish) randy.move_to(dish.get_right(), LEFT) - randy.shift(distance*RIGHT) + randy.shift(distance * RIGHT) - pulse_graph, echo_graph, sum_graph = \ - self.get_pulse_and_echo_graphs( - self.get_single_pulse_graph, - (1,), (1+time_diff,) - ) + pulse_graph, echo_graph, sum_graph = self.get_pulse_and_echo_graphs( + self.get_single_pulse_graph, (1,), (1 + time_diff,) + ) words = ["Original signal", "Echo"] for graph, word in zip([pulse_graph, echo_graph], words): arrow = Vector(DOWN) @@ -1742,30 +1730,23 @@ def measure_distance_with_time(self): graph.label = label double_arrow = DoubleArrow( - pulse_graph.peak_point, - echo_graph.peak_point, - color = WHITE + pulse_graph.peak_point, echo_graph.peak_point, color=WHITE ) distance_text = TextMobject("$2 \\times$ distance/(signal speed)") - distance_text.set_width(0.9*double_arrow.get_width()) + distance_text.set_width(0.9 * double_arrow.get_width()) distance_text.next_to(double_arrow, UP, SMALL_BUFF) - #v_line anim? + # v_line anim? pulse = RadarPulseSingleton( - dish, randy, - speed = 0.97*speed, #Just needs slightly better alignment + dish, + randy, + speed=0.97 * speed, # Just needs slightly better alignment ) graph_draw = turn_animation_into_updater( - ShowCreation( - sum_graph, - rate_func=linear, - run_time = 0.97*axes.x_max - ) - ) - randy_look_at = Mobject.add_updater( - randy, lambda pi : pi.look_at(pulse.mobject) + ShowCreation(sum_graph, rate_func=linear, run_time=0.97 * axes.x_max) ) + randy_look_at = Mobject.add_updater(randy, lambda pi: pi.look_at(pulse.mobject)) axes_anim = ContinualAnimation(axes) self.add(randy_look_at, axes_anim, graph_draw) @@ -1774,30 +1755,32 @@ def measure_distance_with_time(self): self.play( Write(pulse_graph.label), GrowArrow(pulse_graph.arrow), - run_time = 1, + run_time=1, ) self.play(randy.change, "pondering") self.wait(time_diff - 2) - self.play( - Write(echo_graph.label), - GrowArrow(echo_graph.arrow), - run_time = 1 - ) + self.play(Write(echo_graph.label), GrowArrow(echo_graph.arrow), run_time=1) self.wait() - self.play( - GrowFromCenter(double_arrow), - FadeIn(distance_text) - ) + self.play(GrowFromCenter(double_arrow), FadeIn(distance_text)) self.wait() self.remove(graph_draw, pulse, randy_look_at, axes_anim) self.add(axes) - self.play(LaggedStartMap(FadeOut, VGroup( - sum_graph, randy, - pulse_graph.arrow, pulse_graph.label, - echo_graph.arrow, echo_graph.label, - double_arrow, distance_text - ))) + self.play( + LaggedStartMap( + FadeOut, + VGroup( + sum_graph, + randy, + pulse_graph.arrow, + pulse_graph.label, + echo_graph.arrow, + echo_graph.label, + double_arrow, + distance_text, + ), + ) + ) def show_frequency_shift(self): axes = self.axes @@ -1809,13 +1792,11 @@ def show_frequency_shift(self): time_diff = 6 - pulse_graph, echo_graph, sum_graph = graphs = \ - self.get_pulse_and_echo_graphs( - self.get_frequency_pulse_graph, - (1,25), (1+time_diff,50) - ) + pulse_graph, echo_graph, sum_graph = graphs = self.get_pulse_and_echo_graphs( + self.get_frequency_pulse_graph, (1, 25), (1 + time_diff, 50) + ) for graph in graphs: - graph.set_stroke(width = 3) + graph.set_stroke(width=3) signal_graph = self.get_frequency_pulse_graph(1) pulse_brace = Brace(Line(ORIGIN, RIGHT), UP) @@ -1831,15 +1812,13 @@ def show_frequency_shift(self): echo_subtext.match_color(echo_graph) graph_draw = turn_animation_into_updater( - ShowCreation(sum_graph, run_time = 8, rate_func=linear) + ShowCreation(sum_graph, run_time=8, rate_func=linear) ) - pulse = RadarPulse(dish, plane, n_pulse_singletons = 12) + pulse = RadarPulse(dish, plane, n_pulse_singletons=12) always_shift(plane, LEFT, 1.5) self.add(graph_draw, pulse, plane) - self.play(UpdateFromAlphaFunc( - plane, lambda m, a : m.set_fill(opacity = a) - )) + self.play(UpdateFromAlphaFunc(plane, lambda m, a: m.set_fill(opacity=a))) self.play( GrowFromCenter(pulse_brace), FadeIn(pulse_text), @@ -1849,22 +1828,26 @@ def show_frequency_shift(self): GrowFromCenter(echo_brace), GrowFromCenter(echo_text), ) - self.play(UpdateFromAlphaFunc( - plane, lambda m, a : m.set_fill(opacity = 1-a) - )) - #Only for when -s is run - graph_draw.update(10) + self.play(UpdateFromAlphaFunc(plane, lambda m, a: m.set_fill(opacity=1 - a))) + # Only for when -s is run + graph_draw.update(10) self.wait(0.1) - self.play(Write(echo_subtext, run_time = 1)) + self.play(Write(echo_subtext, run_time=1)) self.wait() self.remove(graph_draw, pulse, plane) - pulse_graph.set_stroke(width = 0) - echo_graph.set_stroke(width = 0) + pulse_graph.set_stroke(width=0) + echo_graph.set_stroke(width=0) self.time_graph_group = VGroup( - axes, pulse_brace, pulse_text, - echo_brace, echo_text, echo_subtext, - pulse_graph, echo_graph, sum_graph, + axes, + pulse_brace, + pulse_text, + echo_brace, + echo_text, + echo_subtext, + pulse_graph, + echo_graph, + sum_graph, ) self.set_variables_as_attrs(*self.time_graph_group) @@ -1873,49 +1856,52 @@ def show_frequency_shift_in_fourier(self): pulse_graph = self.pulse_graph pulse_label = VGroup(self.pulse_brace, self.pulse_text) echo_graph = self.echo_graph - echo_label = VGroup( - self.echo_brace, self.echo_text, self.echo_subtext - ) + echo_label = VGroup(self.echo_brace, self.echo_text, self.echo_subtext) - #Setup all fourier graph stuff + # Setup all fourier graph stuff f_max = 0.02 frequency_axes = Axes( - x_min = 0, x_max = 20, - x_axis_config = {"unit_size" : 0.5}, - y_min = -f_max, y_max = f_max, - y_axis_config = { - "unit_size" : 50, - "tick_frequency" : 0.01, + x_min=0, + x_max=20, + x_axis_config={"unit_size": 0.5}, + y_min=-f_max, + y_max=f_max, + y_axis_config={ + "unit_size": 50, + "tick_frequency": 0.01, }, ) frequency_axes.move_to(self.axes, LEFT) frequency_axes.to_edge(DOWN) frequency_label = TextMobject("Frequency") frequency_label.next_to( - frequency_axes.x_axis.get_right(), UP, + frequency_axes.x_axis.get_right(), + UP, ) frequency_label.to_edge(RIGHT) frequency_axes.add(frequency_label) for graph in pulse_graph, echo_graph, sum_graph: graph.fourier_transform = get_fourier_graph( - frequency_axes, graph.underlying_function, - frequency_axes.x_min, 25, - complex_to_real_func = abs, + frequency_axes, + graph.underlying_function, + frequency_axes.x_min, + 25, + complex_to_real_func=abs, ) - #Braces labeling F.T. + # Braces labeling F.T. original_fourier_brace = Brace( Line( - frequency_axes.coords_to_point(7, 0.9*f_max), - frequency_axes.coords_to_point(9, 0.9*f_max), + frequency_axes.coords_to_point(7, 0.9 * f_max), + frequency_axes.coords_to_point(9, 0.9 * f_max), ), UP, ).set_color(BLUE) echo_fourier_brace = Brace( Line( - frequency_axes.coords_to_point(14, 0.4*f_max), - frequency_axes.coords_to_point(18, 0.4*f_max), + frequency_axes.coords_to_point(14, 0.4 * f_max), + frequency_axes.coords_to_point(18, 0.4 * f_max), ), UP, ).set_color(YELLOW) @@ -1925,51 +1911,43 @@ def show_frequency_shift_in_fourier(self): # brace.add(brace.get_text("F.T. of \\\\ %s"%word)) fourier_label = TexMobject("||\\text{Fourier transform}||") # fourier_label.next_to(sum_graph.fourier_transform, UP, MED_LARGE_BUFF) - fourier_label.next_to(frequency_axes.y_axis, UP, buff = SMALL_BUFF) + fourier_label.next_to(frequency_axes.y_axis, UP, buff=SMALL_BUFF) fourier_label.shift_onto_screen() fourier_label.set_color(RED) - - #v_lines + # v_lines v_line = DashedLine( frequency_axes.coords_to_point(8, 0), - frequency_axes.coords_to_point(8, 1.2*f_max), - color = YELLOW, - dash_length = 0.025, - ) - v_line_pair = VGroup(*[ - v_line.copy().shift(u*0.6*RIGHT) - for u in (-1, 1) - ]) + frequency_axes.coords_to_point(8, 1.2 * f_max), + color=YELLOW, + dash_length=0.025, + ) + v_line_pair = VGroup(*[v_line.copy().shift(u * 0.6 * RIGHT) for u in (-1, 1)]) v_line = VGroup(v_line) double_arrow = DoubleArrow( frequency_axes.coords_to_point(8, 0.007), frequency_axes.coords_to_point(16, 0.007), - buff = 0, - color = WHITE + buff=0, + color=WHITE, ) self.play( - self.time_graph_group.to_edge, UP, - ApplyMethod( - self.dish.shift, 2*UP, - remover = True - ), - FadeIn(frequency_axes) + self.time_graph_group.to_edge, + UP, + ApplyMethod(self.dish.shift, 2 * UP, remover=True), + FadeIn(frequency_axes), ) self.wait() self.play( FadeOut(sum_graph), FadeOut(echo_label), - pulse_graph.set_stroke, {"width" : 3}, + pulse_graph.set_stroke, + {"width": 3}, ) self.play( - ReplacementTransform( - pulse_label[0].copy(), - original_fourier_brace - ), - ShowCreation(pulse_graph.fourier_transform) + ReplacementTransform(pulse_label[0].copy(), original_fourier_brace), + ShowCreation(pulse_graph.fourier_transform), ) self.play(Write(fourier_label)) self.wait() @@ -1984,20 +1962,20 @@ def show_frequency_shift_in_fourier(self): FadeOut(pulse_graph), FadeIn(sum_graph), ReplacementTransform( - pulse_graph.fourier_transform, - sum_graph.fourier_transform - ) + pulse_graph.fourier_transform, sum_graph.fourier_transform + ), ) self.play(FadeIn(echo_label)) - self.play(ReplacementTransform( - echo_label[0].copy(), - echo_fourier_brace, - )) + self.play( + ReplacementTransform( + echo_label[0].copy(), + echo_fourier_brace, + ) + ) self.wait(2) self.play(GrowFromCenter(double_arrow)) self.wait() - ### def get_graph(self, func, **kwargs): @@ -2009,20 +1987,18 @@ def get_single_pulse_graph(self, x, **kwargs): return self.get_graph(self.get_single_pulse_function(x), **kwargs) def get_single_pulse_function(self, x): - return lambda t : -2*np.sin(10*(t-x))*np.exp(-100*(t-x)**2) + return lambda t: -2 * np.sin(10 * (t - x)) * np.exp(-100 * (t - x) ** 2) - def get_frequency_pulse_graph(self, x, freq = 50, **kwargs): + def get_frequency_pulse_graph(self, x, freq=50, **kwargs): return self.get_graph( - self.get_frequency_pulse_function(x, freq), - num_graph_points = 700, - **kwargs + self.get_frequency_pulse_function(x, freq), num_graph_points=700, **kwargs ) def get_frequency_pulse_function(self, x, freq): factor = self.frequency_spread_factor - return lambda t : op.mul( - 2*np.cos(2*freq*(t-x)), - min(np.exp(-(freq**2/factor)*(t-x)**2), 0.5) + return lambda t: op.mul( + 2 * np.cos(2 * freq * (t - x)), + min(np.exp(-(freq**2 / factor) * (t - x) ** 2), 0.5), ) def get_peak_point(self, graph): @@ -2030,57 +2006,64 @@ def get_peak_point(self, graph): return anchors[np.argmax([p[1] for p in anchors])] def get_pulse_and_echo_graphs(self, func, args1, args2): - pulse_graph = func(*args1, color = BLUE) - echo_graph = func(*args2, color = YELLOW) + pulse_graph = func(*args1, color=BLUE) + echo_graph = func(*args2, color=YELLOW) sum_graph = self.axes.get_graph( - lambda x : sum([ - pulse_graph.underlying_function(x), - echo_graph.underlying_function(x), - ]), - num_graph_points = echo_graph.get_num_curves(), - color = WHITE + lambda x: sum( + [ + pulse_graph.underlying_function(x), + echo_graph.underlying_function(x), + ] + ), + num_graph_points=echo_graph.get_num_curves(), + color=WHITE, ) sum_graph.background_image_file = "blue_yellow_gradient" return pulse_graph, echo_graph, sum_graph + class DopplerFormulaInsert(Scene): def construct(self): formula = TexMobject( - "f_{\\text{echo}", "=", + "f_{\\text{echo}", + "=", "\\left(1 + \\frac{v}{c}\\right)", - "f_{\\text{pulse}}" + "f_{\\text{pulse}}", ) formula[0].set_color(BLUE) formula[3].set_color(YELLOW) - randy = Randolph(color = BLUE_C) + randy = Randolph(color=BLUE_C) formula.scale(1.5) - formula.next_to(randy, UP+LEFT) + formula.next_to(randy, UP + LEFT) formula.shift_onto_screen() self.add(randy) self.play( LaggedStartMap(FadeIn, formula), - randy.change, "pondering", randy.get_bottom(), + randy.change, + "pondering", + randy.get_bottom(), ) self.play(Blink(randy)) self.wait(2) self.play(Blink(randy)) self.wait() + class MentionPRFNuance(TeacherStudentsScene): def construct(self): - title = TextMobject( - "Speed of light", "$\\gg$", "Speed of a plane" - ) + title = TextMobject("Speed of light", "$\\gg$", "Speed of a plane") title.to_edge(UP) self.add(title) axes = self.axes = Axes( - x_min = 0, x_max = 10, - y_min = 0, y_max = 2, + x_min=0, + x_max=10, + y_min=0, + y_max=2, ) - axes.next_to(title, DOWN, buff = MED_LARGE_BUFF) + axes.next_to(title, DOWN, buff=MED_LARGE_BUFF) frequency_label = TextMobject("Frequency") frequency_label.scale(0.7) frequency_label.next_to(axes.x_axis.get_right(), UP) @@ -2094,107 +2077,107 @@ def construct(self): peak_points = VGroup(pulse_graph.peak_point, shift_graph.peak_point) self.add(pulse_graph) - brace = Brace(peak_points, UP, buff = SMALL_BUFF) - displayed_doppler_shift = TextMobject("How I'm showing the \\\\", "Doppler shift") + brace = Brace(peak_points, UP, buff=SMALL_BUFF) + displayed_doppler_shift = TextMobject( + "How I'm showing the \\\\", "Doppler shift" + ) actual_doppler_shift = TextMobject("Actual\\\\", "Doppler shift") doppler_shift_words = VGroup(displayed_doppler_shift, actual_doppler_shift) doppler_shift_words.set_color(YELLOW) doppler_shift_words.scale(0.75) - displayed_doppler_shift.next_to(brace, UP, buff = SMALL_BUFF) + displayed_doppler_shift.next_to(brace, UP, buff=SMALL_BUFF) actual_doppler_shift.move_to(pulse_graph.peak_point) actual_doppler_shift.align_to(displayed_doppler_shift) self.play( - Animation(pulse_graph), - self.teacher.change, "raise_right_hand", - run_time = 1 + Animation(pulse_graph), self.teacher.change, "raise_right_hand", run_time=1 ) self.play( ShowCreation(shift_graph), FadeIn(brace), - Write(displayed_doppler_shift, run_time = 1), - self.get_student_changes(*3*["sassy"]), + Write(displayed_doppler_shift, run_time=1), + self.get_student_changes(*3 * ["sassy"]), ) self.play( UpdateFromAlphaFunc( - shift_graph, - lambda g, a : Transform( - g, self.get_spike_graph( - interpolate(shift_x, pulse_x+0.01, a), - ).match_style(shift_graph) + shift_graph, + lambda g, a: Transform( + g, + self.get_spike_graph( + interpolate(shift_x, pulse_x + 0.01, a), + ).match_style(shift_graph), ).update(1), ), UpdateFromFunc( brace, - lambda b : b.match_width( - peak_points, stretch = True - ).next_to(peak_points, UP, SMALL_BUFF) + lambda b: b.match_width(peak_points, stretch=True).next_to( + peak_points, UP, SMALL_BUFF + ), ), Transform( - displayed_doppler_shift, actual_doppler_shift, - rate_func = squish_rate_func(smooth, 0.3, 0.6) + displayed_doppler_shift, + actual_doppler_shift, + rate_func=squish_rate_func(smooth, 0.3, 0.6), ), - run_time = 3 + run_time=3, ) self.wait(2) everything = VGroup( - title, - axes, pulse_graph, shift_graph, - brace, displayed_doppler_shift + title, axes, pulse_graph, shift_graph, brace, displayed_doppler_shift ) - rect = SurroundingRectangle(everything, color = WHITE) + rect = SurroundingRectangle(everything, color=WHITE) everything.add(rect) self.teacher_says( "I'll ignore certain \\\\ nuances for now.", - target_mode = "shruggie", - added_anims = [ - everything.scale, 0.4, - everything.to_corner, UP+LEFT, - UpdateFromAlphaFunc( - rect, lambda m, a : m.set_stroke(width = 2*a) - ) + target_mode="shruggie", + added_anims=[ + everything.scale, + 0.4, + everything.to_corner, + UP + LEFT, + UpdateFromAlphaFunc(rect, lambda m, a: m.set_stroke(width=2 * a)), ], ) - self.change_student_modes(*3*["hesitant"]) + self.change_student_modes(*3 * ["hesitant"]) self.wait(2) - - - - def get_spike_graph(self, x, color = RED, **kwargs): + def get_spike_graph(self, x, color=RED, **kwargs): graph = self.axes.get_graph( - lambda t : np.exp(-10*(t-x)**2)*np.cos(10*(t-x)), - color = color, + lambda t: np.exp(-10 * (t - x) ** 2) * np.cos(10 * (t - x)), + color=color, **kwargs ) graph.peak_point = VectorizedPoint(self.axes.input_to_graph_point(x, graph)) graph.add(graph.peak_point) return graph + class TimeAndFrequencyGivePositionAndVelocity(IntroduceDopplerRadar): def construct(self): x = 7 freq = 25 axes = self.axes = Axes( - x_min = 0, x_max = 10, - y_min = -2, y_max = 2, + x_min=0, + x_max=10, + y_min=-2, + y_max=2, ) axes.center() title = TextMobject("Echo signal") title.next_to(axes.y_axis, UP) axes.add(title) axes.to_edge(UP) - graph = self.get_frequency_pulse_graph(x = x, freq = freq) + graph = self.get_frequency_pulse_graph(x=x, freq=freq) graph.background_image_file = "blue_yellow_gradient" arrow = Arrow( axes.coords_to_point(0, -1.5), axes.coords_to_point(x, -1.5), - color = WHITE, - buff = SMALL_BUFF, + color=WHITE, + buff=SMALL_BUFF, ) time = TextMobject("Time") time.next_to(arrow, DOWN, SMALL_BUFF) @@ -2202,10 +2185,10 @@ def construct(self): delta_x = 0.7 brace = Brace( Line( - axes.coords_to_point(x-delta_x, 1), - axes.coords_to_point(x+delta_x, 1) + axes.coords_to_point(x - delta_x, 1), + axes.coords_to_point(x + delta_x, 1), ), - UP + UP, ) frequency = TextMobject("Frequency") frequency.set_color(YELLOW) @@ -2223,71 +2206,60 @@ def construct(self): self.add(axes) self.play(ShowCreation(graph)) - self.play( - GrowArrow(arrow), - LaggedStartMap(FadeIn, time, run_time = 1) - ) - self.play( - GrowFromCenter(brace), - LaggedStartMap(FadeIn, frequency, run_time = 1) - ) + self.play(GrowArrow(arrow), LaggedStartMap(FadeIn, time, run_time=1)) + self.play(GrowFromCenter(brace), LaggedStartMap(FadeIn, frequency, run_time=1)) self.wait() self.play( GrowFromPoint(time_updown_arrow, time_updown_arrow.get_top()), - ReplacementTransform( - time.copy().fade(1), - distance - ) + ReplacementTransform(time.copy().fade(1), distance), ) self.play( GrowFromPoint(freq_updown_arrow, freq_updown_arrow.get_top()), - ReplacementTransform( - frequency.copy().fade(1), - velocity - ) + ReplacementTransform(frequency.copy().fade(1), velocity), ) self.wait() + class RadarOperatorUncertainty(Scene): def construct(self): dish = RadarDish() dish.scale(3) - dish.move_to(4*RIGHT + 2*DOWN) + dish.move_to(4 * RIGHT + 2 * DOWN) dish_words = TextMobject("3b1b industrial \\\\ enterprises") dish_words.scale(0.25) dish_words.set_stroke(BLACK, 0.5) dish_words.set_color(BLACK) dish_words.move_to(dish, DOWN) - dish_words.shift(SMALL_BUFF*(UP+2*LEFT)) + dish_words.shift(SMALL_BUFF * (UP + 2 * LEFT)) dish.add(dish_words) randy = Randolph() - randy.next_to(dish, LEFT, aligned_edge = DOWN) + randy.next_to(dish, LEFT, aligned_edge=DOWN) bubble = randy.get_bubble( - width = 7, - height = 4, + width=7, + height=4, ) echo_object = Square() echo_object.move_to(dish) - echo_object.shift(FRAME_X_RADIUS*RIGHT) - pulse = RadarPulse(dish, echo_object, speed = 6) + echo_object.shift(FRAME_X_RADIUS * RIGHT) + pulse = RadarPulse(dish, echo_object, speed=6) plane = Plane().scale(0.5) - plane.move_to(bubble.get_bubble_center()+LEFT) + plane.move_to(bubble.get_bubble_center() + LEFT) plane_cloud = ProbabalisticMobjectCloud( - plane, - fill_opacity = 0.3, - n_copies = 10, + plane, + fill_opacity=0.3, + n_copies=10, ) plane_gdw = plane_cloud.gaussian_distribution_wrapper vector_cloud = ProbabalisticVectorCloud( - center_func = plane_gdw.get_center, + center_func=plane_gdw.get_center, ) vector_gdw = vector_cloud.gaussian_distribution_wrapper vector_gdw.scale(0.05) vector_gdw.move_to(plane_gdw) - vector_gdw.shift(2*RIGHT) + vector_gdw.shift(2 * RIGHT) self.add(randy, dish, bubble, plane_cloud, pulse) self.play(randy.change, "confused") @@ -2298,19 +2270,25 @@ def construct(self): pulse.internal_time = 0 pulse.frequency = freq self.play( - randy.change, "pondering", plane, - plane_gdw.scale, plane_factor, - vector_gdw.scale, vector_factor, + randy.change, + "pondering", + plane, + plane_gdw.scale, + plane_factor, + vector_gdw.scale, + vector_factor, ) self.wait(2) + class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene): CONFIG = { - "object_x_coords" : [7, 4, 6, 9, 8], - "frequency_spread_factor" : 200, - "n_pulse_singletons" : 16, - "pulse_frequency" : 0.025, + "object_x_coords": [7, 4, 6, 9, 8], + "frequency_spread_factor": 200, + "n_pulse_singletons": 16, + "pulse_frequency": 0.025, } + def construct(self): self.setup_axes() self.setup_objects() @@ -2325,8 +2303,10 @@ def construct(self): def setup_axes(self): axes = self.axes = Axes( - x_min = 0, x_max = 10, - y_min = -1.5, y_max = 1.5, + x_min=0, + x_max=10, + y_min=-1.5, + y_max=1.5, ) time_label = TextMobject("Time") time_label.next_to(axes.x_axis.get_right(), UP) @@ -2337,41 +2317,41 @@ def setup_axes(self): dish = self.dish = RadarDish() dish.move_to(axes, LEFT) - dish.to_edge(UP, buff = LARGE_BUFF) + dish.to_edge(UP, buff=LARGE_BUFF) self.add(dish) def setup_objects(self): objects = self.objects = VGroup( Plane().flip(), SVGMobject( - file_name = "blimp", - color = BLUE_C, - height = 0.5, + file_name="blimp", + color=BLUE_C, + height=0.5, ), SVGMobject( - file_name = "biplane", - color = RED_D, - height = 0.5, + file_name="biplane", + color=RED_D, + height=0.5, ), SVGMobject( - file_name = "helicopter", - color = LIGHT_GREY, - height = 0.5, - ).rotate(-TAU/24), + file_name="helicopter", + color=LIGHT_GREY, + height=0.5, + ).rotate(-TAU / 24), FalconHeavy(), ) y_shifts = [0.25, 0, 0.5, 0.25, -0.5] for x, y, obj in zip(self.object_x_coords, y_shifts, objects): obj.move_to(self.axes.coords_to_point(x, 0)) obj.align_to(self.dish) - obj.shift(y*UP) + obj.shift(y * UP) self.object_velocities = [ - 0.7*LEFT, - 0.1*RIGHT, - 0.4*LEFT, - 0.4*RIGHT, - 0.5*UP, + 0.7 * LEFT, + 0.1 * RIGHT, + 0.4 * LEFT, + 0.4 * RIGHT, + 0.5 * UP, ] def send_long_pulse_single_echo(self): @@ -2388,9 +2368,10 @@ def send_long_pulse_single_echo(self): brace = Brace( Line( - self.axes.coords_to_point(x-1, 1), - self.axes.coords_to_point(x+1, 1), - ), UP + self.axes.coords_to_point(x - 1, 1), + self.axes.coords_to_point(x + 1, 1), + ), + UP, ) words = brace.get_text("Spread over time") @@ -2399,30 +2380,38 @@ def send_long_pulse_single_echo(self): squished_rate_func = squish_rate_func(smooth, 0.6, 0.9) self.play( ShowCreation(pulse_graph, rate_func=linear), - GrowFromCenter(brace, rate_func = squished_rate_func), - Write(words, rate_func = squished_rate_func), - run_time = 3, + GrowFromCenter(brace, rate_func=squished_rate_func), + Write(words, rate_func=squished_rate_func), + run_time=3, ) self.remove(pulse) self.play(FadeIn(randy)) - self.play(PiCreatureBubbleIntroduction( - randy, "Who cares?", - bubble_class = ThoughtBubble, - bubble_kwargs = { - "direction" : LEFT, - "width" : 2, - "height": 1.5, - }, - target_mode = "maybe", - look_at_arg = brace, - )) - self.play(Blink(randy)) - self.play(LaggedStartMap( - FadeOut, VGroup( - randy.bubble, randy.bubble.content, - brace, words, + self.play( + PiCreatureBubbleIntroduction( + randy, + "Who cares?", + bubble_class=ThoughtBubble, + bubble_kwargs={ + "direction": LEFT, + "width": 2, + "height": 1.5, + }, + target_mode="maybe", + look_at_arg=brace, ) - )) + ) + self.play(Blink(randy)) + self.play( + LaggedStartMap( + FadeOut, + VGroup( + randy.bubble, + randy.bubble.content, + brace, + words, + ), + ) + ) self.curr_graph = pulse_graph @@ -2432,26 +2421,19 @@ def introduce_multiple_objects(self): curr_graph = self.curr_graph randy = self.pi_creature - graphs = VGroup(*[ - self.get_frequency_pulse_graph(x) - for x in x_coords - ]) + graphs = VGroup(*[self.get_frequency_pulse_graph(x) for x in x_coords]) graphs.set_color_by_gradient(BLUE, YELLOW) sum_graph = self.axes.get_graph( - lambda t : sum([ - graph.underlying_function(t) - for graph in graphs - ]), - num_graph_points = 1000 + lambda t: sum([graph.underlying_function(t) for graph in graphs]), + num_graph_points=1000, ) - noise_function = lambda t : np.sum([ - 0.5*np.sin(f*t)/f - for f in (2, 3, 5, 7, 11, 13) - ]) + noise_function = lambda t: np.sum( + [0.5 * np.sin(f * t) / f for f in (2, 3, 5, 7, 11, 13)] + ) noisy_graph = self.axes.get_graph( - lambda t : sum_graph.underlying_function(t)*(1+noise_function(t)), - num_graph_points = 1000 + lambda t: sum_graph.underlying_function(t) * (1 + noise_function(t)), + num_graph_points=1000, ) for graph in sum_graph, noisy_graph: graph.background_image_file = "blue_yellow_gradient" @@ -2461,7 +2443,8 @@ def introduce_multiple_objects(self): self.play( LaggedStartMap(GrowFromCenter, objects[1:]), FadeOut(curr_graph), - randy.change, "pondering" + randy.change, + "pondering", ) self.add(*pulses) self.wait(0.5) @@ -2469,17 +2452,18 @@ def introduce_multiple_objects(self): ShowCreation( sum_graph, rate_func=linear, - run_time = 3.5, + run_time=3.5, ), - randy.change, "confused" + randy.change, + "confused", ) self.remove(*pulses) self.play(randy.change, "pondering") - self.play(Transform( - sum_graph, noisy_graph, - rate_func = lambda t : wiggle(t, 4), - run_time = 3 - )) + self.play( + Transform( + sum_graph, noisy_graph, rate_func=lambda t: wiggle(t, 4), run_time=3 + ) + ) self.wait(2) self.curr_graph = sum_graph @@ -2494,16 +2478,10 @@ def use_short_pulse(self): self.n_pulse_singletons = 4 self.pulse_frequency = 0.015 - graphs = VGroup(*[ - self.get_frequency_pulse_graph(x) - for x in x_coords - ]) + graphs = VGroup(*[self.get_frequency_pulse_graph(x) for x in x_coords]) sum_graph = self.axes.get_graph( - lambda t : sum([ - graph.underlying_function(t) - for graph in graphs - ]), - num_graph_points = 1000 + lambda t: sum([graph.underlying_function(t) for graph in graphs]), + num_graph_points=1000, ) sum_graph.background_image_file = "blue_yellow_gradient" @@ -2516,9 +2494,10 @@ def use_short_pulse(self): ShowCreation( sum_graph, rate_func=linear, - run_time = 3.5, + run_time=3.5, ), - randy.change, "happy" + randy.change, + "happy", ) self.wait() @@ -2528,16 +2507,15 @@ def use_short_pulse(self): def fourier_transform_of_one_pulse(self): frequency_axes = Axes( - x_min = 0, x_max = 20, - x_axis_config = { - "unit_size" : 0.5, - "tick_frequency" : 2, + x_min=0, + x_max=20, + x_axis_config={ + "unit_size": 0.5, + "tick_frequency": 2, }, - y_min = -.01, y_max = .01, - y_axis_config = { - "unit_size" : 110, - "tick_frequency" : 0.006 - } + y_min=-0.01, + y_max=0.01, + y_axis_config={"unit_size": 110, "tick_frequency": 0.006}, ) frequency_label = TextMobject("Frequency") frequency_label.next_to(frequency_axes.x_axis.get_right(), UP) @@ -2546,27 +2524,30 @@ def fourier_transform_of_one_pulse(self): self.play( ApplyMethod( - VGroup(self.axes, first_echo_graph).to_edge, UP, - {"buff" : SMALL_BUFF}, - rate_func = squish_rate_func(smooth, 0.5, 1) + VGroup(self.axes, first_echo_graph).to_edge, + UP, + {"buff": SMALL_BUFF}, + rate_func=squish_rate_func(smooth, 0.5, 1), ), LaggedStartMap(FadeOut, self.objects), - LaggedStartMap(FadeOut, VGroup( - self.curr_graph, self.dish, self.pi_creature - )), - run_time = 2 + LaggedStartMap( + FadeOut, VGroup(self.curr_graph, self.dish, self.pi_creature) + ), + run_time=2, ) # frequency_axes.next_to(self.axes, DOWN, LARGE_BUFF, LEFT) fourier_graph = get_fourier_graph( - frequency_axes, first_echo_graph.underlying_function, - t_min = 0, t_max = 25, - complex_to_real_func = np.abs, + frequency_axes, + first_echo_graph.underlying_function, + t_min=0, + t_max=25, + complex_to_real_func=np.abs, ) fourier_graph.save_state() fourier_graph.move_to(first_echo_graph) - h_vect = 4*RIGHT + h_vect = 4 * RIGHT fourier_graph.shift(h_vect) fourier_graph.fade(1) @@ -2576,29 +2557,26 @@ def fourier_transform_of_one_pulse(self): frequency_axes.coords_to_point(f, frequency_axes.y_max), ) v_lines = VGroup( - v_line.copy().shift(2*LEFT), - v_line.copy().shift(2*RIGHT), + v_line.copy().shift(2 * LEFT), + v_line.copy().shift(2 * RIGHT), ) - rect = Rectangle(stroke_width = 0, fill_color = YELLOW, fill_opacity = 0.25) - rect.replace(v_lines, stretch = True) + rect = Rectangle(stroke_width=0, fill_color=YELLOW, fill_opacity=0.25) + rect.replace(v_lines, stretch=True) rect.save_state() rect.stretch(0, 0) - self.play(Write(frequency_axes, run_time = 1)) + self.play(Write(frequency_axes, run_time=1)) self.play( ApplyFunction( - lambda m : m.move_to(fourier_graph.saved_state).shift(-h_vect).fade(1), + lambda m: m.move_to(fourier_graph.saved_state).shift(-h_vect).fade(1), first_echo_graph.copy(), - remover = True, + remover=True, ), - fourier_graph.restore + fourier_graph.restore, ) self.wait() self.play(ShowCreation(v_line)) - self.play( - ReplacementTransform(VGroup(v_line), v_lines), - rect.restore - ) + self.play(ReplacementTransform(VGroup(v_line), v_lines), rect.restore) self.wait() self.play(FadeOut(v_lines), FadeOut(rect)) @@ -2611,31 +2589,29 @@ def show_echos_of_moving_objects(self): object_velocities = self.object_velocities movements = self.object_movements = [ - always_shift( - obj, - direction = v/get_norm(v), - rate = get_norm(v) - ) + always_shift(obj, direction=v / get_norm(v), rate=get_norm(v)) for v, obj in zip(object_velocities, objects) ] pulses = self.get_pulses() - continual_anims = pulses+movements - + continual_anims = pulses + movements + self.play( FadeOut(self.axes), FadeOut(self.first_echo_graph), LaggedStartMap(FadeIn, objects), - FadeIn(self.dish) + FadeIn(self.dish), ) self.add(*continual_anims) self.wait(4) - self.play(*[ - UpdateFromAlphaFunc( - obj, - lambda m, a : m.set_fill(opacity = 1-a), - ) - for obj in objects - ]) + self.play( + *[ + UpdateFromAlphaFunc( + obj, + lambda m, a: m.set_fill(opacity=1 - a), + ) + for obj in objects + ] + ) self.remove(*continual_anims) self.wait() @@ -2645,7 +2621,7 @@ def overlapping_frequenies_of_various_objects(self): shifted_graphs = self.get_shifted_frequency_graphs(fourier_graph) color = fourier_graph.get_color() shifted_graphs.set_color_by_gradient( - average_color(color, WHITE), + average_color(color, WHITE), color, average_color(color, BLACK), ) @@ -2654,21 +2630,27 @@ def overlapping_frequenies_of_various_objects(self): shifted_graphs.save_state() - self.play(ReplacementTransform( - VGroup(fourier_graph), shifted_graphs, - lag_ratio = 0.5, - run_time = 2 - )) + self.play( + ReplacementTransform( + VGroup(fourier_graph), shifted_graphs, lag_ratio=0.5, run_time=2 + ) + ) self.wait() self.play( - shifted_graphs.arrange, DOWN, - shifted_graphs.move_to, fourier_graph, DOWN, + shifted_graphs.arrange, + DOWN, + shifted_graphs.move_to, + fourier_graph, + DOWN, ) self.wait() self.play(shifted_graphs.restore), - self.play(ReplacementTransform( - shifted_graphs, VGroup(sum_graph), - )) + self.play( + ReplacementTransform( + shifted_graphs, + VGroup(sum_graph), + ) + ) self.wait() self.curr_fourier_graph = sum_graph @@ -2677,8 +2659,8 @@ def echos_of_long_pure_signal_in_frequency_space(self): curr_fourier_graph = self.curr_fourier_graph f_max = self.frequency_axes.y_max new_fourier_graph = self.frequency_axes.get_graph( - lambda x : f_max * np.exp(-100*(x-8)**2), - num_graph_points = 1000, + lambda x: f_max * np.exp(-100 * (x - 8) ** 2), + num_graph_points=1000, ) new_fourier_graph.set_color(PINK) @@ -2697,88 +2679,100 @@ def concentrated_fourier_requires_long_time(self): pulses = self.get_pulses() randy = self.pi_creature - continual_anims = object_movements+pulses + continual_anims = object_movements + pulses self.play(FadeIn(randy)) self.add(*continual_anims) - self.play(randy.change, "angry", *[ - UpdateFromAlphaFunc(obj, lambda m, a : m.set_fill(opacity = a)) - for obj in objects - ]) + self.play( + randy.change, + "angry", + *[ + UpdateFromAlphaFunc(obj, lambda m, a: m.set_fill(opacity=a)) + for obj in objects + ] + ) self.play(Blink(randy)) self.wait(2) self.play(Blink(randy)) self.wait() - self.play(randy.change, "plain", *[ - UpdateFromAlphaFunc(obj, lambda m, a : m.set_fill(opacity = 1-a)) - for obj in objects - ]) + self.play( + randy.change, + "plain", + *[ + UpdateFromAlphaFunc(obj, lambda m, a: m.set_fill(opacity=1 - a)) + for obj in objects + ] + ) self.wait() - ### - def get_frequency_pulse_graph(self, x, freq = 25, **kwargs): - graph = IntroduceDopplerRadar.get_frequency_pulse_graph( - self, x, freq, **kwargs - ) + def get_frequency_pulse_graph(self, x, freq=25, **kwargs): + graph = IntroduceDopplerRadar.get_frequency_pulse_graph(self, x, freq, **kwargs) return graph def get_pulse(self, dish, echo_object): return RadarPulse( - dish, echo_object, - n_pulse_singletons = self.n_pulse_singletons, - frequency = 0.025, - speed = 5.0, + dish, + echo_object, + n_pulse_singletons=self.n_pulse_singletons, + frequency=0.025, + speed=5.0, ) def get_pulses(self): return [ - self.get_pulse( - self.dish.copy().shift(0.01*obj.get_center()[0]), - obj - ) + self.get_pulse(self.dish.copy().shift(0.01 * obj.get_center()[0]), obj) for obj in self.objects ] def create_pi_creature(self): randy = Randolph() randy.scale(0.5).flip() - randy.to_edge(RIGHT, buff = 1.7).shift(0.5*UP) + randy.to_edge(RIGHT, buff=1.7).shift(0.5 * UP) return randy def get_shifted_frequency_graphs(self, fourier_graph): frequency_axes = self.frequency_axes + def get_func(v): - return lambda f : fourier_graph.underlying_function(np.clip( - f-5*v[0], - frequency_axes.x_min, - frequency_axes.x_max, - )) + return lambda f: fourier_graph.underlying_function( + np.clip( + f - 5 * v[0], + frequency_axes.x_min, + frequency_axes.x_max, + ) + ) + def get_graph(func): return frequency_axes.get_graph(func) - shifted_graphs = VGroup(*list(map( - get_graph, list(map(get_func, self.object_velocities)) - ))) + + shifted_graphs = VGroup( + *list(map(get_graph, list(map(get_func, self.object_velocities)))) + ) shifted_graphs.match_style(fourier_graph) return shifted_graphs def get_sum_graph(self, axes, graphs): def get_func(graph): return graph.underlying_function + funcs = list(map(get_func, graphs)) return axes.get_graph( - lambda t : sum([func(t) for func in funcs]), + lambda t: sum([func(t) for func in funcs]), ) + class SummarizeFourierTradeoffForDoppler(Scene): def construct(self): time_axes = Axes( - x_min = 0, x_max = 12, - y_min = -0.5, y_max = 1, + x_min=0, + x_max=12, + y_min=-0.5, + y_max=1, ) - time_axes.center().to_edge(UP, buff = LARGE_BUFF) + time_axes.center().to_edge(UP, buff=LARGE_BUFF) frequency_axes = time_axes.copy() - frequency_axes.next_to(time_axes, DOWN, buff = 2) + frequency_axes.next_to(time_axes, DOWN, buff=2) time_label = TextMobject("Time") frequency_label = TextMobject("Frequency") for label, axes in (time_label, time_axes), (frequency_label, frequency_axes): @@ -2789,65 +2783,73 @@ def construct(self): title.next_to(time_axes, DOWN) self.add(title) - - #Position determines log of scale value for exponentials + # Position determines log of scale value for exponentials a_mob = VectorizedPoint() x_values = [3, 5, 6, 7, 8] v_values = [5, 5.5, 5.75, 6.5, 7] + def get_top_graphs(): a = np.exp(a_mob.get_center()[0]) - graphs = VGroup(*[ - time_axes.get_graph(lambda t : np.exp(-5*a*(t-x)**2)) - for x in x_values - ]) + graphs = VGroup( + *[ + time_axes.get_graph(lambda t: np.exp(-5 * a * (t - x) ** 2)) + for x in x_values + ] + ) graphs.set_color(WHITE) graphs.color_using_background_image("blue_yellow_gradient") return graphs + def get_bottom_graphs(): a = np.exp(a_mob.get_center()[0]) - graphs = VGroup(*[ - frequency_axes.get_graph(lambda t : np.exp(-(5./a)*(t-v)**2)) - for v in v_values - ]) + graphs = VGroup( + *[ + frequency_axes.get_graph( + lambda t: np.exp(-(5.0 / a) * (t - v) ** 2) + ) + for v in v_values + ] + ) graphs.set_color(RED) return graphs top_graphs = get_top_graphs() bottom_graphs = get_bottom_graphs() update_top_graphs = Mobject.add_updater( - top_graphs, - lambda g : Transform(g, get_top_graphs()).update(1) + top_graphs, lambda g: Transform(g, get_top_graphs()).update(1) ) update_bottom_graphs = Mobject.add_updater( - bottom_graphs, - lambda g : Transform(g, get_bottom_graphs()).update(1) + bottom_graphs, lambda g: Transform(g, get_bottom_graphs()).update(1) ) self.add(time_axes, frequency_axes) self.add(update_top_graphs, update_bottom_graphs) - shift_vect = 2*RIGHT + shift_vect = 2 * RIGHT for s in 1, -2, 1: - self.play(a_mob.shift, s*shift_vect, run_time = 3) + self.play(a_mob.shift, s * shift_vect, run_time=3) + class MentionUncertaintyPrincipleCopy(MentionUncertaintyPrinciple): pass + class IntroduceDeBroglie(Scene): CONFIG = { - "default_wave_frequency" : 1, - "wave_colors" : [BLUE_D, YELLOW], - "dispersion_factor" : 1, - "amplitude" : 1, + "default_wave_frequency": 1, + "wave_colors": [BLUE_D, YELLOW], + "dispersion_factor": 1, + "amplitude": 1, } + def construct(self): - text_scale_val = 0.8, + text_scale_val = (0.8,) - #Overlay real tower in video editor - eiffel_tower = Line(3*DOWN, 3*UP, stroke_width = 0) + # Overlay real tower in video editor + eiffel_tower = Line(3 * DOWN, 3 * UP, stroke_width=0) picture = ImageMobject("de_Broglie") picture.set_height(4) - picture.to_corner(UP+LEFT) + picture.to_corner(UP + LEFT) name = TextMobject("Louis de Broglie") name.next_to(picture, DOWN) @@ -2855,16 +2857,15 @@ def construct(self): picture.scale(0) picture.move_to(eiffel_tower.get_top()) - broadcasts = [ Broadcast( eiffel_tower.get_top(), - big_radius = 10, - n_circles = 10, - lag_ratio = 0.9, - run_time = 7, - rate_func = squish_rate_func(smooth, a, a+0.3), - color = WHITE, + big_radius=10, + n_circles=10, + lag_ratio=0.9, + run_time=7, + rate_func=squish_rate_func(smooth, a, a + 0.3), + color=WHITE, ) for a in np.linspace(0, 0.7, 3) ] @@ -2874,39 +2875,39 @@ def construct(self): self.play(Write(name)) self.wait() - #Time line + # Time line time_line = NumberLine( - x_min = 1900, - x_max = 1935, - tick_frequency = 1, - numbers_with_elongated_ticks = list(range(1900, 1941, 10)), - color = BLUE_D + x_min=1900, + x_max=1935, + tick_frequency=1, + numbers_with_elongated_ticks=list(range(1900, 1941, 10)), + color=BLUE_D, ) time_line.stretch_to_fit_width(FRAME_WIDTH - picture.get_width() - 2) time_line.add_numbers(*time_line.numbers_with_elongated_ticks) time_line.next_to(picture, RIGHT, MED_LARGE_BUFF, DOWN) year_to_words = { - 1914 : "Wold War I begins", - 1915 : "Einstein field equations", - 1916 : "Lewis dot formulas", - 1917 : "Not a lot of physics...because war", - 1918 : "S'more Rutherford badassery", - 1919 : "Eddington confirms general relativity predictions", - 1920 : "World is generally stoked on general relativity", - 1921 : "Einstein gets long overdue Nobel prize", - 1922 : "Stern-Gerlach Experiment", - 1923 : "Compton scattering observed", - 1924 : "de Broglie's thesis" + 1914: "Wold War I begins", + 1915: "Einstein field equations", + 1916: "Lewis dot formulas", + 1917: "Not a lot of physics...because war", + 1918: "S'more Rutherford badassery", + 1919: "Eddington confirms general relativity predictions", + 1920: "World is generally stoked on general relativity", + 1921: "Einstein gets long overdue Nobel prize", + 1922: "Stern-Gerlach Experiment", + 1923: "Compton scattering observed", + 1924: "de Broglie's thesis", } - arrow = Vector(DOWN, color = WHITE) + arrow = Vector(DOWN, color=WHITE) arrow.next_to(time_line.number_to_point(1914), UP) words = TextMobject(year_to_words[1914]) words.scale(text_scale_val) date = Integer(1914) date.next_to(arrow, UP, LARGE_BUFF) - def get_year(alpha = 0): + def get_year(alpha=0): return int(time_line.point_to_number(arrow.get_end())) def update_words(words): @@ -2914,47 +2915,44 @@ def update_words(words): if text not in words.get_tex_string(): words.__init__(text) words.scale(text_scale_val) - words.move_to(interpolate( - arrow.get_top(), date.get_bottom(), 0.5 - )) + words.move_to(interpolate(arrow.get_top(), date.get_bottom(), 0.5)) + update_words(words) self.play( - FadeIn(time_line), - GrowArrow(arrow), - Write(words), - Write(date), - run_time = 1 + FadeIn(time_line), GrowArrow(arrow), Write(words), Write(date), run_time=1 ) self.wait() self.play( - arrow.next_to, time_line.number_to_point(1924), UP, + arrow.next_to, + time_line.number_to_point(1924), + UP, ChangingDecimal( - date, get_year, - position_update_func = lambda m : m.next_to(arrow, UP, LARGE_BUFF) + date, + get_year, + position_update_func=lambda m: m.next_to(arrow, UP, LARGE_BUFF), ), UpdateFromFunc(words, update_words), - run_time = 3, + run_time=3, ) self.wait() - #Transform time_line + # Transform time_line line = time_line self.play( FadeOut(time_line.numbers), - VGroup(arrow, words, date).shift, MED_LARGE_BUFF*UP, + VGroup(arrow, words, date).shift, + MED_LARGE_BUFF * UP, *[ ApplyFunction( - lambda m : m.rotate(TAU/4).set_stroke(width = 0), - mob, - remover = True + lambda m: m.rotate(TAU / 4).set_stroke(width=0), mob, remover=True ) for mob in time_line.tick_marks ] ) - #Wave function + # Wave function particle = VectorizedPoint() - axes = Axes(x_min = -1, x_max = 10) + axes = Axes(x_min=-1, x_max=10) axes.match_width(line) axes.shift(line.get_center() - axes.x_axis.get_center()) im_line = line.copy() @@ -2967,16 +2965,15 @@ def update_words(words): particle.move_to(axes.coords_to_point(-10, 0)) self.play( ApplyMethod( - particle.move_to, axes.coords_to_point(22, 0), - rate_func=linear + particle.move_to, axes.coords_to_point(22, 0), rate_func=linear ), wave_update_animation, - run_time = 3 + run_time=3, ) self.wait() ### - def get_wave_update_animation(self, axes, particle, re_line = None, im_line = None): + def get_wave_update_animation(self, axes, particle, re_line=None, im_line=None): line = Line( axes.x_axis.get_left(), axes.x_axis.get_right(), @@ -2988,55 +2985,56 @@ def get_wave_update_animation(self, axes, particle, re_line = None, im_line = No im_line = line.copy() im_line.set_color(self.wave_colors[1]) lines = VGroup(im_line, re_line) + def update_lines(lines): waves = self.get_wave_pair(axes, particle) for line, wave in zip(lines, waves): wave.match_style(line) Transform(line, wave).update(1) + return UpdateFromFunc(lines, update_lines) def get_wave( - self, axes, particle, - complex_to_real_func = lambda z : z.real, - freq = None, - **kwargs): + self, axes, particle, complex_to_real_func=lambda z: z.real, freq=None, **kwargs + ): freq = freq or self.default_wave_frequency - k0 = 1./freq + k0 = 1.0 / freq t0 = axes.x_axis.point_to_number(particle.get_center()) + def func(x): - dispersion = fdiv(1., self.dispersion_factor)*(np.sqrt(1./(1+t0**2))) - wave_part = complex_to_real_func(np.exp( - complex(0, TAU*freq*(x-dispersion)) - )) - bell_part = np.exp(-dispersion*(x-t0)**2) + dispersion = fdiv(1.0, self.dispersion_factor) * ( + np.sqrt(1.0 / (1 + t0**2)) + ) + wave_part = complex_to_real_func( + np.exp(complex(0, TAU * freq * (x - dispersion))) + ) + bell_part = np.exp(-dispersion * (x - t0) ** 2) amplitude = self.amplitude - return amplitude*wave_part*bell_part + return amplitude * wave_part * bell_part + graph = axes.get_graph(func) return graph - def get_wave_pair(self, axes, particle, colors = None, **kwargs): + def get_wave_pair(self, axes, particle, colors=None, **kwargs): if colors is None and "color" not in kwargs: colors = self.wave_colors - return VGroup(*[ - self.get_wave( - axes, particle, - C_to_R, color = color, - **kwargs - ) - for C_to_R, color in zip( - [lambda z : z.imag, lambda z : z.real], - colors - ) - ]) + return VGroup( + *[ + self.get_wave(axes, particle, C_to_R, color=color, **kwargs) + for C_to_R, color in zip([lambda z: z.imag, lambda z: z.real], colors) + ] + ) + class ShowMomentumFormula(IntroduceDeBroglie, TeacherStudentsScene): CONFIG = { - "default_wave_frequency" : 2, - "dispersion_factor" : 0.25, - "p_color" : BLUE, - "xi_color" : YELLOW, - "amplitude" : 0.5, + "default_wave_frequency": 2, + "dispersion_factor": 0.25, + "p_color": BLUE, + "xi_color": YELLOW, + "amplitude": 0.5, } + def construct(self): self.introduce_formula() self.react_to_claim() @@ -3048,100 +3046,105 @@ def introduce_formula(self): word_shift_val = 1.75 p_words = TextMobject("Momentum") - p_words.next_to(p, UP, LARGE_BUFF).shift(word_shift_val*LEFT) - p_arrow = Arrow( - p_words.get_bottom(), p.get_corner(UP+LEFT), - buff = SMALL_BUFF - ) + p_words.next_to(p, UP, LARGE_BUFF).shift(word_shift_val * LEFT) + p_arrow = Arrow(p_words.get_bottom(), p.get_corner(UP + LEFT), buff=SMALL_BUFF) added_p_words = TextMobject("(Classically $m \\times v$)") added_p_words.move_to(p_words, DOWN) VGroup(p, p_words, added_p_words, p_arrow).set_color(self.p_color) xi_words = TextMobject("Spatial frequency") added_xi_words = TextMobject("(cycles per unit \\emph{distance})") - xi_words.next_to(xi, UP, LARGE_BUFF).shift(word_shift_val*RIGHT) + xi_words.next_to(xi, UP, LARGE_BUFF).shift(word_shift_val * RIGHT) xi_words.align_to(p_words) xi_arrow = Arrow( - xi_words.get_bottom(), xi.get_corner(UP+RIGHT), - buff = SMALL_BUFF + xi_words.get_bottom(), xi.get_corner(UP + RIGHT), buff=SMALL_BUFF ) added_xi_words.move_to(xi_words, DOWN) added_xi_words.align_to(added_p_words, DOWN) VGroup(xi, xi_words, added_xi_words, xi_arrow).set_color(self.xi_color) axes = Axes( - x_min = 0, x_max = FRAME_WIDTH, - y_min = -1, y_max = 1, + x_min=0, + x_max=FRAME_WIDTH, + y_min=-1, + y_max=1, ) - axes.center().to_edge(UP, buff = -0.5) + axes.center().to_edge(UP, buff=-0.5) # axes.next_to(formula, RIGHT) particle = VectorizedPoint() wave_update_animation = self.get_wave_update_animation(axes, particle) wave = wave_update_animation.mobject - wave[0].set_stroke(width = 0) - particle.next_to(wave, LEFT, buff = 2) + wave[0].set_stroke(width=0) + particle.next_to(wave, LEFT, buff=2) wave_propagation = AnimationGroup( ApplyMethod(particle.move_to, axes.coords_to_point(30, 0)), wave_update_animation, - run_time = 4, + run_time=4, rate_func=linear, ) stopped_wave_propagation = AnimationGroup( ApplyMethod(particle.move_to, xi_words), wave_update_animation, - run_time = 3, + run_time=3, rate_func=linear, ) n_v_lines = 10 - v_lines = VGroup(*[ - DashedLine(UP, DOWN) - for x in range(n_v_lines) - ]) + v_lines = VGroup(*[DashedLine(UP, DOWN) for x in range(n_v_lines)]) v_lines.match_color(xi) v_lines.arrange( - RIGHT, - buff = float(axes.x_axis.unit_size)/self.default_wave_frequency + RIGHT, buff=float(axes.x_axis.unit_size) / self.default_wave_frequency ) v_lines.move_to(stopped_wave_propagation.sub_anims[0].target_mobject) v_lines.align_to(wave) - v_lines.shift(0.125*RIGHT) - + v_lines.shift(0.125 * RIGHT) + self.add(formula, wave) self.play( - self.teacher.change, "raise_right_hand", + self.teacher.change, + "raise_right_hand", GrowArrow(p_arrow), Succession( - Write, p_words, - ApplyMethod, p_words.next_to, added_p_words, UP, + Write, + p_words, + ApplyMethod, + p_words.next_to, + added_p_words, + UP, ), FadeIn( added_p_words, - rate_func = squish_rate_func(smooth, 0.5, 1), - run_time = 2, + rate_func=squish_rate_func(smooth, 0.5, 1), + run_time=2, ), - wave_propagation + wave_propagation, ) self.play( Write(xi_words), GrowArrow(xi_arrow), self.get_student_changes("confused", "erm", "sassy"), - stopped_wave_propagation + stopped_wave_propagation, ) self.play( FadeIn(added_xi_words), - xi_words.next_to, added_xi_words, UP, + xi_words.next_to, + added_xi_words, + UP, ) self.play( LaggedStartMap(ShowCreation, v_lines), - self.get_student_changes(*["pondering"]*3) + self.get_student_changes(*["pondering"] * 3), ) self.play(LaggedStartMap(FadeOut, v_lines)) self.wait() self.formula_labels = VGroup( - p_words, p_arrow, added_p_words, - xi_words, xi_arrow, added_xi_words, - ) + p_words, + p_arrow, + added_p_words, + xi_words, + xi_arrow, + added_xi_words, + ) self.set_variables_as_attrs(wave, wave_propagation, formula) def react_to_claim(self): @@ -3153,40 +3156,39 @@ def react_to_claim(self): student = self.students[2] self.student_says( "Hang on...", - bubble_kwargs = {"height" : 2, "width" : 2, "direction" : LEFT}, - target_mode = "sassy", - student_index = 2, - added_anims = [self.teacher.change, "plain"] + bubble_kwargs={"height": 2, "width": 2, "direction": LEFT}, + target_mode="sassy", + student_index=2, + added_anims=[self.teacher.change, "plain"], ) student.bubble.add(student.bubble.content) self.wait() kwargs = { - "path_arc" : TAU/4, - "lag_ratio" : 0.5, - "lag_ratio" : 0.7, - "run_time" : 1.5, + "path_arc": TAU / 4, + "lag_ratio": 0.7, + "run_time": 1.5, } self.play( - full_formula.scale, 0, - full_formula.move_to, student.eyes.get_bottom()+SMALL_BUFF*DOWN, + full_formula.scale, + 0, + full_formula.move_to, + student.eyes.get_bottom() + SMALL_BUFF * DOWN, Animation(student.bubble), **kwargs ) self.play(full_formula.restore, Animation(student.bubble), **kwargs) - wave_propagation.update_config( - rate_func = lambda a : interpolate(0.35, 1, a) - ) + wave_propagation.update_config(rate_func=lambda a: interpolate(0.35, 1, a)) self.play( - wave_propagation, - RemovePiCreatureBubble(student, target_mode = "confused"), + wave_propagation, + RemovePiCreatureBubble(student, target_mode="confused"), ) - wave_propagation.update_config(rate_func = lambda t : t) + wave_propagation.update_config(rate_func=lambda t: t) self.student_says( "Physics is \\\\ just weird", - bubble_kwargs = {"height" : 2.5, "width" : 3}, - target_mode = "shruggie", - student_index = 0, - added_anims = [ApplyMethod(full_formula.shift, UP)] + bubble_kwargs={"height": 2.5, "width": 3}, + target_mode="shruggie", + student_index=0, + added_anims=[ApplyMethod(full_formula.shift, UP)], ) self.wait() self.play( @@ -3194,19 +3196,23 @@ def react_to_claim(self): ApplyMethod(full_formula.shift, DOWN), FadeOut(self.students[0].bubble), FadeOut(self.students[0].bubble.content), - self.get_student_changes(*3*["pondering"]), - self.teacher.change, "pondering", + self.get_student_changes(*3 * ["pondering"]), + self.teacher.change, + "pondering", ) self.play(wave_propagation) + class AskPhysicists(PiCreatureScene): def construct(self): morty, physy1, physy2, physy3 = self.pi_creatures formula = TexMobject("p", "=", "h", "\\xi") - formula.set_color_by_tex_to_color_map({ - "p" : BLUE, - "\\xi" : YELLOW, - }) + formula.set_color_by_tex_to_color_map( + { + "p": BLUE, + "\\xi": YELLOW, + } + ) formula.scale(1.5) formula.to_edge(UP) @@ -3214,119 +3220,111 @@ def construct(self): formula.shift(DOWN) formula.fade(1) self.play(formula.restore) - self.pi_creature_says( - morty, "So...why?", - target_mode = "maybe" - ) + self.pi_creature_says(morty, "So...why?", target_mode="maybe") self.wait(2) self.play( RemovePiCreatureBubble(morty), PiCreatureSays( physy2, "Take the Schrödinger equation \\\\ with $H = \\frac{p^2}{2m}+V(x)$", - bubble_kwargs = {"fill_opacity" : 0.9}, + bubble_kwargs={"fill_opacity": 0.9}, ), ) self.play( PiCreatureSays( physy1, "Even classically position and \\\\ momentum are conjugate", - target_mode = "surprised", - bubble_kwargs = {"fill_opacity" : 0.9}, + target_mode="surprised", + bubble_kwargs={"fill_opacity": 0.9}, ), ) self.play( PiCreatureSays( physy3, "Consider special relativity \\\\ together with $E = hf$", - target_mode = "hooray", - bubble_kwargs = {"fill_opacity" : 0.9}, + target_mode="hooray", + bubble_kwargs={"fill_opacity": 0.9}, ), - morty.change, "guilty" + morty.change, + "guilty", ) self.wait(2) - - ### def create_pi_creatures(self): scale_factor = 0.85 morty = Mortimer().flip() morty.scale(scale_factor) - morty.to_corner(DOWN+LEFT) + morty.to_corner(DOWN + LEFT) - physies = VGroup(*[ - PiCreature(color = c).flip() - for c in (GREY, LIGHT_GREY, DARK_GREY) - ]) - physies.arrange(RIGHT, buff = MED_SMALL_BUFF) + physies = VGroup( + *[PiCreature(color=c).flip() for c in (GREY, LIGHT_GREY, DARK_GREY)] + ) + physies.arrange(RIGHT, buff=MED_SMALL_BUFF) physies.scale(scale_factor) - physies.to_corner(DOWN+RIGHT) + physies.to_corner(DOWN + RIGHT) self.add(physies) return VGroup(morty, *physies) + class SortOfDopplerEffect(PiCreatureScene): CONFIG = { - "omega" : np.pi, - "arrow_spacing" : 0.25, + "omega": np.pi, + "arrow_spacing": 0.25, } + def setup(self): PiCreatureScene.setup(self) - rect = self.screen_rect = ScreenRectangle(height = FRAME_HEIGHT) - rect.set_stroke(width = 0) - self.camera = MovingCamera( - rect, **self.camera_config - ) + rect = self.screen_rect = ScreenRectangle(height=FRAME_HEIGHT) + rect.set_stroke(width=0) + self.camera = MovingCamera(rect, **self.camera_config) def construct(self): screen_rect = self.screen_rect - #x-coordinate gives time + # x-coordinate gives time t_tracker = VectorizedPoint() - #x-coordinate gives wave number - k_tracker = VectorizedPoint(2*RIGHT) + # x-coordinate gives wave number + k_tracker = VectorizedPoint(2 * RIGHT) always_shift(t_tracker, RIGHT, 1) + def get_wave(): t = t_tracker.get_center()[0] k = k_tracker.get_center()[0] omega = self.omega - color = interpolate_color( - BLUE, RED, (k-2)/2.0 - ) - func = lambda x : 0.5*np.cos(omega*t - k*x) + color = interpolate_color(BLUE, RED, (k - 2) / 2.0) + func = lambda x: 0.5 * np.cos(omega * t - k * x) graph = FunctionGraph( func, - x_min = -5*FRAME_X_RADIUS, - x_max = FRAME_X_RADIUS, - color = color, + x_min=-5 * FRAME_X_RADIUS, + x_max=FRAME_X_RADIUS, + color=color, ) - return VGroup(graph, *[ - Arrow( - x*RIGHT, x*RIGHT + func(x)*UP, - color = color - ) - for x in np.arange( - -4*FRAME_X_RADIUS, FRAME_X_RADIUS, - self.arrow_spacing - ) - ]) - return + return VGroup( + graph, + *[ + Arrow(x * RIGHT, x * RIGHT + func(x) * UP, color=color) + for x in np.arange( + -4 * FRAME_X_RADIUS, FRAME_X_RADIUS, self.arrow_spacing + ) + ] + ) + return + wave = get_wave() wave_update = Mobject.add_updater( - wave, lambda w : Transform(w, get_wave()).update(1) + wave, lambda w: Transform(w, get_wave()).update(1) ) - rect = ScreenRectangle(height = 2) + rect = ScreenRectangle(height=2) rect.to_edge(RIGHT) always_shift(rect, LEFT, 1) rect_movement = rect randy = self.pi_creature - randy_look_at = Mobject.add_updater( - randy, lambda r : r.look_at(rect) - ) + randy_look_at = Mobject.add_updater(randy, lambda r: r.look_at(rect)) ref_frame1 = TextMobject("Reference frame 1") # ref_frame1.next_to(randy, UP, aligned_edge = LEFT) @@ -3335,13 +3333,17 @@ def get_wave(): ref_frame2.next_to(rect, UP) # ref_frame2.set_fill(opacity = 0) ref_frame2_follow = Mobject.add_updater( - ref_frame2, lambda m : m.next_to(rect, UP) + ref_frame2, lambda m: m.next_to(rect, UP) ) ref_frame_1_continual_anim = ContinualAnimation(ref_frame1) self.add( - t_tracker, wave_update, rect_movement, randy_look_at, - ref_frame2_follow, ref_frame_1_continual_anim + t_tracker, + wave_update, + rect_movement, + randy_look_at, + ref_frame2_follow, + ref_frame_1_continual_anim, ) self.add(ref_frame1) self.play(randy.change, "pondering") @@ -3351,24 +3353,22 @@ def get_wave(): self.play( UpdateFromAlphaFunc( screen_rect, - lambda m, a : m.move_to( - interpolate(start_center, rect.get_center(), a) - ) + lambda m, a: m.move_to(interpolate(start_center, rect.get_center(), a)), ), - k_tracker.shift, 2*RIGHT, + k_tracker.shift, + 2 * RIGHT, ) self.play( - MaintainPositionRelativeTo( - screen_rect, rect, - run_time = 4 - ), + MaintainPositionRelativeTo(screen_rect, rect, run_time=4), ) self.play( - screen_rect.move_to, rect.get_right()+FRAME_X_RADIUS*LEFT, - k_tracker.shift, 2*LEFT, + screen_rect.move_to, + rect.get_right() + FRAME_X_RADIUS * LEFT, + k_tracker.shift, + 2 * LEFT, ) - #Frequency words + # Frequency words temporal_frequency = TextMobject("Temporal", "frequency") spatial_frequency = TextMobject("Spatial", "frequency") temporal_frequency.move_to(screen_rect).to_edge(UP) @@ -3377,7 +3377,7 @@ def get_wave(): time = TextMobject("Time") space = TextMobject("Space") - time.next_to(temporal_frequency, RIGHT, buff = 2) + time.next_to(temporal_frequency, RIGHT, buff=2) space.next_to(time, DOWN) space.align_to(spatial_frequency) @@ -3389,8 +3389,8 @@ def get_wave(): self.play( Transform(time, space), Transform(space, time), - lag_ratio = 0.5, - run_time = 1, + lag_ratio=0.5, + run_time=1, ) self.play(FadeOut(time), FadeOut(space)) self.wait(3) @@ -3398,16 +3398,18 @@ def get_wave(): ### def create_pi_creature(self): - return Randolph().scale(0.5).to_corner(DOWN+LEFT) + return Randolph().scale(0.5).to_corner(DOWN + LEFT) + class HangingWeightsScene(MovingCameraScene): CONFIG = { - "frequency" : 0.5, - "ceiling_radius" : 3*FRAME_X_RADIUS, - "n_springs" : 72, - "amplitude" : 0.6, - "spring_radius" : 0.15, + "frequency": 0.5, + "ceiling_radius": 3 * FRAME_X_RADIUS, + "n_springs": 72, + "amplitude": 0.6, + "spring_radius": 0.15, } + def construct(self): self.setup_springs() self.setup_weights() @@ -3419,26 +3421,28 @@ def construct(self): def setup_springs(self): ceiling = self.ceiling = Line(LEFT, RIGHT) ceiling.scale(self.ceiling_radius) - ceiling.to_edge(UP, buff = LARGE_BUFF) + ceiling.to_edge(UP, buff=LARGE_BUFF) self.add(ceiling) - def get_spring(alpha, height = 2): + def get_spring(alpha, height=2): t_max = 6.5 r = self.spring_radius - s = (height - r)/(t_max**2) + s = (height - r) / (t_max**2) spring = ParametricFunction( - lambda t : op.add( - r*(np.sin(TAU*t)*RIGHT+np.cos(TAU*t)*UP), - s*((t_max - t)**2)*DOWN, + lambda t: op.add( + r * (np.sin(TAU * t) * RIGHT + np.cos(TAU * t) * UP), + s * ((t_max - t) ** 2) * DOWN, ), - t_min = 0, t_max = t_max, - color = WHITE, - stroke_width = 2, + t_min=0, + t_max=t_max, + color=WHITE, + stroke_width=2, ) spring.alpha = alpha spring.move_to(ceiling.point_from_proportion(alpha), UP) spring.color_using_background_image("grey_gradient") return spring + alphas = np.linspace(0, 1, self.n_springs) bezier([0, 1, 0, 1]) springs = self.springs = VGroup(*list(map(get_spring, alphas))) @@ -3448,6 +3452,7 @@ def get_spring(alpha, height = 2): always_shift(t_tracker, RIGHT, 1) self.t_tracker_walk = t_tracker equilibrium_height = springs.get_height() + def update_springs(springs): for spring in springs: k = k_tracker.get_center()[0] @@ -3455,35 +3460,33 @@ def update_springs(springs): f = self.frequency x = spring.get_top()[0] A = self.amplitude - d_height = A*np.cos(TAU*f*t - k*x) - new_spring = get_spring(spring.alpha, 2+d_height) + d_height = A * np.cos(TAU * f * t - k * x) + new_spring = get_spring(spring.alpha, 2 + d_height) Transform(spring, new_spring).update(1) + spring_update_anim = Mobject.add_updater(springs, update_springs) self.spring_update_anim = spring_update_anim spring_update_anim.update(0) - self.play( - ShowCreation(ceiling), - LaggedStartMap(ShowCreation, springs) - ) + self.play(ShowCreation(ceiling), LaggedStartMap(ShowCreation, springs)) def setup_weights(self): weights = self.weights = VGroup() weight_anims = weight_anims = [] for spring in self.springs: x = spring.get_top()[0] - mass = np.exp(-0.1*x**2) - weight = Circle(radius = 0.15) + mass = np.exp(-0.1 * x**2) + weight = Circle(radius=0.15) weight.start_radius = 0.15 - weight.target_radius = 0.25*mass #For future update + weight.target_radius = 0.25 * mass # For future update weight.spring = spring weight_anim = Mobject.add_updater( - weight, lambda w : w.move_to(w.spring.get_bottom()) + weight, lambda w: w.move_to(w.spring.get_bottom()) ) weight_anim.update(0) weight_anims.append(weight_anim) weights.add(weight) - weights.set_fill(opacity = 1) + weights.set_fill(opacity=1) weights.set_color_by_gradient(BLUE_D, BLUE_E, BLUE_D) weights.set_stroke(WHITE, 1) @@ -3493,28 +3496,30 @@ def setup_weights(self): self.add(*weight_anims) def introduce(self): - arrow = Arrow(4*LEFT, LEFT) - arrows = VGroup(arrow, arrow.copy().flip(about_point = ORIGIN)) + arrow = Arrow(4 * LEFT, LEFT) + arrows = VGroup(arrow, arrow.copy().flip(about_point=ORIGIN)) arrows.set_color(WHITE) self.wait(3) self.play(*list(map(GrowArrow, arrows))) - self.play(*[ - UpdateFromAlphaFunc( - weight, lambda w, a : w.set_width( - 2*interpolate(w.start_radius, w.target_radius, a) - ), - run_time = 2 - ) - for weight in self.weights - ]) + self.play( + *[ + UpdateFromAlphaFunc( + weight, + lambda w, a: w.set_width( + 2 * interpolate(w.start_radius, w.target_radius, a) + ), + run_time=2, + ) + for weight in self.weights + ] + ) self.play(FadeOut(arrows)) self.wait(3) def show_analogy_with_electron(self): words = TextMobject( - "Analogous to the energy of a particle \\\\", - "(in the sense of $E=mc^2$)" + "Analogous to the energy of a particle \\\\", "(in the sense of $E=mc^2$)" ) words.move_to(DOWN) @@ -3525,33 +3530,30 @@ def show_analogy_with_electron(self): def metaphor_for_something(self): de_broglie = ImageMobject("de_Broglie") de_broglie.set_height(3.5) - de_broglie.to_corner(DOWN+RIGHT) - words = TextMobject(""" + de_broglie.to_corner(DOWN + RIGHT) + words = TextMobject( + """ If a photon's energy is carried as a wave \\\\ is this true for any particle? - """) + """ + ) words.next_to(de_broglie, LEFT) einstein = ImageMobject("Einstein") einstein.match_height(de_broglie) - einstein.to_corner(DOWN+LEFT) + einstein.to_corner(DOWN + LEFT) for picture in de_broglie, einstein: picture.backdrop = Rectangle() - picture.backdrop.replace(picture, stretch = True) + picture.backdrop.replace(picture, stretch=True) picture.backdrop.set_fill(BLACK, 1) picture.backdrop.set_stroke(BLACK, 0) - self.play( - Animation(de_broglie.backdrop, remover = True), - FadeIn(de_broglie) - ) + self.play(Animation(de_broglie.backdrop, remover=True), FadeIn(de_broglie)) self.play(Write(words)) self.wait(7) self.play( - FadeOut(words), - Animation(einstein.backdrop, remover = True), - FadeIn(einstein) + FadeOut(words), Animation(einstein.backdrop, remover=True), FadeIn(einstein) ) self.wait(2) @@ -3559,19 +3561,19 @@ def metaphor_for_something(self): self.einstein = einstein def moving_reference_frame(self): - rect = ScreenRectangle(height = 2.1*FRAME_Y_RADIUS) - rect_movement = always_shift(rect, direction = LEFT, rate = 2) + rect = ScreenRectangle(height=2.1 * FRAME_Y_RADIUS) + rect_movement = always_shift(rect, direction=LEFT, rate=2) camera_frame = self.camera_frame self.add(rect) - self.play( - Animation(self.de_broglie.backdrop, remover = True), + self.play( + Animation(self.de_broglie.backdrop, remover=True), FadeOut(self.de_broglie), - Animation(self.einstein.backdrop, remover = True), + Animation(self.einstein.backdrop, remover=True), FadeOut(self.einstein), ) - self.play(camera_frame.scale, 3, {"about_point" : 2*UP}) - self.play(rect.shift, FRAME_WIDTH*RIGHT, path_arc = -TAU/2) + self.play(camera_frame.scale, 3, {"about_point": 2 * UP}) + self.play(rect.shift, FRAME_WIDTH * RIGHT, path_arc=-TAU / 2) self.add(rect_movement) self.wait(3) @@ -3580,32 +3582,30 @@ def zoom_into_reference_frame(): original_center = camera_frame.get_center() self.play( UpdateFromAlphaFunc( - camera_frame, lambda c, a : c.set_height( - interpolate(original_height, 0.95*rect.get_height(), a) - ).move_to( - interpolate(original_center, rect.get_center(), a) - ) + camera_frame, + lambda c, a: c.set_height( + interpolate(original_height, 0.95 * rect.get_height(), a) + ).move_to(interpolate(original_center, rect.get_center(), a)), ), - ApplyMethod(self.k_tracker.shift, RIGHT) + ApplyMethod(self.k_tracker.shift, RIGHT), ) - self.play(MaintainPositionRelativeTo( - camera_frame, rect, - run_time = 6 - )) + self.play(MaintainPositionRelativeTo(camera_frame, rect, run_time=6)) self.play( - camera_frame.set_height, original_height, - camera_frame.move_to, original_center, - ApplyMethod(self.k_tracker.shift, LEFT) + camera_frame.set_height, + original_height, + camera_frame.move_to, + original_center, + ApplyMethod(self.k_tracker.shift, LEFT), ) zoom_into_reference_frame() self.wait() self.play( - UpdateFromAlphaFunc(rect, lambda m, a : m.set_stroke(width = 2*(1-a))) + UpdateFromAlphaFunc(rect, lambda m, a: m.set_stroke(width=2 * (1 - a))) ) - index = int(0.5*len(self.springs)) - weights = VGroup(self.weights[index], self.weights[index+4]) + index = int(0.5 * len(self.springs)) + weights = VGroup(self.weights[index], self.weights[index + 4]) flashes = list(map(self.get_peak_flash_anim, weights)) weights.save_state() weights.set_fill(RED) @@ -3613,20 +3613,23 @@ def zoom_into_reference_frame(): self.wait(5) rect.align_to(camera_frame, RIGHT) - self.play(UpdateFromAlphaFunc(rect, lambda m, a : m.set_stroke(width = 2*a))) + self.play(UpdateFromAlphaFunc(rect, lambda m, a: m.set_stroke(width=2 * a))) - randy = Randolph(mode = "pondering") - randy.look(UP+RIGHT) + randy = Randolph(mode="pondering") + randy.look(UP + RIGHT) de_broglie = ImageMobject("de_Broglie") de_broglie.set_height(6) - de_broglie.next_to(4*DOWN, DOWN) + de_broglie.next_to(4 * DOWN, DOWN) self.add( Mobject.add_updater( - randy, lambda m : m.next_to( - rect.get_corner(DOWN+LEFT), UP+RIGHT, MED_LARGE_BUFF, - ).look_at(weights) + randy, + lambda m: m.next_to( + rect.get_corner(DOWN + LEFT), + UP + RIGHT, + MED_LARGE_BUFF, + ).look_at(weights), ), - de_broglie + de_broglie, ) self.wait(2) @@ -3636,12 +3639,13 @@ def zoom_into_reference_frame(): ### def get_peak_flash_anim(self, weight): - mobject = Mobject() #Dummy + mobject = Mobject() # Dummy mobject.last_y = 0 mobject.last_dy = 0 mobject.curr_anim = None mobject.curr_anim_time = 0 mobject.time_since_last_flash = 0 + def update(mob, dt): mob.time_since_last_flash += dt point = weight.get_center() @@ -3651,9 +3655,9 @@ def update(mob, dt): if different_dy and mob.time_since_last_flash > 0.5: mob.curr_anim = Flash( VectorizedPoint(point), - flash_radius = 0.5, - line_length = 0.3, - run_time = 0.2, + flash_radius=0.5, + line_length=0.3, + run_time=0.2, ) mob.submobjects = [mob.curr_anim.mobject] mob.time_since_last_flash = 0 @@ -3667,18 +3671,19 @@ def update(mob, dt): mob.submobjects = [] mob.curr_anim_time = 0 return - mob.curr_anim.update(mob.curr_anim_time/mob.curr_anim.run_time) + mob.curr_anim.update(mob.curr_anim_time / mob.curr_anim.run_time) return Mobject.add_updater(mobject, update) + class MinutPhysicsWrapper(Scene): def construct(self): - logo = ImageMobject("minute_physics_logo", invert = True) - logo.to_corner(UP+LEFT) + logo = ImageMobject("minute_physics_logo", invert=True) + logo.to_corner(UP + LEFT) self.add(logo) title = TextMobject("Minute Physics on special relativity") - title.to_edge(UP).shift(MED_LARGE_BUFF*RIGHT) + title.to_edge(UP).shift(MED_LARGE_BUFF * RIGHT) screen_rect = ScreenRectangle() screen_rect.set_width(title.get_width() + LARGE_BUFF) @@ -3688,21 +3693,24 @@ def construct(self): self.play(Write(title)) self.wait(2) + class WhatDoesTheFourierTradeoffTellUs(TeacherStudentsScene): def construct(self): self.teacher_says( "So! What does \\\\ the Fourier trade-off \\\\ tell us?", - target_mode = "surprised", - bubble_kwargs = {"width" : 4, "height" : 3} + target_mode="surprised", + bubble_kwargs={"width": 4, "height": 3}, ) - self.change_student_modes(*["thinking"]*3) + self.change_student_modes(*["thinking"] * 3) self.wait(4) + class FourierTransformOfWaveFunction(Scene): CONFIG = { - "wave_stroke_width" : 3, - "wave_color" : BLUE, + "wave_stroke_width": 3, + "wave_color": BLUE, } + def construct(self): self.show_wave_packet() self.take_fourier_transform() @@ -3717,27 +3725,24 @@ def setup(self): def show_wave_packet(self): axes = Axes( - x_min = 0, x_max = 12, - y_min = -1, y_max = 1, - y_axis_config = { - "tick_frequency" : 0.5 - } + x_min=0, x_max=12, y_min=-1, y_max=1, y_axis_config={"tick_frequency": 0.5} ) position_label = TextMobject("Position") position_label.next_to(axes.x_axis.get_right(), UP) axes.add(position_label) - axes.center().to_edge(UP, buff = LARGE_BUFF) + axes.center().to_edge(UP, buff=LARGE_BUFF) wave = self.get_wave(axes) wave_update_animation = UpdateFromFunc( - wave, lambda w : Transform(w, self.get_wave(axes)).update(1) + wave, lambda w: Transform(w, self.get_wave(axes)).update(1) ) self.add(axes, wave) self.play( - self.x0_tracker.set_value, 5, + self.x0_tracker.set_value, + 5, wave_update_animation, - run_time = 3, + run_time=3, ) self.wait() @@ -3750,36 +3755,39 @@ def take_fourier_transform(self): wave = self.wave wave_update_animation = self.wave_update_animation frequency_axes = Axes( - x_min = 0, x_max = 3, - x_axis_config = { - "unit_size" : 4, - "tick_frequency" : 0.25, - "numbers_with_elongated_ticks" : [1, 2] + x_min=0, + x_max=3, + x_axis_config={ + "unit_size": 4, + "tick_frequency": 0.25, + "numbers_with_elongated_ticks": [1, 2], + }, + y_min=-0.15, + y_max=0.15, + y_axis_config={ + "unit_size": 7.5, + "tick_frequency": 0.05, }, - y_min = -0.15, - y_max = 0.15, - y_axis_config = { - "unit_size" : 7.5, - "tick_frequency" : 0.05, - } ) label = self.frequency_x_axis_label = TextMobject("Spatial frequency") label.next_to(frequency_axes.x_axis.get_right(), UP) frequency_axes.add(label) frequency_axes.move_to(self.axes, LEFT) - frequency_axes.to_edge(DOWN, buff = LARGE_BUFF) + frequency_axes.to_edge(DOWN, buff=LARGE_BUFF) label.shift_onto_screen() def get_wave_function_fourier_graph(): return get_fourier_graph( - frequency_axes, self.get_wave_func(), - t_min = 0, t_max = 15, + frequency_axes, + self.get_wave_func(), + t_min=0, + t_max=15, ) + fourier_graph = get_wave_function_fourier_graph() self.fourier_graph_update_animation = UpdateFromFunc( - fourier_graph, lambda m : Transform( - m, get_wave_function_fourier_graph() - ).update(1) + fourier_graph, + lambda m: Transform(m, get_wave_function_fourier_graph()).update(1), ) wave_copy = wave.copy() @@ -3793,19 +3801,17 @@ def get_wave_function_fourier_graph(): arrow = Arrow( self.axes.coords_to_point(5, -1), frequency_axes.coords_to_point(1, 0.1), - color = YELLOW, + color=YELLOW, ) fourier_label = TextMobject("Fourier Transform") fourier_label.next_to(arrow.get_center(), RIGHT) - self.play(ReplacementTransform( - self.axes.copy(), frequency_axes - )) + self.play(ReplacementTransform(self.axes.copy(), frequency_axes)) self.play( - MoveToTarget(wave_copy, remover = True), + MoveToTarget(wave_copy, remover=True), fourier_graph.restore, GrowArrow(arrow), - Write(fourier_label, run_time = 1), + Write(fourier_label, run_time=1), ) self.wait() @@ -3818,82 +3824,81 @@ def show_correlations_with_pure_frequencies(self): axes = self.axes sinusoid = axes.get_graph( - lambda x : 0.5*np.cos(TAU*x), - x_min = -FRAME_X_RADIUS, x_max = 3*FRAME_X_RADIUS, + lambda x: 0.5 * np.cos(TAU * x), + x_min=-FRAME_X_RADIUS, + x_max=3 * FRAME_X_RADIUS, ) - sinusoid.to_edge(UP, buff = SMALL_BUFF) + sinusoid.to_edge(UP, buff=SMALL_BUFF) - v_line = DashedLine(1.5*UP, ORIGIN, color = YELLOW) + v_line = DashedLine(1.5 * UP, ORIGIN, color=YELLOW) v_line.move_to(frequency_axes.coords_to_point(1, 0), DOWN) f_equals = TexMobject("f = ") freq_decimal = DecimalNumber(1) - freq_decimal.next_to(f_equals, RIGHT, buff = SMALL_BUFF) + freq_decimal.next_to(f_equals, RIGHT, buff=SMALL_BUFF) freq_label = VGroup(f_equals, freq_decimal) - freq_label.next_to( - v_line, UP, SMALL_BUFF, - submobject_to_align = f_equals[0] - ) + freq_label.next_to(v_line, UP, SMALL_BUFF, submobject_to_align=f_equals[0]) self.play( ShowCreation(sinusoid), ShowCreation(v_line), - Write(freq_label, run_time = 1), - FadeOut(self.fourier_label) + Write(freq_label, run_time=1), + FadeOut(self.fourier_label), ) last_f = 1 for f in 1.4, 0.7, 1: self.play( - sinusoid.stretch,f/last_f, 0, - {"about_point" : axes.coords_to_point(0, 0)}, - v_line.move_to, frequency_axes.coords_to_point(f, 0), DOWN, + sinusoid.stretch, + f / last_f, + 0, + {"about_point": axes.coords_to_point(0, 0)}, + v_line.move_to, + frequency_axes.coords_to_point(f, 0), + DOWN, MaintainPositionRelativeTo(freq_label, v_line), ChangeDecimalToValue(freq_decimal, f), - run_time = 3, + run_time=3, ) last_f = f - self.play(*list(map(FadeOut, [ - sinusoid, v_line, freq_label - ]))) + self.play(*list(map(FadeOut, [sinusoid, v_line, freq_label]))) def this_is_momentum(self): formula = TexMobject("p", "=", "h", "\\xi") - formula.set_color_by_tex_to_color_map({ - "p" : BLUE, - "xi" : YELLOW, - }) - formula.next_to( - self.frequency_x_axis_label, UP + formula.set_color_by_tex_to_color_map( + { + "p": BLUE, + "xi": YELLOW, + } ) + formula.next_to(self.frequency_x_axis_label, UP) f_max = 0.12 - brace = Brace(Line(2*LEFT, 2*RIGHT), UP) + brace = Brace(Line(2 * LEFT, 2 * RIGHT), UP) brace.move_to(self.frequency_axes.coords_to_point(1, f_max), DOWN) words = TextMobject("This wave \\\\ describes momentum") words.next_to(brace, UP) self.play(Write(formula)) self.wait() - self.play( - GrowFromCenter(brace), - Write(words) - ) + self.play(GrowFromCenter(brace), Write(words)) brace.add(words) for k in 2, 0.5, 1: self.play( - self.k_tracker.set_value, k, + self.k_tracker.set_value, + k, self.wave_update_animation, self.fourier_graph_update_animation, UpdateFromFunc( - brace, lambda b : b.move_to( + brace, + lambda b: b.move_to( self.frequency_axes.coords_to_point( self.k_tracker.get_value(), f_max, ), - DOWN - ) + DOWN, + ), ), - run_time = 2 + run_time=2, ) self.wait() self.play(*list(map(FadeOut, [brace, words, formula]))) @@ -3901,12 +3906,9 @@ def this_is_momentum(self): def show_tradeoff(self): for a in 5, 0.1, 0.01, 10, 0.5: self.play( - ApplyMethod( - self.a_tracker.set_value, a, - run_time = 2 - ), + ApplyMethod(self.a_tracker.set_value, a, run_time=2), self.wave_update_animation, - self.fourier_graph_update_animation + self.fourier_graph_update_animation, ) self.wait() @@ -3916,85 +3918,91 @@ def get_wave_func(self): x0 = self.x0_tracker.get_value() k = self.k_tracker.get_value() a = self.a_tracker.get_value() - A = a**(0.25) - return lambda x : A*np.cos(TAU*k*x)*np.exp(-a*(x - x0)**2) + A = a ** (0.25) + return lambda x: A * np.cos(TAU * k * x) * np.exp(-a * (x - x0) ** 2) def get_wave(self, axes): return axes.get_graph( - self.get_wave_func(), - color = self.wave_color, - stroke_width = self.wave_stroke_width + self.get_wave_func(), + color=self.wave_color, + stroke_width=self.wave_stroke_width, ) + class DopplerComparisonTodos(TODOStub): CONFIG = { - "message" : """ + "message": """ Insert some Doppler footage, insert some hanging spring scene, insert position-momentum Fourier trade-off """ } + class MusicalNote(AddingPureFrequencies): def construct(self): - speaker = self.speaker = SVGMobject(file_name = "speaker") - speaker.move_to(2*DOWN) + speaker = self.speaker = SVGMobject(file_name="speaker") + speaker.move_to(2 * DOWN) randy = self.pi_creature - axes = Axes( - x_min = 0, x_max = 10, - y_min = -1.5, y_max = 1.5 - ) + axes = Axes(x_min=0, x_max=10, y_min=-1.5, y_max=1.5) axes.center().to_edge(UP) time_label = TextMobject("Time") time_label.next_to(axes.x_axis.get_right(), UP) axes.add(time_label) graph = axes.get_graph( - lambda x : op.mul( - np.exp(-0.2*(x-4)**2), - 0.3*(np.cos(2*TAU*x) + np.cos(3*TAU*x) + np.cos(5*TAU*x)), + lambda x: op.mul( + np.exp(-0.2 * (x - 4) ** 2), + 0.3 * (np.cos(2 * TAU * x) + np.cos(3 * TAU * x) + np.cos(5 * TAU * x)), ), ) graph.set_color(BLUE) - v_line = DashedLine(ORIGIN, 0.5*UP) + v_line = DashedLine(ORIGIN, 0.5 * UP) v_line_update = UpdateFromFunc( - v_line, lambda l : l.put_start_and_end_on_with_projection( + v_line, + lambda l: l.put_start_and_end_on_with_projection( graph.points[-1], axes.x_axis.number_to_point( axes.x_axis.point_to_number(graph.points[-1]) - ) - ) + ), + ), ) self.add(speaker, axes) self.play( - randy.change, "pondering", - self.get_broadcast_animation(n_circles = 6, run_time = 5), - self.get_broadcast_animation(n_circles = 12, run_time = 5), - ShowCreation(graph, run_time = 5, rate_func=linear), - v_line_update + randy.change, + "pondering", + self.get_broadcast_animation(n_circles=6, run_time=5), + self.get_broadcast_animation(n_circles=12, run_time=5), + ShowCreation(graph, run_time=5, rate_func=linear), + v_line_update, ) self.wait(2) + class AskAboutUncertainty(TeacherStudentsScene): def construct(self): self.student_says( "What does this have \\\\ to do with ``certainty''", - bubble_kwargs = {"direction" : LEFT}, - student_index = 2 - ) - self.play(PiCreatureSays( - self.students[0], - "What even are \\\\ these waves?", - target_mode = "confused" - )) + bubble_kwargs={"direction": LEFT}, + student_index=2, + ) + self.play( + PiCreatureSays( + self.students[0], + "What even are \\\\ these waves?", + target_mode="confused", + ) + ) self.wait(2) + class ProbabalisticDetection(FourierTransformOfWaveFunction): CONFIG = { - "wave_stroke_width" : 2, + "wave_stroke_width": 2, } + def construct(self): self.setup_wave() self.detect_only_single_points() @@ -4003,54 +4011,54 @@ def construct(self): def setup_wave(self): axes = Axes( - x_min = 0, x_max = 10, - y_min = -0.5, y_max = 1.5, - y_axis_config = { - "unit_size" : 1.5, - "tick_frequency" : 0.25, - } + x_min=0, + x_max=10, + y_min=-0.5, + y_max=1.5, + y_axis_config={ + "unit_size": 1.5, + "tick_frequency": 0.25, + }, ) - axes.set_stroke(width = 2) + axes.set_stroke(width=2) axes.center() self.x0_tracker.set_value(5) self.k_tracker.set_value(1) self.a_tracker.set_value(0.2) wave = self.get_wave(axes) self.wave_update_animation = UpdateFromFunc( - wave, lambda w : Transform(w, self.get_wave(axes)).update(1) + wave, lambda w: Transform(w, self.get_wave(axes)).update(1) ) self.k_tracker.save_state() self.k_tracker.set_value(0) bell_curve = self.get_wave(axes) self.k_tracker.restore() - bell_curve.set_stroke(width = 0) - bell_curve.set_fill(BLUE, opacity = 0.5) + bell_curve.set_stroke(width=0) + bell_curve.set_fill(BLUE, opacity=0.5) squared_bell_curve = axes.get_graph( - lambda x : bell_curve.underlying_function(x)**2 + lambda x: bell_curve.underlying_function(x) ** 2 ).match_style(bell_curve) - self.set_variables_as_attrs( - axes, wave, bell_curve, squared_bell_curve - ) + self.set_variables_as_attrs(axes, wave, bell_curve, squared_bell_curve) def detect_only_single_points(self): particle = ProbabalisticDotCloud( - n_copies = 100, - fill_opacity = 0.05, - time_per_change = 0.05, + n_copies=100, + fill_opacity=0.05, + time_per_change=0.05, ) - particle.mobject[0].set_fill(BLUE, opacity = 1) + particle.mobject[0].set_fill(BLUE, opacity=1) gdw = particle.gaussian_distribution_wrapper rect = Rectangle( - stroke_width = 0, - height = 0.5, - width = 2, + stroke_width=0, + height=0.5, + width=2, ) rect.set_fill(YELLOW, 0.3) rect.move_to(self.axes.coords_to_point(self.x0_tracker.get_value(), 0)) - brace = Brace(rect, UP, buff = 0) + brace = Brace(rect, UP, buff=0) question = TextMobject("Do we detect the particle \\\\ in this region?") question.next_to(brace, UP) question.add_background_rectangle() @@ -4058,9 +4066,10 @@ def detect_only_single_points(self): rect.stretch(0, 0) gdw_anim = Mobject.add_updater( - gdw, lambda m : m.set_width( - 2.0/(self.a_tracker.get_value()**(0.5)) - ).move_to(rect) + gdw, + lambda m: m.set_width(2.0 / (self.a_tracker.get_value() ** (0.5))).move_to( + rect + ), ) self.add(rect, brace, question) @@ -4071,6 +4080,7 @@ def detect_only_single_points(self): word.next_to(rect, DOWN) # word.add_background_rectangle() answer = VGroup() + def update_answer(answer): px = particle.mobject[0].get_center()[0] lx = rect.get_left()[0] @@ -4079,14 +4089,11 @@ def update_answer(answer): answer.submobjects = [yes] else: answer.submobjects = [no] + answer_anim = Mobject.add_updater(answer, update_answer) self.add(gdw_anim, particle) - self.play( - GrowFromCenter(brace), - rect.restore, - Write(question) - ) + self.play(GrowFromCenter(brace), rect.restore, Write(question)) self.wait() self.add(answer_anim) self.wait(4) @@ -4104,73 +4111,82 @@ def show_probability_distribution(self): gdw = self.particle.gaussian_distribution_wrapper rect = self.rect - v_lines = VGroup(*[ - DashedLine(ORIGIN, 3*UP).move_to(point, DOWN) - for point in (rect.get_left(), rect.get_right()) - ]) - + v_lines = VGroup( + *[ + DashedLine(ORIGIN, 3 * UP).move_to(point, DOWN) + for point in (rect.get_left(), rect.get_right()) + ] + ) + self.play( FadeIn(VGroup(axes, wave)), - question_group.next_to, v_lines, UP, {"buff" : 0}, + question_group.next_to, + v_lines, + UP, + {"buff": 0}, *list(map(ShowCreation, v_lines)) ) self.wait(10) def show_concentration_of_the_wave(self): self.play( - self.a_tracker.set_value, 5, + self.a_tracker.set_value, + 5, self.wave_update_animation, ) self.wait(10) + class HeisenbergCommentTodos(TODOStub): - CONFIG = { - "message" : "Insert position-momentum trade-off" - } + CONFIG = {"message": "Insert position-momentum trade-off"} + class HeisenbergPetPeeve(PiCreatureScene): def construct(self): morty, other = self.pi_creatures particle = ProbabalisticDotCloud() gdw = particle.gaussian_distribution_wrapper - gdw.to_edge(UP, buff = LARGE_BUFF) + gdw.to_edge(UP, buff=LARGE_BUFF) gdw.stretch_to_fit_width(3) - gdw.rotate(3*DEGREES) + gdw.rotate(3 * DEGREES) self.add(particle) self.wait() - self.play(PiCreatureSays( - other, """ + self.play( + PiCreatureSays( + other, + """ According to the H.U.P., the \\\\ universe is unknowable! """, - target_mode = "speaking" - )) + target_mode="speaking", + ) + ) self.play(morty.change, "angry") self.wait(3) self.play( PiCreatureSays( - morty, "Well, yes and no", - target_mode = "sassy", + morty, + "Well, yes and no", + target_mode="sassy", ), - RemovePiCreatureBubble( - other, target_mode = "erm" - ) + RemovePiCreatureBubble(other, target_mode="erm"), ) self.wait(4) ### def create_pi_creatures(self): morty = Mortimer() - morty.to_corner(DOWN+RIGHT) - other = PiCreature(color = MAROON_E) - other.to_edge(DOWN).shift(3*LEFT) + morty.to_corner(DOWN + RIGHT) + other = PiCreature(color=MAROON_E) + other.to_edge(DOWN).shift(3 * LEFT) return VGroup(morty, other) + class OneLevelDeeper(Scene): def construct(self): heisenberg = ImageMobject("Heisenberg") - heisenberg.to_corner(UP+LEFT) + heisenberg.to_corner(UP + LEFT) self.add(heisenberg) hup_words = TextMobject("Heisenberg's uncertainty principle") @@ -4180,12 +4196,10 @@ def construct(self): group.arrange(DOWN) randomness = ProbabalisticMobjectCloud( - TextMobject("Randomness"), - n_copies = 5, - time_per_change = 0.05 + TextMobject("Randomness"), n_copies=5, time_per_change=0.05 ) gdw = randomness.gaussian_distribution_wrapper - gdw.rotate(TAU/4) + gdw.rotate(TAU / 4) gdw.set_height(1) # gdw.set_width(4) gdw.next_to(hup_words, UP, MED_LARGE_BUFF) @@ -4196,12 +4210,16 @@ def construct(self): FadeIn(wave_words), GrowArrow(arrow), ApplyMethod( - gdw.next_to, wave_words, DOWN, MED_LARGE_BUFF, - path_arc = TAU/2, - ) + gdw.next_to, + wave_words, + DOWN, + MED_LARGE_BUFF, + path_arc=TAU / 2, + ), ) self.wait(6) + class BetterTranslation(TeacherStudentsScene): def construct(self): english_term = TextMobject("Uncertainty principle") @@ -4210,67 +4228,60 @@ def construct(self): to_german_words = TextMobject("In German") to_german_words.scale(0.5) - to_german_arrow = Vector(DOWN, color = WHITE, buff = SMALL_BUFF) + to_german_arrow = Vector(DOWN, color=WHITE, buff=SMALL_BUFF) to_german_words.next_to(to_german_arrow, RIGHT, SMALL_BUFF) to_german_words.set_color(YELLOW) to_german_group = VGroup(to_german_arrow, to_german_words) translation_words = TextMobject("Literal translation") translation_words.scale(0.5) - translation_arrow = Vector(DOWN, color = WHITE, buff = SMALL_BUFF) + translation_arrow = Vector(DOWN, color=WHITE, buff=SMALL_BUFF) translation_words.next_to(translation_arrow, LEFT, SMALL_BUFF) translation_words.set_color(YELLOW) translation_group = VGroup(translation_arrow, translation_words) - english_term.next_to(self.teacher, UP+LEFT) + english_term.next_to(self.teacher, UP + LEFT) english_term.save_state() english_term.shift(DOWN) english_term.fade(1) - self.play( - english_term.restore, - self.get_student_changes(*["pondering"]*3) - ) + self.play(english_term.restore, self.get_student_changes(*["pondering"] * 3)) self.wait() german_word.move_to(english_term) - to_german_group.next_to( - german_word, UP, - submobject_to_align = to_german_arrow - ) + to_german_group.next_to(german_word, UP, submobject_to_align=to_german_arrow) self.play( - self.teacher.change, "raise_right_hand", - english_term.next_to, to_german_arrow, UP + self.teacher.change, + "raise_right_hand", + english_term.next_to, + to_german_arrow, + UP, ) self.play( GrowArrow(to_german_arrow), FadeIn(to_german_words), - ReplacementTransform( - english_term.copy().fade(1), - german_word - ) + ReplacementTransform(english_term.copy().fade(1), german_word), ) self.wait(2) group = VGroup(english_term, to_german_group, german_word) translation.move_to(german_word) translation_group.next_to( - german_word, UP, - submobject_to_align = translation_arrow + german_word, UP, submobject_to_align=translation_arrow ) self.play( - group.next_to, translation_arrow, UP, + group.next_to, + translation_arrow, + UP, ) self.play( GrowArrow(translation_arrow), FadeIn(translation_words), - ReplacementTransform( - german_word.copy().fade(1), - translation - ) + ReplacementTransform(german_word.copy().fade(1), translation), ) - self.change_student_modes(*["happy"]*3) + self.change_student_modes(*["happy"] * 3) self.wait(2) + class ThinkOfHeisenbergUncertainty(PiCreatureScene): def construct(self): morty = self.pi_creature @@ -4279,41 +4290,40 @@ def construct(self): dot_cloud = ProbabalisticDotCloud() dot_gdw = dot_cloud.gaussian_distribution_wrapper dot_gdw.set_width(1) - dot_gdw.rotate(TAU/8) - dot_gdw.move_to(FRAME_X_RADIUS*RIGHT/2), + dot_gdw.rotate(TAU / 8) + dot_gdw.move_to(FRAME_X_RADIUS * RIGHT / 2), - vector_cloud = ProbabalisticVectorCloud( - center_func = dot_gdw.get_center - ) + vector_cloud = ProbabalisticVectorCloud(center_func=dot_gdw.get_center) vector_gdw = vector_cloud.gaussian_distribution_wrapper vector_gdw.set_width(0.1) - vector_gdw.rotate(TAU/8) - vector_gdw.next_to(dot_gdw, UP+LEFT, LARGE_BUFF) + vector_gdw.rotate(TAU / 8) + vector_gdw.next_to(dot_gdw, UP + LEFT, LARGE_BUFF) time_tracker = ValueTracker(0) self.add() freq = 1 continual_anims = [ - always_shift(time_tracker, direction = RIGHT, rate = 1), + always_shift(time_tracker, direction=RIGHT, rate=1), Mobject.add_updater( dot_gdw, - lambda d : d.set_width( - (np.cos(freq*time_tracker.get_value()) + 1.1)/2 - ) + lambda d: d.set_width( + (np.cos(freq * time_tracker.get_value()) + 1.1) / 2 + ), ), Mobject.add_updater( vector_gdw, - lambda d : d.set_width( - (-np.cos(freq*time_tracker.get_value()) + 1.1)/2 - ) + lambda d: d.set_width( + (-np.cos(freq * time_tracker.get_value()) + 1.1) / 2 + ), ), - dot_cloud, vector_cloud + dot_cloud, + vector_cloud, ] self.add(*continual_anims) - position, momentum, time, frequency = list(map(TextMobject, [ - "Position", "Momentum", "Time", "Frequency" - ])) + position, momentum, time, frequency = list( + map(TextMobject, ["Position", "Momentum", "Time", "Frequency"]) + ) VGroup(position, time).set_color(BLUE) VGroup(momentum, frequency).set_color(YELLOW) groups = VGroup() @@ -4324,46 +4334,52 @@ def construct(self): lp, rp = parens = TexMobject("\\big(\\big)") parens.stretch(1.5, 1) parens.match_height(group) - lp.next_to(group, LEFT, buff = SMALL_BUFF) - rp.next_to(group, RIGHT, buff = SMALL_BUFF) + lp.next_to(group, LEFT, buff=SMALL_BUFF) + rp.next_to(group, RIGHT, buff=SMALL_BUFF) group.add(parens) groups.add(group) arrow = TexMobject("\\Leftrightarrow").scale(2) groups.submobjects.insert(1, arrow) groups.arrange(RIGHT) - groups.next_to(morty, UP+RIGHT, LARGE_BUFF) + groups.next_to(morty, UP + RIGHT, LARGE_BUFF) groups.shift_onto_screen() - - self.play(PiCreatureBubbleIntroduction( - morty, "Heisenberg \\\\ uncertainty \\\\ principle", - bubble_class = ThoughtBubble, - bubble_kwargs = {"height" : 4, "width" : 4, "direction" : RIGHT}, - target_mode = "pondering" - )) + self.play( + PiCreatureBubbleIntroduction( + morty, + "Heisenberg \\\\ uncertainty \\\\ principle", + bubble_class=ThoughtBubble, + bubble_kwargs={"height": 4, "width": 4, "direction": RIGHT}, + target_mode="pondering", + ) + ) self.wait() self.play(morty.change, "confused", dot_gdw) self.wait(10) self.play( ApplyMethod( - VGroup(dot_gdw, vector_gdw ).shift, - FRAME_X_RADIUS*RIGHT, - rate_func = running_start + VGroup(dot_gdw, vector_gdw).shift, + FRAME_X_RADIUS * RIGHT, + rate_func=running_start, ) ) self.remove(*continual_anims) self.play( - morty.change, "raise_left_hand", groups, + morty.change, + "raise_left_hand", + groups, FadeIn( - groups, - lag_ratio = 0.5, - run_time = 3, - ) + groups, + lag_ratio=0.5, + run_time=3, + ), ) self.wait(2) + # End things + class PatreonMention(PatreonThanks): def construct(self): morty = Mortimer() @@ -4375,92 +4391,95 @@ def construct(self): thank_you = TextMobject("Thank you.") thank_you.next_to(patreon_logo, DOWN) - self.play( - DrawBorderThenFill(patreon_logo), - morty.change, "gracious" - ) + self.play(DrawBorderThenFill(patreon_logo), morty.change, "gracious") self.play(Write(thank_you)) self.wait(3) + class Promotion(PiCreatureScene): CONFIG = { - "camera_class" : ThreeDCamera, - "seconds_to_blink" : 5, + "camera_class": ThreeDCamera, + "seconds_to_blink": 5, } + def construct(self): aops_logo = AoPSLogo() - aops_logo.next_to(self.pi_creature, UP+LEFT) - url = TextMobject( - "AoPS.com/", "3b1b", - arg_separator = "" - ) - url.to_corner(UP+LEFT) - url_rect = Rectangle(color = BLUE) - url_rect.replace( - url.get_part_by_tex("3b1b"), - stretch = True - ) + aops_logo.next_to(self.pi_creature, UP + LEFT) + url = TextMobject("AoPS.com/", "3b1b", arg_separator="") + url.to_corner(UP + LEFT) + url_rect = Rectangle(color=BLUE) + url_rect.replace(url.get_part_by_tex("3b1b"), stretch=True) - url_rect.stretch_in_place(1.1, dim = 1) + url_rect.stretch_in_place(1.1, dim=1) - rect = Rectangle(height = 9, width = 16) + rect = Rectangle(height=9, width=16) rect.set_height(4.5) rect.next_to(url, DOWN) rect.to_edge(LEFT) - rect.set_stroke(width = 0) + rect.set_stroke(width=0) mathy = Mathematician() mathy.flip() - mathy.to_corner(DOWN+RIGHT) + mathy.to_corner(DOWN + RIGHT) morty = self.pi_creature morty.save_state() book = ImageMobject("AoPS_volume_2") book.set_height(2) - book.next_to(mathy, UP+LEFT).shift(MED_LARGE_BUFF*LEFT) + book.next_to(mathy, UP + LEFT).shift(MED_LARGE_BUFF * LEFT) mathy.get_center = mathy.get_top - words = TextMobject(""" + words = TextMobject( + """ Interested in working for \\\\ one of my favorite math\\\\ education companies? - """, alignment = "") + """, + alignment="", + ) words.to_edge(UP) arrow = Arrow( aops_logo.get_top(), morty.get_top(), - path_arc = -0.4*TAU, - stroke_width = 5, - tip_length = 0.5, + path_arc=-0.4 * TAU, + stroke_width=5, + tip_length=0.5, ) - arrow.tip.shift(SMALL_BUFF*DOWN) + arrow.tip.shift(SMALL_BUFF * DOWN) self.add(words) self.play( - self.pi_creature.change_mode, "raise_right_hand", + self.pi_creature.change_mode, + "raise_right_hand", *[ DrawBorderThenFill( submob, - run_time = 2, - rate_func = squish_rate_func(double_smooth, a, a+0.5) + run_time=2, + rate_func=squish_rate_func(double_smooth, a, a + 0.5), ) for submob, a in zip(aops_logo, np.linspace(0, 0.5, len(aops_logo))) ] ) self.play( - words.scale, 0.75, - words.next_to, url, DOWN, LARGE_BUFF, + words.scale, + 0.75, + words.next_to, + url, + DOWN, + LARGE_BUFF, words.shift_onto_screen, Write(url), ) self.wait(2) self.play( LaggedStartMap( - ApplyFunction, aops_logo, - lambda mob : (lambda m : m.shift(0.2*UP).set_color(YELLOW), mob), - rate_func = there_and_back, - run_time = 1, + ApplyFunction, + aops_logo, + lambda mob: (lambda m: m.shift(0.2 * UP).set_color(YELLOW), mob), + rate_func=there_and_back, + run_time=1, ), - morty.change, "thinking" + morty.change, + "thinking", ) self.wait() self.play(ShowCreation(arrow)) @@ -4469,41 +4488,47 @@ def construct(self): # To teacher self.play( - morty.change_mode, "plain", + morty.change_mode, + "plain", morty.flip, - morty.scale, 0.7, - morty.next_to, mathy, LEFT, LARGE_BUFF, - morty.to_edge, DOWN, + morty.scale, + 0.7, + morty.next_to, + mathy, + LEFT, + LARGE_BUFF, + morty.to_edge, + DOWN, FadeIn(mathy), ) self.play( PiCreatureSays( - mathy, "", - bubble_kwargs = {"width" : 5}, - look_at_arg = morty.eyes, + mathy, + "", + bubble_kwargs={"width": 5}, + look_at_arg=morty.eyes, ), - morty.change, "happy", - aops_logo.shift, 1.5*UP + 0.5*RIGHT + morty.change, + "happy", + aops_logo.shift, + 1.5 * UP + 0.5 * RIGHT, ) self.play(Blink(mathy)) self.wait() self.play( - RemovePiCreatureBubble( - mathy, target_mode = "raise_right_hand" - ), - aops_logo.to_corner, UP+RIGHT, - aops_logo.shift, MED_SMALL_BUFF*DOWN, - GrowFromPoint(book, mathy.get_corner(UP+LEFT)), + RemovePiCreatureBubble(mathy, target_mode="raise_right_hand"), + aops_logo.to_corner, + UP + RIGHT, + aops_logo.shift, + MED_SMALL_BUFF * DOWN, + GrowFromPoint(book, mathy.get_corner(UP + LEFT)), ) self.play(morty.change, "pondering", book) self.wait(3) self.play(Blink(mathy)) self.wait() self.play( - Animation( - BackgroundRectangle(book, fill_opacity = 1), - remover = True - ), + Animation(BackgroundRectangle(book, fill_opacity=1), remover=True), FadeOut(book), ) print(self.num_plays) @@ -4511,17 +4536,21 @@ def construct(self): FadeOut(words), ShowCreation(rect), morty.restore, - morty.change, "happy", rect, + morty.change, + "happy", + rect, FadeOut(mathy), ) self.wait(10) self.play(ShowCreation(url_rect)) self.play( FadeOut(url_rect), - url.get_part_by_tex("3b1b").set_color, BLUE, + url.get_part_by_tex("3b1b").set_color, + BLUE, ) self.wait(15) + class PuzzleStatement(Scene): def construct(self): aops_logo = AoPSLogo() @@ -4531,7 +4560,8 @@ def construct(self): group.to_edge(UP) self.add(group) - words = TextMobject(""" + words = TextMobject( + """ AoPS must choose one of 20 people to send to a tug-of-war tournament. We don't care who we send, as long as we don't send our weakest person. \\\\ \\\\ @@ -4540,15 +4570,18 @@ def construct(self): those strengths. We get 10 intramural 10-on-10 matches to determine who we send. Can we make sure we don't send the weakest person? - """, alignment = "") + """, + alignment="", + ) words.set_width(FRAME_WIDTH - 2) words.next_to(group, DOWN, LARGE_BUFF) - self.play(LaggedStartMap(FadeIn, words, run_time = 5, lag_ratio = 0.2)) + self.play(LaggedStartMap(FadeIn, words, run_time=5, lag_ratio=0.2)) self.wait(2) + class UncertaintyEndScreen(PatreonEndScreen): CONFIG = { - "specific_patrons" : [ + "specific_patrons": [ "CrypticSwarm", "Ali Yahya", "Juan Benet", @@ -4639,10 +4672,11 @@ class UncertaintyEndScreen(PatreonEndScreen): ], } + class Thumbnail(Scene): def construct(self): uncertainty_principle = TextMobject("Uncertainty \\\\", "principle") - uncertainty_principle[1].shift(SMALL_BUFF*UP) + uncertainty_principle[1].shift(SMALL_BUFF * UP) quantum = TextMobject("Quantum") VGroup(uncertainty_principle, quantum).scale(2.5) uncertainty_principle.to_edge(UP, MED_LARGE_BUFF) @@ -4650,10 +4684,12 @@ def construct(self): arrow = TexMobject("\\Downarrow") arrow.scale(4) - arrow.move_to(Line( - uncertainty_principle.get_bottom(), - quantum.get_top(), - )) + arrow.move_to( + Line( + uncertainty_principle.get_bottom(), + quantum.get_top(), + ) + ) cross = Cross(arrow) cross.set_stroke(RED, 20) @@ -4664,47 +4700,52 @@ def construct(self): # is_word.shift(0.6*UP) not_word.set_color(RED) not_word.set_stroke(RED, 3) - not_word.rotate(10*DEGREES, about_edge = DOWN+LEFT) - not_word.next_to(is_word, DOWN, 0.1*SMALL_BUFF) + not_word.rotate(10 * DEGREES, about_edge=DOWN + LEFT) + not_word.next_to(is_word, DOWN, 0.1 * SMALL_BUFF) dot_cloud = ProbabalisticDotCloud( - n_copies = 1000, + n_copies=1000, ) dot_gdw = dot_cloud.gaussian_distribution_wrapper # dot_gdw.rotate(3*DEGREES) - dot_gdw.rotate(25*DEGREES) + dot_gdw.rotate(25 * DEGREES) # dot_gdw.scale(2) dot_gdw.scale(2) # dot_gdw.move_to(quantum.get_bottom()+SMALL_BUFF*DOWN) dot_gdw.move_to(quantum) - - def get_func(a): - return lambda t : 0.5*np.exp(-a*t**2)*np.cos(TAU*t) - axes = Axes( - x_min = -6, x_max = 6, - x_axis_config = {"unit_size" : 0.25} - ) - graphs = VGroup(*[ - axes.get_graph(get_func(a)) - for a in (10, 3, 1, 0.3, 0.1,) - ]) - graphs.arrange(DOWN, buff = 0.6) - graphs.to_corner(UP+LEFT) + return lambda t: 0.5 * np.exp(-a * t**2) * np.cos(TAU * t) + + axes = Axes(x_min=-6, x_max=6, x_axis_config={"unit_size": 0.25}) + graphs = VGroup( + *[ + axes.get_graph(get_func(a)) + for a in ( + 10, + 3, + 1, + 0.3, + 0.1, + ) + ] + ) + graphs.arrange(DOWN, buff=0.6) + graphs.to_corner(UP + LEFT) graphs.set_color_by_gradient(BLUE_B, BLUE_D) - frequency_axes = Axes( - x_min = 0, x_max = 2, - x_axis_config = {"unit_size" : 1} + frequency_axes = Axes(x_min=0, x_max=2, x_axis_config={"unit_size": 1}) + fourier_graphs = VGroup( + *[ + get_fourier_graph( + frequency_axes, + graph.underlying_function, + t_min=-10, + t_max=10, + ) + for graph in graphs + ] ) - fourier_graphs = VGroup(*[ - get_fourier_graph( - frequency_axes, graph.underlying_function, - t_min = -10, t_max = 10, - ) - for graph in graphs - ]) for graph, fourier_graph in zip(graphs, fourier_graphs): fourier_graph.pointwise_become_partial(fourier_graph, 0.02, 0.06) fourier_graph.scale(3) @@ -4714,26 +4755,11 @@ def get_func(a): self.add(graphs, fourier_graphs) - self.add(dot_cloud) self.add( - uncertainty_principle, quantum, + uncertainty_principle, + quantum, ) self.add(arrow, cross) # self.add(is_word) # self.add(is_not) - - - - - - - - - - - - - - - diff --git a/manimlib/animation/specialized.py b/manimlib/animation/specialized.py index c4f5457d99..6267788220 100644 --- a/manimlib/animation/specialized.py +++ b/manimlib/animation/specialized.py @@ -30,10 +30,12 @@ def check_if_input_is_car(self, car): def begin(self): super().begin() car = self.mobject - distance = get_norm(op.sub( - self.target_mobject.get_right(), - self.starting_mobject.get_right(), - )) + distance = get_norm( + op.sub( + self.target_mobject.get_right(), + self.starting_mobject.get_right(), + ) + ) if not self.moving_forward: distance *= -1 tire_radius = car.get_tires()[0].get_width() / 2 @@ -55,7 +57,6 @@ class Broadcast(LaggedStart): "n_circles": 5, "start_stroke_width": 8, "color": WHITE, - "remover": True, "lag_ratio": 0.2, "run_time": 3, "remover": True, @@ -70,15 +71,10 @@ def __init__(self, focal_point, **kwargs): stroke_color=BLACK, stroke_width=0, ) - circle.add_updater( - lambda c: c.move_to(focal_point) - ) + circle.add_updater(lambda c: c.move_to(focal_point)) circle.save_state() circle.set_width(self.small_radius * 2) circle.set_stroke(self.color, self.start_stroke_width) circles.add(circle) - animations = [ - Restore(circle) - for circle in circles - ] + animations = [Restore(circle) for circle in circles] super().__init__(*animations, **kwargs) diff --git a/manimlib/scene/vector_space_scene.py b/manimlib/scene/vector_space_scene.py index 825cf758de..811e1b271c 100644 --- a/manimlib/scene/vector_space_scene.py +++ b/manimlib/scene/vector_space_scene.py @@ -42,9 +42,7 @@ # Also, methods I would have thought of as getters, like coords_to_vector, are # actually doing a lot of animating. class VectorScene(Scene): - CONFIG = { - "basis_vector_stroke_width": 6 - } + CONFIG = {"basis_vector_stroke_width": 6} def add_plane(self, animate=False, **kwargs): """ @@ -56,7 +54,7 @@ def add_plane(self, animate=False, **kwargs): Whether or not to animate the addition of the plane via ShowCreation. **kwargs Any valid keyword arguments accepted by NumberPlane. - + Returns ------- NumberPlane @@ -89,12 +87,12 @@ def lock_in_faded_grid(self, dimness=0.7, axes_dimness=0.5): """ This method freezes the NumberPlane and Axes that were already in the background, and adds new, manipulatable ones to the foreground. - + Parameters ---------- dimness (Union[int,float=0.7]) The required dimness of the NumberPlane - + axes_dimness (Union[int,float=0.5]) The required dimness of the Axes. """ @@ -109,14 +107,14 @@ def lock_in_faded_grid(self, dimness=0.7, axes_dimness=0.5): def get_vector(self, numerical_vector, **kwargs): """ Returns an arrow on the Plane given an input numerical vector. - + Parameters ---------- numerical_vector : Union(np.ndarray, list, tuple) The Vector to plot. **kwargs Any valid keyword argument of Arrow. - + Returns ------- Arrow @@ -138,21 +136,21 @@ def add_vector(self, vector, color=YELLOW, animate=True, **kwargs): vector Union(Arrow,list,tuple,np.ndarray) It can be a pre-made graphical vector, or the coordinates of one. - + color (str) The string of the hex color of the vector. This is only taken into consideration if 'vector' is not an Arrow. Defaults to YELLOW. - + animate (bool=True) Whether or not to animate the addition of the vector by using GrowArrow - + **kwargs Any valid keyword argument of Arrow. These are only considered if vector is not an Arrow. - + Returns ------- Arrow @@ -174,10 +172,10 @@ def write_vector_coordinates(self, vector, **kwargs): ---------- vector (Arrow) The arrow representing the vector. - + **kwargs Any valid keyword arguments of matrix.vector_coordinate_label - + integer_labels (True) : Whether or not to round the coordinates to integers. n_dim (2) : The number of dimensions of the vector. @@ -200,26 +198,21 @@ def get_basis_vectors(self, i_hat_color=X_COLOR, j_hat_color=Y_COLOR): ---------- i_hat_color (str) The hex colour to use for the basis vector in the x direction - + j_hat_color (str) The hex colour to use for the basis vector in the y direction - + Returns ------- VGroup VGroup of the Vector Mobjects representing the basis vectors. """ - return VGroup(*[ - Vector( - vect, - color=color, - stroke_width=self.basis_vector_stroke_width - ) - for vect, color in [ - ([1, 0], i_hat_color), - ([0, 1], j_hat_color) + return VGroup( + *[ + Vector(vect, color=color, stroke_width=self.basis_vector_stroke_width) + for vect, color in [([1, 0], i_hat_color), ([0, 1], j_hat_color)] ] - ]) + ) def get_basis_vector_labels(self, **kwargs): """ @@ -238,24 +231,28 @@ def get_basis_vector_labels(self, **kwargs): label_scale_factor=VECTOR_LABEL_SCALE_FACTOR (int, float), """ i_hat, j_hat = self.get_basis_vectors() - return VGroup(*[ - self.get_vector_label( - vect, label, color=color, - label_scale_factor=1, - **kwargs - ) - for vect, label, color in [ - (i_hat, "\\hat{\\imath}", X_COLOR), - (j_hat, "\\hat{\\jmath}", Y_COLOR), + return VGroup( + *[ + self.get_vector_label( + vect, label, color=color, label_scale_factor=1, **kwargs + ) + for vect, label, color in [ + (i_hat, "\\hat{\\imath}", X_COLOR), + (j_hat, "\\hat{\\jmath}", Y_COLOR), + ] ] - ]) + ) - def get_vector_label(self, vector, label, - at_tip=False, - direction="left", - rotate=False, - color=None, - label_scale_factor=VECTOR_LABEL_SCALE_FACTOR): + def get_vector_label( + self, + vector, + label, + at_tip=False, + direction="left", + rotate=False, + color=None, + label_scale_factor=VECTOR_LABEL_SCALE_FACTOR, + ): """ Returns naming labels for the passed vector. @@ -273,7 +270,7 @@ def get_vector_label(self, vector, label, The color to give the label. label_scale_factor (Union[int,float]) How much to scale the label by. - + Returns ------- TexMobject @@ -320,7 +317,7 @@ def label_vector(self, vector, label, animate=True, **kwargs): Whether or not to animate the labelling w/ Write **kwargs Any valid keyword argument of get_vector_label - + Returns ------- TexMobject @@ -332,12 +329,16 @@ def label_vector(self, vector, label, animate=True, **kwargs): self.add(label) return label - def position_x_coordinate(self, x_coord, x_line, vector): #TODO Write DocStrings for this. + def position_x_coordinate( + self, x_coord, x_line, vector + ): # TODO Write DocStrings for this. x_coord.next_to(x_line, -np.sign(vector[1]) * UP) x_coord.set_color(X_COLOR) return x_coord - def position_y_coordinate(self, y_coord, y_line, vector): #TODO Write DocStrings for this. + def position_y_coordinate( + self, y_coord, y_line, vector + ): # TODO Write DocStrings for this. y_coord.next_to(y_line, np.sign(vector[0]) * RIGHT) y_coord.set_color(Y_COLOR) return y_coord @@ -353,13 +354,13 @@ def coords_to_vector(self, vector, coords_start=2 * RIGHT + 2 * UP, clean_up=Tru ---------- vector Union(np.ndarray, list, tuple) The vector to show. - + coords_start Union(np.ndarray,list,tuple) - The starting point of the location of - the label of the vector that shows it + The starting point of the location of + the label of the vector that shows it numerically. Defaults to 2 * RIGHT + 2 * UP or (2,2) - + clean_up (bool=True) Whether or not to remove whatever this method did after it's done. @@ -377,17 +378,17 @@ def coords_to_vector(self, vector, coords_start=2 * RIGHT + 2 * UP, clean_up=Tru self.play(Write(array, run_time=1)) self.wait() - self.play(ApplyFunction( - lambda x: self.position_x_coordinate(x, x_line, vector), - x_coord - )) + self.play( + ApplyFunction( + lambda x: self.position_x_coordinate(x, x_line, vector), x_coord + ) + ) self.play(ShowCreation(x_line)) self.play( ApplyFunction( - lambda y: self.position_y_coordinate(y, y_line, vector), - y_coord + lambda y: self.position_y_coordinate(y, y_line, vector), y_coord ), - FadeOut(array.get_brackets()) + FadeOut(array.get_brackets()), ) y_coord, brackets = self.get_mobjects_from_last_animation() self.play(ShowCreation(y_line)) @@ -399,20 +400,20 @@ def coords_to_vector(self, vector, coords_start=2 * RIGHT + 2 * UP, clean_up=Tru def vector_to_coords(self, vector, integer_labels=True, clean_up=True): """ - This method displays vector as a Vector() based vector, and then shows - the corresponding lines that make up the x and y components of the vector. - Then, a column matrix (henceforth called the label) is created near the + This method displays vector as a Vector() based vector, and then shows + the corresponding lines that make up the x and y components of the vector. + Then, a column matrix (henceforth called the label) is created near the head of the Vector. Parameters ---------- vector Union(np.ndarray, list, tuple) The vector to show. - + integer_label (bool=True) Whether or not to round the value displayed. in the vector's label to the nearest integer - + clean_up (bool=True) Whether or not to remove whatever this method did after it's done. @@ -432,26 +433,14 @@ def vector_to_coords(self, vector, integer_labels=True, clean_up=True): x_line.set_color(X_COLOR) y_line.set_color(Y_COLOR) x_coord, y_coord = array.get_mob_matrix().flatten() - x_coord_start = self.position_x_coordinate( - x_coord.copy(), x_line, vector - ) - y_coord_start = self.position_y_coordinate( - y_coord.copy(), y_line, vector - ) + x_coord_start = self.position_x_coordinate(x_coord.copy(), x_line, vector) + y_coord_start = self.position_y_coordinate(y_coord.copy(), y_line, vector) brackets = array.get_brackets() if show_creation: self.play(ShowCreation(arrow)) - self.play( - ShowCreation(x_line), - Write(x_coord_start), - run_time=1 - ) - self.play( - ShowCreation(y_line), - Write(y_coord_start), - run_time=1 - ) + self.play(ShowCreation(x_line), Write(x_coord_start), run_time=1) + self.play(ShowCreation(y_line), Write(y_coord_start), run_time=1) self.wait() self.play( Transform(x_coord_start, x_coord, lag_ratio=0), @@ -470,7 +459,7 @@ def vector_to_coords(self, vector, integer_labels=True, clean_up=True): def show_ghost_movement(self, vector): """ This method plays an animation that partially shows the entire plane moving - in the direction of a particular vector. This is useful when you wish to + in the direction of a particular vector. This is useful when you wish to convey the idea of mentally moving the entire plane in a direction, without actually moving the plane. @@ -485,21 +474,19 @@ def show_ghost_movement(self, vector): vector = np.append(np.array(vector), 0.0) x_max = int(FRAME_X_RADIUS + abs(vector[0])) y_max = int(FRAME_Y_RADIUS + abs(vector[1])) - dots = VMobject(*[ - Dot(x * RIGHT + y * UP) - for x in range(-x_max, x_max) - for y in range(-y_max, y_max) - ]) + dots = VMobject( + *[ + Dot(x * RIGHT + y * UP) + for x in range(-x_max, x_max) + for y in range(-y_max, y_max) + ] + ) dots.set_fill(BLACK, opacity=0) dots_halfway = dots.copy().shift(vector / 2).set_fill(WHITE, 1) dots_end = dots.copy().shift(vector) - self.play(Transform( - dots, dots_halfway, rate_func=rush_into - )) - self.play(Transform( - dots, dots_end, rate_func=rush_from - )) + self.play(Transform(dots, dots_halfway, rate_func=rush_into)) + self.play(Transform(dots, dots_end, rate_func=rush_from)) self.remove(dots) @@ -508,6 +495,7 @@ class LinearTransformationScene(VectorScene): This scene contains special methods that make it especially suitable for showing Linear Transformations. """ + CONFIG = { "include_background_plane": True, "include_foreground_plane": True, @@ -516,13 +504,10 @@ class LinearTransformationScene(VectorScene): "x_min": -FRAME_WIDTH / 2, "y_max": FRAME_WIDTH / 2, "y_min": -FRAME_WIDTH / 2, - "faded_line_ratio": 0 + "faded_line_ratio": 0, }, "background_plane_kwargs": { "color": GREY, - "axis_config": { - "stroke_color": LIGHT_GREY, - }, "axis_config": { "color": GREY, }, @@ -553,9 +538,7 @@ def setup(self): self.moving_mobjects = [] self.t_matrix = np.array(self.t_matrix) - self.background_plane = NumberPlane( - **self.background_plane_kwargs - ) + self.background_plane = NumberPlane(**self.background_plane_kwargs) if self.show_coordinates: self.background_plane.add_coordinates() @@ -583,7 +566,7 @@ def add_special_mobjects(self, mob_list, *mobs_to_add): mob_list (list) The special list to which you want to add these mobjects. - + *mobs_to_add (Mobject) The mobjects to add. @@ -642,7 +625,7 @@ def add_moving_mobject(self, mobject, target_mobject=None): ---------- mobject (Mobject) The mobjects to add to the list - + target_mobject (Mobject) What the moving_mobject goes to, etc. """ @@ -660,7 +643,7 @@ def get_unit_square(self, color=YELLOW, opacity=0.3, stroke_width=3): stroke_color=color, stroke_width=stroke_width, fill_color=color, - fill_opacity=opacity + fill_opacity=opacity, ) square.move_to(self.plane.coords_to_point(0, 0), DL) return square @@ -678,7 +661,7 @@ def add_unit_square(self, animate=False, **kwargs): **kwargs Any valid keyword arguments of self.get_unit_square() - + Returns ------- Square @@ -687,8 +670,7 @@ def add_unit_square(self, animate=False, **kwargs): square = self.get_unit_square(**kwargs) if animate: self.play( - DrawBorderThenFill(square), - Animation(Group(*self.moving_vectors)) + DrawBorderThenFill(square), Animation(Group(*self.moving_vectors)) ) self.add_transformable_mobject(square) self.bring_to_front(*self.moving_vectors) @@ -705,23 +687,21 @@ def add_vector(self, vector, color=YELLOW, **kwargs): vector Union(Arrow,list,tuple,np.ndarray) It can be a pre-made graphical vector, or the coordinates of one. - + color (str) The string of the hex color of the vector. This is only taken into consideration if 'vector' is not an Arrow. Defaults to YELLOW. - + **kwargs Any valid keyword argument of VectorScene.add_vector. - + Returns ------- Arrow The arrow representing the vector. """ - vector = VectorScene.add_vector( - self, vector, color=color, **kwargs - ) + vector = VectorScene.add_vector(self, vector, color=color, **kwargs) self.moving_vectors.append(vector) return vector @@ -735,7 +715,7 @@ def write_vector_coordinates(self, vector, **kwargs): ---------- vector (Arrow) The arrow representing the vector. - + **kwargs Any valid keyword arguments of VectorScene.write_vector_coordinates @@ -749,10 +729,8 @@ def write_vector_coordinates(self, vector, **kwargs): return coords def add_transformable_label( - self, vector, label, - transformation_name="L", - new_label=None, - **kwargs): + self, vector, label, transformation_name="L", new_label=None, **kwargs + ): """ Method for creating, and animating the addition of a transformable label for the vector. @@ -779,7 +757,7 @@ def add_transformable_label( else: label_mob.target_text = "%s(%s)" % ( transformation_name, - label_mob.get_tex_string() + label_mob.get_tex_string(), ) label_mob.vector = vector label_mob.kwargs = kwargs @@ -798,13 +776,13 @@ def add_title(self, title, scale_factor=1.5, animate=False): ---------- title (str,TexMobject,TextMobject) What the title should be. - + scale_factor (int,float=1.5) How much the title should be scaled by. - + animate (bool=False) Whether or not to animate the addition. - + Returns ------- LinearTransformationScene @@ -835,7 +813,7 @@ def get_matrix_transformation(self, matrix): def get_transposed_matrix_transformation(self, transposed_matrix): """ Returns a function corresponding to the linear - transformation represented by the transposed + transformation represented by the transposed matrix passed. Parameters @@ -854,7 +832,7 @@ def get_transposed_matrix_transformation(self, transposed_matrix): def get_piece_movement(self, pieces): """ - This method returns an animation that moves an arbitrary + This method returns an animation that moves an arbitrary mobject in "pieces" to its corresponding .target value. If self.leave_ghost_vectors is True, ghosts of the original positions/mobjects are left on screen @@ -863,7 +841,7 @@ def get_piece_movement(self, pieces): ---------- pieces (Union[list, tuple, np.array]) The pieces for which the movement must be shown. - + Returns ------- Animation @@ -887,7 +865,7 @@ def get_moving_mobject_movement(self, func): func (function) The function that determines where the .target of the moving mobject goes. - + Returns ------- Animation @@ -912,7 +890,7 @@ def get_vector_movement(self, func): func (function) The function that determines where the .target of the moving mobject goes. - + Returns ------- Animation @@ -936,9 +914,7 @@ def get_transformable_label_movement(self): The animation of the movement. """ for l in self.transformable_labels: - l.target = self.get_vector_label( - l.vector.target, l.target_text, **l.kwargs - ) + l.target = self.get_vector_label(l.vector.target, l.target_text, **l.kwargs) return self.get_piece_movement(self.transformable_labels) def apply_matrix(self, matrix, **kwargs): @@ -961,7 +937,7 @@ def apply_inverse(self, matrix, **kwargs): This method applies the linear transformation represented by the inverse of the passed matrix to the number plane, and each vector/similar mobject on it. - + Parameters ---------- matrix (Union[np.ndarray, list, tuple]) @@ -986,17 +962,16 @@ def apply_transposed_matrix(self, transposed_matrix, **kwargs): """ func = self.get_transposed_matrix_transformation(transposed_matrix) if "path_arc" not in kwargs: - net_rotation = np.mean([ - angle_of_vector(func(RIGHT)), - angle_of_vector(func(UP)) - np.pi / 2 - ]) + net_rotation = np.mean( + [angle_of_vector(func(RIGHT)), angle_of_vector(func(UP)) - np.pi / 2] + ) kwargs["path_arc"] = net_rotation self.apply_function(func, **kwargs) def apply_inverse_transpose(self, t_matrix, **kwargs): """ Applies the inverse of the transformation represented - by the given transposed matrix to the number plane and each + by the given transposed matrix to the number plane and each vector/similar mobject on it. Parameters @@ -1036,25 +1011,27 @@ def apply_function(self, function, added_anims=[], **kwargs): function (Function) The function that affects each point of each mobject in self.transformable_mobjects. - + added_anims (list) Any other animations that need to be played simulataneously with this. - + **kwargs Any valid keyword argument of a self.play() call. """ if "run_time" not in kwargs: kwargs["run_time"] = 3 - anims = [ - ApplyPointwiseFunction(function, t_mob) - for t_mob in self.transformable_mobjects - ] + [ - self.get_vector_movement(function), - self.get_transformable_label_movement(), - self.get_moving_mobject_movement(function), - ] + [ - Animation(f_mob) - for f_mob in self.foreground_mobjects - ] + added_anims + anims = ( + [ + ApplyPointwiseFunction(function, t_mob) + for t_mob in self.transformable_mobjects + ] + + [ + self.get_vector_movement(function), + self.get_transformable_label_movement(), + self.get_moving_mobject_movement(function), + ] + + [Animation(f_mob) for f_mob in self.foreground_mobjects] + + added_anims + ) self.play(*anims, **kwargs)