3b1b-manim/old_projects/eoc/chapter4.py
2019-02-08 15:53:27 -08:00

2287 lines
73 KiB
Python

from big_ol_pile_of_manim_imports import *
SINE_COLOR = BLUE
X_SQUARED_COLOR = GREEN
SUM_COLOR = YELLOW
PRODUCT_COLOR = YELLOW
class Chapter4OpeningQuote(OpeningQuote):
CONFIG = {
"quote" : [
"Using the chain rule is like peeling an onion: ",
"you have to deal with each layer at a time, and ",
"if it is too big you will start crying."
],
"author" : "(Anonymous professor)"
}
class TransitionFromLastVideo(TeacherStudentsScene):
def construct(self):
simple_rules = VGroup(*list(map(TexMobject, [
"\\frac{d(x^3)}{dx} = 3x^2",
"\\frac{d(\\sin(x))}{dx} = \\cos(x)",
"\\frac{d(1/x)}{dx} = -\\frac{1}{x^2}",
])))
combination_rules = VGroup(*[
TexMobject("\\frac{d}{dx}\\left(%s\\right)"%tex)
for tex in [
"\\sin(x) + x^2",
"\\sin(x)(x^2)",
"\\sin(x^2)",
]
])
for rules in simple_rules, combination_rules:
rules.arrange(buff = LARGE_BUFF)
rules.next_to(self.get_teacher(), UP, buff = MED_LARGE_BUFF)
rules.to_edge(LEFT)
series = VideoSeries()
series.to_edge(UP)
last_video = series[2]
last_video.save_state()
this_video = series[3]
brace = Brace(last_video)
#Simple rules
self.add(series)
self.play(
FadeIn(brace),
last_video.set_color, YELLOW
)
for rule in simple_rules:
self.play(
Write(rule, run_time = 2),
self.get_teacher().change_mode, "raise_right_hand",
*[
ApplyMethod(pi.change_mode, "pondering")
for pi in self.get_students()
]
)
self.wait()
self.wait(2)
self.play(simple_rules.replace, last_video)
self.play(
last_video.restore,
Animation(simple_rules),
brace.next_to, this_video, DOWN,
this_video.set_color, YELLOW
)
#Combination rules
self.play(
Write(combination_rules),
*[
ApplyMethod(pi.change_mode, "confused")
for pi in self.get_students()
]
)
self.wait(2)
for rule in combination_rules:
interior = VGroup(*rule[5:-1])
added_anims = []
if rule is combination_rules[-1]:
inner_func = VGroup(*rule[-4:-2])
self.play(inner_func.shift, 0.5*UP)
added_anims = [
inner_func.shift, 0.5*DOWN,
inner_func.set_color, YELLOW
]
self.play(
interior.set_color, YELLOW,
*added_anims,
lag_ratio = 0.5
)
self.wait()
self.wait()
#Address subtraction and division
subtraction = TexMobject("\\sin(x)", "-", "x^2")
decomposed_subtraction = TexMobject("\\sin(x)", "+(-1)\\cdot", "x^2")
pre_division = TexMobject("\\frac{\\sin(x)}{x^2}")
division = VGroup(
VGroup(*pre_division[:6]),
VGroup(*pre_division[6:7]),
VGroup(*pre_division[7:]),
)
pre_decomposed_division = TexMobject("\\sin(x)\\cdot\\frac{1}{x^2}")
decomposed_division = VGroup(
VGroup(*pre_decomposed_division[:6]),
VGroup(*pre_decomposed_division[6:9]),
VGroup(*pre_decomposed_division[9:]),
)
for mob in subtraction, decomposed_subtraction, division, decomposed_division:
mob.next_to(
VGroup(self.get_teacher(), self.get_students()[-1]),
UP, buff = MED_LARGE_BUFF
)
top_group = VGroup(series, simple_rules, brace)
combination_rules.save_state()
self.play(
top_group.next_to, FRAME_Y_RADIUS*UP, UP,
combination_rules.to_edge, UP,
)
pairs = [
(subtraction, decomposed_subtraction),
(division, decomposed_division)
]
for question, answer in pairs:
self.play(
Write(question),
combination_rules.fade, 0.2,
self.get_students()[2].change_mode, "raise_right_hand",
self.get_teacher().change_mode, "plain",
)
self.wait()
answer[1].set_color(GREEN)
self.play(
Transform(question, answer),
self.get_teacher().change_mode, "hooray",
self.get_students()[2].change_mode, "plain",
)
self.wait()
self.play(FadeOut(question))
#Monstrous expression
monster = TexMobject(
"\\Big(",
"e^{\\sin(x)} \\cdot",
"\\cos\\Big(",
"\\frac{1}{x^3}",
" + x^3",
"\\Big)",
"\\Big)^4"
)
monster.next_to(self.get_pi_creatures(), UP)
parts = [
VGroup(*monster[3][2:]),
VGroup(*monster[3][:2]),
monster[4],
VGroup(monster[2], monster[5]),
monster[1],
VGroup(monster[0], monster[6])
]
modes = 3*["erm"] + 3*["pleading"]
for part, mode in zip(parts, modes):
self.play(
FadeIn(part, lag_ratio = 0.5),
self.get_teacher().change_mode, "raise_right_hand",
*[
ApplyMethod(pi.change_mode, mode)
for pi in self.get_students()
]
)
self.wait()
self.change_student_modes(*["happy"]*3)
words = list(map(TextMobject, [
"composition", "product",
"composition", "sum",
"composition"
]))
for word, part in zip(words, reversed(parts)):
word.set_color(YELLOW)
word.next_to(monster, UP)
self.play(
FadeIn(word),
part.scale_in_place, 1.2,
part.set_color, YELLOW
)
self.wait()
self.play(*list(map(FadeOut, [word, part])))
self.play(FadeOut(parts[0]))
#Bring back combinations
self.play(
combination_rules.restore,
*[
ApplyMethod(pi_creature.change_mode, "pondering")
for pi_creature in self.get_pi_creatures()
]
)
self.wait(2)
class DampenedSpring(Scene):
def construct(self):
compact_spring, extended_spring = [
ParametricFunction(
lambda t : (t/denom)*RIGHT+np.sin(t)*UP+np.cos(t)*OUT,
t_max = 12*np.pi,
color = GREY,
).shift(3*LEFT)
for denom in (12.0, 2.0)
]
for spring in compact_spring, extended_spring:
spring.scale(0.5)
spring.rotate(np.pi/6, UP)
spring.set_color(GREY)
spring.shift(-spring.points[0] + 3*LEFT)
moving_spring = compact_spring.copy()
def update_spring(spring, a):
spring.interpolate(
compact_spring,
extended_spring,
0.5*(np.exp(-4*a)*np.cos(40*a)+1)
)
equation = TexMobject(
"\\text{Length} = 2 + e^{-4t}\\cos(20t)"
)
equation.to_edge(UP)
self.add(moving_spring, equation)
self.play(UpdateFromAlphaFunc(
moving_spring, update_spring, run_time = 10,
rate_func=linear
))
self.wait()
class ComingUp(Scene):
def construct(self):
rect = Rectangle(height = 9, width = 16)
rect.set_stroke(WHITE)
rect.set_height(FRAME_HEIGHT-2)
title = TextMobject("Coming up...")
title.to_edge(UP)
rect.next_to(title, DOWN)
self.play(Write(title))
self.play(ShowCreation(rect))
self.wait()
class PreSumRuleDiscussion(Scene):
def construct(self):
title = TextMobject("Sum rule")
title.to_edge(UP)
self.add(title)
specific = TexMobject(
"\\frac{d}{dx}(", "\\sin(x)", "+", "x^2", ")",
"=", "\\cos(x)", "+", "2x"
)
general = TexMobject(
"\\frac{d}{dx}(", "g(x)", "+", "h(x)", ")",
"=", "\\frac{dg}{dx}", "+", "\\frac{dh}{dx}"
)
for formula in specific, general:
formula[1].set_color(SINE_COLOR)
formula[6].set_color(SINE_COLOR)
formula[3].set_color(X_SQUARED_COLOR)
formula[8].set_color(X_SQUARED_COLOR)
VGroup(specific, general).arrange(DOWN, buff = LARGE_BUFF)
#Add on rules
self.add(specific)
for i in 0, 4, 5:
self.add(general[i])
self.wait(2)
for indices in [(1, 2, 3), (6,), (7, 8)]:
self.play(*[
ReplacementTransform(
specific[i].copy(), general[i]
)
for i in indices
])
self.wait()
#Highlight parts
for i in 1, 3, -1, 6, 8:
if i < 0:
self.wait()
else:
part = specific[i]
self.play(
part.set_color, YELLOW,
part.scale_in_place, 1.2,
rate_func = there_and_back
)
self.wait()
class SumRule(GraphScene):
CONFIG = {
"x_labeled_nums" : [],
"y_labeled_nums" : [],
"y_axis_label" : "",
"x_max" : 4,
"x_axis_width" : FRAME_WIDTH,
"y_max" : 3,
"graph_origin" : 2.5*DOWN + 2.5*LEFT,
"graph_label_x_value" : 1.5,
"example_input" : 0.5,
"example_input_string" : "0.5",
"dx" : 0.05,
"v_lines_x_min" : -1,
"v_lines_x_max" : 2,
"graph_scale_factor" : 2,
"tex_scale_factor" : 0.8,
}
def construct(self):
self.write_function()
self.add_graphs()
self.zoom_in_on_graph()
self.show_example_stacking()
self.show_df()
self.expand_derivative()
def write_function(self):
func_mob = TexMobject("f(x)", "=", "\\sin(x)", "+", "x^2")
func_mob.scale(self.tex_scale_factor)
func_mob.set_color_by_tex("f(x)", SUM_COLOR)
func_mob.set_color_by_tex("\\sin(x)", SINE_COLOR)
func_mob.set_color_by_tex("x^2", X_SQUARED_COLOR)
func_mob.to_corner(UP+LEFT)
self.add(func_mob)
self.func_mob = func_mob
def add_graphs(self):
self.setup_axes()
sine_graph = self.get_graph(np.sin, color = SINE_COLOR)
parabola = self.get_graph(lambda x : x**2, color = X_SQUARED_COLOR)
sum_graph = self.get_graph(
lambda x : np.sin(x) + x**2,
color = SUM_COLOR
)
sine_label = self.get_graph_label(
sine_graph, "\\sin(x)",
x_val = self.graph_label_x_value,
direction = UP+RIGHT,
buff = 0,
)
sine_label.scale_in_place(self.tex_scale_factor)
parabola_label = self.get_graph_label(
parabola, "x^2", x_val = self.graph_label_x_value,
)
parabola_label.scale_in_place(self.tex_scale_factor)
graphs = VGroup(sine_graph, parabola)
labels = VGroup(sine_label, parabola_label)
for label in labels:
label.add_background_rectangle()
for graph, label in zip(graphs, labels):
self.play(
ShowCreation(graph),
Write(label)
)
self.wait()
num_lines = (self.v_lines_x_max-self.v_lines_x_min)/self.dx
sine_v_lines, parabox_v_lines = v_line_sets = [
self.get_vertical_lines_to_graph(
graph,
x_min = self.v_lines_x_min,
x_max = self.v_lines_x_max,
num_lines = num_lines,
stroke_width = 2
)
for graph in graphs
]
sine_v_lines.shift(0.02*RIGHT)
for v_lines in v_line_sets:
self.play(ShowCreation(v_lines), Animation(labels))
self.wait()
self.play(*it.chain(
[
ApplyMethod(l2.move_to, l1.get_top(), DOWN)
for l1, l2, in zip(*v_line_sets)
],
[graph.fade for graph in graphs],
[Animation(labels)]
))
self.wait()
self.play(ShowCreation(sum_graph))
self.wait()
self.sum_graph = sum_graph
self.parabola = parabola
self.sine_graph = sine_graph
self.graph_labels = labels
self.v_line_sets = v_line_sets
def zoom_in_on_graph(self):
graph_parts = VGroup(
self.axes,
self.sine_graph, self.parabola, self.sum_graph,
*self.v_line_sets
)
graph_parts.remove(self.func_mob, *self.graph_labels)
graph_parts.generate_target()
self.graph_labels.generate_target()
for mob in graph_parts, self.graph_labels:
mob.target.scale(
self.graph_scale_factor,
about_point = self.graph_origin,
)
for mob in self.graph_labels.target:
mob.scale(
1./self.graph_scale_factor,
about_point = mob.get_bottom()
)
mob.shift_onto_screen()
self.play(*list(map(MoveToTarget, [
graph_parts, self.graph_labels
])))
self.wait()
def show_example_stacking(self):
v_line_sets = self.v_line_sets
num_lines = len(v_line_sets[0])
example_v_lines, nudged_v_lines = [
VGroup(*[v_lines[index] for v_lines in v_line_sets])
for index in (num_lines/2, num_lines/2+1)
]
for line in nudged_v_lines:
line.save_state()
sine_lines, parabola_lines = [
VGroup(example_v_lines[i], nudged_v_lines[i])
for i in (0, 1)
]
faders = VGroup(*[line for line in it.chain(*v_line_sets) if line not in example_v_lines])
label_groups = []
for line, tex, vect in zip(sine_lines, ["", "+dx"], [LEFT, RIGHT]):
dot = Dot(line.get_bottom(), radius = 0.03, color = YELLOW)
label = TexMobject(
"x=" + str(self.example_input) + tex
)
label.next_to(dot, DOWN+vect, buff = MED_LARGE_BUFF)
arrow = Arrow(
label.get_corner(UP-vect), dot,
buff = SMALL_BUFF,
color = WHITE,
tip_length = 0.1
)
label_groups.append(VGroup(label, arrow, dot))
line_tex_direction_triplets = [
(sine_lines[0], "\\sin(0.5)", LEFT),
(sine_lines[1], "\\sin(0.5+dx)", RIGHT),
(parabola_lines[0], "(0.5)^2", LEFT),
(parabola_lines[1], "(0.5+dx)^2", RIGHT),
]
for line, tex, direction in line_tex_direction_triplets:
line.brace = Brace(
line, direction,
buff = SMALL_BUFF,
min_num_quads = 2,
)
line.brace.set_color(line.get_color())
line.brace.add_background_rectangle()
line.brace_text = line.brace.get_text("$%s$"%tex)
line.brace_text.scale(
self.tex_scale_factor,
about_point = line.brace_text.get_edge_center(-direction)
)
line.brace_text.add_background_rectangle()
line.brace_anim = MaintainPositionRelativeTo(
VGroup(line.brace, line.brace_text), line
)
##Look at example lines
self.play(
example_v_lines.set_stroke, None, 4,
faders.fade,
Animation(self.graph_labels),
Write(label_groups[0]),
)
for line in example_v_lines:
line.save_state()
self.wait()
self.play(
GrowFromCenter(sine_lines[0].brace),
Write(sine_lines[0].brace_text),
)
self.wait()
self.play(
sine_lines[0].shift, UP+4*LEFT,
sine_lines[0].brace_anim,
parabola_lines[0].move_to, sine_lines[0], DOWN
)
self.wait()
parabola_lines[0].brace_anim.update(1)
self.play(
GrowFromCenter(parabola_lines[0].brace),
Write(parabola_lines[0].brace_text),
)
self.wait()
self.play(*it.chain(*[
[line.restore, line.brace_anim]
for line in example_v_lines
]))
## Nudged_lines
self.play(
Write(label_groups[1]),
*it.chain(*[
[line.restore, line.set_stroke, None, 4]
for line in nudged_v_lines
])
)
self.wait()
for line in nudged_v_lines:
self.play(
GrowFromCenter(line.brace),
Write(line.brace_text)
)
self.wait()
self.sine_lines = sine_lines
self.parabola_lines = parabola_lines
def show_df(self):
sine_lines = self.sine_lines
parabola_lines = self.parabola_lines
df, equals, d_sine, plus, d_x_squared = deriv_mob = TexMobject(
"df", "=", "d(\\sin(x))", "+", "d(x^2)"
)
df.set_color(SUM_COLOR)
d_sine.set_color(SINE_COLOR)
d_x_squared.set_color(X_SQUARED_COLOR)
deriv_mob.scale(self.tex_scale_factor)
deriv_mob.next_to(
self.func_mob, DOWN,
buff = MED_LARGE_BUFF,
aligned_edge = LEFT
)
for submob in deriv_mob:
submob.add_to_back(BackgroundRectangle(submob))
df_lines = self.show_difference(parabola_lines, df, equals)
self.wait()
self.play(FadeOut(df_lines))
self.play(
parabola_lines[0].shift,
(parabola_lines[1].get_bottom()[1]-parabola_lines[0].get_bottom()[1])*UP,
parabola_lines[0].brace_anim
)
d_sine_lines = self.show_difference(sine_lines, d_sine, plus)
d_x_squared_lines = self.show_difference(parabola_lines, d_x_squared, VGroup())
self.wait()
self.deriv_mob = deriv_mob
self.d_sine_lines = d_sine_lines
self.d_x_squared_lines = d_x_squared_lines
def show_difference(self, v_lines, target_tex, added_tex):
distance = v_lines[1].get_top()[1]-v_lines[0].get_top()[1]
h_lines = VGroup(*[
DashedLine(ORIGIN, 2*RIGHT, stroke_width = 3)
for x in range(2)
])
h_lines.arrange(DOWN, buff = distance)
h_lines.move_to(v_lines[1].get_top(), UP+RIGHT)
brace = Brace(h_lines, LEFT)
brace_text = target_tex.copy()
brace_text.next_to(brace, LEFT)
self.play(ShowCreation(h_lines))
self.play(GrowFromCenter(brace), Write(brace_text))
self.wait()
self.play(
ReplacementTransform(brace_text.copy(), target_tex),
Write(added_tex)
)
return VGroup(h_lines, brace, brace_text)
def expand_derivative(self):
expanded_deriv = TexMobject(
"df", "=", "\\cos(x)", "\\,dx", "+", "2x", "\\,dx"
)
expanded_deriv.set_color_by_tex("df", SUM_COLOR)
VGroup(*expanded_deriv[2:4]).set_color(SINE_COLOR)
VGroup(*expanded_deriv[5:7]).set_color(X_SQUARED_COLOR)
expanded_deriv.scale(self.tex_scale_factor)
expanded_deriv.next_to(
self.deriv_mob, DOWN,
buff = MED_LARGE_BUFF,
aligned_edge = LEFT
)
background_rect = BackgroundRectangle(expanded_deriv)
rearranged_deriv = TexMobject(
"{df \\over", "dx}", "=", "\\cos(x)", "+", "2x"
)
rearranged_deriv[0].set_color(SUM_COLOR)
rearranged_deriv[3].set_color(SINE_COLOR)
rearranged_deriv[5].set_color(X_SQUARED_COLOR)
rearranged_deriv.scale(self.tex_scale_factor)
rearranged_deriv.move_to(expanded_deriv, UP+LEFT)
deriv_target_indices = [0, 2, 3, 1, 4, 5, 1]
self.play(
FadeIn(
background_rect,
rate_func = squish_rate_func(smooth, 0.6, 1)
),
Write(expanded_deriv)
)
self.wait()
tex_group_pairs = [
("\\cos(0.5)dx", self.d_sine_lines),
("2(0.5)dx", self.d_x_squared_lines),
]
def indicate(mob):
self.play(
mob.set_color, YELLOW,
mob.scale_in_place, 1.2,
rate_func = there_and_back
)
for tex, group in tex_group_pairs:
old_label = group[-1]
new_label = TexMobject(tex)
pre_dx = VGroup(*new_label[:-2])
dx = VGroup(*new_label[-2:])
new_label.add_background_rectangle()
new_label.scale(self.tex_scale_factor)
new_label.move_to(old_label, RIGHT)
new_label.set_color(old_label.get_color())
self.play(FocusOn(old_label))
indicate(old_label)
self.wait()
self.play(FadeOut(old_label))
self.play(FadeIn(new_label))
self.wait()
indicate(dx)
self.wait()
indicate(pre_dx)
self.wait()
self.wait()
self.play(*[
Transform(
expanded_deriv[i], rearranged_deriv[j],
path_arc = -np.pi/2
)
for i, j in enumerate(deriv_target_indices)
])
self.wait()
class DiscussProducts(TeacherStudentsScene):
def construct(self):
wrong_product_rule = TexMobject(
"\\frac{d(\\sin(x)x^2)}{dx}",
"\\ne",
"\\left(\\frac{d(\\sin(x))}{dx}\\right)",
"\\left(\\frac{d(x^2)}{dx}\\right)",
)
not_equals = wrong_product_rule[1]
wrong_product_rule[2].set_color(SINE_COLOR)
wrong_product_rule[3].set_color(X_SQUARED_COLOR)
wrong_product_rule.next_to(
self.get_teacher().get_corner(UP+LEFT),
UP,
buff = MED_LARGE_BUFF
).shift_onto_screen()
self.teacher_says(
"Products are a bit different",
target_mode = "sassy"
)
self.wait(2)
self.play(RemovePiCreatureBubble(
self.get_teacher(),
target_mode = "raise_right_hand"
))
self.play(Write(wrong_product_rule))
self.change_student_modes(
"pondering", "confused", "erm",
added_anims = [
not_equals.scale_in_place, 1.3,
not_equals.set_color, RED
]
)
self.wait()
self.teacher_says(
"Think about the \\\\ underlying meaning",
bubble_kwargs = {"height" : 3},
added_anims = [
wrong_product_rule.scale, 0.7,
wrong_product_rule.to_corner, UP+LEFT
]
)
self.change_student_modes(*["pondering"]*3)
self.wait(2)
class NotGraphsForProducts(GraphScene):
CONFIG = {
"y_max" : 25,
"x_max" : 7,
}
def construct(self):
self.setup_axes()
sine_graph = self.get_graph(np.sin, color = SINE_COLOR)
sine_graph.label = self.get_graph_label(
sine_graph, "\\sin(x)",
x_val = 3*np.pi/2,
direction = DOWN
)
parabola = self.get_graph(
lambda x : x**2, color = X_SQUARED_COLOR
)
parabola.label = self.get_graph_label(
parabola, "x^2",
x_val = 2.5,
direction = UP+LEFT,
)
product_graph = self.get_graph(
lambda x : np.sin(x)*(x**2), color = PRODUCT_COLOR
)
product_graph.label = self.get_graph_label(
product_graph, "\\sin(x)x^2",
x_val = 2.8,
direction = UP+RIGHT,
buff = 0
)
graphs = [sine_graph, parabola, product_graph]
for graph in graphs:
self.play(
ShowCreation(graph),
Write(graph.label, run_time = 2)
)
self.wait()
everything = VGroup(*[m for m in self.get_mobjects() if not m.is_subpath])
words = TextMobject("Not the best visualization")
words.scale(1.5)
words.shift(FRAME_Y_RADIUS*UP/2)
words.add_background_rectangle()
words.set_color(RED)
self.play(
everything.fade,
Write(words)
)
self.wait()
class ConfusedMorty(Scene):
def construct(self):
morty = Mortimer()
self.add(morty)
self.wait()
self.play(morty.change_mode, "confused")
self.play(Blink(morty))
self.wait(2)
class IntroduceProductAsArea(ReconfigurableScene):
CONFIG = {
"top_func" : np.sin,
"top_func_label" : "\\sin(x)",
"top_func_nudge_label" : "d(\\sin(x))",
"top_func_derivative" : "\\cos(x)",
"side_func" : lambda x : x**2,
"side_func_label" : "x^2",
"side_func_nudge_label" : "d(x^2)",
"side_func_derivative" : "2x",
"x_unit_to_space_unit" : 3,
"box_kwargs" : {
"fill_color" : YELLOW,
"fill_opacity" : 0.75,
"stroke_width" : 1,
},
"df_box_kwargs" : {
"fill_color" : GREEN,
"fill_opacity" : 0.75,
"stroke_width" : 0,
},
"box_corner_location" : 6*LEFT+2.5*UP,
"slider_center" : 3.5*RIGHT+2*DOWN,
"slider_width" : 6,
"slider_x_max" : 3,
"x_slider_handle_height" : 0.25,
"slider_handle_color" : BLUE,
"default_x" : .75,
"dx" : 0.1,
"tiny_dx" : 0.01,
}
def construct(self):
self.introduce_box()
self.talk_though_sine()
self.define_f_of_x()
self.nudge_x()
self.write_df()
self.show_thinner_dx()
self.expand_derivative()
self.write_derivative_abstractly()
self.write_mneumonic()
def introduce_box(self):
box, labels = self.box_label_group = self.get_box_label_group(self.default_x)
self.x_slider = self.get_x_slider(self.default_x)
self.play(Write(labels))
self.play(DrawBorderThenFill(box))
self.wait()
for mob in self.x_slider:
self.play(Write(mob, run_time = 1))
self.wait()
for new_x in 0.5, 2, self.default_x:
self.animate_x_change(
new_x, run_time = 2
)
self.wait()
def talk_though_sine(self):
x_axis = self.x_slider[0]
graph = FunctionGraph(
np.sin, x_min = 0, x_max = np.pi,
color = SINE_COLOR
)
scale_factor = self.x_slider.get_width()/self.slider_x_max
graph.scale(scale_factor)
graph.move_to(x_axis.number_to_point(0), DOWN+LEFT)
label = TexMobject("\\sin(x)")
label.set_color(SINE_COLOR)
label.next_to(graph, UP)
y_axis = x_axis.copy()
y_axis.remove(*y_axis.numbers)
v_line = Line(ORIGIN, UP, color = WHITE, stroke_width = 2)
def v_line_update(v_line):
x = x_axis.point_to_number(self.x_slider[1].get_top())
v_line.set_height(np.sin(x)*scale_factor)
v_line.move_to(x_axis.number_to_point(x), DOWN)
v_line_update(v_line)
self.play(
Rotate(y_axis, np.pi/2, about_point = y_axis.get_left()),
Animation(x_axis)
)
self.play(
ShowCreation(graph),
Write(label, run_time = 1)
)
self.play(ShowCreation(v_line))
for x, rt in zip([0.25, np.pi/2, 3, self.default_x], [2, 4, 4, 2]):
self.animate_x_change(
x, run_time = rt,
added_anims = [
UpdateFromFunc(v_line, v_line_update)
]
)
self.wait()
self.play(*it.chain(
list(map(FadeOut, [y_axis, graph, label, v_line])),
[Animation(x_axis)]
))
self.wait()
for x in 1, 0.5, self.default_x:
self.animate_x_change(x)
self.wait()
def define_f_of_x(self):
f_def = TexMobject(
"f(x)", "=",
self.top_func_label,
self.side_func_label,
"=",
"\\text{Area}"
)
f_def.to_corner(UP+RIGHT)
f_def[-1].set_color(self.box_kwargs["fill_color"])
box, labels = self.box_label_group
self.play(Write(VGroup(*f_def[:-1])))
self.play(Transform(
box.copy().set_fill(opacity = 0), f_def[-1],
run_time = 1.5,
))
self.wait()
self.f_def = f_def
def nudge_x(self):
box, labels = self.box_label_group
nudge_label_group = self.get_nudge_label_group()
original_dx = self.dx
self.dx = self.tiny_dx
thin_df_boxes = self.get_df_boxes()
self.dx = original_dx
df_boxes = self.get_df_boxes()
right_box, corner_box, right_box = df_boxes
self.play(FocusOn(nudge_label_group))
self.play(*list(map(GrowFromCenter, nudge_label_group)))
self.animate_x_change(
self.default_x+self.dx,
rate_func = there_and_back,
run_time = 2,
added_anims = [Animation(nudge_label_group)]
)
self.wait()
self.play(
ReplacementTransform(thin_df_boxes, df_boxes),
VGroup(*labels[1]).shift, right_box.get_width()*RIGHT,
)
self.play(
df_boxes.space_out_submobjects, 1.1,
df_boxes.move_to, box, UP+LEFT,
)
self.wait()
self.df_boxes = df_boxes
self.df_box_labels = self.get_df_box_labels(df_boxes)
self.x_slider.add(nudge_label_group)
def get_nudge_label_group(self):
line, triangle, x_mob = self.x_slider
dx_line = Line(*[
line.number_to_point(self.x_slider.x_val + num)
for num in (0, self.dx,)
])
dx_line.set_stroke(
self.df_box_kwargs["fill_color"],
width = 6
)
brace = Brace(dx_line, UP, buff = SMALL_BUFF)
brace.stretch_to_fit_height(0.2)
brace.next_to(dx_line, UP, buff = SMALL_BUFF)
brace.set_stroke(width = 1)
dx = TexMobject("dx")
dx.scale(0.7)
dx.next_to(brace, UP, buff = SMALL_BUFF)
dx.set_color(dx_line.get_color())
return VGroup(dx_line, brace, dx)
def get_df_boxes(self):
box, labels = self.box_label_group
alt_box = self.get_box(self.x_slider.x_val + self.dx)
h, w = box.get_height(), box.get_width()
dh, dw = alt_box.get_height()-h, alt_box.get_width()-w
heights_and_widths = [(dh, w), (dh, dw), (h, dw)]
vects = [DOWN, DOWN+RIGHT, RIGHT]
df_boxes = VGroup(*[
Rectangle(
height = height, width = width, **self.df_box_kwargs
).next_to(box, vect, buff = 0)
for (height, width), vect in zip(
heights_and_widths, vects
)
])
return df_boxes
def get_df_box_labels(self, df_boxes):
bottom_box, corner_box, right_box = df_boxes
result = VGroup()
quads = [
(right_box, UP, self.top_func_nudge_label, LEFT),
(corner_box, RIGHT, self.side_func_nudge_label, ORIGIN),
]
for box, vect, label_tex, aligned_edge in quads:
brace = Brace(box, vect)
label = TexMobject(label_tex)
label.next_to(
brace, vect,
aligned_edge = aligned_edge,
buff = SMALL_BUFF
)
label.set_color(df_boxes[0].get_color())
result.add(VGroup(brace, label))
return result
def write_df(self):
deriv = TexMobject(
"df", "=",
self.top_func_label,
self.side_func_nudge_label,
"+",
self.side_func_label,
self.top_func_nudge_label,
)
deriv.scale(0.9)
deriv.next_to(self.f_def, DOWN, buff = LARGE_BUFF)
deriv.to_edge(RIGHT)
for submob, tex in zip(deriv, deriv.expression_parts):
if tex.startswith("d"):
submob.set_color(self.df_box_kwargs["fill_color"])
bottom_box_area = VGroup(*deriv[2:4])
right_box_area = VGroup(*deriv[5:7])
bottom_box, corner_box, right_box = self.df_boxes
plus = TexMobject("+").set_fill(opacity = 0)
df_boxes_copy = VGroup(
bottom_box.copy(),
plus,
right_box.copy(),
plus.copy(),
corner_box.copy(),
)
self.deriv = deriv
self.df_boxes_copy = df_boxes_copy
box, labels = self.box_label_group
self.full_box_parts = VGroup(*it.chain(
[box], self.df_boxes, labels, self.df_box_labels
))
self.play(Write(VGroup(*deriv[:2])))
self.play(
df_boxes_copy.arrange,
df_boxes_copy.set_fill, None, self.df_box_kwargs["fill_opacity"],
df_boxes_copy.next_to, deriv[1]
)
deriv.submobjects[4] = df_boxes_copy[1]
self.wait()
self.set_color_right_boxes()
self.set_color_bottom_boxes()
self.describe_bottom_box(bottom_box_area)
self.describe_right_box(right_box_area)
self.ignore_corner()
# self.add(deriv)
def set_color_boxes_and_label(self, boxes, label):
boxes.save_state()
label.save_state()
self.play(GrowFromCenter(label))
self.play(
boxes.set_color, RED,
label.set_color, RED,
)
self.play(
label[1].scale_in_place, 1.1,
rate_func = there_and_back
)
self.play(boxes.restore, label.restore)
self.wait()
def set_color_right_boxes(self):
self.set_color_boxes_and_label(
VGroup(*self.df_boxes[1:]),
self.df_box_labels[0]
)
def set_color_bottom_boxes(self):
self.set_color_boxes_and_label(
VGroup(*self.df_boxes[:-1]),
self.df_box_labels[1]
)
def describe_bottom_box(self, bottom_box_area):
bottom_box = self.df_boxes[0]
bottom_box_copy = self.df_boxes_copy[0]
other_box_copies = VGroup(*self.df_boxes_copy[1:])
top_label = self.box_label_group[1][0]
right_label = self.df_box_labels[1]
faders = VGroup(*[m for m in self.full_box_parts if m not in [bottom_box, top_label, right_label]])
faders.save_state()
self.play(faders.fade, 0.8)
self.wait()
self.play(FocusOn(bottom_box_copy))
self.play(
ReplacementTransform(bottom_box_copy, bottom_box_area),
other_box_copies.next_to, bottom_box_area, RIGHT
)
self.wait()
self.play(faders.restore)
def describe_right_box(self, right_box_area):
right_box = self.df_boxes[2]
right_box_copy = self.df_boxes_copy[2]
right_box_area.next_to(self.df_boxes_copy[1])
other_box_copies = VGroup(*self.df_boxes_copy[3:])
top_label = self.df_box_labels[0]
right_label = self.box_label_group[1][1]
faders = VGroup(*[m for m in self.full_box_parts if m not in [right_box, top_label, right_label]])
faders.save_state()
self.play(faders.fade, 0.8)
self.wait()
self.play(FocusOn(right_box_copy))
self.play(
ReplacementTransform(right_box_copy, right_box_area),
other_box_copies.next_to, right_box_area, DOWN,
MED_SMALL_BUFF, RIGHT,
)
self.wait()
self.play(faders.restore)
def ignore_corner(self):
corner = self.df_boxes[1]
corner.save_state()
corner_copy = VGroup(*self.df_boxes_copy[-2:])
words = TextMobject("Ignore")
words.set_color(RED)
words.next_to(corner_copy, LEFT, buff = LARGE_BUFF)
words.shift(MED_SMALL_BUFF*DOWN)
arrow = Arrow(words, corner_copy, buff = SMALL_BUFF, color = RED)
self.play(
corner.set_color, RED,
corner_copy.set_color, RED,
)
self.wait()
self.play(Write(words), ShowCreation(arrow))
self.wait()
self.play(*list(map(FadeOut, [words, arrow, corner_copy])))
self.wait()
corner_copy.set_color(BLACK)
def show_thinner_dx(self):
self.transition_to_alt_config(dx = self.tiny_dx)
def expand_derivative(self):
# self.play(
# self.deriv.next_to, self.f_def, DOWN, MED_LARGE_BUFF,
# self.deriv.shift_onto_screen
# )
# self.wait()
expanded_deriv = TexMobject(
"df", "=",
self.top_func_label,
self.side_func_derivative,
"\\,dx",
"+",
self.side_func_label,
self.top_func_derivative,
"\\,dx"
)
final_deriv = TexMobject(
"{df \\over ", "dx}", "=",
self.top_func_label,
self.side_func_derivative,
"+",
self.side_func_label,
self.top_func_derivative,
)
color = self.deriv[0].get_color()
for new_deriv in expanded_deriv, final_deriv:
for submob, tex in zip(new_deriv, new_deriv.expression_parts):
for substr in "df", "dx", self.top_func_derivative, self.side_func_derivative:
if substr in tex:
submob.set_color(color)
new_deriv.scale(0.9)
new_deriv.next_to(self.deriv, DOWN, buff = MED_LARGE_BUFF)
new_deriv.shift_onto_screen()
def indicate(mob):
self.play(
mob.scale_in_place, 1.2,
mob.set_color, YELLOW,
rate_func = there_and_back
)
for index in 6, 3:
self.deriv.submobjects.insert(
index+1, self.deriv[index].copy()
)
non_deriv_indices = list(range(len(expanded_deriv)))
for indices in [(3, 4), (7, 8)]:
top_part = VGroup()
bottom_part = VGroup()
for i in indices:
non_deriv_indices.remove(i)
top_part.add(self.deriv[i].copy())
bottom_part.add(expanded_deriv[i])
self.play(top_part.move_to, bottom_part)
self.wait()
indicate(top_part)
self.wait()
self.play(ReplacementTransform(top_part, bottom_part))
self.wait()
top_part = VGroup()
bottom_part = VGroup()
for i in non_deriv_indices:
top_part.add(self.deriv[i].copy())
bottom_part.add(expanded_deriv[i])
self.play(ReplacementTransform(
top_part, bottom_part
))
self.wait()
self.play(*[
ReplacementTransform(
expanded_deriv[i], final_deriv[j],
path_arc = -np.pi/2
)
for i, j in [
(0, 0),
(1, 2),
(2, 3),
(3, 4),
(4, 1),
(5, 5),
(6, 6),
(7, 7),
(8, 1),
]
])
self.wait()
for index in 0, 1, 3, 4, 6, 7:
indicate(final_deriv[index])
self.wait()
def write_derivative_abstractly(self):
self.transition_to_alt_config(
return_to_original_configuration = False,
top_func_label = "g(x)",
top_func_nudge_label = "dg",
top_func_derivative = "\\frac{dg}{dx}",
side_func_label = "h(x)",
side_func_nudge_label = "dh",
side_func_derivative = "\\frac{dh}{dx}",
)
self.wait()
def write_mneumonic(self):
morty = Mortimer()
morty.scale(0.7)
morty.to_edge(DOWN)
morty.shift(2*LEFT)
words = TextMobject(
"``Left ", "d(Right) ", "+", " Right ", "d(Left)", "''",
arg_separator = ""
)
VGroup(words[1], words[4]).set_color(self.df_boxes[0].get_color())
words.scale(0.7)
words.next_to(morty.get_corner(UP+LEFT), UP)
words.shift_onto_screen()
self.play(FadeIn(morty))
self.play(
morty.change_mode, "raise_right_hand",
Write(words)
)
self.wait()
###############
def animate_x_change(
self, target_x,
box_label_group = None,
x_slider = None,
**kwargs
):
box_label_group = box_label_group or self.box_label_group
x_slider = x_slider or self.x_slider
kwargs["run_time"] = kwargs.get("run_time", 2)
added_anims = kwargs.get("added_anims", [])
start_x = x_slider.x_val
def update_box_label_group(box_label_group, alpha):
new_x = interpolate(start_x, target_x, alpha)
new_box_label_group = self.get_box_label_group(new_x)
Transform(box_label_group, new_box_label_group).update(1)
def update_x_slider(x_slider, alpha):
new_x = interpolate(start_x, target_x, alpha)
new_x_slider = self.get_x_slider(new_x)
Transform(x_slider, new_x_slider).update(1)
self.play(
UpdateFromAlphaFunc(
box_label_group,
update_box_label_group
),
UpdateFromAlphaFunc(
x_slider,
update_x_slider
),
*added_anims,
**kwargs
)
x_slider.x_val = target_x
def get_x_slider(self, x):
numbers = list(range(int(self.slider_x_max) + 1))
line = NumberLine(
x_min = 0,
x_max = self.slider_x_max,
unit_size = float(self.slider_width)/self.slider_x_max,
color = GREY,
numbers_with_elongated_ticks = numbers,
tick_frequency = 0.25,
)
line.add_numbers(*numbers)
line.numbers.next_to(line, UP, buff = SMALL_BUFF)
for number in line.numbers:
number.add_background_rectangle()
line.move_to(self.slider_center)
triangle = RegularPolygon(
3, start_angle = np.pi/2,
fill_color = self.slider_handle_color,
fill_opacity = 0.8,
stroke_width = 0,
)
triangle.set_height(self.x_slider_handle_height)
triangle.move_to(line.number_to_point(x), UP)
x_mob = TexMobject("x")
x_mob.next_to(triangle, DOWN, buff = SMALL_BUFF)
result = VGroup(line, triangle, x_mob)
result.x_val = x
return result
def get_box_label_group(self, x):
box = self.get_box(x)
labels = self.get_box_labels(box)
return VGroup(box, labels)
def get_box(self, x):
box = Rectangle(
width = self.x_unit_to_space_unit * self.top_func(x),
height = self.x_unit_to_space_unit * self.side_func(x),
**self.box_kwargs
)
box.move_to(self.box_corner_location, UP+LEFT)
return box
def get_box_labels(self, box):
result = VGroup()
for label_tex, vect in (self.top_func_label, UP), (self.side_func_label, RIGHT):
brace = Brace(box, vect, min_num_quads = 5)
label = TexMobject(label_tex)
label.next_to(brace, vect, buff = SMALL_BUFF)
result.add(VGroup(brace, label))
return result
class WriteDXSquared(Scene):
def construct(self):
term = TexMobject("(...)(dx)^2")
term.set_color(RED)
self.play(Write(term))
self.wait()
class MneumonicExample(TeacherStudentsScene):
def construct(self):
d, left, right, rp = deriv_q = TexMobject(
"\\frac{d}{dx}(", "\\sin(x)", "x^2", ")"
)
deriv_q.to_edge(UP)
words = TextMobject(
"Left ", "d(Right) ", "+", " Right ", "d(Left)",
arg_separator = ""
)
deriv = TexMobject("\\sin(x)", "2x", "+", "x^2", "\\cos(x)")
for mob in words, deriv:
VGroup(mob[1], mob[4]).set_color(GREEN)
mob.next_to(deriv_q, DOWN, buff = MED_LARGE_BUFF)
deriv.shift(words[2].get_center()-deriv[2].get_center())
self.add(words)
self.play(
Write(deriv_q),
self.get_teacher().change_mode, "raise_right_hand"
)
self.change_student_modes(*["pondering"]*3)
left_words = VGroup(*words[:2])
left_terms = VGroup(*deriv[:2])
self.play(
left_words.next_to, left_terms, DOWN,
MED_LARGE_BUFF, RIGHT
)
self.play(ReplacementTransform(
left_words.copy(), left_terms
))
self.wait()
self.play(*list(map(Indicate, [left, left_words[0], left_terms[0]])))
self.wait()
self.play(*list(map(Indicate, [right, left_words[1], left_terms[1]])))
self.wait()
right_words = VGroup(*words[2:])
right_terms = VGroup(*deriv[2:])
self.play(
right_words.next_to, right_terms, DOWN,
MED_LARGE_BUFF, LEFT
)
self.play(ReplacementTransform(
right_words.copy(), right_terms
))
self.wait()
self.play(*list(map(Indicate, [right, right_words[1], right_terms[1]])))
self.wait()
self.play(*list(map(Indicate, [left, right_words[2], right_terms[2]])))
self.wait(3)
self.play(self.get_teacher().change_mode, "shruggie")
self.wait()
self.change_student_modes(*["confused"]*3)
self.wait(3)
class ConstantMultiplication(TeacherStudentsScene):
def construct(self):
question = TextMobject("What about $\\dfrac{d}{dx}(2\\sin(x))$?")
answer = TextMobject("2\\cos(x)")
self.teacher_says(question)
self.wait()
self.student_says(
answer, target_mode = "hooray",
added_anims = [question.copy().to_edge, UP]
)
self.play(self.get_teacher().change_mode, "happy")
self.change_student_modes("pondering", "hooray", "pondering")
self.wait(3)
class ConstantMultiplicationFigure(IntroduceProductAsArea):
CONFIG = {
"side_func" : lambda x : 1,
"side_func_label" : "\\text{Constant}",
"side_func_nudge_label" : "",
"side_func_derivative" : "",
"x_unit_to_space_unit" : 3,
"default_x" : 0.5,
"dx" : 0.1
}
def construct(self):
self.box_label_group = self.get_box_label_group(self.default_x)
self.x_slider = self.get_x_slider(self.default_x)
# df_boxes = self.get_df_boxes()
# df_box_labels = self.get_df_box_labels(df_boxes)
self.add(self.box_label_group, self.x_slider)
self.nudge_x()
class ShoveXSquaredInSine(Scene):
def construct(self):
title = TextMobject("Function composition")
title.to_edge(UP)
sine = TexMobject("g(", "x", ")", "=", "\\sin(", "x", ")")
sine.set_color(SINE_COLOR)
x_squared = TexMobject("h(x)", "=", "x^2")
x_squared.set_color(X_SQUARED_COLOR)
group = VGroup(sine, x_squared)
group.arrange(buff = LARGE_BUFF)
group.shift(UP)
composition = TexMobject(
"g(", "h(x)", ")", "=", "\\sin(", "x^2", ")"
)
for i in 0, 2, 4, 6:
composition[i].set_color(SINE_COLOR)
for i in 1, 5:
composition[i].set_color(X_SQUARED_COLOR)
composition.next_to(group, DOWN, buff = LARGE_BUFF)
brace = Brace(VGroup(*composition[-3:]), DOWN)
deriv_q = brace.get_text("Derivative?")
self.add(group)
self.play(Write(title))
self.wait()
triplets = [
[sine, (0, 2), (0, 2)],
[x_squared, (0,), (1,)],
[sine, (3, 4, 6), (3, 4, 6)],
[x_squared, (2,), (5,)]
]
for premob, pre_indices, comp_indicies in triplets:
self.play(*[
ReplacementTransform(
premob[i].copy(), composition[j]
)
for i, j in zip(pre_indices, comp_indicies)
])
self.wait()
self.wait()
self.play(
GrowFromCenter(brace),
Write(deriv_q)
)
self.wait()
class ThreeLinesChainRule(ReconfigurableScene):
CONFIG = {
"start_x" : 0.5,
"max_x" : 1,
"min_x" : 0,
"top_x" : 3,
"example_x" : 1.5,
"dx" : 0.1,
"line_configs" : [
{
"func" : lambda x : x,
"func_label" : "x",
"triangle_color" : WHITE,
"center_y" : 3,
"x_min" : 0,
"x_max" : 3,
"numbers_to_show" : list(range(4)),
"numbers_with_elongated_ticks" : list(range(4)),
"tick_frequency" : 0.25,
},
{
"func" : lambda x : x**2,
"func_label" : "x^2",
"triangle_color" : X_SQUARED_COLOR,
"center_y" : 0.5,
"x_min" : 0,
"x_max" : 10,
"numbers_to_show" : list(range(0, 11)),
"numbers_with_elongated_ticks" : list(range(0, 11, 1)),
"tick_frequency" : 0.25,
},
{
"func" : lambda x : np.sin(x**2),
"func_label" : "\\sin(x^2)",
"triangle_color" : SINE_COLOR,
"center_y" : -2,
"x_min" : -2,
"x_max" : 2,
"numbers_to_show" : list(range(-2, 3)),
"numbers_with_elongated_ticks" : list(range(-2, 3)),
"tick_frequency" : 0.25,
},
],
"line_width" : 8,
"triangle_height" : 0.25,
}
def construct(self):
self.introduce_line_group()
self.draw_function_arrows()
self.talk_through_movement()
self.nudge_x()
self.give_example_of_meaning()
def introduce_line_group(self):
self.line_group = self.get_line_group(self.start_x)
lines, labels = self.line_group
for line in lines:
self.play(Write(line, run_time = 2))
self.wait()
last_label = labels[0].copy()
last_label.to_corner(UP+LEFT)
last_label.set_fill(opacity = 0)
for label in labels:
self.play(ReplacementTransform(
last_label.copy(), label
))
self.wait()
last_label = label
for x in self.max_x, self.min_x, self.start_x:
self.animate_x_change(x, run_time = 1)
self.wait()
def draw_function_arrows(self):
lines, line_labels = self.line_group
labels = VGroup(*[
TexMobject("(\\dots)^2").set_color(X_SQUARED_COLOR),
TexMobject("\\sin(\\dots)").set_color(SINE_COLOR)
])
arrows = VGroup()
for lines_subset, label in zip([lines[:2], lines[1:]], labels):
arrow = Arc(start_angle = np.pi/3, angle = -2*np.pi/3)
arrow.add_tip()
arrow.set_color(label.get_color())
arrow.next_to(VGroup(*lines_subset))
arrows.add(arrow)
label.next_to(arrow, RIGHT)
self.play(
ShowCreation(arrow),
Write(label)
)
self.wait()
self.arrows = arrows
self.arrow_labels = labels
def talk_through_movement(self):
lines, labels = self.line_group
self.animate_x_change(self.top_x, run_time = 4)
self.wait()
for label in labels[0], labels[1]:
oval = Circle(color = YELLOW)
oval.replace(label, stretch = True)
oval.scale(2.5)
oval.move_to(label.get_bottom())
self.play(ShowCreation(oval))
self.wait()
self.play(FadeOut(oval))
sine_text = TexMobject("\\sin(9) \\approx 0.412")
sine_text.move_to(labels[-1][-1])
sine_text.to_edge(DOWN)
sine_arrow = Arrow(
sine_text.get_top(),
labels[-1][0].get_bottom(),
buff = SMALL_BUFF,
)
self.play(
FadeIn(sine_text),
ShowCreation(sine_arrow)
)
self.wait(2)
self.play(*list(map(FadeOut, [sine_text, sine_arrow])))
self.animate_x_change(self.example_x, run_time = 3)
def nudge_x(self):
lines, labels = self.line_group
def get_value_points():
return [
label[0].get_bottom()
for label in labels
]
starts = get_value_points()
self.animate_x_change(self.example_x + self.dx, run_time = 0)
ends = get_value_points()
self.animate_x_change(self.example_x, run_time = 0)
nudge_lines = VGroup()
braces = VGroup()
numbers = VGroup()
for start, end, line, label, config in zip(starts, ends, lines, labels, self.line_configs):
color = label[0].get_color()
nudge_line = Line(start, end)
nudge_line.set_stroke(color, width = 6)
brace = Brace(nudge_line, DOWN, buff = SMALL_BUFF)
brace.set_color(color)
func_label = config["func_label"]
if len(func_label) == 1:
text = "$d%s$"%func_label
else:
text = "$d(%s)$"%func_label
brace.text = brace.get_text(text, buff = SMALL_BUFF)
brace.text.set_color(color)
brace.add(brace.text)
line.add(nudge_line)
nudge_lines.add(nudge_line)
braces.add(brace)
numbers.add(line.numbers)
line.remove(*line.numbers)
dx_brace, dx_squared_brace, dsine_brace = braces
x_value = str(self.example_x)
x_value_label = TexMobject("=%s"%x_value)
x_value_label.next_to(labels[0][1], RIGHT)
dx_squared_value = TexMobject(
"= 2x\\,dx ", "\\\\ = 2(%s)dx"%x_value
)
dx_squared_value.shift(
dx_squared_brace.text.get_right()+MED_SMALL_BUFF*RIGHT - \
dx_squared_value[0].get_left()
)
dsine_value = TextMobject(
"$=\\cos(%s)$"%self.line_configs[1]["func_label"],
dx_squared_brace.text.get_tex_string()
)
dsine_value.next_to(dsine_brace.text)
less_than_zero = TexMobject("<0")
less_than_zero.next_to(dsine_brace.text)
all_x_squared_relevant_labels = VGroup(
dx_squared_brace, dsine_brace,
labels[1], labels[2],
dsine_value,
)
all_x_squared_relevant_labels.save_state()
self.play(FadeOut(numbers))
self.animate_x_change(
self.example_x + self.dx,
run_time = 1,
added_anims = it.chain(
[GrowFromCenter(dx_brace)],
list(map(ShowCreation, nudge_lines))
)
)
self.animate_x_change(self.example_x)
self.wait()
self.play(Write(x_value_label))
self.wait()
self.play(FocusOn(dx_squared_brace))
self.play(Write(dx_squared_brace))
self.wiggle_by_dx()
self.wait()
for part in dx_squared_value:
self.play(Write(part))
self.wait()
self.play(FadeOut(dx_squared_value))
self.wait()
#Needs to be part of everything for the reconfiguraiton
dsine_brace.set_fill(opacity = 0)
dsine_value.set_fill(opacity = 0)
self.add(dsine_brace, dsine_value)
self.replace_x_squared_with_h()
self.wait()
self.play(dsine_brace.set_fill, None, 1)
self.discuss_dsine_sign(less_than_zero)
self.wait()
dsine_value.set_fill(opacity = 1)
self.play(Write(dsine_value))
self.wait()
self.play(
all_x_squared_relevant_labels.restore,
lag_ratio = 0.5,
lag_factor = 3,
run_time = 3,
)
self.__dict__.update(self.__class__.CONFIG)
self.wait()
for mob in dsine_value:
self.play(Indicate(mob))
self.wait()
two_x_dx = dx_squared_value[0]
dx_squared = dsine_value[1]
two_x_dx_copy = VGroup(*two_x_dx[1:]).copy()
self.play(FocusOn(two_x_dx))
self.play(Write(two_x_dx))
self.play(
two_x_dx_copy.move_to, dx_squared, LEFT,
dx_squared.next_to, dx_squared, UP,
run_time = 2
)
self.play(FadeOut(dx_squared))
for sublist in two_x_dx_copy[:2], two_x_dx_copy[2:]:
self.play(Indicate(VGroup(*sublist)))
self.wait()
self.wait(2)
self.final_derivative = dsine_value
def discuss_dsine_sign(self, less_than_zero):
self.wiggle_by_dx()
self.wait()
for x in self.example_x+self.dx, self.example_x:
self.animate_x_change(x, run_time = 2)
self.wait()
if less_than_zero not in self.get_mobjects():
self.play(Write(less_than_zero))
else:
self.play(FadeOut(less_than_zero))
def replace_x_squared_with_h(self):
new_config = copy.deepcopy(self.__class__.CONFIG)
new_config["line_configs"][1]["func_label"] = "h"
new_config["line_configs"][2]["func_label"] = "\\sin(h)"
self.transition_to_alt_config(
return_to_original_configuration = False,
**new_config
)
def give_example_of_meaning(self):
words = TextMobject("For example,")
expression = TexMobject("\\cos(1.5^2)\\cdot 2(1.5)\\,dx")
group = VGroup(words, expression)
group.arrange(DOWN, aligned_edge = LEFT)
group.scale(0.8)
group.to_edge(RIGHT)
arrow = Arrow(group.get_bottom(), self.final_derivative[0].get_top())
self.play(*list(map(FadeOut, [self.arrows, self.arrow_labels])))
self.play(FadeIn(group))
self.play(ShowCreation(arrow))
self.wait()
self.wiggle_by_dx()
self.wait()
########
def wiggle_by_dx(self, **kwargs):
kwargs["run_time"] = kwargs.get("run_time", 1)
kwargs["rate_func"] = kwargs.get("rate_func", there_and_back)
target_x = self.line_group.x_val + self.dx
self.animate_x_change(target_x, **kwargs)
def animate_x_change(self, target_x, **kwargs):
#Assume fixed lines, only update labels
kwargs["run_time"] = kwargs.get("run_time", 2)
added_anims = kwargs.get("added_anims", [])
start_x = self.line_group.x_val
def update(line_group, alpha):
lines, labels = line_group
new_x = interpolate(start_x, target_x, alpha)
for line, label, config in zip(lines, labels, self.line_configs):
new_label = self.get_line_label(
line, new_x, **config
)
Transform(label, new_label).update(1)
line_group.x_val = new_x
self.play(
UpdateFromAlphaFunc(self.line_group, update),
*added_anims,
**kwargs
)
def get_line_group(self, x):
group = VGroup()
group.lines, group.labels = VGroup(), VGroup()
for line_config in self.line_configs:
number_line = self.get_number_line(**line_config)
label = self.get_line_label(number_line, x, **line_config)
group.lines.add(number_line)
group.labels.add(label)
group.add(group.lines, group.labels)
group.x_val = x
return group
def get_number_line(
self, center_y, **number_line_config
):
number_line = NumberLine(color = GREY, **number_line_config)
number_line.stretch_to_fit_width(self.line_width)
number_line.add_numbers()
number_line.shift(center_y*UP)
number_line.to_edge(LEFT, buff = LARGE_BUFF)
return number_line
def get_line_label(
self, number_line, x, func, func_label, triangle_color,
**spillover_kwargs
):
triangle = RegularPolygon(
n=3, start_angle = -np.pi/2,
fill_color = triangle_color,
fill_opacity = 0.75,
stroke_width = 0,
)
triangle.set_height(self.triangle_height)
triangle.move_to(
number_line.number_to_point(func(x)), DOWN
)
label_mob = TexMobject(func_label)
label_mob.next_to(triangle, UP, buff = SMALL_BUFF, aligned_edge = LEFT)
return VGroup(triangle, label_mob)
class GeneralizeChainRule(Scene):
def construct(self):
example = TexMobject(
"\\frac{d}{dx}", "\\sin(", "x^2", ")", "=",
"\\cos(", "x^2", ")", "\\,2x",
)
general = TexMobject(
"\\frac{d}{dx}", "g(", "h(x)", ")", "=",
"{dg \\over ", " dh}", "(", "h(x)", ")", "{dh \\over", " dx}", "(x)"
)
example.to_edge(UP, buff = LARGE_BUFF)
example.shift(RIGHT)
general.next_to(example, DOWN, buff = 1.5*LARGE_BUFF)
for mob in example, general:
mob.set_color(SINE_COLOR)
mob[0].set_color(WHITE)
for tex in "x^2", "2x", "(x)", "{dh", " dx}":
mob.set_color_by_tex(tex, X_SQUARED_COLOR, substring = True)
example_outer = VGroup(*example[1:4])
example_inner = example[2]
d_example_outer = VGroup(*example[5:8])
d_example_inner = example[6]
d_example_d_inner = example[8]
general_outer = VGroup(*general[1:4])
general_inner = general[2]
d_general_outer = VGroup(*general[5:10])
d_general_inner = general[8]
d_general_d_inner = VGroup(*general[10:13])
example_outer_brace = Brace(example_outer)
example_inner_brace = Brace(example_inner, UP, buff = SMALL_BUFF)
d_example_outer_brace = Brace(d_example_outer)
d_example_inner_brace = Brace(d_example_inner, buff = SMALL_BUFF)
d_example_d_inner_brace = Brace(d_example_d_inner, UP, buff = SMALL_BUFF)
general_outer_brace = Brace(general_outer)
general_inner_brace = Brace(general_inner, UP, buff = SMALL_BUFF)
d_general_outer_brace = Brace(d_general_outer)
d_general_inner_brace = Brace(d_general_inner, buff = SMALL_BUFF)
d_general_d_inner_brace = Brace(d_general_d_inner, UP, buff = SMALL_BUFF)
for brace in example_outer_brace, general_outer_brace:
brace.text = brace.get_text("Outer")
for brace in example_inner_brace, general_inner_brace:
brace.text = brace.get_text("Inner")
for brace in d_example_outer_brace, d_general_outer_brace:
brace.text = brace.get_text("d(Outer)")
brace.text.shift(SMALL_BUFF*LEFT)
for brace in d_example_d_inner_brace, d_general_d_inner_brace:
brace.text = brace.get_text("d(Inner)", buff = SMALL_BUFF)
#d(out)d(in) for example
self.add(example)
braces = VGroup(
example_outer_brace,
example_inner_brace,
d_example_outer_brace
)
for brace in braces:
self.play(GrowFromCenter(brace))
self.play(Write(brace.text, run_time = 1))
self.wait()
self.wait()
self.play(*it.chain(*[
[mob.scale_in_place, 1.2, mob.set_color, YELLOW]
for mob in (example_inner, d_example_inner)
]), rate_func = there_and_back)
self.play(Transform(
example_inner.copy(), d_example_inner,
path_arc = -np.pi/2,
remover = True
))
self.wait()
self.play(
GrowFromCenter(d_example_d_inner_brace),
Write(d_example_d_inner_brace.text)
)
self.play(Transform(
VGroup(*reversed(example_inner.copy())),
d_example_d_inner,
path_arc = -np.pi/2,
run_time = 2,
remover = True
))
self.wait()
#Generalize
self.play(*list(map(FadeIn, general[:5])))
self.wait()
self.play(
Transform(example_outer_brace, general_outer_brace),
Transform(example_outer_brace.text, general_outer_brace.text),
Transform(example_inner_brace, general_inner_brace),
Transform(example_inner_brace.text, general_inner_brace.text),
)
self.wait()
self.play(
Transform(d_example_outer_brace, d_general_outer_brace),
Transform(d_example_outer_brace.text, d_general_outer_brace.text),
)
self.play(Write(d_general_outer))
self.wait(2)
self.play(
Transform(d_example_d_inner_brace, d_general_d_inner_brace),
Transform(d_example_d_inner_brace.text, d_general_d_inner_brace.text),
)
self.play(Write(d_general_d_inner))
self.wait(2)
#Name chain rule
name = TextMobject("``Chain rule''")
name.scale(1.2)
name.set_color(YELLOW)
name.to_corner(UP+LEFT)
self.play(Write(name))
self.wait()
#Point out dh bottom
morty = Mortimer().flip()
morty.to_corner(DOWN+LEFT)
d_general_outer_copy = d_general_outer.copy()
morty.set_fill(opacity = 0)
self.play(
morty.set_fill, None, 1,
morty.change_mode, "raise_left_hand",
morty.look, UP+LEFT,
d_general_outer_copy.next_to,
morty.get_corner(UP+LEFT), UP, MED_LARGE_BUFF,
d_general_outer_copy.shift_onto_screen
)
self.wait()
circle = Circle(color = YELLOW)
circle.replace(d_general_outer_copy[1])
circle.scale_in_place(1.4)
self.play(ShowCreation(circle))
self.play(Blink(morty))
self.wait()
inner = d_general_outer_copy[3]
self.play(
morty.change_mode, "hooray",
morty.look_at, inner,
inner.shift, UP
)
self.play(inner.shift, DOWN)
self.wait()
self.play(morty.change_mode, "pondering")
self.play(Blink(morty))
self.wait()
self.play(*list(map(FadeOut, [
d_general_outer_copy, inner, circle
])))
#Show cancelation
braces = [
d_example_d_inner_brace,
d_example_outer_brace,
example_inner_brace,
example_outer_brace,
]
texts = [brace.text for brace in braces]
self.play(*list(map(FadeOut, braces+texts)))
to_collapse = VGroup(VGroup(*general[7:10]), general[12])
dg_dh = VGroup(*general[5:7])
dh_dx = VGroup(*general[10:12])
to_collapse.generate_target()
points = VGroup(*list(map(VectorizedPoint,
[m.get_left() for m in to_collapse]
)))
self.play(
Transform(to_collapse, points),
dh_dx.next_to, dg_dh,
morty.look_at, dg_dh,
)
self.wait()
for mob in list(dg_dh)+list(dh_dx):
circle = Circle(color = YELLOW)
circle.replace(mob)
circle.scale_in_place(1.3)
self.play(ShowCreation(circle))
self.wait()
self.play(FadeOut(circle))
strikes = VGroup()
for dh in dg_dh[1], dh_dx[0]:
strike = TexMobject("/")
strike.stretch(2, dim = 0)
strike.rotate(-np.pi/12)
strike.move_to(dh)
strike.set_color(RED)
strikes.add(strike)
self.play(Write(strikes))
self.play(morty.change_mode, "hooray")
equals_dg_dx = TexMobject("= \\frac{dg}{dx}")
equals_dg_dx.next_to(dh_dx)
self.play(Write(equals_dg_dx))
self.play(Blink(morty))
self.wait(2)
##More than a notational trick
self.play(
PiCreatureSays(morty, """
This is more than a
notational trick
"""),
VGroup(
dg_dh, dh_dx, equals_dg_dx, strikes,
*general[:5]
).shift, DOWN,
FadeOut(example)
)
self.wait()
self.play(Blink(morty))
self.wait()
class WatchingVideo(PiCreatureScene):
def construct(self):
laptop = Laptop()
laptop.scale(2)
laptop.to_corner(UP+RIGHT)
randy = self.get_primary_pi_creature()
randy.move_to(laptop, DOWN+LEFT)
randy.shift(MED_SMALL_BUFF*UP)
randy.look_at(laptop.screen)
formulas = VGroup(*[
TexMobject("\\frac{d}{dx}\\left( %s \\right)"%s)
for s in [
"e^x \\sin(x)",
"\\sin(x) \\cdot \\frac{1}{\\cos(x)}",
"\\cos(3x)^2",
"e^x(x^2 + 3x + 2)",
]
])
formulas.arrange(
DOWN,
buff = MED_LARGE_BUFF,
aligned_edge = LEFT
)
formulas.next_to(randy, LEFT, buff = MED_LARGE_BUFF)
formulas.shift_onto_screen()
self.add(randy, laptop)
self.wait()
self.play(randy.change_mode, "erm")
self.play(Blink(randy))
self.wait()
self.play(randy.change_mode, "maybe")
self.wait()
self.play(Blink(randy))
for formula in formulas:
self.play(
Write(formula, run_time = 2),
randy.change_mode, "thinking"
)
self.wait()
def create_pi_creatures(self):
return [Randolph().shift(DOWN+RIGHT)]
class NextVideo(TeacherStudentsScene):
def construct(self):
series = VideoSeries()
series.to_edge(UP)
next_video = series[4]
pre_expression = TexMobject(
"x", "^2", "+", "y", "^2", "=", "1"
)
d_expression = TexMobject(
"2", "x", "\\,dx", "+", "2", "y", "\\,dy", "=", "0"
)
expression_to_d_expression_indices = [
1, 0, 0, 2, 4, 3, 3, 5, 6
]
expression = VGroup()
for i, j in enumerate(expression_to_d_expression_indices):
submob = pre_expression[j].copy()
if d_expression.expression_parts[i] == "2":
two = TexMobject("2")
two.replace(submob)
expression.add(two)
else:
expression.add(submob)
for mob in expression, d_expression:
mob.scale(1.2)
mob.next_to(
self.get_teacher().get_corner(UP+LEFT), UP,
buff = MED_LARGE_BUFF
)
mob.shift_onto_screen()
axes = Axes(x_min = -3, x_max = 3, color = GREY)
axes.add(Circle(color = YELLOW))
line = Line(np.sqrt(2)*UP, np.sqrt(2)*RIGHT)
line.scale_in_place(1.5)
axes.add(line)
axes.scale(0.5)
axes.next_to(d_expression, LEFT)
self.add(series)
self.play(
next_video.shift, 0.5*DOWN,
next_video.set_color, YELLOW,
self.get_teacher().change_mode, "raise_right_hand"
)
self.wait()
self.play(
Write(expression),
*[
ApplyMethod(pi.change_mode, "pondering")
for pi in self.get_students()
]
)
self.play(FadeIn(axes))
self.wait()
self.remove(expression)
self.play(Transform(expression, d_expression, path_arc = np.pi/2))
self.wait()
self.play(
Rotate(
line, np.pi/4,
about_point = axes.get_center(),
rate_func = wiggle,
run_time = 3
)
)
self.wait(2)
class Chapter4Thanks(PatreonThanks):
CONFIG = {
"specific_patrons" : [
"Ali Yahya",
"Meshal Alshammari",
"CrypticSwarm ",
"Ankit Agarwal",
"Yu Jun",
"Shelby Doolittle",
"Dave Nicponski",
"Damion Kistler",
"Juan Benet",
"Othman Alikhan",
"Justin Helps",
"Markus Persson",
"Dan Buchoff",
"Derek Dai",
"Joseph John Cox",
"Luc Ritchie",
"Nils Schneider",
"Mathew Bramson",
"Guido Gambardella",
"Jerry Ling",
"Mark Govea",
"Vecht",
"Shimin Kuang",
"Rish Kundalia",
"Achille Brighton",
"Kirk Werklund",
"Ripta Pasay",
"Felipe Diniz",
],
"patron_group_size" : 8,
}
class Thumbnail(IntroduceProductAsArea):
CONFIG = {
"default_x" : 0.8,
"dx" : 0.05
}
def construct(self):
self.x_slider = self.get_x_slider(self.default_x)
blg = self.box_label_group = self.get_box_label_group(
self.default_x
)
df_boxes = self.get_df_boxes()
df_boxes.space_out_submobjects(1.1)
df_boxes.move_to(blg[0], UP+LEFT)
blg[1][1].next_to(df_boxes[-1], RIGHT)
df_box_labels = self.get_df_box_labels(df_boxes)
blg.add(df_boxes, df_box_labels)
blg.set_height(FRAME_HEIGHT-2*MED_LARGE_BUFF)
blg.center()
self.add(blg)