Merge branch 'master' into eop

This commit is contained in:
Ben Hambrecht 2018-03-29 10:03:30 +02:00
commit feeb9eca49
3 changed files with 516 additions and 65 deletions

View file

@ -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

View file

@ -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)

View file

@ -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):