mirror of
https://github.com/3b1b/manim.git
synced 2025-07-29 13:03:31 +08:00
Initial hanoi animations
This commit is contained in:
@ -253,18 +253,43 @@ class Succession(Animation):
|
||||
else:
|
||||
run_time = sum([anim.run_time for anim in animations])
|
||||
self.num_anims = len(animations)
|
||||
self.anims = animations
|
||||
mobject = animations[0].mobject
|
||||
self.anims = (animations)
|
||||
mobject = Mobject(*[anim.mobject for anim in self.anims])
|
||||
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(self, alpha):
|
||||
def update_mobject(self, alpha):
|
||||
scaled_alpha = alpha*self.num_anims
|
||||
self.mobject = self.anims
|
||||
for index in range(len(self.anims)):
|
||||
self.anims[index].update(scaled_alpha - index)
|
||||
index = min(int(scaled_alpha), len(self.anims)-1)
|
||||
curr_anim = self.anims[index]
|
||||
if index != self.last_index:
|
||||
last_anim = self.anims[self.last_index]
|
||||
last_anim.clean_up()
|
||||
if last_anim.mobject is curr_anim.mobject:
|
||||
self.anims[index].starting_mobject = curr_anim.mobject.copy()
|
||||
self.anims[index].update_mobject(scaled_alpha - index)
|
||||
self.last_index = index
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -2661,18 +2661,6 @@ class Thumbnail(CircleScene):
|
||||
|
||||
self.add(title, arrow, deriv_eq, int_eq)
|
||||
|
||||
# dR = 0.25
|
||||
# rings = VGroup(*[
|
||||
# self.get_ring(rad, 0.9*dR)
|
||||
# for rad in np.arange(0, self.radius, dR)
|
||||
# ])
|
||||
# for ring in rings:
|
||||
# ring.add(ring.copy().rotate(np.pi))
|
||||
# rings.gradient_highlight(BLUE, GREEN_E)
|
||||
# rings.next_to(title, DOWN)
|
||||
|
||||
# self.add(title, rings)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ HELP_MESSAGE = """
|
||||
-l use low quality
|
||||
-m use medium quality
|
||||
-a run and save every scene in the script, or all args for the given scene
|
||||
-q don't pring progress
|
||||
-q don't print progress
|
||||
"""
|
||||
SCENE_NOT_FOUND_MESSAGE = """
|
||||
That scene is not in the script
|
||||
|
667
hanoi.py
Normal file
667
hanoi.py
Normal file
@ -0,0 +1,667 @@
|
||||
from helpers import *
|
||||
|
||||
from mobject.tex_mobject import TexMobject
|
||||
from mobject import Mobject
|
||||
from mobject.image_mobject import ImageMobject
|
||||
from mobject.vectorized_mobject import *
|
||||
|
||||
from animation.animation import Animation
|
||||
from animation.transform import *
|
||||
from animation.simple_animations import *
|
||||
from animation.playground import *
|
||||
from topics.geometry import *
|
||||
from topics.characters import *
|
||||
from topics.functions import *
|
||||
from topics.fractals import *
|
||||
from topics.number_line import *
|
||||
from topics.combinatorics import *
|
||||
from topics.numerals import *
|
||||
from topics.three_dimensions import *
|
||||
from topics.objects import *
|
||||
from scene import Scene
|
||||
from camera import Camera
|
||||
from mobject.svg_mobject import *
|
||||
from mobject.tex_mobject import *
|
||||
|
||||
class CountingScene(Scene):
|
||||
CONFIG = {
|
||||
"base" : 10,
|
||||
"power_colors" : [YELLOW, MAROON_B, RED, GREEN, BLUE, PURPLE_D],
|
||||
"counting_dot_starting_position" : (SPACE_WIDTH-1)*RIGHT + (SPACE_HEIGHT-1)*UP,
|
||||
"count_dot_starting_radius" : 0.5,
|
||||
"dot_configuration_height" : 2,
|
||||
"ones_configuration_location" : UP+2*RIGHT,
|
||||
"num_scale_factor" : 2,
|
||||
"num_start_location" : 2*DOWN,
|
||||
}
|
||||
def setup(self):
|
||||
self.dots = VGroup()
|
||||
self.number = 0
|
||||
self.number_mob = VGroup(TexMobject(str(self.number)))
|
||||
self.number_mob.scale(self.num_scale_factor)
|
||||
self.number_mob.shift(self.num_start_location)
|
||||
|
||||
self.initialize_configurations()
|
||||
|
||||
self.arrows = VGroup()
|
||||
|
||||
self.add(self.number_mob)
|
||||
|
||||
def get_template_configuration(self):
|
||||
#This should probably be replaced for non-base-10 counting scenes
|
||||
down_right = (0.5)*RIGHT + (np.sqrt(3)/2)*DOWN
|
||||
result = []
|
||||
for down_right_steps in range(5):
|
||||
for left_steps in range(down_right_steps):
|
||||
result.append(
|
||||
down_right_steps*down_right + left_steps*LEFT
|
||||
)
|
||||
return reversed(result[:self.base])
|
||||
|
||||
def get_dot_template(self):
|
||||
#This should be replaced for non-base-10 counting scenes
|
||||
down_right = (0.5)*RIGHT + (np.sqrt(3)/2)*DOWN
|
||||
dots = VGroup(*[
|
||||
Dot(
|
||||
point,
|
||||
radius = 0.25,
|
||||
fill_opacity = 0,
|
||||
stroke_width = 2,
|
||||
stroke_color = WHITE,
|
||||
)
|
||||
for point in self.get_template_configuration()
|
||||
])
|
||||
dots.scale_to_fit_height(self.dot_configuration_height)
|
||||
return dots
|
||||
|
||||
def initialize_configurations(self):
|
||||
self.dot_templates = []
|
||||
self.dot_template_iterators = []
|
||||
self.curr_configurations = []
|
||||
|
||||
def add_configuration(self):
|
||||
new_template = self.get_dot_template()
|
||||
new_template.move_to(self.ones_configuration_location)
|
||||
left_vect = (new_template.get_width()+LARGE_BUFF)*LEFT
|
||||
new_template.shift(
|
||||
left_vect*len(self.dot_templates)
|
||||
)
|
||||
self.dot_templates.append(new_template)
|
||||
self.dot_template_iterators.append(
|
||||
it.cycle(new_template)
|
||||
)
|
||||
self.curr_configurations.append(VGroup())
|
||||
|
||||
def count(self, max_val, run_time_per_anim = 1):
|
||||
for x in range(max_val):
|
||||
self.increment(run_time_per_anim)
|
||||
|
||||
def increment(self, run_time_per_anim = 1):
|
||||
moving_dot = Dot(
|
||||
self.counting_dot_starting_position,
|
||||
radius = self.count_dot_starting_radius,
|
||||
color = self.power_colors[0],
|
||||
)
|
||||
moving_dot.generate_target()
|
||||
moving_dot.set_fill(opacity = 0)
|
||||
kwargs = {
|
||||
"run_time" : run_time_per_anim
|
||||
}
|
||||
|
||||
continue_rolling_over = True
|
||||
first_move = True
|
||||
place = 0
|
||||
while continue_rolling_over:
|
||||
added_anims = []
|
||||
if first_move:
|
||||
added_anims += self.get_digit_increment_animations()
|
||||
first_move = False
|
||||
moving_dot.target.replace(
|
||||
self.dot_template_iterators[place].next()
|
||||
)
|
||||
self.play(MoveToTarget(moving_dot), *added_anims, **kwargs)
|
||||
self.curr_configurations[place].add(moving_dot)
|
||||
|
||||
|
||||
if len(self.curr_configurations[place].split()) == self.base:
|
||||
full_configuration = self.curr_configurations[place]
|
||||
self.curr_configurations[place] = VGroup()
|
||||
place += 1
|
||||
center = full_configuration.get_center_of_mass()
|
||||
radius = 0.6*max(
|
||||
full_configuration.get_width(),
|
||||
full_configuration.get_height(),
|
||||
)
|
||||
circle = Circle(
|
||||
radius = radius,
|
||||
stroke_width = 0,
|
||||
fill_color = self.power_colors[place],
|
||||
fill_opacity = 0.5,
|
||||
)
|
||||
circle.move_to(center)
|
||||
moving_dot = VGroup(circle, full_configuration)
|
||||
moving_dot.generate_target()
|
||||
moving_dot[0].set_fill(opacity = 0)
|
||||
else:
|
||||
continue_rolling_over = False
|
||||
|
||||
def get_digit_increment_animations(self):
|
||||
result = []
|
||||
self.number += 1
|
||||
new_number_mob = self.get_number_mob(self.number)
|
||||
new_number_mob.move_to(self.number_mob, RIGHT)
|
||||
if self.is_perfect_power():
|
||||
self.add_configuration()
|
||||
place = len(new_number_mob.split())-1
|
||||
result.append(FadeIn(self.dot_templates[place]))
|
||||
arrow = Arrow(
|
||||
new_number_mob[place].get_top(),
|
||||
self.dot_templates[place].get_bottom(),
|
||||
color = self.power_colors[place]
|
||||
)
|
||||
self.arrows.add(arrow)
|
||||
result.append(ShowCreation(arrow))
|
||||
result.append(Transform(
|
||||
self.number_mob, new_number_mob,
|
||||
submobject_mode = "lagged_start"
|
||||
))
|
||||
return result
|
||||
|
||||
def get_number_mob(self, num):
|
||||
result = VGroup()
|
||||
place = 0
|
||||
while num > 0:
|
||||
digit = TexMobject(str(num % self.base))
|
||||
if place >= len(self.power_colors):
|
||||
self.power_colors += self.power_colors
|
||||
digit.highlight(self.power_colors[place])
|
||||
digit.scale(self.num_scale_factor)
|
||||
digit.next_to(result, LEFT, buff = SMALL_BUFF, aligned_edge = DOWN)
|
||||
result.add(digit)
|
||||
num /= self.base
|
||||
place += 1
|
||||
return result
|
||||
|
||||
def is_perfect_power(self):
|
||||
number = self.number
|
||||
while number > 1:
|
||||
if number%self.base != 0:
|
||||
return False
|
||||
number /= self.base
|
||||
return True
|
||||
|
||||
|
||||
class CountInDecimal(CountingScene):
|
||||
def construct(self):
|
||||
for x in range(11):
|
||||
self.increment()
|
||||
for x in range(85):
|
||||
self.increment(0.25)
|
||||
for x in range(20):
|
||||
self.increment()
|
||||
|
||||
class CountInTernary(CountingScene):
|
||||
CONFIG = {
|
||||
"base" : 3,
|
||||
"dot_configuration_height" : 1,
|
||||
"ones_configuration_location" : UP+4*RIGHT
|
||||
}
|
||||
def construct(self):
|
||||
self.count(27)
|
||||
|
||||
# def get_template_configuration(self):
|
||||
# return [ORIGIN, UP]
|
||||
|
||||
class CountInBinaryTo256(CountingScene):
|
||||
CONFIG = {
|
||||
"base" : 2,
|
||||
"dot_configuration_height" : 1,
|
||||
"ones_configuration_location" : UP+5*RIGHT
|
||||
}
|
||||
def construct(self):
|
||||
self.count(128, 0.3)
|
||||
|
||||
def get_template_configuration(self):
|
||||
return [ORIGIN, UP]
|
||||
|
||||
|
||||
class TowersOfHanoiScene(Scene):
|
||||
CONFIG = {
|
||||
"disk_start_and_end_colors" : [BLUE_E, BLUE_A],
|
||||
"num_disks" : 5,
|
||||
"peg_width" : 0.25,
|
||||
"peg_height" : 2.5,
|
||||
"disk_height" : 0.4,
|
||||
"disk_min_width" : 1,
|
||||
"disk_max_width" : 3,
|
||||
}
|
||||
def setup(self):
|
||||
self.add_pegs()
|
||||
self.add_disks()
|
||||
|
||||
def add_pegs(self):
|
||||
peg = Rectangle(
|
||||
height = self.peg_height,
|
||||
width = self.peg_width,
|
||||
stroke_width = 0,
|
||||
fill_color = GREY_BROWN,
|
||||
fill_opacity = 1,
|
||||
)
|
||||
peg.shift(UP)
|
||||
self.pegs = VGroup(*[
|
||||
peg.copy().shift(vect)
|
||||
for vect in 4*LEFT, ORIGIN, 4*RIGHT
|
||||
])
|
||||
self.peg_labels = VGroup(*[
|
||||
TexMobject(char).next_to(peg, DOWN)
|
||||
for char, peg in zip("ABC", self.pegs)
|
||||
])
|
||||
self.add(self.pegs, self.peg_labels)
|
||||
|
||||
def add_disks(self):
|
||||
self.disks = VGroup(*[
|
||||
Rectangle(
|
||||
height = self.disk_height,
|
||||
width = width,
|
||||
fill_color = color,
|
||||
fill_opacity = 1,
|
||||
stroke_width = 0,
|
||||
)
|
||||
for width, color in zip(
|
||||
np.linspace(
|
||||
self.disk_min_width,
|
||||
self.disk_max_width,
|
||||
self.num_disks
|
||||
),
|
||||
color_gradient(
|
||||
self.disk_start_and_end_colors,
|
||||
self.num_disks
|
||||
)
|
||||
)
|
||||
])
|
||||
for number, disk in enumerate(self.disks):
|
||||
label = TexMobject(str(number))
|
||||
label.highlight(BLACK)
|
||||
label.scale_to_fit_height(self.disk_height/2)
|
||||
label.move_to(disk)
|
||||
disk.add(label)
|
||||
disk.label = label
|
||||
self.reset_disks(run_time = 0)
|
||||
|
||||
self.add(self.disks)
|
||||
|
||||
def reset_disks(self, **kwargs):
|
||||
self.disks.generate_target()
|
||||
self.disks.target.arrange_submobjects(DOWN, buff = 0)
|
||||
self.disks.target.move_to(self.pegs[0], DOWN)
|
||||
self.play(
|
||||
MoveToTarget(self.disks),
|
||||
**kwargs
|
||||
)
|
||||
self.disk_tracker = [
|
||||
set(range(self.num_disks)),
|
||||
set([]),
|
||||
set([])
|
||||
]
|
||||
|
||||
def disk_index_to_peg_index(self, disk_index):
|
||||
for index, disk_set in enumerate(self.disk_tracker):
|
||||
if disk_index in disk_set:
|
||||
return index
|
||||
raise Exception("Somehow this disk wasn't accounted for...")
|
||||
|
||||
def min_disk_index_on_peg(self, peg_index):
|
||||
disk_index_set = self.disk_tracker[peg_index]
|
||||
if disk_index_set:
|
||||
return min(self.disk_tracker[peg_index])
|
||||
else:
|
||||
return self.num_disks
|
||||
|
||||
def bottom_point_for_next_disk(self, peg_index):
|
||||
min_disk_index = self.min_disk_index_on_peg(peg_index)
|
||||
if min_disk_index >= self.num_disks:
|
||||
return self.pegs[peg_index].get_bottom()
|
||||
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
|
||||
|
||||
def get_available_peg(self, disk_index):
|
||||
if disk_index == 0:
|
||||
return self.get_next_disk_0_peg()
|
||||
for index in range(len(list(self.pegs))):
|
||||
if self.min_disk_index_on_peg(index) > disk_index:
|
||||
return index
|
||||
raise Exception("Tower's of Honoi rule broken: No available disks")
|
||||
|
||||
def move_disk(self, disk_index, **kwargs):
|
||||
next_peg_index = self.get_available_peg(disk_index)
|
||||
self.move_disks_to_peg([disk_index], next_peg_index, **kwargs)
|
||||
|
||||
def move_subtower_to_peg(self, num_disks, next_peg_index, **kwargs):
|
||||
disk_indices = range(num_disks)
|
||||
peg_indices = map(self.disk_index_to_peg_index, disk_indices)
|
||||
if len(set(peg_indices)) != 1:
|
||||
warnings.warn("These disks don't make up a tower right now")
|
||||
self.move_disks_to_peg(disk_indices, next_peg_index, **kwargs)
|
||||
|
||||
def move_disks_to_peg(self, disk_indices, next_peg_index, run_time = 1, stay_on_peg = True, added_anims = []):
|
||||
disks = VGroup(*[self.disks[index] for index in disk_indices])
|
||||
max_disk_index = max(disk_indices)
|
||||
next_peg = self.pegs[next_peg_index]
|
||||
curr_peg_index = self.disk_index_to_peg_index(max_disk_index)
|
||||
curr_peg = self.pegs[curr_peg_index]
|
||||
if self.min_disk_index_on_peg(curr_peg_index) != max_disk_index:
|
||||
warnings.warn("Tower's of Hanoi rule broken: disk has crap on top of it")
|
||||
target_bottom_point = self.bottom_point_for_next_disk(next_peg_index)
|
||||
path_arc = np.sign(curr_peg_index-next_peg_index)*np.pi/3
|
||||
if stay_on_peg:
|
||||
self.play(
|
||||
Succession(
|
||||
ApplyMethod(disks.next_to, curr_peg, UP, 0),
|
||||
ApplyMethod(disks.next_to, next_peg, UP, 0, path_arc = path_arc),
|
||||
ApplyMethod(disks.move_to, target_bottom_point, DOWN),
|
||||
),
|
||||
*added_anims,
|
||||
run_time = run_time,
|
||||
rate_func = lambda t : smooth(t, 2)
|
||||
)
|
||||
else:
|
||||
self.play(
|
||||
ApplyMethod(disks.move_to, target_bottom_point, DOWN),
|
||||
*added_anims,
|
||||
path_arc = path_arc*2,
|
||||
run_time = run_time,
|
||||
rate_func = lambda t : smooth(t, 2)
|
||||
)
|
||||
for disk_index in disk_indices:
|
||||
self.disk_tracker[curr_peg_index].remove(disk_index)
|
||||
self.disk_tracker[next_peg_index].add(disk_index)
|
||||
|
||||
class ConstrainedTowersOfHanoiScene(TowersOfHanoiScene):
|
||||
def get_next_disk_0_peg(self):
|
||||
if not hasattr(self, "total_disk_0_movements"):
|
||||
self.total_disk_0_movements = 0
|
||||
curr_peg_index = self.disk_index_to_peg_index(0)
|
||||
if (self.total_disk_0_movements/2)%2 == 0:
|
||||
result = curr_peg_index + 1
|
||||
else:
|
||||
result = curr_peg_index - 1
|
||||
self.total_disk_0_movements += 1
|
||||
return result
|
||||
|
||||
def get_ruler_sequence(order = 4):
|
||||
if order == -1:
|
||||
return []
|
||||
else:
|
||||
smaller = get_ruler_sequence(order - 1)
|
||||
return smaller + [order] + smaller
|
||||
|
||||
def get_ternary_ruler_sequence(order = 4):
|
||||
if order == -1:
|
||||
return []
|
||||
else:
|
||||
smaller = get_ternary_ruler_sequence(order-1)
|
||||
return smaller+[order]+smaller+[order]+smaller
|
||||
|
||||
class SolveHanoi(TowersOfHanoiScene):
|
||||
def construct(self):
|
||||
self.dither()
|
||||
for x in get_ruler_sequence(self.num_disks-1):
|
||||
self.move_disk(x, stay_on_peg = False)
|
||||
self.dither()
|
||||
|
||||
class SolveConstrainedHanoi(ConstrainedTowersOfHanoiScene):
|
||||
def construct(self):
|
||||
self.dither()
|
||||
for x in get_ternary_ruler_sequence(self.num_disks-1):
|
||||
self.move_disk(x, run_time = 0.5, stay_on_peg = False)
|
||||
self.dither()
|
||||
|
||||
class Keith(PiCreature):
|
||||
CONFIG = {
|
||||
"color" : GREEN_D
|
||||
}
|
||||
|
||||
|
||||
####################
|
||||
|
||||
class IntroduceKeith(Scene):
|
||||
def construct(self):
|
||||
morty = Mortimer(mode = "happy")
|
||||
keith = Keith(mode = "dance_kick")
|
||||
keith_image = ImageMobject("keith_schwarz", invert = False)
|
||||
# keith_image = Rectangle()
|
||||
keith_image.scale_to_fit_height(2*SPACE_HEIGHT - 2)
|
||||
keith_image.next_to(ORIGIN, LEFT)
|
||||
keith.move_to(keith_image, DOWN+RIGHT)
|
||||
morty.next_to(keith, buff = LARGE_BUFF, aligned_edge = DOWN)
|
||||
morty.make_eye_contact(keith)
|
||||
|
||||
bubble = keith.get_bubble("speech")
|
||||
bubble.write("Check this out...")
|
||||
bubble.resize_to_content()
|
||||
bubble.pin_to(keith)
|
||||
VGroup(bubble, bubble.content).shift(DOWN)
|
||||
|
||||
title = TextMobject("Keith Schwarz (Computer scientist)")
|
||||
title.to_edge(UP)
|
||||
|
||||
self.add(keith_image, morty)
|
||||
self.play(Write(title))
|
||||
self.play(FadeIn(keith, run_time = 2))
|
||||
self.play(FadeOut(keith_image), Animation(keith))
|
||||
self.play(Blink(morty))
|
||||
self.play(
|
||||
keith.change_mode, "speaking",
|
||||
keith.scale_to_fit_height, morty.get_height(),
|
||||
keith.next_to, morty, LEFT, LARGE_BUFF,
|
||||
run_time = 1.5
|
||||
)
|
||||
self.play(
|
||||
ShowCreation(bubble),
|
||||
Write(bubble.content)
|
||||
)
|
||||
self.play(
|
||||
morty.change_mode, "pondering",
|
||||
morty.look_at, bubble
|
||||
)
|
||||
self.play(Blink(keith))
|
||||
self.dither()
|
||||
|
||||
class IntroduceTowersOfHanoi(TowersOfHanoiScene):
|
||||
def construct(self):
|
||||
self.clear()
|
||||
self.add_title()
|
||||
self.show_setup()
|
||||
self.note_disk_labels()
|
||||
self.show_more_disk_possibility()
|
||||
self.move_full_tower()
|
||||
self.move_single_disk()
|
||||
self.cannot_move_disk_with_crap_on_top()
|
||||
self.cannot_move_disk_onto_smaller_disk()
|
||||
|
||||
def add_title(self):
|
||||
title = TextMobject("Towers of Hanoi")
|
||||
title.to_edge(UP)
|
||||
self.add(title)
|
||||
self.title = title
|
||||
|
||||
def show_setup(self):
|
||||
self.pegs.save_state()
|
||||
bottom = self.pegs.get_bottom()
|
||||
self.pegs.stretch_to_fit_height(0)
|
||||
self.pegs.move_to(bottom)
|
||||
self.play(
|
||||
ApplyMethod(
|
||||
self.pegs.restore,
|
||||
submobject_mode = "lagged_start",
|
||||
run_time = 2
|
||||
),
|
||||
)
|
||||
self.play(Write(self.peg_labels))
|
||||
self.dither()
|
||||
self.bring_in_disks()
|
||||
self.dither()
|
||||
|
||||
def bring_in_disks(self):
|
||||
peg = self.pegs[0]
|
||||
disk_groups = VGroup()
|
||||
for disk in self.disks:
|
||||
top = Circle(radius = disk.get_width()/2)
|
||||
inner = Circle(radius = self.peg_width/2)
|
||||
inner.flip()
|
||||
top.add_subpath(inner.points)
|
||||
top.set_stroke(width = 0)
|
||||
top.set_fill(disk.get_color())
|
||||
top.rotate(np.pi/2, RIGHT)
|
||||
top.move_to(disk, UP)
|
||||
group = VGroup(disk, top)
|
||||
group.original_location = group.get_center()
|
||||
group.next_to(peg, UP, 0)
|
||||
group.save_state()
|
||||
group.rotate_in_place(-np.pi/2, RIGHT)
|
||||
disk.set_fill(opacity = 0)
|
||||
disk_groups.add(group)
|
||||
disk_groups.arrange_submobjects()
|
||||
disk_groups.next_to(self.peg_labels, DOWN)
|
||||
|
||||
self.play(FadeIn(
|
||||
disk_groups,
|
||||
run_time = 2,
|
||||
submobject_mode = "lagged_start"
|
||||
))
|
||||
for group in reversed(list(disk_groups)):
|
||||
self.play(group.restore)
|
||||
self.play(group.move_to, group.original_location)
|
||||
self.remove(disk_groups)
|
||||
self.add(self.disks)
|
||||
|
||||
def note_disk_labels(self):
|
||||
labels = [disk.label for disk in self.disks]
|
||||
last = VGroup().save_state()
|
||||
for label in labels:
|
||||
label.save_state()
|
||||
self.play(
|
||||
label.scale_in_place, 2,
|
||||
label.highlight, YELLOW,
|
||||
last.restore,
|
||||
run_time = 0.5
|
||||
)
|
||||
last = label
|
||||
self.play(last.restore)
|
||||
self.dither()
|
||||
|
||||
def show_more_disk_possibility(self):
|
||||
original_num_disks = self.num_disks
|
||||
original_disk_height = self.disk_height
|
||||
original_disks = self.disks
|
||||
original_disks_copy = original_disks.copy()
|
||||
|
||||
#Hacky
|
||||
self.num_disks = 10
|
||||
self.disk_height = 0.3
|
||||
self.add_disks()
|
||||
new_disks = self.disks
|
||||
self.disks = original_disks
|
||||
self.remove(new_disks)
|
||||
|
||||
self.play(Transform(self.disks, new_disks))
|
||||
self.dither()
|
||||
self.play(Transform(self.disks, original_disks_copy))
|
||||
|
||||
self.remove(self.disks)
|
||||
self.disks = original_disks_copy
|
||||
self.add(self.disks)
|
||||
|
||||
self.num_disks = original_num_disks
|
||||
self.disk_height = original_disk_height
|
||||
|
||||
def move_full_tower(self):
|
||||
self.move_subtower_to_peg(self.num_disks, 1)
|
||||
self.dither()
|
||||
self.reset_disks(run_time = 1, submobject_mode = "lagged_start")
|
||||
self.dither()
|
||||
|
||||
def move_single_disk(self):
|
||||
for x in 0, 1, 0:
|
||||
self.move_disk(x)
|
||||
self.dither()
|
||||
|
||||
def cannot_move_disk_with_crap_on_top(self):
|
||||
not_allowed = TextMobject("Not allowed")
|
||||
not_allowed.to_edge(UP)
|
||||
not_allowed.highlight(RED)
|
||||
cross = TexMobject("\\times")
|
||||
cross.set_fill(RED, opacity = 0.5)
|
||||
|
||||
disk = self.disks[3]
|
||||
disk.save_state()
|
||||
self.move_disks_to_peg([3], 1, added_anims = [
|
||||
Transform(self.title, not_allowed)
|
||||
])
|
||||
cross.replace(disk, stretch = False)
|
||||
self.play(FadeIn(cross))
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(cross),
|
||||
disk.restore
|
||||
)
|
||||
|
||||
def cannot_move_disk_onto_smaller_disk(self):
|
||||
also_not_allowed = TextMobject("Also not allowed")
|
||||
also_not_allowed.to_edge(UP)
|
||||
also_not_allowed.highlight(RED)
|
||||
cross = TexMobject("\\times")
|
||||
cross.set_fill(RED, opacity = 0.5)
|
||||
|
||||
disk = self.disks[2]
|
||||
disk.save_state()
|
||||
self.move_disks_to_peg([2], 2, added_anims = [
|
||||
Transform(self.title, also_not_allowed)
|
||||
])
|
||||
cross.replace(disk)
|
||||
self.play(FadeIn(cross))
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(cross),
|
||||
FadeOut(self.title),
|
||||
disk.restore
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -13,9 +13,7 @@ from helpers import *
|
||||
class Mobject(object):
|
||||
"""
|
||||
Mathematical Object
|
||||
|
||||
"""
|
||||
#Number of numbers used to describe a point (3 for pos, 3 for normal vector)
|
||||
CONFIG = {
|
||||
"color" : WHITE,
|
||||
"stroke_width" : DEFAULT_POINT_THICKNESS,
|
||||
@ -371,6 +369,9 @@ class Mobject(object):
|
||||
##
|
||||
|
||||
def save_state(self):
|
||||
if hasattr(self, "saved_state"):
|
||||
#Prevent exponential growth of data
|
||||
self.saved_state = None
|
||||
self.saved_state = self.copy()
|
||||
return self
|
||||
|
||||
|
@ -18,14 +18,15 @@ class TkSceneRoot(Tkinter.Tk):
|
||||
self.frame.pack()
|
||||
self.canvas = Tkinter.Canvas(self.frame, **kwargs)
|
||||
self.canvas.configure(background='black')
|
||||
self.canvas.place(x=0,y=0)
|
||||
self.canvas.place(x=0, y=0)
|
||||
|
||||
last_time = time.time()
|
||||
for frame in it.cycle(scene.frames):
|
||||
try:
|
||||
self.show_new_image(frame)
|
||||
except:
|
||||
break
|
||||
# try:
|
||||
# self.show_new_image(frame)
|
||||
# except:
|
||||
# break
|
||||
self.show_new_image(frame)
|
||||
sleep_time = scene.frame_duration
|
||||
sleep_time -= time.time() - last_time
|
||||
time.sleep(max(0, sleep_time))
|
||||
@ -34,7 +35,6 @@ class TkSceneRoot(Tkinter.Tk):
|
||||
|
||||
def show_new_image(self, frame):
|
||||
image = Image.fromarray(frame).convert('RGB')
|
||||
image.resize(self.frame.size())
|
||||
photo = ImageTk.PhotoImage(image)
|
||||
self.canvas.delete(Tkinter.ALL)
|
||||
self.canvas.create_image(
|
||||
|
Reference in New Issue
Block a user