diff --git a/active_projects/WindingNumber_G.py b/active_projects/WindingNumber_G.py index 2b5a3934..fbf313b3 100644 --- a/active_projects/WindingNumber_G.py +++ b/active_projects/WindingNumber_G.py @@ -48,24 +48,129 @@ class IntroSceneWrapper(PiCreatureScene): "height" : 2, }, "default_pi_creature_start_corner" : DOWN+LEFT, - # "default_pi_creature_height" : 1, } def construct(self): + self.introduce_two_words() + self.describe_main_topic() + self.describe_meta_topic() + + def introduce_two_words(self): morty = self.pi_creature rect = ScreenRectangle(height = 5) rect.to_corner(UP+RIGHT) self.add(rect) - main_topic, meta_topic = toipcs = VGroup( + h_line = Line(LEFT, RIGHT).scale(2) + h_line.to_corner(UP+LEFT) + h_line.shift(0.5*DOWN) + + main_topic, meta_topic = topics = VGroup( TextMobject("Main topic"), TextMobject("Meta topic"), ) - topics.arrange_submobjects(DOWN, aligned_edge = LEFT) + topics.next_to(morty, UP) + topics.shift_onto_screen() + + self.play( + morty.change, "raise_left_hand", + FadeInFromDown(main_topic) + ) + self.wait() + self.play( + morty.change, "raise_right_hand", + main_topic.next_to, meta_topic.get_top(), UP, MED_SMALL_BUFF, + FadeInFromDown(meta_topic) + ) + self.wait() + self.play( + morty.change, "happy", + main_topic.next_to, h_line, UP, + meta_topic.set_fill, {"opacity" : 0.2}, + ) + self.play(ShowCreation(h_line)) + self.wait() + + self.set_variables_as_attrs(h_line, main_topic, meta_topic) + + def describe_main_topic(self): + h_line = self.h_line + morty = self.pi_creature + main_topic = self.main_topic + meta_topic = self.meta_topic + + solver = TextMobject("2d equation solver") + solver.match_width(h_line) + solver.next_to(h_line, DOWN) + rainbow_solver1 = solver.copy() + rainbow_solver2 = solver.copy() + colors = ["RED", "ORANGE", "YELLOW", "GREEN", BLUE, "PURPLE", PINK] + rainbow_solver1.gradient_highlight(*colors) + rainbow_solver2.gradient_highlight(*reversed(colors)) + xy_equation = TexMobject( + "y", "e", "^x", "=\\sin(|", "x", "y", "|)" + ) + xy_equation.highlight_by_tex_to_color_map({ + "x" : BLUE, + "y" : YELLOW + }) + xy_equation.next_to(solver, DOWN, MED_LARGE_BUFF) + z_equation = TexMobject("z", "^5", "+", "z", "+", "1", "=", "0") + z_equation.highlight_by_tex("z", GREEN) + z_equation.move_to(xy_equation, UP) + zeta = TexMobject("\\zeta(s) = 0") + zeta[2].highlight(GREEN) + zeta.next_to(z_equation, DOWN, MED_LARGE_BUFF) + self.play(Write(solver)) + self.play( + LaggedStart(FadeIn, xy_equation, run_time = 1), + morty.change, "pondering" + ) + self.wait(2) + self.play( + FadeOut(xy_equation), + FadeIn(z_equation) + ) + self.wait() + self.play(Write(zeta)) + self.wait() + solver.save_state() + for rainbow_solver in rainbow_solver1, rainbow_solver2: + self.play(Transform( + solver, rainbow_solver, + run_time = 2, + submobject_mode = "lagged_start" + )) + self.play(solver.restore) + self.wait() + + self.play(LaggedStart( + FadeOut, VGroup(solver, z_equation, zeta) + )) + self.play( + main_topic.move_to, meta_topic, + main_topic.set_fill, {"opacity" : 0.2}, + meta_topic.move_to, main_topic, + meta_topic.set_fill, {"opacity" : 1}, + morty.change, "hesitant", + path_arc = TAU/8, + ) + + def describe_meta_topic(self): + h_line = self.h_line + morty = self.pi_creature + + words = TextMobject("Seek constructs which \\\\ compose nicely") + words.scale(0.7) + words.next_to(h_line, DOWN) + + self.play(Write(words)) + self.play(morty.change, "happy") + self.wait(3) class PiCreaturesAreIntrigued(AltTeacherStudentsScene): def construct(self): @@ -155,6 +260,93 @@ class RewriteEquationWithTeacher(AltTeacherStudentsScene): self.play(dot.move_to, plane.coords_to_point(*coords)) self.wait() +class TwoDScreenInOurThreeDWorld(AltTeacherStudentsScene, ThreeDScene): + def construct(self): + self.ask_about_2d_functions() + self.show_3d() + + def ask_about_2d_functions(self): + in_plane = NumberPlane(x_radius = 2.5, y_radius = 2.5) + in_plane.add_coordinates() + in_plane.scale_to_fit_height(3) + out_plane = in_plane.copy() + + in_text = TextMobject("Input space") + out_text = TextMobject("Output space") + VGroup(in_text, out_text).scale(0.75) + in_text.next_to(in_plane, UP, SMALL_BUFF) + out_text.next_to(out_plane, UP, SMALL_BUFF) + in_plane.add(in_text) + out_plane.add(out_text) + + arrow = CurvedArrow(RIGHT, LEFT, angle = TAU/4) + arrow.pointwise_become_partial(arrow, 0.05, 1.0) + group = VGroup(in_plane, arrow, out_plane) + group.arrange_submobjects(RIGHT) + arrow.shift(UP) + group.move_to(self.students) + group.to_edge(UP) + + dots = VGroup() + dots_target = VGroup() + for x in np.arange(-2.5, 3.0, 0.5): + for y in np.arange(-2.5, 3.0, 0.5): + dot = Dot(radius = 0.05) + dot.move_to(in_plane.coords_to_point(x, y)) + dot.generate_target() + dot.target.move_to(out_plane.coords_to_point( + x + 0.25*np.cos(5*y), y + 0.25*np.sin(3*x) + )) + dots.add(dot) + dots_target.add(dot.target) + dots.gradient_highlight(YELLOW, RED) + dots_target.gradient_highlight(YELLOW, RED) + + self.play( + self.teacher.change, "raise_right_hand", + Write(in_plane, run_time = 1) + ) + self.play( + ShowCreation(arrow), + ReplacementTransform( + in_plane.copy(), out_plane, + path_arc = -TAU/4, + ) + ) + self.play( + LaggedStart(GrowFromCenter, dots, run_time = 1), + self.get_student_changes(*3*["erm"]), + ) + self.play(LaggedStart(MoveToTarget, dots, path_arc = -TAU/4)) + self.wait(3) + + + def show_3d(self): + laptop = Laptop().scale(2) + laptop.rotate(-TAU/12, DOWN) + laptop.rotate(-5*TAU/24, LEFT) + laptop.rotate(TAU/8, LEFT) + laptop.scale(2.3*SPACE_WIDTH/laptop.screen_plate.get_width()) + laptop.shift(-laptop.screen_plate.get_center() + 0.1*IN) + should_shade_in_3d(laptop) + + everything = VGroup(laptop, *self.mobjects) + everything.generate_target() + # for mob in everything.target.submobject_family(): + # if isinstance(mob, PiCreature): + # mob.change_mode("confused") + everything.target.rotate(TAU/12, LEFT) + everything.target.rotate(TAU/16, UP) + everything.target.shift(4*UP) + + self.move_camera( + distance = 12, + run_time = 4, + added_anims = [MoveToTarget(everything, run_time = 4)], + ) + self.add(AmbientRotation(everything, axis = UP, rate = 3*DEGREES)) + self.wait(10) + class DotsHoppingToColor(Scene): CONFIG = { "dot_radius" : 0.05, @@ -247,13 +439,8 @@ class DotsHoppingToColor(Scene): inspector.scale(0.15) inspector_image = inspector.copy() - def point_function(point): - in_coords = input_plane.point_to_coords(point) - out_coords = self.func(in_coords) - return output_plane.coords_to_point(*out_coords) - def update_inspector_image(inspector_image): - inspector_image.move_to(point_function(inspector.get_center())) + inspector_image.move_to(self.point_function(inspector.get_center())) inspector_image_update_anim = UpdateFromFunc( inspector_image, update_inspector_image @@ -361,19 +548,23 @@ class DotsHoppingToColor(Scene): self.wait() - ### def func(self, coord_pair): out_coords = np.array(self.non_renormalized_func(coord_pair)) out_norm = np.linalg.norm(out_coords) - if out_norm > 0.5: + if out_norm > 0.01: angle = angle_of_vector(out_coords) factor = 0.5-0.1*np.cos(4*angle) target_norm = factor*np.log(out_norm) out_coords *= target_norm / out_norm return tuple(out_coords) + def point_function(self, point): + in_coords = self.input_plane.point_to_coords(point) + out_coords = self.func(in_coords) + return self.output_plane.coords_to_point(*out_coords) + def get_colorings(self): in_cmos = ColorMappedObjectsScene( func = lambda p : self.non_renormalized_func( @@ -404,11 +595,11 @@ class DotsHoppingToColor(Scene): return colorings def get_planes(self): - input_plane = NumberPlane( + input_plane = self.input_plane = NumberPlane( x_radius = self.plane_width/2.0, y_radius = self.plane_height/2.0, ) - output_plane = input_plane.copy() + output_plane = self.output_plane = input_plane.copy() planes = [input_plane, output_plane] vects = [LEFT, RIGHT] label_texts = ["Input", "Output"] @@ -430,7 +621,6 @@ class DotsHoppingToColor(Scene): if isinstance(submob, TexMobject) and hasattr(submob, "background_rectangle"): submob.remove(submob.background_rectangle) - return planes def get_dots(self, input_plane, output_plane): @@ -491,7 +681,212 @@ class PiCreatureAsksWhatWentWrong(PiCreatureScene): ) self.wait(5) - +class ForeverNarrowingLoop(DotsHoppingToColor): + CONFIG = { + "non_renormalized_func" : plane_func_by_wind_spec( + (-2, -1, 2), + (1, 1, 1), + (2, -2, -1), + ), + } + def construct(self): + input_coloring, output_coloring = colorings = VGroup(*self.get_colorings()) + input_plane, output_plane = planes = VGroup(*self.get_planes()) + for plane in planes: + plane.white_parts.highlight(BLACK) + plane.lines_to_fade.set_stroke(width = 0) + + v_line = Line(UP, DOWN).scale(SPACE_HEIGHT) + v_line.set_stroke(WHITE, 5) + + self.add(colorings, v_line, planes) + self.play(*it.chain( + [ + ApplyMethod(coloring.set_fill, {"opacity" : 0.2}) + for coloring in colorings + ], + [ + ApplyMethod(plane.white_parts.highlight, WHITE) + for plane in planes + ] + ), run_time = 2) + + # circle + circle = Circle(color = WHITE, radius = 2.25) + circle.flip(axis = RIGHT) + circle.insert_n_anchor_points(50) + circle.next_to(input_coloring.get_corner(UP+RIGHT), DOWN+LEFT, SMALL_BUFF) + circle.set_stroke(width = 5) + circle_image = circle.copy() + circle.match_background_image_file(input_coloring) + circle_image.match_background_image_file(output_coloring) + + def update_circle_image(circle_image): + circle_image.points = circle.points + circle_image.apply_function(self.point_function) + circle_image.make_smooth() + + circle_image_update_anim = UpdateFromFunc( + circle_image, update_circle_image + ) + + self.play( + ShowCreation(circle), + ShowCreation(circle_image), + run_time = 3, + rate_func = bezier([0, 0, 1, 1]) + ) + # self.play( + # ReplacementTransform( + # circle.copy(), + # circle_image.copy().match_background_image_file( + # input_coloring + # ).set_stroke(width = 0) + # ), + # ReplacementTransform( + # circle.copy().match_background_image_file( + # output_coloring + # ).set_stroke(width = 0), + # circle_image + # ), + # run_time = 2 + # ) + self.play( + circle.scale, 0.015, + circle.move_to, input_plane.coords_to_point(1, 1), + circle_image_update_anim, + run_time = 20, + rate_func = bezier([0, 0, 1, 1]) + ) + +class FailureOfComposition(ColorMappedObjectsScene): + CONFIG = { + "func" : lambda p : ( + np.cos(TAU*p[1]/3.5), + np.sin(TAU*p[1]/3.5) + ) + } + def construct(self): + ColorMappedObjectsScene.construct(self) + + big_square = Square(side_length = 4) + big_square.move_to(ORIGIN, RIGHT) + small_squares = VGroup(*[ + Square(side_length = 2) for x in range(2) + ]) + small_squares.match_width(big_square, stretch = True) + small_squares.arrange_submobjects(DOWN, buff = 0) + small_squares.move_to(big_square) + small_squares.space_out_submobjects(1.1) + all_squares = VGroup(big_square, *small_squares) + all_squares.set_stroke(width = 6) + + for square in all_squares: + square.highlight(WHITE) + square.color_using_background_image(self.background_image_file) + + question = TextMobject("Does my border go through every color?") + question.to_edge(UP) + no_answers = VGroup() + yes_answers = VGroup() + for square in all_squares: + if square is big_square: + square.answer = TextMobject("Yes") + square.answer.highlight(GREEN) + yes_answers.add(square.answer) + else: + square.answer = TextMobject("No") + square.answer.highlight(RED) + no_answers.add(square.answer) + square.answer.move_to(square) + + no_answers_in_equation = no_answers.copy() + yes_answers_in_equation = yes_answers.copy() + plus, equals = plus_equals = TexMobject("+=") + equation = VGroup( + no_answers_in_equation[0], plus, + no_answers_in_equation[1], equals, + yes_answers_in_equation + ) + equation.arrange_submobjects(RIGHT, buff = SMALL_BUFF) + equation.next_to(big_square, RIGHT, MED_LARGE_BUFF) + q_marks = TexMobject("???") + q_marks.next_to(equals, UP) + + + self.add(question) + self.play(LaggedStart(ShowCreation, small_squares, lag_ratio = 0.8)) + self.play(LaggedStart(Write, no_answers)) + self.wait() + self.play( + small_squares.arrange_submobjects, DOWN, {"buff" : 0}, + small_squares.move_to, big_square, + no_answers.space_out_submobjects, 0.9, + ) + self.add(big_square) + no_answers_copy = no_answers.copy() + small_squares.save_state() + self.play( + Transform(no_answers, no_answers_in_equation), + Write(plus_equals), + small_squares.set_stroke, {"width" : 0}, + ) + self.play( + Write(yes_answers), + Write(yes_answers_in_equation), + ) + self.play(LaggedStart(FadeIn, q_marks, run_time = 1)) + self.wait(2) + self.play( + small_squares.restore, + FadeOut(yes_answers), + FadeIn(no_answers_copy), + ) + self.wait() + self.play( + small_squares.set_stroke, {"width" : 0}, + FadeOut(no_answers_copy), + FadeIn(yes_answers), + ) + self.wait() + + # We can find a better notion of what we want + + cross = Cross(question) + + self.play( + ShowCreation(cross, run_time = 2), + FadeOut(equation), + FadeOut(no_answers), + FadeOut(q_marks), + FadeOut(yes_answers), + ) + + x, plus, y = x_plus_y = TexMobject("x+y") + x_plus_y.move_to(big_square) + x_plus_y.save_state() + x.move_to(no_answers_copy[0]) + y.move_to(no_answers_copy[1]) + plus.fade(1) + + for square, char in zip(small_squares, [x, y]): + ghost = square.copy() + ghost.set_stroke(width = 5) + ghost.background_image_file = None + self.play( + small_squares.restore, + ShowPassingFlash(ghost), + Write(char) + ) + self.wait() + ghost = big_square.copy() + ghost.background_image_file = None + self.play( + small_squares.set_stroke, {"width" : 0}, + x_plus_y.restore, + ) + self.play(ShowPassingFlash(ghost)) + self.wait()