From 3bfc51541179dc723bf86648e2099987fca5754b Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 16 Jun 2018 01:24:52 -0700 Subject: [PATCH] DivCurlDotCross --- active_projects/div_curl.py | 309 +++++++++++++++++++++++++++++++++++- 1 file changed, 306 insertions(+), 3 deletions(-) diff --git a/active_projects/div_curl.py b/active_projects/div_curl.py index ba0b08a8..40f0edac 100644 --- a/active_projects/div_curl.py +++ b/active_projects/div_curl.py @@ -1290,7 +1290,7 @@ class DefineDivergence(ChangingElectricField): "delta_x": 0.125, "delta_y": 0.125, }, - "virtual_time": 2, + "virtual_time": 5, "n_anchors_per_line": 10, "min_magnitude": 0, "max_magnitude": 1, @@ -2647,7 +2647,7 @@ class ShowTwoPopulations(Scene): return count -class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene): +class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCameraScene): CONFIG = { "origin": 5 * LEFT + 2.5 * DOWN, "vector_field_config": { @@ -2660,6 +2660,10 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene): "flow_time": 10, } + def setup(self): + MovingCameraScene.setup(self) + PiCreatureScene.setup(self) + def construct(self): self.add_axes() self.add_example_point() @@ -2749,8 +2753,8 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene): self.add(*pop_sizes_updates) self.play( dot.restore, + VFadeIn(coord_pair), UpdateFromAlphaFunc(pop_sizes, lambda m, a: m.set_fill(opacity=a)), - VFadeIn(coord_pair) ) self.wait() self.play(Write(phase_space)) @@ -2849,6 +2853,7 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene): self.play( Animation(dot), vector_field.set_fill, {"opacity": 0.2}, + Animation(self.differential_equation_group), GrowArrow(dot_vector), randy.change, "pondering", ) @@ -2924,3 +2929,301 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene): self.add(stream_line_animation) self.add_foreground_mobjects(vector_field) self.wait(self.flow_time) + self.play( + self.camera_frame.scale, 1.5, {"about_point": self.origin}, + run_time=self.flow_time, + ) + self.wait(self.flow_time) + + +class AskAboutComputation(TeacherStudentsScene): + def construct(self): + self.student_says( + "Sure, but how do you \\\\" + + "\\emph{compute} $\\textbf{div}$ and $\\textbf{curl}$?", + target_mode="sassy", + ) + self.change_student_modes( + "confused", "sassy", "angry", + added_anims=[self.teacher.change, "guilty"] + ) + self.wait() + self.teacher_says( + "Are you familiar \\\\" + + "with my work \\\\" + + "at Khan Academy?", + target_mode="speaking", + bubble_kwargs={"width": 4, "height": 3} + ) + self.change_student_modes( + * 3 * ["pondering"], + look_at_arg=self.screen + ) + self.wait(5) + + +class NablaNotation(PiCreatureScene, MovingCameraScene): + CONFIG = { + "default_pi_creature_kwargs": { + "color": GREY_BROWN, + }, + "default_pi_creature_start_corner": DL, + } + + def setup(self): + MovingCameraScene.setup(self) + PiCreatureScene.setup(self) + + def construct(self): + self.show_notation() + self.show_expansion() + self.zoom_out() + + def show_notation(self): + morty = self.pi_creature + + tex_to_color_map = { + "\\text{div}": BLUE, + "\\nabla \\cdot": BLUE, + "\\text{curl}": YELLOW, + "\\nabla \\times": YELLOW, + } + div_equation = TexMobject( + "\\text{div} \\, \\textbf{F} = \\nabla \\cdot \\textbf{F}", + tex_to_color_map=tex_to_color_map + ) + div_nabla = div_equation.get_part_by_tex("\\nabla") + curl_equation = TexMobject( + "\\text{curl} \\, \\textbf{F} = \\nabla \\times \\textbf{F}", + tex_to_color_map=tex_to_color_map + ) + curl_nabla = curl_equation.get_part_by_tex("\\nabla") + equations = VGroup(div_equation, curl_equation) + equations.arrange_submobjects(DOWN, buff=LARGE_BUFF) + equations.next_to(morty, UP, 2) + equations.to_edge(LEFT) + + self.play( + FadeInFromDown(div_equation), + morty.change, "raise_right_hand" + ) + self.wait() + self.play(WiggleOutThenIn(div_nabla, scale_value=1.5)) + self.wait() + self.play( + FadeInFromDown(curl_equation), + morty.change, "raise_left_hand" + ) + self.wait() + self.play(WiggleOutThenIn(curl_nabla, scale_value=1.5)) + self.wait() + + self.equations = equations + + def show_expansion(self): + equations = self.equations + morty = self.pi_creature + + nabla_vector = Matrix([ + ["\\partial \\over \\partial x"], + ["\\partial \\over \\partial y"], + ], v_buff=1.5) + F_vector = Matrix([ + ["\\textbf{F}_x"], + ["\\textbf{F}_y"], + ], v_buff=1.2) + nabla_vector.match_height(F_vector) + + div_lhs, curl_lhs = lhs_groups = VGroup(*[ + VGroup( + nabla_vector.deepcopy(), + TexMobject(tex).scale(1.5), + F_vector.copy(), + TexMobject("=") + ) + for tex in "\\cdot", "\\times" + ]) + colors = [BLUE, YELLOW] + for lhs, color in zip(lhs_groups, colors): + lhs.arrange_submobjects(RIGHT, buff=MED_SMALL_BUFF) + VGroup(lhs[0].brackets, lhs[1]).set_color(color) + div_lhs.to_edge(UP) + curl_lhs.next_to(div_lhs, DOWN, buff=LARGE_BUFF) + + div_rhs = TexMobject( + "{\\partial F_x \\over \\partial x} + " + + "{\\partial F_y \\over \\partial y}" + ) + curl_rhs = TexMobject( + "{\\partial F_y \\over \\partial x} - " + + "{\\partial F_x \\over \\partial y}" + ) + rhs_groups = VGroup(div_rhs, curl_rhs) + for rhs, lhs in zip(rhs_groups, lhs_groups): + rhs.next_to(lhs, RIGHT) + + for rhs, tex, color in zip(rhs_groups, ["div", "curl"], colors): + rhs.rect = SurroundingRectangle(rhs, color=color) + rhs.label = TexMobject( + "\\text{%s}" % tex, + "\\, \\textbf{F}" + ) + rhs.label.set_color(color) + rhs.label.next_to(rhs.rect, UP) + + for i in 0, 1: + self.play( + ReplacementTransform( + equations[i][2].copy(), + lhs_groups[i][0].brackets + ), + ReplacementTransform( + equations[i][3].copy(), + lhs_groups[i][2], + ), + morty.change, "pondering", + *[ + GrowFromPoint(mob, equations[i].get_right()) + for mob in [ + lhs_groups[i][0].get_entries(), + lhs_groups[i][1], + lhs_groups[i][3] + ] + ], + run_time=2 + ) + self.wait() + self.wait() + for rhs in rhs_groups: + self.play( + Write(rhs), + morty.change, 'confused' + ) + self.play( + ShowCreation(rhs.rect), + FadeInFromDown(rhs.label), + ) + self.wait() + self.play(morty.change, "erm") + self.wait(3) + + def zoom_out(self): + screen_rect = self.camera_frame.copy() + screen_rect.set_stroke(WHITE, 3) + screen_rect.scale(1.01) + words = TextMobject("Something deeper at play...") + words.scale(1.3) + words.next_to(screen_rect, UP) + + self.add(screen_rect) + self.play( + self.camera_frame.scale_to_fit_height, FRAME_HEIGHT + 3, + Write(words, rate_func=squish_rate_func(smooth, 0.3, 1)), + run_time=2, + ) + self.wait() + + +class DivCurlDotCross(Scene): + def construct(self): + rects = VGroup(*[ + ScreenRectangle(height=2.5) + for n in range(4) + ]) + rects.arrange_submobjects_in_grid(n_rows=2, buff=LARGE_BUFF) + rects[2:].shift(MED_LARGE_BUFF * DOWN) + titles = VGroup(*map(TextMobject, [ + "Divergence", "Curl", + "Dot product", "Cross product" + ])) + for title, rect in zip(titles, rects): + title.next_to(rect, UP) + + self.add(rects, titles) + + +class ShowDotProduct(MovingCameraScene): + CONFIG = { + "prod_tex": "\\cdot" + } + + def construct(self): + plane = NumberPlane() + v1 = Vector(RIGHT, color=BLUE) + v2 = Vector(UP, color=YELLOW) + + dot_product = TexMobject( + "\\vec{\\textbf{v}}", self.prod_tex, + "\\vec{\\textbf{w}}", "=" + ) + dot_product.set_color_by_tex_to_color_map({ + "textbf{v}": BLUE, + "textbf{w}": YELLOW, + }) + dot_product.add_background_rectangle() + dot_product.next_to(2.25 * UP, RIGHT) + dot_product_value = DecimalNumber( + 1.0, + include_background_rectangle=True, + ) + dot_product_value.next_to(dot_product) + dot_product_value_update = ContinualChangingDecimal( + dot_product_value, + lambda a: self.get_product(v1, v2), + include_background_rectangle=True, + ) + + self.camera_frame.scale_to_fit_height(4) + self.camera_frame.move_to(DL, DL) + self.add(plane) + self.add(dot_product, dot_product_value_update) + self.add_additional_continual_animations(v1, v2) + self.add_foreground_mobjects(v1, v2) + for n in range(5): + self.play( + Rotate(v1, 45 * DEGREES, about_point=ORIGIN), + Rotate(v2, -45 * DEGREES, about_point=ORIGIN), + run_time=3, + rate_func=there_and_back + ) + self.wait(0.5) + + def get_product(self, v1, v2): + return np.dot(v1.get_vector(), v2.get_vector()) + + def add_additional_continual_animations(self): + pass + + +class ShowCrossProduct(ShowDotProduct): + CONFIG = { + "prod_tex": "\\times" + } + + def get_product(self, v1, v2): + return np.linalg.norm( + np.cross(v1.get_vector(), v2.get_vector()) + ) + + def add_additional_continual_animations(self, v1, v2): + square = Square( + stroke_color=YELLOW, + stroke_width=3, + fill_color=YELLOW, + fill_opacity=0.2, + ) + + self.add(ContinualUpdateFromFunc( + square, + lambda s: s.set_points_as_corners([ + ORIGIN, + v1.get_end(), + v1.get_end() + v2.get_end(), + v2.get_end(), + ]) + )) + + +class NewSceneName(Scene): + def construct(self): + pass