diff --git a/active_projects/WindingNumber.py b/old_projects/WindingNumber.py similarity index 92% rename from active_projects/WindingNumber.py rename to old_projects/WindingNumber.py index 296f0c59..ef04b08d 100644 --- a/active_projects/WindingNumber.py +++ b/old_projects/WindingNumber.py @@ -30,6 +30,8 @@ from mobject.svg_mobject import * from mobject.tex_mobject import * from topics.graph_scene import * +import time + import mpmath mpmath.mp.dps = 7 @@ -437,7 +439,9 @@ def resit_near(x, m): # TODO?: Perhaps use modulus of (uniform) continuity instead of num_checkpoints, calculating # latter as needed from former? -def make_alpha_winder(func, start, end, num_checkpoints): +# +# "cheap" argument only used for diagnostic testing right now +def make_alpha_winder(func, start, end, num_checkpoints, cheap = False): check_points = [None for i in range(num_checkpoints)] check_points[0] = func(start) step_size = fdiv(end - start, num_checkpoints) @@ -447,9 +451,15 @@ def make_alpha_winder(func, start, end, num_checkpoints): func(start + (i + 1) * step_size), check_points[i]) def return_func(alpha): + if cheap: + return alpha # A test to see if this func is responsible for slowdown + index = clamp(0, num_checkpoints - 1, int(alpha * num_checkpoints)) x = interpolate(start, end, alpha) - return resit_near(func(x), check_points[index]) + if cheap: + return check_points[index] # A more principled test that at least returns a reasonable answer + else: + return resit_near(func(x), check_points[index]) return return_func # The various inconsistent choices of what datatype to use where are a bit of a mess, @@ -586,6 +596,7 @@ def walker_animation_with_display( show_arrows = True, scale_arrows = False, num_decimal_points = 1, + include_background_rectangle = True, **kwargs ): @@ -601,13 +612,15 @@ def walker_animation_with_display( if number_update_func != None: display = DecimalNumber(0, num_decimal_points = num_decimal_points, - fill_color = WHITE, - include_background_rectangle = True) - display.background_rectangle.fill_opacity = 0.5 - display.background_rectangle.fill_color = GREY - display.background_rectangle.scale(1.2) + fill_color = WHITE if include_background_rectangle else BLACK, + include_background_rectangle = include_background_rectangle) + if include_background_rectangle: + display.background_rectangle.fill_opacity = 0.5 + display.background_rectangle.fill_color = GREY + display.background_rectangle.scale(1.2) displaycement = 0.5 * DOWN # How about that pun, eh? - display.move_to(walker.get_center() + displaycement) + # display.move_to(walker.get_center() + displaycement) + display.next_to(walker, DOWN+RIGHT, SMALL_BUFF) display_anim = ChangingDecimal(display, number_update_func, tracked_mobject = walker_anim.compound_walker.walker, @@ -625,6 +638,7 @@ def LinearWalker( number_update_func = None, show_arrows = True, scale_arrows = False, + include_background_rectangle = True, **kwargs ): walk_func = lambda alpha : interpolate(start_coords, end_coords, alpha) @@ -635,6 +649,7 @@ def LinearWalker( number_update_func = number_update_func, show_arrows = show_arrows, scale_arrows = scale_arrows, + include_background_rectangle = include_background_rectangle, **kwargs) class ColorMappedByFuncScene(Scene): @@ -747,7 +762,8 @@ class PiWalker(ColorMappedByFuncScene): "show_num_plane" : False, "draw_lines" : True, "num_checkpoints" : 10, - "num_decimal_points" : 1 + "num_decimal_points" : 1, + "include_background_rectangle" : False, } def construct(self): @@ -803,6 +819,7 @@ class PiWalker(ColorMappedByFuncScene): run_time = self.step_run_time, walker_stroke_color = WALKER_LIGHT_COLOR if self.color_foreground_not_background else BLACK, num_decimal_points = self.num_decimal_points, + include_background_rectangle = self.include_background_rectangle, ) if self.display_odometer: @@ -984,6 +1001,18 @@ class EquationSolver2dNode(object): bfs_nodes = self.hacky_bfs() return Succession(*map(lambda n : n.first_anim, bfs_nodes)) + def play_in_bfs(self, scene, border_anim): + bfs_nodes = self.hacky_bfs() + print "Number of nodes: ", len(bfs_nodes) + + if len(bfs_nodes) < 1: + print "Less than 1 node! Aborting!" + return + + scene.play(bfs_nodes[0].first_anim, border_anim) + for node in bfs_nodes[1:]: + scene.play(node.first_anim) + class EquationSolver2d(ColorMappedObjectsScene): CONFIG = { "camera_config" : {"use_z_coordinate_for_display_order": True}, @@ -991,7 +1020,7 @@ class EquationSolver2d(ColorMappedObjectsScene): "initial_upper_x" : 5, "initial_lower_y" : -3, "initial_upper_y" : 3, - "num_iterations" : 1, + "num_iterations" : 0, "num_checkpoints" : 10, # Should really merge this into one enum-style variable @@ -999,6 +1028,9 @@ class EquationSolver2d(ColorMappedObjectsScene): "display_in_bfs" : False, "use_fancy_lines" : True, + "line_color" : WHITE, # Only used for non-fancy lines + + # TODO: Consider adding a "find_all_roots" flag, which could be turned off # to only explore one of the two candidate subrectangles when both are viable @@ -1016,18 +1048,26 @@ class EquationSolver2d(ColorMappedObjectsScene): # Used for UhOhScene; "manual_wind_override" : None, - "show_cursor" : False, + "show_cursor" : True, - "linger_parameter" : 0.2, + "linger_parameter" : 0.5, + + "use_separate_plays" : False, + + "use_cheap_winding_numbers" : False, # To use this, make num_checkpoints large } def construct(self): + if self.num_iterations == 0: + print "You forgot to set num_iterations (maybe you meant to subclass something other than EquationSolver2d directly?)" + return + ColorMappedObjectsScene.construct(self) num_plane = self.num_plane clockwise_val_func = lambda p : -point_to_rev(self.func(p)) - base_line = Line(UP, RIGHT, stroke_width = border_stroke_width, color = WHITE) + base_line = Line(UP, RIGHT, stroke_width = border_stroke_width, color = self.line_color) if self.use_fancy_lines: base_line.color_using_background_image(self.background_image_file) @@ -1067,7 +1107,7 @@ class EquationSolver2d(ColorMappedObjectsScene): return EquationSolver2dNode(empty_animation) def draw_line_return_wind(start, end, start_wind, should_linger = False, draw_line = True): - alpha_winder = make_alpha_winder(clockwise_val_func, start, end, self.num_checkpoints) + alpha_winder = make_alpha_winder(clockwise_val_func, start, end, self.num_checkpoints, cheap = self.use_cheap_winding_numbers) a0 = alpha_winder(0) rebased_winder = lambda alpha: alpha_winder(alpha) - a0 + start_wind colored_line = Line(num_plane.coords_to_point(*start) + IN, num_plane.coords_to_point(*end) + IN) @@ -1122,7 +1162,7 @@ class EquationSolver2d(ColorMappedObjectsScene): width = rect.get_width() height = rect.get_height() - cursor.move_to(num_plane.coords_to_point(center_x, center_y)) + cursor.move_to(num_plane.coords_to_point(center_x, center_y) + 10 * IN) cursor.scale(min(width, height)) # Do a quick FadeIn, wait, and quick FadeOut on the cursor, matching rectangle-drawing time @@ -1134,7 +1174,11 @@ class EquationSolver2d(ColorMappedObjectsScene): anim = AnimationGroup(anim, cursor_anim) - total_wind = head(manual_wind_override) or round(wind_so_far) + override_wind = head(manual_wind_override) + if override_wind != None: + total_wind = override_wind + else: + total_wind = round(wind_so_far) if total_wind == 0: coords = [ @@ -1227,15 +1271,17 @@ class EquationSolver2d(ColorMappedObjectsScene): rate_func = rect_rate ) - self.play(anim, border_anim) + print "About to do the big Play; for reference, the current time is ", time.strftime("%H:%M:%S") + + if self.use_separate_plays: + node.play_in_bfs(self, border_anim) + else: + self.play(anim, border_anim) + + print "All done; for reference, the current time is ", time.strftime("%H:%M:%S") self.wait() - # In case we wish to reference these later, as had been done in the - # WindingNumber_G/SearchSpacePerimeterVsArea scene at one point - self.node = node - self.anim = anim - # TODO: Perhaps have option for bullets (pulses) to fade out and in at ends of line, instead of # jarringly popping out and in? # @@ -1248,7 +1294,7 @@ class LinePulser(ContinualAnimation): self.pulse_time = pulse_time self.bullets = [bullet_template.copy() for i in range(num_bullets)] self.output_func = output_func - ContinualAnimation.__init__(self, VGroup(line, VGroup(*self.bullets)), **kwargs) + ContinualAnimation.__init__(self, VGroup(*self.bullets), **kwargs) def update_mobject(self, dt): alpha = self.external_time % self.pulse_time @@ -1983,7 +2029,9 @@ class LoopSplitScene(ColorMappedObjectsScene): def flash_circles(circles): self.play(LaggedStart(FadeIn, VGroup(circles))) - self.play(*map(FadeOut, circles)) + self.wait() + self.play(FadeOut(VGroup(circles))) + self.wait() self.add(left_square_lines_vmobject, right_square_lines_vmobject) self.remove(*midline_lines) @@ -1997,15 +2045,17 @@ class LoopSplitScene(ColorMappedObjectsScene): self.wait() flash_circles([indicate_circle(l) for l in left_square_lines]) self.play(line_fade(faded, 1, right_square_lines_vmobject), *bullet_list_fade(1, 0, left_square_bullets)) + self.wait() self.add(*right_square_anims) self.play(line_fade(1, faded, left_square_lines_vmobject), *bullet_list_fade(0, 1, right_square_bullets)) self.wait() flash_circles([indicate_circle(l) for l in right_square_lines]) self.play(line_fade(faded, 1, left_square_lines_vmobject), *bullet_list_fade(1, 0, right_square_bullets)) - self.wait() + self.play(*bullet_list_fade(0, 1, left_square_bullets + right_square_bullets)) + self.wait() outside_circlers = [ indicate_circle(left_line), @@ -2017,20 +2067,15 @@ class LoopSplitScene(ColorMappedObjectsScene): inner_circle = indicate_circle(midline_lines[0]) self.play(FadeIn(inner_circle)) + self.wait() self.play(FadeOut(inner_circle), line_fade(1, 0, midline_lines_vmobject), *bullet_list_fade(1, 0, midline_bullets)) - - self.wait(3) - -# Is there a way to abstract this into a general process to derive a new mapped scene from an old scene? -class LoopSplitSceneMapped(LoopSplitScene): - - def setup(self): - left_camera = Camera(**self.camera_config) - right_camera = MappingCamera( - mapping_func = lambda (x, y, z) : complex_to_R3(((complex(x,y) + 3)**1.1) - 3), - **self.camera_config) - split_screen_camera = SplitScreenCamera(left_camera, right_camera, **self.camera_config) - self.camera = split_screen_camera + self.wait() + + # Repeat for effect, goes well with narration + self.play(FadeIn(inner_circle), line_fade(0, 1, midline_lines_vmobject), *bullet_list_fade(0, 1, midline_bullets)) + self.wait() + self.play(FadeOut(inner_circle), line_fade(1, 0, midline_lines_vmobject), *bullet_list_fade(1, 0, midline_bullets)) + self.wait() # TODO: Perhaps do extra illustration of zooming out and winding around a large circle, # to illustrate relation between degree and large-scale winding number @@ -2038,16 +2083,121 @@ class FundThmAlg(EquationSolver2d): CONFIG = { "func" : plane_func_by_wind_spec((1, 2), (-1, 1.5), (-1, 1.5)), "num_iterations" : 2, - "use_fancy_lines" : True, } class SolveX5MinusXMinus1(EquationSolver2d): CONFIG = { "func" : plane_func_from_complex_func(lambda c : c**5 - c - 1), - "num_iterations" : 5, - "use_fancy_lines" : True, + "num_iterations" : 10, + "show_cursor" : True, + "display_in_bfs" : True, } +class PureColorMapOfX5Thing(PureColorMap): + CONFIG = { + "func" : plane_func_from_complex_func(lambda c : c**5 - c - 1), + } + +class X5ThingWithRightHalfGreyed(SolveX5MinusXMinus1): + CONFIG = { + "num_iterations" : 3, + "manual_wind_override" : (1, None, (1, (0, None, None), (0, None, None))) + } + +class SolveX5MinusXMinus1_5Iterations(EquationSolver2d): + CONFIG = { + "func" : plane_func_from_complex_func(lambda c : c**5 - c - 1), + "num_iterations" : 5, + "show_cursor" : True, + "display_in_bfs" : True, + "manual_wind_override" : (None, None, (None, (0, None, None), (0, None, None))) + } + +class X5_Monster_Red_Lines(SolveX5MinusXMinus1_5Iterations): + CONFIG = { + "use_separate_plays" : True, + "use_fancy_lines" : False, + "line_color" : RED, + } + +class X5_Monster_Green_Lines(X5_Monster_Red_Lines): + CONFIG = { + "line_color" : GREEN, + } + +class X5_Monster_Red_Lines_Long(X5_Monster_Red_Lines): + CONFIG = { + "num_iterations" : 6 + } + +class X5_Monster_Green_Lines_Long(X5_Monster_Green_Lines): + CONFIG = { + "num_iterations" : 6 + } + +class X5_Monster_Red_Lines_Little_More(X5_Monster_Red_Lines_Long): + CONFIG = { + "num_iterations" : 7 + } + +class X5_Monster_Green_Lines_Little_More(X5_Monster_Green_Lines_Long): + CONFIG = { + "num_iterations" : 7 + } + +class X5_Monster_Red_Lines_No_Numbers(X5_Monster_Red_Lines): + CONFIG = { + "num_iterations" : 3, + "show_winding_numbers" : False, + } + +class X5_Monster_Green_Lines_No_Numbers(X5_Monster_Green_Lines): + CONFIG = { + "num_iterations" : 3, + "show_winding_numbers" : False, + } + +class SolveX5MinusXMinus1_3Iterations(EquationSolver2d): + CONFIG = { + "func" : plane_func_from_complex_func(lambda c : c**5 - c - 1), + "num_iterations" : 3, + "show_cursor" : True, + "display_in_bfs" : True, + } + +class Diagnostic(SolveX5MinusXMinus1_3Iterations): + CONFIG = { + # I think the combination of these two makes things slow + "use_separate_plays" : not False, # This one isn't important to set any particular way, so let's leave it like this + "use_fancy_lines" : True, + + # This causes a small slowdown (before rendering, in particular), but not the big one, I think + "show_winding_numbers" : True, + + # This doesn't significantly matter for rendering time, I think + "camera_config" : {"use_z_coordinate_for_display_order" : True} + } + +# All above flags False (meaning not db = False): just under 30 it/s +# not db = True: 30 +# use_fancy_lines = True: 30 at first (if scene.play(bfs_nodes[0].first_anim, border_anim is off), but then drops to 3 (or drops right away if that simultaneous play is on) +# use_z_coordinate = True: 30 +# show_winding_numbers = True: 10 +# winding AND use_fancy_lines: 10 +# not db AND fancy_lines AND z_coords = true, winding = false: 3. Not 30, but 3. Slow. +# db AND use_fancy: 3. Slow. +# fancy AND z_coords: 30. Fast. [Hm, this may have been a mistake; fancy and z_coords is now slow?] +# fancy, winding, AND z_coords, but not (not db): 10 +# not db, winding, AND z_coords, but not fancy: 10 + +# class DiagnosticB(Diagnostic): +# CONFIG = { +# "num_iterations" : 3, +# #"num_checkpoints" : 100, +# #"show_winding_numbers" : False, +# #"use_cheap_winding_numbers" : True, +# } + class SolveX5MinusXMinus1Parallel(SolveX5MinusXMinus1): CONFIG = { "display_in_parallel" : True @@ -2066,6 +2216,37 @@ class PreviewClip(EquationSolver2d): "use_fancy_lines" : True, } +class ParallelClip(EquationSolver2d): + CONFIG = { + "func" : plane_func_by_wind_spec( + (-3, -1.3, 2), (0.1, 0.2, 1), (2.8, -2, 1) + ), + "num_iterations" : 5, + "display_in_parallel" : True, + } + +class EquationSolver2dMatchBreakdown(EquationSolver2d): + CONFIG = { + "func" : plane_func_by_wind_spec( + (-2, 0.3, 2), (2, -0.2, 1) # Not an exact match, because our breakdown function has a zero along midlines... + ), + "num_iterations" : 5, + "display_in_parallel" : True, + "show_cursor" : True + } + +class EquationSolver2dMatchBreakdown_parallel(EquationSolver2dMatchBreakdown): + CONFIG = { + "display_in_parallel" : True, + "display_in_bfs" : False, + } + +class EquationSolver2dMatchBreakdown_bfs(EquationSolver2dMatchBreakdown): + CONFIG = { + "display_in_parallel" : False, + "display_in_bfs" : True, + } + class QuickPreview(PreviewClip): CONFIG = { "num_iterations" : 3, @@ -2083,6 +2264,11 @@ class LongEquationSolver(EquationSolver2d): "show_cursor" : True, } +class QuickPreviewUnfancy(LongEquationSolver): + CONFIG = { + # "use_fancy_lines" : False, + } + # TODO: Borsuk-Ulam visuals # Note: May want to do an ordinary square scene, then MappingCamera it into a circle # class BorsukUlamScene(PiWalker): @@ -2509,18 +2695,6 @@ class ShowBack(PiWalkerRect): "func" : plane_func_by_wind_spec((1, 2), (-1, 1.5), (-1, 1.5)) } -class Diagnostic(Scene): - def construct(self): - testList = map(lambda n : (n, rev_to_rgba(n)), [0, 0.1, 0.2, 0.3, 0.4]) - print "Here you go:", testList - self.wait() - -class DiagnosticColorMap(PureColorMap): - CONFIG = { - "func" : lambda (x, y) : (25 * x, 25 * y), - "show_num_plane" : False, - } - class PiWalkerOdometerTest(PiWalkerExamplePlaneFunc): CONFIG = { "display_odometer" : True diff --git a/active_projects/WindingNumber_G.py b/old_projects/WindingNumber_G.py similarity index 91% rename from active_projects/WindingNumber_G.py rename to old_projects/WindingNumber_G.py index 5089cbc4..89969d0f 100644 --- a/active_projects/WindingNumber_G.py +++ b/old_projects/WindingNumber_G.py @@ -1928,6 +1928,20 @@ class TinyLoopAroundRed(TinyLoop): "target_coords" : (-1, 1), } +class ConfusedPiCreature(PiCreatureScene): + def construct(self): + morty = self.pi_creature + morty.set_color(YELLOW_E) + morty.flip() + morty.center() + + self.play(morty.change, "awe", DOWN+3*RIGHT) + self.wait(2) + self.play(morty.change, "confused") + self.wait(2) + self.play(morty.change, "pondering") + self.wait(2) + class FailureOfComposition(ColorMappedObjectsScene): CONFIG = { "func" : lambda p : ( @@ -2180,7 +2194,11 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene): CONFIG = { "func" : plane_func_by_wind_spec( (-2, 0, 2), (2, 0, 1) - ) + ), + "dot_fill_opacity" : 1, + "dot_stroke_width" : 1, + "include_walkers" : True, + "include_question_mark" : True, } def construct(self): ColorMappedObjectsScene.construct(self) @@ -2210,7 +2228,10 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene): #Setup region labels - for square, tex in (left_square, "x"), (right_square, "y"), (joint_rect, "x+y \\, ?"): + sum_tex = "x+y" + if self.include_question_mark: + sum_tex += "\\, ?" + for square, tex in (left_square, "x"), (right_square, "y"), (joint_rect, sum_tex): square.label = TextMobject("Winding = ", "$%s$"%tex) square.label.move_to(square) @@ -2264,7 +2285,7 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene): path_arc = TAU/6 ), FadeIn(joint_rect.label[1][1]), - FadeIn(joint_rect.label[1][3]), + FadeIn(joint_rect.label[1][3:]), FadeOut(right_square.label[0]), Transform( right_square.label[1], joint_rect.label[1][2], @@ -2313,9 +2334,12 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene): #Setup dot, arrow and label dot = self.dot = Dot(radius = 0.1) - dot.set_stroke(WHITE, 1) + dot.set_stroke(WHITE, self.dot_stroke_width) update_dot_color = ContinualUpdateFromFunc( - dot, lambda d : d.set_fill(get_output_color()) + dot, lambda d : d.set_fill( + get_output_color(), + self.dot_fill_opacity + ) ) label = DecimalNumber(0, num_decimal_points = 1) @@ -2326,7 +2350,7 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene): arrow_length = 0.75 arrow = Vector(arrow_length*RIGHT) - arrow.set_stroke(WHITE, 1) + arrow.set_stroke(WHITE, self.dot_stroke_width) def arrow_update_func(arrow): arrow.set_fill(get_output_color(), 1) arrow.rotate(-TAU*get_output_rev() - arrow.get_angle()) @@ -2335,7 +2359,8 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene): return arrow update_arrow = ContinualUpdateFromFunc(arrow, arrow_update_func) - self.add(update_arrow, update_dot_color, label_upadte) + if self.include_walkers: + self.add(update_arrow, update_dot_color, label_upadte) return dot def position_dot(self, point): @@ -2343,6 +2368,17 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene): self.start_rev = self.get_output_rev() self.curr_winding = 0 +class TransitionFromPathsToBoundariesArrowless(TransitionFromPathsToBoundaries): + CONFIG = { + "func" : plane_func_by_wind_spec( + (-2, 0, 2), (2, 0, 1) + ), + "dot_fill_opacity" : 0, + "dot_stroke_width" : 0, + "include_walkers" : False, + "include_question_mark" : False, + } + class BreakDownLoopWithNonzeroWinding(TransitionFromPathsToBoundaries): def construct(self): zero_point = 2*LEFT @@ -2621,9 +2657,7 @@ class PolynomialTerms(MonomialTerm): class SearchSpacePerimeterVsArea(EquationSolver2d): CONFIG = { - "func" : plane_func_by_wind_spec( - (-3, -1.3, 2), (0.1, 0.2, 1), (2.8, -2, 1) - ), + "func" : example_plane_func, "num_iterations" : 15, "display_in_parallel" : False, "use_fancy_lines" : True, @@ -2686,6 +2720,223 @@ class SearchSpacePerimeterVsArea(EquationSolver2d): self.play(FadeOut(full_rect)) self.wait() +class ShowPolynomialFinalState(SolveX5MinusXMinus1): + CONFIG = { + "num_iterations" : 15, + } + def construct(self): + self.force_skipping() + SolveX5MinusXMinus1.construct(self) + self.revert_to_original_skipping_status() + +class PiCreatureInAwe(Scene): + def construct(self): + randy = Randolph() + + + self.play(randy.change, "awe") + self.play(Blink(randy)) + self.play(randy.look, UP, run_time = 2) + self.play( + randy.look, RIGHT, + run_time = 4, + rate_func = there_and_back, + path_arc = -TAU/4 + ) + self.wait() + +class ShowComplexFunction(Scene): + def construct(self): + plane = ComplexPlane() + plane.add_coordinates() + four_i = plane.coordinate_labels[-1] + plane.coordinate_labels.remove(four_i) + plane.remove(four_i) + + title = TextMobject("Complex Plane") + title.to_edge(UP, buff = MED_SMALL_BUFF) + rect = BackgroundRectangle(title, fill_opacity = 1, buff = MED_SMALL_BUFF) + + x = complex(1, 0.4) + f = lambda x : x**5 - x - 1 + + x_point = plane.number_to_point(x) + fx_point = plane.number_to_point(f(x)) + + x_dot = Dot(x_point) + fx_dot = Dot(fx_point, color = YELLOW) + arrow = Arrow( + x_point, fx_point, + use_rectangular_stem = False, + path_arc = TAU/3, + color = YELLOW + ) + arrow.pointwise_become_partial(arrow, 0, 0.95) + + x_label = TexMobject("x = %d+%.1fi"%(x.real, x.imag)) + x_label.next_to(x_dot, RIGHT) + x_label.add_background_rectangle() + + fx_label = TexMobject("f(x) = x^5 - x - 1") + fx_label.next_to(fx_dot, DOWN, SMALL_BUFF) + fx_label.highlight(YELLOW) + fx_label.add_background_rectangle() + fx_label.generate_target() + fx_label.target.move_to(title) + fx_label.target[1].highlight(WHITE) + + self.play( + Write(plane), + FadeIn(rect), + LaggedStart(FadeIn, title) + ) + self.play(*map(FadeIn, [x_dot, x_label])) + self.wait() + self.play( + ReplacementTransform(x_dot.copy(), fx_dot, path_arc = arrow.path_arc), + ShowCreation(arrow, rate_func = squish_rate_func(smooth, 0.2, 1)) + ) + self.play(FadeIn(fx_label)) + self.wait(2) + self.play( + MoveToTarget(fx_label), + *map(FadeOut, [title, x_dot, x_label, arrow, fx_dot]) + ) + self.play(FadeOut(plane.coordinate_labels)) + self.wait() + +class WindingNumbersInInputOutputContext(PathContainingZero): + CONFIG = { + "in_loop_center_coords" : (-2, -1), + "run_time" : 10, + } + def construct(self): + self.remove(self.pi_creature) + self.setup_planes() + + in_loop = Circle() + in_loop.flip(RIGHT) + # in_loop = Square(side_length = 2) + in_loop.insert_n_anchor_points(100) + in_loop.move_to(self.input_plane.coords_to_point( + *self.in_loop_center_coords + )) + in_loop.match_background_image_file(self.input_coloring) + + out_loop = in_loop.copy() + out_loop.match_background_image_file(self.output_coloring) + update_out_loop = ContinualUpdateFromFunc( + out_loop, + lambda m : m.set_points(in_loop.points).apply_function(self.point_function) + ) + # self.add(update_out_loop) + + in_dot = Dot(radius = 0.04) + update_in_dot = ContinualUpdateFromFunc( + in_dot, lambda d : d.move_to(in_loop.point_from_proportion(1)) + ) + self.add(update_in_dot) + + out_arrow = Arrow(LEFT, RIGHT) + update_out_arrow = ContinualUpdateFromFunc( + out_arrow, + lambda a : a.put_start_and_end_on( + self.output_plane.coords_to_point(0, 0), + out_loop.point_from_proportion(1) + ) + ) + update_out_arrow_color = ContinualUpdateFromFunc( + out_arrow, + lambda a : a.highlight(rev_to_color(a.get_angle()/TAU)) + ) + self.add(update_out_arrow, update_out_arrow_color) + + words = TextMobject( + "How many times does \\\\ the output wind around?" + ) + label = self.output_plane.label + words.move_to(label, UP) + self.output_plane.remove(label) + self.add(words) + + decimal = DecimalNumber(0) + decimal.next_to(self.output_plane.get_corner(UP+RIGHT), DOWN+LEFT) + + + self.play( + ShowCreation(in_loop), + ShowCreation(out_loop), + ChangeDecimalToValue(decimal, 2), + Animation(in_dot), + run_time = self.run_time, + rate_func = bezier([0, 0, 1, 1]) + ) + +class SolveX5SkipToEnd(SolveX5MinusXMinus1): + CONFIG = { + "num_iterations" : 4, + } + def construct(self): + self.force_skipping() + SolveX5MinusXMinus1.construct(self) + self.revert_to_original_skipping_status() + + mobjects = VGroup(*self.get_mobjects()) + lines = VGroup() + rects = VGroup() + for mob in mobjects: + if mob.background_image_file is not None: + mob.set_stroke(width = 2) + lines.add(mob) + elif isinstance(mob, Polygon): + rects.add(mob) + else: + self.remove(mob) + + self.clear() + self.add(lines, rects) + +class ZeroFoundOnBoundary(Scene): + def construct(self): + arrow = Vector(DOWN+LEFT, color = WHITE) + words = TextMobject("Found zero on boundary!") + words.next_to(arrow.get_start(), UP) + words.shift(1.5*RIGHT) + + point = VectorizedPoint() + point.next_to(arrow, DOWN+LEFT) + + self.play(Flash(point)) + self.play( + GrowArrow(arrow), + Write(words), + ) + self.wait() + +class AllOfTheVideos(Scene): + CONFIG = { + "camera_config" : { + "background_alpha" : 255, + } + } + def construct(self): + thumbnail_dir = os.path.join(MEDIA_DIR, "3b1b_videos/Winding/OldThumbnails") + n = 4 + images = Group(*[ + ImageMobject(os.path.join(thumbnail_dir, file)) + for file in os.listdir(thumbnail_dir)[:n**2] + ]) + for image in images: + rect = SurroundingRectangle(image, buff = 0) + rect.set_stroke(WHITE, 1) + image.add(rect) + images.arrange_submobjects_in_grid(n, n, buff = 0) + images.scale_to_fit_height(2*SPACE_HEIGHT) + random.shuffle(images.submobjects) + + self.play(LaggedStart(FadeIn, images, run_time = 4)) + self.wait() + class EndingCredits(Scene): def construct(self): text = TextMobject( @@ -2999,8 +3250,33 @@ class EndScreen(PatreonEndScreen, PiCreatureScene): self.play(morty.change, mode) self.wait(2) - - +class Thumbnail(SearchSpacePerimeterVsArea): + CONFIG = { + "num_iterations" : 18, + "func" : plane_func_by_wind_spec( + (-3, -1.3, 2), (0.1, 0.2, 1), (2.8, -2, 1) + ), + } + def construct(self): + self.force_skipping() + EquationSolver2d.construct(self) + self.revert_to_original_skipping_status() + + mobjects = VGroup(*self.get_mobjects()) + lines = VGroup() + rects = VGroup() + get_length = lambda mob : max(mob.get_width(), mob.get_height()) + for mob in mobjects: + if mob.background_image_file is not None: + mob.set_stroke(width = 4*np.sqrt(get_length(mob))) + lines.add(mob) + elif isinstance(mob, Polygon): + rects.add(mob) + else: + self.remove(mob) + + self.clear() + self.add(lines) diff --git a/topics/complex_numbers.py b/topics/complex_numbers.py index 0b9a593a..a7bace5c 100644 --- a/topics/complex_numbers.py +++ b/topics/complex_numbers.py @@ -216,6 +216,7 @@ class ComplexPlane(NumberPlane): num_mob.scale_to_fit_height(self.written_coordinate_height) num_mob.next_to(point, DOWN+LEFT, SMALL_BUFF) result.add(num_mob) + self.coordinate_labels = result return result def add_coordinates(self, *numbers):