mirror of
https://github.com/3b1b/manim.git
synced 2025-08-19 13:01:00 +00:00
Up to showing recursive algorithm in Hanoi
This commit is contained in:
parent
4378aac88b
commit
8f06c9db1c
3 changed files with 667 additions and 33 deletions
|
@ -258,10 +258,6 @@ class Succession(Animation):
|
|||
self.last_index = 0
|
||||
Animation.__init__(self, mobject, run_time = run_time, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return self.__class__.__name__ + \
|
||||
"".join(map(str, self.anims))
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
scaled_alpha = alpha*self.num_anims
|
||||
index = min(int(scaled_alpha), len(self.anims)-1)
|
||||
|
|
693
hanoi.py
693
hanoi.py
|
@ -96,7 +96,8 @@ class CountingScene(Scene):
|
|||
self.increment(run_time_per_anim)
|
||||
|
||||
def increment(self, run_time_per_anim = 1, added_anims = [], total_run_time = None):
|
||||
if total_run_time is not None:
|
||||
run_all_at_once = (total_run_time is not None)
|
||||
if run_all_at_once:
|
||||
num_rollovers = self.get_num_rollovers()
|
||||
run_time_per_anim = float(total_run_time)/(num_rollovers+1)
|
||||
moving_dot = Dot(
|
||||
|
@ -106,9 +107,6 @@ class CountingScene(Scene):
|
|||
)
|
||||
moving_dot.generate_target()
|
||||
moving_dot.set_fill(opacity = 0)
|
||||
kwargs = {
|
||||
"run_time" : run_time_per_anim
|
||||
}
|
||||
|
||||
continue_rolling_over = True
|
||||
first_move = True
|
||||
|
@ -123,9 +121,25 @@ class CountingScene(Scene):
|
|||
moving_dot.target.replace(
|
||||
self.dot_template_iterators[place].next()
|
||||
)
|
||||
self.play(MoveToTarget(moving_dot), *added_anims, **kwargs)
|
||||
if run_all_at_once:
|
||||
denom = float(num_rollovers+1)
|
||||
start_t = place/denom
|
||||
def get_modified_rate_func(anim):
|
||||
return lambda t : anim.original_rate_func(
|
||||
start_t + t/denom
|
||||
)
|
||||
for anim in added_anims:
|
||||
if not hasattr(anim, "original_rate_func"):
|
||||
anim.original_rate_func = anim.rate_func
|
||||
anim.rate_func = get_modified_rate_func(anim)
|
||||
self.play(
|
||||
MoveToTarget(moving_dot),
|
||||
*added_anims,
|
||||
run_time = run_time_per_anim
|
||||
)
|
||||
self.curr_configurations[place].add(moving_dot)
|
||||
added_anims = []
|
||||
if not run_all_at_once:
|
||||
added_anims = []
|
||||
|
||||
|
||||
if len(self.curr_configurations[place].split()) == self.base:
|
||||
|
@ -337,7 +351,6 @@ class TowersOfHanoiScene(Scene):
|
|||
else:
|
||||
return self.disks[min_disk_index].get_top()
|
||||
|
||||
|
||||
def get_next_disk_0_peg(self):
|
||||
curr_peg_index = self.disk_index_to_peg_index(0)
|
||||
return (curr_peg_index+1)%3
|
||||
|
@ -455,6 +468,26 @@ def get_binary_tex_mobs(num_list):
|
|||
result.add(bits)
|
||||
return result
|
||||
|
||||
def get_binary_tex_mob(number, n_bits = 4):
|
||||
assert(number < 2**n_bits)
|
||||
curr_bit = n_bits - 1
|
||||
zero_width = TexMobject("0").get_width()
|
||||
result = VGroup()
|
||||
while curr_bit >= 0:
|
||||
if number >= 2**curr_bit:
|
||||
bit = "1"
|
||||
number -= 2**curr_bit
|
||||
else:
|
||||
bit = "0"
|
||||
bit_mob = TexMobject(bit)
|
||||
n = n_bits - curr_bit - 1
|
||||
bit_mob.shift(n*(zero_width+SMALL_BUFF)*RIGHT)
|
||||
result.add(bit_mob)
|
||||
curr_bit -= 1
|
||||
return result.center()
|
||||
|
||||
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
@ -472,8 +505,16 @@ class IntroduceKeith(Scene):
|
|||
randy = Randolph().next_to(keith, LEFT, LARGE_BUFF, aligned_edge = DOWN)
|
||||
randy.shift_onto_screen()
|
||||
|
||||
bubble = keith.get_bubble("speech")
|
||||
bubble.write("Check this out...")
|
||||
bubble = keith.get_bubble("speech", width = 7)
|
||||
bubble.write("01101011 $\\Rightarrow$ Towers of Hanoi")
|
||||
zero_width = bubble.content[0].get_width()
|
||||
one_width = bubble.content[1].get_width()
|
||||
for mob in bubble.content[:8]:
|
||||
if abs(mob.get_width() - zero_width) < 0.01:
|
||||
mob.highlight(GREEN)
|
||||
else:
|
||||
mob.highlight(YELLOW)
|
||||
|
||||
bubble.resize_to_content()
|
||||
bubble.pin_to(keith)
|
||||
VGroup(bubble, bubble.content).shift(DOWN)
|
||||
|
@ -928,7 +969,7 @@ class IntroduceBinaryCounting(BinaryCountingScene):
|
|||
def construct(self):
|
||||
self.introduce_name()
|
||||
self.initial_counting()
|
||||
self.rhtyhm_of_counting()
|
||||
self.show_self_similarity()
|
||||
|
||||
def introduce_name(self):
|
||||
title = TextMobject("Binary (base 2):", "0, 1")
|
||||
|
@ -955,10 +996,6 @@ class IntroduceBinaryCounting(BinaryCountingScene):
|
|||
self.play(bits.restore)
|
||||
self.dither()
|
||||
|
||||
|
||||
# for x in range(16):
|
||||
# self.increment(0.5)
|
||||
|
||||
def initial_counting(self):
|
||||
randy = Randolph().to_corner(DOWN+LEFT)
|
||||
bubble = randy.get_bubble("thought", height = 3.4, width = 5)
|
||||
|
@ -970,6 +1007,7 @@ class IntroduceBinaryCounting(BinaryCountingScene):
|
|||
self.play(self.number_mob.set_fill, self.power_colors[0], 1)
|
||||
self.increment()
|
||||
self.dither()
|
||||
self.start_dot = self.curr_configurations[0][0]
|
||||
|
||||
##Up to 10
|
||||
self.increment()
|
||||
|
@ -1038,22 +1076,619 @@ class IntroduceBinaryCounting(BinaryCountingScene):
|
|||
randy.look_at, self.number_mob,
|
||||
*map(MoveToTarget, [brace, twos_place])
|
||||
)
|
||||
for x in range(7):
|
||||
self.increment(total_run_time = 1)
|
||||
self.randy = randy
|
||||
|
||||
def rhtyhm_of_counting(self):
|
||||
randy = self.randy
|
||||
# randy = Randolph()
|
||||
self.increment(total_run_time = 1, added_anims = [
|
||||
randy.change_mode, "wave_1"
|
||||
])
|
||||
randy.target = randy.copy().change_mode("wave_2")
|
||||
arm_movement = MoveToTarget(randy, rate_func = there_and_back)
|
||||
self.play(arm_movement)
|
||||
|
||||
for x in range(8):
|
||||
self.increment(total_run_time = 1, added_anims = [arm_movement])
|
||||
self.increment(total_run_time = 1)
|
||||
self.dither()
|
||||
for x in range(8):
|
||||
self.increment(total_run_time = 1.5)
|
||||
|
||||
def show_self_similarity(self):
|
||||
cover_rect = Rectangle()
|
||||
cover_rect.scale_to_fit_width(2*SPACE_WIDTH)
|
||||
cover_rect.scale_to_fit_height(2*SPACE_HEIGHT)
|
||||
cover_rect.set_stroke(width = 0)
|
||||
cover_rect.set_fill(BLACK, opacity = 0.85)
|
||||
big_dot = self.curr_configurations[-1][0].copy()
|
||||
self.play(
|
||||
FadeIn(cover_rect),
|
||||
Animation(big_dot)
|
||||
)
|
||||
self.play(
|
||||
big_dot.center,
|
||||
big_dot.scale_to_fit_height, 2*SPACE_HEIGHT-2,
|
||||
big_dot.to_edge, LEFT,
|
||||
run_time = 5
|
||||
)
|
||||
|
||||
class BinaryCountingAtEveryScale(Scene):
|
||||
CONFIG = {
|
||||
"num_bits" : 4,
|
||||
"show_title" : False,
|
||||
}
|
||||
def construct(self):
|
||||
title = TextMobject("Count to %d (which is %s in binary)"%(
|
||||
2**self.num_bits-1, bin(2**self.num_bits-1)[2:]
|
||||
))
|
||||
title.to_edge(UP)
|
||||
if self.show_title:
|
||||
self.add(title)
|
||||
|
||||
bit_mobs = [
|
||||
get_binary_tex_mob(n, self.num_bits)
|
||||
for n in range(2**self.num_bits)
|
||||
]
|
||||
curr_bits = bit_mobs[0]
|
||||
|
||||
lower_brace = Brace(VGroup(*curr_bits[1:]))
|
||||
do_a_thing = lower_brace.get_text("Do a thing")
|
||||
VGroup(lower_brace, do_a_thing).highlight(YELLOW)
|
||||
upper_brace = Brace(curr_bits, UP)
|
||||
roll_over = upper_brace.get_text("Roll over")
|
||||
VGroup(upper_brace, roll_over).highlight(MAROON_B)
|
||||
again = TextMobject("again")
|
||||
again.next_to(do_a_thing, RIGHT, 2*SMALL_BUFF)
|
||||
again.highlight(YELLOW)
|
||||
|
||||
self.add(curr_bits, lower_brace, do_a_thing)
|
||||
|
||||
def get_run_through(mobs):
|
||||
return Succession(*[
|
||||
Transform(
|
||||
curr_bits, mob,
|
||||
rate_func = squish_rate_func(smooth, 0, 0.5)
|
||||
)
|
||||
for mob in mobs
|
||||
], run_time = 1)
|
||||
|
||||
for bit_mob in bit_mobs:
|
||||
curr_bits.align_data(bit_mob)
|
||||
bit_mob.highlight(YELLOW)
|
||||
bit_mob[0].highlight(MAROON_B)
|
||||
self.play(get_run_through(bit_mobs[1:2**(self.num_bits-1)]))
|
||||
self.play(*map(FadeIn, [upper_brace, roll_over]))
|
||||
self.play(Transform(
|
||||
VGroup(*reversed(list(curr_bits))),
|
||||
VGroup(*reversed(list(bit_mobs[2**(self.num_bits-1)]))),
|
||||
submobject_mode = "lagged_start",
|
||||
lag_factor = self.num_bits
|
||||
))
|
||||
self.dither()
|
||||
self.play(
|
||||
get_run_through(bit_mobs[2**(self.num_bits-1)+1:]),
|
||||
Write(again)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
class BinaryCountingAtSmallestScale(BinaryCountingAtEveryScale):
|
||||
CONFIG = {
|
||||
"num_bits" : 2,
|
||||
"show_title" : True,
|
||||
}
|
||||
|
||||
class BinaryCountingAtMediumScale(BinaryCountingAtEveryScale):
|
||||
CONFIG = {
|
||||
"num_bits" : 4,
|
||||
"show_title" : True,
|
||||
}
|
||||
|
||||
class BinaryCountingAtLargeScale(BinaryCountingAtEveryScale):
|
||||
CONFIG = {
|
||||
"num_bits" : 8,
|
||||
"show_title" : True,
|
||||
}
|
||||
|
||||
class IntroduceSolveByCounting(TowersOfHanoiScene):
|
||||
CONFIG = {
|
||||
"num_disks" : 4
|
||||
}
|
||||
def construct(self):
|
||||
self.initialize_bit_mobs()
|
||||
for disk in self.disks:
|
||||
disk.original_fill_color = disk.get_color()
|
||||
braces = [
|
||||
Brace(VGroup(*self.curr_bit_mob[-n:]))
|
||||
for n in range(1, self.num_disks + 1)
|
||||
]
|
||||
word_list = [
|
||||
brace.get_text(text)
|
||||
for brace, text in zip(braces, [
|
||||
"Only flip last bit",
|
||||
"Roll over once",
|
||||
"Roll over twice",
|
||||
"Roll over three times",
|
||||
])
|
||||
]
|
||||
brace = braces[0].copy()
|
||||
words = word_list[0].copy()
|
||||
|
||||
##First increment
|
||||
self.play(self.get_increment_animation())
|
||||
self.play(
|
||||
GrowFromCenter(brace),
|
||||
Write(words, run_time = 1)
|
||||
)
|
||||
disk = self.disks[0]
|
||||
last_bit = self.curr_bit_mob[-1]
|
||||
last_bit.save_state()
|
||||
self.play(
|
||||
disk.set_fill, YELLOW,
|
||||
disk[1].set_fill, BLACK,
|
||||
last_bit.set_fill, YELLOW,
|
||||
)
|
||||
self.dither()
|
||||
self.move_disk(0, run_time = 2)
|
||||
self.play(
|
||||
last_bit.restore,
|
||||
disk.set_fill, disk.original_fill_color,
|
||||
self.disks[0][1].set_fill, BLACK
|
||||
)
|
||||
|
||||
##Second increment
|
||||
self.play(
|
||||
self.get_increment_animation(),
|
||||
Transform(words, word_list[1]),
|
||||
Transform(brace, braces[1]),
|
||||
)
|
||||
disk = self.disks[1]
|
||||
twos_bit = self.curr_bit_mob[-2]
|
||||
twos_bit.save_state()
|
||||
self.play(
|
||||
disk.set_fill, MAROON_B,
|
||||
disk[1].set_fill, BLACK,
|
||||
twos_bit.set_fill, MAROON_B,
|
||||
)
|
||||
self.move_disk(1, run_time = 2)
|
||||
self.dither()
|
||||
self.move_disk_to_peg(1, 1, stay_on_peg = False)
|
||||
cross = TexMobject("\\times")
|
||||
cross.replace(disk)
|
||||
cross.set_fill(RED, opacity = 0.5)
|
||||
self.play(FadeIn(cross))
|
||||
self.dither()
|
||||
self.move_disk_to_peg(
|
||||
1, 2, stay_on_peg = False,
|
||||
added_anims = [FadeOut(cross)]
|
||||
)
|
||||
self.play(
|
||||
disk.set_fill, disk.original_fill_color,
|
||||
disk[1].set_fill, BLACK,
|
||||
twos_bit.restore,
|
||||
Transform(brace, braces[0]),
|
||||
Transform(words, word_list[0]),
|
||||
)
|
||||
self.move_disk(
|
||||
0,
|
||||
added_anims = [self.get_increment_animation()],
|
||||
run_time = 2
|
||||
)
|
||||
self.dither()
|
||||
|
||||
##Fourth increment
|
||||
self.play(
|
||||
Transform(brace, braces[2]),
|
||||
Transform(words, word_list[2]),
|
||||
)
|
||||
self.play(self.get_increment_animation())
|
||||
disk = self.disks[2]
|
||||
fours_bit = self.curr_bit_mob[-3]
|
||||
fours_bit.save_state()
|
||||
self.play(
|
||||
disk.set_fill, RED,
|
||||
disk[1].set_fill, BLACK,
|
||||
fours_bit.set_fill, RED
|
||||
)
|
||||
self.move_disk(2, run_time = 2)
|
||||
self.play(
|
||||
disk.set_fill, disk.original_fill_color,
|
||||
disk[1].set_fill, BLACK,
|
||||
fours_bit.restore,
|
||||
FadeOut(brace),
|
||||
FadeOut(words)
|
||||
)
|
||||
self.dither()
|
||||
for disk_index in 0, 1, 0:
|
||||
self.move_disk(
|
||||
disk_index,
|
||||
added_anims = [self.get_increment_animation()]
|
||||
)
|
||||
self.dither()
|
||||
|
||||
##Eighth incremement
|
||||
brace = braces[3]
|
||||
words = word_list[3]
|
||||
self.play(
|
||||
self.get_increment_animation(),
|
||||
GrowFromCenter(brace),
|
||||
Write(words, run_time = 1)
|
||||
)
|
||||
disk = self.disks[3]
|
||||
eights_bit = self.curr_bit_mob[0]
|
||||
eights_bit.save_state()
|
||||
self.play(
|
||||
disk.set_fill, GREEN,
|
||||
disk[1].set_fill, BLACK,
|
||||
eights_bit.set_fill, GREEN
|
||||
)
|
||||
self.move_disk(3, run_time = 2)
|
||||
self.play(
|
||||
disk.set_fill, disk.original_fill_color,
|
||||
disk[1].set_fill, BLACK,
|
||||
eights_bit.restore,
|
||||
)
|
||||
self.play(*map(FadeOut, [brace, words]))
|
||||
for disk_index in get_ruler_sequence(2):
|
||||
self.move_disk(
|
||||
disk_index,
|
||||
stay_on_peg = False,
|
||||
added_anims = [self.get_increment_animation()]
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def initialize_bit_mobs(self):
|
||||
bit_mobs = VGroup(*[
|
||||
get_binary_tex_mob(n, self.num_disks)
|
||||
for n in range(2**(self.num_disks))
|
||||
])
|
||||
bit_mobs.scale(2)
|
||||
for bit_mob in bit_mobs:
|
||||
for bit, disk in zip(bit_mob, reversed(list(self.disks))):
|
||||
bit.highlight(disk.get_color())
|
||||
bit_mobs.next_to(self.peg_labels, DOWN)
|
||||
self.bit_mobs_iter = it.cycle(bit_mobs)
|
||||
self.curr_bit_mob = self.bit_mobs_iter.next()
|
||||
|
||||
self.add(self.curr_bit_mob)
|
||||
|
||||
def get_increment_animation(self):
|
||||
return Succession(
|
||||
Transform(self.curr_bit_mob, self.bit_mobs_iter.next()),
|
||||
Animation(self.curr_bit_mob)
|
||||
)
|
||||
|
||||
class SolveSixDisksByCounting(IntroduceSolveByCounting):
|
||||
CONFIG = {
|
||||
"num_disks" : 6
|
||||
}
|
||||
def construct(self):
|
||||
self.initialize_bit_mobs()
|
||||
for disk_index in get_ruler_sequence(5):
|
||||
self.move_disk(
|
||||
disk_index,
|
||||
stay_on_peg = False,
|
||||
added_anims = [self.get_increment_animation()],
|
||||
)
|
||||
self.dither()
|
||||
|
||||
class RecursionTime(Scene):
|
||||
def construct(self):
|
||||
keith = Keith().shift(2*DOWN+3*LEFT)
|
||||
morty = Mortimer().shift(2*DOWN+3*RIGHT)
|
||||
keith.make_eye_contact(morty)
|
||||
|
||||
keith_kick = keith.copy().change_mode("dance_kick")
|
||||
keith_kick.scale_in_place(1.3)
|
||||
keith_kick.shift(0.5*LEFT)
|
||||
keith_kick.look_at(morty.eyes)
|
||||
keith_hooray = keith.copy().change_mode("hooray")
|
||||
|
||||
self.add(keith, morty)
|
||||
|
||||
bubble = keith.get_bubble("speech", height = 2)
|
||||
bubble.write("Recursion time!!!")
|
||||
VGroup(bubble, bubble.content).shift(UP)
|
||||
|
||||
self.play(
|
||||
Transform(keith, keith_kick),
|
||||
morty.change_mode, "happy",
|
||||
ShowCreation(bubble),
|
||||
Write(bubble.content, run_time = 1)
|
||||
)
|
||||
self.play(
|
||||
morty.change_mode, "hooray",
|
||||
Transform(keith, keith_hooray),
|
||||
bubble.content.gradient_highlight, BLUE_A, BLUE_E
|
||||
)
|
||||
self.play(Blink(morty))
|
||||
self.dither()
|
||||
|
||||
class Eyes(VMobject):
|
||||
CONFIG = {
|
||||
"height" : 0.3,
|
||||
}
|
||||
def __init__(self, mobject, **kwargs):
|
||||
VMobject.__init__(self, **kwargs)
|
||||
self.mobject = mobject
|
||||
self.submobjects = self.get_eyes().submobjects
|
||||
|
||||
def get_eyes(self, mode = "plain", return_pi = False):
|
||||
pi = Randolph(mode = mode)
|
||||
eyes = VGroup(pi.eyes, pi.pupils)
|
||||
eyes.scale_to_fit_height(self.height)
|
||||
if self.submobjects:
|
||||
eyes.move_to(self, DOWN)
|
||||
else:
|
||||
eyes.move_to(self.mobject.get_top(), DOWN)
|
||||
if return_pi:
|
||||
return eyes, pi
|
||||
else:
|
||||
return eyes
|
||||
|
||||
def change_mode_anim(self, mode, **kwargs):
|
||||
return Transform(self, self.get_eyes(mode), **kwargs)
|
||||
|
||||
def blink_anim(self):
|
||||
target = self.copy()
|
||||
bottom_y = self.get_bottom()[1]
|
||||
for submob in target:
|
||||
submob.apply_function(
|
||||
lambda p : [p[0], bottom_y, p[2]]
|
||||
)
|
||||
return Transform(
|
||||
self, target,
|
||||
rate_func = squish_rate_func(there_and_back)
|
||||
)
|
||||
|
||||
def look_at_anim(self, point_or_mobject, **kwargs):
|
||||
target, pi = self.get_eyes(return_pi = True)
|
||||
pi.look_at(point_or_mobject)
|
||||
return Transform(self, target, **kwargs)
|
||||
|
||||
class RecursiveSolution(TowersOfHanoiScene):
|
||||
CONFIG = {
|
||||
"num_disks" : 4
|
||||
}
|
||||
def construct(self):
|
||||
VGroup(*self.get_mobjects()).shift(1.5*DOWN)
|
||||
big_disk = self.disks[-1]
|
||||
self.eyes = Eyes(big_disk)
|
||||
title = TextMobject("Move 4-tower")
|
||||
sub_steps = TextMobject(
|
||||
"Move 3-tower,",
|
||||
"Move disk 3,",
|
||||
"Move 3-tower",
|
||||
)
|
||||
sub_steps[1].highlight(GREEN)
|
||||
sub_step_brace = Brace(sub_steps, UP)
|
||||
sub_sub_steps = TextMobject(
|
||||
"Move 2-tower,",
|
||||
"Move disk 2,",
|
||||
"Move 2-tower",
|
||||
)
|
||||
sub_sub_steps[1].highlight(RED)
|
||||
sub_sub_steps_brace = Brace(sub_sub_steps, UP)
|
||||
steps = VGroup(
|
||||
title, sub_step_brace, sub_steps,
|
||||
sub_sub_steps_brace, sub_sub_steps
|
||||
)
|
||||
steps.arrange_submobjects(DOWN)
|
||||
steps.scale(0.7)
|
||||
steps.to_edge(UP)
|
||||
VGroup(sub_sub_steps_brace, sub_sub_steps).next_to(sub_steps[-1], DOWN)
|
||||
|
||||
self.add(title)
|
||||
|
||||
##Big disk is frustrated
|
||||
self.play(
|
||||
FadeIn(self.eyes),
|
||||
big_disk.set_fill, GREEN,
|
||||
big_disk.label.set_fill, BLACK,
|
||||
)
|
||||
big_disk.add(self.eyes)
|
||||
self.blink()
|
||||
self.dither()
|
||||
self.change_mode("angry")
|
||||
for x in range(2):
|
||||
self.dither()
|
||||
self.shake(big_disk)
|
||||
self.blink()
|
||||
self.dither()
|
||||
self.change_mode("plain")
|
||||
self.look_at(self.peg_labels[2])
|
||||
self.look_at(self.disks[0])
|
||||
self.blink()
|
||||
|
||||
#Subtower move
|
||||
self.move_subtower_to_peg(3, 1, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[1]),
|
||||
FadeIn(sub_step_brace),
|
||||
Write(sub_steps[0], run_time = 1)
|
||||
])
|
||||
self.dither()
|
||||
self.move_disk_to_peg(0, 0, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[0].get_top())
|
||||
])
|
||||
self.shake(big_disk)
|
||||
self.move_disk_to_peg(0, 2, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[2].get_bottom())
|
||||
])
|
||||
self.change_mode("angry")
|
||||
self.move_disk_to_peg(0, 1, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.disks[1].get_top())
|
||||
])
|
||||
self.blink()
|
||||
|
||||
#Final moves for big case
|
||||
self.move_disk(3, run_time = 2, added_anims = [
|
||||
Write(sub_steps[1])
|
||||
])
|
||||
self.look_at(self.disks[1])
|
||||
self.blink()
|
||||
bubble = SpeechBubble()
|
||||
bubble.write("I'm set!")
|
||||
bubble.resize_to_content()
|
||||
bubble.pin_to(big_disk)
|
||||
bubble.add_content(bubble.content)
|
||||
bubble.set_fill(BLACK, opacity = 0.7)
|
||||
self.play(
|
||||
ShowCreation(bubble),
|
||||
Write(bubble.content)
|
||||
)
|
||||
self.dither()
|
||||
self.blink()
|
||||
self.play(*map(FadeOut, [bubble, bubble.content]))
|
||||
big_disk.remove(self.eyes)
|
||||
self.move_subtower_to_peg(3, 2, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[2].get_top()),
|
||||
Write(sub_steps[2])
|
||||
])
|
||||
self.play(FadeOut(self.eyes))
|
||||
self.dither()
|
||||
|
||||
#Highlight subproblem
|
||||
self.play(
|
||||
VGroup(*self.disks[:3]).move_to, self.pegs[1], DOWN
|
||||
)
|
||||
self.disk_tracker = [set([]), set([0, 1, 2]), set([3])]
|
||||
arc = Arc(-5*np.pi/6, start_angle = 5*np.pi/6)
|
||||
arc.add_tip()
|
||||
arc.highlight(YELLOW)
|
||||
arc.scale_to_fit_width(
|
||||
VGroup(*self.pegs[1:]).get_width()*0.8
|
||||
)
|
||||
arc.next_to(self.disks[0], UP+RIGHT, buff = SMALL_BUFF)
|
||||
q_mark = TextMobject("?")
|
||||
q_mark.next_to(arc, UP)
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
Write(q_mark),
|
||||
sub_steps[-1].highlight, YELLOW
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
GrowFromCenter(sub_sub_steps_brace),
|
||||
*map(FadeOut, [arc, q_mark])
|
||||
)
|
||||
|
||||
#Disk 2 frustration
|
||||
big_disk = self.disks[2]
|
||||
self.eyes.move_to(big_disk.get_top(), DOWN)
|
||||
self.play(
|
||||
FadeIn(self.eyes),
|
||||
big_disk.set_fill, RED,
|
||||
big_disk.label.set_fill, BLACK
|
||||
)
|
||||
big_disk.add(self.eyes)
|
||||
self.change_mode("sad")
|
||||
self.look_at(self.pegs[1].get_top())
|
||||
self.shake(big_disk)
|
||||
self.blink()
|
||||
|
||||
#Move sub-sub-tower
|
||||
self.move_subtower_to_peg(2, 0, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[0].get_bottom()),
|
||||
Write(sub_sub_steps[0])
|
||||
])
|
||||
self.blink()
|
||||
self.move_disk_to_peg(2, 2, run_time = 2, added_anims = [
|
||||
Write(sub_sub_steps[1])
|
||||
])
|
||||
self.look_at(self.disks[0])
|
||||
big_disk.remove(self.eyes)
|
||||
self.move_subtower_to_peg(2, 2, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[2].get_top()),
|
||||
Write(sub_sub_steps[2])
|
||||
])
|
||||
self.blink()
|
||||
self.look_at(self.disks[-1])
|
||||
|
||||
#Move eyes
|
||||
self.play(FadeOut(self.eyes))
|
||||
self.eyes.move_to(self.disks[1].get_top(), DOWN)
|
||||
self.play(FadeIn(self.eyes))
|
||||
self.blink()
|
||||
self.play(FadeOut(self.eyes))
|
||||
self.eyes.move_to(self.disks[3].get_top(), DOWN)
|
||||
self.play(FadeIn(self.eyes))
|
||||
|
||||
#Show process one last time
|
||||
big_disk = self.disks[3]
|
||||
big_disk.add(self.eyes)
|
||||
self.move_subtower_to_peg(3, 1, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[0])
|
||||
])
|
||||
self.move_disk_to_peg(3, 0, run_time = 2)
|
||||
big_disk.remove(self.eyes)
|
||||
self.move_subtower_to_peg(3, 0, run_time = 2, added_anims = [
|
||||
self.eyes.look_at_anim(self.pegs[0].get_top())
|
||||
])
|
||||
self.blink()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def shake(self, disk):
|
||||
self.play(disk.shift, 0.2*UP, rate_func = wiggle)
|
||||
|
||||
def blink(self):
|
||||
self.play(self.eyes.blink_anim())
|
||||
|
||||
def look_at(self, point_or_mobject):
|
||||
self.play(self.eyes.look_at_anim(point_or_mobject))
|
||||
|
||||
def change_mode(self, mode):
|
||||
self.play(self.eyes.change_mode_anim(mode))
|
||||
|
||||
class CodeThisUp(Scene):
|
||||
def construct(self):
|
||||
keith = Keith()
|
||||
keith.shift(2*DOWN+3*LEFT)
|
||||
morty = Mortimer()
|
||||
morty.shift(2*DOWN+3*RIGHT)
|
||||
keith.make_eye_contact(morty)
|
||||
point = 2*UP+3*RIGHT
|
||||
bubble = keith.get_bubble("speech", width = 4.5, height = 3)
|
||||
bubble.write("This is the \\\\ most efficient")
|
||||
self.add(morty, keith)
|
||||
|
||||
self.play(
|
||||
keith.change_mode, "speaking",
|
||||
keith.look_at, point
|
||||
)
|
||||
self.play(
|
||||
morty.change_mode, "pondering",
|
||||
morty.look_at, point
|
||||
)
|
||||
self.play(Blink(keith))
|
||||
self.dither(2)
|
||||
self.play(Blink(morty))
|
||||
self.dither()
|
||||
self.play(
|
||||
keith.change_mode, "hooray",
|
||||
keith.look_at, morty.eyes
|
||||
)
|
||||
self.play(Blink(keith))
|
||||
self.dither()
|
||||
self.play(
|
||||
keith.change_mode, "speaking",
|
||||
keith.look_at, morty.eyes,
|
||||
ShowCreation(bubble),
|
||||
Write(bubble.content),
|
||||
morty.change_mode, "happy",
|
||||
morty.look_at, keith.eyes,
|
||||
)
|
||||
self.dither()
|
||||
self.play(Blink(morty))
|
||||
self.dither()
|
||||
|
||||
class HanoiSolutionCode(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
||||
class WhyDoesBinaryAchieveThis(Scene):
|
||||
def construct(self):
|
||||
keith = Keith()
|
||||
keith.shift(2*DOWN+3*LEFT)
|
||||
morty = Mortimer()
|
||||
morty.shift(2*DOWN+3*RIGHT)
|
||||
keith.make_eye_contact(morty)
|
||||
bubble = morty.get_bubble("speech", width = 5, height = 3)
|
||||
bubble.write("""
|
||||
Why does counting
|
||||
in binary work?
|
||||
""")
|
||||
self.add(morty, keith)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -109,6 +109,9 @@ class PiCreature(SVGMobject):
|
|||
bottom_diff = eye.get_bottom()[1] - pupil.get_bottom()[1]
|
||||
if bottom_diff > 0:
|
||||
pupil.shift(bottom_diff*UP)
|
||||
# top_diff = eye.get_top()[1]-pupil.get_top()[1]
|
||||
# if top_diff < 0:
|
||||
# pupil.shift(top_diff*UP)
|
||||
return self
|
||||
|
||||
def look_at(self, point_or_mobject):
|
||||
|
|
Loading…
Add table
Reference in a new issue