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_submobjects(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, submobject_mode = "lagged_start" ) 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, submobject_mode = "lagged_start"), 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, num_anchor_points = 100, 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 = None )) 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_submobjects(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_submobjects(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_submobjects, 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_submobjects(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, submobject_mode = "lagged_start", 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_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(*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_submobjects( 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)