3b1b-manim/eoc/chapter1.py

831 lines
24 KiB
Python
Raw Normal View History

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
2016-11-11 11:18:41 -08:00
from scene.zoomed_scene import ZoomedScene
from camera import Camera
from mobject.svg_mobject import *
from mobject.tex_mobject import *
2016-11-11 14:42:40 -08:00
from eoc.graph_scene import GraphScene
2017-03-29 16:06:10 -07:00
from topics.common_scenes import OpeningQuote, PatreonThanks
2016-11-09 15:45:36 -08:00
class CircleScene(PiCreatureScene):
CONFIG = {
"radius" : 1.5,
"stroke_color" : WHITE,
"fill_color" : BLUE_E,
2017-03-31 16:58:18 -07:00
"fill_opacity" : 0.75,
"radial_line_color" : MAROON_B,
"outer_ring_color" : GREEN_E,
2017-03-31 16:58:18 -07:00
"ring_colors" : [BLUE, GREEN],
"dR" : 0.1,
2016-11-09 15:45:36 -08:00
"dR_color" : YELLOW,
"unwrapped_tip" : ORIGIN,
2016-11-09 15:45:36 -08:00
"include_pi_creature" : False,
2017-03-31 16:58:18 -07:00
"circle_corner" : UP+LEFT,
}
def setup(self):
2017-03-31 16:58:18 -07:00
PiCreatureScene.setup(self)
self.circle = Circle(
radius = self.radius,
stroke_color = self.stroke_color,
fill_color = self.fill_color,
fill_opacity = self.fill_opacity,
)
2017-01-25 16:40:59 -08:00
self.circle.to_corner(self.circle_corner, buff = MED_LARGE_BUFF)
self.radius_line = Line(
self.circle.get_center(),
self.circle.get_right(),
color = self.radial_line_color
)
self.radius_brace = Brace(self.radius_line, buff = SMALL_BUFF)
2016-11-09 15:45:36 -08:00
self.radius_label = self.radius_brace.get_text("$R$", buff = SMALL_BUFF)
2017-03-31 16:58:18 -07:00
self.radius_group = VGroup(
self.radius_line, self.radius_brace, self.radius_label
)
2017-03-31 16:58:18 -07:00
self.add(self.circle, *self.radius_group)
2017-03-31 16:58:18 -07:00
if not self.include_pi_creature:
self.remove(self.get_primary_pi_creature())
2016-11-09 15:45:36 -08:00
def introduce_circle(self, added_anims = []):
self.remove(self.circle)
self.play(
ShowCreation(self.radius_line),
GrowFromCenter(self.radius_brace),
Write(self.radius_label),
)
self.circle.set_fill(opacity = 0)
self.play(
Rotate(
self.radius_line, 2*np.pi-0.001,
about_point = self.circle.get_center(),
),
ShowCreation(self.circle),
2016-11-09 15:45:36 -08:00
*added_anims,
run_time = 2
)
self.play(
self.circle.set_fill, self.fill_color, self.fill_opacity,
Animation(self.radius_line),
Animation(self.radius_brace),
Animation(self.radius_label),
)
def increase_radius(self, numerical_dr = True, run_time = 2):
radius_mobs = VGroup(
self.radius_line, self.radius_brace, self.radius_label
)
nudge_line = Line(
self.radius_line.get_right(),
self.radius_line.get_right() + self.dR*RIGHT,
2016-11-11 11:18:41 -08:00
color = self.dR_color
)
nudge_arrow = Arrow(
nudge_line.get_center() + 0.5*RIGHT+DOWN,
nudge_line.get_center(),
color = YELLOW,
2016-11-09 15:45:36 -08:00
buff = SMALL_BUFF,
tip_length = 0.2,
)
if numerical_dr:
nudge_label = TexMobject("%.01f"%self.dR)
else:
nudge_label = TexMobject("dr")
2016-11-09 15:45:36 -08:00
nudge_label.highlight(self.dR_color)
nudge_label.scale(0.75)
nudge_label.next_to(nudge_arrow.get_start(), DOWN)
radius_mobs.add(nudge_line, nudge_arrow, nudge_label)
outer_ring = self.get_outer_ring()
self.play(
2016-11-09 15:45:36 -08:00
FadeIn(outer_ring),
ShowCreation(nudge_line),
ShowCreation(nudge_arrow),
2016-11-09 15:45:36 -08:00
Write(nudge_label),
2016-11-11 11:18:41 -08:00
run_time = run_time/2.
)
2016-11-11 11:18:41 -08:00
self.dither(run_time/2.)
2016-11-09 15:45:36 -08:00
self.nudge_line = nudge_line
self.nudge_arrow = nudge_arrow
self.nudge_label = nudge_label
2016-11-11 11:18:41 -08:00
self.outer_ring = outer_ring
return outer_ring
def get_ring(self, radius, dR, color = GREEN):
ring = Circle(radius = radius + dR).center()
inner_ring = Circle(radius = radius)
inner_ring.rotate(np.pi, RIGHT)
ring.append_vectorized_mobject(inner_ring)
ring.set_stroke(width = 0)
ring.set_fill(color)
ring.move_to(self.circle)
ring.R = radius
ring.dR = dR
return ring
2017-03-31 16:58:18 -07:00
def get_rings(self, **kwargs):
dR = kwargs.get("dR", self.dR)
colors = kwargs.get("colors", self.ring_colors)
radii = np.arange(0, self.radius, dR)
colors = color_gradient(colors, len(radii))
rings = VGroup(*[
self.get_ring(radius, dR = dR, color = color)
for radius, color in zip(radii, colors)
])
return rings
def get_outer_ring(self):
return self.get_ring(
radius = self.radius, dR = self.dR,
color = self.outer_ring_color
)
2016-11-09 15:45:36 -08:00
def unwrap_ring(self, ring, **kwargs):
self.unwrap_rings(ring, **kwargs)
2016-11-09 15:45:36 -08:00
def unwrap_rings(self, *rings, **kwargs):
added_anims = kwargs.get("added_anims", [])
rings = VGroup(*rings)
unwrapped = VGroup(*[
2016-11-11 11:18:41 -08:00
self.get_unwrapped(ring, **kwargs)
for ring in rings
])
self.play(
rings.rotate, np.pi/2,
rings.next_to, unwrapped.get_bottom(), UP,
run_time = 2,
path_arc = np.pi/2
)
2016-11-09 15:45:36 -08:00
self.play(
Transform(rings, unwrapped, run_time = 3),
*added_anims
)
2016-11-11 11:18:41 -08:00
def get_unwrapped(self, ring, to_edge = LEFT, **kwargs):
R = ring.R
R_plus_dr = ring.R + ring.dR
n_anchors = ring.get_num_anchor_points()
result = VMobject()
result.set_points_as_corners([
interpolate(np.pi*R_plus_dr*LEFT, np.pi*R_plus_dr*RIGHT, a)
for a in np.linspace(0, 1, n_anchors/2)
]+[
2016-11-11 11:18:41 -08:00
interpolate(np.pi*R*RIGHT+ring.dR*UP, np.pi*R*LEFT+ring.dR*UP, a)
for a in np.linspace(0, 1, n_anchors/2)
])
result.set_style_data(
stroke_color = ring.get_stroke_color(),
stroke_width = ring.get_stroke_width(),
fill_color = ring.get_fill_color(),
fill_opacity = ring.get_fill_opacity(),
)
result.move_to(self.unwrapped_tip, aligned_edge = DOWN)
result.shift(R_plus_dr*DOWN)
2017-03-31 16:58:18 -07:00
if to_edge is not None:
result.to_edge(to_edge)
return result
2017-03-31 16:58:18 -07:00
def create_pi_creature(self):
self.pi_creature = Randolph(color = BLUE_C)
self.pi_creature.to_corner(DOWN+LEFT)
return self.pi_creature
2017-03-29 16:06:10 -07:00
#############
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
#revert_to_original_skipping_status
2017-03-29 16:06:10 -07:00
class Chapter1OpeningQuote(OpeningQuote):
2016-11-11 11:18:41 -08:00
CONFIG = {
2017-03-29 16:06:10 -07:00
"quote" : [
"""The art of doing mathematics is finding
that """, "special case",
"""that contains all the
germs of generality."""
],
"quote_arg_separator" : " ",
"highlighted_quote_terms" : {
"special case" : BLUE
},
"author" : "David Hilbert",
2016-11-11 11:18:41 -08:00
}
2017-03-31 16:58:18 -07:00
class Introduction(TeacherStudentsScene):
def construct(self):
self.show_series()
self.show_many_facts()
self.invent_calculus()
def show_series(self):
series = VideoSeries()
series.to_edge(UP)
this_video = series[0]
this_video.highlight(YELLOW)
this_video.save_state()
this_video.set_fill(opacity = 0)
this_video.center()
this_video.scale_to_fit_height(2*SPACE_HEIGHT)
self.this_video = this_video
words = TextMobject(
"Welcome to \\\\",
"Essence of calculus"
)
words.highlight_by_tex("Essence of calculus", YELLOW)
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
self.teacher.change_mode("happy")
self.play(
FadeIn(
series,
submobject_mode = "lagged_start",
run_time = 2
),
Blink(self.get_teacher())
)
self.teacher_says(words, target_mode = "hooray")
self.change_student_modes(
*["hooray"]*3,
look_at_arg = series[1].get_left(),
added_anims = [
ApplyMethod(this_video.restore, run_time = 3),
]
)
self.play(
ApplyWave(series, direction = DOWN, run_time = 2),
Animation(self.teacher.bubble),
Animation(self.teacher.bubble.content),
)
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
essence_words = words.get_part_by_tex("Essence").copy()
self.play(
FadeOut(self.teacher.bubble),
FadeOut(self.teacher.bubble.content),
essence_words.next_to, series, DOWN,
*[
ApplyMethod(pi.change_mode, "pondering")
for pi in self.get_pi_creatures()
]
)
self.dither(3)
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
self.series = series
self.essence_words = essence_words
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
def show_many_facts(self):
rules = list(it.starmap(TexMobject, [
("{d(", "x", "^2)", "\\over \\,", "dx}", "=", "2", "x"),
(
"d(", "f", "g", ")", "=",
"f", "dg", "+", "g", "df",
),
(
"F(x)", "=", "\\int_0^x",
"\\frac{dF}{dg}(t)\\,", "dt"
),
(
"f(x)", "=", "\\sum_{n = 0}^\\infty",
"f^{(n)}(a)", "\\frac{(x-a)^n}{n!}"
),
]))
video_indices = [2, 3, 7, 10]
tex_to_color = [
("x", BLUE),
("f", BLUE),
("df", BLUE),
("g", YELLOW),
("dg", YELLOW),
("f(x)", BLUE),
( "f^{(n)}(a)", BLUE),
]
for rule in rules:
for tex, color in tex_to_color:
rule.highlight_by_tex(tex, color, substring = False)
rule.next_to(self.teacher.get_corner(UP+LEFT), UP)
rule.shift_onto_screen()
student_index = 1
student = self.get_students()[student_index]
self.change_student_modes(
"pondering", "sassy", "pondering",
look_at_arg = self.teacher.eyes,
added_anims = [
self.teacher.change_mode, "plain"
]
)
self.dither(2)
self.play(
Write(rules[0]),
self.teacher.change_mode, "raise_right_hand",
)
self.dither()
alt_rules_list = list(rules[1:]) + [VectorizedPoint(self.teacher.eyes.get_top())]
for last_rule, rule, video_index in zip(rules, alt_rules_list, video_indices):
video = self.series[video_index]
self.play(
last_rule.replace, video,
FadeIn(rule),
)
self.play(Animation(rule))
self.dither()
self.play(
self.teacher.change_mode, "happy",
self.teacher.look_at, student.eyes
)
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
def invent_calculus(self):
student = self.get_students()[1]
creatures = self.get_pi_creatures()
creatures.remove(student)
creature_copies = creatures.copy()
self.remove(creatures)
self.add(creature_copies)
calculus = VGroup(*self.essence_words[-len("calculus"):])
calculus.generate_target()
invent = TextMobject("Invent")
invent_calculus = VGroup(invent, calculus.target)
invent_calculus.arrange_submobjects(RIGHT, buff = MED_SMALL_BUFF)
invent_calculus.next_to(student, UP, 1.5*LARGE_BUFF)
invent_calculus.shift(RIGHT)
arrow = Arrow(invent_calculus, student)
fader = Rectangle(
width = 2*SPACE_WIDTH,
height = 2*SPACE_HEIGHT,
stroke_width = 0,
fill_color = BLACK,
fill_opacity = 0.5,
)
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
self.play(
FadeIn(fader),
Animation(student),
Animation(calculus)
)
self.play(
Write(invent),
MoveToTarget(calculus),
student.change_mode, "erm",
student.look_at, calculus
)
self.play(ShowCreation(arrow))
self.dither(2)
class PreviewFrame(Scene):
def construct(self):
frame = Rectangle(height = 9, width = 16, color = WHITE)
frame.scale_to_fit_height(1.5*SPACE_HEIGHT)
colors = iter(color_gradient([BLUE, YELLOW], 3))
titles = [
TextMobject("Chapter %d:"%d, s).to_edge(UP).highlight(colors.next())
for d, s in [
(3, "Derivative formulas through geometry"),
(4, "Chain rule, product rule, etc."),
(7, "Limits"),
]
]
title = titles[0]
frame.next_to(title, DOWN)
self.add(frame, title)
self.dither(3)
for next_title in titles[1:]:
self.play(Transform(title, next_title))
self.dither(3)
class ProductRuleDiagram(Scene):
def construct(self):
df = 0.4
dg = 0.2
rect_kwargs = {
"stroke_width" : 0,
"fill_color" : BLUE,
"fill_opacity" : 0.6,
}
rect = Rectangle(width = 4, height = 3, **rect_kwargs)
rect.shift(DOWN)
df_rect = Rectangle(
height = rect.get_height(),
width = df,
**rect_kwargs
)
dg_rect = Rectangle(
height = dg,
width = rect.get_width(),
**rect_kwargs
)
corner_rect = Rectangle(
height = dg,
width = df,
**rect_kwargs
)
d_rects = VGroup(df_rect, dg_rect, corner_rect)
for d_rect, direction in zip(d_rects, [RIGHT, DOWN, RIGHT+DOWN]):
d_rect.next_to(rect, direction, buff = 0)
d_rect.set_fill(YELLOW, 0.75)
corner_pairs = [
(DOWN+RIGHT, UP+RIGHT),
(DOWN+RIGHT, DOWN+LEFT),
(DOWN+RIGHT, DOWN+RIGHT),
]
for d_rect, corner_pair in zip(d_rects, corner_pairs):
line = Line(*[
rect.get_corner(corner)
for corner in corner_pair
])
d_rect.line = d_rect.copy().replace(line, stretch = True)
d_rect.line.highlight(d_rect.get_color())
f_brace = Brace(rect, UP)
g_brace = Brace(rect, LEFT)
df_brace = Brace(df_rect, UP)
dg_brace = Brace(dg_rect, LEFT)
f_label = f_brace.get_text("$f$")
g_label = g_brace.get_text("$g$")
df_label = df_brace.get_text("$df$")
dg_label = dg_brace.get_text("$dg$")
VGroup(f_label, df_label).highlight(GREEN)
VGroup(g_label, dg_label).highlight(RED)
f_label.generate_target()
g_label.generate_target()
fg_group = VGroup(f_label.target, g_label.target)
fg_group.generate_target()
fg_group.target.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
fg_group.target.move_to(rect.get_center())
for mob in df_brace, df_label, dg_brace, dg_label:
mob.save_state()
mob.scale(0.01, about_point = rect.get_corner(
mob.get_center() - rect.get_center()
))
self.add(rect)
self.play(
GrowFromCenter(f_brace),
GrowFromCenter(g_brace),
Write(f_label),
Write(g_label),
)
self.play(MoveToTarget(fg_group))
self.play(*[
mob.restore
for mob in df_brace, df_label, dg_brace, dg_label
] + [
ReplacementTransform(d_rect.line, d_rect)
for d_rect in d_rects
])
self.dither()
self.play(
d_rects.space_out_submobjects, 1.2,
MaintainPositionRelativeTo(
VGroup(df_brace, df_label),
df_rect
),
MaintainPositionRelativeTo(
VGroup(dg_brace, dg_label),
dg_rect
),
)
self.dither()
2016-11-11 11:18:41 -08:00
2017-03-31 16:58:18 -07:00
deriv = TexMobject(
"d(", "fg", ")", "=",
"f", "\\cdot", "dg", "+", "g", "\\cdot", "df"
)
deriv.to_edge(UP)
alpha_iter = iter(np.linspace(0, 0.5, 5))
self.play(*[
ApplyMethod(
mob.copy().move_to,
deriv.get_part_by_tex(tex, substring = False),
rate_func = squish_rate_func(smooth, alpha, alpha+0.5)
)
for mob, tex in [
(fg_group, "fg"),
(f_label, "f"),
(dg_label, "dg"),
(g_label, "g"),
(df_label, "df"),
]
for alpha in [alpha_iter.next()]
]+[
Write(VGroup(*it.chain(*[
deriv.get_parts_by_tex(tex, substring = False)
for tex in "d(", ")", "=", "\\cdot", "+"
])))
], run_time = 3)
self.dither()
class IntroduceCircle(CircleScene):
CONFIG = {
"include_pi_creature" : True,
"unwrapped_tip" : 2*RIGHT
}
def construct(self):
self.force_skipping()
self.introduce_area()
self.question_area()
self.show_calculus_symbols()
def introduce_area(self):
area = TexMobject("\\text{Area}", "=", "\\pi", "R", "^2")
area.next_to(self.pi_creature.get_corner(UP+RIGHT), UP+RIGHT)
self.remove(self.circle, self.radius_group)
self.play(
self.pi_creature.change_mode, "pondering",
self.pi_creature.look_at, self.circle
)
self.introduce_circle()
self.dither()
R_copy = self.radius_label.copy()
self.play(
self.pi_creature.change_mode, "raise_right_hand",
self.pi_creature.look_at, area,
Transform(R_copy, area.get_part_by_tex("R"))
)
self.play(Write(area))
self.remove(R_copy)
self.dither()
self.area = area
def question_area(self):
q_marks = TexMobject("???")
q_marks.next_to(self.pi_creature, UP)
rings = VGroup(*reversed(self.get_rings()))
unwrapped_rings = VGroup(*[
self.get_unwrapped(ring, to_edge = None)
for ring in rings
])
unwrapped_rings.arrange_submobjects(UP, buff = SMALL_BUFF)
unwrapped_rings.move_to(self.unwrapped_tip, UP)
ring_anim_kwargs = {
"run_time" : 3,
"submobject_mode" : "lagged_start"
}
self.play(
Animation(self.area),
Write(q_marks),
self.pi_creature.change_mode, "confused",
self.pi_creature.look_at, self.area,
)
self.dither()
self.play(
FadeIn(rings, **ring_anim_kwargs),
Animation(self.radius_group),
FadeOut(q_marks),
self.pi_creature.change_mode, "thinking"
)
self.dither()
self.play(
rings.rotate, np.pi/2,
rings.move_to, unwrapped_rings.get_top(),
Animation(self.radius_group),
path_arc = np.pi/2,
**ring_anim_kwargs
)
self.play(
Transform(rings, unwrapped_rings, **ring_anim_kwargs),
)
self.dither()
def show_calculus_symbols(self):
ftc = TexMobject(
"\\int_0^R", "\\frac{dA}{dr}", "\\,dr",
"=", "A(R)"
)
ftc.shift(2*UP)
self.play(
ReplacementTransform(
self.area.get_part_by_tex("R").copy(),
ftc.get_part_by_tex("int")
),
self.pi_creature.change_mode, "plain"
)
self.dither()
self.play(
ReplacementTransform(
self.area.get_part_by_tex("Area").copy(),
ftc.get_part_by_tex("frac")
),
ReplacementTransform(
self.area.get_part_by_tex("R").copy(),
ftc.get_part_by_tex("\\,dr")
)
)
self.dither()
self.play(Write(VGroup(*ftc[-2:])))
self.dither(2)
class ApproximateOneRing(CircleScene):
CONFIG = {
"num_lines" : 24,
"ring_index_proportion" : 0.75,
"ring_shift_val" : 6*RIGHT,
"ring_colors" : [BLUE, GREEN_E],
}
def construct(self):
self.force_skipping()
self.write_radius_three()
self.try_to_understand_area()
self.slice_into_rings()
self.isolate_one_ring()
self.straighten_ring_out()
self.ask_about_precise_shape()
self.approximate_as_rectangle()
def write_radius_three(self):
three = TexMobject("3")
three.move_to(self.radius_label)
self.look_at(self.circle)
self.play(Transform(
self.radius_label, three,
path_arc = np.pi
))
self.dither()
def try_to_understand_area(self):
line_sets = [
VGroup(*[
Line(
self.circle.point_from_proportion(alpha),
self.circle.point_from_proportion(func(alpha)),
)
for alpha in np.linspace(0, 1, self.num_lines)
])
for func in [
lambda alpha : 1-alpha,
lambda alpha : (0.5-alpha)%1,
lambda alpha : (alpha + 0.4)%1,
lambda alpha : (alpha + 0.5)%1,
]
]
for lines in line_sets:
lines.set_stroke(BLACK, 2)
lines = line_sets[0]
self.play(
ShowCreation(
lines,
run_time = 2,
submobject_mode = "lagged_start"
),
Animation(self.radius_group),
self.pi_creature.change_mode, "maybe"
)
self.dither(2)
for new_lines in line_sets[1:]:
self.play(
Transform(lines, new_lines),
Animation(self.radius_group)
)
self.dither()
self.dither()
self.play(FadeOut(lines), Animation(self.radius_group))
def slice_into_rings(self):
rings = self.get_rings()
rings.set_stroke(BLACK, 1)
self.play(
FadeIn(
rings,
submobject_mode = "lagged_start",
run_time = 3
),
Animation(self.radius_group),
self.pi_creature.change_mode, "pondering",
self.pi_creature.look_at, self.circle
)
self.dither(2)
group = VGroup(self.circle, rings, self.radius_group)
for x in range(2):
self.play(
ApplyMethod(
group.rotate_in_place, np.pi,
path_arc = np.pi,
rate_func = there_and_back,
),
self.pi_creature.change_mode, "happy"
)
self.dither(2)
self.rings = rings
def isolate_one_ring(self):
index = int(self.ring_index_proportion*len(self.rings))
ring = self.rings[index]
radius = Line(ORIGIN, ring.R*RIGHT, color = WHITE)
radius.rotate(np.pi/4)
r_label = TexMobject("r")
r_label.next_to(radius.get_center(), UP+LEFT, SMALL_BUFF)
area_q = TextMobject("Area?")
area_q.highlight(YELLOW)
self.revert_to_original_skipping_status()
self.play(ring.shift, self.ring_shift_val)
VGroup(radius, r_label).shift(ring.get_center())
area_q.next_to(ring, DOWN)
self.play(ShowCreation(radius))
self.play(Write(r_label))
self.dither()
self.play(Write(area_q))
self.dither()
self.play(
Indicate(
self.rings,
scale_factor = 1.01,
submobject_mode = "lagged_start",
run_time = 3,
),
Animation(self.radius_group)
)
self.dither()
2016-11-11 11:18:41 -08:00
2016-11-11 14:42:40 -08:00
2017-03-31 16:58:18 -07:00
def straighten_ring_out(self):
pass
2017-03-31 16:58:18 -07:00
def ask_about_precise_shape(self):
pass
2017-03-31 16:58:18 -07:00
def approximate_as_rectangle(self):
pass