mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
2946 lines
92 KiB
Python
2946 lines
92 KiB
Python
# -*- coding: utf-8 -*-
|
|
from big_ol_pile_of_manim_imports import *
|
|
|
|
class Chapter7OpeningQuote(OpeningQuote):
|
|
CONFIG = {
|
|
"quote" : [
|
|
" Calculus required ",
|
|
"continuity",
|
|
", and ",
|
|
"continuity ",
|
|
"was supposed to require the ",
|
|
"infinitely little",
|
|
"; but nobody could discover what the ",
|
|
"infinitely little",
|
|
" might be. ",
|
|
],
|
|
"quote_arg_separator" : "",
|
|
"highlighted_quote_terms" : {
|
|
"continuity" : BLUE,
|
|
"infinitely" : GREEN,
|
|
},
|
|
"author" : "Bertrand Russell",
|
|
}
|
|
|
|
class ThisVideo(TeacherStudentsScene):
|
|
def construct(self):
|
|
series = VideoSeries()
|
|
series.to_edge(UP)
|
|
deriv_videos = VGroup(*series[1:6])
|
|
this_video = series[6]
|
|
integral_videos = VGroup(*series[7:9])
|
|
video_groups = [deriv_videos, this_video, integral_videos]
|
|
|
|
braces = list(map(Brace, video_groups))
|
|
deriv_brace, this_brace, integral_brace = braces
|
|
|
|
tex_mobs = [
|
|
TexMobject(*args)
|
|
for args in [
|
|
("{df ", " \\over \\, ", " dx}"),
|
|
("\\lim_{h \\to 0}",),
|
|
("\\int ", "f(x)", "\\,dx"),
|
|
]
|
|
]
|
|
deriv_tex, this_tex, integral_tex = tex_mobs
|
|
for tex_mob, brace in zip(tex_mobs, braces):
|
|
tex_mob.set_color_by_tex("f", GREEN)
|
|
tex_mob.set_color_by_tex("dx", YELLOW)
|
|
tex_mob.next_to(brace, DOWN)
|
|
integral_tex.shift(LARGE_BUFF*RIGHT)
|
|
|
|
lim_to_deriv_arrow = Arrow(this_tex, deriv_tex, color = WHITE)
|
|
|
|
self.add(series)
|
|
for index in 0, 2:
|
|
videos = video_groups[index]
|
|
brace = braces[index]
|
|
tex_mob = tex_mobs[index]
|
|
self.play(ApplyWave(
|
|
videos,
|
|
direction = DOWN,
|
|
))
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(tex_mob, run_time = 2)
|
|
)
|
|
self.play(
|
|
this_video.set_color, YELLOW,
|
|
GrowFromCenter(this_brace),
|
|
self.get_teacher().change_mode, "raise_right_hand",
|
|
self.get_teacher().look_at, this_video
|
|
)
|
|
self.play(Write(this_tex))
|
|
self.wait(2)
|
|
self.play(self.get_teacher().change_mode, "sassy")
|
|
self.wait(2)
|
|
|
|
class LimitJustMeansApproach(PiCreatureScene):
|
|
CONFIG = {
|
|
"dx_color" : GREEN,
|
|
"max_num_zeros" : 7,
|
|
}
|
|
def construct(self):
|
|
limit_expression = self.get_limit_expression()
|
|
limit_expression.shift(2*LEFT)
|
|
limit_expression.to_edge(UP)
|
|
|
|
evaluated_expressions = self.get_evaluated_expressions()
|
|
evaluated_expressions.next_to(limit_expression, DOWN, buff = LARGE_BUFF)
|
|
brace = Brace(evaluated_expressions[0][-1], DOWN)
|
|
question = TextMobject("What does this ``approach''?")
|
|
question.next_to(brace, DOWN)
|
|
|
|
point = VectorizedPoint(limit_expression.get_right())
|
|
expression = VGroup(
|
|
limit_expression[1].copy(),
|
|
point, point.copy()
|
|
)
|
|
self.add(limit_expression)
|
|
self.change_mode("raise_right_hand")
|
|
for next_expression in evaluated_expressions:
|
|
next_expression.move_to(evaluated_expressions[0], RIGHT)
|
|
self.play(
|
|
Transform(
|
|
expression, next_expression,
|
|
lag_ratio = 0.5,
|
|
lag_factor = 1.2,
|
|
),
|
|
self.pi_creature.look_at, next_expression[-1]
|
|
)
|
|
if brace not in self.get_mobjects():
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(question)
|
|
)
|
|
self.wait(0.5)
|
|
self.wait(2)
|
|
|
|
def create_pi_creature(self):
|
|
self.pi_creature = Mortimer().flip()
|
|
self.pi_creature.to_corner(DOWN+LEFT)
|
|
return self.pi_creature
|
|
|
|
def get_limit_expression(self):
|
|
lim = TexMobject("\\lim_", "{dx", " \\to 0}")
|
|
lim.set_color_by_tex("dx", self.dx_color)
|
|
ratio = self.get_expression("dx")
|
|
ratio.next_to(lim, RIGHT)
|
|
limit_expression = VGroup(lim, ratio)
|
|
return limit_expression
|
|
|
|
def get_evaluated_expressions(self):
|
|
result = VGroup()
|
|
for num_zeros in range(1, self.max_num_zeros+1):
|
|
dx_str = "0." + "0"*num_zeros + "1"
|
|
expression = self.get_expression(dx_str)
|
|
dx = float(dx_str)
|
|
ratio = ((2+dx)**3-2**3)/dx
|
|
ratio_mob = TexMobject("%.6f\\dots"%ratio)
|
|
group = VGroup(expression, TexMobject("="), ratio_mob)
|
|
group.arrange(RIGHT)
|
|
result.add(group)
|
|
return result
|
|
|
|
def get_expression(self, dx):
|
|
result = TexMobject(
|
|
"{(2 + ", str(dx), ")^3 - 2^3 \\over", str(dx)
|
|
)
|
|
result.set_color_by_tex(dx, self.dx_color)
|
|
return result
|
|
|
|
class Goals(Scene):
|
|
def construct(self):
|
|
goals = [
|
|
TextMobject("Goal %d:"%d, s)
|
|
for d, s in [
|
|
(1, "Formal definition of derivatives"),
|
|
(2, "$(\\epsilon, \\delta)$ definition of a limit"),
|
|
(3, "L'Hôpital's rule"),
|
|
]
|
|
]
|
|
for goal in goals:
|
|
goal.scale(1.3)
|
|
goal.shift(3*DOWN).to_edge(LEFT)
|
|
|
|
curr_goal = goals[0]
|
|
self.play(FadeIn(curr_goal))
|
|
self.wait(2)
|
|
for goal in goals[1:]:
|
|
self.play(Transform(curr_goal, goal))
|
|
self.wait(2)
|
|
|
|
class RefreshOnDerivativeDefinition(GraphScene):
|
|
CONFIG = {
|
|
"start_x" : 2,
|
|
"start_dx" : 0.7,
|
|
"df_color" : YELLOW,
|
|
"dx_color" : GREEN,
|
|
"secant_line_color" : MAROON_B,
|
|
}
|
|
def construct(self):
|
|
self.setup_axes()
|
|
def func(x):
|
|
u = 0.3*x - 1.5
|
|
return -u**3 + 5*u + 7
|
|
graph = self.get_graph(func)
|
|
graph_label = self.get_graph_label(graph)
|
|
start_x_v_line, nudged_x_v_line = [
|
|
self.get_vertical_line_to_graph(
|
|
self.start_x + nudge, graph,
|
|
line_class = DashedLine,
|
|
color = RED
|
|
)
|
|
for nudge in (0, self.start_dx)
|
|
]
|
|
nudged_x_v_line.save_state()
|
|
ss_group = self.get_secant_slope_group(
|
|
self.start_x, graph,
|
|
dx = self.start_dx,
|
|
dx_label = "dx",
|
|
df_label = "df",
|
|
df_line_color = self.df_color,
|
|
dx_line_color = self.dx_color,
|
|
secant_line_color = self.secant_line_color,
|
|
)
|
|
derivative = TexMobject(
|
|
"{df", "\\over \\,", "dx}", "(", str(self.start_x), ")"
|
|
)
|
|
derivative.set_color_by_tex("df", self.df_color)
|
|
derivative.set_color_by_tex("dx", self.dx_color)
|
|
derivative.set_color_by_tex(str(self.start_x), RED)
|
|
df = derivative.get_part_by_tex("df")
|
|
dx = derivative.get_part_by_tex("dx")
|
|
input_x = derivative.get_part_by_tex(str(self.start_x))
|
|
derivative.move_to(self.coords_to_point(7, 4))
|
|
derivative.save_state()
|
|
deriv_brace = Brace(derivative)
|
|
dx_to_0 = TexMobject("dx", "\\to 0")
|
|
dx_to_0.set_color_by_tex("dx", self.dx_color)
|
|
dx_to_0.next_to(deriv_brace, DOWN)
|
|
|
|
#Introduce graph
|
|
self.play(ShowCreation(graph))
|
|
self.play(Write(graph_label, run_time = 1))
|
|
self.play(Write(derivative))
|
|
self.wait()
|
|
input_copy = input_x.copy()
|
|
self.play(
|
|
input_copy.next_to,
|
|
self.coords_to_point(self.start_x, 0),
|
|
DOWN
|
|
)
|
|
self.play(ShowCreation(start_x_v_line))
|
|
self.wait()
|
|
|
|
#ss_group_development
|
|
self.play(
|
|
ShowCreation(ss_group.dx_line),
|
|
ShowCreation(ss_group.dx_label),
|
|
)
|
|
self.wait()
|
|
self.play(ShowCreation(ss_group.df_line))
|
|
self.play(Write(ss_group.df_label))
|
|
self.wait(2)
|
|
self.play(
|
|
ReplacementTransform(ss_group.dx_label.copy(), dx),
|
|
ReplacementTransform(ss_group.df_label.copy(), df),
|
|
run_time = 2
|
|
)
|
|
self.play(ShowCreation(ss_group.secant_line))
|
|
self.wait()
|
|
|
|
#Let dx approach 0
|
|
self.play(
|
|
GrowFromCenter(deriv_brace),
|
|
Write(dx_to_0),
|
|
)
|
|
self.animate_secant_slope_group_change(
|
|
ss_group,
|
|
target_dx = 0.01,
|
|
run_time = 5,
|
|
)
|
|
self.wait()
|
|
|
|
#Write out fuller limit
|
|
new_deriv = TexMobject(
|
|
"{f", "(", str(self.start_x), "+", "dx", ")",
|
|
"-", "f", "(", str(self.start_x), ")",
|
|
"\\over \\,", "dx"
|
|
)
|
|
new_deriv.set_color_by_tex("dx", self.dx_color)
|
|
new_deriv.set_color_by_tex("f", self.df_color)
|
|
new_deriv.set_color_by_tex(str(self.start_x), RED)
|
|
deriv_to_new_deriv = dict([
|
|
(
|
|
VGroup(derivative.get_part_by_tex(s)),
|
|
VGroup(*new_deriv.get_parts_by_tex(s))
|
|
)
|
|
for s in ["f", "over", "dx", "(", str(self.start_x), ")"]
|
|
])
|
|
covered_new_deriv_parts = list(it.chain(*list(deriv_to_new_deriv.values())))
|
|
uncovered_new_deriv_parts = [part for part in new_deriv if part not in covered_new_deriv_parts]
|
|
new_deriv.move_to(derivative)
|
|
new_brace = Brace(new_deriv, DOWN)
|
|
|
|
self.animate_secant_slope_group_change(
|
|
ss_group,
|
|
target_dx = self.start_dx,
|
|
run_time = 2
|
|
)
|
|
self.play(ShowCreation(nudged_x_v_line))
|
|
self.wait()
|
|
self.play(*[
|
|
ReplacementTransform(*pair, run_time = 2)
|
|
for pair in list(deriv_to_new_deriv.items())
|
|
]+[
|
|
Transform(deriv_brace, new_brace),
|
|
dx_to_0.next_to, new_brace, DOWN
|
|
])
|
|
self.play(Write(VGroup(*uncovered_new_deriv_parts), run_time = 2))
|
|
self.wait()
|
|
|
|
#Introduce limit notation
|
|
lim = TexMobject("\\lim").scale(1.3)
|
|
dx_to_0.generate_target()
|
|
dx_to_0.target.scale(0.7)
|
|
dx_to_0.target.next_to(lim, DOWN, buff = SMALL_BUFF)
|
|
lim_group = VGroup(lim, dx_to_0.target)
|
|
lim_group.move_to(new_deriv, LEFT)
|
|
|
|
self.play(
|
|
ReplacementTransform(deriv_brace, lim),
|
|
MoveToTarget(dx_to_0),
|
|
new_deriv.next_to, lim_group, RIGHT,
|
|
run_time = 2
|
|
)
|
|
for sf, color in (1.2, YELLOW), (1/1.2, WHITE):
|
|
self.play(
|
|
lim.scale_in_place, sf,
|
|
lim.set_color, color,
|
|
lag_ratio = 0.5
|
|
)
|
|
self.wait(2)
|
|
self.animate_secant_slope_group_change(
|
|
ss_group, target_dx = 0.01,
|
|
run_time = 5,
|
|
added_anims = [
|
|
Transform(nudged_x_v_line, start_x_v_line, run_time = 5)
|
|
]
|
|
)
|
|
self.wait(2)
|
|
|
|
#Record attributes for DiscussLowercaseDs below
|
|
digest_locals(self)
|
|
|
|
class RantOpenAndClose(Scene):
|
|
def construct(self):
|
|
opening, closing = [
|
|
TextMobject(
|
|
start, "Rant on infinitesimals", "$>$",
|
|
arg_separator = ""
|
|
)
|
|
for start in ("$<$", "$<$/")
|
|
]
|
|
self.play(FadeIn(opening))
|
|
self.wait(2)
|
|
self.play(Transform(opening, closing))
|
|
self.wait(2)
|
|
|
|
class DiscussLowercaseDs(RefreshOnDerivativeDefinition, PiCreatureScene, ZoomedScene):
|
|
CONFIG = {
|
|
"zoomed_canvas_corner" : UP+LEFT
|
|
}
|
|
def construct(self):
|
|
self.skip_superclass_anims()
|
|
self.replace_dx_terms()
|
|
self.compare_rhs_and_lhs()
|
|
self.h_is_finite()
|
|
|
|
def skip_superclass_anims(self):
|
|
self.remove(self.pi_creature)
|
|
self.force_skipping()
|
|
RefreshOnDerivativeDefinition.construct(self)
|
|
self.revert_to_original_skipping_status()
|
|
self.animate_secant_slope_group_change(
|
|
self.ss_group, target_dx = self.start_dx,
|
|
added_anims = [
|
|
self.nudged_x_v_line.restore,
|
|
Animation(self.ss_group.df_line)
|
|
],
|
|
run_time = 1
|
|
)
|
|
everything = self.get_top_level_mobjects()
|
|
everything.remove(self.derivative)
|
|
self.play(*[
|
|
ApplyMethod(mob.shift, 2.5*LEFT)
|
|
for mob in everything
|
|
] + [
|
|
FadeIn(self.pi_creature)
|
|
])
|
|
|
|
def replace_dx_terms(self):
|
|
dx_list = [self.dx_to_0[0]]
|
|
dx_list += self.new_deriv.get_parts_by_tex("dx")
|
|
mover = dx_list[0]
|
|
mover_scale_val = 1.5
|
|
mover.initial_right = mover.get_right()
|
|
|
|
self.play(
|
|
mover.scale, mover_scale_val,
|
|
mover.next_to, self.pi_creature.get_corner(UP+LEFT),
|
|
UP, MED_SMALL_BUFF,
|
|
self.pi_creature.change_mode, "sassy",
|
|
path_arc = np.pi/2,
|
|
)
|
|
self.blink()
|
|
self.wait()
|
|
for tex in "\\Delta x", "h":
|
|
dx_list_replacement = [
|
|
TexMobject(
|
|
tex
|
|
).set_color(self.dx_color).move_to(dx, DOWN)
|
|
for dx in dx_list
|
|
]
|
|
self.play(
|
|
Transform(
|
|
VGroup(*dx_list),
|
|
VGroup(*dx_list_replacement),
|
|
),
|
|
self.pi_creature.change_mode, "raise_right_hand"
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
mover.scale, 0.9,
|
|
mover.move_to, mover.initial_right, RIGHT,
|
|
self.pi_creature.change_mode, "happy",
|
|
)
|
|
self.play(
|
|
self.dx_to_0.next_to, self.lim, DOWN, SMALL_BUFF,
|
|
)
|
|
self.wait()
|
|
|
|
def compare_rhs_and_lhs(self):
|
|
self.derivative.restore()
|
|
lhs = self.derivative
|
|
equals = TexMobject("=")
|
|
rhs = VGroup(self.lim, self.dx_to_0, self.new_deriv)
|
|
rhs.generate_target()
|
|
rhs.target.next_to(self.pi_creature, UP, MED_LARGE_BUFF)
|
|
rhs.target.to_edge(RIGHT)
|
|
equals.next_to(rhs.target, LEFT)
|
|
lhs.next_to(equals, LEFT)
|
|
|
|
d_circles = VGroup(*[
|
|
Circle(color = BLUE_B).replace(
|
|
lhs.get_part_by_tex(tex)[0],
|
|
stretch = True,
|
|
).scale_in_place(1.5).rotate_in_place(-np.pi/12)
|
|
for tex in ("df", "dx")
|
|
])
|
|
d_words = TextMobject("""
|
|
Limit idea is
|
|
built in
|
|
""")
|
|
d_words.next_to(d_circles, DOWN)
|
|
d_words.set_color(d_circles[0].get_color())
|
|
|
|
lhs_rect, rhs_rect = rects = [
|
|
Rectangle(color = GREEN_B).replace(
|
|
mob, stretch = True
|
|
)
|
|
for mob in (lhs, rhs.target)
|
|
]
|
|
for rect in rects:
|
|
rect.stretch_to_fit_width(rect.get_width()+2*MED_SMALL_BUFF)
|
|
rect.stretch_to_fit_height(rect.get_height()+2*MED_SMALL_BUFF)
|
|
formal_definition_words = TextMobject("""
|
|
Formal derivative definition
|
|
""")
|
|
formal_definition_words.set_width(rhs_rect.get_width())
|
|
formal_definition_words.next_to(rhs_rect, UP)
|
|
formal_definition_words.set_color(rhs_rect.get_color())
|
|
formal_definition_words.add_background_rectangle()
|
|
|
|
df = VGroup(lhs.get_part_by_tex("df"))
|
|
df_target = VGroup(*self.new_deriv.get_parts_by_tex("f"))
|
|
|
|
self.play(
|
|
MoveToTarget(rhs),
|
|
Write(lhs),
|
|
Write(equals),
|
|
)
|
|
self.play(
|
|
ShowCreation(d_circles, run_time = 2),
|
|
self.pi_creature.change_mode, "pondering"
|
|
)
|
|
self.play(Write(d_words))
|
|
self.animate_secant_slope_group_change(
|
|
self.ss_group, target_dx = 0.01,
|
|
added_anims = [
|
|
Transform(
|
|
self.nudged_x_v_line, self.start_x_v_line,
|
|
run_time = 3
|
|
)
|
|
]
|
|
)
|
|
self.change_mode("thinking")
|
|
self.wait(2)
|
|
self.play(
|
|
ShowCreation(lhs_rect),
|
|
FadeOut(d_circles),
|
|
FadeOut(d_words),
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
ReplacementTransform(lhs_rect, rhs_rect),
|
|
self.pi_creature.change_mode, "raise_right_hand"
|
|
)
|
|
self.wait(2)
|
|
self.play(ReplacementTransform(
|
|
df.copy(), df_target,
|
|
path_arc = -np.pi/2,
|
|
run_time = 2
|
|
))
|
|
self.wait(2)
|
|
self.play(Indicate(
|
|
VGroup(*rhs[:2]),
|
|
run_time = 2
|
|
))
|
|
self.wait()
|
|
|
|
self.play(Write(formal_definition_words))
|
|
self.play(
|
|
self.pi_creature.change_mode, "happy",
|
|
self.pi_creature.look_at, formal_definition_words
|
|
)
|
|
self.wait(2)
|
|
|
|
lhs.add_background_rectangle()
|
|
self.add(rhs_rect, rhs)
|
|
self.definition_group = VGroup(
|
|
lhs, equals, rhs_rect, rhs, formal_definition_words
|
|
)
|
|
self.lhs, self.rhs, self.rhs_rect = lhs, rhs, rhs_rect
|
|
|
|
def h_is_finite(self):
|
|
self.play(
|
|
FadeOut(self.graph_label),
|
|
self.definition_group.center,
|
|
self.definition_group.to_corner, UP+RIGHT,
|
|
self.pi_creature.change_mode, "sassy",
|
|
self.pi_creature.look_at, 4*UP
|
|
)
|
|
self.wait()
|
|
|
|
|
|
words = TextMobject("No ``infinitely small''")
|
|
words.next_to(
|
|
self.definition_group, DOWN,
|
|
buff = LARGE_BUFF,
|
|
)
|
|
arrow = Arrow(words.get_top(), self.rhs_rect.get_bottom())
|
|
arrow.set_color(WHITE)
|
|
|
|
h_group = VGroup(
|
|
self.rhs[1].get_part_by_tex("dx"),
|
|
*self.rhs[2].get_parts_by_tex("dx")
|
|
)
|
|
moving_h = h_group[0]
|
|
moving_h.original_center = moving_h.get_center()
|
|
dx_group = VGroup()
|
|
for h in h_group:
|
|
dx = TexMobject("dx")
|
|
dx.set_color(h.get_color())
|
|
dx.replace(h, dim_to_match = 1)
|
|
dx_group.add(dx)
|
|
moving_dx = dx_group[0]
|
|
|
|
self.play(Write(words), ShowCreation(arrow))
|
|
self.wait(2)
|
|
|
|
self.play(
|
|
moving_h.next_to, self.pi_creature.get_corner(UP+RIGHT), UP,
|
|
self.pi_creature.change_mode, "raise_left_hand",
|
|
)
|
|
self.wait()
|
|
moving_dx.move_to(moving_h)
|
|
h_group.save_state()
|
|
self.play(Transform(
|
|
h_group, dx_group,
|
|
path_arc = np.pi,
|
|
))
|
|
self.wait(2)
|
|
self.play(h_group.restore, path_arc = np.pi)
|
|
self.play(
|
|
moving_h.move_to, moving_h.original_center,
|
|
self.pi_creature.change_mode, "plain"
|
|
)
|
|
self.wait()
|
|
|
|
#Zoom in
|
|
self.activate_zooming()
|
|
lil_rect = self.little_rectangle
|
|
lil_rect.move_to(self.ss_group)
|
|
lil_rect.scale_in_place(3)
|
|
lil_rect.save_state()
|
|
self.wait()
|
|
self.add(self.rhs)
|
|
self.play(
|
|
lil_rect.set_width,
|
|
self.ss_group.dx_line.get_width()*4,
|
|
run_time = 4
|
|
)
|
|
self.wait()
|
|
dx = self.ss_group.dx_label
|
|
dx.save_state()
|
|
h = TexMobject("h")
|
|
h.set_color(dx.get_color())
|
|
h.replace(dx, dim_to_match = 1)
|
|
self.play(Transform(dx, h, path_arc = np.pi))
|
|
self.play(Indicate(dx))
|
|
self.wait()
|
|
self.play(dx.restore, path_arc = np.pi)
|
|
self.play(lil_rect.restore, run_time = 4)
|
|
self.wait()
|
|
self.disactivate_zooming()
|
|
self.wait()
|
|
|
|
#Last approaching reference
|
|
for target_dx in 3, 0.01, -2, 0.01:
|
|
self.animate_secant_slope_group_change(
|
|
self.ss_group, target_dx = target_dx,
|
|
run_time = 4,
|
|
)
|
|
self.wait()
|
|
|
|
class OtherViewsOfDx(TeacherStudentsScene):
|
|
def construct(self):
|
|
definition = TexMobject(
|
|
"{df", "\\over \\,", "dx}", "(", "2", ")", "=",
|
|
"\\lim", "_{h", "\\to", "0}",
|
|
"{f", "(", "2", "+", "h", ")", "-", "f", "(", "2", ")",
|
|
"\\over \\,", "h}"
|
|
)
|
|
tex_to_color = {
|
|
"df" : YELLOW,
|
|
"f" : YELLOW,
|
|
"dx" : GREEN,
|
|
"h" : GREEN,
|
|
"2" : RED
|
|
}
|
|
for tex, color in list(tex_to_color.items()):
|
|
definition.set_color_by_tex(tex, color)
|
|
definition.scale(0.8)
|
|
definition.to_corner(UP+LEFT)
|
|
dx_group = VGroup(*definition.get_parts_by_tex("dx"))
|
|
h_group = VGroup(*definition.get_parts_by_tex("h"))
|
|
self.add(definition)
|
|
|
|
statements = [
|
|
TextMobject(*args)
|
|
for args in [
|
|
("Why the new \\\\ variable", "$h$", "?"),
|
|
("$dx$", "is more $\\dots$ contentious."),
|
|
("$dx$", "is infinitely small"),
|
|
("$dx$", "is nothing more \\\\ than a symbol"),
|
|
]
|
|
]
|
|
for statement in statements:
|
|
statement.h, statement.dx = [
|
|
VGroup(*statement.get_parts_by_tex(
|
|
tex, substring = False
|
|
)).set_color(GREEN)
|
|
for tex in ("$h$", "$dx$")
|
|
]
|
|
|
|
#Question
|
|
self.student_says(
|
|
statements[0],
|
|
student_index = 1,
|
|
target_mode = "confused"
|
|
)
|
|
self.play(ReplacementTransform(
|
|
statements[0].h.copy(), h_group,
|
|
run_time = 2,
|
|
lag_ratio = 0.5,
|
|
lag_factor = 1.5,
|
|
))
|
|
self.wait()
|
|
|
|
#Teacher answer
|
|
self.teacher_says(
|
|
statements[1],
|
|
target_mode = "hesitant",
|
|
bubble_creation_class = FadeIn,
|
|
)
|
|
self.play(ReplacementTransform(
|
|
statements[1].dx.copy(), dx_group,
|
|
run_time = 2,
|
|
))
|
|
self.wait()
|
|
|
|
#First alternate view
|
|
moving_dx = dx_group.copy()
|
|
bubble_intro = PiCreatureBubbleIntroduction(
|
|
self.get_students()[2],
|
|
statements[2],
|
|
target_mode = "hooray",
|
|
bubble_creation_class = FadeIn,
|
|
)
|
|
bubble_intro.update(1)
|
|
dx_movement = Transform(
|
|
moving_dx, statements[2].dx,
|
|
run_time = 2
|
|
)
|
|
bubble_intro.update(0)
|
|
self.play(
|
|
bubble_intro, dx_movement,
|
|
RemovePiCreatureBubble(self.get_teacher()),
|
|
)
|
|
self.play(self.get_teacher().change_mode, "erm")
|
|
self.wait()
|
|
|
|
#Next alternate view
|
|
bubble_intro = PiCreatureBubbleIntroduction(
|
|
self.get_students()[0],
|
|
statements[3],
|
|
target_mode = "maybe",
|
|
look_at_arg = 3*UP,
|
|
bubble_creation_class = FadeIn,
|
|
)
|
|
bubble_intro.update(1)
|
|
dx_movement = Transform(
|
|
moving_dx, statements[3].dx,
|
|
run_time = 2
|
|
)
|
|
bubble_intro.update(0)
|
|
last_bubble = self.get_students()[2].bubble
|
|
self.play(
|
|
bubble_intro, dx_movement,
|
|
FadeOut(last_bubble),
|
|
FadeOut(last_bubble.content),
|
|
*it.chain(*[
|
|
[
|
|
pi.change_mode, "pondering",
|
|
pi.look_at, bubble_intro.mobject
|
|
]
|
|
for pi in self.get_students()[1:]
|
|
])
|
|
)
|
|
self.wait(3)
|
|
|
|
class GoalsListed(Scene):
|
|
def construct(self):
|
|
goals = VGroup(*[
|
|
TextMobject("Goal %d: %s"%(d, s))
|
|
for d, s in zip(it.count(1), [
|
|
"Formal definition of a derivative",
|
|
"$(\\epsilon, \\delta)$ definition of limits",
|
|
"L'Hôpital's rule",
|
|
])
|
|
])
|
|
goals.arrange(
|
|
DOWN, buff = LARGE_BUFF, aligned_edge = LEFT
|
|
)
|
|
|
|
for goal in goals:
|
|
self.play(FadeIn(goal))
|
|
self.wait()
|
|
for i, goal in enumerate(goals):
|
|
anims = [goal.set_color, YELLOW]
|
|
if i > 0:
|
|
anims += [goals[i-1].set_color, WHITE]
|
|
self.play(*anims)
|
|
self.wait()
|
|
|
|
class GraphLimitExpression(GraphScene):
|
|
CONFIG = {
|
|
"start_x" : 2,
|
|
"h_color" : GREEN,
|
|
"f_color" : YELLOW,
|
|
"two_color" : RED,
|
|
"graph_origin" : 3*DOWN+LEFT,
|
|
"x_min" : -8,
|
|
"x_max" : 5,
|
|
"x_axis_label" : "$h$",
|
|
"x_labeled_nums" : list(range(-8, 6, 2)),
|
|
"y_min" : 0,
|
|
"y_max" : 20,
|
|
"y_tick_frequency" : 1,
|
|
"y_labeled_nums" : list(range(5, 25, 5)),
|
|
"y_axis_label" : "",
|
|
"big_delta" : 0.7,
|
|
"small_delta" : 0.01,
|
|
}
|
|
def construct(self):
|
|
self.func = lambda h : 3*(2**2) + 3*2*h + h**2
|
|
self.setup_axes()
|
|
self.introduce_function()
|
|
self.emphasize_non_definedness_at_0()
|
|
self.draw_limit_point_hole()
|
|
self.show_limit()
|
|
self.skeptic_asks()
|
|
self.show_epsilon_delta_intuition()
|
|
|
|
def introduce_function(self):
|
|
expression = TexMobject(
|
|
"{(", "2", "+", "h", ")", "^3",
|
|
"-", "(", "2", ")", "^3",
|
|
"\\over \\,", "h}",
|
|
arg_separator = "",
|
|
)
|
|
limit = TexMobject("\\lim", "_{h", "\\to 0}")
|
|
derivative = TexMobject(
|
|
"{d(x^3)", "\\over \\,", "dx}", "(", "2", ")"
|
|
)
|
|
tex_to_color = {
|
|
"h" : self.h_color,
|
|
"dx" : self.h_color,
|
|
"2" : self.two_color
|
|
}
|
|
for tex_mob in expression, limit, derivative:
|
|
for tex, color in list(tex_to_color.items()):
|
|
tex_mob.set_color_by_tex(tex, color)
|
|
tex_mob.next_to(ORIGIN, RIGHT, LARGE_BUFF)
|
|
tex_mob.to_edge(UP)
|
|
|
|
expression.save_state()
|
|
expression.generate_target()
|
|
expression.target.next_to(limit, RIGHT)
|
|
brace = Brace(VGroup(limit, expression.target))
|
|
derivative.next_to(brace, DOWN)
|
|
|
|
|
|
|
|
indices = [0, 6, 11, 13]
|
|
funcs = [
|
|
lambda h : (2+h)**3,
|
|
lambda h : (2+h)**3 - 2**3,
|
|
self.func
|
|
]
|
|
graph = None
|
|
for i, j, func in zip(indices, indices[1:], funcs):
|
|
anims = [FadeIn(
|
|
VGroup(*expression[i:j]),
|
|
lag_ratio = 0.5,
|
|
lag_factor = 1.5
|
|
)]
|
|
new_graph = self.get_graph(func, color = BLUE)
|
|
if graph is None:
|
|
graph = new_graph
|
|
anims.append(FadeIn(graph))
|
|
else:
|
|
anims.append(Transform(graph, new_graph))
|
|
self.play(*anims)
|
|
self.wait()
|
|
self.wait()
|
|
self.play(
|
|
MoveToTarget(expression),
|
|
FadeIn(limit, lag_ratio = 0.5),
|
|
GrowFromCenter(brace)
|
|
)
|
|
self.play(Write(derivative))
|
|
self.wait(2)
|
|
self.play(
|
|
expression.restore,
|
|
*list(map(FadeOut, [derivative, brace, limit]))
|
|
)
|
|
self.wait()
|
|
|
|
colored_graph = graph.copy().set_color(YELLOW)
|
|
self.play(ShowCreation(colored_graph))
|
|
self.wait()
|
|
self.play(ShowCreation(graph))
|
|
self.remove(colored_graph)
|
|
self.wait()
|
|
|
|
self.expression = expression
|
|
self.limit = limit
|
|
self.graph = graph
|
|
|
|
def emphasize_non_definedness_at_0(self):
|
|
expression = self.expression
|
|
|
|
dot = Dot(self.graph_origin, color = GREEN)
|
|
h_equals_0 = TexMobject("h", "=", "0", "?")
|
|
h_equals_0.next_to(self.graph_origin, UP+RIGHT, LARGE_BUFF)
|
|
for tex in "h", "0":
|
|
h_equals_0.set_color_by_tex(tex, GREEN)
|
|
|
|
arrow = Arrow(h_equals_0.get_left(), self.graph_origin)
|
|
arrow.set_color(WHITE)
|
|
|
|
new_expression = expression.deepcopy()
|
|
h_group = VGroup(*new_expression.get_parts_by_tex("h"))
|
|
for h in h_group:
|
|
zero = TexMobject("0")
|
|
zero.set_color(h.get_color())
|
|
zero.replace(h, dim_to_match = 1)
|
|
Transform(h, zero).update(1)
|
|
rhs = TexMobject("=", "{\\, 0\\,", "\\over \\,", "0\\,}")
|
|
rhs.set_color_by_tex("0", GREEN)
|
|
rhs.next_to(new_expression, RIGHT)
|
|
equation = VGroup(new_expression, rhs)
|
|
equation.next_to(expression, DOWN, buff = LARGE_BUFF)
|
|
|
|
ud_brace = Brace(VGroup(*rhs[1:]), DOWN)
|
|
undefined = TextMobject("Undefined")
|
|
undefined.next_to(ud_brace, DOWN)
|
|
undefined.to_edge(RIGHT)
|
|
|
|
self.play(Write(h_equals_0, run_time = 2))
|
|
self.play(*list(map(ShowCreation, [arrow, dot])))
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
expression.copy(), new_expression
|
|
))
|
|
self.wait()
|
|
self.play(Write(rhs))
|
|
self.wait()
|
|
self.play(
|
|
GrowFromCenter(ud_brace),
|
|
Write(undefined)
|
|
)
|
|
self.wait(2)
|
|
|
|
self.point_to_zero_group = VGroup(
|
|
h_equals_0, arrow, dot
|
|
)
|
|
self.plug_in_zero_group = VGroup(
|
|
new_expression, rhs, ud_brace, undefined
|
|
)
|
|
|
|
def draw_limit_point_hole(self):
|
|
dx = 0.07
|
|
color = self.graph.get_color()
|
|
circle = Circle(
|
|
radius = dx,
|
|
stroke_color = color,
|
|
fill_color = BLACK,
|
|
fill_opacity = 1,
|
|
)
|
|
circle.move_to(self.coords_to_point(0, 12))
|
|
colored_circle = circle.copy()
|
|
colored_circle.set_stroke(YELLOW)
|
|
colored_circle.set_fill(opacity = 0)
|
|
|
|
self.play(GrowFromCenter(circle))
|
|
self.wait()
|
|
self.play(ShowCreation(colored_circle))
|
|
self.play(ShowCreation(
|
|
circle.copy().set_fill(opacity = 0),
|
|
remover = True
|
|
))
|
|
self.remove(colored_circle)
|
|
self.play(
|
|
circle.scale_in_place, 0.3,
|
|
run_time = 2,
|
|
rate_func = wiggle
|
|
)
|
|
self.wait()
|
|
|
|
self.limit_point_hole = circle
|
|
|
|
def show_limit(self):
|
|
dot = self.point_to_zero_group[-1]
|
|
ed_group = self.get_epsilon_delta_group(self.big_delta)
|
|
|
|
left_v_line, right_v_line = ed_group.delta_lines
|
|
bottom_h_line, top_h_line = ed_group.epsilon_lines
|
|
ed_group.delta_lines.save_state()
|
|
ed_group.epsilon_lines.save_state()
|
|
|
|
brace = Brace(ed_group.input_range, UP)
|
|
brace_text = brace.get_text("Inputs around 0", buff = SMALL_BUFF)
|
|
brace_text.add_background_rectangle()
|
|
brace_text.shift(RIGHT)
|
|
|
|
limit_point_hole_copy = self.limit_point_hole.copy()
|
|
limit_point_hole_copy.set_stroke(YELLOW)
|
|
h_zero_hole = limit_point_hole_copy.copy()
|
|
h_zero_hole.move_to(self.graph_origin)
|
|
|
|
ed_group.input_range.add(h_zero_hole)
|
|
ed_group.output_range.add(limit_point_hole_copy)
|
|
|
|
#Show range around 0
|
|
self.play(
|
|
FadeOut(self.plug_in_zero_group),
|
|
FadeOut(VGroup(*self.point_to_zero_group[:-1])),
|
|
)
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(brace_text),
|
|
ReplacementTransform(dot, ed_group.input_range),
|
|
)
|
|
self.add(h_zero_hole)
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(
|
|
ed_group.input_range.copy(),
|
|
ed_group.output_range,
|
|
run_time = 2
|
|
),
|
|
)
|
|
self.remove(self.limit_point_hole)
|
|
|
|
#Show approaching
|
|
self.play(*list(map(FadeOut, [brace, brace_text])))
|
|
for v_line, h_line in (right_v_line, top_h_line), (left_v_line, bottom_h_line):
|
|
self.play(
|
|
ShowCreation(v_line),
|
|
ShowCreation(h_line)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
v_line.move_to, self.coords_to_point(0, 0), DOWN,
|
|
h_line.move_to, self.coords_to_point(0, self.func(0)),
|
|
run_time = 3
|
|
)
|
|
self.play(
|
|
VGroup(h_line, v_line).set_stroke, GREY, 2,
|
|
)
|
|
self.wait()
|
|
|
|
#Write limit
|
|
limit = self.limit
|
|
limit.next_to(self.expression, LEFT)
|
|
equals, twelve = rhs = TexMobject("=", "12")
|
|
rhs.next_to(self.expression, RIGHT)
|
|
twelve_copy = twelve.copy()
|
|
limit_group = VGroup(limit, rhs)
|
|
|
|
self.play(Write(limit_group))
|
|
self.wait()
|
|
self.play(twelve_copy.next_to, top_h_line, RIGHT)
|
|
self.wait()
|
|
|
|
self.twelve_copy = twelve_copy
|
|
self.rhs = rhs
|
|
self.ed_group = ed_group
|
|
self.input_range_brace_group = VGroup(brace, brace_text)
|
|
|
|
def skeptic_asks(self):
|
|
randy = Randolph()
|
|
randy.scale(0.9)
|
|
randy.to_edge(DOWN)
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(PiCreatureSays(
|
|
randy, """
|
|
What \\emph{exactly} do you
|
|
mean by ``approach''
|
|
""",
|
|
bubble_kwargs = {
|
|
"height" : 3,
|
|
"width" : 5,
|
|
"fill_opacity" : 1,
|
|
"direction" : LEFT,
|
|
},
|
|
target_mode = "sassy"
|
|
))
|
|
self.remove(self.twelve_copy)
|
|
self.play(randy.look, OUT)
|
|
self.play(Blink(randy))
|
|
self.wait()
|
|
self.play(RemovePiCreatureBubble(
|
|
randy, target_mode = "pondering",
|
|
look_at_arg = self.limit_point_hole
|
|
))
|
|
self.play(
|
|
self.ed_group.delta_lines.restore,
|
|
self.ed_group.epsilon_lines.restore,
|
|
Animation(randy),
|
|
rate_func = there_and_back,
|
|
run_time = 5,
|
|
)
|
|
self.play(Blink(randy))
|
|
self.play(FadeOut(randy))
|
|
|
|
def show_epsilon_delta_intuition(self):
|
|
self.play(
|
|
FadeOut(self.ed_group.epsilon_lines),
|
|
FadeIn(self.input_range_brace_group)
|
|
)
|
|
self.ed_group.epsilon_lines.restore()
|
|
self.wait()
|
|
self.play(
|
|
self.ed_group.delta_lines.restore,
|
|
Animation(self.input_range_brace_group),
|
|
run_time = 2
|
|
)
|
|
self.play(FadeOut(self.input_range_brace_group))
|
|
self.play(
|
|
ReplacementTransform(
|
|
self.ed_group.input_range.copy(),
|
|
self.ed_group.output_range,
|
|
run_time = 2
|
|
)
|
|
)
|
|
self.wait()
|
|
self.play(*list(map(GrowFromCenter, self.ed_group.epsilon_lines)))
|
|
self.play(*[
|
|
ApplyMethod(
|
|
line.copy().set_stroke(GREY, 2).move_to,
|
|
self.coords_to_point(0, self.func(0)),
|
|
run_time = 3,
|
|
rate_func = there_and_back,
|
|
remover = True,
|
|
)
|
|
for line in self.ed_group.epsilon_lines
|
|
])
|
|
self.wait()
|
|
|
|
holes = VGroup(
|
|
self.ed_group.input_range.submobjects.pop(),
|
|
self.ed_group.output_range.submobjects.pop(),
|
|
)
|
|
holes.save_state()
|
|
self.animate_epsilon_delta_group_change(
|
|
self.ed_group,
|
|
target_delta = self.small_delta,
|
|
run_time = 8,
|
|
rate_func = lambda t : smooth(t, 2),
|
|
added_anims = [
|
|
ApplyMethod(
|
|
hole.scale_in_place, 0.5,
|
|
run_time = 8
|
|
)
|
|
for hole in holes
|
|
]
|
|
)
|
|
self.wait()
|
|
|
|
self.holes = holes
|
|
|
|
#########
|
|
|
|
def get_epsilon_delta_group(
|
|
self,
|
|
delta,
|
|
limit_x = 0,
|
|
dashed_line_stroke_width = 3,
|
|
dashed_line_length = FRAME_HEIGHT,
|
|
input_range_color = YELLOW,
|
|
input_range_stroke_width = 6,
|
|
):
|
|
kwargs = dict(locals())
|
|
result = VGroup()
|
|
kwargs.pop("self")
|
|
result.delta = kwargs.pop("delta")
|
|
result.limit_x = kwargs.pop("limit_x")
|
|
result.kwargs = kwargs
|
|
dashed_line = DashedLine(
|
|
ORIGIN, dashed_line_length*RIGHT,
|
|
stroke_width = dashed_line_stroke_width
|
|
)
|
|
x_values = [limit_x-delta, limit_x+delta]
|
|
x_axis_points = [self.coords_to_point(x, 0) for x in x_values]
|
|
result.delta_lines = VGroup(*[
|
|
dashed_line.copy().rotate(np.pi/2).move_to(
|
|
point, DOWN
|
|
)
|
|
for point in x_axis_points
|
|
])
|
|
if self.func(limit_x) < 0:
|
|
result.delta_lines.rotate(
|
|
np.pi, RIGHT,
|
|
about_point = result.delta_lines.get_bottom()
|
|
)
|
|
basically_zero = 0.00001
|
|
result.input_range, result.output_range = [
|
|
VGroup(*[
|
|
self.get_graph(
|
|
func,
|
|
color = input_range_color,
|
|
x_min = x_min,
|
|
x_max = x_max,
|
|
)
|
|
for x_min, x_max in [
|
|
(limit_x-delta, limit_x-basically_zero),
|
|
(limit_x+basically_zero, limit_x+delta),
|
|
]
|
|
]).set_stroke(width = input_range_stroke_width)
|
|
for func in ((lambda h : 0), self.func)
|
|
]
|
|
result.epsilon_lines = VGroup(*[
|
|
dashed_line.copy().move_to(
|
|
self.coords_to_point(limit_x, 0)[0]*RIGHT+\
|
|
result.output_range.get_edge_center(vect)[1]*UP
|
|
)
|
|
for vect in (DOWN, UP)
|
|
])
|
|
|
|
result.digest_mobject_attrs()
|
|
return result
|
|
|
|
def animate_epsilon_delta_group_change(
|
|
self, epsilon_delta_group, target_delta,
|
|
**kwargs
|
|
):
|
|
added_anims = kwargs.get("added_anims", [])
|
|
limit_x = epsilon_delta_group.limit_x
|
|
start_delta = epsilon_delta_group.delta
|
|
ed_group_kwargs = epsilon_delta_group.kwargs
|
|
def update_ed_group(ed_group, alpha):
|
|
delta = interpolate(start_delta, target_delta, alpha)
|
|
new_group = self.get_epsilon_delta_group(
|
|
delta, limit_x = limit_x,
|
|
**ed_group_kwargs
|
|
)
|
|
Transform(ed_group, new_group).update(1)
|
|
return ed_group
|
|
|
|
self.play(
|
|
UpdateFromAlphaFunc(
|
|
epsilon_delta_group, update_ed_group,
|
|
**kwargs
|
|
),
|
|
*added_anims
|
|
)
|
|
|
|
class LimitCounterExample(GraphLimitExpression):
|
|
CONFIG = {
|
|
"x_min" : -8,
|
|
"x_max" : 8,
|
|
"x_labeled_nums" : list(range(-8, 10, 2)),
|
|
"x_axis_width" : FRAME_WIDTH - LARGE_BUFF,
|
|
"y_min" : -4,
|
|
"y_max" : 4,
|
|
"y_labeled_nums" : list(range(-2, 4, 1)),
|
|
"y_axis_height" : FRAME_HEIGHT+2*LARGE_BUFF,
|
|
"graph_origin" : DOWN,
|
|
"graph_color" : BLUE,
|
|
"hole_radius" : 0.075,
|
|
"smaller_hole_radius" : 0.04,
|
|
"big_delta" : 1.5,
|
|
"small_delta" : 0.05,
|
|
}
|
|
def construct(self):
|
|
self.add_func()
|
|
self.setup_axes()
|
|
self.draw_graph()
|
|
self.approach_zero()
|
|
self.write_limit_not_defined()
|
|
self.show_epsilon_delta_intuition()
|
|
|
|
def add_func(self):
|
|
def func(h):
|
|
square = 0.25*h**2
|
|
if h < 0:
|
|
return -square + 1
|
|
else:
|
|
return square + 2
|
|
self.func = func
|
|
|
|
def draw_graph(self):
|
|
epsilon = 0.1
|
|
left_graph, right_graph = [
|
|
self.get_graph(
|
|
self.func,
|
|
color = self.graph_color,
|
|
x_min = x_min,
|
|
x_max = x_max,
|
|
)
|
|
for x_min, x_max in [
|
|
(self.x_min, -epsilon),
|
|
(epsilon, self.x_max),
|
|
]
|
|
]
|
|
left_hole = self.get_hole(0, 1, color = self.graph_color)
|
|
right_hole = self.get_hole(0, 2, color = self.graph_color)
|
|
graph = VGroup(
|
|
left_graph, left_hole,
|
|
right_hole, right_graph
|
|
)
|
|
|
|
self.play(ShowCreation(graph, run_time = 5))
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
left_hole.copy().set_stroke(YELLOW), right_hole
|
|
))
|
|
self.wait()
|
|
|
|
self.graph = graph
|
|
self.graph_holes = VGroup(left_hole, right_hole)
|
|
|
|
def approach_zero(self):
|
|
ed_group = self.get_epsilon_delta_group(self.big_delta)
|
|
left_v_line, right_v_line = ed_group.delta_lines
|
|
bottom_h_line, top_h_line = ed_group.epsilon_lines
|
|
ed_group.delta_lines.save_state()
|
|
ed_group.epsilon_lines.save_state()
|
|
|
|
right_lines = VGroup(right_v_line, top_h_line)
|
|
left_lines = VGroup(left_v_line, bottom_h_line)
|
|
|
|
basically_zero = 0.00001
|
|
def update_lines(lines, alpha):
|
|
v_line, h_line = lines
|
|
sign = 1 if v_line is right_v_line else -1
|
|
x_val = interpolate(sign*self.big_delta, sign*basically_zero, alpha)
|
|
v_line.move_to(self.coords_to_point(x_val, 0), DOWN)
|
|
h_line.move_to(self.coords_to_point(0, self.func(x_val)))
|
|
return lines
|
|
|
|
for lines in right_lines, left_lines:
|
|
self.play(*list(map(ShowCreation, lines)))
|
|
self.play(UpdateFromAlphaFunc(
|
|
lines, update_lines,
|
|
run_time = 3
|
|
))
|
|
self.play(lines.set_stroke, GREY, 3)
|
|
self.wait()
|
|
|
|
self.ed_group = ed_group
|
|
|
|
def write_limit_not_defined(self):
|
|
limit = TexMobject(
|
|
"\\lim", "_{h", "\\to 0}", "f(", "h", ")"
|
|
)
|
|
limit.set_color_by_tex("h", GREEN)
|
|
limit.move_to(self.coords_to_point(2, 1.5))
|
|
|
|
words = TextMobject("is not defined")
|
|
words.set_color(RED)
|
|
words.next_to(limit, RIGHT, align_using_submobjects = True)
|
|
|
|
limit_group = VGroup(limit, words)
|
|
|
|
self.play(Write(limit))
|
|
self.wait()
|
|
self.play(Write(words))
|
|
self.wait()
|
|
self.play(limit_group.to_corner, UP+LEFT)
|
|
self.wait()
|
|
|
|
def show_epsilon_delta_intuition(self):
|
|
ed_group = self.ed_group
|
|
self.play(
|
|
ed_group.delta_lines.restore,
|
|
ed_group.epsilon_lines.restore,
|
|
)
|
|
self.play(ShowCreation(ed_group.input_range))
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
ed_group.input_range.copy(),
|
|
ed_group.output_range,
|
|
run_time = 2
|
|
))
|
|
self.graph.remove(*self.graph_holes)
|
|
self.remove(*self.graph_holes)
|
|
self.wait()
|
|
self.animate_epsilon_delta_group_change(
|
|
ed_group, target_delta = self.small_delta,
|
|
run_time = 6
|
|
)
|
|
self.hole_radius = self.smaller_hole_radius
|
|
|
|
brace = Brace(self.ed_group.epsilon_lines, RIGHT, buff = SMALL_BUFF)
|
|
brace_text = brace.get_text("Can't get \\\\ smaller", buff = SMALL_BUFF)
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(brace_text)
|
|
)
|
|
self.wait()
|
|
run_time_rate_func_pairs = [
|
|
(3, lambda t : 1 - there_and_back(t)),
|
|
(1, lambda t : 1 - 0.2*there_and_back(3*t % 1)),
|
|
(1, lambda t : 1 - 0.2*there_and_back(5*t % 1)),
|
|
]
|
|
for run_time, rate_func in run_time_rate_func_pairs:
|
|
self.animate_epsilon_delta_group_change(
|
|
ed_group, target_delta = self.small_delta,
|
|
run_time = run_time,
|
|
rate_func = rate_func,
|
|
)
|
|
self.wait()
|
|
|
|
#####
|
|
|
|
def get_epsilon_delta_group(self, delta, **kwargs):
|
|
ed_group = GraphLimitExpression.get_epsilon_delta_group(self, delta, **kwargs)
|
|
color = ed_group.kwargs["input_range_color"]
|
|
radius = min(delta/2, self.hole_radius)
|
|
pairs = [
|
|
(ed_group.input_range[0], (0, 0)),
|
|
(ed_group.input_range[1], (0, 0)),
|
|
(ed_group.output_range[0], (0, 1)),
|
|
(ed_group.output_range[1], (0, 2)),
|
|
]
|
|
for mob, coords in pairs:
|
|
mob.add(self.get_hole(
|
|
*coords,
|
|
color = color,
|
|
radius = radius
|
|
))
|
|
return ed_group
|
|
|
|
def get_hole(self, *coords, **kwargs):
|
|
color = kwargs.get("color", BLUE)
|
|
radius = kwargs.get("radius", self.hole_radius)
|
|
return Circle(
|
|
radius = radius,
|
|
stroke_color = color,
|
|
fill_color = BLACK,
|
|
fill_opacity = 1,
|
|
).move_to(self.coords_to_point(*coords))
|
|
|
|
class PrefaceToEpsilonDeltaDefinition(TeacherStudentsScene):
|
|
def construct(self):
|
|
title = TexMobject("(\\epsilon, \\delta) \\text{ definition}")
|
|
title.next_to(self.get_teacher().get_corner(UP+LEFT), UP)
|
|
title.save_state()
|
|
title.shift(DOWN)
|
|
title.set_fill(opacity = 0)
|
|
|
|
self.play(
|
|
title.restore,
|
|
self.get_teacher().change_mode, "raise_right_hand",
|
|
)
|
|
self.change_student_modes(*["confused"]*3)
|
|
self.wait()
|
|
self.student_says(
|
|
"Isn't that pretty \\\\ technical?",
|
|
target_mode = "guilty",
|
|
added_anims = [
|
|
title.to_edge, UP,
|
|
self.get_teacher().change_mode, "plain",
|
|
self.get_teacher().look_at, self.get_students()[1].eyes
|
|
]
|
|
)
|
|
self.look_at(self.get_teacher().eyes, self.get_students())
|
|
self.wait()
|
|
self.teacher_says("", bubble_kwargs = {"stroke_width" : 0})
|
|
self.change_student_modes(
|
|
*["pondering"]*3,
|
|
look_at_arg = UP+LEFT,
|
|
added_anims = [self.get_teacher().look_at, UP+LEFT]
|
|
)
|
|
self.wait(3)
|
|
words = TextMobject(
|
|
"It's a glimpse of\\\\",
|
|
"real analysis"
|
|
)
|
|
words.set_color_by_tex("real", YELLOW)
|
|
self.teacher_says(
|
|
words,
|
|
bubble_kwargs = {"height" : 3, "width" : 6}
|
|
)
|
|
self.change_student_modes(*["happy"]*3)
|
|
self.wait(6)
|
|
|
|
class EpsilonDeltaExample(GraphLimitExpression, ZoomedScene):
|
|
CONFIG = {
|
|
"epsilon_list" : [2, 1, 0.5],
|
|
"zoomed_canvas_corner" : DOWN+RIGHT,
|
|
}
|
|
def construct(self):
|
|
self.delta_list = [
|
|
epsilon/6.0 for epsilon in self.epsilon_list
|
|
]
|
|
self.skip_superclass_anims()
|
|
self.introduce_epsilon()
|
|
self.match_epsilon()
|
|
self.zoom_in()
|
|
self.introduce_delta()
|
|
self.smaller_epsilon()
|
|
|
|
def skip_superclass_anims(self):
|
|
self.force_skipping()
|
|
GraphLimitExpression.construct(self)
|
|
self.animate_epsilon_delta_group_change(
|
|
self.ed_group,
|
|
target_delta = self.big_delta,
|
|
)
|
|
self.holes.restore()
|
|
self.add(self.holes)
|
|
self.revert_to_original_skipping_status()
|
|
|
|
def introduce_epsilon(self):
|
|
epsilon_group, small_epsilon_group = list(map(
|
|
self.get_epsilon_group,
|
|
self.epsilon_list[:2]
|
|
))
|
|
|
|
twelve_line = epsilon_group.limit_line
|
|
twelve = self.rhs[-1]
|
|
twelve_copy = twelve.copy()
|
|
twelve_copy.next_to(twelve_line)
|
|
|
|
distance = TextMobject("Distance")
|
|
distance.next_to(epsilon_group.labels, DOWN, LARGE_BUFF)
|
|
distance.to_edge(RIGHT)
|
|
arrows = VGroup(*[
|
|
Arrow(distance.get_top(), label.get_right())
|
|
for label in epsilon_group.labels
|
|
])
|
|
|
|
self.play(ShowCreation(twelve_line))
|
|
self.play(Write(twelve_copy))
|
|
self.play(ReplacementTransform(twelve_copy, twelve))
|
|
self.wait()
|
|
|
|
self.play(*it.chain(
|
|
[
|
|
ReplacementTransform(twelve_line.copy(), line)
|
|
for line in epsilon_group.epsilon_lines
|
|
],
|
|
list(map(GrowFromCenter, epsilon_group.braces)),
|
|
))
|
|
self.play(*list(map(Write, epsilon_group.labels)))
|
|
self.play(
|
|
Write(distance),
|
|
ShowCreation(arrows)
|
|
)
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [distance, arrows])))
|
|
self.play(Transform(
|
|
epsilon_group, small_epsilon_group,
|
|
run_time = 2
|
|
))
|
|
self.wait()
|
|
|
|
self.epsilon_group = epsilon_group
|
|
|
|
def match_epsilon(self):
|
|
self.animate_epsilon_delta_group_change(
|
|
self.ed_group, target_delta = self.delta_list[1],
|
|
run_time = 2,
|
|
added_anims = [
|
|
ApplyMethod(
|
|
hole.scale_in_place, 0.25,
|
|
run_time = 2
|
|
)
|
|
for hole in self.holes
|
|
]
|
|
)
|
|
self.ed_group.delta = self.delta_list[1]
|
|
self.ed_group.input_range.make_jagged()
|
|
self.wait()
|
|
|
|
def zoom_in(self):
|
|
self.ed_group.input_range.make_jagged()
|
|
|
|
self.activate_zooming()
|
|
lil_rect = self.little_rectangle
|
|
lil_rect.move_to(self.graph_origin)
|
|
lil_rect.scale_in_place(self.zoom_factor)
|
|
self.add(self.holes)
|
|
self.wait()
|
|
self.play(lil_rect.scale_in_place, 1./self.zoom_factor)
|
|
self.wait()
|
|
|
|
def introduce_delta(self):
|
|
delta_group = self.get_delta_group(self.delta_list[1])
|
|
self.play(*list(map(GrowFromCenter, delta_group.braces)))
|
|
self.play(*list(map(Write, delta_group.labels)))
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(
|
|
self.ed_group.input_range.copy(),
|
|
self.ed_group.output_range,
|
|
run_time = 2
|
|
),
|
|
Animation(self.holes),
|
|
)
|
|
self.play(ApplyWave(
|
|
VGroup(self.ed_group.output_range, self.holes[1]),
|
|
direction = RIGHT
|
|
))
|
|
self.wait(2)
|
|
|
|
self.delta_group = delta_group
|
|
|
|
def smaller_epsilon(self):
|
|
new_epsilon = self.epsilon_list[-1]
|
|
new_delta = self.delta_list[-1]
|
|
self.play(Transform(
|
|
self.epsilon_group,
|
|
self.get_epsilon_group(new_epsilon)
|
|
))
|
|
self.wait()
|
|
self.animate_epsilon_delta_group_change(
|
|
self.ed_group, target_delta = new_delta,
|
|
added_anims = [
|
|
Transform(
|
|
self.delta_group,
|
|
self.get_delta_group(new_delta)
|
|
)
|
|
] + [
|
|
ApplyMethod(hole.scale_in_place, 0.5)
|
|
for hole in self.holes
|
|
]
|
|
)
|
|
self.ed_group.input_range.make_jagged()
|
|
self.wait(2)
|
|
|
|
##
|
|
|
|
def get_epsilon_group(self, epsilon, limit_value = 12):
|
|
result = VGroup()
|
|
line_length = FRAME_HEIGHT
|
|
lines = [
|
|
Line(
|
|
ORIGIN, line_length*RIGHT,
|
|
).move_to(self.coords_to_point(0, limit_value+nudge))
|
|
for nudge in (0, -epsilon, epsilon)
|
|
]
|
|
result.limit_line = lines[0]
|
|
result.limit_line.set_stroke(RED, width = 3)
|
|
result.epsilon_lines = VGroup(*lines[1:])
|
|
result.epsilon_lines.set_stroke(MAROON_B, width = 2)
|
|
brace = Brace(Line(ORIGIN, 0.5*UP), RIGHT)
|
|
result.braces = VGroup(*[
|
|
brace.copy().set_height(
|
|
group.get_height()
|
|
).next_to(group, RIGHT, SMALL_BUFF)
|
|
for i in (1, 2)
|
|
for group in [VGroup(lines[0], lines[i])]
|
|
])
|
|
result.labels = VGroup(*[
|
|
brace.get_text("\\Big $\\epsilon$", buff = SMALL_BUFF)
|
|
for brace in result.braces
|
|
])
|
|
for label, brace in zip(result.labels, result.braces):
|
|
label.set_height(min(
|
|
label.get_height(),
|
|
0.8*brace.get_height()
|
|
))
|
|
|
|
result.digest_mobject_attrs()
|
|
return result
|
|
|
|
def get_delta_group(self, delta):
|
|
result = VGroup()
|
|
brace = Brace(Line(ORIGIN, RIGHT), DOWN)
|
|
brace.set_width(
|
|
(self.coords_to_point(delta, 0)-self.graph_origin)[0]
|
|
)
|
|
result.braces = VGroup(*[
|
|
brace.copy().move_to(self.coords_to_point(x, 0))
|
|
for x in (-delta/2, delta/2)
|
|
])
|
|
result.braces.shift(self.holes[0].get_height()*DOWN)
|
|
result.labels = VGroup(*[
|
|
TexMobject("\\delta").scale(
|
|
1./self.zoom_factor
|
|
)
|
|
for brace in result.braces
|
|
])
|
|
for label, brace in zip(result.labels, result.braces):
|
|
label.next_to(
|
|
brace, DOWN,
|
|
buff = SMALL_BUFF/self.zoom_factor
|
|
)
|
|
|
|
result.digest_mobject_attrs()
|
|
return result
|
|
|
|
class EpsilonDeltaCounterExample(LimitCounterExample, EpsilonDeltaExample):
|
|
def construct(self):
|
|
self.hole_radius = 0.04
|
|
self.add_func()
|
|
self.setup_axes()
|
|
self.draw_graph()
|
|
self.introduce_epsilon()
|
|
self.introduce_epsilon_delta_group()
|
|
self.move_epsilon_group_up_and_down()
|
|
|
|
def introduce_epsilon(self):
|
|
epsilon_group = self.get_epsilon_group(0.4, 1.5)
|
|
rhs = TexMobject("=0.4")
|
|
label = epsilon_group.labels[1]
|
|
rhs.next_to(label, RIGHT)
|
|
epsilon_group.add(rhs)
|
|
|
|
self.play(ShowCreation(epsilon_group.limit_line))
|
|
self.play(*it.chain(
|
|
[
|
|
ReplacementTransform(
|
|
epsilon_group.limit_line.copy(),
|
|
line
|
|
)
|
|
for line in epsilon_group.epsilon_lines
|
|
],
|
|
list(map(GrowFromCenter, epsilon_group.braces))
|
|
))
|
|
self.play(*list(map(Write, epsilon_group.labels)))
|
|
self.play(Write(rhs))
|
|
self.wait()
|
|
|
|
self.epsilon_group = epsilon_group
|
|
|
|
def introduce_epsilon_delta_group(self):
|
|
ed_group = self.get_epsilon_delta_group(self.big_delta)
|
|
|
|
self.play(*list(map(ShowCreation, ed_group.delta_lines)))
|
|
self.play(ShowCreation(ed_group.input_range))
|
|
self.play(ReplacementTransform(
|
|
ed_group.input_range.copy(),
|
|
ed_group.output_range,
|
|
run_time = 2
|
|
))
|
|
self.remove(self.graph_holes)
|
|
self.play(*list(map(GrowFromCenter, ed_group.epsilon_lines)))
|
|
self.wait(2)
|
|
self.animate_epsilon_delta_group_change(
|
|
ed_group, target_delta = self.small_delta,
|
|
run_time = 3
|
|
)
|
|
ed_group.delta = self.small_delta
|
|
self.wait()
|
|
|
|
self.ed_group = ed_group
|
|
|
|
def move_epsilon_group_up_and_down(self):
|
|
vects = [
|
|
self.coords_to_point(0, 2) - self.coords_to_point(0, 1.5),
|
|
self.coords_to_point(0, 1) - self.coords_to_point(0, 2),
|
|
self.coords_to_point(0, 1.5) - self.coords_to_point(0, 1),
|
|
]
|
|
for vect in vects:
|
|
self.play(self.epsilon_group.shift, vect)
|
|
self.wait()
|
|
self.shake_ed_group()
|
|
self.wait()
|
|
|
|
##
|
|
|
|
def shake_ed_group(self):
|
|
self.animate_epsilon_delta_group_change(
|
|
self.ed_group, target_delta = self.big_delta,
|
|
rate_func = lambda t : 0.2*there_and_back(2*t%1)
|
|
)
|
|
|
|
class TheoryHeavy(TeacherStudentsScene):
|
|
def construct(self):
|
|
lhs = TexMobject(
|
|
"{df", "\\over \\,", "dx}", "(", "x", ")"
|
|
)
|
|
equals = TexMobject("=")
|
|
rhs = TexMobject(
|
|
"\\lim", "_{h", "\\to 0}",
|
|
"{f", "(", "x", "+", "h", ")", "-", "f", "(", "x", ")",
|
|
"\\over \\,", "h}"
|
|
)
|
|
derivative = VGroup(lhs, equals, rhs)
|
|
derivative.arrange(RIGHT)
|
|
for tex_mob in derivative:
|
|
tex_mob.set_color_by_tex("x", RED)
|
|
tex_mob.set_color_by_tex("h", GREEN)
|
|
tex_mob.set_color_by_tex("dx", GREEN)
|
|
tex_mob.set_color_by_tex("f", YELLOW)
|
|
derivative.next_to(self.get_pi_creatures(), UP, buff = MED_LARGE_BUFF)
|
|
|
|
lim = rhs.get_part_by_tex("lim")
|
|
epsilon_delta = TexMobject("(\\epsilon, \\delta)")
|
|
epsilon_delta.next_to(lim, UP, buff = 1.5*LARGE_BUFF)
|
|
arrow = Arrow(epsilon_delta, lim, color = WHITE)
|
|
|
|
|
|
self.student_says(
|
|
"Too much theory!",
|
|
target_mode = "angry",
|
|
content_introduction_kwargs = {"run_time" : 2},
|
|
)
|
|
self.wait()
|
|
student = self.get_students()[1]
|
|
Scene.play(self,
|
|
Write(lhs),
|
|
FadeOut(student.bubble),
|
|
FadeOut(student.bubble.content),
|
|
*[
|
|
ApplyFunction(
|
|
lambda pi : pi.change_mode("pondering").look_at(epsilon_delta),
|
|
pi
|
|
)
|
|
for pi in self.get_pi_creatures()
|
|
]
|
|
)
|
|
student.bubble = None
|
|
part_tex_pairs = [
|
|
("df", "f"),
|
|
("over", "+"),
|
|
("over", "-"),
|
|
("over", "to"),
|
|
("over", "over"),
|
|
("dx", "h"),
|
|
("(", "("),
|
|
("x", "x"),
|
|
(")", ")"),
|
|
]
|
|
self.play(Write(equals), Write(lim), *[
|
|
ReplacementTransform(
|
|
VGroup(*lhs.get_parts_by_tex(t1)).copy(),
|
|
VGroup(*rhs.get_parts_by_tex(t2)),
|
|
run_time = 2,
|
|
rate_func = squish_rate_func(smooth, alpha, alpha+0.5)
|
|
)
|
|
for (t1, t2), alpha in zip(
|
|
part_tex_pairs,
|
|
np.linspace(0, 0.5, len(part_tex_pairs))
|
|
)
|
|
])
|
|
self.wait(2)
|
|
self.play(
|
|
Write(epsilon_delta),
|
|
ShowCreation(arrow)
|
|
)
|
|
self.wait(3)
|
|
derivative.add(epsilon_delta, arrow)
|
|
self.student_says(
|
|
"How do you \\\\ compute limits?",
|
|
student_index = 2,
|
|
added_anims = [
|
|
derivative.scale, 0.8,
|
|
derivative.to_corner, UP+LEFT
|
|
]
|
|
)
|
|
self.play(self.get_teacher().change_mode, "happy")
|
|
self.wait(2)
|
|
|
|
class LHopitalExample(LimitCounterExample, PiCreatureScene, ZoomedScene, ReconfigurableScene):
|
|
CONFIG = {
|
|
"graph_origin" : ORIGIN,
|
|
"x_axis_width" : FRAME_WIDTH,
|
|
"x_min" : -5,
|
|
"x_max" : 5,
|
|
"x_labeled_nums" : list(range(-6, 8, 2)),
|
|
"x_axis_label" : "$x$",
|
|
"y_axis_height" : FRAME_HEIGHT,
|
|
"y_min" : -3.1,
|
|
"y_max" : 3.1,
|
|
"y_bottom_tick" : -4,
|
|
"y_labeled_nums" : list(range(-2, 4, 2)),
|
|
"y_axis_label" : "",
|
|
"x_color" : RED,
|
|
"hole_radius" : 0.07,
|
|
"big_delta" : 0.5,
|
|
"small_delta" : 0.01,
|
|
"dx" : 0.06,
|
|
"dx_color" : WHITE,
|
|
"tex_scale_value" : 0.9,
|
|
"sin_color" : GREEN,
|
|
"parabola_color" : YELLOW,
|
|
"zoomed_canvas_corner" : DOWN+LEFT,
|
|
"zoom_factor" : 10,
|
|
"zoomed_canvas_frame_shape" : (5, 5),
|
|
"zoomed_canvas_corner_buff" : MED_SMALL_BUFF,
|
|
"zoomed_rect_center_coords" : (1 + 0.1, -0.03),
|
|
}
|
|
def construct(self):
|
|
self.setup_axes()
|
|
self.introduce_function()
|
|
self.show_non_definedness_at_one()
|
|
self.plug_in_value_close_to_one()
|
|
self.ask_about_systematic_process()
|
|
self.show_graph_of_numerator_and_denominator()
|
|
self.zoom_in_to_trouble_point()
|
|
self.talk_through_sizes_of_nudges()
|
|
self.show_final_ratio()
|
|
self.show_final_height()
|
|
|
|
def setup(self):
|
|
PiCreatureScene.setup(self)
|
|
ZoomedScene.setup(self)
|
|
ReconfigurableScene.setup(self)
|
|
self.remove(*self.get_pi_creatures())
|
|
|
|
def setup_axes(self):
|
|
GraphScene.setup_axes(self)
|
|
self.x_axis_label_mob.set_color(self.x_color)
|
|
|
|
def introduce_function(self):
|
|
graph = self.get_graph(self.func)
|
|
colored_graph = graph.copy().set_color(YELLOW)
|
|
func_label = self.get_func_label()
|
|
func_label.next_to(ORIGIN, RIGHT, buff = LARGE_BUFF)
|
|
func_label.to_edge(UP)
|
|
|
|
x_copy = self.x_axis_label_mob.copy()
|
|
|
|
self.play(
|
|
Write(func_label),
|
|
Transform(
|
|
x_copy, VGroup(*func_label.get_parts_by_tex("x")),
|
|
remover = True
|
|
)
|
|
)
|
|
self.play(ShowCreation(
|
|
graph,
|
|
run_time = 3,
|
|
rate_func=linear
|
|
))
|
|
self.wait(4) ## Overly oscillation
|
|
self.play(ShowCreation(colored_graph, run_time = 2))
|
|
self.wait()
|
|
self.play(ShowCreation(graph, run_time = 2))
|
|
self.remove(colored_graph)
|
|
self.wait()
|
|
|
|
self.graph = graph
|
|
self.func_label = func_label
|
|
|
|
def show_non_definedness_at_one(self):
|
|
morty = self.get_primary_pi_creature()
|
|
words = TexMobject("\\text{Try }", "x", "=1")
|
|
words.set_color_by_tex("x", self.x_color, substring = False)
|
|
|
|
v_line, alt_v_line = [
|
|
self.get_vertical_line_to_graph(
|
|
x, self.graph,
|
|
line_class = DashedLine,
|
|
color = self.x_color
|
|
)
|
|
for x in (1, -1)
|
|
]
|
|
hole, alt_hole = [
|
|
self.get_hole(x, self.func(x))
|
|
for x in (1, -1)
|
|
]
|
|
ed_group = self.get_epsilon_delta_group(
|
|
self.big_delta, limit_x = 1,
|
|
)
|
|
|
|
func_1 = self.get_func_label("1")
|
|
func_1.next_to(self.func_label, DOWN, buff = MED_LARGE_BUFF)
|
|
rhs = TexMobject("\\Rightarrow \\frac{0}{0}")
|
|
rhs.next_to(func_1, RIGHT)
|
|
func_1_group = VGroup(func_1, *rhs)
|
|
func_1_group.add_to_back(BackgroundRectangle(func_1_group))
|
|
|
|
lim = TexMobject("\\lim", "_{x", "\\to 1}")
|
|
lim.set_color_by_tex("x", self.x_color)
|
|
lim.move_to(self.func_label, LEFT)
|
|
self.func_label.generate_target()
|
|
self.func_label.target.next_to(lim, RIGHT)
|
|
equals_q = TexMobject("=", "???")
|
|
equals_q.next_to(self.func_label.target, RIGHT)
|
|
|
|
self.play(FadeIn(morty))
|
|
self.play(PiCreatureSays(morty, words))
|
|
self.play(
|
|
Blink(morty),
|
|
ShowCreation(v_line)
|
|
)
|
|
self.play(
|
|
RemovePiCreatureBubble(
|
|
morty, target_mode = "pondering",
|
|
look_at_arg = func_1
|
|
),
|
|
ReplacementTransform(
|
|
self.func_label.copy(),
|
|
func_1
|
|
)
|
|
)
|
|
self.wait(2)
|
|
self.play(Write(VGroup(*rhs[:-1])))
|
|
self.wait()
|
|
self.play(Write(rhs[-1]))
|
|
self.wait()
|
|
self.play(GrowFromCenter(hole))
|
|
self.wait()
|
|
|
|
self.play(ShowCreation(alt_v_line))
|
|
self.play(GrowFromCenter(alt_hole))
|
|
self.wait()
|
|
alt_group = VGroup(alt_v_line, alt_hole)
|
|
self.play(alt_group.set_stroke, GREY, 2)
|
|
self.play(FocusOn(hole))
|
|
self.wait()
|
|
|
|
self.play(GrowFromCenter(ed_group.input_range))
|
|
self.play(
|
|
ReplacementTransform(
|
|
ed_group.input_range.copy(),
|
|
ed_group.output_range
|
|
),
|
|
*list(map(ShowCreation, ed_group.delta_lines))
|
|
)
|
|
self.play(*list(map(GrowFromCenter, ed_group.epsilon_lines)))
|
|
self.play(morty.change_mode, "thinking")
|
|
self.animate_epsilon_delta_group_change(
|
|
ed_group, target_delta = self.small_delta,
|
|
run_time = 4
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Write(lim),
|
|
MoveToTarget(self.func_label),
|
|
Write(equals_q),
|
|
morty.change_mode, "confused",
|
|
morty.look_at, lim
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
func_1_group.to_corner, UP+LEFT,
|
|
*list(map(FadeOut, [morty, ed_group]))
|
|
)
|
|
self.wait()
|
|
|
|
self.lim_group = VGroup(lim, self.func_label, equals_q)
|
|
for part in self.lim_group:
|
|
part.add_background_rectangle()
|
|
self.func_1_group = func_1_group
|
|
self.v_line = v_line
|
|
|
|
def plug_in_value_close_to_one(self):
|
|
num = 1.00001
|
|
result = self.func(num)
|
|
label = self.get_func_label(num)
|
|
label.add_background_rectangle()
|
|
rhs = TexMobject("= %.4f\\dots"%result)
|
|
rhs.next_to(label, RIGHT)
|
|
approx_group = VGroup(label, rhs)
|
|
approx_group.set_width(FRAME_X_RADIUS-2*MED_LARGE_BUFF)
|
|
approx_group.next_to(ORIGIN, UP, buff = MED_LARGE_BUFF)
|
|
approx_group.to_edge(RIGHT)
|
|
|
|
self.play(ReplacementTransform(
|
|
self.func_label.copy(),
|
|
label
|
|
))
|
|
self.wait()
|
|
self.play(Write(rhs))
|
|
self.wait()
|
|
|
|
self.approx_group = approx_group
|
|
|
|
def ask_about_systematic_process(self):
|
|
morty = self.pi_creature
|
|
morty.change_mode("plain")
|
|
|
|
self.func_1_group.save_state()
|
|
to_fade = VGroup(
|
|
*self.x_axis.numbers[:len(self.x_axis.numbers)/2]
|
|
)
|
|
|
|
self.play(
|
|
FadeIn(morty),
|
|
FadeOut(to_fade)
|
|
)
|
|
self.x_axis.remove(*to_fade)
|
|
|
|
self.pi_creature_says(
|
|
morty, "Is there a \\\\ better way?",
|
|
bubble_kwargs = {
|
|
"height" : 3,
|
|
"width" : 4,
|
|
},
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
RemovePiCreatureBubble(
|
|
morty, target_mode = "raise_left_hand",
|
|
look_at_arg = self.func_1_group
|
|
),
|
|
self.func_1_group.scale, self.tex_scale_value,
|
|
self.func_1_group.move_to,
|
|
morty.get_corner(UP+LEFT), DOWN+LEFT,
|
|
self.func_1_group.shift, MED_LARGE_BUFF*UP,
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
morty.change_mode, "raise_right_hand",
|
|
morty.look, UP+RIGHT,
|
|
FadeOut(self.approx_group),
|
|
self.func_1_group.restore,
|
|
self.lim_group.next_to,
|
|
morty.get_corner(UP+RIGHT), RIGHT,
|
|
)
|
|
self.wait(2)
|
|
self.play(
|
|
FadeOut(self.func_1_group),
|
|
self.lim_group.scale, self.tex_scale_value,
|
|
self.lim_group.to_corner, UP+LEFT,
|
|
# self.lim_group.next_to, ORIGIN, UP, MED_LARGE_BUFF,
|
|
# self.lim_group.to_edge, LEFT,
|
|
morty.change_mode, "plain"
|
|
)
|
|
self.wait()
|
|
self.play(FadeOut(morty))
|
|
|
|
def show_graph_of_numerator_and_denominator(self):
|
|
sine_graph = self.get_graph(
|
|
lambda x : np.sin(np.pi*x),
|
|
color = self.sin_color
|
|
)
|
|
sine_label = self.get_graph_label(
|
|
sine_graph, "\\sin(\\pi x)",
|
|
x_val = 4.5,
|
|
direction = UP
|
|
)
|
|
parabola = self.get_graph(
|
|
lambda x : x**2 - 1,
|
|
color = self.parabola_color
|
|
)
|
|
parabola_label = self.get_graph_label(
|
|
parabola, "x^2 - 1"
|
|
)
|
|
fader = VGroup(*[
|
|
Rectangle(
|
|
width = FRAME_WIDTH,
|
|
height = FRAME_HEIGHT,
|
|
stroke_width = 0,
|
|
fill_opacity = 0.75,
|
|
fill_color = BLACK,
|
|
).next_to(self.coords_to_point(1, 0), vect, MED_LARGE_BUFF)
|
|
for vect in (LEFT, RIGHT)
|
|
])
|
|
|
|
self.play(
|
|
ShowCreation(sine_graph, run_time = 2),
|
|
Animation(self.lim_group)
|
|
)
|
|
self.play(FadeIn(sine_label))
|
|
self.wait()
|
|
self.play(ShowCreation(parabola, run_time = 2))
|
|
self.play(FadeIn(parabola_label))
|
|
self.wait()
|
|
self.play(FadeIn(fader, run_time = 2))
|
|
self.wait()
|
|
self.play(FadeOut(fader))
|
|
|
|
self.sine_graph = sine_graph
|
|
self.sine_label = sine_label
|
|
self.parabola = parabola
|
|
self.parabola_label = parabola_label
|
|
|
|
def zoom_in_to_trouble_point(self):
|
|
self.activate_zooming()
|
|
lil_rect = self.little_rectangle
|
|
lil_rect.scale(self.zoom_factor)
|
|
lil_rect.move_to(self.coords_to_point(
|
|
*self.zoomed_rect_center_coords
|
|
))
|
|
self.wait()
|
|
self.play(lil_rect.scale_in_place, 1./self.zoom_factor)
|
|
self.wait()
|
|
|
|
def talk_through_sizes_of_nudges(self):
|
|
arrow_tip_length = 0.15/self.zoom_factor
|
|
zoom_tex_scale_factor = min(
|
|
0.75/self.zoom_factor,
|
|
1.5*self.dx
|
|
)
|
|
|
|
dx_arrow = Arrow(
|
|
self.coords_to_point(1, 0),
|
|
self.coords_to_point(1+self.dx, 0),
|
|
tip_length = arrow_tip_length,
|
|
color = WHITE,
|
|
)
|
|
dx_label = TexMobject("dx")
|
|
dx_label.scale(zoom_tex_scale_factor)
|
|
dx_label.next_to(dx_arrow, UP, buff = SMALL_BUFF/self.zoom_factor)
|
|
|
|
d_sine_arrow, d_parabola_arrow = [
|
|
Arrow(
|
|
self.coords_to_point(1+self.dx, 0),
|
|
self.coords_to_point(
|
|
1+self.dx,
|
|
graph.underlying_function(1+self.dx)
|
|
),
|
|
tip_length = arrow_tip_length,
|
|
color = graph.get_color()
|
|
)
|
|
for graph in (self.sine_graph, self.parabola)
|
|
]
|
|
tex_arrow_pairs = [
|
|
[("d\\big(", "\\sin(", "\\pi", "x", ")", "\\big)"), d_sine_arrow],
|
|
[("d\\big(", "x", "^2", "-1", "\\big)"), d_parabola_arrow],
|
|
[("\\cos(", "\\pi", "x", ")", "\\pi ", "\\, dx"), d_sine_arrow],
|
|
[("2", "x", "\\, dx"), d_parabola_arrow],
|
|
]
|
|
d_labels = []
|
|
for tex_args, arrow in tex_arrow_pairs:
|
|
label = TexMobject(*tex_args)
|
|
label.set_color_by_tex("x", self.x_color)
|
|
label.set_color_by_tex("dx", self.dx_color)
|
|
label.scale(zoom_tex_scale_factor)
|
|
label.next_to(arrow, RIGHT, buff = SMALL_BUFF/self.zoom_factor)
|
|
d_labels.append(label)
|
|
d_sine_label, d_parabola_label, cos_dx, two_x_dx = d_labels
|
|
|
|
#Show dx
|
|
self.play(ShowCreation(dx_arrow))
|
|
self.play(Write(dx_label))
|
|
self.wait()
|
|
|
|
#Show d_sine bump
|
|
point = VectorizedPoint(self.coords_to_point(1, 0))
|
|
self.play(ReplacementTransform(point, d_sine_arrow))
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
VGroup(dx_label[1].copy()),
|
|
d_sine_label,
|
|
run_time = 2
|
|
))
|
|
self.wait(2)
|
|
self.play(
|
|
d_sine_label.shift, d_sine_label.get_height()*UP
|
|
)
|
|
tex_pair_lists = [
|
|
[
|
|
("sin", "cos"),
|
|
("pi", "pi"),
|
|
("x", "x"),
|
|
(")", ")"),
|
|
],
|
|
[
|
|
("pi", "\\pi "), #Space there is important, though hacky
|
|
],
|
|
[
|
|
("d\\big(", "dx"),
|
|
("\\big)", "dx"),
|
|
]
|
|
]
|
|
for tex_pairs in tex_pair_lists:
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
d_sine_label.get_part_by_tex(t1).copy(),
|
|
cos_dx.get_part_by_tex(t2)
|
|
)
|
|
for t1, t2 in tex_pairs
|
|
])
|
|
self.wait()
|
|
self.play(FadeOut(d_sine_label))
|
|
self.wait()
|
|
|
|
#Substitute x = 1
|
|
self.substitute_x_equals_1(cos_dx, zoom_tex_scale_factor)
|
|
|
|
#Proportionality constant
|
|
cos_pi = VGroup(*cos_dx[:-1])
|
|
cos = VGroup(*cos_dx[:-2])
|
|
brace = Brace(Line(LEFT, RIGHT), UP)
|
|
brace.set_width(cos_pi.get_width())
|
|
brace.move_to(cos_pi.get_top(), DOWN)
|
|
brace_text = TextMobject(
|
|
"""
|
|
\\begin{flushleft}
|
|
Proportionality
|
|
constant
|
|
\\end{flushleft}
|
|
"""
|
|
)
|
|
brace_text.scale(0.9*zoom_tex_scale_factor)
|
|
brace_text.add_background_rectangle()
|
|
brace_text.next_to(brace, UP, SMALL_BUFF/self.zoom_factor, LEFT)
|
|
neg_one = TexMobject("-", "1")
|
|
neg_one.add_background_rectangle()
|
|
neg_one.scale(zoom_tex_scale_factor)
|
|
|
|
self.play(GrowFromCenter(brace))
|
|
self.play(Write(brace_text))
|
|
self.wait(2)
|
|
self.play(
|
|
brace.set_width, cos.get_width(),
|
|
brace.next_to, cos, UP, SMALL_BUFF/self.zoom_factor,
|
|
FadeOut(brace_text)
|
|
)
|
|
neg_one.next_to(brace, UP, SMALL_BUFF/self.zoom_factor)
|
|
self.play(Write(neg_one))
|
|
self.wait()
|
|
self.play(FadeOut(cos))
|
|
neg = neg_one.get_part_by_tex("-").copy()
|
|
self.play(neg.next_to, cos_dx[-2], LEFT, SMALL_BUFF/self.zoom_factor)
|
|
self.play(*list(map(FadeOut, [neg_one, brace])))
|
|
neg_pi_dx = VGroup(neg, *cos_dx[-2:])
|
|
self.play(
|
|
neg_pi_dx.next_to, d_sine_arrow,
|
|
RIGHT, SMALL_BUFF/self.zoom_factor
|
|
)
|
|
self.wait()
|
|
|
|
#Show d_parabola bump
|
|
point = VectorizedPoint(self.coords_to_point(1, 0))
|
|
self.play(ReplacementTransform(point, d_parabola_arrow))
|
|
self.play(ReplacementTransform(
|
|
VGroup(dx_label[1].copy()),
|
|
d_parabola_label,
|
|
run_time = 2
|
|
))
|
|
self.wait(2)
|
|
self.play(
|
|
d_parabola_label.shift, d_parabola_label.get_height()*UP
|
|
)
|
|
tex_pair_lists = [
|
|
[
|
|
("2", "2"),
|
|
("x", "x"),
|
|
],
|
|
[
|
|
("d\\big(", "dx"),
|
|
("\\big)", "dx"),
|
|
]
|
|
]
|
|
for tex_pairs in tex_pair_lists:
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
d_parabola_label.get_part_by_tex(t1).copy(),
|
|
two_x_dx.get_part_by_tex(t2)
|
|
)
|
|
for t1, t2 in tex_pairs
|
|
])
|
|
self.wait()
|
|
self.play(FadeOut(d_parabola_label))
|
|
self.wait()
|
|
|
|
#Substitute x = 1
|
|
self.substitute_x_equals_1(two_x_dx, zoom_tex_scale_factor)
|
|
|
|
def substitute_x_equals_1(self, tex_mob, zoom_tex_scale_factor):
|
|
x = tex_mob.get_part_by_tex("x")
|
|
equation = TexMobject("x", "=", "1")
|
|
eq_x, equals, one = equation
|
|
equation.scale(zoom_tex_scale_factor)
|
|
equation.next_to(
|
|
x, UP,
|
|
buff = MED_SMALL_BUFF/self.zoom_factor,
|
|
aligned_edge = LEFT
|
|
)
|
|
equation.set_color_by_tex("x", self.x_color)
|
|
equation.set_color_by_tex("1", self.x_color)
|
|
dot_one = TexMobject("\\cdot", "1")
|
|
dot_one.scale(zoom_tex_scale_factor)
|
|
dot_one.set_color(self.x_color)
|
|
dot_one.move_to(x, DOWN+LEFT)
|
|
|
|
self.play(x.move_to, eq_x)
|
|
self.wait()
|
|
self.play(
|
|
ReplacementTransform(x.copy(), eq_x),
|
|
Transform(x, one),
|
|
Write(equals)
|
|
)
|
|
self.wait()
|
|
self.play(Transform(x, dot_one))
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [eq_x, equals])))
|
|
self.wait()
|
|
|
|
def show_final_ratio(self):
|
|
lim, ratio, equals_q = self.lim_group
|
|
self.remove(self.lim_group)
|
|
self.add(*self.lim_group)
|
|
numerator = VGroup(*ratio[1][:3])
|
|
denominator = VGroup(*ratio[1][-2:])
|
|
rhs = TexMobject(
|
|
"\\approx",
|
|
"{-\\pi", "\\, dx", "\\over \\,", "2", "\\, dx}"
|
|
)
|
|
rhs.add_background_rectangle()
|
|
rhs.move_to(equals_q, LEFT)
|
|
equals = TexMobject("=")
|
|
approx = rhs.get_part_by_tex("approx")
|
|
equals.move_to(approx)
|
|
|
|
dxs = VGroup(*rhs.get_parts_by_tex("dx"))
|
|
circles = VGroup(*[
|
|
Circle(color = GREEN).replace(dx).scale_in_place(1.3)
|
|
for dx in dxs
|
|
])
|
|
|
|
#Show numerator and denominator
|
|
self.play(FocusOn(ratio))
|
|
for mob in numerator, denominator:
|
|
self.play(ApplyWave(
|
|
mob, direction = UP+RIGHT, amplitude = 0.1
|
|
))
|
|
self.wait()
|
|
self.play(ReplacementTransform(equals_q, rhs))
|
|
self.wait()
|
|
|
|
#Cancel dx's
|
|
self.play(*list(map(ShowCreation, circles)), run_time = 2)
|
|
self.wait()
|
|
self.play(dxs.fade, 0.75, FadeOut(circles))
|
|
self.wait()
|
|
|
|
#Shrink dx
|
|
self.transition_to_alt_config(
|
|
transformation_kwargs = {"run_time" : 2},
|
|
dx = self.dx/10
|
|
)
|
|
self.wait()
|
|
self.play(Transform(approx, equals))
|
|
self.play(Indicate(approx))
|
|
self.wait()
|
|
|
|
self.final_ratio = rhs
|
|
|
|
def show_final_height(self):
|
|
brace = Brace(self.v_line, LEFT)
|
|
height = brace.get_text("$\\dfrac{-\\pi}{2}$")
|
|
height.add_background_rectangle()
|
|
|
|
self.disactivate_zooming()
|
|
self.play(*list(map(FadeOut, [
|
|
self.sine_graph, self.sine_label,
|
|
self.parabola, self.parabola_label,
|
|
])) + [
|
|
Animation(self.final_ratio)
|
|
])
|
|
self.play(GrowFromCenter(brace))
|
|
self.play(Write(height))
|
|
self.wait(2)
|
|
|
|
##
|
|
|
|
def create_pi_creature(self):
|
|
self.pi_creature = Mortimer().flip().to_corner(DOWN+LEFT)
|
|
return self.pi_creature
|
|
|
|
def func(self, x):
|
|
if abs(x) != 1:
|
|
return np.sin(x*np.pi) / (x**2 - 1)
|
|
else:
|
|
return np.pi*np.cos(x*np.pi) / (2*x)
|
|
|
|
def get_func_label(self, argument = "x"):
|
|
in_tex = "{%s}"%str(argument)
|
|
result = TexMobject(
|
|
"{\\sin(\\pi ", in_tex, ")", " \\over \\,",
|
|
in_tex, "^2 - 1}"
|
|
)
|
|
result.set_color_by_tex(in_tex, self.x_color)
|
|
return result
|
|
|
|
def get_epsilon_delta_group(self, delta, **kwargs):
|
|
ed_group = GraphLimitExpression.get_epsilon_delta_group(self, delta, **kwargs)
|
|
color = ed_group.kwargs["input_range_color"]
|
|
radius = min(delta/2, self.hole_radius)
|
|
pairs = [
|
|
# (ed_group.input_range[0], (1, 0)),
|
|
(ed_group.input_range[1], (1, 0)),
|
|
# (ed_group.output_range[0], (1, self.func(1))),
|
|
(ed_group.output_range[1], (1, self.func(1))),
|
|
]
|
|
for mob, coords in pairs:
|
|
mob.add(self.get_hole(
|
|
*coords,
|
|
color = color,
|
|
radius = radius
|
|
))
|
|
return ed_group
|
|
|
|
class DerivativeLimitReciprocity(Scene):
|
|
def construct(self):
|
|
arrow = Arrow(LEFT, RIGHT, color = WHITE)
|
|
lim = TexMobject("\\lim", "_{h", "\\to 0}")
|
|
lim.set_color_by_tex("h", GREEN)
|
|
lim.next_to(arrow, LEFT)
|
|
deriv = TexMobject("{df", "\\over\\,", "dx}")
|
|
deriv.set_color_by_tex("dx", GREEN)
|
|
deriv.set_color_by_tex("df", YELLOW)
|
|
deriv.next_to(arrow, RIGHT)
|
|
|
|
self.play(FadeIn(lim, lag_ratio = 0.5))
|
|
self.play(ShowCreation(arrow))
|
|
self.play(FadeIn(deriv, lag_ratio = 0.5))
|
|
self.wait()
|
|
self.play(Rotate(arrow, np.pi, run_time = 2))
|
|
self.wait()
|
|
|
|
class GeneralLHoptial(LHopitalExample):
|
|
CONFIG = {
|
|
"f_color" : BLUE,
|
|
"g_color" : YELLOW,
|
|
"a_value" : 2.5,
|
|
"zoomed_rect_center_coords" : (2.55, 0),
|
|
"zoom_factor" : 15,
|
|
"image_height" : 3,
|
|
}
|
|
def construct(self):
|
|
self.setup_axes()
|
|
self.add_graphs()
|
|
self.zoom_in()
|
|
self.show_limit_in_symbols()
|
|
self.show_tiny_nudge()
|
|
self.show_derivative_ratio()
|
|
self.show_example()
|
|
self.show_bernoulli_and_lHopital()
|
|
|
|
def add_graphs(self):
|
|
f_graph = self.get_graph(self.f, self.f_color)
|
|
f_label = self.get_graph_label(
|
|
f_graph, "f(x)",
|
|
x_val = 3,
|
|
direction = RIGHT
|
|
)
|
|
g_graph = ParametricFunction(
|
|
lambda y : self.coords_to_point(np.exp(y)+self.a_value-1, y),
|
|
t_min = self.y_min,
|
|
t_max = self.y_max,
|
|
color = self.g_color
|
|
)
|
|
g_graph.underlying_function = self.g
|
|
g_label = self.get_graph_label(
|
|
g_graph, "g(x)", x_val = 4, direction = UP
|
|
)
|
|
|
|
a_dot = Dot(self.coords_to_point(self.a_value, 0))
|
|
a_label = TexMobject("x = a")
|
|
a_label.next_to(a_dot, UP, LARGE_BUFF)
|
|
a_arrow = Arrow(a_label.get_bottom(), a_dot, buff = SMALL_BUFF)
|
|
VGroup(a_dot, a_label, a_arrow).set_color(self.x_color)
|
|
|
|
self.play(ShowCreation(f_graph), Write(f_label))
|
|
self.play(ShowCreation(g_graph), Write(g_label))
|
|
self.wait()
|
|
|
|
self.play(
|
|
Write(a_label),
|
|
ShowCreation(a_arrow),
|
|
ShowCreation(a_dot),
|
|
)
|
|
self.wait()
|
|
self.play(*list(map(FadeOut, [a_label, a_arrow])))
|
|
|
|
self.a_dot = a_dot
|
|
self.f_graph = f_graph
|
|
self.f_label = f_label
|
|
self.g_graph = g_graph
|
|
self.g_label = g_label
|
|
|
|
def zoom_in(self):
|
|
self.activate_zooming()
|
|
lil_rect = self.little_rectangle
|
|
lil_rect.scale(self.zoom_factor)
|
|
lil_rect.move_to(self.coords_to_point(
|
|
*self.zoomed_rect_center_coords
|
|
))
|
|
self.wait()
|
|
self.play(
|
|
lil_rect.scale_in_place, 1./self.zoom_factor,
|
|
self.a_dot.scale_in_place, 1./self.zoom_factor,
|
|
run_time = 3,
|
|
)
|
|
self.wait()
|
|
|
|
def show_limit_in_symbols(self):
|
|
frac_a = self.get_frac("a", self.x_color)
|
|
frac_x = self.get_frac("x")
|
|
lim = TexMobject("\\lim", "_{x", "\\to", "a}")
|
|
lim.set_color_by_tex("a", self.x_color)
|
|
equals_zero_over_zero = TexMobject(
|
|
"=", "{\\, 0 \\,", "\\over \\,", "0 \\,}"
|
|
)
|
|
equals_q = TexMobject(*"=???")
|
|
frac_x.next_to(lim, RIGHT, SMALL_BUFF)
|
|
VGroup(lim, frac_x).to_corner(UP+LEFT)
|
|
frac_a.move_to(frac_x)
|
|
equals_zero_over_zero.next_to(frac_a, RIGHT)
|
|
equals_q.next_to(frac_a, RIGHT)
|
|
|
|
self.play(
|
|
ReplacementTransform(
|
|
VGroup(*self.f_label).copy(),
|
|
VGroup(frac_a.numerator)
|
|
),
|
|
ReplacementTransform(
|
|
VGroup(*self.g_label).copy(),
|
|
VGroup(frac_a.denominator)
|
|
),
|
|
Write(frac_a.over),
|
|
run_time = 2
|
|
)
|
|
self.wait()
|
|
self.play(Write(equals_zero_over_zero))
|
|
self.wait(2)
|
|
self.play(
|
|
ReplacementTransform(
|
|
VGroup(*frac_a.get_parts_by_tex("a")),
|
|
VGroup(lim.get_part_by_tex("a"))
|
|
)
|
|
)
|
|
self.play(Write(VGroup(*lim[:-1])))
|
|
self.play(ReplacementTransform(
|
|
VGroup(*lim.get_parts_by_tex("x")).copy(),
|
|
VGroup(*frac_x.get_parts_by_tex("x"))
|
|
))
|
|
self.play(ReplacementTransform(
|
|
equals_zero_over_zero, equals_q
|
|
))
|
|
self.wait()
|
|
|
|
self.remove(frac_a)
|
|
self.add(frac_x)
|
|
self.frac_x = frac_x
|
|
self.remove(equals_q)
|
|
self.add(*equals_q)
|
|
self.equals_q = equals_q
|
|
|
|
def show_tiny_nudge(self):
|
|
arrow_tip_length = 0.15/self.zoom_factor
|
|
zoom_tex_scale_factor = min(
|
|
0.75/self.zoom_factor,
|
|
1.5*self.dx
|
|
)
|
|
z_small_buff = SMALL_BUFF/self.zoom_factor
|
|
|
|
dx_arrow = Arrow(
|
|
self.coords_to_point(self.a_value, 0),
|
|
self.coords_to_point(self.a_value+self.dx, 0),
|
|
tip_length = arrow_tip_length,
|
|
color = WHITE,
|
|
)
|
|
dx_label = TexMobject("dx")
|
|
dx_label.scale(zoom_tex_scale_factor)
|
|
dx_label.next_to(dx_arrow, UP, buff = z_small_buff)
|
|
dx_label.shift(z_small_buff*RIGHT)
|
|
|
|
df_arrow, dg_arrow = [
|
|
Arrow(
|
|
self.coords_to_point(self.a_value+self.dx, 0),
|
|
self.coords_to_point(
|
|
self.a_value+self.dx,
|
|
graph.underlying_function(self.a_value+self.dx)
|
|
),
|
|
tip_length = arrow_tip_length,
|
|
color = graph.get_color()
|
|
)
|
|
for graph in (self.f_graph, self.g_graph)
|
|
]
|
|
v_labels = []
|
|
for char, arrow in ("f", df_arrow), ("g", dg_arrow):
|
|
label = TexMobject(
|
|
"\\frac{d%s}{dx}"%char, "(", "a", ")", "\\,dx"
|
|
)
|
|
label.scale(zoom_tex_scale_factor)
|
|
label.set_color_by_tex("a", self.x_color)
|
|
label.set_color_by_tex("frac", arrow.get_color())
|
|
label.next_to(arrow, RIGHT, z_small_buff)
|
|
v_labels.append(label)
|
|
df_label, dg_label = v_labels
|
|
|
|
self.play(ShowCreation(dx_arrow))
|
|
self.play(Write(dx_label))
|
|
self.play(Indicate(dx_label))
|
|
self.wait(2)
|
|
|
|
self.play(ShowCreation(df_arrow))
|
|
self.play(Write(df_label))
|
|
self.wait()
|
|
|
|
self.play(ShowCreation(dg_arrow))
|
|
self.play(Write(dg_label))
|
|
self.wait()
|
|
|
|
def show_derivative_ratio(self):
|
|
q_marks = VGroup(*self.equals_q[1:])
|
|
|
|
deriv_ratio = TexMobject(
|
|
"{ \\frac{df}{dx}", "(", "a", ")", "\\,dx",
|
|
"\\over \\,",
|
|
"\\frac{dg}{dx}", "(", "a", ")", "\\,dx}",
|
|
)
|
|
deriv_ratio.set_color_by_tex("a", self.x_color)
|
|
deriv_ratio.set_color_by_tex("df", self.f_color)
|
|
deriv_ratio.set_color_by_tex("dg", self.g_color)
|
|
deriv_ratio.move_to(q_marks, LEFT)
|
|
|
|
dxs = VGroup(*deriv_ratio.get_parts_by_tex("\\,dx"))
|
|
circles = VGroup(*[
|
|
Circle(color = GREEN).replace(dx).scale_in_place(1.3)
|
|
for dx in dxs
|
|
])
|
|
|
|
self.play(FadeOut(q_marks))
|
|
self.play(Write(deriv_ratio))
|
|
self.wait(2)
|
|
|
|
self.play(FadeIn(circles))
|
|
self.wait()
|
|
self.play(FadeOut(circles), dxs.fade, 0.75)
|
|
self.wait(2)
|
|
|
|
self.transition_to_alt_config(
|
|
transformation_kwargs = {"run_time" : 2},
|
|
dx = self.dx/10,
|
|
)
|
|
self.wait()
|
|
|
|
def show_example(self):
|
|
lhs = TexMobject(
|
|
"\\lim", "_{x \\to", "0}",
|
|
"{\\sin(", "x", ")", "\\over \\,", "x}",
|
|
)
|
|
rhs = TexMobject(
|
|
"=",
|
|
"{\\cos(", "0", ")", "\\over \\,", "1}",
|
|
"=", "1"
|
|
)
|
|
rhs.next_to(lhs, RIGHT)
|
|
equation = VGroup(lhs, rhs)
|
|
equation.to_corner(UP+RIGHT)
|
|
for part in equation:
|
|
part.set_color_by_tex("0", self.x_color)
|
|
brace = Brace(lhs, DOWN)
|
|
brace_text = brace.get_text("Looks like 0/0")
|
|
brace_text.add_background_rectangle()
|
|
|
|
name = TextMobject(
|
|
"``", "L'Hôpital's", " rule", "''",
|
|
arg_separator = ""
|
|
)
|
|
name.shift(FRAME_X_RADIUS*RIGHT/2)
|
|
name.to_edge(UP)
|
|
|
|
self.play(Write(lhs))
|
|
self.wait()
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(brace_text)
|
|
)
|
|
self.wait()
|
|
self.play(Write(rhs[0]), ReplacementTransform(
|
|
VGroup(*lhs[3:6]).copy(),
|
|
VGroup(*rhs[1:4])
|
|
))
|
|
self.wait()
|
|
self.play(ReplacementTransform(
|
|
VGroup(*lhs[6:8]).copy(),
|
|
VGroup(*rhs[4:6]),
|
|
))
|
|
self.wait()
|
|
self.play(Write(VGroup(*rhs[6:])))
|
|
self.wait(2)
|
|
|
|
##Slide away
|
|
example = VGroup(lhs, rhs, brace, brace_text)
|
|
self.play(
|
|
example.scale, 0.7,
|
|
example.to_corner, DOWN+RIGHT, SMALL_BUFF,
|
|
path_arc = 7*np.pi/6,
|
|
)
|
|
self.play(Write(name))
|
|
self.wait(2)
|
|
|
|
self.rule_name = name
|
|
|
|
def show_bernoulli_and_lHopital(self):
|
|
lhopital_name = self.rule_name.get_part_by_tex("L'Hôpital's")
|
|
strike = Line(
|
|
lhopital_name.get_left(),
|
|
lhopital_name.get_right(),
|
|
color = RED
|
|
)
|
|
bernoulli_name = TextMobject("Bernoulli's")
|
|
bernoulli_name.next_to(lhopital_name, DOWN)
|
|
|
|
bernoulli_image = ImageMobject("Johann_Bernoulli2")
|
|
lhopital_image = ImageMobject("Guillaume_de_L'Hopital")
|
|
for image in bernoulli_image, lhopital_image:
|
|
image.set_height(self.image_height)
|
|
image.to_edge(UP)
|
|
|
|
arrow = Arrow(ORIGIN, DOWN, buff = 0, color = GREEN)
|
|
arrow.next_to(lhopital_image, DOWN, buff = SMALL_BUFF)
|
|
dollars = VGroup(*[TexMobject("\\$") for x in range(5)])
|
|
for dollar, alpha in zip(dollars, np.linspace(0, 1, len(dollars))):
|
|
angle = alpha*np.pi
|
|
dollar.move_to(np.sin(angle)*RIGHT + np.cos(angle)*UP)
|
|
dollars.set_color(GREEN)
|
|
dollars.next_to(arrow, RIGHT, MED_LARGE_BUFF)
|
|
dollars[0].set_fill(opacity = 0)
|
|
dollars.save_state()
|
|
|
|
self.play(ShowCreation(strike))
|
|
self.play(
|
|
Write(bernoulli_name),
|
|
FadeIn(bernoulli_image)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeIn(lhopital_image),
|
|
bernoulli_image.next_to, arrow, DOWN, SMALL_BUFF,
|
|
ShowCreation(arrow),
|
|
FadeIn(dollars)
|
|
)
|
|
|
|
for x in range(10):
|
|
dollars.restore()
|
|
self.play(*[
|
|
Transform(*pair)
|
|
for pair in zip(dollars, dollars[1:])
|
|
] + [
|
|
FadeOut(dollars[-1])
|
|
])
|
|
|
|
####
|
|
|
|
def f(self, x):
|
|
return -0.1*(x-self.a_value)*x*(x+4.5)
|
|
|
|
def g(self, x):
|
|
return np.log(x-self.a_value+1)
|
|
|
|
def get_frac(self, input_tex, color = WHITE):
|
|
result = TexMobject(
|
|
"{f", "(", input_tex, ")", "\\over \\,",
|
|
"g", "(", input_tex, ")}"
|
|
)
|
|
result.set_color_by_tex("f", self.f_color)
|
|
result.set_color_by_tex("g", self.g_color)
|
|
result.set_color_by_tex(input_tex, color)
|
|
|
|
result.numerator = VGroup(*result[:4])
|
|
result.denominator = VGroup(*result[-4:])
|
|
result.over = result.get_part_by_tex("over")
|
|
|
|
return result
|
|
|
|
class CannotUseLHopital(TeacherStudentsScene):
|
|
def construct(self):
|
|
deriv = TexMobject(
|
|
"{d(e^x)", "\\over \\,", "dx}", "(", "x", ")", "=",
|
|
"\\lim", "_{h", "\\to 0}",
|
|
"{e^{", "x", "+", "h}",
|
|
"-", "e^", "x",
|
|
"\\over \\,", "h}"
|
|
)
|
|
deriv.to_edge(UP)
|
|
deriv.set_color_by_tex("x", RED)
|
|
deriv.set_color_by_tex("dx", GREEN)
|
|
deriv.set_color_by_tex("h", GREEN)
|
|
deriv.set_color_by_tex("e^", YELLOW)
|
|
|
|
self.play(
|
|
Write(deriv),
|
|
*it.chain(*[
|
|
[pi.change_mode, "pondering", pi.look_at, deriv]
|
|
for pi in self.get_pi_creatures()
|
|
])
|
|
)
|
|
self.wait()
|
|
self.student_says(
|
|
"Use L'Hôpital's rule!",
|
|
target_mode = "hooray"
|
|
)
|
|
self.wait()
|
|
answer = TexMobject(
|
|
"\\text{That requires knowing }",
|
|
"{d(e^x)", "\\over \\,", "dx}"
|
|
)
|
|
answer.set_color_by_tex("e^", YELLOW)
|
|
answer.set_color_by_tex("dx", GREEN)
|
|
self.teacher_says(
|
|
answer,
|
|
bubble_kwargs = {"height" : 2.5},
|
|
target_mode = "hesitant"
|
|
)
|
|
self.change_student_modes(*["confused"]*3)
|
|
self.wait(3)
|
|
|
|
class NextVideo(TeacherStudentsScene):
|
|
def construct(self):
|
|
series = VideoSeries()
|
|
series.to_edge(UP)
|
|
next_video = series[7]
|
|
brace = Brace(next_video, DOWN)
|
|
|
|
integral = TexMobject("\\int", "f(x)", "dx")
|
|
ftc = TexMobject(
|
|
"F(b)", "-", "F(a)", "=", "\\int_a^b",
|
|
"{dF", "\\over \\,", "dx}", "(x)", "dx"
|
|
)
|
|
for tex_mob in integral, ftc:
|
|
tex_mob.set_color_by_tex("dx", GREEN)
|
|
tex_mob.set_color_by_tex("f", YELLOW)
|
|
tex_mob.set_color_by_tex("F", YELLOW)
|
|
tex_mob.next_to(brace, DOWN)
|
|
|
|
self.add(series)
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
next_video.set_color, YELLOW,
|
|
self.get_teacher().change_mode, "raise_right_hand",
|
|
self.get_teacher().look_at, next_video
|
|
)
|
|
self.play(Write(integral))
|
|
self.wait(2)
|
|
self.play(*[
|
|
ReplacementTransform(
|
|
VGroup(*integral.get_parts_by_tex(p1)),
|
|
VGroup(*ftc.get_parts_by_tex(p2)),
|
|
run_time = 2,
|
|
path_arc = np.pi/2,
|
|
rate_func = squish_rate_func(smooth, alpha, alpha+0.5)
|
|
)
|
|
for alpha, (p1, p2) in zip(np.linspace(0, 0.5, 3), [
|
|
("int", "int"),
|
|
("f", "F"),
|
|
("dx", "dx"),
|
|
])
|
|
]+[
|
|
Write(VGroup(*ftc.get_parts_by_tex(part)))
|
|
for part in ("-", "=", "over", "(x)")
|
|
])
|
|
self.change_student_modes(*["pondering"]*3)
|
|
self.wait(3)
|
|
|
|
class Chapter7PatreonThanks(PatreonThanks):
|
|
CONFIG = {
|
|
"specific_patrons" : [
|
|
"Ali Yahya",
|
|
"Meshal Alshammari",
|
|
"CrypticSwarm ",
|
|
"Kaustuv DeBiswas",
|
|
"Kathryn Schmiedicke",
|
|
"Nathan Pellegrin",
|
|
"Karan Bhargava",
|
|
"Ankit Agarwal",
|
|
"Yu Jun",
|
|
"Dave Nicponski",
|
|
"Damion Kistler",
|
|
"Juan Benet",
|
|
"Othman Alikhan",
|
|
"Justin Helps",
|
|
"Markus Persson",
|
|
"Dan Buchoff",
|
|
"Derek Dai",
|
|
"Joseph John Cox",
|
|
"Luc Ritchie",
|
|
"Daan Smedinga",
|
|
"Jonathan Eppele",
|
|
"Albert Nguyen",
|
|
"Nils Schneider",
|
|
"Mustafa Mahdi",
|
|
"Mathew Bramson",
|
|
"Guido Gambardella",
|
|
"Jerry Ling",
|
|
"Mark Govea",
|
|
"Vecht",
|
|
"Shimin Kuang",
|
|
"Rish Kundalia",
|
|
"Achille Brighton",
|
|
"Ripta Pasay",
|
|
"Felipe Diniz",
|
|
]
|
|
}
|
|
|
|
class Thumbnail(Scene):
|
|
def construct(self):
|
|
lim = TexMobject("\\lim", "_{h", "\\to 0}")
|
|
lim.set_color_by_tex("h", GREEN)
|
|
lim.set_height(5)
|
|
self.add(lim)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|