mirror of
https://github.com/3b1b/manim.git
synced 2025-08-02 11:03:03 +08:00
Merge branch 'master' of github.com:3b1b/manim into winding-number-grant
This commit is contained in:
@ -49,6 +49,9 @@ cw_circle = Circle(color = WHITE).stretch(-1, 0)
|
||||
# Used when walker animations are on black backgrounds, in EquationSolver2d and PiWalker
|
||||
WALKER_LIGHT_COLOR = DARK_GREY
|
||||
|
||||
ODOMETER_RADIUS = 1.5
|
||||
ODOMETER_STROKE_WIDTH = 20
|
||||
|
||||
# TODO/WARNING: There's a lot of refactoring and cleanup to be done in this code,
|
||||
# (and it will be done, but first I'll figure out what I'm doing with all this...)
|
||||
# -SR
|
||||
@ -115,7 +118,6 @@ positive_color = rev_to_color(0)
|
||||
negative_color = rev_to_color(0.5)
|
||||
neutral_color = rev_to_color(0.25)
|
||||
|
||||
# This, the first animation I made, has the messiest code, full of cruft, but so it goes
|
||||
class EquationSolver1d(GraphScene, ZoomedScene):
|
||||
CONFIG = {
|
||||
"camera_config" :
|
||||
@ -132,7 +134,7 @@ class EquationSolver1d(GraphScene, ZoomedScene):
|
||||
"graph_label" : None,
|
||||
"show_target_line" : True,
|
||||
"base_line_y" : 0, # The y coordinate at which to draw our x guesses
|
||||
"show_y_as_deviation" : False, # Displays y-values as deviations from target
|
||||
"show_y_as_deviation" : False, # Displays y-values as deviations from target,
|
||||
}
|
||||
|
||||
def drawGraph(self):
|
||||
@ -454,6 +456,7 @@ def split_interval((a, b)):
|
||||
mid = (a + b)/2.0
|
||||
return ((a, mid), (mid, b))
|
||||
|
||||
# I am surely reinventing some wheel here, but what's done is done...
|
||||
class RectangleData():
|
||||
def __init__(self, x_interval, y_interval):
|
||||
self.rect = (x_interval, y_interval)
|
||||
@ -515,18 +518,6 @@ class RectangleData():
|
||||
def complex_to_pair(c):
|
||||
return np.array((c.real, c.imag))
|
||||
|
||||
# def complex_poly_with_roots(*roots):
|
||||
# def poly(c):
|
||||
# return np.prod([c - z for z in roots])
|
||||
# return poly
|
||||
|
||||
# def complex_func_by_critical_points(roots, poles = []):
|
||||
# numerator = complex_poly_with_roots(*map(pair_to_complex, roots))
|
||||
# denominator = complex_poly_with_roots(*map(pair_to_complex, poles))
|
||||
# def rational_func(c):
|
||||
# return numerator(c)/denominator(c)
|
||||
# return rational_func
|
||||
|
||||
def plane_func_from_complex_func(f):
|
||||
return lambda (x, y) : complex_to_pair(f(complex(x,y)))
|
||||
|
||||
@ -581,6 +572,9 @@ def plane_func_by_wind_spec(*specs):
|
||||
|
||||
return plane_func_from_complex_func(complex_func)
|
||||
|
||||
def scale_func(func, scale_factor):
|
||||
return lambda x : func(x) * scale_factor
|
||||
|
||||
# Used in Initial2dFunc scenes, VectorField scene, and ExamplePlaneFunc
|
||||
example_plane_func_spec = [(-3, -1.3, 2), (0.1, 0.2, 1), (2.8, -2, -1)]
|
||||
example_plane_func = plane_func_by_wind_spec(*example_plane_func_spec)
|
||||
@ -804,9 +798,13 @@ class PiWalker(ColorMappedByFuncScene):
|
||||
"walk_coords" : [],
|
||||
"step_run_time" : 1,
|
||||
"scale_arrows" : False,
|
||||
"display_wind" : True,
|
||||
"display_size" : False,
|
||||
"display_odometer" : False,
|
||||
"color_foreground_not_background" : False,
|
||||
"show_num_plane" : False,
|
||||
"draw_lines" : True,
|
||||
"num_checkpoints" : 10,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
@ -827,8 +825,9 @@ class PiWalker(ColorMappedByFuncScene):
|
||||
polygon.color_using_background_image(self.background_image_file)
|
||||
total_run_time = len(points) * self.step_run_time
|
||||
polygon_anim = ShowCreation(polygon, run_time = total_run_time, rate_func = None)
|
||||
walker_anim = EmptyAnimation
|
||||
walker_anim = empty_animation
|
||||
|
||||
start_wind = 0
|
||||
for i in range(len(walk_coords)):
|
||||
start_coords = walk_coords[i]
|
||||
end_coords = walk_coords[(i + 1) % len(walk_coords)]
|
||||
@ -837,7 +836,12 @@ class PiWalker(ColorMappedByFuncScene):
|
||||
# so the next iteration changing start_coords, end_coords doesn't change this closure
|
||||
val_alpha_func = lambda a, start_coords = start_coords, end_coords = end_coords : self.func(interpolate(start_coords, end_coords, a))
|
||||
|
||||
if self.display_size:
|
||||
if self.display_wind:
|
||||
clockwise_val_func = lambda p : -point_to_rev(self.func(p))
|
||||
alpha_winder = make_alpha_winder(clockwise_val_func, start_coords, end_coords, self.num_checkpoints)
|
||||
number_update_func = lambda alpha, alpha_winder = alpha_winder, start_wind = start_wind: alpha_winder(alpha) - alpha_winder(0) + start_wind
|
||||
start_wind = number_update_func(1)
|
||||
elif self.display_size:
|
||||
# We need to do this roundabout default argument thing to get the closure we want,
|
||||
# so the next iteration changing val_alpha_func doesn't change this closure
|
||||
number_update_func = lambda a, val_alpha_func = val_alpha_func : point_to_rescaled_size(val_alpha_func(a)) # We only use this for diagnostics
|
||||
@ -877,9 +881,18 @@ class PiWalker(ColorMappedByFuncScene):
|
||||
# use point_from_proportion to get points along the way
|
||||
|
||||
if self.display_odometer:
|
||||
color_wheel = Circle(radius = ODOMETER_RADIUS)
|
||||
color_wheel.stroke_width = ODOMETER_STROKE_WIDTH
|
||||
color_wheel.color_using_background_image(self.short_path_to_long_path("pure_color_map.png")) # Manually inserted here; this is unclean
|
||||
self.add(color_wheel)
|
||||
self.play(walker_anim)
|
||||
else:
|
||||
self.play(polygon_anim, walker_anim)
|
||||
if self.draw_lines:
|
||||
self.play(polygon_anim, walker_anim)
|
||||
else:
|
||||
# (Note: Turns out, play is unhappy playing empty_animation, as had been
|
||||
# previous approach to this toggle; should fix that)
|
||||
self.play(walker_anim)
|
||||
|
||||
self.wait()
|
||||
|
||||
@ -889,7 +902,8 @@ class PiWalkerRect(PiWalker):
|
||||
"start_y" : 1,
|
||||
"walk_width" : 2,
|
||||
"walk_height" : 2,
|
||||
"func" : plane_func_from_complex_func(lambda c: c**2)
|
||||
"func" : plane_func_from_complex_func(lambda c: c**2),
|
||||
"double_up" : False,
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
@ -898,6 +912,8 @@ class PiWalkerRect(PiWalker):
|
||||
BR = TR + (0, -self.walk_height)
|
||||
BL = BR + (-self.walk_width, 0)
|
||||
self.walk_coords = [TL, TR, BR, BL]
|
||||
if self.double_up:
|
||||
self.walk_coords = self.walk_coords + self.walk_coords
|
||||
PiWalker.setup(self)
|
||||
|
||||
class PiWalkerCircle(PiWalker):
|
||||
@ -1212,20 +1228,27 @@ class TestRotater(Scene):
|
||||
class OdometerScene(ColorMappedObjectsScene):
|
||||
CONFIG = {
|
||||
# "func" : lambda p : 100 * p # Full coloring, essentially
|
||||
"rotate_func" : lambda x : np.sin(x * TAU), # This is given in terms of CW revs
|
||||
"run_time" : 5,
|
||||
"rotate_func" : lambda x : 2 * np.sin(2 * x * TAU), # This is given in terms of CW revs
|
||||
"run_time" : 40,
|
||||
"dashed_line_angle" : None,
|
||||
"biased_display_start" : None
|
||||
"biased_display_start" : None,
|
||||
"pure_odometer_background" : False
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
ColorMappedObjectsScene.construct(self)
|
||||
|
||||
radius = 1.3
|
||||
radius = ODOMETER_RADIUS
|
||||
circle = Circle(center = ORIGIN, radius = radius)
|
||||
circle.stroke_width = ODOMETER_STROKE_WIDTH
|
||||
circle.color_using_background_image(self.background_image_file)
|
||||
self.add(circle)
|
||||
|
||||
if self.pure_odometer_background:
|
||||
# Just display this background circle, for compositing in Premiere with PiWalker odometers
|
||||
self.wait()
|
||||
return
|
||||
|
||||
if self.dashed_line_angle:
|
||||
dashed_line = DashedLine(ORIGIN, radius * RIGHT)
|
||||
# Clockwise rotation
|
||||
@ -1355,7 +1378,7 @@ class RewriteEquation(Scene):
|
||||
|
||||
class SignsExplanation(Scene):
|
||||
def construct(self):
|
||||
num_line = NumberLine(stroke_width = 1)
|
||||
num_line = NumberLine()
|
||||
largest_num = 10
|
||||
num_line.add_numbers(*range(-largest_num, largest_num + 1))
|
||||
self.add(num_line)
|
||||
@ -1675,44 +1698,75 @@ class Initial2dFuncSceneMorphing(Initial2dFuncSceneBase):
|
||||
|
||||
class DemonstrateColorMapping(ColorMappedObjectsScene):
|
||||
CONFIG = {
|
||||
"show_num_plane" : False
|
||||
"show_num_plane" : False,
|
||||
"show_full_color_map" : True
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
ColorMappedObjectsScene.construct(self)
|
||||
|
||||
# Doing this in Premiere now instead
|
||||
# output_plane_label = TextMobject("Output Plane", color = WHITE)
|
||||
# output_plane_label.move_to(3 * UP)
|
||||
# self.add_foreground_mobject(output_plane_label)
|
||||
|
||||
if self.show_full_color_map:
|
||||
bright_background = Rectangle(width = 2 * SPACE_WIDTH + 1, height = 2 * SPACE_HEIGHT + 1, fill_opacity = 1)
|
||||
bright_background.color_using_background_image(self.background_image_file)
|
||||
dim_background = bright_background.copy()
|
||||
dim_background.fill_opacity = 0.3
|
||||
|
||||
background = bright_background.copy()
|
||||
self.add(background)
|
||||
self.wait()
|
||||
self.play(ReplacementTransform(background, dim_background))
|
||||
|
||||
self.wait()
|
||||
|
||||
ray = Line(ORIGIN, 10 * LEFT)
|
||||
|
||||
|
||||
circle = cw_circle.copy()
|
||||
circle.color_using_background_image(self.background_image_file)
|
||||
|
||||
self.play(ShowCreation(circle))
|
||||
|
||||
self.wait()
|
||||
|
||||
scale_up_factor = 5
|
||||
scale_down_factor = 20
|
||||
self.play(ApplyMethod(circle.scale, fdiv(1, scale_down_factor)))
|
||||
|
||||
self.play(ApplyMethod(circle.scale, scale_up_factor * scale_down_factor))
|
||||
|
||||
self.play(ApplyMethod(circle.scale, fdiv(1, scale_up_factor)))
|
||||
|
||||
self.wait()
|
||||
self.remove(circle)
|
||||
|
||||
ray = Line(ORIGIN, 10 * LEFT)
|
||||
ray.color_using_background_image(self.background_image_file)
|
||||
|
||||
self.play(ShowCreation(circle))
|
||||
|
||||
self.play(ShowCreation(ray))
|
||||
|
||||
scale_up_factor = 5
|
||||
scale_down_factor = 20
|
||||
self.play(ApplyMethod(circle.scale, scale_up_factor))
|
||||
self.wait()
|
||||
|
||||
self.play(ApplyMethod(circle.scale, fdiv(1, scale_up_factor * scale_down_factor)))
|
||||
self.play(Rotating(ray, about_point = ORIGIN, radians = -TAU/2))
|
||||
|
||||
self.play(ApplyMethod(circle.scale, scale_down_factor))
|
||||
self.wait()
|
||||
|
||||
self.play(Rotating(ray, about_point = ORIGIN, radians = -TAU))
|
||||
self.play(Rotating(ray, about_point = ORIGIN, radians = -TAU/2))
|
||||
|
||||
# TODO: Illustrations for introducing domain coloring
|
||||
self.wait()
|
||||
|
||||
# TODO: Bunch of Pi walker scenes
|
||||
|
||||
# TODO: An odometer scene when introducing winding numbers
|
||||
# (Just set up an OdometerScene with function matching the walking of the Pi
|
||||
# creature from previous scene, then place it as a simultaneous inset with Premiere)
|
||||
if self.show_full_color_map:
|
||||
self.play(ReplacementTransform(background, bright_background))
|
||||
self.wait()
|
||||
|
||||
class LoopSplitScene(ColorMappedObjectsScene):
|
||||
CONFIG = {
|
||||
# TODO: Change this to something more colorful down the midline
|
||||
"func" : plane_func_by_wind_spec((0.1/2, 1.1/2, 1), (-4.1/2, -1.3/2, 2), (1.8/2, -2.1/2, -1)),
|
||||
"func" : plane_func_by_wind_spec((0.1/2, 1.1/2, 1), (-3.1/2, -1.3/2, 2), (1.8/2, -2.1/2, -1)),
|
||||
"use_fancy_lines" : True,
|
||||
}
|
||||
|
||||
@ -1722,7 +1776,7 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||
num_bullets = 4,
|
||||
pulse_time = 1,
|
||||
**kwargs):
|
||||
line = Line(start, end, **kwargs)
|
||||
line = Line(start, end, color = WHITE, **kwargs)
|
||||
if self.use_fancy_lines:
|
||||
line.color_using_background_image(self.background_image_file)
|
||||
anim = LinePulser(
|
||||
@ -1732,7 +1786,7 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||
pulse_time = pulse_time,
|
||||
output_func = self.func,
|
||||
**kwargs)
|
||||
return [VGroup(line, *anim.bullets), anim]
|
||||
return (line, VMobject(*anim.bullets), anim)
|
||||
|
||||
def construct(self):
|
||||
ColorMappedObjectsScene.construct(self)
|
||||
@ -1743,83 +1797,90 @@ class LoopSplitScene(ColorMappedObjectsScene):
|
||||
# TODO: Change all this to use a wider than tall loop, made of two squares
|
||||
|
||||
# Original loop
|
||||
tl = scale_factor * (UP + LEFT) + shift_term
|
||||
tm = scale_factor * UP + shift_term
|
||||
tr = scale_factor * (UP + RIGHT) + shift_term
|
||||
mr = scale_factor * RIGHT + shift_term
|
||||
br = scale_factor * (DOWN + RIGHT) + shift_term
|
||||
bm = scale_factor * DOWN + shift_term
|
||||
bl = scale_factor * (DOWN + LEFT) + shift_term
|
||||
lm = scale_factor * LEFT + shift_term
|
||||
tl = (UP + 2 * LEFT) * scale_factor
|
||||
tm = UP * scale_factor
|
||||
tr = (UP + 2 * RIGHT) * scale_factor
|
||||
bl = (DOWN + 2 * LEFT) * scale_factor
|
||||
bm = DOWN * scale_factor
|
||||
br = (DOWN + 2 * RIGHT) * scale_factor
|
||||
|
||||
loop_color = WHITE
|
||||
top_line = Line(tl, tr) # Invisible; only used for surrounding circle
|
||||
bottom_line = Line(br, bl) # Invisible; only used for surrounding circle
|
||||
|
||||
default_bullet = PiCreature(color = RED)
|
||||
stroke_width = top_line.stroke_width
|
||||
|
||||
default_bullet = PiCreature()
|
||||
default_bullet.scale(0.15)
|
||||
|
||||
modified_bullet = PiCreature(color = PINK)
|
||||
modified_bullet.scale(0.15)
|
||||
def pl(a, b):
|
||||
return self.PulsedLine(a, b, default_bullet)
|
||||
|
||||
def SGroup(*args):
|
||||
return VGroup(*[arg[0] for arg in args])
|
||||
faded = 0.3
|
||||
|
||||
top_line = self.PulsedLine(tl, tr, default_bullet, color = loop_color)
|
||||
right_line = self.PulsedLine(tr, br, modified_bullet, color = loop_color)
|
||||
bottom_line = self.PulsedLine(br, bl, default_bullet, color = loop_color)
|
||||
left_line = self.PulsedLine(bl, tl, default_bullet, color = loop_color)
|
||||
line_list = [top_line, right_line, bottom_line, left_line]
|
||||
loop = SGroup(*line_list)
|
||||
for line in line_list:
|
||||
self.add(*line)
|
||||
self.wait()
|
||||
# Workaround for FadeOut/FadeIn not playing well with ContinualAnimations due to
|
||||
# Transforms making copies no longer identified with the ContinualAnimation's tracked mobject
|
||||
def fader_bullet(start, end, mob):
|
||||
return UpdateFromAlphaFunc(mob, lambda m, a : m.set_fill(opacity = interpolate(start, end, a)))
|
||||
|
||||
# Splits in middle
|
||||
if self.use_fancy_lines:
|
||||
split_line = Line(tm, bm)
|
||||
split_line.color_using_background_image(self.background_image_file)
|
||||
else:
|
||||
split_line = DashedLine(tm, bm)
|
||||
self.play(ShowCreation(split_line))
|
||||
def fader_blist(start, end, blist):
|
||||
return map(lambda b : fader_bullet(start, end, b), blist)
|
||||
|
||||
self.remove(*split_line)
|
||||
mid_line_left = self.PulsedLine(tm, bm, default_bullet, color = loop_color)
|
||||
mid_line_right = self.PulsedLine(bm, tm, modified_bullet, color = loop_color)
|
||||
self.add(*mid_line_left)
|
||||
self.add(*mid_line_right)
|
||||
def fader_widthmob(start, end, mob):
|
||||
return UpdateFromAlphaFunc(mob, lambda m, a : m.set_stroke(width = interpolate(start, end, a) * stroke_width))
|
||||
|
||||
top_line_left_half = self.PulsedLine(tl, tm, default_bullet, 2, 1, color = loop_color)
|
||||
top_line_right_half = self.PulsedLine(tm, tr, modified_bullet, 2, 1, color = loop_color)
|
||||
def indicate_circle(x):
|
||||
circle = Circle(color = WHITE)
|
||||
circle.surround(x)
|
||||
# if x.get_slope == 0:
|
||||
# circle.stretch(0, 0.3)
|
||||
# else:
|
||||
# circle.stretch(0.3, 0)
|
||||
circle.stretch(0.3, 0.3)
|
||||
return circle
|
||||
|
||||
bottom_line_left_half = self.PulsedLine(bm, bl, default_bullet, 2, 1, color = loop_color)
|
||||
bottom_line_right_half = self.PulsedLine(br, bm, modified_bullet, 2, 1, color = loop_color)
|
||||
tl_line_trip = pl(tl, tm)
|
||||
left_mid_trip = pl(tm, bm)
|
||||
bl_line_trip = pl(bm, bl)
|
||||
left_line_trip = pl(bl, tl)
|
||||
|
||||
self.remove(*top_line)
|
||||
self.add(*top_line_left_half)
|
||||
self.add(*top_line_right_half)
|
||||
self.remove(*bottom_line)
|
||||
self.add(*bottom_line_left_half)
|
||||
self.add(*bottom_line_right_half)
|
||||
left_square_trips = [tl_line_trip, left_mid_trip, bl_line_trip, left_line_trip]
|
||||
left_square_lines = [x[0] for x in left_square_trips]
|
||||
left_square_lines_vmobject = VMobject(*left_square_lines)
|
||||
left_square_bullets = [x[1] for x in left_square_trips]
|
||||
left_square_anims = [x[2] for x in left_square_trips]
|
||||
|
||||
left_open_loop = SGroup(top_line_left_half, left_line, bottom_line_left_half)
|
||||
left_closed_loop = VGroup(left_open_loop, mid_line_left[0])
|
||||
right_open_loop = SGroup(top_line_right_half, right_line, bottom_line_right_half)
|
||||
right_closed_loop = VGroup(right_open_loop, mid_line_right[0])
|
||||
tr_line_trip = pl(tm, tr)
|
||||
right_line_trip = pl(tr, br)
|
||||
br_line_trip = pl(br, bm)
|
||||
right_midline_trip = pl(bm, tm)
|
||||
|
||||
# self.play(
|
||||
# ApplyMethod(left_closed_loop.shift, LEFT),
|
||||
# ApplyMethod(right_closed_loop.shift, RIGHT)
|
||||
# )
|
||||
right_square_trips = [tr_line_trip, right_line_trip, br_line_trip, right_midline_trip]
|
||||
right_square_lines = [x[0] for x in right_square_trips]
|
||||
right_square_lines_vmobject = VMobject(*right_square_lines)
|
||||
right_square_bullets = [x[1] for x in right_square_trips]
|
||||
right_square_anims = [x[2] for x in right_square_trips]
|
||||
|
||||
# Hm... still some slight bug with this.
|
||||
for b in left_square_bullets + right_square_bullets:
|
||||
b.set_fill(opacity = 0)
|
||||
|
||||
self.add(*left_square_anims)
|
||||
self.play(fader_widthmob(0, 1, left_square_lines_vmobject), *fader_blist(0, 1, left_square_bullets))
|
||||
|
||||
self.add(*right_square_anims)
|
||||
self.play(fader_widthmob(0, 1, right_square_lines_vmobject), *fader_blist(0, 1, right_square_bullets))
|
||||
|
||||
self.play(fader_widthmob(1, faded, right_square_lines_vmobject), *fader_blist(1, faded, right_square_bullets))
|
||||
|
||||
# left_circlers = [indicate_circle(l) for l in left_square_lines]
|
||||
# self.play(*map(FadeIn, left_circlers))
|
||||
# self.play(*map(FadeOut, left_circlers))
|
||||
|
||||
self.play(ShowCreation(indicate_circle(left_square_lines[1])))
|
||||
|
||||
self.wait()
|
||||
|
||||
# self.play(
|
||||
# ApplyMethod(left_open_loop.shift, LEFT),
|
||||
# ApplyMethod(right_open_loop.shift, RIGHT)
|
||||
# )
|
||||
|
||||
self.wait()
|
||||
|
||||
mid_lines = SGroup(mid_line_left, mid_line_right)
|
||||
return
|
||||
|
||||
highlight_circle = Circle(color = WHITE) # Perhaps make this a dashed circle?
|
||||
highlight_circle.surround(mid_lines)
|
||||
@ -1860,6 +1921,21 @@ class FundThmAlg(EquationSolver2d):
|
||||
"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,
|
||||
}
|
||||
|
||||
class SolveX5MinusXMinus1Parallel(EquationSolver2d):
|
||||
CONFIG = {
|
||||
"func" : plane_func_from_complex_func(lambda c : c**5 - c - 1),
|
||||
"num_iterations" : 5,
|
||||
"use_fancy_lines" : True,
|
||||
"display_in_parallel" : True
|
||||
}
|
||||
|
||||
class PreviewClip(EquationSolver2d):
|
||||
CONFIG = {
|
||||
"func" : example_plane_func,
|
||||
@ -1985,23 +2061,45 @@ class CombineInterval2(Scene):
|
||||
|
||||
self.wait()
|
||||
|
||||
tiny_loop_func = plane_func_by_wind_spec((-1, -2), (1, 1), (1, 1))
|
||||
class TinyLoopInInputPlane(ColorMappedByFuncScene):
|
||||
tiny_loop_func = scale_func(plane_func_by_wind_spec((-1, -2), (1, 1), (1, 1)), 0.3)
|
||||
|
||||
class TinyLoopScene(ColorMappedByFuncScene):
|
||||
CONFIG = {
|
||||
"func" : tiny_loop_func,
|
||||
"show_num_plane" : False,
|
||||
"loop_point" : ORIGIN,
|
||||
"circle_scale" : 0.7
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
ColorMappedByFuncScene.construct(self)
|
||||
self.wait()
|
||||
|
||||
circle = cw_circle.copy()
|
||||
circle.move_to(UP + RIGHT)
|
||||
circle.scale(self.circle_scale)
|
||||
circle.move_to(self.loop_point)
|
||||
|
||||
self.play(ShowCreation(circle))
|
||||
self.wait()
|
||||
|
||||
class TinyLoopInOutputPlane(TinyLoopInInputPlane):
|
||||
class TinyLoopInInputPlaneAroundNonZero(TinyLoopScene):
|
||||
CONFIG = {
|
||||
"loop_point" : 0.5 * RIGHT
|
||||
}
|
||||
|
||||
class TinyLoopInInputPlaneAroundZero(TinyLoopScene):
|
||||
CONFIG = {
|
||||
"loop_point" : UP + RIGHT
|
||||
}
|
||||
|
||||
class TinyLoopInOutputPlaneAroundNonZero(TinyLoopInInputPlaneAroundNonZero):
|
||||
CONFIG = {
|
||||
"camera_class" : MappingCamera,
|
||||
"camera_config" : {"mapping_func" : point3d_func_from_plane_func(tiny_loop_func)},
|
||||
"show_output" : True,
|
||||
"show_num_plane" : False,
|
||||
}
|
||||
|
||||
class TinyLoopInOutputPlaneAroundZero(TinyLoopInInputPlaneAroundZero):
|
||||
CONFIG = {
|
||||
"camera_class" : MappingCamera,
|
||||
"camera_config" : {"mapping_func" : point3d_func_from_plane_func(tiny_loop_func)},
|
||||
@ -2179,6 +2277,17 @@ class UhOhScene(EquationSolver2d):
|
||||
"num_iterations" : 5,
|
||||
}
|
||||
|
||||
class UhOhSceneWithWindingNumbers(UhOhScene):
|
||||
CONFIG = {
|
||||
"show_winding_numbers" : True,
|
||||
}
|
||||
|
||||
class UhOhSceneWithWindingNumbersNoOverride(UhOhSceneWithWindingNumbers):
|
||||
CONFIG = {
|
||||
"manual_wind_override" : None,
|
||||
"num_iterations" : 2
|
||||
}
|
||||
|
||||
class UhOhSalientStill(ColorMappedObjectsScene):
|
||||
CONFIG = {
|
||||
"func" : uhOhFunc
|
||||
@ -2301,19 +2410,163 @@ class ZetaViz(PureColorMap):
|
||||
"show_num_plane" : True
|
||||
}
|
||||
|
||||
class RescaledZetaViz(PureColorMap):
|
||||
class TopLabel(Scene):
|
||||
CONFIG = {
|
||||
"func" : rescaled_plane_zeta,
|
||||
#"num_plane" : criticalStrip,
|
||||
"show_num_plane" : True
|
||||
"text" : "Text"
|
||||
}
|
||||
def construct(self):
|
||||
label = TextMobject(self.text)
|
||||
label.move_to(3 * UP)
|
||||
self.add(label)
|
||||
self.wait()
|
||||
|
||||
# This is a giant hack that doesn't handle rev wrap-around correctly; should use
|
||||
# make_alpha_winder instead
|
||||
class SpecifiedWinder(PiWalker):
|
||||
CONFIG = {
|
||||
"start_x" : 0,
|
||||
"start_y" : 0,
|
||||
"x_wind" : 1, # Assumed positive
|
||||
"y_wind" : 1, # Assumed positive
|
||||
"step_size" : 0.1
|
||||
}
|
||||
|
||||
class SolveZeta(EquationSolver2d):
|
||||
def setup(self):
|
||||
rev_func = lambda p : point_to_rev(self.func(p))
|
||||
start_pos = np.array((self.start_x, self.start_y))
|
||||
cur_pos = start_pos.copy()
|
||||
start_rev = rev_func(start_pos)
|
||||
|
||||
mid_rev = start_rev
|
||||
while (abs(mid_rev - start_rev) < self.x_wind):
|
||||
cur_pos += (self.step_size, 0)
|
||||
mid_rev = rev_func(cur_pos)
|
||||
|
||||
print "Reached ", cur_pos, ", with rev ", mid_rev - start_rev
|
||||
mid_pos = cur_pos.copy()
|
||||
|
||||
end_rev = mid_rev
|
||||
while (abs(end_rev - mid_rev) < self.y_wind):
|
||||
cur_pos -= (0, self.step_size)
|
||||
end_rev = rev_func(cur_pos)
|
||||
|
||||
end_pos = cur_pos.copy()
|
||||
|
||||
print "Reached ", cur_pos, ", with rev ", end_rev - mid_rev
|
||||
|
||||
self.walk_coords = [start_pos, mid_pos, end_pos]
|
||||
print "Walk coords: ", self.walk_coords
|
||||
PiWalker.setup(self)
|
||||
|
||||
class OneFifthTwoFifthWinder(SpecifiedWinder):
|
||||
CONFIG = {
|
||||
"func" : rescaled_plane_zeta,
|
||||
"num_iterations" : 7,
|
||||
"display_in_parallel" : False,
|
||||
"use_fancy_lines" : True,
|
||||
"func" : example_plane_func,
|
||||
"start_x" : -2.0,
|
||||
"start_y" : 1.0,
|
||||
"x_wind" : 0.2,
|
||||
"y_wind" : 0.2,
|
||||
"step_size" : 0.01,
|
||||
"show_num_plane" : False,
|
||||
"step_run_time" : 6,
|
||||
}
|
||||
|
||||
class OneFifthTwoFifthWinderOdometer(OneFifthTwoFifthWinder):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class ForwardBackWalker(PiWalker):
|
||||
CONFIG = {
|
||||
"func" : example_plane_func,
|
||||
"walk_coords" : [np.array((-2, 1)), np.array((1, 1))],
|
||||
"step_run_time" : 3,
|
||||
}
|
||||
|
||||
class ForwardBackWalkerOdometer(ForwardBackWalker):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class PureOdometerBackground(OdometerScene):
|
||||
CONFIG = {
|
||||
"pure_odometer_background" : True
|
||||
}
|
||||
|
||||
class CWColorWalk(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"func" : example_plane_func,
|
||||
"start_x" : example_plane_func_spec[0][0] - 1,
|
||||
"start_y" : example_plane_func_spec[0][1] + 1,
|
||||
"walk_width" : 2,
|
||||
"walk_height" : 2,
|
||||
"draw_lines" : False,
|
||||
}
|
||||
|
||||
class CWColorWalkOdometer(CWColorWalk):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class CCWColorWalk(CWColorWalk):
|
||||
CONFIG = {
|
||||
"start_x" : example_plane_func_spec[2][0] - 1,
|
||||
"start_y" : example_plane_func_spec[2][1] + 1,
|
||||
}
|
||||
|
||||
class CCWColorWalkOdometer(CCWColorWalk):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class ThreeTurnWalker(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"func" : plane_func_from_complex_func(lambda c: c**3 * complex(1, 1)**3),
|
||||
"double_up" : True
|
||||
}
|
||||
|
||||
class ThreeTurnWalkerOdometer(ThreeTurnWalker):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class FourTurnWalker(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"func" : plane_func_by_wind_spec((0, 0, 4))
|
||||
}
|
||||
|
||||
class FourTurnWalkerOdometer(FourTurnWalker):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class OneTurnWalker(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"func" : plane_func_from_complex_func(lambda c : np.exp(c) + c)
|
||||
}
|
||||
|
||||
class OneTurnWalkerOdometer(OneTurnWalker):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class ZeroTurnWalker(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"func" : plane_func_by_wind_spec((2, 2, 1), (-1, 2, -1))
|
||||
}
|
||||
|
||||
class ZeroTurnWalkerOdometer(ZeroTurnWalker):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
class NegOneTurnWalker(PiWalkerRect):
|
||||
CONFIG = {
|
||||
"func" : plane_func_by_wind_spec((0, 0, -1))
|
||||
}
|
||||
|
||||
class NegOneTurnWalkerOdometer(NegOneTurnWalker):
|
||||
CONFIG = {
|
||||
"display_odometer" : True,
|
||||
}
|
||||
|
||||
# FIN
|
@ -1813,6 +1813,12 @@ class ForeverNarrowingLoop(InputOutputScene):
|
||||
"target_coords" : (1, 1),
|
||||
"input_plane_corner" : UP+RIGHT,
|
||||
"shrink_time" : 20,
|
||||
"circle_start_radius" : 2.25,
|
||||
"start_around_target" : False,
|
||||
|
||||
# Added as a flag to not mess up one clip already used and fine-timed
|
||||
# but to make it more convenient to do the other TinyLoop edits
|
||||
"add_convenient_waits" : False
|
||||
}
|
||||
def construct(self):
|
||||
input_coloring, output_coloring = colorings = VGroup(*self.get_colorings())
|
||||
@ -1837,14 +1843,17 @@ class ForeverNarrowingLoop(InputOutputScene):
|
||||
), run_time = 2)
|
||||
|
||||
# circle
|
||||
circle = Circle(color = WHITE, radius = 2.25)
|
||||
circle = Circle(color = WHITE, radius = self.circle_start_radius)
|
||||
circle.flip(axis = RIGHT)
|
||||
circle.insert_n_anchor_points(50)
|
||||
circle.next_to(
|
||||
input_coloring.get_corner(self.input_plane_corner),
|
||||
-self.input_plane_corner,
|
||||
SMALL_BUFF
|
||||
)
|
||||
if self.start_around_target:
|
||||
circle.move_to(input_plane.coords_to_point(*self.target_coords))
|
||||
else:
|
||||
circle.next_to(
|
||||
input_coloring.get_corner(self.input_plane_corner),
|
||||
-self.input_plane_corner,
|
||||
SMALL_BUFF
|
||||
)
|
||||
circle.set_stroke(width = 5)
|
||||
circle_image = circle.copy()
|
||||
circle.match_background_image_file(input_coloring)
|
||||
@ -1859,12 +1868,18 @@ class ForeverNarrowingLoop(InputOutputScene):
|
||||
circle_image, update_circle_image
|
||||
)
|
||||
|
||||
def optional_wait():
|
||||
if self.add_convenient_waits:
|
||||
self.wait()
|
||||
|
||||
optional_wait()
|
||||
self.play(
|
||||
ShowCreation(circle),
|
||||
ShowCreation(circle_image),
|
||||
run_time = 3,
|
||||
rate_func = bezier([0, 0, 1, 1])
|
||||
)
|
||||
optional_wait()
|
||||
self.play(
|
||||
circle.scale, 0,
|
||||
circle.move_to, input_plane.coords_to_point(*self.target_coords),
|
||||
@ -1880,6 +1895,39 @@ class AltForeverNarrowingLoop(ForeverNarrowingLoop):
|
||||
"shrink_time" : 3,
|
||||
}
|
||||
|
||||
class TinyLoop(ForeverNarrowingLoop):
|
||||
CONFIG = {
|
||||
"circle_start_radius" : 0.5,
|
||||
"start_around_target" : True,
|
||||
"shrink_time" : 1,
|
||||
"add_convenient_waits" : True,
|
||||
}
|
||||
|
||||
class TinyLoopAroundZero(TinyLoop):
|
||||
CONFIG = {
|
||||
"target_coords" : (1, 1),
|
||||
}
|
||||
|
||||
class TinyLoopAroundBlue(TinyLoop):
|
||||
CONFIG = {
|
||||
"target_coords" : (2.4, 0),
|
||||
}
|
||||
|
||||
class TinyLoopAroundYellow(TinyLoop):
|
||||
CONFIG = {
|
||||
"target_coords" : (0, -1.3),
|
||||
}
|
||||
|
||||
class TinyLoopAroundOrange(TinyLoop):
|
||||
CONFIG = {
|
||||
"target_coords" : (0, -0.5),
|
||||
}
|
||||
|
||||
class TinyLoopAroundRed(TinyLoop):
|
||||
CONFIG = {
|
||||
"target_coords" : (-1, 1),
|
||||
}
|
||||
|
||||
class FailureOfComposition(ColorMappedObjectsScene):
|
||||
CONFIG = {
|
||||
"func" : lambda p : (
|
||||
|
@ -65,13 +65,6 @@ class VMobject(Mobject):
|
||||
return self
|
||||
|
||||
def set_fill(self, color = None, opacity = None, family = True):
|
||||
probably_meant_to_change_opacity = reduce(op.and_, [
|
||||
color is not None,
|
||||
opacity is None,
|
||||
self.fill_opacity == 0
|
||||
])
|
||||
if probably_meant_to_change_opacity:
|
||||
opacity = 1
|
||||
return self.set_style_data(
|
||||
fill_color = color,
|
||||
fill_opacity = opacity,
|
||||
|
Reference in New Issue
Block a user