mirror of
https://github.com/3b1b/manim.git
synced 2025-08-21 05:44:04 +00:00
Merge pull request #176 from 3b1b/winding-number-grant
Winding number grant
This commit is contained in:
commit
873fa49fbe
3 changed files with 291 additions and 13 deletions
|
@ -619,7 +619,8 @@ def walker_animation_with_display(
|
||||||
display.background_rectangle.fill_color = GREY
|
display.background_rectangle.fill_color = GREY
|
||||||
display.background_rectangle.scale(1.2)
|
display.background_rectangle.scale(1.2)
|
||||||
displaycement = 0.5 * DOWN # How about that pun, eh?
|
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,
|
display_anim = ChangingDecimal(display,
|
||||||
number_update_func,
|
number_update_func,
|
||||||
tracked_mobject = walker_anim.compound_walker.walker,
|
tracked_mobject = walker_anim.compound_walker.walker,
|
|
@ -1928,6 +1928,20 @@ class TinyLoopAroundRed(TinyLoop):
|
||||||
"target_coords" : (-1, 1),
|
"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):
|
class FailureOfComposition(ColorMappedObjectsScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : lambda p : (
|
"func" : lambda p : (
|
||||||
|
@ -2180,7 +2194,11 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : plane_func_by_wind_spec(
|
"func" : plane_func_by_wind_spec(
|
||||||
(-2, 0, 2), (2, 0, 1)
|
(-2, 0, 2), (2, 0, 1)
|
||||||
)
|
),
|
||||||
|
"dot_fill_opacity" : 1,
|
||||||
|
"dot_stroke_width" : 1,
|
||||||
|
"include_walkers" : True,
|
||||||
|
"include_question_mark" : True,
|
||||||
}
|
}
|
||||||
def construct(self):
|
def construct(self):
|
||||||
ColorMappedObjectsScene.construct(self)
|
ColorMappedObjectsScene.construct(self)
|
||||||
|
@ -2210,7 +2228,10 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene):
|
||||||
|
|
||||||
#Setup region labels
|
#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 = TextMobject("Winding = ", "$%s$"%tex)
|
||||||
square.label.move_to(square)
|
square.label.move_to(square)
|
||||||
|
|
||||||
|
@ -2264,7 +2285,7 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene):
|
||||||
path_arc = TAU/6
|
path_arc = TAU/6
|
||||||
),
|
),
|
||||||
FadeIn(joint_rect.label[1][1]),
|
FadeIn(joint_rect.label[1][1]),
|
||||||
FadeIn(joint_rect.label[1][3]),
|
FadeIn(joint_rect.label[1][3:]),
|
||||||
FadeOut(right_square.label[0]),
|
FadeOut(right_square.label[0]),
|
||||||
Transform(
|
Transform(
|
||||||
right_square.label[1], joint_rect.label[1][2],
|
right_square.label[1], joint_rect.label[1][2],
|
||||||
|
@ -2313,9 +2334,12 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene):
|
||||||
|
|
||||||
#Setup dot, arrow and label
|
#Setup dot, arrow and label
|
||||||
dot = self.dot = Dot(radius = 0.1)
|
dot = self.dot = Dot(radius = 0.1)
|
||||||
dot.set_stroke(WHITE, 1)
|
dot.set_stroke(WHITE, self.dot_stroke_width)
|
||||||
update_dot_color = ContinualUpdateFromFunc(
|
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)
|
label = DecimalNumber(0, num_decimal_points = 1)
|
||||||
|
@ -2326,7 +2350,7 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene):
|
||||||
|
|
||||||
arrow_length = 0.75
|
arrow_length = 0.75
|
||||||
arrow = Vector(arrow_length*RIGHT)
|
arrow = Vector(arrow_length*RIGHT)
|
||||||
arrow.set_stroke(WHITE, 1)
|
arrow.set_stroke(WHITE, self.dot_stroke_width)
|
||||||
def arrow_update_func(arrow):
|
def arrow_update_func(arrow):
|
||||||
arrow.set_fill(get_output_color(), 1)
|
arrow.set_fill(get_output_color(), 1)
|
||||||
arrow.rotate(-TAU*get_output_rev() - arrow.get_angle())
|
arrow.rotate(-TAU*get_output_rev() - arrow.get_angle())
|
||||||
|
@ -2335,7 +2359,8 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene):
|
||||||
return arrow
|
return arrow
|
||||||
update_arrow = ContinualUpdateFromFunc(arrow, arrow_update_func)
|
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
|
return dot
|
||||||
|
|
||||||
def position_dot(self, point):
|
def position_dot(self, point):
|
||||||
|
@ -2343,6 +2368,17 @@ class TransitionFromPathsToBoundaries(ColorMappedObjectsScene):
|
||||||
self.start_rev = self.get_output_rev()
|
self.start_rev = self.get_output_rev()
|
||||||
self.curr_winding = 0
|
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):
|
class BreakDownLoopWithNonzeroWinding(TransitionFromPathsToBoundaries):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
zero_point = 2*LEFT
|
zero_point = 2*LEFT
|
||||||
|
@ -2621,9 +2657,7 @@ class PolynomialTerms(MonomialTerm):
|
||||||
|
|
||||||
class SearchSpacePerimeterVsArea(EquationSolver2d):
|
class SearchSpacePerimeterVsArea(EquationSolver2d):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"func" : plane_func_by_wind_spec(
|
"func" : example_plane_func,
|
||||||
(-3, -1.3, 2), (0.1, 0.2, 1), (2.8, -2, 1)
|
|
||||||
),
|
|
||||||
"num_iterations" : 15,
|
"num_iterations" : 15,
|
||||||
"display_in_parallel" : False,
|
"display_in_parallel" : False,
|
||||||
"use_fancy_lines" : True,
|
"use_fancy_lines" : True,
|
||||||
|
@ -2686,6 +2720,223 @@ class SearchSpacePerimeterVsArea(EquationSolver2d):
|
||||||
self.play(FadeOut(full_rect))
|
self.play(FadeOut(full_rect))
|
||||||
self.wait()
|
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):
|
class EndingCredits(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
text = TextMobject(
|
text = TextMobject(
|
||||||
|
@ -2999,8 +3250,33 @@ class EndScreen(PatreonEndScreen, PiCreatureScene):
|
||||||
self.play(morty.change, mode)
|
self.play(morty.change, mode)
|
||||||
self.wait(2)
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,7 @@ class ComplexPlane(NumberPlane):
|
||||||
num_mob.scale_to_fit_height(self.written_coordinate_height)
|
num_mob.scale_to_fit_height(self.written_coordinate_height)
|
||||||
num_mob.next_to(point, DOWN+LEFT, SMALL_BUFF)
|
num_mob.next_to(point, DOWN+LEFT, SMALL_BUFF)
|
||||||
result.add(num_mob)
|
result.add(num_mob)
|
||||||
|
self.coordinate_labels = result
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def add_coordinates(self, *numbers):
|
def add_coordinates(self, *numbers):
|
||||||
|
|
Loading…
Add table
Reference in a new issue