mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 08:54:38 +08:00
Up to three lines chain rule in eoc4
This commit is contained in:
424
eoc/chapter4.py
424
eoc/chapter4.py
@ -1319,46 +1319,438 @@ class ShoveXSquaredInSine(Scene):
|
||||
)
|
||||
self.dither()
|
||||
|
||||
class ThreeLinesChainRule(Scene):
|
||||
class ThreeLinesChainRule(ReconfigurableScene):
|
||||
CONFIG = {
|
||||
"default_x" : 0.5,
|
||||
"start_x" : 0.5,
|
||||
"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" : range(4),
|
||||
"numbers_with_elongated_ticks" : 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" : 9,
|
||||
"numbers_to_show" : range(0, 10, 2),
|
||||
"tick_frequency" : 1,
|
||||
"x_max" : 10,
|
||||
"numbers_to_show" : range(0, 11),
|
||||
"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_max" : 2,
|
||||
"numbers_to_show" : range(-2, 3),
|
||||
"numbers_with_elongated_ticks" : range(-2, 3),
|
||||
"tick_frequency" : 0.25,
|
||||
},
|
||||
],
|
||||
"line_width" : 6,
|
||||
"line_width" : 8,
|
||||
"triangle_height" : 0.25,
|
||||
}
|
||||
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):
|
||||
number_lines = VGroup(*[
|
||||
self.get_line(x, **line_config)
|
||||
for line_config in self.line_configs
|
||||
for line in lines:
|
||||
self.play(Write(line, run_time = 2))
|
||||
self.dither()
|
||||
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)
|
||||
return number_lines
|
||||
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.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)
|
||||
|
||||
|
||||
|
||||
|
@ -85,10 +85,11 @@ class TexMobject(SVGMobject):
|
||||
tex.count(char)
|
||||
for char in "{}"
|
||||
]
|
||||
if tex.startswith("{") and num_lefts > num_rights:
|
||||
return tex[1:]
|
||||
elif tex.endswith("}") and num_rights > num_lefts:
|
||||
return tex[:-1]
|
||||
if num_rights > num_lefts:
|
||||
backwards = tex[::-1].replace("}", "", num_rights-num_lefts)
|
||||
tex = backwards[::-1]
|
||||
elif num_lefts > num_rights:
|
||||
tex = tex.replace("{", "", num_lefts-num_rights)
|
||||
return tex
|
||||
|
||||
|
||||
@ -109,13 +110,13 @@ class TexMobject(SVGMobject):
|
||||
self.submobjects = new_submobjects
|
||||
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 tex == self.get_tex_string():
|
||||
self.highlight(color)
|
||||
return self
|
||||
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)
|
||||
return self
|
||||
|
||||
|
Reference in New Issue
Block a user