Up to three lines chain rule in eoc4

This commit is contained in:
Grant Sanderson
2017-02-16 20:21:35 -08:00
parent 3535b39dd1
commit aa9384c9b6
2 changed files with 415 additions and 22 deletions

View File

@ -1319,46 +1319,438 @@ class ShoveXSquaredInSine(Scene):
) )
self.dither() self.dither()
class ThreeLinesChainRule(Scene): class ThreeLinesChainRule(ReconfigurableScene):
CONFIG = { CONFIG = {
"default_x" : 0.5, "start_x" : 0.5,
"top_x" : 3,
"example_x" : 1.5,
"dx" : 0.1,
"line_configs" : [ "line_configs" : [
{ {
"func" : lambda x : x,
"func_label" : "x",
"triangle_color" : WHITE,
"center_y" : 3,
"x_min" : 0, "x_min" : 0,
"x_max" : 3, "x_max" : 3,
"numbers_to_show" : range(4), "numbers_to_show" : range(4),
"numbers_with_elongated_ticks" : range(4),
"tick_frequency" : 0.25, "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_min" : 0,
"x_max" : 9, "x_max" : 10,
"numbers_to_show" : range(0, 10, 2), "numbers_to_show" : range(0, 11),
"tick_frequency" : 1, "numbers_with_elongated_ticks" : 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_min" : -2,
"x_max" : 2, "x_max" : 2,
"numbers_to_show" : range(-2, 3), "numbers_to_show" : range(-2, 3),
"numbers_with_elongated_ticks" : range(-2, 3),
"tick_frequency" : 0.25, "tick_frequency" : 0.25,
}, },
], ],
"line_width" : 6, "line_width" : 8,
"triangle_height" : 0.25,
} }
def construct(self): def construct(self):
lines_group = self.get_three_lines_group() self.introduce_line_group()
self.draw_function_arrows()
self.talk_through_movement()
self.nudge_x()
self.give_example_of_meaning()
self.add(lines_group) def introduce_line_group(self):
self.line_group = self.get_line_group(self.start_x)
lines, labels = self.line_group
def get_three_line_group(self, x): for line in lines:
number_lines = VGroup(*[ self.play(Write(line, run_time = 2))
self.get_line(x, **line_config) self.dither()
for line_config in self.line_configs 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.dither()
last_label = label
for x in 1, 0, self.start_x:
self.animate_x_change(x, run_time = 1)
self.dither()
def draw_function_arrows(self):
lines, line_labels = self.line_group
labels = VGroup(*[
TexMobject("(\\dots)^2").highlight(X_SQUARED_COLOR),
TexMobject("\\sin(\\dots)").highlight(SINE_COLOR)
]) ])
number_lines.arrange_submobjects(DOWN, buff = LARGE_BUFF) arrows = VGroup()
return number_lines 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.highlight(label.get_color())
arrow.next_to(VGroup(*lines_subset))
arrows.add(arrow)
label.next_to(arrow, RIGHT)
self.play(
ShowCreation(arrow),
Write(label)
)
self.dither()
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.dither()
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.dither()
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.dither(2)
self.play(*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.highlight(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.highlight(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)],
map(ShowCreation, nudge_lines)
)
)
self.animate_x_change(self.example_x)
self.dither()
self.play(Write(x_value_label))
self.dither()
self.play(FocusOn(dx_squared_brace))
self.play(Write(dx_squared_brace))
self.wiggle_by_dx()
self.dither()
for part in dx_squared_value:
self.play(Write(part))
self.dither()
self.play(FadeOut(dx_squared_value))
self.dither()
#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.dither()
self.play(dsine_brace.set_fill, None, 1)
self.discuss_dsine_sign(less_than_zero)
self.dither()
dsine_value.set_fill(opacity = 1)
self.play(Write(dsine_value))
self.dither()
self.play(
all_x_squared_relevant_labels.restore,
submobject_mode = "lagged_start",
lag_factor = 3,
run_time = 3,
)
self.__dict__.update(self.__class__.CONFIG)
self.dither()
two_x_dx = dx_squared_value[0]
dx_squared = dsine_value[1]
two_x_dx_copy = VGroup(*two_x_dx[1:]).copy()
self.play(Write(two_x_dx))
self.play(
two_x_dx_copy.move_to, dx_squared, LEFT,
dx_squared.next_to, dx_squared, UP,
)
self.play(FadeOut(dx_squared))
self.dither(2)
self.final_derivative = dsine_value
def discuss_dsine_sign(self, less_than_zero):
self.wiggle_by_dx()
self.dither()
for x in self.example_x+self.dx, self.example_x:
self.animate_x_change(x, run_time = 2)
self.dither()
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_submobjects(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(*map(FadeOut, [self.arrows, self.arrow_labels]))
self.play(FadeIn(group))
self.play(ShowCreation(arrow))
self.dither()
self.wiggle_by_dx()
self.dither()
########
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.scale_to_fit_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)
general.next_to(example, DOWN, buff = 1.5*LARGE_BUFF)
for mob in example, general:
mob.highlight(SINE_COLOR)
mob[0].highlight(WHITE)
for tex in "x^2", "2x", "(x)", "{dh", " dx}":
mob.highlight_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:9])
d_general_inner = general[7]
d_general_d_inner = VGroup(*general[9:12])
example_outer_brace = Brace(example_outer)
example_inner_brace = Brace(example_inner, 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, 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 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)
outer_braces = VGroup(example_outer_brace, d_example_outer_brace)
self.play(*map(GrowFromCenter, outer_braces))
for brace in outer_braces:
self.play(Write(brace.text, run_time = 1))
self.dither()
inner_braces = VGroup(example_inner_brace, d_example_inner_brace)
self.play(*it.chain(*[
[mob.scale_in_place, 1.2, mob.highlight, 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.dither()
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.dither()
#Generalize
def get_number_line(self, x, **line_config):
number_line = NumberLine(**line_config)

View File

@ -85,10 +85,11 @@ class TexMobject(SVGMobject):
tex.count(char) tex.count(char)
for char in "{}" for char in "{}"
] ]
if tex.startswith("{") and num_lefts > num_rights: if num_rights > num_lefts:
return tex[1:] backwards = tex[::-1].replace("}", "", num_rights-num_lefts)
elif tex.endswith("}") and num_rights > num_lefts: tex = backwards[::-1]
return tex[:-1] elif num_lefts > num_rights:
tex = tex.replace("{", "", num_lefts-num_rights)
return tex return tex
@ -109,13 +110,13 @@ class TexMobject(SVGMobject):
self.submobjects = new_submobjects self.submobjects = new_submobjects
return self return self
def highlight_by_tex(self, tex, color): def highlight_by_tex(self, tex, color, substring = True):
if not hasattr(self, "expression_parts"): if not hasattr(self, "expression_parts"):
if tex == self.get_tex_string(): if tex == self.get_tex_string():
self.highlight(color) self.highlight(color)
return self return self
for submob, part_tex in zip(self.split(), self.expression_parts): for submob, part_tex in zip(self.split(), self.expression_parts):
if part_tex == tex: if (part_tex == tex) or (substring and tex in part_tex):
submob.highlight(color) submob.highlight(color)
return self return self