mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
1308 lines
41 KiB
Python
1308 lines
41 KiB
Python
![]() |
#!/usr/bin/env python
|
||
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
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 animation.continual_animation 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 topics.probability import *
|
||
|
from topics.complex_numbers import *
|
||
|
from scene import Scene
|
||
|
from scene.reconfigurable_scene import ReconfigurableScene
|
||
|
from scene.zoomed_scene import *
|
||
|
from camera import Camera
|
||
|
from mobject.svg_mobject import *
|
||
|
from mobject.tex_mobject import *
|
||
|
from topics.graph_scene import *
|
||
|
|
||
|
from old_projects.efvgt import ConfettiSpiril
|
||
|
|
||
|
#revert_to_original_skipping_status
|
||
|
|
||
|
|
||
|
class HappyHolidays(TeacherStudentsScene):
|
||
|
def construct(self):
|
||
|
hats = VGroup(*map(
|
||
|
self.get_hat, self.pi_creatures
|
||
|
))
|
||
|
self.add(self.get_snowflakes())
|
||
|
self.change_student_modes(
|
||
|
*["hooray"]*3,
|
||
|
look_at_arg = SPACE_HEIGHT*UP,
|
||
|
added_anims = [self.teacher.change, "hooray"]
|
||
|
)
|
||
|
self.play(LaggedStart(
|
||
|
DrawBorderThenFill, hats
|
||
|
), Animation(self.pi_creatures))
|
||
|
self.change_student_modes(
|
||
|
"happy", "wave_2", "wave_1",
|
||
|
look_at_arg = SPACE_HEIGHT*UP,
|
||
|
)
|
||
|
self.look_at(self.teacher.get_corner(UP+LEFT))
|
||
|
self.dither(2)
|
||
|
self.play(self.teacher.change, "happy")
|
||
|
self.dither(2)
|
||
|
|
||
|
def get_hat(self, pi):
|
||
|
hat = SVGMobject(
|
||
|
file_name = "santa_hat",
|
||
|
height = 0.5*pi.get_height()
|
||
|
)
|
||
|
hat.rotate(-np.pi/12)
|
||
|
vect = RIGHT
|
||
|
if not pi.is_flipped():
|
||
|
hat.flip()
|
||
|
vect = LEFT
|
||
|
hat.set_fill(RED_D)
|
||
|
hat[0].remove(hat[0][1])
|
||
|
hat[0].set_fill("#EEE")
|
||
|
hat[2].set_fill(WHITE)
|
||
|
hat.add(hat[0])
|
||
|
hat.next_to(pi.body, UP, buff = SMALL_BUFF)
|
||
|
hat.shift(SMALL_BUFF*vect)
|
||
|
return hat
|
||
|
|
||
|
def get_snowflakes(self, n_flakes = 50):
|
||
|
snowflakes = VGroup(*[
|
||
|
SVGMobject(
|
||
|
file_name = "snowflake",
|
||
|
height = 0.5,
|
||
|
stroke_width = 0,
|
||
|
fill_opacity = 0.75,
|
||
|
fill_color = WHITE,
|
||
|
).rotate(np.pi/12, RIGHT)
|
||
|
for x in range(n_flakes)
|
||
|
])
|
||
|
def random_confetti_spiral(mob, **kwargs):
|
||
|
return ConfettiSpiril(
|
||
|
mob, x_start = 2*random.random()*SPACE_WIDTH - SPACE_WIDTH,
|
||
|
**kwargs
|
||
|
)
|
||
|
snowflake_spirils = LaggedStart(
|
||
|
random_confetti_spiral, snowflakes,
|
||
|
run_time = 10,
|
||
|
rate_func = lambda x : x,
|
||
|
)
|
||
|
return NormalAnimationAsContinualAnimation(snowflake_spirils)
|
||
|
|
||
|
class UtilitiesPuzzleScene(Scene):
|
||
|
CONFIG = {
|
||
|
"object_height" : 0.75,
|
||
|
"h_distance" : 2,
|
||
|
"v_distance" : 2,
|
||
|
"line_width" : 4,
|
||
|
}
|
||
|
def setup_configuration(self):
|
||
|
houses = VGroup()
|
||
|
for x in range(3):
|
||
|
house = SVGMobject(file_name = "house")
|
||
|
house.scale_to_fit_height(self.object_height)
|
||
|
house.set_fill(LIGHT_GREY)
|
||
|
house.move_to(x*self.h_distance*RIGHT)
|
||
|
houses.add(house)
|
||
|
houses.move_to(self.v_distance*UP/2)
|
||
|
|
||
|
utilities = VGroup(*[
|
||
|
self.get_utility(u, c).move_to(x*self.h_distance*RIGHT)
|
||
|
for x, u, c in zip(
|
||
|
it.count(),
|
||
|
["fire", "electricity", "water"],
|
||
|
[RED_D, YELLOW_C, BLUE_D]
|
||
|
)
|
||
|
])
|
||
|
utilities.move_to(self.v_distance*DOWN/2)
|
||
|
objects = VGroup(houses, utilities)
|
||
|
bounding_box = SurroundingRectangle(
|
||
|
objects,
|
||
|
buff = MED_LARGE_BUFF,
|
||
|
stroke_width = 0
|
||
|
)
|
||
|
objects.add(bounding_box)
|
||
|
self.add_foreground_mobjects(objects)
|
||
|
self.set_variables_as_attrs(
|
||
|
houses, utilities, objects, bounding_box
|
||
|
)
|
||
|
|
||
|
def get_utility(self, name, color):
|
||
|
circle = Circle(
|
||
|
fill_color = color,
|
||
|
fill_opacity = 1,
|
||
|
stroke_width = 0,
|
||
|
)
|
||
|
utility = SVGMobject(
|
||
|
file_name = name,
|
||
|
height = 0.65*circle.get_height(),
|
||
|
fill_color = WHITE,
|
||
|
)
|
||
|
if color == YELLOW:
|
||
|
utility.set_fill(DARK_GREY)
|
||
|
utility.move_to(circle)
|
||
|
circle.add(utility)
|
||
|
circle.scale_to_fit_height(self.object_height)
|
||
|
return circle
|
||
|
|
||
|
def get_line(
|
||
|
self, utility_index, house_index,
|
||
|
*midpoints,
|
||
|
**kwargs
|
||
|
):
|
||
|
prop = kwargs.pop("prop", 1.0)
|
||
|
utility = self.utilities[utility_index]
|
||
|
points = [utility.get_center()]
|
||
|
points += list(midpoints)
|
||
|
points += [self.houses[house_index].get_center()]
|
||
|
line = Line(
|
||
|
points[0], points[-1],
|
||
|
color = utility[0].get_color(),
|
||
|
stroke_width = self.line_width
|
||
|
)
|
||
|
line.set_points_smoothly(points)
|
||
|
line.pointwise_become_partial(line, 0, prop)
|
||
|
return line
|
||
|
|
||
|
def get_almost_solution_lines(self):
|
||
|
bb = self.bounding_box
|
||
|
return VGroup(
|
||
|
VGroup(
|
||
|
self.get_line(0, 0),
|
||
|
self.get_line(0, 1),
|
||
|
self.get_line(0, 2, bb.get_top()),
|
||
|
),
|
||
|
VGroup(
|
||
|
self.get_line(1, 0, bb.get_corner(DOWN+LEFT)),
|
||
|
self.get_line(1, 1),
|
||
|
self.get_line(1, 2, bb.get_corner(DOWN+RIGHT)),
|
||
|
),
|
||
|
VGroup(
|
||
|
self.get_line(2, 0, bb.get_top()),
|
||
|
self.get_line(2, 1),
|
||
|
self.get_line(2, 2),
|
||
|
),
|
||
|
)
|
||
|
|
||
|
def get_straight_lines(self):
|
||
|
return VGroup(*[
|
||
|
VGroup(*[self.get_line(i, j) for j in range(3)])
|
||
|
for i in range(3)
|
||
|
])
|
||
|
|
||
|
def get_no_crossing_words(self):
|
||
|
arrow = Vector(DOWN)
|
||
|
arrow.next_to(self.bounding_box.get_top(), UP, SMALL_BUFF)
|
||
|
words = TextMobject("No crossing!")
|
||
|
words.next_to(arrow, UP, buff = SMALL_BUFF)
|
||
|
result = VGroup(words, arrow)
|
||
|
result.highlight("RED")
|
||
|
return result
|
||
|
|
||
|
def get_region(self, *bounding_edges):
|
||
|
region = VMobject(mark_paths_closed = True)
|
||
|
for i, edge in enumerate(bounding_edges):
|
||
|
new_edge = edge.copy()
|
||
|
if i%2 == 1:
|
||
|
new_edge.points = new_edge.points[::-1]
|
||
|
region.append_vectorized_mobject(new_edge)
|
||
|
region.set_stroke(width = 0)
|
||
|
region.set_fill(WHITE, opacity = 1)
|
||
|
return region
|
||
|
|
||
|
def convert_objects_to_dots(self, run_time = 2):
|
||
|
group = VGroup(*it.chain(self.houses, self.utilities))
|
||
|
for mob in group:
|
||
|
mob.target = Dot(color = mob.get_color())
|
||
|
mob.target.scale(2)
|
||
|
mob.target.move_to(mob)
|
||
|
self.play(LaggedStart(MoveToTarget, group, run_time = run_time))
|
||
|
|
||
|
class AboutToyPuzzles(UtilitiesPuzzleScene, TeacherStudentsScene, ThreeDScene):
|
||
|
def construct(self):
|
||
|
self.remove(self.pi_creatures)
|
||
|
self.lay_out_puzzle()
|
||
|
self.point_to_abstractions()
|
||
|
self.show_this_video()
|
||
|
|
||
|
def lay_out_puzzle(self):
|
||
|
self.setup_configuration()
|
||
|
houses, utilities = self.houses, self.utilities
|
||
|
lines = VGroup(*it.chain(*self.get_almost_solution_lines()))
|
||
|
no_crossing_words = self.get_no_crossing_words()
|
||
|
|
||
|
self.remove(self.objects)
|
||
|
self.play(
|
||
|
ReplacementTransform(
|
||
|
VGroup(houses[1], houses[1]).copy().fade(1),
|
||
|
VGroup(houses[0], houses[2]),
|
||
|
rate_func = squish_rate_func(smooth, 0.35, 1),
|
||
|
run_time = 2,
|
||
|
),
|
||
|
FadeIn(houses[1]),
|
||
|
LaggedStart(DrawBorderThenFill, utilities, run_time = 2)
|
||
|
)
|
||
|
self.play(
|
||
|
LaggedStart(
|
||
|
ShowCreation, lines,
|
||
|
run_time = 3
|
||
|
),
|
||
|
Animation(self.objects)
|
||
|
)
|
||
|
self.play(
|
||
|
Write(no_crossing_words[0]),
|
||
|
GrowArrow(no_crossing_words[1]),
|
||
|
)
|
||
|
self.dither()
|
||
|
|
||
|
self.objects.add_to_back(lines, no_crossing_words)
|
||
|
|
||
|
def point_to_abstractions(self):
|
||
|
objects = self.objects
|
||
|
objects.generate_target()
|
||
|
objects.target.scale(0.5)
|
||
|
objects.target.move_to(
|
||
|
(SPACE_HEIGHT*DOWN + SPACE_WIDTH*LEFT)/2
|
||
|
)
|
||
|
|
||
|
eulers = TexMobject(*"V-E+F=2")
|
||
|
eulers.highlight_by_tex_to_color_map({
|
||
|
"V" : RED,
|
||
|
"E" : GREEN,
|
||
|
"F" : BLUE,
|
||
|
})
|
||
|
eulers.to_edge(UP, buff = 2)
|
||
|
|
||
|
cube = Cube()
|
||
|
cube.set_stroke(WHITE, 2)
|
||
|
cube.scale_to_fit_height(0.75)
|
||
|
cube.pose_at_angle()
|
||
|
cube.next_to(eulers, UP)
|
||
|
|
||
|
tda = TextMobject("Topological \\\\ Data Analysis")
|
||
|
tda.move_to(DOWN + 4*RIGHT)
|
||
|
|
||
|
arrow_from_eulers = Arrow(
|
||
|
eulers.get_bottom(), tda.get_corner(UP+LEFT),
|
||
|
color = WHITE
|
||
|
)
|
||
|
|
||
|
self.play(
|
||
|
objects.scale, 0.5,
|
||
|
objects.move_to, DOWN + 4*LEFT,
|
||
|
)
|
||
|
arrow_to_eulers = Arrow(
|
||
|
self.houses[2].get_corner(UP+LEFT),
|
||
|
eulers.get_bottom(),
|
||
|
color = WHITE
|
||
|
)
|
||
|
self.add(AmbientRotation(cube, axis = UP))
|
||
|
self.play(
|
||
|
GrowArrow(arrow_to_eulers),
|
||
|
Write(eulers),
|
||
|
FadeIn(cube)
|
||
|
)
|
||
|
self.dither(5)
|
||
|
self.play(
|
||
|
GrowArrow(arrow_from_eulers),
|
||
|
Write(tda)
|
||
|
)
|
||
|
self.dither(2)
|
||
|
|
||
|
self.set_variables_as_attrs(
|
||
|
eulers, cube, tda,
|
||
|
arrows = VGroup(arrow_to_eulers, arrow_from_eulers),
|
||
|
)
|
||
|
|
||
|
def show_this_video(self):
|
||
|
screen_rect = FullScreenFadeRectangle(
|
||
|
stroke_color = WHITE,
|
||
|
stroke_width = 2,
|
||
|
fill_opacity = 0,
|
||
|
)
|
||
|
everything = VGroup(
|
||
|
self.objects, self.arrows, self.eulers,
|
||
|
self.cube, self.tda,
|
||
|
screen_rect,
|
||
|
)
|
||
|
|
||
|
self.teacher.save_state()
|
||
|
self.teacher.fade(1)
|
||
|
|
||
|
self.play(
|
||
|
everything.scale, 0.5,
|
||
|
everything.next_to, self.teacher, UP+LEFT,
|
||
|
self.teacher.restore,
|
||
|
self.teacher.change, "raise_right_hand", UP+LEFT,
|
||
|
LaggedStart(FadeIn, self.students)
|
||
|
)
|
||
|
self.change_student_modes(
|
||
|
*["pondering"]*3, look_at_arg = everything
|
||
|
)
|
||
|
self.dither(5)
|
||
|
|
||
|
class ThisPuzzleIsHard(UtilitiesPuzzleScene, PiCreatureScene):
|
||
|
def construct(self):
|
||
|
self.introduce_components()
|
||
|
self.failed_attempts()
|
||
|
self.ask_meta_puzzle()
|
||
|
|
||
|
def introduce_components(self):
|
||
|
randy = self.pi_creature
|
||
|
|
||
|
try_it = TextMobject("Try it yourself!")
|
||
|
try_it.to_edge(UP)
|
||
|
|
||
|
self.setup_configuration()
|
||
|
houses, utilities = self.houses, self.utilities
|
||
|
self.remove(self.objects)
|
||
|
house = houses[0]
|
||
|
|
||
|
puzzle_words = TextMobject("""
|
||
|
Puzzle: Connect each house to \\\\
|
||
|
each utility without crossing lines.
|
||
|
""")
|
||
|
# puzzle_words.next_to(self.objects, UP)
|
||
|
puzzle_words.to_edge(UP)
|
||
|
|
||
|
self.add(try_it)
|
||
|
self.play(Animation(try_it))
|
||
|
self.play(
|
||
|
LaggedStart(DrawBorderThenFill, houses),
|
||
|
LaggedStart(GrowFromCenter, utilities),
|
||
|
try_it.scale_to_fit_width, house.get_width(),
|
||
|
try_it.fade, 1,
|
||
|
try_it.move_to, house,
|
||
|
self.pi_creature.change, "happy",
|
||
|
)
|
||
|
self.play(LaggedStart(FadeIn, puzzle_words))
|
||
|
|
||
|
self.add_foreground_mobjects(self.objects)
|
||
|
self.set_variables_as_attrs(puzzle_words)
|
||
|
|
||
|
def failed_attempts(self):
|
||
|
bb = self.bounding_box
|
||
|
utilities = self.utilities
|
||
|
houses = self.houses
|
||
|
randy = self.pi_creature
|
||
|
|
||
|
line_sets = [
|
||
|
[
|
||
|
self.get_line(0, 0),
|
||
|
self.get_line(1, 1),
|
||
|
self.get_line(2, 2),
|
||
|
self.get_line(0, 1),
|
||
|
self.get_line(2, 1),
|
||
|
self.get_line(0, 2, bb.get_corner(UP+LEFT)),
|
||
|
self.get_line(
|
||
|
2, 0, bb.get_corner(DOWN+LEFT),
|
||
|
prop = 0.85,
|
||
|
),
|
||
|
self.get_line(
|
||
|
2, 0, bb.get_corner(UP+RIGHT), bb.get_top(),
|
||
|
prop = 0.73,
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
self.get_line(0, 0),
|
||
|
self.get_line(1, 1),
|
||
|
self.get_line(2, 2),
|
||
|
self.get_line(0, 1),
|
||
|
self.get_line(1, 2),
|
||
|
self.get_line(
|
||
|
2, 0,
|
||
|
bb.get_bottom(),
|
||
|
bb.get_corner(DOWN+LEFT)
|
||
|
),
|
||
|
self.get_line(0, 2, bb.get_top()),
|
||
|
self.get_line(
|
||
|
2, 1,
|
||
|
utilities[1].get_bottom() + MED_SMALL_BUFF*DOWN,
|
||
|
utilities[1].get_left() + MED_SMALL_BUFF*LEFT,
|
||
|
),
|
||
|
self.get_line(
|
||
|
1, 0, houses[2].get_corner(UP+LEFT) + MED_LARGE_BUFF*LEFT,
|
||
|
prop = 0.49,
|
||
|
),
|
||
|
self.get_line(
|
||
|
1, 2, bb.get_right(),
|
||
|
prop = 0.25,
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
self.get_line(0, 0),
|
||
|
self.get_line(0, 1),
|
||
|
self.get_line(0, 2, bb.get_top()),
|
||
|
self.get_line(1, 0, bb.get_corner(DOWN+LEFT)),
|
||
|
self.get_line(1, 1),
|
||
|
self.get_line(1, 2, bb.get_corner(DOWN+RIGHT)),
|
||
|
self.get_line(2, 1),
|
||
|
self.get_line(2, 2),
|
||
|
self.get_line(2, 0, bb.get_top(), prop = 0.45),
|
||
|
self.get_line(2, 0, prop = 0.45),
|
||
|
],
|
||
|
]
|
||
|
modes = ["confused", "sassy", "angry"]
|
||
|
|
||
|
for line_set, mode in zip(line_sets, modes):
|
||
|
good_lines = VGroup(*line_set[:-2])
|
||
|
bad_lines = line_set[-2:]
|
||
|
self.play(LaggedStart(ShowCreation, good_lines))
|
||
|
for bl in bad_lines:
|
||
|
self.play(
|
||
|
ShowCreation(
|
||
|
bl,
|
||
|
rate_func = bezier([0, 0, 0, 1, 1, 1, 1, 1, 0.3, 1, 1]),
|
||
|
run_time = 1.5
|
||
|
),
|
||
|
randy.change, mode,
|
||
|
)
|
||
|
self.play(ShowCreation(
|
||
|
bl, rate_func = lambda t : smooth(1-t),
|
||
|
))
|
||
|
self.remove(bl)
|
||
|
self.play(LaggedStart(FadeOut, good_lines))
|
||
|
|
||
|
def ask_meta_puzzle(self):
|
||
|
randy = self.pi_creature
|
||
|
group = VGroup(
|
||
|
self.puzzle_words,
|
||
|
self.objects,
|
||
|
)
|
||
|
rect = SurroundingRectangle(group, color = BLUE, buff = MED_LARGE_BUFF)
|
||
|
group.add(rect)
|
||
|
group.generate_target()
|
||
|
group.target.scale(0.75)
|
||
|
group.target.shift(DOWN)
|
||
|
group[-1].set_stroke(width = 0)
|
||
|
|
||
|
meta_puzzle_words = TextMobject("""
|
||
|
Meta-puzzle: Prove that this\\\\
|
||
|
is impossible.
|
||
|
""")
|
||
|
meta_puzzle_words.next_to(group.target, UP)
|
||
|
meta_puzzle_words.highlight(BLUE)
|
||
|
|
||
|
self.play(
|
||
|
MoveToTarget(group),
|
||
|
randy.change, "pondering"
|
||
|
)
|
||
|
self.play(Write(meta_puzzle_words))
|
||
|
self.play(randy.change, "confused")
|
||
|
|
||
|
straight_lines = self.get_straight_lines()
|
||
|
almost_solution_lines = self.get_almost_solution_lines()
|
||
|
self.play(LaggedStart(
|
||
|
ShowCreation, straight_lines,
|
||
|
run_time = 2,
|
||
|
lag_ratio = 0.8
|
||
|
), Blink(randy))
|
||
|
self.play(Transform(
|
||
|
straight_lines, almost_solution_lines,
|
||
|
run_time = 3,
|
||
|
submobject_mode = "lagged_start"
|
||
|
))
|
||
|
self.dither()
|
||
|
|
||
|
######
|
||
|
|
||
|
def create_pi_creature(self):
|
||
|
return Randolph().to_corner(DOWN+LEFT)
|
||
|
|
||
|
class IntroduceGraph(PiCreatureScene):
|
||
|
def construct(self):
|
||
|
pi_creatures = self.pi_creatures
|
||
|
dots = VGroup(*[
|
||
|
Dot(color = pi.get_color()).scale(2).move_to(pi)
|
||
|
for pi in pi_creatures
|
||
|
])
|
||
|
lines = VGroup(*[
|
||
|
Line(pi1.get_center(), pi2.get_center())
|
||
|
for pi1, pi2 in it.combinations(pi_creatures, 2)
|
||
|
])
|
||
|
|
||
|
graph_word = TextMobject("``", "", "Graph", "''", arg_separator = "")
|
||
|
graph_word.to_edge(UP)
|
||
|
planar_graph_word = TextMobject("``", "Planar", " graph", "''", arg_separator = "")
|
||
|
planar_graph_word.move_to(graph_word)
|
||
|
|
||
|
vertices_word = TextMobject("Vertices")
|
||
|
vertices_word.to_edge(RIGHT, buff = LARGE_BUFF)
|
||
|
vertices_word.highlight(YELLOW)
|
||
|
|
||
|
vertex_arrows = VGroup(*[
|
||
|
Arrow(vertices_word.get_left(), dot)
|
||
|
for dot in dots[-2:]
|
||
|
])
|
||
|
|
||
|
edge_word = TextMobject("Edge")
|
||
|
edge_word.next_to(lines, LEFT, LARGE_BUFF)
|
||
|
edge_arrow = Arrow(
|
||
|
edge_word, lines, buff = SMALL_BUFF,
|
||
|
color = WHITE
|
||
|
)
|
||
|
|
||
|
self.play(LaggedStart(GrowFromCenter, pi_creatures))
|
||
|
self.play(
|
||
|
LaggedStart(ShowCreation, lines),
|
||
|
LaggedStart(
|
||
|
ApplyMethod, pi_creatures,
|
||
|
lambda pi : (pi.change, "pondering", lines)
|
||
|
)
|
||
|
)
|
||
|
self.play(Write(graph_word))
|
||
|
self.play(ReplacementTransform(
|
||
|
pi_creatures, dots,
|
||
|
run_time = 2,
|
||
|
submobject_mode = "lagged_start"
|
||
|
))
|
||
|
self.add_foreground_mobjects(dots)
|
||
|
self.play(
|
||
|
FadeIn(vertex_arrows),
|
||
|
FadeIn(vertices_word),
|
||
|
)
|
||
|
self.dither()
|
||
|
self.play(LaggedStart(
|
||
|
ApplyMethod, lines,
|
||
|
lambda l : (l.rotate_in_place, np.pi/12),
|
||
|
rate_func = wiggle
|
||
|
))
|
||
|
self.play(
|
||
|
FadeIn(edge_word),
|
||
|
GrowArrow(edge_arrow),
|
||
|
)
|
||
|
self.dither(2)
|
||
|
line = lines[2]
|
||
|
self.play(
|
||
|
line.set_points_smoothly, [
|
||
|
line.get_start(),
|
||
|
dots.get_left() + MED_SMALL_BUFF*LEFT,
|
||
|
dots.get_corner(DOWN+LEFT) + MED_SMALL_BUFF*(DOWN+LEFT),
|
||
|
dots.get_bottom() + MED_SMALL_BUFF*DOWN,
|
||
|
line.get_end(),
|
||
|
],
|
||
|
VGroup(edge_word, edge_arrow).shift, MED_LARGE_BUFF*LEFT,
|
||
|
)
|
||
|
self.dither()
|
||
|
self.play(ReplacementTransform(graph_word, planar_graph_word))
|
||
|
self.dither(2)
|
||
|
|
||
|
###
|
||
|
|
||
|
def create_pi_creatures(self):
|
||
|
pis = VGroup(
|
||
|
PiCreature(color = BLUE_D),
|
||
|
PiCreature(color = GREY_BROWN),
|
||
|
PiCreature(color = BLUE_C).flip(),
|
||
|
PiCreature(color = BLUE_E).flip(),
|
||
|
)
|
||
|
pis.scale(0.5)
|
||
|
pis.arrange_submobjects_in_grid(buff = 2)
|
||
|
return pis
|
||
|
|
||
|
class IsK33Planar(UtilitiesPuzzleScene):
|
||
|
def construct(self):
|
||
|
self.setup_configuration()
|
||
|
self.objects.shift(MED_LARGE_BUFF*DOWN)
|
||
|
|
||
|
straight_lines = self.get_straight_lines()
|
||
|
almost_solution_lines = self.get_almost_solution_lines()
|
||
|
|
||
|
question = TextMobject("Is", "this graph", "planar?")
|
||
|
question.highlight_by_tex("this graph", YELLOW)
|
||
|
question.to_edge(UP)
|
||
|
brace = Brace(question.get_part_by_tex("graph"), DOWN, buff = SMALL_BUFF)
|
||
|
fancy_name = brace.get_text(
|
||
|
"``Complete bipartite graph $K_{3, 3}$''",
|
||
|
buff = SMALL_BUFF
|
||
|
)
|
||
|
fancy_name.highlight(YELLOW)
|
||
|
|
||
|
self.add(question)
|
||
|
self.convert_objects_to_dots()
|
||
|
self.play(LaggedStart(ShowCreation, straight_lines))
|
||
|
self.play(
|
||
|
GrowFromCenter(brace),
|
||
|
LaggedStart(FadeIn, fancy_name),
|
||
|
)
|
||
|
self.play(ReplacementTransform(
|
||
|
straight_lines, almost_solution_lines,
|
||
|
run_time = 3,
|
||
|
submobject_mode = "lagged_start"
|
||
|
))
|
||
|
self.dither(2)
|
||
|
|
||
|
class TwoKindsOfViewers(PiCreatureScene, UtilitiesPuzzleScene):
|
||
|
def construct(self):
|
||
|
self.setup_configuration()
|
||
|
objects = self.objects
|
||
|
objects.remove(self.bounding_box)
|
||
|
lines = self.get_straight_lines()
|
||
|
objects.add_to_back(lines)
|
||
|
objects.scale(0.75)
|
||
|
objects.next_to(ORIGIN, RIGHT, LARGE_BUFF)
|
||
|
self.remove(objects)
|
||
|
|
||
|
pi1, pi2 = self.pi_creatures
|
||
|
words = TextMobject(
|
||
|
"$(V-E+F)$", "kinds of viewers"
|
||
|
)
|
||
|
words.to_edge(UP)
|
||
|
eulers = words.get_part_by_tex("V-E+F")
|
||
|
eulers.highlight(GREEN)
|
||
|
non_eulers = VGroup(*filter(lambda m : m is not eulers, words))
|
||
|
|
||
|
self.add(words)
|
||
|
self.dither()
|
||
|
self.play(
|
||
|
pi1.shift, 2*LEFT,
|
||
|
pi2.shift, 2*RIGHT,
|
||
|
)
|
||
|
|
||
|
know_eulers = TextMobject("Know about \\\\ Euler's formula")
|
||
|
know_eulers.next_to(pi1, DOWN)
|
||
|
know_eulers.highlight(GREEN)
|
||
|
dont = TextMobject("Don't")
|
||
|
dont.next_to(pi2, DOWN)
|
||
|
dont.highlight(RED)
|
||
|
|
||
|
self.play(
|
||
|
FadeIn(know_eulers),
|
||
|
pi1.change, "hooray",
|
||
|
)
|
||
|
self.play(
|
||
|
FadeIn(dont),
|
||
|
pi2.change, "maybe", eulers,
|
||
|
)
|
||
|
self.dither()
|
||
|
self.pi_creature_thinks(
|
||
|
pi1, "",
|
||
|
bubble_kwargs = {"width" : 3, "height" : 2},
|
||
|
target_mode = "thinking"
|
||
|
)
|
||
|
self.play(pi2.change, "confused", eulers)
|
||
|
self.dither()
|
||
|
|
||
|
### Out of thin air
|
||
|
self.play(*map(FadeOut, [
|
||
|
non_eulers, pi1, pi2, pi1.bubble,
|
||
|
know_eulers, dont
|
||
|
]))
|
||
|
self.play(eulers.next_to, ORIGIN, LEFT, LARGE_BUFF)
|
||
|
arrow = Arrow(eulers, objects, color = WHITE)
|
||
|
self.play(
|
||
|
GrowArrow(arrow),
|
||
|
LaggedStart(DrawBorderThenFill, VGroup(*it.chain(*objects)))
|
||
|
)
|
||
|
self.dither()
|
||
|
self.play(
|
||
|
objects.move_to, eulers, RIGHT,
|
||
|
eulers.move_to, objects, LEFT,
|
||
|
path_arc = np.pi,
|
||
|
run_time = 1.5,
|
||
|
)
|
||
|
self.dither(2)
|
||
|
|
||
|
###
|
||
|
|
||
|
def create_pi_creatures(self):
|
||
|
group = VGroup(Randolph(color = BLUE_C), Randolph())
|
||
|
group.scale(0.7)
|
||
|
group.shift(MED_LARGE_BUFF*DOWN)
|
||
|
return group
|
||
|
|
||
|
class IntroduceRegions(UtilitiesPuzzleScene):
|
||
|
def construct(self):
|
||
|
self.setup_configuration()
|
||
|
houses, utilities = self.houses, self.utilities
|
||
|
objects = self.objects
|
||
|
lines, line_groups, regions = self.get_lines_line_groups_and_regions()
|
||
|
back_region = regions[0]
|
||
|
front_regions = VGroup(*regions[1:])
|
||
|
|
||
|
self.convert_objects_to_dots(run_time = 0)
|
||
|
self.play(LaggedStart(
|
||
|
ShowCreation, lines,
|
||
|
run_time = 3,
|
||
|
))
|
||
|
self.add_foreground_mobjects(lines, objects)
|
||
|
self.dither()
|
||
|
for region in front_regions:
|
||
|
self.play(FadeIn(region))
|
||
|
self.play(
|
||
|
FadeIn(back_region),
|
||
|
Animation(front_regions),
|
||
|
)
|
||
|
self.dither()
|
||
|
self.play(FadeOut(regions))
|
||
|
|
||
|
##Paint bucket
|
||
|
paint_bucket = SVGMobject(
|
||
|
file_name = "paint_bucket",
|
||
|
height = 0.5,
|
||
|
)
|
||
|
paint_bucket.flip()
|
||
|
paint_bucket.move_to(8*LEFT + 5*UP)
|
||
|
|
||
|
def click(region):
|
||
|
self.play(
|
||
|
UpdateFromAlphaFunc(
|
||
|
region,
|
||
|
lambda m, a : m.set_fill(opacity = int(2*a)),
|
||
|
),
|
||
|
ApplyMethod(
|
||
|
paint_bucket.scale_in_place, 0.5,
|
||
|
rate_func = there_and_back,
|
||
|
),
|
||
|
run_time = 0.25,
|
||
|
)
|
||
|
|
||
|
self.play(
|
||
|
paint_bucket.next_to, utilities, DOWN+LEFT, SMALL_BUFF
|
||
|
)
|
||
|
click(regions[1])
|
||
|
self.play(paint_bucket.next_to, utilities[1], UP+RIGHT, SMALL_BUFF)
|
||
|
click(regions[2])
|
||
|
self.play(paint_bucket.next_to, houses[1], RIGHT)
|
||
|
click(regions[3])
|
||
|
self.play(paint_bucket.move_to, 4*LEFT + 2*UP)
|
||
|
self.add_foreground_mobjects(front_regions, lines, objects)
|
||
|
click(back_region)
|
||
|
self.remove_foreground_mobjects(front_regions)
|
||
|
self.dither()
|
||
|
self.play(
|
||
|
FadeOut(back_region),
|
||
|
FadeOut(front_regions[0]),
|
||
|
FadeOut(paint_bucket),
|
||
|
*map(Animation, front_regions[1:])
|
||
|
)
|
||
|
|
||
|
#Line tries to escape
|
||
|
point_sets = [
|
||
|
[
|
||
|
VGroup(*houses[1:]).get_center(),
|
||
|
houses[2].get_top() + MED_SMALL_BUFF*UP,
|
||
|
],
|
||
|
[
|
||
|
houses[1].get_top() + SMALL_BUFF*UP,
|
||
|
utilities[0].get_center(),
|
||
|
],
|
||
|
[VGroup(houses[1], utilities[1]).get_center()],
|
||
|
[
|
||
|
utilities[2].get_center() + 0.75*(DOWN+RIGHT)
|
||
|
],
|
||
|
]
|
||
|
escape_lines = VGroup(*[
|
||
|
Line(LEFT, RIGHT).set_points_smoothly(
|
||
|
[utilities[2].get_center()] + point_set
|
||
|
)
|
||
|
for point_set in point_sets
|
||
|
])
|
||
|
|
||
|
self.dither()
|
||
|
for line in escape_lines:
|
||
|
self.play(ShowCreation(line,
|
||
|
rate_func = lambda t : 0.8*smooth(t)
|
||
|
))
|
||
|
self.play(ShowCreation(line,
|
||
|
rate_func = lambda t : smooth(1 - t)
|
||
|
))
|
||
|
|
||
|
def get_lines_line_groups_and_regions(self):
|
||
|
lines = self.get_almost_solution_lines()
|
||
|
flat_lines = VGroup(*it.chain(*lines))
|
||
|
flat_lines.remove(lines[2][0])
|
||
|
|
||
|
line_groups = [
|
||
|
VGroup(*[lines[i][j] for i, j in ij_set])
|
||
|
for ij_set in [
|
||
|
[(0, 0), (1, 0), (1, 1), (0, 1)],
|
||
|
[(1, 1), (2, 1), (2, 2), (1, 2)],
|
||
|
[(0, 2), (2, 2), (2, 1), (0, 1)],
|
||
|
[(0, 0), (1, 0), (1, 2), (0, 2)],
|
||
|
]
|
||
|
]
|
||
|
regions = VGroup(*[
|
||
|
self.get_region(*line_group)
|
||
|
for line_group in line_groups
|
||
|
])
|
||
|
back_region = FullScreenFadeRectangle(fill_opacity = 1 )
|
||
|
regions.submobjects.pop()
|
||
|
regions.submobjects.insert(0, back_region)
|
||
|
front_regions = VGroup(*regions[1:])
|
||
|
|
||
|
back_region.highlight(BLUE_E)
|
||
|
front_regions.gradient_highlight(GREEN_E, MAROON_E)
|
||
|
|
||
|
return flat_lines, line_groups, regions
|
||
|
|
||
|
class FromLastVideo(Scene):
|
||
|
def construct(self):
|
||
|
title = TextMobject("From last video")
|
||
|
title.to_edge(UP)
|
||
|
rect = ScreenRectangle(height = 6)
|
||
|
rect.next_to(title, DOWN)
|
||
|
|
||
|
self.add(title)
|
||
|
self.play(ShowCreation(rect))
|
||
|
self.dither(2)
|
||
|
|
||
|
class AskAboutRegions(IntroduceRegions):
|
||
|
def construct(self):
|
||
|
self.setup_configuration()
|
||
|
houses, utilities = self.houses, self.utilities
|
||
|
self.convert_objects_to_dots(run_time = 0)
|
||
|
objects = self.objects
|
||
|
lines, line_groups, regions = self.get_lines_line_groups_and_regions()
|
||
|
back_region = regions[0]
|
||
|
front_regions = VGroup(*regions[1:])
|
||
|
missing_lines = VGroup(
|
||
|
self.get_line(2, 0, self.objects.get_top()),
|
||
|
self.get_line(
|
||
|
2, 0,
|
||
|
self.objects.get_bottom() + DOWN,
|
||
|
self.objects.get_corner(DOWN+LEFT) + DOWN+LEFT,
|
||
|
)
|
||
|
)
|
||
|
missing_lines.set_stroke(width = 5)
|
||
|
|
||
|
front_regions.save_state()
|
||
|
front_regions.generate_target()
|
||
|
front_regions.target.scale(0.5)
|
||
|
front_regions.target.arrange_submobjects(RIGHT, buff = LARGE_BUFF)
|
||
|
front_regions.target.to_edge(UP)
|
||
|
|
||
|
self.add(front_regions)
|
||
|
self.add_foreground_mobjects(lines, objects)
|
||
|
self.dither()
|
||
|
self.play(MoveToTarget(front_regions))
|
||
|
self.play(LaggedStart(
|
||
|
ApplyMethod, front_regions,
|
||
|
lambda m : (m.rotate_in_place, np.pi/12),
|
||
|
rate_func = wiggle,
|
||
|
lag_ratio = 0.75,
|
||
|
run_time = 1
|
||
|
))
|
||
|
self.play(front_regions.restore)
|
||
|
self.dither()
|
||
|
|
||
|
#Show missing lines
|
||
|
for line in missing_lines:
|
||
|
self.play(ShowCreation(
|
||
|
line,
|
||
|
rate_func = there_and_back,
|
||
|
run_time = 2,
|
||
|
))
|
||
|
|
||
|
#Count regions
|
||
|
count = TexMobject("1")
|
||
|
count.scale(1.5)
|
||
|
count.to_edge(UP)
|
||
|
self.play(
|
||
|
FadeIn(back_region),
|
||
|
FadeIn(count),
|
||
|
Animation(front_regions)
|
||
|
)
|
||
|
last_region = None
|
||
|
for n, region in zip(it.count(2), front_regions):
|
||
|
new_count = TexMobject(str(n))
|
||
|
new_count.replace(count, dim_to_match = 1)
|
||
|
self.remove(count)
|
||
|
self.add(new_count)
|
||
|
count = new_count
|
||
|
|
||
|
region.save_state()
|
||
|
anims = [ApplyMethod(region.highlight, YELLOW)]
|
||
|
if last_region:
|
||
|
anims.append(ApplyMethod(last_region.restore))
|
||
|
anims.append(Animation(front_regions))
|
||
|
self.play(*anims, run_time = 0.25)
|
||
|
self.dither(0.5)
|
||
|
last_region = region
|
||
|
self.play(last_region.restore)
|
||
|
self.dither()
|
||
|
self.play(FadeOut(count))
|
||
|
|
||
|
#Count edges per region
|
||
|
fade_rect = FullScreenFadeRectangle(opacity = 0.8)
|
||
|
line_group = line_groups[0].copy()
|
||
|
region = front_regions[0].copy()
|
||
|
self.foreground_mobjects = []
|
||
|
def show_lines(line_group):
|
||
|
lg_copy = line_group.copy()
|
||
|
lg_copy.set_stroke(WHITE, 6)
|
||
|
self.play(LaggedStart(
|
||
|
FadeIn, lg_copy,
|
||
|
run_time = 3,
|
||
|
rate_func = there_and_back,
|
||
|
lag_ratio = 0.4,
|
||
|
remover = True,
|
||
|
))
|
||
|
|
||
|
self.play(
|
||
|
FadeIn(fade_rect),
|
||
|
Animation(region),
|
||
|
Animation(line_group),
|
||
|
)
|
||
|
show_lines(line_group)
|
||
|
last_line_group = line_group
|
||
|
last_region = region
|
||
|
for i in range(1, 3):
|
||
|
line_group = line_groups[i].copy()
|
||
|
region = front_regions[i].copy()
|
||
|
self.play(
|
||
|
FadeOut(last_region),
|
||
|
FadeOut(last_line_group),
|
||
|
FadeIn(region),
|
||
|
FadeIn(line_group),
|
||
|
)
|
||
|
show_lines(line_group)
|
||
|
last_line_group = line_group
|
||
|
last_region = region
|
||
|
self.play(
|
||
|
FadeOut(fade_rect),
|
||
|
FadeOut(last_region),
|
||
|
FadeOut(last_line_group),
|
||
|
)
|
||
|
self.dither()
|
||
|
|
||
|
class NewRegionClosedOnlyForNodesWithEdges(UtilitiesPuzzleScene):
|
||
|
def construct(self):
|
||
|
self.setup_configuration()
|
||
|
self.convert_objects_to_dots(run_time = 0)
|
||
|
objects = self.objects
|
||
|
houses, utilities = self.houses, self.utilities
|
||
|
|
||
|
bb = self.bounding_box
|
||
|
lines = VGroup(
|
||
|
self.get_line(2, 1),
|
||
|
self.get_line(0, 1),
|
||
|
self.get_line(0, 2,
|
||
|
bb.get_corner(UP+LEFT),
|
||
|
bb.get_top() + MED_LARGE_BUFF*UP,
|
||
|
),
|
||
|
self.get_line(2, 2),
|
||
|
)
|
||
|
lit_line = lines[2].copy()
|
||
|
lit_line.points = lit_line.points[::-1]
|
||
|
lit_line.set_stroke(WHITE, 5)
|
||
|
|
||
|
region = self.get_region(*lines)
|
||
|
region.set_fill(MAROON_E)
|
||
|
|
||
|
arrow = Vector(DOWN+LEFT, color = WHITE)
|
||
|
arrow.next_to(houses[2], UP+RIGHT, buff = SMALL_BUFF)
|
||
|
words = TextMobject("Already has \\\\ an edge")
|
||
|
words.next_to(arrow.get_start(), UP, SMALL_BUFF)
|
||
|
|
||
|
for line in lines[:-1]:
|
||
|
self.play(ShowCreation(line))
|
||
|
lines[-1].pointwise_become_partial(lines[-1], 0, 0.92)
|
||
|
lines[-1].save_state()
|
||
|
self.dither()
|
||
|
self.play(ShowCreation(lines[-1]))
|
||
|
self.add(region, lines, objects)
|
||
|
self.dither()
|
||
|
self.remove(region)
|
||
|
self.play(ShowCreation(lines[-1],
|
||
|
rate_func = lambda t : smooth(1-2*t*(1-t))
|
||
|
))
|
||
|
self.add(region, lines, objects)
|
||
|
self.dither()
|
||
|
self.remove(region)
|
||
|
self.play(
|
||
|
ShowCreation(lines[-1],
|
||
|
rate_func = lambda t : smooth(1-0.5*t)
|
||
|
),
|
||
|
FadeIn(words),
|
||
|
GrowArrow(arrow),
|
||
|
)
|
||
|
for x in range(2):
|
||
|
self.play(ShowCreationThenDestruction(lit_line))
|
||
|
self.play(lines[-1].restore)
|
||
|
self.add(region, lines, objects)
|
||
|
self.dither(2)
|
||
|
|
||
|
class LightUpNodes(IntroduceRegions):
|
||
|
def construct(self):
|
||
|
self.setup_configuration()
|
||
|
self.setup_regions()
|
||
|
self.setup_counters()
|
||
|
self.describe_one_as_lit()
|
||
|
self.show_rule_for_lighting()
|
||
|
|
||
|
def setup_configuration(self):
|
||
|
IntroduceRegions.setup_configuration(self)
|
||
|
self.convert_objects_to_dots(run_time = 1)
|
||
|
self.objects.shift(DOWN)
|
||
|
|
||
|
def setup_regions(self):
|
||
|
lines, line_groups, regions = self.get_lines_line_groups_and_regions()
|
||
|
back_region = regions[0]
|
||
|
front_regions = VGroup(*regions[1:])
|
||
|
self.set_variables_as_attrs(
|
||
|
lines, line_groups, regions,
|
||
|
back_region, front_regions,
|
||
|
)
|
||
|
|
||
|
def setup_counters(self):
|
||
|
titles = [
|
||
|
TextMobject("\\# Lit vertices"),
|
||
|
TextMobject("\\# Edges"),
|
||
|
TextMobject("\\# Regions"),
|
||
|
]
|
||
|
for title, vect in zip(titles, [LEFT, ORIGIN, RIGHT]):
|
||
|
title.shift(SPACE_WIDTH*vect/2)
|
||
|
title.to_edge(UP)
|
||
|
underline = Line(LEFT, RIGHT)
|
||
|
underline.stretch_to_fit_width(title.get_width())
|
||
|
underline.next_to(title, DOWN, SMALL_BUFF)
|
||
|
title.add(underline)
|
||
|
self.add(title)
|
||
|
self.v_count, self.e_count, self.f_count = self.counts = map(
|
||
|
Integer, [1, 0, 1]
|
||
|
)
|
||
|
for count, title in zip(self.counts, titles):
|
||
|
count.next_to(title, DOWN)
|
||
|
self.add(count)
|
||
|
|
||
|
def describe_one_as_lit(self):
|
||
|
houses, utilities = self.houses, self.utilities
|
||
|
vertices = VGroup(*it.chain(houses, utilities))
|
||
|
dim_arrows = VGroup()
|
||
|
for vertex in vertices:
|
||
|
arrow = Vector(0.5*(DOWN+LEFT), color = WHITE)
|
||
|
arrow.next_to(vertex, UP+RIGHT, SMALL_BUFF)
|
||
|
vertex.arrow = arrow
|
||
|
dim_arrows.add(arrow)
|
||
|
lit_vertex = utilities[0]
|
||
|
lit_arrow = lit_vertex.arrow
|
||
|
lit_arrow.rotate(np.pi/2, about_point = lit_vertex.get_center())
|
||
|
dim_arrows.remove(lit_arrow)
|
||
|
lit_word = TextMobject("Lit up")
|
||
|
lit_word.next_to(lit_arrow.get_start(), UP, SMALL_BUFF)
|
||
|
dim_word = TextMobject("Dim")
|
||
|
dim_word.next_to(dim_arrows[1].get_start(), UP, MED_LARGE_BUFF)
|
||
|
|
||
|
dot = Dot().move_to(self.v_count)
|
||
|
|
||
|
self.play(
|
||
|
vertices.set_fill, None, 0,
|
||
|
vertices.set_stroke, None, 1,
|
||
|
)
|
||
|
self.play(ReplacementTransform(dot, lit_vertex))
|
||
|
self.play(
|
||
|
FadeIn(lit_word),
|
||
|
GrowArrow(lit_arrow)
|
||
|
)
|
||
|
self.play(*self.get_lit_vertex_animations(lit_vertex))
|
||
|
self.play(
|
||
|
FadeIn(dim_word),
|
||
|
LaggedStart(GrowArrow, dim_arrows)
|
||
|
)
|
||
|
self.dither()
|
||
|
self.play(*map(FadeOut, [
|
||
|
lit_word, lit_arrow, dim_word, dim_arrows
|
||
|
]))
|
||
|
|
||
|
def show_rule_for_lighting(self):
|
||
|
lines = self.lines
|
||
|
regions = self.regions
|
||
|
line_groups = self.line_groups
|
||
|
objects = self.objects
|
||
|
houses, utilities = self.houses, self.utilities
|
||
|
|
||
|
#First region, lines 0, 1, 4, 3
|
||
|
lines[4].rotate_in_place(np.pi)
|
||
|
region = regions[1]
|
||
|
|
||
|
self.play(ShowCreation(lines[0]))
|
||
|
self.play(*self.get_count_change_animations(0, 1, 0))
|
||
|
self.play(*it.chain(
|
||
|
self.get_lit_vertex_animations(houses[0]),
|
||
|
self.get_count_change_animations(1, 0, 0)
|
||
|
))
|
||
|
self.dither()
|
||
|
for line, vertex in (lines[1], houses[1]), (lines[4], utilities[1]):
|
||
|
self.play(
|
||
|
ShowCreation(line),
|
||
|
*self.get_count_change_animations(0, 1, 0)
|
||
|
)
|
||
|
self.play(*it.chain(
|
||
|
self.get_lit_vertex_animations(vertex),
|
||
|
self.get_count_change_animations(1, 0, 0),
|
||
|
))
|
||
|
self.dither()
|
||
|
self.play(
|
||
|
ShowCreation(lines[3], run_time = 2),
|
||
|
*self.get_count_change_animations(0, 1, 0)
|
||
|
)
|
||
|
self.add_foreground_mobjects(line_groups[0])
|
||
|
self.add_foreground_mobjects(objects)
|
||
|
self.play(
|
||
|
FadeIn(region),
|
||
|
*self.get_count_change_animations(0, 0, 1)
|
||
|
)
|
||
|
self.dither()
|
||
|
|
||
|
#Next region, lines 2, 7, 8
|
||
|
region = regions[3]
|
||
|
lines[6].rotate_in_place(np.pi)
|
||
|
|
||
|
for line, vertex in (lines[2], houses[2]), (lines[6], utilities[2]):
|
||
|
self.play(ShowCreation(line), *it.chain(
|
||
|
self.get_lit_vertex_animations(
|
||
|
vertex,
|
||
|
run_time = 2,
|
||
|
squish_range = (0.5, 1),
|
||
|
),
|
||
|
self.get_count_change_animations(1, 1, 0)
|
||
|
))
|
||
|
self.dither()
|
||
|
self.play(
|
||
|
ShowCreation(lines[7]),
|
||
|
*self.get_count_change_animations(0, 1, 1)
|
||
|
)
|
||
|
self.add_foreground_mobjects(line_groups[2])
|
||
|
self.add_foreground_mobjects(objects)
|
||
|
self.play(FadeIn(region))
|
||
|
self.dither()
|
||
|
|
||
|
####
|
||
|
|
||
|
def get_count_change_animations(self, *changes):
|
||
|
anims = []
|
||
|
for change, count in zip(changes, self.counts):
|
||
|
if change == 0:
|
||
|
continue
|
||
|
new_count = Integer(count.number + 1)
|
||
|
new_count.move_to(count)
|
||
|
anims.append(Transform(
|
||
|
count, new_count,
|
||
|
run_time = 2,
|
||
|
rate_func = squish_rate_func(smooth, 0.5, 1)
|
||
|
))
|
||
|
count.number += 1
|
||
|
anims.append(self.get_plus_one_anim(count))
|
||
|
|
||
|
return anims
|
||
|
|
||
|
def get_plus_one_anim(self, count):
|
||
|
plus_one = TexMobject("+1")
|
||
|
plus_one.highlight(YELLOW)
|
||
|
plus_one.move_to(count)
|
||
|
plus_one.next_to(count, DOWN)
|
||
|
plus_one.generate_target()
|
||
|
plus_one.target.move_to(count)
|
||
|
plus_one.target.set_fill(opacity = 0)
|
||
|
move = MoveToTarget(plus_one, remover = True)
|
||
|
grow = GrowFromCenter(plus_one)
|
||
|
return UpdateFromAlphaFunc(
|
||
|
plus_one,
|
||
|
lambda m, a : (
|
||
|
(grow if a < 0.5 else move).update(2*a%1)
|
||
|
),
|
||
|
remover = True,
|
||
|
rate_func = double_smooth,
|
||
|
run_time = 2
|
||
|
)
|
||
|
|
||
|
def get_lit_vertex_animations(self, vertex, run_time = 1, squish_range = (0, 1)):
|
||
|
line = Line(
|
||
|
LEFT, RIGHT,
|
||
|
stroke_width = 0,
|
||
|
stroke_color = BLACK,
|
||
|
)
|
||
|
line.scale_to_fit_width(0.5*vertex.get_width())
|
||
|
line.next_to(ORIGIN, buff = 0.75*vertex.get_width())
|
||
|
lines = VGroup(*[
|
||
|
line.copy().rotate(angle)
|
||
|
for angle in np.arange(0, 2*np.pi, np.pi/4)
|
||
|
])
|
||
|
lines.move_to(vertex)
|
||
|
random.shuffle(lines.submobjects)
|
||
|
return [
|
||
|
LaggedStart(
|
||
|
ApplyMethod, lines,
|
||
|
lambda l : (l.set_stroke, YELLOW, 4),
|
||
|
rate_func = squish_rate_func(there_and_back, *squish_range),
|
||
|
lag_ratio = 0.75,
|
||
|
remover = True,
|
||
|
run_time = run_time
|
||
|
),
|
||
|
ApplyMethod(
|
||
|
vertex.set_fill, None, 1,
|
||
|
run_time = run_time,
|
||
|
rate_func = squish_rate_func(smooth, *squish_range)
|
||
|
),
|
||
|
]
|
||
|
|
||
|
class ShowRule(TeacherStudentsScene):
|
||
|
def construct(self):
|
||
|
new_edge = TextMobject("New edge")
|
||
|
new_vertex = TextMobject("New (lit) vertex")
|
||
|
new_vertex.next_to(new_edge, UP+RIGHT, MED_LARGE_BUFF)
|
||
|
new_region = TextMobject("New region")
|
||
|
new_region.next_to(new_edge, DOWN+RIGHT, MED_LARGE_BUFF)
|
||
|
VGroup(new_vertex, new_region).shift(MED_LARGE_BUFF*RIGHT)
|
||
|
arrows = VGroup(*[
|
||
|
Arrow(
|
||
|
new_edge.get_right(), mob.get_left(),
|
||
|
color = WHITE,
|
||
|
buff = SMALL_BUFF
|
||
|
)
|
||
|
for mob in new_vertex, new_region
|
||
|
])
|
||
|
new_vertex.highlight(YELLOW)
|
||
|
new_edge.highlight(BLUE)
|
||
|
new_region.highlight(RED)
|
||
|
rule = VGroup(new_edge, arrows, new_vertex, new_region)
|
||
|
rule.center().to_edge(UP)
|
||
|
|
||
|
self.add(rule)
|
||
|
|
||
|
class ConcludeFiveRegions(LightUpNodes):
|
||
|
def construct(self):
|
||
|
self.setup_configuration()
|
||
|
self.setup_regions()
|
||
|
self.setup_counters()
|
||
|
self.show_new_region_creation()
|
||
|
self.show_rule()
|
||
|
self.show_nine_lines_to_start()
|
||
|
self.conclude_about_five_regions()
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|